feat(core): ToolParameter 支持 default、minimum、maximum、enum 字段

- 扩展 ToolParameter 类型,新增 default/minimum/maximum/enum 可选字段
- 更新 buildZodSchema 函数处理 enum 和 min/max 约束
- MCP 工具适配器解析 JSON Schema 中的扩展字段
- task_output 工具使用新字段替代 description 中的约束说明
- 新增单元测试覆盖所有扩展字段
This commit is contained in:
2025-12-17 14:15:09 +08:00
parent fe6ef9be9b
commit 78551d68f3
4 changed files with 301 additions and 4 deletions
@@ -246,6 +246,121 @@ describe('buildZodSchema', () => {
});
});
describe('扩展字段', () => {
it('enum 约束字符串类型', () => {
const parameters: Record<string, ToolParameter> = {
color: {
type: 'string',
description: '颜色选择',
required: true,
enum: ['red', 'green', 'blue'],
},
};
const schema = buildZodSchema(parameters);
expect(schema.safeParse({ color: 'red' }).success).toBe(true);
expect(schema.safeParse({ color: 'green' }).success).toBe(true);
expect(schema.safeParse({ color: 'yellow' }).success).toBe(false);
});
it('minimum 约束数字类型', () => {
const parameters: Record<string, ToolParameter> = {
count: {
type: 'number',
description: '数量',
required: true,
minimum: 1,
},
};
const schema = buildZodSchema(parameters);
expect(schema.safeParse({ count: 1 }).success).toBe(true);
expect(schema.safeParse({ count: 100 }).success).toBe(true);
expect(schema.safeParse({ count: 0 }).success).toBe(false);
expect(schema.safeParse({ count: -1 }).success).toBe(false);
});
it('maximum 约束数字类型', () => {
const parameters: Record<string, ToolParameter> = {
limit: {
type: 'number',
description: '限制',
required: true,
maximum: 100,
},
};
const schema = buildZodSchema(parameters);
expect(schema.safeParse({ limit: 100 }).success).toBe(true);
expect(schema.safeParse({ limit: 50 }).success).toBe(true);
expect(schema.safeParse({ limit: 101 }).success).toBe(false);
});
it('minimum 和 maximum 组合约束', () => {
const parameters: Record<string, ToolParameter> = {
timeout: {
type: 'number',
description: '超时时间(毫秒)',
required: false,
minimum: 0,
maximum: 600000,
},
};
const schema = buildZodSchema(parameters);
expect(schema.safeParse({}).success).toBe(true);
expect(schema.safeParse({ timeout: 0 }).success).toBe(true);
expect(schema.safeParse({ timeout: 30000 }).success).toBe(true);
expect(schema.safeParse({ timeout: 600000 }).success).toBe(true);
expect(schema.safeParse({ timeout: -1 }).success).toBe(false);
expect(schema.safeParse({ timeout: 600001 }).success).toBe(false);
});
it('default 字段不影响 Zod 验证', () => {
// default 只用于 JSON SchemaZod 不会自动应用默认值
const parameters: Record<string, ToolParameter> = {
block: {
type: 'boolean',
description: '是否阻塞',
required: false,
default: true,
},
};
const schema = buildZodSchema(parameters);
// 不提供值时仍然是 undefined(不是 true
const result = schema.safeParse({});
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.block).toBeUndefined();
}
// 显式提供值时使用提供的值
expect(schema.safeParse({ block: false }).success).toBe(true);
});
it('空 enum 数组使用普通 string', () => {
const parameters: Record<string, ToolParameter> = {
text: {
type: 'string',
description: '文本',
required: true,
enum: [],
},
};
const schema = buildZodSchema(parameters);
// 空 enum 应该退化为普通 string
expect(schema.safeParse({ text: 'anything' }).success).toBe(true);
});
});
describe('实际工具参数示例', () => {
it('bash 工具参数', () => {
const bashParameters: Record<string, ToolParameter> = {