feat(agui): support tool execution progress and fix stream delta loss#1240
feat(agui): support tool execution progress and fix stream delta loss#1240jujn wants to merge 2 commits intoagentscope-ai:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR enhances the AG-UI adapter’s streaming behavior by (1) optionally surfacing tool-execution progress in real time and (2) preventing “tail-end” deltas from being dropped when the terminating (isLast=true) packet still carries content.
Changes:
- Added
enableToolProgressStreamtoAguiAdapterConfigand wired it intoStreamOptions.includeActingChunk. - Updated
AguiAgentAdapter.convertEvent()to emit text/tool-args deltas even whenevent.isLast()is true, then close lifecycles afterward to avoid tail-data loss. - Added tests covering last-packet content preservation and tool progress streaming scenarios.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| agentscope-extensions/agentscope-extensions-agui/src/main/java/io/agentscope/core/agui/adapter/AguiAgentAdapter.java | Adds acting-chunk option plumbing; adjusts last-event handling; introduces tool-progress-to-reasoning mapping. |
| agentscope-extensions/agentscope-extensions-agui/src/main/java/io/agentscope/core/agui/adapter/AguiAdapterConfig.java | Adds new config flag and builder method for enabling tool progress stream. |
| agentscope-extensions/agentscope-extensions-agui/src/test/java/io/agentscope/core/agui/adapter/AguiAgentAdapterTest.java | Adds/updates tests for tail-end preservation and tool progress streaming behavior. |
| @@ -254,18 +252,57 @@ private List<AguiEvent> convertEvent(Event event, EventConversionState state) { | |||
| state.startToolCall(toolCallId); | |||
| } | |||
There was a problem hiding this comment.
toolResult.getId() can be null (e.g., ToolEmitter chunks / ToolResultBlock.text() start with null id and are only populated from ToolUseBlock id, which can also be null). In that case, constructing ToolCallStart/ToolCallEnd will throw because AG-UI events require non-null toolCallId. Please add a null-handling strategy here (e.g., infer the active toolCallId from state when unambiguous, or generate a stable fallback ID and keep it consistent across chunks) to avoid runtime NPEs when tool IDs are missing.
| // Start message if not started | ||
| if (!state.hasStartedMessage(messageId)) { | ||
| events.add( | ||
| new AguiEvent.TextMessageStart( | ||
| state.threadId, state.runId, messageId, "assistant")); | ||
| state.startMessage(messageId); | ||
| } | ||
|
|
||
| if (!event.isLast()) { | ||
| // In incremental mode, text is already the delta | ||
| events.add( | ||
| new AguiEvent.TextMessageContent( | ||
| state.threadId, state.runId, messageId, text)); | ||
| } else { | ||
| // End message if this is the last event | ||
| if (!state.hasEndedMessage(messageId)) { | ||
| events.add( | ||
| new AguiEvent.TextMessageEnd( | ||
| state.threadId, state.runId, messageId)); | ||
| state.endMessage(messageId); | ||
| } | ||
| } | ||
| events.add( | ||
| new AguiEvent.TextMessageContent( | ||
| state.threadId, state.runId, messageId, text)); |
There was a problem hiding this comment.
Text deltas are now always emitted even when event.isLast() is true. If a TextMessageEnd was already emitted earlier for the same messageId (e.g., because a ToolUseBlock ended the active text message), this can result in TextMessageContent being sent after TextMessageEnd with no subsequent restart/end, which is an invalid lifecycle for many consumers. Consider suppressing content emission for ended messages, or introducing a continuation strategy (e.g., generate a new messageId for post-tool text) so that content is never emitted after an end event.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Description
Close #1222
This PR introduces support for real-time tool execution progress and addresses the streaming data loss edge cases.
Key Changes
Support Tool Progress Stream: - Added
enableToolProgressStreaminAguiAdapterConfig.!event.isLast()) intoReasoningMessageevents to display real-time progress on the frontend without modifying the AG-UI protocol.Fix Tail-End Data Loss:
isLasttermination logic from the data extraction process inconvertEvent().Checklist
Please check the following items before code is ready to be reviewed.
mvn spotless:applymvn test)