mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-06 06:41:08 +00:00
fix: clear stale liveModelSwitchPending flag when model already matches
When the liveModelSwitchPending 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 now consumed eagerly via a fire-and-forget clearLiveModelSwitchPending() call. Without this, the stale flag could persist across fallback iterations and later cause a spurious LiveSessionModelSwitchError when the model rotates to a fallback candidate that differs from the persisted selection. Also expands JSDoc on shouldSwitchToLiveModel to document the stale-flag clearing and deferral semantics.
This commit is contained in:
committed by
Peter Steinberger
parent
251e086eac
commit
e8f6ceedd4
@@ -350,6 +350,39 @@ describe("live model switch", () => {
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("clears the stale liveModelSwitchPending flag when models already match", async () => {
|
||||
const sessionEntry = {
|
||||
liveModelSwitchPending: true,
|
||||
providerOverride: "anthropic",
|
||||
modelOverride: "claude-opus-4-6",
|
||||
};
|
||||
state.loadSessionStoreMock.mockReturnValue({ main: sessionEntry });
|
||||
state.updateSessionStoreMock.mockImplementation(
|
||||
async (_path: string, updater: (store: Record<string, unknown>) => void) => {
|
||||
const store: Record<string, typeof sessionEntry> = { main: sessionEntry };
|
||||
updater(store);
|
||||
},
|
||||
);
|
||||
|
||||
const { shouldSwitchToLiveModel } = await loadModule();
|
||||
|
||||
const result = shouldSwitchToLiveModel({
|
||||
cfg: { session: { store: "/tmp/custom-store.json" } },
|
||||
sessionKey: "main",
|
||||
agentId: "reply",
|
||||
defaultProvider: "anthropic",
|
||||
defaultModel: "claude-opus-4-6",
|
||||
currentProvider: "anthropic",
|
||||
currentModel: "claude-opus-4-6",
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
// Give the fire-and-forget clearLiveModelSwitchPending a tick to resolve
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
expect(state.updateSessionStoreMock).toHaveBeenCalledTimes(1);
|
||||
expect(sessionEntry).not.toHaveProperty("liveModelSwitchPending");
|
||||
});
|
||||
|
||||
it("returns undefined when sessionKey is missing", async () => {
|
||||
const { shouldSwitchToLiveModel } = await loadModule();
|
||||
|
||||
|
||||
@@ -119,6 +119,17 @@ export function shouldTrackPersistedLiveSessionModelSelection(
|
||||
* `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.
|
||||
@@ -164,6 +175,14 @@ export function shouldSwitchToLiveModel(params: {
|
||||
persisted,
|
||||
)
|
||||
) {
|
||||
// Current model already matches the persisted selection — the switch has
|
||||
// effectively been applied. Clear the stale flag so subsequent fallback
|
||||
// iterations don't re-evaluate it.
|
||||
void clearLiveModelSwitchPending({
|
||||
cfg,
|
||||
sessionKey,
|
||||
agentId: params.agentId,
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
return persisted ?? undefined;
|
||||
|
||||
Reference in New Issue
Block a user