UNPKG

@copilotkit/shared

Version:

<div align="center"> <a href="https://copilotkit.ai" target="_blank"> <img src="https://github.com/copilotkit/copilotkit/raw/main/assets/banner.png" alt="CopilotKit Logo"> </a>

510 lines (507 loc) • 17.4 kB
"use strict"; // src/utils/json-schema.test.ts var import_zod2 = require("zod"); // src/utils/json-schema.ts var import_zod = require("zod"); function actionParametersToJsonSchema(actionParameters) { let parameters = {}; for (let parameter of actionParameters || []) { parameters[parameter.name] = convertAttribute(parameter); } let requiredParameterNames = []; for (let arg of actionParameters || []) { if (arg.required !== false) { requiredParameterNames.push(arg.name); } } return { type: "object", properties: parameters, required: requiredParameterNames }; } function jsonSchemaToActionParameters(jsonSchema) { if (jsonSchema.type !== "object" || !jsonSchema.properties) { return []; } const parameters = []; const requiredFields = jsonSchema.required || []; for (const [name, schema] of Object.entries(jsonSchema.properties)) { const parameter = convertJsonSchemaToParameter(name, schema, requiredFields.includes(name)); parameters.push(parameter); } return parameters; } function convertJsonSchemaToParameter(name, schema, isRequired) { const baseParameter = { name, description: schema.description }; if (!isRequired) { baseParameter.required = false; } switch (schema.type) { case "string": return { ...baseParameter, type: "string", ...schema.enum && { enum: schema.enum } }; case "number": case "boolean": return { ...baseParameter, type: schema.type }; case "object": if (schema.properties) { const attributes = []; const requiredFields = schema.required || []; for (const [propName, propSchema] of Object.entries(schema.properties)) { attributes.push( convertJsonSchemaToParameter(propName, propSchema, requiredFields.includes(propName)) ); } return { ...baseParameter, type: "object", attributes }; } return { ...baseParameter, type: "object" }; case "array": if (schema.items.type === "object" && "properties" in schema.items) { const attributes = []; const requiredFields = schema.items.required || []; for (const [propName, propSchema] of Object.entries(schema.items.properties || {})) { attributes.push( convertJsonSchemaToParameter(propName, propSchema, requiredFields.includes(propName)) ); } return { ...baseParameter, type: "object[]", attributes }; } else if (schema.items.type === "array") { throw new Error("Nested arrays are not supported"); } else { return { ...baseParameter, type: `${schema.items.type}[]` }; } default: return { ...baseParameter, type: "string" }; } } function convertAttribute(attribute) { var _a, _b, _c; switch (attribute.type) { case "string": return { type: "string", description: attribute.description, ...attribute.enum && { enum: attribute.enum } }; case "number": case "boolean": return { type: attribute.type, description: attribute.description }; case "object": case "object[]": const properties = (_a = attribute.attributes) == null ? void 0 : _a.reduce( (acc, attr) => { acc[attr.name] = convertAttribute(attr); return acc; }, {} ); const required = (_b = attribute.attributes) == null ? void 0 : _b.filter((attr) => attr.required !== false).map((attr) => attr.name); if (attribute.type === "object[]") { return { type: "array", items: { type: "object", ...properties && { properties }, ...required && required.length > 0 && { required } }, description: attribute.description }; } return { type: "object", description: attribute.description, ...properties && { properties }, ...required && required.length > 0 && { required } }; default: if ((_c = attribute.type) == null ? void 0 : _c.endsWith("[]")) { const itemType = attribute.type.slice(0, -2); return { type: "array", items: { type: itemType }, description: attribute.description }; } return { type: "string", description: attribute.description }; } } function convertJsonSchemaToZodSchema(jsonSchema, required) { if (jsonSchema.type === "object") { const spec = {}; if (!jsonSchema.properties || !Object.keys(jsonSchema.properties).length) { return !required ? import_zod.z.object(spec).optional() : import_zod.z.object(spec); } for (const [key, value] of Object.entries(jsonSchema.properties)) { spec[key] = convertJsonSchemaToZodSchema( value, jsonSchema.required ? jsonSchema.required.includes(key) : false ); } let schema = import_zod.z.object(spec).describe(jsonSchema.description); return required ? schema : schema.optional(); } else if (jsonSchema.type === "string") { let schema = import_zod.z.string().describe(jsonSchema.description); return required ? schema : schema.optional(); } else if (jsonSchema.type === "number") { let schema = import_zod.z.number().describe(jsonSchema.description); return required ? schema : schema.optional(); } else if (jsonSchema.type === "boolean") { let schema = import_zod.z.boolean().describe(jsonSchema.description); return required ? schema : schema.optional(); } else if (jsonSchema.type === "array") { let itemSchema = convertJsonSchemaToZodSchema(jsonSchema.items, true); let schema = import_zod.z.array(itemSchema).describe(jsonSchema.description); return required ? schema : schema.optional(); } throw new Error("Invalid JSON schema"); } // src/utils/json-schema.test.ts var import_zod_to_json_schema = require("zod-to-json-schema"); describe("convertJsonSchemaToZodSchema", () => { it("should convert a simple JSON schema to a Zod schema", () => { const jsonSchema = { type: "object", properties: { name: { type: "string" }, age: { type: "number" } }, required: ["name", "age"] }; const expectedSchema = import_zod2.z.object({ name: import_zod2.z.string(), age: import_zod2.z.number() }); const result = convertJsonSchemaToZodSchema(jsonSchema, true); const resultSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(result); const expectedSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(expectedSchema); expect(resultSchemaJson).toStrictEqual(expectedSchemaJson); }); it("should convert a JSON schema with nested objects to a Zod schema", () => { const jsonSchema = { type: "object", properties: { name: { type: "string" }, address: { type: "object", properties: { street: { type: "string" }, city: { type: "string" } }, required: ["street", "city"] } }, required: ["name", "address"] }; const expectedSchema = import_zod2.z.object({ name: import_zod2.z.string(), address: import_zod2.z.object({ street: import_zod2.z.string(), city: import_zod2.z.string() }) }); const result = convertJsonSchemaToZodSchema(jsonSchema, true); const resultSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(result); const expectedSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(expectedSchema); expect(resultSchemaJson).toStrictEqual(expectedSchemaJson); }); it("should convert a JSON schema with arrays to a Zod schema", () => { const jsonSchema = { type: "object", properties: { names: { type: "array", items: { type: "string" } } }, required: ["names"] }; const expectedSchema = import_zod2.z.object({ names: import_zod2.z.array(import_zod2.z.string()) }); const result = convertJsonSchemaToZodSchema(jsonSchema, true); const resultSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(result); const expectedSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(expectedSchema); expect(resultSchemaJson).toStrictEqual(expectedSchemaJson); }); it("should convert a JSON schema with optional properties to a Zod schema", () => { const jsonSchema = { type: "object", properties: { name: { type: "string" }, age: { type: "number", required: false } } }; const expectedSchema = import_zod2.z.object({ name: import_zod2.z.string().optional(), age: import_zod2.z.number().optional() }).optional(); const result = convertJsonSchemaToZodSchema(jsonSchema, false); console.log(convertJsonSchemaToZodSchema(jsonSchema, false)); const resultSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(result); const expectedSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(expectedSchema); expect(resultSchemaJson).toStrictEqual(expectedSchemaJson); }); it("should convert a JSON schema with different types to a Zod schema", () => { const jsonSchema = { type: "object", properties: { name: { type: "string" }, age: { type: "number" }, isAdmin: { type: "boolean" } }, required: ["name", "age", "isAdmin"] }; const expectedSchema = import_zod2.z.object({ name: import_zod2.z.string(), age: import_zod2.z.number(), isAdmin: import_zod2.z.boolean() }); const result = convertJsonSchemaToZodSchema(jsonSchema, true); const resultSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(result); const expectedSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(expectedSchema); expect(resultSchemaJson).toStrictEqual(expectedSchemaJson); }); it("should handle edge case where JSON schema has no properties", () => { const jsonSchema = { type: "object" }; const expectedSchema = import_zod2.z.object({}); const result = convertJsonSchemaToZodSchema(jsonSchema, true); const resultSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(result); const expectedSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(expectedSchema); expect(resultSchemaJson).toStrictEqual(expectedSchemaJson); }); it("should handle edge case where JSON schema has no required properties", () => { const jsonSchema = { type: "object", properties: { name: { type: "string" }, age: { type: "number" } } }; const expectedSchema = import_zod2.z.object({ name: import_zod2.z.string().optional(), age: import_zod2.z.number().optional() }).optional(); const result = convertJsonSchemaToZodSchema(jsonSchema, false); const resultSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(result); const expectedSchemaJson = (0, import_zod_to_json_schema.zodToJsonSchema)(expectedSchema); expect(resultSchemaJson).toStrictEqual(expectedSchemaJson); }); }); describe("jsonSchemaToActionParameters", () => { it("should convert a simple JSONSchema to Parameter array", () => { const jsonSchema = { type: "object", properties: { name: { type: "string", description: "User name" }, age: { type: "number", description: "User age" } }, required: ["name"] }; const expectedParameters = [ { name: "name", type: "string", description: "User name" }, { name: "age", type: "number", description: "User age", required: false } ]; const result = jsonSchemaToActionParameters(jsonSchema); expect(result).toEqual(expectedParameters); }); it("should convert JSONSchema with enum to Parameter array", () => { const jsonSchema = { type: "object", properties: { role: { type: "string", enum: ["admin", "user", "guest"], description: "User role" } }, required: ["role"] }; const expectedParameters = [ { name: "role", type: "string", enum: ["admin", "user", "guest"], description: "User role" } ]; const result = jsonSchemaToActionParameters(jsonSchema); expect(result).toEqual(expectedParameters); }); it("should convert nested object JSONSchema to Parameter array", () => { const jsonSchema = { type: "object", properties: { user: { type: "object", properties: { name: { type: "string", description: "User name" }, age: { type: "number", description: "User age" } }, required: ["name"], description: "User information" } }, required: ["user"] }; const expectedParameters = [ { name: "user", type: "object", description: "User information", attributes: [ { name: "name", type: "string", description: "User name" }, { name: "age", type: "number", description: "User age", required: false } ] } ]; const result = jsonSchemaToActionParameters(jsonSchema); expect(result).toEqual(expectedParameters); }); it("should convert array JSONSchema to Parameter array", () => { const jsonSchema = { type: "object", properties: { tags: { type: "array", items: { type: "string" }, description: "User tags" } }, required: ["tags"] }; const expectedParameters = [ { name: "tags", type: "string[]", description: "User tags" } ]; const result = jsonSchemaToActionParameters(jsonSchema); expect(result).toEqual(expectedParameters); }); it("should convert object array JSONSchema to Parameter array", () => { const jsonSchema = { type: "object", properties: { addresses: { type: "array", items: { type: "object", properties: { street: { type: "string", description: "Street name" }, city: { type: "string", description: "City name" } }, required: ["street"] }, description: "User addresses" } }, required: ["addresses"] }; const expectedParameters = [ { name: "addresses", type: "object[]", description: "User addresses", attributes: [ { name: "street", type: "string", description: "Street name" }, { name: "city", type: "string", description: "City name", required: false } ] } ]; const result = jsonSchemaToActionParameters(jsonSchema); expect(result).toEqual(expectedParameters); }); it("should handle boolean types", () => { const jsonSchema = { type: "object", properties: { isAdmin: { type: "boolean", description: "Is user an admin" } }, required: ["isAdmin"] }; const expectedParameters = [ { name: "isAdmin", type: "boolean", description: "Is user an admin" } ]; const result = jsonSchemaToActionParameters(jsonSchema); expect(result).toEqual(expectedParameters); }); it("should handle empty object schema", () => { const jsonSchema = { type: "object" }; const expectedParameters = []; const result = jsonSchemaToActionParameters(jsonSchema); expect(result).toEqual(expectedParameters); }); it("should throw error for nested arrays", () => { const jsonSchema = { type: "object", properties: { nestedArray: { type: "array", items: { type: "array", items: { type: "string" } }, description: "Matrix of strings" } }, required: ["nestedArray"] }; expect(() => jsonSchemaToActionParameters(jsonSchema)).toThrow( "Nested arrays are not supported" ); }); it("should ensure round-trip conversion works", () => { const originalParameters = [ { name: "name", type: "string", description: "User name" }, { name: "age", type: "number", description: "User age", required: false }, { name: "role", type: "string", enum: ["admin", "user"], description: "User role" }, { name: "address", type: "object", description: "User address", attributes: [ { name: "street", type: "string", description: "Street name" }, { name: "city", type: "string", description: "City name" } ] }, { name: "contacts", type: "object[]", description: "User contacts", attributes: [ { name: "type", type: "string", description: "Contact type" }, { name: "value", type: "string", description: "Contact value" } ] } ]; const jsonSchema = actionParametersToJsonSchema(originalParameters); const roundTripParameters = jsonSchemaToActionParameters(jsonSchema); expect(roundTripParameters).toEqual(originalParameters); }); }); //# sourceMappingURL=json-schema.test.js.map