@falai/agent 0.3.30 → 0.4.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/dist/cjs/core/Agent.d.ts +1 -0
- package/dist/cjs/core/Agent.d.ts.map +1 -1
- package/dist/cjs/core/Agent.js +62 -2
- package/dist/cjs/core/Agent.js.map +1 -1
- package/dist/cjs/core/ConditionEvaluator.d.ts +72 -0
- package/dist/cjs/core/ConditionEvaluator.d.ts.map +1 -0
- package/dist/cjs/core/ConditionEvaluator.js +272 -0
- package/dist/cjs/core/ConditionEvaluator.js.map +1 -0
- package/dist/cjs/core/PreparationEngine.d.ts +116 -0
- package/dist/cjs/core/PreparationEngine.d.ts.map +1 -0
- package/dist/cjs/core/PreparationEngine.js +353 -0
- package/dist/cjs/core/PreparationEngine.js.map +1 -0
- package/dist/cjs/core/Route.d.ts +6 -0
- package/dist/cjs/core/Route.d.ts.map +1 -1
- package/dist/cjs/core/Route.js +9 -0
- package/dist/cjs/core/Route.js.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.d.ts +3 -3
- package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
- package/dist/cjs/providers/GeminiProvider.d.ts +3 -3
- package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/cjs/providers/GeminiProvider.js +43 -18
- package/dist/cjs/providers/GeminiProvider.js.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.d.ts +3 -3
- package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.d.ts +3 -3
- package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
- package/dist/cjs/types/agent.d.ts +3 -0
- package/dist/cjs/types/agent.d.ts.map +1 -1
- package/dist/cjs/types/ai.d.ts +6 -6
- package/dist/cjs/types/ai.d.ts.map +1 -1
- package/dist/core/Agent.d.ts +1 -0
- package/dist/core/Agent.d.ts.map +1 -1
- package/dist/core/Agent.js +62 -2
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/ConditionEvaluator.d.ts +72 -0
- package/dist/core/ConditionEvaluator.d.ts.map +1 -0
- package/dist/core/ConditionEvaluator.js +268 -0
- package/dist/core/ConditionEvaluator.js.map +1 -0
- package/dist/core/PreparationEngine.d.ts +116 -0
- package/dist/core/PreparationEngine.d.ts.map +1 -0
- package/dist/core/PreparationEngine.js +349 -0
- package/dist/core/PreparationEngine.js.map +1 -0
- package/dist/core/Route.d.ts +6 -0
- package/dist/core/Route.d.ts.map +1 -1
- package/dist/core/Route.js +9 -0
- package/dist/core/Route.js.map +1 -1
- package/dist/providers/AnthropicProvider.d.ts +3 -3
- package/dist/providers/AnthropicProvider.d.ts.map +1 -1
- package/dist/providers/AnthropicProvider.js.map +1 -1
- package/dist/providers/GeminiProvider.d.ts +3 -3
- package/dist/providers/GeminiProvider.d.ts.map +1 -1
- package/dist/providers/GeminiProvider.js +43 -18
- package/dist/providers/GeminiProvider.js.map +1 -1
- package/dist/providers/OpenAIProvider.d.ts +3 -3
- package/dist/providers/OpenAIProvider.d.ts.map +1 -1
- package/dist/providers/OpenAIProvider.js.map +1 -1
- package/dist/providers/OpenRouterProvider.d.ts +3 -3
- package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
- package/dist/providers/OpenRouterProvider.js.map +1 -1
- package/dist/types/agent.d.ts +3 -0
- package/dist/types/agent.d.ts.map +1 -1
- package/dist/types/ai.d.ts +6 -6
- package/dist/types/ai.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/Agent.ts +86 -2
- package/src/core/ConditionEvaluator.ts +381 -0
- package/src/core/PreparationEngine.ts +561 -0
- package/src/core/Route.ts +10 -0
- package/src/providers/AnthropicProvider.ts +51 -21
- package/src/providers/GeminiProvider.ts +86 -40
- package/src/providers/OpenAIProvider.ts +48 -21
- package/src/providers/OpenRouterProvider.ts +36 -18
- package/src/types/agent.ts +3 -0
- package/src/types/ai.ts +13 -8
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConditionEvaluator - Handles AI-powered condition evaluation
|
|
3
|
+
*
|
|
4
|
+
* This class is responsible for:
|
|
5
|
+
* - Evaluating guideline conditions
|
|
6
|
+
* - Evaluating transition conditions
|
|
7
|
+
* - Extracting tool arguments from context and history
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Event, AiProvider, MessageEventData } from "../types/index";
|
|
11
|
+
import { EventKind } from "../types/history";
|
|
12
|
+
import type { Guideline } from "../types/agent";
|
|
13
|
+
import type { ToolRef } from "../types/tool";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Result of guideline condition evaluation
|
|
17
|
+
*/
|
|
18
|
+
export interface GuidelineEvaluationResult {
|
|
19
|
+
matches: boolean;
|
|
20
|
+
rationale?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Result of transition condition evaluation
|
|
25
|
+
*/
|
|
26
|
+
export interface TransitionEvaluationResult {
|
|
27
|
+
shouldFollow: boolean;
|
|
28
|
+
rationale?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Result of tool argument extraction
|
|
33
|
+
*/
|
|
34
|
+
export interface ArgumentExtractionResult {
|
|
35
|
+
arguments: unknown[];
|
|
36
|
+
rationale?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Schema for guideline evaluation response
|
|
41
|
+
*/
|
|
42
|
+
interface GuidelineEvaluationSchema {
|
|
43
|
+
matches: boolean;
|
|
44
|
+
rationale: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Schema for transition evaluation response
|
|
49
|
+
*/
|
|
50
|
+
interface TransitionEvaluationSchema {
|
|
51
|
+
shouldFollow: boolean;
|
|
52
|
+
rationale: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Schema for argument extraction response
|
|
57
|
+
*/
|
|
58
|
+
interface ArgumentExtractionSchema {
|
|
59
|
+
arguments: unknown[];
|
|
60
|
+
rationale?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* ConditionEvaluator - Evaluates conditions using AI
|
|
65
|
+
*/
|
|
66
|
+
export class ConditionEvaluator<TContext = unknown> {
|
|
67
|
+
constructor(private readonly ai: AiProvider) {}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Evaluate a guideline condition against context and history
|
|
71
|
+
*/
|
|
72
|
+
async evaluateGuidelineCondition(
|
|
73
|
+
guideline: Guideline,
|
|
74
|
+
context: TContext,
|
|
75
|
+
history: Event[]
|
|
76
|
+
): Promise<GuidelineEvaluationResult> {
|
|
77
|
+
try {
|
|
78
|
+
const recentMessages = this.extractRecentMessages(history);
|
|
79
|
+
|
|
80
|
+
const prompt = `You are evaluating whether a guideline condition is met.
|
|
81
|
+
|
|
82
|
+
Guideline Condition: ${guideline.condition}
|
|
83
|
+
Action: ${guideline.action}
|
|
84
|
+
|
|
85
|
+
Current Context:
|
|
86
|
+
${JSON.stringify(context, null, 2)}
|
|
87
|
+
|
|
88
|
+
Recent Conversation:
|
|
89
|
+
${recentMessages || "(No recent messages)"}
|
|
90
|
+
|
|
91
|
+
Evaluate whether the guideline condition is currently met based on the context and conversation.
|
|
92
|
+
|
|
93
|
+
Your response must be a JSON object with this exact structure:
|
|
94
|
+
{
|
|
95
|
+
"matches": boolean (true or false),
|
|
96
|
+
"rationale": string (brief explanation)
|
|
97
|
+
}`;
|
|
98
|
+
|
|
99
|
+
const result = await this.ai.generateMessage<
|
|
100
|
+
TContext,
|
|
101
|
+
GuidelineEvaluationSchema
|
|
102
|
+
>({
|
|
103
|
+
prompt,
|
|
104
|
+
history,
|
|
105
|
+
context,
|
|
106
|
+
parameters: {
|
|
107
|
+
jsonMode: true,
|
|
108
|
+
maxOutputTokens: 500,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Parse structured response
|
|
113
|
+
if (result.structured) {
|
|
114
|
+
return {
|
|
115
|
+
matches: result.structured.matches === true,
|
|
116
|
+
rationale: result.structured.rationale,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Fallback parsing
|
|
121
|
+
return this.parseGuidelineResponse(result.message);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error(
|
|
124
|
+
`[ConditionEvaluator] Failed to evaluate guideline condition: ${guideline.id}`,
|
|
125
|
+
error
|
|
126
|
+
);
|
|
127
|
+
return {
|
|
128
|
+
matches: false,
|
|
129
|
+
rationale: "Failed to evaluate condition",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Evaluate a transition condition
|
|
136
|
+
*/
|
|
137
|
+
async evaluateTransitionCondition(
|
|
138
|
+
condition: string,
|
|
139
|
+
context: TContext,
|
|
140
|
+
history: Event[]
|
|
141
|
+
): Promise<TransitionEvaluationResult> {
|
|
142
|
+
try {
|
|
143
|
+
const recentMessages = this.extractRecentMessages(history);
|
|
144
|
+
|
|
145
|
+
const prompt = `You are evaluating whether a state transition condition is met.
|
|
146
|
+
|
|
147
|
+
Transition Condition: ${condition}
|
|
148
|
+
|
|
149
|
+
Current Context:
|
|
150
|
+
${JSON.stringify(context, null, 2)}
|
|
151
|
+
|
|
152
|
+
Recent Conversation:
|
|
153
|
+
${recentMessages || "(No recent messages)"}
|
|
154
|
+
|
|
155
|
+
Evaluate whether this transition should be followed based on the condition, context, and conversation.
|
|
156
|
+
|
|
157
|
+
Your response must be a JSON object with this exact structure:
|
|
158
|
+
{
|
|
159
|
+
"shouldFollow": boolean (true or false),
|
|
160
|
+
"rationale": string (brief explanation)
|
|
161
|
+
}`;
|
|
162
|
+
|
|
163
|
+
const result = await this.ai.generateMessage<
|
|
164
|
+
TContext,
|
|
165
|
+
TransitionEvaluationSchema
|
|
166
|
+
>({
|
|
167
|
+
prompt,
|
|
168
|
+
history,
|
|
169
|
+
context,
|
|
170
|
+
parameters: {
|
|
171
|
+
jsonMode: true,
|
|
172
|
+
maxOutputTokens: 300,
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Parse structured response
|
|
177
|
+
if (result.structured) {
|
|
178
|
+
return {
|
|
179
|
+
shouldFollow: result.structured.shouldFollow === true,
|
|
180
|
+
rationale: result.structured.rationale,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Fallback parsing
|
|
185
|
+
return this.parseTransitionResponse(result.message);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error(
|
|
188
|
+
`[ConditionEvaluator] Failed to evaluate transition condition`,
|
|
189
|
+
error
|
|
190
|
+
);
|
|
191
|
+
return {
|
|
192
|
+
shouldFollow: false,
|
|
193
|
+
rationale: "Failed to evaluate condition",
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Extract tool arguments from context and history
|
|
200
|
+
*/
|
|
201
|
+
async extractToolArguments(
|
|
202
|
+
tool: ToolRef<TContext, unknown[], unknown>,
|
|
203
|
+
context: TContext,
|
|
204
|
+
history: Event[]
|
|
205
|
+
): Promise<ArgumentExtractionResult> {
|
|
206
|
+
try {
|
|
207
|
+
const recentMessages = this.extractRecentMessages(history);
|
|
208
|
+
|
|
209
|
+
const prompt = `You are extracting arguments for a tool call.
|
|
210
|
+
|
|
211
|
+
Tool: ${tool.name}
|
|
212
|
+
Description: ${tool.description || "No description"}
|
|
213
|
+
Parameters: ${JSON.stringify(tool.parameters, null, 2)}
|
|
214
|
+
|
|
215
|
+
Current Context:
|
|
216
|
+
${JSON.stringify(context, null, 2)}
|
|
217
|
+
|
|
218
|
+
Recent Conversation:
|
|
219
|
+
${recentMessages || "(No recent messages)"}
|
|
220
|
+
|
|
221
|
+
Extract the arguments needed to call this tool based on the context and conversation.
|
|
222
|
+
If a parameter is not available, use null or a reasonable default value.
|
|
223
|
+
|
|
224
|
+
Your response must be a JSON object with this exact structure:
|
|
225
|
+
{
|
|
226
|
+
"arguments": [arg1, arg2, ...] (array of argument values),
|
|
227
|
+
"rationale": string (optional explanation)
|
|
228
|
+
}`;
|
|
229
|
+
|
|
230
|
+
const result = await this.ai.generateMessage<
|
|
231
|
+
TContext,
|
|
232
|
+
ArgumentExtractionSchema
|
|
233
|
+
>({
|
|
234
|
+
prompt,
|
|
235
|
+
history,
|
|
236
|
+
context,
|
|
237
|
+
parameters: {
|
|
238
|
+
jsonMode: true,
|
|
239
|
+
maxOutputTokens: 500,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Parse structured response
|
|
244
|
+
if (result.structured) {
|
|
245
|
+
if (
|
|
246
|
+
result.structured.arguments &&
|
|
247
|
+
Array.isArray(result.structured.arguments)
|
|
248
|
+
) {
|
|
249
|
+
return {
|
|
250
|
+
arguments: result.structured.arguments,
|
|
251
|
+
rationale: result.structured.rationale,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Fallback: try to parse from message
|
|
257
|
+
return this.parseArgumentResponse(result.message);
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error(
|
|
260
|
+
`[ConditionEvaluator] Failed to extract tool arguments: ${tool.name}`,
|
|
261
|
+
error
|
|
262
|
+
);
|
|
263
|
+
return {
|
|
264
|
+
arguments: [],
|
|
265
|
+
rationale: "Failed to extract arguments",
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Simple argument extraction from context (fallback)
|
|
272
|
+
*/
|
|
273
|
+
simpleArgumentExtraction(
|
|
274
|
+
tool: ToolRef<TContext, unknown[], unknown>,
|
|
275
|
+
context: TContext
|
|
276
|
+
): unknown[] {
|
|
277
|
+
const contextObj = context as Record<string, unknown>;
|
|
278
|
+
const args: unknown[] = [];
|
|
279
|
+
|
|
280
|
+
// If parameters is an object with properties, try to match context keys
|
|
281
|
+
if (
|
|
282
|
+
tool.parameters &&
|
|
283
|
+
typeof tool.parameters === "object" &&
|
|
284
|
+
!Array.isArray(tool.parameters)
|
|
285
|
+
) {
|
|
286
|
+
const params = tool.parameters as Record<string, unknown>;
|
|
287
|
+
|
|
288
|
+
// Try to match parameter names with context keys
|
|
289
|
+
for (const [paramName, paramDef] of Object.entries(params)) {
|
|
290
|
+
if (contextObj[paramName] !== undefined) {
|
|
291
|
+
args.push(contextObj[paramName]);
|
|
292
|
+
} else if (
|
|
293
|
+
typeof paramDef === "object" &&
|
|
294
|
+
paramDef !== null &&
|
|
295
|
+
"default" in paramDef &&
|
|
296
|
+
typeof (paramDef as { default?: unknown }).default !== "undefined"
|
|
297
|
+
) {
|
|
298
|
+
args.push((paramDef as { default: unknown }).default);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return args;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Private helper methods
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Extract recent messages from history
|
|
310
|
+
*/
|
|
311
|
+
private extractRecentMessages(history: Event[], count = 5): string {
|
|
312
|
+
return history
|
|
313
|
+
.slice(-count)
|
|
314
|
+
.map((event) => {
|
|
315
|
+
if (event.kind === EventKind.MESSAGE) {
|
|
316
|
+
const data = event.data as MessageEventData;
|
|
317
|
+
return `${data.participant.display_name}: ${data.message}`;
|
|
318
|
+
}
|
|
319
|
+
return null;
|
|
320
|
+
})
|
|
321
|
+
.filter((msg): msg is string => msg !== null)
|
|
322
|
+
.join("\n");
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Parse guideline evaluation from text response (fallback)
|
|
327
|
+
*/
|
|
328
|
+
private parseGuidelineResponse(message: string): GuidelineEvaluationResult {
|
|
329
|
+
const lowerMessage = message.toLowerCase();
|
|
330
|
+
const matches =
|
|
331
|
+
lowerMessage.includes("true") || lowerMessage.includes('"matches": true');
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
matches,
|
|
335
|
+
rationale: matches ? "Parsed from text response" : "Condition not met",
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Parse transition evaluation from text response (fallback)
|
|
341
|
+
*/
|
|
342
|
+
private parseTransitionResponse(message: string): TransitionEvaluationResult {
|
|
343
|
+
const lowerMessage = message.toLowerCase();
|
|
344
|
+
const shouldFollow =
|
|
345
|
+
lowerMessage.includes("true") ||
|
|
346
|
+
lowerMessage.includes('"shouldfollow": true');
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
shouldFollow,
|
|
350
|
+
rationale: shouldFollow
|
|
351
|
+
? "Parsed from text response"
|
|
352
|
+
: "Condition not met",
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Parse argument extraction from text response (fallback)
|
|
358
|
+
*/
|
|
359
|
+
private parseArgumentResponse(message: string): ArgumentExtractionResult {
|
|
360
|
+
try {
|
|
361
|
+
// Try to extract JSON from the message
|
|
362
|
+
const jsonMatch = message.match(/\{[\s\S]*\}/);
|
|
363
|
+
if (jsonMatch) {
|
|
364
|
+
const parsed = JSON.parse(jsonMatch[0]) as ArgumentExtractionSchema;
|
|
365
|
+
if (parsed.arguments && Array.isArray(parsed.arguments)) {
|
|
366
|
+
return {
|
|
367
|
+
arguments: parsed.arguments,
|
|
368
|
+
rationale: parsed.rationale,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
} catch {
|
|
373
|
+
// Ignore parse errors
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
arguments: [],
|
|
378
|
+
rationale: "Failed to parse arguments from response",
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|