# Ask AI

> Configure the Ask AI (RAG-powered AI chat) in @farming-labs/docs. Use when enabling AI chat, setting mode (search vs floating), floatingStyle (panel, modal, popover, full-modal), position, providers, models, suggestedQuestions, apiKey, systemPrompt, maxResults, useMcp, feedback, or onActions. Covers Next.js, TanStack Start, SvelteKit, Astro, Nuxt, and env vars.

- Skill: `farming-labs/ask-ai` (Agent Skill)
- Install (CLI): `npx skillmds@latest add farming-labs/ask-ai`
- Raw SKILL.md: https://api.skillmd.com/api/skills/farming-labs/ask-ai/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: farming-labs (https://skillmd.com/u/farming-labs)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/farming-labs/ask-ai

---


# @farming-labs/docs — Ask AI (AI Chat)

Add a built-in AI chat that answers questions using your documentation. The AI searches relevant pages, builds context, and streams a response from an OpenAI-compatible LLM. Use this skill when configuring the **Ask AI** / **DocsBot** feature.

**Full docs:** [Ask AI](https://docs.farming-labs.dev/docs/customization/ai-chat).

---

## Quick start

```ts title="docs.config.ts"
ai: {
  enabled: true,
}
```

The AI reads `process.env.OPENAI_API_KEY` and uses `gpt-4o-mini` by default. Add `OPENAI_API_KEY` to your `.env` file. For SvelteKit/Astro, pass the API key into the docs server config (see below).

---

## Configuration reference

All options go inside the `ai` object in `docs.config.ts`.

### enabled

| Type | Default |
| ---- | ------- |
| `boolean` | `false` |

```ts
ai: { enabled: true }
```

### mode

How the AI chat UI is presented.

| Type | Default |
| ---- | ------- |
| `"search" \| "floating"` | `"search"` |

- **`"search"`** — AI tab inside the Cmd+K search dialog. Users switch between "Search" and "AI" tabs.
- **`"floating"`** — Floating chat widget with a button; opens as panel, modal, popover, or full-modal.

```ts
ai: { enabled: true, mode: "floating" }
```

### position

Position of the floating button. Only when `mode` is `"floating"`.

| Type | Default |
| ---- | ------- |
| `"bottom-right" \| "bottom-left" \| "bottom-center"` | `"bottom-right"` |

### floatingStyle

Visual style when the floating chat is opened. Only when `mode` is `"floating"`.

| Type | Default |
| ---- | ------- |
| `"panel" \| "modal" \| "popover" \| "full-modal"` | `"panel"` |

- **`"panel"`** — Tall panel sliding up from the button; no full backdrop.
- **`"modal"`** — Centered modal with backdrop (like Cmd+K).
- **`"popover"`** — Compact popover near the button.
- **`"full-modal"`** — Full-screen overlay; messages in center, input at bottom, suggested questions as pills.

```ts
ai: { enabled: true, mode: "floating", floatingStyle: "full-modal" }
```

### model

Single model (string) or multiple models with UI dropdown (object).

**Single model:** `model: "gpt-4o-mini"` (default) or `"gpt-4o"`, etc.

**Multiple models:**

```ts
ai: {
  enabled: true,
  model: {
    models: [
      { id: "gpt-4o-mini", label: "GPT-4o mini (fast)", provider: "openai" },
      { id: "gpt-4o", label: "GPT-4o (quality)", provider: "openai" },
      { id: "llama-3.3-70b-versatile", label: "Llama 3.3 70B", provider: "groq" },
    ],
    defaultModel: "gpt-4o-mini",
  },
}
```

Each model: `id` (API model id), `label` (UI name), `provider` (optional, key in `providers`). If `provider` is omitted, the default `baseUrl` and `apiKey` are used.

### providers

Named provider configs so multiple backends (OpenAI, Groq, etc.) can coexist.

```ts
ai: {
  enabled: true,
  providers: {
    openai: {
      baseUrl: "https://api.openai.com/v1",
      apiKey: process.env.OPENAI_API_KEY,
    },
    groq: {
      baseUrl: "https://api.groq.com/openai/v1",
      apiKey: process.env.GROQ_API_KEY,
    },
  },
  model: {
    models: [
      { id: "gpt-4o-mini", label: "GPT-4o mini", provider: "openai" },
      { id: "llama-3.3-70b-versatile", label: "Llama 3.3 70B", provider: "groq" },
    ],
    defaultModel: "gpt-4o-mini",
  },
}
```

All providers must be OpenAI Chat Completions API compatible.

### baseUrl and apiKey

Default base URL and API key when no per-model `provider` is set. Defaults: `baseUrl` = OpenAI v1, `apiKey` = `process.env.OPENAI_API_KEY`. Never hardcode API keys; use env vars.

### suggestedQuestions

Pre-filled questions shown when the chat is empty. Clicking one submits it.

| Type | Default |
| ---- | ------- |
| `string[]` | `[]` |

```ts
ai: {
  enabled: true,
  suggestedQuestions: [
    "How do I get started?",
    "What themes are available?",
    "How do I create a custom component?",
  ],
}
```

### systemPrompt

Custom system prompt prepended to the conversation. Doc context is appended automatically.

```ts
ai: {
  enabled: true,
  systemPrompt: "You are a friendly assistant for Acme Corp. Always mention our support email for complex issues.",
}
```

### maxResults

Number of search results used as context for the AI. More = more context, higher token usage.

| Type | Default |
| ---- | ------- |
| `number` | `5` |

### feedback

Copy, like, and dislike action row shown after a completed Ask AI answer. Set `feedback: false` to
hide the row.

| Type | Default |
| ---- | ------- |
| `boolean \| { enabled?: boolean; onFeedback?: (data) => void \| Promise<void> }` | `true` |

### onActions

Single callback for copy, like, and dislike actions. Use `data.type` to branch.

```ts
ai: {
  enabled: true,
  onActions(data) {
    if (data.type === "copy") console.log("Copied", data.answer);
    if (data.type === "like") console.log("Helpful", data.question);
    if (data.type === "dislike") console.log("Not helpful", data.question);
  },
}
```

The callback receives the action type, question, answer, model, UI surface, URL/path, visible chat
messages up to that answer, and `copied` for copy actions. Like/dislike also dispatch the legacy
`fd:ai-feedback` browser event and emit `ai_feedback` when analytics is enabled. All actions
dispatch `fd:ai-action`.

### Retrieval quality

Ask AI uses the configured docs search pipeline before building model context, including simple
search, Typesense, Algolia, MCP search, or custom adapters. The model context is hydrated from the
matched local page/section, preserves fenced code blocks for commands and config snippets, and
infers package names plus exact import lines from the retrieved context.

Use `ai.useMcp: true` when only Ask AI should retrieve through the built-in MCP `search_docs` tool
while the normal docs search API keeps its own provider. `true` uses `mcp.route` (default
`/api/docs/mcp`) from the MCP server the docs site already exposes. If a project sets
`mcp: { route: "/custom/docs/mcp" }`, `useMcp: true` follows that route automatically. Pass
`{ endpoint: "https://docs.example.com/mcp", headers: { Authorization: "Bearer ..." } }` only for a
hosted or external MCP endpoint. MCP handles retrieval only; `model`, `apiKey`, `baseUrl`, or
`providers` still control the LLM generation request. Custom MCP endpoints must support Streamable
HTTP MCP, return an `mcp-session-id` on `initialize`, and expose a search tool (default
`search_docs`; override with `toolName` if needed). If MCP is disabled or `search_docs` is turned
off, Ask AI falls back to the normal top-level `search` config.

### aiLabel

Label for the AI button (e.g. "DocsBot", "Ask AI").

### packageName

Optional package-name override for unusual docs where install/import examples do not mention the
main package clearly. Most projects should leave this unset because Ask AI infers package names and
exact imports from retrieved docs context.

```ts
ai: {
  enabled: true,
  packageName: "@farming-labs/docs",
}
```

---

## Framework-specific: API key

- **Next.js:** Set `OPENAI_API_KEY` in `.env`; read via `process.env.OPENAI_API_KEY`.
- **TanStack Start:** Set in `.env`; pass it through `createDocsServer` in `src/lib/docs.server.ts`: `ai: { apiKey: process.env.OPENAI_API_KEY, ...docsConfig.ai }`.
- **SvelteKit:** For OpenAI-compatible providers, set in `.env` and pass into `createDocsServer` in `src/lib/docs.server.ts`: `ai: { apiKey: env.OPENAI_API_KEY, ...config.ai }` (use `$env/dynamic/private`). For `ai.provider: "docs-cloud"`, `createDocsServer()` handles the local Docs Cloud proxy automatically; use `PUBLIC_DOCS_CLOUD_PROJECT_ID` for public project identity or `DOCS_CLOUD_PROJECT_ID` for server-only proxy mode.
- **Astro:** Set in `.env`; pass in docs server: `ai: { apiKey: import.meta.env.OPENAI_API_KEY, ...config.ai }`.
- **Nuxt:** Set in `.env`; Nitro/runtime config exposes it; `defineDocsHandler` reads `process.env.OPENAI_API_KEY` on the server.

---

## Edge cases

1. **Static export** — If `staticExport: true`, AI chat is hidden; no API route is deployed.
2. **Multiple models** — Always specify `provider` for each model when using multiple providers so the correct `baseUrl` and `apiKey` are used.
3. **CORS / custom baseUrl** — When using a proxy or custom endpoint, set `baseUrl` (or per-provider `baseUrl`) accordingly.

---

## Resources

- **Full Ask AI docs:** [docs.farming-labs.dev/docs/customization/ai-chat](https://docs.farming-labs.dev/docs/customization/ai-chat)
- **Configuration:** use the `configuration` skill for `staticExport` and top-level config.

