Skip to content

Python: Preserve AG-UI citations through client and agent streaming - #9094

Open
quifox (quifox) wants to merge 3 commits into
microsoft:mainfrom
quifox:fix/agui-streaming-citations
Open

quifox (quifox) wants to merge 3 commits into
microsoft:mainfrom
quifox:fix/agui-streaming-citations

Conversation

@quifox

@quifox quifox (quifox) commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Motivation & Context

Late AG-UI citations can create an empty duplicate message in the default public streaming response instead of attaching to the earlier text. For example, m1/First followed by m2/Second and a citation for m1 becomes three messages, with the citation on the empty duplicate m1.

This remains after #8948 because the function-invocation stream and the mapped Agent.run stream each aggregate updates outside the AG-UI inner finalizer. Non-streaming controls preserve the expected two messages.

Description & Review Guide

  • What are the major changes? Add private aggregation hooks for the complete function loop and mapped agent updates, retaining generic aggregation by default. AGUIChatClient uses a shared annotation-aware algorithm with the appropriate response factory; the agent hook calls it directly. The existing chat finalizer remains shared by inner streaming, non-streaming, and whole-loop finalization. Pure annotation events retain response metadata without creating duplicate messages, while buffered text carrying copied event metadata remains ordinary text.
  • What is the impact of these changes? Citations remain attached to their referenced text through public client and Agent streaming, completed tool rounds, and buffered text transformations. Structured results and response metadata are preserved. Non-Base clients and custom Agent finalizers retain their existing paths. The client and converter test modules pass all 113 cases on Python 3.10 and 3.11; source checks, all five AG-UI test type checkers, and package builds also pass.
  • What do you want reviewers to focus on? Keep per-model-turn provider processing separate from whole-loop and mapped-agent aggregation. Check that mapped updates remain authoritative and that citations, call/result pairs, response fields, and transformed text remain consistent.

Related Issue

Fixes #9093

Follow-up to #8948, covering the remaining whole-loop and mapped-agent aggregation paths. The linked issue requests review of the private core extension points.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

The unit-test item remains unchecked because validation covers the affected modules; the full workspace matrix is left to CI.

Keep late annotations attached to their message through function-loop and
mapped-agent aggregation. Preserve response metadata and buffered middleware
output, with regression coverage for both public response paths.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The scoped aggregation changes preserve existing fallback behavior and are covered across the affected streaming paths.

Review effort: Balanced
Findings: None

What changed in this PR

Preserves late AG-UI citations across client, tool-loop, and agent streaming aggregation without creating duplicate messages.

Changes:

  • Adds private whole-loop and agent-level finalization hooks.
  • Applies annotation-aware aggregation at AG-UI streaming boundaries.
  • Adds regression coverage for metadata, tools, structured output, and middleware.
File Description
python/​packages/​core/​agent_framework/​_tools.py Adds whole-loop finalization hook.
python/​packages/​core/​agent_framework/​_clients.py Adds mapped-agent finalization hook.
python/​packages/​core/​agent_framework/​_agents.py Delegates default agent aggregation to compatible clients.
python/​packages/​ag-ui/​agent_framework_ag_ui/​_event_converters.py Generalizes annotation-aware aggregation for chat and agent responses.
python/​packages/​ag-ui/​agent_framework_ag_ui/​_client.py Implements AG-UI-specific finalization hooks.
python/​packages/​core/​tests/​core/​test_function_invocation_logic.py Tests whole-loop metadata and structured output.
python/​packages/​core/​tests/​core/​test_agents.py Tests client and agent extension-point precedence.
python/​packages/​ag-ui/​tests/​ag_ui/​test_event_converters.py Tests annotation-only metadata aggregation.
python/​packages/​ag-ui/​tests/​ag_ui/​test_ag_ui_client.py Adds public-path citation regressions.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread python/packages/ag-ui/agent_framework_ag_ui/_event_converters.py
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_client.py Outdated

This branch was previously deployed

1 inactive deployment
github-app-auth — e8ded2e2 Deployed Oct 7, 2026 by quifox via add_label #24669
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

3 participants