feat: judge spend guard with deterministic checks, result caching, and a per-run judge budget - #51
Open
brainsparker wants to merge 1 commit into
Open
feat: judge spend guard with deterministic checks, result caching, and a per-run judge budget#51brainsparker wants to merge 1 commit into
brainsparker wants to merge 1 commit into
Conversation
…ache, and per-run judge budget
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Adds a judge spend guard: three layers that cut LLM-as-judge cost without giving up judge quality. All local, no new dependencies.
--no-judge-cacheto disable). Verdicts are cached in<output_dir>/.promptlens/judge_cache.json, keyed by a SHA-256 of the judge model and settings, the test case, and the exact response text (including tool calls). Re-running over unchanged responses reuses the verdict at zero cost. Failed judge calls are never cached. Cached verdicts are markedcached: truein exports.judge.budget_usdin config or--judge-budget 5.00on the CLI). Once the budget is exhausted, remaining judge calls are skipped, each affected result recordsjudge_skipped_reason, and the summary reports the skip count. Model-under-test calls are never blocked. Documented caveat: with concurrency the budget can overshoot by up toparallel_requests - 1in-flight calls; it is a circuit breaker, not exact accounting.Judge spend is now also tracked as a first-class number:
JudgeScore.cost_usd, run-leveljudge_cost_usdin JSON exports, and a Judge Cost line plus a Judge Spend Guard section in the run summary. Previously judge calls were entirely absent from cost reporting.Usage
Why this feature, why now
Judge cost runaway is the loudest complaint in the eval tooling space right now, and no lightweight local-first tool ships a guard for it:
Runner-up candidates considered today and passed on: an MCP 2026-07-28 conformance assertion pack (bigger surface, better as a follow-up once this deterministic-check foundation exists) and an OpenTelemetry
gen_ai.evaluation.resultemitter (the semconv is still Development status, and it would add a dependency).Implementation notes
promptlens/models/checks.py(CheckSpec validation plus the check engine),promptlens/judges/cache.py(JudgeCache),promptlens/judges/spend.py(JudgeSpendTracker).Runner._judge_with_guardorchestrates the order: checks gate, then cache, then budget, then a paid judge call whose verdict is recorded and cached.LLMJudgenow reportscost_usdon verdicts and marks failed calls witherrorso they are never cached.promptlens compareis unaffected.Test status
pytest tests.test_deterministic_checks.py(spec validation and engine outcomes for all 8 check types),test_judge_cache.py(key stability, roundtrip, corrupt file recovery, error verdicts never cached),test_judge_spend_guard.py(gating skips the judge, cache hits skip the judge, budget exhaustion skips and records reasons, gated cases consume no budget).judge_cost_usd, check results, cached flags, and skip reasons.promptlens validatepasses on the updated example golden set;promptlens run --dry-runaccepts the new flags.