@axlsdk/axl 0.20.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1378 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -16
- package/dist/index.d.ts +67 -16
- package/dist/index.js +1378 -310
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.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:
|
|
907
|
-
messages: messages.map((m) => this.formatMessage(m,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
//
|
|
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: [
|
|
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
|
|
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
|
|
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
|
|
1431
|
-
const
|
|
1432
|
-
const
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
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:
|
|
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 ?
|
|
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 ?
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
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
|
|
1737
|
-
"claude-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
"claude-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
"claude-
|
|
1751
|
-
|
|
1752
|
-
|
|
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
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
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
|
-
|
|
1768
|
-
|
|
1769
|
-
const cacheRead = cacheReadTokens ?? 0;
|
|
1770
|
-
const
|
|
1771
|
-
const
|
|
1772
|
-
|
|
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
|
|
1783
|
-
|
|
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
|
|
1786
|
-
|
|
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
|
|
1789
|
-
|
|
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
|
|
1792
|
-
|
|
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
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
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,
|
|
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,
|
|
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:
|
|
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
|
|
1935
|
-
|
|
1936
|
-
if (
|
|
1937
|
-
|
|
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 <
|
|
1959
|
-
body.max_tokens =
|
|
2633
|
+
if (currentMax < thinking.manualBudget + 1024) {
|
|
2634
|
+
body.max_tokens = thinking.manualBudget + 1024;
|
|
1960
2635
|
}
|
|
1961
2636
|
}
|
|
1962
|
-
if (options.temperature !== void 0 && !
|
|
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,
|
|
2769
|
+
parseResponse(json, pricingContext) {
|
|
2083
2770
|
let content = "";
|
|
2084
2771
|
let thinkingContent = "";
|
|
2085
2772
|
const toolCalls = [];
|
|
2086
|
-
|
|
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
|
|
2103
|
-
const
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
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
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
usage
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
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
|
-
|
|
2216
|
-
|
|
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
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
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":
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
"gemini-
|
|
2304
|
-
"gemini-
|
|
2305
|
-
"gemini-3
|
|
2306
|
-
"gemini-3.1-
|
|
2307
|
-
|
|
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
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
function
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
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
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
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
|
|
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
|
-
|
|
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/${
|
|
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,
|
|
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/${
|
|
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,
|
|
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(
|
|
3445
|
+
if (isGemini3x(requestModel)) {
|
|
2525
3446
|
if (effort === "none") {
|
|
2526
|
-
warnGemini3xEffortNone(
|
|
3447
|
+
warnGemini3xEffortNone(requestModel);
|
|
2527
3448
|
}
|
|
2528
|
-
generationConfig.thinkingConfig = { thinkingLevel: minThinkingLevel(
|
|
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(
|
|
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(
|
|
3466
|
+
if (isGemini3x(requestModel)) {
|
|
2546
3467
|
config.thinkingLevel = THINKING_LEVELS[activeEffort] ?? "medium";
|
|
2547
3468
|
} else {
|
|
2548
|
-
if (activeEffort === "max" &&
|
|
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
|
-
|
|
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)
|
|
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,
|
|
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
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
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,
|
|
3675
|
+
async *parseSSEStream(body, pricingContext) {
|
|
2743
3676
|
const reader = body.getReader();
|
|
2744
3677
|
const decoder = new TextDecoder();
|
|
2745
3678
|
let buffer = "";
|
|
2746
|
-
let
|
|
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
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
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:
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
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: "
|
|
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
|
|
2856
|
-
var
|
|
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: "
|
|
3852
|
+
pricing: { kind: "from-response" },
|
|
2864
3853
|
reasoning: {
|
|
2865
3854
|
emit: reasoningEffortEmit((resolved, model) => {
|
|
2866
|
-
if (
|
|
2867
|
-
|
|
2868
|
-
|
|
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) =>
|
|
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: "
|
|
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
|
|
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: "
|
|
3941
|
+
pricing: { kind: "table", table: MISTRAL_PRICING, match: "exact" },
|
|
2909
3942
|
reasoning: {
|
|
2910
3943
|
emit: reasoningEffortEmit((resolved, model) => {
|
|
2911
|
-
if (!
|
|
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: {
|
|
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: {
|