@cloudflare/ai-chat 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,26 @@
1
+ # @cloudflare/ai-chat
2
+
3
+ ## 0.0.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`a5d0137`](https://github.com/cloudflare/agents/commit/a5d01379b9ad2d88bc028c50f1858b4e69f106c5) Thanks [@threepointone](https://github.com/threepointone)! - trigger a new release
8
+
9
+ - Updated dependencies [[`a5d0137`](https://github.com/cloudflare/agents/commit/a5d01379b9ad2d88bc028c50f1858b4e69f106c5)]:
10
+ - agents@0.3.3
11
+
12
+ ## 0.0.2
13
+
14
+ ### Patch Changes
15
+
16
+ - [#756](https://github.com/cloudflare/agents/pull/756) [`0c4275f`](https://github.com/cloudflare/agents/commit/0c4275f8f4b71c264c32c3742d151ef705739c2f) Thanks [@threepointone](https://github.com/threepointone)! - feat: split ai-chat and codemode into separate packages
17
+
18
+ Extract @cloudflare/ai-chat and @cloudflare/codemode into their own packages
19
+ with comprehensive READMEs. Update agents README to remove chat-specific
20
+ content and point to new packages. Fix documentation imports to reflect
21
+ new package structure.
22
+
23
+ Maintains backward compatibility, no breaking changes.
24
+
25
+ - Updated dependencies [[`0c4275f`](https://github.com/cloudflare/agents/commit/0c4275f8f4b71c264c32c3742d151ef705739c2f), [`f12553f`](https://github.com/cloudflare/agents/commit/f12553f2fa65912c68d9a7620b9a11b70b8790a2)]:
26
+ - agents@0.3.2
package/README.md ADDED
@@ -0,0 +1,570 @@
1
+ ### 💬 `@cloudflare/ai-chat` - AI-Powered Conversations
2
+
3
+ ![npm install @cloudflare/ai-chat](../../assets/npm-install-agents.svg)
4
+
5
+ Welcome to `@cloudflare/ai-chat`, a specialized package for building intelligent chat experiences with persistent, stateful AI agents. Built on top of the `agents` framework and integrated with the AI SDK, this package provides everything you need to create conversational AI applications that remember, learn, and evolve.
6
+
7
+ #### The Nature of AI Chat
8
+
9
+ AI chat transcends simple request-response patterns. It's a continuous dialogue where:
10
+
11
+ - **Persistence**: Conversations are automatically saved and restored across sessions
12
+ - **Streaming**: Real-time responses that flow naturally to users
13
+ - **Resumability**: Automatic recovery from disconnections without losing context
14
+ - **Tools**: Seamless integration of client and server-side capabilities
15
+ - **State**: Each conversation maintains its own memory and understanding
16
+
17
+ Built on Cloudflare's global network with Durable Objects, your chat agents persist across time, maintaining context and relationships with users.
18
+
19
+ #### 💫 Core Principles
20
+
21
+ 1. **Automatic Persistence**: Messages are saved automatically - no manual storage needed
22
+ 2. **Resumable Streaming**: Disconnections don't break the flow - streams resume automatically
23
+ 3. **Tool Harmony**: Mix server and client-side tools seamlessly
24
+ 4. **AI SDK Integration**: Built on the latest AI SDK v6 for maximum compatibility
25
+ 5. **React First**: Designed for modern React applications with hooks
26
+
27
+ ---
28
+
29
+ ### 🌱 Beginning the Journey
30
+
31
+ Install the package in your project:
32
+
33
+ ```sh
34
+ npm install @cloudflare/ai-chat agents ai
35
+ ```
36
+
37
+ You'll also need the `agents` package (for the base `Agent` class) and `ai` (for AI SDK integration).
38
+
39
+ ### 📝 Your First Chat Agent
40
+
41
+ Create a chat agent that remembers and responds:
42
+
43
+ ```ts
44
+ import { AIChatAgent } from "@cloudflare/ai-chat";
45
+ import { openai } from "@ai-sdk/openai";
46
+ import {
47
+ streamText,
48
+ convertToModelMessages,
49
+ createUIMessageStream,
50
+ createUIMessageStreamResponse
51
+ } from "ai";
52
+
53
+ export class ChatAgent extends AIChatAgent<Env> {
54
+ async onChatMessage() {
55
+ const stream = createUIMessageStream({
56
+ execute: async ({ writer }) => {
57
+ const result = streamText({
58
+ model: openai("gpt-4o"),
59
+ messages: await convertToModelMessages(this.messages)
60
+ });
61
+
62
+ writer.merge(result.toUIMessageStream());
63
+ }
64
+ });
65
+
66
+ return createUIMessageStreamResponse({ stream });
67
+ }
68
+ }
69
+ ```
70
+
71
+ That's it! Your agent now:
72
+
73
+ - Automatically persists all messages
74
+ - Streams responses in real-time
75
+ - Handles reconnections gracefully
76
+ - Maintains conversation context
77
+
78
+ ### 🏰 Creating Space
79
+
80
+ Define your chat agent in your `wrangler.toml`:
81
+
82
+ ```jsonc
83
+ {
84
+ "durable_objects": {
85
+ "bindings": [
86
+ {
87
+ "name": "ChatAgent",
88
+ "class_name": "ChatAgent"
89
+ }
90
+ ]
91
+ },
92
+ "migrations": [
93
+ {
94
+ "tag": "v1",
95
+ "new_sqlite_classes": ["ChatAgent"]
96
+ }
97
+ ]
98
+ }
99
+ ```
100
+
101
+ ### 🎭 Patterns of Conversation
102
+
103
+ #### Streaming Responses (Recommended)
104
+
105
+ For real-time interaction, use streaming:
106
+
107
+ ```ts
108
+ import { AIChatAgent } from "@cloudflare/ai-chat";
109
+ import { openai } from "@ai-sdk/openai";
110
+ import {
111
+ streamText,
112
+ convertToModelMessages,
113
+ createUIMessageStream,
114
+ createUIMessageStreamResponse
115
+ } from "ai";
116
+
117
+ export class StreamingChat extends AIChatAgent<Env> {
118
+ async onChatMessage(onFinish) {
119
+ const stream = createUIMessageStream({
120
+ execute: async ({ writer }) => {
121
+ const result = streamText({
122
+ model: openai("gpt-4o"),
123
+ messages: await convertToModelMessages(this.messages),
124
+ onFinish
125
+ });
126
+
127
+ writer.merge(result.toUIMessageStream());
128
+ }
129
+ });
130
+
131
+ return createUIMessageStreamResponse({ stream });
132
+ }
133
+ }
134
+ ```
135
+
136
+ #### Non-Streaming Responses
137
+
138
+ For simpler use cases without real-time updates:
139
+
140
+ ```ts
141
+ import { AIChatAgent } from "@cloudflare/ai-chat";
142
+ import { openai } from "@ai-sdk/openai";
143
+ import { generateText, convertToModelMessages } from "ai";
144
+
145
+ export class SimpleChat extends AIChatAgent<Env> {
146
+ async onChatMessage() {
147
+ const result = await generateText({
148
+ model: openai("gpt-4o"),
149
+ messages: await convertToModelMessages(this.messages)
150
+ });
151
+
152
+ const message = result.toUIMessage({
153
+ metadata: {
154
+ model: "gpt-4o",
155
+ totalTokens: result.usage?.totalTokens
156
+ }
157
+ });
158
+
159
+ return new Response(JSON.stringify(message), {
160
+ headers: { "Content-Type": "application/json" }
161
+ });
162
+ }
163
+ }
164
+ ```
165
+
166
+ ### 🌐 Client Integration
167
+
168
+ Connect your React application to your chat agent:
169
+
170
+ ```tsx
171
+ import { useAgent } from "agents/react";
172
+ import { useAgentChat } from "@cloudflare/ai-chat/react";
173
+
174
+ function ChatInterface() {
175
+ // Connect to the agent
176
+ const agent = useAgent({
177
+ agent: "ChatAgent",
178
+ name: "my-chat"
179
+ });
180
+
181
+ // Set up the chat interaction
182
+ const { messages, sendMessage, clearHistory, status } = useAgentChat({
183
+ agent
184
+ });
185
+
186
+ const [input, setInput] = useState("");
187
+
188
+ const handleSubmit = async (e: React.FormEvent) => {
189
+ e.preventDefault();
190
+ if (!input.trim()) return;
191
+
192
+ await sendMessage({
193
+ role: "user",
194
+ parts: [{ type: "text", text: input }]
195
+ });
196
+ setInput("");
197
+ };
198
+
199
+ return (
200
+ <div className="chat-interface">
201
+ <div className="messages">
202
+ {messages.map((message) => (
203
+ <div key={message.id} className="message">
204
+ <div className="role">{message.role}</div>
205
+ <div className="content">
206
+ {message.parts.map((part, i) => {
207
+ if (part.type === "text")
208
+ return <span key={i}>{part.text}</span>;
209
+ return null;
210
+ })}
211
+ </div>
212
+ </div>
213
+ ))}
214
+ </div>
215
+
216
+ <form onSubmit={handleSubmit}>
217
+ <input
218
+ value={input}
219
+ onChange={(e) => setInput(e.target.value)}
220
+ placeholder="Type your message..."
221
+ />
222
+ </form>
223
+
224
+ <button onClick={clearHistory}>Clear Chat</button>
225
+ </div>
226
+ );
227
+ }
228
+ ```
229
+
230
+ ### 🌊 Resumable Streaming
231
+
232
+ One of the most powerful features of `@cloudflare/ai-chat` is **automatic resumable streaming**. When a client disconnects during a stream, the response automatically resumes when they reconnect.
233
+
234
+ #### How It Works
235
+
236
+ 1. **During streaming**: All chunks are automatically persisted to SQLite
237
+ 2. **On disconnect**: The stream continues server-side, buffering chunks
238
+ 3. **On reconnect**: Client receives all buffered chunks and continues streaming
239
+
240
+ It just works! No special configuration needed.
241
+
242
+ #### Example
243
+
244
+ ```tsx
245
+ import { useAgent } from "agents/react";
246
+ import { useAgentChat } from "@cloudflare/ai-chat/react";
247
+
248
+ function ResumableChat() {
249
+ const agent = useAgent({
250
+ agent: "ResumableStreamingChat",
251
+ name: "demo"
252
+ });
253
+
254
+ // resume: true is the default - streams automatically resume on reconnect
255
+ const { messages, sendMessage, status } = useAgentChat({
256
+ agent
257
+ // resume: true is the default
258
+ });
259
+
260
+ // Try it: Start a long response, refresh the page, and watch it resume!
261
+ return (
262
+ <div>
263
+ {status === "streaming" && <div>Streaming...</div>}
264
+ {/* Your chat UI */}
265
+ </div>
266
+ );
267
+ }
268
+ ```
269
+
270
+ The server automatically handles stream resumption - no extra code needed on your part.
271
+
272
+ ### 🔧 Tool Support
273
+
274
+ `@cloudflare/ai-chat` supports both server-side and client-side tools, allowing you to build rich interactive experiences.
275
+
276
+ #### Server-Side Tools
277
+
278
+ Define tools that execute on the server:
279
+
280
+ ```ts
281
+ import { AIChatAgent } from "@cloudflare/ai-chat";
282
+ import { openai } from "@ai-sdk/openai";
283
+ import { streamText, convertToModelMessages, tool } from "ai";
284
+ import { z } from "zod";
285
+ import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
286
+
287
+ export class ToolChat extends AIChatAgent<Env> {
288
+ async onChatMessage() {
289
+ const stream = createUIMessageStream({
290
+ execute: async ({ writer }) => {
291
+ const result = streamText({
292
+ model: openai("gpt-4o"),
293
+ messages: await convertToModelMessages(this.messages),
294
+ tools: {
295
+ getWeather: tool({
296
+ description: "Get weather for a city",
297
+ parameters: z.object({ city: z.string() }),
298
+ execute: async ({ city }) => {
299
+ // Fetch weather from your API
300
+ const weather = await fetch(`https://api.weather.com/${city}`);
301
+ return { temperature: 72, condition: "sunny" };
302
+ }
303
+ })
304
+ }
305
+ });
306
+
307
+ writer.merge(result.toUIMessageStream());
308
+ }
309
+ });
310
+
311
+ return createUIMessageStreamResponse({ stream });
312
+ }
313
+ }
314
+ ```
315
+
316
+ #### Client-Side Tools
317
+
318
+ For tools that need to run in the browser (like DOM manipulation, user interactions):
319
+
320
+ ```tsx
321
+ import { useAgent } from "agents/react";
322
+ import { useAgentChat } from "@cloudflare/ai-chat/react";
323
+ import { tool } from "ai";
324
+ import { z } from "zod";
325
+
326
+ function ChatWithClientTools() {
327
+ const agent = useAgent({ agent: "ToolChat" });
328
+
329
+ const { messages, sendMessage, addToolResult } = useAgentChat({
330
+ agent,
331
+ onToolCall: async ({ toolCall, addToolOutput }) => {
332
+ if (toolCall.toolName === "showAlert") {
333
+ alert(toolCall.input.message);
334
+ addToolOutput({
335
+ toolCallId: toolCall.toolCallId,
336
+ output: { success: true }
337
+ });
338
+ }
339
+ }
340
+ });
341
+
342
+ // Server defines the tool without execute:
343
+ // showAlert: tool({
344
+ // description: "Shows an alert to the user",
345
+ // parameters: z.object({ message: z.string() })
346
+ // // No execute = client handles it
347
+ // })
348
+ }
349
+ ```
350
+
351
+ #### Human-in-the-Loop Tools
352
+
353
+ For tools requiring user approval before execution:
354
+
355
+ ```tsx
356
+ import { useAgentChat } from "@cloudflare/ai-chat/react";
357
+
358
+ function HumanInTheLoopChat() {
359
+ const agent = useAgent({ agent: "ApprovalChat" });
360
+
361
+ const { messages, sendMessage, addToolResult } = useAgentChat({
362
+ agent,
363
+ toolsRequiringConfirmation: ["sendEmail", "deleteFile"],
364
+ onToolCall: async ({ toolCall, addToolOutput }) => {
365
+ if (toolCall.toolName === "sendEmail") {
366
+ const approved = confirm(`Send email to ${toolCall.input.recipient}?`);
367
+ if (approved) {
368
+ // Execute the tool
369
+ const result = await sendEmail(toolCall.input);
370
+ addToolOutput({
371
+ toolCallId: toolCall.toolCallId,
372
+ output: result,
373
+ autoContinue: true // Automatically continue after tool result
374
+ });
375
+ }
376
+ }
377
+ }
378
+ });
379
+ }
380
+ ```
381
+
382
+ ### 📊 Message Metadata
383
+
384
+ Attach custom metadata to messages for tracking, analytics, or display purposes:
385
+
386
+ ```ts
387
+ import { AIChatAgent } from "@cloudflare/ai-chat";
388
+ import { openai } from "@ai-sdk/openai";
389
+ import {
390
+ streamText,
391
+ convertToModelMessages,
392
+ createUIMessageStream,
393
+ createUIMessageStreamResponse
394
+ } from "ai";
395
+
396
+ export class MetadataChat extends AIChatAgent<Env> {
397
+ async onChatMessage() {
398
+ const startTime = Date.now();
399
+
400
+ const stream = createUIMessageStream({
401
+ execute: async ({ writer }) => {
402
+ const result = streamText({
403
+ model: openai("gpt-4o"),
404
+ messages: await convertToModelMessages(this.messages)
405
+ });
406
+
407
+ writer.merge(
408
+ result.toUIMessageStream({
409
+ messageMetadata: ({ part }) => {
410
+ if (part.type === "start") {
411
+ return {
412
+ model: "gpt-4o",
413
+ createdAt: Date.now(),
414
+ messageCount: this.messages.length
415
+ };
416
+ }
417
+ if (part.type === "finish") {
418
+ return {
419
+ responseTime: Date.now() - startTime,
420
+ totalTokens: part.totalUsage?.totalTokens
421
+ };
422
+ }
423
+ }
424
+ })
425
+ );
426
+ }
427
+ });
428
+
429
+ return createUIMessageStreamResponse({ stream });
430
+ }
431
+ }
432
+ ```
433
+
434
+ Access metadata on the client:
435
+
436
+ ```tsx
437
+ {
438
+ messages.map((message) => (
439
+ <div key={message.id}>
440
+ {message.metadata?.createdAt && (
441
+ <span>{new Date(message.metadata.createdAt).toLocaleTimeString()}</span>
442
+ )}
443
+ {message.metadata?.totalTokens && (
444
+ <span>{message.metadata.totalTokens} tokens</span>
445
+ )}
446
+ </div>
447
+ ));
448
+ }
449
+ ```
450
+
451
+ For more details, see the [AI SDK Message Metadata documentation](https://ai-sdk.dev/docs/ai-sdk-ui/message-metadata).
452
+
453
+ ### 🔄 Advanced Patterns
454
+
455
+ #### Custom Request Preparation
456
+
457
+ Add custom headers or context to requests:
458
+
459
+ ```tsx
460
+ const { messages, sendMessage } = useAgentChat({
461
+ agent,
462
+ prepareSendMessagesRequest: ({ id, messages }) => ({
463
+ body: {
464
+ currentUrl: window.location.href,
465
+ userTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone
466
+ },
467
+ headers: {
468
+ "X-Widget-Version": "1.0.0",
469
+ "X-Request-ID": crypto.randomUUID()
470
+ }
471
+ })
472
+ });
473
+ ```
474
+
475
+ #### Automatic Tool Continuation
476
+
477
+ Enable automatic continuation after tool results for seamless UX:
478
+
479
+ ```tsx
480
+ const { messages, addToolResult } = useAgentChat({
481
+ agent,
482
+ autoContinueAfterToolResult: true, // Automatically continue after tool execution
483
+ onToolCall: async ({ toolCall, addToolOutput }) => {
484
+ const result = await executeTool(toolCall);
485
+ addToolOutput({
486
+ toolCallId: toolCall.toolCallId,
487
+ output: result,
488
+ autoContinue: true // Server will automatically continue the conversation
489
+ });
490
+ }
491
+ });
492
+ ```
493
+
494
+ ### 🎯 Real-World Examples
495
+
496
+ Explore these examples to see `@cloudflare/ai-chat` in action:
497
+
498
+ - **Basic Chat**: [`examples/playground/src/agents/chat.ts`](../../examples/playground/src/agents/chat.ts) - Simple streaming chat
499
+ - **Resumable Streaming**: [`examples/resumable-stream-chat/`](../../examples/resumable-stream-chat/) - Automatic stream resumption
500
+ - **Human-in-the-Loop**: [`guides/human-in-the-loop/`](../../guides/human-in-the-loop/) - Tools requiring user approval
501
+
502
+ ### 📚 API Reference
503
+
504
+ #### `AIChatAgent<Env, State>`
505
+
506
+ Base class for chat agents. Extends `Agent` from the `agents` package.
507
+
508
+ **Methods:**
509
+
510
+ - `onChatMessage(onFinish, options?)`: Override this method to handle chat messages and return a `Response`
511
+ - `persistMessages(messages, excludeBroadcastIds?)`: Manually persist messages (usually automatic)
512
+ - `messages`: Array of current chat messages
513
+
514
+ **Properties:**
515
+
516
+ - `messages: ChatMessage[]`: The current conversation messages
517
+
518
+ #### `useAgentChat(options)`
519
+
520
+ React hook for chat interactions.
521
+
522
+ **Options:**
523
+
524
+ - `agent`: Agent connection from `useAgent()`
525
+ - `onToolCall`: Callback for handling client-side tool execution
526
+ - `toolsRequiringConfirmation`: Array of tool names that need user approval
527
+ - `autoContinueAfterToolResult`: Automatically continue conversation after tool results
528
+ - `resume`: Enable automatic stream resumption (default: `true`)
529
+ - `prepareSendMessagesRequest`: Customize request headers/body
530
+
531
+ **Returns:**
532
+
533
+ - `messages`: Array of chat messages
534
+ - `sendMessage`: Function to send a new message
535
+ - `clearHistory`: Function to clear conversation history
536
+ - `addToolResult`: Function to provide tool output
537
+ - `status`: Current status ("idle" | "streaming" | "error")
538
+
539
+ ### 🔗 Integration with Agents Framework
540
+
541
+ `@cloudflare/ai-chat` is built on top of the [`agents`](../agents/) framework. It extends the base `Agent` class with chat-specific capabilities:
542
+
543
+ - Automatic message persistence in SQLite
544
+ - WebSocket-based real-time communication
545
+ - Resumable streaming infrastructure
546
+ - Tool execution coordination
547
+
548
+ For more information about the underlying agent framework, see the [`agents` README](../agents/README.md).
549
+
550
+ ### 💬 The Path Forward
551
+
552
+ We're continuously improving `@cloudflare/ai-chat`:
553
+
554
+ - **Enhanced Tool Support**: More flexible tool execution patterns
555
+ - **Better Error Handling**: Graceful degradation and recovery
556
+ - **Performance Optimizations**: Faster streaming and lower latency
557
+ - **Developer Experience**: Better TypeScript types and debugging tools
558
+
559
+ ### Contributing
560
+
561
+ Contributions are welcome! Please:
562
+
563
+ 1. Open an issue to discuss your proposal
564
+ 2. Ensure your changes align with the package's goals
565
+ 3. Include tests for new features
566
+ 4. Update documentation as needed
567
+
568
+ ### License
569
+
570
+ MIT licensed. See the LICENSE file at the root of this repository for details.