@falai/agent 0.6.9 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -59
- package/dist/adapters/MemoryAdapter.js +2 -2
- package/dist/adapters/MemoryAdapter.js.map +1 -1
- package/dist/adapters/MongoAdapter.js +2 -2
- package/dist/adapters/MongoAdapter.js.map +1 -1
- package/dist/adapters/OpenSearchAdapter.js +7 -7
- package/dist/adapters/OpenSearchAdapter.js.map +1 -1
- package/dist/adapters/PostgreSQLAdapter.js +9 -9
- package/dist/adapters/PostgreSQLAdapter.js.map +1 -1
- package/dist/adapters/PrismaAdapter.js +3 -3
- package/dist/adapters/PrismaAdapter.js.map +1 -1
- package/dist/adapters/RedisAdapter.js +2 -2
- package/dist/adapters/RedisAdapter.js.map +1 -1
- package/dist/adapters/SQLiteAdapter.d.ts +3 -3
- package/dist/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/dist/adapters/SQLiteAdapter.js +11 -11
- package/dist/adapters/SQLiteAdapter.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/cjs/adapters/MemoryAdapter.js +2 -2
- package/dist/cjs/adapters/MemoryAdapter.js.map +1 -1
- package/dist/cjs/adapters/MongoAdapter.js +2 -2
- package/dist/cjs/adapters/MongoAdapter.js.map +1 -1
- package/dist/cjs/adapters/OpenSearchAdapter.js +7 -7
- package/dist/cjs/adapters/OpenSearchAdapter.js.map +1 -1
- package/dist/cjs/adapters/PostgreSQLAdapter.js +9 -9
- package/dist/cjs/adapters/PostgreSQLAdapter.js.map +1 -1
- package/dist/cjs/adapters/PrismaAdapter.js +3 -3
- package/dist/cjs/adapters/PrismaAdapter.js.map +1 -1
- package/dist/cjs/adapters/RedisAdapter.js +2 -2
- package/dist/cjs/adapters/RedisAdapter.js.map +1 -1
- package/dist/cjs/adapters/SQLiteAdapter.d.ts +3 -3
- package/dist/cjs/adapters/SQLiteAdapter.d.ts.map +1 -1
- package/dist/cjs/adapters/SQLiteAdapter.js +11 -11
- package/dist/cjs/adapters/SQLiteAdapter.js.map +1 -1
- package/dist/cjs/adapters/index.d.ts +1 -1
- package/dist/cjs/adapters/index.d.ts.map +1 -1
- package/dist/cjs/constants/index.d.ts +4 -4
- package/dist/cjs/constants/index.js +5 -5
- package/dist/cjs/core/Agent.d.ts +22 -22
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +160 -152
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/Events.d.ts +6 -6
- package/dist/cjs/core/Events.d.ts.map +1 -1
- package/dist/cjs/core/PersistenceManager.d.ts +13 -13
- package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
- package/dist/cjs/core/PersistenceManager.js +24 -24
- package/dist/cjs/core/PersistenceManager.js.map +1 -1
- package/dist/cjs/core/ResponseEngine.d.ts +3 -8
- package/dist/cjs/core/ResponseEngine.d.ts.map +1 -1
- package/dist/cjs/core/ResponseEngine.js +8 -8
- package/dist/cjs/core/ResponseEngine.js.map +1 -1
- package/dist/cjs/core/Route.d.ts +17 -17
- package/dist/cjs/core/Route.d.ts.map +1 -1
- package/dist/cjs/core/Route.js +33 -33
- package/dist/cjs/core/Route.js.map +1 -1
- package/dist/cjs/core/RoutingEngine.d.ts +30 -30
- package/dist/cjs/core/RoutingEngine.d.ts.map +1 -1
- package/dist/cjs/core/RoutingEngine.js +192 -192
- package/dist/cjs/core/RoutingEngine.js.map +1 -1
- package/dist/cjs/core/Step.d.ts +72 -0
- package/dist/cjs/core/Step.d.ts.map +1 -0
- package/dist/cjs/core/Step.js +150 -0
- package/dist/cjs/core/Step.js.map +1 -0
- package/dist/cjs/core/ToolExecutor.d.ts +5 -5
- package/dist/cjs/core/ToolExecutor.d.ts.map +1 -1
- package/dist/cjs/core/ToolExecutor.js +8 -8
- package/dist/cjs/core/ToolExecutor.js.map +1 -1
- package/dist/cjs/core/Transition.d.ts +14 -14
- package/dist/cjs/core/Transition.d.ts.map +1 -1
- package/dist/cjs/core/Transition.js +48 -19
- package/dist/cjs/core/Transition.js.map +1 -1
- package/dist/cjs/index.d.ts +7 -7
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +8 -8
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +8 -8
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/ai.d.ts +2 -2
- package/dist/cjs/types/ai.d.ts.map +1 -1
- package/dist/cjs/types/history.d.ts +3 -3
- package/dist/cjs/types/history.d.ts.map +1 -1
- package/dist/cjs/types/index.d.ts +1 -1
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/persistence.d.ts +5 -5
- package/dist/cjs/types/persistence.d.ts.map +1 -1
- package/dist/cjs/types/route.d.ts +57 -52
- package/dist/cjs/types/route.d.ts.map +1 -1
- package/dist/cjs/types/session.d.ts +27 -27
- package/dist/cjs/types/session.d.ts.map +1 -1
- package/dist/cjs/types/session.js +48 -50
- package/dist/cjs/types/session.js.map +1 -1
- package/dist/cjs/types/tool.d.ts +13 -13
- package/dist/cjs/types/tool.d.ts.map +1 -1
- package/dist/cjs/utils/id.d.ts +8 -3
- package/dist/cjs/utils/id.d.ts.map +1 -1
- package/dist/cjs/utils/id.js +16 -7
- package/dist/cjs/utils/id.js.map +1 -1
- package/dist/constants/index.d.ts +4 -4
- package/dist/constants/index.js +4 -4
- package/dist/core/Agent.d.ts +22 -22
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +162 -154
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/Events.d.ts +6 -6
- package/dist/core/Events.d.ts.map +1 -1
- package/dist/core/PersistenceManager.d.ts +13 -13
- package/dist/core/PersistenceManager.d.ts.map +1 -1
- package/dist/core/PersistenceManager.js +25 -25
- package/dist/core/PersistenceManager.js.map +1 -1
- package/dist/core/ResponseEngine.d.ts +3 -8
- package/dist/core/ResponseEngine.d.ts.map +1 -1
- package/dist/core/ResponseEngine.js +8 -8
- package/dist/core/ResponseEngine.js.map +1 -1
- package/dist/core/Route.d.ts +17 -17
- package/dist/core/Route.d.ts.map +1 -1
- package/dist/core/Route.js +33 -33
- package/dist/core/Route.js.map +1 -1
- package/dist/core/RoutingEngine.d.ts +30 -30
- package/dist/core/RoutingEngine.d.ts.map +1 -1
- package/dist/core/RoutingEngine.js +193 -193
- package/dist/core/RoutingEngine.js.map +1 -1
- package/dist/core/Step.d.ts +72 -0
- package/dist/core/Step.d.ts.map +1 -0
- package/dist/core/Step.js +146 -0
- package/dist/core/Step.js.map +1 -0
- package/dist/core/ToolExecutor.d.ts +5 -5
- package/dist/core/ToolExecutor.d.ts.map +1 -1
- package/dist/core/ToolExecutor.js +8 -8
- package/dist/core/ToolExecutor.js.map +1 -1
- package/dist/core/Transition.d.ts +14 -14
- package/dist/core/Transition.d.ts.map +1 -1
- package/dist/core/Transition.js +48 -19
- package/dist/core/Transition.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/types/agent.d.ts +8 -8
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/ai.d.ts +2 -2
- package/dist/types/ai.d.ts.map +1 -1
- package/dist/types/history.d.ts +3 -3
- package/dist/types/history.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/persistence.d.ts +5 -5
- package/dist/types/persistence.d.ts.map +1 -1
- package/dist/types/route.d.ts +57 -52
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/session.d.ts +27 -27
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js +44 -46
- package/dist/types/session.js.map +1 -1
- package/dist/types/tool.d.ts +13 -13
- package/dist/types/tool.d.ts.map +1 -1
- package/dist/utils/id.d.ts +8 -3
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +14 -6
- package/dist/utils/id.js.map +1 -1
- package/docs/ADAPTERS.md +21 -21
- package/docs/AGENT.md +57 -55
- package/docs/API_REFERENCE.md +218 -220
- package/docs/ARCHITECTURE.md +99 -104
- package/docs/CONTEXT_MANAGEMENT.md +81 -88
- package/docs/DOCS.md +18 -18
- package/docs/DOMAINS.md +16 -16
- package/docs/EXAMPLES.md +43 -43
- package/docs/GETTING_STARTED.md +60 -63
- package/docs/PERSISTENCE.md +66 -70
- package/docs/PROVIDERS.md +2 -2
- package/docs/README.md +6 -6
- package/docs/ROUTES.md +218 -220
- package/docs/STEPS.md +883 -0
- package/examples/business-onboarding.ts +84 -81
- package/examples/company-qna-agent.ts +68 -67
- package/examples/custom-database-persistence.ts +87 -89
- package/examples/declarative-agent.ts +32 -32
- package/examples/domain-scoping.ts +18 -18
- package/examples/extracted-data-modification.ts +92 -97
- package/examples/healthcare-agent.ts +89 -91
- package/examples/openai-agent.ts +29 -32
- package/examples/opensearch-persistence.ts +43 -45
- package/examples/persistent-onboarding.ts +65 -66
- package/examples/prisma-persistence.ts +108 -112
- package/examples/prisma-schema.example.prisma +3 -3
- package/examples/redis-persistence.ts +67 -73
- package/examples/route-transitions.ts +71 -47
- package/examples/rules-prohibitions.ts +28 -28
- package/examples/streaming-agent.ts +24 -24
- package/examples/travel-agent.ts +94 -109
- package/package.json +1 -1
- package/src/adapters/MemoryAdapter.ts +3 -3
- package/src/adapters/MongoAdapter.ts +3 -3
- package/src/adapters/OpenSearchAdapter.ts +8 -8
- package/src/adapters/PostgreSQLAdapter.ts +10 -10
- package/src/adapters/PrismaAdapter.ts +4 -4
- package/src/adapters/RedisAdapter.ts +3 -3
- package/src/adapters/SQLiteAdapter.ts +15 -15
- package/src/adapters/index.ts +1 -1
- package/src/constants/index.ts +4 -4
- package/src/core/Agent.ts +210 -206
- package/src/core/Events.ts +12 -12
- package/src/core/PersistenceManager.ts +32 -36
- package/src/core/ResponseEngine.ts +11 -17
- package/src/core/Route.ts +55 -49
- package/src/core/RoutingEngine.ts +244 -252
- package/src/core/Step.ts +197 -0
- package/src/core/ToolExecutor.ts +11 -11
- package/src/core/Transition.ts +72 -26
- package/src/index.ts +8 -8
- package/src/types/agent.ts +8 -8
- package/src/types/ai.ts +2 -2
- package/src/types/history.ts +3 -3
- package/src/types/index.ts +1 -1
- package/src/types/persistence.ts +6 -6
- package/src/types/route.ts +77 -61
- package/src/types/session.ts +75 -78
- package/src/types/tool.ts +17 -17
- package/src/utils/id.ts +15 -6
- package/dist/cjs/core/State.d.ts +0 -72
- package/dist/cjs/core/State.d.ts.map +0 -1
- package/dist/cjs/core/State.js +0 -148
- package/dist/cjs/core/State.js.map +0 -1
- package/dist/core/State.d.ts +0 -72
- package/dist/core/State.d.ts.map +0 -1
- package/dist/core/State.js +0 -144
- package/dist/core/State.js.map +0 -1
- package/docs/STATES.md +0 -888
- package/src/core/State.ts +0 -212
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { Event } from "../types/history";
|
|
2
2
|
import type { Route } from "./Route";
|
|
3
|
-
import type {
|
|
3
|
+
import type { Step } from "./Step";
|
|
4
4
|
import type { StructuredSchema } from "../types/schema";
|
|
5
5
|
import type { RoutingDecision } from "../types/routing";
|
|
6
|
-
import type {
|
|
6
|
+
import type { SessionStep } from "../types/session";
|
|
7
7
|
import type { AiProvider } from "../types/ai";
|
|
8
|
-
import { enterRoute,
|
|
8
|
+
import { enterRoute, mergeCollected } from "../types/session";
|
|
9
9
|
import { PromptComposer } from "./PromptComposer";
|
|
10
10
|
import { getLastMessageFromHistory } from "../utils/event";
|
|
11
11
|
import { logger } from "../utils/logger";
|
|
@@ -13,8 +13,8 @@ import { logger } from "../utils/logger";
|
|
|
13
13
|
export interface RoutingDecisionOutput {
|
|
14
14
|
context: string;
|
|
15
15
|
routes: Record<string, number>;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
selectedStepId?: string; // For active route, which step to transition to
|
|
17
|
+
stepReasoning?: string; // Why this step was selected
|
|
18
18
|
responseDirectives?: string[];
|
|
19
19
|
extractions?: Array<{
|
|
20
20
|
name: string;
|
|
@@ -37,12 +37,12 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* Optimized decision for single-route scenarios
|
|
40
|
-
* Skips route scoring and only does
|
|
40
|
+
* Skips route scoring and only does step selection
|
|
41
41
|
* @private
|
|
42
42
|
*/
|
|
43
|
-
private async
|
|
43
|
+
private async decideSingleRouteStep(params: {
|
|
44
44
|
route: Route<TContext, unknown>;
|
|
45
|
-
session:
|
|
45
|
+
session: SessionStep;
|
|
46
46
|
history: Event[];
|
|
47
47
|
agentMeta?: {
|
|
48
48
|
name?: string;
|
|
@@ -55,9 +55,9 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
55
55
|
signal?: AbortSignal;
|
|
56
56
|
}): Promise<{
|
|
57
57
|
selectedRoute?: Route<TContext>;
|
|
58
|
-
|
|
58
|
+
selectedStep?: Step<TContext>;
|
|
59
59
|
responseDirectives?: string[];
|
|
60
|
-
session:
|
|
60
|
+
session: SessionStep;
|
|
61
61
|
isRouteComplete?: boolean;
|
|
62
62
|
}> {
|
|
63
63
|
const { route, session, history, agentMeta, ai, context, signal } = params;
|
|
@@ -69,7 +69,7 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
69
69
|
if (!session.currentRoute || session.currentRoute.id !== route.id) {
|
|
70
70
|
updatedSession = enterRoute(session, route.id, route.title);
|
|
71
71
|
if (route.initialData) {
|
|
72
|
-
updatedSession =
|
|
72
|
+
updatedSession = mergeCollected(updatedSession, route.initialData);
|
|
73
73
|
logger.debug(
|
|
74
74
|
`[RoutingEngine] Single-route: Merged initial data:`,
|
|
75
75
|
route.initialData
|
|
@@ -80,18 +80,18 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
80
80
|
);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
// Get candidate
|
|
84
|
-
const
|
|
85
|
-
? route.
|
|
83
|
+
// Get candidate steps
|
|
84
|
+
const currentStep = updatedSession.currentStep
|
|
85
|
+
? route.getStep(updatedSession.currentStep.id)
|
|
86
86
|
: undefined;
|
|
87
|
-
const candidates = this.
|
|
87
|
+
const candidates = this.getCandidateSteps(
|
|
88
88
|
route,
|
|
89
|
-
|
|
90
|
-
updatedSession.
|
|
89
|
+
currentStep,
|
|
90
|
+
updatedSession.data || {}
|
|
91
91
|
);
|
|
92
92
|
|
|
93
93
|
if (candidates.length === 0) {
|
|
94
|
-
logger.warn(`[RoutingEngine] Single-route: No valid
|
|
94
|
+
logger.warn(`[RoutingEngine] Single-route: No valid steps found`);
|
|
95
95
|
return { selectedRoute, session: updatedSession };
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -100,233 +100,229 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
100
100
|
const isRouteComplete = candidates[0].isRouteComplete;
|
|
101
101
|
if (isRouteComplete) {
|
|
102
102
|
logger.debug(
|
|
103
|
-
`[RoutingEngine] Single-route: Route complete - all data collected,
|
|
103
|
+
`[RoutingEngine] Single-route: Route complete - all data collected, END_ROUTE reached`
|
|
104
104
|
);
|
|
105
|
-
// Don't return a
|
|
105
|
+
// Don't return a selectedStep when route is complete - there's no step to enter
|
|
106
106
|
return {
|
|
107
107
|
selectedRoute,
|
|
108
|
-
|
|
108
|
+
selectedStep: undefined,
|
|
109
109
|
session: updatedSession,
|
|
110
110
|
isRouteComplete: true,
|
|
111
111
|
};
|
|
112
112
|
} else {
|
|
113
113
|
logger.debug(
|
|
114
|
-
`[RoutingEngine] Single-route: Only one valid
|
|
114
|
+
`[RoutingEngine] Single-route: Only one valid step: ${candidates[0].step.id}`
|
|
115
115
|
);
|
|
116
116
|
return {
|
|
117
117
|
selectedRoute,
|
|
118
|
-
|
|
118
|
+
selectedStep: candidates[0].step,
|
|
119
119
|
session: updatedSession,
|
|
120
120
|
isRouteComplete: false,
|
|
121
121
|
};
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
// Multiple candidates - use AI to select best
|
|
125
|
+
// Multiple candidates - use AI to select best step
|
|
126
126
|
const lastUserMessage = getLastMessageFromHistory(history);
|
|
127
|
-
const
|
|
127
|
+
const stepPrompt = this.buildStepSelectionPrompt(
|
|
128
128
|
route,
|
|
129
|
-
|
|
129
|
+
currentStep,
|
|
130
130
|
candidates,
|
|
131
|
-
updatedSession.
|
|
131
|
+
updatedSession.data || {},
|
|
132
132
|
history,
|
|
133
133
|
lastUserMessage,
|
|
134
134
|
agentMeta
|
|
135
135
|
);
|
|
136
136
|
|
|
137
|
-
const
|
|
138
|
-
candidates.map((c) => c.
|
|
137
|
+
const stepSchema = this.buildStepSelectionSchema(
|
|
138
|
+
candidates.map((c) => c.step.id)
|
|
139
139
|
);
|
|
140
140
|
|
|
141
|
-
const
|
|
141
|
+
const stepResult = await ai.generateMessage<
|
|
142
142
|
TContext,
|
|
143
143
|
{
|
|
144
144
|
reasoning: string;
|
|
145
|
-
|
|
145
|
+
selectedStepId: string;
|
|
146
146
|
responseDirectives?: string[];
|
|
147
147
|
}
|
|
148
148
|
>({
|
|
149
|
-
prompt:
|
|
149
|
+
prompt: stepPrompt,
|
|
150
150
|
history,
|
|
151
151
|
context,
|
|
152
152
|
signal,
|
|
153
153
|
parameters: {
|
|
154
|
-
jsonSchema:
|
|
155
|
-
schemaName: "
|
|
154
|
+
jsonSchema: stepSchema,
|
|
155
|
+
schemaName: "step_selection",
|
|
156
156
|
},
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
(c) => c.
|
|
162
|
-
)?.
|
|
159
|
+
const selectedStepId = stepResult.structured?.selectedStepId;
|
|
160
|
+
const selectedStep = candidates.find(
|
|
161
|
+
(c) => c.step.id === selectedStepId
|
|
162
|
+
)?.step;
|
|
163
163
|
|
|
164
|
-
if (
|
|
164
|
+
if (selectedStep) {
|
|
165
165
|
logger.debug(
|
|
166
|
-
`[RoutingEngine] Single-route: AI selected
|
|
166
|
+
`[RoutingEngine] Single-route: AI selected step: ${selectedStep.id}`
|
|
167
167
|
);
|
|
168
168
|
logger.debug(
|
|
169
|
-
`[RoutingEngine] Single-route: Reasoning: ${
|
|
169
|
+
`[RoutingEngine] Single-route: Reasoning: ${stepResult.structured?.reasoning}`
|
|
170
170
|
);
|
|
171
171
|
} else {
|
|
172
172
|
logger.warn(
|
|
173
|
-
`[RoutingEngine] Single-route: Invalid
|
|
173
|
+
`[RoutingEngine] Single-route: Invalid step ID returned, using first candidate`
|
|
174
174
|
);
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
return {
|
|
178
178
|
selectedRoute,
|
|
179
|
-
|
|
180
|
-
responseDirectives:
|
|
179
|
+
selectedStep: selectedStep || candidates[0].step,
|
|
180
|
+
responseDirectives: stepResult.structured?.responseDirectives,
|
|
181
181
|
session: updatedSession,
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
/**
|
|
186
|
-
* Recursively traverse
|
|
186
|
+
* Recursively traverse step chain to find first non-skipped step or END_ROUTE
|
|
187
187
|
* @private
|
|
188
188
|
*/
|
|
189
|
-
private
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
private findFirstValidStepRecursive<TData = unknown>(
|
|
190
|
+
currentStep: Step<TContext, TData>,
|
|
191
|
+
data: Partial<TData>,
|
|
192
192
|
visited: Set<string>
|
|
193
193
|
): {
|
|
194
|
-
|
|
194
|
+
step?: Step<TContext, TData>;
|
|
195
195
|
condition?: string;
|
|
196
196
|
isRouteComplete?: boolean;
|
|
197
197
|
} {
|
|
198
198
|
// Prevent infinite loops
|
|
199
|
-
if (visited.has(
|
|
199
|
+
if (visited.has(currentStep.id)) {
|
|
200
200
|
return {};
|
|
201
201
|
}
|
|
202
|
-
visited.add(
|
|
202
|
+
visited.add(currentStep.id);
|
|
203
203
|
|
|
204
|
-
const transitions =
|
|
204
|
+
const transitions = currentStep.getTransitions();
|
|
205
205
|
|
|
206
206
|
for (const transition of transitions) {
|
|
207
207
|
const target = transition.getTarget();
|
|
208
208
|
|
|
209
|
-
// Check for
|
|
209
|
+
// Check for END_ROUTE transition
|
|
210
210
|
if (
|
|
211
211
|
!target &&
|
|
212
|
-
transition.spec.
|
|
213
|
-
typeof transition.spec.
|
|
212
|
+
transition.spec.step &&
|
|
213
|
+
typeof transition.spec.step === "symbol"
|
|
214
214
|
) {
|
|
215
|
-
// Found
|
|
215
|
+
// Found END_ROUTE - route is complete
|
|
216
216
|
return {
|
|
217
|
-
isRouteComplete: true
|
|
217
|
+
isRouteComplete: true,
|
|
218
218
|
};
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
if (!target) continue;
|
|
222
222
|
|
|
223
|
-
// If target should NOT be skipped, we found our
|
|
224
|
-
if (!target.shouldSkip(
|
|
223
|
+
// If target should NOT be skipped, we found our step
|
|
224
|
+
if (!target.shouldSkip(data)) {
|
|
225
225
|
logger.debug(
|
|
226
|
-
`[RoutingEngine] Found valid
|
|
226
|
+
`[RoutingEngine] Found valid step after skipping: ${target.id}`
|
|
227
227
|
);
|
|
228
228
|
return {
|
|
229
|
-
|
|
229
|
+
step: target,
|
|
230
230
|
condition: transition.condition,
|
|
231
231
|
};
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
// Target should be skipped too - recurse deeper
|
|
235
235
|
logger.debug(
|
|
236
|
-
`[RoutingEngine] Skipping
|
|
237
|
-
);
|
|
238
|
-
const result = this.findFirstValidStateRecursive(
|
|
239
|
-
target,
|
|
240
|
-
extracted,
|
|
241
|
-
visited
|
|
236
|
+
`[RoutingEngine] Skipping step ${target.id} (skipIf condition met), continuing traversal...`
|
|
242
237
|
);
|
|
238
|
+
const result = this.findFirstValidStepRecursive(target, data, visited);
|
|
243
239
|
|
|
244
|
-
// If we found something (a valid
|
|
245
|
-
if (result.
|
|
240
|
+
// If we found something (a valid step or END_ROUTE), return it
|
|
241
|
+
if (result.step || result.isRouteComplete) {
|
|
246
242
|
return result;
|
|
247
243
|
}
|
|
248
244
|
}
|
|
249
245
|
|
|
250
|
-
// No valid
|
|
246
|
+
// No valid steps or END_ROUTE found in this branch
|
|
251
247
|
return {};
|
|
252
248
|
}
|
|
253
249
|
|
|
254
250
|
/**
|
|
255
|
-
* Identify valid next candidate
|
|
256
|
-
* Returns
|
|
251
|
+
* Identify valid next candidate steps based on current step and collected data
|
|
252
|
+
* Returns step with isRouteComplete flag if route is complete (all steps skipped + has END_ROUTE transition)
|
|
257
253
|
*/
|
|
258
|
-
|
|
259
|
-
route: Route<TContext,
|
|
260
|
-
|
|
261
|
-
|
|
254
|
+
getCandidateSteps<TData = unknown>(
|
|
255
|
+
route: Route<TContext, TData>,
|
|
256
|
+
currentStep: Step<TContext, TData> | undefined,
|
|
257
|
+
data: Partial<TData>
|
|
262
258
|
): Array<{
|
|
263
|
-
|
|
259
|
+
step: Step<TContext, TData>;
|
|
264
260
|
condition?: string;
|
|
265
|
-
|
|
266
|
-
|
|
261
|
+
requires?: string[];
|
|
262
|
+
collectFields?: string[];
|
|
267
263
|
isRouteComplete?: boolean;
|
|
268
264
|
}> {
|
|
269
265
|
const candidates: Array<{
|
|
270
|
-
|
|
266
|
+
step: Step<TContext, TData>;
|
|
271
267
|
condition?: string;
|
|
272
|
-
|
|
273
|
-
|
|
268
|
+
requires?: string[];
|
|
269
|
+
collectFields?: string[];
|
|
274
270
|
isRouteComplete?: boolean;
|
|
275
271
|
}> = [];
|
|
276
272
|
|
|
277
|
-
if (!
|
|
278
|
-
const
|
|
279
|
-
if (
|
|
280
|
-
// Initial
|
|
281
|
-
const result = this.
|
|
282
|
-
|
|
283
|
-
|
|
273
|
+
if (!currentStep) {
|
|
274
|
+
const initialStep = route.initialStep;
|
|
275
|
+
if (initialStep.shouldSkip(data)) {
|
|
276
|
+
// Initial step should be skipped - recursively traverse to find first non-skipped step or END_ROUTE
|
|
277
|
+
const result = this.findFirstValidStepRecursive(
|
|
278
|
+
initialStep,
|
|
279
|
+
data,
|
|
284
280
|
new Set<string>()
|
|
285
281
|
);
|
|
286
282
|
|
|
287
283
|
if (result.isRouteComplete) {
|
|
288
|
-
// All
|
|
284
|
+
// All steps are skipped and we reached END_ROUTE
|
|
289
285
|
logger.debug(
|
|
290
|
-
`[RoutingEngine] Route complete on entry: all
|
|
286
|
+
`[RoutingEngine] Route complete on entry: all steps skipped, END_ROUTE reached`
|
|
291
287
|
);
|
|
292
288
|
return [
|
|
293
289
|
{
|
|
294
|
-
|
|
290
|
+
step: initialStep,
|
|
295
291
|
condition: "Route complete - all data collected on entry",
|
|
296
292
|
isRouteComplete: true,
|
|
297
293
|
},
|
|
298
294
|
];
|
|
299
|
-
} else if (result.
|
|
300
|
-
// Found a non-skipped
|
|
295
|
+
} else if (result.step) {
|
|
296
|
+
// Found a non-skipped step
|
|
301
297
|
candidates.push({
|
|
302
|
-
|
|
298
|
+
step: result.step,
|
|
303
299
|
condition: result.condition,
|
|
304
|
-
|
|
305
|
-
|
|
300
|
+
requires: result.step.requires,
|
|
301
|
+
collectFields: result.step.collectFields,
|
|
306
302
|
});
|
|
307
303
|
}
|
|
308
|
-
// If no
|
|
304
|
+
// If no step found and not complete, fall through to return empty candidates
|
|
309
305
|
} else {
|
|
310
306
|
candidates.push({
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
307
|
+
step: initialStep,
|
|
308
|
+
requires: initialStep.requires,
|
|
309
|
+
collectFields: initialStep.collectFields,
|
|
314
310
|
});
|
|
315
311
|
}
|
|
316
312
|
return candidates;
|
|
317
313
|
}
|
|
318
314
|
|
|
319
|
-
const transitions =
|
|
315
|
+
const transitions = currentStep.getTransitions();
|
|
320
316
|
let hasEndRoute = false;
|
|
321
317
|
|
|
322
318
|
for (const transition of transitions) {
|
|
323
319
|
const target = transition.getTarget();
|
|
324
320
|
|
|
325
|
-
// Check for
|
|
321
|
+
// Check for END_ROUTE transition (no target step)
|
|
326
322
|
if (
|
|
327
323
|
!target &&
|
|
328
|
-
transition.spec.
|
|
329
|
-
typeof transition.spec.
|
|
324
|
+
transition.spec.step &&
|
|
325
|
+
typeof transition.spec.step === "symbol"
|
|
330
326
|
) {
|
|
331
327
|
hasEndRoute = true;
|
|
332
328
|
continue;
|
|
@@ -334,64 +330,64 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
334
330
|
|
|
335
331
|
if (!target) continue;
|
|
336
332
|
|
|
337
|
-
if (target.shouldSkip(
|
|
333
|
+
if (target.shouldSkip(data)) {
|
|
338
334
|
logger.debug(
|
|
339
|
-
`[RoutingEngine] Skipping
|
|
335
|
+
`[RoutingEngine] Skipping step ${target.id} (skipIf condition met)`
|
|
340
336
|
);
|
|
341
337
|
|
|
342
|
-
// Recursively traverse to find next valid
|
|
343
|
-
const result = this.
|
|
338
|
+
// Recursively traverse to find next valid step or END_ROUTE
|
|
339
|
+
const result = this.findFirstValidStepRecursive(
|
|
344
340
|
target,
|
|
345
|
-
|
|
346
|
-
new Set<string>([
|
|
341
|
+
data,
|
|
342
|
+
new Set<string>([currentStep.id]) // Already visited current step
|
|
347
343
|
);
|
|
348
344
|
|
|
349
345
|
if (result.isRouteComplete) {
|
|
350
346
|
hasEndRoute = true;
|
|
351
|
-
} else if (result.
|
|
352
|
-
// Found a non-skipped
|
|
347
|
+
} else if (result.step) {
|
|
348
|
+
// Found a non-skipped step deeper in the chain
|
|
353
349
|
candidates.push({
|
|
354
|
-
|
|
350
|
+
step: result.step,
|
|
355
351
|
condition: result.condition || transition.condition,
|
|
356
|
-
|
|
357
|
-
|
|
352
|
+
requires: result.step.requires,
|
|
353
|
+
collectFields: result.step.collectFields,
|
|
358
354
|
});
|
|
359
355
|
}
|
|
360
356
|
continue;
|
|
361
357
|
}
|
|
362
358
|
|
|
363
359
|
candidates.push({
|
|
364
|
-
|
|
360
|
+
step: target,
|
|
365
361
|
condition: transition.condition,
|
|
366
|
-
|
|
367
|
-
|
|
362
|
+
requires: target.requires,
|
|
363
|
+
collectFields: target.collectFields,
|
|
368
364
|
});
|
|
369
365
|
}
|
|
370
366
|
|
|
371
367
|
// If no valid candidates found
|
|
372
368
|
if (candidates.length === 0) {
|
|
373
|
-
// If current
|
|
369
|
+
// If current step has END_ROUTE transition, the route is complete
|
|
374
370
|
if (hasEndRoute) {
|
|
375
371
|
logger.debug(
|
|
376
|
-
`[RoutingEngine] Route complete: all
|
|
372
|
+
`[RoutingEngine] Route complete: all steps processed, END_ROUTE reached`
|
|
377
373
|
);
|
|
378
|
-
// Return current
|
|
374
|
+
// Return current step with completion flag
|
|
379
375
|
return [
|
|
380
376
|
{
|
|
381
|
-
|
|
377
|
+
step: currentStep,
|
|
382
378
|
condition: "Route complete - all data collected",
|
|
383
379
|
isRouteComplete: true,
|
|
384
380
|
},
|
|
385
381
|
];
|
|
386
382
|
}
|
|
387
383
|
|
|
388
|
-
// Otherwise, stay in current
|
|
389
|
-
if (!
|
|
384
|
+
// Otherwise, stay in current step if it's still valid
|
|
385
|
+
if (!currentStep.shouldSkip(data)) {
|
|
390
386
|
candidates.push({
|
|
391
|
-
|
|
392
|
-
condition: "Continue in current
|
|
393
|
-
|
|
394
|
-
|
|
387
|
+
step: currentStep,
|
|
388
|
+
condition: "Continue in current step (no valid transitions)",
|
|
389
|
+
requires: currentStep.requires,
|
|
390
|
+
collectFields: currentStep.collectFields,
|
|
395
391
|
});
|
|
396
392
|
}
|
|
397
393
|
}
|
|
@@ -400,14 +396,14 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
400
396
|
}
|
|
401
397
|
|
|
402
398
|
/**
|
|
403
|
-
* Full routing orchestration: builds prompt and schema, calls AI, selects route/
|
|
399
|
+
* Full routing orchestration: builds prompt and schema, calls AI, selects route/step,
|
|
404
400
|
* and updates the session (including initialData merge when entering a new route).
|
|
405
401
|
*
|
|
406
|
-
* OPTIMIZATION: If there's only 1 route, skips route scoring and only does
|
|
402
|
+
* OPTIMIZATION: If there's only 1 route, skips route scoring and only does step selection.
|
|
407
403
|
*/
|
|
408
|
-
async
|
|
404
|
+
async decideRouteAndStep(params: {
|
|
409
405
|
routes: Route<TContext, unknown>[];
|
|
410
|
-
session:
|
|
406
|
+
session: SessionStep;
|
|
411
407
|
history: Event[];
|
|
412
408
|
agentMeta?: {
|
|
413
409
|
name?: string;
|
|
@@ -420,9 +416,9 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
420
416
|
signal?: AbortSignal;
|
|
421
417
|
}): Promise<{
|
|
422
418
|
selectedRoute?: Route<TContext>;
|
|
423
|
-
|
|
419
|
+
selectedStep?: Step<TContext>;
|
|
424
420
|
responseDirectives?: string[];
|
|
425
|
-
session:
|
|
421
|
+
session: SessionStep;
|
|
426
422
|
isRouteComplete?: boolean;
|
|
427
423
|
}> {
|
|
428
424
|
const { routes, session, history, agentMeta, ai, context, signal } = params;
|
|
@@ -431,9 +427,9 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
431
427
|
return { session };
|
|
432
428
|
}
|
|
433
429
|
|
|
434
|
-
// OPTIMIZATION: Single route - skip route scoring, only do
|
|
430
|
+
// OPTIMIZATION: Single route - skip route scoring, only do step selection
|
|
435
431
|
if (routes.length === 1) {
|
|
436
|
-
return this.
|
|
432
|
+
return this.decideSingleRouteStep({
|
|
437
433
|
route: routes[0],
|
|
438
434
|
session,
|
|
439
435
|
history,
|
|
@@ -446,13 +442,13 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
446
442
|
|
|
447
443
|
const lastUserMessage = getLastMessageFromHistory(history);
|
|
448
444
|
|
|
449
|
-
let
|
|
445
|
+
let activeRouteSteps:
|
|
450
446
|
| Array<{
|
|
451
|
-
|
|
447
|
+
stepId: string;
|
|
452
448
|
description: string;
|
|
453
449
|
condition?: string;
|
|
454
|
-
|
|
455
|
-
|
|
450
|
+
requires?: string[];
|
|
451
|
+
collectFields?: string[];
|
|
456
452
|
}>
|
|
457
453
|
| undefined;
|
|
458
454
|
let activeRoute: Route<TContext> | undefined;
|
|
@@ -461,13 +457,13 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
461
457
|
if (session.currentRoute) {
|
|
462
458
|
activeRoute = routes.find((r) => r.id === session.currentRoute?.id);
|
|
463
459
|
if (activeRoute) {
|
|
464
|
-
const
|
|
465
|
-
? activeRoute.
|
|
460
|
+
const currentStep = session.currentStep
|
|
461
|
+
? activeRoute.getStep(session.currentStep.id)
|
|
466
462
|
: undefined;
|
|
467
|
-
const candidates = this.
|
|
463
|
+
const candidates = this.getCandidateSteps(
|
|
468
464
|
activeRoute,
|
|
469
|
-
|
|
470
|
-
session.
|
|
465
|
+
currentStep,
|
|
466
|
+
session.data || {}
|
|
471
467
|
);
|
|
472
468
|
|
|
473
469
|
// Check if route is complete
|
|
@@ -476,18 +472,18 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
476
472
|
logger.debug(
|
|
477
473
|
`[RoutingEngine] Route ${activeRoute.title} is complete - all data collected`
|
|
478
474
|
);
|
|
479
|
-
// Don't include
|
|
480
|
-
|
|
475
|
+
// Don't include steps in routing if route is complete
|
|
476
|
+
activeRouteSteps = undefined;
|
|
481
477
|
} else {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
description: c.
|
|
478
|
+
activeRouteSteps = candidates.map((c) => ({
|
|
479
|
+
stepId: c.step.id,
|
|
480
|
+
description: c.step.description || "",
|
|
485
481
|
condition: c.condition,
|
|
486
|
-
|
|
487
|
-
|
|
482
|
+
requires: c.requires,
|
|
483
|
+
collectFields: c.collectFields,
|
|
488
484
|
}));
|
|
489
485
|
logger.debug(
|
|
490
|
-
`[RoutingEngine] Found ${
|
|
486
|
+
`[RoutingEngine] Found ${activeRouteSteps.length} candidate steps for active route`
|
|
491
487
|
);
|
|
492
488
|
}
|
|
493
489
|
}
|
|
@@ -496,7 +492,7 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
496
492
|
const routingSchema = this.buildDynamicRoutingSchema(
|
|
497
493
|
routes,
|
|
498
494
|
undefined,
|
|
499
|
-
|
|
495
|
+
activeRouteSteps
|
|
500
496
|
);
|
|
501
497
|
|
|
502
498
|
const routingPrompt = this.buildRoutingPrompt(
|
|
@@ -505,7 +501,7 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
505
501
|
lastUserMessage,
|
|
506
502
|
agentMeta,
|
|
507
503
|
session,
|
|
508
|
-
|
|
504
|
+
activeRouteSteps
|
|
509
505
|
);
|
|
510
506
|
|
|
511
507
|
const routingResult = await ai.generateMessage<
|
|
@@ -523,7 +519,7 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
523
519
|
});
|
|
524
520
|
|
|
525
521
|
let selectedRoute: Route<TContext> | undefined;
|
|
526
|
-
let
|
|
522
|
+
let selectedStep: Step<TContext> | undefined;
|
|
527
523
|
let responseDirectives: string[] | undefined;
|
|
528
524
|
let updatedSession = session;
|
|
529
525
|
|
|
@@ -538,18 +534,18 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
538
534
|
|
|
539
535
|
if (
|
|
540
536
|
selectedRoute === activeRoute &&
|
|
541
|
-
routingResult.structured.
|
|
537
|
+
routingResult.structured.selectedStepId &&
|
|
542
538
|
activeRoute
|
|
543
539
|
) {
|
|
544
|
-
|
|
545
|
-
routingResult.structured.
|
|
540
|
+
selectedStep = activeRoute.getStep(
|
|
541
|
+
routingResult.structured.selectedStepId
|
|
546
542
|
);
|
|
547
|
-
if (
|
|
543
|
+
if (selectedStep) {
|
|
548
544
|
logger.debug(
|
|
549
|
-
`[RoutingEngine] AI selected
|
|
545
|
+
`[RoutingEngine] AI selected step: ${selectedStep.id} in active route`
|
|
550
546
|
);
|
|
551
547
|
logger.debug(
|
|
552
|
-
`[RoutingEngine]
|
|
548
|
+
`[RoutingEngine] Step reasoning: ${routingResult.structured.stepReasoning}`
|
|
553
549
|
);
|
|
554
550
|
}
|
|
555
551
|
}
|
|
@@ -566,7 +562,7 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
566
562
|
selectedRoute.title
|
|
567
563
|
);
|
|
568
564
|
if (selectedRoute.initialData) {
|
|
569
|
-
updatedSession =
|
|
565
|
+
updatedSession = mergeCollected(
|
|
570
566
|
updatedSession,
|
|
571
567
|
selectedRoute.initialData
|
|
572
568
|
);
|
|
@@ -582,7 +578,7 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
582
578
|
|
|
583
579
|
return {
|
|
584
580
|
selectedRoute,
|
|
585
|
-
|
|
581
|
+
selectedStep,
|
|
586
582
|
responseDirectives,
|
|
587
583
|
session: updatedSession,
|
|
588
584
|
isRouteComplete,
|
|
@@ -590,19 +586,19 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
590
586
|
}
|
|
591
587
|
|
|
592
588
|
/**
|
|
593
|
-
* Build prompt for
|
|
589
|
+
* Build prompt for step selection within a single route
|
|
594
590
|
* @private
|
|
595
591
|
*/
|
|
596
|
-
private
|
|
592
|
+
private buildStepSelectionPrompt(
|
|
597
593
|
route: Route<TContext>,
|
|
598
|
-
|
|
594
|
+
currentStep: Step<TContext> | undefined,
|
|
599
595
|
candidates: Array<{
|
|
600
|
-
|
|
596
|
+
step: Step<TContext>;
|
|
601
597
|
condition?: string;
|
|
602
|
-
|
|
603
|
-
|
|
598
|
+
requires?: string[];
|
|
599
|
+
collectFields?: string[];
|
|
604
600
|
}>,
|
|
605
|
-
|
|
601
|
+
data: Partial<unknown>,
|
|
606
602
|
history: Event[],
|
|
607
603
|
lastMessage: string,
|
|
608
604
|
agentMeta?: {
|
|
@@ -632,71 +628,71 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
632
628
|
`Active Route: ${route.title}\nDescription: ${route.description || "N/A"}`
|
|
633
629
|
);
|
|
634
630
|
|
|
635
|
-
// Add current
|
|
636
|
-
if (
|
|
631
|
+
// Add current step context
|
|
632
|
+
if (currentStep) {
|
|
637
633
|
pc.addInstruction(
|
|
638
|
-
`Current
|
|
639
|
-
|
|
634
|
+
`Current Step: ${currentStep.id}\nDescription: ${
|
|
635
|
+
currentStep.description || "N/A"
|
|
640
636
|
}`
|
|
641
637
|
);
|
|
642
638
|
} else {
|
|
643
|
-
pc.addInstruction("Current
|
|
639
|
+
pc.addInstruction("Current Step: None (entering route)");
|
|
644
640
|
}
|
|
645
641
|
|
|
646
|
-
// Add
|
|
647
|
-
if (Object.keys(
|
|
642
|
+
// Add collected data context
|
|
643
|
+
if (Object.keys(data).length > 0) {
|
|
648
644
|
pc.addInstruction(
|
|
649
|
-
`
|
|
645
|
+
`Collected Data So Far:\n${JSON.stringify(data, null, 2)}`
|
|
650
646
|
);
|
|
651
647
|
} else {
|
|
652
|
-
pc.addInstruction("
|
|
648
|
+
pc.addInstruction("Collected Data: None yet");
|
|
653
649
|
}
|
|
654
650
|
|
|
655
651
|
// Add conversation history
|
|
656
652
|
pc.addInteractionHistory(history);
|
|
657
653
|
pc.addLastMessage(lastMessage);
|
|
658
654
|
|
|
659
|
-
// Add candidate
|
|
660
|
-
const
|
|
655
|
+
// Add candidate steps
|
|
656
|
+
const stepDescriptions = candidates.map((candidate, idx) => {
|
|
661
657
|
const parts = [
|
|
662
|
-
`${idx + 1}.
|
|
663
|
-
` Description: ${candidate.
|
|
658
|
+
`${idx + 1}. Step ID: ${candidate.step.id}`,
|
|
659
|
+
` Description: ${candidate.step.description || "N/A"}`,
|
|
664
660
|
];
|
|
665
661
|
|
|
666
662
|
if (candidate.condition) {
|
|
667
663
|
parts.push(` Condition: ${candidate.condition}`);
|
|
668
664
|
}
|
|
669
665
|
|
|
670
|
-
if (candidate.
|
|
671
|
-
parts.push(` Required Data: ${candidate.
|
|
666
|
+
if (candidate.requires && candidate.requires.length > 0) {
|
|
667
|
+
parts.push(` Required Data: ${candidate.requires.join(", ")}`);
|
|
672
668
|
}
|
|
673
669
|
|
|
674
|
-
if (candidate.
|
|
675
|
-
parts.push(`
|
|
670
|
+
if (candidate.collectFields && candidate.collectFields.length > 0) {
|
|
671
|
+
parts.push(` Collects: ${candidate.collectFields.join(", ")}`);
|
|
676
672
|
}
|
|
677
673
|
|
|
678
674
|
return parts.join("\n");
|
|
679
675
|
});
|
|
680
676
|
|
|
681
677
|
pc.addInstruction(
|
|
682
|
-
`Available
|
|
678
|
+
`Available Steps to Transition To:\n${stepDescriptions.join("\n\n")}`
|
|
683
679
|
);
|
|
684
680
|
|
|
685
681
|
// Add decision instructions
|
|
686
682
|
pc.addInstruction(
|
|
687
683
|
[
|
|
688
|
-
"Task: Decide which
|
|
684
|
+
"Task: Decide which step to transition to based on:",
|
|
689
685
|
"1. The user's current message and intent",
|
|
690
686
|
"2. The conversation history and context",
|
|
691
|
-
"3. The
|
|
692
|
-
"4. The conditions and requirements of each
|
|
687
|
+
"3. The collected data we already have",
|
|
688
|
+
"4. The conditions and requirements of each step",
|
|
693
689
|
"5. The logical flow of the conversation",
|
|
694
690
|
"",
|
|
695
691
|
"Rules:",
|
|
696
|
-
"- If a
|
|
697
|
-
"- If a
|
|
698
|
-
"- Choose the
|
|
699
|
-
"-
|
|
692
|
+
"- If a step has a condition, evaluate whether it's met based on context",
|
|
693
|
+
"- If a step requires data we don't have, consider if we should collect it now",
|
|
694
|
+
"- Choose the step that makes the most sense for moving the conversation forward",
|
|
695
|
+
"- Steps with skipIf conditions that are met have already been filtered out",
|
|
700
696
|
"",
|
|
701
697
|
"Return ONLY JSON matching the provided schema.",
|
|
702
698
|
].join("\n")
|
|
@@ -706,25 +702,25 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
706
702
|
}
|
|
707
703
|
|
|
708
704
|
/**
|
|
709
|
-
* Build schema for
|
|
705
|
+
* Build schema for step selection
|
|
710
706
|
* @private
|
|
711
707
|
*/
|
|
712
|
-
private
|
|
708
|
+
private buildStepSelectionSchema(validStepIds: string[]): StructuredSchema {
|
|
713
709
|
return {
|
|
714
710
|
description:
|
|
715
|
-
"
|
|
711
|
+
"Step transition decision based on conversation context and collected data",
|
|
716
712
|
type: "object",
|
|
717
713
|
properties: {
|
|
718
714
|
reasoning: {
|
|
719
715
|
type: "string",
|
|
720
716
|
nullable: false,
|
|
721
|
-
description: "Brief explanation of why this
|
|
717
|
+
description: "Brief explanation of why this step was selected",
|
|
722
718
|
},
|
|
723
|
-
|
|
719
|
+
selectedStepId: {
|
|
724
720
|
type: "string",
|
|
725
721
|
nullable: false,
|
|
726
|
-
description: "The ID of the selected
|
|
727
|
-
enum:
|
|
722
|
+
description: "The ID of the selected step to transition to",
|
|
723
|
+
enum: validStepIds,
|
|
728
724
|
},
|
|
729
725
|
responseDirectives: {
|
|
730
726
|
type: "array",
|
|
@@ -733,7 +729,7 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
733
729
|
"Optional bullet points the response should address (concise)",
|
|
734
730
|
},
|
|
735
731
|
},
|
|
736
|
-
required: ["reasoning", "
|
|
732
|
+
required: ["reasoning", "selectedStepId"],
|
|
737
733
|
additionalProperties: false,
|
|
738
734
|
};
|
|
739
735
|
}
|
|
@@ -741,7 +737,7 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
741
737
|
buildDynamicRoutingSchema(
|
|
742
738
|
routes: Route<TContext>[],
|
|
743
739
|
extrasSchema?: StructuredSchema,
|
|
744
|
-
|
|
740
|
+
activeRouteSteps?: { stepId: string; description: string }[]
|
|
745
741
|
): StructuredSchema {
|
|
746
742
|
const routeIds = routes.map((r) => r.id);
|
|
747
743
|
const routeProperties: Record<string, StructuredSchema> = {};
|
|
@@ -783,25 +779,25 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
783
779
|
additionalProperties: false,
|
|
784
780
|
};
|
|
785
781
|
|
|
786
|
-
// Add
|
|
787
|
-
if (
|
|
782
|
+
// Add step selection fields if there's an active route with steps
|
|
783
|
+
if (activeRouteSteps && activeRouteSteps.length > 0) {
|
|
788
784
|
base.properties = base.properties || {};
|
|
789
|
-
base.properties.
|
|
785
|
+
base.properties.selectedStepId = {
|
|
790
786
|
type: "string",
|
|
791
787
|
nullable: false,
|
|
792
788
|
description:
|
|
793
|
-
"The
|
|
794
|
-
enum:
|
|
789
|
+
"The step ID to transition to within the active route (required if continuing in current route)",
|
|
790
|
+
enum: activeRouteSteps.map((s) => s.stepId),
|
|
795
791
|
};
|
|
796
|
-
base.properties.
|
|
792
|
+
base.properties.stepReasoning = {
|
|
797
793
|
type: "string",
|
|
798
794
|
nullable: false,
|
|
799
|
-
description: "Brief explanation of why this
|
|
795
|
+
description: "Brief explanation of why this step was selected",
|
|
800
796
|
};
|
|
801
797
|
base.required = [
|
|
802
798
|
...(base.required || []),
|
|
803
|
-
"
|
|
804
|
-
"
|
|
799
|
+
"selectedStepId",
|
|
800
|
+
"stepReasoning",
|
|
805
801
|
];
|
|
806
802
|
}
|
|
807
803
|
|
|
@@ -823,13 +819,13 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
823
819
|
description?: string;
|
|
824
820
|
personality?: string;
|
|
825
821
|
},
|
|
826
|
-
session?:
|
|
827
|
-
|
|
828
|
-
|
|
822
|
+
session?: SessionStep,
|
|
823
|
+
activeRouteSteps?: Array<{
|
|
824
|
+
stepId: string;
|
|
829
825
|
description: string;
|
|
830
826
|
condition?: string;
|
|
831
|
-
|
|
832
|
-
|
|
827
|
+
requires?: string[];
|
|
828
|
+
collectFields?: string[];
|
|
833
829
|
}>
|
|
834
830
|
): string {
|
|
835
831
|
const pc = new PromptComposer();
|
|
@@ -853,54 +849,50 @@ export class RoutingEngine<TContext = unknown> {
|
|
|
853
849
|
"Current conversation context:",
|
|
854
850
|
`- Active route: ${session.currentRoute.title} (${session.currentRoute.id})`,
|
|
855
851
|
];
|
|
856
|
-
if (session.
|
|
857
|
-
sessionInfo.push(`- Current
|
|
858
|
-
if (session.
|
|
859
|
-
sessionInfo.push(` "${session.
|
|
852
|
+
if (session.currentStep) {
|
|
853
|
+
sessionInfo.push(`- Current step: ${session.currentStep.id}`);
|
|
854
|
+
if (session.currentStep.description) {
|
|
855
|
+
sessionInfo.push(` "${session.currentStep.description}"`);
|
|
860
856
|
}
|
|
861
857
|
}
|
|
862
|
-
if (session.
|
|
863
|
-
sessionInfo.push(
|
|
864
|
-
`- Extracted data: ${JSON.stringify(session.extracted)}`
|
|
865
|
-
);
|
|
858
|
+
if (session.data && Object.keys(session.data).length > 0) {
|
|
859
|
+
sessionInfo.push(`- Collected data: ${JSON.stringify(session.data)}`);
|
|
866
860
|
}
|
|
867
861
|
sessionInfo.push(
|
|
868
862
|
"Note: User is mid-conversation. They may want to continue current route or switch to a new one based on their intent."
|
|
869
863
|
);
|
|
870
864
|
pc.addInstruction(sessionInfo.join("\n"));
|
|
871
865
|
|
|
872
|
-
// Add available
|
|
873
|
-
if (
|
|
874
|
-
const
|
|
866
|
+
// Add available steps for the active route
|
|
867
|
+
if (activeRouteSteps && activeRouteSteps.length > 0) {
|
|
868
|
+
const stepInfo = [
|
|
875
869
|
"",
|
|
876
|
-
"Available
|
|
870
|
+
"Available steps in active route (choose one to transition to):",
|
|
877
871
|
];
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
if (
|
|
881
|
-
|
|
872
|
+
activeRouteSteps.forEach((step, idx) => {
|
|
873
|
+
stepInfo.push(`${idx + 1}. Step: ${step.stepId}`);
|
|
874
|
+
if (step.description) {
|
|
875
|
+
stepInfo.push(` Description: ${step.description}`);
|
|
882
876
|
}
|
|
883
|
-
if (
|
|
884
|
-
|
|
877
|
+
if (step.condition) {
|
|
878
|
+
stepInfo.push(` Condition: ${step.condition}`);
|
|
885
879
|
}
|
|
886
|
-
if (
|
|
887
|
-
|
|
888
|
-
` Required data: ${state.requiredData.join(", ")}`
|
|
889
|
-
);
|
|
880
|
+
if (step.requires && step.requires.length > 0) {
|
|
881
|
+
stepInfo.push(` Required data: ${step.requires.join(", ")}`);
|
|
890
882
|
}
|
|
891
|
-
if (
|
|
892
|
-
|
|
883
|
+
if (step.collectFields && step.collectFields.length > 0) {
|
|
884
|
+
stepInfo.push(` Will collect: ${step.collectFields.join(", ")}`);
|
|
893
885
|
}
|
|
894
886
|
});
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
"IMPORTANT: You MUST select a
|
|
887
|
+
stepInfo.push("");
|
|
888
|
+
stepInfo.push(
|
|
889
|
+
"IMPORTANT: You MUST select a step to transition to. Evaluate which step makes the most sense based on:"
|
|
898
890
|
);
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
pc.addInstruction(
|
|
891
|
+
stepInfo.push("- The conversation flow and what's been collected");
|
|
892
|
+
stepInfo.push("- What data is still needed vs already present");
|
|
893
|
+
stepInfo.push("- The logical next step in the conversation");
|
|
894
|
+
stepInfo.push("- Whether conditions for steps are met");
|
|
895
|
+
pc.addInstruction(stepInfo.join("\n"));
|
|
904
896
|
}
|
|
905
897
|
}
|
|
906
898
|
|