fix(agent): apply configured fast mode to embedded runs

This commit is contained in:
Peter Steinberger
2026-05-01 11:51:38 +01:00
parent f5fde074bd
commit dddf871ad9
5 changed files with 100 additions and 2 deletions

View File

@@ -48,6 +48,7 @@ import { resolveAgentRunContext } from "./command/run-context.js";
import { resolveSession } from "./command/session.js";
import type { AgentCommandIngressOpts, AgentCommandOpts } from "./command/types.js";
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "./defaults.js";
import { resolveFastModeState } from "./fast-mode.js";
import { AGENT_LANE_SUBAGENT } from "./lanes.js";
import { LiveSessionModelSwitchError } from "./live-model-switch.js";
import { loadModelCatalog } from "./model-catalog.js";
@@ -978,6 +979,13 @@ async function agentCommandInternal(
body,
isFallbackRetry,
resolvedThinkLevel,
fastMode: resolveFastModeState({
cfg,
provider: providerOverride,
model: modelOverride,
agentId: sessionAgentId,
sessionEntry,
}).enabled,
timeoutMs,
runId,
opts,

View File

@@ -324,6 +324,7 @@ export function runAgentAttempt(params: {
body: string;
isFallbackRetry: boolean;
resolvedThinkLevel: ThinkLevel;
fastMode?: boolean;
timeoutMs: number;
runId: string;
opts: AgentCommandOpts & { senderIsOwner: boolean };
@@ -577,6 +578,7 @@ export function runAgentAttempt(params: {
authProfileId,
authProfileIdSource: authProfileId ? harnessAuthSelection.authProfileIdSource : undefined,
thinkLevel: params.resolvedThinkLevel,
fastMode: params.fastMode,
verboseLevel: params.resolvedVerboseLevel,
timeoutMs: params.timeoutMs,
runId: params.runId,

View File

@@ -54,6 +54,69 @@ describe("resolveFastModeState", () => {
expect(state.source).toBe("config");
});
it("uses model config when the runtime passes a provider-qualified model ref", () => {
const cfg = {
agents: {
defaults: {
models: {
"openai/gpt-5.5": { params: { fastMode: true } },
},
},
},
} as OpenClawConfig;
const state = resolveFastModeState({
cfg,
provider: "openai",
model: "openai/gpt-5.5",
});
expect(state.enabled).toBe(true);
expect(state.source).toBe("config");
});
it("uses canonical provider/model config for slash-containing model ids", () => {
const cfg = {
agents: {
defaults: {
models: {
"openrouter/anthropic/claude-sonnet-4-6": { params: { fastMode: true } },
},
},
},
} as OpenClawConfig;
const state = resolveFastModeState({
cfg,
provider: "openrouter",
model: "anthropic/claude-sonnet-4-6",
});
expect(state.enabled).toBe(true);
expect(state.source).toBe("config");
});
it("does not use another provider's slash-containing model config", () => {
const cfg = {
agents: {
defaults: {
models: {
"anthropic/claude-sonnet-4-6": { params: { fastMode: true } },
},
},
},
} as OpenClawConfig;
const state = resolveFastModeState({
cfg,
provider: "openrouter",
model: "anthropic/claude-sonnet-4-6",
});
expect(state.enabled).toBe(false);
expect(state.source).toBe("default");
});
it("defaults to off when unset", () => {
const state = resolveFastModeState({
cfg: {} as OpenClawConfig,

View File

@@ -2,6 +2,7 @@ import { normalizeFastMode } from "../auto-reply/thinking.shared.js";
import type { SessionEntry } from "../config/sessions.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { resolveAgentConfig } from "./agent-scope.js";
import { modelKey } from "./model-ref-shared.js";
export type FastModeState = {
enabled: boolean;
@@ -13,8 +14,8 @@ function resolveConfiguredFastModeRaw(params: {
provider: string;
model: string;
}): unknown {
const modelKey = `${params.provider}/${params.model}`;
const modelConfig = params.cfg?.agents?.defaults?.models?.[modelKey];
const modelConfig =
params.cfg?.agents?.defaults?.models?.[modelKey(params.provider, params.model)];
return modelConfig?.params?.fastMode ?? modelConfig?.params?.fast_mode;
}

View File

@@ -105,6 +105,7 @@ vi.mock("../agents/command/attempt-execution.runtime.js", () => {
authProfileId,
authProfileIdSource: authProfileId ? sessionEntry?.authProfileOverrideSource : undefined,
thinkLevel: params.resolvedThinkLevel,
fastMode: params.fastMode,
verboseLevel: params.resolvedVerboseLevel,
timeoutMs: params.timeoutMs,
runId: params.runId,
@@ -385,6 +386,29 @@ describe("agentCommand", () => {
});
});
it("passes configured fast mode to embedded runs", async () => {
await withTempHome(async (home) => {
const store = path.join(home, "sessions.json");
mockConfig(home, store, {
model: "openai/gpt-5.5",
models: {
"openai/gpt-5.5": { params: { fastMode: true } },
},
});
await agentCommand({ message: "ping", agentId: "main" }, runtime);
const callArgs = getLastEmbeddedCall();
expect(callArgs).toEqual(
expect.objectContaining({
provider: "openai",
model: "gpt-5.5",
fastMode: true,
}),
);
});
});
it("does not load the full model catalog for trusted explicit overrides without an allowlist", async () => {
await withTempHome(async (home) => {
const store = path.join(home, "sessions.json");