Skip to content

fix: stream reasoning_content in LM Studio provider - #1175

Open
daewoongoh wants to merge 1 commit into
Zoo-Code-Org:mainfrom
daewoongoh:fix/lmstudio-reasoning-content
Open

fix: stream reasoning_content in LM Studio provider#1175
daewoongoh wants to merge 1 commit into
Zoo-Code-Org:mainfrom
daewoongoh:fix/lmstudio-reasoning-content

Conversation

@daewoongoh

@daewoongoh daewoongoh commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Related GitHub Issue

Closes: #1075

Description

Reasoning models served by LM Studio stream their thinking in a dedicated reasoning_content (or
reasoning) delta field. The LM Studio handler only read delta.content and passed it through
TagMatcher to extract <think>/<thought> tags, so the entire thought stream was silently
dropped for these models.

The repo already has a shared helper for exactly this — extractReasoningFromDelta in
src/api/providers/utils/extract-reasoning.ts — used by 13 providers (openai, deepseek,
lite-llm, kenari, mimo, qwen-code, requesty, unbound, opencode-go,
base-openai-compatible-provider, …). LM Studio was the only one still missing it. That is why
the reporter saw the same model + same LM Studio instance display thinking correctly through the
OpenAI Compatible provider but not through the LM Studio provider.

The fix mirrors the existing call site in openai.ts rather than introducing a new pattern:

const reasoningText = extractReasoningFromDelta(delta)
if (reasoningText) {
    reasoningOutput += reasoningText
    yield { type: "reasoning", text: reasoningText }
}

Test Procedure

Automated. Three tests added to src/api/providers/__tests__/lmstudio.spec.ts, following the
existing patterns in openai.spec.ts:

  • streams reasoning chunks from delta.reasoning_content — the issue reproduction; asserts both the
    reasoning chunk and the text chunk are emitted, so a regression that breaks content handling
    is also caught
  • falls back to delta.reasoning when reasoning_content is absent — the helper's fallback path
  • still parses <think> tags embedded in content — regression guard for models that inline reasoning
    as tags (this one passes before the fix too; it exists to pin existing behaviour)

Verified these are genuine regression tests, not just passing tests: with the handler reverted to its
pre-fix state the reasoning tests fail (0 reasoning chunks emitted); with the fix they pass.

cd src
npx vitest run api/providers/__tests__/lmstudio.spec.ts

Manual verification was not possible — I don't have an LM Studio instance with a reasoning model
in this environment. The tests exercise the delta shapes the issue describes, but a reviewer with the
reporter's setup (LM Studio 0.4.20 + Qwen3.6 35B A3B) confirming thinking now renders would be
valuable before merge.

Pre-Submission Checklist

  • Issue Linked: This PR is linked to an approved GitHub Issue (see "Related GitHub Issue" above).
  • Scope: My changes are focused on the linked issue (one major feature/fix per PR).
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New and/or updated tests have been added to cover my changes (if applicable).
  • Visual Snapshot (UI changes only): N/A — no UI changes; this is provider stream-parsing logic.
  • Documentation Impact: I have considered if my changes require documentation updates (see "Documentation Updates" section below).
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Visual Snapshots

N/A

Videos (interaction / animation only)

N/A

Documentation Updates

  • No documentation updates are required.

Additional Notes

N/A

Get in Touch

hehegwk_23849

Summary by CodeRabbit

  • Improvements
    • Improved LM Studio streaming to correctly capture and display AI reasoning.
    • Added fallback handling for alternate reasoning formats.
    • Reasoning is now included in output token usage counts.
    • Preserved support for reasoning embedded in <think> tags.

The LM Studio handler only read `delta.content` and relied on TagMatcher to
extract <think>/<thought> tags. Reasoning models served by LM Studio (Qwen3,
DeepSeek-R1, QwQ) stream thinking in a dedicated `reasoning_content` field,
which TagMatcher never sees, so the whole thought stream was dropped. The
same model via the OpenAI Compatible provider worked because that handler
already calls the shared `extractReasoningFromDelta` helper — LM Studio was
the last provider missing it.

Reasoning text is now also counted toward output tokens; thinking models
previously reported ~0 output tokens for turns that were mostly reasoning.

Fixes Zoo-Code-Org#1075
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

LM Studio streaming now extracts reasoning from reasoning_content and reasoning delta fields, emits separate reasoning events, preserves <think> tag parsing, and includes reasoning in output-token counts. Tests cover each streaming format.

Changes

LM Studio reasoning output

Layer / File(s) Summary
Reasoning delta extraction and accounting
src/api/providers/lm-studio.ts
The provider extracts dedicated reasoning deltas, emits reasoning events separately from visible content, preserves TagMatcher processing, and counts both output types.
Reasoning stream regression coverage
src/api/providers/__tests__/lmstudio.spec.ts
Tests cover reasoning_content, the reasoning fallback, and embedded <think> tags.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

Possibly related PRs

Suggested labels: awaiting-review

Suggested reviewers: edelauna, navedmerchant

Sequence Diagram(s)

sequenceDiagram
  participant LMStudio
  participant Provider
  participant TagMatcher
  participant Output
  LMStudio->>Provider: Send streaming delta
  Provider->>Provider: Extract reasoning_content or reasoning
  Provider->>Output: Emit reasoning event
  Provider->>TagMatcher: Process visible content
  TagMatcher->>Output: Emit text or tagged reasoning event
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes streaming reasoning content in the LM Studio provider.
Description check ✅ Passed The description includes the linked issue, implementation details, test procedure, checklist, and documentation assessment.
Linked Issues check ✅ Passed The changes satisfy issue #1075 by streaming dedicated LM Studio reasoning fields while preserving existing content and tag parsing.
Out of Scope Changes check ✅ Passed All code and test changes are focused on fixing LM Studio reasoning stream handling for issue #1075.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/api/providers/__tests__/lmstudio.spec.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.

src/api/providers/lm-studio.ts

ESLint skipped: the ESLint configuration for this file references a package that is not available in the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (2)
src/api/providers/__tests__/lmstudio.spec.ts (2)

129-133: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the new output-token contract.

Add a deterministic assertion for the usage.outputTokens chunk. It must include both dedicated reasoning text and visible content; otherwise the accounting change in src/api/providers/lm-studio.ts Lines 165-167 can regress without failing these tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/providers/__tests__/lmstudio.spec.ts` around lines 129 - 133, Add an
assertion in the test covering handler.createMessage to verify the
usage.outputTokens chunk includes both dedicated reasoning text and visible
content, using deterministic expected values from the mocked stream. Preserve
the existing reasoning and text chunk assertions.

129-133: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Assert the emitted chunk order.

src/test-utils/stream.ts yields fixture chunks in order, but toContainEqual checks only membership. In the reasoning_content and <think> cases, compare the filtered reasoning and text chunks as an ordered array so a text-before-reasoning regression fails.

Also applies to: 164-166

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/providers/__tests__/lmstudio.spec.ts` around lines 129 - 133, Update
the assertions in the affected reasoning_content and <think> test cases around
handler.createMessage to filter the emitted chunks to reasoning and text entries
and compare the resulting array in order, expecting reasoning before text.
Replace the membership-only toContainEqual checks while preserving the existing
chunk values and test coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/api/providers/__tests__/lmstudio.spec.ts`:
- Around line 129-133: Add an assertion in the test covering
handler.createMessage to verify the usage.outputTokens chunk includes both
dedicated reasoning text and visible content, using deterministic expected
values from the mocked stream. Preserve the existing reasoning and text chunk
assertions.
- Around line 129-133: Update the assertions in the affected reasoning_content
and <think> test cases around handler.createMessage to filter the emitted chunks
to reasoning and text entries and compare the resulting array in order,
expecting reasoning before text. Replace the membership-only toContainEqual
checks while preserving the existing chunk values and test coverage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c6e16a66-69ca-4e14-bc60-a059e33b59f5

📥 Commits

Reviewing files that changed from the base of the PR and between b2335fa and 306b9a8.

📒 Files selected for processing (2)
  • src/api/providers/__tests__/lmstudio.spec.ts
  • src/api/providers/lm-studio.ts

@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-review PR changes are ready and waiting for maintainer re-review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] "Thinking" content is not displayed in the "lm studio provider" + "lmstudio" + "qwen3.6-35b-a3b" environment.

1 participant