Skip to content

Python: [Bug]: Late AG-UI citations create duplicate messages through public streaming aggregation #9093

Description

@quifox

Description

Late message-linked citations can produce an empty duplicate assistant message through public AG-UI streaming aggregation.

After m1/First and m2/Second have completed, an annotations event targeting m1 produces three final messages: m1/First without its citation, m2/Second, and an empty m1 carrying the citation. This affects default AGUIChatClient.get_response(stream=True) and Agent(client=client).run(stream=True). Both non-streaming controls return the expected two messages.

Code Sample

A mocked AG-UI endpoint emits this ordinary SSE event sequence between RUN_STARTED and RUN_FINISHED:

[
  {"type":"TEXT_MESSAGE_START","messageId":"m1","role":"assistant"},
  {"type":"TEXT_MESSAGE_CONTENT","messageId":"m1","delta":"First"},
  {"type":"TEXT_MESSAGE_END","messageId":"m1"},
  {"type":"TEXT_MESSAGE_START","messageId":"m2","role":"assistant"},
  {"type":"TEXT_MESSAGE_CONTENT","messageId":"m2","delta":"Second"},
  {"type":"TEXT_MESSAGE_END","messageId":"m2"},
  {"type":"CUSTOM","name":"annotations","value":{"messageId":"m1","annotations":[{"type":"citation","url":"https://example.test/source"}]}}
]

After consuming either public response stream and awaiting get_final_response(), m1 should retain its citation without another m1 message.

Error Messages / Stack Traces

No exception is raised. The error is the final message list:

  • Expected: m1/First with its citation, m2/Second.
  • Actual: m1/First without its citation, m2/Second, m1/empty with the citation.

Package Versions

Verified from source at b9d24c8:

  • agent-framework-core: 1.20.0
  • agent-framework-ag-ui: 1.5.0

Python Version

Python 3.12.

Additional Context

This follows #8948 and #7460. The AG-UI inner finalizer handles late annotations by message ID, but the default function-loop stream and mapped Agent stream each perform separate generic aggregation outside that finalizer.

I have a focused fix prepared with regression coverage for both public paths, completed tool rounds, structured output, buffered middleware, and compatibility with existing client and Agent finalizers.

The proposed private hooks retain generic aggregation by default and let AG-UI attach its message-linked annotations at the whole-loop and mapped-agent boundaries. Provider per-model-turn finalizers remain unchanged, and mapped AgentResponseUpdate objects remain authoritative. Please review whether these extension points fit the core aggregation contracts.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

agentsUsage: [Issues, PRs], Target: Single agentpythonUsage: [Issues, PRs], Target: PythonreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions