mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-10 00:31:22 +00:00
147 lines
4.7 KiB
TypeScript
147 lines
4.7 KiB
TypeScript
import { Type } from "@sinclair/typebox";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { normalizeToolParameterSchema, normalizeToolParameters } from "./pi-tools.schema.js";
|
|
import type { AnyAgentTool } from "./pi-tools.types.js";
|
|
|
|
describe("normalizeToolParameterSchema", () => {
|
|
it("normalizes truly empty schemas to type:object with properties:{}", () => {
|
|
expect(normalizeToolParameterSchema({})).toEqual({
|
|
type: "object",
|
|
properties: {},
|
|
});
|
|
});
|
|
|
|
it("leaves top-level allOf schemas unchanged", () => {
|
|
const schema = {
|
|
allOf: [{ type: "object", properties: { id: { type: "string" } } }],
|
|
};
|
|
|
|
expect(normalizeToolParameterSchema(schema)).toEqual(schema);
|
|
});
|
|
|
|
it("adds missing top-level type for raw object-ish schemas", () => {
|
|
expect(
|
|
normalizeToolParameterSchema({
|
|
properties: { q: { type: "string" } },
|
|
required: ["q"],
|
|
}),
|
|
).toEqual({
|
|
type: "object",
|
|
properties: { q: { type: "string" } },
|
|
required: ["q"],
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("normalizeToolParameters", () => {
|
|
it("normalizes truly empty schemas to type:object with properties:{} (MCP parameter-free tools)", () => {
|
|
const tool: AnyAgentTool = {
|
|
name: "get_flux_instance",
|
|
label: "get_flux_instance",
|
|
description: "Get current Flux instance status",
|
|
parameters: {},
|
|
execute: vi.fn(),
|
|
};
|
|
|
|
const normalized = normalizeToolParameters(tool);
|
|
|
|
const parameters = normalized.parameters as Record<string, unknown>;
|
|
expect(parameters.type).toBe("object");
|
|
expect(parameters.properties).toEqual({});
|
|
});
|
|
|
|
it("does not rewrite non-empty schemas that still lack type/properties", () => {
|
|
const tool: AnyAgentTool = {
|
|
name: "conditional",
|
|
label: "conditional",
|
|
description: "Conditional schema stays untouched",
|
|
parameters: { allOf: [] },
|
|
execute: vi.fn(),
|
|
};
|
|
|
|
const normalized = normalizeToolParameters(tool);
|
|
|
|
expect(normalized.parameters).toEqual({ allOf: [] });
|
|
});
|
|
|
|
it("injects properties:{} for type:object schemas missing properties (MCP no-param tools)", () => {
|
|
const tool: AnyAgentTool = {
|
|
name: "list_regions",
|
|
label: "list_regions",
|
|
description: "List all AWS regions",
|
|
parameters: { type: "object" },
|
|
execute: vi.fn(),
|
|
};
|
|
|
|
const normalized = normalizeToolParameters(tool);
|
|
|
|
const parameters = normalized.parameters as Record<string, unknown>;
|
|
expect(parameters.type).toBe("object");
|
|
expect(parameters.properties).toEqual({});
|
|
});
|
|
|
|
it("preserves existing properties on type:object schemas", () => {
|
|
const tool: AnyAgentTool = {
|
|
name: "query",
|
|
label: "query",
|
|
description: "Run a query",
|
|
parameters: { type: "object", properties: { q: { type: "string" } } },
|
|
execute: vi.fn(),
|
|
};
|
|
|
|
const normalized = normalizeToolParameters(tool);
|
|
|
|
const parameters = normalized.parameters as Record<string, unknown>;
|
|
expect(parameters.type).toBe("object");
|
|
expect(parameters.properties).toEqual({ q: { type: "string" } });
|
|
});
|
|
|
|
it("injects properties:{} for type:object with only additionalProperties", () => {
|
|
const tool: AnyAgentTool = {
|
|
name: "passthrough",
|
|
label: "passthrough",
|
|
description: "Accept any input",
|
|
parameters: { type: "object", additionalProperties: true },
|
|
execute: vi.fn(),
|
|
};
|
|
|
|
const normalized = normalizeToolParameters(tool);
|
|
|
|
const parameters = normalized.parameters as Record<string, unknown>;
|
|
expect(parameters.type).toBe("object");
|
|
expect(parameters.properties).toEqual({});
|
|
expect(parameters.additionalProperties).toBe(true);
|
|
});
|
|
|
|
it("strips compat-declared unsupported schema keywords without provider-specific branching", () => {
|
|
const tool: AnyAgentTool = {
|
|
name: "demo",
|
|
label: "demo",
|
|
description: "demo",
|
|
parameters: Type.Object({
|
|
count: Type.Integer({ minimum: 1, maximum: 5 }),
|
|
query: Type.Optional(Type.String({ minLength: 2 })),
|
|
}),
|
|
execute: vi.fn(),
|
|
};
|
|
|
|
const normalized = normalizeToolParameters(tool, {
|
|
modelCompat: {
|
|
unsupportedToolSchemaKeywords: ["minimum", "maximum", "minLength"],
|
|
},
|
|
});
|
|
|
|
const parameters = normalized.parameters as {
|
|
required?: string[];
|
|
properties?: Record<string, Record<string, unknown>>;
|
|
};
|
|
|
|
expect(parameters.required).toEqual(["count"]);
|
|
expect(parameters.properties?.count.minimum).toBeUndefined();
|
|
expect(parameters.properties?.count.maximum).toBeUndefined();
|
|
expect(parameters.properties?.count.type).toBe("integer");
|
|
expect(parameters.properties?.query.minLength).toBeUndefined();
|
|
expect(parameters.properties?.query.type).toBe("string");
|
|
});
|
|
});
|