Skip to main content

Framework-agnostic record/replay for LLM API interactions (streaming and non-streaming)

Project description

agentrec

Framework-agnostic record/replay for LLM API interactions — plus a model-migration report built on the recorded corpus.

Recording happens at the httpx transport layer, below the OpenAI SDK, the Anthropic SDK, LangChain, or any other httpx-backed client. The core depends on nothing but httpx.

Status: beta (0.2). Record/replay is proven for streaming (SSE) and non-streaming (JSON) responses on OpenAI and Anthropic; the API may still change in minor releases before 1.0. Migration translation covers OpenAI ↔ Anthropic, text-only conversations — tools/images become clearly-reasoned skipped rows.

Install

pip install agentrec                 # core is httpx-only
pip install "agentrec[compression]"  # + brotli/zstd cassette decoding

Quick start

Build one agentrec.async_client(), hand it to your SDK, and wrap calls in a cassette: mode="auto" replays a request if it's been recorded, otherwise records it.

import agentrec
from openai import AsyncOpenAI

store = agentrec.FileStore("corpus")
http = agentrec.async_client()          # honours the active cassette scope
oai = AsyncOpenAI(http_client=http)

@agentrec.cassette(store, mode="auto")  # recorded once, replayed thereafter
async def ask(prompt: str) -> str:
    response = await oai.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
    )
    return response.choices[0].message.content

Streaming works identically — the raw SSE bytes are recorded and the SDK parser re-runs on replay. cassette also works as an async context manager, and the same client plugs into the Anthropic SDK unchanged: AsyncAnthropic(http_client=http).

Synchronous SDKs use the same seam: build agentrec.sync_client(), hand it to OpenAI(http_client=...) / Anthropic(http_client=...), and use cassette as a plain with block or decorator on a sync function.

Prefer wiring httpx yourself? Use the transports directly:

from agentrec import RecordingTransport, ReplayTransport

httpx.AsyncClient(transport=RecordingTransport(httpx.AsyncHTTPTransport(), store))
httpx.AsyncClient(transport=ReplayTransport(store))   # offline; cannot touch the network

Model-migration report

Every recording carries provenance: provider, model, and a semantic_key — a hash of the provider-neutral conversation (system + messages), so the same prompt recorded against different models, different providers, or different sampling parameters groups together. The migration runner re-asks every corpus prompt of a target model (cross-provider translation included: an OpenAI-recorded prompt can be re-asked of Claude), caches the answers back into the corpus, and scores baseline vs. target:

Comparator Needs network? What it measures
exact no normalized string equality (classification-style)
fuzzy no difflib sequence similarity
embedding OpenAI API cosine similarity of embeddings
judge LLM API an LLM scores semantic equivalence
agentrec migrate  --corpus corpus --target claude-haiku-4-5 --compare exact,fuzzy,judge
agentrec report   --corpus corpus --target claude-haiku-4-5 --strict   # offline re-render; CI gate
agentrec annotate --corpus corpus                                      # backfill summaries/metadata

Re-runs are cheap: answered prompts are served from disk, rate-limited calls retry with backoff, and failures are never cached. Rows are scored concurrently (--concurrency). Recordings tagged with a category — cassette(store, metadata={"category": "extract"}) — get a per-category breakdown in the report, with output-token columns that surface verbosity/cost differences between the models.

How it works

agentrec/
  capture.py      # storage-agnostic captured request/response data
  keying.py       # request fingerprint → provider / model / semantic_key / cassette id
  store.py        # InMemoryStore + FileStore (human-readable JSON cassettes)
  transport.py    # RecordingTransport / ReplayTransport / AutoTransport
  session.py      # async_client() + cassette — the ergonomic seam
  providers/      # OpenAI + Anthropic request/response dialects
  comparators.py  # exact / fuzzy / embedding / judge response scoring
  migration.py    # run_migration() — replay the corpus against a candidate model
  report.py       # Markdown / HTML / console rendering
  cli.py          # agentrec migrate | report | annotate
  • Tee, don't buffer: the caller and the store see every chunk in order, live — the recorder never holds back the stream.
  • Raw bytes, no parsing: cassettes store the original byte frames; the SDK parser re-runs on replay, so one codebase covers every provider.
  • Replay mode can't leak: ReplayTransport (mode="replay") has no inner transport, so it cannot accidentally hit the network — use it when you need a hard offline guarantee (CI). Note that mode="auto" does make a live call (and records it) whenever a request has no recording yet — e.g. after a prompt edit changes the fingerprint.
  • Failures aren't cached: non-2xx responses are never recorded by default, so a transient 429/500 can't be replayed forever as the answer (record_errors=True opts in deliberately).
  • Request-derived keys: interactions are keyed by a fingerprint (method + path + model + normalised body), so identical calls replay deterministically. The semantic_key that groups prompts for the migration report is derived from the provider-neutral conversation instead — same prompt against OpenAI or Anthropic, at any temperature, groups together.
  • Best-effort secret hygiene: FileStore always redacts auth headers, and scrubs known secret shapes from request bodies and summaries before anything touches disk. This is a safety net, not a guarantee — response bodies are stored verbatim unless you opt in via scrub_response_body=True, and unknown secret formats pass through. Review cassettes before sharing a corpus; extend secret_patterns=[...] with your organisation's shapes.

Any SDK that accepts an httpx client works. Non-httpx SDKs (boto3/Bedrock, some Vertex paths) never route through the transport, so they need a different seam.

Tests

pytest -q

The suite is offline by default: canned SSE/JSON fixtures, with accidental network access failing the test. Live record→replay tests run only when OPENAI_API_KEY / ANTHROPIC_API_KEY are present (read from a project-root .env) and skip cleanly otherwise.

Attributions

See NOTICE for third-party acknowledgements, including inspiration from baml_vcr for the streaming chunk capture/replay pattern.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agentrec-0.3.0.tar.gz (63.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agentrec-0.3.0-py3-none-any.whl (51.4 kB view details)

Uploaded Python 3

File details

Details for the file agentrec-0.3.0.tar.gz.

File metadata

  • Download URL: agentrec-0.3.0.tar.gz
  • Upload date:
  • Size: 63.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for agentrec-0.3.0.tar.gz
Algorithm Hash digest
SHA256 138f18807cec2c3494e67a46b239c10cb8a362db9b54276aa2ddf3990bf5e44e
MD5 c480fad5395b927d89c739e4b34c2223
BLAKE2b-256 538786c39652b6cb8133eb15bd4848bd0e33c3109b45ace69764c72d84c9fdc6

See more details on using hashes here.

File details

Details for the file agentrec-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: agentrec-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 51.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for agentrec-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0c4308657609910ac3d2c1c3d9f0a5c3f6d369e8e377529352db7df186274081
MD5 d7d577b59dc600d69bae8efa71b3b8a3
BLAKE2b-256 dd2fcf1606ca8f272d88cd1c0a9520d219f2d249fd110baa064bd9d668f60356

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page