@falai/agent 0.3.12 → 0.3.20

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.
Files changed (51) hide show
  1. package/README.md +72 -0
  2. package/dist/adapters/MongoAdapter.d.ts +97 -0
  3. package/dist/adapters/MongoAdapter.d.ts.map +1 -0
  4. package/dist/adapters/MongoAdapter.js +163 -0
  5. package/dist/adapters/MongoAdapter.js.map +1 -0
  6. package/dist/adapters/PostgreSQLAdapter.d.ts +71 -0
  7. package/dist/adapters/PostgreSQLAdapter.d.ts.map +1 -0
  8. package/dist/adapters/PostgreSQLAdapter.js +256 -0
  9. package/dist/adapters/PostgreSQLAdapter.js.map +1 -0
  10. package/dist/adapters/RedisAdapter.d.ts +71 -0
  11. package/dist/adapters/RedisAdapter.d.ts.map +1 -0
  12. package/dist/adapters/RedisAdapter.js +226 -0
  13. package/dist/adapters/RedisAdapter.js.map +1 -0
  14. package/dist/adapters/index.d.ts +6 -0
  15. package/dist/adapters/index.d.ts.map +1 -1
  16. package/dist/adapters/index.js +3 -0
  17. package/dist/adapters/index.js.map +1 -1
  18. package/dist/cjs/adapters/MongoAdapter.d.ts +97 -0
  19. package/dist/cjs/adapters/MongoAdapter.d.ts.map +1 -0
  20. package/dist/cjs/adapters/MongoAdapter.js +167 -0
  21. package/dist/cjs/adapters/MongoAdapter.js.map +1 -0
  22. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts +71 -0
  23. package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +1 -0
  24. package/dist/cjs/adapters/PostgreSQLAdapter.js +260 -0
  25. package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -0
  26. package/dist/cjs/adapters/RedisAdapter.d.ts +71 -0
  27. package/dist/cjs/adapters/RedisAdapter.d.ts.map +1 -0
  28. package/dist/cjs/adapters/RedisAdapter.js +230 -0
  29. package/dist/cjs/adapters/RedisAdapter.js.map +1 -0
  30. package/dist/cjs/adapters/index.d.ts +6 -0
  31. package/dist/cjs/adapters/index.d.ts.map +1 -1
  32. package/dist/cjs/adapters/index.js +7 -1
  33. package/dist/cjs/adapters/index.js.map +1 -1
  34. package/dist/cjs/index.d.ts +6 -0
  35. package/dist/cjs/index.d.ts.map +1 -1
  36. package/dist/cjs/index.js +7 -1
  37. package/dist/cjs/index.js.map +1 -1
  38. package/dist/index.d.ts +6 -0
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +3 -0
  41. package/dist/index.js.map +1 -1
  42. package/docs/ADAPTERS.md +127 -0
  43. package/docs/API_REFERENCE.md +337 -0
  44. package/docs/PERSISTENCE.md +100 -6
  45. package/examples/redis-persistence.ts +89 -0
  46. package/package.json +22 -2
  47. package/src/adapters/MongoAdapter.ts +295 -0
  48. package/src/adapters/PostgreSQLAdapter.ts +417 -0
  49. package/src/adapters/RedisAdapter.ts +365 -0
  50. package/src/adapters/index.ts +18 -0
  51. package/src/index.ts +15 -0
@@ -408,12 +408,106 @@ Check your field mappings match your actual database schema.
408
408
 
409
409
  ## Other Databases
410
410
 
411
- The adapter pattern works with any database. Examples:
411
+ The adapter pattern works with any database. Built-in adapters:
412
412
 
413
- - **MongoDB**: Create `MongoAdapter`
414
- - **PostgreSQL (raw)**: Create `PostgresAdapter`
415
- - **MySQL**: Create `MySQLAdapter`
416
- - **Redis**: Create `RedisAdapter`
417
- - **Elasticsearch**: Create `ElasticsearchAdapter`
413
+ ### Redis
414
+
415
+ Perfect for high-throughput, real-time applications:
416
+
417
+ ```typescript
418
+ import { RedisAdapter } from "@falai/agent";
419
+ import Redis from "ioredis";
420
+
421
+ const redis = new Redis();
422
+
423
+ const agent = new Agent({
424
+ persistence: {
425
+ adapter: new RedisAdapter({
426
+ redis,
427
+ keyPrefix: "agent:", // Optional: custom prefix
428
+ sessionTTL: 24 * 60 * 60, // Optional: 24 hours
429
+ messageTTL: 7 * 24 * 60 * 60, // Optional: 7 days
430
+ }),
431
+ },
432
+ });
433
+ ```
434
+
435
+ **Install:** `npm install ioredis` or `npm install redis`
436
+
437
+ ### Coming Soon
438
+
439
+ Create your own adapter for:
440
+
441
+ - **MongoDB**: Document-based storage ✅ **Available!**
442
+ - **PostgreSQL**: Raw SQL for custom schemas ✅ **Available!**
443
+ - **MySQL**: Traditional relational database (coming soon)
444
+ - **Elasticsearch**: Full-text search integration (coming soon)
418
445
 
419
446
  Just implement the `PersistenceAdapter` interface!
447
+
448
+ ### MongoDB
449
+
450
+ Document-based storage with flexible schema:
451
+
452
+ ```typescript
453
+ import { MongoAdapter } from "@falai/agent";
454
+ import { MongoClient } from "mongodb";
455
+
456
+ const client = new MongoClient("mongodb://localhost:27017");
457
+ await client.connect();
458
+
459
+ const agent = new Agent({
460
+ persistence: {
461
+ adapter: new MongoAdapter({
462
+ client,
463
+ databaseName: "myapp",
464
+ collections: {
465
+ // Optional: custom names
466
+ sessions: "agent_sessions",
467
+ messages: "agent_messages",
468
+ },
469
+ }),
470
+ },
471
+ });
472
+ ```
473
+
474
+ **Install:** `npm install mongodb`
475
+
476
+ ### PostgreSQL
477
+
478
+ Raw SQL adapter with auto-table creation:
479
+
480
+ ```typescript
481
+ import { PostgreSQLAdapter } from "@falai/agent";
482
+ import { Client } from "pg";
483
+
484
+ const client = new Client({
485
+ host: "localhost",
486
+ database: "myapp",
487
+ user: "postgres",
488
+ password: "password",
489
+ });
490
+ await client.connect();
491
+
492
+ const adapter = new PostgreSQLAdapter({
493
+ client,
494
+ tables: {
495
+ // Optional: custom names
496
+ sessions: "agent_sessions",
497
+ messages: "agent_messages",
498
+ },
499
+ });
500
+
501
+ // Auto-create tables with indexes
502
+ await adapter.initialize();
503
+
504
+ const agent = new Agent({
505
+ persistence: {
506
+ adapter,
507
+ },
508
+ });
509
+ ```
510
+
511
+ **Install:** `npm install pg`
512
+
513
+ **Note:** PostgreSQL adapter includes `initialize()` method to auto-create tables with proper indexes and foreign keys.
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Example: Using Redis for Persistence
3
+ *
4
+ * Fast, in-memory persistence perfect for:
5
+ * - High-throughput applications
6
+ * - Session caching
7
+ * - Real-time chat applications
8
+ * - Temporary conversation storage
9
+ */
10
+
11
+ import { Agent, GeminiProvider, RedisAdapter } from "../src/index";
12
+ // @ts-ignore
13
+ import Redis from "ioredis";
14
+
15
+ /**
16
+ * Setup Steps:
17
+ *
18
+ * 1. Install Redis and client:
19
+ * brew install redis (macOS) or apt-get install redis (Linux)
20
+ * npm install ioredis
21
+ *
22
+ * 2. Start Redis:
23
+ * redis-server
24
+ *
25
+ * 3. Run this example
26
+ */
27
+
28
+ async function example() {
29
+ // Initialize Redis client
30
+ const redis = new Redis();
31
+
32
+ // Create agent with Redis persistence
33
+ const agent = new Agent({
34
+ name: "Chat Assistant",
35
+ description: "Fast, real-time chat assistant",
36
+ ai: new GeminiProvider({
37
+ apiKey: process.env.GEMINI_API_KEY!,
38
+ model: "models/gemini-2.0-flash-exp",
39
+ }),
40
+ // ✨ Redis adapter with custom options
41
+ persistence: {
42
+ adapter: new RedisAdapter({
43
+ redis,
44
+ keyPrefix: "chat:", // Custom prefix
45
+ sessionTTL: 24 * 60 * 60, // 24 hours
46
+ messageTTL: 7 * 24 * 60 * 60, // 7 days
47
+ }),
48
+ autoSave: true,
49
+ userId: "user_123",
50
+ },
51
+ });
52
+
53
+ const persistence = agent.getPersistenceManager();
54
+ if (!persistence) return;
55
+
56
+ // Create session
57
+ const session = await persistence.createSession({
58
+ userId: "user_123",
59
+ agentName: "Chat Assistant",
60
+ initialData: { chatType: "support" },
61
+ });
62
+
63
+ console.log("✨ Session created in Redis:", session.id);
64
+
65
+ // Save a message
66
+ await persistence.saveMessage({
67
+ sessionId: session.id,
68
+ role: "user",
69
+ content: "Hello! I need help",
70
+ });
71
+
72
+ // Load messages
73
+ const messages = await persistence.getSessionMessages(session.id);
74
+ console.log(`💬 ${messages.length} messages in session`);
75
+
76
+ // Complete session
77
+ await persistence.completeSession(session.id);
78
+ console.log("✅ Session completed");
79
+
80
+ // Cleanup
81
+ await redis.quit();
82
+ }
83
+
84
+ // Run the example
85
+ if (require.main === module) {
86
+ example().catch(console.error);
87
+ }
88
+
89
+ export { example };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@falai/agent",
3
- "version": "0.3.12",
3
+ "version": "0.3.20",
4
4
  "description": "Standalone, strongly-typed AI Agent framework with route DSL and AI provider strategy",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -82,11 +82,31 @@
82
82
  "openai": "^6.3.0"
83
83
  },
84
84
  "peerDependencies": {
85
- "@prisma/client": "^5.0.0 || ^6.0.0"
85
+ "@prisma/client": "^6.0.0",
86
+ "ioredis": "^5.7.0",
87
+ "redis": "^4.6.0 || ^5.0.0",
88
+ "mongodb": "^6.0.0 || ^7.0.0",
89
+ "pg": "^8.11.0",
90
+ "mysql2": "^3.2.0"
86
91
  },
87
92
  "peerDependenciesMeta": {
88
93
  "@prisma/client": {
89
94
  "optional": true
95
+ },
96
+ "ioredis": {
97
+ "optional": true
98
+ },
99
+ "redis": {
100
+ "optional": true
101
+ },
102
+ "mongodb": {
103
+ "optional": true
104
+ },
105
+ "pg": {
106
+ "optional": true
107
+ },
108
+ "mysql2": {
109
+ "optional": true
90
110
  }
91
111
  }
92
112
  }
@@ -0,0 +1,295 @@
1
+ /**
2
+ * MongoDB adapter for persistence
3
+ * Document-based storage with flexible schema
4
+ */
5
+
6
+ import type {
7
+ SessionRepository,
8
+ MessageRepository,
9
+ SessionData,
10
+ MessageData,
11
+ SessionStatus,
12
+ PersistenceAdapter,
13
+ } from "../types/persistence";
14
+
15
+ /**
16
+ * MongoDB collection interface - matches mongodb driver
17
+ */
18
+ export interface MongoCollection<T = Record<string, unknown>> {
19
+ insertOne(doc: T): Promise<{ insertedId: unknown }>;
20
+ findOne(filter: Record<string, unknown>): Promise<T | null>;
21
+ find(filter: Record<string, unknown>): {
22
+ sort(sort: Record<string, number>): {
23
+ limit(limit: number): {
24
+ toArray(): Promise<T[]>;
25
+ };
26
+ };
27
+ toArray(): Promise<T[]>;
28
+ };
29
+ updateOne(
30
+ filter: Record<string, unknown>,
31
+ update: Record<string, unknown>
32
+ ): Promise<{ matchedCount: number }>;
33
+ deleteOne(filter: Record<string, unknown>): Promise<{ deletedCount: number }>;
34
+ deleteMany(
35
+ filter: Record<string, unknown>
36
+ ): Promise<{ deletedCount: number }>;
37
+ }
38
+
39
+ /**
40
+ * MongoDB database interface
41
+ */
42
+ export interface MongoDatabase {
43
+ collection<T = Record<string, unknown>>(name: string): MongoCollection<T>;
44
+ }
45
+
46
+ /**
47
+ * MongoDB client interface
48
+ */
49
+ export interface MongoClient {
50
+ db(name?: string): MongoDatabase;
51
+ close(): Promise<void>;
52
+ }
53
+
54
+ /**
55
+ * Options for MongoDB adapter
56
+ */
57
+ export interface MongoAdapterOptions {
58
+ /**
59
+ * MongoDB client instance
60
+ */
61
+ client: MongoClient;
62
+
63
+ /**
64
+ * Database name
65
+ */
66
+ databaseName: string;
67
+
68
+ /**
69
+ * Collection names (default: "agent_sessions" and "agent_messages")
70
+ */
71
+ collections?: {
72
+ sessions?: string;
73
+ messages?: string;
74
+ };
75
+ }
76
+
77
+ /**
78
+ * MongoDB Adapter - Provider-style API for MongoDB persistence
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { MongoClient } from 'mongodb';
83
+ * import { Agent, MongoAdapter } from '@falai/agent';
84
+ *
85
+ * const client = new MongoClient('mongodb://localhost:27017');
86
+ * await client.connect();
87
+ *
88
+ * const agent = new Agent({
89
+ * name: "My Agent",
90
+ * ai: provider,
91
+ * persistence: {
92
+ * adapter: new MongoAdapter({
93
+ * client,
94
+ * databaseName: 'myapp',
95
+ * }),
96
+ * userId: "user_123",
97
+ * },
98
+ * });
99
+ * ```
100
+ */
101
+ export class MongoAdapter implements PersistenceAdapter {
102
+ public readonly sessionRepository: SessionRepository;
103
+ public readonly messageRepository: MessageRepository;
104
+ private client: MongoClient;
105
+ private db: MongoDatabase;
106
+
107
+ constructor(options: MongoAdapterOptions) {
108
+ this.client = options.client;
109
+ this.db = options.client.db(options.databaseName);
110
+
111
+ const sessionCollection = options.collections?.sessions || "agent_sessions";
112
+ const messageCollection = options.collections?.messages || "agent_messages";
113
+
114
+ this.sessionRepository = new MongoSessionRepository(
115
+ this.db.collection(sessionCollection)
116
+ );
117
+
118
+ this.messageRepository = new MongoMessageRepository(
119
+ this.db.collection(messageCollection)
120
+ );
121
+ }
122
+
123
+ async disconnect(): Promise<void> {
124
+ await this.client.close();
125
+ }
126
+ }
127
+
128
+ /**
129
+ * MongoDB Session Repository
130
+ */
131
+ class MongoSessionRepository implements SessionRepository {
132
+ constructor(private collection: MongoCollection<SessionData>) {}
133
+
134
+ async create(
135
+ data: Omit<SessionData, "id" | "createdAt" | "updatedAt">
136
+ ): Promise<SessionData> {
137
+ const now = new Date();
138
+ const session: SessionData = {
139
+ ...data,
140
+ id: `session_${Date.now()}_${Math.random().toString(36).slice(2)}`,
141
+ status: data.status || "active",
142
+ messageCount: data.messageCount || 0,
143
+ createdAt: now,
144
+ updatedAt: now,
145
+ };
146
+
147
+ await this.collection.insertOne(session);
148
+ return session;
149
+ }
150
+
151
+ async findById(id: string): Promise<SessionData | null> {
152
+ return await this.collection.findOne({ id });
153
+ }
154
+
155
+ async findActiveByUserId(userId: string): Promise<SessionData | null> {
156
+ return await this.collection.findOne({ userId, status: "active" });
157
+ }
158
+
159
+ async findByUserId(userId: string, limit = 100): Promise<SessionData[]> {
160
+ return await this.collection
161
+ .find({ userId })
162
+ .sort({ createdAt: -1 })
163
+ .limit(limit)
164
+ .toArray();
165
+ }
166
+
167
+ async update(
168
+ id: string,
169
+ data: Partial<Omit<SessionData, "id" | "createdAt">>
170
+ ): Promise<SessionData | null> {
171
+ const result = await this.collection.updateOne(
172
+ { id },
173
+ { $set: { ...data, updatedAt: new Date() } }
174
+ );
175
+
176
+ if (result.matchedCount === 0) return null;
177
+ return await this.findById(id);
178
+ }
179
+
180
+ async updateStatus(
181
+ id: string,
182
+ status: SessionStatus,
183
+ completedAt?: Date
184
+ ): Promise<SessionData | null> {
185
+ const updateData: Record<string, unknown> = {
186
+ status,
187
+ updatedAt: new Date(),
188
+ };
189
+ if (completedAt) {
190
+ updateData.completedAt = completedAt;
191
+ }
192
+
193
+ const result = await this.collection.updateOne(
194
+ { id },
195
+ { $set: updateData }
196
+ );
197
+
198
+ if (result.matchedCount === 0) return null;
199
+ return await this.findById(id);
200
+ }
201
+
202
+ async updateCollectedData(
203
+ id: string,
204
+ collectedData: Record<string, unknown>
205
+ ): Promise<SessionData | null> {
206
+ return await this.update(id, { collectedData });
207
+ }
208
+
209
+ async updateRouteState(
210
+ id: string,
211
+ route?: string,
212
+ state?: string
213
+ ): Promise<SessionData | null> {
214
+ return await this.update(id, {
215
+ currentRoute: route,
216
+ currentState: state,
217
+ });
218
+ }
219
+
220
+ async incrementMessageCount(id: string): Promise<SessionData | null> {
221
+ const result = await this.collection.updateOne(
222
+ { id },
223
+ {
224
+ $inc: { messageCount: 1 },
225
+ $set: { lastMessageAt: new Date(), updatedAt: new Date() },
226
+ }
227
+ );
228
+
229
+ if (result.matchedCount === 0) return null;
230
+ return await this.findById(id);
231
+ }
232
+
233
+ async delete(id: string): Promise<boolean> {
234
+ const result = await this.collection.deleteOne({ id });
235
+ return result.deletedCount > 0;
236
+ }
237
+ }
238
+
239
+ /**
240
+ * MongoDB Message Repository
241
+ */
242
+ class MongoMessageRepository implements MessageRepository {
243
+ constructor(private collection: MongoCollection<MessageData>) {}
244
+
245
+ async create(
246
+ data: Omit<MessageData, "id" | "createdAt">
247
+ ): Promise<MessageData> {
248
+ const message: MessageData = {
249
+ ...data,
250
+ id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`,
251
+ createdAt: new Date(),
252
+ };
253
+
254
+ await this.collection.insertOne(message);
255
+ return message;
256
+ }
257
+
258
+ async findById(id: string): Promise<MessageData | null> {
259
+ return await this.collection.findOne({ id });
260
+ }
261
+
262
+ async findBySessionId(
263
+ sessionId: string,
264
+ limit = 1000
265
+ ): Promise<MessageData[]> {
266
+ return await this.collection
267
+ .find({ sessionId })
268
+ .sort({ createdAt: 1 })
269
+ .limit(limit)
270
+ .toArray();
271
+ }
272
+
273
+ async findByUserId(userId: string, limit = 100): Promise<MessageData[]> {
274
+ return await this.collection
275
+ .find({ userId })
276
+ .sort({ createdAt: -1 })
277
+ .limit(limit)
278
+ .toArray();
279
+ }
280
+
281
+ async delete(id: string): Promise<boolean> {
282
+ const result = await this.collection.deleteOne({ id });
283
+ return result.deletedCount > 0;
284
+ }
285
+
286
+ async deleteBySessionId(sessionId: string): Promise<number> {
287
+ const result = await this.collection.deleteMany({ sessionId });
288
+ return result.deletedCount;
289
+ }
290
+
291
+ async deleteByUserId(userId: string): Promise<number> {
292
+ const result = await this.collection.deleteMany({ userId });
293
+ return result.deletedCount;
294
+ }
295
+ }