@falai/agent 0.3.11 → 0.3.12
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 +92 -0
- package/dist/adapters/PrismaAdapter.d.ts +115 -0
- package/dist/adapters/PrismaAdapter.d.ts.map +1 -0
- package/dist/adapters/PrismaAdapter.js +331 -0
- package/dist/adapters/PrismaAdapter.js.map +1 -0
- package/dist/adapters/index.d.ts +6 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +5 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/cjs/adapters/PrismaAdapter.d.ts +115 -0
- package/dist/cjs/adapters/PrismaAdapter.d.ts.map +1 -0
- package/dist/cjs/adapters/PrismaAdapter.js +335 -0
- package/dist/cjs/adapters/PrismaAdapter.js.map +1 -0
- package/dist/cjs/adapters/index.d.ts +6 -0
- package/dist/cjs/adapters/index.d.ts.map +1 -0
- package/dist/cjs/adapters/index.js +9 -0
- package/dist/cjs/adapters/index.js.map +1 -0
- package/dist/cjs/core/Agent.d.ts +10 -0
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +23 -0
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/PersistenceManager.d.ts +77 -0
- package/dist/cjs/core/PersistenceManager.d.ts.map +1 -0
- package/dist/cjs/core/PersistenceManager.js +153 -0
- package/dist/cjs/core/PersistenceManager.js.map +1 -0
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +6 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +3 -0
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/agent.js.map +1 -1
- package/dist/cjs/types/index.d.ts +1 -0
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/persistence.d.ts +194 -0
- package/dist/cjs/types/persistence.d.ts.map +1 -0
- package/dist/cjs/types/persistence.js +7 -0
- package/dist/cjs/types/persistence.js.map +1 -0
- package/dist/core/Agent.d.ts +10 -0
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +23 -0
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/PersistenceManager.d.ts +77 -0
- package/dist/core/PersistenceManager.d.ts.map +1 -0
- package/dist/core/PersistenceManager.js +149 -0
- package/dist/core/PersistenceManager.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/types/agent.d.ts +3 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/agent.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/persistence.d.ts +194 -0
- package/dist/types/persistence.d.ts.map +1 -0
- package/dist/types/persistence.js +6 -0
- package/dist/types/persistence.js.map +1 -0
- package/docs/PERSISTENCE.md +419 -0
- package/examples/prisma-persistence.ts +313 -0
- package/examples/prisma-schema.example.prisma +74 -0
- package/package.json +9 -1
- package/src/adapters/PrismaAdapter.ts +510 -0
- package/src/adapters/index.ts +10 -0
- package/src/core/Agent.ts +31 -0
- package/src/core/PersistenceManager.ts +222 -0
- package/src/index.ts +21 -0
- package/src/types/agent.ts +3 -0
- package/src/types/index.ts +14 -0
- package/src/types/persistence.ts +234 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Using Prisma ORM for Persistence
|
|
3
|
+
*
|
|
4
|
+
* This example shows how to use @falai/agent with Prisma for automatic
|
|
5
|
+
* session and message persistence - as easy as using an AI provider!
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
Agent,
|
|
10
|
+
GeminiProvider,
|
|
11
|
+
PrismaAdapter,
|
|
12
|
+
createMessageEvent,
|
|
13
|
+
EventSource,
|
|
14
|
+
} from "../src/index";
|
|
15
|
+
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
import { PrismaClient } from "@prisma/client";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Setup Steps:
|
|
21
|
+
*
|
|
22
|
+
* 1. Install dependencies:
|
|
23
|
+
* npm install prisma @prisma/client
|
|
24
|
+
*
|
|
25
|
+
* 2. Initialize Prisma:
|
|
26
|
+
* npx prisma init
|
|
27
|
+
*
|
|
28
|
+
* 3. Copy schema from examples/prisma-schema.example.prisma
|
|
29
|
+
* to your prisma/schema.prisma file
|
|
30
|
+
*
|
|
31
|
+
* 4. Generate Prisma client:
|
|
32
|
+
* npx prisma generate
|
|
33
|
+
*
|
|
34
|
+
* 5. Run migrations:
|
|
35
|
+
* npx prisma migrate dev --name init
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
// Example context type
|
|
39
|
+
interface ConversationContext {
|
|
40
|
+
userId: string;
|
|
41
|
+
sessionId: string;
|
|
42
|
+
userName: string;
|
|
43
|
+
preferences: {
|
|
44
|
+
language: string;
|
|
45
|
+
theme: string;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function example() {
|
|
50
|
+
// Initialize Prisma client
|
|
51
|
+
const prisma = new PrismaClient();
|
|
52
|
+
|
|
53
|
+
const userId = "user_123";
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create Agent with Persistence - Simple Provider Pattern! ✨
|
|
57
|
+
*/
|
|
58
|
+
const agent = new Agent<ConversationContext>({
|
|
59
|
+
name: "Shopping Assistant",
|
|
60
|
+
description: "A helpful shopping assistant",
|
|
61
|
+
ai: new GeminiProvider({
|
|
62
|
+
apiKey: process.env.GEMINI_API_KEY!,
|
|
63
|
+
model: "models/gemini-2.0-flash-exp",
|
|
64
|
+
}),
|
|
65
|
+
context: {
|
|
66
|
+
userId,
|
|
67
|
+
sessionId: "", // Will be set when we create/load a session
|
|
68
|
+
userName: "Alice",
|
|
69
|
+
preferences: {
|
|
70
|
+
language: "en",
|
|
71
|
+
theme: "light",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
// ✨ Just pass the adapter - that's it!
|
|
75
|
+
persistence: {
|
|
76
|
+
adapter: new PrismaAdapter({ prisma }),
|
|
77
|
+
autoSave: true,
|
|
78
|
+
userId,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get persistence manager from agent
|
|
84
|
+
*/
|
|
85
|
+
const persistence = agent.getPersistenceManager();
|
|
86
|
+
|
|
87
|
+
if (!persistence) {
|
|
88
|
+
throw new Error("Persistence not configured");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Create or find a session
|
|
93
|
+
*/
|
|
94
|
+
let session = await persistence.findActiveSession(userId);
|
|
95
|
+
|
|
96
|
+
if (!session) {
|
|
97
|
+
session = await persistence.createSession({
|
|
98
|
+
userId,
|
|
99
|
+
agentName: "Shopping Assistant",
|
|
100
|
+
initialData: {
|
|
101
|
+
language: "en",
|
|
102
|
+
theme: "light",
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
console.log("✨ Created new session:", session.id);
|
|
106
|
+
} else {
|
|
107
|
+
console.log("📂 Found active session:", session.id);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Update context with session ID
|
|
111
|
+
await agent.updateContext({
|
|
112
|
+
sessionId: session.id,
|
|
113
|
+
} as Partial<ConversationContext>);
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Load conversation history
|
|
117
|
+
*/
|
|
118
|
+
const history = await persistence.loadSessionHistory(session.id);
|
|
119
|
+
console.log(`📜 Loaded ${history.length} messages from history`);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Send a message
|
|
123
|
+
*/
|
|
124
|
+
const userMessage = createMessageEvent(
|
|
125
|
+
EventSource.CUSTOMER,
|
|
126
|
+
"Alice",
|
|
127
|
+
"I'm looking for a winter jacket"
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// Save user message
|
|
131
|
+
await persistence.saveMessage({
|
|
132
|
+
sessionId: session.id,
|
|
133
|
+
userId,
|
|
134
|
+
role: "user",
|
|
135
|
+
content: "I'm looking for a winter jacket",
|
|
136
|
+
event: userMessage,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
history.push(userMessage);
|
|
140
|
+
|
|
141
|
+
// Generate agent response
|
|
142
|
+
const response = await agent.respond({ history });
|
|
143
|
+
console.log("🤖 Agent:", response.message);
|
|
144
|
+
|
|
145
|
+
// Save agent message
|
|
146
|
+
await persistence.saveMessage({
|
|
147
|
+
sessionId: session.id,
|
|
148
|
+
userId,
|
|
149
|
+
role: "agent",
|
|
150
|
+
content: response.message,
|
|
151
|
+
route: response.route?.id,
|
|
152
|
+
state: response.state?.id,
|
|
153
|
+
toolCalls: response.toolCalls,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Query sessions and messages
|
|
158
|
+
*/
|
|
159
|
+
const userSessions = await persistence.getUserSessions(userId);
|
|
160
|
+
console.log(`👤 User has ${userSessions.length} total sessions`);
|
|
161
|
+
|
|
162
|
+
const messages = await persistence.getSessionMessages(session.id);
|
|
163
|
+
console.log(`💬 Session has ${messages.length} messages`);
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Complete the session
|
|
167
|
+
*/
|
|
168
|
+
await persistence.completeSession(session.id);
|
|
169
|
+
console.log("✅ Session completed");
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Cleanup
|
|
173
|
+
*/
|
|
174
|
+
await prisma.$disconnect();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Advanced Example: Using with Lifecycle Hooks
|
|
179
|
+
*/
|
|
180
|
+
async function advancedExample() {
|
|
181
|
+
const prisma = new PrismaClient();
|
|
182
|
+
const userId = "user_123";
|
|
183
|
+
|
|
184
|
+
const agent = new Agent<ConversationContext>({
|
|
185
|
+
name: "Smart Assistant",
|
|
186
|
+
description: "An intelligent assistant with persistent state",
|
|
187
|
+
ai: new GeminiProvider({
|
|
188
|
+
apiKey: process.env.GEMINI_API_KEY!,
|
|
189
|
+
model: "models/gemini-2.0-flash-exp",
|
|
190
|
+
}),
|
|
191
|
+
context: {
|
|
192
|
+
userId,
|
|
193
|
+
sessionId: "",
|
|
194
|
+
userName: "Alice",
|
|
195
|
+
preferences: {
|
|
196
|
+
language: "en",
|
|
197
|
+
theme: "light",
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
// Lifecycle hooks for automatic context sync
|
|
201
|
+
hooks: {
|
|
202
|
+
// Load fresh context before each response
|
|
203
|
+
beforeRespond: async (currentContext) => {
|
|
204
|
+
const persistence = agent.getPersistenceManager();
|
|
205
|
+
if (!persistence) return currentContext;
|
|
206
|
+
|
|
207
|
+
const freshSession = await persistence.getSession(
|
|
208
|
+
currentContext.sessionId
|
|
209
|
+
);
|
|
210
|
+
return {
|
|
211
|
+
...currentContext,
|
|
212
|
+
preferences:
|
|
213
|
+
(
|
|
214
|
+
freshSession?.collectedData as {
|
|
215
|
+
preferences?: typeof currentContext.preferences;
|
|
216
|
+
}
|
|
217
|
+
)?.preferences || currentContext.preferences,
|
|
218
|
+
};
|
|
219
|
+
},
|
|
220
|
+
// Automatically persist context updates
|
|
221
|
+
onContextUpdate: async (newContext) => {
|
|
222
|
+
const persistence = agent.getPersistenceManager();
|
|
223
|
+
if (!persistence) return;
|
|
224
|
+
|
|
225
|
+
await persistence.updateCollectedData(newContext.sessionId, {
|
|
226
|
+
preferences: newContext.preferences,
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
// ✨ Same simple adapter pattern!
|
|
231
|
+
persistence: {
|
|
232
|
+
adapter: new PrismaAdapter({ prisma }),
|
|
233
|
+
autoSave: true,
|
|
234
|
+
userId,
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Create a session
|
|
239
|
+
const persistence = agent.getPersistenceManager();
|
|
240
|
+
const session = await persistence!.createSession({
|
|
241
|
+
userId,
|
|
242
|
+
agentName: "Smart Assistant",
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
await agent.updateContext({
|
|
246
|
+
sessionId: session.id,
|
|
247
|
+
} as Partial<ConversationContext>);
|
|
248
|
+
|
|
249
|
+
// Now context updates are automatically persisted!
|
|
250
|
+
await agent.updateContext({
|
|
251
|
+
preferences: {
|
|
252
|
+
language: "es",
|
|
253
|
+
theme: "dark",
|
|
254
|
+
},
|
|
255
|
+
} as Partial<ConversationContext>);
|
|
256
|
+
|
|
257
|
+
console.log("🎉 Context updates automatically saved to database!");
|
|
258
|
+
|
|
259
|
+
await prisma.$disconnect();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Minimal Example - Quick Start
|
|
264
|
+
*/
|
|
265
|
+
async function quickStart() {
|
|
266
|
+
const prisma = new PrismaClient();
|
|
267
|
+
|
|
268
|
+
// That's it! Just create the adapter and pass it
|
|
269
|
+
const agent = new Agent({
|
|
270
|
+
name: "My Agent",
|
|
271
|
+
description: "A helpful assistant",
|
|
272
|
+
ai: new GeminiProvider({
|
|
273
|
+
apiKey: process.env.GEMINI_API_KEY!,
|
|
274
|
+
model: "models/gemini-2.5-flash",
|
|
275
|
+
}),
|
|
276
|
+
persistence: {
|
|
277
|
+
adapter: new PrismaAdapter({ prisma }), // ✨ Simple!
|
|
278
|
+
userId: "user_123",
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// Get persistence manager
|
|
283
|
+
const persistence = agent.getPersistenceManager();
|
|
284
|
+
if (!persistence) return;
|
|
285
|
+
|
|
286
|
+
// Create session
|
|
287
|
+
const session = await persistence.createSession({
|
|
288
|
+
userId: "user_123",
|
|
289
|
+
agentName: "My Agent",
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Load history and respond
|
|
293
|
+
const history = await persistence.loadSessionHistory(session.id);
|
|
294
|
+
const response = await agent.respond({ history });
|
|
295
|
+
|
|
296
|
+
// Save message
|
|
297
|
+
await persistence.saveMessage({
|
|
298
|
+
sessionId: session.id,
|
|
299
|
+
role: "agent",
|
|
300
|
+
content: response.message,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
console.log("✅ Done! Messages automatically saved to Prisma.");
|
|
304
|
+
|
|
305
|
+
await prisma.$disconnect();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Run the example
|
|
309
|
+
if (require.main === module) {
|
|
310
|
+
example().catch(console.error);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export { example, advancedExample, quickStart };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// This is an example Prisma schema file for @falai/agent persistence
|
|
2
|
+
// Copy and adapt this to your needs
|
|
3
|
+
|
|
4
|
+
datasource db {
|
|
5
|
+
provider = "postgresql" // or "mysql", "sqlite", "sqlserver", "mongodb"
|
|
6
|
+
url = env("DATABASE_URL")
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
generator client {
|
|
10
|
+
provider = "prisma-client-js"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Agent Session model
|
|
14
|
+
// Stores conversation session state
|
|
15
|
+
model AgentSession {
|
|
16
|
+
id String @id @default(cuid())
|
|
17
|
+
userId String? @map("user_id")
|
|
18
|
+
agentName String? @map("agent_name")
|
|
19
|
+
status String @default("active") // "active" | "completed" | "abandoned"
|
|
20
|
+
currentRoute String? @map("current_route")
|
|
21
|
+
currentState String? @map("current_state")
|
|
22
|
+
collectedData Json? @map("collected_data")
|
|
23
|
+
messageCount Int @default(0) @map("message_count")
|
|
24
|
+
lastMessageAt DateTime? @map("last_message_at")
|
|
25
|
+
completedAt DateTime? @map("completed_at")
|
|
26
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
27
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
28
|
+
|
|
29
|
+
// Relations
|
|
30
|
+
messages AgentMessage[]
|
|
31
|
+
|
|
32
|
+
@@index([userId])
|
|
33
|
+
@@index([status])
|
|
34
|
+
@@index([userId, status])
|
|
35
|
+
@@map("agent_sessions")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Agent Message model
|
|
39
|
+
// Stores individual messages in a conversation
|
|
40
|
+
model AgentMessage {
|
|
41
|
+
id String @id @default(cuid())
|
|
42
|
+
sessionId String @map("session_id")
|
|
43
|
+
userId String? @map("user_id")
|
|
44
|
+
role String // "user" | "agent" | "system"
|
|
45
|
+
content String @db.Text
|
|
46
|
+
route String?
|
|
47
|
+
state String?
|
|
48
|
+
toolCalls Json? @map("tool_calls")
|
|
49
|
+
event Json? // Optional: store full event data
|
|
50
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
51
|
+
|
|
52
|
+
// Relations
|
|
53
|
+
session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
54
|
+
|
|
55
|
+
@@index([sessionId])
|
|
56
|
+
@@index([userId])
|
|
57
|
+
@@index([sessionId, createdAt])
|
|
58
|
+
@@map("agent_messages")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Alternative: If you want different table names, you can customize:
|
|
62
|
+
//
|
|
63
|
+
// model ConversationSession {
|
|
64
|
+
// id String @id @default(cuid())
|
|
65
|
+
// ...
|
|
66
|
+
// @@map("conversations")
|
|
67
|
+
// }
|
|
68
|
+
//
|
|
69
|
+
// model ChatMessage {
|
|
70
|
+
// id String @id @default(cuid())
|
|
71
|
+
// ...
|
|
72
|
+
// @@map("chat_messages")
|
|
73
|
+
// }
|
|
74
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@falai/agent",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
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",
|
|
@@ -80,5 +80,13 @@
|
|
|
80
80
|
"@anthropic-ai/sdk": "^0.65.0",
|
|
81
81
|
"@google/genai": "^0.3.0",
|
|
82
82
|
"openai": "^6.3.0"
|
|
83
|
+
},
|
|
84
|
+
"peerDependencies": {
|
|
85
|
+
"@prisma/client": "^5.0.0 || ^6.0.0"
|
|
86
|
+
},
|
|
87
|
+
"peerDependenciesMeta": {
|
|
88
|
+
"@prisma/client": {
|
|
89
|
+
"optional": true
|
|
90
|
+
}
|
|
83
91
|
}
|
|
84
92
|
}
|