@axlsdk/axl 0.18.2 → 0.19.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 +18 -2
- package/dist/index.cjs +1947 -522
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +815 -42
- package/dist/index.d.ts +815 -42
- package/dist/index.js +1916 -522
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -111,57 +111,88 @@ var init_span_manager = __esm({
|
|
|
111
111
|
var index_exports = {};
|
|
112
112
|
__export(index_exports, {
|
|
113
113
|
AXL_EVENT_TYPES: () => AXL_EVENT_TYPES,
|
|
114
|
+
AZURE_PROFILE: () => AZURE_PROFILE,
|
|
114
115
|
AnthropicProvider: () => AnthropicProvider,
|
|
115
116
|
AxlError: () => AxlError,
|
|
116
117
|
AxlEventBus: () => AxlEventBus,
|
|
117
118
|
AxlRuntime: () => AxlRuntime,
|
|
118
119
|
AxlStream: () => AxlStream,
|
|
120
|
+
BEDROCK_PROFILE: () => BEDROCK_PROFILE,
|
|
121
|
+
BUILTIN_PROFILES: () => BUILTIN_PROFILES,
|
|
119
122
|
BudgetExceededError: () => BudgetExceededError,
|
|
120
123
|
CONFIG_CANDIDATES: () => CONFIG_CANDIDATES,
|
|
121
124
|
COST_BEARING_LEAF_TYPES: () => COST_BEARING_LEAF_TYPES,
|
|
125
|
+
DEEPSEEK_PROFILE: () => DEEPSEEK_PROFILE,
|
|
122
126
|
EventStreamOverflowError: () => EventStreamOverflowError,
|
|
127
|
+
GROQ_PROFILE: () => GROQ_PROFILE,
|
|
123
128
|
GeminiProvider: () => GeminiProvider,
|
|
124
129
|
GuardrailError: () => GuardrailError,
|
|
125
130
|
InMemoryVectorStore: () => InMemoryVectorStore,
|
|
131
|
+
LLAMACPP_PROFILE: () => LLAMACPP_PROFILE,
|
|
132
|
+
LMSTUDIO_PROFILE: () => LMSTUDIO_PROFILE,
|
|
133
|
+
MISTRAL_PROFILE: () => MISTRAL_PROFILE,
|
|
126
134
|
MaxTurnsError: () => MaxTurnsError,
|
|
127
135
|
McpManager: () => McpManager,
|
|
128
136
|
MemoryManager: () => MemoryManager,
|
|
129
137
|
MemoryStore: () => MemoryStore,
|
|
130
138
|
NoConsensus: () => NoConsensus,
|
|
131
139
|
NoopSpanManager: () => NoopSpanManager,
|
|
140
|
+
OLLAMA_PROFILE: () => OLLAMA_PROFILE,
|
|
141
|
+
OPENAI_PRICING: () => OPENAI_PRICING,
|
|
142
|
+
OPENAI_PROFILE: () => OPENAI_PROFILE,
|
|
143
|
+
OPENROUTER_PROFILE: () => OPENROUTER_PROFILE,
|
|
144
|
+
OpenAICompatibleProvider: () => OpenAICompatibleProvider,
|
|
132
145
|
OpenAIEmbedder: () => OpenAIEmbedder,
|
|
133
146
|
OpenAIProvider: () => OpenAIProvider,
|
|
134
147
|
OpenAIResponsesProvider: () => OpenAIResponsesProvider,
|
|
148
|
+
ProviderError: () => ProviderError,
|
|
135
149
|
ProviderRegistry: () => ProviderRegistry,
|
|
136
150
|
QuorumNotMet: () => QuorumNotMet,
|
|
137
151
|
REDACTED: () => REDACTED,
|
|
138
152
|
REDACTION_RULES: () => REDACTION_RULES,
|
|
139
153
|
RateLimiter: () => RateLimiter,
|
|
140
154
|
RedisStore: () => RedisStore,
|
|
155
|
+
SGLANG_PROFILE: () => SGLANG_PROFILE,
|
|
141
156
|
SQLiteStore: () => SQLiteStore,
|
|
142
157
|
Session: () => Session,
|
|
143
158
|
SqliteVectorStore: () => SqliteVectorStore,
|
|
159
|
+
ThinkTagScanner: () => ThinkTagScanner,
|
|
144
160
|
TimeoutError: () => TimeoutError,
|
|
145
161
|
ToolDenied: () => ToolDenied,
|
|
162
|
+
ToolModelOutputError: () => ToolModelOutputError,
|
|
163
|
+
VLLM_PROFILE: () => VLLM_PROFILE,
|
|
146
164
|
ValidationError: () => ValidationError,
|
|
147
165
|
VerifyError: () => VerifyError,
|
|
148
166
|
WorkflowContext: () => WorkflowContext,
|
|
167
|
+
XAI_PROFILE: () => XAI_PROFILE,
|
|
149
168
|
agent: () => agent,
|
|
150
169
|
createSpanManager: () => createSpanManager,
|
|
151
170
|
defineConfig: () => defineConfig,
|
|
171
|
+
estimateOpenAICost: () => estimateOpenAICost,
|
|
152
172
|
eventCostContribution: () => eventCostContribution,
|
|
153
173
|
expandGlob: () => expandGlob,
|
|
154
174
|
extractJson: () => extractJson,
|
|
175
|
+
extractThinkTags: () => extractThinkTags,
|
|
155
176
|
findConfig: () => findConfig,
|
|
177
|
+
hasPositiveTokens: () => hasPositiveTokens,
|
|
156
178
|
importModule: () => importModule,
|
|
157
179
|
isCostBearingLeaf: () => isCostBearingLeaf,
|
|
180
|
+
isRetryableStatus: () => isRetryableStatus,
|
|
158
181
|
isRootLevel: () => isRootLevel,
|
|
182
|
+
isUnpricedLeaf: () => isUnpricedLeaf,
|
|
183
|
+
isUsableCost: () => isUsableCost,
|
|
159
184
|
needsTsxLoader: () => needsTsxLoader,
|
|
185
|
+
openaiReasoningEmit: () => openaiReasoningEmit,
|
|
160
186
|
parsePartialJson: () => parsePartialJson,
|
|
161
187
|
pickDefault: () => pickDefault,
|
|
162
188
|
pickExport: () => pickExport,
|
|
189
|
+
priceFromTable: () => priceFromTable,
|
|
190
|
+
reasoningEffortEmit: () => reasoningEffortEmit,
|
|
191
|
+
reasoningObjectEmit: () => reasoningObjectEmit,
|
|
163
192
|
redactEvent: () => redactEvent,
|
|
164
193
|
registerConditions: () => registerConditions,
|
|
194
|
+
resolveApiKey: () => resolveApiKey,
|
|
195
|
+
resolvePerModel: () => resolvePerModel,
|
|
165
196
|
resolveRuntime: () => resolveRuntime,
|
|
166
197
|
resolveThinkingOptions: () => resolveThinkingOptions,
|
|
167
198
|
stringStreamFromEvents: () => stringStreamFromEvents,
|
|
@@ -242,6 +273,7 @@ function tool(config) {
|
|
|
242
273
|
retry: retryPolicy,
|
|
243
274
|
requireApproval: config.requireApproval ?? false,
|
|
244
275
|
hooks: config.hooks,
|
|
276
|
+
...config.toModelOutput !== void 0 ? { toModelOutput: config.toModelOutput } : {},
|
|
245
277
|
async run(ctx, input) {
|
|
246
278
|
const startTime = Date.now();
|
|
247
279
|
try {
|
|
@@ -289,11 +321,229 @@ function resolveThinkingOptions(options) {
|
|
|
289
321
|
hasBudgetOverride
|
|
290
322
|
};
|
|
291
323
|
}
|
|
324
|
+
async function resolveApiKey(source) {
|
|
325
|
+
if (typeof source === "function") return await source() ?? "";
|
|
326
|
+
return source ?? "";
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/errors.ts
|
|
330
|
+
var AxlError = class extends Error {
|
|
331
|
+
code;
|
|
332
|
+
constructor(code, message) {
|
|
333
|
+
super(message);
|
|
334
|
+
this.name = "AxlError";
|
|
335
|
+
this.code = code;
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
var ToolModelOutputError = class extends AxlError {
|
|
339
|
+
toolName;
|
|
340
|
+
cause;
|
|
341
|
+
constructor(toolName, cause) {
|
|
342
|
+
super("TOOL_MODEL_OUTPUT_ERROR", `Failed to prepare model output for tool "${toolName}"`);
|
|
343
|
+
this.name = "ToolModelOutputError";
|
|
344
|
+
this.toolName = toolName;
|
|
345
|
+
this.cause = cause;
|
|
346
|
+
Object.defineProperty(this, "cause", { enumerable: false });
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
var VerifyError = class extends AxlError {
|
|
350
|
+
lastOutput;
|
|
351
|
+
zodError;
|
|
352
|
+
retries;
|
|
353
|
+
constructor(lastOutput, zodError, retries) {
|
|
354
|
+
super("VERIFY_ERROR", `Schema validation failed after ${retries} retries: ${zodError.message}`);
|
|
355
|
+
this.name = "VerifyError";
|
|
356
|
+
this.lastOutput = lastOutput;
|
|
357
|
+
this.zodError = zodError;
|
|
358
|
+
this.retries = retries;
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
var QuorumNotMet = class extends AxlError {
|
|
362
|
+
results;
|
|
363
|
+
constructor(required, actual, results) {
|
|
364
|
+
super("QUORUM_NOT_MET", `Quorum not met: needed ${required} successes, got ${actual}`);
|
|
365
|
+
this.name = "QuorumNotMet";
|
|
366
|
+
this.results = results;
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
var NoConsensus = class extends AxlError {
|
|
370
|
+
constructor(reason) {
|
|
371
|
+
super("NO_CONSENSUS", `No consensus: ${reason}`);
|
|
372
|
+
this.name = "NoConsensus";
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
var TimeoutError = class extends AxlError {
|
|
376
|
+
constructor(operation, timeoutMs) {
|
|
377
|
+
super("TIMEOUT", `${operation} exceeded timeout of ${timeoutMs}ms`);
|
|
378
|
+
this.name = "TimeoutError";
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
function formatBudgetCost(cost) {
|
|
382
|
+
if (Number.isNaN(cost)) return "$NaN";
|
|
383
|
+
if (cost === Infinity) return "$Infinity";
|
|
384
|
+
if (cost === -Infinity) return "-$Infinity";
|
|
385
|
+
if (cost === 0) return "$0.00";
|
|
386
|
+
const sign = cost < 0 ? "-" : "";
|
|
387
|
+
const abs = Math.abs(cost);
|
|
388
|
+
if (abs < 1e-6) return `${sign}< $0.000001`;
|
|
389
|
+
if (abs < 1e-4) {
|
|
390
|
+
const [mantissa, exponent] = abs.toExponential(2).split("e");
|
|
391
|
+
return `${sign}$${mantissa}e${parseInt(exponent, 10)}`;
|
|
392
|
+
}
|
|
393
|
+
if (abs < 0.01) return `${sign}$${abs.toFixed(6)}`;
|
|
394
|
+
return `${sign}$${abs.toFixed(2)}`;
|
|
395
|
+
}
|
|
396
|
+
var BudgetExceededError = class extends AxlError {
|
|
397
|
+
limit;
|
|
398
|
+
spent;
|
|
399
|
+
policy;
|
|
400
|
+
constructor(limit, spent, policy) {
|
|
401
|
+
super(
|
|
402
|
+
"BUDGET_EXCEEDED",
|
|
403
|
+
`Budget exceeded: spent ${formatBudgetCost(spent)} of ${formatBudgetCost(
|
|
404
|
+
limit
|
|
405
|
+
)} limit (policy: ${policy})`
|
|
406
|
+
);
|
|
407
|
+
this.name = "BudgetExceededError";
|
|
408
|
+
this.limit = limit;
|
|
409
|
+
this.spent = spent;
|
|
410
|
+
this.policy = policy;
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
var MaxTurnsError = class extends AxlError {
|
|
414
|
+
maxTurns;
|
|
415
|
+
constructor(operation, maxTurns) {
|
|
416
|
+
super("MAX_TURNS", `${operation} exceeded maximum of ${maxTurns} turns`);
|
|
417
|
+
this.name = "MaxTurnsError";
|
|
418
|
+
this.maxTurns = maxTurns;
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
var GuardrailError = class extends AxlError {
|
|
422
|
+
guardrailType;
|
|
423
|
+
reason;
|
|
424
|
+
constructor(guardrailType, reason) {
|
|
425
|
+
super("GUARDRAIL_BLOCKED", `${guardrailType} guardrail blocked: ${reason}`);
|
|
426
|
+
this.name = "GuardrailError";
|
|
427
|
+
this.guardrailType = guardrailType;
|
|
428
|
+
this.reason = reason;
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
var ValidationError = class extends AxlError {
|
|
432
|
+
lastOutput;
|
|
433
|
+
reason;
|
|
434
|
+
retries;
|
|
435
|
+
constructor(lastOutput, reason, retries) {
|
|
436
|
+
super("VALIDATION_ERROR", `Validation failed after ${retries} retries: ${reason}`);
|
|
437
|
+
this.name = "ValidationError";
|
|
438
|
+
this.lastOutput = lastOutput;
|
|
439
|
+
this.reason = reason;
|
|
440
|
+
this.retries = retries;
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
var ToolDenied = class extends AxlError {
|
|
444
|
+
toolName;
|
|
445
|
+
agentName;
|
|
446
|
+
constructor(toolName, agentName) {
|
|
447
|
+
super(
|
|
448
|
+
"TOOL_DENIED",
|
|
449
|
+
`Agent "${agentName}" attempted to call tool "${toolName}" which is not in its ACL`
|
|
450
|
+
);
|
|
451
|
+
this.name = "ToolDenied";
|
|
452
|
+
this.toolName = toolName;
|
|
453
|
+
this.agentName = agentName;
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// src/providers/errors.ts
|
|
458
|
+
var ProviderError = class extends AxlError {
|
|
459
|
+
/** Adapter/profile name, e.g. `'openai'`, `'anthropic'`, `'google'`. */
|
|
460
|
+
provider;
|
|
461
|
+
/** HTTP status code; `0` for non-HTTP (network) failures. */
|
|
462
|
+
status;
|
|
463
|
+
/** Semantic failover hint (see module doc) — NOT the transport-retry set. */
|
|
464
|
+
retryable;
|
|
465
|
+
/** Parsed `Retry-After` delay in ms (RAW/unclamped), when the header was present. */
|
|
466
|
+
retryAfterMs;
|
|
467
|
+
/** Provider request id from response headers, when present. */
|
|
468
|
+
requestId;
|
|
469
|
+
/**
|
|
470
|
+
* Raw provider error body. Lives ONLY on the error object — it is
|
|
471
|
+
* intentionally never placed on the event stream (it can echo prompt text
|
|
472
|
+
* and is redaction-eligible). See `docs/security.md`.
|
|
473
|
+
*/
|
|
474
|
+
body;
|
|
475
|
+
constructor(args) {
|
|
476
|
+
super("PROVIDER_ERROR", args.message);
|
|
477
|
+
this.name = "ProviderError";
|
|
478
|
+
this.provider = args.provider;
|
|
479
|
+
this.status = args.status;
|
|
480
|
+
this.retryable = args.retryable;
|
|
481
|
+
this.retryAfterMs = args.retryAfterMs;
|
|
482
|
+
this.requestId = args.requestId;
|
|
483
|
+
this.body = args.body;
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
function isRetryableStatus(status) {
|
|
487
|
+
switch (status) {
|
|
488
|
+
case 0:
|
|
489
|
+
return true;
|
|
490
|
+
case 408:
|
|
491
|
+
// request timeout
|
|
492
|
+
case 429:
|
|
493
|
+
// rate limit
|
|
494
|
+
case 500:
|
|
495
|
+
case 502:
|
|
496
|
+
case 503:
|
|
497
|
+
case 504:
|
|
498
|
+
case 529:
|
|
499
|
+
return true;
|
|
500
|
+
// Explicitly non-retryable (bad request / auth / not found / payload):
|
|
501
|
+
// 400, 401, 403, 404, 413, 422 fall through to the default.
|
|
502
|
+
default:
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function parseRetryAfter(headers) {
|
|
507
|
+
const raw = headers.get("retry-after");
|
|
508
|
+
if (!raw) return void 0;
|
|
509
|
+
const trimmed = raw.trim();
|
|
510
|
+
if (/^\d+(\.\d+)?$/.test(trimmed)) {
|
|
511
|
+
const ms = Number(trimmed) * 1e3;
|
|
512
|
+
return ms > 0 ? ms : void 0;
|
|
513
|
+
}
|
|
514
|
+
const dateMs = Date.parse(trimmed);
|
|
515
|
+
if (Number.isNaN(dateMs)) return void 0;
|
|
516
|
+
const delta = dateMs - Date.now();
|
|
517
|
+
return delta > 0 ? delta : void 0;
|
|
518
|
+
}
|
|
519
|
+
var REQUEST_ID_HEADERS = ["x-request-id", "request-id"];
|
|
520
|
+
function extractRequestId(headers) {
|
|
521
|
+
for (const name of REQUEST_ID_HEADERS) {
|
|
522
|
+
const value = headers.get(name);
|
|
523
|
+
if (value) return value;
|
|
524
|
+
}
|
|
525
|
+
return void 0;
|
|
526
|
+
}
|
|
527
|
+
function buildProviderError(args) {
|
|
528
|
+
return new ProviderError({
|
|
529
|
+
provider: args.provider,
|
|
530
|
+
status: args.status,
|
|
531
|
+
retryable: isRetryableStatus(args.status),
|
|
532
|
+
message: args.message,
|
|
533
|
+
retryAfterMs: args.headers ? parseRetryAfter(args.headers) : void 0,
|
|
534
|
+
requestId: args.headers ? extractRequestId(args.headers) : void 0,
|
|
535
|
+
body: args.body
|
|
536
|
+
});
|
|
537
|
+
}
|
|
292
538
|
|
|
293
539
|
// src/providers/retry.ts
|
|
294
540
|
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 503, 529]);
|
|
295
541
|
var MAX_RETRIES = 2;
|
|
296
542
|
var BASE_DELAY_MS = 1e3;
|
|
543
|
+
var MAX_BACKOFF_MS = 6e4;
|
|
544
|
+
function jitter(ms) {
|
|
545
|
+
return ms * (0.75 + Math.random() * 0.5);
|
|
546
|
+
}
|
|
297
547
|
function sleep2(ms, signal) {
|
|
298
548
|
return new Promise((resolve2) => {
|
|
299
549
|
if (signal?.aborted) {
|
|
@@ -311,9 +561,14 @@ function sleep2(ms, signal) {
|
|
|
311
561
|
);
|
|
312
562
|
});
|
|
313
563
|
}
|
|
564
|
+
function isAbortError(err, signal) {
|
|
565
|
+
if (signal?.aborted) return true;
|
|
566
|
+
return typeof err === "object" && err !== null && "name" in err && err.name === "AbortError";
|
|
567
|
+
}
|
|
314
568
|
async function fetchWithRetry(input, init, opts) {
|
|
315
569
|
const maxRetries = opts?.maxRetries ?? MAX_RETRIES;
|
|
316
570
|
const governor = opts?.governor;
|
|
571
|
+
const provider = opts?.provider ?? "unknown";
|
|
317
572
|
let acquired = false;
|
|
318
573
|
if (governor) {
|
|
319
574
|
await governor.acquire(init?.signal ?? void 0);
|
|
@@ -321,7 +576,21 @@ async function fetchWithRetry(input, init, opts) {
|
|
|
321
576
|
}
|
|
322
577
|
try {
|
|
323
578
|
for (let attempt = 0; ; attempt++) {
|
|
324
|
-
|
|
579
|
+
let res;
|
|
580
|
+
try {
|
|
581
|
+
res = await fetch(input, init);
|
|
582
|
+
} catch (err) {
|
|
583
|
+
if (isAbortError(err, init?.signal ?? void 0)) throw err;
|
|
584
|
+
if (attempt >= maxRetries) {
|
|
585
|
+
throw buildProviderError({
|
|
586
|
+
provider,
|
|
587
|
+
status: 0,
|
|
588
|
+
message: err instanceof Error ? err.message : String(err)
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
await sleep2(jitter(BASE_DELAY_MS * 2 ** attempt), init?.signal ?? void 0);
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
325
594
|
governor?.observe(res);
|
|
326
595
|
if (res.ok || !RETRYABLE_STATUS_CODES.has(res.status) || attempt >= maxRetries) {
|
|
327
596
|
return res;
|
|
@@ -329,15 +598,9 @@ async function fetchWithRetry(input, init, opts) {
|
|
|
329
598
|
if (init?.signal?.aborted) {
|
|
330
599
|
return res;
|
|
331
600
|
}
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
delay = Number(retryAfter) * 1e3;
|
|
336
|
-
} else {
|
|
337
|
-
delay = BASE_DELAY_MS * 2 ** attempt;
|
|
338
|
-
}
|
|
339
|
-
delay *= 0.75 + Math.random() * 0.5;
|
|
340
|
-
await sleep2(delay, init?.signal ?? void 0);
|
|
601
|
+
const retryAfterMs = parseRetryAfter(res.headers);
|
|
602
|
+
const baseDelay = retryAfterMs !== void 0 ? Math.min(retryAfterMs, MAX_BACKOFF_MS) : BASE_DELAY_MS * 2 ** attempt;
|
|
603
|
+
await sleep2(jitter(baseDelay), init?.signal ?? void 0);
|
|
341
604
|
}
|
|
342
605
|
} finally {
|
|
343
606
|
if (acquired) governor.release();
|
|
@@ -485,254 +748,467 @@ var RateLimiter = class {
|
|
|
485
748
|
}
|
|
486
749
|
};
|
|
487
750
|
|
|
488
|
-
// src/providers/openai.ts
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
"
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
"o3-mini": [11e-7, 44e-7, 0.25],
|
|
505
|
-
"o3-pro": [2e-5, 8e-5, 0.25],
|
|
506
|
-
"o4-mini": [11e-7, 44e-7, 0.25],
|
|
507
|
-
// gpt-5 era — cache reads at 10% of input rate
|
|
508
|
-
"gpt-5": [125e-8, 1e-5, 0.1],
|
|
509
|
-
"gpt-5-mini": [25e-8, 2e-6, 0.1],
|
|
510
|
-
"gpt-5-nano": [5e-8, 4e-7, 0.1],
|
|
511
|
-
"gpt-5.1": [125e-8, 1e-5, 0.1],
|
|
512
|
-
"gpt-5.2": [175e-8, 14e-6, 0.1],
|
|
513
|
-
"gpt-5.3": [175e-8, 14e-6, 0.1],
|
|
514
|
-
"gpt-5.4": [25e-7, 15e-6, 0.1],
|
|
515
|
-
"gpt-5.4-pro": [3e-5, 18e-5, 0.1],
|
|
516
|
-
// gpt-5.5 era (flagship released 2026-04) — cache reads at 10% of input rate
|
|
517
|
-
"gpt-5.5": [5e-6, 3e-5, 0.1],
|
|
518
|
-
"gpt-5.5-pro": [3e-5, 18e-5, 0.1]
|
|
519
|
-
};
|
|
520
|
-
var PRICING_KEYS_BY_LENGTH = Object.keys(OPENAI_PRICING).sort((a, b) => b.length - a.length);
|
|
521
|
-
function estimateOpenAICost(model, promptTokens, completionTokens, cachedTokens) {
|
|
522
|
-
let pricing = OPENAI_PRICING[model];
|
|
751
|
+
// src/providers/openai-compatible.ts
|
|
752
|
+
function resolvePerModel(value, model, fallback) {
|
|
753
|
+
if (value === void 0) return fallback;
|
|
754
|
+
return typeof value === "function" ? value(model) : value;
|
|
755
|
+
}
|
|
756
|
+
var SORTED_KEYS_CACHE = /* @__PURE__ */ new WeakMap();
|
|
757
|
+
function sortedKeys(table) {
|
|
758
|
+
let keys = SORTED_KEYS_CACHE.get(table);
|
|
759
|
+
if (!keys) {
|
|
760
|
+
keys = Object.keys(table).sort((a, b) => b.length - a.length);
|
|
761
|
+
SORTED_KEYS_CACHE.set(table, keys);
|
|
762
|
+
}
|
|
763
|
+
return keys;
|
|
764
|
+
}
|
|
765
|
+
function priceFromTable(table, model, promptTokens, completionTokens, cachedTokens) {
|
|
766
|
+
let pricing = table[model];
|
|
523
767
|
if (!pricing) {
|
|
524
|
-
for (const key of
|
|
768
|
+
for (const key of sortedKeys(table)) {
|
|
525
769
|
if (model.startsWith(key)) {
|
|
526
|
-
pricing =
|
|
770
|
+
pricing = table[key];
|
|
527
771
|
break;
|
|
528
772
|
}
|
|
529
773
|
}
|
|
530
774
|
}
|
|
531
|
-
if (!pricing) return 0;
|
|
775
|
+
if (!pricing) return void 0;
|
|
532
776
|
const [inputRate, outputRate, cacheMultiplier] = pricing;
|
|
533
777
|
const cached = cachedTokens ?? 0;
|
|
534
778
|
const inputCost = (promptTokens - cached) * inputRate + cached * inputRate * cacheMultiplier;
|
|
535
779
|
return inputCost + completionTokens * outputRate;
|
|
536
780
|
}
|
|
537
|
-
|
|
538
|
-
|
|
781
|
+
var THINK_OPEN = "<think>";
|
|
782
|
+
var THINK_CLOSE = "</think>";
|
|
783
|
+
function partialTagSuffixLen(s, tag) {
|
|
784
|
+
const max = Math.min(s.length, tag.length - 1);
|
|
785
|
+
for (let n = max; n > 0; n--) {
|
|
786
|
+
if (tag.startsWith(s.slice(s.length - n))) return n;
|
|
787
|
+
}
|
|
788
|
+
return 0;
|
|
539
789
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
790
|
+
var ThinkTagScanner = class {
|
|
791
|
+
buffer = "";
|
|
792
|
+
inside = false;
|
|
793
|
+
push(chunk) {
|
|
794
|
+
this.buffer += chunk;
|
|
795
|
+
let text = "";
|
|
796
|
+
let thinking = "";
|
|
797
|
+
for (; ; ) {
|
|
798
|
+
const tag = this.inside ? THINK_CLOSE : THINK_OPEN;
|
|
799
|
+
const idx = this.buffer.indexOf(tag);
|
|
800
|
+
if (idx === -1) {
|
|
801
|
+
const hold = partialTagSuffixLen(this.buffer, tag);
|
|
802
|
+
const emit = this.buffer.slice(0, this.buffer.length - hold);
|
|
803
|
+
if (this.inside) thinking += emit;
|
|
804
|
+
else text += emit;
|
|
805
|
+
this.buffer = this.buffer.slice(this.buffer.length - hold);
|
|
806
|
+
break;
|
|
807
|
+
}
|
|
808
|
+
const before = this.buffer.slice(0, idx);
|
|
809
|
+
if (this.inside) thinking += before;
|
|
810
|
+
else text += before;
|
|
811
|
+
this.buffer = this.buffer.slice(idx + tag.length);
|
|
812
|
+
this.inside = !this.inside;
|
|
813
|
+
}
|
|
814
|
+
return { text, thinking };
|
|
815
|
+
}
|
|
816
|
+
/** Flush any residual buffer at stream end (treat as the current channel). */
|
|
817
|
+
flush() {
|
|
818
|
+
const rest = this.buffer;
|
|
819
|
+
this.buffer = "";
|
|
820
|
+
return this.inside ? { text: "", thinking: rest } : { text: rest, thinking: "" };
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
function extractThinkTags(content) {
|
|
824
|
+
const scanner = new ThinkTagScanner();
|
|
825
|
+
const a = scanner.push(content);
|
|
826
|
+
const b = scanner.flush();
|
|
827
|
+
return { content: a.text + b.text, thinking: a.thinking + b.thinking };
|
|
545
828
|
}
|
|
546
|
-
function
|
|
547
|
-
return
|
|
829
|
+
function reasoningEffortEmit(map) {
|
|
830
|
+
return (body, resolved, model) => {
|
|
831
|
+
const value = map(resolved, model);
|
|
832
|
+
if (value) body.reasoning_effort = value;
|
|
833
|
+
};
|
|
548
834
|
}
|
|
549
|
-
function
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
835
|
+
function reasoningObjectEmit(mapEffort) {
|
|
836
|
+
return (body, resolved, model) => {
|
|
837
|
+
if (resolved.hasBudgetOverride) {
|
|
838
|
+
body.reasoning = { max_tokens: resolved.thinkingBudget };
|
|
839
|
+
} else if (resolved.activeEffort) {
|
|
840
|
+
body.reasoning = { effort: mapEffort(resolved.activeEffort, model) };
|
|
841
|
+
} else if (resolved.thinkingDisabled) {
|
|
842
|
+
body.reasoning = { enabled: false };
|
|
843
|
+
}
|
|
844
|
+
};
|
|
554
845
|
}
|
|
555
|
-
|
|
556
|
-
|
|
846
|
+
var ROUND_TRIP_FIELDS = ["reasoning_content", "reasoning", "reasoning_details"];
|
|
847
|
+
function isRoundTripField(field) {
|
|
848
|
+
return typeof field === "string" && ROUND_TRIP_FIELDS.includes(field);
|
|
557
849
|
}
|
|
558
|
-
function
|
|
559
|
-
if (
|
|
560
|
-
if (
|
|
561
|
-
return "
|
|
850
|
+
function normalizeMessageContent(content) {
|
|
851
|
+
if (typeof content === "string") return content;
|
|
852
|
+
if (content == null) return "";
|
|
853
|
+
if (!Array.isArray(content)) return "";
|
|
854
|
+
return content.map((part) => {
|
|
855
|
+
if (typeof part === "string") return part;
|
|
856
|
+
if (!part || typeof part !== "object") return "";
|
|
857
|
+
if (typeof part.text === "string") return part.text;
|
|
858
|
+
if (typeof part.content === "string") return part.content;
|
|
859
|
+
return "";
|
|
860
|
+
}).join("");
|
|
562
861
|
}
|
|
563
|
-
var
|
|
564
|
-
name
|
|
862
|
+
var OpenAICompatibleProvider = class {
|
|
863
|
+
name;
|
|
864
|
+
/** `json_schema` is honored natively when the profile's `supportsJsonSchema`
|
|
865
|
+
* capability is true for this model (the default); otherwise the engine
|
|
866
|
+
* downgrades the request to plain `json_object` (see `buildRequestBody`),
|
|
867
|
+
* which does NOT enforce the schema shape. */
|
|
868
|
+
nativeStructuredOutputSupport(model) {
|
|
869
|
+
const supportsSchema = resolvePerModel(
|
|
870
|
+
this.profile.capabilities?.supportsJsonSchema,
|
|
871
|
+
model,
|
|
872
|
+
true
|
|
873
|
+
);
|
|
874
|
+
return supportsSchema ? "schema" : "downgraded";
|
|
875
|
+
}
|
|
876
|
+
profile;
|
|
565
877
|
baseUrl;
|
|
566
|
-
|
|
878
|
+
/** A key string, or a resolver invoked per request (expiring tokens). */
|
|
879
|
+
apiKeySource;
|
|
880
|
+
authHeader;
|
|
567
881
|
governor;
|
|
568
|
-
constructor(options
|
|
569
|
-
|
|
570
|
-
this.
|
|
882
|
+
constructor(options) {
|
|
883
|
+
const p = options.profile;
|
|
884
|
+
this.profile = p;
|
|
885
|
+
this.name = p.name;
|
|
886
|
+
this.apiKeySource = options.apiKey ?? (p.envApiKey ? process.env[p.envApiKey] : void 0) ?? "";
|
|
887
|
+
this.authHeader = options.authHeader;
|
|
888
|
+
const envBase = p.envBaseUrl ? process.env[p.envBaseUrl] : void 0;
|
|
889
|
+
const explicitBase = options.baseUrl ?? envBase;
|
|
890
|
+
this.baseUrl = (explicitBase ?? p.defaultBaseUrl).replace(/\/$/, "");
|
|
571
891
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
572
|
-
|
|
573
|
-
|
|
892
|
+
const label = p.label ?? p.name;
|
|
893
|
+
if (p.requireExplicitBaseUrl && explicitBase === void 0) {
|
|
894
|
+
const env = p.envBaseUrl ? ` or ${p.envBaseUrl}` : "";
|
|
895
|
+
throw new Error(
|
|
896
|
+
`${label} requires a base URL. Set providers.${p.name}.baseUrl${env} (it is resource-specific and has no default).`
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
if (typeof this.apiKeySource === "string" && !this.apiKeySource && !p.allowMissingApiKey) {
|
|
900
|
+
const env = p.envApiKey ?? "the API key env var";
|
|
901
|
+
throw new Error(`${label} API key is required. Set ${env} or pass apiKey in options.`);
|
|
574
902
|
}
|
|
575
903
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
904
|
+
/** Resolve the API key for one request, validating against allowMissingApiKey. */
|
|
905
|
+
async resolveKey() {
|
|
906
|
+
const key = await resolveApiKey(this.apiKeySource);
|
|
907
|
+
if (!key && !this.profile.allowMissingApiKey) {
|
|
908
|
+
const label = this.profile.label ?? this.profile.name;
|
|
909
|
+
const env = this.profile.envApiKey ?? "the API key env var";
|
|
910
|
+
throw new Error(`${label} API key is required. Set ${env} or pass apiKey in options.`);
|
|
911
|
+
}
|
|
912
|
+
return key;
|
|
913
|
+
}
|
|
579
914
|
async chat(messages, options) {
|
|
915
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
580
916
|
const body = this.buildRequestBody(messages, options, false);
|
|
581
917
|
const res = await fetchWithRetry(
|
|
582
918
|
`${this.baseUrl}/chat/completions`,
|
|
583
919
|
{
|
|
584
920
|
method: "POST",
|
|
585
|
-
headers
|
|
586
|
-
"Content-Type": "application/json",
|
|
587
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
588
|
-
},
|
|
921
|
+
headers,
|
|
589
922
|
body: JSON.stringify(body),
|
|
590
923
|
signal: options.signal
|
|
591
924
|
},
|
|
592
|
-
{ governor: this.governor }
|
|
925
|
+
{ governor: this.governor, provider: this.name }
|
|
593
926
|
);
|
|
594
927
|
if (!res.ok) {
|
|
595
928
|
const errorBody = await res.text();
|
|
596
|
-
|
|
597
|
-
|
|
929
|
+
throw buildProviderError({
|
|
930
|
+
provider: this.name,
|
|
931
|
+
status: res.status,
|
|
932
|
+
headers: res.headers,
|
|
933
|
+
message: this.extractErrorMessage(errorBody, res.status),
|
|
934
|
+
body: errorBody
|
|
935
|
+
});
|
|
598
936
|
}
|
|
599
937
|
const json = await res.json();
|
|
600
|
-
|
|
601
|
-
const usage = json.usage ? {
|
|
602
|
-
prompt_tokens: json.usage.prompt_tokens,
|
|
603
|
-
completion_tokens: json.usage.completion_tokens,
|
|
604
|
-
total_tokens: json.usage.total_tokens,
|
|
605
|
-
reasoning_tokens: json.usage.completion_tokens_details?.reasoning_tokens,
|
|
606
|
-
cached_tokens: json.usage.prompt_tokens_details?.cached_tokens
|
|
607
|
-
} : void 0;
|
|
608
|
-
const cost = usage ? estimateOpenAICost(
|
|
609
|
-
options.model,
|
|
610
|
-
usage.prompt_tokens,
|
|
611
|
-
usage.completion_tokens,
|
|
612
|
-
usage.cached_tokens
|
|
613
|
-
) : void 0;
|
|
614
|
-
return {
|
|
615
|
-
content: choice.message.content ?? "",
|
|
616
|
-
tool_calls: choice.message.tool_calls?.map((tc) => ({
|
|
617
|
-
id: tc.id,
|
|
618
|
-
type: "function",
|
|
619
|
-
function: {
|
|
620
|
-
name: tc.function.name,
|
|
621
|
-
arguments: tc.function.arguments
|
|
622
|
-
}
|
|
623
|
-
})),
|
|
624
|
-
usage,
|
|
625
|
-
cost
|
|
626
|
-
};
|
|
938
|
+
return this.parseResponse(json, options.model);
|
|
627
939
|
}
|
|
628
|
-
// ---------------------------------------------------------------------------
|
|
629
|
-
// stream - SSE streaming completion
|
|
630
|
-
// ---------------------------------------------------------------------------
|
|
631
940
|
async *stream(messages, options) {
|
|
941
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
632
942
|
const body = this.buildRequestBody(messages, options, true);
|
|
633
943
|
const res = await fetchWithRetry(
|
|
634
944
|
`${this.baseUrl}/chat/completions`,
|
|
635
945
|
{
|
|
636
946
|
method: "POST",
|
|
637
|
-
headers
|
|
638
|
-
"Content-Type": "application/json",
|
|
639
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
640
|
-
},
|
|
947
|
+
headers,
|
|
641
948
|
body: JSON.stringify(body),
|
|
642
949
|
signal: options.signal
|
|
643
950
|
},
|
|
644
|
-
{ governor: this.governor }
|
|
951
|
+
{ governor: this.governor, provider: this.name }
|
|
645
952
|
);
|
|
646
953
|
if (!res.ok) {
|
|
647
954
|
const errorBody = await res.text();
|
|
648
|
-
|
|
649
|
-
|
|
955
|
+
throw buildProviderError({
|
|
956
|
+
provider: this.name,
|
|
957
|
+
status: res.status,
|
|
958
|
+
headers: res.headers,
|
|
959
|
+
message: this.extractErrorMessage(errorBody, res.status),
|
|
960
|
+
body: errorBody
|
|
961
|
+
});
|
|
650
962
|
}
|
|
651
963
|
if (!res.body) {
|
|
652
|
-
throw new Error(
|
|
964
|
+
throw new Error(`${this.profile.label ?? this.profile.name} stream response has no body`);
|
|
653
965
|
}
|
|
654
966
|
yield* this.parseSSEStream(res.body, options.model);
|
|
655
967
|
}
|
|
656
968
|
// ---------------------------------------------------------------------------
|
|
657
|
-
//
|
|
969
|
+
// Request building
|
|
658
970
|
// ---------------------------------------------------------------------------
|
|
659
|
-
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
if (
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
} else if (thinkingDisabled) {
|
|
670
|
-
wireEffort = clampReasoningEffort(options.model, "none");
|
|
671
|
-
}
|
|
971
|
+
buildHeaders(apiKey) {
|
|
972
|
+
const headers = {
|
|
973
|
+
"Content-Type": "application/json",
|
|
974
|
+
...this.profile.headers ?? {}
|
|
975
|
+
};
|
|
976
|
+
if (apiKey) {
|
|
977
|
+
const auth = this.authHeader ?? this.profile.authHeader ?? "bearer";
|
|
978
|
+
if (auth === "bearer") headers.Authorization = `Bearer ${apiKey}`;
|
|
979
|
+
else if (auth === "api-key") headers["api-key"] = apiKey;
|
|
980
|
+
else headers[auth.header] = auth.scheme ? `${auth.scheme} ${apiKey}` : apiKey;
|
|
672
981
|
}
|
|
673
|
-
|
|
982
|
+
return headers;
|
|
983
|
+
}
|
|
984
|
+
buildRequestBody(messages, options, stream) {
|
|
985
|
+
const profile = this.profile;
|
|
986
|
+
const resolved = resolveThinkingOptions(options);
|
|
674
987
|
const body = {
|
|
675
988
|
model: options.model,
|
|
676
|
-
messages: messages.map((m) => this.formatMessage(m,
|
|
989
|
+
messages: messages.map((m) => this.formatMessage(m, options.model)),
|
|
677
990
|
stream
|
|
678
991
|
};
|
|
679
|
-
|
|
992
|
+
const emitResult = profile.reasoning.emit(body, resolved, options.model) ?? {};
|
|
993
|
+
const stripTemperature = emitResult.stripTemperature ?? false;
|
|
994
|
+
if (options.temperature !== void 0 && !stripTemperature) {
|
|
680
995
|
body.temperature = options.temperature;
|
|
681
996
|
}
|
|
682
997
|
if (options.maxTokens !== void 0) {
|
|
683
|
-
body.max_completion_tokens = options.maxTokens;
|
|
998
|
+
body[profile.maxTokensField ?? "max_completion_tokens"] = options.maxTokens;
|
|
684
999
|
}
|
|
685
1000
|
if (options.stop) body.stop = options.stop;
|
|
686
1001
|
if (options.tools && options.tools.length > 0) {
|
|
687
1002
|
body.tools = options.tools;
|
|
688
|
-
if (
|
|
1003
|
+
if (resolvePerModel(profile.parallelToolCalls, options.model, false)) {
|
|
689
1004
|
body.parallel_tool_calls = true;
|
|
690
1005
|
}
|
|
691
1006
|
}
|
|
692
|
-
if (options.toolChoice !== void 0)
|
|
693
|
-
body.tool_choice = options.toolChoice;
|
|
694
|
-
}
|
|
1007
|
+
if (options.toolChoice !== void 0) body.tool_choice = options.toolChoice;
|
|
695
1008
|
if (options.responseFormat) {
|
|
696
|
-
|
|
1009
|
+
const supportsSchema = resolvePerModel(
|
|
1010
|
+
profile.capabilities?.supportsJsonSchema,
|
|
1011
|
+
options.model,
|
|
1012
|
+
true
|
|
1013
|
+
);
|
|
1014
|
+
body.response_format = options.responseFormat.type === "json_schema" && !supportsSchema ? { type: "json_object" } : options.responseFormat;
|
|
697
1015
|
}
|
|
698
|
-
if (
|
|
699
|
-
if (stream) {
|
|
1016
|
+
if (stream && (profile.capabilities?.supportsStreamUsage ?? true)) {
|
|
700
1017
|
body.stream_options = { include_usage: true };
|
|
701
1018
|
}
|
|
702
|
-
if (
|
|
703
|
-
|
|
1019
|
+
if (profile.requestDefaults) Object.assign(body, profile.requestDefaults);
|
|
1020
|
+
const userKeys = options.providerOptions ? Object.keys(options.providerOptions) : [];
|
|
1021
|
+
if (options.providerOptions) Object.assign(body, options.providerOptions);
|
|
1022
|
+
const forbidden = resolvePerModel(profile.capabilities?.forbiddenParams, options.model, []);
|
|
1023
|
+
for (const key of forbidden) {
|
|
1024
|
+
if (!userKeys.includes(key)) delete body[key];
|
|
704
1025
|
}
|
|
705
1026
|
return body;
|
|
706
1027
|
}
|
|
707
|
-
|
|
1028
|
+
formatMessage(msg, model) {
|
|
1029
|
+
const out = {
|
|
1030
|
+
role: this.profile.roleFor ? this.profile.roleFor(msg.role, model) : msg.role,
|
|
1031
|
+
content: msg.content
|
|
1032
|
+
};
|
|
1033
|
+
if (msg.name && (this.profile.capabilities?.emitsMessageName ?? true)) out.name = msg.name;
|
|
1034
|
+
if (msg.tool_calls) out.tool_calls = msg.tool_calls;
|
|
1035
|
+
if (msg.tool_call_id) out.tool_call_id = msg.tool_call_id;
|
|
1036
|
+
if (this.profile.reasoning.roundTrip === "on-tool-call-turns" && msg.role === "assistant" && msg.tool_calls && msg.tool_calls.length > 0) {
|
|
1037
|
+
const rt = msg.providerMetadata?.openaiCompatReasoning;
|
|
1038
|
+
if (rt && rt.provider === this.name && isRoundTripField(rt.field) && rt.value !== void 0 && rt.value !== null) {
|
|
1039
|
+
out[rt.field] = rt.value;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return out;
|
|
1043
|
+
}
|
|
1044
|
+
// ---------------------------------------------------------------------------
|
|
1045
|
+
// Response parsing
|
|
1046
|
+
// ---------------------------------------------------------------------------
|
|
708
1047
|
extractErrorMessage(body, status) {
|
|
1048
|
+
const label = this.profile.label ?? this.profile.name;
|
|
709
1049
|
try {
|
|
710
1050
|
const json = JSON.parse(body);
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
}
|
|
1051
|
+
const message = json.error?.message ?? json.message ?? json.detail;
|
|
1052
|
+
if (message) return `${label} API error (${status}): ${message}`;
|
|
714
1053
|
} catch {
|
|
715
1054
|
}
|
|
716
|
-
return
|
|
1055
|
+
return `${label} API error (${status}): ${body}`;
|
|
717
1056
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
1057
|
+
toUsage(raw) {
|
|
1058
|
+
if (!raw) return void 0;
|
|
1059
|
+
return {
|
|
1060
|
+
prompt_tokens: raw.prompt_tokens,
|
|
1061
|
+
completion_tokens: raw.completion_tokens,
|
|
1062
|
+
total_tokens: raw.total_tokens,
|
|
1063
|
+
reasoning_tokens: raw.completion_tokens_details?.reasoning_tokens,
|
|
1064
|
+
cached_tokens: raw.prompt_tokens_details?.cached_tokens
|
|
722
1065
|
};
|
|
723
|
-
if (msg.name) out.name = msg.name;
|
|
724
|
-
if (msg.tool_calls) out.tool_calls = msg.tool_calls;
|
|
725
|
-
if (msg.tool_call_id) out.tool_call_id = msg.tool_call_id;
|
|
726
|
-
return out;
|
|
727
1066
|
}
|
|
1067
|
+
computeCost(model, usage, reportedCost) {
|
|
1068
|
+
const p = this.profile.pricing;
|
|
1069
|
+
if (p.kind === "zero") return 0;
|
|
1070
|
+
if (!usage) return void 0;
|
|
1071
|
+
switch (p.kind) {
|
|
1072
|
+
case "unknown":
|
|
1073
|
+
return void 0;
|
|
1074
|
+
case "from-response":
|
|
1075
|
+
return typeof reportedCost === "number" && Number.isFinite(reportedCost) ? reportedCost : void 0;
|
|
1076
|
+
case "table": {
|
|
1077
|
+
const c = priceFromTable(
|
|
1078
|
+
p.table,
|
|
1079
|
+
model,
|
|
1080
|
+
usage.prompt_tokens,
|
|
1081
|
+
usage.completion_tokens,
|
|
1082
|
+
usage.cached_tokens
|
|
1083
|
+
);
|
|
1084
|
+
return c !== void 0 && Number.isFinite(c) ? c : void 0;
|
|
1085
|
+
}
|
|
1086
|
+
default: {
|
|
1087
|
+
const _exhaustive = p;
|
|
1088
|
+
throw new Error(`Unhandled pricing source: ${JSON.stringify(_exhaustive)}`);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
parseResponse(json, model) {
|
|
1093
|
+
const message = json.choices?.[0]?.message ?? {};
|
|
1094
|
+
let content = normalizeMessageContent(message.content);
|
|
1095
|
+
let thinking;
|
|
1096
|
+
let roundTrip;
|
|
1097
|
+
switch (this.profile.reasoning.capture) {
|
|
1098
|
+
case "reasoning_content":
|
|
1099
|
+
if (typeof message.reasoning_content === "string") {
|
|
1100
|
+
thinking = message.reasoning_content;
|
|
1101
|
+
roundTrip = {
|
|
1102
|
+
provider: this.name,
|
|
1103
|
+
field: "reasoning_content",
|
|
1104
|
+
value: message.reasoning_content
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
break;
|
|
1108
|
+
case "reasoning":
|
|
1109
|
+
if (typeof message.reasoning === "string") {
|
|
1110
|
+
thinking = message.reasoning;
|
|
1111
|
+
roundTrip = { provider: this.name, field: "reasoning", value: message.reasoning };
|
|
1112
|
+
}
|
|
1113
|
+
break;
|
|
1114
|
+
case "reasoning_details":
|
|
1115
|
+
if (typeof message.reasoning === "string") thinking = message.reasoning;
|
|
1116
|
+
if (message.reasoning_details !== void 0) {
|
|
1117
|
+
roundTrip = {
|
|
1118
|
+
provider: this.name,
|
|
1119
|
+
field: "reasoning_details",
|
|
1120
|
+
value: message.reasoning_details
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
break;
|
|
1124
|
+
case "think_tags": {
|
|
1125
|
+
const ex = extractThinkTags(content);
|
|
1126
|
+
content = ex.content;
|
|
1127
|
+
if (ex.thinking) thinking = ex.thinking;
|
|
1128
|
+
break;
|
|
1129
|
+
}
|
|
1130
|
+
case "none":
|
|
1131
|
+
break;
|
|
1132
|
+
default: {
|
|
1133
|
+
const _exhaustive = this.profile.reasoning.capture;
|
|
1134
|
+
throw new Error(`Unhandled reasoning capture: ${String(_exhaustive)}`);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
const usage = this.toUsage(json.usage);
|
|
1138
|
+
const cost = this.computeCost(model, usage, json.usage?.cost);
|
|
1139
|
+
return {
|
|
1140
|
+
content,
|
|
1141
|
+
thinking_content: thinking || void 0,
|
|
1142
|
+
tool_calls: message.tool_calls?.map((tc) => ({
|
|
1143
|
+
id: tc.id,
|
|
1144
|
+
type: "function",
|
|
1145
|
+
function: { name: tc.function.name, arguments: tc.function.arguments }
|
|
1146
|
+
})),
|
|
1147
|
+
usage,
|
|
1148
|
+
cost,
|
|
1149
|
+
providerMetadata: this.roundTripMetadata(roundTrip)
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
roundTripMetadata(rt) {
|
|
1153
|
+
if (!rt || this.profile.reasoning.roundTrip === void 0 || this.profile.reasoning.roundTrip === "none") {
|
|
1154
|
+
return void 0;
|
|
1155
|
+
}
|
|
1156
|
+
return rt.value === void 0 || rt.value === null ? void 0 : { openaiCompatReasoning: rt };
|
|
1157
|
+
}
|
|
1158
|
+
// ---------------------------------------------------------------------------
|
|
1159
|
+
// SSE streaming
|
|
1160
|
+
// ---------------------------------------------------------------------------
|
|
728
1161
|
async *parseSSEStream(body, model) {
|
|
729
1162
|
const reader = body.getReader();
|
|
730
1163
|
const decoder = new TextDecoder();
|
|
731
1164
|
let buffer = "";
|
|
732
1165
|
let usageData;
|
|
1166
|
+
const capture = this.profile.reasoning.capture;
|
|
1167
|
+
const thinkScanner = capture === "think_tags" ? new ThinkTagScanner() : void 0;
|
|
1168
|
+
let reasoningText = "";
|
|
1169
|
+
const reasoningDetails = [];
|
|
733
1170
|
const indexToId = /* @__PURE__ */ new Map();
|
|
1171
|
+
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
1172
|
+
const flushPendingToolCalls = () => {
|
|
1173
|
+
const out = [];
|
|
1174
|
+
for (const [index, buf] of pendingToolCalls) {
|
|
1175
|
+
out.push({
|
|
1176
|
+
type: "tool_call_delta",
|
|
1177
|
+
id: `__toolcall_${index}`,
|
|
1178
|
+
name: buf.name,
|
|
1179
|
+
arguments: buf.arguments
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
pendingToolCalls.clear();
|
|
1183
|
+
return out;
|
|
1184
|
+
};
|
|
1185
|
+
const makeDone = () => {
|
|
1186
|
+
const usage = this.toUsage(usageData);
|
|
1187
|
+
let providerMetadata;
|
|
1188
|
+
if (this.profile.reasoning.roundTrip === "on-tool-call-turns") {
|
|
1189
|
+
if (capture === "reasoning_details" && reasoningDetails.length > 0) {
|
|
1190
|
+
providerMetadata = {
|
|
1191
|
+
openaiCompatReasoning: {
|
|
1192
|
+
provider: this.name,
|
|
1193
|
+
field: "reasoning_details",
|
|
1194
|
+
value: reasoningDetails
|
|
1195
|
+
}
|
|
1196
|
+
};
|
|
1197
|
+
} else if ((capture === "reasoning_content" || capture === "reasoning") && reasoningText) {
|
|
1198
|
+
providerMetadata = {
|
|
1199
|
+
openaiCompatReasoning: { provider: this.name, field: capture, value: reasoningText }
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
return {
|
|
1204
|
+
type: "done",
|
|
1205
|
+
usage,
|
|
1206
|
+
cost: this.computeCost(model, usage, usageData?.cost),
|
|
1207
|
+
providerMetadata
|
|
1208
|
+
};
|
|
1209
|
+
};
|
|
734
1210
|
try {
|
|
735
|
-
|
|
1211
|
+
for (; ; ) {
|
|
736
1212
|
const { done, value } = await reader.read();
|
|
737
1213
|
if (done) break;
|
|
738
1214
|
buffer += decoder.decode(value, { stream: true });
|
|
@@ -742,47 +1218,76 @@ var OpenAIProvider = class {
|
|
|
742
1218
|
const trimmed = line.trim();
|
|
743
1219
|
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
744
1220
|
if (trimmed === "data: [DONE]") {
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
usageData.cached_tokens
|
|
753
|
-
) : void 0
|
|
754
|
-
};
|
|
1221
|
+
if (thinkScanner) {
|
|
1222
|
+
const flushed = thinkScanner.flush();
|
|
1223
|
+
if (flushed.text) yield { type: "text_delta", content: flushed.text };
|
|
1224
|
+
if (flushed.thinking) yield { type: "thinking_delta", content: flushed.thinking };
|
|
1225
|
+
}
|
|
1226
|
+
yield* flushPendingToolCalls();
|
|
1227
|
+
yield makeDone();
|
|
755
1228
|
return;
|
|
756
1229
|
}
|
|
757
|
-
if (trimmed.startsWith("data: "))
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
if (!
|
|
777
|
-
|
|
1230
|
+
if (!trimmed.startsWith("data: ")) continue;
|
|
1231
|
+
let parsed;
|
|
1232
|
+
try {
|
|
1233
|
+
parsed = JSON.parse(trimmed.slice(6));
|
|
1234
|
+
} catch {
|
|
1235
|
+
continue;
|
|
1236
|
+
}
|
|
1237
|
+
if (parsed.error) {
|
|
1238
|
+
const message = typeof parsed.error.message === "string" ? parsed.error.message : `${this.profile.label ?? this.profile.name} stream error`;
|
|
1239
|
+
throw new ProviderError({
|
|
1240
|
+
provider: this.name,
|
|
1241
|
+
status: 0,
|
|
1242
|
+
retryable: false,
|
|
1243
|
+
message,
|
|
1244
|
+
body: JSON.stringify(parsed.error)
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
if (parsed.usage) {
|
|
1248
|
+
usageData = parsed.usage;
|
|
1249
|
+
if (!parsed.choices || parsed.choices.length === 0) continue;
|
|
1250
|
+
}
|
|
1251
|
+
const delta = parsed.choices?.[0]?.delta;
|
|
1252
|
+
if (!delta) continue;
|
|
1253
|
+
const deltaReasoning = capture === "reasoning_content" ? delta.reasoning_content : capture === "reasoning" || capture === "reasoning_details" ? delta.reasoning : void 0;
|
|
1254
|
+
if (typeof deltaReasoning === "string" && deltaReasoning.length > 0) {
|
|
1255
|
+
reasoningText += deltaReasoning;
|
|
1256
|
+
yield { type: "thinking_delta", content: deltaReasoning };
|
|
1257
|
+
}
|
|
1258
|
+
if (capture === "reasoning_details" && Array.isArray(delta.reasoning_details)) {
|
|
1259
|
+
reasoningDetails.push(...delta.reasoning_details);
|
|
1260
|
+
}
|
|
1261
|
+
if (typeof delta.content === "string" && delta.content.length > 0) {
|
|
1262
|
+
if (thinkScanner) {
|
|
1263
|
+
const { text, thinking } = thinkScanner.push(delta.content);
|
|
1264
|
+
if (text) yield { type: "text_delta", content: text };
|
|
1265
|
+
if (thinking) yield { type: "thinking_delta", content: thinking };
|
|
1266
|
+
} else {
|
|
778
1267
|
yield { type: "text_delta", content: delta.content };
|
|
779
1268
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
1269
|
+
}
|
|
1270
|
+
if (delta.tool_calls) {
|
|
1271
|
+
for (const tc of delta.tool_calls) {
|
|
1272
|
+
if (tc.id) indexToId.set(tc.index, tc.id);
|
|
1273
|
+
const id = indexToId.get(tc.index);
|
|
1274
|
+
if (id === void 0) {
|
|
1275
|
+
const buf2 = pendingToolCalls.get(tc.index) ?? { arguments: "" };
|
|
1276
|
+
if (tc.function?.name) buf2.name = tc.function.name;
|
|
1277
|
+
if (tc.function?.arguments) buf2.arguments += tc.function.arguments;
|
|
1278
|
+
pendingToolCalls.set(tc.index, buf2);
|
|
1279
|
+
continue;
|
|
1280
|
+
}
|
|
1281
|
+
const buf = pendingToolCalls.get(tc.index);
|
|
1282
|
+
if (buf) {
|
|
1283
|
+
pendingToolCalls.delete(tc.index);
|
|
1284
|
+
yield {
|
|
1285
|
+
type: "tool_call_delta",
|
|
1286
|
+
id,
|
|
1287
|
+
name: buf.name ?? tc.function?.name,
|
|
1288
|
+
arguments: buf.arguments + (tc.function?.arguments ?? "")
|
|
1289
|
+
};
|
|
1290
|
+
} else {
|
|
786
1291
|
yield {
|
|
787
1292
|
type: "tool_call_delta",
|
|
788
1293
|
id,
|
|
@@ -794,58 +1299,176 @@ var OpenAIProvider = class {
|
|
|
794
1299
|
}
|
|
795
1300
|
}
|
|
796
1301
|
}
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
1302
|
+
if (thinkScanner) {
|
|
1303
|
+
const flushed = thinkScanner.flush();
|
|
1304
|
+
if (flushed.text) yield { type: "text_delta", content: flushed.text };
|
|
1305
|
+
if (flushed.thinking) yield { type: "thinking_delta", content: flushed.thinking };
|
|
1306
|
+
}
|
|
1307
|
+
yield* flushPendingToolCalls();
|
|
1308
|
+
throw new ProviderError({
|
|
1309
|
+
provider: this.name,
|
|
1310
|
+
status: 0,
|
|
1311
|
+
retryable: true,
|
|
1312
|
+
message: `${this.profile.label ?? this.profile.name} stream ended before [DONE]`
|
|
1313
|
+
});
|
|
807
1314
|
} finally {
|
|
808
1315
|
reader.releaseLock();
|
|
809
1316
|
}
|
|
810
1317
|
}
|
|
811
1318
|
};
|
|
812
1319
|
|
|
1320
|
+
// src/providers/openai.ts
|
|
1321
|
+
var OPENAI_PRICING = {
|
|
1322
|
+
// gpt-4o era — cache reads at 50% of input rate
|
|
1323
|
+
"gpt-4o": [25e-7, 1e-5, 0.5],
|
|
1324
|
+
"gpt-4o-mini": [15e-8, 6e-7, 0.5],
|
|
1325
|
+
"gpt-4-turbo": [1e-5, 3e-5, 0.5],
|
|
1326
|
+
"gpt-4": [3e-5, 6e-5, 0.5],
|
|
1327
|
+
"gpt-3.5-turbo": [5e-7, 15e-7, 0.5],
|
|
1328
|
+
o1: [15e-6, 6e-5, 0.5],
|
|
1329
|
+
"o1-mini": [3e-6, 12e-6, 0.5],
|
|
1330
|
+
"o1-pro": [15e-5, 6e-4, 0.5],
|
|
1331
|
+
// gpt-4.1 / o3 / o4 era — cache reads at 25% of input rate
|
|
1332
|
+
"gpt-4.1": [2e-6, 8e-6, 0.25],
|
|
1333
|
+
"gpt-4.1-mini": [4e-7, 16e-7, 0.25],
|
|
1334
|
+
"gpt-4.1-nano": [1e-7, 4e-7, 0.25],
|
|
1335
|
+
o3: [1e-5, 4e-5, 0.25],
|
|
1336
|
+
"o3-mini": [11e-7, 44e-7, 0.25],
|
|
1337
|
+
"o3-pro": [2e-5, 8e-5, 0.25],
|
|
1338
|
+
"o4-mini": [11e-7, 44e-7, 0.25],
|
|
1339
|
+
// gpt-5 era — cache reads at 10% of input rate
|
|
1340
|
+
"gpt-5": [125e-8, 1e-5, 0.1],
|
|
1341
|
+
"gpt-5-mini": [25e-8, 2e-6, 0.1],
|
|
1342
|
+
"gpt-5-nano": [5e-8, 4e-7, 0.1],
|
|
1343
|
+
"gpt-5.1": [125e-8, 1e-5, 0.1],
|
|
1344
|
+
"gpt-5.2": [175e-8, 14e-6, 0.1],
|
|
1345
|
+
"gpt-5.3": [175e-8, 14e-6, 0.1],
|
|
1346
|
+
"gpt-5.4": [25e-7, 15e-6, 0.1],
|
|
1347
|
+
"gpt-5.4-pro": [3e-5, 18e-5, 0.1],
|
|
1348
|
+
// gpt-5.5 era (flagship released 2026-04) — cache reads at 10% of input rate
|
|
1349
|
+
"gpt-5.5": [5e-6, 3e-5, 0.1],
|
|
1350
|
+
"gpt-5.5-pro": [3e-5, 18e-5, 0.1]
|
|
1351
|
+
};
|
|
1352
|
+
function estimateOpenAICost(model, promptTokens, completionTokens, cachedTokens) {
|
|
1353
|
+
return priceFromTable(OPENAI_PRICING, model, promptTokens, completionTokens, cachedTokens);
|
|
1354
|
+
}
|
|
1355
|
+
function isOSeriesModel(model) {
|
|
1356
|
+
return /^(o1|o3|o4-mini)/.test(model);
|
|
1357
|
+
}
|
|
1358
|
+
function supportsReasoningEffort(model) {
|
|
1359
|
+
return isOSeriesModel(model) || /^gpt-5/.test(model);
|
|
1360
|
+
}
|
|
1361
|
+
function supportsReasoningNone(model) {
|
|
1362
|
+
return /^gpt-5\.[1-9]/.test(model);
|
|
1363
|
+
}
|
|
1364
|
+
function supportsXhigh(model) {
|
|
1365
|
+
return /^gpt-5\.([2-9]|\d{2,})/.test(model);
|
|
1366
|
+
}
|
|
1367
|
+
function clampReasoningEffort(model, effort) {
|
|
1368
|
+
if (model.startsWith("gpt-5-pro")) return "high";
|
|
1369
|
+
if (effort === "none" && !supportsReasoningNone(model)) return "minimal";
|
|
1370
|
+
if (effort === "xhigh" && !supportsXhigh(model)) return "high";
|
|
1371
|
+
return effort;
|
|
1372
|
+
}
|
|
1373
|
+
function effortToReasoningEffort(effort) {
|
|
1374
|
+
return effort === "max" ? "xhigh" : effort;
|
|
1375
|
+
}
|
|
1376
|
+
function budgetToReasoningEffort(budget) {
|
|
1377
|
+
if (budget <= 1024) return "low";
|
|
1378
|
+
if (budget <= 8192) return "medium";
|
|
1379
|
+
return "high";
|
|
1380
|
+
}
|
|
1381
|
+
var openaiReasoningEmit = (body, resolved, model) => {
|
|
1382
|
+
const oSeries = isOSeriesModel(model);
|
|
1383
|
+
const reasoningCapable = supportsReasoningEffort(model);
|
|
1384
|
+
const { thinkingBudget, thinkingDisabled, activeEffort, hasBudgetOverride } = resolved;
|
|
1385
|
+
let wireEffort;
|
|
1386
|
+
if (reasoningCapable) {
|
|
1387
|
+
if (hasBudgetOverride) {
|
|
1388
|
+
wireEffort = clampReasoningEffort(model, budgetToReasoningEffort(thinkingBudget));
|
|
1389
|
+
} else if (!thinkingDisabled && activeEffort) {
|
|
1390
|
+
wireEffort = clampReasoningEffort(model, effortToReasoningEffort(activeEffort));
|
|
1391
|
+
} else if (thinkingDisabled) {
|
|
1392
|
+
wireEffort = clampReasoningEffort(model, "none");
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (wireEffort) body.reasoning_effort = wireEffort;
|
|
1396
|
+
return { stripTemperature: oSeries || reasoningCapable && wireEffort !== void 0 };
|
|
1397
|
+
};
|
|
1398
|
+
var OPENAI_PROFILE = {
|
|
1399
|
+
name: "openai",
|
|
1400
|
+
label: "OpenAI",
|
|
1401
|
+
defaultBaseUrl: "https://api.openai.com/v1",
|
|
1402
|
+
envApiKey: "OPENAI_API_KEY",
|
|
1403
|
+
envBaseUrl: "OPENAI_BASE_URL",
|
|
1404
|
+
pricing: { kind: "table", table: OPENAI_PRICING },
|
|
1405
|
+
reasoning: { emit: openaiReasoningEmit, capture: "none" },
|
|
1406
|
+
roleFor: (role, model) => role === "system" && isOSeriesModel(model) ? "developer" : role,
|
|
1407
|
+
maxTokensField: "max_completion_tokens",
|
|
1408
|
+
parallelToolCalls: (model) => !isOSeriesModel(model)
|
|
1409
|
+
};
|
|
1410
|
+
var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
1411
|
+
constructor(options = {}) {
|
|
1412
|
+
super({ profile: OPENAI_PROFILE, ...options });
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
|
|
813
1416
|
// src/providers/openai-responses.ts
|
|
814
1417
|
var OpenAIResponsesProvider = class {
|
|
815
1418
|
name = "openai-responses";
|
|
1419
|
+
/** The Responses API honors `json_schema` (structured outputs) natively. */
|
|
1420
|
+
nativeStructuredOutputSupport() {
|
|
1421
|
+
return "schema";
|
|
1422
|
+
}
|
|
816
1423
|
baseUrl;
|
|
817
|
-
|
|
1424
|
+
apiKeySource;
|
|
818
1425
|
governor;
|
|
819
1426
|
constructor(options = {}) {
|
|
820
|
-
this.
|
|
1427
|
+
this.apiKeySource = options.apiKey ?? process.env.OPENAI_API_KEY ?? "";
|
|
821
1428
|
this.baseUrl = (options.baseUrl ?? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
822
1429
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
823
|
-
if (!this.
|
|
1430
|
+
if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
|
|
1431
|
+
throw new Error("OpenAI API key is required. Set OPENAI_API_KEY or pass apiKey in options.");
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
/** Resolve the API key for one request (supports an expiring-token callback). */
|
|
1435
|
+
async resolveKey() {
|
|
1436
|
+
const key = await resolveApiKey(this.apiKeySource);
|
|
1437
|
+
if (!key) {
|
|
824
1438
|
throw new Error("OpenAI API key is required. Set OPENAI_API_KEY or pass apiKey in options.");
|
|
825
1439
|
}
|
|
1440
|
+
return key;
|
|
1441
|
+
}
|
|
1442
|
+
/** Build request headers from a resolved key (used by both chat and stream). */
|
|
1443
|
+
buildHeaders(apiKey) {
|
|
1444
|
+
return { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` };
|
|
826
1445
|
}
|
|
827
1446
|
// ---------------------------------------------------------------------------
|
|
828
1447
|
// chat - non-streaming
|
|
829
1448
|
// ---------------------------------------------------------------------------
|
|
830
1449
|
async chat(messages, options) {
|
|
1450
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
831
1451
|
const body = this.buildRequestBody(messages, options, false);
|
|
832
1452
|
const res = await fetchWithRetry(
|
|
833
1453
|
`${this.baseUrl}/responses`,
|
|
834
1454
|
{
|
|
835
1455
|
method: "POST",
|
|
836
|
-
headers
|
|
837
|
-
"Content-Type": "application/json",
|
|
838
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
839
|
-
},
|
|
1456
|
+
headers,
|
|
840
1457
|
body: JSON.stringify(body),
|
|
841
1458
|
signal: options.signal
|
|
842
1459
|
},
|
|
843
|
-
{ governor: this.governor }
|
|
1460
|
+
{ governor: this.governor, provider: this.name }
|
|
844
1461
|
);
|
|
845
1462
|
if (!res.ok) {
|
|
846
1463
|
const errorBody = await res.text();
|
|
847
1464
|
const message = this.extractErrorMessage(errorBody, res.status);
|
|
848
|
-
throw
|
|
1465
|
+
throw buildProviderError({
|
|
1466
|
+
provider: this.name,
|
|
1467
|
+
status: res.status,
|
|
1468
|
+
headers: res.headers,
|
|
1469
|
+
message,
|
|
1470
|
+
body: errorBody
|
|
1471
|
+
});
|
|
849
1472
|
}
|
|
850
1473
|
const json = await res.json();
|
|
851
1474
|
return this.parseResponse(json, options.model);
|
|
@@ -854,24 +1477,28 @@ var OpenAIResponsesProvider = class {
|
|
|
854
1477
|
// stream - SSE streaming
|
|
855
1478
|
// ---------------------------------------------------------------------------
|
|
856
1479
|
async *stream(messages, options) {
|
|
1480
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
857
1481
|
const body = this.buildRequestBody(messages, options, true);
|
|
858
1482
|
const res = await fetchWithRetry(
|
|
859
1483
|
`${this.baseUrl}/responses`,
|
|
860
1484
|
{
|
|
861
1485
|
method: "POST",
|
|
862
|
-
headers
|
|
863
|
-
"Content-Type": "application/json",
|
|
864
|
-
Authorization: `Bearer ${this.apiKey}`
|
|
865
|
-
},
|
|
1486
|
+
headers,
|
|
866
1487
|
body: JSON.stringify(body),
|
|
867
1488
|
signal: options.signal
|
|
868
1489
|
},
|
|
869
|
-
{ governor: this.governor }
|
|
1490
|
+
{ governor: this.governor, provider: this.name }
|
|
870
1491
|
);
|
|
871
1492
|
if (!res.ok) {
|
|
872
1493
|
const errorBody = await res.text();
|
|
873
1494
|
const message = this.extractErrorMessage(errorBody, res.status);
|
|
874
|
-
throw
|
|
1495
|
+
throw buildProviderError({
|
|
1496
|
+
provider: this.name,
|
|
1497
|
+
status: res.status,
|
|
1498
|
+
headers: res.headers,
|
|
1499
|
+
message,
|
|
1500
|
+
body: errorBody
|
|
1501
|
+
});
|
|
875
1502
|
}
|
|
876
1503
|
if (!res.body) {
|
|
877
1504
|
throw new Error("OpenAI Responses stream has no body");
|
|
@@ -1160,7 +1787,12 @@ var OpenAIResponsesProvider = class {
|
|
|
1160
1787
|
}
|
|
1161
1788
|
case "response.failed": {
|
|
1162
1789
|
const errorMsg = data.response?.error?.message ?? data.response?.status_details?.error?.message ?? "Unknown error";
|
|
1163
|
-
throw new
|
|
1790
|
+
throw new ProviderError({
|
|
1791
|
+
provider: this.name,
|
|
1792
|
+
status: 0,
|
|
1793
|
+
retryable: false,
|
|
1794
|
+
message: `OpenAI Responses API error: ${errorMsg}`
|
|
1795
|
+
});
|
|
1164
1796
|
}
|
|
1165
1797
|
default:
|
|
1166
1798
|
return null;
|
|
@@ -1214,7 +1846,7 @@ function estimateAnthropicCost(model, inputTokens, outputTokens, cacheReadTokens
|
|
|
1214
1846
|
}
|
|
1215
1847
|
}
|
|
1216
1848
|
}
|
|
1217
|
-
if (!pricing) return 0;
|
|
1849
|
+
if (!pricing) return void 0;
|
|
1218
1850
|
const [inputRate, outputRate] = pricing;
|
|
1219
1851
|
const cacheRead = cacheReadTokens ?? 0;
|
|
1220
1852
|
const cacheWrite = cacheWriteTokens ?? 0;
|
|
@@ -1248,63 +1880,90 @@ function clampAnthropicEffort(model, effort) {
|
|
|
1248
1880
|
}
|
|
1249
1881
|
var AnthropicProvider = class {
|
|
1250
1882
|
name = "anthropic";
|
|
1883
|
+
/** Anthropic ignores native `json_schema` structurally — Axl uses a system
|
|
1884
|
+
* prompt JSON instruction + client-side Zod validation (see buildRequest). */
|
|
1885
|
+
nativeStructuredOutputSupport() {
|
|
1886
|
+
return "unsupported";
|
|
1887
|
+
}
|
|
1251
1888
|
baseUrl;
|
|
1252
|
-
|
|
1253
|
-
currentModel;
|
|
1889
|
+
apiKeySource;
|
|
1254
1890
|
governor;
|
|
1255
1891
|
constructor(options = {}) {
|
|
1256
|
-
this.
|
|
1892
|
+
this.apiKeySource = options.apiKey ?? process.env.ANTHROPIC_API_KEY ?? "";
|
|
1257
1893
|
this.baseUrl = (options.baseUrl ?? "https://api.anthropic.com/v1").replace(/\/$/, "");
|
|
1258
1894
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
1259
|
-
if (!this.
|
|
1895
|
+
if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
|
|
1896
|
+
throw new Error(
|
|
1897
|
+
"Anthropic API key is required. Set ANTHROPIC_API_KEY or pass apiKey in options."
|
|
1898
|
+
);
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
/** Resolve the API key for one request (supports an expiring-token callback). */
|
|
1902
|
+
async resolveKey() {
|
|
1903
|
+
const key = await resolveApiKey(this.apiKeySource);
|
|
1904
|
+
if (!key) {
|
|
1260
1905
|
throw new Error(
|
|
1261
1906
|
"Anthropic API key is required. Set ANTHROPIC_API_KEY or pass apiKey in options."
|
|
1262
1907
|
);
|
|
1263
1908
|
}
|
|
1909
|
+
return key;
|
|
1264
1910
|
}
|
|
1265
1911
|
// ---------------------------------------------------------------------------
|
|
1266
1912
|
// chat - non-streaming completion
|
|
1267
1913
|
// ---------------------------------------------------------------------------
|
|
1268
1914
|
async chat(messages, options) {
|
|
1269
|
-
|
|
1915
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
1270
1916
|
const body = this.buildRequestBody(messages, options, false);
|
|
1271
1917
|
const res = await fetchWithRetry(
|
|
1272
1918
|
`${this.baseUrl}/messages`,
|
|
1273
1919
|
{
|
|
1274
1920
|
method: "POST",
|
|
1275
|
-
headers
|
|
1921
|
+
headers,
|
|
1276
1922
|
body: JSON.stringify(body),
|
|
1277
1923
|
signal: options.signal
|
|
1278
1924
|
},
|
|
1279
|
-
{ governor: this.governor }
|
|
1925
|
+
{ governor: this.governor, provider: this.name }
|
|
1280
1926
|
);
|
|
1281
1927
|
if (!res.ok) {
|
|
1282
1928
|
const errorBody = await res.text();
|
|
1283
1929
|
const message = this.extractErrorMessage(errorBody, res.status);
|
|
1284
|
-
throw
|
|
1930
|
+
throw buildProviderError({
|
|
1931
|
+
provider: this.name,
|
|
1932
|
+
status: res.status,
|
|
1933
|
+
headers: res.headers,
|
|
1934
|
+
message,
|
|
1935
|
+
body: errorBody
|
|
1936
|
+
});
|
|
1285
1937
|
}
|
|
1286
1938
|
const json = await res.json();
|
|
1287
|
-
return this.parseResponse(json);
|
|
1939
|
+
return this.parseResponse(json, options.model);
|
|
1288
1940
|
}
|
|
1289
1941
|
// ---------------------------------------------------------------------------
|
|
1290
1942
|
// stream - SSE streaming completion
|
|
1291
1943
|
// ---------------------------------------------------------------------------
|
|
1292
1944
|
async *stream(messages, options) {
|
|
1945
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
1293
1946
|
const body = this.buildRequestBody(messages, options, true);
|
|
1294
1947
|
const res = await fetchWithRetry(
|
|
1295
1948
|
`${this.baseUrl}/messages`,
|
|
1296
1949
|
{
|
|
1297
1950
|
method: "POST",
|
|
1298
|
-
headers
|
|
1951
|
+
headers,
|
|
1299
1952
|
body: JSON.stringify(body),
|
|
1300
1953
|
signal: options.signal
|
|
1301
1954
|
},
|
|
1302
|
-
{ governor: this.governor }
|
|
1955
|
+
{ governor: this.governor, provider: this.name }
|
|
1303
1956
|
);
|
|
1304
1957
|
if (!res.ok) {
|
|
1305
1958
|
const errorBody = await res.text();
|
|
1306
1959
|
const message = this.extractErrorMessage(errorBody, res.status);
|
|
1307
|
-
throw
|
|
1960
|
+
throw buildProviderError({
|
|
1961
|
+
provider: this.name,
|
|
1962
|
+
status: res.status,
|
|
1963
|
+
headers: res.headers,
|
|
1964
|
+
message,
|
|
1965
|
+
body: errorBody
|
|
1966
|
+
});
|
|
1308
1967
|
}
|
|
1309
1968
|
if (!res.body) {
|
|
1310
1969
|
throw new Error("Anthropic stream response has no body");
|
|
@@ -1314,10 +1973,10 @@ var AnthropicProvider = class {
|
|
|
1314
1973
|
// ---------------------------------------------------------------------------
|
|
1315
1974
|
// Internal: request building
|
|
1316
1975
|
// ---------------------------------------------------------------------------
|
|
1317
|
-
buildHeaders() {
|
|
1976
|
+
buildHeaders(apiKey) {
|
|
1318
1977
|
return {
|
|
1319
1978
|
"Content-Type": "application/json",
|
|
1320
|
-
"x-api-key":
|
|
1979
|
+
"x-api-key": apiKey,
|
|
1321
1980
|
"anthropic-version": ANTHROPIC_API_VERSION
|
|
1322
1981
|
};
|
|
1323
1982
|
}
|
|
@@ -1502,7 +2161,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
1502
2161
|
// ---------------------------------------------------------------------------
|
|
1503
2162
|
// Internal: response parsing
|
|
1504
2163
|
// ---------------------------------------------------------------------------
|
|
1505
|
-
parseResponse(json) {
|
|
2164
|
+
parseResponse(json, model) {
|
|
1506
2165
|
let content = "";
|
|
1507
2166
|
let thinkingContent = "";
|
|
1508
2167
|
const toolCalls = [];
|
|
@@ -1531,13 +2190,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
1531
2190
|
total_tokens: totalInput + json.usage.output_tokens,
|
|
1532
2191
|
cached_tokens: cacheRead > 0 ? cacheRead : void 0
|
|
1533
2192
|
} : void 0;
|
|
1534
|
-
const cost = json.usage ? estimateAnthropicCost(
|
|
1535
|
-
this.currentModel ?? "",
|
|
1536
|
-
totalInput,
|
|
1537
|
-
json.usage.output_tokens,
|
|
1538
|
-
cacheRead,
|
|
1539
|
-
cacheWrite
|
|
1540
|
-
) : void 0;
|
|
2193
|
+
const cost = json.usage ? estimateAnthropicCost(model, totalInput, json.usage.output_tokens, cacheRead, cacheWrite) : void 0;
|
|
1541
2194
|
return {
|
|
1542
2195
|
content,
|
|
1543
2196
|
thinking_content: thinkingContent || void 0,
|
|
@@ -1713,6 +2366,15 @@ function sanitizeSchemaForGemini(schema) {
|
|
|
1713
2366
|
}
|
|
1714
2367
|
return out;
|
|
1715
2368
|
}
|
|
2369
|
+
function parseGeminiFunctionResponse(content) {
|
|
2370
|
+
let parsed;
|
|
2371
|
+
try {
|
|
2372
|
+
parsed = JSON.parse(content);
|
|
2373
|
+
} catch {
|
|
2374
|
+
return { result: content };
|
|
2375
|
+
}
|
|
2376
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { result: parsed };
|
|
2377
|
+
}
|
|
1716
2378
|
var GEMINI_PRICING = {
|
|
1717
2379
|
"gemini-2.5-pro": [125e-8, 1e-5],
|
|
1718
2380
|
"gemini-2.5-flash": [3e-7, 25e-7],
|
|
@@ -1739,7 +2401,7 @@ function estimateGeminiCost(model, inputTokens, outputTokens, cachedTokens) {
|
|
|
1739
2401
|
}
|
|
1740
2402
|
}
|
|
1741
2403
|
}
|
|
1742
|
-
if (!pricing) return 0;
|
|
2404
|
+
if (!pricing) return void 0;
|
|
1743
2405
|
const [inputRate, outputRate] = pricing;
|
|
1744
2406
|
const cached = cachedTokens ?? 0;
|
|
1745
2407
|
const inputCost = (inputTokens - cached) * inputRate + cached * inputRate * 0.1;
|
|
@@ -1784,40 +2446,61 @@ function warnGemini3xEffortNone(model) {
|
|
|
1784
2446
|
}
|
|
1785
2447
|
var GeminiProvider = class {
|
|
1786
2448
|
name = "google";
|
|
2449
|
+
/** Gemini accepts a `responseSchema` but `sanitizeSchemaForGemini` strips
|
|
2450
|
+
* keywords it doesn't support (`$ref`/`$defs`/`additionalProperties`/…), so a
|
|
2451
|
+
* derived schema can lose constraints — lossy, not faithful. */
|
|
2452
|
+
nativeStructuredOutputSupport() {
|
|
2453
|
+
return "lossy";
|
|
2454
|
+
}
|
|
1787
2455
|
baseUrl;
|
|
1788
|
-
|
|
2456
|
+
apiKeySource;
|
|
1789
2457
|
callCounter = 0;
|
|
1790
2458
|
governor;
|
|
1791
2459
|
constructor(options = {}) {
|
|
1792
|
-
this.
|
|
2460
|
+
this.apiKeySource = options.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY ?? "";
|
|
1793
2461
|
this.baseUrl = (options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta").replace(
|
|
1794
2462
|
/\/$/,
|
|
1795
2463
|
""
|
|
1796
2464
|
);
|
|
1797
2465
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
1798
|
-
if (!this.
|
|
2466
|
+
if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
|
|
1799
2467
|
throw new Error("Google API key is required. Set GOOGLE_API_KEY or pass apiKey in options.");
|
|
1800
2468
|
}
|
|
1801
2469
|
}
|
|
2470
|
+
/** Resolve the API key for one request (supports an expiring-token callback). */
|
|
2471
|
+
async resolveKey() {
|
|
2472
|
+
const key = await resolveApiKey(this.apiKeySource);
|
|
2473
|
+
if (!key) {
|
|
2474
|
+
throw new Error("Google API key is required. Set GOOGLE_API_KEY or pass apiKey in options.");
|
|
2475
|
+
}
|
|
2476
|
+
return key;
|
|
2477
|
+
}
|
|
1802
2478
|
// ---------------------------------------------------------------------------
|
|
1803
2479
|
// chat - non-streaming completion
|
|
1804
2480
|
// ---------------------------------------------------------------------------
|
|
1805
2481
|
async chat(messages, options) {
|
|
2482
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
1806
2483
|
const body = this.buildRequestBody(messages, options);
|
|
1807
2484
|
const res = await fetchWithRetry(
|
|
1808
2485
|
`${this.baseUrl}/models/${options.model}:generateContent`,
|
|
1809
2486
|
{
|
|
1810
2487
|
method: "POST",
|
|
1811
|
-
headers
|
|
2488
|
+
headers,
|
|
1812
2489
|
body: JSON.stringify(body),
|
|
1813
2490
|
signal: options.signal
|
|
1814
2491
|
},
|
|
1815
|
-
{ governor: this.governor }
|
|
2492
|
+
{ governor: this.governor, provider: this.name }
|
|
1816
2493
|
);
|
|
1817
2494
|
if (!res.ok) {
|
|
1818
2495
|
const errorBody = await res.text();
|
|
1819
2496
|
const message = this.extractErrorMessage(errorBody, res.status);
|
|
1820
|
-
throw
|
|
2497
|
+
throw buildProviderError({
|
|
2498
|
+
provider: this.name,
|
|
2499
|
+
status: res.status,
|
|
2500
|
+
headers: res.headers,
|
|
2501
|
+
message,
|
|
2502
|
+
body: errorBody
|
|
2503
|
+
});
|
|
1821
2504
|
}
|
|
1822
2505
|
const json = await res.json();
|
|
1823
2506
|
return this.parseResponse(json, options.model);
|
|
@@ -1826,21 +2509,28 @@ var GeminiProvider = class {
|
|
|
1826
2509
|
// stream - SSE streaming completion
|
|
1827
2510
|
// ---------------------------------------------------------------------------
|
|
1828
2511
|
async *stream(messages, options) {
|
|
2512
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
1829
2513
|
const body = this.buildRequestBody(messages, options);
|
|
1830
2514
|
const res = await fetchWithRetry(
|
|
1831
2515
|
`${this.baseUrl}/models/${options.model}:streamGenerateContent?alt=sse`,
|
|
1832
2516
|
{
|
|
1833
2517
|
method: "POST",
|
|
1834
|
-
headers
|
|
2518
|
+
headers,
|
|
1835
2519
|
body: JSON.stringify(body),
|
|
1836
2520
|
signal: options.signal
|
|
1837
2521
|
},
|
|
1838
|
-
{ governor: this.governor }
|
|
2522
|
+
{ governor: this.governor, provider: this.name }
|
|
1839
2523
|
);
|
|
1840
2524
|
if (!res.ok) {
|
|
1841
2525
|
const errorBody = await res.text();
|
|
1842
2526
|
const message = this.extractErrorMessage(errorBody, res.status);
|
|
1843
|
-
throw
|
|
2527
|
+
throw buildProviderError({
|
|
2528
|
+
provider: this.name,
|
|
2529
|
+
status: res.status,
|
|
2530
|
+
headers: res.headers,
|
|
2531
|
+
message,
|
|
2532
|
+
body: errorBody
|
|
2533
|
+
});
|
|
1844
2534
|
}
|
|
1845
2535
|
if (!res.body) {
|
|
1846
2536
|
throw new Error("Gemini stream response has no body");
|
|
@@ -1850,10 +2540,10 @@ var GeminiProvider = class {
|
|
|
1850
2540
|
// ---------------------------------------------------------------------------
|
|
1851
2541
|
// Internal: request building
|
|
1852
2542
|
// ---------------------------------------------------------------------------
|
|
1853
|
-
buildHeaders() {
|
|
2543
|
+
buildHeaders(apiKey) {
|
|
1854
2544
|
return {
|
|
1855
2545
|
"Content-Type": "application/json",
|
|
1856
|
-
"x-goog-api-key":
|
|
2546
|
+
"x-goog-api-key": apiKey
|
|
1857
2547
|
};
|
|
1858
2548
|
}
|
|
1859
2549
|
extractErrorMessage(body, status) {
|
|
@@ -2020,15 +2710,9 @@ var GeminiProvider = class {
|
|
|
2020
2710
|
}
|
|
2021
2711
|
} else if (msg.role === "tool") {
|
|
2022
2712
|
const functionName = toolCallIdToName.get(msg.tool_call_id) ?? "unknown";
|
|
2023
|
-
let responseData;
|
|
2024
|
-
try {
|
|
2025
|
-
responseData = JSON.parse(msg.content);
|
|
2026
|
-
} catch {
|
|
2027
|
-
responseData = { result: msg.content };
|
|
2028
|
-
}
|
|
2029
2713
|
const functionResponse = {
|
|
2030
2714
|
name: functionName,
|
|
2031
|
-
response:
|
|
2715
|
+
response: parseGeminiFunctionResponse(msg.content)
|
|
2032
2716
|
};
|
|
2033
2717
|
if (msg.tool_call_id && geminiNativeIds.has(msg.tool_call_id)) {
|
|
2034
2718
|
functionResponse.id = msg.tool_call_id;
|
|
@@ -2210,6 +2894,203 @@ var GeminiProvider = class {
|
|
|
2210
2894
|
}
|
|
2211
2895
|
};
|
|
2212
2896
|
|
|
2897
|
+
// src/providers/profiles/openrouter.ts
|
|
2898
|
+
var OPENROUTER_PROFILE = {
|
|
2899
|
+
name: "openrouter",
|
|
2900
|
+
label: "OpenRouter",
|
|
2901
|
+
defaultBaseUrl: "https://openrouter.ai/api/v1",
|
|
2902
|
+
envApiKey: "OPENROUTER_API_KEY",
|
|
2903
|
+
envBaseUrl: "OPENROUTER_BASE_URL",
|
|
2904
|
+
pricing: { kind: "from-response" },
|
|
2905
|
+
// Ask OpenRouter to report per-call cost in usage.cost.
|
|
2906
|
+
requestDefaults: { usage: { include: true } },
|
|
2907
|
+
reasoning: {
|
|
2908
|
+
// OpenRouter's effort vocabulary is low|medium|high; xhigh/max clamp to high.
|
|
2909
|
+
emit: reasoningObjectEmit(
|
|
2910
|
+
(effort) => effort === "xhigh" || effort === "max" ? "high" : effort
|
|
2911
|
+
),
|
|
2912
|
+
capture: "reasoning_details",
|
|
2913
|
+
roundTrip: "on-tool-call-turns"
|
|
2914
|
+
},
|
|
2915
|
+
// OpenRouter normalizes to max_tokens across its backends.
|
|
2916
|
+
maxTokensField: "max_tokens"
|
|
2917
|
+
};
|
|
2918
|
+
|
|
2919
|
+
// src/providers/profiles/azure.ts
|
|
2920
|
+
var AZURE_PROFILE = {
|
|
2921
|
+
name: "azure",
|
|
2922
|
+
label: "Azure OpenAI",
|
|
2923
|
+
// Never actually used — requireExplicitBaseUrl forces a real value first.
|
|
2924
|
+
defaultBaseUrl: "https://YOUR-RESOURCE.openai.azure.com/openai/v1",
|
|
2925
|
+
requireExplicitBaseUrl: true,
|
|
2926
|
+
envApiKey: "AZURE_OPENAI_API_KEY",
|
|
2927
|
+
envBaseUrl: "AZURE_OPENAI_BASE_URL",
|
|
2928
|
+
authHeader: "api-key",
|
|
2929
|
+
pricing: { kind: "table", table: OPENAI_PRICING },
|
|
2930
|
+
reasoning: { emit: openaiReasoningEmit, capture: "none" },
|
|
2931
|
+
roleFor: (role, model) => role === "system" && isOSeriesModel(model) ? "developer" : role,
|
|
2932
|
+
maxTokensField: "max_completion_tokens",
|
|
2933
|
+
parallelToolCalls: (model) => !isOSeriesModel(model)
|
|
2934
|
+
};
|
|
2935
|
+
|
|
2936
|
+
// src/providers/profiles/xai.ts
|
|
2937
|
+
var acceptsReasoningEffort = (model) => model.startsWith("grok-3-mini");
|
|
2938
|
+
var isReasoningModel = (model) => /^grok-(3-mini|4)/.test(model);
|
|
2939
|
+
var XAI_PROFILE = {
|
|
2940
|
+
name: "xai",
|
|
2941
|
+
label: "xAI",
|
|
2942
|
+
defaultBaseUrl: "https://api.x.ai/v1",
|
|
2943
|
+
envApiKey: "XAI_API_KEY",
|
|
2944
|
+
envBaseUrl: "XAI_BASE_URL",
|
|
2945
|
+
pricing: { kind: "unknown" },
|
|
2946
|
+
reasoning: {
|
|
2947
|
+
emit: reasoningEffortEmit((resolved, model) => {
|
|
2948
|
+
if (!acceptsReasoningEffort(model)) return void 0;
|
|
2949
|
+
if (resolved.thinkingDisabled || !resolved.activeEffort) return void 0;
|
|
2950
|
+
return resolved.activeEffort === "low" ? "low" : "high";
|
|
2951
|
+
}),
|
|
2952
|
+
capture: "reasoning"
|
|
2953
|
+
},
|
|
2954
|
+
capabilities: {
|
|
2955
|
+
forbiddenParams: (model) => isReasoningModel(model) ? ["stop", "presence_penalty", "frequency_penalty"] : []
|
|
2956
|
+
}
|
|
2957
|
+
};
|
|
2958
|
+
|
|
2959
|
+
// src/providers/profiles/deepseek.ts
|
|
2960
|
+
var isReasoner = (model) => /reasoner|v4/i.test(model);
|
|
2961
|
+
var deepseekEmit = (_body, _resolved, model) => ({
|
|
2962
|
+
stripTemperature: isReasoner(model)
|
|
2963
|
+
});
|
|
2964
|
+
var DEEPSEEK_PROFILE = {
|
|
2965
|
+
name: "deepseek",
|
|
2966
|
+
label: "DeepSeek",
|
|
2967
|
+
defaultBaseUrl: "https://api.deepseek.com/v1",
|
|
2968
|
+
envApiKey: "DEEPSEEK_API_KEY",
|
|
2969
|
+
envBaseUrl: "DEEPSEEK_BASE_URL",
|
|
2970
|
+
pricing: { kind: "unknown" },
|
|
2971
|
+
reasoning: {
|
|
2972
|
+
emit: deepseekEmit,
|
|
2973
|
+
capture: "reasoning_content",
|
|
2974
|
+
roundTrip: "on-tool-call-turns"
|
|
2975
|
+
},
|
|
2976
|
+
capabilities: {
|
|
2977
|
+
supportsJsonSchema: false,
|
|
2978
|
+
forbiddenParams: (model) => isReasoner(model) ? ["top_p", "presence_penalty", "frequency_penalty"] : []
|
|
2979
|
+
}
|
|
2980
|
+
};
|
|
2981
|
+
|
|
2982
|
+
// src/providers/profiles/mistral.ts
|
|
2983
|
+
var acceptsReasoningEffort2 = (model) => /^mistral-(small|medium)/i.test(model);
|
|
2984
|
+
var MISTRAL_PROFILE = {
|
|
2985
|
+
name: "mistral",
|
|
2986
|
+
label: "Mistral",
|
|
2987
|
+
defaultBaseUrl: "https://api.mistral.ai/v1",
|
|
2988
|
+
envApiKey: "MISTRAL_API_KEY",
|
|
2989
|
+
envBaseUrl: "MISTRAL_BASE_URL",
|
|
2990
|
+
pricing: { kind: "unknown" },
|
|
2991
|
+
reasoning: {
|
|
2992
|
+
emit: reasoningEffortEmit((resolved, model) => {
|
|
2993
|
+
if (!acceptsReasoningEffort2(model)) return void 0;
|
|
2994
|
+
if (resolved.thinkingDisabled || !resolved.activeEffort) return void 0;
|
|
2995
|
+
return "high";
|
|
2996
|
+
}),
|
|
2997
|
+
capture: "reasoning_content"
|
|
2998
|
+
}
|
|
2999
|
+
};
|
|
3000
|
+
|
|
3001
|
+
// src/providers/profiles/groq.ts
|
|
3002
|
+
var isGptOss = (model) => /gpt-oss/i.test(model);
|
|
3003
|
+
var GROQ_PROFILE = {
|
|
3004
|
+
name: "groq",
|
|
3005
|
+
label: "Groq",
|
|
3006
|
+
defaultBaseUrl: "https://api.groq.com/openai/v1",
|
|
3007
|
+
envApiKey: "GROQ_API_KEY",
|
|
3008
|
+
envBaseUrl: "GROQ_BASE_URL",
|
|
3009
|
+
pricing: { kind: "unknown" },
|
|
3010
|
+
reasoning: {
|
|
3011
|
+
emit: reasoningEffortEmit((resolved, model) => {
|
|
3012
|
+
if (!isGptOss(model) || resolved.thinkingDisabled || !resolved.activeEffort) {
|
|
3013
|
+
return void 0;
|
|
3014
|
+
}
|
|
3015
|
+
const e = resolved.activeEffort;
|
|
3016
|
+
return e === "xhigh" || e === "max" ? "high" : e;
|
|
3017
|
+
}),
|
|
3018
|
+
capture: "reasoning"
|
|
3019
|
+
},
|
|
3020
|
+
capabilities: {
|
|
3021
|
+
emitsMessageName: false,
|
|
3022
|
+
// Groq's `response_format: json_schema` is supported only on a subset of
|
|
3023
|
+
// models — the `openai/gpt-oss-*` family accepts it; llama/gemma/etc 400
|
|
3024
|
+
// with "This model does not support response format `json_schema`".
|
|
3025
|
+
// Per-model so `nativeStructuredOutput` engages the native path only where
|
|
3026
|
+
// it works and otherwise downgrades cleanly to `json_object` (verified live).
|
|
3027
|
+
supportsJsonSchema: isGptOss
|
|
3028
|
+
}
|
|
3029
|
+
};
|
|
3030
|
+
|
|
3031
|
+
// src/providers/profiles/bedrock.ts
|
|
3032
|
+
var isGptOss2 = (model) => /gpt-oss/i.test(model);
|
|
3033
|
+
var BEDROCK_PROFILE = {
|
|
3034
|
+
name: "bedrock",
|
|
3035
|
+
label: "AWS Bedrock",
|
|
3036
|
+
// Never used — requireExplicitBaseUrl forces a real (region-specific) value first.
|
|
3037
|
+
defaultBaseUrl: "https://bedrock-mantle.us-east-1.api.aws/v1",
|
|
3038
|
+
requireExplicitBaseUrl: true,
|
|
3039
|
+
envApiKey: "AWS_BEARER_TOKEN_BEDROCK",
|
|
3040
|
+
envBaseUrl: "BEDROCK_BASE_URL",
|
|
3041
|
+
pricing: { kind: "unknown" },
|
|
3042
|
+
reasoning: {
|
|
3043
|
+
emit: reasoningEffortEmit((resolved, model) => {
|
|
3044
|
+
if (!isGptOss2(model) || resolved.thinkingDisabled || !resolved.activeEffort) {
|
|
3045
|
+
return void 0;
|
|
3046
|
+
}
|
|
3047
|
+
const e = resolved.activeEffort;
|
|
3048
|
+
return e === "xhigh" || e === "max" ? "high" : e;
|
|
3049
|
+
}),
|
|
3050
|
+
capture: "reasoning"
|
|
3051
|
+
}
|
|
3052
|
+
};
|
|
3053
|
+
|
|
3054
|
+
// src/providers/profiles/local.ts
|
|
3055
|
+
function localProfile(name, label, port, envApiKey) {
|
|
3056
|
+
return {
|
|
3057
|
+
name,
|
|
3058
|
+
label,
|
|
3059
|
+
defaultBaseUrl: `http://localhost:${port}/v1`,
|
|
3060
|
+
envApiKey,
|
|
3061
|
+
envBaseUrl: `${envApiKey.replace(/_API_KEY$/, "")}_BASE_URL`,
|
|
3062
|
+
allowMissingApiKey: true,
|
|
3063
|
+
pricing: { kind: "zero" },
|
|
3064
|
+
reasoning: { emit: () => {
|
|
3065
|
+
}, capture: "think_tags" },
|
|
3066
|
+
maxTokensField: "max_tokens"
|
|
3067
|
+
};
|
|
3068
|
+
}
|
|
3069
|
+
var OLLAMA_PROFILE = localProfile("ollama", "Ollama", 11434, "OLLAMA_API_KEY");
|
|
3070
|
+
var VLLM_PROFILE = localProfile("vllm", "vLLM", 8e3, "VLLM_API_KEY");
|
|
3071
|
+
var LMSTUDIO_PROFILE = localProfile("lmstudio", "LM Studio", 1234, "LMSTUDIO_API_KEY");
|
|
3072
|
+
var LLAMACPP_PROFILE = localProfile("llamacpp", "llama.cpp", 8080, "LLAMACPP_API_KEY");
|
|
3073
|
+
var SGLANG_PROFILE = localProfile("sglang", "SGLang", 3e4, "SGLANG_API_KEY");
|
|
3074
|
+
var LOCAL_PROFILES = [
|
|
3075
|
+
OLLAMA_PROFILE,
|
|
3076
|
+
VLLM_PROFILE,
|
|
3077
|
+
LMSTUDIO_PROFILE,
|
|
3078
|
+
LLAMACPP_PROFILE,
|
|
3079
|
+
SGLANG_PROFILE
|
|
3080
|
+
];
|
|
3081
|
+
|
|
3082
|
+
// src/providers/profiles/index.ts
|
|
3083
|
+
var BUILTIN_PROFILES = [
|
|
3084
|
+
OPENROUTER_PROFILE,
|
|
3085
|
+
AZURE_PROFILE,
|
|
3086
|
+
XAI_PROFILE,
|
|
3087
|
+
DEEPSEEK_PROFILE,
|
|
3088
|
+
MISTRAL_PROFILE,
|
|
3089
|
+
GROQ_PROFILE,
|
|
3090
|
+
BEDROCK_PROFILE,
|
|
3091
|
+
...LOCAL_PROFILES
|
|
3092
|
+
];
|
|
3093
|
+
|
|
2213
3094
|
// src/providers/registry.ts
|
|
2214
3095
|
var builtinFactories = {
|
|
2215
3096
|
openai: (config) => {
|
|
@@ -2249,6 +3130,23 @@ var builtinFactories = {
|
|
|
2249
3130
|
});
|
|
2250
3131
|
}
|
|
2251
3132
|
};
|
|
3133
|
+
function presetFactory(profile) {
|
|
3134
|
+
return (config) => {
|
|
3135
|
+
const opts = config.providers?.[profile.name] ?? {};
|
|
3136
|
+
return new OpenAICompatibleProvider({
|
|
3137
|
+
profile,
|
|
3138
|
+
apiKey: opts.apiKey,
|
|
3139
|
+
baseUrl: opts.baseUrl,
|
|
3140
|
+
authHeader: opts.authHeader,
|
|
3141
|
+
rateLimit: opts.rateLimit
|
|
3142
|
+
});
|
|
3143
|
+
};
|
|
3144
|
+
}
|
|
3145
|
+
for (const profile of BUILTIN_PROFILES) {
|
|
3146
|
+
if (!(profile.name in builtinFactories)) {
|
|
3147
|
+
builtinFactories[profile.name] = presetFactory(profile);
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
2252
3150
|
var ProviderRegistry = class {
|
|
2253
3151
|
/** Cached provider instances, keyed by provider name */
|
|
2254
3152
|
instances = /* @__PURE__ */ new Map();
|
|
@@ -2313,175 +3211,58 @@ var ProviderRegistry = class {
|
|
|
2313
3211
|
}
|
|
2314
3212
|
throw new Error(`Unknown provider "${name}". Registered providers: ${this.list().join(", ")}`);
|
|
2315
3213
|
}
|
|
2316
|
-
/**
|
|
2317
|
-
* Resolve a "provider:model" URI string into a Provider instance and model name.
|
|
2318
|
-
*
|
|
2319
|
-
* Supported formats:
|
|
2320
|
-
* - "openai:gpt-4o" -> provider=openai, model=gpt-4o
|
|
2321
|
-
* - "anthropic:claude-3" -> provider=anthropic, model=claude-3
|
|
2322
|
-
* - "gpt-4o" -> uses defaultProvider from config, model=gpt-4o
|
|
2323
|
-
* - undefined / empty -> uses defaultProvider and defaultModel from config
|
|
2324
|
-
*
|
|
2325
|
-
* @param uri Provider:model string, or just a model name
|
|
2326
|
-
* @param config Axl configuration for provider options and defaults
|
|
2327
|
-
*/
|
|
2328
|
-
resolve(uri, config = {}) {
|
|
2329
|
-
if (!uri) {
|
|
2330
|
-
const providerName2 = config.defaultProvider ?? "openai";
|
|
2331
|
-
const model2 = config.defaultModel ?? "gpt-4o";
|
|
2332
|
-
return { provider: this.get(providerName2, config), model: model2 };
|
|
2333
|
-
}
|
|
2334
|
-
const colonIndex = uri.indexOf(":");
|
|
2335
|
-
if (colonIndex === -1) {
|
|
2336
|
-
const providerName2 = config.defaultProvider ?? "openai";
|
|
2337
|
-
return { provider: this.get(providerName2, config), model: uri };
|
|
2338
|
-
}
|
|
2339
|
-
const providerName = uri.slice(0, colonIndex);
|
|
2340
|
-
const model = uri.slice(colonIndex + 1);
|
|
2341
|
-
if (!providerName || !model) {
|
|
2342
|
-
throw new Error(
|
|
2343
|
-
`Invalid provider URI "${uri}". Expected format: "provider:model" (e.g. "openai:gpt-4o")`
|
|
2344
|
-
);
|
|
2345
|
-
}
|
|
2346
|
-
return { provider: this.get(providerName, config), model };
|
|
2347
|
-
}
|
|
2348
|
-
/**
|
|
2349
|
-
* Clear all cached provider instances. Useful for testing or reconfiguration.
|
|
2350
|
-
*/
|
|
2351
|
-
clearCache() {
|
|
2352
|
-
this.instances.clear();
|
|
2353
|
-
}
|
|
2354
|
-
/**
|
|
2355
|
-
* Clear all registered factories (including built-ins).
|
|
2356
|
-
* Useful for test runtimes where only explicitly registered instances should be used.
|
|
2357
|
-
*/
|
|
2358
|
-
clearFactories() {
|
|
2359
|
-
this.factories.clear();
|
|
2360
|
-
}
|
|
2361
|
-
};
|
|
2362
|
-
var defaultRegistry = new ProviderRegistry();
|
|
2363
|
-
|
|
2364
|
-
// src/context.ts
|
|
2365
|
-
var import_node_async_hooks = require("async_hooks");
|
|
2366
|
-
var import_node_crypto = require("crypto");
|
|
2367
|
-
var import_zod = require("zod");
|
|
2368
|
-
|
|
2369
|
-
// src/errors.ts
|
|
2370
|
-
var AxlError = class extends Error {
|
|
2371
|
-
code;
|
|
2372
|
-
constructor(code, message) {
|
|
2373
|
-
super(message);
|
|
2374
|
-
this.name = "AxlError";
|
|
2375
|
-
this.code = code;
|
|
2376
|
-
}
|
|
2377
|
-
};
|
|
2378
|
-
var VerifyError = class extends AxlError {
|
|
2379
|
-
lastOutput;
|
|
2380
|
-
zodError;
|
|
2381
|
-
retries;
|
|
2382
|
-
constructor(lastOutput, zodError, retries) {
|
|
2383
|
-
super("VERIFY_ERROR", `Schema validation failed after ${retries} retries: ${zodError.message}`);
|
|
2384
|
-
this.name = "VerifyError";
|
|
2385
|
-
this.lastOutput = lastOutput;
|
|
2386
|
-
this.zodError = zodError;
|
|
2387
|
-
this.retries = retries;
|
|
2388
|
-
}
|
|
2389
|
-
};
|
|
2390
|
-
var QuorumNotMet = class extends AxlError {
|
|
2391
|
-
results;
|
|
2392
|
-
constructor(required, actual, results) {
|
|
2393
|
-
super("QUORUM_NOT_MET", `Quorum not met: needed ${required} successes, got ${actual}`);
|
|
2394
|
-
this.name = "QuorumNotMet";
|
|
2395
|
-
this.results = results;
|
|
2396
|
-
}
|
|
2397
|
-
};
|
|
2398
|
-
var NoConsensus = class extends AxlError {
|
|
2399
|
-
constructor(reason) {
|
|
2400
|
-
super("NO_CONSENSUS", `No consensus: ${reason}`);
|
|
2401
|
-
this.name = "NoConsensus";
|
|
2402
|
-
}
|
|
2403
|
-
};
|
|
2404
|
-
var TimeoutError = class extends AxlError {
|
|
2405
|
-
constructor(operation, timeoutMs) {
|
|
2406
|
-
super("TIMEOUT", `${operation} exceeded timeout of ${timeoutMs}ms`);
|
|
2407
|
-
this.name = "TimeoutError";
|
|
2408
|
-
}
|
|
2409
|
-
};
|
|
2410
|
-
function formatBudgetCost(cost) {
|
|
2411
|
-
if (Number.isNaN(cost)) return "$NaN";
|
|
2412
|
-
if (cost === Infinity) return "$Infinity";
|
|
2413
|
-
if (cost === -Infinity) return "-$Infinity";
|
|
2414
|
-
if (cost === 0) return "$0.00";
|
|
2415
|
-
const sign = cost < 0 ? "-" : "";
|
|
2416
|
-
const abs = Math.abs(cost);
|
|
2417
|
-
if (abs < 1e-6) return `${sign}< $0.000001`;
|
|
2418
|
-
if (abs < 1e-4) {
|
|
2419
|
-
const [mantissa, exponent] = abs.toExponential(2).split("e");
|
|
2420
|
-
return `${sign}$${mantissa}e${parseInt(exponent, 10)}`;
|
|
2421
|
-
}
|
|
2422
|
-
if (abs < 0.01) return `${sign}$${abs.toFixed(6)}`;
|
|
2423
|
-
return `${sign}$${abs.toFixed(2)}`;
|
|
2424
|
-
}
|
|
2425
|
-
var BudgetExceededError = class extends AxlError {
|
|
2426
|
-
limit;
|
|
2427
|
-
spent;
|
|
2428
|
-
policy;
|
|
2429
|
-
constructor(limit, spent, policy) {
|
|
2430
|
-
super(
|
|
2431
|
-
"BUDGET_EXCEEDED",
|
|
2432
|
-
`Budget exceeded: spent ${formatBudgetCost(spent)} of ${formatBudgetCost(
|
|
2433
|
-
limit
|
|
2434
|
-
)} limit (policy: ${policy})`
|
|
2435
|
-
);
|
|
2436
|
-
this.name = "BudgetExceededError";
|
|
2437
|
-
this.limit = limit;
|
|
2438
|
-
this.spent = spent;
|
|
2439
|
-
this.policy = policy;
|
|
2440
|
-
}
|
|
2441
|
-
};
|
|
2442
|
-
var MaxTurnsError = class extends AxlError {
|
|
2443
|
-
maxTurns;
|
|
2444
|
-
constructor(operation, maxTurns) {
|
|
2445
|
-
super("MAX_TURNS", `${operation} exceeded maximum of ${maxTurns} turns`);
|
|
2446
|
-
this.name = "MaxTurnsError";
|
|
2447
|
-
this.maxTurns = maxTurns;
|
|
2448
|
-
}
|
|
2449
|
-
};
|
|
2450
|
-
var GuardrailError = class extends AxlError {
|
|
2451
|
-
guardrailType;
|
|
2452
|
-
reason;
|
|
2453
|
-
constructor(guardrailType, reason) {
|
|
2454
|
-
super("GUARDRAIL_BLOCKED", `${guardrailType} guardrail blocked: ${reason}`);
|
|
2455
|
-
this.name = "GuardrailError";
|
|
2456
|
-
this.guardrailType = guardrailType;
|
|
2457
|
-
this.reason = reason;
|
|
3214
|
+
/**
|
|
3215
|
+
* Resolve a "provider:model" URI string into a Provider instance and model name.
|
|
3216
|
+
*
|
|
3217
|
+
* Supported formats:
|
|
3218
|
+
* - "openai:gpt-4o" -> provider=openai, model=gpt-4o
|
|
3219
|
+
* - "anthropic:claude-3" -> provider=anthropic, model=claude-3
|
|
3220
|
+
* - "gpt-4o" -> uses defaultProvider from config, model=gpt-4o
|
|
3221
|
+
* - undefined / empty -> uses defaultProvider and defaultModel from config
|
|
3222
|
+
*
|
|
3223
|
+
* @param uri Provider:model string, or just a model name
|
|
3224
|
+
* @param config Axl configuration for provider options and defaults
|
|
3225
|
+
*/
|
|
3226
|
+
resolve(uri, config = {}) {
|
|
3227
|
+
if (!uri) {
|
|
3228
|
+
const providerName2 = config.defaultProvider ?? "openai";
|
|
3229
|
+
const model2 = config.defaultModel ?? "gpt-4o";
|
|
3230
|
+
return { provider: this.get(providerName2, config), model: model2 };
|
|
3231
|
+
}
|
|
3232
|
+
const colonIndex = uri.indexOf(":");
|
|
3233
|
+
if (colonIndex === -1) {
|
|
3234
|
+
const providerName2 = config.defaultProvider ?? "openai";
|
|
3235
|
+
return { provider: this.get(providerName2, config), model: uri };
|
|
3236
|
+
}
|
|
3237
|
+
const providerName = uri.slice(0, colonIndex);
|
|
3238
|
+
const model = uri.slice(colonIndex + 1);
|
|
3239
|
+
if (!providerName || !model) {
|
|
3240
|
+
throw new Error(
|
|
3241
|
+
`Invalid provider URI "${uri}". Expected format: "provider:model" (e.g. "openai:gpt-4o")`
|
|
3242
|
+
);
|
|
3243
|
+
}
|
|
3244
|
+
return { provider: this.get(providerName, config), model };
|
|
2458
3245
|
}
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
constructor(lastOutput, reason, retries) {
|
|
2465
|
-
super("VALIDATION_ERROR", `Validation failed after ${retries} retries: ${reason}`);
|
|
2466
|
-
this.name = "ValidationError";
|
|
2467
|
-
this.lastOutput = lastOutput;
|
|
2468
|
-
this.reason = reason;
|
|
2469
|
-
this.retries = retries;
|
|
3246
|
+
/**
|
|
3247
|
+
* Clear all cached provider instances. Useful for testing or reconfiguration.
|
|
3248
|
+
*/
|
|
3249
|
+
clearCache() {
|
|
3250
|
+
this.instances.clear();
|
|
2470
3251
|
}
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
"TOOL_DENIED",
|
|
2478
|
-
`Agent "${agentName}" attempted to call tool "${toolName}" which is not in its ACL`
|
|
2479
|
-
);
|
|
2480
|
-
this.name = "ToolDenied";
|
|
2481
|
-
this.toolName = toolName;
|
|
2482
|
-
this.agentName = agentName;
|
|
3252
|
+
/**
|
|
3253
|
+
* Clear all registered factories (including built-ins).
|
|
3254
|
+
* Useful for test runtimes where only explicitly registered instances should be used.
|
|
3255
|
+
*/
|
|
3256
|
+
clearFactories() {
|
|
3257
|
+
this.factories.clear();
|
|
2483
3258
|
}
|
|
2484
3259
|
};
|
|
3260
|
+
var defaultRegistry = new ProviderRegistry();
|
|
3261
|
+
|
|
3262
|
+
// src/context.ts
|
|
3263
|
+
var import_node_async_hooks = require("async_hooks");
|
|
3264
|
+
var import_node_crypto = require("crypto");
|
|
3265
|
+
var import_zod = require("zod");
|
|
2485
3266
|
|
|
2486
3267
|
// src/partial-json.ts
|
|
2487
3268
|
var MAX_DEPTH = 256;
|
|
@@ -3057,6 +3838,14 @@ var COST_BEARING_LEAF_TYPES = [
|
|
|
3057
3838
|
"memory_recall"
|
|
3058
3839
|
];
|
|
3059
3840
|
var COST_LEAF_SET = new Set(COST_BEARING_LEAF_TYPES);
|
|
3841
|
+
function hasPositiveTokens(event) {
|
|
3842
|
+
const t = event.tokens;
|
|
3843
|
+
if (!t) return false;
|
|
3844
|
+
return (t.input ?? 0) + (t.output ?? 0) + (t.reasoning ?? 0) > 0;
|
|
3845
|
+
}
|
|
3846
|
+
function isUsableCost(cost) {
|
|
3847
|
+
return typeof cost === "number" && Number.isFinite(cost) && cost >= 0;
|
|
3848
|
+
}
|
|
3060
3849
|
function eventCostContribution(event) {
|
|
3061
3850
|
if (event.type === "ask_end") return 0;
|
|
3062
3851
|
const c = event.cost;
|
|
@@ -3065,6 +3854,9 @@ function eventCostContribution(event) {
|
|
|
3065
3854
|
function isCostBearingLeaf(event) {
|
|
3066
3855
|
return COST_LEAF_SET.has(event.type);
|
|
3067
3856
|
}
|
|
3857
|
+
function isUnpricedLeaf(event) {
|
|
3858
|
+
return event.type !== void 0 && COST_LEAF_SET.has(event.type) && !isUsableCost(event.cost) && hasPositiveTokens(event);
|
|
3859
|
+
}
|
|
3068
3860
|
function isRootLevel(event) {
|
|
3069
3861
|
const d = event.depth;
|
|
3070
3862
|
return (d ?? 0) === 0;
|
|
@@ -3113,6 +3905,10 @@ var AXL_EVENT_TYPES = [
|
|
|
3113
3905
|
"string_delta",
|
|
3114
3906
|
// Verification
|
|
3115
3907
|
"verify",
|
|
3908
|
+
// Schema capability diagnostics (spec 22, Problem E) — surface the silent
|
|
3909
|
+
// structured-output cliffs (oversized appended schema, dropped refinements,
|
|
3910
|
+
// streaming disabled, zero-guidance `schemaPrompt:'none'`).
|
|
3911
|
+
"schema_diagnostic",
|
|
3116
3912
|
// Observability
|
|
3117
3913
|
"log",
|
|
3118
3914
|
"memory_remember",
|
|
@@ -3155,6 +3951,7 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
3155
3951
|
"delegate",
|
|
3156
3952
|
"pipeline",
|
|
3157
3953
|
"verify",
|
|
3954
|
+
"schema_diagnostic",
|
|
3158
3955
|
"workflow_start",
|
|
3159
3956
|
"workflow_end",
|
|
3160
3957
|
"checkpoint_save",
|
|
@@ -3959,6 +4756,11 @@ var REDACTION_RULES = {
|
|
|
3959
4756
|
// Scrub only the delta — preserving path keeps "which fields stream" telemetry.
|
|
3960
4757
|
string_delta: (e) => ({ ...e, data: { ...e.data, delta: REDACTED } }),
|
|
3961
4758
|
verify: (e) => e.data.lastError !== void 0 ? { ...e, data: { ...e.data, lastError: REDACTED } } : e,
|
|
4759
|
+
// `schema_diagnostic` carries only structural schema metadata — token counts,
|
|
4760
|
+
// thresholds, JSON-Schema field paths, a root type name, a tool name — none of
|
|
4761
|
+
// which is user content or PII (field paths are schema-shape, preserved like
|
|
4762
|
+
// `string_delta.path`). Passthrough, as an explicit reviewed decision.
|
|
4763
|
+
schema_diagnostic: passthrough,
|
|
3962
4764
|
log: (e) => {
|
|
3963
4765
|
if (!e.data || typeof e.data !== "object" || Array.isArray(e.data)) return e;
|
|
3964
4766
|
return { ...e, data: walkObjectOneLevel(e.data, LOG_PRESERVE_KEYS) };
|
|
@@ -3991,6 +4793,80 @@ function redactEvent(event) {
|
|
|
3991
4793
|
return rule(event);
|
|
3992
4794
|
}
|
|
3993
4795
|
|
|
4796
|
+
// src/schema-diagnostics.ts
|
|
4797
|
+
var DEFAULT_SCHEMA_OVERSIZED_TOKENS = 4e3;
|
|
4798
|
+
var refinementCache = /* @__PURE__ */ new WeakMap();
|
|
4799
|
+
function detectDroppedRefinements(schema) {
|
|
4800
|
+
const cached = refinementCache.get(schema);
|
|
4801
|
+
if (cached) return cached;
|
|
4802
|
+
const paths = /* @__PURE__ */ new Set();
|
|
4803
|
+
let count = 0;
|
|
4804
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
4805
|
+
const walk = (node, path) => {
|
|
4806
|
+
if (node === null || typeof node !== "object") return;
|
|
4807
|
+
if (visited.has(node)) return;
|
|
4808
|
+
visited.add(node);
|
|
4809
|
+
const def = node._zod?.def;
|
|
4810
|
+
if (!def) return;
|
|
4811
|
+
const here = () => paths.add(path === "" ? "<root>" : path);
|
|
4812
|
+
const checks = def.checks;
|
|
4813
|
+
if (Array.isArray(checks)) {
|
|
4814
|
+
for (const check of checks) {
|
|
4815
|
+
const kind = check?._zod?.def?.check;
|
|
4816
|
+
if (kind === "custom") {
|
|
4817
|
+
count += 1;
|
|
4818
|
+
here();
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
}
|
|
4822
|
+
if (def.check === "custom") {
|
|
4823
|
+
count += 1;
|
|
4824
|
+
here();
|
|
4825
|
+
}
|
|
4826
|
+
const join2 = (child) => path === "" ? child : `${path}.${child}`;
|
|
4827
|
+
const shape = def.shape;
|
|
4828
|
+
if (shape && typeof shape === "object") {
|
|
4829
|
+
for (const [key, child] of Object.entries(shape)) walk(child, join2(key));
|
|
4830
|
+
}
|
|
4831
|
+
if ("element" in def) walk(def.element, `${path}[]`);
|
|
4832
|
+
if (Array.isArray(def.options)) {
|
|
4833
|
+
for (const opt of def.options) walk(opt, path);
|
|
4834
|
+
}
|
|
4835
|
+
if ("innerType" in def) walk(def.innerType, path);
|
|
4836
|
+
if (Array.isArray(def.items)) {
|
|
4837
|
+
def.items.forEach((item, i) => walk(item, `${path}[${i}]`));
|
|
4838
|
+
}
|
|
4839
|
+
if ("rest" in def && def.rest) walk(def.rest, `${path}[]`);
|
|
4840
|
+
if ("catchall" in def && def.catchall) walk(def.catchall, `${path}[*]`);
|
|
4841
|
+
if ("keyType" in def) walk(def.keyType, `${path}[key]`);
|
|
4842
|
+
if ("valueType" in def) walk(def.valueType, `${path}[*]`);
|
|
4843
|
+
if ("in" in def) walk(def.in, path);
|
|
4844
|
+
if ("out" in def) walk(def.out, path);
|
|
4845
|
+
if ("left" in def) walk(def.left, path);
|
|
4846
|
+
if ("right" in def) walk(def.right, path);
|
|
4847
|
+
if (typeof def.getter === "function") {
|
|
4848
|
+
try {
|
|
4849
|
+
walk(def.getter(), path);
|
|
4850
|
+
} catch {
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4853
|
+
};
|
|
4854
|
+
walk(schema, "");
|
|
4855
|
+
const result = { count, paths: [...paths] };
|
|
4856
|
+
refinementCache.set(schema, result);
|
|
4857
|
+
return result;
|
|
4858
|
+
}
|
|
4859
|
+
var DOCS_URL = "docs/observability.md#schema-diagnostics";
|
|
4860
|
+
var MAX_WARNED_KEYS = 2048;
|
|
4861
|
+
var warnedDiagnosticKeys = /* @__PURE__ */ new Set();
|
|
4862
|
+
function warnSchemaDiagnosticOnce(key, message, silent) {
|
|
4863
|
+
if (silent || process.env.AXL_DIAGNOSTICS_SILENT === "true") return;
|
|
4864
|
+
if (warnedDiagnosticKeys.has(key)) return;
|
|
4865
|
+
if (warnedDiagnosticKeys.size >= MAX_WARNED_KEYS) warnedDiagnosticKeys.clear();
|
|
4866
|
+
warnedDiagnosticKeys.add(key);
|
|
4867
|
+
console.warn(`[axl] ${message} See ${DOCS_URL}.`);
|
|
4868
|
+
}
|
|
4869
|
+
|
|
3994
4870
|
// src/config.ts
|
|
3995
4871
|
function defineConfig(config) {
|
|
3996
4872
|
return config;
|
|
@@ -4066,14 +4942,233 @@ function resolveConfig(config) {
|
|
|
4066
4942
|
return resolved;
|
|
4067
4943
|
}
|
|
4068
4944
|
|
|
4945
|
+
// src/tool-model-output.ts
|
|
4946
|
+
var import_node_util = require("util");
|
|
4947
|
+
function invalid(path, reason) {
|
|
4948
|
+
return new Error(`Invalid tool model output at ${path}: ${reason}`);
|
|
4949
|
+
}
|
|
4950
|
+
function propertyPath(parent, key) {
|
|
4951
|
+
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) return `${parent}.${key}`;
|
|
4952
|
+
let escaped = "";
|
|
4953
|
+
for (const character of key) {
|
|
4954
|
+
const code = character.charCodeAt(0);
|
|
4955
|
+
if (character === "\\" || character === '"') escaped += `\\${character}`;
|
|
4956
|
+
else if (code <= 31 || code === 8232 || code === 8233) {
|
|
4957
|
+
escaped += `\\u${code.toString(16).padStart(4, "0")}`;
|
|
4958
|
+
} else escaped += character;
|
|
4959
|
+
}
|
|
4960
|
+
return `${parent}["${escaped}"]`;
|
|
4961
|
+
}
|
|
4962
|
+
function normalize(value, path, stack) {
|
|
4963
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
4964
|
+
if (typeof value === "number") {
|
|
4965
|
+
if (!Number.isFinite(value)) throw invalid(path, "number must be finite");
|
|
4966
|
+
return value;
|
|
4967
|
+
}
|
|
4968
|
+
if (value === void 0) throw invalid(path, "undefined is only allowed on object properties");
|
|
4969
|
+
if (typeof value === "bigint" || typeof value === "function" || typeof value === "symbol") {
|
|
4970
|
+
throw invalid(path, `unsupported ${typeof value}`);
|
|
4971
|
+
}
|
|
4972
|
+
if (typeof value !== "object") throw invalid(path, `unsupported ${typeof value}`);
|
|
4973
|
+
let prototype;
|
|
4974
|
+
let descriptors;
|
|
4975
|
+
try {
|
|
4976
|
+
prototype = Object.getPrototypeOf(value);
|
|
4977
|
+
descriptors = Object.getOwnPropertyDescriptors(value);
|
|
4978
|
+
Object.setPrototypeOf(descriptors, null);
|
|
4979
|
+
} catch (cause) {
|
|
4980
|
+
throw invalid(
|
|
4981
|
+
path,
|
|
4982
|
+
`object inspection failed (${cause instanceof Error ? cause.name : "error"})`
|
|
4983
|
+
);
|
|
4984
|
+
}
|
|
4985
|
+
if (stack.has(value)) throw invalid(path, "cyclic reference");
|
|
4986
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
4987
|
+
const descriptor = descriptors[key];
|
|
4988
|
+
if (descriptor && !("value" in descriptor)) {
|
|
4989
|
+
throw invalid(
|
|
4990
|
+
typeof key === "string" ? propertyPath(path, key) : `${path}[symbol]`,
|
|
4991
|
+
"accessor properties are not supported"
|
|
4992
|
+
);
|
|
4993
|
+
}
|
|
4994
|
+
if (typeof key === "symbol" && descriptor?.enumerable) {
|
|
4995
|
+
throw invalid(`${path}[symbol]`, "enumerable symbol keys are not supported");
|
|
4996
|
+
}
|
|
4997
|
+
}
|
|
4998
|
+
if (typeof descriptors.toJSON?.value === "function") {
|
|
4999
|
+
throw invalid(propertyPath(path, "toJSON"), "custom toJSON is not supported");
|
|
5000
|
+
}
|
|
5001
|
+
const array = Array.isArray(value);
|
|
5002
|
+
if (array && prototype !== Array.prototype && prototype !== null) {
|
|
5003
|
+
throw invalid(path, "array subclasses are not supported");
|
|
5004
|
+
}
|
|
5005
|
+
const inheritedToJSON = prototype === Array.prototype || prototype === Object.prototype ? Object.getOwnPropertyDescriptor(prototype, "toJSON") : void 0;
|
|
5006
|
+
if (typeof inheritedToJSON?.value === "function") {
|
|
5007
|
+
throw invalid(propertyPath(path, "toJSON"), "inherited custom toJSON is not supported");
|
|
5008
|
+
}
|
|
5009
|
+
const inheritedThen = prototype === Array.prototype || prototype === Object.prototype ? Object.getOwnPropertyDescriptor(prototype, "then") : void 0;
|
|
5010
|
+
if (typeof descriptors.then?.value === "function" || inheritedThen !== void 0 && (!("value" in inheritedThen) || typeof inheritedThen.value === "function")) {
|
|
5011
|
+
throw invalid(path, "thenable output is not supported");
|
|
5012
|
+
}
|
|
5013
|
+
stack.add(value);
|
|
5014
|
+
try {
|
|
5015
|
+
if (array) {
|
|
5016
|
+
const normalized2 = [];
|
|
5017
|
+
const length = descriptors.length?.value;
|
|
5018
|
+
if (!Number.isSafeInteger(length) || length < 0) {
|
|
5019
|
+
throw invalid(propertyPath(path, "length"), "array length is invalid");
|
|
5020
|
+
}
|
|
5021
|
+
for (let index = 0; index < length; index++) {
|
|
5022
|
+
const descriptor = descriptors[String(index)];
|
|
5023
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
5024
|
+
throw invalid(`${path}[${index}]`, "arrays must be dense data properties");
|
|
5025
|
+
}
|
|
5026
|
+
if (descriptor.value === void 0) {
|
|
5027
|
+
throw invalid(`${path}[${index}]`, "array values cannot be undefined");
|
|
5028
|
+
}
|
|
5029
|
+
normalized2.push(normalize(descriptor.value, `${path}[${index}]`, stack));
|
|
5030
|
+
}
|
|
5031
|
+
for (const key of Object.keys(descriptors)) {
|
|
5032
|
+
const descriptor = descriptors[key];
|
|
5033
|
+
if (!descriptor?.enumerable) continue;
|
|
5034
|
+
const index = Number(key);
|
|
5035
|
+
if (!Number.isInteger(index) || index < 0 || index >= length || String(index) !== key) {
|
|
5036
|
+
throw invalid(propertyPath(path, key), "non-index array properties are not supported");
|
|
5037
|
+
}
|
|
5038
|
+
}
|
|
5039
|
+
Object.setPrototypeOf(normalized2, null);
|
|
5040
|
+
return normalized2;
|
|
5041
|
+
}
|
|
5042
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
5043
|
+
throw invalid(path, "only plain objects and null-prototype records are supported");
|
|
5044
|
+
}
|
|
5045
|
+
const normalized = /* @__PURE__ */ Object.create(null);
|
|
5046
|
+
for (const key of Object.keys(descriptors)) {
|
|
5047
|
+
const descriptor = descriptors[key];
|
|
5048
|
+
if (!descriptor?.enumerable) continue;
|
|
5049
|
+
if (!("value" in descriptor)) {
|
|
5050
|
+
throw invalid(propertyPath(path, key), "accessor properties are not supported");
|
|
5051
|
+
}
|
|
5052
|
+
if (descriptor.value === void 0) continue;
|
|
5053
|
+
normalized[key] = normalize(descriptor.value, propertyPath(path, key), stack);
|
|
5054
|
+
}
|
|
5055
|
+
return normalized;
|
|
5056
|
+
} finally {
|
|
5057
|
+
stack.delete(value);
|
|
5058
|
+
}
|
|
5059
|
+
}
|
|
5060
|
+
function serializeToolModelOutput(toolName, output) {
|
|
5061
|
+
try {
|
|
5062
|
+
if (import_node_util.types.isPromise(output)) {
|
|
5063
|
+
void Promise.prototype.then.call(output, void 0, () => void 0);
|
|
5064
|
+
}
|
|
5065
|
+
if (typeof output === "string") return output;
|
|
5066
|
+
return JSON.stringify(normalize(output, "$", /* @__PURE__ */ new WeakSet()));
|
|
5067
|
+
} catch (cause) {
|
|
5068
|
+
throw new ToolModelOutputError(toolName, cause);
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
|
|
4069
5072
|
// src/context.ts
|
|
4070
5073
|
var signalStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
5074
|
+
function isErrorShaped(value) {
|
|
5075
|
+
return value !== null && typeof value === "object" && "error" in value;
|
|
5076
|
+
}
|
|
5077
|
+
function isErrorShapedSafely(value) {
|
|
5078
|
+
try {
|
|
5079
|
+
return isErrorShaped(value);
|
|
5080
|
+
} catch {
|
|
5081
|
+
return false;
|
|
5082
|
+
}
|
|
5083
|
+
}
|
|
5084
|
+
function recordToolSpanResult(span, value, suppressInspectionErrors) {
|
|
5085
|
+
let isError = false;
|
|
5086
|
+
let errorMessage;
|
|
5087
|
+
const inspect = () => {
|
|
5088
|
+
if (isErrorShaped(value)) {
|
|
5089
|
+
isError = true;
|
|
5090
|
+
errorMessage = value.error;
|
|
5091
|
+
} else {
|
|
5092
|
+
isError = false;
|
|
5093
|
+
}
|
|
5094
|
+
};
|
|
5095
|
+
if (suppressInspectionErrors) {
|
|
5096
|
+
try {
|
|
5097
|
+
inspect();
|
|
5098
|
+
} catch {
|
|
5099
|
+
isError = false;
|
|
5100
|
+
errorMessage = void 0;
|
|
5101
|
+
}
|
|
5102
|
+
} else {
|
|
5103
|
+
inspect();
|
|
5104
|
+
}
|
|
5105
|
+
span.setAttribute("axl.tool.success", !isError);
|
|
5106
|
+
if (isError) span.setStatus("error", errorMessage);
|
|
5107
|
+
}
|
|
5108
|
+
function prepareToolModelOutput(toolName, project) {
|
|
5109
|
+
let output;
|
|
5110
|
+
try {
|
|
5111
|
+
output = project();
|
|
5112
|
+
} catch (cause) {
|
|
5113
|
+
throw new ToolModelOutputError(toolName, cause);
|
|
5114
|
+
}
|
|
5115
|
+
return serializeToolModelOutput(toolName, output);
|
|
5116
|
+
}
|
|
5117
|
+
function finalizeToolResult(options) {
|
|
5118
|
+
const { toolName, configuredTool, outcome, legacyContent, emitEnd, beforeProjection } = options;
|
|
5119
|
+
const projectionEligible = configuredTool !== void 0 && !configuredTool.sensitive && outcome.kind === "returned" && configuredTool.toModelOutput !== void 0;
|
|
5120
|
+
if (projectionEligible) {
|
|
5121
|
+
emitEnd();
|
|
5122
|
+
beforeProjection();
|
|
5123
|
+
const project = configuredTool.toModelOutput;
|
|
5124
|
+
return prepareToolModelOutput(toolName, () => project(outcome.value));
|
|
5125
|
+
}
|
|
5126
|
+
const content = legacyContent();
|
|
5127
|
+
emitEnd();
|
|
5128
|
+
return content;
|
|
5129
|
+
}
|
|
4071
5130
|
var askStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
5131
|
+
var inlineSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
4072
5132
|
function zodToJsonSchema(schema) {
|
|
5133
|
+
const cached = inlineSchemaCache.get(schema);
|
|
5134
|
+
if (cached !== void 0) return cached;
|
|
4073
5135
|
const result = import_zod.z.toJSONSchema(schema, { unrepresentable: "any" });
|
|
4074
5136
|
delete result.$schema;
|
|
5137
|
+
Object.freeze(result);
|
|
5138
|
+
inlineSchemaCache.set(schema, result);
|
|
4075
5139
|
return result;
|
|
4076
5140
|
}
|
|
5141
|
+
var promptSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
5142
|
+
var inlineSchemaTokenCache = /* @__PURE__ */ new WeakMap();
|
|
5143
|
+
function estimateInlineSchemaTokens(schema) {
|
|
5144
|
+
const cached = inlineSchemaTokenCache.get(schema);
|
|
5145
|
+
if (cached !== void 0) return cached;
|
|
5146
|
+
const tokens = estimateTokens(JSON.stringify(zodToJsonSchema(schema)));
|
|
5147
|
+
inlineSchemaTokenCache.set(schema, tokens);
|
|
5148
|
+
return tokens;
|
|
5149
|
+
}
|
|
5150
|
+
var nativeSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
5151
|
+
function deriveNativeSchema(schema) {
|
|
5152
|
+
const cached = nativeSchemaCache.get(schema);
|
|
5153
|
+
if (cached !== void 0) return cached;
|
|
5154
|
+
const json = import_zod.z.toJSONSchema(schema, { unrepresentable: "any", io: "input" });
|
|
5155
|
+
delete json.$schema;
|
|
5156
|
+
nativeSchemaCache.set(schema, json);
|
|
5157
|
+
return json;
|
|
5158
|
+
}
|
|
5159
|
+
function renderSchemaForPrompt(schema) {
|
|
5160
|
+
const cached = promptSchemaCache.get(schema);
|
|
5161
|
+
if (cached !== void 0) return cached;
|
|
5162
|
+
const json = import_zod.z.toJSONSchema(schema, {
|
|
5163
|
+
unrepresentable: "any",
|
|
5164
|
+
io: "input",
|
|
5165
|
+
reused: "ref"
|
|
5166
|
+
});
|
|
5167
|
+
delete json.$schema;
|
|
5168
|
+
const rendered = JSON.stringify(json);
|
|
5169
|
+
promptSchemaCache.set(schema, rendered);
|
|
5170
|
+
return rendered;
|
|
5171
|
+
}
|
|
4077
5172
|
function estimateTokens(text) {
|
|
4078
5173
|
return Math.ceil(text.length / 4);
|
|
4079
5174
|
}
|
|
@@ -4454,12 +5549,14 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4454
5549
|
depth,
|
|
4455
5550
|
agent: agent2._name,
|
|
4456
5551
|
stepRef,
|
|
4457
|
-
askCost: { value: 0 }
|
|
5552
|
+
askCost: { value: 0 },
|
|
5553
|
+
askUnpriced: false
|
|
4458
5554
|
};
|
|
4459
5555
|
return askStorage.run(frame, async () => {
|
|
4460
5556
|
const askStart = Date.now();
|
|
4461
5557
|
this.emitEvent({ type: "ask_start", prompt });
|
|
4462
5558
|
const costBefore = this.budgetContext?.totalCost ?? 0;
|
|
5559
|
+
const unpricedCountBefore = this.budgetContext?.unpricedCount ?? 0;
|
|
4463
5560
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
4464
5561
|
const usageCapture = {};
|
|
4465
5562
|
const doCall = async () => {
|
|
@@ -4505,6 +5602,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4505
5602
|
) : await doCall();
|
|
4506
5603
|
outcome = { ok: true, result };
|
|
4507
5604
|
const costAfter = this.budgetContext?.totalCost ?? 0;
|
|
5605
|
+
const unpricedCountAfter = this.budgetContext?.unpricedCount ?? 0;
|
|
4508
5606
|
if (this.onAgentCallComplete) {
|
|
4509
5607
|
try {
|
|
4510
5608
|
this.onAgentCallComplete({
|
|
@@ -4513,6 +5611,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4513
5611
|
response: typeof result === "string" ? result : JSON.stringify(result),
|
|
4514
5612
|
model: agent2.resolveModel(resolveCtx),
|
|
4515
5613
|
cost: costAfter - costBefore,
|
|
5614
|
+
unpriced: frame.askUnpriced || unpricedCountAfter > unpricedCountBefore,
|
|
4516
5615
|
duration: Date.now() - askStart,
|
|
4517
5616
|
promptVersion: agent2._config.version,
|
|
4518
5617
|
temperature: options?.temperature ?? agent2._config.temperature,
|
|
@@ -4547,6 +5646,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4547
5646
|
error: "ask_end emitted without outcome \u2014 internal bug"
|
|
4548
5647
|
},
|
|
4549
5648
|
cost: frame.askCost.value,
|
|
5649
|
+
...frame.askUnpriced ? { unpriced: true } : {},
|
|
4550
5650
|
duration: Date.now() - askStart
|
|
4551
5651
|
});
|
|
4552
5652
|
} catch (emitErr) {
|
|
@@ -4630,15 +5730,32 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
4630
5730
|
messages.push(msg);
|
|
4631
5731
|
}
|
|
4632
5732
|
}
|
|
5733
|
+
const schemaPromptMode = options?.schemaPrompt ?? agent2._config.schemaPrompt ?? "json-schema";
|
|
4633
5734
|
let userContent = prompt;
|
|
5735
|
+
let appendedSchemaText;
|
|
4634
5736
|
if (options?.schema) {
|
|
4635
|
-
const
|
|
4636
|
-
|
|
5737
|
+
const schema = options.schema;
|
|
5738
|
+
if (schemaPromptMode === "none") {
|
|
5739
|
+
} else if (schemaPromptMode === "json-schema") {
|
|
5740
|
+
appendedSchemaText = renderSchemaForPrompt(schema);
|
|
5741
|
+
userContent += `
|
|
4637
5742
|
|
|
4638
5743
|
Respond with valid JSON matching this schema:
|
|
4639
|
-
${
|
|
5744
|
+
${appendedSchemaText}`;
|
|
5745
|
+
} else {
|
|
5746
|
+
appendedSchemaText = typeof schemaPromptMode.render === "function" ? schemaPromptMode.render(schema) : schemaPromptMode.render;
|
|
5747
|
+
userContent += `
|
|
5748
|
+
|
|
5749
|
+
${appendedSchemaText}`;
|
|
5750
|
+
}
|
|
4640
5751
|
}
|
|
4641
5752
|
messages.push({ role: "user", content: userContent });
|
|
5753
|
+
this.emitSchemaDiagnostics(agent2, options, toolDefs, {
|
|
5754
|
+
mode: schemaPromptMode,
|
|
5755
|
+
appendedSchemaText,
|
|
5756
|
+
provider,
|
|
5757
|
+
model
|
|
5758
|
+
});
|
|
4642
5759
|
if (handoffMessages && handoffMessages.length > 0) {
|
|
4643
5760
|
const handoffContext = handoffMessages.filter(
|
|
4644
5761
|
(m) => m.role === "assistant" || m.role === "tool"
|
|
@@ -4704,6 +5821,7 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
4704
5821
|
let lastStartAttempt = 1;
|
|
4705
5822
|
let lastStartMaxAttempts = 1;
|
|
4706
5823
|
while (turns < maxTurns) {
|
|
5824
|
+
this.currentSignal?.throwIfAborted();
|
|
4707
5825
|
if (Date.now() - startTime > timeoutMs) {
|
|
4708
5826
|
throw new TimeoutError("ctx.ask()", timeoutMs);
|
|
4709
5827
|
}
|
|
@@ -4751,7 +5869,17 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
4751
5869
|
signal: this.currentSignal
|
|
4752
5870
|
};
|
|
4753
5871
|
if (options?.schema && toolDefs.length === 0) {
|
|
4754
|
-
|
|
5872
|
+
const nativeOut = options?.nativeStructuredOutput ?? agent2._config.nativeStructuredOutput ?? false;
|
|
5873
|
+
chatOptions.responseFormat = nativeOut ? {
|
|
5874
|
+
type: "json_schema",
|
|
5875
|
+
json_schema: {
|
|
5876
|
+
name: "response",
|
|
5877
|
+
// Input-side rendering — see `deriveNativeSchema`. Keeps the
|
|
5878
|
+
// native schema consistent with the prompt and non-empty for
|
|
5879
|
+
// `.transform()` schemas.
|
|
5880
|
+
schema: deriveNativeSchema(options.schema)
|
|
5881
|
+
}
|
|
5882
|
+
} : { type: "json_object" };
|
|
4755
5883
|
}
|
|
4756
5884
|
const callbackMeta = this.currentCallbackMeta(agent2._name);
|
|
4757
5885
|
const callParams = {
|
|
@@ -4795,6 +5923,7 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
4795
5923
|
this.onAgentStart?.({ agent: agent2._name, model: modelUri }, callbackMeta);
|
|
4796
5924
|
let response;
|
|
4797
5925
|
try {
|
|
5926
|
+
this.currentSignal?.throwIfAborted();
|
|
4798
5927
|
if (this._streamingEnabled) {
|
|
4799
5928
|
let content2 = "";
|
|
4800
5929
|
const toolCalls = [];
|
|
@@ -4869,10 +5998,7 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
4869
5998
|
}
|
|
4870
5999
|
});
|
|
4871
6000
|
}
|
|
4872
|
-
response ??= {
|
|
4873
|
-
content: content2,
|
|
4874
|
-
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
4875
|
-
};
|
|
6001
|
+
response ??= { content: content2 };
|
|
4876
6002
|
if (toolCalls.length > 0) {
|
|
4877
6003
|
response.tool_calls = toolCalls;
|
|
4878
6004
|
}
|
|
@@ -4896,7 +6022,11 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
4896
6022
|
response: "",
|
|
4897
6023
|
turn: turns,
|
|
4898
6024
|
...retryReason ? { retryReason } : {},
|
|
4899
|
-
error: err instanceof Error ? err.message : String(err)
|
|
6025
|
+
error: err instanceof Error ? err.message : String(err),
|
|
6026
|
+
// Enrich with typed-error metadata when available. `body` is
|
|
6027
|
+
// deliberately NOT emitted — it's redaction-eligible (see
|
|
6028
|
+
// docs/security.md). `data.error` already carries the message.
|
|
6029
|
+
...err instanceof ProviderError ? { status: err.status, retryable: err.retryable } : {}
|
|
4900
6030
|
}
|
|
4901
6031
|
});
|
|
4902
6032
|
throw err;
|
|
@@ -4960,10 +6090,13 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
4960
6090
|
depth: handoffTargetDepth,
|
|
4961
6091
|
agent: descriptor.agent._name,
|
|
4962
6092
|
stepRef: sourceFrame?.stepRef ?? this.stepRefRoot,
|
|
4963
|
-
askCost: { value: 0 }
|
|
6093
|
+
askCost: { value: 0 },
|
|
6094
|
+
askUnpriced: false
|
|
4964
6095
|
};
|
|
4965
6096
|
const handoffOptions = options ? {
|
|
4966
6097
|
schema: options.schema,
|
|
6098
|
+
schemaPrompt: options.schemaPrompt,
|
|
6099
|
+
nativeStructuredOutput: options.nativeStructuredOutput,
|
|
4967
6100
|
retries: options.retries,
|
|
4968
6101
|
metadata: options.metadata,
|
|
4969
6102
|
validate: options.validate,
|
|
@@ -4984,6 +6117,7 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
4984
6117
|
type: "ask_end",
|
|
4985
6118
|
outcome: { ok: true, result },
|
|
4986
6119
|
cost: targetFrame.askCost.value,
|
|
6120
|
+
...targetFrame.askUnpriced ? { unpriced: true } : {},
|
|
4987
6121
|
duration: Date.now() - targetAskStart
|
|
4988
6122
|
});
|
|
4989
6123
|
return result;
|
|
@@ -4995,6 +6129,7 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
4995
6129
|
error: err instanceof Error ? err.message : String(err)
|
|
4996
6130
|
},
|
|
4997
6131
|
cost: targetFrame.askCost.value,
|
|
6132
|
+
...targetFrame.askUnpriced ? { unpriced: true } : {},
|
|
4998
6133
|
duration: Date.now() - targetAskStart
|
|
4999
6134
|
});
|
|
5000
6135
|
throw err;
|
|
@@ -5090,6 +6225,8 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
5090
6225
|
return await handoffFn();
|
|
5091
6226
|
}
|
|
5092
6227
|
}
|
|
6228
|
+
const tool2 = agent2._config.tools?.find((candidate) => candidate.name === toolName);
|
|
6229
|
+
const isMcpTool = !tool2 && this.mcpManager?.isMcpTool(toolName);
|
|
5093
6230
|
const toolOverride = this.toolOverrides?.get(toolName);
|
|
5094
6231
|
if (toolOverride) {
|
|
5095
6232
|
let toolArgs2;
|
|
@@ -5114,38 +6251,47 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
5114
6251
|
callbackMeta
|
|
5115
6252
|
);
|
|
5116
6253
|
const toolStart2 = Date.now();
|
|
6254
|
+
const suppressErrorShapeInspection2 = tool2?.sensitive === true || tool2?.toModelOutput !== void 0;
|
|
5117
6255
|
const executeOverride = async () => {
|
|
5118
|
-
let toolResult3;
|
|
5119
6256
|
try {
|
|
5120
|
-
|
|
6257
|
+
return { kind: "returned", value: await toolOverride(toolArgs2) };
|
|
5121
6258
|
} catch (err) {
|
|
5122
|
-
|
|
6259
|
+
return {
|
|
6260
|
+
kind: "thrown",
|
|
6261
|
+
value: { error: err instanceof Error ? err.message : String(err) }
|
|
6262
|
+
};
|
|
5123
6263
|
}
|
|
5124
|
-
return toolResult3;
|
|
5125
6264
|
};
|
|
5126
|
-
const
|
|
6265
|
+
const outcome = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
5127
6266
|
"axl.tool.call",
|
|
5128
6267
|
{
|
|
5129
6268
|
"axl.tool.name": toolName,
|
|
5130
6269
|
"axl.agent.name": agent2._name
|
|
5131
6270
|
},
|
|
5132
6271
|
async (span) => {
|
|
5133
|
-
const
|
|
6272
|
+
const result = await executeOverride();
|
|
5134
6273
|
span.setAttribute("axl.tool.duration", Date.now() - toolStart2);
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
if (isError)
|
|
5138
|
-
span.setStatus("error", r.error);
|
|
5139
|
-
return r;
|
|
6274
|
+
recordToolSpanResult(span, result.value, suppressErrorShapeInspection2);
|
|
6275
|
+
return result;
|
|
5140
6276
|
}
|
|
5141
6277
|
) : await executeOverride();
|
|
5142
|
-
const
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
6278
|
+
const toolResult2 = outcome.value;
|
|
6279
|
+
const resultContent2 = finalizeToolResult({
|
|
6280
|
+
toolName,
|
|
6281
|
+
configuredTool: tool2,
|
|
6282
|
+
outcome,
|
|
6283
|
+
legacyContent: () => tool2?.sensitive ? "[REDACTED - sensitive tool output]" : JSON.stringify(toolResult2),
|
|
6284
|
+
emitEnd: () => {
|
|
6285
|
+
const toolDuration2 = Date.now() - toolStart2;
|
|
6286
|
+
this.emitEvent({
|
|
6287
|
+
type: "tool_call_end",
|
|
6288
|
+
agent: agent2._name,
|
|
6289
|
+
tool: toolName,
|
|
6290
|
+
duration: toolDuration2,
|
|
6291
|
+
data: { args: toolArgs2, result: toolResult2, callId: toolCall.id }
|
|
6292
|
+
});
|
|
6293
|
+
},
|
|
6294
|
+
beforeProjection: () => this.currentSignal?.throwIfAborted()
|
|
5149
6295
|
});
|
|
5150
6296
|
currentMessages.push({
|
|
5151
6297
|
role: "tool",
|
|
@@ -5154,8 +6300,6 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
5154
6300
|
});
|
|
5155
6301
|
continue;
|
|
5156
6302
|
}
|
|
5157
|
-
const tool2 = agent2._config.tools?.find((t) => t.name === toolName);
|
|
5158
|
-
const isMcpTool = !tool2 && this.mcpManager?.isMcpTool(toolName);
|
|
5159
6303
|
if (!tool2 && !isMcpTool) {
|
|
5160
6304
|
this.emitEvent({ type: "tool_denied", agent: agent2._name, tool: toolName });
|
|
5161
6305
|
currentMessages.push({
|
|
@@ -5184,6 +6328,7 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
5184
6328
|
});
|
|
5185
6329
|
this.onToolCall?.({ name: toolName, args: toolArgs, callId: toolCall.id }, callbackMeta);
|
|
5186
6330
|
const toolStart = Date.now();
|
|
6331
|
+
const suppressErrorShapeInspection = tool2?.sensitive === true || tool2?.toModelOutput !== void 0;
|
|
5187
6332
|
if (tool2 && tool2.requireApproval) {
|
|
5188
6333
|
const approvalFn = async () => {
|
|
5189
6334
|
const decision = await this.awaitHuman({
|
|
@@ -5245,12 +6390,12 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
5245
6390
|
}
|
|
5246
6391
|
}
|
|
5247
6392
|
const executeTool = async () => {
|
|
5248
|
-
let
|
|
6393
|
+
let outcome;
|
|
5249
6394
|
let resultContent2;
|
|
5250
6395
|
if (isMcpTool && this.mcpManager) {
|
|
5251
6396
|
try {
|
|
5252
6397
|
const mcpResult = await this.mcpManager.callTool(toolName, toolArgs);
|
|
5253
|
-
|
|
6398
|
+
outcome = { kind: "returned", value: mcpResult };
|
|
5254
6399
|
resultContent2 = mcpResult.content.map(
|
|
5255
6400
|
(c) => c.type === "text" ? c.text : `[${c.type}]`
|
|
5256
6401
|
).join("\n");
|
|
@@ -5258,34 +6403,50 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
5258
6403
|
resultContent2 = `Error: ${resultContent2}`;
|
|
5259
6404
|
}
|
|
5260
6405
|
} catch (err) {
|
|
5261
|
-
|
|
5262
|
-
|
|
6406
|
+
outcome = {
|
|
6407
|
+
kind: "thrown",
|
|
6408
|
+
value: { error: err instanceof Error ? err.message : String(err) }
|
|
6409
|
+
};
|
|
6410
|
+
resultContent2 = JSON.stringify(outcome.value);
|
|
5263
6411
|
}
|
|
5264
6412
|
} else if (tool2) {
|
|
5265
6413
|
const childCtx = this.createChildContext();
|
|
5266
6414
|
try {
|
|
5267
|
-
|
|
6415
|
+
outcome = { kind: "returned", value: await tool2._execute(toolArgs, childCtx) };
|
|
5268
6416
|
} catch (err) {
|
|
5269
|
-
|
|
6417
|
+
outcome = {
|
|
6418
|
+
kind: "thrown",
|
|
6419
|
+
value: { error: err instanceof Error ? err.message : String(err) }
|
|
6420
|
+
};
|
|
5270
6421
|
}
|
|
5271
|
-
if (tool2.hooks?.after && !(
|
|
6422
|
+
if (outcome.kind === "returned" && tool2.hooks?.after && !(suppressErrorShapeInspection ? isErrorShapedSafely(outcome.value) : isErrorShaped(outcome.value))) {
|
|
5272
6423
|
try {
|
|
5273
|
-
|
|
6424
|
+
outcome = {
|
|
6425
|
+
kind: "returned",
|
|
6426
|
+
value: await tool2.hooks.after(outcome.value, this)
|
|
6427
|
+
};
|
|
5274
6428
|
} catch (err) {
|
|
5275
|
-
|
|
5276
|
-
|
|
6429
|
+
outcome = {
|
|
6430
|
+
kind: "thrown",
|
|
6431
|
+
value: {
|
|
6432
|
+
error: `After hook error: ${err instanceof Error ? err.message : String(err)}`
|
|
6433
|
+
}
|
|
5277
6434
|
};
|
|
5278
6435
|
}
|
|
5279
6436
|
}
|
|
5280
|
-
|
|
6437
|
+
if (tool2.sensitive) {
|
|
6438
|
+
resultContent2 = "[REDACTED - sensitive tool output]";
|
|
6439
|
+
} else if (outcome.kind === "thrown" || tool2.toModelOutput === void 0) {
|
|
6440
|
+
resultContent2 = JSON.stringify(outcome.value);
|
|
6441
|
+
}
|
|
5281
6442
|
} else {
|
|
5282
|
-
|
|
6443
|
+
outcome = { kind: "returned", value: void 0 };
|
|
5283
6444
|
resultContent2 = "Tool execution error";
|
|
5284
6445
|
}
|
|
5285
|
-
return {
|
|
6446
|
+
return { outcome, resultContent: resultContent2 };
|
|
5286
6447
|
};
|
|
5287
6448
|
const traceName = isMcpTool && this.mcpManager ? this.mcpManager.getQualifiedName(toolName) ?? toolName : toolName;
|
|
5288
|
-
const
|
|
6449
|
+
const execution = this.spanManager ? await this.spanManager.withSpanAsync(
|
|
5289
6450
|
"axl.tool.call",
|
|
5290
6451
|
{
|
|
5291
6452
|
"axl.tool.name": traceName,
|
|
@@ -5294,22 +6455,27 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
5294
6455
|
async (span) => {
|
|
5295
6456
|
const r = await executeTool();
|
|
5296
6457
|
span.setAttribute("axl.tool.duration", Date.now() - toolStart);
|
|
5297
|
-
|
|
5298
|
-
span.setAttribute("axl.tool.success", !isError);
|
|
5299
|
-
if (isError)
|
|
5300
|
-
span.setStatus(
|
|
5301
|
-
"error",
|
|
5302
|
-
r.toolResult.error
|
|
5303
|
-
);
|
|
6458
|
+
recordToolSpanResult(span, r.outcome.value, suppressErrorShapeInspection);
|
|
5304
6459
|
return r;
|
|
5305
6460
|
}
|
|
5306
6461
|
) : await executeTool();
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
6462
|
+
const toolResult = execution.outcome.value;
|
|
6463
|
+
const toolDuration = Date.now() - toolStart;
|
|
6464
|
+
const resultContent = finalizeToolResult({
|
|
6465
|
+
toolName,
|
|
6466
|
+
configuredTool: tool2,
|
|
6467
|
+
outcome: execution.outcome,
|
|
6468
|
+
legacyContent: () => execution.resultContent,
|
|
6469
|
+
emitEnd: () => {
|
|
6470
|
+
this.emitEvent({
|
|
6471
|
+
type: "tool_call_end",
|
|
6472
|
+
agent: agent2._name,
|
|
6473
|
+
tool: traceName,
|
|
6474
|
+
duration: toolDuration,
|
|
6475
|
+
data: { args: toolArgs, result: toolResult, callId: toolCall.id }
|
|
6476
|
+
});
|
|
6477
|
+
},
|
|
6478
|
+
beforeProjection: () => this.currentSignal?.throwIfAborted()
|
|
5313
6479
|
});
|
|
5314
6480
|
currentMessages.push({
|
|
5315
6481
|
role: "tool",
|
|
@@ -5533,6 +6699,148 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
|
|
|
5533
6699
|
...providerMetadata ? { providerMetadata } : {}
|
|
5534
6700
|
});
|
|
5535
6701
|
}
|
|
6702
|
+
/**
|
|
6703
|
+
* Emit `schema_diagnostic` events for the silent structured-output cliffs
|
|
6704
|
+
* (spec 22, Problems B + E). Called once per ask. Covers:
|
|
6705
|
+
* - the appended **prompt** schema (oversized; dropped refinements; or the
|
|
6706
|
+
* zero-guidance `schemaPrompt:'none'` footgun),
|
|
6707
|
+
* - each user **tool-def** schema (oversized, dropped refinements),
|
|
6708
|
+
* - the **streaming** gate (disabled by a non-object root or by tools),
|
|
6709
|
+
* - **native structured output** the resolved provider can't honor (R10).
|
|
6710
|
+
*
|
|
6711
|
+
* The structured event always fires (persisted + streamed). For the genuinely
|
|
6712
|
+
* surprising, low-frequency cliffs it ALSO fires a one-time deduped
|
|
6713
|
+
* `console.warn` so the median consumer — who never wires up `ctx.events` and
|
|
6714
|
+
* runs with the trace console off — still sees it. `oversized` is event-only
|
|
6715
|
+
* (O3: it's tunable/noisy); the `tools` streaming cause is event-only too
|
|
6716
|
+
* (expected behavior for any tool-using agent, not a surprise).
|
|
6717
|
+
*/
|
|
6718
|
+
emitSchemaDiagnostics(agent2, options, toolDefs, ctx) {
|
|
6719
|
+
const silent = this.config.diagnostics?.silent;
|
|
6720
|
+
const threshold = this.config.diagnostics?.schemaOversizedTokens ?? DEFAULT_SCHEMA_OVERSIZED_TOKENS;
|
|
6721
|
+
const schema = options?.schema;
|
|
6722
|
+
if (schema) {
|
|
6723
|
+
if (ctx.mode === "none") {
|
|
6724
|
+
this.emitEvent({
|
|
6725
|
+
type: "schema_diagnostic",
|
|
6726
|
+
agent: agent2._name,
|
|
6727
|
+
data: { kind: "schema_prompt_none_no_guidance" }
|
|
6728
|
+
});
|
|
6729
|
+
warnSchemaDiagnosticOnce(
|
|
6730
|
+
`${agent2._name}\0schema_prompt_none`,
|
|
6731
|
+
`schemaPrompt:'none' is set on agent '${agent2._name}' with an output schema and no custom guidance \u2014 the model receives zero shape hints while the reply is still parsed against the schema, which invites a parse-failure retry loop. Provide a custom { render } or drop 'none'.`,
|
|
6732
|
+
silent
|
|
6733
|
+
);
|
|
6734
|
+
} else {
|
|
6735
|
+
const promptTokens = estimateTokens(ctx.appendedSchemaText ?? "");
|
|
6736
|
+
if (promptTokens > threshold) {
|
|
6737
|
+
this.emitEvent({
|
|
6738
|
+
type: "schema_diagnostic",
|
|
6739
|
+
agent: agent2._name,
|
|
6740
|
+
data: {
|
|
6741
|
+
kind: "prompt_schema_oversized",
|
|
6742
|
+
estimatedTokens: promptTokens,
|
|
6743
|
+
threshold,
|
|
6744
|
+
site: "prompt"
|
|
6745
|
+
}
|
|
6746
|
+
});
|
|
6747
|
+
}
|
|
6748
|
+
if (ctx.mode === "json-schema") {
|
|
6749
|
+
const dropped = detectDroppedRefinements(schema);
|
|
6750
|
+
if (dropped.count > 0) {
|
|
6751
|
+
this.emitEvent({
|
|
6752
|
+
type: "schema_diagnostic",
|
|
6753
|
+
agent: agent2._name,
|
|
6754
|
+
data: {
|
|
6755
|
+
kind: "dropped_refinements",
|
|
6756
|
+
count: dropped.count,
|
|
6757
|
+
paths: dropped.paths,
|
|
6758
|
+
site: "prompt"
|
|
6759
|
+
}
|
|
6760
|
+
});
|
|
6761
|
+
warnSchemaDiagnosticOnce(
|
|
6762
|
+
`${agent2._name}\0dropped_refinements\0prompt\0${dropped.count}\0${dropped.paths.join(",")}`,
|
|
6763
|
+
`${dropped.count} refinement(s) on the output schema for agent '${agent2._name}' (at ${dropped.paths.join(", ")}) are dropped from the model-facing JSON Schema \u2014 the model is never told the rule, then parsing rejects it. Surface it in the prompt (schemaPrompt) or repair with ctx.verify.`,
|
|
6764
|
+
silent
|
|
6765
|
+
);
|
|
6766
|
+
}
|
|
6767
|
+
}
|
|
6768
|
+
}
|
|
6769
|
+
const nativeOut = options?.nativeStructuredOutput ?? agent2._config.nativeStructuredOutput;
|
|
6770
|
+
if (nativeOut && toolDefs.length === 0) {
|
|
6771
|
+
const support = ctx.provider.nativeStructuredOutputSupport?.(ctx.model) ?? "schema";
|
|
6772
|
+
if (support !== "schema") {
|
|
6773
|
+
this.emitEvent({
|
|
6774
|
+
type: "schema_diagnostic",
|
|
6775
|
+
agent: agent2._name,
|
|
6776
|
+
data: {
|
|
6777
|
+
kind: "native_output_unsupported",
|
|
6778
|
+
...ctx.provider.name ? { provider: ctx.provider.name } : {},
|
|
6779
|
+
support
|
|
6780
|
+
}
|
|
6781
|
+
});
|
|
6782
|
+
warnSchemaDiagnosticOnce(
|
|
6783
|
+
`${agent2._name}\0native_output_unsupported\0${ctx.provider.name ?? ctx.model}`,
|
|
6784
|
+
`nativeStructuredOutput is set on agent '${agent2._name}' but provider '${ctx.provider.name ?? ctx.model}' ${support === "downgraded" ? "downgrades it to plain JSON mode" : support === "lossy" ? "sanitizes the schema lossily" : "ignores it structurally"} \u2014 the schema shape is not natively enforced. The prompt text remains the parse contract; the call proceeds.`,
|
|
6785
|
+
silent
|
|
6786
|
+
);
|
|
6787
|
+
}
|
|
6788
|
+
}
|
|
6789
|
+
const rootIsObject = schema instanceof import_zod.z.ZodObject;
|
|
6790
|
+
const hasTools = toolDefs.length > 0;
|
|
6791
|
+
if (this._streamingEnabled && (!rootIsObject || hasTools)) {
|
|
6792
|
+
const cause = !rootIsObject ? "non-object" : "tools";
|
|
6793
|
+
const rootType = schema.constructor?.name ?? "unknown";
|
|
6794
|
+
this.emitEvent({
|
|
6795
|
+
type: "schema_diagnostic",
|
|
6796
|
+
agent: agent2._name,
|
|
6797
|
+
data: { kind: "streaming_disabled", rootType, cause }
|
|
6798
|
+
});
|
|
6799
|
+
if (cause === "non-object") {
|
|
6800
|
+
warnSchemaDiagnosticOnce(
|
|
6801
|
+
`${agent2._name}\0streaming_disabled\0${rootType}`,
|
|
6802
|
+
`Progressive object streaming is disabled for agent '${agent2._name}' because the output schema root is ${rootType}, not a ZodObject. Wrap it \u2014 z.object({ result: <yourSchema> }) \u2014 to re-enable partial_object streaming.`,
|
|
6803
|
+
silent
|
|
6804
|
+
);
|
|
6805
|
+
}
|
|
6806
|
+
}
|
|
6807
|
+
}
|
|
6808
|
+
for (const tool2 of agent2._config.tools ?? []) {
|
|
6809
|
+
const toolTokens = estimateInlineSchemaTokens(tool2.inputSchema);
|
|
6810
|
+
if (toolTokens > threshold) {
|
|
6811
|
+
this.emitEvent({
|
|
6812
|
+
type: "schema_diagnostic",
|
|
6813
|
+
agent: agent2._name,
|
|
6814
|
+
data: {
|
|
6815
|
+
kind: "prompt_schema_oversized",
|
|
6816
|
+
estimatedTokens: toolTokens,
|
|
6817
|
+
threshold,
|
|
6818
|
+
site: "tool",
|
|
6819
|
+
tool: tool2.name
|
|
6820
|
+
}
|
|
6821
|
+
});
|
|
6822
|
+
}
|
|
6823
|
+
const droppedTool = detectDroppedRefinements(tool2.inputSchema);
|
|
6824
|
+
if (droppedTool.count > 0) {
|
|
6825
|
+
this.emitEvent({
|
|
6826
|
+
type: "schema_diagnostic",
|
|
6827
|
+
agent: agent2._name,
|
|
6828
|
+
data: {
|
|
6829
|
+
kind: "dropped_refinements",
|
|
6830
|
+
count: droppedTool.count,
|
|
6831
|
+
paths: droppedTool.paths,
|
|
6832
|
+
site: "tool",
|
|
6833
|
+
tool: tool2.name
|
|
6834
|
+
}
|
|
6835
|
+
});
|
|
6836
|
+
warnSchemaDiagnosticOnce(
|
|
6837
|
+
`${agent2._name}\0dropped_refinements\0tool\0${tool2.name}\0${droppedTool.count}`,
|
|
6838
|
+
`${droppedTool.count} refinement(s) on tool '${tool2.name}' input schema (at ${droppedTool.paths.join(", ")}) are dropped from the tool definition \u2014 the model is never told the constraint. Validate inside the tool handler instead.`,
|
|
6839
|
+
silent
|
|
6840
|
+
);
|
|
6841
|
+
}
|
|
6842
|
+
}
|
|
6843
|
+
}
|
|
5536
6844
|
buildToolDefs(agent2, resolvedHandoffs) {
|
|
5537
6845
|
const defs = [];
|
|
5538
6846
|
if (agent2._config.tools) {
|
|
@@ -6025,24 +7333,44 @@ ${summaryResponse.content}`
|
|
|
6025
7333
|
limit,
|
|
6026
7334
|
exceeded: false,
|
|
6027
7335
|
policy,
|
|
6028
|
-
abortController: controller
|
|
7336
|
+
abortController: controller,
|
|
7337
|
+
unpriced: false,
|
|
7338
|
+
unpricedCount: 0,
|
|
7339
|
+
unpricedWarned: false
|
|
6029
7340
|
};
|
|
6030
7341
|
const executeBudget = async () => {
|
|
6031
7342
|
try {
|
|
6032
7343
|
const value = budgetSignal ? await signalStorage.run(budgetSignal, fn) : await fn();
|
|
6033
7344
|
const totalCost = this.budgetContext.totalCost;
|
|
6034
7345
|
const exceeded = this.budgetContext.exceeded;
|
|
6035
|
-
|
|
7346
|
+
const unpriced = this.budgetContext.unpriced;
|
|
7347
|
+
return { value, budgetExceeded: exceeded, totalCost, unpriced };
|
|
6036
7348
|
} catch (err) {
|
|
6037
7349
|
if (this.budgetContext.exceeded) {
|
|
6038
|
-
return {
|
|
7350
|
+
return {
|
|
7351
|
+
value: null,
|
|
7352
|
+
budgetExceeded: true,
|
|
7353
|
+
totalCost: this.budgetContext.totalCost,
|
|
7354
|
+
unpriced: this.budgetContext.unpriced
|
|
7355
|
+
};
|
|
6039
7356
|
}
|
|
6040
7357
|
if (err instanceof DOMException && err.name === "AbortError" && controller) {
|
|
6041
|
-
return {
|
|
7358
|
+
return {
|
|
7359
|
+
value: null,
|
|
7360
|
+
budgetExceeded: true,
|
|
7361
|
+
totalCost: this.budgetContext.totalCost,
|
|
7362
|
+
unpriced: this.budgetContext.unpriced
|
|
7363
|
+
};
|
|
6042
7364
|
}
|
|
6043
7365
|
throw err;
|
|
6044
7366
|
} finally {
|
|
6045
|
-
if (parentBudget)
|
|
7367
|
+
if (parentBudget) {
|
|
7368
|
+
parentBudget.totalCost += this.budgetContext.totalCost;
|
|
7369
|
+
if (this.budgetContext.unpriced) {
|
|
7370
|
+
parentBudget.unpriced = true;
|
|
7371
|
+
parentBudget.unpricedCount += this.budgetContext.unpricedCount;
|
|
7372
|
+
}
|
|
7373
|
+
}
|
|
6046
7374
|
this.budgetContext = parentBudget;
|
|
6047
7375
|
}
|
|
6048
7376
|
};
|
|
@@ -6063,13 +7391,22 @@ ${summaryResponse.content}`
|
|
|
6063
7391
|
}
|
|
6064
7392
|
return executeBudget();
|
|
6065
7393
|
}
|
|
6066
|
-
/**
|
|
7394
|
+
/**
|
|
7395
|
+
* Get the current budget status, or null if not inside a budget block. Inside
|
|
7396
|
+
* nested budgets this reflects the **innermost active** block (the nested block's
|
|
7397
|
+
* `finally` restores the parent before control returns to it).
|
|
7398
|
+
*
|
|
7399
|
+
* `unpriced` is true when this block ran a model with no usable cost (unpriced /
|
|
7400
|
+
* pricing-table miss): `spent` is then a LOWER BOUND and `remaining` an upper bound —
|
|
7401
|
+
* the limit cannot be enforced on the unmeasured spend. See {@link BudgetResult.unpriced}.
|
|
7402
|
+
*/
|
|
6067
7403
|
getBudgetStatus() {
|
|
6068
7404
|
if (!this.budgetContext) return null;
|
|
6069
7405
|
return {
|
|
6070
7406
|
spent: this.budgetContext.totalCost,
|
|
6071
7407
|
limit: this.budgetContext.limit,
|
|
6072
|
-
remaining: Math.max(0, this.budgetContext.limit - this.budgetContext.totalCost)
|
|
7408
|
+
remaining: Math.max(0, this.budgetContext.limit - this.budgetContext.totalCost),
|
|
7409
|
+
unpriced: this.budgetContext.unpriced
|
|
6073
7410
|
};
|
|
6074
7411
|
}
|
|
6075
7412
|
// ── ctx.race() ────────────────────────────────────────────────────────
|
|
@@ -6651,6 +7988,8 @@ ${summaryResponse.content}`
|
|
|
6651
7988
|
});
|
|
6652
7989
|
return this.ask(agents[0], prompt, {
|
|
6653
7990
|
schema: options?.schema,
|
|
7991
|
+
schemaPrompt: options?.schemaPrompt,
|
|
7992
|
+
nativeStructuredOutput: options?.nativeStructuredOutput,
|
|
6654
7993
|
retries: options?.retries,
|
|
6655
7994
|
metadata: options?.metadata,
|
|
6656
7995
|
validate: options?.validate,
|
|
@@ -6695,6 +8034,8 @@ ${summaryResponse.content}`
|
|
|
6695
8034
|
});
|
|
6696
8035
|
return this.ask(routerAgent, prompt, {
|
|
6697
8036
|
schema: options?.schema,
|
|
8037
|
+
schemaPrompt: options?.schemaPrompt,
|
|
8038
|
+
nativeStructuredOutput: options?.nativeStructuredOutput,
|
|
6698
8039
|
retries: options?.retries,
|
|
6699
8040
|
metadata: options?.metadata,
|
|
6700
8041
|
validate: options?.validate,
|
|
@@ -6713,8 +8054,25 @@ ${summaryResponse.content}`
|
|
|
6713
8054
|
const frame = askStorage.getStore();
|
|
6714
8055
|
const step = (frame?.stepRef ?? this.stepRefRoot).value++;
|
|
6715
8056
|
const cost = partial.cost;
|
|
6716
|
-
if (
|
|
6717
|
-
|
|
8057
|
+
if (COST_BEARING_LEAF_TYPES.includes(partial.type)) {
|
|
8058
|
+
const usable = isUsableCost(cost);
|
|
8059
|
+
const unpriced = !usable && hasPositiveTokens(
|
|
8060
|
+
partial
|
|
8061
|
+
);
|
|
8062
|
+
if (frame) {
|
|
8063
|
+
if (usable) frame.askCost.value += cost;
|
|
8064
|
+
else if (unpriced) frame.askUnpriced = true;
|
|
8065
|
+
}
|
|
8066
|
+
if (unpriced && this.budgetContext) {
|
|
8067
|
+
this.budgetContext.unpriced = true;
|
|
8068
|
+
this.budgetContext.unpricedCount += 1;
|
|
8069
|
+
if (!this.budgetContext.unpricedWarned && Number.isFinite(this.budgetContext.limit)) {
|
|
8070
|
+
this.budgetContext.unpricedWarned = true;
|
|
8071
|
+
console.warn(
|
|
8072
|
+
"Budget honesty: unpriced work detected \u2014 this budget's cost limit is a lower bound and is NOT enforced on unpriced models. See docs/observability.md#budget-honesty."
|
|
8073
|
+
);
|
|
8074
|
+
}
|
|
8075
|
+
}
|
|
6718
8076
|
}
|
|
6719
8077
|
const event = {
|
|
6720
8078
|
executionId: this.executionId,
|
|
@@ -8541,7 +9899,7 @@ var StreamingFlusher = class {
|
|
|
8541
9899
|
await Promise.allSettled(pendings);
|
|
8542
9900
|
}
|
|
8543
9901
|
};
|
|
8544
|
-
function
|
|
9902
|
+
function isAbortError2(err) {
|
|
8545
9903
|
return typeof err === "object" && err !== null && err.name === "AbortError";
|
|
8546
9904
|
}
|
|
8547
9905
|
function pushEventBounded(execInfo, event, cap) {
|
|
@@ -8804,7 +10162,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
8804
10162
|
this.persistExecution(execInfo);
|
|
8805
10163
|
return result;
|
|
8806
10164
|
} catch (err) {
|
|
8807
|
-
const aborted =
|
|
10165
|
+
const aborted = isAbortError2(err);
|
|
8808
10166
|
execInfo.status = "failed";
|
|
8809
10167
|
execInfo.completedAt = Date.now();
|
|
8810
10168
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
@@ -9078,7 +10436,10 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9078
10436
|
totalCost: 0,
|
|
9079
10437
|
limit: budgetLimit,
|
|
9080
10438
|
exceeded: false,
|
|
9081
|
-
policy: "finish_and_stop"
|
|
10439
|
+
policy: "finish_and_stop",
|
|
10440
|
+
unpriced: false,
|
|
10441
|
+
unpricedCount: 0,
|
|
10442
|
+
unpricedWarned: false
|
|
9082
10443
|
}
|
|
9083
10444
|
});
|
|
9084
10445
|
}
|
|
@@ -9113,6 +10474,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9113
10474
|
status: "running",
|
|
9114
10475
|
events: [],
|
|
9115
10476
|
totalCost: 0,
|
|
10477
|
+
unpriced: false,
|
|
9116
10478
|
startedAt: Date.now(),
|
|
9117
10479
|
duration: 0,
|
|
9118
10480
|
metadata: liftPersistedMetadata(options?.metadata)
|
|
@@ -9131,6 +10493,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9131
10493
|
onTrace: (event) => {
|
|
9132
10494
|
pushEventBounded(execInfo, event, this.maxEventsPerExecution);
|
|
9133
10495
|
execInfo.totalCost += eventCostContribution(event);
|
|
10496
|
+
if (isUnpricedLeaf(event)) execInfo.unpriced = true;
|
|
9134
10497
|
if (this.streamableExecutionIds.has(executionId)) {
|
|
9135
10498
|
this.streamingFlusher?.append(executionId, event);
|
|
9136
10499
|
}
|
|
@@ -9177,7 +10540,10 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9177
10540
|
totalCost: 0,
|
|
9178
10541
|
limit: Infinity,
|
|
9179
10542
|
exceeded: false,
|
|
9180
|
-
policy: "finish_and_stop"
|
|
10543
|
+
policy: "finish_and_stop",
|
|
10544
|
+
unpriced: false,
|
|
10545
|
+
unpricedCount: 0,
|
|
10546
|
+
unpricedWarned: false
|
|
9181
10547
|
}
|
|
9182
10548
|
});
|
|
9183
10549
|
return this.spanManager.withSpanAsync(
|
|
@@ -9221,6 +10587,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9221
10587
|
status: "running",
|
|
9222
10588
|
events: [],
|
|
9223
10589
|
totalCost: 0,
|
|
10590
|
+
unpriced: false,
|
|
9224
10591
|
startedAt: Date.now(),
|
|
9225
10592
|
duration: 0,
|
|
9226
10593
|
metadata: liftPersistedMetadata(options?.metadata)
|
|
@@ -9244,6 +10611,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9244
10611
|
onTrace: (event) => {
|
|
9245
10612
|
pushEventBounded(execInfo, event, this.maxEventsPerExecution);
|
|
9246
10613
|
execInfo.totalCost += eventCostContribution(event);
|
|
10614
|
+
if (isUnpricedLeaf(event)) execInfo.unpriced = true;
|
|
9247
10615
|
if (this.streamableExecutionIds.has(executionId)) {
|
|
9248
10616
|
this.streamingFlusher?.append(executionId, event);
|
|
9249
10617
|
}
|
|
@@ -9291,7 +10659,10 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9291
10659
|
totalCost: 0,
|
|
9292
10660
|
limit: Infinity,
|
|
9293
10661
|
exceeded: false,
|
|
9294
|
-
policy: "finish_and_stop"
|
|
10662
|
+
policy: "finish_and_stop",
|
|
10663
|
+
unpriced: false,
|
|
10664
|
+
unpricedCount: 0,
|
|
10665
|
+
unpricedWarned: false
|
|
9295
10666
|
}
|
|
9296
10667
|
});
|
|
9297
10668
|
return this.spanManager.withSpanAsync(
|
|
@@ -9453,12 +10824,14 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9453
10824
|
const completedAt = lastEvent.timestamp ?? startedAt;
|
|
9454
10825
|
const workflowName = workflowStartEvent?.workflow ?? RECOVERED_UNKNOWN_WORKFLOW;
|
|
9455
10826
|
const totalCost = boundedEvents.reduce((sum, e) => sum + eventCostContribution(e), 0);
|
|
10827
|
+
const unpriced = boundedEvents.some(isUnpricedLeaf);
|
|
9456
10828
|
const synthesized = {
|
|
9457
10829
|
executionId: id,
|
|
9458
10830
|
workflow: workflowName,
|
|
9459
10831
|
status: "failed",
|
|
9460
10832
|
events: boundedEvents,
|
|
9461
10833
|
totalCost,
|
|
10834
|
+
unpriced,
|
|
9462
10835
|
startedAt,
|
|
9463
10836
|
completedAt,
|
|
9464
10837
|
duration: completedAt - startedAt,
|
|
@@ -9760,8 +11133,8 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9760
11133
|
* Works with both `createContext()` and `execute()` calls inside `fn`.
|
|
9761
11134
|
*/
|
|
9762
11135
|
async trackCost(fn) {
|
|
9763
|
-
const { result, cost } = await this.trackExecution(fn);
|
|
9764
|
-
return { result, cost };
|
|
11136
|
+
const { result, cost, unpriced } = await this.trackExecution(fn);
|
|
11137
|
+
return { result, cost, unpriced };
|
|
9765
11138
|
}
|
|
9766
11139
|
/**
|
|
9767
11140
|
* Track cost and execution metadata across any runtime operations within the given function.
|
|
@@ -9806,10 +11179,12 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9806
11179
|
const workflowCalls = /* @__PURE__ */ new Map();
|
|
9807
11180
|
const tokens = { input: 0, output: 0, reasoning: 0 };
|
|
9808
11181
|
let agentCalls = 0;
|
|
11182
|
+
let unpriced = false;
|
|
9809
11183
|
const capturedTraces = options?.captureTraces ? [] : void 0;
|
|
9810
11184
|
const listener = (event) => {
|
|
9811
11185
|
if (!scope.trackedIds.has(event.executionId)) return;
|
|
9812
11186
|
scope.totalCost += eventCostContribution(event);
|
|
11187
|
+
if (isUnpricedLeaf(event)) unpriced = true;
|
|
9813
11188
|
if (event.type === "agent_call_end") {
|
|
9814
11189
|
if (event.model) modelCalls.set(event.model, (modelCalls.get(event.model) ?? 0) + 1);
|
|
9815
11190
|
agentCalls++;
|
|
@@ -9847,6 +11222,7 @@ var AxlRuntime = class extends import_node_events2.EventEmitter {
|
|
|
9847
11222
|
return {
|
|
9848
11223
|
result,
|
|
9849
11224
|
cost: scope.totalCost,
|
|
11225
|
+
unpriced,
|
|
9850
11226
|
...capturedTraces ? { traces: capturedTraces } : {},
|
|
9851
11227
|
metadata: {
|
|
9852
11228
|
models: [...modelCalls.keys()],
|
|
@@ -10741,22 +12117,40 @@ var OpenAIEmbedder = class {
|
|
|
10741
12117
|
this.dimensions = options.dimensions ?? 1536;
|
|
10742
12118
|
}
|
|
10743
12119
|
async embed(texts, signal) {
|
|
10744
|
-
const response = await fetchWithRetry(
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
12120
|
+
const response = await fetchWithRetry(
|
|
12121
|
+
`${this.baseUrl}/v1/embeddings`,
|
|
12122
|
+
{
|
|
12123
|
+
method: "POST",
|
|
12124
|
+
headers: {
|
|
12125
|
+
"Content-Type": "application/json",
|
|
12126
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
12127
|
+
},
|
|
12128
|
+
body: JSON.stringify({
|
|
12129
|
+
model: this.model,
|
|
12130
|
+
input: texts,
|
|
12131
|
+
dimensions: this.dimensions
|
|
12132
|
+
}),
|
|
12133
|
+
signal
|
|
10749
12134
|
},
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
}),
|
|
10755
|
-
signal
|
|
10756
|
-
});
|
|
12135
|
+
// Participate in the same typed-error contract as the provider adapters: a
|
|
12136
|
+
// thrown network failure normalizes to ProviderError{status:0,provider:'openai'}.
|
|
12137
|
+
{ provider: "openai" }
|
|
12138
|
+
);
|
|
10757
12139
|
if (!response.ok) {
|
|
10758
12140
|
const body = await response.text();
|
|
10759
|
-
|
|
12141
|
+
let detail = body;
|
|
12142
|
+
try {
|
|
12143
|
+
const json2 = JSON.parse(body);
|
|
12144
|
+
detail = json2.error?.message ?? json2.message ?? body;
|
|
12145
|
+
} catch {
|
|
12146
|
+
}
|
|
12147
|
+
throw buildProviderError({
|
|
12148
|
+
provider: "openai",
|
|
12149
|
+
status: response.status,
|
|
12150
|
+
headers: response.headers,
|
|
12151
|
+
message: `OpenAI embeddings API error (${response.status}): ${detail}`,
|
|
12152
|
+
body
|
|
12153
|
+
});
|
|
10760
12154
|
}
|
|
10761
12155
|
const json = await response.json();
|
|
10762
12156
|
const vectors = json.data.sort((a, b) => a.index - b.index).map((d) => d.embedding);
|
|
@@ -10898,57 +12292,88 @@ function cosineSimilarity2(a, b) {
|
|
|
10898
12292
|
// Annotate the CommonJS export names for ESM import in node:
|
|
10899
12293
|
0 && (module.exports = {
|
|
10900
12294
|
AXL_EVENT_TYPES,
|
|
12295
|
+
AZURE_PROFILE,
|
|
10901
12296
|
AnthropicProvider,
|
|
10902
12297
|
AxlError,
|
|
10903
12298
|
AxlEventBus,
|
|
10904
12299
|
AxlRuntime,
|
|
10905
12300
|
AxlStream,
|
|
12301
|
+
BEDROCK_PROFILE,
|
|
12302
|
+
BUILTIN_PROFILES,
|
|
10906
12303
|
BudgetExceededError,
|
|
10907
12304
|
CONFIG_CANDIDATES,
|
|
10908
12305
|
COST_BEARING_LEAF_TYPES,
|
|
12306
|
+
DEEPSEEK_PROFILE,
|
|
10909
12307
|
EventStreamOverflowError,
|
|
12308
|
+
GROQ_PROFILE,
|
|
10910
12309
|
GeminiProvider,
|
|
10911
12310
|
GuardrailError,
|
|
10912
12311
|
InMemoryVectorStore,
|
|
12312
|
+
LLAMACPP_PROFILE,
|
|
12313
|
+
LMSTUDIO_PROFILE,
|
|
12314
|
+
MISTRAL_PROFILE,
|
|
10913
12315
|
MaxTurnsError,
|
|
10914
12316
|
McpManager,
|
|
10915
12317
|
MemoryManager,
|
|
10916
12318
|
MemoryStore,
|
|
10917
12319
|
NoConsensus,
|
|
10918
12320
|
NoopSpanManager,
|
|
12321
|
+
OLLAMA_PROFILE,
|
|
12322
|
+
OPENAI_PRICING,
|
|
12323
|
+
OPENAI_PROFILE,
|
|
12324
|
+
OPENROUTER_PROFILE,
|
|
12325
|
+
OpenAICompatibleProvider,
|
|
10919
12326
|
OpenAIEmbedder,
|
|
10920
12327
|
OpenAIProvider,
|
|
10921
12328
|
OpenAIResponsesProvider,
|
|
12329
|
+
ProviderError,
|
|
10922
12330
|
ProviderRegistry,
|
|
10923
12331
|
QuorumNotMet,
|
|
10924
12332
|
REDACTED,
|
|
10925
12333
|
REDACTION_RULES,
|
|
10926
12334
|
RateLimiter,
|
|
10927
12335
|
RedisStore,
|
|
12336
|
+
SGLANG_PROFILE,
|
|
10928
12337
|
SQLiteStore,
|
|
10929
12338
|
Session,
|
|
10930
12339
|
SqliteVectorStore,
|
|
12340
|
+
ThinkTagScanner,
|
|
10931
12341
|
TimeoutError,
|
|
10932
12342
|
ToolDenied,
|
|
12343
|
+
ToolModelOutputError,
|
|
12344
|
+
VLLM_PROFILE,
|
|
10933
12345
|
ValidationError,
|
|
10934
12346
|
VerifyError,
|
|
10935
12347
|
WorkflowContext,
|
|
12348
|
+
XAI_PROFILE,
|
|
10936
12349
|
agent,
|
|
10937
12350
|
createSpanManager,
|
|
10938
12351
|
defineConfig,
|
|
12352
|
+
estimateOpenAICost,
|
|
10939
12353
|
eventCostContribution,
|
|
10940
12354
|
expandGlob,
|
|
10941
12355
|
extractJson,
|
|
12356
|
+
extractThinkTags,
|
|
10942
12357
|
findConfig,
|
|
12358
|
+
hasPositiveTokens,
|
|
10943
12359
|
importModule,
|
|
10944
12360
|
isCostBearingLeaf,
|
|
12361
|
+
isRetryableStatus,
|
|
10945
12362
|
isRootLevel,
|
|
12363
|
+
isUnpricedLeaf,
|
|
12364
|
+
isUsableCost,
|
|
10946
12365
|
needsTsxLoader,
|
|
12366
|
+
openaiReasoningEmit,
|
|
10947
12367
|
parsePartialJson,
|
|
10948
12368
|
pickDefault,
|
|
10949
12369
|
pickExport,
|
|
12370
|
+
priceFromTable,
|
|
12371
|
+
reasoningEffortEmit,
|
|
12372
|
+
reasoningObjectEmit,
|
|
10950
12373
|
redactEvent,
|
|
10951
12374
|
registerConditions,
|
|
12375
|
+
resolveApiKey,
|
|
12376
|
+
resolvePerModel,
|
|
10952
12377
|
resolveRuntime,
|
|
10953
12378
|
resolveThinkingOptions,
|
|
10954
12379
|
stringStreamFromEvents,
|