Close the open AG-UI text message before emitting non-text events - #1056
Quim Muntal (qmuntal) merged 2 commits into
Conversation
When a text message was open and the next content was non-text (e.g. a FunctionCallContent tool call), the loop closed any open reasoning message but not the text message, so currentMessageID stayed set and TEXT_MESSAGE_END was only emitted at the end of the run. The tool-call lifecycle (TOOL_CALL_START/ ARGS/END) was therefore nested inside an unclosed text message, violating the file's invariant that AG-UI lifecycles are disjoint (reasoning is already closed before text starts). Close the open text message alongside reasoning before emitting non-text-like events.
There was a problem hiding this comment.
🟢 Approval recommended
No unresolved review issues; the fix includes regression coverage.
Pull request overview
Closes open AG-UI text messages before emitting non-text events, ensuring tool-call lifecycles are not nested inside text messages.
Changes:
- Close active text messages before non-text events.
- Add regression coverage for text followed by a tool call.
File summaries
| File | Description |
|---|---|
provider/aguiprovider/hosting_test.go |
Verifies correct text/tool-call event ordering. |
provider/aguiprovider/hosting_events.go |
Closes text lifecycles before non-text events. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Scope: user-visible behavior (internal helper change, observable via emitted AG-UI SSE event ordering) Changed Go contract: No exported API changed. Upstream evidence reviewed:
Result: aligned. This fix brings the Go implementation's disjoint-lifecycle invariant in line with the upstream Python AG-UI event converter, which already closes an open text message before/around tool-call events. The change is confined to unexported helpers with no exported API impact; the added test (
|
Problem
In
provider/aguiprovider/hosting_events.go, when a text message is open and the next content is non-text (e.g. aFunctionCallContenttool call), the loop closes any open reasoning message but not the text message:currentMessageIDstays set, soTEXT_MESSAGE_ENDis only emitted at the end of the run. The emitted order becomesTEXT_MESSAGE_START, TEXT_MESSAGE_CONTENT, TOOL_CALL_START, TOOL_CALL_ARGS, TOOL_CALL_END, TEXT_MESSAGE_END— the tool-call lifecycle is nested inside an unclosed text message. This violates the file's own invariant that AG-UI lifecycles are disjoint (reasoning is already explicitly closed before a text message starts).Fix
Close the open text message (alongside reasoning) before emitting non-text-like events.
Test
TestHandler_TextThenToolCall_ClosesTextBeforeToolLifecycleemits a text update then a tool-call update and asserts the lifecycle order isTEXT_MESSAGE_START/CONTENT/ENDthenTOOL_CALL_START/ARGS/END. Fails before the fix (TEXT_MESSAGE_ENDlands after the tool call), passes after.