Skip to content

Python: Fix AG-UI resume rejecting JSON for typed responses - #7684

Merged
Evan Mattson (moonbox3) merged 1 commit into
microsoft:mainfrom
orangeCatDeveloper:python-agui-dataclass-resume-payload
Aug 19, 2026
Merged

Python: Fix AG-UI resume rejecting JSON for typed responses#7684
Evan Mattson (moonbox3) merged 1 commit into
microsoft:mainfrom
orangeCatDeveloper:python-agui-dataclass-resume-payload

Conversation

@orangeCatDeveloper

@orangeCatDeveloper NekoPunch (orangeCatDeveloper) commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Motivation & Context

Resuming a Magentic plan-review interrupt over AG-UI fails for the payload an AG-UI client naturally sends — {"review": []}, the canonical "plan approved" answer:

RUN_ERROR  code=WORKFLOW_RESUME_INVALID_RESPONSE
Workflow resume for interruptId '...' does not match expected response type MagenticPlanReviewResponse.

Apps have to hand-build MagenticPlanReviewResponse to work around it. Magentic is only where this surfaced: any request_info whose response_type is a dataclass or model hits the same wall.

Description & Review Guide

  • What are the major changes?

_coerce_response_for_request fell back to isinstance(candidate, target_type), which a JSON dict can never satisfy, so AG-UI rejected the resume before it reached core's try_coerce_to_type — the helper the direct-SDK path already uses to build these objects. That helper had a second defect: target_cls(**data) built the outer object and left nested values as raw dicts, and since _send_responses_internal validates the outer type only, MagenticPlanReviewResponse(review=[dict]) passed.

The coercion therefore lives in core, where every entrypoint benefits: it walks the declared annotations recursively, and a field that does not match its annotation fails the whole build rather than yielding a half-typed object. AG-UI keeps only what is protocol-specific — translating the AG-UI message wire shape into Message for fields declared as such, and turning a coercion failure into RUN_ERROR.

  • What is the impact of these changes?

Field validation is stricter: Point(**{"x": 1, "y": "two"}) used to construct and pass, and is now rejected. Message shorthand is translated for first-level Message / list[Message] fields only; nested messages need the canonical contents form. The interrupt responseSchema for structured response types is still an opaque description, left for separate work.

Same resume request, before and after:

POST /workflow  {"resume": [{"interruptId": "...", "status": "resolved", "payload": {"review": []}}]}

before: data: {"type": "RUN_ERROR", "code": "WORKFLOW_RESUME_INVALID_RESPONSE", ...}
after:  data: {"type": "TEXT_MESSAGE_CONTENT", "delta": "FINAL"}
        data: {"type": "RUN_FINISHED", ...}

Nested coercion, which the direct-SDK path got wrong silently:

try_coerce_to_type({"review": [{"role": "user", "contents": [...]}]}, MagenticPlanReviewResponse)

before:  MagenticPlanReviewResponse(review=[dict])      # is_instance_of() said True
after:   MagenticPlanReviewResponse(review=[Message])

Related Issue

Fixes #7513

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.

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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Improves runtime typing/coercion for workflow payloads so JSON-shaped responses can be converted into typed Python objects (dataclasses, containers, unions), and wires that into AG-UI response handling.

Changes:

  • Enhanced try_coerce_to_type to handle Union, typed containers, dataclass field coercion/validation, Literal, and from_dict / model_validate.
  • Added extensive unit tests around dataclass coercion, Literal, union selection, and container coercion.
  • Updated AG-UI workflow response coercion to leverage the core coercion utilities and normalize AG-UI message wire formats by field annotation.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
python/packages/core/tests/workflow/test_typing_utils.py Adds coverage for new coercion behaviors (dataclasses, containers, literals, unions, bool→float guard, from_dict types).
python/packages/core/agent_framework/_workflows/_typing_utils.py Implements richer coercion logic and dataclass field-level validation/coercion.
python/packages/ag-ui/agent_framework_ag_ui/_workflow_run.py Switches response coercion to core coercion utilities and adds message-field normalization based on annotations.
python/packages/ag-ui/tests/ag_ui/test_workflow_run.py Adds AG-UI tests for dataclass/pydantic response coercion and field-annotation-driven message normalization.
python/packages/ag-ui/tests/ag_ui/test_endpoint.py Adds endpoint-level regression test for resuming dataclass responses from JSON payloads.

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

Comment thread python/packages/core/agent_framework/_workflows/_typing_utils.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_typing_utils.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_typing_utils.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_workflow_run.py
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_workflow_run.py
Comment thread python/packages/core/agent_framework/_workflows/_typing_utils.py Outdated
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/ag-ui/agent_framework_ag_ui
   _workflow_run.py8079188%98, 112, 114, 116, 185–186, 190, 291–294, 338, 349, 354, 385, 421–424, 452, 457, 473, 475, 480–484, 494, 501, 513, 518, 525, 600, 610, 621, 626, 629, 652, 676, 679, 684, 687, 704–706, 711, 713, 719, 723, 725, 742, 748–749, 759, 765, 769–770, 794–795, 833, 841, 910, 930, 946, 961, 1045, 1067–1069, 1104–1118, 1150–1151, 1185–1186, 1261, 1323
packages/core/agent_framework/_workflows
   _typing_utils.py2523287%22, 202, 226, 299, 301, 415, 484, 486–487, 496, 498, 505, 507, 527, 529, 531, 536–543, 546–547, 549–553, 555
TOTAL47037436690% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
9524 36 💤 0 ❌ 0 🔥 2m 28s ⏱️

@moonbox3

Copy link
Copy Markdown
Contributor

Please also look at the failing CI/CD checks.

@moonbox3

Copy link
Copy Markdown
Contributor

Please have a look at some conflicts, thanks. NekoPunch (@orangeCatDeveloper)

@orangeCatDeveloper

Copy link
Copy Markdown
Contributor Author

Please have a look at some conflicts, thanks. NekoPunch (NekoPunch (@orangeCatDeveloper))

rebased

@moonbox3

Copy link
Copy Markdown
Contributor

/review

@github-actions github-actions Bot 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.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (4 commit(s)): 534af1f093e0, 32230375d11b, 5aa5b5a2511c, 7025872aec80
Model: gpt-5.6-sol

Overview

The PR centralizes recursive typed-response coercion in core and keeps AG-UI-specific Message normalization at the protocol boundary, with strong coverage for nested dataclasses, common containers, constructor failures, and invalid resume handling. Two gaps remain: the new dataclass field discovery rejects valid InitVar constructor arguments, and the new frozenset conversion can accept members that violate the declared element type.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
2 verified findings remained after source verification (2 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/core/agent_framework/_workflows/_typing_utils.py

Comment thread python/packages/core/agent_framework/_workflows/_typing_utils.py Outdated
Comment thread python/packages/core/agent_framework/_workflows/_typing_utils.py
AG-UI clients send plain JSON, but structured response types were only
accepted as already-built instances, and core's coercion stopped at the
outer object, letting raw dicts sit inside typed fields. Coercion now
walks declared annotations and returns the input untouched whenever it
cannot satisfy them.
@orangeCatDeveloper
NekoPunch (orangeCatDeveloper) force-pushed the python-agui-dataclass-resume-payload branch from 3e3165f to 7ca5737 Compare August 19, 2026 00:34
@orangeCatDeveloper

NekoPunch (orangeCatDeveloper) commented Aug 19, 2026

Copy link
Copy Markdown
Contributor Author

Rebased to resolve conflict

@moonbox3
Evan Mattson (moonbox3) added this pull request to the merge queue Aug 19, 2026
Merged via the queue into microsoft:main with commit e74ac46 Aug 19, 2026
35 checks passed
@orangeCatDeveloper
NekoPunch (orangeCatDeveloper) deleted the python-agui-dataclass-resume-payload branch August 19, 2026 06:01
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: Python : AG-UI Magentic plan-review resume rejects JSON review payload(expects typed MagenticplanReviewResponse)

3 participants