fix(ollama): honor authoritative model thinking capabilities (#129611)

This commit is contained in:
Peter Steinberger
2026-08-25 15:40:44 -07:00
committed by GitHub
parent 8e9c932b3f
commit 568a773beb
2 changed files with 54 additions and 5 deletions
@@ -1,3 +1,4 @@
import type { WizardPrompter } from "openclaw/plugin-sdk/setup";
import { requestUrl } from "openclaw/plugin-sdk/test-env";
import { afterEach, describe, expect, it, vi } from "vitest";
import {
@@ -8,6 +9,7 @@ import {
normalizeOllamaModelName,
selectAppGuidedOllamaModelFromDiscovery,
} from "./setup-model-selection.js";
import { promptAndConfigureOllama } from "./setup.js";
afterEach(() => {
vi.unstubAllGlobals();
@@ -81,13 +83,24 @@ describe("Ollama onboarding model selection", () => {
).toBe("qwen3:0.6b");
});
it("prefers the smallest non-reasoning setup model", () => {
it.each([
{
description: "skips a smaller model with an explicit thinking capability",
capabilities: ["tools", "thinking"],
expected: "llama3.2:latest",
},
{
description: "trusts explicit non-thinking capabilities over a reasoning-like model name",
capabilities: ["tools"],
expected: "deepseek-r1:8b",
},
])("$description", ({ capabilities, expected }) => {
expect(
selectAppGuidedOllamaModelFromDiscovery([
{
name: "deepseek-r1:8b",
contextWindow: 131_072,
capabilities: ["tools", "thinking"],
capabilities,
size: 1_000,
},
{
@@ -103,7 +116,44 @@ describe("Ollama onboarding model selection", () => {
size: 2_000,
},
]),
).toBe("llama3.2:latest");
).toBe(expected);
});
it("selects the smallest explicitly non-thinking model during interactive setup", async () => {
vi.stubGlobal(
"fetch",
vi.fn(async (input: string | URL | Request) => {
const url = requestUrl(input);
if (url.endsWith("/api/tags")) {
return Response.json({
models: [
{ name: "deepseek-r1:1b", size: 100 },
{ name: "llama3:70b", size: 70_000 },
],
});
}
if (url.endsWith("/api/show")) {
return Response.json({
model_info: { "test.context_length": 32_768 },
capabilities: ["completion", "tools"],
});
}
throw new Error(`Unexpected fetch: ${url}`);
}),
);
const prompter = {
select: vi.fn().mockResolvedValueOnce("local-only"),
text: vi.fn().mockResolvedValueOnce("http://127.0.0.1:11434"),
note: vi.fn(async () => undefined),
} as unknown as WizardPrompter;
const result = await promptAndConfigureOllama({ cfg: {}, prompter });
const selected = result.config.models?.providers?.ollama?.models?.find(
(model) => model.id === "deepseek-r1:1b",
);
expect(selected?.reasoning).toBe(false);
expect(result.defaultModel).toBe("ollama/deepseek-r1:1b");
});
it("aborts pending model discovery with the setup signal", async () => {
@@ -112,8 +112,7 @@ export function selectAppGuidedOllamaModelFromDiscovery(
id: model.name,
contextWindow: model.contextWindow,
supportsTools: model.capabilities?.includes("tools") === true,
reasoning:
model.capabilities?.includes("thinking") === true || isReasoningModelHeuristic(model.name),
reasoning: model.capabilities?.includes("thinking") ?? isReasoningModelHeuristic(model.name),
size: model.size,
})),
);