Skip to content

Python: Support ag-ui-protocol 1.0 in agent-framework-ag-ui - #9378

Open
Markus Ecker (mme) wants to merge 4 commits into
microsoft:mainfrom
mme:mme/ag-ui-protocol-1.0
Open

Markus Ecker (mme) wants to merge 4 commits into
microsoft:mainfrom
mme:mme/ag-ui-protocol-1.0

Conversation

@mme

@mme Markus Ecker (mme) commented Oct 14, 2026 •

Copy link
Copy Markdown

Motivation & Context

agent-framework-ag-ui pins ag-ui-protocol>=0.1.19,<0.2, so it can't be installed alongside AG-UI 1.0, and with 1.0 installed the package fails 24 of its own tests: snapshots with media fail validation on replay, RUN_STARTED doesn't declare protocolVersion, media in tool results is lost, and a 1.0 file source is dropped silently. Details and a reproduction are in #9377.

Description & Review Guide

  • What are the major changes?
    • Allow ag-ui-protocol>=0.1.19,<2 and lock 1.0.0. 0.1.x keeps today's behaviour; every version-specific shape is gated on the installed SDK exposing PROTOCOL_VERSION (_AGUI_PROTOCOL_VERSION in _utils.py).
    • protocolVersion on every RUN_STARTED, through one helper, _build_run_started_event (all 25 construction sites).
    • MESSAGES_SNAPSHOT media in the 1.0 shape. 1.0 rejects the legacy binary part, so snapshots with media, including threads saved before an upgrade, failed on replay. Stored snapshot messages keep their current shape; snapshot_messages_for_wire rewrites them only where a snapshot event is built.
    • Tool-result media. A ContentPart[] tool result becomes the function result's Content items instead of the text of the raw parts list. This covers tool messages, tool media replayed from stored snapshots, and TOOL_CALL_RESULT events in AGUIChatClient. With 1.0, AGUIChatClient also sends that media back as content parts in later requests; MCP results keep their own handling. History deduplication and approval snapshot rebuilds treat an image-only result as output. In the other direction, a function result holding media (such as one AGUIChatClient built from a relayed TOOL_CALL_RESULT) goes out as content parts on TOOL_CALL_RESULT and in the snapshot tool message, so AgentFrameworkAgent relays it intact; approval-path result events do the same, and MCP results keep their Host-payload handling.
    • Workflow snapshots. 1.0 tool-result parts are stored as wire dictionaries (the SDK parses them into models), and a text message without a role defaults to assistant (1.0 no longer defaults it).
    • File sources follow the existing policy for unresolved client references: a 1.0 {"type": "file", "value": ...} source now gets the same rejection as an id source. Before, value wasn't read, so the part was dropped silently.
    • The task-steps example and the predictive-state tests read 1.0's modelled JSON Patch operations.
  • What is the impact of these changes? Users can install the package with AG-UI 1.0 clients and servers. Nothing changes for 0.1.x installs.
  • What do you want reviewers to focus on? The tool-result media change, since it touches several consumers of function results. I audited every reader of Content.result in the package; the ones that only see results of tools the server runs itself are unchanged.

Tests (new in tests/ag_ui/test_protocol_1_0.py, plus version-aware assertions in test_workflow_agent.py, test_predictive_state.py and test_run_common.py; the task-steps example has its own test file; two relay tests run an AgentFrameworkAgent over a stub agent and over an AGUIChatClient fed by a mock AG-UI server):

ag-ui-protocol pytest packages/ag-ui/tests
1.0.0 1794 passed
0.1.22 1788 passed, 6 skipped (1.0-only)
0.1.19 1788 passed, 6 skipped (1.0-only)

poe check -P ag-ui passes on 1.0.0 (ruff format and check, pyrefly, mypy, zuban, ty, pyright, tests). The new tests fail on main with 1.0 installed.

Related Issue

Fixes #9377

AI Assistance

  • No material AI assistance was used.
  • This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.

AI assistance details: implementation, tests and this PR description were written with Claude Code; the change was also reviewed with Codex.

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 links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
  • 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.

Allow ag-ui-protocol 1.x (>=0.1.19,<2) and lock 1.0.0. With 1.0 installed
the package now passes its whole test suite; 0.1.x keeps its behaviour.

- Declare protocolVersion on every RUN_STARTED through one helper,
  _build_run_started_event, gated on the installed SDK exposing
  PROTOCOL_VERSION, so 0.1.x keeps emitting the event it does today.
- Send media in MESSAGES_SNAPSHOT in the 1.0 shape when 1.0 is installed:
  1.0 rejects the legacy binary part, so snapshots with media, including
  threads saved before an upgrade, failed validation on replay. Stored
  snapshot messages keep their current shape.
- Pass media in a ContentPart[] tool result on as the function result's
  Content items instead of the text of the raw parts list: for tool
  messages, for tool media replayed from a stored snapshot, and for
  TOOL_CALL_RESULT events in AGUIChatClient. With 1.0, AGUIChatClient also
  sends such media back as content parts in later requests (MCP results
  keep their own handling). History deduplication and approval snapshot
  rebuilds treat an image-only result as output and keep its media.
- Workflow snapshots: store 1.0 tool-result parts as wire dictionaries,
  and default a text message without a role to assistant (1.0 no longer
  defaults it), so the snapshot serializes and replays.
- Treat a 1.0 file source like an id reference: its handle is in
  "value", which was not read, so the part was dropped silently. It now
  gets the existing unresolved-reference rejection.
- Read 1.0's modelled JSON Patch operations in the task-steps example and
  in the predictive-state tests, and compare replayed snapshots with the
  wire shape for the installed SDK.

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.

🟡 Changes recommended

Rich tool-result media is still stripped when relayed through AgentFrameworkAgent.

1 open finding
What changed in this PR

Adds AG-UI protocol 1.0 compatibility while retaining 0.1.x support.

Changes:

  • Adds version-aware run-start and snapshot serialization.
  • Preserves multimodal tool results and handles 1.0 file sources.
  • Updates examples, dependencies, documentation, and compatibility tests.
File Description
python/​uv.lock Locks AG-UI protocol 1.0.
tests/​ag_ui/​test_workflow_agent.py Adds version-aware snapshot assertions.
tests/​ag_ui/​test_task_steps_example_agent.py Tests the updated task-steps example.
tests/​ag_ui/​test_run_common.py Strengthens tool-result payload assertions.
tests/​ag_ui/​test_protocol_1_0.py Covers protocol 1.0 compatibility behavior.
tests/​ag_ui/​test_predictive_state.py Supports modelled JSON Patch operations.
README.md Documents protocol compatibility.
pyproject.toml Expands the supported protocol range.
AGENTS.md Records AG-UI compatibility conventions.
_workflow.py Updates workflow snapshots and run-start events.
_workflow_run.py Centralizes workflow run-start creation.
_utils.py Detects the installed protocol version.
_snapshot_session.py Converts stored snapshots for wire replay.
_run_common.py Adds version-aware run-start construction.
_predictive_state.py Adapts JSON Patch typing.
_message_adapters.py Converts media snapshots and rich tool results.
_event_converters.py Parses content-part tool results.
_agent_run.py Preserves media in snapshots and approvals.
task_steps_agent.py Supports 1.0 patches and snapshot media.

🧠 Review effort: Balanced


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

Comment on lines +266 to +270
if isinstance(result, list):
# An AG-UI 1.0 ContentPart[] result: pass media on as Content items, as for tool messages.
converted = _tool_content_parts_to_result(cast(list[Any], result), tool_call_id)
if converted is not None:
result = converted

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in ba2d486. _emit_tool_result now encodes a media-bearing result with _function_result_to_agui_content and threads the parts through _emit_tool_result_common, so both the outward TOOL_CALL_RESULT (unless a display payload overrides it) and the snapshot message in flow.tool_results carry the content parts. The approval-path events in _make_approval_tool_result_events do the same, matching the snapshot messages _resolved_tool_result_snapshot_messages already rebuilt with parts. MCP results keep the Host-payload handling. Tests: test_agent_run_relays_tool_result_media (stub agent behind AgentFrameworkAgent) and test_agent_relays_tool_result_media_from_an_agui_server (AGUIChatClient fed by a mock AG-UI server, wrapped in Agent and AgentFrameworkAgent).

Resolve conflicts with the AG-UI changes on main:

- pyproject.toml: keep main's agent-framework-core floor (>=1.22.0) with
  the ag-ui-protocol <2 range.
- _message_adapters.py: main's per-occurrence result slots in
  _deduplicate_messages, judging emptiness with _function_result_has_output
  so an image-only result still replaces an empty placeholder.
- _workflow.py: main's live_owner check, with every RUN_STARTED (including
  the new factory-run site) built by _build_run_started_event.
- tests: main's (call_id, occurrence) snapshot keys and import additions.
A function result holding media (for example one AGUIChatClient built
from a 1.0 ContentPart[] TOOL_CALL_RESULT) reached _emit_tool_result,
which serialized only content.result: the outward TOOL_CALL_RESULT and
the snapshot tool message in flow.tool_results were text-only, so an
AgentFrameworkAgent in front of an AG-UI server dropped the media.

_emit_tool_result now encodes such a result with
_function_result_to_agui_content and threads the parts through
_emit_tool_result_common to the event (unless a display payload
overrides it) and the snapshot message. The approval-path
TOOL_CALL_RESULT events do the same, matching the snapshot messages
_resolved_tool_result_snapshot_messages already rebuilt with parts.
MCP results keep their Host-payload handling.

Tests: an AgentFrameworkAgent run over a stub agent yielding a media
result, and a relay through AGUIChatClient fed by a mock AG-UI server.
@agent-framework-automation agent-framework-automation Bot added .NET Usage: [Issues, PRs], Target: .Net workflows Usage: [Issues, PRs], Target: Workflows lab Usage: [Issues, PRs], Target: lab packages labels Oct 20, 2026
@github-actions github-actions Bot changed the title Python: Support ag-ui-protocol 1.0 in agent-framework-ag-ui .NET: Python: Support ag-ui-protocol 1.0 in agent-framework-ag-ui Oct 20, 2026
The previous merge commit recorded the resolved tree without main as a
parent; this merge records the ancestry (the tree already carries main).
@eavanvalkenburg Eduard van Valkenburg (eavanvalkenburg) changed the title .NET: Python: Support ag-ui-protocol 1.0 in agent-framework-ag-ui Python: Support ag-ui-protocol 1.0 in agent-framework-ag-ui Oct 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The 1.0 media and snapshot compatibility work is thorough, but the client-side version declaration and the promised lower-bound compatibility need durable coverage before the dependency range is widened.

}


def _build_run_started_event(run_id: str, thread_id: str) -> RunStartedEvent:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This correctly centralizes the producer declaration on RUN_STARTED, but the other half of the 1.0 contract is still missing: AGUIChatClient builds its outbound RunAgentInput through AGUIHttpService.post_run() without protocolVersion. The 1.0 SDK explicitly requires a client to declare PROTOCOL_VERSION on RunAgentInput, just as a producer declares it on RunStartedEvent. Please add the installed version to the outbound request payload when available (and omit it for 0.1.x), with HTTP-payload tests for both branches.

dependencies = [
"agent-framework-core>=1.22.0,<2",
"ag-ui-protocol>=0.1.19,<0.2",
"ag-ui-protocol>=0.1.19,<2",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This widens the supported dependency surface from one 0.1.x line to 0.1.19 <= version < 2, but the tracked lock and repository CI exercise only 1.0.0. The local 0.1.19/0.1.22 runs are not a durable compatibility guard. Please add a dependency-bounds/targeted matrix that runs the AG-UI tests against the floor (0.1.19), latest 0.1.x, and 1.0.0 so future changes cannot silently break the published lower range.

@ktz03

Copy link
Copy Markdown
Contributor

Markus Ecker (@mme), I checked the request construction on 7b294ab: _client.py calls post_run(), and _http_service.py sends its request_data dict through self.http_client.stream; that dict does not add protocolVersion. This matches the remaining request in this review thread.

Are you already taking care of that part, or would a small patch adding the SDK version when available and HTTP-payload tests for both branches be useful in this PR? I would keep the dependency matrix and your media changes out of that patch.

This is source analysis only: I have not reproduced this with SDK 1.0 or prepared a verified patch. AI-assisted analysis and writing.

This branch was successfully deployed

1 active deployment
github-app-auth — 7b294aba Deployed Oct 22, 2026 by eavanvalkenburg via add_label #25914
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs lab Usage: [Issues, PRs], Target: lab packages .NET Usage: [Issues, PRs], Target: .Net python Usage: [Issues, PRs], Target: Python workflows Usage: [Issues, PRs], Target: Workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: agent-framework-ag-ui doesn't support ag-ui-protocol 1.0

4 participants