@axlsdk/axl 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/index.cjs +1452 -312
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +131 -66
- package/dist/index.d.ts +131 -66
- package/dist/index.js +1452 -312
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -496,7 +496,7 @@ async function fetchWithRetry(input, init, opts) {
|
|
|
496
496
|
for (let attempt = 0; ; attempt++) {
|
|
497
497
|
let res;
|
|
498
498
|
try {
|
|
499
|
-
res = await fetch(input, init);
|
|
499
|
+
res = await fetch(input, { ...init, redirect: "manual" });
|
|
500
500
|
} catch (err) {
|
|
501
501
|
if (isAbortError2(err, init?.signal ?? void 0)) throw err;
|
|
502
502
|
if (attempt >= maxRetries) {
|
|
@@ -666,6 +666,74 @@ 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
|
+
|
|
705
|
+
// src/http-transport.ts
|
|
706
|
+
function assertSafeProviderBaseUrl(baseUrl, surface, dangerouslyAllowInsecureHttp = false) {
|
|
707
|
+
assertSafeHttpEndpoint(baseUrl, surface, dangerouslyAllowInsecureHttp, "prompts and credentials");
|
|
708
|
+
}
|
|
709
|
+
function assertSafeHttpEndpoint(endpoint, surface, dangerouslyAllowInsecureHttp = false, exposedData = "sensitive data") {
|
|
710
|
+
let url;
|
|
711
|
+
try {
|
|
712
|
+
url = new URL(endpoint);
|
|
713
|
+
} catch {
|
|
714
|
+
throw unsafeTransportError(surface, "a valid absolute HTTP(S) URL", exposedData);
|
|
715
|
+
}
|
|
716
|
+
if (url.protocol === "https:") return;
|
|
717
|
+
if (url.protocol !== "http:") {
|
|
718
|
+
throw unsafeTransportError(surface, "an HTTPS URL", exposedData);
|
|
719
|
+
}
|
|
720
|
+
if (dangerouslyAllowInsecureHttp || isLiteralLoopbackHost(url.hostname)) return;
|
|
721
|
+
throw unsafeTransportError(
|
|
722
|
+
surface,
|
|
723
|
+
"an HTTPS URL, or set dangerouslyAllowInsecureHttp: true only for a deliberately insecure endpoint",
|
|
724
|
+
exposedData
|
|
725
|
+
);
|
|
726
|
+
}
|
|
727
|
+
function isLiteralLoopbackHost(hostname) {
|
|
728
|
+
return hostname === "localhost" || hostname === "localhost." || /^127(?:\.\d{1,3}){3}$/.test(hostname) || hostname === "[::1]";
|
|
729
|
+
}
|
|
730
|
+
function unsafeTransportError(surface, remediation, exposedData) {
|
|
731
|
+
return new AxlError(
|
|
732
|
+
"UNSAFE_TRANSPORT",
|
|
733
|
+
`${surface} requires ${remediation}. Remote HTTP can expose ${exposedData} in transit.`
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
|
|
669
737
|
// src/providers/openai-compatible.ts
|
|
670
738
|
function resolvePerModel(value, model, fallback) {
|
|
671
739
|
if (value === void 0) return fallback;
|
|
@@ -680,9 +748,13 @@ function sortedKeys(table) {
|
|
|
680
748
|
}
|
|
681
749
|
return keys;
|
|
682
750
|
}
|
|
683
|
-
function priceFromTable(table, model, promptTokens, completionTokens, cachedTokens) {
|
|
751
|
+
function priceFromTable(table, model, promptTokens, completionTokens, cachedTokens, match = "prefix") {
|
|
752
|
+
const cached = cachedTokens ?? 0;
|
|
753
|
+
if (!Number.isSafeInteger(promptTokens) || !Number.isSafeInteger(completionTokens) || !Number.isSafeInteger(cached) || promptTokens < 0 || completionTokens < 0 || cached < 0 || cached > promptTokens) {
|
|
754
|
+
return void 0;
|
|
755
|
+
}
|
|
684
756
|
let pricing = table[model];
|
|
685
|
-
if (!pricing) {
|
|
757
|
+
if (!pricing && match === "prefix") {
|
|
686
758
|
for (const key of sortedKeys(table)) {
|
|
687
759
|
if (model.startsWith(key)) {
|
|
688
760
|
pricing = table[key];
|
|
@@ -692,7 +764,6 @@ function priceFromTable(table, model, promptTokens, completionTokens, cachedToke
|
|
|
692
764
|
}
|
|
693
765
|
if (!pricing) return void 0;
|
|
694
766
|
const [inputRate, outputRate, cacheMultiplier] = pricing;
|
|
695
|
-
const cached = cachedTokens ?? 0;
|
|
696
767
|
const inputCost = (promptTokens - cached) * inputRate + cached * inputRate * cacheMultiplier;
|
|
697
768
|
return inputCost + completionTokens * outputRate;
|
|
698
769
|
}
|
|
@@ -806,6 +877,11 @@ var OpenAICompatibleProvider = class {
|
|
|
806
877
|
const envBase = p.envBaseUrl ? process.env[p.envBaseUrl] : void 0;
|
|
807
878
|
const explicitBase = options.baseUrl ?? envBase;
|
|
808
879
|
this.baseUrl = (explicitBase ?? p.defaultBaseUrl).replace(/\/$/, "");
|
|
880
|
+
assertSafeProviderBaseUrl(
|
|
881
|
+
this.baseUrl,
|
|
882
|
+
`${p.label ?? p.name} provider`,
|
|
883
|
+
options.dangerouslyAllowInsecureHttp
|
|
884
|
+
);
|
|
809
885
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
810
886
|
const label = p.label ?? p.name;
|
|
811
887
|
if (p.requireExplicitBaseUrl && explicitBase === void 0) {
|
|
@@ -853,7 +929,7 @@ var OpenAICompatibleProvider = class {
|
|
|
853
929
|
});
|
|
854
930
|
}
|
|
855
931
|
const json = await res.json();
|
|
856
|
-
return this.parseResponse(json, options.model);
|
|
932
|
+
return this.parseResponse(json, this.requestModel(body, options.model), body);
|
|
857
933
|
}
|
|
858
934
|
async *stream(messages, options) {
|
|
859
935
|
const headers = this.buildHeaders(await this.resolveKey());
|
|
@@ -881,7 +957,7 @@ var OpenAICompatibleProvider = class {
|
|
|
881
957
|
if (!res.body) {
|
|
882
958
|
throw new Error(`${this.profile.label ?? this.profile.name} stream response has no body`);
|
|
883
959
|
}
|
|
884
|
-
yield* this.parseSSEStream(res.body, options.model);
|
|
960
|
+
yield* this.parseSSEStream(res.body, this.requestModel(body, options.model), body);
|
|
885
961
|
}
|
|
886
962
|
// ---------------------------------------------------------------------------
|
|
887
963
|
// Request building
|
|
@@ -902,12 +978,13 @@ var OpenAICompatibleProvider = class {
|
|
|
902
978
|
buildRequestBody(messages, options, stream) {
|
|
903
979
|
const profile = this.profile;
|
|
904
980
|
const resolved = resolveThinkingOptions(options);
|
|
981
|
+
const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
905
982
|
const body = {
|
|
906
|
-
model:
|
|
907
|
-
messages: messages.map((m) => this.formatMessage(m,
|
|
983
|
+
model: effectiveModel,
|
|
984
|
+
messages: messages.map((m) => this.formatMessage(m, effectiveModel)),
|
|
908
985
|
stream
|
|
909
986
|
};
|
|
910
|
-
const emitResult = profile.reasoning.emit(body, resolved,
|
|
987
|
+
const emitResult = profile.reasoning.emit(body, resolved, effectiveModel) ?? {};
|
|
911
988
|
const stripTemperature = emitResult.stripTemperature ?? false;
|
|
912
989
|
if (options.temperature !== void 0 && !stripTemperature) {
|
|
913
990
|
body.temperature = options.temperature;
|
|
@@ -918,7 +995,7 @@ var OpenAICompatibleProvider = class {
|
|
|
918
995
|
if (options.stop) body.stop = options.stop;
|
|
919
996
|
if (options.tools && options.tools.length > 0) {
|
|
920
997
|
body.tools = options.tools;
|
|
921
|
-
if (resolvePerModel(profile.parallelToolCalls,
|
|
998
|
+
if (resolvePerModel(profile.parallelToolCalls, effectiveModel, false)) {
|
|
922
999
|
body.parallel_tool_calls = true;
|
|
923
1000
|
}
|
|
924
1001
|
}
|
|
@@ -926,7 +1003,7 @@ var OpenAICompatibleProvider = class {
|
|
|
926
1003
|
if (options.responseFormat) {
|
|
927
1004
|
const supportsSchema = resolvePerModel(
|
|
928
1005
|
profile.capabilities?.supportsJsonSchema,
|
|
929
|
-
|
|
1006
|
+
effectiveModel,
|
|
930
1007
|
true
|
|
931
1008
|
);
|
|
932
1009
|
body.response_format = options.responseFormat.type === "json_schema" && !supportsSchema ? { type: "json_object" } : options.responseFormat;
|
|
@@ -937,7 +1014,7 @@ var OpenAICompatibleProvider = class {
|
|
|
937
1014
|
if (profile.requestDefaults) Object.assign(body, profile.requestDefaults);
|
|
938
1015
|
const userKeys = options.providerOptions ? Object.keys(options.providerOptions) : [];
|
|
939
1016
|
if (options.providerOptions) Object.assign(body, options.providerOptions);
|
|
940
|
-
const forbidden = resolvePerModel(profile.capabilities?.forbiddenParams,
|
|
1017
|
+
const forbidden = resolvePerModel(profile.capabilities?.forbiddenParams, effectiveModel, []);
|
|
941
1018
|
for (const key of forbidden) {
|
|
942
1019
|
if (!userKeys.includes(key)) delete body[key];
|
|
943
1020
|
}
|
|
@@ -972,6 +1049,9 @@ var OpenAICompatibleProvider = class {
|
|
|
972
1049
|
}
|
|
973
1050
|
return `${label} API error (${status}): ${body}`;
|
|
974
1051
|
}
|
|
1052
|
+
requestModel(body, fallback) {
|
|
1053
|
+
return typeof body.model === "string" ? body.model : fallback;
|
|
1054
|
+
}
|
|
975
1055
|
toUsage(raw) {
|
|
976
1056
|
if (!raw) return void 0;
|
|
977
1057
|
return {
|
|
@@ -979,10 +1059,27 @@ var OpenAICompatibleProvider = class {
|
|
|
979
1059
|
completion_tokens: raw.completion_tokens,
|
|
980
1060
|
total_tokens: raw.total_tokens,
|
|
981
1061
|
reasoning_tokens: raw.completion_tokens_details?.reasoning_tokens,
|
|
982
|
-
|
|
1062
|
+
// DeepSeek reports the cache split at the top level rather than under
|
|
1063
|
+
// prompt_tokens_details. Preserve the normalized hit count for the
|
|
1064
|
+
// generic table estimator; its split is validated before pricing.
|
|
1065
|
+
cached_tokens: raw.prompt_cache_hit_tokens ?? raw.prompt_tokens_details?.cached_tokens,
|
|
1066
|
+
cache_write_tokens: raw.prompt_tokens_details?.cache_write_tokens
|
|
983
1067
|
};
|
|
984
1068
|
}
|
|
985
|
-
|
|
1069
|
+
reportedCost(raw) {
|
|
1070
|
+
if (!raw) return void 0;
|
|
1071
|
+
if (this.profile.name === "xai") {
|
|
1072
|
+
const ticks = raw.cost_in_usd_ticks;
|
|
1073
|
+
return typeof ticks === "number" && Number.isSafeInteger(ticks) && ticks >= 0 ? ticks / 1e10 : void 0;
|
|
1074
|
+
}
|
|
1075
|
+
return typeof raw.cost === "number" && Number.isFinite(raw.cost) && raw.cost >= 0 ? raw.cost : void 0;
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* The generic profile path stays flat and public. Native subclasses may
|
|
1079
|
+
* override this protected seam to use a provider-internal estimator without
|
|
1080
|
+
* widening `PricingSource` or `ProviderProfile`.
|
|
1081
|
+
*/
|
|
1082
|
+
computeCost(model, usage, reportedCost, context) {
|
|
986
1083
|
const p = this.profile.pricing;
|
|
987
1084
|
if (p.kind === "zero") return 0;
|
|
988
1085
|
if (!usage) return void 0;
|
|
@@ -990,14 +1087,22 @@ var OpenAICompatibleProvider = class {
|
|
|
990
1087
|
case "unknown":
|
|
991
1088
|
return void 0;
|
|
992
1089
|
case "from-response":
|
|
993
|
-
return typeof reportedCost === "number" && Number.isFinite(reportedCost) ? reportedCost : void 0;
|
|
1090
|
+
return typeof reportedCost === "number" && Number.isFinite(reportedCost) && reportedCost >= 0 ? reportedCost : void 0;
|
|
994
1091
|
case "table": {
|
|
1092
|
+
if (!isBuiltinTablePricingEligible(this.profile, {
|
|
1093
|
+
baseUrl: this.baseUrl,
|
|
1094
|
+
request: context?.request,
|
|
1095
|
+
response: context?.response
|
|
1096
|
+
})) {
|
|
1097
|
+
return void 0;
|
|
1098
|
+
}
|
|
995
1099
|
const c = priceFromTable(
|
|
996
1100
|
p.table,
|
|
997
1101
|
model,
|
|
998
1102
|
usage.prompt_tokens,
|
|
999
1103
|
usage.completion_tokens,
|
|
1000
|
-
usage.cached_tokens
|
|
1104
|
+
usage.cached_tokens,
|
|
1105
|
+
p.match
|
|
1001
1106
|
);
|
|
1002
1107
|
return c !== void 0 && Number.isFinite(c) ? c : void 0;
|
|
1003
1108
|
}
|
|
@@ -1007,7 +1112,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1007
1112
|
}
|
|
1008
1113
|
}
|
|
1009
1114
|
}
|
|
1010
|
-
parseResponse(json, model) {
|
|
1115
|
+
parseResponse(json, model, request) {
|
|
1011
1116
|
const message = json.choices?.[0]?.message ?? {};
|
|
1012
1117
|
let content = normalizeMessageContent(message.content);
|
|
1013
1118
|
let thinking;
|
|
@@ -1053,7 +1158,10 @@ var OpenAICompatibleProvider = class {
|
|
|
1053
1158
|
}
|
|
1054
1159
|
}
|
|
1055
1160
|
const usage = this.toUsage(json.usage);
|
|
1056
|
-
const cost = this.computeCost(model, usage, json.usage
|
|
1161
|
+
const cost = this.computeCost(json.model ?? model, usage, this.reportedCost(json.usage), {
|
|
1162
|
+
request,
|
|
1163
|
+
response: json
|
|
1164
|
+
});
|
|
1057
1165
|
return {
|
|
1058
1166
|
content,
|
|
1059
1167
|
thinking_content: thinking || void 0,
|
|
@@ -1076,11 +1184,12 @@ var OpenAICompatibleProvider = class {
|
|
|
1076
1184
|
// ---------------------------------------------------------------------------
|
|
1077
1185
|
// SSE streaming
|
|
1078
1186
|
// ---------------------------------------------------------------------------
|
|
1079
|
-
async *parseSSEStream(body, model) {
|
|
1187
|
+
async *parseSSEStream(body, model, request) {
|
|
1080
1188
|
const reader = body.getReader();
|
|
1081
1189
|
const decoder = new TextDecoder();
|
|
1082
1190
|
let buffer = "";
|
|
1083
1191
|
let usageData;
|
|
1192
|
+
let pricingResponse;
|
|
1084
1193
|
const capture = this.profile.reasoning.capture;
|
|
1085
1194
|
const thinkScanner = capture === "think_tags" ? new ThinkTagScanner() : void 0;
|
|
1086
1195
|
let reasoningText = "";
|
|
@@ -1121,7 +1230,15 @@ var OpenAICompatibleProvider = class {
|
|
|
1121
1230
|
return {
|
|
1122
1231
|
type: "done",
|
|
1123
1232
|
usage,
|
|
1124
|
-
cost: this.computeCost(
|
|
1233
|
+
cost: this.computeCost(
|
|
1234
|
+
pricingResponse?.model ?? model,
|
|
1235
|
+
usage,
|
|
1236
|
+
this.reportedCost(usageData),
|
|
1237
|
+
{
|
|
1238
|
+
request,
|
|
1239
|
+
response: pricingResponse
|
|
1240
|
+
}
|
|
1241
|
+
),
|
|
1125
1242
|
providerMetadata
|
|
1126
1243
|
};
|
|
1127
1244
|
};
|
|
@@ -1162,8 +1279,12 @@ var OpenAICompatibleProvider = class {
|
|
|
1162
1279
|
body: JSON.stringify(parsed.error)
|
|
1163
1280
|
});
|
|
1164
1281
|
}
|
|
1282
|
+
if (parsed.model !== void 0 || parsed.service_tier !== void 0 || parsed.serviceTier !== void 0) {
|
|
1283
|
+
pricingResponse = { ...pricingResponse, ...parsed };
|
|
1284
|
+
}
|
|
1165
1285
|
if (parsed.usage) {
|
|
1166
1286
|
usageData = parsed.usage;
|
|
1287
|
+
pricingResponse = { ...pricingResponse, ...parsed };
|
|
1167
1288
|
if (!parsed.choices || parsed.choices.length === 0) continue;
|
|
1168
1289
|
}
|
|
1169
1290
|
const delta = parsed.choices?.[0]?.delta;
|
|
@@ -1237,38 +1358,328 @@ var OpenAICompatibleProvider = class {
|
|
|
1237
1358
|
|
|
1238
1359
|
// src/providers/openai.ts
|
|
1239
1360
|
var OPENAI_PRICING = {
|
|
1240
|
-
//
|
|
1361
|
+
// Flat, exact Standard rows only. Context-tiered and cache-write-priced
|
|
1362
|
+
// models intentionally stay out of this compatibility view.
|
|
1241
1363
|
"gpt-4o": [25e-7, 1e-5, 0.5],
|
|
1242
1364
|
"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
1365
|
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
1366
|
"gpt-4.1": [2e-6, 8e-6, 0.25],
|
|
1251
1367
|
"gpt-4.1-mini": [4e-7, 16e-7, 0.25],
|
|
1252
1368
|
"gpt-4.1-nano": [1e-7, 4e-7, 0.25],
|
|
1253
|
-
o3: [
|
|
1369
|
+
o3: [2e-6, 8e-6, 0.25],
|
|
1254
1370
|
"o3-mini": [11e-7, 44e-7, 0.25],
|
|
1255
|
-
"o3-pro": [2e-5, 8e-5, 0.25],
|
|
1256
1371
|
"o4-mini": [11e-7, 44e-7, 0.25],
|
|
1257
|
-
// gpt-5 era — cache reads at 10% of input rate
|
|
1258
1372
|
"gpt-5": [125e-8, 1e-5, 0.1],
|
|
1259
1373
|
"gpt-5-mini": [25e-8, 2e-6, 0.1],
|
|
1260
1374
|
"gpt-5-nano": [5e-8, 4e-7, 0.1],
|
|
1261
1375
|
"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]
|
|
1376
|
+
"gpt-5.2": [175e-8, 14e-6, 0.1]
|
|
1269
1377
|
};
|
|
1378
|
+
var M = 1e6;
|
|
1379
|
+
var LONG_CONTEXT_BOUNDARY = 272e3;
|
|
1380
|
+
var withLongContext = (short) => ({
|
|
1381
|
+
input: short.input * 2,
|
|
1382
|
+
cachedInput: short.cachedInput === void 0 ? void 0 : short.cachedInput * 2,
|
|
1383
|
+
cacheWrite: short.cacheWrite === void 0 ? void 0 : short.cacheWrite * 2,
|
|
1384
|
+
output: short.output * 1.5
|
|
1385
|
+
});
|
|
1386
|
+
var DIRECT_OPENAI_CATALOG = [
|
|
1387
|
+
{
|
|
1388
|
+
aliases: ["gpt-5.6", "gpt-5.6-sol"],
|
|
1389
|
+
snapshotBase: "gpt-5.6-sol",
|
|
1390
|
+
short: { input: 5 / M, cachedInput: 0.5 / M, cacheWrite: 6.25 / M, output: 30 / M },
|
|
1391
|
+
long: withLongContext({
|
|
1392
|
+
input: 5 / M,
|
|
1393
|
+
cachedInput: 0.5 / M,
|
|
1394
|
+
cacheWrite: 6.25 / M,
|
|
1395
|
+
output: 30 / M
|
|
1396
|
+
}),
|
|
1397
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1398
|
+
},
|
|
1399
|
+
{
|
|
1400
|
+
aliases: ["gpt-5.6-terra"],
|
|
1401
|
+
snapshotBase: "gpt-5.6-terra",
|
|
1402
|
+
short: { input: 2 / M, cachedInput: 0.2 / M, cacheWrite: 2.5 / M, output: 12 / M },
|
|
1403
|
+
long: withLongContext({
|
|
1404
|
+
input: 2 / M,
|
|
1405
|
+
cachedInput: 0.2 / M,
|
|
1406
|
+
cacheWrite: 2.5 / M,
|
|
1407
|
+
output: 12 / M
|
|
1408
|
+
}),
|
|
1409
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
aliases: ["gpt-5.6-luna"],
|
|
1413
|
+
snapshotBase: "gpt-5.6-luna",
|
|
1414
|
+
short: { input: 0.2 / M, cachedInput: 0.02 / M, cacheWrite: 0.25 / M, output: 1.2 / M },
|
|
1415
|
+
long: withLongContext({
|
|
1416
|
+
input: 0.2 / M,
|
|
1417
|
+
cachedInput: 0.02 / M,
|
|
1418
|
+
cacheWrite: 0.25 / M,
|
|
1419
|
+
output: 1.2 / M
|
|
1420
|
+
}),
|
|
1421
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1422
|
+
},
|
|
1423
|
+
// Existing direct models retain literal current Standard rows. These flat
|
|
1424
|
+
// rows keep native pricing aligned with the compatibility wrapper while the
|
|
1425
|
+
// private estimator still validates categories and billing mode strictly.
|
|
1426
|
+
{
|
|
1427
|
+
aliases: ["gpt-4o", "gpt-4o-2024-08-06", "gpt-4o-2024-11-20"],
|
|
1428
|
+
snapshotBase: "gpt-4o",
|
|
1429
|
+
short: { input: 2.5 / M, cachedInput: 1.25 / M, output: 10 / M }
|
|
1430
|
+
},
|
|
1431
|
+
{
|
|
1432
|
+
aliases: ["gpt-4o-mini", "gpt-4o-mini-2024-07-18"],
|
|
1433
|
+
snapshotBase: "gpt-4o-mini",
|
|
1434
|
+
short: { input: 0.15 / M, cachedInput: 0.075 / M, output: 0.6 / M }
|
|
1435
|
+
},
|
|
1436
|
+
{
|
|
1437
|
+
aliases: ["gpt-3.5-turbo"],
|
|
1438
|
+
snapshotBase: "gpt-3.5-turbo",
|
|
1439
|
+
short: { input: 0.5 / M, output: 1.5 / M }
|
|
1440
|
+
},
|
|
1441
|
+
{
|
|
1442
|
+
aliases: ["o1", "o1-2024-12-17"],
|
|
1443
|
+
snapshotBase: "o1",
|
|
1444
|
+
short: { input: 15 / M, cachedInput: 7.5 / M, output: 60 / M }
|
|
1445
|
+
},
|
|
1446
|
+
{
|
|
1447
|
+
aliases: ["o1-pro", "o1-pro-2025-03-19"],
|
|
1448
|
+
snapshotBase: "o1-pro",
|
|
1449
|
+
short: { input: 150 / M, output: 600 / M }
|
|
1450
|
+
},
|
|
1451
|
+
{
|
|
1452
|
+
aliases: ["gpt-4.1", "gpt-4.1-2025-04-14"],
|
|
1453
|
+
snapshotBase: "gpt-4.1",
|
|
1454
|
+
short: { input: 2 / M, cachedInput: 0.5 / M, output: 8 / M }
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
aliases: ["gpt-4.1-mini", "gpt-4.1-mini-2025-04-14"],
|
|
1458
|
+
snapshotBase: "gpt-4.1-mini",
|
|
1459
|
+
short: { input: 0.4 / M, cachedInput: 0.1 / M, output: 1.6 / M }
|
|
1460
|
+
},
|
|
1461
|
+
{
|
|
1462
|
+
aliases: ["gpt-4.1-nano", "gpt-4.1-nano-2025-04-14"],
|
|
1463
|
+
snapshotBase: "gpt-4.1-nano",
|
|
1464
|
+
short: { input: 0.1 / M, cachedInput: 0.025 / M, output: 0.4 / M }
|
|
1465
|
+
},
|
|
1466
|
+
{
|
|
1467
|
+
aliases: ["o3", "o3-2025-04-16"],
|
|
1468
|
+
snapshotBase: "o3",
|
|
1469
|
+
short: { input: 2 / M, cachedInput: 0.5 / M, output: 8 / M }
|
|
1470
|
+
},
|
|
1471
|
+
{
|
|
1472
|
+
aliases: ["o3-mini", "o3-mini-2025-01-31"],
|
|
1473
|
+
snapshotBase: "o3-mini",
|
|
1474
|
+
short: { input: 1.1 / M, cachedInput: 0.55 / M, output: 4.4 / M }
|
|
1475
|
+
},
|
|
1476
|
+
{
|
|
1477
|
+
aliases: ["o3-pro", "o3-pro-2025-06-10"],
|
|
1478
|
+
snapshotBase: "o3-pro",
|
|
1479
|
+
short: { input: 20 / M, output: 80 / M }
|
|
1480
|
+
},
|
|
1481
|
+
{
|
|
1482
|
+
aliases: ["o4-mini", "o4-mini-2025-04-16"],
|
|
1483
|
+
snapshotBase: "o4-mini",
|
|
1484
|
+
short: { input: 1.1 / M, cachedInput: 0.275 / M, output: 4.4 / M }
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
aliases: ["gpt-5", "gpt-5-2025-08-07"],
|
|
1488
|
+
snapshotBase: "gpt-5",
|
|
1489
|
+
short: { input: 1.25 / M, cachedInput: 0.125 / M, output: 10 / M }
|
|
1490
|
+
},
|
|
1491
|
+
{
|
|
1492
|
+
aliases: ["gpt-5-mini", "gpt-5-mini-2025-08-07"],
|
|
1493
|
+
snapshotBase: "gpt-5-mini",
|
|
1494
|
+
short: { input: 0.25 / M, cachedInput: 0.025 / M, output: 2 / M }
|
|
1495
|
+
},
|
|
1496
|
+
{
|
|
1497
|
+
aliases: ["gpt-5-nano", "gpt-5-nano-2025-08-07"],
|
|
1498
|
+
snapshotBase: "gpt-5-nano",
|
|
1499
|
+
short: { input: 0.05 / M, cachedInput: 5e-3 / M, output: 0.4 / M }
|
|
1500
|
+
},
|
|
1501
|
+
{
|
|
1502
|
+
aliases: ["gpt-5.1", "gpt-5.1-2025-11-13"],
|
|
1503
|
+
snapshotBase: "gpt-5.1",
|
|
1504
|
+
short: { input: 1.25 / M, cachedInput: 0.125 / M, output: 10 / M }
|
|
1505
|
+
},
|
|
1506
|
+
{
|
|
1507
|
+
aliases: ["gpt-5.2", "gpt-5.2-2025-12-11"],
|
|
1508
|
+
snapshotBase: "gpt-5.2",
|
|
1509
|
+
short: { input: 1.75 / M, cachedInput: 0.175 / M, output: 14 / M }
|
|
1510
|
+
},
|
|
1511
|
+
{
|
|
1512
|
+
aliases: ["gpt-5.4", "gpt-5.4-2026-03-05"],
|
|
1513
|
+
snapshotBase: "gpt-5.4",
|
|
1514
|
+
short: { input: 2.5 / M, cachedInput: 0.25 / M, output: 15 / M },
|
|
1515
|
+
long: withLongContext({ input: 2.5 / M, cachedInput: 0.25 / M, output: 15 / M }),
|
|
1516
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1517
|
+
},
|
|
1518
|
+
{
|
|
1519
|
+
aliases: ["gpt-5.4-pro", "gpt-5.4-pro-2026-03-05"],
|
|
1520
|
+
snapshotBase: "gpt-5.4-pro",
|
|
1521
|
+
short: { input: 30 / M, output: 180 / M },
|
|
1522
|
+
long: withLongContext({ input: 30 / M, output: 180 / M }),
|
|
1523
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1524
|
+
},
|
|
1525
|
+
{
|
|
1526
|
+
aliases: ["gpt-5.5", "gpt-5.5-2026-04-23"],
|
|
1527
|
+
snapshotBase: "gpt-5.5",
|
|
1528
|
+
short: { input: 5 / M, cachedInput: 0.5 / M, output: 30 / M },
|
|
1529
|
+
long: withLongContext({ input: 5 / M, cachedInput: 0.5 / M, output: 30 / M }),
|
|
1530
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1531
|
+
},
|
|
1532
|
+
{
|
|
1533
|
+
aliases: ["gpt-5.5-pro", "gpt-5.5-pro-2026-04-23"],
|
|
1534
|
+
snapshotBase: "gpt-5.5-pro",
|
|
1535
|
+
short: { input: 30 / M, output: 180 / M },
|
|
1536
|
+
long: withLongContext({ input: 30 / M, output: 180 / M }),
|
|
1537
|
+
contextBoundary: LONG_CONTEXT_BOUNDARY
|
|
1538
|
+
},
|
|
1539
|
+
{
|
|
1540
|
+
aliases: ["gpt-5.4-mini", "gpt-5.4-mini-2026-03-17"],
|
|
1541
|
+
short: { input: 0.75 / M, cachedInput: 0.075 / M, output: 4.5 / M }
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
aliases: ["gpt-5.4-nano", "gpt-5.4-nano-2026-03-17"],
|
|
1545
|
+
short: { input: 0.2 / M, cachedInput: 0.02 / M, output: 1.25 / M }
|
|
1546
|
+
},
|
|
1547
|
+
{
|
|
1548
|
+
aliases: ["gpt-5.2-pro", "gpt-5.2-pro-2025-12-11"],
|
|
1549
|
+
short: { input: 21 / M, output: 168 / M }
|
|
1550
|
+
},
|
|
1551
|
+
{ aliases: ["gpt-5-pro", "gpt-5-pro-2025-10-06"], short: { input: 15 / M, output: 120 / M } },
|
|
1552
|
+
{ aliases: ["gpt-3.5-turbo-instruct"], short: { input: 1.5 / M, output: 2 / M } },
|
|
1553
|
+
{ aliases: ["davinci-002"], short: { input: 2 / M, output: 2 / M } },
|
|
1554
|
+
{ aliases: ["babbage-002"], short: { input: 0.4 / M, output: 0.4 / M } },
|
|
1555
|
+
// Explicit snapshots with their own current dedicated-table rows. Do not
|
|
1556
|
+
// generalize these dates: a plausible-looking unlisted snapshot is unpriced.
|
|
1557
|
+
{ aliases: ["gpt-4o-2024-05-13"], short: { input: 5 / M, output: 15 / M } },
|
|
1558
|
+
{ aliases: ["gpt-4-turbo-2024-04-09"], short: { input: 10 / M, output: 30 / M } },
|
|
1559
|
+
{ aliases: ["gpt-4-0613"], short: { input: 30 / M, output: 60 / M } },
|
|
1560
|
+
{
|
|
1561
|
+
aliases: ["gpt-3.5-turbo-0125"],
|
|
1562
|
+
short: { input: 0.5 / M, output: 1.5 / M }
|
|
1563
|
+
},
|
|
1564
|
+
{ aliases: ["gpt-3.5-turbo-1106"], short: { input: 1 / M, output: 2 / M } }
|
|
1565
|
+
];
|
|
1566
|
+
function directOpenAIModel(model) {
|
|
1567
|
+
return DIRECT_OPENAI_CATALOG.find((entry) => entry.aliases.includes(model));
|
|
1568
|
+
}
|
|
1569
|
+
var CANONICAL_OPENAI_BASE_URL = "https://api.openai.com/v1";
|
|
1570
|
+
function isTextContentPart(value, type) {
|
|
1571
|
+
return value !== null && typeof value === "object" && value.type === type && typeof value.text === "string";
|
|
1572
|
+
}
|
|
1573
|
+
function hasUnmodeledDirectOpenAIContent(request) {
|
|
1574
|
+
if ("messages" in request) {
|
|
1575
|
+
if (!Array.isArray(request.messages)) return true;
|
|
1576
|
+
for (const message of request.messages) {
|
|
1577
|
+
if (message === null || typeof message !== "object") return true;
|
|
1578
|
+
const content = message.content;
|
|
1579
|
+
if (content === void 0 || content === null || typeof content === "string") continue;
|
|
1580
|
+
if (!Array.isArray(content) || content.some((part) => !isTextContentPart(part, "text"))) {
|
|
1581
|
+
return true;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return false;
|
|
1585
|
+
}
|
|
1586
|
+
if ("input" in request) {
|
|
1587
|
+
const input = request.input;
|
|
1588
|
+
if (typeof input === "string") return false;
|
|
1589
|
+
if (!Array.isArray(input)) return true;
|
|
1590
|
+
for (const item of input) {
|
|
1591
|
+
if (item === null || typeof item !== "object") return true;
|
|
1592
|
+
const typed = item;
|
|
1593
|
+
switch (typed.type) {
|
|
1594
|
+
case "message": {
|
|
1595
|
+
const content = typed.content;
|
|
1596
|
+
if (typeof content === "string") break;
|
|
1597
|
+
if (!Array.isArray(content) || content.some((part) => !isTextContentPart(part, "input_text"))) {
|
|
1598
|
+
return true;
|
|
1599
|
+
}
|
|
1600
|
+
break;
|
|
1601
|
+
}
|
|
1602
|
+
case "function_call":
|
|
1603
|
+
break;
|
|
1604
|
+
case "function_call_output": {
|
|
1605
|
+
const output = typed.output;
|
|
1606
|
+
if (typeof output === "string") break;
|
|
1607
|
+
if (!Array.isArray(output) || output.some((part) => !isTextContentPart(part, "input_text"))) {
|
|
1608
|
+
return true;
|
|
1609
|
+
}
|
|
1610
|
+
break;
|
|
1611
|
+
}
|
|
1612
|
+
case "custom_tool_call":
|
|
1613
|
+
if (typeof typed.input !== "string") return true;
|
|
1614
|
+
break;
|
|
1615
|
+
case "custom_tool_call_output":
|
|
1616
|
+
if (typeof typed.output !== "string") return true;
|
|
1617
|
+
break;
|
|
1618
|
+
case "reasoning":
|
|
1619
|
+
break;
|
|
1620
|
+
default:
|
|
1621
|
+
return true;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
return false;
|
|
1625
|
+
}
|
|
1626
|
+
return false;
|
|
1627
|
+
}
|
|
1628
|
+
function isEligibleDirectOpenAIContext(context) {
|
|
1629
|
+
const request = context?.request;
|
|
1630
|
+
const response = context?.response;
|
|
1631
|
+
if (context?.baseUrl !== void 0 && context.baseUrl !== CANONICAL_OPENAI_BASE_URL) return false;
|
|
1632
|
+
if (request && hasUnmodeledDirectOpenAIContent(request)) return false;
|
|
1633
|
+
const tier = response?.service_tier ?? response?.serviceTier ?? request?.service_tier ?? request?.serviceTier;
|
|
1634
|
+
if (tier !== void 0 && (typeof tier !== "string" || !["standard", "default"].includes(tier.toLowerCase()))) {
|
|
1635
|
+
return false;
|
|
1636
|
+
}
|
|
1637
|
+
if (["region", "inference_geo", "data_residency"].some((key) => request?.[key] !== void 0)) {
|
|
1638
|
+
return false;
|
|
1639
|
+
}
|
|
1640
|
+
if (["previous_response_id", "conversation", "prompt"].some((key) => request?.[key] !== void 0)) {
|
|
1641
|
+
return false;
|
|
1642
|
+
}
|
|
1643
|
+
const reasoning = request?.reasoning;
|
|
1644
|
+
if (reasoning !== null && typeof reasoning === "object" && "mode" in reasoning && reasoning.mode !== void 0) {
|
|
1645
|
+
return false;
|
|
1646
|
+
}
|
|
1647
|
+
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)) {
|
|
1648
|
+
return false;
|
|
1649
|
+
}
|
|
1650
|
+
if (Array.isArray(request?.tools) && request.tools.some(
|
|
1651
|
+
(tool2) => tool2 === null || typeof tool2 !== "object" || !["function", "custom"].includes(tool2.type)
|
|
1652
|
+
)) {
|
|
1653
|
+
return false;
|
|
1654
|
+
}
|
|
1655
|
+
return true;
|
|
1656
|
+
}
|
|
1657
|
+
function estimateDirectOpenAICost(model, usage, context) {
|
|
1658
|
+
if (!isEligibleDirectOpenAIContext(context)) return void 0;
|
|
1659
|
+
const entry = directOpenAIModel(model);
|
|
1660
|
+
if (!entry) return void 0;
|
|
1661
|
+
const { prompt_tokens, completion_tokens, cached_tokens, cache_write_tokens } = usage;
|
|
1662
|
+
const cached = cached_tokens ?? 0;
|
|
1663
|
+
const cacheWrite = cache_write_tokens ?? 0;
|
|
1664
|
+
if (![prompt_tokens, completion_tokens, cached, cacheWrite].every(
|
|
1665
|
+
(count) => Number.isSafeInteger(count) && count >= 0
|
|
1666
|
+
) || cached + cacheWrite > prompt_tokens) {
|
|
1667
|
+
return void 0;
|
|
1668
|
+
}
|
|
1669
|
+
const rates = entry.long !== void 0 && entry.contextBoundary !== void 0 && prompt_tokens > entry.contextBoundary ? entry.long : entry.short;
|
|
1670
|
+
if (cached > 0 && rates.cachedInput === void 0 || cacheWrite > 0 && rates.cacheWrite === void 0) {
|
|
1671
|
+
return void 0;
|
|
1672
|
+
}
|
|
1673
|
+
const ordinary = prompt_tokens - cached - cacheWrite;
|
|
1674
|
+
return ordinary * rates.input + cached * (rates.cachedInput ?? 0) + cacheWrite * (rates.cacheWrite ?? 0) + completion_tokens * rates.output;
|
|
1675
|
+
}
|
|
1270
1676
|
function estimateOpenAICost(model, promptTokens, completionTokens, cachedTokens) {
|
|
1271
|
-
return
|
|
1677
|
+
return estimateDirectOpenAICost(model, {
|
|
1678
|
+
prompt_tokens: promptTokens,
|
|
1679
|
+
completion_tokens: completionTokens,
|
|
1680
|
+
total_tokens: promptTokens + completionTokens,
|
|
1681
|
+
cached_tokens: cachedTokens
|
|
1682
|
+
});
|
|
1272
1683
|
}
|
|
1273
1684
|
function isOSeriesModel(model) {
|
|
1274
1685
|
return /^(o1|o3|o4-mini)/.test(model);
|
|
@@ -1276,6 +1687,15 @@ function isOSeriesModel(model) {
|
|
|
1276
1687
|
function supportsReasoningEffort(model) {
|
|
1277
1688
|
return isOSeriesModel(model) || /^gpt-5/.test(model);
|
|
1278
1689
|
}
|
|
1690
|
+
var GPT_56_REASONING_MODELS = /* @__PURE__ */ new Set([
|
|
1691
|
+
"gpt-5.6",
|
|
1692
|
+
"gpt-5.6-sol",
|
|
1693
|
+
"gpt-5.6-terra",
|
|
1694
|
+
"gpt-5.6-luna"
|
|
1695
|
+
]);
|
|
1696
|
+
function supportsMaxReasoningEffort(model) {
|
|
1697
|
+
return GPT_56_REASONING_MODELS.has(model);
|
|
1698
|
+
}
|
|
1279
1699
|
function supportsReasoningNone(model) {
|
|
1280
1700
|
return /^gpt-5\.[1-9]/.test(model);
|
|
1281
1701
|
}
|
|
@@ -1285,31 +1705,46 @@ function supportsXhigh(model) {
|
|
|
1285
1705
|
function clampReasoningEffort(model, effort) {
|
|
1286
1706
|
if (model.startsWith("gpt-5-pro")) return "high";
|
|
1287
1707
|
if (effort === "none" && !supportsReasoningNone(model)) return "minimal";
|
|
1708
|
+
if (effort === "max" && !supportsMaxReasoningEffort(model)) {
|
|
1709
|
+
return clampReasoningEffort(model, "xhigh");
|
|
1710
|
+
}
|
|
1288
1711
|
if (effort === "xhigh" && !supportsXhigh(model)) return "high";
|
|
1289
1712
|
return effort;
|
|
1290
1713
|
}
|
|
1291
|
-
function effortToReasoningEffort(effort) {
|
|
1292
|
-
return effort === "max" ? "xhigh" : effort;
|
|
1714
|
+
function effortToReasoningEffort(effort, model) {
|
|
1715
|
+
return effort === "max" && model !== void 0 && supportsMaxReasoningEffort(model) ? "max" : effort === "max" ? "xhigh" : effort;
|
|
1293
1716
|
}
|
|
1294
1717
|
function budgetToReasoningEffort(budget) {
|
|
1295
1718
|
if (budget <= 1024) return "low";
|
|
1296
1719
|
if (budget <= 8192) return "medium";
|
|
1297
1720
|
return "high";
|
|
1298
1721
|
}
|
|
1722
|
+
function resolveOpenAIReasoningEffort(model, resolved) {
|
|
1723
|
+
if (!supportsReasoningEffort(model)) return void 0;
|
|
1724
|
+
if (resolved.hasBudgetOverride) {
|
|
1725
|
+
return clampReasoningEffort(model, budgetToReasoningEffort(resolved.thinkingBudget));
|
|
1726
|
+
}
|
|
1727
|
+
if (!resolved.thinkingDisabled && resolved.activeEffort) {
|
|
1728
|
+
return clampReasoningEffort(model, effortToReasoningEffort(resolved.activeEffort, model));
|
|
1729
|
+
}
|
|
1730
|
+
return resolved.thinkingDisabled ? clampReasoningEffort(model, "none") : void 0;
|
|
1731
|
+
}
|
|
1732
|
+
var warnedChatMaxDowngrades = /* @__PURE__ */ new Set();
|
|
1733
|
+
function resolveOpenAIChatReasoningEffort(model, resolved) {
|
|
1734
|
+
const effort = resolveOpenAIReasoningEffort(model, resolved);
|
|
1735
|
+
if (effort !== "max") return effort;
|
|
1736
|
+
if (resolved.activeEffort === "max" && !warnedChatMaxDowngrades.has(model)) {
|
|
1737
|
+
warnedChatMaxDowngrades.add(model);
|
|
1738
|
+
console.warn(
|
|
1739
|
+
`[axl] OpenAI Chat Completions does not accept effort "max" for ${model}; using "xhigh". Use the OpenAI Responses provider for native "max".`
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
return "xhigh";
|
|
1743
|
+
}
|
|
1299
1744
|
var openaiReasoningEmit = (body, resolved, model) => {
|
|
1300
1745
|
const oSeries = isOSeriesModel(model);
|
|
1301
1746
|
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
|
-
}
|
|
1747
|
+
const wireEffort = resolveOpenAIChatReasoningEffort(model, resolved);
|
|
1313
1748
|
if (wireEffort) body.reasoning_effort = wireEffort;
|
|
1314
1749
|
return { stripTemperature: oSeries || reasoningCapable && wireEffort !== void 0 };
|
|
1315
1750
|
};
|
|
@@ -1319,7 +1754,7 @@ var OPENAI_PROFILE = {
|
|
|
1319
1754
|
defaultBaseUrl: "https://api.openai.com/v1",
|
|
1320
1755
|
envApiKey: "OPENAI_API_KEY",
|
|
1321
1756
|
envBaseUrl: "OPENAI_BASE_URL",
|
|
1322
|
-
pricing: { kind: "table", table: OPENAI_PRICING },
|
|
1757
|
+
pricing: { kind: "table", table: OPENAI_PRICING, match: "exact" },
|
|
1323
1758
|
reasoning: { emit: openaiReasoningEmit, capture: "none" },
|
|
1324
1759
|
roleFor: (role, model) => role === "system" && isOSeriesModel(model) ? "developer" : role,
|
|
1325
1760
|
maxTokensField: "max_completion_tokens",
|
|
@@ -1329,6 +1764,9 @@ var OpenAIProvider = class extends OpenAICompatibleProvider {
|
|
|
1329
1764
|
constructor(options = {}) {
|
|
1330
1765
|
super({ profile: OPENAI_PROFILE, ...options });
|
|
1331
1766
|
}
|
|
1767
|
+
computeCost(model, usage, _reportedCost, context) {
|
|
1768
|
+
return usage ? estimateDirectOpenAICost(model, usage, { ...context, baseUrl: this.baseUrl }) : void 0;
|
|
1769
|
+
}
|
|
1332
1770
|
};
|
|
1333
1771
|
|
|
1334
1772
|
// src/providers/openai-responses.ts
|
|
@@ -1344,6 +1782,11 @@ var OpenAIResponsesProvider = class {
|
|
|
1344
1782
|
constructor(options = {}) {
|
|
1345
1783
|
this.apiKeySource = options.apiKey ?? process.env.OPENAI_API_KEY ?? "";
|
|
1346
1784
|
this.baseUrl = (options.baseUrl ?? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1").replace(/\/$/, "");
|
|
1785
|
+
assertSafeProviderBaseUrl(
|
|
1786
|
+
this.baseUrl,
|
|
1787
|
+
"OpenAI Responses provider",
|
|
1788
|
+
options.dangerouslyAllowInsecureHttp
|
|
1789
|
+
);
|
|
1347
1790
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
1348
1791
|
if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
|
|
1349
1792
|
throw new Error("OpenAI API key is required. Set OPENAI_API_KEY or pass apiKey in options.");
|
|
@@ -1389,7 +1832,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1389
1832
|
});
|
|
1390
1833
|
}
|
|
1391
1834
|
const json = await res.json();
|
|
1392
|
-
return this.parseResponse(json, options.model);
|
|
1835
|
+
return this.parseResponse(json, this.requestModel(body, options.model), body);
|
|
1393
1836
|
}
|
|
1394
1837
|
// ---------------------------------------------------------------------------
|
|
1395
1838
|
// stream - SSE streaming
|
|
@@ -1421,30 +1864,23 @@ var OpenAIResponsesProvider = class {
|
|
|
1421
1864
|
if (!res.body) {
|
|
1422
1865
|
throw new Error("OpenAI Responses stream has no body");
|
|
1423
1866
|
}
|
|
1424
|
-
yield* this.parseSSEStream(res.body, options.model);
|
|
1867
|
+
yield* this.parseSSEStream(res.body, this.requestModel(body, options.model), body);
|
|
1425
1868
|
}
|
|
1426
1869
|
// ---------------------------------------------------------------------------
|
|
1427
1870
|
// Internal: build request body
|
|
1428
1871
|
// ---------------------------------------------------------------------------
|
|
1429
1872
|
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
|
-
}
|
|
1873
|
+
const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
1874
|
+
const oSeries = isOSeriesModel(effectiveModel);
|
|
1875
|
+
const reasoningCapable = supportsReasoningEffort(effectiveModel);
|
|
1876
|
+
const resolved = resolveThinkingOptions(options);
|
|
1877
|
+
const { includeThoughts } = resolved;
|
|
1878
|
+
const wireEffort = resolveOpenAIReasoningEffort(effectiveModel, resolved);
|
|
1443
1879
|
const stripTemp = oSeries || reasoningCapable && wireEffort !== void 0;
|
|
1444
1880
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
1445
1881
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
1446
1882
|
const body = {
|
|
1447
|
-
model:
|
|
1883
|
+
model: effectiveModel,
|
|
1448
1884
|
input: this.buildInput(nonSystemMessages),
|
|
1449
1885
|
store: false,
|
|
1450
1886
|
stream
|
|
@@ -1491,6 +1927,9 @@ var OpenAIResponsesProvider = class {
|
|
|
1491
1927
|
}
|
|
1492
1928
|
return body;
|
|
1493
1929
|
}
|
|
1930
|
+
requestModel(body, fallback) {
|
|
1931
|
+
return typeof body.model === "string" ? body.model : fallback;
|
|
1932
|
+
}
|
|
1494
1933
|
// ---------------------------------------------------------------------------
|
|
1495
1934
|
// Internal: message → input mapping
|
|
1496
1935
|
// ---------------------------------------------------------------------------
|
|
@@ -1558,7 +1997,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1558
1997
|
// ---------------------------------------------------------------------------
|
|
1559
1998
|
// Internal: parse non-streaming response
|
|
1560
1999
|
// ---------------------------------------------------------------------------
|
|
1561
|
-
parseResponse(json, model) {
|
|
2000
|
+
parseResponse(json, model, request) {
|
|
1562
2001
|
let content = "";
|
|
1563
2002
|
let thinkingContent = "";
|
|
1564
2003
|
const toolCalls = [];
|
|
@@ -1595,9 +2034,14 @@ var OpenAIResponsesProvider = class {
|
|
|
1595
2034
|
completion_tokens: json.usage.output_tokens,
|
|
1596
2035
|
total_tokens: json.usage.total_tokens,
|
|
1597
2036
|
reasoning_tokens: json.usage.output_tokens_details?.reasoning_tokens,
|
|
1598
|
-
cached_tokens: json.usage.input_tokens_details?.cached_tokens
|
|
2037
|
+
cached_tokens: json.usage.input_tokens_details?.cached_tokens,
|
|
2038
|
+
cache_write_tokens: json.usage.input_tokens_details?.cache_write_tokens
|
|
1599
2039
|
} : void 0;
|
|
1600
|
-
const cost = usage ?
|
|
2040
|
+
const cost = usage ? estimateDirectOpenAICost(json.model ?? model, usage, {
|
|
2041
|
+
baseUrl: this.baseUrl,
|
|
2042
|
+
request,
|
|
2043
|
+
response: json
|
|
2044
|
+
}) : void 0;
|
|
1601
2045
|
const providerMetadata = reasoningItems.length > 0 ? { openaiReasoningItems: reasoningItems } : void 0;
|
|
1602
2046
|
return {
|
|
1603
2047
|
content,
|
|
@@ -1611,7 +2055,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1611
2055
|
// ---------------------------------------------------------------------------
|
|
1612
2056
|
// Internal: parse SSE stream
|
|
1613
2057
|
// ---------------------------------------------------------------------------
|
|
1614
|
-
async *parseSSEStream(body, model) {
|
|
2058
|
+
async *parseSSEStream(body, model, request) {
|
|
1615
2059
|
const reader = body.getReader();
|
|
1616
2060
|
const decoder = new TextDecoder();
|
|
1617
2061
|
let buffer = "";
|
|
@@ -1639,7 +2083,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1639
2083
|
} catch {
|
|
1640
2084
|
continue;
|
|
1641
2085
|
}
|
|
1642
|
-
const chunk = this.handleStreamEvent(eventType, data, model, callIdMap);
|
|
2086
|
+
const chunk = this.handleStreamEvent(eventType, data, model, callIdMap, request);
|
|
1643
2087
|
if (chunk) {
|
|
1644
2088
|
yield chunk;
|
|
1645
2089
|
if (chunk.type === "done") return;
|
|
@@ -1653,7 +2097,7 @@ var OpenAIResponsesProvider = class {
|
|
|
1653
2097
|
reader.releaseLock();
|
|
1654
2098
|
}
|
|
1655
2099
|
}
|
|
1656
|
-
handleStreamEvent(eventType, data, model, callIdMap) {
|
|
2100
|
+
handleStreamEvent(eventType, data, model, callIdMap, request) {
|
|
1657
2101
|
switch (eventType) {
|
|
1658
2102
|
case "response.output_text.delta":
|
|
1659
2103
|
return { type: "text_delta", content: data.delta ?? "" };
|
|
@@ -1687,19 +2131,19 @@ var OpenAIResponsesProvider = class {
|
|
|
1687
2131
|
completion_tokens: response.usage.output_tokens,
|
|
1688
2132
|
total_tokens: response.usage.total_tokens,
|
|
1689
2133
|
reasoning_tokens: response.usage.output_tokens_details?.reasoning_tokens,
|
|
1690
|
-
cached_tokens: response.usage.input_tokens_details?.cached_tokens
|
|
2134
|
+
cached_tokens: response.usage.input_tokens_details?.cached_tokens,
|
|
2135
|
+
cache_write_tokens: response.usage.input_tokens_details?.cache_write_tokens
|
|
1691
2136
|
} : void 0;
|
|
1692
2137
|
const reasoningItems = response?.output?.filter((item) => item.type === "reasoning") ?? [];
|
|
1693
2138
|
const providerMetadata = reasoningItems.length > 0 ? { openaiReasoningItems: reasoningItems } : void 0;
|
|
1694
2139
|
return {
|
|
1695
2140
|
type: "done",
|
|
1696
2141
|
usage,
|
|
1697
|
-
cost: usage ?
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
) : void 0,
|
|
2142
|
+
cost: usage ? estimateDirectOpenAICost(response?.model ?? model, usage, {
|
|
2143
|
+
baseUrl: this.baseUrl,
|
|
2144
|
+
request,
|
|
2145
|
+
response
|
|
2146
|
+
}) : void 0,
|
|
1703
2147
|
providerMetadata
|
|
1704
2148
|
};
|
|
1705
2149
|
}
|
|
@@ -1733,68 +2177,357 @@ var OpenAIResponsesProvider = class {
|
|
|
1733
2177
|
|
|
1734
2178
|
// src/providers/anthropic.ts
|
|
1735
2179
|
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
|
-
|
|
2180
|
+
var CLAUDE_CAPABILITIES = {
|
|
2181
|
+
"claude-fable-5": {
|
|
2182
|
+
thinking: "adaptive-always-on",
|
|
2183
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
2184
|
+
manualBudget: false,
|
|
2185
|
+
stripTemperature: true
|
|
2186
|
+
},
|
|
2187
|
+
"claude-opus-5": {
|
|
2188
|
+
thinking: "adaptive-default-on",
|
|
2189
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
2190
|
+
manualBudget: false,
|
|
2191
|
+
stripTemperature: true,
|
|
2192
|
+
disableAt: "high"
|
|
2193
|
+
},
|
|
2194
|
+
"claude-sonnet-5": {
|
|
2195
|
+
thinking: "adaptive-default-on",
|
|
2196
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
2197
|
+
manualBudget: false,
|
|
2198
|
+
stripTemperature: true,
|
|
2199
|
+
disableAt: "max"
|
|
2200
|
+
},
|
|
2201
|
+
"claude-opus-4-8": {
|
|
2202
|
+
thinking: "adaptive-optional",
|
|
2203
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
2204
|
+
manualBudget: false,
|
|
2205
|
+
stripTemperature: true
|
|
2206
|
+
},
|
|
2207
|
+
"claude-opus-4-7": {
|
|
2208
|
+
thinking: "adaptive-optional",
|
|
2209
|
+
effortLevels: ["low", "medium", "high", "xhigh", "max"],
|
|
2210
|
+
manualBudget: false,
|
|
2211
|
+
stripTemperature: true
|
|
2212
|
+
},
|
|
2213
|
+
"claude-opus-4-6": {
|
|
2214
|
+
thinking: "adaptive-optional",
|
|
2215
|
+
effortLevels: ["low", "medium", "high", "max"],
|
|
2216
|
+
manualBudget: true
|
|
2217
|
+
},
|
|
2218
|
+
"claude-sonnet-4-6": {
|
|
2219
|
+
thinking: "adaptive-optional",
|
|
2220
|
+
effortLevels: ["low", "medium", "high", "max"],
|
|
2221
|
+
manualBudget: true
|
|
2222
|
+
},
|
|
2223
|
+
"claude-opus-4-5": {
|
|
2224
|
+
thinking: "effort-only",
|
|
2225
|
+
effortLevels: ["low", "medium", "high"],
|
|
2226
|
+
manualBudget: true
|
|
2227
|
+
},
|
|
2228
|
+
"claude-opus-4-5-20251101": {
|
|
2229
|
+
thinking: "effort-only",
|
|
2230
|
+
effortLevels: ["low", "medium", "high"],
|
|
2231
|
+
manualBudget: true
|
|
2232
|
+
}
|
|
1753
2233
|
};
|
|
1754
|
-
var
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
2234
|
+
var LEGACY_CLAUDE_CAPABILITY = {
|
|
2235
|
+
thinking: "legacy-manual",
|
|
2236
|
+
manualBudget: true
|
|
2237
|
+
};
|
|
2238
|
+
var LEGACY_CLAUDE_MODELS = /* @__PURE__ */ new Set([
|
|
2239
|
+
"claude-opus-4-1",
|
|
2240
|
+
"claude-opus-4-1-20250805",
|
|
2241
|
+
"claude-sonnet-4-5",
|
|
2242
|
+
"claude-sonnet-4-5-20250929",
|
|
2243
|
+
"claude-haiku-4-5",
|
|
2244
|
+
"claude-haiku-4-5-20251001",
|
|
2245
|
+
"claude-sonnet-4",
|
|
2246
|
+
"claude-sonnet-4-20250514",
|
|
2247
|
+
"claude-opus-4",
|
|
2248
|
+
"claude-opus-4-20250514",
|
|
2249
|
+
"claude-3-7-sonnet",
|
|
2250
|
+
"claude-3-7-sonnet-20250219",
|
|
2251
|
+
"claude-3-5-sonnet",
|
|
2252
|
+
"claude-3-5-sonnet-20241022",
|
|
2253
|
+
"claude-3-5-sonnet-20240620",
|
|
2254
|
+
"claude-3-5-haiku",
|
|
2255
|
+
"claude-3-5-haiku-20241022",
|
|
2256
|
+
"claude-3-opus",
|
|
2257
|
+
"claude-3-opus-20240229",
|
|
2258
|
+
"claude-3-sonnet",
|
|
2259
|
+
"claude-3-sonnet-20240229",
|
|
2260
|
+
"claude-3-haiku",
|
|
2261
|
+
"claude-3-haiku-20240307"
|
|
2262
|
+
]);
|
|
2263
|
+
var PRE_INFERENCE_GEO_MODELS = /* @__PURE__ */ new Set([
|
|
2264
|
+
...LEGACY_CLAUDE_MODELS,
|
|
2265
|
+
"claude-opus-4-5",
|
|
2266
|
+
"claude-opus-4-5-20251101"
|
|
2267
|
+
]);
|
|
2268
|
+
function resolveClaudeCapability(model) {
|
|
2269
|
+
return CLAUDE_CAPABILITIES[model] ?? (LEGACY_CLAUDE_MODELS.has(model) ? LEGACY_CLAUDE_CAPABILITY : void 0);
|
|
2270
|
+
}
|
|
2271
|
+
var ANTHROPIC_RATES = {
|
|
2272
|
+
"claude-fable-5": { input: 1e-5, output: 5e-5 },
|
|
2273
|
+
"claude-opus-5": { input: 5e-6, output: 25e-6 },
|
|
2274
|
+
"claude-sonnet-5": { input: 3e-6, output: 15e-6, sonnet5Intro: true },
|
|
2275
|
+
"claude-opus-4-8": { input: 5e-6, output: 25e-6 },
|
|
2276
|
+
"claude-opus-4-7": { input: 5e-6, output: 25e-6 },
|
|
2277
|
+
"claude-opus-4-6": { input: 5e-6, output: 25e-6 },
|
|
2278
|
+
"claude-sonnet-4-6": { input: 3e-6, output: 15e-6 },
|
|
2279
|
+
"claude-opus-4-5": { input: 5e-6, output: 25e-6 },
|
|
2280
|
+
"claude-opus-4-5-20251101": { input: 5e-6, output: 25e-6 },
|
|
2281
|
+
"claude-opus-4-1": { input: 15e-6, output: 75e-6 },
|
|
2282
|
+
"claude-opus-4-1-20250805": { input: 15e-6, output: 75e-6 },
|
|
2283
|
+
"claude-sonnet-4-5": { input: 3e-6, output: 15e-6 },
|
|
2284
|
+
"claude-sonnet-4-5-20250929": { input: 3e-6, output: 15e-6 },
|
|
2285
|
+
"claude-haiku-4-5": { input: 1e-6, output: 5e-6 },
|
|
2286
|
+
"claude-haiku-4-5-20251001": { input: 1e-6, output: 5e-6 }
|
|
2287
|
+
};
|
|
2288
|
+
function isValidTokenCount(value) {
|
|
2289
|
+
return value !== void 0 && Number.isSafeInteger(value) && value >= 0;
|
|
2290
|
+
}
|
|
2291
|
+
function estimateAnthropicCost(model, usage, now = /* @__PURE__ */ new Date()) {
|
|
2292
|
+
if (!model || !isValidTokenCount(usage.inputTokens) || !isValidTokenCount(usage.outputTokens)) {
|
|
2293
|
+
return void 0;
|
|
1766
2294
|
}
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
const cacheRead = cacheReadTokens ?? 0;
|
|
1770
|
-
const
|
|
1771
|
-
const
|
|
1772
|
-
|
|
2295
|
+
const rate = ANTHROPIC_RATES[model];
|
|
2296
|
+
if (!rate) return void 0;
|
|
2297
|
+
const cacheRead = usage.cacheReadTokens ?? 0;
|
|
2298
|
+
const cacheWrite5m = usage.cacheWrite5mTokens ?? 0;
|
|
2299
|
+
const cacheWrite1h = usage.cacheWrite1hTokens ?? 0;
|
|
2300
|
+
if (!isValidTokenCount(cacheRead) || !isValidTokenCount(cacheWrite5m) || !isValidTokenCount(cacheWrite1h)) {
|
|
2301
|
+
return void 0;
|
|
2302
|
+
}
|
|
2303
|
+
const cacheWriteTotal = safeTokenSum(cacheWrite5m, cacheWrite1h);
|
|
2304
|
+
if (cacheWriteTotal === void 0 || safeTokenSum(usage.inputTokens, cacheRead, cacheWriteTotal) === void 0) {
|
|
2305
|
+
return void 0;
|
|
2306
|
+
}
|
|
2307
|
+
if (usage.aggregateCacheWriteTokens !== void 0 && (!isValidTokenCount(usage.aggregateCacheWriteTokens) || usage.aggregateCacheWriteTokens !== cacheWriteTotal)) {
|
|
2308
|
+
return void 0;
|
|
2309
|
+
}
|
|
2310
|
+
const { input, output } = rate.sonnet5Intro && now < /* @__PURE__ */ new Date("2026-09-01T00:00:00Z") ? { input: 2e-6, output: 1e-5 } : rate;
|
|
2311
|
+
return usage.inputTokens * input + cacheRead * input * 0.1 + cacheWrite5m * input * 1.25 + cacheWrite1h * input * 2 + usage.outputTokens * output;
|
|
1773
2312
|
}
|
|
1774
2313
|
var THINKING_BUDGETS = {
|
|
1775
2314
|
low: 1024,
|
|
1776
2315
|
medium: 5e3,
|
|
1777
2316
|
high: 1e4,
|
|
2317
|
+
xhigh: 2e4,
|
|
1778
2318
|
// 30000 (not 32000) to stay under the 32K max_tokens limit on Opus 4/4.1.
|
|
1779
2319
|
// With auto-bump (+1024), max_tokens becomes 31024 which fits all models.
|
|
1780
2320
|
max: 3e4
|
|
1781
2321
|
};
|
|
1782
|
-
function
|
|
1783
|
-
|
|
2322
|
+
function clampAnthropicEffort(capability, effort) {
|
|
2323
|
+
const levels = capability.effortLevels;
|
|
2324
|
+
if (!levels) return effort === "max" || effort === "xhigh" ? "high" : effort;
|
|
2325
|
+
if (levels.includes(effort)) return effort;
|
|
2326
|
+
return "high";
|
|
1784
2327
|
}
|
|
1785
|
-
function
|
|
1786
|
-
|
|
2328
|
+
function budgetToClaudeEffort(budget) {
|
|
2329
|
+
if (budget <= THINKING_BUDGETS.low) return "low";
|
|
2330
|
+
if (budget <= THINKING_BUDGETS.medium) return "medium";
|
|
2331
|
+
if (budget <= THINKING_BUDGETS.high) return "high";
|
|
2332
|
+
if (budget <= THINKING_BUDGETS.xhigh) return "xhigh";
|
|
2333
|
+
return "max";
|
|
2334
|
+
}
|
|
2335
|
+
var warnedFableDisable = /* @__PURE__ */ new Set();
|
|
2336
|
+
function warnFableDisable(model) {
|
|
2337
|
+
if (warnedFableDisable.has(model)) return;
|
|
2338
|
+
warnedFableDisable.add(model);
|
|
2339
|
+
console.warn(
|
|
2340
|
+
`[axl] thinking cannot be disabled on Anthropic ${model}; using adaptive thinking at effort 'low'.`
|
|
2341
|
+
);
|
|
1787
2342
|
}
|
|
1788
|
-
function
|
|
1789
|
-
|
|
2343
|
+
function resolveClaudeThinking(model, resolved) {
|
|
2344
|
+
const capability = resolveClaudeCapability(model);
|
|
2345
|
+
if (!capability) return { stripTemperature: false };
|
|
2346
|
+
const activeEffort = resolved.activeEffort ? clampAnthropicEffort(capability, resolved.activeEffort) : void 0;
|
|
2347
|
+
const isClaude5 = capability.thinking === "adaptive-default-on" || capability.thinking === "adaptive-always-on";
|
|
2348
|
+
if (isClaude5) {
|
|
2349
|
+
if (resolved.hasBudgetOverride) {
|
|
2350
|
+
const effort = clampAnthropicEffort(
|
|
2351
|
+
capability,
|
|
2352
|
+
budgetToClaudeEffort(resolved.thinkingBudget)
|
|
2353
|
+
);
|
|
2354
|
+
return {
|
|
2355
|
+
thinking: { type: "adaptive" },
|
|
2356
|
+
outputConfig: { effort },
|
|
2357
|
+
stripTemperature: true
|
|
2358
|
+
};
|
|
2359
|
+
}
|
|
2360
|
+
if (resolved.thinkingDisabled) {
|
|
2361
|
+
if (capability.thinking === "adaptive-always-on") {
|
|
2362
|
+
warnFableDisable(model);
|
|
2363
|
+
return {
|
|
2364
|
+
thinking: { type: "adaptive" },
|
|
2365
|
+
outputConfig: { effort: "low" },
|
|
2366
|
+
stripTemperature: true
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
if (activeEffort && capability.disableAt && !isEffortAtOrBelow(activeEffort, capability.disableAt)) {
|
|
2370
|
+
return {
|
|
2371
|
+
thinking: { type: "adaptive" },
|
|
2372
|
+
outputConfig: { effort: activeEffort },
|
|
2373
|
+
stripTemperature: true
|
|
2374
|
+
};
|
|
2375
|
+
}
|
|
2376
|
+
return {
|
|
2377
|
+
thinking: { type: "disabled" },
|
|
2378
|
+
outputConfig: activeEffort ? { effort: activeEffort } : void 0,
|
|
2379
|
+
stripTemperature: true
|
|
2380
|
+
};
|
|
2381
|
+
}
|
|
2382
|
+
return activeEffort ? {
|
|
2383
|
+
thinking: { type: "adaptive" },
|
|
2384
|
+
outputConfig: { effort: activeEffort },
|
|
2385
|
+
stripTemperature: true
|
|
2386
|
+
} : { stripTemperature: true };
|
|
2387
|
+
}
|
|
2388
|
+
if (resolved.hasBudgetOverride) {
|
|
2389
|
+
if (!capability.manualBudget) {
|
|
2390
|
+
const effort = clampAnthropicEffort(
|
|
2391
|
+
capability,
|
|
2392
|
+
budgetToClaudeEffort(resolved.thinkingBudget)
|
|
2393
|
+
);
|
|
2394
|
+
return capability.thinking === "adaptive-optional" ? {
|
|
2395
|
+
thinking: { type: "adaptive" },
|
|
2396
|
+
outputConfig: { effort },
|
|
2397
|
+
stripTemperature: capability.stripTemperature ?? true
|
|
2398
|
+
} : { outputConfig: { effort }, stripTemperature: capability.stripTemperature ?? false };
|
|
2399
|
+
}
|
|
2400
|
+
return {
|
|
2401
|
+
thinking: { type: "enabled", budget_tokens: resolved.thinkingBudget },
|
|
2402
|
+
outputConfig: activeEffort && capability.effortLevels ? { effort: activeEffort } : void 0,
|
|
2403
|
+
manualBudget: resolved.thinkingBudget,
|
|
2404
|
+
stripTemperature: capability.stripTemperature ?? true
|
|
2405
|
+
};
|
|
2406
|
+
}
|
|
2407
|
+
if (resolved.thinkingDisabled) {
|
|
2408
|
+
return {
|
|
2409
|
+
outputConfig: activeEffort && capability.effortLevels ? { effort: activeEffort } : void 0,
|
|
2410
|
+
stripTemperature: capability.stripTemperature ?? false
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
if (activeEffort && capability.thinking === "adaptive-optional") {
|
|
2414
|
+
return {
|
|
2415
|
+
thinking: { type: "adaptive" },
|
|
2416
|
+
outputConfig: { effort: activeEffort },
|
|
2417
|
+
stripTemperature: capability.stripTemperature ?? true
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
if (activeEffort && capability.thinking === "effort-only") {
|
|
2421
|
+
return { outputConfig: { effort: activeEffort }, stripTemperature: false };
|
|
2422
|
+
}
|
|
2423
|
+
if (activeEffort) {
|
|
2424
|
+
const budget = THINKING_BUDGETS[activeEffort] ?? THINKING_BUDGETS.medium;
|
|
2425
|
+
return {
|
|
2426
|
+
thinking: { type: "enabled", budget_tokens: budget },
|
|
2427
|
+
manualBudget: budget,
|
|
2428
|
+
stripTemperature: true
|
|
2429
|
+
};
|
|
2430
|
+
}
|
|
2431
|
+
return { stripTemperature: capability.stripTemperature ?? false };
|
|
1790
2432
|
}
|
|
1791
|
-
function
|
|
1792
|
-
|
|
2433
|
+
function isEffortAtOrBelow(effort, ceiling) {
|
|
2434
|
+
const levels = ["low", "medium", "high", "xhigh", "max"];
|
|
2435
|
+
return levels.indexOf(effort) <= levels.indexOf(ceiling);
|
|
1793
2436
|
}
|
|
1794
|
-
function
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
2437
|
+
function pricingContextFromBody(body) {
|
|
2438
|
+
return {
|
|
2439
|
+
model: typeof body.model === "string" ? body.model : void 0,
|
|
2440
|
+
unpricedModifier: isAnthropicModifier("inference_geo", body.inference_geo) || isAnthropicModifier("speed", body.speed) || hasAnthropicServerTool(body.tools) || body.fallbacks !== void 0
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
function isModifiedAnthropicResponse(json, model) {
|
|
2444
|
+
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);
|
|
2445
|
+
}
|
|
2446
|
+
function isAnthropicModifier(kind, value) {
|
|
2447
|
+
if (value === void 0) return false;
|
|
2448
|
+
return kind === "speed" ? value !== "standard" : value !== "global";
|
|
2449
|
+
}
|
|
2450
|
+
function isAnthropicResponseModifier(kind, value, model) {
|
|
2451
|
+
if (value === void 0) return false;
|
|
2452
|
+
if (kind === "speed") return value !== "standard";
|
|
2453
|
+
if (value === "global") return false;
|
|
2454
|
+
return value !== "not_available" || model === void 0 || !PRE_INFERENCE_GEO_MODELS.has(model);
|
|
2455
|
+
}
|
|
2456
|
+
function hasAnthropicServerTool(tools) {
|
|
2457
|
+
return Array.isArray(tools) && tools.some(
|
|
2458
|
+
(tool2) => tool2 !== null && typeof tool2 === "object" && typeof tool2.type === "string"
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2461
|
+
function hasBilledUnmodeledUsage(value) {
|
|
2462
|
+
return value !== void 0 && value !== 0;
|
|
2463
|
+
}
|
|
2464
|
+
function hasUnmodeledIterations(value) {
|
|
2465
|
+
return value !== void 0;
|
|
2466
|
+
}
|
|
2467
|
+
function hasFallbackIteration(value) {
|
|
2468
|
+
return Array.isArray(value) && value.some(
|
|
2469
|
+
(iteration) => iteration !== null && typeof iteration === "object" && iteration.type === "fallback_message"
|
|
2470
|
+
);
|
|
2471
|
+
}
|
|
2472
|
+
function safeTokenSum(...values) {
|
|
2473
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
2474
|
+
return Number.isSafeInteger(total) ? total : void 0;
|
|
2475
|
+
}
|
|
2476
|
+
function normalizeAnthropicUsage(raw) {
|
|
2477
|
+
const inputTokens = raw.input_tokens;
|
|
2478
|
+
const outputTokens = raw.output_tokens;
|
|
2479
|
+
const cacheReadTokens = raw.cache_read_input_tokens ?? 0;
|
|
2480
|
+
const cacheWrite5mTokens = raw.cache_creation?.ephemeral_5m_input_tokens ?? 0;
|
|
2481
|
+
const cacheWrite1hTokens = raw.cache_creation?.ephemeral_1h_input_tokens ?? 0;
|
|
2482
|
+
if (!isValidTokenCount(inputTokens) || !isValidTokenCount(outputTokens) || !isValidTokenCount(cacheReadTokens) || !isValidTokenCount(cacheWrite5mTokens) || !isValidTokenCount(cacheWrite1hTokens)) {
|
|
2483
|
+
return void 0;
|
|
2484
|
+
}
|
|
2485
|
+
const hasTtlBreakdown = raw.cache_creation?.ephemeral_5m_input_tokens !== void 0 || raw.cache_creation?.ephemeral_1h_input_tokens !== void 0;
|
|
2486
|
+
const aggregateCacheWriteTokens = raw.cache_creation_input_tokens;
|
|
2487
|
+
const normalizedCacheWrite = hasTtlBreakdown ? safeTokenSum(cacheWrite5mTokens, cacheWrite1hTokens) : void 0;
|
|
2488
|
+
if (hasTtlBreakdown && normalizedCacheWrite === void 0) return void 0;
|
|
2489
|
+
const promptCacheWrite = aggregateCacheWriteTokens ?? normalizedCacheWrite ?? 0;
|
|
2490
|
+
if (!isValidTokenCount(promptCacheWrite)) return void 0;
|
|
2491
|
+
const promptTokens = safeTokenSum(inputTokens, cacheReadTokens, promptCacheWrite);
|
|
2492
|
+
if (promptTokens === void 0) return void 0;
|
|
2493
|
+
const totalTokens = safeTokenSum(promptTokens, outputTokens);
|
|
2494
|
+
if (totalTokens === void 0) return void 0;
|
|
2495
|
+
return {
|
|
2496
|
+
usage: {
|
|
2497
|
+
prompt_tokens: promptTokens,
|
|
2498
|
+
completion_tokens: outputTokens,
|
|
2499
|
+
total_tokens: totalTokens,
|
|
2500
|
+
cached_tokens: cacheReadTokens > 0 ? cacheReadTokens : void 0,
|
|
2501
|
+
// The aggregate is still useful public usage telemetry even though it
|
|
2502
|
+
// cannot be priced without the 5m/1h split. Keep pricingUsage separate
|
|
2503
|
+
// so estimateAnthropicCost continues to fail closed in that case.
|
|
2504
|
+
cache_write_tokens: (normalizedCacheWrite ?? aggregateCacheWriteTokens ?? 0) > 0 ? normalizedCacheWrite ?? aggregateCacheWriteTokens : void 0
|
|
2505
|
+
},
|
|
2506
|
+
pricingUsage: {
|
|
2507
|
+
inputTokens,
|
|
2508
|
+
outputTokens,
|
|
2509
|
+
cacheReadTokens,
|
|
2510
|
+
cacheWrite5mTokens,
|
|
2511
|
+
cacheWrite1hTokens,
|
|
2512
|
+
aggregateCacheWriteTokens
|
|
2513
|
+
}
|
|
2514
|
+
};
|
|
2515
|
+
}
|
|
2516
|
+
function mergeAnthropicUsage(initial, update) {
|
|
2517
|
+
return {
|
|
2518
|
+
input_tokens: update.input_tokens ?? initial?.input_tokens,
|
|
2519
|
+
output_tokens: update.output_tokens ?? initial?.output_tokens,
|
|
2520
|
+
cache_read_input_tokens: update.cache_read_input_tokens ?? initial?.cache_read_input_tokens,
|
|
2521
|
+
cache_creation_input_tokens: update.cache_creation_input_tokens ?? initial?.cache_creation_input_tokens,
|
|
2522
|
+
cache_creation: {
|
|
2523
|
+
ephemeral_5m_input_tokens: update.cache_creation?.ephemeral_5m_input_tokens ?? initial?.cache_creation?.ephemeral_5m_input_tokens,
|
|
2524
|
+
ephemeral_1h_input_tokens: update.cache_creation?.ephemeral_1h_input_tokens ?? initial?.cache_creation?.ephemeral_1h_input_tokens
|
|
2525
|
+
},
|
|
2526
|
+
speed: update.speed ?? initial?.speed,
|
|
2527
|
+
inference_geo: update.inference_geo ?? initial?.inference_geo,
|
|
2528
|
+
iterations: update.iterations ?? initial?.iterations,
|
|
2529
|
+
server_tool_use: update.server_tool_use ?? initial?.server_tool_use
|
|
2530
|
+
};
|
|
1798
2531
|
}
|
|
1799
2532
|
var AnthropicProvider = class {
|
|
1800
2533
|
name = "anthropic";
|
|
@@ -1809,6 +2542,11 @@ var AnthropicProvider = class {
|
|
|
1809
2542
|
constructor(options = {}) {
|
|
1810
2543
|
this.apiKeySource = options.apiKey ?? process.env.ANTHROPIC_API_KEY ?? "";
|
|
1811
2544
|
this.baseUrl = (options.baseUrl ?? "https://api.anthropic.com/v1").replace(/\/$/, "");
|
|
2545
|
+
assertSafeProviderBaseUrl(
|
|
2546
|
+
this.baseUrl,
|
|
2547
|
+
"Anthropic provider",
|
|
2548
|
+
options.dangerouslyAllowInsecureHttp
|
|
2549
|
+
);
|
|
1812
2550
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
1813
2551
|
if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
|
|
1814
2552
|
throw new Error(
|
|
@@ -1832,6 +2570,7 @@ var AnthropicProvider = class {
|
|
|
1832
2570
|
async chat(messages, options) {
|
|
1833
2571
|
const headers = this.buildHeaders(await this.resolveKey());
|
|
1834
2572
|
const body = this.buildRequestBody(messages, options, false);
|
|
2573
|
+
const pricingContext = pricingContextFromBody(body);
|
|
1835
2574
|
const res = await fetchWithRetry(
|
|
1836
2575
|
`${this.baseUrl}/messages`,
|
|
1837
2576
|
{
|
|
@@ -1854,7 +2593,7 @@ var AnthropicProvider = class {
|
|
|
1854
2593
|
});
|
|
1855
2594
|
}
|
|
1856
2595
|
const json = await res.json();
|
|
1857
|
-
return this.parseResponse(json,
|
|
2596
|
+
return this.parseResponse(json, pricingContext);
|
|
1858
2597
|
}
|
|
1859
2598
|
// ---------------------------------------------------------------------------
|
|
1860
2599
|
// stream - SSE streaming completion
|
|
@@ -1862,6 +2601,7 @@ var AnthropicProvider = class {
|
|
|
1862
2601
|
async *stream(messages, options) {
|
|
1863
2602
|
const headers = this.buildHeaders(await this.resolveKey());
|
|
1864
2603
|
const body = this.buildRequestBody(messages, options, true);
|
|
2604
|
+
const pricingContext = pricingContextFromBody(body);
|
|
1865
2605
|
const res = await fetchWithRetry(
|
|
1866
2606
|
`${this.baseUrl}/messages`,
|
|
1867
2607
|
{
|
|
@@ -1886,7 +2626,7 @@ var AnthropicProvider = class {
|
|
|
1886
2626
|
if (!res.body) {
|
|
1887
2627
|
throw new Error("Anthropic stream response has no body");
|
|
1888
2628
|
}
|
|
1889
|
-
yield* this.parseSSEStream(res.body,
|
|
2629
|
+
yield* this.parseSSEStream(res.body, pricingContext);
|
|
1890
2630
|
}
|
|
1891
2631
|
// ---------------------------------------------------------------------------
|
|
1892
2632
|
// Internal: request building
|
|
@@ -1910,11 +2650,12 @@ var AnthropicProvider = class {
|
|
|
1910
2650
|
return `Anthropic API error (${status}): ${body}`;
|
|
1911
2651
|
}
|
|
1912
2652
|
buildRequestBody(messages, options, stream) {
|
|
2653
|
+
const effectiveModel = typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
1913
2654
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
1914
2655
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
1915
2656
|
const systemText = systemMessages.map((m) => m.content).join("\n\n");
|
|
1916
2657
|
const body = {
|
|
1917
|
-
model:
|
|
2658
|
+
model: effectiveModel,
|
|
1918
2659
|
messages: this.mapMessages(nonSystemMessages),
|
|
1919
2660
|
max_tokens: options.maxTokens ?? 4096,
|
|
1920
2661
|
stream
|
|
@@ -1931,35 +2672,16 @@ var AnthropicProvider = class {
|
|
|
1931
2672
|
if (options.toolChoice !== void 0) {
|
|
1932
2673
|
body.tool_choice = this.mapToolChoice(options.toolChoice);
|
|
1933
2674
|
}
|
|
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 };
|
|
2675
|
+
const thinking = resolveClaudeThinking(effectiveModel, resolveThinkingOptions(options));
|
|
2676
|
+
if (thinking.thinking) body.thinking = thinking.thinking;
|
|
2677
|
+
if (thinking.outputConfig) body.output_config = thinking.outputConfig;
|
|
2678
|
+
if (thinking.manualBudget) {
|
|
1957
2679
|
const currentMax = body.max_tokens;
|
|
1958
|
-
if (currentMax <
|
|
1959
|
-
body.max_tokens =
|
|
2680
|
+
if (currentMax < thinking.manualBudget + 1024) {
|
|
2681
|
+
body.max_tokens = thinking.manualBudget + 1024;
|
|
1960
2682
|
}
|
|
1961
2683
|
}
|
|
1962
|
-
if (options.temperature !== void 0 && !
|
|
2684
|
+
if (options.temperature !== void 0 && !thinking.stripTemperature) {
|
|
1963
2685
|
body.temperature = options.temperature;
|
|
1964
2686
|
}
|
|
1965
2687
|
if (options.responseFormat && options.responseFormat.type !== "text") {
|
|
@@ -1984,8 +2706,9 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
1984
2706
|
const result = [];
|
|
1985
2707
|
for (const msg of messages) {
|
|
1986
2708
|
if (msg.role === "assistant") {
|
|
2709
|
+
const replayedThinking = this.getReplayedThinkingBlocks(msg);
|
|
1987
2710
|
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
1988
|
-
const content = [];
|
|
2711
|
+
const content = [...replayedThinking];
|
|
1989
2712
|
if (msg.content) {
|
|
1990
2713
|
content.push({ type: "text", text: msg.content });
|
|
1991
2714
|
}
|
|
@@ -2004,6 +2727,10 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2004
2727
|
});
|
|
2005
2728
|
}
|
|
2006
2729
|
result.push({ role: "assistant", content });
|
|
2730
|
+
} else if (replayedThinking.length > 0) {
|
|
2731
|
+
const content = [...replayedThinking];
|
|
2732
|
+
if (msg.content) content.push({ type: "text", text: msg.content });
|
|
2733
|
+
result.push({ role: "assistant", content });
|
|
2007
2734
|
} else {
|
|
2008
2735
|
result.push({ role: "assistant", content: msg.content });
|
|
2009
2736
|
}
|
|
@@ -2024,6 +2751,13 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2024
2751
|
}
|
|
2025
2752
|
return this.mergeConsecutiveRoles(result);
|
|
2026
2753
|
}
|
|
2754
|
+
getReplayedThinkingBlocks(msg) {
|
|
2755
|
+
const blocks = msg.providerMetadata?.anthropicThinkingBlocks;
|
|
2756
|
+
if (!Array.isArray(blocks)) return [];
|
|
2757
|
+
return blocks.filter(
|
|
2758
|
+
(block) => block !== null && typeof block === "object" && (block.type === "thinking" || block.type === "redacted_thinking")
|
|
2759
|
+
);
|
|
2760
|
+
}
|
|
2027
2761
|
/**
|
|
2028
2762
|
* Merge consecutive messages with the same role into a single message.
|
|
2029
2763
|
* This handles cases where multiple tool_result blocks need to be in one user message.
|
|
@@ -2079,16 +2813,24 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2079
2813
|
// ---------------------------------------------------------------------------
|
|
2080
2814
|
// Internal: response parsing
|
|
2081
2815
|
// ---------------------------------------------------------------------------
|
|
2082
|
-
parseResponse(json,
|
|
2816
|
+
parseResponse(json, pricingContext) {
|
|
2083
2817
|
let content = "";
|
|
2084
2818
|
let thinkingContent = "";
|
|
2085
2819
|
const toolCalls = [];
|
|
2086
|
-
|
|
2820
|
+
const thinkingBlocks = [];
|
|
2821
|
+
const fallbackIndex = lastFallbackIndex(json.content);
|
|
2822
|
+
const hasFallbackBoundary = fallbackIndex >= 0 || hasFallbackIteration(json.usage?.iterations);
|
|
2823
|
+
let refused = json.stop_reason === "refusal";
|
|
2824
|
+
for (const [index, block] of json.content.entries()) {
|
|
2087
2825
|
if (block.type === "thinking") {
|
|
2088
2826
|
thinkingContent += block.thinking;
|
|
2827
|
+
thinkingBlocks.push(block);
|
|
2828
|
+
} else if (block.type === "redacted_thinking") {
|
|
2829
|
+
thinkingBlocks.push(block);
|
|
2089
2830
|
} else if (block.type === "text") {
|
|
2090
2831
|
content += block.text;
|
|
2091
2832
|
} else if (block.type === "tool_use") {
|
|
2833
|
+
if (fallbackIndex >= 0 && index < fallbackIndex) continue;
|
|
2092
2834
|
toolCalls.push({
|
|
2093
2835
|
id: block.id,
|
|
2094
2836
|
type: "function",
|
|
@@ -2097,37 +2839,58 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2097
2839
|
arguments: JSON.stringify(block.input)
|
|
2098
2840
|
}
|
|
2099
2841
|
});
|
|
2842
|
+
} else if (block.type === "fallback") {
|
|
2843
|
+
} else if (block.type === "refusal") {
|
|
2844
|
+
refused = true;
|
|
2100
2845
|
}
|
|
2101
2846
|
}
|
|
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;
|
|
2847
|
+
const normalized = json.usage ? normalizeAnthropicUsage(json.usage) : void 0;
|
|
2848
|
+
const effectiveModel = typeof json.model === "string" ? json.model : pricingContext.model;
|
|
2849
|
+
if (hasFallbackBoundary) {
|
|
2850
|
+
throw new Error(
|
|
2851
|
+
"Anthropic fallback responses cannot be continued safely; retry without fallbacks."
|
|
2852
|
+
);
|
|
2853
|
+
}
|
|
2854
|
+
const cost = normalized && !pricingContext.unpricedModifier && !isModifiedAnthropicResponse(json, effectiveModel) && !hasFallbackBoundary && !refused ? estimateAnthropicCost(effectiveModel, normalized.pricingUsage) : void 0;
|
|
2112
2855
|
return {
|
|
2113
2856
|
content,
|
|
2114
2857
|
thinking_content: thinkingContent || void 0,
|
|
2115
2858
|
tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
2116
|
-
usage,
|
|
2117
|
-
cost
|
|
2859
|
+
usage: normalized?.usage,
|
|
2860
|
+
cost,
|
|
2861
|
+
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
|
|
2118
2862
|
};
|
|
2119
2863
|
}
|
|
2120
2864
|
// ---------------------------------------------------------------------------
|
|
2121
2865
|
// Internal: SSE stream parsing
|
|
2122
2866
|
// ---------------------------------------------------------------------------
|
|
2123
|
-
async *parseSSEStream(body,
|
|
2867
|
+
async *parseSSEStream(body, pricingContext) {
|
|
2124
2868
|
const reader = body.getReader();
|
|
2125
2869
|
const decoder = new TextDecoder();
|
|
2126
2870
|
let buffer = "";
|
|
2127
2871
|
let currentToolId = "";
|
|
2128
2872
|
let currentToolName = "";
|
|
2129
2873
|
let usage;
|
|
2130
|
-
let
|
|
2874
|
+
let pricingUsage;
|
|
2875
|
+
let rawUsage;
|
|
2876
|
+
const thinkingBlocks = [];
|
|
2877
|
+
let currentThinkingBlock;
|
|
2878
|
+
let effectiveModel = pricingContext.model;
|
|
2879
|
+
let unpricedModifier = pricingContext.unpricedModifier;
|
|
2880
|
+
let hasFallbackBoundary = false;
|
|
2881
|
+
let hasFallbackIterationSignal = false;
|
|
2882
|
+
let refused = false;
|
|
2883
|
+
const finalizeUsage = () => {
|
|
2884
|
+
const normalized = rawUsage ? normalizeAnthropicUsage(rawUsage) : void 0;
|
|
2885
|
+
usage = normalized?.usage;
|
|
2886
|
+
pricingUsage = normalized?.pricingUsage;
|
|
2887
|
+
};
|
|
2888
|
+
const doneChunk = () => ({
|
|
2889
|
+
type: "done",
|
|
2890
|
+
usage,
|
|
2891
|
+
cost: pricingUsage && !unpricedModifier && !hasFallbackBoundary && !refused ? estimateAnthropicCost(effectiveModel, pricingUsage) : void 0,
|
|
2892
|
+
providerMetadata: thinkingBlocks.length > 0 && !hasFallbackBoundary && !hasFallbackIterationSignal ? { anthropicThinkingBlocks: thinkingBlocks } : void 0
|
|
2893
|
+
});
|
|
2131
2894
|
try {
|
|
2132
2895
|
while (true) {
|
|
2133
2896
|
const { done, value } = await reader.read();
|
|
@@ -2158,13 +2921,31 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2158
2921
|
name: currentToolName,
|
|
2159
2922
|
arguments: ""
|
|
2160
2923
|
};
|
|
2924
|
+
} else if (block?.type === "thinking" || block?.type === "redacted_thinking") {
|
|
2925
|
+
currentThinkingBlock = {
|
|
2926
|
+
...block,
|
|
2927
|
+
type: block.type
|
|
2928
|
+
};
|
|
2929
|
+
} else if (block?.type === "fallback") {
|
|
2930
|
+
hasFallbackBoundary = true;
|
|
2931
|
+
} else if (block?.type === "refusal") {
|
|
2932
|
+
refused = true;
|
|
2161
2933
|
}
|
|
2162
2934
|
break;
|
|
2163
2935
|
}
|
|
2164
2936
|
case "content_block_delta": {
|
|
2165
2937
|
const delta = event.delta;
|
|
2166
2938
|
if (delta?.type === "thinking_delta" && delta.thinking) {
|
|
2939
|
+
if (currentThinkingBlock?.type === "thinking") {
|
|
2940
|
+
const current = currentThinkingBlock.thinking;
|
|
2941
|
+
currentThinkingBlock.thinking = `${typeof current === "string" ? current : ""}${delta.thinking}`;
|
|
2942
|
+
}
|
|
2167
2943
|
yield { type: "thinking_delta", content: delta.thinking };
|
|
2944
|
+
} else if (delta?.type === "signature_delta" && delta.signature) {
|
|
2945
|
+
if (currentThinkingBlock) {
|
|
2946
|
+
const current = currentThinkingBlock.signature;
|
|
2947
|
+
currentThinkingBlock.signature = `${typeof current === "string" ? current : ""}${delta.signature}`;
|
|
2948
|
+
}
|
|
2168
2949
|
} else if (delta?.type === "text_delta" && delta.text) {
|
|
2169
2950
|
yield { type: "text_delta", content: delta.text };
|
|
2170
2951
|
} else if (delta?.type === "input_json_delta" && delta.partial_json) {
|
|
@@ -2177,76 +2958,82 @@ ${jsonInstruction}` : jsonInstruction;
|
|
|
2177
2958
|
break;
|
|
2178
2959
|
}
|
|
2179
2960
|
case "content_block_stop": {
|
|
2961
|
+
if (currentThinkingBlock) {
|
|
2962
|
+
thinkingBlocks.push(currentThinkingBlock);
|
|
2963
|
+
currentThinkingBlock = void 0;
|
|
2964
|
+
}
|
|
2180
2965
|
currentToolId = "";
|
|
2181
2966
|
currentToolName = "";
|
|
2182
2967
|
break;
|
|
2183
2968
|
}
|
|
2184
2969
|
case "message_start": {
|
|
2185
2970
|
if (event.message?.usage) {
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2971
|
+
rawUsage = mergeAnthropicUsage(rawUsage, event.message.usage);
|
|
2972
|
+
}
|
|
2973
|
+
if (typeof event.message?.model === "string") effectiveModel = event.message.model;
|
|
2974
|
+
const messageFallback = hasFallbackIteration(event.message?.usage?.iterations);
|
|
2975
|
+
if (isAnthropicResponseModifier(
|
|
2976
|
+
"inference_geo",
|
|
2977
|
+
event.message?.inference_geo,
|
|
2978
|
+
effectiveModel
|
|
2979
|
+
) || isAnthropicResponseModifier("speed", event.message?.speed, effectiveModel) || isAnthropicResponseModifier(
|
|
2980
|
+
"inference_geo",
|
|
2981
|
+
event.message?.usage?.inference_geo,
|
|
2982
|
+
effectiveModel
|
|
2983
|
+
) || isAnthropicResponseModifier("speed", event.message?.usage?.speed, effectiveModel) || hasBilledUnmodeledUsage(event.message?.usage?.server_tool_use) || hasUnmodeledIterations(event.message?.usage?.iterations)) {
|
|
2984
|
+
unpricedModifier = true;
|
|
2195
2985
|
}
|
|
2986
|
+
if (messageFallback) hasFallbackIterationSignal = true;
|
|
2196
2987
|
break;
|
|
2197
2988
|
}
|
|
2198
2989
|
case "message_delta": {
|
|
2199
2990
|
if (event.usage) {
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
usage
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
};
|
|
2991
|
+
rawUsage = mergeAnthropicUsage(rawUsage, event.usage);
|
|
2992
|
+
finalizeUsage();
|
|
2993
|
+
const terminalFallback = hasFallbackIteration(event.usage.iterations);
|
|
2994
|
+
if (isAnthropicResponseModifier("speed", event.usage.speed, effectiveModel) || isAnthropicResponseModifier(
|
|
2995
|
+
"inference_geo",
|
|
2996
|
+
event.usage.inference_geo,
|
|
2997
|
+
effectiveModel
|
|
2998
|
+
) || hasBilledUnmodeledUsage(event.usage.server_tool_use) || hasUnmodeledIterations(event.usage.iterations)) {
|
|
2999
|
+
unpricedModifier = true;
|
|
2210
3000
|
}
|
|
3001
|
+
if (terminalFallback) hasFallbackIterationSignal = true;
|
|
2211
3002
|
}
|
|
3003
|
+
if (event.delta?.stop_reason === "refusal") refused = true;
|
|
2212
3004
|
break;
|
|
2213
3005
|
}
|
|
2214
3006
|
case "message_stop": {
|
|
2215
|
-
|
|
2216
|
-
|
|
3007
|
+
finalizeUsage();
|
|
3008
|
+
if (hasFallbackBoundary || hasFallbackIterationSignal) {
|
|
3009
|
+
throw new Error(
|
|
3010
|
+
"Anthropic fallback streams cannot be continued safely; retry without fallbacks."
|
|
3011
|
+
);
|
|
2217
3012
|
}
|
|
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
|
-
};
|
|
3013
|
+
yield doneChunk();
|
|
2229
3014
|
return;
|
|
2230
3015
|
}
|
|
2231
3016
|
}
|
|
2232
3017
|
}
|
|
2233
3018
|
}
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
usage.cached_tokens,
|
|
2242
|
-
cacheWrite
|
|
2243
|
-
) : void 0
|
|
2244
|
-
};
|
|
3019
|
+
finalizeUsage();
|
|
3020
|
+
if (hasFallbackBoundary || hasFallbackIterationSignal) {
|
|
3021
|
+
throw new Error(
|
|
3022
|
+
"Anthropic fallback streams cannot be continued safely; retry without fallbacks."
|
|
3023
|
+
);
|
|
3024
|
+
}
|
|
3025
|
+
yield doneChunk();
|
|
2245
3026
|
} finally {
|
|
2246
3027
|
reader.releaseLock();
|
|
2247
3028
|
}
|
|
2248
3029
|
}
|
|
2249
3030
|
};
|
|
3031
|
+
function lastFallbackIndex(blocks) {
|
|
3032
|
+
for (let index = blocks.length - 1; index >= 0; index--) {
|
|
3033
|
+
if (blocks[index].type === "fallback") return index;
|
|
3034
|
+
}
|
|
3035
|
+
return -1;
|
|
3036
|
+
}
|
|
2250
3037
|
|
|
2251
3038
|
// src/providers/gemini.ts
|
|
2252
3039
|
var GEMINI_DISALLOWED_SCHEMA_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -2294,36 +3081,92 @@ function parseGeminiFunctionResponse(content) {
|
|
|
2294
3081
|
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : { result: parsed };
|
|
2295
3082
|
}
|
|
2296
3083
|
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
|
-
|
|
3084
|
+
"gemini-2.5-pro": {
|
|
3085
|
+
input: 125e-8,
|
|
3086
|
+
cached: 125e-9,
|
|
3087
|
+
output: 1e-5,
|
|
3088
|
+
longContext: { input: 25e-7, cached: 25e-8, output: 15e-6 }
|
|
3089
|
+
},
|
|
3090
|
+
"gemini-2.5-flash": { input: 3e-7, cached: 3e-8, output: 25e-7 },
|
|
3091
|
+
"gemini-2.5-flash-lite": { input: 1e-7, cached: 1e-8, output: 4e-7 },
|
|
3092
|
+
"gemini-3-flash-preview": { input: 5e-7, cached: 5e-8, output: 3e-6 },
|
|
3093
|
+
"gemini-3.1-pro-preview": {
|
|
3094
|
+
input: 2e-6,
|
|
3095
|
+
cached: 2e-7,
|
|
3096
|
+
output: 12e-6,
|
|
3097
|
+
longContext: { input: 4e-6, cached: 4e-7, output: 18e-6 }
|
|
3098
|
+
},
|
|
3099
|
+
"gemini-3.1-pro-preview-customtools": {
|
|
3100
|
+
input: 2e-6,
|
|
3101
|
+
cached: 2e-7,
|
|
3102
|
+
output: 12e-6,
|
|
3103
|
+
longContext: { input: 4e-6, cached: 4e-7, output: 18e-6 }
|
|
3104
|
+
},
|
|
3105
|
+
"gemini-3.1-flash-lite": { input: 25e-8, cached: 25e-9, output: 15e-7 },
|
|
3106
|
+
"gemini-3.5-flash": { input: 15e-7, cached: 15e-8, output: 9e-6 },
|
|
3107
|
+
"gemini-3.5-flash-lite": { input: 3e-7, cached: 3e-8, output: 25e-7 },
|
|
3108
|
+
"gemini-3.6-flash": { input: 15e-7, cached: 15e-8, output: 75e-7 }
|
|
2308
3109
|
};
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
function
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
3110
|
+
function isValidTokenCount2(value) {
|
|
3111
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3112
|
+
}
|
|
3113
|
+
function isStandardGeminiRequestTier(value) {
|
|
3114
|
+
return value === void 0 || value === "SERVICE_TIER_UNSPECIFIED" || value === "SERVICE_TIER_STANDARD";
|
|
3115
|
+
}
|
|
3116
|
+
function isDefinitiveStandardGeminiResponseTier(value) {
|
|
3117
|
+
return value === "SERVICE_TIER_UNSPECIFIED" || value === "SERVICE_TIER_STANDARD" || value === "standard";
|
|
3118
|
+
}
|
|
3119
|
+
function isEligibleGeminiPricing(context, hasDefinitiveStandardResponseTier, hasInvalidResponseTier) {
|
|
3120
|
+
return context.eligibleRequest && isStandardGeminiRequestTier(context.serviceTier) && hasDefinitiveStandardResponseTier && !hasInvalidResponseTier;
|
|
3121
|
+
}
|
|
3122
|
+
function estimateGeminiCost(model, usage) {
|
|
3123
|
+
const pricing = GEMINI_PRICING[model];
|
|
3124
|
+
const cached = usage.cachedTokens ?? 0;
|
|
3125
|
+
if (!pricing || !isValidTokenCount2(usage.inputTokens) || !isValidTokenCount2(usage.outputTokens) || !isValidTokenCount2(cached) || cached > usage.inputTokens) {
|
|
3126
|
+
return void 0;
|
|
2321
3127
|
}
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
3128
|
+
const rate = usage.inputTokens > 2e5 && pricing.longContext ? pricing.longContext : pricing;
|
|
3129
|
+
return (usage.inputTokens - cached) * rate.input + cached * rate.cached + usage.outputTokens * rate.output;
|
|
3130
|
+
}
|
|
3131
|
+
function safeTokenSum2(...values) {
|
|
3132
|
+
const total = values.reduce((sum, value) => sum + value, 0);
|
|
3133
|
+
return Number.isSafeInteger(total) ? total : void 0;
|
|
3134
|
+
}
|
|
3135
|
+
function normalizeGeminiUsage(raw) {
|
|
3136
|
+
const inputTokens = raw.promptTokenCount;
|
|
3137
|
+
const completionTokens = raw.candidatesTokenCount;
|
|
3138
|
+
const totalTokens = raw.totalTokenCount;
|
|
3139
|
+
const cachedTokens = raw.cachedContentTokenCount ?? 0;
|
|
3140
|
+
const reasoningTokens = raw.thoughtsTokenCount ?? 0;
|
|
3141
|
+
const toolUsePromptTokens = raw.toolUsePromptTokenCount ?? 0;
|
|
3142
|
+
if (!isValidTokenCount2(inputTokens) || !isValidTokenCount2(completionTokens) || !isValidTokenCount2(totalTokens) || !isValidTokenCount2(cachedTokens) || !isValidTokenCount2(reasoningTokens) || !isValidTokenCount2(toolUsePromptTokens) || cachedTokens > inputTokens) {
|
|
3143
|
+
return void 0;
|
|
3144
|
+
}
|
|
3145
|
+
const billedOutputTokens = safeTokenSum2(completionTokens, reasoningTokens);
|
|
3146
|
+
const expectedTotal = safeTokenSum2(
|
|
3147
|
+
inputTokens,
|
|
3148
|
+
completionTokens,
|
|
3149
|
+
reasoningTokens,
|
|
3150
|
+
toolUsePromptTokens
|
|
3151
|
+
);
|
|
3152
|
+
if (billedOutputTokens === void 0 || expectedTotal === void 0 || totalTokens !== expectedTotal) {
|
|
3153
|
+
return void 0;
|
|
3154
|
+
}
|
|
3155
|
+
return {
|
|
3156
|
+
usage: {
|
|
3157
|
+
prompt_tokens: inputTokens,
|
|
3158
|
+
completion_tokens: completionTokens,
|
|
3159
|
+
total_tokens: totalTokens,
|
|
3160
|
+
cached_tokens: cachedTokens > 0 ? cachedTokens : void 0,
|
|
3161
|
+
reasoning_tokens: reasoningTokens > 0 ? reasoningTokens : void 0
|
|
3162
|
+
},
|
|
3163
|
+
pricingUsage: {
|
|
3164
|
+
inputTokens,
|
|
3165
|
+
outputTokens: billedOutputTokens,
|
|
3166
|
+
cachedTokens
|
|
3167
|
+
},
|
|
3168
|
+
hasUnmodeledBilledUsage: toolUsePromptTokens !== 0
|
|
3169
|
+
};
|
|
2327
3170
|
}
|
|
2328
3171
|
var THINKING_BUDGETS2 = {
|
|
2329
3172
|
low: 1024,
|
|
@@ -2342,8 +3185,23 @@ var THINKING_LEVELS = {
|
|
|
2342
3185
|
max: "high"
|
|
2343
3186
|
// 3.x caps at 'high'
|
|
2344
3187
|
};
|
|
3188
|
+
var GEMINI_3X_MIN_THINKING_LEVEL = /* @__PURE__ */ new Map([
|
|
3189
|
+
["gemini-3-pro-preview", "minimal"],
|
|
3190
|
+
["gemini-3-flash-preview", "minimal"],
|
|
3191
|
+
["gemini-3.1-pro-preview", "low"],
|
|
3192
|
+
["gemini-3.1-pro-preview-customtools", "low"],
|
|
3193
|
+
["gemini-3.1-flash-lite", "minimal"],
|
|
3194
|
+
["gemini-3.1-flash-lite-preview", "minimal"],
|
|
3195
|
+
["gemini-3.5-flash", "minimal"],
|
|
3196
|
+
["gemini-3.5-flash-lite", "minimal"],
|
|
3197
|
+
["gemini-3.6-flash", "minimal"]
|
|
3198
|
+
]);
|
|
3199
|
+
var GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE = /* @__PURE__ */ new Set([
|
|
3200
|
+
"gemini-3.5-flash-lite",
|
|
3201
|
+
"gemini-3.6-flash"
|
|
3202
|
+
]);
|
|
2345
3203
|
function isGemini3x(model) {
|
|
2346
|
-
return
|
|
3204
|
+
return GEMINI_3X_MIN_THINKING_LEVEL.has(model);
|
|
2347
3205
|
}
|
|
2348
3206
|
function budgetToThinkingLevel(budgetTokens) {
|
|
2349
3207
|
if (budgetTokens <= 1024) return "low";
|
|
@@ -2351,8 +3209,7 @@ function budgetToThinkingLevel(budgetTokens) {
|
|
|
2351
3209
|
return "high";
|
|
2352
3210
|
}
|
|
2353
3211
|
function minThinkingLevel(model) {
|
|
2354
|
-
|
|
2355
|
-
return "minimal";
|
|
3212
|
+
return GEMINI_3X_MIN_THINKING_LEVEL.get(model) ?? "minimal";
|
|
2356
3213
|
}
|
|
2357
3214
|
var _warned3xEffortNone = /* @__PURE__ */ new Set();
|
|
2358
3215
|
function warnGemini3xEffortNone(model) {
|
|
@@ -2380,6 +3237,11 @@ var GeminiProvider = class {
|
|
|
2380
3237
|
/\/$/,
|
|
2381
3238
|
""
|
|
2382
3239
|
);
|
|
3240
|
+
assertSafeProviderBaseUrl(
|
|
3241
|
+
this.baseUrl,
|
|
3242
|
+
"Google provider",
|
|
3243
|
+
options.dangerouslyAllowInsecureHttp
|
|
3244
|
+
);
|
|
2383
3245
|
this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
|
|
2384
3246
|
if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
|
|
2385
3247
|
throw new Error("Google API key is required. Set GOOGLE_API_KEY or pass apiKey in options.");
|
|
@@ -2397,10 +3259,12 @@ var GeminiProvider = class {
|
|
|
2397
3259
|
// chat - non-streaming completion
|
|
2398
3260
|
// ---------------------------------------------------------------------------
|
|
2399
3261
|
async chat(messages, options) {
|
|
2400
|
-
const headers = this.buildHeaders(await this.resolveKey());
|
|
2401
3262
|
const body = this.buildRequestBody(messages, options);
|
|
3263
|
+
const pricingContext = this.pricingContext(this.requestModel(options), body);
|
|
3264
|
+
this.assertSafeGemini3Continuation(messages, pricingContext.model);
|
|
3265
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
2402
3266
|
const res = await fetchWithRetry(
|
|
2403
|
-
`${this.baseUrl}/models/${
|
|
3267
|
+
`${this.baseUrl}/models/${pricingContext.model}:generateContent`,
|
|
2404
3268
|
{
|
|
2405
3269
|
method: "POST",
|
|
2406
3270
|
headers,
|
|
@@ -2421,16 +3285,18 @@ var GeminiProvider = class {
|
|
|
2421
3285
|
});
|
|
2422
3286
|
}
|
|
2423
3287
|
const json = await res.json();
|
|
2424
|
-
return this.parseResponse(json,
|
|
3288
|
+
return this.parseResponse(json, pricingContext);
|
|
2425
3289
|
}
|
|
2426
3290
|
// ---------------------------------------------------------------------------
|
|
2427
3291
|
// stream - SSE streaming completion
|
|
2428
3292
|
// ---------------------------------------------------------------------------
|
|
2429
3293
|
async *stream(messages, options) {
|
|
2430
|
-
const headers = this.buildHeaders(await this.resolveKey());
|
|
2431
3294
|
const body = this.buildRequestBody(messages, options);
|
|
3295
|
+
const pricingContext = this.pricingContext(this.requestModel(options), body);
|
|
3296
|
+
this.assertSafeGemini3Continuation(messages, pricingContext.model);
|
|
3297
|
+
const headers = this.buildHeaders(await this.resolveKey());
|
|
2432
3298
|
const res = await fetchWithRetry(
|
|
2433
|
-
`${this.baseUrl}/models/${
|
|
3299
|
+
`${this.baseUrl}/models/${pricingContext.model}:streamGenerateContent?alt=sse`,
|
|
2434
3300
|
{
|
|
2435
3301
|
method: "POST",
|
|
2436
3302
|
headers,
|
|
@@ -2453,7 +3319,7 @@ var GeminiProvider = class {
|
|
|
2453
3319
|
if (!res.body) {
|
|
2454
3320
|
throw new Error("Gemini stream response has no body");
|
|
2455
3321
|
}
|
|
2456
|
-
yield* this.parseSSEStream(res.body,
|
|
3322
|
+
yield* this.parseSSEStream(res.body, pricingContext);
|
|
2457
3323
|
}
|
|
2458
3324
|
// ---------------------------------------------------------------------------
|
|
2459
3325
|
// Internal: request building
|
|
@@ -2474,7 +3340,114 @@ var GeminiProvider = class {
|
|
|
2474
3340
|
}
|
|
2475
3341
|
return `Gemini API error (${status}): ${body}`;
|
|
2476
3342
|
}
|
|
3343
|
+
pricingContext(fallbackModel, body) {
|
|
3344
|
+
return {
|
|
3345
|
+
// Gemini's model is normally selected in the URL. providerOptions is an
|
|
3346
|
+
// explicit raw escape hatch, so honor a post-merge model override for both
|
|
3347
|
+
// the URL and pricing rather than pricing a different request.
|
|
3348
|
+
model: typeof body.model === "string" ? body.model : fallbackModel,
|
|
3349
|
+
serviceTier: body.serviceTier,
|
|
3350
|
+
eligibleRequest: this.baseUrl === "https://generativelanguage.googleapis.com/v1beta" && this.isTextOnlyClientRequest(body)
|
|
3351
|
+
};
|
|
3352
|
+
}
|
|
3353
|
+
requestModel(options) {
|
|
3354
|
+
return typeof options.providerOptions?.model === "string" ? options.providerOptions.model : options.model;
|
|
3355
|
+
}
|
|
3356
|
+
assertSafeGemini3Continuation(messages, model) {
|
|
3357
|
+
if (GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE.has(model)) {
|
|
3358
|
+
const mapped = this.mapMessages(messages.filter((message) => message.role !== "system"));
|
|
3359
|
+
if (mapped.at(-1)?.role === "model") {
|
|
3360
|
+
throw new Error(
|
|
3361
|
+
`${model} does not support a terminal assistant/model prefill; end the request with a user or tool message.`
|
|
3362
|
+
);
|
|
3363
|
+
}
|
|
3364
|
+
}
|
|
3365
|
+
if (!isGemini3x(model)) return;
|
|
3366
|
+
const replayedNativeCallIds = /* @__PURE__ */ new Set();
|
|
3367
|
+
for (const message of messages) {
|
|
3368
|
+
if (message.role !== "assistant" || !Array.isArray(message.providerMetadata?.geminiParts)) {
|
|
3369
|
+
continue;
|
|
3370
|
+
}
|
|
3371
|
+
for (const part of message.providerMetadata.geminiParts) {
|
|
3372
|
+
const id = part !== null && typeof part === "object" ? part.functionCall?.id : void 0;
|
|
3373
|
+
if (!id) continue;
|
|
3374
|
+
if (replayedNativeCallIds.has(id)) {
|
|
3375
|
+
throw new Error(
|
|
3376
|
+
`${model} native functionCall ids must be globally unique across a replay.`
|
|
3377
|
+
);
|
|
3378
|
+
}
|
|
3379
|
+
replayedNativeCallIds.add(id);
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
const consumedToolMessages = /* @__PURE__ */ new Set();
|
|
3383
|
+
for (const [assistantIndex, message] of messages.entries()) {
|
|
3384
|
+
if (message.role !== "assistant" || !message.tool_calls?.length) continue;
|
|
3385
|
+
const rawParts = message.providerMetadata?.geminiParts;
|
|
3386
|
+
if (!Array.isArray(rawParts)) {
|
|
3387
|
+
throw new Error(
|
|
3388
|
+
`${model} tool continuations require providerMetadata.geminiParts with thought signatures.`
|
|
3389
|
+
);
|
|
3390
|
+
}
|
|
3391
|
+
const nativeCalls = rawParts.flatMap((part) => {
|
|
3392
|
+
const functionCall = part !== null && typeof part === "object" ? part.functionCall : void 0;
|
|
3393
|
+
return functionCall?.id ? [{ id: functionCall.id, name: functionCall.name, part }] : [];
|
|
3394
|
+
});
|
|
3395
|
+
const expectedCalls = new Map(
|
|
3396
|
+
message.tool_calls.map((call) => [call.id, call.function.name])
|
|
3397
|
+
);
|
|
3398
|
+
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)) {
|
|
3399
|
+
throw new Error(
|
|
3400
|
+
`${model} tool continuations require exact native functionCall id/name and a thoughtSignature on the first function call.`
|
|
3401
|
+
);
|
|
3402
|
+
}
|
|
3403
|
+
const seenResponses = /* @__PURE__ */ new Set();
|
|
3404
|
+
let toolIndex = assistantIndex + 1;
|
|
3405
|
+
while (messages[toolIndex]?.role === "tool") {
|
|
3406
|
+
const toolMessage = messages[toolIndex];
|
|
3407
|
+
const toolCallId = toolMessage.tool_call_id;
|
|
3408
|
+
if (!toolCallId || !expectedCalls.has(toolCallId) || seenResponses.has(toolCallId)) {
|
|
3409
|
+
throw new Error(
|
|
3410
|
+
`${model} functionResponse requires exactly one matching native functionCall id and name.`
|
|
3411
|
+
);
|
|
3412
|
+
}
|
|
3413
|
+
seenResponses.add(toolCallId);
|
|
3414
|
+
consumedToolMessages.add(toolIndex);
|
|
3415
|
+
toolIndex++;
|
|
3416
|
+
}
|
|
3417
|
+
if (seenResponses.size !== expectedCalls.size) {
|
|
3418
|
+
throw new Error(
|
|
3419
|
+
`${model} tool continuations require one contiguous functionResponse for every native functionCall.`
|
|
3420
|
+
);
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
for (const [index, message] of messages.entries()) {
|
|
3424
|
+
if (message.role === "tool" && !consumedToolMessages.has(index)) {
|
|
3425
|
+
throw new Error(
|
|
3426
|
+
`${model} functionResponse requires exactly one matching native functionCall id and name.`
|
|
3427
|
+
);
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
isTextOnlyClientRequest(body) {
|
|
3432
|
+
if (!Array.isArray(body.contents) || body.cachedContent !== void 0) return false;
|
|
3433
|
+
if (body.contents.some(
|
|
3434
|
+
(content) => content === null || typeof content !== "object" || !Array.isArray(content.parts) || content.parts.some((part) => this.isNonTextGeminiPart(part))
|
|
3435
|
+
)) {
|
|
3436
|
+
return false;
|
|
3437
|
+
}
|
|
3438
|
+
if (body.tools !== void 0 && (!Array.isArray(body.tools) || body.tools.some(
|
|
3439
|
+
(tool2) => tool2 === null || typeof tool2 !== "object" || !Array.isArray(tool2.functionDeclarations) || Object.keys(tool2).some((key) => key !== "functionDeclarations")
|
|
3440
|
+
))) {
|
|
3441
|
+
return false;
|
|
3442
|
+
}
|
|
3443
|
+
const modalities = body.generationConfig?.responseModalities;
|
|
3444
|
+
return modalities === void 0 || modalities === "TEXT" || Array.isArray(modalities) && modalities.length === 1 && modalities[0] === "TEXT";
|
|
3445
|
+
}
|
|
3446
|
+
isNonTextGeminiPart(part) {
|
|
3447
|
+
return part === null || typeof part !== "object" || ["inlineData", "fileData", "executableCode", "codeExecutionResult"].some((key) => key in part);
|
|
3448
|
+
}
|
|
2477
3449
|
buildRequestBody(messages, options) {
|
|
3450
|
+
const requestModel = this.requestModel(options);
|
|
2478
3451
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
2479
3452
|
const nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
2480
3453
|
const systemText = systemMessages.map((m) => m.content).join("\n\n");
|
|
@@ -2492,7 +3465,7 @@ var GeminiProvider = class {
|
|
|
2492
3465
|
];
|
|
2493
3466
|
}
|
|
2494
3467
|
const generationConfig = {};
|
|
2495
|
-
if (options.temperature !== void 0) {
|
|
3468
|
+
if (options.temperature !== void 0 && !GEMINI_MODELS_WITHOUT_PORTABLE_TEMPERATURE.has(requestModel)) {
|
|
2496
3469
|
generationConfig.temperature = options.temperature;
|
|
2497
3470
|
}
|
|
2498
3471
|
if (options.maxTokens !== void 0) {
|
|
@@ -2521,18 +3494,18 @@ var GeminiProvider = class {
|
|
|
2521
3494
|
hasBudgetOverride
|
|
2522
3495
|
} = resolveThinkingOptions(options);
|
|
2523
3496
|
if (thinkingDisabled) {
|
|
2524
|
-
if (isGemini3x(
|
|
3497
|
+
if (isGemini3x(requestModel)) {
|
|
2525
3498
|
if (effort === "none") {
|
|
2526
|
-
warnGemini3xEffortNone(
|
|
3499
|
+
warnGemini3xEffortNone(requestModel);
|
|
2527
3500
|
}
|
|
2528
|
-
generationConfig.thinkingConfig = { thinkingLevel: minThinkingLevel(
|
|
3501
|
+
generationConfig.thinkingConfig = { thinkingLevel: minThinkingLevel(requestModel) };
|
|
2529
3502
|
} else {
|
|
2530
3503
|
generationConfig.thinkingConfig = { thinkingBudget: 0 };
|
|
2531
3504
|
}
|
|
2532
3505
|
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
2533
3506
|
} else if (hasBudgetOverride) {
|
|
2534
3507
|
const config = {};
|
|
2535
|
-
if (isGemini3x(
|
|
3508
|
+
if (isGemini3x(requestModel)) {
|
|
2536
3509
|
config.thinkingLevel = budgetToThinkingLevel(thinkingBudget);
|
|
2537
3510
|
} else {
|
|
2538
3511
|
config.thinkingBudget = thinkingBudget;
|
|
@@ -2542,10 +3515,10 @@ var GeminiProvider = class {
|
|
|
2542
3515
|
if (!body.generationConfig) body.generationConfig = generationConfig;
|
|
2543
3516
|
} else if (activeEffort) {
|
|
2544
3517
|
const config = {};
|
|
2545
|
-
if (isGemini3x(
|
|
3518
|
+
if (isGemini3x(requestModel)) {
|
|
2546
3519
|
config.thinkingLevel = THINKING_LEVELS[activeEffort] ?? "medium";
|
|
2547
3520
|
} else {
|
|
2548
|
-
if (activeEffort === "max" &&
|
|
3521
|
+
if (activeEffort === "max" && requestModel === "gemini-2.5-pro") {
|
|
2549
3522
|
config.thinkingBudget = 32768;
|
|
2550
3523
|
} else {
|
|
2551
3524
|
config.thinkingBudget = THINKING_BUDGETS2[activeEffort] ?? 5e3;
|
|
@@ -2562,7 +3535,9 @@ var GeminiProvider = class {
|
|
|
2562
3535
|
body.toolConfig = { functionCallingConfig: this.mapToolChoice(options.toolChoice) };
|
|
2563
3536
|
}
|
|
2564
3537
|
if (options.providerOptions) {
|
|
2565
|
-
|
|
3538
|
+
const bodyOptions = { ...options.providerOptions };
|
|
3539
|
+
delete bodyOptions.model;
|
|
3540
|
+
Object.assign(body, bodyOptions);
|
|
2566
3541
|
}
|
|
2567
3542
|
return body;
|
|
2568
3543
|
}
|
|
@@ -2580,6 +3555,7 @@ var GeminiProvider = class {
|
|
|
2580
3555
|
mapMessages(messages) {
|
|
2581
3556
|
const toolCallIdToName = /* @__PURE__ */ new Map();
|
|
2582
3557
|
const geminiNativeIds = /* @__PURE__ */ new Set();
|
|
3558
|
+
const geminiNativeNames = /* @__PURE__ */ new Map();
|
|
2583
3559
|
for (const msg of messages) {
|
|
2584
3560
|
if (msg.role === "assistant" && msg.tool_calls) {
|
|
2585
3561
|
for (const tc of msg.tool_calls) {
|
|
@@ -2590,7 +3566,10 @@ var GeminiProvider = class {
|
|
|
2590
3566
|
const rawParts = msg.providerMetadata?.geminiParts;
|
|
2591
3567
|
if (rawParts) {
|
|
2592
3568
|
for (const p of rawParts) {
|
|
2593
|
-
if (p.functionCall?.id)
|
|
3569
|
+
if (p.functionCall?.id) {
|
|
3570
|
+
geminiNativeIds.add(p.functionCall.id);
|
|
3571
|
+
geminiNativeNames.set(p.functionCall.id, p.functionCall.name);
|
|
3572
|
+
}
|
|
2594
3573
|
}
|
|
2595
3574
|
}
|
|
2596
3575
|
}
|
|
@@ -2627,7 +3606,7 @@ var GeminiProvider = class {
|
|
|
2627
3606
|
}
|
|
2628
3607
|
}
|
|
2629
3608
|
} else if (msg.role === "tool") {
|
|
2630
|
-
const functionName = toolCallIdToName.get(msg.tool_call_id) ?? "unknown";
|
|
3609
|
+
const functionName = (msg.tool_call_id ? geminiNativeNames.get(msg.tool_call_id) : void 0) ?? toolCallIdToName.get(msg.tool_call_id) ?? "unknown";
|
|
2631
3610
|
const functionResponse = {
|
|
2632
3611
|
name: functionName,
|
|
2633
3612
|
response: parseGeminiFunctionResponse(msg.content)
|
|
@@ -2692,7 +3671,7 @@ var GeminiProvider = class {
|
|
|
2692
3671
|
// ---------------------------------------------------------------------------
|
|
2693
3672
|
// Internal: response parsing
|
|
2694
3673
|
// ---------------------------------------------------------------------------
|
|
2695
|
-
parseResponse(json,
|
|
3674
|
+
parseResponse(json, pricingContext) {
|
|
2696
3675
|
const candidate = json.candidates?.[0];
|
|
2697
3676
|
let content = "";
|
|
2698
3677
|
let thinkingContent = "";
|
|
@@ -2715,35 +3694,46 @@ var GeminiProvider = class {
|
|
|
2715
3694
|
}
|
|
2716
3695
|
}
|
|
2717
3696
|
}
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
3697
|
+
if (!candidate) {
|
|
3698
|
+
throw new Error("Gemini response did not include a candidate.");
|
|
3699
|
+
}
|
|
3700
|
+
if (candidate.finishReason && !["STOP", "MAX_TOKENS"].includes(candidate.finishReason)) {
|
|
3701
|
+
throw new Error(`Gemini returned non-success finish reason: ${candidate.finishReason}`);
|
|
3702
|
+
}
|
|
3703
|
+
const normalized = json.usageMetadata ? normalizeGeminiUsage(json.usageMetadata) : void 0;
|
|
3704
|
+
const effectiveModel = typeof json.modelVersion === "string" ? json.modelVersion : pricingContext.model;
|
|
3705
|
+
const cost = normalized && !normalized.hasUnmodeledBilledUsage && this.isTextOnlyGeminiResponse(candidate) && isEligibleGeminiPricing(
|
|
3706
|
+
pricingContext,
|
|
3707
|
+
isDefinitiveStandardGeminiResponseTier(json.usageMetadata?.serviceTier),
|
|
3708
|
+
json.usageMetadata?.serviceTier !== void 0 && !isDefinitiveStandardGeminiResponseTier(json.usageMetadata.serviceTier)
|
|
3709
|
+
) ? estimateGeminiCost(effectiveModel, normalized.pricingUsage) : void 0;
|
|
2728
3710
|
const rawParts = candidate?.content?.parts;
|
|
2729
3711
|
const providerMetadata = rawParts ? { geminiParts: rawParts } : void 0;
|
|
2730
3712
|
return {
|
|
2731
3713
|
content,
|
|
2732
3714
|
thinking_content: thinkingContent || void 0,
|
|
2733
3715
|
tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
|
|
2734
|
-
usage,
|
|
3716
|
+
usage: normalized?.usage,
|
|
2735
3717
|
cost,
|
|
2736
3718
|
providerMetadata
|
|
2737
3719
|
};
|
|
2738
3720
|
}
|
|
3721
|
+
isTextOnlyGeminiResponse(candidate) {
|
|
3722
|
+
return candidate?.content?.parts.every((part) => !this.isNonTextGeminiPart(part)) ?? false;
|
|
3723
|
+
}
|
|
2739
3724
|
// ---------------------------------------------------------------------------
|
|
2740
3725
|
// Internal: SSE stream parsing
|
|
2741
3726
|
// ---------------------------------------------------------------------------
|
|
2742
|
-
async *parseSSEStream(body,
|
|
3727
|
+
async *parseSSEStream(body, pricingContext) {
|
|
2743
3728
|
const reader = body.getReader();
|
|
2744
3729
|
const decoder = new TextDecoder();
|
|
2745
3730
|
let buffer = "";
|
|
2746
|
-
let
|
|
3731
|
+
let normalizedUsage;
|
|
3732
|
+
let effectiveModel = pricingContext.model;
|
|
3733
|
+
let hasDefinitiveStandardResponseTier = false;
|
|
3734
|
+
let hasInvalidResponseTier = false;
|
|
3735
|
+
let responseIsTextOnly = true;
|
|
3736
|
+
let terminalFailure;
|
|
2747
3737
|
const accumulatedParts = [];
|
|
2748
3738
|
try {
|
|
2749
3739
|
while (true) {
|
|
@@ -2763,20 +3753,21 @@ var GeminiProvider = class {
|
|
|
2763
3753
|
} catch {
|
|
2764
3754
|
continue;
|
|
2765
3755
|
}
|
|
3756
|
+
if (typeof chunk.modelVersion === "string") effectiveModel = chunk.modelVersion;
|
|
2766
3757
|
if (chunk.usageMetadata) {
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
};
|
|
3758
|
+
normalizedUsage = normalizeGeminiUsage(chunk.usageMetadata);
|
|
3759
|
+
if (chunk.usageMetadata.serviceTier !== void 0) {
|
|
3760
|
+
if (isDefinitiveStandardGeminiResponseTier(chunk.usageMetadata.serviceTier)) {
|
|
3761
|
+
hasDefinitiveStandardResponseTier = true;
|
|
3762
|
+
} else {
|
|
3763
|
+
hasInvalidResponseTier = true;
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
2776
3766
|
}
|
|
2777
3767
|
const candidate = chunk.candidates?.[0];
|
|
2778
3768
|
if (candidate?.content?.parts) {
|
|
2779
3769
|
for (const part of candidate.content.parts) {
|
|
3770
|
+
if (this.isNonTextGeminiPart(part)) responseIsTextOnly = false;
|
|
2780
3771
|
accumulatedParts.push(part);
|
|
2781
3772
|
if (part.thought && part.text) {
|
|
2782
3773
|
yield { type: "thinking_delta", content: part.text };
|
|
@@ -2792,18 +3783,23 @@ var GeminiProvider = class {
|
|
|
2792
3783
|
}
|
|
2793
3784
|
}
|
|
2794
3785
|
}
|
|
3786
|
+
if (candidate?.finishReason && !["STOP", "MAX_TOKENS"].includes(candidate.finishReason)) {
|
|
3787
|
+
terminalFailure = candidate.finishReason;
|
|
3788
|
+
}
|
|
2795
3789
|
}
|
|
2796
3790
|
}
|
|
3791
|
+
if (terminalFailure) {
|
|
3792
|
+
throw new Error(`Gemini returned non-success finish reason: ${terminalFailure}`);
|
|
3793
|
+
}
|
|
2797
3794
|
const providerMetadata = accumulatedParts.length > 0 ? { geminiParts: accumulatedParts } : void 0;
|
|
2798
3795
|
yield {
|
|
2799
3796
|
type: "done",
|
|
2800
|
-
usage,
|
|
2801
|
-
cost:
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
) : void 0,
|
|
3797
|
+
usage: normalizedUsage?.usage,
|
|
3798
|
+
cost: normalizedUsage && !normalizedUsage.hasUnmodeledBilledUsage && responseIsTextOnly && isEligibleGeminiPricing(
|
|
3799
|
+
pricingContext,
|
|
3800
|
+
hasDefinitiveStandardResponseTier,
|
|
3801
|
+
hasInvalidResponseTier
|
|
3802
|
+
) ? estimateGeminiCost(effectiveModel, normalizedUsage.pricingUsage) : void 0,
|
|
2807
3803
|
providerMetadata
|
|
2808
3804
|
};
|
|
2809
3805
|
} finally {
|
|
@@ -2820,8 +3816,6 @@ var OPENROUTER_PROFILE = {
|
|
|
2820
3816
|
envApiKey: "OPENROUTER_API_KEY",
|
|
2821
3817
|
envBaseUrl: "OPENROUTER_BASE_URL",
|
|
2822
3818
|
pricing: { kind: "from-response" },
|
|
2823
|
-
// Ask OpenRouter to report per-call cost in usage.cost.
|
|
2824
|
-
requestDefaults: { usage: { include: true } },
|
|
2825
3819
|
reasoning: {
|
|
2826
3820
|
// OpenRouter's effort vocabulary is low|medium|high; xhigh/max clamp to high.
|
|
2827
3821
|
emit: reasoningObjectEmit(
|
|
@@ -2844,7 +3838,7 @@ var AZURE_PROFILE = {
|
|
|
2844
3838
|
envApiKey: "AZURE_OPENAI_API_KEY",
|
|
2845
3839
|
envBaseUrl: "AZURE_OPENAI_BASE_URL",
|
|
2846
3840
|
authHeader: "api-key",
|
|
2847
|
-
pricing: { kind: "
|
|
3841
|
+
pricing: { kind: "unknown" },
|
|
2848
3842
|
reasoning: { emit: openaiReasoningEmit, capture: "none" },
|
|
2849
3843
|
roleFor: (role, model) => role === "system" && isOSeriesModel(model) ? "developer" : role,
|
|
2850
3844
|
maxTokensField: "max_completion_tokens",
|
|
@@ -2852,29 +3846,100 @@ var AZURE_PROFILE = {
|
|
|
2852
3846
|
};
|
|
2853
3847
|
|
|
2854
3848
|
// src/providers/profiles/xai.ts
|
|
2855
|
-
var
|
|
2856
|
-
var
|
|
3849
|
+
var GROK_45 = /* @__PURE__ */ new Set(["grok-4.5", "grok-4.5-latest", "grok-build-latest"]);
|
|
3850
|
+
var GROK_43 = /* @__PURE__ */ new Set(["grok-4.3", "grok-4.3-latest", "grok-latest"]);
|
|
3851
|
+
var GROK_420_REASONING = /* @__PURE__ */ new Set([
|
|
3852
|
+
"grok-4.20-0309-reasoning",
|
|
3853
|
+
"grok-4.20-reasoning-latest",
|
|
3854
|
+
"grok-4.20",
|
|
3855
|
+
"grok-4.20-reasoning",
|
|
3856
|
+
"grok-4.20-0309",
|
|
3857
|
+
"grok-4.20-beta-0309-reasoning",
|
|
3858
|
+
"grok-4.20-beta",
|
|
3859
|
+
"grok-4.20-beta-0309",
|
|
3860
|
+
"grok-4.20-beta-latest",
|
|
3861
|
+
"grok-4.20-beta-latest-reasoning",
|
|
3862
|
+
"grok-4.20-beta-reasoning",
|
|
3863
|
+
"grok-4.20-experimental-beta-0304-reasoning",
|
|
3864
|
+
"grok-4.20-experimental-beta-0304",
|
|
3865
|
+
"grok-4.20-experimental-beta-reasoning-latest",
|
|
3866
|
+
"grok-4.20-experimental-beta-latest",
|
|
3867
|
+
"grok-4.20-reasoning-gv2"
|
|
3868
|
+
]);
|
|
3869
|
+
var GROK_420_NON_REASONING = /* @__PURE__ */ new Set([
|
|
3870
|
+
"grok-4.20-0309-non-reasoning",
|
|
3871
|
+
"grok-4.20-non-reasoning",
|
|
3872
|
+
"grok-4.20-non-reasoning-latest",
|
|
3873
|
+
"grok-4.20-beta-non-reasoning",
|
|
3874
|
+
"grok-4.20-beta-latest-non-reasoning",
|
|
3875
|
+
"grok-4.20-experimental-beta-0304-non-reasoning",
|
|
3876
|
+
"grok-4.20-experimental-beta-non-reasoning-latest",
|
|
3877
|
+
"grok-4.20-beta-0309-non-reasoning",
|
|
3878
|
+
"grok-4.20-non-reasoning-gv2"
|
|
3879
|
+
]);
|
|
3880
|
+
var warnedIrreducible45 = /* @__PURE__ */ new Set();
|
|
3881
|
+
var warnedEffortClamp = /* @__PURE__ */ new Set();
|
|
3882
|
+
function warnIrreducible45(model) {
|
|
3883
|
+
if (warnedIrreducible45.has(model)) return;
|
|
3884
|
+
warnedIrreducible45.add(model);
|
|
3885
|
+
console.warn(
|
|
3886
|
+
`[axl] effort: 'none' on xAI ${model} maps to 'low'; Grok 4.5 cannot disable reasoning.`
|
|
3887
|
+
);
|
|
3888
|
+
}
|
|
3889
|
+
function warnEffortClamp(model, effort) {
|
|
3890
|
+
const key = `${model}:${effort}`;
|
|
3891
|
+
if (warnedEffortClamp.has(key)) return;
|
|
3892
|
+
warnedEffortClamp.add(key);
|
|
3893
|
+
console.warn(
|
|
3894
|
+
`[axl] effort: '${effort}' on xAI ${model} maps to 'high'; this model supports at most 'high'.`
|
|
3895
|
+
);
|
|
3896
|
+
}
|
|
3897
|
+
var isReasoningChatModel = (model) => GROK_45.has(model) || GROK_43.has(model) || GROK_420_REASONING.has(model);
|
|
2857
3898
|
var XAI_PROFILE = {
|
|
2858
3899
|
name: "xai",
|
|
2859
3900
|
label: "xAI",
|
|
2860
3901
|
defaultBaseUrl: "https://api.x.ai/v1",
|
|
2861
3902
|
envApiKey: "XAI_API_KEY",
|
|
2862
3903
|
envBaseUrl: "XAI_BASE_URL",
|
|
2863
|
-
pricing: { kind: "
|
|
3904
|
+
pricing: { kind: "from-response" },
|
|
2864
3905
|
reasoning: {
|
|
2865
3906
|
emit: reasoningEffortEmit((resolved, model) => {
|
|
2866
|
-
if (
|
|
2867
|
-
|
|
2868
|
-
|
|
3907
|
+
if (GROK_45.has(model)) {
|
|
3908
|
+
if (resolved.thinkingDisabled) {
|
|
3909
|
+
warnIrreducible45(model);
|
|
3910
|
+
return "low";
|
|
3911
|
+
}
|
|
3912
|
+
if (!resolved.activeEffort) return void 0;
|
|
3913
|
+
if (resolved.activeEffort === "xhigh" || resolved.activeEffort === "max") {
|
|
3914
|
+
warnEffortClamp(model, resolved.activeEffort);
|
|
3915
|
+
return "high";
|
|
3916
|
+
}
|
|
3917
|
+
return resolved.activeEffort;
|
|
3918
|
+
}
|
|
3919
|
+
if (GROK_43.has(model)) {
|
|
3920
|
+
if (resolved.thinkingDisabled) return "none";
|
|
3921
|
+
if (!resolved.activeEffort) return void 0;
|
|
3922
|
+
if (resolved.activeEffort === "xhigh" || resolved.activeEffort === "max") {
|
|
3923
|
+
warnEffortClamp(model, resolved.activeEffort);
|
|
3924
|
+
return "high";
|
|
3925
|
+
}
|
|
3926
|
+
return resolved.activeEffort;
|
|
3927
|
+
}
|
|
3928
|
+
if (GROK_420_NON_REASONING.has(model)) return void 0;
|
|
3929
|
+
return void 0;
|
|
2869
3930
|
}),
|
|
2870
3931
|
capture: "reasoning"
|
|
2871
3932
|
},
|
|
2872
3933
|
capabilities: {
|
|
2873
|
-
forbiddenParams: (model) =>
|
|
3934
|
+
forbiddenParams: (model) => isReasoningChatModel(model) ? ["stop", "presence_penalty", "frequency_penalty"] : []
|
|
2874
3935
|
}
|
|
2875
3936
|
};
|
|
2876
3937
|
|
|
2877
3938
|
// src/providers/profiles/deepseek.ts
|
|
3939
|
+
var DEEPSEEK_PRICING = {
|
|
3940
|
+
"deepseek-v4-flash": [14e-8, 28e-8, 0.02],
|
|
3941
|
+
"deepseek-v4-pro": [435e-9, 87e-8, 1 / 120]
|
|
3942
|
+
};
|
|
2878
3943
|
var isReasoner = (model) => /reasoner|v4/i.test(model);
|
|
2879
3944
|
var deepseekEmit = (_body, _resolved, model) => ({
|
|
2880
3945
|
stripTemperature: isReasoner(model)
|
|
@@ -2885,7 +3950,7 @@ var DEEPSEEK_PROFILE = {
|
|
|
2885
3950
|
defaultBaseUrl: "https://api.deepseek.com/v1",
|
|
2886
3951
|
envApiKey: "DEEPSEEK_API_KEY",
|
|
2887
3952
|
envBaseUrl: "DEEPSEEK_BASE_URL",
|
|
2888
|
-
pricing: { kind: "
|
|
3953
|
+
pricing: { kind: "table", table: DEEPSEEK_PRICING, match: "exact" },
|
|
2889
3954
|
reasoning: {
|
|
2890
3955
|
emit: deepseekEmit,
|
|
2891
3956
|
capture: "reasoning_content",
|
|
@@ -2896,27 +3961,56 @@ var DEEPSEEK_PROFILE = {
|
|
|
2896
3961
|
forbiddenParams: (model) => isReasoner(model) ? ["top_p", "presence_penalty", "frequency_penalty"] : []
|
|
2897
3962
|
}
|
|
2898
3963
|
};
|
|
3964
|
+
attachBuiltinTablePricingPolicy(DEEPSEEK_PROFILE, {
|
|
3965
|
+
canonicalBaseUrl: "https://api.deepseek.com/v1",
|
|
3966
|
+
requireCacheSplit: true
|
|
3967
|
+
});
|
|
2899
3968
|
|
|
2900
3969
|
// src/providers/profiles/mistral.ts
|
|
2901
|
-
var
|
|
3970
|
+
var MISTRAL_PRICING = {
|
|
3971
|
+
"mistral-medium-latest": [15e-7, 75e-7, 0.1],
|
|
3972
|
+
"mistral-small-latest": [15e-8, 6e-7, 0.1],
|
|
3973
|
+
"mistral-large-latest": [5e-7, 15e-7, 0.1],
|
|
3974
|
+
"devstral-medium-latest": [4e-7, 2e-6, 0.1],
|
|
3975
|
+
"devstral-small-latest": [1e-7, 3e-7, 0.1],
|
|
3976
|
+
"codestral-latest": [3e-7, 9e-7, 0.1],
|
|
3977
|
+
"magistral-medium-latest": [2e-6, 5e-6, 0.1],
|
|
3978
|
+
"magistral-small-latest": [5e-7, 15e-7, 0.1],
|
|
3979
|
+
"ministral-3b-latest": [1e-7, 1e-7, 0.1],
|
|
3980
|
+
"ministral-8b-latest": [15e-8, 15e-8, 0.1],
|
|
3981
|
+
"ministral-14b-latest": [2e-7, 2e-7, 0.1],
|
|
3982
|
+
"open-mistral-nemo": [15e-8, 15e-8, 0.1],
|
|
3983
|
+
"open-mixtral-8x7b": [7e-7, 7e-7, 0.1],
|
|
3984
|
+
"open-mixtral-8x22b": [2e-6, 6e-6, 0.1]
|
|
3985
|
+
};
|
|
3986
|
+
var acceptsReasoningEffort = (model) => /^mistral-(small|medium)/i.test(model);
|
|
2902
3987
|
var MISTRAL_PROFILE = {
|
|
2903
3988
|
name: "mistral",
|
|
2904
3989
|
label: "Mistral",
|
|
2905
3990
|
defaultBaseUrl: "https://api.mistral.ai/v1",
|
|
2906
3991
|
envApiKey: "MISTRAL_API_KEY",
|
|
2907
3992
|
envBaseUrl: "MISTRAL_BASE_URL",
|
|
2908
|
-
pricing: { kind: "
|
|
3993
|
+
pricing: { kind: "table", table: MISTRAL_PRICING, match: "exact" },
|
|
2909
3994
|
reasoning: {
|
|
2910
3995
|
emit: reasoningEffortEmit((resolved, model) => {
|
|
2911
|
-
if (!
|
|
3996
|
+
if (!acceptsReasoningEffort(model)) return void 0;
|
|
2912
3997
|
if (resolved.thinkingDisabled || !resolved.activeEffort) return void 0;
|
|
2913
3998
|
return "high";
|
|
2914
3999
|
}),
|
|
2915
4000
|
capture: "reasoning_content"
|
|
2916
4001
|
}
|
|
2917
4002
|
};
|
|
4003
|
+
attachBuiltinTablePricingPolicy(MISTRAL_PROFILE, {
|
|
4004
|
+
canonicalBaseUrl: "https://api.mistral.ai/v1"
|
|
4005
|
+
});
|
|
2918
4006
|
|
|
2919
4007
|
// src/providers/profiles/groq.ts
|
|
4008
|
+
var GROQ_PRICING = {
|
|
4009
|
+
"llama-3.1-8b-instant": [5e-8, 8e-8, 1],
|
|
4010
|
+
"llama-3.3-70b-versatile": [59e-8, 79e-8, 1],
|
|
4011
|
+
"openai/gpt-oss-120b": [15e-8, 6e-7, 0.5],
|
|
4012
|
+
"openai/gpt-oss-20b": [75e-9, 3e-7, 0.5]
|
|
4013
|
+
};
|
|
2920
4014
|
var isGptOss = (model) => /gpt-oss/i.test(model);
|
|
2921
4015
|
var GROQ_PROFILE = {
|
|
2922
4016
|
name: "groq",
|
|
@@ -2924,7 +4018,11 @@ var GROQ_PROFILE = {
|
|
|
2924
4018
|
defaultBaseUrl: "https://api.groq.com/openai/v1",
|
|
2925
4019
|
envApiKey: "GROQ_API_KEY",
|
|
2926
4020
|
envBaseUrl: "GROQ_BASE_URL",
|
|
2927
|
-
pricing: {
|
|
4021
|
+
pricing: {
|
|
4022
|
+
kind: "table",
|
|
4023
|
+
table: GROQ_PRICING,
|
|
4024
|
+
match: "exact"
|
|
4025
|
+
},
|
|
2928
4026
|
reasoning: {
|
|
2929
4027
|
emit: reasoningEffortEmit((resolved, model) => {
|
|
2930
4028
|
if (!isGptOss(model) || resolved.thinkingDisabled || !resolved.activeEffort) {
|
|
@@ -2945,6 +4043,21 @@ var GROQ_PROFILE = {
|
|
|
2945
4043
|
supportsJsonSchema: isGptOss
|
|
2946
4044
|
}
|
|
2947
4045
|
};
|
|
4046
|
+
attachBuiltinTablePricingPolicy(GROQ_PROFILE, {
|
|
4047
|
+
canonicalBaseUrl: "https://api.groq.com/openai/v1",
|
|
4048
|
+
supportedServiceTiers: ["on_demand", "flex"],
|
|
4049
|
+
functionToolsOnly: true,
|
|
4050
|
+
chargedRequestModifiers: [
|
|
4051
|
+
"browser_search",
|
|
4052
|
+
"code_interpreter",
|
|
4053
|
+
"web_search",
|
|
4054
|
+
"visit_website",
|
|
4055
|
+
"wolfram_alpha",
|
|
4056
|
+
"remote_mcp",
|
|
4057
|
+
"mcp",
|
|
4058
|
+
"compound_custom"
|
|
4059
|
+
]
|
|
4060
|
+
});
|
|
2948
4061
|
|
|
2949
4062
|
// src/providers/profiles/bedrock.ts
|
|
2950
4063
|
var isGptOss2 = (model) => /gpt-oss/i.test(model);
|
|
@@ -3016,6 +4129,7 @@ var builtinFactories = {
|
|
|
3016
4129
|
return new OpenAIProvider({
|
|
3017
4130
|
apiKey: opts.apiKey,
|
|
3018
4131
|
baseUrl: opts.baseUrl,
|
|
4132
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
3019
4133
|
rateLimit: opts.rateLimit
|
|
3020
4134
|
});
|
|
3021
4135
|
},
|
|
@@ -3028,6 +4142,7 @@ var builtinFactories = {
|
|
|
3028
4142
|
return new OpenAIResponsesProvider({
|
|
3029
4143
|
apiKey: opts.apiKey,
|
|
3030
4144
|
baseUrl: opts.baseUrl,
|
|
4145
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
3031
4146
|
rateLimit: opts.rateLimit
|
|
3032
4147
|
});
|
|
3033
4148
|
},
|
|
@@ -3036,6 +4151,7 @@ var builtinFactories = {
|
|
|
3036
4151
|
return new AnthropicProvider({
|
|
3037
4152
|
apiKey: opts.apiKey,
|
|
3038
4153
|
baseUrl: opts.baseUrl,
|
|
4154
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
3039
4155
|
rateLimit: opts.rateLimit
|
|
3040
4156
|
});
|
|
3041
4157
|
},
|
|
@@ -3044,6 +4160,7 @@ var builtinFactories = {
|
|
|
3044
4160
|
return new GeminiProvider({
|
|
3045
4161
|
apiKey: opts.apiKey,
|
|
3046
4162
|
baseUrl: opts.baseUrl,
|
|
4163
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
3047
4164
|
rateLimit: opts.rateLimit
|
|
3048
4165
|
});
|
|
3049
4166
|
}
|
|
@@ -3055,6 +4172,7 @@ function presetFactory(profile) {
|
|
|
3055
4172
|
profile,
|
|
3056
4173
|
apiKey: opts.apiKey,
|
|
3057
4174
|
baseUrl: opts.baseUrl,
|
|
4175
|
+
dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
|
|
3058
4176
|
authHeader: opts.authHeader,
|
|
3059
4177
|
rateLimit: opts.rateLimit
|
|
3060
4178
|
});
|
|
@@ -3759,7 +4877,7 @@ var COST_LEAF_SET = new Set(COST_BEARING_LEAF_TYPES);
|
|
|
3759
4877
|
function hasPositiveTokens(event) {
|
|
3760
4878
|
const t = event.tokens;
|
|
3761
4879
|
if (!t) return false;
|
|
3762
|
-
return (t.input ?? 0) + (t.output ?? 0) + (t.reasoning ?? 0) > 0;
|
|
4880
|
+
return (t.input ?? 0) + (t.output ?? 0) + (t.reasoning ?? 0) + (t.cached ?? 0) + (t.cacheWrite ?? 0) > 0;
|
|
3763
4881
|
}
|
|
3764
4882
|
function isUsableCost(cost) {
|
|
3765
4883
|
return typeof cost === "number" && Number.isFinite(cost) && cost >= 0;
|
|
@@ -6372,6 +7490,11 @@ var WorkflowContext = class _WorkflowContext {
|
|
|
6372
7490
|
"axl.agent.cached_tokens",
|
|
6373
7491
|
usageCapture.value.cached_tokens
|
|
6374
7492
|
);
|
|
7493
|
+
if (usageCapture.value.cache_write_tokens)
|
|
7494
|
+
span.setAttribute(
|
|
7495
|
+
"axl.agent.cache_write_tokens",
|
|
7496
|
+
usageCapture.value.cache_write_tokens
|
|
7497
|
+
);
|
|
6375
7498
|
}
|
|
6376
7499
|
return r;
|
|
6377
7500
|
}
|
|
@@ -6817,7 +7940,9 @@ ${appendedSchemaText}`;
|
|
|
6817
7940
|
tokens: response.usage ? {
|
|
6818
7941
|
input: response.usage.prompt_tokens,
|
|
6819
7942
|
output: response.usage.completion_tokens,
|
|
6820
|
-
reasoning: response.usage.reasoning_tokens
|
|
7943
|
+
reasoning: response.usage.reasoning_tokens,
|
|
7944
|
+
cached: response.usage.cached_tokens,
|
|
7945
|
+
cacheWrite: response.usage.cache_write_tokens
|
|
6821
7946
|
} : void 0,
|
|
6822
7947
|
duration: Date.now() - turnStart,
|
|
6823
7948
|
data: {
|
|
@@ -10058,6 +11183,14 @@ var HttpMcpClient = class {
|
|
|
10058
11183
|
this.config = config;
|
|
10059
11184
|
this.name = config.name;
|
|
10060
11185
|
this.baseUrl = (config.uri ?? "").replace(/\/$/, "");
|
|
11186
|
+
if (this.baseUrl) {
|
|
11187
|
+
assertSafeHttpEndpoint(
|
|
11188
|
+
this.baseUrl,
|
|
11189
|
+
`MCP server "${this.name}"`,
|
|
11190
|
+
config.dangerouslyAllowInsecureHttp,
|
|
11191
|
+
"tool arguments and results"
|
|
11192
|
+
);
|
|
11193
|
+
}
|
|
10061
11194
|
}
|
|
10062
11195
|
name;
|
|
10063
11196
|
tools = [];
|
|
@@ -10087,7 +11220,9 @@ var HttpMcpClient = class {
|
|
|
10087
11220
|
method: "POST",
|
|
10088
11221
|
headers: { "Content-Type": "application/json" },
|
|
10089
11222
|
body: JSON.stringify(request),
|
|
10090
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
11223
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
11224
|
+
// Tool calls can be non-idempotent: never forward or retry their body.
|
|
11225
|
+
redirect: "manual"
|
|
10091
11226
|
});
|
|
10092
11227
|
if (!res.ok) {
|
|
10093
11228
|
throw new Error(`MCP HTTP error (${res.status}): ${await res.text()}`);
|
|
@@ -12879,6 +14014,11 @@ var OpenAIEmbedder = class {
|
|
|
12879
14014
|
}
|
|
12880
14015
|
this.model = options.model ?? "text-embedding-3-small";
|
|
12881
14016
|
this.baseUrl = options.baseUrl ?? "https://api.openai.com";
|
|
14017
|
+
assertSafeProviderBaseUrl(
|
|
14018
|
+
this.baseUrl,
|
|
14019
|
+
"OpenAI embedder",
|
|
14020
|
+
options.dangerouslyAllowInsecureHttp
|
|
14021
|
+
);
|
|
12882
14022
|
this.dimensions = options.dimensions ?? 1536;
|
|
12883
14023
|
}
|
|
12884
14024
|
async embed(texts, signal) {
|