mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 03:50:20 +00:00
perf: fast-path built-in reasoning provider checks
This commit is contained in:
committed by
Peter Steinberger
parent
9c4ea016d9
commit
590655472b
77
src/utils/provider-utils.test.ts
Normal file
77
src/utils/provider-utils.test.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const { resolveProviderReasoningOutputModeWithPluginMock } = vi.hoisted(() => ({
|
||||
resolveProviderReasoningOutputModeWithPluginMock: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../plugins/provider-runtime.js", () => ({
|
||||
resolveProviderReasoningOutputModeWithPlugin: resolveProviderReasoningOutputModeWithPluginMock,
|
||||
}));
|
||||
|
||||
import { isReasoningTagProvider, resolveReasoningOutputMode } from "./provider-utils.js";
|
||||
|
||||
describe("resolveReasoningOutputMode", () => {
|
||||
beforeEach(() => {
|
||||
resolveProviderReasoningOutputModeWithPluginMock.mockReset();
|
||||
resolveProviderReasoningOutputModeWithPluginMock.mockReturnValue(undefined);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["google", "tagged"],
|
||||
["Google", "tagged"],
|
||||
["google-gemini-cli", "tagged"],
|
||||
["google-generative-ai", "tagged"],
|
||||
["anthropic", "native"],
|
||||
["openai", "native"],
|
||||
["openrouter", "native"],
|
||||
["ollama", "native"],
|
||||
["minimax", "native"],
|
||||
["minimax-cn", "native"],
|
||||
] as const)("uses the built-in fast path for %s", (provider, expected) => {
|
||||
expect(resolveReasoningOutputMode({ provider, workspaceDir: process.cwd() })).toBe(expected);
|
||||
expect(resolveProviderReasoningOutputModeWithPluginMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("falls back to provider hooks for unknown providers", () => {
|
||||
resolveProviderReasoningOutputModeWithPluginMock.mockReturnValue("tagged");
|
||||
|
||||
expect(
|
||||
resolveReasoningOutputMode({
|
||||
provider: "custom-provider",
|
||||
workspaceDir: process.cwd(),
|
||||
modelId: "custom/model",
|
||||
}),
|
||||
).toBe("tagged");
|
||||
expect(resolveProviderReasoningOutputModeWithPluginMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("returns native when hooks do not provide an override", () => {
|
||||
expect(resolveReasoningOutputMode({ provider: "custom-provider" })).toBe("native");
|
||||
expect(resolveProviderReasoningOutputModeWithPluginMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isReasoningTagProvider", () => {
|
||||
beforeEach(() => {
|
||||
resolveProviderReasoningOutputModeWithPluginMock.mockReset();
|
||||
resolveProviderReasoningOutputModeWithPluginMock.mockReturnValue(undefined);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["google", true],
|
||||
["Google", true],
|
||||
["google-gemini-cli", true],
|
||||
["google-generative-ai", true],
|
||||
["anthropic", false],
|
||||
["openai", false],
|
||||
["openrouter", false],
|
||||
["ollama", false],
|
||||
["minimax", false],
|
||||
["minimax-cn", false],
|
||||
[null, false],
|
||||
[undefined, false],
|
||||
["", false],
|
||||
] as const)("returns %s for %s", (value, expected) => {
|
||||
expect(isReasoningTagProvider(value, { workspaceDir: process.cwd() })).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,18 @@ import type { OpenClawConfig } from "../config/config.js";
|
||||
import { resolveProviderReasoningOutputModeWithPlugin } from "../plugins/provider-runtime.js";
|
||||
import type { ProviderRuntimeModel } from "../plugins/types.js";
|
||||
|
||||
const BUILTIN_REASONING_OUTPUT_MODES = {
|
||||
anthropic: "native",
|
||||
google: "tagged",
|
||||
"google-gemini-cli": "tagged",
|
||||
"google-generative-ai": "tagged",
|
||||
minimax: "native",
|
||||
"minimax-cn": "native",
|
||||
ollama: "native",
|
||||
openai: "native",
|
||||
openrouter: "native",
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Utility functions for provider-specific logic and capabilities.
|
||||
*/
|
||||
@@ -20,6 +32,13 @@ export function resolveReasoningOutputMode(params: {
|
||||
return "native";
|
||||
}
|
||||
|
||||
const normalized = provider.toLowerCase();
|
||||
const builtInMode =
|
||||
BUILTIN_REASONING_OUTPUT_MODES[normalized as keyof typeof BUILTIN_REASONING_OUTPUT_MODES];
|
||||
if (builtInMode) {
|
||||
return builtInMode;
|
||||
}
|
||||
|
||||
const pluginMode = resolveProviderReasoningOutputModeWithPlugin({
|
||||
provider,
|
||||
config: params.config,
|
||||
@@ -39,6 +58,14 @@ export function resolveReasoningOutputMode(params: {
|
||||
return pluginMode;
|
||||
}
|
||||
|
||||
// Check for exact matches or known prefixes/substrings for reasoning providers.
|
||||
// Note: Ollama is intentionally excluded - its OpenAI-compatible endpoint
|
||||
// handles reasoning natively via the `reasoning` field in streaming chunks,
|
||||
// so tag-based enforcement is unnecessary and causes all output to be
|
||||
// discarded as "(no output)" (#2279).
|
||||
// Note: MiniMax is also intentionally excluded. In production it does not
|
||||
// reliably wrap user-visible output in <final> tags, so forcing tag
|
||||
// enforcement suppresses normal assistant replies.
|
||||
return "native";
|
||||
}
|
||||
|
||||
|
||||
@@ -69,59 +69,6 @@ describe("parseBooleanValue", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("isReasoningTagProvider", () => {
|
||||
it.each([
|
||||
{
|
||||
name: "returns false for ollama when the provider plugin has no tagged override",
|
||||
value: "ollama",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "returns false for case-insensitive ollama",
|
||||
value: "Ollama",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "returns true for google via provider hook",
|
||||
value: "google",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "returns true for Google (case-insensitive)",
|
||||
value: "Google",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "returns true for google-gemini-cli via provider hook",
|
||||
value: "google-gemini-cli",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "returns true for google-generative-ai via provider hook",
|
||||
value: "google-generative-ai",
|
||||
expected: true,
|
||||
},
|
||||
{ name: "returns true for minimax via provider hook", value: "minimax", expected: true },
|
||||
{
|
||||
name: "returns true for minimax-cn via provider hook alias",
|
||||
value: "minimax-cn",
|
||||
expected: true,
|
||||
},
|
||||
{ name: "returns false for null", value: null, expected: false },
|
||||
{ name: "returns false for undefined", value: undefined, expected: false },
|
||||
{ name: "returns false for empty", value: "", expected: false },
|
||||
{ name: "returns false for anthropic", value: "anthropic", expected: false },
|
||||
{ name: "returns false for openai", value: "openai", expected: false },
|
||||
{ name: "returns false for openrouter", value: "openrouter", expected: false },
|
||||
] satisfies Array<{
|
||||
name: string;
|
||||
value: string | null | undefined;
|
||||
expected: boolean;
|
||||
}>)("$name", ({ value, expected }) => {
|
||||
expect(isReasoningTagProvider(value)).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("splitShellArgs", () => {
|
||||
it("splits whitespace and respects quotes", () => {
|
||||
expect(splitShellArgs(`qmd --foo "bar baz"`)).toEqual(["qmd", "--foo", "bar baz"]);
|
||||
|
||||
Reference in New Issue
Block a user