Skip to content

Commit 8ebbba2

Browse files
jpr5AlemTuzlak
authored andcommitted
fix: handle anyOf/oneOf/ in JSON schema to Zod conversion (CopilotKit#2220)
1 parent 9273085 commit 8ebbba2

1 file changed

Lines changed: 42 additions & 3 deletions

File tree

packages/shared/src/utils/json-schema.ts

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,36 @@ function convertAttribute(attribute: Parameter): JSONSchema {
241241
export function convertJsonSchemaToZodSchema(
242242
jsonSchema: any,
243243
required: boolean,
244+
definitions?: Record<string, any>,
244245
): z.ZodSchema {
246+
// Resolve $ref references
247+
if (jsonSchema.$ref && definitions) {
248+
const refPath = jsonSchema.$ref.replace(/^#\/\$defs\/|^#\/definitions\//, "");
249+
const resolved = definitions[refPath];
250+
if (resolved) {
251+
return convertJsonSchemaToZodSchema(resolved, required, definitions);
252+
}
253+
}
254+
255+
// Collect top-level definitions for $ref resolution
256+
const defs = definitions ?? jsonSchema.$defs ?? jsonSchema.definitions;
257+
258+
// Handle anyOf / oneOf as z.union
259+
const unionVariants = jsonSchema.anyOf ?? jsonSchema.oneOf;
260+
if (Array.isArray(unionVariants) && unionVariants.length > 0) {
261+
if (unionVariants.length === 1) {
262+
return convertJsonSchemaToZodSchema(unionVariants[0], required, defs);
263+
}
264+
const schemas = unionVariants.map((v: any) =>
265+
convertJsonSchemaToZodSchema(v, true, defs),
266+
);
267+
let schema = z.union(schemas as [z.ZodSchema, z.ZodSchema, ...z.ZodSchema[]]);
268+
if (jsonSchema.description) {
269+
schema = schema.describe(jsonSchema.description);
270+
}
271+
return required ? schema : schema.optional();
272+
}
273+
245274
if (jsonSchema.type === "object") {
246275
const spec: { [key: string]: z.ZodSchema } = {};
247276

@@ -253,6 +282,7 @@ export function convertJsonSchemaToZodSchema(
253282
spec[key] = convertJsonSchemaToZodSchema(
254283
value,
255284
jsonSchema.required ? jsonSchema.required.includes(key) : false,
285+
defs,
256286
);
257287
}
258288
let schema = z.object(spec).describe(jsonSchema.description);
@@ -266,18 +296,27 @@ export function convertJsonSchemaToZodSchema(
266296
}
267297
let schema = z.string().describe(jsonSchema.description);
268298
return required ? schema : schema.optional();
269-
} else if (jsonSchema.type === "number") {
299+
} else if (jsonSchema.type === "number" || jsonSchema.type === "integer") {
270300
let schema = z.number().describe(jsonSchema.description);
271301
return required ? schema : schema.optional();
272302
} else if (jsonSchema.type === "boolean") {
273303
let schema = z.boolean().describe(jsonSchema.description);
274304
return required ? schema : schema.optional();
275305
} else if (jsonSchema.type === "array") {
276-
let itemSchema = convertJsonSchemaToZodSchema(jsonSchema.items, true);
306+
let itemSchema = convertJsonSchemaToZodSchema(jsonSchema.items, true, defs);
277307
let schema = z.array(itemSchema).describe(jsonSchema.description);
278308
return required ? schema : schema.optional();
309+
} else if (jsonSchema.type === "null") {
310+
let schema = z.null().describe(jsonSchema.description);
311+
return required ? schema : schema.optional();
312+
}
313+
314+
// Fallback: accept any value rather than throwing
315+
let schema = z.any();
316+
if (jsonSchema.description) {
317+
schema = schema.describe(jsonSchema.description);
279318
}
280-
throw new Error("Invalid JSON schema");
319+
return required ? schema : schema.optional();
281320
}
282321

283322
export function getZodParameters<T extends [] | Parameter[] | undefined>(

0 commit comments

Comments
 (0)