@falai/agent 1.0.2 → 1.1.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/dist/cjs/core/BatchExecutor.d.ts +6 -0
- package/dist/cjs/core/BatchExecutor.d.ts.map +1 -1
- package/dist/cjs/core/BatchExecutor.js +13 -1
- package/dist/cjs/core/BatchExecutor.js.map +1 -1
- package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
- package/dist/cjs/core/ResponseModal.js +1 -0
- package/dist/cjs/core/ResponseModal.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +11 -0
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/route.d.ts +1 -1
- package/dist/cjs/types/route.d.ts.map +1 -1
- package/dist/core/BatchExecutor.d.ts +6 -0
- package/dist/core/BatchExecutor.d.ts.map +1 -1
- package/dist/core/BatchExecutor.js +13 -1
- package/dist/core/BatchExecutor.js.map +1 -1
- package/dist/core/ResponseModal.d.ts.map +1 -1
- package/dist/core/ResponseModal.js +1 -0
- package/dist/core/ResponseModal.js.map +1 -1
- package/dist/types/agent.d.ts +11 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/route.d.ts +1 -1
- package/dist/types/route.d.ts.map +1 -1
- package/docs/api/overview.md +11 -7
- package/docs/architecture/multi-step-execution.md +37 -3
- package/docs/core/conversation-flows/data-collection.md +1 -1
- package/docs/core/conversation-flows/step-transitions.md +4 -0
- package/docs/core/conversation-flows/steps.md +3 -1
- package/docs/guides/migration/README.md +2 -0
- package/docs/guides/migration/multi-step-execution.md +29 -9
- package/package.json +1 -1
- package/src/core/BatchExecutor.ts +22 -1
- package/src/core/ResponseModal.ts +1 -0
- package/src/types/agent.ts +11 -0
- package/src/types/route.ts +8 -7
package/docs/api/overview.md
CHANGED
|
@@ -761,6 +761,9 @@ interface AgentOptions<TContext = unknown, TData = unknown> {
|
|
|
761
761
|
rules?: Template<TContext, TData>[];
|
|
762
762
|
prohibitions?: Template<TContext, TData>[];
|
|
763
763
|
|
|
764
|
+
// NEW: Control multi-step batching
|
|
765
|
+
maxStepsPerBatch?: number; // Default: 1 (single-step). Set higher or Infinity to batch.
|
|
766
|
+
|
|
764
767
|
hooks?: ContextLifecycleHooks<TContext, TData>;
|
|
765
768
|
debug?: boolean;
|
|
766
769
|
session?: SessionState;
|
|
@@ -935,13 +938,14 @@ Types for multi-step batch execution:
|
|
|
935
938
|
* Reason why batch execution stopped
|
|
936
939
|
*/
|
|
937
940
|
type StoppedReason =
|
|
938
|
-
| 'needs_input'
|
|
939
|
-
| 'end_route'
|
|
940
|
-
| 'route_complete'
|
|
941
|
-
| '
|
|
942
|
-
| '
|
|
943
|
-
| '
|
|
944
|
-
| '
|
|
941
|
+
| 'needs_input' // Step requires uncollected data
|
|
942
|
+
| 'end_route' // Reached END_ROUTE
|
|
943
|
+
| 'route_complete' // All Steps processed
|
|
944
|
+
| 'max_steps_reached' // Batch hit the maxStepsPerBatch limit
|
|
945
|
+
| 'prepare_error' // Error in prepare hook
|
|
946
|
+
| 'llm_error' // Error during LLM call
|
|
947
|
+
| 'validation_error' // Error validating collected data
|
|
948
|
+
| 'finalize_error'; // Error in finalize hook (non-fatal)
|
|
945
949
|
|
|
946
950
|
/**
|
|
947
951
|
* Result of batch determination - which steps can execute together
|
|
@@ -6,6 +6,28 @@
|
|
|
6
6
|
|
|
7
7
|
The core insight is simple: **if a Step's data requirements are already satisfied, there's no reason to pause and wait for user input**. By detecting which Steps can execute together and combining them into a single batch, we reduce LLM calls while improving conversation UX.
|
|
8
8
|
|
|
9
|
+
> **Note:** Multi-step batching is **opt-in**. By default, `maxStepsPerBatch` is `1`, meaning steps execute one at a time (classic behavior). Set `maxStepsPerBatch` to a higher value or `Infinity` to enable batching.
|
|
10
|
+
|
|
11
|
+
## Configuration
|
|
12
|
+
|
|
13
|
+
Control batching behavior with the `maxStepsPerBatch` option on the agent:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
const agent = new Agent({
|
|
17
|
+
name: "Assistant",
|
|
18
|
+
provider: provider,
|
|
19
|
+
// Default: 1 (single-step execution)
|
|
20
|
+
// Set higher to enable batching
|
|
21
|
+
maxStepsPerBatch: Infinity, // No limit — batch all eligible steps
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
| Value | Behavior |
|
|
26
|
+
|-------|----------|
|
|
27
|
+
| `1` (default) | Steps execute one at a time |
|
|
28
|
+
| `N` (e.g. `3`) | Up to N steps per batch |
|
|
29
|
+
| `Infinity` | No limit — all eligible steps batch together |
|
|
30
|
+
|
|
9
31
|
## Design Priorities
|
|
10
32
|
|
|
11
33
|
1. **Conversation UX** - Reduce unnecessary back-and-forth
|
|
@@ -23,7 +45,9 @@ flowchart TD
|
|
|
23
45
|
D -->|Yes| E[Evaluate Step]
|
|
24
46
|
E --> F{Needs Input?}
|
|
25
47
|
F -->|No| G[Add to Batch]
|
|
26
|
-
G -->
|
|
48
|
+
G --> G2{maxStepsPerBatch reached?}
|
|
49
|
+
G2 -->|No| D
|
|
50
|
+
G2 -->|Yes| H[Stop Batch]
|
|
27
51
|
F -->|Yes| H[Stop Batch]
|
|
28
52
|
D -->|No| H
|
|
29
53
|
H --> I[Execute Prepare Hooks]
|
|
@@ -67,8 +91,10 @@ For each Step starting from current position:
|
|
|
67
91
|
c. If skipIf is true → skip Step, continue to next
|
|
68
92
|
d. If skipIf throws error → treat as non-skippable
|
|
69
93
|
e. Evaluate needsInput(step, sessionDataAfterPreExtraction)
|
|
70
|
-
f. If needsInput is false → include Step in batch
|
|
94
|
+
f. If needsInput is false → include Step in batch
|
|
71
95
|
g. If needsInput is true → stop with 'needs_input'
|
|
96
|
+
h. Check if batch size reached maxStepsPerBatch → stop with 'max_steps_reached'
|
|
97
|
+
i. Continue to next Step
|
|
72
98
|
```
|
|
73
99
|
|
|
74
100
|
### Phase 3: Hook Execution (Prepare)
|
|
@@ -170,6 +196,13 @@ Combines multiple Step prompts into a single coherent prompt:
|
|
|
170
196
|
## Example: Multi-Step Batch
|
|
171
197
|
|
|
172
198
|
```typescript
|
|
199
|
+
// Enable batching to process multiple steps at once
|
|
200
|
+
const agent = new Agent({
|
|
201
|
+
name: "Booking Agent",
|
|
202
|
+
provider: provider,
|
|
203
|
+
maxStepsPerBatch: Infinity, // Enable full batching
|
|
204
|
+
});
|
|
205
|
+
|
|
173
206
|
// User provides all booking info at once
|
|
174
207
|
const response = await agent.respond(
|
|
175
208
|
"I want to book the Grand Hotel for 2 people next Friday"
|
|
@@ -202,7 +235,8 @@ Enable debug mode to see batch execution decisions:
|
|
|
202
235
|
const agent = new Agent({
|
|
203
236
|
name: "Assistant",
|
|
204
237
|
provider: provider,
|
|
205
|
-
debug: true
|
|
238
|
+
debug: true, // Enable detailed logging
|
|
239
|
+
maxStepsPerBatch: 5, // Allow up to 5 steps per batch
|
|
206
240
|
});
|
|
207
241
|
```
|
|
208
242
|
|
|
@@ -17,7 +17,7 @@ The agent-level data collection system provides:
|
|
|
17
17
|
|
|
18
18
|
## Data Collection Across Batched Steps
|
|
19
19
|
|
|
20
|
-
When multiple steps execute in a single batch, data collection works across all steps simultaneously.
|
|
20
|
+
When multiple steps execute in a single batch (requires `maxStepsPerBatch` > 1), data collection works across all steps simultaneously.
|
|
21
21
|
|
|
22
22
|
### How Batch Data Collection Works
|
|
23
23
|
|
|
@@ -23,6 +23,7 @@ When executing multiple steps in a batch, the engine stops for specific reasons
|
|
|
23
23
|
| `needs_input` | Step requires data not yet available | Batch stops, LLM generates response to collect data |
|
|
24
24
|
| `end_route` | Reached END_ROUTE marker | Route is complete, no more steps to execute |
|
|
25
25
|
| `route_complete` | All steps in route processed | Route finished successfully |
|
|
26
|
+
| `max_steps_reached` | Batch hit the `maxStepsPerBatch` limit | Batch stops, remaining steps execute in subsequent calls |
|
|
26
27
|
| `prepare_error` | Error in prepare hook | Batch stops, error returned with last successful state |
|
|
27
28
|
| `llm_error` | Error during LLM call | Batch stops, session state preserved |
|
|
28
29
|
| `validation_error` | Data validation failed | Batch continues, errors included in response |
|
|
@@ -130,6 +131,9 @@ switch (response.stoppedReason) {
|
|
|
130
131
|
case 'needs_input':
|
|
131
132
|
console.log("Waiting for user input");
|
|
132
133
|
break;
|
|
134
|
+
case 'max_steps_reached':
|
|
135
|
+
console.log("Batch limit reached, more steps pending");
|
|
136
|
+
break;
|
|
133
137
|
case 'end_route':
|
|
134
138
|
case 'route_complete':
|
|
135
139
|
console.log("Route finished:", response.isRouteComplete);
|
|
@@ -147,7 +147,9 @@ const dataStep = previousStep.nextStep({
|
|
|
147
147
|
|
|
148
148
|
## Multi-Step Batch Execution
|
|
149
149
|
|
|
150
|
-
Steps can execute together in a single LLM call when their data requirements are already satisfied
|
|
150
|
+
Steps can execute together in a single LLM call when their data requirements are already satisfied and `maxStepsPerBatch` is set higher than `1`. This reduces unnecessary back-and-forth and minimizes LLM costs.
|
|
151
|
+
|
|
152
|
+
> **Note:** By default, `maxStepsPerBatch` is `1` (single-step execution). Set it to a higher value or `Infinity` on the agent to enable batching.
|
|
151
153
|
|
|
152
154
|
### How Steps Are Batched
|
|
153
155
|
|
|
@@ -6,6 +6,8 @@ This directory contains migration guides for major changes and updates to the `@
|
|
|
6
6
|
|
|
7
7
|
### [Multi-Step Execution Migration Guide](./multi-step-execution.md)
|
|
8
8
|
|
|
9
|
+
**v1.1.0 - Breaking Change** - `maxStepsPerBatch` now defaults to `1` (single-step execution). Set `maxStepsPerBatch: Infinity` to restore v1.0.x batching behavior.
|
|
10
|
+
|
|
9
11
|
**v1.0.0 - Major Release** - Guide for understanding and migrating to multi-step batch execution.
|
|
10
12
|
|
|
11
13
|
**Breaking Changes:**
|
|
@@ -103,9 +103,14 @@ const response3 = await agent.respond("2 people");
|
|
|
103
103
|
|
|
104
104
|
### After: Multi-Step Execution
|
|
105
105
|
|
|
106
|
-
Now, multiple steps can execute in a single call when data requirements are satisfied
|
|
106
|
+
Now, multiple steps can execute in a single call when data requirements are satisfied and `maxStepsPerBatch` is set higher than `1`:
|
|
107
107
|
|
|
108
108
|
```typescript
|
|
109
|
+
const agent = new Agent({
|
|
110
|
+
// ...
|
|
111
|
+
maxStepsPerBatch: Infinity, // Enable batching
|
|
112
|
+
});
|
|
113
|
+
|
|
109
114
|
// Turn 1
|
|
110
115
|
const response = await agent.respond("Book Grand Hotel for 2 on Friday");
|
|
111
116
|
// Pre-extraction captures: { hotel: "Grand Hotel", date: "Friday", guests: 2 }
|
|
@@ -115,6 +120,8 @@ const response = await agent.respond("Book Grand Hotel for 2 on Friday");
|
|
|
115
120
|
// Total: 1 LLM call
|
|
116
121
|
```
|
|
117
122
|
|
|
123
|
+
> **Note:** By default, `maxStepsPerBatch` is `1`, which preserves the classic single-step behavior. You must explicitly opt in to batching.
|
|
124
|
+
|
|
118
125
|
## What Changed
|
|
119
126
|
|
|
120
127
|
| Aspect | Before | After |
|
|
@@ -326,9 +333,20 @@ const step2 = { skipIf: (d) => !!d.email }; // Skipped if email exists
|
|
|
326
333
|
// - Both steps skipped, route may complete immediately
|
|
327
334
|
```
|
|
328
335
|
|
|
329
|
-
##
|
|
336
|
+
## Controlling Batching with `maxStepsPerBatch`
|
|
337
|
+
|
|
338
|
+
As of v1.1.0, batching is **off by default** (`maxStepsPerBatch: 1`). To enable multi-step batching, set the option on your agent:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
const agent = new Agent({
|
|
342
|
+
name: "Assistant",
|
|
343
|
+
provider: provider,
|
|
344
|
+
maxStepsPerBatch: Infinity, // Batch all eligible steps (v1.0.x behavior)
|
|
345
|
+
// maxStepsPerBatch: 3, // Or cap at 3 steps per batch
|
|
346
|
+
});
|
|
347
|
+
```
|
|
330
348
|
|
|
331
|
-
If you need single-step behavior for specific steps, use `requires` to create dependencies:
|
|
349
|
+
If you need single-step behavior for specific steps while batching is enabled, use `requires` to create dependencies:
|
|
332
350
|
|
|
333
351
|
```typescript
|
|
334
352
|
// Force step2 to wait for step1's data
|
|
@@ -363,11 +381,13 @@ const agent = new Agent({
|
|
|
363
381
|
|
|
364
382
|
## Summary
|
|
365
383
|
|
|
366
|
-
1.
|
|
367
|
-
2. **
|
|
368
|
-
3. **
|
|
369
|
-
4. **
|
|
370
|
-
5. **
|
|
371
|
-
6. **
|
|
384
|
+
1. **`maxStepsPerBatch` defaults to `1`** - single-step execution by default (v1.1.0 breaking change)
|
|
385
|
+
2. **Set `maxStepsPerBatch: Infinity`** to restore v1.0.x batching behavior
|
|
386
|
+
3. **Multiple steps can execute together** when batching is enabled, reducing LLM calls
|
|
387
|
+
4. **Pre-extraction happens before batch determination** - maximizing batching
|
|
388
|
+
5. **New response fields** - `executedSteps`, `stoppedReason`, `error`
|
|
389
|
+
6. **Hook execution order changed** - all prepare, then LLM, then all finalize
|
|
390
|
+
7. **SkipIf affects batching** - evaluated during batch determination
|
|
391
|
+
8. **Partial progress preserved** - on errors, completed steps are retained
|
|
372
392
|
|
|
373
393
|
The changes improve efficiency and UX while maintaining API compatibility. Most existing code will work without changes, but reviewing hook dependencies and test expectations is recommended.
|
package/package.json
CHANGED
|
@@ -108,6 +108,12 @@ export interface DetermineBatchParams<TContext, TData> {
|
|
|
108
108
|
sessionData: Partial<TData>;
|
|
109
109
|
/** Agent context for condition evaluation */
|
|
110
110
|
context: TContext;
|
|
111
|
+
/**
|
|
112
|
+
* Maximum number of steps to include in this batch.
|
|
113
|
+
* When reached, the batch stops with 'max_steps_reached'.
|
|
114
|
+
* Defaults to 1 (single-step execution).
|
|
115
|
+
*/
|
|
116
|
+
maxSteps?: number;
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
/**
|
|
@@ -222,7 +228,7 @@ export class BatchExecutor<TContext = unknown, TData = unknown> {
|
|
|
222
228
|
* **Validates: Requirements 1.1, 1.4, 1.5, 7.1, 7.2, 7.3**
|
|
223
229
|
*/
|
|
224
230
|
async determineBatch(params: DetermineBatchParams<TContext, TData>): Promise<BatchResult<TContext, TData>> {
|
|
225
|
-
const { route, currentStep, sessionData, context } = params;
|
|
231
|
+
const { route, currentStep, sessionData, context, maxSteps = 1 } = params;
|
|
226
232
|
const startTime = Date.now();
|
|
227
233
|
|
|
228
234
|
const batchSteps: StepOptions<TContext, TData>[] = [];
|
|
@@ -361,6 +367,21 @@ export class BatchExecutor<TContext = unknown, TData = unknown> {
|
|
|
361
367
|
batchSize: batchSteps.length,
|
|
362
368
|
});
|
|
363
369
|
|
|
370
|
+
// Check if we've reached the max steps limit
|
|
371
|
+
if (batchSteps.length >= maxSteps) {
|
|
372
|
+
stoppedReason = 'max_steps_reached';
|
|
373
|
+
|
|
374
|
+
logger.debug(`[BatchExecutor] Reached maxStepsPerBatch limit (${maxSteps}), stopping batch`);
|
|
375
|
+
|
|
376
|
+
this.emitBatchEvent('batch_stop', {
|
|
377
|
+
stepId: step.id,
|
|
378
|
+
reason: `Reached maxStepsPerBatch limit (${maxSteps})`,
|
|
379
|
+
stoppedReason: 'max_steps_reached',
|
|
380
|
+
batchSize: batchSteps.length,
|
|
381
|
+
});
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
|
|
364
385
|
// Move to next step in the sequence
|
|
365
386
|
const transitions = step.getTransitions();
|
|
366
387
|
if (transitions.length === 0) {
|
|
@@ -575,6 +575,7 @@ export class ResponseModal<TContext = unknown, TData = unknown> {
|
|
|
575
575
|
currentStep,
|
|
576
576
|
sessionData: updatedSession.data || {},
|
|
577
577
|
context: params.context,
|
|
578
|
+
maxSteps: this.agent.getAgentOptions().maxStepsPerBatch,
|
|
578
579
|
});
|
|
579
580
|
|
|
580
581
|
batchSteps = batchResult.steps;
|
package/src/types/agent.ts
CHANGED
|
@@ -121,6 +121,17 @@ export interface AgentOptions<TContext = unknown, TData = unknown> {
|
|
|
121
121
|
* @default 15
|
|
122
122
|
*/
|
|
123
123
|
routeSwitchMargin?: number;
|
|
124
|
+
/**
|
|
125
|
+
* Maximum number of steps to execute in a single batch.
|
|
126
|
+
* Controls how many consecutive steps can run together in one LLM call.
|
|
127
|
+
*
|
|
128
|
+
* - `1` (default): Steps execute one at a time (classic behavior)
|
|
129
|
+
* - `Infinity`: No limit — all eligible steps batch together
|
|
130
|
+
* - Any positive integer: Cap the batch to that many steps
|
|
131
|
+
*
|
|
132
|
+
* @default 1
|
|
133
|
+
*/
|
|
134
|
+
maxStepsPerBatch?: number;
|
|
124
135
|
}
|
|
125
136
|
|
|
126
137
|
/**
|
package/src/types/route.ts
CHANGED
|
@@ -12,13 +12,14 @@ import { Template, ConditionTemplate } from "./template";
|
|
|
12
12
|
* Used to indicate the stopping condition for multi-step execution
|
|
13
13
|
*/
|
|
14
14
|
export type StoppedReason =
|
|
15
|
-
| 'needs_input'
|
|
16
|
-
| 'end_route'
|
|
17
|
-
| 'route_complete'
|
|
18
|
-
| '
|
|
19
|
-
| '
|
|
20
|
-
| '
|
|
21
|
-
| '
|
|
15
|
+
| 'needs_input' // Step requires uncollected data
|
|
16
|
+
| 'end_route' // Reached END_ROUTE
|
|
17
|
+
| 'route_complete' // All Steps processed
|
|
18
|
+
| 'max_steps_reached' // Batch hit the maxStepsPerBatch limit
|
|
19
|
+
| 'prepare_error' // Error in prepare hook
|
|
20
|
+
| 'llm_error' // Error during LLM call
|
|
21
|
+
| 'validation_error' // Error validating collected data
|
|
22
|
+
| 'finalize_error'; // Error in finalize hook (non-fatal, logged)
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Event types for batch execution observability
|