chatkit
3
总安装量
2
周安装量
#54785
全站排名
安装命令
npx skills add https://github.com/mumerrazzaq/claude-code-skills-lab --skill chatkit
Agent 安装分布
amp
2
gemini-cli
2
github-copilot
2
codex
2
kimi-cli
2
opencode
2
Skill 文档
ChatKit Development Guide
Build production AI chat applications with OpenAI ChatKit.
Before You Start
Clarify these before proceeding:
| Question | Why It Matters | Default |
|---|---|---|
| Single-user or multi-user? | Determines Store implementation | Single-user |
| Which LLM provider? | Affects dependencies and model config | OpenAI |
| Need widgets/forms? | Adds complexity to respond() | No |
| Persistent storage? | InMemory vs PostgreSQL | InMemory (dev) |
Skip clarification if: User says “just get it working” – use defaults.
Version Compatibility
| Package | Tested Version | Check Latest |
|---|---|---|
openai-chatkit |
0.1.x | pip index versions openai-chatkit |
openai-agents |
0.0.x | pip index versions openai-agents |
@openai/chatkit-react |
0.1.x | npm view @openai/chatkit-react version |
next |
16.x | npm view next version |
Breaking change risk: ChatKit is early-stage. Pin versions in production.
Next.js 16 verified patterns:
- Route Handlers use standard Web API
Responsewithresponse.bodyfor streaming - Use
request.text()to read request body - Use
Cache-Control: no-storefor SSE responses
Architecture
Frontend (@openai/chatkit-react) <---> FastAPI + ChatKitServer <---> Agent (LLM)
| |
domainKey Store (DB)
Fastest Path (60 seconds)
# Create project with script
python scripts/init_chatkit_project.py my-app --provider openai
# Add API key
echo "OPENAI_API_KEY=sk-xxx" > my-app/.env
# Run server
cd my-app && uv run uvicorn main:app --reload --port 8000
Verify it works:
curl -X POST http://localhost:8000/chatkit \
-H "Content-Type: application/json" \
-d '{"type":"new_thread"}'
# Should return: {"thread_id": "thread_xxx", ...}
Required Dependencies
# Backend (Python)
uv add openai-chatkit openai-agents fastapi "uvicorn[standard]"
# For non-OpenAI models (Gemini, Claude, etc.)
uv add "openai-agents[litellm]"
# For PostgreSQL persistence
uv add asyncpg
# Frontend (JavaScript/React)
npm install @openai/chatkit-react
# or vanilla JS
npm install @openai/chatkit
Minimal Working Server
from datetime import datetime
from typing import AsyncIterator
from collections import defaultdict
from fastapi import FastAPI, Request
from fastapi.responses import Response, StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
from agents import Agent, Runner
from chatkit.server import ChatKitServer, StreamingResult
from chatkit.store import Store, NotFoundError
from chatkit.types import (
ThreadMetadata, ThreadItem, Page, UserMessageItem,
ThreadStreamEvent, ThreadItemDoneEvent, AssistantMessageItem, AssistantMessageContent
)
from chatkit.agents import AgentContext, stream_agent_response, simple_to_agent_input
app = FastAPI()
# CORS required for frontend
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Restrict in production
allow_methods=["*"],
allow_headers=["*"],
)
class InMemoryStore(Store[dict]):
"""Development store - data lost on restart."""
def __init__(self):
self.threads, self.items = {}, defaultdict(list)
async def load_thread(self, thread_id, context):
if thread_id not in self.threads: raise NotFoundError(f"Thread {thread_id} not found")
return self.threads[thread_id]
async def save_thread(self, thread, context): self.threads[thread.id] = thread
async def delete_thread(self, thread_id, context): self.threads.pop(thread_id, None); self.items.pop(thread_id, None)
async def load_threads(self, limit, after, order, context):
return Page(data=list(self.threads.values())[:limit], has_more=False, after=None)
async def load_thread_items(self, thread_id, after, limit, order, context):
return Page(data=self.items.get(thread_id, [])[:limit], has_more=False, after=None)
async def add_thread_item(self, thread_id, item, context): self.items[thread_id].append(item)
async def save_item(self, thread_id, item, context): pass
async def load_item(self, thread_id, item_id, context): raise NotFoundError("")
async def delete_thread_item(self, thread_id, item_id, context): pass
async def save_attachment(self, attachment, context): raise NotImplementedError()
async def load_attachment(self, attachment_id, context): raise NotImplementedError()
async def delete_attachment(self, attachment_id, context): raise NotImplementedError()
class MyChatKitServer(ChatKitServer[dict]):
agent = Agent(model="gpt-4.1", name="Assistant", instructions="You are helpful.")
async def respond(self, thread: ThreadMetadata, input: UserMessageItem | None, context: dict) -> AsyncIterator[ThreadStreamEvent]:
# Load recent conversation history (REQUIRED for multi-turn)
items_page = await self.store.load_thread_items(thread.id, after=None, limit=20, order="asc", context=context)
input_items = await simple_to_agent_input(items_page.data)
agent_context = AgentContext(thread=thread, store=self.store, request_context=context)
result = Runner.run_streamed(self.agent, input_items, context=agent_context)
async for event in stream_agent_response(agent_context, result):
yield event
store = InMemoryStore()
server = MyChatKitServer(store=store)
@app.post("/chatkit")
async def chatkit_endpoint(request: Request):
result = await server.process(await request.body(), context={})
if isinstance(result, StreamingResult):
return StreamingResponse(result, media_type="text/event-stream")
return Response(content=result.json, media_type="application/json")
Run: uv run uvicorn main:app --reload
Minimal Frontend (React)
import { ChatKit, useChatKit } from '@openai/chatkit-react';
export function Chat() {
const { control } = useChatKit({
api: {
url: 'http://localhost:8000/chatkit',
domainKey: 'local-dev', // Required - use 'local-dev' for development
},
});
return <ChatKit control={control} className="h-[600px] w-[400px]" />;
}
See frontend.md for complete frontend integration.
What Can Go Wrong
| Symptom | Cause | Fix |
|---|---|---|
| CORS error in browser | Missing middleware | Add CORSMiddleware with frontend origin |
| “domainKey is required” | Missing config | Set domainKey: 'local-dev' in useChatKit |
| No streaming (all at once) | Wrong Runner method | Use Runner.run_streamed() not Runner.run() |
| Chat history lost | Wrong order parameter | Use order="asc" in load_thread_items() |
| Agent ignores history | Not loading items | Call simple_to_agent_input(items_page.data) |
| 401 Unauthorized | Auth header missing | Pass headers: { Authorization: ... } in api config |
Full troubleshooting: troubleshooting.md
Choose Your Path
Store Selection
| Need | Solution | Command |
|---|---|---|
| Prototyping | InMemoryStore (above) | – |
| Production single-user | PostgreSQL | python scripts/create_postgres_store.py store.py |
| Production multi-user | PostgreSQL + sessions | python scripts/create_postgres_store.py store.py --multi-user |
See store.md for implementation details.
LLM Provider
| Provider | Config |
|---|---|
| OpenAI | Agent(model="gpt-4.1") |
| Gemini | See models.md |
| OpenRouter | See models.md |
| Ollama | See models.md |
Adding Tools
from agents import function_tool
@function_tool
def search_products(query: str) -> str:
"""Search the product catalog."""
return f"Found results for: {query}"
agent = Agent(tools=[search_products], ...)
See mcp.md for MCP servers and hosted tools.
Multi-User Sessions
from dataclasses import dataclass
@dataclass
class RequestContext:
user_id: str
thread_id: str | None = None
# Filter by user_id in all store queries
async def load_threads(self, limit, after, order, ctx: RequestContext):
# WHERE user_id = ctx.user_id
...
See sessions.md for complete implementation.
Scripts
| Script | Purpose |
|---|---|
python scripts/init_chatkit_project.py <name> --provider <x> |
Create new project |
python scripts/create_postgres_store.py <file> [--multi-user] |
Generate PostgreSQL store |
Providers: openai, gemini, openrouter, ollama
How Do I Know I’m Done?
Minimum Viable ChatKit
- Server starts without errors (
uv run uvicorn main:app) -
curl -X POST .../chatkit -d '{"type":"new_thread"}'returns thread_id - Frontend renders ChatKit component (check: element visible, no console errors)
- Sending a message returns a streaming response
- Multi-turn conversation works (agent remembers previous messages)
Production Ready
- PostgreSQL store connected and tables created
- CORS restricted to production domain
- API key in environment variable (not hardcoded)
- User authentication extracts user_id into RequestContext
- Store queries filter by user_id
- Error handling returns graceful messages
- domainKey registered with OpenAI (not ‘local-dev’)
References
| File | Content |
|---|---|
| store.md | Store interface, PostgreSQL, SQLite implementations |
| models.md | LLM provider configurations |
| mcp.md | Function tools, MCP servers, hosted tools |
| sessions.md | Multi-user session management, API endpoints |
| frontend.md | React/JS integration, customization, widgets |
| troubleshooting.md | Common errors and solutions |
External Documentation
- ChatKit Python: https://openai.github.io/chatkit-python/
- ChatKit JS: https://openai.github.io/chatkit-js/
- OpenAI Agents SDK: https://openai.github.io/openai-agents-python/