@falai/agent 0.9.0-alpha-2 → 0.9.2
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 +42 -34
- package/dist/cjs/src/core/Agent.d.ts +48 -44
- package/dist/cjs/src/core/Agent.d.ts.map +1 -1
- package/dist/cjs/src/core/Agent.js +151 -1110
- package/dist/cjs/src/core/Agent.js.map +1 -1
- package/dist/cjs/src/core/ResponseModal.d.ts +211 -0
- package/dist/cjs/src/core/ResponseModal.d.ts.map +1 -0
- package/dist/cjs/src/core/ResponseModal.js +1394 -0
- package/dist/cjs/src/core/ResponseModal.js.map +1 -0
- package/dist/cjs/src/core/ResponsePipeline.d.ts +8 -4
- package/dist/cjs/src/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/cjs/src/core/ResponsePipeline.js +48 -20
- package/dist/cjs/src/core/ResponsePipeline.js.map +1 -1
- package/dist/cjs/src/core/Route.d.ts +12 -5
- package/dist/cjs/src/core/Route.d.ts.map +1 -1
- package/dist/cjs/src/core/Route.js +26 -5
- package/dist/cjs/src/core/Route.js.map +1 -1
- package/dist/cjs/src/core/RoutingEngine.d.ts +5 -0
- package/dist/cjs/src/core/RoutingEngine.d.ts.map +1 -1
- package/dist/cjs/src/core/RoutingEngine.js +37 -25
- package/dist/cjs/src/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/src/core/SessionManager.d.ts +9 -1
- package/dist/cjs/src/core/SessionManager.d.ts.map +1 -1
- package/dist/cjs/src/core/SessionManager.js +27 -5
- package/dist/cjs/src/core/SessionManager.js.map +1 -1
- package/dist/cjs/src/core/Step.d.ts +60 -7
- package/dist/cjs/src/core/Step.d.ts.map +1 -1
- package/dist/cjs/src/core/Step.js +151 -4
- package/dist/cjs/src/core/Step.js.map +1 -1
- package/dist/cjs/src/core/ToolManager.d.ts +234 -0
- package/dist/cjs/src/core/ToolManager.d.ts.map +1 -0
- package/dist/cjs/src/core/ToolManager.js +1117 -0
- package/dist/cjs/src/core/ToolManager.js.map +1 -0
- package/dist/cjs/src/index.d.ts +5 -4
- package/dist/cjs/src/index.d.ts.map +1 -1
- package/dist/cjs/src/index.js +11 -3
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/types/agent.d.ts +2 -1
- package/dist/cjs/src/types/agent.d.ts.map +1 -1
- package/dist/cjs/src/types/ai.d.ts +1 -1
- package/dist/cjs/src/types/ai.d.ts.map +1 -1
- package/dist/cjs/src/types/index.d.ts +3 -2
- package/dist/cjs/src/types/index.d.ts.map +1 -1
- package/dist/cjs/src/types/index.js +3 -1
- package/dist/cjs/src/types/index.js.map +1 -1
- package/dist/cjs/src/types/route.d.ts +6 -4
- package/dist/cjs/src/types/route.d.ts.map +1 -1
- package/dist/cjs/src/types/tool.d.ts +84 -14
- package/dist/cjs/src/types/tool.d.ts.map +1 -1
- package/dist/cjs/src/types/tool.js +13 -0
- package/dist/cjs/src/types/tool.js.map +1 -1
- package/dist/cjs/src/utils/clone.d.ts.map +1 -1
- package/dist/cjs/src/utils/clone.js +0 -4
- package/dist/cjs/src/utils/clone.js.map +1 -1
- package/dist/cjs/src/utils/history.d.ts +30 -1
- package/dist/cjs/src/utils/history.d.ts.map +1 -1
- package/dist/cjs/src/utils/history.js +169 -23
- package/dist/cjs/src/utils/history.js.map +1 -1
- package/dist/cjs/src/utils/index.d.ts +1 -1
- package/dist/cjs/src/utils/index.d.ts.map +1 -1
- package/dist/cjs/src/utils/index.js +5 -1
- package/dist/cjs/src/utils/index.js.map +1 -1
- package/dist/src/core/Agent.d.ts +48 -44
- package/dist/src/core/Agent.d.ts.map +1 -1
- package/dist/src/core/Agent.js +152 -1111
- package/dist/src/core/Agent.js.map +1 -1
- package/dist/src/core/ResponseModal.d.ts +211 -0
- package/dist/src/core/ResponseModal.d.ts.map +1 -0
- package/dist/src/core/ResponseModal.js +1389 -0
- package/dist/src/core/ResponseModal.js.map +1 -0
- package/dist/src/core/ResponsePipeline.d.ts +8 -4
- package/dist/src/core/ResponsePipeline.d.ts.map +1 -1
- package/dist/src/core/ResponsePipeline.js +48 -20
- package/dist/src/core/ResponsePipeline.js.map +1 -1
- package/dist/src/core/Route.d.ts +12 -5
- package/dist/src/core/Route.d.ts.map +1 -1
- package/dist/src/core/Route.js +26 -5
- package/dist/src/core/Route.js.map +1 -1
- package/dist/src/core/RoutingEngine.d.ts +5 -0
- package/dist/src/core/RoutingEngine.d.ts.map +1 -1
- package/dist/src/core/RoutingEngine.js +37 -25
- package/dist/src/core/RoutingEngine.js.map +1 -1
- package/dist/src/core/SessionManager.d.ts +9 -1
- package/dist/src/core/SessionManager.d.ts.map +1 -1
- package/dist/src/core/SessionManager.js +27 -5
- package/dist/src/core/SessionManager.js.map +1 -1
- package/dist/src/core/Step.d.ts +60 -7
- package/dist/src/core/Step.d.ts.map +1 -1
- package/dist/src/core/Step.js +151 -4
- package/dist/src/core/Step.js.map +1 -1
- package/dist/src/core/ToolManager.d.ts +234 -0
- package/dist/src/core/ToolManager.d.ts.map +1 -0
- package/dist/src/core/ToolManager.js +1111 -0
- package/dist/src/core/ToolManager.js.map +1 -0
- package/dist/src/index.d.ts +5 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/types/agent.d.ts +2 -1
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/ai.d.ts +1 -1
- package/dist/src/types/ai.d.ts.map +1 -1
- package/dist/src/types/index.d.ts +3 -2
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +1 -0
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/route.d.ts +6 -4
- package/dist/src/types/route.d.ts.map +1 -1
- package/dist/src/types/tool.d.ts +84 -14
- package/dist/src/types/tool.d.ts.map +1 -1
- package/dist/src/types/tool.js +12 -1
- package/dist/src/types/tool.js.map +1 -1
- package/dist/src/utils/clone.d.ts.map +1 -1
- package/dist/src/utils/clone.js +0 -4
- package/dist/src/utils/clone.js.map +1 -1
- package/dist/src/utils/history.d.ts +30 -1
- package/dist/src/utils/history.d.ts.map +1 -1
- package/dist/src/utils/history.js +165 -23
- package/dist/src/utils/history.js.map +1 -1
- package/dist/src/utils/index.d.ts +1 -1
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/index.js +1 -1
- package/dist/src/utils/index.js.map +1 -1
- package/docs/CONTRIBUTING.md +40 -0
- package/docs/README.md +14 -6
- package/docs/api/README.md +235 -45
- package/docs/api/overview.md +140 -33
- package/docs/core/agent/session-management.md +152 -5
- package/docs/core/ai-integration/response-processing.md +115 -4
- package/docs/core/conversation-flows/routes.md +130 -0
- package/docs/core/error-handling.md +638 -0
- package/docs/core/tools/tool-definition.md +684 -60
- package/docs/core/tools/tool-scoping.md +244 -53
- package/docs/guides/error-handling-patterns.md +578 -0
- package/docs/guides/getting-started/README.md +139 -28
- package/docs/guides/migration/README.md +72 -0
- package/docs/guides/migration/response-modal-refactor.md +518 -0
- package/examples/advanced-patterns/knowledge-based-agent.ts +6 -6
- package/examples/advanced-patterns/persistent-onboarding.ts +30 -43
- package/examples/advanced-patterns/streaming-responses.ts +169 -96
- package/examples/ai-providers/anthropic-integration.ts +9 -5
- package/examples/ai-providers/openai-integration.ts +11 -7
- package/examples/core-concepts/basic-agent.ts +106 -67
- package/examples/core-concepts/modern-streaming-api.ts +309 -0
- package/examples/core-concepts/schema-driven-extraction.ts +10 -7
- package/examples/core-concepts/session-management.ts +71 -18
- package/examples/integrations/healthcare-integration.ts +15 -29
- package/examples/integrations/server-session-management.ts +3 -3
- package/examples/persistence/memory-sessions.ts +3 -3
- package/examples/tools/basic-tools.ts +293 -89
- package/examples/tools/data-enrichment-tools.ts +185 -75
- package/package.json +1 -1
- package/src/core/Agent.ts +190 -1529
- package/src/core/ResponseModal.ts +1798 -0
- package/src/core/ResponsePipeline.ts +83 -57
- package/src/core/Route.ts +39 -12
- package/src/core/RoutingEngine.ts +46 -42
- package/src/core/SessionManager.ts +39 -7
- package/src/core/Step.ts +198 -20
- package/src/core/ToolManager.ts +1394 -0
- package/src/index.ts +19 -3
- package/src/types/agent.ts +2 -1
- package/src/types/ai.ts +1 -1
- package/src/types/index.ts +13 -2
- package/src/types/route.ts +6 -4
- package/src/types/tool.ts +116 -25
- package/src/utils/clone.ts +6 -8
- package/src/utils/history.ts +190 -27
- package/src/utils/index.ts +4 -0
- package/dist/cjs/src/core/ToolExecutor.d.ts +0 -45
- package/dist/cjs/src/core/ToolExecutor.d.ts.map +0 -1
- package/dist/cjs/src/core/ToolExecutor.js +0 -84
- package/dist/cjs/src/core/ToolExecutor.js.map +0 -1
- package/dist/src/core/ToolExecutor.d.ts +0 -45
- package/dist/src/core/ToolExecutor.d.ts.map +0 -1
- package/dist/src/core/ToolExecutor.js +0 -80
- package/dist/src/core/ToolExecutor.js.map +0 -1
- package/docs/core/tools/tool-execution.md +0 -815
- package/src/core/ToolExecutor.ts +0 -126
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
import { Route } from "../core/Route";
|
|
23
23
|
import { Step } from "../core/Step";
|
|
24
24
|
import { RoutingEngine } from "../core/RoutingEngine";
|
|
25
|
-
import {
|
|
25
|
+
import type { ToolManager } from "../core/ToolManager";
|
|
26
26
|
import { END_ROUTE_ID } from "../constants";
|
|
27
27
|
|
|
28
28
|
export interface ResponsePreparationResult<TContext, TData = unknown> {
|
|
@@ -42,8 +42,8 @@ export interface RoutingResult<TContext, TData = unknown> {
|
|
|
42
42
|
export interface ToolExecutionResult<TData = unknown> {
|
|
43
43
|
session: SessionState<TData>;
|
|
44
44
|
toolCalls:
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
| Array<{ toolName: string; arguments: Record<string, unknown> }>
|
|
46
|
+
| undefined;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export interface DataCollectionResult<TData = unknown> {
|
|
@@ -57,8 +57,8 @@ export interface DataCollectionResult<TData = unknown> {
|
|
|
57
57
|
export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
58
58
|
constructor(
|
|
59
59
|
private readonly options: AgentOptions<TContext, TData>,
|
|
60
|
-
private readonly
|
|
61
|
-
private readonly tools: Tool<TContext, TData
|
|
60
|
+
private readonly getRoutes: () => Route<TContext, TData>[],
|
|
61
|
+
private readonly tools: Tool<TContext, TData>[],
|
|
62
62
|
private readonly routingEngine: RoutingEngine<TContext, TData>,
|
|
63
63
|
private readonly updateContext: (
|
|
64
64
|
updates: Partial<TContext>
|
|
@@ -69,8 +69,9 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
69
69
|
) => Promise<SessionState<TData>>,
|
|
70
70
|
private readonly updateCollectedData?: (
|
|
71
71
|
updates: Partial<TData>
|
|
72
|
-
) => Promise<void
|
|
73
|
-
|
|
72
|
+
) => Promise<void>,
|
|
73
|
+
private readonly toolManager?: ToolManager<TContext, TData>
|
|
74
|
+
) { }
|
|
74
75
|
|
|
75
76
|
/**
|
|
76
77
|
* Prepare context and session for response generation
|
|
@@ -125,9 +126,12 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
125
126
|
let completedRoutes: Route<TContext, TData>[] = [];
|
|
126
127
|
let targetSession = session;
|
|
127
128
|
|
|
129
|
+
// Get routes early since we need them for pending transitions
|
|
130
|
+
const routes = this.getRoutes();
|
|
131
|
+
|
|
128
132
|
// Check for pending transition from previous route completion
|
|
129
133
|
if (targetSession.pendingTransition) {
|
|
130
|
-
const targetRoute =
|
|
134
|
+
const targetRoute = routes.find(
|
|
131
135
|
(r) => r.id === targetSession.pendingTransition?.targetRouteId
|
|
132
136
|
);
|
|
133
137
|
|
|
@@ -166,11 +170,11 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
166
170
|
};
|
|
167
171
|
}
|
|
168
172
|
}
|
|
169
|
-
|
|
173
|
+
|
|
170
174
|
// If no pending transition or transition handled, do normal routing
|
|
171
|
-
if (
|
|
175
|
+
if (routes.length > 0 && !selectedRoute) {
|
|
172
176
|
const orchestration = await this.routingEngine.decideRouteAndStep({
|
|
173
|
-
routes:
|
|
177
|
+
routes: routes,
|
|
174
178
|
session: targetSession,
|
|
175
179
|
history,
|
|
176
180
|
agentOptions: this.options,
|
|
@@ -281,8 +285,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
281
285
|
}
|
|
282
286
|
|
|
283
287
|
logger.debug(
|
|
284
|
-
`[ResponseHandler] Executing ${toolCalls.length} ${
|
|
285
|
-
isStreaming ? "streaming " : ""
|
|
288
|
+
`[ResponseHandler] Executing ${toolCalls.length} ${isStreaming ? "streaming " : ""
|
|
286
289
|
}tool calls`
|
|
287
290
|
);
|
|
288
291
|
|
|
@@ -299,15 +302,21 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
299
302
|
continue;
|
|
300
303
|
}
|
|
301
304
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
305
|
+
// Use ToolManager for unified tool execution
|
|
306
|
+
let result;
|
|
307
|
+
if (this.toolManager) {
|
|
308
|
+
result = await this.toolManager.executeTool({
|
|
309
|
+
tool,
|
|
310
|
+
context,
|
|
311
|
+
updateContext: this.updateContext,
|
|
312
|
+
updateData: this.updateCollectedData || (async () => { }),
|
|
313
|
+
history,
|
|
314
|
+
data: updatedSession.data,
|
|
315
|
+
});
|
|
316
|
+
} else {
|
|
317
|
+
// Fallback: execute tool directly if ToolManager not available
|
|
318
|
+
throw new Error(`ToolManager not available for tool execution: ${toolCall.toolName}`);
|
|
319
|
+
}
|
|
311
320
|
|
|
312
321
|
executedToolCalls.push(toolCall);
|
|
313
322
|
|
|
@@ -323,16 +332,14 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
323
332
|
result.dataUpdate as Partial<TData>
|
|
324
333
|
);
|
|
325
334
|
logger.debug(
|
|
326
|
-
`[ResponseHandler] ${
|
|
327
|
-
isStreaming ? "Streaming " : ""
|
|
335
|
+
`[ResponseHandler] ${isStreaming ? "Streaming " : ""
|
|
328
336
|
}Tool updated collected data:`,
|
|
329
337
|
result.dataUpdate
|
|
330
338
|
);
|
|
331
339
|
}
|
|
332
340
|
|
|
333
341
|
logger.debug(
|
|
334
|
-
`[ResponseHandler] Executed ${isStreaming ? "streaming " : ""}tool: ${
|
|
335
|
-
result.toolName
|
|
342
|
+
`[ResponseHandler] Executed ${isStreaming ? "streaming " : ""}tool: ${String(tool.id || tool.name || 'unknown')
|
|
336
343
|
} (success: ${result.success})`
|
|
337
344
|
);
|
|
338
345
|
}
|
|
@@ -348,8 +355,8 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
348
355
|
*/
|
|
349
356
|
async executeToolLoop(params: {
|
|
350
357
|
initialToolCalls:
|
|
351
|
-
|
|
352
|
-
|
|
358
|
+
| Array<{ toolName: string; arguments: Record<string, unknown> }>
|
|
359
|
+
| undefined;
|
|
353
360
|
selectedRoute?: Route<TContext, TData>;
|
|
354
361
|
nextStep: Step<TContext, TData>;
|
|
355
362
|
responsePrompt: string;
|
|
@@ -380,8 +387,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
380
387
|
while (hasToolCalls && toolLoopCount < MAX_TOOL_LOOPS) {
|
|
381
388
|
toolLoopCount++;
|
|
382
389
|
logger.debug(
|
|
383
|
-
`[ResponseHandler] Starting ${
|
|
384
|
-
isStreaming ? "streaming " : ""
|
|
390
|
+
`[ResponseHandler] Starting ${isStreaming ? "streaming " : ""
|
|
385
391
|
}tool loop ${toolLoopCount}/${MAX_TOOL_LOOPS}`
|
|
386
392
|
);
|
|
387
393
|
|
|
@@ -430,8 +436,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
430
436
|
|
|
431
437
|
if (hasToolCalls) {
|
|
432
438
|
logger.debug(
|
|
433
|
-
`[ResponseHandler] Follow-up call produced ${
|
|
434
|
-
followUpToolCalls!.length
|
|
439
|
+
`[ResponseHandler] Follow-up call produced ${followUpToolCalls!.length
|
|
435
440
|
} additional tool calls`
|
|
436
441
|
);
|
|
437
442
|
|
|
@@ -449,8 +454,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
449
454
|
currentToolCalls = followUpToolCalls;
|
|
450
455
|
} else {
|
|
451
456
|
logger.debug(
|
|
452
|
-
`[ResponseHandler] ${
|
|
453
|
-
isStreaming ? "Streaming " : ""
|
|
457
|
+
`[ResponseHandler] ${isStreaming ? "Streaming " : ""
|
|
454
458
|
}Tool loop completed after ${toolLoopCount} iterations`
|
|
455
459
|
);
|
|
456
460
|
// Update final toolCalls from follow-up result if no more tools
|
|
@@ -461,8 +465,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
461
465
|
|
|
462
466
|
if (toolLoopCount >= MAX_TOOL_LOOPS) {
|
|
463
467
|
logger.warn(
|
|
464
|
-
`[ResponseHandler] ${
|
|
465
|
-
isStreaming ? "Streaming " : ""
|
|
468
|
+
`[ResponseHandler] ${isStreaming ? "Streaming " : ""
|
|
466
469
|
}Tool loop limit reached (${MAX_TOOL_LOOPS}), stopping`
|
|
467
470
|
);
|
|
468
471
|
}
|
|
@@ -505,7 +508,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
505
508
|
if (this.updateCollectedData) {
|
|
506
509
|
await this.updateCollectedData(collectedData);
|
|
507
510
|
}
|
|
508
|
-
|
|
511
|
+
|
|
509
512
|
// Update session with validated data
|
|
510
513
|
updatedSession = await this.updateData(session, collectedData);
|
|
511
514
|
logger.debug(`[ResponseHandler] Collected data:`, collectedData);
|
|
@@ -554,7 +557,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
554
557
|
|
|
555
558
|
if (transitionConfig) {
|
|
556
559
|
// Find target route by ID or title
|
|
557
|
-
const targetRoute = this.
|
|
560
|
+
const targetRoute = this.getRoutes().find(
|
|
558
561
|
(r) =>
|
|
559
562
|
r.id === transitionConfig.nextStep ||
|
|
560
563
|
r.title === transitionConfig.nextStep
|
|
@@ -602,12 +605,21 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
602
605
|
}
|
|
603
606
|
|
|
604
607
|
/**
|
|
605
|
-
* Find an available tool by name for the given route
|
|
608
|
+
* Find an available tool by name for the given route using ToolManager
|
|
609
|
+
* Delegates to ToolManager for unified tool resolution
|
|
606
610
|
*/
|
|
607
611
|
private findAvailableTool(
|
|
608
612
|
toolName: string,
|
|
609
613
|
route?: Route<TContext, TData>
|
|
610
|
-
): Tool<TContext, TData
|
|
614
|
+
): Tool<TContext, TData> | undefined {
|
|
615
|
+
// Use ToolManager for unified tool resolution if available
|
|
616
|
+
if (this.toolManager) {
|
|
617
|
+
return this.toolManager.find(toolName, undefined, undefined, route);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Fallback to legacy resolution if ToolManager not available
|
|
621
|
+
logger.warn(`[ResponsePipeline] ToolManager not available, using legacy tool resolution for: ${toolName}`);
|
|
622
|
+
|
|
611
623
|
// Check route-level tools first (if route provided)
|
|
612
624
|
if (route) {
|
|
613
625
|
const routeTool = route
|
|
@@ -623,15 +635,29 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
623
635
|
}
|
|
624
636
|
|
|
625
637
|
/**
|
|
626
|
-
* Collect all available tools for the given route and step context
|
|
638
|
+
* Collect all available tools for the given route and step context using ToolManager
|
|
639
|
+
* Delegates to ToolManager for unified tool resolution and deduplication
|
|
627
640
|
*/
|
|
628
641
|
private collectAvailableTools(
|
|
629
642
|
route?: Route<TContext, TData>,
|
|
630
643
|
step?: Step<TContext, TData>
|
|
631
644
|
): Array<{ id: string; description?: string; parameters?: unknown }> {
|
|
645
|
+
// Use ToolManager for unified tool collection if available
|
|
646
|
+
if (this.toolManager) {
|
|
647
|
+
const availableTools = this.toolManager.getAvailable(undefined, step, route);
|
|
648
|
+
return availableTools.map((tool) => ({
|
|
649
|
+
id: tool.id,
|
|
650
|
+
description: tool.description,
|
|
651
|
+
parameters: tool.parameters,
|
|
652
|
+
}));
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Fallback to legacy collection logic if ToolManager not available
|
|
656
|
+
logger.warn(`[ResponsePipeline] ToolManager not available, using legacy tool collection`);
|
|
657
|
+
|
|
632
658
|
const availableTools = new Map<
|
|
633
659
|
string,
|
|
634
|
-
Tool<TContext, TData
|
|
660
|
+
Tool<TContext, TData>
|
|
635
661
|
>();
|
|
636
662
|
|
|
637
663
|
// Add agent-level tools
|
|
@@ -649,7 +675,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
649
675
|
// Filter by step-level allowed tools if specified
|
|
650
676
|
if (step?.tools) {
|
|
651
677
|
const allowedToolIds = new Set<string>();
|
|
652
|
-
const stepTools: Tool<TContext, TData
|
|
678
|
+
const stepTools: Tool<TContext, TData>[] = [];
|
|
653
679
|
|
|
654
680
|
for (const toolRef of step.tools) {
|
|
655
681
|
if (typeof toolRef === "string") {
|
|
@@ -668,9 +694,9 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
668
694
|
if (allowedToolIds.size > 0) {
|
|
669
695
|
const filteredTools = new Map<
|
|
670
696
|
string,
|
|
671
|
-
Tool<TContext, TData
|
|
697
|
+
Tool<TContext, TData>
|
|
672
698
|
>();
|
|
673
|
-
for (const toolId of allowedToolIds) {
|
|
699
|
+
for (const toolId of Array.from(allowedToolIds)) {
|
|
674
700
|
const tool = availableTools.get(toolId);
|
|
675
701
|
if (tool) {
|
|
676
702
|
filteredTools.set(toolId, tool);
|
|
@@ -747,35 +773,35 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
747
773
|
}>;
|
|
748
774
|
}> {
|
|
749
775
|
const { routes, session, context } = params;
|
|
750
|
-
|
|
776
|
+
|
|
751
777
|
// Evaluate all routes for completion
|
|
752
778
|
const completedRoutes: Route<TContext, TData>[] = [];
|
|
753
779
|
const pendingTransitions: Array<{
|
|
754
780
|
route: Route<TContext, TData>;
|
|
755
781
|
transitionConfig: RouteTransitionConfig<TContext, TData>;
|
|
756
782
|
}> = [];
|
|
757
|
-
|
|
783
|
+
|
|
758
784
|
for (const route of routes) {
|
|
759
785
|
if (route.isComplete(session.data || {})) {
|
|
760
786
|
completedRoutes.push(route);
|
|
761
|
-
|
|
787
|
+
|
|
762
788
|
// Check for onComplete transitions
|
|
763
789
|
const transitionConfig = await route.evaluateOnComplete(
|
|
764
790
|
{ data: session.data },
|
|
765
791
|
context
|
|
766
792
|
);
|
|
767
|
-
|
|
793
|
+
|
|
768
794
|
if (transitionConfig) {
|
|
769
795
|
pendingTransitions.push({ route, transitionConfig });
|
|
770
796
|
}
|
|
771
|
-
|
|
797
|
+
|
|
772
798
|
logger.debug(
|
|
773
799
|
`[ResponsePipeline] Route completed: ${route.title} ` +
|
|
774
800
|
`(${Math.round(route.getCompletionProgress(session.data || {}) * 100)}%)`
|
|
775
801
|
);
|
|
776
802
|
}
|
|
777
803
|
}
|
|
778
|
-
|
|
804
|
+
|
|
779
805
|
// Log completion status for all routes
|
|
780
806
|
if (completedRoutes.length > 0) {
|
|
781
807
|
logger.debug(
|
|
@@ -783,7 +809,7 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
783
809
|
`${completedRoutes.length}/${routes.length} routes complete`
|
|
784
810
|
);
|
|
785
811
|
}
|
|
786
|
-
|
|
812
|
+
|
|
787
813
|
return {
|
|
788
814
|
session,
|
|
789
815
|
completedRoutes,
|
|
@@ -801,15 +827,15 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
801
827
|
routes: Route<TContext, TData>[];
|
|
802
828
|
}): Promise<SessionState<TData>> {
|
|
803
829
|
const { session, dataUpdate, routes } = params;
|
|
804
|
-
|
|
830
|
+
|
|
805
831
|
// Update session data
|
|
806
832
|
const updatedSession = await this.updateData(session, dataUpdate);
|
|
807
|
-
|
|
833
|
+
|
|
808
834
|
// Update agent-level data if handler is available
|
|
809
835
|
if (this.updateCollectedData) {
|
|
810
836
|
await this.updateCollectedData(dataUpdate);
|
|
811
837
|
}
|
|
812
|
-
|
|
838
|
+
|
|
813
839
|
// Evaluate route completions after data update
|
|
814
840
|
const completionResults = await this.handleCrossRouteCompletion({
|
|
815
841
|
routes,
|
|
@@ -817,14 +843,14 @@ export class ResponsePipeline<TContext = unknown, TData = unknown> {
|
|
|
817
843
|
context: this.context!,
|
|
818
844
|
history: [],
|
|
819
845
|
});
|
|
820
|
-
|
|
846
|
+
|
|
821
847
|
// Log any newly completed routes
|
|
822
848
|
if (completionResults.completedRoutes.length > 0) {
|
|
823
849
|
logger.debug(
|
|
824
850
|
`[ResponsePipeline] Data update resulted in ${completionResults.completedRoutes.length} completed routes`
|
|
825
851
|
);
|
|
826
852
|
}
|
|
827
|
-
|
|
853
|
+
|
|
828
854
|
return completionResults.session;
|
|
829
855
|
}
|
|
830
856
|
}
|
package/src/core/Route.ts
CHANGED
|
@@ -18,6 +18,7 @@ import type {
|
|
|
18
18
|
} from "../types";
|
|
19
19
|
|
|
20
20
|
import { Step } from "./Step";
|
|
21
|
+
import { Agent } from './Agent'
|
|
21
22
|
import { generateRouteId } from "../utils/id";
|
|
22
23
|
import { END_ROUTE } from "../constants";
|
|
23
24
|
|
|
@@ -50,10 +51,13 @@ export class Route<TContext = unknown, TData = unknown> {
|
|
|
50
51
|
public routingExtrasSchema?: StructuredSchema;
|
|
51
52
|
public guidelines: Guideline<TContext>[] = [];
|
|
52
53
|
public terms: Term<TContext>[] = [];
|
|
53
|
-
public tools: Tool<TContext, TData
|
|
54
|
+
public tools: Tool<TContext, TData>[] = [];
|
|
54
55
|
public knowledgeBase: Record<string, unknown> = {};
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
// Reference to parent agent for ToolManager access
|
|
58
|
+
private parentAgent?: Agent<TContext, TData>;
|
|
59
|
+
|
|
60
|
+
constructor(options: RouteOptions<TContext, TData>, parentAgent?: Agent<TContext, TData>) {
|
|
57
61
|
// Use provided ID or generate a deterministic one from the title
|
|
58
62
|
this.id = options.id || generateRouteId(options.title);
|
|
59
63
|
this.title = options.title;
|
|
@@ -64,6 +68,9 @@ export class Route<TContext = unknown, TData = unknown> {
|
|
|
64
68
|
this.rules = options.rules || [];
|
|
65
69
|
this.prohibitions = options.prohibitions || [];
|
|
66
70
|
|
|
71
|
+
// Store reference to parent agent for ToolManager access
|
|
72
|
+
this.parentAgent = parentAgent;
|
|
73
|
+
|
|
67
74
|
// Handle initial step logic
|
|
68
75
|
let initialStepOptions = options.initialStep;
|
|
69
76
|
let stepsToChain: StepOptions<TContext, TData>[] = [];
|
|
@@ -79,7 +86,7 @@ export class Route<TContext = unknown, TData = unknown> {
|
|
|
79
86
|
}
|
|
80
87
|
}
|
|
81
88
|
|
|
82
|
-
this.initialStep = new Step<TContext, TData>(this.id, initialStepOptions);
|
|
89
|
+
this.initialStep = new Step<TContext, TData>(this.id, initialStepOptions, this.parentAgent);
|
|
83
90
|
|
|
84
91
|
// Store endStep spec (will be used when route completes)
|
|
85
92
|
this.endStepSpec = options.endStep || {
|
|
@@ -133,14 +140,13 @@ export class Route<TContext = unknown, TData = unknown> {
|
|
|
133
140
|
private buildSequentialSteps(
|
|
134
141
|
steps: Array<StepOptions<TContext, TData> | typeof END_ROUTE>
|
|
135
142
|
): void {
|
|
136
|
-
let
|
|
137
|
-
this.initialStep.asStepResult();
|
|
143
|
+
let currentStepResult: StepResult<TContext, TData> = this.initialStep.asStepResult();
|
|
138
144
|
|
|
139
145
|
for (const step of steps) {
|
|
140
146
|
if (step === END_ROUTE) {
|
|
141
|
-
|
|
147
|
+
currentStepResult.nextStep({ step: END_ROUTE });
|
|
142
148
|
} else {
|
|
143
|
-
|
|
149
|
+
currentStepResult = currentStepResult.nextStep(step);
|
|
144
150
|
}
|
|
145
151
|
}
|
|
146
152
|
}
|
|
@@ -168,7 +174,12 @@ export class Route<TContext = unknown, TData = unknown> {
|
|
|
168
174
|
/**
|
|
169
175
|
* Register a tool for this route
|
|
170
176
|
*/
|
|
171
|
-
createTool(tool: Tool<TContext, TData
|
|
177
|
+
createTool(tool: Tool<TContext, TData>): this {
|
|
178
|
+
// Validate tool before adding
|
|
179
|
+
if (!tool || !tool.id || !tool.handler) {
|
|
180
|
+
throw new Error(`Invalid tool: must have id and handler properties`);
|
|
181
|
+
}
|
|
182
|
+
|
|
172
183
|
this.tools.push(tool);
|
|
173
184
|
return this;
|
|
174
185
|
}
|
|
@@ -176,11 +187,28 @@ export class Route<TContext = unknown, TData = unknown> {
|
|
|
176
187
|
/**
|
|
177
188
|
* Register multiple tools for this route
|
|
178
189
|
*/
|
|
179
|
-
registerTools(tools: Tool<TContext, TData
|
|
190
|
+
registerTools(tools: Tool<TContext, TData>[]): this {
|
|
180
191
|
tools.forEach((tool) => this.createTool(tool));
|
|
181
192
|
return this;
|
|
182
193
|
}
|
|
183
194
|
|
|
195
|
+
/**
|
|
196
|
+
* Add a tool to this route using the ToolManager API
|
|
197
|
+
* Creates and adds the tool to route scope in one operation
|
|
198
|
+
*/
|
|
199
|
+
addTool(
|
|
200
|
+
tool: Tool<TContext, TData>
|
|
201
|
+
): this {
|
|
202
|
+
if (this.parentAgent && this.parentAgent.tool) {
|
|
203
|
+
// Use ToolManager to add to route scope - no casting needed with unified interface
|
|
204
|
+
this.parentAgent.tool.addToRoute(this, tool);
|
|
205
|
+
} else {
|
|
206
|
+
// Fallback: add tool directly to route tools
|
|
207
|
+
this.createTool(tool);
|
|
208
|
+
}
|
|
209
|
+
return this;
|
|
210
|
+
}
|
|
211
|
+
|
|
184
212
|
/**
|
|
185
213
|
* Get all guidelines for this route
|
|
186
214
|
*/
|
|
@@ -198,7 +226,7 @@ export class Route<TContext = unknown, TData = unknown> {
|
|
|
198
226
|
/**
|
|
199
227
|
* Get all tools for this route
|
|
200
228
|
*/
|
|
201
|
-
getTools(): Tool<TContext, TData
|
|
229
|
+
getTools(): Tool<TContext, TData>[] {
|
|
202
230
|
return [...this.tools];
|
|
203
231
|
}
|
|
204
232
|
|
|
@@ -311,8 +339,7 @@ export class Route<TContext = unknown, TData = unknown> {
|
|
|
311
339
|
const transitions = step.getTransitions();
|
|
312
340
|
for (const transition of transitions) {
|
|
313
341
|
lines.push(
|
|
314
|
-
` -> ${transition.id}${
|
|
315
|
-
transition.description ? `: ${transition.description}` : ""
|
|
342
|
+
` -> ${transition.id}${transition.description ? `: ${transition.description}` : ""
|
|
316
343
|
}`
|
|
317
344
|
);
|
|
318
345
|
}
|
|
@@ -70,6 +70,29 @@ export interface BuildRoutingPromptParams<TContext = unknown, TData = unknown> {
|
|
|
70
70
|
export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
71
71
|
constructor(private readonly options?: RoutingEngineOptions) { }
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Enter a route if not already in it, merging initial data
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
private enterRouteIfNeeded(
|
|
78
|
+
session: SessionState<TData>,
|
|
79
|
+
route: Route<TContext, TData>
|
|
80
|
+
): SessionState<TData> {
|
|
81
|
+
if (!session.currentRoute || session.currentRoute.id !== route.id) {
|
|
82
|
+
let updatedSession = enterRoute(session, route.id, route.title);
|
|
83
|
+
if (route.initialData) {
|
|
84
|
+
updatedSession = mergeCollected(updatedSession, route.initialData);
|
|
85
|
+
logger.debug(
|
|
86
|
+
`[RoutingEngine] Merged initial data for route ${route.title}:`,
|
|
87
|
+
route.initialData
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
logger.debug(`[RoutingEngine] Entered route: ${route.title}`);
|
|
91
|
+
return updatedSession;
|
|
92
|
+
}
|
|
93
|
+
return session;
|
|
94
|
+
}
|
|
95
|
+
|
|
73
96
|
/**
|
|
74
97
|
* Optimized decision for single-route scenarios
|
|
75
98
|
* Skips route scoring and only does step selection
|
|
@@ -94,26 +117,13 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
94
117
|
const { route, session, history, agentOptions, provider, context, signal } =
|
|
95
118
|
params;
|
|
96
119
|
|
|
97
|
-
let updatedSession = session;
|
|
98
120
|
const selectedRoute = route;
|
|
99
121
|
|
|
100
|
-
//
|
|
101
|
-
const
|
|
122
|
+
// Enter route if not already in it (this may merge initial data)
|
|
123
|
+
const updatedSession = this.enterRouteIfNeeded(session, route);
|
|
102
124
|
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
updatedSession = enterRoute(session, route.id, route.title);
|
|
106
|
-
if (route.initialData) {
|
|
107
|
-
updatedSession = mergeCollected(updatedSession, route.initialData);
|
|
108
|
-
logger.debug(
|
|
109
|
-
`[RoutingEngine] Single-route: Merged initial data:`,
|
|
110
|
-
route.initialData
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
logger.debug(
|
|
114
|
-
`[RoutingEngine] Single-route: Entered route: ${route.title}`
|
|
115
|
-
);
|
|
116
|
-
}
|
|
125
|
+
// Check if this single route is complete (use updated session data)
|
|
126
|
+
const completedRoutes = route.isComplete(updatedSession.data || {}) ? [route] : [];
|
|
117
127
|
|
|
118
128
|
// Get candidate steps
|
|
119
129
|
const currentStep = updatedSession.currentStep
|
|
@@ -130,9 +140,11 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
130
140
|
return { selectedRoute, session: updatedSession };
|
|
131
141
|
}
|
|
132
142
|
|
|
133
|
-
// If only one candidate,
|
|
143
|
+
// If only one candidate, no need for AI selection
|
|
134
144
|
if (candidates.length === 1) {
|
|
135
|
-
const
|
|
145
|
+
const candidate = candidates[0];
|
|
146
|
+
const isRouteComplete = candidate.isRouteComplete;
|
|
147
|
+
|
|
136
148
|
if (isRouteComplete) {
|
|
137
149
|
logger.debug(
|
|
138
150
|
`[RoutingEngine] Single-route: Route complete - all data collected, END_ROUTE reached`
|
|
@@ -147,11 +159,11 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
147
159
|
};
|
|
148
160
|
} else {
|
|
149
161
|
logger.debug(
|
|
150
|
-
`[RoutingEngine] Single-route: Only one valid step: ${
|
|
162
|
+
`[RoutingEngine] Single-route: Only one valid step: ${candidate.step.id}`
|
|
151
163
|
);
|
|
152
164
|
return {
|
|
153
165
|
selectedRoute,
|
|
154
|
-
selectedStep:
|
|
166
|
+
selectedStep: candidate.step,
|
|
155
167
|
session: updatedSession,
|
|
156
168
|
isRouteComplete: false,
|
|
157
169
|
completedRoutes,
|
|
@@ -159,6 +171,18 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
159
171
|
}
|
|
160
172
|
}
|
|
161
173
|
|
|
174
|
+
// No candidates means route is likely complete or has no valid next steps
|
|
175
|
+
if (candidates.length === 0) {
|
|
176
|
+
logger.debug(`[RoutingEngine] Single-route: No valid steps found, route may be complete`);
|
|
177
|
+
return {
|
|
178
|
+
selectedRoute,
|
|
179
|
+
selectedStep: undefined,
|
|
180
|
+
session: updatedSession,
|
|
181
|
+
isRouteComplete: true, // Assume complete if no valid steps
|
|
182
|
+
completedRoutes,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
162
186
|
// Multiple candidates - use AI to select best step
|
|
163
187
|
const lastUserMessage = getLastMessageFromHistory(history);
|
|
164
188
|
const stepPrompt = await this.buildStepSelectionPrompt({
|
|
@@ -573,27 +597,7 @@ export class RoutingEngine<TContext = unknown, TData = unknown> {
|
|
|
573
597
|
|
|
574
598
|
if (selectedRoute) {
|
|
575
599
|
logger.debug(`[RoutingEngine] Selected route: ${selectedRoute.title}`);
|
|
576
|
-
|
|
577
|
-
!session.currentRoute ||
|
|
578
|
-
session.currentRoute.id !== selectedRoute.id
|
|
579
|
-
) {
|
|
580
|
-
updatedSession = enterRoute(
|
|
581
|
-
session,
|
|
582
|
-
selectedRoute.id,
|
|
583
|
-
selectedRoute.title
|
|
584
|
-
);
|
|
585
|
-
if (selectedRoute.initialData) {
|
|
586
|
-
updatedSession = mergeCollected(
|
|
587
|
-
updatedSession,
|
|
588
|
-
selectedRoute.initialData
|
|
589
|
-
);
|
|
590
|
-
logger.debug(
|
|
591
|
-
`[RoutingEngine] Merged initial data:`,
|
|
592
|
-
selectedRoute.initialData
|
|
593
|
-
);
|
|
594
|
-
}
|
|
595
|
-
logger.debug(`[RoutingEngine] Entered route: ${selectedRoute.title}`);
|
|
596
|
-
}
|
|
600
|
+
updatedSession = this.enterRouteIfNeeded(updatedSession, selectedRoute);
|
|
597
601
|
}
|
|
598
602
|
}
|
|
599
603
|
|