mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-18 23:27:26 +00:00
feat(llm-task): add thinking override
Co-authored-by: Xaden Ryan <165437834+xadenryan@users.noreply.github.com>
This commit is contained in:
@@ -69,6 +69,7 @@ outside the list is rejected.
|
||||
- `schema` (object, optional JSON Schema)
|
||||
- `provider` (string, optional)
|
||||
- `model` (string, optional)
|
||||
- `thinking` (string, optional)
|
||||
- `authProfileId` (string, optional)
|
||||
- `temperature` (number, optional)
|
||||
- `maxTokens` (number, optional)
|
||||
|
||||
@@ -109,6 +109,59 @@ describe("llm-task tool (json-only)", () => {
|
||||
expect(call.model).toBe("claude-4-sonnet");
|
||||
});
|
||||
|
||||
it("passes thinking override to embedded runner", async () => {
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
(runEmbeddedPiAgent as any).mockResolvedValueOnce({
|
||||
meta: {},
|
||||
payloads: [{ text: JSON.stringify({ ok: true }) }],
|
||||
});
|
||||
const tool = createLlmTaskTool(fakeApi());
|
||||
await tool.execute("id", { prompt: "x", thinking: "high" });
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
const call = (runEmbeddedPiAgent as any).mock.calls[0]?.[0];
|
||||
expect(call.thinkLevel).toBe("high");
|
||||
});
|
||||
|
||||
it("normalizes thinking aliases", async () => {
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
(runEmbeddedPiAgent as any).mockResolvedValueOnce({
|
||||
meta: {},
|
||||
payloads: [{ text: JSON.stringify({ ok: true }) }],
|
||||
});
|
||||
const tool = createLlmTaskTool(fakeApi());
|
||||
await tool.execute("id", { prompt: "x", thinking: "on" });
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
const call = (runEmbeddedPiAgent as any).mock.calls[0]?.[0];
|
||||
expect(call.thinkLevel).toBe("low");
|
||||
});
|
||||
|
||||
it("throws on invalid thinking level", async () => {
|
||||
const tool = createLlmTaskTool(fakeApi());
|
||||
await expect(tool.execute("id", { prompt: "x", thinking: "banana" })).rejects.toThrow(
|
||||
/invalid thinking level/i,
|
||||
);
|
||||
});
|
||||
|
||||
it("throws on unsupported xhigh thinking level", async () => {
|
||||
const tool = createLlmTaskTool(fakeApi());
|
||||
await expect(tool.execute("id", { prompt: "x", thinking: "xhigh" })).rejects.toThrow(
|
||||
/only supported/i,
|
||||
);
|
||||
});
|
||||
|
||||
it("does not pass thinkLevel when thinking is omitted", async () => {
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
(runEmbeddedPiAgent as any).mockResolvedValueOnce({
|
||||
meta: {},
|
||||
payloads: [{ text: JSON.stringify({ ok: true }) }],
|
||||
});
|
||||
const tool = createLlmTaskTool(fakeApi());
|
||||
await tool.execute("id", { prompt: "x" });
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
const call = (runEmbeddedPiAgent as any).mock.calls[0]?.[0];
|
||||
expect(call.thinkLevel).toBeUndefined();
|
||||
});
|
||||
|
||||
it("enforces allowedModels", async () => {
|
||||
// oxlint-disable-next-line typescript/no-explicit-any
|
||||
(runEmbeddedPiAgent as any).mockResolvedValueOnce({
|
||||
|
||||
@@ -2,7 +2,13 @@ import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import Ajv from "ajv";
|
||||
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/llm-task";
|
||||
import {
|
||||
formatThinkingLevels,
|
||||
formatXHighModelHint,
|
||||
normalizeThinkLevel,
|
||||
resolvePreferredOpenClawTmpDir,
|
||||
supportsXHighThinking,
|
||||
} from "openclaw/plugin-sdk/llm-task";
|
||||
// NOTE: This extension is intended to be bundled with OpenClaw.
|
||||
// When running from source (tests/dev), OpenClaw internals live under src/.
|
||||
// When running from a built install, internals live under dist/ (no src/ tree).
|
||||
@@ -86,6 +92,7 @@ export function createLlmTaskTool(api: OpenClawPluginApi) {
|
||||
Type.String({ description: "Provider override (e.g. openai-codex, anthropic)." }),
|
||||
),
|
||||
model: Type.Optional(Type.String({ description: "Model id override." })),
|
||||
thinking: Type.Optional(Type.String({ description: "Thinking level override." })),
|
||||
authProfileId: Type.Optional(Type.String({ description: "Auth profile override." })),
|
||||
temperature: Type.Optional(Type.Number({ description: "Best-effort temperature override." })),
|
||||
maxTokens: Type.Optional(Type.Number({ description: "Best-effort maxTokens override." })),
|
||||
@@ -144,6 +151,18 @@ export function createLlmTaskTool(api: OpenClawPluginApi) {
|
||||
);
|
||||
}
|
||||
|
||||
const thinkingRaw =
|
||||
typeof params.thinking === "string" && params.thinking.trim() ? params.thinking : undefined;
|
||||
const thinkLevel = thinkingRaw ? normalizeThinkLevel(thinkingRaw) : undefined;
|
||||
if (thinkingRaw && !thinkLevel) {
|
||||
throw new Error(
|
||||
`Invalid thinking level "${thinkingRaw}". Use one of: ${formatThinkingLevels(provider, model)}.`,
|
||||
);
|
||||
}
|
||||
if (thinkLevel === "xhigh" && !supportsXHighThinking(provider, model)) {
|
||||
throw new Error(`Thinking level "xhigh" is only supported for ${formatXHighModelHint()}.`);
|
||||
}
|
||||
|
||||
const timeoutMs =
|
||||
(typeof params.timeoutMs === "number" && params.timeoutMs > 0
|
||||
? params.timeoutMs
|
||||
@@ -204,6 +223,7 @@ export function createLlmTaskTool(api: OpenClawPluginApi) {
|
||||
model,
|
||||
authProfileId,
|
||||
authProfileIdSource: authProfileId ? "user" : "auto",
|
||||
thinkLevel,
|
||||
streamParams,
|
||||
disableTools: true,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user