feat(adk): integrate AgenticMessage into ADK#920
Open
shentongmartin wants to merge 1 commit intoalpha/09from
Open
feat(adk): integrate AgenticMessage into ADK#920shentongmartin wants to merge 1 commit intoalpha/09from
shentongmartin wants to merge 1 commit intoalpha/09from
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## alpha/09 #920 +/- ##
===========================================
Coverage ? 82.25%
===========================================
Files ? 161
Lines ? 21471
Branches ? 0
===========================================
Hits ? 17660
Misses ? 2590
Partials ? 1221 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
4fcbbd1 to
47a2eb2
Compare
84bf882 to
eb66867
Compare
9546746 to
93fa7f4
Compare
97cb63d to
136e9c6
Compare
shentongmartin
commented
Apr 13, 2026
shentongmartin
added a commit
that referenced
this pull request
Apr 13, 2026
- Restore pre-execution cancel check comments in chatmodel.go - Replace manual CompositeInterrupt with TypedCompositeInterrupt[M] - Delete ComponentOfAgenticAgent (not deprecate) - Revert OnSubAgents to concrete interface, add internal generic variant - Migrate gob.Register to schema.RegisterName for agentic types - Fix runner.go comment to reflect flowAgent wrapping - Remove genAgenticTransferMessages from utils.go and all call-sites - Revert workflow.go to concrete types, extract generic to workflow_typed.go - Remove construction-time failover rejection for AgenticMessage - Genericize failover code to support AgenticMessage - Fix typedConsumeStream to preserve partial messages on mid-stream errors - Revert AgentOption to concrete type, keep internal typed variant Change-Id: I643d54219696be9ba0b9ed01d5d1be5fa87ff4af
4c6d7e9 to
26b0af2
Compare
shentongmartin
commented
Apr 13, 2026
shentongmartin
added a commit
that referenced
this pull request
Apr 13, 2026
- failover_chatmodel: replace any-typed failoverCurrentModel with typedSetFailoverCurrentModel/typedGetFailoverCurrentModel context funcs - failover_chatmodel: restore removed comments in Generate/Stream methods - react: restore State doc comment, remove unnecessary AgentInput registration, move AgenticMessage registrations to schema/serialization.go - flow: revert genericization of flowAgent back to concrete alpha/09 code, keep typedFlowAgent[M] as separate independent generic struct - workflow: remove TypedSubAgentEvents and TypedOrigInput fields - workflow_typed: delete entire file (workflow agents only support *schema.Message) - runctx: remove genericized lane event functions, simplify addTypedEvent/getTypedEvents - tests: delete 11 agentic workflow test functions referencing removed types, update failover_chatmodel_test to use typed API Change-Id: I0326ceccdd3fb2237de51a741cc919d28642e2f3
shentongmartin
added a commit
that referenced
this pull request
Apr 14, 2026
- Restore pre-execution cancel check comments in chatmodel.go - Replace manual CompositeInterrupt with TypedCompositeInterrupt[M] - Delete ComponentOfAgenticAgent (not deprecate) - Revert OnSubAgents to concrete interface, add internal generic variant - Migrate gob.Register to schema.RegisterName for agentic types - Fix runner.go comment to reflect flowAgent wrapping - Remove genAgenticTransferMessages from utils.go and all call-sites - Revert workflow.go to concrete types, extract generic to workflow_typed.go - Remove construction-time failover rejection for AgenticMessage - Genericize failover code to support AgenticMessage - Fix typedConsumeStream to preserve partial messages on mid-stream errors - Revert AgentOption to concrete type, keep internal typed variant Change-Id: I643d54219696be9ba0b9ed01d5d1be5fa87ff4af
shentongmartin
added a commit
that referenced
this pull request
Apr 14, 2026
- failover_chatmodel: replace any-typed failoverCurrentModel with typedSetFailoverCurrentModel/typedGetFailoverCurrentModel context funcs - failover_chatmodel: restore removed comments in Generate/Stream methods - react: restore State doc comment, remove unnecessary AgentInput registration, move AgenticMessage registrations to schema/serialization.go - flow: revert genericization of flowAgent back to concrete alpha/09 code, keep typedFlowAgent[M] as separate independent generic struct - workflow: remove TypedSubAgentEvents and TypedOrigInput fields - workflow_typed: delete entire file (workflow agents only support *schema.Message) - runctx: remove genericized lane event functions, simplify addTypedEvent/getTypedEvents - tests: delete 11 agentic workflow test functions referencing removed types, update failover_chatmodel_test to use typed API Change-Id: I0326ceccdd3fb2237de51a741cc919d28642e2f3
78b08ff to
116f95a
Compare
a9f222e to
6834a39
Compare
shentongmartin
commented
Apr 14, 2026
f20ee8f to
9661935
Compare
shentongmartin
added a commit
that referenced
this pull request
Apr 14, 2026
- Restore pre-execution cancel check comments in chatmodel.go - Replace manual CompositeInterrupt with TypedCompositeInterrupt[M] - Delete ComponentOfAgenticAgent (not deprecate) - Revert OnSubAgents to concrete interface, add internal generic variant - Migrate gob.Register to schema.RegisterName for agentic types - Fix runner.go comment to reflect flowAgent wrapping - Remove genAgenticTransferMessages from utils.go and all call-sites - Revert workflow.go to concrete types, extract generic to workflow_typed.go - Remove construction-time failover rejection for AgenticMessage - Genericize failover code to support AgenticMessage - Fix typedConsumeStream to preserve partial messages on mid-stream errors - Revert AgentOption to concrete type, keep internal typed variant Change-Id: I643d54219696be9ba0b9ed01d5d1be5fa87ff4af
shentongmartin
added a commit
that referenced
this pull request
Apr 14, 2026
- failover_chatmodel: replace any-typed failoverCurrentModel with typedSetFailoverCurrentModel/typedGetFailoverCurrentModel context funcs - failover_chatmodel: restore removed comments in Generate/Stream methods - react: restore State doc comment, remove unnecessary AgentInput registration, move AgenticMessage registrations to schema/serialization.go - flow: revert genericization of flowAgent back to concrete alpha/09 code, keep typedFlowAgent[M] as separate independent generic struct - workflow: remove TypedSubAgentEvents and TypedOrigInput fields - workflow_typed: delete entire file (workflow agents only support *schema.Message) - runctx: remove genericized lane event functions, simplify addTypedEvent/getTypedEvents - tests: delete 11 agentic workflow test functions referencing removed types, update failover_chatmodel_test to use typed API Change-Id: I0326ceccdd3fb2237de51a741cc919d28642e2f3
c414236 to
80ada2f
Compare
shentongmartin
commented
Apr 14, 2026
9661935 to
d7161f7
Compare
b060ded to
46e35e4
Compare
Change-Id: Icbac619b1acfccb83fc48b9f56621bdd3f284bcd
46e35e4 to
74605e1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Integrate AgenticMessage into ADK
Problem
The Eino framework only supported
*schema.Messageas the message type for agent communication. To support richer agentic interactions — including structured content blocks, MCP tool calls, multi-modal content, and vendor-specific extensions — the framework needs a new message type (*schema.AgenticMessage) integrated across all layers: schema definition, component interfaces, compose graph, ADK agent runtime, callbacks, and checkpoint serialization.Solution
This PR introduces
*schema.AgenticMessageas a first-class message type across the entire Eino stack, parameterized via theMessageTypeconstraint (*schema.Message | *schema.AgenticMessage).Schema Layer
AgenticMessagewith structuredContentBlockvariants (text, tool call, tool result, MCP operations, images, etc.)AgenticMessageUserAgenticMessage,AssistantAgenticMessage,ToolAgenticMessage, etc.schema/openai,schema/claude,schema/geminifor provider-specific metadataGobEncode/GobDecodeonMCPListToolsItemto handlejsonschema.Schema(which contains unexportedorderedmap.OrderedMapfields)Component Layer
model.BaseModel[M]generic interface unifyingBaseChatModelandAgenticModelvia type aliases (backward compatible)prompt.AgenticChatTemplatefor agentic prompt templatesAgenticCallbackInput/Output) and prompt componentsComponentOfAgenticModel,ComponentOfAgenticPromptconstantsCompose Layer
AgenticToolsNodefor executing agentic tool calls within compose graphsComponentOfAgenticToolsNodeconstantADK Layer
TypedAgent[M],TypedChatModelAgent[M],TypedRunner[M]— all core types parameterized byMessageTypeAgent,ChatModelAgent,Runner, etc.) default to*schema.Messagefor backward compatibilityTypedAgentTool[M]for wrapping agents as tools, now supporting both*schema.Messageand*schema.AgenticMessageinfullChatHistoryAsInputmodeTypedCancellable[M]) with preemptive and cooperative cancellationTurnLoop[T, M MessageType]— push-based streaming event loop now generic over both item typeTand message typeM, enabling orchestration of*schema.AgenticMessageagentsagentsmdmiddleware for auto-injecting Agents.md into model context*schema.AgenticMessagevariantstypedStateis unexported — users interact with it viaChatModelAgentStatein middleware callbacks or theStatealiasCallbacks
ComponentOfAgenticAgent,AgenticAgentCallbackInput/Output,AgenticAgentCallbackHandlerinHandlerHelper— the fullOnStart/OnEndcallback pipeline now fires for agentic agentsAgenticAgentCallbackHandlerdeliberately omitsOnError— the ADK runtime does not fireOnErrorfor agent callbacks (same as non-agenticAgentCallbackHandler), so including it would be dead codeDecisions
Why
TypedAgent[M]with type aliases instead of a unified generic-only API?Go's callback system uses
interface{}(CallbackInput/CallbackOutput), so generics can't enforce type safety at callback boundaries. Type aliases (type Agent = TypedAgent[*schema.Message]) let existing code compile unchanged while new code can opt intoTypedAgent[*schema.AgenticMessage]. Each component's callback layer uses distinct agentic-variant types (e.g.,AgenticAgentCallbackHandler) rather than generic callback types, matching the pattern used by model, prompt, and tools node callbacks.Why custom
GobEncodeonMCPListToolsIteminstead of JSON for the whole message?The
anyfields inAgenticMessagecontent blocks carry concrete types that would lose type information through JSON round-tripping. By targetingGobEncode/GobDecodeat the narrowest problematic type (MCPListToolsItem, which contains*jsonschema.Schemawith unexportedorderedmap.OrderedMapfields), we preserve native gob encoding for all other fields.Why add
M MessageTypedirectly to TurnLoop instead of using theTyped*+ alias pattern?TurnLoop is unreleased API. Adding
M MessageTypedirectly to the existing types (TurnLoop[T, M]instead ofTypedTurnLoop[T, M]+type TurnLoop = TypedTurnLoop[T, *schema.Message]) avoids unnecessary API surface. The asymmetry with other ADK types (which useTyped*+ alias) is acceptable for unreleased API since there are no backward compatibility constraints.Why no
OnErroronAgenticAgentCallbackHandler?The ADK runtime (
flow.go) never firesTimingOnErrorfor agent callbacks — neither for*schema.Messageagents nor*schema.AgenticMessageagents.AgentCallbackHandleromitsOnErrorfor this reason;AgenticAgentCallbackHandlermatches this by also omitting it, rather than declaring a dead field that would never be called.Why no convenience constructors (
NewRunnerFrom,NewChatModelAgentFrom,New*AgentFrom)?These were thin wrappers over the
Typed*constructors with minimal value — users only save one line at the cost of additional API surface. Removing them keeps the API minimal. Users useNewTypedChatModelAgent[M]/NewTypedRunner[M]/NewTypedSequentialAgent[M]etc. directly.Why is
TypedStateunexported (typedState)?TypedStateappears in zero exported function signatures or struct fields. It only exists to support theStatetype alias (type State = typedState[*schema.Message]). Exporting it would add to the API surface without enabling any new use case — users interact with state throughChatModelAgentStatein middleware callbacks, not throughTypedStatedirectly.Key Insight
The ADK's
flow.gouses a two-phase type dispatch: compile-time generics (TypedAgent[M]) handle agent execution, while runtime type checks (if _, ok := any(zero).(*schema.Message); ok) bridge to the non-generic callback system. Every such guard is a load-bearing dispatch point — a missingelsebranch silently disables the entire callback pipeline for agentic agents. The correct pattern is to always add the*schema.AgenticMessagebranch whenever adding*schema.Messagecallback logic.This same pattern appears in agent tools: the
fullChatHistoryAsInputcode path inagent_tool.godispatches on message type to retrieve chat history from the correct typed state. Each dispatch point must handle both message types, or the feature silently fails for one type.Summary
*schema.AgenticMessagewith typed content blocks, concat, vendor extensions*schema.MessageBaseModel[M],AgenticChatTemplate,AgenticToolsNodewith backward-compatible type aliases*schema.AgenticMessageTypedAgent[M],TypedRunner[M]and all supporting types parameterized byMessageTypeComponentOfAgenticAgent+AgenticAgentCallbackHandler+elsebranches in allflow.godispatch pointsAgenticMessagewithjsonschema.SchemaGobEncode/GobDecodeonMCPListToolsItemTypedCancellable[M]with preemptive and cooperative cancellationagentsmdmiddlewarefullChatHistoryAsInputfails for agentic agentscase *schema.AgenticMessage:branch withgetAgenticReactChatHistory*schema.MessageagentsTurnLoop[T, M MessageType]— all types parameterized by message typeAgenticAgentCallbackHandler.OnErrorwas dead codeOnErrorfor agent callbacksNewRunnerFrom,NewChatModelAgentFrom,New*AgentFrom— users useTyped*constructors directlyTypedStateexported without external usagetypedState— users access state viaStatealias orChatModelAgentStatein middleware将 AgenticMessage 集成到 ADK 中
问题
Eino 框架仅支持
*schema.Message作为 Agent 通信的消息类型。为了支持更丰富的 agentic 交互——包括结构化内容块、MCP 工具调用、多模态内容和厂商特定扩展——框架需要一种新的消息类型(*schema.AgenticMessage),并将其集成到所有层:schema 定义、组件接口、compose 图、ADK agent 运行时、回调和检查点序列化。方案
本 PR 将
*schema.AgenticMessage作为一等消息类型引入整个 Eino 技术栈,通过MessageType约束(*schema.Message | *schema.AgenticMessage)进行参数化。Schema 层
ContentBlock变体(文本、工具调用、工具结果、MCP 操作、图片等)的AgenticMessageAgenticMessage的完整流式 concat 支持UserAgenticMessage、AssistantAgenticMessage、ToolAgenticMessage等schema/openai、schema/claude、schema/geminiMCPListToolsItem上的自定义GobEncode/GobDecode处理jsonschema.Schema组件层
model.BaseModel[M]泛型接口,通过类型别名统一BaseChatModel和AgenticModel(向后兼容)prompt.AgenticChatTemplate用于 agentic 提示模板Compose 层
AgenticToolsNode用于在 compose 图中执行 agentic 工具调用ADK 层
TypedAgent[M]、TypedChatModelAgent[M]、TypedRunner[M]— 所有核心类型按MessageType参数化Agent、ChatModelAgent、Runner等)默认为*schema.Message,保持向后兼容TypedAgentTool[M]现已支持*schema.Message和*schema.AgenticMessage的fullChatHistoryAsInput模式TurnLoop[T, M MessageType]— 基于推送的流式事件循环,现可编排*schema.AgenticMessageagentTypedCancellable[M])支持抢占式和协作式取消agentsmd中间件自动注入 Agents.md 到模型上下文typedState未导出 — 用户通过 middleware 回调中的ChatModelAgentState或State别名与之交互回调
ComponentOfAgenticAgent、AgenticAgentCallbackInput/Output、AgenticAgentCallbackHandler— 完整的OnStart/OnEnd回调管线AgenticAgentCallbackHandler刻意省略OnError— ADK 运行时不会为 agent 回调触发OnError(与非 agentic 的AgentCallbackHandler保持一致),包含它只会是死代码决策
为什么使用独立的
ComponentOfAgenticAgent而不是泛型化回调?回调系统通过
interface{}传递数据,泛型在回调边界无法提供类型安全。每个组件的回调层使用独立的 agentic 变体类型,与 model、prompt、tools node 的回调模式保持一致。为什么在
MCPListToolsItem上做自定义GobEncode而不是整个消息用 JSON?AgenticMessage中any字段携带的具体类型在 JSON 往返序列化中会丢失类型信息。通过将GobEncode/GobDecode精确定位到问题最窄的类型(MCPListToolsItem),其他字段保留原生 gob 编码。为什么直接给 TurnLoop 加
M MessageType而不用Typed*+ 别名模式?TurnLoop 是未发布 API。直接在现有类型上添加
M MessageType(TurnLoop[T, M])而不是创建TypedTurnLoop[T, M]+type TurnLoop = TypedTurnLoop[T, *schema.Message],可以避免不必要的 API 表面积。与其他 ADK 类型(使用Typed*+ 别名)的不对称性,对于未发布 API 是可以接受的。为什么
AgenticAgentCallbackHandler没有OnError?ADK 运行时(
flow.go)从不为 agent 回调触发TimingOnError— 无论是*schema.Messageagent 还是*schema.AgenticMessageagent。AgentCallbackHandler因此省略了OnError;AgenticAgentCallbackHandler也同样省略,而不是声明一个永远不会被调用的死字段。为什么没有便捷构造器(
NewRunnerFrom、NewChatModelAgentFrom、New*AgentFrom)?这些是
Typed*构造器的薄封装,收益极小 — 用户仅节省一行代码,却增加了 API 表面积。移除它们保持 API 最小化。用户直接使用NewTypedChatModelAgent[M]/NewTypedRunner[M]/NewTypedSequentialAgent[M]等。为什么
TypedState未导出(typedState)?TypedState未出现在任何导出函数签名或结构体字段中。它仅用于支持State类型别名(type State = typedState[*schema.Message])。导出它会增加 API 表面积而不带来任何新用例 — 用户通过 middleware 回调中的ChatModelAgentState与状态交互,而不是直接使用TypedState。核心洞察
ADK 的
flow.go使用两阶段类型分发:编译时泛型处理 agent 执行,运行时类型检查桥接到非泛型回调系统。每个*schema.Message守卫都是承载关键分发逻辑的点——缺少else分支会静默禁用该路径的整个回调管线。正确模式是:添加*schema.Message回调逻辑时,必须同时添加*schema.AgenticMessage分支。同样的模式出现在 agent tool 中:
agent_tool.go的fullChatHistoryAsInput代码路径根据消息类型分发,从正确的类型化状态中检索聊天历史。每个分发点都必须处理两种消息类型,否则功能会静默失效。摘要
*schema.AgenticMessage+ 类型化内容块、concat、厂商扩展*schema.MessageBaseModel[M]、AgenticChatTemplate、AgenticToolsNode+ 向后兼容类型别名*schema.AgenticMessageTypedAgent[M]、TypedRunner[M]等按MessageType参数化ComponentOfAgenticAgent+AgenticAgentCallbackHandler+flow.go分发点的else分支AgenticMessage的检查点序列化因jsonschema.Schema失败MCPListToolsItem上的精确GobEncode/GobDecodeTypedCancellable[M]支持抢占式和协作式取消agentsmd中间件fullChatHistoryAsInput对 agentic agent 失败case *schema.AgenticMessage:分支 +getAgenticReactChatHistory*schema.MessageagentTurnLoop[T, M MessageType]— 所有类型按消息类型参数化AgenticAgentCallbackHandler.OnError是死代码OnErrorNewRunnerFrom、NewChatModelAgentFrom、New*AgentFrom— 用户直接使用Typed*构造器TypedState导出但无外部使用typedState— 用户通过State别名或 middleware 中的ChatModelAgentState访问状态