@langchain/google 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"params.cjs","names":["settableServiceTier"],"sources":["../../src/converters/params.ts"],"sourcesContent":["import {\n ChatGoogleFields,\n GooglePlatformType,\n settableServiceTier,\n SimplifiedSpeechConfig,\n SimplifiedSpeechLanguageConfig,\n SpeechSpeakerName,\n SpeechVoice,\n SpeechVoiceLanguage,\n} from \"../chat_models/types.js\";\nimport type { Gemini } from \"../chat_models/api-types.js\";\n\n/**\n * Converts a reasoning \"effort\" level value (e.g., \"minimal\", \"low\", \"medium\", \"high\")\n * to a corresponding reasoning token budget, depending on the target Gemini model.\n *\n * This mapping is essential when configuring Google Gemini API calls with reasoning\n * effort/level fields (as in the Gemini OpenAI-compatible API) or when translating\n * user-friendly configuration to explicit token budgets.\n *\n * - The mapping is refined to reflect the constraints documented by Google:\n * - For \"gemini-2.5-flash\" models, token budgets may be set as low as 0 and up to 24k.\n * - For other Gemini models, minimum is 128, maximum is 32k.\n * - Standard effort levels and their token mappings:\n * - \"none\" / \"minimal\": lowest allowed (minTokens)\n * - \"low\": 1k tokens\n * - \"medium\": 8k tokens\n * - \"high\": maximum allowed for the model (maxTokens)\n * - See reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} modelName\n * The Gemini model name (e.g., \"gemini-2.5-flash\"). Used to determine token range constraints.\n * @param {string | undefined} effort\n * The reasoning effort level: one of \"none\", \"minimal\", \"low\", \"medium\", \"high\"\n * (case-insensitive). If undefined, the function returns undefined.\n * @returns {number | undefined}\n * The corresponding token count for the given effort and model. Returns undefined if\n * the effort is not recognized or not provided.\n *\n * @example\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"medium\") // 8192\n * convertReasoningEffortToReasoningTokens(\"gemini-1.5-pro\", \"high\") // 32768\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"none\") // 0\n * convertReasoningEffortToReasoningTokens(undefined, \"low\") // 1024\n */\nexport function convertReasoningEffortToReasoningTokens(\n modelName?: string,\n effort?: string\n): number | undefined {\n if (effort === undefined) {\n return undefined;\n }\n\n // gemini-2.5-flash and -flash-lite can be disabled. Others can't.\n // https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n const minTokens: number = modelName?.startsWith(\"gemini-2.5-flash\") ? 0 : 128;\n const maxTokens: number = modelName?.startsWith(\"gemini-2.5-flash\")\n ? 24 * 1024\n : 32 * 1024;\n\n const normalizedEffort = effort.toLowerCase();\n switch (normalizedEffort) {\n case \"none\":\n case \"minimal\":\n return minTokens;\n case \"low\":\n // Defined as 1k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 1024;\n case \"medium\":\n // Defined as 8k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 8 * 1024;\n case \"high\":\n // Defined as 24k or 32k (model-dependent) by https://ai.google.dev/gemini-api/docs/openai#thinking\n return maxTokens;\n default:\n return undefined;\n }\n}\n\n/**\n * Converts a reasoning token budget (e.g., 0, 1024, 8192, 32768) to its corresponding\n * reasoning \"effort\" level, as used in the Google Gemini API (\"minimal\", \"low\", \"medium\", \"high\").\n *\n * This is the inverse of {@link convertReasoningEffortToReasoningTokens}. It is useful for\n * translating explicit numeric settings (often surfaced in raw API responses or configs)\n * into user-facing friendly reasoning levels/labels. The mapping relies on Google's\n * documented reasoning levels for different models:\n *\n * - -1 means default effort (per API documentation: this is \"minimal\" for flash-lite models\n * and \"high\" (dynamic) for others).\n * - 0 means \"minimal\" unless the model is \"gemini-3-pro\", for which it maps to \"low\".\n * - <= 1024 tokens: \"low\"\n * - <= 8192 tokens: \"medium\" (unless model is \"gemini-3-pro\", for which it reverts to \"low\")\n * - > 8192: \"high\"\n *\n * Reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} model\n * The Gemini model name (e.g., \"gemini-3-pro\"). Used for model-specific special cases.\n * @param {number | undefined} reasoningTokens\n * The token budget to convert. If undefined, the function returns undefined.\n * @returns {Lowercase<Gemini.ThinkingLevel> | undefined}\n * The reasoning \"effort\" level (\"minimal\", \"low\", \"medium\", \"high\") or undefined if input is not valid.\n *\n * @example\n * convertReasoningTokensToReasoningEffort(\"gemini-3-pro\", 0) // \"low\"\n * convertReasoningTokensToReasoningEffort(\"gemini-1.5-pro\", 0) // \"minimal\"\n * convertReasoningTokensToReasoningEffort(undefined, 1024) // \"low\"\n * convertReasoningTokensToReasoningEffort(undefined, -1) // \"high\"\n * convertReasoningTokensToReasoningEffort(undefined, 9000) // \"high\"\n */\nexport function convertReasoningTokensToReasoningEffort(\n model?: string,\n reasoningTokens?: number\n): Gemini.ThinkingLevel | undefined {\n if (typeof reasoningTokens === \"undefined\") {\n return undefined;\n } else if (reasoningTokens === -1) {\n // -1 means \"minimal\" for flash-lite models and \"high\"/dynamic for others\n // (\"default\" per https://ai.google.dev/gemini-api/docs/thinking#thinking-levels)\n if (model && getModelLevel(model) === \"flash-lite\") {\n return \"minimal\";\n }\n return \"high\";\n } else if (reasoningTokens === 0) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"minimal\";\n }\n } else if (reasoningTokens <= 1024) {\n return \"low\";\n } else if (reasoningTokens <= 8192) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"medium\";\n }\n } else {\n return \"high\";\n }\n}\n\n/**\n * Infers and returns the Google platform type from the provided chat model parameters.\n *\n * This function helps determine which Google platform (e.g., Vertex AI on GCP or the public API)\n * should be used for calling Gemini/PaLM endpoints, based on the options provided in\n * {@link BaseChatGoogleParams} (such as model constructors or call options).\n */\nexport function convertParamsToPlatformType(params: {\n platformType?: GooglePlatformType;\n vertexai?: boolean;\n}): GooglePlatformType | undefined {\n if (typeof params === \"undefined\") {\n return undefined;\n }\n if (typeof params.platformType !== \"undefined\") {\n return params.platformType;\n }\n if (params.vertexai === true) {\n return \"gcp\";\n }\n return undefined;\n}\n\n/**\n * Returns the model family from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"gemini\"\n */\nexport function getModelFamily(model: string): string {\n return model.split(\"-\")[0];\n}\n\n/**\n * Returns the model version from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"2.5\"\n */\nexport function getModelVersion(model: string): string {\n return model.split(\"-\")[1];\n}\n\n/**\n * Returns the model level from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"flash\", \"gemini-2.5-flash-lite\" → \"flash-lite\"\n */\nexport function getModelLevel(model: string): string {\n const parts = model.split(\"-\");\n let ret = parts[2];\n if (ret === \"flash\" && parts[3] === \"lite\") {\n ret = \"flash-lite\";\n }\n return ret;\n}\n\n/**\n * Returns the model specialty from a Gemini model name.\n * e.g. \"gemini-2.5-flash-preview-image\" → \"image\",\n * \"gemini-2.5-flash-preview-tts\" → \"tts\",\n * \"gemini-2.5-flash\" → \"\"\n */\nexport function getModelSpecialty(model: string): string {\n if (model.includes(\"image\")) {\n return \"image\";\n } else if (model.includes(\"tts\")) {\n return \"tts\";\n } else {\n return \"\";\n }\n}\n\n/**\n * Builds the `thinkingConfig` object for the Gemini `generationConfig`,\n * translating user-facing reasoning fields into the wire format.\n *\n * @param model The full Gemini model name (e.g. \"gemini-2.5-flash\").\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.ThinkingConfig}, or `{}`\n * when thinking should not be explicitly configured.\n */\nexport function convertFieldsToThinkingConfig(\n model: string,\n fields: ChatGoogleFields\n): Gemini.ThinkingConfig | undefined {\n // Thinking / reasoning\n let includeThoughts = true;\n\n let thinkingBudget =\n fields?.maxReasoningTokens ??\n fields?.thinkingBudget ??\n convertReasoningEffortToReasoningTokens(\n model,\n fields?.reasoningEffort ?? fields?.thinkingLevel\n );\n if (\n thinkingBudget === 0 ||\n (model.includes(\"pro\") && thinkingBudget === 128)\n ) {\n includeThoughts = false;\n }\n if (\n model.startsWith(\"gemini-2.5-pro\") &&\n typeof thinkingBudget !== \"undefined\"\n ) {\n // Can't turn off Gemini 2.5 Pro thinking completely\n if (thinkingBudget >= 0 && thinkingBudget < 128) {\n thinkingBudget = 128;\n }\n }\n\n const thinkingLevelRaw =\n fields?.reasoningEffort ??\n fields?.thinkingLevel ??\n convertReasoningTokensToReasoningEffort(\n model,\n fields?.maxReasoningTokens ?? fields?.thinkingBudget\n );\n let thinkingLevel = thinkingLevelRaw?.toUpperCase();\n if (thinkingLevel === \"MINIMAL\") {\n includeThoughts = false;\n }\n if (model.startsWith(\"gemini-3-pro\")) {\n // Gemini 3 Pro has only low and high.\n if (thinkingLevel === \"MINIMAL\") {\n thinkingLevel = \"LOW\";\n } else if (thinkingLevel === \"MEDIUM\") {\n thinkingLevel = \"HIGH\";\n }\n } else if (model.startsWith(\"gemini-3.1-pro\")) {\n // Gemini 3.1 Pro does not have \"minimal\"\n if (thinkingLevel === \"MINIMAL\") {\n thinkingLevel = \"LOW\";\n }\n }\n\n // If we are using a model that doesn't support thinking at all (gemini 2.5 imaging)\n // or we haven't explicitly tried to set a thinking budget/level, then bail out.\n const modelVersion = getModelVersion(model);\n const modelSpecialty = getModelSpecialty(model);\n const thoughtsNotSupported =\n modelVersion === \"2.5\" && modelSpecialty === \"image\";\n if (\n thoughtsNotSupported ||\n typeof thinkingBudget === \"undefined\" ||\n typeof thinkingLevel === \"undefined\"\n ) {\n return undefined;\n }\n\n // If we have gotten this far, then we want to explicitly set if we include thoughts or not.\n const thinkingConfig: Gemini.ThinkingConfig = {\n includeThoughts,\n };\n\n // Explicitly setting the budget/level is only valid (currently) for text models.\n if (modelSpecialty === \"\") {\n if (model.startsWith(\"gemini-2.5\")) {\n thinkingConfig.thinkingBudget = thinkingBudget;\n } else {\n thinkingConfig.thinkingLevel = thinkingLevel as Gemini.ThinkingLevel;\n }\n }\n\n return thinkingConfig;\n}\n\n/**\n * Builds the `speechConfig` object for the Gemini `generationConfig`,\n * normalising the user-friendly simplified forms into the wire format.\n *\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.SpeechConfig}, or `{}`\n * when no speech config was provided.\n */\nexport function convertFieldsToSpeechConfig(\n fields: ChatGoogleFields\n): Gemini.SpeechConfig | undefined {\n const config: Gemini.SpeechConfig | SimplifiedSpeechConfig | undefined =\n fields.speechConfig;\n if (typeof config === \"undefined\") {\n return undefined;\n }\n\n function isSpeechConfig(\n config: Gemini.SpeechConfig | SimplifiedSpeechConfig\n ): config is Gemini.SpeechConfig {\n return (\n typeof config === \"object\" &&\n (Object.hasOwn(config, \"voiceConfig\") ||\n Object.hasOwn(config, \"multiSpeakerVoiceConfig\"))\n );\n }\n\n function hasLanguage(\n config: SimplifiedSpeechConfig\n ): config is SimplifiedSpeechLanguageConfig {\n return typeof config === \"object\" && Object.hasOwn(config, \"languageCode\");\n }\n\n function hasVoice(\n config: SimplifiedSpeechLanguageConfig\n ): config is SpeechVoiceLanguage {\n return Object.hasOwn(config, \"voice\");\n }\n\n // If this is already a SpeechConfig, just return it\n if (isSpeechConfig(config)) {\n return config;\n }\n\n let languageCode: string | undefined;\n let voice: SpeechVoice;\n if (hasLanguage(config)) {\n languageCode = config.languageCode;\n voice = hasVoice(config) ? config.voice : config.voices;\n } else {\n languageCode = undefined;\n voice = config;\n }\n\n let ret: Gemini.SpeechConfig;\n\n if (typeof voice === \"string\") {\n // They just provided the prebuilt voice configuration name. Use it.\n ret = {\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: voice,\n },\n },\n };\n } else {\n // This is multi-speaker, so we have speaker/name pairs\n // If we have just one (why?), turn it into an array for the moment\n const voices: SpeechSpeakerName[] = Array.isArray(voice) ? voice : [voice];\n // Go through all the speaker/name pairs and turn this into the voice config array\n const speakerVoiceConfigs: Gemini.SpeakerVoiceConfig[] = voices.map(\n (v: SpeechSpeakerName): Gemini.SpeakerVoiceConfig => ({\n speaker: v.speaker,\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: v.name,\n },\n },\n })\n );\n // Create the multi-speaker voice configuration\n ret = {\n multiSpeakerVoiceConfig: {\n speakerVoiceConfigs,\n },\n };\n }\n\n if (languageCode) {\n ret.languageCode = languageCode;\n }\n\n return ret;\n}\n\ntype ServiceTierResponse = {} | { serviceTier: Gemini.ServiceTier };\n\n/**\n * Builds an object that can be included as part of the request\n * which will add a `speechConfig` attribute if appropriate.\n */\nexport function convertFieldsToServiceTier(\n platform: GooglePlatformType,\n fields: ChatGoogleFields\n): ServiceTierResponse {\n // Service tier is handled in a different way on GCP\n if (platform === \"gcp\") {\n return {};\n }\n\n const serviceTier = fields.serviceTier;\n if (!serviceTier || !settableServiceTier.includes(serviceTier)) {\n return {};\n }\n\n return {\n serviceTier: serviceTier,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"params.cjs","names":["settableServiceTier"],"sources":["../../src/converters/params.ts"],"sourcesContent":["import {\n ChatGoogleFields,\n GooglePlatformType,\n settableServiceTier,\n SimplifiedSpeechConfig,\n SimplifiedSpeechLanguageConfig,\n SpeechSpeakerName,\n SpeechVoice,\n SpeechVoiceLanguage,\n} from \"../chat_models/types.js\";\nimport type { Gemini } from \"../chat_models/api-types.js\";\n\n/**\n * Converts a reasoning \"effort\" level value (e.g., \"minimal\", \"low\", \"medium\", \"high\")\n * to a corresponding reasoning token budget, depending on the target Gemini model.\n *\n * This mapping is essential when configuring Google Gemini API calls with reasoning\n * effort/level fields (as in the Gemini OpenAI-compatible API) or when translating\n * user-friendly configuration to explicit token budgets.\n *\n * - The mapping is refined to reflect the constraints documented by Google:\n * - For \"gemini-2.5-flash\" models, token budgets may be set as low as 0 and up to 24k.\n * - For other Gemini models, minimum is 128, maximum is 32k.\n * - Standard effort levels and their token mappings:\n * - \"none\" / \"minimal\": lowest allowed (minTokens)\n * - \"low\": 1k tokens\n * - \"medium\": 8k tokens\n * - \"high\": maximum allowed for the model (maxTokens)\n * - See reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} modelName\n * The Gemini model name (e.g., \"gemini-2.5-flash\"). Used to determine token range constraints.\n * @param {string | undefined} effort\n * The reasoning effort level: one of \"none\", \"minimal\", \"low\", \"medium\", \"high\"\n * (case-insensitive). If undefined, the function returns undefined.\n * @returns {number | undefined}\n * The corresponding token count for the given effort and model. Returns undefined if\n * the effort is not recognized or not provided.\n *\n * @example\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"medium\") // 8192\n * convertReasoningEffortToReasoningTokens(\"gemini-1.5-pro\", \"high\") // 32768\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"none\") // 0\n * convertReasoningEffortToReasoningTokens(undefined, \"low\") // 1024\n */\nexport function convertReasoningEffortToReasoningTokens(\n modelName?: string,\n effort?: string\n): number | undefined {\n if (effort === undefined) {\n return undefined;\n }\n\n // gemini-2.5-flash and -flash-lite can be disabled. Others can't.\n // https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n const minTokens: number = modelName?.startsWith(\"gemini-2.5-flash\") ? 0 : 128;\n const maxTokens: number = modelName?.startsWith(\"gemini-2.5-flash\")\n ? 24 * 1024\n : 32 * 1024;\n\n const normalizedEffort = effort.toLowerCase();\n switch (normalizedEffort) {\n case \"none\":\n case \"minimal\":\n return minTokens;\n case \"low\":\n // Defined as 1k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 1024;\n case \"medium\":\n // Defined as 8k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 8 * 1024;\n case \"high\":\n // Defined as 24k or 32k (model-dependent) by https://ai.google.dev/gemini-api/docs/openai#thinking\n return maxTokens;\n default:\n return undefined;\n }\n}\n\n/**\n * Converts a reasoning token budget (e.g., 0, 1024, 8192, 32768) to its corresponding\n * reasoning \"effort\" level, as used in the Google Gemini API (\"minimal\", \"low\", \"medium\", \"high\").\n *\n * This is the inverse of {@link convertReasoningEffortToReasoningTokens}. It is useful for\n * translating explicit numeric settings (often surfaced in raw API responses or configs)\n * into user-facing friendly reasoning levels/labels. The mapping relies on Google's\n * documented reasoning levels for different models:\n *\n * - -1 means default effort (per API documentation: this is \"minimal\" for flash-lite models\n * and \"high\" (dynamic) for others).\n * - 0 means \"minimal\" unless the model is \"gemini-3-pro\", for which it maps to \"low\".\n * - <= 1024 tokens: \"low\"\n * - <= 8192 tokens: \"medium\" (unless model is \"gemini-3-pro\", for which it reverts to \"low\")\n * - > 8192: \"high\"\n *\n * Reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} model\n * The Gemini model name (e.g., \"gemini-3-pro\"). Used for model-specific special cases.\n * @param {number | undefined} reasoningTokens\n * The token budget to convert. If undefined, the function returns undefined.\n * @returns {Lowercase<Gemini.ThinkingLevel> | undefined}\n * The reasoning \"effort\" level (\"minimal\", \"low\", \"medium\", \"high\") or undefined if input is not valid.\n *\n * @example\n * convertReasoningTokensToReasoningEffort(\"gemini-3-pro\", 0) // \"low\"\n * convertReasoningTokensToReasoningEffort(\"gemini-1.5-pro\", 0) // \"minimal\"\n * convertReasoningTokensToReasoningEffort(undefined, 1024) // \"low\"\n * convertReasoningTokensToReasoningEffort(undefined, -1) // \"high\"\n * convertReasoningTokensToReasoningEffort(undefined, 9000) // \"high\"\n */\nexport function convertReasoningTokensToReasoningEffort(\n model?: string,\n reasoningTokens?: number\n): Gemini.ThinkingLevel | undefined {\n if (typeof reasoningTokens === \"undefined\") {\n return undefined;\n } else if (reasoningTokens === -1) {\n // -1 means \"minimal\" for flash-lite models and \"high\"/dynamic for others\n // (\"default\" per https://ai.google.dev/gemini-api/docs/thinking#thinking-levels)\n if (model && getModelLevel(model) === \"flash-lite\") {\n return \"minimal\";\n }\n return \"high\";\n } else if (reasoningTokens === 0) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"minimal\";\n }\n } else if (reasoningTokens <= 1024) {\n return \"low\";\n } else if (reasoningTokens <= 8192) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"medium\";\n }\n } else {\n return \"high\";\n }\n}\n\n/**\n * Infers and returns the Google platform type from the provided chat model parameters.\n *\n * This function helps determine which Google platform (e.g., Vertex AI on GCP or the public API)\n * should be used for calling Gemini/PaLM endpoints, based on the options provided in\n * {@link BaseChatGoogleParams} (such as model constructors or call options).\n */\nexport function convertParamsToPlatformType(params: {\n platformType?: GooglePlatformType;\n vertexai?: boolean;\n}): GooglePlatformType | undefined {\n if (typeof params === \"undefined\") {\n return undefined;\n }\n if (typeof params.platformType !== \"undefined\") {\n return params.platformType;\n }\n if (params.vertexai === true) {\n return \"gcp\";\n }\n return undefined;\n}\n\n/**\n * Returns the model family from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"gemini\"\n */\nexport function getModelFamily(model: string): string {\n return model.split(\"-\")[0];\n}\n\n/**\n * Returns the model version from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"2.5\"\n */\nexport function getModelVersion(model: string): string {\n return model.split(\"-\")[1];\n}\n\n/**\n * Returns the model level from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"flash\", \"gemini-2.5-flash-lite\" → \"flash-lite\"\n */\nexport function getModelLevel(model: string): string {\n const parts = model.split(\"-\");\n let ret = parts[2];\n if (ret === \"flash\" && parts[3] === \"lite\") {\n ret = \"flash-lite\";\n }\n return ret;\n}\n\n/**\n * Returns the model specialty from a Gemini model name.\n * e.g. \"gemini-2.5-flash-preview-image\" → \"image\",\n * \"gemini-2.5-flash-preview-tts\" → \"tts\",\n * \"gemini-2.5-flash\" → \"\"\n */\nexport function getModelSpecialty(model: string): string {\n if (model.includes(\"image\")) {\n return \"image\";\n } else if (model.includes(\"tts\")) {\n return \"tts\";\n } else {\n return \"\";\n }\n}\n\n/**\n * Builds the `thinkingConfig` object for the Gemini `generationConfig`,\n * translating user-facing reasoning fields into the wire format.\n *\n * @param model The full Gemini model name (e.g. \"gemini-2.5-flash\").\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.ThinkingConfig}, or `{}`\n * when thinking should not be explicitly configured.\n */\nexport function convertFieldsToThinkingConfig(\n model: string,\n fields: ChatGoogleFields\n): Gemini.ThinkingConfig | undefined {\n // Thinking / reasoning\n let includeThoughts = true;\n\n let thinkingBudget =\n fields?.maxReasoningTokens ??\n fields?.thinkingBudget ??\n convertReasoningEffortToReasoningTokens(\n model,\n fields?.reasoningEffort ?? fields?.thinkingLevel\n );\n if (\n thinkingBudget === 0 ||\n (model.includes(\"pro\") && thinkingBudget === 128)\n ) {\n includeThoughts = false;\n }\n if (\n model.startsWith(\"gemini-2.5-pro\") &&\n typeof thinkingBudget !== \"undefined\"\n ) {\n // Can't turn off Gemini 2.5 Pro thinking completely\n if (thinkingBudget >= 0 && thinkingBudget < 128) {\n thinkingBudget = 128;\n }\n }\n\n const thinkingLevelRaw =\n fields?.reasoningEffort ??\n fields?.thinkingLevel ??\n convertReasoningTokensToReasoningEffort(\n model,\n fields?.maxReasoningTokens ?? fields?.thinkingBudget\n );\n let thinkingLevel = thinkingLevelRaw?.toUpperCase();\n if (thinkingLevel === \"MINIMAL\") {\n includeThoughts = false;\n }\n if (model.startsWith(\"gemini-3-pro\")) {\n // Gemini 3 Pro has only low and high.\n if (thinkingLevel === \"MINIMAL\") {\n thinkingLevel = \"LOW\";\n } else if (thinkingLevel === \"MEDIUM\") {\n thinkingLevel = \"HIGH\";\n }\n } else if (model.startsWith(\"gemini-3.1-pro\")) {\n // Gemini 3.1 Pro does not have \"minimal\"\n if (thinkingLevel === \"MINIMAL\") {\n thinkingLevel = \"LOW\";\n }\n }\n\n // If we are using a model that doesn't support thinking at all (gemini 2.5 imaging)\n // or we haven't explicitly tried to set a thinking budget/level, then bail out.\n const modelVersion = getModelVersion(model);\n const modelSpecialty = getModelSpecialty(model);\n const thoughtsNotSupported =\n modelVersion === \"2.5\" && modelSpecialty === \"image\";\n if (\n thoughtsNotSupported ||\n typeof thinkingBudget === \"undefined\" ||\n typeof thinkingLevel === \"undefined\"\n ) {\n return undefined;\n }\n\n // If we have gotten this far, then we want to explicitly set if we include thoughts or not.\n const thinkingConfig: Gemini.ThinkingConfig = {\n includeThoughts,\n };\n\n // Explicitly setting the budget/level is only valid (currently) for text models.\n if (modelSpecialty === \"\") {\n if (model.startsWith(\"gemini-2.5\")) {\n thinkingConfig.thinkingBudget = thinkingBudget;\n } else {\n thinkingConfig.thinkingLevel = thinkingLevel as Gemini.ThinkingLevel;\n }\n }\n\n return thinkingConfig;\n}\n\n/**\n * Builds the `speechConfig` object for the Gemini `generationConfig`,\n * normalising the user-friendly simplified forms into the wire format.\n *\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.SpeechConfig}, or `{}`\n * when no speech config was provided.\n */\nexport function convertFieldsToSpeechConfig(\n fields: ChatGoogleFields\n): Gemini.SpeechConfig | undefined {\n const config: Gemini.SpeechConfig | SimplifiedSpeechConfig | undefined =\n fields.speechConfig;\n if (typeof config === \"undefined\") {\n return undefined;\n }\n\n function isSpeechConfig(\n config: Gemini.SpeechConfig | SimplifiedSpeechConfig\n ): config is Gemini.SpeechConfig {\n return (\n typeof config === \"object\" &&\n (Object.hasOwn(config, \"voiceConfig\") ||\n Object.hasOwn(config, \"multiSpeakerVoiceConfig\"))\n );\n }\n\n function hasLanguage(\n config: SimplifiedSpeechConfig\n ): config is SimplifiedSpeechLanguageConfig {\n return typeof config === \"object\" && Object.hasOwn(config, \"languageCode\");\n }\n\n function hasVoice(\n config: SimplifiedSpeechLanguageConfig\n ): config is SpeechVoiceLanguage {\n return Object.hasOwn(config, \"voice\");\n }\n\n // If this is already a SpeechConfig, just return it\n if (isSpeechConfig(config)) {\n return config;\n }\n\n let languageCode: string | undefined;\n let voice: SpeechVoice;\n if (hasLanguage(config)) {\n languageCode = config.languageCode;\n voice = hasVoice(config) ? config.voice : config.voices;\n } else {\n languageCode = undefined;\n voice = config;\n }\n\n let ret: Gemini.SpeechConfig;\n\n if (typeof voice === \"string\") {\n // They just provided the prebuilt voice configuration name. Use it.\n ret = {\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: voice,\n },\n },\n };\n } else {\n // This is multi-speaker, so we have speaker/name pairs\n // If we have just one (why?), turn it into an array for the moment\n const voices: SpeechSpeakerName[] = Array.isArray(voice) ? voice : [voice];\n // Go through all the speaker/name pairs and turn this into the voice config array\n const speakerVoiceConfigs: Gemini.SpeakerVoiceConfig[] = voices.map(\n (v: SpeechSpeakerName): Gemini.SpeakerVoiceConfig => ({\n speaker: v.speaker,\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: v.name,\n },\n },\n })\n );\n // Create the multi-speaker voice configuration\n ret = {\n multiSpeakerVoiceConfig: {\n speakerVoiceConfigs,\n },\n };\n }\n\n if (languageCode) {\n ret.languageCode = languageCode;\n }\n\n return ret;\n}\n\ntype ServiceTierResponse = {} | { serviceTier: Gemini.ServiceTier };\n\n/**\n * Builds an object that can be included as part of the request\n * which will add a `speechConfig` attribute if appropriate.\n */\nexport function convertFieldsToServiceTier(\n platform: GooglePlatformType,\n fields: ChatGoogleFields\n): ServiceTierResponse {\n // Service tier is handled in a different way on GCP\n if (platform === \"gcp\") {\n return {};\n }\n\n const serviceTier = fields.serviceTier;\n if (!serviceTier || !settableServiceTier.includes(serviceTier)) {\n return {};\n }\n\n return {\n serviceTier: serviceTier,\n };\n}\n"],"mappings":"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"}
@@ -38,12 +38,12 @@ import { settableServiceTier } from "../chat_models/types.js";
38
38
  function convertReasoningEffortToReasoningTokens(modelName, effort) {
39
39
  if (effort === void 0) return;
40
40
  const minTokens = modelName?.startsWith("gemini-2.5-flash") ? 0 : 128;
41
- const maxTokens = modelName?.startsWith("gemini-2.5-flash") ? 24 * 1024 : 32 * 1024;
41
+ const maxTokens = modelName?.startsWith("gemini-2.5-flash") ? 24576 : 32768;
42
42
  switch (effort.toLowerCase()) {
43
43
  case "none":
44
44
  case "minimal": return minTokens;
45
45
  case "low": return 1024;
46
- case "medium": return 8 * 1024;
46
+ case "medium": return 8192;
47
47
  case "high": return maxTokens;
48
48
  default: return;
49
49
  }
@@ -87,12 +87,14 @@ function convertReasoningTokensToReasoningEffort(model, reasoningTokens) {
87
87
  else if (reasoningTokens === -1) {
88
88
  if (model && getModelLevel(model) === "flash-lite") return "minimal";
89
89
  return "high";
90
- } else if (reasoningTokens === 0) if (model?.startsWith("gemini-3-pro")) return "low";
91
- else return "minimal";
92
- else if (reasoningTokens <= 1024) return "low";
93
- else if (reasoningTokens <= 8192) if (model?.startsWith("gemini-3-pro")) return "low";
94
- else return "medium";
95
- else return "high";
90
+ } else if (reasoningTokens === 0) {
91
+ if (model?.startsWith("gemini-3-pro")) return "low";
92
+ else return "minimal";
93
+ } else if (reasoningTokens <= 1024) return "low";
94
+ else if (reasoningTokens <= 8192) {
95
+ if (model?.startsWith("gemini-3-pro")) return "low";
96
+ else return "medium";
97
+ } else return "high";
96
98
  }
97
99
  /**
98
100
  * Infers and returns the Google platform type from the provided chat model parameters.
@@ -162,8 +164,10 @@ function convertFieldsToThinkingConfig(model, fields) {
162
164
  const modelSpecialty = getModelSpecialty(model);
163
165
  if (modelVersion === "2.5" && modelSpecialty === "image" || typeof thinkingBudget === "undefined" || typeof thinkingLevel === "undefined") return;
164
166
  const thinkingConfig = { includeThoughts };
165
- if (modelSpecialty === "") if (model.startsWith("gemini-2.5")) thinkingConfig.thinkingBudget = thinkingBudget;
166
- else thinkingConfig.thinkingLevel = thinkingLevel;
167
+ if (modelSpecialty === "") {
168
+ if (model.startsWith("gemini-2.5")) thinkingConfig.thinkingBudget = thinkingBudget;
169
+ else thinkingConfig.thinkingLevel = thinkingLevel;
170
+ }
167
171
  return thinkingConfig;
168
172
  }
169
173
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"params.js","names":[],"sources":["../../src/converters/params.ts"],"sourcesContent":["import {\n ChatGoogleFields,\n GooglePlatformType,\n settableServiceTier,\n SimplifiedSpeechConfig,\n SimplifiedSpeechLanguageConfig,\n SpeechSpeakerName,\n SpeechVoice,\n SpeechVoiceLanguage,\n} from \"../chat_models/types.js\";\nimport type { Gemini } from \"../chat_models/api-types.js\";\n\n/**\n * Converts a reasoning \"effort\" level value (e.g., \"minimal\", \"low\", \"medium\", \"high\")\n * to a corresponding reasoning token budget, depending on the target Gemini model.\n *\n * This mapping is essential when configuring Google Gemini API calls with reasoning\n * effort/level fields (as in the Gemini OpenAI-compatible API) or when translating\n * user-friendly configuration to explicit token budgets.\n *\n * - The mapping is refined to reflect the constraints documented by Google:\n * - For \"gemini-2.5-flash\" models, token budgets may be set as low as 0 and up to 24k.\n * - For other Gemini models, minimum is 128, maximum is 32k.\n * - Standard effort levels and their token mappings:\n * - \"none\" / \"minimal\": lowest allowed (minTokens)\n * - \"low\": 1k tokens\n * - \"medium\": 8k tokens\n * - \"high\": maximum allowed for the model (maxTokens)\n * - See reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} modelName\n * The Gemini model name (e.g., \"gemini-2.5-flash\"). Used to determine token range constraints.\n * @param {string | undefined} effort\n * The reasoning effort level: one of \"none\", \"minimal\", \"low\", \"medium\", \"high\"\n * (case-insensitive). If undefined, the function returns undefined.\n * @returns {number | undefined}\n * The corresponding token count for the given effort and model. Returns undefined if\n * the effort is not recognized or not provided.\n *\n * @example\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"medium\") // 8192\n * convertReasoningEffortToReasoningTokens(\"gemini-1.5-pro\", \"high\") // 32768\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"none\") // 0\n * convertReasoningEffortToReasoningTokens(undefined, \"low\") // 1024\n */\nexport function convertReasoningEffortToReasoningTokens(\n modelName?: string,\n effort?: string\n): number | undefined {\n if (effort === undefined) {\n return undefined;\n }\n\n // gemini-2.5-flash and -flash-lite can be disabled. Others can't.\n // https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n const minTokens: number = modelName?.startsWith(\"gemini-2.5-flash\") ? 0 : 128;\n const maxTokens: number = modelName?.startsWith(\"gemini-2.5-flash\")\n ? 24 * 1024\n : 32 * 1024;\n\n const normalizedEffort = effort.toLowerCase();\n switch (normalizedEffort) {\n case \"none\":\n case \"minimal\":\n return minTokens;\n case \"low\":\n // Defined as 1k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 1024;\n case \"medium\":\n // Defined as 8k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 8 * 1024;\n case \"high\":\n // Defined as 24k or 32k (model-dependent) by https://ai.google.dev/gemini-api/docs/openai#thinking\n return maxTokens;\n default:\n return undefined;\n }\n}\n\n/**\n * Converts a reasoning token budget (e.g., 0, 1024, 8192, 32768) to its corresponding\n * reasoning \"effort\" level, as used in the Google Gemini API (\"minimal\", \"low\", \"medium\", \"high\").\n *\n * This is the inverse of {@link convertReasoningEffortToReasoningTokens}. It is useful for\n * translating explicit numeric settings (often surfaced in raw API responses or configs)\n * into user-facing friendly reasoning levels/labels. The mapping relies on Google's\n * documented reasoning levels for different models:\n *\n * - -1 means default effort (per API documentation: this is \"minimal\" for flash-lite models\n * and \"high\" (dynamic) for others).\n * - 0 means \"minimal\" unless the model is \"gemini-3-pro\", for which it maps to \"low\".\n * - <= 1024 tokens: \"low\"\n * - <= 8192 tokens: \"medium\" (unless model is \"gemini-3-pro\", for which it reverts to \"low\")\n * - > 8192: \"high\"\n *\n * Reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} model\n * The Gemini model name (e.g., \"gemini-3-pro\"). Used for model-specific special cases.\n * @param {number | undefined} reasoningTokens\n * The token budget to convert. If undefined, the function returns undefined.\n * @returns {Lowercase<Gemini.ThinkingLevel> | undefined}\n * The reasoning \"effort\" level (\"minimal\", \"low\", \"medium\", \"high\") or undefined if input is not valid.\n *\n * @example\n * convertReasoningTokensToReasoningEffort(\"gemini-3-pro\", 0) // \"low\"\n * convertReasoningTokensToReasoningEffort(\"gemini-1.5-pro\", 0) // \"minimal\"\n * convertReasoningTokensToReasoningEffort(undefined, 1024) // \"low\"\n * convertReasoningTokensToReasoningEffort(undefined, -1) // \"high\"\n * convertReasoningTokensToReasoningEffort(undefined, 9000) // \"high\"\n */\nexport function convertReasoningTokensToReasoningEffort(\n model?: string,\n reasoningTokens?: number\n): Gemini.ThinkingLevel | undefined {\n if (typeof reasoningTokens === \"undefined\") {\n return undefined;\n } else if (reasoningTokens === -1) {\n // -1 means \"minimal\" for flash-lite models and \"high\"/dynamic for others\n // (\"default\" per https://ai.google.dev/gemini-api/docs/thinking#thinking-levels)\n if (model && getModelLevel(model) === \"flash-lite\") {\n return \"minimal\";\n }\n return \"high\";\n } else if (reasoningTokens === 0) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"minimal\";\n }\n } else if (reasoningTokens <= 1024) {\n return \"low\";\n } else if (reasoningTokens <= 8192) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"medium\";\n }\n } else {\n return \"high\";\n }\n}\n\n/**\n * Infers and returns the Google platform type from the provided chat model parameters.\n *\n * This function helps determine which Google platform (e.g., Vertex AI on GCP or the public API)\n * should be used for calling Gemini/PaLM endpoints, based on the options provided in\n * {@link BaseChatGoogleParams} (such as model constructors or call options).\n */\nexport function convertParamsToPlatformType(params: {\n platformType?: GooglePlatformType;\n vertexai?: boolean;\n}): GooglePlatformType | undefined {\n if (typeof params === \"undefined\") {\n return undefined;\n }\n if (typeof params.platformType !== \"undefined\") {\n return params.platformType;\n }\n if (params.vertexai === true) {\n return \"gcp\";\n }\n return undefined;\n}\n\n/**\n * Returns the model family from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"gemini\"\n */\nexport function getModelFamily(model: string): string {\n return model.split(\"-\")[0];\n}\n\n/**\n * Returns the model version from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"2.5\"\n */\nexport function getModelVersion(model: string): string {\n return model.split(\"-\")[1];\n}\n\n/**\n * Returns the model level from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"flash\", \"gemini-2.5-flash-lite\" → \"flash-lite\"\n */\nexport function getModelLevel(model: string): string {\n const parts = model.split(\"-\");\n let ret = parts[2];\n if (ret === \"flash\" && parts[3] === \"lite\") {\n ret = \"flash-lite\";\n }\n return ret;\n}\n\n/**\n * Returns the model specialty from a Gemini model name.\n * e.g. \"gemini-2.5-flash-preview-image\" → \"image\",\n * \"gemini-2.5-flash-preview-tts\" → \"tts\",\n * \"gemini-2.5-flash\" → \"\"\n */\nexport function getModelSpecialty(model: string): string {\n if (model.includes(\"image\")) {\n return \"image\";\n } else if (model.includes(\"tts\")) {\n return \"tts\";\n } else {\n return \"\";\n }\n}\n\n/**\n * Builds the `thinkingConfig` object for the Gemini `generationConfig`,\n * translating user-facing reasoning fields into the wire format.\n *\n * @param model The full Gemini model name (e.g. \"gemini-2.5-flash\").\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.ThinkingConfig}, or `{}`\n * when thinking should not be explicitly configured.\n */\nexport function convertFieldsToThinkingConfig(\n model: string,\n fields: ChatGoogleFields\n): Gemini.ThinkingConfig | undefined {\n // Thinking / reasoning\n let includeThoughts = true;\n\n let thinkingBudget =\n fields?.maxReasoningTokens ??\n fields?.thinkingBudget ??\n convertReasoningEffortToReasoningTokens(\n model,\n fields?.reasoningEffort ?? fields?.thinkingLevel\n );\n if (\n thinkingBudget === 0 ||\n (model.includes(\"pro\") && thinkingBudget === 128)\n ) {\n includeThoughts = false;\n }\n if (\n model.startsWith(\"gemini-2.5-pro\") &&\n typeof thinkingBudget !== \"undefined\"\n ) {\n // Can't turn off Gemini 2.5 Pro thinking completely\n if (thinkingBudget >= 0 && thinkingBudget < 128) {\n thinkingBudget = 128;\n }\n }\n\n const thinkingLevelRaw =\n fields?.reasoningEffort ??\n fields?.thinkingLevel ??\n convertReasoningTokensToReasoningEffort(\n model,\n fields?.maxReasoningTokens ?? fields?.thinkingBudget\n );\n let thinkingLevel = thinkingLevelRaw?.toUpperCase();\n if (thinkingLevel === \"MINIMAL\") {\n includeThoughts = false;\n }\n if (model.startsWith(\"gemini-3-pro\")) {\n // Gemini 3 Pro has only low and high.\n if (thinkingLevel === \"MINIMAL\") {\n thinkingLevel = \"LOW\";\n } else if (thinkingLevel === \"MEDIUM\") {\n thinkingLevel = \"HIGH\";\n }\n } else if (model.startsWith(\"gemini-3.1-pro\")) {\n // Gemini 3.1 Pro does not have \"minimal\"\n if (thinkingLevel === \"MINIMAL\") {\n thinkingLevel = \"LOW\";\n }\n }\n\n // If we are using a model that doesn't support thinking at all (gemini 2.5 imaging)\n // or we haven't explicitly tried to set a thinking budget/level, then bail out.\n const modelVersion = getModelVersion(model);\n const modelSpecialty = getModelSpecialty(model);\n const thoughtsNotSupported =\n modelVersion === \"2.5\" && modelSpecialty === \"image\";\n if (\n thoughtsNotSupported ||\n typeof thinkingBudget === \"undefined\" ||\n typeof thinkingLevel === \"undefined\"\n ) {\n return undefined;\n }\n\n // If we have gotten this far, then we want to explicitly set if we include thoughts or not.\n const thinkingConfig: Gemini.ThinkingConfig = {\n includeThoughts,\n };\n\n // Explicitly setting the budget/level is only valid (currently) for text models.\n if (modelSpecialty === \"\") {\n if (model.startsWith(\"gemini-2.5\")) {\n thinkingConfig.thinkingBudget = thinkingBudget;\n } else {\n thinkingConfig.thinkingLevel = thinkingLevel as Gemini.ThinkingLevel;\n }\n }\n\n return thinkingConfig;\n}\n\n/**\n * Builds the `speechConfig` object for the Gemini `generationConfig`,\n * normalising the user-friendly simplified forms into the wire format.\n *\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.SpeechConfig}, or `{}`\n * when no speech config was provided.\n */\nexport function convertFieldsToSpeechConfig(\n fields: ChatGoogleFields\n): Gemini.SpeechConfig | undefined {\n const config: Gemini.SpeechConfig | SimplifiedSpeechConfig | undefined =\n fields.speechConfig;\n if (typeof config === \"undefined\") {\n return undefined;\n }\n\n function isSpeechConfig(\n config: Gemini.SpeechConfig | SimplifiedSpeechConfig\n ): config is Gemini.SpeechConfig {\n return (\n typeof config === \"object\" &&\n (Object.hasOwn(config, \"voiceConfig\") ||\n Object.hasOwn(config, \"multiSpeakerVoiceConfig\"))\n );\n }\n\n function hasLanguage(\n config: SimplifiedSpeechConfig\n ): config is SimplifiedSpeechLanguageConfig {\n return typeof config === \"object\" && Object.hasOwn(config, \"languageCode\");\n }\n\n function hasVoice(\n config: SimplifiedSpeechLanguageConfig\n ): config is SpeechVoiceLanguage {\n return Object.hasOwn(config, \"voice\");\n }\n\n // If this is already a SpeechConfig, just return it\n if (isSpeechConfig(config)) {\n return config;\n }\n\n let languageCode: string | undefined;\n let voice: SpeechVoice;\n if (hasLanguage(config)) {\n languageCode = config.languageCode;\n voice = hasVoice(config) ? config.voice : config.voices;\n } else {\n languageCode = undefined;\n voice = config;\n }\n\n let ret: Gemini.SpeechConfig;\n\n if (typeof voice === \"string\") {\n // They just provided the prebuilt voice configuration name. Use it.\n ret = {\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: voice,\n },\n },\n };\n } else {\n // This is multi-speaker, so we have speaker/name pairs\n // If we have just one (why?), turn it into an array for the moment\n const voices: SpeechSpeakerName[] = Array.isArray(voice) ? voice : [voice];\n // Go through all the speaker/name pairs and turn this into the voice config array\n const speakerVoiceConfigs: Gemini.SpeakerVoiceConfig[] = voices.map(\n (v: SpeechSpeakerName): Gemini.SpeakerVoiceConfig => ({\n speaker: v.speaker,\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: v.name,\n },\n },\n })\n );\n // Create the multi-speaker voice configuration\n ret = {\n multiSpeakerVoiceConfig: {\n speakerVoiceConfigs,\n },\n };\n }\n\n if (languageCode) {\n ret.languageCode = languageCode;\n }\n\n return ret;\n}\n\ntype ServiceTierResponse = {} | { serviceTier: Gemini.ServiceTier };\n\n/**\n * Builds an object that can be included as part of the request\n * which will add a `speechConfig` attribute if appropriate.\n */\nexport function convertFieldsToServiceTier(\n platform: GooglePlatformType,\n fields: ChatGoogleFields\n): ServiceTierResponse {\n // Service tier is handled in a different way on GCP\n if (platform === \"gcp\") {\n return {};\n }\n\n const serviceTier = fields.serviceTier;\n if (!serviceTier || !settableServiceTier.includes(serviceTier)) {\n return {};\n }\n\n return {\n serviceTier: serviceTier,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"params.js","names":[],"sources":["../../src/converters/params.ts"],"sourcesContent":["import {\n ChatGoogleFields,\n GooglePlatformType,\n settableServiceTier,\n SimplifiedSpeechConfig,\n SimplifiedSpeechLanguageConfig,\n SpeechSpeakerName,\n SpeechVoice,\n SpeechVoiceLanguage,\n} from \"../chat_models/types.js\";\nimport type { Gemini } from \"../chat_models/api-types.js\";\n\n/**\n * Converts a reasoning \"effort\" level value (e.g., \"minimal\", \"low\", \"medium\", \"high\")\n * to a corresponding reasoning token budget, depending on the target Gemini model.\n *\n * This mapping is essential when configuring Google Gemini API calls with reasoning\n * effort/level fields (as in the Gemini OpenAI-compatible API) or when translating\n * user-friendly configuration to explicit token budgets.\n *\n * - The mapping is refined to reflect the constraints documented by Google:\n * - For \"gemini-2.5-flash\" models, token budgets may be set as low as 0 and up to 24k.\n * - For other Gemini models, minimum is 128, maximum is 32k.\n * - Standard effort levels and their token mappings:\n * - \"none\" / \"minimal\": lowest allowed (minTokens)\n * - \"low\": 1k tokens\n * - \"medium\": 8k tokens\n * - \"high\": maximum allowed for the model (maxTokens)\n * - See reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} modelName\n * The Gemini model name (e.g., \"gemini-2.5-flash\"). Used to determine token range constraints.\n * @param {string | undefined} effort\n * The reasoning effort level: one of \"none\", \"minimal\", \"low\", \"medium\", \"high\"\n * (case-insensitive). If undefined, the function returns undefined.\n * @returns {number | undefined}\n * The corresponding token count for the given effort and model. Returns undefined if\n * the effort is not recognized or not provided.\n *\n * @example\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"medium\") // 8192\n * convertReasoningEffortToReasoningTokens(\"gemini-1.5-pro\", \"high\") // 32768\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"none\") // 0\n * convertReasoningEffortToReasoningTokens(undefined, \"low\") // 1024\n */\nexport function convertReasoningEffortToReasoningTokens(\n modelName?: string,\n effort?: string\n): number | undefined {\n if (effort === undefined) {\n return undefined;\n }\n\n // gemini-2.5-flash and -flash-lite can be disabled. Others can't.\n // https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n const minTokens: number = modelName?.startsWith(\"gemini-2.5-flash\") ? 0 : 128;\n const maxTokens: number = modelName?.startsWith(\"gemini-2.5-flash\")\n ? 24 * 1024\n : 32 * 1024;\n\n const normalizedEffort = effort.toLowerCase();\n switch (normalizedEffort) {\n case \"none\":\n case \"minimal\":\n return minTokens;\n case \"low\":\n // Defined as 1k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 1024;\n case \"medium\":\n // Defined as 8k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 8 * 1024;\n case \"high\":\n // Defined as 24k or 32k (model-dependent) by https://ai.google.dev/gemini-api/docs/openai#thinking\n return maxTokens;\n default:\n return undefined;\n }\n}\n\n/**\n * Converts a reasoning token budget (e.g., 0, 1024, 8192, 32768) to its corresponding\n * reasoning \"effort\" level, as used in the Google Gemini API (\"minimal\", \"low\", \"medium\", \"high\").\n *\n * This is the inverse of {@link convertReasoningEffortToReasoningTokens}. It is useful for\n * translating explicit numeric settings (often surfaced in raw API responses or configs)\n * into user-facing friendly reasoning levels/labels. The mapping relies on Google's\n * documented reasoning levels for different models:\n *\n * - -1 means default effort (per API documentation: this is \"minimal\" for flash-lite models\n * and \"high\" (dynamic) for others).\n * - 0 means \"minimal\" unless the model is \"gemini-3-pro\", for which it maps to \"low\".\n * - <= 1024 tokens: \"low\"\n * - <= 8192 tokens: \"medium\" (unless model is \"gemini-3-pro\", for which it reverts to \"low\")\n * - > 8192: \"high\"\n *\n * Reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} model\n * The Gemini model name (e.g., \"gemini-3-pro\"). Used for model-specific special cases.\n * @param {number | undefined} reasoningTokens\n * The token budget to convert. If undefined, the function returns undefined.\n * @returns {Lowercase<Gemini.ThinkingLevel> | undefined}\n * The reasoning \"effort\" level (\"minimal\", \"low\", \"medium\", \"high\") or undefined if input is not valid.\n *\n * @example\n * convertReasoningTokensToReasoningEffort(\"gemini-3-pro\", 0) // \"low\"\n * convertReasoningTokensToReasoningEffort(\"gemini-1.5-pro\", 0) // \"minimal\"\n * convertReasoningTokensToReasoningEffort(undefined, 1024) // \"low\"\n * convertReasoningTokensToReasoningEffort(undefined, -1) // \"high\"\n * convertReasoningTokensToReasoningEffort(undefined, 9000) // \"high\"\n */\nexport function convertReasoningTokensToReasoningEffort(\n model?: string,\n reasoningTokens?: number\n): Gemini.ThinkingLevel | undefined {\n if (typeof reasoningTokens === \"undefined\") {\n return undefined;\n } else if (reasoningTokens === -1) {\n // -1 means \"minimal\" for flash-lite models and \"high\"/dynamic for others\n // (\"default\" per https://ai.google.dev/gemini-api/docs/thinking#thinking-levels)\n if (model && getModelLevel(model) === \"flash-lite\") {\n return \"minimal\";\n }\n return \"high\";\n } else if (reasoningTokens === 0) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"minimal\";\n }\n } else if (reasoningTokens <= 1024) {\n return \"low\";\n } else if (reasoningTokens <= 8192) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"medium\";\n }\n } else {\n return \"high\";\n }\n}\n\n/**\n * Infers and returns the Google platform type from the provided chat model parameters.\n *\n * This function helps determine which Google platform (e.g., Vertex AI on GCP or the public API)\n * should be used for calling Gemini/PaLM endpoints, based on the options provided in\n * {@link BaseChatGoogleParams} (such as model constructors or call options).\n */\nexport function convertParamsToPlatformType(params: {\n platformType?: GooglePlatformType;\n vertexai?: boolean;\n}): GooglePlatformType | undefined {\n if (typeof params === \"undefined\") {\n return undefined;\n }\n if (typeof params.platformType !== \"undefined\") {\n return params.platformType;\n }\n if (params.vertexai === true) {\n return \"gcp\";\n }\n return undefined;\n}\n\n/**\n * Returns the model family from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"gemini\"\n */\nexport function getModelFamily(model: string): string {\n return model.split(\"-\")[0];\n}\n\n/**\n * Returns the model version from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"2.5\"\n */\nexport function getModelVersion(model: string): string {\n return model.split(\"-\")[1];\n}\n\n/**\n * Returns the model level from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"flash\", \"gemini-2.5-flash-lite\" → \"flash-lite\"\n */\nexport function getModelLevel(model: string): string {\n const parts = model.split(\"-\");\n let ret = parts[2];\n if (ret === \"flash\" && parts[3] === \"lite\") {\n ret = \"flash-lite\";\n }\n return ret;\n}\n\n/**\n * Returns the model specialty from a Gemini model name.\n * e.g. \"gemini-2.5-flash-preview-image\" → \"image\",\n * \"gemini-2.5-flash-preview-tts\" → \"tts\",\n * \"gemini-2.5-flash\" → \"\"\n */\nexport function getModelSpecialty(model: string): string {\n if (model.includes(\"image\")) {\n return \"image\";\n } else if (model.includes(\"tts\")) {\n return \"tts\";\n } else {\n return \"\";\n }\n}\n\n/**\n * Builds the `thinkingConfig` object for the Gemini `generationConfig`,\n * translating user-facing reasoning fields into the wire format.\n *\n * @param model The full Gemini model name (e.g. \"gemini-2.5-flash\").\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.ThinkingConfig}, or `{}`\n * when thinking should not be explicitly configured.\n */\nexport function convertFieldsToThinkingConfig(\n model: string,\n fields: ChatGoogleFields\n): Gemini.ThinkingConfig | undefined {\n // Thinking / reasoning\n let includeThoughts = true;\n\n let thinkingBudget =\n fields?.maxReasoningTokens ??\n fields?.thinkingBudget ??\n convertReasoningEffortToReasoningTokens(\n model,\n fields?.reasoningEffort ?? fields?.thinkingLevel\n );\n if (\n thinkingBudget === 0 ||\n (model.includes(\"pro\") && thinkingBudget === 128)\n ) {\n includeThoughts = false;\n }\n if (\n model.startsWith(\"gemini-2.5-pro\") &&\n typeof thinkingBudget !== \"undefined\"\n ) {\n // Can't turn off Gemini 2.5 Pro thinking completely\n if (thinkingBudget >= 0 && thinkingBudget < 128) {\n thinkingBudget = 128;\n }\n }\n\n const thinkingLevelRaw =\n fields?.reasoningEffort ??\n fields?.thinkingLevel ??\n convertReasoningTokensToReasoningEffort(\n model,\n fields?.maxReasoningTokens ?? fields?.thinkingBudget\n );\n let thinkingLevel = thinkingLevelRaw?.toUpperCase();\n if (thinkingLevel === \"MINIMAL\") {\n includeThoughts = false;\n }\n if (model.startsWith(\"gemini-3-pro\")) {\n // Gemini 3 Pro has only low and high.\n if (thinkingLevel === \"MINIMAL\") {\n thinkingLevel = \"LOW\";\n } else if (thinkingLevel === \"MEDIUM\") {\n thinkingLevel = \"HIGH\";\n }\n } else if (model.startsWith(\"gemini-3.1-pro\")) {\n // Gemini 3.1 Pro does not have \"minimal\"\n if (thinkingLevel === \"MINIMAL\") {\n thinkingLevel = \"LOW\";\n }\n }\n\n // If we are using a model that doesn't support thinking at all (gemini 2.5 imaging)\n // or we haven't explicitly tried to set a thinking budget/level, then bail out.\n const modelVersion = getModelVersion(model);\n const modelSpecialty = getModelSpecialty(model);\n const thoughtsNotSupported =\n modelVersion === \"2.5\" && modelSpecialty === \"image\";\n if (\n thoughtsNotSupported ||\n typeof thinkingBudget === \"undefined\" ||\n typeof thinkingLevel === \"undefined\"\n ) {\n return undefined;\n }\n\n // If we have gotten this far, then we want to explicitly set if we include thoughts or not.\n const thinkingConfig: Gemini.ThinkingConfig = {\n includeThoughts,\n };\n\n // Explicitly setting the budget/level is only valid (currently) for text models.\n if (modelSpecialty === \"\") {\n if (model.startsWith(\"gemini-2.5\")) {\n thinkingConfig.thinkingBudget = thinkingBudget;\n } else {\n thinkingConfig.thinkingLevel = thinkingLevel as Gemini.ThinkingLevel;\n }\n }\n\n return thinkingConfig;\n}\n\n/**\n * Builds the `speechConfig` object for the Gemini `generationConfig`,\n * normalising the user-friendly simplified forms into the wire format.\n *\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.SpeechConfig}, or `{}`\n * when no speech config was provided.\n */\nexport function convertFieldsToSpeechConfig(\n fields: ChatGoogleFields\n): Gemini.SpeechConfig | undefined {\n const config: Gemini.SpeechConfig | SimplifiedSpeechConfig | undefined =\n fields.speechConfig;\n if (typeof config === \"undefined\") {\n return undefined;\n }\n\n function isSpeechConfig(\n config: Gemini.SpeechConfig | SimplifiedSpeechConfig\n ): config is Gemini.SpeechConfig {\n return (\n typeof config === \"object\" &&\n (Object.hasOwn(config, \"voiceConfig\") ||\n Object.hasOwn(config, \"multiSpeakerVoiceConfig\"))\n );\n }\n\n function hasLanguage(\n config: SimplifiedSpeechConfig\n ): config is SimplifiedSpeechLanguageConfig {\n return typeof config === \"object\" && Object.hasOwn(config, \"languageCode\");\n }\n\n function hasVoice(\n config: SimplifiedSpeechLanguageConfig\n ): config is SpeechVoiceLanguage {\n return Object.hasOwn(config, \"voice\");\n }\n\n // If this is already a SpeechConfig, just return it\n if (isSpeechConfig(config)) {\n return config;\n }\n\n let languageCode: string | undefined;\n let voice: SpeechVoice;\n if (hasLanguage(config)) {\n languageCode = config.languageCode;\n voice = hasVoice(config) ? config.voice : config.voices;\n } else {\n languageCode = undefined;\n voice = config;\n }\n\n let ret: Gemini.SpeechConfig;\n\n if (typeof voice === \"string\") {\n // They just provided the prebuilt voice configuration name. Use it.\n ret = {\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: voice,\n },\n },\n };\n } else {\n // This is multi-speaker, so we have speaker/name pairs\n // If we have just one (why?), turn it into an array for the moment\n const voices: SpeechSpeakerName[] = Array.isArray(voice) ? voice : [voice];\n // Go through all the speaker/name pairs and turn this into the voice config array\n const speakerVoiceConfigs: Gemini.SpeakerVoiceConfig[] = voices.map(\n (v: SpeechSpeakerName): Gemini.SpeakerVoiceConfig => ({\n speaker: v.speaker,\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: v.name,\n },\n },\n })\n );\n // Create the multi-speaker voice configuration\n ret = {\n multiSpeakerVoiceConfig: {\n speakerVoiceConfigs,\n },\n };\n }\n\n if (languageCode) {\n ret.languageCode = languageCode;\n }\n\n return ret;\n}\n\ntype ServiceTierResponse = {} | { serviceTier: Gemini.ServiceTier };\n\n/**\n * Builds an object that can be included as part of the request\n * which will add a `speechConfig` attribute if appropriate.\n */\nexport function convertFieldsToServiceTier(\n platform: GooglePlatformType,\n fields: ChatGoogleFields\n): ServiceTierResponse {\n // Service tier is handled in a different way on GCP\n if (platform === \"gcp\") {\n return {};\n }\n\n const serviceTier = fields.serviceTier;\n if (!serviceTier || !settableServiceTier.includes(serviceTier)) {\n return {};\n }\n\n return {\n serviceTier: serviceTier,\n };\n}\n"],"mappings":"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"}
@@ -257,9 +257,10 @@ const convertToolsToGeminiTools = (tools) => {
257
257
  if (tools.length === 0) return [];
258
258
  const geminiTools = [];
259
259
  const functionDeclarations = [];
260
- for (const tool of tools) if (isGeminiTool(tool)) if ("functionDeclarations" in tool) geminiTools.push(tool);
261
- else geminiTools.push(tool);
262
- else if ("googleSearchRetrieval" in tool) geminiTools.push({ googleSearch: {} });
260
+ for (const tool of tools) if (isGeminiTool(tool)) {
261
+ if ("functionDeclarations" in tool) geminiTools.push(tool);
262
+ else geminiTools.push(tool);
263
+ } else if ("googleSearchRetrieval" in tool) geminiTools.push({ googleSearch: {} });
263
264
  else {
264
265
  const funcDecls = convertToolsToGeminiFunctionDeclarations([tool]);
265
266
  functionDeclarations.push(...funcDecls);
@@ -335,8 +336,10 @@ function convertToolChoiceToGeminiConfig(toolChoice, hasTools) {
335
336
  mode = "ANY";
336
337
  toolChoiceFunction = toolChoiceMode;
337
338
  }
338
- if (toolChoiceFunction) if (Array.isArray(toolChoiceFunction)) allowedFunctionNames = toolChoiceFunction;
339
- else allowedFunctionNames = [toolChoiceFunction];
339
+ if (toolChoiceFunction) {
340
+ if (Array.isArray(toolChoiceFunction)) allowedFunctionNames = toolChoiceFunction;
341
+ else allowedFunctionNames = [toolChoiceFunction];
342
+ }
340
343
  const functionCallingConfig = {};
341
344
  if (allowedFunctionNames?.length) functionCallingConfig.allowedFunctionNames = allowedFunctionNames;
342
345
  if (mode) functionCallingConfig.mode = mode;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.cjs","names":["InvalidInputError","InvalidToolError"],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { isOpenAITool } from \"@langchain/core/language_models/base\";\nimport {\n InteropZodType,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n type JsonSchema7Type,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type {\n BindToolsInput,\n ToolChoice,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { InvalidInputError, InvalidToolError } from \"../utils/errors.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\n\n/**\n * Adjusts a JSON Schema object type to be compatible with Gemini's function schema format.\n *\n * Gemini's function schema format has specific requirements:\n * - Type must be a string, not an array\n * - Nullable types should use the `nullable` property instead of including \"null\" in a type array\n * - Union types are not supported\n *\n * @param obj - The JSON Schema object to adjust\n * @returns The adjusted object with type converted to Gemini-compatible format\n * @throws {Error} If the type is a union type or null-only type, which Gemini cannot handle\n *\n * @internal\n */\nfunction adjustObjectType(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, any> {\n if (!Array.isArray(obj.type)) {\n return obj;\n }\n\n const len = obj.type.length;\n const nullIndex = obj.type.indexOf(\"null\");\n if (len === 2 && nullIndex >= 0) {\n // There are only two values set for the type, and one of them is \"null\".\n // Set the type to the other one and set nullable to true.\n const typeIndex = nullIndex === 0 ? 1 : 0;\n obj.type = obj.type[typeIndex];\n obj.nullable = true;\n } else if (len === 1 && nullIndex === 0) {\n // This is nullable only without a type, which doesn't make sense for Gemini\n throw new InvalidInputError(\n \"Gemini does not support null-only types in function schemas. Provide a non-null type alongside nullable.\"\n );\n } else if (len === 1) {\n // Although an array, it has only one value.\n // So set it to the string to match what Gemini expects.\n obj.type = obj?.type[0];\n } else if (len === 0) {\n // Empty type array is invalid\n throw new InvalidInputError(\n \"Gemini does not support empty type arrays in function schemas. Provide at least one type.\"\n );\n } else {\n // Anything else could be a union type, so reject it.\n throw new InvalidInputError(\n \"Gemini does not support union types in function schemas. Use a single type instead.\"\n );\n }\n return obj;\n}\n\n/**\n * Recursively removes unsupported properties from a JSON Schema object to make it\n * compatible with Gemini's function schema format.\n *\n * Gemini's function schema format does not support:\n * - `additionalProperties` property\n * - Array types (must be converted to string types with nullable flag)\n * - Union types\n *\n * @param obj - The JSON Schema object to clean\n * @returns A cleaned GeminiFunctionSchema object\n *\n * @internal\n */\nfunction removeAdditionalProperties(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n): Gemini.Tools.Schema & { $schema?: string } {\n if (typeof obj === \"object\" && obj !== null) {\n const newObj = { ...obj };\n\n if (\"additionalProperties\" in newObj) {\n delete newObj.additionalProperties;\n }\n\n adjustObjectType(newObj);\n\n for (const key in newObj) {\n if (key in newObj) {\n if (Array.isArray(newObj[key])) {\n newObj[key] = newObj[key].map(removeAdditionalProperties);\n } else if (typeof newObj[key] === \"object\" && newObj[key] !== null) {\n newObj[key] = removeAdditionalProperties(newObj[key]);\n }\n }\n }\n\n return newObj as Gemini.Tools.Schema;\n }\n\n return obj as Gemini.Tools.Schema;\n}\n\n/**\n * Converts a Zod schema or JSON Schema to Gemini function parameters format.\n *\n * This function handles the conversion of schema definitions from various formats\n * (Zod schemas, JSON Schema 7) to Gemini's function schema format. It removes\n * unsupported properties and adjusts type definitions to be compatible with Gemini.\n *\n * @param schema - The schema to convert (Zod schema or JSON Schema 7)\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nexport function schemaToGeminiParameters<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n>(\n schema:\n | SerializableSchema<RunOutput>\n | InteropZodType<RunOutput>\n | JsonSchema7Type\n): Gemini.Tools.Schema {\n // Gemini doesn't accept either the $schema or additionalProperties\n // attributes, so we need to explicitly remove them.\n // Zod sometimes also makes an array of type (because of .nullish()),\n // which needs cleaning up.\n const jsonSchema = removeAdditionalProperties(\n isInteropZodSchema(schema) || isSerializableSchema(schema)\n ? toJsonSchema(schema)\n : schema\n );\n const { $schema, ...rest } = jsonSchema;\n return rest;\n}\n\n/**\n * Converts a JSON Schema object to Gemini function parameters format.\n *\n * @param schema - The JSON Schema object to convert\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nfunction jsonSchemaToGeminiParameters(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n schema: Record<string, any>\n): Gemini.Tools.Schema {\n const jsonSchema = removeAdditionalProperties(schema);\n const { $schema, ...rest } = jsonSchema;\n\n return rest;\n}\n\n/**\n * Type guard to check if a tool is already in Gemini's native format.\n *\n * Gemini tools can be:\n * - Function declarations (`functionDeclarations`)\n * - Code execution tools (`codeExecution`)\n * - Google Search retrieval tools (`googleSearchRetrieval` or `googleSearch`)\n *\n * The full list of tool types are those defined by the `Gemini.Tool` type.\n * Updating that list requires a change to the `geminiToolAttributes` below.\n *\n * @param tool - The tool to check\n * @returns `true` if the tool is already in Gemini format, `false` otherwise\n *\n * @internal\n */\nfunction isGeminiTool(tool: BindToolsInput): tool is Gemini.Tool {\n if (typeof tool !== \"object\" || tool === null) {\n return false;\n }\n\n const geminiToolAttributes = [\n \"functionDeclarations\",\n \"codeExecution\",\n \"googleSearch\",\n \"urlContext\",\n \"googleMaps\",\n \"fileSearch\",\n \"computerUse\",\n \"mcpServers\",\n ];\n\n return geminiToolAttributes.some((attr) => attr in tool);\n}\n\n/**\n * Converts an array of LangChain tools to Gemini function declarations.\n *\n * This converter transforms various tool formats into Gemini's function declaration\n * format. It supports:\n * - LangChain structured tools (with Zod schemas)\n * - OpenAI format tools (with JSON Schema)\n * - Gemini-native function declarations (passed through)\n * - Raw function declaration objects\n *\n * @remarks\n * The converter handles each tool type differently:\n * - **LangChain tools**: Extracts name, description, and schema, converting the schema\n * to Gemini-compatible format\n * - **OpenAI tools**: Extracts function name, description, and parameters from the\n * OpenAI tool format\n * - **Gemini tools**: If the tool contains `functionDeclarations`, extracts and returns\n * them directly. Non-function Gemini tools (like `codeExecution`) are skipped.\n * - **Raw objects**: Assumes the object is a function declaration and validates it has\n * a `name` property\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather for a location\",\n * schema: z.object({\n * location: z.string().describe(\"The city name\"),\n * unit: z.enum([\"celsius\", \"fahrenheit\"]).optional()\n * }),\n * func: async (args) => { /* ... *\\/ }\n * });\n *\n * const declarations = convertToolsToGeminiFunctionDeclarations([weatherTool]);\n * // Returns: [{\n * // name: \"get_weather\",\n * // description: \"Get the weather for a location\",\n * // parameters: {\n * // type: \"object\",\n * // properties: {\n * // location: { type: \"string\", description: \"The city name\" },\n * // unit: { type: \"string\", enum: [\"celsius\", \"fahrenheit\"], nullable: true }\n * // },\n * // required: [\"location\"]\n * // }\n * // }]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Gemini function declarations ready for API submission\n * @throws {Error} If a tool cannot be converted (invalid format)\n */\nexport const convertToolsToGeminiFunctionDeclarations: Converter<\n BindToolsInput[],\n Gemini.Tools.FunctionDeclaration[]\n> = (tools) => {\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n for (const tool of tools) {\n // Skip if already in Gemini format (non-function tools)\n if (isGeminiTool(tool) && !(\"functionDeclarations\" in tool)) {\n continue;\n }\n\n // Handle Gemini format with functionDeclarations\n if (\n isGeminiTool(tool) &&\n \"functionDeclarations\" in tool &&\n Array.isArray(tool.functionDeclarations)\n ) {\n functionDeclarations.push(...tool.functionDeclarations);\n continue;\n }\n\n // Handle LangChain structured tools\n if (isLangChainTool(tool)) {\n const jsonSchema = schemaToGeminiParameters(tool.schema);\n functionDeclarations.push({\n name: tool.name,\n description: tool.description ?? `A function available to call.`,\n parameters: jsonSchema,\n });\n continue;\n }\n\n // Handle OpenAI format tools\n if (isOpenAITool(tool)) {\n functionDeclarations.push({\n name: tool.function.name,\n description:\n tool.function.description ?? `A function available to call.`,\n parameters: jsonSchemaToGeminiParameters(tool.function.parameters),\n });\n continue;\n }\n\n // Handle raw function declaration objects\n if (\n typeof tool === \"object\" &&\n tool !== null &&\n \"name\" in tool &&\n typeof tool.name === \"string\"\n ) {\n const funcDecl = tool as Partial<Gemini.Tools.FunctionDeclaration>;\n functionDeclarations.push({\n name: funcDecl.name!,\n description: funcDecl.description ?? `A function available to call.`,\n parameters: funcDecl.parameters,\n });\n continue;\n }\n\n throw new InvalidToolError(tool);\n }\n\n return functionDeclarations;\n};\n\n/**\n * Converts an array of tools to Gemini's Tools array format.\n *\n * This converter groups function declarations into a single tool object while\n * preserving Gemini-native tools (like code execution and Google Search retrieval)\n * as separate tool objects. This matches Gemini's API structure where:\n * - Function declarations are grouped together in a single tool\n * - Non-function tools (codeExecution, googleSearchRetrieval) are separate tools\n *\n * @remarks\n * The conversion process:\n * 1. Separates Gemini-native tools from function declarations\n * 2. Converts LangChain/OpenAI tools to function declarations\n * 3. Groups all function declarations into a single Tool object\n * 4. Returns an array with the grouped function declarations tool plus any\n * non-function Gemini tools\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather\",\n * schema: z.object({ location: z.string() }),\n * func: async () => {}\n * });\n *\n * const geminiCodeTool: Gemini.Tool = {\n * codeExecution: { enabled: true }\n * };\n *\n * const tools = convertToolsToGeminiTools([weatherTool, geminiCodeTool]);\n * // Returns: [\n * // { functionDeclarations: [{ name: \"get_weather\", ... }] },\n * // { codeExecution: { enabled: true } }\n * // ]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Tool objects ready for API submission\n */\nexport const convertToolsToGeminiTools: Converter<\n BindToolsInput[],\n Gemini.Tool[]\n> = (tools) => {\n if (tools.length === 0) {\n return [];\n }\n\n const geminiTools: Gemini.Tool[] = [];\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n // Separate Gemini-native tools from function declarations\n for (const tool of tools) {\n if (isGeminiTool(tool)) {\n if (\"functionDeclarations\" in tool) {\n // Already a Gemini tool with function declarations\n geminiTools.push(tool);\n } else {\n // Non-function Gemini tool (codeExecution, googleSearch, etc.)\n geminiTools.push(tool);\n }\n } else if (\"googleSearchRetrieval\" in tool) {\n // Convert the obsolete search into the modern one\n const searchTool: Gemini.Tool = {\n googleSearch: {},\n };\n geminiTools.push(searchTool);\n } else {\n // Convert to function declaration\n const funcDecls = convertToolsToGeminiFunctionDeclarations([tool]);\n functionDeclarations.push(...funcDecls);\n }\n }\n\n // Add function declarations as a single tool if we have any\n if (functionDeclarations.length > 0) {\n geminiTools.push({\n functionDeclarations,\n });\n }\n\n return geminiTools;\n};\n\n/**\n * Converts tool choice to Gemini's function calling configuration.\n *\n * This converter transforms LangChain's tool choice format into Gemini's\n * `toolConfig.functionCallingConfig` format. The configuration is only\n * created when tools are present (`hasTools` is true).\n *\n * @remarks\n * The conversion logic:\n * - If `hasTools` is `false`, returns `undefined` (no config needed)\n * - If `toolChoice` is provided, converts it to a Gemini mode:\n * - `\"auto\"` → `\"AUTO\"` - Model decides whether to call functions\n * - `\"any\"` or `\"required\"` → `\"ANY\"` - Model must call at least one function\n * - `\"none\"` → `\"NONE\"` - Model cannot call functions\n * - Function name string → `\"ANY\"` - Forces function use\n * - Object with `mode` → Maps the mode string to Gemini format\n * - Object with `function` → `\"ANY\"` - Forces function use\n * - If `toolChoice` is not provided but tools exist, defaults to `\"AUTO\"`\n *\n * Gemini's function calling modes:\n * - **AUTO**: The model can choose whether to call functions based on the conversation\n * - **ANY**: The model must call at least one function before responding\n * - **NONE**: The model cannot call any functions\n *\n * When a specific function is requested (via function name string or `{ function: { name } }`),\n * the converter uses `\"ANY\"` mode to force function usage, as Gemini doesn't support\n * specifying individual functions in the mode.\n *\n * @example\n * ```typescript\n * // With explicit tool choice\n * convertToolChoiceToGeminiConfig(\"auto\", true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // Required mode - must call a function\n * convertToolChoiceToGeminiConfig(\"any\", true);\n * // Returns: { functionCallingConfig: { mode: \"ANY\" } }\n *\n * // Without tool choice (defaults to AUTO when tools exist)\n * convertToolChoiceToGeminiConfig(undefined, true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // No tools - returns undefined\n * convertToolChoiceToGeminiConfig(\"auto\", false);\n * // Returns: undefined\n * ```\n *\n * @param toolChoice - The tool choice option from LangChain (string, object, or undefined)\n * @param hasTools - Whether tools are present. If false, returns undefined\n * @returns The Gemini tool configuration object, or undefined if no tools are present\n */\nexport function convertToolChoiceToGeminiConfig(\n toolChoice: ToolChoice | undefined,\n hasTools: boolean\n): Gemini.Tools.ToolConfig | undefined {\n if (!hasTools || toolChoice === undefined) {\n return undefined;\n }\n\n let mode: Gemini.Tools.FunctionCallingConfigMode | undefined;\n let allowedFunctionNames: string[] | undefined;\n\n let toolChoiceMode: string | undefined;\n let toolChoiceFunction: string | string[] | undefined;\n if (typeof toolChoice === \"object\") {\n toolChoiceMode = toolChoice.mode;\n toolChoiceFunction = toolChoice.function?.name;\n } else {\n toolChoiceMode = toolChoice;\n }\n\n if (toolChoiceMode === \"auto\") {\n mode = \"AUTO\";\n } else if (toolChoiceMode === \"any\" || toolChoiceMode === \"required\") {\n mode = \"ANY\";\n } else if (toolChoiceMode === \"none\") {\n mode = \"NONE\";\n } else if (typeof toolChoiceMode === \"string\") {\n mode = \"ANY\";\n toolChoiceFunction = toolChoiceMode;\n }\n\n if (toolChoiceFunction) {\n if (Array.isArray(toolChoiceFunction)) {\n allowedFunctionNames = toolChoiceFunction;\n } else {\n allowedFunctionNames = [toolChoiceFunction];\n }\n }\n\n const functionCallingConfig: Gemini.Tools.FunctionCallingConfig = {};\n if (allowedFunctionNames?.length) {\n functionCallingConfig.allowedFunctionNames = allowedFunctionNames;\n }\n if (mode) {\n functionCallingConfig.mode = mode;\n }\n return {\n functionCallingConfig,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"tools.cjs","names":["InvalidInputError","isInteropZodSchema","isSerializableSchema","toJsonSchema","isLangChainTool","isOpenAITool","InvalidToolError"],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { isOpenAITool } from \"@langchain/core/language_models/base\";\nimport {\n InteropZodType,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n type JsonSchema7Type,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type {\n BindToolsInput,\n ToolChoice,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { InvalidInputError, InvalidToolError } from \"../utils/errors.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\n\n/**\n * Adjusts a JSON Schema object type to be compatible with Gemini's function schema format.\n *\n * Gemini's function schema format has specific requirements:\n * - Type must be a string, not an array\n * - Nullable types should use the `nullable` property instead of including \"null\" in a type array\n * - Union types are not supported\n *\n * @param obj - The JSON Schema object to adjust\n * @returns The adjusted object with type converted to Gemini-compatible format\n * @throws {Error} If the type is a union type or null-only type, which Gemini cannot handle\n *\n * @internal\n */\nfunction adjustObjectType(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, any> {\n if (!Array.isArray(obj.type)) {\n return obj;\n }\n\n const len = obj.type.length;\n const nullIndex = obj.type.indexOf(\"null\");\n if (len === 2 && nullIndex >= 0) {\n // There are only two values set for the type, and one of them is \"null\".\n // Set the type to the other one and set nullable to true.\n const typeIndex = nullIndex === 0 ? 1 : 0;\n obj.type = obj.type[typeIndex];\n obj.nullable = true;\n } else if (len === 1 && nullIndex === 0) {\n // This is nullable only without a type, which doesn't make sense for Gemini\n throw new InvalidInputError(\n \"Gemini does not support null-only types in function schemas. Provide a non-null type alongside nullable.\"\n );\n } else if (len === 1) {\n // Although an array, it has only one value.\n // So set it to the string to match what Gemini expects.\n obj.type = obj?.type[0];\n } else if (len === 0) {\n // Empty type array is invalid\n throw new InvalidInputError(\n \"Gemini does not support empty type arrays in function schemas. Provide at least one type.\"\n );\n } else {\n // Anything else could be a union type, so reject it.\n throw new InvalidInputError(\n \"Gemini does not support union types in function schemas. Use a single type instead.\"\n );\n }\n return obj;\n}\n\n/**\n * Recursively removes unsupported properties from a JSON Schema object to make it\n * compatible with Gemini's function schema format.\n *\n * Gemini's function schema format does not support:\n * - `additionalProperties` property\n * - Array types (must be converted to string types with nullable flag)\n * - Union types\n *\n * @param obj - The JSON Schema object to clean\n * @returns A cleaned GeminiFunctionSchema object\n *\n * @internal\n */\nfunction removeAdditionalProperties(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n): Gemini.Tools.Schema & { $schema?: string } {\n if (typeof obj === \"object\" && obj !== null) {\n const newObj = { ...obj };\n\n if (\"additionalProperties\" in newObj) {\n delete newObj.additionalProperties;\n }\n\n adjustObjectType(newObj);\n\n for (const key in newObj) {\n if (key in newObj) {\n if (Array.isArray(newObj[key])) {\n newObj[key] = newObj[key].map(removeAdditionalProperties);\n } else if (typeof newObj[key] === \"object\" && newObj[key] !== null) {\n newObj[key] = removeAdditionalProperties(newObj[key]);\n }\n }\n }\n\n return newObj as Gemini.Tools.Schema;\n }\n\n return obj as Gemini.Tools.Schema;\n}\n\n/**\n * Converts a Zod schema or JSON Schema to Gemini function parameters format.\n *\n * This function handles the conversion of schema definitions from various formats\n * (Zod schemas, JSON Schema 7) to Gemini's function schema format. It removes\n * unsupported properties and adjusts type definitions to be compatible with Gemini.\n *\n * @param schema - The schema to convert (Zod schema or JSON Schema 7)\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nexport function schemaToGeminiParameters<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n>(\n schema:\n | SerializableSchema<RunOutput>\n | InteropZodType<RunOutput>\n | JsonSchema7Type\n): Gemini.Tools.Schema {\n // Gemini doesn't accept either the $schema or additionalProperties\n // attributes, so we need to explicitly remove them.\n // Zod sometimes also makes an array of type (because of .nullish()),\n // which needs cleaning up.\n const jsonSchema = removeAdditionalProperties(\n isInteropZodSchema(schema) || isSerializableSchema(schema)\n ? toJsonSchema(schema)\n : schema\n );\n const { $schema, ...rest } = jsonSchema;\n return rest;\n}\n\n/**\n * Converts a JSON Schema object to Gemini function parameters format.\n *\n * @param schema - The JSON Schema object to convert\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nfunction jsonSchemaToGeminiParameters(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n schema: Record<string, any>\n): Gemini.Tools.Schema {\n const jsonSchema = removeAdditionalProperties(schema);\n const { $schema, ...rest } = jsonSchema;\n\n return rest;\n}\n\n/**\n * Type guard to check if a tool is already in Gemini's native format.\n *\n * Gemini tools can be:\n * - Function declarations (`functionDeclarations`)\n * - Code execution tools (`codeExecution`)\n * - Google Search retrieval tools (`googleSearchRetrieval` or `googleSearch`)\n *\n * The full list of tool types are those defined by the `Gemini.Tool` type.\n * Updating that list requires a change to the `geminiToolAttributes` below.\n *\n * @param tool - The tool to check\n * @returns `true` if the tool is already in Gemini format, `false` otherwise\n *\n * @internal\n */\nfunction isGeminiTool(tool: BindToolsInput): tool is Gemini.Tool {\n if (typeof tool !== \"object\" || tool === null) {\n return false;\n }\n\n const geminiToolAttributes = [\n \"functionDeclarations\",\n \"codeExecution\",\n \"googleSearch\",\n \"urlContext\",\n \"googleMaps\",\n \"fileSearch\",\n \"computerUse\",\n \"mcpServers\",\n ];\n\n return geminiToolAttributes.some((attr) => attr in tool);\n}\n\n/**\n * Converts an array of LangChain tools to Gemini function declarations.\n *\n * This converter transforms various tool formats into Gemini's function declaration\n * format. It supports:\n * - LangChain structured tools (with Zod schemas)\n * - OpenAI format tools (with JSON Schema)\n * - Gemini-native function declarations (passed through)\n * - Raw function declaration objects\n *\n * @remarks\n * The converter handles each tool type differently:\n * - **LangChain tools**: Extracts name, description, and schema, converting the schema\n * to Gemini-compatible format\n * - **OpenAI tools**: Extracts function name, description, and parameters from the\n * OpenAI tool format\n * - **Gemini tools**: If the tool contains `functionDeclarations`, extracts and returns\n * them directly. Non-function Gemini tools (like `codeExecution`) are skipped.\n * - **Raw objects**: Assumes the object is a function declaration and validates it has\n * a `name` property\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather for a location\",\n * schema: z.object({\n * location: z.string().describe(\"The city name\"),\n * unit: z.enum([\"celsius\", \"fahrenheit\"]).optional()\n * }),\n * func: async (args) => { /* ... *\\/ }\n * });\n *\n * const declarations = convertToolsToGeminiFunctionDeclarations([weatherTool]);\n * // Returns: [{\n * // name: \"get_weather\",\n * // description: \"Get the weather for a location\",\n * // parameters: {\n * // type: \"object\",\n * // properties: {\n * // location: { type: \"string\", description: \"The city name\" },\n * // unit: { type: \"string\", enum: [\"celsius\", \"fahrenheit\"], nullable: true }\n * // },\n * // required: [\"location\"]\n * // }\n * // }]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Gemini function declarations ready for API submission\n * @throws {Error} If a tool cannot be converted (invalid format)\n */\nexport const convertToolsToGeminiFunctionDeclarations: Converter<\n BindToolsInput[],\n Gemini.Tools.FunctionDeclaration[]\n> = (tools) => {\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n for (const tool of tools) {\n // Skip if already in Gemini format (non-function tools)\n if (isGeminiTool(tool) && !(\"functionDeclarations\" in tool)) {\n continue;\n }\n\n // Handle Gemini format with functionDeclarations\n if (\n isGeminiTool(tool) &&\n \"functionDeclarations\" in tool &&\n Array.isArray(tool.functionDeclarations)\n ) {\n functionDeclarations.push(...tool.functionDeclarations);\n continue;\n }\n\n // Handle LangChain structured tools\n if (isLangChainTool(tool)) {\n const jsonSchema = schemaToGeminiParameters(tool.schema);\n functionDeclarations.push({\n name: tool.name,\n description: tool.description ?? `A function available to call.`,\n parameters: jsonSchema,\n });\n continue;\n }\n\n // Handle OpenAI format tools\n if (isOpenAITool(tool)) {\n functionDeclarations.push({\n name: tool.function.name,\n description:\n tool.function.description ?? `A function available to call.`,\n parameters: jsonSchemaToGeminiParameters(tool.function.parameters),\n });\n continue;\n }\n\n // Handle raw function declaration objects\n if (\n typeof tool === \"object\" &&\n tool !== null &&\n \"name\" in tool &&\n typeof tool.name === \"string\"\n ) {\n const funcDecl = tool as Partial<Gemini.Tools.FunctionDeclaration>;\n functionDeclarations.push({\n name: funcDecl.name!,\n description: funcDecl.description ?? `A function available to call.`,\n parameters: funcDecl.parameters,\n });\n continue;\n }\n\n throw new InvalidToolError(tool);\n }\n\n return functionDeclarations;\n};\n\n/**\n * Converts an array of tools to Gemini's Tools array format.\n *\n * This converter groups function declarations into a single tool object while\n * preserving Gemini-native tools (like code execution and Google Search retrieval)\n * as separate tool objects. This matches Gemini's API structure where:\n * - Function declarations are grouped together in a single tool\n * - Non-function tools (codeExecution, googleSearchRetrieval) are separate tools\n *\n * @remarks\n * The conversion process:\n * 1. Separates Gemini-native tools from function declarations\n * 2. Converts LangChain/OpenAI tools to function declarations\n * 3. Groups all function declarations into a single Tool object\n * 4. Returns an array with the grouped function declarations tool plus any\n * non-function Gemini tools\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather\",\n * schema: z.object({ location: z.string() }),\n * func: async () => {}\n * });\n *\n * const geminiCodeTool: Gemini.Tool = {\n * codeExecution: { enabled: true }\n * };\n *\n * const tools = convertToolsToGeminiTools([weatherTool, geminiCodeTool]);\n * // Returns: [\n * // { functionDeclarations: [{ name: \"get_weather\", ... }] },\n * // { codeExecution: { enabled: true } }\n * // ]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Tool objects ready for API submission\n */\nexport const convertToolsToGeminiTools: Converter<\n BindToolsInput[],\n Gemini.Tool[]\n> = (tools) => {\n if (tools.length === 0) {\n return [];\n }\n\n const geminiTools: Gemini.Tool[] = [];\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n // Separate Gemini-native tools from function declarations\n for (const tool of tools) {\n if (isGeminiTool(tool)) {\n if (\"functionDeclarations\" in tool) {\n // Already a Gemini tool with function declarations\n geminiTools.push(tool);\n } else {\n // Non-function Gemini tool (codeExecution, googleSearch, etc.)\n geminiTools.push(tool);\n }\n } else if (\"googleSearchRetrieval\" in tool) {\n // Convert the obsolete search into the modern one\n const searchTool: Gemini.Tool = {\n googleSearch: {},\n };\n geminiTools.push(searchTool);\n } else {\n // Convert to function declaration\n const funcDecls = convertToolsToGeminiFunctionDeclarations([tool]);\n functionDeclarations.push(...funcDecls);\n }\n }\n\n // Add function declarations as a single tool if we have any\n if (functionDeclarations.length > 0) {\n geminiTools.push({\n functionDeclarations,\n });\n }\n\n return geminiTools;\n};\n\n/**\n * Converts tool choice to Gemini's function calling configuration.\n *\n * This converter transforms LangChain's tool choice format into Gemini's\n * `toolConfig.functionCallingConfig` format. The configuration is only\n * created when tools are present (`hasTools` is true).\n *\n * @remarks\n * The conversion logic:\n * - If `hasTools` is `false`, returns `undefined` (no config needed)\n * - If `toolChoice` is provided, converts it to a Gemini mode:\n * - `\"auto\"` → `\"AUTO\"` - Model decides whether to call functions\n * - `\"any\"` or `\"required\"` → `\"ANY\"` - Model must call at least one function\n * - `\"none\"` → `\"NONE\"` - Model cannot call functions\n * - Function name string → `\"ANY\"` - Forces function use\n * - Object with `mode` → Maps the mode string to Gemini format\n * - Object with `function` → `\"ANY\"` - Forces function use\n * - If `toolChoice` is not provided but tools exist, defaults to `\"AUTO\"`\n *\n * Gemini's function calling modes:\n * - **AUTO**: The model can choose whether to call functions based on the conversation\n * - **ANY**: The model must call at least one function before responding\n * - **NONE**: The model cannot call any functions\n *\n * When a specific function is requested (via function name string or `{ function: { name } }`),\n * the converter uses `\"ANY\"` mode to force function usage, as Gemini doesn't support\n * specifying individual functions in the mode.\n *\n * @example\n * ```typescript\n * // With explicit tool choice\n * convertToolChoiceToGeminiConfig(\"auto\", true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // Required mode - must call a function\n * convertToolChoiceToGeminiConfig(\"any\", true);\n * // Returns: { functionCallingConfig: { mode: \"ANY\" } }\n *\n * // Without tool choice (defaults to AUTO when tools exist)\n * convertToolChoiceToGeminiConfig(undefined, true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // No tools - returns undefined\n * convertToolChoiceToGeminiConfig(\"auto\", false);\n * // Returns: undefined\n * ```\n *\n * @param toolChoice - The tool choice option from LangChain (string, object, or undefined)\n * @param hasTools - Whether tools are present. If false, returns undefined\n * @returns The Gemini tool configuration object, or undefined if no tools are present\n */\nexport function convertToolChoiceToGeminiConfig(\n toolChoice: ToolChoice | undefined,\n hasTools: boolean\n): Gemini.Tools.ToolConfig | undefined {\n if (!hasTools || toolChoice === undefined) {\n return undefined;\n }\n\n let mode: Gemini.Tools.FunctionCallingConfigMode | undefined;\n let allowedFunctionNames: string[] | undefined;\n\n let toolChoiceMode: string | undefined;\n let toolChoiceFunction: string | string[] | undefined;\n if (typeof toolChoice === \"object\") {\n toolChoiceMode = toolChoice.mode;\n toolChoiceFunction = toolChoice.function?.name;\n } else {\n toolChoiceMode = toolChoice;\n }\n\n if (toolChoiceMode === \"auto\") {\n mode = \"AUTO\";\n } else if (toolChoiceMode === \"any\" || toolChoiceMode === \"required\") {\n mode = \"ANY\";\n } else if (toolChoiceMode === \"none\") {\n mode = \"NONE\";\n } else if (typeof toolChoiceMode === \"string\") {\n mode = \"ANY\";\n toolChoiceFunction = toolChoiceMode;\n }\n\n if (toolChoiceFunction) {\n if (Array.isArray(toolChoiceFunction)) {\n allowedFunctionNames = toolChoiceFunction;\n } else {\n allowedFunctionNames = [toolChoiceFunction];\n }\n }\n\n const functionCallingConfig: Gemini.Tools.FunctionCallingConfig = {};\n if (allowedFunctionNames?.length) {\n functionCallingConfig.allowedFunctionNames = allowedFunctionNames;\n }\n if (mode) {\n functionCallingConfig.mode = mode;\n }\n return {\n functionCallingConfig,\n };\n}\n"],"mappings":"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"}
@@ -257,9 +257,10 @@ const convertToolsToGeminiTools = (tools) => {
257
257
  if (tools.length === 0) return [];
258
258
  const geminiTools = [];
259
259
  const functionDeclarations = [];
260
- for (const tool of tools) if (isGeminiTool(tool)) if ("functionDeclarations" in tool) geminiTools.push(tool);
261
- else geminiTools.push(tool);
262
- else if ("googleSearchRetrieval" in tool) geminiTools.push({ googleSearch: {} });
260
+ for (const tool of tools) if (isGeminiTool(tool)) {
261
+ if ("functionDeclarations" in tool) geminiTools.push(tool);
262
+ else geminiTools.push(tool);
263
+ } else if ("googleSearchRetrieval" in tool) geminiTools.push({ googleSearch: {} });
263
264
  else {
264
265
  const funcDecls = convertToolsToGeminiFunctionDeclarations([tool]);
265
266
  functionDeclarations.push(...funcDecls);
@@ -335,8 +336,10 @@ function convertToolChoiceToGeminiConfig(toolChoice, hasTools) {
335
336
  mode = "ANY";
336
337
  toolChoiceFunction = toolChoiceMode;
337
338
  }
338
- if (toolChoiceFunction) if (Array.isArray(toolChoiceFunction)) allowedFunctionNames = toolChoiceFunction;
339
- else allowedFunctionNames = [toolChoiceFunction];
339
+ if (toolChoiceFunction) {
340
+ if (Array.isArray(toolChoiceFunction)) allowedFunctionNames = toolChoiceFunction;
341
+ else allowedFunctionNames = [toolChoiceFunction];
342
+ }
340
343
  const functionCallingConfig = {};
341
344
  if (allowedFunctionNames?.length) functionCallingConfig.allowedFunctionNames = allowedFunctionNames;
342
345
  if (mode) functionCallingConfig.mode = mode;
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","names":[],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { isOpenAITool } from \"@langchain/core/language_models/base\";\nimport {\n InteropZodType,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n type JsonSchema7Type,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type {\n BindToolsInput,\n ToolChoice,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { InvalidInputError, InvalidToolError } from \"../utils/errors.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\n\n/**\n * Adjusts a JSON Schema object type to be compatible with Gemini's function schema format.\n *\n * Gemini's function schema format has specific requirements:\n * - Type must be a string, not an array\n * - Nullable types should use the `nullable` property instead of including \"null\" in a type array\n * - Union types are not supported\n *\n * @param obj - The JSON Schema object to adjust\n * @returns The adjusted object with type converted to Gemini-compatible format\n * @throws {Error} If the type is a union type or null-only type, which Gemini cannot handle\n *\n * @internal\n */\nfunction adjustObjectType(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, any> {\n if (!Array.isArray(obj.type)) {\n return obj;\n }\n\n const len = obj.type.length;\n const nullIndex = obj.type.indexOf(\"null\");\n if (len === 2 && nullIndex >= 0) {\n // There are only two values set for the type, and one of them is \"null\".\n // Set the type to the other one and set nullable to true.\n const typeIndex = nullIndex === 0 ? 1 : 0;\n obj.type = obj.type[typeIndex];\n obj.nullable = true;\n } else if (len === 1 && nullIndex === 0) {\n // This is nullable only without a type, which doesn't make sense for Gemini\n throw new InvalidInputError(\n \"Gemini does not support null-only types in function schemas. Provide a non-null type alongside nullable.\"\n );\n } else if (len === 1) {\n // Although an array, it has only one value.\n // So set it to the string to match what Gemini expects.\n obj.type = obj?.type[0];\n } else if (len === 0) {\n // Empty type array is invalid\n throw new InvalidInputError(\n \"Gemini does not support empty type arrays in function schemas. Provide at least one type.\"\n );\n } else {\n // Anything else could be a union type, so reject it.\n throw new InvalidInputError(\n \"Gemini does not support union types in function schemas. Use a single type instead.\"\n );\n }\n return obj;\n}\n\n/**\n * Recursively removes unsupported properties from a JSON Schema object to make it\n * compatible with Gemini's function schema format.\n *\n * Gemini's function schema format does not support:\n * - `additionalProperties` property\n * - Array types (must be converted to string types with nullable flag)\n * - Union types\n *\n * @param obj - The JSON Schema object to clean\n * @returns A cleaned GeminiFunctionSchema object\n *\n * @internal\n */\nfunction removeAdditionalProperties(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n): Gemini.Tools.Schema & { $schema?: string } {\n if (typeof obj === \"object\" && obj !== null) {\n const newObj = { ...obj };\n\n if (\"additionalProperties\" in newObj) {\n delete newObj.additionalProperties;\n }\n\n adjustObjectType(newObj);\n\n for (const key in newObj) {\n if (key in newObj) {\n if (Array.isArray(newObj[key])) {\n newObj[key] = newObj[key].map(removeAdditionalProperties);\n } else if (typeof newObj[key] === \"object\" && newObj[key] !== null) {\n newObj[key] = removeAdditionalProperties(newObj[key]);\n }\n }\n }\n\n return newObj as Gemini.Tools.Schema;\n }\n\n return obj as Gemini.Tools.Schema;\n}\n\n/**\n * Converts a Zod schema or JSON Schema to Gemini function parameters format.\n *\n * This function handles the conversion of schema definitions from various formats\n * (Zod schemas, JSON Schema 7) to Gemini's function schema format. It removes\n * unsupported properties and adjusts type definitions to be compatible with Gemini.\n *\n * @param schema - The schema to convert (Zod schema or JSON Schema 7)\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nexport function schemaToGeminiParameters<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n>(\n schema:\n | SerializableSchema<RunOutput>\n | InteropZodType<RunOutput>\n | JsonSchema7Type\n): Gemini.Tools.Schema {\n // Gemini doesn't accept either the $schema or additionalProperties\n // attributes, so we need to explicitly remove them.\n // Zod sometimes also makes an array of type (because of .nullish()),\n // which needs cleaning up.\n const jsonSchema = removeAdditionalProperties(\n isInteropZodSchema(schema) || isSerializableSchema(schema)\n ? toJsonSchema(schema)\n : schema\n );\n const { $schema, ...rest } = jsonSchema;\n return rest;\n}\n\n/**\n * Converts a JSON Schema object to Gemini function parameters format.\n *\n * @param schema - The JSON Schema object to convert\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nfunction jsonSchemaToGeminiParameters(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n schema: Record<string, any>\n): Gemini.Tools.Schema {\n const jsonSchema = removeAdditionalProperties(schema);\n const { $schema, ...rest } = jsonSchema;\n\n return rest;\n}\n\n/**\n * Type guard to check if a tool is already in Gemini's native format.\n *\n * Gemini tools can be:\n * - Function declarations (`functionDeclarations`)\n * - Code execution tools (`codeExecution`)\n * - Google Search retrieval tools (`googleSearchRetrieval` or `googleSearch`)\n *\n * The full list of tool types are those defined by the `Gemini.Tool` type.\n * Updating that list requires a change to the `geminiToolAttributes` below.\n *\n * @param tool - The tool to check\n * @returns `true` if the tool is already in Gemini format, `false` otherwise\n *\n * @internal\n */\nfunction isGeminiTool(tool: BindToolsInput): tool is Gemini.Tool {\n if (typeof tool !== \"object\" || tool === null) {\n return false;\n }\n\n const geminiToolAttributes = [\n \"functionDeclarations\",\n \"codeExecution\",\n \"googleSearch\",\n \"urlContext\",\n \"googleMaps\",\n \"fileSearch\",\n \"computerUse\",\n \"mcpServers\",\n ];\n\n return geminiToolAttributes.some((attr) => attr in tool);\n}\n\n/**\n * Converts an array of LangChain tools to Gemini function declarations.\n *\n * This converter transforms various tool formats into Gemini's function declaration\n * format. It supports:\n * - LangChain structured tools (with Zod schemas)\n * - OpenAI format tools (with JSON Schema)\n * - Gemini-native function declarations (passed through)\n * - Raw function declaration objects\n *\n * @remarks\n * The converter handles each tool type differently:\n * - **LangChain tools**: Extracts name, description, and schema, converting the schema\n * to Gemini-compatible format\n * - **OpenAI tools**: Extracts function name, description, and parameters from the\n * OpenAI tool format\n * - **Gemini tools**: If the tool contains `functionDeclarations`, extracts and returns\n * them directly. Non-function Gemini tools (like `codeExecution`) are skipped.\n * - **Raw objects**: Assumes the object is a function declaration and validates it has\n * a `name` property\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather for a location\",\n * schema: z.object({\n * location: z.string().describe(\"The city name\"),\n * unit: z.enum([\"celsius\", \"fahrenheit\"]).optional()\n * }),\n * func: async (args) => { /* ... *\\/ }\n * });\n *\n * const declarations = convertToolsToGeminiFunctionDeclarations([weatherTool]);\n * // Returns: [{\n * // name: \"get_weather\",\n * // description: \"Get the weather for a location\",\n * // parameters: {\n * // type: \"object\",\n * // properties: {\n * // location: { type: \"string\", description: \"The city name\" },\n * // unit: { type: \"string\", enum: [\"celsius\", \"fahrenheit\"], nullable: true }\n * // },\n * // required: [\"location\"]\n * // }\n * // }]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Gemini function declarations ready for API submission\n * @throws {Error} If a tool cannot be converted (invalid format)\n */\nexport const convertToolsToGeminiFunctionDeclarations: Converter<\n BindToolsInput[],\n Gemini.Tools.FunctionDeclaration[]\n> = (tools) => {\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n for (const tool of tools) {\n // Skip if already in Gemini format (non-function tools)\n if (isGeminiTool(tool) && !(\"functionDeclarations\" in tool)) {\n continue;\n }\n\n // Handle Gemini format with functionDeclarations\n if (\n isGeminiTool(tool) &&\n \"functionDeclarations\" in tool &&\n Array.isArray(tool.functionDeclarations)\n ) {\n functionDeclarations.push(...tool.functionDeclarations);\n continue;\n }\n\n // Handle LangChain structured tools\n if (isLangChainTool(tool)) {\n const jsonSchema = schemaToGeminiParameters(tool.schema);\n functionDeclarations.push({\n name: tool.name,\n description: tool.description ?? `A function available to call.`,\n parameters: jsonSchema,\n });\n continue;\n }\n\n // Handle OpenAI format tools\n if (isOpenAITool(tool)) {\n functionDeclarations.push({\n name: tool.function.name,\n description:\n tool.function.description ?? `A function available to call.`,\n parameters: jsonSchemaToGeminiParameters(tool.function.parameters),\n });\n continue;\n }\n\n // Handle raw function declaration objects\n if (\n typeof tool === \"object\" &&\n tool !== null &&\n \"name\" in tool &&\n typeof tool.name === \"string\"\n ) {\n const funcDecl = tool as Partial<Gemini.Tools.FunctionDeclaration>;\n functionDeclarations.push({\n name: funcDecl.name!,\n description: funcDecl.description ?? `A function available to call.`,\n parameters: funcDecl.parameters,\n });\n continue;\n }\n\n throw new InvalidToolError(tool);\n }\n\n return functionDeclarations;\n};\n\n/**\n * Converts an array of tools to Gemini's Tools array format.\n *\n * This converter groups function declarations into a single tool object while\n * preserving Gemini-native tools (like code execution and Google Search retrieval)\n * as separate tool objects. This matches Gemini's API structure where:\n * - Function declarations are grouped together in a single tool\n * - Non-function tools (codeExecution, googleSearchRetrieval) are separate tools\n *\n * @remarks\n * The conversion process:\n * 1. Separates Gemini-native tools from function declarations\n * 2. Converts LangChain/OpenAI tools to function declarations\n * 3. Groups all function declarations into a single Tool object\n * 4. Returns an array with the grouped function declarations tool plus any\n * non-function Gemini tools\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather\",\n * schema: z.object({ location: z.string() }),\n * func: async () => {}\n * });\n *\n * const geminiCodeTool: Gemini.Tool = {\n * codeExecution: { enabled: true }\n * };\n *\n * const tools = convertToolsToGeminiTools([weatherTool, geminiCodeTool]);\n * // Returns: [\n * // { functionDeclarations: [{ name: \"get_weather\", ... }] },\n * // { codeExecution: { enabled: true } }\n * // ]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Tool objects ready for API submission\n */\nexport const convertToolsToGeminiTools: Converter<\n BindToolsInput[],\n Gemini.Tool[]\n> = (tools) => {\n if (tools.length === 0) {\n return [];\n }\n\n const geminiTools: Gemini.Tool[] = [];\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n // Separate Gemini-native tools from function declarations\n for (const tool of tools) {\n if (isGeminiTool(tool)) {\n if (\"functionDeclarations\" in tool) {\n // Already a Gemini tool with function declarations\n geminiTools.push(tool);\n } else {\n // Non-function Gemini tool (codeExecution, googleSearch, etc.)\n geminiTools.push(tool);\n }\n } else if (\"googleSearchRetrieval\" in tool) {\n // Convert the obsolete search into the modern one\n const searchTool: Gemini.Tool = {\n googleSearch: {},\n };\n geminiTools.push(searchTool);\n } else {\n // Convert to function declaration\n const funcDecls = convertToolsToGeminiFunctionDeclarations([tool]);\n functionDeclarations.push(...funcDecls);\n }\n }\n\n // Add function declarations as a single tool if we have any\n if (functionDeclarations.length > 0) {\n geminiTools.push({\n functionDeclarations,\n });\n }\n\n return geminiTools;\n};\n\n/**\n * Converts tool choice to Gemini's function calling configuration.\n *\n * This converter transforms LangChain's tool choice format into Gemini's\n * `toolConfig.functionCallingConfig` format. The configuration is only\n * created when tools are present (`hasTools` is true).\n *\n * @remarks\n * The conversion logic:\n * - If `hasTools` is `false`, returns `undefined` (no config needed)\n * - If `toolChoice` is provided, converts it to a Gemini mode:\n * - `\"auto\"` → `\"AUTO\"` - Model decides whether to call functions\n * - `\"any\"` or `\"required\"` → `\"ANY\"` - Model must call at least one function\n * - `\"none\"` → `\"NONE\"` - Model cannot call functions\n * - Function name string → `\"ANY\"` - Forces function use\n * - Object with `mode` → Maps the mode string to Gemini format\n * - Object with `function` → `\"ANY\"` - Forces function use\n * - If `toolChoice` is not provided but tools exist, defaults to `\"AUTO\"`\n *\n * Gemini's function calling modes:\n * - **AUTO**: The model can choose whether to call functions based on the conversation\n * - **ANY**: The model must call at least one function before responding\n * - **NONE**: The model cannot call any functions\n *\n * When a specific function is requested (via function name string or `{ function: { name } }`),\n * the converter uses `\"ANY\"` mode to force function usage, as Gemini doesn't support\n * specifying individual functions in the mode.\n *\n * @example\n * ```typescript\n * // With explicit tool choice\n * convertToolChoiceToGeminiConfig(\"auto\", true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // Required mode - must call a function\n * convertToolChoiceToGeminiConfig(\"any\", true);\n * // Returns: { functionCallingConfig: { mode: \"ANY\" } }\n *\n * // Without tool choice (defaults to AUTO when tools exist)\n * convertToolChoiceToGeminiConfig(undefined, true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // No tools - returns undefined\n * convertToolChoiceToGeminiConfig(\"auto\", false);\n * // Returns: undefined\n * ```\n *\n * @param toolChoice - The tool choice option from LangChain (string, object, or undefined)\n * @param hasTools - Whether tools are present. If false, returns undefined\n * @returns The Gemini tool configuration object, or undefined if no tools are present\n */\nexport function convertToolChoiceToGeminiConfig(\n toolChoice: ToolChoice | undefined,\n hasTools: boolean\n): Gemini.Tools.ToolConfig | undefined {\n if (!hasTools || toolChoice === undefined) {\n return undefined;\n }\n\n let mode: Gemini.Tools.FunctionCallingConfigMode | undefined;\n let allowedFunctionNames: string[] | undefined;\n\n let toolChoiceMode: string | undefined;\n let toolChoiceFunction: string | string[] | undefined;\n if (typeof toolChoice === \"object\") {\n toolChoiceMode = toolChoice.mode;\n toolChoiceFunction = toolChoice.function?.name;\n } else {\n toolChoiceMode = toolChoice;\n }\n\n if (toolChoiceMode === \"auto\") {\n mode = \"AUTO\";\n } else if (toolChoiceMode === \"any\" || toolChoiceMode === \"required\") {\n mode = \"ANY\";\n } else if (toolChoiceMode === \"none\") {\n mode = \"NONE\";\n } else if (typeof toolChoiceMode === \"string\") {\n mode = \"ANY\";\n toolChoiceFunction = toolChoiceMode;\n }\n\n if (toolChoiceFunction) {\n if (Array.isArray(toolChoiceFunction)) {\n allowedFunctionNames = toolChoiceFunction;\n } else {\n allowedFunctionNames = [toolChoiceFunction];\n }\n }\n\n const functionCallingConfig: Gemini.Tools.FunctionCallingConfig = {};\n if (allowedFunctionNames?.length) {\n functionCallingConfig.allowedFunctionNames = allowedFunctionNames;\n }\n if (mode) {\n functionCallingConfig.mode = mode;\n }\n return {\n functionCallingConfig,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoCA,SAAS,iBAEP,KAEqB;CACrB,IAAI,CAAC,MAAM,QAAQ,IAAI,IAAI,GACzB,OAAO;CAGT,MAAM,MAAM,IAAI,KAAK;CACrB,MAAM,YAAY,IAAI,KAAK,QAAQ,MAAM;CACzC,IAAI,QAAQ,KAAK,aAAa,GAAG;EAG/B,MAAM,YAAY,cAAc,IAAI,IAAI;EACxC,IAAI,OAAO,IAAI,KAAK;EACpB,IAAI,WAAW;CACjB,OAAO,IAAI,QAAQ,KAAK,cAAc,GAEpC,MAAM,IAAI,kBACR,0GACF;MACK,IAAI,QAAQ,GAGjB,IAAI,OAAO,KAAK,KAAK;MAChB,IAAI,QAAQ,GAEjB,MAAM,IAAI,kBACR,2FACF;MAGA,MAAM,IAAI,kBACR,qFACF;CAEF,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,SAAS,2BAEP,KAC4C;CAC5C,IAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;EAC3C,MAAM,SAAS,EAAE,GAAG,IAAI;EAExB,IAAI,0BAA0B,QAC5B,OAAO,OAAO;EAGhB,iBAAiB,MAAM;EAEvB,KAAK,MAAM,OAAO,QAChB,IAAI,OAAO,QACL;OAAA,MAAM,QAAQ,OAAO,IAAI,GAC3B,OAAO,OAAO,OAAO,IAAI,CAAC,IAAI,0BAA0B;QACnD,IAAI,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,MAC5D,OAAO,OAAO,2BAA2B,OAAO,IAAI;EAAA;EAK1D,OAAO;CACT;CAEA,OAAO;AACT;;;;;;;;;;;;;AAcA,SAAgB,yBAId,QAIqB;CAUrB,MAAM,EAAE,SAAS,GAAG,SALD,2BACjB,mBAAmB,MAAM,KAAK,qBAAqB,MAAM,IACrD,aAAa,MAAM,IACnB,MAEgC;CACtC,OAAO;AACT;;;;;;;;;AAUA,SAAS,6BAEP,QACqB;CAErB,MAAM,EAAE,SAAS,GAAG,SADD,2BAA2B,MACR;CAEtC,OAAO;AACT;;;;;;;;;;;;;;;;;AAkBA,SAAS,aAAa,MAA2C;CAC/D,IAAI,OAAO,SAAS,YAAY,SAAS,MACvC,OAAO;CAcT,OAAO;EAVL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAGwB,CAAC,CAAC,MAAM,SAAS,QAAQ,IAAI;AACzD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyDA,MAAa,4CAGR,UAAU;CACb,MAAM,uBAA2D,CAAC;CAElE,KAAK,MAAM,QAAQ,OAAO;EAExB,IAAI,aAAa,IAAI,KAAK,EAAE,0BAA0B,OACpD;EAIF,IACE,aAAa,IAAI,KACjB,0BAA0B,QAC1B,MAAM,QAAQ,KAAK,oBAAoB,GACvC;GACA,qBAAqB,KAAK,GAAG,KAAK,oBAAoB;GACtD;EACF;EAGA,IAAI,gBAAgB,IAAI,GAAG;GACzB,MAAM,aAAa,yBAAyB,KAAK,MAAM;GACvD,qBAAqB,KAAK;IACxB,MAAM,KAAK;IACX,aAAa,KAAK,eAAe;IACjC,YAAY;GACd,CAAC;GACD;EACF;EAGA,IAAI,aAAa,IAAI,GAAG;GACtB,qBAAqB,KAAK;IACxB,MAAM,KAAK,SAAS;IACpB,aACE,KAAK,SAAS,eAAe;IAC/B,YAAY,6BAA6B,KAAK,SAAS,UAAU;GACnE,CAAC;GACD;EACF;EAGA,IACE,OAAO,SAAS,YAChB,SAAS,QACT,UAAU,QACV,OAAO,KAAK,SAAS,UACrB;GACA,MAAM,WAAW;GACjB,qBAAqB,KAAK;IACxB,MAAM,SAAS;IACf,aAAa,SAAS,eAAe;IACrC,YAAY,SAAS;GACvB,CAAC;GACD;EACF;EAEA,MAAM,IAAI,iBAAiB,IAAI;CACjC;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA,MAAa,6BAGR,UAAU;CACb,IAAI,MAAM,WAAW,GACnB,OAAO,CAAC;CAGV,MAAM,cAA6B,CAAC;CACpC,MAAM,uBAA2D,CAAC;CAGlE,KAAK,MAAM,QAAQ,OACjB,IAAI,aAAa,IAAI,GACnB,IAAI,0BAA0B,MAE5B,YAAY,KAAK,IAAI;MAGrB,YAAY,KAAK,IAAI;MAElB,IAAI,2BAA2B,MAKpC,YAAY,KAAK,EAFf,cAAc,CAAC,EAES,CAAC;MACtB;EAEL,MAAM,YAAY,yCAAyC,CAAC,IAAI,CAAC;EACjE,qBAAqB,KAAK,GAAG,SAAS;CACxC;CAIF,IAAI,qBAAqB,SAAS,GAChC,YAAY,KAAK,EACf,qBACF,CAAC;CAGH,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,SAAgB,gCACd,YACA,UACqC;CACrC,IAAI,CAAC,YAAY,eAAe,KAAA,GAC9B;CAGF,IAAI;CACJ,IAAI;CAEJ,IAAI;CACJ,IAAI;CACJ,IAAI,OAAO,eAAe,UAAU;EAClC,iBAAiB,WAAW;EAC5B,qBAAqB,WAAW,UAAU;CAC5C,OACE,iBAAiB;CAGnB,IAAI,mBAAmB,QACrB,OAAO;MACF,IAAI,mBAAmB,SAAS,mBAAmB,YACxD,OAAO;MACF,IAAI,mBAAmB,QAC5B,OAAO;MACF,IAAI,OAAO,mBAAmB,UAAU;EAC7C,OAAO;EACP,qBAAqB;CACvB;CAEA,IAAI,oBACF,IAAI,MAAM,QAAQ,kBAAkB,GAClC,uBAAuB;MAEvB,uBAAuB,CAAC,kBAAkB;CAI9C,MAAM,wBAA4D,CAAC;CACnE,IAAI,sBAAsB,QACxB,sBAAsB,uBAAuB;CAE/C,IAAI,MACF,sBAAsB,OAAO;CAE/B,OAAO,EACL,sBACF;AACF"}
1
+ {"version":3,"file":"tools.js","names":[],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { isOpenAITool } from \"@langchain/core/language_models/base\";\nimport {\n InteropZodType,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n type JsonSchema7Type,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type {\n BindToolsInput,\n ToolChoice,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { InvalidInputError, InvalidToolError } from \"../utils/errors.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\n\n/**\n * Adjusts a JSON Schema object type to be compatible with Gemini's function schema format.\n *\n * Gemini's function schema format has specific requirements:\n * - Type must be a string, not an array\n * - Nullable types should use the `nullable` property instead of including \"null\" in a type array\n * - Union types are not supported\n *\n * @param obj - The JSON Schema object to adjust\n * @returns The adjusted object with type converted to Gemini-compatible format\n * @throws {Error} If the type is a union type or null-only type, which Gemini cannot handle\n *\n * @internal\n */\nfunction adjustObjectType(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, any> {\n if (!Array.isArray(obj.type)) {\n return obj;\n }\n\n const len = obj.type.length;\n const nullIndex = obj.type.indexOf(\"null\");\n if (len === 2 && nullIndex >= 0) {\n // There are only two values set for the type, and one of them is \"null\".\n // Set the type to the other one and set nullable to true.\n const typeIndex = nullIndex === 0 ? 1 : 0;\n obj.type = obj.type[typeIndex];\n obj.nullable = true;\n } else if (len === 1 && nullIndex === 0) {\n // This is nullable only without a type, which doesn't make sense for Gemini\n throw new InvalidInputError(\n \"Gemini does not support null-only types in function schemas. Provide a non-null type alongside nullable.\"\n );\n } else if (len === 1) {\n // Although an array, it has only one value.\n // So set it to the string to match what Gemini expects.\n obj.type = obj?.type[0];\n } else if (len === 0) {\n // Empty type array is invalid\n throw new InvalidInputError(\n \"Gemini does not support empty type arrays in function schemas. Provide at least one type.\"\n );\n } else {\n // Anything else could be a union type, so reject it.\n throw new InvalidInputError(\n \"Gemini does not support union types in function schemas. Use a single type instead.\"\n );\n }\n return obj;\n}\n\n/**\n * Recursively removes unsupported properties from a JSON Schema object to make it\n * compatible with Gemini's function schema format.\n *\n * Gemini's function schema format does not support:\n * - `additionalProperties` property\n * - Array types (must be converted to string types with nullable flag)\n * - Union types\n *\n * @param obj - The JSON Schema object to clean\n * @returns A cleaned GeminiFunctionSchema object\n *\n * @internal\n */\nfunction removeAdditionalProperties(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n): Gemini.Tools.Schema & { $schema?: string } {\n if (typeof obj === \"object\" && obj !== null) {\n const newObj = { ...obj };\n\n if (\"additionalProperties\" in newObj) {\n delete newObj.additionalProperties;\n }\n\n adjustObjectType(newObj);\n\n for (const key in newObj) {\n if (key in newObj) {\n if (Array.isArray(newObj[key])) {\n newObj[key] = newObj[key].map(removeAdditionalProperties);\n } else if (typeof newObj[key] === \"object\" && newObj[key] !== null) {\n newObj[key] = removeAdditionalProperties(newObj[key]);\n }\n }\n }\n\n return newObj as Gemini.Tools.Schema;\n }\n\n return obj as Gemini.Tools.Schema;\n}\n\n/**\n * Converts a Zod schema or JSON Schema to Gemini function parameters format.\n *\n * This function handles the conversion of schema definitions from various formats\n * (Zod schemas, JSON Schema 7) to Gemini's function schema format. It removes\n * unsupported properties and adjusts type definitions to be compatible with Gemini.\n *\n * @param schema - The schema to convert (Zod schema or JSON Schema 7)\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nexport function schemaToGeminiParameters<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n>(\n schema:\n | SerializableSchema<RunOutput>\n | InteropZodType<RunOutput>\n | JsonSchema7Type\n): Gemini.Tools.Schema {\n // Gemini doesn't accept either the $schema or additionalProperties\n // attributes, so we need to explicitly remove them.\n // Zod sometimes also makes an array of type (because of .nullish()),\n // which needs cleaning up.\n const jsonSchema = removeAdditionalProperties(\n isInteropZodSchema(schema) || isSerializableSchema(schema)\n ? toJsonSchema(schema)\n : schema\n );\n const { $schema, ...rest } = jsonSchema;\n return rest;\n}\n\n/**\n * Converts a JSON Schema object to Gemini function parameters format.\n *\n * @param schema - The JSON Schema object to convert\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nfunction jsonSchemaToGeminiParameters(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n schema: Record<string, any>\n): Gemini.Tools.Schema {\n const jsonSchema = removeAdditionalProperties(schema);\n const { $schema, ...rest } = jsonSchema;\n\n return rest;\n}\n\n/**\n * Type guard to check if a tool is already in Gemini's native format.\n *\n * Gemini tools can be:\n * - Function declarations (`functionDeclarations`)\n * - Code execution tools (`codeExecution`)\n * - Google Search retrieval tools (`googleSearchRetrieval` or `googleSearch`)\n *\n * The full list of tool types are those defined by the `Gemini.Tool` type.\n * Updating that list requires a change to the `geminiToolAttributes` below.\n *\n * @param tool - The tool to check\n * @returns `true` if the tool is already in Gemini format, `false` otherwise\n *\n * @internal\n */\nfunction isGeminiTool(tool: BindToolsInput): tool is Gemini.Tool {\n if (typeof tool !== \"object\" || tool === null) {\n return false;\n }\n\n const geminiToolAttributes = [\n \"functionDeclarations\",\n \"codeExecution\",\n \"googleSearch\",\n \"urlContext\",\n \"googleMaps\",\n \"fileSearch\",\n \"computerUse\",\n \"mcpServers\",\n ];\n\n return geminiToolAttributes.some((attr) => attr in tool);\n}\n\n/**\n * Converts an array of LangChain tools to Gemini function declarations.\n *\n * This converter transforms various tool formats into Gemini's function declaration\n * format. It supports:\n * - LangChain structured tools (with Zod schemas)\n * - OpenAI format tools (with JSON Schema)\n * - Gemini-native function declarations (passed through)\n * - Raw function declaration objects\n *\n * @remarks\n * The converter handles each tool type differently:\n * - **LangChain tools**: Extracts name, description, and schema, converting the schema\n * to Gemini-compatible format\n * - **OpenAI tools**: Extracts function name, description, and parameters from the\n * OpenAI tool format\n * - **Gemini tools**: If the tool contains `functionDeclarations`, extracts and returns\n * them directly. Non-function Gemini tools (like `codeExecution`) are skipped.\n * - **Raw objects**: Assumes the object is a function declaration and validates it has\n * a `name` property\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather for a location\",\n * schema: z.object({\n * location: z.string().describe(\"The city name\"),\n * unit: z.enum([\"celsius\", \"fahrenheit\"]).optional()\n * }),\n * func: async (args) => { /* ... *\\/ }\n * });\n *\n * const declarations = convertToolsToGeminiFunctionDeclarations([weatherTool]);\n * // Returns: [{\n * // name: \"get_weather\",\n * // description: \"Get the weather for a location\",\n * // parameters: {\n * // type: \"object\",\n * // properties: {\n * // location: { type: \"string\", description: \"The city name\" },\n * // unit: { type: \"string\", enum: [\"celsius\", \"fahrenheit\"], nullable: true }\n * // },\n * // required: [\"location\"]\n * // }\n * // }]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Gemini function declarations ready for API submission\n * @throws {Error} If a tool cannot be converted (invalid format)\n */\nexport const convertToolsToGeminiFunctionDeclarations: Converter<\n BindToolsInput[],\n Gemini.Tools.FunctionDeclaration[]\n> = (tools) => {\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n for (const tool of tools) {\n // Skip if already in Gemini format (non-function tools)\n if (isGeminiTool(tool) && !(\"functionDeclarations\" in tool)) {\n continue;\n }\n\n // Handle Gemini format with functionDeclarations\n if (\n isGeminiTool(tool) &&\n \"functionDeclarations\" in tool &&\n Array.isArray(tool.functionDeclarations)\n ) {\n functionDeclarations.push(...tool.functionDeclarations);\n continue;\n }\n\n // Handle LangChain structured tools\n if (isLangChainTool(tool)) {\n const jsonSchema = schemaToGeminiParameters(tool.schema);\n functionDeclarations.push({\n name: tool.name,\n description: tool.description ?? `A function available to call.`,\n parameters: jsonSchema,\n });\n continue;\n }\n\n // Handle OpenAI format tools\n if (isOpenAITool(tool)) {\n functionDeclarations.push({\n name: tool.function.name,\n description:\n tool.function.description ?? `A function available to call.`,\n parameters: jsonSchemaToGeminiParameters(tool.function.parameters),\n });\n continue;\n }\n\n // Handle raw function declaration objects\n if (\n typeof tool === \"object\" &&\n tool !== null &&\n \"name\" in tool &&\n typeof tool.name === \"string\"\n ) {\n const funcDecl = tool as Partial<Gemini.Tools.FunctionDeclaration>;\n functionDeclarations.push({\n name: funcDecl.name!,\n description: funcDecl.description ?? `A function available to call.`,\n parameters: funcDecl.parameters,\n });\n continue;\n }\n\n throw new InvalidToolError(tool);\n }\n\n return functionDeclarations;\n};\n\n/**\n * Converts an array of tools to Gemini's Tools array format.\n *\n * This converter groups function declarations into a single tool object while\n * preserving Gemini-native tools (like code execution and Google Search retrieval)\n * as separate tool objects. This matches Gemini's API structure where:\n * - Function declarations are grouped together in a single tool\n * - Non-function tools (codeExecution, googleSearchRetrieval) are separate tools\n *\n * @remarks\n * The conversion process:\n * 1. Separates Gemini-native tools from function declarations\n * 2. Converts LangChain/OpenAI tools to function declarations\n * 3. Groups all function declarations into a single Tool object\n * 4. Returns an array with the grouped function declarations tool plus any\n * non-function Gemini tools\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather\",\n * schema: z.object({ location: z.string() }),\n * func: async () => {}\n * });\n *\n * const geminiCodeTool: Gemini.Tool = {\n * codeExecution: { enabled: true }\n * };\n *\n * const tools = convertToolsToGeminiTools([weatherTool, geminiCodeTool]);\n * // Returns: [\n * // { functionDeclarations: [{ name: \"get_weather\", ... }] },\n * // { codeExecution: { enabled: true } }\n * // ]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Tool objects ready for API submission\n */\nexport const convertToolsToGeminiTools: Converter<\n BindToolsInput[],\n Gemini.Tool[]\n> = (tools) => {\n if (tools.length === 0) {\n return [];\n }\n\n const geminiTools: Gemini.Tool[] = [];\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n // Separate Gemini-native tools from function declarations\n for (const tool of tools) {\n if (isGeminiTool(tool)) {\n if (\"functionDeclarations\" in tool) {\n // Already a Gemini tool with function declarations\n geminiTools.push(tool);\n } else {\n // Non-function Gemini tool (codeExecution, googleSearch, etc.)\n geminiTools.push(tool);\n }\n } else if (\"googleSearchRetrieval\" in tool) {\n // Convert the obsolete search into the modern one\n const searchTool: Gemini.Tool = {\n googleSearch: {},\n };\n geminiTools.push(searchTool);\n } else {\n // Convert to function declaration\n const funcDecls = convertToolsToGeminiFunctionDeclarations([tool]);\n functionDeclarations.push(...funcDecls);\n }\n }\n\n // Add function declarations as a single tool if we have any\n if (functionDeclarations.length > 0) {\n geminiTools.push({\n functionDeclarations,\n });\n }\n\n return geminiTools;\n};\n\n/**\n * Converts tool choice to Gemini's function calling configuration.\n *\n * This converter transforms LangChain's tool choice format into Gemini's\n * `toolConfig.functionCallingConfig` format. The configuration is only\n * created when tools are present (`hasTools` is true).\n *\n * @remarks\n * The conversion logic:\n * - If `hasTools` is `false`, returns `undefined` (no config needed)\n * - If `toolChoice` is provided, converts it to a Gemini mode:\n * - `\"auto\"` → `\"AUTO\"` - Model decides whether to call functions\n * - `\"any\"` or `\"required\"` → `\"ANY\"` - Model must call at least one function\n * - `\"none\"` → `\"NONE\"` - Model cannot call functions\n * - Function name string → `\"ANY\"` - Forces function use\n * - Object with `mode` → Maps the mode string to Gemini format\n * - Object with `function` → `\"ANY\"` - Forces function use\n * - If `toolChoice` is not provided but tools exist, defaults to `\"AUTO\"`\n *\n * Gemini's function calling modes:\n * - **AUTO**: The model can choose whether to call functions based on the conversation\n * - **ANY**: The model must call at least one function before responding\n * - **NONE**: The model cannot call any functions\n *\n * When a specific function is requested (via function name string or `{ function: { name } }`),\n * the converter uses `\"ANY\"` mode to force function usage, as Gemini doesn't support\n * specifying individual functions in the mode.\n *\n * @example\n * ```typescript\n * // With explicit tool choice\n * convertToolChoiceToGeminiConfig(\"auto\", true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // Required mode - must call a function\n * convertToolChoiceToGeminiConfig(\"any\", true);\n * // Returns: { functionCallingConfig: { mode: \"ANY\" } }\n *\n * // Without tool choice (defaults to AUTO when tools exist)\n * convertToolChoiceToGeminiConfig(undefined, true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // No tools - returns undefined\n * convertToolChoiceToGeminiConfig(\"auto\", false);\n * // Returns: undefined\n * ```\n *\n * @param toolChoice - The tool choice option from LangChain (string, object, or undefined)\n * @param hasTools - Whether tools are present. If false, returns undefined\n * @returns The Gemini tool configuration object, or undefined if no tools are present\n */\nexport function convertToolChoiceToGeminiConfig(\n toolChoice: ToolChoice | undefined,\n hasTools: boolean\n): Gemini.Tools.ToolConfig | undefined {\n if (!hasTools || toolChoice === undefined) {\n return undefined;\n }\n\n let mode: Gemini.Tools.FunctionCallingConfigMode | undefined;\n let allowedFunctionNames: string[] | undefined;\n\n let toolChoiceMode: string | undefined;\n let toolChoiceFunction: string | string[] | undefined;\n if (typeof toolChoice === \"object\") {\n toolChoiceMode = toolChoice.mode;\n toolChoiceFunction = toolChoice.function?.name;\n } else {\n toolChoiceMode = toolChoice;\n }\n\n if (toolChoiceMode === \"auto\") {\n mode = \"AUTO\";\n } else if (toolChoiceMode === \"any\" || toolChoiceMode === \"required\") {\n mode = \"ANY\";\n } else if (toolChoiceMode === \"none\") {\n mode = \"NONE\";\n } else if (typeof toolChoiceMode === \"string\") {\n mode = \"ANY\";\n toolChoiceFunction = toolChoiceMode;\n }\n\n if (toolChoiceFunction) {\n if (Array.isArray(toolChoiceFunction)) {\n allowedFunctionNames = toolChoiceFunction;\n } else {\n allowedFunctionNames = [toolChoiceFunction];\n }\n }\n\n const functionCallingConfig: Gemini.Tools.FunctionCallingConfig = {};\n if (allowedFunctionNames?.length) {\n functionCallingConfig.allowedFunctionNames = allowedFunctionNames;\n }\n if (mode) {\n functionCallingConfig.mode = mode;\n }\n return {\n functionCallingConfig,\n };\n}\n"],"mappings":"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"}
@@ -146,18 +146,20 @@ async function getGCPCredentialsAccessToken(credentials) {
146
146
  const cacheKey = cacheKeyUrl.toString();
147
147
  const cacheValue = cache.get(cacheKey);
148
148
  let now = Math.floor(Date.now() / 1e3);
149
- if (cacheValue) if (cacheValue.created > now - CACHE_MAX_AGE) {
150
- let token = await cacheValue.promise;
151
- if (token.expires > now) return token.token;
152
- else {
153
- const nextValue = cache.get(cacheKey);
154
- if (nextValue && nextValue !== cacheValue) {
155
- token = await nextValue.promise;
156
- if (token.expires > now) return token.token;
157
- else cache.delete(cacheKey);
149
+ if (cacheValue) {
150
+ if (cacheValue.created > now - CACHE_MAX_AGE) {
151
+ let token = await cacheValue.promise;
152
+ if (token.expires > now) return token.token;
153
+ else {
154
+ const nextValue = cache.get(cacheKey);
155
+ if (nextValue && nextValue !== cacheValue) {
156
+ token = await nextValue.promise;
157
+ if (token.expires > now) return token.token;
158
+ else cache.delete(cacheKey);
159
+ }
158
160
  }
159
- }
160
- } else cache.delete(cacheKey);
161
+ } else cache.delete(cacheKey);
162
+ }
161
163
  const promise = require_misc.iife(async () => {
162
164
  now = Math.floor(Date.now() / 1e3);
163
165
  const jwt = await getGCPCustomToken(credentials);