Skip to content

fix(reasoning): preserve compatible response state - #10574

Merged
diegosouzapw merged 12 commits into
diegosouzapw:release/v3.8.50from
jackjinke:fix/responses-reasoning-transport
Aug 21, 2026
Merged

diegosouzapw merged 12 commits into
diegosouzapw:release/v3.8.50from
jackjinke:fix/responses-reasoning-transport

Conversation

@jackjinke

@jackjinke jackjinke commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Preserve authentic plaintext reasoning continuations across Chat Completions and Responses, for streaming and non-streaming requests.
  • Apply one target-aware reasoning transport policy before protocol translation. Unannotated targets accept portable plaintext by default; provider-bound opaque state requires an explicit compatible target declaration.
  • Keep plaintext reasoning, opaque state, and display-only summary / summary_text separate. Summaries never participate in compatibility decisions. Summaries attached to active reasoning are preserved; orphan summaries are removed from explicitly stateless (store: false) input.
  • Default Combo fallback to copy-on-write drop: remove only incompatible active reasoning for the current target attempt, without mutating the original request or later targets. Explicit skip remains strict fallthrough behavior.
  • Remove protocol-specific transport metadata and adapter-level compatibility decisions. Chat and Responses adapters only encode or decode their wire shapes.

Fixes #10550.

Before / After

Before, reasoning compatibility was coupled to Responses routing and missing provider metadata failed closed. This rejected portable plaintext for unannotated targets such as OpenCode Go, while Chat input and stream mode could follow different paths. Combo also defaulted to skipping the target.

After, both Chat and Responses inputs are inspected before request translation and before streaming/non-streaming handling diverges:

Active continuation state Target transport / storage Result
Plaintext only Missing or plaintext Preserve
Plaintext only opaque or none Reject directly; Combo default drops plaintext for that attempt
Opaque only Matching opaque Preserve
Opaque only Missing, plaintext, or none Reject directly; Combo default drops opaque state for that attempt
Plaintext + opaque Any single transport Incompatible; Combo keeps only the target-compatible active kind
Summary only store unset or true Ignored by compatibility and preserved independently
Summary only store: false Dropped as orphan display state

Provider-generated state is never synthesized or cross-converted. Rejected requests remain unmodified, and target-local drop clones only the affected Chat messages or Responses items.

Validation

Latest post-merge repair suite:

58 tests
58 passed
0 failed

The broader focused reasoning suite previously reported:

169 tests
169 passed
0 failed

Covered paths include:

  • Chat and Responses source shapes
  • streaming and non-streaming policy entry
  • unannotated plaintext targets
  • explicitly opaque providers and custom-target opt-in
  • mixed plaintext/opaque state
  • summary independence and stateless orphan removal
  • Combo default drop, explicit skip, and attempt isolation
  • Chat ↔ Responses translation and authentic reasoning_text replay

Additional checks passed:

  • focused ESLint
  • npm run typecheck:core
  • npm run check:changelog-integrity
  • LSP diagnostics
  • git diff --check
  • commit hooks: Prettier, ESLint, docs sync, tracked-artifact check, and type budgets

Focused coverage for reasoningInputPolicy.ts after the repair reports 86.72% statements/lines, 85.60% branches, and 90.90% functions. The earlier safe explicit-file coverage run on the broader branch reported 65.79% statements/lines, 66.49% branches, and 78.46% functions. Repository-wide coverage is intentionally left to CI because the root test glob includes a lifecycle test that can terminate listeners on port 20128.

npm run check:open-sse-typecheck still reports the unchanged release-branch GlmExecutor.buildHeaders mismatch at open-sse/executors/index.ts:239; this PR does not widen the frozen baseline.

Risk

The compatibility decision is internal and target-scoped. Plaintext remains portable by default; only provider-bound opaque state needs explicit metadata. Direct single-target calls still return 400 for genuinely incompatible active state. Combo drop never changes capability filtering, effort, budgets, provider thinking controls, or the original fallback body. Stateless input removes only reasoning records with no active plaintext or opaque continuation state.

@jackjinke

jackjinke commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor Author

Live continuation verification update:

  • Authenticated two-turn tool-call continuations passed through /v1/responses with both stream: false and stream: true.
  • DeepSeek preserved non-empty plaintext reasoning_text and accepted turn two.
  • Kimi K3 completed the tool continuation and returned the expected turn-two marker.
  • Codex preserved provider-generated opaque reasoning state and accepted turn two.
  • Native non-streaming Responses reasoning items retain their complete continuation fields through sanitization.

Current focused validation:

  • Chat continuation, cache, and request-translation suite: 191 passed, 0 failed.
  • Additional Responses streaming/translation suite: 83 passed, 0 failed.
  • Focused changed-file coverage: 65.79% statements/lines, 66.49% branches, 78.46% functions.
  • Lint, core typecheck, Open SSE typecheck, changelog integrity, LSP diagnostics, and git diff --check pass.

The PR body now separates plaintext and opaque transports, includes the Chat/Responses streaming matrix, and distinguishes completed live /v1/responses verification from the pending live /v1/chat/completions deployment verification.

@jackjinke
jackjinke force-pushed the fix/responses-reasoning-transport branch from bfa4a48 to 1060683 Compare August 17, 2026 15:53
@diegosouzapw

Copy link
Copy Markdown
Owner

This is a solid, well-scoped fix — the plaintext-vs-opaque reasoning transport split and the target-local reject/drop behavior for Combo fallback are correctly threaded through both call sites (chatCore.ts and executors/codex.ts), and the cross-layer wiring (Zod schema, DB-backed combo config, i18n'd dashboard toggle with a drop-mode warning) is complete. CI is fully green. Given the size of the diff, it may be worth one more focused pass over the transport-compatibility matrix in responsesInputPolicy.ts before merging, but I found no blocking issues.

…onses-reasoning-transport

# Conflicts:
#	open-sse/handlers/chatCore.ts
#	open-sse/handlers/responseTranslator.ts
#	src/sse/handlers/chat.ts
#	src/sse/handlers/chatHelpers.ts
#	tests/unit/chatcore-translation-paths.test.ts
@jackjinke

Copy link
Copy Markdown
Contributor Author

Post-merge regression repair is pushed in d7fcf1b63.

  • Exact store: false Responses input now drops only orphan summary-only reasoning items.
  • Active compatible reasoning state remains intact, including attached summaries; store: true or absent store still preserves summary-only history.
  • Focused post-repair suite: 58 passed, 0 failed.
  • Focused reasoningInputPolicy.ts coverage: 86.72% statements/lines, 85.60% branches, 90.90% functions.
  • Core typecheck, focused ESLint, LSP diagnostics, changelog integrity, whitespace, and commit hooks passed.

Refreshed Quality Gates run: https://github.com/diegosouzapw/OmniRoute/actions/runs/32342839553

The PR-specific Codex failure is absent from the refreshed failure list. Build, Vitest, ESLint, merge integrity, and change classification pass. The run remains red on the previously identified release/v3.8.50 drift: env/docs and pt-BR sync, mutation mapping, dead-code baseline, the GlmExecutor Open SSE type mismatch, lease inventory, rejected-request logging, heartbeat/stream/shim behavior, and OpenRouter warm-cache tests. Those are intentionally not folded into this reasoning-transport PR.

…eadfix

# Conflicts:
#	config/quality/file-size-baseline.json
@diegosouzapw
diegosouzapw merged commit 769ab62 into diegosouzapw:release/v3.8.50 Aug 21, 2026
4 of 7 checks passed
QuangBlue pushed a commit to QuangBlue/OmniRoute that referenced this pull request Sep 23, 2026
…stream content

With a Responses-API upstream (e.g. grok-cli) serving a Chat Completions
client, translate mode collects the streamed assistant content from the
raw upstream events. No branch recognised Responses events, so the
generic fallback appended every string `delta` and `text` field:
reasoning deltas, the `response.reasoning_text.done` snapshot, output
deltas, the `response.output_text.done` snapshot and even
function_call_arguments deltas. The collected content became
"Think hard.Think hard.Hello worldHello world" while the client itself
received the correct stream.

That content is not only the call-log responseBody: chatCore also feeds
it to the semantic cache, memory extraction and reasoning replay, which
now receive the real answer.

Only `response.output_text.delta` now contributes to content; every
other Responses event is kept out of the generic fallback. Reasoning is
left to the response translator, which already stores the replayable
reasoning text on `output_item.done` (diegosouzapw#10574), so it is not collected
twice. Usage estimation still counts every string delta.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
Preserves authentic plaintext reasoning continuations across Chat Completions and Responses (streaming + non-streaming), applying one target-aware reasoning transport policy before protocol translation. Fixes diegosouzapw#10550.

Validated in an isolated worktree boarded onto origin/release/v3.8.50 (0 conflicts, 39 files):
- 446/446 focused node:test tests pass (chat-route-coverage, chatcore-translation-paths, combo-attempt-body-isolation-7847, combo-config, executor-codex, kimi-coding-translator, moonshot-k3, reasoning-cache, response-sanitizer, responses-handler, responses-translation-fixes, strip-reasoning-blobs-agentic-context-1599, translator-openai-responses-req).
- 12/12 vitest tests pass (edit-connection-modal-free-models.test.tsx).
- check-changelog-integrity: OK.
- typecheck:core: clean.
- check-complexity / check-cognitive-complexity: OK, both under baseline.
- file-size: chatHelpers.ts crossed the frozen cap by +2 lines (irreducible reasoningTransportFallback option threading) — rebaselined 1017->1019 with justification, pushed to the PR branch (fix-in-place), re-validated after a base-drift re-merge against the latest release tip.

Co-authored-by: jackjinke <jackjinke@users.noreply.github.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.

fix(combos): preserve Chat reasoning for DeepSeek Responses targets

3 participants