Available in the TypeScript, Python, and Ruby SDKs. The Go SDK does not support replay yet, so replay mocking does not apply there.
When to use it
Use replay mocking when a child span is not the thing you are trying to improve:- An LLM call is expensive, slow, or rate-limited.
- An external API is flaky or needs credentials you do not have locally.
- A database read depends on production-only rows.
- A retrieval or preprocessing step should stay fixed while you iterate on later logic.
How it works
Every replay has a mock strategy. If you omit the option, Bitfab uses"marked".
The root function always runs real code. Mocking only applies to descendant spans.
Mark child spans
Replay with marked mocks
fetch-article-from-db returns the output recorded in each historical trace. summarize-article runs your current code, so the experiment tests the code you are changing.
What Bitfab matches
Bitfab matches mocked child calls against the recorded span tree for each source trace. Repeated calls to the same span are matched by call order, so the first call gets the first historical output, the second call gets the second, and so on. If no historical span matches a child call, Bitfab falls through to the real function. It never silently omits the call.Inject custom values (overrides)
Mock overrides are available in the TypeScript, Python, and Ruby SDKs.
match + value pair. match selects which spans it applies to (by trace_function_key, type, or any structural field); value is the substitution — a flat value used as-is, or a function that produces one. Full replacement: value becomes the span’s output.
value is a function it receives the span’s live replay inputs, plus get_original_output to read the recorded output when you want to tweak it rather than replace it wholesale. In the Python and Ruby SDKs the context also carries the live keyword args (ctx.kwargs / ctx[:kwargs]) alongside the positional inputs:
value (one that never calls get_original_output) fetches no recorded output at all. Pass a single override or an array (first matcher wins). To apply overrides to every replay on a client, register them:
mockOverride wins, then a registered override, then the base mock strategy. A span no override matches falls back to that strategy, so you can combine overrides with "marked" or "all".
In the TypeScript SDK,
getOriginalOutput() fetches asynchronously, so a synchronous wrapped function cannot be mocked with a value that must be fetched (a recorded-output reuse, or a value function that awaits getOriginalOutput). Make the span async, or use mock: "all". A flat value (or a synchronous function) works on synchronous spans. The Python and Ruby SDKs read the recorded output synchronously, so this restriction does not apply there.Common patterns
Keep expensive model calls fixed
Mark a paid classification or extraction span withmockOnReplay / mock_on_replay, then iterate on the decision logic that consumes its output.