Compare commits
2 Commits
main
...
codex/llm-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fff82599fc | ||
|
|
7b8b268079 |
@ -1,4 +1,81 @@
|
|||||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||||
|
|
||||||
|
vi.mock("@sinclair/typebox", () => ({
|
||||||
|
Type: {
|
||||||
|
Object: (schema: unknown) => schema,
|
||||||
|
String: (schema?: unknown) => schema,
|
||||||
|
Optional: (schema: unknown) => schema,
|
||||||
|
Unknown: (schema?: unknown) => schema,
|
||||||
|
Number: (schema?: unknown) => schema,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("ajv", () => ({
|
||||||
|
default: class MockAjv {
|
||||||
|
compile(schema: unknown) {
|
||||||
|
return (value: unknown) => {
|
||||||
|
if (
|
||||||
|
schema &&
|
||||||
|
typeof schema === "object" &&
|
||||||
|
!Array.isArray(schema) &&
|
||||||
|
(schema as { properties?: Record<string, { type?: string }> }).properties?.foo?.type ===
|
||||||
|
"string"
|
||||||
|
) {
|
||||||
|
const ok = typeof (value as { foo?: unknown })?.foo === "string";
|
||||||
|
(this as { errors?: Array<{ instancePath: string; message: string }> }).errors = ok
|
||||||
|
? undefined
|
||||||
|
: [{ instancePath: "/foo", message: "must be string" }];
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
(this as { errors?: Array<{ instancePath: string; message: string }> }).errors = undefined;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
errors?: Array<{ instancePath: string; message: string }>;
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("../api.js", () => ({
|
||||||
|
formatXHighModelHint: () => "provider models that advertise xhigh reasoning",
|
||||||
|
normalizeThinkLevel: (raw?: string | null) => {
|
||||||
|
if (!raw) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const key = raw.trim().toLowerCase();
|
||||||
|
const collapsed = key.replace(/[\s_-]+/g, "");
|
||||||
|
if (collapsed === "adaptive" || collapsed === "auto") {
|
||||||
|
return "adaptive";
|
||||||
|
}
|
||||||
|
if (collapsed === "xhigh" || collapsed === "extrahigh") {
|
||||||
|
return "xhigh";
|
||||||
|
}
|
||||||
|
if (["off"].includes(key)) {
|
||||||
|
return "off";
|
||||||
|
}
|
||||||
|
if (["on", "enable", "enabled"].includes(key)) {
|
||||||
|
return "low";
|
||||||
|
}
|
||||||
|
if (["min", "minimal", "think"].includes(key)) {
|
||||||
|
return "minimal";
|
||||||
|
}
|
||||||
|
if (["low", "thinkhard", "think-hard", "think_hard"].includes(key)) {
|
||||||
|
return "low";
|
||||||
|
}
|
||||||
|
if (["mid", "med", "medium", "thinkharder", "think-harder", "harder"].includes(key)) {
|
||||||
|
return "medium";
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
["high", "ultra", "ultrathink", "think-hard", "thinkhardest", "highest", "max"].includes(key)
|
||||||
|
) {
|
||||||
|
return "high";
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
},
|
||||||
|
resolvePreferredOpenClawTmpDir: () => "/tmp",
|
||||||
|
supportsXHighThinking: () => false,
|
||||||
|
}));
|
||||||
|
|
||||||
import { createLlmTaskTool } from "./llm-task-tool.js";
|
import { createLlmTaskTool } from "./llm-task-tool.js";
|
||||||
|
|
||||||
const runEmbeddedPiAgent = vi.fn(async () => ({
|
const runEmbeddedPiAgent = vi.fn(async () => ({
|
||||||
@ -137,6 +214,7 @@ describe("llm-task tool (json-only)", () => {
|
|||||||
await expect(tool.execute("id", { prompt: "x", thinking: "banana" })).rejects.toThrow(
|
await expect(tool.execute("id", { prompt: "x", thinking: "banana" })).rejects.toThrow(
|
||||||
/invalid thinking level/i,
|
/invalid thinking level/i,
|
||||||
);
|
);
|
||||||
|
expect(runEmbeddedPiAgent).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("throws on unsupported xhigh thinking level", async () => {
|
it("throws on unsupported xhigh thinking level", async () => {
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import path from "node:path";
|
|||||||
import { Type } from "@sinclair/typebox";
|
import { Type } from "@sinclair/typebox";
|
||||||
import Ajv from "ajv";
|
import Ajv from "ajv";
|
||||||
import {
|
import {
|
||||||
formatThinkingLevels,
|
|
||||||
formatXHighModelHint,
|
formatXHighModelHint,
|
||||||
normalizeThinkLevel,
|
normalizeThinkLevel,
|
||||||
resolvePreferredOpenClawTmpDir,
|
resolvePreferredOpenClawTmpDir,
|
||||||
@ -45,6 +44,9 @@ type PluginCfg = {
|
|||||||
timeoutMs?: number;
|
timeoutMs?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const INVALID_THINKING_LEVELS_HINT =
|
||||||
|
"off, minimal, low, medium, high, adaptive, and xhigh where supported";
|
||||||
|
|
||||||
export function createLlmTaskTool(api: OpenClawPluginApi) {
|
export function createLlmTaskTool(api: OpenClawPluginApi) {
|
||||||
return {
|
return {
|
||||||
name: "llm-task",
|
name: "llm-task",
|
||||||
@ -125,7 +127,7 @@ export function createLlmTaskTool(api: OpenClawPluginApi) {
|
|||||||
const thinkLevel = thinkingRaw ? normalizeThinkLevel(thinkingRaw) : undefined;
|
const thinkLevel = thinkingRaw ? normalizeThinkLevel(thinkingRaw) : undefined;
|
||||||
if (thinkingRaw && !thinkLevel) {
|
if (thinkingRaw && !thinkLevel) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Invalid thinking level "${thinkingRaw}". Use one of: ${formatThinkingLevels(provider, model)}.`,
|
`Invalid thinking level "${thinkingRaw}". Use one of: ${INVALID_THINKING_LEVELS_HINT}.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (thinkLevel === "xhigh" && !supportsXHighThinking(provider, model)) {
|
if (thinkLevel === "xhigh" && !supportsXHighThinking(provider, model)) {
|
||||||
|
|||||||
@ -6,6 +6,23 @@ const logLevel = process.env.OPENCLAW_BUILD_VERBOSE ? "info" : "warn";
|
|||||||
const extraArgs = process.argv.slice(2);
|
const extraArgs = process.argv.slice(2);
|
||||||
const INEFFECTIVE_DYNAMIC_IMPORT_RE = /\[INEFFECTIVE_DYNAMIC_IMPORT\]/;
|
const INEFFECTIVE_DYNAMIC_IMPORT_RE = /\[INEFFECTIVE_DYNAMIC_IMPORT\]/;
|
||||||
const UNRESOLVED_IMPORT_RE = /\[UNRESOLVED_IMPORT\]/;
|
const UNRESOLVED_IMPORT_RE = /\[UNRESOLVED_IMPORT\]/;
|
||||||
|
const ANSI_ESCAPE_RE = new RegExp(String.raw`\u001B\[[0-9;]*m`, "g");
|
||||||
|
|
||||||
|
function findFatalUnresolvedImport(lines) {
|
||||||
|
for (const line of lines) {
|
||||||
|
if (!UNRESOLVED_IMPORT_RE.test(line)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedLine = line.replace(ANSI_ESCAPE_RE, "");
|
||||||
|
if (!normalizedLine.includes("extensions/")) {
|
||||||
|
return normalizedLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const result = spawnSync(
|
const result = spawnSync(
|
||||||
"pnpm",
|
"pnpm",
|
||||||
["exec", "tsdown", "--config-loader", "unrun", "--logLevel", logLevel, ...extraArgs],
|
["exec", "tsdown", "--config-loader", "unrun", "--logLevel", logLevel, ...extraArgs],
|
||||||
@ -32,10 +49,11 @@ if (result.status === 0 && INEFFECTIVE_DYNAMIC_IMPORT_RE.test(`${stdout}\n${stde
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.status === 0 && UNRESOLVED_IMPORT_RE.test(`${stdout}\n${stderr}`)) {
|
const fatalUnresolvedImport =
|
||||||
console.error(
|
result.status === 0 ? findFatalUnresolvedImport(`${stdout}\n${stderr}`.split("\n")) : null;
|
||||||
"Build emitted [UNRESOLVED_IMPORT]. Declare or bundle the missing dependency instead of silently externalizing it.",
|
|
||||||
);
|
if (fatalUnresolvedImport) {
|
||||||
|
console.error(`Build emitted [UNRESOLVED_IMPORT] outside extensions: ${fatalUnresolvedImport}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user