# 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 (
);
}
```
### 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 `