# Vercel AI SDK

Source: https://aiand-docs-staging.pages.dev/integrations/vercel-ai/

The Vercel AI SDK connects to ai& through `@ai-sdk/openai-compatible`.

## Setup

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

```ts
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";

export const aiand = createOpenAICompatible({
  name: "aiand",
  baseURL: "https://api.aiand.com/v1",
  apiKey: process.env.AIAND_API_KEY,
  includeUsage: true,
});
```

Create an API key in the [ai& console](https://console.aiand.com) and set `AIAND_API_KEY`.

## Generate text

```ts
import { generateText } from "ai";
import { aiand } from "./client";

const { text } = await generateText({
  model: aiand("google/gemma-4-31b-it"),
  prompt: "Tell me a haiku about Tokyo.",
});

console.log(text);
```

A system prompt belongs in `instructions`, not a `{ role: "system" }` message.

## Stream to the browser (Next.js App Router)

```bash
npm install @ai-sdk/react
```

```ts
// app/api/chat/route.ts
import { convertToModelMessages, createUIMessageStreamResponse, streamText, toUIMessageStream, type UIMessage } from "ai";
import { aiand } from "@/lib/aiand";

export async function POST(req: Request) {
  const { messages }: { messages: UIMessage[] } = await req.json();
  const result = streamText({
    model: aiand("google/gemma-4-31b-it"),
    messages: await convertToModelMessages(messages),
  });
  return createUIMessageStreamResponse({
    stream: toUIMessageStream({ stream: result.stream }),
  });
}
```

```tsx
// app/page.tsx
"use client";
import { useChat } from "@ai-sdk/react";
import { useState } from "react";

export default function Page() {
  const { messages, sendMessage } = useChat();
  const [input, setInput] = useState("");
  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        sendMessage({ text: input });
        setInput("");
      }}
    >
      {messages.map((m) => (
        <div key={m.id}>
          {m.role}:{" "}
          {m.parts.map((part, i) => (part.type === "text" ? <span key={i}>{part.text}</span> : null))}
        </div>
      ))}
      <input value={input} onChange={(e) => setInput(e.target.value)} />
    </form>
  );
}
```

## Tools

```ts
import { generateText, tool } from "ai";
import { z } from "zod";
import { aiand } from "./client";

const result = await generateText({
  model: aiand("google/gemma-4-31b-it"),
  tools: {
    getWeather: tool({
      description: "Get current weather",
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => `Sunny in ${city}, 22°C`,
    }),
  },
  prompt: "What's the weather in Tokyo?",
});
```

## Reasoning

Reasoning models accept a level via `providerOptions`. Supported levels per model are in the `reasoning_efforts` field of the [model catalog](https://api.aiand.com/v1/models).

```ts
const { text } = await generateText({
  model: aiand("moonshotai/kimi-k3"),
  prompt: "Solve this step by step: What is 15 * 23?",
  providerOptions: {
    aiand: {
      reasoningEffort: "high",
    },
  },
});
```

<Aside type="tip">
  For React Server Components UI streaming with `useUIState` / `streamUI`, the same `aiand` provider works without changes.
</Aside>
