@langchain/google 0.1.0 → 0.1.1

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.
Files changed (66) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/chat_models/base.cjs +31 -40
  3. package/dist/chat_models/base.cjs.map +1 -1
  4. package/dist/chat_models/base.js +9 -17
  5. package/dist/chat_models/base.js.map +1 -1
  6. package/dist/chat_models/index.cjs +2 -3
  7. package/dist/chat_models/index.cjs.map +1 -1
  8. package/dist/chat_models/index.d.cts +0 -1
  9. package/dist/chat_models/index.d.ts +0 -1
  10. package/dist/chat_models/index.js.map +1 -1
  11. package/dist/chat_models/node.cjs +3 -6
  12. package/dist/chat_models/node.cjs.map +1 -1
  13. package/dist/chat_models/node.js +1 -3
  14. package/dist/chat_models/node.js.map +1 -1
  15. package/dist/chat_models/profiles.cjs +94 -95
  16. package/dist/chat_models/profiles.cjs.map +1 -1
  17. package/dist/chat_models/profiles.js +94 -95
  18. package/dist/chat_models/profiles.js.map +1 -1
  19. package/dist/chat_models/types.d.cts +4 -3
  20. package/dist/chat_models/types.d.ts +4 -3
  21. package/dist/clients/index.cjs +3 -5
  22. package/dist/clients/index.cjs.map +1 -1
  23. package/dist/clients/index.js +0 -1
  24. package/dist/clients/index.js.map +1 -1
  25. package/dist/clients/node.cjs +8 -15
  26. package/dist/clients/node.cjs.map +1 -1
  27. package/dist/clients/node.d.cts +0 -1
  28. package/dist/clients/node.d.ts +0 -1
  29. package/dist/clients/node.js +4 -10
  30. package/dist/clients/node.js.map +1 -1
  31. package/dist/const.cjs.map +1 -1
  32. package/dist/const.js.map +1 -1
  33. package/dist/converters/messages.cjs +46 -67
  34. package/dist/converters/messages.cjs.map +1 -1
  35. package/dist/converters/messages.js +22 -42
  36. package/dist/converters/messages.js.map +1 -1
  37. package/dist/converters/params.cjs +20 -27
  38. package/dist/converters/params.cjs.map +1 -1
  39. package/dist/converters/params.js +20 -27
  40. package/dist/converters/params.js.map +1 -1
  41. package/dist/converters/tools.cjs +13 -19
  42. package/dist/converters/tools.cjs.map +1 -1
  43. package/dist/converters/tools.js +7 -12
  44. package/dist/converters/tools.js.map +1 -1
  45. package/dist/index.cjs +1 -0
  46. package/dist/node.cjs +1 -0
  47. package/dist/utils/errors.cjs +34 -30
  48. package/dist/utils/errors.cjs.map +1 -1
  49. package/dist/utils/errors.js +34 -30
  50. package/dist/utils/errors.js.map +1 -1
  51. package/dist/utils/gcp-auth.cjs +10 -6
  52. package/dist/utils/gcp-auth.cjs.map +1 -1
  53. package/dist/utils/gcp-auth.js +9 -4
  54. package/dist/utils/gcp-auth.js.map +1 -1
  55. package/dist/utils/handler.cjs +3 -4
  56. package/dist/utils/handler.cjs.map +1 -1
  57. package/dist/utils/handler.js +1 -1
  58. package/dist/utils/handler.js.map +1 -1
  59. package/dist/utils/misc.cjs.map +1 -1
  60. package/dist/utils/misc.d.cts +6 -2
  61. package/dist/utils/misc.d.ts +6 -2
  62. package/dist/utils/misc.js.map +1 -1
  63. package/dist/utils/stream.cjs.map +1 -1
  64. package/dist/utils/stream.js.map +1 -1
  65. package/package.json +4 -4
  66. package/dist/_virtual/rolldown_runtime.cjs +0 -25
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","names":["geminiContentBlockConverter: StandardContentBlockConverter<{\n text: Gemini.Part;\n image: Gemini.Part;\n audio: Gemini.Part;\n file: Gemini.Part;\n}>","block: Data.StandardTextBlock","block: Data.StandardImageBlock","block: Data.StandardAudioBlock","block: Data.StandardFileBlock","block: ContentBlock.Multimodal.Data","data: Uint8Array","str: string","data: string","block: ContentBlock.Multimodal.Video","ret: Gemini.Part | null","block: ContentBlock.Standard","message: BaseMessage","role: Gemini.Role","parts: Gemini.Part[]","part: Gemini.Part | null","messages: BaseMessage[]","content: object","url: string","mimeTypeMap: Record<string, string>","content: MessageContentImageUrl","content: Record<string, any>","content: MessageContentImageUrl | Record<string, any>","ret: Gemini.Part.InlineData | Gemini.Part.FileData","convertMessagesToGeminiContents: Converter<\n BaseMessage[],\n Gemini.GenerateContentRequest[\"contents\"]\n>","contents: Gemini.Content[]","content: Gemini.Content | null","convertMessagesToGeminiSystemInstruction: Converter<\n BaseMessage[],\n Gemini.GenerateContentRequest[\"systemInstruction\"]\n>","systemParts: Gemini.Part[]","convertGeminiPartsToToolCalls: Converter<\n Gemini.Part[],\n ContentBlock.Tools.ToolCall<string, Record<string, unknown>>[]\n>","toolCalls: ContentBlock.Tools.ToolCall<\n string,\n Record<string, unknown>\n >[]","convertGeminiPartToContentBlock: Converter<\n Gemini.Part,\n ContentBlock\n>","part: Gemini.Part","block: ContentBlock","ret: ContentBlock","convertGeminiCandidateToAIMessage: Converter<\n Gemini.Candidate,\n AIMessage\n>","groundingSupports?: Gemini.GroundingSupport[]","ret: Gemini.GroundingSupport[][]","content: string | ContentBlock[]","originalTextContentBlock: ContentBlock | undefined","p: Gemini.Part","mergedPart: Gemini.Part","additional_kwargs: Record<string, unknown>","response_metadata: Record<string, unknown>","convertAIMessageToText: Converter<AIMessage, string>","message: AIMessage","convertGeminiGenerateContentResponseToUsageMetadata: Converter<\n Gemini.GenerateContentResponse,\n UsageMetadata\n>","data: Gemini.GenerateContentResponse","modalityTokenCounts: Gemini.ModalityTokenCount[] | undefined","details: InputTokenDetails | OutputTokenDetails","modalityLc: keyof ModalitiesTokenDetails","usageMetadata: Gemini.UsageMetadata | undefined","input_token_details: InputTokenDetails","output_token_details: OutputTokenDetails"],"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 { 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\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\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 switch (block.type) {\n case \"text\":\n return { text: block.text };\n case \"image\":\n case \"audio\":\n return convertStandardDataContentBlockToGeminiPart(block);\n case \"video\":\n return convertStandardVideoContentBlockToGeminiPart(block);\n default:\n return null;\n }\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): 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 are represented as function responses\n role = \"function\";\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 = \"function\";\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 const parts: Gemini.Part[] = [];\n\n // Process standard content blocks\n message.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 // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent =\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content);\n // FIXME: ToolMessage almost never has a name, we need to refer to the message history\n parts.push({\n functionResponse: {\n name: message.name || \"unknown\",\n response: { result: responseContent },\n },\n });\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\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 /**\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 // eslint-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 // eslint-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 // eslint-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 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 are represented as function responses\n return \"function\";\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 \"function\";\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 if (typeof item === \"string\") {\n parts.push({ text: item });\n } else if (typeof item === \"object\" && item !== null) {\n if (isMessageContentText(item)) {\n parts.push({ text: item.text });\n } else if (isDataContentBlock(item)) {\n parts.push(\n convertToProviderContentBlock(item, geminiContentBlockConverter)\n );\n } else if (item?.type === \"functionCall\") {\n const { type, functionCall, ...etc } = item;\n parts.push({\n ...etc,\n functionCall,\n } as Gemini.Part.FunctionCall);\n } else if (isMessageContentImageUrl(item)) {\n parts.push(messageContentImageUrl(item));\n } else if (isMessageContentMedia(item)) {\n parts.push(messageContentMedia(item));\n } else {\n parts.push(item as Gemini.Part);\n }\n }\n }\n }\n\n // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent =\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content);\n // Find the response name by checking previous messages for the tool call\n const toolCall = messages\n .filter(AIMessage.isInstance)\n .find((msg) =>\n msg.tool_calls?.find((tc) => tc.id === message.tool_call_id)\n );\n if (!toolCall) {\n throw new ToolCallNotFoundError(message.tool_call_id);\n }\n parts.push({\n functionResponse: {\n name: toolCall?.name || \"unknown\",\n response: { result: responseContent },\n },\n });\n }\n\n // Remove non-functionResponse parts if this is a tool response\n if (role === \"function\") {\n parts = parts.filter((part) => \"functionResponse\" in part);\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(message);\n case \"v0\":\n default:\n return convertLegacyContentMessageToGeminiContent(message, messages);\n }\n });\n if (content) {\n contents.push(content);\n }\n }\n\n return contents;\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: `call_${toolCalls.length}`,\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":[],"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 { 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\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\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 switch (block.type) {\n case \"text\":\n return { text: block.text };\n case \"image\":\n case \"audio\":\n return convertStandardDataContentBlockToGeminiPart(block);\n case \"video\":\n return convertStandardVideoContentBlockToGeminiPart(block);\n default:\n return null;\n }\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): 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 are represented as function responses\n role = \"function\";\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 = \"function\";\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 const parts: Gemini.Part[] = [];\n\n // Process standard content blocks\n message.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 // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent =\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content);\n // FIXME: ToolMessage almost never has a name, we need to refer to the message history\n parts.push({\n functionResponse: {\n name: message.name || \"unknown\",\n response: { result: responseContent },\n },\n });\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\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 /**\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 // eslint-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 // eslint-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 // eslint-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 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 are represented as function responses\n return \"function\";\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 \"function\";\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 if (typeof item === \"string\") {\n parts.push({ text: item });\n } else if (typeof item === \"object\" && item !== null) {\n if (isMessageContentText(item)) {\n parts.push({ text: item.text });\n } else if (isDataContentBlock(item)) {\n parts.push(\n convertToProviderContentBlock(item, geminiContentBlockConverter)\n );\n } else if (item?.type === \"functionCall\") {\n const { type, functionCall, ...etc } = item;\n parts.push({\n ...etc,\n functionCall,\n } as Gemini.Part.FunctionCall);\n } else if (isMessageContentImageUrl(item)) {\n parts.push(messageContentImageUrl(item));\n } else if (isMessageContentMedia(item)) {\n parts.push(messageContentMedia(item));\n } else {\n parts.push(item as Gemini.Part);\n }\n }\n }\n }\n\n // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent =\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content);\n // Find the response name by checking previous messages for the tool call\n const toolCall = messages\n .filter(AIMessage.isInstance)\n .find((msg) =>\n msg.tool_calls?.find((tc) => tc.id === message.tool_call_id)\n );\n if (!toolCall) {\n throw new ToolCallNotFoundError(message.tool_call_id);\n }\n parts.push({\n functionResponse: {\n name: toolCall?.name || \"unknown\",\n response: { result: responseContent },\n },\n });\n }\n\n // Remove non-functionResponse parts if this is a tool response\n if (role === \"function\") {\n parts = parts.filter((part) => \"functionResponse\" in part);\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(message);\n case \"v0\":\n default:\n return convertLegacyContentMessageToGeminiContent(message, messages);\n }\n });\n if (content) {\n contents.push(content);\n }\n }\n\n return contents;\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: `call_${toolCalls.length}`,\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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"}
@@ -23,8 +23,8 @@
23
23
  * @param {string | undefined} modelName
24
24
  * The Gemini model name (e.g., "gemini-2.5-flash"). Used to determine token range constraints.
25
25
  * @param {string | undefined} effort
26
- * The reasoning effort level: one of "none", "minimal", "low", "medium", "high".
27
- * If undefined, the function returns undefined.
26
+ * The reasoning effort level: one of "none", "minimal", "low", "medium", "high"
27
+ * (case-insensitive). If undefined, the function returns undefined.
28
28
  * @returns {number | undefined}
29
29
  * The corresponding token count for the given effort and model. Returns undefined if
30
30
  * the effort is not recognized or not provided.
@@ -36,16 +36,16 @@
36
36
  * convertReasoningEffortToReasoningTokens(undefined, "low") // 1024
37
37
  */
38
38
  function convertReasoningEffortToReasoningTokens(modelName, effort) {
39
- if (effort === void 0) return void 0;
39
+ if (effort === void 0) return;
40
40
  const minTokens = modelName?.startsWith("gemini-2.5-flash") ? 0 : 128;
41
41
  const maxTokens = modelName?.startsWith("gemini-2.5-flash") ? 24 * 1024 : 32 * 1024;
42
- switch (effort) {
42
+ switch (effort.toLowerCase()) {
43
43
  case "none":
44
44
  case "minimal": return minTokens;
45
45
  case "low": return 1024;
46
46
  case "medium": return 8 * 1024;
47
47
  case "high": return maxTokens;
48
- default: return void 0;
48
+ default: return;
49
49
  }
50
50
  }
51
51
  /**
@@ -82,7 +82,7 @@ function convertReasoningEffortToReasoningTokens(modelName, effort) {
82
82
  * convertReasoningTokensToReasoningEffort(undefined, 9000) // "high"
83
83
  */
84
84
  function convertReasoningTokensToReasoningEffort(model, reasoningTokens) {
85
- if (typeof reasoningTokens === "undefined") return void 0;
85
+ if (typeof reasoningTokens === "undefined") return;
86
86
  else if (reasoningTokens === -1) return "high";
87
87
  else if (reasoningTokens === 0) if (model?.startsWith("gemini-3-pro")) return "low";
88
88
  else return "minimal";
@@ -99,10 +99,9 @@ function convertReasoningTokensToReasoningEffort(model, reasoningTokens) {
99
99
  * {@link BaseChatGoogleParams} (such as model constructors or call options).
100
100
  */
101
101
  function convertParamsToPlatformType(params) {
102
- if (typeof params === "undefined") return void 0;
102
+ if (typeof params === "undefined") return;
103
103
  if (typeof params.platformType !== "undefined") return params.platformType;
104
104
  if (params.vertexai === true) return "gcp";
105
- return void 0;
106
105
  }
107
106
  /**
108
107
  * Returns the model version from a Gemini model name.
@@ -138,8 +137,7 @@ function convertFieldsToThinkingConfig(model, fields) {
138
137
  if (model.startsWith("gemini-2.5-pro") && typeof thinkingBudget !== "undefined") {
139
138
  if (thinkingBudget >= 0 && thinkingBudget < 128) thinkingBudget = 128;
140
139
  }
141
- const thinkingLevelRaw = fields?.reasoningEffort ?? fields?.thinkingLevel ?? convertReasoningTokensToReasoningEffort(model, fields?.maxReasoningTokens ?? fields?.thinkingBudget);
142
- let thinkingLevel = thinkingLevelRaw?.toUpperCase();
140
+ let thinkingLevel = (fields?.reasoningEffort ?? fields?.thinkingLevel ?? convertReasoningTokensToReasoningEffort(model, fields?.maxReasoningTokens ?? fields?.thinkingBudget))?.toUpperCase();
143
141
  if (thinkingLevel === "MINIMAL") includeThoughts = false;
144
142
  if (model.startsWith("gemini-3-pro")) {
145
143
  if (thinkingLevel === "MINIMAL") thinkingLevel = "LOW";
@@ -147,8 +145,7 @@ function convertFieldsToThinkingConfig(model, fields) {
147
145
  }
148
146
  const modelVersion = getModelVersion(model);
149
147
  const modelSpecialty = getModelSpecialty(model);
150
- const thoughtsNotSupported = modelVersion === "2.5" && modelSpecialty === "image";
151
- if (thoughtsNotSupported || typeof thinkingBudget === "undefined" || typeof thinkingLevel === "undefined") return void 0;
148
+ if (modelVersion === "2.5" && modelSpecialty === "image" || typeof thinkingBudget === "undefined" || typeof thinkingLevel === "undefined") return;
152
149
  const thinkingConfig = { includeThoughts };
153
150
  if (modelSpecialty === "") if (model.startsWith("gemini-2.5")) thinkingConfig.thinkingBudget = thinkingBudget;
154
151
  else thinkingConfig.thinkingLevel = thinkingLevel;
@@ -164,15 +161,15 @@ function convertFieldsToThinkingConfig(model, fields) {
164
161
  */
165
162
  function convertFieldsToSpeechConfig(fields) {
166
163
  const config = fields.speechConfig;
167
- if (typeof config === "undefined") return void 0;
168
- function isSpeechConfig(config$1) {
169
- return typeof config$1 === "object" && (Object.hasOwn(config$1, "voiceConfig") || Object.hasOwn(config$1, "multiSpeakerVoiceConfig"));
164
+ if (typeof config === "undefined") return;
165
+ function isSpeechConfig(config) {
166
+ return typeof config === "object" && (Object.hasOwn(config, "voiceConfig") || Object.hasOwn(config, "multiSpeakerVoiceConfig"));
170
167
  }
171
- function hasLanguage(config$1) {
172
- return typeof config$1 === "object" && Object.hasOwn(config$1, "languageCode");
168
+ function hasLanguage(config) {
169
+ return typeof config === "object" && Object.hasOwn(config, "languageCode");
173
170
  }
174
- function hasVoice(config$1) {
175
- return Object.hasOwn(config$1, "voice");
171
+ function hasVoice(config) {
172
+ return Object.hasOwn(config, "voice");
176
173
  }
177
174
  if (isSpeechConfig(config)) return config;
178
175
  let languageCode;
@@ -186,14 +183,10 @@ function convertFieldsToSpeechConfig(fields) {
186
183
  }
187
184
  let ret;
188
185
  if (typeof voice === "string") ret = { voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } } };
189
- else {
190
- const voices = Array.isArray(voice) ? voice : [voice];
191
- const speakerVoiceConfigs = voices.map((v) => ({
192
- speaker: v.speaker,
193
- voiceConfig: { prebuiltVoiceConfig: { voiceName: v.name } }
194
- }));
195
- ret = { multiSpeakerVoiceConfig: { speakerVoiceConfigs } };
196
- }
186
+ else ret = { multiSpeakerVoiceConfig: { speakerVoiceConfigs: (Array.isArray(voice) ? voice : [voice]).map((v) => ({
187
+ speaker: v.speaker,
188
+ voiceConfig: { prebuiltVoiceConfig: { voiceName: v.name } }
189
+ })) } };
197
190
  if (languageCode) ret.languageCode = languageCode;
198
191
  return ret;
199
192
  }
@@ -1 +1 @@
1
- {"version":3,"file":"params.cjs","names":["modelName?: string","effort?: string","minTokens: number","maxTokens: number","model?: string","reasoningTokens?: number","params: {\n platformType?: GooglePlatformType;\n vertexai?: boolean;\n}","model: string","fields: ChatGoogleFields","thinkingConfig: Gemini.ThinkingConfig","config: Gemini.SpeechConfig | SimplifiedSpeechConfig | undefined","config: Gemini.SpeechConfig | SimplifiedSpeechConfig","config","config: SimplifiedSpeechConfig","config: SimplifiedSpeechLanguageConfig","languageCode: string | undefined","voice: SpeechVoice","ret: Gemini.SpeechConfig","voices: SpeechSpeakerName[]","speakerVoiceConfigs: Gemini.SpeakerVoiceConfig[]","v: SpeechSpeakerName"],"sources":["../../src/converters/params.ts"],"sourcesContent":["import type {\n ChatGoogleFields,\n GooglePlatformType,\n SimplifiedSpeechConfig,\n SimplifiedSpeechLanguageConfig,\n SpeechSpeakerName,\n SpeechVoice,\n SpeechVoiceLanguage,\n} from \"../chat_models/types.js\";\nimport type { Gemini } from \"../chat_models/api-types.js\";\n\n/**\n * Converts a reasoning \"effort\" level value (e.g., \"minimal\", \"low\", \"medium\", \"high\")\n * to a corresponding reasoning token budget, depending on the target Gemini model.\n *\n * This mapping is essential when configuring Google Gemini API calls with reasoning\n * effort/level fields (as in the Gemini OpenAI-compatible API) or when translating\n * user-friendly configuration to explicit token budgets.\n *\n * - The mapping is refined to reflect the constraints documented by Google:\n * - For \"gemini-2.5-flash\" models, token budgets may be set as low as 0 and up to 24k.\n * - For other Gemini models, minimum is 128, maximum is 32k.\n * - Standard effort levels and their token mappings:\n * - \"none\" / \"minimal\": lowest allowed (minTokens)\n * - \"low\": 1k tokens\n * - \"medium\": 8k tokens\n * - \"high\": maximum allowed for the model (maxTokens)\n * - See reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} modelName\n * The Gemini model name (e.g., \"gemini-2.5-flash\"). Used to determine token range constraints.\n * @param {string | undefined} effort\n * The reasoning effort level: one of \"none\", \"minimal\", \"low\", \"medium\", \"high\".\n * If undefined, the function returns undefined.\n * @returns {number | undefined}\n * The corresponding token count for the given effort and model. Returns undefined if\n * the effort is not recognized or not provided.\n *\n * @example\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"medium\") // 8192\n * convertReasoningEffortToReasoningTokens(\"gemini-1.5-pro\", \"high\") // 32768\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"none\") // 0\n * convertReasoningEffortToReasoningTokens(undefined, \"low\") // 1024\n */\nexport function convertReasoningEffortToReasoningTokens(\n modelName?: string,\n effort?: string\n): number | undefined {\n if (effort === undefined) {\n return undefined;\n }\n\n // gemini-2.5-flash and -flash-lite can be disabled. Others can't.\n // https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n const minTokens: number = modelName?.startsWith(\"gemini-2.5-flash\") ? 0 : 128;\n const maxTokens: number = modelName?.startsWith(\"gemini-2.5-flash\")\n ? 24 * 1024\n : 32 * 1024;\n\n switch (effort) {\n case \"none\":\n case \"minimal\":\n return minTokens;\n case \"low\":\n // Defined as 1k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 1024;\n case \"medium\":\n // Defined as 8k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 8 * 1024;\n case \"high\":\n // Defined as 24k or 32k (model-dependent) by https://ai.google.dev/gemini-api/docs/openai#thinking\n return maxTokens;\n default:\n return undefined;\n }\n}\n\n/**\n * Converts a reasoning token budget (e.g., 0, 1024, 8192, 32768) to its corresponding\n * reasoning \"effort\" level, as used in the Google Gemini API (\"minimal\", \"low\", \"medium\", \"high\").\n *\n * This is the inverse of {@link convertReasoningEffortToReasoningTokens}. It is useful for\n * translating explicit numeric settings (often surfaced in raw API responses or configs)\n * into user-facing friendly reasoning levels/labels. The mapping relies on Google's\n * documented reasoning levels for different models:\n *\n * - -1 means \"high\" effort (per API documentation: the default, dynamic setting).\n * - 0 means \"minimal\" unless the model is \"gemini-3-pro\", for which it maps to \"low\".\n * - <= 1024 tokens: \"low\"\n * - <= 8192 tokens: \"medium\" (unless model is \"gemini-3-pro\", for which it reverts to \"low\")\n * - > 8192: \"high\"\n *\n * Reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} model\n * The Gemini model name (e.g., \"gemini-3-pro\"). Used for model-specific special cases.\n * @param {number | undefined} reasoningTokens\n * The token budget to convert. If undefined, the function returns undefined.\n * @returns {Lowercase<Gemini.ThinkingLevel> | undefined}\n * The reasoning \"effort\" level (\"minimal\", \"low\", \"medium\", \"high\") or undefined if input is not valid.\n *\n * @example\n * convertReasoningTokensToReasoningEffort(\"gemini-3-pro\", 0) // \"low\"\n * convertReasoningTokensToReasoningEffort(\"gemini-1.5-pro\", 0) // \"minimal\"\n * convertReasoningTokensToReasoningEffort(undefined, 1024) // \"low\"\n * convertReasoningTokensToReasoningEffort(undefined, -1) // \"high\"\n * convertReasoningTokensToReasoningEffort(undefined, 9000) // \"high\"\n */\nexport function convertReasoningTokensToReasoningEffort(\n model?: string,\n reasoningTokens?: number\n): Gemini.ThinkingLevel | undefined {\n if (typeof reasoningTokens === \"undefined\") {\n return undefined;\n } else if (reasoningTokens === -1) {\n // -1 means \"high\"/dynamic (\"default\" per https://ai.google.dev/gemini-api/docs/thinking#thinking-levels)\n return \"high\";\n } else if (reasoningTokens === 0) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"minimal\";\n }\n } else if (reasoningTokens <= 1024) {\n return \"low\";\n } else if (reasoningTokens <= 8192) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"medium\";\n }\n } else {\n return \"high\";\n }\n}\n\n/**\n * Infers and returns the Google platform type from the provided chat model parameters.\n *\n * This function helps determine which Google platform (e.g., Vertex AI on GCP or the public API)\n * should be used for calling Gemini/PaLM endpoints, based on the options provided in\n * {@link BaseChatGoogleParams} (such as model constructors or call options).\n */\nexport function convertParamsToPlatformType(params: {\n platformType?: GooglePlatformType;\n vertexai?: boolean;\n}): GooglePlatformType | undefined {\n if (typeof params === \"undefined\") {\n return undefined;\n }\n if (typeof params.platformType !== \"undefined\") {\n return params.platformType;\n }\n if (params.vertexai === true) {\n return \"gcp\";\n }\n return undefined;\n}\n\n/**\n * Returns the model family from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"gemini\"\n */\nexport function getModelFamily(model: string): string {\n return model.split(\"-\")[0];\n}\n\n/**\n * Returns the model version from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"2.5\"\n */\nexport function getModelVersion(model: string): string {\n return model.split(\"-\")[1];\n}\n\n/**\n * Returns the model level from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"flash\", \"gemini-2.5-flash-lite\" → \"flash-lite\"\n */\nexport function getModelLevel(model: string): string {\n const parts = model.split(\"-\");\n let ret = parts[2];\n if (ret === \"flash\" && parts[3] === \"lite\") {\n ret = \"flash-lite\";\n }\n return ret;\n}\n\n/**\n * Returns the model specialty from a Gemini model name.\n * e.g. \"gemini-2.5-flash-preview-image\" → \"image\",\n * \"gemini-2.5-flash-preview-tts\" → \"tts\",\n * \"gemini-2.5-flash\" → \"\"\n */\nexport function getModelSpecialty(model: string): string {\n if (model.includes(\"image\")) {\n return \"image\";\n } else if (model.includes(\"tts\")) {\n return \"tts\";\n } else {\n return \"\";\n }\n}\n\n/**\n * Builds the `thinkingConfig` object for the Gemini `generationConfig`,\n * translating user-facing reasoning fields into the wire format.\n *\n * @param model The full Gemini model name (e.g. \"gemini-2.5-flash\").\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.ThinkingConfig}, or `{}`\n * when thinking should not be explicitly configured.\n */\nexport function convertFieldsToThinkingConfig(\n model: string,\n fields: ChatGoogleFields\n): Gemini.ThinkingConfig | undefined {\n // Thinking / reasoning\n let includeThoughts = true;\n\n let thinkingBudget =\n fields?.maxReasoningTokens ??\n fields?.thinkingBudget ??\n convertReasoningEffortToReasoningTokens(\n model,\n fields?.reasoningEffort ?? fields?.thinkingLevel\n );\n if (\n thinkingBudget === 0 ||\n (model.includes(\"pro\") && thinkingBudget === 128)\n ) {\n includeThoughts = false;\n }\n if (\n model.startsWith(\"gemini-2.5-pro\") &&\n typeof thinkingBudget !== \"undefined\"\n ) {\n // Can't turn off Gemini 2.5 Pro thinking completely\n if (thinkingBudget >= 0 && thinkingBudget < 128) {\n thinkingBudget = 128;\n }\n }\n\n const thinkingLevelRaw =\n fields?.reasoningEffort ??\n fields?.thinkingLevel ??\n convertReasoningTokensToReasoningEffort(\n model,\n fields?.maxReasoningTokens ?? fields?.thinkingBudget\n );\n let thinkingLevel = thinkingLevelRaw?.toUpperCase();\n if (thinkingLevel === \"MINIMAL\") {\n includeThoughts = false;\n }\n if (model.startsWith(\"gemini-3-pro\")) {\n // Gemini 3 Pro has only low and high.\n if (thinkingLevel === \"MINIMAL\") {\n thinkingLevel = \"LOW\";\n } else if (thinkingLevel === \"MEDIUM\") {\n thinkingLevel = \"HIGH\";\n }\n }\n\n // If we are using a model that doesn't support thinking at all (gemini 2.5 imaging)\n // or we haven't explicitly tried to set a thinking budget/level, then bail out.\n const modelVersion = getModelVersion(model);\n const modelSpecialty = getModelSpecialty(model);\n const thoughtsNotSupported =\n modelVersion === \"2.5\" && modelSpecialty === \"image\";\n if (\n thoughtsNotSupported ||\n typeof thinkingBudget === \"undefined\" ||\n typeof thinkingLevel === \"undefined\"\n ) {\n return undefined;\n }\n\n // If we have gotten this far, then we want to explicitly set if we include thoughts or not.\n const thinkingConfig: Gemini.ThinkingConfig = {\n includeThoughts,\n };\n\n // Explicitly setting the budget/level is only valid (currently) for text models.\n if (modelSpecialty === \"\") {\n if (model.startsWith(\"gemini-2.5\")) {\n thinkingConfig.thinkingBudget = thinkingBudget;\n } else {\n thinkingConfig.thinkingLevel = thinkingLevel as Gemini.ThinkingLevel;\n }\n }\n\n return thinkingConfig;\n}\n\n/**\n * Builds the `speechConfig` object for the Gemini `generationConfig`,\n * normalising the user-friendly simplified forms into the wire format.\n *\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.SpeechConfig}, or `{}`\n * when no speech config was provided.\n */\nexport function convertFieldsToSpeechConfig(\n fields: ChatGoogleFields\n): Gemini.SpeechConfig | undefined {\n const config: Gemini.SpeechConfig | SimplifiedSpeechConfig | undefined =\n fields.speechConfig;\n if (typeof config === \"undefined\") {\n return undefined;\n }\n\n function isSpeechConfig(\n config: Gemini.SpeechConfig | SimplifiedSpeechConfig\n ): config is Gemini.SpeechConfig {\n return (\n typeof config === \"object\" &&\n (Object.hasOwn(config, \"voiceConfig\") ||\n Object.hasOwn(config, \"multiSpeakerVoiceConfig\"))\n );\n }\n\n function hasLanguage(\n config: SimplifiedSpeechConfig\n ): config is SimplifiedSpeechLanguageConfig {\n return typeof config === \"object\" && Object.hasOwn(config, \"languageCode\");\n }\n\n function hasVoice(\n config: SimplifiedSpeechLanguageConfig\n ): config is SpeechVoiceLanguage {\n return Object.hasOwn(config, \"voice\");\n }\n\n // If this is already a SpeechConfig, just return it\n if (isSpeechConfig(config)) {\n return config;\n }\n\n let languageCode: string | undefined;\n let voice: SpeechVoice;\n if (hasLanguage(config)) {\n languageCode = config.languageCode;\n voice = hasVoice(config) ? config.voice : config.voices;\n } else {\n languageCode = undefined;\n voice = config;\n }\n\n let ret: Gemini.SpeechConfig;\n\n if (typeof voice === \"string\") {\n // They just provided the prebuilt voice configuration name. Use it.\n ret = {\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: voice,\n },\n },\n };\n } else {\n // This is multi-speaker, so we have speaker/name pairs\n // If we have just one (why?), turn it into an array for the moment\n const voices: SpeechSpeakerName[] = Array.isArray(voice) ? voice : [voice];\n // Go through all the speaker/name pairs and turn this into the voice config array\n const speakerVoiceConfigs: Gemini.SpeakerVoiceConfig[] = voices.map(\n (v: SpeechSpeakerName): Gemini.SpeakerVoiceConfig => ({\n speaker: v.speaker,\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: v.name,\n },\n },\n })\n );\n // Create the multi-speaker voice configuration\n ret = {\n multiSpeakerVoiceConfig: {\n speakerVoiceConfigs,\n },\n };\n }\n\n if (languageCode) {\n ret.languageCode = languageCode;\n }\n\n return ret;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"params.cjs","names":[],"sources":["../../src/converters/params.ts"],"sourcesContent":["import type {\n ChatGoogleFields,\n GooglePlatformType,\n SimplifiedSpeechConfig,\n SimplifiedSpeechLanguageConfig,\n SpeechSpeakerName,\n SpeechVoice,\n SpeechVoiceLanguage,\n} from \"../chat_models/types.js\";\nimport type { Gemini } from \"../chat_models/api-types.js\";\n\n/**\n * Converts a reasoning \"effort\" level value (e.g., \"minimal\", \"low\", \"medium\", \"high\")\n * to a corresponding reasoning token budget, depending on the target Gemini model.\n *\n * This mapping is essential when configuring Google Gemini API calls with reasoning\n * effort/level fields (as in the Gemini OpenAI-compatible API) or when translating\n * user-friendly configuration to explicit token budgets.\n *\n * - The mapping is refined to reflect the constraints documented by Google:\n * - For \"gemini-2.5-flash\" models, token budgets may be set as low as 0 and up to 24k.\n * - For other Gemini models, minimum is 128, maximum is 32k.\n * - Standard effort levels and their token mappings:\n * - \"none\" / \"minimal\": lowest allowed (minTokens)\n * - \"low\": 1k tokens\n * - \"medium\": 8k tokens\n * - \"high\": maximum allowed for the model (maxTokens)\n * - See reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} modelName\n * The Gemini model name (e.g., \"gemini-2.5-flash\"). Used to determine token range constraints.\n * @param {string | undefined} effort\n * The reasoning effort level: one of \"none\", \"minimal\", \"low\", \"medium\", \"high\"\n * (case-insensitive). If undefined, the function returns undefined.\n * @returns {number | undefined}\n * The corresponding token count for the given effort and model. Returns undefined if\n * the effort is not recognized or not provided.\n *\n * @example\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"medium\") // 8192\n * convertReasoningEffortToReasoningTokens(\"gemini-1.5-pro\", \"high\") // 32768\n * convertReasoningEffortToReasoningTokens(\"gemini-2.5-flash\", \"none\") // 0\n * convertReasoningEffortToReasoningTokens(undefined, \"low\") // 1024\n */\nexport function convertReasoningEffortToReasoningTokens(\n modelName?: string,\n effort?: string\n): number | undefined {\n if (effort === undefined) {\n return undefined;\n }\n\n // gemini-2.5-flash and -flash-lite can be disabled. Others can't.\n // https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n const minTokens: number = modelName?.startsWith(\"gemini-2.5-flash\") ? 0 : 128;\n const maxTokens: number = modelName?.startsWith(\"gemini-2.5-flash\")\n ? 24 * 1024\n : 32 * 1024;\n\n const normalizedEffort = effort.toLowerCase();\n switch (normalizedEffort) {\n case \"none\":\n case \"minimal\":\n return minTokens;\n case \"low\":\n // Defined as 1k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 1024;\n case \"medium\":\n // Defined as 8k by https://ai.google.dev/gemini-api/docs/openai#thinking\n return 8 * 1024;\n case \"high\":\n // Defined as 24k or 32k (model-dependent) by https://ai.google.dev/gemini-api/docs/openai#thinking\n return maxTokens;\n default:\n return undefined;\n }\n}\n\n/**\n * Converts a reasoning token budget (e.g., 0, 1024, 8192, 32768) to its corresponding\n * reasoning \"effort\" level, as used in the Google Gemini API (\"minimal\", \"low\", \"medium\", \"high\").\n *\n * This is the inverse of {@link convertReasoningEffortToReasoningTokens}. It is useful for\n * translating explicit numeric settings (often surfaced in raw API responses or configs)\n * into user-facing friendly reasoning levels/labels. The mapping relies on Google's\n * documented reasoning levels for different models:\n *\n * - -1 means \"high\" effort (per API documentation: the default, dynamic setting).\n * - 0 means \"minimal\" unless the model is \"gemini-3-pro\", for which it maps to \"low\".\n * - <= 1024 tokens: \"low\"\n * - <= 8192 tokens: \"medium\" (unless model is \"gemini-3-pro\", for which it reverts to \"low\")\n * - > 8192: \"high\"\n *\n * Reference:\n * https://ai.google.dev/gemini-api/docs/thinking#thinking-levels\n * https://ai.google.dev/gemini-api/docs/openai#thinking\n *\n * @param {string | undefined} model\n * The Gemini model name (e.g., \"gemini-3-pro\"). Used for model-specific special cases.\n * @param {number | undefined} reasoningTokens\n * The token budget to convert. If undefined, the function returns undefined.\n * @returns {Lowercase<Gemini.ThinkingLevel> | undefined}\n * The reasoning \"effort\" level (\"minimal\", \"low\", \"medium\", \"high\") or undefined if input is not valid.\n *\n * @example\n * convertReasoningTokensToReasoningEffort(\"gemini-3-pro\", 0) // \"low\"\n * convertReasoningTokensToReasoningEffort(\"gemini-1.5-pro\", 0) // \"minimal\"\n * convertReasoningTokensToReasoningEffort(undefined, 1024) // \"low\"\n * convertReasoningTokensToReasoningEffort(undefined, -1) // \"high\"\n * convertReasoningTokensToReasoningEffort(undefined, 9000) // \"high\"\n */\nexport function convertReasoningTokensToReasoningEffort(\n model?: string,\n reasoningTokens?: number\n): Gemini.ThinkingLevel | undefined {\n if (typeof reasoningTokens === \"undefined\") {\n return undefined;\n } else if (reasoningTokens === -1) {\n // -1 means \"high\"/dynamic (\"default\" per https://ai.google.dev/gemini-api/docs/thinking#thinking-levels)\n return \"high\";\n } else if (reasoningTokens === 0) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"minimal\";\n }\n } else if (reasoningTokens <= 1024) {\n return \"low\";\n } else if (reasoningTokens <= 8192) {\n if (model?.startsWith(\"gemini-3-pro\")) {\n return \"low\";\n } else {\n return \"medium\";\n }\n } else {\n return \"high\";\n }\n}\n\n/**\n * Infers and returns the Google platform type from the provided chat model parameters.\n *\n * This function helps determine which Google platform (e.g., Vertex AI on GCP or the public API)\n * should be used for calling Gemini/PaLM endpoints, based on the options provided in\n * {@link BaseChatGoogleParams} (such as model constructors or call options).\n */\nexport function convertParamsToPlatformType(params: {\n platformType?: GooglePlatformType;\n vertexai?: boolean;\n}): GooglePlatformType | undefined {\n if (typeof params === \"undefined\") {\n return undefined;\n }\n if (typeof params.platformType !== \"undefined\") {\n return params.platformType;\n }\n if (params.vertexai === true) {\n return \"gcp\";\n }\n return undefined;\n}\n\n/**\n * Returns the model family from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"gemini\"\n */\nexport function getModelFamily(model: string): string {\n return model.split(\"-\")[0];\n}\n\n/**\n * Returns the model version from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"2.5\"\n */\nexport function getModelVersion(model: string): string {\n return model.split(\"-\")[1];\n}\n\n/**\n * Returns the model level from a Gemini model name.\n * e.g. \"gemini-2.5-flash\" → \"flash\", \"gemini-2.5-flash-lite\" → \"flash-lite\"\n */\nexport function getModelLevel(model: string): string {\n const parts = model.split(\"-\");\n let ret = parts[2];\n if (ret === \"flash\" && parts[3] === \"lite\") {\n ret = \"flash-lite\";\n }\n return ret;\n}\n\n/**\n * Returns the model specialty from a Gemini model name.\n * e.g. \"gemini-2.5-flash-preview-image\" → \"image\",\n * \"gemini-2.5-flash-preview-tts\" → \"tts\",\n * \"gemini-2.5-flash\" → \"\"\n */\nexport function getModelSpecialty(model: string): string {\n if (model.includes(\"image\")) {\n return \"image\";\n } else if (model.includes(\"tts\")) {\n return \"tts\";\n } else {\n return \"\";\n }\n}\n\n/**\n * Builds the `thinkingConfig` object for the Gemini `generationConfig`,\n * translating user-facing reasoning fields into the wire format.\n *\n * @param model The full Gemini model name (e.g. \"gemini-2.5-flash\").\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.ThinkingConfig}, or `{}`\n * when thinking should not be explicitly configured.\n */\nexport function convertFieldsToThinkingConfig(\n model: string,\n fields: ChatGoogleFields\n): Gemini.ThinkingConfig | undefined {\n // Thinking / reasoning\n let includeThoughts = true;\n\n let thinkingBudget =\n fields?.maxReasoningTokens ??\n fields?.thinkingBudget ??\n convertReasoningEffortToReasoningTokens(\n model,\n fields?.reasoningEffort ?? fields?.thinkingLevel\n );\n if (\n thinkingBudget === 0 ||\n (model.includes(\"pro\") && thinkingBudget === 128)\n ) {\n includeThoughts = false;\n }\n if (\n model.startsWith(\"gemini-2.5-pro\") &&\n typeof thinkingBudget !== \"undefined\"\n ) {\n // Can't turn off Gemini 2.5 Pro thinking completely\n if (thinkingBudget >= 0 && thinkingBudget < 128) {\n thinkingBudget = 128;\n }\n }\n\n const thinkingLevelRaw =\n fields?.reasoningEffort ??\n fields?.thinkingLevel ??\n convertReasoningTokensToReasoningEffort(\n model,\n fields?.maxReasoningTokens ?? fields?.thinkingBudget\n );\n let thinkingLevel = thinkingLevelRaw?.toUpperCase();\n if (thinkingLevel === \"MINIMAL\") {\n includeThoughts = false;\n }\n if (model.startsWith(\"gemini-3-pro\")) {\n // Gemini 3 Pro has only low and high.\n if (thinkingLevel === \"MINIMAL\") {\n thinkingLevel = \"LOW\";\n } else if (thinkingLevel === \"MEDIUM\") {\n thinkingLevel = \"HIGH\";\n }\n }\n\n // If we are using a model that doesn't support thinking at all (gemini 2.5 imaging)\n // or we haven't explicitly tried to set a thinking budget/level, then bail out.\n const modelVersion = getModelVersion(model);\n const modelSpecialty = getModelSpecialty(model);\n const thoughtsNotSupported =\n modelVersion === \"2.5\" && modelSpecialty === \"image\";\n if (\n thoughtsNotSupported ||\n typeof thinkingBudget === \"undefined\" ||\n typeof thinkingLevel === \"undefined\"\n ) {\n return undefined;\n }\n\n // If we have gotten this far, then we want to explicitly set if we include thoughts or not.\n const thinkingConfig: Gemini.ThinkingConfig = {\n includeThoughts,\n };\n\n // Explicitly setting the budget/level is only valid (currently) for text models.\n if (modelSpecialty === \"\") {\n if (model.startsWith(\"gemini-2.5\")) {\n thinkingConfig.thinkingBudget = thinkingBudget;\n } else {\n thinkingConfig.thinkingLevel = thinkingLevel as Gemini.ThinkingLevel;\n }\n }\n\n return thinkingConfig;\n}\n\n/**\n * Builds the `speechConfig` object for the Gemini `generationConfig`,\n * normalising the user-friendly simplified forms into the wire format.\n *\n * @param fields The combined user/call-option fields.\n * @returns A populated {@link Gemini.SpeechConfig}, or `{}`\n * when no speech config was provided.\n */\nexport function convertFieldsToSpeechConfig(\n fields: ChatGoogleFields\n): Gemini.SpeechConfig | undefined {\n const config: Gemini.SpeechConfig | SimplifiedSpeechConfig | undefined =\n fields.speechConfig;\n if (typeof config === \"undefined\") {\n return undefined;\n }\n\n function isSpeechConfig(\n config: Gemini.SpeechConfig | SimplifiedSpeechConfig\n ): config is Gemini.SpeechConfig {\n return (\n typeof config === \"object\" &&\n (Object.hasOwn(config, \"voiceConfig\") ||\n Object.hasOwn(config, \"multiSpeakerVoiceConfig\"))\n );\n }\n\n function hasLanguage(\n config: SimplifiedSpeechConfig\n ): config is SimplifiedSpeechLanguageConfig {\n return typeof config === \"object\" && Object.hasOwn(config, \"languageCode\");\n }\n\n function hasVoice(\n config: SimplifiedSpeechLanguageConfig\n ): config is SpeechVoiceLanguage {\n return Object.hasOwn(config, \"voice\");\n }\n\n // If this is already a SpeechConfig, just return it\n if (isSpeechConfig(config)) {\n return config;\n }\n\n let languageCode: string | undefined;\n let voice: SpeechVoice;\n if (hasLanguage(config)) {\n languageCode = config.languageCode;\n voice = hasVoice(config) ? config.voice : config.voices;\n } else {\n languageCode = undefined;\n voice = config;\n }\n\n let ret: Gemini.SpeechConfig;\n\n if (typeof voice === \"string\") {\n // They just provided the prebuilt voice configuration name. Use it.\n ret = {\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: voice,\n },\n },\n };\n } else {\n // This is multi-speaker, so we have speaker/name pairs\n // If we have just one (why?), turn it into an array for the moment\n const voices: SpeechSpeakerName[] = Array.isArray(voice) ? voice : [voice];\n // Go through all the speaker/name pairs and turn this into the voice config array\n const speakerVoiceConfigs: Gemini.SpeakerVoiceConfig[] = voices.map(\n (v: SpeechSpeakerName): Gemini.SpeakerVoiceConfig => ({\n speaker: v.speaker,\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: v.name,\n },\n },\n })\n );\n // Create the multi-speaker voice configuration\n ret = {\n multiSpeakerVoiceConfig: {\n speakerVoiceConfigs,\n },\n };\n }\n\n if (languageCode) {\n ret.languageCode = languageCode;\n }\n\n return ret;\n}\n"],"mappings":"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"}
@@ -22,8 +22,8 @@
22
22
  * @param {string | undefined} modelName
23
23
  * The Gemini model name (e.g., "gemini-2.5-flash"). Used to determine token range constraints.
24
24
  * @param {string | undefined} effort
25
- * The reasoning effort level: one of "none", "minimal", "low", "medium", "high".
26
- * If undefined, the function returns undefined.
25
+ * The reasoning effort level: one of "none", "minimal", "low", "medium", "high"
26
+ * (case-insensitive). If undefined, the function returns undefined.
27
27
  * @returns {number | undefined}
28
28
  * The corresponding token count for the given effort and model. Returns undefined if
29
29
  * the effort is not recognized or not provided.
@@ -35,16 +35,16 @@
35
35
  * convertReasoningEffortToReasoningTokens(undefined, "low") // 1024
36
36
  */
37
37
  function convertReasoningEffortToReasoningTokens(modelName, effort) {
38
- if (effort === void 0) return void 0;
38
+ if (effort === void 0) return;
39
39
  const minTokens = modelName?.startsWith("gemini-2.5-flash") ? 0 : 128;
40
40
  const maxTokens = modelName?.startsWith("gemini-2.5-flash") ? 24 * 1024 : 32 * 1024;
41
- switch (effort) {
41
+ switch (effort.toLowerCase()) {
42
42
  case "none":
43
43
  case "minimal": return minTokens;
44
44
  case "low": return 1024;
45
45
  case "medium": return 8 * 1024;
46
46
  case "high": return maxTokens;
47
- default: return void 0;
47
+ default: return;
48
48
  }
49
49
  }
50
50
  /**
@@ -81,7 +81,7 @@ function convertReasoningEffortToReasoningTokens(modelName, effort) {
81
81
  * convertReasoningTokensToReasoningEffort(undefined, 9000) // "high"
82
82
  */
83
83
  function convertReasoningTokensToReasoningEffort(model, reasoningTokens) {
84
- if (typeof reasoningTokens === "undefined") return void 0;
84
+ if (typeof reasoningTokens === "undefined") return;
85
85
  else if (reasoningTokens === -1) return "high";
86
86
  else if (reasoningTokens === 0) if (model?.startsWith("gemini-3-pro")) return "low";
87
87
  else return "minimal";
@@ -98,10 +98,9 @@ function convertReasoningTokensToReasoningEffort(model, reasoningTokens) {
98
98
  * {@link BaseChatGoogleParams} (such as model constructors or call options).
99
99
  */
100
100
  function convertParamsToPlatformType(params) {
101
- if (typeof params === "undefined") return void 0;
101
+ if (typeof params === "undefined") return;
102
102
  if (typeof params.platformType !== "undefined") return params.platformType;
103
103
  if (params.vertexai === true) return "gcp";
104
- return void 0;
105
104
  }
106
105
  /**
107
106
  * Returns the model version from a Gemini model name.
@@ -137,8 +136,7 @@ function convertFieldsToThinkingConfig(model, fields) {
137
136
  if (model.startsWith("gemini-2.5-pro") && typeof thinkingBudget !== "undefined") {
138
137
  if (thinkingBudget >= 0 && thinkingBudget < 128) thinkingBudget = 128;
139
138
  }
140
- const thinkingLevelRaw = fields?.reasoningEffort ?? fields?.thinkingLevel ?? convertReasoningTokensToReasoningEffort(model, fields?.maxReasoningTokens ?? fields?.thinkingBudget);
141
- let thinkingLevel = thinkingLevelRaw?.toUpperCase();
139
+ let thinkingLevel = (fields?.reasoningEffort ?? fields?.thinkingLevel ?? convertReasoningTokensToReasoningEffort(model, fields?.maxReasoningTokens ?? fields?.thinkingBudget))?.toUpperCase();
142
140
  if (thinkingLevel === "MINIMAL") includeThoughts = false;
143
141
  if (model.startsWith("gemini-3-pro")) {
144
142
  if (thinkingLevel === "MINIMAL") thinkingLevel = "LOW";
@@ -146,8 +144,7 @@ function convertFieldsToThinkingConfig(model, fields) {
146
144
  }
147
145
  const modelVersion = getModelVersion(model);
148
146
  const modelSpecialty = getModelSpecialty(model);
149
- const thoughtsNotSupported = modelVersion === "2.5" && modelSpecialty === "image";
150
- if (thoughtsNotSupported || typeof thinkingBudget === "undefined" || typeof thinkingLevel === "undefined") return void 0;
147
+ if (modelVersion === "2.5" && modelSpecialty === "image" || typeof thinkingBudget === "undefined" || typeof thinkingLevel === "undefined") return;
151
148
  const thinkingConfig = { includeThoughts };
152
149
  if (modelSpecialty === "") if (model.startsWith("gemini-2.5")) thinkingConfig.thinkingBudget = thinkingBudget;
153
150
  else thinkingConfig.thinkingLevel = thinkingLevel;
@@ -163,15 +160,15 @@ function convertFieldsToThinkingConfig(model, fields) {
163
160
  */
164
161
  function convertFieldsToSpeechConfig(fields) {
165
162
  const config = fields.speechConfig;
166
- if (typeof config === "undefined") return void 0;
167
- function isSpeechConfig(config$1) {
168
- return typeof config$1 === "object" && (Object.hasOwn(config$1, "voiceConfig") || Object.hasOwn(config$1, "multiSpeakerVoiceConfig"));
163
+ if (typeof config === "undefined") return;
164
+ function isSpeechConfig(config) {
165
+ return typeof config === "object" && (Object.hasOwn(config, "voiceConfig") || Object.hasOwn(config, "multiSpeakerVoiceConfig"));
169
166
  }
170
- function hasLanguage(config$1) {
171
- return typeof config$1 === "object" && Object.hasOwn(config$1, "languageCode");
167
+ function hasLanguage(config) {
168
+ return typeof config === "object" && Object.hasOwn(config, "languageCode");
172
169
  }
173
- function hasVoice(config$1) {
174
- return Object.hasOwn(config$1, "voice");
170
+ function hasVoice(config) {
171
+ return Object.hasOwn(config, "voice");
175
172
  }
176
173
  if (isSpeechConfig(config)) return config;
177
174
  let languageCode;
@@ -185,14 +182,10 @@ function convertFieldsToSpeechConfig(fields) {
185
182
  }
186
183
  let ret;
187
184
  if (typeof voice === "string") ret = { voiceConfig: { prebuiltVoiceConfig: { voiceName: voice } } };
188
- else {
189
- const voices = Array.isArray(voice) ? voice : [voice];
190
- const speakerVoiceConfigs = voices.map((v) => ({
191
- speaker: v.speaker,
192
- voiceConfig: { prebuiltVoiceConfig: { voiceName: v.name } }
193
- }));
194
- ret = { multiSpeakerVoiceConfig: { speakerVoiceConfigs } };
195
- }
185
+ else ret = { multiSpeakerVoiceConfig: { speakerVoiceConfigs: (Array.isArray(voice) ? voice : [voice]).map((v) => ({
186
+ speaker: v.speaker,
187
+ voiceConfig: { prebuiltVoiceConfig: { voiceName: v.name } }
188
+ })) } };
196
189
  if (languageCode) ret.languageCode = languageCode;
197
190
  return ret;
198
191
  }