Skip to content

Surface AG-UI REASONING_MESSAGE_CHUNK events as reasoning content - #936

Open
PratikDhanave (PratikDhanave) wants to merge 1 commit into
microsoft:mainfrom
PratikDhanaveFork:agui-surface-reasoning-message-chunk
Open

Surface AG-UI REASONING_MESSAGE_CHUNK events as reasoning content#936
PratikDhanave (PratikDhanave) wants to merge 1 commit into
microsoft:mainfrom
PratikDhanaveFork:agui-surface-reasoning-message-chunk

Conversation

@PratikDhanave

Copy link
Copy Markdown
Contributor

Problem

The AG-UI client's onEvent switch (provider/aguiprovider/agui.go) handles ReasoningMessageContentEvent and TextMessageChunkEvent, but has no case for ReasoningMessageChunkEvent — the chunk-form encoding of reasoning that the AG-UI decoder also produces (decoder.go case EventTypeReasoningMessageChunk). A server that streams reasoning as REASONING_MESSAGE_CHUNK events therefore has its reasoning silently dropped (falls through to the default).

This is the reasoning counterpart of the already-handled TEXT_MESSAGE_CHUNK case.

Fix

Add a *aguiEvents.ReasoningMessageChunkEvent case that emits a message.TextReasoningContent from the chunk delta, reusing the last seen chunk MessageID when a chunk omits it — mirroring the existing TextMessageChunkEvent handling and the ReasoningMessageContentEvent mapping.

Test

TestAGUIAgentRun_SurfacesReasoningMessageChunkEvents streams two REASONING_MESSAGE_CHUNK events and asserts the concatenated reasoning is surfaced as TextReasoningContent. Fails before the fix (empty), passes after.

Note: TOOL_CALL_CHUNK is intentionally not addressed here — the AG-UI Go decoder has no case for it, so it never reaches the client.

The client onEvent switch handles ReasoningMessageContentEvent but not the
semantically equivalent ReasoningMessageChunkEvent, which the AG-UI decoder
also produces (decoder EventTypeReasoningMessageChunk). A server that streams
reasoning as chunk events therefore has its reasoning silently dropped.

Add a ReasoningMessageChunkEvent case that emits a TextReasoningContent,
reusing the last chunk MessageID for chunks that omit it - mirroring the
existing TextMessageChunkEvent handling and ReasoningMessageContentEvent.
Copilot AI lite review requested due to automatic review settings August 28, 2026 05:46
@PratikDhanave
PratikDhanave (PratikDhanave) requested a review from a team as a code owner August 28, 2026 05:46
@github-actions github-actions Bot added area:provider Changes files in the provider area area:provider/agui Changes files in the provider / agui area size:medium At most 100 changed lines across at most 5 files labels Aug 28, 2026
@github-actions github-actions Bot added the pending-auto-risk Automatic risk classification is in progress label Aug 28, 2026

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

Adds support in the AG-UI provider for streaming reasoning deltas sent as REASONING_MESSAGE_CHUNK events, so chunked reasoning is surfaced to collectors instead of being dropped.

Changes:

  • Handle *aguiEvents.ReasoningMessageChunkEvent in toolCallAccumulator.onEvent by emitting message.TextReasoningContent updates.
  • Add a unit test that streams reasoning chunk events and asserts the concatenated reasoning is surfaced.

Reviewed changes

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

File Description
provider/aguiprovider/agui.go Adds a switch-case to map ReasoningMessageChunkEvent into TextReasoningContent response updates (with MessageID continuation logic).
provider/aguiprovider/agui_test.go Adds a test asserting chunked reasoning is collected and exposed as TextReasoningContent.

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

Comment on lines +449 to +463
case *aguiEvents.ReasoningMessageChunkEvent:
delta := deref(e.Delta)
if delta == "" {
return nil, nil
}
// A chunk may omit MessageID to continue the current reasoning message;
// reuse the last seen chunk MessageID, mirroring the text-chunk handling.
if id := deref(e.MessageID); id != "" {
a.lastChunkMessageID = id
}
return []*agent.ResponseUpdate{{
Role: message.RoleAssistant,
MessageID: a.lastChunkMessageID,
CreatedAt: eventTime(evt),
Contents: message.Contents{&message.TextReasoningContent{Text: delta}},
Comment on lines +719 to +741
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
writeSSE(t, w, aguiEvents.NewRunStartedEvent("thread-1", "run-1"))
writeSSE(t, w, aguiEvents.NewReasoningMessageChunkEvent(strPtr("r1"), strPtr("think")))
writeSSE(t, w, aguiEvents.NewReasoningMessageChunkEvent(strPtr("r1"), strPtr("ing")))
writeSSE(t, w, aguiEvents.NewRunFinishedEvent("thread-1", "run-1"))
}))
defer server.Close()

a := aguiprovider.NewAgent(newTestClient(server.URL), aguiprovider.AgentConfig{})
resp, err := a.RunText(context.Background(), "hi").Collect()
if err != nil {
t.Fatalf("run error: %v", err)
}
var reasoning string
for content := range resp.Contents() {
if rc, ok := content.(*message.TextReasoningContent); ok {
reasoning += rc.Text
}
}
if reasoning != "thinking" {
t.Fatalf("reasoning text = %q, want %q", reasoning, "thinking")
}

@qmuntal Quim Muntal (qmuntal) left a comment

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.

Fix copilot feedback.

@github-actions github-actions Bot added risk:medium Contained production impact requiring normal review depth parity-approved Go API consistency review found no parity issues and removed pending-auto-risk Automatic risk classification is in progress labels Aug 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Parity Review Summary

Scope: User-visible behavior (provider/aguiprovider)
Changed Go contract: No new exported types or methods. The bug fix adds a *aguiEvents.ReasoningMessageChunkEvent case to the unexported onEvent switch in provider/aguiprovider/agui.go, routing REASONING_MESSAGE_CHUNK events to the already-exported message.TextReasoningContent type — the same type that ReasoningMessageContentEvent already mapped to.
Upstream evidence reviewed:

  • python/packages/ag-ui/agent_framework_ag_ui/_event_converters.py — Python ag-ui package is a server-side emitter of AG-UI events; no equivalent client-side REASONING_MESSAGE_CHUNK consumer exists. The Python client (_client.py) uses AGUIEventConverter which has no REASONING_MESSAGE_CHUNK handling, consistent with its server-side role.
  • dotnet/src/Microsoft.Agents.AI.AGUI — contains only a README; no .NET client-side AGUI consumer found to compare against.

Result: ✅ Aligned / no parity issues

This is a self-contained bug fix in the Go AG-UI client: REASONING_MESSAGE_CHUNK was previously silently dropped while its non-chunk counterpart (ReasoningMessageContentEvent) was handled. The fix mirrors the pattern already established for TextMessageChunkEventTextMessageContentEvent. No new Go-only API surface is introduced, no defaults change, and no upstream .NET or Python equivalent client-side handling conflicts with this fix.

public-api-change label not applied — no exported identifiers were added, removed, or changed.

Generated by Go API Consistency Review Agent · sonnet46 · 37.2 AIC · ⌖ 5.1 AIC · ⊞ 6.4K ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:provider/agui Changes files in the provider / agui area area:provider Changes files in the provider area parity-approved Go API consistency review found no parity issues risk:medium Contained production impact requiring normal review depth size:medium At most 100 changed lines across at most 5 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants