Repository navigation
Conversation
Prediction rows now carry live_calls, cache_hit_calls and live_elapsed_seconds, filled by every row writer (sync household and single-output, chunked, batch, row repair) from the same cache_hit flag the spend ledger records. Rows written before the columns existed stay empty, and the supervisor still accepts their scenario files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…is.py Hypothesis drives run_no_tools_eval and the single-output writer through the real request path (chunks, repair rounds, budget escalation and explanation repair) with a fake clock and per-call cache flags. It checks each row's invariants, the counts against the spend ledger's records of the same calls, the CSV, and a byte-identical run-store round trip. Further tests cover batch rows, row repair and its header-widening append, the retry merge, resumed outputs, run-store CSVs that predate the columns, and supervised resumes of scenario files written before them. household_call_provenance moves to policybench/call_provenance.py, so analysis.py, whose hash the paper prints as the scoring code, is unchanged. The runbook documents the columns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…both APIs A row-repair merge that applies a repair file written before the provenance columns existed kept the source row's values, which described the replaced request and could exceed the repair's elapsed time. The merge now empties all three on such rows. The end-to-end property now also raises (non-fatal failures on answer and explanation requests), answers the requested tool (submit_explanations for explanation repairs), and runs the Responses API (GPT-5.4, and GPT-5.5 at three outputs per request). Its oracle is the mock's call history: a group with no failed request must count every returned response exactly, and a group with a failure must equal some subset of them, counts and times together. Shrinking is off for it, since each example runs a whole evaluation. Deterministic tests pin a replayed and a live explanation repair and a Responses API replay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
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.
Follow-up to the latency replay audit (#228). That audit found 328 households in the frozen US run that are full disk-cache replays and 33 that are partial replays, and that their rows record the cache lookup as
elapsed_seconds. The spend ledger already recordscache_hitper call, but prediction rows did not, so analysis could not tell a replay from a call. Now every prediction row records where its responses came from.The fields
live_callscache_hit_callslive_elapsed_secondselapsed_secondsThe counts cover the request group whose time the row's
elapsed_secondsapportions. Forrun_no_tools_evalthat is the whole household: every chunk, repair round, budget escalation and explanation repair. For single-output runs and row repairs it is the one output, and for batch rows the one request. Every row of a group repeats the counts.live_elapsed_secondsis divided among the rows likeelapsed_seconds. All three come from_response_cache_hit, the flag the spend ledger already used. LiteLLM sets_hidden_params["cache_hit"] = Trueon every disk-cache replay (litellm/caching/caching_handler.py, read for this PR).Every writer
eval_no_tools):_request_predictions_onceand_request_explanations_oncerecord each answered request._aggregate_request_resultsand the chunked branch ofrun_single_no_toolssum them. Both row builders write them: household rows divide the live time bybatch_size, and single-output rows write it unchanged. Their exception rows record zero calls. Unknown in means unknown out: a result without provenance makes the aggregate empty rather than undercounted.batch_eval.rows_from_unit):cache_hit_callsis 0, because batch requests never read the disk cache.live_callsis 1 when a response answered the row, and the live time is empty, likeelapsed_seconds._append_rowsused to append under whatever header the file had. It now widens an older header first and keeps every existing cell's text, so a repair round resumed across this change cannot shift cells. It also replaces a headerless empty file, which it previously appended to without a header. A merge that applies an older repair, written without the columns, empties them on the row it replaces: the source's values described the replaced request and could otherwise exceed the repair's time.retry_eval) and the merges (chunked_eval,fold_board,supervisor.combine) concatenate frames, so the columns pass through and older rows stay empty. Tests cover the retry and row-repair merges._RESPONSE_COLUMNS). Their exact text is stashed for a byte-identical export, and typed copies go inresponses.usage_jsonfor SQL queries. A CSV written before the columns existed exports without them, byte for byte._scenario_completestill accepts a scenario file that predates the columns, so a supervised run resumed across this change does not refuse its finished scenarios. A file with only some of the three columns is rejected.call_provenance.household_call_provenanceclassifies each household aslive,partial_replay,full_replay,no_responseorunknown, with its summed live time. It is a new module, soanalysis.py(whose hash the paper prints as the scoring code) is untouched.Nothing published changes
latencySeconds,model_cost_latencyandusage_summary_by_modelare unchanged, and nothing published reads the new fields. A property test checks that adding the columns leaves both functions' output identical. Whether replays leave the median is decision d1542, which is still open, so it is not implemented here. The frozen board predates the fields, so all of its households classify asunknown, and a test reads the snapshot header to confirm it.Invariants
These hold for every row a writer emits.
tests/test_call_provenance.pyexecutes each one:live_callsandcache_hit_callsare non-negative integers. Their sum is every response behind the row's time, live or replayed.0 <= live_elapsed_seconds <= elapsed_seconds, and the two are empty together.live_calls == 0andlive_elapsed_seconds == 0exactly.live_elapsed_seconds == elapsed_secondsexactly. Both sums run over the same sequence, and float addition and division are monotone, so 2 to 4 hold exactly, not within a tolerance.model_cost_latencyandusage_summary_by_modelidentical.Intended non-invariant: the counts are not additive across rows (each row of a group repeats them).
live_elapsed_secondsis additive.How the end-to-end property runs
test_rows_record_each_responses_provenance_and_agree_with_the_ledgerdrivesrun_no_tools_evalandrun_no_tools_single_output_evalthrough the real request path:_request_predictions_once, escalation, repair rounds, explanation repair, retries, partial rounds, chunking, and failed chunks and households. Onlylitellm.completion,litellm.responsesand the clock are mocked. The models cover chat completions with one output per request (Claude Sonnet 4.6) and unchunked (Claude Opus 4.6), and the Responses API unchunked (GPT-5.4) and three outputs per request (GPT-5.5). Hypothesis draws each response's cache flag, duration and shape: an answer to the requested tool (submit_outputsorsubmit_explanations), values only, nothing, a length cut, or a failure. Over 100 examples it produced partial replays (53%), full replays (35%), live households (21%) and households with no response (2%). A request failed in 43% of examples, 43% used the Responses API, and 13% answered an explanation repair. Each example checks invariants 1 to 5 and 7 on the rows, the written CSV and a run-store round trip.test_responses_api_requests_record_replays_and_live_callspins a Responses API replay followed by a live repair.Mutation check
Twenty-two mutants, applied one at a time, each break one rule: a replay keeping its time or counting as live, the aggregate counting only one response or ignoring an unknown one, the chunk path dropping provenance, a failed chunk turning provenance unknown, live time not being split, explanation repairs losing provenance or hiding a replay, answer replays going unseen, a failed household turning unknown, batch rows counting a hit, repair rows dropping counts,
_append_rowsignoring the header, the legacy-repair merge keeping the source's values, the run store skipping the typed copies, the supervisor requiring the columns or accepting some of them, and the classifier misreading mixes and unknowns. All 22 are killed (~/reviews/policybench-row-cache-hit-calls/mutants-r2.log). Two first survived and led to tests: the supervisor requiring the columns (test_scenario_written_before_call_provenance_is_still_complete) and explanation replays going unseen (test_an_explanation_repair_records_its_own_provenance, which kills it on its own).Review
Round 1 (GPT-6.1 Sol at ce67c35) asked for changes: an older repair file kept the source row's provenance (blocking, fixed above); invariant 5 was overstated and failures were untested; successful explanation repairs and the Responses API were uncovered. All three are addressed at 182decf.
Tests
PYTHONHASHSEED=0 pytest -m "not slow" -n 4at 182decf: 5169 passed, 58 skipped. Without the pinned seed, xdist workers collecttests/test_reference_audit_pins.pyin different orders, because it parametrizes over frozensets; that predates this PR, CI runs without-n, and a separate task fixes it.ruff format --checkandruff checkare clean on the changed files.axiom: n/a: benchmark harness instrumentation, no policy change.
🤖 Generated with Claude Code