Skip to content

[Bug] shouldDisableReasoningForJson regex doesn’t match deepseek-v4-flash → wastes ~88-94% of output tokens on reasoning trace #929

Description

@DerekEXS

Title

[Bug] shouldDisableReasoningForJson regex doesn't match deepseek-v4-flash → wastes ~88-94% of output tokens on reasoning trace

Body

Summary

shouldDisableReasoningForJson in src/llm-client.ts (line 102-103) currently uses the regex:

return /qwen3|deepseek.*r1|qwq/i.test(model);

This regex does not match deepseek-v4-flash (the v4 family ships without an explicit r1 suffix). When the model is configured for JSON extraction in smart-extractor, the LLM client therefore does not inject the chat_template_kwargs: { enable_thinking: false } flag, and DashScope's deepseek-v4-flash returns the answer only in the reasoning_content field with content empty.

PR #914 added a graceful fallback (recoverJsonFromReasoning) that rescues the JSON from the reasoning trace, so extraction still succeeds — but the regex still leaves the door open for downstream inefficiency:

Impact (measured)

Using DashScope OpenAI-compatible endpoint with model deepseek-v4-flash and 6-category extraction prompt:

Metric Value
Tokens in prompt ~3,200
reasoning_content tokens (per call) ~2,800
content tokens (per call) ~50
Reasoning share of output ~88–94%
Estimated monthly waste on 200 extractions/day ~150M reasoning tokens

Since deepseek-v4-flash accepts chat_template_kwargs: { enable_thinking: false } on DashScope (verified end-to-end), this is pure waste — the model does have a non-thinking mode and the client just isn't asking for it.

Reproduction

  1. Set llm.model to deepseek-v4-flash (DashScope OpenAI-compatible endpoint).
  2. Trigger autoCapture / smart-extractor with a non-trivial conversation.
  3. Inspect gateway logs / LLM request payload — note that chat_template_kwargs is not present in the request body.
  4. Inspect LLM response — choices[0].message.content is empty or near-empty; the JSON lives inside reasoning_content. (PR fix(smart-extractor): only learn noise from a genuine empty extraction; recover JSON from the reasoning field #914's recoverJsonFromReasoning rescues it, so extraction still works, but at the cost above.)

Expected

The regex should treat the deepseek-v* family (non-reasoning variants) the same way it treats deepseek.*r1 — i.e., inject enable_thinking: false so the model emits JSON directly into content.

Suggested fix

One-line patch in src/llm-client.ts:

- return /qwen3|deepseek.*r1|qwq/i.test(model);
+ return /qwen3|deepseek.*(r1|v\d)|qwq/i.test(model);

This makes the regex match deepseek-v4-flash, deepseek-v4-pro (the pro variant also accepts enable_thinking: false on DashScope), and any future deepseek-vN-* releases, while keeping the existing matches for deepseek-r1, qwen3, and qwq.

Workaround (in use locally)

Currently working around by configuring DashScope's deepseek-v4-flash directly and accepting the reasoning-token waste until the regex is fixed upstream. recoverJsonFromReasoning from PR #914 prevents the worst-case extraction failure, so this is a performance/cost issue rather than a correctness issue.

Related

Environment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions