@falai/agent 0.9.0-alpha-1 → 0.9.0
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 +34 -22
- package/dist/cjs/src/core/Agent.d.ts +77 -59
- package/dist/cjs/src/core/Agent.d.ts.map +1 -1
- package/dist/cjs/src/core/Agent.js +284 -1060
- package/dist/cjs/src/core/Agent.js.map +1 -1
- package/dist/cjs/src/core/PersistenceManager.d.ts.map +1 -1
- package/dist/cjs/src/core/PersistenceManager.js +48 -25
- package/dist/cjs/src/core/PersistenceManager.js.map +1 -1
- package/dist/cjs/src/core/PromptComposer.d.ts +1 -1
- package/dist/cjs/src/core/PromptComposer.d.ts.map +1 -1
- package/dist/cjs/src/core/PromptComposer.js.map +1 -1
- package/dist/cjs/src/core/ResponseEngine.d.ts +13 -12
- package/dist/cjs/src/core/ResponseEngine.d.ts.map +1 -1
- package/dist/cjs/src/core/ResponseEngine.js +4 -4
- package/dist/cjs/src/core/ResponseEngine.js.map +1 -1
- package/dist/cjs/src/core/ResponseModal.d.ts +205 -0
- package/dist/cjs/src/core/ResponseModal.d.ts.map +1 -0
- package/dist/cjs/src/core/ResponseModal.js +1328 -0
- package/dist/cjs/src/core/ResponseModal.js.map +1 -0
- package/dist/cjs/src/core/ResponsePipeline.d.ts +66 -38
- package/dist/cjs/src/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/cjs/src/core/ResponsePipeline.js +72 -4
- package/dist/cjs/src/core/ResponsePipeline.js.map +1 -1
- package/dist/cjs/src/core/Route.d.ts +24 -5
- package/dist/cjs/src/core/Route.d.ts.map +1 -1
- package/dist/cjs/src/core/Route.js +45 -1
- package/dist/cjs/src/core/Route.js.map +1 -1
- package/dist/cjs/src/core/RoutingEngine.d.ts +31 -6
- package/dist/cjs/src/core/RoutingEngine.d.ts.map +1 -1
- package/dist/cjs/src/core/RoutingEngine.js +113 -9
- package/dist/cjs/src/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/src/core/SessionManager.d.ts +14 -4
- package/dist/cjs/src/core/SessionManager.d.ts.map +1 -1
- package/dist/cjs/src/core/SessionManager.js +25 -5
- package/dist/cjs/src/core/SessionManager.js.map +1 -1
- package/dist/cjs/src/core/Step.d.ts +10 -10
- package/dist/cjs/src/core/Step.d.ts.map +1 -1
- package/dist/cjs/src/core/Step.js.map +1 -1
- package/dist/cjs/src/core/ToolExecutor.d.ts +4 -2
- package/dist/cjs/src/core/ToolExecutor.d.ts.map +1 -1
- package/dist/cjs/src/core/ToolExecutor.js +13 -3
- package/dist/cjs/src/core/ToolExecutor.js.map +1 -1
- package/dist/cjs/src/index.d.ts +3 -1
- package/dist/cjs/src/index.d.ts.map +1 -1
- package/dist/cjs/src/index.js +7 -1
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/types/agent.d.ts +42 -21
- package/dist/cjs/src/types/agent.d.ts.map +1 -1
- package/dist/cjs/src/types/agent.js.map +1 -1
- package/dist/cjs/src/types/ai.d.ts +1 -1
- package/dist/cjs/src/types/ai.d.ts.map +1 -1
- package/dist/cjs/src/types/index.d.ts +1 -1
- package/dist/cjs/src/types/index.d.ts.map +1 -1
- package/dist/cjs/src/types/index.js.map +1 -1
- package/dist/cjs/src/types/persistence.d.ts +0 -1
- package/dist/cjs/src/types/persistence.d.ts.map +1 -1
- package/dist/cjs/src/types/route.d.ts +22 -16
- package/dist/cjs/src/types/route.d.ts.map +1 -1
- package/dist/cjs/src/types/session.d.ts +6 -11
- package/dist/cjs/src/types/session.d.ts.map +1 -1
- package/dist/cjs/src/types/tool.d.ts +12 -6
- package/dist/cjs/src/types/tool.d.ts.map +1 -1
- package/dist/cjs/src/utils/clone.d.ts.map +1 -1
- package/dist/cjs/src/utils/clone.js +0 -4
- package/dist/cjs/src/utils/clone.js.map +1 -1
- package/dist/cjs/src/utils/history.d.ts +30 -1
- package/dist/cjs/src/utils/history.d.ts.map +1 -1
- package/dist/cjs/src/utils/history.js +169 -23
- package/dist/cjs/src/utils/history.js.map +1 -1
- package/dist/cjs/src/utils/index.d.ts +1 -1
- package/dist/cjs/src/utils/index.d.ts.map +1 -1
- package/dist/cjs/src/utils/index.js +5 -1
- package/dist/cjs/src/utils/index.js.map +1 -1
- package/dist/cjs/src/utils/session.d.ts +2 -2
- package/dist/cjs/src/utils/session.d.ts.map +1 -1
- package/dist/cjs/src/utils/session.js +6 -26
- package/dist/cjs/src/utils/session.js.map +1 -1
- package/dist/src/core/Agent.d.ts +77 -59
- package/dist/src/core/Agent.d.ts.map +1 -1
- package/dist/src/core/Agent.js +285 -1061
- package/dist/src/core/Agent.js.map +1 -1
- package/dist/src/core/PersistenceManager.d.ts.map +1 -1
- package/dist/src/core/PersistenceManager.js +48 -25
- package/dist/src/core/PersistenceManager.js.map +1 -1
- package/dist/src/core/PromptComposer.d.ts +1 -1
- package/dist/src/core/PromptComposer.d.ts.map +1 -1
- package/dist/src/core/PromptComposer.js.map +1 -1
- package/dist/src/core/ResponseEngine.d.ts +13 -12
- package/dist/src/core/ResponseEngine.d.ts.map +1 -1
- package/dist/src/core/ResponseEngine.js +4 -4
- package/dist/src/core/ResponseEngine.js.map +1 -1
- package/dist/src/core/ResponseModal.d.ts +205 -0
- package/dist/src/core/ResponseModal.d.ts.map +1 -0
- package/dist/src/core/ResponseModal.js +1323 -0
- package/dist/src/core/ResponseModal.js.map +1 -0
- package/dist/src/core/ResponsePipeline.d.ts +66 -38
- package/dist/src/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/src/core/ResponsePipeline.js +72 -4
- package/dist/src/core/ResponsePipeline.js.map +1 -1
- package/dist/src/core/Route.d.ts +24 -5
- package/dist/src/core/Route.d.ts.map +1 -1
- package/dist/src/core/Route.js +45 -1
- package/dist/src/core/Route.js.map +1 -1
- package/dist/src/core/RoutingEngine.d.ts +31 -6
- package/dist/src/core/RoutingEngine.d.ts.map +1 -1
- package/dist/src/core/RoutingEngine.js +113 -9
- package/dist/src/core/RoutingEngine.js.map +1 -1
- package/dist/src/core/SessionManager.d.ts +14 -4
- package/dist/src/core/SessionManager.d.ts.map +1 -1
- package/dist/src/core/SessionManager.js +25 -5
- package/dist/src/core/SessionManager.js.map +1 -1
- package/dist/src/core/Step.d.ts +10 -10
- package/dist/src/core/Step.d.ts.map +1 -1
- package/dist/src/core/Step.js.map +1 -1
- package/dist/src/core/ToolExecutor.d.ts +4 -2
- package/dist/src/core/ToolExecutor.d.ts.map +1 -1
- package/dist/src/core/ToolExecutor.js +13 -3
- package/dist/src/core/ToolExecutor.js.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/types/agent.d.ts +42 -21
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/agent.js.map +1 -1
- package/dist/src/types/ai.d.ts +1 -1
- package/dist/src/types/ai.d.ts.map +1 -1
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/persistence.d.ts +0 -1
- package/dist/src/types/persistence.d.ts.map +1 -1
- package/dist/src/types/route.d.ts +22 -16
- package/dist/src/types/route.d.ts.map +1 -1
- package/dist/src/types/session.d.ts +6 -11
- package/dist/src/types/session.d.ts.map +1 -1
- package/dist/src/types/tool.d.ts +12 -6
- package/dist/src/types/tool.d.ts.map +1 -1
- package/dist/src/utils/clone.d.ts.map +1 -1
- package/dist/src/utils/clone.js +0 -4
- package/dist/src/utils/clone.js.map +1 -1
- package/dist/src/utils/history.d.ts +30 -1
- package/dist/src/utils/history.d.ts.map +1 -1
- package/dist/src/utils/history.js +165 -23
- package/dist/src/utils/history.js.map +1 -1
- package/dist/src/utils/index.d.ts +1 -1
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +1 -1
- package/dist/src/utils/index.js.map +1 -1
- package/dist/src/utils/session.d.ts +2 -2
- package/dist/src/utils/session.d.ts.map +1 -1
- package/dist/src/utils/session.js +6 -26
- package/dist/src/utils/session.js.map +1 -1
- package/docs/README.md +5 -4
- package/docs/api/README.md +195 -4
- package/docs/api/overview.md +232 -13
- package/docs/core/agent/README.md +162 -17
- package/docs/core/agent/context-management.md +39 -15
- package/docs/core/agent/session-management.md +49 -16
- package/docs/core/ai-integration/prompt-composition.md +38 -14
- package/docs/core/ai-integration/response-processing.md +28 -17
- package/docs/core/conversation-flows/data-collection.md +103 -25
- package/docs/core/conversation-flows/route-dsl.md +45 -22
- package/docs/core/conversation-flows/routes.md +74 -18
- package/docs/core/conversation-flows/step-transitions.md +3 -3
- package/docs/core/conversation-flows/steps.md +39 -15
- package/docs/core/routing/intelligent-routing.md +18 -9
- package/docs/core/tools/tool-definition.md +8 -8
- package/docs/core/tools/tool-execution.md +26 -26
- package/docs/core/tools/tool-scoping.md +5 -5
- package/docs/guides/getting-started/README.md +54 -32
- package/docs/guides/migration/README.md +72 -0
- package/docs/guides/migration/response-modal-refactor.md +518 -0
- package/examples/advanced-patterns/knowledge-based-agent.ts +37 -28
- package/examples/advanced-patterns/persistent-onboarding.ts +70 -41
- package/examples/advanced-patterns/route-lifecycle-hooks.ts +28 -2
- package/examples/advanced-patterns/streaming-responses.ts +197 -119
- package/examples/ai-providers/anthropic-integration.ts +40 -33
- package/examples/ai-providers/openai-integration.ts +25 -25
- package/examples/conversation-flows/completion-transitions.ts +36 -32
- package/examples/core-concepts/basic-agent.ts +76 -78
- package/examples/core-concepts/modern-streaming-api.ts +309 -0
- package/examples/core-concepts/schema-driven-extraction.ts +20 -16
- package/examples/core-concepts/session-management.ts +65 -53
- package/examples/integrations/database-integration.ts +49 -34
- package/examples/integrations/healthcare-integration.ts +96 -91
- package/examples/integrations/search-integration.ts +79 -82
- package/examples/integrations/server-session-management.ts +25 -17
- package/examples/persistence/database-persistence.ts +61 -45
- package/examples/persistence/memory-sessions.ts +52 -63
- package/examples/persistence/redis-persistence.ts +81 -95
- package/examples/tools/basic-tools.ts +73 -62
- package/examples/tools/data-enrichment-tools.ts +52 -44
- package/package.json +1 -1
- package/src/core/Agent.ts +396 -1499
- package/src/core/PersistenceManager.ts +51 -27
- package/src/core/PromptComposer.ts +1 -1
- package/src/core/ResponseEngine.ts +21 -19
- package/src/core/ResponseModal.ts +1722 -0
- package/src/core/ResponsePipeline.ts +175 -60
- package/src/core/Route.ts +58 -6
- package/src/core/RoutingEngine.ts +174 -27
- package/src/core/SessionManager.ts +32 -8
- package/src/core/Step.ts +20 -12
- package/src/core/ToolExecutor.ts +19 -5
- package/src/index.ts +11 -0
- package/src/types/agent.ts +47 -23
- package/src/types/ai.ts +1 -1
- package/src/types/index.ts +2 -0
- package/src/types/persistence.ts +0 -1
- package/src/types/route.ts +22 -16
- package/src/types/session.ts +6 -12
- package/src/types/tool.ts +15 -9
- package/src/utils/clone.ts +6 -8
- package/src/utils/history.ts +190 -27
- package/src/utils/index.ts +4 -0
- package/src/utils/session.ts +6 -31
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
# Schema-Driven Data Collection
|
|
1
|
+
# Agent-Level Schema-Driven Data Collection
|
|
2
2
|
|
|
3
|
-
@falai/agent implements a powerful schema-first approach to data collection, enabling type-safe, structured information extraction from natural conversations. Unlike traditional
|
|
3
|
+
@falai/agent implements a powerful agent-level schema-first approach to data collection, enabling type-safe, structured information extraction from natural conversations across all routes. Unlike traditional route-specific data collection, this system centralizes data schemas at the agent level while allowing routes to specify completion requirements.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
The data collection system provides:
|
|
7
|
+
The agent-level data collection system provides:
|
|
8
8
|
|
|
9
|
-
- **JSON Schema
|
|
9
|
+
- **Centralized JSON Schema**: Define comprehensive data structures at the agent level
|
|
10
|
+
- **Cross-Route Data Sharing**: Data collected by any route is available to all routes
|
|
11
|
+
- **Route Completion Logic**: Routes complete when their required fields are satisfied
|
|
10
12
|
- **Type-Safe Extraction**: Automatic mapping from AI responses to typed data
|
|
11
13
|
- **Natural Conversations**: AI handles information gathering conversationally
|
|
12
|
-
- **Validation & Enrichment**:
|
|
13
|
-
- **Session Persistence**: Data survives across conversation turns
|
|
14
|
+
- **Validation & Enrichment**: Agent-level lifecycle hooks for data processing
|
|
15
|
+
- **Session Persistence**: Data survives across conversation turns and route transitions
|
|
14
16
|
|
|
15
|
-
## Schema Definition
|
|
17
|
+
## Agent-Level Schema Definition
|
|
16
18
|
|
|
17
|
-
###
|
|
19
|
+
### Centralized Schema
|
|
18
20
|
|
|
19
21
|
```typescript
|
|
20
22
|
interface UserProfile {
|
|
@@ -26,10 +28,19 @@ interface UserProfile {
|
|
|
26
28
|
notifications: boolean;
|
|
27
29
|
theme: "light" | "dark";
|
|
28
30
|
};
|
|
31
|
+
// Additional fields for other routes
|
|
32
|
+
supportTicketId?: string;
|
|
33
|
+
issueType?: string;
|
|
34
|
+
feedbackRating?: number;
|
|
35
|
+
subscriptionTier?: "free" | "premium" | "enterprise";
|
|
29
36
|
}
|
|
30
37
|
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
// Define schema at agent level
|
|
39
|
+
const agent = new Agent<{}, UserProfile>({
|
|
40
|
+
name: "User Management Agent",
|
|
41
|
+
provider: new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY }),
|
|
42
|
+
|
|
43
|
+
// Comprehensive agent-level schema
|
|
33
44
|
schema: {
|
|
34
45
|
type: "object",
|
|
35
46
|
properties: {
|
|
@@ -63,9 +74,32 @@ const userProfileRoute = agent.createRoute<UserProfile>({
|
|
|
63
74
|
},
|
|
64
75
|
},
|
|
65
76
|
},
|
|
77
|
+
supportTicketId: { type: "string" },
|
|
78
|
+
issueType: { type: "string" },
|
|
79
|
+
feedbackRating: { type: "number", minimum: 1, maximum: 5 },
|
|
80
|
+
subscriptionTier: { type: "string", enum: ["free", "premium", "enterprise"] }
|
|
66
81
|
},
|
|
67
82
|
required: ["name", "email"],
|
|
68
|
-
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Routes specify required fields instead of schemas
|
|
87
|
+
const profileRoute = agent.createRoute({
|
|
88
|
+
title: "User Profile Collection",
|
|
89
|
+
requiredFields: ["name", "email", "age"],
|
|
90
|
+
optionalFields: ["interests", "preferences"]
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const supportRoute = agent.createRoute({
|
|
94
|
+
title: "Support Ticket",
|
|
95
|
+
requiredFields: ["name", "email", "issueType"],
|
|
96
|
+
optionalFields: ["supportTicketId"]
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const feedbackRoute = agent.createRoute({
|
|
100
|
+
title: "Feedback Collection",
|
|
101
|
+
requiredFields: ["name", "email", "feedbackRating"],
|
|
102
|
+
optionalFields: ["subscriptionTier"]
|
|
69
103
|
});
|
|
70
104
|
```
|
|
71
105
|
|
|
@@ -128,24 +162,33 @@ const complexSchema = {
|
|
|
128
162
|
|
|
129
163
|
## Step-Level Data Collection
|
|
130
164
|
|
|
131
|
-
### Basic Collection
|
|
165
|
+
### Basic Collection with Agent-Level Data
|
|
132
166
|
|
|
133
167
|
```typescript
|
|
134
168
|
const profileRoute = agent
|
|
135
169
|
.createRoute({
|
|
136
170
|
title: "Profile Collection",
|
|
137
|
-
|
|
171
|
+
requiredFields: ["name", "email", "age"], // Required for route completion
|
|
172
|
+
optionalFields: ["interests"], // Optional but helpful
|
|
138
173
|
initialStep: {
|
|
139
174
|
prompt:
|
|
140
175
|
"Hi! I'm collecting some information to personalize your experience. What's your name?",
|
|
141
|
-
collect: ["name"], // Maps to schema field
|
|
176
|
+
collect: ["name"], // Maps to agent schema field
|
|
142
177
|
},
|
|
143
178
|
})
|
|
144
179
|
.nextStep({
|
|
145
180
|
prompt: "Thanks {{name}}! What's your email address?",
|
|
146
181
|
collect: ["email"],
|
|
147
182
|
requires: ["name"], // Must have name before collecting email
|
|
183
|
+
})
|
|
184
|
+
.nextStep({
|
|
185
|
+
prompt: "What's your age?",
|
|
186
|
+
collect: ["age"],
|
|
187
|
+
requires: ["name", "email"],
|
|
148
188
|
});
|
|
189
|
+
|
|
190
|
+
// Route completes when all required fields are collected
|
|
191
|
+
// Data is available to all other routes
|
|
149
192
|
```
|
|
150
193
|
|
|
151
194
|
### Multi-Field Collection
|
|
@@ -160,12 +203,12 @@ const comprehensiveStep = {
|
|
|
160
203
|
- Your preferred theme (light/dark)
|
|
161
204
|
`,
|
|
162
205
|
collect: [
|
|
163
|
-
"age", // Single field
|
|
164
|
-
"interests", // Array field
|
|
206
|
+
"age", // Single field from agent schema
|
|
207
|
+
"interests", // Array field from agent schema
|
|
165
208
|
"preferences.notifications", // Nested field (dot notation)
|
|
166
209
|
"preferences.theme", // Another nested field
|
|
167
210
|
],
|
|
168
|
-
requires: ["name", "email"],
|
|
211
|
+
requires: ["name", "email"], // Prerequisites from agent data
|
|
169
212
|
};
|
|
170
213
|
```
|
|
171
214
|
|
|
@@ -175,34 +218,69 @@ const comprehensiveStep = {
|
|
|
175
218
|
const conditionalCollection = {
|
|
176
219
|
prompt: "Would you like to set up notifications? (yes/no)",
|
|
177
220
|
collect: ["preferences.notifications"],
|
|
178
|
-
skipIf: (data) => data.preferences?.notifications !== undefined,
|
|
179
|
-
requires: ["name", "email"],
|
|
221
|
+
skipIf: (data) => data.preferences?.notifications !== undefined, // Skip if already collected
|
|
222
|
+
requires: ["name", "email"], // Prerequisites from agent data
|
|
180
223
|
};
|
|
181
224
|
```
|
|
182
225
|
|
|
226
|
+
### Cross-Route Data Sharing
|
|
227
|
+
|
|
228
|
+
With agent-level data collection, routes can share data seamlessly:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
// User starts with profile collection
|
|
232
|
+
const response1 = await agent.respond("Hi, I'm John Doe, email john@example.com");
|
|
233
|
+
// Agent data: { name: "John Doe", email: "john@example.com" }
|
|
234
|
+
|
|
235
|
+
// User switches to support - data is already available
|
|
236
|
+
const response2 = await agent.respond("Actually, I need help with a technical issue");
|
|
237
|
+
// Support route can access name and email, only needs to collect issue details
|
|
238
|
+
// Support route: 2/3 required fields already satisfied
|
|
239
|
+
|
|
240
|
+
// User provides issue details
|
|
241
|
+
const response3 = await agent.respond("My account won't sync properly");
|
|
242
|
+
// Support route completes: { name: "John Doe", email: "john@example.com", issueType: "technical" }
|
|
243
|
+
|
|
244
|
+
// Later, user wants to give feedback
|
|
245
|
+
const response4 = await agent.respond("I want to rate my support experience - 5 stars");
|
|
246
|
+
// Feedback route completes immediately: already has name, email, and now rating
|
|
247
|
+
```
|
|
248
|
+
|
|
183
249
|
## Data Validation & Processing
|
|
184
250
|
|
|
185
|
-
### Lifecycle Hooks
|
|
251
|
+
### Agent-Level Lifecycle Hooks
|
|
186
252
|
|
|
187
253
|
```typescript
|
|
188
|
-
const
|
|
189
|
-
|
|
254
|
+
const agent = new Agent<{}, UserProfile>({
|
|
255
|
+
name: "User Management Agent",
|
|
256
|
+
schema: { /* agent schema */ },
|
|
190
257
|
|
|
191
|
-
// Agent-level data validation
|
|
258
|
+
// Agent-level data validation (applies to all routes)
|
|
192
259
|
hooks: {
|
|
193
260
|
onDataUpdate: (newData, previousData) => {
|
|
194
|
-
// Cross-field validation
|
|
261
|
+
// Cross-field validation using complete agent data
|
|
195
262
|
if (newData.email && newData.confirmEmail) {
|
|
196
263
|
if (newData.email !== newData.confirmEmail) {
|
|
197
264
|
throw new Error("Email addresses don't match");
|
|
198
265
|
}
|
|
199
266
|
}
|
|
200
267
|
|
|
201
|
-
// Data enrichment
|
|
268
|
+
// Data enrichment based on agent-level data
|
|
202
269
|
if (newData.name && !newData.displayName) {
|
|
203
270
|
newData.displayName = newData.name.split(" ")[0]; // First name only
|
|
204
271
|
}
|
|
205
272
|
|
|
273
|
+
// Auto-set subscription tier based on email domain
|
|
274
|
+
if (newData.email && !newData.subscriptionTier) {
|
|
275
|
+
newData.subscriptionTier = newData.email.includes('@enterprise.com') ? 'enterprise' : 'free';
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Validate against agent schema
|
|
279
|
+
const validation = agent.validateData(newData);
|
|
280
|
+
if (!validation.valid) {
|
|
281
|
+
throw new Error(`Data validation failed: ${validation.errors.map(e => e.message).join(', ')}`);
|
|
282
|
+
}
|
|
283
|
+
|
|
206
284
|
return newData;
|
|
207
285
|
},
|
|
208
286
|
},
|
|
@@ -31,31 +31,44 @@ const greetingRoute = agent
|
|
|
31
31
|
});
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
### Route with Schema
|
|
34
|
+
### Route with Agent-Level Schema
|
|
35
35
|
|
|
36
36
|
```typescript
|
|
37
37
|
interface UserInfo {
|
|
38
38
|
name: string;
|
|
39
39
|
email: string;
|
|
40
40
|
interests: string[];
|
|
41
|
+
preferences?: object;
|
|
42
|
+
profileComplete?: boolean;
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
// Agent defines comprehensive schema
|
|
46
|
+
const agent = new Agent<{}, UserInfo>({
|
|
47
|
+
name: "Profile Assistant",
|
|
48
|
+
provider: openaiProvider,
|
|
49
|
+
schema: {
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
name: { type: "string" },
|
|
53
|
+
email: { type: "string", format: "email" },
|
|
54
|
+
interests: {
|
|
55
|
+
type: "array",
|
|
56
|
+
items: { type: "string" },
|
|
57
|
+
},
|
|
58
|
+
preferences: { type: "object" },
|
|
59
|
+
profileComplete: { type: "boolean" }
|
|
60
|
+
},
|
|
61
|
+
required: ["name", "email"],
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Route specifies required fields instead of schema
|
|
43
66
|
const userProfileRoute = agent
|
|
44
|
-
.createRoute
|
|
67
|
+
.createRoute({
|
|
45
68
|
title: "User Profile Collection",
|
|
46
69
|
description: "Collect basic user information",
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
properties: {
|
|
50
|
-
name: { type: "string" },
|
|
51
|
-
email: { type: "string", format: "email" },
|
|
52
|
-
interests: {
|
|
53
|
-
type: "array",
|
|
54
|
-
items: { type: "string" },
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
required: ["name", "email"],
|
|
58
|
-
},
|
|
70
|
+
requiredFields: ["name", "email", "interests"], // Required for completion
|
|
71
|
+
optionalFields: ["preferences"], // Nice to have
|
|
59
72
|
initialStep: {
|
|
60
73
|
prompt: "Let's create your profile. What's your name?",
|
|
61
74
|
collect: ["name"],
|
|
@@ -64,12 +77,12 @@ const userProfileRoute = agent
|
|
|
64
77
|
.nextStep({
|
|
65
78
|
prompt: "Great, {{name}}! What's your email address?",
|
|
66
79
|
collect: ["email"],
|
|
67
|
-
requires: ["name"],
|
|
80
|
+
requires: ["name"], // Prerequisites from agent data
|
|
68
81
|
})
|
|
69
82
|
.nextStep({
|
|
70
83
|
prompt: "What are your interests? (comma-separated)",
|
|
71
84
|
collect: ["interests"],
|
|
72
|
-
requires: ["name", "email"],
|
|
85
|
+
requires: ["name", "email"], // Prerequisites from agent data
|
|
73
86
|
});
|
|
74
87
|
```
|
|
75
88
|
|
|
@@ -99,9 +112,9 @@ interface StepOptions<TContext, TData> {
|
|
|
99
112
|
```typescript
|
|
100
113
|
const dataCollectionStep = {
|
|
101
114
|
prompt: "What's your preferred contact method?",
|
|
102
|
-
collect: ["contactMethod"], // Maps to schema field
|
|
103
|
-
requires: ["name", "email"], // Must have these fields
|
|
104
|
-
skipIf: (data) => data.contactMethod !== undefined, // Skip if already collected
|
|
115
|
+
collect: ["contactMethod"], // Maps to agent schema field
|
|
116
|
+
requires: ["name", "email"], // Must have these fields from agent data
|
|
117
|
+
skipIf: (data) => data.contactMethod !== undefined, // Skip if already collected by any route
|
|
105
118
|
};
|
|
106
119
|
```
|
|
107
120
|
|
|
@@ -296,6 +309,10 @@ const advancedRoute = agent.createRoute({
|
|
|
296
309
|
title: "Advanced Interaction",
|
|
297
310
|
description: "Complex multi-step conversation",
|
|
298
311
|
|
|
312
|
+
// Route completion requirements
|
|
313
|
+
requiredFields: ["customerName", "email", "issueType"],
|
|
314
|
+
optionalFields: ["phone", "priority"],
|
|
315
|
+
|
|
299
316
|
// Route-level identity overrides agent identity
|
|
300
317
|
identity: "You are an expert consultant specializing in {{domain}}",
|
|
301
318
|
|
|
@@ -316,19 +333,25 @@ const advancedRoute = agent.createRoute({
|
|
|
316
333
|
},
|
|
317
334
|
],
|
|
318
335
|
|
|
319
|
-
// Initial data to pre-populate
|
|
336
|
+
// Initial data to pre-populate (maps to agent schema)
|
|
320
337
|
initialData: {
|
|
321
338
|
sessionId: generateId(),
|
|
322
339
|
startTime: new Date().toISOString(),
|
|
323
340
|
},
|
|
324
341
|
|
|
325
|
-
//
|
|
342
|
+
// Route-level lifecycle hooks (work with agent data)
|
|
326
343
|
hooks: {
|
|
327
344
|
onDataUpdate: (newData, previousData) => {
|
|
328
|
-
// Validate or enrich collected data
|
|
345
|
+
// Validate or enrich agent-level collected data
|
|
329
346
|
if (newData.email && !isValidEmail(newData.email)) {
|
|
330
347
|
throw new Error("Invalid email format");
|
|
331
348
|
}
|
|
349
|
+
|
|
350
|
+
// Auto-set priority based on issue type
|
|
351
|
+
if (newData.issueType === 'billing' && !newData.priority) {
|
|
352
|
+
newData.priority = 'high';
|
|
353
|
+
}
|
|
354
|
+
|
|
332
355
|
return newData;
|
|
333
356
|
},
|
|
334
357
|
onContextUpdate: (newContext, previousContext) => {
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
# Routes
|
|
2
2
|
|
|
3
|
-
Routes define conversational journeys in @falai/agent. This document covers route definition, lifecycle management, and completion handling.
|
|
3
|
+
Routes define conversational journeys in @falai/agent with agent-level data collection. This document covers route definition with required fields, lifecycle management, and completion handling based on data availability.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
Routes represent complete conversational workflows that guide users through specific tasks or processes. Each route consists of steps
|
|
7
|
+
Routes represent complete conversational workflows that guide users through specific tasks or processes. Each route specifies required fields for completion, consists of steps that collect data into the agent-level schema, and can complete when their data requirements are satisfied regardless of which route collected the data.
|
|
8
8
|
|
|
9
|
-
## Route Definition
|
|
9
|
+
## Route Definition with Required Fields
|
|
10
10
|
|
|
11
11
|
```typescript
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
// Agent defines comprehensive schema
|
|
13
|
+
interface HotelData {
|
|
14
|
+
destination: string;
|
|
15
|
+
checkIn: string;
|
|
16
|
+
checkOut: string;
|
|
17
|
+
guests: number;
|
|
18
|
+
roomType?: string;
|
|
19
|
+
customerName?: string;
|
|
20
|
+
email?: string;
|
|
21
|
+
phone?: string;
|
|
22
|
+
specialRequests?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const agent = new Agent<{}, HotelData>({
|
|
26
|
+
name: "Hotel Booking Agent",
|
|
16
27
|
schema: {
|
|
17
28
|
type: "object",
|
|
18
29
|
properties: {
|
|
@@ -20,42 +31,87 @@ const bookingRoute = agent.createRoute<BookingData>({
|
|
|
20
31
|
checkIn: { type: "string", format: "date" },
|
|
21
32
|
checkOut: { type: "string", format: "date" },
|
|
22
33
|
guests: { type: "number", minimum: 1 },
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
roomType: { type: "string" },
|
|
35
|
+
customerName: { type: "string" },
|
|
36
|
+
email: { type: "string", format: "email" },
|
|
37
|
+
phone: { type: "string" },
|
|
38
|
+
specialRequests: { type: "string" }
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Routes specify required fields instead of schemas
|
|
44
|
+
const bookingRoute = agent.createRoute({
|
|
45
|
+
title: "Hotel Booking",
|
|
46
|
+
description: "Help users book hotel accommodations",
|
|
47
|
+
conditions: ["User wants to book a hotel"],
|
|
48
|
+
requiredFields: ["destination", "checkIn", "checkOut", "guests", "customerName", "email"],
|
|
49
|
+
optionalFields: ["roomType", "phone", "specialRequests"]
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const customerServiceRoute = agent.createRoute({
|
|
53
|
+
title: "Customer Service",
|
|
54
|
+
description: "Help with booking issues",
|
|
55
|
+
conditions: ["User needs help with existing booking"],
|
|
56
|
+
requiredFields: ["customerName", "email"], // Minimal requirements
|
|
57
|
+
optionalFields: ["phone", "destination"]
|
|
26
58
|
});
|
|
27
59
|
```
|
|
28
60
|
|
|
29
61
|
## Route Lifecycle
|
|
30
62
|
|
|
31
|
-
Routes have a complete lifecycle from creation through execution to completion.
|
|
63
|
+
Routes have a complete lifecycle from creation through execution to completion based on data availability.
|
|
32
64
|
|
|
33
65
|
### Route Creation
|
|
34
66
|
|
|
35
|
-
Routes are created using the agent's `createRoute()` method with
|
|
67
|
+
Routes are created using the agent's `createRoute()` method with required fields specifications that reference the agent-level schema.
|
|
36
68
|
|
|
37
69
|
### Route Execution
|
|
38
70
|
|
|
39
|
-
Routes are selected by the AI routing system based on user intent and conversation context.
|
|
71
|
+
Routes are selected by the AI routing system based on user intent and conversation context, with access to all agent-level data.
|
|
40
72
|
|
|
41
73
|
### Route Completion
|
|
42
74
|
|
|
43
|
-
Routes complete when
|
|
75
|
+
Routes complete when all their required fields are present in the agent's collected data, regardless of which route collected the data. This enables flexible cross-route completion scenarios.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
// Route completion evaluation
|
|
79
|
+
const isComplete = bookingRoute.isComplete(agent.getCollectedData());
|
|
80
|
+
const missingFields = bookingRoute.getMissingRequiredFields(agent.getCollectedData());
|
|
81
|
+
const progress = bookingRoute.getCompletionProgress(agent.getCollectedData()); // 0-1
|
|
82
|
+
|
|
83
|
+
console.log(`Booking route is ${Math.round(progress * 100)}% complete`);
|
|
84
|
+
if (missingFields.length > 0) {
|
|
85
|
+
console.log(`Still need: ${missingFields.join(', ')}`);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
44
88
|
|
|
45
89
|
## Route Transitions
|
|
46
90
|
|
|
47
|
-
Routes can automatically transition to other routes upon completion using the `onComplete` configuration.
|
|
91
|
+
Routes can automatically transition to other routes upon completion using the `onComplete` configuration. With agent-level data, the target route may already have some of its required data.
|
|
48
92
|
|
|
49
93
|
```typescript
|
|
50
94
|
const bookingRoute = agent.createRoute({
|
|
51
95
|
title: "Hotel Booking",
|
|
96
|
+
requiredFields: ["destination", "checkIn", "checkOut", "guests", "customerName", "email"],
|
|
52
97
|
onComplete: "Feedback Collection", // Transition to feedback route
|
|
53
98
|
});
|
|
99
|
+
|
|
100
|
+
const feedbackRoute = agent.createRoute({
|
|
101
|
+
title: "Feedback Collection",
|
|
102
|
+
requiredFields: ["customerName", "email", "rating"], // Already has name and email from booking
|
|
103
|
+
optionalFields: ["comments"]
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// When booking completes, feedback route is already 2/3 complete
|
|
54
107
|
```
|
|
55
108
|
|
|
56
109
|
## Best Practices
|
|
57
110
|
|
|
58
111
|
- Use descriptive titles and conditions for better AI routing
|
|
59
|
-
- Define comprehensive schemas for type safety
|
|
60
|
-
-
|
|
61
|
-
-
|
|
112
|
+
- Define comprehensive agent-level schemas for type safety across all routes
|
|
113
|
+
- Specify minimal required fields for faster route completion
|
|
114
|
+
- Use optional fields to enhance user experience without blocking completion
|
|
115
|
+
- Implement appropriate completion handlers that leverage shared data
|
|
116
|
+
- Consider route transitions for multi-step workflows with data continuity
|
|
117
|
+
- Design routes that can benefit from cross-route data sharing
|
|
@@ -26,14 +26,14 @@ const smartRoute = agent
|
|
|
26
26
|
initialStep: {
|
|
27
27
|
prompt: "What's your name?",
|
|
28
28
|
collect: ["name"],
|
|
29
|
-
skipIf: (data) => data.name !== undefined, // Skip if name already
|
|
29
|
+
skipIf: (data) => data.name !== undefined, // Skip if name already collected by any route
|
|
30
30
|
},
|
|
31
31
|
})
|
|
32
32
|
.nextStep({
|
|
33
33
|
prompt: "What's your email, {{name}}?",
|
|
34
34
|
collect: ["email"],
|
|
35
|
-
requires: ["name"], // Must have name to proceed
|
|
36
|
-
skipIf: (data) => data.email !== undefined, // Skip if email already
|
|
35
|
+
requires: ["name"], // Must have name from agent data to proceed
|
|
36
|
+
skipIf: (data) => data.email !== undefined, // Skip if email already collected by any route
|
|
37
37
|
});
|
|
38
38
|
```
|
|
39
39
|
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
# Steps
|
|
2
2
|
|
|
3
|
-
Steps are the building blocks of conversational routes in @falai/agent. This document covers step configuration, data collection, and transition logic.
|
|
3
|
+
Steps are the building blocks of conversational routes in @falai/agent with agent-level data collection. This document covers step configuration, data collection into the agent schema, and transition logic based on agent data.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
7
|
Steps represent individual moments in a conversation where the agent can:
|
|
8
8
|
|
|
9
9
|
- Prompt the user for information
|
|
10
|
-
- Collect structured data
|
|
11
|
-
- Execute tools
|
|
12
|
-
- Make decisions about conversation flow
|
|
10
|
+
- Collect structured data into the agent-level schema
|
|
11
|
+
- Execute tools that work with complete agent data
|
|
12
|
+
- Make decisions about conversation flow based on agent data
|
|
13
|
+
- Skip execution if required data is already available from other routes
|
|
13
14
|
|
|
14
15
|
## Step Configuration
|
|
15
16
|
|
|
16
17
|
```typescript
|
|
17
18
|
const nameStep = bookingRoute.initialStep.nextStep({
|
|
18
19
|
prompt: "What's your name?",
|
|
19
|
-
collect: ["
|
|
20
|
+
collect: ["customerName"], // Collects into agent-level schema
|
|
20
21
|
requires: [], // No prerequisites
|
|
21
|
-
skipIf: (data) => data.
|
|
22
|
+
skipIf: (data) => data.customerName, // Skip if already collected by any route
|
|
22
23
|
});
|
|
23
24
|
```
|
|
24
25
|
|
|
@@ -42,34 +43,57 @@ Steps that end the route using `END_ROUTE`.
|
|
|
42
43
|
|
|
43
44
|
## Data Collection
|
|
44
45
|
|
|
45
|
-
Steps collect data through the `collect` array, which maps to the
|
|
46
|
+
Steps collect data through the `collect` array, which maps to the agent's JSON schema and is validated against it.
|
|
46
47
|
|
|
47
48
|
```typescript
|
|
48
49
|
const contactStep = nameStep.nextStep({
|
|
49
50
|
prompt: "What's your email and phone number?",
|
|
50
|
-
collect: ["email", "phone"],
|
|
51
|
-
requires: ["
|
|
51
|
+
collect: ["email", "phone"], // Maps to agent schema fields
|
|
52
|
+
requires: ["customerName"], // Must have name first (from agent data)
|
|
52
53
|
});
|
|
53
54
|
```
|
|
54
55
|
|
|
55
56
|
## Conditional Logic
|
|
56
57
|
|
|
57
|
-
Steps support various conditional behaviors:
|
|
58
|
+
Steps support various conditional behaviors based on agent-level data:
|
|
58
59
|
|
|
59
|
-
- `skipIf`: Skip the step if a condition is met
|
|
60
|
-
- `requires`: Prerequisites that must be satisfied
|
|
60
|
+
- `skipIf`: Skip the step if a condition is met (evaluates against complete agent data)
|
|
61
|
+
- `requires`: Prerequisites that must be satisfied (checks agent data from any route)
|
|
61
62
|
- `when`: AI-evaluated conditions for branching
|
|
62
63
|
|
|
63
64
|
## Tool Integration
|
|
64
65
|
|
|
65
|
-
Steps can execute tools
|
|
66
|
+
Steps can execute tools that work with complete agent-level data:
|
|
66
67
|
|
|
67
68
|
```typescript
|
|
68
69
|
const weatherStep = planningStep.nextStep({
|
|
69
70
|
prompt: "I'll check the weather for your destination",
|
|
70
|
-
tool: weatherLookupTool,
|
|
71
|
-
requires: ["destination", "
|
|
71
|
+
tool: weatherLookupTool, // Tool receives complete agent data
|
|
72
|
+
requires: ["destination", "checkIn"], // Prerequisites from agent data
|
|
72
73
|
});
|
|
74
|
+
|
|
75
|
+
// Tool implementation with agent data access
|
|
76
|
+
const weatherLookupTool: Tool<Context, [], WeatherData, HotelData> = {
|
|
77
|
+
id: "weather_lookup",
|
|
78
|
+
description: "Look up weather for destination",
|
|
79
|
+
parameters: { type: "object", properties: {} },
|
|
80
|
+
handler: async (toolContext) => {
|
|
81
|
+
const { data } = toolContext; // Complete agent data
|
|
82
|
+
|
|
83
|
+
if (!data.destination || !data.checkIn) {
|
|
84
|
+
return { data: undefined };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const weather = await getWeather(data.destination, data.checkIn);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
data: weather,
|
|
91
|
+
dataUpdate: {
|
|
92
|
+
weatherInfo: weather.summary // Update agent data
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
};
|
|
73
97
|
```
|
|
74
98
|
|
|
75
99
|
## Step Transitions
|
|
@@ -58,8 +58,9 @@ The system intelligently traverses step chains:
|
|
|
58
58
|
All routing decisions consider:
|
|
59
59
|
|
|
60
60
|
- **Conversation History**: Full dialogue context
|
|
61
|
-
- **
|
|
62
|
-
- **Session State**: Current route and step position
|
|
61
|
+
- **Agent-Level Data**: Centralized information gathered across all routes
|
|
62
|
+
- **Session State**: Current route and step position with cross-route data access
|
|
63
|
+
- **Route Completion**: Progress toward required fields for each route
|
|
63
64
|
- **Agent Knowledge**: Guidelines, terms, and domain knowledge
|
|
64
65
|
|
|
65
66
|
## Route Selection API
|
|
@@ -107,20 +108,27 @@ The `getCandidateSteps()` method implements sophisticated logic:
|
|
|
107
108
|
const candidates = routingEngine.getCandidateSteps(
|
|
108
109
|
route, // Current route
|
|
109
110
|
currentStep, // Current step (or null for route start)
|
|
110
|
-
|
|
111
|
+
agentData // Agent-level data collected across all routes
|
|
111
112
|
);
|
|
112
113
|
```
|
|
113
114
|
|
|
114
115
|
### SkipIf Processing
|
|
115
116
|
|
|
116
|
-
Steps are automatically filtered based on conditions:
|
|
117
|
+
Steps are automatically filtered based on agent-level data conditions:
|
|
117
118
|
|
|
118
119
|
```typescript
|
|
119
|
-
// Step definition with skipIf
|
|
120
|
+
// Step definition with skipIf using agent data
|
|
120
121
|
initialStep: {
|
|
121
122
|
prompt: "What's your name?",
|
|
122
123
|
collect: ["name"],
|
|
123
|
-
skipIf: (data) => data.name !== undefined // Skip if name already collected
|
|
124
|
+
skipIf: (data) => data.name !== undefined // Skip if name already collected by any route
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Cross-route skipping example
|
|
128
|
+
const emailStep = {
|
|
129
|
+
prompt: "What's your email?",
|
|
130
|
+
collect: ["email"],
|
|
131
|
+
skipIf: (data) => data.email !== undefined // Skip if collected in different route
|
|
124
132
|
}
|
|
125
133
|
```
|
|
126
134
|
|
|
@@ -155,8 +163,9 @@ const routingPrompt = await routingEngine.buildRoutingPrompt({
|
|
|
155
163
|
Prompts include:
|
|
156
164
|
|
|
157
165
|
- Agent identity and personality
|
|
158
|
-
- Available routes with descriptions and
|
|
159
|
-
- Session context and collected data
|
|
166
|
+
- Available routes with descriptions and required fields
|
|
167
|
+
- Session context and agent-level collected data
|
|
168
|
+
- Route completion progress (e.g., "2/3 required fields collected")
|
|
160
169
|
- Scoring guidelines (90-100 scale)
|
|
161
170
|
- Conversation history and directives
|
|
162
171
|
|
|
@@ -169,7 +178,7 @@ const stepPrompt = await routingEngine.buildStepSelectionPrompt({
|
|
|
169
178
|
route,
|
|
170
179
|
currentStep,
|
|
171
180
|
candidates,
|
|
172
|
-
data:
|
|
181
|
+
data: agent.getCollectedData(), // Agent-level data
|
|
173
182
|
history,
|
|
174
183
|
lastMessage,
|
|
175
184
|
agentOptions,
|