openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
370 lines (369 loc) • 17.6 kB
JavaScript
import { t as createLazyImportLoader } from "./lazy-promise-BONnzNfb.js";
import { i as hasLegacyAutoFallbackWithoutOrigin } from "./agent-scope-MrLta7Pq.js";
import { r as resolveAgentConfig } from "./agent-scope-config-CgCYpZfK.js";
import { i as listOpenAIAuthProfileProvidersForAgentRuntime } from "./openai-routing-DXJmS9CT.js";
import { n as resolveAgentHarnessPolicy } from "./harness-runtimes-B7OdBY2U.js";
import "./defaults-mDjiWzE5.js";
import { f as parseConfiguredModelVisibilityEntries, r as buildConfiguredModelCatalog } from "./model-selection-shared-b32cUYts.js";
import { a as normalizeModelRef, i as modelKey, o as normalizeProviderId, r as legacyModelKey } from "./model-selection-normalize-roKDxQ9_.js";
import { t as resolveThinkingDefault } from "./model-thinking-default-dIc9T64G.js";
import { f as resolveReasoningDefault, u as resolvePersistedOverrideModelRef } from "./model-selection-CA_65iXi.js";
import { s as resolveContextTokensForModel } from "./context-CvrdNrKN.js";
import { t as applyModelOverrideToSessionEntry } from "./model-overrides-D5WaLvcp.js";
import { n as createModelVisibilityPolicy, t as RUNTIME_MODEL_VISIBILITY_NORMALIZATION } from "./model-visibility-policy-B8VgY13t.js";
import { n as resolveStoredModelOverride, t as isStaleHeartbeatAutoFallbackOverride } from "./stored-model-override-B3aWM_od.js";
import { t as clearSessionAuthProfileOverride } from "./session-override-Bjia8kVD.js";
import "./model-selection-directive-qQ0EudS-.js";
//#region src/auto-reply/reply/model-selection.ts
/** Model selection state for reply runs, including catalog and override handling. */
/** Creates minimal model-selection state for fast test mode. */
function createFastTestModelSelectionState(params) {
return {
provider: params.provider,
model: params.model,
allowedModelKeys: /* @__PURE__ */ new Set(),
allowedModelCatalog: [],
resetModelOverride: false,
resetModelOverrideRef: void 0,
resolveThinkingCatalog: async () => [],
resolveDefaultThinkingLevel: async () => params.agentCfg?.thinkingDefault,
resolveDefaultReasoningLevel: async () => "off",
needsModelCatalog: false
};
}
function shouldLogModelSelectionTiming() {
return process.env.OPENCLAW_DEBUG_INGRESS_TIMING === "1";
}
const modelCatalogRuntimeLoader = createLazyImportLoader(() => import("./agents/model-catalog.runtime.js"));
const sessionStoreRuntimeLoader = createLazyImportLoader(() => import("./store.runtime.js"));
function normalizeRuntimeModelRef(provider, model) {
return normalizeModelRef(provider, model, RUNTIME_MODEL_VISIBILITY_NORMALIZATION);
}
function loadModelCatalogRuntime() {
return modelCatalogRuntimeLoader.load();
}
function loadSessionStoreRuntime() {
return sessionStoreRuntimeLoader.load();
}
function findSelectedCatalogEntry(params) {
const selectedKey = modelKey(normalizeProviderId(params.provider), params.model);
return params.catalog?.find((entry) => modelKey(entry.provider, entry.id) === selectedKey);
}
/** Resolves provider/model, allowlist, catalog, and thinking defaults for a reply run. */
async function createModelSelectionState(params) {
const timingEnabled = shouldLogModelSelectionTiming();
const startMs = timingEnabled ? Date.now() : 0;
const logStage = (stage, extra) => {
if (!timingEnabled) return;
const suffix = extra ? ` ${extra}` : "";
console.log(`[model-selection] session=${params.sessionKey ?? "(no-session)"} stage=${stage} elapsedMs=${Date.now() - startMs}${suffix}`);
};
const { cfg, agentCfg, sessionEntry, sessionStore, sessionKey, parentSessionKey, storePath, defaultProvider, defaultModel } = params;
let provider = params.provider;
let model = params.model;
const primaryProvider = params.primaryProvider ?? defaultProvider;
const primaryModel = params.primaryModel ?? defaultModel;
const hasAllowlist = agentCfg?.models && Object.keys(agentCfg.models).length > 0;
const visibility = parseConfiguredModelVisibilityEntries({ cfg });
const defaultProviderVisibleByWildcard = visibility.providerWildcards.has(normalizeProviderId(defaultProvider));
const configuredModelCatalog = buildConfiguredModelCatalog({ cfg });
const needsModelCatalog = params.hasModelDirective || Boolean(hasAllowlist && visibility.providerWildcards.size > 0 && !defaultProviderVisibleByWildcard);
let allowedModelKeys = /* @__PURE__ */ new Set();
let allowedModelCatalog = configuredModelCatalog;
let visibilityPolicy = createModelVisibilityPolicy({
cfg,
catalog: configuredModelCatalog,
defaultProvider,
defaultModel,
agentId: params.agentId,
...RUNTIME_MODEL_VISIBILITY_NORMALIZATION
});
let modelCatalog = null;
let resetModelOverride = false;
let resetModelOverrideRef;
const agentEntry = params.agentId ? resolveAgentConfig(cfg, params.agentId) : void 0;
const directStoredOverride = resolvePersistedOverrideModelRef({
defaultProvider,
overrideProvider: sessionEntry?.providerOverride,
overrideModel: sessionEntry?.modelOverride
});
const directStoredModelOverride = directStoredOverride ? {
...directStoredOverride,
source: "session"
} : null;
const staleHeartbeatAutoFallbackOverride = isStaleHeartbeatAutoFallbackOverride({
isHeartbeat: params.isHeartbeat,
hasResolvedHeartbeatModelOverride: params.hasResolvedHeartbeatModelOverride,
sessionEntry,
storedOverride: directStoredModelOverride,
defaultProvider,
defaultModel,
primaryProvider: params.primaryProvider,
primaryModel: params.primaryModel
});
const primaryHarnessPolicy = resolveAgentHarnessPolicy({
provider: primaryProvider,
modelId: primaryModel,
config: cfg,
agentId: params.agentId,
sessionKey
});
const staleLegacyOpenAICodexAutoOverride = directStoredModelOverride?.source === "session" && sessionEntry?.modelOverrideSource === "auto" && normalizeProviderId(directStoredModelOverride.provider ?? "") === "openai" && normalizeProviderId(primaryProvider) === "openai" && primaryHarnessPolicy.runtime === "codex" && normalizeRuntimeModelRef("openai", directStoredModelOverride.model).model === normalizeRuntimeModelRef("openai", primaryModel).model;
const normalizedCurrentSelection = normalizeRuntimeModelRef(provider, model);
const normalizedDirectOverride = directStoredModelOverride ? normalizeRuntimeModelRef(directStoredModelOverride.provider, directStoredModelOverride.model) : null;
const staleLegacyAutoFallbackWithoutOrigin = directStoredModelOverride?.source === "session" && hasLegacyAutoFallbackWithoutOrigin(sessionEntry) && normalizedDirectOverride !== null && modelKey(normalizedCurrentSelection.provider, normalizedCurrentSelection.model) !== modelKey(normalizedDirectOverride.provider, normalizedDirectOverride.model);
const staleDirectStoredOverride = staleHeartbeatAutoFallbackOverride || staleLegacyOpenAICodexAutoOverride || staleLegacyAutoFallbackWithoutOrigin;
if (needsModelCatalog) {
modelCatalog = await (await loadModelCatalogRuntime()).loadModelCatalog({ config: cfg });
logStage("catalog-loaded", `entries=${modelCatalog.length}`);
visibilityPolicy = createModelVisibilityPolicy({
cfg,
catalog: modelCatalog,
defaultProvider,
defaultModel,
agentId: params.agentId,
...RUNTIME_MODEL_VISIBILITY_NORMALIZATION
});
allowedModelCatalog = visibilityPolicy.allowedCatalog;
allowedModelKeys = visibilityPolicy.allowedKeys;
logStage("allowlist-built", `allowed=${allowedModelCatalog.length} keys=${allowedModelKeys.size}`);
} else if (hasAllowlist) {
visibilityPolicy = createModelVisibilityPolicy({
cfg,
catalog: configuredModelCatalog,
defaultProvider,
defaultModel,
agentId: params.agentId,
...RUNTIME_MODEL_VISIBILITY_NORMALIZATION
});
allowedModelCatalog = visibilityPolicy.allowedCatalog;
allowedModelKeys = visibilityPolicy.allowedKeys;
logStage("configured-allowlist-built", `allowed=${allowedModelCatalog.length} keys=${allowedModelKeys.size}`);
} else if (configuredModelCatalog.length > 0) logStage("configured-catalog-ready", `entries=${configuredModelCatalog.length}`);
if (sessionEntry && sessionStore && sessionKey && directStoredOverride) {
const normalizedOverride = normalizeRuntimeModelRef(directStoredOverride.provider, directStoredOverride.model);
const key = modelKey(normalizedOverride.provider, normalizedOverride.model);
if (staleDirectStoredOverride || !visibilityPolicy.allowsKey(key)) {
const { updated } = applyModelOverrideToSessionEntry({
entry: sessionEntry,
selection: {
provider: primaryProvider,
model: primaryModel,
isDefault: true
},
preserveAuthProfileOverride: staleDirectStoredOverride
});
if (updated) {
sessionStore[sessionKey] = sessionEntry;
if (storePath) await (await loadSessionStoreRuntime()).updateSessionStore(storePath, (store) => {
store[sessionKey] = sessionEntry;
});
}
resetModelOverride = updated;
if (updated) resetModelOverrideRef = key;
}
}
if (staleDirectStoredOverride) {
if (modelKey(normalizedCurrentSelection.provider, normalizedCurrentSelection.model) === (normalizedDirectOverride ? modelKey(normalizedDirectOverride.provider, normalizedDirectOverride.model) : void 0)) {
provider = primaryProvider;
model = primaryModel;
}
}
const storedOverride = resolveStoredModelOverride({
sessionEntry,
sessionStore,
sessionKey,
parentSessionKey,
defaultProvider
});
const skipStoredOverride = params.skipStoredModelOverride === true || params.hasResolvedHeartbeatModelOverride === true || staleDirectStoredOverride && storedOverride?.source === "session";
if (storedOverride?.model && !skipStoredOverride) {
const normalizedStoredOverride = normalizeRuntimeModelRef(storedOverride.provider || defaultProvider, storedOverride.model);
const key = modelKey(normalizedStoredOverride.provider, normalizedStoredOverride.model);
if (visibilityPolicy.allowsKey(key)) {
provider = normalizedStoredOverride.provider;
model = normalizedStoredOverride.model;
}
}
if (!params.hasModelDirective) {
const allowedInitialSelection = visibilityPolicy.resolveSelection({
provider,
model
});
if (!allowedInitialSelection) throw new Error(`Configured default model "${modelKey(provider, model)}" is not allowed by agents.defaults.models, and no allowed model is available.`);
provider = allowedInitialSelection.provider;
model = allowedInitialSelection.model;
}
if (!params.skipStoredModelOverride && sessionEntry && sessionStore && sessionKey && sessionEntry.authProfileOverride) {
const { ensureAuthProfileStore } = await import("./agents/auth-profiles.runtime.js");
const store = ensureAuthProfileStore(void 0, { allowKeychainPrompt: false });
logStage("auth-profile-store-loaded", `profiles=${Object.keys(store.profiles).length}`);
const profile = store.profiles[sessionEntry.authProfileOverride];
const profileProvider = profile ? normalizeProviderId(profile.provider) : void 0;
const harnessPolicy = resolveAgentHarnessPolicy({
provider,
modelId: model,
config: cfg,
agentId: params.agentId,
sessionKey
});
const acceptedAuthProviders = listOpenAIAuthProfileProvidersForAgentRuntime({
provider,
harnessRuntime: harnessPolicy.runtime,
config: cfg
}).map(normalizeProviderId);
if (!profile || !acceptedAuthProviders.includes(profileProvider ?? "")) await clearSessionAuthProfileOverride({
sessionEntry,
sessionStore,
sessionKey,
storePath
});
}
let thinkingCatalog;
let manifestModelCatalog = null;
const buildThinkingCatalog = (catalog) => createModelVisibilityPolicy({
cfg,
catalog,
defaultProvider,
defaultModel,
agentId: params.agentId,
...RUNTIME_MODEL_VISIBILITY_NORMALIZATION
}).allowedCatalog;
const loadManifestCatalog = async () => {
if (manifestModelCatalog) return manifestModelCatalog;
const { loadManifestModelCatalog } = await loadModelCatalogRuntime();
manifestModelCatalog = loadManifestModelCatalog({
config: cfg,
fallbackToMetadataScan: false
});
logStage("manifest-catalog-loaded", `entries=${manifestModelCatalog.length}`);
return manifestModelCatalog;
};
const resolveThinkingCatalog = async () => {
if (thinkingCatalog) return thinkingCatalog;
let catalogForThinking = allowedModelCatalog.length > 0 ? allowedModelCatalog : modelCatalog && modelCatalog.length > 0 ? buildThinkingCatalog(modelCatalog) : [];
let selectedCatalogEntry = findSelectedCatalogEntry({
catalog: catalogForThinking,
provider,
model
});
if (!modelCatalog && selectedCatalogEntry?.reasoning === void 0) {
const manifestCatalog = buildThinkingCatalog(await loadManifestCatalog());
const manifestSelectedEntry = findSelectedCatalogEntry({
catalog: manifestCatalog,
provider,
model
});
if (manifestSelectedEntry?.reasoning !== void 0) {
catalogForThinking = manifestCatalog;
selectedCatalogEntry = manifestSelectedEntry;
}
}
if (!modelCatalog && (!selectedCatalogEntry || selectedCatalogEntry.reasoning === void 0)) {
modelCatalog = await (await loadModelCatalogRuntime()).loadModelCatalog({ config: cfg });
logStage("catalog-loaded-for-thinking", `entries=${modelCatalog.length}`);
const runtimeCatalog = buildThinkingCatalog(modelCatalog);
catalogForThinking = findSelectedCatalogEntry({
catalog: runtimeCatalog,
provider,
model
}) || !catalogForThinking || catalogForThinking.length === 0 ? runtimeCatalog.length > 0 ? runtimeCatalog : allowedModelCatalog : allowedModelCatalog;
}
thinkingCatalog = catalogForThinking.length > 0 ? catalogForThinking : void 0;
return thinkingCatalog;
};
let defaultThinkingLevel;
const resolveDefaultThinkingLevel = async () => {
if (defaultThinkingLevel) return defaultThinkingLevel;
const agentThinkingDefault = agentEntry?.thinkingDefault;
if (agentThinkingDefault) {
defaultThinkingLevel = agentThinkingDefault;
return defaultThinkingLevel;
}
const configuredModels = cfg.agents?.defaults?.models;
const canonicalKey = modelKey(provider, model);
const legacyKey = legacyModelKey(provider, model);
const configuredModelThinkingDefault = configuredModels?.[canonicalKey]?.params?.thinking ?? (legacyKey ? configuredModels?.[legacyKey]?.params?.thinking : void 0);
if (configuredModelThinkingDefault === false || configuredModelThinkingDefault === "disabled" || configuredModelThinkingDefault === "none") {
defaultThinkingLevel = "off";
return defaultThinkingLevel;
}
if (configuredModelThinkingDefault === "off" || configuredModelThinkingDefault === "minimal" || configuredModelThinkingDefault === "low" || configuredModelThinkingDefault === "medium" || configuredModelThinkingDefault === "high" || configuredModelThinkingDefault === "xhigh" || configuredModelThinkingDefault === "adaptive" || configuredModelThinkingDefault === "max") {
defaultThinkingLevel = configuredModelThinkingDefault;
return defaultThinkingLevel;
}
const configuredThinkingDefault = agentCfg?.thinkingDefault;
if (configuredThinkingDefault) {
defaultThinkingLevel = configuredThinkingDefault;
return defaultThinkingLevel;
}
const catalogForThinking = await resolveThinkingCatalog();
defaultThinkingLevel = resolveThinkingDefault({
cfg,
provider,
model,
catalog: catalogForThinking
}) ?? "off";
return defaultThinkingLevel;
};
let defaultReasoningLevel;
const resolveDefaultReasoningLevel = async () => {
if (defaultReasoningLevel) return defaultReasoningLevel;
let catalogForReasoning = modelCatalog ?? allowedModelCatalog;
let selectedReasoningEntry = findSelectedCatalogEntry({
catalog: catalogForReasoning,
provider,
model
});
if (!modelCatalog && selectedReasoningEntry?.reasoning === void 0) {
const manifestCatalog = await loadManifestCatalog();
const manifestReasoningCatalog = hasAllowlist ? buildThinkingCatalog(manifestCatalog) : manifestCatalog;
const manifestSelectedEntry = findSelectedCatalogEntry({
catalog: manifestReasoningCatalog,
provider,
model
});
if (manifestSelectedEntry?.reasoning !== void 0) {
catalogForReasoning = manifestReasoningCatalog;
selectedReasoningEntry = manifestSelectedEntry;
}
}
if ((!catalogForReasoning || catalogForReasoning.length === 0) && selectedReasoningEntry?.reasoning === void 0) {
modelCatalog = await (await loadModelCatalogRuntime()).loadModelCatalog({ config: cfg });
logStage("catalog-loaded-for-reasoning", `entries=${modelCatalog.length}`);
catalogForReasoning = modelCatalog;
}
defaultReasoningLevel = resolveReasoningDefault({
provider,
model,
catalog: catalogForReasoning
});
return defaultReasoningLevel;
};
return {
provider,
model,
allowedModelKeys,
allowedModelCatalog,
resetModelOverride,
resetModelOverrideRef,
resolveThinkingCatalog,
resolveDefaultThinkingLevel,
resolveDefaultReasoningLevel,
needsModelCatalog
};
}
/** Resolves the context window token count for the selected provider/model. */
function resolveContextTokens(params) {
const modelContextTokens = resolveContextTokensForModel({
cfg: params.cfg,
provider: params.provider,
model: params.model,
allowAsyncLoad: false
});
const agentContextTokens = typeof params.agentCfg?.contextTokens === "number" && params.agentCfg.contextTokens > 0 ? Math.floor(params.agentCfg.contextTokens) : void 0;
if (agentContextTokens !== void 0) return modelContextTokens !== void 0 ? Math.min(agentContextTokens, modelContextTokens) : agentContextTokens;
return modelContextTokens ?? 2e5;
}
//#endregion
export { createModelSelectionState as n, resolveContextTokens as r, createFastTestModelSelectionState as t };