feat: allow compaction model override via config (#38753)

Merged via squash.

Prepared head SHA: a3d6d6c845
Co-authored-by: starbuck100 <25417736+starbuck100@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
GitBuck
2026-03-08 18:47:34 +01:00
committed by GitHub
parent b6520d7172
commit caf1b84822
12 changed files with 143 additions and 4 deletions

View File

@@ -52,7 +52,10 @@ describe("models-config merge helpers", () => {
it("merges explicit providers onto trimmed keys", () => {
const merged = mergeProviders({
explicit: {
" custom ": { api: "openai-responses", models: [] } as ProviderConfig,
" custom ": {
api: "openai-responses",
models: [] as ProviderConfig["models"],
} as ProviderConfig,
},
});

View File

@@ -271,8 +271,31 @@ export async function compactEmbeddedPiSessionDirect(
const resolvedWorkspace = resolveUserPath(params.workspaceDir);
const prevCwd = process.cwd();
const provider = (params.provider ?? DEFAULT_PROVIDER).trim() || DEFAULT_PROVIDER;
const modelId = (params.model ?? DEFAULT_MODEL).trim() || DEFAULT_MODEL;
// Resolve compaction model: prefer config override, then fall back to caller-supplied model
const compactionModelOverride = params.config?.agents?.defaults?.compaction?.model?.trim();
let provider: string;
let modelId: string;
// When switching provider via override, drop the primary auth profile to avoid
// sending the wrong credentials (e.g. OpenAI profile token to OpenRouter).
let authProfileId: string | undefined = params.authProfileId;
if (compactionModelOverride) {
const slashIdx = compactionModelOverride.indexOf("/");
if (slashIdx > 0) {
provider = compactionModelOverride.slice(0, slashIdx).trim();
modelId = compactionModelOverride.slice(slashIdx + 1).trim() || DEFAULT_MODEL;
// Provider changed — drop primary auth profile so getApiKeyForModel
// falls back to provider-based key resolution for the override model.
if (provider !== (params.provider ?? "").trim()) {
authProfileId = undefined;
}
} else {
provider = (params.provider ?? DEFAULT_PROVIDER).trim() || DEFAULT_PROVIDER;
modelId = compactionModelOverride;
}
} else {
provider = (params.provider ?? DEFAULT_PROVIDER).trim() || DEFAULT_PROVIDER;
modelId = (params.model ?? DEFAULT_MODEL).trim() || DEFAULT_MODEL;
}
const fail = (reason: string): EmbeddedPiCompactResult => {
log.warn(
`[compaction-diag] end runId=${runId} sessionKey=${params.sessionKey ?? params.sessionId} ` +
@@ -302,7 +325,7 @@ export async function compactEmbeddedPiSessionDirect(
const apiKeyInfo = await getApiKeyForModel({
model,
cfg: params.config,
profileId: params.authProfileId,
profileId: authProfileId,
agentDir,
});

View File

@@ -639,6 +639,72 @@ describe("prependSystemPromptAddition", () => {
});
describe("buildAfterTurnLegacyCompactionParams", () => {
it("uses primary model when compaction.model is not set", () => {
const legacy = buildAfterTurnLegacyCompactionParams({
attempt: {
sessionKey: "agent:main:session:abc",
messageChannel: "slack",
messageProvider: "slack",
agentAccountId: "acct-1",
authProfileId: "openai:p1",
config: {} as OpenClawConfig,
skillsSnapshot: undefined,
senderIsOwner: true,
provider: "openai-codex",
modelId: "gpt-5.3-codex",
thinkLevel: "off",
reasoningLevel: "on",
extraSystemPrompt: "extra",
ownerNumbers: ["+15555550123"],
},
workspaceDir: "/tmp/workspace",
agentDir: "/tmp/agent",
});
expect(legacy).toMatchObject({
provider: "openai-codex",
model: "gpt-5.3-codex",
});
});
it("passes primary model through even when compaction.model is set (override resolved in compactDirect)", () => {
const legacy = buildAfterTurnLegacyCompactionParams({
attempt: {
sessionKey: "agent:main:session:abc",
messageChannel: "slack",
messageProvider: "slack",
agentAccountId: "acct-1",
authProfileId: "openai:p1",
config: {
agents: {
defaults: {
compaction: {
model: "openrouter/anthropic/claude-sonnet-4-5",
},
},
},
} as OpenClawConfig,
skillsSnapshot: undefined,
senderIsOwner: true,
provider: "openai-codex",
modelId: "gpt-5.3-codex",
thinkLevel: "off",
reasoningLevel: "on",
extraSystemPrompt: "extra",
ownerNumbers: ["+15555550123"],
},
workspaceDir: "/tmp/workspace",
agentDir: "/tmp/agent",
});
// buildAfterTurnLegacyCompactionParams no longer resolves the override;
// compactEmbeddedPiSessionDirect does it centrally for both auto + manual paths.
expect(legacy).toMatchObject({
provider: "openai-codex",
model: "gpt-5.3-codex",
});
});
it("includes resolved auth profile fields for context-engine afterTurn compaction", () => {
const legacy = buildAfterTurnLegacyCompactionParams({
attempt: {