# Memory for the Vercel AI SDK: a middleware for wrapLanguageModel, with any provider | Niadra

> How to give customer memory to a chat route with the Vercel AI SDK: niadraMiddleware is a language model middleware for wrapLanguageModel, which works with any provider; transformParams places the context, wrapGenerate and wrapStream record the answer, and niadraTools hands over the history as AI SDK tools. Code and limits.

URL: https://niadra.com/en/integracoes/ai-sdk

Integration · Vercel AI SDK

# Memory for the Vercel AI SDK.

niadraMiddleware(session) is a language model middleware for wrapLanguageModel({ model, middleware }): the AI SDK's idiomatic path, which works with every provider without a separate provider package. niadraTools(session) returns the history kit as AI SDK tools. TypeScript only.

[Request early access](/en/enterprise)[The adapter's documentation(opens docs.niadra.com)](https://docs.niadra.com/en/integrations/ai-sdk)

The AI SDK hands the interface's messages to the model and returns the stream. The memory, if there is one, is what your route loads from the database for this chat session. What the same person said on WhatsApp to another vendor, or what the orders agent did in the ERP, never reaches the route.

The middleware goes into wrapLanguageModel, once, for any provider: transformParams places the customer's context as a system message right after yours and the suffix, with the deltas and other channels' turns, at the end of the last user message, where every provider accepts it. wrapGenerate and wrapStream record the model's text as the agent's turn, with the usage reported.

The minimal example, as the documentation has it

```
npm install @niadra/sdk ai   # ai 5, 6 or 7, as an optional peer dependency
```

```
// A chat route with the Vercel AI SDK (Next.js App Router or any fetch handler): the model gets
// the customer's context through a middleware, and the history tools next to your own.
import { openai } from "@ai-sdk/openai";
import {
  type UIMessage,
  convertToModelMessages,
  createUIMessageStreamResponse,
  isStepCount,
  streamText,
  toUIMessageStream,
  wrapLanguageModel,
} from "ai";
import { Niadra, handles } from "@niadra/sdk";
import { niadraMiddleware, niadraTools } from "@niadra/sdk/ai-sdk";

const niadra = new Niadra();

/** `userId` comes from your session: the customer is never something the model or the browser picks. */
export async function POST(request: Request, userId: string): Promise<Response> {
  const { id, messages } = (await request.json()) as { id: string; messages: UIMessage[] };
  const convo = niadra.conversation({ subject: handles.appUserId(userId), channel: "web_chat", conversation_id: id });

  const tools = niadraTools(convo);
  const result = streamText({
    model: wrapLanguageModel({
      model: openai("gpt-4.1"),
      // The user signed in, which proves V2 in this space's policy.
      middleware: niadraMiddleware(convo, { verify: { method: "login", level: "V2" } }),
    }),
    system: "You are Acme's support agent. Be brief.",
    messages: await convertToModelMessages(messages),
    tools,
    stopWhen: isStepCount(4),
  });
  return createUIMessageStreamResponse({ stream: toUIMessageStream({ stream: result.stream, tools }) });
}
```

-   TypeScript

The same code is in examples/ai-sdk.ts in the SDK repositories, where it runs in CI against the framework's real types and Niadra's emulator. To try it without Niadra's cloud, niadra-mock and NIADRA\_BASE\_URL=http://127.0.0.1:8765.

## How the adapter wires in

The five primitives of every Niadra integration, in this framework's extension points.

Context

transformParams puts the pack as a system message right after your system messages and the suffix (deltas and other channels' turns) as a text part at the end of the last user message, where every provider accepts it.

Turns

transformParams records the customer's newest message; wrapGenerate and wrapStream record the model's text as the agent's turn, with the usage the provider reported: prompt tokens, cache reads and writes. Tool-only steps record nothing.

Tools

niadraTools(session): the kit as AI SDK tools, bound to the customer; spread them next to your own.

Verification

niadraMiddleware(session, { verify: { method, level } }) records what your app proved before the first read, a login, for instance.

Handoff

conv.handoff() where your route transfers.

## What the agent receives

The context is compiled when the memory changes and served ready, with no AI model on the read. What another channel said during the conversation arrives as a delta, at the end of the prompt.

-   Who the customer is, by what the conversation has proven: the verification level decides what goes in
-   Facts, open items and promises, with the date and the channel they came from
-   What other agents did inside the company, confirmed by the system of record
-   Patterns computed by rule, with the evidence and the expiry
-   The three history tools: search, timeline and open an item, bound to the customer in your code
-   A receipt of every read, chained by SHA-256

[See the context from the inside](/en/produtos/contexto)

## What the adapter does not do

-   Nothing here fails the model call: a context that does not arrive is left out, and a failure to record is logged without content.
-   The middleware does not wrap the provider: any AI SDK LanguageModel, from any provider, works.
-   Tested against ai 7, with the types of versions 5 and 6, and a fake model, with Niadra on the emulator, on Node, Deno, Bun, workerd and the Edge Runtime.

## Frequently asked questions

### Does it work on the Edge Runtime and on Workers?

It does: the TypeScript SDK runs on Node, Deno, Bun, workerd and the Edge Runtime, and the middleware is tested on all five. The Niadra key stays in the server route, never in the browser.

### Who decides which customer it is?

Your code, from the authenticated session: handles.appUserId(userId) opens the conversation. The customer is never something the model or the browser picks. The login proves the level your policy defines, and the middleware records it before the first read.

### Can I mix the history tools with my own?

You can: niadraTools(session) returns AI SDK tools, and you spread them next to yours in the same tools object. stopWhen and the rest of the call stay as they are.

### Do I have to change my model, my prompt or my vendor?

No. The adapter places the context after your instructions and the delta at the end of the prompt, in the extension points the framework already has. Your model, your prompt and your vendor stay the same, and switching any of them later does not erase the memory.

### Where does the data live, and what does it cost?

The data stays in a single region, stated in the contract, encrypted with AES-256-GCM under a key exclusive to your company and protected in a FIPS 140-3 HSM. The price is per conversation or task in which an agent read the memory: US$ 2 to 3 per thousand, by volume, with reads, searches and system events included. Niadra is opening to companies by request, before the public launch.

## Other integrations

-   [LiveKit Agents](/en/integracoes/livekit)
-   [Pipecat](/en/integracoes/pipecat)
-   [Vapi](/en/integracoes/vapi)
-   [Retell AI](/en/integracoes/retell)
-   [ElevenLabs Agents Platform](/en/integracoes/elevenlabs)
-   [Twilio](/en/integracoes/twilio)
-   [WhatsApp Cloud API](/en/integracoes/whatsapp)
-   [OpenAI Agents SDK](/en/integracoes/openai-agents)
-   [LangGraph](/en/integracoes/langgraph)
-   [LangChain](/en/integracoes/langchain)
-   [CrewAI](/en/integracoes/crewai)
-   [n8n](/en/integracoes/n8n)
-   [All 36 integrations, in the documentation(opens docs.niadra.com)](https://docs.niadra.com/en/integrations/overview)

## Tell us what you are building.

A work email and two lines about your agents are enough. The people who write the code reply, with an early-access proposal for your case.

[Rather tell us more about your company? Use the full form](/en/enterprise)
