fix(selfhost): force JSON mode on OpenAI-compatible review calls and stop retrying deterministic-identical outputs - #8794
Conversation
…stop retrying deterministic-identical outputs (#8790) Confirmed live 2026-07-26 (the back-to-back inconclusive-review incident): the ollama fallback answered the review prompt with the same 2,814-char markdown prose on all 3 attempts — hasJsonObject:false every time — so any primary-model bail became a guaranteed "no usable verdict" manual hold, and the retry budget was pure waste (reviews run at temperature 0; identical input yields identical output). - AiRunOptions gains responseFormat: "json_object"; createOpenAiCompatibleAi forwards it as OpenAI's response_format (Ollama/vLLM honor it), with a single 400-fallback retry stripping the parameter for older servers that reject it (degrade to ask-nicely, never fail the call). Non-400 failures and 400s without the declared contract throw exactly as before. - runWorkersOpinion declares the JSON contract on every review call (other providers ignore the field; the subscription CLIs already comply via the prompt) and stops a model's retries when an attempt returns byte-identical output to the previous one — the same stop-retrying-this-model reasoning as the deliberate-bail/timeout/429 breaks. The next model keeps its full budget. New diagnostic status: identical_retry_skipped.
Logic backtestReplayed 0 historical case(s) for Backtest comparison:
|
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
loopover-ui | db49fcc | Commit Preview URL Branch Preview URL |
Jul 26 2026, 06:18 AM |
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8794 +/- ##
=======================================
Coverage ? 92.26%
=======================================
Files ? 797
Lines ? 79589
Branches ? 24118
=======================================
Hits ? 73434
Misses ? 5090
Partials ? 1065
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
Tip ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-26 06:30:21 UTC
Review summary Nits — 6 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
Summary
Half of the back-to-back "dual-model AI review did not return a usable verdict" incident fix (PRs #8735/#8737, traced 2026-07-26). Production logs showed the ollama fallback answering the review prompt with byte-identical 2,814-char markdown prose on all three attempts — structurally incapable of rescuing a failed primary, converting every primary bail into a manual-review hold. Two mechanical fixes:
Changes
src/selfhost/ai.ts—AiRunOptions.responseFormat: "json_object"; the OpenAI-compatible chat path forwards it asresponse_format(Ollama's and vLLM's compat layers honor it — the model is forced into JSON mode instead of merely asked). A server that rejects the parameter with a 400 gets exactly one retry without it (degrade to the pre-change ask-nicely posture, never fail the call); non-400 errors and 400s without the declared contract throw unchanged, single fetch.src/services/ai-review.ts— reviews declare the JSON contract on everyai.runcall (ignored by the CLIs, which already comply via prompt). The per-model retry loop stops when an attempt returns byte-identical output to the previous attempt — reviews run at temperature 0, so a repeat is deterministic and the remaining budget provably useless (same reasoning as the existing deliberate-bail/timeout/429/structural breaks; the fallback model keeps its own full budget). Newidentical_retry_skippeddiagnostic status +ai_review_provider_identical_retry_skippedlog event.Two pre-existing tests asserting the full 3×2 retry budget used byte-identical fixtures incidentally; updated to the new per-slot 2-attempt contract with a varying-output sibling test pinning that ONLY determinism short-circuits.
Test plan
npm run typecheck— cleanCloses #8790