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

# Vercel AI SDK Integration

> Complete guide to integrating Satori memory with the Vercel AI SDK

## Overview

Satori provides first-class integration with the Vercel AI SDK through the `@usesatori/tools` package. This guide covers everything from basic setup to advanced patterns.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @usesatori/tools ai @ai-sdk/openai
  ```

  ```bash pnpm theme={null}
  pnpm add @usesatori/tools ai @ai-sdk/openai
  ```

  ```bash yarn theme={null}
  yarn add @usesatori/tools ai @ai-sdk/openai
  ```
</CodeGroup>

## Basic Integration

### Step 1: Create Memory Tools

The `memoryTools()` function creates AI SDK-compatible tools that the LLM can use to manage memories:

```typescript theme={null}
import { memoryTools, getContext } from '@usesatori/tools';

const tools = memoryTools({
  apiKey: process.env.SATORI_API_KEY!,
  baseUrl: process.env.SATORI_URL!,
  userId: 'user-123',
});
```

### Step 2: Pre-fetch Memory Context

Fetch relevant memories before calling the LLM:

```typescript theme={null}
const memoryContext = await getContext(
  {
    apiKey: process.env.SATORI_API_KEY!,
    baseUrl: process.env.SATORI_URL!,
    userId: 'user-123',
  },
  userMessage,
  { limit: 5, threshold: 0.7 }
);
```

### Step 3: Stream with Memory

Use `streamText()` with memory tools and context:

```typescript theme={null}
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';

const result = await streamText({
  model: openai('gpt-4o'),
  system: `You are a helpful assistant with long-term memory.
  
What you know about this user:
${memoryContext}

When the user shares important information, use the add_item tool to save it.`,
  messages,
  tools,
});

return result.toDataStreamResponse();
```

## Complete API Route Example

Here's a full Next.js API route with memory:

```typescript app/api/chat/route.ts theme={null}
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { memoryTools, getContext } from '@usesatori/tools';

export async function POST(req: Request) {
  try {
    const { messages } = await req.json();
    const userMessage = messages[messages.length - 1].content;
    
    // Get user ID from your auth system
    const session = await getSession(req);
    if (!session?.userId) {
      return new Response('Unauthorized', { status: 401 });
    }
    
    // Create memory configuration
    const memoryConfig = {
      apiKey: process.env.SATORI_API_KEY!,
      baseUrl: process.env.SATORI_URL!,
      userId: session.userId,
    };
    
    // Create memory tools
    const tools = memoryTools(memoryConfig);
    
    // Pre-fetch relevant context
    const memoryContext = await getContext(
      memoryConfig,
      userMessage,
      { limit: 5 }
    );
    
    // Stream response with memory
    const result = await streamText({
      model: openai('gpt-4o'),
      system: `You are a helpful assistant with long-term memory.
      
What you know about this user:
${memoryContext}

Use the add_item tool when the user:
- Shares preferences or opinions
- Provides personal information
- Mentions important dates or events
- Expresses goals or intentions

Be natural and conversational. Don't explicitly mention that you're saving memories.`,
      messages,
      tools,
      maxSteps: 5, // Allow multiple tool calls
    });
    
    return result.toDataStreamResponse();
  } catch (error) {
    console.error('Chat error:', error);
    return new Response('Internal Server Error', { status: 500 });
  }
}
```

<Tip>
  Set `maxSteps: 5` to allow the LLM to make multiple tool calls in a single response (e.g., search and then save).
</Tip>

## Available Tools

The `memoryTools()` function provides two tools:

### add\_item

Saves information to memory. The LLM calls this automatically when it detects important information.

```typescript theme={null}
// LLM automatically calls this
{
  tool: 'add_item',
  parameters: {
    memory: 'User prefers TypeScript over JavaScript for type safety'
  }
}
```

<ParamField body="memory" type="string" required>
  The information to save. Should be a complete, self-contained statement.
</ParamField>

<ParamField body="metadata" type="object">
  Optional metadata for categorization:

  ```typescript theme={null}
  {
    memory: 'User prefers TypeScript',
    metadata: {
      category: 'preferences',
      tags: ['programming', 'languages']
    }
  }
  ```
</ParamField>

### delete\_memory

Removes a specific memory by ID.

```typescript theme={null}
// LLM calls this when user asks to forget something
{
  tool: 'delete_memory',
  parameters: {
    memoryId: 'uuid-of-memory'
  }
}
```

<ParamField body="memoryId" type="string" required>
  The UUID of the memory to delete. The LLM can get this from the context.
</ParamField>

## Advanced Patterns

### Pattern 1: Conditional Context Injection

Only inject context when relevant:

```typescript theme={null}
const userMessage = messages[messages.length - 1].content;

// Check if message might need memory context
const needsContext = /what|remember|know|told|said/i.test(userMessage);

let memoryContext = '';
if (needsContext) {
  memoryContext = await getContext(config, userMessage);
}

const result = await streamText({
  model: openai('gpt-4o'),
  system: `You are a helpful assistant.
  ${memoryContext ? `\nWhat you know:\n${memoryContext}` : ''}`,
  messages,
  tools,
});
```

### Pattern 2: Category-Based Memory

Use metadata to organize memories by category:

```typescript theme={null}
const tools = {
  add_preference: tool({
    description: 'Save a user preference',
    parameters: z.object({
      preference: z.string(),
    }),
    execute: async ({ preference }) => {
      await client.addMemory(preference, {
        metadata: { category: 'preference' },
      });
      return 'Preference saved';
    },
  }),
  add_fact: tool({
    description: 'Save a factual piece of information',
    parameters: z.object({
      fact: z.string(),
    }),
    execute: async ({ fact }) => {
      await client.addMemory(fact, {
        metadata: { category: 'fact' },
      });
      return 'Fact saved';
    },
  }),
};
```

### Pattern 3: Streaming with Tool Call Feedback

Show users when memories are being saved:

```typescript theme={null}
'use client';

import { useChat } from 'ai/react';

export default function ChatPage() {
  const { messages, input, handleInputChange, handleSubmit } = useChat({
    onToolCall: ({ toolCall }) => {
      if (toolCall.toolName === 'add_item') {
        console.log('Saving memory:', toolCall.args.memory);
        // Show toast notification
      }
    },
  });
  
  return (
    <div>
      {messages.map((message) => (
        <div key={message.id}>
          <p>{message.content}</p>
          
          {/* Show tool calls */}
          {message.toolInvocations?.map((tool, i) => (
            <div key={i} className="text-sm text-gray-500">
              {tool.toolName === 'add_item' && (
                <span>💾 Saved to memory</span>
              )}
            </div>
          ))}
        </div>
      ))}
      
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} />
        <button type="submit">Send</button>
      </form>
    </div>
  );
}
```

### Pattern 4: Multi-Step Reasoning

Allow the LLM to search before responding:

```typescript theme={null}
const tools = {
  ...memoryTools(config),
  search_memory: tool({
    description: 'Search for relevant memories',
    parameters: z.object({
      query: z.string().describe('What to search for'),
    }),
    execute: async ({ query }) => {
      const memories = await client.searchMemories(query, { limit: 3 });
      return memories.map(m => m.content).join('\n');
    },
  }),
};

const result = await streamText({
  model: openai('gpt-4o'),
  system: `You are a helpful assistant with memory.
  
Use search_memory to find relevant information before answering questions.
Use add_item to save important new information.`,
  messages,
  tools,
  maxSteps: 5, // Allow search → respond → save flow
});
```

<Warning>
  This pattern is less reliable than pre-fetching context. The LLM may not always call `search_memory` when needed.
</Warning>

## Error Handling

Handle errors gracefully in production:

```typescript theme={null}
export async function POST(req: Request) {
  try {
    const { messages } = await req.json();
    
    // Validate input
    if (!messages || !Array.isArray(messages)) {
      return new Response('Invalid request', { status: 400 });
    }
    
    const memoryConfig = {
      apiKey: process.env.SATORI_API_KEY!,
      baseUrl: process.env.SATORI_URL!,
      userId: session.userId,
    };
    
    // Try to fetch context, but don't fail if it errors
    let memoryContext = '';
    try {
      memoryContext = await getContext(
        memoryConfig,
        messages[messages.length - 1].content
      );
    } catch (error) {
      console.error('Failed to fetch memory context:', error);
      // Continue without context
    }
    
    const tools = memoryTools(memoryConfig);
    
    const result = await streamText({
      model: openai('gpt-4o'),
      system: `You are a helpful assistant.
      ${memoryContext ? `\nWhat you know:\n${memoryContext}` : ''}`,
      messages,
      tools,
    });
    
    return result.toDataStreamResponse();
  } catch (error) {
    console.error('Chat error:', error);
    
    // Return user-friendly error
    return new Response(
      JSON.stringify({ error: 'Failed to process message' }),
      { status: 500, headers: { 'Content-Type': 'application/json' } }
    );
  }
}
```

## Testing

Test your memory integration:

```typescript theme={null}
import { POST } from './route';

describe('Chat API with Memory', () => {
  it('saves memories when user shares information', async () => {
    const request = new Request('http://localhost:3000/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        messages: [
          { role: 'user', content: 'Remember that I love TypeScript' }
        ],
      }),
    });
    
    const response = await POST(request);
    expect(response.status).toBe(200);
    
    // Verify memory was saved
    const memories = await client.searchMemories('TypeScript');
    expect(memories).toHaveLength(1);
    expect(memories[0].content).toContain('TypeScript');
  });
});
```

## Performance Optimization

<AccordionGroup>
  <Accordion title="Cache embeddings for common queries">
    ```typescript theme={null}
    const embeddingCache = new Map<string, string>();

    async function getCachedContext(query: string) {
      if (embeddingCache.has(query)) {
        return embeddingCache.get(query)!;
      }
      
      const context = await getContext(config, query);
      embeddingCache.set(query, context);
      return context;
    }
    ```
  </Accordion>

  <Accordion title="Parallel context fetching">
    ```typescript theme={null}
    // Fetch context and start LLM call in parallel
    const [memoryContext] = await Promise.all([
      getContext(config, userMessage),
      // Other async operations
    ]);
    ```
  </Accordion>

  <Accordion title="Limit context size">
    ```typescript theme={null}
    // Fetch fewer memories for faster responses
    const context = await getContext(config, userMessage, {
      limit: 3, // Instead of default 10
    });
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Direct Client" icon="wrench" href="/guides/direct-client">
    Use MemoryClient for custom integrations
  </Card>

  <Card title="Next.js Integration" icon="react" href="/guides/nextjs-integration">
    Build a complete Next.js app with memory
  </Card>

  <Card title="API Reference" icon="book" href="/api-reference/memory/add">
    Explore the complete API
  </Card>

  <Card title="Examples" icon="code" href="/examples/chat-with-memory">
    See complete implementations
  </Card>
</CardGroup>
