# Install
# Overview
Install the CLI as a dependency so you can run `peam` in local builds and CI.
## Install
npm
pnpm
yarn
bun
```bash
npm install peam
```
```bash
pnpm add peam
```
```bash
yarn add peam
```
```bash
bun add peam
```
## Usage
The CLI builds a search index from one or more sources and exports it to one or more stores.
### Basic usage
```bash
peam \
--source fileBased --projectDir . --sourceDir .next \
--exclude /admin/** \
--store fileBased --indexPath .peam/index.json
```
### Multiple sources and stores
```bash
peam \
--source fileBased --projectDir . --sourceDir dist \
--source prerender --projectDir . --prerenders ./out/prerender-manifest.json \
--exclude /drafts/** --exclude /private/** \
--store fileBased --indexPath .peam/index.json
```
### Help and discovery
```bash
peam --help
peam --help sources
peam --help stores
peam --version
```
### Notes
* When `--source` or `--store` is omitted, the CLI uses the default `fileBased` type.
* `--exclude` can be repeated to add multiple patterns.
* `--robotsTxt` accepts `true`, `false`, or a file path.
# Sources
# Overview
Sources discover pages or files to index. The CLI supports `fileBased` and `prerender` sources.
## File-based source
Scans a build output directory for HTML files. It matches the [file-based source](/docs/indexing/file-based-source) behavior.
```bash
peam \
--source fileBased --projectDir . --sourceDir dist --glob "**/*.html" \
--store fileBased --indexPath .peam/index.json
```
**Options**
* `projectDir`: project root directory.
* `sourceDir`: directory containing files to index (auto-detected if omitted).
* `glob`: glob pattern for files (default `**/*.{html,htm}`).
## Prerender source
Uses framework prerender manifests. It matches the [prerender source](/docs/indexing/prerender-source) behavior.
```bash
peam \
--source prerender --projectDir . --prerenders ./out/prerender-manifest.json \
--store fileBased --indexPath .peam/index.json
```
**Options**
* `projectDir`: project root directory.
* `prerenders`: array of prerender pages (JSON) or a JSON string.
# Stores
# Overview
Stores define where Peam writes the search index. The CLI currently supports `fileBased` stores.
## File-based store
Writes a JSON index file to disk. It matches the [file-based store](/docs/storage/file-based-store) behavior.
```bash
peam \
--source fileBased --projectDir . --sourceDir dist \
--store fileBased --indexPath .peam/index.json
```
**Options**
* `indexPath`: path to the index file relative to `baseDir` (default `.peam/index.json`).
* `baseDir`: base directory for the index file (defaults to `process.cwd()`).
## Multiple stores
Repeat `--store` to export to multiple destinations:
```bash
peam \
--source fileBased --projectDir . --sourceDir dist \
--store fileBased --indexPath .peam/index.json \
--store fileBased --indexPath public/.peam/index.json
```
# Usage
# Overview
The CLI builds a search index from one or more sources and exports it to one or more stores.
## Basic usage
```bash
peam \
--source fileBased --projectDir . --sourceDir .next \
--exclude /admin/** \
--store fileBased --indexPath .peam/index.json
```
## Multiple sources and stores
```bash
peam \
--source fileBased --projectDir . --sourceDir dist \
--source prerender --projectDir . --prerenders ./out/prerender-manifest.json \
--exclude /drafts/** --exclude /private/** \
--store fileBased --indexPath .peam/index.json
```
## Help and discovery
```bash
peam --help
peam --help sources
peam --help stores
peam --version
```
## Notes
* When `--source` or `--store` is omitted, the CLI uses the default `fileBased` type.
* `--exclude` can be repeated to add multiple patterns.
* `--robotsTxt` accepts `true`, `false`, or a file path.
# AskAI.Chat
import { Preview } from '@/components/preview';
# Overview
Floating chat surface that adapts to device size, sliding up as a bottom sheet on mobile and anchoring as a compact popup on desktop. Pair it with [`AskAI.Trigger`](/docs/components/ask-ai-trigger).
## Demo
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Props
| Prop | Type | Default | Description |
| ---------- | ---------------------- | ----------- | --------------------------------- |
| `children` | `ReactNode` | `undefined` | Custom chat contents. |
| *(all)* | `HTMLDivElement` props | — | Accepts all standard `div` props. |
# AskAI.Dialog
import { Preview } from '@/components/preview';
# Overview
Centered modal surface that shows the AskAI chat UI in a focused overlay. Pair it with [`AskAI.Trigger`](/docs/components/ask-ai-trigger) or another custom control.
## Demo
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Props
| Prop | Type | Default | Description |
| ---------- | ---------------------- | ----------- | --------------------------------- |
| `children` | `ReactNode` | `undefined` | Custom dialog contents. |
| *(all)* | `HTMLDivElement` props | — | Accepts all standard `div` props. |
# AskAI.Header
import { Preview } from '@/components/preview';
# Overview
Header row with the Peam icon, title, and close/clear actions, designed to sit at the top of AskAI surfaces like [`AskAI.Dialog`](/docs/components/ask-ai-dialog), [`AskAI.Chat`](/docs/components/ask-ai-chat), or [`AskAI.Sidepane`](/docs/components/ask-ai-sidepane).
## Demo
## Props
| Prop | Type | Default | Description |
| ------------ | ---------------------- | ---------- | -------------------------------------- |
| `heading` | `ReactNode` | `'Ask AI'` | Heading text or element. |
| `closeLabel` | `string` | `'Close'` | Accessible label for the close action. |
| *(all)* | `HTMLDivElement` props | — | Accepts all standard `div` props. |
# AskAI.Inline
import { Preview } from '@/components/preview';
# Overview
Render the entire chat surface ([`AskAI.Header`](/docs/components/ask-ai-header), [`AskAI.Messages`](/docs/components/ask-ai-messages), [`AskAI.Suggestions`](/docs/components/ask-ai-suggestions), [`AskAI.Input`](/docs/components/ask-ai-input)) inline within any container, without needing a separate dialog or sheet wrapper.
## Demo
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Props
| Prop | Type | Default | Description |
| ------- | ---------------------- | ------- | --------------------------------- |
| *(all)* | `HTMLDivElement` props | — | Accepts all standard `div` props. |
# AskAI.Input
import { Preview } from '@/components/preview';
# Overview
Prompt input area with text entry, optional speech input, and submit actions for sending messages. Often used with [`AskAI.Messages`](/docs/components/ask-ai-messages) and [`AskAI.Suggestions`](/docs/components/ask-ai-suggestions).
## Demo
## Props
| Prop | Type | Default | Description |
| ------- | ---------------------- | ------- | --------------------------------- |
| *(all)* | `HTMLDivElement` props | — | Accepts all standard `div` props. |
# AskAI.Messages
import { Preview } from '@/components/preview';
# Overview
Scrollable message list for the current chat session, including per-message actions and expandable source references. Typically used inside [`AskAI.Dialog`](/docs/components/ask-ai-dialog), [`AskAI.Chat`](/docs/components/ask-ai-chat), or [`AskAI.Sidepane`](/docs/components/ask-ai-sidepane).
## Demo
## Props
| Prop | Type | Default | Description |
| ------- | ---------------------- | ------- | --------------------------------- |
| *(all)* | `HTMLDivElement` props | — | Accepts all standard `div` props. |
# AskAIProvider
# Overview
`AskAIProvider` supplies the AskAI context so you can render [`AskAI`](/docs/components/ask-ai) surfaces or call [`useAskAI`](/docs/components/use-ask-ai) anywhere in your tree without needing a preset surface wrapper.
This is useful when you want global access to chat actions (open, send, clear) while keeping the UI surfaces in a fixed place, such as a layout footer or floating panel.
## Usage
```tsx
import { AskAIProvider, AskAI, useAskAI } from 'peam/client';
function AskAICTA() {
const { sendMessage } = useAskAI();
return (
);
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
## Context reuse
By default, [`AskAI`](/docs/components/ask-ai) will reuse an existing provider when one is present. If you need a local, isolated chat state, set `reuseContext={false}` on a nested `AskAI`.
```tsx
import { AskAIProvider, AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Props
`AskAIProvider` accepts the same props as [`AskAI`](/docs/components/ask-ai), including `endpoint`, `chatTransport`, and `persistence`.
# AskAI.Sidepane
import { Preview } from '@/components/preview';
# Overview
Sidepane surface that slides in from the right on desktop and from the bottom on mobile, giving you a persistent chat surface without blocking the page. Pair it with [`AskAI.Trigger`](/docs/components/ask-ai-trigger).
## Demo
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Notes
* Must be rendered inside `AskAI`.
## Props
| Prop | Type | Default | Description |
| ---------- | ---------------------- | ----------- | --------------------------------- |
| `children` | `ReactNode` | `undefined` | Custom sidepane contents. |
| *(all)* | `HTMLDivElement` props | — | Accepts all standard `div` props. |
# AskAI.Suggestions
import { Preview } from '@/components/preview';
# Overview
Suggested prompt chips rendered above the input to help users start a conversation or explore common tasks. Usually paired with [`AskAI.Input`](/docs/components/ask-ai-input).
## Demo
## Props
| Prop | Type | Default | Description |
| --------------- | -------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------- |
| `prompts` | `string[]` | `['Summarize this page', 'Where should I get started?', 'What can you help me with?']` | Suggested prompts. |
| `onPromptClick` | `(prompt: string) => void` | `undefined` | Handle prompt clicks. |
| `onlyWhenEmpty` | `boolean` | `true` | Render only when there are no messages. |
# AskAI.Trigger
import { Preview } from '@/components/preview';
# Overview
Button-like trigger that toggles the open state for [`AskAI.Dialog`](/docs/components/ask-ai-dialog), [`AskAI.Chat`](/docs/components/ask-ai-chat), or [`AskAI.Sidepane`](/docs/components/ask-ai-sidepane). Use the default floating Peam button or pass custom content (including `asChild`) to integrate with your UI.
## Demo
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Props
| Prop | Type | Default | Description |
| -------------- | ----------------------- | ------------- | ----------------------------------------------------- |
| `asChild` | `boolean` | `false` | Use a custom element via Radix `Slot`. |
| `children` | `ReactNode` | `undefined` | Custom trigger contents. |
| `inlineButton` | `boolean` | `false` | Renders the default button without fixed positioning. |
| `variant` | `'icon' \| 'iconLabel'` | `'iconLabel'` | Default Peam button style. |
# AskAI
import { Preview } from '@/components/preview';
# Overview
The top-level component that wires context, transport, and persistence, then renders the default preset ([`AskAI.Trigger`](/docs/components/ask-ai-trigger) + [`AskAI.Dialog`](/docs/components/ask-ai-dialog)). Use it as-is for a drop-in chat UI or compose the underlying pieces for custom layouts.
Here is a basic example using the default [`AskAI.Trigger`](/docs/components/ask-ai-trigger) and [`AskAI.Dialog`](/docs/components/ask-ai-dialog):
```tsx
```
## Anatomy
* `AskAI` provides state, transport, and context.
* [`AskAI.Trigger`](/docs/components/ask-ai-trigger) toggles open state (defaults to the floating Peam button).
* [`AskAI.Dialog`](/docs/components/ask-ai-dialog), [`AskAI.Chat`](/docs/components/ask-ai-chat), or [`AskAI.Sidepane`](/docs/components/ask-ai-sidepane) render the surface.
* [`AskAI.Header`](/docs/components/ask-ai-header), [`AskAI.Messages`](/docs/components/ask-ai-messages), [`AskAI.Suggestions`](/docs/components/ask-ai-suggestions), and [`AskAI.Input`](/docs/components/ask-ai-input) are layout primitives.
* [`AskAI.Inline`](/docs/components/ask-ai-inline) renders the full inline surface without another wrapper.
```tsx
```
## Overview
## Props
| Prop | Type | Default | Description |
| --------------- | ------------------------------ | ------------- | ---------------------------------------------------------- |
| `endpoint` | `string` | `'/api/peam'` | API endpoint used by the chat transport. |
| `open` | `boolean` | `undefined` | Controlled open state. |
| `defaultOpen` | `boolean` | `false` | Uncontrolled initial open state. |
| `chatTransport` | `HttpChatTransport` | `undefined` | Override the default transport. |
| `persistence` | `boolean \| { key?: string }` | `true` | Configure chat persistence or disable it. |
| `reuseContext` | `boolean` | `true` | Reuse an existing `AskAIContext` if present. |
| `children` | `ReactNode` | `undefined` | Custom composition (if omitted, renders Trigger + Dialog). |
| `className` | `string` | `undefined` | Class name applied to the root wrapper. |
# Components
# Overview
Peam is built from small, composable pieces. You can use the preset [`AskAI`](/docs/components/ask-ai) for a complete chat UI, or compose individual surfaces like [`AskAI.Dialog`](/docs/components/ask-ai-dialog) and [`AskAI.Chat`](/docs/components/ask-ai-chat) with a [`AskAI.Trigger`](/docs/components/ask-ai-trigger).
To customize the visuals, adjust theme tokens on the root using the [Styling](/docs/components/styling) guide. If you need to control chat actions from anywhere in your app, use [`useAskAI`](/docs/components/use-ask-ai) or the [`AskAIProvider`](/docs/components/ask-ai-provider).
Start with [`AskAI`](/docs/components/ask-ai) for the quickest setup, then explore surfaces and primitives to tailor the experience.
# Styling
import { Preview } from '@/components/preview';
# Overview
Peam client components are styled with shadcn/ui primitives and theme tokens. You can customize the look and feel by overriding the CSS variables listed below on any wrapper element (for example, the `AskAI` root). For dark mode, apply a `dark` class on the same element (or a parent) and override the variables again for the dark palette.
## CSS variables
| Variable | Purpose |
| -------------------------- | ----------------------------------- |
| `--background` | Base surface background. |
| `--foreground` | Base surface text color. |
| `--card` | Card/surface background for panels. |
| `--card-foreground` | Text color on cards/surfaces. |
| `--popover` | Popover surface background. |
| `--popover-foreground` | Text color on popovers. |
| `--primary` | Primary brand/action color. |
| `--primary-foreground` | Text color on primary elements. |
| `--secondary` | Secondary surface/background color. |
| `--secondary-foreground` | Text color on secondary surfaces. |
| `--muted` | Muted surface/background color. |
| `--muted-foreground` | Text color on muted surfaces. |
| `--accent` | Accent surface/background color. |
| `--accent-foreground` | Text color on accent surfaces. |
| `--destructive` | Destructive/action color. |
| `--destructive-foreground` | Text color on destructive elements. |
| `--border` | Border color. |
| `--input` | Input border color. |
| `--ring` | Focus ring color. |
| `--radius` | Base border radius. |
## Dark mode
To customize the dark theme, add a `dark` class and override the same variables for the dark palette. The preview below uses `.peam-root` for the light theme and `.peam-root.dark` for the dark theme.
## Demo
# useAskAI
import { Preview } from '@/components/preview';
# Overview
Access and control the AskAI state from any component inside [`AskAI`](/docs/components/ask-ai), including surfaces like [`AskAI.Dialog`](/docs/components/ask-ai-dialog) or [`AskAI.Chat`](/docs/components/ask-ai-chat).
## Demo
This demo uses `useAskAI` to open [`AskAI.Dialog`](/docs/components/ask-ai-dialog) and send a predefined question when the button is clicked.
## Example
```tsx
import { AskAI, useAskAI } from 'peam/client';
function UseAskAIButton() {
const { sendMessage } = useAskAI();
return (
);
}
export default function Page() {
return (
);
}
```
## Example with [`AskAIProvider`](/docs/components/ask-ai-provider)
Use [`AskAIProvider`](/docs/components/ask-ai-provider) in a layout and call `useAskAI` from a page or nested component, then render [`AskAI`](/docs/components/ask-ai) to provide the surfaces (like [`AskAI.Trigger`](/docs/components/ask-ai-trigger) and [`AskAI.Dialog`](/docs/components/ask-ai-dialog)).
```tsx
// app/layout.tsx
import { AskAIProvider, AskAI } from 'peam/client';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
{children}
);
}
```
```tsx
// app/page.tsx
import { useAskAI } from 'peam/client';
function AskAICTA() {
const { sendMessage } = useAskAI();
return (
);
}
export default function Page() {
return (
);
}
```
## Returns
| Value | Type | Description |
| --------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------- |
| `open` | `boolean` | Current open state. |
| `setOpen` | `(open: boolean) => void` | Set open state. |
| `toggleOpen` | `() => void` | Toggle open state. |
| `input` | `string` | Current input text. |
| `setInput` | `(value: string, options?: { open?: boolean }) => void` | Update input text and optionally open the surface. |
| `messages` | `UIMessage[]` | Current chat messages. |
| `status` | `ChatStatus` | Streaming status. |
| `error` | `Error \| undefined` | Transport or streaming error. |
| `isLoading` | `boolean` | Loading persisted history. |
| `sendMessage` | `({ text: string }, options?: { open?: boolean }) => void` | Send a message and optionally open the surface. |
| `handleSubmit` | `(message: PromptInputMessage) => void` | Submit handler used by [`AskAI.Input`](/docs/components/ask-ai-input). |
| `regenerate` | `(options?: { messageId?: string }) => void` | Regenerate a response. |
| `clearMessages` | `() => void \| Promise` | Clear message history. |
# Architecture
# Overview
Peam is organized around a build-time pipeline and a runtime query stack. The builder ingests your content through [index sources](/docs/indexing/sources), applies [filters](/docs/indexing/filters), and produces index artifacts that are written to [index stores](/docs/storage). At runtime, the search engine loads those artifacts to serve AI-powered answers to your client.
## High-level architecture
Server
Server --> SearchEngine
SearchEngine --> Retriever
Retriever --> LLM
LLM --> Client
end
subgraph Build_Time[Build-time]
direction TB
Sources[Docs, files, APIs]
IndexSources[Index sources]
Filters[Filters]
Indexers[Indexers]
Stores[Index stores]
Sources --> IndexSources
IndexSources --> Filters
Filters --> Indexers
Indexers --> Stores
end
`}
/>
## What each part does
* **Index sources** discover pages or content to index. See [Index sources](/docs/indexing/sources).
* **Filters** prune or refine candidates before indexing. See [Filters](/docs/indexing/filters).
* **Peam Builder** orchestrates ingestion, normalization, and indexing.
* **Indexers** parse and structure content, producing embeddings, metadata, and search artifacts.
* **Index stores** persist index artifacts for runtime search. See [Storage](/docs/storage).
* **Search engine** loads stored artifacts and powers retrieval.
* **Runtime server** handles user queries, retrieval, and answer composition.
* **Client** renders [AskAI](/docs/components/ask-ai) surfaces and sends messages to the runtime API.
Next, see [Data flow](/docs/core-concepts/data-flow) to understand how a single query moves through the system.
# Data flow
# Overview
Peam uses a two-phase workflow: [indexing](/docs/indexing) at build time and querying at runtime. The builder creates artifacts once; the runtime reuses them for fast, consistent answers.
## Indexing flow
>Source: Discover pages
Source->>Filters: Apply filters
Filters-->>Indexer: Filtered pages
Filters->>Indexer: Normalize + chunk
Indexer->>Indexer: Enrich metadata
Indexer->>Store: Write index
`}
/>
## Query flow
>Server: sendMessage()
Server->>Search: search(query)
Search->>Store: Load index artifacts
Store-->>Search: Index data
Search->>Retriever: Retrieve + rerank
Retriever-->>Server: Ranked context
Server->>LLM: Compose answer
LLM-->>Client: Streamed response
`}
/>
## Notes
* Index sources define where content is discovered from. See [Index sources](/docs/indexing/sources).
* Filters narrow the candidate set before indexing. See [Filters](/docs/indexing/filters).
* Index stores persist artifacts for runtime search. See [Storage](/docs/storage).
* Client surfaces stay thin: they render [AskAI](/docs/components/ask-ai) UI and send messages to the server.
# Core concepts
# Overview
This section explains how Peam is structured and how data moves through the platform. Start with the [architecture overview](/docs/core-concepts/architecture), then dive into the [data flow](/docs/core-concepts/data-flow) to understand [indexing](/docs/indexing), retrieval, and runtime answers.
## Continue with
# Astro
import { Step, Steps } from 'fumadocs-ui/components/steps';
import Link from 'fumadocs-core/link';
# Overview
Use Astro with React islands to render AskAI where you need it, without turning the whole site into React.
The API route lives in `src/pages/api/peam.ts`, keeping server logic clean and scoped.
You get fast pages with a focused, AI-ready experience.
## Install Dependencies
npm
pnpm
yarn
bun
```bash
npm install peam
```
```bash
pnpm add peam
```
```bash
yarn add peam
```
```bash
bun add peam
```
## Add AskAI in the Base Layout
```astro
---
import { AskAI } from 'peam/client';
import '../styles/global.css';
interface Props {
title: string;
description?: string;
}
const { title, description } = Astro.props as Props;
---
```
## Add the API Route
Create `src/pages/api/peam.ts` and add:
```ts
// [!code highlight]
export { POST } from 'peam/server';
export const prerender = false;
```
## Export the OpenAI API Key
Peam uses ChatGPT (OpenAI GPT-4o) by default, so `OPENAI_API_KEY` must be set in your environment.
```bash
export OPENAI_API_KEY="your-api-key"
```
## Examples
Browse the full app in:
*
examples/astro-react
See the [API Reference](/docs/api-reference) for all available options.
## Customize the model
Define your own handler and pass a model to `createChat`. Peam uses the Vercel AI SDK, so import models from the provider package you choose. See the providers and models guide.
```ts
import { createChat } from 'peam/server';
import { openai } from '@ai-sdk/openai';
export const POST = createChat({
model: openai('gpt-4o-mini'),
}).handler;
```
# Getting Started
import { Card, Cards } from 'fumadocs-ui/components/card';
import {
AstroDark,
AstroLight,
Next,
NuxtGray,
ReactRouterDark,
ReactRouterLight,
Vite,
} from '@/app/(home)/components/frameworks';
import Link from 'fumadocs-core/link';
import { Globe } from 'lucide-react';
# Frameworks
Pick a guide below, or browse the full examples folder on GitHub: examples.
Next.js
Vite
Astro
React Router
Any website
Coming soon
Nuxt
Coming soon
# Next.js
import { Step, Steps } from 'fumadocs-ui/components/steps';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import Link from 'fumadocs-core/link';
# Overview
Connect Peam to your Next.js app by adding the AskAI client and a single API route.
Wrap your config with `withPeam()` so the build adapter can generate the search index.
You’ll be running in minutes with a clean, production-ready setup.
## Setup
## Install Dependencies
npm
pnpm
yarn
bun
```bash
npm install peam @peam-ai/next
```
```bash
pnpm add peam @peam-ai/next
```
```bash
yarn add peam @peam-ai/next
```
```bash
bun add peam @peam-ai/next
```
## Add AskAI to the Root Layout
```tsx
import type { Metadata } from 'next';
import { AskAI } from 'peam/client';
import './globals.css';
export const metadata: Metadata = {
title: 'Next.js Example - Peam',
description: 'A simple Next.js example application with Peam',
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
{/* ... */}
{children}
// [!code highlight]
);
}
```
## Add the Peam API Route
Create `app/api/peam/route.ts` in your project and add:
```ts
export const maxDuration = 30;
// [!code highlight]
export { POST } from '@peam-ai/next/route';
```
## Wrap Next.js Config with withPeam
```ts
import withPeam from '@peam-ai/next';
const nextConfig = {
/* config options here */
};
export default withPeam()(nextConfig);
```
## Export the OpenAI API Key
Peam uses ChatGPT (OpenAI GPT-4o) by default, so `OPENAI_API_KEY` must be set in your environment.
```bash
export OPENAI_API_KEY="your-api-key"
```
Next.js 14 or older
Next.js 15+ uses the build adapter configured by `withPeam()`. For Next.js 14 (or older), add a `postbuild` script
that runs `peam` to generate the index after `next build`.
## Examples
Browse the example apps for a complete setup:
*
examples/nextjs-14
*
examples/nextjs-15
*
examples/nextjs-16
See the [API Reference](/docs/api-reference) for all available options.
## Customize the model
If you want a different model, define your own route handler and pass a model to `createChat`. Peam uses the Vercel AI SDK, so import models from the provider package you choose. See the providers and models guide.
```ts
import { createChat } from '@peam-ai/next/route';
import { openai } from '@ai-sdk/openai';
export const POST = createChat({
model: openai('gpt-4o-mini'),
}).handler;
```
# React Router
import { Step, Steps } from 'fumadocs-ui/components/steps';
import Link from 'fumadocs-core/link';
# Overview
Mount `AskAI` in your root route and expose a simple `/api/peam` action endpoint.
Peam streams responses from the route action while keeping your React Router app fully client-driven.
## Install Dependencies
npm
pnpm
yarn
bun
```bash
npm install peam
```
```bash
pnpm add peam
```
```bash
yarn add peam
```
```bash
bun add peam
```
## Add AskAI to the Root Route
In `app/root.tsx`:
```tsx
import { AskAI } from 'peam/client';
export default function App() {
return (
{/* ... */}
// [!code highlight]
);
}
```
## Add the Peam API Route
Create `app/routes/api.peam.ts` and add:
```ts
import { createChat } from 'peam/server';
const handler = createChat().handler;
export async function action({ request }: { request: Request }) {
return handler(request);
}
export function loader() {
return new Response(JSON.stringify({ error: 'Method not allowed' }), {
status: 405,
headers: { 'Content-Type': 'application/json' },
});
}
```
## Export the OpenAI API Key
Peam uses ChatGPT (OpenAI GPT-4o) by default, so `OPENAI_API_KEY` must be set in your environment.
```bash
export OPENAI_API_KEY="your-api-key"
```
## Examples
Browse the full app in:
*
examples/react-router
See the [API Reference](/docs/api-reference) for all available options.
## Customize the model
Pass a custom model into `createChat`. Peam uses the Vercel AI SDK, so import models from the provider package you choose. See the providers and models guide.
```ts
import { createChat } from 'peam/server';
import { openai } from '@ai-sdk/openai';
const handler = createChat({
model: openai('gpt-4o-mini'),
}).handler;
```
# Vite
import { Step, Steps } from 'fumadocs-ui/components/steps';
import Link from 'fumadocs-core/link';
# Overview
Add Peam to a Vite + React SSR app by mounting AskAI in your layout and wiring a simple API route.
Your server calls `createChat()` per request, while the client streams results from `/api/peam`.
It’s a lightweight setup that keeps your stack fast and flexible.
## Install Dependencies
npm
pnpm
yarn
bun
```bash
npm install peam
```
```bash
pnpm add peam
```
```bash
yarn add peam
```
```bash
bun add peam
```
## Add AskAI to the Root Layout
```tsx
export { Layout };
import { AskAI } from 'peam/client';
import type { ReactNode } from 'react';
import '../index.css';
function Layout({ children }: { children: ReactNode }) {
return (
{/* ... */}
{/* ... */}
{children}
// [!code highlight]
);
}
```
## Add an API Endpoint in the Express Server
The server handler is built with `createChat()` from `peam/server` and invoked per request.
Create or edit `api/server.ts` and add the /api/peam route:
```ts
import { createChat } from 'peam/server';
// ... Express setup
const chat = createChat();
// [!code highlight]
app.post('/api/peam', async (req, res) => {
// ... convert to Request
const response = await chat.handler(request);
// ... stream response
});
// ... server startup
```
## Export the OpenAI API Key
Peam uses ChatGPT (OpenAI GPT-4o) by default, so `OPENAI_API_KEY` must be set in your environment.
```bash
export OPENAI_API_KEY="your-api-key"
```
## Examples
Browse the full app in:
*
examples/vite-react
See the [API Reference](/docs/api-reference) for all available options.
## Customize the model
Pass a custom model to `createChat` when wiring your route. Peam uses the Vercel AI SDK, so import models from the provider package you choose. See the providers and models guide.
```ts
import { createChat } from 'peam/server';
import { openai } from '@ai-sdk/openai';
const chat = createChat({
model: openai('gpt-4o-mini'),
});
```
# File-based source
# Overview
The file-based source scans a build output directory for HTML files and turns them into page candidates. It is the default source for most static or pre-rendered sites. See [Index sources](/docs/indexing/sources) for how sources fit into the pipeline.
## Options (CLI)
| Option | Type | Default | Description |
| ------------ | -------- | ----------------- | ------------------------------- |
| `projectDir` | `string` | `process.cwd()` | Project root directory. |
| `sourceDir` | `string` | auto-detected | Build output directory to scan. |
| `glob` | `string` | `**/*.{html,htm}` | Glob pattern for HTML files. |
## Example (CLI)
```bash
peam \
--source fileBased --projectDir . --sourceDir dist --glob "**/*.html" \
--store fileBased --indexPath .peam/index.json
```
## Notes
* The source auto-detects output folders like `.next`, `dist`, and `build` if `sourceDir` is not provided.
* It normalizes paths by stripping common build prefixes and removing `.html` / `/index`.
# Filters
# Overview
Filters receive a list of page candidates and return a refined list. They run after [sources](/docs/indexing/sources) and before indexing. Configure them via [withPeam](/docs/api-reference/next/with-peam) or the CLI.
## Built-in filters
* **CommonFilter**: removes common framework error pages and internal routes.
* **PrerenderPathFilter**: normalizes prerender paths (used when prerender sources are present).
* **ExcludePatternFilter**: drops paths matching user-provided patterns.
* **RobotsTxtFilter**: applies rules from `robots.txt` (optional, auto-discovered or custom path).
## Configuration (Next.js)
```ts
// next.config.ts
import withPeam from '@peam-ai/next';
export default withPeam({
exclude: ['/private', '/drafts/**'],
robotsTxt: true,
})();
```
When `robotsTxt` is `true`, Peam automatically discovers `robots.txt` in your project. To use a custom location, pass a path instead. See [`robotsTxt`](/docs/api-reference/next/with-peam#robotstxt).
## Configuration (CLI)
```bash
peam \
--source fileBased --projectDir . --sourceDir dist \
--exclude /private --exclude /drafts/** \
--robotsTxt true \
--store fileBased --indexPath .peam/index.json
```
## Notes
* `exclude` accepts a string or array, and supports glob-style patterns.
* `robotsTxt` can be `true` for auto-discovery or a custom file path (for example, `--robotsTxt ./public/robots.txt`). See [`robotsTxt`](/docs/api-reference/next/with-peam#robotstxt).
* If a prerender source is used, the prerender filter is added automatically.
# Indexing
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
# Overview
Indexing is the build-time pipeline that turns your content into searchable artifacts. Most teams configure it via [withPeam](/docs/api-reference/next/with-peam) (Next.js) or the **Peam CLI**. The builder and indexers are managed for you.
Next.js 14 or older
Automatic prerender discovery only works on Next.js 15+. For Next.js 14 (or older), add a `postbuild` script that
runs `peam` to discover pages after `next build`. See the [Next.js getting started
guide](/docs/getting-started/next).
## Quick start (Next.js)
```ts
// next.config.ts
import withPeam from '@peam-ai/next';
export default withPeam({
exclude: ['/admin/**', '/private/**'],
robotsTxt: true,
})();
```
## Quick start (CLI)
```bash
peam \
--source fileBased --projectDir . --sourceDir .next \
--exclude /admin/** \
--store fileBased --indexPath .peam/index.json
```
## How indexing runs
At build time, Peam discovers pages via [index sources](/docs/indexing/sources), applies [filters](/docs/indexing/filters), then uses the indexing pipeline to produce artifacts that are exported to your configured [store](/docs/storage).
## Continue with
# Prerender source
# Overview
The prerender source indexes framework-provided prerender manifests. It expects an array of prerendered pages (or a JSON string) and maps them to file paths.
It is primarily used by [withPeam](/docs/api-reference/next/with-peam) on Next.js 15+ for automatic page discovery, but you can also supply it directly via the CLI.
## Options (CLI)
| Option | Type | Default | Description |
| ------------ | --------------------------- | ------- | ------------------------------------ |
| `prerenders` | `PrerenderPage[] \| string` | — | Prerender pages list or JSON string. |
| `projectDir` | `string` | — | Project root directory. |
## Example (CLI)
```bash
peam \
--source prerender --projectDir . --prerenders ./out/prerender-manifest.json \
--store fileBased --indexPath .peam/index.json
```
## Notes
* If a fallback file path ends with `/.html` (Next.js 15), it is normalized to `/index.html`.
* Paths are derived from prerender entries rather than filesystem scanning.
# Index sources
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
# Overview
Index sources discover pages or files that should be indexed. Each source produces a list of page candidates (path + file path) that the pipeline can [filter](/docs/indexing/filters) and index.
Peam ships with two sources today:
* [File-based source](/docs/indexing/file-based-source)
* [Prerender source](/docs/indexing/prerender-source)
The prerender source is primarily used by [withPeam](/docs/api-reference/next/with-peam) on Next.js 15+ to auto-discover pages, but it can also be selected manually via the CLI.
Next.js 14 or older
Automatic prerender discovery only works on Next.js 15+. For Next.js 14 (or older), add a `postbuild` script that
runs `peam` to discover pages after `next build`. See the [Next.js getting started
guide](/docs/getting-started/next).
## Configure sources
### Next.js (withPeam)
When using Next.js, [withPeam](/docs/api-reference/next/with-peam) auto-detects build output and wires the default source for you. You typically only set [filters](/docs/indexing/filters) and [storage](/docs/storage) options:
```ts
// next.config.ts
import withPeam from '@peam-ai/next';
export default withPeam({
exclude: ['/admin/**'],
robotsTxt: true,
})();
```
### CLI
Use the CLI to explicitly choose and configure sources:
```bash
peam \
--source fileBased --projectDir . --sourceDir dist --glob "**/*.html" \
--source prerender --projectDir . --prerenders ./out/prerender-manifest.json \
--store fileBased --indexPath .peam/index.json
```
# File-based store
# Overview
The file-based store reads and writes index data to a JSON file on disk. Configure it through [withPeam](/docs/api-reference/next/with-peam) or the CLI. See [Storage](/docs/storage) for selecting a store.
## Options (withPeam)
| Option | Type | Default | Description |
| ----------- | -------- | ------------------ | ---------------------------------------------- |
| `baseDir` | `string` | `process.cwd()` | Base directory for the index file. |
| `indexPath` | `string` | `.peam/index.json` | Path to the index file, relative to `baseDir`. |
## Example (Next.js)
```ts
// next.config.ts
import withPeam from '@peam-ai/next';
export default withPeam({
searchStore: {
type: 'fileBased',
config: { indexPath: '.peam/index.json' },
},
})();
```
## Example (CLI)
```bash
peam \
--source fileBased --projectDir . --sourceDir dist \
--store fileBased --indexPath .peam/index.json
```
# Storage
# Overview
Storage is where index artifacts are persisted and later loaded at runtime. Configure it through [withPeam](/docs/api-reference/next/with-peam) (Next.js) or the **Peam CLI**.
## Selecting a store
Index stores define where Peam writes index artifacts and where the runtime reads them back. Use [`searchStore`](/docs/api-reference/next/with-peam#searchstore) (Next.js) or `--store` (CLI) to choose an implementation.
## Quick start (Next.js)
```ts
// next.config.ts
import withPeam from '@peam-ai/next';
export default withPeam({
searchStore: {
type: 'fileBased',
config: { indexPath: '.peam/index.json' },
},
})();
```
## Quick start (CLI)
```bash
peam \
--source fileBased --projectDir . --sourceDir dist \
--store fileBased --indexPath .peam/index.json
```
## Notes
* `withPeam` uses a single [`searchStore`](/docs/api-reference/next/with-peam#searchstore) configuration at build time and runtime.
* The CLI can export to multiple stores by repeating `--store` with different configs.
## Continue with
# AskAI.Chat
# Overview
Popup chat surface that floats over the page. Pair it with [AskAI.Trigger](/docs/api-reference/client/ask-ai-trigger).
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Notes
* `AskAI.Chat` must be rendered inside [AskAI](/docs/api-reference/client/ask-ai).
* For inline rendering, use [AskAI.Inline](/docs/api-reference/client/ask-ai-inline).
# AskAI.Dialog
# Overview
Modal dialog chat surface. Pair it with [AskAI.Trigger](/docs/api-reference/client/ask-ai-trigger).
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Notes
* `AskAI.Dialog` must be rendered inside [AskAI](/docs/api-reference/client/ask-ai).
* For inline rendering inside the dialog, use [AskAI.Inline](/docs/api-reference/client/ask-ai-inline).
# AskAI.Header
# Overview
Header bar that shows the AskAI title and actions (clear conversation, close). Typically used with [AskAI.Messages](/docs/api-reference/client/ask-ai-messages) and [AskAI.Input](/docs/api-reference/client/ask-ai-input).
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Props
```ts
import type { ComponentPropsWithoutRef, ReactNode } from 'react';
export interface AskAIHeaderProps extends ComponentPropsWithoutRef<'div'> {
heading?: ReactNode;
closeLabel?: string;
}
```
## Notes
* Uses `closeLabel` for the close button aria-label.
* The delete button only shows when there are messages and the chat is idle.
# AskAI.Inline
# Overview
Inline layout that composes [AskAI.Header](/docs/api-reference/client/ask-ai-header), [AskAI.Messages](/docs/api-reference/client/ask-ai-messages), [AskAI.Suggestions](/docs/api-reference/client/ask-ai-suggestions), and [AskAI.Input](/docs/api-reference/client/ask-ai-input).
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Notes
* Must be rendered inside [AskAI](/docs/api-reference/client/ask-ai) or [AskAIProvider](/docs/api-reference/client/ask-ai-provider).
# AskAI.Input
# Overview
Text input for sending prompts, including optional speech input and submit button. It binds to [useAskAI](/docs/api-reference/client/use-ask-ai).
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Notes
* Binds to the AskAI context `input` and `handleSubmit`.
* Disables submit when the input is empty and not streaming.
# AskAI.Messages
# Overview
Message list that renders conversation history, sources, and retry/copy actions. Often paired with [AskAI.Header](/docs/api-reference/client/ask-ai-header) and [AskAI.Input](/docs/api-reference/client/ask-ai-input).
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Notes
* Renders sources when `source-url` parts are present in the assistant response.
* Shows a retry action when the response fails.
# AskAIProvider
# Overview
Provides AskAI context for composing surfaces manually or sharing context across multiple surfaces. Pair it with [AskAI.Trigger](/docs/api-reference/client/ask-ai-trigger) and a surface like [AskAI.Sidepane](/docs/api-reference/client/ask-ai-sidepane).
## Type Definition
```ts
import type { ReactNode } from 'react';
import type { HttpChatTransport, UIMessage } from 'ai';
export type AskAIProviderProps = {
children?: ReactNode;
endpoint?: string;
open?: boolean;
defaultOpen?: boolean;
chatTransport?: HttpChatTransport;
persistence?: boolean | { key?: string };
};
```
## Usage
```tsx
import { AskAI, AskAIProvider } from 'peam/client';
export default function Page() {
return (
);
}
```
## Notes
* `AskAIProvider` does not render UI. It only supplies context.
* [AskAI.Trigger](/docs/api-reference/client/ask-ai-trigger), [AskAI.Dialog](/docs/api-reference/client/ask-ai-dialog), [AskAI.Chat](/docs/api-reference/client/ask-ai-chat), [AskAI.Sidepane](/docs/api-reference/client/ask-ai-sidepane), and [AskAI.Inline](/docs/api-reference/client/ask-ai-inline) all rely on this context.
# AskAI.Sidepane
# Overview
Sidepane chat surface that slides in from the right on desktop and from the bottom on mobile. Pair it with [AskAI.Trigger](/docs/api-reference/client/ask-ai-trigger).
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Notes
* `AskAI.Sidepane` must be rendered inside [AskAI](/docs/api-reference/client/ask-ai).
* For inline rendering inside the sidepane, use [AskAI.Inline](/docs/api-reference/client/ask-ai-inline).
# AskAI.Suggestions
# Overview
Renders a horizontal list of prompt suggestions that can be clicked to send a message. The suggestions call into [useAskAI](/docs/api-reference/client/use-ask-ai).
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Props
```ts
import type { ComponentPropsWithoutRef } from 'react';
export interface AskAISuggestionsProps extends ComponentPropsWithoutRef<'div'> {
prompts?: string[];
onPromptClick?: (prompt: string) => void;
onlyWhenEmpty?: boolean;
}
```
## Notes
* Defaults to a small set of common prompts.
* When `onlyWhenEmpty` is `true`, it renders only if there are no messages.
# AskAI.Trigger
# Overview
Trigger button for opening and closing AskAI surfaces. Use it with [AskAI.Dialog](/docs/api-reference/client/ask-ai-dialog), [AskAI.Chat](/docs/api-reference/client/ask-ai-chat), or [AskAI.Sidepane](/docs/api-reference/client/ask-ai-sidepane).
## Usage
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Props
```ts
import type { ComponentPropsWithoutRef, ReactNode } from 'react';
import type { PeamButtonVariant } from 'peam/client';
export interface AskAITriggerProps extends ComponentPropsWithoutRef<'button'> {
asChild?: boolean;
children?: ReactNode;
inlineButton?: boolean;
variant?: PeamButtonVariant;
}
```
## Notes
* If `children` is not provided, a default floating button is rendered.
* When `asChild` is `true`, the trigger renders a Radix `Slot`.
* Must be rendered inside [AskAI](/docs/api-reference/client/ask-ai) or [AskAIProvider](/docs/api-reference/client/ask-ai-provider).
# AskAI
# Overview
Composable AI chat primitives with a simple preset and full control through composition. Combine [AskAI.Trigger](/docs/api-reference/client/ask-ai-trigger) with surfaces like [AskAI.Dialog](/docs/api-reference/client/ask-ai-dialog), [AskAI.Chat](/docs/api-reference/client/ask-ai-chat), or [AskAI.Sidepane](/docs/api-reference/client/ask-ai-sidepane), or render an [AskAI.Inline](/docs/api-reference/client/ask-ai-inline) layout.
## Type Definition
```ts
import type { ReactNode } from 'react';
import type { HttpChatTransport, UIMessage } from 'ai';
export interface AskAIProps {
children?: ReactNode;
className?: string;
endpoint?: string;
open?: boolean;
defaultOpen?: boolean;
chatTransport?: HttpChatTransport;
persistence?: boolean | { key?: string };
reuseContext?: boolean;
}
```
## Usage
### Preset (Trigger + Dialog)
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return ;
}
```
### Sidepane Composition
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
### Custom Trigger (asChild)
```tsx
import { AskAI } from 'peam/client';
import { Button } from '@/components/ui/button';
export default function Page() {
return (
);
}
```
### Inline Chat
```tsx
import { AskAI } from 'peam/client';
export default function Page() {
return (
);
}
```
## Props
### AskAI (Preset)
| Prop | Type | Default | Description |
| --------------- | ------------------------------ | ------------- | ------------------------------------------- |
| `endpoint` | `string` | `'/api/peam'` | API endpoint used by the chat transport |
| `open` | `boolean` | `undefined` | Controlled open state |
| `defaultOpen` | `boolean` | `false` | Uncontrolled initial open state |
| `chatTransport` | `HttpChatTransport` | `undefined` | Override the default chat transport |
| `persistence` | `boolean \| { key?: string }` | `true` | Configure chat persistence or disable it |
| `reuseContext` | `boolean` | `true` | Reuse an existing `AskAIContext` if present |
| `className` | `string` | `undefined` | Root wrapper class name |
## Notes
* `AskAI` renders a preset: `AskAI.Trigger` + `AskAI.Dialog`.
* For other layouts, compose `AskAI` with the surface you want, or provide context manually with [AskAIProvider](/docs/api-reference/client/ask-ai-provider).
* Use [useAskAI](/docs/api-reference/client/use-ask-ai) for direct access to state and actions, and [BoundedChatTransport](/docs/api-reference/client/bounded-chat-transport) to override transport behavior.
# BoundedChatTransport
# Overview
A custom chat transport that sends only recent messages since the last summary, keeping the context window bounded. It pairs with [AskAI](/docs/api-reference/client/ask-ai) via the `chatTransport` prop.
## Type Definition
```ts
import type { UIMessage } from 'ai';
import { DefaultChatTransport } from 'ai';
export interface BoundedChatTransportOptions {
endpoint: string;
}
export declare class BoundedChatTransport extends DefaultChatTransport {
constructor(options: BoundedChatTransportOptions);
}
```
## Usage
```tsx
import { AskAI, BoundedChatTransport } from 'peam/client';
export default function Page() {
return (
);
}
```
## Notes
* This is the default transport used by [AskAI](/docs/api-reference/client/ask-ai) when `chatTransport` is not provided.
* It reads the summary markers in the request body to limit the message window.
# useAskAI
# Overview
Accesses the AskAI context for state, messages, and actions. Use it inside [AskAI](/docs/api-reference/client/ask-ai) or [AskAIProvider](/docs/api-reference/client/ask-ai-provider).
## Type Definition
```ts
import type { ChatStatus, UIMessage } from 'ai';
export type AskAIActionOptions = {
open?: boolean;
};
export interface AskAIContextValue {
open: boolean;
setOpen: (open: boolean) => void;
toggleOpen: () => void;
input: string;
setInput: (value: string, options?: AskAIActionOptions) => void;
messages: UIMessage[];
status: ChatStatus;
error: Error | undefined;
isLoading: boolean;
sendMessage: (message: { text: string }, options?: AskAIActionOptions) => void;
handleSubmit: (message: { text?: string }) => void;
regenerate: (options?: { messageId?: string }) => void;
clearMessages: () => void | Promise;
}
export declare function useAskAI(): AskAIContextValue;
```
## Usage
```tsx
import { useAskAI } from 'peam/client';
export function AskButton() {
const { sendMessage, setOpen } = useAskAI();
return (
);
}
```
## Notes
* `setInput` and `sendMessage` accept `options.open` (default `true`) to control whether the UI opens.
* The hook must be used inside [AskAI](/docs/api-reference/client/ask-ai) or [AskAIProvider](/docs/api-reference/client/ask-ai-provider).
# createChat
# Overview
Creates a chat runtime that automatically injects the `searchIndexStore` resolved by [withPeam](/docs/api-reference/next/with-peam). It pairs with the [route handler](/docs/api-reference/next/route) export.
## Type Definition
```ts
import type { ChatRuntimeOptions, DefaultChatRuntime } from 'peam/server';
export declare function createChat(options?: ChatRuntimeOptions): DefaultChatRuntime;
```
## Usage
```ts
import { createChat } from '@peam-ai/next/route';
import { openai } from '@ai-sdk/openai';
export const POST = createChat({
model: openai('gpt-4o-mini'),
}).handler;
```
## Notes
* [withPeam](/docs/api-reference/next/with-peam) must run during build so `searchIndexStore` can be resolved.
* You can override `searchIndexStore` by passing it in `options`.
# POST
# Overview
Exports a ready-to-use `POST` handler for Next.js route handlers. The handler uses the Next.js [createChat](/docs/api-reference/next/create-chat) wrapper and the configuration produced by [withPeam](/docs/api-reference/next/with-peam).
## Source
```ts
import { createChat } from './createChat';
export const POST = createChat().handler;
```
## Usage
```ts
export const maxDuration = 30;
export { POST } from '@peam-ai/next/route';
```
## Notes
* [withPeam](/docs/api-reference/next/with-peam) must run during build so `getConfig()` can resolve the search store settings.
* The handler reads the search index via the configured `SearchIndexStore` injected by the Next.js [createChat](/docs/api-reference/next/create-chat) wrapper.
# withPeam
# Overview
Wraps your Next.js config to enable Peam content extraction during build. It sets configuration for the adapter, search store, and output file tracing, which are consumed by [createChat](/docs/api-reference/next/create-chat) and the [route handler](/docs/api-reference/next/route).
## Type Definition
```ts
import type { NextConfig } from 'next';
import type { SearchStoreConfig } from 'peam/search';
export interface PeamConfig {
/**
* Search store configuration
* @default { type: 'fileBased', config: { indexPath: '.peam/index.json' } }
*/
searchStore?: SearchStoreConfig;
/**
* Auto-discover robots.txt when true, use a custom path when string, or false to disable
* @default undefined
*/
robotsTxt?: string | boolean;
/**
* Array of wildcard patterns to exclude from indexing
* @default []
*/
exclude?: string[];
}
export declare function withPeam(peamConfig?: PeamConfig): (nextConfig?: NextConfig) => NextConfig;
```
## Usage Examples
### Basic Usage
```ts
// next.config.ts
import withPeam from '@peam-ai/next';
const nextConfig = {
// your Next.js config
};
export default withPeam()(nextConfig);
```
### With Custom Index Path
```ts
// next.config.ts
import withPeam from '@peam-ai/next';
const nextConfig = {
// your Next.js config
};
export default withPeam({
searchStore: {
type: 'fileBased',
config: {
indexPath: 'public/.peam/index.json',
},
},
})(nextConfig);
```
### CommonJS (JavaScript)
```js
// next.config.js
const withPeam = require('@peam-ai/next');
module.exports = withPeam()({
// your Next.js config
});
```
### With Existing Next.js Plugins
```ts
// next.config.ts
import withPeam from '@peam-ai/next';
import withBundleAnalyzer from '@next/bundle-analyzer';
const nextConfig = {
// your Next.js config
};
// Compose multiple config wrappers
export default withPeam()(
withBundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})(nextConfig)
);
```
## Configuration Options
### searchStore
Controls how the search index is stored during the build process.
Currently, the only supported store type is `fileBased`.
```ts
const peamConfig: PeamConfig = {
searchStore: {
type: 'fileBased',
config: {
indexPath: '.peam/index.json',
},
},
};
```
### robotsTxt
Controls robots.txt filtering. When `true`, Peam auto-discovers `robots.txt` in your project. When a string, it uses that path. If `false`, robots filtering is disabled.
```ts
const peamConfig: PeamConfig = {
robotsTxt: 'custom/robots.txt',
};
```
### exclude
Wildcard patterns to exclude from indexing.
```ts
const peamConfig: PeamConfig = {
exclude: ['/admin/**', '/api/*', '/private-*'],
};
```
## How It Works
The `withPeam` function enhances your Next.js configuration by:
1. **Adding a Build Adapter**: For Next.js 15+, configures the adapter to extract content during build
2. **Configuring File Tracing**: Adds output file tracing for the search index
3. **Creating Stub Index**: Creates an empty index file in production if missing
## Requirements
* **Next.js 15+**: The adapter feature requires Next.js 15 or higher
* For Next.js 14, use the postbuild CLI approach (see [Next.js guide](/docs/getting-started/next))
## Deployment
When deploying to Vercel or other platforms, the `withPeam` configuration ensures that:
* The search index is properly traced and included in the deployment
* The index is accessible to your API routes at runtime
* File paths are correctly resolved in serverless environments
## See Also
* [Next.js guide](/docs/getting-started/next)
* [createChat](/docs/api-reference/next/create-chat)
* [route](/docs/api-reference/next/route)
* [Next.js Configuration](https://nextjs.org/docs/app/api-reference/next-config-js)
# createChat
# Overview
Creates a [DefaultChatRuntime](/docs/api-reference/server/default-chat-runtime) instance with a `.handler` function for HTTP requests.
## Type Definition
```ts
import type { LanguageModel } from 'ai';
import type { SearchEngine, SearchIndexStore } from '@peam-ai/search';
import type { ConversationSummarizer, DefaultChatRuntime, SummarizationOptions } from 'peam/server';
export interface ChatRuntimeOptions {
/**
* The language model to use for generating responses and summarization.
* Defaults to OpenAI GPT-4o if not provided.
*/
model?: LanguageModel;
/**
* Maximum allowed length for a single message.
* @default 30000
*/
maxMessageLength?: number;
/**
* Search index store to use for loading the search index.
*/
searchIndexStore?: SearchIndexStore;
/**
* SearchEngine to use for retrieving relevant documents.
*/
searchEngine?: SearchEngine;
/**
* Options for message summarization.
*/
summarization?: SummarizationOptions | false;
/**
* Custom summarizer implementation.
*/
summarizer?: ConversationSummarizer;
}
export declare function createChat(options?: ChatRuntimeOptions): DefaultChatRuntime;
```
## Request Body
The handler expects the following JSON payload:
```ts
import type { UIMessage } from 'ai';
export interface Summary {
text: string;
lastSummarizedMessageId: string;
}
export interface ChatRequestBody {
messages: UIMessage[];
summary?: Summary;
}
```
## Usage
### Basic Usage
```ts
import { createChat } from 'peam/server';
export const POST = createChat().handler;
```
### With Custom Model
```ts
import { createChat } from 'peam/server';
import { openai } from '@ai-sdk/openai';
export const POST = createChat({
model: openai('gpt-4o'),
}).handler;
```
### With Custom Search Store
```ts
import { createChat } from 'peam/server';
import { FileBasedSearchIndexStore } from '@peam-ai/search';
export const POST = createChat({
searchIndexStore: new FileBasedSearchIndexStore({
indexPath: '.peam/index.json',
}),
}).handler;
```
### Disable Summarization
```ts
import { createChat } from 'peam/server';
export const POST = createChat({
summarization: false,
}).handler;
```
## Notes
* If `searchIndexStore` is not provided, [DefaultChatRuntime](/docs/api-reference/server/default-chat-runtime) uses a `FileBasedSearchIndexStore` pointing to `.peam/index.json`.
* If `searchEngine` is provided, it is used directly instead of loading from a store (see [getSearchEngine](/docs/api-reference/server/get-search-engine)).
* If `summarization` is not set to `false`, [WindowedConversationSummarizer](/docs/api-reference/server/windowed-conversation-summarizer) is used with `maxMessages` defaulting to 10.
# DefaultChatRuntime
# Overview
The default implementation of `ChatRuntime`. It powers the server-side chat behavior, including streaming responses and request handling. For most cases, use [createChat](/docs/api-reference/server/create-chat) to configure it.
## Type Definition
```ts
import type { ChatExecutionContext, ChatRuntime, ChatRuntimeOptions, ChatStreamInput } from 'peam/server';
import type { InferUIMessageChunk, UIMessage } from 'ai';
export declare class DefaultChatRuntime implements ChatRuntime {
constructor(options?: ChatRuntimeOptions);
stream(input: ChatStreamInput, context: ChatExecutionContext): ReadableStream>;
handler(request: Request): Promise;
}
```
## Usage
```ts
import { DefaultChatRuntime } from 'peam/server';
const runtime = new DefaultChatRuntime();
export const POST = runtime.handler;
```
## Notes
* If `searchIndexStore` is not provided, it uses a file-based store at `.peam/index.json`.
* The handler expects the `ChatRequestBody` payload (see [createChat](/docs/api-reference/server/create-chat)).
* Summarization defaults to [WindowedConversationSummarizer](/docs/api-reference/server/windowed-conversation-summarizer).
# getSearchEngine
# Overview
Loads and caches a `SearchEngine` instance from a `SearchIndexStore`. This is used by [DefaultChatRuntime](/docs/api-reference/server/default-chat-runtime) when no `searchEngine` is provided.
## Type Definition
```ts
import type { SearchEngine, SearchIndexStore } from '@peam-ai/search';
export declare function getSearchEngine(store: SearchIndexStore): Promise;
```
## Notes
* Returns `undefined` if the index is missing or empty.
* Caches the `SearchEngine` instance in memory.
# POST
# Overview
A ready-to-use HTTP handler exported from `peam/server`. It is equivalent to [createChat](/docs/api-reference/server/create-chat) with default options:
```ts
import { createChat } from 'peam/server';
export const POST = createChat().handler;
```
## Usage
```ts
export { POST } from 'peam/server';
```
## Notes
* Uses the default chat runtime configuration.
* Expects a POST request with the `ChatRequestBody` shape (`messages` and optional `summary`).
# WindowedConversationSummarizer
# Overview
Summarizes conversations after a fixed number of messages to keep the context window bounded.
## Type Definition
```ts
import type { SummarizationOptions, SummarizerInput, SummaryUpdate } from 'peam/server';
export declare class WindowedConversationSummarizer {
constructor(options: SummarizationOptions);
summarize(input: SummarizerInput): Promise;
}
```
## Usage
Create a summarizer and pass it to the chat runtime with [createChat](/docs/api-reference/server/create-chat):
```ts
import { createChat, WindowedConversationSummarizer } from 'peam/server';
import { openai } from '@ai-sdk/openai';
const summarizer = new WindowedConversationSummarizer({
model: openai('gpt-4o'),
maxMessages: 12,
});
export const POST = createChat({
summarizer,
}).handler;
```
You can also pass summarization options directly and let [createChat](/docs/api-reference/server/create-chat) construct it:
```ts
import { createChat } from 'peam/server';
import { openai } from '@ai-sdk/openai';
export const POST = createChat({
summarization: {
model: openai('gpt-4o'),
maxMessages: 12,
},
}).handler;
```
## Notes
* Uses `maxMessages` with a default of 10.
* Used automatically by [DefaultChatRuntime](/docs/api-reference/server/default-chat-runtime) unless `summarization` is set to `false`.