docs: add Frontend guides (CopilotKit + AG-UI) - #6686
Conversation
Add a Frontend sub-group under Guides documenting how to build user interfaces for CrewAI Crews and Flows with CopilotKit over the AG-UI protocol. Pages: overview, generative UI, tool-based generative UI, agentic generative UI, human-in-the-loop, shared state, frontend actions, predictive state updates, and channels.
Also register the Frontend sub-group and pages under the default v1.15.5 version so the section is visible without switching to Edge.
- Use useRenderTool for display-only tool rendering (was useFrontendTool) - Correct state 'auto-streams' claims: snapshot at step boundaries, document copilotkit_emit_state for mid-step progress - Fix setState usage to spread full state (replace, not merge) - Add tool description to the frontend-action example - Rewrite Channels with the real @copilotkit/channels createBot API (Slack + Discord adapters); drop unsupported platform claims - Note self-hosted vs managed CopilotKit paths and pin package versions
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Adds a new Frontend guides section to the CrewAI docs, describing how to build UIs for Crews/Flows using CopilotKit over the AG-UI protocol, including patterns like tool rendering, state rendering, HITL, shared state, predictive state, and Channels.
Changes:
- Adds new Frontend guide pages under
docs/edge/en/guides/frontend/and mirrors them underdocs/v1.15.5/en/guides/frontend/. - Updates
docs/docs.jsonnavigation to include the new Frontend group for both Edge and v1.15.5. - Introduces multiple end-to-end code snippets (FastAPI + Flow + React) demonstrating the integration patterns.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/docs.json | Adds “Frontend” group to the Guides navigation for Edge and v1.15.5. |
| docs/edge/en/guides/frontend/overview.mdx | End-to-end architecture + setup (agent server → runtime → React provider). |
| docs/edge/en/guides/frontend/generative-ui.mdx | Introduces tool-rendering vs state-rendering concepts. |
| docs/edge/en/guides/frontend/tool-based-generative-ui.mdx | Walkthrough for rendering tool calls with useRenderTool. |
| docs/edge/en/guides/frontend/agentic-generative-ui.mdx | Walkthrough for rendering live Flow state with useAgent (+ emitting state). |
| docs/edge/en/guides/frontend/human-in-the-loop.mdx | Walkthrough for pausing/resuming via useHumanInTheLoop. |
| docs/edge/en/guides/frontend/shared-state.mdx | Walkthrough for two-way shared state with agent.setState(...). |
| docs/edge/en/guides/frontend/frontend-actions.mdx | Walkthrough for browser-side tools via useFrontendTool. |
| docs/edge/en/guides/frontend/predictive-state-updates.mdx | Walkthrough for copilotkit_predict_state optimistic UI updates. |
| docs/edge/en/guides/frontend/channels.mdx | Walkthrough for running the same agent via Slack/Discord using Channels SDK. |
| docs/v1.15.5/en/guides/frontend/overview.mdx | Snapshot copy of the Frontend Overview page. |
| docs/v1.15.5/en/guides/frontend/generative-ui.mdx | Snapshot copy of the Generative UI page. |
| docs/v1.15.5/en/guides/frontend/tool-based-generative-ui.mdx | Snapshot copy of the Tool-Based Generative UI page. |
| docs/v1.15.5/en/guides/frontend/agentic-generative-ui.mdx | Snapshot copy of the Agentic Generative UI page. |
| docs/v1.15.5/en/guides/frontend/human-in-the-loop.mdx | Snapshot copy of the Human-in-the-Loop page. |
| docs/v1.15.5/en/guides/frontend/shared-state.mdx | Snapshot copy of the Shared State page. |
| docs/v1.15.5/en/guides/frontend/frontend-actions.mdx | Snapshot copy of the Frontend Actions page. |
| docs/v1.15.5/en/guides/frontend/predictive-state-updates.mdx | Snapshot copy of the Predictive State Updates page. |
| docs/v1.15.5/en/guides/frontend/channels.mdx | Snapshot copy of the Channels page. |
Comments suppressed due to low confidence (5)
docs/edge/en/guides/frontend/agentic-generative-ui.mdx:76
- The tool schema restricts
statusto only"enabled", but the state model allows"disabled"(and the guide later setsstep.status = "disabled"). This mismatch makes the example self-contradictory and can cause validation/tooling issues; include both enum values (or adjust the state type to match).
"description": {"type": "string"},
"status": {"type": "string", "enum": ["enabled"]},
},
docs/v1.15.5/en/guides/frontend/agentic-generative-ui.mdx:76
- The tool schema restricts
statusto only"enabled", but the state model allows"disabled"(and the guide later setsstep.status = "disabled"). This mismatch makes the example self-contradictory and can cause validation/tooling issues; include both enum values (or adjust the state type to match).
"description": {"type": "string"},
"status": {"type": "string", "enum": ["enabled"]},
},
docs/edge/en/guides/frontend/agentic-generative-ui.mdx:103
- In the “Write progress into state” step, the Flow never assigns the generated tool-call arguments into
self.state.steps, so the frontend won’t see any progress viaagent.state. Parse thegenerate_task_stepstool call and write the steps into state (and optionally append a tool result for a well-formed tool-call turn).
message = response.choices[0].message
self.state.messages.append(message)
docs/v1.15.5/en/guides/frontend/agentic-generative-ui.mdx:103
- In the “Write progress into state” step, the Flow never assigns the generated tool-call arguments into
self.state.steps, so the frontend won’t see any progress viaagent.state. Parse thegenerate_task_stepstool call and write the steps into state (and optionally append a tool result for a well-formed tool-call turn).
message = response.choices[0].message
self.state.messages.append(message)
docs/edge/en/guides/frontend/shared-state.mdx:166
updateRecipecan throw whenagentorrecipeis still undefined (e.g.,{ ...recipe, ...patch }will crash ifrecipeisundefined, andagent.setStatewill crash ifagentis not ready). Guard against an undefined agent and defaultrecipeto{}before spreading.
const updateRecipe = (patch: Partial<Recipe>) =>
agent.setState({ ...agent.state, recipe: { ...recipe, ...patch } });
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| --- | ||
| title: Frontend Overview | ||
| description: Build interactive user interfaces for your CrewAI agents with CopilotKit and the AG-UI protocol. | ||
| icon: browser | ||
| mode: "wide" | ||
| --- |
| if message.get("tool_calls"): | ||
| call = message["tool_calls"][0] | ||
| if call["function"]["name"] == "generate_recipe": | ||
| args = json.loads(call["function"]["arguments"]) | ||
| self.state.recipe = Recipe(**args["recipe"]) # write to shared state | ||
| self.state.messages.append({ | ||
| "role": "tool", | ||
| "content": "Recipe updated.", | ||
| "tool_call_id": call["id"], | ||
| }) | ||
| return "route_follow_up" | ||
| return "route_end" |
| if message.get("tool_calls"): | ||
| call = message["tool_calls"][0] | ||
| if call["function"]["name"] == "generate_recipe": | ||
| args = json.loads(call["function"]["arguments"]) | ||
| self.state.recipe = Recipe(**args["recipe"]) # write to shared state | ||
| self.state.messages.append({ | ||
| "role": "tool", | ||
| "content": "Recipe updated.", | ||
| "tool_call_id": call["id"], | ||
| }) | ||
| return "route_follow_up" | ||
| return "route_end" |
| const state = agent?.state as { recipe?: Recipe } | undefined; | ||
| const isLoading = agent?.isRunning; | ||
|
|
||
| const recipe = state?.recipe; | ||
|
|
||
| // setState replaces the whole state object, so spread the current | ||
| // state and override only the field you changed. Passing just | ||
| // `{ recipe }` would drop messages and other runtime fields. | ||
| const updateRecipe = (patch: Partial<Recipe>) => | ||
| agent.setState({ ...agent.state, recipe: { ...recipe, ...patch } }); |
| from typing import List, Literal | ||
| from pydantic import BaseModel | ||
| from ag_ui_crewai.sdk import CopilotKitState | ||
|
|
||
|
|
||
| class TaskStep(BaseModel): | ||
| description: str | ||
| status: Literal["enabled", "disabled"] | ||
|
|
||
|
|
||
| class AgentState(CopilotKitState): | ||
| steps: List[TaskStep] = [] |
| from typing import List, Literal | ||
| from pydantic import BaseModel | ||
| from ag_ui_crewai.sdk import CopilotKitState | ||
|
|
||
|
|
||
| class TaskStep(BaseModel): | ||
| description: str | ||
| status: Literal["enabled", "disabled"] | ||
|
|
||
|
|
||
| class AgentState(CopilotKitState): | ||
| steps: List[TaskStep] = [] |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (9)
docs/v1.15.5/en/guides/frontend/overview.mdx:5
- This file is being added under
docs/v1.15.5/, but repo docs policy treatsdocs/v*/as frozen release snapshots managed by devtools; CI rejects edits there unless this is a[docs-freeze]PR. These pages should live underdocs/edge/only and be snapshotted by the release tooling.
---
title: Frontend Overview
description: Build interactive user interfaces for your CrewAI agents with CopilotKit and the AG-UI protocol.
icon: browser
mode: "wide"
docs/edge/en/guides/frontend/agentic-generative-ui.mdx:45
steps: List[TaskStep] = []is a mutable default. UseField(default_factory=list)to avoid accidentally sharing state between instances (and to match the pattern used elsewhere in the docs).
class AgentState(CopilotKitState):
steps: List[TaskStep] = []
docs/docs.json:692
docs/v*/is a frozen snapshot; adding new pages to the v1.15.5 nav will trigger the docs-snapshots CI guard unless this is a[docs-freeze]PR. Please remove thev1.15.5/en/guides/frontend/*entries and keep the new Frontend section inedge/only.
docs/edge/en/guides/frontend/tool-based-generative-ui.mdx:85- This example treats
messageas an object with.tool_calls/.id, but elsewhere in CrewAI docs and code, chat messages are handled as dicts (e.g.,message.get("tool_calls")). As written, one of these shapes will break for users copying the snippet.
message = response.choices[0].message
self.state.messages.append(message)
if message.tool_calls:
self.state.messages.append({
"tool_call_id": message.tool_calls[0].id,
"role": "tool",
docs/edge/en/guides/frontend/shared-state.mdx:166
{ ...recipe, ...patch }will throw ifrecipeis undefined (object-spread onundefined/null). Sincerecipeis optional initially, this can break the example as soon as the user types into the input.
const updateRecipe = (patch: Partial<Recipe>) =>
agent.setState({ ...agent.state, recipe: { ...recipe, ...patch } });
docs/edge/en/guides/frontend/generative-ui.mdx:37
- This code calls
useRenderTool(...)at module scope. Since it is a React hook, calling it outside a component/custom hook violates the Rules of Hooks and will crash/lint-fail in real Next.js apps.
useRenderTool({
name: "generate_recipe",
parameters: z.object({
title: z.string(),
ingredients: z.array(z.string()),
}),
render: ({ args }) => <RecipeCard title={args.title} ingredients={args.ingredients} />,
});
docs/edge/en/guides/frontend/frontend-actions.mdx:45
- This example calls
useFrontendTool(...)at module scope. As a React hook, it needs to run inside a component (or custom hook) to satisfy the Rules of Hooks.
useFrontendTool({
agentId: "assistant",
name: "set_theme",
description: "Switch the app between light and dark mode.",
parameters: z.object({
theme: z.enum(["light", "dark"]),
}),
followUp: false,
handler: async ({ theme }) => {
docs/edge/en/guides/frontend/human-in-the-loop.mdx:124
- This snippet calls
useHumanInTheLoop(...)at module scope. As a React hook, it should be invoked inside a component/custom hook to avoid Rules-of-Hooks violations in Next.js.
useHumanInTheLoop({
agentId: "human_in_the_loop",
name: "generate_task_steps",
parameters: z.object({
steps: z.array(
z.object({
description: z.string(),
status: z.enum(["enabled", "disabled", "executing"]),
})
),
}),
render: ({ args, respond, status }) => (
docs/edge/en/guides/frontend/tool-based-generative-ui.mdx:138
- This snippet calls
useRenderTool(...)at module scope. Since it is a React hook, this violates the Rules of Hooks in real Next.js apps; wrap the registration in a component and render it under your<CopilotKit>provider.
useRenderTool({
name: "generate_haiku",
parameters: z.object({
japanese: z.array(z.string()),
english: z.array(z.string()),
}),
render: ({ args, status }) => {
if (!args.japanese) return <></>; // still streaming
return <HaikuCard japanese={args.japanese} english={args.english} />;
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (4)
docs/edge/en/guides/frontend/shared-state.mdx:104
- This snippet treats
messageas a dict (message.get(...),message[...]), but elsewhere in this repo the OpenAI-style message object is accessed via attributes (e.g.,message.tool_calls). As written, the example will raise at runtime ifmessageis not dict-like, and it’s inconsistent with the other examples in this new section.
if message.get("tool_calls"):
call = message["tool_calls"][0]
if call["function"]["name"] == "generate_recipe":
args = json.loads(call["function"]["arguments"])
self.state.recipe = Recipe(**args["recipe"]) # write to shared state
docs/edge/en/guides/frontend/shared-state.mdx:172
- This input stays enabled while
agentis still undefined, which makes it easy to trigger theupdateRecipehandler before the agent is ready. Disable the input until the agent is available (or until loading completes).
disabled={isLoading}
docs/edge/en/guides/frontend/shared-state.mdx:166
updateRecipecallsagent.setState(...)unconditionally, butagentcan be undefined on first render (before the hook initializes). That would throw if the user types immediately. Guard againstagentbeing undefined before callingsetState.
This issue also appears on line 172 of the same file.
const updateRecipe = (patch: Partial<Recipe>) =>
agent.setState({ ...agent.state, recipe: { ...recipe, ...patch } });
docs/edge/en/guides/frontend/agentic-generative-ui.mdx:76
- The tool schema for
generate_task_stepsonly allowsstatusto be "enabled", but later in this guide you setstep.status = "disabled". Update the JSON schema enum to match the statuses the guide uses so the tool call validates correctly.
"description": {"type": "string"},
"status": {"type": "string", "enum": ["enabled"]},
},
| { | ||
| "group": "Frontend", | ||
| "icon": "browser", | ||
| "pages": [ | ||
| "v1.15.5/en/guides/frontend/overview", | ||
| "v1.15.5/en/guides/frontend/generative-ui", | ||
| "v1.15.5/en/guides/frontend/tool-based-generative-ui", | ||
| "v1.15.5/en/guides/frontend/agentic-generative-ui", | ||
| "v1.15.5/en/guides/frontend/human-in-the-loop", | ||
| "v1.15.5/en/guides/frontend/shared-state", | ||
| "v1.15.5/en/guides/frontend/frontend-actions", | ||
| "v1.15.5/en/guides/frontend/predictive-state-updates", | ||
| "v1.15.5/en/guides/frontend/channels" | ||
| ] | ||
| }, |
What
Adds a Frontend guides section documenting how to build user interfaces for CrewAI Crews and Flows with CopilotKit over the AG-UI protocol.
New sub-group under Guides (after Flows), 9 pages:
ag-ui-crewai) → CopilotKit runtime → React provideruseRenderTooluseAgentuseHumanInTheLoopuseFrontendToolcopilotkit_predict_stateApproach
ag-ui-crewai), consistent with the OSS integration. A note links CopilotKit's managed/Enterprise quickstart as the alternative.@copilotkit/react-core/v2, 1.61.2+); a version note calls out the difference from the oldercopilotkitPython package / pre-v2 hooks.ag-ui-crewaiexamples.Versioning
Pages added to
edge/en/guides/frontend/and mirrored intov1.15.5/en/guides/frontend/, withdocs.jsonnav updated for both. Rendered and verified locally withmint dev.Opening as draft for maintainer review — happy to adjust placement, naming, self-hosted vs managed framing, or any API details.