Copy this into your coding agent
Paste into Cursor, Codex, Claude Code, or another coding agent in your project.Beta release. All four languages support tracing, real app tests and scripted simulations.
Install
Package on PyPI · Source and examplesRecord one interaction
Choose Python on the SDK setup page and copy the coding-agent prompt. Bench fills in your account setup key automatically. Python 3.10 or later is required.Let the framework report the structure
If the application uses a framework that emits OpenTelemetry GenAI spans, do not wrap calls by hand. Attach the bridge and Bench receives the agents, model calls and tools as they run, metadata-only:bench.record_external_span(trace_id=..., span_id=..., parent_span_id=..., name=..., started_at=..., ended_at=..., attributes={...}); the kind is inferred from the
gen_ai.* attributes (gen_ai.operation.name first, then gen_ai.agent.name,
gen_ai.tool.name, gen_ai.request.model).
Failed spans exported through the bridge keep error.message (the span’s status
description) and exception.type / exception.message, never the stack trace.
Bench shows that message in the span’s drawer under Production. When a span
does not report bench.cost.usd, Bench estimates its cost from the recorded
tokens and labels the estimate ~.
Register the model calls
Traces never carry the editable template. After the first request, register the model calls the system makes from the repository, one entry per call, using theregister_calls operation (POST /api/ai-systems/{id}/calls) or the
bench_register_calls MCP tool, then put the returned component ids on the
matching model-call spans with component_id=. Your coding agent does this for
you when set up from Bench’s setup prompt.
Wrap your application
Usewith bench.trace(...) around a Deep Agents, LangGraph, LangChain or custom
application call. It also works around await agent.ainvoke(...). Wrap tools
inside the same request with kind="TOOL"; nested async tasks inherit their
parent trace. Keep streaming spans open until the stream finishes.
Inputs and outputs are omitted by default. With content capture enabled, pass
input=... and call span.set_output(result). Built-in filtering runs before
delivery. Add a redact(value) callback for your application’s extra fields.
See privacy and redaction.
Call bench.flush() at request boundaries, or await bench.aflush() in async
code. Call shutdown() after active requests finish. Delivery retries once;
bench.stats shows queued and dropped spans. A delivery failure does not change
your application’s result or exception.
Set component_id to a real prompt component from Bench to connect the event to
its saved criteria; it is sent as bench.component_id. Production checks
require recorded content and a key with evaluation allowance. Report
bench.cost.usd when your provider gives you the price; otherwise Bench estimates
cost from tokens and labels it ~.
Prefer the OpenTelemetry bridge above where the framework already emits GenAI
spans; use explicit wrappers for the rest.
Test your application
await bench.evaluate_system(...) calls your application’s request handler with
pinned cases. It captures the real nested tool/model traces, compares the final
output and independently observed state, and returns a redacted report locally.
Use a test database and test service credentials.
run(input, context) and observe(context) may be synchronous or asynchronous.
When a case has expected_state, supply observe to read the authoritative test
state. Context provides case_id, cancelled, signal, deadline and
raise_if_cancelled(). Inputs are JSON snapshots; changes inside the application
do not change the case’s assertions.
await bench.simulate_system(...) accepts the same revisions and cases, plus
create_session(initial_state, context). Cases use
input={"initial_state": {...}, "turns": [...]} and require expected_state.
Return an object with turn(message, context), observe() and close() methods.
A fresh session receives 1 to 20 scripted customer turns. Bench snapshots observed
state before closing the session. context.turn_index identifies the turn.
Both helpers use a 30-second timeout per case, configurable with timeout up to
300 seconds. Pass a threading.Event as cancel_event to stop the suite. Missing
assertions, missing state, unfinished traces and timeouts remain incomplete.
Callbacks must honor cancellation; Python cannot forcibly stop a synchronous
thread. Await all child work and isolate external side effects. These helpers
are local execution, not a process sandbox or a hosted verification claim.
Tests record redacted content even when production capture is metadata-only, so
use synthetic inputs. Reports are not uploaded and paid checks are not started
unless you take a separate explicit action.
Run the complete refund simulation example. It sends two refund requests, checks the number of refunds written, and demonstrates the failing implementation and its fix.

