OpenAI
Auto-instrument the OpenAI SDK. Every chat.completions.create and responses.create call inside an active run emits an llm_call event with model, token usage, cached and reasoning tokens, latency, finish reason, and tool calls. Cost is computed server-side from the rate card.
Install / enable
Python:
pip install "agentping-io[openai]"
import agentping
from openai import OpenAI
agentping.init()
agentping.instrument_openai()
client = OpenAI()
with agentping.run("support-triage", customer_id="acme-corp") as r:
reply = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "classify this ticket"}],
)
r.finish(scores={"confidence": 0.88})
instrument_openai() patches the Completions and Responses resource classes for both openai.OpenAI and openai.AsyncOpenAI. Idempotent and global; call it once at module load. Azure OpenAI (AzureOpenAI) goes through the same classes; the event's provider is openai and model carries whatever the response reports. Supported openai versions: 1.30 up to, but not including, 3.0.
TypeScript:
npm install @agentping/sdk openai
import OpenAI from "openai";
import * as agentping from "@agentping/sdk";
agentping.init({ apiKey: process.env.AGENTPING_API_KEY });
const run = agentping.run("answer-bot");
const openai = agentping.instrumentOpenAI(new OpenAI(), { run });
const reply = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "hi" }],
});
await run.finish({ status: "success" });
instrumentOpenAI returns a wrapped client whose return types match the original; reply.choices[0].message.content works as before. Pass { run, mode: "batch" } to price calls made through the Batch API at the batch rate.
What's captured
| Field | Chat Completions | Responses API |
|---|---|---|
provider |
openai |
openai |
model |
response.model |
response.model |
input_tokens |
usage.prompt_tokens (already gross) |
usage.input_tokens |
output_tokens |
usage.completion_tokens |
usage.output_tokens |
cached_input_tokens |
prompt_tokens_details.cached_tokens |
input_tokens_details.cached_tokens |
reasoning_tokens |
completion_tokens_details.reasoning_tokens |
output_tokens_details.reasoning_tokens |
finish_reason |
choices[0].finish_reason |
status, or incomplete_details.reason when the response is incomplete |
tool_calls |
Number of message.tool_calls |
Number of function_call items in output |
latency_ms |
Wall-clock time of the call | Wall-clock time of the call |
stream |
true on streamed calls |
true on streamed calls |
OpenAI has no cache-creation concept, so cache_creation_input_tokens is never set. Zero-valued fields are omitted.
A call that raises is recorded as an llm_call with status: "error", error and exception, then re-raised. Function-call exchanges stay one llm_call per round trip; the tool executions themselves come from the agent framework or your own tool_call events.
Streaming
Both SDKs capture streamed calls on both APIs. The llm_call fires once the stream is fully consumed or closed, with stream: true.
Chat Completions only sends usage on the final chunk when you ask for it. Without stream_options, the streamed event has the model, latency, finish reason and tool-call count but no token counts:
with agentping.run("answer-bot"):
stream = client.chat.completions.create(
model="gpt-4o-mini",
stream=True,
stream_options={"include_usage": True},
messages=[{"role": "user", "content": "explain briefly"}],
)
for chunk in stream:
...
# llm_call is emitted here, once the stream is exhausted
Responses API streams carry usage on the response.completed event, so no extra option is needed.
Prompt capture and time to first token
Streamed calls on both APIs also report ttft_ms, the time from the request to the first delta carrying text or a tool call. Pulse charts its p95 per agent.
Prompt and completion text are off by default. Turn them on once at init, with an optional cap per piece (default 4000 characters):
agentping.init(capture_llm_content=True, llm_content_max_chars=4000)
agentping.init({ captureLlmContent: true, llmContentMaxChars: 4000 });
Chat Completions calls then carry input (the messages list, system message included, as the API takes it) and output (the first choice's content). Responses calls carry input (the input parameter, a string or items), system_instructions (the instructions parameter) and output (output_text). Images and tool parts appear as markers such as [tool_call lookup], and each piece is clipped at the cap. The run page renders them as messages; see events for the wire shape. In Go, pass instrumentation.WithContentCapture(4000) to the transport instead.
Source / notes
- Python:
agentping.instrument_openai()in agent-ping-python - TypeScript:
instrumentOpenAIin agent-ping-typescript
Using the OpenAI Agents SDK? Model calls are captured by the same wrapper; see the OpenAI Agents SDK page for handoffs and tool calls. Unknown models still land with their token counts and appear in the unpriced-models list until you add a rate under Spend, Rate cards. See Spend.