mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 06:11:27 -07:00
193 lines
5.5 KiB
Markdown
193 lines
5.5 KiB
Markdown
# Patterns & Use Cases
|
|
|
|
## AI Chat w/Tools
|
|
|
|
**Server (AIChatAgent):**
|
|
|
|
```ts
|
|
import { AIChatAgent } from "agents";
|
|
import { openai } from "@ai-sdk/openai";
|
|
import { tool } from "ai";
|
|
import { z } from "zod";
|
|
|
|
export class ChatAgent extends AIChatAgent<Env> {
|
|
async onChatMessage(onFinish) {
|
|
return this.streamText({
|
|
model: openai("gpt-4"),
|
|
messages: this.messages, // Auto-managed
|
|
tools: {
|
|
getWeather: tool({
|
|
description: "Get current weather",
|
|
parameters: z.object({ city: z.string() }),
|
|
execute: async ({ city }) => `Weather in ${city}: Sunny, 72°F`
|
|
}),
|
|
searchDocs: tool({
|
|
description: "Search documentation",
|
|
parameters: z.object({ query: z.string() }),
|
|
execute: async ({ query }) => JSON.stringify(
|
|
this.sql<{title, content}>`SELECT title, content FROM docs WHERE content LIKE ${'%' + query + '%'}`
|
|
)
|
|
})
|
|
},
|
|
onFinish,
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
**Client (React):**
|
|
|
|
```tsx
|
|
import { useAgent } from "agents/react";
|
|
import { useAgentChat } from "agents/ai-react";
|
|
|
|
function ChatUI() {
|
|
const agent = useAgent({ agent: "ChatAgent" });
|
|
const { messages, input, handleInputChange, handleSubmit, isLoading } = useAgentChat({ agent });
|
|
|
|
return (
|
|
<div>
|
|
{messages.map(m => <div key={m.id}>{m.role}: {m.content}</div>)}
|
|
<form onSubmit={handleSubmit}>
|
|
<input value={input} onChange={handleInputChange} disabled={isLoading} />
|
|
<button disabled={isLoading}>Send</button>
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
## Human-in-the-Loop (Client Tools)
|
|
|
|
Server defines tool, client executes:
|
|
|
|
```ts
|
|
// Server
|
|
export class ChatAgent extends AIChatAgent<Env> {
|
|
async onChatMessage(onFinish) {
|
|
return this.streamText({
|
|
model: openai("gpt-4"),
|
|
messages: this.messages,
|
|
tools: {
|
|
confirmAction: tool({
|
|
description: "Ask user to confirm",
|
|
parameters: z.object({ action: z.string() }),
|
|
execute: "client", // Client-side execution
|
|
})
|
|
},
|
|
onFinish,
|
|
});
|
|
}
|
|
}
|
|
|
|
// Client
|
|
const { messages } = useAgentChat({
|
|
agent,
|
|
onToolCall: async (toolCall) => {
|
|
if (toolCall.toolName === "confirmAction") {
|
|
return { confirmed: window.confirm(`Confirm: ${toolCall.args.action}?`) };
|
|
}
|
|
}
|
|
});
|
|
```
|
|
|
|
## Task Queue & Scheduled Processing
|
|
|
|
```ts
|
|
export class TaskAgent extends Agent<Env> {
|
|
onStart() {
|
|
this.schedule("*/5 * * * *", "processQueue", {}); // Every 5 min
|
|
this.schedule("0 0 * * *", "dailyCleanup", {}); // Daily
|
|
}
|
|
|
|
async onRequest(req: Request) {
|
|
await this.queue("processVideo", { videoId: (await req.json()).videoId });
|
|
return Response.json({ queued: true });
|
|
}
|
|
|
|
async processQueue() {
|
|
const tasks = await this.dequeue(10);
|
|
for (const task of tasks) {
|
|
if (task.name === "processVideo") await this.processVideo(task.data.videoId);
|
|
}
|
|
}
|
|
|
|
async dailyCleanup() {
|
|
this.sql`DELETE FROM logs WHERE created_at < ${Date.now() - 86400000}`;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Manual WebSocket Chat
|
|
|
|
Custom protocols (non-AI):
|
|
|
|
```ts
|
|
export class ChatAgent extends Agent<Env> {
|
|
async onConnect(conn: Connection, ctx: ConnectionContext) {
|
|
conn.accept();
|
|
conn.setState({userId: ctx.request.headers.get("X-User-ID") || "anon"});
|
|
conn.send(JSON.stringify({type: "history", messages: this.state.messages}));
|
|
}
|
|
|
|
async onMessage(conn: Connection, msg: WSMessage) {
|
|
const newMsg = {userId: conn.state.userId, text: JSON.parse(msg as string).text, timestamp: Date.now()};
|
|
this.setState({messages: [...this.state.messages, newMsg]});
|
|
this.connections.forEach(c => c.send(JSON.stringify(newMsg)));
|
|
}
|
|
}
|
|
```
|
|
|
|
## Email Processing w/AI
|
|
|
|
```ts
|
|
export class EmailAgent extends Agent<Env> {
|
|
async onEmail(email: AgentEmail) {
|
|
const [text, from, subject] = [await email.text(), email.from, email.headers.get("subject") || ""];
|
|
this.sql`INSERT INTO emails (from_addr, subject, body) VALUES (${from}, ${subject}, ${text})`;
|
|
|
|
const { text: summary } = await generateText({
|
|
model: openai("gpt-4o-mini"), prompt: `Summarize: ${subject}\n\n${text}`
|
|
});
|
|
|
|
this.connections.forEach(c => c.send(JSON.stringify({type: "new_email", from, summary})));
|
|
if (summary.includes("urgent")) await this.schedule(0, "sendAutoReply", { to: from });
|
|
}
|
|
}
|
|
```
|
|
|
|
## Real-time Collaboration
|
|
|
|
```ts
|
|
export class GameAgent extends Agent<Env> {
|
|
initialState = { players: [], gameStarted: false };
|
|
|
|
async onConnect(conn: Connection, ctx: ConnectionContext) {
|
|
conn.accept();
|
|
const playerId = ctx.request.headers.get("X-Player-ID") || crypto.randomUUID();
|
|
conn.setState({ playerId });
|
|
|
|
const newPlayer = { id: playerId, score: 0 };
|
|
this.setState({...this.state, players: [...this.state.players, newPlayer]});
|
|
this.connections.forEach(c => c.send(JSON.stringify({type: "player_joined", player: newPlayer})));
|
|
}
|
|
|
|
async onMessage(conn: Connection, msg: WSMessage) {
|
|
const m = JSON.parse(msg as string);
|
|
|
|
if (m.type === "move") {
|
|
this.setState({
|
|
...this.state,
|
|
players: this.state.players.map(p => p.id === conn.state.playerId ? {...p, score: p.score + m.points} : p)
|
|
});
|
|
this.connections.forEach(c => c.send(JSON.stringify({type: "player_moved", playerId: conn.state.playerId})));
|
|
}
|
|
|
|
if (m.type === "start" && this.state.players.length >= 2) {
|
|
this.setState({...this.state, gameStarted: true});
|
|
this.connections.forEach(c => c.send(JSON.stringify({type: "game_started"})));
|
|
}
|
|
}
|
|
}
|
|
```
|