Repository navigation
Conversation
TextMessageChunkEvent.role is None on every chunk except the one that opens a message, so continuation chunks fell through to the unexpected role branch and their delta was discarded, truncating the collected response. Chunks that repeat the open message_id, or omit it, now extend that message and inherit its role instead of being dropped.
|
Thanks for this. I wrote the original AG-UI integration (#2395), reproduced this defect on The bug (confirmed): Validation (exact patch at
Edge cases I tested beyond the PR's tests, all handled correctly:
Requested follow-up: close the open chunk message at stream boundaries. Tracking only one open chunk message is correct. Per the AG-UI spec (Closing a chunk stream), a chunk that opens a different message closes the previous one. The spec also says the open chunk stream closes when:
Strictly, a chunk with no
A regression test for the tool-call row would cover it. @anistark, you reviewed and merged the original integration. Could you take a look when you have a chance, or point us to the current maintainer for — Mark Edit: corrected my earlier note on |
|
Thanks @contextablemark — this is an exceptionally thorough validation, and the boundary table made the gap easy to see. Pushed
One behavior note: after a boundary, a chunk with no Verified: 49 passed on both Also noting for @anistark: the |
|
Thanks for addressing the stream-boundary request in On your question about chunks arriving without an ID after a boundary: the current AG-UI 1.0 streaming spec requires an opening text chunk to carry
Those behaviors mean I would not describe the handler as fully protocol-compliant. However, strict rejection may reasonably belong in a separate follow-up, rather than expanding this truncation/boundary fix into a broader validation change. From my perspective, I would not hold this fix up solely for that work, provided the remaining behavior is documented and tracked. A follow-up should surface malformed streams explicitly rather than silently discard text. The final scope decision belongs to the maintainers. I also inspected the failing One important clarification: I’m the author of the original AG-UI integration, not a Ragas maintainer. My comments are contributor review and validation, not merge authority. Unfortunately, the project appears to have stalled on maintenance: the latest merge I could verify was February 24, 2026, with none in the last 90 days despite ongoing contributor activity. I don’t know whether that is a pause or a handover, so I can’t promise when this will receive maintainer attention. I support the updated fix for the reported truncation and boundary defects. Thanks for responding so thoroughly. — Mark |
|
Recorded both cases in the PR description, under "Known limitation (not addressed here)": a Head is still
|
Issue Link / Problem Description
No tracking issue — reporting the defect directly, per the contributing guide.
TextMessageChunkEventis the AG-UI shorthand that "stands in for a start, content and end sequence, for producers that cannot know in advance where a message begins" (its own model docstring). Every field on it is optional:roleis documented as "Who the message is from, on the chunk that opens it",message_idas "The message this chunk belongs to. Absent continues the message already open", anddeltacarries the fragment to append.AGUIEventCollector._handle_text_message_chunktreated each chunk as a complete, self-contained message. It read the role withgetattr(event, "role", "assistant")— a default that never applies, because the field exists and isNoneon continuation chunks — so any chunk after the first fell through to the unexpected-role branch, logged a warning, and itsdeltawas discarded. A streamed response was therefore truncated to its first chunk.Reproduction on the current
main:Changes Made
src/ragas/integrations/ag_ui.py_handle_text_message_chunknow keeps the position of the chunk message currently open (_active_text_chunk, reset toNoneinclear()). A chunk that repeats thatmessage_id, or omitsmessage_identirely, appends itsdeltato that open message and returns, so the message keeps the role it was opened with.message_id, or the first chunk of a stream, still opens a message exactly as before. Defaultingroleto"assistant"moved after the continuation check, so it only applies when a message is actually opened — the fallback the code always intended.rolevalue at append time made theAIMessage/HumanMessageconstruction identical in both branches; the messages are appended once at the end and the open-chunk position recorded. No behaviour change for single-chunk events (the documented example) or formetadata=True.Not changed: the
TextMessageStart/Content/Endtriad path,ToolCallChunk,MessagesSnapshot, andextract_response.Testing
How to Test
Three regression tests in
tests/unit/integrations/test_ag_ui.py, next to the existingtest_text_message_chunk:test_text_message_chunk_continuation_is_merged_into_open_message— three chunks sharing onemessage_id, role only on the first; asserts oneAIMessagewhose content is the full concatenation and thatextract_responsereturns it.test_text_message_chunk_without_message_id_continues_open_message— a producer that omitsmessage_idon later chunks.test_text_message_chunk_continuation_keeps_opening_role— arole="user"message keepsHumanMessagetype through its continuation chunk.Results, all run with
./.venv/bin/python -m pytest -p no:randomly -q:pytest tests/unit/integrations/test_ag_ui.py3 failed, 43 passed in 0.13s46 passed in 0.35spytest tests/unit/integrations73 passed in 0.08spytest tests/unit(4 environment-broken modules excluded)789 passed, 62 skipped789 passed, 62 skippedruff format --check/ruff checkon the two changed files2 files already formatted/All checks passed!Unpatched failure for the first test:
Local sandbox notes (not attributable to this change):
test_chrf_score.py,test_import.pyandtest_prechunked_generation.pyneedsacrebleu/haystackthat are not installed here, andTestVersionExperimentneeds a git remote layout absent in this worktree; all of them fail identically on the unpatched tree.pyrightreports 6 errors inintegrations/langsmith.py,tracing/langfuse.pyandtracing/mlflow.pyon both trees, none inag_ui.py.References
MessagesSnapshot, separate defect)ag_ui/_generated/models.pyTextMessageChunkEventfield docs (message_id,role,delta)docs/howtos/integrations/ag_ui.md, "Working directly with AG-UI events"Screenshots/Examples (if applicable)
Patch this before / after:
Known limitation (not addressed here)
Two malformed-stream cases from the AG-UI streaming spec are tolerated rather than rejected, and are outside this fix:
TEXT_MESSAGE_CHUNKwith nomessage_idthat arrives after a closing event opens a new assistant message. The spec wants an opening chunk to carrymessageId; the text is kept and the closed message stays intact.message_idthat declares a differentrolekeeps the role the message was opened with and ignores the conflicting value.Both need explicit stream validation, which changes behavior for streams that currently produce a response, so they belong in a separate change with their own tests.
contextablemark, who wrote the original integration, reviewede27853fand asked for this to be recorded rather than folded into the fix.