Repository navigation
Conversation
| continue | ||
| } | ||
|
|
||
| merged, err := concatFunctionToolResultBlocks(ret[len(ret)-1], block) |
There was a problem hiding this comment.
问题简述: 这里逐块合并累计结果,会使无 StreamingMeta 的长工具结果流仍产生 O(n²) 复制。
导致问题的链路: 每次循环都把 ret[len(ret)-1] 中已经累计的完整文本与下一个 block 重新传给 concatFunctionToolResultBlocks。第 2 次复制前两块,第 3 次再复制前三块,以此类推。即使内层改用预分配的 strings.Builder,整个长流的累计复制量仍是平方级,与本次降低长流复制成本的目标不符。
修复建议: 先收集属于同一次调用的一整段连续 block,再一次性合并 FunctionToolResult.Content 和 Extra;不要在每收到一个 block 时重新构造此前的累计结果。
There was a problem hiding this comment.
Fixed in #1388。连续的 FunctionToolResult blocks 先按整段确定范围,再一次合并 Content 和 Extra,避免逐块重建累计结果;保留空 CallID/Name 的原有合并规则。
| @@ -29,6 +29,8 @@ func init() { | |||
| RegisterName[[]*Message]("_eino_message_slice") | |||
| RegisterName[*AgenticMessage]("_eino_agentic_message") | |||
| RegisterName[[]*AgenticMessage]("_eino_agentic_message_slice") | |||
| RegisterName[*ToolInfo]("_eino_tool_info") | |||
There was a problem hiding this comment.
问题简述: 在包初始化阶段为 ToolInfo 固定注册名称,会使此前合法的用户自定义注册产生 breaking change。
导致问题的链路: 旧版本允许 SDK 用户调用 schema.RegisterName[*schema.ToolInfo]("custom_name"),并可能已经用该名称生成 checkpoint。升级后,schema.init 会先把同一具体类型注册为 _eino_tool_info;用户原有注册随后会因 gob 不允许同一类型对应多个名称而 panic。同时,历史 checkpoint 中的自定义 $type 名称也无法通过当前单一 type-to-name 注册表恢复。
There was a problem hiding this comment.
确认这是 breaking change。本次保留 ToolInfo 和 []*ToolInfo 的固定名称注册。
升级后,用户此前对这些类型使用其他名称的自定义注册会发生冲突;依赖旧注册名称的历史 checkpoint 也不能直接沿用新默认名称恢复。发布时需要在 release notes 中明确说明这项不兼容变更,并说明受影响用户的注册调整及历史 checkpoint 迁移要求。
| inner := func(ctx context.Context, input *ToolInput) (*ToolOutput, error) { | ||
| return next(core.PopAddressSegment(ctx), input) | ||
| } | ||
| return spec.fn(inner)(mwCtx, input) |
There was a problem hiding this comment.
问题简述: 设置 Name 后,middleware factory 会从构建 endpoint 时执行一次,变成每次工具调用都重新执行;Name 本应只增加可恢复的地址身份,不应改变 middleware 生命周期。
导致问题的链路: 当前 spec.fn(inner) 位于请求闭包内。用户给已有 middleware 增加 Name 后,每个工具调用都会重新创建 middleware endpoint,factory 内捕获的计数器、缓存、限流器等状态会反复重置,并可能在并发工具调用中重复初始化。同样的问题也存在于另外三类命名 middleware wrapper。
修复建议: 在 wrap*ToolMiddleware 构建阶段先创建稳定的 inner,执行一次 wrapped := spec.fn(inner);请求闭包只负责追加 middleware 地址并调用 wrapped(mwCtx, input)。
There was a problem hiding this comment.
Fixed in #1388。四类命名工具 middleware 均改为在构建 endpoint 时执行一次 factory;每次调用只处理 middleware 地址并调用已构建的 endpoint。
| @@ -607,7 +609,7 @@ func (r *runner) handleInterrupt( | |||
| for _, t := range nextTasks { | |||
| cp.Inputs[t.nodeKey] = t.input | |||
| } | |||
| err = r.checkPointer.convertCheckPoint(cp, isStream) | |||
| err = r.checkPointer.convertCheckPoint(cp, isStream, is) | |||
There was a problem hiding this comment.
问题简述: 这里的 checkpoint 转换可能改写 cp.RerunNodes,但此前构造的 intInfo.RerunNodes 不会同步,导致公开中断信息与实际恢复行为不一致。
导致问题的链路: 工具输出流在物化时返回 toolStreamCheckpointError,applyToolStreamCheckpoint 会删除下游 consumer,并把真正的 tools node 加入 cp.RerunNodes;resume 随后使用更新后的 checkpoint,但 ExtractInterruptInfo 和子图上报仍直接返回转换前的 intInfo。相同的遗漏也存在于 handleInterruptWithSubGraphAndRerunNodes。
修复建议: 在 convertCheckPoint 成功后,用最终的 cp.RerunNodes 更新或重新构造 intInfo.RerunNodes,并对另一条保存路径做相同处理;补充断言,确保公开 metadata、嵌套 SubGraphs 与持久化 checkpoint 一致。
There was a problem hiding this comment.
Fixed in #1388。两条中断处理路径都在 convertCheckPoint 完成后生成公开 RerunNodes;子图仍通过 InterruptInfo.SubGraphs 表达,不直接将 checkpoint 中的子图恢复节点混入公开 RerunNodes。
| // instruction, so it is preserved. | ||
| if len(inputMessages) > 0 && inputMessages[0].Role == schema.System && | ||
| !isReminderExtra(inputMessages[0].Extra) { | ||
| inputMessages = inputMessages[1:] |
There was a problem hiding this comment.
[P1] 调用方传入的首条 system message 会被静默删除
问题简述: 当 Agent 配置了非空 Instruction,且 SDK 用户通过 input.Messages 显式传入普通 system message 时,该消息仅因 role 和位置符合条件就会在这里被移除。
导致问题的链路: defaultGenModelInput 删除首条非 reminder system message,随后在输入首位加入 Agent instruction。调用方传入的租户策略、安全约束等 system 内容因此不会进入最终模型输入。AgenticMessage 分支存在相同行为。
| if instruction != "" { | ||
| if len(inputMessages) > 0 { | ||
| if msg, ok := any(inputMessages[0]).(*schema.Message); ok && msg.Role == schema.System { | ||
| inputMessages = inputMessages[1:] |
There was a problem hiding this comment.
[P1] Deep 会删除调用方 system message以及 middleware 注入的 system reminder
问题简述: Deep 始终具有非空 instruction,并固定使用 typedGenModelInput;这里仅根据首条消息的 role 进行删除,没有区分消息来源,也没有识别 MessageExtraKeySystemReminder。
导致问题的链路: 当调用方的 system message或 middleware 注入的 system reminder 位于输入首位时,该消息会在 Deep instruction 加入前被移除,最终无法发送给模型。AgenticMessage 分支存在相同行为。
| if reflect.DeepEqual(a, b) { | ||
| return true | ||
| } | ||
| aBytes, aErr := json.Marshal(a) |
There was a problem hiding this comment.
想确认一下是否确实需要引入 model_context 事件,以及目前是否已经有明确的 consumer。如果需要,还要评估它在模型调用路径上的性能成本:每次都需要遍历 ToolInfos 做 reflect.DeepEqual,比较不等时还会对两边分别执行 json.Marshal;在工具数量较多、参数 schema 较大或 ReAct 模型调用频繁时,这部分开销可能比较明显。
| ReminderMessageRoleUser | ||
| ) | ||
|
|
||
| var reminderMessageRole atomic.Value |
There was a problem hiding this comment.
[P1] ReminderMessageRole 的 session 级契约实际是进程全局
问题简述: 这里将 ReminderMessageRole 保存为包级单例,但公开 API 声明它的作用域是单个 session。公开 setter 没有携带任何 session 信息,因此任一 session 的设置都会覆盖同一进程内所有 session 读取到的角色。
导致问题的链路: Session A 设置为 User 后,Session B 的 toolsearch、skill、subagent middleware 会在模型调用前读取同一个全局值,用它归一化已有 reminder并创建新 reminder;其他 session 随后设置为 System,又会反向改变 A、B 下一次模型调用看到的角色。atomic.Value 只能避免数据竞争,不能提供 session 隔离。共享进程中使用不同模型约束的 session 因而会互相改变 reminder 角色,可能导致模型拒绝请求或错误理解 reminder。
There was a problem hiding this comment.
这里还涉及一个更通用的扩展问题:内置 middleware 在返回前就通过 TypedSendEvent 发出了 MessageInserted 事件。用户即使包装这个 middleware,在拿到返回值后修改 message,也无法同步修改已经发出的事件;仅修改模型输入则不会改变 SessionStore 中保存的消息。
如果用户希望自定义内置 middleware 注入的消息,并让修改后的消息进入 session 历史,目前缺少事件发送前的修改入口。这可能涉及 session event / SessionStore 的整体设计,建议单独讨论消息修改与事件持久化的扩展契约。
| ended: ended, | ||
| msg: result, | ||
| err: err, | ||
| accepted: err == nil, |
There was a problem hiding this comment.
[P1] ShouldRetry 的 Generate 路径在 verdict 前提交了错误的 span 状态
问题简述: ShouldRetry 的 Generate 路径中,这里发生在 verdict 之前,因此模型无错误返回、但随后被 ShouldRetry 拒绝的 attempt 也会被记录为 Accepted=true。
导致问题的链路: retry wrapper 先设置 suppressEventSend 并调用 event sender;本函数先发送 model end span,之后才检查 suppression。最终接受某次输出时,retry 层只补发普通 model output event,没有执行正常路径中创建 SessionEventVariant、写入 CurrentModelSpanID 和 CurrentAssistantMessageEventID 的逻辑。后续 tool span 因而会读取到空值或旧值。即使 Session Runner 之后为 output event回填 SessionEventVariant,也不会把这些 ID 写回 typed state。每次 attempt 仍有独立 span ID 和正确的 Attempt 序号,但 rejected attempt 的 Accepted 状态及最终 tool span 关联是错误的。
修复建议: 在 ShouldRetry verdict 后再提交 Generate 的 span completion 和 accepted output 状态:被拒绝的 attempt 以 Accepted=false 结束;最终接受的 attempt 统一创建 assistant event identity、更新两个 Current ID,并发送与该 model span 对应的 output event。
There was a problem hiding this comment.
Fixed in #1388。正常返回的 Generate attempt 在 ShouldRetry verdict 后结束 span,被拒绝时记录 Accepted=false;最终接受的输出复用完整发送逻辑,创建 assistant event 并更新 CurrentModelSpanID、CurrentAssistantMessageEventID。模型报错、中断或取消时保留立即结束 span 的原有行为。
| var typedEvents any | ||
| rs.mtx.Lock() | ||
| events = append(events, rs.Events...) | ||
| typedEvents = rs.TypedEvents |
There was a problem hiding this comment.
问题简述: 锁内只复制了 TypedEvents 的 interface 值,其动态值仍指向原 slice header。解锁后的 len(*store) 和遍历会与 addTypedEvent 中的 *store = append(...) 并发读写同一个 slice header。
导致问题的链路: cancel checkpoint 保存时可能尚未等待上游 typedFlowAgent.run 完全退出,上游仍可能追加 typed event,因此 checkpoint 中保存的事件集合没有稳定的快照边界,并会触发数据竞争。
修复建议: 在持有 rs.mtx 时复制 *store 的元素,解锁后只对独立快照执行 sanitize。
There was a problem hiding this comment.
Fixed in #1388。在 rs.mtx 锁内复制 TypedEvents slice 元素,解锁后仅对独立列表快照执行 sanitize,避免与 addTypedEvent 并发读写同一个 slice header。
| // uses pointer-to-slice semantics (nil = absent, non-nil = active replacement). | ||
| type SessionEvent[M MessageType] struct { | ||
| // EventID is the canonical, session-unique identity of this event. | ||
| // Assigned exactly once by the Runner at event materialization |
There was a problem hiding this comment.
这里的公开 API 注释存在不一致和契约缺失,SDK 用户无法明确判断 EventID、TurnID 是否应该自己填写:
SessionEvent.EventID注释仍称 ID 在 Runner materialization 时分配,但当前包装器已经提前生成,注释与实现不符。- 同一字段注释称 Runner 分配的是 UUIDv4,但
SessionConfig.EventIDGenerator允许返回业务 ID。应明确 UUIDv4 只是默认规则。 TypedSendEvent/SendEvent没有说明 EventID、TurnID 的填写要求,也没有说明用户显式填写时会被保留还是覆盖、EventID 唯一性由谁负责。TurnID字段虽然写了 runner-owned,发送接口处仍缺少直接可用的契约。
这些说明应在字段和发送接口的公开注释中统一,不能让 SDK 用户通过阅读内部实现推断。
|
|
||
| // ValidateEmittedSessionEventKind enforces that runtime-emitted session events | ||
| // carry an explicit Kind matching their active payload. | ||
| func ValidateEmittedSessionEventKind[M MessageType](event *SessionEvent[M]) error { |
There was a problem hiding this comment.
这个函数只是在 NormalizeSessionEventKind 前增加 nil 和 Kind 非空检查,当前调用也都在 adk 包内,没有独立的 SDK 使用需求。为了这层很薄的内部校验新增一个公开 API,会让用户额外理解“emitted event”的概念并增加兼容性维护负担。建议改为未导出的内部函数,不必暴露给 SDK 用户。
|
|
||
| // ClassifySessionEvent derives the canonical event kind from the single active | ||
| // payload carried by event. | ||
| func ClassifySessionEvent[M MessageType](event *SessionEvent[M]) (SessionEventKind, error) { |
There was a problem hiding this comment.
SessionEvent 已有 Kind 字段,SDK 用户可以直接读取;根据 payload 重新推导 Kind 的逻辑主要供内部补齐和一致性校验使用,当前也没有包外生产代码调用。没有必要将这个实现步骤单独暴露为公共 API,增加用户需要理解的概念和兼容性维护负担。建议将 ClassifySessionEvent 改为未导出的内部函数,一致性校验仍由发送/存储边界负责。
| // mechanism: legacy data (e.g. pre-refactor "turn_end") and events written by | ||
| // newer code with kinds we don't yet understand are accepted as-is rather than | ||
| // failing replay. | ||
| func NormalizeSessionEventKind[M MessageType](event *SessionEvent[M]) error { |
There was a problem hiding this comment.
这个函数承担 Kind 补齐、payload 一致性校验和旧事件兼容,主要是框架及 store 的内部处理,没有必要要求普通 SDK 用户理解和调用。内置 adk/session store 的跨包复用也不足以成为公开 API 的理由;目前没有说明第三方 store 必须使用它的契约。建议收敛为内部实现,并调整内置 store 的共享方式(例如在依赖允许时放入共享 internal 包),避免将内部兼容规则固化成公共 SDK API。
| // with additional event-envelope Extra. Provider output is merged before | ||
| // EventIDGenerator runs, so custom generators can inspect the final Extra map. | ||
| // Returned keys prefixed with "_eino_" are rejected. | ||
| EventExtraProvider SessionEventExtraProvider[M] |
There was a problem hiding this comment.
EventExtraProvider 的命名待讨论,注释也未说明入参字段的可用性:流式预留 ID 时,即使 Kind == message,Message 仍为 nil,EventID 也为空,调用方无法从当前契约得知这些情况。
| @@ -255,7 +256,15 @@ type TypedModelRetryConfig[M MessageType] struct { | |||
| // ModelRetryConfig is the default retry config type using *schema.Message. | |||
| type ModelRetryConfig = TypedModelRetryConfig[*schema.Message] | |||
|
|
|||
| type retryableBeforeOutputError interface { | |||
There was a problem hiding this comment.
这里应删除 retryableBeforeOutputError 及 errors.As 的特殊判断,默认重试判断只保留 return err != nil。SDK 默认逻辑不应识别业务项目自定义的 IsModelTimeoutBeforeOutput() 方法来改变行为;业务侧的特殊重试条件放到已有的 ShouldRetry 中即可,不需要再增加接口或概念。
| // Capture caller-provided messages BEFORE prepending history. These will be | ||
| // emitted as session events at turn start so they appear in the event log. | ||
| sessionState.inputMessages = append([]M{}, messages...) | ||
| messages = append(append([]M{}, sessionState.latestState.Messages...), sessionState.inputMessages...) |
There was a problem hiding this comment.
Run 在这里已把历史拼接进独立的输入切片,但 sessionState.latestState.Messages 仍由事件处理流程持有到运行结束。两份切片起初共享消息对象;后续 Agent 摘要、删除或替换历史后,这份旧引用可能继续保留已不需要的消息,增加长时间运行的内存占用。这是运行期间的冗余引用,不是已确认的永久泄漏。
请在拼接完成后立即设置 sessionState.latestState.Messages = nil,保留 latestState 的工具上下文等字段即可。无需感知后续摘要时机,也不会影响已经传给 Agent 的输入。
There was a problem hiding this comment.
Fixed in #1388。Run 将历史拼入独立输入切片后,立即清空 sessionState.latestState.Messages,移除运行期间不再需要的额外历史引用,保留工具上下文字段。
| return nil, "", fmt.Errorf("failed to reconstruct session[%s]: %w", sessionID, err) | ||
| } | ||
| if reconstructResult != nil && reconstructResult.state != nil { | ||
| state.latestState = reconstructResult.state |
There was a problem hiding this comment.
Resume 从事件日志重建完整历史并保存在 latestState.Messages,但实际执行历史来自 checkpoint;后续只读取 latestState 的工具上下文信息,没有使用这份消息列表。sessionState 又在整个恢复执行期间被持有,导致长会话恢复时额外保留一份重建的历史消息。
请让 Resume 只重建所需的工具上下文信息;至少在准备阶段清空 latestState.Messages,避免持续保留无用消息。清空只能减少持续占用,不能消除完整历史重建本身的耗时和内存峰值。
There was a problem hiding this comment.
已在 #1388 修复持续持有部分:Resume 重建完成后立即清空 latestState.Messages,仅保留后续使用的工具上下文字段。当前仍执行完整历史重建,因此尚未消除重建阶段的耗时和内存峰值;本次按最小修复范围处理,未改变现有历史重建、回滚及替换边界逻辑。
| type TypedRunnerConfig[M MessageType] struct { | ||
| Agent TypedAgent[M] | ||
| EnableStreaming bool | ||
|
|
||
| CheckPointStore CheckPointStore | ||
|
|
||
| SessionID string |
There was a problem hiding this comment.
这里的 SessionID、SessionStore 没有字段注释,用户无法知道配置后的效果和组合要求。请明确:SessionID 由调用方提供;两者同时有效才启用消息托管,当前只配一个会静默按非托管模式运行;Run 会加载该会话当前历史并拼接本次输入,运行事件写入 SessionStore;SessionConfig 可省略并使用默认值。
| @@ -680,97 +166,25 @@ type GenResumeResult[T any, M MessageType] struct { | |||
| // ResumeParams are optional parameters for resuming an interrupted agent. | |||
| ResumeParams *ResumeParams | |||
|
|
|||
| // Decision selects whether to resume the suspended checkpoint or abandon it | |||
| // and start a fresh turn. The zero value resumes for compatibility. | |||
| Decision TurnLoopResumeDecision | |||
There was a problem hiding this comment.
首版先不要暴露这套恢复决策配置。当前仓库中 StartNewTurn 只有实现和测试使用,尚未体现明确的业务需求,却引入了“恢复 checkpoint / 放弃 checkpoint 并新开一轮”的公开契约及额外执行分支。请一起移除 Decision、TurnLoopResumeDecision 和仅供 StartNewTurn 使用的 GenResumeResult.Input,让 GenResume 只负责生成恢复所需的参数和待处理输入的分配;等确有放弃旧执行并开启新一轮的需求时,再设计相应 API。
| TurnLoopResumeDecisionResume TurnLoopResumeDecision = iota | ||
| // TurnLoopResumeDecisionStartNewTurn abandons the checkpoint and starts a | ||
| // fresh Runner.Run turn using GenResumeResult.Input. | ||
| TurnLoopResumeDecisionStartNewTurn |
There was a problem hiding this comment.
这里是否有必要增加 StartNewTurn?建议考虑删除 TurnLoopResumeDecision、GenResumeResult.Decision 和 Input,让 GenResume 只负责生成恢复参数。
如果需要选择“恢复旧执行”或“开始新任务”,业务层也可以在外部判断:恢复时加载 checkpoint;开始新任务时明确废弃旧 checkpoint,再启动不加载旧断点的 TurnLoop 或调用 Runner.Run。这样可以减少公开 API 和 TurnLoop 内部的决策分支。待讨论。
| // InterruptSignalFromEvent returns the internal interrupt signal carried by an | ||
| // interrupt event as an error. Nil events, events without actions, and | ||
| // non-interrupt events return nil. | ||
| func InterruptSignalFromEvent[M MessageType](event *TypedAgentEvent[M]) error { |
There was a problem hiding this comment.
建议删除 InterruptSignalFromEvent。当前 eino 非测试代码中没有调用方;已有内部中断传播直接读取 internalInterrupted,普通 SDK 用户则可通过 InterruptContexts 查看中断信息。这个方法返回内部 signal 作为 error,但没有说明明确的包外使用场景和后续处理契约,暂时没有必要新增公开 API。
| // conversations are summarized automatically). When nil, the default note is used; | ||
| // returning "" suppresses the note entirely. | ||
| // Optional. | ||
| CustomFormatContextManagementInstruction func(ctx context.Context) string |
There was a problem hiding this comment.
这段 prompt 没有提供更多有效的指导,建议不追加这段说明,也没有必要为了它增加 CustomFormatContextManagementInstruction 配置。
| // consolidation: Read, GlobInfo, Write, and Edit. | ||
| // | ||
| // LocalBackend and InMemoryBackend both implement this interface. | ||
| type Backend = ainternal.Backend |
There was a problem hiding this comment.
这里是否也需要考虑通过 Bash 操作记忆文件的使用方式,而不是把加载/整理能力限定在 Read、GlobInfo、Write、Edit 这组文件接口上?如果确有该需求,可以复用已有 filesystem.Shell 作为可选能力,并明确命令执行与 Backend 是否访问同一文件空间、MemoryDirectory 的目录限制如何生效。不要把 Execute 强制加入这个最小存储接口,否则 InMemoryBackend 等只提供文件存储的实现也会被迫承担 Shell 能力。
| } | ||
|
|
||
| func buildPendingSnapshot[M adk.MessageType](messages []M, cursor int, toolInfos []*schema.ToolInfo) (*PendingSnapshot, error) { | ||
| raw, err := json.Marshal(messages) |
There was a problem hiding this comment.
AutoMemory 内的 JSON 编解码统一改用 sonic.Marshal / sonic.Unmarshal,包括这里的消息、ToolInfos,以及 coordinator 中 PendingSnapshot 的序列化和反序列化。请检查 AutoMemory 范围内的其他同类调用一起替换;本条仅针对 AutoMemory,不扩展到其他模块。
#1145) feat(adk): add response format fallback for automemory topic selection
…tem tools (#1298) * fix(adk): prefer automemory extraction tool definitions * feat(adk): support custom dream agent handlers
refactor(adk): internalize conformance test helpers
chore: remove internal review and release artifacts
alpha/10 genericized ChatModelAgentContext into ChatModelAgentContext[M
MessageType] without leaving a non-generic spelling behind. Every
downstream middleware that names the type in its BeforeAgent signature
stopped compiling, because *adk.ChatModelAgentContext is no longer a
valid type expression once the type takes a parameter.
Follow the convention already used by the 19 other genericized ADK types
(ModelContext, ChatModelAgentState, Runner, AgentEvent, ...): rename the
generic type to TypedChatModelAgentContext[M] and add
type ChatModelAgentContext = TypedChatModelAgentContext[*schema.Message]
Type aliases are source-compatible, so existing code keeps compiling
unchanged, and callers on a non-default message type gain the Typed form.
All in-repo references already carried explicit type arguments, so they
were renamed mechanically with no semantic change.
Adds adk/handler_alias_test.go in package adk_test, which declares
BeforeAgent with the bare, non-generic *adk.ChatModelAgentContext exactly
as a downstream user would and asserts the result satisfies
adk.ChatModelAgentMiddleware. That file does not compile without the
alias, so it guards the compatibility contract at build time.
Verified: go build, go vet, go test -race ./..., golangci-lint (0 issues).
apidiff no longer reports the type as gaining a type parameter.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix(serialization): preserve typed persistence round trips * fix(serialization): preserve native gob encode behavior * fix(serialization): retain existing ToolInfo JSON contract * fix(serialization): preserve signed zero and expand regression coverage * fix(serialization): validate custom JSON method pairs * refactor(serialization): clarify custom JSON marshal helper name * refactor(serialization): use sonic with number-preserving decoding * docs(serialization): clarify custom JSON null handling * docs(serialization): mark human-readable serializer experimental * docs(session): mark built-in stores experimental * ci(ci): allow session commit scope * test(adk): make notification conformance lease timing deterministic The 20ms wall-clock attempt lease can expire during instrumented CI payload validation. Reuse the test clock and explicit expiry hooks to preserve lease-fencing and outbox-redelivery coverage without depending on execution speed.
* refactor(adk): reuse internal stream collection and message concatenation helpers * refactor(schema): move message stream helpers out of ADK * refactor(schema): distinguish complete and prefix stream concatenation * refactor(adk): replace remaining private message stream concat helpers
| "reduction", | ||
| "schema", | ||
| "session", | ||
| "skill", |
There was a problem hiding this comment.
🚨 Breaking API Changes Detected
Package: github.com/cloudwego/eino/compose
Incompatible changes:
- MigrateCheckpointState: changed from func([]byte, Serializer, func(state any) (any, bool, error)) ([]byte, error) to func([]byte, github.com/cloudwego/eino/schema.Serializer, func(state any) (any, bool, error)) ([]byte, error)
- Serializer: changed from Serializer to github.com/cloudwego/eino/schema.Serializer
- WithSerializer: changed from func(Serializer) GraphCompileOption to func(github.com/cloudwego/eino/schema.Serializer) GraphCompileOption
Review Guidelines
Please ensure that:
- The changes are absolutely necessary
- They are properly documented
- Migration guides are provided if needed
| "reduction", | ||
| "schema", | ||
| "session", | ||
| "skill", |
There was a problem hiding this comment.
🚨 Breaking API Changes Detected
Package: github.com/cloudwego/eino/adk/filesystem
Incompatible changes:
- ExecuteRequest.RunInBackendGround: removed
Review Guidelines
Please ensure that:
- The changes are absolutely necessary
- They are properly documented
- Migration guides are provided if needed
| "reduction", | ||
| "schema", | ||
| "session", | ||
| "skill", |
There was a problem hiding this comment.
🚨 Breaking API Changes Detected
Package: github.com/cloudwego/eino/adk/middlewares/filesystem
Incompatible changes:
- EditFileToolDesc: value changed from "Performs exact string replacements in files.\n\nUsage
- EditFileToolDescChinese: value changed from "在文件中执行精确的字符串替换。\n\n使用方法:\n- 在编辑之前,你必须在对话中至少使用一次 'read_file' 工具。如果你在未... to "在文件中执行精确的字符串替换。\n\n- 编辑前你必须在本次对话中 Read 过该文件,否则调用会失败。\n-
old_string... - EnhancedReadFileDescSuffix: removed
- EnhancedReadFileDescSuffixChinese: removed
- ExecuteToolDesc: value changed from "\nExecutes a given command in the sandbox environment with proper ha... to "Executes a bash command and returns its output.\n\n- Working directo...
- ExecuteToolDescChinese: value changed from "\n在沙箱环境中执行给定命令,具有适当的处理和安全措施。\n\n执行命令前,请按照以下步骤操作:\n\n1. 目录验证:\n- 如果命令... to "执行一条 bash 命令并返回其输出。\n\n- 工作目录在多次调用间保持,但优先使用绝对路径 —— 复合命令中的
cd可能触发权... - GlobToolDesc: value changed from "Fast file pattern matching tool that works with any codebase size\n-... to "- Fast file pattern matching tool that works with any codebase size...
- GlobToolDescChinese: value changed from "适用于任何代码库大小的快速文件模式匹配工具\n- 支持 glob 模式,如 "/*.js" 或 "src//.ts"... to "- 适用于任何代码库规模的快速文件模式匹配工具\n- 支持形如 "**/.js" 或 "src/**/*.ts" 的 glob...
- GrepToolDesc: value changed from "\nA powerful search tool built on ripgrep\n\n Usage
- GrepToolDescChinese: value changed from "\n基于 ripgrep 的强大搜索工具\n\n 使用方法:\n - 始终使用 Grep 进行搜索任务。不要将 'grep' 或 '... to "基于 ripgrep 的强大搜索工具\n\n 使用方法:\n - 搜索任务始终使用 Grep。不要将
grep或rg作... - MiddlewareConfig: old is comparable, new is not
- ReadFileToolDesc: value changed from "Reads a file from the filesystem. You can access any file directly b... to "Reads a file from the local filesystem.\n\n-
file_pathmust be an ... - ReadFileToolDescChinese: value changed from "从文件系统读取文件。你可以使用此工具直接访问任何文件。\n假设此工具能够读取机器上的所有文件。如果用户提供了文件路径,假设该路径是有效的... to "从本地文件系统读取文件。\n\n-
file_path必须是绝对路径。\n- 默认最多读取 2000 行。\n- 当你已经知道需要... - WriteFileToolDesc: value changed from "Writes a file to the local filesystem.\n\nUsage
- WriteFileToolDescChinese: value changed from "将文件写入本地文件系统。\n\n使用方法:\n- 如果提供的路径已存在文件,此工具将覆盖现有文件\n- 如果这是一个现有文件,你必须先使... to "将文件写入本地文件系统,若已存在则覆盖。\n\n何时使用:创建新文件,或完全替换一个你已 Read 过的文件。覆盖你尚未 Read 过的...
Review Guidelines
Please ensure that:
- The changes are absolutely necessary
- They are properly documented
- Migration guides are provided if needed
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "errors" |
There was a problem hiding this comment.
🚨 Breaking API Changes Detected
Package: github.com/cloudwego/eino/adk
Incompatible changes:
- **(TypedBaseChatModelAgentMiddleware[github.com/cloudwego/eino/schema.Message]).BeforeAgent: changed from func(context.Context, *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error) to func(context.Context, *TypedChatModelAgentContext[*github.com/cloudwego/eino/schema.Message]) (context.Context, *TypedChatModelAgentContext[*github.com/cloudwego/eino/schema.Message], error)
- *(TypedBaseChatModelAgentMiddleware[M]).BeforeAgent: changed from func(context.Context, *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error) to func(context.Context, *TypedChatModelAgentContext[M]) (context.Context, *TypedChatModelAgentContext[M], error)
- ChatModelAgentContext: changed from ChatModelAgentContext to TypedChatModelAgentContext[*github.com/cloudwego/eino/schema.Message]
- *TypedChatModelAgentMiddleware[github.com/cloudwego/eino/schema.Message].BeforeAgent: changed from func(context.Context, *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error) to func(context.Context, *TypedChatModelAgentContext[*github.com/cloudwego/eino/schema.Message]) (context.Context, *TypedChatModelAgentContext[*github.com/cloudwego/eino/schema.Message], error)
- TypedChatModelAgentMiddleware[M MessageType].BeforeAgent: changed from func(context.Context, *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error) to func(context.Context, *TypedChatModelAgentContext[M]) (context.Context, *TypedChatModelAgentContext[M], error)
- TypedChatModelAgentMiddleware[M].BeforeAgent: changed from func(context.Context, *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error) to func(context.Context, *TypedChatModelAgentContext[M]) (context.Context, *TypedChatModelAgentContext[M], error)
- TypedChatModelAgentMiddleware[M].BeforeAgent: changed from func(context.Context, *ChatModelAgentContext) (context.Context, *ChatModelAgentContext, error) to func(context.Context, *TypedChatModelAgentContext[M]) (context.Context, *TypedChatModelAgentContext[M], error)
Review Guidelines
Please ensure that:
- The changes are absolutely necessary
- They are properly documented
- Migration guides are provided if needed
| "reduction", | ||
| "schema", | ||
| "session", | ||
| "skill", |
There was a problem hiding this comment.
🚨 Breaking API Changes Detected
Package: github.com/cloudwego/eino/adk/middlewares/summarization
Incompatible changes:
- *github.com/cloudwego/eino/adk.(TypedBaseChatModelAgentMiddleware[M]).BeforeAgent, method set of TypedMiddleware: removed
- *github.com/cloudwego/eino/adk.(TypedBaseChatModelAgentMiddleware[M]).BeforeAgent: changed from func(context.Context, *github.com/cloudwego/eino/adk.ChatModelAgentContext) (context.Context, *github.com/cloudwego/eino/adk.ChatModelAgentContext, error) to func(context.Context, *github.com/cloudwego/eino/adk.TypedChatModelAgentContext[M]) (context.Context, *github.com/cloudwego/eino/adk.TypedChatModelAgentContext[M], error)
Review Guidelines
Please ensure that:
- The changes are absolutely necessary
- They are properly documented
- Migration guides are provided if needed
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1358 +/- ##
==========================================
- Coverage 83.97% 82.26% -1.72%
==========================================
Files 162 216 +54
Lines 24181 36662 +12481
==========================================
+ Hits 20307 30160 +9853
- Misses 2615 4362 +1747
- Partials 1259 2140 +881 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Release overview
This PR merges
alpha/10intomain, extending ADK with persistent conversation timelines, independently managed background work, and long-term memory.The main capabilities are:
Supporting changes align middleware, message handling, serialization, and Compose execution with these capabilities. The feature inventory below covers alpha-side changes; main-only changes and behavior already present on
mainare excluded from the summary.What changes
Persistent sessions and turn execution
Add typed session event logs with persistence, pagination, replay, message mutations, and rollback, along with file and in-memory store implementations. Runner integrates session history with message IDs, turn correlation, usage, and model/tool attempt events.
TurnLoop execution is reorganized around run, stop, preemption, and checkpoint handling. Related fixes address cancellation/resume behavior, tool-result ordering across turns, terminal stream errors, and checkpoint memory amplification. These changes make agreement between live state and replay a central runtime contract.
Background tasks and reusable sub-agents
Add task management and store protocols for lifecycle transitions, leases, heartbeats, recovery, and parent notifications, plus middleware for querying and stopping tasks.
Execution adapters cover local work, managed tools, Shell commands, and sub-agents. Foreground/background handoff, progress projection, durable input requests, and worker handoff have explicit boundaries. Recovery depends on the configured store and executor capabilities; external tools must satisfy the relevant idempotency and checkpoint contracts.
Reusable sub-agent middleware supports independent sessions and foreground/background execution. Deep Agent composition uses the new sub-agent and task-control components. Filesystem and Shell changes add incremental output, multi-modal file handling, long-running Bash integration, and backend-owned command timeouts.
Long-term memory
AutoMemory adds memory indexing, topic selection, synchronous/asynchronous injection, and post-conversation extraction, with configurable backends and prompts. Asynchronous injection and persisted replay use the same ordering semantics.
Dream adds manual and scheduled memory consolidation, session-touch tracking, coordination locks, optional session-history search, and custom handlers.
Middleware and context consistency
Skill, ToolSearch, and AGENTS.md reminders become part of persisted history. Skill refresh and reminder customization improve how these instructions are maintained across turns.
Reduction, Summarization, and tool-history repair emit or preserve the history changes needed for replay. Related improvements retain message metadata, reuse summarization prompt prefixes, preserve loaded skills, and repair orphaned or duplicate tool results. PlanTask tool descriptions are also refined.
Shared APIs and execution foundations
Add model response-format options and shared JSON/Gob serialization. Extend typed agent contexts and tool direct-return controls, including a compatibility alias for the default
ChatModelAgentContextspelling.Schema changes improve streamed concatenation, retain tool-result metadata, and prevent media formatting from mutating shared templates. Compose changes refine tool-node middleware and interruption/checkpoint behavior, make
WithForceNewRunindependent of option order, and return a validation error for missing Workflow branch endpoints.Review map
The boundaries combine package ownership with end-to-end functionality: use a package where its public interface has a distinct responsibility, and group files across packages where one runtime behavior needs to be reviewed together.
adk/session*.go,adk/session/,adk/runner.go,adk/turn_loop*.go,adk/interrupt.goadk/chatmodel.go,adk/handler.go,adk/runctx.go,adk/wrappers.go, retry/failover implementationsadk/backgroundtask/,adk/backgroundtask/local/,adk/backgroundtask/tool/,adk/middlewares/backgroundtask/adk/agent_tool.go,adk/middlewares/subagent/,adk/backgroundtask/subagent/,adk/filesystem/,adk/middlewares/filesystem/,adk/backgroundtask/shell/,adk/prebuilt/deep/adk/middlewares/automemory/,adk/middlewares/automemory/dream/adk/middlewares/, including shared reminder insertion helpersschema/,components/model/option.go,internal/serialization/,compose/Checks across module boundaries
Compatibility and validation
Review public API and configuration changes together with runtime behavior, particularly typed/default aliases, Session/store implementations, recoverable tool contracts, filesystem backends, and Deep Agent configuration. The restored
ChatModelAgentContextalias has downstream-facing regression coverage; this does not establish compatibility for every changed API.The branch adds regression, integration, and store/executor conformance tests for persistence, replay, background execution, and recovery. No additional local tests were run while preparing this release PR. Module review, API compatibility assessment, and validation of the final merge result remain pending. Assess this PR's CI results for race-enabled tests, supported Go versions, benchmarks, lint, and API differences before merging.