diff --git a/.changeset/gemini-live-property-ordering.md b/.changeset/gemini-live-property-ordering.md new file mode 100644 index 000000000..30b10d04d --- /dev/null +++ b/.changeset/gemini-live-property-ordering.md @@ -0,0 +1,5 @@ +--- +'@livekit/agents-plugin-google': patch +--- + +Send `propertyOrdering` in Gemini Live tool schemas so tool arguments are generated in declared order. diff --git a/plugins/google/src/utils.test.ts b/plugins/google/src/utils.test.ts index c1691a8f0..f33e3d546 100644 --- a/plugins/google/src/utils.test.ts +++ b/plugins/google/src/utils.test.ts @@ -56,10 +56,29 @@ describe('Gemini function declarations', () => { expect(declaration.parameters).toEqual({ type: 'object', properties: { fields: { type: 'object' } }, + propertyOrdering: ['fields'], required: ['fields'], }); }); + it('keeps declared property order on the Live API, nested objects included', () => { + const decide = llm.tool({ + name: 'decide', + description: 'Reason first, then answer.', + parameters: z.object({ + reasoning: z.string(), + answer: z.enum(['a', 'b']), + detail: z.object({ why: z.string(), confidence: z.number() }), + }), + execute: async () => {}, + }); + const parameters = singleDeclaration(new llm.ToolContext([decide]), false).parameters!; + + expect(parameters.propertyOrdering).toEqual(['reasoning', 'answer', 'detail']); + expect(parameters.properties!.detail!.propertyOrdering).toEqual(['why', 'confidence']); + expect(parameters.properties!.reasoning!.propertyOrdering).toBeUndefined(); + }); + it('omits the schema for a function tool without parameters', () => { const toolCtx = new llm.ToolContext([ping]); const textDeclaration = singleDeclaration(toolCtx); diff --git a/plugins/google/src/utils.ts b/plugins/google/src/utils.ts index a004e4ffa..c1e2dd788 100644 --- a/plugins/google/src/utils.ts +++ b/plugins/google/src/utils.ts @@ -81,6 +81,10 @@ export function convertJSONSchemaToOpenAPISchema(jsonSchema: JSONSchema7Definiti }, {} as Record, ); + // Gemini generates an OpenAPI schema's properties alphabetically unless told + // otherwise; keep the declared order so earlier fields can inform later ones. + const keys = Object.keys(properties); + if (keys.length > 0) result.propertyOrdering = keys; } if (items) {