@falai/agent 0.3.10 → 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 +192 -16
- 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 +35 -0
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +153 -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 +6 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.d.ts +43 -0
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -0
- package/dist/cjs/providers/AnthropicProvider.js +328 -0
- package/dist/cjs/providers/AnthropicProvider.js.map +1 -0
- package/dist/cjs/providers/GeminiProvider.d.ts +4 -1
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +96 -0
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts +4 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.js +115 -0
- package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts +4 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.js +115 -0
- package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
- package/dist/cjs/providers/index.d.ts +13 -0
- package/dist/cjs/providers/index.d.ts.map +1 -0
- package/dist/cjs/providers/index.js +16 -0
- package/dist/cjs/providers/index.js.map +1 -0
- 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/ai.d.ts +28 -0
- package/dist/cjs/types/ai.d.ts.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 +35 -0
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +153 -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 +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/AnthropicProvider.d.ts +43 -0
- package/dist/providers/AnthropicProvider.d.ts.map +1 -0
- package/dist/providers/AnthropicProvider.js +321 -0
- package/dist/providers/AnthropicProvider.js.map +1 -0
- package/dist/providers/GeminiProvider.d.ts +4 -1
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +96 -0
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/OpenAIProvider.d.ts +4 -1
- package/dist/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/OpenAIProvider.js +115 -0
- package/dist/providers/OpenAIProvider.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts +4 -1
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/OpenRouterProvider.js +115 -0
- package/dist/providers/OpenRouterProvider.js.map +1 -1
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +9 -0
- package/dist/providers/index.js.map +1 -0
- 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/ai.d.ts +28 -0
- package/dist/types/ai.d.ts.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/API_REFERENCE.md +260 -2
- package/docs/PERSISTENCE.md +419 -0
- package/docs/PROVIDERS.md +139 -2
- package/examples/business-onboarding.ts +5 -4
- package/examples/declarative-agent.ts +1 -1
- package/examples/domain-scoping.ts +5 -4
- package/examples/healthcare-agent.ts +4 -4
- package/examples/openai-agent.ts +6 -4
- package/examples/prisma-persistence.ts +313 -0
- package/examples/prisma-schema.example.prisma +74 -0
- package/examples/rules-prohibitions.ts +4 -4
- package/examples/streaming-agent.ts +371 -0
- package/examples/travel-agent.ts +7 -4
- package/package.json +10 -1
- package/src/adapters/PrismaAdapter.ts +510 -0
- package/src/adapters/index.ts +10 -0
- package/src/core/Agent.ts +205 -0
- package/src/core/PersistenceManager.ts +222 -0
- package/src/index.ts +23 -0
- package/src/providers/AnthropicProvider.ts +467 -0
- package/src/providers/GeminiProvider.ts +135 -0
- package/src/providers/OpenAIProvider.ts +157 -0
- package/src/providers/OpenRouterProvider.ts +157 -0
- package/src/providers/index.ts +16 -0
- package/src/types/agent.ts +3 -0
- package/src/types/ai.ts +32 -0
- package/src/types/index.ts +14 -0
- package/src/types/persistence.ts +234 -0
package/docs/PROVIDERS.md
CHANGED
|
@@ -10,11 +10,111 @@ This guide covers the available AI providers and how to configure them for optim
|
|
|
10
10
|
- ✅ Configure provider-specific settings
|
|
11
11
|
- ✅ Use backup models for failover
|
|
12
12
|
- ✅ Customize retry logic and timeouts
|
|
13
|
+
- ⚡ **Stream responses in real-time** (all providers)
|
|
14
|
+
|
|
15
|
+
## Streaming Support
|
|
16
|
+
|
|
17
|
+
**All providers support real-time streaming** via the `respondStream()` method on the Agent class.
|
|
18
|
+
|
|
19
|
+
Streaming provides:
|
|
20
|
+
|
|
21
|
+
- 🌊 Real-time text generation for better UX
|
|
22
|
+
- 📊 Incremental delivery with `delta` and `accumulated` properties
|
|
23
|
+
- 🛑 Cancellable streams using AbortSignal
|
|
24
|
+
- ✅ Full compatibility with routes, states, and tool calls
|
|
25
|
+
|
|
26
|
+
**Example:**
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
for await (const chunk of agent.respondStream({ history })) {
|
|
30
|
+
process.stdout.write(chunk.delta); // Print incremental text
|
|
31
|
+
|
|
32
|
+
if (chunk.done) {
|
|
33
|
+
// Access final metadata
|
|
34
|
+
console.log("Route:", chunk.route?.title);
|
|
35
|
+
console.log("Tool calls:", chunk.toolCalls?.length);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
See [streaming-agent.ts](../examples/streaming-agent.ts) for comprehensive examples with all providers.
|
|
13
41
|
|
|
14
42
|
---
|
|
15
43
|
|
|
16
44
|
## Available Providers
|
|
17
45
|
|
|
46
|
+
### 🤖 Anthropic (Claude)
|
|
47
|
+
|
|
48
|
+
**Package:** `@anthropic-ai/sdk`
|
|
49
|
+
|
|
50
|
+
#### Overview
|
|
51
|
+
|
|
52
|
+
Anthropic's Claude models are known for their exceptional reasoning, analysis, and long context windows. Claude 3.5 Sonnet offers:
|
|
53
|
+
|
|
54
|
+
- State-of-the-art reasoning and analysis
|
|
55
|
+
- 200K context window
|
|
56
|
+
- Excellent at following complex instructions
|
|
57
|
+
- Strong coding and writing capabilities
|
|
58
|
+
|
|
59
|
+
#### Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
bun add @anthropic-ai/sdk
|
|
63
|
+
# or
|
|
64
|
+
npm install @anthropic-ai/sdk
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Basic Usage
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { AnthropicProvider } from "@falai/agent";
|
|
71
|
+
|
|
72
|
+
const provider = new AnthropicProvider({
|
|
73
|
+
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
74
|
+
model: "claude-sonnet-4-5", // Latest Claude 4.5 Sonnet
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Configuration Options
|
|
79
|
+
|
|
80
|
+
All models are specified by the user - see [Anthropic Models](https://docs.anthropic.com/en/docs/models-overview) for available options.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
const provider = new AnthropicProvider({
|
|
84
|
+
// Required
|
|
85
|
+
apiKey: string;
|
|
86
|
+
model: string; // e.g., "claude-sonnet-4-5", "claude-opus-4-1", etc.
|
|
87
|
+
|
|
88
|
+
// Optional
|
|
89
|
+
backupModels?: string[]; // Default: []
|
|
90
|
+
config?: Partial<Omit<MessageCreateParamsNonStreaming, "model" | "messages" | "max_tokens">>; // Uses @anthropic-ai/sdk types
|
|
91
|
+
retryConfig?: {
|
|
92
|
+
timeout?: number; // Default: 60000ms (60s)
|
|
93
|
+
retries?: number; // Default: 3
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### Example: Advanced Configuration
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const provider = new AnthropicProvider({
|
|
102
|
+
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
103
|
+
model: "claude-sonnet-4-5",
|
|
104
|
+
backupModels: ["claude-opus-4-1", "claude-sonnet-4-0"],
|
|
105
|
+
config: {
|
|
106
|
+
temperature: 0.7,
|
|
107
|
+
top_p: 0.9,
|
|
108
|
+
},
|
|
109
|
+
retryConfig: {
|
|
110
|
+
timeout: 45000,
|
|
111
|
+
retries: 2,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
18
118
|
### 🌐 OpenRouter (Multi-Model Access)
|
|
19
119
|
|
|
20
120
|
**Package:** `openai` (OpenRouter uses OpenAI-compatible API)
|
|
@@ -274,7 +374,22 @@ const provider = new OpenAIProvider({
|
|
|
274
374
|
You can easily switch between providers:
|
|
275
375
|
|
|
276
376
|
```typescript
|
|
277
|
-
import {
|
|
377
|
+
import {
|
|
378
|
+
Agent,
|
|
379
|
+
AnthropicProvider,
|
|
380
|
+
GeminiProvider,
|
|
381
|
+
OpenAIProvider,
|
|
382
|
+
OpenRouterProvider,
|
|
383
|
+
} from "@falai/agent";
|
|
384
|
+
|
|
385
|
+
// Use Anthropic (Claude)
|
|
386
|
+
const claudeAgent = new Agent({
|
|
387
|
+
name: "Claude Assistant",
|
|
388
|
+
ai: new AnthropicProvider({
|
|
389
|
+
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
390
|
+
model: "claude-sonnet-4-5",
|
|
391
|
+
}),
|
|
392
|
+
});
|
|
278
393
|
|
|
279
394
|
// Use Gemini
|
|
280
395
|
const geminiAgent = new Agent({
|
|
@@ -290,10 +405,20 @@ const openaiAgent = new Agent({
|
|
|
290
405
|
name: "OpenAI Assistant",
|
|
291
406
|
ai: new OpenAIProvider({
|
|
292
407
|
apiKey: process.env.OPENAI_API_KEY!,
|
|
408
|
+
model: "gpt-5",
|
|
293
409
|
}),
|
|
294
410
|
});
|
|
295
411
|
|
|
296
|
-
//
|
|
412
|
+
// Use OpenRouter (access to 200+ models)
|
|
413
|
+
const openrouterAgent = new Agent({
|
|
414
|
+
name: "OpenRouter Assistant",
|
|
415
|
+
ai: new OpenRouterProvider({
|
|
416
|
+
apiKey: process.env.OPENROUTER_API_KEY!,
|
|
417
|
+
model: "anthropic/claude-sonnet-4-5",
|
|
418
|
+
}),
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// All agents have the same interface!
|
|
297
422
|
```
|
|
298
423
|
|
|
299
424
|
---
|
|
@@ -304,8 +429,10 @@ It's recommended to store API keys in environment variables:
|
|
|
304
429
|
|
|
305
430
|
```bash
|
|
306
431
|
# .env
|
|
432
|
+
ANTHROPIC_API_KEY=your-anthropic-api-key-here
|
|
307
433
|
GEMINI_API_KEY=your-gemini-api-key-here
|
|
308
434
|
OPENAI_API_KEY=your-openai-api-key-here
|
|
435
|
+
OPENROUTER_API_KEY=your-openrouter-api-key-here
|
|
309
436
|
```
|
|
310
437
|
|
|
311
438
|
Then load them:
|
|
@@ -314,6 +441,11 @@ Then load them:
|
|
|
314
441
|
import { config } from "dotenv";
|
|
315
442
|
config();
|
|
316
443
|
|
|
444
|
+
const anthropicProvider = new AnthropicProvider({
|
|
445
|
+
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
446
|
+
model: "claude-sonnet-4-5",
|
|
447
|
+
});
|
|
448
|
+
|
|
317
449
|
const geminiProvider = new GeminiProvider({
|
|
318
450
|
apiKey: process.env.GEMINI_API_KEY!,
|
|
319
451
|
model: "models/gemini-2.5-flash",
|
|
@@ -323,6 +455,11 @@ const openaiProvider = new OpenAIProvider({
|
|
|
323
455
|
apiKey: process.env.OPENAI_API_KEY!,
|
|
324
456
|
model: "gpt-5",
|
|
325
457
|
});
|
|
458
|
+
|
|
459
|
+
const openrouterProvider = new OpenRouterProvider({
|
|
460
|
+
apiKey: process.env.OPENROUTER_API_KEY!,
|
|
461
|
+
model: "anthropic/claude-sonnet-4-5",
|
|
462
|
+
});
|
|
326
463
|
```
|
|
327
464
|
|
|
328
465
|
---
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
import {
|
|
14
14
|
Agent,
|
|
15
15
|
defineTool,
|
|
16
|
-
GeminiProvider,
|
|
17
16
|
END_ROUTE,
|
|
18
17
|
EventSource,
|
|
19
18
|
createMessageEvent,
|
|
19
|
+
OpenAIProvider,
|
|
20
20
|
type ToolContext,
|
|
21
21
|
} from "../src/index";
|
|
22
22
|
|
|
@@ -362,9 +362,10 @@ async function createBusinessOnboardingAgent(
|
|
|
362
362
|
sessionId: string,
|
|
363
363
|
initialData: OnboardingData = { routes: [] }
|
|
364
364
|
): Promise<Agent<OnboardingContext>> {
|
|
365
|
-
const provider = new
|
|
366
|
-
apiKey: process.env.
|
|
367
|
-
model: "
|
|
365
|
+
const provider = new OpenAIProvider({
|
|
366
|
+
apiKey: process.env.OPENAI_API_KEY || "test-key",
|
|
367
|
+
model: "gpt-5",
|
|
368
|
+
backupModels: ["gpt-5-mini"],
|
|
368
369
|
retryConfig: {
|
|
369
370
|
timeout: 60000,
|
|
370
371
|
retries: 3,
|
|
@@ -6,12 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Agent, createMessageEvent, EventSource } from "../src/index";
|
|
9
|
-
import {
|
|
9
|
+
import { OpenRouterProvider } from "../src/providers";
|
|
10
10
|
|
|
11
11
|
// Initialize AI provider
|
|
12
|
-
const ai = new
|
|
13
|
-
apiKey: process.env.
|
|
14
|
-
model: "gemini-2.0-flash-exp",
|
|
12
|
+
const ai = new OpenRouterProvider({
|
|
13
|
+
apiKey: process.env.OPENROUTER_API_KEY || "your-api-key-here",
|
|
14
|
+
model: "google/gemini-2.0-flash-exp",
|
|
15
|
+
backupModels: ["anthropic/claude-sonnet-4-5"],
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
// Create agent
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import {
|
|
6
6
|
Agent,
|
|
7
7
|
defineTool,
|
|
8
|
-
|
|
8
|
+
AnthropicProvider,
|
|
9
9
|
END_ROUTE,
|
|
10
10
|
EventSource,
|
|
11
11
|
createMessageEvent,
|
|
@@ -63,9 +63,9 @@ const getLabResults = defineTool<HealthcareContext, [], object>(
|
|
|
63
63
|
);
|
|
64
64
|
|
|
65
65
|
async function createHealthcareAgent() {
|
|
66
|
-
const provider = new
|
|
67
|
-
apiKey: process.env.
|
|
68
|
-
model: "
|
|
66
|
+
const provider = new AnthropicProvider({
|
|
67
|
+
apiKey: process.env.ANTHROPIC_API_KEY || "test-key",
|
|
68
|
+
model: "claude-sonnet-4-5",
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
const agent = new Agent<HealthcareContext>({
|
package/examples/openai-agent.ts
CHANGED
|
@@ -108,10 +108,12 @@ async function main() {
|
|
|
108
108
|
chatState: "Ask which city they want weather for",
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
-
const fetchWeather = askLocation.transitionTo(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
const fetchWeather = askLocation.transitionTo(
|
|
112
|
+
{
|
|
113
|
+
toolState: getWeather,
|
|
114
|
+
},
|
|
115
|
+
"User provides a city name"
|
|
116
|
+
);
|
|
115
117
|
|
|
116
118
|
const showWeather = fetchWeather.transitionTo({
|
|
117
119
|
chatState:
|
|
@@ -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
|
+
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { Agent, createMessageEvent, EventSource } from "../src/index";
|
|
9
|
-
import {
|
|
9
|
+
import { AnthropicProvider } from "../src/providers";
|
|
10
10
|
|
|
11
11
|
// Initialize AI provider
|
|
12
|
-
const ai = new
|
|
13
|
-
apiKey: process.env.
|
|
14
|
-
model: "
|
|
12
|
+
const ai = new AnthropicProvider({
|
|
13
|
+
apiKey: process.env.ANTHROPIC_API_KEY || "your-api-key-here",
|
|
14
|
+
model: "claude-sonnet-4-5",
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
// Create WhatsApp support bot with different styles per route
|