@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/API_REFERENCE.md
CHANGED
|
@@ -109,6 +109,107 @@ if (response.route?.title === END_ROUTE) {
|
|
|
109
109
|
}
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
##### `respondStream(input: RespondInput<TContext>): AsyncGenerator<StreamChunk>`
|
|
113
|
+
|
|
114
|
+
Generates an AI response as a real-time stream for better user experience. Provides the same structured output as `respond()` but delivers it incrementally.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
interface StreamChunk {
|
|
118
|
+
/** The incremental text delta */
|
|
119
|
+
delta: string;
|
|
120
|
+
/** Full accumulated text so far */
|
|
121
|
+
accumulated: string;
|
|
122
|
+
/** Whether this is the final chunk */
|
|
123
|
+
done: boolean;
|
|
124
|
+
/** Route chosen by the agent (only in final chunk) */
|
|
125
|
+
route?: { id: string; title: string };
|
|
126
|
+
/** Current state within the route (only in final chunk) */
|
|
127
|
+
state?: { id: string; description?: string };
|
|
128
|
+
/** Tool calls requested by the agent (only in final chunk) */
|
|
129
|
+
toolCalls?: Array<{
|
|
130
|
+
toolName: string;
|
|
131
|
+
arguments: Record<string, unknown>;
|
|
132
|
+
}>;
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Key Features:**
|
|
137
|
+
|
|
138
|
+
- 🌊 Real-time streaming for better perceived performance
|
|
139
|
+
- 📊 Access to route, state, and tool information in final chunk
|
|
140
|
+
- 🛑 Cancellable with AbortSignal
|
|
141
|
+
- ✅ Supported by all providers (Anthropic, OpenAI, Gemini, OpenRouter)
|
|
142
|
+
|
|
143
|
+
**Example:**
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// Basic streaming
|
|
147
|
+
for await (const chunk of agent.respondStream({ history })) {
|
|
148
|
+
if (chunk.delta) {
|
|
149
|
+
// Display incremental text to user
|
|
150
|
+
process.stdout.write(chunk.delta);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (chunk.done) {
|
|
154
|
+
console.log("\n✅ Complete!");
|
|
155
|
+
// Access final metadata
|
|
156
|
+
if (chunk.route) {
|
|
157
|
+
console.log("Route:", chunk.route.title);
|
|
158
|
+
}
|
|
159
|
+
if (chunk.toolCalls) {
|
|
160
|
+
console.log("Tool calls:", chunk.toolCalls.length);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**With Cancellation:**
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const abortController = new AbortController();
|
|
170
|
+
|
|
171
|
+
// Cancel after 5 seconds
|
|
172
|
+
setTimeout(() => abortController.abort(), 5000);
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
for await (const chunk of agent.respondStream({
|
|
176
|
+
history,
|
|
177
|
+
signal: abortController.signal,
|
|
178
|
+
})) {
|
|
179
|
+
console.log(chunk.delta);
|
|
180
|
+
}
|
|
181
|
+
} catch (error) {
|
|
182
|
+
if (error.name === "AbortError") {
|
|
183
|
+
console.log("Stream cancelled");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Collecting Full Response:**
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
let fullMessage = "";
|
|
192
|
+
let finalChunk;
|
|
193
|
+
|
|
194
|
+
for await (const chunk of agent.respondStream({ history })) {
|
|
195
|
+
fullMessage += chunk.delta;
|
|
196
|
+
if (chunk.done) {
|
|
197
|
+
finalChunk = chunk;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Save to database
|
|
202
|
+
await db.agentMessages.create({
|
|
203
|
+
sessionId: session.id,
|
|
204
|
+
role: "agent",
|
|
205
|
+
content: fullMessage,
|
|
206
|
+
route: finalChunk.route?.title,
|
|
207
|
+
toolCalls: finalChunk.toolCalls || [],
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**See Also:** [streaming-agent.ts](../examples/streaming-agent.ts) for comprehensive examples.
|
|
212
|
+
|
|
112
213
|
#### Properties
|
|
113
214
|
|
|
114
215
|
##### `name: string`
|
|
@@ -366,11 +467,15 @@ Returns filtered domains by names. If `allowedNames` is `undefined`, returns all
|
|
|
366
467
|
const registry = new DomainRegistry();
|
|
367
468
|
|
|
368
469
|
registry.register("payment", {
|
|
369
|
-
processPayment: async (amount: number) => {
|
|
470
|
+
processPayment: async (amount: number) => {
|
|
471
|
+
/* ... */
|
|
472
|
+
},
|
|
370
473
|
});
|
|
371
474
|
|
|
372
475
|
registry.register("shipping", {
|
|
373
|
-
calculateShipping: (zipCode: string) => {
|
|
476
|
+
calculateShipping: (zipCode: string) => {
|
|
477
|
+
/* ... */
|
|
478
|
+
},
|
|
374
479
|
});
|
|
375
480
|
|
|
376
481
|
// Get specific domains
|
|
@@ -386,6 +491,61 @@ Returns list of all registered domain names.
|
|
|
386
491
|
|
|
387
492
|
---
|
|
388
493
|
|
|
494
|
+
### `AnthropicProvider`
|
|
495
|
+
|
|
496
|
+
AI provider implementation for Anthropic (Claude models).
|
|
497
|
+
|
|
498
|
+
#### Constructor
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
new AnthropicProvider(options: AnthropicProviderOptions)
|
|
502
|
+
|
|
503
|
+
interface AnthropicProviderOptions {
|
|
504
|
+
apiKey: string;
|
|
505
|
+
model: string; // Required: e.g., "claude-sonnet-4-5"
|
|
506
|
+
backupModels?: string[]; // Fallback models
|
|
507
|
+
config?: Partial<Omit<MessageCreateParamsNonStreaming, "model" | "messages" | "max_tokens">>;
|
|
508
|
+
retryConfig?: {
|
|
509
|
+
timeout?: number; // Default: 60000ms
|
|
510
|
+
retries?: number; // Default: 3
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Note:** `model` is required for AnthropicProvider. Available models include:
|
|
516
|
+
|
|
517
|
+
- `claude-sonnet-4-5` - Latest Claude Sonnet 4.5 (most capable)
|
|
518
|
+
- `claude-opus-4-1` - Claude Opus 4.1 (powerful for complex tasks)
|
|
519
|
+
- `claude-sonnet-4-0` - Claude Sonnet 4.0 (stable, production-ready)
|
|
520
|
+
|
|
521
|
+
#### Methods
|
|
522
|
+
|
|
523
|
+
##### `generateMessage<TContext>(input: GenerateMessageInput<TContext>): Promise<GenerateMessageOutput>`
|
|
524
|
+
|
|
525
|
+
Generates a message with retry logic and backup models using Anthropic's API.
|
|
526
|
+
|
|
527
|
+
**Example:**
|
|
528
|
+
|
|
529
|
+
```typescript
|
|
530
|
+
import { AnthropicProvider } from "@falai/agent";
|
|
531
|
+
|
|
532
|
+
const provider = new AnthropicProvider({
|
|
533
|
+
apiKey: process.env.ANTHROPIC_API_KEY!,
|
|
534
|
+
model: "claude-sonnet-4-5",
|
|
535
|
+
backupModels: ["claude-opus-4-1", "claude-sonnet-4-0"],
|
|
536
|
+
config: {
|
|
537
|
+
temperature: 0.7,
|
|
538
|
+
top_p: 0.9,
|
|
539
|
+
},
|
|
540
|
+
retryConfig: {
|
|
541
|
+
timeout: 60000,
|
|
542
|
+
retries: 3,
|
|
543
|
+
},
|
|
544
|
+
});
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
---
|
|
548
|
+
|
|
389
549
|
### `GeminiProvider`
|
|
390
550
|
|
|
391
551
|
AI provider implementation for Google Gemini.
|
|
@@ -414,6 +574,104 @@ Generates a message with retry logic and backup models.
|
|
|
414
574
|
|
|
415
575
|
---
|
|
416
576
|
|
|
577
|
+
### `OpenAIProvider`
|
|
578
|
+
|
|
579
|
+
AI provider implementation for OpenAI (GPT models).
|
|
580
|
+
|
|
581
|
+
#### Constructor
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
new OpenAIProvider(options: OpenAIProviderOptions)
|
|
585
|
+
|
|
586
|
+
interface OpenAIProviderOptions {
|
|
587
|
+
apiKey: string;
|
|
588
|
+
model: string; // Required: e.g., "gpt-4o", "gpt-5"
|
|
589
|
+
backupModels?: string[]; // Fallback models
|
|
590
|
+
retryConfig?: {
|
|
591
|
+
timeout?: number; // Default: 60000ms
|
|
592
|
+
retries?: number; // Default: 3
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
**Note:** Unlike GeminiProvider, `model` is required for OpenAIProvider. Choose from available OpenAI models like "gpt-4o", "gpt-5", "gpt-4-turbo", etc.
|
|
598
|
+
|
|
599
|
+
#### Methods
|
|
600
|
+
|
|
601
|
+
##### `generateMessage<TContext>(input: GenerateMessageInput<TContext>): Promise<GenerateMessageOutput>`
|
|
602
|
+
|
|
603
|
+
Generates a message with retry logic and backup models using OpenAI's API.
|
|
604
|
+
|
|
605
|
+
**Example:**
|
|
606
|
+
|
|
607
|
+
```typescript
|
|
608
|
+
import { OpenAIProvider } from "@falai/agent";
|
|
609
|
+
|
|
610
|
+
const provider = new OpenAIProvider({
|
|
611
|
+
apiKey: process.env.OPENAI_API_KEY!,
|
|
612
|
+
model: "gpt-5",
|
|
613
|
+
backupModels: ["gpt-4o", "gpt-4-turbo"],
|
|
614
|
+
retryConfig: {
|
|
615
|
+
timeout: 60000,
|
|
616
|
+
retries: 3,
|
|
617
|
+
},
|
|
618
|
+
});
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
---
|
|
622
|
+
|
|
623
|
+
### `OpenRouterProvider`
|
|
624
|
+
|
|
625
|
+
AI provider implementation for OpenRouter (access to 200+ models).
|
|
626
|
+
|
|
627
|
+
#### Constructor
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
new OpenRouterProvider(options: OpenRouterProviderOptions)
|
|
631
|
+
|
|
632
|
+
interface OpenRouterProviderOptions {
|
|
633
|
+
apiKey: string;
|
|
634
|
+
model: string; // Required: e.g., "openai/gpt-5", "anthropic/claude-sonnet-4-5"
|
|
635
|
+
backupModels?: string[]; // Fallback models
|
|
636
|
+
siteUrl?: string; // Optional: your app URL for OpenRouter rankings
|
|
637
|
+
siteName?: string; // Optional: your app name for OpenRouter rankings
|
|
638
|
+
retryConfig?: {
|
|
639
|
+
timeout?: number; // Default: 60000ms
|
|
640
|
+
retries?: number; // Default: 3
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
**Note:** OpenRouter provides access to models from multiple providers. Model names follow the format `provider/model-name` (e.g., "openai/gpt-5", "anthropic/claude-sonnet-4-5", "google/gemini-pro").
|
|
646
|
+
|
|
647
|
+
#### Methods
|
|
648
|
+
|
|
649
|
+
##### `generateMessage<TContext>(input: GenerateMessageInput<TContext>): Promise<GenerateMessageOutput>`
|
|
650
|
+
|
|
651
|
+
Generates a message with retry logic and backup models using OpenRouter's API.
|
|
652
|
+
|
|
653
|
+
**Example:**
|
|
654
|
+
|
|
655
|
+
```typescript
|
|
656
|
+
import { OpenRouterProvider } from "@falai/agent";
|
|
657
|
+
|
|
658
|
+
const provider = new OpenRouterProvider({
|
|
659
|
+
apiKey: process.env.OPENROUTER_API_KEY!,
|
|
660
|
+
model: "openai/gpt-5",
|
|
661
|
+
backupModels: ["anthropic/claude-sonnet-4-5", "google/gemini-pro"],
|
|
662
|
+
siteUrl: "https://yourapp.com",
|
|
663
|
+
siteName: "Your App Name",
|
|
664
|
+
retryConfig: {
|
|
665
|
+
timeout: 60000,
|
|
666
|
+
retries: 3,
|
|
667
|
+
},
|
|
668
|
+
});
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
**See Also:** [Providers Guide](./PROVIDERS.md) for detailed provider comparison and configuration examples.
|
|
672
|
+
|
|
673
|
+
---
|
|
674
|
+
|
|
417
675
|
### `PromptBuilder`
|
|
418
676
|
|
|
419
677
|
Constructs prompts for AI generation.
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# Persistence with @falai/agent
|
|
2
|
+
|
|
3
|
+
The `@falai/agent` framework provides optional, flexible persistence for automatically saving conversation sessions and messages to your database.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Optional** - Persistence is completely optional
|
|
8
|
+
- ✅ **Provider Pattern** - Simple API like AI providers (`new PrismaAdapter({ prisma })`)
|
|
9
|
+
- ✅ **Type-safe** - Full TypeScript support
|
|
10
|
+
- ✅ **Prisma Ready** - Built-in Prisma ORM adapter
|
|
11
|
+
- ✅ **Extensible** - Create adapters for any database
|
|
12
|
+
- ✅ **Auto-save** - Automatic message persistence
|
|
13
|
+
|
|
14
|
+
## Quick Start with Prisma
|
|
15
|
+
|
|
16
|
+
### 1. Install Dependencies
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @prisma/client prisma
|
|
20
|
+
npx prisma init
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 2. Set Up Schema
|
|
24
|
+
|
|
25
|
+
Copy this schema to `prisma/schema.prisma`:
|
|
26
|
+
|
|
27
|
+
```prisma
|
|
28
|
+
datasource db {
|
|
29
|
+
provider = "postgresql"
|
|
30
|
+
url = env("DATABASE_URL")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
generator client {
|
|
34
|
+
provider = "prisma-client-js"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
model AgentSession {
|
|
38
|
+
id String @id @default(cuid())
|
|
39
|
+
userId String? @map("user_id")
|
|
40
|
+
agentName String? @map("agent_name")
|
|
41
|
+
status String @default("active")
|
|
42
|
+
currentRoute String? @map("current_route")
|
|
43
|
+
currentState String? @map("current_state")
|
|
44
|
+
collectedData Json? @map("collected_data")
|
|
45
|
+
messageCount Int @default(0) @map("message_count")
|
|
46
|
+
lastMessageAt DateTime? @map("last_message_at")
|
|
47
|
+
completedAt DateTime? @map("completed_at")
|
|
48
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
49
|
+
updatedAt DateTime @updatedAt @map("updated_at")
|
|
50
|
+
|
|
51
|
+
messages AgentMessage[]
|
|
52
|
+
|
|
53
|
+
@@index([userId, status])
|
|
54
|
+
@@map("agent_sessions")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
model AgentMessage {
|
|
58
|
+
id String @id @default(cuid())
|
|
59
|
+
sessionId String @map("session_id")
|
|
60
|
+
userId String? @map("user_id")
|
|
61
|
+
role String
|
|
62
|
+
content String @db.Text
|
|
63
|
+
route String?
|
|
64
|
+
state String?
|
|
65
|
+
toolCalls Json? @map("tool_calls")
|
|
66
|
+
event Json?
|
|
67
|
+
createdAt DateTime @default(now()) @map("created_at")
|
|
68
|
+
|
|
69
|
+
session AgentSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
|
|
70
|
+
|
|
71
|
+
@@index([sessionId])
|
|
72
|
+
@@map("agent_messages")
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. Generate and Migrate
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npx prisma generate
|
|
80
|
+
npx prisma migrate dev --name init
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 4. Use in Your Agent (That's it!)
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { Agent, PrismaAdapter, GeminiProvider } from "@falai/agent";
|
|
87
|
+
import { PrismaClient } from "@prisma/client";
|
|
88
|
+
|
|
89
|
+
const prisma = new PrismaClient();
|
|
90
|
+
|
|
91
|
+
const agent = new Agent({
|
|
92
|
+
name: "My Agent",
|
|
93
|
+
ai: new GeminiProvider({ apiKey: "..." }),
|
|
94
|
+
// ✨ Just add this!
|
|
95
|
+
persistence: {
|
|
96
|
+
adapter: new PrismaAdapter({ prisma }),
|
|
97
|
+
userId: "user_123",
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Access persistence methods
|
|
102
|
+
const persistence = agent.getPersistenceManager();
|
|
103
|
+
|
|
104
|
+
// Create a session
|
|
105
|
+
const session = await persistence.createSession({
|
|
106
|
+
userId: "user_123",
|
|
107
|
+
agentName: "My Agent",
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Load history
|
|
111
|
+
const history = await persistence.loadSessionHistory(session.id);
|
|
112
|
+
|
|
113
|
+
// Generate response
|
|
114
|
+
const response = await agent.respond({ history });
|
|
115
|
+
|
|
116
|
+
// Save message (auto-saved if configured)
|
|
117
|
+
await persistence.saveMessage({
|
|
118
|
+
sessionId: session.id,
|
|
119
|
+
role: "agent",
|
|
120
|
+
content: response.message,
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Configuration Options
|
|
125
|
+
|
|
126
|
+
### Basic Configuration
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
new PrismaAdapter({
|
|
130
|
+
prisma: prismaClient, // Required: Your Prisma client
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Custom Table Names
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
new PrismaAdapter({
|
|
138
|
+
prisma,
|
|
139
|
+
tables: {
|
|
140
|
+
sessions: "myCustomSessions",
|
|
141
|
+
messages: "myCustomMessages",
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Custom Field Mappings
|
|
147
|
+
|
|
148
|
+
If your database uses different field names:
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
new PrismaAdapter({
|
|
152
|
+
prisma,
|
|
153
|
+
fieldMappings: {
|
|
154
|
+
sessions: {
|
|
155
|
+
userId: "user_id",
|
|
156
|
+
createdAt: "created_at",
|
|
157
|
+
updatedAt: "updated_at",
|
|
158
|
+
},
|
|
159
|
+
messages: {
|
|
160
|
+
sessionId: "session_id",
|
|
161
|
+
createdAt: "created_at",
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Auto-Save Messages
|
|
168
|
+
|
|
169
|
+
Enable automatic message persistence:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const agent = new Agent({
|
|
173
|
+
name: "My Agent",
|
|
174
|
+
ai: provider,
|
|
175
|
+
persistence: {
|
|
176
|
+
adapter: new PrismaAdapter({ prisma }),
|
|
177
|
+
autoSave: true, // Default: true
|
|
178
|
+
userId: "user_123",
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Using with Lifecycle Hooks
|
|
184
|
+
|
|
185
|
+
The most powerful pattern - auto-sync context with database:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const agent = new Agent({
|
|
189
|
+
name: "Smart Assistant",
|
|
190
|
+
ai: provider,
|
|
191
|
+
context: {
|
|
192
|
+
userId: "user_123",
|
|
193
|
+
sessionId: session.id,
|
|
194
|
+
preferences: { theme: "light" },
|
|
195
|
+
},
|
|
196
|
+
hooks: {
|
|
197
|
+
// Load fresh context before each response
|
|
198
|
+
beforeRespond: async (ctx) => {
|
|
199
|
+
const persistence = agent.getPersistenceManager();
|
|
200
|
+
const session = await persistence?.getSession(ctx.sessionId);
|
|
201
|
+
return {
|
|
202
|
+
...ctx,
|
|
203
|
+
preferences: session?.collectedData?.preferences || ctx.preferences,
|
|
204
|
+
};
|
|
205
|
+
},
|
|
206
|
+
// Auto-save context updates
|
|
207
|
+
onContextUpdate: async (ctx) => {
|
|
208
|
+
const persistence = agent.getPersistenceManager();
|
|
209
|
+
await persistence?.updateCollectedData(ctx.sessionId, {
|
|
210
|
+
preferences: ctx.preferences,
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
persistence: {
|
|
215
|
+
adapter: new PrismaAdapter({ prisma }),
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Now context updates are automatically persisted!
|
|
220
|
+
await agent.updateContext({ preferences: { theme: "dark" } });
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## PersistenceManager API
|
|
224
|
+
|
|
225
|
+
Access via `agent.getPersistenceManager()`:
|
|
226
|
+
|
|
227
|
+
### Session Methods
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
// Create session
|
|
231
|
+
await persistence.createSession({
|
|
232
|
+
userId: "user_123",
|
|
233
|
+
agentName: "My Agent",
|
|
234
|
+
initialData: { key: "value" },
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Get session
|
|
238
|
+
await persistence.getSession(sessionId);
|
|
239
|
+
|
|
240
|
+
// Find active session for user
|
|
241
|
+
await persistence.findActiveSession(userId);
|
|
242
|
+
|
|
243
|
+
// Get all user sessions
|
|
244
|
+
await persistence.getUserSessions(userId);
|
|
245
|
+
|
|
246
|
+
// Update session
|
|
247
|
+
await persistence.updateSessionStatus(sessionId, "completed");
|
|
248
|
+
await persistence.updateCollectedData(sessionId, { key: "value" });
|
|
249
|
+
await persistence.updateRouteState(sessionId, routeId, stateId);
|
|
250
|
+
|
|
251
|
+
// Complete/abandon session
|
|
252
|
+
await persistence.completeSession(sessionId);
|
|
253
|
+
await persistence.abandonSession(sessionId);
|
|
254
|
+
|
|
255
|
+
// Delete session
|
|
256
|
+
await persistence.deleteSession(sessionId);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Message Methods
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// Save message
|
|
263
|
+
await persistence.saveMessage({
|
|
264
|
+
sessionId: session.id,
|
|
265
|
+
userId: "user_123",
|
|
266
|
+
role: "user" | "agent" | "system",
|
|
267
|
+
content: "Hello!",
|
|
268
|
+
route: "route_id",
|
|
269
|
+
state: "state_id",
|
|
270
|
+
toolCalls: [...],
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Get messages
|
|
274
|
+
await persistence.getSessionMessages(sessionId);
|
|
275
|
+
await persistence.getUserMessages(userId);
|
|
276
|
+
|
|
277
|
+
// Load as Event history
|
|
278
|
+
await persistence.loadSessionHistory(sessionId);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
## Creating Custom Adapters
|
|
282
|
+
|
|
283
|
+
Create adapters for any database:
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
import { PersistenceAdapter, SessionRepository, MessageRepository } from "@falai/agent";
|
|
287
|
+
|
|
288
|
+
class MyDatabaseAdapter implements PersistenceAdapter {
|
|
289
|
+
public readonly sessionRepository: SessionRepository;
|
|
290
|
+
public readonly messageRepository: MessageRepository;
|
|
291
|
+
|
|
292
|
+
constructor(config: MyConfig) {
|
|
293
|
+
this.sessionRepository = new MySessionRepository(config);
|
|
294
|
+
this.messageRepository = new MyMessageRepository(config);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async initialize?(): Promise<void> {
|
|
298
|
+
// Optional: Setup tables/indexes
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async disconnect?(): Promise<void> {
|
|
302
|
+
// Optional: Cleanup
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Use it
|
|
307
|
+
const agent = new Agent({
|
|
308
|
+
persistence: {
|
|
309
|
+
adapter: new MyDatabaseAdapter({ ... }),
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Examples
|
|
315
|
+
|
|
316
|
+
### Complete Example
|
|
317
|
+
|
|
318
|
+
See `examples/prisma-persistence.ts` for a full working example.
|
|
319
|
+
|
|
320
|
+
### Minimal Example
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
import { Agent, PrismaAdapter } from "@falai/agent";
|
|
324
|
+
import { PrismaClient } from "@prisma/client";
|
|
325
|
+
|
|
326
|
+
const prisma = new PrismaClient();
|
|
327
|
+
const agent = new Agent({
|
|
328
|
+
name: "Assistant",
|
|
329
|
+
ai: provider,
|
|
330
|
+
persistence: {
|
|
331
|
+
adapter: new PrismaAdapter({ prisma }),
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const persistence = agent.getPersistenceManager();
|
|
336
|
+
const session = await persistence.createSession({ userId: "user_123" });
|
|
337
|
+
const history = await persistence.loadSessionHistory(session.id);
|
|
338
|
+
const response = await agent.respond({ history });
|
|
339
|
+
await persistence.saveMessage({
|
|
340
|
+
sessionId: session.id,
|
|
341
|
+
role: "agent",
|
|
342
|
+
content: response.message,
|
|
343
|
+
});
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Database Schema Details
|
|
347
|
+
|
|
348
|
+
### SessionData Fields
|
|
349
|
+
|
|
350
|
+
- `id`: Unique session identifier
|
|
351
|
+
- `userId`: Optional user identifier
|
|
352
|
+
- `agentName`: Name of the agent
|
|
353
|
+
- `status`: `"active" | "completed" | "abandoned"`
|
|
354
|
+
- `currentRoute`: Current route ID
|
|
355
|
+
- `currentState`: Current state ID
|
|
356
|
+
- `collectedData`: JSON object for custom data
|
|
357
|
+
- `messageCount`: Number of messages in session
|
|
358
|
+
- `lastMessageAt`: Timestamp of last message
|
|
359
|
+
- `completedAt`: When session was completed
|
|
360
|
+
- `createdAt`: Session creation time
|
|
361
|
+
- `updatedAt`: Last update time
|
|
362
|
+
|
|
363
|
+
### MessageData Fields
|
|
364
|
+
|
|
365
|
+
- `id`: Unique message identifier
|
|
366
|
+
- `sessionId`: Reference to session
|
|
367
|
+
- `userId`: Optional user identifier
|
|
368
|
+
- `role`: `"user" | "agent" | "system"`
|
|
369
|
+
- `content`: Message text
|
|
370
|
+
- `route`: Route ID when message was sent
|
|
371
|
+
- `state`: State ID when message was sent
|
|
372
|
+
- `toolCalls`: Array of tool calls (if any)
|
|
373
|
+
- `event`: Full event data (optional)
|
|
374
|
+
- `createdAt`: Message creation time
|
|
375
|
+
|
|
376
|
+
## Best Practices
|
|
377
|
+
|
|
378
|
+
1. ✅ Use lifecycle hooks for automatic context persistence
|
|
379
|
+
2. ✅ Enable `autoSave` to track message counts automatically
|
|
380
|
+
3. ✅ Store minimal data in `collectedData`
|
|
381
|
+
4. ✅ Index frequently queried fields
|
|
382
|
+
5. ✅ Use cascading deletes for cleanup
|
|
383
|
+
6. ✅ Handle errors gracefully
|
|
384
|
+
7. ✅ Complete or abandon sessions when done
|
|
385
|
+
|
|
386
|
+
## Troubleshooting
|
|
387
|
+
|
|
388
|
+
### "Cannot find module '@prisma/client'"
|
|
389
|
+
|
|
390
|
+
Install Prisma:
|
|
391
|
+
|
|
392
|
+
```bash
|
|
393
|
+
npm install @prisma/client prisma
|
|
394
|
+
npx prisma generate
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### "Table not found"
|
|
398
|
+
|
|
399
|
+
Run migrations:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
npx prisma migrate dev
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Custom schema not working
|
|
406
|
+
|
|
407
|
+
Check your field mappings match your actual database schema.
|
|
408
|
+
|
|
409
|
+
## Other Databases
|
|
410
|
+
|
|
411
|
+
The adapter pattern works with any database. Examples:
|
|
412
|
+
|
|
413
|
+
- **MongoDB**: Create `MongoAdapter`
|
|
414
|
+
- **PostgreSQL (raw)**: Create `PostgresAdapter`
|
|
415
|
+
- **MySQL**: Create `MySQLAdapter`
|
|
416
|
+
- **Redis**: Create `RedisAdapter`
|
|
417
|
+
- **Elasticsearch**: Create `ElasticsearchAdapter`
|
|
418
|
+
|
|
419
|
+
Just implement the `PersistenceAdapter` interface!
|