Skip to content

feat(adk): add AfterAgent method to ChatModelAgentMiddleware#735

Open
shentongmartin wants to merge 6 commits intoalpha/08from
feat/after_agent
Open

feat(adk): add AfterAgent method to ChatModelAgentMiddleware#735
shentongmartin wants to merge 6 commits intoalpha/08from
feat/after_agent

Conversation

@shentongmartin
Copy link
Contributor

@shentongmartin shentongmartin commented Feb 2, 2026

Problem

Middleware authors need to perform cleanup operations after the ChatModelAgent completes execution. For example, during execution, a middleware might store temporary file paths in RunLocalValues. When the agent finishes, the middleware needs to delete these temporary files.

The challenge: GetRunLocalValue only works while the graph is executing. Once the graph exits, the state context is lost and RunLocalValues become inaccessible.

Solution

Add AfterAgent method to ChatModelAgentMiddleware interface:

AfterAgent(ctx context.Context, state *ChatModelAgentState) error

This method is called before the graph exits, so GetRunLocalValue still works. The state parameter contains all messages including the final one.

Graph topology for react path:

Before:
  ChatModel --[no tools]--> END
  ToolNodeToEndConverter --> END

After:
  ChatModel --[no tools]--> AfterAgentNode --> END
  ToolNodeToEndConverter --> AfterAgentNode --> END

Key Insight

The stateModelWrapper already appends the model response to State.Messages and writes it back via ProcessState before any post-processing. This means in AfterAgent, we can simply read State.Messages directly - no need to copy/concatenate the stream ourselves.

Initial implementation copied and concatenated the stream to get the final message:

streams := msgStream.Copy(2)
finalMsg, err := schema.ConcatMessageStream(streams[0])
state := &ChatModelAgentState{Messages: append(stateMessages, finalMsg)}

But this was redundant and would have duplicated the final message. The simplified implementation just reads from state:

var stateMessages []Message
compose.ProcessState(ctx, func(_ context.Context, st *State) error {
    stateMessages = st.Messages
    return nil
})
state := &ChatModelAgentState{Messages: stateMessages}

Summary

Problem Solution
Middleware cannot clean up RunLocalValues after agent execution because state is lost when graph exits Add AfterAgent method that is called before graph exits, while GetRunLocalValue still works

shentongmartin and others added 5 commits January 30, 2026 15:12
…722)

* feat: add enhanced tool support with multimodal output capabilities and improve message formatting

This commit introduces enhanced tool interfaces that support structured multimodal outputs,
enabling tools to return rich content beyond simple text responses.

Key Changes:

1. New Enhanced Tool Interfaces:
   - Added EnhancedInvokableTool and EnhancedStreamableTool interfaces for multimodal tool execution
   - Both interfaces use ToolCallInfo as input and return ToolResult for structured output

2. ToolResult Schema:
   - Introduced ToolResult type to represent multimodal tool outputs
   - Supports multiple content types: text, image, audio, video, and file
   - Added ToolOutputPart with Index field for streaming chunk merging
   - Implemented ToMessageInputParts() for seamless model integration

3. ToolsNode Enhancements:
   - Extended ToolsNode to support both legacy and enhanced tool types
   - Added automatic conversion between invokable and streamable endpoints
   - Implemented middleware support for enhanced tools
   - Enhanced interrupt and rerun mechanism to handle ToolResult

4. React Agent Integration:
   - Introduce enhancedToolResultSender and enhancedStreamToolResultSender types
   - Support sending *schema.ToolResult with multimodal content (images, audio, video, files)
   - Implement EnhancedInvokable and EnhancedStreamable middleware in tool result collector

5. Message.String() Enhancement:
   - Add formatting support for UserInputMultiContent, AssistantGenMultiContent, and MultiContent
   - Implement formatInputPart, formatOutputPart, and formatChatMessagePart helper functions
   - Create mediaPartFormatter interface with wrapper types for unified media formatting

6. User Input Multi-Content Concatenation:
   - Implement concatUserMultiContent function for merging MessageInputPart slices
   - Support text and base64 audio merging with proper MIME type handling
   - Integrate into ConcatMessages function

7. Callback System:
   - Added CallbackInput and CallbackOutput types for tool callbacks
   - Implemented conversion functions for different callback input/output types

8. Comprehensive Test Coverage:
   - Added tests for enhanced invokable and streamable tools
   - Added TestMessageString with 14 test cases covering various message types

Impact:
- Enables tools to return rich multimodal content (images, audio, video, files)
- Provides foundation for more sophisticated tool implementations
- Maintains full backward compatibility with existing tool ecosystem
Add AfterAgent method that allows middleware to perform cleanup operations
after the agent completes execution while RunLocalValues are still accessible.

Changes:
- Add AfterAgent method to ChatModelAgentMiddleware interface
- Add default no-op implementation to BaseChatModelAgentMiddleware
- Add hasAfterAgent field to handlerInfo for optimization
- Implement AfterAgent for no-tools path in chatmodel.go
- Implement AfterAgent for react path with shared afterAgentNode
- Add comprehensive tests for AfterAgent functionality~

Change-Id: I2164ae4c5ae7b2ce1cee5dbbc20f087008dd5fe7
@codecov
Copy link

codecov bot commented Feb 2, 2026

Codecov Report

❌ Patch coverage is 92.59259% with 4 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (alpha/08@6565ff4). Learn more about missing BASE report.

Files with missing lines Patch % Lines
adk/handler.go 33.33% 2 Missing ⚠️
adk/react.go 92.30% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             alpha/08     #735   +/-   ##
===========================================
  Coverage            ?   80.75%           
===========================================
  Files               ?      132           
  Lines               ?    13638           
  Branches            ?        0           
===========================================
  Hits                ?    11013           
  Misses              ?     1812           
  Partials            ?      813           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@shentongmartin shentongmartin changed the base branch from main to alpha/08 February 2, 2026 04:02
Change-Id: I6fba41e947c047a0874860b4c11cd2ff3daa7fc6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants