Observed
codecoradev/cosy#63 (comment 5447415658): `cora review` failed with
failed to parse LLM JSON response: parse failed (original: EOF while parsing a value at line 1 column 0, after repair: …)
while the model (glm/glm-5.3 via Bifrost) had actually produced a valid findings JSON array — visible in backend logs.
Root cause
Default `max_tokens: 4096` is consumed by the model's `reasoning_content` (chain-of-thought) before the final answer; provider returns HTTP 200 with `content: ""` and `finish_reason: "length"`. cora:
- never reads `finish_reason` or `reasoning_content` (`src/engine/llm.rs` `ChatResponse`)
- silently defaults empty `content` to `""` and feeds it to the JSON parser, producing the misleading `EOF while parsing a value at line 1 column 0`
- retries only re-send the same 4096 budget, so the retry always fails identically
Fix direction (productivity over token frugality)
- Raise default `max_tokens` 4096 → 8192.
- Parse `finish_reason` + `reasoning_content` from the response.
- Empty content + `finish_reason=length` → auto-retry with doubled budget (up to 32768) inside `chat_completion`.
- Last resort: if `reasoning_content` contains JSON, use it as the raw response.
- Empty raw at parse layer → explicit "provider returned an EMPTY response" error instead of serde EOF noise.
Observed
codecoradev/cosy#63 (comment 5447415658): `cora review` failed with
while the model (glm/glm-5.3 via Bifrost) had actually produced a valid findings JSON array — visible in backend logs.
Root cause
Default `max_tokens: 4096` is consumed by the model's `reasoning_content` (chain-of-thought) before the final answer; provider returns HTTP 200 with `content: ""` and `finish_reason: "length"`. cora:
Fix direction (productivity over token frugality)