Skip to main content

📼 Rerunner

Deterministic flight recorder, replay engine, and CI test runner for AI agents.

CI Release Python Version License RFCs Code style: ruff Type Checked: mypy

Record your AI agent once. Replay forever in CI in 0ms with $0 API tokens.


⚡ The Problem

Building production AI agents requires automated testing and continuous integration (CI). But running multi-step agents in tests creates three massive bottlenecks:

  1. Flaky CI Builds: LLMs are non-deterministic. Even at temperature=0, small prompt variations break downstream assertions.
  2. Exorbitant API Costs: Re-running an 8-step agent across 50 pull requests burns thousands of dollars in LLM API credits.
  3. Debug Nightmare: When an agent fails at Step 5, you cannot reproduce the bug locally without paying to re-run Steps 1 through 4.

Rerunner solves this by providing a zero-overhead, tamper-evident cassette recorder that intercepts httpx traffic natively across the Anthropic Claude SDK, OpenAI SDK, and custom agents.


🚀 Key Features

  • $0 CI Replays: Freeze agent runs into append-only JSONL cassettes. Replay in 0ms without hitting external LLM APIs.
  • Fork from Step N: Replay Steps 1..4 from the cassette for free, and execute Step 5 live against the model with your new prompt or bug fix.
  • Tolerant Request Matching: Automatically strips volatile nonces, millisecond timestamps, request IDs, and SDK metadata headers (x-stainless-*).
  • Side-Effect Safety: The @side_effect decorator prevents destructive external actions (database writes, Slack webhooks, payments) during replays.
  • Cryptographic Integrity: SHA-256 tamper-evident hash chaining ensures recorded cassettes have not been altered.
  • Native Pytest Integration: Seamless @pytest.mark.rerunner marker and fixtures.

📊 Comparison

Capability rerunner vcrpy LangSmith LangGraph
Primary Focus Deterministic CI Replay Generic HTTP Web Observability Orchestration
LLM Streaming (SSE) Support ✅ Yes ❌ Breaks ✅ Yes N/A
Fork-from-Step Execution ✅ Yes ❌ No ❌ No ⚠️ Graph-locked
Side-Effect Stubbing ✅ Yes ❌ No ❌ No ❌ No
Zero Infrastructure / $0 Cost ✅ Local JSONL ✅ Local YAML ❌ Cloud / Paid ❌ Variable
Framework Agnostic ✅ Any httpx SDK ✅ HTTP ⚠️ Complex ❌ Locked

📦 Installation

pip install rerunner

🛠️ Quickstart

1. Recording an Agent Session

Rerunner transparently attaches to standard SDK clients via httpx.Client(transport=...):

import anthropic
import httpx
from rerunner import CassetteTransport

# 1. Record live interactions to a cassette
transport = CassetteTransport("session.jsonl", mode="record")
client = anthropic.Anthropic(http_client=httpx.Client(transport=transport))

response = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Analyze quarterly revenue report."}]
)
print(response.content[0].text)

2. Deterministic $0 Replay in CI

# 2. Replay locally or in CI without spending API tokens or waiting on network
transport = CassetteTransport("session.jsonl", mode="replay")
client = anthropic.Anthropic(http_client=httpx.Client(transport=transport))

# Instant response, zero cost, completely reproducible
response = client.messages.create(
    model="claude-3-5-sonnet-20241022",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Analyze quarterly revenue report."}]
)

3. Forking from Step 3

from rerunner import fork

# Replay steps 1 through 3 from cassette; run step 4 live with your modified prompt
with fork("session.jsonl", step=3) as session:
    client = session.get_client()
    # Steps 1..3 replayed from tape
    # Step 4 executed live against model

Compressed Cassettes

For long-running agent workflows, use the .jsonl.gz extension to store cassettes in gzip-compressed form:

from rerunner import CassetteTransport

transport = CassetteTransport("session.jsonl.gz", mode="record")

Rerunner automatically compresses .jsonl.gz cassettes when recording and decompresses them when replaying. The same CLI commands and SHA-256 integrity verification work with both .jsonl and .jsonl.gz files:

rerunner inspect session.jsonl.gz
rerunner verify session.jsonl.gz
rerunner play session.jsonl.gz

Streaming Replay

Rerunner records responses with a text/event-stream content type as individual chunks. During replay, those chunks are yielded through HTTPX's streaming interface without making another network request.

import httpx
import openai

from rerunner import CassetteTransport

transport = CassetteTransport(
    "agent_run.jsonl",
    mode="replay",
)

client = openai.OpenAI(
    api_key="not-used-during-replay",
    http_client=httpx.Client(transport=transport),
)

stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Summarize this report."}],
    stream=True,
)

for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")

AsyncCassetteTransport provides the equivalent asynchronous streaming interface for consumers using async for.

To simulate the timing of a live stream in UI tests, configure a delay between replayed chunks:

transport = CassetteTransport(
    "agent_run.jsonl",
    mode="replay",
    chunk_delay_ms=25,
)

The first chunk is returned immediately. Each subsequent chunk is delayed by the configured number of milliseconds. The default value is 0, which adds no intentional delay. Buffered responses continue to use the existing replay behavior.


🛡️ Guarding Destructive Side-Effects

Ensure your replayed agents don't charge cards or send duplicate Slack messages:

from rerunner import side_effect

@side_effect(name="post_slack_alert")
def send_slack_notification(channel: str, message: str) -> bool:
    # Runs live during recording. During replay, safely returns recorded result!
    return slack_client.chat_postMessage(channel=channel, text=message)

🧪 Pytest Integration

Mark your unit tests with @pytest.mark.rerunner:

import pytest

@pytest.mark.rerunner("tests/fixtures/agent_run.jsonl")
def test_agent_decision(rerunner_transport):
    client = openai.OpenAI(http_client=httpx.Client(transport=rerunner_transport))
    completion = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": "Evaluate risk factor"}]
    )
    assert "low risk" in completion.choices[0].message.content.lower()

Run test suite in replay mode (0 network calls, $0 cost)

pytest

Re-record all cassettes when prompts change intentionally

pytest --rerunner-record


For a reusable GitHub Actions workflow that restores recorded cassettes before
replay tests, see the [GitHub Actions cassette caching recipe](docs/recipes/github_actions_caching.md).

---

## ⚡ 1-Line Client Wrappers

Wrap your existing SDK instances with zero configuration:

```python
import anthropic
import openai
from google import genai
from rerunner.integrations import wrap_anthropic, wrap_google_genai, wrap_openai

# Wrap Anthropic Claude client
client_claude = wrap_anthropic(anthropic.Anthropic(), "agent_run.jsonl", mode="replay")

# Wrap OpenAI client
client_openai = wrap_openai(openai.OpenAI(), "agent_run.jsonl", mode="replay")

# Wrap Google GenAI / Vertex AI Gemini client
client_gemini = wrap_google_genai(genai.Client(), "agent_run.jsonl", mode="replay")

LangChain & LangGraph Integration

from rerunner.integrations import RerunnerCallbackHandler

handler = RerunnerCallbackHandler("langgraph_agent.jsonl", mode="replay")
agent_executor.invoke({"input": "Fetch stock price"}, config={"callbacks": [handler]})

🔭 OpenTelemetry (OTel) Distributed Traces

Bridge testing and enterprise observability. Export cassettes to standardized OpenTelemetry trace JSON compatible with Datadog, Jaeger, Grafana Tempo, and Dynatrace:

# Export cassette to OpenTelemetry trace
rerunner export-otel session.jsonl --output trace.otel.json --service-name "fraud-agent"

Or programmatically in Python:

from rerunner import export_cassette_to_otel_file

export_cassette_to_otel_file("session.jsonl", "trace.otel.json", service_name="fraud-agent")

🔒 Enterprise Secret & PII Sanitization

Never accidentally commit API keys or auth tokens to GitHub. Rerunner automatically scrubs:

  • Anthropic API keys (sk-ant-...) $\rightarrow$ [REDACTED_ANTHROPIC_KEY]
  • OpenAI API keys (sk-proj-..., sk-...) $\rightarrow$ [REDACTED_OPENAI_KEY]
  • Google AI Studio / Gemini keys $\rightarrow$ [REDACTED_API_KEY]
  • Authorization headers (Bearer ...) $\rightarrow$ Bearer [REDACTED_TOKEN]
  • AWS access keys (AKIA...) $\rightarrow$ [REDACTED_AWS_KEY_ID]

💻 CLI Commands

# Calculate CI cost reductions and token savings
rerunner stats session.jsonl

# Visually play back agent execution steps in terminal
rerunner play session.jsonl

# Inspect cassette details and step sequences
rerunner inspect session.jsonl

# Export cassette into OpenTelemetry OTLP trace format
rerunner export-otel session.jsonl -o session.otel.json

# Cryptographically verify tamper-evident hash chain
rerunner verify session.jsonl

# Compare prompt and response divergences between two cassettes
rerunner diff session_v1.jsonl session_v2.jsonl

🤝 Contributing & Enterprise Architecture

Contributions from the open-source community and frontier AI research teams are warmly welcomed:

  • 🗺️ Strategic Vision: Review our ROADMAP.md for planned milestones.
  • 🏛️ Enterprise RFCs: Propose or review major architectural designs in our RFC Directory or open an RFC Issue.
  • 🏷️ Good First Issues: Grab starter tasks on GitHub Issues.
  • 🛠️ Developer Setup: Read CONTRIBUTING.md for environment setup and linting rules.

📜 License

Rerunner is licensed under the Apache 2.0 License.

Metadata

Release files for rerunner 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rerunner 0.2.0
File Size Uploaded
rerunner-0.2.0.tar.gz 47.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rerunner 0.2.0
File Interpreter ABI Platform
rerunner-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 79.0 kB

Release files / rerunner-0.2.0.tar.gz

Download URL rerunner-0.2.0.tar.gz
Size 47.4 kB
Tags Source
SHA-256 checksum
How to use checksums
3c94f32b343376fbec8088332290be8006e54564000ff56275b64e42dfcdc2ba
BLAKE2b-256 checksum
How to use checksums
32733404cb5b6e81053582d7072291464cc0740032ba5d144d8b3144b065404c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / rerunner-0.2.0-py3-none-any.whl

Download URL rerunner-0.2.0-py3-none-any.whl
Size 31.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ba99a2f16d720f70d6889c1b61547a356ed4326e4de27fca28274a1e552721a8
BLAKE2b-256 checksum
How to use checksums
c8af4cae72fbe423966a4cb9dc328916ff68efc4dfdd839feff6f521307ad9d2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page