openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
182 lines (181 loc) • 8.47 kB
JavaScript
import { u as asPositiveFiniteNumber } from "./number-coercion-CLj0HTDM.js";
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { r as normalizeProviderId } from "./provider-id-DMd-TDFp.js";
import { t as createConfiguredProviderCatalogModelIdNormalizer } from "./model-ref-shared-Dz7QU0Lx.js";
import { t as modelKey } from "./model-key-CMdQNkZf.js";
import { n as resolveCatalogOwnedModelCompat } from "./model-compat-catalog-BNBUeFnX.js";
import { t as mergeModelCost } from "./model-cost-BNQWuFdo.js";
import { c as isNonSecretApiKeyMarker } from "./model-auth-markers-jBKQn38x.js";
//#region src/agents/models-config.merge.ts
/**
* Merges generated model-provider config with explicit user config and
* preserved secret fields. Setup and doctor flows use this boundary to update
* model catalogs without discarding existing credentials.
*/
function normalizeProviderMapKeys(providers) {
const normalized = {};
const canonicalKeys = /* @__PURE__ */ new Set();
for (const [key, value] of Object.entries(providers ?? {})) {
const providerKey = normalizeProviderId(key);
if (!providerKey) continue;
if (key === providerKey) {
canonicalKeys.add(providerKey);
delete normalized[providerKey];
normalized[providerKey] = value;
continue;
}
if (!canonicalKeys.has(providerKey)) normalized[providerKey] = value;
}
return normalized;
}
function getProviderModelId(model) {
if (!model || typeof model !== "object") return "";
const id = model.id;
return normalizeOptionalString(id) ?? "";
}
/** Merges implicit provider models with explicit config while preserving explicit fields. */
function mergeProviderModels(implicit, explicit, options) {
const implicitModels = Array.isArray(implicit.models) ? implicit.models : [];
const explicitModels = Array.isArray(explicit.models) ? explicit.models : [];
const implicitHeaders = implicit.headers && typeof implicit.headers === "object" && !Array.isArray(implicit.headers) ? implicit.headers : void 0;
const explicitHeaders = explicit.headers && typeof explicit.headers === "object" && !Array.isArray(explicit.headers) ? explicit.headers : void 0;
if (implicitModels.length === 0) return {
...implicit,
...explicit,
...implicitHeaders || explicitHeaders ? { headers: {
...implicitHeaders,
...explicitHeaders
} } : {}
};
const implicitById = new Map(implicitModels.map((model) => [getProviderModelId(model), model]).filter(([id]) => Boolean(id)));
const seen = /* @__PURE__ */ new Set();
const normalizeModelId = createConfiguredProviderCatalogModelIdNormalizer(options);
const mergedModels = explicitModels.map((explicitModel) => {
const id = getProviderModelId(explicitModel);
if (!id) return explicitModel;
seen.add(id);
const implicitModel = implicitById.get(id);
if (!implicitModel) return explicitModel;
const sourceFields = options?.sourceModelFields?.get(modelKey(normalizeProviderId(options.providerId), normalizeModelId(options.providerId, id)));
const cost = mergeModelCost(implicitModel.cost, sourceFields ? sourceFields.cost : explicitModel.cost);
const input = sourceFields?.inputOmitted && implicitModel.input !== void 0 ? implicitModel.input : "input" in explicitModel ? explicitModel.input : implicitModel.input;
if (options?.preserveConfiguredModelMembership) return Object.assign({}, explicitModel, { cost }, sourceFields?.inputOmitted ? { input } : {});
const contextWindow = asPositiveFiniteNumber(explicitModel.contextWindow) ?? asPositiveFiniteNumber(implicitModel.contextWindow);
const contextTokens = asPositiveFiniteNumber(explicitModel.contextTokens) ?? asPositiveFiniteNumber(implicitModel.contextTokens);
const maxTokens = asPositiveFiniteNumber(explicitModel.maxTokens) ?? asPositiveFiniteNumber(implicitModel.maxTokens);
const compat = resolveCatalogOwnedModelCompat({
catalogRoute: {
api: implicitModel.api ?? implicit.api,
baseUrl: implicitModel.baseUrl ?? implicit.baseUrl
},
catalogCompat: implicitModel.compat,
configuredRoute: {
api: explicitModel.api ?? explicit.api ?? implicitModel.api ?? implicit.api,
baseUrl: explicitModel.baseUrl ?? explicit.baseUrl ?? implicitModel.baseUrl ?? implicit.baseUrl
},
configuredCompat: explicitModel.compat
});
return Object.assign({}, explicitModel, {
input,
cost,
reasoning: `reasoning` in explicitModel ? explicitModel.reasoning : implicitModel.reasoning
}, contextWindow === void 0 ? {} : { contextWindow }, contextTokens === void 0 ? {} : { contextTokens }, maxTokens === void 0 ? {} : { maxTokens }, { compat });
});
if (!options?.preserveConfiguredModelMembership) for (const implicitModel of implicitModels) {
const id = getProviderModelId(implicitModel);
if (!id || seen.has(id)) continue;
seen.add(id);
mergedModels.push(implicitModel);
}
return {
...implicit,
...explicit,
...implicitHeaders || explicitHeaders ? { headers: {
...implicitHeaders,
...explicitHeaders
} } : {},
models: mergedModels
};
}
/** Merges implicit and explicit provider config maps by provider id. */
function mergeProviders(params) {
const out = normalizeProviderMapKeys(params.implicit);
for (const [providerKey, explicit] of Object.entries(normalizeProviderMapKeys(params.explicit))) {
const implicit = out[providerKey];
out[providerKey] = implicit ? mergeProviderModels(implicit, explicit, {
providerId: providerKey,
sourceModelFields: params.sourceModelFields,
manifestPlugins: params.manifestPlugins
}) : explicit;
}
return out;
}
function resolveProviderApi(entry) {
return normalizeOptionalString(entry?.api);
}
function resolveModelApiSurface(entry) {
if (!Array.isArray(entry?.models)) return;
const apis = entry.models.flatMap((model) => {
if (!model || typeof model !== "object") return [];
const api = model.api;
const normalized = normalizeOptionalString(api);
return normalized ? [normalized] : [];
}).toSorted();
return apis.length > 0 ? JSON.stringify(apis) : void 0;
}
function resolveProviderApiSurface(entry) {
return resolveProviderApi(entry) ?? resolveModelApiSurface(entry);
}
function shouldPreserveExistingApiKey(params) {
const { providerKey, existing, nextEntry, secretRefManagedProviders } = params;
const nextApiKey = typeof nextEntry.apiKey === "string" ? nextEntry.apiKey : "";
if (nextApiKey && isNonSecretApiKeyMarker(nextApiKey)) return false;
return !secretRefManagedProviders.has(providerKey) && typeof existing.apiKey === "string" && existing.apiKey.length > 0 && !isNonSecretApiKeyMarker(existing.apiKey, { includeEnvVarName: false });
}
function shouldPreserveExistingBaseUrl(params) {
const { existing, nextEntry } = params;
if (typeof existing.baseUrl !== "string" || existing.baseUrl.length === 0) return false;
const existingApi = resolveProviderApiSurface(existing);
const nextApi = resolveProviderApiSurface(nextEntry);
return !existingApi || !nextApi || existingApi === nextApi;
}
function isExistingProviderSelfContained(entry) {
if (!Array.isArray(entry.models) || entry.models.length === 0) return true;
return Boolean(entry.baseUrl?.trim() && (entry.apiKey === void 0 || entry.apiKey));
}
/** Merges generated provider config with existing secrets safe to preserve. */
function mergeWithExistingProviderSecrets(params) {
const { nextProviders, existingProviders, secretRefManagedProviders } = params;
const normalizedExistingProviders = normalizeProviderMapKeys(existingProviders);
const normalizedNextProviders = normalizeProviderMapKeys(nextProviders);
const mergedProviders = {};
for (const [key, entry] of Object.entries(normalizedExistingProviders)) {
if (!isExistingProviderSelfContained(entry)) continue;
mergedProviders[key] = entry;
}
for (const [key, newEntry] of Object.entries(normalizedNextProviders)) {
const existing = normalizedExistingProviders[key];
if (!existing) {
mergedProviders[key] = newEntry;
continue;
}
const preserved = {};
if (shouldPreserveExistingApiKey({
providerKey: key,
existing,
nextEntry: newEntry,
secretRefManagedProviders
})) preserved.apiKey = existing.apiKey;
if (shouldPreserveExistingBaseUrl({
existing,
nextEntry: newEntry
})) preserved.baseUrl = existing.baseUrl;
mergedProviders[key] = {
...newEntry,
...preserved
};
}
return mergedProviders;
}
//#endregion
export { normalizeProviderMapKeys as i, mergeProviders as n, mergeWithExistingProviderSecrets as r, mergeProviderModels as t };