diff --git a/libs/langchain-google-common/src/tests/chat_models.test.ts b/libs/langchain-google-common/src/tests/chat_models.test.ts index dfd7de527e38..cb6674459c32 100644 --- a/libs/langchain-google-common/src/tests/chat_models.test.ts +++ b/libs/langchain-google-common/src/tests/chat_models.test.ts @@ -10,11 +10,14 @@ import { ToolMessage, } from "@langchain/core/messages"; +import { z } from "zod"; +import { zodToJsonSchema } from "zod-to-json-schema"; import { ChatGoogleBase, ChatGoogleBaseInput } from "../chat_models.js"; import { authOptions, MockClient, MockClientAuthInfo, mockId } from "./mock.js"; import { GeminiTool, GoogleAIBaseLLMInput } from "../types.js"; import { GoogleAbstractedClient } from "../auth.js"; import { GoogleAISafetyError } from "../utils/safety.js"; +import { removeAdditionalProperties } from "../utils/zod_to_gemini_parameters.js"; class ChatGoogle extends ChatGoogleBase { constructor(fields?: ChatGoogleBaseInput) { @@ -840,3 +843,74 @@ describe("Mock ChatGoogle", () => { console.log(JSON.stringify(record?.opts?.data, null, 1)); }); }); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function extractKeys(obj: Record, keys: string[] = []) { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + keys.push(key); + if (typeof obj[key] === "object" && obj[key] !== null) { + extractKeys(obj[key], keys); + } + } + } + return keys; +} + +test("removeAdditionalProperties can remove all instances of additionalProperties", async () => { + const idealResponseSchema = z.object({ + idealResponse: z + .string() + .optional() + .describe("The ideal response to the question"), + }); + const questionSchema = z.object({ + question: z.string().describe("Question text"), + type: z.enum(["singleChoice", "multiChoice"]).describe("Question type"), + options: z.array(z.string()).describe("List of possible answers"), + correctAnswer: z + .string() + .optional() + .describe("correct answer from the possible answers"), + idealResponses: z + .array(idealResponseSchema) + .describe("Array of ideal responses to the question"), + }); + + const schema = z.object({ + questions: z.array(questionSchema).describe("Array of question objects"), + }); + + const parsedSchemaArr = removeAdditionalProperties(zodToJsonSchema(schema)); + const arrSchemaKeys = extractKeys(parsedSchemaArr); + expect( + arrSchemaKeys.find((key) => key === "additionalProperties") + ).toBeUndefined(); + const parsedSchemaObj = removeAdditionalProperties( + zodToJsonSchema(questionSchema) + ); + const arrSchemaObj = extractKeys(parsedSchemaObj); + expect( + arrSchemaObj.find((key) => key === "additionalProperties") + ).toBeUndefined(); + + const analysisSchema = z.object({ + decision: z.enum(["UseAPI", "UseFallback"]), + explanation: z.string(), + apiDetails: z + .object({ + serviceName: z.string(), + endpointName: z.string(), + parameters: z.record(z.unknown()), + extractionPath: z.string(), + }) + .optional(), + }); + const parsedAnalysisSchema = removeAdditionalProperties( + zodToJsonSchema(analysisSchema) + ); + const analysisSchemaObj = extractKeys(parsedAnalysisSchema); + expect( + analysisSchemaObj.find((key) => key === "additionalProperties") + ).toBeUndefined(); +}); diff --git a/libs/langchain-google-common/src/utils/zod_to_gemini_parameters.ts b/libs/langchain-google-common/src/utils/zod_to_gemini_parameters.ts index 229a578fbc21..101005e66bef 100644 --- a/libs/langchain-google-common/src/utils/zod_to_gemini_parameters.ts +++ b/libs/langchain-google-common/src/utils/zod_to_gemini_parameters.ts @@ -15,10 +15,7 @@ export function removeAdditionalProperties( if (typeof obj === "object" && obj !== null) { const newObj = { ...obj }; - if ( - "additionalProperties" in newObj && - typeof newObj.additionalProperties === "boolean" - ) { + if ("additionalProperties" in newObj) { delete newObj.additionalProperties; }