From a4542a61acbc0410beae5056e52d7f98c71bd132 Mon Sep 17 00:00:00 2001 From: nitin yadav Date: Mon, 1 Dec 2025 16:22:56 +0530 Subject: [PATCH 1/2] fix(google-genai): sanitize JSON schema by removing unsupported propertyNames and patternProperties for Gemini tool-calling --- .../langchain-google-genai/src/chat_models.ts | 3 ++- .../src/cleanGeminiSchema.ts | 22 +++++++++++++++++++ .../src/utils/common.ts | 8 ++++--- 3 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 libs/providers/langchain-google-genai/src/cleanGeminiSchema.ts diff --git a/libs/providers/langchain-google-genai/src/chat_models.ts b/libs/providers/langchain-google-genai/src/chat_models.ts index 55e93f19d6be..979326e2988c 100644 --- a/libs/providers/langchain-google-genai/src/chat_models.ts +++ b/libs/providers/langchain-google-genai/src/chat_models.ts @@ -63,6 +63,7 @@ import { } from "./types.js"; import { convertToolsToGenAI } from "./utils/tools.js"; import PROFILES from "./profiles.js"; +import { cleanGeminiSchema } from "./cleanGeminiSchema"; interface TokenUsage { completionTokens?: number; @@ -1093,7 +1094,7 @@ export class ChatGoogleGenerativeAI name: functionName, description: jsonSchema.description ?? "A function available to call.", - parameters: jsonSchema as GenerativeAIFunctionDeclarationSchema, + parameters: cleanGeminiSchema(jsonSchema) as GenerativeAIFunctionDeclarationSchema, }, ], }, diff --git a/libs/providers/langchain-google-genai/src/cleanGeminiSchema.ts b/libs/providers/langchain-google-genai/src/cleanGeminiSchema.ts new file mode 100644 index 000000000000..4e85cd7b9a0c --- /dev/null +++ b/libs/providers/langchain-google-genai/src/cleanGeminiSchema.ts @@ -0,0 +1,22 @@ +export function cleanGeminiSchema(schema: any): any { + if (!schema || typeof schema !== "object") { + return schema; + } + + const cleaned: any = Array.isArray(schema) + ? schema.map((item) => cleanGeminiSchema(item)) + : { ...schema }; + + + delete cleaned.propertyNames; + delete cleaned.patternProperties; + + + for (const key of Object.keys(cleaned)) { + if (typeof cleaned[key] === "object") { + cleaned[key] = cleanGeminiSchema(cleaned[key]); + } + } + + return cleaned; +} diff --git a/libs/providers/langchain-google-genai/src/utils/common.ts b/libs/providers/langchain-google-genai/src/utils/common.ts index f59042aeb3f7..9ab3223e4b6d 100644 --- a/libs/providers/langchain-google-genai/src/utils/common.ts +++ b/libs/providers/langchain-google-genai/src/utils/common.ts @@ -39,6 +39,7 @@ import { isLangChainTool } from "@langchain/core/utils/function_calling"; import { isOpenAITool } from "@langchain/core/language_models/base"; import { ToolCallChunk } from "@langchain/core/messages/tool"; import { v4 as uuidv4 } from "uuid"; +import { cleanGeminiSchema } from "../cleanGeminiSchema.js"; import { jsonSchemaToGeminiParameters, schemaToGenerativeAIParameters, @@ -358,8 +359,8 @@ export function convertMessageContentToParts( const result = Array.isArray(message.content) ? (message.content - .map((c) => _convertLangChainContentToPart(c, isMultimodalModel)) - .filter((p) => p !== undefined) as Part[]) + .map((c) => _convertLangChainContentToPart(c, isMultimodalModel)) + .filter((p) => p !== undefined) as Part[]) : message.content; if (message.status === "error") { @@ -799,7 +800,8 @@ export function convertToGenerativeAITools( return { name: tool.name, description: tool.description, - parameters: jsonSchema, + parameters: cleanGeminiSchema(jsonSchema), + }; } if (isOpenAITool(tool)) { From e7edff015a0aa59760553fa8e4b1bb721104f32a Mon Sep 17 00:00:00 2001 From: nitin yadav Date: Tue, 16 Dec 2025 23:56:59 +0530 Subject: [PATCH 2/2] test(google-genai): add regression test for cleanGeminiSchema --- .../src/tests/cleanGeminiSchema.test.ts | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 libs/providers/langchain-google-genai/src/tests/cleanGeminiSchema.test.ts diff --git a/libs/providers/langchain-google-genai/src/tests/cleanGeminiSchema.test.ts b/libs/providers/langchain-google-genai/src/tests/cleanGeminiSchema.test.ts new file mode 100644 index 000000000000..87257f02adcc --- /dev/null +++ b/libs/providers/langchain-google-genai/src/tests/cleanGeminiSchema.test.ts @@ -0,0 +1,50 @@ +import { cleanGeminiSchema } from "../cleanGeminiSchema"; + +describe("cleanGeminiSchema", () => { + it("removes propertyNames from schema", () => { + const schema = { + type: "object", + propertyNames: { + pattern: "^[a-zA-Z]+$", + }, + properties: { + name: { type: "string" }, + }, + }; + + const cleaned = cleanGeminiSchema(schema as any); + + expect(cleaned.propertyNames).toBeUndefined(); + expect(cleaned.properties).toBeDefined(); + }); + + it("removes patternProperties from schema", () => { + const schema = { + type: "object", + patternProperties: { + "^S_": { type: "string" }, + }, + properties: { + id: { type: "number" }, + }, + }; + + const cleaned = cleanGeminiSchema(schema as any); + + expect(cleaned.patternProperties).toBeUndefined(); + expect(cleaned.properties).toBeDefined(); + }); + + it("does not modify schema without unsupported fields", () => { + const schema = { + type: "object", + properties: { + age: { type: "number" }, + }, + }; + + const cleaned = cleanGeminiSchema(schema as any); + + expect(cleaned).toEqual(schema); + }); +});