@axlsdk/axl 0.7.5 → 0.8.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 +29 -6
- package/dist/index.cjs +267 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -3
- package/dist/index.d.ts +48 -3
- package/dist/index.js +266 -92
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -165,29 +165,32 @@ async function fetchWithRetry(input, init, maxRetries = MAX_RETRIES) {
|
|
|
165
165
|
|
|
166
166
|
// src/providers/openai.ts
|
|
167
167
|
var OPENAI_PRICING = {
|
|
168
|
-
|
|
169
|
-
"gpt-4o
|
|
170
|
-
"gpt-
|
|
171
|
-
"gpt-4": [
|
|
172
|
-
"gpt-
|
|
173
|
-
"gpt-5": [
|
|
174
|
-
|
|
175
|
-
"
|
|
176
|
-
"
|
|
177
|
-
|
|
178
|
-
"gpt-
|
|
179
|
-
"gpt-
|
|
180
|
-
"gpt-
|
|
181
|
-
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
"
|
|
187
|
-
"
|
|
188
|
-
"gpt-
|
|
189
|
-
"gpt-
|
|
190
|
-
"gpt-
|
|
168
|
+
// gpt-4o era — cache reads at 50% of input rate
|
|
169
|
+
"gpt-4o": [25e-7, 1e-5, 0.5],
|
|
170
|
+
"gpt-4o-mini": [15e-8, 6e-7, 0.5],
|
|
171
|
+
"gpt-4-turbo": [1e-5, 3e-5, 0.5],
|
|
172
|
+
"gpt-4": [3e-5, 6e-5, 0.5],
|
|
173
|
+
"gpt-3.5-turbo": [5e-7, 15e-7, 0.5],
|
|
174
|
+
o1: [15e-6, 6e-5, 0.5],
|
|
175
|
+
"o1-mini": [3e-6, 12e-6, 0.5],
|
|
176
|
+
"o1-pro": [15e-5, 6e-4, 0.5],
|
|
177
|
+
// gpt-4.1 / o3 / o4 era — cache reads at 25% of input rate
|
|
178
|
+
"gpt-4.1": [2e-6, 8e-6, 0.25],
|
|
179
|
+
"gpt-4.1-mini": [4e-7, 16e-7, 0.25],
|
|
180
|
+
"gpt-4.1-nano": [1e-7, 4e-7, 0.25],
|
|
181
|
+
o3: [1e-5, 4e-5, 0.25],
|
|
182
|
+
"o3-mini": [11e-7, 44e-7, 0.25],
|
|
183
|
+
"o3-pro": [2e-5, 8e-5, 0.25],
|
|
184
|
+
"o4-mini": [11e-7, 44e-7, 0.25],
|
|
185
|
+
// gpt-5 era — cache reads at 10% of input rate
|
|
186
|
+
"gpt-5": [125e-8, 1e-5, 0.1],
|
|
187
|
+
"gpt-5-mini": [25e-8, 2e-6, 0.1],
|
|
188
|
+
"gpt-5-nano": [5e-8, 4e-7, 0.1],
|
|
189
|
+
"gpt-5.1": [125e-8, 1e-5, 0.1],
|
|
190
|
+
"gpt-5.2": [175e-8, 14e-6, 0.1],
|
|
191
|
+
"gpt-5.3": [175e-8, 14e-6, 0.1],
|
|
192
|
+
"gpt-5.4": [25e-7, 15e-6, 0.1],
|
|
193
|
+
"gpt-5.4-pro": [3e-5, 18e-5, 0.1]
|
|
191
194
|
};
|
|
192
195
|
var PRICING_KEYS_BY_LENGTH = Object.keys(OPENAI_PRICING).sort((a, b) => b.length - a.length);
|
|
193
196
|
function estimateOpenAICost(model, promptTokens, completionTokens, cachedTokens) {
|
|
@@ -201,9 +204,9 @@ function estimateOpenAICost(model, promptTokens, completionTokens, cachedTokens)
|
|
|
201
204
|
}
|
|
202
205
|
}
|
|
203
206
|
if (!pricing) return 0;
|
|
204
|
-
const [inputRate, outputRate] = pricing;
|
|
207
|
+
const [inputRate, outputRate, cacheMultiplier] = pricing;
|
|
205
208
|
const cached = cachedTokens ?? 0;
|
|
206
|
-
const inputCost = (promptTokens - cached) * inputRate + cached * inputRate *
|
|
209
|
+
const inputCost = (promptTokens - cached) * inputRate + cached * inputRate * cacheMultiplier;
|
|
207
210
|
return inputCost + completionTokens * outputRate;
|
|
208
211
|
}
|
|
209
212
|
function isOSeriesModel(model) {
|
|
@@ -313,7 +316,7 @@ var OpenAIProvider = class {
|
|
|
313
316
|
if (!res.body) {
|
|
314
317
|
throw new Error("OpenAI stream response has no body");
|
|
315
318
|
}
|
|
316
|
-
yield* this.parseSSEStream(res.body);
|
|
319
|
+
yield* this.parseSSEStream(res.body, options.model);
|
|
317
320
|
}
|
|
318
321
|
// ---------------------------------------------------------------------------
|
|
319
322
|
// Internal helpers
|
|
@@ -387,7 +390,7 @@ var OpenAIProvider = class {
|
|
|
387
390
|
if (msg.tool_call_id) out.tool_call_id = msg.tool_call_id;
|
|
388
391
|
return out;
|
|
389
392
|
}
|
|
390
|
-
async *parseSSEStream(body) {
|
|
393
|
+
async *parseSSEStream(body, model) {
|
|
391
394
|
const reader = body.getReader();
|
|
392
395
|
const decoder = new TextDecoder();
|
|
393
396
|
let buffer = "";
|
|
@@ -404,7 +407,16 @@ var OpenAIProvider = class {
|
|
|
404
407
|
const trimmed = line.trim();
|
|
405
408
|
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
406
409
|
if (trimmed === "data: [DONE]") {
|
|
407
|
-
yield {
|
|
410
|
+
yield {
|
|
411
|
+
type: "done",
|
|
412
|
+
usage: usageData,
|
|
413
|
+
cost: usageData ? estimateOpenAICost(
|
|
414
|
+
model,
|
|
415
|
+
usageData.prompt_tokens,
|
|
416
|
+
usageData.completion_tokens,
|
|
417
|
+
usageData.cached_tokens
|
|
418
|
+
) : void 0
|
|
419
|
+
};
|
|
408
420
|
return;
|
|
409
421
|
}
|
|
410
422
|
if (trimmed.startsWith("data: ")) {
|
|
@@ -447,7 +459,16 @@ var OpenAIProvider = class {
|
|
|
447
459
|
}
|
|
448
460
|
}
|
|
449
461
|
}
|
|
450
|
-
yield {
|
|
462
|
+
yield {
|
|
463
|
+
type: "done",
|
|
464
|
+
usage: usageData,
|
|
465
|
+
cost: usageData ? estimateOpenAICost(
|
|
466
|
+
model,
|
|
467
|
+
usageData.prompt_tokens,
|
|
468
|
+
usageData.completion_tokens,
|
|
469
|
+
usageData.cached_tokens
|
|
470
|
+
) : void 0
|
|
471
|
+
};
|
|
451
472
|
} finally {
|
|
452
473
|
reader.releaseLock();
|
|
453
474
|
}
|
|
@@ -705,6 +726,7 @@ var OpenAIResponsesProvider = class {
|
|
|
705
726
|
const decoder = new TextDecoder();
|
|
706
727
|
let buffer = "";
|
|
707
728
|
const callIdMap = /* @__PURE__ */ new Map();
|
|
729
|
+
let eventType = "";
|
|
708
730
|
try {
|
|
709
731
|
while (true) {
|
|
710
732
|
const { done, value } = await reader.read();
|
|
@@ -712,7 +734,6 @@ var OpenAIResponsesProvider = class {
|
|
|
712
734
|
buffer += decoder.decode(value, { stream: true });
|
|
713
735
|
const lines = buffer.split("\n");
|
|
714
736
|
buffer = lines.pop() ?? "";
|
|
715
|
-
let eventType = "";
|
|
716
737
|
for (const line of lines) {
|
|
717
738
|
const trimmed = line.trim();
|
|
718
739
|
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
@@ -780,7 +801,17 @@ var OpenAIResponsesProvider = class {
|
|
|
780
801
|
} : void 0;
|
|
781
802
|
const reasoningItems = response?.output?.filter((item) => item.type === "reasoning") ?? [];
|
|
782
803
|
const providerMetadata = reasoningItems.length > 0 ? { openaiReasoningItems: reasoningItems } : void 0;
|
|
783
|
-
return {
|
|
804
|
+
return {
|
|
805
|
+
type: "done",
|
|
806
|
+
usage,
|
|
807
|
+
cost: usage ? estimateOpenAICost(
|
|
808
|
+
model,
|
|
809
|
+
usage.prompt_tokens,
|
|
810
|
+
usage.completion_tokens,
|
|
811
|
+
usage.cached_tokens
|
|
812
|
+
) : void 0,
|
|
813
|
+
providerMetadata
|
|
814
|
+
};
|
|
784
815
|
}
|
|
785
816
|
case "response.failed": {
|
|
786
817
|
const errorMsg = data.response?.error?.message ?? data.response?.status_details?.error?.message ?? "Unknown error";
|
|
@@ -913,7 +944,7 @@ var AnthropicProvider = class {
|
|
|
913
944
|
if (!res.body) {
|
|
914
945
|
throw new Error("Anthropic stream response has no body");
|
|
915
946
|
}
|
|
916
|
-
yield* this.parseSSEStream(res.body);
|
|
947
|
+
yield* this.parseSSEStream(res.body, options.model);
|
|
917
948
|
}
|
|
918
949
|
// ---------------------------------------------------------------------------
|
|
919
950
|
// Internal: request building
|
|
@@ -1156,13 +1187,14 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
1156
1187
|
// ---------------------------------------------------------------------------
|
|
1157
1188
|
// Internal: SSE stream parsing
|
|
1158
1189
|
// ---------------------------------------------------------------------------
|
|
1159
|
-
async *parseSSEStream(body) {
|
|
1190
|
+
async *parseSSEStream(body, model) {
|
|
1160
1191
|
const reader = body.getReader();
|
|
1161
1192
|
const decoder = new TextDecoder();
|
|
1162
1193
|
let buffer = "";
|
|
1163
1194
|
let currentToolId = "";
|
|
1164
1195
|
let currentToolName = "";
|
|
1165
1196
|
let usage;
|
|
1197
|
+
let cacheWrite = 0;
|
|
1166
1198
|
try {
|
|
1167
1199
|
while (true) {
|
|
1168
1200
|
const { done, value } = await reader.read();
|
|
@@ -1219,7 +1251,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
1219
1251
|
case "message_start": {
|
|
1220
1252
|
if (event.message?.usage) {
|
|
1221
1253
|
const cacheRead = event.message.usage.cache_read_input_tokens ?? 0;
|
|
1222
|
-
|
|
1254
|
+
cacheWrite = event.message.usage.cache_creation_input_tokens ?? 0;
|
|
1223
1255
|
const inputTokens = (event.message.usage.input_tokens ?? 0) + cacheRead + cacheWrite;
|
|
1224
1256
|
usage = {
|
|
1225
1257
|
prompt_tokens: inputTokens,
|
|
@@ -1250,13 +1282,33 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
1250
1282
|
if (usage) {
|
|
1251
1283
|
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
|
|
1252
1284
|
}
|
|
1253
|
-
yield {
|
|
1285
|
+
yield {
|
|
1286
|
+
type: "done",
|
|
1287
|
+
usage,
|
|
1288
|
+
cost: usage ? estimateAnthropicCost(
|
|
1289
|
+
model,
|
|
1290
|
+
usage.prompt_tokens,
|
|
1291
|
+
usage.completion_tokens,
|
|
1292
|
+
usage.cached_tokens,
|
|
1293
|
+
cacheWrite
|
|
1294
|
+
) : void 0
|
|
1295
|
+
};
|
|
1254
1296
|
return;
|
|
1255
1297
|
}
|
|
1256
1298
|
}
|
|
1257
1299
|
}
|
|
1258
1300
|
}
|
|
1259
|
-
yield {
|
|
1301
|
+
yield {
|
|
1302
|
+
type: "done",
|
|
1303
|
+
usage,
|
|
1304
|
+
cost: usage ? estimateAnthropicCost(
|
|
1305
|
+
model,
|
|
1306
|
+
usage.prompt_tokens,
|
|
1307
|
+
usage.completion_tokens,
|
|
1308
|
+
usage.cached_tokens,
|
|
1309
|
+
cacheWrite
|
|
1310
|
+
) : void 0
|
|
1311
|
+
};
|
|
1260
1312
|
} finally {
|
|
1261
1313
|
reader.releaseLock();
|
|
1262
1314
|
}
|
|
@@ -1383,7 +1435,7 @@ var GeminiProvider = class {
|
|
|
1383
1435
|
if (!res.body) {
|
|
1384
1436
|
throw new Error("Gemini stream response has no body");
|
|
1385
1437
|
}
|
|
1386
|
-
yield* this.parseSSEStream(res.body);
|
|
1438
|
+
yield* this.parseSSEStream(res.body, options.model);
|
|
1387
1439
|
}
|
|
1388
1440
|
// ---------------------------------------------------------------------------
|
|
1389
1441
|
// Internal: request building
|
|
@@ -1664,7 +1716,7 @@ var GeminiProvider = class {
|
|
|
1664
1716
|
// ---------------------------------------------------------------------------
|
|
1665
1717
|
// Internal: SSE stream parsing
|
|
1666
1718
|
// ---------------------------------------------------------------------------
|
|
1667
|
-
async *parseSSEStream(body) {
|
|
1719
|
+
async *parseSSEStream(body, model) {
|
|
1668
1720
|
const reader = body.getReader();
|
|
1669
1721
|
const decoder = new TextDecoder();
|
|
1670
1722
|
let buffer = "";
|
|
@@ -1720,7 +1772,17 @@ var GeminiProvider = class {
|
|
|
1720
1772
|
}
|
|
1721
1773
|
}
|
|
1722
1774
|
const providerMetadata = accumulatedParts.length > 0 ? { geminiParts: accumulatedParts } : void 0;
|
|
1723
|
-
yield {
|
|
1775
|
+
yield {
|
|
1776
|
+
type: "done",
|
|
1777
|
+
usage,
|
|
1778
|
+
cost: usage ? estimateGeminiCost(
|
|
1779
|
+
model,
|
|
1780
|
+
usage.prompt_tokens,
|
|
1781
|
+
usage.completion_tokens,
|
|
1782
|
+
usage.cached_tokens
|
|
1783
|
+
) : void 0,
|
|
1784
|
+
providerMetadata
|
|
1785
|
+
};
|
|
1724
1786
|
} finally {
|
|
1725
1787
|
reader.releaseLock();
|
|
1726
1788
|
}
|
|
@@ -1936,6 +1998,18 @@ var GuardrailError = class extends AxlError {
|
|
|
1936
1998
|
this.reason = reason;
|
|
1937
1999
|
}
|
|
1938
2000
|
};
|
|
2001
|
+
var ValidationError = class extends AxlError {
|
|
2002
|
+
lastOutput;
|
|
2003
|
+
reason;
|
|
2004
|
+
retries;
|
|
2005
|
+
constructor(lastOutput, reason, retries) {
|
|
2006
|
+
super("VALIDATION_ERROR", `Validation failed after ${retries} retries: ${reason}`);
|
|
2007
|
+
this.name = "ValidationError";
|
|
2008
|
+
this.lastOutput = lastOutput;
|
|
2009
|
+
this.reason = reason;
|
|
2010
|
+
this.retries = retries;
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
1939
2013
|
var ToolDenied = class extends AxlError {
|
|
1940
2014
|
toolName;
|
|
1941
2015
|
agentName;
|
|
@@ -2200,9 +2274,6 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
2200
2274
|
agent2,
|
|
2201
2275
|
prompt,
|
|
2202
2276
|
options,
|
|
2203
|
-
0,
|
|
2204
|
-
void 0,
|
|
2205
|
-
void 0,
|
|
2206
2277
|
void 0,
|
|
2207
2278
|
usageCapture
|
|
2208
2279
|
);
|
|
@@ -2252,7 +2323,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
2252
2323
|
return result;
|
|
2253
2324
|
});
|
|
2254
2325
|
}
|
|
2255
|
-
async executeAgentCall(agent2, prompt, options,
|
|
2326
|
+
async executeAgentCall(agent2, prompt, options, handoffMessages, usageCapture) {
|
|
2256
2327
|
if (this.budgetContext?.exceeded) {
|
|
2257
2328
|
const { limit, totalCost: spent, policy } = this.budgetContext;
|
|
2258
2329
|
if (policy === "warn") {
|
|
@@ -2324,16 +2395,6 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
2324
2395
|
|
|
2325
2396
|
Respond with valid JSON matching this schema:
|
|
2326
2397
|
${JSON.stringify(jsonSchema, null, 2)}`;
|
|
2327
|
-
}
|
|
2328
|
-
if (previousOutput && previousError) {
|
|
2329
|
-
userContent += `
|
|
2330
|
-
|
|
2331
|
-
Your previous response was invalid:
|
|
2332
|
-
${previousOutput}
|
|
2333
|
-
|
|
2334
|
-
Error: ${previousError}
|
|
2335
|
-
|
|
2336
|
-
Please fix and try again.`;
|
|
2337
2398
|
}
|
|
2338
2399
|
messages.push({ role: "user", content: userContent });
|
|
2339
2400
|
if (handoffMessages && handoffMessages.length > 0) {
|
|
@@ -2378,9 +2439,17 @@ Please fix and try again.`;
|
|
|
2378
2439
|
const maxTurns = agent2._config.maxTurns ?? 25;
|
|
2379
2440
|
const timeoutMs = parseDuration(agent2._config.timeout ?? "60s");
|
|
2380
2441
|
const startTime = Date.now();
|
|
2442
|
+
if (this.onToken && options?.validate) {
|
|
2443
|
+
throw new AxlError(
|
|
2444
|
+
"INVALID_CONFIG",
|
|
2445
|
+
"Cannot use validate with streaming. Validate requires schema (JSON output) which does not benefit from token streaming. Use a non-streaming call instead."
|
|
2446
|
+
);
|
|
2447
|
+
}
|
|
2381
2448
|
const currentMessages = [...messages];
|
|
2382
2449
|
let turns = 0;
|
|
2383
2450
|
let guardrailOutputRetries = 0;
|
|
2451
|
+
let schemaRetries = 0;
|
|
2452
|
+
let validateRetries = 0;
|
|
2384
2453
|
while (turns < maxTurns) {
|
|
2385
2454
|
if (Date.now() - startTime > timeoutMs) {
|
|
2386
2455
|
throw new TimeoutError("ctx.ask()", timeoutMs);
|
|
@@ -2430,7 +2499,8 @@ Please fix and try again.`;
|
|
|
2430
2499
|
response = {
|
|
2431
2500
|
content: content2,
|
|
2432
2501
|
tool_calls: void 0,
|
|
2433
|
-
usage: chunk.usage
|
|
2502
|
+
usage: chunk.usage,
|
|
2503
|
+
cost: chunk.cost
|
|
2434
2504
|
};
|
|
2435
2505
|
}
|
|
2436
2506
|
}
|
|
@@ -2507,14 +2577,17 @@ Please fix and try again.`;
|
|
|
2507
2577
|
}
|
|
2508
2578
|
}
|
|
2509
2579
|
const handoffStart = Date.now();
|
|
2510
|
-
const handoffOptions = options ? {
|
|
2580
|
+
const handoffOptions = options ? {
|
|
2581
|
+
schema: options.schema,
|
|
2582
|
+
retries: options.retries,
|
|
2583
|
+
metadata: options.metadata,
|
|
2584
|
+
validate: options.validate,
|
|
2585
|
+
validateRetries: options.validateRetries
|
|
2586
|
+
} : void 0;
|
|
2511
2587
|
const handoffFn = () => this.executeAgentCall(
|
|
2512
2588
|
descriptor.agent,
|
|
2513
2589
|
handoffPrompt,
|
|
2514
2590
|
handoffOptions,
|
|
2515
|
-
0,
|
|
2516
|
-
void 0,
|
|
2517
|
-
void 0,
|
|
2518
2591
|
currentMessages,
|
|
2519
2592
|
usageCapture
|
|
2520
2593
|
);
|
|
@@ -2848,26 +2921,26 @@ Please fix and try again.`;
|
|
|
2848
2921
|
throw new GuardrailError("output", outputResult.reason ?? "Output blocked by guardrail");
|
|
2849
2922
|
}
|
|
2850
2923
|
}
|
|
2924
|
+
let validated = void 0;
|
|
2851
2925
|
if (options?.schema) {
|
|
2852
2926
|
try {
|
|
2853
2927
|
const parsed = JSON.parse(stripMarkdownFences(content));
|
|
2854
|
-
|
|
2855
|
-
this.pushAssistantToSessionHistory(content, response.providerMetadata);
|
|
2856
|
-
return validated;
|
|
2928
|
+
validated = options.schema.parse(parsed);
|
|
2857
2929
|
} catch (err) {
|
|
2858
|
-
const
|
|
2859
|
-
if (
|
|
2930
|
+
const maxSchemaRetries = options.retries ?? 3;
|
|
2931
|
+
if (schemaRetries < maxSchemaRetries) {
|
|
2932
|
+
schemaRetries++;
|
|
2860
2933
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
prompt,
|
|
2864
|
-
options,
|
|
2865
|
-
retryCount + 1,
|
|
2934
|
+
currentMessages.push({
|
|
2935
|
+
role: "assistant",
|
|
2866
2936
|
content,
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2937
|
+
...response.providerMetadata ? { providerMetadata: response.providerMetadata } : {}
|
|
2938
|
+
});
|
|
2939
|
+
currentMessages.push({
|
|
2940
|
+
role: "system",
|
|
2941
|
+
content: `Your response was not valid JSON or did not match the required schema: ${errorMsg}. Please fix and try again.`
|
|
2942
|
+
});
|
|
2943
|
+
continue;
|
|
2871
2944
|
}
|
|
2872
2945
|
const zodErr = err instanceof ZodError ? err : new ZodError([
|
|
2873
2946
|
{
|
|
@@ -2876,11 +2949,55 @@ Please fix and try again.`;
|
|
|
2876
2949
|
message: err instanceof Error ? err.message : String(err)
|
|
2877
2950
|
}
|
|
2878
2951
|
]);
|
|
2879
|
-
throw new VerifyError(content, zodErr,
|
|
2952
|
+
throw new VerifyError(content, zodErr, maxSchemaRetries);
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
if (options?.schema && options.validate) {
|
|
2956
|
+
let validateResult;
|
|
2957
|
+
try {
|
|
2958
|
+
validateResult = await options.validate(validated, {
|
|
2959
|
+
metadata: this.metadata
|
|
2960
|
+
});
|
|
2961
|
+
} catch (err) {
|
|
2962
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
2963
|
+
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
2964
|
+
}
|
|
2965
|
+
this.emitTrace({
|
|
2966
|
+
type: "validate",
|
|
2967
|
+
agent: agent2._name,
|
|
2968
|
+
data: {
|
|
2969
|
+
valid: validateResult.valid,
|
|
2970
|
+
...validateResult.reason ? { reason: validateResult.reason } : {}
|
|
2971
|
+
}
|
|
2972
|
+
});
|
|
2973
|
+
this.spanManager?.addEventToActiveSpan("axl.validate.check", {
|
|
2974
|
+
"axl.validate.valid": validateResult.valid,
|
|
2975
|
+
...validateResult.reason ? { "axl.validate.reason": validateResult.reason } : {}
|
|
2976
|
+
});
|
|
2977
|
+
if (!validateResult.valid) {
|
|
2978
|
+
const maxValidateRetries = options.validateRetries ?? 2;
|
|
2979
|
+
if (validateRetries < maxValidateRetries) {
|
|
2980
|
+
validateRetries++;
|
|
2981
|
+
currentMessages.push({
|
|
2982
|
+
role: "assistant",
|
|
2983
|
+
content,
|
|
2984
|
+
...response.providerMetadata ? { providerMetadata: response.providerMetadata } : {}
|
|
2985
|
+
});
|
|
2986
|
+
currentMessages.push({
|
|
2987
|
+
role: "system",
|
|
2988
|
+
content: `Your response parsed correctly but failed validation: ${validateResult.reason ?? "Validation failed"}. Previous attempts are visible above. Please fix and try again.`
|
|
2989
|
+
});
|
|
2990
|
+
continue;
|
|
2991
|
+
}
|
|
2992
|
+
throw new ValidationError(
|
|
2993
|
+
validated,
|
|
2994
|
+
validateResult.reason ?? "Validation failed",
|
|
2995
|
+
maxValidateRetries
|
|
2996
|
+
);
|
|
2880
2997
|
}
|
|
2881
2998
|
}
|
|
2882
2999
|
this.pushAssistantToSessionHistory(content, response.providerMetadata);
|
|
2883
|
-
return content;
|
|
3000
|
+
return validated ?? content;
|
|
2884
3001
|
}
|
|
2885
3002
|
throw new MaxTurnsError("ctx.ask()", maxTurns);
|
|
2886
3003
|
}
|
|
@@ -3237,32 +3354,57 @@ ${summaryResponse.content}`
|
|
|
3237
3354
|
// ── ctx.verify() ──────────────────────────────────────────────────────
|
|
3238
3355
|
async verify(fn, schema, options) {
|
|
3239
3356
|
const maxRetries = options?.retries ?? 3;
|
|
3240
|
-
let
|
|
3241
|
-
let lastErrorMessage = void 0;
|
|
3357
|
+
let lastRetry = void 0;
|
|
3242
3358
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
3243
|
-
let
|
|
3359
|
+
let rawOutput;
|
|
3244
3360
|
try {
|
|
3245
|
-
result = await fn(
|
|
3246
|
-
|
|
3247
|
-
|
|
3361
|
+
const result = await fn(lastRetry);
|
|
3362
|
+
rawOutput = result;
|
|
3363
|
+
const parsed = schema.parse(result);
|
|
3364
|
+
if (options?.validate) {
|
|
3365
|
+
let validateResult;
|
|
3366
|
+
try {
|
|
3367
|
+
validateResult = await options.validate(parsed, { metadata: this.metadata });
|
|
3368
|
+
} catch (err) {
|
|
3369
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
3370
|
+
validateResult = { valid: false, reason: `Validator error: ${reason}` };
|
|
3371
|
+
}
|
|
3372
|
+
if (!validateResult.valid) {
|
|
3373
|
+
const errorMsg = validateResult.reason ?? "Validation failed";
|
|
3374
|
+
lastRetry = { error: errorMsg, output: rawOutput, parsed };
|
|
3375
|
+
if (attempt === maxRetries) {
|
|
3376
|
+
if (options?.fallback !== void 0) return options.fallback;
|
|
3377
|
+
throw new ValidationError(parsed, errorMsg, maxRetries);
|
|
3378
|
+
}
|
|
3379
|
+
continue;
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
return parsed;
|
|
3248
3383
|
} catch (err) {
|
|
3249
|
-
if (err instanceof
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3384
|
+
if (err instanceof ValidationError) {
|
|
3385
|
+
lastRetry = {
|
|
3386
|
+
error: err.reason,
|
|
3387
|
+
output: rawOutput,
|
|
3388
|
+
parsed: err.lastOutput
|
|
3389
|
+
};
|
|
3390
|
+
if (attempt === maxRetries) {
|
|
3391
|
+
if (options?.fallback !== void 0) return options.fallback;
|
|
3392
|
+
throw err;
|
|
3393
|
+
}
|
|
3394
|
+
continue;
|
|
3255
3395
|
}
|
|
3396
|
+
const errorMsg = err instanceof ZodError ? err.message : err instanceof Error ? err.message : String(err);
|
|
3397
|
+
lastRetry = { error: errorMsg, output: rawOutput };
|
|
3256
3398
|
if (attempt === maxRetries) {
|
|
3257
3399
|
if (options?.fallback !== void 0) return options.fallback;
|
|
3258
|
-
const zodErr = err instanceof ZodError ? err : new ZodError([{ code: "custom", path: [], message:
|
|
3259
|
-
throw new VerifyError(
|
|
3400
|
+
const zodErr = err instanceof ZodError ? err : new ZodError([{ code: "custom", path: [], message: errorMsg }]);
|
|
3401
|
+
throw new VerifyError(rawOutput, zodErr, maxRetries);
|
|
3260
3402
|
}
|
|
3261
3403
|
}
|
|
3262
3404
|
}
|
|
3263
3405
|
if (options?.fallback !== void 0) return options.fallback;
|
|
3264
3406
|
throw new VerifyError(
|
|
3265
|
-
|
|
3407
|
+
lastRetry?.output,
|
|
3266
3408
|
new ZodError([{ code: "custom", path: [], message: "Verify failed" }]),
|
|
3267
3409
|
maxRetries
|
|
3268
3410
|
);
|
|
@@ -3364,7 +3506,7 @@ ${summaryResponse.content}`
|
|
|
3364
3506
|
let remaining = fns.length;
|
|
3365
3507
|
for (const fn of fns) {
|
|
3366
3508
|
const p = signalStorage.run(composedSignal, fn);
|
|
3367
|
-
p.then((value) => {
|
|
3509
|
+
p.then(async (value) => {
|
|
3368
3510
|
if (settled) return;
|
|
3369
3511
|
if (schema) {
|
|
3370
3512
|
const parsed = schema.safeParse(value);
|
|
@@ -3377,6 +3519,33 @@ ${summaryResponse.content}`
|
|
|
3377
3519
|
}
|
|
3378
3520
|
return;
|
|
3379
3521
|
}
|
|
3522
|
+
if (options?.validate) {
|
|
3523
|
+
try {
|
|
3524
|
+
const validateResult = await options.validate(parsed.data, {
|
|
3525
|
+
metadata: this.metadata
|
|
3526
|
+
});
|
|
3527
|
+
if (!validateResult.valid) {
|
|
3528
|
+
remaining--;
|
|
3529
|
+
lastError = new Error(
|
|
3530
|
+
`Validation failed: ${validateResult.reason ?? "Validation failed"}`
|
|
3531
|
+
);
|
|
3532
|
+
if (remaining === 0 && !settled) {
|
|
3533
|
+
settled = true;
|
|
3534
|
+
reject(lastError);
|
|
3535
|
+
}
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
} catch (err) {
|
|
3539
|
+
remaining--;
|
|
3540
|
+
lastError = err instanceof Error ? err : new Error(`Validator error: ${String(err)}`);
|
|
3541
|
+
if (remaining === 0 && !settled) {
|
|
3542
|
+
settled = true;
|
|
3543
|
+
reject(lastError);
|
|
3544
|
+
}
|
|
3545
|
+
return;
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
if (settled) return;
|
|
3380
3549
|
settled = true;
|
|
3381
3550
|
controller.abort();
|
|
3382
3551
|
resolve(parsed.data);
|
|
@@ -3628,7 +3797,9 @@ ${summaryResponse.content}`
|
|
|
3628
3797
|
return this.ask(agents[0], prompt, {
|
|
3629
3798
|
schema: options?.schema,
|
|
3630
3799
|
retries: options?.retries,
|
|
3631
|
-
metadata: options?.metadata
|
|
3800
|
+
metadata: options?.metadata,
|
|
3801
|
+
validate: options?.validate,
|
|
3802
|
+
validateRetries: options?.validateRetries
|
|
3632
3803
|
});
|
|
3633
3804
|
}
|
|
3634
3805
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
@@ -3669,7 +3840,9 @@ ${summaryResponse.content}`
|
|
|
3669
3840
|
return this.ask(routerAgent, prompt, {
|
|
3670
3841
|
schema: options?.schema,
|
|
3671
3842
|
retries: options?.retries,
|
|
3672
|
-
metadata: options?.metadata
|
|
3843
|
+
metadata: options?.metadata,
|
|
3844
|
+
validate: options?.validate,
|
|
3845
|
+
validateRetries: options?.validateRetries
|
|
3673
3846
|
});
|
|
3674
3847
|
}
|
|
3675
3848
|
// ── Private ───────────────────────────────────────────────────────────
|
|
@@ -5949,6 +6122,7 @@ export {
|
|
|
5949
6122
|
SqliteVectorStore,
|
|
5950
6123
|
TimeoutError,
|
|
5951
6124
|
ToolDenied,
|
|
6125
|
+
ValidationError,
|
|
5952
6126
|
VerifyError,
|
|
5953
6127
|
WorkflowContext,
|
|
5954
6128
|
agent,
|