openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
187 lines (186 loc) • 8.16 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs";
import { d as asPositiveSafeInteger } from "./number-coercion-CuVhdCBs.mjs";
import { a as asOptionalRecord } from "./record-coerce-TIsquoVR.mjs";
import { n as filterStringEntries } from "./string-normalization-CECOqf64.mjs";
import { t as normalizeOpenRouterModelReasoning } from "./model-catalog-reasoning-BFwHFb-5.mjs";
import { i as normalizeOpenRouterModelPricing } from "./model-catalog-pricing-C9-7BMLS.mjs";
import { g as ssrfPolicyFromHttpBaseUrlAllowedOrigin } from "./ssrf-v45DW-yR.mjs";
import { i as fetchWithSsrFGuard } from "./fetch-guard-DI6biu8d.mjs";
import { l as normalizeBaseUrl, p as sanitizeConfiguredModelProviderRequest } from "./provider-request-config-By9R9pyt.mjs";
import "./string-coerce-runtime-50qKeWeL.mjs";
import { h as resolveProviderHttpRequestConfig } from "./shared-DcnGQdHV.mjs";
import "./model-catalog-pricing-DEK-l_a-.mjs";
import "./ssrf-runtime-BzkFo_VJ.mjs";
import "./provider-http-CuRGJD0L.mjs";
import { t as buildLiveModelProviderConfig } from "./provider-catalog-live-runtime-Cii7Axx2.mjs";
import "./provider-catalog-shared-Bd3PD4e1.mjs";
import { t as OPENROUTER_BASE_URL } from "./provider-defaults-ilQX6q3J.mjs";
//#region extensions/openrouter/provider-catalog.ts
const OPENROUTER_LEGACY_BASE_URL = "https://openrouter.ai/v1";
const OPENROUTER_MODELS_CACHE_TTL_MS = 6e4;
const OPENROUTER_DEFAULT_MODEL_ID = "openrouter/auto";
const OPENROUTER_DEFAULT_CONTEXT_WINDOW = 2e5;
const OPENROUTER_DEFAULT_MAX_TOKENS = 8192;
const OPENROUTER_DEFAULT_COST = {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0
};
const OPENROUTER_PROXY_REASONING_UNSUPPORTED_MODEL_IDS = /* @__PURE__ */ new Set(["openrouter/hunter-alpha"]);
const OPENROUTER_KIMI_K2_6_COST = {
input: .8,
output: 3.5,
cacheRead: .2,
cacheWrite: 0
};
const OPENROUTER_KIMI_K2_5_COST = {
input: .44,
output: 2,
cacheRead: .22,
cacheWrite: 0
};
function normalizeOpenRouterBaseUrl(baseUrl) {
const normalized = baseUrl?.trim().replace(/\/+$/, "");
if (!normalized) return;
if (normalized === "https://openrouter.ai/api/v1" || normalized === OPENROUTER_LEGACY_BASE_URL) return OPENROUTER_BASE_URL;
}
function resolveOpenRouterApiBaseUrl(baseUrl) {
const normalized = normalizeOpenRouterBaseUrl(baseUrl) ?? normalizeBaseUrl(baseUrl, "https://openrouter.ai/api/v1");
const parsed = URL.parse(normalized);
if (!parsed || parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.search || parsed.hash) throw new Error("Invalid OpenRouter API base URL");
return normalized;
}
function resolveOpenRouterSsrfPolicy(requestConfig, request) {
return requestConfig.allowPrivateNetwork ? { allowPrivateNetwork: true } : request?.allowPrivateNetwork === false ? {} : ssrfPolicyFromHttpBaseUrlAllowedOrigin(requestConfig.baseUrl);
}
function isOpenRouterProxyReasoningUnsupportedModel(modelId) {
const normalized = (modelId ?? "").trim().toLowerCase();
if (!normalized) return false;
return OPENROUTER_PROXY_REASONING_UNSUPPORTED_MODEL_IDS.has(normalized) || normalized.startsWith("openrouter/hunter-alpha:");
}
function buildOpenrouterProvider() {
return {
baseUrl: OPENROUTER_BASE_URL,
api: "openai-completions",
models: [
{
id: OPENROUTER_DEFAULT_MODEL_ID,
name: "OpenRouter Auto",
reasoning: false,
input: ["text", "image"],
cost: OPENROUTER_DEFAULT_COST,
contextWindow: OPENROUTER_DEFAULT_CONTEXT_WINDOW,
maxTokens: OPENROUTER_DEFAULT_MAX_TOKENS
},
{
id: "moonshotai/kimi-k2.6",
name: "MoonshotAI: Kimi K2.6",
reasoning: true,
input: ["text", "image"],
cost: OPENROUTER_KIMI_K2_6_COST,
contextWindow: 262144,
maxTokens: 262144
},
{
id: "moonshotai/kimi-k2.5",
name: "MoonshotAI: Kimi K2.5",
reasoning: true,
input: ["text", "image"],
cost: OPENROUTER_KIMI_K2_5_COST,
contextWindow: 262144,
maxTokens: 262144
}
]
};
}
function readOpenRouterModalities(architecture, direction) {
const explicit = filterStringEntries(architecture?.[`${direction}_modalities`]);
if (explicit.length > 0) return explicit;
const modality = normalizeOptionalString(architecture?.modality);
if (!modality) return [];
const [input = "", output = ""] = modality.split("->", 2);
return (direction === "input" ? input : output).split("+").filter(Boolean);
}
function buildOpenRouterLiveModel(row) {
const record = asOptionalRecord(row);
const id = normalizeOptionalString(record?.id);
const architecture = asOptionalRecord(record?.architecture);
const outputModalities = readOpenRouterModalities(architecture, "output");
if (!id || outputModalities.length > 0 && !outputModalities.includes("text")) return;
const inputModalities = readOpenRouterModalities(architecture, "input");
const supportedParameters = filterStringEntries(record?.supported_parameters);
const topProvider = asOptionalRecord(record?.top_provider);
const reasoning = normalizeOpenRouterModelReasoning(record?.reasoning);
return {
id,
name: normalizeOptionalString(record?.name) ?? id,
reasoning: supportedParameters.includes("reasoning") || supportedParameters.includes("include_reasoning"),
...reasoning,
input: inputModalities.includes("image") ? ["text", "image"] : ["text"],
...Array.isArray(record?.supported_parameters) ? { compat: {
...reasoning?.compat,
supportsTools: supportedParameters.includes("tools")
} } : {},
cost: normalizeOpenRouterModelPricing(record?.pricing) ?? { ...OPENROUTER_DEFAULT_COST },
contextWindow: asPositiveSafeInteger(topProvider?.context_length) ?? asPositiveSafeInteger(record?.context_length) ?? OPENROUTER_DEFAULT_CONTEXT_WINDOW,
maxTokens: asPositiveSafeInteger(topProvider?.max_completion_tokens) ?? asPositiveSafeInteger(record?.max_completion_tokens) ?? asPositiveSafeInteger(record?.max_output_tokens) ?? OPENROUTER_DEFAULT_MAX_TOKENS
};
}
async function buildOpenrouterLiveProvider(params) {
const fallback = buildOpenrouterProvider();
const baseUrl = resolveOpenRouterApiBaseUrl(params.baseUrl);
const request = sanitizeConfiguredModelProviderRequest(params.request);
const resolveRequest = (apiKey) => resolveProviderHttpRequestConfig({
provider: "openrouter",
capability: "llm",
baseUrl,
defaultBaseUrl: OPENROUTER_BASE_URL,
defaultHeaders: {
Accept: "application/json",
...apiKey ? { Authorization: `Bearer ${apiKey}` } : {}
},
request
});
const requestConfig = resolveRequest();
const endpoint = `${requestConfig.baseUrl}/models`;
return await buildLiveModelProviderConfig({
discoveryMode: "strict",
providerId: "openrouter",
endpoint,
providerConfig: {
baseUrl: requestConfig.baseUrl,
api: fallback.api,
...params.request ? { request: params.request } : {}
},
models: fallback.models,
apiKey: params.apiKey,
discoveryApiKey: params.discoveryApiKey,
fetchGuard: async (fetchParams) => await (params.fetchGuard ?? fetchWithSsrFGuard)({
...fetchParams,
...requestConfig.dispatcherPolicy ? { dispatcherPolicy: requestConfig.dispatcherPolicy } : {}
}),
signal: params.signal,
ttlMs: OPENROUTER_MODELS_CACHE_TTL_MS,
auditContext: "openrouter-model-discovery",
policy: resolveOpenRouterSsrfPolicy(requestConfig, params.request),
cacheKeyParts: [
"openrouter",
"model-rows",
endpoint,
params.discoveryApiKey ?? params.apiKey,
request ?? null
],
buildRequestHeaders: ({ apiKey, discoveryApiKey }) => resolveRequest(discoveryApiKey ?? apiKey).headers,
projectRows: (rows, fallbackProvider) => {
const liveModels = rows.flatMap((row) => {
const model = buildOpenRouterLiveModel(row);
return model ? [model] : [];
});
if (liveModels.length === 0) return [];
return [...new Map([...fallbackProvider.models, ...liveModels].map((model) => [model.id, model])).values()].toSorted((a, b) => a.id.localeCompare(b.id));
}
});
}
//#endregion
export { resolveOpenRouterApiBaseUrl as a, normalizeOpenRouterBaseUrl as i, buildOpenrouterProvider as n, resolveOpenRouterSsrfPolicy as o, isOpenRouterProxyReasoningUnsupportedModel as r, buildOpenrouterLiveProvider as t };