Skip to main content
GitHub

Vercel AI (JS)

Instrument Vercel AI SDK.

Auto-instrument the Vercel AI SDK for unified tracing across providers.

Installation

npm install risicare ai @ai-sdk/openai

Quick Start

import { init } from 'risicare';
import { patchVercelAI } from 'risicare/vercel-ai';
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
 
// Initialize Risicare
init();
 
// Get traced wrapper functions
const { tracedGenerateText, tracedStreamText, tracedGenerateObject } = patchVercelAI();
 
// Wrap the Vercel AI functions
const wrappedGenerateText = tracedGenerateText(generateText);
 
// Use the wrapped function — traced automatically
const { text } = await wrappedGenerateText({
  model: openai('gpt-4o'),
  prompt: 'Hello!',
});

How It Works

patchVercelAI() returns higher-order functions that wrap Vercel AI SDK functions. Each HOF takes the original function and returns a traced version:

import { patchVercelAI } from 'risicare/vercel-ai';
import { generateText, streamText, generateObject } from 'ai';
 
const { tracedGenerateText, tracedStreamText, tracedGenerateObject } = patchVercelAI();
 
// Wrap each function you want to trace
const generate = tracedGenerateText(generateText);
const stream = tracedStreamText(streamText);
const genObject = tracedGenerateObject(generateObject);

Available Wrappers

WrapperWrapsDescription
tracedGenerateTextgenerateTextSingle text generation
tracedStreamTextstreamTextStreaming text generation
tracedGenerateObjectgenerateObjectStructured output generation

Captured Data

Typed span fields, not gen_ai.* attributes

Unlike the OpenAI and Anthropic patches, the Vercel AI wrapper does not emit gen_ai.* attributes. Provider, model, token counts, and cost are populated as typed span fields (via setLlmFields({ provider, model, ...tokens, costUsd })).

FieldDescription
providervercel-ai (the underlying provider is not detected)
modelModel identifier (from response, not request)
input tokensPrompt token count
output tokensCompletion token count
total tokensTotal token count
costUsdCalculated cost (typed field)

Streaming captures limited data

tracedStreamText creates a span but does not capture response attributes (tokens, cost, model). For complete telemetry, use tracedGenerateText when possible.

Streaming

tracedStreamText creates a span for the call but does not capture tokens, cost, or model (see the warning above) — these are not accumulated on the Risicare span. Use tracedGenerateText when you need full telemetry.

import { patchVercelAI } from 'risicare/vercel-ai';
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
 
const { tracedStreamText } = patchVercelAI();
const wrappedStreamText = tracedStreamText(streamText);
 
const result = await wrappedStreamText({
  model: openai('gpt-4o'),
  prompt: 'Write a story',
});
 
// Span tracks the stream
for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}
 
// result.usage is the Vercel AI SDK's own value — Risicare does not record
// tokens/cost on the streamed span.
const usage = await result.usage;

Structured Output

Object generation is traced with schema information:

import { patchVercelAI } from 'risicare/vercel-ai';
import { generateObject } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
 
const { tracedGenerateObject } = patchVercelAI();
const wrappedGenerateObject = tracedGenerateObject(generateObject);
 
const { object } = await wrappedGenerateObject({
  model: openai('gpt-4o'),
  schema: z.object({
    name: z.string(),
    age: z.number(),
  }),
  prompt: 'Generate a person',
});

Multi-Provider Support

Vercel AI supports multiple providers — all are traced:

import { patchVercelAI } from 'risicare/vercel-ai';
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { anthropic } from '@ai-sdk/anthropic';
import { google } from '@ai-sdk/google';
 
const { tracedGenerateText } = patchVercelAI();
const generate = tracedGenerateText(generateText);
 
// All Vercel AI calls are tagged provider: "vercel-ai"; the underlying
// vendor is reflected only in the captured `model` field (no gen_ai.system).
await generate({ model: openai('gpt-4o'), prompt: '...' });                       // model: "gpt-4o"
await generate({ model: anthropic('claude-sonnet-4-20250514'), prompt: '...' });  // model: "claude-sonnet-4-20250514"
await generate({ model: google('gemini-pro'), prompt: '...' });                   // model: "gemini-pro"

Tool Calls

Tool execution is automatically traced:

import { patchVercelAI } from 'risicare/vercel-ai';
import { generateText, tool } from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
 
const { tracedGenerateText } = patchVercelAI();
const generate = tracedGenerateText(generateText);
 
const { text, toolCalls } = await generate({
  model: openai('gpt-4o'),
  tools: {
    weather: tool({
      description: 'Get weather for a location',
      parameters: z.object({ location: z.string() }),
      execute: async ({ location }) => {
        return { temperature: 72, condition: 'sunny' };
      },
    }),
  },
  prompt: 'What is the weather in Paris?',
});

Next.js Integration

For Next.js applications:

// app/api/chat/route.ts
import { init } from 'risicare';
import { patchVercelAI } from 'risicare/vercel-ai';
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
 
// Initialize once
init();
const { tracedStreamText } = patchVercelAI();
const stream = tracedStreamText(streamText);
 
export async function POST(req: Request) {
  const { messages } = await req.json();
 
  const result = await stream({
    model: openai('gpt-4o'),
    messages,
  });
 
  return result.toDataStreamResponse();
}

Next Steps