From a8f525554ccce45519eb815778a0c96d487a2418 Mon Sep 17 00:00:00 2001 From: Aarav Mittal <137450929+a2105z@users.noreply.github.com> Date: Thu, 30 Jul 2026 17:39:54 +0000 Subject: [PATCH] fix(js/core): allow Gemini propertyOrdering in schema validation Register the non-standard propertyOrdering keyword with Ajv so structured output schemas that include Gemini field-order hints no longer fail to compile. --- js/core/src/schema.ts | 4 +++ js/core/tests/schema_test.ts | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/js/core/src/schema.ts b/js/core/src/schema.ts index 67a0e8f0c1..7e799732c7 100644 --- a/js/core/src/schema.ts +++ b/js/core/src/schema.ts @@ -25,6 +25,10 @@ import { GenkitError } from './error.js'; import type { Registry } from './registry.js'; const ajv = new Ajv(); addFormats(ajv); +// Gemini structured output uses the non-standard `propertyOrdering` keyword as a +// field-order hint. Register it so Ajv strict mode does not reject those schemas. +// See https://ai.google.dev/gemini-api/docs/structured-output#property-ordering +ajv.addVocabulary(['propertyOrdering']); export { z }; // provide a consistent zod to use throughout genkit diff --git a/js/core/tests/schema_test.ts b/js/core/tests/schema_test.ts index bf359a5b1a..272e13acf6 100644 --- a/js/core/tests/schema_test.ts +++ b/js/core/tests/schema_test.ts @@ -105,6 +105,38 @@ describe('validate()', () => { { path: '(root)', message: "must have required property 'foo'" }, ], }, + { + it: 'should allow Gemini propertyOrdering keyword in json schema', + jsonSchema: { + type: 'object', + properties: { + answer: { type: 'number' }, + x: { type: 'number' }, + y: { type: 'number' }, + }, + required: ['x', 'y', 'answer'], + propertyOrdering: ['x', 'y', 'answer'], + additionalProperties: false, + }, + data: { x: 1, y: 2, answer: 3 }, + valid: true, + }, + { + it: 'should still validate data when propertyOrdering is present', + jsonSchema: { + type: 'object', + properties: { + x: { type: 'number' }, + y: { type: 'number' }, + }, + required: ['x', 'y'], + propertyOrdering: ['x', 'y'], + additionalProperties: false, + }, + data: { x: 'nope', y: 2 }, + valid: false, + errors: [{ path: 'x', message: 'must be number' }], + }, ]; for (const test of tests) { it(test.it, () => { @@ -375,6 +407,27 @@ describe('disableSchemaCodeGeneration()', () => { assert.strictEqual(result.valid, true); }); + it('should allow Gemini propertyOrdering keyword in interpret mode', () => { + disableSchemaCodeGeneration(); + const result = validateSchema( + { x: 1, y: 2, answer: 3 }, + { + jsonSchema: { + type: 'object', + properties: { + answer: { type: 'number' }, + x: { type: 'number' }, + y: { type: 'number' }, + }, + required: ['x', 'y', 'answer'], + propertyOrdering: ['x', 'y', 'answer'], + additionalProperties: false, + }, + } + ); + assert.strictEqual(result.valid, true); + }); + it('should strip undefined values recursively', () => { disableSchemaCodeGeneration(); const result = validateSchema(