fix: keep scoped model policy consistent

This commit is contained in:
Dallin Romney
2026-08-20 23:31:02 -07:00
parent e82644aa87
commit ed2b657d6a
12 changed files with 108 additions and 11 deletions
+24
View File
@@ -597,6 +597,30 @@ describe("openrouter provider hooks", () => {
expect(contribution?.dynamicSuffix).not.toContain("google/gemini-3.5-flash");
});
it("omits Fusion prompt details when a narrower scope disables extra body", async () => {
const provider = await registerSingleProviderPlugin(openrouterPlugin);
const contribution = provider.resolveSystemPromptContribution?.({
provider: "openrouter",
modelId: "openrouter/fusion",
promptMode: "full",
agentId: "analyst",
config: {
agents: {
defaults: {
params: {
extra_body: {
plugins: [{ id: "fusion", analysis_models: ["default/model"] }],
},
},
},
entries: { analyst: { params: { extraBody: null } } },
},
},
} as never);
expect(contribution).toBeUndefined();
});
it("reads per-agent Fusion config from the canonical agent roster", async () => {
const provider = await registerSingleProviderPlugin(openrouterPlugin);
const contribution = provider.resolveSystemPromptContribution?.({
+4 -5
View File
@@ -162,12 +162,11 @@ function resolveFusionExtraBody(
];
let effective: Record<string, unknown> | undefined;
for (const source of sources) {
const raw =
source && Object.hasOwn(source, "extra_body") ? source.extra_body : source?.extraBody;
const candidate = readRecord(raw);
if (candidate) {
effective = candidate;
if (!source || (!Object.hasOwn(source, "extra_body") && !Object.hasOwn(source, "extraBody"))) {
continue;
}
const raw = Object.hasOwn(source, "extra_body") ? source.extra_body : source.extraBody;
effective = readRecord(raw);
}
return effective;
}
+1
View File
@@ -514,6 +514,7 @@ export async function resolveEmbeddedModelSelection(params: {
cfg: params.cfg,
provider,
model,
agentId: params.sessionAgentId,
});
let catalogForThinking =
allowedModelCatalog.length > 0
@@ -386,6 +386,7 @@ export async function runEmbeddedAgentAttempt(params: {
cfg,
provider: providerOverride,
model: modelOverride,
agentId: sessionAgentId,
});
let candidateThinkingCatalog = thinkingCatalog;
if (
@@ -220,6 +220,7 @@ export async function prepareEmbeddedRunRuntime(input: {
provider,
modelId,
model: effectiveModel,
agentId: params.agentId,
});
const initialThinkLevel = modelSelectionChangedByHook
? (resolveCandidateThinkingLevel({
@@ -26,4 +26,27 @@ describe("resolveInitialThinkLevel", () => {
}),
).toBe("ultra");
});
it("uses the selected agent model thinking default", () => {
expect(
resolveInitialThinkLevel({
config: {
agents: {
defaults: {
models: { "openai/gpt-5.5": { params: { thinking: "low" } } },
},
entries: {
audit: {
models: { "openai/gpt-5.5": { params: { thinking: "high" } } },
},
},
},
},
provider: "openai",
modelId: "gpt-5.5",
model: { reasoning: true },
agentId: "audit",
}),
).toBe("high");
});
});
@@ -57,6 +57,7 @@ export function resolveInitialThinkLevel(params: {
provider: string;
modelId: string;
model: { reasoning?: boolean };
agentId?: string;
}): ThinkLevel {
if (params.requested) {
return params.requested;
@@ -65,6 +66,7 @@ export function resolveInitialThinkLevel(params: {
cfg: params.config ?? {},
provider: params.provider,
model: params.modelId,
agentId: params.agentId,
catalog: [
{
provider: params.provider,
+12 -5
View File
@@ -14,6 +14,7 @@ import {
} from "../auto-reply/thinking.shared.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import type { ModelCatalogEntry } from "./model-catalog.types.js";
import { resolveModelExtraParamSources } from "./model-extra-params.js";
import { legacyModelKey, modelKey, normalizeProviderId } from "./model-ref-shared.js";
import { normalizeModelSelection } from "./model-selection-resolve.js";
import { buildConfiguredModelCatalog } from "./model-selection-shared.js";
@@ -24,19 +25,25 @@ type ThinkingDefaultParams = {
model: string;
catalog?: ModelCatalogEntry[];
agentRuntime?: string | null;
agentId?: string;
};
export function resolveConfiguredThinkingDefaultCore(params: {
cfg: OpenClawConfig;
provider: string;
model: string;
agentId?: string;
}): ThinkLevel | undefined {
const configuredModels = params.cfg.agents?.defaults?.models;
const canonicalKey = modelKey(params.provider, params.model);
const legacyKey = legacyModelKey(params.provider, params.model);
const { modelParams, agentModelParams } = resolveModelExtraParamSources({
config: params.cfg,
provider: params.provider,
modelId: params.model,
agentId: params.agentId,
});
const perModelThinking =
configuredModels?.[canonicalKey]?.params?.thinking ??
(legacyKey ? configuredModels?.[legacyKey]?.params?.thinking : undefined);
agentModelParams && Object.hasOwn(agentModelParams, "thinking")
? agentModelParams.thinking
: modelParams?.thinking;
if (
perModelThinking === false ||
perModelThinking === "disabled" ||
+3
View File
@@ -17,6 +17,7 @@ export function resolveConfiguredThinkingDefault(params: {
cfg: OpenClawConfig;
provider: string;
model: string;
agentId?: string;
}): ThinkLevel | undefined {
return resolveConfiguredThinkingDefaultCore(params);
}
@@ -28,6 +29,7 @@ export function resolveThinkingDefault(params: {
model: string;
catalog?: ModelCatalogEntry[];
agentRuntime?: string | null;
agentId?: string;
}): ThinkLevel {
return resolveThinkingDefaultCore(params);
}
@@ -39,6 +41,7 @@ export async function resolveThinkingDefaultWithRuntimeCatalog(params: {
model: string;
loadRuntimeCatalog: () => Promise<ModelCatalogEntry[]>;
agentRuntime?: string | null;
agentId?: string;
}): Promise<ThinkLevel> {
const configuredCatalog = buildConfiguredModelCatalog({ cfg: params.cfg });
const configuredSelectedEntry = configuredCatalog.find(
+29
View File
@@ -305,4 +305,33 @@ describe("Anthropic server compaction host threshold", () => {
}),
).toBe(expected);
});
it("uses the selected agent context window for the fallback threshold", () => {
const cfg: OpenClawConfig = {
agents: {
defaults: {
params: { anthropicServerCompaction: true },
models: {
"anthropic/claude-opus-4-7": { params: { context1m: false } },
},
},
entries: {
audit: {
models: {
"anthropic/claude-opus-4-7": { params: { context1m: true } },
},
},
},
},
};
expect(
resolveResponsesServerCompactionThreshold({
cfg,
provider: "anthropic",
modelId: "claude-opus-4-7",
agentId: "audit",
}),
).toBe(700_000);
});
});
+7 -1
View File
@@ -78,7 +78,12 @@ export function resolveResponsesServerCompactionThreshold(params: {
baseUrl: configuredModel?.baseUrl ?? providerConfig?.baseUrl,
contextWindow:
configuredModel?.contextWindow ??
resolveMemoryFlushContextWindowTokens({ cfg: params.cfg, provider, modelId }),
resolveMemoryFlushContextWindowTokens({
cfg: params.cfg,
provider,
modelId,
agentId: params.agentId,
}),
},
extraParams,
).threshold;
@@ -89,6 +94,7 @@ export function resolveResponsesServerCompactionThreshold(params: {
cfg: params.cfg,
provider,
modelId,
agentId: params.agentId,
});
return resolveOpenAIResponsesServerCompactionPlan(
{
+1
View File
@@ -468,6 +468,7 @@ function createCronPromptExecutor(params: {
cfg: params.cfgWithAgentDefaults,
provider: providerOverride,
model: modelOverride,
agentId: params.agentId,
});
if (
candidateConfiguredThinkLevel !== "off" &&