@langchain/google 0.1.6-dev-1773345797648 → 0.1.7-dev-1773698445534
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/chat_models/base.cjs +10 -11
- package/dist/chat_models/base.cjs.map +1 -1
- package/dist/chat_models/base.js +3 -4
- package/dist/chat_models/base.js.map +1 -1
- package/dist/chat_models/index.cjs +3 -4
- package/dist/chat_models/index.cjs.map +1 -1
- package/dist/chat_models/index.js +1 -2
- package/dist/chat_models/index.js.map +1 -1
- package/dist/chat_models/node.cjs +5 -6
- package/dist/chat_models/node.cjs.map +1 -1
- package/dist/chat_models/node.js +1 -2
- package/dist/chat_models/node.js.map +1 -1
- package/dist/chat_models/profiles.cjs +1 -2
- package/dist/chat_models/profiles.cjs.map +1 -1
- package/dist/chat_models/profiles.js +1 -1
- package/dist/clients/index.cjs +5 -6
- package/dist/clients/index.cjs.map +1 -1
- package/dist/clients/index.js +1 -2
- package/dist/clients/index.js.map +1 -1
- package/dist/clients/node.cjs +6 -7
- package/dist/clients/node.cjs.map +1 -1
- package/dist/clients/node.js +1 -2
- package/dist/clients/node.js.map +1 -1
- package/dist/const.cjs +1 -2
- package/dist/const.cjs.map +1 -1
- package/dist/const.js +1 -1
- package/dist/converters/messages.cjs +24 -10
- package/dist/converters/messages.cjs.map +1 -1
- package/dist/converters/messages.js +22 -8
- package/dist/converters/messages.js.map +1 -1
- package/dist/converters/params.cjs +1 -2
- package/dist/converters/params.cjs.map +1 -1
- package/dist/converters/params.js +1 -1
- package/dist/converters/params.js.map +1 -1
- package/dist/converters/tools.cjs +2 -3
- package/dist/converters/tools.cjs.map +1 -1
- package/dist/converters/tools.js +1 -2
- package/dist/converters/tools.js.map +1 -1
- package/dist/index.cjs +4 -5
- package/dist/index.js +1 -2
- package/dist/node.cjs +4 -5
- package/dist/node.js +1 -2
- package/dist/utils/errors.cjs +19 -19
- package/dist/utils/errors.cjs.map +1 -1
- package/dist/utils/errors.js +18 -18
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/gcp-auth.cjs +3 -4
- package/dist/utils/gcp-auth.cjs.map +1 -1
- package/dist/utils/gcp-auth.js +1 -2
- package/dist/utils/gcp-auth.js.map +1 -1
- package/dist/utils/handler.cjs +1 -2
- package/dist/utils/handler.cjs.map +1 -1
- package/dist/utils/handler.js +1 -2
- package/dist/utils/handler.js.map +1 -1
- package/dist/utils/metadata.cjs +1 -2
- package/dist/utils/metadata.cjs.map +1 -1
- package/dist/utils/metadata.js +1 -1
- package/dist/utils/metadata.js.map +1 -1
- package/dist/utils/misc.cjs +1 -2
- package/dist/utils/misc.cjs.map +1 -1
- package/dist/utils/misc.js +1 -1
- package/dist/utils/stream.cjs +1 -2
- package/dist/utils/stream.cjs.map +1 -1
- package/dist/utils/stream.js +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.cjs","names":["InvalidInputError","InvalidToolError"],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { isOpenAITool } from \"@langchain/core/language_models/base\";\nimport {\n InteropZodType,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n type JsonSchema7Type,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type {\n BindToolsInput,\n ToolChoice,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { InvalidInputError, InvalidToolError } from \"../utils/errors.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\n\n/**\n * Adjusts a JSON Schema object type to be compatible with Gemini's function schema format.\n *\n * Gemini's function schema format has specific requirements:\n * - Type must be a string, not an array\n * - Nullable types should use the `nullable` property instead of including \"null\" in a type array\n * - Union types are not supported\n *\n * @param obj - The JSON Schema object to adjust\n * @returns The adjusted object with type converted to Gemini-compatible format\n * @throws {Error} If the type is a union type or null-only type, which Gemini cannot handle\n *\n * @internal\n */\nfunction adjustObjectType(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, any> {\n if (!Array.isArray(obj.type)) {\n return obj;\n }\n\n const len = obj.type.length;\n const nullIndex = obj.type.indexOf(\"null\");\n if (len === 2 && nullIndex >= 0) {\n // There are only two values set for the type, and one of them is \"null\".\n // Set the type to the other one and set nullable to true.\n const typeIndex = nullIndex === 0 ? 1 : 0;\n obj.type = obj.type[typeIndex];\n obj.nullable = true;\n } else if (len === 1 && nullIndex === 0) {\n // This is nullable only without a type, which doesn't make sense for Gemini\n throw new InvalidInputError(\n \"Gemini does not support null-only types in function schemas. Provide a non-null type alongside nullable.\"\n );\n } else if (len === 1) {\n // Although an array, it has only one value.\n // So set it to the string to match what Gemini expects.\n obj.type = obj?.type[0];\n } else if (len === 0) {\n // Empty type array is invalid\n throw new InvalidInputError(\n \"Gemini does not support empty type arrays in function schemas. Provide at least one type.\"\n );\n } else {\n // Anything else could be a union type, so reject it.\n throw new InvalidInputError(\n \"Gemini does not support union types in function schemas. Use a single type instead.\"\n );\n }\n return obj;\n}\n\n/**\n * Recursively removes unsupported properties from a JSON Schema object to make it\n * compatible with Gemini's function schema format.\n *\n * Gemini's function schema format does not support:\n * - `additionalProperties` property\n * - Array types (must be converted to string types with nullable flag)\n * - Union types\n *\n * @param obj - The JSON Schema object to clean\n * @returns A cleaned GeminiFunctionSchema object\n *\n * @internal\n */\nfunction removeAdditionalProperties(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n): Gemini.Tools.Schema & { $schema?: string } {\n if (typeof obj === \"object\" && obj !== null) {\n const newObj = { ...obj };\n\n if (\"additionalProperties\" in newObj) {\n delete newObj.additionalProperties;\n }\n\n adjustObjectType(newObj);\n\n for (const key in newObj) {\n if (key in newObj) {\n if (Array.isArray(newObj[key])) {\n newObj[key] = newObj[key].map(removeAdditionalProperties);\n } else if (typeof newObj[key] === \"object\" && newObj[key] !== null) {\n newObj[key] = removeAdditionalProperties(newObj[key]);\n }\n }\n }\n\n return newObj as Gemini.Tools.Schema;\n }\n\n return obj as Gemini.Tools.Schema;\n}\n\n/**\n * Converts a Zod schema or JSON Schema to Gemini function parameters format.\n *\n * This function handles the conversion of schema definitions from various formats\n * (Zod schemas, JSON Schema 7) to Gemini's function schema format. It removes\n * unsupported properties and adjusts type definitions to be compatible with Gemini.\n *\n * @param schema - The schema to convert (Zod schema or JSON Schema 7)\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nexport function schemaToGeminiParameters<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>\n>(\n schema:\n | SerializableSchema<RunOutput>\n | InteropZodType<RunOutput>\n | JsonSchema7Type\n): Gemini.Tools.Schema {\n // Gemini doesn't accept either the $schema or additionalProperties\n // attributes, so we need to explicitly remove them.\n // Zod sometimes also makes an array of type (because of .nullish()),\n // which needs cleaning up.\n const jsonSchema = removeAdditionalProperties(\n isInteropZodSchema(schema) || isSerializableSchema(schema)\n ? toJsonSchema(schema)\n : schema\n );\n const { $schema, ...rest } = jsonSchema;\n return rest;\n}\n\n/**\n * Converts a JSON Schema object to Gemini function parameters format.\n *\n * @param schema - The JSON Schema object to convert\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nfunction jsonSchemaToGeminiParameters(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema: Record<string, any>\n): Gemini.Tools.Schema {\n const jsonSchema = removeAdditionalProperties(schema);\n const { $schema, ...rest } = jsonSchema;\n\n return rest;\n}\n\n/**\n * Type guard to check if a tool is already in Gemini's native format.\n *\n * Gemini tools can be:\n * - Function declarations (`functionDeclarations`)\n * - Code execution tools (`codeExecution`)\n * - Google Search retrieval tools (`googleSearchRetrieval` or `googleSearch`)\n *\n * The full list of tool types are those defined by the `Gemini.Tool` type.\n * Updating that list requires a change to the `geminiToolAttributes` below.\n *\n * @param tool - The tool to check\n * @returns `true` if the tool is already in Gemini format, `false` otherwise\n *\n * @internal\n */\nfunction isGeminiTool(tool: BindToolsInput): tool is Gemini.Tool {\n if (typeof tool !== \"object\" || tool === null) {\n return false;\n }\n\n const geminiToolAttributes = [\n \"functionDeclarations\",\n \"codeExecution\",\n \"googleSearch\",\n \"urlContext\",\n \"googleMaps\",\n \"fileSearch\",\n \"computerUse\",\n \"mcpServers\",\n ];\n\n return geminiToolAttributes.some((attr) => attr in tool);\n}\n\n/**\n * Converts an array of LangChain tools to Gemini function declarations.\n *\n * This converter transforms various tool formats into Gemini's function declaration\n * format. It supports:\n * - LangChain structured tools (with Zod schemas)\n * - OpenAI format tools (with JSON Schema)\n * - Gemini-native function declarations (passed through)\n * - Raw function declaration objects\n *\n * @remarks\n * The converter handles each tool type differently:\n * - **LangChain tools**: Extracts name, description, and schema, converting the schema\n * to Gemini-compatible format\n * - **OpenAI tools**: Extracts function name, description, and parameters from the\n * OpenAI tool format\n * - **Gemini tools**: If the tool contains `functionDeclarations`, extracts and returns\n * them directly. Non-function Gemini tools (like `codeExecution`) are skipped.\n * - **Raw objects**: Assumes the object is a function declaration and validates it has\n * a `name` property\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather for a location\",\n * schema: z.object({\n * location: z.string().describe(\"The city name\"),\n * unit: z.enum([\"celsius\", \"fahrenheit\"]).optional()\n * }),\n * func: async (args) => { /* ... *\\/ }\n * });\n *\n * const declarations = convertToolsToGeminiFunctionDeclarations([weatherTool]);\n * // Returns: [{\n * // name: \"get_weather\",\n * // description: \"Get the weather for a location\",\n * // parameters: {\n * // type: \"object\",\n * // properties: {\n * // location: { type: \"string\", description: \"The city name\" },\n * // unit: { type: \"string\", enum: [\"celsius\", \"fahrenheit\"], nullable: true }\n * // },\n * // required: [\"location\"]\n * // }\n * // }]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Gemini function declarations ready for API submission\n * @throws {Error} If a tool cannot be converted (invalid format)\n */\nexport const convertToolsToGeminiFunctionDeclarations: Converter<\n BindToolsInput[],\n Gemini.Tools.FunctionDeclaration[]\n> = (tools) => {\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n for (const tool of tools) {\n // Skip if already in Gemini format (non-function tools)\n if (isGeminiTool(tool) && !(\"functionDeclarations\" in tool)) {\n continue;\n }\n\n // Handle Gemini format with functionDeclarations\n if (\n isGeminiTool(tool) &&\n \"functionDeclarations\" in tool &&\n Array.isArray(tool.functionDeclarations)\n ) {\n functionDeclarations.push(...tool.functionDeclarations);\n continue;\n }\n\n // Handle LangChain structured tools\n if (isLangChainTool(tool)) {\n const jsonSchema = schemaToGeminiParameters(tool.schema);\n functionDeclarations.push({\n name: tool.name,\n description: tool.description ?? `A function available to call.`,\n parameters: jsonSchema,\n });\n continue;\n }\n\n // Handle OpenAI format tools\n if (isOpenAITool(tool)) {\n functionDeclarations.push({\n name: tool.function.name,\n description:\n tool.function.description ?? `A function available to call.`,\n parameters: jsonSchemaToGeminiParameters(tool.function.parameters),\n });\n continue;\n }\n\n // Handle raw function declaration objects\n if (\n typeof tool === \"object\" &&\n tool !== null &&\n \"name\" in tool &&\n typeof tool.name === \"string\"\n ) {\n const funcDecl = tool as Partial<Gemini.Tools.FunctionDeclaration>;\n functionDeclarations.push({\n name: funcDecl.name!,\n description: funcDecl.description ?? `A function available to call.`,\n parameters: funcDecl.parameters,\n });\n continue;\n }\n\n throw new InvalidToolError(tool);\n }\n\n return functionDeclarations;\n};\n\n/**\n * Converts an array of tools to Gemini's Tools array format.\n *\n * This converter groups function declarations into a single tool object while\n * preserving Gemini-native tools (like code execution and Google Search retrieval)\n * as separate tool objects. This matches Gemini's API structure where:\n * - Function declarations are grouped together in a single tool\n * - Non-function tools (codeExecution, googleSearchRetrieval) are separate tools\n *\n * @remarks\n * The conversion process:\n * 1. Separates Gemini-native tools from function declarations\n * 2. Converts LangChain/OpenAI tools to function declarations\n * 3. Groups all function declarations into a single Tool object\n * 4. Returns an array with the grouped function declarations tool plus any\n * non-function Gemini tools\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather\",\n * schema: z.object({ location: z.string() }),\n * func: async () => {}\n * });\n *\n * const geminiCodeTool: Gemini.Tool = {\n * codeExecution: { enabled: true }\n * };\n *\n * const tools = convertToolsToGeminiTools([weatherTool, geminiCodeTool]);\n * // Returns: [\n * // { functionDeclarations: [{ name: \"get_weather\", ... }] },\n * // { codeExecution: { enabled: true } }\n * // ]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Tool objects ready for API submission\n */\nexport const convertToolsToGeminiTools: Converter<\n BindToolsInput[],\n Gemini.Tool[]\n> = (tools) => {\n if (tools.length === 0) {\n return [];\n }\n\n const geminiTools: Gemini.Tool[] = [];\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n // Separate Gemini-native tools from function declarations\n for (const tool of tools) {\n if (isGeminiTool(tool)) {\n if (\"functionDeclarations\" in tool) {\n // Already a Gemini tool with function declarations\n geminiTools.push(tool);\n } else {\n // Non-function Gemini tool (codeExecution, googleSearch, etc.)\n geminiTools.push(tool);\n }\n } else if (\"googleSearchRetrieval\" in tool) {\n // Convert the obsolete search into the modern one\n const searchTool: Gemini.Tool = {\n googleSearch: {},\n };\n geminiTools.push(searchTool);\n } else {\n // Convert to function declaration\n const funcDecls = convertToolsToGeminiFunctionDeclarations([tool]);\n functionDeclarations.push(...funcDecls);\n }\n }\n\n // Add function declarations as a single tool if we have any\n if (functionDeclarations.length > 0) {\n geminiTools.push({\n functionDeclarations,\n });\n }\n\n return geminiTools;\n};\n\n/**\n * Converts tool choice to Gemini's function calling configuration.\n *\n * This converter transforms LangChain's tool choice format into Gemini's\n * `toolConfig.functionCallingConfig` format. The configuration is only\n * created when tools are present (`hasTools` is true).\n *\n * @remarks\n * The conversion logic:\n * - If `hasTools` is `false`, returns `undefined` (no config needed)\n * - If `toolChoice` is provided, converts it to a Gemini mode:\n * - `\"auto\"` → `\"AUTO\"` - Model decides whether to call functions\n * - `\"any\"` or `\"required\"` → `\"ANY\"` - Model must call at least one function\n * - `\"none\"` → `\"NONE\"` - Model cannot call functions\n * - Function name string → `\"ANY\"` - Forces function use\n * - Object with `mode` → Maps the mode string to Gemini format\n * - Object with `function` → `\"ANY\"` - Forces function use\n * - If `toolChoice` is not provided but tools exist, defaults to `\"AUTO\"`\n *\n * Gemini's function calling modes:\n * - **AUTO**: The model can choose whether to call functions based on the conversation\n * - **ANY**: The model must call at least one function before responding\n * - **NONE**: The model cannot call any functions\n *\n * When a specific function is requested (via function name string or `{ function: { name } }`),\n * the converter uses `\"ANY\"` mode to force function usage, as Gemini doesn't support\n * specifying individual functions in the mode.\n *\n * @example\n * ```typescript\n * // With explicit tool choice\n * convertToolChoiceToGeminiConfig(\"auto\", true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // Required mode - must call a function\n * convertToolChoiceToGeminiConfig(\"any\", true);\n * // Returns: { functionCallingConfig: { mode: \"ANY\" } }\n *\n * // Without tool choice (defaults to AUTO when tools exist)\n * convertToolChoiceToGeminiConfig(undefined, true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // No tools - returns undefined\n * convertToolChoiceToGeminiConfig(\"auto\", false);\n * // Returns: undefined\n * ```\n *\n * @param toolChoice - The tool choice option from LangChain (string, object, or undefined)\n * @param hasTools - Whether tools are present. If false, returns undefined\n * @returns The Gemini tool configuration object, or undefined if no tools are present\n */\nexport function convertToolChoiceToGeminiConfig(\n toolChoice: ToolChoice | undefined,\n hasTools: boolean\n): Gemini.Tools.ToolConfig | undefined {\n if (!hasTools || toolChoice === undefined) {\n return undefined;\n }\n\n let mode: Gemini.Tools.FunctionCallingConfigMode | undefined;\n let allowedFunctionNames: string[] | undefined;\n\n let toolChoiceMode: string | undefined;\n let toolChoiceFunction: string | string[] | undefined;\n if (typeof toolChoice === \"object\") {\n toolChoiceMode = toolChoice.mode;\n toolChoiceFunction = toolChoice.function?.name;\n } else {\n toolChoiceMode = toolChoice;\n }\n\n if (toolChoiceMode === \"auto\") {\n mode = \"AUTO\";\n } else if (toolChoiceMode === \"any\" || toolChoiceMode === \"required\") {\n mode = \"ANY\";\n } else if (toolChoiceMode === \"none\") {\n mode = \"NONE\";\n } else if (typeof toolChoiceMode === \"string\") {\n mode = \"ANY\";\n toolChoiceFunction = toolChoiceMode;\n }\n\n if (toolChoiceFunction) {\n if (Array.isArray(toolChoiceFunction)) {\n allowedFunctionNames = toolChoiceFunction;\n } else {\n allowedFunctionNames = [toolChoiceFunction];\n }\n }\n\n const functionCallingConfig: Gemini.Tools.FunctionCallingConfig = {};\n if (allowedFunctionNames?.length) {\n functionCallingConfig.allowedFunctionNames = allowedFunctionNames;\n }\n if (mode) {\n functionCallingConfig.mode = mode;\n }\n return {\n functionCallingConfig,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tools.cjs","names":["InvalidInputError","InvalidToolError"],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { isOpenAITool } from \"@langchain/core/language_models/base\";\nimport {\n InteropZodType,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n type JsonSchema7Type,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type {\n BindToolsInput,\n ToolChoice,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { InvalidInputError, InvalidToolError } from \"../utils/errors.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\n\n/**\n * Adjusts a JSON Schema object type to be compatible with Gemini's function schema format.\n *\n * Gemini's function schema format has specific requirements:\n * - Type must be a string, not an array\n * - Nullable types should use the `nullable` property instead of including \"null\" in a type array\n * - Union types are not supported\n *\n * @param obj - The JSON Schema object to adjust\n * @returns The adjusted object with type converted to Gemini-compatible format\n * @throws {Error} If the type is a union type or null-only type, which Gemini cannot handle\n *\n * @internal\n */\nfunction adjustObjectType(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, any> {\n if (!Array.isArray(obj.type)) {\n return obj;\n }\n\n const len = obj.type.length;\n const nullIndex = obj.type.indexOf(\"null\");\n if (len === 2 && nullIndex >= 0) {\n // There are only two values set for the type, and one of them is \"null\".\n // Set the type to the other one and set nullable to true.\n const typeIndex = nullIndex === 0 ? 1 : 0;\n obj.type = obj.type[typeIndex];\n obj.nullable = true;\n } else if (len === 1 && nullIndex === 0) {\n // This is nullable only without a type, which doesn't make sense for Gemini\n throw new InvalidInputError(\n \"Gemini does not support null-only types in function schemas. Provide a non-null type alongside nullable.\"\n );\n } else if (len === 1) {\n // Although an array, it has only one value.\n // So set it to the string to match what Gemini expects.\n obj.type = obj?.type[0];\n } else if (len === 0) {\n // Empty type array is invalid\n throw new InvalidInputError(\n \"Gemini does not support empty type arrays in function schemas. Provide at least one type.\"\n );\n } else {\n // Anything else could be a union type, so reject it.\n throw new InvalidInputError(\n \"Gemini does not support union types in function schemas. Use a single type instead.\"\n );\n }\n return obj;\n}\n\n/**\n * Recursively removes unsupported properties from a JSON Schema object to make it\n * compatible with Gemini's function schema format.\n *\n * Gemini's function schema format does not support:\n * - `additionalProperties` property\n * - Array types (must be converted to string types with nullable flag)\n * - Union types\n *\n * @param obj - The JSON Schema object to clean\n * @returns A cleaned GeminiFunctionSchema object\n *\n * @internal\n */\nfunction removeAdditionalProperties(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n): Gemini.Tools.Schema & { $schema?: string } {\n if (typeof obj === \"object\" && obj !== null) {\n const newObj = { ...obj };\n\n if (\"additionalProperties\" in newObj) {\n delete newObj.additionalProperties;\n }\n\n adjustObjectType(newObj);\n\n for (const key in newObj) {\n if (key in newObj) {\n if (Array.isArray(newObj[key])) {\n newObj[key] = newObj[key].map(removeAdditionalProperties);\n } else if (typeof newObj[key] === \"object\" && newObj[key] !== null) {\n newObj[key] = removeAdditionalProperties(newObj[key]);\n }\n }\n }\n\n return newObj as Gemini.Tools.Schema;\n }\n\n return obj as Gemini.Tools.Schema;\n}\n\n/**\n * Converts a Zod schema or JSON Schema to Gemini function parameters format.\n *\n * This function handles the conversion of schema definitions from various formats\n * (Zod schemas, JSON Schema 7) to Gemini's function schema format. It removes\n * unsupported properties and adjusts type definitions to be compatible with Gemini.\n *\n * @param schema - The schema to convert (Zod schema or JSON Schema 7)\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nexport function schemaToGeminiParameters<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>\n>(\n schema:\n | SerializableSchema<RunOutput>\n | InteropZodType<RunOutput>\n | JsonSchema7Type\n): Gemini.Tools.Schema {\n // Gemini doesn't accept either the $schema or additionalProperties\n // attributes, so we need to explicitly remove them.\n // Zod sometimes also makes an array of type (because of .nullish()),\n // which needs cleaning up.\n const jsonSchema = removeAdditionalProperties(\n isInteropZodSchema(schema) || isSerializableSchema(schema)\n ? toJsonSchema(schema)\n : schema\n );\n const { $schema, ...rest } = jsonSchema;\n return rest;\n}\n\n/**\n * Converts a JSON Schema object to Gemini function parameters format.\n *\n * @param schema - The JSON Schema object to convert\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nfunction jsonSchemaToGeminiParameters(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema: Record<string, any>\n): Gemini.Tools.Schema {\n const jsonSchema = removeAdditionalProperties(schema);\n const { $schema, ...rest } = jsonSchema;\n\n return rest;\n}\n\n/**\n * Type guard to check if a tool is already in Gemini's native format.\n *\n * Gemini tools can be:\n * - Function declarations (`functionDeclarations`)\n * - Code execution tools (`codeExecution`)\n * - Google Search retrieval tools (`googleSearchRetrieval` or `googleSearch`)\n *\n * The full list of tool types are those defined by the `Gemini.Tool` type.\n * Updating that list requires a change to the `geminiToolAttributes` below.\n *\n * @param tool - The tool to check\n * @returns `true` if the tool is already in Gemini format, `false` otherwise\n *\n * @internal\n */\nfunction isGeminiTool(tool: BindToolsInput): tool is Gemini.Tool {\n if (typeof tool !== \"object\" || tool === null) {\n return false;\n }\n\n const geminiToolAttributes = [\n \"functionDeclarations\",\n \"codeExecution\",\n \"googleSearch\",\n \"urlContext\",\n \"googleMaps\",\n \"fileSearch\",\n \"computerUse\",\n \"mcpServers\",\n ];\n\n return geminiToolAttributes.some((attr) => attr in tool);\n}\n\n/**\n * Converts an array of LangChain tools to Gemini function declarations.\n *\n * This converter transforms various tool formats into Gemini's function declaration\n * format. It supports:\n * - LangChain structured tools (with Zod schemas)\n * - OpenAI format tools (with JSON Schema)\n * - Gemini-native function declarations (passed through)\n * - Raw function declaration objects\n *\n * @remarks\n * The converter handles each tool type differently:\n * - **LangChain tools**: Extracts name, description, and schema, converting the schema\n * to Gemini-compatible format\n * - **OpenAI tools**: Extracts function name, description, and parameters from the\n * OpenAI tool format\n * - **Gemini tools**: If the tool contains `functionDeclarations`, extracts and returns\n * them directly. Non-function Gemini tools (like `codeExecution`) are skipped.\n * - **Raw objects**: Assumes the object is a function declaration and validates it has\n * a `name` property\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather for a location\",\n * schema: z.object({\n * location: z.string().describe(\"The city name\"),\n * unit: z.enum([\"celsius\", \"fahrenheit\"]).optional()\n * }),\n * func: async (args) => { /* ... *\\/ }\n * });\n *\n * const declarations = convertToolsToGeminiFunctionDeclarations([weatherTool]);\n * // Returns: [{\n * // name: \"get_weather\",\n * // description: \"Get the weather for a location\",\n * // parameters: {\n * // type: \"object\",\n * // properties: {\n * // location: { type: \"string\", description: \"The city name\" },\n * // unit: { type: \"string\", enum: [\"celsius\", \"fahrenheit\"], nullable: true }\n * // },\n * // required: [\"location\"]\n * // }\n * // }]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Gemini function declarations ready for API submission\n * @throws {Error} If a tool cannot be converted (invalid format)\n */\nexport const convertToolsToGeminiFunctionDeclarations: Converter<\n BindToolsInput[],\n Gemini.Tools.FunctionDeclaration[]\n> = (tools) => {\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n for (const tool of tools) {\n // Skip if already in Gemini format (non-function tools)\n if (isGeminiTool(tool) && !(\"functionDeclarations\" in tool)) {\n continue;\n }\n\n // Handle Gemini format with functionDeclarations\n if (\n isGeminiTool(tool) &&\n \"functionDeclarations\" in tool &&\n Array.isArray(tool.functionDeclarations)\n ) {\n functionDeclarations.push(...tool.functionDeclarations);\n continue;\n }\n\n // Handle LangChain structured tools\n if (isLangChainTool(tool)) {\n const jsonSchema = schemaToGeminiParameters(tool.schema);\n functionDeclarations.push({\n name: tool.name,\n description: tool.description ?? `A function available to call.`,\n parameters: jsonSchema,\n });\n continue;\n }\n\n // Handle OpenAI format tools\n if (isOpenAITool(tool)) {\n functionDeclarations.push({\n name: tool.function.name,\n description:\n tool.function.description ?? `A function available to call.`,\n parameters: jsonSchemaToGeminiParameters(tool.function.parameters),\n });\n continue;\n }\n\n // Handle raw function declaration objects\n if (\n typeof tool === \"object\" &&\n tool !== null &&\n \"name\" in tool &&\n typeof tool.name === \"string\"\n ) {\n const funcDecl = tool as Partial<Gemini.Tools.FunctionDeclaration>;\n functionDeclarations.push({\n name: funcDecl.name!,\n description: funcDecl.description ?? `A function available to call.`,\n parameters: funcDecl.parameters,\n });\n continue;\n }\n\n throw new InvalidToolError(tool);\n }\n\n return functionDeclarations;\n};\n\n/**\n * Converts an array of tools to Gemini's Tools array format.\n *\n * This converter groups function declarations into a single tool object while\n * preserving Gemini-native tools (like code execution and Google Search retrieval)\n * as separate tool objects. This matches Gemini's API structure where:\n * - Function declarations are grouped together in a single tool\n * - Non-function tools (codeExecution, googleSearchRetrieval) are separate tools\n *\n * @remarks\n * The conversion process:\n * 1. Separates Gemini-native tools from function declarations\n * 2. Converts LangChain/OpenAI tools to function declarations\n * 3. Groups all function declarations into a single Tool object\n * 4. Returns an array with the grouped function declarations tool plus any\n * non-function Gemini tools\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather\",\n * schema: z.object({ location: z.string() }),\n * func: async () => {}\n * });\n *\n * const geminiCodeTool: Gemini.Tool = {\n * codeExecution: { enabled: true }\n * };\n *\n * const tools = convertToolsToGeminiTools([weatherTool, geminiCodeTool]);\n * // Returns: [\n * // { functionDeclarations: [{ name: \"get_weather\", ... }] },\n * // { codeExecution: { enabled: true } }\n * // ]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Tool objects ready for API submission\n */\nexport const convertToolsToGeminiTools: Converter<\n BindToolsInput[],\n Gemini.Tool[]\n> = (tools) => {\n if (tools.length === 0) {\n return [];\n }\n\n const geminiTools: Gemini.Tool[] = [];\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n // Separate Gemini-native tools from function declarations\n for (const tool of tools) {\n if (isGeminiTool(tool)) {\n if (\"functionDeclarations\" in tool) {\n // Already a Gemini tool with function declarations\n geminiTools.push(tool);\n } else {\n // Non-function Gemini tool (codeExecution, googleSearch, etc.)\n geminiTools.push(tool);\n }\n } else if (\"googleSearchRetrieval\" in tool) {\n // Convert the obsolete search into the modern one\n const searchTool: Gemini.Tool = {\n googleSearch: {},\n };\n geminiTools.push(searchTool);\n } else {\n // Convert to function declaration\n const funcDecls = convertToolsToGeminiFunctionDeclarations([tool]);\n functionDeclarations.push(...funcDecls);\n }\n }\n\n // Add function declarations as a single tool if we have any\n if (functionDeclarations.length > 0) {\n geminiTools.push({\n functionDeclarations,\n });\n }\n\n return geminiTools;\n};\n\n/**\n * Converts tool choice to Gemini's function calling configuration.\n *\n * This converter transforms LangChain's tool choice format into Gemini's\n * `toolConfig.functionCallingConfig` format. The configuration is only\n * created when tools are present (`hasTools` is true).\n *\n * @remarks\n * The conversion logic:\n * - If `hasTools` is `false`, returns `undefined` (no config needed)\n * - If `toolChoice` is provided, converts it to a Gemini mode:\n * - `\"auto\"` → `\"AUTO\"` - Model decides whether to call functions\n * - `\"any\"` or `\"required\"` → `\"ANY\"` - Model must call at least one function\n * - `\"none\"` → `\"NONE\"` - Model cannot call functions\n * - Function name string → `\"ANY\"` - Forces function use\n * - Object with `mode` → Maps the mode string to Gemini format\n * - Object with `function` → `\"ANY\"` - Forces function use\n * - If `toolChoice` is not provided but tools exist, defaults to `\"AUTO\"`\n *\n * Gemini's function calling modes:\n * - **AUTO**: The model can choose whether to call functions based on the conversation\n * - **ANY**: The model must call at least one function before responding\n * - **NONE**: The model cannot call any functions\n *\n * When a specific function is requested (via function name string or `{ function: { name } }`),\n * the converter uses `\"ANY\"` mode to force function usage, as Gemini doesn't support\n * specifying individual functions in the mode.\n *\n * @example\n * ```typescript\n * // With explicit tool choice\n * convertToolChoiceToGeminiConfig(\"auto\", true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // Required mode - must call a function\n * convertToolChoiceToGeminiConfig(\"any\", true);\n * // Returns: { functionCallingConfig: { mode: \"ANY\" } }\n *\n * // Without tool choice (defaults to AUTO when tools exist)\n * convertToolChoiceToGeminiConfig(undefined, true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // No tools - returns undefined\n * convertToolChoiceToGeminiConfig(\"auto\", false);\n * // Returns: undefined\n * ```\n *\n * @param toolChoice - The tool choice option from LangChain (string, object, or undefined)\n * @param hasTools - Whether tools are present. If false, returns undefined\n * @returns The Gemini tool configuration object, or undefined if no tools are present\n */\nexport function convertToolChoiceToGeminiConfig(\n toolChoice: ToolChoice | undefined,\n hasTools: boolean\n): Gemini.Tools.ToolConfig | undefined {\n if (!hasTools || toolChoice === undefined) {\n return undefined;\n }\n\n let mode: Gemini.Tools.FunctionCallingConfigMode | undefined;\n let allowedFunctionNames: string[] | undefined;\n\n let toolChoiceMode: string | undefined;\n let toolChoiceFunction: string | string[] | undefined;\n if (typeof toolChoice === \"object\") {\n toolChoiceMode = toolChoice.mode;\n toolChoiceFunction = toolChoice.function?.name;\n } else {\n toolChoiceMode = toolChoice;\n }\n\n if (toolChoiceMode === \"auto\") {\n mode = \"AUTO\";\n } else if (toolChoiceMode === \"any\" || toolChoiceMode === \"required\") {\n mode = \"ANY\";\n } else if (toolChoiceMode === \"none\") {\n mode = \"NONE\";\n } else if (typeof toolChoiceMode === \"string\") {\n mode = \"ANY\";\n toolChoiceFunction = toolChoiceMode;\n }\n\n if (toolChoiceFunction) {\n if (Array.isArray(toolChoiceFunction)) {\n allowedFunctionNames = toolChoiceFunction;\n } else {\n allowedFunctionNames = [toolChoiceFunction];\n }\n }\n\n const functionCallingConfig: Gemini.Tools.FunctionCallingConfig = {};\n if (allowedFunctionNames?.length) {\n functionCallingConfig.allowedFunctionNames = allowedFunctionNames;\n }\n if (mode) {\n functionCallingConfig.mode = mode;\n }\n return {\n functionCallingConfig,\n };\n}\n"],"mappings":"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"}
|
package/dist/converters/tools.js
CHANGED
|
@@ -4,7 +4,6 @@ import { isInteropZodSchema } from "@langchain/core/utils/types";
|
|
|
4
4
|
import { isLangChainTool } from "@langchain/core/utils/function_calling";
|
|
5
5
|
import { isOpenAITool } from "@langchain/core/language_models/base";
|
|
6
6
|
import { isSerializableSchema } from "@langchain/core/utils/standard_schema";
|
|
7
|
-
|
|
8
7
|
//#region src/converters/tools.ts
|
|
9
8
|
/**
|
|
10
9
|
* Adjusts a JSON Schema object type to be compatible with Gemini's function schema format.
|
|
@@ -343,7 +342,7 @@ function convertToolChoiceToGeminiConfig(toolChoice, hasTools) {
|
|
|
343
342
|
if (mode) functionCallingConfig.mode = mode;
|
|
344
343
|
return { functionCallingConfig };
|
|
345
344
|
}
|
|
346
|
-
|
|
347
345
|
//#endregion
|
|
348
346
|
export { convertToolChoiceToGeminiConfig, convertToolsToGeminiTools, schemaToGeminiParameters };
|
|
347
|
+
|
|
349
348
|
//# sourceMappingURL=tools.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","names":[],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { isOpenAITool } from \"@langchain/core/language_models/base\";\nimport {\n InteropZodType,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n type JsonSchema7Type,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type {\n BindToolsInput,\n ToolChoice,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { InvalidInputError, InvalidToolError } from \"../utils/errors.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\n\n/**\n * Adjusts a JSON Schema object type to be compatible with Gemini's function schema format.\n *\n * Gemini's function schema format has specific requirements:\n * - Type must be a string, not an array\n * - Nullable types should use the `nullable` property instead of including \"null\" in a type array\n * - Union types are not supported\n *\n * @param obj - The JSON Schema object to adjust\n * @returns The adjusted object with type converted to Gemini-compatible format\n * @throws {Error} If the type is a union type or null-only type, which Gemini cannot handle\n *\n * @internal\n */\nfunction adjustObjectType(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, any> {\n if (!Array.isArray(obj.type)) {\n return obj;\n }\n\n const len = obj.type.length;\n const nullIndex = obj.type.indexOf(\"null\");\n if (len === 2 && nullIndex >= 0) {\n // There are only two values set for the type, and one of them is \"null\".\n // Set the type to the other one and set nullable to true.\n const typeIndex = nullIndex === 0 ? 1 : 0;\n obj.type = obj.type[typeIndex];\n obj.nullable = true;\n } else if (len === 1 && nullIndex === 0) {\n // This is nullable only without a type, which doesn't make sense for Gemini\n throw new InvalidInputError(\n \"Gemini does not support null-only types in function schemas. Provide a non-null type alongside nullable.\"\n );\n } else if (len === 1) {\n // Although an array, it has only one value.\n // So set it to the string to match what Gemini expects.\n obj.type = obj?.type[0];\n } else if (len === 0) {\n // Empty type array is invalid\n throw new InvalidInputError(\n \"Gemini does not support empty type arrays in function schemas. Provide at least one type.\"\n );\n } else {\n // Anything else could be a union type, so reject it.\n throw new InvalidInputError(\n \"Gemini does not support union types in function schemas. Use a single type instead.\"\n );\n }\n return obj;\n}\n\n/**\n * Recursively removes unsupported properties from a JSON Schema object to make it\n * compatible with Gemini's function schema format.\n *\n * Gemini's function schema format does not support:\n * - `additionalProperties` property\n * - Array types (must be converted to string types with nullable flag)\n * - Union types\n *\n * @param obj - The JSON Schema object to clean\n * @returns A cleaned GeminiFunctionSchema object\n *\n * @internal\n */\nfunction removeAdditionalProperties(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n): Gemini.Tools.Schema & { $schema?: string } {\n if (typeof obj === \"object\" && obj !== null) {\n const newObj = { ...obj };\n\n if (\"additionalProperties\" in newObj) {\n delete newObj.additionalProperties;\n }\n\n adjustObjectType(newObj);\n\n for (const key in newObj) {\n if (key in newObj) {\n if (Array.isArray(newObj[key])) {\n newObj[key] = newObj[key].map(removeAdditionalProperties);\n } else if (typeof newObj[key] === \"object\" && newObj[key] !== null) {\n newObj[key] = removeAdditionalProperties(newObj[key]);\n }\n }\n }\n\n return newObj as Gemini.Tools.Schema;\n }\n\n return obj as Gemini.Tools.Schema;\n}\n\n/**\n * Converts a Zod schema or JSON Schema to Gemini function parameters format.\n *\n * This function handles the conversion of schema definitions from various formats\n * (Zod schemas, JSON Schema 7) to Gemini's function schema format. It removes\n * unsupported properties and adjusts type definitions to be compatible with Gemini.\n *\n * @param schema - The schema to convert (Zod schema or JSON Schema 7)\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nexport function schemaToGeminiParameters<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>\n>(\n schema:\n | SerializableSchema<RunOutput>\n | InteropZodType<RunOutput>\n | JsonSchema7Type\n): Gemini.Tools.Schema {\n // Gemini doesn't accept either the $schema or additionalProperties\n // attributes, so we need to explicitly remove them.\n // Zod sometimes also makes an array of type (because of .nullish()),\n // which needs cleaning up.\n const jsonSchema = removeAdditionalProperties(\n isInteropZodSchema(schema) || isSerializableSchema(schema)\n ? toJsonSchema(schema)\n : schema\n );\n const { $schema, ...rest } = jsonSchema;\n return rest;\n}\n\n/**\n * Converts a JSON Schema object to Gemini function parameters format.\n *\n * @param schema - The JSON Schema object to convert\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nfunction jsonSchemaToGeminiParameters(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema: Record<string, any>\n): Gemini.Tools.Schema {\n const jsonSchema = removeAdditionalProperties(schema);\n const { $schema, ...rest } = jsonSchema;\n\n return rest;\n}\n\n/**\n * Type guard to check if a tool is already in Gemini's native format.\n *\n * Gemini tools can be:\n * - Function declarations (`functionDeclarations`)\n * - Code execution tools (`codeExecution`)\n * - Google Search retrieval tools (`googleSearchRetrieval` or `googleSearch`)\n *\n * The full list of tool types are those defined by the `Gemini.Tool` type.\n * Updating that list requires a change to the `geminiToolAttributes` below.\n *\n * @param tool - The tool to check\n * @returns `true` if the tool is already in Gemini format, `false` otherwise\n *\n * @internal\n */\nfunction isGeminiTool(tool: BindToolsInput): tool is Gemini.Tool {\n if (typeof tool !== \"object\" || tool === null) {\n return false;\n }\n\n const geminiToolAttributes = [\n \"functionDeclarations\",\n \"codeExecution\",\n \"googleSearch\",\n \"urlContext\",\n \"googleMaps\",\n \"fileSearch\",\n \"computerUse\",\n \"mcpServers\",\n ];\n\n return geminiToolAttributes.some((attr) => attr in tool);\n}\n\n/**\n * Converts an array of LangChain tools to Gemini function declarations.\n *\n * This converter transforms various tool formats into Gemini's function declaration\n * format. It supports:\n * - LangChain structured tools (with Zod schemas)\n * - OpenAI format tools (with JSON Schema)\n * - Gemini-native function declarations (passed through)\n * - Raw function declaration objects\n *\n * @remarks\n * The converter handles each tool type differently:\n * - **LangChain tools**: Extracts name, description, and schema, converting the schema\n * to Gemini-compatible format\n * - **OpenAI tools**: Extracts function name, description, and parameters from the\n * OpenAI tool format\n * - **Gemini tools**: If the tool contains `functionDeclarations`, extracts and returns\n * them directly. Non-function Gemini tools (like `codeExecution`) are skipped.\n * - **Raw objects**: Assumes the object is a function declaration and validates it has\n * a `name` property\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather for a location\",\n * schema: z.object({\n * location: z.string().describe(\"The city name\"),\n * unit: z.enum([\"celsius\", \"fahrenheit\"]).optional()\n * }),\n * func: async (args) => { /* ... *\\/ }\n * });\n *\n * const declarations = convertToolsToGeminiFunctionDeclarations([weatherTool]);\n * // Returns: [{\n * // name: \"get_weather\",\n * // description: \"Get the weather for a location\",\n * // parameters: {\n * // type: \"object\",\n * // properties: {\n * // location: { type: \"string\", description: \"The city name\" },\n * // unit: { type: \"string\", enum: [\"celsius\", \"fahrenheit\"], nullable: true }\n * // },\n * // required: [\"location\"]\n * // }\n * // }]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Gemini function declarations ready for API submission\n * @throws {Error} If a tool cannot be converted (invalid format)\n */\nexport const convertToolsToGeminiFunctionDeclarations: Converter<\n BindToolsInput[],\n Gemini.Tools.FunctionDeclaration[]\n> = (tools) => {\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n for (const tool of tools) {\n // Skip if already in Gemini format (non-function tools)\n if (isGeminiTool(tool) && !(\"functionDeclarations\" in tool)) {\n continue;\n }\n\n // Handle Gemini format with functionDeclarations\n if (\n isGeminiTool(tool) &&\n \"functionDeclarations\" in tool &&\n Array.isArray(tool.functionDeclarations)\n ) {\n functionDeclarations.push(...tool.functionDeclarations);\n continue;\n }\n\n // Handle LangChain structured tools\n if (isLangChainTool(tool)) {\n const jsonSchema = schemaToGeminiParameters(tool.schema);\n functionDeclarations.push({\n name: tool.name,\n description: tool.description ?? `A function available to call.`,\n parameters: jsonSchema,\n });\n continue;\n }\n\n // Handle OpenAI format tools\n if (isOpenAITool(tool)) {\n functionDeclarations.push({\n name: tool.function.name,\n description:\n tool.function.description ?? `A function available to call.`,\n parameters: jsonSchemaToGeminiParameters(tool.function.parameters),\n });\n continue;\n }\n\n // Handle raw function declaration objects\n if (\n typeof tool === \"object\" &&\n tool !== null &&\n \"name\" in tool &&\n typeof tool.name === \"string\"\n ) {\n const funcDecl = tool as Partial<Gemini.Tools.FunctionDeclaration>;\n functionDeclarations.push({\n name: funcDecl.name!,\n description: funcDecl.description ?? `A function available to call.`,\n parameters: funcDecl.parameters,\n });\n continue;\n }\n\n throw new InvalidToolError(tool);\n }\n\n return functionDeclarations;\n};\n\n/**\n * Converts an array of tools to Gemini's Tools array format.\n *\n * This converter groups function declarations into a single tool object while\n * preserving Gemini-native tools (like code execution and Google Search retrieval)\n * as separate tool objects. This matches Gemini's API structure where:\n * - Function declarations are grouped together in a single tool\n * - Non-function tools (codeExecution, googleSearchRetrieval) are separate tools\n *\n * @remarks\n * The conversion process:\n * 1. Separates Gemini-native tools from function declarations\n * 2. Converts LangChain/OpenAI tools to function declarations\n * 3. Groups all function declarations into a single Tool object\n * 4. Returns an array with the grouped function declarations tool plus any\n * non-function Gemini tools\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather\",\n * schema: z.object({ location: z.string() }),\n * func: async () => {}\n * });\n *\n * const geminiCodeTool: Gemini.Tool = {\n * codeExecution: { enabled: true }\n * };\n *\n * const tools = convertToolsToGeminiTools([weatherTool, geminiCodeTool]);\n * // Returns: [\n * // { functionDeclarations: [{ name: \"get_weather\", ... }] },\n * // { codeExecution: { enabled: true } }\n * // ]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Tool objects ready for API submission\n */\nexport const convertToolsToGeminiTools: Converter<\n BindToolsInput[],\n Gemini.Tool[]\n> = (tools) => {\n if (tools.length === 0) {\n return [];\n }\n\n const geminiTools: Gemini.Tool[] = [];\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n // Separate Gemini-native tools from function declarations\n for (const tool of tools) {\n if (isGeminiTool(tool)) {\n if (\"functionDeclarations\" in tool) {\n // Already a Gemini tool with function declarations\n geminiTools.push(tool);\n } else {\n // Non-function Gemini tool (codeExecution, googleSearch, etc.)\n geminiTools.push(tool);\n }\n } else if (\"googleSearchRetrieval\" in tool) {\n // Convert the obsolete search into the modern one\n const searchTool: Gemini.Tool = {\n googleSearch: {},\n };\n geminiTools.push(searchTool);\n } else {\n // Convert to function declaration\n const funcDecls = convertToolsToGeminiFunctionDeclarations([tool]);\n functionDeclarations.push(...funcDecls);\n }\n }\n\n // Add function declarations as a single tool if we have any\n if (functionDeclarations.length > 0) {\n geminiTools.push({\n functionDeclarations,\n });\n }\n\n return geminiTools;\n};\n\n/**\n * Converts tool choice to Gemini's function calling configuration.\n *\n * This converter transforms LangChain's tool choice format into Gemini's\n * `toolConfig.functionCallingConfig` format. The configuration is only\n * created when tools are present (`hasTools` is true).\n *\n * @remarks\n * The conversion logic:\n * - If `hasTools` is `false`, returns `undefined` (no config needed)\n * - If `toolChoice` is provided, converts it to a Gemini mode:\n * - `\"auto\"` → `\"AUTO\"` - Model decides whether to call functions\n * - `\"any\"` or `\"required\"` → `\"ANY\"` - Model must call at least one function\n * - `\"none\"` → `\"NONE\"` - Model cannot call functions\n * - Function name string → `\"ANY\"` - Forces function use\n * - Object with `mode` → Maps the mode string to Gemini format\n * - Object with `function` → `\"ANY\"` - Forces function use\n * - If `toolChoice` is not provided but tools exist, defaults to `\"AUTO\"`\n *\n * Gemini's function calling modes:\n * - **AUTO**: The model can choose whether to call functions based on the conversation\n * - **ANY**: The model must call at least one function before responding\n * - **NONE**: The model cannot call any functions\n *\n * When a specific function is requested (via function name string or `{ function: { name } }`),\n * the converter uses `\"ANY\"` mode to force function usage, as Gemini doesn't support\n * specifying individual functions in the mode.\n *\n * @example\n * ```typescript\n * // With explicit tool choice\n * convertToolChoiceToGeminiConfig(\"auto\", true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // Required mode - must call a function\n * convertToolChoiceToGeminiConfig(\"any\", true);\n * // Returns: { functionCallingConfig: { mode: \"ANY\" } }\n *\n * // Without tool choice (defaults to AUTO when tools exist)\n * convertToolChoiceToGeminiConfig(undefined, true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // No tools - returns undefined\n * convertToolChoiceToGeminiConfig(\"auto\", false);\n * // Returns: undefined\n * ```\n *\n * @param toolChoice - The tool choice option from LangChain (string, object, or undefined)\n * @param hasTools - Whether tools are present. If false, returns undefined\n * @returns The Gemini tool configuration object, or undefined if no tools are present\n */\nexport function convertToolChoiceToGeminiConfig(\n toolChoice: ToolChoice | undefined,\n hasTools: boolean\n): Gemini.Tools.ToolConfig | undefined {\n if (!hasTools || toolChoice === undefined) {\n return undefined;\n }\n\n let mode: Gemini.Tools.FunctionCallingConfigMode | undefined;\n let allowedFunctionNames: string[] | undefined;\n\n let toolChoiceMode: string | undefined;\n let toolChoiceFunction: string | string[] | undefined;\n if (typeof toolChoice === \"object\") {\n toolChoiceMode = toolChoice.mode;\n toolChoiceFunction = toolChoice.function?.name;\n } else {\n toolChoiceMode = toolChoice;\n }\n\n if (toolChoiceMode === \"auto\") {\n mode = \"AUTO\";\n } else if (toolChoiceMode === \"any\" || toolChoiceMode === \"required\") {\n mode = \"ANY\";\n } else if (toolChoiceMode === \"none\") {\n mode = \"NONE\";\n } else if (typeof toolChoiceMode === \"string\") {\n mode = \"ANY\";\n toolChoiceFunction = toolChoiceMode;\n }\n\n if (toolChoiceFunction) {\n if (Array.isArray(toolChoiceFunction)) {\n allowedFunctionNames = toolChoiceFunction;\n } else {\n allowedFunctionNames = [toolChoiceFunction];\n }\n }\n\n const functionCallingConfig: Gemini.Tools.FunctionCallingConfig = {};\n if (allowedFunctionNames?.length) {\n functionCallingConfig.allowedFunctionNames = allowedFunctionNames;\n }\n if (mode) {\n functionCallingConfig.mode = mode;\n }\n return {\n functionCallingConfig,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tools.js","names":[],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { isLangChainTool } from \"@langchain/core/utils/function_calling\";\nimport { isOpenAITool } from \"@langchain/core/language_models/base\";\nimport {\n InteropZodType,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport {\n type JsonSchema7Type,\n toJsonSchema,\n} from \"@langchain/core/utils/json_schema\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type {\n BindToolsInput,\n ToolChoice,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { InvalidInputError, InvalidToolError } from \"../utils/errors.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\n\n/**\n * Adjusts a JSON Schema object type to be compatible with Gemini's function schema format.\n *\n * Gemini's function schema format has specific requirements:\n * - Type must be a string, not an array\n * - Nullable types should use the `nullable` property instead of including \"null\" in a type array\n * - Union types are not supported\n *\n * @param obj - The JSON Schema object to adjust\n * @returns The adjusted object with type converted to Gemini-compatible format\n * @throws {Error} If the type is a union type or null-only type, which Gemini cannot handle\n *\n * @internal\n */\nfunction adjustObjectType(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n): Record<string, any> {\n if (!Array.isArray(obj.type)) {\n return obj;\n }\n\n const len = obj.type.length;\n const nullIndex = obj.type.indexOf(\"null\");\n if (len === 2 && nullIndex >= 0) {\n // There are only two values set for the type, and one of them is \"null\".\n // Set the type to the other one and set nullable to true.\n const typeIndex = nullIndex === 0 ? 1 : 0;\n obj.type = obj.type[typeIndex];\n obj.nullable = true;\n } else if (len === 1 && nullIndex === 0) {\n // This is nullable only without a type, which doesn't make sense for Gemini\n throw new InvalidInputError(\n \"Gemini does not support null-only types in function schemas. Provide a non-null type alongside nullable.\"\n );\n } else if (len === 1) {\n // Although an array, it has only one value.\n // So set it to the string to match what Gemini expects.\n obj.type = obj?.type[0];\n } else if (len === 0) {\n // Empty type array is invalid\n throw new InvalidInputError(\n \"Gemini does not support empty type arrays in function schemas. Provide at least one type.\"\n );\n } else {\n // Anything else could be a union type, so reject it.\n throw new InvalidInputError(\n \"Gemini does not support union types in function schemas. Use a single type instead.\"\n );\n }\n return obj;\n}\n\n/**\n * Recursively removes unsupported properties from a JSON Schema object to make it\n * compatible with Gemini's function schema format.\n *\n * Gemini's function schema format does not support:\n * - `additionalProperties` property\n * - Array types (must be converted to string types with nullable flag)\n * - Union types\n *\n * @param obj - The JSON Schema object to clean\n * @returns A cleaned GeminiFunctionSchema object\n *\n * @internal\n */\nfunction removeAdditionalProperties(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n obj: Record<string, any>\n): Gemini.Tools.Schema & { $schema?: string } {\n if (typeof obj === \"object\" && obj !== null) {\n const newObj = { ...obj };\n\n if (\"additionalProperties\" in newObj) {\n delete newObj.additionalProperties;\n }\n\n adjustObjectType(newObj);\n\n for (const key in newObj) {\n if (key in newObj) {\n if (Array.isArray(newObj[key])) {\n newObj[key] = newObj[key].map(removeAdditionalProperties);\n } else if (typeof newObj[key] === \"object\" && newObj[key] !== null) {\n newObj[key] = removeAdditionalProperties(newObj[key]);\n }\n }\n }\n\n return newObj as Gemini.Tools.Schema;\n }\n\n return obj as Gemini.Tools.Schema;\n}\n\n/**\n * Converts a Zod schema or JSON Schema to Gemini function parameters format.\n *\n * This function handles the conversion of schema definitions from various formats\n * (Zod schemas, JSON Schema 7) to Gemini's function schema format. It removes\n * unsupported properties and adjusts type definitions to be compatible with Gemini.\n *\n * @param schema - The schema to convert (Zod schema or JSON Schema 7)\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nexport function schemaToGeminiParameters<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>\n>(\n schema:\n | SerializableSchema<RunOutput>\n | InteropZodType<RunOutput>\n | JsonSchema7Type\n): Gemini.Tools.Schema {\n // Gemini doesn't accept either the $schema or additionalProperties\n // attributes, so we need to explicitly remove them.\n // Zod sometimes also makes an array of type (because of .nullish()),\n // which needs cleaning up.\n const jsonSchema = removeAdditionalProperties(\n isInteropZodSchema(schema) || isSerializableSchema(schema)\n ? toJsonSchema(schema)\n : schema\n );\n const { $schema, ...rest } = jsonSchema;\n return rest;\n}\n\n/**\n * Converts a JSON Schema object to Gemini function parameters format.\n *\n * @param schema - The JSON Schema object to convert\n * @returns A schema object compatible with Gemini's API\n *\n * @internal\n */\nfunction jsonSchemaToGeminiParameters(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schema: Record<string, any>\n): Gemini.Tools.Schema {\n const jsonSchema = removeAdditionalProperties(schema);\n const { $schema, ...rest } = jsonSchema;\n\n return rest;\n}\n\n/**\n * Type guard to check if a tool is already in Gemini's native format.\n *\n * Gemini tools can be:\n * - Function declarations (`functionDeclarations`)\n * - Code execution tools (`codeExecution`)\n * - Google Search retrieval tools (`googleSearchRetrieval` or `googleSearch`)\n *\n * The full list of tool types are those defined by the `Gemini.Tool` type.\n * Updating that list requires a change to the `geminiToolAttributes` below.\n *\n * @param tool - The tool to check\n * @returns `true` if the tool is already in Gemini format, `false` otherwise\n *\n * @internal\n */\nfunction isGeminiTool(tool: BindToolsInput): tool is Gemini.Tool {\n if (typeof tool !== \"object\" || tool === null) {\n return false;\n }\n\n const geminiToolAttributes = [\n \"functionDeclarations\",\n \"codeExecution\",\n \"googleSearch\",\n \"urlContext\",\n \"googleMaps\",\n \"fileSearch\",\n \"computerUse\",\n \"mcpServers\",\n ];\n\n return geminiToolAttributes.some((attr) => attr in tool);\n}\n\n/**\n * Converts an array of LangChain tools to Gemini function declarations.\n *\n * This converter transforms various tool formats into Gemini's function declaration\n * format. It supports:\n * - LangChain structured tools (with Zod schemas)\n * - OpenAI format tools (with JSON Schema)\n * - Gemini-native function declarations (passed through)\n * - Raw function declaration objects\n *\n * @remarks\n * The converter handles each tool type differently:\n * - **LangChain tools**: Extracts name, description, and schema, converting the schema\n * to Gemini-compatible format\n * - **OpenAI tools**: Extracts function name, description, and parameters from the\n * OpenAI tool format\n * - **Gemini tools**: If the tool contains `functionDeclarations`, extracts and returns\n * them directly. Non-function Gemini tools (like `codeExecution`) are skipped.\n * - **Raw objects**: Assumes the object is a function declaration and validates it has\n * a `name` property\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather for a location\",\n * schema: z.object({\n * location: z.string().describe(\"The city name\"),\n * unit: z.enum([\"celsius\", \"fahrenheit\"]).optional()\n * }),\n * func: async (args) => { /* ... *\\/ }\n * });\n *\n * const declarations = convertToolsToGeminiFunctionDeclarations([weatherTool]);\n * // Returns: [{\n * // name: \"get_weather\",\n * // description: \"Get the weather for a location\",\n * // parameters: {\n * // type: \"object\",\n * // properties: {\n * // location: { type: \"string\", description: \"The city name\" },\n * // unit: { type: \"string\", enum: [\"celsius\", \"fahrenheit\"], nullable: true }\n * // },\n * // required: [\"location\"]\n * // }\n * // }]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Gemini function declarations ready for API submission\n * @throws {Error} If a tool cannot be converted (invalid format)\n */\nexport const convertToolsToGeminiFunctionDeclarations: Converter<\n BindToolsInput[],\n Gemini.Tools.FunctionDeclaration[]\n> = (tools) => {\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n for (const tool of tools) {\n // Skip if already in Gemini format (non-function tools)\n if (isGeminiTool(tool) && !(\"functionDeclarations\" in tool)) {\n continue;\n }\n\n // Handle Gemini format with functionDeclarations\n if (\n isGeminiTool(tool) &&\n \"functionDeclarations\" in tool &&\n Array.isArray(tool.functionDeclarations)\n ) {\n functionDeclarations.push(...tool.functionDeclarations);\n continue;\n }\n\n // Handle LangChain structured tools\n if (isLangChainTool(tool)) {\n const jsonSchema = schemaToGeminiParameters(tool.schema);\n functionDeclarations.push({\n name: tool.name,\n description: tool.description ?? `A function available to call.`,\n parameters: jsonSchema,\n });\n continue;\n }\n\n // Handle OpenAI format tools\n if (isOpenAITool(tool)) {\n functionDeclarations.push({\n name: tool.function.name,\n description:\n tool.function.description ?? `A function available to call.`,\n parameters: jsonSchemaToGeminiParameters(tool.function.parameters),\n });\n continue;\n }\n\n // Handle raw function declaration objects\n if (\n typeof tool === \"object\" &&\n tool !== null &&\n \"name\" in tool &&\n typeof tool.name === \"string\"\n ) {\n const funcDecl = tool as Partial<Gemini.Tools.FunctionDeclaration>;\n functionDeclarations.push({\n name: funcDecl.name!,\n description: funcDecl.description ?? `A function available to call.`,\n parameters: funcDecl.parameters,\n });\n continue;\n }\n\n throw new InvalidToolError(tool);\n }\n\n return functionDeclarations;\n};\n\n/**\n * Converts an array of tools to Gemini's Tools array format.\n *\n * This converter groups function declarations into a single tool object while\n * preserving Gemini-native tools (like code execution and Google Search retrieval)\n * as separate tool objects. This matches Gemini's API structure where:\n * - Function declarations are grouped together in a single tool\n * - Non-function tools (codeExecution, googleSearchRetrieval) are separate tools\n *\n * @remarks\n * The conversion process:\n * 1. Separates Gemini-native tools from function declarations\n * 2. Converts LangChain/OpenAI tools to function declarations\n * 3. Groups all function declarations into a single Tool object\n * 4. Returns an array with the grouped function declarations tool plus any\n * non-function Gemini tools\n *\n * @example\n * ```typescript\n * import { StructuredTool } from \"@langchain/core/tools\";\n * import { z } from \"zod\";\n *\n * const weatherTool = new StructuredTool({\n * name: \"get_weather\",\n * description: \"Get the weather\",\n * schema: z.object({ location: z.string() }),\n * func: async () => {}\n * });\n *\n * const geminiCodeTool: Gemini.Tool = {\n * codeExecution: { enabled: true }\n * };\n *\n * const tools = convertToolsToGeminiTools([weatherTool, geminiCodeTool]);\n * // Returns: [\n * // { functionDeclarations: [{ name: \"get_weather\", ... }] },\n * // { codeExecution: { enabled: true } }\n * // ]\n * ```\n *\n * @param tools - Array of tools in various formats (LangChain, OpenAI, or Gemini)\n * @returns Array of Tool objects ready for API submission\n */\nexport const convertToolsToGeminiTools: Converter<\n BindToolsInput[],\n Gemini.Tool[]\n> = (tools) => {\n if (tools.length === 0) {\n return [];\n }\n\n const geminiTools: Gemini.Tool[] = [];\n const functionDeclarations: Gemini.Tools.FunctionDeclaration[] = [];\n\n // Separate Gemini-native tools from function declarations\n for (const tool of tools) {\n if (isGeminiTool(tool)) {\n if (\"functionDeclarations\" in tool) {\n // Already a Gemini tool with function declarations\n geminiTools.push(tool);\n } else {\n // Non-function Gemini tool (codeExecution, googleSearch, etc.)\n geminiTools.push(tool);\n }\n } else if (\"googleSearchRetrieval\" in tool) {\n // Convert the obsolete search into the modern one\n const searchTool: Gemini.Tool = {\n googleSearch: {},\n };\n geminiTools.push(searchTool);\n } else {\n // Convert to function declaration\n const funcDecls = convertToolsToGeminiFunctionDeclarations([tool]);\n functionDeclarations.push(...funcDecls);\n }\n }\n\n // Add function declarations as a single tool if we have any\n if (functionDeclarations.length > 0) {\n geminiTools.push({\n functionDeclarations,\n });\n }\n\n return geminiTools;\n};\n\n/**\n * Converts tool choice to Gemini's function calling configuration.\n *\n * This converter transforms LangChain's tool choice format into Gemini's\n * `toolConfig.functionCallingConfig` format. The configuration is only\n * created when tools are present (`hasTools` is true).\n *\n * @remarks\n * The conversion logic:\n * - If `hasTools` is `false`, returns `undefined` (no config needed)\n * - If `toolChoice` is provided, converts it to a Gemini mode:\n * - `\"auto\"` → `\"AUTO\"` - Model decides whether to call functions\n * - `\"any\"` or `\"required\"` → `\"ANY\"` - Model must call at least one function\n * - `\"none\"` → `\"NONE\"` - Model cannot call functions\n * - Function name string → `\"ANY\"` - Forces function use\n * - Object with `mode` → Maps the mode string to Gemini format\n * - Object with `function` → `\"ANY\"` - Forces function use\n * - If `toolChoice` is not provided but tools exist, defaults to `\"AUTO\"`\n *\n * Gemini's function calling modes:\n * - **AUTO**: The model can choose whether to call functions based on the conversation\n * - **ANY**: The model must call at least one function before responding\n * - **NONE**: The model cannot call any functions\n *\n * When a specific function is requested (via function name string or `{ function: { name } }`),\n * the converter uses `\"ANY\"` mode to force function usage, as Gemini doesn't support\n * specifying individual functions in the mode.\n *\n * @example\n * ```typescript\n * // With explicit tool choice\n * convertToolChoiceToGeminiConfig(\"auto\", true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // Required mode - must call a function\n * convertToolChoiceToGeminiConfig(\"any\", true);\n * // Returns: { functionCallingConfig: { mode: \"ANY\" } }\n *\n * // Without tool choice (defaults to AUTO when tools exist)\n * convertToolChoiceToGeminiConfig(undefined, true);\n * // Returns: { functionCallingConfig: { mode: \"AUTO\" } }\n *\n * // No tools - returns undefined\n * convertToolChoiceToGeminiConfig(\"auto\", false);\n * // Returns: undefined\n * ```\n *\n * @param toolChoice - The tool choice option from LangChain (string, object, or undefined)\n * @param hasTools - Whether tools are present. If false, returns undefined\n * @returns The Gemini tool configuration object, or undefined if no tools are present\n */\nexport function convertToolChoiceToGeminiConfig(\n toolChoice: ToolChoice | undefined,\n hasTools: boolean\n): Gemini.Tools.ToolConfig | undefined {\n if (!hasTools || toolChoice === undefined) {\n return undefined;\n }\n\n let mode: Gemini.Tools.FunctionCallingConfigMode | undefined;\n let allowedFunctionNames: string[] | undefined;\n\n let toolChoiceMode: string | undefined;\n let toolChoiceFunction: string | string[] | undefined;\n if (typeof toolChoice === \"object\") {\n toolChoiceMode = toolChoice.mode;\n toolChoiceFunction = toolChoice.function?.name;\n } else {\n toolChoiceMode = toolChoice;\n }\n\n if (toolChoiceMode === \"auto\") {\n mode = \"AUTO\";\n } else if (toolChoiceMode === \"any\" || toolChoiceMode === \"required\") {\n mode = \"ANY\";\n } else if (toolChoiceMode === \"none\") {\n mode = \"NONE\";\n } else if (typeof toolChoiceMode === \"string\") {\n mode = \"ANY\";\n toolChoiceFunction = toolChoiceMode;\n }\n\n if (toolChoiceFunction) {\n if (Array.isArray(toolChoiceFunction)) {\n allowedFunctionNames = toolChoiceFunction;\n } else {\n allowedFunctionNames = [toolChoiceFunction];\n }\n }\n\n const functionCallingConfig: Gemini.Tools.FunctionCallingConfig = {};\n if (allowedFunctionNames?.length) {\n functionCallingConfig.allowedFunctionNames = allowedFunctionNames;\n }\n if (mode) {\n functionCallingConfig.mode = mode;\n }\n return {\n functionCallingConfig,\n };\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value:
|
|
2
|
-
const require_index = require(
|
|
3
|
-
const require_handler = require(
|
|
4
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_index = require("./chat_models/index.cjs");
|
|
3
|
+
const require_handler = require("./utils/handler.cjs");
|
|
5
4
|
exports.ChatGoogle = require_index.ChatGoogle;
|
|
6
5
|
exports.GoogleRequestCallbackHandler = require_handler.GoogleRequestCallbackHandler;
|
|
7
6
|
exports.GoogleRequestLogger = require_handler.GoogleRequestLogger;
|
|
8
|
-
exports.GoogleRequestRecorder = require_handler.GoogleRequestRecorder;
|
|
7
|
+
exports.GoogleRequestRecorder = require_handler.GoogleRequestRecorder;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import { ChatGoogle } from "./chat_models/index.js";
|
|
2
2
|
import { GoogleRequestCallbackHandler, GoogleRequestLogger, GoogleRequestRecorder } from "./utils/handler.js";
|
|
3
|
-
|
|
4
|
-
export { ChatGoogle, GoogleRequestCallbackHandler, GoogleRequestLogger, GoogleRequestRecorder };
|
|
3
|
+
export { ChatGoogle, GoogleRequestCallbackHandler, GoogleRequestLogger, GoogleRequestRecorder };
|
package/dist/node.cjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value:
|
|
2
|
-
const require_handler = require(
|
|
3
|
-
const require_node = require(
|
|
4
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_handler = require("./utils/handler.cjs");
|
|
3
|
+
const require_node = require("./chat_models/node.cjs");
|
|
5
4
|
exports.ChatGoogle = require_node.ChatGoogleNode;
|
|
6
5
|
exports.GoogleRequestCallbackHandler = require_handler.GoogleRequestCallbackHandler;
|
|
7
6
|
exports.GoogleRequestLogger = require_handler.GoogleRequestLogger;
|
|
8
|
-
exports.GoogleRequestRecorder = require_handler.GoogleRequestRecorder;
|
|
7
|
+
exports.GoogleRequestRecorder = require_handler.GoogleRequestRecorder;
|
package/dist/node.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import { GoogleRequestCallbackHandler, GoogleRequestLogger, GoogleRequestRecorder } from "./utils/handler.js";
|
|
2
2
|
import { ChatGoogleNode } from "./chat_models/node.js";
|
|
3
|
-
|
|
4
|
-
export { ChatGoogleNode as ChatGoogle, GoogleRequestCallbackHandler, GoogleRequestLogger, GoogleRequestRecorder };
|
|
3
|
+
export { ChatGoogleNode as ChatGoogle, GoogleRequestCallbackHandler, GoogleRequestLogger, GoogleRequestRecorder };
|
package/dist/utils/errors.cjs
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
const require_misc = require(
|
|
1
|
+
const require_misc = require("./misc.cjs");
|
|
2
2
|
let _langchain_core_errors = require("@langchain/core/errors");
|
|
3
|
-
|
|
4
3
|
//#region src/utils/errors.ts
|
|
5
4
|
const ns = _langchain_core_errors.ns.sub("google");
|
|
5
|
+
async function readErrorResponseBody(response) {
|
|
6
|
+
return require_misc.iife(async () => {
|
|
7
|
+
try {
|
|
8
|
+
if ((response.headers.get("content-type") ?? "").includes("application/json")) return await response.json();
|
|
9
|
+
const text = await response.text();
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(text);
|
|
12
|
+
} catch {
|
|
13
|
+
return text;
|
|
14
|
+
}
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
6
20
|
/**
|
|
7
21
|
* Base error class for all Google provider errors.
|
|
8
22
|
*
|
|
@@ -205,14 +219,7 @@ var AuthError = class AuthError extends ns.brand(GoogleError, "auth") {
|
|
|
205
219
|
* ```
|
|
206
220
|
*/
|
|
207
221
|
static async fromResponse(response) {
|
|
208
|
-
const errorBody = await
|
|
209
|
-
try {
|
|
210
|
-
if ((response.headers.get("content-type") ?? "").includes("application/json")) return await response.json();
|
|
211
|
-
return await response.text();
|
|
212
|
-
} catch {
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
});
|
|
222
|
+
const errorBody = await readErrorResponseBody(response);
|
|
216
223
|
const message = errorBody?.error_description ?? `Authentication failed with status code ${response.status}`;
|
|
217
224
|
const headers = require_misc.iife(() => {
|
|
218
225
|
const object = {};
|
|
@@ -351,14 +358,7 @@ var RequestError = class RequestError extends ns.brand(GoogleError, "request") {
|
|
|
351
358
|
* ```
|
|
352
359
|
*/
|
|
353
360
|
static async fromResponse(response) {
|
|
354
|
-
const errorBody = await
|
|
355
|
-
try {
|
|
356
|
-
if ((response.headers.get("content-type") ?? "").includes("application/json")) return await response.json();
|
|
357
|
-
return await response.text();
|
|
358
|
-
} catch {
|
|
359
|
-
return null;
|
|
360
|
-
}
|
|
361
|
-
});
|
|
361
|
+
const errorBody = await readErrorResponseBody(response);
|
|
362
362
|
const message = errorBody?.error?.message ?? errorBody?.message ?? errorBody?.error ?? `Request failed with status code ${response.status}`;
|
|
363
363
|
const headers = require_misc.iife(() => {
|
|
364
364
|
const object = {};
|
|
@@ -533,7 +533,6 @@ var MalformedOutputError = class extends ns.brand(GoogleError, "malformed-output
|
|
|
533
533
|
var InvalidInputError = class extends ns.brand(GoogleError, "invalid-input") {
|
|
534
534
|
name = "InvalidInputError";
|
|
535
535
|
};
|
|
536
|
-
|
|
537
536
|
//#endregion
|
|
538
537
|
exports.AuthError = AuthError;
|
|
539
538
|
exports.ConfigurationError = ConfigurationError;
|
|
@@ -544,4 +543,5 @@ exports.NoCandidatesError = NoCandidatesError;
|
|
|
544
543
|
exports.PromptBlockedError = PromptBlockedError;
|
|
545
544
|
exports.RequestError = RequestError;
|
|
546
545
|
exports.ToolCallNotFoundError = ToolCallNotFoundError;
|
|
546
|
+
|
|
547
547
|
//# sourceMappingURL=errors.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","names":["baseNs","LangChainError","iife"],"sources":["../../src/utils/errors.ts"],"sourcesContent":["import { ns as baseNs, LangChainError } from \"@langchain/core/errors\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { iife } from \"./misc.js\";\n\n// Internal namespace for all Google provider errors\nconst ns = baseNs.sub(\"google\");\n\n/**\n * Base error class for all Google provider errors.\n *\n * All Google-specific error classes extend this class. Use\n * `GoogleError.isInstance(obj)` to check if an object is any\n * Google provider error.\n *\n * @example\n * ```typescript\n * try {\n * await model.invoke(\"hello\");\n * } catch (error) {\n * if (GoogleError.isInstance(error)) {\n * console.log(\"Got a Google error:\", error.message);\n * }\n * }\n * ```\n */\nexport class GoogleError extends ns.brand(LangChainError) {\n readonly name: string = \"GoogleError\";\n}\n\n/**\n * Error thrown when there is a configuration issue with the Google provider.\n *\n * This error is thrown when the Google provider encounters invalid configuration\n * parameters or settings that prevent it from functioning correctly.\n *\n * @example\n * ```typescript\n * try {\n * const model = new ChatGoogle({ invalidOption: true });\n * } catch (error) {\n * if (ConfigurationError.isInstance(error)) {\n * console.log(`Configuration error: ${error.message}`);\n * }\n * }\n * ```\n */\nexport class ConfigurationError extends ns.brand(GoogleError, \"configuration\") {\n readonly name = \"ConfigurationError\";\n}\n\n/**\n * Parameters for constructing a PromptBlockedError\n */\ntype PromptBlockedErrorParams = {\n /**\n * The reason why the prompt was blocked (e.g., \"SAFETY\", \"OTHER\", \"UNKNOWN\")\n */\n blockReason: string;\n /**\n * Optional array of safety ratings that contributed to the block decision\n */\n safetyRatings?: Gemini.SafetyRating[];\n /**\n * Optional custom error message. If not provided, a default message will be generated\n */\n message?: string;\n};\n\n/**\n * Error thrown when a prompt is blocked by Google's safety filters.\n *\n * This error is thrown when the Gemini API blocks a prompt due to safety concerns.\n * It contains information about why the prompt was blocked and the safety ratings\n * that triggered the block.\n *\n * @example\n * ```typescript\n * try {\n * await model.invoke(\"some potentially unsafe prompt\");\n * } catch (error) {\n * if (PromptBlockedError.isInstance(error)) {\n * console.log(`Blocked due to: ${error.blockReason}`);\n * console.log(`Safety ratings:`, error.safetyRatings);\n * }\n * }\n * ```\n */\nexport class PromptBlockedError extends ns.brand(\n GoogleError,\n \"prompt-blocked\"\n) {\n readonly name = \"PromptBlockedError\";\n\n /**\n * The reason why the prompt was blocked.\n *\n * Common values include:\n * - \"SAFETY\" - Blocked due to safety concerns\n * - \"OTHER\" - Blocked for other reasons\n * - \"UNKNOWN\" - Reason is unknown\n *\n * @readonly\n */\n readonly blockReason: string;\n\n /**\n * Optional array of safety ratings that contributed to the block decision.\n *\n * Each rating contains information about different safety categories\n * (e.g., hate speech, sexually explicit content, dangerous content)\n * and their probability levels.\n *\n * @readonly\n */\n readonly safetyRatings?: Gemini.SafetyRating[];\n\n constructor(params: PromptBlockedErrorParams) {\n const message =\n params.message ??\n `Prompt was blocked: ${params.blockReason}.${\n params.safetyRatings && params.safetyRatings.length > 0\n ? ` Safety ratings: ${JSON.stringify(params.safetyRatings)}`\n : \"\"\n }`;\n\n super(message);\n this.blockReason = params.blockReason;\n this.safetyRatings = params.safetyRatings;\n }\n\n /**\n * Creates a PromptBlockedError from a Gemini API prompt feedback object.\n *\n * This is a convenience factory method for creating errors from the API response\n * when a prompt is blocked. It extracts the relevant information from the\n * promptFeedback object returned by the Gemini API.\n *\n * @param promptFeedback - The prompt feedback object from the Gemini API response\n * @returns A new PromptBlockedError instance with information from the feedback\n *\n * @example\n * ```typescript\n * const response = await geminiApi.generateContent(request);\n * if (response.promptFeedback?.blockReason) {\n * throw PromptBlockedError.fromPromptFeedback(response.promptFeedback);\n * }\n * ```\n */\n static fromPromptFeedback(\n promptFeedback: Gemini.PromptFeedback\n ): PromptBlockedError {\n return new PromptBlockedError({\n blockReason: promptFeedback.blockReason ?? \"UNKNOWN\",\n safetyRatings: promptFeedback.safetyRatings,\n });\n }\n}\n\n/**\n * Parameters for constructing an AuthError\n */\ntype AuthErrorParams = {\n /**\n * The error message describing what went wrong\n */\n message: string;\n\n /**\n * The HTTP status code of the failed response.\n * Common values include 400 (Bad Request), 401 (Unauthorized),\n * 403 (Forbidden), 404 (Not Found), 429 (Too Many Requests),\n * 500 (Internal Server Error), etc.\n */\n statusCode?: number;\n\n /**\n * The HTTP status text of the failed response.\n * This is the human-readable status message that accompanies the status code\n * (e.g., \"Bad Request\", \"Internal Server Error\").\n */\n statusText?: string;\n\n /**\n * The HTTP response headers from the failed request.\n * Stored as a key-value record for easy access to header information.\n */\n headers?: Record<string, string>;\n\n /**\n * The response body data from the failed request.\n * This may be a parsed JSON object, a text string, or null if the body\n * could not be read. Often contains additional error details from the API.\n */\n data?: unknown;\n};\n\n/**\n * Error class for authentication failures when communicating with Google APIs.\n *\n * This error is thrown when authentication with a Google API fails, typically\n * during the OAuth token exchange process or when using service account credentials.\n * It captures detailed information about the failed authentication attempt including\n * the status code, headers, and response body to aid in debugging and error handling.\n *\n * Common authentication failure scenarios include:\n * - Invalid or expired credentials\n * - Incorrect service account configuration\n * - Missing or invalid API keys\n * - Insufficient permissions or scopes\n * - Token exchange failures\n *\n * @example\n * ```typescript\n * try {\n * const token = await getAccessToken(credentials);\n * } catch (error) {\n * if (AuthError.isInstance(error)) {\n * console.error(`Auth failed with status ${error.statusCode}: ${error.message}`);\n * console.error('Response data:', error.data);\n * }\n * }\n * ```\n */\nexport class AuthError extends ns.brand(GoogleError, \"auth\") {\n readonly name = \"AuthError\" as const;\n\n /**\n * The HTTP status code of the failed response.\n * Common values include 400 (Bad Request), 401 (Unauthorized),\n * 403 (Forbidden), 404 (Not Found), 429 (Too Many Requests),\n * 500 (Internal Server Error), etc.\n */\n readonly statusCode?: number;\n\n /**\n * The HTTP status text of the failed response.\n * This is the human-readable status message that accompanies the status code\n * (e.g., \"Bad Request\", \"Internal Server Error\").\n */\n readonly statusText?: string;\n\n /**\n * The HTTP response headers from the failed request.\n * Stored as a key-value record for easy access to header information.\n */\n readonly headers?: Record<string, string>;\n\n /**\n * The response body data from the failed request.\n * This may be a parsed JSON object, a text string, or null if the body\n * could not be read. Often contains additional error details from the API.\n */\n readonly data?: unknown;\n\n constructor(params: AuthErrorParams) {\n super(params.message);\n\n this.statusCode = params.statusCode;\n this.statusText = params.statusText;\n this.headers = params.headers;\n this.data = params.data;\n }\n\n /**\n * Creates an AuthError from a failed HTTP response.\n *\n * This is a convenience factory method for creating authentication errors from\n * HTTP responses. It automatically extracts the error message, status code,\n * headers, and response body from the Response object.\n *\n * The method attempts to parse the response body as JSON to extract an\n * `error_description` field if available. If JSON parsing fails or the\n * content type is not JSON, it falls back to reading the response as text.\n * If both fail, the error body will be null.\n *\n * @param response - The failed HTTP Response object from the authentication request\n * @returns A Promise that resolves to a new AuthError instance with information from the response\n *\n * @example\n * ```typescript\n * const response = await fetch(tokenUrl, {\n * method: 'POST',\n * body: authRequestBody\n * });\n *\n * if (!response.ok) {\n * throw await AuthError.fromResponse(response);\n * }\n * ```\n */\n static async fromResponse(response: Response): Promise<AuthError> {\n const errorBody = await iife(async () => {\n try {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n return await response.json();\n }\n return await response.text();\n } catch {\n return null;\n }\n });\n\n const message =\n errorBody?.error_description ??\n `Authentication failed with status code ${response.status}`;\n\n const headers = iife(() => {\n const object: Record<string, string> = {};\n response.headers.forEach((value, name) => {\n object[name] = value;\n });\n return object;\n });\n\n return new AuthError({\n message,\n statusCode: response.status,\n statusText: response.statusText,\n headers,\n data: errorBody,\n });\n }\n}\n\nconst RETRYABLE_STATUS_CODES = [\n 408, // Request Timeout\n 429, // Too Many Requests\n 500, // Internal Server Error\n 502, // Bad Gateway\n 503, // Service Unavailable\n 504, // Gateway Timeout\n];\n\n/**\n * Parameters for constructing a RequestError\n */\ntype RequestErrorParams = {\n /**\n * The error message describing what went wrong\n */\n message: string;\n\n /**\n * The URL of the failed request.\n * This is the full URL that was being accessed when the error occurred.\n */\n url: string;\n\n /**\n * The HTTP status code of the failed response.\n * Common values include 400 (Bad Request), 401 (Unauthorized),\n * 403 (Forbidden), 404 (Not Found), 429 (Too Many Requests),\n * 500 (Internal Server Error), etc.\n */\n statusCode?: number;\n\n /**\n * The HTTP status text of the failed response.\n * This is the human-readable status message that accompanies the status code\n * (e.g., \"Bad Request\", \"Internal Server Error\").\n */\n statusText?: string;\n\n /**\n * The HTTP response headers from the failed request.\n * Stored as a key-value record for easy access to header information.\n */\n headers?: Record<string, string>;\n\n /**\n * The response body data from the failed request.\n * This may be a parsed JSON object, a text string, or null if the body\n * could not be read. Often contains additional error details from the API.\n */\n data?: unknown;\n};\n\n/**\n * Error class for HTTP request failures when communicating with Google APIs.\n *\n * This error is thrown when an HTTP request to a Google API endpoint fails.\n * It captures detailed information about the failed request including the URL,\n * status code, headers, and response body to aid in debugging and error handling.\n *\n * The error includes a `isRetryable()` method to determine if the request should\n * be retried based on the HTTP status code (e.g., 429 Too Many Requests, 503 Service Unavailable).\n *\n * @example\n * ```typescript\n * try {\n * const response = await apiClient.fetch(request);\n * if (!response.ok) {\n * throw await RequestError.fromResponse(response);\n * }\n * } catch (error) {\n * if (RequestError.isInstance(error) && error.isRetryable()) {\n * // Retry the request\n * }\n * }\n * ```\n */\nexport class RequestError extends ns.brand(GoogleError, \"request\") {\n readonly name = \"RequestError\" as const;\n\n /**\n * The URL of the failed request.\n * This is the full URL that was being accessed when the error occurred.\n */\n readonly url: string;\n\n /**\n * The HTTP status code of the failed response.\n * Common values include 400 (Bad Request), 401 (Unauthorized),\n * 403 (Forbidden), 404 (Not Found), 429 (Too Many Requests),\n * 500 (Internal Server Error), etc.\n */\n readonly statusCode?: number;\n\n /**\n * The HTTP status text of the failed response.\n * This is the human-readable status message that accompanies the status code\n * (e.g., \"Bad Request\", \"Internal Server Error\").\n */\n readonly statusText?: string;\n\n /**\n * The HTTP response headers from the failed request.\n * Stored as a key-value record for easy access to header information.\n */\n readonly headers?: Record<string, string>;\n\n /**\n * The response body data from the failed request.\n * This may be a parsed JSON object, a text string, or null if the body\n * could not be read. Often contains additional error details from the API.\n */\n readonly data?: unknown;\n\n constructor(params: RequestErrorParams) {\n super(params.message);\n\n this.url = params.url;\n this.statusCode = params.statusCode;\n this.statusText = params.statusText;\n this.headers = params.headers;\n this.data = params.data;\n }\n\n /**\n * Determines if this error represents a retryable request failure.\n *\n * A request is considered retryable if it failed with a status code that\n * indicates a temporary issue that might succeed on retry. This includes:\n * - 408 (Request Timeout)\n * - 429 (Too Many Requests / Rate Limiting)\n * - 500 (Internal Server Error)\n * - 502 (Bad Gateway)\n * - 503 (Service Unavailable)\n * - 504 (Gateway Timeout)\n *\n * @returns `true` if the request should be retried, `false` otherwise\n *\n * @example\n * ```typescript\n * if (RequestError.isInstance(error) && error.isRetryable()) {\n * await sleep(1000);\n * return retry(request);\n * }\n * ```\n */\n isRetryable(): boolean {\n if (!this.statusCode) return false;\n return RETRYABLE_STATUS_CODES.includes(this.statusCode);\n }\n\n /**\n * Creates a RequestError from a failed HTTP Response object.\n *\n * This is a factory method that extracts all relevant information from a\n * Response object and constructs a RequestError with appropriate details.\n * It attempts to parse the response body as JSON first, falling back to\n * text if that fails, and finally to null if the body cannot be read.\n *\n * The error message is extracted from the response body if available\n * (looking for `message` or `error` fields), otherwise a generic message\n * is constructed using the status code.\n *\n * @param response - The failed HTTP Response object to convert into an error\n * @returns A Promise that resolves to a new RequestError instance\n *\n * @example\n * ```typescript\n * const response = await fetch(url);\n * if (!response.ok) {\n * throw await RequestError.fromResponse(response);\n * }\n * ```\n */\n static async fromResponse(response: Response): Promise<RequestError> {\n const errorBody = await iife(async () => {\n try {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n return await response.json();\n }\n return await response.text();\n } catch {\n return null;\n }\n });\n\n const message =\n errorBody?.error?.message ??\n errorBody?.message ??\n errorBody?.error ??\n `Request failed with status code ${response.status}`;\n\n const headers = iife(() => {\n const object: Record<string, string> = {};\n response.headers.forEach((value, name) => {\n object[name] = value;\n });\n return object;\n });\n\n return new RequestError({\n message,\n url: response.url,\n statusCode: response.status,\n statusText: response.statusText,\n headers,\n data: errorBody,\n });\n }\n}\n\n/**\n * Error thrown when the Gemini API returns no candidates in the response.\n *\n * This error is thrown when the API successfully processes a request but returns\n * an empty candidates array. This can occur when:\n * - The prompt was blocked by safety filters (but no explicit block reason was provided)\n * - An internal error occurred during generation\n * - The model was unable to generate a valid response\n *\n * @example\n * ```typescript\n * try {\n * await model.invoke(\"some prompt\");\n * } catch (error) {\n * if (NoCandidatesError.isInstance(error)) {\n * console.log(\"No response candidates were generated\");\n * }\n * }\n * ```\n */\nexport class NoCandidatesError extends ns.brand(GoogleError, \"no-candidates\") {\n readonly name = \"NoCandidatesError\";\n\n constructor() {\n super(\n \"No candidates returned from API. This may indicate the prompt was blocked or an error occurred.\"\n );\n }\n}\n\n/**\n * Error thrown when an invalid tool is provided to the tool converter.\n *\n * This error is thrown when a tool cannot be converted to Gemini's function\n * declaration format. Tools must be one of:\n * - LangChain structured tools (with Zod schemas)\n * - OpenAI format tools (with JSON Schema)\n * - Gemini-native function declarations\n * - Gemini-native tools (codeExecution, googleSearchRetrieval)\n *\n * @example\n * ```typescript\n * try {\n * convertToolsToGeminiFunctionDeclarations([invalidTool]);\n * } catch (error) {\n * if (InvalidToolError.isInstance(error)) {\n * console.log(`Invalid tool: ${error.tool}`);\n * console.log(`Expected formats: LangChain tools, OpenAI tools, or Gemini function declarations`);\n * }\n * }\n * ```\n */\nexport class InvalidToolError extends ns.brand(GoogleError, \"invalid-tool\") {\n readonly name = \"InvalidToolError\";\n\n /**\n * The invalid tool that was provided.\n */\n readonly tool: unknown;\n\n constructor(tool: unknown, message?: string) {\n const defaultMessage = `Received invalid tool: ${JSON.stringify(\n tool\n )}. Tools must be LangChain tools, OpenAI tools, or Gemini function declarations.`;\n\n super(message ?? defaultMessage);\n this.tool = tool;\n }\n}\n\n/**\n * Error thrown when a ToolMessage references a tool call that cannot be found.\n *\n * This error is thrown when a ToolMessage is passed to ChatGoogle but there is no\n * corresponding AIMessage with a matching toolCallId in the conversation history.\n * This typically indicates that:\n * - The tool call ID in the ToolMessage doesn't match any previous tool calls\n * - The AIMessage containing the tool call was not included in the message history\n * - The tool call ID was incorrectly set or modified\n *\n * @example\n * ```typescript\n * try {\n * await model.invoke([\n * new ToolMessage({ content: \"result\", tool_call_id: \"nonexistent-id\" })\n * ]);\n * } catch (error) {\n * if (ToolCallNotFoundError.isInstance(error)) {\n * console.log(`Tool call not found: ${error.toolCallId}`);\n * }\n * }\n * ```\n */\nexport class ToolCallNotFoundError extends ns.brand(\n GoogleError,\n \"tool-call-not-found\"\n) {\n readonly name = \"ToolCallNotFoundError\";\n\n /**\n * The tool call ID that could not be found in the conversation history.\n */\n readonly toolCallId: string;\n\n constructor(toolCallId: string, message?: string) {\n const defaultMessage = `Tool call with ID \"${toolCallId}\" not found in conversation history. Ensure the corresponding AIMessage with this tool call ID is included in the input.`;\n\n super(message ?? defaultMessage);\n this.toolCallId = toolCallId;\n }\n}\n\n/**\n * Parameters for constructing a MalformedOutputError\n */\ntype MalformedOutputErrorParams = {\n /**\n * The error message describing what went wrong during parsing\n */\n message: string;\n /**\n * Optional cause of the parsing error (e.g., the original error that occurred)\n */\n cause?: unknown;\n};\n\n/**\n * Error thrown when output parsing fails in structured output methods.\n *\n * This error is thrown when the model's response cannot be parsed according to\n * the expected schema or format. Common causes include:\n * - Invalid message type (not an AIMessage or AIMessageChunk)\n * - Missing or empty content in the response\n * - Malformed JSON that cannot be parsed\n * - Schema validation failures\n *\n * @example\n * ```typescript\n * try {\n * const result = await model.withStructuredOutput(schema).invoke(\"extract data\");\n * } catch (error) {\n * if (MalformedOutputError.isInstance(error)) {\n * console.log(`Failed to parse output: ${error.message}`);\n * if (error.cause) {\n * console.log(`Cause:`, error.cause);\n * }\n * }\n * }\n * ```\n */\nexport class MalformedOutputError extends ns.brand(\n GoogleError,\n \"malformed-output\"\n) {\n readonly name = \"MalformedOutputError\";\n\n /**\n * Optional cause of the parsing error.\n * This may be the original error that occurred during parsing,\n * or additional context about what went wrong.\n */\n readonly cause?: unknown;\n\n constructor(params: MalformedOutputErrorParams) {\n super(params.message);\n this.cause = params.cause;\n }\n}\n\n/**\n * Error thrown when input provided to the Google provider is invalid.\n *\n * This error is thrown when the user provides input that cannot be\n * processed by Gemini, such as:\n * - Union types in tool/function schemas\n * - Null-only types in schemas\n * - Unsupported content block source types\n * - Missing required fields in content blocks\n *\n * @example\n * ```typescript\n * try {\n * const result = schemaToGeminiParameters(unionTypeSchema);\n * } catch (error) {\n * if (InvalidInputError.isInstance(error)) {\n * console.log(`Invalid input: ${error.message}`);\n * }\n * }\n * ```\n */\nexport class InvalidInputError extends ns.brand(GoogleError, \"invalid-input\") {\n readonly name = \"InvalidInputError\";\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"errors.cjs","names":["baseNs","iife","LangChainError"],"sources":["../../src/utils/errors.ts"],"sourcesContent":["import { ns as baseNs, LangChainError } from \"@langchain/core/errors\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { iife } from \"./misc.js\";\n\n// Internal namespace for all Google provider errors\nconst ns = baseNs.sub(\"google\");\n\nasync function readErrorResponseBody(response: Response): Promise<unknown> {\n return iife(async () => {\n try {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n if (contentType.includes(\"application/json\")) {\n return await response.json();\n }\n\n const text = await response.text();\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n } catch {\n return null;\n }\n });\n}\n\n/**\n * Base error class for all Google provider errors.\n *\n * All Google-specific error classes extend this class. Use\n * `GoogleError.isInstance(obj)` to check if an object is any\n * Google provider error.\n *\n * @example\n * ```typescript\n * try {\n * await model.invoke(\"hello\");\n * } catch (error) {\n * if (GoogleError.isInstance(error)) {\n * console.log(\"Got a Google error:\", error.message);\n * }\n * }\n * ```\n */\nexport class GoogleError extends ns.brand(LangChainError) {\n readonly name: string = \"GoogleError\";\n}\n\n/**\n * Error thrown when there is a configuration issue with the Google provider.\n *\n * This error is thrown when the Google provider encounters invalid configuration\n * parameters or settings that prevent it from functioning correctly.\n *\n * @example\n * ```typescript\n * try {\n * const model = new ChatGoogle({ invalidOption: true });\n * } catch (error) {\n * if (ConfigurationError.isInstance(error)) {\n * console.log(`Configuration error: ${error.message}`);\n * }\n * }\n * ```\n */\nexport class ConfigurationError extends ns.brand(GoogleError, \"configuration\") {\n readonly name = \"ConfigurationError\";\n}\n\n/**\n * Parameters for constructing a PromptBlockedError\n */\ntype PromptBlockedErrorParams = {\n /**\n * The reason why the prompt was blocked (e.g., \"SAFETY\", \"OTHER\", \"UNKNOWN\")\n */\n blockReason: string;\n /**\n * Optional array of safety ratings that contributed to the block decision\n */\n safetyRatings?: Gemini.SafetyRating[];\n /**\n * Optional custom error message. If not provided, a default message will be generated\n */\n message?: string;\n};\n\n/**\n * Error thrown when a prompt is blocked by Google's safety filters.\n *\n * This error is thrown when the Gemini API blocks a prompt due to safety concerns.\n * It contains information about why the prompt was blocked and the safety ratings\n * that triggered the block.\n *\n * @example\n * ```typescript\n * try {\n * await model.invoke(\"some potentially unsafe prompt\");\n * } catch (error) {\n * if (PromptBlockedError.isInstance(error)) {\n * console.log(`Blocked due to: ${error.blockReason}`);\n * console.log(`Safety ratings:`, error.safetyRatings);\n * }\n * }\n * ```\n */\nexport class PromptBlockedError extends ns.brand(\n GoogleError,\n \"prompt-blocked\"\n) {\n readonly name = \"PromptBlockedError\";\n\n /**\n * The reason why the prompt was blocked.\n *\n * Common values include:\n * - \"SAFETY\" - Blocked due to safety concerns\n * - \"OTHER\" - Blocked for other reasons\n * - \"UNKNOWN\" - Reason is unknown\n *\n * @readonly\n */\n readonly blockReason: string;\n\n /**\n * Optional array of safety ratings that contributed to the block decision.\n *\n * Each rating contains information about different safety categories\n * (e.g., hate speech, sexually explicit content, dangerous content)\n * and their probability levels.\n *\n * @readonly\n */\n readonly safetyRatings?: Gemini.SafetyRating[];\n\n constructor(params: PromptBlockedErrorParams) {\n const message =\n params.message ??\n `Prompt was blocked: ${params.blockReason}.${\n params.safetyRatings && params.safetyRatings.length > 0\n ? ` Safety ratings: ${JSON.stringify(params.safetyRatings)}`\n : \"\"\n }`;\n\n super(message);\n this.blockReason = params.blockReason;\n this.safetyRatings = params.safetyRatings;\n }\n\n /**\n * Creates a PromptBlockedError from a Gemini API prompt feedback object.\n *\n * This is a convenience factory method for creating errors from the API response\n * when a prompt is blocked. It extracts the relevant information from the\n * promptFeedback object returned by the Gemini API.\n *\n * @param promptFeedback - The prompt feedback object from the Gemini API response\n * @returns A new PromptBlockedError instance with information from the feedback\n *\n * @example\n * ```typescript\n * const response = await geminiApi.generateContent(request);\n * if (response.promptFeedback?.blockReason) {\n * throw PromptBlockedError.fromPromptFeedback(response.promptFeedback);\n * }\n * ```\n */\n static fromPromptFeedback(\n promptFeedback: Gemini.PromptFeedback\n ): PromptBlockedError {\n return new PromptBlockedError({\n blockReason: promptFeedback.blockReason ?? \"UNKNOWN\",\n safetyRatings: promptFeedback.safetyRatings,\n });\n }\n}\n\n/**\n * Parameters for constructing an AuthError\n */\ntype AuthErrorParams = {\n /**\n * The error message describing what went wrong\n */\n message: string;\n\n /**\n * The HTTP status code of the failed response.\n * Common values include 400 (Bad Request), 401 (Unauthorized),\n * 403 (Forbidden), 404 (Not Found), 429 (Too Many Requests),\n * 500 (Internal Server Error), etc.\n */\n statusCode?: number;\n\n /**\n * The HTTP status text of the failed response.\n * This is the human-readable status message that accompanies the status code\n * (e.g., \"Bad Request\", \"Internal Server Error\").\n */\n statusText?: string;\n\n /**\n * The HTTP response headers from the failed request.\n * Stored as a key-value record for easy access to header information.\n */\n headers?: Record<string, string>;\n\n /**\n * The response body data from the failed request.\n * This may be a parsed JSON object, a text string, or null if the body\n * could not be read. Often contains additional error details from the API.\n */\n data?: unknown;\n};\n\n/**\n * Error class for authentication failures when communicating with Google APIs.\n *\n * This error is thrown when authentication with a Google API fails, typically\n * during the OAuth token exchange process or when using service account credentials.\n * It captures detailed information about the failed authentication attempt including\n * the status code, headers, and response body to aid in debugging and error handling.\n *\n * Common authentication failure scenarios include:\n * - Invalid or expired credentials\n * - Incorrect service account configuration\n * - Missing or invalid API keys\n * - Insufficient permissions or scopes\n * - Token exchange failures\n *\n * @example\n * ```typescript\n * try {\n * const token = await getAccessToken(credentials);\n * } catch (error) {\n * if (AuthError.isInstance(error)) {\n * console.error(`Auth failed with status ${error.statusCode}: ${error.message}`);\n * console.error('Response data:', error.data);\n * }\n * }\n * ```\n */\nexport class AuthError extends ns.brand(GoogleError, \"auth\") {\n readonly name = \"AuthError\" as const;\n\n /**\n * The HTTP status code of the failed response.\n * Common values include 400 (Bad Request), 401 (Unauthorized),\n * 403 (Forbidden), 404 (Not Found), 429 (Too Many Requests),\n * 500 (Internal Server Error), etc.\n */\n readonly statusCode?: number;\n\n /**\n * The HTTP status text of the failed response.\n * This is the human-readable status message that accompanies the status code\n * (e.g., \"Bad Request\", \"Internal Server Error\").\n */\n readonly statusText?: string;\n\n /**\n * The HTTP response headers from the failed request.\n * Stored as a key-value record for easy access to header information.\n */\n readonly headers?: Record<string, string>;\n\n /**\n * The response body data from the failed request.\n * This may be a parsed JSON object, a text string, or null if the body\n * could not be read. Often contains additional error details from the API.\n */\n readonly data?: unknown;\n\n constructor(params: AuthErrorParams) {\n super(params.message);\n\n this.statusCode = params.statusCode;\n this.statusText = params.statusText;\n this.headers = params.headers;\n this.data = params.data;\n }\n\n /**\n * Creates an AuthError from a failed HTTP response.\n *\n * This is a convenience factory method for creating authentication errors from\n * HTTP responses. It automatically extracts the error message, status code,\n * headers, and response body from the Response object.\n *\n * The method attempts to parse the response body as JSON to extract an\n * `error_description` field if available. If JSON parsing fails or the\n * content type is not JSON, it falls back to reading the response as text.\n * If both fail, the error body will be null.\n *\n * @param response - The failed HTTP Response object from the authentication request\n * @returns A Promise that resolves to a new AuthError instance with information from the response\n *\n * @example\n * ```typescript\n * const response = await fetch(tokenUrl, {\n * method: 'POST',\n * body: authRequestBody\n * });\n *\n * if (!response.ok) {\n * throw await AuthError.fromResponse(response);\n * }\n * ```\n */\n static async fromResponse(response: Response): Promise<AuthError> {\n const errorBody = await readErrorResponseBody(response);\n\n const message =\n errorBody?.error_description ??\n `Authentication failed with status code ${response.status}`;\n\n const headers = iife(() => {\n const object: Record<string, string> = {};\n response.headers.forEach((value, name) => {\n object[name] = value;\n });\n return object;\n });\n\n return new AuthError({\n message,\n statusCode: response.status,\n statusText: response.statusText,\n headers,\n data: errorBody,\n });\n }\n}\n\nconst RETRYABLE_STATUS_CODES = [\n 408, // Request Timeout\n 429, // Too Many Requests\n 500, // Internal Server Error\n 502, // Bad Gateway\n 503, // Service Unavailable\n 504, // Gateway Timeout\n];\n\n/**\n * Parameters for constructing a RequestError\n */\ntype RequestErrorParams = {\n /**\n * The error message describing what went wrong\n */\n message: string;\n\n /**\n * The URL of the failed request.\n * This is the full URL that was being accessed when the error occurred.\n */\n url: string;\n\n /**\n * The HTTP status code of the failed response.\n * Common values include 400 (Bad Request), 401 (Unauthorized),\n * 403 (Forbidden), 404 (Not Found), 429 (Too Many Requests),\n * 500 (Internal Server Error), etc.\n */\n statusCode?: number;\n\n /**\n * The HTTP status text of the failed response.\n * This is the human-readable status message that accompanies the status code\n * (e.g., \"Bad Request\", \"Internal Server Error\").\n */\n statusText?: string;\n\n /**\n * The HTTP response headers from the failed request.\n * Stored as a key-value record for easy access to header information.\n */\n headers?: Record<string, string>;\n\n /**\n * The response body data from the failed request.\n * This may be a parsed JSON object, a text string, or null if the body\n * could not be read. Often contains additional error details from the API.\n */\n data?: unknown;\n};\n\n/**\n * Error class for HTTP request failures when communicating with Google APIs.\n *\n * This error is thrown when an HTTP request to a Google API endpoint fails.\n * It captures detailed information about the failed request including the URL,\n * status code, headers, and response body to aid in debugging and error handling.\n *\n * The error includes a `isRetryable()` method to determine if the request should\n * be retried based on the HTTP status code (e.g., 429 Too Many Requests, 503 Service Unavailable).\n *\n * @example\n * ```typescript\n * try {\n * const response = await apiClient.fetch(request);\n * if (!response.ok) {\n * throw await RequestError.fromResponse(response);\n * }\n * } catch (error) {\n * if (RequestError.isInstance(error) && error.isRetryable()) {\n * // Retry the request\n * }\n * }\n * ```\n */\nexport class RequestError extends ns.brand(GoogleError, \"request\") {\n readonly name = \"RequestError\" as const;\n\n /**\n * The URL of the failed request.\n * This is the full URL that was being accessed when the error occurred.\n */\n readonly url: string;\n\n /**\n * The HTTP status code of the failed response.\n * Common values include 400 (Bad Request), 401 (Unauthorized),\n * 403 (Forbidden), 404 (Not Found), 429 (Too Many Requests),\n * 500 (Internal Server Error), etc.\n */\n readonly statusCode?: number;\n\n /**\n * The HTTP status text of the failed response.\n * This is the human-readable status message that accompanies the status code\n * (e.g., \"Bad Request\", \"Internal Server Error\").\n */\n readonly statusText?: string;\n\n /**\n * The HTTP response headers from the failed request.\n * Stored as a key-value record for easy access to header information.\n */\n readonly headers?: Record<string, string>;\n\n /**\n * The response body data from the failed request.\n * This may be a parsed JSON object, a text string, or null if the body\n * could not be read. Often contains additional error details from the API.\n */\n readonly data?: unknown;\n\n constructor(params: RequestErrorParams) {\n super(params.message);\n\n this.url = params.url;\n this.statusCode = params.statusCode;\n this.statusText = params.statusText;\n this.headers = params.headers;\n this.data = params.data;\n }\n\n /**\n * Determines if this error represents a retryable request failure.\n *\n * A request is considered retryable if it failed with a status code that\n * indicates a temporary issue that might succeed on retry. This includes:\n * - 408 (Request Timeout)\n * - 429 (Too Many Requests / Rate Limiting)\n * - 500 (Internal Server Error)\n * - 502 (Bad Gateway)\n * - 503 (Service Unavailable)\n * - 504 (Gateway Timeout)\n *\n * @returns `true` if the request should be retried, `false` otherwise\n *\n * @example\n * ```typescript\n * if (RequestError.isInstance(error) && error.isRetryable()) {\n * await sleep(1000);\n * return retry(request);\n * }\n * ```\n */\n isRetryable(): boolean {\n if (!this.statusCode) return false;\n return RETRYABLE_STATUS_CODES.includes(this.statusCode);\n }\n\n /**\n * Creates a RequestError from a failed HTTP Response object.\n *\n * This is a factory method that extracts all relevant information from a\n * Response object and constructs a RequestError with appropriate details.\n * It attempts to parse the response body as JSON first, falling back to\n * text if that fails, and finally to null if the body cannot be read.\n *\n * The error message is extracted from the response body if available\n * (looking for `message` or `error` fields), otherwise a generic message\n * is constructed using the status code.\n *\n * @param response - The failed HTTP Response object to convert into an error\n * @returns A Promise that resolves to a new RequestError instance\n *\n * @example\n * ```typescript\n * const response = await fetch(url);\n * if (!response.ok) {\n * throw await RequestError.fromResponse(response);\n * }\n * ```\n */\n static async fromResponse(response: Response): Promise<RequestError> {\n const errorBody = await readErrorResponseBody(response);\n\n const message =\n errorBody?.error?.message ??\n errorBody?.message ??\n errorBody?.error ??\n `Request failed with status code ${response.status}`;\n\n const headers = iife(() => {\n const object: Record<string, string> = {};\n response.headers.forEach((value, name) => {\n object[name] = value;\n });\n return object;\n });\n\n return new RequestError({\n message,\n url: response.url,\n statusCode: response.status,\n statusText: response.statusText,\n headers,\n data: errorBody,\n });\n }\n}\n\n/**\n * Error thrown when the Gemini API returns no candidates in the response.\n *\n * This error is thrown when the API successfully processes a request but returns\n * an empty candidates array. This can occur when:\n * - The prompt was blocked by safety filters (but no explicit block reason was provided)\n * - An internal error occurred during generation\n * - The model was unable to generate a valid response\n *\n * @example\n * ```typescript\n * try {\n * await model.invoke(\"some prompt\");\n * } catch (error) {\n * if (NoCandidatesError.isInstance(error)) {\n * console.log(\"No response candidates were generated\");\n * }\n * }\n * ```\n */\nexport class NoCandidatesError extends ns.brand(GoogleError, \"no-candidates\") {\n readonly name = \"NoCandidatesError\";\n\n constructor() {\n super(\n \"No candidates returned from API. This may indicate the prompt was blocked or an error occurred.\"\n );\n }\n}\n\n/**\n * Error thrown when an invalid tool is provided to the tool converter.\n *\n * This error is thrown when a tool cannot be converted to Gemini's function\n * declaration format. Tools must be one of:\n * - LangChain structured tools (with Zod schemas)\n * - OpenAI format tools (with JSON Schema)\n * - Gemini-native function declarations\n * - Gemini-native tools (codeExecution, googleSearchRetrieval)\n *\n * @example\n * ```typescript\n * try {\n * convertToolsToGeminiFunctionDeclarations([invalidTool]);\n * } catch (error) {\n * if (InvalidToolError.isInstance(error)) {\n * console.log(`Invalid tool: ${error.tool}`);\n * console.log(`Expected formats: LangChain tools, OpenAI tools, or Gemini function declarations`);\n * }\n * }\n * ```\n */\nexport class InvalidToolError extends ns.brand(GoogleError, \"invalid-tool\") {\n readonly name = \"InvalidToolError\";\n\n /**\n * The invalid tool that was provided.\n */\n readonly tool: unknown;\n\n constructor(tool: unknown, message?: string) {\n const defaultMessage = `Received invalid tool: ${JSON.stringify(\n tool\n )}. Tools must be LangChain tools, OpenAI tools, or Gemini function declarations.`;\n\n super(message ?? defaultMessage);\n this.tool = tool;\n }\n}\n\n/**\n * Error thrown when a ToolMessage references a tool call that cannot be found.\n *\n * This error is thrown when a ToolMessage is passed to ChatGoogle but there is no\n * corresponding AIMessage with a matching toolCallId in the conversation history.\n * This typically indicates that:\n * - The tool call ID in the ToolMessage doesn't match any previous tool calls\n * - The AIMessage containing the tool call was not included in the message history\n * - The tool call ID was incorrectly set or modified\n *\n * @example\n * ```typescript\n * try {\n * await model.invoke([\n * new ToolMessage({ content: \"result\", tool_call_id: \"nonexistent-id\" })\n * ]);\n * } catch (error) {\n * if (ToolCallNotFoundError.isInstance(error)) {\n * console.log(`Tool call not found: ${error.toolCallId}`);\n * }\n * }\n * ```\n */\nexport class ToolCallNotFoundError extends ns.brand(\n GoogleError,\n \"tool-call-not-found\"\n) {\n readonly name = \"ToolCallNotFoundError\";\n\n /**\n * The tool call ID that could not be found in the conversation history.\n */\n readonly toolCallId: string;\n\n constructor(toolCallId: string, message?: string) {\n const defaultMessage = `Tool call with ID \"${toolCallId}\" not found in conversation history. Ensure the corresponding AIMessage with this tool call ID is included in the input.`;\n\n super(message ?? defaultMessage);\n this.toolCallId = toolCallId;\n }\n}\n\n/**\n * Parameters for constructing a MalformedOutputError\n */\ntype MalformedOutputErrorParams = {\n /**\n * The error message describing what went wrong during parsing\n */\n message: string;\n /**\n * Optional cause of the parsing error (e.g., the original error that occurred)\n */\n cause?: unknown;\n};\n\n/**\n * Error thrown when output parsing fails in structured output methods.\n *\n * This error is thrown when the model's response cannot be parsed according to\n * the expected schema or format. Common causes include:\n * - Invalid message type (not an AIMessage or AIMessageChunk)\n * - Missing or empty content in the response\n * - Malformed JSON that cannot be parsed\n * - Schema validation failures\n *\n * @example\n * ```typescript\n * try {\n * const result = await model.withStructuredOutput(schema).invoke(\"extract data\");\n * } catch (error) {\n * if (MalformedOutputError.isInstance(error)) {\n * console.log(`Failed to parse output: ${error.message}`);\n * if (error.cause) {\n * console.log(`Cause:`, error.cause);\n * }\n * }\n * }\n * ```\n */\nexport class MalformedOutputError extends ns.brand(\n GoogleError,\n \"malformed-output\"\n) {\n readonly name = \"MalformedOutputError\";\n\n /**\n * Optional cause of the parsing error.\n * This may be the original error that occurred during parsing,\n * or additional context about what went wrong.\n */\n readonly cause?: unknown;\n\n constructor(params: MalformedOutputErrorParams) {\n super(params.message);\n this.cause = params.cause;\n }\n}\n\n/**\n * Error thrown when input provided to the Google provider is invalid.\n *\n * This error is thrown when the user provides input that cannot be\n * processed by Gemini, such as:\n * - Union types in tool/function schemas\n * - Null-only types in schemas\n * - Unsupported content block source types\n * - Missing required fields in content blocks\n *\n * @example\n * ```typescript\n * try {\n * const result = schemaToGeminiParameters(unionTypeSchema);\n * } catch (error) {\n * if (InvalidInputError.isInstance(error)) {\n * console.log(`Invalid input: ${error.message}`);\n * }\n * }\n * ```\n */\nexport class InvalidInputError extends ns.brand(GoogleError, \"invalid-input\") {\n readonly name = \"InvalidInputError\";\n}\n"],"mappings":"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"}
|
package/dist/utils/errors.js
CHANGED
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
import { iife } from "./misc.js";
|
|
2
2
|
import { LangChainError, ns } from "@langchain/core/errors";
|
|
3
|
-
|
|
4
3
|
//#region src/utils/errors.ts
|
|
5
4
|
const ns$1 = ns.sub("google");
|
|
5
|
+
async function readErrorResponseBody(response) {
|
|
6
|
+
return iife(async () => {
|
|
7
|
+
try {
|
|
8
|
+
if ((response.headers.get("content-type") ?? "").includes("application/json")) return await response.json();
|
|
9
|
+
const text = await response.text();
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(text);
|
|
12
|
+
} catch {
|
|
13
|
+
return text;
|
|
14
|
+
}
|
|
15
|
+
} catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
6
20
|
/**
|
|
7
21
|
* Base error class for all Google provider errors.
|
|
8
22
|
*
|
|
@@ -205,14 +219,7 @@ var AuthError = class AuthError extends ns$1.brand(GoogleError, "auth") {
|
|
|
205
219
|
* ```
|
|
206
220
|
*/
|
|
207
221
|
static async fromResponse(response) {
|
|
208
|
-
const errorBody = await
|
|
209
|
-
try {
|
|
210
|
-
if ((response.headers.get("content-type") ?? "").includes("application/json")) return await response.json();
|
|
211
|
-
return await response.text();
|
|
212
|
-
} catch {
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
});
|
|
222
|
+
const errorBody = await readErrorResponseBody(response);
|
|
216
223
|
const message = errorBody?.error_description ?? `Authentication failed with status code ${response.status}`;
|
|
217
224
|
const headers = iife(() => {
|
|
218
225
|
const object = {};
|
|
@@ -351,14 +358,7 @@ var RequestError = class RequestError extends ns$1.brand(GoogleError, "request")
|
|
|
351
358
|
* ```
|
|
352
359
|
*/
|
|
353
360
|
static async fromResponse(response) {
|
|
354
|
-
const errorBody = await
|
|
355
|
-
try {
|
|
356
|
-
if ((response.headers.get("content-type") ?? "").includes("application/json")) return await response.json();
|
|
357
|
-
return await response.text();
|
|
358
|
-
} catch {
|
|
359
|
-
return null;
|
|
360
|
-
}
|
|
361
|
-
});
|
|
361
|
+
const errorBody = await readErrorResponseBody(response);
|
|
362
362
|
const message = errorBody?.error?.message ?? errorBody?.message ?? errorBody?.error ?? `Request failed with status code ${response.status}`;
|
|
363
363
|
const headers = iife(() => {
|
|
364
364
|
const object = {};
|
|
@@ -533,7 +533,7 @@ var MalformedOutputError = class extends ns$1.brand(GoogleError, "malformed-outp
|
|
|
533
533
|
var InvalidInputError = class extends ns$1.brand(GoogleError, "invalid-input") {
|
|
534
534
|
name = "InvalidInputError";
|
|
535
535
|
};
|
|
536
|
-
|
|
537
536
|
//#endregion
|
|
538
537
|
export { AuthError, ConfigurationError, InvalidInputError, InvalidToolError, MalformedOutputError, NoCandidatesError, PromptBlockedError, RequestError, ToolCallNotFoundError };
|
|
538
|
+
|
|
539
539
|
//# sourceMappingURL=errors.js.map
|