Available for: Python and TypeScript.
ZespanCallbackHandler integrates with LangChain’s callback system to automatically capture every LLM call, tool invocation, chain step, and retrieval operation as linked spans in Zespan. You pass it once and it instruments the entire execution graph — no changes to your chain or agent logic are needed.
How it works
LangChain calls lifecycle methods on every registeredBaseCallbackHandler at each stage of execution. The Zespan handler subscribes to:
All events share the same
trace_id so you see the entire chain as a single flame graph in the Traces view.
TypeScript
Installation
Basic usage
Chains
Pass the handler toRunnableSequence or any chain using the config argument:
Agents and tools
The handler captures the full agent loop: plan → tool call → observation → next plan. Each iteration appears as linked spans.RAG with retriever tracing
Python
Installation
Basic usage
Chains
Agents
Tool calls join the tool’s trace
A model, MCP or HTTP call made inside a tool belongs to the tool’s span and to the same trace as the rest of the run, even if you did not wrap the run inwithZespanTrace / with_zespan_trace. Before, such a call started a trace of its own. The handler makes the tool’s span the current context when the tool starts and puts the previous context back when it ends or fails; tools running in parallel each see their own span.
- TypeScript: the handler is awaited by LangChain so this happens before the tool runs. If you set
LANGCHAIN_CALLBACKS_BACKGROUND=true, LangChain runs handlers in the background and the tool’s own calls start their own traces, as before. - Python: the handler runs inline, so this also holds for async runs. A handler created with
guardrailskeeps LangChain’s default delivery, because its guardrail check is a blocking call that would stall your event loop; in async runs its tools’ own calls then start their own traces, as before. - A tool cancelled by a timeout (Python) never reports its end to LangChain. Its context is put back when the run that called it ends, or at the next top-level LangChain run (Python 3.11 or newer). Wrap each job in
with_zespan_traceto put it back as soon as the job ends.
LangGraph node steps
A LangGraph node run is recorded as anagent_step span: zespan.agent.step is the superstep, zespan.agent.route is the node name, and a node that raises also carries zespan.agent.stop_reason: error. Other chains are recorded as before. See Agent steps.
How traces look in the dashboard
A LangChain agent run produces a trace like this in the flame graph:Guardrails with LangChain
Pass guardrail options toZespanCallbackHandler to enable pre/post content checks on LLM calls made by the chain:
invoke / stream throws (raises) GuardrailBlockedError and the model is not called for a pre-check block. Catch GuardrailBlockedError where you call the chain, drop guardrails from the handler to record without blocking, or set the rule’s action to warn in the dashboard (no deploy needed). Any other problem inside the handler never reaches your code.
- Streaming: a pre-check block yields nothing. A post-check block runs after the model has answered, so the stream ends with
GuardrailBlockedErrorafter its chunks were already yielded. - Retries: retry wrappers (
withRetry/with_retry, a LangGraph retry policy) treat a block like any other error and retry it up to their attempt limit. ExcludeGuardrailBlockedErrorfrom retries if a retry cannot change the outcome. - The check itself fails (service unreachable, timeout): the call goes on, unless you set
failClosed: true/"fail_closed": Trueon the handler, in which case the check’s error stops the call.

