Supported Versions: Vercel AI SDK v3, v4, v5, and v6. Works with generateText, streamText, generateObject, and streamObject.
What Gets Tracked Automatically
LLM Calls
Model, provider, prompt, messages, system prompt — captured per call.
Responses
Full generated text, finish reason, and structured objects.
Token Usage
Prompt tokens, completion tokens, and total — per session.
Tool Calls
Every tool execution with input args, output result, duration, and errors.
Cost
Auto-calculated per provider (OpenAI, Anthropic, Google).
Errors
Caught, recorded with type and message, then re-thrown — nothing swallowed.
Installation
npm install @sentrial/sdk ai @ai-sdk/openai
# or with other providers
npm install @ai-sdk/anthropic @ai-sdk/google
Quick Start
3 lines of setup. Then use generateText and streamText exactly like you normally would.
import { configureVercel, wrapAISDK } from '@sentrial/sdk';
import * as ai from 'ai';
import { openai } from '@ai-sdk/openai';
// 1. Configure Sentrial
configureVercel({
apiKey: process.env.SENTRIAL_API_KEY,
defaultAgent: 'my-ai-agent', // groups sessions by agent name
userId: 'user_123', // optional — ties sessions to your end users
});
// 2. Wrap the AI SDK
const { generateText, streamText, generateObject, streamObject } = wrapAISDK(ai);
// 3. Use normally — everything is tracked automatically
const { text } = await generateText({
model: openai('gpt-4o'),
prompt: 'What is the capital of France?',
});
// Session created with: input, output, tokens, cost, latency
Configuration Options
configureVercel({
apiKey: process.env.SENTRIAL_API_KEY, // required
apiUrl: 'https://api.sentrial.com', // optional — defaults to production
defaultAgent: 'my-agent', // optional — agent name for grouping
userId: 'user_123', // optional — default user ID
convoId: 'convo_abc', // optional — group sessions into a conversation thread
failSilently: true, // optional — true by default
});
Fail-Safe by Default — With failSilently: true (the default), any Sentrial API errors are logged but never crash your app. Your AI calls always go through. Set to false during development to see full errors.
When you pass tools to generateText or streamText, every tool’s execute function is automatically wrapped. Each execution is recorded with input args, output, duration, and any errors. Zero changes to your tool code.
import { z } from 'zod';
const { generateText } = wrapAISDK(ai);
const { text } = await generateText({
model: openai('gpt-4o'),
prompt: "What's the weather in San Francisco?",
tools: {
getWeather: {
description: 'Get weather for a location',
parameters: z.object({ location: z.string() }),
execute: async ({ location }) => {
// Automatically traced: input args, return value, duration
const res = await fetch(`https://api.weather.com/${location}`);
return res.json();
},
},
searchWeb: {
description: 'Search the web',
parameters: z.object({ query: z.string() }),
execute: async ({ query }) => {
// Errors are caught, recorded, then re-thrown
return await searchAPI(query);
},
},
},
});
In your Sentrial dashboard, each tool call appears as a child event under the session — with the tool name, input, output, and execution time.
Conversation Threading
Use convoId to link multiple AI SDK calls into a single conversation thread. Set it globally in configureVercel() or per-instance in wrapAISDK().
// Per-instance: link all calls from this wrapper to the same conversation
const { generateText } = wrapAISDK(ai, { convoId: `user-${userId}-${Date.now()}` });
// Turn 1
await generateText({ model: openai('gpt-4o'), prompt: 'My name is Alice.' });
// Turn 2 — automatically linked to the same conversation in the dashboard
await generateText({ model: openai('gpt-4o'), prompt: 'What was my name?' });
You can also pass a custom SentrialClient instance if you need full control:
const client = new SentrialClient({ apiKey: process.env.SENTRIAL_API_KEY, pii: true });
const { generateText } = wrapAISDK(ai, { client, defaultAgent: 'my-agent' });
Multi-Step Agentic Calls
When using maxSteps for agentic loops, each step is automatically tracked as a separate event with its own token usage, tool calls, and finish reason. The session aggregates totals across all steps.
const { generateText } = wrapAISDK(ai);
const { text } = await generateText({
model: openai('gpt-4o'),
prompt: 'Find the weather in NYC and summarize it.',
tools: { getWeather: weatherTool },
maxSteps: 5,
});
// Session events: step 1 (tool_call) → step 2 (tool_result) → step 3 (final answer)
// Each step tracked with individual token counts; session has aggregated totals
Streaming
Streaming works transparently. The wrapper intercepts the text stream, accumulates the full response, and records the session when the stream completes.
const { streamText } = wrapAISDK(ai);
const result = streamText({
model: openai('gpt-4o'),
prompt: 'Write a haiku about debugging',
});
// Option 1: Consume the text stream directly
for await (const chunk of result.textStream) {
process.stdout.write(chunk);
}
// Session recorded when stream finishes: full text, tokens, cost, latency
// Option 2: Use with Next.js streaming response
// return result.toDataStreamResponse();
Structured Output
generateObject and streamObject are also wrapped. The output JSON is stored as the session response.
import { z } from 'zod';
const { generateObject } = wrapAISDK(ai);
const { object } = await generateObject({
model: openai('gpt-4o'),
prompt: 'Generate a user profile for testing',
schema: z.object({
name: z.string(),
email: z.string().email(),
role: z.enum(['admin', 'user', 'viewer']),
}),
});
// Session output: JSON.stringify(object)
Multiple Providers
Cost is auto-calculated per provider. The wrapper detects the provider from the model ID and applies the correct pricing.
import { openai } from '@ai-sdk/openai';
import { anthropic } from '@ai-sdk/anthropic';
import { google } from '@ai-sdk/google';
const { generateText } = wrapAISDK(ai);
// OpenAI — gpt-4o, gpt-4o-mini, o3, o3-mini, etc.
await generateText({ model: openai('gpt-4o'), prompt: 'Hello!' });
// Anthropic — claude-sonnet-4, claude-haiku-3.5, etc.
await generateText({ model: anthropic('claude-sonnet-4'), prompt: 'Hello!' });
// Google — gemini-2.5-pro, gemini-2.5-flash, etc.
await generateText({ model: google('gemini-2.5-flash'), prompt: 'Hello!' });
Next.js Chat Route (Full Example)
A complete Next.js API route with streaming, tool calls, and Sentrial tracking:
// app/api/chat/route.ts
import { configureVercel, wrapAISDK } from '@sentrial/sdk';
import * as ai from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
// Configure once at module level
configureVercel({
apiKey: process.env.SENTRIAL_API_KEY,
defaultAgent: 'nextjs-chat',
});
const { streamText } = wrapAISDK(ai);
export async function POST(request: Request) {
const { messages, userId } = await request.json();
const result = streamText({
model: openai('gpt-4o'),
system: 'You are a helpful assistant.',
messages,
tools: {
searchKnowledgeBase: {
description: 'Search internal docs',
parameters: z.object({ query: z.string() }),
execute: async ({ query }) => {
return { results: ['doc1', 'doc2'] };
},
},
},
});
// Stream response to client — session auto-completes when done
return result.toDataStreamResponse();
}
What You See in the Dashboard
Each AI SDK call creates a session in Sentrial with:
Session Overview
Agent name, user ID, status (completed/failed), duration, cost.
Input / Output
The user prompt and the full generated response, displayed as a conversation.
Events Timeline
Tool calls shown as events with input, output, and execution time.
Token & Cost Breakdown
Prompt tokens, completion tokens, total tokens, estimated cost in USD.
Error Handling
If an AI call or tool execution throws, the error is recorded and the session is marked as failed. The original error is always re-thrown so your app’s error handling works normally.
try {
const { text } = await generateText({
model: openai('gpt-4o'),
prompt: 'Hello!',
});
} catch (error) {
// Error is recorded in Sentrial as a failed session
// with error type and message, then re-thrown here
console.error(error);
}
Next Steps