agents 0.0.0-07086ea
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/README.md +428 -0
- package/dist/ai-chat-agent.d.ts +56 -0
- package/dist/ai-chat-agent.js +234 -0
- package/dist/ai-chat-agent.js.map +1 -0
- package/dist/ai-react.d.ts +88 -0
- package/dist/ai-react.js +204 -0
- package/dist/ai-react.js.map +1 -0
- package/dist/ai-types.d.ts +74 -0
- package/dist/ai-types.js +1 -0
- package/dist/ai-types.js.map +1 -0
- package/dist/chunk-HMLY7DHA.js +16 -0
- package/dist/chunk-HMLY7DHA.js.map +1 -0
- package/dist/chunk-WNICV3OI.js +436 -0
- package/dist/chunk-WNICV3OI.js.map +1 -0
- package/dist/chunk-ZRZEISHY.js +597 -0
- package/dist/chunk-ZRZEISHY.js.map +1 -0
- package/dist/client.d.ts +84 -0
- package/dist/client.js +131 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +313 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +768 -0
- package/dist/mcp/client.js +10 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/do-oauth-client-provider.d.ts +41 -0
- package/dist/mcp/do-oauth-client-provider.js +107 -0
- package/dist/mcp/do-oauth-client-provider.js.map +1 -0
- package/dist/mcp/index.d.ts +74 -0
- package/dist/mcp/index.js +808 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/react.d.ts +39 -0
- package/dist/react.js +104 -0
- package/dist/react.js.map +1 -0
- package/dist/schedule.d.ts +53 -0
- package/dist/schedule.js +73 -0
- package/dist/schedule.js.map +1 -0
- package/package.json +98 -0
- package/src/index.ts +918 -0
package/README.md
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
### 🧠 `agents` - A Framework for Digital Intelligence
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Welcome to a new chapter in software development, where AI agents persist, think, and act with purpose. The `agents` framework creates an environment where artificial intelligence can flourish - maintaining state, engaging in meaningful interactions, and evolving over time.
|
|
6
|
+
|
|
7
|
+
_This project is in active development. Join us in shaping the future of intelligent agents._
|
|
8
|
+
|
|
9
|
+
#### The Nature of Agents
|
|
10
|
+
|
|
11
|
+
An AI agent transcends traditional software boundaries. It's an entity that:
|
|
12
|
+
|
|
13
|
+
- **Persistence**: Maintains its state and knowledge across time
|
|
14
|
+
- **Agency**: Acts autonomously within its defined purpose
|
|
15
|
+
- **Connection**: Communicates through multiple channels with both humans and other agents
|
|
16
|
+
- **Growth**: Learns and adapts through its interactions
|
|
17
|
+
|
|
18
|
+
Built on Cloudflare's global network, this framework provides agents with a reliable, distributed foundation where they can operate continuously and effectively.
|
|
19
|
+
|
|
20
|
+
#### 💫 Core Principles
|
|
21
|
+
|
|
22
|
+
1. **Stateful Existence**: Each agent maintains its own persistent reality
|
|
23
|
+
2. **Long-lived Presence**: Agents can run for extended periods, resting when idle
|
|
24
|
+
3. **Natural Communication**: Interact through HTTP, WebSockets, or direct calls
|
|
25
|
+
4. **Global Distribution**: Leverage Cloudflare's network for worldwide presence
|
|
26
|
+
5. **Resource Harmony**: Efficient hibernation and awakening as needed
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
### 🌱 Beginning the Journey
|
|
31
|
+
|
|
32
|
+
Start with a complete environment:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
# Create a new project
|
|
36
|
+
npm create cloudflare@latest -- --template cloudflare/agents-starter
|
|
37
|
+
|
|
38
|
+
# Or enhance an existing one
|
|
39
|
+
npm install agents
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 📝 Your First Agent
|
|
43
|
+
|
|
44
|
+
Create an agent that bridges thought and action:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { Agent } from "agents";
|
|
48
|
+
|
|
49
|
+
export class IntelligentAgent extends Agent {
|
|
50
|
+
async onRequest(request) {
|
|
51
|
+
// Transform intention into response
|
|
52
|
+
return new Response("Ready to assist.");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 🎭 Patterns of Intelligence
|
|
58
|
+
|
|
59
|
+
Agents can manifest various forms of understanding:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { Agent } from "agents";
|
|
63
|
+
import { OpenAI } from "openai";
|
|
64
|
+
|
|
65
|
+
export class AIAgent extends Agent {
|
|
66
|
+
async onRequest(request) {
|
|
67
|
+
// Connect with AI capabilities
|
|
68
|
+
const ai = new OpenAI({
|
|
69
|
+
apiKey: this.env.OPENAI_API_KEY,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Process and understand
|
|
73
|
+
const response = await ai.chat.completions.create({
|
|
74
|
+
model: "gpt-4",
|
|
75
|
+
messages: [{ role: "user", content: await request.text() }],
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return new Response(response.choices[0].message.content);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async processTask(task) {
|
|
82
|
+
await this.understand(task);
|
|
83
|
+
await this.act();
|
|
84
|
+
await this.reflect();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 🏰 Creating Space
|
|
90
|
+
|
|
91
|
+
Define your agent's domain:
|
|
92
|
+
|
|
93
|
+
```jsonc
|
|
94
|
+
{
|
|
95
|
+
"durable_objects": {
|
|
96
|
+
"bindings": [
|
|
97
|
+
{
|
|
98
|
+
"name": "AIAgent",
|
|
99
|
+
"class_name": "AIAgent",
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
"migrations": [
|
|
104
|
+
{
|
|
105
|
+
"tag": "v1",
|
|
106
|
+
// Mandatory for the Agent to store state
|
|
107
|
+
"new_sqlite_classes": ["AIAgent"],
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 🌐 Lifecycle
|
|
114
|
+
|
|
115
|
+
Bring your agent into being:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
// Create a new instance
|
|
119
|
+
const id = env.AIAgent.newUniqueId();
|
|
120
|
+
const agent = env.AIAgent.get(id);
|
|
121
|
+
|
|
122
|
+
// Initialize with purpose
|
|
123
|
+
await agent.processTask({
|
|
124
|
+
type: "analysis",
|
|
125
|
+
context: "incoming_data",
|
|
126
|
+
parameters: initialConfig,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Or reconnect with an existing one
|
|
130
|
+
const existingAgent = await getAgentByName(env.AIAgent, "data-analyzer");
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 🔄 Paths of Communication
|
|
134
|
+
|
|
135
|
+
#### HTTP Understanding
|
|
136
|
+
|
|
137
|
+
Process and respond to direct requests:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
export class APIAgent extends Agent {
|
|
141
|
+
async onRequest(request) {
|
|
142
|
+
const data = await request.json();
|
|
143
|
+
|
|
144
|
+
return Response.json({
|
|
145
|
+
insight: await this.process(data),
|
|
146
|
+
moment: Date.now(),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Persistent Connections
|
|
153
|
+
|
|
154
|
+
Maintain ongoing dialogues through WebSocket:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
export class DialogueAgent extends Agent {
|
|
158
|
+
async onConnect(connection) {
|
|
159
|
+
await this.initiate(connection);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async onMessage(connection, message) {
|
|
163
|
+
const understanding = await this.comprehend(message);
|
|
164
|
+
await this.respond(connection, understanding);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### Client Communion
|
|
170
|
+
|
|
171
|
+
For direct connection to your agent:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { AgentClient } from "agents/client";
|
|
175
|
+
|
|
176
|
+
const connection = new AgentClient({
|
|
177
|
+
agent: "dialogue-agent",
|
|
178
|
+
name: "insight-seeker",
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
connection.addEventListener("message", (event) => {
|
|
182
|
+
console.log("Received:", event.data);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
connection.send(
|
|
186
|
+
JSON.stringify({
|
|
187
|
+
type: "inquiry",
|
|
188
|
+
content: "What patterns do you see?",
|
|
189
|
+
})
|
|
190
|
+
);
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### React Integration
|
|
194
|
+
|
|
195
|
+
For harmonious integration with React:
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
import { useAgent } from "agents/react";
|
|
199
|
+
|
|
200
|
+
function AgentInterface() {
|
|
201
|
+
const connection = useAgent({
|
|
202
|
+
agent: "dialogue-agent",
|
|
203
|
+
name: "insight-seeker",
|
|
204
|
+
onMessage: (message) => {
|
|
205
|
+
console.log("Understanding received:", message.data);
|
|
206
|
+
},
|
|
207
|
+
onOpen: () => console.log("Connection established"),
|
|
208
|
+
onClose: () => console.log("Connection closed"),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const inquire = () => {
|
|
212
|
+
connection.send(
|
|
213
|
+
JSON.stringify({
|
|
214
|
+
type: "inquiry",
|
|
215
|
+
content: "What insights have you gathered?",
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
return (
|
|
221
|
+
<div className="agent-interface">
|
|
222
|
+
<button onClick={inquire}>Seek Understanding</button>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### 🌊 Flow of State
|
|
229
|
+
|
|
230
|
+
Maintain and evolve your agent's understanding:
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
export class ThinkingAgent extends Agent {
|
|
234
|
+
async evolve(newInsight) {
|
|
235
|
+
this.setState({
|
|
236
|
+
...this.state,
|
|
237
|
+
insights: [...(this.state.insights || []), newInsight],
|
|
238
|
+
understanding: this.state.understanding + 1,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
onStateUpdate(state, source) {
|
|
243
|
+
console.log("Understanding deepened:", {
|
|
244
|
+
newState: state,
|
|
245
|
+
origin: source,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Connect to your agent's state from React:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
import { useState } from "react";
|
|
255
|
+
import { useAgent } from "agents/react";
|
|
256
|
+
|
|
257
|
+
function StateInterface() {
|
|
258
|
+
const [state, setState] = useState({ counter: 0 });
|
|
259
|
+
|
|
260
|
+
const agent = useAgent({
|
|
261
|
+
agent: "thinking-agent",
|
|
262
|
+
onStateUpdate: (newState) => setState(newState),
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const increment = () => {
|
|
266
|
+
agent.setState({ counter: state.counter + 1 });
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<div>
|
|
271
|
+
<div>Count: {state.counter}</div>
|
|
272
|
+
<button onClick={increment}>Increment</button>
|
|
273
|
+
</div>
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
This creates a synchronized state that automatically updates across all connected clients.
|
|
279
|
+
|
|
280
|
+
### ⏳ Temporal Patterns
|
|
281
|
+
|
|
282
|
+
Schedule moments of action and reflection:
|
|
283
|
+
|
|
284
|
+
```ts
|
|
285
|
+
export class TimeAwareAgent extends Agent {
|
|
286
|
+
async initialize() {
|
|
287
|
+
// Quick reflection
|
|
288
|
+
this.schedule(10, "quickInsight", { focus: "patterns" });
|
|
289
|
+
|
|
290
|
+
// Daily synthesis
|
|
291
|
+
this.schedule("0 0 * * *", "dailySynthesis", {
|
|
292
|
+
depth: "comprehensive",
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Milestone review
|
|
296
|
+
this.schedule(new Date("2024-12-31"), "yearlyAnalysis");
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async quickInsight(data) {
|
|
300
|
+
await this.analyze(data.focus);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async dailySynthesis(data) {
|
|
304
|
+
await this.synthesize(data.depth);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async yearlyAnalysis() {
|
|
308
|
+
await this.analyze();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### 💬 AI Dialogue
|
|
314
|
+
|
|
315
|
+
Create meaningful conversations with intelligence:
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
import { AIChatAgent } from "agents/ai-chat-agent";
|
|
319
|
+
import { openai } from "@ai-sdk/openai";
|
|
320
|
+
|
|
321
|
+
export class DialogueAgent extends AIChatAgent {
|
|
322
|
+
async onChatMessage(onFinish) {
|
|
323
|
+
return createDataStreamResponse({
|
|
324
|
+
execute: async (dataStream) => {
|
|
325
|
+
const stream = streamText({
|
|
326
|
+
model: openai("gpt-4o"),
|
|
327
|
+
messages: this.messages,
|
|
328
|
+
onFinish, // call onFinish so that messages get saved
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
stream.mergeIntoDataStream(dataStream);
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
#### Creating the Interface
|
|
339
|
+
|
|
340
|
+
Connect with your agent through a React interface:
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
import { useAgent } from "agents/react";
|
|
344
|
+
import { useAgentChat } from "agents/ai-react";
|
|
345
|
+
|
|
346
|
+
function ChatInterface() {
|
|
347
|
+
// Connect to the agent
|
|
348
|
+
const agent = useAgent({
|
|
349
|
+
agent: "dialogue-agent",
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// Set up the chat interaction
|
|
353
|
+
const { messages, input, handleInputChange, handleSubmit, clearHistory } =
|
|
354
|
+
useAgentChat({
|
|
355
|
+
agent,
|
|
356
|
+
maxSteps: 5,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
return (
|
|
360
|
+
<div className="chat-interface">
|
|
361
|
+
{/* Message History */}
|
|
362
|
+
<div className="message-flow">
|
|
363
|
+
{messages.map((message) => (
|
|
364
|
+
<div key={message.id} className="message">
|
|
365
|
+
<div className="role">{message.role}</div>
|
|
366
|
+
<div className="content">{message.content}</div>
|
|
367
|
+
</div>
|
|
368
|
+
))}
|
|
369
|
+
</div>
|
|
370
|
+
|
|
371
|
+
{/* Input Area */}
|
|
372
|
+
<form onSubmit={handleSubmit} className="input-area">
|
|
373
|
+
<input
|
|
374
|
+
value={input}
|
|
375
|
+
onChange={handleInputChange}
|
|
376
|
+
placeholder="Type your message..."
|
|
377
|
+
className="message-input"
|
|
378
|
+
/>
|
|
379
|
+
</form>
|
|
380
|
+
|
|
381
|
+
<button onClick={clearHistory} className="clear-button">
|
|
382
|
+
Clear Chat
|
|
383
|
+
</button>
|
|
384
|
+
</div>
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
This creates:
|
|
390
|
+
|
|
391
|
+
- Real-time message streaming
|
|
392
|
+
- Simple message history
|
|
393
|
+
- Intuitive input handling
|
|
394
|
+
- Easy conversation reset
|
|
395
|
+
|
|
396
|
+
### 💬 The Path Forward
|
|
397
|
+
|
|
398
|
+
We're developing new dimensions of agent capability:
|
|
399
|
+
|
|
400
|
+
#### Enhanced Understanding
|
|
401
|
+
|
|
402
|
+
- **WebRTC Perception**: Audio and video communication channels
|
|
403
|
+
- **Email Discourse**: Automated email interaction and response
|
|
404
|
+
- **Deep Memory**: Long-term context and relationship understanding
|
|
405
|
+
|
|
406
|
+
#### Development Insights
|
|
407
|
+
|
|
408
|
+
- **Evaluation Framework**: Understanding agent effectiveness
|
|
409
|
+
- **Clear Sight**: Deep visibility into agent processes
|
|
410
|
+
- **Private Realms**: Complete self-hosting guide
|
|
411
|
+
|
|
412
|
+
These capabilities will expand your agents' potential while maintaining their reliability and purpose.
|
|
413
|
+
|
|
414
|
+
Welcome to the future of intelligent agents. Create something meaningful. 🌟
|
|
415
|
+
|
|
416
|
+
### Contributing
|
|
417
|
+
|
|
418
|
+
Contributions are welcome, but are especially welcome when:
|
|
419
|
+
|
|
420
|
+
- You have opened an issue as a Request for Comment (RFC) to discuss your proposal, show your thinking, and iterate together.
|
|
421
|
+
- Is not "AI slop": LLMs are powerful tools, but contributions entirely authored by vibe coding are unlikely to meet the quality bar, and will be rejected.
|
|
422
|
+
- You're willing to accept feedback and make sure the changes fit the goals of the `agents` sdk. Not everything will, and that's OK.
|
|
423
|
+
|
|
424
|
+
Small fixes, type bugs, and documentation improvements can be raised directly as PRs.
|
|
425
|
+
|
|
426
|
+
### License
|
|
427
|
+
|
|
428
|
+
MIT licensed. See the LICENSE file at the root of this repository for details.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Agent, AgentContext } from "./index.js";
|
|
2
|
+
import { Message, StreamTextOnFinishCallback, ToolSet } from "ai";
|
|
3
|
+
import { Connection, WSMessage } from "partyserver";
|
|
4
|
+
import "node:async_hooks";
|
|
5
|
+
import "./mcp/client.js";
|
|
6
|
+
import "zod";
|
|
7
|
+
import "@modelcontextprotocol/sdk/types.js";
|
|
8
|
+
import "@modelcontextprotocol/sdk/client/index.js";
|
|
9
|
+
import "@modelcontextprotocol/sdk/client/sse.js";
|
|
10
|
+
import "./mcp/do-oauth-client-provider.js";
|
|
11
|
+
import "@modelcontextprotocol/sdk/client/auth.js";
|
|
12
|
+
import "@modelcontextprotocol/sdk/shared/auth.js";
|
|
13
|
+
import "@modelcontextprotocol/sdk/shared/protocol.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Extension of Agent with built-in chat capabilities
|
|
17
|
+
* @template Env Environment type containing bindings
|
|
18
|
+
*/
|
|
19
|
+
declare class AIChatAgent<Env = unknown, State = unknown> extends Agent<
|
|
20
|
+
Env,
|
|
21
|
+
State
|
|
22
|
+
> {
|
|
23
|
+
#private;
|
|
24
|
+
/** Array of chat messages for the current conversation */
|
|
25
|
+
messages: Message[];
|
|
26
|
+
constructor(ctx: AgentContext, env: Env);
|
|
27
|
+
onMessage(connection: Connection, message: WSMessage): Promise<void>;
|
|
28
|
+
onRequest(request: Request): Promise<Response>;
|
|
29
|
+
/**
|
|
30
|
+
* Handle incoming chat messages and generate a response
|
|
31
|
+
* @param onFinish Callback to be called when the response is finished
|
|
32
|
+
* @param options.signal A signal to pass to any child requests which can be used to cancel them
|
|
33
|
+
* @returns Response to send to the client or undefined
|
|
34
|
+
*/
|
|
35
|
+
onChatMessage(
|
|
36
|
+
onFinish: StreamTextOnFinishCallback<ToolSet>,
|
|
37
|
+
options?: {
|
|
38
|
+
abortSignal: AbortSignal | undefined;
|
|
39
|
+
}
|
|
40
|
+
): Promise<Response | undefined>;
|
|
41
|
+
/**
|
|
42
|
+
* Save messages on the server side and trigger AI response
|
|
43
|
+
* @param messages Chat messages to save
|
|
44
|
+
*/
|
|
45
|
+
saveMessages(messages: Message[]): Promise<void>;
|
|
46
|
+
persistMessages(
|
|
47
|
+
messages: Message[],
|
|
48
|
+
excludeBroadcastIds?: string[]
|
|
49
|
+
): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* When the DO is destroyed, cancel all pending requests
|
|
52
|
+
*/
|
|
53
|
+
destroy(): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { AIChatAgent };
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Agent
|
|
3
|
+
} from "./chunk-ZRZEISHY.js";
|
|
4
|
+
import "./chunk-WNICV3OI.js";
|
|
5
|
+
import {
|
|
6
|
+
__privateAdd,
|
|
7
|
+
__privateGet,
|
|
8
|
+
__privateMethod,
|
|
9
|
+
__privateSet
|
|
10
|
+
} from "./chunk-HMLY7DHA.js";
|
|
11
|
+
|
|
12
|
+
// src/ai-chat-agent.ts
|
|
13
|
+
import { appendResponseMessages } from "ai";
|
|
14
|
+
var decoder = new TextDecoder();
|
|
15
|
+
var _chatMessageAbortControllers, _AIChatAgent_instances, broadcastChatMessage_fn, tryCatch_fn, reply_fn, getAbortSignal_fn, removeAbortController_fn, cancelChatRequest_fn, destroyAbortControllers_fn;
|
|
16
|
+
var AIChatAgent = class extends Agent {
|
|
17
|
+
constructor(ctx, env) {
|
|
18
|
+
super(ctx, env);
|
|
19
|
+
__privateAdd(this, _AIChatAgent_instances);
|
|
20
|
+
/**
|
|
21
|
+
* Map of message `id`s to `AbortController`s
|
|
22
|
+
* useful to propagate request cancellation signals for any external calls made by the agent
|
|
23
|
+
*/
|
|
24
|
+
__privateAdd(this, _chatMessageAbortControllers);
|
|
25
|
+
this.sql`create table if not exists cf_ai_chat_agent_messages (
|
|
26
|
+
id text primary key,
|
|
27
|
+
message text not null,
|
|
28
|
+
created_at datetime default current_timestamp
|
|
29
|
+
)`;
|
|
30
|
+
this.messages = (this.sql`select * from cf_ai_chat_agent_messages` || []).map((row) => {
|
|
31
|
+
return JSON.parse(row.message);
|
|
32
|
+
});
|
|
33
|
+
__privateSet(this, _chatMessageAbortControllers, /* @__PURE__ */ new Map());
|
|
34
|
+
}
|
|
35
|
+
async onMessage(connection, message) {
|
|
36
|
+
if (typeof message === "string") {
|
|
37
|
+
let data;
|
|
38
|
+
try {
|
|
39
|
+
data = JSON.parse(message);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (data.type === "cf_agent_use_chat_request" && data.init.method === "POST") {
|
|
44
|
+
const {
|
|
45
|
+
method,
|
|
46
|
+
keepalive,
|
|
47
|
+
headers,
|
|
48
|
+
body,
|
|
49
|
+
// we're reading this
|
|
50
|
+
redirect,
|
|
51
|
+
integrity,
|
|
52
|
+
credentials,
|
|
53
|
+
mode,
|
|
54
|
+
referrer,
|
|
55
|
+
referrerPolicy,
|
|
56
|
+
window
|
|
57
|
+
// dispatcher,
|
|
58
|
+
// duplex
|
|
59
|
+
} = data.init;
|
|
60
|
+
const { messages } = JSON.parse(body);
|
|
61
|
+
__privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
|
|
62
|
+
type: "cf_agent_chat_messages",
|
|
63
|
+
messages
|
|
64
|
+
}, [connection.id]);
|
|
65
|
+
await this.persistMessages(messages, [connection.id]);
|
|
66
|
+
const chatMessageId = data.id;
|
|
67
|
+
const abortSignal = __privateMethod(this, _AIChatAgent_instances, getAbortSignal_fn).call(this, chatMessageId);
|
|
68
|
+
return __privateMethod(this, _AIChatAgent_instances, tryCatch_fn).call(this, async () => {
|
|
69
|
+
const response = await this.onChatMessage(
|
|
70
|
+
async ({ response: response2 }) => {
|
|
71
|
+
const finalMessages = appendResponseMessages({
|
|
72
|
+
messages,
|
|
73
|
+
responseMessages: response2.messages
|
|
74
|
+
});
|
|
75
|
+
await this.persistMessages(finalMessages, [connection.id]);
|
|
76
|
+
__privateMethod(this, _AIChatAgent_instances, removeAbortController_fn).call(this, chatMessageId);
|
|
77
|
+
},
|
|
78
|
+
abortSignal ? { abortSignal } : void 0
|
|
79
|
+
);
|
|
80
|
+
if (response) {
|
|
81
|
+
await __privateMethod(this, _AIChatAgent_instances, reply_fn).call(this, data.id, response);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (data.type === "cf_agent_chat_clear") {
|
|
86
|
+
__privateMethod(this, _AIChatAgent_instances, destroyAbortControllers_fn).call(this);
|
|
87
|
+
this.sql`delete from cf_ai_chat_agent_messages`;
|
|
88
|
+
this.messages = [];
|
|
89
|
+
__privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
|
|
90
|
+
type: "cf_agent_chat_clear"
|
|
91
|
+
}, [connection.id]);
|
|
92
|
+
} else if (data.type === "cf_agent_chat_messages") {
|
|
93
|
+
await this.persistMessages(data.messages, [connection.id]);
|
|
94
|
+
} else if (data.type === "cf_agent_chat_request_cancel") {
|
|
95
|
+
__privateMethod(this, _AIChatAgent_instances, cancelChatRequest_fn).call(this, data.id);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async onRequest(request) {
|
|
100
|
+
return __privateMethod(this, _AIChatAgent_instances, tryCatch_fn).call(this, () => {
|
|
101
|
+
const url = new URL(request.url);
|
|
102
|
+
if (url.pathname.endsWith("/get-messages")) {
|
|
103
|
+
const messages = (this.sql`select * from cf_ai_chat_agent_messages` || []).map((row) => {
|
|
104
|
+
return JSON.parse(row.message);
|
|
105
|
+
});
|
|
106
|
+
return Response.json(messages);
|
|
107
|
+
}
|
|
108
|
+
return super.onRequest(request);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Handle incoming chat messages and generate a response
|
|
113
|
+
* @param onFinish Callback to be called when the response is finished
|
|
114
|
+
* @param options.signal A signal to pass to any child requests which can be used to cancel them
|
|
115
|
+
* @returns Response to send to the client or undefined
|
|
116
|
+
*/
|
|
117
|
+
async onChatMessage(onFinish, options) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
"recieved a chat message, override onChatMessage and return a Response to send to the client"
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Save messages on the server side and trigger AI response
|
|
124
|
+
* @param messages Chat messages to save
|
|
125
|
+
*/
|
|
126
|
+
async saveMessages(messages) {
|
|
127
|
+
await this.persistMessages(messages);
|
|
128
|
+
const response = await this.onChatMessage(async ({ response: response2 }) => {
|
|
129
|
+
const finalMessages = appendResponseMessages({
|
|
130
|
+
messages,
|
|
131
|
+
responseMessages: response2.messages
|
|
132
|
+
});
|
|
133
|
+
await this.persistMessages(finalMessages, []);
|
|
134
|
+
});
|
|
135
|
+
if (response) {
|
|
136
|
+
for await (const chunk of response.body) {
|
|
137
|
+
decoder.decode(chunk);
|
|
138
|
+
}
|
|
139
|
+
response.body?.cancel();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async persistMessages(messages, excludeBroadcastIds = []) {
|
|
143
|
+
this.sql`delete from cf_ai_chat_agent_messages`;
|
|
144
|
+
for (const message of messages) {
|
|
145
|
+
this.sql`insert into cf_ai_chat_agent_messages (id, message) values (${message.id},${JSON.stringify(message)})`;
|
|
146
|
+
}
|
|
147
|
+
this.messages = messages;
|
|
148
|
+
__privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
|
|
149
|
+
type: "cf_agent_chat_messages",
|
|
150
|
+
messages
|
|
151
|
+
}, excludeBroadcastIds);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* When the DO is destroyed, cancel all pending requests
|
|
155
|
+
*/
|
|
156
|
+
async destroy() {
|
|
157
|
+
__privateMethod(this, _AIChatAgent_instances, destroyAbortControllers_fn).call(this);
|
|
158
|
+
await super.destroy();
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
_chatMessageAbortControllers = new WeakMap();
|
|
162
|
+
_AIChatAgent_instances = new WeakSet();
|
|
163
|
+
broadcastChatMessage_fn = function(message, exclude) {
|
|
164
|
+
this.broadcast(JSON.stringify(message), exclude);
|
|
165
|
+
};
|
|
166
|
+
tryCatch_fn = async function(fn) {
|
|
167
|
+
try {
|
|
168
|
+
return await fn();
|
|
169
|
+
} catch (e) {
|
|
170
|
+
throw this.onError(e);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
reply_fn = async function(id, response) {
|
|
174
|
+
return __privateMethod(this, _AIChatAgent_instances, tryCatch_fn).call(this, async () => {
|
|
175
|
+
for await (const chunk of response.body) {
|
|
176
|
+
const body = decoder.decode(chunk);
|
|
177
|
+
__privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
|
|
178
|
+
id,
|
|
179
|
+
type: "cf_agent_use_chat_response",
|
|
180
|
+
body,
|
|
181
|
+
done: false
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
__privateMethod(this, _AIChatAgent_instances, broadcastChatMessage_fn).call(this, {
|
|
185
|
+
id,
|
|
186
|
+
type: "cf_agent_use_chat_response",
|
|
187
|
+
body: "",
|
|
188
|
+
done: true
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* For the given message id, look up its associated AbortController
|
|
194
|
+
* If the AbortController does not exist, create and store one in memory
|
|
195
|
+
*
|
|
196
|
+
* returns the AbortSignal associated with the AbortController
|
|
197
|
+
*/
|
|
198
|
+
getAbortSignal_fn = function(id) {
|
|
199
|
+
if (typeof id !== "string") {
|
|
200
|
+
return void 0;
|
|
201
|
+
}
|
|
202
|
+
if (!__privateGet(this, _chatMessageAbortControllers).has(id)) {
|
|
203
|
+
__privateGet(this, _chatMessageAbortControllers).set(id, new AbortController());
|
|
204
|
+
}
|
|
205
|
+
return __privateGet(this, _chatMessageAbortControllers).get(id)?.signal;
|
|
206
|
+
};
|
|
207
|
+
/**
|
|
208
|
+
* Remove an abort controller from the cache of pending message responses
|
|
209
|
+
*/
|
|
210
|
+
removeAbortController_fn = function(id) {
|
|
211
|
+
__privateGet(this, _chatMessageAbortControllers).delete(id);
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* Propagate an abort signal for any requests associated with the given message id
|
|
215
|
+
*/
|
|
216
|
+
cancelChatRequest_fn = function(id) {
|
|
217
|
+
if (__privateGet(this, _chatMessageAbortControllers).has(id)) {
|
|
218
|
+
const abortController = __privateGet(this, _chatMessageAbortControllers).get(id);
|
|
219
|
+
abortController?.abort();
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
/**
|
|
223
|
+
* Abort all pending requests and clear the cache of AbortControllers
|
|
224
|
+
*/
|
|
225
|
+
destroyAbortControllers_fn = function() {
|
|
226
|
+
for (const controller of __privateGet(this, _chatMessageAbortControllers).values()) {
|
|
227
|
+
controller?.abort();
|
|
228
|
+
}
|
|
229
|
+
__privateGet(this, _chatMessageAbortControllers).clear();
|
|
230
|
+
};
|
|
231
|
+
export {
|
|
232
|
+
AIChatAgent
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=ai-chat-agent.js.map
|