UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

127 lines (126 loc) 5.49 kB
import { c as normalizeOptionalString } from "./string-coerce-mnp54Vah.js"; import { i as normalizeProviderId } from "./provider-id-Dq06Bcx6.js"; import { u as resolveStorePath } from "./paths-NEwU8m3X.js"; import { t as loadSessionStore } from "./store-load-C4uq6Lrq.js"; import { d as updateSessionStore } from "./store-Qsgtu-0y.js"; import { c as resolveDefaultModelForAgent, d as resolvePersistedSelectedModelRef, i as normalizeStoredOverrideModel } from "./model-selection-CA_65iXi.js"; import "./runs-B4dP_Q30.js"; //#region src/agents/live-model-switch.ts /** * Resolves and persists live-session model switch requests. */ const OPENAI_PROVIDER_ID = "openai"; const OPENAI_CODEX_PROVIDER_ID = "openai"; function resolveLiveSessionModelSelection(params) { const sessionKey = normalizeOptionalString(params.sessionKey); const cfg = params.cfg; if (!cfg || !sessionKey) return null; const agentId = normalizeOptionalString(params.agentId); const defaultModelRef = agentId ? resolveDefaultModelForAgent({ cfg, agentId }) : { provider: params.defaultProvider, model: params.defaultModel }; const entry = loadSessionStore(resolveStorePath(cfg.session?.store, { agentId }), { hydrateSkillPromptRefs: false, skipCache: true })[sessionKey]; const normalizedSelection = normalizeStoredOverrideModel({ providerOverride: entry?.providerOverride, modelOverride: entry?.modelOverride }); const persisted = resolvePersistedSelectedModelRef({ defaultProvider: defaultModelRef.provider, runtimeProvider: entry?.modelProvider, runtimeModel: entry?.model, overrideProvider: normalizedSelection.providerOverride, overrideModel: normalizedSelection.modelOverride }); const provider = persisted?.provider ?? normalizedSelection.providerOverride ?? entry?.providerOverride?.trim() ?? defaultModelRef.provider; const model = persisted?.model ?? defaultModelRef.model; const authProfileId = normalizeOptionalString(entry?.authProfileOverride); return { provider, model, authProfileId, authProfileIdSource: authProfileId ? entry?.authProfileOverrideSource : void 0 }; } function isAlreadyAppliedOpenAICodexRuntimePromotion(current, next) { return normalizeProviderId(current.provider) === OPENAI_CODEX_PROVIDER_ID && normalizeProviderId(next.provider) === OPENAI_PROVIDER_ID && current.model === next.model; } function hasDifferentLiveSessionModelSelection(current, next) { if (!next) return false; return (current.provider !== next.provider || current.model !== next.model) && !isAlreadyAppliedOpenAICodexRuntimePromotion(current, next) || normalizeOptionalString(current.authProfileId) !== next.authProfileId || (normalizeOptionalString(current.authProfileId) ? current.authProfileIdSource : void 0) !== next.authProfileIdSource; } /** * Check whether a user-initiated live model switch is pending for the given * session. Returns the persisted model selection when the session's * `liveModelSwitchPending` flag is `true` AND the persisted selection differs * from the currently running model; otherwise returns `undefined`. * * When the flag is set but the current model already matches the persisted * selection (e.g. the switch was applied as an override and the current * attempt is already using the new model), the flag is consumed (cleared) * eagerly to prevent it from persisting as stale state. * * **Deferral semantics:** The caller in `run.ts` only acts on the returned * selection when `canRestartForLiveSwitch` is `true`. If the run cannot * restart (e.g. a tool call is in progress), the flag intentionally remains * set so the switch fires on the next clean retry opportunity — even if that * falls into a subsequent user turn. * * This replaces the previous approach that used an in-memory map * (`consumeEmbeddedRunModelSwitch`) which could not distinguish between * user-initiated `/model` switches and system-initiated fallback rotations. */ function shouldSwitchToLiveModel(params) { const sessionKey = params.sessionKey?.trim(); const cfg = params.cfg; if (!cfg || !sessionKey) return; if (!loadSessionStore(resolveStorePath(cfg.session?.store, { agentId: params.agentId?.trim() }), { hydrateSkillPromptRefs: false, skipCache: true, clone: false })[sessionKey]?.liveModelSwitchPending) return; const persisted = resolveLiveSessionModelSelection({ cfg, sessionKey, agentId: params.agentId, defaultProvider: params.defaultProvider, defaultModel: params.defaultModel }); if (!hasDifferentLiveSessionModelSelection({ provider: params.currentProvider, model: params.currentModel, authProfileId: params.currentAuthProfileId, authProfileIdSource: params.currentAuthProfileIdSource }, persisted)) { clearLiveModelSwitchPending({ cfg, sessionKey, agentId: params.agentId }).catch(() => {}); return; } return persisted ?? void 0; } /** * Clear the `liveModelSwitchPending` flag from the session entry on disk so * subsequent retry iterations do not re-trigger the switch. */ async function clearLiveModelSwitchPending(params) { const sessionKey = params.sessionKey?.trim(); const cfg = params.cfg; if (!cfg || !sessionKey) return; const storePath = resolveStorePath(cfg.session?.store, { agentId: params.agentId?.trim() }); if (!storePath) return; await updateSessionStore(storePath, (store) => { const entry = store[sessionKey]; if (entry) delete entry.liveModelSwitchPending; }); } //#endregion export { shouldSwitchToLiveModel as n, clearLiveModelSwitchPending as t };