Skip to content

Python: fix: surface AG-UI workflow intermediate events as reasoning - #8003

Open
Manjunath Janardhan (manjunathshiva) wants to merge 2 commits into
microsoft:mainfrom
manjunathshiva:python-agui-intermediate-reasoning-8000
Open

Python: fix: surface AG-UI workflow intermediate events as reasoning#8003
Manjunath Janardhan (manjunathshiva) wants to merge 2 commits into
microsoft:mainfrom
manjunathshiva:python-agui-intermediate-reasoning-8000

Conversation

@manjunathshiva

Copy link
Copy Markdown
Contributor

Motivation & Context

The AG-UI workflow runner only processed output and data workflow events, so intermediate events — the modern designation produced by executors listed under intermediate_output_from — fell through to the generic CustomEvent fallback (_workflow_run.py, the if event_type in {"output", "data"} branch). As a result, intermediate workflow output was emitted as CustomEvent(name="intermediate") instead of AG-UI reasoning events, so consumers (CopilotKit, assistant-ui, …) could not render it as a collapsible "thinking" block.

AGENT_FORWARDED_EVENT_TYPES in _workflows/_events.py already lists output, intermediate, and data (the deprecated alias for intermediate), so the AG-UI layer was silently dropping a forwarded event type into the diagnostics fallback.

Description & Review Guide

  • What are the major changes?

    • Add "intermediate" to the handled event set in run_workflow_stream so intermediate events are no longer routed to the generic CustomEvent fallback.
    • Route intermediate text content through a new _as_reasoning_content() helper that re-tags it as text_reasoning, so it emits via the existing _emit_text_reasoning path (REASONING_* events). Non-text content (tool calls/results, etc.) passes through unchanged and still emits as its native AG-UI events.
    • The deprecated "data" alias is handled identically to "intermediate", matching its definition in _events.py as the intermediate compatibility alias.
    • "output" keeps the existing terminal-message behavior, including the last_assistant_text de-duplication (reasoning is a separate channel, so that de-dup does not apply to intermediate output).
    • This is the AG-UI-layer fix (Option B in the issue). It deliberately does not touch WorkflowAgent._convert_workflow_event_to_agent_response_updates() (Option A), which is being addressed under Python: [Bug]: WorkflowAgent drops response metadata when forwarding AgentResponseUpdate #7952.
  • What is the impact of these changes?

    • Intermediate workflow output now renders as reasoning in AG-UI clients instead of a generic custom event.
    • Behavior note for reviewers: the deprecated type="data" alias previously rendered as a final assistant message; it now renders as reasoning, aligning the deprecated path with its intermediate replacement. If you would prefer to keep data → final message for strict backward-compat, that is a one-line change (drop "data" from is_intermediate) plus removing the alias test — happy to adjust either way.
  • What do you want reviewers to focus on?

    • The data-alias rendering decision above.

Related Issue

Fixes #8000

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 AG-UI workflow runner only handled "output" and "data" workflow
events, so "intermediate" events (the modern designation produced by
executors listed under intermediate_output_from) fell through to the
generic CustomEvent fallback instead of being converted to AG-UI
reasoning events.

Add "intermediate" to the handled set and route intermediate text
content to text_reasoning so consumers render it as a collapsible
"thinking" block rather than a final assistant message. The deprecated
"data" alias is treated the same as "intermediate" (per its definition
as the intermediate compatibility alias in _events.py). "output" keeps
the terminal-message behavior, and non-text content (tool calls and
results) still emits as its native AG-UI events.

Fixes microsoft#8000

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

Reasoning conversion currently drops encrypted protected_data metadata.

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

Pull request overview

Routes intermediate workflow text through AG-UI’s reasoning pipeline.

Changes:

  • Handles intermediate and deprecated data events as reasoning.
  • Adds regression tests for intermediate and terminal output behavior.
File summaries
File Description
_workflow_run.py Converts intermediate text into reasoning content.
test_workflow_run.py Tests intermediate and deprecated alias routing.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 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/_workflow_run.py
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
# message, so the last_assistant_text dedup does not apply.
for content in contents:
reasoning_content = _as_reasoning_content(content)
for out_event in _emit_content(

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.

It may make sense for the workflow snapshot path to retain the reasoning emitted here. _emit_content records it in flow.reasoning_messages, but run_workflow_stream never emits a MessagesSnapshotEvent, and _WorkflowSnapshotBuilder.observe ignores the reasoning events. With a snapshot store enabled, intermediate output renders live but disappears when the thread is hydrated; teaching _WorkflowSnapshotBuilder to fold these events, or emitting the same terminal snapshot as the agent runner, would keep live and replayed output consistent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed this is a real gap — the workflow runner records reasoning into flow.reasoning_messages but never emits a MessagesSnapshotEvent, and _WorkflowSnapshotBuilder.observe ignores the reasoning events, so intermediate output renders live but doesn't survive hydration (the agent runner emits the terminal snapshot; the workflow runner doesn't). It's larger than the fixes here and touches the snapshot/hydration path, so I'd lean toward a focused follow-up PR rather than expanding this one — unless you'd prefer it land here. Happy either way; let me know and I'll pick it up.

Follow-up to the intermediate-reasoning change on this branch, addressing
maintainer review:

- Preserve protected_data when re-tagging intermediate text as reasoning so
  the ReasoningEncryptedValueEvent and the snapshot encryptedValue survive.
- Close any open reasoning block (and assistant text message) before every
  terminal event (RUN_FINISHED / RUN_ERROR) and before a request_info tool
  call, via an idempotent _drain_open_blocks() helper. Previously the
  post-loop cleanup emitted the reasoning end events after the terminal
  event, so a client that stopped at RUN_FINISHED never saw them; a
  request_info tool call could likewise sit inside an unclosed reasoning
  block.
- Keep role-less AgentResponseUpdate text (and tool content) instead of
  dropping it to a CUSTOM workflow_output. Explicit non-assistant roles and
  approval requests stay excluded.
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]: AG-UI workflow runner ignores "intermediate" events

3 participants