Skip to main content
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.

Automatic Tool Tracking

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