feat: add reasoning_content support for OpenRouter chat completions#88
Conversation
commit: |
jpr5
left a comment
There was a problem hiding this comment.
PR Review Summary
Reviewed by 7 specialized agents (code-reviewer, silent-failure-hunter, test-analyzer, comment-analyzer, type-design-analyzer, test-file-reviewer, CI/packaging) across all 6 changed files.
Zero actionable findings — clean PR.
The implementation is straightforward and well-executed. reasoning_content is correctly threaded through the full pipeline:
- Types (
SSEDelta,ChatCompletionMessage): optional, not nullable — matches OpenRouter's wire format - Helpers (
buildTextChunks,buildTextCompletion): reasoning chunks emitted before content (streaming), conditional spread for non-streaming — both correct - Server (
handleCompletions): passesresponse.reasoningthrough to both builders — clean plumbing - Stream collapse (
collapseOpenAISSE): extractsreasoning_contentfrom deltas using the same defensive pattern as existing content extraction
Tests are solid — 8 new tests covering streaming ordering, content/reasoning reconstruction, absence cases, and stream-collapse extraction. Assertions are specific (exact string equality, index comparison for ordering), not weak.
One note
The branch is ~21 commits behind main (branched before v1.8.0). A rebase before merge would confirm no conflicts with the reasoning work that landed in PR #81.
🤖 Reviewed with Claude Code
OpenRouter returns reasoning via reasoning_content in the chat completions message/delta format. This adds support for emitting and collapsing reasoning_content in both streaming and non-streaming responses.
fc6a74c to
66e61be
Compare
|
Thank you! 🙏 |
## Summary - **Per-test sequence isolation** via `X-Test-Id` header — each test gets its own fixture match counters across all 12 HTTP + 3 WebSocket handlers (#93) - **Combined content + toolCalls** in fixture responses — new `ContentWithToolCallsResponse` type across OpenAI Chat, Responses, Anthropic Messages, and Gemini with stream collapse support (#92) - **OpenRouter reasoning_content** support (#88) - **Clean URLs** for docs site — all pages restructured as directories, .html extensions removed from links - Fix `web_search_call` items to use `action.query` matching real OpenAI API (#89) - Bump aimock-pytest to 0.3.0 ## Version bumps - `package.json`: 1.8.0 → 1.9.0 - `pyproject.toml`: 0.2.0 → 0.3.0 - `Chart.yaml` appVersion: 1.8.0 → 1.9.0 - `plugin.json` / `marketplace.json`: 1.8.0 → 1.9.0 ## Test plan - [x] All 2206 tests pass (including 80 new clean-URL validation tests) - [x] Build passes - [x] Prettier clean - [ ] npm publish triggers automatically on merge via OIDC workflow - [ ] Verify v1.9.0 appears on npm after merge 🤖 Generated with [Claude Code](https://claude.com/claude-code)
) ## Summary - Adds `reasoning_content` field to chat completions streaming (`delta.reasoning_content`) and non-streaming (`message.reasoning_content`) responses - Updates `collapseOpenAISSE` to extract `reasoning_content` from chat completions delta chunks for record-and-replay - OpenRouter models that support reasoning (DeepSeek, Claude via OpenRouter, etc.) return thinking tokens via this field — aimock previously only supported reasoning for the Responses API and Anthropic Messages API ## What's included This PR covers the **aimock mock server** side — emitting `reasoning_content` in `/v1/chat/completions` responses when a fixture has `reasoning` set. It does NOT cover any additional OpenRouter-specific behavior (e.g. `reasoning_details`, model routing, provider preferences). Those may need separate work. ## Files changed - `src/types.ts` — added `reasoning_content` to `SSEDelta` and `ChatCompletionMessage` - `src/helpers.ts` — `buildTextChunks` and `buildTextCompletion` now accept and emit reasoning - `src/server.ts` — passes `response.reasoning` through to chat completions builders - `src/stream-collapse.ts` — extracts `reasoning_content` from chat completions deltas - Tests — 5 new tests covering streaming, non-streaming, absence, delta reconstruction, and stream-collapse ## Test plan - [x] Streaming: reasoning_content deltas emitted before content deltas - [x] Streaming: reasoning_content deltas reconstruct full reasoning text - [x] Streaming: content deltas still reconstruct full text - [x] Streaming: no reasoning_content when fixture has no reasoning - [x] Non-streaming: reasoning_content present in message - [x] Non-streaming: reasoning_content absent when no reasoning - [x] Stream-collapse: extracts reasoning_content from chat completions deltas - [x] All 2034 existing tests still pass 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## Summary - **Per-test sequence isolation** via `X-Test-Id` header — each test gets its own fixture match counters across all 12 HTTP + 3 WebSocket handlers (#93) - **Combined content + toolCalls** in fixture responses — new `ContentWithToolCallsResponse` type across OpenAI Chat, Responses, Anthropic Messages, and Gemini with stream collapse support (#92) - **OpenRouter reasoning_content** support (#88) - **Clean URLs** for docs site — all pages restructured as directories, .html extensions removed from links - Fix `web_search_call` items to use `action.query` matching real OpenAI API (#89) - Bump aimock-pytest to 0.3.0 ## Version bumps - `package.json`: 1.8.0 → 1.9.0 - `pyproject.toml`: 0.2.0 → 0.3.0 - `Chart.yaml` appVersion: 1.8.0 → 1.9.0 - `plugin.json` / `marketplace.json`: 1.8.0 → 1.9.0 ## Test plan - [x] All 2206 tests pass (including 80 new clean-URL validation tests) - [x] Build passes - [x] Prettier clean - [ ] npm publish triggers automatically on merge via OIDC workflow - [ ] Verify v1.9.0 appears on npm after merge 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Summary
reasoning_contentfield to chat completions streaming (delta.reasoning_content) and non-streaming (message.reasoning_content) responsescollapseOpenAISSEto extractreasoning_contentfrom chat completions delta chunks for record-and-replayWhat's included
This PR covers the aimock mock server side — emitting
reasoning_contentin/v1/chat/completionsresponses when a fixture hasreasoningset. It does NOT cover any additional OpenRouter-specific behavior (e.g.reasoning_details, model routing, provider preferences). Those may need separate work.Files changed
src/types.ts— addedreasoning_contenttoSSEDeltaandChatCompletionMessagesrc/helpers.ts—buildTextChunksandbuildTextCompletionnow accept and emit reasoningsrc/server.ts— passesresponse.reasoningthrough to chat completions builderssrc/stream-collapse.ts— extractsreasoning_contentfrom chat completions deltasTest plan
🤖 Generated with Claude Code