Repository navigation
Review fixes and cassette-backed provider tests - #1
Merged
Merged
Conversation
added 6 commits
August 8, 2026 18:00
The score is an expected value, so it is now computed over an explicitly normalized distribution while the reported probabilities stay untouched when normalize_probabilities is disabled. Previously an LLM distribution summing to 0.2 produced a score of 0.1 on a 0..1 scale. A single RunUsage is threaded through every PydanticAI attempt so tokens spent on attempts that later failed are still billed. Request counts now accumulate across attempts, so malformed-structure retries are counted from a per-attempt snapshot rather than the run total. system_one and system_one_async share an _Evaluation dataclass instead of duplicating ~35 lines each.
Misses are computed outside the lock so concurrent calls do not serialize on the network, with a re-check under the lock so the first stored value wins. The file is written via a temp file and replace so an interrupted run cannot truncate it. The docstring example no longer looks like a runnable doctest.
test_provider_errors grows from 2 to 10 rows covering timeout mapping, auth on 403, and TypeSafeUnknownError status_code passthrough. Adds retry exhaustion, async retry coverage, and a test asserting tokens from failed attempts are billed. Invalid question collections are now checked. The live-API test no longer constructs TypeSafeClient at import time without a key, so a fully cached run works with no credentials.
Enables the rule families the existing noqa comments already referenced, stops pytest collecting from .venv, and renames the distribution to match the imported module.
Recording real provider traffic surfaced two prompt defects that a cached response could not. The answers field was described as "keyed by question ID", so gpt-4o-mini invented an ID and nested every answer under it, failing validation. The description now fixes the property names instead. Discrete score questions carried no criteria at all, leaving the model to guess its own scale: it returned 5 on a 0-4 score. The criteria are now listed as a score legend in discrete mode only, since probabilities mode already carries them as per-label descriptions and naming an integer there made models return a score instead of a distribution.
json_cache memoized system_one's return value, so on a cache hit none of the client ran and the test only compared a stored blob to a literal beside it. VCR replays at the HTTP layer instead, so prompt building, output parsing, normalization, confidence, and usage aggregation all execute against recorded provider traffic. It caught two real prompt bugs immediately. Expected responses stay inline in the test as readable JSON, now the single source of truth rather than being duplicated in json_cache.json. Latency is dropped before comparison instead of pinned to an unreproducible value. Cassettes are JSON, matched on request body because every call posts to the same endpoint. Request credentials are filtered and response headers reduced to an allowlist, since providers return organization IDs, request IDs, cookies, and per-account rate-limit quotas. Placeholder keys are set at conftest import because provider SDKs demand one before any HTTP happens, so replay needs no credentials. --block-network is on by default so a missing cassette fails rather than silently making a live call.
This was referenced Aug 9, 2026
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.
Fixes from a review of the repo, then a migration of the provider tests from
json_cacheto HTTP cassettes.Correctness
scoreis an expected value, so it only means anything over weights summing to 1. An LLM distribution summing to 0.2 producedscore=0.1on a 0..1 scale. It is now computed over an explicitly normalized copy while the reportedprobabilitiesstay untouched whennormalize_probabilities=False.RunUsageis now threaded through every attempt. Because request counts accumulate across attempts, malformed-structure retries are counted from a per-attempt snapshot rather than the run total.answersfield was described as "keyed by question ID", so gpt-4o-mini invented an ID and nested every answer under it. Discrete score questions carried no criteria at all, so the model guessed its own scale and returned5on a 0-4 score.Tests
json_cachememoizedsystem_one's return value, so on a cache hit none of the client ran — the test compared a stored blob against a literal in the file beside it. VCR (vcrpyviapytest-recording) replays at the HTTP layer instead, so prompt building, output parsing, normalization, confidence, and usage aggregation all execute for real. It caught both prompt bugs above on the first recording run.json_cache.json.latencyis dropped before comparison rather than pinned to a value no later run can reproduce (REGENERATE_JSON_CACHE=1previously failed by construction).content-typeallowlist, since providers return organization and workspace IDs, request IDs, cookies, and per-account rate-limit quotas.--block-networkis on by default, so a missing cassette fails instead of silently making a live call.Coverage went from 30 to 46 tests: timeout mapping,
TypeSafeUnknownErrorstatus passthrough, retry exhaustion, the async retry path, invalid question collections, and a test asserting tokens from failed attempts are billed.Also
# noqa: B008/BLE001referenced rules that were never on),testpathsset so pytest stops collecting from.venv, distribution renamed to match the imported module, real description..venvconsole scripts were shebanged to a staleOpenTypeSafe2path from the directory rename, souv run pytestfailed outright. Rebuilt.OpenTypeSafeClient.__init__skipssuper().__init__(), and the invariant that creates.Test plan
uv run pytest-> 46 passed.OPENAI_API_KEY,ANTHROPIC_API_KEY, andTYPESAFE_API_KEYunset and network blocked -> 46 passed.uv run pytest tests/test_live_apis.py --record-mode=rewritere-records against the live APIs; verified end to end.sk-/Bearerpatterns, and identifying headers -> clean.Notes for review
011551fhardensJsonCacheand24e7595then deletes it. That is the order the work was requested in; squash if you would rather not carry it.model: "speed_v10_mango_loco"rather than the requestedspeed_latestalias, and itsScoreAnswercarries alegendmapping each score level to its criterion, whichOpenTypeSafeClientnever emits._resolve_pydantic_ai_modelonly knows OpenAI and Anthropic prefixes, and_TOKEN_ERROR_MARKERSsubstring matching is fragile.