@axlsdk/axl 0.20.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1378 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -16
- package/dist/index.d.ts +67 -16
- package/dist/index.js +1378 -310
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -875,6 +875,42 @@ var RateLimiter = class {
|
|
|
875
875
|
}
|
|
876
876
|
};
|
|
877
877
|
|
|
878
|
+
// src/providers/builtin-table-pricing.ts
|
|
879
|
+
var policies = /* @__PURE__ */ new WeakMap();
|
|
880
|
+
function attachBuiltinTablePricingPolicy(profile, policy) {
|
|
881
|
+
if (profile.pricing.kind !== "table") {
|
|
882
|
+
throw new Error("Built-in table pricing policy requires table pricing.");
|
|
883
|
+
}
|
|
884
|
+
policies.set(profile.pricing.table, policy);
|
|
885
|
+
}
|
|
886
|
+
function hasFunctionToolsOnly(value) {
|
|
887
|
+
return value === void 0 || Array.isArray(value) && value.every(
|
|
888
|
+
(tool2) => tool2 !== null && typeof tool2 === "object" && tool2.type === "function"
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
function hasValidRequiredCacheSplit(context) {
|
|
892
|
+
const usage = context.response?.usage;
|
|
893
|
+
const hit = usage?.prompt_cache_hit_tokens;
|
|
894
|
+
const miss = usage?.prompt_cache_miss_tokens;
|
|
895
|
+
return typeof hit === "number" && typeof miss === "number" && Number.isSafeInteger(hit) && Number.isSafeInteger(miss) && hit >= 0 && miss >= 0 && hit + miss === usage?.prompt_tokens;
|
|
896
|
+
}
|
|
897
|
+
function isBuiltinTablePricingEligible(profile, context) {
|
|
898
|
+
const policy = profile.pricing.kind === "table" ? policies.get(profile.pricing.table) : void 0;
|
|
899
|
+
if (!policy) return true;
|
|
900
|
+
if (policy.canonicalBaseUrl && context.baseUrl !== policy.canonicalBaseUrl) return false;
|
|
901
|
+
if (policy.requireCacheSplit && !hasValidRequiredCacheSplit(context)) return false;
|
|
902
|
+
if (policy.functionToolsOnly && !hasFunctionToolsOnly(context.request?.tools)) return false;
|
|
903
|
+
if (policy.chargedRequestModifiers?.some((modifier) => context.request?.[modifier] !== void 0)) {
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
if (!policy.supportedServiceTiers) return true;
|
|
907
|
+
const requestTier = context.request?.service_tier ?? context.request?.serviceTier;
|
|
908
|
+
const responseTier = context.response?.service_tier ?? context.response?.serviceTier;
|
|
909
|
+
const effectiveTier = responseTier ?? requestTier;
|
|
910
|
+
if (effectiveTier === void 0) return true;
|
|
911
|
+
return typeof effectiveTier === "string" && policy.supportedServiceTiers.includes(effectiveTier);
|
|
912
|
+
}
|
|
913
|
+
|
|
878
914
|
// src/providers/openai-compatible.ts
|
|
879
915
|
function resolvePerModel(value, model, fallback) {
|
|
880
916
|
if (value === void 0) return fallback;
|
|
@@ -889,9 +925,13 @@ function sortedKeys(table) {
|
|
|
889
925
|
}
|
|
890
926
|
return keys;
|
|
891
927
|
}
|
|
892
|
-
function priceFromTable(table, model, promptTokens, completionTokens, cachedTokens) {
|
|
928
|
+
function priceFromTable(table, model, promptTokens, completionTokens, cachedTokens, match = "prefix") {
|
|
929
|
+
const cached = cachedTokens ?? 0;
|
|
930
|
+
if (!Number.isSafeInteger(promptTokens) || !Number.isSafeInteger(completionTokens) || !Number.isSafeInteger(cached) || promptTokens < 0 || completionTokens < 0 || cached < 0 || cached > promptTokens) {
|
|
931
|
+
return void 0;
|
|
932
|
+
}
|
|
893
933
|
let pricing = table[model];
|
|
894
|
-
if (!pricing) {
|
|
934
|
+
if (!pricing && match === "prefix") {
|
|
895
935
|
for (const key of sortedKeys(table)) {
|
|
896
936
|
if (model.startsWith(key)) {
|
|
897
937
|
pricing = table[key];
|
|
@@ -901,7 +941,6 @@ function priceFromTable(table, model, promptTokens, completionTokens, cachedToke
|
|
|
901
941
|
}
|
|
902
942
|
if (!pricing) return void 0;
|
|
903
943
|
const [inputRate, outputRate, cacheMultiplier] = pricing;
|
|
904
|
-
const cached = cachedTokens ?? 0;
|
|
905
944
|
const inputCost = (promptTokens - cached) * inputRate + cached * inputRate * cacheMultiplier;
|
|
906
945
|
return inputCost + completionTokens * outputRate;
|
|
907
946
|
}
|
|
@@ -1062,7 +1101,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1062
1101
|
});
|
|
1063
1102
|
}
|
|
1064
1103
|
const json = await res.json();
|
|
1065
|
-
return this.parseResponse(json, options.model);
|
|
1104
|
+
return this.parseResponse(json, this.requestModel(body, options.model), body);
|
|
1066
1105
|
}
|
|
1067
1106
|
async *stream(messages, options) {
|
|
1068
1107
|
const headers = this.buildHeaders(await this.resolveKey());
|
|
@@ -1090,7 +1129,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1090
1129
|
if (!res.body) {
|
|
1091
1130
|
throw new Error(`${this.profile.label ?? this.profile.name} stream response has no body`);
|
|
1092
1131
|
}
|
|
1093
|
-
yield* this.parseSSEStream(res.body, options.model);
|
|
1132
|
+
yield* this.parseSSEStream(res.body, this.requestModel(body, options.model), body);
|
|
1094
1133
|
}
|
|
1095
1134
|
// ---------------------------------------------------------------------------
|
|
1096
1135
|
// Request building
|
|
@@ -1111,12 +1150,13 @@ var OpenAICompatibleProvider = class {
|
|
|
1111
1150
|
buildRequestBody(messages, options, stream) {
|
|
1112
1151
|
const profile = this.profile;
|
|
1113
1152
|
const resolved = resolveThinkingOptions(options);
|
|
1153
|
+
const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
1114
1154
|
const body = {
|
|
1115
|
-
model:
|
|
1116
|
-
messages: messages.map((m) => this.formatMessage(m,
|
|
1155
|
+
model: effectiveModel,
|
|
1156
|
+
messages: messages.map((m) => this.formatMessage(m, effectiveModel)),
|
|
1117
1157
|
stream
|
|
1118
1158
|
};
|
|
1119
|
-
const emitResult = profile.reasoning.emit(body, resolved,
|
|
1159
|
+
const emitResult = profile.reasoning.emit(body, resolved, effectiveModel) ?? {};
|
|
1120
1160
|
const stripTemperature = emitResult.stripTemperature ?? false;
|
|
1121
1161
|
if (options.temperature !== void 0 && !stripTemperature) {
|
|
1122
1162
|
body.temperature = options.temperature;
|
|
@@ -1127,7 +1167,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1127
1167
|
if (options.stop) body.stop = options.stop;
|
|
1128
1168
|
if (options.tools && options.tools.length > 0) {
|
|
1129
1169
|
body.tools = options.tools;
|
|
1130
|
-
if (resolvePerModel(profile.parallelToolCalls,
|
|
1170
|
+
if (resolvePerModel(profile.parallelToolCalls, effectiveModel, false)) {
|
|
1131
1171
|
body.parallel_tool_calls = true;
|
|
1132
1172
|
}
|
|
1133
1173
|
}
|
|
@@ -1135,7 +1175,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1135
1175
|
if (options.responseFormat) {
|
|
1136
1176
|
const supportsSchema = resolvePerModel(
|
|
1137
1177
|
profile.capabilities?.supportsJsonSchema,
|
|
1138
|
-
|
|
1178
|
+
effectiveModel,
|
|
1139
1179
|
true
|
|
1140
1180
|
);
|
|
1141
1181
|
body.response_format = options.responseFormat.type === "json_schema" && !supportsSchema ? { type: "json_object" } : options.responseFormat;
|
|
@@ -1146,7 +1186,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1146
1186
|
if (profile.requestDefaults) Object.assign(body, profile.requestDefaults);
|
|
1147
1187
|
const userKeys = options.providerOptions ? Object.keys(options.providerOptions) : [];
|
|
1148
1188
|
if (options.providerOptions) Object.assign(body, options.providerOptions);
|
|
1149
|
-
const forbidden = resolvePerModel(profile.capabilities?.forbiddenParams,
|
|
1189
|
+
const forbidden = resolvePerModel(profile.capabilities?.forbiddenParams, effectiveModel, []);
|
|
1150
1190
|
for (const key of forbidden) {
|
|
1151
1191
|
if (!userKeys.includes(key)) delete body[key];
|
|
1152
1192
|
}
|
|
@@ -1181,6 +1221,9 @@ var OpenAICompatibleProvider = class {
|
|
|
1181
1221
|
}
|
|
1182
1222
|
return `${label} API error (${status}): ${body}`;
|
|
1183
1223
|
}
|
|
1224
|
+
requestModel(body, fallback) {
|
|
1225
|
+
return typeof body.model === "string" ? body.model : fallback;
|
|
1226
|
+
}
|
|
1184
1227
|
toUsage(raw) {
|
|
1185
1228
|
if (!raw) return void 0;
|
|
1186
1229
|
return {
|
|
@@ -1188,10 +1231,27 @@ var OpenAICompatibleProvider = class {
|
|
|
1188
1231
|
completion_tokens: raw.completion_tokens,
|
|
1189
1232
|
total_tokens: raw.total_tokens,
|
|
1190
1233
|
reasoning_tokens: raw.completion_tokens_details?.reasoning_tokens,
|
|
1191
|
-
|
|
1234
|
+
// DeepSeek reports the cache split at the top level rather than under
|
|
1235
|
+
// prompt_tokens_details. Preserve the normalized hit count for the
|
|
1236
|
+
// generic table estimator; its split is validated before pricing.
|
|
1237
|
+
cached_tokens: raw.prompt_cache_hit_tokens ?? raw.prompt_tokens_details?.cached_tokens,
|
|
1238
|
+
cache_write_tokens: raw.prompt_tokens_details?.cache_write_tokens
|
|
1192
1239
|
};
|
|
1193
1240
|
}
|
|
1194
|
-
|
|
1241
|
+
reportedCost(raw) {
|
|
1242
|
+
if (!raw) return void 0;
|
|
1243
|
+
if (this.profile.name === "xai") {
|
|
1244
|
+
const ticks = raw.cost_in_usd_ticks;
|
|
1245
|
+
return typeof ticks === "number" && Number.isSafeInteger(ticks) && ticks >= 0 ? ticks / 1e10 : void 0;
|
|
1246
|
+
}
|
|
1247
|
+
return typeof raw.cost === "number" && Number.isFinite(raw.cost) && raw.cost >= 0 ? raw.cost : void 0;
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* The generic profile path stays flat and public. Native subclasses may
|
|
1251
|
+
* override this protected seam to use a provider-internal estimator without
|
|
1252
|
+
* widening `PricingSource` or `ProviderProfile`.
|
|
1253
|
+
*/
|
|
1254
|
+
computeCost(model, usage, reportedCost, context) {
|
|
1195
1255
|
const p = this.profile.pricing;
|
|
1196
1256
|
if (p.kind === "zero") return 0;
|
|
1197
1257
|
if (!usage) return void 0;
|
|
@@ -1199,14 +1259,22 @@ var OpenAICompatibleProvider = class {
|
|
|
1199
1259
|
case "unknown":
|
|
1200
1260
|
return void 0;
|
|
1201
1261
|
case "from-response":
|
|
1202
|
-
return typeof reportedCost === "number" && Number.isFinite(reportedCost) ? reportedCost : void 0;
|
|
1262
|
+
return typeof reportedCost === "number" && Number.isFinite(reportedCost) && reportedCost >= 0 ? reportedCost : void 0;
|
|
1203
1263
|
case "table": {
|
|
1264
|
+
if (!isBuiltinTablePricingEligible(this.profile, {
|
|
1265
|
+
baseUrl: this.baseUrl,
|
|
1266
|
+
request: context?.request,
|
|
1267
|
+
response: context?.response
|
|
1268
|
+
})) {
|
|
1269
|
+
return void 0;
|
|
1270
|
+
}
|
|
1204
1271
|
const c = priceFromTable(
|
|
1205
1272
|
p.table,
|
|
1206
1273
|
model,
|
|
1207
1274
|
usage.prompt_tokens,
|
|
1208
1275
|
usage.completion_tokens,
|
|
1209
|
-
usage.cached_tokens
|
|
1276
|
+
usage.cached_tokens,
|
|
1277
|
+
p.match
|
|
1210
1278
|
);
|
|
1211
1279
|
return c !== void 0 && Number.isFinite(c) ? c : void 0;
|
|
1212
1280
|
}
|
|
@@ -1216,7 +1284,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1216
1284
|
}
|
|
1217
1285
|
}
|
|
1218
1286
|
}
|
|
1219
|
-
parseResponse(json, model) {
|
|
1287
|
+
parseResponse(json, model, request) {
|
|
1220
1288
|
const message = json.choices?.[0]?.message ?? {};
|
|
1221
1289
|
let content = normalizeMessageContent(message.content);
|
|
1222
1290
|
let thinking;
|
|
@@ -1262,7 +1330,10 @@ var OpenAICompatibleProvider = class {
|
|
|
1262
1330
|
}
|
|
1263
1331
|
}
|
|
1264
1332
|
const usage = this.toUsage(json.usage);
|
|
1265
|
-
const cost = this.computeCost(model, usage, json.usage
|
|
1333
|
+
const cost = this.computeCost(json.model ?? model, usage, this.reportedCost(json.usage), {
|
|
1334
|
+
request,
|
|
1335
|
+
response: json
|
|
1336
|
+
});
|
|
1266
1337
|
return {
|
|
1267
1338
|
content,
|
|
1268
1339
|
thinking_content: thinking || void 0,
|
|
@@ -1285,11 +1356,12 @@ var OpenAICompatibleProvider = class {
|
|
|
1285
1356
|
// ---------------------------------------------------------------------------
|
|
1286
1357
|
// SSE streaming
|
|
1287
1358
|
// ---------------------------------------------------------------------------
|
|
1288
|
-
async *parseSSEStream(body, model) {
|
|
1359
|
+
async *parseSSEStream(body, model, request) {
|
|
1289
1360
|
const reader = body.getReader();
|
|
1290
1361
|
const decoder = new TextDecoder();
|
|
1291
1362
|
let buffer = "";
|
|
1292
1363
|
let usageData;
|
|
1364
|
+
let pricingResponse;
|
|
1293
1365
|
const capture = this.profile.reasoning.capture;
|
|
1294
1366
|
const thinkScanner = capture === "think_tags" ? new ThinkTagScanner() : void 0;
|
|
1295
1367
|
let reasoningText = "";
|
|
@@ -1330,7 +1402,15 @@ var OpenAICompatibleProvider = class {
|
|
|
1330
1402
|
return {
|
|
1331
1403
|
type: "done",
|
|
1332
1404
|
usage,
|
|
1333
|
-
cost: this.computeCost(
|
|
1405
|
+
cost: this.computeCost(
|
|
1406
|
+
pricingResponse?.model ?? model,
|
|
1407
|
+
usage,
|
|
1408
|
+
this.reportedCost(usageData),
|
|
1409
|
+
{
|
|
1410
|
+
request,
|
|
1411
|
+
response: pricingResponse
|
|
1412
|
+
}
|
|
1413
|
+
),
|
|
1334
1414
|
providerMetadata
|
|
1335
1415
|
};
|
|
1336
1416
|
};
|
|
@@ -1371,8 +1451,12 @@ var OpenAICompatibleProvider = class {
|
|
|
1371
1451
|
body: JSON.stringify(parsed.error)
|
|
1372
1452
|
});
|
|
1373
1453
|
}
|
|
1454
|
+
if (parsed.model !== void 0 || parsed.service_tier !== void 0 || parsed.serviceTier !== void 0) {
|
|
1455
|
+
pricingResponse = { ...pricingResponse, ...parsed };
|
|
1456
|
+
}
|
|
1374
1457
|
if (parsed.usage) {
|
|
1375
1458
|
usageData = parsed.usage;
|
|
1459
|
+
pricingResponse = { ...pricingResponse, ...parsed };
|
|
1376
1460
|
if (!parsed.choices || parsed.choices.length === 0) continue;
|
|
1377
1461
|
}
|
|
1378
1462
|
const delta = parsed.choices?.[0]?.delta;
|
|
@@ -1446,38 +1530,328 @@ var OpenAICompatibleProvider = class {
|
|
|
1446
1530
|
|
|
1447
1531
|
// src/providers/openai.ts
|
|
1448
1532
|
var OPENAI_PRICING = {
|
|
1449
|
-
//
|
|
1533
|
+
// Flat, exact Standard rows only. Context-tiered and cache-write-priced
|
|
1534
|
+
// models intentionally stay out of this compatibility view.
|
|
1450
1535
|
"gpt-4o": [25e-7, 1e-5, 0.5],
|
|
1451
1536
|
"gpt-4o-mini": [15e-8, 6e-7, 0.5],
|
|
1452
|
-
"gpt-4-turbo": [1e-5, 3e-5, 0.5],
|
|
1453
|
-
"gpt-4": [3e-5, 6e-5, 0.5],
|
|
1454
|
-
"gpt-3.5-turbo": [5e-7, 15e-7, 0.5],
|
|
1455
1537
|
o1: [15e-6, 6e-5, 0.5],
|
|
1456
|
-
"o1-mini": [3e-6, 12e-6, 0.5],
|
|
1457
|
-
"o1-pro": [15e-5, 6e-4, 0.5],
|
|
1458
|
-
// gpt-4.1 / o3 / o4 era — cache reads at 25% of input rate
|
|
1459
1538
|
"gpt-4.1": [2e-6, 8e-6, 0.25],
|
|
1460
1539
|
"gpt-4.1-mini": [4e-7, 16e-7, 0.25],
|
|
1461
1540
|
"gpt-4.1-nano": [1e-7, 4e-7, 0.25],
|
|
1462
|
-
o3: [
|
|
1541
|
+
o3: [2e-6, 8e-6, 0.25],
|
|
1463
1542
|
"o3-mini": [11e-7, 44e-7, 0.25],
|
|
1464
|
-
"o3-pro": [2e-5, 8e-5, 0.25],
|
|
1465
1543
|
"o4-mini": [11e-7, 44e-7, 0.25],
|
|
1466
|
-
// gpt-5 era — cache reads at 10% of input rate
|
|
1467
1544
|
"gpt-5": [125e-8, 1e-5, 0.1],
|
|
1468
1545
|
"gpt-5-mini": [25e-8, 2e-6, 0.1],
|
|
1469
1546
|
"gpt-5-nano": [5e-8, 4e-7, 0.1],
|
|
1470
1547
|
"gpt-5.1": [125e-8, 1e-5, 0.1],
|
|
1471
|
-
"gpt-5.2": [175e-8, 14e-6, 0.1]
|
|
1472
|
-
"gpt-5.3": [175e-8, 14e-6, 0.1],
|
|
1473
|
-
"gpt-5.4": [25e-7, 15e-6, 0.1],
|
|
1474
|
-
"gpt-5.4-pro": [3e-5, 18e-5, 0.1],
|
|
1475
|
-
// gpt-5.5 era (flagship released 2026-04) — cache reads at 10% of input rate
|
|
1476
|
-
"gpt-5.5": [5e-6, 3e-5, 0.1],
|
|
1477
|
-
"gpt-5.5-pro": [3e-5, 18e-5, 0.1]
|
|
1548
|
+
"gpt-5.2": [175e-8, 14e-6, 0.1]
|
|
1478
1549
|
};
|
|
1550
|
+
var M = 1e6;
|
|
1551
|
+
var LONG_CONTEXT_BOUNDARY = 272e3;
|
|
1552
|
+
var withLongContext = (short) => ({
|
|
1553
|
+
input: short.input * 2,
|
|
1554
|
+
cachedInput: short.cachedInput === void 0 ? void 0 : short.cachedInput * 2,
|
|
1555
|
+
cacheWrite: short.cacheWrite === void 0 ? void 0 : short.cacheWrite * 2,
|
|
1556
|
+
output: short.output * 1.5
|
|
1557
|
+
});
|
|
1558
|
+
var DIRECT_OPENAI_CATALOG = [
|
|
1559
|
+
{
|
|
1560
|
+
aliases: ["gpt-5.6", "gpt-5.6-sol"],
|
|
1561
|
+
snapshotBase: "gpt-5.6-sol",
|
|
1562
|
+
short: { input: 5 / M, cachedInput: 0.5 / M, cacheWrite: 6.25 / M, output: 30 / M },
|
|
1563
|
+
long: withLongContext({
|
|
1564
|
+
input: 5 / M,
|
|
1565
|
+
cachedInput: 0.5 / M,
|
|
1566
|
+
cacheWrite: 6.25 / M,
|
|
1567
|
+
output: 30 / M
|
|
1568
|
+
}),
|
|
1569
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
aliases: ["gpt-5.6-terra"],
|
|
1573
|
+
snapshotBase: "gpt-5.6-terra",
|
|
1574
|
+
short: { input: 2 / M, cachedInput: 0.2 / M, cacheWrite: 2.5 / M, output: 12 / M },
|
|
1575
|
+
long: withLongContext({
|
|
1576
|
+
input: 2 / M,
|
|
1577
|
+
cachedInput: 0.2 / M,
|
|
1578
|
+
cacheWrite: 2.5 / M,
|
|
1579
|
+
output: 12 / M
|
|
1580
|
+
}),
|
|
1581
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1582
|
+
},
|
|
1583
|
+
{
|
|
1584
|
+
aliases: ["gpt-5.6-luna"],
|
|
1585
|
+
snapshotBase: "gpt-5.6-luna",
|
|
1586
|
+
short: { input: 0.2 / M, cachedInput: 0.02 / M, cacheWrite: 0.25 / M, output: 1.2 / M },
|
|
1587
|
+
long: withLongContext({
|
|
1588
|
+
input: 0.2 / M,
|
|
1589
|
+
cachedInput: 0.02 / M,
|
|
1590
|
+
cacheWrite: 0.25 / M,
|
|
1591
|
+
output: 1.2 / M
|
|
1592
|
+
}),
|
|
1593
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1594
|
+
},
|
|
1595
|
+
// Existing direct models retain literal current Standard rows. These flat
|
|
1596
|
+
// rows keep native pricing aligned with the compatibility wrapper while the
|
|
1597
|
+
// private estimator still validates categories and billing mode strictly.
|
|
1598
|
+
{
|
|
1599
|
+
aliases: ["gpt-4o", "gpt-4o-2024-08-06", "gpt-4o-2024-11-20"],
|
|
1600
|
+
snapshotBase: "gpt-4o",
|
|
1601
|
+
short: { input: 2.5 / M, cachedInput: 1.25 / M, output: 10 / M }
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
aliases: ["gpt-4o-mini", "gpt-4o-mini-2024-07-18"],
|
|
1605
|
+
snapshotBase: "gpt-4o-mini",
|
|
1606
|
+
short: { input: 0.15 / M, cachedInput: 0.075 / M, output: 0.6 / M }
|
|
1607
|
+
},
|
|
1608
|
+
{
|
|
1609
|
+
aliases: ["gpt-3.5-turbo"],
|
|
1610
|
+
snapshotBase: "gpt-3.5-turbo",
|
|
1611
|
+
short: { input: 0.5 / M, output: 1.5 / M }
|
|
1612
|
+
},
|
|
1613
|
+
{
|
|
1614
|
+
aliases: ["o1", "o1-2024-12-17"],
|
|
1615
|
+
snapshotBase: "o1",
|
|
1616
|
+
short: { input: 15 / M, cachedInput: 7.5 / M, output: 60 / M }
|
|
1617
|
+
},
|
|
1618
|
+
{
|
|
1619
|
+
aliases: ["o1-pro", "o1-pro-2025-03-19"],
|
|
1620
|
+
snapshotBase: "o1-pro",
|
|
1621
|
+
short: { input: 150 / M, output: 600 / M }
|
|
1622
|
+
},
|
|
1623
|
+
{
|
|
1624
|
+
aliases: ["gpt-4.1", "gpt-4.1-2025-04-14"],
|
|
1625
|
+
snapshotBase: "gpt-4.1",
|
|
1626
|
+
short: { input: 2 / M, cachedInput: 0.5 / M, output: 8 / M }
|
|
1627
|
+
},
|
|
1628
|
+
{
|
|
1629
|
+
aliases: ["gpt-4.1-mini", "gpt-4.1-mini-2025-04-14"],
|
|
1630
|
+
snapshotBase: "gpt-4.1-mini",
|
|
1631
|
+
short: { input: 0.4 / M, cachedInput: 0.1 / M, output: 1.6 / M }
|
|
1632
|
+
},
|
|
1633
|
+
{
|
|
1634
|
+
aliases: ["gpt-4.1-nano", "gpt-4.1-nano-2025-04-14"],
|
|
1635
|
+
snapshotBase: "gpt-4.1-nano",
|
|
1636
|
+
short: { input: 0.1 / M, cachedInput: 0.025 / M, output: 0.4 / M }
|
|
1637
|
+
},
|
|
1638
|
+
{
|
|
1639
|
+
aliases: ["o3", "o3-2025-04-16"],
|
|
1640
|
+
snapshotBase: "o3",
|
|
1641
|
+
short: { input: 2 / M, cachedInput: 0.5 / M, output: 8 / M }
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
aliases: ["o3-mini", "o3-mini-2025-01-31"],
|
|
1645
|
+
snapshotBase: "o3-mini",
|
|
1646
|
+
short: { input: 1.1 / M, cachedInput: 0.55 / M, output: 4.4 / M }
|
|
1647
|
+
},
|
|
1648
|
+
{
|
|
1649
|
+
aliases: ["o3-pro", "o3-pro-2025-06-10"],
|
|
1650
|
+
snapshotBase: "o3-pro",
|
|
1651
|
+
short: { input: 20 / M, output: 80 / M }
|
|
1652
|
+
},
|
|
1653
|
+
{
|
|
1654
|
+
aliases: ["o4-mini", "o4-mini-2025-04-16"],
|
|
1655
|
+
snapshotBase: "o4-mini",
|
|
1656
|
+
short: { input: 1.1 / M, cachedInput: 0.275 / M, output: 4.4 / M }
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
aliases: ["gpt-5", "gpt-5-2025-08-07"],
|
|
1660
|
+
snapshotBase: "gpt-5",
|
|
1661
|
+
short: { input: 1.25 / M, cachedInput: 0.125 / M, output: 10 / M }
|
|
1662
|
+
},
|
|
1663
|
+
{
|
|
1664
|
+
aliases: ["gpt-5-mini", "gpt-5-mini-2025-08-07"],
|
|
1665
|
+
snapshotBase: "gpt-5-mini",
|
|
1666
|
+
short: { input: 0.25 / M, cachedInput: 0.025 / M, output: 2 / M }
|
|
1667
|
+
},
|
|
1668
|
+
{
|
|
1669
|
+
aliases: ["gpt-5-nano", "gpt-5-nano-2025-08-07"],
|
|
1670
|
+
snapshotBase: "gpt-5-nano",
|
|
1671
|
+
short: { input: 0.05 / M, cachedInput: 5e-3 / M, output: 0.4 / M }
|
|
1672
|
+
},
|
|
1673
|
+
{
|
|
1674
|
+
aliases: ["gpt-5.1", "gpt-5.1-2025-11-13"],
|
|
1675
|
+
snapshotBase: "gpt-5.1",
|
|
1676
|
+
short: { input: 1.25 / M, cachedInput: 0.125 / M, output: 10 / M }
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
aliases: ["gpt-5.2", "gpt-5.2-2025-12-11"],
|
|
1680
|
+
snapshotBase: "gpt-5.2",
|
|
1681
|
+
short: { input: 1.75 / M, cachedInput: 0.175 / M, output: 14 / M }
|
|
1682
|
+
},
|
|
1683
|
+
{
|
|
1684
|
+
aliases: ["gpt-5.4", "gpt-5.4-2026-03-05"],
|
|
1685
|
+
snapshotBase: "gpt-5.4",
|
|
1686
|
+
short: { input: 2.5 / M, cachedInput: 0.25 / M, output: 15 / M },
|
|
1687
|
+
long: withLongContext({ input: 2.5 / M, cachedInput: 0.25 / M, output: 15 / M }),
|
|
1688
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1689
|
+
},
|
|
1690
|
+
{
|
|
1691
|
+
aliases: ["gpt-5.4-pro", "gpt-5.4-pro-2026-03-05"],
|
|
1692
|
+
snapshotBase: "gpt-5.4-pro",
|
|
1693
|
+
short: { input: 30 / M, output: 180 / M },
|
|
1694
|
+
long: withLongContext({ input: 30 / M, output: 180 / M }),
|
|
1695
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1696
|
+
},
|
|
1697
|
+
{
|
|
1698
|
+
aliases: ["gpt-5.5", "gpt-5.5-2026-04-23"],
|
|
1699
|
+
snapshotBase: "gpt-5.5",
|
|
1700
|
+
short: { input: 5 / M, cachedInput: 0.5 / M, output: 30 / M },
|
|
1701
|
+
long: withLongContext({ input: 5 / M, cachedInput: 0.5 / M, output: 30 / M }),
|
|
1702
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1703
|
+
},
|
|
1704
|
+
{
|
|
1705
|
+
aliases: ["gpt-5.5-pro", "gpt-5.5-pro-2026-04-23"],
|
|
1706
|
+
snapshotBase: "gpt-5.5-pro",
|
|
1707
|
+
short: { input: 30 / M, output: 180 / M },
|
|
1708
|
+
long: withLongContext({ input: 30 / M, output: 180 / M }),
|
|
1709
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
aliases: ["gpt-5.4-mini", "gpt-5.4-mini-2026-03-17"],
|
|
1713
|
+
short: { input: 0.75 / M, cachedInput: 0.075 / M, output: 4.5 / M }
|
|
1714
|
+
},
|
|
1715
|
+
{
|
|
1716
|
+
aliases: ["gpt-5.4-nano", "gpt-5.4-nano-2026-03-17"],
|
|
1717
|
+
short: { input: 0.2 / M, cachedInput: 0.02 / M, output: 1.25 / M }
|
|
1718
|
+
},
|
|
1719
|
+
{
|
|
1720
|
+
aliases: ["gpt-5.2-pro", "gpt-5.2-pro-2025-12-11"],
|
|
1721
|
+
short: { input: 21 / M, output: 168 / M }
|
|
1722
|
+
},
|
|
1723
|
+
{ aliases: ["gpt-5-pro", "gpt-5-pro-2025-10-06"], short: { input: 15 / M, output: 120 / M } },
|
|
1724
|
+
{ aliases: ["gpt-3.5-turbo-instruct"], short: { input: 1.5 / M, output: 2 / M } },
|
|
1725
|
+
{ aliases: ["davinci-002"], short: { input: 2 / M, output: 2 / M } },
|
|
1726
|
+
{ aliases: ["babbage-002"], short: { input: 0.4 / M, output: 0.4 / M } },
|
|
1727
|
+
// Explicit snapshots with their own current dedicated-table rows. Do not
|
|
1728
|
+
// generalize these dates: a plausible-looking unlisted snapshot is unpriced.
|
|
1729
|
+
{ aliases: ["gpt-4o-2024-05-13"], short: { input: 5 / M, output: 15 / M } },
|
|
1730
|
+
{ aliases: ["gpt-4-turbo-2024-04-09"], short: { input: 10 / M, output: 30 / M } },
|
|
1731
|
+
{ aliases: ["gpt-4-0613"], short: { input: 30 / M, output: 60 / M } },
|
|
1732
|
+
{
|
|
1733
|
+
aliases: ["gpt-3.5-turbo-0125"],
|
|
1734
|
+
short: { input: 0.5 / M, output: 1.5 / M }
|
|
1735
|
+
},
|
|
1736
|
+
{ aliases: ["gpt-3.5-turbo-1106"], short: { input: 1 / M, output: 2 / M } }
|
|
1737
|
+
];
|
|
1738
|
+
function directOpenAIModel(model) {
|
|
1739
|
+
return DIRECT_OPENAI_CATALOG.find((entry) => entry.aliases.includes(model));
|
|
1740
|
+
}
|
|
1741
|
+
var CANONICAL_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
1742
|
+
function isTextContentPart(value, type) {
|
|
1743
|
+
return value !== null && typeof value === "object" && value.type === type && typeof value.text === "string";
|
|
1744
|
+
}
|
|
1745
|
+
function hasUnmodeledDirectOpenAIContent(request) {
|
|
1746
|
+
if ("messages" in request) {
|
|
1747
|
+
if (!Array.isArray(request.messages)) return true;
|
|
1748
|
+
for (const message of request.messages) {
|
|
1749
|
+
if (message === null || typeof message !== "object") return true;
|
|
1750
|
+
const content = message.content;
|
|
1751
|
+
if (content === void 0 || content === null || typeof content === "string") continue;
|
|
1752
|
+
if (!Array.isArray(content) || content.some((part) => !isTextContentPart(part, "text"))) {
|
|
1753
|
+
return true;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
return false;
|
|
1757
|
+
}
|
|
1758
|
+
if ("input" in request) {
|
|
1759
|
+
const input = request.input;
|
|
1760
|
+
if (typeof input === "string") return false;
|
|
1761
|
+
if (!Array.isArray(input)) return true;
|
|
1762
|
+
for (const item of input) {
|
|
1763
|
+
if (item === null || typeof item !== "object") return true;
|
|
1764
|
+
const typed = item;
|
|
1765
|
+
switch (typed.type) {
|
|
1766
|
+
case "message": {
|
|
1767
|
+
const content = typed.content;
|
|
1768
|
+
if (typeof content === "string") break;
|
|
1769
|
+
if (!Array.isArray(content) || content.some((part) => !isTextContentPart(part, "input_text"))) {
|
|
1770
|
+
return true;
|
|
1771
|
+
}
|
|
1772
|
+
break;
|
|
1773
|
+
}
|
|
1774
|
+
case "function_call":
|
|
1775
|
+
break;
|
|
1776
|
+
case "function_call_output": {
|
|
1777
|
+
const output = typed.output;
|
|
1778
|
+
if (typeof output === "string") break;
|
|
1779
|
+
if (!Array.isArray(output) || output.some((part) => !isTextContentPart(part, "input_text"))) {
|
|
1780
|
+
return true;
|
|
1781
|
+
}
|
|
1782
|
+
break;
|
|
1783
|
+
}
|
|
1784
|
+
case "custom_tool_call":
|
|
1785
|
+
if (typeof typed.input !== "string") return true;
|
|
1786
|
+
break;
|
|
1787
|
+
case "custom_tool_call_output":
|
|
1788
|
+
if (typeof typed.output !== "string") return true;
|
|
1789
|
+
break;
|
|
1790
|
+
case "reasoning":
|
|
1791
|
+
break;
|
|
1792
|
+
default:
|
|
1793
|
+
return true;
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
return false;
|
|
1797
|
+
}
|
|
1798
|
+
return false;
|
|
1799
|
+
}
|
|
1800
|
+
function isEligibleDirectOpenAIContext(context) {
|
|
1801
|
+
const request = context?.request;
|
|
1802
|
+
const response = context?.response;
|
|
1803
|
+
if (context?.baseUrl !== void 0 && context.baseUrl !== CANONICAL_OPENAI_BASE_URL) return false;
|
|
1804
|
+
if (request && hasUnmodeledDirectOpenAIContent(request)) return false;
|
|
1805
|
+
const tier = response?.service_tier ?? response?.serviceTier ?? request?.service_tier ?? request?.serviceTier;
|
|
1806
|
+
if (tier !== void 0 && (typeof tier !== "string" || !["standard", "default"].includes(tier.toLowerCase()))) {
|
|
1807
|
+
return false;
|
|
1808
|
+
}
|
|
1809
|
+
if (["region", "inference_geo", "data_residency"].some((key) => request?.[key] !== void 0)) {
|
|
1810
|
+
return false;
|
|
1811
|
+
}
|
|
1812
|
+
if (["previous_response_id", "conversation", "prompt"].some((key) => request?.[key] !== void 0)) {
|
|
1813
|
+
return false;
|
|
1814
|
+
}
|
|
1815
|
+
const reasoning = request?.reasoning;
|
|
1816
|
+
if (reasoning !== null && typeof reasoning === "object" && "mode" in reasoning && reasoning.mode !== void 0) {
|
|
1817
|
+
return false;
|
|
1818
|
+
}
|
|
1819
|
+
if (request?.modalities !== void 0 && (!Array.isArray(request.modalities) || request.modalities.length === 0 || request.modalities.some((modality) => modality !== "text")) || ["audio", "image_generation", "web_search_options"].some((key) => request?.[key] !== void 0)) {
|
|
1820
|
+
return false;
|
|
1821
|
+
}
|
|
1822
|
+
if (Array.isArray(request?.tools) && request.tools.some(
|
|
1823
|
+
(tool2) => tool2 === null || typeof tool2 !== "object" || !["function", "custom"].includes(tool2.type)
|
|
1824
|
+
)) {
|
|
1825
|
+
return false;
|
|
1826
|
+
}
|
|
1827
|
+
return true;
|
|
1828
|
+
}
|
|
1829
|
+
function estimateDirectOpenAICost(model, usage, context) {
|
|
1830
|
+
if (!isEligibleDirectOpenAIContext(context)) return void 0;
|
|
1831
|
+
const entry = directOpenAIModel(model);
|
|
1832
|
+
if (!entry) return void 0;
|
|
1833
|
+
const { prompt_tokens, completion_tokens, cached_tokens, cache_write_tokens } = usage;
|
|
1834
|
+
const cached = cached_tokens ?? 0;
|
|
1835
|
+
const cacheWrite = cache_write_tokens ?? 0;
|
|
1836
|
+
if (![prompt_tokens, completion_tokens, cached, cacheWrite].every(
|
|
1837
|
+
(count) => Number.isSafeInteger(count) && count >= 0
|
|
1838
|
+
) || cached + cacheWrite > prompt_tokens) {
|
|
1839
|
+
return void 0;
|
|
1840
|
+
}
|
|
1841
|
+
const rates = entry.long !== void 0 && entry.contextBoundary !== void 0 && prompt_tokens > entry.contextBoundary ? entry.long : entry.short;
|
|
1842
|
+
if (cached > 0 && rates.cachedInput === void 0 || cacheWrite > 0 && rates.cacheWrite === void 0) {
|
|
1843
|
+
return void 0;
|
|
1844
|
+
}
|
|
1845
|
+
const ordinary = prompt_tokens - cached - cacheWrite;
|
|
1846
|
+
return ordinary * rates.input + cached * (rates.cachedInput ?? 0) + cacheWrite * (rates.cacheWrite ?? 0) + completion_tokens * rates.output;
|
|
1847
|
+
}
|
|
1479
1848
|
function estimateOpenAICost(model, promptTokens, completionTokens, cachedTokens) {
|
|
1480
|
-
return
|
|
1849
|
+
return estimateDirectOpenAICost(model, {
|
|
1850
|
+
prompt_tokens: promptTokens,
|
|
1851
|
+
completion_tokens: completionTokens,
|
|
1852
|
+
total_tokens: promptTokens + completionTokens,
|
|
1853
|
+
cached_tokens: cachedTokens
|
|
1854
|
+
});
|
|
1481
1855
|
}
|
|
1482
1856
|
function isOSeriesModel(model) {
|
|
1483
1857
|
return /^(o1|o3|o4-mini)/.test(model);
|
|
@@ -1485,6 +1859,15 @@ function isOSeriesModel(model) {
|
|
|
1485
1859
|
function supportsReasoningEffort(model) {
|
|
1486
1860
|
return isOSeriesModel(model) || /^gpt-5/.test(model);
|
|
1487
1861
|
}
|
|
1862
|
+
var GPT_56_REASONING_MODELS = /* @__PURE__ */ new Set([
|
|
1863
|
+
"gpt-5.6",
|
|
1864
|
+
"gpt-5.6-sol",
|
|
1865
|
+
"gpt-5.6-terra",
|
|
1866
|
+
"gpt-5.6-luna"
|
|
1867
|
+
]);
|
|
1868
|
+
function supportsMaxReasoningEffort(model) {
|
|
1869
|
+
return GPT_56_REASONING_MODELS.has(model);
|
|
1870
|
+
}
|
|
1488
1871
|
function supportsReasoningNone(model) {
|
|
1489
1872
|
return /^gpt-5\.[1-9]/.test(model);
|
|
1490
1873
|
}
|
|
@@ -1494,31 +1877,46 @@ function supportsXhigh(model) {
|
|
|
1494
1877
|
function clampReasoningEffort(model, effort) {
|
|
1495
1878
|
if (model.startsWith("gpt-5-pro")) return "high";
|
|
1496
1879
|
if (effort === "none" && !supportsReasoningNone(model)) return "minimal";
|
|
1880
|
+
if (effort === "max" && !supportsMaxReasoningEffort(model)) {
|
|
1881
|
+
return clampReasoningEffort(model, "xhigh");
|
|
1882
|
+
}
|
|
1497
1883
|
if (effort === "xhigh" && !supportsXhigh(model)) return "high";
|
|
1498
1884
|
return effort;
|
|
1499
1885
|
}
|
|
1500
|
-
function effortToReasoningEffort(effort) {
|
|
1501
|
-
return effort === "max" ? "xhigh" : effort;
|
|
1886
|
+
function effortToReasoningEffort(effort, model) {
|
|
1887
|
+
return effort === "max" && model !== void 0 && supportsMaxReasoningEffort(model) ? "max" : effort === "max" ? "xhigh" : effort;
|
|
1502
1888
|
}
|
|
1503
1889
|
function budgetToReasoningEffort(budget) {
|
|
1504
1890
|
if (budget <= 1024) return "low";
|
|
1505
1891
|
if (budget <= 8192) return "medium";
|
|
1506
1892
|
return "high";
|
|
1507
1893
|
}
|
|
1894
|
+
function resolveOpenAIReasoningEffort(model, resolved) {
|
|
1895
|
+
if (!supportsReasoningEffort(model)) return void 0;
|
|
1896
|
+
if (resolved.hasBudgetOverride) {
|
|
1897
|
+
return clampReasoningEffort(model, budgetToReasoningEffort(resolved.thinkingBudget));
|
|
1898
|
+
}
|
|
1899
|
+
if (!resolved.thinkingDisabled && resolved.activeEffort) {
|
|
1900
|
+
return clampReasoningEffort(model, effortToReasoningEffort(resolved.activeEffort, model));
|
|
1901
|
+
}
|
|
1902
|
+
return resolved.thinkingDisabled ? clampReasoningEffort(model, "none") : void 0;
|
|
1903
|
+
}
|
|
1904
|
+
var warnedChatMaxDowngrades = /* @__PURE__ */ new Set();
|
|
1905
|
+
function resolveOpenAIChatReasoningEffort(model, resolved) {
|
|
1906
|
+
const effort = resolveOpenAIReasoningEffort(model, resolved);
|
|
1907
|
+
if (effort !== "max") return effort;
|
|
1908
|
+
if (resolved.activeEffort === "max" && !warnedChatMaxDowngrades.has(model)) {
|
|
1909
|
+
warnedChatMaxDowngrades.add(model);
|
|
1910
|
+
console.warn(
|
|
1911
|
+
`[axl] OpenAI Chat Completions does not accept effort "max" for ${model}; using "xhigh". Use the OpenAI Responses provider for native "max".`
|
|
1912
|
+
);
|
|
1913
|
+
}
|
|
1914
|
+
return "xhigh";
|
|
1915
|
+
}
|
|
1508
1916
|
var openaiReasoningEmit = (body, resolved, model) => {
|
|
1509
1917
|
const oSeries = isOSeriesModel(model);
|
|
1510
1918
|
const reasoningCapable = supportsReasoningEffort(model);
|
|
1511
|
-
const
|
|
1512
|
-
let wireEffort;
|
|
1513
|
-
if (reasoningCapable) {
|
|
1514
|
-
if (hasBudgetOverride) {
|
|
1515
|
-
wireEffort = clampReasoningEffort(model, budgetToReasoningEffort(thinkingBudget));
|
|
1516
|
-
} else if (!thinkingDisabled && activeEffort) {
|
|
1517
|
-
wireEffort = clampReasoningEffort(model, effortToReasoningEffort(activeEffort));
|
|
1518
|
-
} else if (thinkingDisabled) {
|
|
1519
|
-
wireEffort = clampReasoningEffort(model, "none");
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1919
|
+
const wireEffort = resolveOpenAIChatReasoningEffort(model, resolved);
|
|
1522
1920
|
if (wireEffort) body.reasoning_effort = wireEffort;
|
|
1523
1921
|
return { stripTemperature: oSeries || reasoningCapable && wireEffort !== void 0 };
|
|
1524
1922
|
};
|
|
@@ -1528,7 +1926,7 @@ var OPENAI_PROFILE = {
|
|
|
1528
1926
|
defaultBaseUrl: "https://api.openai.com/v1",
|
|
1529
1927
|
envApiKey: "OPENAI_API_KEY",
|
|
1530
1928
|
envBaseUrl: "OPENAI_BASE_URL",
|
|
1531
|
-
pricing: { kind: "table", table: OPENAI_PRICING },
|
|
1929
|
+
pricing: { kind: "table", table: OPENAI_PRICING, match: "exact" },
|
|
1532
1930
|
reasoning: { emit: openaiReasoningEmit, capture: "none" },
|
|
1533
1931
|
roleFor: (role, model) => role === "system" && isOSeriesModel(model) ? "developer" : role,
|
|
1534
1932
|
maxTokensField: "max_completion_tokens",
|
|
@@ -1538,6 +1936,9 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
1538
1936
|
constructor(options = {}) {
|
|
1539
1937
|
super({ profile: OPENAI_PROFILE, ...options });
|
|
1540
1938
|
}
|
|
1939
|
+
computeCost(model, usage, _reportedCost, context) {
|
|
1940
|
+
return usage ? estimateDirectOpenAICost(model, usage, { ...context, baseUrl: this.baseUrl }) : void 0;
|
|
1941
|
+
}
|
|
1541
1942
|
};
|
|
1542
1943
|
|
|
1543
1944
|
// src/providers/openai-responses.ts
|
|
@@ -1598,7 +1999,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1598
1999
|
});
|
|
1599
2000
|
}
|
|
1600
2001
|
const json = await res.json();
|
|
1601
|
-
return this.parseResponse(json, options.model);
|
|
2002
|
+
return this.parseResponse(json, this.requestModel(body, options.model), body);
|
|
1602
2003
|
}
|
|
1603
2004
|
// ---------------------------------------------------------------------------
|
|
1604
2005
|
// stream - SSE streaming
|
|
@@ -1630,30 +2031,23 @@ var OpenAIResponsesProvider = class {
|
|
|
1630
2031
|
if (!res.body) {
|
|
1631
2032
|
throw new Error("OpenAI Responses stream has no body");
|
|
1632
2033
|
}
|
|
1633
|
-
yield* this.parseSSEStream(res.body, options.model);
|
|
2034
|
+
yield* this.parseSSEStream(res.body, this.requestModel(body, options.model), body);
|
|
1634
2035
|
}
|
|
1635
2036
|
// ---------------------------------------------------------------------------
|
|
1636
2037
|
// Internal: build request body
|
|
1637
2038
|
// ---------------------------------------------------------------------------
|
|
1638
2039
|
buildRequestBody(messages, options, stream) {
|
|
1639
|
-
const
|
|
1640
|
-
const
|
|
1641
|
-
const
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
wireEffort = clampReasoningEffort(options.model, budgetToReasoningEffort(thinkingBudget));
|
|
1646
|
-
} else if (!thinkingDisabled && activeEffort) {
|
|
1647
|
-
wireEffort = clampReasoningEffort(options.model, effortToReasoningEffort(activeEffort));
|
|
1648
|
-
} else if (thinkingDisabled) {
|
|
1649
|
-
wireEffort = clampReasoningEffort(options.model, "none");
|
|
1650
|
-
}
|
|
1651
|
-
}
|
|
2040
|
+
const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
2041
|
+
const oSeries = isOSeriesModel(effectiveModel);
|
|
2042
|
+
const reasoningCapable = supportsReasoningEffort(effectiveModel);
|
|
2043
|
+
const resolved = resolveThinkingOptions(options);
|
|
2044
|
+
const { includeThoughts } = resolved;
|
|
2045
|
+
const wireEffort = resolveOpenAIReasoningEffort(effectiveModel, resolved);
|
|
1652
2046
|
const stripTemp = oSeries || reasoningCapable && wireEffort !== void 0;
|
|
1653
2047
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
1654
2048
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
1655
2049
|
const body = {
|
|
1656
|
-
model:
|
|
2050
|
+
model: effectiveModel,
|
|
1657
2051
|
input: this.buildInput(nonSystemMessages),
|
|
1658
2052
|
store: false,
|
|
1659
2053
|
stream
|
|
@@ -1700,6 +2094,9 @@ var OpenAIResponsesProvider = class {
|
|
|
1700
2094
|
}
|
|
1701
2095
|
return body;
|
|
1702
2096
|
}
|
|
2097
|
+
requestModel(body, fallback) {
|
|
2098
|
+
return typeof body.model === "string" ? body.model : fallback;
|
|
2099
|
+
}
|
|
1703
2100
|
// ---------------------------------------------------------------------------
|
|
1704
2101
|
// Internal: message → input mapping
|
|
1705
2102
|
// ---------------------------------------------------------------------------
|
|
@@ -1767,7 +2164,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1767
2164
|
// ---------------------------------------------------------------------------
|
|
1768
2165
|
// Internal: parse non-streaming response
|
|
1769
2166
|
// ---------------------------------------------------------------------------
|
|
1770
|
-
parseResponse(json, model) {
|
|
2167
|
+
parseResponse(json, model, request) {
|
|
1771
2168
|
let content = "";
|
|
1772
2169
|
let thinkingContent = "";
|
|
1773
2170
|
const toolCalls = [];
|
|
@@ -1804,9 +2201,14 @@ var OpenAIResponsesProvider = class {
|
|
|
1804
2201
|
completion_tokens: json.usage.output_tokens,
|
|
1805
2202
|
total_tokens: json.usage.total_tokens,
|
|
1806
2203
|
reasoning_tokens: json.usage.output_tokens_details?.reasoning_tokens,
|
|
1807
|
-
cached_tokens: json.usage.input_tokens_details?.cached_tokens
|
|
2204
|
+
cached_tokens: json.usage.input_tokens_details?.cached_tokens,
|
|
2205
|
+
cache_write_tokens: json.usage.input_tokens_details?.cache_write_tokens
|
|
1808
2206
|
} : void 0;
|
|
1809
|
-
const cost = usage ?
|
|
2207
|
+
const cost = usage ? estimateDirectOpenAICost(json.model ?? model, usage, {
|
|
2208
|
+
baseUrl: this.baseUrl,
|
|
2209
|
+
request,
|
|
2210
|
+
response: json
|
|
2211
|
+
}) : void 0;
|
|
1810
2212
|
const providerMetadata = reasoningItems.length > 0 ? { openaiReasoningItems: reasoningItems } : void 0;
|
|
1811
2213
|
return {
|
|
1812
2214
|
content,
|
|
@@ -1820,7 +2222,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1820
2222
|
// ---------------------------------------------------------------------------
|
|
1821
2223
|
// Internal: parse SSE stream
|
|
1822
2224
|
// ---------------------------------------------------------------------------
|
|
1823
|
-
async *parseSSEStream(body, model) {
|
|
2225
|
+
async *parseSSEStream(body, model, request) {
|
|
1824
2226
|
const reader = body.getReader();
|
|
1825
2227
|
const decoder = new TextDecoder();
|
|
1826
2228
|
let buffer = "";
|
|
@@ -1848,7 +2250,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1848
2250
|
} catch {
|
|
1849
2251
|
continue;
|
|
1850
2252
|
}
|
|
1851
|
-
const chunk = this.handleStreamEvent(eventType, data, model, callIdMap);
|
|
2253
|
+
const chunk = this.handleStreamEvent(eventType, data, model, callIdMap, request);
|
|
1852
2254
|
if (chunk) {
|
|
1853
2255
|
yield chunk;
|
|
1854
2256
|
if (chunk.type === "done") return;
|
|
@@ -1862,7 +2264,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1862
2264
|
reader.releaseLock();
|
|
1863
2265
|
}
|
|
1864
2266
|
}
|
|
1865
|
-
handleStreamEvent(eventType, data, model, callIdMap) {
|
|
2267
|
+
handleStreamEvent(eventType, data, model, callIdMap, request) {
|
|
1866
2268
|
switch (eventType) {
|
|
1867
2269
|
case "response.output_text.delta":
|
|
1868
2270
|
return { type: "text_delta", content: data.delta ?? "" };
|
|
@@ -1896,19 +2298,19 @@ var OpenAIResponsesProvider = class {
|
|
|
1896
2298
|
completion_tokens: response.usage.output_tokens,
|
|
1897
2299
|
total_tokens: response.usage.total_tokens,
|
|
1898
2300
|
reasoning_tokens: response.usage.output_tokens_details?.reasoning_tokens,
|
|
1899
|
-
cached_tokens: response.usage.input_tokens_details?.cached_tokens
|
|
2301
|
+
cached_tokens: response.usage.input_tokens_details?.cached_tokens,
|
|
2302
|
+
cache_write_tokens: response.usage.input_tokens_details?.cache_write_tokens
|
|
1900
2303
|
} : void 0;
|
|
1901
2304
|
const reasoningItems = response?.output?.filter((item) => item.type === "reasoning") ?? [];
|
|
1902
2305
|
const providerMetadata = reasoningItems.length > 0 ? { openaiReasoningItems: reasoningItems } : void 0;
|
|
1903
2306
|
return {
|
|
1904
2307
|
type: "done",
|
|
1905
2308
|
usage,
|
|
1906
|
-
cost: usage ?
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
) : void 0,
|
|
2309
|
+
cost: usage ? estimateDirectOpenAICost(response?.model ?? model, usage, {
|
|
2310
|
+
baseUrl: this.baseUrl,
|
|
2311
|
+
request,
|
|
2312
|
+
response
|
|
2313
|
+
}) : void 0,
|
|
1912
2314
|
providerMetadata
|
|
1913
2315
|
};
|
|
1914
2316
|
}
|
|
@@ -1942,68 +2344,357 @@ var OpenAIResponsesProvider = class {
|
|
|
1942
2344
|
|
|
1943
2345
|
// src/providers/anthropic.ts
|
|
1944
2346
|
var ANTHROPIC_API_VERSION = "2023-06-01";
|
|
1945
|
-
var
|
|
1946
|
-
"claude-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
"claude-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
"claude-
|
|
1960
|
-
|
|
1961
|
-
|
|
2347
|
+
var CLAUDE_CAPABILITIES = {
|
|
2348
|
+
"claude-fable-5": {
|
|
2349
|
+
thinking: "adaptive-always-on",
|
|
2350
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
2351
|
+
manualBudget: false,
|
|
2352
|
+
stripTemperature: true
|
|
2353
|
+
},
|
|
2354
|
+
"claude-opus-5": {
|
|
2355
|
+
thinking: "adaptive-default-on",
|
|
2356
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
2357
|
+
manualBudget: false,
|
|
2358
|
+
stripTemperature: true,
|
|
2359
|
+
disableAt: "high"
|
|
2360
|
+
},
|
|
2361
|
+
"claude-sonnet-5": {
|
|
2362
|
+
thinking: "adaptive-default-on",
|
|
2363
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
2364
|
+
manualBudget: false,
|
|
2365
|
+
stripTemperature: true,
|
|
2366
|
+
disableAt: "max"
|
|
2367
|
+
},
|
|
2368
|
+
"claude-opus-4-8": {
|
|
2369
|
+
thinking: "adaptive-optional",
|
|
2370
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
2371
|
+
manualBudget: false,
|
|
2372
|
+
stripTemperature: true
|
|
2373
|
+
},
|
|
2374
|
+
"claude-opus-4-7": {
|
|
2375
|
+
thinking: "adaptive-optional",
|
|
2376
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
2377
|
+
manualBudget: false,
|
|
2378
|
+
stripTemperature: true
|
|
2379
|
+
},
|
|
2380
|
+
"claude-opus-4-6": {
|
|
2381
|
+
thinking: "adaptive-optional",
|
|
2382
|
+
effortLevels: ["low", "medium", "high", "max"],
|
|
2383
|
+
manualBudget: true
|
|
2384
|
+
},
|
|
2385
|
+
"claude-sonnet-4-6": {
|
|
2386
|
+
thinking: "adaptive-optional",
|
|
2387
|
+
effortLevels: ["low", "medium", "high", "max"],
|
|
2388
|
+
manualBudget: true
|
|
2389
|
+
},
|
|
2390
|
+
"claude-opus-4-5": {
|
|
2391
|
+
thinking: "effort-only",
|
|
2392
|
+
effortLevels: ["low", "medium", "high"],
|
|
2393
|
+
manualBudget: true
|
|
2394
|
+
},
|
|
2395
|
+
"claude-opus-4-5-20251101": {
|
|
2396
|
+
thinking: "effort-only",
|
|
2397
|
+
effortLevels: ["low", "medium", "high"],
|
|
2398
|
+
manualBudget: true
|
|
2399
|
+
}
|
|
1962
2400
|
};
|
|
1963
|
-
var
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2401
|
+
var LEGACY_CLAUDE_CAPABILITY = {
|
|
2402
|
+
thinking: "legacy-manual",
|
|
2403
|
+
manualBudget: true
|
|
2404
|
+
};
|
|
2405
|
+
var LEGACY_CLAUDE_MODELS = /* @__PURE__ */ new Set([
|
|
2406
|
+
"claude-opus-4-1",
|
|
2407
|
+
"claude-opus-4-1-20250805",
|
|
2408
|
+
"claude-sonnet-4-5",
|
|
2409
|
+
"claude-sonnet-4-5-20250929",
|
|
2410
|
+
"claude-haiku-4-5",
|
|
2411
|
+
"claude-haiku-4-5-20251001",
|
|
2412
|
+
"claude-sonnet-4",
|
|
2413
|
+
"claude-sonnet-4-20250514",
|
|
2414
|
+
"claude-opus-4",
|
|
2415
|
+
"claude-opus-4-20250514",
|
|
2416
|
+
"claude-3-7-sonnet",
|
|
2417
|
+
"claude-3-7-sonnet-20250219",
|
|
2418
|
+
"claude-3-5-sonnet",
|
|
2419
|
+
"claude-3-5-sonnet-20241022",
|
|
2420
|
+
"claude-3-5-sonnet-20240620",
|
|
2421
|
+
"claude-3-5-haiku",
|
|
2422
|
+
"claude-3-5-haiku-20241022",
|
|
2423
|
+
"claude-3-opus",
|
|
2424
|
+
"claude-3-opus-20240229",
|
|
2425
|
+
"claude-3-sonnet",
|
|
2426
|
+
"claude-3-sonnet-20240229",
|
|
2427
|
+
"claude-3-haiku",
|
|
2428
|
+
"claude-3-haiku-20240307"
|
|
2429
|
+
]);
|
|
2430
|
+
var PRE_INFERENCE_GEO_MODELS = /* @__PURE__ */ new Set([
|
|
2431
|
+
...LEGACY_CLAUDE_MODELS,
|
|
2432
|
+
"claude-opus-4-5",
|
|
2433
|
+
"claude-opus-4-5-20251101"
|
|
2434
|
+
]);
|
|
2435
|
+
function resolveClaudeCapability(model) {
|
|
2436
|
+
return CLAUDE_CAPABILITIES[model] ?? (LEGACY_CLAUDE_MODELS.has(model) ? LEGACY_CLAUDE_CAPABILITY : void 0);
|
|
2437
|
+
}
|
|
2438
|
+
var ANTHROPIC_RATES = {
|
|
2439
|
+
"claude-fable-5": { input: 1e-5, output: 5e-5 },
|
|
2440
|
+
"claude-opus-5": { input: 5e-6, output: 25e-6 },
|
|
2441
|
+
"claude-sonnet-5": { input: 3e-6, output: 15e-6, sonnet5Intro: true },
|
|
2442
|
+
"claude-opus-4-8": { input: 5e-6, output: 25e-6 },
|
|
2443
|
+
"claude-opus-4-7": { input: 5e-6, output: 25e-6 },
|
|
2444
|
+
"claude-opus-4-6": { input: 5e-6, output: 25e-6 },
|
|
2445
|
+
"claude-sonnet-4-6": { input: 3e-6, output: 15e-6 },
|
|
2446
|
+
"claude-opus-4-5": { input: 5e-6, output: 25e-6 },
|
|
2447
|
+
"claude-opus-4-5-20251101": { input: 5e-6, output: 25e-6 },
|
|
2448
|
+
"claude-opus-4-1": { input: 15e-6, output: 75e-6 },
|
|
2449
|
+
"claude-opus-4-1-20250805": { input: 15e-6, output: 75e-6 },
|
|
2450
|
+
"claude-sonnet-4-5": { input: 3e-6, output: 15e-6 },
|
|
2451
|
+
"claude-sonnet-4-5-20250929": { input: 3e-6, output: 15e-6 },
|
|
2452
|
+
"claude-haiku-4-5": { input: 1e-6, output: 5e-6 },
|
|
2453
|
+
"claude-haiku-4-5-20251001": { input: 1e-6, output: 5e-6 }
|
|
2454
|
+
};
|
|
2455
|
+
function isValidTokenCount(value) {
|
|
2456
|
+
return value !== void 0 && Number.isSafeInteger(value) && value >= 0;
|
|
2457
|
+
}
|
|
2458
|
+
function estimateAnthropicCost(model, usage, now = /* @__PURE__ */ new Date()) {
|
|
2459
|
+
if (!model || !isValidTokenCount(usage.inputTokens) || !isValidTokenCount(usage.outputTokens)) {
|
|
2460
|
+
return void 0;
|
|
1975
2461
|
}
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
const cacheRead = cacheReadTokens ?? 0;
|
|
1979
|
-
const
|
|
1980
|
-
const
|
|
1981
|
-
|
|
2462
|
+
const rate = ANTHROPIC_RATES[model];
|
|
2463
|
+
if (!rate) return void 0;
|
|
2464
|
+
const cacheRead = usage.cacheReadTokens ?? 0;
|
|
2465
|
+
const cacheWrite5m = usage.cacheWrite5mTokens ?? 0;
|
|
2466
|
+
const cacheWrite1h = usage.cacheWrite1hTokens ?? 0;
|
|
2467
|
+
if (!isValidTokenCount(cacheRead) || !isValidTokenCount(cacheWrite5m) || !isValidTokenCount(cacheWrite1h)) {
|
|
2468
|
+
return void 0;
|
|
2469
|
+
}
|
|
2470
|
+
const cacheWriteTotal = safeTokenSum(cacheWrite5m, cacheWrite1h);
|
|
2471
|
+
if (cacheWriteTotal === void 0 || safeTokenSum(usage.inputTokens, cacheRead, cacheWriteTotal) === void 0) {
|
|
2472
|
+
return void 0;
|
|
2473
|
+
}
|
|
2474
|
+
if (usage.aggregateCacheWriteTokens !== void 0 && (!isValidTokenCount(usage.aggregateCacheWriteTokens) || usage.aggregateCacheWriteTokens !== cacheWriteTotal)) {
|
|
2475
|
+
return void 0;
|
|
2476
|
+
}
|
|
2477
|
+
const { input, output } = rate.sonnet5Intro && now < /* @__PURE__ */ new Date("2026-09-01T00:00:00Z") ? { input: 2e-6, output: 1e-5 } : rate;
|
|
2478
|
+
return usage.inputTokens * input + cacheRead * input * 0.1 + cacheWrite5m * input * 1.25 + cacheWrite1h * input * 2 + usage.outputTokens * output;
|
|
1982
2479
|
}
|
|
1983
2480
|
var THINKING_BUDGETS = {
|
|
1984
2481
|
low: 1024,
|
|
1985
2482
|
medium: 5e3,
|
|
1986
2483
|
high: 1e4,
|
|
2484
|
+
xhigh: 2e4,
|
|
1987
2485
|
// 30000 (not 32000) to stay under the 32K max_tokens limit on Opus 4/4.1.
|
|
1988
2486
|
// With auto-bump (+1024), max_tokens becomes 31024 which fits all models.
|
|
1989
2487
|
max: 3e4
|
|
1990
2488
|
};
|
|
1991
|
-
function
|
|
1992
|
-
|
|
2489
|
+
function clampAnthropicEffort(capability, effort) {
|
|
2490
|
+
const levels = capability.effortLevels;
|
|
2491
|
+
if (!levels) return effort === "max" || effort === "xhigh" ? "high" : effort;
|
|
2492
|
+
if (levels.includes(effort)) return effort;
|
|
2493
|
+
return "high";
|
|
2494
|
+
}
|
|
2495
|
+
function budgetToClaudeEffort(budget) {
|
|
2496
|
+
if (budget <= THINKING_BUDGETS.low) return "low";
|
|
2497
|
+
if (budget <= THINKING_BUDGETS.medium) return "medium";
|
|
2498
|
+
if (budget <= THINKING_BUDGETS.high) return "high";
|
|
2499
|
+
if (budget <= THINKING_BUDGETS.xhigh) return "xhigh";
|
|
2500
|
+
return "max";
|
|
2501
|
+
}
|
|
2502
|
+
var warnedFableDisable = /* @__PURE__ */ new Set();
|
|
2503
|
+
function warnFableDisable(model) {
|
|
2504
|
+
if (warnedFableDisable.has(model)) return;
|
|
2505
|
+
warnedFableDisable.add(model);
|
|
2506
|
+
console.warn(
|
|
2507
|
+
`[axl] thinking cannot be disabled on Anthropic ${model}; using adaptive thinking at effort 'low'.`
|
|
2508
|
+
);
|
|
1993
2509
|
}
|
|
1994
|
-
function
|
|
1995
|
-
|
|
2510
|
+
function resolveClaudeThinking(model, resolved) {
|
|
2511
|
+
const capability = resolveClaudeCapability(model);
|
|
2512
|
+
if (!capability) return { stripTemperature: false };
|
|
2513
|
+
const activeEffort = resolved.activeEffort ? clampAnthropicEffort(capability, resolved.activeEffort) : void 0;
|
|
2514
|
+
const isClaude5 = capability.thinking === "adaptive-default-on" || capability.thinking === "adaptive-always-on";
|
|
2515
|
+
if (isClaude5) {
|
|
2516
|
+
if (resolved.hasBudgetOverride) {
|
|
2517
|
+
const effort = clampAnthropicEffort(
|
|
2518
|
+
capability,
|
|
2519
|
+
budgetToClaudeEffort(resolved.thinkingBudget)
|
|
2520
|
+
);
|
|
2521
|
+
return {
|
|
2522
|
+
thinking: { type: "adaptive" },
|
|
2523
|
+
outputConfig: { effort },
|
|
2524
|
+
stripTemperature: true
|
|
2525
|
+
};
|
|
2526
|
+
}
|
|
2527
|
+
if (resolved.thinkingDisabled) {
|
|
2528
|
+
if (capability.thinking === "adaptive-always-on") {
|
|
2529
|
+
warnFableDisable(model);
|
|
2530
|
+
return {
|
|
2531
|
+
thinking: { type: "adaptive" },
|
|
2532
|
+
outputConfig: { effort: "low" },
|
|
2533
|
+
stripTemperature: true
|
|
2534
|
+
};
|
|
2535
|
+
}
|
|
2536
|
+
if (activeEffort && capability.disableAt && !isEffortAtOrBelow(activeEffort, capability.disableAt)) {
|
|
2537
|
+
return {
|
|
2538
|
+
thinking: { type: "adaptive" },
|
|
2539
|
+
outputConfig: { effort: activeEffort },
|
|
2540
|
+
stripTemperature: true
|
|
2541
|
+
};
|
|
2542
|
+
}
|
|
2543
|
+
return {
|
|
2544
|
+
thinking: { type: "disabled" },
|
|
2545
|
+
outputConfig: activeEffort ? { effort: activeEffort } : void 0,
|
|
2546
|
+
stripTemperature: true
|
|
2547
|
+
};
|
|
2548
|
+
}
|
|
2549
|
+
return activeEffort ? {
|
|
2550
|
+
thinking: { type: "adaptive" },
|
|
2551
|
+
outputConfig: { effort: activeEffort },
|
|
2552
|
+
stripTemperature: true
|
|
2553
|
+
} : { stripTemperature: true };
|
|
2554
|
+
}
|
|
2555
|
+
if (resolved.hasBudgetOverride) {
|
|
2556
|
+
if (!capability.manualBudget) {
|
|
2557
|
+
const effort = clampAnthropicEffort(
|
|
2558
|
+
capability,
|
|
2559
|
+
budgetToClaudeEffort(resolved.thinkingBudget)
|
|
2560
|
+
);
|
|
2561
|
+
return capability.thinking === "adaptive-optional" ? {
|
|
2562
|
+
thinking: { type: "adaptive" },
|
|
2563
|
+
outputConfig: { effort },
|
|
2564
|
+
stripTemperature: capability.stripTemperature ?? true
|
|
2565
|
+
} : { outputConfig: { effort }, stripTemperature: capability.stripTemperature ?? false };
|
|
2566
|
+
}
|
|
2567
|
+
return {
|
|
2568
|
+
thinking: { type: "enabled", budget_tokens: resolved.thinkingBudget },
|
|
2569
|
+
outputConfig: activeEffort && capability.effortLevels ? { effort: activeEffort } : void 0,
|
|
2570
|
+
manualBudget: resolved.thinkingBudget,
|
|
2571
|
+
stripTemperature: capability.stripTemperature ?? true
|
|
2572
|
+
};
|
|
2573
|
+
}
|
|
2574
|
+
if (resolved.thinkingDisabled) {
|
|
2575
|
+
return {
|
|
2576
|
+
outputConfig: activeEffort && capability.effortLevels ? { effort: activeEffort } : void 0,
|
|
2577
|
+
stripTemperature: capability.stripTemperature ?? false
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
if (activeEffort && capability.thinking === "adaptive-optional") {
|
|
2581
|
+
return {
|
|
2582
|
+
thinking: { type: "adaptive" },
|
|
2583
|
+
outputConfig: { effort: activeEffort },
|
|
2584
|
+
stripTemperature: capability.stripTemperature ?? true
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2587
|
+
if (activeEffort && capability.thinking === "effort-only") {
|
|
2588
|
+
return { outputConfig: { effort: activeEffort }, stripTemperature: false };
|
|
2589
|
+
}
|
|
2590
|
+
if (activeEffort) {
|
|
2591
|
+
const budget = THINKING_BUDGETS[activeEffort] ?? THINKING_BUDGETS.medium;
|
|
2592
|
+
return {
|
|
2593
|
+
thinking: { type: "enabled", budget_tokens: budget },
|
|
2594
|
+
manualBudget: budget,
|
|
2595
|
+
stripTemperature: true
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
return { stripTemperature: capability.stripTemperature ?? false };
|
|
1996
2599
|
}
|
|
1997
|
-
function
|
|
1998
|
-
|
|
2600
|
+
function isEffortAtOrBelow(effort, ceiling) {
|
|
2601
|
+
const levels = ["low", "medium", "high", "xhigh", "max"];
|
|
2602
|
+
return levels.indexOf(effort) <= levels.indexOf(ceiling);
|
|
1999
2603
|
}
|
|
2000
|
-
function
|
|
2001
|
-
return
|
|
2604
|
+
function pricingContextFromBody(body) {
|
|
2605
|
+
return {
|
|
2606
|
+
model: typeof body.model === "string" ? body.model : void 0,
|
|
2607
|
+
unpricedModifier: isAnthropicModifier("inference_geo", body.inference_geo) || isAnthropicModifier("speed", body.speed) || hasAnthropicServerTool(body.tools) || body.fallbacks !== void 0
|
|
2608
|
+
};
|
|
2002
2609
|
}
|
|
2003
|
-
function
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2610
|
+
function isModifiedAnthropicResponse(json, model) {
|
|
2611
|
+
return isAnthropicResponseModifier("inference_geo", json.inference_geo, model) || isAnthropicResponseModifier("speed", json.speed, model) || isAnthropicResponseModifier("inference_geo", json.usage?.inference_geo, model) || isAnthropicResponseModifier("speed", json.usage?.speed, model) || hasBilledUnmodeledUsage(json.usage?.server_tool_use) || hasUnmodeledIterations(json.usage?.iterations);
|
|
2612
|
+
}
|
|
2613
|
+
function isAnthropicModifier(kind, value) {
|
|
2614
|
+
if (value === void 0) return false;
|
|
2615
|
+
return kind === "speed" ? value !== "standard" : value !== "global";
|
|
2616
|
+
}
|
|
2617
|
+
function isAnthropicResponseModifier(kind, value, model) {
|
|
2618
|
+
if (value === void 0) return false;
|
|
2619
|
+
if (kind === "speed") return value !== "standard";
|
|
2620
|
+
if (value === "global") return false;
|
|
2621
|
+
return value !== "not_available" || model === void 0 || !PRE_INFERENCE_GEO_MODELS.has(model);
|
|
2622
|
+
}
|
|
2623
|
+
function hasAnthropicServerTool(tools) {
|
|
2624
|
+
return Array.isArray(tools) && tools.some(
|
|
2625
|
+
(tool2) => tool2 !== null && typeof tool2 === "object" && typeof tool2.type === "string"
|
|
2626
|
+
);
|
|
2627
|
+
}
|
|
2628
|
+
function hasBilledUnmodeledUsage(value) {
|
|
2629
|
+
return value !== void 0 && value !== 0;
|
|
2630
|
+
}
|
|
2631
|
+
function hasUnmodeledIterations(value) {
|
|
2632
|
+
return value !== void 0;
|
|
2633
|
+
}
|
|
2634
|
+
function hasFallbackIteration(value) {
|
|
2635
|
+
return Array.isArray(value) && value.some(
|
|
2636
|
+
(iteration) => iteration !== null && typeof iteration === "object" && iteration.type === "fallback_message"
|
|
2637
|
+
);
|
|
2638
|
+
}
|
|
2639
|
+
function safeTokenSum(...values) {
|
|
2640
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
2641
|
+
return Number.isSafeInteger(total) ? total : void 0;
|
|
2642
|
+
}
|
|
2643
|
+
function normalizeAnthropicUsage(raw) {
|
|
2644
|
+
const inputTokens = raw.input_tokens;
|
|
2645
|
+
const outputTokens = raw.output_tokens;
|
|
2646
|
+
const cacheReadTokens = raw.cache_read_input_tokens ?? 0;
|
|
2647
|
+
const cacheWrite5mTokens = raw.cache_creation?.ephemeral_5m_input_tokens ?? 0;
|
|
2648
|
+
const cacheWrite1hTokens = raw.cache_creation?.ephemeral_1h_input_tokens ?? 0;
|
|
2649
|
+
if (!isValidTokenCount(inputTokens) || !isValidTokenCount(outputTokens) || !isValidTokenCount(cacheReadTokens) || !isValidTokenCount(cacheWrite5mTokens) || !isValidTokenCount(cacheWrite1hTokens)) {
|
|
2650
|
+
return void 0;
|
|
2651
|
+
}
|
|
2652
|
+
const hasTtlBreakdown = raw.cache_creation?.ephemeral_5m_input_tokens !== void 0 || raw.cache_creation?.ephemeral_1h_input_tokens !== void 0;
|
|
2653
|
+
const aggregateCacheWriteTokens = raw.cache_creation_input_tokens;
|
|
2654
|
+
const normalizedCacheWrite = hasTtlBreakdown ? safeTokenSum(cacheWrite5mTokens, cacheWrite1hTokens) : void 0;
|
|
2655
|
+
if (hasTtlBreakdown && normalizedCacheWrite === void 0) return void 0;
|
|
2656
|
+
const promptCacheWrite = aggregateCacheWriteTokens ?? normalizedCacheWrite ?? 0;
|
|
2657
|
+
if (!isValidTokenCount(promptCacheWrite)) return void 0;
|
|
2658
|
+
const promptTokens = safeTokenSum(inputTokens, cacheReadTokens, promptCacheWrite);
|
|
2659
|
+
if (promptTokens === void 0) return void 0;
|
|
2660
|
+
const totalTokens = safeTokenSum(promptTokens, outputTokens);
|
|
2661
|
+
if (totalTokens === void 0) return void 0;
|
|
2662
|
+
return {
|
|
2663
|
+
usage: {
|
|
2664
|
+
prompt_tokens: promptTokens,
|
|
2665
|
+
completion_tokens: outputTokens,
|
|
2666
|
+
total_tokens: totalTokens,
|
|
2667
|
+
cached_tokens: cacheReadTokens > 0 ? cacheReadTokens : void 0,
|
|
2668
|
+
// The aggregate is still useful public usage telemetry even though it
|
|
2669
|
+
// cannot be priced without the 5m/1h split. Keep pricingUsage separate
|
|
2670
|
+
// so estimateAnthropicCost continues to fail closed in that case.
|
|
2671
|
+
cache_write_tokens: (normalizedCacheWrite ?? aggregateCacheWriteTokens ?? 0) > 0 ? normalizedCacheWrite ?? aggregateCacheWriteTokens : void 0
|
|
2672
|
+
},
|
|
2673
|
+
pricingUsage: {
|
|
2674
|
+
inputTokens,
|
|
2675
|
+
outputTokens,
|
|
2676
|
+
cacheReadTokens,
|
|
2677
|
+
cacheWrite5mTokens,
|
|
2678
|
+
cacheWrite1hTokens,
|
|
2679
|
+
aggregateCacheWriteTokens
|
|
2680
|
+
}
|
|
2681
|
+
};
|
|
2682
|
+
}
|
|
2683
|
+
function mergeAnthropicUsage(initial, update) {
|
|
2684
|
+
return {
|
|
2685
|
+
input_tokens: update.input_tokens ?? initial?.input_tokens,
|
|
2686
|
+
output_tokens: update.output_tokens ?? initial?.output_tokens,
|
|
2687
|
+
cache_read_input_tokens: update.cache_read_input_tokens ?? initial?.cache_read_input_tokens,
|
|
2688
|
+
cache_creation_input_tokens: update.cache_creation_input_tokens ?? initial?.cache_creation_input_tokens,
|
|
2689
|
+
cache_creation: {
|
|
2690
|
+
ephemeral_5m_input_tokens: update.cache_creation?.ephemeral_5m_input_tokens ?? initial?.cache_creation?.ephemeral_5m_input_tokens,
|
|
2691
|
+
ephemeral_1h_input_tokens: update.cache_creation?.ephemeral_1h_input_tokens ?? initial?.cache_creation?.ephemeral_1h_input_tokens
|
|
2692
|
+
},
|
|
2693
|
+
speed: update.speed ?? initial?.speed,
|
|
2694
|
+
inference_geo: update.inference_geo ?? initial?.inference_geo,
|
|
2695
|
+
iterations: update.iterations ?? initial?.iterations,
|
|
2696
|
+
server_tool_use: update.server_tool_use ?? initial?.server_tool_use
|
|
2697
|
+
};
|
|
2007
2698
|
}
|
|
2008
2699
|
var AnthropicProvider = class {
|
|
2009
2700
|
name = "anthropic";
|
|
@@ -2041,6 +2732,7 @@ var AnthropicProvider = class {
|
|
|
2041
2732
|
async chat(messages, options) {
|
|
2042
2733
|
const headers = this.buildHeaders(await this.resolveKey());
|
|
2043
2734
|
const body = this.buildRequestBody(messages, options, false);
|
|
2735
|
+
const pricingContext = pricingContextFromBody(body);
|
|
2044
2736
|
const res = await fetchWithRetry(
|
|
2045
2737
|
`${this.baseUrl}/messages`,
|
|
2046
2738
|
{
|
|
@@ -2063,7 +2755,7 @@ var AnthropicProvider = class {
|
|
|
2063
2755
|
});
|
|
2064
2756
|
}
|
|
2065
2757
|
const json = await res.json();
|
|
2066
|
-
return this.parseResponse(json,
|
|
2758
|
+
return this.parseResponse(json, pricingContext);
|
|
2067
2759
|
}
|
|
2068
2760
|
// ---------------------------------------------------------------------------
|
|
2069
2761
|
// stream - SSE streaming completion
|
|
@@ -2071,6 +2763,7 @@ var AnthropicProvider = class {
|
|
|
2071
2763
|
async *stream(messages, options) {
|
|
2072
2764
|
const headers = this.buildHeaders(await this.resolveKey());
|
|
2073
2765
|
const body = this.buildRequestBody(messages, options, true);
|
|
2766
|
+
const pricingContext = pricingContextFromBody(body);
|
|
2074
2767
|
const res = await fetchWithRetry(
|
|
2075
2768
|
`${this.baseUrl}/messages`,
|
|
2076
2769
|
{
|
|
@@ -2095,7 +2788,7 @@ var AnthropicProvider = class {
|
|
|
2095
2788
|
if (!res.body) {
|
|
2096
2789
|
throw new Error("Anthropic stream response has no body");
|
|
2097
2790
|
}
|
|
2098
|
-
yield* this.parseSSEStream(res.body,
|
|
2791
|
+
yield* this.parseSSEStream(res.body, pricingContext);
|
|
2099
2792
|
}
|
|
2100
2793
|
// ---------------------------------------------------------------------------
|
|
2101
2794
|
// Internal: request building
|
|
@@ -2119,11 +2812,12 @@ var AnthropicProvider = class {
|
|
|
2119
2812
|
return `Anthropic API error (${status}): ${body}`;
|
|
2120
2813
|
}
|
|
2121
2814
|
buildRequestBody(messages, options, stream) {
|
|
2815
|
+
const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
2122
2816
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
2123
2817
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
2124
2818
|
const systemText = systemMessages.map((m) => m.content).join("\n\n");
|
|
2125
2819
|
const body = {
|
|
2126
|
-
model:
|
|
2820
|
+
model: effectiveModel,
|
|
2127
2821
|
messages: this.mapMessages(nonSystemMessages),
|
|
2128
2822
|
max_tokens: options.maxTokens ?? 4096,
|
|
2129
2823
|
stream
|
|
@@ -2140,35 +2834,16 @@ var AnthropicProvider = class {
|
|
|
2140
2834
|
if (options.toolChoice !== void 0) {
|
|
2141
2835
|
body.tool_choice = this.mapToolChoice(options.toolChoice);
|
|
2142
2836
|
}
|
|
2143
|
-
const
|
|
2144
|
-
|
|
2145
|
-
if (
|
|
2146
|
-
|
|
2147
|
-
const currentMax = body.max_tokens;
|
|
2148
|
-
if (currentMax < thinkingBudget + 1024) {
|
|
2149
|
-
body.max_tokens = thinkingBudget + 1024;
|
|
2150
|
-
}
|
|
2151
|
-
if (resolvedEffort && supportsEffort(options.model)) {
|
|
2152
|
-
body.output_config = { effort: resolvedEffort };
|
|
2153
|
-
}
|
|
2154
|
-
} else if (thinkingDisabled) {
|
|
2155
|
-
if (resolvedEffort && supportsEffort(options.model)) {
|
|
2156
|
-
body.output_config = { effort: resolvedEffort };
|
|
2157
|
-
}
|
|
2158
|
-
} else if (resolvedEffort && supportsAdaptiveThinking(options.model)) {
|
|
2159
|
-
body.thinking = { type: "adaptive" };
|
|
2160
|
-
body.output_config = { effort: resolvedEffort };
|
|
2161
|
-
} else if (resolvedEffort && supportsEffort(options.model)) {
|
|
2162
|
-
body.output_config = { effort: resolvedEffort };
|
|
2163
|
-
} else if (resolvedEffort) {
|
|
2164
|
-
const budget = THINKING_BUDGETS[resolvedEffort] ?? 5e3;
|
|
2165
|
-
body.thinking = { type: "enabled", budget_tokens: budget };
|
|
2837
|
+
const thinking = resolveClaudeThinking(effectiveModel, resolveThinkingOptions(options));
|
|
2838
|
+
if (thinking.thinking) body.thinking = thinking.thinking;
|
|
2839
|
+
if (thinking.outputConfig) body.output_config = thinking.outputConfig;
|
|
2840
|
+
if (thinking.manualBudget) {
|
|
2166
2841
|
const currentMax = body.max_tokens;
|
|
2167
|
-
if (currentMax <
|
|
2168
|
-
body.max_tokens =
|
|
2842
|
+
if (currentMax < thinking.manualBudget + 1024) {
|
|
2843
|
+
body.max_tokens = thinking.manualBudget + 1024;
|
|
2169
2844
|
}
|
|
2170
2845
|
}
|
|
2171
|
-
if (options.temperature !== void 0 && !
|
|
2846
|
+
if (options.temperature !== void 0 && !thinking.stripTemperature) {
|
|
2172
2847
|
body.temperature = options.temperature;
|
|
2173
2848
|
}
|
|
2174
2849
|
if (options.responseFormat && options.responseFormat.type !== "text") {
|
|
@@ -2193,8 +2868,9 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2193
2868
|
const result = [];
|
|
2194
2869
|
for (const msg of messages) {
|
|
2195
2870
|
if (msg.role === "assistant") {
|
|
2871
|
+
const replayedThinking = this.getReplayedThinkingBlocks(msg);
|
|
2196
2872
|
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
2197
|
-
const content = [];
|
|
2873
|
+
const content = [...replayedThinking];
|
|
2198
2874
|
if (msg.content) {
|
|
2199
2875
|
content.push({ type: "text", text: msg.content });
|
|
2200
2876
|
}
|
|
@@ -2213,6 +2889,10 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2213
2889
|
});
|
|
2214
2890
|
}
|
|
2215
2891
|
result.push({ role: "assistant", content });
|
|
2892
|
+
} else if (replayedThinking.length > 0) {
|
|
2893
|
+
const content = [...replayedThinking];
|
|
2894
|
+
if (msg.content) content.push({ type: "text", text: msg.content });
|
|
2895
|
+
result.push({ role: "assistant", content });
|
|
2216
2896
|
} else {
|
|
2217
2897
|
result.push({ role: "assistant", content: msg.content });
|
|
2218
2898
|
}
|
|
@@ -2233,6 +2913,13 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2233
2913
|
}
|
|
2234
2914
|
return this.mergeConsecutiveRoles(result);
|
|
2235
2915
|
}
|
|
2916
|
+
getReplayedThinkingBlocks(msg) {
|
|
2917
|
+
const blocks = msg.providerMetadata?.anthropicThinkingBlocks;
|
|
2918
|
+
if (!Array.isArray(blocks)) return [];
|
|
2919
|
+
return blocks.filter(
|
|
2920
|
+
(block) => block !== null && typeof block === "object" && (block.type === "thinking" || block.type === "redacted_thinking")
|
|
2921
|
+
);
|
|
2922
|
+
}
|
|
2236
2923
|
/**
|
|
2237
2924
|
* Merge consecutive messages with the same role into a single message.
|
|
2238
2925
|
* This handles cases where multiple tool_result blocks need to be in one user message.
|
|
@@ -2288,16 +2975,24 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2288
2975
|
// ---------------------------------------------------------------------------
|
|
2289
2976
|
// Internal: response parsing
|
|
2290
2977
|
// ---------------------------------------------------------------------------
|
|
2291
|
-
parseResponse(json,
|
|
2978
|
+
parseResponse(json, pricingContext) {
|
|
2292
2979
|
let content = "";
|
|
2293
2980
|
let thinkingContent = "";
|
|
2294
2981
|
const toolCalls = [];
|
|
2295
|
-
|
|
2982
|
+
const thinkingBlocks = [];
|
|
2983
|
+
const fallbackIndex = lastFallbackIndex(json.content);
|
|
2984
|
+
const hasFallbackBoundary = fallbackIndex >= 0 || hasFallbackIteration(json.usage?.iterations);
|
|
2985
|
+
let refused = json.stop_reason === "refusal";
|
|
2986
|
+
for (const [index, block] of json.content.entries()) {
|
|
2296
2987
|
if (block.type === "thinking") {
|
|
2297
2988
|
thinkingContent += block.thinking;
|
|
2989
|
+
thinkingBlocks.push(block);
|
|
2990
|
+
} else if (block.type === "redacted_thinking") {
|
|
2991
|
+
thinkingBlocks.push(block);
|
|
2298
2992
|
} else if (block.type === "text") {
|
|
2299
2993
|
content += block.text;
|
|
2300
2994
|
} else if (block.type === "tool_use") {
|
|
2995
|
+
if (fallbackIndex >= 0 && index < fallbackIndex) continue;
|
|
2301
2996
|
toolCalls.push({
|
|
2302
2997
|
id: block.id,
|
|
2303
2998
|
type: "function",
|
|
@@ -2306,37 +3001,58 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2306
3001
|
arguments: JSON.stringify(block.input)
|
|
2307
3002
|
}
|
|
2308
3003
|
});
|
|
3004
|
+
} else if (block.type === "fallback") {
|
|
3005
|
+
} else if (block.type === "refusal") {
|
|
3006
|
+
refused = true;
|
|
2309
3007
|
}
|
|
2310
3008
|
}
|
|
2311
|
-
const
|
|
2312
|
-
const
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
} : void 0;
|
|
2320
|
-
const cost = json.usage ? estimateAnthropicCost(model, totalInput, json.usage.output_tokens, cacheRead, cacheWrite) : void 0;
|
|
3009
|
+
const normalized = json.usage ? normalizeAnthropicUsage(json.usage) : void 0;
|
|
3010
|
+
const effectiveModel = typeof json.model === "string" ? json.model : pricingContext.model;
|
|
3011
|
+
if (hasFallbackBoundary) {
|
|
3012
|
+
throw new Error(
|
|
3013
|
+
"Anthropic fallback responses cannot be continued safely; retry without fallbacks."
|
|
3014
|
+
);
|
|
3015
|
+
}
|
|
3016
|
+
const cost = normalized && !pricingContext.unpricedModifier && !isModifiedAnthropicResponse(json, effectiveModel) && !hasFallbackBoundary && !refused ? estimateAnthropicCost(effectiveModel, normalized.pricingUsage) : void 0;
|
|
2321
3017
|
return {
|
|
2322
3018
|
content,
|
|
2323
3019
|
thinking_content: thinkingContent || void 0,
|
|
2324
3020
|
tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
2325
|
-
usage,
|
|
2326
|
-
cost
|
|
3021
|
+
usage: normalized?.usage,
|
|
3022
|
+
cost,
|
|
3023
|
+
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
|
|
2327
3024
|
};
|
|
2328
3025
|
}
|
|
2329
3026
|
// ---------------------------------------------------------------------------
|
|
2330
3027
|
// Internal: SSE stream parsing
|
|
2331
3028
|
// ---------------------------------------------------------------------------
|
|
2332
|
-
async *parseSSEStream(body,
|
|
3029
|
+
async *parseSSEStream(body, pricingContext) {
|
|
2333
3030
|
const reader = body.getReader();
|
|
2334
3031
|
const decoder = new TextDecoder();
|
|
2335
3032
|
let buffer = "";
|
|
2336
3033
|
let currentToolId = "";
|
|
2337
3034
|
let currentToolName = "";
|
|
2338
3035
|
let usage;
|
|
2339
|
-
let
|
|
3036
|
+
let pricingUsage;
|
|
3037
|
+
let rawUsage;
|
|
3038
|
+
const thinkingBlocks = [];
|
|
3039
|
+
let currentThinkingBlock;
|
|
3040
|
+
let effectiveModel = pricingContext.model;
|
|
3041
|
+
let unpricedModifier = pricingContext.unpricedModifier;
|
|
3042
|
+
let hasFallbackBoundary = false;
|
|
3043
|
+
let hasFallbackIterationSignal = false;
|
|
3044
|
+
let refused = false;
|
|
3045
|
+
const finalizeUsage = () => {
|
|
3046
|
+
const normalized = rawUsage ? normalizeAnthropicUsage(rawUsage) : void 0;
|
|
3047
|
+
usage = normalized?.usage;
|
|
3048
|
+
pricingUsage = normalized?.pricingUsage;
|
|
3049
|
+
};
|
|
3050
|
+
const doneChunk = () => ({
|
|
3051
|
+
type: "done",
|
|
3052
|
+
usage,
|
|
3053
|
+
cost: pricingUsage && !unpricedModifier && !hasFallbackBoundary && !refused ? estimateAnthropicCost(effectiveModel, pricingUsage) : void 0,
|
|
3054
|
+
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary && !hasFallbackIterationSignal ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
|
|
3055
|
+
});
|
|
2340
3056
|
try {
|
|
2341
3057
|
while (true) {
|
|
2342
3058
|
const { done, value } = await reader.read();
|
|
@@ -2367,13 +3083,31 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2367
3083
|
name: currentToolName,
|
|
2368
3084
|
arguments: ""
|
|
2369
3085
|
};
|
|
3086
|
+
} else if (block?.type === "thinking" || block?.type === "redacted_thinking") {
|
|
3087
|
+
currentThinkingBlock = {
|
|
3088
|
+
...block,
|
|
3089
|
+
type: block.type
|
|
3090
|
+
};
|
|
3091
|
+
} else if (block?.type === "fallback") {
|
|
3092
|
+
hasFallbackBoundary = true;
|
|
3093
|
+
} else if (block?.type === "refusal") {
|
|
3094
|
+
refused = true;
|
|
2370
3095
|
}
|
|
2371
3096
|
break;
|
|
2372
3097
|
}
|
|
2373
3098
|
case "content_block_delta": {
|
|
2374
3099
|
const delta = event.delta;
|
|
2375
3100
|
if (delta?.type === "thinking_delta" && delta.thinking) {
|
|
3101
|
+
if (currentThinkingBlock?.type === "thinking") {
|
|
3102
|
+
const current = currentThinkingBlock.thinking;
|
|
3103
|
+
currentThinkingBlock.thinking = `${typeof current === "string" ? current : ""}${delta.thinking}`;
|
|
3104
|
+
}
|
|
2376
3105
|
yield { type: "thinking_delta", content: delta.thinking };
|
|
3106
|
+
} else if (delta?.type === "signature_delta" && delta.signature) {
|
|
3107
|
+
if (currentThinkingBlock) {
|
|
3108
|
+
const current = currentThinkingBlock.signature;
|
|
3109
|
+
currentThinkingBlock.signature = `${typeof current === "string" ? current : ""}${delta.signature}`;
|
|
3110
|
+
}
|
|
2377
3111
|
} else if (delta?.type === "text_delta" && delta.text) {
|
|
2378
3112
|
yield { type: "text_delta", content: delta.text };
|
|
2379
3113
|
} else if (delta?.type === "input_json_delta" && delta.partial_json) {
|
|
@@ -2386,76 +3120,82 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2386
3120
|
break;
|
|
2387
3121
|
}
|
|
2388
3122
|
case "content_block_stop": {
|
|
3123
|
+
if (currentThinkingBlock) {
|
|
3124
|
+
thinkingBlocks.push(currentThinkingBlock);
|
|
3125
|
+
currentThinkingBlock = void 0;
|
|
3126
|
+
}
|
|
2389
3127
|
currentToolId = "";
|
|
2390
3128
|
currentToolName = "";
|
|
2391
3129
|
break;
|
|
2392
3130
|
}
|
|
2393
3131
|
case "message_start": {
|
|
2394
3132
|
if (event.message?.usage) {
|
|
2395
|
-
|
|
2396
|
-
cacheWrite = event.message.usage.cache_creation_input_tokens ?? 0;
|
|
2397
|
-
const inputTokens = (event.message.usage.input_tokens ?? 0) + cacheRead + cacheWrite;
|
|
2398
|
-
usage = {
|
|
2399
|
-
prompt_tokens: inputTokens,
|
|
2400
|
-
completion_tokens: 0,
|
|
2401
|
-
total_tokens: inputTokens,
|
|
2402
|
-
cached_tokens: cacheRead > 0 ? cacheRead : void 0
|
|
2403
|
-
};
|
|
3133
|
+
rawUsage = mergeAnthropicUsage(rawUsage, event.message.usage);
|
|
2404
3134
|
}
|
|
3135
|
+
if (typeof event.message?.model === "string") effectiveModel = event.message.model;
|
|
3136
|
+
const messageFallback = hasFallbackIteration(event.message?.usage?.iterations);
|
|
3137
|
+
if (isAnthropicResponseModifier(
|
|
3138
|
+
"inference_geo",
|
|
3139
|
+
event.message?.inference_geo,
|
|
3140
|
+
effectiveModel
|
|
3141
|
+
) || isAnthropicResponseModifier("speed", event.message?.speed, effectiveModel) || isAnthropicResponseModifier(
|
|
3142
|
+
"inference_geo",
|
|
3143
|
+
event.message?.usage?.inference_geo,
|
|
3144
|
+
effectiveModel
|
|
3145
|
+
) || isAnthropicResponseModifier("speed", event.message?.usage?.speed, effectiveModel) || hasBilledUnmodeledUsage(event.message?.usage?.server_tool_use) || hasUnmodeledIterations(event.message?.usage?.iterations)) {
|
|
3146
|
+
unpricedModifier = true;
|
|
3147
|
+
}
|
|
3148
|
+
if (messageFallback) hasFallbackIterationSignal = true;
|
|
2405
3149
|
break;
|
|
2406
3150
|
}
|
|
2407
3151
|
case "message_delta": {
|
|
2408
3152
|
if (event.usage) {
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
usage
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
};
|
|
3153
|
+
rawUsage = mergeAnthropicUsage(rawUsage, event.usage);
|
|
3154
|
+
finalizeUsage();
|
|
3155
|
+
const terminalFallback = hasFallbackIteration(event.usage.iterations);
|
|
3156
|
+
if (isAnthropicResponseModifier("speed", event.usage.speed, effectiveModel) || isAnthropicResponseModifier(
|
|
3157
|
+
"inference_geo",
|
|
3158
|
+
event.usage.inference_geo,
|
|
3159
|
+
effectiveModel
|
|
3160
|
+
) || hasBilledUnmodeledUsage(event.usage.server_tool_use) || hasUnmodeledIterations(event.usage.iterations)) {
|
|
3161
|
+
unpricedModifier = true;
|
|
2419
3162
|
}
|
|
3163
|
+
if (terminalFallback) hasFallbackIterationSignal = true;
|
|
2420
3164
|
}
|
|
3165
|
+
if (event.delta?.stop_reason === "refusal") refused = true;
|
|
2421
3166
|
break;
|
|
2422
3167
|
}
|
|
2423
3168
|
case "message_stop": {
|
|
2424
|
-
|
|
2425
|
-
|
|
3169
|
+
finalizeUsage();
|
|
3170
|
+
if (hasFallbackBoundary || hasFallbackIterationSignal) {
|
|
3171
|
+
throw new Error(
|
|
3172
|
+
"Anthropic fallback streams cannot be continued safely; retry without fallbacks."
|
|
3173
|
+
);
|
|
2426
3174
|
}
|
|
2427
|
-
yield
|
|
2428
|
-
type: "done",
|
|
2429
|
-
usage,
|
|
2430
|
-
cost: usage ? estimateAnthropicCost(
|
|
2431
|
-
model,
|
|
2432
|
-
usage.prompt_tokens,
|
|
2433
|
-
usage.completion_tokens,
|
|
2434
|
-
usage.cached_tokens,
|
|
2435
|
-
cacheWrite
|
|
2436
|
-
) : void 0
|
|
2437
|
-
};
|
|
3175
|
+
yield doneChunk();
|
|
2438
3176
|
return;
|
|
2439
3177
|
}
|
|
2440
3178
|
}
|
|
2441
3179
|
}
|
|
2442
3180
|
}
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
usage.cached_tokens,
|
|
2451
|
-
cacheWrite
|
|
2452
|
-
) : void 0
|
|
2453
|
-
};
|
|
3181
|
+
finalizeUsage();
|
|
3182
|
+
if (hasFallbackBoundary || hasFallbackIterationSignal) {
|
|
3183
|
+
throw new Error(
|
|
3184
|
+
"Anthropic fallback streams cannot be continued safely; retry without fallbacks."
|
|
3185
|
+
);
|
|
3186
|
+
}
|
|
3187
|
+
yield doneChunk();
|
|
2454
3188
|
} finally {
|
|
2455
3189
|
reader.releaseLock();
|
|
2456
3190
|
}
|
|
2457
3191
|
}
|
|
2458
3192
|
};
|
|
3193
|
+
function lastFallbackIndex(blocks) {
|
|
3194
|
+
for (let index = blocks.length - 1; index >= 0; index--) {
|
|
3195
|
+
if (blocks[index].type === "fallback") return index;
|
|
3196
|
+
}
|
|
3197
|
+
return -1;
|
|
3198
|
+
}
|
|
2459
3199
|
|
|
2460
3200
|
// src/providers/gemini.ts
|
|
2461
3201
|
var GEMINI_DISALLOWED_SCHEMA_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -2503,36 +3243,92 @@ function parseGeminiFunctionResponse(content) {
|
|
|
2503
3243
|
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { result: parsed };
|
|
2504
3244
|
}
|
|
2505
3245
|
var GEMINI_PRICING = {
|
|
2506
|
-
"gemini-2.5-pro":
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
"gemini-
|
|
2513
|
-
"gemini-
|
|
2514
|
-
"gemini-3
|
|
2515
|
-
"gemini-3.1-
|
|
2516
|
-
|
|
3246
|
+
"gemini-2.5-pro": {
|
|
3247
|
+
input: 125e-8,
|
|
3248
|
+
cached: 125e-9,
|
|
3249
|
+
output: 1e-5,
|
|
3250
|
+
longContext: { input: 25e-7, cached: 25e-8, output: 15e-6 }
|
|
3251
|
+
},
|
|
3252
|
+
"gemini-2.5-flash": { input: 3e-7, cached: 3e-8, output: 25e-7 },
|
|
3253
|
+
"gemini-2.5-flash-lite": { input: 1e-7, cached: 1e-8, output: 4e-7 },
|
|
3254
|
+
"gemini-3-flash-preview": { input: 5e-7, cached: 5e-8, output: 3e-6 },
|
|
3255
|
+
"gemini-3.1-pro-preview": {
|
|
3256
|
+
input: 2e-6,
|
|
3257
|
+
cached: 2e-7,
|
|
3258
|
+
output: 12e-6,
|
|
3259
|
+
longContext: { input: 4e-6, cached: 4e-7, output: 18e-6 }
|
|
3260
|
+
},
|
|
3261
|
+
"gemini-3.1-pro-preview-customtools": {
|
|
3262
|
+
input: 2e-6,
|
|
3263
|
+
cached: 2e-7,
|
|
3264
|
+
output: 12e-6,
|
|
3265
|
+
longContext: { input: 4e-6, cached: 4e-7, output: 18e-6 }
|
|
3266
|
+
},
|
|
3267
|
+
"gemini-3.1-flash-lite": { input: 25e-8, cached: 25e-9, output: 15e-7 },
|
|
3268
|
+
"gemini-3.5-flash": { input: 15e-7, cached: 15e-8, output: 9e-6 },
|
|
3269
|
+
"gemini-3.5-flash-lite": { input: 3e-7, cached: 3e-8, output: 25e-7 },
|
|
3270
|
+
"gemini-3.6-flash": { input: 15e-7, cached: 15e-8, output: 75e-7 }
|
|
2517
3271
|
};
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
function
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
3272
|
+
function isValidTokenCount2(value) {
|
|
3273
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3274
|
+
}
|
|
3275
|
+
function isStandardGeminiRequestTier(value) {
|
|
3276
|
+
return value === void 0 || value === "SERVICE_TIER_UNSPECIFIED" || value === "SERVICE_TIER_STANDARD";
|
|
3277
|
+
}
|
|
3278
|
+
function isDefinitiveStandardGeminiResponseTier(value) {
|
|
3279
|
+
return value === "SERVICE_TIER_UNSPECIFIED" || value === "SERVICE_TIER_STANDARD" || value === "standard";
|
|
3280
|
+
}
|
|
3281
|
+
function isEligibleGeminiPricing(context, hasDefinitiveStandardResponseTier, hasInvalidResponseTier) {
|
|
3282
|
+
return context.eligibleRequest && isStandardGeminiRequestTier(context.serviceTier) && hasDefinitiveStandardResponseTier && !hasInvalidResponseTier;
|
|
3283
|
+
}
|
|
3284
|
+
function estimateGeminiCost(model, usage) {
|
|
3285
|
+
const pricing = GEMINI_PRICING[model];
|
|
3286
|
+
const cached = usage.cachedTokens ?? 0;
|
|
3287
|
+
if (!pricing || !isValidTokenCount2(usage.inputTokens) || !isValidTokenCount2(usage.outputTokens) || !isValidTokenCount2(cached) || cached > usage.inputTokens) {
|
|
3288
|
+
return void 0;
|
|
2530
3289
|
}
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
3290
|
+
const rate = usage.inputTokens > 2e5 && pricing.longContext ? pricing.longContext : pricing;
|
|
3291
|
+
return (usage.inputTokens - cached) * rate.input + cached * rate.cached + usage.outputTokens * rate.output;
|
|
3292
|
+
}
|
|
3293
|
+
function safeTokenSum2(...values) {
|
|
3294
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
3295
|
+
return Number.isSafeInteger(total) ? total : void 0;
|
|
3296
|
+
}
|
|
3297
|
+
function normalizeGeminiUsage(raw) {
|
|
3298
|
+
const inputTokens = raw.promptTokenCount;
|
|
3299
|
+
const completionTokens = raw.candidatesTokenCount;
|
|
3300
|
+
const totalTokens = raw.totalTokenCount;
|
|
3301
|
+
const cachedTokens = raw.cachedContentTokenCount ?? 0;
|
|
3302
|
+
const reasoningTokens = raw.thoughtsTokenCount ?? 0;
|
|
3303
|
+
const toolUsePromptTokens = raw.toolUsePromptTokenCount ?? 0;
|
|
3304
|
+
if (!isValidTokenCount2(inputTokens) || !isValidTokenCount2(completionTokens) || !isValidTokenCount2(totalTokens) || !isValidTokenCount2(cachedTokens) || !isValidTokenCount2(reasoningTokens) || !isValidTokenCount2(toolUsePromptTokens) || cachedTokens > inputTokens) {
|
|
3305
|
+
return void 0;
|
|
3306
|
+
}
|
|
3307
|
+
const billedOutputTokens = safeTokenSum2(completionTokens, reasoningTokens);
|
|
3308
|
+
const expectedTotal = safeTokenSum2(
|
|
3309
|
+
inputTokens,
|
|
3310
|
+
completionTokens,
|
|
3311
|
+
reasoningTokens,
|
|
3312
|
+
toolUsePromptTokens
|
|
3313
|
+
);
|
|
3314
|
+
if (billedOutputTokens === void 0 || expectedTotal === void 0 || totalTokens !== expectedTotal) {
|
|
3315
|
+
return void 0;
|
|
3316
|
+
}
|
|
3317
|
+
return {
|
|
3318
|
+
usage: {
|
|
3319
|
+
prompt_tokens: inputTokens,
|
|
3320
|
+
completion_tokens: completionTokens,
|
|
3321
|
+
total_tokens: totalTokens,
|
|
3322
|
+
cached_tokens: cachedTokens > 0 ? cachedTokens : void 0,
|
|
3323
|
+
reasoning_tokens: reasoningTokens > 0 ? reasoningTokens : void 0
|
|
3324
|
+
},
|
|
3325
|
+
pricingUsage: {
|
|
3326
|
+
inputTokens,
|
|
3327
|
+
outputTokens: billedOutputTokens,
|
|
3328
|
+
cachedTokens
|
|
3329
|
+
},
|
|
3330
|
+
hasUnmodeledBilledUsage: toolUsePromptTokens !== 0
|
|
3331
|
+
};
|
|
2536
3332
|
}
|
|
2537
3333
|
var THINKING_BUDGETS2 = {
|
|
2538
3334
|
low: 1024,
|
|
@@ -2551,8 +3347,23 @@ var THINKING_LEVELS = {
|
|
|
2551
3347
|
max: "high"
|
|
2552
3348
|
// 3.x caps at 'high'
|
|
2553
3349
|
};
|
|
3350
|
+
var GEMINI_3X_MIN_THINKING_LEVEL = /* @__PURE__ */ new Map([
|
|
3351
|
+
["gemini-3-pro-preview", "minimal"],
|
|
3352
|
+
["gemini-3-flash-preview", "minimal"],
|
|
3353
|
+
["gemini-3.1-pro-preview", "low"],
|
|
3354
|
+
["gemini-3.1-pro-preview-customtools", "low"],
|
|
3355
|
+
["gemini-3.1-flash-lite", "minimal"],
|
|
3356
|
+
["gemini-3.1-flash-lite-preview", "minimal"],
|
|
3357
|
+
["gemini-3.5-flash", "minimal"],
|
|
3358
|
+
["gemini-3.5-flash-lite", "minimal"],
|
|
3359
|
+
["gemini-3.6-flash", "minimal"]
|
|
3360
|
+
]);
|
|
3361
|
+
var GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE = /* @__PURE__ */ new Set([
|
|
3362
|
+
"gemini-3.5-flash-lite",
|
|
3363
|
+
"gemini-3.6-flash"
|
|
3364
|
+
]);
|
|
2554
3365
|
function isGemini3x(model) {
|
|
2555
|
-
return
|
|
3366
|
+
return GEMINI_3X_MIN_THINKING_LEVEL.has(model);
|
|
2556
3367
|
}
|
|
2557
3368
|
function budgetToThinkingLevel(budgetTokens) {
|
|
2558
3369
|
if (budgetTokens <= 1024) return "low";
|
|
@@ -2560,8 +3371,7 @@ function budgetToThinkingLevel(budgetTokens) {
|
|
|
2560
3371
|
return "high";
|
|
2561
3372
|
}
|
|
2562
3373
|
function minThinkingLevel(model) {
|
|
2563
|
-
|
|
2564
|
-
return "minimal";
|
|
3374
|
+
return GEMINI_3X_MIN_THINKING_LEVEL.get(model) ?? "minimal";
|
|
2565
3375
|
}
|
|
2566
3376
|
var _warned3xEffortNone = /* @__PURE__ */ new Set();
|
|
2567
3377
|
function warnGemini3xEffortNone(model) {
|
|
@@ -2606,10 +3416,12 @@ var GeminiProvider = class {
|
|
|
2606
3416
|
// chat - non-streaming completion
|
|
2607
3417
|
// ---------------------------------------------------------------------------
|
|
2608
3418
|
async chat(messages, options) {
|
|
2609
|
-
const headers = this.buildHeaders(await this.resolveKey());
|
|
2610
3419
|
const body = this.buildRequestBody(messages, options);
|
|
3420
|
+
const pricingContext = this.pricingContext(this.requestModel(options), body);
|
|
3421
|
+
this.assertSafeGemini3Continuation(messages, pricingContext.model);
|
|
3422
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
2611
3423
|
const res = await fetchWithRetry(
|
|
2612
|
-
`${this.baseUrl}/models/${
|
|
3424
|
+
`${this.baseUrl}/models/${pricingContext.model}:generateContent`,
|
|
2613
3425
|
{
|
|
2614
3426
|
method: "POST",
|
|
2615
3427
|
headers,
|
|
@@ -2630,16 +3442,18 @@ var GeminiProvider = class {
|
|
|
2630
3442
|
});
|
|
2631
3443
|
}
|
|
2632
3444
|
const json = await res.json();
|
|
2633
|
-
return this.parseResponse(json,
|
|
3445
|
+
return this.parseResponse(json, pricingContext);
|
|
2634
3446
|
}
|
|
2635
3447
|
// ---------------------------------------------------------------------------
|
|
2636
3448
|
// stream - SSE streaming completion
|
|
2637
3449
|
// ---------------------------------------------------------------------------
|
|
2638
3450
|
async *stream(messages, options) {
|
|
2639
|
-
const headers = this.buildHeaders(await this.resolveKey());
|
|
2640
3451
|
const body = this.buildRequestBody(messages, options);
|
|
3452
|
+
const pricingContext = this.pricingContext(this.requestModel(options), body);
|
|
3453
|
+
this.assertSafeGemini3Continuation(messages, pricingContext.model);
|
|
3454
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
2641
3455
|
const res = await fetchWithRetry(
|
|
2642
|
-
`${this.baseUrl}/models/${
|
|
3456
|
+
`${this.baseUrl}/models/${pricingContext.model}:streamGenerateContent?alt=sse`,
|
|
2643
3457
|
{
|
|
2644
3458
|
method: "POST",
|
|
2645
3459
|
headers,
|
|
@@ -2662,7 +3476,7 @@ var GeminiProvider = class {
|
|
|
2662
3476
|
if (!res.body) {
|
|
2663
3477
|
throw new Error("Gemini stream response has no body");
|
|
2664
3478
|
}
|
|
2665
|
-
yield* this.parseSSEStream(res.body,
|
|
3479
|
+
yield* this.parseSSEStream(res.body, pricingContext);
|
|
2666
3480
|
}
|
|
2667
3481
|
// ---------------------------------------------------------------------------
|
|
2668
3482
|
// Internal: request building
|
|
@@ -2683,7 +3497,114 @@ var GeminiProvider = class {
|
|
|
2683
3497
|
}
|
|
2684
3498
|
return `Gemini API error (${status}): ${body}`;
|
|
2685
3499
|
}
|
|
3500
|
+
pricingContext(fallbackModel, body) {
|
|
3501
|
+
return {
|
|
3502
|
+
// Gemini's model is normally selected in the URL. providerOptions is an
|
|
3503
|
+
// explicit raw escape hatch, so honor a post-merge model override for both
|
|
3504
|
+
// the URL and pricing rather than pricing a different request.
|
|
3505
|
+
model: typeof body.model === "string" ? body.model : fallbackModel,
|
|
3506
|
+
serviceTier: body.serviceTier,
|
|
3507
|
+
eligibleRequest: this.baseUrl === "https://generativelanguage.googleapis.com/v1beta" && this.isTextOnlyClientRequest(body)
|
|
3508
|
+
};
|
|
3509
|
+
}
|
|
3510
|
+
requestModel(options) {
|
|
3511
|
+
return typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
3512
|
+
}
|
|
3513
|
+
assertSafeGemini3Continuation(messages, model) {
|
|
3514
|
+
if (GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE.has(model)) {
|
|
3515
|
+
const mapped = this.mapMessages(messages.filter((message) => message.role !== "system"));
|
|
3516
|
+
if (mapped.at(-1)?.role === "model") {
|
|
3517
|
+
throw new Error(
|
|
3518
|
+
`${model} does not support a terminal assistant/model prefill; end the request with a user or tool message.`
|
|
3519
|
+
);
|
|
3520
|
+
}
|
|
3521
|
+
}
|
|
3522
|
+
if (!isGemini3x(model)) return;
|
|
3523
|
+
const replayedNativeCallIds = /* @__PURE__ */ new Set();
|
|
3524
|
+
for (const message of messages) {
|
|
3525
|
+
if (message.role !== "assistant" || !Array.isArray(message.providerMetadata?.geminiParts)) {
|
|
3526
|
+
continue;
|
|
3527
|
+
}
|
|
3528
|
+
for (const part of message.providerMetadata.geminiParts) {
|
|
3529
|
+
const id = part !== null && typeof part === "object" ? part.functionCall?.id : void 0;
|
|
3530
|
+
if (!id) continue;
|
|
3531
|
+
if (replayedNativeCallIds.has(id)) {
|
|
3532
|
+
throw new Error(
|
|
3533
|
+
`${model} native functionCall ids must be globally unique across a replay.`
|
|
3534
|
+
);
|
|
3535
|
+
}
|
|
3536
|
+
replayedNativeCallIds.add(id);
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
const consumedToolMessages = /* @__PURE__ */ new Set();
|
|
3540
|
+
for (const [assistantIndex, message] of messages.entries()) {
|
|
3541
|
+
if (message.role !== "assistant" || !message.tool_calls?.length) continue;
|
|
3542
|
+
const rawParts = message.providerMetadata?.geminiParts;
|
|
3543
|
+
if (!Array.isArray(rawParts)) {
|
|
3544
|
+
throw new Error(
|
|
3545
|
+
`${model} tool continuations require providerMetadata.geminiParts with thought signatures.`
|
|
3546
|
+
);
|
|
3547
|
+
}
|
|
3548
|
+
const nativeCalls = rawParts.flatMap((part) => {
|
|
3549
|
+
const functionCall = part !== null && typeof part === "object" ? part.functionCall : void 0;
|
|
3550
|
+
return functionCall?.id ? [{ id: functionCall.id, name: functionCall.name, part }] : [];
|
|
3551
|
+
});
|
|
3552
|
+
const expectedCalls = new Map(
|
|
3553
|
+
message.tool_calls.map((call) => [call.id, call.function.name])
|
|
3554
|
+
);
|
|
3555
|
+
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)) {
|
|
3556
|
+
throw new Error(
|
|
3557
|
+
`${model} tool continuations require exact native functionCall id/name and a thoughtSignature on the first function call.`
|
|
3558
|
+
);
|
|
3559
|
+
}
|
|
3560
|
+
const seenResponses = /* @__PURE__ */ new Set();
|
|
3561
|
+
let toolIndex = assistantIndex + 1;
|
|
3562
|
+
while (messages[toolIndex]?.role === "tool") {
|
|
3563
|
+
const toolMessage = messages[toolIndex];
|
|
3564
|
+
const toolCallId = toolMessage.tool_call_id;
|
|
3565
|
+
if (!toolCallId || !expectedCalls.has(toolCallId) || seenResponses.has(toolCallId)) {
|
|
3566
|
+
throw new Error(
|
|
3567
|
+
`${model} functionResponse requires exactly one matching native functionCall id and name.`
|
|
3568
|
+
);
|
|
3569
|
+
}
|
|
3570
|
+
seenResponses.add(toolCallId);
|
|
3571
|
+
consumedToolMessages.add(toolIndex);
|
|
3572
|
+
toolIndex++;
|
|
3573
|
+
}
|
|
3574
|
+
if (seenResponses.size !== expectedCalls.size) {
|
|
3575
|
+
throw new Error(
|
|
3576
|
+
`${model} tool continuations require one contiguous functionResponse for every native functionCall.`
|
|
3577
|
+
);
|
|
3578
|
+
}
|
|
3579
|
+
}
|
|
3580
|
+
for (const [index, message] of messages.entries()) {
|
|
3581
|
+
if (message.role === "tool" && !consumedToolMessages.has(index)) {
|
|
3582
|
+
throw new Error(
|
|
3583
|
+
`${model} functionResponse requires exactly one matching native functionCall id and name.`
|
|
3584
|
+
);
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
isTextOnlyClientRequest(body) {
|
|
3589
|
+
if (!Array.isArray(body.contents) || body.cachedContent !== void 0) return false;
|
|
3590
|
+
if (body.contents.some(
|
|
3591
|
+
(content) => content === null || typeof content !== "object" || !Array.isArray(content.parts) || content.parts.some((part) => this.isNonTextGeminiPart(part))
|
|
3592
|
+
)) {
|
|
3593
|
+
return false;
|
|
3594
|
+
}
|
|
3595
|
+
if (body.tools !== void 0 && (!Array.isArray(body.tools) || body.tools.some(
|
|
3596
|
+
(tool2) => tool2 === null || typeof tool2 !== "object" || !Array.isArray(tool2.functionDeclarations) || Object.keys(tool2).some((key) => key !== "functionDeclarations")
|
|
3597
|
+
))) {
|
|
3598
|
+
return false;
|
|
3599
|
+
}
|
|
3600
|
+
const modalities = body.generationConfig?.responseModalities;
|
|
3601
|
+
return modalities === void 0 || modalities === "TEXT" || Array.isArray(modalities) && modalities.length === 1 && modalities[0] === "TEXT";
|
|
3602
|
+
}
|
|
3603
|
+
isNonTextGeminiPart(part) {
|
|
3604
|
+
return part === null || typeof part !== "object" || ["inlineData", "fileData", "executableCode", "codeExecutionResult"].some((key) => key in part);
|
|
3605
|
+
}
|
|
2686
3606
|
buildRequestBody(messages, options) {
|
|
3607
|
+
const requestModel = this.requestModel(options);
|
|
2687
3608
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
2688
3609
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
2689
3610
|
const systemText = systemMessages.map((m) => m.content).join("\n\n");
|
|
@@ -2701,7 +3622,7 @@ var GeminiProvider = class {
|
|
|
2701
3622
|
];
|
|
2702
3623
|
}
|
|
2703
3624
|
const generationConfig = {};
|
|
2704
|
-
if (options.temperature !== void 0) {
|
|
3625
|
+
if (options.temperature !== void 0 && !GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE.has(requestModel)) {
|
|
2705
3626
|
generationConfig.temperature = options.temperature;
|
|
2706
3627
|
}
|
|
2707
3628
|
if (options.maxTokens !== void 0) {
|
|
@@ -2730,18 +3651,18 @@ var GeminiProvider = class {
|
|
|
2730
3651
|
hasBudgetOverride
|
|
2731
3652
|
} = resolveThinkingOptions(options);
|
|
2732
3653
|
if (thinkingDisabled) {
|
|
2733
|
-
if (isGemini3x(
|
|
3654
|
+
if (isGemini3x(requestModel)) {
|
|
2734
3655
|
if (effort === "none") {
|
|
2735
|
-
warnGemini3xEffortNone(
|
|
3656
|
+
warnGemini3xEffortNone(requestModel);
|
|
2736
3657
|
}
|
|
2737
|
-
generationConfig.thinkingConfig = { thinkingLevel: minThinkingLevel(
|
|
3658
|
+
generationConfig.thinkingConfig = { thinkingLevel: minThinkingLevel(requestModel) };
|
|
2738
3659
|
} else {
|
|
2739
3660
|
generationConfig.thinkingConfig = { thinkingBudget: 0 };
|
|
2740
3661
|
}
|
|
2741
3662
|
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
2742
3663
|
} else if (hasBudgetOverride) {
|
|
2743
3664
|
const config = {};
|
|
2744
|
-
if (isGemini3x(
|
|
3665
|
+
if (isGemini3x(requestModel)) {
|
|
2745
3666
|
config.thinkingLevel = budgetToThinkingLevel(thinkingBudget);
|
|
2746
3667
|
} else {
|
|
2747
3668
|
config.thinkingBudget = thinkingBudget;
|
|
@@ -2751,10 +3672,10 @@ var GeminiProvider = class {
|
|
|
2751
3672
|
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
2752
3673
|
} else if (activeEffort) {
|
|
2753
3674
|
const config = {};
|
|
2754
|
-
if (isGemini3x(
|
|
3675
|
+
if (isGemini3x(requestModel)) {
|
|
2755
3676
|
config.thinkingLevel = THINKING_LEVELS[activeEffort] ?? "medium";
|
|
2756
3677
|
} else {
|
|
2757
|
-
if (activeEffort === "max" &&
|
|
3678
|
+
if (activeEffort === "max" && requestModel === "gemini-2.5-pro") {
|
|
2758
3679
|
config.thinkingBudget = 32768;
|
|
2759
3680
|
} else {
|
|
2760
3681
|
config.thinkingBudget = THINKING_BUDGETS2[activeEffort] ?? 5e3;
|
|
@@ -2771,7 +3692,9 @@ var GeminiProvider = class {
|
|
|
2771
3692
|
body.toolConfig = { functionCallingConfig: this.mapToolChoice(options.toolChoice) };
|
|
2772
3693
|
}
|
|
2773
3694
|
if (options.providerOptions) {
|
|
2774
|
-
|
|
3695
|
+
const bodyOptions = { ...options.providerOptions };
|
|
3696
|
+
delete bodyOptions.model;
|
|
3697
|
+
Object.assign(body, bodyOptions);
|
|
2775
3698
|
}
|
|
2776
3699
|
return body;
|
|
2777
3700
|
}
|
|
@@ -2789,6 +3712,7 @@ var GeminiProvider = class {
|
|
|
2789
3712
|
mapMessages(messages) {
|
|
2790
3713
|
const toolCallIdToName = /* @__PURE__ */ new Map();
|
|
2791
3714
|
const geminiNativeIds = /* @__PURE__ */ new Set();
|
|
3715
|
+
const geminiNativeNames = /* @__PURE__ */ new Map();
|
|
2792
3716
|
for (const msg of messages) {
|
|
2793
3717
|
if (msg.role === "assistant" && msg.tool_calls) {
|
|
2794
3718
|
for (const tc of msg.tool_calls) {
|
|
@@ -2799,7 +3723,10 @@ var GeminiProvider = class {
|
|
|
2799
3723
|
const rawParts = msg.providerMetadata?.geminiParts;
|
|
2800
3724
|
if (rawParts) {
|
|
2801
3725
|
for (const p of rawParts) {
|
|
2802
|
-
if (p.functionCall?.id)
|
|
3726
|
+
if (p.functionCall?.id) {
|
|
3727
|
+
geminiNativeIds.add(p.functionCall.id);
|
|
3728
|
+
geminiNativeNames.set(p.functionCall.id, p.functionCall.name);
|
|
3729
|
+
}
|
|
2803
3730
|
}
|
|
2804
3731
|
}
|
|
2805
3732
|
}
|
|
@@ -2836,7 +3763,7 @@ var GeminiProvider = class {
|
|
|
2836
3763
|
}
|
|
2837
3764
|
}
|
|
2838
3765
|
} else if (msg.role === "tool") {
|
|
2839
|
-
const functionName = toolCallIdToName.get(msg.tool_call_id) ?? "unknown";
|
|
3766
|
+
const functionName = (msg.tool_call_id ? geminiNativeNames.get(msg.tool_call_id) : void 0) ?? toolCallIdToName.get(msg.tool_call_id) ?? "unknown";
|
|
2840
3767
|
const functionResponse = {
|
|
2841
3768
|
name: functionName,
|
|
2842
3769
|
response: parseGeminiFunctionResponse(msg.content)
|
|
@@ -2901,7 +3828,7 @@ var GeminiProvider = class {
|
|
|
2901
3828
|
// ---------------------------------------------------------------------------
|
|
2902
3829
|
// Internal: response parsing
|
|
2903
3830
|
// ---------------------------------------------------------------------------
|
|
2904
|
-
parseResponse(json,
|
|
3831
|
+
parseResponse(json, pricingContext) {
|
|
2905
3832
|
const candidate = json.candidates?.[0];
|
|
2906
3833
|
let content = "";
|
|
2907
3834
|
let thinkingContent = "";
|
|
@@ -2924,35 +3851,46 @@ var GeminiProvider = class {
|
|
|
2924
3851
|
}
|
|
2925
3852
|
}
|
|
2926
3853
|
}
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
3854
|
+
if (!candidate) {
|
|
3855
|
+
throw new Error("Gemini response did not include a candidate.");
|
|
3856
|
+
}
|
|
3857
|
+
if (candidate.finishReason && !["STOP", "MAX_TOKENS"].includes(candidate.finishReason)) {
|
|
3858
|
+
throw new Error(`Gemini returned non-success finish reason: ${candidate.finishReason}`);
|
|
3859
|
+
}
|
|
3860
|
+
const normalized = json.usageMetadata ? normalizeGeminiUsage(json.usageMetadata) : void 0;
|
|
3861
|
+
const effectiveModel = typeof json.modelVersion === "string" ? json.modelVersion : pricingContext.model;
|
|
3862
|
+
const cost = normalized && !normalized.hasUnmodeledBilledUsage && this.isTextOnlyGeminiResponse(candidate) && isEligibleGeminiPricing(
|
|
3863
|
+
pricingContext,
|
|
3864
|
+
isDefinitiveStandardGeminiResponseTier(json.usageMetadata?.serviceTier),
|
|
3865
|
+
json.usageMetadata?.serviceTier !== void 0 && !isDefinitiveStandardGeminiResponseTier(json.usageMetadata.serviceTier)
|
|
3866
|
+
) ? estimateGeminiCost(effectiveModel, normalized.pricingUsage) : void 0;
|
|
2937
3867
|
const rawParts = candidate?.content?.parts;
|
|
2938
3868
|
const providerMetadata = rawParts ? { geminiParts: rawParts } : void 0;
|
|
2939
3869
|
return {
|
|
2940
3870
|
content,
|
|
2941
3871
|
thinking_content: thinkingContent || void 0,
|
|
2942
3872
|
tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
2943
|
-
usage,
|
|
3873
|
+
usage: normalized?.usage,
|
|
2944
3874
|
cost,
|
|
2945
3875
|
providerMetadata
|
|
2946
3876
|
};
|
|
2947
3877
|
}
|
|
3878
|
+
isTextOnlyGeminiResponse(candidate) {
|
|
3879
|
+
return candidate?.content?.parts.every((part) => !this.isNonTextGeminiPart(part)) ?? false;
|
|
3880
|
+
}
|
|
2948
3881
|
// ---------------------------------------------------------------------------
|
|
2949
3882
|
// Internal: SSE stream parsing
|
|
2950
3883
|
// ---------------------------------------------------------------------------
|
|
2951
|
-
async *parseSSEStream(body,
|
|
3884
|
+
async *parseSSEStream(body, pricingContext) {
|
|
2952
3885
|
const reader = body.getReader();
|
|
2953
3886
|
const decoder = new TextDecoder();
|
|
2954
3887
|
let buffer = "";
|
|
2955
|
-
let
|
|
3888
|
+
let normalizedUsage;
|
|
3889
|
+
let effectiveModel = pricingContext.model;
|
|
3890
|
+
let hasDefinitiveStandardResponseTier = false;
|
|
3891
|
+
let hasInvalidResponseTier = false;
|
|
3892
|
+
let responseIsTextOnly = true;
|
|
3893
|
+
let terminalFailure;
|
|
2956
3894
|
const accumulatedParts = [];
|
|
2957
3895
|
try {
|
|
2958
3896
|
while (true) {
|
|
@@ -2972,20 +3910,21 @@ var GeminiProvider = class {
|
|
|
2972
3910
|
} catch {
|
|
2973
3911
|
continue;
|
|
2974
3912
|
}
|
|
3913
|
+
if (typeof chunk.modelVersion === "string") effectiveModel = chunk.modelVersion;
|
|
2975
3914
|
if (chunk.usageMetadata) {
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
};
|
|
3915
|
+
normalizedUsage = normalizeGeminiUsage(chunk.usageMetadata);
|
|
3916
|
+
if (chunk.usageMetadata.serviceTier !== void 0) {
|
|
3917
|
+
if (isDefinitiveStandardGeminiResponseTier(chunk.usageMetadata.serviceTier)) {
|
|
3918
|
+
hasDefinitiveStandardResponseTier = true;
|
|
3919
|
+
} else {
|
|
3920
|
+
hasInvalidResponseTier = true;
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
2985
3923
|
}
|
|
2986
3924
|
const candidate = chunk.candidates?.[0];
|
|
2987
3925
|
if (candidate?.content?.parts) {
|
|
2988
3926
|
for (const part of candidate.content.parts) {
|
|
3927
|
+
if (this.isNonTextGeminiPart(part)) responseIsTextOnly = false;
|
|
2989
3928
|
accumulatedParts.push(part);
|
|
2990
3929
|
if (part.thought && part.text) {
|
|
2991
3930
|
yield { type: "thinking_delta", content: part.text };
|
|
@@ -3001,18 +3940,23 @@ var GeminiProvider = class {
|
|
|
3001
3940
|
}
|
|
3002
3941
|
}
|
|
3003
3942
|
}
|
|
3943
|
+
if (candidate?.finishReason && !["STOP", "MAX_TOKENS"].includes(candidate.finishReason)) {
|
|
3944
|
+
terminalFailure = candidate.finishReason;
|
|
3945
|
+
}
|
|
3004
3946
|
}
|
|
3005
3947
|
}
|
|
3948
|
+
if (terminalFailure) {
|
|
3949
|
+
throw new Error(`Gemini returned non-success finish reason: ${terminalFailure}`);
|
|
3950
|
+
}
|
|
3006
3951
|
const providerMetadata = accumulatedParts.length > 0 ? { geminiParts: accumulatedParts } : void 0;
|
|
3007
3952
|
yield {
|
|
3008
3953
|
type: "done",
|
|
3009
|
-
usage,
|
|
3010
|
-
cost:
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
) : void 0,
|
|
3954
|
+
usage: normalizedUsage?.usage,
|
|
3955
|
+
cost: normalizedUsage && !normalizedUsage.hasUnmodeledBilledUsage && responseIsTextOnly && isEligibleGeminiPricing(
|
|
3956
|
+
pricingContext,
|
|
3957
|
+
hasDefinitiveStandardResponseTier,
|
|
3958
|
+
hasInvalidResponseTier
|
|
3959
|
+
) ? estimateGeminiCost(effectiveModel, normalizedUsage.pricingUsage) : void 0,
|
|
3016
3960
|
providerMetadata
|
|
3017
3961
|
};
|
|
3018
3962
|
} finally {
|
|
@@ -3029,8 +3973,6 @@ var OPENROUTER_PROFILE = {
|
|
|
3029
3973
|
envApiKey: "OPENROUTER_API_KEY",
|
|
3030
3974
|
envBaseUrl: "OPENROUTER_BASE_URL",
|
|
3031
3975
|
pricing: { kind: "from-response" },
|
|
3032
|
-
// Ask OpenRouter to report per-call cost in usage.cost.
|
|
3033
|
-
requestDefaults: { usage: { include: true } },
|
|
3034
3976
|
reasoning: {
|
|
3035
3977
|
// OpenRouter's effort vocabulary is low|medium|high; xhigh/max clamp to high.
|
|
3036
3978
|
emit: reasoningObjectEmit(
|
|
@@ -3053,7 +3995,7 @@ var AZURE_PROFILE = {
|
|
|
3053
3995
|
envApiKey: "AZURE_OPENAI_API_KEY",
|
|
3054
3996
|
envBaseUrl: "AZURE_OPENAI_BASE_URL",
|
|
3055
3997
|
authHeader: "api-key",
|
|
3056
|
-
pricing: { kind: "
|
|
3998
|
+
pricing: { kind: "unknown" },
|
|
3057
3999
|
reasoning: { emit: openaiReasoningEmit, capture: "none" },
|
|
3058
4000
|
roleFor: (role, model) => role === "system" && isOSeriesModel(model) ? "developer" : role,
|
|
3059
4001
|
maxTokensField: "max_completion_tokens",
|
|
@@ -3061,29 +4003,100 @@ var AZURE_PROFILE = {
|
|
|
3061
4003
|
};
|
|
3062
4004
|
|
|
3063
4005
|
// src/providers/profiles/xai.ts
|
|
3064
|
-
var
|
|
3065
|
-
var
|
|
4006
|
+
var GROK_45 = /* @__PURE__ */ new Set(["grok-4.5", "grok-4.5-latest", "grok-build-latest"]);
|
|
4007
|
+
var GROK_43 = /* @__PURE__ */ new Set(["grok-4.3", "grok-4.3-latest", "grok-latest"]);
|
|
4008
|
+
var GROK_420_REASONING = /* @__PURE__ */ new Set([
|
|
4009
|
+
"grok-4.20-0309-reasoning",
|
|
4010
|
+
"grok-4.20-reasoning-latest",
|
|
4011
|
+
"grok-4.20",
|
|
4012
|
+
"grok-4.20-reasoning",
|
|
4013
|
+
"grok-4.20-0309",
|
|
4014
|
+
"grok-4.20-beta-0309-reasoning",
|
|
4015
|
+
"grok-4.20-beta",
|
|
4016
|
+
"grok-4.20-beta-0309",
|
|
4017
|
+
"grok-4.20-beta-latest",
|
|
4018
|
+
"grok-4.20-beta-latest-reasoning",
|
|
4019
|
+
"grok-4.20-beta-reasoning",
|
|
4020
|
+
"grok-4.20-experimental-beta-0304-reasoning",
|
|
4021
|
+
"grok-4.20-experimental-beta-0304",
|
|
4022
|
+
"grok-4.20-experimental-beta-reasoning-latest",
|
|
4023
|
+
"grok-4.20-experimental-beta-latest",
|
|
4024
|
+
"grok-4.20-reasoning-gv2"
|
|
4025
|
+
]);
|
|
4026
|
+
var GROK_420_NON_REASONING = /* @__PURE__ */ new Set([
|
|
4027
|
+
"grok-4.20-0309-non-reasoning",
|
|
4028
|
+
"grok-4.20-non-reasoning",
|
|
4029
|
+
"grok-4.20-non-reasoning-latest",
|
|
4030
|
+
"grok-4.20-beta-non-reasoning",
|
|
4031
|
+
"grok-4.20-beta-latest-non-reasoning",
|
|
4032
|
+
"grok-4.20-experimental-beta-0304-non-reasoning",
|
|
4033
|
+
"grok-4.20-experimental-beta-non-reasoning-latest",
|
|
4034
|
+
"grok-4.20-beta-0309-non-reasoning",
|
|
4035
|
+
"grok-4.20-non-reasoning-gv2"
|
|
4036
|
+
]);
|
|
4037
|
+
var warnedIrreducible45 = /* @__PURE__ */ new Set();
|
|
4038
|
+
var warnedEffortClamp = /* @__PURE__ */ new Set();
|
|
4039
|
+
function warnIrreducible45(model) {
|
|
4040
|
+
if (warnedIrreducible45.has(model)) return;
|
|
4041
|
+
warnedIrreducible45.add(model);
|
|
4042
|
+
console.warn(
|
|
4043
|
+
`[axl] effort: 'none' on xAI ${model} maps to 'low'; Grok 4.5 cannot disable reasoning.`
|
|
4044
|
+
);
|
|
4045
|
+
}
|
|
4046
|
+
function warnEffortClamp(model, effort) {
|
|
4047
|
+
const key = `${model}:${effort}`;
|
|
4048
|
+
if (warnedEffortClamp.has(key)) return;
|
|
4049
|
+
warnedEffortClamp.add(key);
|
|
4050
|
+
console.warn(
|
|
4051
|
+
`[axl] effort: '${effort}' on xAI ${model} maps to 'high'; this model supports at most 'high'.`
|
|
4052
|
+
);
|
|
4053
|
+
}
|
|
4054
|
+
var isReasoningChatModel = (model) => GROK_45.has(model) || GROK_43.has(model) || GROK_420_REASONING.has(model);
|
|
3066
4055
|
var XAI_PROFILE = {
|
|
3067
4056
|
name: "xai",
|
|
3068
4057
|
label: "xAI",
|
|
3069
4058
|
defaultBaseUrl: "https://api.x.ai/v1",
|
|
3070
4059
|
envApiKey: "XAI_API_KEY",
|
|
3071
4060
|
envBaseUrl: "XAI_BASE_URL",
|
|
3072
|
-
pricing: { kind: "
|
|
4061
|
+
pricing: { kind: "from-response" },
|
|
3073
4062
|
reasoning: {
|
|
3074
4063
|
emit: reasoningEffortEmit((resolved, model) => {
|
|
3075
|
-
if (
|
|
3076
|
-
|
|
3077
|
-
|
|
4064
|
+
if (GROK_45.has(model)) {
|
|
4065
|
+
if (resolved.thinkingDisabled) {
|
|
4066
|
+
warnIrreducible45(model);
|
|
4067
|
+
return "low";
|
|
4068
|
+
}
|
|
4069
|
+
if (!resolved.activeEffort) return void 0;
|
|
4070
|
+
if (resolved.activeEffort === "xhigh" || resolved.activeEffort === "max") {
|
|
4071
|
+
warnEffortClamp(model, resolved.activeEffort);
|
|
4072
|
+
return "high";
|
|
4073
|
+
}
|
|
4074
|
+
return resolved.activeEffort;
|
|
4075
|
+
}
|
|
4076
|
+
if (GROK_43.has(model)) {
|
|
4077
|
+
if (resolved.thinkingDisabled) return "none";
|
|
4078
|
+
if (!resolved.activeEffort) return void 0;
|
|
4079
|
+
if (resolved.activeEffort === "xhigh" || resolved.activeEffort === "max") {
|
|
4080
|
+
warnEffortClamp(model, resolved.activeEffort);
|
|
4081
|
+
return "high";
|
|
4082
|
+
}
|
|
4083
|
+
return resolved.activeEffort;
|
|
4084
|
+
}
|
|
4085
|
+
if (GROK_420_NON_REASONING.has(model)) return void 0;
|
|
4086
|
+
return void 0;
|
|
3078
4087
|
}),
|
|
3079
4088
|
capture: "reasoning"
|
|
3080
4089
|
},
|
|
3081
4090
|
capabilities: {
|
|
3082
|
-
forbiddenParams: (model) =>
|
|
4091
|
+
forbiddenParams: (model) => isReasoningChatModel(model) ? ["stop", "presence_penalty", "frequency_penalty"] : []
|
|
3083
4092
|
}
|
|
3084
4093
|
};
|
|
3085
4094
|
|
|
3086
4095
|
// src/providers/profiles/deepseek.ts
|
|
4096
|
+
var DEEPSEEK_PRICING = {
|
|
4097
|
+
"deepseek-v4-flash": [14e-8, 28e-8, 0.02],
|
|
4098
|
+
"deepseek-v4-pro": [435e-9, 87e-8, 1 / 120]
|
|
4099
|
+
};
|
|
3087
4100
|
var isReasoner = (model) => /reasoner|v4/i.test(model);
|
|
3088
4101
|
var deepseekEmit = (_body, _resolved, model) => ({
|
|
3089
4102
|
stripTemperature: isReasoner(model)
|
|
@@ -3094,7 +4107,7 @@ var DEEPSEEK_PROFILE = {
|
|
|
3094
4107
|
defaultBaseUrl: "https://api.deepseek.com/v1",
|
|
3095
4108
|
envApiKey: "DEEPSEEK_API_KEY",
|
|
3096
4109
|
envBaseUrl: "DEEPSEEK_BASE_URL",
|
|
3097
|
-
pricing: { kind: "
|
|
4110
|
+
pricing: { kind: "table", table: DEEPSEEK_PRICING, match: "exact" },
|
|
3098
4111
|
reasoning: {
|
|
3099
4112
|
emit: deepseekEmit,
|
|
3100
4113
|
capture: "reasoning_content",
|
|
@@ -3105,27 +4118,56 @@ var DEEPSEEK_PROFILE = {
|
|
|
3105
4118
|
forbiddenParams: (model) => isReasoner(model) ? ["top_p", "presence_penalty", "frequency_penalty"] : []
|
|
3106
4119
|
}
|
|
3107
4120
|
};
|
|
4121
|
+
attachBuiltinTablePricingPolicy(DEEPSEEK_PROFILE, {
|
|
4122
|
+
canonicalBaseUrl: "https://api.deepseek.com/v1",
|
|
4123
|
+
requireCacheSplit: true
|
|
4124
|
+
});
|
|
3108
4125
|
|
|
3109
4126
|
// src/providers/profiles/mistral.ts
|
|
3110
|
-
var
|
|
4127
|
+
var MISTRAL_PRICING = {
|
|
4128
|
+
"mistral-medium-latest": [15e-7, 75e-7, 0.1],
|
|
4129
|
+
"mistral-small-latest": [15e-8, 6e-7, 0.1],
|
|
4130
|
+
"mistral-large-latest": [5e-7, 15e-7, 0.1],
|
|
4131
|
+
"devstral-medium-latest": [4e-7, 2e-6, 0.1],
|
|
4132
|
+
"devstral-small-latest": [1e-7, 3e-7, 0.1],
|
|
4133
|
+
"codestral-latest": [3e-7, 9e-7, 0.1],
|
|
4134
|
+
"magistral-medium-latest": [2e-6, 5e-6, 0.1],
|
|
4135
|
+
"magistral-small-latest": [5e-7, 15e-7, 0.1],
|
|
4136
|
+
"ministral-3b-latest": [1e-7, 1e-7, 0.1],
|
|
4137
|
+
"ministral-8b-latest": [15e-8, 15e-8, 0.1],
|
|
4138
|
+
"ministral-14b-latest": [2e-7, 2e-7, 0.1],
|
|
4139
|
+
"open-mistral-nemo": [15e-8, 15e-8, 0.1],
|
|
4140
|
+
"open-mixtral-8x7b": [7e-7, 7e-7, 0.1],
|
|
4141
|
+
"open-mixtral-8x22b": [2e-6, 6e-6, 0.1]
|
|
4142
|
+
};
|
|
4143
|
+
var acceptsReasoningEffort = (model) => /^mistral-(small|medium)/i.test(model);
|
|
3111
4144
|
var MISTRAL_PROFILE = {
|
|
3112
4145
|
name: "mistral",
|
|
3113
4146
|
label: "Mistral",
|
|
3114
4147
|
defaultBaseUrl: "https://api.mistral.ai/v1",
|
|
3115
4148
|
envApiKey: "MISTRAL_API_KEY",
|
|
3116
4149
|
envBaseUrl: "MISTRAL_BASE_URL",
|
|
3117
|
-
pricing: { kind: "
|
|
4150
|
+
pricing: { kind: "table", table: MISTRAL_PRICING, match: "exact" },
|
|
3118
4151
|
reasoning: {
|
|
3119
4152
|
emit: reasoningEffortEmit((resolved, model) => {
|
|
3120
|
-
if (!
|
|
4153
|
+
if (!acceptsReasoningEffort(model)) return void 0;
|
|
3121
4154
|
if (resolved.thinkingDisabled || !resolved.activeEffort) return void 0;
|
|
3122
4155
|
return "high";
|
|
3123
4156
|
}),
|
|
3124
4157
|
capture: "reasoning_content"
|
|
3125
4158
|
}
|
|
3126
4159
|
};
|
|
4160
|
+
attachBuiltinTablePricingPolicy(MISTRAL_PROFILE, {
|
|
4161
|
+
canonicalBaseUrl: "https://api.mistral.ai/v1"
|
|
4162
|
+
});
|
|
3127
4163
|
|
|
3128
4164
|
// src/providers/profiles/groq.ts
|
|
4165
|
+
var GROQ_PRICING = {
|
|
4166
|
+
"llama-3.1-8b-instant": [5e-8, 8e-8, 1],
|
|
4167
|
+
"llama-3.3-70b-versatile": [59e-8, 79e-8, 1],
|
|
4168
|
+
"openai/gpt-oss-120b": [15e-8, 6e-7, 0.5],
|
|
4169
|
+
"openai/gpt-oss-20b": [75e-9, 3e-7, 0.5]
|
|
4170
|
+
};
|
|
3129
4171
|
var isGptOss = (model) => /gpt-oss/i.test(model);
|
|
3130
4172
|
var GROQ_PROFILE = {
|
|
3131
4173
|
name: "groq",
|
|
@@ -3133,7 +4175,11 @@ var GROQ_PROFILE = {
|
|
|
3133
4175
|
defaultBaseUrl: "https://api.groq.com/openai/v1",
|
|
3134
4176
|
envApiKey: "GROQ_API_KEY",
|
|
3135
4177
|
envBaseUrl: "GROQ_BASE_URL",
|
|
3136
|
-
pricing: {
|
|
4178
|
+
pricing: {
|
|
4179
|
+
kind: "table",
|
|
4180
|
+
table: GROQ_PRICING,
|
|
4181
|
+
match: "exact"
|
|
4182
|
+
},
|
|
3137
4183
|
reasoning: {
|
|
3138
4184
|
emit: reasoningEffortEmit((resolved, model) => {
|
|
3139
4185
|
if (!isGptOss(model) || resolved.thinkingDisabled || !resolved.activeEffort) {
|
|
@@ -3154,6 +4200,21 @@ var GROQ_PROFILE = {
|
|
|
3154
4200
|
supportsJsonSchema: isGptOss
|
|
3155
4201
|
}
|
|
3156
4202
|
};
|
|
4203
|
+
attachBuiltinTablePricingPolicy(GROQ_PROFILE, {
|
|
4204
|
+
canonicalBaseUrl: "https://api.groq.com/openai/v1",
|
|
4205
|
+
supportedServiceTiers: ["on_demand", "flex"],
|
|
4206
|
+
functionToolsOnly: true,
|
|
4207
|
+
chargedRequestModifiers: [
|
|
4208
|
+
"browser_search",
|
|
4209
|
+
"code_interpreter",
|
|
4210
|
+
"web_search",
|
|
4211
|
+
"visit_website",
|
|
4212
|
+
"wolfram_alpha",
|
|
4213
|
+
"remote_mcp",
|
|
4214
|
+
"mcp",
|
|
4215
|
+
"compound_custom"
|
|
4216
|
+
]
|
|
4217
|
+
});
|
|
3157
4218
|
|
|
3158
4219
|
// src/providers/profiles/bedrock.ts
|
|
3159
4220
|
var isGptOss2 = (model) => /gpt-oss/i.test(model);
|
|
@@ -3968,7 +5029,7 @@ var COST_LEAF_SET = new Set(COST_BEARING_LEAF_TYPES);
|
|
|
3968
5029
|
function hasPositiveTokens(event) {
|
|
3969
5030
|
const t = event.tokens;
|
|
3970
5031
|
if (!t) return false;
|
|
3971
|
-
return (t.input ?? 0) + (t.output ?? 0) + (t.reasoning ?? 0) > 0;
|
|
5032
|
+
return (t.input ?? 0) + (t.output ?? 0) + (t.reasoning ?? 0) + (t.cached ?? 0) + (t.cacheWrite ?? 0) > 0;
|
|
3972
5033
|
}
|
|
3973
5034
|
function isUsableCost(cost) {
|
|
3974
5035
|
return typeof cost === "number" && Number.isFinite(cost) && cost >= 0;
|
|
@@ -6581,6 +7642,11 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
6581
7642
|
"axl.agent.cached_tokens",
|
|
6582
7643
|
usageCapture.value.cached_tokens
|
|
6583
7644
|
);
|
|
7645
|
+
if (usageCapture.value.cache_write_tokens)
|
|
7646
|
+
span.setAttribute(
|
|
7647
|
+
"axl.agent.cache_write_tokens",
|
|
7648
|
+
usageCapture.value.cache_write_tokens
|
|
7649
|
+
);
|
|
6584
7650
|
}
|
|
6585
7651
|
return r;
|
|
6586
7652
|
}
|
|
@@ -7026,7 +8092,9 @@ ${appendedSchemaText}`;
|
|
|
7026
8092
|
tokens: response.usage ? {
|
|
7027
8093
|
input: response.usage.prompt_tokens,
|
|
7028
8094
|
output: response.usage.completion_tokens,
|
|
7029
|
-
reasoning: response.usage.reasoning_tokens
|
|
8095
|
+
reasoning: response.usage.reasoning_tokens,
|
|
8096
|
+
cached: response.usage.cached_tokens,
|
|
8097
|
+
cacheWrite: response.usage.cache_write_tokens
|
|
7030
8098
|
} : void 0,
|
|
7031
8099
|
duration: Date.now() - turnStart,
|
|
7032
8100
|
data: {
|