fix(hooks): honor configured ollama slug timeout (#66455)

This commit is contained in:
Vincent Koc
2026-04-14 10:28:09 +01:00
committed by GitHub
parent 072e8cfe62
commit dfed74b254
3 changed files with 73 additions and 1 deletions

View File

@@ -27,6 +27,7 @@ Docs: https://docs.openclaw.ai
- Browser: keep loopback CDP readiness checks reachable under strict SSRF defaults so OpenClaw can reconnect to locally started managed Chrome. (#66354) Thanks @hxy91819.
- Agents/context engine: compact engine-owned sessions from the first tool-loop delta and preserve ingest fallback when `afterTurn` is absent, so long-running tool loops can stay bounded without dropping engine state. (#63555) Thanks @Bikkies.
- Discord/native commands: return the real status card for native `/status` interactions instead of falling through to the synthetic `✅ Done.` ack when the generic dispatcher produces no visible reply. (#54629) Thanks @tkozzer and @vincentkoc.
- Hooks/Ollama: let LLM-backed session-memory slug generation honor an explicit `agents.defaults.timeoutSeconds` override instead of always aborting after 15 seconds, so slow local Ollama runs stop silently dropping back to generic filenames. (#66237) Thanks @dmak and @vincentkoc.
## 2026.4.14-beta.1

View File

@@ -0,0 +1,60 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../config/types.openclaw.js";
const runEmbeddedPiAgentMock = vi.fn();
vi.mock("../agents/agent-scope.js", () => ({
resolveDefaultAgentId: vi.fn(() => "main"),
resolveAgentWorkspaceDir: vi.fn(() => "/tmp/openclaw-agent"),
resolveAgentDir: vi.fn(() => "/tmp/openclaw-agent/.openclaw-agent"),
resolveAgentEffectiveModelPrimary: vi.fn(() => null),
}));
vi.mock("../agents/pi-embedded.js", () => ({
runEmbeddedPiAgent: (...args: unknown[]) => runEmbeddedPiAgentMock(...args),
}));
import { generateSlugViaLLM } from "./llm-slug-generator.js";
describe("generateSlugViaLLM", () => {
beforeEach(() => {
runEmbeddedPiAgentMock.mockReset();
runEmbeddedPiAgentMock.mockResolvedValue({
payloads: [{ text: "test-slug" }],
});
});
it("keeps the helper default timeout when no agent timeout is configured", async () => {
await generateSlugViaLLM({
sessionContent: "hello",
cfg: {} as OpenClawConfig,
});
expect(runEmbeddedPiAgentMock).toHaveBeenCalledOnce();
expect(runEmbeddedPiAgentMock.mock.calls[0]?.[0]).toEqual(
expect.objectContaining({
timeoutMs: 15_000,
}),
);
});
it("honors configured agent timeoutSeconds for slow local providers", async () => {
await generateSlugViaLLM({
sessionContent: "hello",
cfg: {
agents: {
defaults: {
timeoutSeconds: 500,
},
},
} as OpenClawConfig,
});
expect(runEmbeddedPiAgentMock).toHaveBeenCalledOnce();
expect(runEmbeddedPiAgentMock.mock.calls[0]?.[0]).toEqual(
expect.objectContaining({
timeoutMs: 500_000,
}),
);
});
});

View File

@@ -14,11 +14,21 @@ import {
import { DEFAULT_PROVIDER, DEFAULT_MODEL } from "../agents/defaults.js";
import { parseModelRef } from "../agents/model-selection.js";
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
import { resolveAgentTimeoutMs } from "../agents/timeout.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
const log = createSubsystemLogger("llm-slug-generator");
const DEFAULT_SLUG_GENERATOR_TIMEOUT_MS = 15_000;
function resolveSlugGeneratorTimeoutMs(cfg: OpenClawConfig): number {
const configuredTimeoutSeconds = cfg.agents?.defaults?.timeoutSeconds;
if (typeof configuredTimeoutSeconds !== "number" || !Number.isFinite(configuredTimeoutSeconds)) {
return DEFAULT_SLUG_GENERATOR_TIMEOUT_MS;
}
return resolveAgentTimeoutMs({ cfg });
}
/**
* Generate a short 1-2 word filename slug from session content using LLM
@@ -50,6 +60,7 @@ Reply with ONLY the slug, nothing else. Examples: "vendor-pitch", "api-design",
const parsed = modelRef ? parseModelRef(modelRef, DEFAULT_PROVIDER) : null;
const provider = parsed?.provider ?? DEFAULT_PROVIDER;
const model = parsed?.model ?? DEFAULT_MODEL;
const timeoutMs = resolveSlugGeneratorTimeoutMs(params.cfg);
const result = await runEmbeddedPiAgent({
sessionId: `slug-generator-${Date.now()}`,
@@ -62,7 +73,7 @@ Reply with ONLY the slug, nothing else. Examples: "vendor-pitch", "api-design",
prompt,
provider,
model,
timeoutMs: 15_000, // 15 second timeout
timeoutMs,
runId: `slug-gen-${Date.now()}`,
});