mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
tests(vcr): add LITELLM_VCR_VERBOSE per-test hit/miss reporting
Set LITELLM_VCR_VERBOSE=1 to print a one-line cassette verdict per test (HIT / MISS / PARTIAL / NOOP) showing replay vs new-recording counts. Useful for local QA to confirm which tests actually exercised the cache and which fell through to the live provider.
This commit is contained in:
parent
a7e8189b17
commit
cf4c9ede61
3 changed files with 79 additions and 0 deletions
|
|
@ -146,3 +146,42 @@ def patch_vcrpy_aiohttp_record_path() -> None:
|
|||
|
||||
_aiohttp_stubs.record_response = _record_response_preserving_body
|
||||
_PATCHED_AIOHTTP_RECORD = True
|
||||
|
||||
|
||||
VCR_VERBOSE_ENV = "LITELLM_VCR_VERBOSE"
|
||||
|
||||
|
||||
def vcr_verbose_enabled() -> bool:
|
||||
return os.environ.get(VCR_VERBOSE_ENV) == "1"
|
||||
|
||||
|
||||
def format_vcr_verdict(cassette: Any) -> str:
|
||||
"""Build a one-line hit/miss verdict for a vcrpy Cassette.
|
||||
|
||||
HIT — at least one request was served from cache and nothing new was
|
||||
recorded. (Pure replay.)
|
||||
MISS — nothing from cache; one or more requests went live and were
|
||||
recorded. (Cold cache.)
|
||||
PARTIAL — mix of replay and new recordings. Usually means the cassette
|
||||
matches some but not all requests for this test (e.g. retries,
|
||||
new branches, or vcrpy match_on too strict).
|
||||
NOOP — test made no HTTP calls (or VCR not engaged for it).
|
||||
"""
|
||||
if cassette is None:
|
||||
return "[VCR NOOP]"
|
||||
played = getattr(cassette, "play_count", 0) or 0
|
||||
# cassette.data is the recorded request/response list; len(cassette) counts
|
||||
# recorded episodes. New recordings during this test = len - prior_len, but
|
||||
# we don't have prior_len here, so we use cassette.dirty (set when an append
|
||||
# happened during this run) as the "new recording" signal.
|
||||
dirty = getattr(cassette, "dirty", False)
|
||||
total = len(cassette) if hasattr(cassette, "__len__") else 0
|
||||
if played == 0 and not dirty:
|
||||
return "[VCR NOOP] (no http traffic)"
|
||||
if played > 0 and not dirty:
|
||||
return f"[VCR HIT] {played} replayed, 0 new ({total} cassette entries)"
|
||||
if played == 0 and dirty:
|
||||
return f"[VCR MISS] 0 replayed, recorded new ({total} cassette entries)"
|
||||
return (
|
||||
f"[VCR PARTIAL] {played} replayed + new recordings ({total} cassette entries)"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ import litellm # noqa: E402
|
|||
|
||||
from tests._vcr_redis_persister import ( # noqa: E402
|
||||
filter_non_2xx_response,
|
||||
format_vcr_verdict,
|
||||
make_redis_persister,
|
||||
patch_vcrpy_aiohttp_record_path,
|
||||
vcr_verbose_enabled,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -102,6 +104,24 @@ def pytest_recording_configure(config, vcr):
|
|||
patch_vcrpy_aiohttp_record_path()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _vcr_hit_miss_report(request, vcr):
|
||||
"""When LITELLM_VCR_VERBOSE=1, print a one-line cassette verdict per test.
|
||||
|
||||
Runs after the `vcr` fixture (which yields the active Cassette), so we can
|
||||
inspect play_count / dirty / len in teardown."""
|
||||
yield
|
||||
if not vcr_verbose_enabled():
|
||||
return
|
||||
verdict = format_vcr_verdict(vcr)
|
||||
reporter = request.config.pluginmanager.get_plugin("terminalreporter")
|
||||
line = f"{verdict} :: {request.node.nodeid}"
|
||||
if reporter is not None:
|
||||
reporter.write_line(line)
|
||||
else: # pragma: no cover - reporter is always present in normal runs
|
||||
print(line)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def event_loop():
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -20,8 +20,10 @@ import litellm # noqa: E402
|
|||
|
||||
from tests._vcr_redis_persister import ( # noqa: E402
|
||||
filter_non_2xx_response,
|
||||
format_vcr_verdict,
|
||||
make_redis_persister,
|
||||
patch_vcrpy_aiohttp_record_path,
|
||||
vcr_verbose_enabled,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -126,6 +128,24 @@ def pytest_recording_configure(config, vcr):
|
|||
patch_vcrpy_aiohttp_record_path()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _vcr_hit_miss_report(request, vcr):
|
||||
"""When LITELLM_VCR_VERBOSE=1, print a one-line cassette verdict per test.
|
||||
|
||||
Runs after the `vcr` fixture (which yields the active Cassette), so we can
|
||||
inspect play_count / dirty / len in teardown."""
|
||||
yield
|
||||
if not vcr_verbose_enabled():
|
||||
return
|
||||
verdict = format_vcr_verdict(vcr)
|
||||
reporter = request.config.pluginmanager.get_plugin("terminalreporter")
|
||||
line = f"{verdict} :: {request.node.nodeid}"
|
||||
if reporter is not None:
|
||||
reporter.write_line(line)
|
||||
else: # pragma: no cover - reporter is always present in normal runs
|
||||
print(line)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Capture TRUE defaults at conftest import time (before test modules pollute).
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue