@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.
Files changed (34) hide show
  1. package/dist/cjs/core/BatchExecutor.d.ts +6 -0
  2. package/dist/cjs/core/BatchExecutor.d.ts.map +1 -1
  3. package/dist/cjs/core/BatchExecutor.js +13 -1
  4. package/dist/cjs/core/BatchExecutor.js.map +1 -1
  5. package/dist/cjs/core/ResponseModal.d.ts.map +1 -1
  6. package/dist/cjs/core/ResponseModal.js +1 -0
  7. package/dist/cjs/core/ResponseModal.js.map +1 -1
  8. package/dist/cjs/types/agent.d.ts +11 -0
  9. package/dist/cjs/types/agent.d.ts.map +1 -1
  10. package/dist/cjs/types/route.d.ts +1 -1
  11. package/dist/cjs/types/route.d.ts.map +1 -1
  12. package/dist/core/BatchExecutor.d.ts +6 -0
  13. package/dist/core/BatchExecutor.d.ts.map +1 -1
  14. package/dist/core/BatchExecutor.js +13 -1
  15. package/dist/core/BatchExecutor.js.map +1 -1
  16. package/dist/core/ResponseModal.d.ts.map +1 -1
  17. package/dist/core/ResponseModal.js +1 -0
  18. package/dist/core/ResponseModal.js.map +1 -1
  19. package/dist/types/agent.d.ts +11 -0
  20. package/dist/types/agent.d.ts.map +1 -1
  21. package/dist/types/route.d.ts +1 -1
  22. package/dist/types/route.d.ts.map +1 -1
  23. package/docs/api/overview.md +11 -7
  24. package/docs/architecture/multi-step-execution.md +37 -3
  25. package/docs/core/conversation-flows/data-collection.md +1 -1
  26. package/docs/core/conversation-flows/step-transitions.md +4 -0
  27. package/docs/core/conversation-flows/steps.md +3 -1
  28. package/docs/guides/migration/README.md +2 -0
  29. package/docs/guides/migration/multi-step-execution.md +29 -9
  30. package/package.json +1 -1
  31. package/src/core/BatchExecutor.ts +22 -1
  32. package/src/core/ResponseModal.ts +1 -0
  33. package/src/types/agent.ts +11 -0
  34. package/src/types/route.ts +8 -7
@@ -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' // Step requires uncollected data
939
- | 'end_route' // Reached END_ROUTE
940
- | 'route_complete' // All Steps processed
941
- | 'prepare_error' // Error in prepare hook
942
- | 'llm_error' // Error during LLM call
943
- | 'validation_error' // Error validating collected data
944
- | 'finalize_error'; // Error in finalize hook (non-fatal)
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 --> D
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, continue to next
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 // Enable detailed logging
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. This reduces unnecessary back-and-forth and minimizes LLM costs.
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
- ## Opting Out of Batching
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. **Multiple steps can now execute together** - reducing LLM calls
367
- 2. **Pre-extraction happens before batch determination** - maximizing batching
368
- 3. **New response fields** - `executedSteps`, `stoppedReason`, `error`
369
- 4. **Hook execution order changed** - all prepare, then LLM, then all finalize
370
- 5. **SkipIf affects batching** - evaluated during batch determination
371
- 6. **Partial progress preserved** - on errors, completed steps are retained
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@falai/agent",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Standalone, strongly-typed AI Agent framework with route DSL and AI provider strategy",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -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;
@@ -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
  /**
@@ -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' // Step requires uncollected data
16
- | 'end_route' // Reached END_ROUTE
17
- | 'route_complete' // All Steps processed
18
- | 'prepare_error' // Error in prepare hook
19
- | 'llm_error' // Error during LLM call
20
- | 'validation_error' // Error validating collected data
21
- | 'finalize_error'; // Error in finalize hook (non-fatal, logged)
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