@firtoz/chat-agent 1.0.1 → 2.1.0
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 +107 -349
- package/dist/chat-agent-base.d.ts +253 -0
- package/dist/chat-agent-base.js +4 -0
- package/dist/chat-agent-base.js.map +1 -0
- package/dist/chat-messages.d.ts +481 -0
- package/dist/chat-messages.js +3 -0
- package/dist/chat-messages.js.map +1 -0
- package/dist/chunk-G5P5JXRF.js +1068 -0
- package/dist/chunk-G5P5JXRF.js.map +1 -0
- package/dist/chunk-OEX3D4WL.js +292 -0
- package/dist/chunk-OEX3D4WL.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/package.json +25 -26
- package/src/chat-agent-base.ts +731 -303
- package/src/chat-messages.ts +81 -5
- package/src/index.ts +11 -22
- package/src/chat-agent-drizzle.ts +0 -227
- package/src/chat-agent-sql.ts +0 -199
- package/src/db/index.ts +0 -21
- package/src/db/schema.ts +0 -47
package/README.md
CHANGED
|
@@ -1,39 +1,79 @@
|
|
|
1
1
|
# @firtoz/chat-agent
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@firtoz/chat-agent)
|
|
4
|
+
[](https://www.npmjs.com/package/@firtoz/chat-agent)
|
|
5
|
+
[](https://github.com/firtoz/fullstack-toolkit/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://developers.cloudflare.com/durable-objects/)
|
|
9
|
+
[](https://openrouter.ai/)
|
|
10
|
+
|
|
11
|
+
**Wire protocol (Zod 4), `defineTool`, and `ChatAgentBase` for Durable Objects + OpenRouter** — streaming chat, tools, and multi-tab sync; plug in Drizzle or raw SQL via sibling packages.
|
|
12
|
+
|
|
13
|
+
**Persistence is separate:** install one of:
|
|
14
|
+
|
|
15
|
+
- **`@firtoz/chat-agent-drizzle`** — Drizzle ORM (recommended)
|
|
16
|
+
- **`@firtoz/chat-agent-sql`** — raw `this.sql`
|
|
17
|
+
|
|
18
|
+
## Upgrading from v1
|
|
19
|
+
|
|
20
|
+
v1 shipped `DrizzleChatAgent`, `SqlChatAgent`, and `ChatAgent` (alias) from this package. v2 keeps only protocol + base here.
|
|
21
|
+
|
|
22
|
+
| v1 | v2 |
|
|
23
|
+
|----|-----|
|
|
24
|
+
| `import { DrizzleChatAgent, defineTool } from "@firtoz/chat-agent"` | `import { defineTool } from "@firtoz/chat-agent"` and `import { DrizzleChatAgent } from "@firtoz/chat-agent-drizzle"` |
|
|
25
|
+
| `import { SqlChatAgent } from "@firtoz/chat-agent"` | `import { SqlChatAgent } from "@firtoz/chat-agent-sql"` |
|
|
26
|
+
| `import { … } from "@firtoz/chat-agent/db/schema"` | `import { … } from "@firtoz/chat-agent-drizzle/db/schema"` |
|
|
27
|
+
| `import { ChatAgent } from "@firtoz/chat-agent"` (Drizzle alias) | `import { DrizzleChatAgent } from "@firtoz/chat-agent-drizzle"` (or a local `type ChatAgent = DrizzleChatAgent`) |
|
|
28
|
+
|
|
29
|
+
Add `bun add @firtoz/chat-agent-drizzle` (and `drizzle-orm`) or `bun add @firtoz/chat-agent-sql` as needed.
|
|
4
30
|
|
|
5
31
|
## Overview
|
|
6
32
|
|
|
7
|
-
|
|
33
|
+
- **`ChatAgentBase`** — Abstract class with chat + streaming + tools + multi-tab broadcast logic
|
|
34
|
+
- **Concrete agents** — `DrizzleChatAgent` (`@firtoz/chat-agent-drizzle`), `SqlChatAgent` (`@firtoz/chat-agent-sql`)
|
|
8
35
|
|
|
9
|
-
|
|
10
|
-
- **`DrizzleChatAgent`** - Type-safe implementation using Drizzle ORM (recommended)
|
|
11
|
-
- **`SqlChatAgent`** - Raw SQL implementation using `this.sql` template tags
|
|
36
|
+
Shared behavior (all implementations):
|
|
12
37
|
|
|
13
|
-
All implementations share the same API and features:
|
|
14
38
|
- OpenRouter API integration (simpler than AI SDK)
|
|
15
39
|
- Resumable streaming with chunk buffering
|
|
16
|
-
-
|
|
40
|
+
- **Multi-tab sync**: `messageStart`, chunks, `messageEnd`, `messageUpdated`, `streamResume`, and post-mutation `history` are **broadcast** to every WebSocket on the same Durable Object (merge by message `id` / `streamId` on the client)
|
|
41
|
+
- Serialized chat turns with batched `toolResult` + `autoContinue`
|
|
42
|
+
- Server-side and client-side tools
|
|
17
43
|
- Cloudflare AI Gateway support
|
|
18
|
-
-
|
|
44
|
+
- Optional `maxPersistedMessages` and `sanitizeMessageForPersistence()`
|
|
45
|
+
- `waitUntilStable()`, `resetTurnState()`, `hasPendingInteraction()` (subclasses)
|
|
46
|
+
- Tool approval (`needsApproval` → `toolApprovalRequest` / `toolApprovalResponse`)
|
|
47
|
+
- Regenerate / client sync (`sendMessage` with `trigger`, optional `messages`)
|
|
48
|
+
- **Provider metadata** on tool calls for upstream round-trips
|
|
49
|
+
- Wire schemas use **Zod 4** (`zod/v4`)
|
|
50
|
+
|
|
51
|
+
### Long-running streams (Durable Object keep-alive)
|
|
52
|
+
|
|
53
|
+
Streaming uses Partyserver’s `experimental_waitUntil`. Enable **`enable_ctx_exports`** in `wrangler.jsonc` (required for `experimental_waitUntil` on `Server` / `Agent`).
|
|
19
54
|
|
|
20
55
|
## Installation
|
|
21
56
|
|
|
22
57
|
```bash
|
|
23
58
|
bun add @firtoz/chat-agent @openrouter/sdk agents
|
|
59
|
+
```
|
|
24
60
|
|
|
25
|
-
|
|
26
|
-
|
|
61
|
+
For a runnable Durable Object agent, add a persistence package:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Drizzle (recommended)
|
|
65
|
+
bun add @firtoz/chat-agent-drizzle drizzle-orm
|
|
27
66
|
bun add -d drizzle-kit
|
|
28
|
-
```
|
|
29
67
|
|
|
30
|
-
|
|
68
|
+
# Or raw SQL only
|
|
69
|
+
bun add @firtoz/chat-agent-sql
|
|
70
|
+
```
|
|
31
71
|
|
|
32
|
-
|
|
72
|
+
## Quick start (Drizzle)
|
|
33
73
|
|
|
34
74
|
```typescript
|
|
35
|
-
import {
|
|
36
|
-
import
|
|
75
|
+
import { defineTool, type ToolDefinition } from "@firtoz/chat-agent";
|
|
76
|
+
import { DrizzleChatAgent } from "@firtoz/chat-agent-drizzle";
|
|
37
77
|
|
|
38
78
|
interface Env {
|
|
39
79
|
OPENROUTER_API_KEY: string;
|
|
@@ -48,14 +88,14 @@ class MyAgent extends DrizzleChatAgent<Env> {
|
|
|
48
88
|
return "anthropic/claude-sonnet-4.5";
|
|
49
89
|
}
|
|
50
90
|
|
|
51
|
-
protected override getTools() {
|
|
91
|
+
protected override getTools(): ToolDefinition[] {
|
|
52
92
|
return [
|
|
53
93
|
defineTool({
|
|
54
94
|
name: "get_time",
|
|
55
95
|
description: "Get current time",
|
|
56
96
|
parameters: { type: "object", properties: {} },
|
|
57
|
-
execute: async () => ({ time: new Date().toISOString() })
|
|
58
|
-
})
|
|
97
|
+
execute: async () => ({ time: new Date().toISOString() }),
|
|
98
|
+
}),
|
|
59
99
|
];
|
|
60
100
|
}
|
|
61
101
|
}
|
|
@@ -63,381 +103,99 @@ class MyAgent extends DrizzleChatAgent<Env> {
|
|
|
63
103
|
export { MyAgent };
|
|
64
104
|
```
|
|
65
105
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
import { SqlChatAgent } from "@firtoz/chat-agent";
|
|
70
|
-
|
|
71
|
-
class MyAgent extends SqlChatAgent<Env> {
|
|
72
|
-
// Same API as DrizzleChatAgent
|
|
73
|
-
protected override getSystemPrompt(): string {
|
|
74
|
-
return "You are a helpful assistant.";
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
## Setup Instructions
|
|
80
|
-
|
|
81
|
-
### DrizzleChatAgent Setup
|
|
82
|
-
|
|
83
|
-
#### 1. Add Wrangler Rules
|
|
84
|
-
|
|
85
|
-
**IMPORTANT:** Add this to your `wrangler.jsonc` to import SQL migration files:
|
|
86
|
-
|
|
87
|
-
```jsonc
|
|
88
|
-
{
|
|
89
|
-
/**
|
|
90
|
-
* Rules to import SQL migration files as text
|
|
91
|
-
* Required for Drizzle ORM migrations in Durable Objects
|
|
92
|
-
* @see https://orm.drizzle.team/docs/connect-cloudflare-do
|
|
93
|
-
*/
|
|
94
|
-
"rules": [
|
|
95
|
-
{
|
|
96
|
-
"type": "Text",
|
|
97
|
-
"globs": ["**/*.sql"],
|
|
98
|
-
"fallthrough": true
|
|
99
|
-
}
|
|
100
|
-
]
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
#### 2. Create Drizzle Config
|
|
105
|
-
|
|
106
|
-
Create `drizzle.config.ts` in your package:
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
import { defineConfig } from "drizzle-kit";
|
|
110
|
-
|
|
111
|
-
export default defineConfig({
|
|
112
|
-
schema: "./node_modules/@firtoz/chat-agent/src/db/schema.ts",
|
|
113
|
-
out: "./drizzle",
|
|
114
|
-
dialect: "sqlite",
|
|
115
|
-
driver: "durable-sqlite",
|
|
116
|
-
});
|
|
117
|
-
```
|
|
106
|
+
See **`@firtoz/chat-agent-drizzle`** for Wrangler rules, migrations, and `drizzle.config.ts`.
|
|
118
107
|
|
|
119
|
-
|
|
108
|
+
## Quick start (SQL)
|
|
120
109
|
|
|
121
110
|
```typescript
|
|
122
|
-
import {
|
|
123
|
-
|
|
124
|
-
export default defineConfig({
|
|
125
|
-
schema: "./src/db/schema.ts",
|
|
126
|
-
out: "./drizzle",
|
|
127
|
-
dialect: "sqlite",
|
|
128
|
-
driver: "durable-sqlite",
|
|
129
|
-
});
|
|
130
|
-
```
|
|
111
|
+
import { defineTool } from "@firtoz/chat-agent";
|
|
112
|
+
import { SqlChatAgent } from "@firtoz/chat-agent-sql";
|
|
131
113
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
```json
|
|
135
|
-
{
|
|
136
|
-
"scripts": {
|
|
137
|
-
"db:generate": "bunx drizzle-kit generate"
|
|
138
|
-
}
|
|
114
|
+
class MyAgent extends SqlChatAgent<Env> {
|
|
115
|
+
// Same overrides as Drizzle; tables are created in dbInitialize()
|
|
139
116
|
}
|
|
140
117
|
```
|
|
141
118
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
bun run db:generate
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
This creates:
|
|
149
|
-
- `drizzle/migrations.js` - Runtime migration imports
|
|
150
|
-
- `drizzle/0000_*.sql` - Migration SQL files
|
|
151
|
-
- `drizzle/meta/` - Journal and snapshots
|
|
152
|
-
|
|
153
|
-
The migrations are automatically run when `DrizzleChatAgent` initializes.
|
|
154
|
-
|
|
155
|
-
### SqlChatAgent Setup
|
|
156
|
-
|
|
157
|
-
No additional setup needed! The SQL version creates tables automatically using `this.sql` template tags in `dbInitialize()`.
|
|
158
|
-
|
|
159
|
-
## ChatAgentBase (Abstract Class)
|
|
160
|
-
|
|
161
|
-
The base class handles all chat logic and defines the abstract database interface that implementations must provide.
|
|
119
|
+
## ChatAgentBase (abstract)
|
|
162
120
|
|
|
163
|
-
|
|
121
|
+
Subclasses must implement the database interface (see Drizzle/SQL packages for examples).
|
|
164
122
|
|
|
165
|
-
|
|
123
|
+
### Abstract methods
|
|
166
124
|
|
|
167
125
|
```typescript
|
|
168
|
-
// Initialization
|
|
169
126
|
protected abstract dbInitialize(): void;
|
|
170
|
-
|
|
171
|
-
// Messages
|
|
172
127
|
protected abstract dbLoadMessages(): ChatMessage[];
|
|
173
128
|
protected abstract dbSaveMessage(msg: ChatMessage): void;
|
|
174
129
|
protected abstract dbClearAll(): void;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
messageId: string;
|
|
180
|
-
createdAt: Date
|
|
130
|
+
protected abstract dbFindActiveStream(): {
|
|
131
|
+
id: string;
|
|
132
|
+
messageId: string;
|
|
133
|
+
createdAt: Date;
|
|
181
134
|
} | null;
|
|
182
135
|
protected abstract dbDeleteStreamWithChunks(streamId: string): void;
|
|
183
136
|
protected abstract dbInsertStreamMetadata(streamId: string, messageId: string): void;
|
|
184
|
-
protected abstract dbUpdateStreamStatus(
|
|
137
|
+
protected abstract dbUpdateStreamStatus(
|
|
138
|
+
streamId: string,
|
|
139
|
+
status: "completed" | "error",
|
|
140
|
+
): void;
|
|
185
141
|
protected abstract dbDeleteOldCompletedStreams(cutoffMs: number): void;
|
|
186
|
-
|
|
187
|
-
// Stream chunks
|
|
188
142
|
protected abstract dbFindMaxChunkIndex(streamId: string): number | null;
|
|
189
|
-
protected abstract dbInsertChunks(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
143
|
+
protected abstract dbInsertChunks(
|
|
144
|
+
chunks: Array<{
|
|
145
|
+
id: string;
|
|
146
|
+
streamId: string;
|
|
147
|
+
content: string;
|
|
148
|
+
chunkIndex: number;
|
|
149
|
+
}>,
|
|
150
|
+
): void;
|
|
195
151
|
protected abstract dbGetChunks(streamId: string): string[];
|
|
196
152
|
protected abstract dbDeleteChunks(streamId: string): void;
|
|
153
|
+
protected abstract dbIsStreamKnown(streamId: string): boolean;
|
|
154
|
+
protected abstract dbReplaceAllMessages(messages: ChatMessage[]): void;
|
|
155
|
+
protected abstract dbTrimMessagesToMax(maxMessages: number): void;
|
|
197
156
|
```
|
|
198
157
|
|
|
199
|
-
###
|
|
200
|
-
|
|
201
|
-
Customize your agent's behavior:
|
|
158
|
+
### Common overrides
|
|
202
159
|
|
|
203
160
|
```typescript
|
|
204
|
-
protected getSystemPrompt(): string {
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
protected getModel(): string {
|
|
209
|
-
// Popular OpenRouter models:
|
|
210
|
-
// - anthropic/claude-opus-4.5 (most capable)
|
|
211
|
-
// - anthropic/claude-sonnet-4.5 (balanced, default)
|
|
212
|
-
// - anthropic/claude-haiku-3.5 (fastest, cheapest)
|
|
213
|
-
return "anthropic/claude-sonnet-4.5";
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
protected getTools(): ToolDefinition[] {
|
|
217
|
-
return [
|
|
218
|
-
// Your tools here
|
|
219
|
-
];
|
|
220
|
-
}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## DrizzleChatAgent
|
|
224
|
-
|
|
225
|
-
Type-safe implementation using Drizzle ORM.
|
|
226
|
-
|
|
227
|
-
### Database Schema
|
|
228
|
-
|
|
229
|
-
The package provides three tables:
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
// From @firtoz/chat-agent/db/schema
|
|
233
|
-
|
|
234
|
-
export const messagesTable = sqliteTable("messages", {
|
|
235
|
-
id: text("id").primaryKey(),
|
|
236
|
-
role: text("role", { enum: ["user", "assistant", "tool"] }).notNull(),
|
|
237
|
-
messageJson: text("message_json").notNull(),
|
|
238
|
-
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
export const streamChunksTable = sqliteTable("stream_chunks", {
|
|
242
|
-
id: text("id").primaryKey(),
|
|
243
|
-
streamId: text("stream_id").notNull(),
|
|
244
|
-
content: text("content").notNull(),
|
|
245
|
-
chunkIndex: integer("chunk_index").notNull(),
|
|
246
|
-
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
export const streamMetadataTable = sqliteTable("stream_metadata", {
|
|
250
|
-
id: text("id").primaryKey(),
|
|
251
|
-
messageId: text("message_id").notNull(),
|
|
252
|
-
status: text("status", { enum: ["streaming", "completed", "error"] }).notNull(),
|
|
253
|
-
createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull(),
|
|
254
|
-
completedAt: integer("completed_at", { mode: "timestamp_ms" }),
|
|
255
|
-
});
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### Implementation Details
|
|
259
|
-
|
|
260
|
-
```typescript
|
|
261
|
-
import { DrizzleChatAgent } from "@firtoz/chat-agent";
|
|
262
|
-
|
|
263
|
-
// Automatically:
|
|
264
|
-
// - Creates Drizzle DB instance in dbInitialize()
|
|
265
|
-
// - Runs migrations from drizzle/migrations.js
|
|
266
|
-
// - Uses type-safe query builder for all operations
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
## SqlChatAgent
|
|
270
|
-
|
|
271
|
-
Raw SQL implementation following `@cloudflare/ai-chat` pattern.
|
|
272
|
-
|
|
273
|
-
### Table Structure
|
|
274
|
-
|
|
275
|
-
Creates these tables automatically:
|
|
276
|
-
|
|
277
|
-
```sql
|
|
278
|
-
CREATE TABLE IF NOT EXISTS cf_ai_chat_agent_messages (
|
|
279
|
-
id TEXT PRIMARY KEY,
|
|
280
|
-
message TEXT NOT NULL,
|
|
281
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
282
|
-
);
|
|
283
|
-
|
|
284
|
-
CREATE TABLE IF NOT EXISTS cf_ai_chat_stream_chunks (
|
|
285
|
-
id TEXT PRIMARY KEY,
|
|
286
|
-
stream_id TEXT NOT NULL,
|
|
287
|
-
body TEXT NOT NULL,
|
|
288
|
-
chunk_index INTEGER NOT NULL,
|
|
289
|
-
created_at INTEGER NOT NULL
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
CREATE TABLE IF NOT EXISTS cf_ai_chat_stream_metadata (
|
|
293
|
-
id TEXT PRIMARY KEY,
|
|
294
|
-
request_id TEXT NOT NULL,
|
|
295
|
-
status TEXT NOT NULL,
|
|
296
|
-
created_at INTEGER NOT NULL,
|
|
297
|
-
completed_at INTEGER
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
CREATE INDEX IF NOT EXISTS idx_stream_chunks_stream_id
|
|
301
|
-
ON cf_ai_chat_stream_chunks(stream_id, chunk_index);
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### Implementation Details
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
import { SqlChatAgent } from "@firtoz/chat-agent";
|
|
308
|
-
|
|
309
|
-
// Uses Agent's built-in this.sql template tag:
|
|
310
|
-
// - this.sql`SELECT * FROM table WHERE id = ${id}`
|
|
311
|
-
// - No additional dependencies
|
|
312
|
-
// - Tables created automatically in dbInitialize()
|
|
161
|
+
protected getSystemPrompt(): string { /* … */ }
|
|
162
|
+
protected getModel(): string { /* … */ }
|
|
163
|
+
protected getTools(): ToolDefinition[] { /* … */ }
|
|
313
164
|
```
|
|
314
165
|
|
|
315
166
|
## Tools
|
|
316
167
|
|
|
317
|
-
### Server-
|
|
168
|
+
### Server-side
|
|
318
169
|
|
|
319
|
-
|
|
170
|
+
`defineTool` with `execute` runs on the server; results are merged into the conversation.
|
|
320
171
|
|
|
321
|
-
|
|
322
|
-
defineTool({
|
|
323
|
-
name: "get_weather",
|
|
324
|
-
description: "Get weather for a location",
|
|
325
|
-
parameters: {
|
|
326
|
-
type: "object",
|
|
327
|
-
properties: {
|
|
328
|
-
location: { type: "string" }
|
|
329
|
-
},
|
|
330
|
-
required: ["location"]
|
|
331
|
-
},
|
|
332
|
-
execute: async (args: { location: string }) => {
|
|
333
|
-
// Runs on server, result automatically added to conversation
|
|
334
|
-
const weather = await fetchWeather(args.location);
|
|
335
|
-
return { temperature: weather.temp, condition: weather.condition };
|
|
336
|
-
}
|
|
337
|
-
})
|
|
338
|
-
```
|
|
172
|
+
### Client-side
|
|
339
173
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
Tools without `execute` are sent to client for execution:
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
defineTool({
|
|
346
|
-
name: "get_user_location",
|
|
347
|
-
description: "Get user's browser location",
|
|
348
|
-
parameters: {
|
|
349
|
-
type: "object",
|
|
350
|
-
properties: {}
|
|
351
|
-
}
|
|
352
|
-
// No execute - client handles this via WebSocket
|
|
353
|
-
})
|
|
354
|
-
```
|
|
174
|
+
Omit `execute`; tool calls are sent to the client over the WebSocket.
|
|
355
175
|
|
|
356
|
-
## Environment
|
|
176
|
+
## Environment variables
|
|
357
177
|
|
|
358
178
|
```env
|
|
359
|
-
# Required
|
|
360
179
|
OPENROUTER_API_KEY=sk-or-...
|
|
361
|
-
|
|
362
180
|
# Optional: Cloudflare AI Gateway
|
|
363
|
-
CLOUDFLARE_ACCOUNT_ID
|
|
364
|
-
AI_GATEWAY_NAME
|
|
365
|
-
AI_GATEWAY_TOKEN
|
|
181
|
+
CLOUDFLARE_ACCOUNT_ID=…
|
|
182
|
+
AI_GATEWAY_NAME=…
|
|
183
|
+
AI_GATEWAY_TOKEN=…
|
|
366
184
|
```
|
|
367
185
|
|
|
368
|
-
##
|
|
186
|
+
## Types (excerpt)
|
|
369
187
|
|
|
370
|
-
|
|
188
|
+
`UserMessage`, `AssistantMessage`, `ToolMessage`, `ToolCall`, `ClientMessage`, `ServerMessage`, etc. are exported from this package—import types from `@firtoz/chat-agent` only.
|
|
371
189
|
|
|
372
|
-
|
|
373
|
-
type UserMessage = {
|
|
374
|
-
id: string;
|
|
375
|
-
role: "user";
|
|
376
|
-
content: string;
|
|
377
|
-
createdAt: number;
|
|
378
|
-
};
|
|
190
|
+
## Drizzle vs SQL
|
|
379
191
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
type ToolMessage = {
|
|
389
|
-
id: string;
|
|
390
|
-
role: "tool";
|
|
391
|
-
toolCallId: string;
|
|
392
|
-
content: string;
|
|
393
|
-
createdAt: number;
|
|
394
|
-
};
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
### ToolDefinition
|
|
398
|
-
|
|
399
|
-
```typescript
|
|
400
|
-
type ToolDefinition = {
|
|
401
|
-
type: "function";
|
|
402
|
-
function: {
|
|
403
|
-
name: string;
|
|
404
|
-
description?: string;
|
|
405
|
-
parameters?: JSONSchema;
|
|
406
|
-
strict?: boolean;
|
|
407
|
-
};
|
|
408
|
-
execute?: (args: any) => unknown | Promise<unknown>;
|
|
409
|
-
};
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
## Features
|
|
413
|
-
|
|
414
|
-
### Implemented
|
|
415
|
-
|
|
416
|
-
- ✅ Message persistence (Drizzle or SQL)
|
|
417
|
-
- ✅ Resumable streaming with chunk buffering
|
|
418
|
-
- ✅ Stream restoration on reconnect
|
|
419
|
-
- ✅ Request cancellation via AbortController
|
|
420
|
-
- ✅ Server-side and client-side tools
|
|
421
|
-
- ✅ Tool result handling with auto-continue
|
|
422
|
-
- ✅ Cloudflare AI Gateway support
|
|
423
|
-
- ✅ DB-agnostic architecture
|
|
424
|
-
|
|
425
|
-
### Comparison: Drizzle vs SQL
|
|
426
|
-
|
|
427
|
-
| Feature | DrizzleChatAgent | SqlChatAgent |
|
|
428
|
-
|---------|-----------------|--------------|
|
|
429
|
-
| Type Safety | ✅ Full type inference | ❌ Template string types only |
|
|
430
|
-
| Setup Complexity | ⚠️ Requires migrations | ✅ Auto-creates tables |
|
|
431
|
-
| Dependencies | Drizzle ORM + drizzle-kit | None (uses Agent.sql) |
|
|
432
|
-
| Query Builder | ✅ Yes | ❌ Raw SQL only |
|
|
433
|
-
| Performance | Same | Same |
|
|
434
|
-
| Wrangler Config | ⚠️ Requires rules for .sql | ✅ No special config |
|
|
435
|
-
| Recommended For | New projects, teams | Quick prototypes, SQL experts |
|
|
192
|
+
| | Drizzle | SQL |
|
|
193
|
+
|---|--------|-----|
|
|
194
|
+
| Package | `@firtoz/chat-agent-drizzle` | `@firtoz/chat-agent-sql` |
|
|
195
|
+
| Type safety | Full schema + query builder | Template SQL |
|
|
196
|
+
| Setup | Migrations + Wrangler `.sql` rules | Auto-creates tables |
|
|
197
|
+
| Best for | New projects | Prototypes / raw SQL |
|
|
436
198
|
|
|
437
199
|
## License
|
|
438
200
|
|
|
439
201
|
MIT
|
|
440
|
-
|
|
441
|
-
## Contributing
|
|
442
|
-
|
|
443
|
-
PRs welcome!
|