@falai/agent 0.4.1 → 0.5.1
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 +55 -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 +77 -0
- package/dist/cjs/types/session.d.ts.map +1 -0
- package/dist/cjs/types/session.js +146 -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 +55 -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 +77 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +138 -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 +329 -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 +668 -356
- package/src/core/Events.ts +12 -2
- package/src/core/PersistenceManager.ts +81 -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 +219 -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
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example: Modifying Extracted Data with Tools and Hooks
|
|
3
|
+
*
|
|
4
|
+
* This demonstrates:
|
|
5
|
+
* 1. Schema-first data extraction with JSON Schema
|
|
6
|
+
* 2. Tools that modify extracted data (validation, enrichment)
|
|
7
|
+
* 3. Lifecycle hooks for data validation and business logic
|
|
8
|
+
* 4. Data-driven state flows with code-based logic (skipIf, requiredData)
|
|
9
|
+
* 5. Three-phase pipeline: PREPARATION → ROUTING → RESPONSE
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Agent, createSession, EventSource, createMessageEvent } from "../src";
|
|
13
|
+
import type { Event } from "../src/types";
|
|
14
|
+
import type { ToolRef } from "../src/types/tool";
|
|
15
|
+
|
|
16
|
+
// ==============================================================================
|
|
17
|
+
// CONTEXT & EXTRACTED DATA TYPES
|
|
18
|
+
// ==============================================================================
|
|
19
|
+
|
|
20
|
+
interface FlightBookingContext {
|
|
21
|
+
availableFlights?: Array<{
|
|
22
|
+
flightNumber: string;
|
|
23
|
+
price: number;
|
|
24
|
+
departure: string;
|
|
25
|
+
arrival: string;
|
|
26
|
+
}>;
|
|
27
|
+
airportCodes?: Record<string, string>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface FlightData {
|
|
31
|
+
destination: string;
|
|
32
|
+
destinationCode?: string; // Enriched by tool
|
|
33
|
+
departureDate: string;
|
|
34
|
+
departureDateParsed?: string; // Enriched by tool
|
|
35
|
+
passengers: number;
|
|
36
|
+
cabinClass: "economy" | "business" | "first";
|
|
37
|
+
shouldSearchFlights?: boolean; // Trigger flag
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ==============================================================================
|
|
41
|
+
// TOOLS: Data Enrichment (PREPARATION Phase)
|
|
42
|
+
// ==============================================================================
|
|
43
|
+
|
|
44
|
+
// Tool 1: Convert city names to airport codes
|
|
45
|
+
const enrichDestinationTool: ToolRef<
|
|
46
|
+
FlightBookingContext,
|
|
47
|
+
[],
|
|
48
|
+
void,
|
|
49
|
+
FlightData
|
|
50
|
+
> = {
|
|
51
|
+
id: "enrich_destination",
|
|
52
|
+
name: "Enrich Destination",
|
|
53
|
+
description: "Convert city names to IATA airport codes",
|
|
54
|
+
handler: async (context) => {
|
|
55
|
+
const { extracted } = context;
|
|
56
|
+
const destination = (extracted as Partial<FlightData>)?.destination;
|
|
57
|
+
|
|
58
|
+
if (!destination) {
|
|
59
|
+
return { data: undefined };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Simulate airport code lookup
|
|
63
|
+
const airportCodes: Record<string, string> = {
|
|
64
|
+
Paris: "CDG",
|
|
65
|
+
London: "LHR",
|
|
66
|
+
"New York": "JFK",
|
|
67
|
+
Tokyo: "NRT",
|
|
68
|
+
"Los Angeles": "LAX",
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const destinationCode = airportCodes[destination];
|
|
72
|
+
|
|
73
|
+
console.log(`[Tool] Enriched: ${destination} → ${destinationCode}`);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
data: undefined,
|
|
77
|
+
extractedUpdate: {
|
|
78
|
+
destinationCode,
|
|
79
|
+
} as Partial<FlightData>,
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Tool 2: Parse and validate dates
|
|
85
|
+
const validateDateTool: ToolRef<FlightBookingContext, [], void, FlightData> = {
|
|
86
|
+
id: "validate_date",
|
|
87
|
+
name: "Validate Date",
|
|
88
|
+
description:
|
|
89
|
+
"Parse relative dates (today, tomorrow) to ISO format and validate",
|
|
90
|
+
handler: async (context) => {
|
|
91
|
+
const { extracted } = context;
|
|
92
|
+
const departureDate = (extracted as Partial<FlightData>)?.departureDate;
|
|
93
|
+
|
|
94
|
+
if (!departureDate) {
|
|
95
|
+
return { data: undefined };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let parsedDate: string;
|
|
99
|
+
const today = new Date();
|
|
100
|
+
|
|
101
|
+
// Parse relative dates
|
|
102
|
+
if (departureDate.toLowerCase() === "today") {
|
|
103
|
+
parsedDate = today.toISOString().split("T")[0];
|
|
104
|
+
} else if (departureDate.toLowerCase() === "tomorrow") {
|
|
105
|
+
const tomorrow = new Date(today);
|
|
106
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
107
|
+
parsedDate = tomorrow.toISOString().split("T")[0];
|
|
108
|
+
} else if (departureDate.toLowerCase().startsWith("next ")) {
|
|
109
|
+
// Handle "next week", "next month", etc.
|
|
110
|
+
const nextWeek = new Date(today);
|
|
111
|
+
nextWeek.setDate(nextWeek.getDate() + 7);
|
|
112
|
+
parsedDate = nextWeek.toISOString().split("T")[0];
|
|
113
|
+
} else {
|
|
114
|
+
// Assume it's already in a proper format
|
|
115
|
+
parsedDate = departureDate;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Validate that date isn't in the past
|
|
119
|
+
const parsedDateObj = new Date(parsedDate);
|
|
120
|
+
if (parsedDateObj < today) {
|
|
121
|
+
console.warn(`[Tool] Warning: Date ${parsedDate} is in the past`);
|
|
122
|
+
// You could throw an error here or set a flag
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log(`[Tool] Parsed date: ${departureDate} → ${parsedDate}`);
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
data: undefined,
|
|
129
|
+
extractedUpdate: {
|
|
130
|
+
departureDateParsed: parsedDate,
|
|
131
|
+
} as Partial<FlightData>,
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Tool 3: Search for flights (triggered by flag)
|
|
137
|
+
const searchFlightsTool: ToolRef<FlightBookingContext, [], void, FlightData> = {
|
|
138
|
+
id: "search_flights",
|
|
139
|
+
name: "Search Flights",
|
|
140
|
+
description: "Search for available flights based on gathered data",
|
|
141
|
+
handler: async (context) => {
|
|
142
|
+
const { extracted } = context;
|
|
143
|
+
const flightData = extracted as Partial<FlightData>;
|
|
144
|
+
|
|
145
|
+
if (!flightData?.destinationCode || !flightData?.departureDateParsed) {
|
|
146
|
+
console.log("[Tool] Cannot search flights: missing required data");
|
|
147
|
+
return { data: undefined };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Simulate flight search API call
|
|
151
|
+
const flights = [
|
|
152
|
+
{
|
|
153
|
+
flightNumber: "UA123",
|
|
154
|
+
price: 450,
|
|
155
|
+
departure: "08:00",
|
|
156
|
+
arrival: "14:30",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
flightNumber: "DL456",
|
|
160
|
+
price: 520,
|
|
161
|
+
departure: "10:30",
|
|
162
|
+
arrival: "16:45",
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
console.log(
|
|
167
|
+
`[Tool] Found ${flights.length} flights to ${flightData.destinationCode}`
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
data: undefined,
|
|
172
|
+
contextUpdate: {
|
|
173
|
+
availableFlights: flights,
|
|
174
|
+
},
|
|
175
|
+
extractedUpdate: {
|
|
176
|
+
shouldSearchFlights: false, // Clear the flag to prevent re-execution
|
|
177
|
+
} as Partial<FlightData>,
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// ==============================================================================
|
|
183
|
+
// LIFECYCLE HOOKS: Data Validation & Business Logic (RESPONSE Phase)
|
|
184
|
+
// ==============================================================================
|
|
185
|
+
|
|
186
|
+
async function onExtractedUpdate(
|
|
187
|
+
extracted: Record<string, unknown>,
|
|
188
|
+
previousExtracted: Record<string, unknown>
|
|
189
|
+
): Promise<Record<string, unknown>> {
|
|
190
|
+
console.log("[Hook] onExtractedUpdate called");
|
|
191
|
+
console.log(" Previous:", previousExtracted);
|
|
192
|
+
console.log(" New:", extracted);
|
|
193
|
+
|
|
194
|
+
// Example: Validate passenger count
|
|
195
|
+
const passengers = extracted.passengers as number | undefined;
|
|
196
|
+
if (typeof passengers === "number") {
|
|
197
|
+
if (passengers < 1) {
|
|
198
|
+
console.warn("[Hook] Invalid passengers count, setting to 1");
|
|
199
|
+
extracted.passengers = 1;
|
|
200
|
+
}
|
|
201
|
+
if (passengers > 9) {
|
|
202
|
+
console.warn("[Hook] Too many passengers, capping at 9");
|
|
203
|
+
extracted.passengers = 9;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Example: Normalize cabin class
|
|
208
|
+
const cabinClass = extracted.cabinClass as string | undefined;
|
|
209
|
+
if (typeof cabinClass === "string") {
|
|
210
|
+
extracted.cabinClass = cabinClass.toLowerCase();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Example: Auto-trigger flight search when we have enough data
|
|
214
|
+
const hasDestination = extracted.destinationCode || extracted.destination;
|
|
215
|
+
const hasDate = extracted.departureDateParsed || extracted.departureDate;
|
|
216
|
+
const hasPassengers = typeof passengers === "number";
|
|
217
|
+
|
|
218
|
+
if (
|
|
219
|
+
hasDestination &&
|
|
220
|
+
hasDate &&
|
|
221
|
+
hasPassengers &&
|
|
222
|
+
!extracted.shouldSearchFlights
|
|
223
|
+
) {
|
|
224
|
+
console.log("[Hook] All required data gathered, triggering flight search");
|
|
225
|
+
extracted.shouldSearchFlights = true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return extracted;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ==============================================================================
|
|
232
|
+
// AGENT SETUP
|
|
233
|
+
// ==============================================================================
|
|
234
|
+
|
|
235
|
+
const agent = new Agent<FlightBookingContext>({
|
|
236
|
+
name: "Flight Booking Agent",
|
|
237
|
+
goal: "Help users book flights efficiently",
|
|
238
|
+
description: "I help you find and book flights",
|
|
239
|
+
ai: null as any, // Replace with actual AI provider
|
|
240
|
+
context: {},
|
|
241
|
+
hooks: {
|
|
242
|
+
onExtractedUpdate, // Validation & enrichment hook
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Define route with data extraction
|
|
247
|
+
const bookingRoute = agent.createRoute<FlightData>({
|
|
248
|
+
title: "Book Flight",
|
|
249
|
+
description: "Help user book a flight",
|
|
250
|
+
conditions: [
|
|
251
|
+
"User wants to book a flight",
|
|
252
|
+
"User mentions flying, traveling, or booking",
|
|
253
|
+
],
|
|
254
|
+
gatherSchema: {
|
|
255
|
+
type: "object",
|
|
256
|
+
properties: {
|
|
257
|
+
destination: {
|
|
258
|
+
type: "string",
|
|
259
|
+
description: "City or airport the user wants to fly to",
|
|
260
|
+
},
|
|
261
|
+
destinationCode: {
|
|
262
|
+
type: "string",
|
|
263
|
+
description: "IATA airport code (enriched by tool)",
|
|
264
|
+
},
|
|
265
|
+
departureDate: {
|
|
266
|
+
type: "string",
|
|
267
|
+
description: "When the user wants to depart",
|
|
268
|
+
},
|
|
269
|
+
departureDateParsed: {
|
|
270
|
+
type: "string",
|
|
271
|
+
description: "Parsed ISO date (enriched by tool)",
|
|
272
|
+
},
|
|
273
|
+
passengers: {
|
|
274
|
+
type: "number",
|
|
275
|
+
minimum: 1,
|
|
276
|
+
maximum: 9,
|
|
277
|
+
},
|
|
278
|
+
cabinClass: {
|
|
279
|
+
type: "string",
|
|
280
|
+
enum: ["economy", "business", "first"],
|
|
281
|
+
default: "economy",
|
|
282
|
+
},
|
|
283
|
+
shouldSearchFlights: {
|
|
284
|
+
type: "boolean",
|
|
285
|
+
description: "Flag to trigger flight search",
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
required: ["destination", "departureDate", "passengers"],
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// State 1: Gather destination
|
|
293
|
+
const gatherDestination = bookingRoute.initialState.transitionTo({
|
|
294
|
+
chatState: "Ask where they want to fly",
|
|
295
|
+
gather: ["destination"],
|
|
296
|
+
skipIf: (data) => !!data.destination,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// State 2: Enrich destination (tool execution)
|
|
300
|
+
const enrichDestination = gatherDestination.transitionTo({
|
|
301
|
+
toolState: enrichDestinationTool,
|
|
302
|
+
requiredData: ["destination"],
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// State 3: Gather date
|
|
306
|
+
const gatherDate = enrichDestination.transitionTo({
|
|
307
|
+
chatState: "Ask when they want to depart",
|
|
308
|
+
gather: ["departureDate"],
|
|
309
|
+
skipIf: (data) => !!data.departureDate,
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// State 4: Validate/parse date (tool execution)
|
|
313
|
+
const validateDate = gatherDate.transitionTo({
|
|
314
|
+
toolState: validateDateTool,
|
|
315
|
+
requiredData: ["departureDate"],
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// State 5: Gather passengers
|
|
319
|
+
const gatherPassengers = validateDate.transitionTo({
|
|
320
|
+
chatState: "Ask how many passengers",
|
|
321
|
+
gather: ["passengers"],
|
|
322
|
+
skipIf: (data) => !!data.passengers,
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// State 6: Search flights (triggered by hook setting shouldSearchFlights)
|
|
326
|
+
const searchFlights = gatherPassengers.transitionTo({
|
|
327
|
+
toolState: searchFlightsTool,
|
|
328
|
+
// This state is entered when shouldSearchFlights is true
|
|
329
|
+
// The hook automatically sets this flag when all data is gathered
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// State 7: Present results
|
|
333
|
+
const presentResults = searchFlights.transitionTo({
|
|
334
|
+
chatState: "Present available flights to the user",
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// ==============================================================================
|
|
338
|
+
// USAGE EXAMPLE: Three-Phase Pipeline Demonstration
|
|
339
|
+
// ==============================================================================
|
|
340
|
+
|
|
341
|
+
async function main() {
|
|
342
|
+
let session = createSession<FlightData>();
|
|
343
|
+
|
|
344
|
+
// Turn 1: User provides everything at once
|
|
345
|
+
const history: Event[] = [
|
|
346
|
+
createMessageEvent(
|
|
347
|
+
EventSource.CUSTOMER,
|
|
348
|
+
"User",
|
|
349
|
+
"I want to fly to Paris tomorrow with 2 passengers"
|
|
350
|
+
),
|
|
351
|
+
];
|
|
352
|
+
|
|
353
|
+
const response = await agent.respond({ history, session });
|
|
354
|
+
|
|
355
|
+
console.log("\n=== RESPONSE ===");
|
|
356
|
+
console.log("Message:", response.message);
|
|
357
|
+
console.log("Extracted:", response.session?.extracted);
|
|
358
|
+
console.log("Context:", agent["context"]);
|
|
359
|
+
|
|
360
|
+
/*
|
|
361
|
+
* Expected flow:
|
|
362
|
+
* 1. AI extracts: { destination: "Paris", departureDate: "tomorrow", passengers: 2 }
|
|
363
|
+
* 2. Hook validates passengers ✓
|
|
364
|
+
* 3. Hook detects all data present, sets shouldSearchFlights: true
|
|
365
|
+
* 4. State machine:
|
|
366
|
+
* - Skips gatherDestination (has destination)
|
|
367
|
+
* - Runs enrichDestination tool → adds destinationCode: "CDG"
|
|
368
|
+
* - Skips gatherDate (has departureDate)
|
|
369
|
+
* - Runs validateDate tool → adds departureDateParsed: "2025-10-16"
|
|
370
|
+
* - Skips gatherPassengers (has passengers)
|
|
371
|
+
* - Runs searchFlights tool → updates context with flights, clears flag
|
|
372
|
+
* - Enters presentResults state
|
|
373
|
+
* 5. AI generates response with flight options
|
|
374
|
+
*/
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ==============================================================================
|
|
378
|
+
// KEY TAKEAWAYS: Architecture Principles Demonstrated
|
|
379
|
+
// ==============================================================================
|
|
380
|
+
|
|
381
|
+
/*
|
|
382
|
+
* 1. SCHEMA-FIRST DATA EXTRACTION
|
|
383
|
+
* - Define data contracts upfront with JSON Schema
|
|
384
|
+
* - Type-safe extraction throughout the conversation
|
|
385
|
+
* - Predictable data structure every time
|
|
386
|
+
*
|
|
387
|
+
* 2. CODE-BASED STATE LOGIC
|
|
388
|
+
* - Use TypeScript functions (skipIf, requiredData) instead of LLM conditions
|
|
389
|
+
* - Deterministic flow based on extracted data
|
|
390
|
+
* - No fuzzy LLM interpretation of state logic
|
|
391
|
+
*
|
|
392
|
+
* 3. TOOLS ENRICH EXTRACTED DATA (PREPARATION Phase)
|
|
393
|
+
* - Tools modify extracted data via `extractedUpdate`
|
|
394
|
+
* - Great for: validation, enrichment, normalization, computed fields
|
|
395
|
+
* - Execute before AI sees the conversation
|
|
396
|
+
*
|
|
397
|
+
* 4. LIFECYCLE HOOKS (RESPONSE Phase)
|
|
398
|
+
* - onExtractedUpdate runs after each data extraction
|
|
399
|
+
* - Cross-cutting logic: validation, business rules, auto-triggering
|
|
400
|
+
* - Return modified extracted data
|
|
401
|
+
*
|
|
402
|
+
* 5. THREE-PHASE PIPELINE
|
|
403
|
+
* - PREPARATION: Tools execute and enrich context/data
|
|
404
|
+
* - ROUTING: AI scores routes based on current state
|
|
405
|
+
* - RESPONSE: AI generates message with schema-enforced extraction
|
|
406
|
+
*
|
|
407
|
+
* 6. SESSION STATE MANAGEMENT
|
|
408
|
+
* - Extracted data persists across conversation turns
|
|
409
|
+
* - Always-on routing respects user intent changes
|
|
410
|
+
* - State recovery enables conversation resumption
|
|
411
|
+
*/
|
|
412
|
+
|
|
413
|
+
if (require.main === module) {
|
|
414
|
+
main().catch(console.error);
|
|
415
|
+
}
|