Bug Description
Mira's review pipeline silently truncates findings when a chunk produces more output than llm.max_tokens (default 4096) allows. The LLM stops mid-JSON (finish_reason: length), and the response parser's repair logic silently re-balances braces so json.loads succeeds with a shorter comments array — tail findings are dropped without error.
Steps to Reproduce
- Configure mira with default
llm.max_tokens=4096 (or leave unset).
- Submit a PR with a chunk dense enough to generate >10 review findings (e.g., multiple medium-severity issues in a single file).
- Observe that only the first several findings appear in the review; the remainder are silently absent.
Expected
All findings generated by the review LLM call are preserved. If output exceeds the token budget, mira either uses a larger budget for review calls or surfaces a visible warning.
Actual
The review path is engine._review_chunk → self.llm.review() → complete_with_tools() → _call_llm_with_tools(). Every hop hardcodes self.config.max_tokens (default 4096), and complete_with_tools does not accept a max_tokens override at all. When the LLM exhausts its budget, the tool-call JSON ends mid-array. response_parser's _repair_json/_balance_json close dangling braces so parsing succeeds, and LLMReviewResponse.model_validate accepts the truncated comments array. No finish_reason inspection occurs.
Affected Code
src/mira/config.py:50 — LLMConfig.max_tokens default 4096, no per-purpose override
src/mira/llm/provider.py:214 — _call_llm_with_tools hardcodes max_tokens: self.config.max_tokens
src/mira/llm/provider.py:341 — _call_llm_agentic hardcodes the same
src/mira/llm/provider.py:403-421 — complete_with_tools() signature accepts no max_tokens (unlike complete() at 270-285)
src/mira/core/engine.py:1270-1286 — _review_chunk and ensemble calls pass no max_tokens
src/mira/core/passes.py:383-386 — self_critique uses complete_with_tools with no override
src/mira/llm/response_parser.py:51-133 — _repair_json/_balance_json silently re-balance truncated JSON without surfacing the truncation
Root Cause Analysis
The cause chain is: review calls cannot override max_tokens → default 4096 is too small for large finding batches (each comment is ~200-500 tokens in tool_schemas.py:12-108) → finish_reason: length fires mid-JSON → repair path produces valid-but-incomplete JSON → no error is raised, tail comments disappear. The indexing path already encountered this and added a ~16k cap (indexer.py:385-390) with the explicit comment: "4096 — large batches were getting truncated mid-JSON (finish_reason: length)". The review path lacks the same protection.
Possible Fix
- Thread a
max_tokens parameter through complete_with_tools/_call_llm_with_tools/_call_llm_agentic, mirroring complete() (provider.py:270).
- Have review callers (
engine._review_chunk, agentic loop, self_critique, security/dependency passes) pass a capped override — model max_output_tokens, default ~16384 like the indexer.
- Inspect
finish_reason in _call_llm_with_tools; if it is "length", log a visible warning or raise so truncation is surfaced rather than silently absorbed.
Related Issues
None.
References
Bug Description
Mira's review pipeline silently truncates findings when a chunk produces more output than
llm.max_tokens(default 4096) allows. The LLM stops mid-JSON (finish_reason: length), and the response parser's repair logic silently re-balances braces sojson.loadssucceeds with a shortercommentsarray — tail findings are dropped without error.Steps to Reproduce
llm.max_tokens=4096(or leave unset).Expected
All findings generated by the review LLM call are preserved. If output exceeds the token budget, mira either uses a larger budget for review calls or surfaces a visible warning.
Actual
The review path is
engine._review_chunk→self.llm.review()→complete_with_tools()→_call_llm_with_tools(). Every hop hardcodesself.config.max_tokens(default 4096), andcomplete_with_toolsdoes not accept amax_tokensoverride at all. When the LLM exhausts its budget, the tool-call JSON ends mid-array.response_parser's_repair_json/_balance_jsonclose dangling braces so parsing succeeds, andLLMReviewResponse.model_validateaccepts the truncatedcommentsarray. Nofinish_reasoninspection occurs.Affected Code
src/mira/config.py:50—LLMConfig.max_tokensdefault 4096, no per-purpose overridesrc/mira/llm/provider.py:214—_call_llm_with_toolshardcodesmax_tokens: self.config.max_tokenssrc/mira/llm/provider.py:341—_call_llm_agentichardcodes the samesrc/mira/llm/provider.py:403-421—complete_with_tools()signature accepts nomax_tokens(unlikecomplete()at 270-285)src/mira/core/engine.py:1270-1286—_review_chunkand ensemble calls pass nomax_tokenssrc/mira/core/passes.py:383-386—self_critiqueusescomplete_with_toolswith no overridesrc/mira/llm/response_parser.py:51-133—_repair_json/_balance_jsonsilently re-balance truncated JSON without surfacing the truncationRoot Cause Analysis
The cause chain is: review calls cannot override
max_tokens→ default 4096 is too small for large finding batches (each comment is ~200-500 tokens intool_schemas.py:12-108) →finish_reason: lengthfires mid-JSON → repair path produces valid-but-incomplete JSON → no error is raised, tail comments disappear. The indexing path already encountered this and added a ~16k cap (indexer.py:385-390) with the explicit comment:"4096 — large batches were getting truncated mid-JSON (finish_reason: length)". The review path lacks the same protection.Possible Fix
max_tokensparameter throughcomplete_with_tools/_call_llm_with_tools/_call_llm_agentic, mirroringcomplete()(provider.py:270).engine._review_chunk, agentic loop,self_critique, security/dependency passes) pass a capped override — modelmax_output_tokens, default ~16384 like the indexer.finish_reasonin_call_llm_with_tools; if it is"length", log a visible warning or raise so truncation is surfaced rather than silently absorbed.Related Issues
None.
References