mirror of https://github.com/openclaw/openclaw.git
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:
parent
b6520d7172
commit
c44cc77fa6
|
|
@ -24,6 +24,36 @@ Compaction **persists** in the session’s 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 model’s context window, OpenClaw triggers auto-compaction and may retry the original request using the compacted context.
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue