Skip to content

feat: add reasoning_content support for OpenRouter chat completions#88

Merged
jpr5 merged 1 commit into
mainfrom
feat/openrouter-reasoning-content
Apr 7, 2026
Merged

feat: add reasoning_content support for OpenRouter chat completions#88
jpr5 merged 1 commit into
mainfrom
feat/openrouter-reasoning-content

Conversation

@AlemTuzlak

Copy link
Copy Markdown
Contributor

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.tsbuildTextChunks 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

  • Streaming: reasoning_content deltas emitted before content deltas
  • Streaming: reasoning_content deltas reconstruct full reasoning text
  • Streaming: content deltas still reconstruct full text
  • Streaming: no reasoning_content when fixture has no reasoning
  • Non-streaming: reasoning_content present in message
  • Non-streaming: reasoning_content absent when no reasoning
  • Stream-collapse: extracts reasoning_content from chat completions deltas
  • All 2034 existing tests still pass

🤖 Generated with Claude Code

@pkg-pr-new

pkg-pr-new Bot commented Apr 7, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@copilotkit/aimock@88

commit: 66e61be

jpr5
jpr5 previously approved these changes Apr 7, 2026

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

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): passes response.reasoning through to both builders — clean plumbing
  • Stream collapse (collapseOpenAISSE): extracts reasoning_content from 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.
@jpr5 jpr5 force-pushed the feat/openrouter-reasoning-content branch from fc6a74c to 66e61be Compare April 7, 2026 21:38
@jpr5 jpr5 merged commit fd4bf4a into main Apr 7, 2026
18 checks passed
@jpr5 jpr5 deleted the feat/openrouter-reasoning-content branch April 7, 2026 22:12
@jpr5

jpr5 commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

Thank you! 🙏

jpr5 added a commit that referenced this pull request Apr 8, 2026
## 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)
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.

2 participants