@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.
- package/README.md +72 -0
- package/dist/adapters/MongoAdapter.d.ts +97 -0
- package/dist/adapters/MongoAdapter.d.ts.map +1 -0
- package/dist/adapters/MongoAdapter.js +163 -0
- package/dist/adapters/MongoAdapter.js.map +1 -0
- package/dist/adapters/PostgreSQLAdapter.d.ts +71 -0
- package/dist/adapters/PostgreSQLAdapter.d.ts.map +1 -0
- package/dist/adapters/PostgreSQLAdapter.js +256 -0
- package/dist/adapters/PostgreSQLAdapter.js.map +1 -0
- package/dist/adapters/RedisAdapter.d.ts +71 -0
- package/dist/adapters/RedisAdapter.d.ts.map +1 -0
- package/dist/adapters/RedisAdapter.js +226 -0
- package/dist/adapters/RedisAdapter.js.map +1 -0
- package/dist/adapters/index.d.ts +6 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/cjs/adapters/MongoAdapter.d.ts +97 -0
- package/dist/cjs/adapters/MongoAdapter.d.ts.map +1 -0
- package/dist/cjs/adapters/MongoAdapter.js +167 -0
- package/dist/cjs/adapters/MongoAdapter.js.map +1 -0
- package/dist/cjs/adapters/PostgreSQLAdapter.d.ts +71 -0
- package/dist/cjs/adapters/PostgreSQLAdapter.d.ts.map +1 -0
- package/dist/cjs/adapters/PostgreSQLAdapter.js +260 -0
- package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -0
- package/dist/cjs/adapters/RedisAdapter.d.ts +71 -0
- package/dist/cjs/adapters/RedisAdapter.d.ts.map +1 -0
- package/dist/cjs/adapters/RedisAdapter.js +230 -0
- package/dist/cjs/adapters/RedisAdapter.js.map +1 -0
- package/dist/cjs/adapters/index.d.ts +6 -0
- package/dist/cjs/adapters/index.d.ts.map +1 -1
- package/dist/cjs/adapters/index.js +7 -1
- package/dist/cjs/adapters/index.js.map +1 -1
- package/dist/cjs/index.d.ts +6 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +7 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/docs/ADAPTERS.md +127 -0
- package/docs/API_REFERENCE.md +337 -0
- package/docs/PERSISTENCE.md +100 -6
- package/examples/redis-persistence.ts +89 -0
- package/package.json +22 -2
- package/src/adapters/MongoAdapter.ts +295 -0
- package/src/adapters/PostgreSQLAdapter.ts +417 -0
- package/src/adapters/RedisAdapter.ts +365 -0
- package/src/adapters/index.ts +18 -0
- package/src/index.ts +15 -0
package/docs/PERSISTENCE.md
CHANGED
|
@@ -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.
|
|
411
|
+
The adapter pattern works with any database. Built-in adapters:
|
|
412
412
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
-
|
|
416
|
-
|
|
417
|
-
|
|
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.
|
|
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": "^
|
|
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
|
+
}
|