---
title: Vercel AI SDK
description: "Use beliefs with the Vercel AI SDK. Middleware-based integration for streamText and generateText."
---

## Use Today with the Core SDK

The core `beliefs` package works with the Vercel AI SDK right now. Wrap your `generateText` or `streamText` calls with `before`/`after`:

```bash
npm i beliefs ai @ai-sdk/anthropic
```

### With generateText

```ts
import { generateText } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import Beliefs from 'beliefs'

const beliefs = new Beliefs({
  apiKey: process.env.BELIEFS_KEY,
  agent: 'research-agent',
  namespace: 'vercel-ai',
  writeScope: 'space',
})

async function research(question: string) {
  const context = await beliefs.before(question)

  const { text } = await generateText({
    model: anthropic('claude-sonnet-4-20250514'),
    system: context.prompt,
    prompt: question,
  })

  const delta = await beliefs.after(text)
  console.log(`clarity: ${delta.clarity}, changes: ${delta.changes.length}`)
  return text
}
```

### With streamText

```ts
import { streamText } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import Beliefs from 'beliefs'

const beliefs = new Beliefs({
  apiKey: process.env.BELIEFS_KEY,
  agent: 'research-agent',
  namespace: 'vercel-ai',
  writeScope: 'space',
})

async function researchStream(question: string) {
  const context = await beliefs.before(question)

  const result = streamText({
    model: anthropic('claude-sonnet-4-20250514'),
    system: context.prompt,
    prompt: question,
  })

  let fullText = ''
  for await (const chunk of result.textStream) {
    process.stdout.write(chunk)
    fullText += chunk
  }

  const delta = await beliefs.after(fullText)
  return { text: fullText, delta }
}
```

<Callout type="warning" title="Streaming lifecycle">
Call `after()` exactly once per turn, after the stream completes. Do not call it on partial chunks. Each call triggers extraction and fusion. Calling per-chunk creates duplicate beliefs from incomplete text. For Next.js route handlers, use the `onFinish` callback shown below.
</Callout>

### With Tool Results

Feed tool results individually so beliefs update as evidence arrives:

```ts
import { generateText, tool } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import { z } from 'zod'

const context = await beliefs.before(question)

const { text, toolResults } = await generateText({
  model: anthropic('claude-sonnet-4-20250514'),
  system: context.prompt,
  prompt: question,
  tools: {
    search: tool({
      description: 'Search the web',
      parameters: z.object({ query: z.string() }),
      execute: async ({ query }) => searchWeb(query),
    }),
  },
  maxSteps: 5,
})

for (const result of toolResults) {
  await beliefs.after(JSON.stringify(result.result), { tool: result.toolName })
}
await beliefs.after(text)
```

### In a Next.js Route Handler

```ts
import { streamText } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import Beliefs from 'beliefs'

const beliefs = new Beliefs({
  apiKey: process.env.BELIEFS_KEY,
  agent: 'chat-agent',
  namespace: 'chat',
  writeScope: 'space',
})

export async function POST(req: Request) {
  const { messages } = await req.json()
  const lastMessage = messages[messages.length - 1]?.content ?? ''

  const context = await beliefs.before(lastMessage)

  const result = streamText({
    model: anthropic('claude-sonnet-4-20250514'),
    system: context.prompt,
    messages,
    onFinish: async ({ text }) => {
      await beliefs.after(text)
    },
  })

  return result.toDataStreamResponse()
}
```

---

## Middleware Adapter

```bash
npm i beliefs
```

The adapter integrates through the Vercel AI SDK's middleware system. Wrap your model with `beliefsMiddleware` for automatic belief extraction:

```ts
import { generateText, wrapLanguageModel } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import Beliefs from 'beliefs'
import { beliefsMiddleware } from 'beliefs/vercel-ai'

const beliefs = new Beliefs({
  apiKey: process.env.BELIEFS_KEY,
  agent: 'research-agent',
  namespace: 'vercel-ai',
  writeScope: 'space',
})

const { text } = await generateText({
  model: wrapLanguageModel({
    model: anthropic('claude-sonnet-4-20250514'),
    middleware: beliefsMiddleware(beliefs),
  }),
  prompt: 'Research the competitive landscape for AI dev tools',
})
```

### Capture Modes

The `capture` option controls what the middleware feeds back to `after()`:

- **`'response'`** (default): extract beliefs only from the model's final text response. Lightest mode; one `after()` call per turn.
- **`'tools'`:** extract beliefs from each tool result as it returns. Best when tools fetch external data (web search, DB queries) you want tracked individually.
- **`'all'`:** extract from both tool results *and* the final response. Most comprehensive; one `after()` call per tool result plus one for the final text.

```ts
beliefsMiddleware(beliefs, { capture: 'response' }) // final response (default)
beliefsMiddleware(beliefs, { capture: 'tools' })    // each tool call result
beliefsMiddleware(beliefs, { capture: 'all' })      // both
```

### Configuration

```ts
beliefsMiddleware(beliefs, {
  capture: 'all',
  includeContext: true,
})
```

| Option | Default | Description |
|--------|---------|-------------|
| `capture` | `'response'` | What to extract beliefs from: `'response'`, `'tools'`, or `'all'` |
| `includeContext` | `true` | Inject belief context into system prompt via `before()` |
| `resolveThreadId` | — | Required when `beliefs` uses `writeScope: 'thread'` and no thread is already bound |

<Callout type="info" title="Thread-scoped chat memory">
If you keep the SDK default `writeScope: 'thread'`, either bind the thread ahead of time with `beliefs.withThread(threadId)` or pass `resolveThreadId` so the middleware can scope each invocation correctly.
</Callout>

```ts
import { streamText, wrapLanguageModel } from 'ai'
import { anthropic } from '@ai-sdk/anthropic'
import Beliefs from 'beliefs'
import { beliefsMiddleware } from 'beliefs/vercel-ai'

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

  const beliefs = new Beliefs({
    apiKey: process.env.BELIEFS_KEY,
    namespace: 'support',
    writeScope: 'thread',
  })

  const model = wrapLanguageModel({
    model: anthropic('claude-sonnet-4-20250514'),
    middleware: beliefsMiddleware(beliefs, {
      resolveThreadId: () => conversationId,
    }),
  })

  const result = streamText({ model, messages })
  return result.toDataStreamResponse()
}
```

<CardGroup cols={2}>
  <DocsCard title="Patterns" description="Common integration patterns with the core SDK." href="/dev/sdk/patterns" />
  <DocsCard title="Hack Guide" description="Quick framework recipes for hackathons." href="/dev/tutorial/hack-guide" />
</CardGroup>
