Skip to content

feat: thinking-aware synthesis budgets #62

Description

@moritzthecat

Problem or use case

Reasoning summary models silently degrade to the deterministic fallback: the per-batch output budget is thinking-unaware, so thinking tokens + answer can never fit and every synthesis batch stops at length

Problem

In v9.6.2 each synthesis call's output budget is:

// src (decoded from 9.6.2 dist)
MODE_POLICIES = { balanced: { batchOutput: { min: 1000, perChunk: 250, max: 4096 }, ... }, ... }
batchOutputLimit(mode, chunks, providerMax)
  = Math.min(Math.max(budget.min, chunks * budget.perChunk), budget.max, providerMax)

so for the default balanced mode the per-batch max_tokens is capped at 4,096 (clamped to model.maxTokens in trackedComplete). This budget was designed for answer-only generation.

A reasoning summary model with thinking enabled must emit thinking tokens + answer tokens within the same budget — this is documented by the local-server provider itself:

"Pi resolves max_tokens … for each turn — so this is a real generation cap, and reasoning and the answer come out of the same budget. … A reasoning model that thinks its way to the ceiling spends the entire budget reasoning and returns no answer at all — which then reads as a truncated turn."

Once the per-batch input grows (measured threshold: session context ≈ 55–60 % of the model window), the model's thinking alone exceeds 4,096 tokens → the server stops the stream with finish reason length → the batch response is rejected (Malformed batch summary response: non-terminal stop reason length) → the pipeline degrades to its deterministic template fallback.

The degradation is nearly invisible to the user: the surfaced warning is only

Synthesis batch stopped · deterministic evidence fallback preserved coverage

— no stop reason, no hint that thinking tokens were the cause. The length reason appears only in the smart-compact debug log. There is also no warning at run start even when the configured summary model is a reasoning model with thinking enabled and a budget that small.

Net effect: the extension's headline value (a verified LLM summary) is silently replaced by the deterministic template on every compaction, for any local reasoning model whose server keeps thinking on by default — Qwen3.8-27B, Gemma-4, Llama-108B all default to thinking-on when served through LM Studio (the provider cannot disable thinking per-request for models on unmeasured backends; the page/server default is the only control).

Measured evidence (one machine, one model, one budget — only the thinking state differs)

48 GB Apple-Silicon box, LM Studio, summary model google/gemma-4-12b-qat (262 k window, 65 k maxTokens registry, balanced mode → 4,096/batch), both runs verified via prepareRun debug line:

run page thinking context synthesis result
2026-09-15 22:44 on (checkpoint default) 79 % 4/4 calls stopped at length → 2× "Synthesis batch stopped · deterministic fallback" warnings; lower-quality fallback summary; ~3 min
2026-09-16 01:5x off ~67 % 0 warnings, 0 errors, ~1 min, clean verified LLM summary, context → ~24 %

Supporting data point (qwen3.8-27b, 09/15 11:04, reduced-power profile): one synthesis call emitted 5,999 output tokens of which only 1,976 were the summary — ≈4 k thinking tokens competing for the same budget; took 17.5 min.

Workaround (verified working in our setup, but user-side and undocumented): set smartCompact.summaryThinkingLevel: "off" and disable thinking on the summary model in the server UI.

Impact

  • Users with local reasoning models (the fastest-growing local-model class: Qwen3.8, Gemma-4, Llama-108B thinking builds) get template-quality summaries and a vague "coverage preserved" warning on every compaction above ~55–60 % context — with no diagnosable signal in the normal output.
  • The working LLM path (thinking off) is undiscoverable from the warning text or the config docs.

Proposed solution

Proposed fix (options, any or all)

  1. Thinking-aware budget: when the summary model is registered as a reasoning model, multiply the per-batch output budget by a configurable thinking factor (e.g. batchThinkingFactor, default 2–3) or reserve a floor for the answer — in batchOutputLimit / the per-call maxTokens clamp.
  2. Self-healing retry: on a batch stop reason length for a reasoning model, retry that batch with reasoning: "off". The plumbing already exists — trackedComplete resolves summaryThinkingLevel into the per-call reasoning option (configuredReasoning), so a per-batch downgrade is a small change.
  3. Actionable warning: surface the stop reason in the user-visible warning: Synthesis batch stopped (stop reason: length) · deterministic fallback — thinking tokens may have exhausted the 4 k output budget; set summaryThinkingLevel to "off" for reasoning models (paraphrased).
  4. Preflight notice: in prepareRun, warn when the summary model is reasoning + thinking enabled + budget below a safe threshold, so the user knows before the run degrades.

Compatibility / scope

  • Non-reasoning summary models are unaffected (factor applies only when model.reasoning is true).
  • The thinking-off path is verified in production (table above); no watchdog/latency interaction (those are the companion timeout issue).
  • No API/config breakage: option 1 adds a config key with a default; options 2–4 are behavior-only.

Alternatives considered

No response

Area

extraction

Additional context

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions