Vercel AI SDK Integration

Persistent memory and message history for the Vercel AI SDK

Persistent chat history and long-term user memory in one API. Semantic search included, no vector database to run.

Vercel AI SDKapp/api/chat/route.ts
streamText({ model, messages })
  .toUIMessageStreamResponse({
    onFinish: async ({ messages }) => {
      // ← DialogueDB slots in here
    },
  })
DialogueDBdialogue-db
await dialogue.saveMessages(
  toStoredMessages(messages)
)

// history is one call, fed to useChat
await db.getDialogue(id, { namespace })

The integration in one call

From onFinish to a persisted conversation, in one line

Everything an AI app needs to remember users, without a schema, a vector database, or a load-history endpoint to maintain.

With DialogueDB

The conversation database with messages, memory, and semantic search behind one API. A single call inside onFinish persists the full turn.

onFinish: async ({ messages }) => {
  await dialogue.saveMessages(
    toStoredMessages(messages)
  )
}
With a split stack

A chat store for messages, a memory service for facts, a vector layer for search, and code to keep them aligned. Every piece works on its own; together they multiply the SDKs, auth tokens, and data models to maintain.

onFinish: async ({ response }) => {
  // save messages via a chat store SDK
  // send updates to a memory service SDK
  // keep user IDs aligned across both
  // track two data models in app code
  // handle failures in either half
  // pay for two subscriptions
}

The memory difference

Memory that lives alongside every message, not in a second service

Standalone memory tools run beside a chat store and ask you to sync between them. DialogueDB puts messages, memory, and semantic search behind one API and one namespace, so agents pull both from the same place.

The whole conversation, not just extracted facts

Full message history and structured memory live in the same database. Search past turns by meaning, replay tool calls, or query stored facts, without forcing a choice between remembering everything and remembering meaningfully.

You decide what gets remembered

No background inference over conversations, no facts extracted for you. Call createMemory with the value you want to persist, so the memory store stays intentional and easy to audit.

One system, one integration

The same SDK, the same namespace, and the same auth token cover messages, memory, threads, and state. A standalone memory service adds another integration surface and sync logic between two stores.

Memory inside streamTextapp/api/chat/route.ts
1

Search memories relevant to the latest turn

db.searchMemories(userMessage, { namespace })
2

Inject the matches into the system prompt

system: buildPrompt(memories)
3

Stream the response with useChat, unchanged

streamText({ system, messages, onFinish })
4

Save new context worth remembering

db.createMemory({ value, namespace })

The Vercel AI SDK stays stateless. DialogueDB carries the user context between calls without touching the streaming path.

Install in 3 steps

From npm install to the first persisted turn, in under five minutes.

1

Install the client

npm install dialogue-db
2

Set the API key

DIALOGUE_DB_API_KEY=...
3

Wire onFinish

await dialogue.saveMessages(
  toStoredMessages(messages)
)
Get Your Free API Key

No credit card. Free tier to start. Starter is $29/month when usage grows.

The reference example

A working Route Handler and client component you can clone and run as-is.

Persist the UI messages from toUIMessageStreamResponse's onFinish with a small toStoredMessages mapper, then reload them with validateUIMessages. The mapper is written to keep structured tool calls and the tool role intact so they round-trip unchanged.

Runs on Edge and serverless too. The client is HTTP-only, with no connection pool and no native dependencies.

app/api/chat/route.ts
import { streamText, convertToModelMessages } from "ai"
import { openai } from "@ai-sdk/openai"
import { DialogueDB } from "dialogue-db"
import { toStoredMessages } from "@/lib/persist"
 
const db = new DialogueDB()
 
export async function POST(req: Request) {
const { messages, dialogueId, userId } = await req.json()
 
const dialogue = await db.getOrCreateDialogue({
id: dialogueId,
namespace: userId,
})
 
// persist the incoming user turn
await dialogue.saveMessages(toStoredMessages([messages[messages.length - 1]]))
 
const result = streamText({
model: openai("gpt-4o"),
messages: await convertToModelMessages(messages),
})
 
return result.toUIMessageStreamResponse({
originalMessages: messages,
onFinish: async ({ messages: updated }) => {
// persist the new assistant message(s)
await dialogue.saveMessages(toStoredMessages(updated.slice(messages.length)))
},
})
}

The same client does more

Every capability below runs on the SDK you already installed, shown by what it lets you build.

Agent handoffs that keep context

Parent-child dialogues let sub-agents pick up mid-conversation without losing the thread.

dialogue.createThread()

Facts and preferences across sessions

Store what an agent should remember about a user, separate from the message history.

db.createMemory()

Search past conversations by meaning

Find every message a user has sent on a topic without relying on shared keywords.

db.searchMessages()

Per-conversation scratchpads

Give each dialogue its own state for agent workflows, session flags, or in-progress context.

dialogue.saveState()

Multi-user isolation without query code

Namespace every operation so users, tenants, and workspaces stay separated at the data layer.

namespace: "user_abc"

Faithful tool call replay

Every tool invocation and result persists inside the message, so replays match reality.

toStoredMessages(messages)

Where DialogueDB fits

Three trade-offs to weigh when adding memory and persistence to a Vercel AI SDK app.

ApproachPersists messagesUser memory across sessionsSame system
Standalone memory serviceNo, or a second store requiredYes, usually extracted for youNo, two services to run
Framework-bundled persistenceYesRarely, or basicYes but framework-locked
Self-managed (Postgres + pgvector)Design your ownDesign your ownIf you build it that way
DialogueDBYesYes, you control what's storedYes, one API

If you know the AI SDK, you already know DialogueDB

Everything nests under a namespace. The core concepts map one-to-one to what the AI SDK already exposes.

The data model

namespace

scoped to one user or tenant

dialogue

messages, threads, state

indexed for semantic search

memory

facts and preferences, cross-session

indexed for semantic search

AI SDKDialogueDB
Messagemessage
useChat conversationdialogue
User (from auth)namespace
Tool callmessage with tool metadata
User context to remembermemory

What DialogueDB sees

Only what's needed for conversation storage

Model provider calls run directly from your Route Handler to the LLM. DialogueDB sits alongside that request path, not inside it.

The SDK only receives what you explicitly send (message content, memory values, and the identifiers you use to scope them). Model keys, user tokens, and unrelated app data never pass through, even by accident.

What we receive

  • Message content and roles
  • Memory values and metadata
  • Dialogue and namespace IDs

What we never see

  • Model provider credentials
  • User authentication tokens
  • Data outside the conversation

Two paths from your Route Handler

Route Handler

app/api/chat/route.ts

Your server code decides what goes where.

two independent calls

Model provider

OpenAI, Anthropic, etc.

Streaming call goes directly. DialogueDB never sees the payload or the credentials.

DialogueDB

Message + memory only

Receives what you explicitly send. Nothing implicit, nothing extra.

Frequently asked questions

One API for messages, memory, and search

Add it to your Vercel AI SDK app in minutes.