Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions scripts/generate-openclaw-config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ const OPENCLAW_MIN_PROMPT_BUDGET_TOKENS = 8_000;
const SMALL_OLLAMA_CONTEXT_THRESHOLD =
OPENCLAW_DEFAULT_RESERVE_TOKENS_FLOOR + OPENCLAW_MIN_PROMPT_BUDGET_TOKENS;
const LOCAL_OLLAMA_UPSTREAM_PROVIDER = "ollama-local";
const MANAGED_INFERENCE_PROVIDER_KEY = "inference";
const MANAGED_INFERENCE_HOSTNAME = "inference.local";
const MANAGED_INFERENCE_SAFEGUARD_COMPACTION: JsonObject = {
mode: "safeguard",
timeoutSeconds: 120,
maxHistoryShare: 0.35,
recentTurnsPreserve: 1,
qualityGuard: { enabled: true, maxRetries: 0 },
notifyUser: true,
truncateAfterCompaction: true,
};
const FALSE_VALUES = new Set(["0", "false", "no", "off"]);
const WEB_SEARCH_PROVIDERS = {
brave: { credentialEnv: "BRAVE_API_KEY" },
Expand Down Expand Up @@ -1018,6 +1029,38 @@ export function buildLocalOllamaSmallContextCompaction(
return { reserveTokens, reserveTokensFloor: reserveTokens };
}

function isManagedInferenceLocalRoute(
providerKey: string | undefined,
inferenceBaseUrl: string,
): boolean {
if ((providerKey || "").trim() !== MANAGED_INFERENCE_PROVIDER_KEY) {
return false;
}
return parseUrl(normalizeUrlForParse(inferenceBaseUrl)).hostname === MANAGED_INFERENCE_HOSTNAME;
}

// Managed remote inference sessions use OpenClaw's safeguard compaction rather
// than its plain runtime compactor. A two-minute timeout bounds each attempt,
// lifecycle notices expose automatic and agent-run compaction progress, and
// successful compaction rotates the active transcript. These safeguards do not
// guarantee that summarization succeeds or that the resulting context is smaller.
export function buildManagedInferenceSafeguardCompaction(
providerKey: string | undefined,
upstreamProvider: string | undefined,
inferenceBaseUrl: string,
): JsonObject | undefined {
if (!isManagedInferenceLocalRoute(providerKey, inferenceBaseUrl)) {
return undefined;
}
if ((upstreamProvider || "").trim() === LOCAL_OLLAMA_UPSTREAM_PROVIDER) {
return undefined;
}
return {
...MANAGED_INFERENCE_SAFEGUARD_COMPACTION,
qualityGuard: { ...MANAGED_INFERENCE_SAFEGUARD_COMPACTION.qualityGuard },
};
}

export function buildConfig(env: Env = process.env): JsonObject {
const proxyHost = env.NEMOCLAW_PROXY_HOST || "10.200.0.1";
const proxyPort = env.NEMOCLAW_PROXY_PORT || "3128";
Expand Down Expand Up @@ -1237,6 +1280,14 @@ export function buildConfig(env: Env = process.env): JsonObject {
if (smallOllamaCompaction) {
agentDefaults.compaction = smallOllamaCompaction;
}
const managedInferenceCompaction = buildManagedInferenceSafeguardCompaction(
providerKey,
env.NEMOCLAW_UPSTREAM_PROVIDER,
inferenceBaseUrl,
);
if (managedInferenceCompaction) {
agentDefaults.compaction = managedInferenceCompaction;
}

const config: JsonObject = {
agents: {
Expand Down
35 changes: 32 additions & 3 deletions test/ollama-local-openclaw-config-propagation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { afterEach, describe, expect, it } from "vitest";
import {
buildConfig,
buildLocalOllamaSmallContextCompaction,
buildManagedInferenceSafeguardCompaction,
} from "../scripts/generate-openclaw-config.mts";
import { patchStagedDockerfile } from "../src/lib/onboard/dockerfile-patch";

Expand Down Expand Up @@ -131,7 +132,7 @@ describe("ollama-local OpenClaw config propagation", () => {
});
});

describe("ollama-local small-context compaction policy (#5468)", () => {
describe("OpenClaw managed-route compaction policy (#5468, #4781)", () => {
it("emits a lowered compaction reserve for a small Local Ollama window", () => {
const config = buildConfig({
NEMOCLAW_MODEL: "qwen2.5:0.5b",
Expand All @@ -152,7 +153,7 @@ describe("ollama-local small-context compaction policy (#5468)", () => {
});
});

it("does not touch compaction for a non-ollama upstream provider", () => {
it("uses safeguard compaction for remote managed inference (#4781)", () => {
const config = buildConfig({
NEMOCLAW_MODEL: "nvidia/nemotron-3-super-120b-a12b",
NEMOCLAW_PROVIDER_KEY: "inference",
Expand All @@ -164,7 +165,15 @@ describe("ollama-local small-context compaction policy (#5468)", () => {
NEMOCLAW_MAX_TOKENS: "4096",
NEMOCLAW_AGENT_TIMEOUT: "600",
});
expect(config.agents.defaults.compaction).toBeUndefined();
expect(config.agents.defaults.compaction).toEqual({
mode: "safeguard",
timeoutSeconds: 120,
maxHistoryShare: 0.35,
recentTurnsPreserve: 1,
qualityGuard: { enabled: true, maxRetries: 0 },
notifyUser: true,
truncateAfterCompaction: true,
});
});

it("leaves OpenClaw's default reserve intact for large Local Ollama windows", () => {
Expand All @@ -182,6 +191,26 @@ describe("ollama-local small-context compaction policy (#5468)", () => {
expect(config.agents.defaults.compaction).toBeUndefined();
});

it("does not enable managed-inference safeguards outside inference.local (#4781)", () => {
expect(
buildManagedInferenceSafeguardCompaction(
"inference",
"nvidia-prod",
"https://integrate.api.nvidia.com/v1",
),
).toBeUndefined();
});

it("does not enable managed-inference safeguards for another provider key (#4781)", () => {
expect(
buildManagedInferenceSafeguardCompaction(
"nvidia-prod",
"nvidia-prod",
"https://inference.local/v1",
),
).toBeUndefined();
});

it("clamps the reserve so the prompt budget never drops below OpenClaw's 8k minimum", () => {
// A pathological maxTokens must not make the window worse than the default.
const compaction = buildLocalOllamaSmallContextCompaction("ollama-local", 16384, 99999);
Expand Down