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.