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

# Vercel AI SDK

> Persist AI SDK streams on S2 so chat generations can survive refreshes and reconnects.

S2 integrates with the [AI SDK](https://ai-sdk.dev/) through the
`@s2-dev/resumable-stream/aisdk` entrypoint.

The package stores AI SDK `UIMessageChunk` events in S2 and replays them with
the same SSE format that the AI SDK transport expects.

## Install

```bash theme={null}
npm install @s2-dev/resumable-stream ai @ai-sdk/react @ai-sdk/openai
```

This integration requires `ai >= 5.0`; older AI SDK releases do not export
`UIMessageChunk`.

Create a basin with **Create Stream on Append** enabled. If your app reads
history streams before writing to them, also enable **Create Stream on Read**.

```bash theme={null}
export S2_ACCESS_TOKEN="..."
export S2_BASIN="my-basin"
export OPENAI_API_KEY="..."
```

## Server Setup

Create the S2 chat helper once and reuse it in your routes.

```ts lib/s2.ts theme={null}
import { createResumableChat } from '@s2-dev/resumable-stream/aisdk';

export const chat = createResumableChat({
  accessToken: process.env.S2_ACCESS_TOKEN!,
  basin: process.env.S2_BASIN!,
});
```

## Start A Turn

The POST route starts the model call, writes chunks to S2, and streams the same
chunks back to the AI SDK client.

```ts app/api/chat/route.ts theme={null}
import { openai } from '@ai-sdk/openai';
import { convertToModelMessages, streamText, type UIMessage } from 'ai';
import { after } from 'next/server';
import { chat } from '@/lib/s2';

export async function POST(req: Request) {
  const { id, messages } = (await req.json()) as {
    id: string;
    messages: UIMessage[];
  };

  const result = streamText({
    model: openai('gpt-4o-mini'),
    messages: await convertToModelMessages(messages),
  });

  return chat.makeResumable(`chat-${id}`, result.toUIMessageStream(), {
    waitUntil: (p) => after(async () => { await p; }),
  });
}
```

## Reconnect Route

`resume: true` in `useChat` calls the reconnect route on mount. With the default
AI SDK transport, that route is `${api}/${chatId}/stream`.

```ts app/api/chat/[id]/stream/route.ts theme={null}
import { chat } from '@/lib/s2';

export async function GET(
  _req: Request,
  { params }: { params: Promise<{ id: string }> },
) {
  const { id } = await params;
  return chat.replay(`chat-${id}`);
}
```

If the turn is still active, S2 replays the remaining chunks. If there is no
active turn, the route returns `204`.

## Client

Use the AI SDK `useChat` React hook to create a resumable chat.

```tsx app/page.tsx theme={null}
'use client';

import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';

const transport = new DefaultChatTransport({ api: '/api/chat' });

export default function Chat() {
  const { messages, sendMessage, status } = useChat({
    id: 'my-chat-id',
    transport,
    resume: true,
  });

  // Render messages and call sendMessage(...)
  return null;
}
```

## Completed History

`@s2-dev/resumable-stream/aisdk` stores the streaming chunks for resumability.
It does not decide where your completed transcript lives.

For a chat app, keep a transcript stream or database table in app code:

1. Load completed messages before rendering the page.
2. Pass them to `useChat` as initial state if your UI needs refresh-after-done
   history.
3. Use `chat.makeResumable(...)` for the active assistant turn.

The runnable example stores completed messages in a separate S2 history stream
and uses a live stream for the active generation.

## Options

`createResumableChat` accepts the shared resumable-stream options:

| option            | default         | description                                                                                                                                           |
| ----------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mode`            | `"single-use"`  | `"single-use"` uses one stream per generation. `"shared"` reuses one active-generation stream. `"session"` appends generations to one durable stream. |
| `endpoints`       | S2 defaults     | Optional endpoint overrides, commonly used with S2 Lite.                                                                                              |
| `batchSize`       | `10`            | Maximum number of chunks per append batch.                                                                                                            |
| `lingerDuration`  | `50`            | Maximum batching delay in milliseconds.                                                                                                               |
| `leaseDurationMs` | `5000`          | `shared` mode takeover window for stale active generations.                                                                                           |
| `onError`         | generic message | Maps upstream errors to the stored AI SDK error chunk.                                                                                                |

`makeResumable` accepts:

| option      | default      | description                                                                                                                          |
| ----------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| `delivery`  | `"response"` | `"response"` streams chunks on the POST response. `"replay"` returns `202` and expects the client to read from `replay`.             |
| `waitUntil` | none         | Keeps persistence alive after the HTTP response returns. Use `after`, Cloudflare `waitUntil`, or your server's background-task hook. |

## Example

A complete Bun server and browser client is available here:
[`examples/ai-sdk-resumable-chat`](https://github.com/s2-streamstore/s2-sdk-typescript/tree/main/examples/ai-sdk-resumable-chat).

The TypeScript SDK repo also includes direct S2 examples for:

* [Agent sessions](https://github.com/s2-streamstore/s2-sdk-typescript/blob/main/examples/ai-sdk-agent-session.ts)
* [Chat persistence](https://github.com/s2-streamstore/s2-sdk-typescript/blob/main/examples/ai-sdk-chat-persistence.ts)
* [Multi-agent dinner party](https://github.com/s2-streamstore/s2-sdk-typescript/blob/main/examples/ai-sdk-dinner-party.ts)
