Vercel AI SDK

The Vercel AI SDK (ai on npm) reports usage through the onFinish callback on generateText and streamText, and per-step detail through onStepFinish. The TypeScript SDK ships a helper that builds those callbacks for you, so one AI SDK call lands as one llm_call plus a tool_call for each tool the model used. TypeScript only; there is no Python AI SDK.

Install / enable

npm install @agentping/sdk ai
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
import * as agentping from "@agentping/sdk";

agentping.init({ apiKey: process.env.AGENTPING_API_KEY });

const run = agentping.run("support-triage", { customerId: "acme-corp" });

const { text } = await generateText({
  model: openai("gpt-4o-mini"),
  prompt: "classify this ticket",
  tools: { lookupOrder },
  ...agentping.withAgentPing(run),
});

await run.finish({ status: "success" });

withAgentPing(run?, options?) returns { onFinish, onStepFinish? }, so spread it into the call. If you already have an onFinish, take just the handler with agentPingOnFinish(run?, options?) and call it from yours. Options: provider and model overrides, perStep, captureToolPayloads (default true) and toolPayloadMaxChars (default 4000). The run argument is optional; without it the handler uses the active run from runScopeAsync.

Events

AI SDK callback AgentPing event What is recorded
onFinish llm_call Provider and model from response.modelId (openai/gpt-4o-mini splits into the two), input and output tokens summed across all steps of the call (totalUsage), cached and reasoning tokens when reported, finish reason, number of tool calls, latency for the whole call.
onFinish tool_call, one per tool the model used Tool name, tool_invocation_id (the AI SDK's toolCallId), arguments and result (capped at toolPayloadMaxChars). A tool whose step reports a tool-error part lands with status: "error" and the error message instead of a result.
onStepFinish with perStep: true llm_call per model step The same fields per step, so a tool-using call shows each round trip separately, each followed by its own tool_call rows. onFinish then emits nothing extra.

By default a call that loops through several tool steps is one llm_call with the summed usage, which is what the rate card prices. Turn on perStep when you want to see the cost of each round trip.

Three gaps to know. A call that throws before onFinish (a rejected request, a rate limit) never reaches it, so there is no errored llm_call; record the failure with run.finish({ status: "error" }) or your own error event. streamText is captured through the same onFinish once the stream completes, but the llm_call carries no stream flag. And tool_call rows carry no latency, because the AI SDK runs tools itself and does not report per-tool timing.

Naming

Provider and model come from the AI SDK's response.modelId, which is what the rate card is keyed on. When you use a provider the AI SDK reports under a different name than the rate card expects (a gateway, a custom provider), pass provider and model in the options and those win.

Source / notes

The AI SDK also emits OpenTelemetry spans when you pass experimental_telemetry: { isEnabled: true }; export those to the OpenTelemetry endpoint for span-level traces, with or without the onFinish helper.