Skip to content

Vercel AI SDK

Pisama ingests AI SDK telemetry over OpenTelemetry.

Setup

Enable AI SDK telemetry on the calls you want covered:

const result = await generateText({
  model,
  prompt: 'What is the weather in Helsinki?',
  experimental_telemetry: { isEnabled: true, functionId: 'weather-agent' },
})

Then point your OTLP exporter at POST /api/v1/traces/ingest, or at POST /api/v1/tenants/{tenant_id}/traces/ingest to name the tenant in the path.

Both require authentication. "Keyless" in the route naming refers to the tenant not appearing in the path, not to the request being unauthenticated: the handler depends on a scoped ingest token, and an unauthenticated POST returns 401.

What Pisama reads

The AI SDK emits two attribute vocabularies. ai 6.0.x still uses the legacy ai.* names by default; the gen_ai.* semantic conventions are an opt-in. Both are handled.

Content Legacy GenAI conventions
Operation marker ai.operationId gen_ai.operation.name
Prompt ai.prompt, ai.prompt.messages gen_ai.input.messages
Response ai.response.text, ai.response.object gen_ai.output.messages
Tool calls ai.response.toolCalls, ai.toolCall.name / .args / .result / .id gen_ai.tool.name, gen_ai.tool.call.arguments / .result / .id
Tokens ai.usage.promptTokens, ai.usage.completionTokens gen_ai.usage.input_tokens, gen_ai.usage.output_tokens
Model ai.model.id, ai.model.provider gen_ai.request.model

Span shapes

A generateText call with one tool produces four spans:

ai.generateText                    outer span, carries ai.prompt + ai.response.text
  ai.generateText.doGenerate       step 1, returns a tool call
  ai.toolCall                      the tool executing
  ai.generateText.doGenerate       step 2, returns final text

The outer ai.generateText span carries only ai.* attributes, so it is identified by ai.operationId. It holds the run's top-level prompt and final response; the inner doGenerate spans hold the per-step detail.

Framework attribution caveat

A doGenerate span carries gen_ai.system set to the provider (openai, anthropic), so framework detection resolves it to that provider rather than to vercel_ai_sdk. Only the outer span, which has no gen_ai.system, resolves to vercel_ai_sdk. This is deliberate: marker precedence was left alone because reordering it would reclassify existing traces from every other framework.

On the @pisama/sdk npm package

A TypeScript middleware package is published on npm exposing an observe() wrapper. It does not currently deliver data. It posts to an ingest route that was retired in a June 2026 product change, so the wrapper runs and the call succeeds, but nothing arrives. Use the OpenTelemetry path above instead. The package is not maintained against the current backend.