VCR / time-travel replay for Claude Code and Codex — deterministic offline agent testing
Project description
LLMReplay
VCR / time-travel for coding agents. Record Claude Code or Codex once, then replay offline — stop, tweak, fork, and assert — without burning tokens or waiting on nondeterministic model runs.
Status: Early alpha. See docs/alpha-limitations.md.
Get started in 30 seconds
pip install coding-agent-vcr
export LLMREPLAY_HMAC_KEY=dev-local-hmac
llmreplay doctor
Note: PyPI package name is
coding-agent-vcr(other names were taken). The CLI and import remainllmreplay.
Record, replay, diagnose
# 1. Record an agent turn (proxy + child in one process):
llmreplay run --mode record --cassette .llmreplay/demo \
--upstream http://127.0.0.1:3456 -- claude --print
# 2. Replay offline — deterministic, no tokens burned:
llmreplay run --mode replay --cassette .llmreplay/demo -- claude --print
# 3. On a miss — see exactly what changed:
llmreplay why --cassette .llmreplay/demo --request .llmreplay/demo/requests/<tx-id>.json
# 4. Check cassette health for CI:
llmreplay replay --check --cassette .llmreplay/demo
Two-terminal workflow (advanced)
# Terminal A — start proxy
llmreplay record --cassette .llmreplay/demo --upstream http://127.0.0.1:3456
# Terminal B — point agent at proxy
export ANTHROPIC_BASE_URL=http://127.0.0.1:7432
export ANTHROPIC_API_KEY=unused-local
# run one agent turn, then Ctrl-C the proxy
llmreplay replay --cassette .llmreplay/demo --profile ci
Why this exists
Coding agents fail in ways unit tests miss:
| Problem | Without LLMReplay | With LLMReplay |
|---|---|---|
| Flaky tool order across runs | Re-run and hope | Sorted canonically, deterministic match |
| Prompt regressions | Unnoticed until prod | Golden cassettes catch diffs in CI |
| CI needs live API keys + tokens | Expensive, slow, brittle | Fully offline replay from fixtures |
| Can't reproduce that weird agent bug | "Works on my machine" | Fork cassette at turn N, tweak, replay |
Observability shows what happened. LLMReplay decides what must match and re-executes the trajectory.
Core ideas
record replay
Agent ──→ LLMReplay Proxy ──→ LLM Agent ──→ LLMReplay Proxy ──→ Cassette
│ │
▼ ▼
Cassette (scrubbed) Match by SHA-256 key
Every request/response field is classified:
| Field class | Meaning | Example |
|---|---|---|
| static | Must match — drives agent behavior | model, messages, tools |
| ignore | Noise — stripped before hashing | timestamp, request_id |
| scrub | Secrets replaced with HMAC placeholders | API keys, tokens |
| live | Always hit the real tool/LLM | mark-live Bash, mark-live __llm__ |
Normative rules: SPEC.md. Architecture: DESIGN.md.
Integrations
| Agent | Quick start |
|---|---|
| Claude Code | llmreplay run --mode record -- claude --print "hi" |
| Codex | llmreplay run --mode record -- codex --prompt "hi" |
| pytest | @pytest.mark.llmreplay(cassette="...") + llmreplay_cassette fixture |
| GitHub Actions | Copy examples/github-actions/llmreplay-replay.yml |
| Any agent | Set ANTHROPIC_BASE_URL or OPENAI_BASE_URL to the proxy |
Use as a library
from llmreplay import ReplayTransport
import httpx
transport = ReplayTransport(cassette_dir=Path(".llmreplay/cassette"))
async with httpx.AsyncClient(transport=transport, base_url="http://llmreplay") as client:
resp = await client.post("/v1/messages", json={...})
Full API: docs/reference/library.md.
Hermetic smoke test
git clone https://github.com/dmallya93/llmreplay.git && cd llmreplay
pip install -e ".[dev]"
export LLMREPLAY_HMAC_KEY=dev-local-hmac
./scripts/smoke.sh
# smoke ok: record→replay (fake upstream)
No Ollama, no paid APIs, no network — pure in-process replay.
Documentation
| Category | Links |
|---|---|
| Getting started | Quickstart / Alpha limitations |
| Reference | CLI / Library API / SPEC |
| Integrations | Claude Code / Codex / pytest |
| Operations | CI / Portable cassettes / Troubleshooting |
| Security | Threat model / SECURITY.md |
| Optional | Free test-stack (CCR+Ollama, $0 local LLM) |
| Contributing | CONTRIBUTING.md / Publishing |
Contributing
See CONTRIBUTING.md and docs/ci.md for the CI-required validation commands (pytest, coverage gate, repro stress, smoke).
License
Apache-2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file coding_agent_vcr-0.2.0.tar.gz.
File metadata
- Download URL: coding_agent_vcr-0.2.0.tar.gz
- Upload date:
- Size: 75.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd79a722bf71fa3615d77205f5c95411410d3b0dc7cb58133958eaf35213c520
|
|
| MD5 |
5f20e32512ec77ebff3cccb679f4d741
|
|
| BLAKE2b-256 |
3c4fb115c51308ed0499e2a68fd51d11508bd03d6d992ab7811922c47a86f861
|
Provenance
The following attestation bundles were made for coding_agent_vcr-0.2.0.tar.gz:
Publisher:
publish.yml on dmallya93/llmreplay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
coding_agent_vcr-0.2.0.tar.gz -
Subject digest:
cd79a722bf71fa3615d77205f5c95411410d3b0dc7cb58133958eaf35213c520 - Sigstore transparency entry: 2294364618
- Sigstore integration time:
-
Permalink:
dmallya93/llmreplay@3cd0e245d47a7f2ea37ea6a98e239bb5c4c6259c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/dmallya93
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3cd0e245d47a7f2ea37ea6a98e239bb5c4c6259c -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file coding_agent_vcr-0.2.0-py3-none-any.whl.
File metadata
- Download URL: coding_agent_vcr-0.2.0-py3-none-any.whl
- Upload date:
- Size: 78.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bea46910dc0a090c43020efae8c01c6a2310106c7d5de12fafb1d12ecd9f4c12
|
|
| MD5 |
39cfd673914a74fc3f072f96690e5266
|
|
| BLAKE2b-256 |
c8b5e816ae9c63b4fead10a723b1d6050160bdabf9bd370838cfd763c096cc45
|
Provenance
The following attestation bundles were made for coding_agent_vcr-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on dmallya93/llmreplay
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
coding_agent_vcr-0.2.0-py3-none-any.whl -
Subject digest:
bea46910dc0a090c43020efae8c01c6a2310106c7d5de12fafb1d12ecd9f4c12 - Sigstore transparency entry: 2294364775
- Sigstore integration time:
-
Permalink:
dmallya93/llmreplay@3cd0e245d47a7f2ea37ea6a98e239bb5c4c6259c -
Branch / Tag:
refs/heads/main - Owner: https://github.com/dmallya93
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3cd0e245d47a7f2ea37ea6a98e239bb5c4c6259c -
Trigger Event:
workflow_dispatch
-
Statement type: