fix(gateway): reasoning display is silently lost on every streamed turn - #57693
fix(gateway): reasoning display is silently lost on every streamed turn#57693CocaKova wants to merge 1 commit into
Conversation
teknium1
left a comment
There was a problem hiding this comment.
Thanks for tracing the streamed-final suppression path; the premise is confirmed on current main. gateway/run.py:11919-11953 formats reasoning only for the normal response, while gateway/run.py:20339-20347 marks a confirmed stream as already sent.
Problems
- The new direct final edit in
gateway/run.py:19307omits stream metadata.gateway/stream_consumer.py:301-311forwards that metadata for edits, and Slack uses it to select the workspace client (plugins/platforms/slack/adapter.py:1525,:1545-1547). A non-default Slack workspace can therefore fail this best-effort edit and still lose reasoning. - The diff changes only
gateway/run.py; the citedtests/test_gateway_streaming_nested_config.pyonly covers parsing nested streaming configuration, not this delivery path.
Suggested changes
- Reuse the stream consumer's metadata-aware edit path for the reasoning fold.
- Add a streamed reasoning regression asserting the final edit,
already_sentsuppression, and metadata preservation.
This is an automated hermes-sweeper review.
| source, response.get("last_reasoning") | ||
| ) | ||
| _sc_msg_id = _sc.message_id if _sc else None | ||
| if _reasoning_block and _sc_msg_id and _final: |
There was a problem hiding this comment.
Please preserve the stream consumer's routing metadata here. GatewayStreamConsumer._edit_message() forwards metadata when supported, and Slack uses it to select the workspace client; this direct call can silently fail for a non-default workspace and leave reasoning absent.
…a-aware edit With streaming enabled the stream consumer commits the final message and the gateway suppresses the normal send (already_sent=True). But the normal send path is the only place the 💭 reasoning block is prepended (show_reasoning / reasoning_style), so turning streaming on silently disabled reasoning display for every model and platform. Fix: extract the block formatting into _format_reasoning_block() (shared by the normal path — behavior unchanged) and, in the suppression branch, fold the block into the already-streamed message with one final edit. The fold routes through the stream consumer's metadata-aware _edit_message() rather than a raw adapter.edit_message(): the routing metadata Slack uses to select the workspace client (and Telegram uses for topic/thread routing) is preserved, so a non-default Slack workspace no longer fails the edit and loses reasoning. Best-effort — a failed fold only loses the reasoning display, never the answer, and never un-suppresses the send. tests/gateway/test_streamed_reasoning_fold.py: metadata forwarding on the edit path, the fold's content/message-id/finalize/metadata, best-effort no-ops (no reasoning / no consumer / uncommitted stream / edit failure), and the already_sent suppression gate. 9 passed; existing suppression + stream-consumer suites (184) still green.
3338f45 to
b1128f4
Compare
|
Thanks — both points addressed, rebased onto current main ( Metadata-aware edit path. The reasoning fold no longer calls Real delivery-path regression. New
9 new tests pass; the existing suppression + stream-consumer suites (184) stay green. The formatting extraction into |
Problem
With
streaming.enabled: true, the stream consumer commits the final message and_run_agent_innersuppresses the normal send (already_sent=True). But the normal send path in_handle_message_with_agentis the only place the 💭 reasoning block is prepended (show_reasoning/reasoning_style). Net effect: enabling streaming silently turns off reasoning display for every model and every platform — the two features look independent but are mutually exclusive today.Repro: any brain that emits
reasoning_content,display.show_reasoning: true,streaming.enabled: true→ the streamed final message arrives without the reasoning block; flip streaming off and it comes back.Fix
_format_reasoning_block()(per-platformshow_reasoningresolution incl. the Mattermost explicit-override rule, 15-line collapse,reasoning_stylerendering). The normal send path now calls it — behavior there is unchanged, this is a pure refactor.edit_message(..., finalize=True)— the exact pattern the plugin-transform branch directly below already uses for the same "content changed after streaming" situation.Testing
tests/test_gateway_streaming_nested_config.py: 3 passed.--reasoning-parser): streamed turns now show the collapsed reasoning block again, identical to non-streamed turns. I've been running this fix in production since 2026-07-02 as a local patch.Contact for CLA/CI: jonathan.kovacs999@gmail.com
🤖 Generated with Claude Code