UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

297 lines (296 loc) 11.9 kB
import { c as isRecord } from "./record-coerce-DItp3I4t.js"; import { t as isBlockedObjectKey } from "./prototype-keys-CuYw53fZ.js"; import "./utils-P__uGsPB.js"; import { r as normalizeProviderId } from "./provider-id-DMd-TDFp.js"; import { t as isSafeExecutableValue } from "./exec-safety-DtLGRBJm.js"; import { r as isModelThinkingFormat } from "./types.models-Z6EPRVI_.js"; import { r as resolveUniqueCatalogModelRoute, t as modelTransportRoutesMatch } from "./model-compat-catalog-BNBUeFnX.js"; import { t as planManifestModelCatalogRows } from "./manifest-planner-bVm3dG3U.js"; import { t as listOpenClawPluginManifestMetadata } from "./manifest-metadata-scan-BaLPCL7D.js"; import { r as resolveProviderModelRoutes } from "./provider-model-routes-jLTliGaX.js"; import { isDeepStrictEqual } from "node:util"; //#region src/config/legacy.shared.ts const getRecord = (value) => isRecord(value) ? value : null; const ensureRecord = (root, key) => { const existing = root[key]; if (isRecord(existing)) return existing; const next = {}; root[key] = next; return next; }; const mergeMissing = (target, source) => { for (const [key, value] of Object.entries(source)) { if (value === void 0 || isBlockedObjectKey(key)) continue; const existing = target[key]; if (existing === void 0) { target[key] = value; continue; } if (isRecord(existing) && isRecord(value)) mergeMissing(existing, value); } }; const mapLegacyAudioTranscription = (value) => { const transcriber = getRecord(value); const command = Array.isArray(transcriber?.command) ? transcriber?.command : null; if (!command || command.length === 0) return null; if (typeof command[0] !== "string") return null; if (!command.every((part) => typeof part === "string")) return null; const rawExecutable = command[0].trim(); if (!rawExecutable) return null; if (!isSafeExecutableValue(rawExecutable)) return null; const args = command.slice(1).map((part) => part.replace(/\{input\}/g, "{{AttachmentPath}}")); const timeoutSeconds = typeof transcriber?.timeoutSeconds === "number" ? transcriber?.timeoutSeconds : void 0; const result = { command: rawExecutable, type: "cli" }; if (args.length > 0) result.args = args; if (timeoutSeconds !== void 0) result.timeoutSeconds = timeoutSeconds; return result; }; const defineLegacyConfigMigration = (migration) => migration; //#endregion //#region src/commands/doctor/shared/legacy-config-migrations.runtime.models.catalog.ts const STALE_CONTEXT_WINDOW_FIXES = { "deepseek/deepseek-v4-flash": { stale: 2e5, correct: 1e6 }, "xai/grok-4.20-0309-reasoning": { stale: 2e6, correct: 1e6 }, "xai/grok-4.20-0309-non-reasoning": { stale: 2e6, correct: 1e6 }, "xai/grok-4.20-beta-latest-reasoning": { stale: 2e6, correct: 1e6 }, "xai/grok-4.20-beta-latest-non-reasoning": { stale: 2e6, correct: 1e6 }, "xai/grok-4.20-experimental-beta-0304-reasoning": { stale: 2e6, correct: 1e6 }, "xai/grok-4.20-experimental-beta-0304-non-reasoning": { stale: 2e6, correct: 1e6 }, "xai/grok-4.20-reasoning": { stale: 2e6, correct: 1e6 }, "xai/grok-4.20-non-reasoning": { stale: 2e6, correct: 1e6 } }; const DEAD_MODEL_COMPAT_KEYS = ["nativeWebSearchTool", "requiresMistralToolIds"]; function normalizedCatalogModelKey(provider, modelId) { const normalizedProvider = normalizeProviderId(provider); const normalizedId = modelId.trim().toLowerCase(); const providerPrefix = `${normalizedProvider}/`; return `${normalizedProvider}::${normalizedId.startsWith(providerPrefix) ? normalizedId.slice(providerPrefix.length) : normalizedId}`; } const modelCompatCatalogRowsByProvider = /* @__PURE__ */ new Map(); let modelCompatCatalogPlugins; function getModelCompatCatalogPlugins() { modelCompatCatalogPlugins ??= listOpenClawPluginManifestMetadata().flatMap(({ manifest }) => { const id = typeof manifest.id === "string" ? manifest.id.trim() : ""; const modelCatalog = getRecord(manifest.modelCatalog); if (!id || !modelCatalog) return []; return [{ id, providers: Array.isArray(manifest.providers) ? manifest.providers.filter((value) => typeof value === "string") : [], modelCatalog }]; }); return modelCompatCatalogPlugins; } function buildConfiguredProviderCatalogRows(providers) { const rows = /* @__PURE__ */ new Map(); for (const providerId of Object.keys(providers)) { const normalizedProviderId = normalizeProviderId(providerId); let providerRows = modelCompatCatalogRowsByProvider.get(normalizedProviderId); if (!providerRows) { providerRows = planManifestModelCatalogRows({ registry: { plugins: getModelCompatCatalogPlugins() }, providerFilter: normalizedProviderId }).rows; modelCompatCatalogRowsByProvider.set(normalizedProviderId, providerRows); } for (const row of providerRows) { const key = normalizedCatalogModelKey(row.provider, row.id); const variants = rows.get(key) ?? []; variants.push(row); rows.set(key, variants); } } return rows; } /** Resolves one catalog identity and whether its configured route remains provider-owned. */ function resolveConfiguredModelCatalogOwnership(params) { const modelId = typeof params.model.id === "string" ? params.model.id : ""; if (!modelId) return; const rows = buildConfiguredProviderCatalogRows({ [params.providerId]: params.provider }).get(normalizedCatalogModelKey(params.providerId, modelId)); const catalogRow = rows?.length === 1 ? rows[0] : void 0; if (!catalogRow) return; const configuredRoute = { api: params.model.api ?? params.provider.api, baseUrl: params.model.baseUrl ?? params.provider.baseUrl }; const exactCatalogRoute = modelTransportRoutesMatch(catalogRow, configuredRoute); const providerRoutes = resolveProviderModelRoutes({ provider: params.providerId, modelId, env: {} }); const providerOwnedRoute = providerRoutes?.kind === "routes" && providerRoutes.routes.some((route) => modelTransportRoutesMatch(route, configuredRoute)); return { catalogRow, ownsRoute: exactCatalogRoute || providerOwnedRoute }; } function inspectModelCompatOverrides(providersValue, onEntry) { const providers = getRecord(providersValue); const total = { dead: 0, divergent: 0, matching: 0 }; if (!providers) return total; if (!Object.values(providers).some((providerValue) => { const models = getRecord(providerValue)?.models; return Array.isArray(models) && models.some((modelValue) => Boolean(getRecord(getRecord(modelValue)?.compat))); })) return total; const catalogRows = buildConfiguredProviderCatalogRows(providers); for (const [providerId, providerValue] of Object.entries(providers)) { const provider = getRecord(providerValue); const models = provider?.models; if (!provider || !Array.isArray(models)) continue; for (const [modelIndex, modelValue] of models.entries()) { const model = getRecord(modelValue); const compat = getRecord(model?.compat); const modelId = typeof model?.id === "string" ? model.id : ""; if (!model || !compat || !modelId) continue; const state = { dead: 0, divergent: 0, matching: 0 }; for (const key of DEAD_MODEL_COMPAT_KEYS) if (Object.hasOwn(compat, key)) state.dead += 1; const configuredRoute = { api: model.api ?? provider.api, baseUrl: model.baseUrl ?? provider.baseUrl }; const catalogRow = resolveUniqueCatalogModelRoute(catalogRows.get(normalizedCatalogModelKey(providerId, modelId)), configuredRoute); if (catalogRow !== void 0) { const catalogCompat = catalogRow.compat ?? {}; for (const [key, value] of Object.entries(compat)) { if (DEAD_MODEL_COMPAT_KEYS.includes(key)) continue; if (isDeepStrictEqual(value, catalogCompat[key])) state.matching += 1; else state.divergent += 1; } } total.dead += state.dead; total.divergent += state.divergent; total.matching += state.matching; onEntry?.({ catalogRow, compat, model, modelIndex, provider, providerId, state }); } } return total; } const MODEL_COMPAT_CATALOG_RULES = [ { path: ["models", "providers"], message: "nativeWebSearchTool and requiresMistralToolIds are unused and retired; run \"openclaw doctor --fix\" to remove them.", match: (value) => inspectModelCompatOverrides(value).dead > 0 }, { path: ["models", "providers"], message: "Catalog-known model compat values are provider-owned; run \"openclaw doctor --fix\" to remove matching config overrides.", match: (value) => inspectModelCompatOverrides(value).matching > 0 }, { path: ["models", "providers"], message: "Catalog-known model compat differs from the provider catalog and was preserved for review. Use a distinct custom route when the endpoint really has different capabilities.", match: (value) => inspectModelCompatOverrides(value).divergent > 0 } ]; function migrateModelCompatCatalogOwnership(raw, changes) { inspectModelCompatOverrides(getRecord(getRecord(raw.models)?.providers), ({ catalogRow, compat, model, modelIndex, provider, providerId }) => { const removed = []; for (const key of DEAD_MODEL_COMPAT_KEYS) if (Object.hasOwn(compat, key)) { delete compat[key]; removed.push(key); } if (catalogRow && modelTransportRoutesMatch(catalogRow, { api: model.api ?? provider.api ?? catalogRow.api, baseUrl: model.baseUrl ?? provider.baseUrl ?? catalogRow.baseUrl })) { const catalogCompat = catalogRow.compat ?? {}; for (const [key, value] of Object.entries(compat)) if (isDeepStrictEqual(value, catalogCompat[key])) { delete compat[key]; removed.push(key); } } if (removed.length === 0) return; if (Object.keys(compat).length === 0) delete model.compat; changes.push(`Removed models.providers.${providerId}.models.${modelIndex}.compat catalog/dead overrides: ${removed.toSorted().join(", ")}.`); }); } function resolveStaleContextWindowFix(params) { const providerId = params.providerId.trim().toLowerCase(); const modelId = params.modelId.trim().toLowerCase(); const providerPrefix = `${providerId}/`; const scopedModelId = `${providerId}/${modelId.startsWith(providerPrefix) ? modelId.slice(providerPrefix.length) : modelId}`; const fix = STALE_CONTEXT_WINDOW_FIXES[scopedModelId]; return fix && params.contextWindow === fix.stale ? fix : void 0; } function hasStaleContextWindowValue(providers) { const providersRecord = getRecord(providers); if (!providersRecord) return false; for (const [providerId, provider] of Object.entries(providersRecord)) { const models = getRecord(provider)?.models; if (!Array.isArray(models)) continue; for (const model of models) { const modelRecord = getRecord(model); const modelId = typeof modelRecord?.id === "string" ? modelRecord.id : void 0; const contextWindow = modelRecord?.contextWindow; if (!modelId || typeof contextWindow !== "number" || !Number.isFinite(contextWindow)) continue; if (resolveStaleContextWindowFix({ providerId, modelId, contextWindow })) return true; } } return false; } function hasInvalidThinkingFormat(providers) { const providersRecord = getRecord(providers); if (!providersRecord) return false; for (const provider of Object.values(providersRecord)) { const models = getRecord(provider)?.models; if (!Array.isArray(models)) continue; for (const model of models) { const thinkingFormat = getRecord(getRecord(model)?.compat)?.thinkingFormat; if (typeof thinkingFormat === "string" && !isModelThinkingFormat(thinkingFormat)) return true; } } return false; } //#endregion export { resolveConfiguredModelCatalogOwnership as a, ensureRecord as c, mergeMissing as d, migrateModelCompatCatalogOwnership as i, getRecord as l, hasInvalidThinkingFormat as n, resolveStaleContextWindowFix as o, hasStaleContextWindowValue as r, defineLegacyConfigMigration as s, MODEL_COMPAT_CATALOG_RULES as t, mapLegacyAudioTranscription as u };