@falai/agent 0.6.8 → 0.7.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 +62 -59
- package/dist/adapters/MemoryAdapter.js +2 -2
- package/dist/adapters/MemoryAdapter.js.map +1 -1
- package/dist/adapters/MongoAdapter.js +2 -2
- package/dist/adapters/MongoAdapter.js.map +1 -1
- package/dist/adapters/OpenSearchAdapter.js +7 -7
- package/dist/adapters/OpenSearchAdapter.js.map +1 -1
- package/dist/adapters/PostgreSQLAdapter.js +9 -9
- package/dist/adapters/PostgreSQLAdapter.js.map +1 -1
- package/dist/adapters/PrismaAdapter.js +3 -3
- package/dist/adapters/PrismaAdapter.js.map +1 -1
- package/dist/adapters/RedisAdapter.js +2 -2
- package/dist/adapters/RedisAdapter.js.map +1 -1
- package/dist/adapters/SQLiteAdapter.d.ts +3 -3
- package/dist/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/dist/adapters/SQLiteAdapter.js +11 -11
- package/dist/adapters/SQLiteAdapter.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/cjs/adapters/MemoryAdapter.js +2 -2
- package/dist/cjs/adapters/MemoryAdapter.js.map +1 -1
- package/dist/cjs/adapters/MongoAdapter.js +2 -2
- package/dist/cjs/adapters/MongoAdapter.js.map +1 -1
- package/dist/cjs/adapters/OpenSearchAdapter.js +7 -7
- package/dist/cjs/adapters/OpenSearchAdapter.js.map +1 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.js +9 -9
- package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -1
- package/dist/cjs/adapters/PrismaAdapter.js +3 -3
- package/dist/cjs/adapters/PrismaAdapter.js.map +1 -1
- package/dist/cjs/adapters/RedisAdapter.js +2 -2
- package/dist/cjs/adapters/RedisAdapter.js.map +1 -1
- package/dist/cjs/adapters/SQLiteAdapter.d.ts +3 -3
- package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/SQLiteAdapter.js +11 -11
- package/dist/cjs/adapters/SQLiteAdapter.js.map +1 -1
- package/dist/cjs/adapters/index.d.ts +1 -1
- package/dist/cjs/adapters/index.d.ts.map +1 -1
- package/dist/cjs/constants/index.d.ts +4 -4
- package/dist/cjs/constants/index.js +5 -5
- package/dist/cjs/core/Agent.d.ts +22 -22
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +160 -152
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/Events.d.ts +6 -6
- package/dist/cjs/core/Events.d.ts.map +1 -1
- package/dist/cjs/core/PersistenceManager.d.ts +13 -13
- package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
- package/dist/cjs/core/PersistenceManager.js +24 -24
- package/dist/cjs/core/PersistenceManager.js.map +1 -1
- package/dist/cjs/core/ResponseEngine.d.ts +3 -8
- package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
- package/dist/cjs/core/ResponseEngine.js +8 -8
- package/dist/cjs/core/ResponseEngine.js.map +1 -1
- package/dist/cjs/core/Route.d.ts +17 -17
- package/dist/cjs/core/Route.d.ts.map +1 -1
- package/dist/cjs/core/Route.js +33 -33
- package/dist/cjs/core/Route.js.map +1 -1
- package/dist/cjs/core/RoutingEngine.d.ts +30 -30
- package/dist/cjs/core/RoutingEngine.d.ts.map +1 -1
- package/dist/cjs/core/RoutingEngine.js +192 -192
- package/dist/cjs/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/core/Step.d.ts +72 -0
- package/dist/cjs/core/Step.d.ts.map +1 -0
- package/dist/cjs/core/Step.js +150 -0
- package/dist/cjs/core/Step.js.map +1 -0
- package/dist/cjs/core/ToolExecutor.d.ts +5 -5
- package/dist/cjs/core/ToolExecutor.d.ts.map +1 -1
- package/dist/cjs/core/ToolExecutor.js +8 -8
- package/dist/cjs/core/ToolExecutor.js.map +1 -1
- package/dist/cjs/core/Transition.d.ts +14 -14
- package/dist/cjs/core/Transition.d.ts.map +1 -1
- package/dist/cjs/core/Transition.js +48 -19
- package/dist/cjs/core/Transition.js.map +1 -1
- package/dist/cjs/index.d.ts +7 -7
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +8 -8
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +8 -8
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/ai.d.ts +2 -2
- package/dist/cjs/types/ai.d.ts.map +1 -1
- package/dist/cjs/types/history.d.ts +3 -3
- package/dist/cjs/types/history.d.ts.map +1 -1
- package/dist/cjs/types/index.d.ts +1 -1
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/persistence.d.ts +5 -5
- package/dist/cjs/types/persistence.d.ts.map +1 -1
- package/dist/cjs/types/route.d.ts +57 -52
- package/dist/cjs/types/route.d.ts.map +1 -1
- package/dist/cjs/types/session.d.ts +27 -27
- package/dist/cjs/types/session.d.ts.map +1 -1
- package/dist/cjs/types/session.js +48 -50
- package/dist/cjs/types/session.js.map +1 -1
- package/dist/cjs/types/tool.d.ts +13 -13
- package/dist/cjs/types/tool.d.ts.map +1 -1
- package/dist/cjs/utils/id.d.ts +8 -3
- package/dist/cjs/utils/id.d.ts.map +1 -1
- package/dist/cjs/utils/id.js +16 -7
- package/dist/cjs/utils/id.js.map +1 -1
- package/dist/constants/index.d.ts +4 -4
- package/dist/constants/index.js +4 -4
- package/dist/core/Agent.d.ts +22 -22
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +162 -154
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/Events.d.ts +6 -6
- package/dist/core/Events.d.ts.map +1 -1
- package/dist/core/PersistenceManager.d.ts +13 -13
- package/dist/core/PersistenceManager.d.ts.map +1 -1
- package/dist/core/PersistenceManager.js +25 -25
- package/dist/core/PersistenceManager.js.map +1 -1
- package/dist/core/ResponseEngine.d.ts +3 -8
- package/dist/core/ResponseEngine.d.ts.map +1 -1
- package/dist/core/ResponseEngine.js +8 -8
- package/dist/core/ResponseEngine.js.map +1 -1
- package/dist/core/Route.d.ts +17 -17
- package/dist/core/Route.d.ts.map +1 -1
- package/dist/core/Route.js +33 -33
- package/dist/core/Route.js.map +1 -1
- package/dist/core/RoutingEngine.d.ts +30 -30
- package/dist/core/RoutingEngine.d.ts.map +1 -1
- package/dist/core/RoutingEngine.js +193 -193
- package/dist/core/RoutingEngine.js.map +1 -1
- package/dist/core/Step.d.ts +72 -0
- package/dist/core/Step.d.ts.map +1 -0
- package/dist/core/Step.js +146 -0
- package/dist/core/Step.js.map +1 -0
- package/dist/core/ToolExecutor.d.ts +5 -5
- package/dist/core/ToolExecutor.d.ts.map +1 -1
- package/dist/core/ToolExecutor.js +8 -8
- package/dist/core/ToolExecutor.js.map +1 -1
- package/dist/core/Transition.d.ts +14 -14
- package/dist/core/Transition.d.ts.map +1 -1
- package/dist/core/Transition.js +48 -19
- package/dist/core/Transition.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/types/agent.d.ts +8 -8
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/ai.d.ts +2 -2
- package/dist/types/ai.d.ts.map +1 -1
- package/dist/types/history.d.ts +3 -3
- package/dist/types/history.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/persistence.d.ts +5 -5
- package/dist/types/persistence.d.ts.map +1 -1
- package/dist/types/route.d.ts +57 -52
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/session.d.ts +27 -27
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js +44 -46
- package/dist/types/session.js.map +1 -1
- package/dist/types/tool.d.ts +13 -13
- package/dist/types/tool.d.ts.map +1 -1
- package/dist/utils/id.d.ts +8 -3
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +14 -6
- package/dist/utils/id.js.map +1 -1
- package/docs/ADAPTERS.md +21 -21
- package/docs/AGENT.md +57 -55
- package/docs/API_REFERENCE.md +218 -220
- package/docs/ARCHITECTURE.md +99 -104
- package/docs/CONTEXT_MANAGEMENT.md +81 -88
- package/docs/DOCS.md +18 -18
- package/docs/DOMAINS.md +16 -16
- package/docs/EXAMPLES.md +43 -43
- package/docs/GETTING_STARTED.md +60 -63
- package/docs/PERSISTENCE.md +66 -70
- package/docs/PROVIDERS.md +2 -2
- package/docs/README.md +6 -6
- package/docs/ROUTES.md +218 -220
- package/docs/STEPS.md +883 -0
- package/examples/business-onboarding.ts +84 -81
- package/examples/company-qna-agent.ts +68 -67
- package/examples/custom-database-persistence.ts +87 -89
- package/examples/declarative-agent.ts +32 -32
- package/examples/domain-scoping.ts +18 -18
- package/examples/extracted-data-modification.ts +92 -97
- package/examples/healthcare-agent.ts +89 -91
- package/examples/openai-agent.ts +29 -32
- package/examples/opensearch-persistence.ts +43 -45
- package/examples/persistent-onboarding.ts +65 -66
- package/examples/prisma-persistence.ts +108 -112
- package/examples/prisma-schema.example.prisma +3 -3
- package/examples/redis-persistence.ts +67 -73
- package/examples/route-transitions.ts +71 -47
- package/examples/rules-prohibitions.ts +28 -28
- package/examples/streaming-agent.ts +24 -24
- package/examples/travel-agent.ts +94 -109
- package/package.json +1 -1
- package/src/adapters/MemoryAdapter.ts +3 -3
- package/src/adapters/MongoAdapter.ts +3 -3
- package/src/adapters/OpenSearchAdapter.ts +8 -8
- package/src/adapters/PostgreSQLAdapter.ts +10 -10
- package/src/adapters/PrismaAdapter.ts +4 -4
- package/src/adapters/RedisAdapter.ts +3 -3
- package/src/adapters/SQLiteAdapter.ts +15 -15
- package/src/adapters/index.ts +1 -1
- package/src/constants/index.ts +4 -4
- package/src/core/Agent.ts +210 -206
- package/src/core/Events.ts +12 -12
- package/src/core/PersistenceManager.ts +32 -36
- package/src/core/ResponseEngine.ts +11 -17
- package/src/core/Route.ts +55 -49
- package/src/core/RoutingEngine.ts +244 -252
- package/src/core/Step.ts +197 -0
- package/src/core/ToolExecutor.ts +11 -11
- package/src/core/Transition.ts +72 -26
- package/src/index.ts +8 -8
- package/src/types/agent.ts +8 -8
- package/src/types/ai.ts +2 -2
- package/src/types/history.ts +3 -3
- package/src/types/index.ts +1 -1
- package/src/types/persistence.ts +6 -6
- package/src/types/route.ts +77 -61
- package/src/types/session.ts +75 -78
- package/src/types/tool.ts +17 -17
- package/src/utils/id.ts +15 -6
- package/dist/cjs/core/State.d.ts +0 -72
- package/dist/cjs/core/State.d.ts.map +0 -1
- package/dist/cjs/core/State.js +0 -148
- package/dist/cjs/core/State.js.map +0 -1
- package/dist/core/State.d.ts +0 -72
- package/dist/core/State.d.ts.map +0 -1
- package/dist/core/State.js +0 -144
- package/dist/core/State.js.map +0 -1
- package/docs/STATES.md +0 -888
- package/src/core/State.ts +0 -212
package/docs/ROUTES.md
CHANGED
|
@@ -8,8 +8,8 @@ A complete guide to creating and managing conversational routes in `@falai/agent
|
|
|
8
8
|
|
|
9
9
|
- [What is a Route?](#what-is-a-route)
|
|
10
10
|
- [Creating Routes](#creating-routes)
|
|
11
|
-
- [Initial
|
|
12
|
-
- [End
|
|
11
|
+
- [Initial Step Configuration](#initial-step-configuration)
|
|
12
|
+
- [End Step Configuration](#end-step-configuration)
|
|
13
13
|
- [Data Extraction](#data-extraction)
|
|
14
14
|
- [Sequential Steps](#sequential-steps)
|
|
15
15
|
- [Route Properties](#route-properties)
|
|
@@ -20,7 +20,7 @@ A complete guide to creating and managing conversational routes in `@falai/agent
|
|
|
20
20
|
|
|
21
21
|
## What is a Route?
|
|
22
22
|
|
|
23
|
-
A **Route** (also called a "Journey") represents a specific conversational flow in your agent. Think of it as a
|
|
23
|
+
A **Route** (also called a "Journey") represents a specific conversational flow in your agent. Think of it as a step machine that guides the conversation through a series of steps to accomplish a specific goal.
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
26
|
// Example: A route for booking flights
|
|
@@ -34,7 +34,7 @@ const bookingRoute = agent.createRoute<FlightData>({
|
|
|
34
34
|
**Key Concepts:**
|
|
35
35
|
|
|
36
36
|
- Each route has a **goal** (e.g., "book a flight", "answer FAQ", "collect feedback")
|
|
37
|
-
- Routes contain **
|
|
37
|
+
- Routes contain **steps** that represent conversation steps
|
|
38
38
|
- Routes can extract and track **typed data** throughout the conversation
|
|
39
39
|
- Routes have their own **rules**, **prohibitions**, and **guidelines**
|
|
40
40
|
|
|
@@ -70,7 +70,7 @@ const bookingRoute = agent.createRoute<FlightData>({
|
|
|
70
70
|
conditions: ["User wants to book a flight"],
|
|
71
71
|
|
|
72
72
|
// Define what data to extract
|
|
73
|
-
|
|
73
|
+
schema: {
|
|
74
74
|
type: "object",
|
|
75
75
|
properties: {
|
|
76
76
|
destination: {
|
|
@@ -117,9 +117,9 @@ const paymentRoute = agent.createRoute({
|
|
|
117
117
|
|
|
118
118
|
---
|
|
119
119
|
|
|
120
|
-
## Initial
|
|
120
|
+
## Initial Step Configuration
|
|
121
121
|
|
|
122
|
-
Every route starts with an initial
|
|
122
|
+
Every route starts with an initial step. You can now configure it in two ways:
|
|
123
123
|
|
|
124
124
|
### Option 1: Configure at Route Creation
|
|
125
125
|
|
|
@@ -127,16 +127,16 @@ Every route starts with an initial state. You can now configure it in two ways:
|
|
|
127
127
|
const bookingRoute = agent.createRoute<FlightData>({
|
|
128
128
|
title: "Book Flight",
|
|
129
129
|
|
|
130
|
-
// Configure the initial
|
|
131
|
-
|
|
132
|
-
id: "
|
|
133
|
-
|
|
130
|
+
// Configure the initial step
|
|
131
|
+
initialStep: {
|
|
132
|
+
id: "welcome_step",
|
|
133
|
+
instructions:
|
|
134
134
|
"Welcome! I'll help you book a flight. Where would you like to go?",
|
|
135
|
-
|
|
136
|
-
skipIf: (
|
|
135
|
+
collect: ["destination"],
|
|
136
|
+
skipIf: (data) => !!data.destination,
|
|
137
137
|
},
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
schema: {
|
|
140
140
|
// ... schema definition
|
|
141
141
|
},
|
|
142
142
|
});
|
|
@@ -150,160 +150,158 @@ const bookingRoute = agent.createRoute<FlightData>({
|
|
|
150
150
|
// ... other options
|
|
151
151
|
});
|
|
152
152
|
|
|
153
|
-
// Configure initial
|
|
154
|
-
bookingRoute.
|
|
153
|
+
// Configure initial step later
|
|
154
|
+
bookingRoute.initialStep.configure({
|
|
155
155
|
description: "Welcome! Let's book your flight",
|
|
156
|
-
|
|
157
|
-
skipIf: (
|
|
156
|
+
collectFields: ["destination"],
|
|
157
|
+
skipIf: (data) => !!data.destination,
|
|
158
158
|
});
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
### Initial
|
|
161
|
+
### Initial Step Options
|
|
162
162
|
|
|
163
163
|
```typescript
|
|
164
|
-
|
|
165
|
-
// Custom ID for the initial
|
|
164
|
+
initialStep: {
|
|
165
|
+
// Custom ID for the initial step
|
|
166
166
|
id?: string;
|
|
167
167
|
|
|
168
|
-
// Description/prompt for the initial
|
|
169
|
-
|
|
168
|
+
// Description/prompt for the initial step
|
|
169
|
+
instructions?: string;
|
|
170
170
|
|
|
171
|
-
// Fields to extract in this
|
|
172
|
-
|
|
171
|
+
// Fields to extract in this step
|
|
172
|
+
collect?: string[];
|
|
173
173
|
|
|
174
|
-
// Skip this
|
|
175
|
-
skipIf?: (
|
|
174
|
+
// Skip this step if condition is met
|
|
175
|
+
skipIf?: (data: Partial<TData>) => boolean;
|
|
176
176
|
|
|
177
177
|
// Prerequisites that must be metw
|
|
178
|
-
|
|
178
|
+
requires?: string[];
|
|
179
179
|
}
|
|
180
180
|
```
|
|
181
181
|
|
|
182
182
|
---
|
|
183
183
|
|
|
184
|
-
## End
|
|
184
|
+
## End Step Configuration
|
|
185
185
|
|
|
186
|
-
Every route ends when it reaches `
|
|
186
|
+
Every route ends when it reaches `END_ROUTE`. You can configure what happens at route completion:
|
|
187
187
|
|
|
188
|
-
### Configure End
|
|
188
|
+
### Configure End Step at Route Creation
|
|
189
189
|
|
|
190
190
|
```typescript
|
|
191
|
-
import {
|
|
191
|
+
import { END_ROUTE } from "@falai/agent";
|
|
192
192
|
|
|
193
193
|
const bookingRoute = agent.createRoute<FlightData>({
|
|
194
194
|
title: "Book Flight",
|
|
195
195
|
|
|
196
196
|
// Configure what happens when route completes
|
|
197
|
-
|
|
197
|
+
endStep: {
|
|
198
198
|
// Custom completion message
|
|
199
|
-
|
|
199
|
+
instructions: "Confirm the booking details and thank the user warmly!",
|
|
200
200
|
|
|
201
201
|
// Optional: Execute final actions (like sending confirmation emails)
|
|
202
|
-
|
|
202
|
+
tool: sendConfirmationEmail,
|
|
203
203
|
|
|
204
|
-
// Optional:
|
|
205
|
-
|
|
204
|
+
// Optional: Collect final data before completing
|
|
205
|
+
collect: ["finalConfirmation"],
|
|
206
206
|
|
|
207
207
|
// Optional: Require certain data to be present
|
|
208
|
-
|
|
208
|
+
requires: ["destination", "departureDate"],
|
|
209
209
|
|
|
210
|
-
// Optional: Custom
|
|
210
|
+
// Optional: Custom step ID
|
|
211
211
|
id: "booking_complete",
|
|
212
212
|
},
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
schema: {
|
|
215
215
|
// ... schema definition
|
|
216
216
|
},
|
|
217
217
|
});
|
|
218
218
|
|
|
219
|
-
// Then just transition to
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
// Then just transition to END_ROUTE
|
|
220
|
+
lastStep.nextStep({
|
|
221
|
+
step: END_ROUTE,
|
|
222
222
|
});
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
### Per-Transition Override
|
|
226
226
|
|
|
227
|
-
You can also override the
|
|
227
|
+
You can also override the endStep configuration for specific transitions:
|
|
228
228
|
|
|
229
229
|
```typescript
|
|
230
|
-
// Use route-level
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
// Use route-level endStep
|
|
231
|
+
lastStep.nextStep({
|
|
232
|
+
step: END_ROUTE,
|
|
233
233
|
});
|
|
234
234
|
|
|
235
|
-
// OR override with custom
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
235
|
+
// OR override with custom instructions for this specific transition
|
|
236
|
+
lastStep.nextStep({
|
|
237
|
+
instructions: "Special completion message for this path!",
|
|
238
|
+
step: END_ROUTE,
|
|
239
239
|
});
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
-
### End
|
|
242
|
+
### End Step Options
|
|
243
243
|
|
|
244
244
|
```typescript
|
|
245
|
-
|
|
245
|
+
endStep: {
|
|
246
246
|
// Completion message instruction (RECOMMENDED)
|
|
247
|
-
|
|
247
|
+
instructions?: string;
|
|
248
248
|
|
|
249
249
|
// Execute final tools/actions before completion
|
|
250
|
-
|
|
250
|
+
tool?: ToolRef;
|
|
251
251
|
|
|
252
|
-
//
|
|
253
|
-
|
|
252
|
+
// Collect final data at completion
|
|
253
|
+
collect?: string[];
|
|
254
254
|
|
|
255
255
|
// Require specific data to be present
|
|
256
|
-
|
|
256
|
+
requires?: string[];
|
|
257
257
|
|
|
258
|
-
// Custom
|
|
258
|
+
// Custom step ID for debugging
|
|
259
259
|
id?: string;
|
|
260
260
|
}
|
|
261
261
|
```
|
|
262
262
|
|
|
263
263
|
### Default Behavior
|
|
264
264
|
|
|
265
|
-
If you don't configure `
|
|
265
|
+
If you don't configure `endStep`, a smart default is used:
|
|
266
266
|
|
|
267
267
|
```typescript
|
|
268
|
-
// Default
|
|
268
|
+
// Default endStep behavior:
|
|
269
269
|
{
|
|
270
|
-
|
|
270
|
+
instructions: "Summarize what was accomplished and confirm completion based on the conversation history and collected data";
|
|
271
271
|
}
|
|
272
272
|
```
|
|
273
273
|
|
|
274
|
-
### End
|
|
274
|
+
### End Step with Tools
|
|
275
275
|
|
|
276
276
|
Execute final actions when route completes:
|
|
277
277
|
|
|
278
278
|
```typescript
|
|
279
|
-
import { defineTool,
|
|
280
|
-
|
|
281
|
-
const sendConfirmation = defineTool(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return { data: "Confirmation sent" };
|
|
290
|
-
}
|
|
291
|
-
);
|
|
279
|
+
import { defineTool, END_ROUTE } from "@falai/agent";
|
|
280
|
+
|
|
281
|
+
const sendConfirmation = defineTool("send_confirmation", async ({ data }) => {
|
|
282
|
+
await emailService.send({
|
|
283
|
+
to: data.email,
|
|
284
|
+
subject: "Booking Confirmed",
|
|
285
|
+
body: `Your flight to ${data.destination} is confirmed!`,
|
|
286
|
+
});
|
|
287
|
+
return { data: "Confirmation sent" };
|
|
288
|
+
});
|
|
292
289
|
|
|
293
290
|
const bookingRoute = agent.createRoute({
|
|
294
291
|
title: "Book Flight",
|
|
295
292
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
293
|
+
endStep: {
|
|
294
|
+
tool: sendConfirmation, // Executes when route completes
|
|
295
|
+
instructions: "Your booking is complete! Confirmation email sent.",
|
|
299
296
|
},
|
|
300
297
|
});
|
|
301
298
|
```
|
|
302
299
|
|
|
303
300
|
**Key Points:**
|
|
304
|
-
|
|
301
|
+
|
|
302
|
+
- ✅ `endStep` is configured once at the route level (DRY principle)
|
|
305
303
|
- ✅ Can be overridden per-transition if needed
|
|
306
|
-
- ✅ Supports full
|
|
304
|
+
- ✅ Supports full step capabilities: `instructions`, `tool`, `collect`, `requires`
|
|
307
305
|
- ✅ Falls back to smart default if not configured
|
|
308
306
|
|
|
309
307
|
---
|
|
@@ -318,7 +316,7 @@ The extraction schema defines what data your route will collect:
|
|
|
318
316
|
const onboardingRoute = agent.createRoute<OnboardingData>({
|
|
319
317
|
title: "User Onboarding",
|
|
320
318
|
|
|
321
|
-
|
|
319
|
+
schema: {
|
|
322
320
|
type: "object",
|
|
323
321
|
properties: {
|
|
324
322
|
firstName: { type: "string" },
|
|
@@ -335,17 +333,17 @@ const onboardingRoute = agent.createRoute<OnboardingData>({
|
|
|
335
333
|
});
|
|
336
334
|
```
|
|
337
335
|
|
|
338
|
-
### Getting
|
|
336
|
+
### Getting Collected data
|
|
339
337
|
|
|
340
338
|
```typescript
|
|
341
|
-
// Get
|
|
342
|
-
const
|
|
339
|
+
// Get collected data from the route
|
|
340
|
+
const data = bookingRoute.getData(session);
|
|
343
341
|
|
|
344
|
-
console.log(
|
|
342
|
+
console.log(data);
|
|
345
343
|
// { destination: "Paris", departureDate: "2025-06-15", passengers: 2 }
|
|
346
344
|
|
|
347
345
|
// Only returns data if session is in this route
|
|
348
|
-
const otherRouteData = otherRoute.
|
|
346
|
+
const otherRouteData = otherRoute.getData(session);
|
|
349
347
|
// {} - empty if session is in a different route
|
|
350
348
|
```
|
|
351
349
|
|
|
@@ -363,12 +361,12 @@ const bookingRoute = agent.createRoute<FlightData>({
|
|
|
363
361
|
passengers: 1, // Default value
|
|
364
362
|
},
|
|
365
363
|
|
|
366
|
-
|
|
364
|
+
schema: {
|
|
367
365
|
// ... schema
|
|
368
366
|
},
|
|
369
367
|
});
|
|
370
368
|
|
|
371
|
-
//
|
|
369
|
+
// Steps with skipIf will automatically bypass if data exists
|
|
372
370
|
```
|
|
373
371
|
|
|
374
372
|
---
|
|
@@ -384,33 +382,33 @@ const feedbackRoute = agent.createRoute({
|
|
|
384
382
|
steps: [
|
|
385
383
|
{
|
|
386
384
|
id: "ask_rating",
|
|
387
|
-
|
|
388
|
-
|
|
385
|
+
instructions: "How would you rate your experience? (1-5 stars)",
|
|
386
|
+
collect: ["rating"],
|
|
389
387
|
},
|
|
390
388
|
{
|
|
391
389
|
id: "ask_liked",
|
|
392
|
-
|
|
393
|
-
|
|
390
|
+
instructions: "What did you like most?",
|
|
391
|
+
collect: ["likedMost"],
|
|
394
392
|
},
|
|
395
393
|
{
|
|
396
394
|
id: "ask_improve",
|
|
397
|
-
|
|
398
|
-
|
|
395
|
+
instructions: "What could we improve?",
|
|
396
|
+
collect: ["improvements"],
|
|
399
397
|
},
|
|
400
398
|
{
|
|
401
399
|
id: "thank_you",
|
|
402
|
-
|
|
400
|
+
instructions: "Thank you for your feedback! 🙏",
|
|
403
401
|
},
|
|
404
402
|
],
|
|
405
403
|
});
|
|
406
404
|
|
|
407
|
-
// Automatically chains:
|
|
405
|
+
// Automatically chains: initialStep → ask_rating → ask_liked → ask_improve → thank_you → END_ROUTE
|
|
408
406
|
```
|
|
409
407
|
|
|
410
408
|
**When to use steps vs manual chaining:**
|
|
411
409
|
|
|
412
410
|
- ✅ Use `steps` for: Linear flows, simple wizards, sequential data collection
|
|
413
|
-
- ✅ Use manual chaining for: Branching logic, conditional flows, complex
|
|
411
|
+
- ✅ Use manual chaining for: Branching logic, conditional flows, complex step machines
|
|
414
412
|
|
|
415
413
|
---
|
|
416
414
|
|
|
@@ -445,18 +443,18 @@ const routeRef = route.getRef();
|
|
|
445
443
|
console.log(routeRef); // { id: "route_book_flight_abc123" }
|
|
446
444
|
|
|
447
445
|
// Use reference to jump to specific routes
|
|
448
|
-
|
|
446
|
+
step.nextStep({ step: routeRef });
|
|
449
447
|
```
|
|
450
448
|
|
|
451
449
|
### Route Structure
|
|
452
450
|
|
|
453
451
|
```typescript
|
|
454
|
-
// Get all
|
|
455
|
-
const
|
|
456
|
-
console.log(
|
|
452
|
+
// Get all steps in the route
|
|
453
|
+
const steps = route.getAllSteps();
|
|
454
|
+
console.log(steps); // [Step, Step, Step, ...]
|
|
457
455
|
|
|
458
|
-
// Get specific
|
|
459
|
-
const
|
|
456
|
+
// Get specific step by ID
|
|
457
|
+
const step = route.getStep("ask_destination");
|
|
460
458
|
|
|
461
459
|
// Describe route structure
|
|
462
460
|
console.log(route.describe());
|
|
@@ -466,8 +464,8 @@ console.log(route.describe());
|
|
|
466
464
|
// Description: Help user book a flight
|
|
467
465
|
// Conditions: User wants to book a flight
|
|
468
466
|
//
|
|
469
|
-
//
|
|
470
|
-
// -
|
|
467
|
+
// Steps:
|
|
468
|
+
// - initial_step: Initial step
|
|
471
469
|
// -> ask_destination
|
|
472
470
|
// - ask_destination: Ask where they want to fly
|
|
473
471
|
// -> ask_dates
|
|
@@ -548,36 +546,36 @@ const supportRoute = agent.createRoute<SupportData>({
|
|
|
548
546
|
});
|
|
549
547
|
|
|
550
548
|
// Ask issue type
|
|
551
|
-
const askIssueType = supportRoute.
|
|
552
|
-
|
|
553
|
-
|
|
549
|
+
const askIssueType = supportRoute.initialStep.nextStep({
|
|
550
|
+
instructions: "What type of issue are you experiencing?",
|
|
551
|
+
collect: ["issueType"],
|
|
554
552
|
});
|
|
555
553
|
|
|
556
554
|
// Branch 1: Technical issues
|
|
557
|
-
const technicalFlow = askIssueType.
|
|
558
|
-
|
|
555
|
+
const technicalFlow = askIssueType.nextStep({
|
|
556
|
+
instructions: "Let me help with your technical issue...",
|
|
559
557
|
condition: "Issue type is technical",
|
|
560
558
|
});
|
|
561
559
|
|
|
562
560
|
// Branch 2: Billing issues
|
|
563
|
-
const billingFlow = askIssueType.
|
|
564
|
-
|
|
561
|
+
const billingFlow = askIssueType.nextStep({
|
|
562
|
+
instructions: "Let me help with your billing issue...",
|
|
565
563
|
condition: "Issue type is billing",
|
|
566
564
|
});
|
|
567
565
|
|
|
568
566
|
// Branch 3: General inquiries
|
|
569
|
-
const generalFlow = askIssueType.
|
|
570
|
-
|
|
567
|
+
const generalFlow = askIssueType.nextStep({
|
|
568
|
+
instructions: "Let me help with your inquiry...",
|
|
571
569
|
condition: "Issue type is general",
|
|
572
570
|
});
|
|
573
571
|
```
|
|
574
572
|
|
|
575
|
-
### Pattern 2: Conditional Skip
|
|
573
|
+
### Pattern 2: Conditional Skip Steps
|
|
576
574
|
|
|
577
575
|
```typescript
|
|
578
576
|
const checkoutRoute = agent.createRoute<CheckoutData>({
|
|
579
577
|
title: "Checkout",
|
|
580
|
-
|
|
578
|
+
schema: {
|
|
581
579
|
properties: {
|
|
582
580
|
hasAccount: { type: "boolean" },
|
|
583
581
|
email: { type: "string" },
|
|
@@ -588,22 +586,22 @@ const checkoutRoute = agent.createRoute<CheckoutData>({
|
|
|
588
586
|
});
|
|
589
587
|
|
|
590
588
|
// Skip login if user already has account
|
|
591
|
-
const login = checkoutRoute.
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
skipIf: (
|
|
589
|
+
const login = checkoutRoute.initialStep.nextStep({
|
|
590
|
+
instructions: "Please log in or continue as guest",
|
|
591
|
+
collect: ["hasAccount", "email"],
|
|
592
|
+
skipIf: (data) => data.hasAccount === true,
|
|
595
593
|
});
|
|
596
594
|
|
|
597
595
|
// Skip billing address if same as shipping
|
|
598
|
-
const shippingAddress = login.
|
|
599
|
-
|
|
600
|
-
|
|
596
|
+
const shippingAddress = login.nextStep({
|
|
597
|
+
instructions: "What's your shipping address?",
|
|
598
|
+
collect: ["shippingAddress"],
|
|
601
599
|
});
|
|
602
600
|
|
|
603
|
-
const billingAddress = shippingAddress.
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
skipIf: (
|
|
601
|
+
const billingAddress = shippingAddress.nextStep({
|
|
602
|
+
instructions: "Is your billing address the same as shipping?",
|
|
603
|
+
collect: ["billingAddress"],
|
|
604
|
+
skipIf: (data) => data.billingAddress !== undefined,
|
|
607
605
|
});
|
|
608
606
|
```
|
|
609
607
|
|
|
@@ -635,10 +633,10 @@ import { defineTool } from "@falai/agent";
|
|
|
635
633
|
|
|
636
634
|
const searchFlights = defineTool<MyContext, [], FlightResults>(
|
|
637
635
|
"search_flights",
|
|
638
|
-
async ({ context,
|
|
636
|
+
async ({ context, data }) => {
|
|
639
637
|
const flights = await api.searchFlights({
|
|
640
|
-
destination:
|
|
641
|
-
date:
|
|
638
|
+
destination: data.destination,
|
|
639
|
+
date: data.departureDate,
|
|
642
640
|
});
|
|
643
641
|
|
|
644
642
|
return {
|
|
@@ -653,21 +651,21 @@ const bookingRoute = agent.createRoute<FlightData>({
|
|
|
653
651
|
domains: ["booking"], // Ensure tool is in this domain
|
|
654
652
|
});
|
|
655
653
|
|
|
656
|
-
//
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
654
|
+
// Collect data
|
|
655
|
+
const collectDetails = bookingRoute.initialStep.nextStep({
|
|
656
|
+
instructions: "Where and when would you like to fly?",
|
|
657
|
+
collect: ["destination", "departureDate", "passengers"],
|
|
660
658
|
});
|
|
661
659
|
|
|
662
660
|
// Execute tool
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
661
|
+
const searchStep = collectDetails.nextStep({
|
|
662
|
+
tool: searchFlights,
|
|
663
|
+
requires: ["destination", "departureDate", "passengers"],
|
|
666
664
|
});
|
|
667
665
|
|
|
668
666
|
// Present results
|
|
669
|
-
const presentFlights =
|
|
670
|
-
|
|
667
|
+
const presentFlights = searchStep.nextStep({
|
|
668
|
+
instructions: "Here are available flights based on your search",
|
|
671
669
|
});
|
|
672
670
|
```
|
|
673
671
|
|
|
@@ -675,36 +673,36 @@ const presentFlights = searchState.transitionTo({
|
|
|
675
673
|
|
|
676
674
|
## Route Completion
|
|
677
675
|
|
|
678
|
-
When a route reaches its final
|
|
676
|
+
When a route reaches its final step and transitions to `END_ROUTE`, the agent returns `isRouteComplete: true` to signal that all required data has been collected.
|
|
679
677
|
|
|
680
678
|
### Ending a Route
|
|
681
679
|
|
|
682
|
-
Use the `
|
|
680
|
+
Use the `END_ROUTE` symbol to mark the end of a route:
|
|
683
681
|
|
|
684
682
|
```typescript
|
|
685
|
-
import {
|
|
683
|
+
import { END_ROUTE } from "@falai/agent";
|
|
686
684
|
|
|
687
685
|
const onboardingRoute = agent.createRoute<OnboardingData>({
|
|
688
686
|
title: "User Onboarding",
|
|
689
|
-
|
|
687
|
+
schema: ONBOARDING_SCHEMA,
|
|
690
688
|
});
|
|
691
689
|
|
|
692
|
-
const askName = onboardingRoute.
|
|
693
|
-
|
|
694
|
-
|
|
690
|
+
const askName = onboardingRoute.initialStep.nextStep({
|
|
691
|
+
instructions: "What's your name?",
|
|
692
|
+
collect: ["name"],
|
|
695
693
|
});
|
|
696
694
|
|
|
697
|
-
const askEmail = askName.
|
|
698
|
-
|
|
699
|
-
|
|
695
|
+
const askEmail = askName.nextStep({
|
|
696
|
+
instructions: "What's your email?",
|
|
697
|
+
collect: ["email"],
|
|
700
698
|
});
|
|
701
699
|
|
|
702
|
-
const thankYou = askEmail.
|
|
703
|
-
|
|
700
|
+
const thankYou = askEmail.nextStep({
|
|
701
|
+
instructions: "Thank you! Your profile is complete.",
|
|
704
702
|
});
|
|
705
703
|
|
|
706
704
|
// End the route
|
|
707
|
-
thankYou.
|
|
705
|
+
thankYou.nextStep({ step: END_ROUTE });
|
|
708
706
|
```
|
|
709
707
|
|
|
710
708
|
### Handling Completion
|
|
@@ -723,7 +721,7 @@ if (response.isRouteComplete) {
|
|
|
723
721
|
// ✅ Route is complete! All data has been collected
|
|
724
722
|
|
|
725
723
|
// Get the collected data
|
|
726
|
-
const data = agent.
|
|
724
|
+
const data = agent.getData(response.session!);
|
|
727
725
|
console.log("Collected data:", data);
|
|
728
726
|
|
|
729
727
|
// Process the data
|
|
@@ -738,21 +736,21 @@ if (response.isRouteComplete) {
|
|
|
738
736
|
}
|
|
739
737
|
```
|
|
740
738
|
|
|
741
|
-
#### Method 2: Using `
|
|
739
|
+
#### Method 2: Using `END_ROUTE_ID` Constant
|
|
742
740
|
|
|
743
741
|
For users who prefer a symbol-based pattern consistent with building routes:
|
|
744
742
|
|
|
745
743
|
```typescript
|
|
746
|
-
import {
|
|
744
|
+
import { END_ROUTE_ID } from "@falai/agent";
|
|
747
745
|
|
|
748
746
|
const response = await agent.respond({
|
|
749
747
|
history,
|
|
750
748
|
session,
|
|
751
749
|
});
|
|
752
750
|
|
|
753
|
-
if (response.session?.
|
|
754
|
-
// ✅ Route completed -
|
|
755
|
-
const data = agent.
|
|
751
|
+
if (response.session?.currentStep?.id === END_ROUTE_ID) {
|
|
752
|
+
// ✅ Route completed - currentStep is now END_ROUTE
|
|
753
|
+
const data = agent.getData(response.session!);
|
|
756
754
|
await handleCompletion(data);
|
|
757
755
|
return "Complete!";
|
|
758
756
|
}
|
|
@@ -763,7 +761,7 @@ return response.message;
|
|
|
763
761
|
**Which method should you use?**
|
|
764
762
|
|
|
765
763
|
- ✅ **Use `isRouteComplete`** for simplicity and clarity
|
|
766
|
-
- ✅ **Use `
|
|
764
|
+
- ✅ **Use `END_ROUTE_ID`** if you want consistency with how you build routes (`END_ROUTE` symbol)
|
|
767
765
|
|
|
768
766
|
---
|
|
769
767
|
|
|
@@ -784,7 +782,7 @@ The simplest form - just specify the target route ID or title:
|
|
|
784
782
|
const bookingRoute = agent.createRoute<BookingData>({
|
|
785
783
|
title: "Book Hotel",
|
|
786
784
|
conditions: ["User wants to book a hotel"],
|
|
787
|
-
|
|
785
|
+
schema: BOOKING_SCHEMA,
|
|
788
786
|
// Automatically transition to feedback when booking completes
|
|
789
787
|
onComplete: "Collect Feedback",
|
|
790
788
|
});
|
|
@@ -792,7 +790,7 @@ const bookingRoute = agent.createRoute<BookingData>({
|
|
|
792
790
|
const feedbackRoute = agent.createRoute<FeedbackData>({
|
|
793
791
|
title: "Collect Feedback",
|
|
794
792
|
conditions: ["Collect user feedback"],
|
|
795
|
-
|
|
793
|
+
schema: FEEDBACK_SCHEMA,
|
|
796
794
|
});
|
|
797
795
|
```
|
|
798
796
|
|
|
@@ -803,9 +801,9 @@ Add an optional condition that the AI evaluates to determine if transition shoul
|
|
|
803
801
|
```typescript
|
|
804
802
|
const bookingRoute = agent.createRoute<BookingData>({
|
|
805
803
|
title: "Book Hotel",
|
|
806
|
-
|
|
804
|
+
schema: BOOKING_SCHEMA,
|
|
807
805
|
onComplete: {
|
|
808
|
-
|
|
806
|
+
nextStep: "Collect Feedback",
|
|
809
807
|
condition: "if booking was successful", // AI evaluates this
|
|
810
808
|
},
|
|
811
809
|
});
|
|
@@ -813,19 +811,19 @@ const bookingRoute = agent.createRoute<BookingData>({
|
|
|
813
811
|
|
|
814
812
|
### Dynamic Function-Based Transition
|
|
815
813
|
|
|
816
|
-
Use a function for complex logic based on
|
|
814
|
+
Use a function for complex logic based on collected data or context:
|
|
817
815
|
|
|
818
816
|
```typescript
|
|
819
817
|
const bookingRoute = agent.createRoute<BookingData>({
|
|
820
818
|
title: "Book Hotel",
|
|
821
|
-
|
|
819
|
+
schema: BOOKING_SCHEMA,
|
|
822
820
|
// Function receives session and context
|
|
823
821
|
onComplete: (session, context) => {
|
|
824
|
-
// Conditional logic based on
|
|
825
|
-
if (session.
|
|
822
|
+
// Conditional logic based on collected data
|
|
823
|
+
if (session.data?.guests && session.data.guests > 5) {
|
|
826
824
|
return "VIP Feedback"; // Large groups get VIP treatment
|
|
827
825
|
}
|
|
828
|
-
if (session.
|
|
826
|
+
if (session.data?.bookingFailed) {
|
|
829
827
|
return "Error Recovery"; // Handle failures gracefully
|
|
830
828
|
}
|
|
831
829
|
return "Collect Feedback"; // Standard feedback flow
|
|
@@ -837,16 +835,16 @@ Function can also return a config object:
|
|
|
837
835
|
|
|
838
836
|
```typescript
|
|
839
837
|
onComplete: (session) => ({
|
|
840
|
-
|
|
841
|
-
condition: session.
|
|
842
|
-
})
|
|
838
|
+
nextStep: "Collect Feedback",
|
|
839
|
+
condition: session.data?.vip ? "if user is satisfied" : undefined,
|
|
840
|
+
});
|
|
843
841
|
```
|
|
844
842
|
|
|
845
843
|
### How It Works
|
|
846
844
|
|
|
847
|
-
1. **Route completes** → reaches `
|
|
845
|
+
1. **Route completes** → reaches `END_ROUTE`
|
|
848
846
|
2. **Agent evaluates** `onComplete` handler
|
|
849
|
-
3. **Sets pending transition** in session
|
|
847
|
+
3. **Sets pending transition** in session step
|
|
850
848
|
4. **Next `respond()` call** → automatically transitions to target route
|
|
851
849
|
5. **User sees seamless flow** → no interruption in conversation
|
|
852
850
|
|
|
@@ -864,14 +862,14 @@ console.log(response2.session?.currentRoute?.title); // "Collect Feedback"
|
|
|
864
862
|
|
|
865
863
|
### Manual Transition Control
|
|
866
864
|
|
|
867
|
-
For more control, use `agent.
|
|
865
|
+
For more control, use `agent.nextStepRoute()` to manually set the transition:
|
|
868
866
|
|
|
869
867
|
```typescript
|
|
870
868
|
const response = await agent.respond({ history, session });
|
|
871
869
|
|
|
872
870
|
if (response.isRouteComplete && shouldCollectFeedback) {
|
|
873
871
|
// Manually trigger transition instead of onComplete
|
|
874
|
-
const updatedSession = agent.
|
|
872
|
+
const updatedSession = agent.nextStepRoute(
|
|
875
873
|
"Collect Feedback",
|
|
876
874
|
response.session
|
|
877
875
|
);
|
|
@@ -902,7 +900,7 @@ interface FeedbackData {
|
|
|
902
900
|
const bookingRoute = agent.createRoute<BookingData>({
|
|
903
901
|
title: "Book Hotel",
|
|
904
902
|
conditions: ["User wants to book a hotel"],
|
|
905
|
-
|
|
903
|
+
schema: {
|
|
906
904
|
type: "object",
|
|
907
905
|
properties: {
|
|
908
906
|
hotelName: { type: "string" },
|
|
@@ -914,34 +912,34 @@ const bookingRoute = agent.createRoute<BookingData>({
|
|
|
914
912
|
onComplete: "Collect Feedback",
|
|
915
913
|
});
|
|
916
914
|
|
|
917
|
-
const askHotel = bookingRoute.
|
|
918
|
-
|
|
919
|
-
|
|
915
|
+
const askHotel = bookingRoute.initialStep.nextStep({
|
|
916
|
+
instructions: "Ask which hotel",
|
|
917
|
+
collect: ["hotelName"],
|
|
920
918
|
skipIf: (e) => !!e.hotelName,
|
|
921
919
|
});
|
|
922
920
|
|
|
923
|
-
const askDate = askHotel.
|
|
924
|
-
|
|
925
|
-
|
|
921
|
+
const askDate = askHotel.nextStep({
|
|
922
|
+
instructions: "Ask for date",
|
|
923
|
+
collect: ["date"],
|
|
926
924
|
skipIf: (e) => !!e.date,
|
|
927
925
|
});
|
|
928
926
|
|
|
929
|
-
const askGuests = askDate.
|
|
930
|
-
|
|
931
|
-
|
|
927
|
+
const askGuests = askDate.nextStep({
|
|
928
|
+
instructions: "Ask for guests",
|
|
929
|
+
collect: ["guests"],
|
|
932
930
|
skipIf: (e) => !!e.guests,
|
|
933
931
|
});
|
|
934
932
|
|
|
935
|
-
askGuests.
|
|
936
|
-
|
|
937
|
-
|
|
933
|
+
askGuests.nextStep({
|
|
934
|
+
instructions: "Confirm booking",
|
|
935
|
+
step: END_ROUTE,
|
|
938
936
|
});
|
|
939
937
|
|
|
940
938
|
// Feedback route
|
|
941
939
|
const feedbackRoute = agent.createRoute<FeedbackData>({
|
|
942
940
|
title: "Collect Feedback",
|
|
943
941
|
conditions: ["Collect user feedback"],
|
|
944
|
-
|
|
942
|
+
schema: {
|
|
945
943
|
type: "object",
|
|
946
944
|
properties: {
|
|
947
945
|
rating: { type: "number" },
|
|
@@ -951,14 +949,14 @@ const feedbackRoute = agent.createRoute<FeedbackData>({
|
|
|
951
949
|
},
|
|
952
950
|
});
|
|
953
951
|
|
|
954
|
-
const askRating = feedbackRoute.
|
|
955
|
-
|
|
956
|
-
|
|
952
|
+
const askRating = feedbackRoute.initialStep.nextStep({
|
|
953
|
+
instructions: "Ask for rating 1-5",
|
|
954
|
+
collect: ["rating"],
|
|
957
955
|
});
|
|
958
956
|
|
|
959
|
-
askRating.
|
|
960
|
-
|
|
961
|
-
|
|
957
|
+
askRating.nextStep({
|
|
958
|
+
instructions: "Thank user",
|
|
959
|
+
step: END_ROUTE,
|
|
962
960
|
});
|
|
963
961
|
|
|
964
962
|
// Usage - seamless transition from booking to feedback
|
|
@@ -976,14 +974,14 @@ const response2 = await agent.respond({ history, session: response1.session });
|
|
|
976
974
|
✅ **Seamless user experience** - No awkward pauses between flows
|
|
977
975
|
✅ **Predictable behavior** - Transitions defined at design time
|
|
978
976
|
✅ **Flexible** - Simple strings, conditions, or complex functions
|
|
979
|
-
✅ **Type-safe** - Full TypeScript inference for
|
|
977
|
+
✅ **Type-safe** - Full TypeScript inference for collected data
|
|
980
978
|
✅ **Non-breaking** - Existing routes without `onComplete` work as before
|
|
981
979
|
|
|
982
980
|
See [examples/route-transitions.ts](../examples/route-transitions.ts) for a complete working example.
|
|
983
981
|
|
|
984
982
|
### Immediate Completion
|
|
985
983
|
|
|
986
|
-
Routes can complete **immediately** if all
|
|
984
|
+
Routes can complete **immediately** if all steps are skipped due to `skipIf` conditions. This is useful when:
|
|
987
985
|
|
|
988
986
|
- Resuming a partially completed route
|
|
989
987
|
- Pre-filling data from an existing session
|
|
@@ -992,47 +990,47 @@ Routes can complete **immediately** if all states are skipped due to `skipIf` co
|
|
|
992
990
|
```typescript
|
|
993
991
|
const onboardingRoute = agent.createRoute<OnboardingData>({
|
|
994
992
|
title: "User Onboarding",
|
|
995
|
-
|
|
993
|
+
schema: ONBOARDING_SCHEMA,
|
|
996
994
|
initialData: existingUserData, // Pre-fill with existing data
|
|
997
995
|
});
|
|
998
996
|
|
|
999
|
-
const askName = onboardingRoute.
|
|
1000
|
-
|
|
1001
|
-
|
|
997
|
+
const askName = onboardingRoute.initialStep.nextStep({
|
|
998
|
+
instructions: "What's your name?",
|
|
999
|
+
collect: ["name"],
|
|
1002
1000
|
skipIf: (data) => !!data.name, // Skip if name exists
|
|
1003
1001
|
});
|
|
1004
1002
|
|
|
1005
|
-
const askEmail = askName.
|
|
1006
|
-
|
|
1007
|
-
|
|
1003
|
+
const askEmail = askName.nextStep({
|
|
1004
|
+
instructions: "What's your email?",
|
|
1005
|
+
collect: ["email"],
|
|
1008
1006
|
skipIf: (data) => !!data.email, // Skip if email exists
|
|
1009
1007
|
});
|
|
1010
1008
|
|
|
1011
|
-
const complete = askEmail.
|
|
1012
|
-
|
|
1009
|
+
const complete = askEmail.nextStep({
|
|
1010
|
+
instructions: "All done!",
|
|
1013
1011
|
});
|
|
1014
1012
|
|
|
1015
|
-
complete.
|
|
1013
|
+
complete.nextStep({ step: END_ROUTE });
|
|
1016
1014
|
|
|
1017
1015
|
// In your handler:
|
|
1018
1016
|
const response = await agent.respond({ history, session });
|
|
1019
1017
|
|
|
1020
1018
|
if (response.isRouteComplete) {
|
|
1021
1019
|
// If existingUserData had all fields, route completes immediately!
|
|
1022
|
-
// The routing engine recursively skips all
|
|
1020
|
+
// The routing engine recursively skips all steps and reaches END_ROUTE
|
|
1023
1021
|
console.log("Profile already complete!");
|
|
1024
1022
|
}
|
|
1025
1023
|
```
|
|
1026
1024
|
|
|
1027
1025
|
### Important Notes
|
|
1028
1026
|
|
|
1029
|
-
- **`isRouteComplete: true`** indicates the route has reached `
|
|
1030
|
-
- **`
|
|
1027
|
+
- **`isRouteComplete: true`** indicates the route has reached `END_ROUTE`
|
|
1028
|
+
- **`currentStep.id`** is set to `END_ROUTE_ID` when the route completes
|
|
1031
1029
|
- **`response.message`** will be empty (`""`) when route is complete
|
|
1032
|
-
- **The routing engine** recursively traverses skipped
|
|
1033
|
-
- **You can check either** `isRouteComplete` or `
|
|
1034
|
-
- **Session
|
|
1035
|
-
- **The route** (`currentRoute.id`) remains the completed route (e.g., "onboarding"), not
|
|
1030
|
+
- **The routing engine** recursively traverses skipped steps to detect completion
|
|
1031
|
+
- **You can check either** `isRouteComplete` or `currentStep.id === END_ROUTE_ID`
|
|
1032
|
+
- **Session step** still contains all the collected data via `agent.getData()`
|
|
1033
|
+
- **The route** (`currentRoute.id`) remains the completed route (e.g., "onboarding"), not END_ROUTE
|
|
1036
1034
|
|
|
1037
1035
|
### With Streaming
|
|
1038
1036
|
|
|
@@ -1044,7 +1042,7 @@ for await (const chunk of agent.respondStream({ history, session })) {
|
|
|
1044
1042
|
|
|
1045
1043
|
if (chunk.done && chunk.isRouteComplete) {
|
|
1046
1044
|
console.log("\n🎉 Route completed!");
|
|
1047
|
-
const data = agent.
|
|
1045
|
+
const data = agent.getData(chunk.session!);
|
|
1048
1046
|
await processCompletedData(data);
|
|
1049
1047
|
}
|
|
1050
1048
|
}
|
|
@@ -1058,7 +1056,7 @@ for await (const chunk of agent.respondStream({ history, session })) {
|
|
|
1058
1056
|
|
|
1059
1057
|
- **Use descriptive titles and descriptions** - Makes routing more accurate
|
|
1060
1058
|
- **Define extraction schemas** - Type-safe data collection
|
|
1061
|
-
- **Configure initial
|
|
1059
|
+
- **Configure initial step** - Set up proper entry point
|
|
1062
1060
|
- **Use skipIf for known data** - Avoid redundant questions
|
|
1063
1061
|
- **Scope domains** - Limit tool access per route
|
|
1064
1062
|
- **Add route-specific guidelines** - Context-aware behavior
|
|
@@ -1069,15 +1067,15 @@ for await (const chunk of agent.respondStream({ history, session })) {
|
|
|
1069
1067
|
- **Don't use vague conditions** - Be specific about when to activate
|
|
1070
1068
|
- **Don't skip extraction schemas** - Loses type safety
|
|
1071
1069
|
- **Don't create overly complex routes** - Split into multiple routes
|
|
1072
|
-
- **Don't forget
|
|
1070
|
+
- **Don't forget requires** - Prevent steps from executing too early
|
|
1073
1071
|
- **Don't mix concerns** - One route = one goal
|
|
1074
|
-
- **Don't hardcode
|
|
1072
|
+
- **Don't hardcode step IDs** - Let framework generate deterministic IDs
|
|
1075
1073
|
|
|
1076
1074
|
---
|
|
1077
1075
|
|
|
1078
1076
|
## See Also
|
|
1079
1077
|
|
|
1080
|
-
- [
|
|
1078
|
+
- [Steps Guide](./STEPS.md) - Deep dive into step management
|
|
1081
1079
|
- [API Reference - Route](./API_REFERENCE.md#route) - Complete API docs
|
|
1082
1080
|
- [Examples](../examples/) - Real-world route implementations
|
|
1083
1081
|
- [Architecture Guide](./ARCHITECTURE.md) - How routes fit in the system
|