compaction: add configurable model override

Allow users to specify a different model for compaction summarization
via `agents.defaults.compaction.model`. When set, compaction uses
this model instead of the agent's primary model.

This is useful for hybrid local+cloud setups where the primary model
is a small local model (e.g. Ollama/Qwen) but compaction benefits
from a more capable summarizer (e.g. cloud Claude), or a dedicated
local model fine-tuned for summarization.

Config example:
  "compaction": { "model": "openrouter/anthropic/claude-sonnet-4-5" }

Falls back to the primary model when unset (no behavior change for
existing configs).

Closes #7926

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
starbuck100 2026-03-07 10:23:48 +01:00 committed by Josh Lehman
parent b6520d7172
commit c44cc77fa6
No known key found for this signature in database
GPG Key ID: D141B425AC7F876B
5 changed files with 115 additions and 2 deletions

View File

@ -24,6 +24,36 @@ Compaction **persists** in the sessions JSONL history.
Use the `agents.defaults.compaction` setting in your `openclaw.json` to configure compaction behavior (mode, target tokens, etc.).
Compaction summarization preserves opaque identifiers by default (`identifierPolicy: "strict"`). You can override this with `identifierPolicy: "off"` or provide custom text with `identifierPolicy: "custom"` and `identifierInstructions`.
You can optionally specify a different model for compaction summarization via `agents.defaults.compaction.model`. This is useful when your primary model is a local or small model and you want compaction summaries produced by a more capable model. The override accepts any `provider/model-id` string:
```json
{
"agents": {
"defaults": {
"compaction": {
"model": "openrouter/anthropic/claude-sonnet-4-5"
}
}
}
}
```
This also works with local models, for example a second Ollama model dedicated to summarization or a fine-tuned compaction specialist:
```json
{
"agents": {
"defaults": {
"compaction": {
"model": "ollama/llama3.1:8b"
}
}
}
}
```
When unset, compaction uses the agent's primary model.
## Auto-compaction (default on)
When a session nears or exceeds the models context window, OpenClaw triggers auto-compaction and may retry the original request using the compacted context.

View File

@ -639,6 +639,70 @@ 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("uses compaction.model override when set, splitting provider from model id", () => {
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",
});
expect(legacy).toMatchObject({
provider: "openrouter",
model: "anthropic/claude-sonnet-4-5",
});
});
it("includes resolved auth profile fields for context-engine afterTurn compaction", () => {
const legacy = buildAfterTurnLegacyCompactionParams({
attempt: {

View File

@ -659,6 +659,20 @@ export function buildAfterTurnLegacyCompactionParams(params: {
workspaceDir: string;
agentDir: string;
}): Partial<CompactEmbeddedPiSessionParams> {
// Resolve compaction model: use config override or fall back to primary model
const compactionModelOverride = params.attempt.config?.agents?.defaults?.compaction?.model;
let compactionProvider = params.attempt.provider;
let compactionModelId = params.attempt.modelId;
if (compactionModelOverride) {
const slashIdx = compactionModelOverride.indexOf("/");
if (slashIdx > 0) {
compactionProvider = compactionModelOverride.slice(0, slashIdx);
compactionModelId = compactionModelOverride.slice(slashIdx + 1);
} else {
compactionModelId = compactionModelOverride;
}
}
return {
sessionKey: params.attempt.sessionKey,
messageChannel: params.attempt.messageChannel,
@ -670,8 +684,8 @@ export function buildAfterTurnLegacyCompactionParams(params: {
config: params.attempt.config,
skillsSnapshot: params.attempt.skillsSnapshot,
senderIsOwner: params.attempt.senderIsOwner,
provider: params.attempt.provider,
model: params.attempt.modelId,
provider: compactionProvider,
model: compactionModelId,
thinkLevel: params.attempt.thinkLevel,
reasoningLevel: params.attempt.reasoningLevel,
bashElevated: params.attempt.bashElevated,

View File

@ -322,6 +322,10 @@ export type AgentCompactionConfig = {
* Set to [] to disable post-compaction context injection entirely.
*/
postCompactionSections?: string[];
/** Optional model override for compaction summarization (e.g. "openrouter/anthropic/claude-sonnet-4-5").
* When set, compaction uses this model instead of the agent's primary model.
* Falls back to the primary model when unset. */
model?: string;
};
export type AgentCompactionMemoryFlushConfig = {

View File

@ -104,6 +104,7 @@ export const AgentDefaultsSchema = z
.strict()
.optional(),
postCompactionSections: z.array(z.string()).optional(),
model: z.string().optional(),
memoryFlush: z
.object({
enabled: z.boolean().optional(),