@falai/agent 0.6.2 → 0.6.4
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 +89 -56
- package/dist/cjs/constants/index.d.ts +6 -1
- package/dist/cjs/constants/index.d.ts.map +1 -1
- package/dist/cjs/constants/index.js +8 -3
- package/dist/cjs/constants/index.js.map +1 -1
- package/dist/cjs/core/Agent.d.ts +22 -0
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +108 -21
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/Events.d.ts +13 -0
- package/dist/cjs/core/Events.d.ts.map +1 -1
- package/dist/cjs/core/Events.js +28 -14
- package/dist/cjs/core/Events.js.map +1 -1
- package/dist/cjs/core/Route.d.ts.map +1 -1
- package/dist/cjs/core/Route.js +4 -4
- package/dist/cjs/core/Route.js.map +1 -1
- package/dist/cjs/core/RoutingEngine.d.ts +6 -1
- package/dist/cjs/core/RoutingEngine.d.ts.map +1 -1
- package/dist/cjs/core/RoutingEngine.js +112 -37
- package/dist/cjs/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/core/State.d.ts +15 -5
- package/dist/cjs/core/State.d.ts.map +1 -1
- package/dist/cjs/core/State.js +24 -5
- package/dist/cjs/core/State.js.map +1 -1
- package/dist/cjs/core/Tool.d.ts +8 -1
- package/dist/cjs/core/Tool.d.ts.map +1 -1
- package/dist/cjs/core/Tool.js +25 -28
- package/dist/cjs/core/Tool.js.map +1 -1
- package/dist/cjs/core/Transition.js +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +3 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +5 -0
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/agent.js.map +1 -1
- package/dist/cjs/types/route.d.ts +7 -1
- package/dist/cjs/types/route.d.ts.map +1 -1
- package/dist/cjs/types/session.d.ts +12 -1
- package/dist/cjs/types/session.d.ts.map +1 -1
- package/dist/cjs/types/session.js +26 -5
- package/dist/cjs/types/session.js.map +1 -1
- package/dist/cjs/utils/logger.d.ts +10 -0
- package/dist/cjs/utils/logger.d.ts.map +1 -0
- package/dist/cjs/utils/logger.js +23 -0
- package/dist/cjs/utils/logger.js.map +1 -0
- package/dist/constants/index.d.ts +6 -1
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +6 -1
- package/dist/constants/index.js.map +1 -1
- package/dist/core/Agent.d.ts +22 -0
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +108 -21
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/Events.d.ts +13 -0
- package/dist/core/Events.d.ts.map +1 -1
- package/dist/core/Events.js +28 -14
- package/dist/core/Events.js.map +1 -1
- package/dist/core/Route.d.ts.map +1 -1
- package/dist/core/Route.js +4 -4
- package/dist/core/Route.js.map +1 -1
- package/dist/core/RoutingEngine.d.ts +6 -1
- package/dist/core/RoutingEngine.d.ts.map +1 -1
- package/dist/core/RoutingEngine.js +112 -37
- package/dist/core/RoutingEngine.js.map +1 -1
- package/dist/core/State.d.ts +15 -5
- package/dist/core/State.d.ts.map +1 -1
- package/dist/core/State.js +25 -6
- package/dist/core/State.js.map +1 -1
- package/dist/core/Tool.d.ts +8 -1
- package/dist/core/Tool.d.ts.map +1 -1
- package/dist/core/Tool.js +25 -28
- package/dist/core/Tool.js.map +1 -1
- package/dist/core/Transition.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/agent.d.ts +5 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/agent.js.map +1 -1
- package/dist/types/route.d.ts +7 -1
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/session.d.ts +12 -1
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js +26 -5
- package/dist/types/session.js.map +1 -1
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +17 -0
- package/dist/utils/logger.js.map +1 -0
- package/docs/{CONSTRUCTOR_OPTIONS.md → AGENT.md} +79 -7
- package/docs/API_REFERENCE.md +309 -18
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/DOCS.md +46 -22
- package/docs/GETTING_STARTED.md +1 -1
- package/docs/README.md +13 -5
- package/docs/ROUTES.md +743 -0
- package/docs/STATES.md +798 -0
- package/examples/business-onboarding.ts +46 -5
- package/examples/company-qna-agent.ts +107 -1
- package/examples/custom-database-persistence.ts +44 -1
- package/examples/declarative-agent.ts +80 -37
- package/examples/domain-scoping.ts +91 -21
- package/examples/extracted-data-modification.ts +64 -2
- package/examples/healthcare-agent.ts +61 -4
- package/examples/openai-agent.ts +24 -2
- package/examples/opensearch-persistence.ts +26 -1
- package/examples/persistent-onboarding.ts +84 -18
- package/examples/prisma-persistence.ts +90 -16
- package/examples/redis-persistence.ts +89 -17
- package/examples/rules-prohibitions.ts +300 -139
- package/examples/streaming-agent.ts +60 -0
- package/examples/travel-agent.ts +66 -24
- package/package.json +3 -2
- package/src/constants/index.ts +6 -1
- package/src/core/Agent.ts +135 -21
- package/src/core/Events.ts +73 -10
- package/src/core/Route.ts +8 -4
- package/src/core/RoutingEngine.ts +150 -39
- package/src/core/State.ts +35 -10
- package/src/core/Tool.ts +67 -10
- package/src/core/Transition.ts +1 -1
- package/src/index.ts +1 -1
- package/src/types/agent.ts +5 -0
- package/src/types/route.ts +10 -1
- package/src/types/session.ts +42 -6
- package/src/utils/logger.ts +19 -0
package/docs/STATES.md
ADDED
|
@@ -0,0 +1,798 @@
|
|
|
1
|
+
# States Guide
|
|
2
|
+
|
|
3
|
+
A complete guide to creating and managing states in conversational flows.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [What is a State?](#what-is-a-state)
|
|
10
|
+
- [Creating States](#creating-states)
|
|
11
|
+
- [State Configuration](#state-configuration)
|
|
12
|
+
- [Transitions](#transitions)
|
|
13
|
+
- [Data Gathering](#data-gathering)
|
|
14
|
+
- [State Logic](#state-logic)
|
|
15
|
+
- [State Types](#state-types)
|
|
16
|
+
- [Advanced Patterns](#advanced-patterns)
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## What is a State?
|
|
21
|
+
|
|
22
|
+
A **State** represents a specific step or moment in a conversation. Each state can:
|
|
23
|
+
|
|
24
|
+
- Display a message to the user (chat state)
|
|
25
|
+
- Execute a tool (tool state)
|
|
26
|
+
- Gather data from the conversation
|
|
27
|
+
- Make decisions about what to do next
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// Example: A state that asks for user's name
|
|
31
|
+
const askName = route.initialState.transitionTo({
|
|
32
|
+
chatState: "What's your name?",
|
|
33
|
+
gather: ["firstName", "lastName"],
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Key Concepts:**
|
|
38
|
+
|
|
39
|
+
- States form a **state machine** within a route
|
|
40
|
+
- Each state has a unique **ID** (auto-generated or custom)
|
|
41
|
+
- States can **gather data** from user responses
|
|
42
|
+
- States can be **skipped** based on conditions
|
|
43
|
+
- States can have **prerequisites** (required data)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Creating States
|
|
48
|
+
|
|
49
|
+
### Chat States
|
|
50
|
+
|
|
51
|
+
Chat states present a message and optionally gather data:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// Simple chat state
|
|
55
|
+
const welcome = route.initialState.transitionTo({
|
|
56
|
+
chatState: "Welcome! How can I help you today?",
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Chat state with data gathering
|
|
60
|
+
const askDestination = welcome.transitionTo({
|
|
61
|
+
chatState: "Where would you like to fly?",
|
|
62
|
+
gather: ["destination"],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Chat state with custom ID
|
|
66
|
+
const askDates = askDestination.transitionTo({
|
|
67
|
+
id: "ask_travel_dates",
|
|
68
|
+
chatState: "When would you like to depart?",
|
|
69
|
+
gather: ["departureDate"],
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Tool States
|
|
74
|
+
|
|
75
|
+
Tool states execute functions and can update context or extracted data:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { defineTool } from "@falai/agent";
|
|
79
|
+
|
|
80
|
+
const searchFlights = defineTool<Context, [], Results>(
|
|
81
|
+
"search_flights",
|
|
82
|
+
async ({ context, extracted }) => {
|
|
83
|
+
const results = await api.search(extracted.destination);
|
|
84
|
+
return {
|
|
85
|
+
data: results,
|
|
86
|
+
contextUpdate: { availableFlights: results },
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Tool state
|
|
92
|
+
const searchState = askDates.transitionTo({
|
|
93
|
+
toolState: searchFlights,
|
|
94
|
+
requiredData: ["destination", "departureDate"],
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Direct State References
|
|
99
|
+
|
|
100
|
+
Jump to specific states or end the route:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { END_STATE } from "@falai/agent";
|
|
104
|
+
|
|
105
|
+
// Jump to another state
|
|
106
|
+
const confirm = processPayment.transitionTo({
|
|
107
|
+
state: previousState.getRef(), // Jump back
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// End the route
|
|
111
|
+
const complete = confirm.transitionTo({
|
|
112
|
+
state: END_STATE,
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## State Configuration
|
|
119
|
+
|
|
120
|
+
### Configuring Initial State
|
|
121
|
+
|
|
122
|
+
Every route has an initial state that can be configured:
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Option 1: Configure at route creation
|
|
126
|
+
const route = agent.createRoute({
|
|
127
|
+
title: "Booking",
|
|
128
|
+
initialState: {
|
|
129
|
+
id: "welcome",
|
|
130
|
+
chatState: "Welcome to our booking system!",
|
|
131
|
+
gather: ["intention"],
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Option 2: Configure after creation
|
|
136
|
+
route.initialState.configure({
|
|
137
|
+
description: "Welcome! Let's start booking",
|
|
138
|
+
gatherFields: ["destination"],
|
|
139
|
+
skipIf: (extracted) => !!extracted.destination,
|
|
140
|
+
requiredData: [],
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Configuring Any State
|
|
145
|
+
|
|
146
|
+
You can configure any state after creation:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
const askName = route.initialState.transitionTo({
|
|
150
|
+
chatState: "What's your name?",
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Later, reconfigure it
|
|
154
|
+
askName.configure({
|
|
155
|
+
description: "Ask for user's full name",
|
|
156
|
+
gatherFields: ["firstName", "lastName"],
|
|
157
|
+
skipIf: (extracted) => !!extracted.firstName && !!extracted.lastName,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Chaining is supported
|
|
161
|
+
askName
|
|
162
|
+
.configure({ description: "Updated description" })
|
|
163
|
+
.configure({ gatherFields: ["fullName"] });
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Configuration Options
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
state.configure({
|
|
170
|
+
// State description
|
|
171
|
+
description?: string;
|
|
172
|
+
|
|
173
|
+
// Fields to gather from conversation
|
|
174
|
+
gatherFields?: string[];
|
|
175
|
+
|
|
176
|
+
// Skip this state if condition is met
|
|
177
|
+
skipIf?: (extracted: Partial<TExtracted>) => boolean;
|
|
178
|
+
|
|
179
|
+
// Prerequisites that must be met before entering
|
|
180
|
+
requiredData?: string[];
|
|
181
|
+
});
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Transitions
|
|
187
|
+
|
|
188
|
+
### Transition Specification
|
|
189
|
+
|
|
190
|
+
Every transition from one state to another uses a `TransitionSpec`:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
interface TransitionSpec<TExtracted = unknown> {
|
|
194
|
+
// Custom state ID (optional)
|
|
195
|
+
id?: string;
|
|
196
|
+
|
|
197
|
+
// Chat state description
|
|
198
|
+
chatState?: string;
|
|
199
|
+
|
|
200
|
+
// Tool to execute
|
|
201
|
+
toolState?: ToolRef;
|
|
202
|
+
|
|
203
|
+
// Direct state reference or END_STATE
|
|
204
|
+
state?: StateRef | symbol;
|
|
205
|
+
|
|
206
|
+
// Fields to gather in this state
|
|
207
|
+
gather?: string[];
|
|
208
|
+
|
|
209
|
+
// Skip condition (code-based)
|
|
210
|
+
skipIf?: (extracted: Partial<TExtracted>) => boolean;
|
|
211
|
+
|
|
212
|
+
// Prerequisites
|
|
213
|
+
requiredData?: string[];
|
|
214
|
+
|
|
215
|
+
// AI-evaluated condition (for state selection)
|
|
216
|
+
condition?: string;
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Transition Chaining
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// Linear flow
|
|
224
|
+
route.initialState
|
|
225
|
+
.transitionTo({
|
|
226
|
+
chatState: "Step 1",
|
|
227
|
+
gather: ["field1"],
|
|
228
|
+
})
|
|
229
|
+
.transitionTo({
|
|
230
|
+
chatState: "Step 2",
|
|
231
|
+
gather: ["field2"],
|
|
232
|
+
})
|
|
233
|
+
.transitionTo({
|
|
234
|
+
chatState: "Step 3",
|
|
235
|
+
gather: ["field3"],
|
|
236
|
+
})
|
|
237
|
+
.transitionTo({ state: END_STATE });
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Branching Transitions
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
const askType = route.initialState.transitionTo({
|
|
244
|
+
chatState: "Are you booking a flight or hotel?",
|
|
245
|
+
gather: ["bookingType"],
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Branch 1: Flight booking
|
|
249
|
+
const flightFlow = askType.transitionTo({
|
|
250
|
+
chatState: "Let's book your flight",
|
|
251
|
+
condition: "User selected flight",
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Branch 2: Hotel booking
|
|
255
|
+
const hotelFlow = askType.transitionTo({
|
|
256
|
+
chatState: "Let's book your hotel",
|
|
257
|
+
condition: "User selected hotel",
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// Both branches can converge later
|
|
261
|
+
const payment = flightFlow.transitionTo({
|
|
262
|
+
chatState: "Let's process payment",
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
hotelFlow.transitionTo({ state: payment }); // Converge to payment
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Data Gathering
|
|
271
|
+
|
|
272
|
+
### Basic Gathering
|
|
273
|
+
|
|
274
|
+
Specify which fields to extract in each state:
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
interface UserData {
|
|
278
|
+
firstName: string;
|
|
279
|
+
lastName: string;
|
|
280
|
+
email: string;
|
|
281
|
+
phone: string;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const route = agent.createRoute<UserData>({ ... });
|
|
285
|
+
|
|
286
|
+
// Gather single field
|
|
287
|
+
const askName = route.initialState.transitionTo({
|
|
288
|
+
chatState: "What's your first name?",
|
|
289
|
+
gather: ["firstName"],
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Gather multiple fields at once
|
|
293
|
+
const askContact = askName.transitionTo({
|
|
294
|
+
chatState: "Please provide your email and phone number",
|
|
295
|
+
gather: ["email", "phone"],
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Gathering with Schema Validation
|
|
300
|
+
|
|
301
|
+
The extraction schema validates gathered data:
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
const route = agent.createRoute<UserData>({
|
|
305
|
+
title: "User Registration",
|
|
306
|
+
|
|
307
|
+
extractionSchema: {
|
|
308
|
+
type: "object",
|
|
309
|
+
properties: {
|
|
310
|
+
firstName: {
|
|
311
|
+
type: "string",
|
|
312
|
+
minLength: 1,
|
|
313
|
+
},
|
|
314
|
+
email: {
|
|
315
|
+
type: "string",
|
|
316
|
+
format: "email", // Validates email format
|
|
317
|
+
},
|
|
318
|
+
age: {
|
|
319
|
+
type: "number",
|
|
320
|
+
minimum: 18,
|
|
321
|
+
maximum: 120,
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
required: ["firstName", "email"],
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// AI will extract and validate according to schema
|
|
329
|
+
const askInfo = route.initialState.transitionTo({
|
|
330
|
+
chatState: "Please provide your name, email, and age",
|
|
331
|
+
gather: ["firstName", "email", "age"],
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Conditional Gathering
|
|
336
|
+
|
|
337
|
+
Use `skipIf` to avoid re-asking for data:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
const askDestination = route.initialState.transitionTo({
|
|
341
|
+
chatState: "Where would you like to go?",
|
|
342
|
+
gather: ["destination"],
|
|
343
|
+
// Skip if we already have the destination
|
|
344
|
+
skipIf: (extracted) => !!extracted.destination,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const askDates = askDestination.transitionTo({
|
|
348
|
+
chatState: "When would you like to travel?",
|
|
349
|
+
gather: ["departureDate", "returnDate"],
|
|
350
|
+
// Skip if we have both dates
|
|
351
|
+
skipIf: (extracted) => !!extracted.departureDate && !!extracted.returnDate,
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## State Logic
|
|
358
|
+
|
|
359
|
+
### Skip Conditions
|
|
360
|
+
|
|
361
|
+
Control when states should be bypassed:
|
|
362
|
+
|
|
363
|
+
```typescript
|
|
364
|
+
// Skip if data already exists
|
|
365
|
+
const askEmail = route.initialState.transitionTo({
|
|
366
|
+
chatState: "What's your email?",
|
|
367
|
+
gather: ["email"],
|
|
368
|
+
skipIf: (extracted) => !!extracted.email,
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Skip based on business logic
|
|
372
|
+
const askShipping = askEmail.transitionTo({
|
|
373
|
+
chatState: "What's your shipping address?",
|
|
374
|
+
gather: ["shippingAddress"],
|
|
375
|
+
// Skip if user selected digital product
|
|
376
|
+
skipIf: (extracted) => extracted.productType === "digital",
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
// Skip based on multiple conditions
|
|
380
|
+
const askBilling = askShipping.transitionTo({
|
|
381
|
+
chatState: "What's your billing address?",
|
|
382
|
+
gather: ["billingAddress"],
|
|
383
|
+
// Skip if billing same as shipping, or already provided
|
|
384
|
+
skipIf: (extracted) =>
|
|
385
|
+
extracted.billingSameAsShipping === true || !!extracted.billingAddress,
|
|
386
|
+
});
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Required Data
|
|
390
|
+
|
|
391
|
+
Ensure prerequisites are met before entering a state:
|
|
392
|
+
|
|
393
|
+
```typescript
|
|
394
|
+
// Can't search without destination and dates
|
|
395
|
+
const searchFlights = askDates.transitionTo({
|
|
396
|
+
toolState: searchFlightsTool,
|
|
397
|
+
requiredData: ["destination", "departureDate"],
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// Can't checkout without all required fields
|
|
401
|
+
const processPayment = selectFlight.transitionTo({
|
|
402
|
+
toolState: processPaymentTool,
|
|
403
|
+
requiredData: [
|
|
404
|
+
"destination",
|
|
405
|
+
"departureDate",
|
|
406
|
+
"selectedFlight",
|
|
407
|
+
"paymentMethod",
|
|
408
|
+
],
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// Multiple prerequisites
|
|
412
|
+
const generateInvoice = processPayment.transitionTo({
|
|
413
|
+
chatState: "Here's your invoice",
|
|
414
|
+
requiredData: ["paymentConfirmation", "customerEmail", "bookingReference"],
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### State Conditions
|
|
419
|
+
|
|
420
|
+
AI-evaluated conditions for state selection:
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
const askIssue = route.initialState.transitionTo({
|
|
424
|
+
chatState: "What seems to be the problem?",
|
|
425
|
+
gather: ["issueDescription"],
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// Technical support path
|
|
429
|
+
const technicalHelp = askIssue.transitionTo({
|
|
430
|
+
chatState: "Let me help with your technical issue",
|
|
431
|
+
condition: "Issue is technical in nature",
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Billing support path
|
|
435
|
+
const billingHelp = askIssue.transitionTo({
|
|
436
|
+
chatState: "Let me help with your billing issue",
|
|
437
|
+
condition: "Issue is related to billing or payments",
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// General inquiry path
|
|
441
|
+
const generalHelp = askIssue.transitionTo({
|
|
442
|
+
chatState: "Let me help with your question",
|
|
443
|
+
condition: "Issue is a general inquiry",
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## State Types
|
|
450
|
+
|
|
451
|
+
### 1. Chat States
|
|
452
|
+
|
|
453
|
+
Present information and gather data:
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
const chat = route.initialState.transitionTo({
|
|
457
|
+
chatState: "What would you like to know?",
|
|
458
|
+
gather: ["question"],
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**When to use:**
|
|
463
|
+
|
|
464
|
+
- Ask questions
|
|
465
|
+
- Present information
|
|
466
|
+
- Gather user input
|
|
467
|
+
- Confirm actions
|
|
468
|
+
|
|
469
|
+
### 2. Tool States
|
|
470
|
+
|
|
471
|
+
Execute functions:
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
const tool = route.initialState.transitionTo({
|
|
475
|
+
toolState: myTool,
|
|
476
|
+
requiredData: ["param1", "param2"],
|
|
477
|
+
});
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**When to use:**
|
|
481
|
+
|
|
482
|
+
- Call APIs
|
|
483
|
+
- Database operations
|
|
484
|
+
- Complex computations
|
|
485
|
+
- External integrations
|
|
486
|
+
- Data validation/enrichment
|
|
487
|
+
|
|
488
|
+
### 3. Initial State
|
|
489
|
+
|
|
490
|
+
Every route's starting point:
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
route.initialState.configure({
|
|
494
|
+
description: "Welcome message",
|
|
495
|
+
gatherFields: ["initialInput"],
|
|
496
|
+
});
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
**When to configure:**
|
|
500
|
+
|
|
501
|
+
- Set up welcome messages
|
|
502
|
+
- Gather initial context
|
|
503
|
+
- Set expectations
|
|
504
|
+
- Pre-populate data
|
|
505
|
+
|
|
506
|
+
### 4. Terminal State
|
|
507
|
+
|
|
508
|
+
End of a route:
|
|
509
|
+
|
|
510
|
+
```typescript
|
|
511
|
+
const end = finalStep.transitionTo({
|
|
512
|
+
state: END_STATE,
|
|
513
|
+
});
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**When to use:**
|
|
517
|
+
|
|
518
|
+
- Route completion
|
|
519
|
+
- Success/failure outcomes
|
|
520
|
+
- Handoff to another route
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## Advanced Patterns
|
|
525
|
+
|
|
526
|
+
### Pattern 1: State Loops
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
const askItems = route.initialState.transitionTo({
|
|
530
|
+
chatState: "What items would you like to add to your cart?",
|
|
531
|
+
gather: ["newItem"],
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
const confirmMore = askItems.transitionTo({
|
|
535
|
+
chatState: "Would you like to add more items?",
|
|
536
|
+
gather: ["addMore"],
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
// Loop back to askItems if user wants more
|
|
540
|
+
confirmMore.transitionTo({
|
|
541
|
+
state: askItems,
|
|
542
|
+
condition: "User wants to add more items",
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// Or continue to checkout
|
|
546
|
+
const checkout = confirmMore.transitionTo({
|
|
547
|
+
chatState: "Let's proceed to checkout",
|
|
548
|
+
condition: "User is done adding items",
|
|
549
|
+
});
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
### Pattern 2: Error Handling States
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
const processPayment = route.initialState.transitionTo({
|
|
556
|
+
toolState: paymentTool,
|
|
557
|
+
requiredData: ["amount", "paymentMethod"],
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
// Success path
|
|
561
|
+
const paymentSuccess = processPayment.transitionTo({
|
|
562
|
+
chatState: "Payment successful! Here's your receipt",
|
|
563
|
+
condition: "Payment was successful",
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// Failure path
|
|
567
|
+
const paymentFailed = processPayment.transitionTo({
|
|
568
|
+
chatState:
|
|
569
|
+
"Payment failed. Would you like to try a different payment method?",
|
|
570
|
+
gather: ["retryPayment"],
|
|
571
|
+
condition: "Payment failed",
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
// Retry logic
|
|
575
|
+
paymentFailed.transitionTo({
|
|
576
|
+
state: processPayment,
|
|
577
|
+
condition: "User wants to retry",
|
|
578
|
+
});
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
### Pattern 3: Progressive Disclosure
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
// Start with basic info
|
|
585
|
+
const askBasic = route.initialState.transitionTo({
|
|
586
|
+
chatState: "Let's start with the basics. What's your name?",
|
|
587
|
+
gather: ["name"],
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
// Reveal more options
|
|
591
|
+
const askPreferences = askBasic.transitionTo({
|
|
592
|
+
chatState: "Great! Now, would you like to customize your experience?",
|
|
593
|
+
gather: ["wantsCustomization"],
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// Only ask detailed questions if user wants customization
|
|
597
|
+
const askDetailed = askPreferences.transitionTo({
|
|
598
|
+
chatState: "Tell me about your preferences...",
|
|
599
|
+
gather: ["theme", "notifications", "language"],
|
|
600
|
+
skipIf: (extracted) => extracted.wantsCustomization === false,
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
const finish = askDetailed.transitionTo({
|
|
604
|
+
chatState: "All set! Your account is ready",
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
// Direct path if no customization
|
|
608
|
+
askPreferences.transitionTo({
|
|
609
|
+
state: finish,
|
|
610
|
+
condition: "User doesn't want customization",
|
|
611
|
+
});
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
### Pattern 4: State Validation
|
|
615
|
+
|
|
616
|
+
```typescript
|
|
617
|
+
const askAge = route.initialState.transitionTo({
|
|
618
|
+
chatState: "How old are you?",
|
|
619
|
+
gather: ["age"],
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
const validateAge = askAge.transitionTo({
|
|
623
|
+
toolState: validateAgeTool,
|
|
624
|
+
requiredData: ["age"],
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// Valid age
|
|
628
|
+
const proceed = validateAge.transitionTo({
|
|
629
|
+
chatState: "Great! Let's continue",
|
|
630
|
+
condition: "Age is valid",
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Invalid age - loop back
|
|
634
|
+
validateAge.transitionTo({
|
|
635
|
+
state: askAge,
|
|
636
|
+
condition: "Age is invalid",
|
|
637
|
+
});
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Pattern 5: Context-Aware States
|
|
641
|
+
|
|
642
|
+
```typescript
|
|
643
|
+
const askQuestion = route.initialState.transitionTo({
|
|
644
|
+
chatState: "What would you like to know?",
|
|
645
|
+
gather: ["question"],
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
// Different responses based on user type
|
|
649
|
+
const premiumResponse = askQuestion.transitionTo({
|
|
650
|
+
chatState: "As a premium member, here's detailed information...",
|
|
651
|
+
condition: "User has premium account",
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
const basicResponse = askQuestion.transitionTo({
|
|
655
|
+
chatState: "Here's the basic information. Upgrade for more details!",
|
|
656
|
+
condition: "User has basic account",
|
|
657
|
+
});
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
## State Properties
|
|
663
|
+
|
|
664
|
+
### Accessing State Properties
|
|
665
|
+
|
|
666
|
+
```typescript
|
|
667
|
+
const state = route.getState("ask_name");
|
|
668
|
+
|
|
669
|
+
// State identification
|
|
670
|
+
console.log(state.id); // "state_ask_name_abc123"
|
|
671
|
+
console.log(state.routeId); // "route_onboarding_xyz789"
|
|
672
|
+
console.log(state.description); // "Ask for user's name"
|
|
673
|
+
|
|
674
|
+
// State configuration
|
|
675
|
+
console.log(state.gatherFields); // ["firstName", "lastName"]
|
|
676
|
+
console.log(state.requiredData); // ["userId"]
|
|
677
|
+
|
|
678
|
+
// State logic
|
|
679
|
+
if (state.skipIf) {
|
|
680
|
+
const shouldSkip = state.skipIf(extractedData);
|
|
681
|
+
console.log("Should skip:", shouldSkip);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// State guidelines
|
|
685
|
+
state.addGuideline({
|
|
686
|
+
condition: "User provides invalid name",
|
|
687
|
+
action: "Ask for valid name format",
|
|
688
|
+
});
|
|
689
|
+
console.log(state.getGuidelines());
|
|
690
|
+
|
|
691
|
+
// Transitions
|
|
692
|
+
console.log(state.getTransitions()); // [Transition, Transition, ...]
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### State Reference
|
|
696
|
+
|
|
697
|
+
```typescript
|
|
698
|
+
const stateRef = state.getRef();
|
|
699
|
+
console.log(stateRef);
|
|
700
|
+
// { id: "state_ask_name_abc123", routeId: "route_onboarding_xyz789" }
|
|
701
|
+
|
|
702
|
+
// Use reference in transitions
|
|
703
|
+
anotherState.transitionTo({ state: stateRef });
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
## Best Practices
|
|
709
|
+
|
|
710
|
+
### ✅ Do's
|
|
711
|
+
|
|
712
|
+
- **Use descriptive chatState** - Clear prompts for better UX
|
|
713
|
+
- **Define gather fields** - Explicit data extraction
|
|
714
|
+
- **Use skipIf for efficiency** - Avoid redundant questions
|
|
715
|
+
- **Set requiredData** - Prevent premature execution
|
|
716
|
+
- **Configure initial state** - Proper route entry point
|
|
717
|
+
- **Use tool states for logic** - Separate concerns
|
|
718
|
+
- **Add state-specific guidelines** - Context-aware behavior
|
|
719
|
+
|
|
720
|
+
### ❌ Don'ts
|
|
721
|
+
|
|
722
|
+
- **Don't hardcode state IDs** - Let framework generate them
|
|
723
|
+
- **Don't skip validation** - Always validate with requiredData
|
|
724
|
+
- **Don't create circular loops** - Without exit conditions
|
|
725
|
+
- **Don't gather unrelated data** - One concept per state
|
|
726
|
+
- **Don't use vague conditions** - Be specific
|
|
727
|
+
- **Don't forget skipIf** - For pre-populated data
|
|
728
|
+
- **Don't mix chat and tool** - One type per state
|
|
729
|
+
|
|
730
|
+
---
|
|
731
|
+
|
|
732
|
+
## Troubleshooting
|
|
733
|
+
|
|
734
|
+
### State Not Being Entered
|
|
735
|
+
|
|
736
|
+
```typescript
|
|
737
|
+
// ❌ Problem: Required data missing
|
|
738
|
+
const state = prev.transitionTo({
|
|
739
|
+
chatState: "Do something",
|
|
740
|
+
requiredData: ["field1", "field2"], // Missing field2
|
|
741
|
+
});
|
|
742
|
+
|
|
743
|
+
// ✅ Solution: Ensure required data is gathered first
|
|
744
|
+
const gatherData = prev.transitionTo({
|
|
745
|
+
chatState: "Gather data",
|
|
746
|
+
gather: ["field1", "field2"],
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
const state = gatherData.transitionTo({
|
|
750
|
+
chatState: "Do something",
|
|
751
|
+
requiredData: ["field1", "field2"], // Now available
|
|
752
|
+
});
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
### State Always Skipped
|
|
756
|
+
|
|
757
|
+
```typescript
|
|
758
|
+
// ❌ Problem: skipIf always returns true
|
|
759
|
+
const state = prev.transitionTo({
|
|
760
|
+
chatState: "Ask something",
|
|
761
|
+
skipIf: (extracted) => true, // Always skips!
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
// ✅ Solution: Use proper condition
|
|
765
|
+
const state = prev.transitionTo({
|
|
766
|
+
chatState: "Ask something",
|
|
767
|
+
skipIf: (extracted) => !!extracted.field, // Only skip if field exists
|
|
768
|
+
});
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
### Data Not Being Gathered
|
|
772
|
+
|
|
773
|
+
```typescript
|
|
774
|
+
// ❌ Problem: Forgot to specify gather
|
|
775
|
+
const state = prev.transitionTo({
|
|
776
|
+
chatState: "What's your name?",
|
|
777
|
+
// Missing gather!
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
// ✅ Solution: Add gather fields
|
|
781
|
+
const state = prev.transitionTo({
|
|
782
|
+
chatState: "What's your name?",
|
|
783
|
+
gather: ["firstName", "lastName"],
|
|
784
|
+
});
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
---
|
|
788
|
+
|
|
789
|
+
## See Also
|
|
790
|
+
|
|
791
|
+
- [Routes Guide](./ROUTES.md) - Understanding routes
|
|
792
|
+
- [API Reference - State](./API_REFERENCE.md#state) - Complete API docs
|
|
793
|
+
- [Examples](../examples/) - Real-world implementations
|
|
794
|
+
- [Architecture Guide](./ARCHITECTURE.md) - System overview
|
|
795
|
+
|
|
796
|
+
---
|
|
797
|
+
|
|
798
|
+
**Made with ❤️ for the community**
|