Skip to content

Record & replay

CassetteModel records a real model's responses to a JSON cassette once, then replays them offline — so tests exercise true model behaviour with no API keys, no cost, and no flakiness. It wraps any model exactly like the resilience wrappers do.

Record once

Set a real key, run with mode="record" and an inner model. Every completion and stream is appended to the cassette file (provider keys are stripped before writing — they never land in a cassette).

from yaab import Agent
from yaab.testing import CassetteModel

# Record once (with a real key in the environment):
model = CassetteModel("tests/cassettes/run.json", inner=real_model, mode="record")
agent = Agent("a", model=model)

Replay forever after

In CI, construct the model with no inner and mode="replay". Requests are matched by a hash of the normalized request; an unmatched request raises CassetteMiss rather than silently calling a provider.

from yaab.testing import use_cassette

# CI: no inner — replays the committed cassette deterministically.
with use_cassette("tests/cassettes/run.json") as model:
    agent = Agent("a", model=model)

use_cassette picks the mode from the environment: with YAAB_RECORD=1 and an inner it records; otherwise it replays. So a developer records once locally and CI replays the committed file.

Modes

mode request on file request not on file
record overwrite (call inner) call inner, append
once (default) replay call inner, append
replay replay raise CassetteMiss

Repeated identical requests (a multi-step tool loop) replay in recorded order. Streaming is recorded and replayed chunk-for-chunk.