Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/agents-sdk/patterns.md
1# Patterns & Use Cases23## AI Chat w/Tools45**Server (AIChatAgent):**67```ts8import { AIChatAgent } from "@cloudflare/ai-chat";9import { openai } from "@ai-sdk/openai";10import { tool } from "ai";11import { z } from "zod";1213export class ChatAgent extends AIChatAgent<Env> {14async onChatMessage(onFinish) {15return this.streamText({16model: openai("gpt-4"),17messages: this.messages, // Auto-managed18tools: {19getWeather: tool({20description: "Get current weather",21parameters: z.object({ city: z.string() }),22execute: async ({ city }) => `Weather in ${city}: Sunny, 72°F`23}),24searchDocs: tool({25description: "Search documentation",26parameters: z.object({ query: z.string() }),27execute: async ({ query }) => JSON.stringify(28this.sql<{title, content}>`SELECT title, content FROM docs WHERE content LIKE ${'%' + query + '%'}`29)30})31},32onFinish,33});34}35}36```3738**Client (React):**3940```tsx41import { useAgent } from "agents/react";42import { useAgentChat } from "@cloudflare/ai-chat/react";4344function ChatUI() {45const agent = useAgent({ agent: "ChatAgent" });46const { messages, input, handleInputChange, handleSubmit, isLoading } = useAgentChat({ agent });4748return (49<div>50{messages.map(m => <div key={m.id}>{m.role}: {m.content}</div>)}51<form onSubmit={handleSubmit}>52<input value={input} onChange={handleInputChange} disabled={isLoading} />53<button disabled={isLoading}>Send</button>54</form>55</div>56);57}58```5960## Human-in-the-Loop (Client Tools)6162Server defines tool, client executes:6364```ts65// Server66export class ChatAgent extends AIChatAgent<Env> {67async onChatMessage(onFinish) {68return this.streamText({69model: openai("gpt-4"),70messages: this.messages,71tools: {72confirmAction: tool({73description: "Ask user to confirm",74parameters: z.object({ action: z.string() }),75execute: "client", // Client-side execution76})77},78onFinish,79});80}81}8283// Client84const { messages } = useAgentChat({85agent,86onToolCall: async (toolCall) => {87if (toolCall.toolName === "confirmAction") {88return { confirmed: window.confirm(`Confirm: ${toolCall.args.action}?`) };89}90}91});92```9394## Task Queue & Scheduled Processing9596```ts97export class TaskAgent extends Agent<Env> {98onStart() {99this.schedule("*/5 * * * *", "processQueue", {}); // Every 5 min100this.schedule("0 0 * * *", "dailyCleanup", {}); // Daily101}102103async onRequest(req: Request) {104await this.queue("processVideo", { videoId: (await req.json()).videoId });105return Response.json({ queued: true });106}107108async processQueue() {109const tasks = await this.dequeue(10);110for (const task of tasks) {111if (task.name === "processVideo") await this.processVideo(task.data.videoId);112}113}114115async dailyCleanup() {116this.sql`DELETE FROM logs WHERE created_at < ${Date.now() - 86400000}`;117}118}119```120121## Manual WebSocket Chat122123Custom protocols (non-AI):124125```ts126export class ChatAgent extends Agent<Env> {127async onConnect(conn: Connection, ctx: ConnectionContext) {128conn.accept();129conn.setState({userId: ctx.request.headers.get("X-User-ID") || "anon"});130conn.send(JSON.stringify({type: "history", messages: this.state.messages}));131}132133async onMessage(conn: Connection, msg: WSMessage) {134const newMsg = {userId: conn.state.userId, text: JSON.parse(msg as string).text, timestamp: Date.now()};135this.setState({messages: [...this.state.messages, newMsg]});136this.connections.forEach(c => c.send(JSON.stringify(newMsg)));137}138}139```140141## Email Processing w/AI142143```ts144export class EmailAgent extends Agent<Env> {145async onEmail(email: AgentEmail) {146const [text, from, subject] = [await email.text(), email.from, email.headers.get("subject") || ""];147this.sql`INSERT INTO emails (from_addr, subject, body) VALUES (${from}, ${subject}, ${text})`;148149const { text: summary } = await generateText({150model: openai("gpt-4o-mini"), prompt: `Summarize: ${subject}\n\n${text}`151});152153this.connections.forEach(c => c.send(JSON.stringify({type: "new_email", from, summary})));154if (summary.includes("urgent")) await this.schedule(0, "sendAutoReply", { to: from });155}156}157```158159## Real-time Collaboration160161```ts162export class GameAgent extends Agent<Env> {163initialState = { players: [], gameStarted: false };164165async onConnect(conn: Connection, ctx: ConnectionContext) {166conn.accept();167const playerId = ctx.request.headers.get("X-Player-ID") || crypto.randomUUID();168conn.setState({ playerId });169170const newPlayer = { id: playerId, score: 0 };171this.setState({...this.state, players: [...this.state.players, newPlayer]});172this.connections.forEach(c => c.send(JSON.stringify({type: "player_joined", player: newPlayer})));173}174175async onMessage(conn: Connection, msg: WSMessage) {176const m = JSON.parse(msg as string);177178if (m.type === "move") {179this.setState({180...this.state,181players: this.state.players.map(p => p.id === conn.state.playerId ? {...p, score: p.score + m.points} : p)182});183this.connections.forEach(c => c.send(JSON.stringify({type: "player_moved", playerId: conn.state.playerId})));184}185186if (m.type === "start" && this.state.players.length >= 2) {187this.setState({...this.state, gameStarted: true});188this.connections.forEach(c => c.send(JSON.stringify({type: "game_started"})));189}190}191}192```193