OpenAI, Anthropic and Gemini clients
If your code calls a provider's SDK directly, wrap the client once where you create it. Every call through it is recorded from then on, including calls written later.
Wrap the client
from openai import OpenAI, AsyncOpenAI
from anthropic import Anthropic
from google import genai
gpt = OpenAI(http_client=reporter.openai_http_client())
gpt_async = AsyncOpenAI(http_client=reporter.openai_http_client(asynchronous=True))
claude = Anthropic(http_client=reporter.anthropic_http_client())
gemini = genai.Client(http_options=reporter.genai_http_options())
gemini, err := genai.NewClient(ctx, &genai.ClientConfig{Backend: genai.BackendVertexAI})
reporter.InstrumentGenAI(gemini)
claude := anthropic.NewClient(option.WithMiddleware(reporter.AnthropicMiddleware()))
// Also records Perplexity and any other API that speaks OpenAI's, through option.WithBaseURL.
gpt := openai.NewClient(option.WithMiddleware(reporter.OpenAIMiddleware()))
In TypeScript, use the Vercel AI SDK middleware, or record calls by hand as shown there.
Each call is recorded under the request, user and customer set with scope, and under the function in your code that made it.
Name the part of your product
Where several functions do one job, or every call goes through one shared helper, name the part yourself:
with agentpulse.scope(component="report_generation"):
write_report()
ctx = recorder.WithComponent(ctx, "report_generation")
Things the client cannot see
- Streamed OpenAI chats report their token counts only when the request sets
stream_options={"include_usage": True}. Without it, the call is recorded with no counts. - Claude through Vertex or Bedrock is billed by Google or Amazon. In Go, set
BilledByon the attribution and register the middleware before the SDK's Vertex or Bedrock option. A streamed Bedrock answer is recorded without token counts. - Gemini's Live API does not use the client's HTTP transport, so it is not recorded.
- A client an ADK agent also uses should not be wrapped: the agent's hooks already record its calls.
A provider none of these reach
Record the call yourself, passing the provider's own usage numbers unchanged. Agent Pulse prices them on the server.
reporter.model_call(agentpulse.ModelCall(
model="gemini-2.5-pro",
reported=agentpulse.reported_from_genai(response.usage_metadata),
format=agentpulse.FORMAT_VERTEX,
duration=time.monotonic() - started,
))
reporter.ModelCall(ctx, recorder.ModelCall{
Model: "claude-sonnet-4-5",
Duration: time.Since(started),
Err: err,
Format: recorder.FormatAnthropic,
Reported: map[string]int64{"input_tokens": in, "output_tokens": out},
})
Pass a failure as the error. Only its code is recorded, never its message.