@falai/agent 0.4.1 → 0.5.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 +21 -74
- package/dist/cjs/core/Agent.d.ts +22 -29
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +464 -291
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/Events.d.ts +10 -1
- package/dist/cjs/core/Events.d.ts.map +1 -1
- package/dist/cjs/core/Events.js +3 -2
- package/dist/cjs/core/Events.js.map +1 -1
- package/dist/cjs/core/PersistenceManager.d.ts +19 -0
- package/dist/cjs/core/PersistenceManager.d.ts.map +1 -1
- package/dist/cjs/core/PersistenceManager.js +57 -0
- package/dist/cjs/core/PersistenceManager.js.map +1 -1
- package/dist/cjs/core/PromptComposer.d.ts +24 -0
- package/dist/cjs/core/PromptComposer.d.ts.map +1 -0
- package/dist/cjs/core/PromptComposer.js +127 -0
- package/dist/cjs/core/PromptComposer.js.map +1 -0
- package/dist/cjs/core/ResponseEngine.d.ts +19 -0
- package/dist/cjs/core/ResponseEngine.d.ts.map +1 -0
- package/dist/cjs/core/ResponseEngine.js +51 -0
- package/dist/cjs/core/ResponseEngine.js.map +1 -0
- package/dist/cjs/core/Route.d.ts +18 -12
- package/dist/cjs/core/Route.d.ts.map +1 -1
- package/dist/cjs/core/Route.js +15 -9
- package/dist/cjs/core/Route.js.map +1 -1
- package/dist/cjs/core/RoutingEngine.d.ts +38 -0
- package/dist/cjs/core/RoutingEngine.d.ts.map +1 -0
- package/dist/cjs/core/RoutingEngine.js +110 -0
- package/dist/cjs/core/RoutingEngine.js.map +1 -0
- package/dist/cjs/core/State.d.ts +15 -4
- package/dist/cjs/core/State.d.ts.map +1 -1
- package/dist/cjs/core/State.js +21 -2
- package/dist/cjs/core/State.js.map +1 -1
- package/dist/cjs/core/ToolExecutor.d.ts +29 -0
- package/dist/cjs/core/ToolExecutor.d.ts.map +1 -0
- package/dist/cjs/core/ToolExecutor.js +73 -0
- package/dist/cjs/core/ToolExecutor.js.map +1 -0
- package/dist/cjs/core/Transition.d.ts +5 -5
- package/dist/cjs/core/Transition.d.ts.map +1 -1
- package/dist/cjs/core/Transition.js.map +1 -1
- package/dist/cjs/index.d.ts +6 -8
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +8 -10
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.js +10 -13
- package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +12 -8
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.js +10 -53
- package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.js +10 -53
- package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +13 -12
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/ai.d.ts +8 -2
- package/dist/cjs/types/ai.d.ts.map +1 -1
- package/dist/cjs/types/history.d.ts +8 -0
- package/dist/cjs/types/history.d.ts.map +1 -1
- package/dist/cjs/types/index.d.ts +0 -3
- package/dist/cjs/types/index.d.ts.map +1 -1
- package/dist/cjs/types/index.js +1 -3
- package/dist/cjs/types/index.js.map +1 -1
- package/dist/cjs/types/route.d.ts +39 -4
- package/dist/cjs/types/route.d.ts.map +1 -1
- package/dist/cjs/types/routing.d.ts +16 -0
- package/dist/cjs/types/routing.d.ts.map +1 -0
- package/dist/cjs/types/routing.js +3 -0
- package/dist/cjs/types/routing.js.map +1 -0
- package/dist/cjs/types/schema.d.ts +22 -0
- package/dist/cjs/types/schema.d.ts.map +1 -0
- package/dist/cjs/types/schema.js +3 -0
- package/dist/cjs/types/schema.js.map +1 -0
- package/dist/cjs/types/session.d.ts +72 -0
- package/dist/cjs/types/session.d.ts.map +1 -0
- package/dist/cjs/types/session.js +140 -0
- package/dist/cjs/types/session.js.map +1 -0
- package/dist/cjs/types/tool.d.ts +11 -5
- package/dist/cjs/types/tool.d.ts.map +1 -1
- package/dist/cjs/utils/id.d.ts +0 -5
- package/dist/cjs/utils/id.d.ts.map +1 -1
- package/dist/cjs/utils/id.js +0 -10
- package/dist/cjs/utils/id.js.map +1 -1
- package/dist/cjs/utils/schema.d.ts +17 -0
- package/dist/cjs/utils/schema.d.ts.map +1 -0
- package/dist/cjs/utils/schema.js +32 -0
- package/dist/cjs/utils/schema.js.map +1 -0
- package/dist/core/Agent.d.ts +22 -29
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +464 -291
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/Events.d.ts +10 -1
- package/dist/core/Events.d.ts.map +1 -1
- package/dist/core/Events.js +3 -2
- package/dist/core/Events.js.map +1 -1
- package/dist/core/PersistenceManager.d.ts +19 -0
- package/dist/core/PersistenceManager.d.ts.map +1 -1
- package/dist/core/PersistenceManager.js +57 -0
- package/dist/core/PersistenceManager.js.map +1 -1
- package/dist/core/PromptComposer.d.ts +24 -0
- package/dist/core/PromptComposer.d.ts.map +1 -0
- package/dist/core/PromptComposer.js +123 -0
- package/dist/core/PromptComposer.js.map +1 -0
- package/dist/core/ResponseEngine.d.ts +19 -0
- package/dist/core/ResponseEngine.d.ts.map +1 -0
- package/dist/core/ResponseEngine.js +47 -0
- package/dist/core/ResponseEngine.js.map +1 -0
- package/dist/core/Route.d.ts +18 -12
- package/dist/core/Route.d.ts.map +1 -1
- package/dist/core/Route.js +15 -9
- package/dist/core/Route.js.map +1 -1
- package/dist/core/RoutingEngine.d.ts +38 -0
- package/dist/core/RoutingEngine.d.ts.map +1 -0
- package/dist/core/RoutingEngine.js +106 -0
- package/dist/core/RoutingEngine.js.map +1 -0
- package/dist/core/State.d.ts +15 -4
- package/dist/core/State.d.ts.map +1 -1
- package/dist/core/State.js +21 -2
- package/dist/core/State.js.map +1 -1
- package/dist/core/ToolExecutor.d.ts +29 -0
- package/dist/core/ToolExecutor.d.ts.map +1 -0
- package/dist/core/ToolExecutor.js +69 -0
- package/dist/core/ToolExecutor.js.map +1 -0
- package/dist/core/Transition.d.ts +5 -5
- package/dist/core/Transition.d.ts.map +1 -1
- package/dist/core/Transition.js.map +1 -1
- package/dist/index.d.ts +6 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -1
- package/dist/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/AnthropicProvider.js +10 -13
- package/dist/providers/AnthropicProvider.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +12 -8
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/OpenAIProvider.js +10 -53
- package/dist/providers/OpenAIProvider.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/OpenRouterProvider.js +10 -53
- package/dist/providers/OpenRouterProvider.js.map +1 -1
- package/dist/types/agent.d.ts +13 -12
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/ai.d.ts +8 -2
- package/dist/types/ai.d.ts.map +1 -1
- package/dist/types/history.d.ts +8 -0
- package/dist/types/history.d.ts.map +1 -1
- package/dist/types/index.d.ts +0 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/route.d.ts +39 -4
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/routing.d.ts +16 -0
- package/dist/types/routing.d.ts.map +1 -0
- package/dist/types/routing.js +2 -0
- package/dist/types/routing.js.map +1 -0
- package/dist/types/schema.d.ts +22 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +2 -0
- package/dist/types/schema.js.map +1 -0
- package/dist/types/session.d.ts +72 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +132 -0
- package/dist/types/session.js.map +1 -0
- package/dist/types/tool.d.ts +11 -5
- package/dist/types/tool.d.ts.map +1 -1
- package/dist/utils/id.d.ts +0 -5
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +0 -9
- package/dist/utils/id.js.map +1 -1
- package/dist/utils/schema.d.ts +17 -0
- package/dist/utils/schema.d.ts.map +1 -0
- package/dist/utils/schema.js +27 -0
- package/dist/utils/schema.js.map +1 -0
- package/docs/ADAPTERS.md +83 -3
- package/docs/API_REFERENCE.md +95 -104
- package/docs/ARCHITECTURE.md +284 -286
- package/docs/CONSTRUCTOR_OPTIONS.md +192 -135
- package/docs/CONTEXT_MANAGEMENT.md +311 -28
- package/docs/CONTRIBUTING.md +1 -1
- package/docs/DOMAINS.md +61 -0
- package/docs/GETTING_STARTED.md +177 -88
- package/docs/PERSISTENCE.md +170 -23
- package/docs/README.md +7 -10
- package/examples/business-onboarding.ts +21 -9
- package/examples/company-qna-agent.ts +508 -0
- package/examples/declarative-agent.ts +143 -26
- package/examples/domain-scoping.ts +31 -10
- package/examples/extracted-data-modification.ts +415 -0
- package/examples/healthcare-agent.ts +194 -90
- package/examples/openai-agent.ts +67 -25
- package/examples/opensearch-persistence.ts +455 -151
- package/examples/persistent-onboarding.ts +162 -96
- package/examples/prisma-persistence.ts +371 -125
- package/examples/redis-persistence.ts +393 -23
- package/examples/rules-prohibitions.ts +32 -11
- package/examples/streaming-agent.ts +61 -13
- package/examples/travel-agent.ts +266 -133
- package/package.json +1 -1
- package/src/core/Agent.ts +674 -356
- package/src/core/Events.ts +12 -2
- package/src/core/PersistenceManager.ts +83 -0
- package/src/core/PromptComposer.ts +143 -0
- package/src/core/ResponseEngine.ts +82 -0
- package/src/core/Route.ts +32 -17
- package/src/core/RoutingEngine.ts +165 -0
- package/src/core/State.ts +55 -15
- package/src/core/ToolExecutor.ts +117 -0
- package/src/core/Transition.ts +5 -5
- package/src/index.ts +12 -21
- package/src/providers/AnthropicProvider.ts +10 -13
- package/src/providers/GeminiProvider.ts +12 -8
- package/src/providers/OpenAIProvider.ts +10 -56
- package/src/providers/OpenRouterProvider.ts +10 -56
- package/src/types/agent.ts +16 -13
- package/src/types/ai.ts +6 -2
- package/src/types/history.ts +8 -0
- package/src/types/index.ts +0 -11
- package/src/types/route.ts +41 -5
- package/src/types/routing.ts +18 -0
- package/src/types/schema.ts +23 -0
- package/src/types/session.ts +207 -0
- package/src/types/tool.ts +29 -7
- package/src/utils/id.ts +0 -10
- package/src/utils/schema.ts +32 -0
- package/dist/cjs/core/ConditionEvaluator.d.ts +0 -72
- package/dist/cjs/core/ConditionEvaluator.d.ts.map +0 -1
- package/dist/cjs/core/ConditionEvaluator.js +0 -272
- package/dist/cjs/core/ConditionEvaluator.js.map +0 -1
- package/dist/cjs/core/Observation.d.ts +0 -24
- package/dist/cjs/core/Observation.d.ts.map +0 -1
- package/dist/cjs/core/Observation.js +0 -39
- package/dist/cjs/core/Observation.js.map +0 -1
- package/dist/cjs/core/PreparationEngine.d.ts +0 -116
- package/dist/cjs/core/PreparationEngine.d.ts.map +0 -1
- package/dist/cjs/core/PreparationEngine.js +0 -353
- package/dist/cjs/core/PreparationEngine.js.map +0 -1
- package/dist/cjs/core/PromptBuilder.d.ts +0 -136
- package/dist/cjs/core/PromptBuilder.d.ts.map +0 -1
- package/dist/cjs/core/PromptBuilder.js +0 -421
- package/dist/cjs/core/PromptBuilder.js.map +0 -1
- package/dist/cjs/types/observation.d.ts +0 -27
- package/dist/cjs/types/observation.d.ts.map +0 -1
- package/dist/cjs/types/observation.js +0 -6
- package/dist/cjs/types/observation.js.map +0 -1
- package/dist/cjs/types/prompt.d.ts +0 -46
- package/dist/cjs/types/prompt.d.ts.map +0 -1
- package/dist/cjs/types/prompt.js +0 -19
- package/dist/cjs/types/prompt.js.map +0 -1
- package/dist/core/ConditionEvaluator.d.ts +0 -72
- package/dist/core/ConditionEvaluator.d.ts.map +0 -1
- package/dist/core/ConditionEvaluator.js +0 -268
- package/dist/core/ConditionEvaluator.js.map +0 -1
- package/dist/core/Observation.d.ts +0 -24
- package/dist/core/Observation.d.ts.map +0 -1
- package/dist/core/Observation.js +0 -35
- package/dist/core/Observation.js.map +0 -1
- package/dist/core/PreparationEngine.d.ts +0 -116
- package/dist/core/PreparationEngine.d.ts.map +0 -1
- package/dist/core/PreparationEngine.js +0 -349
- package/dist/core/PreparationEngine.js.map +0 -1
- package/dist/core/PromptBuilder.d.ts +0 -136
- package/dist/core/PromptBuilder.d.ts.map +0 -1
- package/dist/core/PromptBuilder.js +0 -417
- package/dist/core/PromptBuilder.js.map +0 -1
- package/dist/types/observation.d.ts +0 -27
- package/dist/types/observation.d.ts.map +0 -1
- package/dist/types/observation.js +0 -5
- package/dist/types/observation.js.map +0 -1
- package/dist/types/prompt.d.ts +0 -46
- package/dist/types/prompt.d.ts.map +0 -1
- package/dist/types/prompt.js +0 -16
- package/dist/types/prompt.js.map +0 -1
- package/docs/STRUCTURE.md +0 -58
- package/src/core/ConditionEvaluator.ts +0 -381
- package/src/core/Observation.ts +0 -47
- package/src/core/PreparationEngine.ts +0 -561
- package/src/core/PromptBuilder.ts +0 -617
- package/src/types/observation.ts +0 -29
- package/src/types/prompt.ts +0 -49
package/src/core/Events.ts
CHANGED
|
@@ -71,7 +71,16 @@ export function createMessageEvent(
|
|
|
71
71
|
source: EventSource,
|
|
72
72
|
participantName: string,
|
|
73
73
|
message: string,
|
|
74
|
-
|
|
74
|
+
options?: {
|
|
75
|
+
timestamp?: string;
|
|
76
|
+
session?: {
|
|
77
|
+
routeId?: string;
|
|
78
|
+
routeTitle?: string;
|
|
79
|
+
stateId?: string;
|
|
80
|
+
stateDescription?: string;
|
|
81
|
+
extracted?: Record<string, unknown>;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
75
84
|
): Event<MessageEventData> {
|
|
76
85
|
return {
|
|
77
86
|
kind: EventKind.MESSAGE,
|
|
@@ -79,8 +88,9 @@ export function createMessageEvent(
|
|
|
79
88
|
data: {
|
|
80
89
|
participant: { display_name: participantName },
|
|
81
90
|
message,
|
|
91
|
+
session: options?.session,
|
|
82
92
|
},
|
|
83
|
-
timestamp: timestamp || new Date().toISOString(),
|
|
93
|
+
timestamp: options?.timestamp || new Date().toISOString(),
|
|
84
94
|
};
|
|
85
95
|
}
|
|
86
96
|
|
|
@@ -14,6 +14,12 @@ import type {
|
|
|
14
14
|
MessageRepository,
|
|
15
15
|
} from "../types/persistence";
|
|
16
16
|
import type { Event } from "../types/history";
|
|
17
|
+
import type { SessionState } from "../types/session";
|
|
18
|
+
import {
|
|
19
|
+
createSession,
|
|
20
|
+
sessionStateToData,
|
|
21
|
+
sessionDataToState,
|
|
22
|
+
} from "../types/session";
|
|
17
23
|
|
|
18
24
|
/**
|
|
19
25
|
* Manager for handling persistence operations
|
|
@@ -219,4 +225,81 @@ export class PersistenceManager {
|
|
|
219
225
|
.map((m) => this.messageToEvent(m))
|
|
220
226
|
.filter((e): e is Event => e !== undefined);
|
|
221
227
|
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Save SessionState to database
|
|
231
|
+
* Converts SessionState to SessionData and persists it
|
|
232
|
+
*/
|
|
233
|
+
async saveSessionState<TExtracted = Record<string, unknown>>(
|
|
234
|
+
sessionId: string,
|
|
235
|
+
sessionState: SessionState<TExtracted>
|
|
236
|
+
): Promise<SessionData | null> {
|
|
237
|
+
const persistenceData = sessionStateToData(sessionState);
|
|
238
|
+
|
|
239
|
+
return await this.sessionRepository.update(sessionId, {
|
|
240
|
+
currentRoute: persistenceData.currentRoute,
|
|
241
|
+
currentState: persistenceData.currentState,
|
|
242
|
+
collectedData: persistenceData.collectedData,
|
|
243
|
+
lastMessageAt: new Date(),
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Load SessionState from database
|
|
249
|
+
* Converts SessionData to SessionState
|
|
250
|
+
*/
|
|
251
|
+
async loadSessionState<TExtracted = Record<string, unknown>>(
|
|
252
|
+
sessionId: string
|
|
253
|
+
): Promise<SessionState<TExtracted> | null> {
|
|
254
|
+
const sessionData = await this.sessionRepository.findById(sessionId);
|
|
255
|
+
|
|
256
|
+
if (!sessionData) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const stateData = sessionDataToState<TExtracted>({
|
|
261
|
+
currentRoute: sessionData.currentRoute,
|
|
262
|
+
currentState: sessionData.currentState,
|
|
263
|
+
collectedData: sessionData.collectedData,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// Create a full session state with the loaded data
|
|
267
|
+
const session = createSession<TExtracted>({
|
|
268
|
+
sessionId,
|
|
269
|
+
createdAt: sessionData.createdAt,
|
|
270
|
+
lastUpdatedAt: sessionData.updatedAt,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
...session,
|
|
275
|
+
...stateData,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Create session with SessionState support
|
|
281
|
+
* Returns both SessionData and initialized SessionState
|
|
282
|
+
*/
|
|
283
|
+
async createSessionWithState<TExtracted = Record<string, unknown>>(
|
|
284
|
+
options: CreateSessionOptions
|
|
285
|
+
): Promise<{
|
|
286
|
+
sessionData: SessionData;
|
|
287
|
+
sessionState: SessionState<TExtracted>;
|
|
288
|
+
}> {
|
|
289
|
+
const sessionData = await this.createSession(options);
|
|
290
|
+
|
|
291
|
+
// Create SessionState with database session ID
|
|
292
|
+
const sessionState = createSession<TExtracted>({
|
|
293
|
+
sessionId: sessionData.id,
|
|
294
|
+
createdAt: sessionData.createdAt,
|
|
295
|
+
lastUpdatedAt: sessionData.updatedAt,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// If initial data was provided, merge it as extracted data
|
|
299
|
+
if (options.initialData) {
|
|
300
|
+
sessionState.extracted = options.initialData as Partial<TExtracted>;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return { sessionData, sessionState };
|
|
304
|
+
}
|
|
222
305
|
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { Event } from "../types/history";
|
|
2
|
+
import type { Term, Guideline, Capability } from "../types/agent";
|
|
3
|
+
import type { Route } from "./Route";
|
|
4
|
+
|
|
5
|
+
export class PromptComposer<TContext = unknown> {
|
|
6
|
+
private parts: string[] = [];
|
|
7
|
+
|
|
8
|
+
// Specific, typed sections tailored to the framework
|
|
9
|
+
|
|
10
|
+
addAgentMeta(agent: {
|
|
11
|
+
name: string;
|
|
12
|
+
goal?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
}): this {
|
|
15
|
+
const lines: string[] = [];
|
|
16
|
+
lines.push(`Agent: ${agent.name}`);
|
|
17
|
+
if (agent.goal) lines.push(`Goal: ${agent.goal}`);
|
|
18
|
+
if (agent.description) lines.push(`Description: ${agent.description}`);
|
|
19
|
+
this.parts.push(lines.join("\n"));
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
addPersonality(personality?: string): this {
|
|
24
|
+
if (personality && personality.trim().length) {
|
|
25
|
+
this.parts.push(`Personality: ${personality.trim()}`);
|
|
26
|
+
}
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
addRoutingOverview(routes: Route<TContext>[]): this {
|
|
31
|
+
return this.addActiveRoutes(routes);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
addScoringRules(): this {
|
|
35
|
+
this.parts.push(
|
|
36
|
+
[
|
|
37
|
+
"Scoring rules:",
|
|
38
|
+
"- 90-100: explicit keywords + clear intent",
|
|
39
|
+
"- 70-89: strong contextual evidence + relevant keywords",
|
|
40
|
+
"- 50-69: moderate relevance",
|
|
41
|
+
"- 30-49: weak connection or ambiguous",
|
|
42
|
+
"- 0-29: minimal/none",
|
|
43
|
+
"Return ONLY JSON matching the provided schema. Include scores for ALL routes.",
|
|
44
|
+
].join("\n")
|
|
45
|
+
);
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
addInstruction(text: string): this {
|
|
50
|
+
if (text) this.parts.push(text);
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
addInteractionHistory(history: Event[], note?: string): this {
|
|
55
|
+
const recent = history
|
|
56
|
+
.slice(-10)
|
|
57
|
+
.map((e) => JSON.stringify(e))
|
|
58
|
+
.join("\n");
|
|
59
|
+
const header = note ? `${note}\n` : "";
|
|
60
|
+
this.parts.push(`${header}Recent conversation events:\n${recent}`);
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
addLastMessage(message: string): this {
|
|
65
|
+
this.parts.push(`Last user message:\n${message}`);
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
addGlossary(terms: Term[]): this {
|
|
70
|
+
if (!terms.length) return this;
|
|
71
|
+
const text = terms
|
|
72
|
+
.map(
|
|
73
|
+
(t, i) =>
|
|
74
|
+
`${i + 1}) ${t.name}${
|
|
75
|
+
t.synonyms?.length ? ` (synonyms: ${t.synonyms.join(", ")})` : ""
|
|
76
|
+
}: ${t.description}`
|
|
77
|
+
)
|
|
78
|
+
.join("\n");
|
|
79
|
+
this.parts.push(`Glossary:\n${text}`);
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
addGuidelines(guidelines: Guideline[]): this {
|
|
84
|
+
const enabled = guidelines.filter((g) => g.enabled !== false);
|
|
85
|
+
if (!enabled.length) return this;
|
|
86
|
+
const text = enabled
|
|
87
|
+
.map((g, i) => {
|
|
88
|
+
const cond = g.condition
|
|
89
|
+
? `When ${g.condition}, then ${g.action}`
|
|
90
|
+
: g.action;
|
|
91
|
+
return `Guideline #${i + 1}) ${cond}`;
|
|
92
|
+
})
|
|
93
|
+
.join("\n");
|
|
94
|
+
this.parts.push(`Guidelines:\n${text}`);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
addCapabilities(capabilities: Capability[]): this {
|
|
99
|
+
if (!capabilities.length) return this;
|
|
100
|
+
const text = capabilities
|
|
101
|
+
.map((c, i) => `Capability ${i + 1}: ${c.title}\n${c.description}`)
|
|
102
|
+
.join("\n\n");
|
|
103
|
+
this.parts.push(`Capabilities:\n${text}`);
|
|
104
|
+
return this;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
addActiveRoutes(routes: Route<TContext>[]): this {
|
|
108
|
+
if (!routes.length) return this;
|
|
109
|
+
const text = routes
|
|
110
|
+
.map((r, i) => {
|
|
111
|
+
const conditions = r.conditions.length
|
|
112
|
+
? `\n Triggered when: ${r.conditions.join(" OR ")}`
|
|
113
|
+
: "";
|
|
114
|
+
const desc = r.description ? `\n ${r.description}` : "";
|
|
115
|
+
const rules = r.getRules();
|
|
116
|
+
const prohibitions = r.getProhibitions();
|
|
117
|
+
const rulesInfo = rules.length
|
|
118
|
+
? `\n RULES: ${rules.map((x, idx) => `${idx + 1}. ${x}`).join("; ")}`
|
|
119
|
+
: "";
|
|
120
|
+
const prohibitionsInfo = prohibitions.length
|
|
121
|
+
? `\n PROHIBITIONS: ${prohibitions
|
|
122
|
+
.map((x, idx) => `${idx + 1}. ${x}`)
|
|
123
|
+
.join("; ")}`
|
|
124
|
+
: "";
|
|
125
|
+
return `${i + 1}) ${
|
|
126
|
+
r.title
|
|
127
|
+
}${desc}${conditions}${rulesInfo}${prohibitionsInfo}`;
|
|
128
|
+
})
|
|
129
|
+
.join("\n\n");
|
|
130
|
+
this.parts.push(`Available routes:\n${text}`);
|
|
131
|
+
return this;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
addDirectives(directives?: string[]): this {
|
|
135
|
+
if (!directives?.length) return this;
|
|
136
|
+
this.parts.push(`Address concisely:\n- ${directives.join("\n- ")}`);
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
build(): string {
|
|
141
|
+
return this.parts.filter(Boolean).join("\n\n").trim();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { Event } from "../types/history";
|
|
2
|
+
import type { Route } from "./Route";
|
|
3
|
+
import type { State } from "./State";
|
|
4
|
+
import type { StructuredSchema } from "../types/schema";
|
|
5
|
+
import { PromptComposer } from "./PromptComposer";
|
|
6
|
+
|
|
7
|
+
export interface ResponseOutput<TData = unknown> {
|
|
8
|
+
message: string;
|
|
9
|
+
data?: TData;
|
|
10
|
+
contextUpdate?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class ResponseEngine<TContext = unknown> {
|
|
14
|
+
responseSchemaForRoute<TExtracted = unknown>(
|
|
15
|
+
route: Route<TContext, TExtracted>,
|
|
16
|
+
currentState?: State<TContext, TExtracted>
|
|
17
|
+
): StructuredSchema {
|
|
18
|
+
const base: StructuredSchema = {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
message: { type: "string", description: "Final user-facing message" },
|
|
22
|
+
data: route.responseOutputSchema || { type: "object" },
|
|
23
|
+
contextUpdate: { type: "object" },
|
|
24
|
+
},
|
|
25
|
+
required: ["message"],
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Add gather fields from current state
|
|
30
|
+
if (currentState?.gatherFields && route.gatherSchema?.properties) {
|
|
31
|
+
for (const field of currentState.gatherFields) {
|
|
32
|
+
const fieldSchema = route.gatherSchema.properties[field];
|
|
33
|
+
if (fieldSchema) {
|
|
34
|
+
base.properties![field] = fieldSchema;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return base;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
buildResponsePrompt(
|
|
43
|
+
route: Route<TContext>,
|
|
44
|
+
rules: string[],
|
|
45
|
+
prohibitions: string[],
|
|
46
|
+
directives: string[] | undefined,
|
|
47
|
+
history: Event[],
|
|
48
|
+
lastMessage: string,
|
|
49
|
+
agentMeta?: {
|
|
50
|
+
name?: string;
|
|
51
|
+
goal?: string;
|
|
52
|
+
description?: string;
|
|
53
|
+
personality?: string;
|
|
54
|
+
}
|
|
55
|
+
): string {
|
|
56
|
+
const pc = new PromptComposer();
|
|
57
|
+
if (agentMeta?.name || agentMeta?.goal || agentMeta?.description)
|
|
58
|
+
pc.addAgentMeta({
|
|
59
|
+
name: agentMeta?.name || "Agent",
|
|
60
|
+
goal: agentMeta?.goal,
|
|
61
|
+
description: agentMeta?.description,
|
|
62
|
+
});
|
|
63
|
+
const personality =
|
|
64
|
+
agentMeta?.personality || "Tone: brief, natural, 1-2 short sentences.";
|
|
65
|
+
pc.addPersonality(personality);
|
|
66
|
+
pc.addInstruction(
|
|
67
|
+
`Route: ${route.title}${
|
|
68
|
+
route.description ? ` — ${route.description}` : ""
|
|
69
|
+
}`
|
|
70
|
+
);
|
|
71
|
+
if (rules.length) pc.addInstruction(`Rules:\n- ${rules.join("\n- ")}`);
|
|
72
|
+
if (prohibitions.length)
|
|
73
|
+
pc.addInstruction(`Prohibitions:\n- ${prohibitions.join("\n- ")}`);
|
|
74
|
+
pc.addDirectives(directives);
|
|
75
|
+
pc.addInteractionHistory(history);
|
|
76
|
+
pc.addLastMessage(lastMessage);
|
|
77
|
+
pc.addInstruction(
|
|
78
|
+
"Return ONLY JSON matching the schema (message + optional data/contextUpdate)."
|
|
79
|
+
);
|
|
80
|
+
return pc.build();
|
|
81
|
+
}
|
|
82
|
+
}
|
package/src/core/Route.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { RouteOptions, RouteRef } from "../types/route";
|
|
6
|
+
import type { StructuredSchema } from "../types/schema";
|
|
6
7
|
import type { Guideline } from "../types/agent";
|
|
7
8
|
|
|
8
9
|
import { State } from "./State";
|
|
@@ -11,7 +12,7 @@ import { generateRouteId } from "../utils/id";
|
|
|
11
12
|
/**
|
|
12
13
|
* Represents a conversational route/journey
|
|
13
14
|
*/
|
|
14
|
-
export class Route<TContext = unknown> {
|
|
15
|
+
export class Route<TContext = unknown, TExtracted = unknown> {
|
|
15
16
|
public readonly id: string;
|
|
16
17
|
public readonly title: string;
|
|
17
18
|
public readonly description?: string;
|
|
@@ -19,10 +20,14 @@ export class Route<TContext = unknown> {
|
|
|
19
20
|
public readonly domains?: string[];
|
|
20
21
|
public readonly rules: string[];
|
|
21
22
|
public readonly prohibitions: string[];
|
|
22
|
-
public readonly initialState: State<TContext>;
|
|
23
|
+
public readonly initialState: State<TContext, TExtracted>;
|
|
24
|
+
public readonly responseOutputSchema?: StructuredSchema;
|
|
25
|
+
public readonly gatherSchema?: StructuredSchema;
|
|
26
|
+
public readonly initialData?: Partial<TExtracted>;
|
|
27
|
+
private routingExtrasSchema?: StructuredSchema;
|
|
23
28
|
private guidelines: Guideline[] = [];
|
|
24
29
|
|
|
25
|
-
constructor(options: RouteOptions) {
|
|
30
|
+
constructor(options: RouteOptions<TExtracted>) {
|
|
26
31
|
// Use provided ID or generate a deterministic one from the title
|
|
27
32
|
this.id = options.id || generateRouteId(options.title);
|
|
28
33
|
this.title = options.title;
|
|
@@ -31,7 +36,14 @@ export class Route<TContext = unknown> {
|
|
|
31
36
|
this.domains = options.domains;
|
|
32
37
|
this.rules = options.rules || ([] as string[]);
|
|
33
38
|
this.prohibitions = options.prohibitions || ([] as string[]);
|
|
34
|
-
this.initialState = new State<TContext>(
|
|
39
|
+
this.initialState = new State<TContext, TExtracted>(
|
|
40
|
+
this.id,
|
|
41
|
+
"Initial state"
|
|
42
|
+
);
|
|
43
|
+
this.routingExtrasSchema = options.routingExtrasSchema;
|
|
44
|
+
this.responseOutputSchema = options.responseOutputSchema;
|
|
45
|
+
this.gatherSchema = options.gatherSchema;
|
|
46
|
+
this.initialData = options.initialData;
|
|
35
47
|
|
|
36
48
|
// Initialize guidelines from options
|
|
37
49
|
if (options.guidelines) {
|
|
@@ -83,32 +95,35 @@ export class Route<TContext = unknown> {
|
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
/**
|
|
86
|
-
* Get
|
|
98
|
+
* Get optional extras schema requested during routing
|
|
87
99
|
*/
|
|
88
|
-
|
|
89
|
-
return
|
|
90
|
-
id: this.id,
|
|
91
|
-
};
|
|
100
|
+
getRoutingExtrasSchema(): StructuredSchema | undefined {
|
|
101
|
+
return this.routingExtrasSchema;
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
/**
|
|
95
|
-
*
|
|
96
|
-
* Useful for disambiguation
|
|
105
|
+
* Get optional structured response schema for this route's message
|
|
97
106
|
*/
|
|
98
|
-
|
|
107
|
+
getResponseOutputSchema(): StructuredSchema | undefined {
|
|
108
|
+
return this.responseOutputSchema;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Get route reference
|
|
113
|
+
*/
|
|
114
|
+
getRef(): RouteRef {
|
|
99
115
|
return {
|
|
100
116
|
id: this.id,
|
|
101
|
-
route: this,
|
|
102
117
|
};
|
|
103
118
|
}
|
|
104
119
|
|
|
105
120
|
/**
|
|
106
121
|
* Get all states in this route (via traversal from initial state)
|
|
107
122
|
*/
|
|
108
|
-
getAllStates(): State<TContext>[] {
|
|
123
|
+
getAllStates(): State<TContext, TExtracted>[] {
|
|
109
124
|
const visited = new Set<string>();
|
|
110
|
-
const states: State<TContext>[] = [];
|
|
111
|
-
const queue: State<TContext>[] = [this.initialState];
|
|
125
|
+
const states: State<TContext, TExtracted>[] = [];
|
|
126
|
+
const queue: State<TContext, TExtracted>[] = [this.initialState];
|
|
112
127
|
|
|
113
128
|
while (queue.length > 0) {
|
|
114
129
|
const current = queue.shift()!;
|
|
@@ -137,7 +152,7 @@ export class Route<TContext = unknown> {
|
|
|
137
152
|
* @param stateId - The state ID to find
|
|
138
153
|
* @returns The state if found, undefined otherwise
|
|
139
154
|
*/
|
|
140
|
-
getState(stateId: string): State<TContext> | undefined {
|
|
155
|
+
getState(stateId: string): State<TContext, TExtracted> | undefined {
|
|
141
156
|
const states = this.getAllStates();
|
|
142
157
|
return states.find((state) => state.id === stateId);
|
|
143
158
|
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { Event } from "../types/history";
|
|
2
|
+
import type { Route } from "./Route";
|
|
3
|
+
import type { StructuredSchema } from "../types/schema";
|
|
4
|
+
import type { RoutingDecision } from "../types/routing";
|
|
5
|
+
import type { SessionState } from "../types/session";
|
|
6
|
+
import { PromptComposer } from "./PromptComposer";
|
|
7
|
+
|
|
8
|
+
export interface RoutingDecisionOutput {
|
|
9
|
+
context: string;
|
|
10
|
+
routes: Record<string, number>;
|
|
11
|
+
responseDirectives?: string[];
|
|
12
|
+
extractions?: Array<{
|
|
13
|
+
name: string;
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
value: any;
|
|
16
|
+
confidence?: number;
|
|
17
|
+
source?: "message" | "history";
|
|
18
|
+
}>;
|
|
19
|
+
contextUpdate?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface RoutingEngineOptions {
|
|
23
|
+
allowRouteSwitch?: boolean;
|
|
24
|
+
switchThreshold?: number; // 0-100
|
|
25
|
+
maxCandidates?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class RoutingEngine<TContext = unknown> {
|
|
29
|
+
constructor(private readonly options?: RoutingEngineOptions) {}
|
|
30
|
+
|
|
31
|
+
buildDynamicRoutingSchema(
|
|
32
|
+
routes: Route<TContext>[],
|
|
33
|
+
extrasSchema?: StructuredSchema
|
|
34
|
+
): StructuredSchema {
|
|
35
|
+
const routeIds = routes.map((r) => r.id);
|
|
36
|
+
const routeProperties: Record<string, StructuredSchema> = {};
|
|
37
|
+
for (const id of routeIds) {
|
|
38
|
+
routeProperties[id] = {
|
|
39
|
+
type: "number",
|
|
40
|
+
nullable: false,
|
|
41
|
+
description: `Score for route ${id} based on direct evidence, context and semantic fit (0-100)`,
|
|
42
|
+
minimum: 0,
|
|
43
|
+
maximum: 100,
|
|
44
|
+
} as StructuredSchema;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const base: StructuredSchema = {
|
|
48
|
+
description:
|
|
49
|
+
"Full intent analysis: score ALL available routes (0-100) using evidence and context",
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: {
|
|
52
|
+
context: {
|
|
53
|
+
type: "string",
|
|
54
|
+
nullable: false,
|
|
55
|
+
description: "Brief summary of the user's intent/context",
|
|
56
|
+
},
|
|
57
|
+
routes: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: routeProperties,
|
|
60
|
+
required: routeIds,
|
|
61
|
+
nullable: false,
|
|
62
|
+
description: "Mapping of routeId to score (0-100)",
|
|
63
|
+
},
|
|
64
|
+
responseDirectives: {
|
|
65
|
+
type: "array",
|
|
66
|
+
items: { type: "string" },
|
|
67
|
+
description:
|
|
68
|
+
"Optional bullet points the response should address (concise)",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
required: ["context", "routes"],
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
if (extrasSchema) {
|
|
76
|
+
base.properties = base.properties || {};
|
|
77
|
+
base.properties.extractions = extrasSchema;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return base;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
buildRoutingPrompt(
|
|
84
|
+
history: Event[],
|
|
85
|
+
routes: Route<TContext>[],
|
|
86
|
+
lastMessage: string,
|
|
87
|
+
agentMeta?: {
|
|
88
|
+
name?: string;
|
|
89
|
+
goal?: string;
|
|
90
|
+
description?: string;
|
|
91
|
+
personality?: string;
|
|
92
|
+
},
|
|
93
|
+
session?: SessionState
|
|
94
|
+
): string {
|
|
95
|
+
const pc = new PromptComposer();
|
|
96
|
+
if (agentMeta?.name || agentMeta?.goal || agentMeta?.description) {
|
|
97
|
+
pc.addAgentMeta({
|
|
98
|
+
name: agentMeta?.name || "Agent",
|
|
99
|
+
description: agentMeta?.description,
|
|
100
|
+
goal: agentMeta?.goal,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const personality =
|
|
104
|
+
agentMeta?.personality || "Tone: brief, natural, 1-2 short sentences.";
|
|
105
|
+
pc.addPersonality(personality);
|
|
106
|
+
pc.addInstruction(
|
|
107
|
+
"Task: Intent analysis and route scoring (0-100). Score ALL listed routes."
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Add session context if available
|
|
111
|
+
if (session?.currentRoute) {
|
|
112
|
+
const sessionInfo = [
|
|
113
|
+
"Current conversation context:",
|
|
114
|
+
`- Active route: ${session.currentRoute.title} (${session.currentRoute.id})`,
|
|
115
|
+
];
|
|
116
|
+
if (session.currentState) {
|
|
117
|
+
sessionInfo.push(`- Current state: ${session.currentState.id}`);
|
|
118
|
+
if (session.currentState.description) {
|
|
119
|
+
sessionInfo.push(` "${session.currentState.description}"`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (Object.keys(session.extracted).length > 0) {
|
|
123
|
+
sessionInfo.push(
|
|
124
|
+
`- Extracted data: ${JSON.stringify(session.extracted)}`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
sessionInfo.push(
|
|
128
|
+
"Note: User is mid-conversation. They may want to continue current route or switch to a new one based on their intent."
|
|
129
|
+
);
|
|
130
|
+
pc.addInstruction(sessionInfo.join("\n"));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
pc.addInteractionHistory(history);
|
|
134
|
+
pc.addLastMessage(lastMessage);
|
|
135
|
+
// Cast to unknown to satisfy generic constraints in composer
|
|
136
|
+
// This is safe because PromptComposer only reads route metadata (id, title, description)
|
|
137
|
+
pc.addRoutingOverview(routes as unknown as Route<unknown>[]);
|
|
138
|
+
pc.addInstruction(
|
|
139
|
+
[
|
|
140
|
+
"Scoring rules:",
|
|
141
|
+
"- 90-100: explicit keywords + clear intent",
|
|
142
|
+
"- 70-89: strong contextual evidence + relevant keywords",
|
|
143
|
+
"- 50-69: moderate relevance",
|
|
144
|
+
"- 30-49: weak connection or ambiguous",
|
|
145
|
+
"- 0-29: minimal/none",
|
|
146
|
+
"Return ONLY JSON matching the provided schema. Include scores for ALL routes.",
|
|
147
|
+
].join("\n")
|
|
148
|
+
);
|
|
149
|
+
return pc.build();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
decideRouteFromScores(output: RoutingDecision): {
|
|
153
|
+
routeId: string;
|
|
154
|
+
maxScore: number;
|
|
155
|
+
} {
|
|
156
|
+
// Optionally limit candidates and apply switching threshold
|
|
157
|
+
const entries = Object.entries(output.routes).sort((a, b) => b[1] - a[1]);
|
|
158
|
+
const limited = this.options?.maxCandidates
|
|
159
|
+
? entries.slice(0, this.options.maxCandidates)
|
|
160
|
+
: entries;
|
|
161
|
+
const [topId, topScore] = limited[0] || ["", 0];
|
|
162
|
+
// switchThreshold is enforced by caller when a current route exists
|
|
163
|
+
return { routeId: topId, maxScore: topScore };
|
|
164
|
+
}
|
|
165
|
+
}
|