@axlsdk/axl 0.21.1 → 0.22.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 +12 -1
- package/dist/index.cjs +2859 -233
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +580 -47
- package/dist/index.d.ts +580 -47
- package/dist/index.js +2835 -222
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -14,6 +14,75 @@ var AxlError = class extends Error {
|
|
|
14
14
|
this.code = code;
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
|
+
var InvalidModelInputError = class extends AxlError {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super("INVALID_MODEL_INPUT", message);
|
|
20
|
+
this.name = "InvalidModelInputError";
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
var UnsupportedModelInputError = class extends AxlError {
|
|
24
|
+
provider;
|
|
25
|
+
model;
|
|
26
|
+
modality;
|
|
27
|
+
source;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
super(
|
|
30
|
+
"UNSUPPORTED_MODEL_INPUT",
|
|
31
|
+
`Provider '${options.provider}' model '${options.model}' does not support ${options.feature ?? options.modality}${options.source ? ` from ${options.source}` : ""}`
|
|
32
|
+
);
|
|
33
|
+
this.name = "UnsupportedModelInputError";
|
|
34
|
+
this.provider = options.provider;
|
|
35
|
+
this.model = options.model;
|
|
36
|
+
this.modality = options.modality;
|
|
37
|
+
this.source = options.source;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var InvalidTranscriptionInputError = class extends AxlError {
|
|
41
|
+
constructor(message) {
|
|
42
|
+
super("INVALID_TRANSCRIPTION_INPUT", message);
|
|
43
|
+
this.name = "InvalidTranscriptionInputError";
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var UnsupportedTranscriptionInputError = class extends AxlError {
|
|
47
|
+
provider;
|
|
48
|
+
model;
|
|
49
|
+
constructor(options) {
|
|
50
|
+
super(
|
|
51
|
+
"UNSUPPORTED_TRANSCRIPTION_INPUT",
|
|
52
|
+
`Transcription provider '${options.provider}'${options.model ? ` model '${options.model}'` : ""} does not support ${options.feature ?? "this request"}`
|
|
53
|
+
);
|
|
54
|
+
this.name = "UnsupportedTranscriptionInputError";
|
|
55
|
+
this.provider = options.provider;
|
|
56
|
+
this.model = options.model;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var TranscriptionOperationError = class extends AxlError {
|
|
60
|
+
provider;
|
|
61
|
+
model;
|
|
62
|
+
usage;
|
|
63
|
+
pricingStatus;
|
|
64
|
+
cleanupStatus;
|
|
65
|
+
/** Safe HTTP diagnostics projected from ProviderError when available. */
|
|
66
|
+
status;
|
|
67
|
+
retryable;
|
|
68
|
+
retryAfterMs;
|
|
69
|
+
requestId;
|
|
70
|
+
constructor(options) {
|
|
71
|
+
super("TRANSCRIPTION_PROVIDER_ERROR", "Transcription provider operation failed");
|
|
72
|
+
this.name = "TranscriptionOperationError";
|
|
73
|
+
this.provider = options.provider;
|
|
74
|
+
this.model = options.model;
|
|
75
|
+
this.usage = options.usage;
|
|
76
|
+
this.pricingStatus = options.pricingStatus;
|
|
77
|
+
this.cleanupStatus = options.cleanupStatus;
|
|
78
|
+
this.status = options.status;
|
|
79
|
+
this.retryable = options.retryable;
|
|
80
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
81
|
+
this.requestId = options.requestId;
|
|
82
|
+
if (options.cause !== void 0)
|
|
83
|
+
Object.defineProperty(this, "cause", { enumerable: false, value: options.cause });
|
|
84
|
+
}
|
|
85
|
+
};
|
|
17
86
|
var EventStreamOverflowError = class extends Error {
|
|
18
87
|
maxQueued;
|
|
19
88
|
eventType;
|
|
@@ -986,6 +1055,37 @@ function unsafeTransportError(surface, remediation, exposedData) {
|
|
|
986
1055
|
}
|
|
987
1056
|
|
|
988
1057
|
// src/providers/openai-compatible.ts
|
|
1058
|
+
function compatibleImageBase64(source) {
|
|
1059
|
+
return source.type === "base64" ? source.data : Buffer.from(source.data.buffer, source.data.byteOffset, source.data.byteLength).toString(
|
|
1060
|
+
"base64"
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1063
|
+
function openRouterImageParts(parts, model) {
|
|
1064
|
+
const content = [];
|
|
1065
|
+
for (const part of parts) {
|
|
1066
|
+
if (part.type === "text") {
|
|
1067
|
+
content.push({ type: "text", text: part.text });
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
const { source } = part;
|
|
1071
|
+
if (source.type === "provider-file") {
|
|
1072
|
+
throw new UnsupportedModelInputError({
|
|
1073
|
+
provider: "openrouter",
|
|
1074
|
+
model,
|
|
1075
|
+
modality: "image",
|
|
1076
|
+
source: "provider-file"
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
content.push({
|
|
1080
|
+
type: "image_url",
|
|
1081
|
+
image_url: {
|
|
1082
|
+
url: source.type === "url" ? source.url : `data:${source.mediaType};base64,${compatibleImageBase64(source)}`
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1085
|
+
if (part.label) content.push({ type: "text", text: `[Image: ${part.label}]` });
|
|
1086
|
+
}
|
|
1087
|
+
return content;
|
|
1088
|
+
}
|
|
989
1089
|
function resolvePerModel(value, model, fallback) {
|
|
990
1090
|
if (value === void 0) return fallback;
|
|
991
1091
|
return typeof value === "function" ? value(model) : value;
|
|
@@ -1101,6 +1201,40 @@ function normalizeMessageContent(content) {
|
|
|
1101
1201
|
}
|
|
1102
1202
|
var OpenAICompatibleProvider = class {
|
|
1103
1203
|
name;
|
|
1204
|
+
inputCapabilities(model) {
|
|
1205
|
+
return this.name === "openrouter" && model.trim().length > 0 ? { image: { sources: ["url", "bytes", "base64"] } } : {};
|
|
1206
|
+
}
|
|
1207
|
+
validateInput(request) {
|
|
1208
|
+
const effectiveModel = typeof request.providerOptions?.model === "string" ? request.providerOptions.model : request.model;
|
|
1209
|
+
const fail = (source, feature) => {
|
|
1210
|
+
throw new UnsupportedModelInputError({
|
|
1211
|
+
provider: this.name,
|
|
1212
|
+
model: effectiveModel || request.model,
|
|
1213
|
+
modality: "image",
|
|
1214
|
+
...source ? { source } : {},
|
|
1215
|
+
...feature ? { feature } : {}
|
|
1216
|
+
});
|
|
1217
|
+
};
|
|
1218
|
+
if (this.name !== "openrouter" || effectiveModel.trim().length === 0) {
|
|
1219
|
+
fail(void 0, "image input for this model");
|
|
1220
|
+
}
|
|
1221
|
+
if (request.providerOptions && "messages" in request.providerOptions) {
|
|
1222
|
+
fail(void 0, "raw messages providerOptions");
|
|
1223
|
+
}
|
|
1224
|
+
for (const message of request.history) {
|
|
1225
|
+
if (!Array.isArray(message.content)) continue;
|
|
1226
|
+
if (message.role !== "user") fail(void 0, "rich non-user history");
|
|
1227
|
+
if (message.content.some(
|
|
1228
|
+
(part) => part.type === "image" && part.source.type === "provider-file"
|
|
1229
|
+
)) {
|
|
1230
|
+
fail("provider-file");
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
if (Array.isArray(request.input) && request.input.some((part) => part.type === "image" && part.source.type === "provider-file")) {
|
|
1234
|
+
fail("provider-file");
|
|
1235
|
+
}
|
|
1236
|
+
return { effectiveModel };
|
|
1237
|
+
}
|
|
1104
1238
|
/** `json_schema` is honored natively when the profile's `supportsJsonSchema`
|
|
1105
1239
|
* capability is true for this model (the default); otherwise the engine
|
|
1106
1240
|
* downgrades the request to plain `json_object` (see `buildRequestBody`),
|
|
@@ -1274,7 +1408,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1274
1408
|
formatMessage(msg, model) {
|
|
1275
1409
|
const out = {
|
|
1276
1410
|
role: this.profile.roleFor ? this.profile.roleFor(msg.role, model) : msg.role,
|
|
1277
|
-
content: msg.content
|
|
1411
|
+
content: Array.isArray(msg.content) && this.name === "openrouter" ? openRouterImageParts(msg.content, model) : msg.content
|
|
1278
1412
|
};
|
|
1279
1413
|
if (msg.name && (this.profile.capabilities?.emitsMessageName ?? true)) out.name = msg.name;
|
|
1280
1414
|
if (msg.tool_calls) out.tool_calls = msg.tool_calls;
|
|
@@ -2021,8 +2155,88 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
2021
2155
|
};
|
|
2022
2156
|
|
|
2023
2157
|
// src/providers/openai-responses.ts
|
|
2158
|
+
var OPENAI_RESPONSES_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
2159
|
+
"gpt-4o",
|
|
2160
|
+
"gpt-4o-2024-08-06",
|
|
2161
|
+
"gpt-4o-2024-11-20",
|
|
2162
|
+
"gpt-4o-mini",
|
|
2163
|
+
"gpt-4o-mini-2024-07-18"
|
|
2164
|
+
]);
|
|
2165
|
+
function base64FromSource(source) {
|
|
2166
|
+
return source.type === "base64" ? source.data : Buffer.from(source.data.buffer, source.data.byteOffset, source.data.byteLength).toString(
|
|
2167
|
+
"base64"
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2170
|
+
function responseImageParts(parts) {
|
|
2171
|
+
const mapped = [];
|
|
2172
|
+
for (const part of parts) {
|
|
2173
|
+
if (part.type === "text") {
|
|
2174
|
+
mapped.push({ type: "input_text", text: part.text });
|
|
2175
|
+
continue;
|
|
2176
|
+
}
|
|
2177
|
+
const { source } = part;
|
|
2178
|
+
if (source.type === "provider-file") {
|
|
2179
|
+
if (source.provider !== "openai-responses") {
|
|
2180
|
+
throw new UnsupportedModelInputError({
|
|
2181
|
+
provider: "openai-responses",
|
|
2182
|
+
model: "unknown",
|
|
2183
|
+
modality: "image",
|
|
2184
|
+
source: "provider-file"
|
|
2185
|
+
});
|
|
2186
|
+
}
|
|
2187
|
+
mapped.push({ type: "input_image", file_id: source.reference });
|
|
2188
|
+
} else if (source.type === "url") {
|
|
2189
|
+
mapped.push({ type: "input_image", image_url: source.url });
|
|
2190
|
+
} else {
|
|
2191
|
+
mapped.push({
|
|
2192
|
+
type: "input_image",
|
|
2193
|
+
image_url: `data:${source.mediaType};base64,${base64FromSource(source)}`
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
if (part.label) mapped.push({ type: "input_text", text: `[Image: ${part.label}]` });
|
|
2197
|
+
}
|
|
2198
|
+
return mapped;
|
|
2199
|
+
}
|
|
2024
2200
|
var OpenAIResponsesProvider = class {
|
|
2025
2201
|
name = "openai-responses";
|
|
2202
|
+
inputCapabilities(model) {
|
|
2203
|
+
return OPENAI_RESPONSES_IMAGE_MODELS.has(model) ? { image: { sources: ["url", "bytes", "base64", "provider-file"] } } : {};
|
|
2204
|
+
}
|
|
2205
|
+
validateInput(request) {
|
|
2206
|
+
const effectiveModel = typeof request.providerOptions?.model === "string" ? request.providerOptions.model : request.model;
|
|
2207
|
+
const fail = (source, feature) => {
|
|
2208
|
+
throw new UnsupportedModelInputError({
|
|
2209
|
+
provider: this.name,
|
|
2210
|
+
model: effectiveModel || request.model,
|
|
2211
|
+
modality: "image",
|
|
2212
|
+
...source ? { source } : {},
|
|
2213
|
+
...feature ? { feature } : {}
|
|
2214
|
+
});
|
|
2215
|
+
};
|
|
2216
|
+
if (!OPENAI_RESPONSES_IMAGE_MODELS.has(effectiveModel)) {
|
|
2217
|
+
fail(void 0, "image input for this model");
|
|
2218
|
+
}
|
|
2219
|
+
if (request.providerOptions && "input" in request.providerOptions) {
|
|
2220
|
+
fail(void 0, "raw input-container providerOptions");
|
|
2221
|
+
}
|
|
2222
|
+
for (const message of request.history) {
|
|
2223
|
+
if (!Array.isArray(message.content)) continue;
|
|
2224
|
+
if (message.role !== "user") fail(void 0, "rich non-user history");
|
|
2225
|
+
for (const part of message.content) {
|
|
2226
|
+
if (part.type === "image" && part.source.type === "provider-file" && part.source.provider !== this.name) {
|
|
2227
|
+
fail("provider-file");
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
if (Array.isArray(request.input)) {
|
|
2232
|
+
for (const part of request.input) {
|
|
2233
|
+
if (part.type === "image" && part.source.type === "provider-file" && part.source.provider !== this.name) {
|
|
2234
|
+
fail("provider-file");
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
return { effectiveModel };
|
|
2239
|
+
}
|
|
2026
2240
|
/** The Responses API honors `json_schema` (structured outputs) natively. */
|
|
2027
2241
|
nativeStructuredOutputSupport() {
|
|
2028
2242
|
return "schema";
|
|
@@ -2187,11 +2401,12 @@ var OpenAIResponsesProvider = class {
|
|
|
2187
2401
|
buildInput(messages) {
|
|
2188
2402
|
const input = [];
|
|
2189
2403
|
for (const msg of messages) {
|
|
2404
|
+
const text = typeof msg.content === "string" ? msg.content : "";
|
|
2190
2405
|
if (msg.role === "tool") {
|
|
2191
2406
|
input.push({
|
|
2192
2407
|
type: "function_call_output",
|
|
2193
2408
|
call_id: msg.tool_call_id ?? "",
|
|
2194
|
-
output:
|
|
2409
|
+
output: text
|
|
2195
2410
|
});
|
|
2196
2411
|
} else if (msg.role === "assistant" && msg.tool_calls && msg.tool_calls.length > 0) {
|
|
2197
2412
|
const reasoningItems = msg.providerMetadata?.openaiReasoningItems;
|
|
@@ -2200,8 +2415,8 @@ var OpenAIResponsesProvider = class {
|
|
|
2200
2415
|
input.push(item);
|
|
2201
2416
|
}
|
|
2202
2417
|
}
|
|
2203
|
-
if (
|
|
2204
|
-
input.push({ type: "message", role: "assistant", content:
|
|
2418
|
+
if (text) {
|
|
2419
|
+
input.push({ type: "message", role: "assistant", content: text });
|
|
2205
2420
|
}
|
|
2206
2421
|
for (const tc of msg.tool_calls) {
|
|
2207
2422
|
input.push({
|
|
@@ -2221,7 +2436,7 @@ var OpenAIResponsesProvider = class {
|
|
|
2221
2436
|
input.push({
|
|
2222
2437
|
type: "message",
|
|
2223
2438
|
role: msg.role,
|
|
2224
|
-
content: msg.content
|
|
2439
|
+
content: typeof msg.content === "string" ? msg.content : responseImageParts(msg.content)
|
|
2225
2440
|
});
|
|
2226
2441
|
}
|
|
2227
2442
|
}
|
|
@@ -2288,7 +2503,7 @@ var OpenAIResponsesProvider = class {
|
|
|
2288
2503
|
cached_tokens: json.usage.input_tokens_details?.cached_tokens,
|
|
2289
2504
|
cache_write_tokens: json.usage.input_tokens_details?.cache_write_tokens
|
|
2290
2505
|
} : void 0;
|
|
2291
|
-
const cost = usage ? estimateDirectOpenAICost(json.model ?? model, usage, {
|
|
2506
|
+
const cost = usage && !this.requestContainsImages(request) ? estimateDirectOpenAICost(json.model ?? model, usage, {
|
|
2292
2507
|
baseUrl: this.baseUrl,
|
|
2293
2508
|
request,
|
|
2294
2509
|
response: json
|
|
@@ -2303,6 +2518,16 @@ var OpenAIResponsesProvider = class {
|
|
|
2303
2518
|
providerMetadata
|
|
2304
2519
|
};
|
|
2305
2520
|
}
|
|
2521
|
+
/** Responses usage does not report a media-inclusive price, so rich calls stay unpriced. */
|
|
2522
|
+
requestContainsImages(request) {
|
|
2523
|
+
return Array.isArray(request?.input) && request.input.some((item) => {
|
|
2524
|
+
if (!item || typeof item !== "object") return false;
|
|
2525
|
+
const content = item.content;
|
|
2526
|
+
return Array.isArray(content) && content.some(
|
|
2527
|
+
(part) => part !== null && typeof part === "object" && part.type === "input_image"
|
|
2528
|
+
);
|
|
2529
|
+
});
|
|
2530
|
+
}
|
|
2306
2531
|
// ---------------------------------------------------------------------------
|
|
2307
2532
|
// Internal: parse SSE stream
|
|
2308
2533
|
// ---------------------------------------------------------------------------
|
|
@@ -2390,7 +2615,7 @@ var OpenAIResponsesProvider = class {
|
|
|
2390
2615
|
return {
|
|
2391
2616
|
type: "done",
|
|
2392
2617
|
usage,
|
|
2393
|
-
cost: usage ? estimateDirectOpenAICost(response?.model ?? model, usage, {
|
|
2618
|
+
cost: usage && !this.requestContainsImages(request) ? estimateDirectOpenAICost(response?.model ?? model, usage, {
|
|
2394
2619
|
baseUrl: this.baseUrl,
|
|
2395
2620
|
request,
|
|
2396
2621
|
response
|
|
@@ -2427,7 +2652,51 @@ var OpenAIResponsesProvider = class {
|
|
|
2427
2652
|
};
|
|
2428
2653
|
|
|
2429
2654
|
// src/providers/anthropic.ts
|
|
2655
|
+
function anthropicBase64(source) {
|
|
2656
|
+
return source.type === "base64" ? source.data : Buffer.from(source.data.buffer, source.data.byteOffset, source.data.byteLength).toString(
|
|
2657
|
+
"base64"
|
|
2658
|
+
);
|
|
2659
|
+
}
|
|
2660
|
+
function anthropicImageBlocks(parts) {
|
|
2661
|
+
const blocks = [];
|
|
2662
|
+
for (const part of parts) {
|
|
2663
|
+
if (part.type === "text") {
|
|
2664
|
+
blocks.push({ type: "text", text: part.text });
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
const { source } = part;
|
|
2668
|
+
if (source.type === "provider-file") {
|
|
2669
|
+
if (source.provider !== "anthropic") {
|
|
2670
|
+
throw new UnsupportedModelInputError({
|
|
2671
|
+
provider: "anthropic",
|
|
2672
|
+
model: "unknown",
|
|
2673
|
+
modality: "image",
|
|
2674
|
+
source: "provider-file"
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2677
|
+
blocks.push({ type: "image", source: { type: "file", file_id: source.reference } });
|
|
2678
|
+
} else if (source.type === "url") {
|
|
2679
|
+
blocks.push({ type: "image", source: { type: "url", url: source.url } });
|
|
2680
|
+
} else {
|
|
2681
|
+
blocks.push({
|
|
2682
|
+
type: "image",
|
|
2683
|
+
source: { type: "base64", media_type: source.mediaType, data: anthropicBase64(source) }
|
|
2684
|
+
});
|
|
2685
|
+
}
|
|
2686
|
+
if (part.label) blocks.push({ type: "text", text: `[Image: ${part.label}]` });
|
|
2687
|
+
}
|
|
2688
|
+
return blocks;
|
|
2689
|
+
}
|
|
2430
2690
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
2691
|
+
var ANTHROPIC_FILES_BETA = "files-api-2025-04-14";
|
|
2692
|
+
var ANTHROPIC_IMAGE_MODELS = /* @__PURE__ */ new Set(["claude-sonnet-4-5", "claude-opus-4-8"]);
|
|
2693
|
+
function hasAnthropicProviderFileImage(messages) {
|
|
2694
|
+
return messages.some(
|
|
2695
|
+
(message) => Array.isArray(message.content) && message.content.some(
|
|
2696
|
+
(part) => part.type === "image" && part.source.type === "provider-file" && part.source.provider === "anthropic"
|
|
2697
|
+
)
|
|
2698
|
+
);
|
|
2699
|
+
}
|
|
2431
2700
|
var CLAUDE_CAPABILITIES = {
|
|
2432
2701
|
"claude-fable-5": {
|
|
2433
2702
|
thinking: "adaptive-always-on",
|
|
@@ -2688,6 +2957,11 @@ function isEffortAtOrBelow(effort, ceiling) {
|
|
|
2688
2957
|
function pricingContextFromBody(body) {
|
|
2689
2958
|
return {
|
|
2690
2959
|
model: typeof body.model === "string" ? body.model : void 0,
|
|
2960
|
+
hasRichInput: Array.isArray(body.messages) && body.messages.some(
|
|
2961
|
+
(message) => message !== null && typeof message === "object" && Array.isArray(message.content) && message.content.some(
|
|
2962
|
+
(part) => part !== null && typeof part === "object" && part.type === "image"
|
|
2963
|
+
)
|
|
2964
|
+
),
|
|
2691
2965
|
unpricedModifier: isAnthropicModifier("inference_geo", body.inference_geo) || isAnthropicModifier("speed", body.speed) || hasAnthropicServerTool(body.tools) || body.fallbacks !== void 0
|
|
2692
2966
|
};
|
|
2693
2967
|
}
|
|
@@ -2782,6 +3056,44 @@ function mergeAnthropicUsage(initial, update) {
|
|
|
2782
3056
|
}
|
|
2783
3057
|
var AnthropicProvider = class {
|
|
2784
3058
|
name = "anthropic";
|
|
3059
|
+
inputCapabilities(model) {
|
|
3060
|
+
return ANTHROPIC_IMAGE_MODELS.has(model) ? { image: { sources: ["url", "bytes", "base64", "provider-file"] } } : {};
|
|
3061
|
+
}
|
|
3062
|
+
validateInput(request) {
|
|
3063
|
+
const effectiveModel = typeof request.providerOptions?.model === "string" ? request.providerOptions.model : request.model;
|
|
3064
|
+
const fail = (source, feature) => {
|
|
3065
|
+
throw new UnsupportedModelInputError({
|
|
3066
|
+
provider: this.name,
|
|
3067
|
+
model: effectiveModel || request.model,
|
|
3068
|
+
modality: "image",
|
|
3069
|
+
...source ? { source } : {},
|
|
3070
|
+
...feature ? { feature } : {}
|
|
3071
|
+
});
|
|
3072
|
+
};
|
|
3073
|
+
if (!ANTHROPIC_IMAGE_MODELS.has(effectiveModel)) {
|
|
3074
|
+
fail(void 0, "image input for this model");
|
|
3075
|
+
}
|
|
3076
|
+
if (request.providerOptions && "messages" in request.providerOptions) {
|
|
3077
|
+
fail(void 0, "raw messages providerOptions");
|
|
3078
|
+
}
|
|
3079
|
+
for (const message of request.history) {
|
|
3080
|
+
if (!Array.isArray(message.content)) continue;
|
|
3081
|
+
if (message.role !== "user") fail(void 0, "rich non-user history");
|
|
3082
|
+
for (const part of message.content) {
|
|
3083
|
+
if (part.type === "image" && part.source.type === "provider-file" && part.source.provider !== this.name) {
|
|
3084
|
+
fail("provider-file");
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
if (Array.isArray(request.input)) {
|
|
3089
|
+
for (const part of request.input) {
|
|
3090
|
+
if (part.type === "image" && part.source.type === "provider-file" && part.source.provider !== this.name) {
|
|
3091
|
+
fail("provider-file");
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
return { effectiveModel };
|
|
3096
|
+
}
|
|
2785
3097
|
/** Anthropic ignores native `json_schema` structurally — Axl uses a system
|
|
2786
3098
|
* prompt JSON instruction + client-side Zod validation (see buildRequest). */
|
|
2787
3099
|
nativeStructuredOutputSupport() {
|
|
@@ -2819,7 +3131,10 @@ var AnthropicProvider = class {
|
|
|
2819
3131
|
// chat - non-streaming completion
|
|
2820
3132
|
// ---------------------------------------------------------------------------
|
|
2821
3133
|
async chat(messages, options) {
|
|
2822
|
-
const headers = this.buildHeaders(
|
|
3134
|
+
const headers = this.buildHeaders(
|
|
3135
|
+
await this.resolveKey(),
|
|
3136
|
+
hasAnthropicProviderFileImage(messages)
|
|
3137
|
+
);
|
|
2823
3138
|
const body = this.buildRequestBody(messages, options, false);
|
|
2824
3139
|
const pricingContext = pricingContextFromBody(body);
|
|
2825
3140
|
const res = await fetchWithRetry(
|
|
@@ -2850,7 +3165,10 @@ var AnthropicProvider = class {
|
|
|
2850
3165
|
// stream - SSE streaming completion
|
|
2851
3166
|
// ---------------------------------------------------------------------------
|
|
2852
3167
|
async *stream(messages, options) {
|
|
2853
|
-
const headers = this.buildHeaders(
|
|
3168
|
+
const headers = this.buildHeaders(
|
|
3169
|
+
await this.resolveKey(),
|
|
3170
|
+
hasAnthropicProviderFileImage(messages)
|
|
3171
|
+
);
|
|
2854
3172
|
const body = this.buildRequestBody(messages, options, true);
|
|
2855
3173
|
const pricingContext = pricingContextFromBody(body);
|
|
2856
3174
|
const res = await fetchWithRetry(
|
|
@@ -2882,11 +3200,12 @@ var AnthropicProvider = class {
|
|
|
2882
3200
|
// ---------------------------------------------------------------------------
|
|
2883
3201
|
// Internal: request building
|
|
2884
3202
|
// ---------------------------------------------------------------------------
|
|
2885
|
-
buildHeaders(apiKey) {
|
|
3203
|
+
buildHeaders(apiKey, requiresFilesBeta = false) {
|
|
2886
3204
|
return {
|
|
2887
3205
|
"Content-Type": "application/json",
|
|
2888
3206
|
"x-api-key": apiKey,
|
|
2889
|
-
"anthropic-version": ANTHROPIC_API_VERSION
|
|
3207
|
+
"anthropic-version": ANTHROPIC_API_VERSION,
|
|
3208
|
+
...requiresFilesBeta ? { "anthropic-beta": ANTHROPIC_FILES_BETA } : {}
|
|
2890
3209
|
};
|
|
2891
3210
|
}
|
|
2892
3211
|
/** Extract a human-readable message from an API error response body. */
|
|
@@ -2956,12 +3275,13 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2956
3275
|
mapMessages(messages) {
|
|
2957
3276
|
const result = [];
|
|
2958
3277
|
for (const msg of messages) {
|
|
3278
|
+
const text = typeof msg.content === "string" ? msg.content : "";
|
|
2959
3279
|
if (msg.role === "assistant") {
|
|
2960
3280
|
const replayedThinking = this.getReplayedThinkingBlocks(msg);
|
|
2961
3281
|
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
2962
3282
|
const content = [...replayedThinking];
|
|
2963
|
-
if (
|
|
2964
|
-
content.push({ type: "text", text
|
|
3283
|
+
if (text) {
|
|
3284
|
+
content.push({ type: "text", text });
|
|
2965
3285
|
}
|
|
2966
3286
|
for (const tc of msg.tool_calls) {
|
|
2967
3287
|
let parsedArgs;
|
|
@@ -2980,10 +3300,10 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2980
3300
|
result.push({ role: "assistant", content });
|
|
2981
3301
|
} else if (replayedThinking.length > 0) {
|
|
2982
3302
|
const content = [...replayedThinking];
|
|
2983
|
-
if (
|
|
3303
|
+
if (text) content.push({ type: "text", text });
|
|
2984
3304
|
result.push({ role: "assistant", content });
|
|
2985
3305
|
} else {
|
|
2986
|
-
result.push({ role: "assistant", content:
|
|
3306
|
+
result.push({ role: "assistant", content: text });
|
|
2987
3307
|
}
|
|
2988
3308
|
} else if (msg.role === "tool") {
|
|
2989
3309
|
result.push({
|
|
@@ -2992,12 +3312,15 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2992
3312
|
{
|
|
2993
3313
|
type: "tool_result",
|
|
2994
3314
|
tool_use_id: msg.tool_call_id,
|
|
2995
|
-
content:
|
|
3315
|
+
content: text
|
|
2996
3316
|
}
|
|
2997
3317
|
]
|
|
2998
3318
|
});
|
|
2999
3319
|
} else if (msg.role === "user") {
|
|
3000
|
-
result.push({
|
|
3320
|
+
result.push({
|
|
3321
|
+
role: "user",
|
|
3322
|
+
content: typeof msg.content === "string" ? msg.content : anthropicImageBlocks(msg.content)
|
|
3323
|
+
});
|
|
3001
3324
|
}
|
|
3002
3325
|
}
|
|
3003
3326
|
return this.mergeConsecutiveRoles(result);
|
|
@@ -3102,7 +3425,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
3102
3425
|
"Anthropic fallback responses cannot be continued safely; retry without fallbacks."
|
|
3103
3426
|
);
|
|
3104
3427
|
}
|
|
3105
|
-
const cost = normalized && !pricingContext.unpricedModifier && !isModifiedAnthropicResponse(json, effectiveModel) && !hasFallbackBoundary && !refused ? estimateAnthropicCost(effectiveModel, normalized.pricingUsage) : void 0;
|
|
3428
|
+
const cost = normalized && !pricingContext.hasRichInput && !pricingContext.unpricedModifier && !isModifiedAnthropicResponse(json, effectiveModel) && !hasFallbackBoundary && !refused ? estimateAnthropicCost(effectiveModel, normalized.pricingUsage) : void 0;
|
|
3106
3429
|
return {
|
|
3107
3430
|
content,
|
|
3108
3431
|
thinking_content: thinkingContent || void 0,
|
|
@@ -3139,7 +3462,7 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
3139
3462
|
const doneChunk = () => ({
|
|
3140
3463
|
type: "done",
|
|
3141
3464
|
usage,
|
|
3142
|
-
cost: pricingUsage && !unpricedModifier && !hasFallbackBoundary && !refused ? estimateAnthropicCost(effectiveModel, pricingUsage) : void 0,
|
|
3465
|
+
cost: pricingUsage && !pricingContext.hasRichInput && !unpricedModifier && !hasFallbackBoundary && !refused ? estimateAnthropicCost(effectiveModel, pricingUsage) : void 0,
|
|
3143
3466
|
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary && !hasFallbackIterationSignal ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
|
|
3144
3467
|
});
|
|
3145
3468
|
try {
|
|
@@ -3287,6 +3610,60 @@ function lastFallbackIndex(blocks) {
|
|
|
3287
3610
|
}
|
|
3288
3611
|
|
|
3289
3612
|
// src/providers/gemini.ts
|
|
3613
|
+
function hasRichGeminiMessages(messages) {
|
|
3614
|
+
return messages.some((message) => Array.isArray(message.content));
|
|
3615
|
+
}
|
|
3616
|
+
function geminiImageBase64(source) {
|
|
3617
|
+
return source.type === "base64" ? source.data : Buffer.from(source.data.buffer, source.data.byteOffset, source.data.byteLength).toString(
|
|
3618
|
+
"base64"
|
|
3619
|
+
);
|
|
3620
|
+
}
|
|
3621
|
+
function geminiInteractionContent(parts, model) {
|
|
3622
|
+
const content = [];
|
|
3623
|
+
for (const part of parts) {
|
|
3624
|
+
if (part.type === "text") {
|
|
3625
|
+
content.push({ type: "text", text: part.text });
|
|
3626
|
+
continue;
|
|
3627
|
+
}
|
|
3628
|
+
const { source } = part;
|
|
3629
|
+
if (source.type === "provider-file") {
|
|
3630
|
+
if (source.provider !== "google") {
|
|
3631
|
+
throw new UnsupportedModelInputError({
|
|
3632
|
+
provider: "google",
|
|
3633
|
+
model,
|
|
3634
|
+
modality: "image",
|
|
3635
|
+
source: "provider-file"
|
|
3636
|
+
});
|
|
3637
|
+
}
|
|
3638
|
+
if (!source.mediaType) {
|
|
3639
|
+
throw new UnsupportedModelInputError({
|
|
3640
|
+
provider: "google",
|
|
3641
|
+
model,
|
|
3642
|
+
modality: "image",
|
|
3643
|
+
source: "provider-file",
|
|
3644
|
+
feature: "Interactions URI image mediaType"
|
|
3645
|
+
});
|
|
3646
|
+
}
|
|
3647
|
+
content.push({
|
|
3648
|
+
type: "image",
|
|
3649
|
+
uri: source.reference,
|
|
3650
|
+
mime_type: source.mediaType
|
|
3651
|
+
});
|
|
3652
|
+
} else if (source.type === "url") {
|
|
3653
|
+
throw new UnsupportedModelInputError({
|
|
3654
|
+
provider: "google",
|
|
3655
|
+
model,
|
|
3656
|
+
modality: "image",
|
|
3657
|
+
source: "url",
|
|
3658
|
+
feature: "direct URL image input; pass bytes/base64 or a Gemini provider-file"
|
|
3659
|
+
});
|
|
3660
|
+
} else {
|
|
3661
|
+
content.push({ type: "image", data: geminiImageBase64(source), mime_type: source.mediaType });
|
|
3662
|
+
}
|
|
3663
|
+
if (part.label) content.push({ type: "text", text: `[Image: ${part.label}]` });
|
|
3664
|
+
}
|
|
3665
|
+
return content;
|
|
3666
|
+
}
|
|
3290
3667
|
var GEMINI_DISALLOWED_SCHEMA_KEYS = /* @__PURE__ */ new Set([
|
|
3291
3668
|
"additionalProperties",
|
|
3292
3669
|
"$schema",
|
|
@@ -3358,6 +3735,7 @@ var GEMINI_PRICING = {
|
|
|
3358
3735
|
"gemini-3.5-flash-lite": { input: 3e-7, cached: 3e-8, output: 25e-7 },
|
|
3359
3736
|
"gemini-3.6-flash": { input: 15e-7, cached: 15e-8, output: 75e-7 }
|
|
3360
3737
|
};
|
|
3738
|
+
var GEMINI_INTERACTIONS_IMAGE_MODELS = /* @__PURE__ */ new Set(["gemini-3.7-flash"]);
|
|
3361
3739
|
function isValidTokenCount2(value) {
|
|
3362
3740
|
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3363
3741
|
}
|
|
@@ -3445,7 +3823,10 @@ var GEMINI_3X_MIN_THINKING_LEVEL = /* @__PURE__ */ new Map([
|
|
|
3445
3823
|
["gemini-3.1-flash-lite-preview", "minimal"],
|
|
3446
3824
|
["gemini-3.5-flash", "minimal"],
|
|
3447
3825
|
["gemini-3.5-flash-lite", "minimal"],
|
|
3448
|
-
["gemini-3.6-flash", "minimal"]
|
|
3826
|
+
["gemini-3.6-flash", "minimal"],
|
|
3827
|
+
// L4 live evidence: Interactions gemini-3.7-flash accepts low/medium/high,
|
|
3828
|
+
// but rejects minimal. Keep this exact entry isolated from legacy aliases.
|
|
3829
|
+
["gemini-3.7-flash", "low"]
|
|
3449
3830
|
]);
|
|
3450
3831
|
var GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE = /* @__PURE__ */ new Set([
|
|
3451
3832
|
"gemini-3.5-flash-lite",
|
|
@@ -3472,6 +3853,61 @@ function warnGemini3xEffortNone(model) {
|
|
|
3472
3853
|
}
|
|
3473
3854
|
var GeminiProvider = class {
|
|
3474
3855
|
name = "google";
|
|
3856
|
+
inputCapabilities(model) {
|
|
3857
|
+
return GEMINI_INTERACTIONS_IMAGE_MODELS.has(model) ? { image: { sources: ["bytes", "base64", "provider-file"] } } : {};
|
|
3858
|
+
}
|
|
3859
|
+
validateInput(request) {
|
|
3860
|
+
const effectiveModel = typeof request.providerOptions?.model === "string" ? request.providerOptions.model : request.model;
|
|
3861
|
+
const fail = (source, feature) => {
|
|
3862
|
+
throw new UnsupportedModelInputError({
|
|
3863
|
+
provider: this.name,
|
|
3864
|
+
model: effectiveModel || request.model,
|
|
3865
|
+
modality: "image",
|
|
3866
|
+
...source ? { source } : {},
|
|
3867
|
+
...feature ? { feature } : {}
|
|
3868
|
+
});
|
|
3869
|
+
};
|
|
3870
|
+
if (!GEMINI_INTERACTIONS_IMAGE_MODELS.has(effectiveModel)) {
|
|
3871
|
+
fail(void 0, "image input for this model");
|
|
3872
|
+
}
|
|
3873
|
+
if (request.providerOptions) {
|
|
3874
|
+
const forbidden = [
|
|
3875
|
+
"input",
|
|
3876
|
+
"contents",
|
|
3877
|
+
"messages",
|
|
3878
|
+
"previous_interaction_id",
|
|
3879
|
+
"background",
|
|
3880
|
+
"stream",
|
|
3881
|
+
"store"
|
|
3882
|
+
].find((key) => key in request.providerOptions);
|
|
3883
|
+
if (forbidden) fail(void 0, `raw ${forbidden} providerOptions`);
|
|
3884
|
+
}
|
|
3885
|
+
for (const message of request.history) {
|
|
3886
|
+
if (!Array.isArray(message.content)) continue;
|
|
3887
|
+
if (message.role !== "user") fail(void 0, "rich non-user history");
|
|
3888
|
+
for (const part of message.content) {
|
|
3889
|
+
if (part.type !== "image") continue;
|
|
3890
|
+
if (part.source.type === "url")
|
|
3891
|
+
fail("url", "direct URL image input; pass bytes/base64 or a Gemini provider-file");
|
|
3892
|
+
if (part.source.type === "provider-file" && part.source.provider !== this.name)
|
|
3893
|
+
fail("provider-file");
|
|
3894
|
+
if (part.source.type === "provider-file" && !part.source.mediaType)
|
|
3895
|
+
fail("provider-file", "Interactions URI image mediaType");
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
if (Array.isArray(request.input)) {
|
|
3899
|
+
for (const part of request.input) {
|
|
3900
|
+
if (part.type !== "image") continue;
|
|
3901
|
+
if (part.source.type === "url")
|
|
3902
|
+
fail("url", "direct URL image input; pass bytes/base64 or a Gemini provider-file");
|
|
3903
|
+
if (part.source.type === "provider-file" && part.source.provider !== this.name)
|
|
3904
|
+
fail("provider-file");
|
|
3905
|
+
if (part.source.type === "provider-file" && !part.source.mediaType)
|
|
3906
|
+
fail("provider-file", "Interactions URI image mediaType");
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
return { effectiveModel };
|
|
3910
|
+
}
|
|
3475
3911
|
/** Gemini accepts a `responseSchema` but `sanitizeSchemaForGemini` strips
|
|
3476
3912
|
* keywords it doesn't support (`$ref`/`$defs`/`additionalProperties`/…), so a
|
|
3477
3913
|
* derived schema can lose constraints — lossy, not faithful. */
|
|
@@ -3510,6 +3946,7 @@ var GeminiProvider = class {
|
|
|
3510
3946
|
// chat - non-streaming completion
|
|
3511
3947
|
// ---------------------------------------------------------------------------
|
|
3512
3948
|
async chat(messages, options) {
|
|
3949
|
+
if (hasRichGeminiMessages(messages)) return this.chatInteraction(messages, options);
|
|
3513
3950
|
const body = this.buildRequestBody(messages, options);
|
|
3514
3951
|
const pricingContext = this.pricingContext(this.requestModel(options), body);
|
|
3515
3952
|
this.assertSafeGemini3Continuation(messages, pricingContext.model);
|
|
@@ -3542,6 +3979,10 @@ var GeminiProvider = class {
|
|
|
3542
3979
|
// stream - SSE streaming completion
|
|
3543
3980
|
// ---------------------------------------------------------------------------
|
|
3544
3981
|
async *stream(messages, options) {
|
|
3982
|
+
if (hasRichGeminiMessages(messages)) {
|
|
3983
|
+
yield* this.streamInteraction(messages, options);
|
|
3984
|
+
return;
|
|
3985
|
+
}
|
|
3545
3986
|
const body = this.buildRequestBody(messages, options);
|
|
3546
3987
|
const pricingContext = this.pricingContext(this.requestModel(options), body);
|
|
3547
3988
|
this.assertSafeGemini3Continuation(messages, pricingContext.model);
|
|
@@ -3573,80 +4014,413 @@ var GeminiProvider = class {
|
|
|
3573
4014
|
yield* this.parseSSEStream(res.body, pricingContext);
|
|
3574
4015
|
}
|
|
3575
4016
|
// ---------------------------------------------------------------------------
|
|
3576
|
-
//
|
|
4017
|
+
// Rich image requests use Gemini Interactions. It is deliberately stateless:
|
|
4018
|
+
// Axl sends the complete application-owned history and never asks Google to
|
|
4019
|
+
// retain an interaction or follow a previous_interaction_id.
|
|
3577
4020
|
// ---------------------------------------------------------------------------
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
4021
|
+
async chatInteraction(messages, options) {
|
|
4022
|
+
this.assertSafeInteractionOptions(options);
|
|
4023
|
+
const body = this.buildInteractionRequestBody(messages, options, false);
|
|
4024
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
4025
|
+
const res = await fetchWithRetry(
|
|
4026
|
+
`${this.baseUrl}/interactions`,
|
|
4027
|
+
{ method: "POST", headers, body: JSON.stringify(body), signal: options.signal },
|
|
4028
|
+
{ governor: this.governor, provider: this.name }
|
|
4029
|
+
);
|
|
4030
|
+
if (!res.ok) {
|
|
4031
|
+
const errorBody = await res.text();
|
|
4032
|
+
throw buildProviderError({
|
|
4033
|
+
provider: this.name,
|
|
4034
|
+
status: res.status,
|
|
4035
|
+
headers: res.headers,
|
|
4036
|
+
message: this.extractErrorMessage(errorBody, res.status),
|
|
4037
|
+
body: errorBody
|
|
4038
|
+
});
|
|
4039
|
+
}
|
|
4040
|
+
return this.parseInteractionResponse(await res.json());
|
|
3583
4041
|
}
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
}
|
|
3590
|
-
|
|
4042
|
+
async *streamInteraction(messages, options) {
|
|
4043
|
+
this.assertSafeInteractionOptions(options);
|
|
4044
|
+
const body = this.buildInteractionRequestBody(messages, options, true);
|
|
4045
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
4046
|
+
const res = await fetchWithRetry(
|
|
4047
|
+
`${this.baseUrl}/interactions?alt=sse`,
|
|
4048
|
+
{ method: "POST", headers, body: JSON.stringify(body), signal: options.signal },
|
|
4049
|
+
{ governor: this.governor, provider: this.name }
|
|
4050
|
+
);
|
|
4051
|
+
if (!res.ok) {
|
|
4052
|
+
const errorBody = await res.text();
|
|
4053
|
+
throw buildProviderError({
|
|
4054
|
+
provider: this.name,
|
|
4055
|
+
status: res.status,
|
|
4056
|
+
headers: res.headers,
|
|
4057
|
+
message: this.extractErrorMessage(errorBody, res.status),
|
|
4058
|
+
body: errorBody
|
|
4059
|
+
});
|
|
3591
4060
|
}
|
|
3592
|
-
|
|
4061
|
+
if (!res.body) throw new Error("Gemini Interactions stream has no body");
|
|
4062
|
+
yield* this.parseInteractionSSEStream(res.body);
|
|
3593
4063
|
}
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
4064
|
+
buildInteractionRequestBody(messages, options, stream) {
|
|
4065
|
+
const model = this.requestModel(options);
|
|
4066
|
+
const systemInstruction = messages.filter((message) => message.role === "system").map((message) => typeof message.content === "string" ? message.content : "").filter(Boolean).join("\n\n");
|
|
4067
|
+
const body = {
|
|
4068
|
+
model,
|
|
4069
|
+
input: this.mapInteractionInput(
|
|
4070
|
+
messages.filter((message) => message.role !== "system"),
|
|
4071
|
+
model
|
|
4072
|
+
),
|
|
4073
|
+
stream
|
|
3602
4074
|
};
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
4075
|
+
if (systemInstruction) body.system_instruction = systemInstruction;
|
|
4076
|
+
if (options.tools?.length) {
|
|
4077
|
+
body.tools = options.tools.map((tool2) => ({
|
|
4078
|
+
type: "function",
|
|
4079
|
+
name: tool2.function.name,
|
|
4080
|
+
description: tool2.function.description,
|
|
4081
|
+
parameters: sanitizeSchemaForGemini(tool2.function.parameters)
|
|
4082
|
+
}));
|
|
4083
|
+
}
|
|
4084
|
+
const generationConfig = {};
|
|
4085
|
+
if (options.maxTokens !== void 0) generationConfig.max_output_tokens = options.maxTokens;
|
|
4086
|
+
if (options.stop) generationConfig.stop_sequences = options.stop;
|
|
4087
|
+
if (options.toolChoice !== void 0) {
|
|
4088
|
+
generationConfig.tool_choice = typeof options.toolChoice === "string" ? options.toolChoice === "required" ? "any" : options.toolChoice : { allowed_tools: { mode: "any", tools: [options.toolChoice.function.name] } };
|
|
4089
|
+
}
|
|
4090
|
+
const thinking = resolveThinkingOptions(options);
|
|
4091
|
+
if (thinking.thinkingDisabled) {
|
|
4092
|
+
generationConfig.thinking_level = isGemini3x(model) ? minThinkingLevel(model) : "minimal";
|
|
4093
|
+
} else if (thinking.hasBudgetOverride) {
|
|
4094
|
+
generationConfig.thinking_level = budgetToThinkingLevel(thinking.thinkingBudget);
|
|
4095
|
+
} else if (thinking.activeEffort) {
|
|
4096
|
+
generationConfig.thinking_level = isGemini3x(model) ? THINKING_LEVELS[thinking.activeEffort] ?? "medium" : budgetToThinkingLevel(THINKING_BUDGETS2[thinking.activeEffort] ?? 5e3);
|
|
4097
|
+
}
|
|
4098
|
+
if (thinking.includeThoughts) generationConfig.thinking_summaries = "auto";
|
|
4099
|
+
if (Object.keys(generationConfig).length) body.generation_config = generationConfig;
|
|
4100
|
+
if (options.responseFormat?.type && options.responseFormat.type !== "text") {
|
|
4101
|
+
body.response_format = {
|
|
4102
|
+
type: "text",
|
|
4103
|
+
mime_type: "application/json",
|
|
4104
|
+
...options.responseFormat.type === "json_schema" ? { schema: sanitizeSchemaForGemini(options.responseFormat.json_schema.schema) } : {}
|
|
4105
|
+
};
|
|
4106
|
+
}
|
|
4107
|
+
if (options.providerOptions) {
|
|
4108
|
+
const overrides = { ...options.providerOptions };
|
|
4109
|
+
delete overrides.model;
|
|
4110
|
+
delete overrides.input;
|
|
4111
|
+
delete overrides.contents;
|
|
4112
|
+
delete overrides.messages;
|
|
4113
|
+
delete overrides.previous_interaction_id;
|
|
4114
|
+
delete overrides.background;
|
|
4115
|
+
delete overrides.stream;
|
|
4116
|
+
delete overrides.store;
|
|
4117
|
+
const rawGenerationConfig = overrides.generation_config;
|
|
4118
|
+
if (rawGenerationConfig && typeof rawGenerationConfig === "object" && !Array.isArray(rawGenerationConfig)) {
|
|
4119
|
+
const generationConfigOverrides = { ...rawGenerationConfig };
|
|
4120
|
+
delete generationConfigOverrides.temperature;
|
|
4121
|
+
if (Object.keys(generationConfigOverrides).length)
|
|
4122
|
+
overrides.generation_config = generationConfigOverrides;
|
|
4123
|
+
else delete overrides.generation_config;
|
|
3614
4124
|
}
|
|
4125
|
+
Object.assign(body, overrides);
|
|
3615
4126
|
}
|
|
3616
|
-
|
|
3617
|
-
|
|
4127
|
+
body.stream = stream;
|
|
4128
|
+
body.store = false;
|
|
4129
|
+
return body;
|
|
4130
|
+
}
|
|
4131
|
+
assertSafeInteractionOptions(options) {
|
|
4132
|
+
const model = this.requestModel(options);
|
|
4133
|
+
const fail = (feature) => {
|
|
4134
|
+
throw new UnsupportedModelInputError({
|
|
4135
|
+
provider: this.name,
|
|
4136
|
+
model,
|
|
4137
|
+
modality: "image",
|
|
4138
|
+
feature
|
|
4139
|
+
});
|
|
4140
|
+
};
|
|
4141
|
+
if (!GEMINI_INTERACTIONS_IMAGE_MODELS.has(model)) fail("image input for this model");
|
|
4142
|
+
const forbidden = [
|
|
4143
|
+
"input",
|
|
4144
|
+
"contents",
|
|
4145
|
+
"messages",
|
|
4146
|
+
"previous_interaction_id",
|
|
4147
|
+
"background",
|
|
4148
|
+
"stream",
|
|
4149
|
+
"store"
|
|
4150
|
+
].find((key) => key in (options.providerOptions ?? {}));
|
|
4151
|
+
if (forbidden) fail(`raw ${forbidden} providerOptions`);
|
|
4152
|
+
}
|
|
4153
|
+
mapInteractionInput(messages, model) {
|
|
4154
|
+
const steps = [];
|
|
3618
4155
|
for (const message of messages) {
|
|
3619
|
-
if (message.role
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
4156
|
+
if (message.role === "user") {
|
|
4157
|
+
steps.push({
|
|
4158
|
+
type: "user_input",
|
|
4159
|
+
content: Array.isArray(message.content) ? geminiInteractionContent(message.content, model) : [{ type: "text", text: message.content }]
|
|
4160
|
+
});
|
|
4161
|
+
} else if (message.role === "assistant") {
|
|
4162
|
+
const nativeSteps = message.providerMetadata?.geminiInteractionSteps;
|
|
4163
|
+
if (Array.isArray(nativeSteps) && nativeSteps.every(isGeminiInteractionStep)) {
|
|
4164
|
+
steps.push(...nativeSteps);
|
|
4165
|
+
continue;
|
|
3629
4166
|
}
|
|
3630
|
-
|
|
4167
|
+
if (typeof message.content === "string" && message.content) {
|
|
4168
|
+
steps.push({ type: "model_output", content: [{ type: "text", text: message.content }] });
|
|
4169
|
+
}
|
|
4170
|
+
for (const call of message.tool_calls ?? []) {
|
|
4171
|
+
steps.push({
|
|
4172
|
+
type: "function_call",
|
|
4173
|
+
id: call.id,
|
|
4174
|
+
name: call.function.name,
|
|
4175
|
+
arguments: safeJsonObject(call.function.arguments)
|
|
4176
|
+
});
|
|
4177
|
+
}
|
|
4178
|
+
} else if (message.role === "tool") {
|
|
4179
|
+
steps.push({
|
|
4180
|
+
type: "function_result",
|
|
4181
|
+
call_id: message.tool_call_id ?? "",
|
|
4182
|
+
result: [
|
|
4183
|
+
{ type: "text", text: typeof message.content === "string" ? message.content : "" }
|
|
4184
|
+
]
|
|
4185
|
+
});
|
|
3631
4186
|
}
|
|
3632
4187
|
}
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
4188
|
+
return steps;
|
|
4189
|
+
}
|
|
4190
|
+
parseInteractionResponse(json) {
|
|
4191
|
+
let content = "";
|
|
4192
|
+
let thinkingContent = "";
|
|
4193
|
+
const toolCalls = [];
|
|
4194
|
+
for (const step of json.steps ?? []) {
|
|
4195
|
+
if (step.type === "model_output") content += interactionText(step.content);
|
|
4196
|
+
else if (step.type === "thought") thinkingContent += interactionText(step.summary);
|
|
4197
|
+
else if (step.type === "function_call" && step.id && step.name) {
|
|
4198
|
+
toolCalls.push({
|
|
4199
|
+
id: step.id,
|
|
4200
|
+
type: "function",
|
|
4201
|
+
function: { name: step.name, arguments: JSON.stringify(step.arguments ?? {}) }
|
|
4202
|
+
});
|
|
3641
4203
|
}
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
4204
|
+
}
|
|
4205
|
+
this.assertInteractionTerminalStatus(json.status, toolCalls.length);
|
|
4206
|
+
const usage = normalizeInteractionUsage(json.usage);
|
|
4207
|
+
return {
|
|
4208
|
+
content,
|
|
4209
|
+
thinking_content: thinkingContent || void 0,
|
|
4210
|
+
tool_calls: toolCalls.length ? toolCalls : void 0,
|
|
4211
|
+
usage,
|
|
4212
|
+
// Interactions returns modality usage but no authoritative monetary cost.
|
|
4213
|
+
cost: void 0,
|
|
4214
|
+
providerMetadata: json.steps?.length ? { geminiInteractionSteps: json.steps.filter(isGeminiInteractionStep) } : void 0
|
|
4215
|
+
};
|
|
4216
|
+
}
|
|
4217
|
+
async *parseInteractionSSEStream(body) {
|
|
4218
|
+
const reader = body.getReader();
|
|
4219
|
+
const decoder = new TextDecoder();
|
|
4220
|
+
let buffer = "";
|
|
4221
|
+
const steps = /* @__PURE__ */ new Map();
|
|
4222
|
+
const argumentBuffers = /* @__PURE__ */ new Map();
|
|
4223
|
+
const stopped = /* @__PURE__ */ new Set();
|
|
4224
|
+
let completed = false;
|
|
4225
|
+
try {
|
|
4226
|
+
while (true) {
|
|
4227
|
+
const { done, value } = await reader.read();
|
|
4228
|
+
if (done) break;
|
|
4229
|
+
buffer += decoder.decode(value, { stream: true });
|
|
4230
|
+
const lines = buffer.split("\n");
|
|
4231
|
+
buffer = lines.pop() ?? "";
|
|
4232
|
+
for (const line of lines) {
|
|
4233
|
+
const trimmed = line.trim();
|
|
4234
|
+
if (!trimmed.startsWith("data: ")) continue;
|
|
4235
|
+
if (trimmed.slice(6) === "[DONE]") continue;
|
|
4236
|
+
let event;
|
|
4237
|
+
try {
|
|
4238
|
+
event = JSON.parse(trimmed.slice(6));
|
|
4239
|
+
} catch {
|
|
4240
|
+
continue;
|
|
4241
|
+
}
|
|
4242
|
+
if (event.event_type === "error") {
|
|
4243
|
+
throw new Error(event.error?.message ?? "Gemini Interactions stream failed");
|
|
4244
|
+
}
|
|
4245
|
+
if (event.event_type === "step.start" && event.step) {
|
|
4246
|
+
const index = event.index;
|
|
4247
|
+
if (index === void 0 || steps.has(index)) {
|
|
4248
|
+
throw new Error("Gemini Interactions stream contained an invalid step.start index");
|
|
4249
|
+
}
|
|
4250
|
+
const step = cloneInteractionStep(event.step);
|
|
4251
|
+
steps.set(index, step);
|
|
4252
|
+
if (step.type === "model_output") {
|
|
4253
|
+
for (const text of interactionTextChunks(step.content)) {
|
|
4254
|
+
yield { type: "text_delta", content: text };
|
|
4255
|
+
}
|
|
4256
|
+
} else if (step.type === "thought") {
|
|
4257
|
+
for (const text of interactionTextChunks(step.summary)) {
|
|
4258
|
+
yield { type: "thinking_delta", content: text };
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
if (step.type === "function_call" && step.id && step.name) {
|
|
4262
|
+
argumentBuffers.set(index, "");
|
|
4263
|
+
yield { type: "tool_call_delta", id: step.id, name: step.name };
|
|
4264
|
+
}
|
|
4265
|
+
} else if (event.event_type === "step.delta" && event.delta) {
|
|
4266
|
+
const delta = event.delta;
|
|
4267
|
+
const index = event.index;
|
|
4268
|
+
if (index === void 0 || stopped.has(index)) {
|
|
4269
|
+
throw new Error("Gemini Interactions stream delta arrived outside an active step");
|
|
4270
|
+
}
|
|
4271
|
+
const step = steps.get(index);
|
|
4272
|
+
if (!step)
|
|
4273
|
+
throw new Error("Gemini Interactions stream delta arrived before step.start");
|
|
4274
|
+
if (delta.type === "text" && typeof delta.text === "string" && step.type === "model_output") {
|
|
4275
|
+
appendInteractionContent(step, "content", { type: "text", text: delta.text });
|
|
4276
|
+
yield { type: "text_delta", content: delta.text };
|
|
4277
|
+
} else if (delta.type === "thought_summary" && step.type === "thought" && delta.content) {
|
|
4278
|
+
appendInteractionContent(step, "summary", delta.content);
|
|
4279
|
+
for (const text of interactionTextChunks([delta.content])) {
|
|
4280
|
+
yield { type: "thinking_delta", content: text };
|
|
4281
|
+
}
|
|
4282
|
+
} else if (delta.type === "thought_signature" && step.type === "thought" && typeof delta.signature === "string") {
|
|
4283
|
+
step.signature = delta.signature;
|
|
4284
|
+
} else if (delta.type === "arguments_delta" && step.type === "function_call" && typeof delta.arguments === "string") {
|
|
4285
|
+
argumentBuffers.set(index, (argumentBuffers.get(index) ?? "") + delta.arguments);
|
|
4286
|
+
if (step.id)
|
|
4287
|
+
yield { type: "tool_call_delta", id: step.id, arguments: delta.arguments };
|
|
4288
|
+
}
|
|
4289
|
+
} else if (event.event_type === "step.stop") {
|
|
4290
|
+
const index = event.index;
|
|
4291
|
+
const step = index === void 0 ? void 0 : steps.get(index);
|
|
4292
|
+
if (step === void 0 || stopped.has(index)) {
|
|
4293
|
+
throw new Error("Gemini Interactions stream contained an invalid step.stop index");
|
|
4294
|
+
}
|
|
4295
|
+
if (step.type === "function_call") {
|
|
4296
|
+
const rawArguments = argumentBuffers.get(index) ?? "";
|
|
4297
|
+
if (rawArguments) {
|
|
4298
|
+
try {
|
|
4299
|
+
const parsed = JSON.parse(rawArguments);
|
|
4300
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4301
|
+
throw new Error("not an object");
|
|
4302
|
+
}
|
|
4303
|
+
step.arguments = parsed;
|
|
4304
|
+
} catch {
|
|
4305
|
+
throw new Error(
|
|
4306
|
+
"Gemini Interactions function_call arguments were not valid JSON"
|
|
4307
|
+
);
|
|
4308
|
+
}
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
stopped.add(index);
|
|
4312
|
+
} else if (event.event_type === "interaction.completed") {
|
|
4313
|
+
if (steps.size !== stopped.size) {
|
|
4314
|
+
throw new Error("Gemini Interactions stream completed before all steps stopped");
|
|
4315
|
+
}
|
|
4316
|
+
const toolCallCount = [...steps.values()].filter(
|
|
4317
|
+
(step) => step.type === "function_call" && !!step.id && !!step.name
|
|
4318
|
+
).length;
|
|
4319
|
+
this.assertInteractionTerminalStatus(event.interaction?.status, toolCallCount);
|
|
4320
|
+
completed = true;
|
|
4321
|
+
yield {
|
|
4322
|
+
type: "done",
|
|
4323
|
+
usage: normalizeInteractionUsage(event.interaction?.usage),
|
|
4324
|
+
providerMetadata: steps.size ? {
|
|
4325
|
+
geminiInteractionSteps: [...steps.entries()].sort(([a], [b]) => a - b).map(([, step]) => step)
|
|
4326
|
+
} : void 0
|
|
4327
|
+
};
|
|
4328
|
+
return;
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
if (!completed)
|
|
4333
|
+
throw new Error("Gemini Interactions stream ended before interaction.completed");
|
|
4334
|
+
} finally {
|
|
4335
|
+
reader.releaseLock();
|
|
4336
|
+
}
|
|
4337
|
+
}
|
|
4338
|
+
assertInteractionTerminalStatus(status, toolCallCount) {
|
|
4339
|
+
if (status === "completed") return;
|
|
4340
|
+
if (status === "requires_action" && toolCallCount > 0) return;
|
|
4341
|
+
const normalizedStatus = typeof status === "string" ? status : "unknown";
|
|
4342
|
+
throw new ProviderError({
|
|
4343
|
+
provider: this.name,
|
|
4344
|
+
status: 0,
|
|
4345
|
+
retryable: false,
|
|
4346
|
+
message: `Gemini Interactions ended with non-success status: ${normalizedStatus}`
|
|
4347
|
+
});
|
|
4348
|
+
}
|
|
4349
|
+
// ---------------------------------------------------------------------------
|
|
4350
|
+
// Internal: request building
|
|
4351
|
+
// ---------------------------------------------------------------------------
|
|
4352
|
+
buildHeaders(apiKey) {
|
|
4353
|
+
return {
|
|
4354
|
+
"Content-Type": "application/json",
|
|
4355
|
+
"x-goog-api-key": apiKey
|
|
4356
|
+
};
|
|
4357
|
+
}
|
|
4358
|
+
extractErrorMessage(body, status) {
|
|
4359
|
+
try {
|
|
4360
|
+
const json = JSON.parse(body);
|
|
4361
|
+
if (json.error?.message) {
|
|
4362
|
+
return `Gemini API error (${status}): ${json.error.message}`;
|
|
4363
|
+
}
|
|
4364
|
+
} catch {
|
|
4365
|
+
}
|
|
4366
|
+
return `Gemini API error (${status}): ${body}`;
|
|
4367
|
+
}
|
|
4368
|
+
pricingContext(fallbackModel, body) {
|
|
4369
|
+
return {
|
|
4370
|
+
// Gemini's model is normally selected in the URL. providerOptions is an
|
|
4371
|
+
// explicit raw escape hatch, so honor a post-merge model override for both
|
|
4372
|
+
// the URL and pricing rather than pricing a different request.
|
|
4373
|
+
model: typeof body.model === "string" ? body.model : fallbackModel,
|
|
4374
|
+
serviceTier: body.serviceTier,
|
|
4375
|
+
eligibleRequest: this.baseUrl === "https://generativelanguage.googleapis.com/v1beta" && this.isTextOnlyClientRequest(body)
|
|
4376
|
+
};
|
|
4377
|
+
}
|
|
4378
|
+
requestModel(options) {
|
|
4379
|
+
return typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
4380
|
+
}
|
|
4381
|
+
assertSafeGemini3Continuation(messages, model) {
|
|
4382
|
+
if (GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE.has(model)) {
|
|
4383
|
+
const mapped = this.mapMessages(messages.filter((message) => message.role !== "system"));
|
|
4384
|
+
if (mapped.at(-1)?.role === "model") {
|
|
4385
|
+
throw new Error(
|
|
4386
|
+
`${model} does not support a terminal assistant/model prefill; end the request with a user or tool message.`
|
|
4387
|
+
);
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
if (!isGemini3x(model)) return;
|
|
4391
|
+
const replayedNativeCallIds = /* @__PURE__ */ new Set();
|
|
4392
|
+
for (const message of messages) {
|
|
4393
|
+
if (message.role !== "assistant" || !Array.isArray(message.providerMetadata?.geminiParts)) {
|
|
4394
|
+
continue;
|
|
4395
|
+
}
|
|
4396
|
+
for (const part of message.providerMetadata.geminiParts) {
|
|
4397
|
+
const id = part !== null && typeof part === "object" ? part.functionCall?.id : void 0;
|
|
4398
|
+
if (!id) continue;
|
|
4399
|
+
if (replayedNativeCallIds.has(id)) {
|
|
4400
|
+
throw new Error(
|
|
4401
|
+
`${model} native functionCall ids must be globally unique across a replay.`
|
|
4402
|
+
);
|
|
4403
|
+
}
|
|
4404
|
+
replayedNativeCallIds.add(id);
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
const consumedToolMessages = /* @__PURE__ */ new Set();
|
|
4408
|
+
for (const [assistantIndex, message] of messages.entries()) {
|
|
4409
|
+
if (message.role !== "assistant" || !message.tool_calls?.length) continue;
|
|
4410
|
+
const rawParts = message.providerMetadata?.geminiParts;
|
|
4411
|
+
if (!Array.isArray(rawParts)) {
|
|
4412
|
+
throw new Error(
|
|
4413
|
+
`${model} tool continuations require providerMetadata.geminiParts with thought signatures.`
|
|
4414
|
+
);
|
|
4415
|
+
}
|
|
4416
|
+
const nativeCalls = rawParts.flatMap((part) => {
|
|
4417
|
+
const functionCall = part !== null && typeof part === "object" ? part.functionCall : void 0;
|
|
4418
|
+
return functionCall?.id ? [{ id: functionCall.id, name: functionCall.name, part }] : [];
|
|
4419
|
+
});
|
|
4420
|
+
const expectedCalls = new Map(
|
|
4421
|
+
message.tool_calls.map((call) => [call.id, call.function.name])
|
|
4422
|
+
);
|
|
4423
|
+
if (expectedCalls.size !== message.tool_calls.length || nativeCalls.length !== message.tool_calls.length || new Set(nativeCalls.map((call) => call.id)).size !== nativeCalls.length || typeof nativeCalls[0]?.part.thoughtSignature !== "string" || nativeCalls[0].part.thoughtSignature.length === 0 || nativeCalls.some((call) => expectedCalls.get(call.id) !== call.name)) {
|
|
3650
4424
|
throw new Error(
|
|
3651
4425
|
`${model} tool continuations require exact native functionCall id/name and a thoughtSignature on the first function call.`
|
|
3652
4426
|
);
|
|
@@ -3827,14 +4601,15 @@ var GeminiProvider = class {
|
|
|
3827
4601
|
}
|
|
3828
4602
|
const result = [];
|
|
3829
4603
|
for (const msg of messages) {
|
|
4604
|
+
const text = typeof msg.content === "string" ? msg.content : "";
|
|
3830
4605
|
if (msg.role === "assistant") {
|
|
3831
4606
|
const rawParts = msg.providerMetadata?.geminiParts;
|
|
3832
4607
|
if (rawParts && rawParts.length > 0) {
|
|
3833
4608
|
result.push({ role: "model", parts: rawParts });
|
|
3834
4609
|
} else {
|
|
3835
4610
|
const parts = [];
|
|
3836
|
-
if (
|
|
3837
|
-
parts.push({ text
|
|
4611
|
+
if (text) {
|
|
4612
|
+
parts.push({ text });
|
|
3838
4613
|
}
|
|
3839
4614
|
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
3840
4615
|
for (const tc of msg.tool_calls) {
|
|
@@ -3860,7 +4635,7 @@ var GeminiProvider = class {
|
|
|
3860
4635
|
const functionName = (msg.tool_call_id ? geminiNativeNames.get(msg.tool_call_id) : void 0) ?? toolCallIdToName.get(msg.tool_call_id) ?? "unknown";
|
|
3861
4636
|
const functionResponse = {
|
|
3862
4637
|
name: functionName,
|
|
3863
|
-
response: parseGeminiFunctionResponse(
|
|
4638
|
+
response: parseGeminiFunctionResponse(text)
|
|
3864
4639
|
};
|
|
3865
4640
|
if (msg.tool_call_id && geminiNativeIds.has(msg.tool_call_id)) {
|
|
3866
4641
|
functionResponse.id = msg.tool_call_id;
|
|
@@ -3870,7 +4645,7 @@ var GeminiProvider = class {
|
|
|
3870
4645
|
parts: [{ functionResponse }]
|
|
3871
4646
|
});
|
|
3872
4647
|
} else if (msg.role === "user") {
|
|
3873
|
-
result.push({ role: "user", parts: [{ text
|
|
4648
|
+
result.push({ role: "user", parts: [{ text }] });
|
|
3874
4649
|
}
|
|
3875
4650
|
}
|
|
3876
4651
|
return this.mergeConsecutiveRoles(result);
|
|
@@ -4058,6 +4833,56 @@ var GeminiProvider = class {
|
|
|
4058
4833
|
}
|
|
4059
4834
|
}
|
|
4060
4835
|
};
|
|
4836
|
+
function isGeminiInteractionStep(value) {
|
|
4837
|
+
return value !== null && typeof value === "object" && ["user_input", "model_output", "thought", "function_call", "function_result"].includes(
|
|
4838
|
+
value.type
|
|
4839
|
+
);
|
|
4840
|
+
}
|
|
4841
|
+
function interactionText(content) {
|
|
4842
|
+
return content?.map((part) => part.type === "text" && typeof part.text === "string" ? part.text : "").join("") ?? "";
|
|
4843
|
+
}
|
|
4844
|
+
function interactionTextChunks(content) {
|
|
4845
|
+
return content?.flatMap(
|
|
4846
|
+
(part) => part.type === "text" && typeof part.text === "string" ? [part.text] : []
|
|
4847
|
+
) ?? [];
|
|
4848
|
+
}
|
|
4849
|
+
function cloneInteractionStep(step) {
|
|
4850
|
+
return JSON.parse(JSON.stringify(step));
|
|
4851
|
+
}
|
|
4852
|
+
function appendInteractionContent(step, field, content) {
|
|
4853
|
+
const existing = step[field] ?? [];
|
|
4854
|
+
const last = existing.at(-1);
|
|
4855
|
+
if (content.type === "text" && last?.type === "text" && typeof content.text === "string" && typeof last.text === "string") {
|
|
4856
|
+
last.text += content.text;
|
|
4857
|
+
} else {
|
|
4858
|
+
existing.push(content);
|
|
4859
|
+
}
|
|
4860
|
+
step[field] = existing;
|
|
4861
|
+
}
|
|
4862
|
+
function safeJsonObject(value) {
|
|
4863
|
+
try {
|
|
4864
|
+
const parsed = JSON.parse(value);
|
|
4865
|
+
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
4866
|
+
} catch {
|
|
4867
|
+
return {};
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
function normalizeInteractionUsage(usage) {
|
|
4871
|
+
if (!usage || !isValidTokenCount2(usage.total_input_tokens) || !isValidTokenCount2(usage.total_output_tokens) || !isValidTokenCount2(usage.total_tokens)) {
|
|
4872
|
+
return void 0;
|
|
4873
|
+
}
|
|
4874
|
+
const cached = usage.total_cached_tokens;
|
|
4875
|
+
const thinking = usage.total_thought_tokens;
|
|
4876
|
+
if (cached !== void 0 && (!isValidTokenCount2(cached) || cached > usage.total_input_tokens) || thinking !== void 0 && !isValidTokenCount2(thinking))
|
|
4877
|
+
return void 0;
|
|
4878
|
+
return {
|
|
4879
|
+
prompt_tokens: usage.total_input_tokens,
|
|
4880
|
+
completion_tokens: usage.total_output_tokens,
|
|
4881
|
+
total_tokens: usage.total_tokens,
|
|
4882
|
+
...cached ? { cached_tokens: cached } : {},
|
|
4883
|
+
...thinking ? { reasoning_tokens: thinking } : {}
|
|
4884
|
+
};
|
|
4885
|
+
}
|
|
4061
4886
|
|
|
4062
4887
|
// src/providers/profiles/openrouter.ts
|
|
4063
4888
|
var OPENROUTER_PROFILE = {
|
|
@@ -4498,59 +5323,1216 @@ var ProviderRegistry = class {
|
|
|
4498
5323
|
}
|
|
4499
5324
|
throw new Error(`Unknown provider "${name}". Registered providers: ${this.list().join(", ")}`);
|
|
4500
5325
|
}
|
|
4501
|
-
/**
|
|
4502
|
-
* Resolve a "provider:model" URI string into a Provider instance and model name.
|
|
4503
|
-
*
|
|
4504
|
-
* Supported formats:
|
|
4505
|
-
* - "openai:gpt-4o" -> provider=openai, model=gpt-4o
|
|
4506
|
-
* - "anthropic:claude-3" -> provider=anthropic, model=claude-3
|
|
4507
|
-
* - "gpt-4o" -> uses defaultProvider from config, model=gpt-4o
|
|
4508
|
-
* - undefined / empty -> uses defaultProvider and defaultModel from config
|
|
4509
|
-
*
|
|
4510
|
-
* @param uri Provider:model string, or just a model name
|
|
4511
|
-
* @param config Axl configuration for provider options and defaults
|
|
4512
|
-
*/
|
|
4513
|
-
resolve(uri, config = {}) {
|
|
4514
|
-
if (!uri) {
|
|
4515
|
-
const providerName2 = config.defaultProvider ?? "openai";
|
|
4516
|
-
const model2 = config.defaultModel ?? "gpt-4o";
|
|
4517
|
-
return { provider: this.get(providerName2, config), model: model2 };
|
|
4518
|
-
}
|
|
4519
|
-
const colonIndex = uri.indexOf(":");
|
|
4520
|
-
if (colonIndex === -1) {
|
|
4521
|
-
const providerName2 = config.defaultProvider ?? "openai";
|
|
4522
|
-
return { provider: this.get(providerName2, config), model: uri };
|
|
4523
|
-
}
|
|
4524
|
-
const providerName = uri.slice(0, colonIndex);
|
|
4525
|
-
const model = uri.slice(colonIndex + 1);
|
|
4526
|
-
if (!providerName || !model) {
|
|
4527
|
-
throw new Error(
|
|
4528
|
-
`Invalid provider URI "${uri}". Expected format: "provider:model" (e.g. "openai:gpt-4o")`
|
|
4529
|
-
);
|
|
4530
|
-
}
|
|
4531
|
-
return { provider: this.get(providerName, config), model };
|
|
5326
|
+
/**
|
|
5327
|
+
* Resolve a "provider:model" URI string into a Provider instance and model name.
|
|
5328
|
+
*
|
|
5329
|
+
* Supported formats:
|
|
5330
|
+
* - "openai:gpt-4o" -> provider=openai, model=gpt-4o
|
|
5331
|
+
* - "anthropic:claude-3" -> provider=anthropic, model=claude-3
|
|
5332
|
+
* - "gpt-4o" -> uses defaultProvider from config, model=gpt-4o
|
|
5333
|
+
* - undefined / empty -> uses defaultProvider and defaultModel from config
|
|
5334
|
+
*
|
|
5335
|
+
* @param uri Provider:model string, or just a model name
|
|
5336
|
+
* @param config Axl configuration for provider options and defaults
|
|
5337
|
+
*/
|
|
5338
|
+
resolve(uri, config = {}) {
|
|
5339
|
+
if (!uri) {
|
|
5340
|
+
const providerName2 = config.defaultProvider ?? "openai";
|
|
5341
|
+
const model2 = config.defaultModel ?? "gpt-4o";
|
|
5342
|
+
return { provider: this.get(providerName2, config), model: model2 };
|
|
5343
|
+
}
|
|
5344
|
+
const colonIndex = uri.indexOf(":");
|
|
5345
|
+
if (colonIndex === -1) {
|
|
5346
|
+
const providerName2 = config.defaultProvider ?? "openai";
|
|
5347
|
+
return { provider: this.get(providerName2, config), model: uri };
|
|
5348
|
+
}
|
|
5349
|
+
const providerName = uri.slice(0, colonIndex);
|
|
5350
|
+
const model = uri.slice(colonIndex + 1);
|
|
5351
|
+
if (!providerName || !model) {
|
|
5352
|
+
throw new Error(
|
|
5353
|
+
`Invalid provider URI "${uri}". Expected format: "provider:model" (e.g. "openai:gpt-4o")`
|
|
5354
|
+
);
|
|
5355
|
+
}
|
|
5356
|
+
return { provider: this.get(providerName, config), model };
|
|
5357
|
+
}
|
|
5358
|
+
/**
|
|
5359
|
+
* Clear all cached provider instances. Useful for testing or reconfiguration.
|
|
5360
|
+
*/
|
|
5361
|
+
clearCache() {
|
|
5362
|
+
this.instances.clear();
|
|
5363
|
+
}
|
|
5364
|
+
/**
|
|
5365
|
+
* Clear all registered factories (including built-ins).
|
|
5366
|
+
* Useful for test runtimes where only explicitly registered instances should be used.
|
|
5367
|
+
*/
|
|
5368
|
+
clearFactories() {
|
|
5369
|
+
this.factories.clear();
|
|
5370
|
+
}
|
|
5371
|
+
};
|
|
5372
|
+
var defaultRegistry = new ProviderRegistry();
|
|
5373
|
+
|
|
5374
|
+
// src/transcription.ts
|
|
5375
|
+
var MAX_INLINE_TRANSCRIPTION_BYTES = 25 * 1024 * 1024;
|
|
5376
|
+
var RESERVED_PROVIDER_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
5377
|
+
"model",
|
|
5378
|
+
"audio",
|
|
5379
|
+
"signal",
|
|
5380
|
+
"cleanup",
|
|
5381
|
+
"cleanupStatus",
|
|
5382
|
+
"usage",
|
|
5383
|
+
"cost",
|
|
5384
|
+
"pricingStatus",
|
|
5385
|
+
"accounting"
|
|
5386
|
+
]);
|
|
5387
|
+
function nonEmptyString(value) {
|
|
5388
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
5389
|
+
}
|
|
5390
|
+
function cloneValue(value) {
|
|
5391
|
+
try {
|
|
5392
|
+
return structuredClone(value);
|
|
5393
|
+
} catch {
|
|
5394
|
+
throw new InvalidTranscriptionInputError("Transcription options must be structured-cloneable");
|
|
5395
|
+
}
|
|
5396
|
+
}
|
|
5397
|
+
function normalizeTranscriptionRequest(request) {
|
|
5398
|
+
if (!request || typeof request !== "object" || !nonEmptyString(request.model)) {
|
|
5399
|
+
throw new InvalidTranscriptionInputError("A transcription model URI is required");
|
|
5400
|
+
}
|
|
5401
|
+
const audio = request.audio;
|
|
5402
|
+
if (!audio || typeof audio !== "object") {
|
|
5403
|
+
throw new InvalidTranscriptionInputError("A transcription audio source is required");
|
|
5404
|
+
}
|
|
5405
|
+
let normalizedAudio;
|
|
5406
|
+
if (audio.type === "bytes") {
|
|
5407
|
+
if (!(audio.data instanceof Uint8Array) || audio.data.byteLength === 0 || !nonEmptyString(audio.mediaType)) {
|
|
5408
|
+
throw new InvalidTranscriptionInputError(
|
|
5409
|
+
"Inline transcription bytes require nonempty data and mediaType"
|
|
5410
|
+
);
|
|
5411
|
+
}
|
|
5412
|
+
if (audio.data.byteLength > MAX_INLINE_TRANSCRIPTION_BYTES) {
|
|
5413
|
+
throw new InvalidTranscriptionInputError(
|
|
5414
|
+
"Inline transcription audio must not exceed 25 MiB; chunk it or use a supported provider-file source"
|
|
5415
|
+
);
|
|
5416
|
+
}
|
|
5417
|
+
normalizedAudio = {
|
|
5418
|
+
type: "bytes",
|
|
5419
|
+
data: new Uint8Array(audio.data),
|
|
5420
|
+
mediaType: audio.mediaType
|
|
5421
|
+
};
|
|
5422
|
+
} else if (audio.type === "base64") {
|
|
5423
|
+
if (!nonEmptyString(audio.data) || !nonEmptyString(audio.mediaType)) {
|
|
5424
|
+
throw new InvalidTranscriptionInputError(
|
|
5425
|
+
"Inline base64 transcription audio requires data and mediaType"
|
|
5426
|
+
);
|
|
5427
|
+
}
|
|
5428
|
+
if (audio.data.length > 4 * Math.ceil(MAX_INLINE_TRANSCRIPTION_BYTES / 3)) {
|
|
5429
|
+
throw new InvalidTranscriptionInputError(
|
|
5430
|
+
"Inline transcription audio must not exceed 25 MiB; chunk it or use a supported provider-file source"
|
|
5431
|
+
);
|
|
5432
|
+
}
|
|
5433
|
+
let decoded;
|
|
5434
|
+
try {
|
|
5435
|
+
decoded = Uint8Array.from(Buffer.from(audio.data, "base64"));
|
|
5436
|
+
} catch {
|
|
5437
|
+
throw new InvalidTranscriptionInputError(
|
|
5438
|
+
"Transcription base64 must be canonical and decode to audio"
|
|
5439
|
+
);
|
|
5440
|
+
}
|
|
5441
|
+
if (!decoded.byteLength || Buffer.from(decoded).toString("base64") !== audio.data) {
|
|
5442
|
+
throw new InvalidTranscriptionInputError(
|
|
5443
|
+
"Transcription base64 must be canonical and decode to nonempty audio"
|
|
5444
|
+
);
|
|
5445
|
+
}
|
|
5446
|
+
if (decoded.byteLength > MAX_INLINE_TRANSCRIPTION_BYTES) {
|
|
5447
|
+
throw new InvalidTranscriptionInputError(
|
|
5448
|
+
"Inline transcription audio must not exceed 25 MiB; chunk it or use a supported provider-file source"
|
|
5449
|
+
);
|
|
5450
|
+
}
|
|
5451
|
+
normalizedAudio = { type: "base64", data: audio.data, mediaType: audio.mediaType };
|
|
5452
|
+
} else if (audio.type === "provider-file") {
|
|
5453
|
+
if (!nonEmptyString(audio.provider) || !nonEmptyString(audio.reference)) {
|
|
5454
|
+
throw new InvalidTranscriptionInputError(
|
|
5455
|
+
"A provider-file transcription source requires provider and reference"
|
|
5456
|
+
);
|
|
5457
|
+
}
|
|
5458
|
+
if (audio.mediaType !== void 0 && !nonEmptyString(audio.mediaType)) {
|
|
5459
|
+
throw new InvalidTranscriptionInputError(
|
|
5460
|
+
"Transcription provider-file mediaType must be nonempty when supplied"
|
|
5461
|
+
);
|
|
5462
|
+
}
|
|
5463
|
+
normalizedAudio = {
|
|
5464
|
+
type: "provider-file",
|
|
5465
|
+
provider: audio.provider,
|
|
5466
|
+
reference: audio.reference,
|
|
5467
|
+
...audio.mediaType !== void 0 ? { mediaType: audio.mediaType } : {}
|
|
5468
|
+
};
|
|
5469
|
+
} else {
|
|
5470
|
+
throw new InvalidTranscriptionInputError(
|
|
5471
|
+
"Transcription audio must be bytes, base64, or provider-file"
|
|
5472
|
+
);
|
|
5473
|
+
}
|
|
5474
|
+
if (request.language !== void 0 && !nonEmptyString(request.language)) {
|
|
5475
|
+
throw new InvalidTranscriptionInputError(
|
|
5476
|
+
"Transcription language must be nonempty when supplied"
|
|
5477
|
+
);
|
|
5478
|
+
}
|
|
5479
|
+
if (request.timestamps !== void 0 && request.timestamps !== "segment" && request.timestamps !== "word") {
|
|
5480
|
+
throw new InvalidTranscriptionInputError("Transcription timestamps must be segment or word");
|
|
5481
|
+
}
|
|
5482
|
+
if (request.diarization !== void 0 && typeof request.diarization !== "boolean") {
|
|
5483
|
+
throw new InvalidTranscriptionInputError("Transcription diarization must be boolean");
|
|
5484
|
+
}
|
|
5485
|
+
if (request.providerOptions !== void 0) {
|
|
5486
|
+
if (!request.providerOptions || Array.isArray(request.providerOptions) || typeof request.providerOptions !== "object") {
|
|
5487
|
+
throw new InvalidTranscriptionInputError("Transcription providerOptions must be an object");
|
|
5488
|
+
}
|
|
5489
|
+
for (const key of Object.keys(request.providerOptions)) {
|
|
5490
|
+
if (RESERVED_PROVIDER_OPTION_KEYS.has(key)) {
|
|
5491
|
+
throw new InvalidTranscriptionInputError(
|
|
5492
|
+
"Transcription providerOptions cannot override reserved operation fields"
|
|
5493
|
+
);
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
}
|
|
5497
|
+
return {
|
|
5498
|
+
model: request.model,
|
|
5499
|
+
audio: normalizedAudio,
|
|
5500
|
+
...request.language !== void 0 ? { language: request.language } : {},
|
|
5501
|
+
...request.timestamps !== void 0 ? { timestamps: request.timestamps } : {},
|
|
5502
|
+
...request.diarization !== void 0 ? { diarization: request.diarization } : {},
|
|
5503
|
+
...request.providerOptions !== void 0 ? { providerOptions: cloneValue(request.providerOptions) } : {}
|
|
5504
|
+
};
|
|
5505
|
+
}
|
|
5506
|
+
function cloneTranscript(transcript) {
|
|
5507
|
+
if (!transcript || typeof transcript !== "object" || typeof transcript.text !== "string") {
|
|
5508
|
+
throw new InvalidTranscriptionInputError(
|
|
5509
|
+
"Transcription provider returned an invalid transcript"
|
|
5510
|
+
);
|
|
5511
|
+
}
|
|
5512
|
+
const cloned = cloneValue(transcript);
|
|
5513
|
+
const accounting = normalizeTranscriptionAccounting(cloned.usage, cloned.pricingStatus);
|
|
5514
|
+
return {
|
|
5515
|
+
text: cloned.text,
|
|
5516
|
+
...cloned.detectedLanguages ? { detectedLanguages: cloned.detectedLanguages } : {},
|
|
5517
|
+
...cloned.segments ? { segments: cloned.segments } : {},
|
|
5518
|
+
...cloned.words ? { words: cloned.words } : {},
|
|
5519
|
+
...accounting.usage ? { usage: accounting.usage } : {},
|
|
5520
|
+
...accounting.pricingStatus ? { pricingStatus: accounting.pricingStatus } : {},
|
|
5521
|
+
...cloned.providerMetadata ? { providerMetadata: cloned.providerMetadata } : {}
|
|
5522
|
+
};
|
|
5523
|
+
}
|
|
5524
|
+
function normalizeTranscriptionAccounting(usageValue, pricingValue) {
|
|
5525
|
+
const value = usageValue && typeof usageValue === "object" ? usageValue : void 0;
|
|
5526
|
+
const number = (key) => {
|
|
5527
|
+
const candidate = value?.[key];
|
|
5528
|
+
return typeof candidate === "number" && Number.isFinite(candidate) && candidate >= 0 ? candidate : void 0;
|
|
5529
|
+
};
|
|
5530
|
+
const audioSeconds = number("audioSeconds");
|
|
5531
|
+
const inputTokens = number("inputTokens");
|
|
5532
|
+
const outputTokens = number("outputTokens");
|
|
5533
|
+
const totalTokens = number("totalTokens");
|
|
5534
|
+
const reportedCost = number("cost");
|
|
5535
|
+
const usage = value && (audioSeconds !== void 0 || inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 || reportedCost !== void 0) ? {
|
|
5536
|
+
...audioSeconds !== void 0 ? { audioSeconds } : {},
|
|
5537
|
+
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
5538
|
+
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
5539
|
+
...totalTokens !== void 0 ? { totalTokens } : {},
|
|
5540
|
+
...reportedCost !== void 0 ? { cost: reportedCost } : {}
|
|
5541
|
+
} : void 0;
|
|
5542
|
+
const hasWork = (audioSeconds ?? 0) > 0 || (inputTokens ?? 0) > 0 || (outputTokens ?? 0) > 0 || (totalTokens ?? 0) > 0;
|
|
5543
|
+
const requested = pricingValue === "priced" || pricingValue === "unpriced" || pricingValue === "zero" ? pricingValue : void 0;
|
|
5544
|
+
const cost = requested === "zero" && (reportedCost === void 0 || reportedCost === 0) ? 0 : reportedCost;
|
|
5545
|
+
const pricingStatus = requested === "zero" && (reportedCost === void 0 || reportedCost === 0) ? "zero" : requested === "priced" && cost !== void 0 ? "priced" : requested === "unpriced" && cost === void 0 ? "unpriced" : cost !== void 0 ? "priced" : hasWork || requested === "unpriced" || requested === "priced" ? "unpriced" : void 0;
|
|
5546
|
+
const tokens = inputTokens !== void 0 || outputTokens !== void 0 ? {
|
|
5547
|
+
...inputTokens !== void 0 ? { input: inputTokens } : {},
|
|
5548
|
+
...outputTokens !== void 0 ? { output: outputTokens } : {}
|
|
5549
|
+
} : void 0;
|
|
5550
|
+
return {
|
|
5551
|
+
...usage ? { usage } : {},
|
|
5552
|
+
...pricingStatus ? { pricingStatus } : {},
|
|
5553
|
+
...cost !== void 0 ? { cost } : {},
|
|
5554
|
+
...tokens ? { tokens } : {},
|
|
5555
|
+
hasWork
|
|
5556
|
+
};
|
|
5557
|
+
}
|
|
5558
|
+
|
|
5559
|
+
// src/providers/transcription-utils.ts
|
|
5560
|
+
var OPENAI_TRANSCRIPTION_MODEL = "gpt-transcribe";
|
|
5561
|
+
var GEMINI_TRANSCRIPTION_MODEL = "gemini-3.5-transcribe";
|
|
5562
|
+
function requireExactTranscriptionModel(provider, model, expected) {
|
|
5563
|
+
if (model !== expected) {
|
|
5564
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5565
|
+
provider,
|
|
5566
|
+
model,
|
|
5567
|
+
feature: "this transcription model"
|
|
5568
|
+
});
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5571
|
+
function inlineAudioBytes(audio) {
|
|
5572
|
+
if (audio.type === "bytes") return new Uint8Array(audio.data);
|
|
5573
|
+
if (audio.type === "base64") return Uint8Array.from(Buffer.from(audio.data, "base64"));
|
|
5574
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5575
|
+
provider: audio.provider,
|
|
5576
|
+
model: "",
|
|
5577
|
+
feature: "provider-file transcription input"
|
|
5578
|
+
});
|
|
5579
|
+
}
|
|
5580
|
+
function requireNoProviderFile(audio, provider, model) {
|
|
5581
|
+
if (audio.type === "provider-file") {
|
|
5582
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5583
|
+
provider,
|
|
5584
|
+
model,
|
|
5585
|
+
feature: "provider-file transcription input"
|
|
5586
|
+
});
|
|
5587
|
+
}
|
|
5588
|
+
}
|
|
5589
|
+
function assertOnlyProviderOptions(request, provider, model, allowed) {
|
|
5590
|
+
const options = request.providerOptions ?? {};
|
|
5591
|
+
for (const key of Object.keys(options)) {
|
|
5592
|
+
if (!allowed.includes(key)) {
|
|
5593
|
+
throw new InvalidTranscriptionInputError(
|
|
5594
|
+
`Unsupported ${provider} transcription provider option for model '${model}'`
|
|
5595
|
+
);
|
|
5596
|
+
}
|
|
5597
|
+
}
|
|
5598
|
+
return options;
|
|
5599
|
+
}
|
|
5600
|
+
function transcriptUsage(value) {
|
|
5601
|
+
const source = value && typeof value === "object" ? value : {};
|
|
5602
|
+
const number = (...keys) => {
|
|
5603
|
+
for (const key of keys) {
|
|
5604
|
+
const candidate = source[key];
|
|
5605
|
+
if (typeof candidate === "number" && Number.isFinite(candidate) && candidate >= 0)
|
|
5606
|
+
return candidate;
|
|
5607
|
+
}
|
|
5608
|
+
return void 0;
|
|
5609
|
+
};
|
|
5610
|
+
const inputTokens = number("total_input_tokens", "input_tokens", "prompt_tokens", "inputTokens");
|
|
5611
|
+
const outputTokens = number(
|
|
5612
|
+
"total_output_tokens",
|
|
5613
|
+
"output_tokens",
|
|
5614
|
+
"completion_tokens",
|
|
5615
|
+
"outputTokens"
|
|
5616
|
+
);
|
|
5617
|
+
const totalTokens = number("total_tokens", "totalTokens");
|
|
5618
|
+
const audioSeconds = number("seconds", "audio_seconds", "audioSeconds", "duration_seconds");
|
|
5619
|
+
const cost = number("cost");
|
|
5620
|
+
const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 || audioSeconds !== void 0 || cost !== void 0 ? {
|
|
5621
|
+
...audioSeconds !== void 0 ? { audioSeconds } : {},
|
|
5622
|
+
...inputTokens !== void 0 ? { inputTokens } : {},
|
|
5623
|
+
...outputTokens !== void 0 ? { outputTokens } : {},
|
|
5624
|
+
...totalTokens !== void 0 ? { totalTokens } : {},
|
|
5625
|
+
...cost !== void 0 ? { cost } : {}
|
|
5626
|
+
} : void 0;
|
|
5627
|
+
const hasWork = (audioSeconds ?? 0) > 0 || (inputTokens ?? 0) > 0 || (outputTokens ?? 0) > 0 || (totalTokens ?? 0) > 0;
|
|
5628
|
+
return {
|
|
5629
|
+
...usage ? { usage } : {},
|
|
5630
|
+
...cost !== void 0 ? { pricingStatus: "priced" } : hasWork ? { pricingStatus: "unpriced" } : {}
|
|
5631
|
+
};
|
|
5632
|
+
}
|
|
5633
|
+
function safeProviderMessage(provider, status, body) {
|
|
5634
|
+
try {
|
|
5635
|
+
const parsed = JSON.parse(body);
|
|
5636
|
+
const message = parsed.error?.message ?? parsed.message;
|
|
5637
|
+
if (typeof message === "string")
|
|
5638
|
+
return `${provider} transcription API error (${status}): ${message}`;
|
|
5639
|
+
} catch {
|
|
5640
|
+
}
|
|
5641
|
+
return `${provider} transcription API error (${status})`;
|
|
5642
|
+
}
|
|
5643
|
+
function attachTranscriptionFailureMetadata(error, metadata) {
|
|
5644
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
5645
|
+
if (value !== void 0) Object.defineProperty(error, key, { enumerable: true, value });
|
|
5646
|
+
}
|
|
5647
|
+
return error;
|
|
5648
|
+
}
|
|
5649
|
+
|
|
5650
|
+
// src/providers/openai-transcription.ts
|
|
5651
|
+
var OPENAI_FILE_EXTENSIONS = {
|
|
5652
|
+
"audio/flac": "flac",
|
|
5653
|
+
"audio/m4a": "m4a",
|
|
5654
|
+
"audio/mp3": "mp3",
|
|
5655
|
+
"audio/mp4": "mp4",
|
|
5656
|
+
"audio/mpeg": "mp3",
|
|
5657
|
+
"audio/mpga": "mpga",
|
|
5658
|
+
"audio/ogg": "ogg",
|
|
5659
|
+
"audio/wav": "wav",
|
|
5660
|
+
"audio/webm": "webm"
|
|
5661
|
+
};
|
|
5662
|
+
var OpenAITranscriptionProvider = class {
|
|
5663
|
+
name = "openai-transcription";
|
|
5664
|
+
baseUrl;
|
|
5665
|
+
apiKeySource;
|
|
5666
|
+
governor;
|
|
5667
|
+
constructor(options = {}) {
|
|
5668
|
+
this.apiKeySource = options.apiKey ?? process.env.OPENAI_API_KEY ?? "";
|
|
5669
|
+
this.baseUrl = (options.baseUrl ?? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
5670
|
+
assertSafeProviderBaseUrl(
|
|
5671
|
+
this.baseUrl,
|
|
5672
|
+
"OpenAI transcription provider",
|
|
5673
|
+
options.dangerouslyAllowInsecureHttp
|
|
5674
|
+
);
|
|
5675
|
+
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
5676
|
+
}
|
|
5677
|
+
capabilities(model) {
|
|
5678
|
+
return model === OPENAI_TRANSCRIPTION_MODEL ? { sources: ["bytes", "base64"] } : void 0;
|
|
5679
|
+
}
|
|
5680
|
+
async transcribe(request) {
|
|
5681
|
+
requireExactTranscriptionModel(this.name, request.model, OPENAI_TRANSCRIPTION_MODEL);
|
|
5682
|
+
requireNoProviderFile(request.audio, this.name, request.model);
|
|
5683
|
+
if (request.audio.type === "provider-file")
|
|
5684
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5685
|
+
provider: this.name,
|
|
5686
|
+
model: request.model,
|
|
5687
|
+
feature: "provider-file transcription input"
|
|
5688
|
+
});
|
|
5689
|
+
const options = assertOnlyProviderOptions(request, this.name, request.model, [
|
|
5690
|
+
"temperature",
|
|
5691
|
+
"prompt"
|
|
5692
|
+
]);
|
|
5693
|
+
if (options.temperature !== void 0 && (typeof options.temperature !== "number" || !Number.isFinite(options.temperature) || options.temperature < 0 || options.temperature > 1))
|
|
5694
|
+
throw new InvalidTranscriptionInputError(
|
|
5695
|
+
"OpenAI transcription temperature must be a number between 0 and 1"
|
|
5696
|
+
);
|
|
5697
|
+
if (options.prompt !== void 0 && typeof options.prompt !== "string")
|
|
5698
|
+
throw new InvalidTranscriptionInputError("OpenAI transcription prompt must be a string");
|
|
5699
|
+
const mediaType2 = request.audio.mediaType;
|
|
5700
|
+
const extension = OPENAI_FILE_EXTENSIONS[mediaType2];
|
|
5701
|
+
if (!extension)
|
|
5702
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5703
|
+
provider: this.name,
|
|
5704
|
+
model: request.model,
|
|
5705
|
+
feature: "this audio media type"
|
|
5706
|
+
});
|
|
5707
|
+
const bytes = inlineAudioBytes(request.audio);
|
|
5708
|
+
if (bytes.byteLength > MAX_INLINE_TRANSCRIPTION_BYTES)
|
|
5709
|
+
throw new InvalidTranscriptionInputError("OpenAI transcription audio must not exceed 25 MiB");
|
|
5710
|
+
const form = new FormData();
|
|
5711
|
+
form.set("file", new Blob([Buffer.from(bytes)], { type: mediaType2 }), `audio.${extension}`);
|
|
5712
|
+
form.set("model", request.model);
|
|
5713
|
+
form.set("response_format", "json");
|
|
5714
|
+
if (request.language) form.set("language", request.language);
|
|
5715
|
+
if (options.temperature !== void 0) form.set("temperature", String(options.temperature));
|
|
5716
|
+
if (options.prompt !== void 0) form.set("prompt", options.prompt);
|
|
5717
|
+
const key = await this.resolveKey();
|
|
5718
|
+
const res = await fetchWithRetry(
|
|
5719
|
+
`${this.baseUrl}/audio/transcriptions`,
|
|
5720
|
+
{
|
|
5721
|
+
method: "POST",
|
|
5722
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
5723
|
+
body: form,
|
|
5724
|
+
signal: request.signal
|
|
5725
|
+
},
|
|
5726
|
+
{ governor: this.governor, provider: this.name }
|
|
5727
|
+
);
|
|
5728
|
+
if (!res.ok) {
|
|
5729
|
+
const body = await res.text();
|
|
5730
|
+
throw buildProviderError({
|
|
5731
|
+
provider: this.name,
|
|
5732
|
+
status: res.status,
|
|
5733
|
+
headers: res.headers,
|
|
5734
|
+
message: safeProviderMessage("OpenAI", res.status, body),
|
|
5735
|
+
body
|
|
5736
|
+
});
|
|
5737
|
+
}
|
|
5738
|
+
const json = await res.json();
|
|
5739
|
+
if (typeof json.text !== "string")
|
|
5740
|
+
throw new InvalidTranscriptionInputError(
|
|
5741
|
+
"OpenAI transcription response did not contain text"
|
|
5742
|
+
);
|
|
5743
|
+
return {
|
|
5744
|
+
transcript: { text: json.text, ...transcriptUsage(json.usage) },
|
|
5745
|
+
cleanupStatus: "not_required"
|
|
5746
|
+
};
|
|
5747
|
+
}
|
|
5748
|
+
async resolveKey() {
|
|
5749
|
+
const key = await resolveApiKey(this.apiKeySource);
|
|
5750
|
+
if (!key)
|
|
5751
|
+
throw new Error("OpenAI API key is required. Set OPENAI_API_KEY or pass apiKey in options.");
|
|
5752
|
+
return key;
|
|
5753
|
+
}
|
|
5754
|
+
};
|
|
5755
|
+
|
|
5756
|
+
// src/providers/gemini-transcription.ts
|
|
5757
|
+
var CLEANUP_TIMEOUT_MS = 3e3;
|
|
5758
|
+
var READY_TIMEOUT_MS = 3e4;
|
|
5759
|
+
var READY_POLL_MS = 250;
|
|
5760
|
+
var GEMINI_AUDIO_MEDIA_TYPES = /* @__PURE__ */ new Set([
|
|
5761
|
+
"audio/wav",
|
|
5762
|
+
"audio/mp3",
|
|
5763
|
+
"audio/aiff",
|
|
5764
|
+
"audio/aac",
|
|
5765
|
+
"audio/ogg",
|
|
5766
|
+
"audio/flac",
|
|
5767
|
+
"audio/mpeg",
|
|
5768
|
+
"audio/m4a",
|
|
5769
|
+
"audio/l16",
|
|
5770
|
+
"audio/opus",
|
|
5771
|
+
"audio/alaw",
|
|
5772
|
+
"audio/mulaw",
|
|
5773
|
+
"audio/webm"
|
|
5774
|
+
]);
|
|
5775
|
+
function abortError2() {
|
|
5776
|
+
return new DOMException("The transcription operation was aborted", "AbortError");
|
|
5777
|
+
}
|
|
5778
|
+
function audioMime(audio) {
|
|
5779
|
+
if (!audio.mediaType)
|
|
5780
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5781
|
+
provider: "gemini-transcription",
|
|
5782
|
+
model: GEMINI_TRANSCRIPTION_MODEL,
|
|
5783
|
+
feature: "provider-file audio mediaType"
|
|
5784
|
+
});
|
|
5785
|
+
return audio.mediaType;
|
|
5786
|
+
}
|
|
5787
|
+
function assertGeminiAudioMime(mediaType2) {
|
|
5788
|
+
if (!mediaType2 || !GEMINI_AUDIO_MEDIA_TYPES.has(mediaType2)) {
|
|
5789
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5790
|
+
provider: "gemini-transcription",
|
|
5791
|
+
model: GEMINI_TRANSCRIPTION_MODEL,
|
|
5792
|
+
feature: "this audio media type"
|
|
5793
|
+
});
|
|
5794
|
+
}
|
|
5795
|
+
return mediaType2;
|
|
5796
|
+
}
|
|
5797
|
+
function secondsOffset(value) {
|
|
5798
|
+
if (typeof value !== "string") return void 0;
|
|
5799
|
+
const match = /^(\d+(?:\.\d+)?)s$/.exec(value);
|
|
5800
|
+
if (!match) return void 0;
|
|
5801
|
+
const seconds = Number(match[1]);
|
|
5802
|
+
return Number.isFinite(seconds) && seconds >= 0 ? seconds : void 0;
|
|
5803
|
+
}
|
|
5804
|
+
function sleep3(ms, signal) {
|
|
5805
|
+
return new Promise((resolve2, reject) => {
|
|
5806
|
+
if (signal?.aborted) {
|
|
5807
|
+
reject(abortError2());
|
|
5808
|
+
return;
|
|
5809
|
+
}
|
|
5810
|
+
const timer = setTimeout(done, ms);
|
|
5811
|
+
const onAbort = () => {
|
|
5812
|
+
clearTimeout(timer);
|
|
5813
|
+
done(abortError2());
|
|
5814
|
+
};
|
|
5815
|
+
function done(error) {
|
|
5816
|
+
signal?.removeEventListener("abort", onAbort);
|
|
5817
|
+
if (error) reject(error);
|
|
5818
|
+
else resolve2();
|
|
5819
|
+
}
|
|
5820
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5821
|
+
});
|
|
5822
|
+
}
|
|
5823
|
+
var GeminiTranscriptionProvider = class {
|
|
5824
|
+
name = "gemini-transcription";
|
|
5825
|
+
baseUrl;
|
|
5826
|
+
apiKeySource;
|
|
5827
|
+
governor;
|
|
5828
|
+
constructor(options = {}) {
|
|
5829
|
+
this.apiKeySource = options.apiKey ?? process.env.GOOGLE_API_KEY ?? process.env.GEMINI_API_KEY ?? "";
|
|
5830
|
+
this.baseUrl = (options.baseUrl ?? "https://generativelanguage.googleapis.com/v1beta").replace(
|
|
5831
|
+
/\/$/,
|
|
5832
|
+
""
|
|
5833
|
+
);
|
|
5834
|
+
assertSafeProviderBaseUrl(
|
|
5835
|
+
this.baseUrl,
|
|
5836
|
+
"Gemini transcription provider",
|
|
5837
|
+
options.dangerouslyAllowInsecureHttp
|
|
5838
|
+
);
|
|
5839
|
+
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
5840
|
+
}
|
|
5841
|
+
capabilities(model) {
|
|
5842
|
+
return model === GEMINI_TRANSCRIPTION_MODEL ? { sources: ["bytes", "base64", "provider-file"], timestamps: ["word"], diarization: true } : void 0;
|
|
5843
|
+
}
|
|
5844
|
+
async transcribe(request) {
|
|
5845
|
+
requireExactTranscriptionModel(this.name, request.model, GEMINI_TRANSCRIPTION_MODEL);
|
|
5846
|
+
const options = assertOnlyProviderOptions(request, this.name, request.model, [
|
|
5847
|
+
"mode",
|
|
5848
|
+
"customVocabulary"
|
|
5849
|
+
]);
|
|
5850
|
+
const mode = options.mode ?? "verbatim";
|
|
5851
|
+
if (mode !== "smart" && mode !== "verbatim")
|
|
5852
|
+
throw new InvalidTranscriptionInputError(
|
|
5853
|
+
"Gemini transcription mode must be smart or verbatim"
|
|
5854
|
+
);
|
|
5855
|
+
if (options.customVocabulary !== void 0 && (!Array.isArray(options.customVocabulary) || options.customVocabulary.length > 1e3 || options.customVocabulary.some(
|
|
5856
|
+
(value) => typeof value !== "string" || !value.trim() || value.length > 256
|
|
5857
|
+
)))
|
|
5858
|
+
throw new InvalidTranscriptionInputError(
|
|
5859
|
+
"Gemini transcription customVocabulary must contain at most 1000 nonempty strings up to 256 characters"
|
|
5860
|
+
);
|
|
5861
|
+
if (mode === "smart" && (request.timestamps || request.diarization))
|
|
5862
|
+
throw new InvalidTranscriptionInputError(
|
|
5863
|
+
"Gemini smart transcription does not support timestamps or diarization"
|
|
5864
|
+
);
|
|
5865
|
+
if (request.timestamps && options.customVocabulary !== void 0)
|
|
5866
|
+
throw new InvalidTranscriptionInputError(
|
|
5867
|
+
"Gemini transcription customVocabulary cannot be combined with timestamps"
|
|
5868
|
+
);
|
|
5869
|
+
if (request.timestamps && request.timestamps !== "word")
|
|
5870
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5871
|
+
provider: this.name,
|
|
5872
|
+
model: request.model,
|
|
5873
|
+
feature: "segment timestamps"
|
|
5874
|
+
});
|
|
5875
|
+
if (request.diarization && request.timestamps !== "word")
|
|
5876
|
+
throw new InvalidTranscriptionInputError("Gemini diarization requires word timestamps");
|
|
5877
|
+
if (request.audio.type === "provider-file") {
|
|
5878
|
+
if (request.audio.provider !== this.name || typeof request.audio.reference !== "string" || !request.audio.reference.trim()) {
|
|
5879
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5880
|
+
provider: this.name,
|
|
5881
|
+
model: request.model,
|
|
5882
|
+
feature: "a matching nonempty provider-file reference"
|
|
5883
|
+
});
|
|
5884
|
+
}
|
|
5885
|
+
assertGeminiAudioMime(request.audio.mediaType);
|
|
5886
|
+
} else {
|
|
5887
|
+
assertGeminiAudioMime(request.audio.mediaType);
|
|
5888
|
+
}
|
|
5889
|
+
const key = await this.resolveKey();
|
|
5890
|
+
if (request.audio.type === "provider-file")
|
|
5891
|
+
return this.transcribeReference(request, key, mode, options);
|
|
5892
|
+
return this.transcribeUpload(request, key, mode, options);
|
|
5893
|
+
}
|
|
5894
|
+
async transcribeReference(request, key, mode, options) {
|
|
5895
|
+
const audio = request.audio;
|
|
5896
|
+
if (audio.type !== "provider-file" || audio.provider !== this.name)
|
|
5897
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5898
|
+
provider: this.name,
|
|
5899
|
+
model: request.model,
|
|
5900
|
+
feature: "a provider-file owned by another provider"
|
|
5901
|
+
});
|
|
5902
|
+
const mimeType = audioMime(audio);
|
|
5903
|
+
const transcript = await this.infer(request, key, audio.reference, mimeType, mode, options);
|
|
5904
|
+
return { transcript, cleanupStatus: "not_required" };
|
|
5905
|
+
}
|
|
5906
|
+
async transcribeUpload(request, key, mode, options) {
|
|
5907
|
+
let created = false;
|
|
5908
|
+
let fileName;
|
|
5909
|
+
let primaryError;
|
|
5910
|
+
let transcript;
|
|
5911
|
+
let cleanupStatus = "not_required";
|
|
5912
|
+
try {
|
|
5913
|
+
const uploaded = await this.upload(request, key);
|
|
5914
|
+
created = true;
|
|
5915
|
+
fileName = uploaded.name;
|
|
5916
|
+
const ready = await this.waitForReady(
|
|
5917
|
+
this.fileFromJson(uploaded.file, uploaded.mimeType),
|
|
5918
|
+
key,
|
|
5919
|
+
request.signal
|
|
5920
|
+
);
|
|
5921
|
+
transcript = await this.infer(request, key, ready.uri, ready.mimeType, mode, options);
|
|
5922
|
+
} catch (error) {
|
|
5923
|
+
primaryError = error;
|
|
5924
|
+
} finally {
|
|
5925
|
+
if (created && fileName) cleanupStatus = await this.cleanup(fileName, key);
|
|
5926
|
+
}
|
|
5927
|
+
if (primaryError) {
|
|
5928
|
+
throw attachTranscriptionFailureMetadata(
|
|
5929
|
+
primaryError instanceof Error ? primaryError : new Error("Gemini transcription failed"),
|
|
5930
|
+
{ cleanupStatus }
|
|
5931
|
+
);
|
|
5932
|
+
}
|
|
5933
|
+
if (!transcript)
|
|
5934
|
+
throw new InvalidTranscriptionInputError(
|
|
5935
|
+
"Gemini transcription response did not contain text"
|
|
5936
|
+
);
|
|
5937
|
+
return { transcript, cleanupStatus };
|
|
5938
|
+
}
|
|
5939
|
+
/** A finalization response with a name creates a caller-owned cleanup obligation
|
|
5940
|
+
* even when its URI/state is malformed. `transcribeUpload` stores the name before
|
|
5941
|
+
* strictly parsing the rest of the file representation. */
|
|
5942
|
+
async upload(request, key) {
|
|
5943
|
+
if (request.audio.type === "provider-file")
|
|
5944
|
+
throw new UnsupportedTranscriptionInputError({
|
|
5945
|
+
provider: this.name,
|
|
5946
|
+
model: request.model,
|
|
5947
|
+
feature: "provider-file upload"
|
|
5948
|
+
});
|
|
5949
|
+
const mimeType = assertGeminiAudioMime(request.audio.mediaType);
|
|
5950
|
+
const bytes = inlineAudioBytes(request.audio);
|
|
5951
|
+
const start = await fetchWithRetry(
|
|
5952
|
+
`${this.baseUrl.replace(/\/v1beta$/, "")}/upload/v1beta/files`,
|
|
5953
|
+
{
|
|
5954
|
+
method: "POST",
|
|
5955
|
+
headers: {
|
|
5956
|
+
"x-goog-api-key": key,
|
|
5957
|
+
"X-Goog-Upload-Protocol": "resumable",
|
|
5958
|
+
"X-Goog-Upload-Command": "start",
|
|
5959
|
+
"X-Goog-Upload-Header-Content-Length": String(bytes.byteLength),
|
|
5960
|
+
"X-Goog-Upload-Header-Content-Type": mimeType,
|
|
5961
|
+
"Content-Type": "application/json"
|
|
5962
|
+
},
|
|
5963
|
+
body: JSON.stringify({ file: { display_name: "axl-transcription" } }),
|
|
5964
|
+
signal: request.signal
|
|
5965
|
+
},
|
|
5966
|
+
{ governor: this.governor, provider: this.name, maxRetries: 0 }
|
|
5967
|
+
);
|
|
5968
|
+
if (!start.ok) {
|
|
5969
|
+
const body = await start.text();
|
|
5970
|
+
throw buildProviderError({
|
|
5971
|
+
provider: this.name,
|
|
5972
|
+
status: start.status,
|
|
5973
|
+
headers: start.headers,
|
|
5974
|
+
message: safeProviderMessage("Gemini", start.status, body),
|
|
5975
|
+
body
|
|
5976
|
+
});
|
|
5977
|
+
}
|
|
5978
|
+
const uploadUrl = start.headers.get("x-goog-upload-url");
|
|
5979
|
+
if (!uploadUrl)
|
|
5980
|
+
throw new InvalidTranscriptionInputError(
|
|
5981
|
+
"Gemini transcription upload did not return an upload URL"
|
|
5982
|
+
);
|
|
5983
|
+
let parsedUploadUrl;
|
|
5984
|
+
try {
|
|
5985
|
+
parsedUploadUrl = new URL(uploadUrl);
|
|
5986
|
+
} catch {
|
|
5987
|
+
throw new InvalidTranscriptionInputError(
|
|
5988
|
+
"Gemini transcription upload returned an invalid upload URL"
|
|
5989
|
+
);
|
|
5990
|
+
}
|
|
5991
|
+
const baseOrigin = new URL(this.baseUrl).origin;
|
|
5992
|
+
if (parsedUploadUrl.username || parsedUploadUrl.password || parsedUploadUrl.origin !== baseOrigin) {
|
|
5993
|
+
throw new InvalidTranscriptionInputError(
|
|
5994
|
+
"Gemini transcription upload returned an untrusted upload URL"
|
|
5995
|
+
);
|
|
5996
|
+
}
|
|
5997
|
+
assertSafeProviderBaseUrl(
|
|
5998
|
+
parsedUploadUrl.toString(),
|
|
5999
|
+
"Gemini transcription upload endpoint",
|
|
6000
|
+
new URL(this.baseUrl).protocol === "http:"
|
|
6001
|
+
);
|
|
6002
|
+
const finalize = await fetchWithRetry(
|
|
6003
|
+
parsedUploadUrl.toString(),
|
|
6004
|
+
{
|
|
6005
|
+
method: "POST",
|
|
6006
|
+
headers: {
|
|
6007
|
+
"X-Goog-Upload-Command": "upload, finalize",
|
|
6008
|
+
"X-Goog-Upload-Offset": "0",
|
|
6009
|
+
"Content-Length": String(bytes.byteLength)
|
|
6010
|
+
},
|
|
6011
|
+
body: bytes,
|
|
6012
|
+
signal: request.signal
|
|
6013
|
+
},
|
|
6014
|
+
{ governor: this.governor, provider: this.name, maxRetries: 0 }
|
|
6015
|
+
);
|
|
6016
|
+
if (!finalize.ok) {
|
|
6017
|
+
const body = await finalize.text();
|
|
6018
|
+
throw buildProviderError({
|
|
6019
|
+
provider: this.name,
|
|
6020
|
+
status: finalize.status,
|
|
6021
|
+
headers: finalize.headers,
|
|
6022
|
+
message: safeProviderMessage("Gemini", finalize.status, body),
|
|
6023
|
+
body
|
|
6024
|
+
});
|
|
6025
|
+
}
|
|
6026
|
+
const payload = await finalize.json();
|
|
6027
|
+
const file = payload.file;
|
|
6028
|
+
if (!file || typeof file.name !== "string") {
|
|
6029
|
+
throw new InvalidTranscriptionInputError(
|
|
6030
|
+
"Gemini transcription upload did not return a file name"
|
|
6031
|
+
);
|
|
6032
|
+
}
|
|
6033
|
+
return { name: file.name, file, mimeType };
|
|
6034
|
+
}
|
|
6035
|
+
async waitForReady(file, key, signal) {
|
|
6036
|
+
let current = file;
|
|
6037
|
+
const deadline = Date.now() + READY_TIMEOUT_MS;
|
|
6038
|
+
if (!current.state) {
|
|
6039
|
+
throw new InvalidTranscriptionInputError(
|
|
6040
|
+
"Gemini transcription file readiness state is missing"
|
|
6041
|
+
);
|
|
6042
|
+
}
|
|
6043
|
+
while (current.state && current.state !== "ACTIVE") {
|
|
6044
|
+
if (current.state === "FAILED")
|
|
6045
|
+
throw new InvalidTranscriptionInputError("Gemini transcription file processing failed");
|
|
6046
|
+
if (Date.now() >= deadline)
|
|
6047
|
+
throw new InvalidTranscriptionInputError("Gemini transcription file readiness timed out");
|
|
6048
|
+
await sleep3(Math.min(READY_POLL_MS, deadline - Date.now()), signal);
|
|
6049
|
+
const res = await fetchWithRetry(
|
|
6050
|
+
`${this.baseUrl}/${current.name}`,
|
|
6051
|
+
{ method: "GET", headers: { "x-goog-api-key": key }, signal },
|
|
6052
|
+
{ governor: this.governor, provider: this.name, maxRetries: 0 }
|
|
6053
|
+
);
|
|
6054
|
+
if (!res.ok) {
|
|
6055
|
+
const body = await res.text();
|
|
6056
|
+
throw buildProviderError({
|
|
6057
|
+
provider: this.name,
|
|
6058
|
+
status: res.status,
|
|
6059
|
+
headers: res.headers,
|
|
6060
|
+
message: safeProviderMessage("Gemini", res.status, body),
|
|
6061
|
+
body
|
|
6062
|
+
});
|
|
6063
|
+
}
|
|
6064
|
+
current = this.fileFromJson(await res.json(), current.mimeType);
|
|
6065
|
+
}
|
|
6066
|
+
return current;
|
|
6067
|
+
}
|
|
6068
|
+
async infer(request, key, uri, mimeType, mode, options) {
|
|
6069
|
+
const transcriptionConfig = {
|
|
6070
|
+
mode: mode === "smart" ? "smart" : {
|
|
6071
|
+
type: "verbatim",
|
|
6072
|
+
...request.timestamps ? { timestamp_granularities: ["word"] } : {},
|
|
6073
|
+
...request.diarization ? { diarization_mode: "speaker" } : {}
|
|
6074
|
+
},
|
|
6075
|
+
...request.language ? { language_codes: [request.language] } : {},
|
|
6076
|
+
...options.customVocabulary ? { custom_vocabulary: options.customVocabulary } : {}
|
|
6077
|
+
};
|
|
6078
|
+
const body = {
|
|
6079
|
+
model: request.model,
|
|
6080
|
+
input: [{ type: "audio", uri, mime_type: mimeType }],
|
|
6081
|
+
store: false,
|
|
6082
|
+
generation_config: { transcription_config: transcriptionConfig }
|
|
6083
|
+
};
|
|
6084
|
+
const res = await fetchWithRetry(
|
|
6085
|
+
`${this.baseUrl}/interactions`,
|
|
6086
|
+
{
|
|
6087
|
+
method: "POST",
|
|
6088
|
+
headers: { "x-goog-api-key": key, "Content-Type": "application/json" },
|
|
6089
|
+
body: JSON.stringify(body),
|
|
6090
|
+
signal: request.signal
|
|
6091
|
+
},
|
|
6092
|
+
{ governor: this.governor, provider: this.name }
|
|
6093
|
+
);
|
|
6094
|
+
if (!res.ok) {
|
|
6095
|
+
const errorBody = await res.text();
|
|
6096
|
+
throw buildProviderError({
|
|
6097
|
+
provider: this.name,
|
|
6098
|
+
status: res.status,
|
|
6099
|
+
headers: res.headers,
|
|
6100
|
+
message: safeProviderMessage("Gemini", res.status, errorBody),
|
|
6101
|
+
body: errorBody
|
|
6102
|
+
});
|
|
6103
|
+
}
|
|
6104
|
+
const json = await res.json();
|
|
6105
|
+
if (json.status !== "completed") {
|
|
6106
|
+
throw new InvalidTranscriptionInputError("Gemini transcription interaction did not complete");
|
|
6107
|
+
}
|
|
6108
|
+
const words = [];
|
|
6109
|
+
let text = "";
|
|
6110
|
+
let hasModelOutputText = false;
|
|
6111
|
+
if (Array.isArray(json.steps)) {
|
|
6112
|
+
for (const step of json.steps) {
|
|
6113
|
+
if (!step || typeof step !== "object") continue;
|
|
6114
|
+
const modelOutput = step;
|
|
6115
|
+
if (modelOutput.type !== "model_output" || !Array.isArray(modelOutput.content)) continue;
|
|
6116
|
+
for (const block of modelOutput.content) {
|
|
6117
|
+
if (!block || typeof block !== "object") continue;
|
|
6118
|
+
const content = block;
|
|
6119
|
+
if (content.type !== "text" || typeof content.text !== "string") continue;
|
|
6120
|
+
hasModelOutputText = true;
|
|
6121
|
+
text += content.text;
|
|
6122
|
+
if (!Array.isArray(content.annotations)) continue;
|
|
6123
|
+
for (const raw of content.annotations) {
|
|
6124
|
+
if (!raw || typeof raw !== "object") continue;
|
|
6125
|
+
const annotation = raw;
|
|
6126
|
+
if (annotation.type !== "word_info") continue;
|
|
6127
|
+
if (typeof annotation.text !== "string") continue;
|
|
6128
|
+
const start = secondsOffset(annotation.start_offset);
|
|
6129
|
+
const end = secondsOffset(annotation.end_offset);
|
|
6130
|
+
if (start === void 0 || end === void 0 || end < start) continue;
|
|
6131
|
+
words.push({
|
|
6132
|
+
text: annotation.text,
|
|
6133
|
+
start,
|
|
6134
|
+
end,
|
|
6135
|
+
...typeof annotation.speaker === "string" ? { speaker: annotation.speaker } : {}
|
|
6136
|
+
});
|
|
6137
|
+
}
|
|
6138
|
+
}
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
if (!hasModelOutputText)
|
|
6142
|
+
throw new InvalidTranscriptionInputError(
|
|
6143
|
+
"Gemini transcription response did not contain model output text"
|
|
6144
|
+
);
|
|
6145
|
+
return { text, ...words.length ? { words } : {}, ...transcriptUsage(json.usage) };
|
|
6146
|
+
}
|
|
6147
|
+
fileFromJson(value, fallbackMimeType) {
|
|
6148
|
+
const file = "file" in value && value.file ? value.file : value;
|
|
6149
|
+
if (typeof file.name !== "string" || typeof file.uri !== "string")
|
|
6150
|
+
throw new InvalidTranscriptionInputError(
|
|
6151
|
+
"Gemini transcription upload did not return a file reference"
|
|
6152
|
+
);
|
|
6153
|
+
return {
|
|
6154
|
+
name: file.name,
|
|
6155
|
+
uri: file.uri,
|
|
6156
|
+
mimeType: typeof file.mimeType === "string" ? file.mimeType : typeof file.mime_type === "string" ? file.mime_type : fallbackMimeType,
|
|
6157
|
+
...typeof file.state === "string" ? { state: file.state } : {}
|
|
6158
|
+
};
|
|
6159
|
+
}
|
|
6160
|
+
async cleanup(fileName, key) {
|
|
6161
|
+
const controller = new AbortController();
|
|
6162
|
+
const timer = setTimeout(() => controller.abort(), CLEANUP_TIMEOUT_MS);
|
|
6163
|
+
try {
|
|
6164
|
+
const res = await fetchWithRetry(
|
|
6165
|
+
`${this.baseUrl}/${fileName}`,
|
|
6166
|
+
{ method: "DELETE", headers: { "x-goog-api-key": key }, signal: controller.signal },
|
|
6167
|
+
{ governor: this.governor, provider: this.name, maxRetries: 0 }
|
|
6168
|
+
);
|
|
6169
|
+
return res.ok ? "deleted" : "failed";
|
|
6170
|
+
} catch {
|
|
6171
|
+
return controller.signal.aborted ? "timed_out" : "failed";
|
|
6172
|
+
} finally {
|
|
6173
|
+
clearTimeout(timer);
|
|
6174
|
+
}
|
|
6175
|
+
}
|
|
6176
|
+
async resolveKey() {
|
|
6177
|
+
const key = await resolveApiKey(this.apiKeySource);
|
|
6178
|
+
if (!key)
|
|
6179
|
+
throw new Error("Google API key is required. Set GOOGLE_API_KEY or pass apiKey in options.");
|
|
6180
|
+
return key;
|
|
6181
|
+
}
|
|
6182
|
+
};
|
|
6183
|
+
|
|
6184
|
+
// src/providers/openrouter-transcription.ts
|
|
6185
|
+
var FORMAT_BY_MIME = {
|
|
6186
|
+
"audio/aac": "aac",
|
|
6187
|
+
"audio/flac": "flac",
|
|
6188
|
+
"audio/m4a": "m4a",
|
|
6189
|
+
"audio/mp3": "mp3",
|
|
6190
|
+
"audio/mp4": "mp4",
|
|
6191
|
+
"audio/mpeg": "mp3",
|
|
6192
|
+
"audio/ogg": "ogg",
|
|
6193
|
+
"audio/wav": "wav",
|
|
6194
|
+
"audio/webm": "webm"
|
|
6195
|
+
};
|
|
6196
|
+
var OpenRouterTranscriptionProvider = class {
|
|
6197
|
+
name = "openrouter-transcription";
|
|
6198
|
+
baseUrl;
|
|
6199
|
+
apiKeySource;
|
|
6200
|
+
governor;
|
|
6201
|
+
constructor(options = {}) {
|
|
6202
|
+
this.apiKeySource = options.apiKey ?? process.env.OPENROUTER_API_KEY ?? "";
|
|
6203
|
+
this.baseUrl = (options.baseUrl ?? process.env.OPENROUTER_BASE_URL ?? "https://openrouter.ai/api/v1").replace(/\/$/, "");
|
|
6204
|
+
assertSafeProviderBaseUrl(
|
|
6205
|
+
this.baseUrl,
|
|
6206
|
+
"OpenRouter transcription provider",
|
|
6207
|
+
options.dangerouslyAllowInsecureHttp
|
|
6208
|
+
);
|
|
6209
|
+
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
6210
|
+
}
|
|
6211
|
+
capabilities(model) {
|
|
6212
|
+
return model.trim().length > 0 ? { sources: ["bytes", "base64"] } : void 0;
|
|
6213
|
+
}
|
|
6214
|
+
async transcribe(request) {
|
|
6215
|
+
if (request.model.trim().length === 0) {
|
|
6216
|
+
throw new UnsupportedTranscriptionInputError({
|
|
6217
|
+
provider: this.name,
|
|
6218
|
+
model: request.model,
|
|
6219
|
+
feature: "this transcription model"
|
|
6220
|
+
});
|
|
6221
|
+
}
|
|
6222
|
+
requireNoProviderFile(request.audio, this.name, request.model);
|
|
6223
|
+
if (request.audio.type === "provider-file")
|
|
6224
|
+
throw new UnsupportedTranscriptionInputError({
|
|
6225
|
+
provider: this.name,
|
|
6226
|
+
model: request.model,
|
|
6227
|
+
feature: "provider-file transcription input"
|
|
6228
|
+
});
|
|
6229
|
+
const options = assertOnlyProviderOptions(request, this.name, request.model, [
|
|
6230
|
+
"temperature",
|
|
6231
|
+
"provider"
|
|
6232
|
+
]);
|
|
6233
|
+
if (options.temperature !== void 0 && (typeof options.temperature !== "number" || !Number.isFinite(options.temperature) || options.temperature < 0 || options.temperature > 1))
|
|
6234
|
+
throw new InvalidTranscriptionInputError(
|
|
6235
|
+
"OpenRouter transcription temperature must be a number between 0 and 1"
|
|
6236
|
+
);
|
|
6237
|
+
if (options.provider !== void 0 && (!options.provider || Array.isArray(options.provider) || typeof options.provider !== "object"))
|
|
6238
|
+
throw new InvalidTranscriptionInputError(
|
|
6239
|
+
"OpenRouter transcription provider routing must be an object"
|
|
6240
|
+
);
|
|
6241
|
+
const format = FORMAT_BY_MIME[request.audio.mediaType];
|
|
6242
|
+
if (!format)
|
|
6243
|
+
throw new UnsupportedTranscriptionInputError({
|
|
6244
|
+
provider: this.name,
|
|
6245
|
+
model: request.model,
|
|
6246
|
+
feature: "this audio media type"
|
|
6247
|
+
});
|
|
6248
|
+
const bytes = inlineAudioBytes(request.audio);
|
|
6249
|
+
const body = {
|
|
6250
|
+
model: request.model,
|
|
6251
|
+
input_audio: { data: Buffer.from(bytes).toString("base64"), format },
|
|
6252
|
+
...request.language ? { language: request.language } : {},
|
|
6253
|
+
...options.temperature !== void 0 ? { temperature: options.temperature } : {},
|
|
6254
|
+
...options.provider !== void 0 ? { provider: options.provider } : {}
|
|
6255
|
+
};
|
|
6256
|
+
const key = await this.resolveKey();
|
|
6257
|
+
const res = await fetchWithRetry(
|
|
6258
|
+
`${this.baseUrl}/audio/transcriptions`,
|
|
6259
|
+
{
|
|
6260
|
+
method: "POST",
|
|
6261
|
+
headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
|
|
6262
|
+
body: JSON.stringify(body),
|
|
6263
|
+
signal: request.signal
|
|
6264
|
+
},
|
|
6265
|
+
{ governor: this.governor, provider: this.name }
|
|
6266
|
+
);
|
|
6267
|
+
if (!res.ok) {
|
|
6268
|
+
const errorBody = await res.text();
|
|
6269
|
+
throw buildProviderError({
|
|
6270
|
+
provider: this.name,
|
|
6271
|
+
status: res.status,
|
|
6272
|
+
headers: res.headers,
|
|
6273
|
+
message: safeProviderMessage("OpenRouter", res.status, errorBody),
|
|
6274
|
+
body: errorBody
|
|
6275
|
+
});
|
|
6276
|
+
}
|
|
6277
|
+
const json = await res.json();
|
|
6278
|
+
if (typeof json.text !== "string")
|
|
6279
|
+
throw new InvalidTranscriptionInputError(
|
|
6280
|
+
"OpenRouter transcription response did not contain text"
|
|
6281
|
+
);
|
|
6282
|
+
return {
|
|
6283
|
+
transcript: { text: json.text, ...transcriptUsage(json.usage) },
|
|
6284
|
+
cleanupStatus: "not_required"
|
|
6285
|
+
};
|
|
6286
|
+
}
|
|
6287
|
+
async resolveKey() {
|
|
6288
|
+
const key = await resolveApiKey(this.apiKeySource);
|
|
6289
|
+
if (!key)
|
|
6290
|
+
throw new Error(
|
|
6291
|
+
"OpenRouter API key is required. Set OPENROUTER_API_KEY or pass apiKey in options."
|
|
6292
|
+
);
|
|
6293
|
+
return key;
|
|
6294
|
+
}
|
|
6295
|
+
};
|
|
6296
|
+
|
|
6297
|
+
// src/providers/transcription-registry.ts
|
|
6298
|
+
var builtinFactories2 = {
|
|
6299
|
+
"openai-transcription": (config) => {
|
|
6300
|
+
const opts = config.providers?.openai ?? {};
|
|
6301
|
+
return new OpenAITranscriptionProvider({
|
|
6302
|
+
apiKey: opts.apiKey,
|
|
6303
|
+
baseUrl: opts.baseUrl,
|
|
6304
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
6305
|
+
rateLimit: opts.rateLimit
|
|
6306
|
+
});
|
|
6307
|
+
},
|
|
6308
|
+
"gemini-transcription": (config) => {
|
|
6309
|
+
const opts = config.providers?.google ?? {};
|
|
6310
|
+
return new GeminiTranscriptionProvider({
|
|
6311
|
+
apiKey: opts.apiKey,
|
|
6312
|
+
baseUrl: opts.baseUrl,
|
|
6313
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
6314
|
+
rateLimit: opts.rateLimit
|
|
6315
|
+
});
|
|
6316
|
+
},
|
|
6317
|
+
"openrouter-transcription": (config) => {
|
|
6318
|
+
const opts = config.providers?.openrouter ?? {};
|
|
6319
|
+
return new OpenRouterTranscriptionProvider({
|
|
6320
|
+
apiKey: opts.apiKey,
|
|
6321
|
+
baseUrl: opts.baseUrl,
|
|
6322
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
6323
|
+
rateLimit: opts.rateLimit
|
|
6324
|
+
});
|
|
6325
|
+
}
|
|
6326
|
+
};
|
|
6327
|
+
var TranscriptionProviderRegistry = class {
|
|
6328
|
+
instances = /* @__PURE__ */ new Map();
|
|
6329
|
+
factories = /* @__PURE__ */ new Map();
|
|
6330
|
+
constructor() {
|
|
6331
|
+
for (const [name, factory] of Object.entries(builtinFactories2))
|
|
6332
|
+
this.factories.set(name, factory);
|
|
6333
|
+
}
|
|
6334
|
+
register(name, factory) {
|
|
6335
|
+
this.factories.set(name, factory);
|
|
6336
|
+
this.instances.delete(name);
|
|
6337
|
+
}
|
|
6338
|
+
registerInstance(name, provider) {
|
|
6339
|
+
this.instances.set(name, provider);
|
|
6340
|
+
}
|
|
6341
|
+
has(name) {
|
|
6342
|
+
return this.instances.has(name) || this.factories.has(name);
|
|
4532
6343
|
}
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
6344
|
+
get(name, config = {}) {
|
|
6345
|
+
const instance = this.instances.get(name);
|
|
6346
|
+
if (instance) return instance;
|
|
6347
|
+
const factory = this.factories.get(name);
|
|
6348
|
+
if (factory) {
|
|
6349
|
+
const created = factory(config);
|
|
6350
|
+
this.instances.set(name, created);
|
|
6351
|
+
return created;
|
|
6352
|
+
}
|
|
6353
|
+
throw new UnsupportedTranscriptionInputError({
|
|
6354
|
+
provider: name,
|
|
6355
|
+
model: "",
|
|
6356
|
+
feature: "transcription"
|
|
6357
|
+
});
|
|
4538
6358
|
}
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
6359
|
+
resolve(uri, config = {}) {
|
|
6360
|
+
const colon = typeof uri === "string" ? uri.indexOf(":") : -1;
|
|
6361
|
+
if (colon <= 0 || colon === uri.length - 1) {
|
|
6362
|
+
throw new UnsupportedTranscriptionInputError({
|
|
6363
|
+
provider: "unknown",
|
|
6364
|
+
model: "",
|
|
6365
|
+
feature: "an explicit transcription provider:model URI"
|
|
6366
|
+
});
|
|
6367
|
+
}
|
|
6368
|
+
const providerName = uri.slice(0, colon);
|
|
6369
|
+
const model = uri.slice(colon + 1);
|
|
6370
|
+
return { provider: this.get(providerName, config), model, providerName };
|
|
4545
6371
|
}
|
|
4546
6372
|
};
|
|
4547
|
-
var defaultRegistry = new ProviderRegistry();
|
|
4548
6373
|
|
|
4549
6374
|
// src/context.ts
|
|
4550
6375
|
import { AsyncLocalStorage } from "async_hooks";
|
|
4551
6376
|
import { randomUUID } from "crypto";
|
|
4552
6377
|
import { z, ZodError as ZodError2 } from "zod";
|
|
4553
6378
|
|
|
6379
|
+
// src/input.ts
|
|
6380
|
+
var MAX_INLINE_MODEL_INPUT_BYTES = 25 * 1024 * 1024;
|
|
6381
|
+
function invalid(message) {
|
|
6382
|
+
throw new InvalidModelInputError(message);
|
|
6383
|
+
}
|
|
6384
|
+
function nonEmptyString2(value, field) {
|
|
6385
|
+
if (typeof value !== "string" || value.length === 0)
|
|
6386
|
+
invalid(`${field} must be a non-empty string`);
|
|
6387
|
+
return value;
|
|
6388
|
+
}
|
|
6389
|
+
function mediaType(value, field) {
|
|
6390
|
+
return nonEmptyString2(value, field);
|
|
6391
|
+
}
|
|
6392
|
+
function validBase64(value) {
|
|
6393
|
+
return value.length > 0 && value.length % 4 === 0 && /^[A-Za-z0-9+/]*={0,2}$/.test(value);
|
|
6394
|
+
}
|
|
6395
|
+
function decodedBase64Bytes(value) {
|
|
6396
|
+
return Math.floor(value.length * 3 / 4) - (value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0);
|
|
6397
|
+
}
|
|
6398
|
+
function normalizeModelInput(input) {
|
|
6399
|
+
if (typeof input === "string") return input;
|
|
6400
|
+
if (!Array.isArray(input) || input.length === 0)
|
|
6401
|
+
invalid("ModelInput parts must be a non-empty array");
|
|
6402
|
+
let inlineBytes = 0;
|
|
6403
|
+
const reserveInlineBytes = (bytes) => {
|
|
6404
|
+
if (bytes > MAX_INLINE_MODEL_INPUT_BYTES - inlineBytes) {
|
|
6405
|
+
invalid(
|
|
6406
|
+
"Inline image data must not exceed 25 MiB total; use a URL or provider-file source where supported"
|
|
6407
|
+
);
|
|
6408
|
+
}
|
|
6409
|
+
inlineBytes += bytes;
|
|
6410
|
+
};
|
|
6411
|
+
return input.map((part, index) => {
|
|
6412
|
+
if (!part || typeof part !== "object") invalid(`ModelInput part ${index} must be an object`);
|
|
6413
|
+
if (part.type === "text")
|
|
6414
|
+
return { type: "text", text: nonEmptyString2(part.text, `part ${index}.text`) };
|
|
6415
|
+
if (part.type !== "image") invalid(`part ${index}.type must be 'text' or 'image'`);
|
|
6416
|
+
const source = part.source;
|
|
6417
|
+
if (!source || typeof source !== "object") invalid(`part ${index}.source must be an object`);
|
|
6418
|
+
let clone;
|
|
6419
|
+
switch (source.type) {
|
|
6420
|
+
case "url": {
|
|
6421
|
+
const url = nonEmptyString2(source.url, `part ${index}.source.url`);
|
|
6422
|
+
let parsed;
|
|
6423
|
+
try {
|
|
6424
|
+
parsed = new URL(url);
|
|
6425
|
+
} catch {
|
|
6426
|
+
invalid(`part ${index}.source.url must be an http(s) URL`);
|
|
6427
|
+
}
|
|
6428
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:")
|
|
6429
|
+
invalid(`part ${index}.source.url must be an http(s) URL`);
|
|
6430
|
+
clone = {
|
|
6431
|
+
type: "url",
|
|
6432
|
+
url,
|
|
6433
|
+
...source.mediaType === void 0 ? {} : { mediaType: mediaType(source.mediaType, `part ${index}.source.mediaType`) }
|
|
6434
|
+
};
|
|
6435
|
+
break;
|
|
6436
|
+
}
|
|
6437
|
+
case "bytes":
|
|
6438
|
+
if (!(source.data instanceof Uint8Array) || source.data.byteLength === 0)
|
|
6439
|
+
invalid(`part ${index}.source.data must be a non-empty Uint8Array`);
|
|
6440
|
+
reserveInlineBytes(source.data.byteLength);
|
|
6441
|
+
clone = {
|
|
6442
|
+
type: "bytes",
|
|
6443
|
+
data: source.data.slice(),
|
|
6444
|
+
mediaType: mediaType(source.mediaType, `part ${index}.source.mediaType`)
|
|
6445
|
+
};
|
|
6446
|
+
break;
|
|
6447
|
+
case "base64": {
|
|
6448
|
+
const data = nonEmptyString2(source.data, `part ${index}.source.data`);
|
|
6449
|
+
if (data.length > 4 * Math.ceil(MAX_INLINE_MODEL_INPUT_BYTES / 3)) {
|
|
6450
|
+
invalid(
|
|
6451
|
+
"Inline image data must not exceed 25 MiB total; use a URL or provider-file source where supported"
|
|
6452
|
+
);
|
|
6453
|
+
}
|
|
6454
|
+
if (!validBase64(data)) invalid(`part ${index}.source.data must be valid base64`);
|
|
6455
|
+
reserveInlineBytes(decodedBase64Bytes(data));
|
|
6456
|
+
clone = {
|
|
6457
|
+
type: "base64",
|
|
6458
|
+
data,
|
|
6459
|
+
mediaType: mediaType(source.mediaType, `part ${index}.source.mediaType`)
|
|
6460
|
+
};
|
|
6461
|
+
break;
|
|
6462
|
+
}
|
|
6463
|
+
case "provider-file":
|
|
6464
|
+
clone = {
|
|
6465
|
+
type: "provider-file",
|
|
6466
|
+
provider: nonEmptyString2(source.provider, `part ${index}.source.provider`),
|
|
6467
|
+
reference: nonEmptyString2(source.reference, `part ${index}.source.reference`),
|
|
6468
|
+
...source.mediaType === void 0 ? {} : { mediaType: mediaType(source.mediaType, `part ${index}.source.mediaType`) }
|
|
6469
|
+
};
|
|
6470
|
+
break;
|
|
6471
|
+
default:
|
|
6472
|
+
invalid(`part ${index}.source.type is unsupported`);
|
|
6473
|
+
}
|
|
6474
|
+
return {
|
|
6475
|
+
type: "image",
|
|
6476
|
+
source: clone,
|
|
6477
|
+
...part.label === void 0 ? {} : { label: nonEmptyString2(part.label, `part ${index}.label`) }
|
|
6478
|
+
};
|
|
6479
|
+
});
|
|
6480
|
+
}
|
|
6481
|
+
function inputText(input) {
|
|
6482
|
+
return typeof input === "string" ? input : input.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
6483
|
+
}
|
|
6484
|
+
function cloneModelInput(input) {
|
|
6485
|
+
return typeof input === "string" ? input : normalizeModelInput(input);
|
|
6486
|
+
}
|
|
6487
|
+
function describeModelInput(input) {
|
|
6488
|
+
if (typeof input === "string") return void 0;
|
|
6489
|
+
return {
|
|
6490
|
+
parts: input.map((part) => {
|
|
6491
|
+
if (part.type === "text") return { type: "text", characters: part.text.length };
|
|
6492
|
+
const { source } = part;
|
|
6493
|
+
return {
|
|
6494
|
+
type: "image",
|
|
6495
|
+
source: source.type,
|
|
6496
|
+
...source.mediaType ? { mediaType: source.mediaType } : {},
|
|
6497
|
+
...source.type === "bytes" ? { bytes: source.data.byteLength } : {},
|
|
6498
|
+
...source.type === "base64" ? {
|
|
6499
|
+
bytes: decodedBase64Bytes(source.data)
|
|
6500
|
+
} : {},
|
|
6501
|
+
...(source.type === "url" ? source.url : source.type === "provider-file" ? source.reference : void 0) ? {
|
|
6502
|
+
locator: source.type === "url" ? source.url : source.type === "provider-file" ? source.reference : void 0
|
|
6503
|
+
} : {},
|
|
6504
|
+
...part.label ? { label: part.label } : {}
|
|
6505
|
+
};
|
|
6506
|
+
})
|
|
6507
|
+
};
|
|
6508
|
+
}
|
|
6509
|
+
function summarizeModelInput(input) {
|
|
6510
|
+
if (typeof input === "string") return input;
|
|
6511
|
+
return input.map(
|
|
6512
|
+
(part) => part.type === "text" ? part.text : `[image ${part.source.mediaType ?? "media"}]`
|
|
6513
|
+
).join("\n");
|
|
6514
|
+
}
|
|
6515
|
+
function sanitizeModelInputForTrace(input) {
|
|
6516
|
+
return inputText(input);
|
|
6517
|
+
}
|
|
6518
|
+
function normalizePersistedSessionHistory(history) {
|
|
6519
|
+
return history.map((message, index) => {
|
|
6520
|
+
if (typeof message.content === "string") return { ...message };
|
|
6521
|
+
if (message.role !== "user") {
|
|
6522
|
+
throw new InvalidModelInputError(
|
|
6523
|
+
`History message ${index} has non-text content on a non-user role`
|
|
6524
|
+
);
|
|
6525
|
+
}
|
|
6526
|
+
const content = normalizeModelInput(message.content);
|
|
6527
|
+
if (typeof content !== "string" && content.some((part) => part.type === "image" && part.source.type === "bytes")) {
|
|
6528
|
+
throw new InvalidModelInputError(
|
|
6529
|
+
"Uint8Array image input cannot be persisted in session history"
|
|
6530
|
+
);
|
|
6531
|
+
}
|
|
6532
|
+
return { ...message, content };
|
|
6533
|
+
});
|
|
6534
|
+
}
|
|
6535
|
+
|
|
4554
6536
|
// src/partial-json.ts
|
|
4555
6537
|
var MAX_DEPTH = 256;
|
|
4556
6538
|
function parsePartialJson(input) {
|
|
@@ -5122,7 +7104,8 @@ var COST_BEARING_LEAF_TYPES = [
|
|
|
5122
7104
|
"agent_call_end",
|
|
5123
7105
|
"tool_call_end",
|
|
5124
7106
|
"memory_remember",
|
|
5125
|
-
"memory_recall"
|
|
7107
|
+
"memory_recall",
|
|
7108
|
+
"transcription_end"
|
|
5126
7109
|
];
|
|
5127
7110
|
var COST_LEAF_SET = new Set(COST_BEARING_LEAF_TYPES);
|
|
5128
7111
|
function hasPositiveTokens(event) {
|
|
@@ -5130,6 +7113,12 @@ function hasPositiveTokens(event) {
|
|
|
5130
7113
|
if (!t) return false;
|
|
5131
7114
|
return (t.input ?? 0) + (t.output ?? 0) + (t.reasoning ?? 0) + (t.cached ?? 0) + (t.cacheWrite ?? 0) > 0;
|
|
5132
7115
|
}
|
|
7116
|
+
function hasPositiveBillableWork(event) {
|
|
7117
|
+
if (hasPositiveTokens(event)) return true;
|
|
7118
|
+
const seconds = event.data?.usage?.audioSeconds;
|
|
7119
|
+
const totalTokens = event.data?.usage?.totalTokens;
|
|
7120
|
+
return typeof seconds === "number" && Number.isFinite(seconds) && seconds > 0 || typeof totalTokens === "number" && Number.isFinite(totalTokens) && totalTokens > 0;
|
|
7121
|
+
}
|
|
5133
7122
|
function isUsableCost(cost) {
|
|
5134
7123
|
return typeof cost === "number" && Number.isFinite(cost) && cost >= 0;
|
|
5135
7124
|
}
|
|
@@ -5142,7 +7131,7 @@ function isCostBearingLeaf(event) {
|
|
|
5142
7131
|
return COST_LEAF_SET.has(event.type);
|
|
5143
7132
|
}
|
|
5144
7133
|
function isUnpricedLeaf(event) {
|
|
5145
|
-
return event.type !== void 0 && COST_LEAF_SET.has(event.type) && !isUsableCost(event.cost) &&
|
|
7134
|
+
return event.type !== void 0 && COST_LEAF_SET.has(event.type) && !isUsableCost(event.cost) && hasPositiveBillableWork(event);
|
|
5146
7135
|
}
|
|
5147
7136
|
function isRootLevel(event) {
|
|
5148
7137
|
const d = event.depth;
|
|
@@ -5157,6 +7146,9 @@ var AXL_EVENT_TYPES = [
|
|
|
5157
7146
|
// Workflow lifecycle
|
|
5158
7147
|
"workflow_start",
|
|
5159
7148
|
"workflow_end",
|
|
7149
|
+
// Transcription operation lifecycle (separate from chat/agent turns)
|
|
7150
|
+
"transcription_start",
|
|
7151
|
+
"transcription_end",
|
|
5160
7152
|
// Ask boundary
|
|
5161
7153
|
"ask_start",
|
|
5162
7154
|
"ask_end",
|
|
@@ -5247,6 +7239,8 @@ var LIFECYCLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
5247
7239
|
"schema_diagnostic",
|
|
5248
7240
|
"workflow_start",
|
|
5249
7241
|
"workflow_end",
|
|
7242
|
+
"transcription_start",
|
|
7243
|
+
"transcription_end",
|
|
5250
7244
|
"checkpoint_save",
|
|
5251
7245
|
"checkpoint_replay",
|
|
5252
7246
|
"await_human",
|
|
@@ -6062,6 +8056,21 @@ function redactGate(event) {
|
|
|
6062
8056
|
}
|
|
6063
8057
|
};
|
|
6064
8058
|
}
|
|
8059
|
+
function redactInputDescriptor(input) {
|
|
8060
|
+
return {
|
|
8061
|
+
...input,
|
|
8062
|
+
parts: input.parts.map((part) => {
|
|
8063
|
+
if (!part || typeof part !== "object" || !("type" in part) || part.type === "text")
|
|
8064
|
+
return part;
|
|
8065
|
+
const value = part;
|
|
8066
|
+
return {
|
|
8067
|
+
...part,
|
|
8068
|
+
...value.locator !== void 0 ? { locator: REDACTED } : {},
|
|
8069
|
+
...value.label !== void 0 ? { label: REDACTED } : {}
|
|
8070
|
+
};
|
|
8071
|
+
})
|
|
8072
|
+
};
|
|
8073
|
+
}
|
|
6065
8074
|
function redactMemory(event) {
|
|
6066
8075
|
const d = event.data;
|
|
6067
8076
|
if (!d || typeof d !== "object") return event;
|
|
@@ -6085,7 +8094,11 @@ var LEGACY_REDACTION_RULES = {
|
|
|
6085
8094
|
}
|
|
6086
8095
|
};
|
|
6087
8096
|
},
|
|
6088
|
-
ask_start: (e) => ({
|
|
8097
|
+
ask_start: (e) => ({
|
|
8098
|
+
...e,
|
|
8099
|
+
prompt: REDACTED,
|
|
8100
|
+
...e.input ? { input: redactInputDescriptor(e.input) } : {}
|
|
8101
|
+
}),
|
|
6089
8102
|
ask_end: (e) => ({
|
|
6090
8103
|
...e,
|
|
6091
8104
|
outcome: e.outcome.ok ? { ok: true, result: REDACTED } : { ok: false, error: REDACTED }
|
|
@@ -6093,6 +8106,14 @@ var LEGACY_REDACTION_RULES = {
|
|
|
6093
8106
|
agent_call_start: (e) => {
|
|
6094
8107
|
const d = e.data;
|
|
6095
8108
|
const out = { ...d, prompt: REDACTED };
|
|
8109
|
+
if (d.input) {
|
|
8110
|
+
out.input = redactInputDescriptor(d.input);
|
|
8111
|
+
}
|
|
8112
|
+
if (d.messageInputs)
|
|
8113
|
+
out.messageInputs = d.messageInputs.map((entry) => ({
|
|
8114
|
+
...entry,
|
|
8115
|
+
input: redactInputDescriptor(entry.input)
|
|
8116
|
+
}));
|
|
6096
8117
|
if (d.system !== void 0) out.system = REDACTED;
|
|
6097
8118
|
if (Array.isArray(d.messages)) {
|
|
6098
8119
|
out.messages = [{ role: "system", content: `[${d.messages.length} messages redacted]` }];
|
|
@@ -6248,6 +8269,18 @@ function reuseLegacyRule(type) {
|
|
|
6248
8269
|
var COMMON_V2_REDACTION_RULES = {
|
|
6249
8270
|
workflow_start: reuseLegacyRule("workflow_start"),
|
|
6250
8271
|
workflow_end: reuseLegacyRule("workflow_end"),
|
|
8272
|
+
transcription_start: (e) => e,
|
|
8273
|
+
transcription_end: (e) => {
|
|
8274
|
+
const d = e.data;
|
|
8275
|
+
return {
|
|
8276
|
+
...e,
|
|
8277
|
+
data: {
|
|
8278
|
+
...d,
|
|
8279
|
+
...d.text !== void 0 ? { text: REDACTED } : {},
|
|
8280
|
+
...d.error !== void 0 ? { error: REDACTED } : {}
|
|
8281
|
+
}
|
|
8282
|
+
};
|
|
8283
|
+
},
|
|
6251
8284
|
ask_start: reuseLegacyRule("ask_start"),
|
|
6252
8285
|
ask_end: reuseLegacyRule("ask_end"),
|
|
6253
8286
|
agent_call_start: reuseLegacyRule("agent_call_start"),
|
|
@@ -6442,7 +8475,7 @@ function resolveConfig(config) {
|
|
|
6442
8475
|
|
|
6443
8476
|
// src/tool-model-output.ts
|
|
6444
8477
|
import { types as nodeTypes } from "util";
|
|
6445
|
-
function
|
|
8478
|
+
function invalid2(path, reason) {
|
|
6446
8479
|
return new Error(`Invalid tool model output at ${path}: ${reason}`);
|
|
6447
8480
|
}
|
|
6448
8481
|
function propertyPath(parent, key) {
|
|
@@ -6460,14 +8493,14 @@ function propertyPath(parent, key) {
|
|
|
6460
8493
|
function normalize(value, path, stack) {
|
|
6461
8494
|
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
6462
8495
|
if (typeof value === "number") {
|
|
6463
|
-
if (!Number.isFinite(value)) throw
|
|
8496
|
+
if (!Number.isFinite(value)) throw invalid2(path, "number must be finite");
|
|
6464
8497
|
return value;
|
|
6465
8498
|
}
|
|
6466
|
-
if (value === void 0) throw
|
|
8499
|
+
if (value === void 0) throw invalid2(path, "undefined is only allowed on object properties");
|
|
6467
8500
|
if (typeof value === "bigint" || typeof value === "function" || typeof value === "symbol") {
|
|
6468
|
-
throw
|
|
8501
|
+
throw invalid2(path, `unsupported ${typeof value}`);
|
|
6469
8502
|
}
|
|
6470
|
-
if (typeof value !== "object") throw
|
|
8503
|
+
if (typeof value !== "object") throw invalid2(path, `unsupported ${typeof value}`);
|
|
6471
8504
|
let prototype;
|
|
6472
8505
|
let descriptors;
|
|
6473
8506
|
try {
|
|
@@ -6475,38 +8508,38 @@ function normalize(value, path, stack) {
|
|
|
6475
8508
|
descriptors = Object.getOwnPropertyDescriptors(value);
|
|
6476
8509
|
Object.setPrototypeOf(descriptors, null);
|
|
6477
8510
|
} catch (cause) {
|
|
6478
|
-
throw
|
|
8511
|
+
throw invalid2(
|
|
6479
8512
|
path,
|
|
6480
8513
|
`object inspection failed (${cause instanceof Error ? cause.name : "error"})`
|
|
6481
8514
|
);
|
|
6482
8515
|
}
|
|
6483
|
-
if (stack.has(value)) throw
|
|
8516
|
+
if (stack.has(value)) throw invalid2(path, "cyclic reference");
|
|
6484
8517
|
for (const key of Reflect.ownKeys(descriptors)) {
|
|
6485
8518
|
const descriptor = descriptors[key];
|
|
6486
8519
|
if (descriptor && !("value" in descriptor)) {
|
|
6487
|
-
throw
|
|
8520
|
+
throw invalid2(
|
|
6488
8521
|
typeof key === "string" ? propertyPath(path, key) : `${path}[symbol]`,
|
|
6489
8522
|
"accessor properties are not supported"
|
|
6490
8523
|
);
|
|
6491
8524
|
}
|
|
6492
8525
|
if (typeof key === "symbol") {
|
|
6493
|
-
throw
|
|
8526
|
+
throw invalid2(`${path}[symbol]`, "symbol keys are not supported");
|
|
6494
8527
|
}
|
|
6495
8528
|
}
|
|
6496
8529
|
if (typeof descriptors.toJSON?.value === "function") {
|
|
6497
|
-
throw
|
|
8530
|
+
throw invalid2(propertyPath(path, "toJSON"), "custom toJSON is not supported");
|
|
6498
8531
|
}
|
|
6499
8532
|
const array = Array.isArray(value);
|
|
6500
8533
|
if (array && prototype !== Array.prototype && prototype !== null) {
|
|
6501
|
-
throw
|
|
8534
|
+
throw invalid2(path, "array subclasses are not supported");
|
|
6502
8535
|
}
|
|
6503
8536
|
const inheritedToJSON = prototype === Array.prototype || prototype === Object.prototype ? Object.getOwnPropertyDescriptor(prototype, "toJSON") : void 0;
|
|
6504
8537
|
if (typeof inheritedToJSON?.value === "function") {
|
|
6505
|
-
throw
|
|
8538
|
+
throw invalid2(propertyPath(path, "toJSON"), "inherited custom toJSON is not supported");
|
|
6506
8539
|
}
|
|
6507
8540
|
const inheritedThen = prototype === Array.prototype || prototype === Object.prototype ? Object.getOwnPropertyDescriptor(prototype, "then") : void 0;
|
|
6508
8541
|
if (typeof descriptors.then?.value === "function" || inheritedThen !== void 0 && (!("value" in inheritedThen) || typeof inheritedThen.value === "function")) {
|
|
6509
|
-
throw
|
|
8542
|
+
throw invalid2(path, "thenable output is not supported");
|
|
6510
8543
|
}
|
|
6511
8544
|
stack.add(value);
|
|
6512
8545
|
try {
|
|
@@ -6514,15 +8547,15 @@ function normalize(value, path, stack) {
|
|
|
6514
8547
|
const normalized2 = [];
|
|
6515
8548
|
const length = descriptors.length?.value;
|
|
6516
8549
|
if (!Number.isSafeInteger(length) || length < 0) {
|
|
6517
|
-
throw
|
|
8550
|
+
throw invalid2(propertyPath(path, "length"), "array length is invalid");
|
|
6518
8551
|
}
|
|
6519
8552
|
for (let index = 0; index < length; index++) {
|
|
6520
8553
|
const descriptor = descriptors[String(index)];
|
|
6521
8554
|
if (!descriptor || !("value" in descriptor)) {
|
|
6522
|
-
throw
|
|
8555
|
+
throw invalid2(`${path}[${index}]`, "arrays must be dense data properties");
|
|
6523
8556
|
}
|
|
6524
8557
|
if (descriptor.value === void 0) {
|
|
6525
|
-
throw
|
|
8558
|
+
throw invalid2(`${path}[${index}]`, "array values cannot be undefined");
|
|
6526
8559
|
}
|
|
6527
8560
|
normalized2.push(normalize(descriptor.value, `${path}[${index}]`, stack));
|
|
6528
8561
|
}
|
|
@@ -6531,21 +8564,21 @@ function normalize(value, path, stack) {
|
|
|
6531
8564
|
if (!descriptor?.enumerable) continue;
|
|
6532
8565
|
const index = Number(key);
|
|
6533
8566
|
if (!Number.isInteger(index) || index < 0 || index >= length || String(index) !== key) {
|
|
6534
|
-
throw
|
|
8567
|
+
throw invalid2(propertyPath(path, key), "non-index array properties are not supported");
|
|
6535
8568
|
}
|
|
6536
8569
|
}
|
|
6537
8570
|
Object.setPrototypeOf(normalized2, null);
|
|
6538
8571
|
return normalized2;
|
|
6539
8572
|
}
|
|
6540
8573
|
if (prototype !== Object.prototype && prototype !== null) {
|
|
6541
|
-
throw
|
|
8574
|
+
throw invalid2(path, "only plain objects and null-prototype records are supported");
|
|
6542
8575
|
}
|
|
6543
8576
|
const normalized = /* @__PURE__ */ Object.create(null);
|
|
6544
8577
|
for (const key of Object.keys(descriptors)) {
|
|
6545
8578
|
const descriptor = descriptors[key];
|
|
6546
8579
|
if (!descriptor?.enumerable) continue;
|
|
6547
8580
|
if (!("value" in descriptor)) {
|
|
6548
|
-
throw
|
|
8581
|
+
throw invalid2(propertyPath(path, key), "accessor properties are not supported");
|
|
6549
8582
|
}
|
|
6550
8583
|
if (descriptor.value === void 0) continue;
|
|
6551
8584
|
normalized[key] = normalize(descriptor.value, propertyPath(path, key), stack);
|
|
@@ -7053,44 +9086,44 @@ function resolveBranchDrainTimeoutMs(value) {
|
|
|
7053
9086
|
return resolved;
|
|
7054
9087
|
}
|
|
7055
9088
|
function parseHumanDecision(value) {
|
|
7056
|
-
const
|
|
9089
|
+
const invalid3 = () => {
|
|
7057
9090
|
throw new AxlError(
|
|
7058
9091
|
"INVALID_HUMAN_DECISION",
|
|
7059
9092
|
"Human decision must be a plain object with a boolean approved field and only branch-appropriate string data"
|
|
7060
9093
|
);
|
|
7061
9094
|
};
|
|
7062
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
9095
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) invalid3();
|
|
7063
9096
|
let prototype;
|
|
7064
9097
|
try {
|
|
7065
9098
|
prototype = Object.getPrototypeOf(value);
|
|
7066
9099
|
} catch {
|
|
7067
|
-
return
|
|
9100
|
+
return invalid3();
|
|
7068
9101
|
}
|
|
7069
|
-
if (prototype !== Object.prototype && prototype !== null)
|
|
9102
|
+
if (prototype !== Object.prototype && prototype !== null) invalid3();
|
|
7070
9103
|
let ownKeys;
|
|
7071
9104
|
try {
|
|
7072
9105
|
ownKeys = Reflect.ownKeys(value);
|
|
7073
9106
|
} catch {
|
|
7074
|
-
return
|
|
9107
|
+
return invalid3();
|
|
7075
9108
|
}
|
|
7076
9109
|
if (ownKeys.some(
|
|
7077
9110
|
(key) => typeof key !== "string" || key !== "approved" && key !== "data" && key !== "reason"
|
|
7078
9111
|
)) {
|
|
7079
|
-
|
|
9112
|
+
invalid3();
|
|
7080
9113
|
}
|
|
7081
9114
|
const readDataProperty = (key) => {
|
|
7082
9115
|
let descriptor;
|
|
7083
9116
|
try {
|
|
7084
9117
|
descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
7085
9118
|
} catch {
|
|
7086
|
-
return
|
|
9119
|
+
return invalid3();
|
|
7087
9120
|
}
|
|
7088
9121
|
if (!descriptor) {
|
|
7089
|
-
if (ownKeys.includes(key))
|
|
9122
|
+
if (ownKeys.includes(key)) invalid3();
|
|
7090
9123
|
return { present: false, value: void 0 };
|
|
7091
9124
|
}
|
|
7092
|
-
if (!ownKeys.includes(key)) return
|
|
7093
|
-
if (!("value" in descriptor)) return
|
|
9125
|
+
if (!ownKeys.includes(key)) return invalid3();
|
|
9126
|
+
if (!("value" in descriptor)) return invalid3();
|
|
7094
9127
|
return { present: true, value: descriptor.value };
|
|
7095
9128
|
};
|
|
7096
9129
|
const approved = readDataProperty("approved");
|
|
@@ -7109,7 +9142,7 @@ function parseHumanDecision(value) {
|
|
|
7109
9142
|
};
|
|
7110
9143
|
}
|
|
7111
9144
|
}
|
|
7112
|
-
return
|
|
9145
|
+
return invalid3();
|
|
7113
9146
|
}
|
|
7114
9147
|
var askStorage = new AsyncLocalStorage();
|
|
7115
9148
|
var inlineSchemaCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -7217,8 +9250,12 @@ function appendRetryMessages(messages, content, feedbackMessage, providerMetadat
|
|
|
7217
9250
|
}
|
|
7218
9251
|
function estimateMessagesTokens(messages) {
|
|
7219
9252
|
let total = 0;
|
|
9253
|
+
let unmeasured = false;
|
|
7220
9254
|
for (const msg of messages) {
|
|
7221
|
-
total += estimateTokens(msg.content);
|
|
9255
|
+
total += estimateTokens(inputText(msg.content));
|
|
9256
|
+
if (typeof msg.content !== "string") {
|
|
9257
|
+
unmeasured ||= msg.content.some((part) => part.type === "image");
|
|
9258
|
+
}
|
|
7222
9259
|
if (msg.tool_calls) {
|
|
7223
9260
|
for (const tc of msg.tool_calls) {
|
|
7224
9261
|
total += estimateTokens(tc.function.name + tc.function.arguments);
|
|
@@ -7226,7 +9263,73 @@ function estimateMessagesTokens(messages) {
|
|
|
7226
9263
|
}
|
|
7227
9264
|
total += 4;
|
|
7228
9265
|
}
|
|
7229
|
-
return total;
|
|
9266
|
+
return { tokens: total, unmeasured };
|
|
9267
|
+
}
|
|
9268
|
+
function cloneMessagesForProvider(messages) {
|
|
9269
|
+
return messages.map((message) => ({
|
|
9270
|
+
...message,
|
|
9271
|
+
content: cloneModelInput(message.content),
|
|
9272
|
+
...message.tool_calls ? {
|
|
9273
|
+
tool_calls: message.tool_calls.map((call) => ({
|
|
9274
|
+
...call,
|
|
9275
|
+
function: { ...call.function }
|
|
9276
|
+
}))
|
|
9277
|
+
} : {}
|
|
9278
|
+
}));
|
|
9279
|
+
}
|
|
9280
|
+
function assertRichMessagesAreUserOnly(messages) {
|
|
9281
|
+
for (const message of messages) {
|
|
9282
|
+
if (typeof message.content !== "string" && message.role !== "user") {
|
|
9283
|
+
throw new InvalidModelInputError(
|
|
9284
|
+
"Non-text model input parts are only supported on user messages"
|
|
9285
|
+
);
|
|
9286
|
+
}
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
9289
|
+
function normalizeSessionHistory(messages) {
|
|
9290
|
+
return messages.map((message) => {
|
|
9291
|
+
if (typeof message.content === "string") return { ...message };
|
|
9292
|
+
if (message.role !== "user") {
|
|
9293
|
+
throw new InvalidModelInputError(
|
|
9294
|
+
"Non-text model input parts are only supported on user messages"
|
|
9295
|
+
);
|
|
9296
|
+
}
|
|
9297
|
+
return { ...message, content: normalizeModelInput(message.content) };
|
|
9298
|
+
});
|
|
9299
|
+
}
|
|
9300
|
+
function hasRichMessage(messages) {
|
|
9301
|
+
return messages.some((message) => typeof message.content !== "string");
|
|
9302
|
+
}
|
|
9303
|
+
function firstImageSource(input, history) {
|
|
9304
|
+
const find = (value) => typeof value === "string" ? void 0 : value.find((part) => part.type === "image")?.source.type;
|
|
9305
|
+
return find(input) ?? history.map((message) => find(message.content)).find(Boolean);
|
|
9306
|
+
}
|
|
9307
|
+
function appendHandoffInstruction(input, originalText, instruction) {
|
|
9308
|
+
if (typeof input === "string" || instruction.length === 0 || instruction === originalText)
|
|
9309
|
+
return input;
|
|
9310
|
+
return [...input, { type: "text", text: instruction }];
|
|
9311
|
+
}
|
|
9312
|
+
var RICH_MODEL_INPUT_ERROR_MESSAGE = "Provider request failed while processing rich model input";
|
|
9313
|
+
var RICH_MODEL_INPUT_ERROR_NAME = "RichModelInputError";
|
|
9314
|
+
var MAX_RICH_FAILURE_ASSOCIATIONS = 32;
|
|
9315
|
+
var MAX_RICH_FAILURE_CAUSE_DEPTH = 4;
|
|
9316
|
+
function legacyErrorMessage(error) {
|
|
9317
|
+
return error instanceof Error ? error.message : String(error);
|
|
9318
|
+
}
|
|
9319
|
+
function providerErrorEventMetadata(error, richRequest) {
|
|
9320
|
+
if (!(error instanceof ProviderError)) {
|
|
9321
|
+
return error instanceof AxlError && richRequest ? { code: error.code } : {};
|
|
9322
|
+
}
|
|
9323
|
+
return {
|
|
9324
|
+
status: error.status,
|
|
9325
|
+
retryable: error.retryable,
|
|
9326
|
+
...richRequest ? {
|
|
9327
|
+
code: error.code,
|
|
9328
|
+
provider: error.provider,
|
|
9329
|
+
...error.requestId ? { requestId: error.requestId } : {},
|
|
9330
|
+
...error.retryAfterMs !== void 0 ? { retryAfterMs: error.retryAfterMs } : {}
|
|
9331
|
+
} : {}
|
|
9332
|
+
};
|
|
7230
9333
|
}
|
|
7231
9334
|
var WorkflowContext = class _WorkflowContext {
|
|
7232
9335
|
input;
|
|
@@ -7242,6 +9345,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7242
9345
|
}
|
|
7243
9346
|
config;
|
|
7244
9347
|
providerRegistry;
|
|
9348
|
+
transcriptionProviderRegistry;
|
|
7245
9349
|
sessionHistory;
|
|
7246
9350
|
onTrace;
|
|
7247
9351
|
pendingDecisions;
|
|
@@ -7267,6 +9371,10 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7267
9371
|
*/
|
|
7268
9372
|
autoCheckpointCounters;
|
|
7269
9373
|
branchDrainState;
|
|
9374
|
+
/** Bounded exact-identity failure association for this execution tree.
|
|
9375
|
+
* Unlike a process-global marker, this cannot affect another context that
|
|
9376
|
+
* happens to reuse the same Error object. */
|
|
9377
|
+
richFailureState;
|
|
7270
9378
|
/** Root-shared idempotency guards for workflow lifecycle emission.
|
|
7271
9379
|
* Both `runtime.execute()` and `runtime.stream()` have paths where a
|
|
7272
9380
|
* post-emit side-effect (checkpoint deletion, state-store persistence)
|
|
@@ -7350,7 +9458,8 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7350
9458
|
get events() {
|
|
7351
9459
|
if (!this._busRef.current) {
|
|
7352
9460
|
this._busRef.current = new AxlEventBus(this._eventStreamOptions);
|
|
7353
|
-
|
|
9461
|
+
const signal = this.currentSignal;
|
|
9462
|
+
if (signal?.aborted) {
|
|
7354
9463
|
this._busRef.current._finish();
|
|
7355
9464
|
}
|
|
7356
9465
|
}
|
|
@@ -7388,6 +9497,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7388
9497
|
this.metadata = init.metadata ?? {};
|
|
7389
9498
|
this.config = init.config;
|
|
7390
9499
|
this.providerRegistry = init.providerRegistry;
|
|
9500
|
+
this.transcriptionProviderRegistry = init.transcriptionProviderRegistry;
|
|
7391
9501
|
this.sessionHistory = init.sessionHistory ?? [];
|
|
7392
9502
|
this.onTrace = init.onTrace;
|
|
7393
9503
|
this.pendingDecisions = init.pendingDecisions;
|
|
@@ -7421,6 +9531,11 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7421
9531
|
byAgent: /* @__PURE__ */ new Map(),
|
|
7422
9532
|
root: 0
|
|
7423
9533
|
};
|
|
9534
|
+
this.richFailureState = init.richFailureState ?? {
|
|
9535
|
+
associations: [],
|
|
9536
|
+
overflowed: false,
|
|
9537
|
+
indeterminate: false
|
|
9538
|
+
};
|
|
7424
9539
|
const branchDrainTimeoutMs = resolveBranchDrainTimeoutMs(init.branchDrainTimeoutMs);
|
|
7425
9540
|
this.branchDrainState = init.branchDrainState ?? {
|
|
7426
9541
|
pending: /* @__PURE__ */ new Set(),
|
|
@@ -7453,6 +9568,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7453
9568
|
executionId: this.executionId,
|
|
7454
9569
|
config: this.config,
|
|
7455
9570
|
providerRegistry: this.providerRegistry,
|
|
9571
|
+
transcriptionProviderRegistry: this.transcriptionProviderRegistry,
|
|
7456
9572
|
metadata: { ...this.metadata },
|
|
7457
9573
|
// Shared infrastructure
|
|
7458
9574
|
budgetContext: this.budgetContext,
|
|
@@ -7475,6 +9591,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7475
9591
|
autoCheckpointCounters: this.autoCheckpointCounters,
|
|
7476
9592
|
branchDrainState: this.branchDrainState,
|
|
7477
9593
|
workflowLifecycleState: this.workflowLifecycleState,
|
|
9594
|
+
richFailureState: this.richFailureState,
|
|
7478
9595
|
// Share the parent's bus slot (mutable ref). Late allocation in
|
|
7479
9596
|
// either parent or child propagates because both read the same
|
|
7480
9597
|
// slot. Nested-ask events (agent-as-tool pattern) bubble up to
|
|
@@ -7486,6 +9603,52 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7486
9603
|
// Isolated: sessionHistory (empty)
|
|
7487
9604
|
});
|
|
7488
9605
|
}
|
|
9606
|
+
/** Associate an exact thrown value with rich input in this execution only. */
|
|
9607
|
+
recordRichFailure(error) {
|
|
9608
|
+
const state = this.richFailureState;
|
|
9609
|
+
if (state.associations.some((known) => Object.is(known, error))) return;
|
|
9610
|
+
if (state.associations.length >= MAX_RICH_FAILURE_ASSOCIATIONS) {
|
|
9611
|
+
state.associations.shift();
|
|
9612
|
+
state.overflowed = true;
|
|
9613
|
+
state.indeterminate = true;
|
|
9614
|
+
}
|
|
9615
|
+
state.associations.push(error);
|
|
9616
|
+
}
|
|
9617
|
+
/** @internal Safe observer projection for a failure associated with rich input. */
|
|
9618
|
+
_observerErrorProjection(error) {
|
|
9619
|
+
const state = this.richFailureState;
|
|
9620
|
+
const safe = () => ({
|
|
9621
|
+
message: RICH_MODEL_INPUT_ERROR_MESSAGE,
|
|
9622
|
+
name: RICH_MODEL_INPUT_ERROR_NAME
|
|
9623
|
+
});
|
|
9624
|
+
const indeterminate = () => {
|
|
9625
|
+
if (state.associations.length === 0) return void 0;
|
|
9626
|
+
state.indeterminate = true;
|
|
9627
|
+
return safe();
|
|
9628
|
+
};
|
|
9629
|
+
let current = error;
|
|
9630
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
9631
|
+
for (let depth = 0; depth < MAX_RICH_FAILURE_CAUSE_DEPTH; depth++) {
|
|
9632
|
+
if (state.associations.some((known) => Object.is(known, current))) {
|
|
9633
|
+
return safe();
|
|
9634
|
+
}
|
|
9635
|
+
if ((typeof current !== "object" || current === null) && typeof current !== "function") {
|
|
9636
|
+
return state.overflowed || state.indeterminate ? safe() : void 0;
|
|
9637
|
+
}
|
|
9638
|
+
if (seen.has(current)) return indeterminate();
|
|
9639
|
+
seen.add(current);
|
|
9640
|
+
let descriptor;
|
|
9641
|
+
try {
|
|
9642
|
+
descriptor = Object.getOwnPropertyDescriptor(current, "cause");
|
|
9643
|
+
} catch {
|
|
9644
|
+
return indeterminate();
|
|
9645
|
+
}
|
|
9646
|
+
if (!descriptor) return state.overflowed || state.indeterminate ? safe() : void 0;
|
|
9647
|
+
if (!("value" in descriptor)) return indeterminate();
|
|
9648
|
+
current = descriptor.value;
|
|
9649
|
+
}
|
|
9650
|
+
return indeterminate();
|
|
9651
|
+
}
|
|
7489
9652
|
/**
|
|
7490
9653
|
* Resolve the current abort signal.
|
|
7491
9654
|
* Branch-scoped signals (from race/spawn/map/budget) in AsyncLocalStorage
|
|
@@ -7687,6 +9850,17 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7687
9850
|
}
|
|
7688
9851
|
// ── ctx.ask() ─────────────────────────────────────────────────────────
|
|
7689
9852
|
async ask(agent2, prompt, options) {
|
|
9853
|
+
const normalizedInput = normalizeModelInput(prompt);
|
|
9854
|
+
const normalizedHistory = normalizeSessionHistory(this.sessionHistory);
|
|
9855
|
+
const delegateInputHolder = agent2;
|
|
9856
|
+
if (delegateInputHolder._delegateOriginalInput !== void 0) {
|
|
9857
|
+
delegateInputHolder._delegateOriginalInput = normalizeModelInput(
|
|
9858
|
+
delegateInputHolder._delegateOriginalInput
|
|
9859
|
+
);
|
|
9860
|
+
}
|
|
9861
|
+
const richAsk = typeof normalizedInput !== "string" || hasRichMessage(normalizedHistory) || delegateInputHolder._delegateOriginalInput !== void 0 && typeof delegateInputHolder._delegateOriginalInput !== "string";
|
|
9862
|
+
const promptText = inputText(normalizedInput);
|
|
9863
|
+
const inputDescriptor = describeModelInput(normalizedInput);
|
|
7690
9864
|
const agentName = agent2._name;
|
|
7691
9865
|
return this._checkpoint(
|
|
7692
9866
|
this._autoCheckpointName("ask", agentName),
|
|
@@ -7706,7 +9880,11 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7706
9880
|
};
|
|
7707
9881
|
return askStorage.run(frame, async () => {
|
|
7708
9882
|
const askStart = Date.now();
|
|
7709
|
-
this.emitEvent({
|
|
9883
|
+
this.emitEvent({
|
|
9884
|
+
type: "ask_start",
|
|
9885
|
+
prompt: promptText,
|
|
9886
|
+
...inputDescriptor ? { input: inputDescriptor } : {}
|
|
9887
|
+
});
|
|
7710
9888
|
const costBefore = this.budgetContext?.totalCost ?? 0;
|
|
7711
9889
|
const unpricedCountBefore = this.budgetContext?.unpricedCount ?? 0;
|
|
7712
9890
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
@@ -7714,10 +9892,11 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7714
9892
|
const doCall = async () => {
|
|
7715
9893
|
const result2 = await this.executeAgentCall(
|
|
7716
9894
|
agent2,
|
|
7717
|
-
|
|
9895
|
+
normalizedInput,
|
|
7718
9896
|
options,
|
|
7719
9897
|
void 0,
|
|
7720
|
-
usageCapture
|
|
9898
|
+
usageCapture,
|
|
9899
|
+
normalizedHistory
|
|
7721
9900
|
);
|
|
7722
9901
|
return result2;
|
|
7723
9902
|
};
|
|
@@ -7734,6 +9913,9 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7734
9913
|
},
|
|
7735
9914
|
async (span) => {
|
|
7736
9915
|
const r = await doCall();
|
|
9916
|
+
if (usageCapture.modelUri) {
|
|
9917
|
+
span.setAttribute("axl.agent.model", usageCapture.modelUri);
|
|
9918
|
+
}
|
|
7737
9919
|
const costAfter2 = this.budgetContext?.totalCost ?? 0;
|
|
7738
9920
|
span.setAttribute("axl.agent.cost", costAfter2 - costBefore);
|
|
7739
9921
|
span.setAttribute("axl.agent.duration", Date.now() - askStart);
|
|
@@ -7767,9 +9949,10 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7767
9949
|
try {
|
|
7768
9950
|
this.onAgentCallComplete({
|
|
7769
9951
|
agent: agent2._name,
|
|
7770
|
-
prompt,
|
|
9952
|
+
prompt: promptText,
|
|
9953
|
+
...inputDescriptor ? { input: inputDescriptor } : {},
|
|
7771
9954
|
response: typeof result === "string" ? result : JSON.stringify(result),
|
|
7772
|
-
model: agent2.resolveModel(resolveCtx),
|
|
9955
|
+
model: usageCapture.modelUri ?? agent2.resolveModel(resolveCtx),
|
|
7773
9956
|
cost: costAfter - costBefore,
|
|
7774
9957
|
unpriced: frame.askUnpriced || unpricedCountAfter > unpricedCountBefore,
|
|
7775
9958
|
duration: Date.now() - askStart,
|
|
@@ -7793,9 +9976,10 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7793
9976
|
} catch (err) {
|
|
7794
9977
|
askFailed = true;
|
|
7795
9978
|
askFailure = err;
|
|
9979
|
+
if (richAsk) this.recordRichFailure(err);
|
|
7796
9980
|
outcome = {
|
|
7797
9981
|
ok: false,
|
|
7798
|
-
error: err
|
|
9982
|
+
error: this._observerErrorProjection(err)?.message ?? legacyErrorMessage(err)
|
|
7799
9983
|
};
|
|
7800
9984
|
}
|
|
7801
9985
|
let terminalFailure;
|
|
@@ -7823,7 +10007,7 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7823
10007
|
{ agent: agentName }
|
|
7824
10008
|
);
|
|
7825
10009
|
}
|
|
7826
|
-
async executeAgentCall(agent2,
|
|
10010
|
+
async executeAgentCall(agent2, input, options, handoffMessages, usageCapture, sessionHistory = normalizeSessionHistory(this.sessionHistory)) {
|
|
7827
10011
|
if (this.budgetContext?.exceeded) {
|
|
7828
10012
|
const { limit, totalCost: spent, policy } = this.budgetContext;
|
|
7829
10013
|
if (policy === "warn") {
|
|
@@ -7840,7 +10024,82 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7840
10024
|
const resolveCtx = options?.metadata ? { metadata: { ...this.metadata, ...options.metadata } } : { metadata: this.metadata };
|
|
7841
10025
|
const modelUri = agent2.resolveModel(resolveCtx);
|
|
7842
10026
|
const systemPrompt = agent2.resolveSystem(resolveCtx);
|
|
7843
|
-
const { provider, model } = this.providerRegistry.resolve(modelUri, this.config);
|
|
10027
|
+
const { provider, model: resolvedModel } = this.providerRegistry.resolve(modelUri, this.config);
|
|
10028
|
+
let model = resolvedModel;
|
|
10029
|
+
const prompt = inputText(input);
|
|
10030
|
+
const providerOptions = options?.providerOptions ?? agent2._config.providerOptions;
|
|
10031
|
+
const richRequest = typeof input !== "string" || hasRichMessage(sessionHistory);
|
|
10032
|
+
if (richRequest) {
|
|
10033
|
+
if (providerOptions && ("messages" in providerOptions || "input" in providerOptions)) {
|
|
10034
|
+
throw new UnsupportedModelInputError({
|
|
10035
|
+
provider: provider.name ?? modelUri.split(":", 1)[0],
|
|
10036
|
+
model,
|
|
10037
|
+
modality: "image",
|
|
10038
|
+
feature: "raw input-container providerOptions"
|
|
10039
|
+
});
|
|
10040
|
+
}
|
|
10041
|
+
const source = firstImageSource(input, sessionHistory);
|
|
10042
|
+
if (!provider.validateInput) {
|
|
10043
|
+
throw new UnsupportedModelInputError({
|
|
10044
|
+
provider: provider.name ?? modelUri.split(":", 1)[0],
|
|
10045
|
+
model,
|
|
10046
|
+
modality: "image",
|
|
10047
|
+
...source ? { source } : {}
|
|
10048
|
+
});
|
|
10049
|
+
}
|
|
10050
|
+
const validation = provider.validateInput({
|
|
10051
|
+
model,
|
|
10052
|
+
input: cloneModelInput(input),
|
|
10053
|
+
history: cloneMessagesForProvider(sessionHistory),
|
|
10054
|
+
stream: this._streamingEnabled,
|
|
10055
|
+
hasTools: Boolean(
|
|
10056
|
+
agent2._config.tools?.length || agent2._config.mcp?.length || agent2._config.mcpTools?.length || agent2._config.handoffs
|
|
10057
|
+
),
|
|
10058
|
+
responseMode: options?.schema ? "structured" : "text",
|
|
10059
|
+
providerOptions
|
|
10060
|
+
});
|
|
10061
|
+
if (!validation || typeof validation.effectiveModel !== "string" || validation.effectiveModel.length === 0) {
|
|
10062
|
+
throw new UnsupportedModelInputError({
|
|
10063
|
+
provider: provider.name ?? modelUri.split(":", 1)[0],
|
|
10064
|
+
model,
|
|
10065
|
+
modality: "image",
|
|
10066
|
+
...source ? { source } : {}
|
|
10067
|
+
});
|
|
10068
|
+
}
|
|
10069
|
+
model = validation.effectiveModel;
|
|
10070
|
+
}
|
|
10071
|
+
const effectiveModelUri = !richRequest ? modelUri : `${modelUri.slice(0, modelUri.indexOf(":") + 1)}${model}`;
|
|
10072
|
+
if (usageCapture) usageCapture.modelUri ??= effectiveModelUri;
|
|
10073
|
+
if (richRequest) {
|
|
10074
|
+
const descriptors = [
|
|
10075
|
+
describeModelInput(input),
|
|
10076
|
+
...sessionHistory.map((message) => describeModelInput(message.content))
|
|
10077
|
+
].filter(
|
|
10078
|
+
(descriptor) => descriptor !== void 0
|
|
10079
|
+
);
|
|
10080
|
+
const parts = descriptors.flatMap((descriptor) => descriptor.parts);
|
|
10081
|
+
this.spanManager?.addEventToActiveSpan("axl.model_input", {
|
|
10082
|
+
"axl.input.parts": parts.length,
|
|
10083
|
+
"axl.input.images": parts.filter((part) => part.type === "image").length,
|
|
10084
|
+
"axl.input.source.bytes": parts.filter(
|
|
10085
|
+
(part) => part.type === "image" && part.source === "bytes"
|
|
10086
|
+
).length,
|
|
10087
|
+
"axl.input.source.base64": parts.filter(
|
|
10088
|
+
(part) => part.type === "image" && part.source === "base64"
|
|
10089
|
+
).length,
|
|
10090
|
+
"axl.input.source.url": parts.filter(
|
|
10091
|
+
(part) => part.type === "image" && part.source === "url"
|
|
10092
|
+
).length,
|
|
10093
|
+
"axl.input.source.provider_file": parts.filter(
|
|
10094
|
+
(part) => part.type === "image" && part.source === "provider-file"
|
|
10095
|
+
).length,
|
|
10096
|
+
"axl.input.inline_bytes": parts.reduce(
|
|
10097
|
+
(sum, part) => sum + (part.type === "image" ? part.bytes ?? 0 : 0),
|
|
10098
|
+
0
|
|
10099
|
+
)
|
|
10100
|
+
});
|
|
10101
|
+
}
|
|
10102
|
+
assertRichMessagesAreUserOnly(sessionHistory);
|
|
7844
10103
|
let resolvedHandoffs;
|
|
7845
10104
|
if (typeof agent2._config.handoffs === "function") {
|
|
7846
10105
|
try {
|
|
@@ -7861,50 +10120,60 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
7861
10120
|
messages.push({ role: "system", content: systemPrompt });
|
|
7862
10121
|
}
|
|
7863
10122
|
const maxContext = agent2._config.maxContext;
|
|
7864
|
-
if (maxContext &&
|
|
10123
|
+
if (maxContext && sessionHistory.length > 0) {
|
|
7865
10124
|
const reserveTokens = this.config.contextManagement?.reserveTokens ?? 2e3;
|
|
7866
10125
|
const systemTokens = systemPrompt ? estimateTokens(systemPrompt) : 0;
|
|
7867
10126
|
const toolTokens = toolDefs.length > 0 ? estimateTokens(JSON.stringify(toolDefs)) : 0;
|
|
7868
10127
|
const overhead = systemTokens + toolTokens + reserveTokens;
|
|
7869
10128
|
const availableForHistory = maxContext - overhead;
|
|
7870
|
-
const
|
|
7871
|
-
if (
|
|
10129
|
+
const historyEstimate = estimateMessagesTokens(sessionHistory);
|
|
10130
|
+
if (historyEstimate.unmeasured) {
|
|
10131
|
+
this.emitEvent({
|
|
10132
|
+
type: "log",
|
|
10133
|
+
data: {
|
|
10134
|
+
warning: "Context media contribution is unmeasured; provider will enforce its context limit"
|
|
10135
|
+
}
|
|
10136
|
+
});
|
|
10137
|
+
}
|
|
10138
|
+
if (historyEstimate.tokens > availableForHistory) {
|
|
7872
10139
|
const summarizedMessages = await this.summarizeHistory(
|
|
7873
10140
|
provider,
|
|
7874
10141
|
model,
|
|
7875
|
-
|
|
10142
|
+
sessionHistory,
|
|
7876
10143
|
availableForHistory
|
|
7877
10144
|
);
|
|
7878
10145
|
for (const msg of summarizedMessages) {
|
|
7879
10146
|
messages.push(msg);
|
|
7880
10147
|
}
|
|
7881
10148
|
} else {
|
|
7882
|
-
for (const msg of
|
|
10149
|
+
for (const msg of sessionHistory) {
|
|
7883
10150
|
messages.push(msg);
|
|
7884
10151
|
}
|
|
7885
10152
|
}
|
|
7886
10153
|
} else {
|
|
7887
|
-
for (const msg of
|
|
10154
|
+
for (const msg of sessionHistory) {
|
|
7888
10155
|
messages.push(msg);
|
|
7889
10156
|
}
|
|
7890
10157
|
}
|
|
7891
10158
|
const schemaPromptMode = options?.schemaPrompt ?? agent2._config.schemaPrompt ?? "json-schema";
|
|
7892
|
-
let userContent =
|
|
10159
|
+
let userContent = input;
|
|
7893
10160
|
let appendedSchemaText;
|
|
7894
10161
|
if (options?.schema) {
|
|
7895
10162
|
const schema = options.schema;
|
|
7896
10163
|
if (schemaPromptMode === "none") {
|
|
7897
10164
|
} else if (schemaPromptMode === "json-schema") {
|
|
7898
10165
|
appendedSchemaText = renderSchemaForPrompt(schema);
|
|
7899
|
-
|
|
10166
|
+
const guidance = `
|
|
7900
10167
|
|
|
7901
10168
|
Respond with valid JSON matching this schema:
|
|
7902
10169
|
${appendedSchemaText}`;
|
|
10170
|
+
userContent = typeof input === "string" ? input + guidance : [...input, { type: "text", text: guidance }];
|
|
7903
10171
|
} else {
|
|
7904
10172
|
appendedSchemaText = typeof schemaPromptMode.render === "function" ? schemaPromptMode.render(schema) : schemaPromptMode.render;
|
|
7905
|
-
|
|
10173
|
+
const guidance = `
|
|
7906
10174
|
|
|
7907
10175
|
${appendedSchemaText}`;
|
|
10176
|
+
userContent = typeof input === "string" ? input + guidance : [...input, { type: "text", text: guidance }];
|
|
7908
10177
|
}
|
|
7909
10178
|
}
|
|
7910
10179
|
messages.push({ role: "user", content: userContent });
|
|
@@ -7932,7 +10201,10 @@ ${appendedSchemaText}`;
|
|
|
7932
10201
|
}
|
|
7933
10202
|
const guardrails = agent2._config.guardrails;
|
|
7934
10203
|
if (guardrails?.input) {
|
|
7935
|
-
const inputResult = await guardrails.input(prompt, {
|
|
10204
|
+
const inputResult = await guardrails.input(prompt, {
|
|
10205
|
+
metadata: this.metadata,
|
|
10206
|
+
input: cloneModelInput(input)
|
|
10207
|
+
});
|
|
7936
10208
|
this.emitEvent({
|
|
7937
10209
|
type: "guardrail",
|
|
7938
10210
|
agent: agent2._name,
|
|
@@ -8023,7 +10295,7 @@ ${appendedSchemaText}`;
|
|
|
8023
10295
|
includeThoughts: options?.includeThoughts ?? agent2._config.includeThoughts,
|
|
8024
10296
|
toolChoice: options?.toolChoice ?? agent2._config.toolChoice,
|
|
8025
10297
|
stop: options?.stop ?? agent2._config.stop,
|
|
8026
|
-
providerOptions
|
|
10298
|
+
providerOptions,
|
|
8027
10299
|
signal: this.currentSignal
|
|
8028
10300
|
};
|
|
8029
10301
|
if (options?.schema && toolDefs.length === 0) {
|
|
@@ -8049,15 +10321,26 @@ ${appendedSchemaText}`;
|
|
|
8049
10321
|
...chatOptions.stop !== void 0 ? { stop: chatOptions.stop } : {}
|
|
8050
10322
|
};
|
|
8051
10323
|
let messagesSnapshot;
|
|
10324
|
+
let messageInputs;
|
|
8052
10325
|
if (verboseTrace) {
|
|
8053
10326
|
try {
|
|
8054
|
-
|
|
10327
|
+
messageInputs = currentMessages.flatMap((message, index) => {
|
|
10328
|
+
const descriptor = describeModelInput(message.content);
|
|
10329
|
+
return descriptor ? [{ index, input: descriptor }] : [];
|
|
10330
|
+
});
|
|
10331
|
+
messagesSnapshot = currentMessages.map((message) => ({
|
|
10332
|
+
...message,
|
|
10333
|
+
content: sanitizeModelInputForTrace(message.content)
|
|
10334
|
+
}));
|
|
8055
10335
|
} catch (err) {
|
|
8056
10336
|
console.warn(
|
|
8057
10337
|
"[axl] verbose trace messages snapshot failed to clone; emitting shallow copy:",
|
|
8058
10338
|
err instanceof Error ? err.message : String(err)
|
|
8059
10339
|
);
|
|
8060
|
-
messagesSnapshot =
|
|
10340
|
+
messagesSnapshot = currentMessages.map((message) => ({
|
|
10341
|
+
...message,
|
|
10342
|
+
content: sanitizeModelInputForTrace(message.content)
|
|
10343
|
+
}));
|
|
8061
10344
|
}
|
|
8062
10345
|
}
|
|
8063
10346
|
const retryReason = pendingRetryReason;
|
|
@@ -8065,16 +10348,18 @@ ${appendedSchemaText}`;
|
|
|
8065
10348
|
this.emitEvent({
|
|
8066
10349
|
type: "agent_call_start",
|
|
8067
10350
|
agent: agent2._name,
|
|
8068
|
-
model:
|
|
10351
|
+
model: effectiveModelUri,
|
|
8069
10352
|
turn: turns,
|
|
8070
10353
|
data: {
|
|
8071
10354
|
prompt,
|
|
10355
|
+
...typeof input === "string" ? {} : { input: describeModelInput(input) },
|
|
8072
10356
|
...systemPrompt ? { system: systemPrompt } : {},
|
|
8073
10357
|
params: callParams,
|
|
8074
10358
|
turn: turns,
|
|
8075
10359
|
...retryReason ? { retryReason } : {},
|
|
8076
10360
|
...toolDefs.length > 0 ? { toolNames: toolDefs.map((t) => t.function.name) } : {},
|
|
8077
|
-
...messagesSnapshot ? { messages: messagesSnapshot } : {}
|
|
10361
|
+
...messagesSnapshot ? { messages: messagesSnapshot } : {},
|
|
10362
|
+
...messageInputs && messageInputs.length > 0 ? { messageInputs } : {}
|
|
8078
10363
|
}
|
|
8079
10364
|
});
|
|
8080
10365
|
let response;
|
|
@@ -8098,7 +10383,10 @@ ${appendedSchemaText}`;
|
|
|
8098
10383
|
});
|
|
8099
10384
|
}
|
|
8100
10385
|
}) : void 0;
|
|
8101
|
-
for await (const chunk of provider.stream(
|
|
10386
|
+
for await (const chunk of provider.stream(
|
|
10387
|
+
cloneMessagesForProvider(currentMessages),
|
|
10388
|
+
chatOptions
|
|
10389
|
+
)) {
|
|
8102
10390
|
if (chunk.type === "text_delta") {
|
|
8103
10391
|
content2 += chunk.content;
|
|
8104
10392
|
this.emitEvent({ type: "token", data: chunk.content });
|
|
@@ -8164,24 +10452,25 @@ ${appendedSchemaText}`;
|
|
|
8164
10452
|
response.thinking_content = thinkingContent;
|
|
8165
10453
|
}
|
|
8166
10454
|
} else {
|
|
8167
|
-
response = await provider.chat(currentMessages, chatOptions);
|
|
10455
|
+
response = await provider.chat(cloneMessagesForProvider(currentMessages), chatOptions);
|
|
8168
10456
|
}
|
|
8169
10457
|
} catch (err) {
|
|
10458
|
+
if (richRequest) this.recordRichFailure(err);
|
|
8170
10459
|
this.emitEvent({
|
|
8171
10460
|
type: "agent_call_end",
|
|
8172
10461
|
agent: agent2._name,
|
|
8173
|
-
model:
|
|
10462
|
+
model: effectiveModelUri,
|
|
8174
10463
|
promptVersion: agent2._config.version,
|
|
8175
10464
|
duration: Date.now() - turnStart,
|
|
8176
10465
|
data: {
|
|
8177
10466
|
response: "",
|
|
8178
10467
|
turn: turns,
|
|
8179
10468
|
...retryReason ? { retryReason } : {},
|
|
8180
|
-
error: err
|
|
10469
|
+
error: this._observerErrorProjection(err)?.message ?? legacyErrorMessage(err),
|
|
8181
10470
|
// Enrich with typed-error metadata when available. `body` is
|
|
8182
10471
|
// deliberately NOT emitted — it's redaction-eligible (see
|
|
8183
10472
|
// docs/security.md). `data.error` already carries the message.
|
|
8184
|
-
...err
|
|
10473
|
+
...providerErrorEventMetadata(err, richRequest)
|
|
8185
10474
|
}
|
|
8186
10475
|
});
|
|
8187
10476
|
throw err;
|
|
@@ -8195,7 +10484,7 @@ ${appendedSchemaText}`;
|
|
|
8195
10484
|
this.emitEvent({
|
|
8196
10485
|
type: "agent_call_end",
|
|
8197
10486
|
agent: agent2._name,
|
|
8198
|
-
model:
|
|
10487
|
+
model: effectiveModelUri,
|
|
8199
10488
|
promptVersion: agent2._config.version,
|
|
8200
10489
|
cost: response.cost,
|
|
8201
10490
|
tokens: response.usage ? {
|
|
@@ -8229,12 +10518,12 @@ ${appendedSchemaText}`;
|
|
|
8229
10518
|
if (descriptor) {
|
|
8230
10519
|
const mode = descriptor.mode ?? "oneway";
|
|
8231
10520
|
let handoffPrompt = prompt;
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
} catch {
|
|
10521
|
+
try {
|
|
10522
|
+
const args = JSON.parse(toolCall.function.arguments);
|
|
10523
|
+
if (typeof args.message === "string" && args.message.length > 0) {
|
|
10524
|
+
handoffPrompt = args.message;
|
|
8237
10525
|
}
|
|
10526
|
+
} catch {
|
|
8238
10527
|
}
|
|
8239
10528
|
const handoffStart = Date.now();
|
|
8240
10529
|
const sourceFrame = askStorage.getStore();
|
|
@@ -8261,15 +10550,27 @@ ${appendedSchemaText}`;
|
|
|
8261
10550
|
validateRetries: options.validateRetries
|
|
8262
10551
|
} : void 0;
|
|
8263
10552
|
const handoffFn = () => askStorage.run(targetFrame, async () => {
|
|
8264
|
-
|
|
10553
|
+
const delegateOriginal = agent2._delegateOriginalInput;
|
|
10554
|
+
const handoffInput = delegateOriginal ?? (typeof input === "string" ? handoffPrompt : input);
|
|
10555
|
+
const enrichedHandoffInput = appendHandoffInstruction(
|
|
10556
|
+
handoffInput,
|
|
10557
|
+
inputText(input),
|
|
10558
|
+
handoffPrompt
|
|
10559
|
+
);
|
|
10560
|
+
this.emitEvent({
|
|
10561
|
+
type: "ask_start",
|
|
10562
|
+
prompt: inputText(enrichedHandoffInput),
|
|
10563
|
+
...typeof enrichedHandoffInput === "string" ? {} : { input: describeModelInput(enrichedHandoffInput) }
|
|
10564
|
+
});
|
|
8265
10565
|
const targetAskStart = Date.now();
|
|
8266
10566
|
try {
|
|
8267
10567
|
const result = await this.executeAgentCall(
|
|
8268
10568
|
descriptor.agent,
|
|
8269
|
-
|
|
10569
|
+
enrichedHandoffInput,
|
|
8270
10570
|
handoffOptions,
|
|
8271
10571
|
currentMessages,
|
|
8272
|
-
usageCapture
|
|
10572
|
+
usageCapture,
|
|
10573
|
+
sessionHistory
|
|
8273
10574
|
);
|
|
8274
10575
|
this.emitEvent({
|
|
8275
10576
|
type: "ask_end",
|
|
@@ -8280,11 +10581,13 @@ ${appendedSchemaText}`;
|
|
|
8280
10581
|
});
|
|
8281
10582
|
return result;
|
|
8282
10583
|
} catch (err) {
|
|
10584
|
+
const richHandoff = typeof enrichedHandoffInput !== "string" || hasRichMessage(sessionHistory);
|
|
10585
|
+
if (richHandoff) this.recordRichFailure(err);
|
|
8283
10586
|
this.emitEvent({
|
|
8284
10587
|
type: "ask_end",
|
|
8285
10588
|
outcome: {
|
|
8286
10589
|
ok: false,
|
|
8287
|
-
error: err
|
|
10590
|
+
error: this._observerErrorProjection(err)?.message ?? legacyErrorMessage(err)
|
|
8288
10591
|
},
|
|
8289
10592
|
cost: targetFrame.askCost.value,
|
|
8290
10593
|
...targetFrame.askUnpriced ? { unpriced: true } : {},
|
|
@@ -8793,12 +11096,12 @@ ${appendedSchemaText}`;
|
|
|
8793
11096
|
content: `Summary of earlier conversation:
|
|
8794
11097
|
${this.summaryCache}`
|
|
8795
11098
|
};
|
|
8796
|
-
const summaryTokens = estimateTokens(summaryMsg2.content) + 4;
|
|
11099
|
+
const summaryTokens = estimateTokens(summarizeModelInput(summaryMsg2.content)) + 4;
|
|
8797
11100
|
const remaining = availableTokens - summaryTokens;
|
|
8798
11101
|
let recentTokens2 = 0;
|
|
8799
11102
|
let splitIdx2 = history.length;
|
|
8800
11103
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
8801
|
-
const msgTokens = estimateTokens(history[i].content) + 4;
|
|
11104
|
+
const msgTokens = estimateTokens(summarizeModelInput(history[i].content)) + 4;
|
|
8802
11105
|
if (recentTokens2 + msgTokens > remaining) break;
|
|
8803
11106
|
recentTokens2 += msgTokens;
|
|
8804
11107
|
splitIdx2 = i;
|
|
@@ -8811,7 +11114,7 @@ ${this.summaryCache}`
|
|
|
8811
11114
|
let splitIdx = history.length;
|
|
8812
11115
|
const targetRecent = Math.floor(availableTokens * 0.6);
|
|
8813
11116
|
for (let i = history.length - 1; i >= 0; i--) {
|
|
8814
|
-
const msgTokens = estimateTokens(history[i].content) + 4;
|
|
11117
|
+
const msgTokens = estimateTokens(summarizeModelInput(history[i].content)) + 4;
|
|
8815
11118
|
if (recentTokens + msgTokens > targetRecent) break;
|
|
8816
11119
|
recentTokens += msgTokens;
|
|
8817
11120
|
splitIdx = i;
|
|
@@ -8829,7 +11132,7 @@ ${this.summaryCache}`
|
|
|
8829
11132
|
summaryProvider = provider;
|
|
8830
11133
|
summaryModel = model;
|
|
8831
11134
|
}
|
|
8832
|
-
const oldContent = oldMessages.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
11135
|
+
const oldContent = oldMessages.map((m) => `${m.role}: ${summarizeModelInput(m.content)}`).join("\n");
|
|
8833
11136
|
const summaryResponse = await summaryProvider.chat(
|
|
8834
11137
|
[
|
|
8835
11138
|
{
|
|
@@ -10063,7 +12366,9 @@ ${summaryResponse.content}`
|
|
|
10063
12366
|
reason: "routed"
|
|
10064
12367
|
}
|
|
10065
12368
|
});
|
|
10066
|
-
|
|
12369
|
+
const routerInput = options?.routerInput === "text" ? inputText(prompt) : prompt;
|
|
12370
|
+
routerAgent._delegateOriginalInput = prompt;
|
|
12371
|
+
return this.ask(routerAgent, routerInput, {
|
|
10067
12372
|
schema: options?.schema,
|
|
10068
12373
|
schemaPrompt: options?.schemaPrompt,
|
|
10069
12374
|
nativeStructuredOutput: options?.nativeStructuredOutput,
|
|
@@ -10073,6 +12378,278 @@ ${summaryResponse.content}`
|
|
|
10073
12378
|
validateRetries: options?.validateRetries
|
|
10074
12379
|
});
|
|
10075
12380
|
}
|
|
12381
|
+
/** Transcribe recorded audio through the dedicated transcription registry.
|
|
12382
|
+
* This operation never enters the chat history or chat-provider path. */
|
|
12383
|
+
async transcribe(request) {
|
|
12384
|
+
const startedAt = Date.now();
|
|
12385
|
+
const transcriptionId = randomUUID();
|
|
12386
|
+
const signal = this.currentSignal;
|
|
12387
|
+
const rawAudio = request && typeof request === "object" && "audio" in request ? request.audio : void 0;
|
|
12388
|
+
const source = rawAudio?.type;
|
|
12389
|
+
const requestedUri = typeof request?.model === "string" ? request.model : void 0;
|
|
12390
|
+
const colon = requestedUri?.indexOf(":") ?? -1;
|
|
12391
|
+
const requestedProvider = colon > 0 ? requestedUri.slice(0, colon) : void 0;
|
|
12392
|
+
const requestedModel = colon > 0 && colon < requestedUri.length - 1 ? requestedUri.slice(colon + 1) : void 0;
|
|
12393
|
+
const startAudio = {
|
|
12394
|
+
source: typeof source === "string" ? source : "unknown",
|
|
12395
|
+
...rawAudio?.data instanceof Uint8Array ? { bytes: rawAudio.data.byteLength } : {},
|
|
12396
|
+
...typeof rawAudio?.mediaType === "string" ? { mediaType: rawAudio.mediaType } : {}
|
|
12397
|
+
};
|
|
12398
|
+
this.emitEvent({
|
|
12399
|
+
type: "transcription_start",
|
|
12400
|
+
transcriptionId,
|
|
12401
|
+
model: requestedModel,
|
|
12402
|
+
data: {
|
|
12403
|
+
...requestedProvider ? { provider: requestedProvider } : {},
|
|
12404
|
+
...requestedModel ? { model: requestedModel } : {},
|
|
12405
|
+
audio: startAudio
|
|
12406
|
+
}
|
|
12407
|
+
});
|
|
12408
|
+
let terminalEmitted = false;
|
|
12409
|
+
const emitTerminal = (partial) => {
|
|
12410
|
+
if (terminalEmitted) return;
|
|
12411
|
+
terminalEmitted = true;
|
|
12412
|
+
this.emitEvent({
|
|
12413
|
+
type: "transcription_end",
|
|
12414
|
+
transcriptionId,
|
|
12415
|
+
model: partial.model,
|
|
12416
|
+
duration: Date.now() - startedAt,
|
|
12417
|
+
...partial.cost !== void 0 ? { cost: partial.cost } : {},
|
|
12418
|
+
...partial.tokens ? { tokens: partial.tokens } : {},
|
|
12419
|
+
data: partial.data
|
|
12420
|
+
});
|
|
12421
|
+
};
|
|
12422
|
+
const abortError3 = () => new DOMException("The transcription operation was aborted", "AbortError");
|
|
12423
|
+
const cleanupStatus = (value) => value === "not_required" || value === "deleted" || value === "failed" || value === "timed_out" ? value : void 0;
|
|
12424
|
+
const safeProviderFailure = (error, provider, model) => {
|
|
12425
|
+
const details = error && typeof error === "object" ? error : void 0;
|
|
12426
|
+
const accounting = normalizeTranscriptionAccounting(details?.usage, details?.pricingStatus);
|
|
12427
|
+
const cleanup = cleanupStatus(details?.cleanupStatus);
|
|
12428
|
+
const providerError = error instanceof ProviderError ? error : void 0;
|
|
12429
|
+
const diagnostics = providerError ? {
|
|
12430
|
+
status: providerError.status,
|
|
12431
|
+
retryable: providerError.retryable,
|
|
12432
|
+
...providerError.retryAfterMs !== void 0 ? { retryAfterMs: providerError.retryAfterMs } : {},
|
|
12433
|
+
...providerError.requestId ? { requestId: providerError.requestId } : {}
|
|
12434
|
+
} : void 0;
|
|
12435
|
+
return {
|
|
12436
|
+
accounting,
|
|
12437
|
+
cleanup,
|
|
12438
|
+
diagnostics,
|
|
12439
|
+
error: new TranscriptionOperationError({
|
|
12440
|
+
provider,
|
|
12441
|
+
model,
|
|
12442
|
+
...accounting.usage ? { usage: accounting.usage } : {},
|
|
12443
|
+
...accounting.pricingStatus ? { pricingStatus: accounting.pricingStatus } : {},
|
|
12444
|
+
...cleanup ? { cleanupStatus: cleanup } : {},
|
|
12445
|
+
...diagnostics ?? {},
|
|
12446
|
+
cause: error
|
|
12447
|
+
})
|
|
12448
|
+
};
|
|
12449
|
+
};
|
|
12450
|
+
const terminalData = (status, provider, model, audio, accounting, extras = {}) => ({
|
|
12451
|
+
status,
|
|
12452
|
+
...provider ? { provider } : {},
|
|
12453
|
+
...model ? { model } : {},
|
|
12454
|
+
...audio ? { audio } : {},
|
|
12455
|
+
...accounting.usage ? { usage: accounting.usage } : {},
|
|
12456
|
+
...accounting.pricingStatus ? { pricingStatus: accounting.pricingStatus } : {},
|
|
12457
|
+
...extras
|
|
12458
|
+
});
|
|
12459
|
+
try {
|
|
12460
|
+
const normalized = normalizeTranscriptionRequest(request);
|
|
12461
|
+
const descriptor = {
|
|
12462
|
+
source: normalized.audio.type,
|
|
12463
|
+
...normalized.audio.type === "bytes" ? { bytes: normalized.audio.data.byteLength } : {},
|
|
12464
|
+
...normalized.audio.mediaType !== void 0 ? { mediaType: normalized.audio.mediaType } : {}
|
|
12465
|
+
};
|
|
12466
|
+
if (this.currentSignal?.aborted || signal?.aborted) {
|
|
12467
|
+
emitTerminal({
|
|
12468
|
+
model: requestedModel,
|
|
12469
|
+
data: terminalData("aborted", requestedProvider, requestedModel, descriptor, {
|
|
12470
|
+
hasWork: false
|
|
12471
|
+
})
|
|
12472
|
+
});
|
|
12473
|
+
throw abortError3();
|
|
12474
|
+
}
|
|
12475
|
+
if (this.budgetContext?.exceeded) {
|
|
12476
|
+
const { limit, totalCost: spent, policy } = this.budgetContext;
|
|
12477
|
+
if (policy === "warn") {
|
|
12478
|
+
this.emitEvent({
|
|
12479
|
+
type: "log",
|
|
12480
|
+
data: { warning: "Budget exceeded", limit, spent, policy }
|
|
12481
|
+
});
|
|
12482
|
+
} else {
|
|
12483
|
+
throw new BudgetExceededError(limit, spent, policy);
|
|
12484
|
+
}
|
|
12485
|
+
}
|
|
12486
|
+
if (!this.transcriptionProviderRegistry) {
|
|
12487
|
+
throw new InvalidTranscriptionInputError(
|
|
12488
|
+
"No transcription provider registry is configured"
|
|
12489
|
+
);
|
|
12490
|
+
}
|
|
12491
|
+
const resolved = this.transcriptionProviderRegistry.resolve(normalized.model, this.config);
|
|
12492
|
+
if (normalized.audio.type === "provider-file" && normalized.audio.provider !== resolved.providerName) {
|
|
12493
|
+
throw new InvalidTranscriptionInputError(
|
|
12494
|
+
"Transcription provider-file source does not match the requested provider"
|
|
12495
|
+
);
|
|
12496
|
+
}
|
|
12497
|
+
const capabilities = resolved.provider.capabilities(resolved.model);
|
|
12498
|
+
if (!capabilities || !capabilities.sources.includes(normalized.audio.type)) {
|
|
12499
|
+
throw new InvalidTranscriptionInputError(
|
|
12500
|
+
"The requested transcription model does not support this audio source"
|
|
12501
|
+
);
|
|
12502
|
+
}
|
|
12503
|
+
if (normalized.timestamps && !capabilities.timestamps?.includes(normalized.timestamps)) {
|
|
12504
|
+
throw new InvalidTranscriptionInputError(
|
|
12505
|
+
"The requested transcription model does not support requested timestamps"
|
|
12506
|
+
);
|
|
12507
|
+
}
|
|
12508
|
+
if (normalized.diarization && !capabilities.diarization) {
|
|
12509
|
+
throw new InvalidTranscriptionInputError(
|
|
12510
|
+
"The requested transcription model does not support diarization"
|
|
12511
|
+
);
|
|
12512
|
+
}
|
|
12513
|
+
const providerRequest = {
|
|
12514
|
+
...normalized,
|
|
12515
|
+
model: resolved.model,
|
|
12516
|
+
...signal ? { signal } : {}
|
|
12517
|
+
};
|
|
12518
|
+
let result;
|
|
12519
|
+
try {
|
|
12520
|
+
result = await resolved.provider.transcribe(providerRequest);
|
|
12521
|
+
} catch (error) {
|
|
12522
|
+
const failure = safeProviderFailure(error, resolved.providerName, resolved.model);
|
|
12523
|
+
if (this.currentSignal?.aborted || signal?.aborted) {
|
|
12524
|
+
if (failure.accounting.cost !== void 0)
|
|
12525
|
+
this._accumulateBudgetCost(failure.accounting.cost);
|
|
12526
|
+
emitTerminal({
|
|
12527
|
+
model: resolved.model,
|
|
12528
|
+
...failure.accounting.cost !== void 0 ? { cost: failure.accounting.cost } : {},
|
|
12529
|
+
...failure.accounting.tokens ? { tokens: failure.accounting.tokens } : {},
|
|
12530
|
+
data: terminalData(
|
|
12531
|
+
"aborted",
|
|
12532
|
+
resolved.providerName,
|
|
12533
|
+
resolved.model,
|
|
12534
|
+
descriptor,
|
|
12535
|
+
failure.accounting,
|
|
12536
|
+
...failure.cleanup ? [{ cleanupStatus: failure.cleanup }] : []
|
|
12537
|
+
)
|
|
12538
|
+
});
|
|
12539
|
+
throw abortError3();
|
|
12540
|
+
}
|
|
12541
|
+
if (failure.accounting.cost !== void 0)
|
|
12542
|
+
this._accumulateBudgetCost(failure.accounting.cost);
|
|
12543
|
+
emitTerminal({
|
|
12544
|
+
model: resolved.model,
|
|
12545
|
+
...failure.accounting.cost !== void 0 ? { cost: failure.accounting.cost } : {},
|
|
12546
|
+
...failure.accounting.tokens ? { tokens: failure.accounting.tokens } : {},
|
|
12547
|
+
data: terminalData(
|
|
12548
|
+
"failed",
|
|
12549
|
+
resolved.providerName,
|
|
12550
|
+
resolved.model,
|
|
12551
|
+
descriptor,
|
|
12552
|
+
failure.accounting,
|
|
12553
|
+
{
|
|
12554
|
+
error: "Transcription operation failed",
|
|
12555
|
+
errorCode: failure.error.code,
|
|
12556
|
+
...failure.diagnostics ? { providerError: failure.diagnostics } : {},
|
|
12557
|
+
...failure.cleanup ? { cleanupStatus: failure.cleanup } : {}
|
|
12558
|
+
}
|
|
12559
|
+
)
|
|
12560
|
+
});
|
|
12561
|
+
throw failure.error;
|
|
12562
|
+
}
|
|
12563
|
+
const accounting = normalizeTranscriptionAccounting(
|
|
12564
|
+
result.transcript?.usage,
|
|
12565
|
+
result.transcript?.pricingStatus
|
|
12566
|
+
);
|
|
12567
|
+
if (accounting.cost !== void 0) this._accumulateBudgetCost(accounting.cost);
|
|
12568
|
+
if (this.currentSignal?.aborted || signal?.aborted) {
|
|
12569
|
+
emitTerminal({
|
|
12570
|
+
model: resolved.model,
|
|
12571
|
+
...accounting.cost !== void 0 ? { cost: accounting.cost } : {},
|
|
12572
|
+
...accounting.tokens ? { tokens: accounting.tokens } : {},
|
|
12573
|
+
data: terminalData(
|
|
12574
|
+
"aborted",
|
|
12575
|
+
resolved.providerName,
|
|
12576
|
+
resolved.model,
|
|
12577
|
+
descriptor,
|
|
12578
|
+
accounting,
|
|
12579
|
+
...cleanupStatus(result.cleanupStatus) ? [{ cleanupStatus: cleanupStatus(result.cleanupStatus) }] : []
|
|
12580
|
+
)
|
|
12581
|
+
});
|
|
12582
|
+
throw abortError3();
|
|
12583
|
+
}
|
|
12584
|
+
let transcript;
|
|
12585
|
+
try {
|
|
12586
|
+
transcript = cloneTranscript(result.transcript);
|
|
12587
|
+
} catch (error) {
|
|
12588
|
+
const cleanup = cleanupStatus(result.cleanupStatus);
|
|
12589
|
+
const failure = new TranscriptionOperationError({
|
|
12590
|
+
provider: resolved.providerName,
|
|
12591
|
+
model: resolved.model,
|
|
12592
|
+
...accounting.usage ? { usage: accounting.usage } : {},
|
|
12593
|
+
...accounting.pricingStatus ? { pricingStatus: accounting.pricingStatus } : {},
|
|
12594
|
+
...cleanup ? { cleanupStatus: cleanup } : {},
|
|
12595
|
+
cause: error
|
|
12596
|
+
});
|
|
12597
|
+
emitTerminal({
|
|
12598
|
+
model: resolved.model,
|
|
12599
|
+
...accounting.cost !== void 0 ? { cost: accounting.cost } : {},
|
|
12600
|
+
...accounting.tokens ? { tokens: accounting.tokens } : {},
|
|
12601
|
+
data: terminalData(
|
|
12602
|
+
"failed",
|
|
12603
|
+
resolved.providerName,
|
|
12604
|
+
resolved.model,
|
|
12605
|
+
descriptor,
|
|
12606
|
+
accounting,
|
|
12607
|
+
{
|
|
12608
|
+
error: "Transcription operation failed",
|
|
12609
|
+
errorCode: "TRANSCRIPTION_PROVIDER_ERROR",
|
|
12610
|
+
...cleanup ? { cleanupStatus: cleanup } : {}
|
|
12611
|
+
}
|
|
12612
|
+
)
|
|
12613
|
+
});
|
|
12614
|
+
throw failure;
|
|
12615
|
+
}
|
|
12616
|
+
emitTerminal({
|
|
12617
|
+
model: resolved.model,
|
|
12618
|
+
...accounting.cost !== void 0 ? { cost: accounting.cost } : {},
|
|
12619
|
+
...accounting.tokens ? { tokens: accounting.tokens } : {},
|
|
12620
|
+
data: terminalData(
|
|
12621
|
+
"completed",
|
|
12622
|
+
resolved.providerName,
|
|
12623
|
+
resolved.model,
|
|
12624
|
+
descriptor,
|
|
12625
|
+
accounting,
|
|
12626
|
+
{
|
|
12627
|
+
text: transcript.text,
|
|
12628
|
+
...cleanupStatus(result.cleanupStatus) ? { cleanupStatus: cleanupStatus(result.cleanupStatus) } : {}
|
|
12629
|
+
}
|
|
12630
|
+
)
|
|
12631
|
+
});
|
|
12632
|
+
return transcript;
|
|
12633
|
+
} catch (error) {
|
|
12634
|
+
if (!terminalEmitted) {
|
|
12635
|
+
const aborted = signal?.aborted || error instanceof Error && error.name === "AbortError";
|
|
12636
|
+
emitTerminal({
|
|
12637
|
+
model: requestedModel,
|
|
12638
|
+
data: terminalData(
|
|
12639
|
+
aborted ? "aborted" : "failed",
|
|
12640
|
+
requestedProvider,
|
|
12641
|
+
requestedModel,
|
|
12642
|
+
void 0,
|
|
12643
|
+
{ hasWork: false },
|
|
12644
|
+
{
|
|
12645
|
+
error: aborted ? "Transcription operation aborted" : "Transcription operation failed"
|
|
12646
|
+
}
|
|
12647
|
+
)
|
|
12648
|
+
});
|
|
12649
|
+
}
|
|
12650
|
+
throw error;
|
|
12651
|
+
}
|
|
12652
|
+
}
|
|
10076
12653
|
// ── Private ───────────────────────────────────────────────────────────
|
|
10077
12654
|
/**
|
|
10078
12655
|
* Internal emitter input — intentionally loose so call sites don't need to
|
|
@@ -10088,7 +12665,7 @@ ${summaryResponse.content}`
|
|
|
10088
12665
|
const cost = partial.cost;
|
|
10089
12666
|
if (COST_BEARING_LEAF_TYPES.includes(partial.type)) {
|
|
10090
12667
|
const usable = isUsableCost(cost);
|
|
10091
|
-
const unpriced = !usable &&
|
|
12668
|
+
const unpriced = !usable && hasPositiveBillableWork(
|
|
10092
12669
|
partial
|
|
10093
12670
|
);
|
|
10094
12671
|
if (frame) {
|
|
@@ -10193,11 +12770,13 @@ function agent(config) {
|
|
|
10193
12770
|
_name: defaultName,
|
|
10194
12771
|
async ask(prompt, options) {
|
|
10195
12772
|
const registry = new ProviderRegistry();
|
|
12773
|
+
const transcriptionRegistry = new TranscriptionProviderRegistry();
|
|
10196
12774
|
const ctx = new WorkflowContext({
|
|
10197
12775
|
input: prompt,
|
|
10198
12776
|
executionId: randomUUID2(),
|
|
10199
12777
|
config: {},
|
|
10200
|
-
providerRegistry: registry
|
|
12778
|
+
providerRegistry: registry,
|
|
12779
|
+
transcriptionProviderRegistry: transcriptionRegistry
|
|
10201
12780
|
});
|
|
10202
12781
|
return ctx.ask(agentInstance, prompt, options);
|
|
10203
12782
|
},
|
|
@@ -10257,7 +12836,7 @@ var MemoryStore = class {
|
|
|
10257
12836
|
return data !== void 0 ? structuredClone(data) : null;
|
|
10258
12837
|
}
|
|
10259
12838
|
async saveSession(sessionId, history) {
|
|
10260
|
-
this.sessions.set(sessionId, structuredClone(history));
|
|
12839
|
+
this.sessions.set(sessionId, structuredClone(normalizePersistedSessionHistory(history)));
|
|
10261
12840
|
}
|
|
10262
12841
|
async getSession(sessionId) {
|
|
10263
12842
|
const history = this.sessions.get(sessionId);
|
|
@@ -10681,7 +13260,7 @@ var SQLiteStore = class {
|
|
|
10681
13260
|
const stmt = this.db.prepare(
|
|
10682
13261
|
"INSERT OR REPLACE INTO sessions (session_id, history) VALUES (?, ?)"
|
|
10683
13262
|
);
|
|
10684
|
-
stmt.run(sessionId, JSON.stringify(history));
|
|
13263
|
+
stmt.run(sessionId, JSON.stringify(normalizePersistedSessionHistory(history)));
|
|
10685
13264
|
}
|
|
10686
13265
|
async getSession(sessionId) {
|
|
10687
13266
|
const stmt = this.db.prepare("SELECT history FROM sessions WHERE session_id = ?");
|
|
@@ -11249,7 +13828,7 @@ var AxlStream = class extends Readable {
|
|
|
11249
13828
|
/** Signal an error. `executionId` is required for the same reason as
|
|
11250
13829
|
* `_done`: terminal events must carry a real id even when the
|
|
11251
13830
|
* failure happens before any real trace event fires (review S4). */
|
|
11252
|
-
_error(error, executionId) {
|
|
13831
|
+
_error(error, executionId, observerMessage = error.message, observerName = error.name) {
|
|
11253
13832
|
if (this.events.isFinished) return;
|
|
11254
13833
|
const errorEvent = {
|
|
11255
13834
|
schemaVersion: 2,
|
|
@@ -11257,7 +13836,7 @@ var AxlStream = class extends Readable {
|
|
|
11257
13836
|
executionId,
|
|
11258
13837
|
step: Number.MAX_SAFE_INTEGER,
|
|
11259
13838
|
timestamp: Date.now(),
|
|
11260
|
-
data: { message:
|
|
13839
|
+
data: { message: observerMessage, name: observerName }
|
|
11261
13840
|
};
|
|
11262
13841
|
this.events._push(errorEvent);
|
|
11263
13842
|
this.push(errorEvent);
|
|
@@ -11984,6 +14563,10 @@ var StreamingFlusher = class {
|
|
|
11984
14563
|
function isAbortError4(err) {
|
|
11985
14564
|
return typeof err === "object" && err !== null && err.name === "AbortError";
|
|
11986
14565
|
}
|
|
14566
|
+
function executionErrorProjection(ctx, error) {
|
|
14567
|
+
const safe = ctx?._observerErrorProjection(error);
|
|
14568
|
+
return safe ?? { message: error instanceof Error ? error.message : String(error) };
|
|
14569
|
+
}
|
|
11987
14570
|
function pushEventBounded(execInfo, event, cap) {
|
|
11988
14571
|
if (STREAMING_EXCLUDED_TYPES.has(event.type)) return;
|
|
11989
14572
|
if (cap === Infinity || execInfo.events.length < cap) {
|
|
@@ -12078,6 +14661,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12078
14661
|
tools = /* @__PURE__ */ new Map();
|
|
12079
14662
|
agents = /* @__PURE__ */ new Map();
|
|
12080
14663
|
providerRegistry;
|
|
14664
|
+
transcriptionProviderRegistry;
|
|
12081
14665
|
stateStore;
|
|
12082
14666
|
executions = /* @__PURE__ */ new Map();
|
|
12083
14667
|
pendingDecisionResolvers = /* @__PURE__ */ new Map();
|
|
@@ -12133,6 +14717,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12133
14717
|
super();
|
|
12134
14718
|
this.config = resolveConfig(config ?? {});
|
|
12135
14719
|
this.providerRegistry = new ProviderRegistry();
|
|
14720
|
+
this.transcriptionProviderRegistry = new TranscriptionProviderRegistry();
|
|
12136
14721
|
this.stateStore = this.createStateStore();
|
|
12137
14722
|
const requested = this.config.state?.maxEventsPerExecution;
|
|
12138
14723
|
if (requested === void 0) {
|
|
@@ -12281,7 +14866,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12281
14866
|
execInfo.status = "failed";
|
|
12282
14867
|
execInfo.completedAt = Date.now();
|
|
12283
14868
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
12284
|
-
execInfo.error =
|
|
14869
|
+
execInfo.error = executionErrorProjection(ctx, terminalError).message;
|
|
12285
14870
|
execInfo.observation = observation;
|
|
12286
14871
|
ctx._emitWorkflowEnd({
|
|
12287
14872
|
status: "failed",
|
|
@@ -12536,6 +15121,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12536
15121
|
metadata: options?.metadata,
|
|
12537
15122
|
config: this.config,
|
|
12538
15123
|
providerRegistry: this.providerRegistry,
|
|
15124
|
+
transcriptionProviderRegistry: this.transcriptionProviderRegistry,
|
|
12539
15125
|
stateStore: this.stateStore,
|
|
12540
15126
|
mcpManager: this.mcpManager,
|
|
12541
15127
|
spanManager: this.spanManager,
|
|
@@ -12564,6 +15150,10 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12564
15150
|
registerProvider(name, provider) {
|
|
12565
15151
|
this.providerRegistry.registerInstance(name, provider);
|
|
12566
15152
|
}
|
|
15153
|
+
/** Register a dedicated transcription adapter. It is never used for chat. */
|
|
15154
|
+
registerTranscriptionProvider(name, provider) {
|
|
15155
|
+
this.transcriptionProviderRegistry.registerInstance(name, provider);
|
|
15156
|
+
}
|
|
12567
15157
|
/** Resolve a provider:model URI to a Provider instance and model name. */
|
|
12568
15158
|
resolveProvider(uri) {
|
|
12569
15159
|
return this.providerRegistry.resolve(uri, this.config);
|
|
@@ -12607,6 +15197,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12607
15197
|
metadata: options?.metadata,
|
|
12608
15198
|
config: this.config,
|
|
12609
15199
|
providerRegistry: this.providerRegistry,
|
|
15200
|
+
transcriptionProviderRegistry: this.transcriptionProviderRegistry,
|
|
12610
15201
|
sessionHistory,
|
|
12611
15202
|
signal: controller.signal,
|
|
12612
15203
|
eventStreamOptions: options?.events,
|
|
@@ -12694,6 +15285,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12694
15285
|
const executionId = randomUUID3();
|
|
12695
15286
|
this.abortControllers.set(executionId, controller);
|
|
12696
15287
|
let execInfo;
|
|
15288
|
+
let wfCtx;
|
|
12697
15289
|
const run = async () => {
|
|
12698
15290
|
const workflow2 = this.workflows.get(name);
|
|
12699
15291
|
if (!workflow2) throw new Error(`Workflow "${name}" not registered`);
|
|
@@ -12717,12 +15309,13 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12717
15309
|
metadata: liftPersistedMetadata(options?.metadata)
|
|
12718
15310
|
};
|
|
12719
15311
|
this.executions.set(executionId, execInfo);
|
|
12720
|
-
const
|
|
15312
|
+
const context = new WorkflowContext({
|
|
12721
15313
|
input: validated,
|
|
12722
15314
|
executionId,
|
|
12723
15315
|
metadata: options?.metadata,
|
|
12724
15316
|
config: this.config,
|
|
12725
15317
|
providerRegistry: this.providerRegistry,
|
|
15318
|
+
transcriptionProviderRegistry: this.transcriptionProviderRegistry,
|
|
12726
15319
|
sessionHistory,
|
|
12727
15320
|
signal: controller.signal,
|
|
12728
15321
|
eventStreamOptions: options?.events,
|
|
@@ -12787,6 +15380,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12787
15380
|
unpricedWarned: false
|
|
12788
15381
|
}
|
|
12789
15382
|
});
|
|
15383
|
+
wfCtx = context;
|
|
12790
15384
|
return this.spanManager.withSpanAsync(
|
|
12791
15385
|
"axl.workflow.execute",
|
|
12792
15386
|
{
|
|
@@ -12796,7 +15390,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12796
15390
|
},
|
|
12797
15391
|
(span) => this.runWorkflowBody({
|
|
12798
15392
|
workflow: workflow2,
|
|
12799
|
-
ctx:
|
|
15393
|
+
ctx: context,
|
|
12800
15394
|
execInfo,
|
|
12801
15395
|
validated,
|
|
12802
15396
|
span,
|
|
@@ -12809,12 +15403,14 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
12809
15403
|
execInfo.status = "failed";
|
|
12810
15404
|
execInfo.completedAt = Date.now();
|
|
12811
15405
|
execInfo.duration = execInfo.completedAt - execInfo.startedAt;
|
|
12812
|
-
execInfo.error =
|
|
15406
|
+
execInfo.error = executionErrorProjection(wfCtx, err).message;
|
|
12813
15407
|
this.persistExecution(execInfo);
|
|
12814
15408
|
}
|
|
12815
15409
|
this.abortControllers.delete(executionId);
|
|
12816
15410
|
cleanupAbortForwarder();
|
|
12817
|
-
|
|
15411
|
+
const originalError = err instanceof Error ? err : new Error(String(err));
|
|
15412
|
+
const projection = executionErrorProjection(wfCtx, err);
|
|
15413
|
+
axlStream._error(originalError, executionId, projection.message, projection.name);
|
|
12818
15414
|
});
|
|
12819
15415
|
return axlStream;
|
|
12820
15416
|
}
|
|
@@ -13225,7 +15821,7 @@ var AxlRuntime = class extends EventEmitter2 {
|
|
|
13225
15821
|
},
|
|
13226
15822
|
{
|
|
13227
15823
|
role: "user",
|
|
13228
|
-
content: messages.map((m) => `${m.role}: ${m.content}`).join("\n")
|
|
15824
|
+
content: messages.map((m) => `${m.role}: ${summarizeModelInput(m.content)}`).join("\n")
|
|
13229
15825
|
}
|
|
13230
15826
|
],
|
|
13231
15827
|
{ model, maxTokens: 1024 }
|
|
@@ -13672,7 +16268,7 @@ async function registerConditions(conditions) {
|
|
|
13672
16268
|
].join("\n");
|
|
13673
16269
|
nodeModule.register(`data:text/javascript,${encodeURIComponent(hookCode)}`);
|
|
13674
16270
|
} catch {
|
|
13675
|
-
console.warn("Warning:
|
|
16271
|
+
console.warn("Warning: this Node.js runtime does not support --conditions");
|
|
13676
16272
|
}
|
|
13677
16273
|
}
|
|
13678
16274
|
|
|
@@ -13981,7 +16577,11 @@ var RedisStore = class _RedisStore {
|
|
|
13981
16577
|
async saveSession(sessionId, history) {
|
|
13982
16578
|
const ttl = this.ttlFor("session");
|
|
13983
16579
|
const setOptions = ttl !== void 0 ? { EX: ttl } : void 0;
|
|
13984
|
-
await this.client.multi().set(
|
|
16580
|
+
await this.client.multi().set(
|
|
16581
|
+
this.sessionKey(sessionId),
|
|
16582
|
+
JSON.stringify(normalizePersistedSessionHistory(history)),
|
|
16583
|
+
setOptions
|
|
16584
|
+
).sAdd(this.sessionIdsKey(), sessionId).exec();
|
|
13985
16585
|
}
|
|
13986
16586
|
async getSession(sessionId) {
|
|
13987
16587
|
const raw = await this.client.get(this.sessionKey(sessionId));
|
|
@@ -14469,10 +17069,15 @@ export {
|
|
|
14469
17069
|
EventStreamOverflowError,
|
|
14470
17070
|
GROQ_PROFILE,
|
|
14471
17071
|
GeminiProvider,
|
|
17072
|
+
GeminiTranscriptionProvider,
|
|
14472
17073
|
GuardrailError,
|
|
14473
17074
|
InMemoryVectorStore,
|
|
17075
|
+
InvalidModelInputError,
|
|
17076
|
+
InvalidTranscriptionInputError,
|
|
14474
17077
|
LLAMACPP_PROFILE,
|
|
14475
17078
|
LMSTUDIO_PROFILE,
|
|
17079
|
+
MAX_INLINE_MODEL_INPUT_BYTES,
|
|
17080
|
+
MAX_INLINE_TRANSCRIPTION_BYTES,
|
|
14476
17081
|
MISTRAL_PROFILE,
|
|
14477
17082
|
MaxTurnsError,
|
|
14478
17083
|
McpManager,
|
|
@@ -14488,6 +17093,8 @@ export {
|
|
|
14488
17093
|
OpenAIEmbedder,
|
|
14489
17094
|
OpenAIProvider,
|
|
14490
17095
|
OpenAIResponsesProvider,
|
|
17096
|
+
OpenAITranscriptionProvider,
|
|
17097
|
+
OpenRouterTranscriptionProvider,
|
|
14491
17098
|
ProviderError,
|
|
14492
17099
|
ProviderRegistry,
|
|
14493
17100
|
QuorumNotMet,
|
|
@@ -14503,7 +17110,11 @@ export {
|
|
|
14503
17110
|
TimeoutError,
|
|
14504
17111
|
ToolFailure,
|
|
14505
17112
|
ToolModelOutputError,
|
|
17113
|
+
TranscriptionOperationError,
|
|
17114
|
+
TranscriptionProviderRegistry,
|
|
14506
17115
|
UnsupportedEventSchemaVersionError,
|
|
17116
|
+
UnsupportedModelInputError,
|
|
17117
|
+
UnsupportedTranscriptionInputError,
|
|
14507
17118
|
VLLM_PROFILE,
|
|
14508
17119
|
ValidationError,
|
|
14509
17120
|
VerifyError,
|
|
@@ -14520,8 +17131,10 @@ export {
|
|
|
14520
17131
|
findConfig,
|
|
14521
17132
|
getEventSchemaVersion,
|
|
14522
17133
|
getExecutionEventSchemaVersion,
|
|
17134
|
+
hasPositiveBillableWork,
|
|
14523
17135
|
hasPositiveTokens,
|
|
14524
17136
|
importModule,
|
|
17137
|
+
inputText,
|
|
14525
17138
|
isCostBearingLeaf,
|
|
14526
17139
|
isRetryableStatus,
|
|
14527
17140
|
isRootLevel,
|