UNPKG

@techery/zod-to-vertex-schema

Version:

Convert Zod schemas to Vertex AI/Gemini compatible schemas

268 lines 9.16 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SchemaType = void 0; exports.zodDynamicEnum = zodDynamicEnum; exports.zodToVertexSchema = zodToVertexSchema; const zod_1 = require("zod"); var SchemaType; (function (SchemaType) { /** String type. */ SchemaType["STRING"] = "STRING"; /** Number type. */ SchemaType["NUMBER"] = "NUMBER"; /** Integer type. */ SchemaType["INTEGER"] = "INTEGER"; /** Boolean type. */ SchemaType["BOOLEAN"] = "BOOLEAN"; /** Array type. */ SchemaType["ARRAY"] = "ARRAY"; /** Object type. */ SchemaType["OBJECT"] = "OBJECT"; })(SchemaType || (exports.SchemaType = SchemaType = {})); function zodDynamicEnum(values) { return zod_1.z.enum(values); } /** * Main function to convert any Zod schema into a Gemini-compatible VertexSchema. * Defaults are intentionally ignored (not included in final schema). */ function zodToVertexSchema(schema) { const isZodNull = schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodNull; // 1) If schema is optional or nullable, unwrap it and mark "nullable" if appropriate. if (!isZodNull && (schema.isOptional() || schema.isNullable())) { const baseSchema = unwrapOptionalOrNullable(schema); const geminiInner = zodToVertexSchema(baseSchema); // If the Zod schema is nullable, reflect that in the Gemini schema if (schema.isNullable()) { geminiInner.nullable = true; } // Carry over the description if present if (schema.description) { geminiInner.description = schema.description; } return geminiInner; } // 2) Dispatch based on known Zod constructors if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodString) { return makeStringSchema(schema); } if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodNumber) { return makeNumberSchema(schema); } if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodBoolean) { return { type: SchemaType.BOOLEAN, description: schema.description, }; } if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodObject) { return makeObjectSchema(schema); } if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodArray) { return makeArraySchema(schema); } if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodEnum) { return makeEnumSchema(schema); } if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodUnion) { return makeUnionSchema(schema); } if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodLiteral) { return makeLiteralSchema(schema); } if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodNull) { // No native "null" type in Gemini. Use type=STRING + nullable=true as a fallback. return { type: SchemaType.STRING, nullable: true, description: schema.description, }; } if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodDiscriminatedUnion) { return makeDiscriminatedUnionSchema(schema); } throw new Error(`Unsupported Zod type: ${schema.constructor.name}`); } /** * If a schema is optional/null/default, unwrap to the "inner" schema. * This way we can apply the relevant checks to the base type. */ function unwrapOptionalOrNullable(schema) { var _a, _b; if (schema._def.typeName === zod_1.ZodFirstPartyTypeKind.ZodNull) { throw new Error('ZodNull is not supported'); } // Zod chain can store underlying schema in ._def.innerType or ._def.schema. if ((_a = schema._def) === null || _a === void 0 ? void 0 : _a.innerType) { return schema._def.innerType; } if ((_b = schema._def) === null || _b === void 0 ? void 0 : _b.schema) { return schema._def.schema; } return schema; } /** * Convert a ZodString into a Gemini schema: * - format * - (Ignores any default value) */ function makeStringSchema(schema) { const gemini = { type: SchemaType.STRING, description: schema.description, }; // Iterate over each check in the ZodString's definition. const checks = schema._def.checks || []; for (const check of checks) { switch (check.kind) { case 'datetime': gemini.format = 'date-time'; break; case 'date': gemini.format = 'date'; break; default: throw new Error(`Unsupported string check: ${check.kind}`); } } return gemini; } /** * Convert z.number() to a Gemini schema: * - type=NUMBER or INTEGER * - min => minimum, max => maximum * - Ignores default values * - Includes description */ function makeNumberSchema(schema) { const gemini = { description: schema.description, }; const checks = schema._def.checks || []; const isInt = checks.some((c) => c.kind === 'int'); gemini.type = isInt ? SchemaType.INTEGER : SchemaType.NUMBER; for (const check of checks) { if (check.kind === 'min') { gemini.minimum = check.value; } else if (check.kind === 'max') { gemini.maximum = check.value; } } return gemini; } /** * Convert z.object(...) to a Gemini schema: * - type=OBJECT * - properties => recursively derived from each field * - required => array of non-optional field names * - propertyOrdering => preserve the field order * - Ignores default * - Includes description */ function makeObjectSchema(schema) { const shape = schema._def.shape(); const propertyKeys = Object.keys(shape); const properties = {}; const required = []; const propertyOrdering = []; for (const key of propertyKeys) { const fieldSchema = shape[key]; propertyOrdering.push(key); if (!fieldSchema.isOptional()) { required.push(key); } // Recursively convert each field to a Gemini schema properties[key] = zodToVertexSchema(fieldSchema); } const gemini = { type: SchemaType.OBJECT, properties, propertyOrdering, description: schema.description, }; if (required.length > 0) { gemini.required = required; } return gemini; } /** * Convert z.array(T) => type=ARRAY with "items" as T's schema, * and minItems / maxItems / exactLength if specified. * (Ignores default, includes description.) */ function makeArraySchema(schema) { const itemsGemini = zodToVertexSchema(schema.element); const gemini = { type: SchemaType.ARRAY, items: itemsGemini, description: schema.description, }; // For arrays, Zod uses ._def.minLength / ._def.maxLength / ._def.exactLength const { minLength, maxLength, exactLength } = schema._def; if ((minLength === null || minLength === void 0 ? void 0 : minLength.value) !== undefined) { gemini.minItems = minLength.value; } if ((maxLength === null || maxLength === void 0 ? void 0 : maxLength.value) !== undefined) { gemini.maxItems = maxLength.value; } if ((exactLength === null || exactLength === void 0 ? void 0 : exactLength.value) !== undefined) { gemini.minItems = exactLength.value; gemini.maxItems = exactLength.value; } return gemini; } /** * Convert z.enum([...]) => type=STRING + enum=[...]. * Defaults are ignored. Description is included. */ function makeEnumSchema(schema) { return { type: SchemaType.STRING, enum: schema._def.values, description: schema.description, }; } /** * Convert a non-discriminated z.union([...]) into anyOf: [ ... ]. */ function makeUnionSchema(schema) { const variants = schema._def.options; return { anyOf: variants.map((variant) => zodToVertexSchema(variant)), }; } /** * Convert z.literal(...) => type + enum with a single value. * (No default to consider, plus description.) */ function makeLiteralSchema(schema) { const literalValue = schema._def.value; const valueType = typeof literalValue; const gemini = { description: schema.description, }; // We only handle "string" in detail here, fallback for other literal types // as type=STRING with that single enum value as a string if (valueType === 'string') { gemini.type = SchemaType.STRING; gemini.enum = [literalValue]; } else { throw new Error(`Unsupported literal type. Gemini doesn't support ${valueType} literals.`); } return gemini; } /** * Convert a discriminated union => anyOf for each branch. * Ignores default. No special "discriminator" field included. */ function makeDiscriminatedUnionSchema(schema) { const { optionsMap } = schema._def; // Convert each variant in the union const anyOfSchemas = Array.from(optionsMap.values()).map((option) => zodToVertexSchema(option)); return { anyOf: anyOfSchemas, }; } //# sourceMappingURL=zod-to-vertex-schema.js.map