Skip to content

feat: render streaming reasoning content#594

Open
Shinyaigeek wants to merge 1 commit into
thesysdev:mainfrom
Shinyaigeek:feat/reasoning-events
Open

feat: render streaming reasoning content#594
Shinyaigeek wants to merge 1 commit into
thesysdev:mainfrom
Shinyaigeek:feat/reasoning-events

Conversation

@Shinyaigeek

Copy link
Copy Markdown
Contributor

Summary

@ag-ui/core fully defines the ReasoningMessage type and the REASONING_MESSAGE_START / CONTENT / END event sequence, but neither @openuidev/react-headless nor @openuidev/react-ui consumed them, so reasoning/thinking output was silently dropped end-to-end (#414).

This wires reasoning through, colocated on the assistant turn (a reasoning field on AssistantMessage) rather than as a separate message — matching how the streaming layer already folds text and tool calls into a single assistant message, and how the format converters already treat reasoning.

What changed

react-headless

  • types/message.ts — extend AssistantMessage with an optional reasoning field; swap the Message union's assistant branch so role === "assistant" narrowing exposes it.
  • stream/processStreamedMessage.ts — handle REASONING_MESSAGE_CONTENT / CHUNK, accumulating deltas into currentMessage.reasoning. The reasoning-only messageId is intentionally ignored; a later TEXT_MESSAGE_START swaps in the real assistant id and the spread preserves the accumulated reasoning.
  • types/messageFormat.ts — the default identityMessageFormat now strips the client-only reasoning field in toApi, so accumulated thinking is not echoed back to the backend on every subsequent turn.

react-ui

  • New Reasoning/ component dir: ReasoningContent (markdown) and a shared ReasoningSection (the collapsible BehindTheScenes panel).
  • Applied to all assistant render surfaces — Shell, CopilotShell, BottomTray (via ReasoningSection) and OpenUIChat (nested in its existing merged tool-call panel).
  • Exported from the package root.

Design notes

  • Colocation over a separate message — the live stream builds one assistant message per run; reasoning as a field fits that grain. See the discussion in the issue.
  • Reasoning streams expanded ("Working…") and collapses once the answer text arrives (toolCallsComplete={!!message.content}).

Out of scope (follow-ups)

  • Restoring reasoning from server history (loadThread / the format converters) — currently the live-stream path only.
  • Interleaved reasoning↔answer ordering (single reasoning string concatenates blocks).
  • Markdown re-parse cost for very long reasoning during streaming.

Testing

  • processStreamedMessage: reasoning accumulation, preservation across the TEXT_MESSAGE_START id swap, and absence (no reasoning events).
  • identityMessageFormat: strips reasoning outbound, leaves other messages untouched.
  • pnpm test green (react-headless), typecheck + build green (both packages).

Closes #414

🤖 Generated with Claude Code

Consume AG-UI REASONING_MESSAGE_* events end-to-end. The stream processor
now accumulates reasoning deltas into a colocated AssistantMessage.reasoning
field, and the Shell / CopilotShell / BottomTray / OpenUIChat surfaces render
it inside a collapsible "Behind the scenes" panel via a shared ReasoningSection
component.

- types: extend AssistantMessage with an optional `reasoning` field and swap
  the Message union's assistant branch so narrowing exposes it
- processStreamedMessage: handle REASONING_MESSAGE_CONTENT/CHUNK, ignoring the
  reasoning-only messageId (preserved across the TEXT_MESSAGE_START id swap)
- identityMessageFormat: strip the client-only `reasoning` field from outbound
  history so it is not echoed back to the backend each turn
- react-ui: new Reasoning/ component dir (ReasoningContent + ReasoningSection),
  applied to all assistant render surfaces; exported from the package root

Tests: reasoning accumulation, id-swap preservation, absence, and the
identityMessageFormat strip.

Addresses thesysdev#414

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

processStreamedMessage doesn't handle reasoning events; Shell.Messages skips reasoning role

1 participant