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

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions