> ## Documentation Index
> Fetch the complete documentation index at: https://www.sentrial.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Vercel AI SDK Integration

> 3 lines of setup to automatically track all Vercel AI SDK calls — input/output, tokens, cost, tool executions, and errors.

<Note>
  **Supported Versions:** Vercel AI SDK v3, v4, v5, and v6. Works with `generateText`, `streamText`, `generateObject`, and `streamObject`.
</Note>

## What Gets Tracked Automatically

<CardGroup cols={2}>
  <Card title="LLM Calls">
    Model, provider, prompt, messages, system prompt — captured per call.
  </Card>

  <Card title="Responses">
    Full generated text, finish reason, and structured objects.
  </Card>

  <Card title="Token Usage">
    Prompt tokens, completion tokens, and total — per session.
  </Card>

  <Card title="Tool Calls">
    Every tool execution with input args, output result, duration, and errors.
  </Card>

  <Card title="Cost">
    Auto-calculated per provider (OpenAI, Anthropic, Google).
  </Card>

  <Card title="Errors">
    Caught, recorded with type and message, then re-thrown — nothing swallowed.
  </Card>
</CardGroup>

## Installation

```bash theme={null}
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.

```typescript theme={null}
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 — string or () => string
});

// 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

```typescript theme={null}
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 — string or function
  convoId: 'convo_abc',                  // optional — string or function
  failSilently: true,                    // optional — true by default
});
```

<Tip>
  **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.
</Tip>

## Dynamic User & Conversation IDs

`userId` and `convoId` accept functions for per-request resolution. This is essential for apps using Clerk, NextAuth, or any auth system where the user changes per request.

```typescript theme={null}
import { auth } from '@clerk/nextjs/server';

configureVercel({
  apiKey: process.env.SENTRIAL_API_KEY,
  defaultAgent: 'my-chatbot',
  userId: () => auth().userId ?? 'anonymous',  // resolved per AI call
  convoId: () => getConversationId(),           // resolved per AI call
});
```

Functions are called at session creation time (every `generateText`/`streamText` call), so they always get the current request's values.

You can also set them per-instance via `wrapAISDK`:

```typescript theme={null}
const { streamText } = wrapAISDK(ai, {
  userId: () => getCurrentUser(),
  convoId: () => getCurrentThreadId(),
});
```

## 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.

```typescript theme={null}
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()`.

```typescript theme={null}
// 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:

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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.

```typescript theme={null}
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, dynamic user tracking, and Sentrial:

```typescript theme={null}
// app/api/chat/route.ts
import { configureVercel, wrapAISDK } from '@sentrial/sdk';
import { auth } from '@clerk/nextjs/server';
import * as ai from 'ai';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';

// Configure once at module level — userId resolved per request
configureVercel({
  apiKey: process.env.SENTRIAL_API_KEY,
  defaultAgent: 'nextjs-chat',
  userId: () => auth().userId ?? 'anonymous',
});

const { streamText } = wrapAISDK(ai);

export async function POST(request: Request) {
  const { messages, conversationId } = await request.json();

  // Per-instance convoId for this conversation thread
  const { streamText: streamWithConvo } = wrapAISDK(ai, {
    convoId: conversationId,
  });

  const result = streamWithConvo({
    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();
}
```

<Note>
  **No Clerk?** Replace `auth().userId` with whatever your auth system provides — `session.user.id`, `req.headers['x-user-id']`, `getServerSession().user.id`, etc. Any function that returns a string works.
</Note>

## What You See in the Dashboard

Each AI SDK call creates a session in Sentrial with:

<CardGroup cols={2}>
  <Card title="Session Overview">
    Agent name, user ID, status (completed/failed), duration, cost.
  </Card>

  <Card title="Input / Output">
    The user prompt and the full generated response, displayed as a conversation.
  </Card>

  <Card title="Events Timeline">
    Tool calls shown as events with input, output, and execution time.
  </Card>

  <Card title="Token & Cost Breakdown">
    Prompt tokens, completion tokens, total tokens, estimated cost in USD.
  </Card>
</CardGroup>

## 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.

```typescript theme={null}
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

<CardGroup cols={2}>
  <Card title="TypeScript SDK Reference" icon="js" href="/sdk/typescript">
    Full SDK documentation with all methods and options.
  </Card>

  <Card title="Custom Agents" icon="gear" href="/integrations/custom">
    Framework-agnostic integration for non-Vercel AI SDK setups.
  </Card>
</CardGroup>
