feat(api-server): emit Responses API reasoning output items behind show_reasoning - #43644
Conversation
99ce568 to
52df5ac
Compare
There was a problem hiding this comment.
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 wirereasoning_callbackthrough 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.
|
Follow-up on current head Local proof on the current branch:
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. |
|
Follow-up on current head
Local verification on the pushed head:
The new tests cover:
|
b9630b3 to
6be3eef
Compare
teknium1
left a comment
There was a problem hiding this comment.
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:3837collects 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-2893appends final-message fallback reasoning only toresponse.completed; it does not emitresponse.output_item.added, summary, orresponse.output_item.doneevents 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: |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 | |||
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
…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.
2aabd4c to
6af6532
Compare
Summary
/v1/responsescan expose the model's reasoning as spec-shapedreasoningoutput items, gated bydisplay.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_agentnow forwards it), accumulating into onereasoningitem per burst.reasoning.availableis deliberately not used — it carries the assistant message content, not the model's reasoning.[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).output_item.added → summary → output_item.donelifecycle as the live path, not justresponse.completed(test_stream_falls_back_to_final_reasoning_when_callback_is_silent).Incremental reasoning events carry the full text;
response.completedand the store trim it — the same policy asfunction_callargs.Changes
gateway/platforms/api_server.py—show_reasoninggate,reasoning_callbackwiring, 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.