@bman654/clodex 2.3.0 → 2.4.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/dist/cli.js
CHANGED
|
@@ -40,7 +40,7 @@ import {
|
|
|
40
40
|
withProviderMutationLock,
|
|
41
41
|
withRegistryWriteLock,
|
|
42
42
|
withRegistryWriteLockSync
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-LBEJOEUY.js";
|
|
44
44
|
|
|
45
45
|
// src/cli.ts
|
|
46
46
|
import pc13 from "picocolors";
|
|
@@ -218,7 +218,7 @@ import { join } from "path";
|
|
|
218
218
|
// package.json
|
|
219
219
|
var package_default = {
|
|
220
220
|
name: "@bman654/clodex",
|
|
221
|
-
version: "2.
|
|
221
|
+
version: "2.4.0",
|
|
222
222
|
publishConfig: {
|
|
223
223
|
access: "public"
|
|
224
224
|
},
|
|
@@ -840,6 +840,51 @@ function applyClaudeCodeThirdPartyCompat(env) {
|
|
|
840
840
|
env["ENABLE_TOOL_SEARCH"] = "true";
|
|
841
841
|
env["CLAUDE_CODE_SIMPLE_SYSTEM_PROMPT"] = "0";
|
|
842
842
|
}
|
|
843
|
+
var LOOPBACK_NO_PROXY_ENTRIES = ["localhost", "127.0.0.1", "::1"];
|
|
844
|
+
function gatewayHostname(gatewayUrl) {
|
|
845
|
+
try {
|
|
846
|
+
return new URL(gatewayUrl).hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
847
|
+
} catch {
|
|
848
|
+
return "";
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
function isLoopbackHost(host) {
|
|
852
|
+
if (host === "localhost" || host.endsWith(".localhost")) return true;
|
|
853
|
+
if (host === "::1" || host === "0:0:0:0:0:0:0:1") return true;
|
|
854
|
+
const v4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
855
|
+
if (!v4) return false;
|
|
856
|
+
const octets = v4.slice(1).map(Number);
|
|
857
|
+
if (octets.some((o) => o > 255)) return false;
|
|
858
|
+
return octets[0] === 127;
|
|
859
|
+
}
|
|
860
|
+
function effectiveNoProxyValue(env) {
|
|
861
|
+
return env["no_proxy"] || env["NO_PROXY"] || "";
|
|
862
|
+
}
|
|
863
|
+
function normalizedNoProxyEntries(env) {
|
|
864
|
+
return effectiveNoProxyValue(env).split(",").map((value) => value.trim()).filter(Boolean);
|
|
865
|
+
}
|
|
866
|
+
function addGatewayNoProxyBypass(env, gatewayUrl) {
|
|
867
|
+
const hasProxy = Boolean(
|
|
868
|
+
env["HTTPS_PROXY"]?.trim() || env["https_proxy"]?.trim() || env["HTTP_PROXY"]?.trim() || env["http_proxy"]?.trim()
|
|
869
|
+
);
|
|
870
|
+
if (!hasProxy) return;
|
|
871
|
+
const host = gatewayHostname(gatewayUrl);
|
|
872
|
+
if (!isLoopbackHost(host)) return;
|
|
873
|
+
if (effectiveNoProxyValue(env) === "*") return;
|
|
874
|
+
const existing = normalizedNoProxyEntries(env);
|
|
875
|
+
const additions = [...LOOPBACK_NO_PROXY_ENTRIES, host];
|
|
876
|
+
const seen = new Set(existing.map((entry) => entry.toLowerCase()));
|
|
877
|
+
const merged = [...existing];
|
|
878
|
+
for (const entry of additions) {
|
|
879
|
+
const key = entry.toLowerCase();
|
|
880
|
+
if (seen.has(key)) continue;
|
|
881
|
+
seen.add(key);
|
|
882
|
+
merged.push(entry);
|
|
883
|
+
}
|
|
884
|
+
const value = merged.join(",");
|
|
885
|
+
env["NO_PROXY"] = value;
|
|
886
|
+
env["no_proxy"] = value;
|
|
887
|
+
}
|
|
843
888
|
function buildChildEnv(baseUrl, model, apiKey, proxyPort, contextWindow, enableGatewayDiscovery) {
|
|
844
889
|
const env = { ...process.env };
|
|
845
890
|
for (const name of CONFLICTING_ENV_VARS) {
|
|
@@ -847,6 +892,7 @@ function buildChildEnv(baseUrl, model, apiKey, proxyPort, contextWindow, enableG
|
|
|
847
892
|
}
|
|
848
893
|
env["ANTHROPIC_BASE_URL"] = proxyPort ? `http://127.0.0.1:${proxyPort}` : baseUrl;
|
|
849
894
|
env["ANTHROPIC_API_KEY"] = apiKey;
|
|
895
|
+
addGatewayNoProxyBypass(env, env["ANTHROPIC_BASE_URL"]);
|
|
850
896
|
const bareModel = stripOneMContextSuffix(model);
|
|
851
897
|
env["ANTHROPIC_MODEL"] = claudeCodeClientModelId(model, contextWindow);
|
|
852
898
|
env["CLAUDE_CODE_MAX_CONTEXT_TOKENS"] = String(resolveContextWindow(bareModel, contextWindow));
|
|
@@ -3414,6 +3460,7 @@ function applyPricingToRegistryProviders(registry, cache) {
|
|
|
3414
3460
|
const index = buildPricingIndex(cache);
|
|
3415
3461
|
let changed = false;
|
|
3416
3462
|
for (const provider of registry.providers) {
|
|
3463
|
+
if (provider.preserveModelPricing) continue;
|
|
3417
3464
|
if (!provider.modelsCache?.models.length) continue;
|
|
3418
3465
|
const platform = TEMPLATE_TO_PRICING_PLATFORM[provider.templateId] ?? TEMPLATE_TO_PRICING_PLATFORM[provider.id];
|
|
3419
3466
|
const enriched = enrichModelsWithPricing(provider.modelsCache.models, index, platform);
|
|
@@ -3716,7 +3763,9 @@ function cachedModelToLocal(cached, provider) {
|
|
|
3716
3763
|
reasoning: cached.reasoning ?? modelsDev?.reasoning,
|
|
3717
3764
|
interleavedReasoningField: cached.interleavedReasoningField ?? modelsDev?.interleaved?.field,
|
|
3718
3765
|
useResponsesLite: cached.useResponsesLite,
|
|
3719
|
-
preferWebSockets: cached.preferWebSockets
|
|
3766
|
+
preferWebSockets: cached.preferWebSockets,
|
|
3767
|
+
modalities: cached.modalities,
|
|
3768
|
+
compatibility: cached.compatibility
|
|
3720
3769
|
};
|
|
3721
3770
|
}
|
|
3722
3771
|
function isAnonymousProvider(provider) {
|
|
@@ -3926,6 +3975,7 @@ function localProvidersToServerModels(localProviders) {
|
|
|
3926
3975
|
interleavedReasoningField: model.interleavedReasoningField,
|
|
3927
3976
|
useResponsesLite: model.useResponsesLite,
|
|
3928
3977
|
preferWebSockets: model.preferWebSockets,
|
|
3978
|
+
compatibility: model.compatibility,
|
|
3929
3979
|
headers: provider.headers,
|
|
3930
3980
|
providerData: provider.providerData
|
|
3931
3981
|
}))
|
|
@@ -4044,7 +4094,7 @@ function frameStatusCode(code, discriminator) {
|
|
|
4044
4094
|
const numeric = Number(code);
|
|
4045
4095
|
if (numeric >= 400 && numeric <= 599) return numeric;
|
|
4046
4096
|
}
|
|
4047
|
-
if (/insufficient_quota|rate_limit/.test(discriminator)) return 429;
|
|
4097
|
+
if (/insufficient_quota|rate_limit|usage_limit/.test(discriminator)) return 429;
|
|
4048
4098
|
if (discriminator.includes("authentication")) return 401;
|
|
4049
4099
|
if (discriminator.includes("permission")) return 403;
|
|
4050
4100
|
if (discriminator.includes("not_found")) return 404;
|
|
@@ -5267,7 +5317,8 @@ function handleSocketMessage(entry, data) {
|
|
|
5267
5317
|
return;
|
|
5268
5318
|
}
|
|
5269
5319
|
const errorStatus = type === "error" && !ctx.emittedModelData ? responseErrorStatus(event) : void 0;
|
|
5270
|
-
|
|
5320
|
+
const emptyFailureTerminal = FAILURE_EVENT_TYPES.has(type ?? "") && errorStatus === void 0 && !willRetry && !ctx.emittedModelData;
|
|
5321
|
+
if (FAILURE_EVENT_TYPES.has(type ?? "") && (errorStatus === void 0 || willRetry) && !emptyFailureTerminal) {
|
|
5271
5322
|
emitResponseErrorDiagnostic(entry, ctx, {
|
|
5272
5323
|
source: "response_event",
|
|
5273
5324
|
upstreamEventType: type,
|
|
@@ -5310,6 +5361,39 @@ function handleSocketMessage(entry, data) {
|
|
|
5310
5361
|
);
|
|
5311
5362
|
return;
|
|
5312
5363
|
}
|
|
5364
|
+
if (emptyFailureTerminal) {
|
|
5365
|
+
const details = responseFailureDetails(event);
|
|
5366
|
+
const named = [details.errorType, details.errorCode, details.incompleteReason].filter((value) => typeof value === "string");
|
|
5367
|
+
const summary = "OpenAI ended the response with no output" + (named.length ? ` (${named.join(" / ")})` : ` (${type})`);
|
|
5368
|
+
const discriminator = [details.errorType, details.errorCode].filter((value) => typeof value === "string").join(" ").toLowerCase();
|
|
5369
|
+
const settledReason = details.incompleteReason === "content_filter" || details.incompleteReason === "max_output_tokens";
|
|
5370
|
+
const numericOrNamed = typeof details.errorCode === "string" ? details.errorCode : void 0;
|
|
5371
|
+
const classified = numericOrNamed !== void 0 || discriminator ? frameStatusCode(numericOrNamed, discriminator) : 500;
|
|
5372
|
+
const statusCode = classified !== 500 ? classified : settledReason ? 400 : 502;
|
|
5373
|
+
const usageLimited = statusCode === 429;
|
|
5374
|
+
const retryAfterSeconds = usageLimited && responseRetryAfterSeconds(event) !== void 0 ? clampRetryAfterSeconds(responseRetryAfterSeconds(event)) : void 0;
|
|
5375
|
+
failContext(
|
|
5376
|
+
entry,
|
|
5377
|
+
ctx,
|
|
5378
|
+
retryAfterSeconds === void 0 ? summary : `${summary}; retry after ${retryAfterSeconds}s`,
|
|
5379
|
+
{
|
|
5380
|
+
source: "empty_failure_terminal",
|
|
5381
|
+
upstreamEventType: type,
|
|
5382
|
+
...details,
|
|
5383
|
+
// Under DISTINCT keys. `failContext` fingerprints the message it was
|
|
5384
|
+
// given after spreading these, so an `errorMessage*` pair here is
|
|
5385
|
+
// overwritten by the summary's — which would silently discard the only
|
|
5386
|
+
// content-free evidence of what upstream actually said, and leave two
|
|
5387
|
+
// failures with the same type and code indistinguishable. `errorMessage*`
|
|
5388
|
+
// now means "what the client was told", `upstreamMessage*` means "what
|
|
5389
|
+
// upstream said", and both survive.
|
|
5390
|
+
...diagnosticTextFingerprint("upstreamMessage", responseErrorMessage(event))
|
|
5391
|
+
},
|
|
5392
|
+
statusCode,
|
|
5393
|
+
retryAfterSeconds
|
|
5394
|
+
);
|
|
5395
|
+
return;
|
|
5396
|
+
}
|
|
5313
5397
|
ctx.pendingEvents.push(event);
|
|
5314
5398
|
if (isModelDataEvent(type)) flushPending(ctx);
|
|
5315
5399
|
if (TERMINAL_EVENT_TYPES.has(type ?? "") || type === "error") {
|
|
@@ -5805,6 +5889,63 @@ function isCredentialBearingHeader(name) {
|
|
|
5805
5889
|
return CREDENTIAL_BEARING_HEADER.test(name);
|
|
5806
5890
|
}
|
|
5807
5891
|
|
|
5892
|
+
// src/model-runtime-compatibility.ts
|
|
5893
|
+
function remapMaxTokensField(body, field) {
|
|
5894
|
+
if (field === "max_tokens") {
|
|
5895
|
+
if (body.max_tokens === void 0 && body.max_completion_tokens !== void 0) {
|
|
5896
|
+
body.max_tokens = body.max_completion_tokens;
|
|
5897
|
+
}
|
|
5898
|
+
delete body.max_completion_tokens;
|
|
5899
|
+
return;
|
|
5900
|
+
}
|
|
5901
|
+
if (field === "max_completion_tokens") {
|
|
5902
|
+
if (body.max_completion_tokens === void 0 && body.max_tokens !== void 0) {
|
|
5903
|
+
body.max_completion_tokens = body.max_tokens;
|
|
5904
|
+
}
|
|
5905
|
+
delete body.max_tokens;
|
|
5906
|
+
}
|
|
5907
|
+
}
|
|
5908
|
+
function transformMessages(messages, compatibility) {
|
|
5909
|
+
if (!Array.isArray(messages)) return messages;
|
|
5910
|
+
const rewriteDeveloper = compatibility.supportsDeveloperRole === false;
|
|
5911
|
+
const replayReasoning = compatibility.requiresReasoningContentOnAssistantMessages === true;
|
|
5912
|
+
if (!rewriteDeveloper && !replayReasoning) return messages;
|
|
5913
|
+
let changed = false;
|
|
5914
|
+
const transformed = messages.map((message) => {
|
|
5915
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) return message;
|
|
5916
|
+
const source = message;
|
|
5917
|
+
const role = source.role;
|
|
5918
|
+
const needsRoleRewrite = rewriteDeveloper && role === "developer";
|
|
5919
|
+
const needsReasoningReplay = replayReasoning && role === "assistant" && !Object.prototype.hasOwnProperty.call(source, "reasoning_content");
|
|
5920
|
+
if (!needsRoleRewrite && !needsReasoningReplay) return message;
|
|
5921
|
+
changed = true;
|
|
5922
|
+
return {
|
|
5923
|
+
...source,
|
|
5924
|
+
...needsRoleRewrite ? { role: "system" } : {},
|
|
5925
|
+
...needsReasoningReplay ? { reasoning_content: "" } : {}
|
|
5926
|
+
};
|
|
5927
|
+
});
|
|
5928
|
+
return changed ? transformed : messages;
|
|
5929
|
+
}
|
|
5930
|
+
function transformOpenAiCompatibleRequestBody(body, compatibility) {
|
|
5931
|
+
const transformed = { ...body };
|
|
5932
|
+
if (compatibility.supportsStore === false) delete transformed.store;
|
|
5933
|
+
if (compatibility.supportsLongCacheRetention === false) {
|
|
5934
|
+
delete transformed.prompt_cache_retention;
|
|
5935
|
+
delete transformed.promptCacheRetention;
|
|
5936
|
+
}
|
|
5937
|
+
remapMaxTokensField(transformed, compatibility.maxTokensField);
|
|
5938
|
+
const messages = transformMessages(transformed.messages, compatibility);
|
|
5939
|
+
if (messages !== transformed.messages) transformed.messages = messages;
|
|
5940
|
+
const hasReasoningEffort = typeof transformed.reasoning_effort === "string" && transformed.reasoning_effort.trim().length > 0;
|
|
5941
|
+
if (hasReasoningEffort && compatibility.thinkingFormat === "deepseek") {
|
|
5942
|
+
if (transformed.thinking === void 0) transformed.thinking = { type: "enabled" };
|
|
5943
|
+
} else if (hasReasoningEffort && compatibility.thinkingFormat === "qwen") {
|
|
5944
|
+
if (transformed.enable_thinking === void 0) transformed.enable_thinking = true;
|
|
5945
|
+
}
|
|
5946
|
+
return transformed;
|
|
5947
|
+
}
|
|
5948
|
+
|
|
5808
5949
|
// src/provider-factory.ts
|
|
5809
5950
|
var RESPONSES_ONLY_PREFIXES = [
|
|
5810
5951
|
"gpt-5-codex",
|
|
@@ -5947,7 +6088,10 @@ async function createLanguageModel(spec) {
|
|
|
5947
6088
|
baseURL: baseURL ?? "",
|
|
5948
6089
|
...spec.authType !== "none" && apiKey.trim() ? { apiKey } : {},
|
|
5949
6090
|
...spec.authType === "none" ? { fetch: fetchWithoutCredentialHeaders } : {},
|
|
5950
|
-
...spec.headers ? { headers: spec.headers } : {}
|
|
6091
|
+
...spec.headers ? { headers: spec.headers } : {},
|
|
6092
|
+
...spec.compatibility ? {
|
|
6093
|
+
transformRequestBody: (body) => transformOpenAiCompatibleRequestBody(body, spec.compatibility)
|
|
6094
|
+
} : {}
|
|
5951
6095
|
};
|
|
5952
6096
|
model = createOpenAICompatible({
|
|
5953
6097
|
...options
|
|
@@ -6203,8 +6347,68 @@ function mapCodexEffortToGeminiBudget(effort) {
|
|
|
6203
6347
|
if (!level) return void 0;
|
|
6204
6348
|
return GEMINI_25_BUDGETS[level];
|
|
6205
6349
|
}
|
|
6350
|
+
function compatibilityReasoningCapabilities(metadata) {
|
|
6351
|
+
const compatibility = metadata?.compatibility;
|
|
6352
|
+
if (!compatibility) return void 0;
|
|
6353
|
+
if (compatibility.supportsReasoningEffort === false) {
|
|
6354
|
+
return metadata?.reasoning === false ? EMPTY_REASONING : {
|
|
6355
|
+
...EMPTY_REASONING,
|
|
6356
|
+
mode: "internal-only",
|
|
6357
|
+
source: "provider-metadata",
|
|
6358
|
+
confidence: "documented"
|
|
6359
|
+
};
|
|
6360
|
+
}
|
|
6361
|
+
if (compatibility.reasoningEffortMap) {
|
|
6362
|
+
const levels = Object.entries(compatibility.reasoningEffortMap).filter(([, mapped]) => mapped !== null).map(([level]) => level);
|
|
6363
|
+
if (levels.length === 0) {
|
|
6364
|
+
return metadata?.reasoning === false ? EMPTY_REASONING : {
|
|
6365
|
+
...EMPTY_REASONING,
|
|
6366
|
+
mode: "internal-only",
|
|
6367
|
+
source: "provider-metadata",
|
|
6368
|
+
confidence: "documented"
|
|
6369
|
+
};
|
|
6370
|
+
}
|
|
6371
|
+
const preferredDefault = ["medium", "high", "max", "low"].find((level) => levels.includes(level));
|
|
6372
|
+
return {
|
|
6373
|
+
levels,
|
|
6374
|
+
defaultLevel: preferredDefault ?? levels[0],
|
|
6375
|
+
supportsSummaries: false,
|
|
6376
|
+
mode: "controllable",
|
|
6377
|
+
source: "provider-metadata",
|
|
6378
|
+
confidence: "documented",
|
|
6379
|
+
wireFormat: compatibility.thinkingFormat === "deepseek" ? { kind: "deepseek-thinking" } : { kind: "openai-reasoning-effort" }
|
|
6380
|
+
};
|
|
6381
|
+
}
|
|
6382
|
+
if (compatibility.supportsReasoningEffort === true || compatibility.thinkingFormat !== void 0) {
|
|
6383
|
+
if (metadata?.reasoning === false) return EMPTY_REASONING;
|
|
6384
|
+
return {
|
|
6385
|
+
levels: ["low", "medium", "high"],
|
|
6386
|
+
defaultLevel: "medium",
|
|
6387
|
+
supportsSummaries: false,
|
|
6388
|
+
mode: "controllable",
|
|
6389
|
+
source: "provider-metadata",
|
|
6390
|
+
confidence: "documented",
|
|
6391
|
+
wireFormat: compatibility.thinkingFormat === "deepseek" ? { kind: "deepseek-thinking" } : { kind: "openai-reasoning-effort" }
|
|
6392
|
+
};
|
|
6393
|
+
}
|
|
6394
|
+
return void 0;
|
|
6395
|
+
}
|
|
6396
|
+
function compatibilityReasoningEffort(effort, modelId, compatibility) {
|
|
6397
|
+
if (compatibility.supportsReasoningEffort === false) return void 0;
|
|
6398
|
+
const map = compatibility.reasoningEffortMap;
|
|
6399
|
+
if (map) {
|
|
6400
|
+
if (!Object.prototype.hasOwnProperty.call(map, effort)) return void 0;
|
|
6401
|
+
return map[effort] ?? void 0;
|
|
6402
|
+
}
|
|
6403
|
+
if (compatibility.supportsReasoningEffort === true || compatibility.thinkingFormat !== void 0) {
|
|
6404
|
+
return mapCodexEffortToOpenAI(effort, modelId);
|
|
6405
|
+
}
|
|
6406
|
+
return void 0;
|
|
6407
|
+
}
|
|
6206
6408
|
function getReasoningCapabilities(npm, modelId, metadata) {
|
|
6207
6409
|
const id = modelId.toLowerCase();
|
|
6410
|
+
const compatibilityCapabilities = compatibilityReasoningCapabilities(metadata);
|
|
6411
|
+
if (compatibilityCapabilities) return compatibilityCapabilities;
|
|
6208
6412
|
if (isOpenRouterRoute(npm, metadata)) {
|
|
6209
6413
|
return openRouterReasoningCapabilities(metadata);
|
|
6210
6414
|
}
|
|
@@ -6350,7 +6554,10 @@ function getReasoningCapabilities(npm, modelId, metadata) {
|
|
|
6350
6554
|
return EMPTY_REASONING;
|
|
6351
6555
|
}
|
|
6352
6556
|
function getPatchReasoningCapabilities(npm, modelId, metadata) {
|
|
6353
|
-
if (metadata?.
|
|
6557
|
+
if (metadata?.compatibility?.supportsReasoningEffort === false) {
|
|
6558
|
+
return compatibilityReasoningCapabilities(metadata) ?? EMPTY_REASONING;
|
|
6559
|
+
}
|
|
6560
|
+
if (metadata?.reasoning === false && !metadata?.compatibility?.reasoningEffortMap && !hasSupportedParameter(metadata, "reasoning_effort") && !hasSupportedParameter(metadata, "reasoning")) {
|
|
6354
6561
|
return EMPTY_REASONING;
|
|
6355
6562
|
}
|
|
6356
6563
|
const capabilities = getReasoningCapabilities(npm, modelId, metadata);
|
|
@@ -6369,11 +6576,22 @@ function getPatchReasoningCapabilities(npm, modelId, metadata) {
|
|
|
6369
6576
|
}
|
|
6370
6577
|
function effortProviderOptions(npm, effort, modelId, metadata) {
|
|
6371
6578
|
if (!effort) return void 0;
|
|
6579
|
+
if (npm === "@ai-sdk/openai-compatible" && modelId && metadata?.compatibility) {
|
|
6580
|
+
const reasoningEffort = compatibilityReasoningEffort(
|
|
6581
|
+
effort,
|
|
6582
|
+
modelId,
|
|
6583
|
+
metadata.compatibility
|
|
6584
|
+
);
|
|
6585
|
+
if (!reasoningEffort) return void 0;
|
|
6586
|
+
const key = metadata.providerId ? toCamelCase(metadata.providerId) : "openaiCompatible";
|
|
6587
|
+
return { [key]: { reasoningEffort } };
|
|
6588
|
+
}
|
|
6372
6589
|
if (isOpenRouterRoute(npm, metadata)) {
|
|
6373
6590
|
const caps = openRouterReasoningCapabilities(metadata);
|
|
6374
6591
|
if (caps.mode !== "controllable") return void 0;
|
|
6375
6592
|
const allowed = new Set(OPENROUTER_EFFORT_LEVELS);
|
|
6376
|
-
const
|
|
6593
|
+
const candidate = effort;
|
|
6594
|
+
const mapped = allowed.has(candidate) ? candidate : candidate === "max" ? "xhigh" : void 0;
|
|
6377
6595
|
return mapped ? { openrouter: { reasoning: { effort: mapped, exclude: false } } } : void 0;
|
|
6378
6596
|
}
|
|
6379
6597
|
if (npm === "@ai-sdk/openai" || npm === "@ai-sdk/azure") {
|
|
@@ -6433,7 +6651,8 @@ function effortProviderOptions(npm, effort, modelId, metadata) {
|
|
|
6433
6651
|
}
|
|
6434
6652
|
if (hasSupportedParameter(metadata, "reasoning")) {
|
|
6435
6653
|
const allowed = new Set(OPENROUTER_EFFORT_LEVELS);
|
|
6436
|
-
const
|
|
6654
|
+
const candidate = effort;
|
|
6655
|
+
const mapped = allowed.has(candidate) ? candidate : candidate === "max" ? "xhigh" : void 0;
|
|
6437
6656
|
return mapped ? { openrouter: { reasoning: { effort: mapped, exclude: false } } } : void 0;
|
|
6438
6657
|
}
|
|
6439
6658
|
return void 0;
|
|
@@ -7243,6 +7462,62 @@ function parseModelList(body, npm) {
|
|
|
7243
7462
|
}
|
|
7244
7463
|
return models;
|
|
7245
7464
|
}
|
|
7465
|
+
function materializeTemplateModel(template, model, baseUrl) {
|
|
7466
|
+
const npm = model.npm ?? template.npm;
|
|
7467
|
+
const { id, upstreamModelId: normalizedUpstream } = normalizeGoogleModelId(model.id, npm);
|
|
7468
|
+
const family = model.family ?? (id.split(/[-/:]/)[0] ?? id);
|
|
7469
|
+
const freeStatus = model.freeStatus ?? classifyFreeStatus({ model });
|
|
7470
|
+
return {
|
|
7471
|
+
...model,
|
|
7472
|
+
id,
|
|
7473
|
+
name: normalizeGoogleDisplayName(model.name, id),
|
|
7474
|
+
upstreamModelId: model.upstreamModelId ?? normalizedUpstream,
|
|
7475
|
+
family,
|
|
7476
|
+
brand: model.brand ?? deriveBrand(family),
|
|
7477
|
+
contextWindow: model.contextWindow ?? resolveContextWindow(id),
|
|
7478
|
+
isFree: model.isFree ?? isFreeStatus(freeStatus),
|
|
7479
|
+
freeStatus,
|
|
7480
|
+
modelFormat: model.modelFormat ?? modelFormatForNpm(npm),
|
|
7481
|
+
npm,
|
|
7482
|
+
apiUrl: model.apiUrl ?? baseUrl
|
|
7483
|
+
};
|
|
7484
|
+
}
|
|
7485
|
+
function normalizeTemplateOverlay(template, model) {
|
|
7486
|
+
const npm = model.npm ?? template.npm;
|
|
7487
|
+
const { id } = normalizeGoogleModelId(model.id, npm);
|
|
7488
|
+
const family = model.family;
|
|
7489
|
+
const hasFreeMetadata = model.cost !== void 0 || model.isFree !== void 0 || model.freeStatus !== void 0;
|
|
7490
|
+
const freeStatus = hasFreeMetadata ? model.freeStatus ?? classifyFreeStatus({ model }) : void 0;
|
|
7491
|
+
return {
|
|
7492
|
+
...model,
|
|
7493
|
+
id,
|
|
7494
|
+
name: normalizeGoogleDisplayName(model.name, id),
|
|
7495
|
+
...model.upstreamModelId !== void 0 ? { upstreamModelId: normalizeGoogleModelId(model.upstreamModelId, npm).upstreamModelId } : {},
|
|
7496
|
+
...model.npm !== void 0 ? { npm } : {},
|
|
7497
|
+
...model.modelFormat !== void 0 ? { modelFormat: model.modelFormat } : model.npm !== void 0 ? { modelFormat: modelFormatForNpm(npm) } : {},
|
|
7498
|
+
...family !== void 0 ? { family, brand: model.brand ?? deriveBrand(family) } : {},
|
|
7499
|
+
...freeStatus !== void 0 ? {
|
|
7500
|
+
freeStatus,
|
|
7501
|
+
isFree: model.isFree ?? isFreeStatus(freeStatus)
|
|
7502
|
+
} : {}
|
|
7503
|
+
};
|
|
7504
|
+
}
|
|
7505
|
+
function applyTemplateModelMetadata(template, discovered, _baseUrl) {
|
|
7506
|
+
const curated = new Map(
|
|
7507
|
+
(template.staticModels ?? []).map((model) => normalizeTemplateOverlay(template, model)).map((model) => [model.id, model])
|
|
7508
|
+
);
|
|
7509
|
+
const visible = template.staticModelPolicy === "allowlist" ? discovered.filter((model) => curated.has(model.id)) : discovered;
|
|
7510
|
+
return visible.map((model) => {
|
|
7511
|
+
const overlay = curated.get(model.id);
|
|
7512
|
+
if (!overlay) return model;
|
|
7513
|
+
return {
|
|
7514
|
+
...model,
|
|
7515
|
+
...overlay,
|
|
7516
|
+
id: model.id,
|
|
7517
|
+
upstreamModelId: overlay.upstreamModelId ?? model.upstreamModelId
|
|
7518
|
+
};
|
|
7519
|
+
});
|
|
7520
|
+
}
|
|
7246
7521
|
async function fetchTemplateModels(template, apiKey, baseUrlOverride, extraHeaders) {
|
|
7247
7522
|
const trimmedOverride = baseUrlOverride?.trim();
|
|
7248
7523
|
const baseUrl = (trimmedOverride || template.defaultBaseUrl)?.replace(/\/$/, "");
|
|
@@ -7254,19 +7529,7 @@ async function fetchTemplateModels(template, apiKey, baseUrlOverride, extraHeade
|
|
|
7254
7529
|
};
|
|
7255
7530
|
}
|
|
7256
7531
|
if (template.modelSource === "static-seed") {
|
|
7257
|
-
const models = (template.staticModels
|
|
7258
|
-
const family = sm.id.split(/[-/:]/)[0] ?? sm.id;
|
|
7259
|
-
return {
|
|
7260
|
-
id: sm.id,
|
|
7261
|
-
name: sm.name,
|
|
7262
|
-
upstreamModelId: sm.id,
|
|
7263
|
-
family,
|
|
7264
|
-
brand: deriveBrand(family),
|
|
7265
|
-
contextWindow: resolveContextWindow(sm.id),
|
|
7266
|
-
modelFormat: modelFormatForNpm(template.npm),
|
|
7267
|
-
npm: template.npm
|
|
7268
|
-
};
|
|
7269
|
-
});
|
|
7532
|
+
const models = (template.staticModels ?? []).map((model) => materializeTemplateModel(template, model, baseUrl));
|
|
7270
7533
|
return { models, baseUrl };
|
|
7271
7534
|
}
|
|
7272
7535
|
const url = modelsUrl(baseUrl, template);
|
|
@@ -7336,7 +7599,7 @@ async function fetchTemplateModels(template, apiKey, baseUrlOverride, extraHeade
|
|
|
7336
7599
|
}
|
|
7337
7600
|
} catch {
|
|
7338
7601
|
}
|
|
7339
|
-
const models = parseModelList(json, template.npm);
|
|
7602
|
+
const models = applyTemplateModelMetadata(template, parseModelList(json, template.npm), baseUrl);
|
|
7340
7603
|
if (models.length === 0) {
|
|
7341
7604
|
return {
|
|
7342
7605
|
models: [],
|
|
@@ -7424,11 +7687,11 @@ async function addProviderFromTemplate(template, apiKey, opts) {
|
|
|
7424
7687
|
}
|
|
7425
7688
|
const pricingCache = loadPricingCache();
|
|
7426
7689
|
const platform = pricingPlatformForProvider(template.id, template.id);
|
|
7427
|
-
const
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
);
|
|
7690
|
+
const discoveredModels = usableModels.map((m) => ({
|
|
7691
|
+
...m,
|
|
7692
|
+
apiUrl: m.apiUrl ?? fetched.baseUrl
|
|
7693
|
+
}));
|
|
7694
|
+
const pricedModels = template.preserveModelPricing ? discoveredModels : enrichModelsWithPricing(discoveredModels, buildPricingIndex(pricingCache), platform);
|
|
7432
7695
|
const account = `provider:${template.id}`;
|
|
7433
7696
|
const result = await withProviderMutationLock(template.id, async () => {
|
|
7434
7697
|
const currentState = await withRegistryWriteLock(() => {
|
|
@@ -7485,6 +7748,7 @@ async function addProviderFromTemplate(template, apiKey, opts) {
|
|
|
7485
7748
|
enabled: true,
|
|
7486
7749
|
authRef,
|
|
7487
7750
|
authType: trimmedKey ? template.authType : "none",
|
|
7751
|
+
...template.preserveModelPricing ? { preserveModelPricing: true } : {},
|
|
7488
7752
|
...!trimmedKey && template.anonymousFreeModels ? { subscriptionFilter: "free" } : {},
|
|
7489
7753
|
api: {
|
|
7490
7754
|
npm: template.npm,
|
|
@@ -8087,7 +8351,7 @@ async function refreshApiListProvider(provider, apiKey) {
|
|
|
8087
8351
|
return { models: [], error: fetched2.error ?? "No models returned.", baseUrl: fetched2.baseUrl };
|
|
8088
8352
|
}
|
|
8089
8353
|
return {
|
|
8090
|
-
models: fetched2.models.map((m) => ({ ...m, apiUrl: fetched2.baseUrl })),
|
|
8354
|
+
models: fetched2.models.map((m) => ({ ...m, apiUrl: m.apiUrl ?? fetched2.baseUrl })),
|
|
8091
8355
|
baseUrl: fetched2.baseUrl
|
|
8092
8356
|
};
|
|
8093
8357
|
}
|
|
@@ -8106,7 +8370,7 @@ async function refreshApiListProvider(provider, apiKey) {
|
|
|
8106
8370
|
return {
|
|
8107
8371
|
models: usableModels.map((m) => ({
|
|
8108
8372
|
...m,
|
|
8109
|
-
apiUrl: fetched.baseUrl
|
|
8373
|
+
apiUrl: m.apiUrl ?? fetched.baseUrl
|
|
8110
8374
|
})),
|
|
8111
8375
|
baseUrl: fetched.baseUrl
|
|
8112
8376
|
};
|
|
@@ -8220,7 +8484,7 @@ async function refreshProviderModels(providerId, apiKey, registry) {
|
|
|
8220
8484
|
}
|
|
8221
8485
|
const pricingCache = loadPricingCache();
|
|
8222
8486
|
const platform = pricingPlatformForProvider(provider.templateId, provider.id);
|
|
8223
|
-
const enriched = enrichModelsWithPricing(models, buildPricingIndex(pricingCache), platform);
|
|
8487
|
+
const enriched = provider.preserveModelPricing ? models : enrichModelsWithPricing(models, buildPricingIndex(pricingCache), platform);
|
|
8224
8488
|
await withRegistryWriteLock(() => {
|
|
8225
8489
|
const currentRegistry = loadRegistryStrict();
|
|
8226
8490
|
const currentProvider = currentRegistry.providers.find((candidate) => candidate.id === providerId);
|
|
@@ -9462,7 +9726,8 @@ function localModelToRoute(lp, model) {
|
|
|
9462
9726
|
reasoning: model.reasoning,
|
|
9463
9727
|
interleavedReasoningField: model.interleavedReasoningField,
|
|
9464
9728
|
useResponsesLite: model.useResponsesLite,
|
|
9465
|
-
preferWebSockets: model.preferWebSockets
|
|
9729
|
+
preferWebSockets: model.preferWebSockets,
|
|
9730
|
+
compatibility: model.compatibility
|
|
9466
9731
|
};
|
|
9467
9732
|
}
|
|
9468
9733
|
function makeRouteResolver(localProviders) {
|
|
@@ -9535,7 +9800,8 @@ function buildHttpProxyRoutes(providers, favorites, modelAliases = void 0, max =
|
|
|
9535
9800
|
unavailable.push(favorite);
|
|
9536
9801
|
continue;
|
|
9537
9802
|
}
|
|
9538
|
-
|
|
9803
|
+
const supported = model.modelFormat === "anthropic" ? Boolean(model.baseUrl) : isSdkMigratedNpm(model.npm);
|
|
9804
|
+
if (!supported) {
|
|
9539
9805
|
unsupported.push(favorite);
|
|
9540
9806
|
continue;
|
|
9541
9807
|
}
|
|
@@ -9708,7 +9974,8 @@ function routeUnavailableMessage(modelId, reason) {
|
|
|
9708
9974
|
}
|
|
9709
9975
|
|
|
9710
9976
|
// src/upstream-forward.ts
|
|
9711
|
-
import { Readable } from "stream";
|
|
9977
|
+
import { Readable, Transform } from "stream";
|
|
9978
|
+
import { StringDecoder } from "string_decoder";
|
|
9712
9979
|
|
|
9713
9980
|
// src/server/auth.ts
|
|
9714
9981
|
function sanitizeCredential(value) {
|
|
@@ -9789,6 +10056,34 @@ async function fetchWithOAuthRetry(apiKey, request3, refreshToken) {
|
|
|
9789
10056
|
response = await request3(refreshed);
|
|
9790
10057
|
return { response, apiKey: refreshed, refreshed: true };
|
|
9791
10058
|
}
|
|
10059
|
+
function anthropicSseModelRewrite(override) {
|
|
10060
|
+
const decoder = new StringDecoder("utf8");
|
|
10061
|
+
let tail = "";
|
|
10062
|
+
const rewriteLine = (line) => {
|
|
10063
|
+
if (!line.startsWith("data:") || !line.includes('"message_start"')) return line;
|
|
10064
|
+
try {
|
|
10065
|
+
const parsed = JSON.parse(line.slice(5));
|
|
10066
|
+
if (parsed.type === "message_start" && parsed.message && typeof parsed.message.model === "string") {
|
|
10067
|
+
parsed.message.model = override;
|
|
10068
|
+
return "data: " + JSON.stringify(parsed);
|
|
10069
|
+
}
|
|
10070
|
+
} catch {
|
|
10071
|
+
}
|
|
10072
|
+
return line;
|
|
10073
|
+
};
|
|
10074
|
+
return new Transform({
|
|
10075
|
+
transform(chunk, _encoding, callback) {
|
|
10076
|
+
const lines = (tail + decoder.write(chunk)).split("\n");
|
|
10077
|
+
tail = lines.pop() ?? "";
|
|
10078
|
+
const rewritten = lines.map(rewriteLine);
|
|
10079
|
+
callback(null, rewritten.length ? rewritten.join("\n") + "\n" : "");
|
|
10080
|
+
},
|
|
10081
|
+
flush(callback) {
|
|
10082
|
+
const rest = tail + decoder.end();
|
|
10083
|
+
callback(null, rest ? rewriteLine(rest) : "");
|
|
10084
|
+
}
|
|
10085
|
+
});
|
|
10086
|
+
}
|
|
9792
10087
|
async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWantsStream, options = {}) {
|
|
9793
10088
|
const doFetch = (key) => fetch(messagesUrl, {
|
|
9794
10089
|
method: "POST",
|
|
@@ -9825,7 +10120,12 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
9825
10120
|
"Cache-Control": "no-cache",
|
|
9826
10121
|
"Connection": "keep-alive"
|
|
9827
10122
|
});
|
|
9828
|
-
Readable.fromWeb(upstreamRes.body).on("error", () => res.destroy())
|
|
10123
|
+
const upstream = Readable.fromWeb(upstreamRes.body).on("error", () => res.destroy());
|
|
10124
|
+
if (options.responseModelOverride) {
|
|
10125
|
+
upstream.pipe(anthropicSseModelRewrite(options.responseModelOverride)).on("error", () => res.destroy()).pipe(res);
|
|
10126
|
+
} else {
|
|
10127
|
+
upstream.pipe(res);
|
|
10128
|
+
}
|
|
9829
10129
|
return;
|
|
9830
10130
|
}
|
|
9831
10131
|
if (!upstreamRes.body) {
|
|
@@ -9833,14 +10133,19 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
9833
10133
|
res.end(JSON.stringify({ type: "error", error: { type: "api_error", message: "Upstream returned empty response body" } }));
|
|
9834
10134
|
return;
|
|
9835
10135
|
}
|
|
9836
|
-
|
|
10136
|
+
let text4 = await upstreamRes.text();
|
|
10137
|
+
let parsed;
|
|
9837
10138
|
try {
|
|
9838
|
-
JSON.parse(text4);
|
|
10139
|
+
parsed = JSON.parse(text4);
|
|
9839
10140
|
} catch {
|
|
9840
10141
|
res.writeHead(502, { "Content-Type": "application/json" });
|
|
9841
10142
|
res.end(JSON.stringify({ type: "error", error: { type: "api_error", message: "Upstream response was not valid JSON" } }));
|
|
9842
10143
|
return;
|
|
9843
10144
|
}
|
|
10145
|
+
if (options.responseModelOverride && parsed && typeof parsed === "object" && !Array.isArray(parsed) && typeof parsed.model === "string") {
|
|
10146
|
+
parsed.model = options.responseModelOverride;
|
|
10147
|
+
text4 = JSON.stringify(parsed);
|
|
10148
|
+
}
|
|
9844
10149
|
res.writeHead(200, {
|
|
9845
10150
|
"Content-Type": "application/json",
|
|
9846
10151
|
"Content-Length": Buffer.byteLength(text4).toString()
|
|
@@ -11102,6 +11407,10 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
11102
11407
|
route.apiKey = refreshed;
|
|
11103
11408
|
},
|
|
11104
11409
|
signal: clientAbort.signal,
|
|
11410
|
+
// A route selected through a clodex: id or short alias must echo the
|
|
11411
|
+
// exact requested id back, or patched Claude Code misses the alias
|
|
11412
|
+
// context-window key and can skip auto-compaction.
|
|
11413
|
+
responseModelOverride: typeof originalModel === "string" && originalModel !== route.realModelId ? originalModel : void 0,
|
|
11105
11414
|
onUpstreamError: inferenceLogPath ? (statusCode, errorContent) => writeInferenceResponseErrorLog(inferenceLogPath, {
|
|
11106
11415
|
modelId: originalModel,
|
|
11107
11416
|
provider: route.providerId ?? route.aliasId.split(":")[1] ?? "unknown",
|
|
@@ -11140,6 +11449,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
11140
11449
|
supportedParameters: route.supportedParameters,
|
|
11141
11450
|
reasoning: route.reasoning,
|
|
11142
11451
|
interleavedReasoningField: route.interleavedReasoningField,
|
|
11452
|
+
compatibility: route.compatibility,
|
|
11143
11453
|
upstreamModelId: route.realModelId
|
|
11144
11454
|
}
|
|
11145
11455
|
});
|
|
@@ -11158,6 +11468,7 @@ async function startProxyCatalog(routes, defaultAliasId, debug = false, inferenc
|
|
|
11158
11468
|
headers: route.headers,
|
|
11159
11469
|
useResponsesLite: route.useResponsesLite,
|
|
11160
11470
|
preferWebSockets: route.preferWebSockets,
|
|
11471
|
+
compatibility: route.compatibility,
|
|
11161
11472
|
onDebug: (msg) => plog(() => msg),
|
|
11162
11473
|
onWebSocketDiagnostic: webSocketDiagnosticsLogPath ? (event) => writeWebSocketDiagnosticLog(webSocketDiagnosticsLogPath, event) : void 0
|
|
11163
11474
|
});
|
|
@@ -11367,6 +11678,7 @@ function startProxy(completionsUrl, modelId, debug = false, contextWindow, sdk,
|
|
|
11367
11678
|
interleavedReasoningField: sdk?.interleavedReasoningField,
|
|
11368
11679
|
useResponsesLite: sdk?.useResponsesLite,
|
|
11369
11680
|
preferWebSockets: sdk?.preferWebSockets,
|
|
11681
|
+
compatibility: sdk?.compatibility,
|
|
11370
11682
|
headers: sdk?.headers
|
|
11371
11683
|
}], clientModelId, debug);
|
|
11372
11684
|
}
|
|
@@ -11922,6 +12234,9 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
|
11922
12234
|
onTokenRefreshed: (refreshed) => {
|
|
11923
12235
|
model.apiKey = refreshed;
|
|
11924
12236
|
},
|
|
12237
|
+
// Echo the exact requested id when it differs from the upstream id, so
|
|
12238
|
+
// clients that key context windows on the response model still resolve.
|
|
12239
|
+
responseModelOverride: typeof body.model === "string" && body.model !== upstreamModelId(model) ? body.model : void 0,
|
|
11925
12240
|
onUpstreamError: options.inferenceLogPath ? (statusCode, errorContent) => writeInferenceResponseErrorLog(options.inferenceLogPath, {
|
|
11926
12241
|
requestId,
|
|
11927
12242
|
modelId: body.model,
|
|
@@ -11972,6 +12287,7 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
|
11972
12287
|
supportedParameters: model.supportedParameters,
|
|
11973
12288
|
reasoning: model.reasoning,
|
|
11974
12289
|
interleavedReasoningField: model.interleavedReasoningField,
|
|
12290
|
+
compatibility: model.compatibility,
|
|
11975
12291
|
upstreamModelId: upstreamModelId(model)
|
|
11976
12292
|
},
|
|
11977
12293
|
maxTools: npmMaxTools
|
|
@@ -12252,6 +12568,7 @@ async function getOrInitLanguageModel(modelCache, model, npm, baseURL, apiKey, w
|
|
|
12252
12568
|
headers: model.headers,
|
|
12253
12569
|
useResponsesLite: model.useResponsesLite,
|
|
12254
12570
|
preferWebSockets: model.preferWebSockets,
|
|
12571
|
+
compatibility: model.compatibility,
|
|
12255
12572
|
onWebSocketDiagnostic: webSocketDiagnosticsLogPath ? (event) => writeWebSocketDiagnosticLog(webSocketDiagnosticsLogPath, event) : void 0
|
|
12256
12573
|
});
|
|
12257
12574
|
cached = { apiKey, languageModel };
|
|
@@ -13595,7 +13912,8 @@ function enrichServerModelReasoning(model) {
|
|
|
13595
13912
|
apiBaseUrl: model.apiBaseUrl,
|
|
13596
13913
|
supportedParameters: model.supportedParameters,
|
|
13597
13914
|
reasoning: model.reasoning,
|
|
13598
|
-
interleavedReasoningField: model.interleavedReasoningField
|
|
13915
|
+
interleavedReasoningField: model.interleavedReasoningField,
|
|
13916
|
+
compatibility: model.compatibility
|
|
13599
13917
|
});
|
|
13600
13918
|
if (!caps.defaultLevel) return model;
|
|
13601
13919
|
return { ...model, defaultEffort: caps.defaultLevel };
|
|
@@ -15091,6 +15409,7 @@ function buildDesiredPatchConfig() {
|
|
|
15091
15409
|
supportedParameters: model.supportedParameters,
|
|
15092
15410
|
reasoning: model.reasoning ?? modelsDev?.reasoning,
|
|
15093
15411
|
interleavedReasoningField: model.interleavedReasoningField ?? modelsDev?.interleaved?.field,
|
|
15412
|
+
compatibility: model.compatibility,
|
|
15094
15413
|
upstreamModelId: upstreamModelId2
|
|
15095
15414
|
});
|
|
15096
15415
|
meta.set(`${provider.id}:${model.id}`, {
|
|
@@ -16730,6 +17049,7 @@ Error: ${launchPlan.error}
|
|
|
16730
17049
|
interleavedReasoningField: selectedModel.interleavedReasoningField,
|
|
16731
17050
|
useResponsesLite: selectedModel.useResponsesLite,
|
|
16732
17051
|
preferWebSockets: selectedModel.preferWebSockets,
|
|
17052
|
+
compatibility: selectedModel.compatibility,
|
|
16733
17053
|
headers: activeProvider.headers
|
|
16734
17054
|
},
|
|
16735
17055
|
launchApiKey ?? ""
|