mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 11:25:50 -06:00
fix: scope global model results
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
} from "../../lib/sessions/index.ts";
|
||||
import {
|
||||
areUiSessionKeysEquivalent,
|
||||
isUiGlobalSessionKey,
|
||||
resolveUiDefaultAgentId,
|
||||
type UiSessionDefaultsHost,
|
||||
} from "../../lib/sessions/session-key.ts";
|
||||
@@ -137,6 +138,21 @@ function isChatCommandTargetCurrent(host: ChatCommandHost, target: ChatCommandTa
|
||||
);
|
||||
}
|
||||
|
||||
function isChatCommandModelCacheOwnerCurrent(
|
||||
host: ChatCommandHost,
|
||||
target: ChatCommandTarget,
|
||||
): boolean {
|
||||
if (!isChatCommandConnectionCurrent(host, target)) {
|
||||
return false;
|
||||
}
|
||||
// The selected-agent global session shares one UI cache key across agents.
|
||||
// Keep delayed results out when that selection changes on the same Gateway.
|
||||
return (
|
||||
!isUiGlobalSessionKey(target.sessionKey) ||
|
||||
scopedAgentIdForSession(host, target.sessionKey) === target.agentId
|
||||
);
|
||||
}
|
||||
|
||||
export function readChatResetTargetAccess(
|
||||
host: ChatCommandHost,
|
||||
target: ChatCommandTarget,
|
||||
@@ -461,7 +477,7 @@ export async function dispatchChatSlashCommand(
|
||||
if (result.sessionPatch && "modelOverride" in result.sessionPatch) {
|
||||
// A route switch on the same Gateway still owns the originating session's
|
||||
// cache. A replacement connection must not consume this late command result.
|
||||
if (isChatCommandConnectionCurrent(host, target)) {
|
||||
if (isChatCommandModelCacheOwnerCurrent(host, target)) {
|
||||
host.sessions.setModelOverride(
|
||||
target.sessionKey,
|
||||
result.sessionPatch.modelOverride?.value ?? null,
|
||||
|
||||
@@ -4490,6 +4490,42 @@ describe("handleSendChat", () => {
|
||||
expect(host.sessions.state.modelOverrides[item.sessionKey]).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not apply a late global model result after the selected agent changes", async () => {
|
||||
const command = createDeferred<Awaited<ReturnType<ExecuteSlashCommand>>>();
|
||||
executeSlashCommandMock.mockImplementationOnce(() => command.promise);
|
||||
|
||||
const item = createQueuedLocalCommand("switched-global-model-command", "/model gpt-5-mini", {
|
||||
sessionKey: "global",
|
||||
});
|
||||
const host = makeHost({
|
||||
requestHandlers: {
|
||||
"chat.history": () => idleChatHistory(item.sessionKey),
|
||||
},
|
||||
assistantAgentId: "work",
|
||||
agentsList: { defaultId: "main" },
|
||||
chatQueue: [item],
|
||||
sessionKey: item.sessionKey,
|
||||
});
|
||||
const setModelOverride = vi.spyOn(host.sessions, "setModelOverride");
|
||||
expect(admitQueuedMessageForSession(host, item.sessionKey, item)).toBe(true);
|
||||
|
||||
const draining = retryReconnectableQueuedChatSends(host);
|
||||
await waitForFast(() => expect(executeSlashCommandMock).toHaveBeenCalledTimes(1));
|
||||
|
||||
host.assistantAgentId = "main";
|
||||
command.resolve({
|
||||
action: "refresh",
|
||||
content: "Model set to `gpt-5-mini`.",
|
||||
sessionPatch: {
|
||||
modelOverride: { kind: "qualified", value: "openai/gpt-5-mini" },
|
||||
},
|
||||
});
|
||||
await draining;
|
||||
|
||||
expect(setModelOverride).not.toHaveBeenCalled();
|
||||
expect(host.sessions.state.modelOverrides[item.sessionKey]).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not borrow a replacement connection error for a stale queued command", async () => {
|
||||
const command = createDeferred<Awaited<ReturnType<ExecuteSlashCommand>>>();
|
||||
executeSlashCommandMock.mockImplementationOnce(() => command.promise);
|
||||
|
||||
Reference in New Issue
Block a user