diff --git a/apps/docs/alpha-docs/BETA-FEATURES.md b/apps/docs/alpha-docs/BETA-FEATURES.md new file mode 100644 index 0000000..e39eb55 --- /dev/null +++ b/apps/docs/alpha-docs/BETA-FEATURES.md @@ -0,0 +1,303 @@ +# Beta Features — Complete List + +> All features present in `beta` branch but **not yet in `main`** (production). +> Organised by SDK package. Docs/alpha-doc status noted per feature. + +--- + +## Copilot SDK (`@yourgpt/copilot-sdk`) + +### 1. Conversation Branching + +Edit any user message to fork a new parallel conversation path. Each edit spawns a sibling branch; `← N/M →` arrows let the user navigate between variants — same UX pattern as ChatGPT, Claude.ai, and Gemini. + +- **API:** `allowEdit` prop on `` · `switchBranch()` · `getAllMessages()` · `BranchNavigator` component +- **Package:** `@yourgpt/copilot-sdk/react` + `@yourgpt/copilot-sdk/ui` +- **Docs page:** `content/docs/chat/branching.mdx` +- **Alpha-doc:** `alpha-docs/BRANCHING.md` + +--- + +### 2. Message History Compaction + +Automatic context-window management. When token usage crosses a configurable threshold the SDK compacts old messages using a chosen strategy, without ever shrinking the display history the user sees. + +- **Strategies:** `none` (default) · `sliding-window` · `selective-prune` · `summary-buffer` (recommended) +- **API:** `messageHistory` prop on `` · `useMessageHistory()` hook · `compactSession(instructions?)` for manual trigger +- **Package:** `@yourgpt/copilot-sdk/react` +- **Docs page:** `content/docs/context/compaction.mdx` +- **Alpha-doc:** `alpha-docs/message-history-compaction.md` + +--- + +### 3. Skills System + +On-demand instruction playbooks the AI loads at runtime. Skills are Markdown files or inline strings. Keeps the system prompt lean by deferring behaviour descriptions until the AI actually needs them. + +- **Strategies:** `eager` (always injected) · `auto` (AI calls `load_skill` tool on demand) · `manual` (accessible but not advertised) +- **API:** `` · `defineSkill()` · `useSkill()` · `useSkillStatus()` +- **Package:** `@yourgpt/copilot-sdk/react` +- **Docs page:** `content/docs/skills/client.mdx` + `content/docs/skills/server.mdx` +- **Alpha-doc:** `alpha-docs/SKILLS.md` + `alpha-docs/skills-system.md` + +--- + +### 4. Chat Primitives (`ChatPrimitives` namespace) + +A complete set of low-level building blocks for composing fully custom chat UIs. Every primitive reads state from SDK context internally — no manual wiring needed. + +- **Primitives:** `MessageList` · `DefaultMessage` · `Header` · `Welcome` · `Input` · `ScrollAnchor` · `Message` · `MessageAvatar` · `MessageContent` · `MessageActions` · `MessageAction` · `Loader` +- **API:** `import { ChatPrimitives as Chat } from "@yourgpt/copilot-sdk/ui"` or via `CopilotChat.*` compound extensions +- **Package:** `@yourgpt/copilot-sdk/ui` +- **Docs page:** `content/docs/customizations/chat-primitives.mdx` +- **Alpha-doc:** `alpha-docs/CHAT-PRIMITIVES.md` + +--- + +### 5. Custom Message View (`messageView` prop) + +Intercepts the message-list render loop and hands both raw messages and pre-rendered SDK elements to a render-prop callback. Use it to inject custom UI, conditionally replace messages by type, or build entirely custom layouts. + +- **API:** ` ReactNode }} />` +- **Package:** `@yourgpt/copilot-sdk/ui` +- **Docs page:** `content/docs/customizations/custom-message-view.mdx` +- **Alpha-doc:** `alpha-docs/CUSTOM-MESSAGE-VIEW.md` + +--- + +### 6. Message Actions (Compound Components) + +Declarative floating action buttons on chat messages — copy, inline edit, thumbs up/down, or fully custom actions. Role-based configuration (user vs assistant). Appears on hover; same compound-component pattern as shadcn/Radix. + +- **API:** `CopilotChat.MessageActions` · `CopilotChat.CopyAction` · `CopilotChat.EditAction` · `CopilotChat.FeedbackAction` · `CopilotChat.Action` +- **Package:** `@yourgpt/copilot-sdk/ui` +- **Docs page:** `content/docs/chat/message-actions.mdx` +- **Alpha-doc:** `alpha-docs/MESSAGE-ACTIONS.md` + +--- + +### 7. File Attachments + +Drag-and-drop file and media attachments in chat. Includes an `AttachmentStrip` thumbnail strip, a drop-zone overlay, upload error handling, and automatic forwarding to the server runtime. + +- **API:** Auto-enabled when `runtimeUrl` is set · `useAttachments()` for headless access +- **Package:** `@yourgpt/copilot-sdk/ui` +- **Docs page:** `content/docs/attachments.mdx` +- **Alpha-doc:** ✗ not covered + +--- + +### 8. Headless Primitives (`useCopilotEvent` + `useMessageMeta`) + +Subscribe to raw stream events and read per-message metadata without using any SDK UI components. The foundation for building fully headless integrations (Slack bots, custom renderers, etc.). + +- **Events:** `thinking:delta` · `action:start` · `action:end` · `loop:iteration` · `*` (catch-all) +- **API:** `useCopilotEvent(event, handler)` · `useMessageMeta(messageId)` → `{ tokenUsage, metadata }` +- **Package:** `@yourgpt/copilot-sdk/react` +- **Docs page:** `content/docs/headless.mdx` +- **Alpha-doc:** ✗ not covered + +--- + +### 9. Context Stats (`useContextStats`) + +Real-time reactive context window usage. Returns token counts and percentages updated after every LLM call, plus a convenient chars-based estimate before the first send. + +- **API:** `const { contextUsage, totalTokens, usagePercent } = useContextStats()` +- **Package:** `@yourgpt/copilot-sdk/react` +- **Docs page:** `content/docs/context/token-tracking.mdx` +- **Alpha-doc:** `alpha-docs/CONTEXT-MANAGEMENT.md` §5 + +--- + +### 10. Thread Management + Thread Picker + +Enhanced multi-thread support with `activeLeafId` (tracks the active conversation leaf), a `ThreadPicker` UI component for switching between saved threads, and a localStorage adapter for zero-config thread persistence. + +- **API:** `` from `@yourgpt/copilot-sdk/ui` · `useThread()` hook · `localStorageAdapter` +- **Package:** `@yourgpt/copilot-sdk/react` + `@yourgpt/copilot-sdk/ui` +- **Docs page:** `content/docs/context/session.mdx` (partial) +- **Alpha-doc:** ✗ not covered + +--- + +### 11. Agent Iteration Tracking + +Track how many tool-use loops the agent has run in a single turn. Enforce a `maxAgentIterations` cap to prevent runaway loops. Exposes checkpoint exports for state inspection. + +- **API:** `agentIteration` value from `useContextStats()` · `maxAgentIterations` on `` · `allowEdit` prop on `` +- **Package:** `@yourgpt/copilot-sdk/react` +- **Docs page:** `content/docs/tools/agentic-loop.mdx` +- **Alpha-doc:** `alpha-docs/CONTEXT-MANAGEMENT.md` §6 + +--- + +### 12. Deferred Tools + +Tools not eagerly sent to the LLM. Instead they sit in a registry and are only included in the request when the user message semantically matches them via BM25 search. Reduces tokens significantly for large tool sets. + +- **API:** `useTool({ name: "...", deferred: true, description: "...", ... })` +- **Package:** `@yourgpt/copilot-sdk/react` +- **Docs page:** `content/docs/tools/deferred-tools.mdx` +- **Alpha-doc:** `alpha-docs/CONTEXT-MANAGEMENT.md` §7 + +--- + +### 13. Hidden Tools + +Tools registered with the SDK but never sent to the LLM. Used for client-side execution the AI triggers indirectly, or for internal state mutations that should never appear in the model's tool list. + +- **API:** `useTool({ name: "...", hidden: true, ... })` +- **Package:** `@yourgpt/copilot-sdk/react` +- **Docs page:** `content/docs/tools/hidden-tools.mdx` +- **Alpha-doc:** `alpha-docs/CONTEXT-MANAGEMENT.md` §7 + +--- + +### 14. Fallback Tool Renderer + +A catch-all render function for tool calls that have no registered renderer. Prevents unknown or dynamically-named tool calls from showing a blank area in the chat. + +- **API:** ` } />` +- **Package:** `@yourgpt/copilot-sdk/ui` +- **Docs page:** partial — `content/docs/tools/` section +- **Alpha-doc:** `alpha-docs/CONTEXT-MANAGEMENT.md` §7 + +--- + +### 15. Message Grouping + +Consecutive tool call + tool result pairs are visually grouped in the chat UI instead of rendering as separate message bubbles. Keeps the conversation thread readable during multi-step agent runs. + +- **API:** Automatic — internal `groupConsecutiveToolMessages` config +- **Package:** `@yourgpt/copilot-sdk/ui` +- **Docs page:** ✗ no dedicated page +- **Alpha-doc:** `alpha-docs/CONTEXT-MANAGEMENT.md` §8 + +--- + +### 16. CSS Class Reference + +Comprehensive reference for all `csdk-*` CSS classes applied to every chat UI element. Enables deterministic custom theming without touching component internals. + +- **API:** Target `.csdk-chat` · `.csdk-message` · `.csdk-input` · `.csdk-overlay` etc. in your stylesheets +- **Package:** `@yourgpt/copilot-sdk/ui` +- **Docs page:** `content/docs/customizations/css-classes.mdx` +- **Alpha-doc:** ✗ not covered + +--- + +### 17. Generative UI — Experimental + +The AI can render structured UI components (cards, tables, charts, stat tiles) inline inside the chat, generated from tool call results. Ships with four built-in renderers plus a hook for custom ones. + +- **Renderers:** `CardRenderer` · `TableRenderer` · `StatRenderer` · `HtmlRenderer` +- **API:** `import { generativeUITool, useGenerativeUI } from "@yourgpt/copilot-sdk/experimental"` — register `generativeUITool` as a tool, use `useGenerativeUI()` to render results +- **Package:** `@yourgpt/copilot-sdk/experimental` +- **Docs page:** `content/docs/generative-ui.mdx` (page existed on main; full demo + renderers added in beta) +- **Alpha-doc:** ✗ not covered + +--- + +### 18. Streaming Enhancements (Pre-created Message IDs) + +Messages receive stable IDs before streaming begins, enabling optimistic UI updates, correct server-side event ordering, and `useMessageMeta()` access from the very first chunk. + +- **API:** Internal — no breaking API change. Consumed transparently by `useMessageMeta(id)`. +- **Package:** `@yourgpt/copilot-sdk` (core) +- **Docs page:** ✗ not documented +- **Alpha-doc:** ✗ not covered + +--- + +## LLM SDK (`@yourgpt/llm-sdk`) + +### 19. Fallback Chain & Routing Strategies + +Chain multiple LLM providers or models with automatic failover. Supports `priority` (try in order) and `round-robin` routing. Per-model retry with exponential or fixed backoff. Pluggable `RoutingStore` for serverless environments (Redis, Upstash, Cloudflare KV). + +- **API:** `createFallbackChain([provider1, provider2], { routing: "round-robin", retries: 3, backoff: "exponential", onFallback, onRetry })` → pass result to `createRuntime({ provider: chain })` +- **Package:** `@yourgpt/llm-sdk` — `fallback` module +- **Docs page:** `content/docs/providers/fallback.mdx` +- **Alpha-doc:** ✗ not covered + +--- + +### 20. Tool Profiles + BM25 Tool Search + +For large tool sets (50+ tools), tools are grouped into named profiles and ranked per-request using BM25. Only the most relevant tools are forwarded to the LLM, keeping per-request token cost low. Also supports native provider search (Anthropic / OpenAI). + +- **API:** `createRuntime({ toolProfiles: { defaultProfile: "core", profiles: { ... } }, maxEagerTools: 20, exposeWhenExceeds: 40 })` +- **Package:** `@yourgpt/llm-sdk/server` +- **Docs page:** ✗ no dedicated page yet +- **Alpha-doc:** `alpha-docs/CONTEXT-MANAGEMENT.md` §7 + +--- + +### 21. YourGPT Provider (`createYourGPT`) + +First-party storage adapter for the YourGPT backend. Plugging it into `createRuntime({ storage })` auto-creates sessions on first message, persists every user message + assistant response + tool pair, and provides a one-liner file upload endpoint. + +- **API:** `import { createYourGPT } from "@yourgpt/llm-sdk/yourgpt"` → `createRuntime({ provider, model, storage: createYourGPT({ apiKey, widgetUid }) })` +- **Package:** `@yourgpt/llm-sdk/yourgpt` +- **Docs page:** `content/docs/server/storage.mdx` +- **Alpha-doc:** `alpha-docs/STORAGE-ADAPTER.md` + +--- + +### 22. Resolvable Pattern + +Any runtime config value — system prompt, model ID, tools list, max tokens — can be a static value, a synchronous function, or an async function. Evaluated fresh on every request, enabling per-user or per-tenant dynamic configuration. + +- **API:** `createRuntime({ systemPrompt: async (req) => getPromptFor(req.userId), model: (req) => req.body.model ?? "gpt-4o" })` +- **Package:** `@yourgpt/llm-sdk` + `@yourgpt/copilot-sdk` +- **Docs page:** ✗ no dedicated page +- **Alpha-doc:** ✗ not covered + +--- + +### 23. New Provider Adapters (Azure, xAI, Ollama) + +Official adapters for Azure OpenAI, xAI (Grok), and Ollama (local models) added alongside the existing OpenAI, Anthropic, and Google adapters. Drop-in compatible with `createRuntime` and the fallback chain. + +- **API:** `import { createAzure } from "@yourgpt/llm-sdk/azure"` · `import { createXAI } from "@yourgpt/llm-sdk/xai"` · `import { createOllama } from "@yourgpt/llm-sdk/ollama"` +- **Package:** `@yourgpt/llm-sdk` +- **Docs page:** `content/docs/providers/xai.mdx` + `content/docs/providers/ollama.mdx` (existed on main) +- **Alpha-doc:** ✗ not covered + +--- + +### 24. Context Budget / Tool Result Truncation + +Automatically truncates tool results that exceed `toolResultMaxChars` before they are included in the LLM context, preventing a single large tool response from blowing the context window. + +- **API:** `` (client) · runtime enforces server-side +- **Package:** `@yourgpt/llm-sdk/server` + `@yourgpt/copilot-sdk` +- **Docs page:** `content/docs/context/compaction.mdx` (partial) +- **Alpha-doc:** `alpha-docs/CONTEXT-MANAGEMENT.md` + `alpha-docs/message-history-compaction.md` + +--- + +### 25. Server-side Compaction Endpoint + +A `compactSession()` function on the server runtime that summarises a stored conversation before the next LLM call. Pairs with the client-side compaction system and can be triggered via a dedicated API route. + +- **API:** `runtime.compactSession(threadId, instructions?)` +- **Package:** `@yourgpt/llm-sdk/server` +- **Docs page:** `content/docs/context/compaction.mdx` +- **Alpha-doc:** `alpha-docs/message-history-compaction.md` + +--- + +## Missing Docs Summary + +Features that are **in beta code** but have **no proper docs page** yet: + +| Feature | SDK | Coverage | +| ---------------------------------------- | ------------------------- | ---------------------- | +| Tool Profiles + BM25 Tool Search | `llm-sdk` | Alpha-doc only | +| Message Grouping | `copilot-sdk` | Alpha-doc only (brief) | +| Thread Picker (full guide) | `copilot-sdk` | Not documented | +| Resolvable Pattern | `llm-sdk` + `copilot-sdk` | Not documented | +| Fallback Tool Renderer | `copilot-sdk` | Alpha-doc only | +| Streaming Enhancements (pre-created IDs) | `copilot-sdk` | Not documented | diff --git a/apps/docs/components/icons/ai-book.tsx b/apps/docs/components/icons/ai-book.tsx new file mode 100644 index 0000000..59c5a8f --- /dev/null +++ b/apps/docs/components/icons/ai-book.tsx @@ -0,0 +1,45 @@ +import type { SVGProps } from "react"; + +export function AiBookIcon(props: SVGProps) { + return ( + + + + + + + + ); +} diff --git a/apps/docs/components/icons/ai-chip1.tsx b/apps/docs/components/icons/ai-chip1.tsx index 408b6a5..cbb867b 100644 --- a/apps/docs/components/icons/ai-chip1.tsx +++ b/apps/docs/components/icons/ai-chip1.tsx @@ -1,41 +1,40 @@ -interface AiChip1Props { - width?: number | string; - height?: number | string; - className?: string; - color?: string; -} +import type { SVGProps } from "react"; -export function AiChip1({ - width = 24, - height = 24, - className, - color = "currentColor", -}: AiChip1Props) { +export function AiChip1(props: SVGProps) { return ( + ); diff --git a/apps/docs/components/icons/ai-magic.tsx b/apps/docs/components/icons/ai-magic.tsx new file mode 100644 index 0000000..36ea77f --- /dev/null +++ b/apps/docs/components/icons/ai-magic.tsx @@ -0,0 +1,40 @@ +import * as React from "react"; +import type { SVGProps } from "react"; + +export function AiMagicIcon(props: SVGProps) { + return ( + + {/* Duotone fill layer */} + + + {/* Stroke outline layer */} + + + + ); +} diff --git a/apps/docs/components/icons/bubble-chat.tsx b/apps/docs/components/icons/bubble-chat.tsx new file mode 100644 index 0000000..cccea31 --- /dev/null +++ b/apps/docs/components/icons/bubble-chat.tsx @@ -0,0 +1,34 @@ +import type { SVGProps } from "react"; + +export function BubbleChatIcon(props: SVGProps) { + return ( + + + + + + ); +} diff --git a/apps/docs/components/icons/file-code.tsx b/apps/docs/components/icons/file-code.tsx new file mode 100644 index 0000000..5cfbb20 --- /dev/null +++ b/apps/docs/components/icons/file-code.tsx @@ -0,0 +1,41 @@ +import type { SVGProps } from "react"; + +export function FileCodeIcon(props: SVGProps) { + return ( + + + + + + + ); +} diff --git a/apps/docs/components/icons/index.ts b/apps/docs/components/icons/index.ts index 7842af0..6227922 100644 --- a/apps/docs/components/icons/index.ts +++ b/apps/docs/components/icons/index.ts @@ -5,3 +5,11 @@ export { RocketIcon } from "./rocket"; export { MessageQuestionIcon } from "./message-question"; export { AiChip1 } from "./ai-chip1"; export { Grid1 } from "./grid1"; +export { SlidersHorizontalIcon } from "./sliders-horizontal"; +export { AiBookIcon } from "./ai-book"; +export { MagicWandIcon } from "./magic-wand"; +export { PuzzleIcon } from "./puzzle"; +export { BubbleChatIcon } from "./bubble-chat"; +export { FileCodeIcon } from "./file-code"; +export { ServerStackIcon } from "./server-stack"; +export { AiMagicIcon } from "./ai-magic"; diff --git a/apps/docs/components/icons/magic-wand.tsx b/apps/docs/components/icons/magic-wand.tsx new file mode 100644 index 0000000..4b29f40 --- /dev/null +++ b/apps/docs/components/icons/magic-wand.tsx @@ -0,0 +1,49 @@ +import type { SVGProps } from "react"; + +export function MagicWandIcon(props: SVGProps) { + return ( + + + + + + + + + ); +} diff --git a/apps/docs/components/icons/puzzle.tsx b/apps/docs/components/icons/puzzle.tsx new file mode 100644 index 0000000..1224728 --- /dev/null +++ b/apps/docs/components/icons/puzzle.tsx @@ -0,0 +1,26 @@ +import type { SVGProps } from "react"; + +export function PuzzleIcon(props: SVGProps) { + return ( + + + + + ); +} diff --git a/apps/docs/components/icons/server-stack.tsx b/apps/docs/components/icons/server-stack.tsx new file mode 100644 index 0000000..fe47c2c --- /dev/null +++ b/apps/docs/components/icons/server-stack.tsx @@ -0,0 +1,67 @@ +import type { SVGProps } from "react"; + +export function ServerStackIcon(props: SVGProps) { + return ( + + + + + + + + + + + ); +} diff --git a/apps/docs/components/icons/sliders-horizontal.tsx b/apps/docs/components/icons/sliders-horizontal.tsx new file mode 100644 index 0000000..0f29760 --- /dev/null +++ b/apps/docs/components/icons/sliders-horizontal.tsx @@ -0,0 +1,54 @@ +import type { SVGProps } from "react"; + +export function SlidersHorizontalIcon(props: SVGProps) { + return ( + + + + + + + + + ); +} diff --git a/apps/docs/content/docs/chat/branching.mdx b/apps/docs/content/docs/advanced/branching.mdx similarity index 72% rename from apps/docs/content/docs/chat/branching.mdx rename to apps/docs/content/docs/advanced/branching.mdx index 9c0f3d5..406dd49 100644 --- a/apps/docs/content/docs/chat/branching.mdx +++ b/apps/docs/content/docs/advanced/branching.mdx @@ -1,15 +1,9 @@ --- title: Conversation Branching description: Edit messages to create parallel conversation paths, just like ChatGPT and Claude.ai -icon: GitBranch --- import { Callout } from 'fumadocs-ui/components/callout'; -import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; - - -**Beta** — This feature is in **alpha**. APIs may change before stable release. - Edit any user message to create a parallel conversation path, preserving the original. Navigate between variants with `← N/M →` — the same UX as ChatGPT, Claude.ai, and Gemini. @@ -30,9 +24,9 @@ If you use ``, branching is **already active**. No code changes n --- -## New APIs +## API -### `useCopilot()` / `useCopilotProvider` +### `useCopilot()` ```typescript const { @@ -44,7 +38,7 @@ const { } = useCopilot(); ``` -### `BranchInfo` type +### `BranchInfo` ```typescript interface BranchInfo { @@ -76,7 +70,7 @@ import { BranchNavigator } from "@yourgpt/copilot-sdk/ui"; ### `MessageTree` (framework-agnostic) ```typescript -import { MessageTree, type BranchInfo } from "@yourgpt/copilot-sdk"; +import { MessageTree } from "@yourgpt/copilot-sdk"; const tree = new MessageTree(messages); tree.getVisibleMessages(); // active path only (sent to AI) @@ -88,26 +82,6 @@ tree.hasBranches; // boolean --- -## Manual Wiring (`` users) - -Wire the three props from `useCopilot()`: - -```tsx -function MyChat() { - const { switchBranch, getBranchInfo, editMessage } = useCopilot(); - - return ( - - ); -} -``` - ---- - ## Custom Message Renderers Use `getBranchInfo` + `BranchNavigator` in your own message components: @@ -158,9 +132,7 @@ await saveToServer(allMessages); ## Persistence -### New DB columns (optional) - -Two new optional columns on your messages table: +### Optional DB columns ```sql ALTER TABLE messages @@ -169,12 +141,10 @@ ALTER TABLE messages ``` -These columns are **optional**. Existing rows without them are auto-migrated to a linear tree on load — no data loss, no required migration script. +These columns are **optional**. Existing rows without them are auto-migrated to a linear tree on load — no data loss, no migration required. -### What gets saved - -When `onMessagesChange` fires, the payload now contains **all messages across all branches**. Each message carries: +When `onMessagesChange` fires, the payload contains **all messages across all branches**: ```json { @@ -186,25 +156,12 @@ When `onMessagesChange` fires, the payload now contains **all messages across al } ``` -### Upsert strategy (recommended) +Use upsert-by-ID when saving — a simple overwrite will lose inactive branches: ```typescript -// ✅ Safe for branching — upsert by ID +// ✅ Safe for branching await db.messages.upsert({ id: msg.id, ...msg }); // ⚠️ Loses inactive branches await db.threads.update({ messages: visibleMessages }); ``` - ---- - -## Breaking Changes - -**None.** All new fields and methods are optional. Existing usage is untouched. - -| Scenario | Behavior | -|----------|----------| -| Messages with no `parentId` | Falls back to insertion order (legacy linear) | -| `regenerate()` with no args | Identical to before | -| `sendMessage()` with no options | Identical to before | -| `onMessagesChange` consumers | Payload now includes all branches — shape unchanged | diff --git a/apps/docs/content/docs/context/compaction.mdx b/apps/docs/content/docs/advanced/compaction.mdx similarity index 100% rename from apps/docs/content/docs/context/compaction.mdx rename to apps/docs/content/docs/advanced/compaction.mdx diff --git a/apps/docs/content/docs/advanced/meta.json b/apps/docs/content/docs/advanced/meta.json new file mode 100644 index 0000000..c2cf730 --- /dev/null +++ b/apps/docs/content/docs/advanced/meta.json @@ -0,0 +1,5 @@ +{ + "title": "Advanced", + "icon": "SlidersHorizontal", + "pages": ["compaction", "token-tracking", "branching"] +} diff --git a/apps/docs/content/docs/context/token-tracking.mdx b/apps/docs/content/docs/advanced/token-tracking.mdx similarity index 100% rename from apps/docs/content/docs/context/token-tracking.mdx rename to apps/docs/content/docs/advanced/token-tracking.mdx diff --git a/apps/docs/content/docs/api-reference/angular.mdx b/apps/docs/content/docs/api-reference/frameworks/angular.mdx similarity index 100% rename from apps/docs/content/docs/api-reference/angular.mdx rename to apps/docs/content/docs/api-reference/frameworks/angular.mdx diff --git a/apps/docs/content/docs/api-reference/frameworks/meta.json b/apps/docs/content/docs/api-reference/frameworks/meta.json new file mode 100644 index 0000000..c1c72cb --- /dev/null +++ b/apps/docs/content/docs/api-reference/frameworks/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Other Frameworks", + "pages": ["vue", "angular"] +} diff --git a/apps/docs/content/docs/api-reference/vue.mdx b/apps/docs/content/docs/api-reference/frameworks/vue.mdx similarity index 100% rename from apps/docs/content/docs/api-reference/vue.mdx rename to apps/docs/content/docs/api-reference/frameworks/vue.mdx diff --git a/apps/docs/content/docs/api-reference/meta.json b/apps/docs/content/docs/api-reference/meta.json index ed618ed..aabc09a 100644 --- a/apps/docs/content/docs/api-reference/meta.json +++ b/apps/docs/content/docs/api-reference/meta.json @@ -1,5 +1,5 @@ { "title": "API Reference", "icon": "FileCode", - "pages": ["core", "chat", "react", "vue", "angular"] + "pages": ["core", "chat", "react", "frameworks"] } diff --git a/apps/docs/content/docs/attachments.mdx b/apps/docs/content/docs/chat/attachments.mdx similarity index 99% rename from apps/docs/content/docs/attachments.mdx rename to apps/docs/content/docs/chat/attachments.mdx index 4edd12d..bf2ba81 100644 --- a/apps/docs/content/docs/attachments.mdx +++ b/apps/docs/content/docs/chat/attachments.mdx @@ -1,7 +1,6 @@ --- title: Attachments description: Send images, PDFs, and files alongside chat messages -icon: Paperclip --- import { Callout } from 'fumadocs-ui/components/callout'; diff --git a/apps/docs/content/docs/chat/index.mdx b/apps/docs/content/docs/chat/index.mdx index 2d68742..a3a7c51 100644 --- a/apps/docs/content/docs/chat/index.mdx +++ b/apps/docs/content/docs/chat/index.mdx @@ -1,7 +1,6 @@ --- title: Chat description: Pre-built chat component -icon: MessageSquare --- import { Callout } from 'fumadocs-ui/components/callout'; diff --git a/apps/docs/content/docs/chat/message-actions.mdx b/apps/docs/content/docs/chat/message-actions.mdx deleted file mode 100644 index 2ee635e..0000000 --- a/apps/docs/content/docs/chat/message-actions.mdx +++ /dev/null @@ -1,158 +0,0 @@ ---- -title: Message Actions -description: Add floating copy, edit, feedback, and custom action buttons to chat messages -icon: MousePointerClick ---- - -import { Callout } from 'fumadocs-ui/components/callout'; - - -**Beta** — This feature is in **alpha**. APIs may change before stable release. - - -A compound component API for registering floating action buttons on chat messages — copy, edit, feedback, or fully custom actions. Actions appear on hover, floating below the message bubble. Declarative, role-based, fully composable. - ---- - -## Quick Start - -```tsx - - - - sendFeedback({ messageId: message.id, type })} - /> - - - - - - -``` - - -If no `` children are declared, nothing changes — existing chat UI looks and behaves identically. - - ---- - -## Compound Components - -| Component | Description | -|-----------|-------------| -| `CopilotChat.MessageActions` | Registers actions for a role (`user` or `assistant`) | -| `CopilotChat.CopyAction` | Copy message to clipboard (with ✓ feedback) | -| `CopilotChat.EditAction` | Inline edit for user messages (wired to branching) | -| `CopilotChat.FeedbackAction` | Thumbs up / down | -| `CopilotChat.Action` | Fully custom action button | - ---- - -## Props Reference - -```tsx -// MessageActions -role: "user" | "assistant" - -// CopyAction -tooltip?: string -className?: string - -// EditAction -tooltip?: string -className?: string - -// FeedbackAction -onFeedback?: (message: ChatMessage, type: "helpful" | "not-helpful") => void -tooltip?: string -className?: string - -// Action (custom) -id?: string -icon: ReactNode -tooltip: string -onClick: (props: { message: ChatMessage }) => void -hidden?: boolean | ((props: { message: ChatMessage }) => boolean) -className?: string -``` - ---- - -## Examples - -### Copy + Feedback on assistant - -```tsx - - - - { - sendFeedback({ messageId: message.id, type }); - }} - /> - - -``` - -### Custom action - -```tsx - - - - } - tooltip="Share" - onClick={({ message }) => share(message.content)} - /> - - -``` - -### Conditional action (hide based on message content) - -```tsx - - - } - tooltip="Report" - hidden={({ message }) => !message.content} - onClick={({ message }) => report(message.id)} - /> - - -``` - -### Full setup — both roles - -```tsx - - - - log(msg.id, type)} /> - } - tooltip="Save" - onClick={({ message }) => save(message)} - /> - - - - - } - tooltip="Delete" - onClick={({ message }) => deleteMessage(message.id)} - /> - - -``` - ---- - -## Breaking Changes - -**None.** Purely additive. If no `MessageActions` children are declared, the chat UI is identical to before. diff --git a/apps/docs/content/docs/chat/meta.json b/apps/docs/content/docs/chat/meta.json index edc9c09..7709c7a 100644 --- a/apps/docs/content/docs/chat/meta.json +++ b/apps/docs/content/docs/chat/meta.json @@ -1,5 +1,5 @@ { "title": "Chat", - "icon": "MessageSquare", - "pages": ["index", "branching", "message-actions"] + "icon": "BubbleChat", + "pages": ["ui", "attachments", "storage"] } diff --git a/apps/docs/content/docs/chat-history.mdx b/apps/docs/content/docs/chat/storage.mdx similarity index 78% rename from apps/docs/content/docs/chat-history.mdx rename to apps/docs/content/docs/chat/storage.mdx index 9061439..f814d8d 100644 --- a/apps/docs/content/docs/chat-history.mdx +++ b/apps/docs/content/docs/chat/storage.mdx @@ -1,18 +1,100 @@ --- -title: Chat History -description: Save and restore chat conversations across sessions -icon: Database +title: Storage +description: Persist chat sessions, threads, and conversation history across reloads and devices --- import { Callout } from 'fumadocs-ui/components/callout'; import { Tabs, Tab } from 'fumadocs-ui/components/tabs'; import { Steps, Step } from 'fumadocs-ui/components/steps'; -Save and persist chat threads and messages so users can continue conversations across browser sessions, devices, or after logging out. +--- + +## Session Persistence + + +**Beta** — This feature is in **alpha**. APIs may change before stable release. + + +Persist the full conversation state — including compaction metadata and message history — across page reloads with zero extra code. + +### Basic Setup + +```typescript +useMessageHistory({ + persistSession: true, + storageKey: "my-app-chat", // default: "copilot-session" +}); +``` + +### What gets persisted + +| Data | Where | Notes | +|------|-------|-------| +| `compactionState` (small metadata) | `localStorage` | Sync, available immediately on cold start | +| `displayMessages` (can be large) | `IndexedDB` | Async, avoids localStorage quota issues | + +Both are keyed by `storageKey`. Multiple chat instances can coexist with different keys. + +### Clear Everything + +```typescript +const { resetSession } = useMessageHistory({ persistSession: true }); + +// Clears state AND storage +await resetSession(); +``` + +### Multiple Chat Instances + +```tsx +// Support chat — separate session + + + {/* useMessageHistory({ storageKey: "support-chat" }) */} + + + +// Sales chat — separate session + + + {/* useMessageHistory({ storageKey: "sales-chat" }) */} + + +``` + +### Full Setup Example + +```tsx +// app/layout.tsx +import { CopilotProvider } from "@yourgpt/copilot-sdk/react"; + +export default function RootLayout({ children }) { + return ( + console.log("Compacted:", e), + }} + > + {children} + + ); +} +``` --- -## Browser Storage +## Chat History + +Save and persist chat threads and messages so users can continue conversations across browser sessions, devices, or after logging out. + +### Browser Storage For simple browser-level persistence without server setup: @@ -25,7 +107,7 @@ For simple browser-level persistence without server setup: Data is stored in localStorage (~5MB limit, single device only). -### Storage quota and auto-eviction +#### Storage quota and auto-eviction When the browser's localStorage quota is exceeded (typically after many long conversations), the SDK automatically evicts the oldest threads to make room for new ones. The most recent threads are always preserved. @@ -44,9 +126,7 @@ This eviction happens silently — users won't see an error. If you need to moni Auto-eviction only applies to browser localStorage. Server persistence has no such limit — use it when conversation history must be retained long-term. ---- - -## Server Persistence +### Server Persistence Store threads in your own database for cross-device sync and user accounts. @@ -61,7 +141,7 @@ Store threads in your own database for cross-device sync and user accounts. /> ``` -### API Contract +#### API Contract Your endpoint must implement these routes: @@ -73,7 +153,7 @@ Your endpoint must implement these routes: | `PATCH` | `/api/threads/:id` | Update thread | | `DELETE` | `/api/threads/:id` | Delete thread | -### Implementation +#### Implementation @@ -328,4 +408,9 @@ interface WelcomeConfig { } ``` +--- + +## Related +- [Compaction](/docs/context/compaction) — how the session is summarized +- [Token Tracking](/docs/context/token-tracking) — monitor context window usage diff --git a/apps/docs/content/docs/ui.mdx b/apps/docs/content/docs/chat/ui.mdx similarity index 80% rename from apps/docs/content/docs/ui.mdx rename to apps/docs/content/docs/chat/ui.mdx index 6d9e314..c30ad4e 100644 --- a/apps/docs/content/docs/ui.mdx +++ b/apps/docs/content/docs/chat/ui.mdx @@ -1,7 +1,6 @@ --- title: Copilot UI description: Pre-built chat components and styling setup -icon: Palette --- import { Callout } from 'fumadocs-ui/components/callout'; @@ -332,8 +331,84 @@ import { --- +--- + +## Message Actions + +Add floating copy, edit, feedback, and custom buttons to messages. Actions appear on hover below each message bubble. + +```tsx + + + + sendFeedback({ messageId: message.id, type })} + /> + + + + + + +``` + +| Component | Description | +|-----------|-------------| +| `CopilotChat.MessageActions` | Registers actions for a role (`user` or `assistant`) | +| `CopilotChat.CopyAction` | Copy message to clipboard | +| `CopilotChat.EditAction` | Inline edit for user messages | +| `CopilotChat.FeedbackAction` | Thumbs up / down | +| `CopilotChat.Action` | Fully custom action button | + +```tsx +// Custom action + + + } + tooltip="Share" + onClick={({ message }) => share(message.content)} + /> + + +// Conditional — hide based on message content +} + tooltip="Report" + hidden={({ message }) => !message.content} + onClick={({ message }) => report(message.id)} +/> +``` + +### Props + +```tsx +// MessageActions +role: "user" | "assistant" + +// CopyAction / EditAction +tooltip?: string +className?: string + +// FeedbackAction +onFeedback?: (message: ChatMessage, type: "helpful" | "not-helpful") => void +tooltip?: string + +// Action (custom) +icon: ReactNode +tooltip: string +onClick: (props: { message: ChatMessage }) => void +hidden?: boolean | ((props: { message: ChatMessage }) => boolean) +``` + + +If no `MessageActions` children are declared, the chat UI is identical to before — purely additive. + + +--- + ## Next Steps - [Customizations](/docs/customizations) - Create custom themes, CSS classes, branding -- [Chat](/docs/chat) - Chat component props and configuration -- [Generative UI](/docs/generative-ui) - Render custom components from AI +- [Generative UI](/docs/chat/generative-ui) - Render custom components from AI +- [Branching](/docs/advanced/branching) - Conversation branching and edit history diff --git a/apps/docs/content/docs/context/index.mdx b/apps/docs/content/docs/context/index.mdx deleted file mode 100644 index 2e5df51..0000000 --- a/apps/docs/content/docs/context/index.mdx +++ /dev/null @@ -1,251 +0,0 @@ ---- -title: Context Management -description: Make AI aware of your application state and manage the context window -icon: Lightbulb ---- - -import { Callout } from 'fumadocs-ui/components/callout'; - -Give the AI awareness of your application state so it can provide relevant, contextual responses. The SDK also provides advanced context window management for long conversations. - ---- - -## Application Context - -Inject your app state into the AI's context so it can answer questions about what's happening in your app. - -### Without context - -``` -User: "Is this in stock?" -AI: "I don't know what product you're referring to." -``` - -### With context - -```tsx -useAIContext({ - key: 'current-product', - data: { name: 'Wireless Headphones', stock: 42, price: 79.99 }, -}); -``` - -``` -User: "Is this in stock?" -AI: "Yes! The Wireless Headphones are in stock with 42 units available at $79.99." -``` - ---- - -## useAIContext - -Register a single context: - -```tsx -import { useAIContext } from '@yourgpt/copilot-sdk/react'; - -function ProductPage({ product }) { - useAIContext({ - key: 'current-product', - data: { - id: product.id, - name: product.name, - price: product.price, - description: product.description, - inStock: product.inventory > 0, - category: product.category, - }, - description: 'The product the user is currently viewing', - }); - - return ; -} -``` - -### Parameters - -| Parameter | Type | Required | Description | -|-----------|------|----------|-------------| -| `key` | `string` | Yes | Unique identifier for this context | -| `data` | `any` | Yes | The data to expose to AI | -| `description` | `string` | No | Helps AI understand when to use this context | -| `parentId` | `string` | No | For hierarchical contexts | - -### Returns - -Returns a context ID string that can be used as `parentId` for nested contexts. - ---- - -## useAIContexts - -Register multiple contexts at once: - -```tsx -import { useAIContexts } from '@yourgpt/copilot-sdk/react'; - -function AppContext() { - useAIContexts([ - { - key: 'user', - data: { - name: user.name, - email: user.email, - plan: user.subscription, - role: user.role, - }, - description: 'Current logged-in user information', - }, - { - key: 'cart', - data: { - items: cart.items, - total: cart.total, - itemCount: cart.items.length, - }, - description: 'Shopping cart contents', - }, - { - key: 'page', - data: { - route: router.pathname, - params: router.query, - }, - description: 'Current page location', - }, - ]); - - return null; -} -``` - ---- - -## Hierarchical Contexts - -Create parent-child relationships for complex data: - -```tsx -function TeamDashboard({ team }) { - const teamContextId = useAIContext({ - key: 'team', - data: { name: team.name, memberCount: team.members.length }, - description: 'The team being viewed', - }); - - return ( -
-

{team.name}

- {team.members.map(member => ( - - ))} -
- ); -} - -function MemberCard({ member, parentId }) { - useAIContext({ - key: `member-${member.id}`, - data: { name: member.name, role: member.role, tasks: member.tasks }, - description: `Team member: ${member.name}`, - parentId, - }); - - return
{member.name}
; -} -``` - - -Hierarchical contexts help AI understand relationships. When a user asks about "John's tasks", AI knows John is part of the team context. - - ---- - -## Dynamic Context Updates - -Context updates automatically when data changes: - -```tsx -function LiveDashboard() { - const [metrics, setMetrics] = useState(null); - - useEffect(() => { - const interval = setInterval(async () => { - const data = await fetchMetrics(); - setMetrics(data); - }, 5000); - return () => clearInterval(interval); - }, []); - - // Context auto-updates when metrics change - useAIContext({ - key: 'live-metrics', - data: metrics, - description: 'Real-time dashboard metrics (updates every 5s)', - }); - - return ; -} -``` - ---- - -## Context Cleanup - -Contexts are automatically removed when components unmount: - -```tsx -function ConditionalContext({ showDetails }) { - if (showDetails) { - return ; - } - return null; -} - -function DetailedContext() { - useAIContext({ - key: 'details', - data: { /* ... */ }, - }); - // Context removed when this component unmounts - return
; -} -``` - ---- - -## What to Include in Context - -```tsx -// ✅ Good — relevant, scoped data -useAIContext({ - key: 'order-details', - data: { - orderId: order.id, - status: order.status, - items: order.items.map(i => ({ name: i.name, qty: i.qty })), - total: order.total, - }, -}); -``` - - -Never include sensitive data (passwords, API keys, credit cards) in AI context. The context is sent to the LLM provider. - - ---- - -## Advanced Context Window Management - -For long conversations, the SDK provides tools to control what the AI sees and how history is managed. - -- **[Compaction](/docs/context/compaction)** — auto-summarize old messages to stay within token limits -- **[Token Tracking](/docs/context/token-tracking)** — monitor context window usage with `useContextStats` -- **[Session Persistence](/docs/context/session)** — survive page reloads and compact on the server - ---- - -## Next Steps - -- [Custom Tools](/docs/tools) - Build tools that use context -- [Compaction](/docs/context/compaction) - Manage long conversation history diff --git a/apps/docs/content/docs/context/meta.json b/apps/docs/content/docs/context/meta.json deleted file mode 100644 index 00cb426..0000000 --- a/apps/docs/content/docs/context/meta.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "title": "Context Management", - "icon": "Lightbulb", - "pages": ["compaction", "token-tracking", "session"] -} diff --git a/apps/docs/content/docs/context/session.mdx b/apps/docs/content/docs/context/session.mdx deleted file mode 100644 index 9cb70c8..0000000 --- a/apps/docs/content/docs/context/session.mdx +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: Session Persistence -description: Survive page reloads and persist conversation state across sessions ---- - -import { Callout } from 'fumadocs-ui/components/callout'; - - -**Beta** — This feature is in **alpha**. APIs may change before stable release. - - -Persist the full conversation state — including compaction metadata and message history — across page reloads with zero extra code. - ---- - -## Basic Setup - -```typescript -useMessageHistory({ - persistSession: true, - storageKey: "my-app-chat", // default: "copilot-session" -}); -``` - -### What gets persisted - -| Data | Where | Notes | -|------|-------|-------| -| `compactionState` (small metadata) | `localStorage` | Sync, available immediately on cold start | -| `displayMessages` (can be large) | `IndexedDB` | Async, avoids localStorage quota issues | - -Both are keyed by `storageKey`. Multiple chat instances can coexist with different keys. - ---- - -## Clear Everything - -```typescript -const { resetSession } = useMessageHistory({ persistSession: true }); - -// Clears state AND storage -await resetSession(); -``` - ---- - -## Multiple Chat Instances - -```tsx -// Support chat — separate session - - - {/* useMessageHistory({ storageKey: "support-chat" }) */} - - - -// Sales chat — separate session - - - {/* useMessageHistory({ storageKey: "sales-chat" }) */} - - -``` - ---- - -## Full Setup Example - -```tsx -// app/layout.tsx -import { CopilotProvider } from "@yourgpt/copilot-sdk/react"; - -export default function RootLayout({ children }) { - return ( - console.log("Compacted:", e), - }} - > - {children} - - ); -} -``` - ---- - -## Related - -- [Compaction](/docs/context/compaction) — how the session is summarized -- [Token Tracking](/docs/context/token-tracking) — monitor context window usage diff --git a/apps/docs/content/docs/headless.mdx b/apps/docs/content/docs/customizations/headless.mdx similarity index 99% rename from apps/docs/content/docs/headless.mdx rename to apps/docs/content/docs/customizations/headless.mdx index 32eed7d..6873a73 100644 --- a/apps/docs/content/docs/headless.mdx +++ b/apps/docs/content/docs/customizations/headless.mdx @@ -1,7 +1,6 @@ --- title: Headless Copilot description: Build fully custom chat UIs using raw SDK primitives — no built-in components required -icon: Layers --- import { Callout } from 'fumadocs-ui/components/callout'; diff --git a/apps/docs/content/docs/customizations/index.mdx b/apps/docs/content/docs/customizations/index.mdx index e236ed7..78de9c6 100644 --- a/apps/docs/content/docs/customizations/index.mdx +++ b/apps/docs/content/docs/customizations/index.mdx @@ -1,7 +1,6 @@ --- title: Customizations description: Create custom themes, extend components, and brand your copilot -icon: Paintbrush --- import { Callout } from 'fumadocs-ui/components/callout'; diff --git a/apps/docs/content/docs/customizations/meta.json b/apps/docs/content/docs/customizations/meta.json index df28bea..9fde28f 100644 --- a/apps/docs/content/docs/customizations/meta.json +++ b/apps/docs/content/docs/customizations/meta.json @@ -1,5 +1,5 @@ { "title": "Customizations", - "icon": "Paintbrush", - "pages": ["css-classes", "chat-primitives", "custom-message-view"] + "icon": "MagicWand", + "pages": ["headless", "css-classes", "chat-primitives", "custom-message-view"] } diff --git a/apps/docs/content/docs/deploy.mdx b/apps/docs/content/docs/deploy.mdx index 82f8171..f8c195f 100644 --- a/apps/docs/content/docs/deploy.mdx +++ b/apps/docs/content/docs/deploy.mdx @@ -1,7 +1,6 @@ --- title: Deploy description: Deploy your Copilot backend to any platform -icon: Rocket --- import { Callout } from 'fumadocs-ui/components/callout'; diff --git a/apps/docs/content/docs/generative-ui.mdx b/apps/docs/content/docs/generative-ui.mdx index 32c418f..b277df2 100644 --- a/apps/docs/content/docs/generative-ui.mdx +++ b/apps/docs/content/docs/generative-ui.mdx @@ -1,44 +1,41 @@ --- title: Generative UI -description: Render custom React components from tool results -icon: Blocks +description: Render rich React components from AI tool results — per-tool custom renderers or AI-driven built-in components +icon: AiMagic --- import { Callout } from 'fumadocs-ui/components/callout'; +import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; -Transform tool results into rich, interactive React components instead of plain text. +Instead of showing raw JSON or plain text from tool calls, render interactive UI directly inside the chat — from your own branded React components per tool, to fully AI-generated dashboards, charts, and layouts running in a sandboxed iframe. --- -## Overview +## Two Approaches -When AI calls a tool, instead of showing raw JSON, you can render custom UI: - -``` -User: "What's the weather in Miami?" - ↓ -AI calls: get_weather({ city: "Miami" }) - ↓ -Tool returns: { temp: 82, conditions: "Sunny" } - ↓ -UI renders: [Beautiful weather card component] -``` +| | `toolRenderers` | `useGenerativeUI` (experimental) | +|---|---|---| +| **What it does** | Your React component renders per tool result | AI writes full HTML + Tailwind + Chart.js, runs in a sandboxed iframe — or picks a typed renderer (table, stat, card, chart) | +| **Who decides the UI** | You — one renderer per tool | The AI — generates or selects based on the data | +| **Setup** | Pass `toolRenderers` to `` | One `useGenerativeUI()` call + backend `generativeUITool()` | +| **Best for** | Domain-specific, branded components | Dashboards, charts, tables, any data layout you haven't pre-built | +| **Customization** | Full control | Override any built-in renderer | --- -## Basic Setup +## Approach 1 — `toolRenderers` + +Map tool names to React components. Each component receives the tool's args and result as props. -### 1. Define Tool Renderers +### Basic example ```tsx -import { CopilotChat } from '@yourgpt/copilot-sdk/ui'; +import { CopilotChat } from "@yourgpt/copilot-sdk/ui"; -// Custom component for weather results function WeatherCard({ data, status }) { - if (status === 'executing') { + if (status === "executing") { return
Loading weather...
; } - return (

{data.city}

@@ -48,74 +45,35 @@ function WeatherCard({ data, status }) { ); } -// Pass to CopilotChat ``` -### 2. Register the Tool - -```tsx -useTools({ - get_weather: { - description: 'Get current weather for a city', - parameters: z.object({ - city: z.string(), - }), - handler: async ({ city }) => { - const weather = await fetchWeather(city); - return { - success: true, - data: { - city, - temp: weather.temperature, - conditions: weather.conditions, - }, - }; - }, - }, -}); -``` - ---- - -## ToolRendererProps +### ToolRendererProps -Every tool renderer receives these props: +Every renderer receives these props: ```typescript interface ToolRendererProps { - // Current execution status - status: 'pending' | 'executing' | 'completed' | 'error' | 'failed' | 'rejected'; - - // Arguments passed to the tool - args: Record; - - // Result data (when completed) - data?: unknown; - - // Error message (when failed) - error?: string; - - // Unique execution ID + status: "pending" | "executing" | "completed" | "error" | "failed" | "rejected"; + args: Record; // arguments passed to the tool + data?: unknown; // result (when completed) + error?: string; // error message (when failed) executionId: string; - - // Tool name toolName: string; } ``` ---- - -## Handling All States +### Handling all states ```tsx function ChartCard({ status, data, error, args }: ToolRendererProps) { - // Loading state - if (status === 'pending' || status === 'executing') { + if (status === "pending" || status === "executing") { return (
@@ -126,8 +84,7 @@ function ChartCard({ status, data, error, args }: ToolRendererProps) { ); } - // Error state - if (status === 'error' || status === 'failed') { + if (status === "error" || status === "failed") { return (

Failed to load chart

@@ -136,8 +93,7 @@ function ChartCard({ status, data, error, args }: ToolRendererProps) { ); } - // Rejected (user denied approval) - if (status === 'rejected') { + if (status === "rejected") { return (

Chart request was declined

@@ -145,7 +101,6 @@ function ChartCard({ status, data, error, args }: ToolRendererProps) { ); } - // Success state return (

{data.title}

@@ -161,44 +116,20 @@ function ChartCard({ status, data, error, args }: ToolRendererProps) { } ``` ---- - -## Multiple Tool Renderers - -```tsx - -``` - ---- - -## Interactive Components +### Interactive components -Tool renderers can be fully interactive: +Renderers can be fully interactive and call back into the chat: ```tsx function ProductCard({ data }: ToolRendererProps) { const [quantity, setQuantity] = useState(1); const { sendMessage } = useCopilot(); - const handleAddToCart = () => { - // Trigger AI to call add_to_cart tool - sendMessage(`Add ${quantity} of ${data.name} to my cart`); - }; - return (
{data.name}

{data.name}

${data.price}

-
setQuantity(Number(e.target.value))} className="w-16 border rounded px-2 py-1" /> -
@@ -215,111 +149,193 @@ function ProductCard({ data }: ToolRendererProps) { } ``` ---- - -## With AI Response Control +### Control AI response verbosity -Combine with `_aiResponseMode` to control AI behavior: +Return `_aiResponseMode: "brief"` from your tool handler to prevent the AI from describing what the UI already shows: ```tsx -useTools({ - show_dashboard: { - description: 'Display analytics dashboard', - parameters: z.object({ timeRange: z.string() }), - handler: async ({ timeRange }) => { - const data = await fetchDashboardData(timeRange); - return { - success: true, - data, - // Tell AI to be brief - UI speaks for itself - _aiResponseMode: 'brief', - _aiContext: `Dashboard displayed for ${timeRange}`, - }; - }, - }, -}); +handler: async ({ timeRange }) => { + const data = await fetchDashboardData(timeRange); + return { + success: true, + data, + _aiResponseMode: "brief", + _aiContext: `Dashboard for ${timeRange}`, + }; +}, ``` -Use `_aiResponseMode: 'brief'` when your UI component is self-explanatory. The AI will give a short acknowledgment instead of describing the data. +Use `_aiResponseMode: "brief"` when your UI component is self-explanatory. The AI gives a short acknowledgment instead of narrating the data. --- -## Best Practices +## Approach 2 — AI-Generated UI (Experimental) -1. **Handle all states** - Show loading, error, and success states -2. **Keep it focused** - One component per tool, single responsibility -3. **Make it responsive** - Components appear inline with chat messages -4. **Use AI Response Control** - Prevent AI from redundantly describing visual data -5. **Add interactivity** - Let users take actions directly from the UI + +`@yourgpt/copilot-sdk/experimental` — APIs may change without a semver major bump. + ---- +The AI calls a single `render_ui` tool and generates the UI itself. The standout capability is `type: "html"` — the AI writes full HTML with Tailwind CSS and Chart.js, rendered in a sandboxed iframe. No pre-built component needed. For structured data it can also pick typed renderers (`table`, `stat`, `card`, `chart`) automatically. -## Example: Complete Weather Tool +``` +User: "Show Q1 revenue by region" + ↓ +AI calls: render_ui({ type: "chart", chartType: "bar", labels: ["NA","EU","APAC"], datasets: [...] }) + ↓ +UI renders: [Bar chart] -```tsx -// Tool definition -useTools({ - get_weather: { - description: 'Get current weather for any city', - parameters: z.object({ - city: z.string().describe('City name'), - units: z.enum(['celsius', 'fahrenheit']).optional(), - }), - handler: async ({ city, units = 'fahrenheit' }) => { - const weather = await fetchWeatherAPI(city, units); - return { - success: true, - data: { - city, - temp: weather.temperature, - conditions: weather.conditions, - humidity: weather.humidity, - wind: weather.wind, - units, - }, - _aiResponseMode: 'brief', - _aiContext: `Weather: ${weather.temperature}° ${weather.conditions} in ${city}`, - }; +User: "Build an analytics dashboard" + ↓ +AI calls: render_ui({ type: "html", html: "
...
", height: "600px" }) + ↓ +UI renders: [Full dashboard in sandboxed iframe with Tailwind + Chart.js] +``` + +### Setup + + + + +Register `generativeUITool()` in your route. The key becomes the tool name. + +```typescript +import { generativeUITool } from "@yourgpt/copilot-sdk/experimental"; +import { streamText } from "@yourgpt/llm-sdk"; + +export async function POST(req: Request) { + const { messages } = await req.json(); + + const result = await streamText({ + model: openai("gpt-4o"), + system: "Use render_ui for any data, charts, or structured results.", + messages, + tools: { + render_ui: generativeUITool(), }, - }, -}); + }); -// Renderer component -function WeatherCard({ data, status }: ToolRendererProps) { - if (status !== 'completed') { - return ; - } + return result.toDataStreamResponse(); +} +``` - const { city, temp, conditions, humidity, wind, units } = data; - const tempUnit = units === 'celsius' ? '°C' : '°F'; + + - return ( -
-
-
-

{city}

-

{temp}{tempUnit}

-
- -
+Call `useGenerativeUI()` once in your component tree — it registers the renderer automatically. + +```tsx +import { useGenerativeUI } from "@yourgpt/copilot-sdk/experimental"; +import { CopilotChat } from "@yourgpt/copilot-sdk/ui"; + +function App() { + useGenerativeUI({ + chartRenderer: MyChartComponent, // required for chart type + }); + + return ; +} +``` + + + -

{conditions}

+### Built-in component types -
- 💧 {humidity}% - 💨 {wind} mph +| Type | When the AI uses it | Renderer | +|------|-------------------|----------| +| `html` | Dashboards, custom layouts, anything freeform | `HtmlRenderer` — sandboxed `