mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
Add v1 reference selector and content
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import type { Metadata } from "next";
|
||||
import type React from "react";
|
||||
import Link from "next/link";
|
||||
import { notFound } from "next/navigation";
|
||||
import { MDXRemote } from "next-mdx-remote/rsc";
|
||||
import matter from "gray-matter";
|
||||
import { LinkIcon } from "lucide-react";
|
||||
import { PropertyReference } from "@/components/property-reference";
|
||||
import {
|
||||
Callout,
|
||||
@@ -19,14 +21,16 @@ import {
|
||||
DocsDescription,
|
||||
} from "fumadocs-ui/page";
|
||||
import { ShellDocsLayout } from "@/components/shell-docs-layout";
|
||||
import type * as PageTree from "fumadocs-core/page-tree";
|
||||
import { ReferenceVersionSelector } from "@/components/reference-version-selector";
|
||||
import {
|
||||
REFERENCE_CONTENT_DIR,
|
||||
loadAllReferenceItems,
|
||||
REFERENCE_VERSIONS,
|
||||
buildReferencePageTree,
|
||||
referenceHref,
|
||||
referenceStaticParams,
|
||||
referenceVersionHref,
|
||||
resolveReferencePage,
|
||||
} from "@/lib/reference-items";
|
||||
import { stripLeadingImports } from "@/lib/docs-render";
|
||||
import { safeReadFileSync } from "@/lib/safe-fs";
|
||||
import { buildDocMetadata } from "@/lib/seo-metadata";
|
||||
|
||||
// Self-canonical for /reference/<slug>. Reference pages are not
|
||||
@@ -41,11 +45,8 @@ export async function generateMetadata({
|
||||
params: Promise<{ slug: string[] }>;
|
||||
}): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const slugPath = slug.join("/");
|
||||
const canonicalPath = `/reference/${slugPath}`;
|
||||
// Read the reference MDX directly to extract frontmatter. Reuse
|
||||
// safeReadFileSync so a crafted slug can't escape REFERENCE_CONTENT_DIR.
|
||||
const raw = safeReadFileSync(REFERENCE_CONTENT_DIR, `${slugPath}.mdx`);
|
||||
const resolved = resolveReferencePage(slug);
|
||||
const raw = resolved?.raw ?? null;
|
||||
let title: string | undefined;
|
||||
let description: string | undefined;
|
||||
if (raw !== null) {
|
||||
@@ -64,7 +65,9 @@ export async function generateMetadata({
|
||||
return buildDocMetadata({
|
||||
title: title ?? slug[slug.length - 1],
|
||||
description,
|
||||
canonicalPath,
|
||||
canonicalPath: resolved
|
||||
? referenceHref(resolved.version, resolved.pageSlug)
|
||||
: `/reference/${slug.join("/")}`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,6 +80,12 @@ const mdxComponents = {
|
||||
Accordions,
|
||||
Accordion,
|
||||
OpsPlatformCTA,
|
||||
LinkIcon,
|
||||
Frame: ({ children }: { children: React.ReactNode }) => (
|
||||
<div className="my-6 rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] p-4">
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
// Strip unknown imports — MDX import statements become no-ops in next-mdx-remote
|
||||
};
|
||||
|
||||
@@ -90,15 +99,12 @@ export default async function ReferenceSlugPage({
|
||||
params: Promise<{ slug: string[] }>;
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
const slugPath = slug.join("/");
|
||||
// slugPath is user-supplied (URL segments). Route the filesystem read
|
||||
// through safeReadFileSync so crafted paths like `..%2F..%2Fsecrets`
|
||||
// can't escape REFERENCE_CONTENT_DIR.
|
||||
const raw = safeReadFileSync(REFERENCE_CONTENT_DIR, `${slugPath}.mdx`);
|
||||
if (raw === null) {
|
||||
const resolved = resolveReferencePage(slug);
|
||||
if (resolved === null) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const { version, pageSlug, contentSlug, raw } = resolved;
|
||||
let content = "";
|
||||
let data: Record<string, unknown> = {};
|
||||
try {
|
||||
@@ -107,7 +113,7 @@ export default async function ReferenceSlugPage({
|
||||
data = parsed.data;
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[reference] Failed to parse frontmatter in ${slugPath}.mdx:`,
|
||||
`[reference] Failed to parse frontmatter in ${contentSlug}.mdx:`,
|
||||
err,
|
||||
);
|
||||
notFound();
|
||||
@@ -115,35 +121,28 @@ export default async function ReferenceSlugPage({
|
||||
|
||||
const cleanedContent = stripLeadingImports(content);
|
||||
|
||||
const allItems = loadAllReferenceItems();
|
||||
const title =
|
||||
typeof data.title === "string" && data.title.length > 0
|
||||
? data.title
|
||||
: slug[slug.length - 1];
|
||||
const description =
|
||||
typeof data.description === "string" ? data.description : undefined;
|
||||
|
||||
// Build a Fumadocs PageTree from the reference items, grouped by
|
||||
// category. Reference's IA is its own (Components / Hooks) — we don't
|
||||
// share the docs nav tree here.
|
||||
const pageTree: PageTree.Root = {
|
||||
name: "Reference",
|
||||
children: ["Components", "Hooks"].flatMap((cat) => [
|
||||
{ type: "separator" as const, name: cat },
|
||||
...allItems
|
||||
.filter((i) => i.category === cat)
|
||||
.map(
|
||||
(item): PageTree.Item => ({
|
||||
type: "page",
|
||||
name: item.title,
|
||||
url: `/reference/${item.slug}`,
|
||||
}),
|
||||
),
|
||||
]),
|
||||
};
|
||||
const pageTree = buildReferencePageTree(version);
|
||||
const versionOptions = REFERENCE_VERSIONS.map((referenceVersion) => ({
|
||||
version: referenceVersion,
|
||||
href: referenceVersionHref(referenceVersion, pageSlug),
|
||||
}));
|
||||
|
||||
return (
|
||||
<ShellDocsLayout tree={pageTree}>
|
||||
<ShellDocsLayout
|
||||
tree={pageTree}
|
||||
banner={
|
||||
<ReferenceVersionSelector
|
||||
activeVersion={version}
|
||||
options={versionOptions}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DocsPage
|
||||
toc={[]}
|
||||
tableOfContent={{ enabled: false }}
|
||||
@@ -161,7 +160,15 @@ export default async function ReferenceSlugPage({
|
||||
Reference
|
||||
</Link>
|
||||
{" / "}
|
||||
<span className="capitalize">{slug[0]}</span>
|
||||
<span>{version}</span>
|
||||
{pageSlug && (
|
||||
<>
|
||||
{" / "}
|
||||
<span className="capitalize">
|
||||
{pageSlug.split("/")[0]}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<DocsTitle className="text-2xl font-bold">{title}</DocsTitle>
|
||||
{description && (
|
||||
|
||||
@@ -1,60 +1,50 @@
|
||||
import Link from "next/link";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import matter from "gray-matter";
|
||||
import { DocsPage } from "fumadocs-ui/page";
|
||||
import { ShellDocsLayout } from "@/components/shell-docs-layout";
|
||||
import type * as PageTree from "fumadocs-core/page-tree";
|
||||
import { ReferenceVersionSelector } from "@/components/reference-version-selector";
|
||||
import {
|
||||
REFERENCE_CONTENT_DIR,
|
||||
loadReferenceItems,
|
||||
loadAllReferenceItems,
|
||||
REFERENCE_CATEGORIES,
|
||||
REFERENCE_VERSIONS,
|
||||
buildReferencePageTree,
|
||||
loadReferenceVersionItems,
|
||||
referenceVersionHref,
|
||||
readReferenceIndexDescription,
|
||||
} from "@/lib/reference-items";
|
||||
import type { ReferenceCategory, ReferenceItem } from "@/lib/reference-items";
|
||||
|
||||
function displayTitle(item: ReferenceItem): string {
|
||||
if (item.category === "Components") return `<${item.title} />`;
|
||||
if (item.category === "Hooks") return `${item.title}()`;
|
||||
return item.title;
|
||||
}
|
||||
|
||||
function categoryItems(
|
||||
items: ReferenceItem[],
|
||||
category: ReferenceCategory,
|
||||
): ReferenceItem[] {
|
||||
return items.filter((item) => item.category === category);
|
||||
}
|
||||
|
||||
export default function ReferencePage() {
|
||||
const components = loadReferenceItems("components");
|
||||
const hooks = loadReferenceItems("hooks");
|
||||
const allItems = loadAllReferenceItems();
|
||||
|
||||
// Mirror the PageTree built by `/reference/[...slug]` so the sidebar
|
||||
// chrome is identical between the index and the per-item pages.
|
||||
const pageTree: PageTree.Root = {
|
||||
name: "Reference",
|
||||
children: ["Components", "Hooks"].flatMap((cat) => [
|
||||
{ type: "separator" as const, name: cat },
|
||||
...allItems
|
||||
.filter((i) => i.category === cat)
|
||||
.map(
|
||||
(item): PageTree.Item => ({
|
||||
type: "page",
|
||||
name: item.title,
|
||||
url: `/reference/${item.slug}`,
|
||||
}),
|
||||
),
|
||||
]),
|
||||
};
|
||||
|
||||
// Also load the index page frontmatter for the intro. Guarded so a
|
||||
// malformed frontmatter block falls back to a default rather than
|
||||
// crashing the whole index page.
|
||||
let intro = "API Reference for the next-generation CopilotKit React API.";
|
||||
const indexPath = path.join(REFERENCE_CONTENT_DIR, "index.mdx");
|
||||
if (fs.existsSync(indexPath)) {
|
||||
try {
|
||||
const { data } = matter(fs.readFileSync(indexPath, "utf-8"));
|
||||
if (typeof data.description === "string" && data.description.length > 0) {
|
||||
intro = data.description;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[reference] Failed to parse frontmatter in ${indexPath}:`,
|
||||
err,
|
||||
);
|
||||
}
|
||||
}
|
||||
const activeVersion = "v2";
|
||||
const allItems = loadReferenceVersionItems(activeVersion);
|
||||
const pageTree = buildReferencePageTree(activeVersion);
|
||||
const intro = readReferenceIndexDescription(activeVersion);
|
||||
const versionOptions = REFERENCE_VERSIONS.map((version) => ({
|
||||
version,
|
||||
href: referenceVersionHref(version),
|
||||
}));
|
||||
|
||||
return (
|
||||
<ShellDocsLayout tree={pageTree}>
|
||||
<ShellDocsLayout
|
||||
tree={pageTree}
|
||||
banner={
|
||||
<ReferenceVersionSelector
|
||||
activeVersion={activeVersion}
|
||||
options={versionOptions}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DocsPage
|
||||
toc={[]}
|
||||
tableOfContent={{ enabled: false }}
|
||||
@@ -68,55 +58,36 @@ export default function ReferencePage() {
|
||||
</h1>
|
||||
<p className="text-[var(--text-muted)] text-sm mb-10">{intro}</p>
|
||||
|
||||
<section className="mb-10">
|
||||
<h2 className="text-lg font-semibold text-[var(--text)] mb-4">
|
||||
UI Components
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{components.map((item) => (
|
||||
<Link
|
||||
key={item.slug}
|
||||
href={`/reference/${item.slug}`}
|
||||
className="block rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] p-4 hover:bg-[var(--bg-elevated)] transition-colors"
|
||||
>
|
||||
<div className="font-mono text-sm font-semibold text-[var(--accent)]">
|
||||
{"<"}
|
||||
{item.title}
|
||||
{" />"}
|
||||
</div>
|
||||
{item.description && (
|
||||
<div className="text-xs text-[var(--text-muted)] mt-1">
|
||||
{item.description}
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
{REFERENCE_CATEGORIES.map((category) => {
|
||||
const items = categoryItems(allItems, category);
|
||||
if (items.length === 0) return null;
|
||||
|
||||
<section>
|
||||
<h2 className="text-lg font-semibold text-[var(--text)] mb-4">
|
||||
Hooks
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{hooks.map((item) => (
|
||||
<Link
|
||||
key={item.slug}
|
||||
href={`/reference/${item.slug}`}
|
||||
className="block rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] p-4 hover:bg-[var(--bg-elevated)] transition-colors"
|
||||
>
|
||||
<div className="font-mono text-sm font-semibold text-[var(--accent)]">
|
||||
{item.title}()
|
||||
</div>
|
||||
{item.description && (
|
||||
<div className="text-xs text-[var(--text-muted)] mt-1">
|
||||
{item.description}
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
return (
|
||||
<section key={category} className="mb-10 last:mb-0">
|
||||
<h2 className="text-lg font-semibold text-[var(--text)] mb-4">
|
||||
{category === "Components" ? "UI Components" : category}
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{items.map((item) => (
|
||||
<Link
|
||||
key={item.slug}
|
||||
href={item.url}
|
||||
className="block rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] p-4 hover:bg-[var(--bg-elevated)] transition-colors"
|
||||
>
|
||||
<div className="font-mono text-sm font-semibold text-[var(--accent)]">
|
||||
{displayTitle(item)}
|
||||
</div>
|
||||
{item.description && (
|
||||
<div className="text-xs text-[var(--text-muted)] mt-1">
|
||||
{item.description}
|
||||
</div>
|
||||
)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</DocsPage>
|
||||
</ShellDocsLayout>
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { ChevronDown } from "lucide-react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
export type ReferenceVersion = "v2" | "v1";
|
||||
|
||||
export type ReferenceVersionOption = {
|
||||
version: ReferenceVersion;
|
||||
href: string;
|
||||
};
|
||||
|
||||
const VERSION_LABELS: Record<ReferenceVersion, string> = {
|
||||
v2: "v2",
|
||||
v1: "v1",
|
||||
};
|
||||
|
||||
export function ReferenceVersionSelector({
|
||||
activeVersion,
|
||||
options,
|
||||
}: {
|
||||
activeVersion: ReferenceVersion;
|
||||
options: ReferenceVersionOption[];
|
||||
}) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
|
||||
const handleClick = (event: MouseEvent) => {
|
||||
const target = event.target instanceof Node ? event.target : null;
|
||||
if (!target) return;
|
||||
if (
|
||||
panelRef.current?.contains(target) ||
|
||||
buttonRef.current?.contains(target)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
setOpen(false);
|
||||
};
|
||||
const handleKey = (event: KeyboardEvent) => {
|
||||
if (event.key === "Escape") setOpen(false);
|
||||
};
|
||||
|
||||
document.addEventListener("mousedown", handleClick);
|
||||
document.addEventListener("keydown", handleKey);
|
||||
return () => {
|
||||
document.removeEventListener("mousedown", handleClick);
|
||||
document.removeEventListener("keydown", handleKey);
|
||||
};
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<div className="sticky top-0 z-10 bg-[var(--bg-surface)] backdrop-blur-lg">
|
||||
<div className="relative">
|
||||
<button
|
||||
ref={buttonRef}
|
||||
type="button"
|
||||
onClick={() => setOpen((value) => !value)}
|
||||
aria-haspopup="listbox"
|
||||
aria-expanded={open}
|
||||
className="flex h-12 w-full cursor-pointer items-center gap-2 rounded-xl border border-[var(--accent)] bg-[var(--accent-light)] p-1.5 text-[13px] font-medium text-[var(--text)] transition-colors hover:border-[var(--accent)]"
|
||||
>
|
||||
<span
|
||||
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-[var(--accent)]/25 text-base dark:bg-white/10"
|
||||
aria-hidden="true"
|
||||
>
|
||||
🪁
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 text-left">
|
||||
<span className="block truncate leading-tight">
|
||||
{VERSION_LABELS[activeVersion]}
|
||||
</span>
|
||||
<span className="mt-0.5 block text-[9px] uppercase leading-tight tracking-wider text-[var(--text-faint)]">
|
||||
API version
|
||||
</span>
|
||||
</span>
|
||||
<ChevronDown className="mr-0.5 h-3.5 w-3.5 shrink-0 text-[var(--text-muted)]" />
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<div
|
||||
ref={panelRef}
|
||||
role="listbox"
|
||||
className="absolute left-0 right-0 top-full z-50 mt-1 rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] p-2 shadow-lg"
|
||||
>
|
||||
{options.map(({ version, href }) => {
|
||||
const active = version === activeVersion;
|
||||
return (
|
||||
<Link
|
||||
key={version}
|
||||
href={href}
|
||||
aria-current={active ? "page" : undefined}
|
||||
onClick={() => setOpen(false)}
|
||||
className={[
|
||||
"flex w-full items-center gap-2 rounded px-2 py-1.5 text-[13px] transition-colors",
|
||||
active
|
||||
? "bg-[var(--accent-light)] text-[var(--accent)]"
|
||||
: "text-[var(--text-secondary)] hover:bg-[var(--bg-elevated)] hover:text-[var(--text)]",
|
||||
].join(" ")}
|
||||
>
|
||||
<span aria-hidden="true" className="shrink-0 text-sm">
|
||||
🪁
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 truncate">
|
||||
{VERSION_LABELS[version]}
|
||||
</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"title": "LLM Adapters",
|
||||
"pages": [
|
||||
"OpenAIAdapter",
|
||||
"OpenAIAssistantAdapter",
|
||||
"AnthropicAdapter",
|
||||
"LangChainAdapter",
|
||||
"GroqAdapter",
|
||||
"GoogleGenerativeAIAdapter"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"title": "Copilot Runtime",
|
||||
"pages": [
|
||||
"CopilotRuntime",
|
||||
"llm-adapters",
|
||||
"CopilotTask"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: All Chat Components
|
||||
---
|
||||
import { LinkIcon } from "lucide-react"
|
||||
|
||||
<Callout type="warning">
|
||||
v1 chat components are still supported, but we recommend using the [v2 chat components](/reference/v2/components/CopilotChat) for new projects.
|
||||
</Callout>
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="CopilotChat"
|
||||
description="The CopilotChat component, providing a chat interface for interacting with your copilot."
|
||||
href="/reference/v1/components/chat/CopilotChat"
|
||||
icon={<LinkIcon />}
|
||||
/>
|
||||
<Card
|
||||
title="CopilotPopup"
|
||||
description="The CopilotPopup component, providing a popup interface for interacting with your copilot."
|
||||
href="/reference/v1/components/chat/CopilotPopup"
|
||||
icon={<LinkIcon />}
|
||||
/>
|
||||
<Card
|
||||
title="CopilotSidebar"
|
||||
description="The CopilotSidebar component, providing a sidebar interface for interacting with your copilot."
|
||||
href="/reference/v1/components/chat/CopilotSidebar"
|
||||
icon={<LinkIcon />}
|
||||
/>
|
||||
</Cards>
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"title": "Chat Components",
|
||||
"pages": [
|
||||
"CopilotChat",
|
||||
"CopilotPopup",
|
||||
"CopilotSidebar"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"title": "UI Components",
|
||||
"pages": [
|
||||
"chat",
|
||||
"CopilotTextarea",
|
||||
"CopilotKit"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"title": "hooks",
|
||||
"pages": [
|
||||
"useAgent",
|
||||
"useDefaultTool",
|
||||
"useFrontendTool",
|
||||
"useRenderToolCall",
|
||||
"useHumanInTheLoop",
|
||||
"useCopilotReadable",
|
||||
"useCopilotAdditionalInstructions",
|
||||
"useCopilotChat",
|
||||
"useCopilotChatHeadless_c",
|
||||
"useCopilotChatSuggestions",
|
||||
"useCoAgent",
|
||||
"useCoAgentStateRender",
|
||||
"useLangGraphInterrupt",
|
||||
"useCopilotAction"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
---
|
||||
title: "useAgent"
|
||||
description: "React hook for accessing AG-UI agent instances"
|
||||
---
|
||||
|
||||
<Callout type="warning">
|
||||
`useAgent` is still supported, but we recommend migrating to [`useAgent`](/reference/v2/hooks/useAgent) from the v2 API (`@copilotkit/react-core/v2`).
|
||||
</Callout>
|
||||
|
||||
## Overview
|
||||
|
||||
`useAgent` is a React hook that returns an [AG-UI AbstractAgent](https://docs.ag-ui.com/sdk/js/client/abstract-agent) instance. The hook subscribes to agent state changes and triggers re-renders when the agent's state, messages, or execution status changes.
|
||||
|
||||
**Throws error** if no agent is configured with the specified `agentId`.
|
||||
|
||||
## Signature
|
||||
|
||||
```tsx
|
||||
function useAgent(options?: UseAgentOptions): { agent: AbstractAgent }
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
<PropertyReference name="options" type="UseAgentOptions">
|
||||
Configuration object for the hook.
|
||||
|
||||
<PropertyReference name="agentId" type="string" default='"default"'>
|
||||
ID of the agent to retrieve. Must match an agent configured in `CopilotKitProvider`.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="updates" type="UseAgentUpdate[]" default="[OnMessagesChanged, OnStateChanged, OnRunStatusChanged]">
|
||||
Controls which agent changes trigger component re-renders. Options:
|
||||
- `UseAgentUpdate.OnMessagesChanged` - Re-render when messages change
|
||||
- `UseAgentUpdate.OnStateChanged` - Re-render when state changes
|
||||
- `UseAgentUpdate.OnRunStatusChanged` - Re-render when execution status changes
|
||||
|
||||
Pass an empty array `[]` to prevent automatic re-renders.
|
||||
</PropertyReference>
|
||||
</PropertyReference>
|
||||
|
||||
## Return Value
|
||||
|
||||
<PropertyReference name="object" type="{ agent: AbstractAgent }">
|
||||
Object containing the agent instance.
|
||||
|
||||
<PropertyReference name="agent" type="AbstractAgent">
|
||||
The AG-UI agent instance. See [AbstractAgent documentation](https://docs.ag-ui.com/sdk/js/client/abstract-agent) for full interface details.
|
||||
|
||||
### Core Properties
|
||||
|
||||
<PropertyReference name="agentId" type="string | undefined">
|
||||
Unique identifier for the agent instance.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="description" type="string">
|
||||
Human-readable description of the agent's purpose.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="threadId" type="string">
|
||||
Unique identifier for the current conversation thread.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="messages" type="Message[]">
|
||||
Array of conversation messages. Each message contains:
|
||||
- `id: string` - Unique message identifier
|
||||
- `role: "user" | "assistant" | "system"` - Message role
|
||||
- `content: string` - Message content
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="state" type="any">
|
||||
Shared state object synchronized between application and agent. Both can read and modify this state.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="isRunning" type="boolean">
|
||||
Indicates whether the agent is currently executing.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="debug" type="boolean">
|
||||
Enables debug logging for agent events and execution.
|
||||
</PropertyReference>
|
||||
|
||||
### Methods
|
||||
|
||||
<PropertyReference name="runAgent" type="(options?: RunAgentOptions) => Promise<void>">
|
||||
Manually triggers agent execution.
|
||||
|
||||
**Parameters:**
|
||||
- `options.forwardedProps?: any` - Data to pass to the agent execution context
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
await agent.runAgent({
|
||||
forwardedProps: {
|
||||
command: { resume: "user response" }
|
||||
}
|
||||
});
|
||||
```
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="setState" type="(newState: any) => void">
|
||||
Updates the shared state. Changes are immediately available to both application and agent.
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
agent.setState({
|
||||
...agent.state,
|
||||
theme: "dark"
|
||||
});
|
||||
```
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="subscribe" type="(subscriber: AgentSubscriber) => { unsubscribe: () => void }">
|
||||
Subscribes to agent events. Returns cleanup function.
|
||||
|
||||
**Subscriber Events:**
|
||||
- `onCustomEvent?: ({ event: { name: string, value: any } }) => void` - Custom events (e.g., LangGraph interrupts)
|
||||
- `onRunStartedEvent?: () => void` - Agent execution starts
|
||||
- `onRunFinalized?: () => void` - Agent execution completes
|
||||
- `onStateChanged?: (state: any) => void` - State changes
|
||||
- `onMessagesChanged?: (messages: Message[]) => void` - Messages added/modified
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
const { unsubscribe } = agent.subscribe({
|
||||
onCustomEvent: ({ event }) => {
|
||||
console.log(event.name, event.value);
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
unsubscribe();
|
||||
```
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="addMessage" type="(message: Message) => void">
|
||||
Adds a single message to the conversation and notifies subscribers.
|
||||
|
||||
**Example:**
|
||||
```tsx
|
||||
agent.addMessage({
|
||||
id: crypto.randomUUID(),
|
||||
role: "user",
|
||||
content: "Hello"
|
||||
});
|
||||
```
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="addMessages" type="(messages: Message[]) => void">
|
||||
Adds multiple messages to the conversation and notifies subscribers once.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="setMessages" type="(messages: Message[]) => void">
|
||||
Replaces the entire message history with a new array of messages.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="connectAgent" type="(options?: RunAgentParameters) => Promise<RunAgentResult>">
|
||||
Connects to a streaming agent endpoint. Similar to `runAgent` but uses the `connect()` method for persistent connections.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="detachActiveRun" type="() => Promise<void>">
|
||||
Detaches from the currently active agent run without aborting it. The run continues in the background but stops updating the local agent state.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="abortRun" type="() => void">
|
||||
Aborts the currently running agent execution. Implementation varies by agent type.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="clone" type="() => AbstractAgent">
|
||||
Creates a deep copy of the agent with cloned messages, state, and configuration.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="use" type="(...middlewares: Middleware[]) => this">
|
||||
Adds middleware to the agent's execution pipeline. Middlewares can intercept and transform agent runs.
|
||||
</PropertyReference>
|
||||
|
||||
</PropertyReference>
|
||||
</PropertyReference>
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```tsx
|
||||
import { useAgent } from "@copilotkit/react-core/v2";
|
||||
|
||||
function AgentStatus() {
|
||||
const { agent } = useAgent();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Agent: {agent.agentId}</div>
|
||||
<div>Messages: {agent.messages.length}</div>
|
||||
<div>Running: {agent.isRunning ? "Yes" : "No"}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Accessing State
|
||||
|
||||
```tsx
|
||||
function StateDisplay() {
|
||||
const { agent } = useAgent();
|
||||
|
||||
return <pre>{JSON.stringify(agent.state, null, 2)}</pre>;
|
||||
}
|
||||
```
|
||||
|
||||
### Updating State
|
||||
|
||||
```tsx
|
||||
function StateController() {
|
||||
const { agent } = useAgent();
|
||||
|
||||
return (
|
||||
<button onClick={() => agent.setState({ ...agent.state, count: 1 })}>
|
||||
Increment
|
||||
</button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Event Subscription
|
||||
|
||||
```tsx
|
||||
import { useEffect } from "react";
|
||||
import { useAgent } from "@copilotkit/react-core/v2";
|
||||
import type { AgentSubscriber } from "@ag-ui/client";
|
||||
|
||||
function EventListener() {
|
||||
const { agent } = useAgent();
|
||||
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = agent.subscribe({
|
||||
onRunStartedEvent: () => console.log("Started"),
|
||||
onRunFinalized: () => console.log("Finished"),
|
||||
});
|
||||
|
||||
return unsubscribe;
|
||||
}, []);
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple Agents
|
||||
|
||||
```tsx
|
||||
function MultiAgentView() {
|
||||
const { agent: primary } = useAgent({ agentId: "primary" });
|
||||
const { agent: support } = useAgent({ agentId: "support" });
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>Primary: {primary.messages.length}</div>
|
||||
<div>Support: {support.messages.length}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Optimizing Re-renders
|
||||
|
||||
Control when your component re-renders using the `updates` parameter:
|
||||
|
||||
```tsx
|
||||
import { useAgent, UseAgentUpdate } from "@copilotkit/react-core/v2";
|
||||
|
||||
// Only re-render when messages change
|
||||
function MessageCount() {
|
||||
const { agent } = useAgent({
|
||||
updates: [UseAgentUpdate.OnMessagesChanged]
|
||||
});
|
||||
|
||||
return <div>Messages: {agent.messages.length}</div>;
|
||||
}
|
||||
|
||||
// Manually manage subscriptions (no automatic re-renders)
|
||||
function ManualSubscription() {
|
||||
const { agent } = useAgent({ updates: [] });
|
||||
|
||||
useEffect(() => {
|
||||
const { unsubscribe } = agent.subscribe({
|
||||
onMessagesChanged: () => {
|
||||
// Handle changes manually
|
||||
}
|
||||
});
|
||||
return unsubscribe;
|
||||
}, [agent]);
|
||||
|
||||
return <div>Manual mode</div>;
|
||||
}
|
||||
```
|
||||
|
||||
## Behavior
|
||||
|
||||
- **Automatic Re-renders**: Component re-renders when agent state, messages, or execution status changes (configurable via `updates` parameter)
|
||||
- **Error Handling**: Throws error if no agent exists with specified `agentId`
|
||||
- **State Synchronization**: State updates via `setState()` are immediately available to both app and agent
|
||||
- **Event Subscriptions**: Subscribe/unsubscribe pattern for lifecycle and custom events
|
||||
|
||||
## Related
|
||||
|
||||
- [AG-UI AbstractAgent](https://docs.ag-ui.com/sdk/js/client/abstract-agent) - Full agent interface documentation
|
||||
@@ -0,0 +1,199 @@
|
||||
---
|
||||
title: "useCopilotAction"
|
||||
description: "The useCopilotAction hook allows your copilot to take action in the app."
|
||||
---
|
||||
|
||||
<Callout type="warning">
|
||||
`useCopilotAction` is still supported, but we recommend migrating to [`useFrontendTool`](/reference/v2/hooks/useFrontendTool) from the v2 API.
|
||||
</Callout>
|
||||
|
||||
<br />
|
||||
<img src="https://cdn.copilotkit.ai/docs/copilotkit/images/use-copilot-action/useCopilotAction.gif" width="500" />
|
||||
|
||||
`useCopilotAction` is a React hook that you can use in your application to provide
|
||||
custom actions that can be called by the AI. Essentially, it allows the Copilot to
|
||||
execute these actions contextually during a chat, based on the user’s interactions
|
||||
and needs.
|
||||
|
||||
Here's how it works:
|
||||
|
||||
Use `useCopilotAction` to set up actions that the Copilot can call. To provide
|
||||
more context to the Copilot, you can provide it with a `description` (for example to explain
|
||||
what the action does, under which conditions it can be called, etc.).
|
||||
|
||||
Then you define the parameters of the action, which can be simple, e.g. primitives like strings or numbers,
|
||||
or complex, e.g. objects or arrays.
|
||||
|
||||
Finally, you provide a `handler` function that receives the parameters and returns a result.
|
||||
CopilotKit takes care of automatically inferring the parameter types, so you get type safety
|
||||
and autocompletion for free.
|
||||
|
||||
To render a custom UI for the action, you can provide a `render()` function. This function
|
||||
lets you render a custom component or return a string to display.
|
||||
|
||||
## Usage
|
||||
|
||||
### Simple Usage
|
||||
|
||||
```tsx
|
||||
useCopilotAction({
|
||||
name: "sayHello",
|
||||
description: "Say hello to someone.",
|
||||
parameters: [
|
||||
{
|
||||
name: "name",
|
||||
type: "string",
|
||||
description: "name of the person to say greet",
|
||||
},
|
||||
],
|
||||
handler: async ({ name }) => {
|
||||
alert(`Hello, ${name}!`);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Generative UI
|
||||
|
||||
This hooks enables you to dynamically generate UI elements and render them in the copilot chat. For more information, check out the [Generative UI](/generative-ui/your-components/display-only) page.
|
||||
|
||||
## Parameters
|
||||
|
||||
<PropertyReference name="action" type="Action" required>
|
||||
The function made available to the Copilot. See [Action](#action).
|
||||
|
||||
<PropertyReference name="name" type="string" required>
|
||||
The name of the action.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="handler" type="(args) => Promise<any>" required>
|
||||
The handler of the action.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="description" type="string">
|
||||
A description of the action. This is used to instruct the Copilot on how to
|
||||
use the action.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="available" type="'enabled' | 'disabled' | 'remote'">
|
||||
Use this property to control when the action is available to the Copilot. When set to `"remote"`, the action is
|
||||
available only for remote agents.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="followUp" type="boolean" default="true">
|
||||
Whether to report the result of a function call to the LLM which will then provide a follow-up response. Pass `false` to disable
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="parameters" type="Parameter[]">
|
||||
The parameters of the action. See [Parameter](#parameter).
|
||||
|
||||
<PropertyReference name="name" type="string" required>
|
||||
The name of the parameter.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference
|
||||
name="type"
|
||||
type="string"
|
||||
required
|
||||
>
|
||||
The type of the argument. One of:
|
||||
- `"string"`
|
||||
- `"number"`
|
||||
- `"boolean"`
|
||||
- `"object"`
|
||||
- `"object[]"`
|
||||
- `"string[]"`
|
||||
- `"number[]"`
|
||||
- `"boolean[]"`
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="description" type="string">
|
||||
A description of the argument. This is used to instruct the Copilot on what
|
||||
this argument is used for.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="enum" type="string[]">
|
||||
For string arguments, you can provide an array of possible values.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="required" type="boolean">
|
||||
Whether or not the argument is required. Defaults to true.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="attributes">
|
||||
If the argument is of a complex type, i.e. `object` or `object[]`, this field
|
||||
lets you define the attributes of the object. For example:
|
||||
```js
|
||||
{
|
||||
name: "addresses",
|
||||
description: "The addresses extracted from the text.",
|
||||
type: "object[]",
|
||||
attributes: [
|
||||
{
|
||||
name: "street",
|
||||
type: "string",
|
||||
description: "The street of the address.",
|
||||
},
|
||||
{
|
||||
name: "city",
|
||||
type: "string",
|
||||
description: "The city of the address.",
|
||||
},
|
||||
// ...
|
||||
],
|
||||
}
|
||||
````
|
||||
</PropertyReference>
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="render" type="string | (props: ActionRenderProps<T>) => string">
|
||||
Render lets you define a custom component or string to render instead of the
|
||||
default. You can either pass in a string or a function that takes the following props:
|
||||
|
||||
<div className="ml-8">
|
||||
<PropertyReference name="status" type="'inProgress' | 'executing' | 'complete'">
|
||||
- `"inProgress"`: arguments are dynamically streamed to the function, allowing you to adjust your UI in real-time.
|
||||
- `"executing"`: The action handler is executing.
|
||||
- `"complete"`: The action handler has completed execution.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="args" type="T">
|
||||
The arguments passed to the action in real time. When the status is `"inProgress"`, they are
|
||||
possibly incomplete.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="result" type="any">
|
||||
The result returned by the action. It is only available when the status is `"complete"`.
|
||||
</PropertyReference>
|
||||
</div>
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="renderAndWaitForResponse" type="(props: ActionRenderPropsWait<T>) => React.ReactElement">
|
||||
This is similar to `render`, but provides a `respond` function in the props that you must call with the user's response. The component will remain rendered until `respond` is called. The response will be passed as the result to the action handler.
|
||||
<div className="ml-8">
|
||||
<PropertyReference name="status" type="'inProgress' | 'executing' | 'complete'">
|
||||
- `"inProgress"`: arguments are dynamically streamed to the function, allowing you to adjust your UI in real-time.
|
||||
- `"executing"`: The action handler is executing.
|
||||
- `"complete"`: The action handler has completed execution.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="args" type="T">
|
||||
The arguments passed to the action in real time. When the status is `"inProgress"`, they are
|
||||
possibly incomplete.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="respond" type="(result: any) => void">
|
||||
A function that must be called with the user's response. The response will be passed as the result to the action handler.
|
||||
Only available when status is `"executing"`.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="result" type="any">
|
||||
The result returned by the action. It is only available when the status is `"complete"`.
|
||||
</PropertyReference>
|
||||
</div>
|
||||
</PropertyReference>
|
||||
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="dependencies" type="any[]">
|
||||
An optional array of dependencies.
|
||||
</PropertyReference>
|
||||
@@ -0,0 +1,140 @@
|
||||
---
|
||||
title: "useDefaultTool"
|
||||
description: "The useDefaultTool hook enables rendering of a default UI which catches any tool that does not have a specific renderer."
|
||||
---
|
||||
|
||||
<Callout type="warning">
|
||||
`useDefaultTool` is still supported, but we recommend migrating to [`useFrontendTool`](/reference/v2/hooks/useFrontendTool) from the v2 API.
|
||||
</Callout>
|
||||
|
||||
`useDefaultTool` is a React hook that allows you to render custom UI for any tool
|
||||
call that doesn't have a specific renderer
|
||||
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { useDefaultTool } from "@copilotkit/react-core";
|
||||
|
||||
useDefaultTool({
|
||||
render: ({ name, args, status, result }) => {
|
||||
return (
|
||||
<div className="p-4 border rounded my-2">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h4 className="font-semibold">{name}</h4>
|
||||
<span className="text-sm text-gray-500">
|
||||
{status === "inProgress" && "Running..."}
|
||||
{status === "executing" && "Executing..."}
|
||||
{status === "complete" && "Complete"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{Object.keys(args).length > 0 && (
|
||||
<div className="mb-2">
|
||||
<p className="text-sm font-medium text-gray-600">Parameters:</p>
|
||||
<pre className="text-xs bg-gray-100 p-2 rounded mt-1">
|
||||
{JSON.stringify(args, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{status === "complete" && result && (
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Result:</p>
|
||||
<pre className="text-xs bg-gray-100 p-2 rounded mt-1">
|
||||
{JSON.stringify(result, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Rendering Model Context Protocol (MCP) Tools
|
||||
|
||||
```tsx
|
||||
import { useDefaultTool } from "@copilotkit/react-core";
|
||||
|
||||
// Render any MCP tool call with a custom UI
|
||||
useDefaultTool({
|
||||
render: ({ name, args, status, result }) => {
|
||||
// Custom rendering for MCP tools
|
||||
if (name.startsWith("mcp_")) {
|
||||
return <MCPToolRenderer name={name} args={args} status={status} result={result} />;
|
||||
}
|
||||
|
||||
// Default rendering for other tools
|
||||
return <DefaultToolRenderer name={name} args={args} status={status} result={result} />;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
<PropertyReference name="tool" type="ReactRenderToolCall<T>" required>
|
||||
The tool rendering configuration object.
|
||||
<PropertyReference name="render" type="React.ComponentType<RenderProps>">
|
||||
A React component that renders the tool call UI. The component receives props with:
|
||||
|
||||
<div className="ml-8">
|
||||
<PropertyReference name="status" type="'inProgress' | 'executing' | 'complete'">
|
||||
- `"inProgress"`: Tool is being prepared or arguments are being streamed.
|
||||
- `"executing"`: Tool is actively running.
|
||||
- `"complete"`: Tool execution has finished.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="args" type="Partial<T> | T | any">
|
||||
The arguments passed to the tool. Type-safe if parameters schema is provided.
|
||||
For catch-all renderers (`name: "*"`), this will be `any`.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="result" type="any">
|
||||
The result returned by the tool. Only available when status is `"complete"`.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="name" type="string">
|
||||
The actual name of the tool being executed. Particularly useful for
|
||||
catch-all renderers to know which tool is being rendered.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="description" type="string">
|
||||
The description of the tool being executed.
|
||||
</PropertyReference>
|
||||
</div>
|
||||
</PropertyReference>
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="dependencies" type="any[]">
|
||||
An optional array of dependencies.
|
||||
</PropertyReference>
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
1. **Backend Tool Visualization**: Display progress and results of long-running backend operations
|
||||
2. **Generic Tool Rendering**: Provide a fallback UI for any tool without specific rendering
|
||||
3. **MCP Tool Integration**: Render Model Context Protocol tools from various sources
|
||||
4. **Debugging**: Display all tool calls during development
|
||||
5. **Analytics**: Track and display tool usage
|
||||
|
||||
## Migration from useCopilotAction
|
||||
|
||||
If you're migrating from `useCopilotAction` with only a `render` function:
|
||||
|
||||
```tsx
|
||||
// Before with useCopilotAction
|
||||
useCopilotAction({
|
||||
render: ({ name, args, status, result }) => (
|
||||
<GenericToolCall name={name} args={args} status={status} result={result} />
|
||||
),
|
||||
});
|
||||
|
||||
// After with useDefaultTool
|
||||
useDefaultTool({
|
||||
render: ({ name, args, status, result }) => (
|
||||
<GenericToolCall name={name} args={args} status={status} result={result} />
|
||||
),
|
||||
});
|
||||
```
|
||||
|
||||
The migration is straightforward - just change the hook name. The render props remain the same.
|
||||
@@ -0,0 +1,187 @@
|
||||
---
|
||||
title: "useFrontendTool"
|
||||
description: "The useFrontendTool hook allows the Copilot to execute tools in the frontend."
|
||||
---
|
||||
|
||||
<Callout type="warning">
|
||||
`useFrontendTool` is still supported, but we recommend migrating to [`useFrontendTool`](/reference/v2/hooks/useFrontendTool) from the v2 API, which uses Zod schemas for parameters.
|
||||
</Callout>
|
||||
|
||||
`useFrontendTool` allows you to define executable actions that the AI can call with a handler function.
|
||||
This is the primary way to give your AI agent the ability to perform actions in your application—whether
|
||||
that's updating state, making API calls, or triggering side effects.
|
||||
|
||||
The hook requires three main pieces:
|
||||
1. A name and description so the AI knows when to call it
|
||||
2. A parameters definition describing what inputs the tool accepts
|
||||
3. A handler function that executes when the AI calls the tool
|
||||
|
||||
Optionally, you can provide a `render` function to display custom UI showing the tool's execution
|
||||
status and results in the chat interface.
|
||||
|
||||
## Usage
|
||||
|
||||
### Simple Usage
|
||||
|
||||
```tsx
|
||||
import { useFrontendTool } from "@copilotkit/react-core";
|
||||
|
||||
useFrontendTool({
|
||||
name: "sayHello",
|
||||
description: "Say hello to someone.",
|
||||
parameters: [
|
||||
{
|
||||
name: "name",
|
||||
type: "string",
|
||||
description: "name of the person to greet",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
handler: async ({ name }) => {
|
||||
alert(`Hello, ${name}!`);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### With Custom UI Rendering
|
||||
|
||||
```tsx
|
||||
import { useFrontendTool } from "@copilotkit/react-core";
|
||||
|
||||
useFrontendTool({
|
||||
name: "showWeatherCard",
|
||||
description: "Display weather information for a location",
|
||||
parameters: [
|
||||
{
|
||||
name: "location",
|
||||
type: "string",
|
||||
description: "The location to show weather for",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "temperature",
|
||||
type: "number",
|
||||
description: "Temperature in celsius",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
handler: async ({ location, temperature }) => {
|
||||
// Fetch and return weather data
|
||||
return { location, temperature, conditions: "Sunny" };
|
||||
},
|
||||
render: ({ args, status, result }) => {
|
||||
if (status === "inProgress") {
|
||||
return <div>Loading weather for {args.location}...</div>;
|
||||
}
|
||||
if (status === "complete" && result) {
|
||||
return (
|
||||
<WeatherCard
|
||||
location={result.location}
|
||||
temperature={result.temperature}
|
||||
conditions={result.conditions}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Generative UI
|
||||
|
||||
This hook enables you to dynamically generate UI elements and render them in the copilot chat. For more information, check out the [Generative UI](/generative-ui/your-components/display-only) page.
|
||||
|
||||
## Migration from useCopilotAction
|
||||
|
||||
If you're migrating from `useCopilotAction`, here are the key differences:
|
||||
|
||||
1. The render component props include `name` and `description`
|
||||
|
||||
### Migration Example
|
||||
|
||||
```tsx
|
||||
// Before with useCopilotAction
|
||||
useCopilotAction({
|
||||
name: "addTodo",
|
||||
parameters: [
|
||||
{
|
||||
name: "text",
|
||||
type: "string",
|
||||
description: "The todo text",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
handler: ({ text }) => {
|
||||
addTodo(text);
|
||||
},
|
||||
});
|
||||
|
||||
// After with useFrontendTool
|
||||
useFrontendTool({
|
||||
name: "addTodo",
|
||||
parameters: [
|
||||
{
|
||||
name: "text",
|
||||
type: "string",
|
||||
description: "The todo text",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
handler: ({ text }) => {
|
||||
addTodo(text);
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
<PropertyReference name="name" type="string" required >
|
||||
The name of the tool.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="description" type="string" >
|
||||
A description of the tool. This is used to instruct the Copilot on how to use the tool.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="parameters" type="T" >
|
||||
Array of parameter definitions for the tool. Each parameter object should have:
|
||||
- `name` (string): The parameter name
|
||||
- `type` (string): The parameter type (e.g., "string", "number", "boolean", "string[]", "object")
|
||||
- `description` (string): A description of what the parameter is for
|
||||
- `required` (boolean): Whether the parameter is required
|
||||
- `properties` (array, optional): For object types, define nested properties using the same schema
|
||||
|
||||
Simple example: `[{ name: "query", type: "string", description: "The search query", required: true }]`
|
||||
|
||||
Nested example:
|
||||
```typescript
|
||||
[
|
||||
{
|
||||
name: "user",
|
||||
type: "object",
|
||||
description: "User information",
|
||||
required: true,
|
||||
properties: [
|
||||
{ name: "name", type: "string", description: "User's name", required: true },
|
||||
{ name: "age", type: "number", description: "User's age", required: false }
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="handler" type="FrontendAction<T>['handler']" >
|
||||
The handler function that executes the tool logic.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="followUp" type="boolean" >
|
||||
Whether to report the result of the tool call to the LLM which will then provide a follow-up response. Pass `false` to disable.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="render" type="FrontendAction<T>['render']" >
|
||||
A React component that renders custom UI for the tool.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="available" type="'disabled' | 'enabled'" >
|
||||
Whether the tool is available. Set to "disabled" to prevent the tool from being called.
|
||||
</PropertyReference>
|
||||
@@ -0,0 +1,237 @@
|
||||
---
|
||||
title: "useHumanInTheLoop"
|
||||
description: "The useHumanInTheLoop hook enables human approval and interaction workflows."
|
||||
---
|
||||
|
||||
<Callout type="warning">
|
||||
`useHumanInTheLoop` is still supported, but we recommend migrating to [`useHumanInTheLoop`](/reference/v2/hooks/useHumanInTheLoop) from the v2 API.
|
||||
</Callout>
|
||||
|
||||
`useHumanInTheLoop` pauses AI execution to request human input or approval. When the AI calls this
|
||||
tool, it stops and waits for the user to respond through your custom UI before continuing. This is
|
||||
essential for sensitive operations, confirmations, or collecting information that only the user can provide.
|
||||
|
||||
Unlike `useFrontendTool`, there's no handler function—instead, your render function receives a `respond`
|
||||
callback that sends the user's input back to the AI. The AI execution remains paused until `respond` is called,
|
||||
making this a true blocking interaction.
|
||||
|
||||
## Usage
|
||||
|
||||
### Simple Confirmation Example
|
||||
|
||||
```tsx
|
||||
import { useHumanInTheLoop } from "@copilotkit/react-core";
|
||||
|
||||
useHumanInTheLoop({
|
||||
name: "confirmDeletion",
|
||||
description: "Ask user to confirm before deleting items",
|
||||
parameters: [
|
||||
{
|
||||
name: "itemName",
|
||||
type: "string",
|
||||
description: "Name of the item to delete",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "itemCount",
|
||||
type: "number",
|
||||
description: "Number of items to delete",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
render: ({ args, status, respond, result }) => {
|
||||
if (status === "executing" && respond) {
|
||||
return (
|
||||
<div className="p-4 border rounded">
|
||||
<p>Are you sure you want to delete {args.itemCount} {args.itemName}(s)?</p>
|
||||
<div className="flex gap-2 mt-4">
|
||||
<button
|
||||
onClick={() => respond({ confirmed: true })}
|
||||
className="bg-red-500 text-white px-4 py-2 rounded"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
onClick={() => respond({ confirmed: false })}
|
||||
className="bg-gray-300 px-4 py-2 rounded"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === "complete" && result) {
|
||||
return (
|
||||
<div className="p-2 text-sm text-gray-600">
|
||||
{result.confirmed ? "Items deleted" : "Deletion cancelled"}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Complex Input Collection Example
|
||||
|
||||
```tsx
|
||||
import { useHumanInTheLoop } from "@copilotkit/react-core";
|
||||
import { useState } from "react";
|
||||
|
||||
useHumanInTheLoop({
|
||||
name: "collectUserPreferences",
|
||||
description: "Collect detailed preferences from the user",
|
||||
parameters: [
|
||||
{
|
||||
name: "context",
|
||||
type: "string",
|
||||
description: "Context for why preferences are needed",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "requiredFields",
|
||||
type: "string[]",
|
||||
description: "Fields to collect",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
render: ({ args, status, respond }) => {
|
||||
const [preferences, setPreferences] = useState({
|
||||
theme: "light",
|
||||
notifications: true,
|
||||
language: "en",
|
||||
});
|
||||
|
||||
if (status === "executing" && respond) {
|
||||
return (
|
||||
<div className="p-4 border rounded">
|
||||
<h3 className="font-bold mb-2">{args.context}</h3>
|
||||
<form onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
respond(preferences);
|
||||
}}>
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-blue-500 text-white px-4 py-2 rounded"
|
||||
>
|
||||
Save Preferences
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Always check for the `respond` function before rendering interactive elements
|
||||
2. Handle all status states to provide good user feedback
|
||||
3. Validate user input before calling `respond`
|
||||
4. Provide clear instructions in your UI about what input is expected
|
||||
5. Consider timeout scenarios for time-sensitive operations
|
||||
|
||||
## Migration from useCopilotAction
|
||||
|
||||
If you're migrating from `useCopilotAction` with `renderAndWaitForResponse`:
|
||||
|
||||
```tsx
|
||||
// Before with useCopilotAction
|
||||
useCopilotAction({
|
||||
name: "confirmAction",
|
||||
parameters: [
|
||||
{ name: "message", type: "string", required: true },
|
||||
],
|
||||
renderAndWaitForResponse: ({ args, respond, status }) => {
|
||||
return (
|
||||
<ConfirmDialog
|
||||
message={args.message}
|
||||
onConfirm={() => respond(true)}
|
||||
onCancel={() => respond(false)}
|
||||
isActive={status === "executing"}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// After with useHumanInTheLoop
|
||||
useHumanInTheLoop({
|
||||
name: "confirmAction",
|
||||
parameters: [
|
||||
{
|
||||
name: "message",
|
||||
type: "string",
|
||||
description: "The message to display",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
render: ({ args, respond, status }) => {
|
||||
if (status === "executing" && respond) {
|
||||
return (
|
||||
<ConfirmDialog
|
||||
message={args.message}
|
||||
onConfirm={() => respond(true)}
|
||||
onCancel={() => respond(false)}
|
||||
isActive={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
The main differences are:
|
||||
1. The property is called `render` instead of `renderAndWaitForResponse`
|
||||
2. You need to check for the `respond` function's existence
|
||||
|
||||
## Parameters
|
||||
|
||||
<PropertyReference name="name" type="string" required >
|
||||
The name of the tool.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="description" type="string" >
|
||||
A description of the tool. This is used to instruct the Copilot on when to request human input.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="parameters" type="T" >
|
||||
Array of parameter definitions that will be passed to the render function. Each parameter object should have:
|
||||
- `name` (string): The parameter name
|
||||
- `type` (string): The parameter type (e.g., "string", "number", "boolean", "string[]", "object")
|
||||
- `description` (string): A description of what the parameter is for
|
||||
- `required` (boolean): Whether the parameter is required
|
||||
- `properties` (array, optional): For object types, define nested properties using the same schema
|
||||
|
||||
Simple example: `[{ name: "itemName", type: "string", description: "Name of the item", required: true }]`
|
||||
|
||||
Nested example:
|
||||
```typescript
|
||||
[
|
||||
{
|
||||
name: "approval",
|
||||
type: "object",
|
||||
description: "Approval request details",
|
||||
required: true,
|
||||
properties: [
|
||||
{ name: "action", type: "string", description: "Action requiring approval", required: true },
|
||||
{ name: "reason", type: "string", description: "Reason for the action", required: false }
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="render" type="FrontendAction<T>['renderAndWaitForResponse']" required >
|
||||
A React component that renders the interactive UI for human input. The component receives props including `args`, `status`, and `respond` function.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="available" type="'disabled' | 'enabled'" >
|
||||
Whether the tool is available. Set to "disabled" to prevent the tool from being called.
|
||||
</PropertyReference>
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
title: "useLangGraphInterrupt"
|
||||
description: "The useLangGraphInterrupt hook allows setting the generative UI to be displayed on LangGraph's Interrupt event."
|
||||
---
|
||||
|
||||
<br />
|
||||
<video src="https://cdn.copilotkit.ai/docs/copilotkit/images/coagents/interrupt-flow.mp4" className="rounded-lg shadow-xl" loop playsInline controls autoPlay muted />
|
||||
|
||||
<Callout type="warning">
|
||||
`useLangGraphInterrupt` is still supported, but we recommend migrating to [`useHumanInTheLoop`](/reference/v2/hooks/useHumanInTheLoop) from the v2 API.
|
||||
</Callout>
|
||||
|
||||
`useLangGraphInterrupt` is a React hook that you can use in your application to provide
|
||||
custom UI to be rendered when using `interrupt` by LangGraph.
|
||||
Once an Interrupt event is emitted, that hook would execute, allowing to receive user input with a user experience to your choice.
|
||||
|
||||
## Usage
|
||||
|
||||
### Simple Usage
|
||||
|
||||
```tsx title="app/page.tsx"
|
||||
import { useLangGraphInterrupt } from "@copilotkit/react-core"; // [!code highlight]
|
||||
// ...
|
||||
|
||||
const YourMainContent = () => {
|
||||
// ...
|
||||
// [!code highlight:15]
|
||||
// styles omitted for brevity
|
||||
useLangGraphInterrupt<string>({
|
||||
render: ({ event, resolve }) => (
|
||||
<div>
|
||||
<p>{event.value}</p>
|
||||
<form onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
resolve((e.target as HTMLFormElement).response.value);
|
||||
}}>
|
||||
<input type="text" name="response" placeholder="Enter your response" />
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
// ...
|
||||
|
||||
return <div>{/* ... */}</div>
|
||||
}
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
<PropertyReference name="action" type="Action" required>
|
||||
The action to perform when an Interrupt event is emitted. Either `handler` or `render` must be defined as arguments
|
||||
|
||||
<PropertyReference name="name" type="string" required>
|
||||
The name of the action.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="handler" type="(args: LangGraphInterruptRenderProps<T>) => any | Promise<any>">
|
||||
A handler to programmatically resolve the Interrupt, or perform operations which result will be passed to the `render` method
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="render" type="(props: LangGraphInterruptRenderProps<T>) => string | React.ReactElement">
|
||||
Render lets you define a custom component or string to render when an Interrupt event is emitted.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="enabled" type="(args: { eventValue: TEventValue; agentMetadata: AgentSession }) => boolean">
|
||||
Method that returns a boolean, indicating if the interrupt action should run. Useful when using multiple interrupts
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="agentId" type="string">
|
||||
Optional agent ID to scope this interrupt to a specific agent. Defaults to the agent configured in the CopilotKit chat configuration.
|
||||
</PropertyReference>
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="dependencies" type="any[]">
|
||||
An optional array of dependencies.
|
||||
</PropertyReference>
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
title: "useRenderToolCall"
|
||||
description: "The useRenderToolCall hook enables rendering of backend tool calls in the frontend."
|
||||
---
|
||||
|
||||
<Callout type="warning">
|
||||
`useRenderToolCall` is still supported, but we recommend migrating to [`useRenderToolCall`](/reference/v2/hooks/useRenderToolCall) from the v2 API.
|
||||
</Callout>
|
||||
|
||||
`useRenderToolCall` is purely a rendering hook — it displays custom UI for tool calls without executing
|
||||
any logic. This is typically used to visualize backend tool executions in your chat interface, showing
|
||||
users what the AI is doing behind the scenes.
|
||||
|
||||
This hook has no handler function. You only provide a render function that receives information about
|
||||
the tool call (arguments, status, results) and displays it however you want. You can target specific
|
||||
tool names or use an asterisk (`"*"`) to catch and render all tool calls.
|
||||
|
||||
## Usage
|
||||
|
||||
### Rendering a Specific Backend Tool
|
||||
|
||||
```tsx
|
||||
import { useRenderToolCall } from "@copilotkit/react-core";
|
||||
|
||||
useRenderToolCall({
|
||||
name: "analyzeData",
|
||||
description: "Display results of data analysis",
|
||||
parameters: [
|
||||
{
|
||||
name: "datasetName",
|
||||
type: "string",
|
||||
description: "Name of the dataset being analyzed",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "metrics",
|
||||
type: "string[]",
|
||||
description: "Metrics being calculated",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
render: ({ args, status, result }) => {
|
||||
if (status === "inProgress") {
|
||||
return (
|
||||
<div className="p-4 border rounded animate-pulse">
|
||||
<h3>Analyzing {args.datasetName}...</h3>
|
||||
<p>Calculating: {args.metrics?.join(", ")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === "complete" && result) {
|
||||
return (
|
||||
<div className="p-4 border rounded bg-green-50">
|
||||
<h3>Analysis Complete: {args.datasetName}</h3>
|
||||
<pre className="mt-2 p-2 bg-gray-100 rounded">
|
||||
{JSON.stringify(result, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Migration from useCopilotAction
|
||||
|
||||
If you're migrating from `useCopilotAction` with only a `render` function:
|
||||
|
||||
```tsx
|
||||
// Before with useCopilotAction
|
||||
useCopilotAction({
|
||||
name: "showResult",
|
||||
render: ({ args }) => <ResultCard {...args} />,
|
||||
});
|
||||
|
||||
// After with useRenderToolCall
|
||||
useRenderToolCall({
|
||||
name: "showResult",
|
||||
render: ({ args }) => <ResultCard {...args} />,
|
||||
});
|
||||
```
|
||||
|
||||
The migration is straightforward - just change the hook name. The render props remain the same.
|
||||
|
||||
## Parameters
|
||||
|
||||
<PropertyReference name="name" type="string" required >
|
||||
The name of the tool to render. Use `"*"` to catch all tool calls.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="description" type="string" >
|
||||
A description of what this renderer does. Mainly for documentation purposes.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="parameters" type="T" >
|
||||
Optional array of parameter definitions. If provided, adds type safety to the args in the render function. Each parameter object should have:
|
||||
- `name` (string): The parameter name
|
||||
- `type` (string): The parameter type (e.g., "string", "number", "boolean", "string[]", "object")
|
||||
- `description` (string): A description of what the parameter is for
|
||||
- `required` (boolean): Whether the parameter is required
|
||||
- `properties` (array, optional): For object types, define nested properties using the same schema
|
||||
|
||||
Simple example: `[{ name: "datasetName", type: "string", description: "Name of the dataset", required: true }]`
|
||||
|
||||
Nested example:
|
||||
```typescript
|
||||
[
|
||||
{
|
||||
name: "analysisConfig",
|
||||
type: "object",
|
||||
description: "Configuration for the analysis",
|
||||
required: true,
|
||||
properties: [
|
||||
{ name: "method", type: "string", description: "Analysis method to use", required: true },
|
||||
{ name: "threshold", type: "number", description: "Threshold value", required: false }
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="render" type="FrontendAction<T>['render']" >
|
||||
A React component that renders the tool call UI. The component receives props with `status`, `args`, `result`, `name`, and `description`.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="available" type="'disabled' | 'enabled'" >
|
||||
Whether the renderer is available. Set to "disabled" to prevent rendering.
|
||||
</PropertyReference>
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: "API Reference"
|
||||
description: "API Reference for CopilotKit's components, classes and hooks."
|
||||
---
|
||||
|
||||
import { LinkIcon } from "lucide-react";
|
||||
|
||||
<Callout type="warning">
|
||||
The v1 APIs will continue to work, but we strongly recommend using or migrating to the [v2 APIs](/reference/v2).
|
||||
</Callout>
|
||||
|
||||
<Cards>
|
||||
<Card
|
||||
title="UI Components"
|
||||
description="See the list of all available UI components in CopilotKit."
|
||||
href="/reference/v1/components/chat/CopilotChat"
|
||||
icon={<LinkIcon />}
|
||||
/>
|
||||
<Card
|
||||
title="Hooks"
|
||||
description="See the list of all available hooks in CopilotKit."
|
||||
href="/reference/v1/hooks/useCopilotReadable"
|
||||
icon={<LinkIcon />}
|
||||
/>
|
||||
<Card
|
||||
title="Classes"
|
||||
description="See the list of all available classes in CopilotKit."
|
||||
href="/reference/v1/classes/CopilotRuntime"
|
||||
icon={<LinkIcon />}
|
||||
/>
|
||||
<Card
|
||||
title="LLM Adapters"
|
||||
description="See the list of all available LLM Adapters in CopilotKit."
|
||||
href="/reference/v1/classes/llm-adapters/OpenAIAdapter"
|
||||
icon={<LinkIcon />}
|
||||
/>
|
||||
<Card
|
||||
title="SDKs"
|
||||
description="Python and JavaScript SDKs for CopilotKit."
|
||||
href="/reference/v1/sdk/python/LangGraph"
|
||||
icon={<LinkIcon />}
|
||||
/>
|
||||
</Cards>
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"title": "v1",
|
||||
"pages": [
|
||||
"index",
|
||||
"---UI Components---",
|
||||
"...components",
|
||||
"---Hooks---",
|
||||
"...hooks",
|
||||
"---Classes---",
|
||||
"...classes",
|
||||
"---SDKs---",
|
||||
"...sdk"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: "LangGraph SDK"
|
||||
description: "The CopilotKit LangGraph SDK for JavaScript allows you to build and run LangGraph workflows with CopilotKit."
|
||||
---
|
||||
|
||||
{
|
||||
/*
|
||||
* ATTENTION! DO NOT MODIFY THIS FILE!
|
||||
* This page is auto-generated. If you want to make any changes to this page, changes must be made at:
|
||||
* packages/sdk-js/src/langgraph/index.ts
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "JavaScript"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"pages": ["python", "js"]
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: "CrewAI SDK"
|
||||
description: "The CopilotKit CrewAI SDK for Python allows you to build and run CrewAI agents with CopilotKit."
|
||||
---
|
||||
|
||||
{
|
||||
/*
|
||||
* ATTENTION! DO NOT MODIFY THIS FILE!
|
||||
* This page is auto-generated. If you want to make any changes to this page, changes must be made at:
|
||||
* sdk-python/copilotkit/crewai/crewai_sdk.py
|
||||
*/
|
||||
}
|
||||
## copilotkit_predict_state
|
||||
|
||||
Stream tool calls as state to CopilotKit.
|
||||
|
||||
To emit a tool call as streaming CrewAI state, pass the destination key in state,
|
||||
the tool name and optionally the tool argument. (If you don't pass the argument name,
|
||||
all arguments are emitted under the state key.)
|
||||
|
||||
```python
|
||||
from copilotkit.crewai import copilotkit_predict_state
|
||||
|
||||
await copilotkit_predict_state(
|
||||
{
|
||||
"steps": {
|
||||
"tool_name": "SearchTool",
|
||||
"tool_argument": "steps",
|
||||
},
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="config" type="Dict[str, CopilotKitPredictStateConfig]" required>
|
||||
The configuration to predict the state.
|
||||
</PropertyReference>
|
||||
|
||||
### Returns
|
||||
|
||||
<PropertyReference name="returns" type="Awaitable[bool]">
|
||||
Always return True.
|
||||
</PropertyReference>
|
||||
|
||||
## copilotkit_emit_message
|
||||
|
||||
Manually emits a message to CopilotKit. Useful in longer running nodes to update the user.
|
||||
Important: You still need to return the messages from the node.
|
||||
|
||||
### Examples
|
||||
|
||||
```python
|
||||
from copilotkit.crewai import copilotkit_emit_message
|
||||
|
||||
message = "Step 1 of 10 complete"
|
||||
await copilotkit_emit_message(message)
|
||||
|
||||
# Return the message from the node
|
||||
return {
|
||||
"messages": [AIMessage(content=message)]
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="message" type="str" required>
|
||||
The message to emit.
|
||||
</PropertyReference>
|
||||
|
||||
### Returns
|
||||
|
||||
<PropertyReference name="returns" type="Awaitable[bool]">
|
||||
Always return True.
|
||||
</PropertyReference>
|
||||
|
||||
## copilotkit_emit_tool_call
|
||||
|
||||
Manually emits a tool call to CopilotKit.
|
||||
|
||||
```python
|
||||
from copilotkit.crewai import copilotkit_emit_tool_call
|
||||
|
||||
await copilotkit_emit_tool_call(name="SearchTool", args={"steps": 10})
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="name" type="str" required>
|
||||
The name of the tool to emit.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="args" type="Dict[str, Any]" required>
|
||||
The arguments to emit.
|
||||
</PropertyReference>
|
||||
|
||||
### Returns
|
||||
|
||||
<PropertyReference name="returns" type="Awaitable[bool]">
|
||||
Always return True.
|
||||
</PropertyReference>
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
---
|
||||
title: "CrewAIAgent"
|
||||
description: "CrewAIAgent lets you define your agent for use with CopilotKit."
|
||||
---
|
||||
|
||||
{
|
||||
/*
|
||||
* ATTENTION! DO NOT MODIFY THIS FILE!
|
||||
* This page is auto-generated. If you want to make any changes to this page, changes must be made at:
|
||||
* sdk-python/copilotkit/crewai/crewai_agent.py
|
||||
*/
|
||||
}
|
||||
## CrewAIAgent
|
||||
|
||||
CrewAIAgent lets you define your agent for use with CopilotKit.
|
||||
|
||||
To install, run:
|
||||
|
||||
```bash
|
||||
pip install copilotkit[crewai]
|
||||
```
|
||||
|
||||
Every agent must have the `name` and either `crew` or `flow` properties defined. An optional
|
||||
`description` can also be provided. This is used when CopilotKit is dynamically routing requests
|
||||
to the agent.
|
||||
|
||||
## Serving a Crew based agent
|
||||
|
||||
To serve a Crew based agent, pass in a `Crew` object to the `crew` parameter.
|
||||
|
||||
Note:
|
||||
You need to make sure to have a `chat_llm` set on the `Crew` object.
|
||||
See [the CrewAI docs](https://docs.crewai.com/concepts/cli#9-chat) for more information.
|
||||
|
||||
```python
|
||||
from copilotkit import CrewAIAgent
|
||||
|
||||
|
||||
CrewAIAgent(
|
||||
name="email_agent_crew",
|
||||
description="This crew based agent sends emails",
|
||||
crew=SendEmailCrew(),
|
||||
)
|
||||
```
|
||||
|
||||
## Serving a Flow based agent
|
||||
|
||||
To serve a Flow based agent, pass in a `Flow` object to the `flow` parameter.
|
||||
|
||||
```python
|
||||
CrewAIAgent(
|
||||
name="email_agent_flow",
|
||||
description="This flow based agent sends emails",
|
||||
flow=SendEmailFlow(),
|
||||
)
|
||||
```
|
||||
|
||||
Note:
|
||||
Either a `crew` or `flow` must be provided to CrewAIAgent.
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="name" type="str" required>
|
||||
The name of the agent.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="crew" type="Crew" required>
|
||||
When using a Crew based agent, pass in a `Crew` object to the `crew` parameter.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="flow" type="Flow" required>
|
||||
When using a Flow based agent, pass in a `Flow` object to the `flow` parameter.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="description" type="Optional[str]" >
|
||||
The description of the agent.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="copilotkit_config" type="Optional[CopilotKitConfig]" >
|
||||
The CopilotKit config to use with the agent.
|
||||
</PropertyReference>
|
||||
|
||||
## CopilotKitConfig
|
||||
|
||||
CopilotKit config for CrewAIAgent
|
||||
|
||||
This is used for advanced cases where you want to customize how CopilotKit interacts with
|
||||
CrewAI.
|
||||
|
||||
```python
|
||||
# Function signatures:
|
||||
def merge_state(
|
||||
*,
|
||||
state: dict,
|
||||
messages: List[BaseMessage],
|
||||
actions: List[Any],
|
||||
agent_name: str
|
||||
):
|
||||
# ...implementation...
|
||||
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="merge_state" type="Callable" required>
|
||||
This function lets you customize how CopilotKit merges the agent state.
|
||||
</PropertyReference>
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
---
|
||||
title: "LangGraph SDK"
|
||||
description: "The CopilotKit LangGraph SDK for Python allows you to build and run LangGraph workflows with CopilotKit."
|
||||
---
|
||||
|
||||
{
|
||||
/*
|
||||
* ATTENTION! DO NOT MODIFY THIS FILE!
|
||||
* This page is auto-generated. If you want to make any changes to this page, changes must be made at:
|
||||
* sdk-python/copilotkit/langgraph.py
|
||||
*/
|
||||
}
|
||||
## copilotkit_customize_config
|
||||
|
||||
Customize the LangGraph configuration for use in CopilotKit.
|
||||
|
||||
To install the CopilotKit SDK, run:
|
||||
|
||||
```bash
|
||||
pip install copilotkit
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
Disable emitting messages and tool calls:
|
||||
|
||||
```python
|
||||
from copilotkit.langgraph import copilotkit_customize_config
|
||||
|
||||
config = copilotkit_customize_config(
|
||||
config,
|
||||
emit_messages=False,
|
||||
emit_tool_calls=False
|
||||
)
|
||||
```
|
||||
|
||||
To emit a tool call as streaming LangGraph state, pass the destination key in state,
|
||||
the tool name and optionally the tool argument. (If you don't pass the argument name,
|
||||
all arguments are emitted under the state key.)
|
||||
|
||||
```python
|
||||
from copilotkit.langgraph import copilotkit_customize_config
|
||||
|
||||
config = copilotkit_customize_config(
|
||||
config,
|
||||
emit_intermediate_state=[
|
||||
{
|
||||
"state_key": "steps",
|
||||
"tool": "SearchTool",
|
||||
"tool_argument": "steps"
|
||||
},
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="base_config" type="Optional[RunnableConfig]" >
|
||||
The LangChain/LangGraph configuration to customize. Pass None to make a new configuration.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="emit_messages" type="Optional[bool]" >
|
||||
Configure how messages are emitted. By default, all messages are emitted. Pass False to disable emitting messages.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="emit_tool_calls" type="Optional[Union[bool, str, List[str]]]" >
|
||||
Configure how tool calls are emitted. By default, all tool calls are emitted. Pass False to disable emitting tool calls. Pass a string or list of strings to emit only specific tool calls.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="emit_intermediate_state" type="Optional[List[IntermediateStateConfig]]" >
|
||||
Lets you emit tool calls as streaming LangGraph state.
|
||||
</PropertyReference>
|
||||
|
||||
### Returns
|
||||
|
||||
<PropertyReference name="returns" type="RunnableConfig">
|
||||
The customized LangGraph configuration.
|
||||
</PropertyReference>
|
||||
|
||||
## copilotkit_exit
|
||||
|
||||
Exits the current agent after the run completes. Calling copilotkit_exit() will
|
||||
not immediately stop the agent. Instead, it signals to CopilotKit to stop the agent after
|
||||
the run completes.
|
||||
|
||||
### Examples
|
||||
|
||||
```python
|
||||
from copilotkit.langgraph import copilotkit_exit
|
||||
|
||||
def my_node(state: Any):
|
||||
await copilotkit_exit(config)
|
||||
return state
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="config" type="RunnableConfig" required>
|
||||
The LangGraph configuration.
|
||||
</PropertyReference>
|
||||
|
||||
### Returns
|
||||
|
||||
<PropertyReference name="returns" type="Awaitable[bool]">
|
||||
Always return True.
|
||||
</PropertyReference>
|
||||
|
||||
## copilotkit_emit_state
|
||||
|
||||
Emits intermediate state to CopilotKit. Useful if you have a longer running node and you want to
|
||||
update the user with the current state of the node.
|
||||
|
||||
### Examples
|
||||
|
||||
```python
|
||||
from copilotkit.langgraph import copilotkit_emit_state
|
||||
|
||||
for i in range(10):
|
||||
await some_long_running_operation(i)
|
||||
await copilotkit_emit_state(config, {"progress": i})
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="config" type="RunnableConfig" required>
|
||||
The LangGraph configuration.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="state" type="Any" required>
|
||||
The state to emit (Must be JSON serializable).
|
||||
</PropertyReference>
|
||||
|
||||
### Returns
|
||||
|
||||
<PropertyReference name="returns" type="Awaitable[bool]">
|
||||
Always return True.
|
||||
</PropertyReference>
|
||||
|
||||
## copilotkit_emit_message
|
||||
|
||||
Manually emits a message to CopilotKit. Useful in longer running nodes to update the user.
|
||||
Important: You still need to return the messages from the node.
|
||||
|
||||
### Examples
|
||||
|
||||
```python
|
||||
from copilotkit.langgraph import copilotkit_emit_message
|
||||
|
||||
message = "Step 1 of 10 complete"
|
||||
await copilotkit_emit_message(config, message)
|
||||
|
||||
# Return the message from the node
|
||||
return {
|
||||
"messages": [AIMessage(content=message)]
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="config" type="RunnableConfig" required>
|
||||
The LangGraph configuration.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="message" type="str" required>
|
||||
The message to emit.
|
||||
</PropertyReference>
|
||||
|
||||
### Returns
|
||||
|
||||
<PropertyReference name="returns" type="Awaitable[bool]">
|
||||
Always return True.
|
||||
</PropertyReference>
|
||||
|
||||
## copilotkit_emit_tool_call
|
||||
|
||||
Manually emits a tool call to CopilotKit.
|
||||
|
||||
```python
|
||||
from copilotkit.langgraph import copilotkit_emit_tool_call
|
||||
|
||||
await copilotkit_emit_tool_call(config, name="SearchTool", args={"steps": 10})
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="config" type="RunnableConfig" required>
|
||||
The LangGraph configuration.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="name" type="str" required>
|
||||
The name of the tool to emit.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="args" type="Dict[str, Any]" required>
|
||||
The arguments to emit.
|
||||
</PropertyReference>
|
||||
|
||||
### Returns
|
||||
|
||||
<PropertyReference name="returns" type="Awaitable[bool]">
|
||||
Always return True.
|
||||
</PropertyReference>
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: "LangGraphAgent"
|
||||
description: "LangGraphAgent lets you define your agent for use with CopilotKit."
|
||||
---
|
||||
|
||||
{
|
||||
/*
|
||||
* ATTENTION! DO NOT MODIFY THIS FILE!
|
||||
* This page is auto-generated. If you want to make any changes to this page, changes must be made at:
|
||||
* sdk-python/copilotkit/langgraph_agent.py
|
||||
*/
|
||||
}
|
||||
## LangGraphAgent
|
||||
|
||||
LangGraphAgent lets you define your agent for use with CopilotKit.
|
||||
|
||||
To install, run:
|
||||
|
||||
```bash
|
||||
pip install copilotkit
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
Every agent must have the `name` and `graph` properties defined. An optional `description`
|
||||
can also be provided. This is used when CopilotKit is dynamically routing requests to the
|
||||
agent.
|
||||
|
||||
```python
|
||||
from copilotkit import LangGraphAgent
|
||||
|
||||
LangGraphAgent(
|
||||
name="email_agent",
|
||||
description="This agent sends emails",
|
||||
graph=graph,
|
||||
)
|
||||
```
|
||||
|
||||
If you have a custom LangGraph/LangChain config that you want to use with the agent, you can
|
||||
pass it in as the `langgraph_config` parameter.
|
||||
|
||||
```python
|
||||
LangGraphAgent(
|
||||
...
|
||||
langgraph_config=config,
|
||||
)
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="name" type="str" required>
|
||||
The name of the agent.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="graph" type="CompiledStateGraph" required>
|
||||
The LangGraph graph to use with the agent.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="description" type="Optional[str]" >
|
||||
The description of the agent.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="langgraph_config" type="Optional[RunnableConfig]" >
|
||||
The LangGraph/LangChain config to use with the agent.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="copilotkit_config" type="Optional[CopilotKitConfig]" >
|
||||
The CopilotKit config to use with the agent.
|
||||
</PropertyReference>
|
||||
|
||||
## CopilotKitConfig
|
||||
|
||||
CopilotKit config for LangGraphAgent
|
||||
|
||||
This is used for advanced cases where you want to customize how CopilotKit interacts with
|
||||
LangGraph.
|
||||
|
||||
```python
|
||||
# Function signatures:
|
||||
def merge_state(
|
||||
*,
|
||||
state: dict,
|
||||
messages: List[BaseMessage],
|
||||
actions: List[Any],
|
||||
agent_name: str
|
||||
):
|
||||
# ...implementation...
|
||||
|
||||
def convert_messages(messages: List[Message]):
|
||||
# ...implementation...
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="merge_state" type="Callable" required>
|
||||
This function lets you customize how CopilotKit merges the agent state.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="convert_messages" type="Callable" required>
|
||||
Use this function to customize how CopilotKit converts its messages to LangChain messages.`
|
||||
</PropertyReference>
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
---
|
||||
title: "Remote Endpoints"
|
||||
description: "CopilotKit Remote Endpoints allow you to connect actions and agents written in Python to your CopilotKit application."
|
||||
---
|
||||
|
||||
{
|
||||
/*
|
||||
* ATTENTION! DO NOT MODIFY THIS FILE!
|
||||
* This page is auto-generated. If you want to make any changes to this page, changes must be made at:
|
||||
* sdk-python/copilotkit/sdk.py
|
||||
*/
|
||||
}
|
||||
## CopilotKitRemoteEndpoint
|
||||
|
||||
CopilotKitRemoteEndpoint lets you connect actions and agents written in Python to your
|
||||
CopilotKit application.
|
||||
|
||||
To install CopilotKit for Python, run:
|
||||
|
||||
```bash
|
||||
pip install copilotkit
|
||||
# or to include crewai
|
||||
pip install copilotkit[crewai]
|
||||
```
|
||||
|
||||
## Adding actions
|
||||
|
||||
In this example, we provide a simple action to the Copilot:
|
||||
|
||||
```python
|
||||
from copilotkit import CopilotKitRemoteEndpoint, Action
|
||||
|
||||
sdk = CopilotKitRemoteEndpoint(
|
||||
actions=[
|
||||
Action(
|
||||
name="greet_user",
|
||||
handler=greet_user_handler,
|
||||
description="Greet the user",
|
||||
parameters=[
|
||||
{
|
||||
"name": "name",
|
||||
"type": "string",
|
||||
"description": "The name of the user"
|
||||
}
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
You can also dynamically build actions by providing a callable that returns a list of actions.
|
||||
In this example, we use "name" from the `properties` object to parameterize the action handler.
|
||||
|
||||
```python
|
||||
from copilotkit import CopilotKitRemoteEndpoint, Action
|
||||
|
||||
sdk = CopilotKitRemoteEndpoint(
|
||||
actions=lambda context: [
|
||||
Action(
|
||||
name="greet_user",
|
||||
handler=make_greet_user_handler(context["properties"]["name"]),
|
||||
description="Greet the user"
|
||||
)
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
Using the same approach, you can restrict the actions available to the Copilot:
|
||||
|
||||
```python
|
||||
from copilotkit import CopilotKitRemoteEndpoint, Action
|
||||
|
||||
sdk = CopilotKitRemoteEndpoint(
|
||||
actions=lambda context: (
|
||||
[action_a, action_b] if is_admin(context["properties"]["token"]) else [action_a]
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Adding agents
|
||||
|
||||
Serving agents works in a similar way to serving actions:
|
||||
|
||||
```python
|
||||
from copilotkit import CopilotKitRemoteEndpoint, LangGraphAgent
|
||||
from my_agent.agent import graph
|
||||
|
||||
sdk = CopilotKitRemoteEndpoint(
|
||||
agents=[
|
||||
LangGraphAgent(
|
||||
name="email_agent",
|
||||
description="This agent sends emails",
|
||||
graph=graph,
|
||||
)
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
To dynamically build agents, provide a callable that returns a list of agents:
|
||||
|
||||
```python
|
||||
from copilotkit import CopilotKitRemoteEndpoint, LangGraphAgent
|
||||
from my_agent.agent import graph
|
||||
|
||||
sdk = CopilotKitRemoteEndpoint(
|
||||
agents=lambda context: [
|
||||
LangGraphAgent(
|
||||
name="email_agent",
|
||||
description="This agent sends emails",
|
||||
graph=graph,
|
||||
langgraph_config={
|
||||
"token": context["properties"]["token"]
|
||||
}
|
||||
)
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
To restrict the agents available to the Copilot, simply return a different list of agents based on the `context`:
|
||||
|
||||
```python
|
||||
from copilotkit import CopilotKitRemoteEndpoint
|
||||
from my_agents import agent_a, agent_b, is_admin
|
||||
|
||||
sdk = CopilotKitRemoteEndpoint(
|
||||
agents=lambda context: (
|
||||
[agent_a, agent_b] if is_admin(context["properties"]["token"]) else [agent_a]
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
## Serving the CopilotKit SDK
|
||||
|
||||
To serve the CopilotKit SDK, you can use the `add_fastapi_endpoint` function from the `copilotkit.integrations.fastapi` module:
|
||||
|
||||
```python
|
||||
from copilotkit.integrations.fastapi import add_fastapi_endpoint
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
sdk = CopilotKitRemoteEndpoint(...)
|
||||
add_fastapi_endpoint(app, sdk, "/copilotkit")
|
||||
|
||||
def main():
|
||||
uvicorn.run(
|
||||
"your_package:app",
|
||||
host="0.0.0.0",
|
||||
port=8000,
|
||||
reload=True,
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="actions" type="Optional[Union[List[Action], Callable[[CopilotKitContext], List[Action]]]]" >
|
||||
The actions to make available to the Copilot.
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="agents" type="Optional[Union[List[Agent], Callable[[CopilotKitContext], List[Agent]]]]" >
|
||||
The agents to make available to the Copilot.
|
||||
</PropertyReference>
|
||||
|
||||
## CopilotKitContext
|
||||
|
||||
CopilotKit Context
|
||||
|
||||
### Parameters
|
||||
|
||||
<PropertyReference name="properties" type="Any" required>
|
||||
The properties provided to the frontend via `<CopilotKit properties={...} />`
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="frontend_url" type="Optional[str]" >
|
||||
The current URL of the frontend
|
||||
</PropertyReference>
|
||||
|
||||
<PropertyReference name="headers" type="Mapping[str, str]" required>
|
||||
The headers of the request
|
||||
</PropertyReference>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"pages": [
|
||||
"RemoteEndpoints",
|
||||
"LangGraphAgent",
|
||||
"LangGraph",
|
||||
"CrewAIAgent",
|
||||
"CrewAI"
|
||||
]
|
||||
}
|
||||
@@ -1,38 +1,115 @@
|
||||
// Shared helpers for walking the `src/content/reference/` tree. Used by
|
||||
// both /reference (index page) and /reference/[...slug] so the two stay
|
||||
// in sync: same subdirs, same recursive traversal, same gray-matter
|
||||
// handling, same caching behavior.
|
||||
// Shared helpers for walking and resolving the `src/content/reference/`
|
||||
// tree. The v2 reference lives at the root for backwards-compatible
|
||||
// `/reference/<slug>` URLs and is also exposed as `/reference/v2/<slug>`.
|
||||
// The v1 reference is nested under `src/content/reference/v1`.
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import matter from "gray-matter";
|
||||
import type * as PageTree from "fumadocs-core/page-tree";
|
||||
import { safeExistsSync, safeReadFileSync } from "@/lib/safe-fs";
|
||||
|
||||
export const REFERENCE_CONTENT_DIR = path.join(
|
||||
process.cwd(),
|
||||
"src/content/reference",
|
||||
);
|
||||
|
||||
// Top-level reference categories we index. Anything outside this list is
|
||||
// ignored (e.g. a stray snippet file at the root).
|
||||
export const REFERENCE_SUBDIRS = ["components", "hooks"] as const;
|
||||
export type ReferenceSubdir = (typeof REFERENCE_SUBDIRS)[number];
|
||||
export const REFERENCE_VERSIONS = ["v2", "v1"] as const;
|
||||
export type ReferenceVersion = (typeof REFERENCE_VERSIONS)[number];
|
||||
|
||||
export const REFERENCE_CATEGORIES = [
|
||||
"Components",
|
||||
"Hooks",
|
||||
"Classes",
|
||||
"SDKs",
|
||||
] as const;
|
||||
export type ReferenceCategory = (typeof REFERENCE_CATEGORIES)[number];
|
||||
|
||||
type ReferenceSubdir = "components" | "hooks" | "classes" | "sdk";
|
||||
|
||||
const VERSION_SUBDIRS: Record<ReferenceVersion, ReferenceSubdir[]> = {
|
||||
v2: ["components", "hooks", "sdk"],
|
||||
v1: ["components", "hooks", "classes", "sdk"],
|
||||
};
|
||||
|
||||
const CATEGORY_BY_SUBDIR: Record<ReferenceSubdir, ReferenceCategory> = {
|
||||
components: "Components",
|
||||
hooks: "Hooks",
|
||||
classes: "Classes",
|
||||
sdk: "SDKs",
|
||||
};
|
||||
|
||||
export type ReferenceItem = {
|
||||
/** subdir-relative slug, e.g. `components/chat` or `components/inputs/textarea`. */
|
||||
/** Version-relative slug, e.g. `components/chat` or `hooks/useAgent`. */
|
||||
slug: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
category: "Components" | "Hooks";
|
||||
category: ReferenceCategory;
|
||||
version: ReferenceVersion;
|
||||
url: string;
|
||||
};
|
||||
|
||||
function categoryFor(subdir: ReferenceSubdir): "Components" | "Hooks" {
|
||||
return subdir === "components" ? "Components" : "Hooks";
|
||||
export type ResolvedReferencePage = {
|
||||
version: ReferenceVersion;
|
||||
pageSlug: string;
|
||||
contentSlug: string;
|
||||
raw: string;
|
||||
};
|
||||
|
||||
function isProd(): boolean {
|
||||
return process.env.NODE_ENV === "production";
|
||||
}
|
||||
|
||||
function versionDir(version: ReferenceVersion): string {
|
||||
return version === "v1"
|
||||
? path.join(REFERENCE_CONTENT_DIR, "v1")
|
||||
: REFERENCE_CONTENT_DIR;
|
||||
}
|
||||
|
||||
function versionRelativePrefix(version: ReferenceVersion): string {
|
||||
return version === "v1" ? "v1/" : "";
|
||||
}
|
||||
|
||||
export function referenceHref(
|
||||
version: ReferenceVersion,
|
||||
pageSlug?: string,
|
||||
): string {
|
||||
const cleanSlug = pageSlug?.replace(/^\/+|\/+$/g, "");
|
||||
const suffix = cleanSlug ? `/${cleanSlug}` : "";
|
||||
return `/reference/${version}${suffix}`;
|
||||
}
|
||||
|
||||
function contentSlugForPage(
|
||||
version: ReferenceVersion,
|
||||
pageSlug: string,
|
||||
): string {
|
||||
const prefix = versionRelativePrefix(version);
|
||||
return `${prefix}${pageSlug || "index"}`;
|
||||
}
|
||||
|
||||
function pageExists(version: ReferenceVersion, pageSlug: string): boolean {
|
||||
const contentSlug = contentSlugForPage(version, pageSlug);
|
||||
return (
|
||||
safeExistsSync(REFERENCE_CONTENT_DIR, `${contentSlug}.mdx`) ||
|
||||
safeExistsSync(REFERENCE_CONTENT_DIR, `${contentSlug}/index.mdx`)
|
||||
);
|
||||
}
|
||||
|
||||
export function referenceVersionHref(
|
||||
version: ReferenceVersion,
|
||||
currentPageSlug?: string,
|
||||
): string {
|
||||
const cleanSlug = currentPageSlug?.replace(/^\/+|\/+$/g, "") ?? "";
|
||||
return referenceHref(
|
||||
version,
|
||||
cleanSlug && pageExists(version, cleanSlug) ? cleanSlug : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively collect all `.mdx` files under `dir` and return their paths
|
||||
* relative to `dir` (without the `.mdx` extension). Silently skips
|
||||
* unreadable subdirectories so a single EACCES doesn't break the build.
|
||||
* Recursively collect `.mdx` files under `dir` and return paths relative
|
||||
* to `dir` without the `.mdx` extension. Directory index pages are kept
|
||||
* as `folder/index` here and normalized later.
|
||||
*/
|
||||
function walkMdx(dir: string, prefix: string = ""): string[] {
|
||||
let entries: fs.Dirent[];
|
||||
@@ -42,9 +119,10 @@ function walkMdx(dir: string, prefix: string = ""): string[] {
|
||||
console.error(`[reference-items] Failed to read dir ${dir}:`, err);
|
||||
return [];
|
||||
}
|
||||
|
||||
const out: string[] = [];
|
||||
for (const entry of entries) {
|
||||
if (entry.name.startsWith(".")) continue;
|
||||
if (entry.name.startsWith(".") || entry.name === "meta.json") continue;
|
||||
const childAbs = path.join(dir, entry.name);
|
||||
const childRel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
||||
if (entry.isDirectory()) {
|
||||
@@ -56,17 +134,32 @@ function walkMdx(dir: string, prefix: string = ""): string[] {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load items from a single reference subdir, recursing into subfolders.
|
||||
* Malformed frontmatter on any file is logged and skipped — we never
|
||||
* crash the whole index just because one page has a bad YAML block.
|
||||
*/
|
||||
function loadSubdirItems(subdir: ReferenceSubdir): ReferenceItem[] {
|
||||
const dir = path.join(REFERENCE_CONTENT_DIR, subdir);
|
||||
function normalizeRouteSlug(subdir: ReferenceSubdir, relSlug: string): string {
|
||||
const normalized = relSlug.endsWith("/index")
|
||||
? relSlug.slice(0, -"/index".length)
|
||||
: relSlug;
|
||||
return normalized === "index" ? subdir : `${subdir}/${normalized}`;
|
||||
}
|
||||
|
||||
function fallbackTitle(routeSlug: string): string {
|
||||
return routeSlug.split("/").filter(Boolean).pop() ?? routeSlug;
|
||||
}
|
||||
|
||||
function loadSubdirItems(
|
||||
version: ReferenceVersion,
|
||||
subdir: ReferenceSubdir,
|
||||
): ReferenceItem[] {
|
||||
const dir = path.join(versionDir(version), subdir);
|
||||
if (!fs.existsSync(dir)) return [];
|
||||
|
||||
const items: ReferenceItem[] = [];
|
||||
const seenSlugs = new Set<string>();
|
||||
|
||||
for (const relSlug of walkMdx(dir)) {
|
||||
const routeSlug = normalizeRouteSlug(subdir, relSlug);
|
||||
if (seenSlugs.has(routeSlug)) continue;
|
||||
seenSlugs.add(routeSlug);
|
||||
|
||||
const filePath = path.join(dir, `${relSlug}.mdx`);
|
||||
let raw: string;
|
||||
try {
|
||||
@@ -75,6 +168,7 @@ function loadSubdirItems(subdir: ReferenceSubdir): ReferenceItem[] {
|
||||
console.error(`[reference-items] Failed to read ${filePath}:`, err);
|
||||
continue;
|
||||
}
|
||||
|
||||
let data: Record<string, unknown> = {};
|
||||
try {
|
||||
({ data } = matter(raw));
|
||||
@@ -85,50 +179,148 @@ function loadSubdirItems(subdir: ReferenceSubdir): ReferenceItem[] {
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const fallbackTitle = relSlug.split("/").pop() ?? relSlug;
|
||||
|
||||
items.push({
|
||||
slug: `${subdir}/${relSlug}`,
|
||||
slug: routeSlug,
|
||||
title:
|
||||
typeof data.title === "string" && data.title.length > 0
|
||||
? data.title
|
||||
: fallbackTitle,
|
||||
: fallbackTitle(routeSlug),
|
||||
description:
|
||||
typeof data.description === "string" ? data.description : undefined,
|
||||
category: categoryFor(subdir),
|
||||
category: CATEGORY_BY_SUBDIR[subdir],
|
||||
version,
|
||||
url: referenceHref(version, routeSlug),
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
// In-memory cache — keyed by subdir, rebuilt once per process in prod. In
|
||||
// dev we skip the cache so MDX edits show up without a server restart.
|
||||
const __itemsCache = new Map<ReferenceSubdir, ReferenceItem[]>();
|
||||
function isProd(): boolean {
|
||||
return process.env.NODE_ENV === "production";
|
||||
}
|
||||
const itemsCache = new Map<string, ReferenceItem[]>();
|
||||
|
||||
export function loadReferenceItems(subdir: ReferenceSubdir): ReferenceItem[] {
|
||||
export function loadReferenceItems(
|
||||
version: ReferenceVersion,
|
||||
subdir: ReferenceSubdir,
|
||||
): ReferenceItem[] {
|
||||
const cacheKey = `${version}:${subdir}`;
|
||||
if (isProd()) {
|
||||
const cached = __itemsCache.get(subdir);
|
||||
const cached = itemsCache.get(cacheKey);
|
||||
if (cached) return cached;
|
||||
}
|
||||
const items = loadSubdirItems(subdir);
|
||||
if (isProd()) __itemsCache.set(subdir, items);
|
||||
|
||||
const items = loadSubdirItems(version, subdir);
|
||||
if (isProd()) itemsCache.set(cacheKey, items);
|
||||
return items;
|
||||
}
|
||||
|
||||
export function loadAllReferenceItems(): ReferenceItem[] {
|
||||
return REFERENCE_SUBDIRS.flatMap((s) => loadReferenceItems(s));
|
||||
export function loadReferenceVersionItems(
|
||||
version: ReferenceVersion,
|
||||
): ReferenceItem[] {
|
||||
return VERSION_SUBDIRS[version].flatMap((subdir) =>
|
||||
loadReferenceItems(version, subdir),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* For `generateStaticParams`: return every reference page as its Next.js
|
||||
* catch-all slug array. Recursive (unlike the previous one-level-only
|
||||
* implementation), so subfolder docs like `components/inputs/textarea`
|
||||
* are statically generated too.
|
||||
*/
|
||||
export function referenceStaticParams(): { slug: string[] }[] {
|
||||
return loadAllReferenceItems().map((item) => ({
|
||||
slug: item.slug.split("/"),
|
||||
}));
|
||||
export function buildReferencePageTree(
|
||||
version: ReferenceVersion,
|
||||
): PageTree.Root {
|
||||
const allItems = loadReferenceVersionItems(version);
|
||||
return {
|
||||
name: "Reference",
|
||||
children: REFERENCE_CATEGORIES.flatMap((category) => {
|
||||
const categoryItems = allItems.filter(
|
||||
(item) => item.category === category,
|
||||
);
|
||||
if (categoryItems.length === 0) return [];
|
||||
return [
|
||||
{ type: "separator" as const, name: category },
|
||||
...categoryItems.map(
|
||||
(item): PageTree.Item => ({
|
||||
type: "page",
|
||||
name: item.title,
|
||||
url: item.url,
|
||||
}),
|
||||
),
|
||||
];
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function splitVersionedSlug(slugPath: string): {
|
||||
version: ReferenceVersion;
|
||||
pageSlug: string;
|
||||
} {
|
||||
if (slugPath === "v1" || slugPath.startsWith("v1/")) {
|
||||
return { version: "v1", pageSlug: slugPath.replace(/^v1\/?/, "") };
|
||||
}
|
||||
if (slugPath === "v2" || slugPath.startsWith("v2/")) {
|
||||
return { version: "v2", pageSlug: slugPath.replace(/^v2\/?/, "") };
|
||||
}
|
||||
return { version: "v2", pageSlug: slugPath };
|
||||
}
|
||||
|
||||
export function resolveReferencePage(
|
||||
slug: string[],
|
||||
): ResolvedReferencePage | null {
|
||||
const slugPath = slug.join("/");
|
||||
const { version, pageSlug } = splitVersionedSlug(slugPath);
|
||||
const contentSlug = contentSlugForPage(version, pageSlug);
|
||||
const raw =
|
||||
safeReadFileSync(REFERENCE_CONTENT_DIR, `${contentSlug}.mdx`) ??
|
||||
safeReadFileSync(REFERENCE_CONTENT_DIR, `${contentSlug}/index.mdx`);
|
||||
|
||||
if (raw === null) return null;
|
||||
return {
|
||||
version,
|
||||
pageSlug,
|
||||
contentSlug,
|
||||
raw,
|
||||
};
|
||||
}
|
||||
|
||||
export function readReferenceIndexDescription(
|
||||
version: ReferenceVersion,
|
||||
): string {
|
||||
const fallback =
|
||||
version === "v1"
|
||||
? "API Reference for CopilotKit's components, classes and hooks."
|
||||
: "API Reference for the next-generation CopilotKit React API.";
|
||||
const raw = safeReadFileSync(
|
||||
REFERENCE_CONTENT_DIR,
|
||||
`${versionRelativePrefix(version)}index.mdx`,
|
||||
);
|
||||
if (raw === null) return fallback;
|
||||
|
||||
try {
|
||||
const { data } = matter(raw);
|
||||
return typeof data.description === "string" && data.description.length > 0
|
||||
? data.description
|
||||
: fallback;
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[reference] Failed to parse ${version} index frontmatter:`,
|
||||
err,
|
||||
);
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
export function referenceStaticParams(): { slug: string[] }[] {
|
||||
const params = new Map<string, string[]>();
|
||||
const add = (slug: string[]) => params.set(slug.join("/"), slug);
|
||||
|
||||
add(["v1"]);
|
||||
add(["v2"]);
|
||||
|
||||
for (const version of REFERENCE_VERSIONS) {
|
||||
for (const item of loadReferenceVersionItems(version)) {
|
||||
add([version, ...item.slug.split("/")]);
|
||||
if (version === "v2") {
|
||||
add(item.slug.split("/"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...params.values()].map((slug) => ({ slug }));
|
||||
}
|
||||
|
||||
@@ -984,11 +984,6 @@ const WILDCARD_REDIRECTS: RedirectEntry[] = [
|
||||
},
|
||||
{ id: "T1-unscoped-root", source: "/tutorials", destination: "/" },
|
||||
// Category 1: Pattern rules (bulk coverage)
|
||||
{
|
||||
id: "P10",
|
||||
source: "/reference/v1/:path*",
|
||||
destination: "/reference/v2/:path*",
|
||||
},
|
||||
{
|
||||
id: "P11",
|
||||
source: "/guides/:path*",
|
||||
|
||||
Reference in New Issue
Block a user