Agents: add lean local model mode (#66495)
Merged via squash. Prepared head SHA: d88da6082c1ce2a9b497e19075540c384d7bf1e3 Co-authored-by: ImLukeF <92253590+ImLukeF@users.noreply.github.com> Co-authored-by: ImLukeF <92253590+ImLukeF@users.noreply.github.com> Reviewed-by: @ImLukeF
This commit is contained in:
@@ -7,6 +7,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Changes
|
||||
|
||||
- Docs/showcase: add a scannable hero, complete section jump links, and a responsive video grid for community examples. (#48493) Thanks @jchopard69.
|
||||
- Agents/local models: add `agents.defaults.localModelMode: "lean"` to drop heavyweight default tools like `browser`, `cron`, and `message`, reducing prompt size for weaker local-model setups without changing the normal path. Thanks @ImLukeF.
|
||||
|
||||
### Fixes
|
||||
|
||||
|
||||
@@ -164,8 +164,10 @@ Compatibility notes for stricter OpenAI-compatible backends:
|
||||
- Some smaller or stricter local backends are unstable with OpenClaw's full
|
||||
agent-runtime prompt shape, especially when tool schemas are included. If the
|
||||
backend works for tiny direct `/v1/chat/completions` calls but fails on normal
|
||||
OpenClaw agent turns, try
|
||||
`models.providers.<provider>.models[].compat.supportsTools: false` first.
|
||||
OpenClaw agent turns, first try
|
||||
`agents.defaults.localModelMode: "lean"` to drop heavyweight default tools
|
||||
like `browser`, `cron`, and `message`; if that still fails, try
|
||||
`models.providers.<provider>.models[].compat.supportsTools: false`.
|
||||
- If the backend still fails only on larger OpenClaw runs, the remaining issue
|
||||
is usually upstream model/server capacity or a backend bug, not OpenClaw's
|
||||
transport layer.
|
||||
|
||||
@@ -115,4 +115,56 @@ describe("applyModelProviderToolPolicy", () => {
|
||||
|
||||
expect(toolNames(filtered)).toEqual(["read", "web_search", "exec"]);
|
||||
});
|
||||
|
||||
it("drops heavyweight tools when lean local-model mode is enabled", () => {
|
||||
const filtered = __testing.applyModelProviderToolPolicy(
|
||||
[
|
||||
{ name: "read" },
|
||||
{ name: "browser" },
|
||||
{ name: "cron" },
|
||||
{ name: "message" },
|
||||
{ name: "exec" },
|
||||
] as unknown as AnyAgentTool[],
|
||||
{
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
localModelMode: "lean",
|
||||
},
|
||||
},
|
||||
},
|
||||
modelProvider: "openai",
|
||||
modelApi: "openai-responses",
|
||||
modelId: "gpt-5.4",
|
||||
},
|
||||
);
|
||||
|
||||
expect(toolNames(filtered)).toEqual(["read", "exec"]);
|
||||
});
|
||||
|
||||
it("keeps heavyweight tools when lean local-model mode is not enabled", () => {
|
||||
const filtered = __testing.applyModelProviderToolPolicy(
|
||||
[
|
||||
{ name: "read" },
|
||||
{ name: "browser" },
|
||||
{ name: "cron" },
|
||||
{ name: "message" },
|
||||
{ name: "exec" },
|
||||
] as unknown as AnyAgentTool[],
|
||||
{
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
localModelMode: "default",
|
||||
},
|
||||
},
|
||||
},
|
||||
modelProvider: "openai",
|
||||
modelApi: "openai-responses",
|
||||
modelId: "gpt-5.4",
|
||||
},
|
||||
);
|
||||
|
||||
expect(toolNames(filtered)).toEqual(["read", "browser", "cron", "message", "exec"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,6 +131,11 @@ function applyModelProviderToolPolicy(
|
||||
modelCompat?: ModelCompatConfig;
|
||||
},
|
||||
): AnyAgentTool[] {
|
||||
if (params?.config?.agents?.defaults?.localModelMode === "lean") {
|
||||
const leanDeny = new Set(["browser", "cron", "message"]);
|
||||
tools = tools.filter((tool) => !leanDeny.has(tool.name));
|
||||
}
|
||||
|
||||
if (
|
||||
shouldSuppressManagedWebSearchTool({
|
||||
config: params?.config,
|
||||
|
||||
@@ -3243,6 +3243,21 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
|
||||
description:
|
||||
"Max total characters across all injected workspace bootstrap files (default: 150000).",
|
||||
},
|
||||
localModelMode: {
|
||||
anyOf: [
|
||||
{
|
||||
type: "string",
|
||||
const: "default",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
const: "lean",
|
||||
},
|
||||
],
|
||||
title: "Local Model Mode",
|
||||
description:
|
||||
'Local-model prompt profile: "default" keeps the standard tool surface, while "lean" drops heavyweight non-essential tools for smaller or weaker models.',
|
||||
},
|
||||
bootstrapPromptTruncationWarning: {
|
||||
anyOf: [
|
||||
{
|
||||
@@ -24513,6 +24528,11 @@ export const GENERATED_BASE_CONFIG_SCHEMA: BaseConfigSchemaResponse = {
|
||||
help: "Max total characters across all injected workspace bootstrap files (default: 150000).",
|
||||
tags: ["performance"],
|
||||
},
|
||||
"agents.defaults.localModelMode": {
|
||||
label: "Local Model Mode",
|
||||
help: 'Local-model prompt profile: "default" keeps the standard tool surface, while "lean" drops heavyweight non-essential tools for smaller or weaker models.',
|
||||
tags: ["advanced"],
|
||||
},
|
||||
"agents.defaults.bootstrapPromptTruncationWarning": {
|
||||
label: "Bootstrap Prompt Truncation Warning",
|
||||
help: 'Inject agent-visible warning text when bootstrap files are truncated: "off", "once" (default), or "always".',
|
||||
|
||||
@@ -848,6 +848,8 @@ export const FIELD_HELP: Record<string, string> = {
|
||||
"Max characters of each workspace bootstrap file injected into the system prompt before truncation (default: 20000).",
|
||||
"agents.defaults.bootstrapTotalMaxChars":
|
||||
"Max total characters across all injected workspace bootstrap files (default: 150000).",
|
||||
"agents.defaults.localModelMode":
|
||||
'Local-model prompt profile: "default" keeps the standard tool surface, while "lean" drops heavyweight non-essential tools for smaller or weaker models.',
|
||||
"agents.defaults.bootstrapPromptTruncationWarning":
|
||||
'Inject agent-visible warning text when bootstrap files are truncated: "off", "once" (default), or "always".',
|
||||
"agents.defaults.startupContext":
|
||||
|
||||
@@ -343,6 +343,7 @@ export const FIELD_LABELS: Record<string, string> = {
|
||||
"agents.defaults.contextInjection": "Context Injection",
|
||||
"agents.defaults.bootstrapMaxChars": "Bootstrap Max Chars",
|
||||
"agents.defaults.bootstrapTotalMaxChars": "Bootstrap Total Max Chars",
|
||||
"agents.defaults.localModelMode": "Local Model Mode",
|
||||
"agents.defaults.bootstrapPromptTruncationWarning": "Bootstrap Prompt Truncation Warning",
|
||||
"agents.defaults.startupContext": "Startup Context",
|
||||
"agents.defaults.startupContext.enabled": "Enable Startup Context",
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { MemorySearchConfig } from "./types.tools.js";
|
||||
|
||||
export type AgentContextInjection = "always" | "continuation-skip";
|
||||
export type EmbeddedPiExecutionContract = "default" | "strict-agentic";
|
||||
export type LocalModelMode = "default" | "lean";
|
||||
|
||||
export type AgentModelEntryConfig = {
|
||||
alias?: string;
|
||||
@@ -198,6 +199,12 @@ export type AgentDefaultsConfig = {
|
||||
bootstrapMaxChars?: number;
|
||||
/** Max total chars across all injected bootstrap files (default: 150000). */
|
||||
bootstrapTotalMaxChars?: number;
|
||||
/**
|
||||
* Optional local-model prompt profile:
|
||||
* - default: keep the standard tool surface
|
||||
* - lean: drop heavyweight non-essential tools for smaller or weaker models
|
||||
*/
|
||||
localModelMode?: LocalModelMode;
|
||||
/**
|
||||
* Agent-visible bootstrap truncation warning mode:
|
||||
* - off: do not inject warning text
|
||||
|
||||
@@ -52,6 +52,7 @@ export const AgentDefaultsSchema = z
|
||||
contextInjection: z.union([z.literal("always"), z.literal("continuation-skip")]).optional(),
|
||||
bootstrapMaxChars: z.number().int().positive().optional(),
|
||||
bootstrapTotalMaxChars: z.number().int().positive().optional(),
|
||||
localModelMode: z.union([z.literal("default"), z.literal("lean")]).optional(),
|
||||
bootstrapPromptTruncationWarning: z
|
||||
.union([z.literal("off"), z.literal("once"), z.literal("always")])
|
||||
.optional(),
|
||||
|
||||
Reference in New Issue
Block a user