Skip to content

feat(api-server): emit Responses API reasoning output items behind show_reasoning - #43644

Open
connorblack wants to merge 1 commit into
NousResearch:mainfrom
connorblack:feat/api-server-responses-reasoning-items
Open

feat(api-server): emit Responses API reasoning output items behind show_reasoning#43644
connorblack wants to merge 1 commit into
NousResearch:mainfrom
connorblack:feat/api-server-responses-reasoning-items

Conversation

@connorblack

@connorblack connorblack commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

/v1/responses can expose the model's reasoning as spec-shaped reasoning output items, gated by display.platforms.api_server.show_reasoning (default off — the wire format is byte-identical until enabled). Clients that render Responses reasoning items (e.g. stock Open WebUI) get a live "Thinking…" block with no client-side change.

Rebased onto current main; both points from the last review are addressed and covered by tests.

How

Real reasoning streams via the agent's reasoning_callback (_create_agent now forwards it), accumulating into one reasoning item per burst. reasoning.available is deliberately not used — it carries the assistant message content, not the model's reasoning.

  • Ordering — reasoning is extracted inline in the message-order walk, so a multi-iteration turn yields [reasoning, function_call, function_call_output, reasoning, message] (test_batch_output_preserves_reasoning_tool_result_order), and the streaming silent-callback fallback interleaves the same way in the stored output (test_stream_fallback_reasoning_interleaves_like_batch).
  • Silent-callback fallback — providers that report reasoning only on the final message run the same output_item.added → summary → output_item.done lifecycle as the live path, not just response.completed (test_stream_falls_back_to_final_reasoning_when_callback_is_silent).

Incremental reasoning events carry the full text; response.completed and the store trim it — the same policy as function_call args.

Changes

  • gateway/platforms/api_server.pyshow_reasoning gate, reasoning_callback wiring, streaming reasoning items + final-message fallback, echoed-reasoning input/history skip.
  • tests/gateway/test_api_server.py — gate, streaming lifecycle, ordering (batch + stream fallback), fallback, trimming, echo-skip.

Closes #21655. Closes #7556.

Copilot AI review requested due to automatic review settings June 10, 2026 16:24
@connorblack
connorblack force-pushed the feat/api-server-responses-reasoning-items branch from 99ce568 to 52df5ac Compare June 10, 2026 16:25

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.

Adds gated support for emitting OpenAI Responses-style reasoning output items (streaming SSE + final envelope) on /v1/responses, controlled by display.platforms.api_server.show_reasoning.

Changes:

  • Add a per-request gate (_reasoning_items_enabled) and wire reasoning_callback through agent creation/execution so real reasoning deltas can be streamed.
  • Stream reasoning as spec-shaped SSE events and include reasoning items in the completed/stored response; trim long reasoning text in the final envelope.
  • Add tests to validate gate behavior, SSE event shapes, trimming, and skipping echoed reasoning items in input/conversation_history.

Reviewed changes

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

File Description
gateway/platforms/api_server.py Implements reasoning-item gating, streaming accumulation/closure, input/history skipping, and envelope trimming.
tests/gateway/test_api_server.py Adds coverage for reasoning-item gating, streaming SSE events, trimming behavior, and echoed-input handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gateway/platforms/api_server.py
Comment thread gateway/platforms/api_server.py Outdated
Comment thread tests/gateway/test_api_server.py
@alt-glitch alt-glitch added type/feature New feature or request comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have labels Jun 10, 2026
@connorblack

Copy link
Copy Markdown
Contributor Author

Follow-up on current head 1b5c5d6: I rechecked this branch locally after the earlier Copilot response commits and do not see a remaining code-side blocker on the /v1/responses reasoning-items path.

Local proof on the current branch:

  • ./.venv/bin/pytest -q tests/gateway/test_api_server.py -k "reasoning or responses" -> 47 passed, 120 deselected

That covers the reasoning-item gate behavior, SSE event shapes, final-envelope behavior, and the echoed-input/history handling in the changed API-server surface. From current evidence this PR looks like maintainer/review attention rather than another autonomous bug-fix lane.

@connorblack

Copy link
Copy Markdown
Contributor Author

Follow-up on current head b9630b3: I found and fixed two real reasoning-item consistency bugs in the /v1/responses path.

  1. Streamed responses previously emitted reasoning items only from live reasoning_callback deltas. If a provider surfaced reasoning only on the final assistant message (reasoning_content / reasoning) and never fired the callback, stream=true silently dropped reasoning while stream=false still returned it. The stream path now falls back to final-message reasoning when no streamed reasoning item was emitted.
  2. Streamed reasoning response.output_item.done and the terminal response.completed / stored snapshot could drift on the same item id because the final envelope trim mutated the stored copy after the done event had already emitted the full text. The stream path now emits the same terminal reasoning-item shape that lands in the completed envelope/store, keeping those surfaces consistent.

Local verification on the pushed head:

  • python3 -m py_compile gateway/platforms/api_server.py
  • ./.venv/bin/pytest -q tests/gateway/test_api_server.py -k "reasoning or responses" -> 48 passed, 120 deselected

The new tests cover:

  • stream fallback when reasoning exists only in final assistant messages
  • consistency between streamed response.output_item.done, response.completed, and the stored response for trimmed reasoning items

@connorblack
connorblack force-pushed the feat/api-server-responses-reasoning-items branch from b9630b3 to 6be3eef Compare June 30, 2026 14:11

@teknium1 teknium1 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.

Thanks for the focused API-server reasoning work. The feature is still absent on current main: gateway/platforms/api_server.py:3368-3378 turns every input object into a message, and _extract_output_items at :4044-4092 has no reasoning items.

Problems

  • gateway/platforms/api_server.py:3837 collects all reasoning items before the existing message walk. A tool-using turn therefore serializes all reasoning before all function calls/results, rather than preserving the claimed interleaving.
  • gateway/platforms/api_server.py:2881-2893 appends final-message fallback reasoning only to response.completed; it does not emit response.output_item.added, summary, or response.output_item.done events for that item.

Suggested changes

  • Extract reasoning inline with the existing message-order walk and add a multi-iteration ordering test.
  • Route the silent-callback fallback through the same terminal reasoning SSE lifecycle and test those events.

The branch is 1,649 commits behind current main, so this needs a deliberate salvage into the current API-server implementation rather than a clean cherry-pick.

Automated hermes-sweeper review.

"""
items: List[Dict[str, Any]] = []
messages = result.get("messages", [])
if start_index > 0:

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.

This prepends reasoning for every assistant message before the subsequent loop emits any function_call/function_call_output items. For a multi-iteration turn that loses the actual reasoning → tool → result → reasoning order. Extract reasoning inside the existing message-order loop and add a multi-iteration ordering regression test.

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.

Addressed. Reasoning is extracted inline in the message-order walk (not collected up front), so a multi-iteration turn preserves reasoning → tool → result → reasoning — in the batch path (test_batch_output_preserves_reasoning_tool_result_order) and, after a follow-up fix, in the streaming silent-callback fallback's stored output (test_stream_fallback_reasoning_interleaves_like_batch). Rebased onto current main.

@@ -2766,6 +2881,21 @@
final_response_text = agent_final

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.

The silent-callback fallback adds an item only to the terminal envelope after the SSE drain; it emits none of the reasoning output-item lifecycle events. Emit a completed lifecycle for this fallback as well, then test added/done events in addition to response.completed.

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.

Addressed. The silent-callback fallback now runs the same output_item.added → reasoning_summary_* → output_item.done lifecycle as the live path, in addition to response.completed and the store (test_stream_falls_back_to_final_reasoning_when_callback_is_silent).

@teknium1 teknium1 added sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 14, 2026
…ow_reasoning

/v1/responses exposes the model's reasoning as spec-shaped `reasoning` output
items, gated by `display.platforms.api_server.show_reasoning` (default off; the
wire format is byte-identical until enabled). Reasoning streams live via the
agent's reasoning_callback and accumulates into one item per burst; a
silent-callback fallback emits the same added -> summary -> done lifecycle from
final-message reasoning. Items are extracted inline in message order, so
multi-iteration turns keep reasoning -> tool -> result -> reasoning.

Closes NousResearch#21655. Closes NousResearch#7556.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/feature New feature or request

Projects

None yet

4 participants