UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

368 lines (367 loc) • 17.2 kB
import { t as createSubsystemLogger } from "./subsystem-4NzR43uz.mjs"; import { n as createLazyPromise } from "./lazy-promise-DGqyc4Y4.mjs"; import { r as normalizeProviderId } from "./provider-id-D1VImdKk.mjs"; import { t as buildModelCatalogMergeKey } from "./model-catalog-refs-D3xvQZh0.mjs"; import { u as normalizePluginsConfig } from "./config-state-Ba1MVBAI.mjs"; import { i as getCurrentPluginMetadataSnapshot } from "./current-plugin-metadata-snapshot-DpeEPkbV.mjs"; import { t as isDiagnosticFlagEnabled } from "./diagnostic-flags-CaSenm_x.mjs"; import { u as resolvePluginMetadataSnapshot } from "./plugin-metadata-snapshot-DZdnLZdU.mjs"; import { t as createConfiguredProviderCatalogModelIdNormalizer } from "./model-ref-shared-Df4xc5Gg.mjs"; import { r as normalizeCatalogRouteBaseUrl } from "./model-compat-catalog-VI3Psdyr.mjs"; import { n as isManifestPluginAvailableForControlPlane } from "./manifest-contract-eligibility-P_yAPTzd.mjs"; import { r as buildConfiguredModelCatalog, x as overlayCatalogMetadata } from "./model-selection-shared-CHVNQnp1.mjs"; import { t as createModelCatalogIdentityKeyResolver } from "./openai-model-routes-QgeWIyIu.mjs"; import { r as modelSupportsInput } from "./model-catalog-lookup-_nlJdlgO.mjs"; import { r as isProviderCatalogSourceAllowed } from "./provider-config-owner-BabgQdAS.mjs"; import { t as planEffectiveModelCatalogRows } from "./model-catalog-C8Z6CIjy.mjs"; import { u as resolveClaudeFable5ModelIdentity } from "./anthropic-B3HirzdP.mjs"; import "./src-D3dCIQYS.mjs"; import { t as augmentModelCatalogWithProviderPlugins } from "./provider-runtime.runtime.js"; import { r as modelCatalogRowToEntry } from "./model-catalog-entry-DTkj669f.mjs"; import { n as compareModelCatalogEntries, t as assignProviderModelOrder } from "./model-catalog-order-B46aayk6.mjs"; import { t as buildEffectiveManifestProviderConfig } from "./provider-catalog-gH155Jmw.mjs"; import { t as createPreparedModelCatalogProviderNormalizer } from "./model-catalog-provider-normalizer-BSG0E_f4.mjs"; //#region src/agents/model-catalog-manifest.ts function resolveEligibleManifestCatalogPlugins(snapshot, config) { let normalizedConfig; return snapshot.plugins.filter((plugin) => plugin.modelCatalog && isManifestPluginAvailableForControlPlane({ snapshot, plugin, config, normalizedConfig: config.plugins && (normalizedConfig ??= normalizePluginsConfig(config.plugins)) })); } /** * Known manifest inventory for the caller's admitted providers. Captured publications show it * until live discovery replaces that provider's rows. */ function loadManifestModelProviderConfigs(params) { if (params.config.models?.mode === "replace" || params.providerIds.length === 0) return {}; const { rows } = planEffectiveModelCatalogRows({ registry: { plugins: resolveEligibleManifestCatalogPlugins(params.metadataSnapshot, params.config) }, config: params.config, providerFilters: params.providerIds }); const rowsByProvider = /* @__PURE__ */ new Map(); for (const row of rows) { const providerRows = rowsByProvider.get(row.provider); if (providerRows) providerRows.push(row); else rowsByProvider.set(row.provider, [row]); } const providers = {}; for (const [provider, providerRows] of rowsByProvider) { const providerConfig = buildEffectiveManifestProviderConfig(providerRows); if (providerConfig) providers[provider] = providerConfig; } return providers; } //#endregion //#region src/agents/model-catalog.ts /** * Loads bundled, manifest, and discovered model catalog entries. */ const log = createSubsystemLogger("model-catalog"); let hasLoggedModelCatalogError = false; let manifestModelCatalogCache = /* @__PURE__ */ new WeakMap(); const loadModelSuppression = createLazyPromise(() => import("./model-suppression-D2q0Z6d1.mjs")); const loadProviderApiKeyResolver = createLazyPromise(() => import("./models-config.providers.secrets-BeGI2Xaf.mjs")); function resetModelCatalogBuilderCacheForTest() { manifestModelCatalogCache = /* @__PURE__ */ new WeakMap(); hasLoggedModelCatalogError = false; } function normalizeCatalogEntryContract(entry) { if (entry.api === "anthropic-messages" && resolveClaudeFable5ModelIdentity({ id: entry.id, params: entry.params })) return { ...entry, reasoning: true }; return entry; } function mergeCatalogEntries(models, entries, options) { const identityKeyOf = createModelCatalogIdentityKeyResolver(); const keyOf = options?.routeVariantIndex ? (entry) => catalogRouteVariantKey(entry, identityKeyOf(entry)) : identityKeyOf; const indexByKey = options?.routeVariantIndex ?? new Map(models.map((entry, index) => [keyOf(entry), index])); for (const entry of entries) { const key = keyOf(entry); const existingIndex = indexByKey.get(key); if (existingIndex === void 0) { models.push(entry); indexByKey.set(key, models.length - 1); continue; } const existing = models.at(existingIndex); if (existing) { const routes = options?.catalogRoutes; const routeIndex = options?.preserveBaseCompat ? routes?.indexByKey.get(catalogRouteVariantKey(entry, key)) : void 0; const catalogRoute = routeIndex === void 0 ? void 0 : routes?.entries[routeIndex]; models[existingIndex] = overlayCatalogMetadata(catalogRoute ?? existing, entry, options); } } } function catalogRouteVariantKey(entry, identityKey) { return [ identityKey, entry.api ?? "", normalizeCatalogRouteBaseUrl(entry.baseUrl) ?? "" ].join("\0"); } function createModelCatalogSnapshot(entries, routeVariants, providerOutcomes) { return { entries: sortModelCatalogEntries(applyReadyCatalogModelOrder(entries, providerOutcomes)), routeVariants: sortModelCatalogEntries(applyReadyCatalogModelOrder(routeVariants.entries, providerOutcomes)) }; } function applyReadyCatalogModelOrder(entries, outcomes) { const keyOf = createModelCatalogIdentityKeyResolver(); const orders = /* @__PURE__ */ new Map(); for (const outcome of outcomes ?? []) { if (outcome.status !== "ready" || !outcome.modelOrder?.length) continue; const provider = normalizeProviderId(outcome.provider); const order = /* @__PURE__ */ new Map(); for (const id of outcome.modelOrder) { const key = keyOf({ provider, id }); if (!order.has(key)) order.set(key, order.size); } orders.set(provider, order); } if (orders.size === 0) return entries; return entries.map((entry) => { const order = orders.get(normalizeProviderId(entry.provider)); if (!order) return entry; const rank = order.get(keyOf(entry)); return { ...entry, providerOrder: rank ?? (entry.providerOrder === void 0 ? void 0 : order.size + entry.providerOrder) }; }); } function loadManifestModelCatalog(params) { if (params.config.models?.mode === "replace") return []; const resolvedSnapshot = params.metadataSnapshot ?? (params.fallbackToMetadataScan === false ? getCurrentPluginMetadataSnapshot({ config: params.config, env: params.env, ...params.workspaceDir !== void 0 ? { workspaceDir: params.workspaceDir } : {}, ...params.workspaceDir === void 0 ? { allowWorkspaceScopedSnapshot: true } : {} }) : resolvePluginMetadataSnapshot({ config: params.config, ...params.workspaceDir !== void 0 ? { workspaceDir: params.workspaceDir } : {}, env: params.env ?? process.env, allowWorkspaceScopedCurrent: params.workspaceDir === void 0 })); return resolvedSnapshot ? loadManifestModelCatalogRows(params.config, resolvedSnapshot) : []; } /** Overlays configured capabilities on a copy of the captured catalog. */ function overlayConfiguredModelCatalog(params) { const models = params.config.models?.mode === "replace" ? [] : [...params.catalog]; mergeCatalogEntries(models, buildConfiguredModelCatalog({ cfg: params.config, catalog: models, workspaceDir: params.workspaceDir }), { preserveBaseCompat: true }); return models; } function loadManifestModelCatalogRows(config, snapshot, preparedPlan) { if (config.models?.mode === "replace") return []; const cached = manifestModelCatalogCache.get(config); if (cached?.snapshot === snapshot) return cached.rows; const plugins = resolveEligibleManifestCatalogPlugins(snapshot, config); const plan = preparedPlan ?? planEffectiveModelCatalogRows({ registry: { plugins }, config }); const providerOrderByKey = /* @__PURE__ */ new Map(); for (const plugin of plugins) for (const [provider, providerCatalog] of Object.entries(plugin.modelCatalog?.providers ?? {})) providerCatalog.models.forEach((model, providerOrder) => { const key = buildModelCatalogMergeKey(provider, model.id); if (!providerOrderByKey.has(key)) providerOrderByKey.set(key, providerOrder); }); const rows = plan.rows.map((row) => { const entry = modelCatalogRowToEntry(row); const providerOrder = providerOrderByKey.get(buildModelCatalogMergeKey(row.provider, row.id)); if (providerOrder !== void 0) entry.providerOrder = providerOrder; return entry; }); manifestModelCatalogCache.set(config, { snapshot, rows }); return rows; } function sortModelCatalogEntries(entries) { return entries.map(normalizeCatalogEntryContract).toSorted(compareModelCatalogEntries); } /** Builds the catalog once for a lifecycle generation. No request-time discovery or cache IO. */ async function buildPreparedModelCatalogSnapshot(params) { const models = []; const routeVariants = { entries: [], indexByKey: /* @__PURE__ */ new Map() }; const cfg = params.config; const env = params.env ?? process.env; const timingEnabled = isDiagnosticFlagEnabled("ingress.timing", cfg); const startMs = timingEnabled ? Date.now() : 0; const logStage = (stage, extra) => { if (!timingEnabled) return; const suffix = extra ? ` ${extra}` : ""; log.info(`model-catalog stage=${stage} elapsedMs=${Date.now() - startMs}${suffix}`); }; try { const workspaceDir = params.workspaceDir; const manifestMetadataSnapshot = params.metadataSnapshot; const normalizeModelId = createConfiguredProviderCatalogModelIdNormalizer({ manifestPlugins: manifestMetadataSnapshot }); const normalizeProvider = createPreparedModelCatalogProviderNormalizer(manifestMetadataSnapshot, cfg, env); const observedProviders = new Set(params.providerOutcomes?.map((outcome) => normalizeProvider(outcome.provider))); const { buildShouldSuppressBuiltInModelCore } = await loadModelSuppression(); logStage("catalog-deps-ready"); const entries = params.models; const manifestPlan = planEffectiveModelCatalogRows({ registry: { plugins: resolveEligibleManifestCatalogPlugins(manifestMetadataSnapshot, cfg) }, config: cfg }); const declaredManifestModels = loadManifestModelCatalogRows(cfg, manifestMetadataSnapshot, manifestPlan); logStage("registry-read", `entries=${entries.length}`); const shouldSuppressBuiltInModel = buildShouldSuppressBuiltInModelCore({ config: cfg }); logStage("suppress-resolver-ready"); for (const entry of entries) { const id = entry.id.trim(); if (!id) continue; const rawProvider = entry.provider.trim(); if (!rawProvider) continue; const provider = normalizeProvider(rawProvider); const baseUrl = entry.baseUrl?.trim(); if (shouldSuppressBuiltInModel({ provider, id, baseUrl })) continue; const model = modelCatalogRowToEntry({ ...entry, id, name: entry.name.trim() || id, provider, baseUrl }); models.push(model); } const orderedRegistryModels = assignProviderModelOrder(models, declaredManifestModels, { appendUnknown: false }); models.splice(0, models.length, ...orderedRegistryModels); mergeCatalogEntries(routeVariants.entries, orderedRegistryModels, { routeVariantIndex: routeVariants.indexByKey }); const dynamicManifestKeys = new Set(manifestPlan.entries.flatMap((entry) => entry.discovery === "runtime" || entry.discovery === "refreshable" ? entry.rows.map((row) => buildModelCatalogMergeKey(row.provider, row.id)) : [])); const runtimeDiscoveryProviders = /* @__PURE__ */ new Set([...observedProviders, ...manifestPlan.entries.flatMap((entry) => entry.discovery === "runtime" || entry.discovery === "refreshable" ? [normalizeProviderId(entry.provider)] : [])]); const manifestKeyOf = createModelCatalogIdentityKeyResolver(); const discoveredKeys = new Set(models.map(manifestKeyOf)); const manifestModels = declaredManifestModels.filter((entry) => (params.includeProviderPluginAugmentation === false || !dynamicManifestKeys.has(buildModelCatalogMergeKey(entry.provider, entry.id))) && (!observedProviders.has(entry.provider) || discoveredKeys.has(manifestKeyOf(entry)))); mergeCatalogEntries(routeVariants.entries, manifestModels, { routeVariantIndex: routeVariants.indexByKey }); mergeCatalogEntries(models, manifestModels); logStage("manifest-models-merged", `entries=${models.length}`); const configuredCatalogParams = { cfg, catalog: orderedRegistryModels, manifestPlugins: manifestMetadataSnapshot }; const configuredModels = buildConfiguredModelCatalog(configuredCatalogParams); logStage("configured-models-prepared", `entries=${models.length}`); if (cfg.models?.mode !== "replace" && !params.readOnly && params.includeProviderPluginAugmentation !== false) { const augmentEntries = [...models]; if (configuredModels.length > 0) mergeCatalogEntries(augmentEntries, configuredModels, { catalogRoutes: routeVariants, preserveBaseCompat: true }); const { createProviderApiKeyResolverFromPreparedCredentials } = await loadProviderApiKeyResolver(); const resolveProviderApiKeyForProvider = createProviderApiKeyResolverFromPreparedCredentials(env, params.authCredentials, cfg, workspaceDir); const resolveProviderApiKey = (providerId) => providerId?.trim() ? resolveProviderApiKeyForProvider(providerId) : { apiKey: void 0, discoveryApiKey: void 0 }; const supplemental = await augmentModelCatalogWithProviderPlugins({ providerIds: params.providerIds, config: cfg, workspaceDir, env, metadataSnapshot: manifestMetadataSnapshot, context: { config: cfg, agentDir: params.agentDir, workspaceDir, env, resolveProviderApiKey, entries: augmentEntries } }); if (supplemental.length > 0) { const keyOf = createModelCatalogIdentityKeyResolver(); const accountVisibleModelKeys = new Set([...models, ...configuredModels].map(keyOf)); const normalizedSupplemental = []; for (const entry of supplemental) { const provider = normalizeProvider(entry.provider); const owners = manifestMetadataSnapshot.owners; const pluginIds = owners.modelCatalogProviders.get(provider) ?? owners.providers.get(provider); const pluginId = pluginIds?.length === 1 ? pluginIds[0] : void 0; const plugin = pluginId ? manifestMetadataSnapshot.byPluginId.get(pluginId) : void 0; if (!isProviderCatalogSourceAllowed({ provider, config: cfg, plugin })) continue; const id = normalizeModelId(provider, entry.id); if (runtimeDiscoveryProviders.has(normalizeProviderId(provider)) && !accountVisibleModelKeys.has(keyOf({ provider, id }))) continue; normalizedSupplemental.push({ ...entry, provider, id }); } const orderedSupplemental = assignProviderModelOrder(normalizedSupplemental, [...declaredManifestModels, ...models]); mergeCatalogEntries(routeVariants.entries, orderedSupplemental, { routeVariantIndex: routeVariants.indexByKey }); mergeCatalogEntries(models, orderedSupplemental); } } logStage("plugin-models-merged", `entries=${models.length}`); if (configuredModels.length > 0) { const configuredOverrides = buildConfiguredModelCatalog(configuredCatalogParams); routeVariants.indexByKey.clear(); const keyOf = createModelCatalogIdentityKeyResolver(); routeVariants.entries.forEach((entry, index) => { const key = catalogRouteVariantKey(entry, keyOf(entry)); if (!routeVariants.indexByKey.has(key)) routeVariants.indexByKey.set(key, index); }); mergeCatalogEntries(models, configuredOverrides, { catalogRoutes: routeVariants, preserveBaseCompat: true }); mergeCatalogEntries(routeVariants.entries, configuredOverrides, { routeVariantIndex: routeVariants.indexByKey, preserveBaseCompat: true }); } logStage("configured-models-finalized", `entries=${models.length}`); const snapshot = createModelCatalogSnapshot(models, routeVariants, params.providerOutcomes); logStage("complete", `entries=${snapshot.entries.length}`); return params.providerOutcomes ? { ...snapshot, authoritative: params.providerOutcomes.every((outcome) => outcome.status === "ready") } : snapshot; } catch (error) { if (!hasLoggedModelCatalogError) { hasLoggedModelCatalogError = true; log.warn(`Failed to load model catalog: ${String(error)}`); } throw error; } } /** * Check if a model supports image input based on its catalog entry. */ function modelSupportsVision(entry) { return modelSupportsInput(entry, "image"); } //#endregion export { resetModelCatalogBuilderCacheForTest as a, overlayConfiguredModelCatalog as i, loadManifestModelCatalog as n, loadManifestModelProviderConfigs as o, modelSupportsVision as r, buildPreparedModelCatalogSnapshot as t };