Usage
Callpatch_anthropic_client() once after creating your Anthropic client. All subsequent client.messages.create() and client.messages.stream() calls are traced automatically. No changes to individual API calls are needed.
Works with both anthropic.Anthropic and anthropic.AsyncAnthropic - pass whichever client you use, sync or async, streaming or not.
What gets traced
By default, eachmessages.create() or messages.stream() call produces its own trace.
The raw Anthropic SDK has no built-in concept of “tool call” or “retrieval”; those only exist as plain Python code around your
messages.create() calls, so the patch can’t see them on its own. Use tracer.trace_tool_call() and tracer.trace_retrieval() (below) to record them manually so they show up in the trace’s performance summary.
Tool use
Anthropic tool use is a manual loop: you callmessages.create(), execute whatever tool the model requested yourself, then call messages.create() again with the result. The tool execution happens in plain Python between the two API calls, so wrap it in tracer.trace_tool_call() to record it:
tracer.trace_tool_call()
t.output before the block exits.
For cases where you already have the timing and result computed, use
tracer.record_tool_call() directly instead of the context manager:
Retrieval-augmented generation (RAG)
Like tool calls, a hand-rolled retrieval step (vector search, keyword lookup, etc.) run beforemessages.create() is invisible to the patch. Record it with tracer.trace_retrieval():
tracer.trace_retrieval()
r.doc_count and r.output inside the block.
The context manager attaches the retrieval to whichever trace is sent next: either the current active span (see below) or the next standalone
messages.create() call.
A retrieval or tool call recorded with no active span is queued and merged
into the very next trace this tracer sends, so call
trace_retrieval() /
trace_tool_call() immediately before the messages.create() call it
belongs to.Multi-call agentic loops
Tool-use loops and multi-turn agents callmessages.create() more than once. By default each call is its own independent trace. Wrap the whole loop in with tracer.trace(...) to collapse it into one trace instead: every messages.create() call made while that span is active is folded in as an "LLM Call N" step (in call order, interleaved with any trace_tool_call() steps):
patch_anthropic_client() checks tracer.current_span on every call: if a span is active on the current thread, the call is attached to it; otherwise it sends its own trace as usual. No other code changes are needed. The same patched client works standalone or inside a span.
Without the
with tracer.trace(...) wrapper, a tool-use loop like the one
above still produces a working trace per call, but the tool call recorded
via trace_tool_call() attaches to whichever trace is sent next, which is
the following turn, not the turn that requested the tool. Wrapping the loop
in a span keeps everything on one trace, in the right order.patch_anthropic_client() reference
Calling
patch_anthropic_client() on an already-patched client is a no-op; it is safe to call multiple times.

