--- title: AI description: A thin wrapper over the AI SDK, with chat primitives. type: reference related: - /en/docs/examples/ai-chatbot --- # AI `@kreogen/ai` re-exports the [AI SDK](https://ai-sdk.dev) and adds two things: a configured model registry, and the minimum UI needed to render a conversation. It is deliberately thin. There is no chat abstraction of kreogen's own to learn or work around — everything the SDK documents works here unchanged. ## Entry points | Import | Provides | | -------------------------------- | ------------------------------------------------------------------------------------ | | `@kreogen/ai` | Everything from `ai` — `streamText`, `generateObject`, `DefaultChatTransport`, types | | `@kreogen/ai/lib/react` | Everything from `@ai-sdk/react` — `useChat`, `useCompletion` | | `@kreogen/ai/lib/models` | The configured `models` object | | `@kreogen/ai/components/thread` | ``, a scrolling conversation container | | `@kreogen/ai/components/message` | ``, one bubble, markdown-aware | This package is on AI SDK **v6** (`ai@^6`, `@ai-sdk/react@^3`). Guides written for v4 will not work: `useChat` no longer owns the input, and `toDataStreamResponse()` is `toUIMessageStreamResponse()`. The [chatbot example](/en/docs/examples/ai-chatbot#what-changed-since-v4) has the full mapping. ## Models ```ts title="packages/ai/lib/models.ts" const { OPENAI_API_KEY } = keys(); const openai = OPENAI_API_KEY ? createOpenAI({ apiKey: OPENAI_API_KEY }) : undefined; export const models: | { chat: LanguageModel; embeddings: EmbeddingModel; } | undefined = openai ? { chat: openai('gpt-4o-mini'), embeddings: openai.textEmbeddingModel('text-embedding-3-small'), } : undefined; ``` Naming the models in one place is what lets you change provider or tier in one edit rather than across every call site. Swap `@ai-sdk/openai` for another provider package and the rest of the repository is unaffected — the SDK's model interface is the boundary. The type annotation is not decorative. Without it TypeScript infers a type naming the hoisted `@ai-sdk/provider` path, which is not portable across installs and breaks in a way that has nothing obviously to do with models. `models` is `undefined` without `OPENAI_API_KEY`, like every other optional integration here. The guard is not defensive tidiness: the provider constructs happily without a key — it only reads one per request — so before it existed the first model call failed with a `LoadAPIKeyError` at runtime while the type insisted `chat` was always there, leaving a caller no signal that it had to degrade. ## Configuration | Variable | Required | Purpose | | ---------------- | -------- | --------------------- | | `OPENAI_API_KEY` | no | Must start with `sk-` | Without the key, `models` is `undefined` and every AI feature has to say so. Guard with `models?.` at the seam, or answer 503 from the route handler — see the [chatbot example](/en/docs/examples/ai-chatbot). ## Components `` is a flex column that scrolls. `` takes a `UIMessage` and renders it, aligned by role, with the assistant's markdown streamed through [Streamdown](https://streamdown.ai) so partial output does not flicker as syntax completes. ```tsx {messages.map((message) => ( ))} ``` `` reads `message.parts`, filtering to the text ones. In v6 a message is an array of typed parts — text, reasoning, tool calls, files — and `message.content` no longer exists. A component reading it renders nothing rather than failing, which is a slow bug to find. ## Things worth doing per project **Authenticate the route.** A handler that calls a paid model on request is somebody else's bill if it is public. Route handlers are not covered by the page-level auth check. **Rate limit it.** [A limiter](/en/docs/packages/rate-limit) keyed on the user id bounds the damage from a stuck client as much as from an attacker. **Bound the tool loop.** `stopWhen: stepCountIs(n)` on `streamText` caps how many round trips one request may make. Without it, a model that keeps calling tools keeps spending. --- For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md) For an index of all available documentation, see [/llms.txt](/llms.txt)