Skip to content

Python: Preserve AG-UI tool message IDs across snapshots - #7510

Draft
jstar0 wants to merge 2 commits into
microsoft:mainfrom
jstar0:fix/ag-ui-tool-message-ids
Draft

Python: Preserve AG-UI tool message IDs across snapshots#7510
jstar0 wants to merge 2 commits into
microsoft:mainfrom
jstar0:fix/ag-ui-tool-message-ids

Conversation

@jstar0

@jstar0 jstar0 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Motivation & Context

When an AG-UI run streams assistant text followed by a tool call, the stream uses the open text message ID as the tool call's parent. The final MESSAGES_SNAPSHOT represents text and tool calls as separate messages, but currently assigns the tool-call message a new ID. The reference AG-UI client merges snapshots by ID, so the tool call and its result are appended after later assistant text instead of remaining in their original position.

This affects any AG-UI frontend that follows the reference ID-based merge behavior and is reproducible with interleaved assistant text and tool calls.

Description & Review Guide

  • What are the major changes?
    • Allocate a message ID when a streamed tool-call segment starts.
    • Reuse that ID for ToolCallStartEvent.parent_message_id and the corresponding snapshot assistant message.
    • Add a regression test covering text, tool call, tool result, and trailing text.
  • What is the impact of these changes?
    • Streamed tool calls and final snapshots now describe the same message identity, preserving client-side ordering.
    • Tool-only turns and existing legacy snapshot fallbacks retain their current behavior.
  • What do you want reviewers to focus on?
    • Confirm that the segment ID allocation matches the AG-UI event/snapshot identity contract and does not merge tool calls into text messages.

Related Issue

Fixes #7491

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) — a workflow keeps the label and title prefix in sync automatically.

Copilot AI lite review requested due to automatic review settings August 4, 2026 15:56
@jstar0
jstar0 temporarily deployed to github-app-auth August 4, 2026 15:56 — with GitHub Actions Inactive
@jstar0
jstar0 temporarily deployed to github-app-auth August 4, 2026 15:56 — with GitHub Actions Inactive
@jstar0
jstar0 temporarily deployed to github-app-auth August 4, 2026 15:56 — with GitHub Actions Inactive
@agent-framework-automation agent-framework-automation Bot added the python Usage: [Issues, PRs], Target: Python label Aug 4, 2026
@jstar0
jstar0 temporarily deployed to github-app-auth August 4, 2026 15:56 — with GitHub Actions Inactive

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.

🟡 Not ready to approve

Some ToolCallStartEvent emitters in _run_common.py still parent tool calls to flow.message_id while snapshots use the newly allocated segment id, which can reintroduce the stream/snapshot ID mismatch for those tool-call paths.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR updates the Python AG-UI streaming/snapshot implementation so tool-call “assistant messages” can keep a stable identity between streamed events and the final MESSAGES_SNAPSHOT, preserving client-side ordering for interleaved text/tool-call turns.

Changes:

  • Allocate and track a dedicated message ID for streamed tool-call segments and use it as ToolCallStartEvent.parent_message_id.
  • Reuse the tracked tool-call segment ID when building the corresponding snapshot assistant tool_calls message.
  • Add a regression test asserting that the snapshot tool-call message ID matches the streamed tool-call parent message ID after leading text.
File summaries
File Description
python/packages/ag-ui/tests/ag_ui/test_run.py Adds regression coverage ensuring snapshot tool-call message IDs reuse the streamed tool-call parent ID.
python/packages/ag-ui/agent_framework_ag_ui/_run_common.py Tracks per-tool-call-segment message IDs during streaming and uses them for ToolCallStartEvent.parent_message_id.
python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Prefers the tracked tool-call segment ID when emitting snapshot tool_calls messages.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment on lines +522 to +526
def _track_tool_call_segment(flow: FlowState, tool_call_id: str) -> str:
"""Record a tool call and return the message ID used by its stream events."""
segment: dict[str, Any]
if flow.snapshot_segments and flow.snapshot_segments[-1]["kind"] == "tool_calls":
flow.snapshot_segments[-1]["call_ids"].append(tool_call_id)
segment = flow.snapshot_segments[-1]
@jstar0
jstar0 temporarily deployed to github-app-auth August 4, 2026 16:17 — with GitHub Actions Inactive
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

2 participants