Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions docs/additional-features/chatkit-integration.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: "ChatKit Integration"
description: "Add a chat UI to your agency with OpenAI ChatKit."
icon: "comments"
---

[OpenAI ChatKit](https://platform.openai.com/docs/guides/chatkit) is a React-based chat UI. Agency Swarm includes a ready-to-use demo.

<Warning>
Requires [Node.js](https://nodejs.org/) v18+ and npm.
</Warning>

## Quick Start

```python
from agency_swarm import Agency, Agent

agent = Agent(name="Assistant", instructions="You are helpful.")
agency = Agency(agent, name="my_agency")

agency.chatkit_demo() # Opens browser at http://localhost:3000
```

<Accordion title="Parameters">
```python
agency.chatkit_demo(
host="0.0.0.0", # Backend host
port=8000, # Backend port
frontend_port=3000, # Frontend port
cors_origins=None, # CORS origins list
open_browser=True, # Auto-open browser
)
```
</Accordion>

---

## Backend Only

Use `enable_chatkit=True` to expose the ChatKit endpoint without the demo frontend:

```python
from agency_swarm import Agency, Agent, run_fastapi

def create_agency(**kwargs):
agent = Agent(name="Assistant", instructions="You are helpful.")
return Agency(agent, name="my_agency")

run_fastapi(agencies={"my_agency": create_agency}, enable_chatkit=True)
```

This exposes `/{agency_name}/chatkit` for your own ChatKit frontend.

<Accordion title="Connecting Your Frontend">

Point your ChatKit React app to the backend:

```typescript
const chatkit = useChatKit({
api: { url: "http://localhost:8000/my_agency/chatkit" },
});
```

Or use a Vite proxy:

```typescript
// vite.config.ts
export default defineConfig({
server: {
proxy: {
"/chatkit": {
target: "http://localhost:8000",
rewrite: (path) => `/my_agency${path}`,
},
},
},
});
```
</Accordion>

---

## Persistence

By default, ChatKit is stateless. For conversation persistence, pass custom `RunHooks` via `hooks_override` parameter in `get_response` or `get_response_stream`:

```python
from agents import RunHooks

class MyPersistenceHooks(RunHooks):
async def on_agent_end(self, context, agent, output):
messages = context.context.thread_manager.get_all_messages()
db.save(thread_id, messages) # Save to your database

result = await agency.get_response(
message=user_message,
hooks_override=MyPersistenceHooks(),
)
```
5 changes: 5 additions & 0 deletions docs/additional-features/fastapi-integration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Optionally, you can specify following parameters:
- return_app (default: False) - If True, will return the FastAPI instead of running the server
- cors_origins: (default: ["*"])
- enable_agui (default: `False`) - Enable AG-UI protocol compatibility for streaming endpoints
- enable_chatkit (default: `False`) - Enable [ChatKit](https://platform.openai.com/docs/guides/chatkit) protocol endpoint
- enable_logging (default: `False`) - Enable request tracking and expose `/get_logs` endpoint
- logs_dir (default: `"activity-logs"`) - Directory for log files when logging is enabled

Expand Down Expand Up @@ -184,6 +185,9 @@ print("Response:", tool_response.json())
- **AG-UI Protocol:**
When `enable_agui=True`, only the streaming endpoint is exposed and follows the AG-UI protocol for enhanced frontend integration.

- **ChatKit Protocol:**
When `enable_chatkit=True`, adds `/{agency_name}/chatkit` endpoint. See [ChatKit Integration](/additional-features/chatkit-integration).

---

## Inspecting Tool Schemas
Expand Down Expand Up @@ -251,3 +255,4 @@ When using the agency endpoints (`/{your_agency}/get_response` and `/{your_agenc
Behavior with `file_urls`:
- The server downloads each URL, uploads it to OpenAI, waits until processed, and uses the resulting File IDs.
- `file_ids_map` (shape: `{ filename: file_id }`) is returned in the non‑streaming JSON response of `POST /get_response` and in the final `event: messages` SSE payload of `POST /get_response_stream`.

1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
"additional-features/guardrails",
"additional-features/streaming",
"additional-features/fastapi-integration",
"additional-features/chatkit-integration",
"additional-features/mcp-tools-server",
{
"group": "Custom Communication Flows",
Expand Down
70 changes: 70 additions & 0 deletions examples/interactive/chatkit_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""
Agency Swarm ChatKit Demo

This example demonstrates the ChatKit UI capabilities of Agency Swarm v1.x.
Sets up a frontend and backend server for the OpenAI ChatKit UI chat demo.
"""
Comment on lines +2 to +6

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge ChatKit demo script is syntactically invalid

The new examples/interactive/chatkit_demo.py lacks an opening triple‑quote before the intended module docstring, leaving bare words (Agency Swarm ChatKit Demo) at the top level. Importing or running the demo raises a SyntaxError before any code executes, so the documented ChatKit demo cannot be launched.

Useful? React with 👍 / 👎.


import sys
from pathlib import Path

# Add the src directory to the path so we can import agency_swarm
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example file has incorrect module path calculation

Medium Severity

The path Path(__file__).parent.parent / "src" resolves to examples/src instead of the root src/ directory. Since this file is in examples/interactive/, it needs .parent.parent.parent / "src" (three levels up) to reach the repository root where src/ is located.

Fix in Cursor Fix in Web


from agency_swarm import Agency, Agent, RunContextWrapper, function_tool


@function_tool()
async def example_tool(wrapper: RunContextWrapper) -> str:
"""Example tool for chatkit demo"""
return "Example tool executed"


def create_demo_agency():
"""Create a demo agency for chatkit demo"""

# Create agents using v1.x pattern (direct instantiation)
ceo = Agent(
name="CEO",
description="Chief Executive Officer - oversees all operations",
instructions="You are the CEO responsible for high-level decision making and coordination.",
tools=[example_tool],
)

worker = Agent(
name="Worker",
description="Worker - performs tasks",
instructions="Follow instructions given by the CEO.",
tools=[example_tool],
)

# Create agency with communication flows (v1.x pattern)
agency = Agency(
ceo, # Entry point agent (positional argument)
communication_flows=[ceo > worker],
name="ChatKitDemoAgency",
)

return agency


def main():
"""Launch interactive ChatKit demo"""
print("Agency Swarm ChatKit Demo")
print("=" * 50)
print()

try:
agency = create_demo_agency()
# Launch the ChatKit UI demo with backend and frontend servers.
agency.chatkit_demo()

except Exception as e:
print(f"❌ Demo failed with error: {e}")
import traceback

traceback.print_exc()


if __name__ == "__main__":
main()
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ artifacts = [
"ui/demos/copilot/**/*.mjs",
"ui/demos/copilot/**/*.ico",
"ui/demos/copilot/**/*.svg",
# ChatKit frontend files
"ui/demos/chatkit/**/*.ts",
"ui/demos/chatkit/**/*.tsx",
"ui/demos/chatkit/**/*.css",
"ui/demos/chatkit/**/*.json",
"ui/demos/chatkit/**/*.mjs",
"ui/demos/chatkit/**/*.html",
]

[tool.hatch.build.targets.wheel.force-include]
Expand All @@ -104,8 +111,11 @@ artifacts = [
include = [
"src/agency_swarm/**/*.py",
"src/agency_swarm/**/*.ts",
"src/agency_swarm/**/*.tsx",
"src/agency_swarm/**/*.html",
"src/agency_swarm/ui/templates/**",
"src/agency_swarm/ui/demos/copilot/**",
"src/agency_swarm/ui/demos/chatkit/**",
]
exclude = ["**/node_modules/**", "**/.next/**"]

Expand Down
15 changes: 15 additions & 0 deletions src/agency_swarm/agency/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,3 +495,18 @@ def copilot_demo(
from .visualization import copilot_demo

return copilot_demo(self, host, port, frontend_port, cors_origins)

def chatkit_demo(
self,
host: str = "0.0.0.0",
port: int = 8000,
frontend_port: int = 3000,
cors_origins: list[str] | None = None,
open_browser: bool = True,
) -> None:
"""
Run a ChatKit demo of the agency.
"""
from .visualization import chatkit_demo

return chatkit_demo(self, host, port, frontend_port, cors_origins, open_browser)
18 changes: 18 additions & 0 deletions src/agency_swarm/agency/visualization.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,21 @@ def copilot_demo(
Run a copilot demo of the agency.
"""
CopilotDemoLauncher.start(agency, host=host, port=port, frontend_port=frontend_port, cors_origins=cors_origins)


def chatkit_demo(
agency: "Agency",
host: str = "0.0.0.0",
port: int = 8000,
frontend_port: int = 3000,
cors_origins: list[str] | None = None,
open_browser: bool = True,
) -> None:
"""
Run a ChatKit demo of the agency.
"""
from agency_swarm.ui.demos.chatkit import ChatkitDemoLauncher

ChatkitDemoLauncher.start(
agency, host=host, port=port, frontend_port=frontend_port, cors_origins=cors_origins, open_browser=open_browser
)
24 changes: 23 additions & 1 deletion src/agency_swarm/integrations/fastapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def run_fastapi(
return_app: bool = False,
cors_origins: list[str] | None = None,
enable_agui: bool = False,
enable_chatkit: bool = False,
enable_logging: bool = False,
logs_dir: str = "activity-logs",
):
Expand All @@ -39,6 +40,11 @@ def run_fastapi(
server_url : str | None
Optional base URL to be included in the server OpenAPI schema.
Defaults to ``http://{host}:{port}``
enable_agui : bool
Enable AG-UI protocol compatibility for streaming endpoints.
enable_chatkit : bool
Enable ChatKit protocol endpoints (adds /{agency}/chatkit routes).
ChatKit is OpenAI's pre-built chat UI framework.
enable_logging : bool
Enable request tracking and file logging.
When enabled, adds middleware to track requests and allows conditional
Expand Down Expand Up @@ -154,6 +160,17 @@ def run_fastapi(
endpoints.append(f"/{agency_name}/get_response")
endpoints.append(f"/{agency_name}/get_response_stream")

# Add ChatKit endpoint if enabled
if enable_chatkit:
from .fastapi_utils.chatkit_handlers import ChatkitRequest, make_chatkit_endpoint

app.add_api_route(
f"/{agency_name}/chatkit",
make_chatkit_endpoint(ChatkitRequest, agency_factory, verify_token),
methods=["POST"],
)
endpoints.append(f"/{agency_name}/chatkit")

app.add_api_route(
f"/{agency_name}/get_metadata",
make_metadata_endpoint(agency_metadata, verify_token),
Expand Down Expand Up @@ -189,6 +206,11 @@ def run_fastapi(
if return_app:
return app

logger.info(f"Starting FastAPI {'AG-UI ' if enable_agui else ''}server at http://{host}:{port}")
mode_str = ""
if enable_agui:
mode_str = "AG-UI "
elif enable_chatkit:
mode_str = "ChatKit "
logger.info(f"Starting FastAPI {mode_str}server at http://{host}:{port}")

uvicorn.run(app, host=host, port=port)
Loading