@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.js CHANGED
@@ -666,6 +666,42 @@ var RateLimiter = class {
666
666
  }
667
667
  };
668
668
 
669
+ // src/providers/builtin-table-pricing.ts
670
+ var policies = /* @__PURE__ */ new WeakMap();
671
+ function attachBuiltinTablePricingPolicy(profile, policy) {
672
+ if (profile.pricing.kind !== "table") {
673
+ throw new Error("Built-in table pricing policy requires table pricing.");
674
+ }
675
+ policies.set(profile.pricing.table, policy);
676
+ }
677
+ function hasFunctionToolsOnly(value) {
678
+ return value === void 0 || Array.isArray(value) && value.every(
679
+ (tool2) => tool2 !== null && typeof tool2 === "object" && tool2.type === "function"
680
+ );
681
+ }
682
+ function hasValidRequiredCacheSplit(context) {
683
+ const usage = context.response?.usage;
684
+ const hit = usage?.prompt_cache_hit_tokens;
685
+ const miss = usage?.prompt_cache_miss_tokens;
686
+ return typeof hit === "number" && typeof miss === "number" && Number.isSafeInteger(hit) && Number.isSafeInteger(miss) && hit >= 0 && miss >= 0 && hit + miss === usage?.prompt_tokens;
687
+ }
688
+ function isBuiltinTablePricingEligible(profile, context) {
689
+ const policy = profile.pricing.kind === "table" ? policies.get(profile.pricing.table) : void 0;
690
+ if (!policy) return true;
691
+ if (policy.canonicalBaseUrl && context.baseUrl !== policy.canonicalBaseUrl) return false;
692
+ if (policy.requireCacheSplit && !hasValidRequiredCacheSplit(context)) return false;
693
+ if (policy.functionToolsOnly && !hasFunctionToolsOnly(context.request?.tools)) return false;
694
+ if (policy.chargedRequestModifiers?.some((modifier) => context.request?.[modifier] !== void 0)) {
695
+ return false;
696
+ }
697
+ if (!policy.supportedServiceTiers) return true;
698
+ const requestTier = context.request?.service_tier ?? context.request?.serviceTier;
699
+ const responseTier = context.response?.service_tier ?? context.response?.serviceTier;
700
+ const effectiveTier = responseTier ?? requestTier;
701
+ if (effectiveTier === void 0) return true;
702
+ return typeof effectiveTier === "string" && policy.supportedServiceTiers.includes(effectiveTier);
703
+ }
704
+
669
705
  // src/providers/openai-compatible.ts
670
706
  function resolvePerModel(value, model, fallback) {
671
707
  if (value === void 0) return fallback;
@@ -680,9 +716,13 @@ function sortedKeys(table) {
680
716
  }
681
717
  return keys;
682
718
  }
683
- function priceFromTable(table, model, promptTokens, completionTokens, cachedTokens) {
719
+ function priceFromTable(table, model, promptTokens, completionTokens, cachedTokens, match = "prefix") {
720
+ const cached = cachedTokens ?? 0;
721
+ if (!Number.isSafeInteger(promptTokens) || !Number.isSafeInteger(completionTokens) || !Number.isSafeInteger(cached) || promptTokens < 0 || completionTokens < 0 || cached < 0 || cached > promptTokens) {
722
+ return void 0;
723
+ }
684
724
  let pricing = table[model];
685
- if (!pricing) {
725
+ if (!pricing && match === "prefix") {
686
726
  for (const key of sortedKeys(table)) {
687
727
  if (model.startsWith(key)) {
688
728
  pricing = table[key];
@@ -692,7 +732,6 @@ function priceFromTable(table, model, promptTokens, completionTokens, cachedToke
692
732
  }
693
733
  if (!pricing) return void 0;
694
734
  const [inputRate, outputRate, cacheMultiplier] = pricing;
695
- const cached = cachedTokens ?? 0;
696
735
  const inputCost = (promptTokens - cached) * inputRate + cached * inputRate * cacheMultiplier;
697
736
  return inputCost + completionTokens * outputRate;
698
737
  }
@@ -853,7 +892,7 @@ var OpenAICompatibleProvider = class {
853
892
  });
854
893
  }
855
894
  const json = await res.json();
856
- return this.parseResponse(json, options.model);
895
+ return this.parseResponse(json, this.requestModel(body, options.model), body);
857
896
  }
858
897
  async *stream(messages, options) {
859
898
  const headers = this.buildHeaders(await this.resolveKey());
@@ -881,7 +920,7 @@ var OpenAICompatibleProvider = class {
881
920
  if (!res.body) {
882
921
  throw new Error(`${this.profile.label ?? this.profile.name} stream response has no body`);
883
922
  }
884
- yield* this.parseSSEStream(res.body, options.model);
923
+ yield* this.parseSSEStream(res.body, this.requestModel(body, options.model), body);
885
924
  }
886
925
  // ---------------------------------------------------------------------------
887
926
  // Request building
@@ -902,12 +941,13 @@ var OpenAICompatibleProvider = class {
902
941
  buildRequestBody(messages, options, stream) {
903
942
  const profile = this.profile;
904
943
  const resolved = resolveThinkingOptions(options);
944
+ const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
905
945
  const body = {
906
- model: options.model,
907
- messages: messages.map((m) => this.formatMessage(m, options.model)),
946
+ model: effectiveModel,
947
+ messages: messages.map((m) => this.formatMessage(m, effectiveModel)),
908
948
  stream
909
949
  };
910
- const emitResult = profile.reasoning.emit(body, resolved, options.model) ?? {};
950
+ const emitResult = profile.reasoning.emit(body, resolved, effectiveModel) ?? {};
911
951
  const stripTemperature = emitResult.stripTemperature ?? false;
912
952
  if (options.temperature !== void 0 && !stripTemperature) {
913
953
  body.temperature = options.temperature;
@@ -918,7 +958,7 @@ var OpenAICompatibleProvider = class {
918
958
  if (options.stop) body.stop = options.stop;
919
959
  if (options.tools && options.tools.length > 0) {
920
960
  body.tools = options.tools;
921
- if (resolvePerModel(profile.parallelToolCalls, options.model, false)) {
961
+ if (resolvePerModel(profile.parallelToolCalls, effectiveModel, false)) {
922
962
  body.parallel_tool_calls = true;
923
963
  }
924
964
  }
@@ -926,7 +966,7 @@ var OpenAICompatibleProvider = class {
926
966
  if (options.responseFormat) {
927
967
  const supportsSchema = resolvePerModel(
928
968
  profile.capabilities?.supportsJsonSchema,
929
- options.model,
969
+ effectiveModel,
930
970
  true
931
971
  );
932
972
  body.response_format = options.responseFormat.type === "json_schema" && !supportsSchema ? { type: "json_object" } : options.responseFormat;
@@ -937,7 +977,7 @@ var OpenAICompatibleProvider = class {
937
977
  if (profile.requestDefaults) Object.assign(body, profile.requestDefaults);
938
978
  const userKeys = options.providerOptions ? Object.keys(options.providerOptions) : [];
939
979
  if (options.providerOptions) Object.assign(body, options.providerOptions);
940
- const forbidden = resolvePerModel(profile.capabilities?.forbiddenParams, options.model, []);
980
+ const forbidden = resolvePerModel(profile.capabilities?.forbiddenParams, effectiveModel, []);
941
981
  for (const key of forbidden) {
942
982
  if (!userKeys.includes(key)) delete body[key];
943
983
  }
@@ -972,6 +1012,9 @@ var OpenAICompatibleProvider = class {
972
1012
  }
973
1013
  return `${label} API error (${status}): ${body}`;
974
1014
  }
1015
+ requestModel(body, fallback) {
1016
+ return typeof body.model === "string" ? body.model : fallback;
1017
+ }
975
1018
  toUsage(raw) {
976
1019
  if (!raw) return void 0;
977
1020
  return {
@@ -979,10 +1022,27 @@ var OpenAICompatibleProvider = class {
979
1022
  completion_tokens: raw.completion_tokens,
980
1023
  total_tokens: raw.total_tokens,
981
1024
  reasoning_tokens: raw.completion_tokens_details?.reasoning_tokens,
982
- cached_tokens: raw.prompt_tokens_details?.cached_tokens
1025
+ // DeepSeek reports the cache split at the top level rather than under
1026
+ // prompt_tokens_details. Preserve the normalized hit count for the
1027
+ // generic table estimator; its split is validated before pricing.
1028
+ cached_tokens: raw.prompt_cache_hit_tokens ?? raw.prompt_tokens_details?.cached_tokens,
1029
+ cache_write_tokens: raw.prompt_tokens_details?.cache_write_tokens
983
1030
  };
984
1031
  }
985
- computeCost(model, usage, reportedCost) {
1032
+ reportedCost(raw) {
1033
+ if (!raw) return void 0;
1034
+ if (this.profile.name === "xai") {
1035
+ const ticks = raw.cost_in_usd_ticks;
1036
+ return typeof ticks === "number" && Number.isSafeInteger(ticks) && ticks >= 0 ? ticks / 1e10 : void 0;
1037
+ }
1038
+ return typeof raw.cost === "number" && Number.isFinite(raw.cost) && raw.cost >= 0 ? raw.cost : void 0;
1039
+ }
1040
+ /**
1041
+ * The generic profile path stays flat and public. Native subclasses may
1042
+ * override this protected seam to use a provider-internal estimator without
1043
+ * widening `PricingSource` or `ProviderProfile`.
1044
+ */
1045
+ computeCost(model, usage, reportedCost, context) {
986
1046
  const p = this.profile.pricing;
987
1047
  if (p.kind === "zero") return 0;
988
1048
  if (!usage) return void 0;
@@ -990,14 +1050,22 @@ var OpenAICompatibleProvider = class {
990
1050
  case "unknown":
991
1051
  return void 0;
992
1052
  case "from-response":
993
- return typeof reportedCost === "number" && Number.isFinite(reportedCost) ? reportedCost : void 0;
1053
+ return typeof reportedCost === "number" && Number.isFinite(reportedCost) && reportedCost >= 0 ? reportedCost : void 0;
994
1054
  case "table": {
1055
+ if (!isBuiltinTablePricingEligible(this.profile, {
1056
+ baseUrl: this.baseUrl,
1057
+ request: context?.request,
1058
+ response: context?.response
1059
+ })) {
1060
+ return void 0;
1061
+ }
995
1062
  const c = priceFromTable(
996
1063
  p.table,
997
1064
  model,
998
1065
  usage.prompt_tokens,
999
1066
  usage.completion_tokens,
1000
- usage.cached_tokens
1067
+ usage.cached_tokens,
1068
+ p.match
1001
1069
  );
1002
1070
  return c !== void 0 && Number.isFinite(c) ? c : void 0;
1003
1071
  }
@@ -1007,7 +1075,7 @@ var OpenAICompatibleProvider = class {
1007
1075
  }
1008
1076
  }
1009
1077
  }
1010
- parseResponse(json, model) {
1078
+ parseResponse(json, model, request) {
1011
1079
  const message = json.choices?.[0]?.message ?? {};
1012
1080
  let content = normalizeMessageContent(message.content);
1013
1081
  let thinking;
@@ -1053,7 +1121,10 @@ var OpenAICompatibleProvider = class {
1053
1121
  }
1054
1122
  }
1055
1123
  const usage = this.toUsage(json.usage);
1056
- const cost = this.computeCost(model, usage, json.usage?.cost);
1124
+ const cost = this.computeCost(json.model ?? model, usage, this.reportedCost(json.usage), {
1125
+ request,
1126
+ response: json
1127
+ });
1057
1128
  return {
1058
1129
  content,
1059
1130
  thinking_content: thinking || void 0,
@@ -1076,11 +1147,12 @@ var OpenAICompatibleProvider = class {
1076
1147
  // ---------------------------------------------------------------------------
1077
1148
  // SSE streaming
1078
1149
  // ---------------------------------------------------------------------------
1079
- async *parseSSEStream(body, model) {
1150
+ async *parseSSEStream(body, model, request) {
1080
1151
  const reader = body.getReader();
1081
1152
  const decoder = new TextDecoder();
1082
1153
  let buffer = "";
1083
1154
  let usageData;
1155
+ let pricingResponse;
1084
1156
  const capture = this.profile.reasoning.capture;
1085
1157
  const thinkScanner = capture === "think_tags" ? new ThinkTagScanner() : void 0;
1086
1158
  let reasoningText = "";
@@ -1121,7 +1193,15 @@ var OpenAICompatibleProvider = class {
1121
1193
  return {
1122
1194
  type: "done",
1123
1195
  usage,
1124
- cost: this.computeCost(model, usage, usageData?.cost),
1196
+ cost: this.computeCost(
1197
+ pricingResponse?.model ?? model,
1198
+ usage,
1199
+ this.reportedCost(usageData),
1200
+ {
1201
+ request,
1202
+ response: pricingResponse
1203
+ }
1204
+ ),
1125
1205
  providerMetadata
1126
1206
  };
1127
1207
  };
@@ -1162,8 +1242,12 @@ var OpenAICompatibleProvider = class {
1162
1242
  body: JSON.stringify(parsed.error)
1163
1243
  });
1164
1244
  }
1245
+ if (parsed.model !== void 0 || parsed.service_tier !== void 0 || parsed.serviceTier !== void 0) {
1246
+ pricingResponse = { ...pricingResponse, ...parsed };
1247
+ }
1165
1248
  if (parsed.usage) {
1166
1249
  usageData = parsed.usage;
1250
+ pricingResponse = { ...pricingResponse, ...parsed };
1167
1251
  if (!parsed.choices || parsed.choices.length === 0) continue;
1168
1252
  }
1169
1253
  const delta = parsed.choices?.[0]?.delta;
@@ -1237,38 +1321,328 @@ var OpenAICompatibleProvider = class {
1237
1321
 
1238
1322
  // src/providers/openai.ts
1239
1323
  var OPENAI_PRICING = {
1240
- // gpt-4o era — cache reads at 50% of input rate
1324
+ // Flat, exact Standard rows only. Context-tiered and cache-write-priced
1325
+ // models intentionally stay out of this compatibility view.
1241
1326
  "gpt-4o": [25e-7, 1e-5, 0.5],
1242
1327
  "gpt-4o-mini": [15e-8, 6e-7, 0.5],
1243
- "gpt-4-turbo": [1e-5, 3e-5, 0.5],
1244
- "gpt-4": [3e-5, 6e-5, 0.5],
1245
- "gpt-3.5-turbo": [5e-7, 15e-7, 0.5],
1246
1328
  o1: [15e-6, 6e-5, 0.5],
1247
- "o1-mini": [3e-6, 12e-6, 0.5],
1248
- "o1-pro": [15e-5, 6e-4, 0.5],
1249
- // gpt-4.1 / o3 / o4 era — cache reads at 25% of input rate
1250
1329
  "gpt-4.1": [2e-6, 8e-6, 0.25],
1251
1330
  "gpt-4.1-mini": [4e-7, 16e-7, 0.25],
1252
1331
  "gpt-4.1-nano": [1e-7, 4e-7, 0.25],
1253
- o3: [1e-5, 4e-5, 0.25],
1332
+ o3: [2e-6, 8e-6, 0.25],
1254
1333
  "o3-mini": [11e-7, 44e-7, 0.25],
1255
- "o3-pro": [2e-5, 8e-5, 0.25],
1256
1334
  "o4-mini": [11e-7, 44e-7, 0.25],
1257
- // gpt-5 era — cache reads at 10% of input rate
1258
1335
  "gpt-5": [125e-8, 1e-5, 0.1],
1259
1336
  "gpt-5-mini": [25e-8, 2e-6, 0.1],
1260
1337
  "gpt-5-nano": [5e-8, 4e-7, 0.1],
1261
1338
  "gpt-5.1": [125e-8, 1e-5, 0.1],
1262
- "gpt-5.2": [175e-8, 14e-6, 0.1],
1263
- "gpt-5.3": [175e-8, 14e-6, 0.1],
1264
- "gpt-5.4": [25e-7, 15e-6, 0.1],
1265
- "gpt-5.4-pro": [3e-5, 18e-5, 0.1],
1266
- // gpt-5.5 era (flagship released 2026-04) — cache reads at 10% of input rate
1267
- "gpt-5.5": [5e-6, 3e-5, 0.1],
1268
- "gpt-5.5-pro": [3e-5, 18e-5, 0.1]
1339
+ "gpt-5.2": [175e-8, 14e-6, 0.1]
1269
1340
  };
1341
+ var M = 1e6;
1342
+ var LONG_CONTEXT_BOUNDARY = 272e3;
1343
+ var withLongContext = (short) => ({
1344
+ input: short.input * 2,
1345
+ cachedInput: short.cachedInput === void 0 ? void 0 : short.cachedInput * 2,
1346
+ cacheWrite: short.cacheWrite === void 0 ? void 0 : short.cacheWrite * 2,
1347
+ output: short.output * 1.5
1348
+ });
1349
+ var DIRECT_OPENAI_CATALOG = [
1350
+ {
1351
+ aliases: ["gpt-5.6", "gpt-5.6-sol"],
1352
+ snapshotBase: "gpt-5.6-sol",
1353
+ short: { input: 5 / M, cachedInput: 0.5 / M, cacheWrite: 6.25 / M, output: 30 / M },
1354
+ long: withLongContext({
1355
+ input: 5 / M,
1356
+ cachedInput: 0.5 / M,
1357
+ cacheWrite: 6.25 / M,
1358
+ output: 30 / M
1359
+ }),
1360
+ contextBoundary: LONG_CONTEXT_BOUNDARY
1361
+ },
1362
+ {
1363
+ aliases: ["gpt-5.6-terra"],
1364
+ snapshotBase: "gpt-5.6-terra",
1365
+ short: { input: 2 / M, cachedInput: 0.2 / M, cacheWrite: 2.5 / M, output: 12 / M },
1366
+ long: withLongContext({
1367
+ input: 2 / M,
1368
+ cachedInput: 0.2 / M,
1369
+ cacheWrite: 2.5 / M,
1370
+ output: 12 / M
1371
+ }),
1372
+ contextBoundary: LONG_CONTEXT_BOUNDARY
1373
+ },
1374
+ {
1375
+ aliases: ["gpt-5.6-luna"],
1376
+ snapshotBase: "gpt-5.6-luna",
1377
+ short: { input: 0.2 / M, cachedInput: 0.02 / M, cacheWrite: 0.25 / M, output: 1.2 / M },
1378
+ long: withLongContext({
1379
+ input: 0.2 / M,
1380
+ cachedInput: 0.02 / M,
1381
+ cacheWrite: 0.25 / M,
1382
+ output: 1.2 / M
1383
+ }),
1384
+ contextBoundary: LONG_CONTEXT_BOUNDARY
1385
+ },
1386
+ // Existing direct models retain literal current Standard rows. These flat
1387
+ // rows keep native pricing aligned with the compatibility wrapper while the
1388
+ // private estimator still validates categories and billing mode strictly.
1389
+ {
1390
+ aliases: ["gpt-4o", "gpt-4o-2024-08-06", "gpt-4o-2024-11-20"],
1391
+ snapshotBase: "gpt-4o",
1392
+ short: { input: 2.5 / M, cachedInput: 1.25 / M, output: 10 / M }
1393
+ },
1394
+ {
1395
+ aliases: ["gpt-4o-mini", "gpt-4o-mini-2024-07-18"],
1396
+ snapshotBase: "gpt-4o-mini",
1397
+ short: { input: 0.15 / M, cachedInput: 0.075 / M, output: 0.6 / M }
1398
+ },
1399
+ {
1400
+ aliases: ["gpt-3.5-turbo"],
1401
+ snapshotBase: "gpt-3.5-turbo",
1402
+ short: { input: 0.5 / M, output: 1.5 / M }
1403
+ },
1404
+ {
1405
+ aliases: ["o1", "o1-2024-12-17"],
1406
+ snapshotBase: "o1",
1407
+ short: { input: 15 / M, cachedInput: 7.5 / M, output: 60 / M }
1408
+ },
1409
+ {
1410
+ aliases: ["o1-pro", "o1-pro-2025-03-19"],
1411
+ snapshotBase: "o1-pro",
1412
+ short: { input: 150 / M, output: 600 / M }
1413
+ },
1414
+ {
1415
+ aliases: ["gpt-4.1", "gpt-4.1-2025-04-14"],
1416
+ snapshotBase: "gpt-4.1",
1417
+ short: { input: 2 / M, cachedInput: 0.5 / M, output: 8 / M }
1418
+ },
1419
+ {
1420
+ aliases: ["gpt-4.1-mini", "gpt-4.1-mini-2025-04-14"],
1421
+ snapshotBase: "gpt-4.1-mini",
1422
+ short: { input: 0.4 / M, cachedInput: 0.1 / M, output: 1.6 / M }
1423
+ },
1424
+ {
1425
+ aliases: ["gpt-4.1-nano", "gpt-4.1-nano-2025-04-14"],
1426
+ snapshotBase: "gpt-4.1-nano",
1427
+ short: { input: 0.1 / M, cachedInput: 0.025 / M, output: 0.4 / M }
1428
+ },
1429
+ {
1430
+ aliases: ["o3", "o3-2025-04-16"],
1431
+ snapshotBase: "o3",
1432
+ short: { input: 2 / M, cachedInput: 0.5 / M, output: 8 / M }
1433
+ },
1434
+ {
1435
+ aliases: ["o3-mini", "o3-mini-2025-01-31"],
1436
+ snapshotBase: "o3-mini",
1437
+ short: { input: 1.1 / M, cachedInput: 0.55 / M, output: 4.4 / M }
1438
+ },
1439
+ {
1440
+ aliases: ["o3-pro", "o3-pro-2025-06-10"],
1441
+ snapshotBase: "o3-pro",
1442
+ short: { input: 20 / M, output: 80 / M }
1443
+ },
1444
+ {
1445
+ aliases: ["o4-mini", "o4-mini-2025-04-16"],
1446
+ snapshotBase: "o4-mini",
1447
+ short: { input: 1.1 / M, cachedInput: 0.275 / M, output: 4.4 / M }
1448
+ },
1449
+ {
1450
+ aliases: ["gpt-5", "gpt-5-2025-08-07"],
1451
+ snapshotBase: "gpt-5",
1452
+ short: { input: 1.25 / M, cachedInput: 0.125 / M, output: 10 / M }
1453
+ },
1454
+ {
1455
+ aliases: ["gpt-5-mini", "gpt-5-mini-2025-08-07"],
1456
+ snapshotBase: "gpt-5-mini",
1457
+ short: { input: 0.25 / M, cachedInput: 0.025 / M, output: 2 / M }
1458
+ },
1459
+ {
1460
+ aliases: ["gpt-5-nano", "gpt-5-nano-2025-08-07"],
1461
+ snapshotBase: "gpt-5-nano",
1462
+ short: { input: 0.05 / M, cachedInput: 5e-3 / M, output: 0.4 / M }
1463
+ },
1464
+ {
1465
+ aliases: ["gpt-5.1", "gpt-5.1-2025-11-13"],
1466
+ snapshotBase: "gpt-5.1",
1467
+ short: { input: 1.25 / M, cachedInput: 0.125 / M, output: 10 / M }
1468
+ },
1469
+ {
1470
+ aliases: ["gpt-5.2", "gpt-5.2-2025-12-11"],
1471
+ snapshotBase: "gpt-5.2",
1472
+ short: { input: 1.75 / M, cachedInput: 0.175 / M, output: 14 / M }
1473
+ },
1474
+ {
1475
+ aliases: ["gpt-5.4", "gpt-5.4-2026-03-05"],
1476
+ snapshotBase: "gpt-5.4",
1477
+ short: { input: 2.5 / M, cachedInput: 0.25 / M, output: 15 / M },
1478
+ long: withLongContext({ input: 2.5 / M, cachedInput: 0.25 / M, output: 15 / M }),
1479
+ contextBoundary: LONG_CONTEXT_BOUNDARY
1480
+ },
1481
+ {
1482
+ aliases: ["gpt-5.4-pro", "gpt-5.4-pro-2026-03-05"],
1483
+ snapshotBase: "gpt-5.4-pro",
1484
+ short: { input: 30 / M, output: 180 / M },
1485
+ long: withLongContext({ input: 30 / M, output: 180 / M }),
1486
+ contextBoundary: LONG_CONTEXT_BOUNDARY
1487
+ },
1488
+ {
1489
+ aliases: ["gpt-5.5", "gpt-5.5-2026-04-23"],
1490
+ snapshotBase: "gpt-5.5",
1491
+ short: { input: 5 / M, cachedInput: 0.5 / M, output: 30 / M },
1492
+ long: withLongContext({ input: 5 / M, cachedInput: 0.5 / M, output: 30 / M }),
1493
+ contextBoundary: LONG_CONTEXT_BOUNDARY
1494
+ },
1495
+ {
1496
+ aliases: ["gpt-5.5-pro", "gpt-5.5-pro-2026-04-23"],
1497
+ snapshotBase: "gpt-5.5-pro",
1498
+ short: { input: 30 / M, output: 180 / M },
1499
+ long: withLongContext({ input: 30 / M, output: 180 / M }),
1500
+ contextBoundary: LONG_CONTEXT_BOUNDARY
1501
+ },
1502
+ {
1503
+ aliases: ["gpt-5.4-mini", "gpt-5.4-mini-2026-03-17"],
1504
+ short: { input: 0.75 / M, cachedInput: 0.075 / M, output: 4.5 / M }
1505
+ },
1506
+ {
1507
+ aliases: ["gpt-5.4-nano", "gpt-5.4-nano-2026-03-17"],
1508
+ short: { input: 0.2 / M, cachedInput: 0.02 / M, output: 1.25 / M }
1509
+ },
1510
+ {
1511
+ aliases: ["gpt-5.2-pro", "gpt-5.2-pro-2025-12-11"],
1512
+ short: { input: 21 / M, output: 168 / M }
1513
+ },
1514
+ { aliases: ["gpt-5-pro", "gpt-5-pro-2025-10-06"], short: { input: 15 / M, output: 120 / M } },
1515
+ { aliases: ["gpt-3.5-turbo-instruct"], short: { input: 1.5 / M, output: 2 / M } },
1516
+ { aliases: ["davinci-002"], short: { input: 2 / M, output: 2 / M } },
1517
+ { aliases: ["babbage-002"], short: { input: 0.4 / M, output: 0.4 / M } },
1518
+ // Explicit snapshots with their own current dedicated-table rows. Do not
1519
+ // generalize these dates: a plausible-looking unlisted snapshot is unpriced.
1520
+ { aliases: ["gpt-4o-2024-05-13"], short: { input: 5 / M, output: 15 / M } },
1521
+ { aliases: ["gpt-4-turbo-2024-04-09"], short: { input: 10 / M, output: 30 / M } },
1522
+ { aliases: ["gpt-4-0613"], short: { input: 30 / M, output: 60 / M } },
1523
+ {
1524
+ aliases: ["gpt-3.5-turbo-0125"],
1525
+ short: { input: 0.5 / M, output: 1.5 / M }
1526
+ },
1527
+ { aliases: ["gpt-3.5-turbo-1106"], short: { input: 1 / M, output: 2 / M } }
1528
+ ];
1529
+ function directOpenAIModel(model) {
1530
+ return DIRECT_OPENAI_CATALOG.find((entry) => entry.aliases.includes(model));
1531
+ }
1532
+ var CANONICAL_OPENAI_BASE_URL = "https://api.openai.com/v1";
1533
+ function isTextContentPart(value, type) {
1534
+ return value !== null && typeof value === "object" && value.type === type && typeof value.text === "string";
1535
+ }
1536
+ function hasUnmodeledDirectOpenAIContent(request) {
1537
+ if ("messages" in request) {
1538
+ if (!Array.isArray(request.messages)) return true;
1539
+ for (const message of request.messages) {
1540
+ if (message === null || typeof message !== "object") return true;
1541
+ const content = message.content;
1542
+ if (content === void 0 || content === null || typeof content === "string") continue;
1543
+ if (!Array.isArray(content) || content.some((part) => !isTextContentPart(part, "text"))) {
1544
+ return true;
1545
+ }
1546
+ }
1547
+ return false;
1548
+ }
1549
+ if ("input" in request) {
1550
+ const input = request.input;
1551
+ if (typeof input === "string") return false;
1552
+ if (!Array.isArray(input)) return true;
1553
+ for (const item of input) {
1554
+ if (item === null || typeof item !== "object") return true;
1555
+ const typed = item;
1556
+ switch (typed.type) {
1557
+ case "message": {
1558
+ const content = typed.content;
1559
+ if (typeof content === "string") break;
1560
+ if (!Array.isArray(content) || content.some((part) => !isTextContentPart(part, "input_text"))) {
1561
+ return true;
1562
+ }
1563
+ break;
1564
+ }
1565
+ case "function_call":
1566
+ break;
1567
+ case "function_call_output": {
1568
+ const output = typed.output;
1569
+ if (typeof output === "string") break;
1570
+ if (!Array.isArray(output) || output.some((part) => !isTextContentPart(part, "input_text"))) {
1571
+ return true;
1572
+ }
1573
+ break;
1574
+ }
1575
+ case "custom_tool_call":
1576
+ if (typeof typed.input !== "string") return true;
1577
+ break;
1578
+ case "custom_tool_call_output":
1579
+ if (typeof typed.output !== "string") return true;
1580
+ break;
1581
+ case "reasoning":
1582
+ break;
1583
+ default:
1584
+ return true;
1585
+ }
1586
+ }
1587
+ return false;
1588
+ }
1589
+ return false;
1590
+ }
1591
+ function isEligibleDirectOpenAIContext(context) {
1592
+ const request = context?.request;
1593
+ const response = context?.response;
1594
+ if (context?.baseUrl !== void 0 && context.baseUrl !== CANONICAL_OPENAI_BASE_URL) return false;
1595
+ if (request && hasUnmodeledDirectOpenAIContent(request)) return false;
1596
+ const tier = response?.service_tier ?? response?.serviceTier ?? request?.service_tier ?? request?.serviceTier;
1597
+ if (tier !== void 0 && (typeof tier !== "string" || !["standard", "default"].includes(tier.toLowerCase()))) {
1598
+ return false;
1599
+ }
1600
+ if (["region", "inference_geo", "data_residency"].some((key) => request?.[key] !== void 0)) {
1601
+ return false;
1602
+ }
1603
+ if (["previous_response_id", "conversation", "prompt"].some((key) => request?.[key] !== void 0)) {
1604
+ return false;
1605
+ }
1606
+ const reasoning = request?.reasoning;
1607
+ if (reasoning !== null && typeof reasoning === "object" && "mode" in reasoning && reasoning.mode !== void 0) {
1608
+ return false;
1609
+ }
1610
+ 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)) {
1611
+ return false;
1612
+ }
1613
+ if (Array.isArray(request?.tools) && request.tools.some(
1614
+ (tool2) => tool2 === null || typeof tool2 !== "object" || !["function", "custom"].includes(tool2.type)
1615
+ )) {
1616
+ return false;
1617
+ }
1618
+ return true;
1619
+ }
1620
+ function estimateDirectOpenAICost(model, usage, context) {
1621
+ if (!isEligibleDirectOpenAIContext(context)) return void 0;
1622
+ const entry = directOpenAIModel(model);
1623
+ if (!entry) return void 0;
1624
+ const { prompt_tokens, completion_tokens, cached_tokens, cache_write_tokens } = usage;
1625
+ const cached = cached_tokens ?? 0;
1626
+ const cacheWrite = cache_write_tokens ?? 0;
1627
+ if (![prompt_tokens, completion_tokens, cached, cacheWrite].every(
1628
+ (count) => Number.isSafeInteger(count) && count >= 0
1629
+ ) || cached + cacheWrite > prompt_tokens) {
1630
+ return void 0;
1631
+ }
1632
+ const rates = entry.long !== void 0 && entry.contextBoundary !== void 0 && prompt_tokens > entry.contextBoundary ? entry.long : entry.short;
1633
+ if (cached > 0 && rates.cachedInput === void 0 || cacheWrite > 0 && rates.cacheWrite === void 0) {
1634
+ return void 0;
1635
+ }
1636
+ const ordinary = prompt_tokens - cached - cacheWrite;
1637
+ return ordinary * rates.input + cached * (rates.cachedInput ?? 0) + cacheWrite * (rates.cacheWrite ?? 0) + completion_tokens * rates.output;
1638
+ }
1270
1639
  function estimateOpenAICost(model, promptTokens, completionTokens, cachedTokens) {
1271
- return priceFromTable(OPENAI_PRICING, model, promptTokens, completionTokens, cachedTokens);
1640
+ return estimateDirectOpenAICost(model, {
1641
+ prompt_tokens: promptTokens,
1642
+ completion_tokens: completionTokens,
1643
+ total_tokens: promptTokens + completionTokens,
1644
+ cached_tokens: cachedTokens
1645
+ });
1272
1646
  }
1273
1647
  function isOSeriesModel(model) {
1274
1648
  return /^(o1|o3|o4-mini)/.test(model);
@@ -1276,6 +1650,15 @@ function isOSeriesModel(model) {
1276
1650
  function supportsReasoningEffort(model) {
1277
1651
  return isOSeriesModel(model) || /^gpt-5/.test(model);
1278
1652
  }
1653
+ var GPT_56_REASONING_MODELS = /* @__PURE__ */ new Set([
1654
+ "gpt-5.6",
1655
+ "gpt-5.6-sol",
1656
+ "gpt-5.6-terra",
1657
+ "gpt-5.6-luna"
1658
+ ]);
1659
+ function supportsMaxReasoningEffort(model) {
1660
+ return GPT_56_REASONING_MODELS.has(model);
1661
+ }
1279
1662
  function supportsReasoningNone(model) {
1280
1663
  return /^gpt-5\.[1-9]/.test(model);
1281
1664
  }
@@ -1285,31 +1668,46 @@ function supportsXhigh(model) {
1285
1668
  function clampReasoningEffort(model, effort) {
1286
1669
  if (model.startsWith("gpt-5-pro")) return "high";
1287
1670
  if (effort === "none" && !supportsReasoningNone(model)) return "minimal";
1671
+ if (effort === "max" && !supportsMaxReasoningEffort(model)) {
1672
+ return clampReasoningEffort(model, "xhigh");
1673
+ }
1288
1674
  if (effort === "xhigh" && !supportsXhigh(model)) return "high";
1289
1675
  return effort;
1290
1676
  }
1291
- function effortToReasoningEffort(effort) {
1292
- return effort === "max" ? "xhigh" : effort;
1677
+ function effortToReasoningEffort(effort, model) {
1678
+ return effort === "max" && model !== void 0 && supportsMaxReasoningEffort(model) ? "max" : effort === "max" ? "xhigh" : effort;
1293
1679
  }
1294
1680
  function budgetToReasoningEffort(budget) {
1295
1681
  if (budget <= 1024) return "low";
1296
1682
  if (budget <= 8192) return "medium";
1297
1683
  return "high";
1298
1684
  }
1685
+ function resolveOpenAIReasoningEffort(model, resolved) {
1686
+ if (!supportsReasoningEffort(model)) return void 0;
1687
+ if (resolved.hasBudgetOverride) {
1688
+ return clampReasoningEffort(model, budgetToReasoningEffort(resolved.thinkingBudget));
1689
+ }
1690
+ if (!resolved.thinkingDisabled && resolved.activeEffort) {
1691
+ return clampReasoningEffort(model, effortToReasoningEffort(resolved.activeEffort, model));
1692
+ }
1693
+ return resolved.thinkingDisabled ? clampReasoningEffort(model, "none") : void 0;
1694
+ }
1695
+ var warnedChatMaxDowngrades = /* @__PURE__ */ new Set();
1696
+ function resolveOpenAIChatReasoningEffort(model, resolved) {
1697
+ const effort = resolveOpenAIReasoningEffort(model, resolved);
1698
+ if (effort !== "max") return effort;
1699
+ if (resolved.activeEffort === "max" && !warnedChatMaxDowngrades.has(model)) {
1700
+ warnedChatMaxDowngrades.add(model);
1701
+ console.warn(
1702
+ `[axl] OpenAI Chat Completions does not accept effort "max" for ${model}; using "xhigh". Use the OpenAI Responses provider for native "max".`
1703
+ );
1704
+ }
1705
+ return "xhigh";
1706
+ }
1299
1707
  var openaiReasoningEmit = (body, resolved, model) => {
1300
1708
  const oSeries = isOSeriesModel(model);
1301
1709
  const reasoningCapable = supportsReasoningEffort(model);
1302
- const { thinkingBudget, thinkingDisabled, activeEffort, hasBudgetOverride } = resolved;
1303
- let wireEffort;
1304
- if (reasoningCapable) {
1305
- if (hasBudgetOverride) {
1306
- wireEffort = clampReasoningEffort(model, budgetToReasoningEffort(thinkingBudget));
1307
- } else if (!thinkingDisabled && activeEffort) {
1308
- wireEffort = clampReasoningEffort(model, effortToReasoningEffort(activeEffort));
1309
- } else if (thinkingDisabled) {
1310
- wireEffort = clampReasoningEffort(model, "none");
1311
- }
1312
- }
1710
+ const wireEffort = resolveOpenAIChatReasoningEffort(model, resolved);
1313
1711
  if (wireEffort) body.reasoning_effort = wireEffort;
1314
1712
  return { stripTemperature: oSeries || reasoningCapable && wireEffort !== void 0 };
1315
1713
  };
@@ -1319,7 +1717,7 @@ var OPENAI_PROFILE = {
1319
1717
  defaultBaseUrl: "https://api.openai.com/v1",
1320
1718
  envApiKey: "OPENAI_API_KEY",
1321
1719
  envBaseUrl: "OPENAI_BASE_URL",
1322
- pricing: { kind: "table", table: OPENAI_PRICING },
1720
+ pricing: { kind: "table", table: OPENAI_PRICING, match: "exact" },
1323
1721
  reasoning: { emit: openaiReasoningEmit, capture: "none" },
1324
1722
  roleFor: (role, model) => role === "system" && isOSeriesModel(model) ? "developer" : role,
1325
1723
  maxTokensField: "max_completion_tokens",
@@ -1329,6 +1727,9 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
1329
1727
  constructor(options = {}) {
1330
1728
  super({ profile: OPENAI_PROFILE, ...options });
1331
1729
  }
1730
+ computeCost(model, usage, _reportedCost, context) {
1731
+ return usage ? estimateDirectOpenAICost(model, usage, { ...context, baseUrl: this.baseUrl }) : void 0;
1732
+ }
1332
1733
  };
1333
1734
 
1334
1735
  // src/providers/openai-responses.ts
@@ -1389,7 +1790,7 @@ var OpenAIResponsesProvider = class {
1389
1790
  });
1390
1791
  }
1391
1792
  const json = await res.json();
1392
- return this.parseResponse(json, options.model);
1793
+ return this.parseResponse(json, this.requestModel(body, options.model), body);
1393
1794
  }
1394
1795
  // ---------------------------------------------------------------------------
1395
1796
  // stream - SSE streaming
@@ -1421,30 +1822,23 @@ var OpenAIResponsesProvider = class {
1421
1822
  if (!res.body) {
1422
1823
  throw new Error("OpenAI Responses stream has no body");
1423
1824
  }
1424
- yield* this.parseSSEStream(res.body, options.model);
1825
+ yield* this.parseSSEStream(res.body, this.requestModel(body, options.model), body);
1425
1826
  }
1426
1827
  // ---------------------------------------------------------------------------
1427
1828
  // Internal: build request body
1428
1829
  // ---------------------------------------------------------------------------
1429
1830
  buildRequestBody(messages, options, stream) {
1430
- const oSeries = isOSeriesModel(options.model);
1431
- const reasoningCapable = supportsReasoningEffort(options.model);
1432
- const { thinkingBudget, includeThoughts, thinkingDisabled, activeEffort, hasBudgetOverride } = resolveThinkingOptions(options);
1433
- let wireEffort;
1434
- if (reasoningCapable) {
1435
- if (hasBudgetOverride) {
1436
- wireEffort = clampReasoningEffort(options.model, budgetToReasoningEffort(thinkingBudget));
1437
- } else if (!thinkingDisabled && activeEffort) {
1438
- wireEffort = clampReasoningEffort(options.model, effortToReasoningEffort(activeEffort));
1439
- } else if (thinkingDisabled) {
1440
- wireEffort = clampReasoningEffort(options.model, "none");
1441
- }
1442
- }
1831
+ const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
1832
+ const oSeries = isOSeriesModel(effectiveModel);
1833
+ const reasoningCapable = supportsReasoningEffort(effectiveModel);
1834
+ const resolved = resolveThinkingOptions(options);
1835
+ const { includeThoughts } = resolved;
1836
+ const wireEffort = resolveOpenAIReasoningEffort(effectiveModel, resolved);
1443
1837
  const stripTemp = oSeries || reasoningCapable && wireEffort !== void 0;
1444
1838
  const systemMessages = messages.filter((m) => m.role === "system");
1445
1839
  const nonSystemMessages = messages.filter((m) => m.role !== "system");
1446
1840
  const body = {
1447
- model: options.model,
1841
+ model: effectiveModel,
1448
1842
  input: this.buildInput(nonSystemMessages),
1449
1843
  store: false,
1450
1844
  stream
@@ -1491,6 +1885,9 @@ var OpenAIResponsesProvider = class {
1491
1885
  }
1492
1886
  return body;
1493
1887
  }
1888
+ requestModel(body, fallback) {
1889
+ return typeof body.model === "string" ? body.model : fallback;
1890
+ }
1494
1891
  // ---------------------------------------------------------------------------
1495
1892
  // Internal: message → input mapping
1496
1893
  // ---------------------------------------------------------------------------
@@ -1558,7 +1955,7 @@ var OpenAIResponsesProvider = class {
1558
1955
  // ---------------------------------------------------------------------------
1559
1956
  // Internal: parse non-streaming response
1560
1957
  // ---------------------------------------------------------------------------
1561
- parseResponse(json, model) {
1958
+ parseResponse(json, model, request) {
1562
1959
  let content = "";
1563
1960
  let thinkingContent = "";
1564
1961
  const toolCalls = [];
@@ -1595,9 +1992,14 @@ var OpenAIResponsesProvider = class {
1595
1992
  completion_tokens: json.usage.output_tokens,
1596
1993
  total_tokens: json.usage.total_tokens,
1597
1994
  reasoning_tokens: json.usage.output_tokens_details?.reasoning_tokens,
1598
- cached_tokens: json.usage.input_tokens_details?.cached_tokens
1995
+ cached_tokens: json.usage.input_tokens_details?.cached_tokens,
1996
+ cache_write_tokens: json.usage.input_tokens_details?.cache_write_tokens
1599
1997
  } : void 0;
1600
- const cost = usage ? estimateOpenAICost(model, usage.prompt_tokens, usage.completion_tokens, usage.cached_tokens) : void 0;
1998
+ const cost = usage ? estimateDirectOpenAICost(json.model ?? model, usage, {
1999
+ baseUrl: this.baseUrl,
2000
+ request,
2001
+ response: json
2002
+ }) : void 0;
1601
2003
  const providerMetadata = reasoningItems.length > 0 ? { openaiReasoningItems: reasoningItems } : void 0;
1602
2004
  return {
1603
2005
  content,
@@ -1611,7 +2013,7 @@ var OpenAIResponsesProvider = class {
1611
2013
  // ---------------------------------------------------------------------------
1612
2014
  // Internal: parse SSE stream
1613
2015
  // ---------------------------------------------------------------------------
1614
- async *parseSSEStream(body, model) {
2016
+ async *parseSSEStream(body, model, request) {
1615
2017
  const reader = body.getReader();
1616
2018
  const decoder = new TextDecoder();
1617
2019
  let buffer = "";
@@ -1639,7 +2041,7 @@ var OpenAIResponsesProvider = class {
1639
2041
  } catch {
1640
2042
  continue;
1641
2043
  }
1642
- const chunk = this.handleStreamEvent(eventType, data, model, callIdMap);
2044
+ const chunk = this.handleStreamEvent(eventType, data, model, callIdMap, request);
1643
2045
  if (chunk) {
1644
2046
  yield chunk;
1645
2047
  if (chunk.type === "done") return;
@@ -1653,7 +2055,7 @@ var OpenAIResponsesProvider = class {
1653
2055
  reader.releaseLock();
1654
2056
  }
1655
2057
  }
1656
- handleStreamEvent(eventType, data, model, callIdMap) {
2058
+ handleStreamEvent(eventType, data, model, callIdMap, request) {
1657
2059
  switch (eventType) {
1658
2060
  case "response.output_text.delta":
1659
2061
  return { type: "text_delta", content: data.delta ?? "" };
@@ -1687,19 +2089,19 @@ var OpenAIResponsesProvider = class {
1687
2089
  completion_tokens: response.usage.output_tokens,
1688
2090
  total_tokens: response.usage.total_tokens,
1689
2091
  reasoning_tokens: response.usage.output_tokens_details?.reasoning_tokens,
1690
- cached_tokens: response.usage.input_tokens_details?.cached_tokens
2092
+ cached_tokens: response.usage.input_tokens_details?.cached_tokens,
2093
+ cache_write_tokens: response.usage.input_tokens_details?.cache_write_tokens
1691
2094
  } : void 0;
1692
2095
  const reasoningItems = response?.output?.filter((item) => item.type === "reasoning") ?? [];
1693
2096
  const providerMetadata = reasoningItems.length > 0 ? { openaiReasoningItems: reasoningItems } : void 0;
1694
2097
  return {
1695
2098
  type: "done",
1696
2099
  usage,
1697
- cost: usage ? estimateOpenAICost(
1698
- model,
1699
- usage.prompt_tokens,
1700
- usage.completion_tokens,
1701
- usage.cached_tokens
1702
- ) : void 0,
2100
+ cost: usage ? estimateDirectOpenAICost(response?.model ?? model, usage, {
2101
+ baseUrl: this.baseUrl,
2102
+ request,
2103
+ response
2104
+ }) : void 0,
1703
2105
  providerMetadata
1704
2106
  };
1705
2107
  }
@@ -1733,68 +2135,357 @@ var OpenAIResponsesProvider = class {
1733
2135
 
1734
2136
  // src/providers/anthropic.ts
1735
2137
  var ANTHROPIC_API_VERSION = "2023-06-01";
1736
- var ANTHROPIC_PRICING = {
1737
- "claude-opus-4-8": [5e-6, 25e-6],
1738
- "claude-opus-4-7": [5e-6, 25e-6],
1739
- "claude-opus-4-6": [5e-6, 25e-6],
1740
- "claude-sonnet-4-6": [3e-6, 15e-6],
1741
- "claude-opus-4-5": [5e-6, 25e-6],
1742
- "claude-opus-4-1": [15e-6, 75e-6],
1743
- "claude-sonnet-4-5": [3e-6, 15e-6],
1744
- "claude-haiku-4-5": [1e-6, 5e-6],
1745
- "claude-sonnet-4": [3e-6, 15e-6],
1746
- "claude-opus-4": [15e-6, 75e-6],
1747
- "claude-3-7-sonnet": [3e-6, 15e-6],
1748
- "claude-3-5-sonnet": [3e-6, 15e-6],
1749
- "claude-3-5-haiku": [8e-7, 4e-6],
1750
- "claude-3-opus": [15e-6, 75e-6],
1751
- "claude-3-sonnet": [3e-6, 15e-6],
1752
- "claude-3-haiku": [25e-8, 125e-8]
2138
+ var CLAUDE_CAPABILITIES = {
2139
+ "claude-fable-5": {
2140
+ thinking: "adaptive-always-on",
2141
+ effortLevels: ["low", "medium", "high", "xhigh", "max"],
2142
+ manualBudget: false,
2143
+ stripTemperature: true
2144
+ },
2145
+ "claude-opus-5": {
2146
+ thinking: "adaptive-default-on",
2147
+ effortLevels: ["low", "medium", "high", "xhigh", "max"],
2148
+ manualBudget: false,
2149
+ stripTemperature: true,
2150
+ disableAt: "high"
2151
+ },
2152
+ "claude-sonnet-5": {
2153
+ thinking: "adaptive-default-on",
2154
+ effortLevels: ["low", "medium", "high", "xhigh", "max"],
2155
+ manualBudget: false,
2156
+ stripTemperature: true,
2157
+ disableAt: "max"
2158
+ },
2159
+ "claude-opus-4-8": {
2160
+ thinking: "adaptive-optional",
2161
+ effortLevels: ["low", "medium", "high", "xhigh", "max"],
2162
+ manualBudget: false,
2163
+ stripTemperature: true
2164
+ },
2165
+ "claude-opus-4-7": {
2166
+ thinking: "adaptive-optional",
2167
+ effortLevels: ["low", "medium", "high", "xhigh", "max"],
2168
+ manualBudget: false,
2169
+ stripTemperature: true
2170
+ },
2171
+ "claude-opus-4-6": {
2172
+ thinking: "adaptive-optional",
2173
+ effortLevels: ["low", "medium", "high", "max"],
2174
+ manualBudget: true
2175
+ },
2176
+ "claude-sonnet-4-6": {
2177
+ thinking: "adaptive-optional",
2178
+ effortLevels: ["low", "medium", "high", "max"],
2179
+ manualBudget: true
2180
+ },
2181
+ "claude-opus-4-5": {
2182
+ thinking: "effort-only",
2183
+ effortLevels: ["low", "medium", "high"],
2184
+ manualBudget: true
2185
+ },
2186
+ "claude-opus-4-5-20251101": {
2187
+ thinking: "effort-only",
2188
+ effortLevels: ["low", "medium", "high"],
2189
+ manualBudget: true
2190
+ }
1753
2191
  };
1754
- var ANTHROPIC_PRICING_KEYS_BY_LENGTH = Object.keys(ANTHROPIC_PRICING).sort(
1755
- (a, b) => b.length - a.length
1756
- );
1757
- function estimateAnthropicCost(model, inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens) {
1758
- let pricing = ANTHROPIC_PRICING[model];
1759
- if (!pricing) {
1760
- for (const key of ANTHROPIC_PRICING_KEYS_BY_LENGTH) {
1761
- if (model.startsWith(key)) {
1762
- pricing = ANTHROPIC_PRICING[key];
1763
- break;
1764
- }
1765
- }
2192
+ var LEGACY_CLAUDE_CAPABILITY = {
2193
+ thinking: "legacy-manual",
2194
+ manualBudget: true
2195
+ };
2196
+ var LEGACY_CLAUDE_MODELS = /* @__PURE__ */ new Set([
2197
+ "claude-opus-4-1",
2198
+ "claude-opus-4-1-20250805",
2199
+ "claude-sonnet-4-5",
2200
+ "claude-sonnet-4-5-20250929",
2201
+ "claude-haiku-4-5",
2202
+ "claude-haiku-4-5-20251001",
2203
+ "claude-sonnet-4",
2204
+ "claude-sonnet-4-20250514",
2205
+ "claude-opus-4",
2206
+ "claude-opus-4-20250514",
2207
+ "claude-3-7-sonnet",
2208
+ "claude-3-7-sonnet-20250219",
2209
+ "claude-3-5-sonnet",
2210
+ "claude-3-5-sonnet-20241022",
2211
+ "claude-3-5-sonnet-20240620",
2212
+ "claude-3-5-haiku",
2213
+ "claude-3-5-haiku-20241022",
2214
+ "claude-3-opus",
2215
+ "claude-3-opus-20240229",
2216
+ "claude-3-sonnet",
2217
+ "claude-3-sonnet-20240229",
2218
+ "claude-3-haiku",
2219
+ "claude-3-haiku-20240307"
2220
+ ]);
2221
+ var PRE_INFERENCE_GEO_MODELS = /* @__PURE__ */ new Set([
2222
+ ...LEGACY_CLAUDE_MODELS,
2223
+ "claude-opus-4-5",
2224
+ "claude-opus-4-5-20251101"
2225
+ ]);
2226
+ function resolveClaudeCapability(model) {
2227
+ return CLAUDE_CAPABILITIES[model] ?? (LEGACY_CLAUDE_MODELS.has(model) ? LEGACY_CLAUDE_CAPABILITY : void 0);
2228
+ }
2229
+ var ANTHROPIC_RATES = {
2230
+ "claude-fable-5": { input: 1e-5, output: 5e-5 },
2231
+ "claude-opus-5": { input: 5e-6, output: 25e-6 },
2232
+ "claude-sonnet-5": { input: 3e-6, output: 15e-6, sonnet5Intro: true },
2233
+ "claude-opus-4-8": { input: 5e-6, output: 25e-6 },
2234
+ "claude-opus-4-7": { input: 5e-6, output: 25e-6 },
2235
+ "claude-opus-4-6": { input: 5e-6, output: 25e-6 },
2236
+ "claude-sonnet-4-6": { input: 3e-6, output: 15e-6 },
2237
+ "claude-opus-4-5": { input: 5e-6, output: 25e-6 },
2238
+ "claude-opus-4-5-20251101": { input: 5e-6, output: 25e-6 },
2239
+ "claude-opus-4-1": { input: 15e-6, output: 75e-6 },
2240
+ "claude-opus-4-1-20250805": { input: 15e-6, output: 75e-6 },
2241
+ "claude-sonnet-4-5": { input: 3e-6, output: 15e-6 },
2242
+ "claude-sonnet-4-5-20250929": { input: 3e-6, output: 15e-6 },
2243
+ "claude-haiku-4-5": { input: 1e-6, output: 5e-6 },
2244
+ "claude-haiku-4-5-20251001": { input: 1e-6, output: 5e-6 }
2245
+ };
2246
+ function isValidTokenCount(value) {
2247
+ return value !== void 0 && Number.isSafeInteger(value) && value >= 0;
2248
+ }
2249
+ function estimateAnthropicCost(model, usage, now = /* @__PURE__ */ new Date()) {
2250
+ if (!model || !isValidTokenCount(usage.inputTokens) || !isValidTokenCount(usage.outputTokens)) {
2251
+ return void 0;
1766
2252
  }
1767
- if (!pricing) return void 0;
1768
- const [inputRate, outputRate] = pricing;
1769
- const cacheRead = cacheReadTokens ?? 0;
1770
- const cacheWrite = cacheWriteTokens ?? 0;
1771
- const inputCost = (inputTokens - cacheRead - cacheWrite) * inputRate + cacheRead * inputRate * 0.1 + cacheWrite * inputRate * 1.25;
1772
- return inputCost + outputTokens * outputRate;
2253
+ const rate = ANTHROPIC_RATES[model];
2254
+ if (!rate) return void 0;
2255
+ const cacheRead = usage.cacheReadTokens ?? 0;
2256
+ const cacheWrite5m = usage.cacheWrite5mTokens ?? 0;
2257
+ const cacheWrite1h = usage.cacheWrite1hTokens ?? 0;
2258
+ if (!isValidTokenCount(cacheRead) || !isValidTokenCount(cacheWrite5m) || !isValidTokenCount(cacheWrite1h)) {
2259
+ return void 0;
2260
+ }
2261
+ const cacheWriteTotal = safeTokenSum(cacheWrite5m, cacheWrite1h);
2262
+ if (cacheWriteTotal === void 0 || safeTokenSum(usage.inputTokens, cacheRead, cacheWriteTotal) === void 0) {
2263
+ return void 0;
2264
+ }
2265
+ if (usage.aggregateCacheWriteTokens !== void 0 && (!isValidTokenCount(usage.aggregateCacheWriteTokens) || usage.aggregateCacheWriteTokens !== cacheWriteTotal)) {
2266
+ return void 0;
2267
+ }
2268
+ const { input, output } = rate.sonnet5Intro && now < /* @__PURE__ */ new Date("2026-09-01T00:00:00Z") ? { input: 2e-6, output: 1e-5 } : rate;
2269
+ return usage.inputTokens * input + cacheRead * input * 0.1 + cacheWrite5m * input * 1.25 + cacheWrite1h * input * 2 + usage.outputTokens * output;
1773
2270
  }
1774
2271
  var THINKING_BUDGETS = {
1775
2272
  low: 1024,
1776
2273
  medium: 5e3,
1777
2274
  high: 1e4,
2275
+ xhigh: 2e4,
1778
2276
  // 30000 (not 32000) to stay under the 32K max_tokens limit on Opus 4/4.1.
1779
2277
  // With auto-bump (+1024), max_tokens becomes 31024 which fits all models.
1780
2278
  max: 3e4
1781
2279
  };
1782
- function supportsAdaptiveThinking(model) {
1783
- return model.startsWith("claude-opus-4-8") || model.startsWith("claude-opus-4-7") || model.startsWith("claude-opus-4-6") || model.startsWith("claude-sonnet-4-6");
2280
+ function clampAnthropicEffort(capability, effort) {
2281
+ const levels = capability.effortLevels;
2282
+ if (!levels) return effort === "max" || effort === "xhigh" ? "high" : effort;
2283
+ if (levels.includes(effort)) return effort;
2284
+ return "high";
1784
2285
  }
1785
- function supportsMaxEffort(model) {
1786
- return model.startsWith("claude-opus-4-8") || model.startsWith("claude-opus-4-7") || model.startsWith("claude-opus-4-6");
2286
+ function budgetToClaudeEffort(budget) {
2287
+ if (budget <= THINKING_BUDGETS.low) return "low";
2288
+ if (budget <= THINKING_BUDGETS.medium) return "medium";
2289
+ if (budget <= THINKING_BUDGETS.high) return "high";
2290
+ if (budget <= THINKING_BUDGETS.xhigh) return "xhigh";
2291
+ return "max";
2292
+ }
2293
+ var warnedFableDisable = /* @__PURE__ */ new Set();
2294
+ function warnFableDisable(model) {
2295
+ if (warnedFableDisable.has(model)) return;
2296
+ warnedFableDisable.add(model);
2297
+ console.warn(
2298
+ `[axl] thinking cannot be disabled on Anthropic ${model}; using adaptive thinking at effort 'low'.`
2299
+ );
1787
2300
  }
1788
- function supportsXhigh2(model) {
1789
- return model.startsWith("claude-opus-4-8") || model.startsWith("claude-opus-4-7");
2301
+ function resolveClaudeThinking(model, resolved) {
2302
+ const capability = resolveClaudeCapability(model);
2303
+ if (!capability) return { stripTemperature: false };
2304
+ const activeEffort = resolved.activeEffort ? clampAnthropicEffort(capability, resolved.activeEffort) : void 0;
2305
+ const isClaude5 = capability.thinking === "adaptive-default-on" || capability.thinking === "adaptive-always-on";
2306
+ if (isClaude5) {
2307
+ if (resolved.hasBudgetOverride) {
2308
+ const effort = clampAnthropicEffort(
2309
+ capability,
2310
+ budgetToClaudeEffort(resolved.thinkingBudget)
2311
+ );
2312
+ return {
2313
+ thinking: { type: "adaptive" },
2314
+ outputConfig: { effort },
2315
+ stripTemperature: true
2316
+ };
2317
+ }
2318
+ if (resolved.thinkingDisabled) {
2319
+ if (capability.thinking === "adaptive-always-on") {
2320
+ warnFableDisable(model);
2321
+ return {
2322
+ thinking: { type: "adaptive" },
2323
+ outputConfig: { effort: "low" },
2324
+ stripTemperature: true
2325
+ };
2326
+ }
2327
+ if (activeEffort && capability.disableAt && !isEffortAtOrBelow(activeEffort, capability.disableAt)) {
2328
+ return {
2329
+ thinking: { type: "adaptive" },
2330
+ outputConfig: { effort: activeEffort },
2331
+ stripTemperature: true
2332
+ };
2333
+ }
2334
+ return {
2335
+ thinking: { type: "disabled" },
2336
+ outputConfig: activeEffort ? { effort: activeEffort } : void 0,
2337
+ stripTemperature: true
2338
+ };
2339
+ }
2340
+ return activeEffort ? {
2341
+ thinking: { type: "adaptive" },
2342
+ outputConfig: { effort: activeEffort },
2343
+ stripTemperature: true
2344
+ } : { stripTemperature: true };
2345
+ }
2346
+ if (resolved.hasBudgetOverride) {
2347
+ if (!capability.manualBudget) {
2348
+ const effort = clampAnthropicEffort(
2349
+ capability,
2350
+ budgetToClaudeEffort(resolved.thinkingBudget)
2351
+ );
2352
+ return capability.thinking === "adaptive-optional" ? {
2353
+ thinking: { type: "adaptive" },
2354
+ outputConfig: { effort },
2355
+ stripTemperature: capability.stripTemperature ?? true
2356
+ } : { outputConfig: { effort }, stripTemperature: capability.stripTemperature ?? false };
2357
+ }
2358
+ return {
2359
+ thinking: { type: "enabled", budget_tokens: resolved.thinkingBudget },
2360
+ outputConfig: activeEffort && capability.effortLevels ? { effort: activeEffort } : void 0,
2361
+ manualBudget: resolved.thinkingBudget,
2362
+ stripTemperature: capability.stripTemperature ?? true
2363
+ };
2364
+ }
2365
+ if (resolved.thinkingDisabled) {
2366
+ return {
2367
+ outputConfig: activeEffort && capability.effortLevels ? { effort: activeEffort } : void 0,
2368
+ stripTemperature: capability.stripTemperature ?? false
2369
+ };
2370
+ }
2371
+ if (activeEffort && capability.thinking === "adaptive-optional") {
2372
+ return {
2373
+ thinking: { type: "adaptive" },
2374
+ outputConfig: { effort: activeEffort },
2375
+ stripTemperature: capability.stripTemperature ?? true
2376
+ };
2377
+ }
2378
+ if (activeEffort && capability.thinking === "effort-only") {
2379
+ return { outputConfig: { effort: activeEffort }, stripTemperature: false };
2380
+ }
2381
+ if (activeEffort) {
2382
+ const budget = THINKING_BUDGETS[activeEffort] ?? THINKING_BUDGETS.medium;
2383
+ return {
2384
+ thinking: { type: "enabled", budget_tokens: budget },
2385
+ manualBudget: budget,
2386
+ stripTemperature: true
2387
+ };
2388
+ }
2389
+ return { stripTemperature: capability.stripTemperature ?? false };
1790
2390
  }
1791
- function supportsEffort(model) {
1792
- return model.startsWith("claude-opus-4-8") || model.startsWith("claude-opus-4-7") || model.startsWith("claude-opus-4-6") || model.startsWith("claude-sonnet-4-6") || model.startsWith("claude-opus-4-5");
2391
+ function isEffortAtOrBelow(effort, ceiling) {
2392
+ const levels = ["low", "medium", "high", "xhigh", "max"];
2393
+ return levels.indexOf(effort) <= levels.indexOf(ceiling);
1793
2394
  }
1794
- function clampAnthropicEffort(model, effort) {
1795
- if (effort === "max" && !supportsMaxEffort(model)) return "high";
1796
- if (effort === "xhigh" && !supportsXhigh2(model)) return "high";
1797
- return effort;
2395
+ function pricingContextFromBody(body) {
2396
+ return {
2397
+ model: typeof body.model === "string" ? body.model : void 0,
2398
+ unpricedModifier: isAnthropicModifier("inference_geo", body.inference_geo) || isAnthropicModifier("speed", body.speed) || hasAnthropicServerTool(body.tools) || body.fallbacks !== void 0
2399
+ };
2400
+ }
2401
+ function isModifiedAnthropicResponse(json, model) {
2402
+ 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);
2403
+ }
2404
+ function isAnthropicModifier(kind, value) {
2405
+ if (value === void 0) return false;
2406
+ return kind === "speed" ? value !== "standard" : value !== "global";
2407
+ }
2408
+ function isAnthropicResponseModifier(kind, value, model) {
2409
+ if (value === void 0) return false;
2410
+ if (kind === "speed") return value !== "standard";
2411
+ if (value === "global") return false;
2412
+ return value !== "not_available" || model === void 0 || !PRE_INFERENCE_GEO_MODELS.has(model);
2413
+ }
2414
+ function hasAnthropicServerTool(tools) {
2415
+ return Array.isArray(tools) && tools.some(
2416
+ (tool2) => tool2 !== null && typeof tool2 === "object" && typeof tool2.type === "string"
2417
+ );
2418
+ }
2419
+ function hasBilledUnmodeledUsage(value) {
2420
+ return value !== void 0 && value !== 0;
2421
+ }
2422
+ function hasUnmodeledIterations(value) {
2423
+ return value !== void 0;
2424
+ }
2425
+ function hasFallbackIteration(value) {
2426
+ return Array.isArray(value) && value.some(
2427
+ (iteration) => iteration !== null && typeof iteration === "object" && iteration.type === "fallback_message"
2428
+ );
2429
+ }
2430
+ function safeTokenSum(...values) {
2431
+ const total = values.reduce((sum, value) => sum + value, 0);
2432
+ return Number.isSafeInteger(total) ? total : void 0;
2433
+ }
2434
+ function normalizeAnthropicUsage(raw) {
2435
+ const inputTokens = raw.input_tokens;
2436
+ const outputTokens = raw.output_tokens;
2437
+ const cacheReadTokens = raw.cache_read_input_tokens ?? 0;
2438
+ const cacheWrite5mTokens = raw.cache_creation?.ephemeral_5m_input_tokens ?? 0;
2439
+ const cacheWrite1hTokens = raw.cache_creation?.ephemeral_1h_input_tokens ?? 0;
2440
+ if (!isValidTokenCount(inputTokens) || !isValidTokenCount(outputTokens) || !isValidTokenCount(cacheReadTokens) || !isValidTokenCount(cacheWrite5mTokens) || !isValidTokenCount(cacheWrite1hTokens)) {
2441
+ return void 0;
2442
+ }
2443
+ const hasTtlBreakdown = raw.cache_creation?.ephemeral_5m_input_tokens !== void 0 || raw.cache_creation?.ephemeral_1h_input_tokens !== void 0;
2444
+ const aggregateCacheWriteTokens = raw.cache_creation_input_tokens;
2445
+ const normalizedCacheWrite = hasTtlBreakdown ? safeTokenSum(cacheWrite5mTokens, cacheWrite1hTokens) : void 0;
2446
+ if (hasTtlBreakdown && normalizedCacheWrite === void 0) return void 0;
2447
+ const promptCacheWrite = aggregateCacheWriteTokens ?? normalizedCacheWrite ?? 0;
2448
+ if (!isValidTokenCount(promptCacheWrite)) return void 0;
2449
+ const promptTokens = safeTokenSum(inputTokens, cacheReadTokens, promptCacheWrite);
2450
+ if (promptTokens === void 0) return void 0;
2451
+ const totalTokens = safeTokenSum(promptTokens, outputTokens);
2452
+ if (totalTokens === void 0) return void 0;
2453
+ return {
2454
+ usage: {
2455
+ prompt_tokens: promptTokens,
2456
+ completion_tokens: outputTokens,
2457
+ total_tokens: totalTokens,
2458
+ cached_tokens: cacheReadTokens > 0 ? cacheReadTokens : void 0,
2459
+ // The aggregate is still useful public usage telemetry even though it
2460
+ // cannot be priced without the 5m/1h split. Keep pricingUsage separate
2461
+ // so estimateAnthropicCost continues to fail closed in that case.
2462
+ cache_write_tokens: (normalizedCacheWrite ?? aggregateCacheWriteTokens ?? 0) > 0 ? normalizedCacheWrite ?? aggregateCacheWriteTokens : void 0
2463
+ },
2464
+ pricingUsage: {
2465
+ inputTokens,
2466
+ outputTokens,
2467
+ cacheReadTokens,
2468
+ cacheWrite5mTokens,
2469
+ cacheWrite1hTokens,
2470
+ aggregateCacheWriteTokens
2471
+ }
2472
+ };
2473
+ }
2474
+ function mergeAnthropicUsage(initial, update) {
2475
+ return {
2476
+ input_tokens: update.input_tokens ?? initial?.input_tokens,
2477
+ output_tokens: update.output_tokens ?? initial?.output_tokens,
2478
+ cache_read_input_tokens: update.cache_read_input_tokens ?? initial?.cache_read_input_tokens,
2479
+ cache_creation_input_tokens: update.cache_creation_input_tokens ?? initial?.cache_creation_input_tokens,
2480
+ cache_creation: {
2481
+ ephemeral_5m_input_tokens: update.cache_creation?.ephemeral_5m_input_tokens ?? initial?.cache_creation?.ephemeral_5m_input_tokens,
2482
+ ephemeral_1h_input_tokens: update.cache_creation?.ephemeral_1h_input_tokens ?? initial?.cache_creation?.ephemeral_1h_input_tokens
2483
+ },
2484
+ speed: update.speed ?? initial?.speed,
2485
+ inference_geo: update.inference_geo ?? initial?.inference_geo,
2486
+ iterations: update.iterations ?? initial?.iterations,
2487
+ server_tool_use: update.server_tool_use ?? initial?.server_tool_use
2488
+ };
1798
2489
  }
1799
2490
  var AnthropicProvider = class {
1800
2491
  name = "anthropic";
@@ -1832,6 +2523,7 @@ var AnthropicProvider = class {
1832
2523
  async chat(messages, options) {
1833
2524
  const headers = this.buildHeaders(await this.resolveKey());
1834
2525
  const body = this.buildRequestBody(messages, options, false);
2526
+ const pricingContext = pricingContextFromBody(body);
1835
2527
  const res = await fetchWithRetry(
1836
2528
  `${this.baseUrl}/messages`,
1837
2529
  {
@@ -1854,7 +2546,7 @@ var AnthropicProvider = class {
1854
2546
  });
1855
2547
  }
1856
2548
  const json = await res.json();
1857
- return this.parseResponse(json, options.model);
2549
+ return this.parseResponse(json, pricingContext);
1858
2550
  }
1859
2551
  // ---------------------------------------------------------------------------
1860
2552
  // stream - SSE streaming completion
@@ -1862,6 +2554,7 @@ var AnthropicProvider = class {
1862
2554
  async *stream(messages, options) {
1863
2555
  const headers = this.buildHeaders(await this.resolveKey());
1864
2556
  const body = this.buildRequestBody(messages, options, true);
2557
+ const pricingContext = pricingContextFromBody(body);
1865
2558
  const res = await fetchWithRetry(
1866
2559
  `${this.baseUrl}/messages`,
1867
2560
  {
@@ -1886,7 +2579,7 @@ var AnthropicProvider = class {
1886
2579
  if (!res.body) {
1887
2580
  throw new Error("Anthropic stream response has no body");
1888
2581
  }
1889
- yield* this.parseSSEStream(res.body, options.model);
2582
+ yield* this.parseSSEStream(res.body, pricingContext);
1890
2583
  }
1891
2584
  // ---------------------------------------------------------------------------
1892
2585
  // Internal: request building
@@ -1910,11 +2603,12 @@ var AnthropicProvider = class {
1910
2603
  return `Anthropic API error (${status}): ${body}`;
1911
2604
  }
1912
2605
  buildRequestBody(messages, options, stream) {
2606
+ const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
1913
2607
  const systemMessages = messages.filter((m) => m.role === "system");
1914
2608
  const nonSystemMessages = messages.filter((m) => m.role !== "system");
1915
2609
  const systemText = systemMessages.map((m) => m.content).join("\n\n");
1916
2610
  const body = {
1917
- model: options.model,
2611
+ model: effectiveModel,
1918
2612
  messages: this.mapMessages(nonSystemMessages),
1919
2613
  max_tokens: options.maxTokens ?? 4096,
1920
2614
  stream
@@ -1931,35 +2625,16 @@ var AnthropicProvider = class {
1931
2625
  if (options.toolChoice !== void 0) {
1932
2626
  body.tool_choice = this.mapToolChoice(options.toolChoice);
1933
2627
  }
1934
- const { thinkingBudget, thinkingDisabled, activeEffort, hasBudgetOverride } = resolveThinkingOptions(options);
1935
- const resolvedEffort = activeEffort ? clampAnthropicEffort(options.model, activeEffort) : void 0;
1936
- if (hasBudgetOverride) {
1937
- body.thinking = { type: "enabled", budget_tokens: thinkingBudget };
1938
- const currentMax = body.max_tokens;
1939
- if (currentMax < thinkingBudget + 1024) {
1940
- body.max_tokens = thinkingBudget + 1024;
1941
- }
1942
- if (resolvedEffort && supportsEffort(options.model)) {
1943
- body.output_config = { effort: resolvedEffort };
1944
- }
1945
- } else if (thinkingDisabled) {
1946
- if (resolvedEffort && supportsEffort(options.model)) {
1947
- body.output_config = { effort: resolvedEffort };
1948
- }
1949
- } else if (resolvedEffort && supportsAdaptiveThinking(options.model)) {
1950
- body.thinking = { type: "adaptive" };
1951
- body.output_config = { effort: resolvedEffort };
1952
- } else if (resolvedEffort && supportsEffort(options.model)) {
1953
- body.output_config = { effort: resolvedEffort };
1954
- } else if (resolvedEffort) {
1955
- const budget = THINKING_BUDGETS[resolvedEffort] ?? 5e3;
1956
- body.thinking = { type: "enabled", budget_tokens: budget };
2628
+ const thinking = resolveClaudeThinking(effectiveModel, resolveThinkingOptions(options));
2629
+ if (thinking.thinking) body.thinking = thinking.thinking;
2630
+ if (thinking.outputConfig) body.output_config = thinking.outputConfig;
2631
+ if (thinking.manualBudget) {
1957
2632
  const currentMax = body.max_tokens;
1958
- if (currentMax < budget + 1024) {
1959
- body.max_tokens = budget + 1024;
2633
+ if (currentMax < thinking.manualBudget + 1024) {
2634
+ body.max_tokens = thinking.manualBudget + 1024;
1960
2635
  }
1961
2636
  }
1962
- if (options.temperature !== void 0 && !body.thinking) {
2637
+ if (options.temperature !== void 0 && !thinking.stripTemperature) {
1963
2638
  body.temperature = options.temperature;
1964
2639
  }
1965
2640
  if (options.responseFormat && options.responseFormat.type !== "text") {
@@ -1984,8 +2659,9 @@ ${jsonInstruction}` : jsonInstruction;
1984
2659
  const result = [];
1985
2660
  for (const msg of messages) {
1986
2661
  if (msg.role === "assistant") {
2662
+ const replayedThinking = this.getReplayedThinkingBlocks(msg);
1987
2663
  if (msg.tool_calls && msg.tool_calls.length > 0) {
1988
- const content = [];
2664
+ const content = [...replayedThinking];
1989
2665
  if (msg.content) {
1990
2666
  content.push({ type: "text", text: msg.content });
1991
2667
  }
@@ -2004,6 +2680,10 @@ ${jsonInstruction}` : jsonInstruction;
2004
2680
  });
2005
2681
  }
2006
2682
  result.push({ role: "assistant", content });
2683
+ } else if (replayedThinking.length > 0) {
2684
+ const content = [...replayedThinking];
2685
+ if (msg.content) content.push({ type: "text", text: msg.content });
2686
+ result.push({ role: "assistant", content });
2007
2687
  } else {
2008
2688
  result.push({ role: "assistant", content: msg.content });
2009
2689
  }
@@ -2024,6 +2704,13 @@ ${jsonInstruction}` : jsonInstruction;
2024
2704
  }
2025
2705
  return this.mergeConsecutiveRoles(result);
2026
2706
  }
2707
+ getReplayedThinkingBlocks(msg) {
2708
+ const blocks = msg.providerMetadata?.anthropicThinkingBlocks;
2709
+ if (!Array.isArray(blocks)) return [];
2710
+ return blocks.filter(
2711
+ (block) => block !== null && typeof block === "object" && (block.type === "thinking" || block.type === "redacted_thinking")
2712
+ );
2713
+ }
2027
2714
  /**
2028
2715
  * Merge consecutive messages with the same role into a single message.
2029
2716
  * This handles cases where multiple tool_result blocks need to be in one user message.
@@ -2079,16 +2766,24 @@ ${jsonInstruction}` : jsonInstruction;
2079
2766
  // ---------------------------------------------------------------------------
2080
2767
  // Internal: response parsing
2081
2768
  // ---------------------------------------------------------------------------
2082
- parseResponse(json, model) {
2769
+ parseResponse(json, pricingContext) {
2083
2770
  let content = "";
2084
2771
  let thinkingContent = "";
2085
2772
  const toolCalls = [];
2086
- for (const block of json.content) {
2773
+ const thinkingBlocks = [];
2774
+ const fallbackIndex = lastFallbackIndex(json.content);
2775
+ const hasFallbackBoundary = fallbackIndex >= 0 || hasFallbackIteration(json.usage?.iterations);
2776
+ let refused = json.stop_reason === "refusal";
2777
+ for (const [index, block] of json.content.entries()) {
2087
2778
  if (block.type === "thinking") {
2088
2779
  thinkingContent += block.thinking;
2780
+ thinkingBlocks.push(block);
2781
+ } else if (block.type === "redacted_thinking") {
2782
+ thinkingBlocks.push(block);
2089
2783
  } else if (block.type === "text") {
2090
2784
  content += block.text;
2091
2785
  } else if (block.type === "tool_use") {
2786
+ if (fallbackIndex >= 0 && index < fallbackIndex) continue;
2092
2787
  toolCalls.push({
2093
2788
  id: block.id,
2094
2789
  type: "function",
@@ -2097,37 +2792,58 @@ ${jsonInstruction}` : jsonInstruction;
2097
2792
  arguments: JSON.stringify(block.input)
2098
2793
  }
2099
2794
  });
2795
+ } else if (block.type === "fallback") {
2796
+ } else if (block.type === "refusal") {
2797
+ refused = true;
2100
2798
  }
2101
2799
  }
2102
- const cacheRead = json.usage?.cache_read_input_tokens ?? 0;
2103
- const cacheWrite = json.usage?.cache_creation_input_tokens ?? 0;
2104
- const totalInput = (json.usage?.input_tokens ?? 0) + cacheRead + cacheWrite;
2105
- const usage = json.usage ? {
2106
- prompt_tokens: totalInput,
2107
- completion_tokens: json.usage.output_tokens,
2108
- total_tokens: totalInput + json.usage.output_tokens,
2109
- cached_tokens: cacheRead > 0 ? cacheRead : void 0
2110
- } : void 0;
2111
- const cost = json.usage ? estimateAnthropicCost(model, totalInput, json.usage.output_tokens, cacheRead, cacheWrite) : void 0;
2800
+ const normalized = json.usage ? normalizeAnthropicUsage(json.usage) : void 0;
2801
+ const effectiveModel = typeof json.model === "string" ? json.model : pricingContext.model;
2802
+ if (hasFallbackBoundary) {
2803
+ throw new Error(
2804
+ "Anthropic fallback responses cannot be continued safely; retry without fallbacks."
2805
+ );
2806
+ }
2807
+ const cost = normalized && !pricingContext.unpricedModifier && !isModifiedAnthropicResponse(json, effectiveModel) && !hasFallbackBoundary && !refused ? estimateAnthropicCost(effectiveModel, normalized.pricingUsage) : void 0;
2112
2808
  return {
2113
2809
  content,
2114
2810
  thinking_content: thinkingContent || void 0,
2115
2811
  tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
2116
- usage,
2117
- cost
2812
+ usage: normalized?.usage,
2813
+ cost,
2814
+ providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
2118
2815
  };
2119
2816
  }
2120
2817
  // ---------------------------------------------------------------------------
2121
2818
  // Internal: SSE stream parsing
2122
2819
  // ---------------------------------------------------------------------------
2123
- async *parseSSEStream(body, model) {
2820
+ async *parseSSEStream(body, pricingContext) {
2124
2821
  const reader = body.getReader();
2125
2822
  const decoder = new TextDecoder();
2126
2823
  let buffer = "";
2127
2824
  let currentToolId = "";
2128
2825
  let currentToolName = "";
2129
2826
  let usage;
2130
- let cacheWrite = 0;
2827
+ let pricingUsage;
2828
+ let rawUsage;
2829
+ const thinkingBlocks = [];
2830
+ let currentThinkingBlock;
2831
+ let effectiveModel = pricingContext.model;
2832
+ let unpricedModifier = pricingContext.unpricedModifier;
2833
+ let hasFallbackBoundary = false;
2834
+ let hasFallbackIterationSignal = false;
2835
+ let refused = false;
2836
+ const finalizeUsage = () => {
2837
+ const normalized = rawUsage ? normalizeAnthropicUsage(rawUsage) : void 0;
2838
+ usage = normalized?.usage;
2839
+ pricingUsage = normalized?.pricingUsage;
2840
+ };
2841
+ const doneChunk = () => ({
2842
+ type: "done",
2843
+ usage,
2844
+ cost: pricingUsage && !unpricedModifier && !hasFallbackBoundary && !refused ? estimateAnthropicCost(effectiveModel, pricingUsage) : void 0,
2845
+ providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary && !hasFallbackIterationSignal ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
2846
+ });
2131
2847
  try {
2132
2848
  while (true) {
2133
2849
  const { done, value } = await reader.read();
@@ -2158,13 +2874,31 @@ ${jsonInstruction}` : jsonInstruction;
2158
2874
  name: currentToolName,
2159
2875
  arguments: ""
2160
2876
  };
2877
+ } else if (block?.type === "thinking" || block?.type === "redacted_thinking") {
2878
+ currentThinkingBlock = {
2879
+ ...block,
2880
+ type: block.type
2881
+ };
2882
+ } else if (block?.type === "fallback") {
2883
+ hasFallbackBoundary = true;
2884
+ } else if (block?.type === "refusal") {
2885
+ refused = true;
2161
2886
  }
2162
2887
  break;
2163
2888
  }
2164
2889
  case "content_block_delta": {
2165
2890
  const delta = event.delta;
2166
2891
  if (delta?.type === "thinking_delta" && delta.thinking) {
2892
+ if (currentThinkingBlock?.type === "thinking") {
2893
+ const current = currentThinkingBlock.thinking;
2894
+ currentThinkingBlock.thinking = `${typeof current === "string" ? current : ""}${delta.thinking}`;
2895
+ }
2167
2896
  yield { type: "thinking_delta", content: delta.thinking };
2897
+ } else if (delta?.type === "signature_delta" && delta.signature) {
2898
+ if (currentThinkingBlock) {
2899
+ const current = currentThinkingBlock.signature;
2900
+ currentThinkingBlock.signature = `${typeof current === "string" ? current : ""}${delta.signature}`;
2901
+ }
2168
2902
  } else if (delta?.type === "text_delta" && delta.text) {
2169
2903
  yield { type: "text_delta", content: delta.text };
2170
2904
  } else if (delta?.type === "input_json_delta" && delta.partial_json) {
@@ -2177,76 +2911,82 @@ ${jsonInstruction}` : jsonInstruction;
2177
2911
  break;
2178
2912
  }
2179
2913
  case "content_block_stop": {
2914
+ if (currentThinkingBlock) {
2915
+ thinkingBlocks.push(currentThinkingBlock);
2916
+ currentThinkingBlock = void 0;
2917
+ }
2180
2918
  currentToolId = "";
2181
2919
  currentToolName = "";
2182
2920
  break;
2183
2921
  }
2184
2922
  case "message_start": {
2185
2923
  if (event.message?.usage) {
2186
- const cacheRead = event.message.usage.cache_read_input_tokens ?? 0;
2187
- cacheWrite = event.message.usage.cache_creation_input_tokens ?? 0;
2188
- const inputTokens = (event.message.usage.input_tokens ?? 0) + cacheRead + cacheWrite;
2189
- usage = {
2190
- prompt_tokens: inputTokens,
2191
- completion_tokens: 0,
2192
- total_tokens: inputTokens,
2193
- cached_tokens: cacheRead > 0 ? cacheRead : void 0
2194
- };
2924
+ rawUsage = mergeAnthropicUsage(rawUsage, event.message.usage);
2925
+ }
2926
+ if (typeof event.message?.model === "string") effectiveModel = event.message.model;
2927
+ const messageFallback = hasFallbackIteration(event.message?.usage?.iterations);
2928
+ if (isAnthropicResponseModifier(
2929
+ "inference_geo",
2930
+ event.message?.inference_geo,
2931
+ effectiveModel
2932
+ ) || isAnthropicResponseModifier("speed", event.message?.speed, effectiveModel) || isAnthropicResponseModifier(
2933
+ "inference_geo",
2934
+ event.message?.usage?.inference_geo,
2935
+ effectiveModel
2936
+ ) || isAnthropicResponseModifier("speed", event.message?.usage?.speed, effectiveModel) || hasBilledUnmodeledUsage(event.message?.usage?.server_tool_use) || hasUnmodeledIterations(event.message?.usage?.iterations)) {
2937
+ unpricedModifier = true;
2195
2938
  }
2939
+ if (messageFallback) hasFallbackIterationSignal = true;
2196
2940
  break;
2197
2941
  }
2198
2942
  case "message_delta": {
2199
2943
  if (event.usage) {
2200
- const outputTokens = event.usage.output_tokens ?? 0;
2201
- if (usage) {
2202
- usage.completion_tokens = outputTokens;
2203
- usage.total_tokens = usage.prompt_tokens + outputTokens;
2204
- } else {
2205
- usage = {
2206
- prompt_tokens: 0,
2207
- completion_tokens: outputTokens,
2208
- total_tokens: outputTokens
2209
- };
2944
+ rawUsage = mergeAnthropicUsage(rawUsage, event.usage);
2945
+ finalizeUsage();
2946
+ const terminalFallback = hasFallbackIteration(event.usage.iterations);
2947
+ if (isAnthropicResponseModifier("speed", event.usage.speed, effectiveModel) || isAnthropicResponseModifier(
2948
+ "inference_geo",
2949
+ event.usage.inference_geo,
2950
+ effectiveModel
2951
+ ) || hasBilledUnmodeledUsage(event.usage.server_tool_use) || hasUnmodeledIterations(event.usage.iterations)) {
2952
+ unpricedModifier = true;
2210
2953
  }
2954
+ if (terminalFallback) hasFallbackIterationSignal = true;
2211
2955
  }
2956
+ if (event.delta?.stop_reason === "refusal") refused = true;
2212
2957
  break;
2213
2958
  }
2214
2959
  case "message_stop": {
2215
- if (usage) {
2216
- usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
2960
+ finalizeUsage();
2961
+ if (hasFallbackBoundary || hasFallbackIterationSignal) {
2962
+ throw new Error(
2963
+ "Anthropic fallback streams cannot be continued safely; retry without fallbacks."
2964
+ );
2217
2965
  }
2218
- yield {
2219
- type: "done",
2220
- usage,
2221
- cost: usage ? estimateAnthropicCost(
2222
- model,
2223
- usage.prompt_tokens,
2224
- usage.completion_tokens,
2225
- usage.cached_tokens,
2226
- cacheWrite
2227
- ) : void 0
2228
- };
2966
+ yield doneChunk();
2229
2967
  return;
2230
2968
  }
2231
2969
  }
2232
2970
  }
2233
2971
  }
2234
- yield {
2235
- type: "done",
2236
- usage,
2237
- cost: usage ? estimateAnthropicCost(
2238
- model,
2239
- usage.prompt_tokens,
2240
- usage.completion_tokens,
2241
- usage.cached_tokens,
2242
- cacheWrite
2243
- ) : void 0
2244
- };
2972
+ finalizeUsage();
2973
+ if (hasFallbackBoundary || hasFallbackIterationSignal) {
2974
+ throw new Error(
2975
+ "Anthropic fallback streams cannot be continued safely; retry without fallbacks."
2976
+ );
2977
+ }
2978
+ yield doneChunk();
2245
2979
  } finally {
2246
2980
  reader.releaseLock();
2247
2981
  }
2248
2982
  }
2249
2983
  };
2984
+ function lastFallbackIndex(blocks) {
2985
+ for (let index = blocks.length - 1; index >= 0; index--) {
2986
+ if (blocks[index].type === "fallback") return index;
2987
+ }
2988
+ return -1;
2989
+ }
2250
2990
 
2251
2991
  // src/providers/gemini.ts
2252
2992
  var GEMINI_DISALLOWED_SCHEMA_KEYS = /* @__PURE__ */ new Set([
@@ -2294,36 +3034,92 @@ function parseGeminiFunctionResponse(content) {
2294
3034
  return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { result: parsed };
2295
3035
  }
2296
3036
  var GEMINI_PRICING = {
2297
- "gemini-2.5-pro": [125e-8, 1e-5],
2298
- "gemini-2.5-flash": [3e-7, 25e-7],
2299
- "gemini-2.5-flash-lite": [1e-7, 4e-7],
2300
- "gemini-2.0-flash": [1e-7, 4e-7],
2301
- "gemini-2.0-flash-lite": [1e-7, 4e-7],
2302
- "gemini-3-pro-preview": [2e-6, 12e-6],
2303
- "gemini-3-flash-preview": [5e-7, 3e-6],
2304
- "gemini-3.1-pro-preview": [2e-6, 12e-6],
2305
- "gemini-3.1-flash-lite": [25e-8, 15e-7],
2306
- "gemini-3.1-flash-lite-preview": [25e-8, 15e-7],
2307
- "gemini-3.5-flash": [15e-7, 9e-6]
3037
+ "gemini-2.5-pro": {
3038
+ input: 125e-8,
3039
+ cached: 125e-9,
3040
+ output: 1e-5,
3041
+ longContext: { input: 25e-7, cached: 25e-8, output: 15e-6 }
3042
+ },
3043
+ "gemini-2.5-flash": { input: 3e-7, cached: 3e-8, output: 25e-7 },
3044
+ "gemini-2.5-flash-lite": { input: 1e-7, cached: 1e-8, output: 4e-7 },
3045
+ "gemini-3-flash-preview": { input: 5e-7, cached: 5e-8, output: 3e-6 },
3046
+ "gemini-3.1-pro-preview": {
3047
+ input: 2e-6,
3048
+ cached: 2e-7,
3049
+ output: 12e-6,
3050
+ longContext: { input: 4e-6, cached: 4e-7, output: 18e-6 }
3051
+ },
3052
+ "gemini-3.1-pro-preview-customtools": {
3053
+ input: 2e-6,
3054
+ cached: 2e-7,
3055
+ output: 12e-6,
3056
+ longContext: { input: 4e-6, cached: 4e-7, output: 18e-6 }
3057
+ },
3058
+ "gemini-3.1-flash-lite": { input: 25e-8, cached: 25e-9, output: 15e-7 },
3059
+ "gemini-3.5-flash": { input: 15e-7, cached: 15e-8, output: 9e-6 },
3060
+ "gemini-3.5-flash-lite": { input: 3e-7, cached: 3e-8, output: 25e-7 },
3061
+ "gemini-3.6-flash": { input: 15e-7, cached: 15e-8, output: 75e-7 }
2308
3062
  };
2309
- var GEMINI_PRICING_KEYS_BY_LENGTH = Object.keys(GEMINI_PRICING).sort(
2310
- (a, b) => b.length - a.length
2311
- );
2312
- function estimateGeminiCost(model, inputTokens, outputTokens, cachedTokens) {
2313
- let pricing = GEMINI_PRICING[model];
2314
- if (!pricing) {
2315
- for (const key of GEMINI_PRICING_KEYS_BY_LENGTH) {
2316
- if (model.startsWith(key)) {
2317
- pricing = GEMINI_PRICING[key];
2318
- break;
2319
- }
2320
- }
3063
+ function isValidTokenCount2(value) {
3064
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
3065
+ }
3066
+ function isStandardGeminiRequestTier(value) {
3067
+ return value === void 0 || value === "SERVICE_TIER_UNSPECIFIED" || value === "SERVICE_TIER_STANDARD";
3068
+ }
3069
+ function isDefinitiveStandardGeminiResponseTier(value) {
3070
+ return value === "SERVICE_TIER_UNSPECIFIED" || value === "SERVICE_TIER_STANDARD" || value === "standard";
3071
+ }
3072
+ function isEligibleGeminiPricing(context, hasDefinitiveStandardResponseTier, hasInvalidResponseTier) {
3073
+ return context.eligibleRequest && isStandardGeminiRequestTier(context.serviceTier) && hasDefinitiveStandardResponseTier && !hasInvalidResponseTier;
3074
+ }
3075
+ function estimateGeminiCost(model, usage) {
3076
+ const pricing = GEMINI_PRICING[model];
3077
+ const cached = usage.cachedTokens ?? 0;
3078
+ if (!pricing || !isValidTokenCount2(usage.inputTokens) || !isValidTokenCount2(usage.outputTokens) || !isValidTokenCount2(cached) || cached > usage.inputTokens) {
3079
+ return void 0;
2321
3080
  }
2322
- if (!pricing) return void 0;
2323
- const [inputRate, outputRate] = pricing;
2324
- const cached = cachedTokens ?? 0;
2325
- const inputCost = (inputTokens - cached) * inputRate + cached * inputRate * 0.1;
2326
- return inputCost + outputTokens * outputRate;
3081
+ const rate = usage.inputTokens > 2e5 && pricing.longContext ? pricing.longContext : pricing;
3082
+ return (usage.inputTokens - cached) * rate.input + cached * rate.cached + usage.outputTokens * rate.output;
3083
+ }
3084
+ function safeTokenSum2(...values) {
3085
+ const total = values.reduce((sum, value) => sum + value, 0);
3086
+ return Number.isSafeInteger(total) ? total : void 0;
3087
+ }
3088
+ function normalizeGeminiUsage(raw) {
3089
+ const inputTokens = raw.promptTokenCount;
3090
+ const completionTokens = raw.candidatesTokenCount;
3091
+ const totalTokens = raw.totalTokenCount;
3092
+ const cachedTokens = raw.cachedContentTokenCount ?? 0;
3093
+ const reasoningTokens = raw.thoughtsTokenCount ?? 0;
3094
+ const toolUsePromptTokens = raw.toolUsePromptTokenCount ?? 0;
3095
+ if (!isValidTokenCount2(inputTokens) || !isValidTokenCount2(completionTokens) || !isValidTokenCount2(totalTokens) || !isValidTokenCount2(cachedTokens) || !isValidTokenCount2(reasoningTokens) || !isValidTokenCount2(toolUsePromptTokens) || cachedTokens > inputTokens) {
3096
+ return void 0;
3097
+ }
3098
+ const billedOutputTokens = safeTokenSum2(completionTokens, reasoningTokens);
3099
+ const expectedTotal = safeTokenSum2(
3100
+ inputTokens,
3101
+ completionTokens,
3102
+ reasoningTokens,
3103
+ toolUsePromptTokens
3104
+ );
3105
+ if (billedOutputTokens === void 0 || expectedTotal === void 0 || totalTokens !== expectedTotal) {
3106
+ return void 0;
3107
+ }
3108
+ return {
3109
+ usage: {
3110
+ prompt_tokens: inputTokens,
3111
+ completion_tokens: completionTokens,
3112
+ total_tokens: totalTokens,
3113
+ cached_tokens: cachedTokens > 0 ? cachedTokens : void 0,
3114
+ reasoning_tokens: reasoningTokens > 0 ? reasoningTokens : void 0
3115
+ },
3116
+ pricingUsage: {
3117
+ inputTokens,
3118
+ outputTokens: billedOutputTokens,
3119
+ cachedTokens
3120
+ },
3121
+ hasUnmodeledBilledUsage: toolUsePromptTokens !== 0
3122
+ };
2327
3123
  }
2328
3124
  var THINKING_BUDGETS2 = {
2329
3125
  low: 1024,
@@ -2342,8 +3138,23 @@ var THINKING_LEVELS = {
2342
3138
  max: "high"
2343
3139
  // 3.x caps at 'high'
2344
3140
  };
3141
+ var GEMINI_3X_MIN_THINKING_LEVEL = /* @__PURE__ */ new Map([
3142
+ ["gemini-3-pro-preview", "minimal"],
3143
+ ["gemini-3-flash-preview", "minimal"],
3144
+ ["gemini-3.1-pro-preview", "low"],
3145
+ ["gemini-3.1-pro-preview-customtools", "low"],
3146
+ ["gemini-3.1-flash-lite", "minimal"],
3147
+ ["gemini-3.1-flash-lite-preview", "minimal"],
3148
+ ["gemini-3.5-flash", "minimal"],
3149
+ ["gemini-3.5-flash-lite", "minimal"],
3150
+ ["gemini-3.6-flash", "minimal"]
3151
+ ]);
3152
+ var GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE = /* @__PURE__ */ new Set([
3153
+ "gemini-3.5-flash-lite",
3154
+ "gemini-3.6-flash"
3155
+ ]);
2345
3156
  function isGemini3x(model) {
2346
- return /^gemini-3[.-]/.test(model);
3157
+ return GEMINI_3X_MIN_THINKING_LEVEL.has(model);
2347
3158
  }
2348
3159
  function budgetToThinkingLevel(budgetTokens) {
2349
3160
  if (budgetTokens <= 1024) return "low";
@@ -2351,8 +3162,7 @@ function budgetToThinkingLevel(budgetTokens) {
2351
3162
  return "high";
2352
3163
  }
2353
3164
  function minThinkingLevel(model) {
2354
- if (model.startsWith("gemini-3.1-pro")) return "low";
2355
- return "minimal";
3165
+ return GEMINI_3X_MIN_THINKING_LEVEL.get(model) ?? "minimal";
2356
3166
  }
2357
3167
  var _warned3xEffortNone = /* @__PURE__ */ new Set();
2358
3168
  function warnGemini3xEffortNone(model) {
@@ -2397,10 +3207,12 @@ var GeminiProvider = class {
2397
3207
  // chat - non-streaming completion
2398
3208
  // ---------------------------------------------------------------------------
2399
3209
  async chat(messages, options) {
2400
- const headers = this.buildHeaders(await this.resolveKey());
2401
3210
  const body = this.buildRequestBody(messages, options);
3211
+ const pricingContext = this.pricingContext(this.requestModel(options), body);
3212
+ this.assertSafeGemini3Continuation(messages, pricingContext.model);
3213
+ const headers = this.buildHeaders(await this.resolveKey());
2402
3214
  const res = await fetchWithRetry(
2403
- `${this.baseUrl}/models/${options.model}:generateContent`,
3215
+ `${this.baseUrl}/models/${pricingContext.model}:generateContent`,
2404
3216
  {
2405
3217
  method: "POST",
2406
3218
  headers,
@@ -2421,16 +3233,18 @@ var GeminiProvider = class {
2421
3233
  });
2422
3234
  }
2423
3235
  const json = await res.json();
2424
- return this.parseResponse(json, options.model);
3236
+ return this.parseResponse(json, pricingContext);
2425
3237
  }
2426
3238
  // ---------------------------------------------------------------------------
2427
3239
  // stream - SSE streaming completion
2428
3240
  // ---------------------------------------------------------------------------
2429
3241
  async *stream(messages, options) {
2430
- const headers = this.buildHeaders(await this.resolveKey());
2431
3242
  const body = this.buildRequestBody(messages, options);
3243
+ const pricingContext = this.pricingContext(this.requestModel(options), body);
3244
+ this.assertSafeGemini3Continuation(messages, pricingContext.model);
3245
+ const headers = this.buildHeaders(await this.resolveKey());
2432
3246
  const res = await fetchWithRetry(
2433
- `${this.baseUrl}/models/${options.model}:streamGenerateContent?alt=sse`,
3247
+ `${this.baseUrl}/models/${pricingContext.model}:streamGenerateContent?alt=sse`,
2434
3248
  {
2435
3249
  method: "POST",
2436
3250
  headers,
@@ -2453,7 +3267,7 @@ var GeminiProvider = class {
2453
3267
  if (!res.body) {
2454
3268
  throw new Error("Gemini stream response has no body");
2455
3269
  }
2456
- yield* this.parseSSEStream(res.body, options.model);
3270
+ yield* this.parseSSEStream(res.body, pricingContext);
2457
3271
  }
2458
3272
  // ---------------------------------------------------------------------------
2459
3273
  // Internal: request building
@@ -2474,7 +3288,114 @@ var GeminiProvider = class {
2474
3288
  }
2475
3289
  return `Gemini API error (${status}): ${body}`;
2476
3290
  }
3291
+ pricingContext(fallbackModel, body) {
3292
+ return {
3293
+ // Gemini's model is normally selected in the URL. providerOptions is an
3294
+ // explicit raw escape hatch, so honor a post-merge model override for both
3295
+ // the URL and pricing rather than pricing a different request.
3296
+ model: typeof body.model === "string" ? body.model : fallbackModel,
3297
+ serviceTier: body.serviceTier,
3298
+ eligibleRequest: this.baseUrl === "https://generativelanguage.googleapis.com/v1beta" && this.isTextOnlyClientRequest(body)
3299
+ };
3300
+ }
3301
+ requestModel(options) {
3302
+ return typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
3303
+ }
3304
+ assertSafeGemini3Continuation(messages, model) {
3305
+ if (GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE.has(model)) {
3306
+ const mapped = this.mapMessages(messages.filter((message) => message.role !== "system"));
3307
+ if (mapped.at(-1)?.role === "model") {
3308
+ throw new Error(
3309
+ `${model} does not support a terminal assistant/model prefill; end the request with a user or tool message.`
3310
+ );
3311
+ }
3312
+ }
3313
+ if (!isGemini3x(model)) return;
3314
+ const replayedNativeCallIds = /* @__PURE__ */ new Set();
3315
+ for (const message of messages) {
3316
+ if (message.role !== "assistant" || !Array.isArray(message.providerMetadata?.geminiParts)) {
3317
+ continue;
3318
+ }
3319
+ for (const part of message.providerMetadata.geminiParts) {
3320
+ const id = part !== null && typeof part === "object" ? part.functionCall?.id : void 0;
3321
+ if (!id) continue;
3322
+ if (replayedNativeCallIds.has(id)) {
3323
+ throw new Error(
3324
+ `${model} native functionCall ids must be globally unique across a replay.`
3325
+ );
3326
+ }
3327
+ replayedNativeCallIds.add(id);
3328
+ }
3329
+ }
3330
+ const consumedToolMessages = /* @__PURE__ */ new Set();
3331
+ for (const [assistantIndex, message] of messages.entries()) {
3332
+ if (message.role !== "assistant" || !message.tool_calls?.length) continue;
3333
+ const rawParts = message.providerMetadata?.geminiParts;
3334
+ if (!Array.isArray(rawParts)) {
3335
+ throw new Error(
3336
+ `${model} tool continuations require providerMetadata.geminiParts with thought signatures.`
3337
+ );
3338
+ }
3339
+ const nativeCalls = rawParts.flatMap((part) => {
3340
+ const functionCall = part !== null && typeof part === "object" ? part.functionCall : void 0;
3341
+ return functionCall?.id ? [{ id: functionCall.id, name: functionCall.name, part }] : [];
3342
+ });
3343
+ const expectedCalls = new Map(
3344
+ message.tool_calls.map((call) => [call.id, call.function.name])
3345
+ );
3346
+ 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)) {
3347
+ throw new Error(
3348
+ `${model} tool continuations require exact native functionCall id/name and a thoughtSignature on the first function call.`
3349
+ );
3350
+ }
3351
+ const seenResponses = /* @__PURE__ */ new Set();
3352
+ let toolIndex = assistantIndex + 1;
3353
+ while (messages[toolIndex]?.role === "tool") {
3354
+ const toolMessage = messages[toolIndex];
3355
+ const toolCallId = toolMessage.tool_call_id;
3356
+ if (!toolCallId || !expectedCalls.has(toolCallId) || seenResponses.has(toolCallId)) {
3357
+ throw new Error(
3358
+ `${model} functionResponse requires exactly one matching native functionCall id and name.`
3359
+ );
3360
+ }
3361
+ seenResponses.add(toolCallId);
3362
+ consumedToolMessages.add(toolIndex);
3363
+ toolIndex++;
3364
+ }
3365
+ if (seenResponses.size !== expectedCalls.size) {
3366
+ throw new Error(
3367
+ `${model} tool continuations require one contiguous functionResponse for every native functionCall.`
3368
+ );
3369
+ }
3370
+ }
3371
+ for (const [index, message] of messages.entries()) {
3372
+ if (message.role === "tool" && !consumedToolMessages.has(index)) {
3373
+ throw new Error(
3374
+ `${model} functionResponse requires exactly one matching native functionCall id and name.`
3375
+ );
3376
+ }
3377
+ }
3378
+ }
3379
+ isTextOnlyClientRequest(body) {
3380
+ if (!Array.isArray(body.contents) || body.cachedContent !== void 0) return false;
3381
+ if (body.contents.some(
3382
+ (content) => content === null || typeof content !== "object" || !Array.isArray(content.parts) || content.parts.some((part) => this.isNonTextGeminiPart(part))
3383
+ )) {
3384
+ return false;
3385
+ }
3386
+ if (body.tools !== void 0 && (!Array.isArray(body.tools) || body.tools.some(
3387
+ (tool2) => tool2 === null || typeof tool2 !== "object" || !Array.isArray(tool2.functionDeclarations) || Object.keys(tool2).some((key) => key !== "functionDeclarations")
3388
+ ))) {
3389
+ return false;
3390
+ }
3391
+ const modalities = body.generationConfig?.responseModalities;
3392
+ return modalities === void 0 || modalities === "TEXT" || Array.isArray(modalities) && modalities.length === 1 && modalities[0] === "TEXT";
3393
+ }
3394
+ isNonTextGeminiPart(part) {
3395
+ return part === null || typeof part !== "object" || ["inlineData", "fileData", "executableCode", "codeExecutionResult"].some((key) => key in part);
3396
+ }
2477
3397
  buildRequestBody(messages, options) {
3398
+ const requestModel = this.requestModel(options);
2478
3399
  const systemMessages = messages.filter((m) => m.role === "system");
2479
3400
  const nonSystemMessages = messages.filter((m) => m.role !== "system");
2480
3401
  const systemText = systemMessages.map((m) => m.content).join("\n\n");
@@ -2492,7 +3413,7 @@ var GeminiProvider = class {
2492
3413
  ];
2493
3414
  }
2494
3415
  const generationConfig = {};
2495
- if (options.temperature !== void 0) {
3416
+ if (options.temperature !== void 0 && !GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE.has(requestModel)) {
2496
3417
  generationConfig.temperature = options.temperature;
2497
3418
  }
2498
3419
  if (options.maxTokens !== void 0) {
@@ -2521,18 +3442,18 @@ var GeminiProvider = class {
2521
3442
  hasBudgetOverride
2522
3443
  } = resolveThinkingOptions(options);
2523
3444
  if (thinkingDisabled) {
2524
- if (isGemini3x(options.model)) {
3445
+ if (isGemini3x(requestModel)) {
2525
3446
  if (effort === "none") {
2526
- warnGemini3xEffortNone(options.model);
3447
+ warnGemini3xEffortNone(requestModel);
2527
3448
  }
2528
- generationConfig.thinkingConfig = { thinkingLevel: minThinkingLevel(options.model) };
3449
+ generationConfig.thinkingConfig = { thinkingLevel: minThinkingLevel(requestModel) };
2529
3450
  } else {
2530
3451
  generationConfig.thinkingConfig = { thinkingBudget: 0 };
2531
3452
  }
2532
3453
  if (!body.generationConfig) body.generationConfig = generationConfig;
2533
3454
  } else if (hasBudgetOverride) {
2534
3455
  const config = {};
2535
- if (isGemini3x(options.model)) {
3456
+ if (isGemini3x(requestModel)) {
2536
3457
  config.thinkingLevel = budgetToThinkingLevel(thinkingBudget);
2537
3458
  } else {
2538
3459
  config.thinkingBudget = thinkingBudget;
@@ -2542,10 +3463,10 @@ var GeminiProvider = class {
2542
3463
  if (!body.generationConfig) body.generationConfig = generationConfig;
2543
3464
  } else if (activeEffort) {
2544
3465
  const config = {};
2545
- if (isGemini3x(options.model)) {
3466
+ if (isGemini3x(requestModel)) {
2546
3467
  config.thinkingLevel = THINKING_LEVELS[activeEffort] ?? "medium";
2547
3468
  } else {
2548
- if (activeEffort === "max" && options.model.startsWith("gemini-2.5-pro")) {
3469
+ if (activeEffort === "max" && requestModel === "gemini-2.5-pro") {
2549
3470
  config.thinkingBudget = 32768;
2550
3471
  } else {
2551
3472
  config.thinkingBudget = THINKING_BUDGETS2[activeEffort] ?? 5e3;
@@ -2562,7 +3483,9 @@ var GeminiProvider = class {
2562
3483
  body.toolConfig = { functionCallingConfig: this.mapToolChoice(options.toolChoice) };
2563
3484
  }
2564
3485
  if (options.providerOptions) {
2565
- Object.assign(body, options.providerOptions);
3486
+ const bodyOptions = { ...options.providerOptions };
3487
+ delete bodyOptions.model;
3488
+ Object.assign(body, bodyOptions);
2566
3489
  }
2567
3490
  return body;
2568
3491
  }
@@ -2580,6 +3503,7 @@ var GeminiProvider = class {
2580
3503
  mapMessages(messages) {
2581
3504
  const toolCallIdToName = /* @__PURE__ */ new Map();
2582
3505
  const geminiNativeIds = /* @__PURE__ */ new Set();
3506
+ const geminiNativeNames = /* @__PURE__ */ new Map();
2583
3507
  for (const msg of messages) {
2584
3508
  if (msg.role === "assistant" && msg.tool_calls) {
2585
3509
  for (const tc of msg.tool_calls) {
@@ -2590,7 +3514,10 @@ var GeminiProvider = class {
2590
3514
  const rawParts = msg.providerMetadata?.geminiParts;
2591
3515
  if (rawParts) {
2592
3516
  for (const p of rawParts) {
2593
- if (p.functionCall?.id) geminiNativeIds.add(p.functionCall.id);
3517
+ if (p.functionCall?.id) {
3518
+ geminiNativeIds.add(p.functionCall.id);
3519
+ geminiNativeNames.set(p.functionCall.id, p.functionCall.name);
3520
+ }
2594
3521
  }
2595
3522
  }
2596
3523
  }
@@ -2627,7 +3554,7 @@ var GeminiProvider = class {
2627
3554
  }
2628
3555
  }
2629
3556
  } else if (msg.role === "tool") {
2630
- const functionName = toolCallIdToName.get(msg.tool_call_id) ?? "unknown";
3557
+ const functionName = (msg.tool_call_id ? geminiNativeNames.get(msg.tool_call_id) : void 0) ?? toolCallIdToName.get(msg.tool_call_id) ?? "unknown";
2631
3558
  const functionResponse = {
2632
3559
  name: functionName,
2633
3560
  response: parseGeminiFunctionResponse(msg.content)
@@ -2692,7 +3619,7 @@ var GeminiProvider = class {
2692
3619
  // ---------------------------------------------------------------------------
2693
3620
  // Internal: response parsing
2694
3621
  // ---------------------------------------------------------------------------
2695
- parseResponse(json, model) {
3622
+ parseResponse(json, pricingContext) {
2696
3623
  const candidate = json.candidates?.[0];
2697
3624
  let content = "";
2698
3625
  let thinkingContent = "";
@@ -2715,35 +3642,46 @@ var GeminiProvider = class {
2715
3642
  }
2716
3643
  }
2717
3644
  }
2718
- const cachedTokens = json.usageMetadata?.cachedContentTokenCount;
2719
- const reasoningTokens = json.usageMetadata?.thoughtsTokenCount;
2720
- const usage = json.usageMetadata ? {
2721
- prompt_tokens: json.usageMetadata.promptTokenCount ?? 0,
2722
- completion_tokens: json.usageMetadata.candidatesTokenCount ?? 0,
2723
- total_tokens: json.usageMetadata.totalTokenCount ?? 0,
2724
- cached_tokens: cachedTokens && cachedTokens > 0 ? cachedTokens : void 0,
2725
- reasoning_tokens: reasoningTokens && reasoningTokens > 0 ? reasoningTokens : void 0
2726
- } : void 0;
2727
- const cost = usage ? estimateGeminiCost(model, usage.prompt_tokens, usage.completion_tokens, usage.cached_tokens) : void 0;
3645
+ if (!candidate) {
3646
+ throw new Error("Gemini response did not include a candidate.");
3647
+ }
3648
+ if (candidate.finishReason && !["STOP", "MAX_TOKENS"].includes(candidate.finishReason)) {
3649
+ throw new Error(`Gemini returned non-success finish reason: ${candidate.finishReason}`);
3650
+ }
3651
+ const normalized = json.usageMetadata ? normalizeGeminiUsage(json.usageMetadata) : void 0;
3652
+ const effectiveModel = typeof json.modelVersion === "string" ? json.modelVersion : pricingContext.model;
3653
+ const cost = normalized && !normalized.hasUnmodeledBilledUsage && this.isTextOnlyGeminiResponse(candidate) && isEligibleGeminiPricing(
3654
+ pricingContext,
3655
+ isDefinitiveStandardGeminiResponseTier(json.usageMetadata?.serviceTier),
3656
+ json.usageMetadata?.serviceTier !== void 0 && !isDefinitiveStandardGeminiResponseTier(json.usageMetadata.serviceTier)
3657
+ ) ? estimateGeminiCost(effectiveModel, normalized.pricingUsage) : void 0;
2728
3658
  const rawParts = candidate?.content?.parts;
2729
3659
  const providerMetadata = rawParts ? { geminiParts: rawParts } : void 0;
2730
3660
  return {
2731
3661
  content,
2732
3662
  thinking_content: thinkingContent || void 0,
2733
3663
  tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
2734
- usage,
3664
+ usage: normalized?.usage,
2735
3665
  cost,
2736
3666
  providerMetadata
2737
3667
  };
2738
3668
  }
3669
+ isTextOnlyGeminiResponse(candidate) {
3670
+ return candidate?.content?.parts.every((part) => !this.isNonTextGeminiPart(part)) ?? false;
3671
+ }
2739
3672
  // ---------------------------------------------------------------------------
2740
3673
  // Internal: SSE stream parsing
2741
3674
  // ---------------------------------------------------------------------------
2742
- async *parseSSEStream(body, model) {
3675
+ async *parseSSEStream(body, pricingContext) {
2743
3676
  const reader = body.getReader();
2744
3677
  const decoder = new TextDecoder();
2745
3678
  let buffer = "";
2746
- let usage;
3679
+ let normalizedUsage;
3680
+ let effectiveModel = pricingContext.model;
3681
+ let hasDefinitiveStandardResponseTier = false;
3682
+ let hasInvalidResponseTier = false;
3683
+ let responseIsTextOnly = true;
3684
+ let terminalFailure;
2747
3685
  const accumulatedParts = [];
2748
3686
  try {
2749
3687
  while (true) {
@@ -2763,20 +3701,21 @@ var GeminiProvider = class {
2763
3701
  } catch {
2764
3702
  continue;
2765
3703
  }
3704
+ if (typeof chunk.modelVersion === "string") effectiveModel = chunk.modelVersion;
2766
3705
  if (chunk.usageMetadata) {
2767
- const cached = chunk.usageMetadata.cachedContentTokenCount;
2768
- const reasoning = chunk.usageMetadata.thoughtsTokenCount;
2769
- usage = {
2770
- prompt_tokens: chunk.usageMetadata.promptTokenCount ?? 0,
2771
- completion_tokens: chunk.usageMetadata.candidatesTokenCount ?? 0,
2772
- total_tokens: chunk.usageMetadata.totalTokenCount ?? 0,
2773
- cached_tokens: cached && cached > 0 ? cached : void 0,
2774
- reasoning_tokens: reasoning && reasoning > 0 ? reasoning : void 0
2775
- };
3706
+ normalizedUsage = normalizeGeminiUsage(chunk.usageMetadata);
3707
+ if (chunk.usageMetadata.serviceTier !== void 0) {
3708
+ if (isDefinitiveStandardGeminiResponseTier(chunk.usageMetadata.serviceTier)) {
3709
+ hasDefinitiveStandardResponseTier = true;
3710
+ } else {
3711
+ hasInvalidResponseTier = true;
3712
+ }
3713
+ }
2776
3714
  }
2777
3715
  const candidate = chunk.candidates?.[0];
2778
3716
  if (candidate?.content?.parts) {
2779
3717
  for (const part of candidate.content.parts) {
3718
+ if (this.isNonTextGeminiPart(part)) responseIsTextOnly = false;
2780
3719
  accumulatedParts.push(part);
2781
3720
  if (part.thought && part.text) {
2782
3721
  yield { type: "thinking_delta", content: part.text };
@@ -2792,18 +3731,23 @@ var GeminiProvider = class {
2792
3731
  }
2793
3732
  }
2794
3733
  }
3734
+ if (candidate?.finishReason && !["STOP", "MAX_TOKENS"].includes(candidate.finishReason)) {
3735
+ terminalFailure = candidate.finishReason;
3736
+ }
2795
3737
  }
2796
3738
  }
3739
+ if (terminalFailure) {
3740
+ throw new Error(`Gemini returned non-success finish reason: ${terminalFailure}`);
3741
+ }
2797
3742
  const providerMetadata = accumulatedParts.length > 0 ? { geminiParts: accumulatedParts } : void 0;
2798
3743
  yield {
2799
3744
  type: "done",
2800
- usage,
2801
- cost: usage ? estimateGeminiCost(
2802
- model,
2803
- usage.prompt_tokens,
2804
- usage.completion_tokens,
2805
- usage.cached_tokens
2806
- ) : void 0,
3745
+ usage: normalizedUsage?.usage,
3746
+ cost: normalizedUsage && !normalizedUsage.hasUnmodeledBilledUsage && responseIsTextOnly && isEligibleGeminiPricing(
3747
+ pricingContext,
3748
+ hasDefinitiveStandardResponseTier,
3749
+ hasInvalidResponseTier
3750
+ ) ? estimateGeminiCost(effectiveModel, normalizedUsage.pricingUsage) : void 0,
2807
3751
  providerMetadata
2808
3752
  };
2809
3753
  } finally {
@@ -2820,8 +3764,6 @@ var OPENROUTER_PROFILE = {
2820
3764
  envApiKey: "OPENROUTER_API_KEY",
2821
3765
  envBaseUrl: "OPENROUTER_BASE_URL",
2822
3766
  pricing: { kind: "from-response" },
2823
- // Ask OpenRouter to report per-call cost in usage.cost.
2824
- requestDefaults: { usage: { include: true } },
2825
3767
  reasoning: {
2826
3768
  // OpenRouter's effort vocabulary is low|medium|high; xhigh/max clamp to high.
2827
3769
  emit: reasoningObjectEmit(
@@ -2844,7 +3786,7 @@ var AZURE_PROFILE = {
2844
3786
  envApiKey: "AZURE_OPENAI_API_KEY",
2845
3787
  envBaseUrl: "AZURE_OPENAI_BASE_URL",
2846
3788
  authHeader: "api-key",
2847
- pricing: { kind: "table", table: OPENAI_PRICING },
3789
+ pricing: { kind: "unknown" },
2848
3790
  reasoning: { emit: openaiReasoningEmit, capture: "none" },
2849
3791
  roleFor: (role, model) => role === "system" && isOSeriesModel(model) ? "developer" : role,
2850
3792
  maxTokensField: "max_completion_tokens",
@@ -2852,29 +3794,100 @@ var AZURE_PROFILE = {
2852
3794
  };
2853
3795
 
2854
3796
  // src/providers/profiles/xai.ts
2855
- var acceptsReasoningEffort = (model) => model.startsWith("grok-3-mini");
2856
- var isReasoningModel = (model) => /^grok-(3-mini|4)/.test(model);
3797
+ var GROK_45 = /* @__PURE__ */ new Set(["grok-4.5", "grok-4.5-latest", "grok-build-latest"]);
3798
+ var GROK_43 = /* @__PURE__ */ new Set(["grok-4.3", "grok-4.3-latest", "grok-latest"]);
3799
+ var GROK_420_REASONING = /* @__PURE__ */ new Set([
3800
+ "grok-4.20-0309-reasoning",
3801
+ "grok-4.20-reasoning-latest",
3802
+ "grok-4.20",
3803
+ "grok-4.20-reasoning",
3804
+ "grok-4.20-0309",
3805
+ "grok-4.20-beta-0309-reasoning",
3806
+ "grok-4.20-beta",
3807
+ "grok-4.20-beta-0309",
3808
+ "grok-4.20-beta-latest",
3809
+ "grok-4.20-beta-latest-reasoning",
3810
+ "grok-4.20-beta-reasoning",
3811
+ "grok-4.20-experimental-beta-0304-reasoning",
3812
+ "grok-4.20-experimental-beta-0304",
3813
+ "grok-4.20-experimental-beta-reasoning-latest",
3814
+ "grok-4.20-experimental-beta-latest",
3815
+ "grok-4.20-reasoning-gv2"
3816
+ ]);
3817
+ var GROK_420_NON_REASONING = /* @__PURE__ */ new Set([
3818
+ "grok-4.20-0309-non-reasoning",
3819
+ "grok-4.20-non-reasoning",
3820
+ "grok-4.20-non-reasoning-latest",
3821
+ "grok-4.20-beta-non-reasoning",
3822
+ "grok-4.20-beta-latest-non-reasoning",
3823
+ "grok-4.20-experimental-beta-0304-non-reasoning",
3824
+ "grok-4.20-experimental-beta-non-reasoning-latest",
3825
+ "grok-4.20-beta-0309-non-reasoning",
3826
+ "grok-4.20-non-reasoning-gv2"
3827
+ ]);
3828
+ var warnedIrreducible45 = /* @__PURE__ */ new Set();
3829
+ var warnedEffortClamp = /* @__PURE__ */ new Set();
3830
+ function warnIrreducible45(model) {
3831
+ if (warnedIrreducible45.has(model)) return;
3832
+ warnedIrreducible45.add(model);
3833
+ console.warn(
3834
+ `[axl] effort: 'none' on xAI ${model} maps to 'low'; Grok 4.5 cannot disable reasoning.`
3835
+ );
3836
+ }
3837
+ function warnEffortClamp(model, effort) {
3838
+ const key = `${model}:${effort}`;
3839
+ if (warnedEffortClamp.has(key)) return;
3840
+ warnedEffortClamp.add(key);
3841
+ console.warn(
3842
+ `[axl] effort: '${effort}' on xAI ${model} maps to 'high'; this model supports at most 'high'.`
3843
+ );
3844
+ }
3845
+ var isReasoningChatModel = (model) => GROK_45.has(model) || GROK_43.has(model) || GROK_420_REASONING.has(model);
2857
3846
  var XAI_PROFILE = {
2858
3847
  name: "xai",
2859
3848
  label: "xAI",
2860
3849
  defaultBaseUrl: "https://api.x.ai/v1",
2861
3850
  envApiKey: "XAI_API_KEY",
2862
3851
  envBaseUrl: "XAI_BASE_URL",
2863
- pricing: { kind: "unknown" },
3852
+ pricing: { kind: "from-response" },
2864
3853
  reasoning: {
2865
3854
  emit: reasoningEffortEmit((resolved, model) => {
2866
- if (!acceptsReasoningEffort(model)) return void 0;
2867
- if (resolved.thinkingDisabled || !resolved.activeEffort) return void 0;
2868
- return resolved.activeEffort === "low" ? "low" : "high";
3855
+ if (GROK_45.has(model)) {
3856
+ if (resolved.thinkingDisabled) {
3857
+ warnIrreducible45(model);
3858
+ return "low";
3859
+ }
3860
+ if (!resolved.activeEffort) return void 0;
3861
+ if (resolved.activeEffort === "xhigh" || resolved.activeEffort === "max") {
3862
+ warnEffortClamp(model, resolved.activeEffort);
3863
+ return "high";
3864
+ }
3865
+ return resolved.activeEffort;
3866
+ }
3867
+ if (GROK_43.has(model)) {
3868
+ if (resolved.thinkingDisabled) return "none";
3869
+ if (!resolved.activeEffort) return void 0;
3870
+ if (resolved.activeEffort === "xhigh" || resolved.activeEffort === "max") {
3871
+ warnEffortClamp(model, resolved.activeEffort);
3872
+ return "high";
3873
+ }
3874
+ return resolved.activeEffort;
3875
+ }
3876
+ if (GROK_420_NON_REASONING.has(model)) return void 0;
3877
+ return void 0;
2869
3878
  }),
2870
3879
  capture: "reasoning"
2871
3880
  },
2872
3881
  capabilities: {
2873
- forbiddenParams: (model) => isReasoningModel(model) ? ["stop", "presence_penalty", "frequency_penalty"] : []
3882
+ forbiddenParams: (model) => isReasoningChatModel(model) ? ["stop", "presence_penalty", "frequency_penalty"] : []
2874
3883
  }
2875
3884
  };
2876
3885
 
2877
3886
  // src/providers/profiles/deepseek.ts
3887
+ var DEEPSEEK_PRICING = {
3888
+ "deepseek-v4-flash": [14e-8, 28e-8, 0.02],
3889
+ "deepseek-v4-pro": [435e-9, 87e-8, 1 / 120]
3890
+ };
2878
3891
  var isReasoner = (model) => /reasoner|v4/i.test(model);
2879
3892
  var deepseekEmit = (_body, _resolved, model) => ({
2880
3893
  stripTemperature: isReasoner(model)
@@ -2885,7 +3898,7 @@ var DEEPSEEK_PROFILE = {
2885
3898
  defaultBaseUrl: "https://api.deepseek.com/v1",
2886
3899
  envApiKey: "DEEPSEEK_API_KEY",
2887
3900
  envBaseUrl: "DEEPSEEK_BASE_URL",
2888
- pricing: { kind: "unknown" },
3901
+ pricing: { kind: "table", table: DEEPSEEK_PRICING, match: "exact" },
2889
3902
  reasoning: {
2890
3903
  emit: deepseekEmit,
2891
3904
  capture: "reasoning_content",
@@ -2896,27 +3909,56 @@ var DEEPSEEK_PROFILE = {
2896
3909
  forbiddenParams: (model) => isReasoner(model) ? ["top_p", "presence_penalty", "frequency_penalty"] : []
2897
3910
  }
2898
3911
  };
3912
+ attachBuiltinTablePricingPolicy(DEEPSEEK_PROFILE, {
3913
+ canonicalBaseUrl: "https://api.deepseek.com/v1",
3914
+ requireCacheSplit: true
3915
+ });
2899
3916
 
2900
3917
  // src/providers/profiles/mistral.ts
2901
- var acceptsReasoningEffort2 = (model) => /^mistral-(small|medium)/i.test(model);
3918
+ var MISTRAL_PRICING = {
3919
+ "mistral-medium-latest": [15e-7, 75e-7, 0.1],
3920
+ "mistral-small-latest": [15e-8, 6e-7, 0.1],
3921
+ "mistral-large-latest": [5e-7, 15e-7, 0.1],
3922
+ "devstral-medium-latest": [4e-7, 2e-6, 0.1],
3923
+ "devstral-small-latest": [1e-7, 3e-7, 0.1],
3924
+ "codestral-latest": [3e-7, 9e-7, 0.1],
3925
+ "magistral-medium-latest": [2e-6, 5e-6, 0.1],
3926
+ "magistral-small-latest": [5e-7, 15e-7, 0.1],
3927
+ "ministral-3b-latest": [1e-7, 1e-7, 0.1],
3928
+ "ministral-8b-latest": [15e-8, 15e-8, 0.1],
3929
+ "ministral-14b-latest": [2e-7, 2e-7, 0.1],
3930
+ "open-mistral-nemo": [15e-8, 15e-8, 0.1],
3931
+ "open-mixtral-8x7b": [7e-7, 7e-7, 0.1],
3932
+ "open-mixtral-8x22b": [2e-6, 6e-6, 0.1]
3933
+ };
3934
+ var acceptsReasoningEffort = (model) => /^mistral-(small|medium)/i.test(model);
2902
3935
  var MISTRAL_PROFILE = {
2903
3936
  name: "mistral",
2904
3937
  label: "Mistral",
2905
3938
  defaultBaseUrl: "https://api.mistral.ai/v1",
2906
3939
  envApiKey: "MISTRAL_API_KEY",
2907
3940
  envBaseUrl: "MISTRAL_BASE_URL",
2908
- pricing: { kind: "unknown" },
3941
+ pricing: { kind: "table", table: MISTRAL_PRICING, match: "exact" },
2909
3942
  reasoning: {
2910
3943
  emit: reasoningEffortEmit((resolved, model) => {
2911
- if (!acceptsReasoningEffort2(model)) return void 0;
3944
+ if (!acceptsReasoningEffort(model)) return void 0;
2912
3945
  if (resolved.thinkingDisabled || !resolved.activeEffort) return void 0;
2913
3946
  return "high";
2914
3947
  }),
2915
3948
  capture: "reasoning_content"
2916
3949
  }
2917
3950
  };
3951
+ attachBuiltinTablePricingPolicy(MISTRAL_PROFILE, {
3952
+ canonicalBaseUrl: "https://api.mistral.ai/v1"
3953
+ });
2918
3954
 
2919
3955
  // src/providers/profiles/groq.ts
3956
+ var GROQ_PRICING = {
3957
+ "llama-3.1-8b-instant": [5e-8, 8e-8, 1],
3958
+ "llama-3.3-70b-versatile": [59e-8, 79e-8, 1],
3959
+ "openai/gpt-oss-120b": [15e-8, 6e-7, 0.5],
3960
+ "openai/gpt-oss-20b": [75e-9, 3e-7, 0.5]
3961
+ };
2920
3962
  var isGptOss = (model) => /gpt-oss/i.test(model);
2921
3963
  var GROQ_PROFILE = {
2922
3964
  name: "groq",
@@ -2924,7 +3966,11 @@ var GROQ_PROFILE = {
2924
3966
  defaultBaseUrl: "https://api.groq.com/openai/v1",
2925
3967
  envApiKey: "GROQ_API_KEY",
2926
3968
  envBaseUrl: "GROQ_BASE_URL",
2927
- pricing: { kind: "unknown" },
3969
+ pricing: {
3970
+ kind: "table",
3971
+ table: GROQ_PRICING,
3972
+ match: "exact"
3973
+ },
2928
3974
  reasoning: {
2929
3975
  emit: reasoningEffortEmit((resolved, model) => {
2930
3976
  if (!isGptOss(model) || resolved.thinkingDisabled || !resolved.activeEffort) {
@@ -2945,6 +3991,21 @@ var GROQ_PROFILE = {
2945
3991
  supportsJsonSchema: isGptOss
2946
3992
  }
2947
3993
  };
3994
+ attachBuiltinTablePricingPolicy(GROQ_PROFILE, {
3995
+ canonicalBaseUrl: "https://api.groq.com/openai/v1",
3996
+ supportedServiceTiers: ["on_demand", "flex"],
3997
+ functionToolsOnly: true,
3998
+ chargedRequestModifiers: [
3999
+ "browser_search",
4000
+ "code_interpreter",
4001
+ "web_search",
4002
+ "visit_website",
4003
+ "wolfram_alpha",
4004
+ "remote_mcp",
4005
+ "mcp",
4006
+ "compound_custom"
4007
+ ]
4008
+ });
2948
4009
 
2949
4010
  // src/providers/profiles/bedrock.ts
2950
4011
  var isGptOss2 = (model) => /gpt-oss/i.test(model);
@@ -3759,7 +4820,7 @@ var COST_LEAF_SET = new Set(COST_BEARING_LEAF_TYPES);
3759
4820
  function hasPositiveTokens(event) {
3760
4821
  const t = event.tokens;
3761
4822
  if (!t) return false;
3762
- return (t.input ?? 0) + (t.output ?? 0) + (t.reasoning ?? 0) > 0;
4823
+ return (t.input ?? 0) + (t.output ?? 0) + (t.reasoning ?? 0) + (t.cached ?? 0) + (t.cacheWrite ?? 0) > 0;
3763
4824
  }
3764
4825
  function isUsableCost(cost) {
3765
4826
  return typeof cost === "number" && Number.isFinite(cost) && cost >= 0;
@@ -6372,6 +7433,11 @@ var WorkflowContext = class _WorkflowContext {
6372
7433
  "axl.agent.cached_tokens",
6373
7434
  usageCapture.value.cached_tokens
6374
7435
  );
7436
+ if (usageCapture.value.cache_write_tokens)
7437
+ span.setAttribute(
7438
+ "axl.agent.cache_write_tokens",
7439
+ usageCapture.value.cache_write_tokens
7440
+ );
6375
7441
  }
6376
7442
  return r;
6377
7443
  }
@@ -6817,7 +7883,9 @@ ${appendedSchemaText}`;
6817
7883
  tokens: response.usage ? {
6818
7884
  input: response.usage.prompt_tokens,
6819
7885
  output: response.usage.completion_tokens,
6820
- reasoning: response.usage.reasoning_tokens
7886
+ reasoning: response.usage.reasoning_tokens,
7887
+ cached: response.usage.cached_tokens,
7888
+ cacheWrite: response.usage.cache_write_tokens
6821
7889
  } : void 0,
6822
7890
  duration: Date.now() - turnStart,
6823
7891
  data: {