feat: deterministic checks, zero-cost assertions that run alongside the LLM judge - #55
Open
brainsparker wants to merge 1 commit into
Open
feat: deterministic checks, zero-cost assertions that run alongside the LLM judge#55brainsparker wants to merge 1 commit into
brainsparker wants to merge 1 commit into
Conversation
Adds a checks field to test cases: contains, not_contains, regex, not_regex, exact_match, is_valid_json, max_latency_ms, max_cost_usd, min_length, max_length. Checks run in plain Python with no judge tokens, surface in the CLI summary, HTML report, and JUnit XML export, and can gate CI on their own. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
checksfield to golden-set test cases: deterministic assertions evaluated in plain Python against the model response. No judge tokens, no extra latency, no API calls. The LLM judge keeps grading nuanced quality; checks catch hard rule violations and budget overruns.Ten check types ship in this PR:
contains/not_containsregex/not_regexexact_matchis_valid_jsonmax_latency_ms/max_cost_usdmin_length/max_lengthString checks accept
case_insensitive: true. Example:Check outcomes surface in three places: the CLI run summary (per-model
Checks: N/M passed), the HTML report (pass/fail pills per response, reason on hover), and the JUnit XML export, where any failing check marks the test case as a CI failure regardless of judge score. A case whose checks all pass counts as a pass even with judging disabled, so a team can gate a build purely on deterministic checks with zero judge spend.Why this feature, why now
--fail-underand JUnit export, but every gate currently requires paying for judge calls on every push. Checks close that gap: run checks on every commit, save the judge for nightly or pre-release runs.checksdefaults to an empty list, existing golden sets and serialized results load unchanged, and no new dependencies are added (JSON validation uses the stdlib).Runner-up candidates considered today and passed on: a GitHub Copilot export target for you.md (strong fit but a much smaller change), and A2A v1.0 signed agent cards for youagent (timely but riskier given RFC 8785 canonicalization precision and no existing test coverage in that module).
Implementation notes
promptlens/models/checks.py:CheckDefinition(validated per type: string checks need strings, budget checks need non-negative numbers, regexes must compile at load time) andCheckResult.promptlens/checks/engine.py: pure-Pythonevaluate_checks().is_valid_jsonalso accepts a response that is entirely one fenced code block, a common LLM output shape. A check that raises unexpectedly is recorded as a failed check rather than killing the run.promptlens/runners/runner.py: checks run after generation and before judging, skipped when the response errored.promptlens/exporters/junit_exporter.py: precedence is error > failed checks > judge threshold. Failing checks emit one aggregated<failure type="DeterministicCheckFailed">listing each failed check and reason; all check outcomes are written to system-out.promptlens/exporters/html_exporter.py+templates/report.html: pass/fail check pills per model response.EvaluationResultgainscheck_results,checks_passed, andfailed_checks;RunResultgainsget_check_stats().examples/golden_sets/deterministic_checks.yaml(passespromptlens validate), plus README docs for the check table and CI behavior.Test status
tests/test_checks.py: definition validation, every check type (pass, fail, case folding, fenced JSON, None-cost handling), declaration-order preservation, result helpers, backward compatibility of old serialized results, and JUnit integration (failure element, precedence, system-out, checks-only pass semantics).promptlens validateon the new example golden set, and an HTML render smoke test confirming check pills appear in the report.Opened by Daily Feature Builder.
🤖 Generated with Claude Code