# Copilot SDK - Full Documentation > Open-source SDK for building AI assistants with App Context Awareness. --- ## Deploy URL: https://copilot-sdk.yourgpt.ai/docs/deploy Description: Deploy your Copilot backend to any platform Your Copilot backend uses standard Web APIs (`fetch`, `Response`, `ReadableStream`), so the **same code runs everywhere** — Vercel, Cloudflare, Deno, AWS, or your own servers. --- ## Vercel Deploy to Vercel with Next.js. Supports both Serverless and Edge runtimes. ```ts title="app/api/chat/route.ts" import { createRuntime } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; const runtime = createRuntime({ provider: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); export async function POST(req: Request) { const body = await req.json(); return runtime.stream(body).toResponse(); } ``` Edge functions have faster cold starts (~25ms vs ~250ms) and run closer to users. ```ts title="app/api/chat/route.ts" import { createRuntime } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; export const runtime = 'edge'; // Enable Edge Runtime const rt = createRuntime({ provider: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); export async function POST(req: Request) { const body = await req.json(); return rt.stream(body).toResponse(); } ``` Edge functions have a 30-second execution limit. For long-running agent loops, use Serverless. ### Non-Streaming ```ts title="app/api/chat/route.ts" export async function POST(req: Request) { const body = await req.json(); const result = await runtime.chat(body); return Response.json(result); } ``` ### Deploy ```bash npm i -g vercel vercel ``` Set your environment variable in the Vercel dashboard or CLI: ```bash vercel env add OPENAI_API_KEY ``` --- ## Cloudflare Workers Deploy to Cloudflare's edge network with Workers. Runs in 300+ locations worldwide. ```ts title="src/index.ts" import { createRuntime, createHonoApp } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; export interface Env { OPENAI_API_KEY: string; } export default { async fetch(request: Request, env: Env): Promise { const runtime = createRuntime({ provider: createOpenAI({ apiKey: env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); return createHonoApp(runtime).fetch(request, env); }, }; ``` ```ts title="src/index.ts" import { createRuntime } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; export interface Env { OPENAI_API_KEY: string; } export default { async fetch(request: Request, env: Env): Promise { const runtime = createRuntime({ provider: createOpenAI({ apiKey: env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); if (request.method !== 'POST') { return new Response('Method not allowed', { status: 405 }); } const body = await request.json(); const result = await runtime.chat(body); return Response.json(result); }, }; ``` ### Configuration ```toml title="wrangler.toml" name = "my-copilot-api" main = "src/index.ts" compatibility_date = "2024-01-01" compatibility_flags = ["nodejs_compat"] ``` ### Deploy ```bash npm i -g wrangler # Add your API key as a secret wrangler secret put OPENAI_API_KEY # Deploy wrangler deploy ``` Your API will be available at `https://my-copilot-api..workers.dev` --- ## Deno Deploy Deploy to Deno's global edge network with zero configuration. ```ts title="main.ts" import { createRuntime, createHonoApp } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; const runtime = createRuntime({ provider: createOpenAI({ apiKey: Deno.env.get('OPENAI_API_KEY') }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); Deno.serve(createHonoApp(runtime).fetch); ``` ```ts title="main.ts" import { createRuntime } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; const runtime = createRuntime({ provider: createOpenAI({ apiKey: Deno.env.get('OPENAI_API_KEY') }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); Deno.serve(async (req: Request) => { if (req.method !== 'POST') { return new Response('Method not allowed', { status: 405 }); } const body = await req.json(); const result = await runtime.chat(body); return Response.json(result); }); ``` ### Deploy ```bash # Install Deno Deploy CLI deno install -Arf jsr:@deno/deployctl # Deploy deployctl deploy --project=my-copilot main.ts ``` Set environment variables in the Deno Deploy dashboard. --- ## AWS Lambda Deploy to AWS Lambda using SST, Serverless Framework, or AWS CDK. ```ts title="packages/functions/src/chat.ts" import { createRuntime } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; import { Resource } from 'sst'; const runtime = createRuntime({ provider: createOpenAI({ apiKey: Resource.OpenAIApiKey.value }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); export async function handler(event: any) { const body = JSON.parse(event.body); // Non-streaming (Lambda default) const result = await runtime.chat(body); return { statusCode: 200, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(result), }; } ``` ```ts title="sst.config.ts" export default $config({ app(input) { return { name: 'my-copilot', region: 'us-east-1' }; }, async run() { const api = new sst.aws.Function('Chat', { handler: 'packages/functions/src/chat.handler', url: true, }); return { url: api.url }; }, }); ``` ```ts title="handler.ts" import { createRuntime } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; import type { APIGatewayProxyHandler } from 'aws-lambda'; const runtime = createRuntime({ provider: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); export const chat: APIGatewayProxyHandler = async (event) => { const body = JSON.parse(event.body || '{}'); const result = await runtime.chat(body); return { statusCode: 200, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(result), }; }; ``` ```yaml title="serverless.yml" service: my-copilot provider: name: aws runtime: nodejs20.x environment: OPENAI_API_KEY: ${env:OPENAI_API_KEY} functions: chat: handler: handler.chat events: - http: path: /chat method: post ``` ### Streaming on Lambda AWS Lambda requires **Function URL with streaming** for SSE responses. Standard API Gateway does not support streaming. ```ts title="packages/functions/src/chat-stream.ts" const runtime = createRuntime({ provider: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }), model: 'gpt-4o', }); export const handler = streamHandle(createHonoApp(runtime)); ``` ### Deploy ```bash # SST npx sst deploy # Serverless serverless deploy ``` --- ## Express / Node.js Deploy to any Node.js hosting (Railway, Render, Fly.io, DigitalOcean, etc.). ```ts title="server.ts" import express from 'express'; import { createRuntime } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; const app = express(); app.use(express.json()); const runtime = createRuntime({ provider: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); app.post('/api/chat', async (req, res) => { await runtime.stream(req.body).pipeToResponse(res); }); app.listen(3000, () => { console.log('Server running on http://localhost:3000'); }); ``` ```ts title="server.ts" import express from 'express'; import { createRuntime } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; const app = express(); app.use(express.json()); const runtime = createRuntime({ provider: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); app.post('/api/chat', async (req, res) => { const result = await runtime.chat(req.body); res.json(result); }); app.listen(3000, () => { console.log('Server running on http://localhost:3000'); }); ``` ```ts title="server.ts" import express from 'express'; import { createRuntime } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; const app = express(); app.use(express.json()); const runtime = createRuntime({ provider: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); // Streaming endpoint app.post('/api/chat/stream', async (req, res) => { await runtime.stream(req.body).pipeToResponse(res); }); // Non-streaming endpoint app.post('/api/chat', async (req, res) => { const result = await runtime.chat(req.body); res.json(result); }); app.listen(3000, () => { console.log('Server running on http://localhost:3000'); }); ``` ### Deploy to Popular Platforms ```bash npm i -g @railway/cli railway login railway init railway up ``` Set `OPENAI_API_KEY` in Railway dashboard → Variables. 1. Push to GitHub 2. Create new **Web Service** on Render 3. Set build command: `npm install && npm run build` 4. Set start command: `npm start` 5. Add `OPENAI_API_KEY` in Environment ```bash npm i -g flyctl fly launch fly secrets set OPENAI_API_KEY=sk-... fly deploy ``` --- ## Docker Self-host your Copilot backend with Docker. ```ts title="server.ts" import { serve } from '@hono/node-server'; import { createRuntime, createHonoApp } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; const runtime = createRuntime({ provider: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); const port = Number(process.env.PORT) || 3000; serve({ fetch: createHonoApp(runtime).fetch, port }, (info) => { console.log(`Server running on http://localhost:${info.port}`); }); ``` ```ts title="server.ts" import { serve } from '@hono/node-server'; import { Hono } from 'hono'; import { createRuntime } from '@yourgpt/llm-sdk'; import { createOpenAI } from '@yourgpt/llm-sdk/openai'; const runtime = createRuntime({ provider: createOpenAI({ apiKey: process.env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); const app = new Hono(); app.post('/api/chat', async (c) => { const body = await c.req.json(); const result = await runtime.chat(body); return c.json(result); }); const port = Number(process.env.PORT) || 3000; serve({ fetch: app.fetch, port }); ``` ### Dockerfile ```dockerfile title="Dockerfile" FROM node:20-slim AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:20-slim WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /app/package.json ./ ENV NODE_ENV=production EXPOSE 3000 CMD ["node", "dist/server.js"] ``` ### Docker Compose ```yaml title="docker-compose.yml" services: copilot: build: . ports: - "3000:3000" environment: - OPENAI_API_KEY=${OPENAI_API_KEY} restart: unless-stopped ``` ### Run ```bash # Build and run docker compose up -d # Or without compose docker build -t my-copilot . docker run -p 3000:3000 -e OPENAI_API_KEY=sk-... my-copilot ``` --- ## Bun Deploy with Bun for faster startup and better performance. ```ts title="server.ts" const runtime = createRuntime({ provider: createOpenAI({ apiKey: Bun.env.OPENAI_API_KEY }), model: 'gpt-4o', systemPrompt: 'You are a helpful assistant.', }); const app = createHonoApp(runtime); export default { port: 3000, fetch: app.fetch, }; ``` ### Run ```bash bun run server.ts ``` --- ## Connect Frontend Point your Copilot SDK frontend to your deployed API: ```tsx title="app/providers.tsx" 'use client'; export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` | Mode | Server Method | CopilotProvider | |------|---------------|-----------------| | Streaming | `.stream(body).toResponse()` | `streaming={true}` (default) | | Non-streaming | `await runtime.chat(body)` | `streaming={false}` | --- ## CORS If your frontend and backend are on different domains, add CORS headers: ```ts title="app/api/chat/route.ts" const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', }; export async function OPTIONS() { return new Response(null, { headers: corsHeaders }); } export async function POST(req: Request) { const body = await req.json(); const response = runtime.stream(body).toResponse(); // Add CORS headers Object.entries(corsHeaders).forEach(([key, value]) => { response.headers.set(key, value); }); return response; } ``` ```ts import { cors } from 'hono/cors'; const app = createHonoApp(runtime); app.use('*', cors()); ``` ```ts import cors from 'cors'; app.use(cors()); ``` --- ## Next Steps - [Server Setup](/docs/server) — Full runtime configuration and options - [Tools](/docs/tools) — Add function calling to your Copilot - [Providers](/docs/providers) — Configure different LLM providers --- ## Examples URL: https://copilot-sdk.yourgpt.ai/docs/examples Description: Real-world Copilot SDK implementations --- ## Generative UI URL: https://copilot-sdk.yourgpt.ai/docs/generative-ui Description: Render rich React components from AI tool results — per-tool custom renderers or AI-driven built-in components Instead of showing raw JSON or plain text from tool calls, render interactive UI directly inside the chat — from your own branded React components per tool, to fully AI-generated dashboards, charts, and layouts running in a sandboxed iframe. --- ## Two Approaches | | `toolRenderers` | `useGenerativeUI` (experimental) | |---|---|---| | **What it does** | Your React component renders per tool result | AI writes full HTML + Tailwind + Chart.js, runs in a sandboxed iframe — or picks a typed renderer (table, stat, card, chart) | | **Who decides the UI** | You — one renderer per tool | The AI — generates or selects based on the data | | **Setup** | Pass `toolRenderers` to `` | One `useGenerativeUI()` call + backend `generativeUITool()` | | **Best for** | Domain-specific, branded components | Dashboards, charts, tables, any data layout you haven't pre-built | | **Customization** | Full control | Override any built-in renderer | --- ## Approach 1 — `toolRenderers` Map tool names to React components. Each component receives the tool's args and result as props. ### Basic example ```tsx function WeatherCard({ data, status }) { if (status === "executing") { return
Loading weather...
; } return (

{data.city}

{data.temp}°F

{data.conditions}

); } ``` ### ToolRendererProps Every renderer receives these props: ```typescript interface ToolRendererProps { status: "pending" | "executing" | "completed" | "error" | "failed" | "rejected"; args: Record; // arguments passed to the tool data?: unknown; // result (when completed) error?: string; // error message (when failed) executionId: string; toolName: string; } ``` ### Handling all states ```tsx function ChartCard({ status, data, error, args }: ToolRendererProps) { if (status === "pending" || status === "executing") { return (

Generating {args.metric} chart...

); } if (status === "error" || status === "failed") { return (

Failed to load chart

{error}

); } if (status === "rejected") { return (

Chart request was declined

); } return (

{data.title}

); } ``` ### Interactive components Renderers can be fully interactive and call back into the chat: ```tsx function ProductCard({ data }: ToolRendererProps) { const [quantity, setQuantity] = useState(1); const { sendMessage } = useCopilot(); return (
{data.name}

{data.name}

${data.price}

setQuantity(Number(e.target.value))} className="w-16 border rounded px-2 py-1" />
); } ``` ### Control AI response verbosity Return `_aiResponseMode: "brief"` from your tool handler to prevent the AI from describing what the UI already shows: ```tsx handler: async ({ timeRange }) => { const data = await fetchDashboardData(timeRange); return { success: true, data, _aiResponseMode: "brief", _aiContext: `Dashboard for ${timeRange}`, }; }, ``` Use `_aiResponseMode: "brief"` when your UI component is self-explanatory. The AI gives a short acknowledgment instead of narrating the data. --- ## Approach 2 — AI-Generated UI (Experimental) `@yourgpt/copilot-sdk/experimental` — APIs may change without a semver major bump. The AI calls a single `render_ui` tool and generates the UI itself. The standout capability is `type: "html"` — the AI writes full HTML with Tailwind CSS and Chart.js, rendered in a sandboxed iframe. No pre-built component needed. For structured data it can also pick typed renderers (`table`, `stat`, `card`, `chart`) automatically. ``` User: "Show Q1 revenue by region" ↓ AI calls: render_ui({ type: "chart", chartType: "bar", labels: ["NA","EU","APAC"], datasets: [...] }) ↓ UI renders: [Bar chart] User: "Build an analytics dashboard" ↓ AI calls: render_ui({ type: "html", html: "
...
", height: "600px" }) ↓ UI renders: [Full dashboard in sandboxed iframe with Tailwind + Chart.js] ``` ### Setup Register `generativeUITool()` in your route. The key becomes the tool name. ```typescript export async function POST(req: Request) { const { messages } = await req.json(); const result = await streamText({ model: openai("gpt-4o"), system: "Use render_ui for any data, charts, or structured results.", messages, tools: { render_ui: generativeUITool(), }, }); return result.toDataStreamResponse(); } ``` Call `useGenerativeUI()` once in your component tree — it registers the renderer automatically. ```tsx function App() { useGenerativeUI({ chartRenderer: MyChartComponent, // required for chart type }); return ; } ``` ### Built-in component types | Type | When the AI uses it | Renderer | |------|-------------------|----------| | `html` | Dashboards, custom layouts, anything freeform | `HtmlRenderer` — sandboxed `