@langchain/google 0.2.5 → 0.2.6
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.
- package/CHANGELOG.md +24 -0
- package/dist/chat_models/base.cjs +21 -4
- package/dist/chat_models/base.cjs.map +1 -1
- package/dist/chat_models/base.js +22 -5
- package/dist/chat_models/base.js.map +1 -1
- package/dist/converters/messages.cjs +14 -8
- package/dist/converters/messages.cjs.map +1 -1
- package/dist/converters/messages.js +14 -8
- package/dist/converters/messages.js.map +1 -1
- package/dist/converters/tools.cjs +51 -30
- package/dist/converters/tools.cjs.map +1 -1
- package/dist/converters/tools.js +51 -31
- package/dist/converters/tools.js.map +1 -1
- package/dist/utils/stream_events.cjs +31 -7
- package/dist/utils/stream_events.cjs.map +1 -1
- package/dist/utils/stream_events.js +31 -7
- package/dist/utils/stream_events.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","names":["uuidv4"],"sources":["../../src/converters/messages.ts"],"sourcesContent":["import {\n BaseMessage,\n AIMessage,\n isDataContentBlock,\n ContentBlock,\n parseBase64DataUrl,\n SystemMessage,\n HumanMessage,\n ToolMessage,\n ChatMessage,\n StandardContentBlockConverter,\n convertToProviderContentBlock,\n type Data,\n UsageMetadata,\n InputTokenDetails,\n OutputTokenDetails,\n ModalitiesTokenDetails,\n MessageContentImageUrl,\n MessageContentComplex,\n MessageContentText,\n} from \"@langchain/core/messages\";\nimport { v4 as uuidv4 } from \"@langchain/core/utils/uuid\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { iife } from \"../utils/misc.js\";\nimport { InvalidInputError, ToolCallNotFoundError } from \"../utils/errors.js\";\n\n/**\n * Standard content block converter for Google Gemini API.\n * Converts deprecated Data content blocks to Gemini Part format.\n *\n * @deprecated This converter is for backward compatibility with older message formats.\n * Use ContentBlock.Multimodal.Standard instead.\n */\nexport const geminiContentBlockConverter: StandardContentBlockConverter<{\n text: Gemini.Part;\n image: Gemini.Part;\n audio: Gemini.Part;\n file: Gemini.Part;\n}> = {\n providerName: \"ChatGoogle\",\n\n fromStandardTextBlock(block: Data.StandardTextBlock): Gemini.Part {\n return { text: block.text };\n },\n\n fromStandardImageBlock(block: Data.StandardImageBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 image blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType: parsed.mime_type || block.mime_type || \"image/jpeg\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"image/jpeg\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"image/jpeg\",\n fileUri: block.id,\n },\n };\n }\n }\n\n throw new InvalidInputError(\n `Image content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\".`\n );\n },\n\n fromStandardAudioBlock(block: Data.StandardAudioBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 audio blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType: parsed.mime_type || block.mime_type || \"audio/mpeg\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"audio/mpeg\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"audio/mpeg\",\n fileUri: block.id,\n },\n };\n }\n }\n\n throw new InvalidInputError(\n `Audio content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\".`\n );\n },\n\n fromStandardFileBlock(block: Data.StandardFileBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 file blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType:\n parsed.mime_type ||\n block.mime_type ||\n \"application/octet-stream\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"application/octet-stream\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"application/octet-stream\",\n fileUri: block.id,\n },\n };\n }\n }\n\n // Handle text source type (for plain text file blocks)\n if (\n \"source_type\" in block &&\n block.source_type === \"text\" &&\n \"text\" in block\n ) {\n return {\n text: block.text,\n };\n }\n\n throw new InvalidInputError(\n `File content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\", \"text\".`\n );\n },\n};\n\nconst MEDIA_BLOCK_TYPES = new Set([\n \"media\",\n \"image\",\n \"audio\",\n \"video\",\n \"file\",\n \"text-plain\",\n]);\n\nfunction isMediaContentBlock(item: unknown): boolean {\n if (typeof item !== \"object\" || item === null) {\n return false;\n }\n if (isDataContentBlock(item)) {\n return item.source_type !== \"text\";\n }\n if (\"image_url\" in item || \"inlineData\" in item || \"fileData\" in item) {\n return true;\n }\n return MEDIA_BLOCK_TYPES.has((item as { type?: unknown }).type as string);\n}\n\nfunction stripMediaBlocksForFunctionResponse(content: unknown): unknown {\n if (!Array.isArray(content)) {\n return content;\n }\n return content.filter((item) => !isMediaContentBlock(item));\n}\n\nfunction convertStandardDataContentBlockToGeminiPart(\n block: ContentBlock.Multimodal.Data\n): Gemini.Part | null {\n function uint8arrayToString(data: Uint8Array): string {\n return btoa(\n Array.from(data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n }\n\n function extractMimeType(str: string): {\n mimeType: string | null;\n data: string | null;\n } {\n if (str.startsWith(\"data:\")) {\n return {\n mimeType: str.split(\":\")[1].split(\";\")[0],\n data: str.split(\",\")[1],\n };\n }\n return {\n mimeType: null,\n data: null,\n };\n }\n\n if (\"mimeType\" in block && \"data\" in block) {\n const mimeType = block.mimeType!;\n const data: string =\n typeof block.data === \"string\"\n ? block.data\n : uint8arrayToString(block.data!);\n return {\n inlineData: {\n mimeType,\n data,\n },\n };\n } else if (\"mimeType\" in block && \"url\" in block) {\n const mimeType = block.mimeType!;\n const fileUri = block.url!;\n return {\n fileData: {\n mimeType,\n fileUri,\n },\n };\n } else if (\"url\" in block && block.url?.startsWith(\"data:\")) {\n const { mimeType, data } = extractMimeType(block.url!);\n if (mimeType && data) {\n return {\n inlineData: {\n mimeType,\n data,\n },\n };\n }\n }\n // FIXME - report this somehow?\n return null;\n}\n\nfunction convertStandardVideoContentBlockToGeminiPart(\n block: ContentBlock.Multimodal.Video\n): Gemini.Part | null {\n const ret: Gemini.Part | null =\n convertStandardDataContentBlockToGeminiPart(block);\n if (ret && block.metadata && \"videoMetadata\" in block.metadata) {\n (ret as Gemini.Part.FileData).videoMetadata = block.metadata.videoMetadata!;\n }\n return ret;\n}\n\nfunction convertStandardReasoningBlockToGeminiPart(\n block: ContentBlock.Reasoning\n): Gemini.Part | null {\n // Reasoning blocks created from Gemini should contain the base\n // standard block they would be if \"thought\" wasn't set to true.\n // If it exists, we'll use that. Otherwise, we'll assume it was\n // originally a text block.\n const ret =\n \"reasoningContentBlock\" in block && block.reasoningContentBlock\n ? convertStandardContentBlockToGeminiPart(\n block.reasoningContentBlock as ContentBlock.Standard\n )\n : { text: block.reasoning };\n\n // Then add that this came from the thinking process\n if (ret) {\n ret.thought = true;\n }\n\n return ret;\n}\n\n/**\n * Converts a single LangChain standard content block (v1 format)\n * into a Gemini.Part.\n *\n * This is intended to be called from `convertStandardContentMessageToGeminiContent`\n */\nfunction convertStandardContentBlockToGeminiPart(\n block: ContentBlock.Standard\n): Gemini.Part | null {\n function baseGeminiPart(): Gemini.Part | null {\n switch (block.type) {\n case \"text\":\n return { text: block.text };\n case \"reasoning\":\n return convertStandardReasoningBlockToGeminiPart(block);\n case \"image\":\n case \"audio\":\n case \"text-plain\":\n case \"file\":\n return convertStandardDataContentBlockToGeminiPart(block);\n case \"video\":\n return convertStandardVideoContentBlockToGeminiPart(block);\n default:\n return null;\n }\n }\n\n const ret: Gemini.Part | null = baseGeminiPart();\n if (ret) {\n if (\"thoughtSignature\" in block) {\n ret.thoughtSignature = block.thoughtSignature! as string;\n }\n }\n return ret;\n}\n\n/**\n * Converts a single LangChain message with standard content blocks (v1 format) to Gemini Content.\n * This handles messages that have `response_metadata.output_version === \"v1\"`.\n *\n * This is intended to be called from `convertMessagesToGeminiContent`\n */\nfunction convertStandardContentMessageToGeminiContent(\n message: BaseMessage,\n messages: BaseMessage[]\n): Gemini.Content | null {\n // Skip system messages - they're handled separately\n if (SystemMessage.isInstance(message)) {\n return null;\n }\n\n let role: Gemini.Role;\n if (HumanMessage.isInstance(message)) {\n role = \"user\";\n } else if (AIMessage.isInstance(message)) {\n role = \"model\";\n } else if (ToolMessage.isInstance(message)) {\n // Tool messages in Gemini were represented as function responses, but now are \"user\"\n role = \"user\";\n } else if (ChatMessage.isInstance(message)) {\n // Map ChatMessage roles to Gemini roles\n const msgRole = message.role.toLowerCase();\n if (msgRole === \"user\" || msgRole === \"human\") {\n role = \"user\";\n } else if (\n msgRole === \"assistant\" ||\n msgRole === \"ai\" ||\n msgRole === \"model\"\n ) {\n role = \"model\";\n } else if (msgRole === \"function\" || msgRole === \"tool\") {\n role = \"user\";\n } else {\n // Default to user for unknown roles\n role = \"user\";\n }\n } else {\n // Unknown message type - default to user\n role = \"user\";\n }\n\n let parts: Gemini.Part[] = [];\n\n // Process standard content blocks\n const contentBlocks = Array.isArray(message.contentBlocks)\n ? message.contentBlocks\n : [];\n contentBlocks.forEach((block: ContentBlock.Standard) => {\n const contentBlock =\n (message.additional_kwargs\n .originalTextContentBlock as ContentBlock.Standard) || block;\n const part: Gemini.Part | null =\n convertStandardContentBlockToGeminiPart(contentBlock);\n if (part) {\n parts.push(part);\n }\n });\n\n // Convert AIMessage tool_calls to functionCall parts\n if (AIMessage.isInstance(message) && message.tool_calls?.length) {\n for (const toolCall of message.tool_calls) {\n const part = {\n functionCall: {\n name: toolCall.name,\n args: toolCall.args ?? {},\n },\n } as Gemini.Part.FunctionCall;\n if (\"thoughtSignature\" in toolCall) {\n part.thoughtSignature = toolCall.thoughtSignature as string;\n }\n parts.push(part);\n }\n }\n\n // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent = stripMediaBlocksForFunctionResponse(\n message.content\n );\n // Find the matching tool call in a preceding AIMessage to get the function name\n const aiMsg = messages\n .filter(AIMessage.isInstance)\n .find((msg) =>\n msg.tool_calls?.find((tc) => tc.id === message.tool_call_id)\n );\n const matchedToolCall = aiMsg?.tool_calls?.find(\n (tc) => tc.id === message.tool_call_id\n );\n const isGeneratedId = message.tool_call_id.startsWith(\"lc-tool-call-\");\n parts.push({\n functionResponse: {\n ...(isGeneratedId ? {} : { id: message.tool_call_id }),\n name: matchedToolCall?.name ?? message.name ?? \"unknown\",\n response: { result: responseContent },\n },\n });\n\n // For tool messages, keep only the functionResponse part(s): any text is\n // already included in functionResponse.response.result, and a `user`\n // content mixing a functionResponse with text is rejected or corrupted\n // by the API. Mirrors the legacy-path filter.\n parts = parts.filter((part) => \"functionResponse\" in part);\n }\n\n // Only return content if we have parts\n if (parts.length === 0) {\n return null;\n }\n\n return { role, parts };\n}\n\nfunction convertLegacyPartToGeminiPart(\n item: string | ContentBlock | Text\n): Gemini.Part {\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentText(\n content: object\n ): content is MessageContentText {\n return typeof content === \"object\" && content !== null && \"text\" in content;\n }\n\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentImageUrl(\n content: object\n ): content is MessageContentImageUrl {\n return (\n typeof content === \"object\" && content !== null && \"image_url\" in content\n );\n }\n\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentMedia(\n content: object\n ): content is MessageContentComplex {\n return (\n typeof content === \"object\" &&\n content !== null &&\n \"type\" in content &&\n content.type === \"media\"\n );\n }\n\n /**\n * Infers the MIME type from a URL based on its file extension.\n * This is used as a fallback when the MIME type is not provided.\n *\n * @param url - The URL to infer the MIME type from\n * @returns The inferred MIME type or a generic type if it cannot be determined\n */\n function inferMimeTypeFromUrl(url: string): string {\n const mimeTypeMap: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n };\n\n try {\n // Extract the pathname from the URL\n const pathname = new URL(url).pathname;\n // Get the file extension (handle query params and fragments)\n const extension = pathname\n .split(\".\")\n .pop()\n ?.toLowerCase()\n .split(/[?#]/)[0];\n return extension ? mimeTypeMap[extension] : \"application/octet-stream\";\n } catch {\n // If URL parsing fails, try a simple extension extraction\n const match = url.match(/\\.([a-zA-Z0-9]+)(?:[?#]|$)/);\n if (match) {\n const extension = match[1].toLowerCase();\n return mimeTypeMap[extension];\n }\n return \"application/octet-stream\";\n }\n }\n\n function messageContentImageUrlData(\n content: MessageContentImageUrl\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const url: string =\n typeof content.image_url === \"string\"\n ? content.image_url\n : content.image_url.url;\n if (!url) {\n throw new InvalidInputError(\n \"Missing image URL in image_url content block.\"\n );\n }\n\n const dataUrl = parseBase64DataUrl({ dataUrl: url });\n if (dataUrl?.data && dataUrl?.mime_type) {\n return {\n inlineData: {\n data: dataUrl.data,\n mimeType: dataUrl.mime_type,\n },\n };\n } else {\n // Infer MIME type from URL extension\n const mimeType = inferMimeTypeFromUrl(url) || \"image/png\";\n return {\n fileData: {\n mimeType,\n fileUri: url,\n },\n };\n }\n }\n\n function messageContentImageUrl(\n content: MessageContentImageUrl\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const ret = messageContentImageUrlData(content);\n supplementVideoMetadata(content, ret);\n return ret;\n }\n\n function messageContentMediaData(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: Record<string, any>\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n if (\"mimeType\" in content && \"data\" in content) {\n return {\n inlineData: {\n mimeType: content.mimeType,\n data: content.data,\n },\n };\n } else if (\"mimeType\" in content && \"fileUri\" in content) {\n return {\n fileData: {\n mimeType: content.mimeType,\n fileUri: content.fileUri,\n },\n };\n } else {\n // The old version would attempt to read the URL, but we're\n // not in an async function, so we can't do that.\n }\n\n throw new InvalidInputError(\n `Invalid media content: ${JSON.stringify(\n content,\n null,\n 1\n )}. Expected either { mimeType, data } for inline data or { mimeType, fileUri } for file references.`\n );\n }\n\n function supplementVideoMetadata(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: MessageContentImageUrl | Record<string, any>,\n ret: Gemini.Part.InlineData | Gemini.Part.FileData\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n // Add videoMetadata if defined\n if (\"videoMetadata\" in content && typeof ret === \"object\") {\n ret.videoMetadata = content.videoMetadata;\n }\n return ret;\n }\n\n function messageContentMedia(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: Record<string, any>\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const ret = messageContentMediaData(content);\n supplementVideoMetadata(content, ret);\n return ret;\n }\n\n function baseGeminiPart(): Gemini.Part {\n if (typeof item === \"string\") {\n return { text: item };\n } else if (typeof item === \"object\" && item !== null) {\n if (isMessageContentText(item)) {\n return { text: item.text };\n } else if (isDataContentBlock(item)) {\n return convertToProviderContentBlock(item, geminiContentBlockConverter);\n } else if (\"type\" in item && item?.type === \"functionCall\") {\n const { type, functionCall, ...etc } = item;\n return {\n ...etc,\n functionCall,\n } as Gemini.Part.FunctionCall;\n } else if (\"type\" in item && item?.type === \"executableCode\") {\n const { type, executableCode, ...etc } = item;\n return {\n ...etc,\n executableCode,\n } as Gemini.Part.ExecutableCode;\n } else if (\"type\" in item && item?.type === \"codeExecutionResult\") {\n const { type, codeExecutionResult, ...etc } = item;\n return {\n ...etc,\n codeExecutionResult,\n } as Gemini.Part.CodeExecutionResult;\n } else if (isMessageContentImageUrl(item)) {\n return messageContentImageUrl(item);\n } else if (isMessageContentMedia(item)) {\n return messageContentMedia(item);\n }\n }\n return item as Gemini.Part;\n }\n\n const ret = baseGeminiPart();\n const itemRecord = item as Record<string, unknown>;\n if (\"thought\" in itemRecord) {\n ret.thought = itemRecord.thought as boolean;\n }\n if (\"thoughtSignature\" in itemRecord) {\n ret.thoughtSignature = itemRecord.thoughtSignature as string;\n }\n return ret;\n}\n\n/**\n * Converts a single LangChain message with legacy content blocks (v0 format) to Gemini Content.\n * This handles messages that have `response_metadata.output_version === \"v0\"`.\n *\n * This is intended to be called from `convertMessagesToGeminiContent`\n */\nfunction convertLegacyContentMessageToGeminiContent(\n message: BaseMessage,\n messages: BaseMessage[]\n): Gemini.Content | null {\n // Skip system messages - they're handled separately\n if (SystemMessage.isInstance(message)) {\n return null;\n }\n\n const role: Gemini.Role = iife(() => {\n if (HumanMessage.isInstance(message)) {\n return \"user\";\n } else if (AIMessage.isInstance(message)) {\n return \"model\";\n } else if (ToolMessage.isInstance(message)) {\n // Tool messages in Gemini were represented as function responses, but now are \"user\"\n return \"user\";\n } else if (ChatMessage.isInstance(message)) {\n // Map ChatMessage roles to Gemini roles\n const msgRole = message.role.toLowerCase();\n if (msgRole === \"user\" || msgRole === \"human\") {\n return \"user\";\n } else if (\n msgRole === \"assistant\" ||\n msgRole === \"ai\" ||\n msgRole === \"model\"\n ) {\n return \"model\";\n } else if (msgRole === \"function\" || msgRole === \"tool\") {\n return \"user\";\n } else {\n // Default to user for unknown roles\n return \"user\";\n }\n } else {\n // Unknown message type - skip or default to user\n return \"user\";\n }\n });\n\n let parts: Gemini.Part[] = [];\n\n // Handle legacy content formats\n if (typeof message.content === \"string\") {\n // Simple string content\n if (message.content.trim()) {\n parts.push({ text: message.content });\n }\n } else if (Array.isArray(message.content)) {\n // Array of content blocks (legacy format)\n for (const item of message.content) {\n const part = convertLegacyPartToGeminiPart(item);\n parts.push(part);\n }\n }\n\n // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent = stripMediaBlocksForFunctionResponse(\n message.content\n );\n // Find the matching tool call in a preceding AIMessage to get the function name\n const aiMsg = messages\n .filter(AIMessage.isInstance)\n .find((msg) =>\n msg.tool_calls?.find((tc) => tc.id === message.tool_call_id)\n );\n if (!aiMsg) {\n throw new ToolCallNotFoundError(message.tool_call_id);\n }\n const isGeneratedId = message.tool_call_id.startsWith(\"lc-tool-call-\");\n const matchedToolCall = aiMsg.tool_calls?.find(\n (tc) => tc.id === message.tool_call_id\n );\n parts.push({\n functionResponse: {\n ...(isGeneratedId ? {} : { id: message.tool_call_id }),\n name: matchedToolCall?.name ?? message.name ?? \"unknown\",\n response: { result: responseContent },\n },\n });\n\n // For tool messages, drop the redundant text parts - their content is\n // already folded into functionResponse.response.result above - but keep\n // any media parts (inlineData/fileData) as siblings so images/audio/video\n // reach Gemini as real parts instead of being lost inside the JSON string.\n parts = parts.filter(\n (part) =>\n \"functionResponse\" in part || \"inlineData\" in part || \"fileData\" in part\n );\n }\n\n // Only add content if we have parts\n if (parts.length > 0) {\n return { role, parts };\n }\n\n return null;\n}\n\n/**\n * Converts an array of LangChain messages to Gemini API content format.\n *\n * This converter transforms LangChain's message types into the content structure\n * expected by Google's Gemini API.\n *\n * @remarks\n * The converter processes messages in two distinct ways based on their format:\n *\n * **V1 Standard Content Format**:\n * Messages with `output_version: \"v1\"` in their response metadata use the\n * standardized content block format. These are processed through\n * `convertStandardContentMessageToGeminiContent`, which extracts text blocks\n * from the `contentBlocks` array and maps message roles appropriately.\n *\n * **Google Format**:\n * Messages without the v1 marker are processed using generic logic that handles:\n * - String content: Simple text strings are converted to text parts\n * - Array content: Arrays of strings or content blocks are processed individually\n * - Tool messages: Converted to function response parts with tool call metadata\n *\n * @example\n * ```typescript\n * // Simple conversation with text messages\n * const messages = [\n * new HumanMessage(\"What is the weather?\"),\n * new AIMessage(\"I'll check that for you.\")\n * ];\n * const contents = convertMessagesToGeminiContents(messages);\n * // Returns: [\n * // { role: \"user\", parts: [{ text: \"What is the weather?\" }] },\n * // { role: \"model\", parts: [{ text: \"I'll check that for you.\" }] }\n * // ]\n * ```\n */\nexport const convertMessagesToGeminiContents: Converter<\n BaseMessage[],\n Gemini.GenerateContentRequest[\"contents\"]\n> = (messages) => {\n const contents: Gemini.Content[] = [];\n\n for (const message of messages) {\n // const content: Gemini.Content | null = convertContentMessageToGeminiContent(message, messages);\n const content: Gemini.Content | null = iife(() => {\n const outputVersion =\n \"output_version\" in message.response_metadata\n ? (message.response_metadata?.output_version as string)\n : \"v0\";\n switch (outputVersion) {\n case \"v1\":\n return convertStandardContentMessageToGeminiContent(\n message,\n messages\n );\n case \"v0\":\n default:\n return convertLegacyContentMessageToGeminiContent(message, messages);\n }\n });\n if (content) {\n const prev = contents[contents.length - 1];\n if (canMergeInto(prev, content) && content.parts) {\n prev.parts ??= [];\n prev.parts.push(...content.parts);\n } else {\n contents.push(content);\n }\n }\n }\n\n return contents;\n};\n\n/**\n * Returns true if any part of the given content is a functionResponse part\n * (i.e. it was produced from a ToolMessage).\n */\nfunction hasFunctionResponse(content: Gemini.Content): boolean {\n return content.parts?.some((part) => \"functionResponse\" in part) === true;\n}\n\n/**\n * Adjacent same-role contents merge only when they are the same kind of\n * turn:\n *\n * - Both carry functionResponse parts (a run of tool responses answering\n * one model turn's parallel calls): the API requires those to share a\n * single `user` content whose part count matches the call count.\n * - Neither does (plain text turns): coalesced as before #11444.\n *\n * A functionResponse turn never merges with a plain one: a `user` content\n * mixing the two is rejected by newer Gemini models and corrupts\n * generation on older ones.\n */\nfunction canMergeInto(\n prev: Gemini.Content | undefined,\n content: Gemini.Content\n): boolean {\n return (\n prev !== undefined &&\n prev.role === content.role &&\n hasFunctionResponse(prev) === hasFunctionResponse(content)\n );\n}\n\n/**\n * Converts LangChain system messages to Gemini API system instruction format.\n *\n * This converter extracts system messages from a message array and transforms them\n * into the format expected by Google's Gemini API for system instructions. System\n * instructions provide context and guidelines that influence the model's behavior\n * throughout the conversation.\n *\n * @remarks\n * The converter handles two distinct message formats:\n *\n * 1. **V1 Standard Content Format**: Messages with `output_version: \"v1\"` in their\n * response metadata use the standardized content block format. Only text blocks\n * are extracted from the `contentBlocks` array.\n *\n * 2. **Legacy Google Format**: Messages without the v1 marker use the legacy format\n * where content can be:\n * - A simple string (trimmed and converted to a text part)\n * - An array of strings or objects with `type: \"text\"` and a `text` property\n *\n * The converter filters out non-SystemMessage instances and only processes text\n * content, ignoring other content types like images or tool calls that may be\n * present in the message array.\n *\n * @example\n * ```typescript\n * // Simple string system message\n * const messages = [\n * new SystemMessage(\"You are a helpful assistant.\")\n * ];\n * const instruction = convertMessagesToGeminiSystemInstruction(messages);\n * // Returns: { parts: [{ text: \"You are a helpful assistant.\" }] }\n * ```\n *\n * @param messages - Array of LangChain messages to extract system instructions from\n * @returns A Gemini system instruction object with text parts, or undefined if no\n * system messages with text content are found\n */\nexport const convertMessagesToGeminiSystemInstruction: Converter<\n BaseMessage[],\n Gemini.GenerateContentRequest[\"systemInstruction\"]\n> = (messages) => {\n const systemParts: Gemini.Part[] = [];\n\n for (const message of messages) {\n if (SystemMessage.isInstance(message)) {\n if (message.text.trim()) {\n systemParts.push({ text: message.text });\n }\n }\n }\n\n if (systemParts.length === 0) {\n return undefined;\n }\n\n return { parts: systemParts };\n};\n\n/**\n * Converts an array of Gemini API parts into an array of LangChain tool calls.\n *\n * This function iterates through a given array of `Gemini.Part` objects,\n * identifies parts that represent a function call, and transforms them into\n * LangChain's standardized `ToolCall` format. Each resulting tool call is\n * assigned a unique, sequential ID.\n *\n * @param parts - An array of `Gemini.Part` objects, which may or may not\n * contain function calls.\n * @returns An array of `ContentBlock.Tools.ToolCall` objects. If no function\n * call parts are found, an empty array is returned.\n *\n * @remarks\n * The `id` for each tool call is generated sequentially in the format `call_0`,\n * `call_1`, and so on, based on its order of appearance in the input array.\n *\n * @example\n * ```typescript\n * const geminiParts: Gemini.Part[] = [\n * { text: \"Thinking about what to do...\" },\n * {\n * functionCall: {\n * name: \"search_web\",\n * args: { query: \"latest AI news\" }\n * }\n * },\n * {\n * functionCall: {\n * name: \"calculator\",\n * args: { expression: \"2 + 2\" }\n * }\n * }\n * ];\n *\n * const toolCalls = convertGeminiPartsToToolCalls(geminiParts);\n * // toolCalls will be:\n * // [\n * // { type: \"tool_call\", id: \"call_0\", name: \"search_web\", args: { query: \"latest AI news\" } },\n * // { type: \"tool_call\", id: \"call_1\", name: \"calculator\", args: { expression: \"2 + 2\" } }\n * // ]\n * ```\n */\nexport const convertGeminiPartsToToolCalls: Converter<\n Gemini.Part[],\n ContentBlock.Tools.ToolCall<string, Record<string, unknown>>[]\n> = (\n parts: Gemini.Part[]\n): ContentBlock.Tools.ToolCall<string, Record<string, unknown>>[] => {\n const toolCalls: ContentBlock.Tools.ToolCall<\n string,\n Record<string, unknown>\n >[] = [];\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n if (\"functionCall\" in part && part.functionCall) {\n const functionCallPart = part as Gemini.Part.FunctionCall;\n toolCalls.push({\n type: \"tool_call\",\n id:\n functionCallPart.functionCall.id ??\n `lc-tool-call-${uuidv4().replace(/-/g, \"\")}`,\n name: functionCallPart.functionCall.name,\n args: functionCallPart.functionCall.args ?? {},\n thoughtSignature: functionCallPart.thoughtSignature,\n });\n }\n }\n\n return toolCalls;\n};\n\nexport const convertGeminiPartToContentBlock: Converter<\n Gemini.Part,\n ContentBlock\n> = (part: Gemini.Part): ContentBlock => {\n const block: ContentBlock = iife(() => {\n if (\"text\" in part && typeof part.text === \"string\") {\n return {\n type: \"text\",\n text: part.text,\n };\n } else if (\"inlineData\" in part && part.inlineData) {\n return {\n type: \"inlineData\",\n inlineData: part.inlineData,\n };\n } else if (\"fileData\" in part && part.fileData) {\n return {\n type: \"fileData\",\n fileData: part.fileData,\n };\n } else if (\"functionCall\" in part && part.functionCall) {\n return {\n type: \"functionCall\",\n functionCall: part.functionCall,\n };\n } else if (\"functionResponse\" in part && part.functionResponse) {\n return {\n type: \"functionResponse\",\n functionResponse: part.functionResponse,\n };\n } else if (\"executableCode\" in part && part.executableCode) {\n return {\n type: \"executableCode\",\n executableCode: part.executableCode,\n };\n } else if (\"codeExecutionResult\" in part && part.codeExecutionResult) {\n return {\n type: \"codeExecutionResult\",\n codeExecutionResult: part.codeExecutionResult,\n };\n }\n return part as unknown as ContentBlock;\n });\n const ret: ContentBlock = {\n thought: part.thought,\n thoughtSignature: part.thoughtSignature,\n partMetadata: part.partMetadata,\n ...block,\n };\n for (const attribute in ret) {\n if (ret[attribute] === undefined) {\n delete ret[attribute];\n }\n }\n\n return ret;\n};\n\n/**\n * Converts a Gemini API candidate response to a LangChain AIMessage.\n *\n * This converter transforms the response from Google's Gemini API into a standardized\n * AIMessage format that can be used throughout the LangChain ecosystem. It handles\n * various types of content including text, inline data, file data, function calls,\n * and code execution results.\n *\n * @remarks\n * The converter performs the following transformations:\n * - Extracts tool calls from function call parts and assigns sequential IDs\n * - Converts single text responses to string format for simplicity\n * - Converts multi-part responses to ContentBlock array format\n * - Preserves metadata including finish reason, safety ratings, and citations\n * - Handles various Gemini part types (text, inlineData, fileData, functionCall, etc.)\n *\n * @example\n * ```typescript\n * const candidate: Gemini.Candidate = {\n * content: {\n * parts: [\n * { text: \"Hello, how can I help you?\" }\n * ]\n * },\n * finishReason: \"STOP\",\n * safetyRatings: [...]\n * };\n *\n * const aiMessage = convertGeminiCandidateToAIMessage(candidate);\n * // Returns: AIMessage with content \"Hello, how can I help you?\"\n * ```\n *\n * @param candidate - The Gemini API candidate response containing content parts and metadata\n * @returns An AIMessage instance with converted content, tool calls, and metadata\n */\nexport const convertGeminiCandidateToAIMessage: Converter<\n Gemini.Candidate,\n AIMessage\n> = (candidate) => {\n function groundingSupportByPart(\n groundingSupports?: Gemini.GroundingSupport[]\n ): Gemini.GroundingSupport[][] {\n const ret: Gemini.GroundingSupport[][] = [];\n\n if (!groundingSupports || groundingSupports.length === 0) {\n return [[]];\n }\n\n groundingSupports?.forEach((groundingSupport) => {\n const segment = groundingSupport?.segment;\n const partIndex = segment?.partIndex ?? 0;\n if (ret[partIndex]) {\n ret[partIndex].push(groundingSupport);\n } else {\n ret[partIndex] = [groundingSupport];\n }\n });\n\n return ret;\n }\n\n const parts = candidate.content?.parts || [];\n\n // Extract tool calls from function call parts\n const toolCalls = convertGeminiPartsToToolCalls(parts);\n\n // Convert parts to content format that the translator understands\n // Format: array of objects with type field matching the part type\n let content: string | ContentBlock[];\n let originalTextContentBlock: ContentBlock | undefined = undefined;\n\n const groundingMetadata = candidate?.groundingMetadata;\n const citationMetadata = candidate?.citationMetadata;\n const groundingParts = groundingSupportByPart(\n groundingMetadata?.groundingSupports\n );\n\n // Determine if a part carries actual content vs. just metadata.\n // Metadata-only parts (e.g. empty text with only thoughtSignature) should not\n // prevent collapsing a single-text response into a string.\n const hasContentPayload = (p: Gemini.Part): boolean =>\n !!p.text ||\n !!p.thought ||\n !!p.inlineData ||\n !!p.fileData ||\n !!p.functionCall ||\n !!p.functionResponse ||\n !!p.executableCode ||\n !!p.codeExecutionResult;\n\n const contentParts = parts.filter(hasContentPayload);\n\n if (contentParts.length === 0) {\n // No content-bearing parts (may still have metadata-only parts)\n content = \"\";\n if (parts.length > 0) {\n originalTextContentBlock = convertGeminiPartToContentBlock(parts[0]);\n }\n } else if (\n contentParts.length === 1 &&\n \"text\" in contentParts[0] &&\n !(\"thought\" in contentParts[0])\n ) {\n // Single text content part (possibly alongside metadata-only parts like\n // thoughtSignature) - store as string\n content = contentParts[0].text ?? \"\";\n // Merge metadata from any metadata-only parts into the content block\n const mergedPart: Gemini.Part = { ...contentParts[0] };\n for (const p of parts) {\n if (p !== contentParts[0] && p.thoughtSignature) {\n mergedPart.thoughtSignature = p.thoughtSignature;\n }\n }\n originalTextContentBlock = convertGeminiPartToContentBlock(mergedPart);\n } else {\n // Multiple content parts - convert to array format with type fields\n content = parts.map((p: Gemini.Part) => convertGeminiPartToContentBlock(p));\n }\n\n const additional_kwargs: Record<string, unknown> = {\n finishReason: candidate.finishReason,\n finishMessage: candidate.finishMessage,\n safetyRatings: candidate.safetyRatings,\n tokenCount: candidate.tokenCount,\n citationMetadata: candidate.citationMetadata,\n groundingMetadata: candidate.groundingMetadata,\n groundingAttributions: candidate.groundingAttributions,\n urlRetrievalMetadata: candidate.urlRetrievalMetadata,\n urlContextMetadata: candidate.urlContextMetadata,\n avgLogprobs: candidate.avgLogprobs,\n logprobsResult: candidate.logprobsResult,\n originalTextContentBlock,\n };\n\n const response_metadata: Record<string, unknown> = {\n model_provider: \"google\",\n finish_reason: candidate.finishReason,\n safety_ratings: candidate.safetyRatings,\n citation_metadata: candidate.citationMetadata,\n grounding_metadata: candidate.groundingMetadata,\n grounding_attributions: candidate.groundingAttributions,\n url_retrieval_metadata: candidate.urlRetrievalMetadata,\n url_context_metadata: candidate.urlContextMetadata,\n avg_logprobs: candidate.avgLogprobs,\n logprobs_result: candidate.logprobsResult,\n\n // Backwards compatibility\n citationMetadata,\n groundingMetadata,\n groundingSupport: groundingParts[0],\n };\n // Remove any undefined properties, so we don't include them\n for (const attribute in additional_kwargs) {\n if (additional_kwargs[attribute] === undefined) {\n delete additional_kwargs[attribute];\n }\n }\n for (const attribute in response_metadata) {\n if (response_metadata[attribute] === undefined) {\n delete response_metadata[attribute];\n }\n }\n\n return new AIMessage({\n content,\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n additional_kwargs,\n response_metadata,\n });\n};\n\nexport const convertAIMessageToText: Converter<AIMessage, string> = (\n message: AIMessage\n): string => {\n if (typeof message.content === \"string\") {\n return message.content;\n } else if (Array.isArray(message.content)) {\n return message.content\n .filter(\n (c) => typeof c === \"string\" || (c as { type?: string }).type === \"text\"\n )\n .map((c) =>\n typeof c === \"string\" ? c : (c as { text?: string }).text || \"\"\n )\n .join(\"\");\n } else {\n return \"\";\n }\n};\n\nexport const convertGeminiGenerateContentResponseToUsageMetadata: Converter<\n Gemini.GenerateContentResponse,\n UsageMetadata\n> = (data: Gemini.GenerateContentResponse): UsageMetadata => {\n function addModalityCounts(\n modalityTokenCounts: Gemini.ModalityTokenCount[] | undefined,\n details: InputTokenDetails | OutputTokenDetails\n ): void {\n modalityTokenCounts?.forEach((modalityTokenCount) => {\n const { modality, tokenCount } = modalityTokenCount;\n if (\n typeof modality === \"undefined\" ||\n typeof tokenCount === \"undefined\"\n ) {\n return;\n }\n const modalityLc: keyof ModalitiesTokenDetails =\n modality.toLowerCase() as keyof ModalitiesTokenDetails;\n const currentCount = details[modalityLc] ?? 0;\n details[modalityLc] = currentCount + tokenCount;\n });\n }\n\n const usageMetadata: Gemini.UsageMetadata | undefined = data.usageMetadata;\n\n const inputTokenCount = usageMetadata?.promptTokenCount ?? 0;\n const candidatesTokenCount = usageMetadata?.candidatesTokenCount ?? 0;\n const thoughtsTokenCount = usageMetadata?.thoughtsTokenCount ?? 0;\n const outputTokenCount = candidatesTokenCount + thoughtsTokenCount;\n const totalTokens =\n usageMetadata?.totalTokenCount ?? inputTokenCount + outputTokenCount;\n\n const input_token_details: InputTokenDetails = {};\n addModalityCounts(usageMetadata?.promptTokensDetails, input_token_details);\n input_token_details.cache_read = usageMetadata?.cachedContentTokenCount ?? 0;\n\n const output_token_details: OutputTokenDetails = {};\n addModalityCounts(\n usageMetadata?.candidatesTokensDetails,\n output_token_details\n );\n output_token_details.reasoning = usageMetadata?.thoughtsTokenCount ?? 0;\n\n return {\n input_tokens: inputTokenCount,\n output_tokens: outputTokenCount,\n total_tokens: totalTokens,\n input_token_details,\n output_token_details,\n };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"messages.js","names":["uuidv4"],"sources":["../../src/converters/messages.ts"],"sourcesContent":["import {\n BaseMessage,\n AIMessage,\n isDataContentBlock,\n ContentBlock,\n parseBase64DataUrl,\n SystemMessage,\n HumanMessage,\n ToolMessage,\n ChatMessage,\n StandardContentBlockConverter,\n convertToProviderContentBlock,\n type Data,\n UsageMetadata,\n InputTokenDetails,\n OutputTokenDetails,\n ModalitiesTokenDetails,\n MessageContentImageUrl,\n MessageContentComplex,\n MessageContentText,\n} from \"@langchain/core/messages\";\nimport { v4 as uuidv4 } from \"@langchain/core/utils/uuid\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { iife } from \"../utils/misc.js\";\nimport { InvalidInputError, ToolCallNotFoundError } from \"../utils/errors.js\";\n\n/**\n * Standard content block converter for Google Gemini API.\n * Converts deprecated Data content blocks to Gemini Part format.\n *\n * @deprecated This converter is for backward compatibility with older message formats.\n * Use ContentBlock.Multimodal.Standard instead.\n */\nexport const geminiContentBlockConverter: StandardContentBlockConverter<{\n text: Gemini.Part;\n image: Gemini.Part;\n audio: Gemini.Part;\n file: Gemini.Part;\n}> = {\n providerName: \"ChatGoogle\",\n\n fromStandardTextBlock(block: Data.StandardTextBlock): Gemini.Part {\n return { text: block.text };\n },\n\n fromStandardImageBlock(block: Data.StandardImageBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 image blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType: parsed.mime_type || block.mime_type || \"image/jpeg\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"image/jpeg\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"image/jpeg\",\n fileUri: block.id,\n },\n };\n }\n }\n\n throw new InvalidInputError(\n `Image content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\".`\n );\n },\n\n fromStandardAudioBlock(block: Data.StandardAudioBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 audio blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType: parsed.mime_type || block.mime_type || \"audio/mpeg\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"audio/mpeg\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"audio/mpeg\",\n fileUri: block.id,\n },\n };\n }\n }\n\n throw new InvalidInputError(\n `Audio content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\".`\n );\n },\n\n fromStandardFileBlock(block: Data.StandardFileBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 file blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType:\n parsed.mime_type ||\n block.mime_type ||\n \"application/octet-stream\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"application/octet-stream\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"application/octet-stream\",\n fileUri: block.id,\n },\n };\n }\n }\n\n // Handle text source type (for plain text file blocks)\n if (\n \"source_type\" in block &&\n block.source_type === \"text\" &&\n \"text\" in block\n ) {\n return {\n text: block.text,\n };\n }\n\n throw new InvalidInputError(\n `File content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\", \"text\".`\n );\n },\n};\n\nconst MEDIA_BLOCK_TYPES = new Set([\n \"media\",\n \"image\",\n \"audio\",\n \"video\",\n \"file\",\n \"text-plain\",\n]);\n\nfunction isMediaContentBlock(item: unknown): boolean {\n if (typeof item !== \"object\" || item === null) {\n return false;\n }\n if (isDataContentBlock(item)) {\n return item.source_type !== \"text\";\n }\n if (\"image_url\" in item || \"inlineData\" in item || \"fileData\" in item) {\n return true;\n }\n return MEDIA_BLOCK_TYPES.has((item as { type?: unknown }).type as string);\n}\n\nfunction stripMediaBlocksForFunctionResponse(content: unknown): unknown {\n if (!Array.isArray(content)) {\n return content;\n }\n return content.filter((item) => !isMediaContentBlock(item));\n}\n\nfunction convertStandardDataContentBlockToGeminiPart(\n block: ContentBlock.Multimodal.Data\n): Gemini.Part | null {\n function uint8arrayToString(data: Uint8Array): string {\n return btoa(\n Array.from(data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n }\n\n function extractMimeType(str: string): {\n mimeType: string | null;\n data: string | null;\n } {\n if (str.startsWith(\"data:\")) {\n return {\n mimeType: str.split(\":\")[1].split(\";\")[0],\n data: str.split(\",\")[1],\n };\n }\n return {\n mimeType: null,\n data: null,\n };\n }\n\n if (\"mimeType\" in block && \"data\" in block) {\n const mimeType = block.mimeType!;\n const data: string =\n typeof block.data === \"string\"\n ? block.data\n : uint8arrayToString(block.data!);\n return {\n inlineData: {\n mimeType,\n data,\n },\n };\n } else if (\"mimeType\" in block && \"url\" in block) {\n const mimeType = block.mimeType!;\n const fileUri = block.url!;\n return {\n fileData: {\n mimeType,\n fileUri,\n },\n };\n } else if (\"url\" in block && block.url?.startsWith(\"data:\")) {\n const { mimeType, data } = extractMimeType(block.url!);\n if (mimeType && data) {\n return {\n inlineData: {\n mimeType,\n data,\n },\n };\n }\n }\n // FIXME - report this somehow?\n return null;\n}\n\nfunction convertStandardVideoContentBlockToGeminiPart(\n block: ContentBlock.Multimodal.Video\n): Gemini.Part | null {\n const ret: Gemini.Part | null =\n convertStandardDataContentBlockToGeminiPart(block);\n if (ret && block.metadata && \"videoMetadata\" in block.metadata) {\n (ret as Gemini.Part.FileData).videoMetadata = block.metadata.videoMetadata!;\n }\n return ret;\n}\n\nfunction convertStandardReasoningBlockToGeminiPart(\n block: ContentBlock.Reasoning\n): Gemini.Part | null {\n // Reasoning blocks created from Gemini should contain the base\n // standard block they would be if \"thought\" wasn't set to true.\n // If it exists, we'll use that. Otherwise, we'll assume it was\n // originally a text block.\n const ret =\n \"reasoningContentBlock\" in block && block.reasoningContentBlock\n ? convertStandardContentBlockToGeminiPart(\n block.reasoningContentBlock as ContentBlock.Standard\n )\n : { text: block.reasoning };\n\n // Then add that this came from the thinking process\n if (ret) {\n ret.thought = true;\n }\n\n return ret;\n}\n\n/**\n * Converts a single LangChain standard content block (v1 format)\n * into a Gemini.Part.\n *\n * This is intended to be called from `convertStandardContentMessageToGeminiContent`\n */\nfunction convertStandardContentBlockToGeminiPart(\n block: ContentBlock.Standard\n): Gemini.Part | null {\n function baseGeminiPart(): Gemini.Part | null {\n switch (block.type) {\n case \"text\":\n return { text: block.text };\n case \"reasoning\":\n return convertStandardReasoningBlockToGeminiPart(block);\n case \"image\":\n case \"audio\":\n case \"text-plain\":\n case \"file\":\n return convertStandardDataContentBlockToGeminiPart(block);\n case \"video\":\n return convertStandardVideoContentBlockToGeminiPart(block);\n default:\n return null;\n }\n }\n\n const ret: Gemini.Part | null = baseGeminiPart();\n if (ret) {\n if (\"thoughtSignature\" in block) {\n ret.thoughtSignature = block.thoughtSignature! as string;\n }\n }\n return ret;\n}\n\n/**\n * Converts a single LangChain message with standard content blocks (v1 format) to Gemini Content.\n * This handles messages that have `response_metadata.output_version === \"v1\"`.\n *\n * This is intended to be called from `convertMessagesToGeminiContent`\n */\nfunction convertStandardContentMessageToGeminiContent(\n message: BaseMessage,\n messages: BaseMessage[]\n): Gemini.Content | null {\n // Skip system messages - they're handled separately\n if (SystemMessage.isInstance(message)) {\n return null;\n }\n\n let role: Gemini.Role;\n if (HumanMessage.isInstance(message)) {\n role = \"user\";\n } else if (AIMessage.isInstance(message)) {\n role = \"model\";\n } else if (ToolMessage.isInstance(message)) {\n // Tool messages in Gemini were represented as function responses, but now are \"user\"\n role = \"user\";\n } else if (ChatMessage.isInstance(message)) {\n // Map ChatMessage roles to Gemini roles\n const msgRole = message.role.toLowerCase();\n if (msgRole === \"user\" || msgRole === \"human\") {\n role = \"user\";\n } else if (\n msgRole === \"assistant\" ||\n msgRole === \"ai\" ||\n msgRole === \"model\"\n ) {\n role = \"model\";\n } else if (msgRole === \"function\" || msgRole === \"tool\") {\n role = \"user\";\n } else {\n // Default to user for unknown roles\n role = \"user\";\n }\n } else {\n // Unknown message type - default to user\n role = \"user\";\n }\n\n let parts: Gemini.Part[] = [];\n\n // Process standard content blocks\n const contentBlocks = Array.isArray(message.contentBlocks)\n ? message.contentBlocks\n : [];\n contentBlocks.forEach((block: ContentBlock.Standard) => {\n const contentBlock =\n (message.additional_kwargs\n .originalTextContentBlock as ContentBlock.Standard) || block;\n const part: Gemini.Part | null =\n convertStandardContentBlockToGeminiPart(contentBlock);\n if (part) {\n parts.push(part);\n }\n });\n\n // Convert tool_calls to functionCall parts, reading thoughtSignature from the matching content block, falling back to the tool_call itself.\n if (AIMessage.isInstance(message) && message.tool_calls?.length) {\n const signaturesById: Record<string, string> = {};\n for (const block of contentBlocks) {\n if (\n block.type === \"tool_call\" &&\n block.id &&\n \"thoughtSignature\" in block &&\n typeof block.thoughtSignature === \"string\"\n ) {\n signaturesById[block.id] = block.thoughtSignature;\n }\n }\n for (const toolCall of message.tool_calls) {\n const part = {\n functionCall: {\n name: toolCall.name,\n args: toolCall.args ?? {},\n },\n } as Gemini.Part.FunctionCall;\n const thoughtSignature =\n (toolCall.id && signaturesById[toolCall.id]) ??\n (\"thoughtSignature\" in toolCall\n ? (toolCall.thoughtSignature as string)\n : undefined);\n if (thoughtSignature) {\n part.thoughtSignature = thoughtSignature;\n }\n parts.push(part);\n }\n }\n\n // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent = stripMediaBlocksForFunctionResponse(\n message.content\n );\n // Find the matching tool call in a preceding AIMessage to get the function name\n const aiMsg = messages\n .filter(AIMessage.isInstance)\n .find((msg) =>\n msg.tool_calls?.find((tc) => tc.id === message.tool_call_id)\n );\n const matchedToolCall = aiMsg?.tool_calls?.find(\n (tc) => tc.id === message.tool_call_id\n );\n const isGeneratedId = message.tool_call_id.startsWith(\"lc-tool-call-\");\n parts.push({\n functionResponse: {\n ...(isGeneratedId ? {} : { id: message.tool_call_id }),\n name: matchedToolCall?.name ?? message.name ?? \"unknown\",\n response: { result: responseContent },\n },\n });\n\n // For tool messages, keep only the functionResponse part(s): any text is\n // already included in functionResponse.response.result, and a `user`\n // content mixing a functionResponse with text is rejected or corrupted\n // by the API. Mirrors the legacy-path filter.\n parts = parts.filter((part) => \"functionResponse\" in part);\n }\n\n // Only return content if we have parts\n if (parts.length === 0) {\n return null;\n }\n\n return { role, parts };\n}\n\nfunction convertLegacyPartToGeminiPart(\n item: string | ContentBlock | Text\n): Gemini.Part {\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentText(\n content: object\n ): content is MessageContentText {\n return typeof content === \"object\" && content !== null && \"text\" in content;\n }\n\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentImageUrl(\n content: object\n ): content is MessageContentImageUrl {\n return (\n typeof content === \"object\" && content !== null && \"image_url\" in content\n );\n }\n\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentMedia(\n content: object\n ): content is MessageContentComplex {\n return (\n typeof content === \"object\" &&\n content !== null &&\n \"type\" in content &&\n content.type === \"media\"\n );\n }\n\n /**\n * Infers the MIME type from a URL based on its file extension.\n * This is used as a fallback when the MIME type is not provided.\n *\n * @param url - The URL to infer the MIME type from\n * @returns The inferred MIME type or a generic type if it cannot be determined\n */\n function inferMimeTypeFromUrl(url: string): string {\n const mimeTypeMap: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n };\n\n try {\n // Extract the pathname from the URL\n const pathname = new URL(url).pathname;\n // Get the file extension (handle query params and fragments)\n const extension = pathname\n .split(\".\")\n .pop()\n ?.toLowerCase()\n .split(/[?#]/)[0];\n return extension ? mimeTypeMap[extension] : \"application/octet-stream\";\n } catch {\n // If URL parsing fails, try a simple extension extraction\n const match = url.match(/\\.([a-zA-Z0-9]+)(?:[?#]|$)/);\n if (match) {\n const extension = match[1].toLowerCase();\n return mimeTypeMap[extension];\n }\n return \"application/octet-stream\";\n }\n }\n\n function messageContentImageUrlData(\n content: MessageContentImageUrl\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const url: string =\n typeof content.image_url === \"string\"\n ? content.image_url\n : content.image_url.url;\n if (!url) {\n throw new InvalidInputError(\n \"Missing image URL in image_url content block.\"\n );\n }\n\n const dataUrl = parseBase64DataUrl({ dataUrl: url });\n if (dataUrl?.data && dataUrl?.mime_type) {\n return {\n inlineData: {\n data: dataUrl.data,\n mimeType: dataUrl.mime_type,\n },\n };\n } else {\n // Infer MIME type from URL extension\n const mimeType = inferMimeTypeFromUrl(url) || \"image/png\";\n return {\n fileData: {\n mimeType,\n fileUri: url,\n },\n };\n }\n }\n\n function messageContentImageUrl(\n content: MessageContentImageUrl\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const ret = messageContentImageUrlData(content);\n supplementVideoMetadata(content, ret);\n return ret;\n }\n\n function messageContentMediaData(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: Record<string, any>\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n if (\"mimeType\" in content && \"data\" in content) {\n return {\n inlineData: {\n mimeType: content.mimeType,\n data: content.data,\n },\n };\n } else if (\"mimeType\" in content && \"fileUri\" in content) {\n return {\n fileData: {\n mimeType: content.mimeType,\n fileUri: content.fileUri,\n },\n };\n } else {\n // The old version would attempt to read the URL, but we're\n // not in an async function, so we can't do that.\n }\n\n throw new InvalidInputError(\n `Invalid media content: ${JSON.stringify(\n content,\n null,\n 1\n )}. Expected either { mimeType, data } for inline data or { mimeType, fileUri } for file references.`\n );\n }\n\n function supplementVideoMetadata(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: MessageContentImageUrl | Record<string, any>,\n ret: Gemini.Part.InlineData | Gemini.Part.FileData\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n // Add videoMetadata if defined\n if (\"videoMetadata\" in content && typeof ret === \"object\") {\n ret.videoMetadata = content.videoMetadata;\n }\n return ret;\n }\n\n function messageContentMedia(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: Record<string, any>\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const ret = messageContentMediaData(content);\n supplementVideoMetadata(content, ret);\n return ret;\n }\n\n function baseGeminiPart(): Gemini.Part {\n if (typeof item === \"string\") {\n return { text: item };\n } else if (typeof item === \"object\" && item !== null) {\n if (isMessageContentText(item)) {\n return { text: item.text };\n } else if (isDataContentBlock(item)) {\n return convertToProviderContentBlock(item, geminiContentBlockConverter);\n } else if (\"type\" in item && item?.type === \"functionCall\") {\n const { type, functionCall, ...etc } = item;\n return {\n ...etc,\n functionCall,\n } as Gemini.Part.FunctionCall;\n } else if (\"type\" in item && item?.type === \"executableCode\") {\n const { type, executableCode, ...etc } = item;\n return {\n ...etc,\n executableCode,\n } as Gemini.Part.ExecutableCode;\n } else if (\"type\" in item && item?.type === \"codeExecutionResult\") {\n const { type, codeExecutionResult, ...etc } = item;\n return {\n ...etc,\n codeExecutionResult,\n } as Gemini.Part.CodeExecutionResult;\n } else if (isMessageContentImageUrl(item)) {\n return messageContentImageUrl(item);\n } else if (isMessageContentMedia(item)) {\n return messageContentMedia(item);\n }\n }\n return item as Gemini.Part;\n }\n\n const ret = baseGeminiPart();\n const itemRecord = item as Record<string, unknown>;\n if (\"thought\" in itemRecord) {\n ret.thought = itemRecord.thought as boolean;\n }\n if (\"thoughtSignature\" in itemRecord) {\n ret.thoughtSignature = itemRecord.thoughtSignature as string;\n }\n return ret;\n}\n\n/**\n * Converts a single LangChain message with legacy content blocks (v0 format) to Gemini Content.\n * This handles messages that have `response_metadata.output_version === \"v0\"`.\n *\n * This is intended to be called from `convertMessagesToGeminiContent`\n */\nfunction convertLegacyContentMessageToGeminiContent(\n message: BaseMessage,\n messages: BaseMessage[]\n): Gemini.Content | null {\n // Skip system messages - they're handled separately\n if (SystemMessage.isInstance(message)) {\n return null;\n }\n\n const role: Gemini.Role = iife(() => {\n if (HumanMessage.isInstance(message)) {\n return \"user\";\n } else if (AIMessage.isInstance(message)) {\n return \"model\";\n } else if (ToolMessage.isInstance(message)) {\n // Tool messages in Gemini were represented as function responses, but now are \"user\"\n return \"user\";\n } else if (ChatMessage.isInstance(message)) {\n // Map ChatMessage roles to Gemini roles\n const msgRole = message.role.toLowerCase();\n if (msgRole === \"user\" || msgRole === \"human\") {\n return \"user\";\n } else if (\n msgRole === \"assistant\" ||\n msgRole === \"ai\" ||\n msgRole === \"model\"\n ) {\n return \"model\";\n } else if (msgRole === \"function\" || msgRole === \"tool\") {\n return \"user\";\n } else {\n // Default to user for unknown roles\n return \"user\";\n }\n } else {\n // Unknown message type - skip or default to user\n return \"user\";\n }\n });\n\n let parts: Gemini.Part[] = [];\n\n // Handle legacy content formats\n if (typeof message.content === \"string\") {\n // Simple string content\n if (message.content.trim()) {\n parts.push({ text: message.content });\n }\n } else if (Array.isArray(message.content)) {\n // Array of content blocks (legacy format)\n for (const item of message.content) {\n const part = convertLegacyPartToGeminiPart(item);\n parts.push(part);\n }\n }\n\n // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent = stripMediaBlocksForFunctionResponse(\n message.content\n );\n // Find the matching tool call in a preceding AIMessage to get the function name\n const aiMsg = messages\n .filter(AIMessage.isInstance)\n .find((msg) =>\n msg.tool_calls?.find((tc) => tc.id === message.tool_call_id)\n );\n if (!aiMsg) {\n throw new ToolCallNotFoundError(message.tool_call_id);\n }\n const isGeneratedId = message.tool_call_id.startsWith(\"lc-tool-call-\");\n const matchedToolCall = aiMsg.tool_calls?.find(\n (tc) => tc.id === message.tool_call_id\n );\n parts.push({\n functionResponse: {\n ...(isGeneratedId ? {} : { id: message.tool_call_id }),\n name: matchedToolCall?.name ?? message.name ?? \"unknown\",\n response: { result: responseContent },\n },\n });\n\n // For tool messages, drop the redundant text parts - their content is\n // already folded into functionResponse.response.result above - but keep\n // any media parts (inlineData/fileData) as siblings so images/audio/video\n // reach Gemini as real parts instead of being lost inside the JSON string.\n parts = parts.filter(\n (part) =>\n \"functionResponse\" in part || \"inlineData\" in part || \"fileData\" in part\n );\n }\n\n // Only add content if we have parts\n if (parts.length > 0) {\n return { role, parts };\n }\n\n return null;\n}\n\n/**\n * Converts an array of LangChain messages to Gemini API content format.\n *\n * This converter transforms LangChain's message types into the content structure\n * expected by Google's Gemini API.\n *\n * @remarks\n * The converter processes messages in two distinct ways based on their format:\n *\n * **V1 Standard Content Format**:\n * Messages with `output_version: \"v1\"` in their response metadata use the\n * standardized content block format. These are processed through\n * `convertStandardContentMessageToGeminiContent`, which extracts text blocks\n * from the `contentBlocks` array and maps message roles appropriately.\n *\n * **Google Format**:\n * Messages without the v1 marker are processed using generic logic that handles:\n * - String content: Simple text strings are converted to text parts\n * - Array content: Arrays of strings or content blocks are processed individually\n * - Tool messages: Converted to function response parts with tool call metadata\n *\n * @example\n * ```typescript\n * // Simple conversation with text messages\n * const messages = [\n * new HumanMessage(\"What is the weather?\"),\n * new AIMessage(\"I'll check that for you.\")\n * ];\n * const contents = convertMessagesToGeminiContents(messages);\n * // Returns: [\n * // { role: \"user\", parts: [{ text: \"What is the weather?\" }] },\n * // { role: \"model\", parts: [{ text: \"I'll check that for you.\" }] }\n * // ]\n * ```\n */\nexport const convertMessagesToGeminiContents: Converter<\n BaseMessage[],\n Gemini.GenerateContentRequest[\"contents\"]\n> = (messages) => {\n const contents: Gemini.Content[] = [];\n\n for (const message of messages) {\n // const content: Gemini.Content | null = convertContentMessageToGeminiContent(message, messages);\n const content: Gemini.Content | null = iife(() => {\n const outputVersion =\n \"output_version\" in message.response_metadata\n ? (message.response_metadata?.output_version as string)\n : \"v0\";\n switch (outputVersion) {\n case \"v1\":\n return convertStandardContentMessageToGeminiContent(\n message,\n messages\n );\n case \"v0\":\n default:\n return convertLegacyContentMessageToGeminiContent(message, messages);\n }\n });\n if (content) {\n const prev = contents[contents.length - 1];\n if (canMergeInto(prev, content) && content.parts) {\n prev.parts ??= [];\n prev.parts.push(...content.parts);\n } else {\n contents.push(content);\n }\n }\n }\n\n return contents;\n};\n\n/**\n * Returns true if any part of the given content is a functionResponse part\n * (i.e. it was produced from a ToolMessage).\n */\nfunction hasFunctionResponse(content: Gemini.Content): boolean {\n return content.parts?.some((part) => \"functionResponse\" in part) === true;\n}\n\n/**\n * Adjacent same-role contents merge only when they are the same kind of\n * turn:\n *\n * - Both carry functionResponse parts (a run of tool responses answering\n * one model turn's parallel calls): the API requires those to share a\n * single `user` content whose part count matches the call count.\n * - Neither does (plain text turns): coalesced as before #11444.\n *\n * A functionResponse turn never merges with a plain one: a `user` content\n * mixing the two is rejected by newer Gemini models and corrupts\n * generation on older ones.\n */\nfunction canMergeInto(\n prev: Gemini.Content | undefined,\n content: Gemini.Content\n): boolean {\n return (\n prev !== undefined &&\n prev.role === content.role &&\n hasFunctionResponse(prev) === hasFunctionResponse(content)\n );\n}\n\n/**\n * Converts LangChain system messages to Gemini API system instruction format.\n *\n * This converter extracts system messages from a message array and transforms them\n * into the format expected by Google's Gemini API for system instructions. System\n * instructions provide context and guidelines that influence the model's behavior\n * throughout the conversation.\n *\n * @remarks\n * The converter handles two distinct message formats:\n *\n * 1. **V1 Standard Content Format**: Messages with `output_version: \"v1\"` in their\n * response metadata use the standardized content block format. Only text blocks\n * are extracted from the `contentBlocks` array.\n *\n * 2. **Legacy Google Format**: Messages without the v1 marker use the legacy format\n * where content can be:\n * - A simple string (trimmed and converted to a text part)\n * - An array of strings or objects with `type: \"text\"` and a `text` property\n *\n * The converter filters out non-SystemMessage instances and only processes text\n * content, ignoring other content types like images or tool calls that may be\n * present in the message array.\n *\n * @example\n * ```typescript\n * // Simple string system message\n * const messages = [\n * new SystemMessage(\"You are a helpful assistant.\")\n * ];\n * const instruction = convertMessagesToGeminiSystemInstruction(messages);\n * // Returns: { parts: [{ text: \"You are a helpful assistant.\" }] }\n * ```\n *\n * @param messages - Array of LangChain messages to extract system instructions from\n * @returns A Gemini system instruction object with text parts, or undefined if no\n * system messages with text content are found\n */\nexport const convertMessagesToGeminiSystemInstruction: Converter<\n BaseMessage[],\n Gemini.GenerateContentRequest[\"systemInstruction\"]\n> = (messages) => {\n const systemParts: Gemini.Part[] = [];\n\n for (const message of messages) {\n if (SystemMessage.isInstance(message)) {\n if (message.text.trim()) {\n systemParts.push({ text: message.text });\n }\n }\n }\n\n if (systemParts.length === 0) {\n return undefined;\n }\n\n return { parts: systemParts };\n};\n\n/**\n * Converts an array of Gemini API parts into an array of LangChain tool calls.\n *\n * This function iterates through a given array of `Gemini.Part` objects,\n * identifies parts that represent a function call, and transforms them into\n * LangChain's standardized `ToolCall` format. Each resulting tool call is\n * assigned a unique, sequential ID.\n *\n * @param parts - An array of `Gemini.Part` objects, which may or may not\n * contain function calls.\n * @returns An array of `ContentBlock.Tools.ToolCall` objects. If no function\n * call parts are found, an empty array is returned.\n *\n * @remarks\n * The `id` for each tool call is generated sequentially in the format `call_0`,\n * `call_1`, and so on, based on its order of appearance in the input array.\n *\n * @example\n * ```typescript\n * const geminiParts: Gemini.Part[] = [\n * { text: \"Thinking about what to do...\" },\n * {\n * functionCall: {\n * name: \"search_web\",\n * args: { query: \"latest AI news\" }\n * }\n * },\n * {\n * functionCall: {\n * name: \"calculator\",\n * args: { expression: \"2 + 2\" }\n * }\n * }\n * ];\n *\n * const toolCalls = convertGeminiPartsToToolCalls(geminiParts);\n * // toolCalls will be:\n * // [\n * // { type: \"tool_call\", id: \"call_0\", name: \"search_web\", args: { query: \"latest AI news\" } },\n * // { type: \"tool_call\", id: \"call_1\", name: \"calculator\", args: { expression: \"2 + 2\" } }\n * // ]\n * ```\n */\nexport const convertGeminiPartsToToolCalls: Converter<\n Gemini.Part[],\n ContentBlock.Tools.ToolCall<string, Record<string, unknown>>[]\n> = (\n parts: Gemini.Part[]\n): ContentBlock.Tools.ToolCall<string, Record<string, unknown>>[] => {\n const toolCalls: ContentBlock.Tools.ToolCall<\n string,\n Record<string, unknown>\n >[] = [];\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n if (\"functionCall\" in part && part.functionCall) {\n const functionCallPart = part as Gemini.Part.FunctionCall;\n toolCalls.push({\n type: \"tool_call\",\n id:\n functionCallPart.functionCall.id ??\n `lc-tool-call-${uuidv4().replace(/-/g, \"\")}`,\n name: functionCallPart.functionCall.name,\n args: functionCallPart.functionCall.args ?? {},\n thoughtSignature: functionCallPart.thoughtSignature,\n });\n }\n }\n\n return toolCalls;\n};\n\nexport const convertGeminiPartToContentBlock: Converter<\n Gemini.Part,\n ContentBlock\n> = (part: Gemini.Part): ContentBlock => {\n const block: ContentBlock = iife(() => {\n if (\"text\" in part && typeof part.text === \"string\") {\n return {\n type: \"text\",\n text: part.text,\n };\n } else if (\"inlineData\" in part && part.inlineData) {\n return {\n type: \"inlineData\",\n inlineData: part.inlineData,\n };\n } else if (\"fileData\" in part && part.fileData) {\n return {\n type: \"fileData\",\n fileData: part.fileData,\n };\n } else if (\"functionCall\" in part && part.functionCall) {\n return {\n type: \"functionCall\",\n functionCall: part.functionCall,\n };\n } else if (\"functionResponse\" in part && part.functionResponse) {\n return {\n type: \"functionResponse\",\n functionResponse: part.functionResponse,\n };\n } else if (\"executableCode\" in part && part.executableCode) {\n return {\n type: \"executableCode\",\n executableCode: part.executableCode,\n };\n } else if (\"codeExecutionResult\" in part && part.codeExecutionResult) {\n return {\n type: \"codeExecutionResult\",\n codeExecutionResult: part.codeExecutionResult,\n };\n }\n return part as unknown as ContentBlock;\n });\n const ret: ContentBlock = {\n thought: part.thought,\n thoughtSignature: part.thoughtSignature,\n partMetadata: part.partMetadata,\n ...block,\n };\n for (const attribute in ret) {\n if (ret[attribute] === undefined) {\n delete ret[attribute];\n }\n }\n\n return ret;\n};\n\n/**\n * Converts a Gemini API candidate response to a LangChain AIMessage.\n *\n * This converter transforms the response from Google's Gemini API into a standardized\n * AIMessage format that can be used throughout the LangChain ecosystem. It handles\n * various types of content including text, inline data, file data, function calls,\n * and code execution results.\n *\n * @remarks\n * The converter performs the following transformations:\n * - Extracts tool calls from function call parts and assigns sequential IDs\n * - Converts single text responses to string format for simplicity\n * - Converts multi-part responses to ContentBlock array format\n * - Preserves metadata including finish reason, safety ratings, and citations\n * - Handles various Gemini part types (text, inlineData, fileData, functionCall, etc.)\n *\n * @example\n * ```typescript\n * const candidate: Gemini.Candidate = {\n * content: {\n * parts: [\n * { text: \"Hello, how can I help you?\" }\n * ]\n * },\n * finishReason: \"STOP\",\n * safetyRatings: [...]\n * };\n *\n * const aiMessage = convertGeminiCandidateToAIMessage(candidate);\n * // Returns: AIMessage with content \"Hello, how can I help you?\"\n * ```\n *\n * @param candidate - The Gemini API candidate response containing content parts and metadata\n * @returns An AIMessage instance with converted content, tool calls, and metadata\n */\nexport const convertGeminiCandidateToAIMessage: Converter<\n Gemini.Candidate,\n AIMessage\n> = (candidate) => {\n function groundingSupportByPart(\n groundingSupports?: Gemini.GroundingSupport[]\n ): Gemini.GroundingSupport[][] {\n const ret: Gemini.GroundingSupport[][] = [];\n\n if (!groundingSupports || groundingSupports.length === 0) {\n return [[]];\n }\n\n groundingSupports?.forEach((groundingSupport) => {\n const segment = groundingSupport?.segment;\n const partIndex = segment?.partIndex ?? 0;\n if (ret[partIndex]) {\n ret[partIndex].push(groundingSupport);\n } else {\n ret[partIndex] = [groundingSupport];\n }\n });\n\n return ret;\n }\n\n const parts = candidate.content?.parts || [];\n\n // Extract tool calls from function call parts\n const toolCalls = convertGeminiPartsToToolCalls(parts);\n\n // Convert parts to content format that the translator understands\n // Format: array of objects with type field matching the part type\n let content: string | ContentBlock[];\n let originalTextContentBlock: ContentBlock | undefined = undefined;\n\n const groundingMetadata = candidate?.groundingMetadata;\n const citationMetadata = candidate?.citationMetadata;\n const groundingParts = groundingSupportByPart(\n groundingMetadata?.groundingSupports\n );\n\n // Determine if a part carries actual content vs. just metadata.\n // Metadata-only parts (e.g. empty text with only thoughtSignature) should not\n // prevent collapsing a single-text response into a string.\n const hasContentPayload = (p: Gemini.Part): boolean =>\n !!p.text ||\n !!p.thought ||\n !!p.inlineData ||\n !!p.fileData ||\n !!p.functionCall ||\n !!p.functionResponse ||\n !!p.executableCode ||\n !!p.codeExecutionResult;\n\n const contentParts = parts.filter(hasContentPayload);\n\n if (contentParts.length === 0) {\n // No content-bearing parts (may still have metadata-only parts)\n content = \"\";\n if (parts.length > 0) {\n originalTextContentBlock = convertGeminiPartToContentBlock(parts[0]);\n }\n } else if (\n contentParts.length === 1 &&\n \"text\" in contentParts[0] &&\n !(\"thought\" in contentParts[0])\n ) {\n // Single text content part (possibly alongside metadata-only parts like\n // thoughtSignature) - store as string\n content = contentParts[0].text ?? \"\";\n // Merge metadata from any metadata-only parts into the content block\n const mergedPart: Gemini.Part = { ...contentParts[0] };\n for (const p of parts) {\n if (p !== contentParts[0] && p.thoughtSignature) {\n mergedPart.thoughtSignature = p.thoughtSignature;\n }\n }\n originalTextContentBlock = convertGeminiPartToContentBlock(mergedPart);\n } else {\n // Multiple content parts - convert to array format with type fields\n content = parts.map((p: Gemini.Part) => convertGeminiPartToContentBlock(p));\n }\n\n const additional_kwargs: Record<string, unknown> = {\n finishReason: candidate.finishReason,\n finishMessage: candidate.finishMessage,\n safetyRatings: candidate.safetyRatings,\n tokenCount: candidate.tokenCount,\n citationMetadata: candidate.citationMetadata,\n groundingMetadata: candidate.groundingMetadata,\n groundingAttributions: candidate.groundingAttributions,\n urlRetrievalMetadata: candidate.urlRetrievalMetadata,\n urlContextMetadata: candidate.urlContextMetadata,\n avgLogprobs: candidate.avgLogprobs,\n logprobsResult: candidate.logprobsResult,\n originalTextContentBlock,\n };\n\n const response_metadata: Record<string, unknown> = {\n model_provider: \"google\",\n finish_reason: candidate.finishReason,\n safety_ratings: candidate.safetyRatings,\n citation_metadata: candidate.citationMetadata,\n grounding_metadata: candidate.groundingMetadata,\n grounding_attributions: candidate.groundingAttributions,\n url_retrieval_metadata: candidate.urlRetrievalMetadata,\n url_context_metadata: candidate.urlContextMetadata,\n avg_logprobs: candidate.avgLogprobs,\n logprobs_result: candidate.logprobsResult,\n\n // Backwards compatibility\n citationMetadata,\n groundingMetadata,\n groundingSupport: groundingParts[0],\n };\n // Remove any undefined properties, so we don't include them\n for (const attribute in additional_kwargs) {\n if (additional_kwargs[attribute] === undefined) {\n delete additional_kwargs[attribute];\n }\n }\n for (const attribute in response_metadata) {\n if (response_metadata[attribute] === undefined) {\n delete response_metadata[attribute];\n }\n }\n\n return new AIMessage({\n content,\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n additional_kwargs,\n response_metadata,\n });\n};\n\nexport const convertAIMessageToText: Converter<AIMessage, string> = (\n message: AIMessage\n): string => {\n if (typeof message.content === \"string\") {\n return message.content;\n } else if (Array.isArray(message.content)) {\n return message.content\n .filter(\n (c) => typeof c === \"string\" || (c as { type?: string }).type === \"text\"\n )\n .map((c) =>\n typeof c === \"string\" ? c : (c as { text?: string }).text || \"\"\n )\n .join(\"\");\n } else {\n return \"\";\n }\n};\n\nexport const convertGeminiGenerateContentResponseToUsageMetadata: Converter<\n Gemini.GenerateContentResponse,\n UsageMetadata\n> = (data: Gemini.GenerateContentResponse): UsageMetadata => {\n function addModalityCounts(\n modalityTokenCounts: Gemini.ModalityTokenCount[] | undefined,\n details: InputTokenDetails | OutputTokenDetails\n ): void {\n modalityTokenCounts?.forEach((modalityTokenCount) => {\n const { modality, tokenCount } = modalityTokenCount;\n if (\n typeof modality === \"undefined\" ||\n typeof tokenCount === \"undefined\"\n ) {\n return;\n }\n const modalityLc: keyof ModalitiesTokenDetails =\n modality.toLowerCase() as keyof ModalitiesTokenDetails;\n const currentCount = details[modalityLc] ?? 0;\n details[modalityLc] = currentCount + tokenCount;\n });\n }\n\n const usageMetadata: Gemini.UsageMetadata | undefined = data.usageMetadata;\n\n const inputTokenCount = usageMetadata?.promptTokenCount ?? 0;\n const candidatesTokenCount = usageMetadata?.candidatesTokenCount ?? 0;\n const thoughtsTokenCount = usageMetadata?.thoughtsTokenCount ?? 0;\n const outputTokenCount = candidatesTokenCount + thoughtsTokenCount;\n const totalTokens =\n usageMetadata?.totalTokenCount ?? inputTokenCount + outputTokenCount;\n\n const input_token_details: InputTokenDetails = {};\n addModalityCounts(usageMetadata?.promptTokensDetails, input_token_details);\n input_token_details.cache_read = usageMetadata?.cachedContentTokenCount ?? 0;\n\n const output_token_details: OutputTokenDetails = {};\n addModalityCounts(\n usageMetadata?.candidatesTokensDetails,\n output_token_details\n );\n output_token_details.reasoning = usageMetadata?.thoughtsTokenCount ?? 0;\n\n return {\n input_tokens: inputTokenCount,\n output_tokens: outputTokenCount,\n total_tokens: totalTokens,\n input_token_details,\n output_token_details,\n };\n};\n"],"mappings":"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"}
|
|
@@ -33,32 +33,42 @@ function adjustObjectType(obj) {
|
|
|
33
33
|
else throw new require_errors.InvalidInputError("Gemini does not support union types in function schemas. Use a single type instead.");
|
|
34
34
|
return obj;
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return
|
|
36
|
+
const GEMINI_SCHEMA_KEYS = /* @__PURE__ */ new Set([
|
|
37
|
+
"anyOf",
|
|
38
|
+
"default",
|
|
39
|
+
"description",
|
|
40
|
+
"enum",
|
|
41
|
+
"example",
|
|
42
|
+
"format",
|
|
43
|
+
"items",
|
|
44
|
+
"maxItems",
|
|
45
|
+
"maxLength",
|
|
46
|
+
"maxProperties",
|
|
47
|
+
"maximum",
|
|
48
|
+
"minItems",
|
|
49
|
+
"minLength",
|
|
50
|
+
"minProperties",
|
|
51
|
+
"minimum",
|
|
52
|
+
"nullable",
|
|
53
|
+
"pattern",
|
|
54
|
+
"properties",
|
|
55
|
+
"propertyOrdering",
|
|
56
|
+
"required",
|
|
57
|
+
"title",
|
|
58
|
+
"type"
|
|
59
|
+
]);
|
|
60
|
+
function sanitizeGeminiSchema(node) {
|
|
61
|
+
if (Array.isArray(node)) return node.map(sanitizeGeminiSchema);
|
|
62
|
+
if (typeof node !== "object" || node === null) return node;
|
|
63
|
+
if ("$ref" in node) throw new require_errors.InvalidInputError("Gemini does not support recursive or referenced ($ref) schemas in function or response schemas. Inline the schema instead.");
|
|
64
|
+
const source = node;
|
|
65
|
+
const schema = {};
|
|
66
|
+
for (const key of Object.keys(source)) if (GEMINI_SCHEMA_KEYS.has(key)) schema[key] = source[key];
|
|
67
|
+
adjustObjectType(schema);
|
|
68
|
+
if (schema.properties && typeof schema.properties === "object") schema.properties = Object.fromEntries(Object.entries(schema.properties).map(([name, value]) => [name, sanitizeGeminiSchema(value)]));
|
|
69
|
+
if (schema.items !== void 0) schema.items = sanitizeGeminiSchema(schema.items);
|
|
70
|
+
if (Array.isArray(schema.anyOf)) schema.anyOf = schema.anyOf.map(sanitizeGeminiSchema);
|
|
71
|
+
return schema;
|
|
62
72
|
}
|
|
63
73
|
/**
|
|
64
74
|
* Converts a Zod schema or JSON Schema to Gemini function parameters format.
|
|
@@ -73,8 +83,7 @@ function removeAdditionalProperties(obj) {
|
|
|
73
83
|
* @internal
|
|
74
84
|
*/
|
|
75
85
|
function schemaToGeminiParameters(schema) {
|
|
76
|
-
|
|
77
|
-
return rest;
|
|
86
|
+
return sanitizeGeminiSchema((0, _langchain_core_utils_types.isInteropZodSchema)(schema) || (0, _langchain_core_utils_standard_schema.isSerializableSchema)(schema) ? (0, _langchain_core_utils_json_schema.toJsonSchema)(schema) : schema);
|
|
78
87
|
}
|
|
79
88
|
/**
|
|
80
89
|
* Converts a JSON Schema object to Gemini function parameters format.
|
|
@@ -85,8 +94,7 @@ function schemaToGeminiParameters(schema) {
|
|
|
85
94
|
* @internal
|
|
86
95
|
*/
|
|
87
96
|
function jsonSchemaToGeminiParameters(schema) {
|
|
88
|
-
|
|
89
|
-
return rest;
|
|
97
|
+
return sanitizeGeminiSchema(schema);
|
|
90
98
|
}
|
|
91
99
|
/**
|
|
92
100
|
* Type guard to check if a tool is already in Gemini's native format.
|
|
@@ -345,10 +353,23 @@ function convertToolChoiceToGeminiConfig(toolChoice, hasTools) {
|
|
|
345
353
|
if (mode) functionCallingConfig.mode = mode;
|
|
346
354
|
return { functionCallingConfig };
|
|
347
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Checks whether a tools array mixes server-side built-in tools (e.g.
|
|
358
|
+
* `googleSearch`, `codeExecution`) with function-declaration tools.
|
|
359
|
+
*
|
|
360
|
+
* Gemini rejects such a mix with a 400 unless
|
|
361
|
+
* `toolConfig.includeServerSideToolInvocations` is set.
|
|
362
|
+
* See https://ai.google.dev/gemini-api/docs/tool-combination
|
|
363
|
+
*/
|
|
364
|
+
function mixesBuiltinAndFunctionTools(tools) {
|
|
365
|
+
const isFunctionDeclarationTool = (tool) => "functionDeclarations" in tool && !!tool.functionDeclarations?.length;
|
|
366
|
+
return tools.some(isFunctionDeclarationTool) && tools.some((tool) => !isFunctionDeclarationTool(tool));
|
|
367
|
+
}
|
|
348
368
|
//#endregion
|
|
349
369
|
exports.convertToolChoiceToGeminiConfig = convertToolChoiceToGeminiConfig;
|
|
350
370
|
exports.convertToolsToGeminiFunctionDeclarations = convertToolsToGeminiFunctionDeclarations;
|
|
351
371
|
exports.convertToolsToGeminiTools = convertToolsToGeminiTools;
|
|
372
|
+
exports.mixesBuiltinAndFunctionTools = mixesBuiltinAndFunctionTools;
|
|
352
373
|
exports.schemaToGeminiParameters = schemaToGeminiParameters;
|
|
353
374
|
|
|
354
375
|
//# sourceMappingURL=tools.cjs.map
|
|
@@ -1 +1 @@
|
|
|
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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"}
|
|
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// Keywords Gemini's function schema accepts. Keep in sync with Gemini.Tools.Schema (api-types.ts); anything else 400s (#8584).\nconst GEMINI_SCHEMA_KEYS = new Set([\n \"anyOf\",\n \"default\",\n \"description\",\n \"enum\",\n \"example\",\n \"format\",\n \"items\",\n \"maxItems\",\n \"maxLength\",\n \"maxProperties\",\n \"maximum\",\n \"minItems\",\n \"minLength\",\n \"minProperties\",\n \"minimum\",\n \"nullable\",\n \"pattern\",\n \"properties\",\n \"propertyOrdering\",\n \"required\",\n \"title\",\n \"type\",\n]);\n\n// Recursively allowlist-filters a schema node; `properties`' own keys are user field names, not schema keywords, so only its values recurse.\nfunction sanitizeGeminiSchema(node: unknown): unknown {\n if (Array.isArray(node)) {\n return node.map(sanitizeGeminiSchema);\n }\n if (typeof node !== \"object\" || node === null) {\n return node;\n }\n\n if (\"$ref\" in node) {\n throw new InvalidInputError(\n \"Gemini does not support recursive or referenced ($ref) schemas in function or response schemas. Inline the schema instead.\"\n );\n }\n\n const source = node as Record<string, unknown>;\n const schema: Record<string, unknown> = {};\n for (const key of Object.keys(source)) {\n if (GEMINI_SCHEMA_KEYS.has(key)) {\n schema[key] = source[key];\n }\n }\n\n adjustObjectType(schema);\n\n if (schema.properties && typeof schema.properties === \"object\") {\n // fromEntries defines properties (safe for a field literally named \"__proto__\"), unlike bracket assignment on a plain {}.\n schema.properties = Object.fromEntries(\n Object.entries(schema.properties).map(([name, value]) => [\n name,\n sanitizeGeminiSchema(value),\n ])\n );\n }\n if (schema.items !== undefined) {\n schema.items = sanitizeGeminiSchema(schema.items);\n }\n if (Array.isArray(schema.anyOf)) {\n schema.anyOf = schema.anyOf.map(sanitizeGeminiSchema);\n }\n\n return 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 return sanitizeGeminiSchema(\n isInteropZodSchema(schema) || isSerializableSchema(schema)\n ? toJsonSchema(schema)\n : schema\n ) as Gemini.Tools.Schema;\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 return sanitizeGeminiSchema(schema) as Gemini.Tools.Schema;\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\n/**\n * Checks whether a tools array mixes server-side built-in tools (e.g.\n * `googleSearch`, `codeExecution`) with function-declaration tools.\n *\n * Gemini rejects such a mix with a 400 unless\n * `toolConfig.includeServerSideToolInvocations` is set.\n * See https://ai.google.dev/gemini-api/docs/tool-combination\n */\nexport function mixesBuiltinAndFunctionTools(tools: Gemini.Tool[]): boolean {\n const isFunctionDeclarationTool = (tool: Gemini.Tool) =>\n \"functionDeclarations\" in tool && !!tool.functionDeclarations?.length;\n return (\n tools.some(isFunctionDeclarationTool) &&\n tools.some((tool) => !isFunctionDeclarationTool(tool))\n );\n}\n"],"mappings":"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"}
|