# Vercel AI SDK



The gateway works with the [Vercel AI SDK](https://ai-sdk.dev) in two ways:

* **OpenAI-compatible provider** — `generateText`, `streamText`, and the rest
  of the core API against `/v1/chat/completions`.
* **Native UI Message Stream** — `POST /v1/chat` speaks the AI SDK's UI
  Message Stream protocol directly, so `useChat` can consume the gateway
  without a translation layer.

## Server: generateText and streamText [#server-generatetext-and-streamtext]

Install the AI SDK and its OpenAI-compatible provider:

```bash
npm install ai @ai-sdk/openai-compatible
```

Create a provider pointed at the gateway, then use any core function:

```ts
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
import { generateText, streamText } from 'ai';

const clusterbase = createOpenAICompatible({
  name: 'clusterbase',
  baseURL: 'https://llm.clusterbase.dev/v1',
  apiKey: process.env.CLUSTER_API_KEY,
  // Report token usage on streaming calls (result.usage).
  includeUsage: true,
});

// One-shot generation
const { text } = await generateText({
  model: clusterbase('claude-opus-5'),
  prompt: 'Explain quantum computing in simple terms.',
});

// Streaming
const result = streamText({
  model: clusterbase('gpt-5.6'),
  prompt: 'Write a haiku about gateways.',
});

for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}
```

Switch models by changing the ID string — see
[Models and pricing](/docs/llm-gateway/models) for the catalog.

## Browser: useChat against /v1/chat [#browser-usechat-against-v1chat]

`POST /v1/chat` accepts AI SDK `UIMessage[]` input and streams typed UI
Message Stream chunks (`x-vercel-ai-ui-message-stream: v1`). The endpoint is
streaming-only.

Point `useChat` at a route handler in your app, and have the handler forward
to the gateway with your API key. Keep the key on the server — never ship it
to the browser.

```ts title="app/api/chat/route.ts"
export async function POST(req: Request) {
  const { messages } = await req.json();

  return fetch('https://llm.clusterbase.dev/v1/chat', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.CLUSTER_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'claude-opus-5',
      messages,
    }),
  });
}
```

```tsx title="app/page.tsx"
'use client';

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

export default function Chat() {
  const { messages, sendMessage } = useChat();
  // Render messages and call sendMessage(...) from your input.
}
```

The stream carries text, reasoning, and tool-input chunks. Tool execution is
client-side: the gateway emits `tool-input-*` chunks for tools you define, and
your app runs them.

## Finish reasons [#finish-reasons]

The terminal `finish` chunk (and `result.finishReason` from `generateText` /
`streamText`) carries the AI SDK's finish reason, not always `"stop"`. For
Claude models, a refusal reports `"content-filter"` and a `max_tokens` stop
reports `"length"`; usage in that final chunk reflects the exact tokens
generated, including a refusal that produced no visible output. A terminal
reason the gateway doesn't recognize is reported as `"other"`.

## Options [#options]

`/v1/chat` accepts the same top-level options as `/v1/chat/completions` —
`temperature`, `max_tokens`, `top_p`, `stop`, `tools`, `tool_choice`, and
`reasoning_effort` — alongside `model` and `messages`. See the
[LLM Gateway API reference](/docs/api/llm-gateway) for the full schema.
