UNPKG

@genkit-ai/core

Version:

Genkit AI framework core libraries.

1 lines 15.2 kB
{"version":3,"sources":["../src/schema.ts"],"sourcesContent":["/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Validator } from '@cfworker/json-schema';\nimport Ajv, { type ErrorObject, type JSONSchemaType } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport { z } from 'zod';\nimport zodToJsonSchema from 'zod-to-json-schema';\nimport { getGenkitRuntimeConfig } from './config.mjs';\nimport { GenkitError } from './error.mjs';\n\nimport type { Registry } from './registry.mjs';\nconst ajv = new Ajv();\naddFormats(ajv);\n\nexport { z }; // provide a consistent zod to use throughout genkit\n\n/**\n * JSON schema.\n */\nexport type JSONSchema = JSONSchemaType<any> | any;\n\nconst jsonSchemas = new WeakMap<z.ZodTypeAny, JSONSchema>();\nconst ajvValidators = new WeakMap<JSONSchema, ReturnType<typeof ajv.compile>>();\nconst cfWorkerValidators = new WeakMap<JSONSchema, Validator>();\nconst schemaAnnotations = new WeakMap<z.ZodTypeAny, Record<string, any>>();\n\n/**\n * Annotates a Zod schema with UI-specific metadata.\n *\n * NOTE: It's typically recommended to use x-genkit-* (or similar) as the prefix.\n */\nexport function annotateSchema<T extends z.ZodTypeAny>(\n schema: T,\n annotations: Record<string, any>\n): T {\n const current = schemaAnnotations.get(schema) || {};\n schemaAnnotations.set(schema, { ...current, ...annotations });\n return schema;\n}\n\n/**\n * Wrapper object for various ways schema can be provided.\n */\nexport interface ProvidedSchema {\n jsonSchema?: JSONSchema;\n schema?: z.ZodTypeAny;\n}\n\n/**\n * Schema validation error.\n */\nexport class ValidationError extends GenkitError {\n constructor({\n data,\n errors,\n schema,\n }: {\n data: any;\n errors: ValidationErrorDetail[];\n schema: JSONSchema;\n }) {\n super({\n status: 'INVALID_ARGUMENT',\n message: `Schema validation failed. Parse Errors:\\n\\n${errors.map((e) => `- ${e.path}: ${e.message}`).join('\\n')}\\n\\nProvided data:\\n\\n${JSON.stringify(data, null, 2)}\\n\\nRequired JSON schema:\\n\\n${JSON.stringify(schema, null, 2)}`,\n detail: { errors, schema },\n });\n }\n}\n\n/**\n * Converts a Zod schema into a JSON schema, utilizing an in-memory cache for known objects.\n * @param options Provide a json schema and/or zod schema. JSON schema has priority.\n * @returns A JSON schema.\n */\nexport function toJsonSchema({\n jsonSchema,\n schema,\n}: ProvidedSchema): JSONSchema | undefined {\n // if neither jsonSchema or schema is present return undefined\n if (!jsonSchema && !schema) return null;\n if (jsonSchema) return jsonSchema;\n if (jsonSchemas.has(schema!)) return jsonSchemas.get(schema!)!;\n const outSchema = zodToJsonSchema(schema!, {\n removeAdditionalStrategy: 'strict',\n });\n const annotatedSchema = applyAnnotations(schema!, outSchema as JSONSchema);\n jsonSchemas.set(schema!, annotatedSchema);\n return annotatedSchema;\n}\n\n/**\n * Recursively applies annotations to a JSON schema by walking the Zod tree\n * and matching it against the JSON schema structure.\n *\n * Note: This currently does not resolve JSON schema `$ref` nodes. Annotations\n * on recursive schemas (using `z.lazy`) may not be correctly applied to the\n * referenced definitions.\n */\nfunction applyAnnotations(schema: z.ZodTypeAny, json: any): any {\n if (!json || typeof json !== 'object') return json;\n\n const annotationsToApply: Record<string, any>[] = [];\n let current = schema;\n\n // Collect all annotations in the hierarchy (outer to inner)\n while (current) {\n const ann = schemaAnnotations.get(current);\n if (ann) annotationsToApply.push(ann);\n\n if (\n current instanceof z.ZodOptional ||\n current instanceof z.ZodNullable ||\n current instanceof z.ZodDefault ||\n current instanceof z.ZodEffects\n ) {\n current = (current as any)._def.innerType || (current as any)._def.schema;\n } else {\n break;\n }\n }\n\n // Resolve annotations (outer-most last so it wins)\n const resolvedAnnotations: Record<string, any> = {};\n for (let i = annotationsToApply.length - 1; i >= 0; i--) {\n Object.assign(resolvedAnnotations, annotationsToApply[i]);\n }\n\n for (const key in resolvedAnnotations) {\n if (Object.prototype.hasOwnProperty.call(json, key)) {\n console.warn(\n `Annotation key \"${key}\" conflicts with existing JSON schema property and will be ignored.`\n );\n continue;\n }\n json[key] = resolvedAnnotations[key];\n }\n\n const inner = current;\n if (inner instanceof z.ZodObject && json.properties) {\n for (const key in inner.shape) {\n if (json.properties[key]) {\n applyAnnotations(inner.shape[key], json.properties[key]);\n }\n }\n } else if (inner instanceof z.ZodArray && json.items) {\n applyAnnotations(inner.element, json.items);\n } else if (inner instanceof z.ZodUnion && json.anyOf) {\n for (let i = 0; i < inner.options.length; i++) {\n applyAnnotations(inner.options[i], json.anyOf[i]);\n }\n } else if (inner instanceof z.ZodIntersection && json.allOf) {\n const schemas: z.ZodTypeAny[] = [];\n const collect = (s: z.ZodTypeAny) => {\n if (s instanceof z.ZodIntersection) {\n collect(s._def.left);\n collect(s._def.right);\n } else {\n schemas.push(s);\n }\n };\n collect(inner);\n if (schemas.length === json.allOf.length) {\n for (let i = 0; i < schemas.length; i++) {\n applyAnnotations(schemas[i], json.allOf[i]);\n }\n }\n } else if (inner instanceof z.ZodRecord && json.additionalProperties) {\n applyAnnotations(inner.valueSchema, json.additionalProperties);\n } else if (inner instanceof z.ZodTuple && Array.isArray(json.items)) {\n for (let i = 0; i < inner.items.length; i++) {\n applyAnnotations(inner.items[i], json.items[i]);\n }\n } else if (inner instanceof z.ZodDiscriminatedUnion && json.anyOf) {\n for (let i = 0; i < inner.options.length; i++) {\n applyAnnotations(inner.options[i], json.anyOf[i]);\n }\n }\n\n return json;\n}\n\n/**\n * Schema validation error details.\n */\nexport interface ValidationErrorDetail {\n path: string;\n message: string;\n}\n\nfunction ajvErrorToValidationErrorDetail(\n error: ErrorObject\n): ValidationErrorDetail {\n return {\n path: error.instancePath.substring(1).replace(/\\//g, '.') || '(root)',\n message: error.message!,\n };\n}\n\nfunction cfWorkerErrorToValidationErrorDetail(error: {\n instanceLocation: string;\n error: string;\n}): ValidationErrorDetail {\n const path = error.instanceLocation.startsWith('#/')\n ? error.instanceLocation.substring(2)\n : '';\n return {\n path: path.replace(/\\//g, '.') || '(root)',\n message: error.error,\n };\n}\n\n/**\n * Validation response.\n */\nexport type ValidationResponse =\n | {\n valid: true;\n errors?: undefined;\n schema: JSONSchema;\n }\n | {\n valid: false;\n errors: ValidationErrorDetail[];\n schema: JSONSchema;\n };\n\n/**\n * Validates the provided data against the provided schema.\n */\nexport function validateSchema(\n data: unknown,\n options: ProvidedSchema\n): ValidationResponse {\n const toValidate = toJsonSchema(options);\n if (!toValidate) {\n return { valid: true, schema: toValidate };\n }\n const validationMode = getGenkitRuntimeConfig().jsonSchemaMode;\n\n if (validationMode === 'interpret') {\n let validator = cfWorkerValidators.get(toValidate);\n if (!validator) {\n validator = new Validator(toValidate);\n cfWorkerValidators.set(toValidate, validator);\n }\n\n const result = validator.validate(sanitizeForJsonSchema(data));\n if (!result.valid) {\n return {\n valid: false,\n errors: result.errors.map(cfWorkerErrorToValidationErrorDetail),\n schema: toValidate,\n };\n }\n\n return {\n valid: result.valid,\n schema: toValidate,\n };\n }\n\n let validator = ajvValidators.get(toValidate);\n if (!validator) {\n validator = ajv.compile(toValidate);\n ajvValidators.set(toValidate, validator);\n }\n\n const valid = validator(data) as boolean;\n if (!valid) {\n return {\n valid: false,\n errors: (validator.errors ?? []).map(ajvErrorToValidationErrorDetail),\n schema: toValidate,\n };\n }\n\n return { valid, schema: toValidate };\n}\n\n/**\n * Parses raw data object against the provided schema.\n */\nexport function parseSchema<T = unknown>(\n data: unknown,\n options: ProvidedSchema\n): T {\n const result = validateSchema(data, options);\n if (!result.valid) {\n throw new ValidationError({\n data,\n errors: result.errors,\n schema: result.schema,\n });\n }\n return data as T;\n}\n\n/**\n * Registers provided schema as a named schema object in the Genkit registry.\n *\n * @hidden\n */\nexport function defineSchema<T extends z.ZodTypeAny>(\n registry: Registry,\n name: string,\n schema: T\n): T {\n registry.registerSchema(name, { schema });\n return schema;\n}\n\n/**\n * Registers provided JSON schema as a named schema object in the Genkit registry.\n *\n * @hidden\n */\nexport function defineJsonSchema(\n registry: Registry,\n name: string,\n jsonSchema: JSONSchema\n) {\n registry.registerSchema(name, { jsonSchema });\n return jsonSchema;\n}\n\nfunction sanitizeForJsonSchema(data: any): any {\n if (Array.isArray(data)) {\n return data.map(sanitizeForJsonSchema);\n } else if (data !== null && typeof data === 'object') {\n const out: any = {};\n for (const key in data) {\n if (data[key] !== undefined) {\n out[key] = sanitizeForJsonSchema(data[key]);\n }\n }\n return out;\n }\n return data;\n}\n"],"mappings":"AAgBA,SAAS,iBAAiB;AAC1B,OAAO,SAAoD;AAC3D,OAAO,gBAAgB;AACvB,SAAS,SAAS;AAClB,OAAO,qBAAqB;AAC5B,SAAS,8BAA8B;AACvC,SAAS,mBAAmB;AAG5B,MAAM,MAAM,IAAI,IAAI;AACpB,WAAW,GAAG;AASd,MAAM,cAAc,oBAAI,QAAkC;AAC1D,MAAM,gBAAgB,oBAAI,QAAoD;AAC9E,MAAM,qBAAqB,oBAAI,QAA+B;AAC9D,MAAM,oBAAoB,oBAAI,QAA2C;AAOlE,SAAS,eACd,QACA,aACG;AACH,QAAM,UAAU,kBAAkB,IAAI,MAAM,KAAK,CAAC;AAClD,oBAAkB,IAAI,QAAQ,EAAE,GAAG,SAAS,GAAG,YAAY,CAAC;AAC5D,SAAO;AACT;AAaO,MAAM,wBAAwB,YAAY;AAAA,EAC/C,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAIG;AACD,UAAM;AAAA,MACJ,QAAQ;AAAA,MACR,SAAS;AAAA;AAAA,EAA8C,OAAO,IAAI,CAAC,MAAM,KAAK,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA,EAAyB,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA,EAAgC,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,MACrO,QAAQ,EAAE,QAAQ,OAAO;AAAA,IAC3B,CAAC;AAAA,EACH;AACF;AAOO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AACF,GAA2C;AAEzC,MAAI,CAAC,cAAc,CAAC,OAAQ,QAAO;AACnC,MAAI,WAAY,QAAO;AACvB,MAAI,YAAY,IAAI,MAAO,EAAG,QAAO,YAAY,IAAI,MAAO;AAC5D,QAAM,YAAY,gBAAgB,QAAS;AAAA,IACzC,0BAA0B;AAAA,EAC5B,CAAC;AACD,QAAM,kBAAkB,iBAAiB,QAAS,SAAuB;AACzE,cAAY,IAAI,QAAS,eAAe;AACxC,SAAO;AACT;AAUA,SAAS,iBAAiB,QAAsB,MAAgB;AAC9D,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAE9C,QAAM,qBAA4C,CAAC;AACnD,MAAI,UAAU;AAGd,SAAO,SAAS;AACd,UAAM,MAAM,kBAAkB,IAAI,OAAO;AACzC,QAAI,IAAK,oBAAmB,KAAK,GAAG;AAEpC,QACE,mBAAmB,EAAE,eACrB,mBAAmB,EAAE,eACrB,mBAAmB,EAAE,cACrB,mBAAmB,EAAE,YACrB;AACA,gBAAW,QAAgB,KAAK,aAAc,QAAgB,KAAK;AAAA,IACrE,OAAO;AACL;AAAA,IACF;AAAA,EACF;AAGA,QAAM,sBAA2C,CAAC;AAClD,WAAS,IAAI,mBAAmB,SAAS,GAAG,KAAK,GAAG,KAAK;AACvD,WAAO,OAAO,qBAAqB,mBAAmB,CAAC,CAAC;AAAA,EAC1D;AAEA,aAAW,OAAO,qBAAqB;AACrC,QAAI,OAAO,UAAU,eAAe,KAAK,MAAM,GAAG,GAAG;AACnD,cAAQ;AAAA,QACN,mBAAmB,GAAG;AAAA,MACxB;AACA;AAAA,IACF;AACA,SAAK,GAAG,IAAI,oBAAoB,GAAG;AAAA,EACrC;AAEA,QAAM,QAAQ;AACd,MAAI,iBAAiB,EAAE,aAAa,KAAK,YAAY;AACnD,eAAW,OAAO,MAAM,OAAO;AAC7B,UAAI,KAAK,WAAW,GAAG,GAAG;AACxB,yBAAiB,MAAM,MAAM,GAAG,GAAG,KAAK,WAAW,GAAG,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF,WAAW,iBAAiB,EAAE,YAAY,KAAK,OAAO;AACpD,qBAAiB,MAAM,SAAS,KAAK,KAAK;AAAA,EAC5C,WAAW,iBAAiB,EAAE,YAAY,KAAK,OAAO;AACpD,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,QAAQ,KAAK;AAC7C,uBAAiB,MAAM,QAAQ,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,IAClD;AAAA,EACF,WAAW,iBAAiB,EAAE,mBAAmB,KAAK,OAAO;AAC3D,UAAM,UAA0B,CAAC;AACjC,UAAM,UAAU,CAAC,MAAoB;AACnC,UAAI,aAAa,EAAE,iBAAiB;AAClC,gBAAQ,EAAE,KAAK,IAAI;AACnB,gBAAQ,EAAE,KAAK,KAAK;AAAA,MACtB,OAAO;AACL,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AACA,YAAQ,KAAK;AACb,QAAI,QAAQ,WAAW,KAAK,MAAM,QAAQ;AACxC,eAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,yBAAiB,QAAQ,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,MAC5C;AAAA,IACF;AAAA,EACF,WAAW,iBAAiB,EAAE,aAAa,KAAK,sBAAsB;AACpE,qBAAiB,MAAM,aAAa,KAAK,oBAAoB;AAAA,EAC/D,WAAW,iBAAiB,EAAE,YAAY,MAAM,QAAQ,KAAK,KAAK,GAAG;AACnE,aAAS,IAAI,GAAG,IAAI,MAAM,MAAM,QAAQ,KAAK;AAC3C,uBAAiB,MAAM,MAAM,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,IAChD;AAAA,EACF,WAAW,iBAAiB,EAAE,yBAAyB,KAAK,OAAO;AACjE,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,QAAQ,KAAK;AAC7C,uBAAiB,MAAM,QAAQ,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC;AAAA,IAClD;AAAA,EACF;AAEA,SAAO;AACT;AAUA,SAAS,gCACP,OACuB;AACvB,SAAO;AAAA,IACL,MAAM,MAAM,aAAa,UAAU,CAAC,EAAE,QAAQ,OAAO,GAAG,KAAK;AAAA,IAC7D,SAAS,MAAM;AAAA,EACjB;AACF;AAEA,SAAS,qCAAqC,OAGpB;AACxB,QAAM,OAAO,MAAM,iBAAiB,WAAW,IAAI,IAC/C,MAAM,iBAAiB,UAAU,CAAC,IAClC;AACJ,SAAO;AAAA,IACL,MAAM,KAAK,QAAQ,OAAO,GAAG,KAAK;AAAA,IAClC,SAAS,MAAM;AAAA,EACjB;AACF;AAoBO,SAAS,eACd,MACA,SACoB;AACpB,QAAM,aAAa,aAAa,OAAO;AACvC,MAAI,CAAC,YAAY;AACf,WAAO,EAAE,OAAO,MAAM,QAAQ,WAAW;AAAA,EAC3C;AACA,QAAM,iBAAiB,uBAAuB,EAAE;AAEhD,MAAI,mBAAmB,aAAa;AAClC,QAAIA,aAAY,mBAAmB,IAAI,UAAU;AACjD,QAAI,CAACA,YAAW;AACd,MAAAA,aAAY,IAAI,UAAU,UAAU;AACpC,yBAAmB,IAAI,YAAYA,UAAS;AAAA,IAC9C;AAEA,UAAM,SAASA,WAAU,SAAS,sBAAsB,IAAI,CAAC;AAC7D,QAAI,CAAC,OAAO,OAAO;AACjB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,OAAO,OAAO,IAAI,oCAAoC;AAAA,QAC9D,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,WAAO;AAAA,MACL,OAAO,OAAO;AAAA,MACd,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,MAAI,YAAY,cAAc,IAAI,UAAU;AAC5C,MAAI,CAAC,WAAW;AACd,gBAAY,IAAI,QAAQ,UAAU;AAClC,kBAAc,IAAI,YAAY,SAAS;AAAA,EACzC;AAEA,QAAM,QAAQ,UAAU,IAAI;AAC5B,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SAAS,UAAU,UAAU,CAAC,GAAG,IAAI,+BAA+B;AAAA,MACpE,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,QAAQ,WAAW;AACrC;AAKO,SAAS,YACd,MACA,SACG;AACH,QAAM,SAAS,eAAe,MAAM,OAAO;AAC3C,MAAI,CAAC,OAAO,OAAO;AACjB,UAAM,IAAI,gBAAgB;AAAA,MACxB;AAAA,MACA,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO;AAAA,IACjB,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAOO,SAAS,aACd,UACA,MACA,QACG;AACH,WAAS,eAAe,MAAM,EAAE,OAAO,CAAC;AACxC,SAAO;AACT;AAOO,SAAS,iBACd,UACA,MACA,YACA;AACA,WAAS,eAAe,MAAM,EAAE,WAAW,CAAC;AAC5C,SAAO;AACT;AAEA,SAAS,sBAAsB,MAAgB;AAC7C,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK,IAAI,qBAAqB;AAAA,EACvC,WAAW,SAAS,QAAQ,OAAO,SAAS,UAAU;AACpD,UAAM,MAAW,CAAC;AAClB,eAAW,OAAO,MAAM;AACtB,UAAI,KAAK,GAAG,MAAM,QAAW;AAC3B,YAAI,GAAG,IAAI,sBAAsB,KAAK,GAAG,CAAC;AAAA,MAC5C;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":["validator"]}