@langchain/google 0.1.11 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/chat_models/api-types.d.cts +257 -102
  3. package/dist/chat_models/api-types.d.ts +257 -102
  4. package/dist/chat_models/base.cjs +76 -9
  5. package/dist/chat_models/base.cjs.map +1 -1
  6. package/dist/chat_models/base.d.cts +5 -0
  7. package/dist/chat_models/base.d.ts +5 -0
  8. package/dist/chat_models/base.js +77 -10
  9. package/dist/chat_models/base.js.map +1 -1
  10. package/dist/chat_models/node.cjs.map +1 -1
  11. package/dist/chat_models/node.js.map +1 -1
  12. package/dist/chat_models/types.cjs +6 -0
  13. package/dist/chat_models/types.cjs.map +1 -0
  14. package/dist/chat_models/types.d.cts +13 -0
  15. package/dist/chat_models/types.d.ts +13 -0
  16. package/dist/chat_models/types.js +6 -0
  17. package/dist/chat_models/types.js.map +1 -0
  18. package/dist/clients/node.cjs.map +1 -1
  19. package/dist/clients/node.js.map +1 -1
  20. package/dist/converters/messages.cjs +12 -0
  21. package/dist/converters/messages.cjs.map +1 -1
  22. package/dist/converters/messages.js +12 -0
  23. package/dist/converters/messages.js.map +1 -1
  24. package/dist/converters/params.cjs +12 -0
  25. package/dist/converters/params.cjs.map +1 -1
  26. package/dist/converters/params.js +12 -1
  27. package/dist/converters/params.js.map +1 -1
  28. package/dist/converters/tools.cjs.map +1 -1
  29. package/dist/converters/tools.js.map +1 -1
  30. package/dist/utils/gcp-auth.cjs.map +1 -1
  31. package/dist/utils/gcp-auth.js.map +1 -1
  32. package/dist/utils/stream_events.cjs +173 -0
  33. package/dist/utils/stream_events.cjs.map +1 -0
  34. package/dist/utils/stream_events.js +173 -0
  35. package/dist/utils/stream_events.js.map +1 -0
  36. package/node.cjs +1 -0
  37. package/node.d.cts +1 -0
  38. package/node.d.ts +1 -0
  39. package/node.js +1 -0
  40. package/package.json +13 -8
@@ -1 +1 @@
1
- {"version":3,"file":"node.cjs","names":["ApiClient","iife","normalizeGCPCredentials","GoogleAuth","GOOGLE_API_KEY_HEADER","GCP_API_KEY_HEADER","getGCPCredentialsAccessToken","ConfigurationError"],"sources":["../../src/clients/node.ts"],"sourcesContent":["import { GoogleAuth, type GoogleAuthOptions } from \"google-auth-library\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { GCP_API_KEY_HEADER, GOOGLE_API_KEY_HEADER } from \"../const.js\";\nimport { ApiClient } from \"./index.js\";\nimport {\n GCPCredentials,\n getGCPCredentialsAccessToken,\n normalizeGCPCredentials,\n} from \"../utils/gcp-auth.js\";\nimport { ConfigurationError } from \"../utils/errors.js\";\nimport { iife } from \"../utils/misc.js\";\n\n/**\n * Configuration parameters for the NodeApiClient.\n *\n * This interface defines the authentication options available for Node.js-based\n * API clients. Authentication can be provided via an API key, GCP service\n * account credentials, or advanced Google Auth options. The client will\n * automatically check environment variables if explicit values are not provided.\n *\n * @interface\n */\nexport interface NodeApiClientParams {\n /**\n * Google API key for authentication.\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @see https://cloud.google.com/docs/authentication/api-keys\n */\n apiKey?: string;\n\n /**\n * GCP service account credentials for authentication.\n *\n * Can be provided as either:\n * - A JSON string containing the service account key\n * - A GCPCredentials object\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable.\n *\n * @see https://cloud.google.com/iam/docs/creating-managing-service-account-keys\n */\n credentials?: string | GCPCredentials;\n\n /**\n * Google Auth options configuration.\n *\n * Provides fine-grained control over authentication behavior using the\n * google-auth-library. This option enables:\n * - Custom OAuth2 scopes\n * - Application Default Credentials (ADC)\n * - Impersonation and delegation\n * - Custom credential sources\n *\n * When provided, this takes precedence over simple credential-based auth\n * but is lower priority than API key authentication.\n *\n * @see https://github.com/googleapis/google-auth-library-nodejs\n * @see https://cloud.google.com/docs/authentication/application-default-credentials\n */\n googleAuthOptions?: GoogleAuthOptions;\n}\n\n/**\n * Node.js-specific API client for Google services.\n *\n * This client provides comprehensive authentication for Google API requests in\n * Node.js environments, leveraging the full capabilities of the google-auth-library.\n * It supports three authentication methods with the following priority:\n *\n * 1. **API Key Authentication**: Simple key-based authentication suitable for\n * public APIs and development environments.\n *\n * 2. **Service Account Credentials**: OAuth2-based authentication using GCP\n * service account keys. The client automatically handles token generation\n * and caching using a web-compatible implementation.\n *\n * 3. **Google Auth Library**: Advanced authentication using google-auth-library,\n * supporting Application Default Credentials (ADC), custom scopes, and\n * various credential sources.\n *\n * The client follows this authentication priority:\n * 1. Explicit `apiKey` parameter\n * 2. `GOOGLE_API_KEY` environment variable\n * 3. Explicit `credentials` parameter\n * 4. `GOOGLE_CLOUD_CREDENTIALS` environment variable\n * 5. `googleAuthOptions` parameter\n * 6. Application Default Credentials (if googleAuthOptions is provided without explicit credentials)\n *\n * @extends ApiClient\n *\n * @example\n * ```typescript\n * // Using API key\n * const client = new NodeApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // Using service account credentials\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * project_id: \"my-project\",\n * private_key_id: \"key-id\",\n * private_key: \"-----BEGIN PRIVATE KEY-----\\n...\",\n * client_email: \"service-account@project.iam.gserviceaccount.com\",\n * // ... other required fields\n * }\n * });\n *\n * // Using Google Auth with custom scopes\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * keyFilename: '/path/to/service-account-key.json'\n * }\n * });\n *\n * // Using Application Default Credentials\n * const client = new NodeApiClient({\n * googleAuthOptions: {}\n * });\n *\n * // Using environment variables\n * // Set GOOGLE_API_KEY or GOOGLE_CLOUD_CREDENTIALS\n * const client = new NodeApiClient();\n *\n * // Make authenticated requests\n * const request = new Request('https://api.google.com/...');\n * const response = await client.fetch(request);\n * ```\n *\n * @remarks\n * - API keys are added via the `X-Goog-Api-Key` header\n * - Service account tokens are added via the `Authorization` header with Bearer scheme\n * - Google Auth headers are automatically generated and added by google-auth-library\n * - Tokens are automatically cached and refreshed as needed\n * - For web/edge environments without google-auth-library, use WebApiClient instead\n *\n * @see {@link WebApiClient} for web-compatible authentication without google-auth-library\n * @see {@link GCPCredentials} for service account credential structure\n * @see {@link GoogleAuthOptions} for advanced authentication configuration\n */\nexport class NodeApiClient extends ApiClient {\n hasApiKey(): boolean {\n return typeof this.apiKey === \"string\" && this.apiKey !== \"\";\n }\n\n async getProjectId(): Promise<string> {\n if (typeof this.credentials !== \"undefined\") {\n return this.credentials.project_id;\n } else if (typeof this.googleAuth !== \"undefined\") {\n return this.googleAuth.getProjectId();\n } else {\n return super.getProjectId();\n }\n }\n\n /**\n * The Google API key used for authentication, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @protected\n */\n protected apiKey?: string;\n\n /**\n * The normalized GCP service account credentials, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable, and is normalized\n * to ensure immutability.\n *\n * @protected\n */\n protected credentials?: GCPCredentials;\n\n /**\n * The Google Auth instance for advanced authentication, if configured.\n *\n * This instance is created when `googleAuthOptions` is provided and\n * handles Application Default Credentials, custom scopes, and other\n * advanced authentication scenarios.\n *\n * @protected\n */\n protected googleAuth?: GoogleAuth;\n\n /**\n * Creates a new NodeApiClient instance.\n *\n * The constructor initializes authentication credentials by checking:\n * 1. Explicit parameters passed to the constructor\n * 2. Environment variables (`GOOGLE_API_KEY`, `GOOGLE_CLOUD_CREDENTIALS`)\n * 3. Application Default Credentials (if googleAuthOptions is provided)\n *\n * Credentials are normalized and frozen to prevent accidental modification.\n * The GoogleAuth instance is lazily initialized only when googleAuthOptions\n * is provided.\n *\n * @param params - Configuration parameters for the client. Defaults to an empty object.\n *\n * @example\n * ```typescript\n * // Minimal configuration (uses environment variables or ADC)\n * const client = new NodeApiClient();\n *\n * // With explicit API key\n * const client = new WebApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // With service account credentials as JSON string\n * const client = new WebApiClient({\n * credentials: '{\"type\":\"service_account\",...}'\n * });\n *\n * // With service account credentials as object\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * // ... other fields\n * }\n * });\n *\n * // With Google Auth options\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * projectId: 'my-project'\n * }\n * });\n * ```\n */\n constructor(protected params: NodeApiClientParams = {}) {\n super();\n\n this.apiKey = params.apiKey ?? getEnvironmentVariable(\"GOOGLE_API_KEY\");\n this.credentials = iife(() => {\n if (params.credentials) {\n return normalizeGCPCredentials(params.credentials);\n }\n const credentialsVar = getEnvironmentVariable(\"GOOGLE_CLOUD_CREDENTIALS\");\n if (credentialsVar) {\n return normalizeGCPCredentials(credentialsVar);\n }\n return undefined;\n });\n this.googleAuth = iife(() => {\n if (params.googleAuthOptions) {\n return new GoogleAuth(params.googleAuthOptions);\n }\n if (!this.apiKey && !this.credentials) {\n return new GoogleAuth();\n }\n return undefined;\n });\n }\n\n /**\n * Executes an HTTP request with appropriate Google API authentication.\n *\n * This method adds authentication headers to the request based on the\n * configured authentication method, following this priority:\n *\n * 1. If an API key is configured, adds the `X-Goog-Api-Key` header\n * 2. If service account credentials are configured, generates an access token\n * and adds it via the `Authorization` header with Bearer scheme\n * 3. If Google Auth is configured, uses google-auth-library to generate\n * appropriate authentication headers (may include Authorization, x-goog-user-project, etc.)\n *\n * The authentication priority ensures that explicit API keys take precedence,\n * followed by service account credentials, and finally Google Auth options.\n *\n * @param request - The HTTP request to execute. Headers will be modified\n * in-place to include authentication information.\n * @returns A Promise that resolves to the HTTP response\n *\n * @throws {AuthError} If service account authentication fails (e.g., invalid\n * credentials, network error, or token generation failure)\n * @throws {Error} If Google Auth authentication fails (e.g., missing credentials,\n * invalid configuration, or ADC not available)\n *\n * @example\n * ```typescript\n * const client = new NodeApiClient({ apiKey: 'your-key' });\n * const request = new Request('https://generativelanguage.googleapis.com/v1/models');\n * const response = await client.fetch(request);\n * const data = await response.json();\n * ```\n *\n * @remarks\n * - The request object is modified in-place by adding headers\n * - Service account tokens are automatically cached and refreshed\n * - Google Auth tokens are managed by google-auth-library with automatic refresh\n * - Only one authentication method is used per request (based on priority)\n * - Google Auth may add multiple headers depending on the authentication type\n */\n async fetch(request: Request): Promise<Response> {\n if (this.apiKey) {\n request.headers.set(GOOGLE_API_KEY_HEADER, this.apiKey);\n } else if (this.credentials) {\n request.headers.set(\n GCP_API_KEY_HEADER,\n `Bearer ${getGCPCredentialsAccessToken(this.credentials)}`\n );\n } else if (this.googleAuth) {\n const authHeaders = await this.googleAuth.getRequestHeaders(request.url);\n authHeaders.forEach((value, key) => {\n if (value !== null) {\n request.headers.set(key, value);\n }\n });\n }\n return fetch(request);\n }\n}\n\n/**\n * Ensures that Google Auth options include required OAuth2 scopes.\n *\n * This utility function validates and augments GoogleAuthOptions to ensure\n * that all required OAuth2 scopes are present. It handles three scenarios:\n *\n * 1. **No auth options provided**: Creates new options with required scopes\n * 2. **Auth options without scopes**: Adds required scopes to existing options\n * 3. **Auth options with scopes**: Validates that all required scopes are present\n *\n * This is particularly useful when working with Google APIs that require specific\n * scopes for access, ensuring that authentication is properly configured before\n * making API requests.\n *\n * @param authOptions - The existing Google Auth options to validate/augment.\n * Can be undefined or an empty object.\n * @param requiredScopes - Array of OAuth2 scope URLs that must be present.\n * These are typically Google API scope URLs like\n * 'https://www.googleapis.com/auth/cloud-platform'\n * @returns The validated/augmented GoogleAuthOptions with all required scopes\n *\n * @throws {Error} If authOptions already has scopes but is missing any of the\n * required scopes. This prevents accidental scope reduction that\n * could break API access.\n *\n * @example\n * ```typescript\n * // Create new options with required scopes\n * const options = ensureAuthScopes(undefined, [\n * 'https://www.googleapis.com/auth/cloud-platform'\n * ]);\n * // Result: { scopes: ['https://www.googleapis.com/auth/cloud-platform'] }\n *\n * // Add scopes to existing options\n * const options = ensureAuthScopes(\n * { projectId: 'my-project' },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: { projectId: 'my-project', scopes: [...] }\n *\n * // Validate existing scopes\n * const options = ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/cloud-platform'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: Original options (validation passed)\n *\n * // Error case - missing required scope\n * try {\n * ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/userinfo.email'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * } catch (error) {\n * // Error: Invalid auth scopes. Scopes must include ...\n * }\n * ```\n *\n * @remarks\n * - Scopes can be provided as a string or array of strings in GoogleAuthOptions\n * - The function normalizes single-string scopes to arrays for validation\n * - Required scopes are not automatically added if existing scopes are present;\n * instead, an error is thrown to prevent unintended scope changes\n * - This function does not modify the original authOptions object\n *\n * @see https://developers.google.com/identity/protocols/oauth2/scopes\n * @see {@link GoogleAuthOptions} for the full authentication configuration interface\n */\nexport function ensureAuthScopes(\n authOptions: GoogleAuthOptions,\n requiredScopes: string[]\n): GoogleAuthOptions {\n if (!authOptions) {\n return { scopes: requiredScopes };\n } else if (!authOptions.scopes) {\n return { ...authOptions, scopes: requiredScopes };\n } else {\n const existingScopes = Array.isArray(authOptions.scopes)\n ? authOptions.scopes\n : [authOptions.scopes];\n const missingScopes = requiredScopes.filter(\n (scope) => !existingScopes.includes(scope)\n );\n if (missingScopes.length > 0) {\n throw new ConfigurationError(\n `Invalid auth scopes. Scopes must include ${requiredScopes.join(\", \")}`\n );\n }\n }\n return authOptions;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkJA,IAAa,gBAAb,cAAmCA,cAAAA,UAAU;CAC3C,YAAqB;AACnB,SAAO,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;;CAG5D,MAAM,eAAgC;AACpC,MAAI,OAAO,KAAK,gBAAgB,YAC9B,QAAO,KAAK,YAAY;WACf,OAAO,KAAK,eAAe,YACpC,QAAO,KAAK,WAAW,cAAc;MAErC,QAAO,MAAM,cAAc;;;;;;;;;;CAY/B;;;;;;;;;;CAWA;;;;;;;;;;CAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDA,YAAY,SAAwC,EAAE,EAAE;AACtD,SAAO;AADa,OAAA,SAAA;AAGpB,OAAK,SAAS,OAAO,WAAA,GAAA,0BAAA,wBAAiC,iBAAiB;AACvE,OAAK,cAAcC,aAAAA,WAAW;AAC5B,OAAI,OAAO,YACT,QAAOC,iBAAAA,wBAAwB,OAAO,YAAY;GAEpD,MAAM,kBAAA,GAAA,0BAAA,wBAAwC,2BAA2B;AACzE,OAAI,eACF,QAAOA,iBAAAA,wBAAwB,eAAe;IAGhD;AACF,OAAK,aAAaD,aAAAA,WAAW;AAC3B,OAAI,OAAO,kBACT,QAAO,IAAIE,oBAAAA,WAAW,OAAO,kBAAkB;AAEjD,OAAI,CAAC,KAAK,UAAU,CAAC,KAAK,YACxB,QAAO,IAAIA,oBAAAA,YAAY;IAGzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CJ,MAAM,MAAM,SAAqC;AAC/C,MAAI,KAAK,OACP,SAAQ,QAAQ,IAAIC,cAAAA,uBAAuB,KAAK,OAAO;WAC9C,KAAK,YACd,SAAQ,QAAQ,IACdC,cAAAA,oBACA,UAAUC,iBAAAA,6BAA6B,KAAK,YAAY,GACzD;WACQ,KAAK,WAEd,EADoB,MAAM,KAAK,WAAW,kBAAkB,QAAQ,IAAI,EAC5D,SAAS,OAAO,QAAQ;AAClC,OAAI,UAAU,KACZ,SAAQ,QAAQ,IAAI,KAAK,MAAM;IAEjC;AAEJ,SAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEzB,SAAgB,iBACd,aACA,gBACmB;AACnB,KAAI,CAAC,YACH,QAAO,EAAE,QAAQ,gBAAgB;UACxB,CAAC,YAAY,OACtB,QAAO;EAAE,GAAG;EAAa,QAAQ;EAAgB;MAC5C;EACL,MAAM,iBAAiB,MAAM,QAAQ,YAAY,OAAO,GACpD,YAAY,SACZ,CAAC,YAAY,OAAO;AAIxB,MAHsB,eAAe,QAClC,UAAU,CAAC,eAAe,SAAS,MAAM,CAC3C,CACiB,SAAS,EACzB,OAAM,IAAIC,eAAAA,mBACR,4CAA4C,eAAe,KAAK,KAAK,GACtE;;AAGL,QAAO"}
1
+ {"version":3,"file":"node.cjs","names":["ApiClient","iife","normalizeGCPCredentials","GoogleAuth","GOOGLE_API_KEY_HEADER","GCP_API_KEY_HEADER","getGCPCredentialsAccessToken","ConfigurationError"],"sources":["../../src/clients/node.ts"],"sourcesContent":["import { GoogleAuth, type GoogleAuthOptions } from \"google-auth-library\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { GCP_API_KEY_HEADER, GOOGLE_API_KEY_HEADER } from \"../const.js\";\nimport { ApiClient } from \"./index.js\";\nimport {\n GCPCredentials,\n getGCPCredentialsAccessToken,\n normalizeGCPCredentials,\n} from \"../utils/gcp-auth.js\";\nimport { ConfigurationError } from \"../utils/errors.js\";\nimport { iife } from \"../utils/misc.js\";\n\n/**\n * Configuration parameters for the NodeApiClient.\n *\n * This interface defines the authentication options available for Node.js-based\n * API clients. Authentication can be provided via an API key, GCP service\n * account credentials, or advanced Google Auth options. The client will\n * automatically check environment variables if explicit values are not provided.\n *\n * @interface\n */\nexport interface NodeApiClientParams {\n /**\n * Google API key for authentication.\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @see https://cloud.google.com/docs/authentication/api-keys\n */\n apiKey?: string;\n\n /**\n * GCP service account credentials for authentication.\n *\n * Can be provided as either:\n * - A JSON string containing the service account key\n * - A GCPCredentials object\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable.\n *\n * @see https://cloud.google.com/iam/docs/creating-managing-service-account-keys\n */\n credentials?: string | GCPCredentials;\n\n /**\n * Google Auth options configuration.\n *\n * Provides fine-grained control over authentication behavior using the\n * google-auth-library. This option enables:\n * - Custom OAuth2 scopes\n * - Application Default Credentials (ADC)\n * - Impersonation and delegation\n * - Custom credential sources\n *\n * When provided, this takes precedence over simple credential-based auth\n * but is lower priority than API key authentication.\n *\n * @see https://github.com/googleapis/google-auth-library-nodejs\n * @see https://cloud.google.com/docs/authentication/application-default-credentials\n */\n googleAuthOptions?: GoogleAuthOptions;\n}\n\n/**\n * Node.js-specific API client for Google services.\n *\n * This client provides comprehensive authentication for Google API requests in\n * Node.js environments, leveraging the full capabilities of the google-auth-library.\n * It supports three authentication methods with the following priority:\n *\n * 1. **API Key Authentication**: Simple key-based authentication suitable for\n * public APIs and development environments.\n *\n * 2. **Service Account Credentials**: OAuth2-based authentication using GCP\n * service account keys. The client automatically handles token generation\n * and caching using a web-compatible implementation.\n *\n * 3. **Google Auth Library**: Advanced authentication using google-auth-library,\n * supporting Application Default Credentials (ADC), custom scopes, and\n * various credential sources.\n *\n * The client follows this authentication priority:\n * 1. Explicit `apiKey` parameter\n * 2. `GOOGLE_API_KEY` environment variable\n * 3. Explicit `credentials` parameter\n * 4. `GOOGLE_CLOUD_CREDENTIALS` environment variable\n * 5. `googleAuthOptions` parameter\n * 6. Application Default Credentials (if googleAuthOptions is provided without explicit credentials)\n *\n * @extends ApiClient\n *\n * @example\n * ```typescript\n * // Using API key\n * const client = new NodeApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // Using service account credentials\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * project_id: \"my-project\",\n * private_key_id: \"key-id\",\n * private_key: \"-----BEGIN PRIVATE KEY-----\\n...\",\n * client_email: \"service-account@project.iam.gserviceaccount.com\",\n * // ... other required fields\n * }\n * });\n *\n * // Using Google Auth with custom scopes\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * keyFilename: '/path/to/service-account-key.json'\n * }\n * });\n *\n * // Using Application Default Credentials\n * const client = new NodeApiClient({\n * googleAuthOptions: {}\n * });\n *\n * // Using environment variables\n * // Set GOOGLE_API_KEY or GOOGLE_CLOUD_CREDENTIALS\n * const client = new NodeApiClient();\n *\n * // Make authenticated requests\n * const request = new Request('https://api.google.com/...');\n * const response = await client.fetch(request);\n * ```\n *\n * @remarks\n * - API keys are added via the `X-Goog-Api-Key` header\n * - Service account tokens are added via the `Authorization` header with Bearer scheme\n * - Google Auth headers are automatically generated and added by google-auth-library\n * - Tokens are automatically cached and refreshed as needed\n * - For web/edge environments without google-auth-library, use WebApiClient instead\n *\n * @see {@link WebApiClient} for web-compatible authentication without google-auth-library\n * @see {@link GCPCredentials} for service account credential structure\n * @see {@link GoogleAuthOptions} for advanced authentication configuration\n */\nexport class NodeApiClient extends ApiClient {\n hasApiKey(): boolean {\n return typeof this.apiKey === \"string\" && this.apiKey !== \"\";\n }\n\n async getProjectId(): Promise<string> {\n if (typeof this.credentials !== \"undefined\") {\n return this.credentials.project_id;\n } else if (typeof this.googleAuth !== \"undefined\") {\n return this.googleAuth.getProjectId();\n } else {\n return super.getProjectId();\n }\n }\n\n /**\n * The Google API key used for authentication, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @protected\n */\n protected apiKey?: string;\n\n /**\n * The normalized GCP service account credentials, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable, and is normalized\n * to ensure immutability.\n *\n * @protected\n */\n protected credentials?: GCPCredentials;\n\n /**\n * The Google Auth instance for advanced authentication, if configured.\n *\n * This instance is created when `googleAuthOptions` is provided and\n * handles Application Default Credentials, custom scopes, and other\n * advanced authentication scenarios.\n *\n * @protected\n */\n protected googleAuth?: GoogleAuth;\n\n /**\n * Creates a new NodeApiClient instance.\n *\n * The constructor initializes authentication credentials by checking:\n * 1. Explicit parameters passed to the constructor\n * 2. Environment variables (`GOOGLE_API_KEY`, `GOOGLE_CLOUD_CREDENTIALS`)\n * 3. Application Default Credentials (if googleAuthOptions is provided)\n *\n * Credentials are normalized and frozen to prevent accidental modification.\n * The GoogleAuth instance is lazily initialized only when googleAuthOptions\n * is provided.\n *\n * @param params - Configuration parameters for the client. Defaults to an empty object.\n *\n * @example\n * ```typescript\n * // Minimal configuration (uses environment variables or ADC)\n * const client = new NodeApiClient();\n *\n * // With explicit API key\n * const client = new WebApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // With service account credentials as JSON string\n * const client = new WebApiClient({\n * credentials: '{\"type\":\"service_account\",...}'\n * });\n *\n * // With service account credentials as object\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * // ... other fields\n * }\n * });\n *\n * // With Google Auth options\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * projectId: 'my-project'\n * }\n * });\n * ```\n */\n constructor(protected params: NodeApiClientParams = {}) {\n super();\n\n this.apiKey = params.apiKey ?? getEnvironmentVariable(\"GOOGLE_API_KEY\");\n this.credentials = iife(() => {\n if (params.credentials) {\n return normalizeGCPCredentials(params.credentials);\n }\n const credentialsVar = getEnvironmentVariable(\"GOOGLE_CLOUD_CREDENTIALS\");\n if (credentialsVar) {\n return normalizeGCPCredentials(credentialsVar);\n }\n return undefined;\n });\n this.googleAuth = iife(() => {\n if (params.googleAuthOptions) {\n return new GoogleAuth(params.googleAuthOptions);\n }\n if (!this.apiKey && !this.credentials) {\n return new GoogleAuth();\n }\n return undefined;\n });\n }\n\n /**\n * Executes an HTTP request with appropriate Google API authentication.\n *\n * This method adds authentication headers to the request based on the\n * configured authentication method, following this priority:\n *\n * 1. If an API key is configured, adds the `X-Goog-Api-Key` header\n * 2. If service account credentials are configured, generates an access token\n * and adds it via the `Authorization` header with Bearer scheme\n * 3. If Google Auth is configured, uses google-auth-library to generate\n * appropriate authentication headers (may include Authorization, x-goog-user-project, etc.)\n *\n * The authentication priority ensures that explicit API keys take precedence,\n * followed by service account credentials, and finally Google Auth options.\n *\n * @param request - The HTTP request to execute. Headers will be modified\n * in-place to include authentication information.\n * @returns A Promise that resolves to the HTTP response\n *\n * @throws {AuthError} If service account authentication fails (e.g., invalid\n * credentials, network error, or token generation failure)\n * @throws {Error} If Google Auth authentication fails (e.g., missing credentials,\n * invalid configuration, or ADC not available)\n *\n * @example\n * ```typescript\n * const client = new NodeApiClient({ apiKey: 'your-key' });\n * const request = new Request('https://generativelanguage.googleapis.com/v1/models');\n * const response = await client.fetch(request);\n * const data = await response.json();\n * ```\n *\n * @remarks\n * - The request object is modified in-place by adding headers\n * - Service account tokens are automatically cached and refreshed\n * - Google Auth tokens are managed by google-auth-library with automatic refresh\n * - Only one authentication method is used per request (based on priority)\n * - Google Auth may add multiple headers depending on the authentication type\n */\n async fetch(request: Request): Promise<Response> {\n if (this.apiKey) {\n request.headers.set(GOOGLE_API_KEY_HEADER, this.apiKey);\n } else if (this.credentials) {\n request.headers.set(\n GCP_API_KEY_HEADER,\n `Bearer ${getGCPCredentialsAccessToken(this.credentials)}`\n );\n } else if (this.googleAuth) {\n const authHeaders = await this.googleAuth.getRequestHeaders(request.url);\n authHeaders.forEach((value, key) => {\n if (value !== null) {\n request.headers.set(key, value);\n }\n });\n }\n return fetch(request);\n }\n}\n\n/**\n * Ensures that Google Auth options include required OAuth2 scopes.\n *\n * This utility function validates and augments GoogleAuthOptions to ensure\n * that all required OAuth2 scopes are present. It handles three scenarios:\n *\n * 1. **No auth options provided**: Creates new options with required scopes\n * 2. **Auth options without scopes**: Adds required scopes to existing options\n * 3. **Auth options with scopes**: Validates that all required scopes are present\n *\n * This is particularly useful when working with Google APIs that require specific\n * scopes for access, ensuring that authentication is properly configured before\n * making API requests.\n *\n * @param authOptions - The existing Google Auth options to validate/augment.\n * Can be undefined or an empty object.\n * @param requiredScopes - Array of OAuth2 scope URLs that must be present.\n * These are typically Google API scope URLs like\n * 'https://www.googleapis.com/auth/cloud-platform'\n * @returns The validated/augmented GoogleAuthOptions with all required scopes\n *\n * @throws {Error} If authOptions already has scopes but is missing any of the\n * required scopes. This prevents accidental scope reduction that\n * could break API access.\n *\n * @example\n * ```typescript\n * // Create new options with required scopes\n * const options = ensureAuthScopes(undefined, [\n * 'https://www.googleapis.com/auth/cloud-platform'\n * ]);\n * // Result: { scopes: ['https://www.googleapis.com/auth/cloud-platform'] }\n *\n * // Add scopes to existing options\n * const options = ensureAuthScopes(\n * { projectId: 'my-project' },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: { projectId: 'my-project', scopes: [...] }\n *\n * // Validate existing scopes\n * const options = ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/cloud-platform'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: Original options (validation passed)\n *\n * // Error case - missing required scope\n * try {\n * ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/userinfo.email'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * } catch (error) {\n * // Error: Invalid auth scopes. Scopes must include ...\n * }\n * ```\n *\n * @remarks\n * - Scopes can be provided as a string or array of strings in GoogleAuthOptions\n * - The function normalizes single-string scopes to arrays for validation\n * - Required scopes are not automatically added if existing scopes are present;\n * instead, an error is thrown to prevent unintended scope changes\n * - This function does not modify the original authOptions object\n *\n * @see https://developers.google.com/identity/protocols/oauth2/scopes\n * @see {@link GoogleAuthOptions} for the full authentication configuration interface\n */\nexport function ensureAuthScopes(\n authOptions: GoogleAuthOptions,\n requiredScopes: string[]\n): GoogleAuthOptions {\n if (!authOptions) {\n return { scopes: requiredScopes };\n } else if (!authOptions.scopes) {\n return { ...authOptions, scopes: requiredScopes };\n } else {\n const existingScopes = Array.isArray(authOptions.scopes)\n ? authOptions.scopes\n : [authOptions.scopes];\n const missingScopes = requiredScopes.filter(\n (scope) => !existingScopes.includes(scope)\n );\n if (missingScopes.length > 0) {\n throw new ConfigurationError(\n `Invalid auth scopes. Scopes must include ${requiredScopes.join(\", \")}`\n );\n }\n }\n return authOptions;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkJA,IAAa,gBAAb,cAAmCA,cAAAA,UAAU;CAC3C,YAAqB;AACnB,SAAO,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;;CAG5D,MAAM,eAAgC;AACpC,MAAI,OAAO,KAAK,gBAAgB,YAC9B,QAAO,KAAK,YAAY;WACf,OAAO,KAAK,eAAe,YACpC,QAAO,KAAK,WAAW,cAAc;MAErC,QAAO,MAAM,cAAc;;;;;;;;;;CAY/B;;;;;;;;;;CAWA;;;;;;;;;;CAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDA,YAAY,SAAwC,EAAE,EAAE;AACtD,SAAO;AADa,OAAA,SAAA;AAGpB,OAAK,SAAS,OAAO,WAAA,GAAA,0BAAA,wBAAiC,iBAAiB;AACvE,OAAK,cAAcC,aAAAA,WAAW;AAC5B,OAAI,OAAO,YACT,QAAOC,iBAAAA,wBAAwB,OAAO,YAAY;GAEpD,MAAM,kBAAA,GAAA,0BAAA,wBAAwC,2BAA2B;AACzE,OAAI,eACF,QAAOA,iBAAAA,wBAAwB,eAAe;IAGhD;AACF,OAAK,aAAaD,aAAAA,WAAW;AAC3B,OAAI,OAAO,kBACT,QAAO,IAAIE,oBAAAA,WAAW,OAAO,kBAAkB;AAEjD,OAAI,CAAC,KAAK,UAAU,CAAC,KAAK,YACxB,QAAO,IAAIA,oBAAAA,YAAY;IAGzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CJ,MAAM,MAAM,SAAqC;AAC/C,MAAI,KAAK,OACP,SAAQ,QAAQ,IAAIC,cAAAA,uBAAuB,KAAK,OAAO;WAC9C,KAAK,YACd,SAAQ,QAAQ,IACdC,cAAAA,oBACA,UAAUC,iBAAAA,6BAA6B,KAAK,YAAY,GACzD;WACQ,KAAK,WAEd,EAAA,MAD0B,KAAK,WAAW,kBAAkB,QAAQ,IAAI,EAC5D,SAAS,OAAO,QAAQ;AAClC,OAAI,UAAU,KACZ,SAAQ,QAAQ,IAAI,KAAK,MAAM;IAEjC;AAEJ,SAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEzB,SAAgB,iBACd,aACA,gBACmB;AACnB,KAAI,CAAC,YACH,QAAO,EAAE,QAAQ,gBAAgB;UACxB,CAAC,YAAY,OACtB,QAAO;EAAE,GAAG;EAAa,QAAQ;EAAgB;MAC5C;EACL,MAAM,iBAAiB,MAAM,QAAQ,YAAY,OAAO,GACpD,YAAY,SACZ,CAAC,YAAY,OAAO;AAIxB,MAHsB,eAAe,QAClC,UAAU,CAAC,eAAe,SAAS,MAAM,CAE3B,CAAC,SAAS,EACzB,OAAM,IAAIC,eAAAA,mBACR,4CAA4C,eAAe,KAAK,KAAK,GACtE;;AAGL,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"node.js","names":[],"sources":["../../src/clients/node.ts"],"sourcesContent":["import { GoogleAuth, type GoogleAuthOptions } from \"google-auth-library\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { GCP_API_KEY_HEADER, GOOGLE_API_KEY_HEADER } from \"../const.js\";\nimport { ApiClient } from \"./index.js\";\nimport {\n GCPCredentials,\n getGCPCredentialsAccessToken,\n normalizeGCPCredentials,\n} from \"../utils/gcp-auth.js\";\nimport { ConfigurationError } from \"../utils/errors.js\";\nimport { iife } from \"../utils/misc.js\";\n\n/**\n * Configuration parameters for the NodeApiClient.\n *\n * This interface defines the authentication options available for Node.js-based\n * API clients. Authentication can be provided via an API key, GCP service\n * account credentials, or advanced Google Auth options. The client will\n * automatically check environment variables if explicit values are not provided.\n *\n * @interface\n */\nexport interface NodeApiClientParams {\n /**\n * Google API key for authentication.\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @see https://cloud.google.com/docs/authentication/api-keys\n */\n apiKey?: string;\n\n /**\n * GCP service account credentials for authentication.\n *\n * Can be provided as either:\n * - A JSON string containing the service account key\n * - A GCPCredentials object\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable.\n *\n * @see https://cloud.google.com/iam/docs/creating-managing-service-account-keys\n */\n credentials?: string | GCPCredentials;\n\n /**\n * Google Auth options configuration.\n *\n * Provides fine-grained control over authentication behavior using the\n * google-auth-library. This option enables:\n * - Custom OAuth2 scopes\n * - Application Default Credentials (ADC)\n * - Impersonation and delegation\n * - Custom credential sources\n *\n * When provided, this takes precedence over simple credential-based auth\n * but is lower priority than API key authentication.\n *\n * @see https://github.com/googleapis/google-auth-library-nodejs\n * @see https://cloud.google.com/docs/authentication/application-default-credentials\n */\n googleAuthOptions?: GoogleAuthOptions;\n}\n\n/**\n * Node.js-specific API client for Google services.\n *\n * This client provides comprehensive authentication for Google API requests in\n * Node.js environments, leveraging the full capabilities of the google-auth-library.\n * It supports three authentication methods with the following priority:\n *\n * 1. **API Key Authentication**: Simple key-based authentication suitable for\n * public APIs and development environments.\n *\n * 2. **Service Account Credentials**: OAuth2-based authentication using GCP\n * service account keys. The client automatically handles token generation\n * and caching using a web-compatible implementation.\n *\n * 3. **Google Auth Library**: Advanced authentication using google-auth-library,\n * supporting Application Default Credentials (ADC), custom scopes, and\n * various credential sources.\n *\n * The client follows this authentication priority:\n * 1. Explicit `apiKey` parameter\n * 2. `GOOGLE_API_KEY` environment variable\n * 3. Explicit `credentials` parameter\n * 4. `GOOGLE_CLOUD_CREDENTIALS` environment variable\n * 5. `googleAuthOptions` parameter\n * 6. Application Default Credentials (if googleAuthOptions is provided without explicit credentials)\n *\n * @extends ApiClient\n *\n * @example\n * ```typescript\n * // Using API key\n * const client = new NodeApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // Using service account credentials\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * project_id: \"my-project\",\n * private_key_id: \"key-id\",\n * private_key: \"-----BEGIN PRIVATE KEY-----\\n...\",\n * client_email: \"service-account@project.iam.gserviceaccount.com\",\n * // ... other required fields\n * }\n * });\n *\n * // Using Google Auth with custom scopes\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * keyFilename: '/path/to/service-account-key.json'\n * }\n * });\n *\n * // Using Application Default Credentials\n * const client = new NodeApiClient({\n * googleAuthOptions: {}\n * });\n *\n * // Using environment variables\n * // Set GOOGLE_API_KEY or GOOGLE_CLOUD_CREDENTIALS\n * const client = new NodeApiClient();\n *\n * // Make authenticated requests\n * const request = new Request('https://api.google.com/...');\n * const response = await client.fetch(request);\n * ```\n *\n * @remarks\n * - API keys are added via the `X-Goog-Api-Key` header\n * - Service account tokens are added via the `Authorization` header with Bearer scheme\n * - Google Auth headers are automatically generated and added by google-auth-library\n * - Tokens are automatically cached and refreshed as needed\n * - For web/edge environments without google-auth-library, use WebApiClient instead\n *\n * @see {@link WebApiClient} for web-compatible authentication without google-auth-library\n * @see {@link GCPCredentials} for service account credential structure\n * @see {@link GoogleAuthOptions} for advanced authentication configuration\n */\nexport class NodeApiClient extends ApiClient {\n hasApiKey(): boolean {\n return typeof this.apiKey === \"string\" && this.apiKey !== \"\";\n }\n\n async getProjectId(): Promise<string> {\n if (typeof this.credentials !== \"undefined\") {\n return this.credentials.project_id;\n } else if (typeof this.googleAuth !== \"undefined\") {\n return this.googleAuth.getProjectId();\n } else {\n return super.getProjectId();\n }\n }\n\n /**\n * The Google API key used for authentication, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @protected\n */\n protected apiKey?: string;\n\n /**\n * The normalized GCP service account credentials, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable, and is normalized\n * to ensure immutability.\n *\n * @protected\n */\n protected credentials?: GCPCredentials;\n\n /**\n * The Google Auth instance for advanced authentication, if configured.\n *\n * This instance is created when `googleAuthOptions` is provided and\n * handles Application Default Credentials, custom scopes, and other\n * advanced authentication scenarios.\n *\n * @protected\n */\n protected googleAuth?: GoogleAuth;\n\n /**\n * Creates a new NodeApiClient instance.\n *\n * The constructor initializes authentication credentials by checking:\n * 1. Explicit parameters passed to the constructor\n * 2. Environment variables (`GOOGLE_API_KEY`, `GOOGLE_CLOUD_CREDENTIALS`)\n * 3. Application Default Credentials (if googleAuthOptions is provided)\n *\n * Credentials are normalized and frozen to prevent accidental modification.\n * The GoogleAuth instance is lazily initialized only when googleAuthOptions\n * is provided.\n *\n * @param params - Configuration parameters for the client. Defaults to an empty object.\n *\n * @example\n * ```typescript\n * // Minimal configuration (uses environment variables or ADC)\n * const client = new NodeApiClient();\n *\n * // With explicit API key\n * const client = new WebApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // With service account credentials as JSON string\n * const client = new WebApiClient({\n * credentials: '{\"type\":\"service_account\",...}'\n * });\n *\n * // With service account credentials as object\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * // ... other fields\n * }\n * });\n *\n * // With Google Auth options\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * projectId: 'my-project'\n * }\n * });\n * ```\n */\n constructor(protected params: NodeApiClientParams = {}) {\n super();\n\n this.apiKey = params.apiKey ?? getEnvironmentVariable(\"GOOGLE_API_KEY\");\n this.credentials = iife(() => {\n if (params.credentials) {\n return normalizeGCPCredentials(params.credentials);\n }\n const credentialsVar = getEnvironmentVariable(\"GOOGLE_CLOUD_CREDENTIALS\");\n if (credentialsVar) {\n return normalizeGCPCredentials(credentialsVar);\n }\n return undefined;\n });\n this.googleAuth = iife(() => {\n if (params.googleAuthOptions) {\n return new GoogleAuth(params.googleAuthOptions);\n }\n if (!this.apiKey && !this.credentials) {\n return new GoogleAuth();\n }\n return undefined;\n });\n }\n\n /**\n * Executes an HTTP request with appropriate Google API authentication.\n *\n * This method adds authentication headers to the request based on the\n * configured authentication method, following this priority:\n *\n * 1. If an API key is configured, adds the `X-Goog-Api-Key` header\n * 2. If service account credentials are configured, generates an access token\n * and adds it via the `Authorization` header with Bearer scheme\n * 3. If Google Auth is configured, uses google-auth-library to generate\n * appropriate authentication headers (may include Authorization, x-goog-user-project, etc.)\n *\n * The authentication priority ensures that explicit API keys take precedence,\n * followed by service account credentials, and finally Google Auth options.\n *\n * @param request - The HTTP request to execute. Headers will be modified\n * in-place to include authentication information.\n * @returns A Promise that resolves to the HTTP response\n *\n * @throws {AuthError} If service account authentication fails (e.g., invalid\n * credentials, network error, or token generation failure)\n * @throws {Error} If Google Auth authentication fails (e.g., missing credentials,\n * invalid configuration, or ADC not available)\n *\n * @example\n * ```typescript\n * const client = new NodeApiClient({ apiKey: 'your-key' });\n * const request = new Request('https://generativelanguage.googleapis.com/v1/models');\n * const response = await client.fetch(request);\n * const data = await response.json();\n * ```\n *\n * @remarks\n * - The request object is modified in-place by adding headers\n * - Service account tokens are automatically cached and refreshed\n * - Google Auth tokens are managed by google-auth-library with automatic refresh\n * - Only one authentication method is used per request (based on priority)\n * - Google Auth may add multiple headers depending on the authentication type\n */\n async fetch(request: Request): Promise<Response> {\n if (this.apiKey) {\n request.headers.set(GOOGLE_API_KEY_HEADER, this.apiKey);\n } else if (this.credentials) {\n request.headers.set(\n GCP_API_KEY_HEADER,\n `Bearer ${getGCPCredentialsAccessToken(this.credentials)}`\n );\n } else if (this.googleAuth) {\n const authHeaders = await this.googleAuth.getRequestHeaders(request.url);\n authHeaders.forEach((value, key) => {\n if (value !== null) {\n request.headers.set(key, value);\n }\n });\n }\n return fetch(request);\n }\n}\n\n/**\n * Ensures that Google Auth options include required OAuth2 scopes.\n *\n * This utility function validates and augments GoogleAuthOptions to ensure\n * that all required OAuth2 scopes are present. It handles three scenarios:\n *\n * 1. **No auth options provided**: Creates new options with required scopes\n * 2. **Auth options without scopes**: Adds required scopes to existing options\n * 3. **Auth options with scopes**: Validates that all required scopes are present\n *\n * This is particularly useful when working with Google APIs that require specific\n * scopes for access, ensuring that authentication is properly configured before\n * making API requests.\n *\n * @param authOptions - The existing Google Auth options to validate/augment.\n * Can be undefined or an empty object.\n * @param requiredScopes - Array of OAuth2 scope URLs that must be present.\n * These are typically Google API scope URLs like\n * 'https://www.googleapis.com/auth/cloud-platform'\n * @returns The validated/augmented GoogleAuthOptions with all required scopes\n *\n * @throws {Error} If authOptions already has scopes but is missing any of the\n * required scopes. This prevents accidental scope reduction that\n * could break API access.\n *\n * @example\n * ```typescript\n * // Create new options with required scopes\n * const options = ensureAuthScopes(undefined, [\n * 'https://www.googleapis.com/auth/cloud-platform'\n * ]);\n * // Result: { scopes: ['https://www.googleapis.com/auth/cloud-platform'] }\n *\n * // Add scopes to existing options\n * const options = ensureAuthScopes(\n * { projectId: 'my-project' },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: { projectId: 'my-project', scopes: [...] }\n *\n * // Validate existing scopes\n * const options = ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/cloud-platform'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: Original options (validation passed)\n *\n * // Error case - missing required scope\n * try {\n * ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/userinfo.email'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * } catch (error) {\n * // Error: Invalid auth scopes. Scopes must include ...\n * }\n * ```\n *\n * @remarks\n * - Scopes can be provided as a string or array of strings in GoogleAuthOptions\n * - The function normalizes single-string scopes to arrays for validation\n * - Required scopes are not automatically added if existing scopes are present;\n * instead, an error is thrown to prevent unintended scope changes\n * - This function does not modify the original authOptions object\n *\n * @see https://developers.google.com/identity/protocols/oauth2/scopes\n * @see {@link GoogleAuthOptions} for the full authentication configuration interface\n */\nexport function ensureAuthScopes(\n authOptions: GoogleAuthOptions,\n requiredScopes: string[]\n): GoogleAuthOptions {\n if (!authOptions) {\n return { scopes: requiredScopes };\n } else if (!authOptions.scopes) {\n return { ...authOptions, scopes: requiredScopes };\n } else {\n const existingScopes = Array.isArray(authOptions.scopes)\n ? authOptions.scopes\n : [authOptions.scopes];\n const missingScopes = requiredScopes.filter(\n (scope) => !existingScopes.includes(scope)\n );\n if (missingScopes.length > 0) {\n throw new ConfigurationError(\n `Invalid auth scopes. Scopes must include ${requiredScopes.join(\", \")}`\n );\n }\n }\n return authOptions;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkJA,IAAa,gBAAb,cAAmC,UAAU;CAC3C,YAAqB;AACnB,SAAO,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;;CAG5D,MAAM,eAAgC;AACpC,MAAI,OAAO,KAAK,gBAAgB,YAC9B,QAAO,KAAK,YAAY;WACf,OAAO,KAAK,eAAe,YACpC,QAAO,KAAK,WAAW,cAAc;MAErC,QAAO,MAAM,cAAc;;;;;;;;;;CAY/B;;;;;;;;;;CAWA;;;;;;;;;;CAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDA,YAAY,SAAwC,EAAE,EAAE;AACtD,SAAO;AADa,OAAA,SAAA;AAGpB,OAAK,SAAS,OAAO,UAAU,uBAAuB,iBAAiB;AACvE,OAAK,cAAc,WAAW;AAC5B,OAAI,OAAO,YACT,QAAO,wBAAwB,OAAO,YAAY;GAEpD,MAAM,iBAAiB,uBAAuB,2BAA2B;AACzE,OAAI,eACF,QAAO,wBAAwB,eAAe;IAGhD;AACF,OAAK,aAAa,WAAW;AAC3B,OAAI,OAAO,kBACT,QAAO,IAAI,WAAW,OAAO,kBAAkB;AAEjD,OAAI,CAAC,KAAK,UAAU,CAAC,KAAK,YACxB,QAAO,IAAI,YAAY;IAGzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CJ,MAAM,MAAM,SAAqC;AAC/C,MAAI,KAAK,OACP,SAAQ,QAAQ,IAAI,uBAAuB,KAAK,OAAO;WAC9C,KAAK,YACd,SAAQ,QAAQ,IACd,oBACA,UAAU,6BAA6B,KAAK,YAAY,GACzD;WACQ,KAAK,WAEd,EADoB,MAAM,KAAK,WAAW,kBAAkB,QAAQ,IAAI,EAC5D,SAAS,OAAO,QAAQ;AAClC,OAAI,UAAU,KACZ,SAAQ,QAAQ,IAAI,KAAK,MAAM;IAEjC;AAEJ,SAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEzB,SAAgB,iBACd,aACA,gBACmB;AACnB,KAAI,CAAC,YACH,QAAO,EAAE,QAAQ,gBAAgB;UACxB,CAAC,YAAY,OACtB,QAAO;EAAE,GAAG;EAAa,QAAQ;EAAgB;MAC5C;EACL,MAAM,iBAAiB,MAAM,QAAQ,YAAY,OAAO,GACpD,YAAY,SACZ,CAAC,YAAY,OAAO;AAIxB,MAHsB,eAAe,QAClC,UAAU,CAAC,eAAe,SAAS,MAAM,CAC3C,CACiB,SAAS,EACzB,OAAM,IAAI,mBACR,4CAA4C,eAAe,KAAK,KAAK,GACtE;;AAGL,QAAO"}
1
+ {"version":3,"file":"node.js","names":[],"sources":["../../src/clients/node.ts"],"sourcesContent":["import { GoogleAuth, type GoogleAuthOptions } from \"google-auth-library\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { GCP_API_KEY_HEADER, GOOGLE_API_KEY_HEADER } from \"../const.js\";\nimport { ApiClient } from \"./index.js\";\nimport {\n GCPCredentials,\n getGCPCredentialsAccessToken,\n normalizeGCPCredentials,\n} from \"../utils/gcp-auth.js\";\nimport { ConfigurationError } from \"../utils/errors.js\";\nimport { iife } from \"../utils/misc.js\";\n\n/**\n * Configuration parameters for the NodeApiClient.\n *\n * This interface defines the authentication options available for Node.js-based\n * API clients. Authentication can be provided via an API key, GCP service\n * account credentials, or advanced Google Auth options. The client will\n * automatically check environment variables if explicit values are not provided.\n *\n * @interface\n */\nexport interface NodeApiClientParams {\n /**\n * Google API key for authentication.\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @see https://cloud.google.com/docs/authentication/api-keys\n */\n apiKey?: string;\n\n /**\n * GCP service account credentials for authentication.\n *\n * Can be provided as either:\n * - A JSON string containing the service account key\n * - A GCPCredentials object\n *\n * If not provided, the client will attempt to read from the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable.\n *\n * @see https://cloud.google.com/iam/docs/creating-managing-service-account-keys\n */\n credentials?: string | GCPCredentials;\n\n /**\n * Google Auth options configuration.\n *\n * Provides fine-grained control over authentication behavior using the\n * google-auth-library. This option enables:\n * - Custom OAuth2 scopes\n * - Application Default Credentials (ADC)\n * - Impersonation and delegation\n * - Custom credential sources\n *\n * When provided, this takes precedence over simple credential-based auth\n * but is lower priority than API key authentication.\n *\n * @see https://github.com/googleapis/google-auth-library-nodejs\n * @see https://cloud.google.com/docs/authentication/application-default-credentials\n */\n googleAuthOptions?: GoogleAuthOptions;\n}\n\n/**\n * Node.js-specific API client for Google services.\n *\n * This client provides comprehensive authentication for Google API requests in\n * Node.js environments, leveraging the full capabilities of the google-auth-library.\n * It supports three authentication methods with the following priority:\n *\n * 1. **API Key Authentication**: Simple key-based authentication suitable for\n * public APIs and development environments.\n *\n * 2. **Service Account Credentials**: OAuth2-based authentication using GCP\n * service account keys. The client automatically handles token generation\n * and caching using a web-compatible implementation.\n *\n * 3. **Google Auth Library**: Advanced authentication using google-auth-library,\n * supporting Application Default Credentials (ADC), custom scopes, and\n * various credential sources.\n *\n * The client follows this authentication priority:\n * 1. Explicit `apiKey` parameter\n * 2. `GOOGLE_API_KEY` environment variable\n * 3. Explicit `credentials` parameter\n * 4. `GOOGLE_CLOUD_CREDENTIALS` environment variable\n * 5. `googleAuthOptions` parameter\n * 6. Application Default Credentials (if googleAuthOptions is provided without explicit credentials)\n *\n * @extends ApiClient\n *\n * @example\n * ```typescript\n * // Using API key\n * const client = new NodeApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // Using service account credentials\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * project_id: \"my-project\",\n * private_key_id: \"key-id\",\n * private_key: \"-----BEGIN PRIVATE KEY-----\\n...\",\n * client_email: \"service-account@project.iam.gserviceaccount.com\",\n * // ... other required fields\n * }\n * });\n *\n * // Using Google Auth with custom scopes\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * keyFilename: '/path/to/service-account-key.json'\n * }\n * });\n *\n * // Using Application Default Credentials\n * const client = new NodeApiClient({\n * googleAuthOptions: {}\n * });\n *\n * // Using environment variables\n * // Set GOOGLE_API_KEY or GOOGLE_CLOUD_CREDENTIALS\n * const client = new NodeApiClient();\n *\n * // Make authenticated requests\n * const request = new Request('https://api.google.com/...');\n * const response = await client.fetch(request);\n * ```\n *\n * @remarks\n * - API keys are added via the `X-Goog-Api-Key` header\n * - Service account tokens are added via the `Authorization` header with Bearer scheme\n * - Google Auth headers are automatically generated and added by google-auth-library\n * - Tokens are automatically cached and refreshed as needed\n * - For web/edge environments without google-auth-library, use WebApiClient instead\n *\n * @see {@link WebApiClient} for web-compatible authentication without google-auth-library\n * @see {@link GCPCredentials} for service account credential structure\n * @see {@link GoogleAuthOptions} for advanced authentication configuration\n */\nexport class NodeApiClient extends ApiClient {\n hasApiKey(): boolean {\n return typeof this.apiKey === \"string\" && this.apiKey !== \"\";\n }\n\n async getProjectId(): Promise<string> {\n if (typeof this.credentials !== \"undefined\") {\n return this.credentials.project_id;\n } else if (typeof this.googleAuth !== \"undefined\") {\n return this.googleAuth.getProjectId();\n } else {\n return super.getProjectId();\n }\n }\n\n /**\n * The Google API key used for authentication, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_API_KEY` environment variable.\n *\n * @protected\n */\n protected apiKey?: string;\n\n /**\n * The normalized GCP service account credentials, if provided.\n *\n * This value is resolved from either the constructor parameter or the\n * `GOOGLE_CLOUD_CREDENTIALS` environment variable, and is normalized\n * to ensure immutability.\n *\n * @protected\n */\n protected credentials?: GCPCredentials;\n\n /**\n * The Google Auth instance for advanced authentication, if configured.\n *\n * This instance is created when `googleAuthOptions` is provided and\n * handles Application Default Credentials, custom scopes, and other\n * advanced authentication scenarios.\n *\n * @protected\n */\n protected googleAuth?: GoogleAuth;\n\n /**\n * Creates a new NodeApiClient instance.\n *\n * The constructor initializes authentication credentials by checking:\n * 1. Explicit parameters passed to the constructor\n * 2. Environment variables (`GOOGLE_API_KEY`, `GOOGLE_CLOUD_CREDENTIALS`)\n * 3. Application Default Credentials (if googleAuthOptions is provided)\n *\n * Credentials are normalized and frozen to prevent accidental modification.\n * The GoogleAuth instance is lazily initialized only when googleAuthOptions\n * is provided.\n *\n * @param params - Configuration parameters for the client. Defaults to an empty object.\n *\n * @example\n * ```typescript\n * // Minimal configuration (uses environment variables or ADC)\n * const client = new NodeApiClient();\n *\n * // With explicit API key\n * const client = new WebApiClient({\n * apiKey: 'your-api-key'\n * });\n *\n * // With service account credentials as JSON string\n * const client = new WebApiClient({\n * credentials: '{\"type\":\"service_account\",...}'\n * });\n *\n * // With service account credentials as object\n * const client = new NodeApiClient({\n * credentials: {\n * type: \"service_account\",\n * // ... other fields\n * }\n * });\n *\n * // With Google Auth options\n * const client = new NodeApiClient({\n * googleAuthOptions: {\n * scopes: ['https://www.googleapis.com/auth/cloud-platform'],\n * projectId: 'my-project'\n * }\n * });\n * ```\n */\n constructor(protected params: NodeApiClientParams = {}) {\n super();\n\n this.apiKey = params.apiKey ?? getEnvironmentVariable(\"GOOGLE_API_KEY\");\n this.credentials = iife(() => {\n if (params.credentials) {\n return normalizeGCPCredentials(params.credentials);\n }\n const credentialsVar = getEnvironmentVariable(\"GOOGLE_CLOUD_CREDENTIALS\");\n if (credentialsVar) {\n return normalizeGCPCredentials(credentialsVar);\n }\n return undefined;\n });\n this.googleAuth = iife(() => {\n if (params.googleAuthOptions) {\n return new GoogleAuth(params.googleAuthOptions);\n }\n if (!this.apiKey && !this.credentials) {\n return new GoogleAuth();\n }\n return undefined;\n });\n }\n\n /**\n * Executes an HTTP request with appropriate Google API authentication.\n *\n * This method adds authentication headers to the request based on the\n * configured authentication method, following this priority:\n *\n * 1. If an API key is configured, adds the `X-Goog-Api-Key` header\n * 2. If service account credentials are configured, generates an access token\n * and adds it via the `Authorization` header with Bearer scheme\n * 3. If Google Auth is configured, uses google-auth-library to generate\n * appropriate authentication headers (may include Authorization, x-goog-user-project, etc.)\n *\n * The authentication priority ensures that explicit API keys take precedence,\n * followed by service account credentials, and finally Google Auth options.\n *\n * @param request - The HTTP request to execute. Headers will be modified\n * in-place to include authentication information.\n * @returns A Promise that resolves to the HTTP response\n *\n * @throws {AuthError} If service account authentication fails (e.g., invalid\n * credentials, network error, or token generation failure)\n * @throws {Error} If Google Auth authentication fails (e.g., missing credentials,\n * invalid configuration, or ADC not available)\n *\n * @example\n * ```typescript\n * const client = new NodeApiClient({ apiKey: 'your-key' });\n * const request = new Request('https://generativelanguage.googleapis.com/v1/models');\n * const response = await client.fetch(request);\n * const data = await response.json();\n * ```\n *\n * @remarks\n * - The request object is modified in-place by adding headers\n * - Service account tokens are automatically cached and refreshed\n * - Google Auth tokens are managed by google-auth-library with automatic refresh\n * - Only one authentication method is used per request (based on priority)\n * - Google Auth may add multiple headers depending on the authentication type\n */\n async fetch(request: Request): Promise<Response> {\n if (this.apiKey) {\n request.headers.set(GOOGLE_API_KEY_HEADER, this.apiKey);\n } else if (this.credentials) {\n request.headers.set(\n GCP_API_KEY_HEADER,\n `Bearer ${getGCPCredentialsAccessToken(this.credentials)}`\n );\n } else if (this.googleAuth) {\n const authHeaders = await this.googleAuth.getRequestHeaders(request.url);\n authHeaders.forEach((value, key) => {\n if (value !== null) {\n request.headers.set(key, value);\n }\n });\n }\n return fetch(request);\n }\n}\n\n/**\n * Ensures that Google Auth options include required OAuth2 scopes.\n *\n * This utility function validates and augments GoogleAuthOptions to ensure\n * that all required OAuth2 scopes are present. It handles three scenarios:\n *\n * 1. **No auth options provided**: Creates new options with required scopes\n * 2. **Auth options without scopes**: Adds required scopes to existing options\n * 3. **Auth options with scopes**: Validates that all required scopes are present\n *\n * This is particularly useful when working with Google APIs that require specific\n * scopes for access, ensuring that authentication is properly configured before\n * making API requests.\n *\n * @param authOptions - The existing Google Auth options to validate/augment.\n * Can be undefined or an empty object.\n * @param requiredScopes - Array of OAuth2 scope URLs that must be present.\n * These are typically Google API scope URLs like\n * 'https://www.googleapis.com/auth/cloud-platform'\n * @returns The validated/augmented GoogleAuthOptions with all required scopes\n *\n * @throws {Error} If authOptions already has scopes but is missing any of the\n * required scopes. This prevents accidental scope reduction that\n * could break API access.\n *\n * @example\n * ```typescript\n * // Create new options with required scopes\n * const options = ensureAuthScopes(undefined, [\n * 'https://www.googleapis.com/auth/cloud-platform'\n * ]);\n * // Result: { scopes: ['https://www.googleapis.com/auth/cloud-platform'] }\n *\n * // Add scopes to existing options\n * const options = ensureAuthScopes(\n * { projectId: 'my-project' },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: { projectId: 'my-project', scopes: [...] }\n *\n * // Validate existing scopes\n * const options = ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/cloud-platform'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * // Result: Original options (validation passed)\n *\n * // Error case - missing required scope\n * try {\n * ensureAuthScopes(\n * { scopes: ['https://www.googleapis.com/auth/userinfo.email'] },\n * ['https://www.googleapis.com/auth/cloud-platform']\n * );\n * } catch (error) {\n * // Error: Invalid auth scopes. Scopes must include ...\n * }\n * ```\n *\n * @remarks\n * - Scopes can be provided as a string or array of strings in GoogleAuthOptions\n * - The function normalizes single-string scopes to arrays for validation\n * - Required scopes are not automatically added if existing scopes are present;\n * instead, an error is thrown to prevent unintended scope changes\n * - This function does not modify the original authOptions object\n *\n * @see https://developers.google.com/identity/protocols/oauth2/scopes\n * @see {@link GoogleAuthOptions} for the full authentication configuration interface\n */\nexport function ensureAuthScopes(\n authOptions: GoogleAuthOptions,\n requiredScopes: string[]\n): GoogleAuthOptions {\n if (!authOptions) {\n return { scopes: requiredScopes };\n } else if (!authOptions.scopes) {\n return { ...authOptions, scopes: requiredScopes };\n } else {\n const existingScopes = Array.isArray(authOptions.scopes)\n ? authOptions.scopes\n : [authOptions.scopes];\n const missingScopes = requiredScopes.filter(\n (scope) => !existingScopes.includes(scope)\n );\n if (missingScopes.length > 0) {\n throw new ConfigurationError(\n `Invalid auth scopes. Scopes must include ${requiredScopes.join(\", \")}`\n );\n }\n }\n return authOptions;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkJA,IAAa,gBAAb,cAAmC,UAAU;CAC3C,YAAqB;AACnB,SAAO,OAAO,KAAK,WAAW,YAAY,KAAK,WAAW;;CAG5D,MAAM,eAAgC;AACpC,MAAI,OAAO,KAAK,gBAAgB,YAC9B,QAAO,KAAK,YAAY;WACf,OAAO,KAAK,eAAe,YACpC,QAAO,KAAK,WAAW,cAAc;MAErC,QAAO,MAAM,cAAc;;;;;;;;;;CAY/B;;;;;;;;;;CAWA;;;;;;;;;;CAWA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgDA,YAAY,SAAwC,EAAE,EAAE;AACtD,SAAO;AADa,OAAA,SAAA;AAGpB,OAAK,SAAS,OAAO,UAAU,uBAAuB,iBAAiB;AACvE,OAAK,cAAc,WAAW;AAC5B,OAAI,OAAO,YACT,QAAO,wBAAwB,OAAO,YAAY;GAEpD,MAAM,iBAAiB,uBAAuB,2BAA2B;AACzE,OAAI,eACF,QAAO,wBAAwB,eAAe;IAGhD;AACF,OAAK,aAAa,WAAW;AAC3B,OAAI,OAAO,kBACT,QAAO,IAAI,WAAW,OAAO,kBAAkB;AAEjD,OAAI,CAAC,KAAK,UAAU,CAAC,KAAK,YACxB,QAAO,IAAI,YAAY;IAGzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0CJ,MAAM,MAAM,SAAqC;AAC/C,MAAI,KAAK,OACP,SAAQ,QAAQ,IAAI,uBAAuB,KAAK,OAAO;WAC9C,KAAK,YACd,SAAQ,QAAQ,IACd,oBACA,UAAU,6BAA6B,KAAK,YAAY,GACzD;WACQ,KAAK,WAEd,EAAA,MAD0B,KAAK,WAAW,kBAAkB,QAAQ,IAAI,EAC5D,SAAS,OAAO,QAAQ;AAClC,OAAI,UAAU,KACZ,SAAQ,QAAQ,IAAI,KAAK,MAAM;IAEjC;AAEJ,SAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwEzB,SAAgB,iBACd,aACA,gBACmB;AACnB,KAAI,CAAC,YACH,QAAO,EAAE,QAAQ,gBAAgB;UACxB,CAAC,YAAY,OACtB,QAAO;EAAE,GAAG;EAAa,QAAQ;EAAgB;MAC5C;EACL,MAAM,iBAAiB,MAAM,QAAQ,YAAY,OAAO,GACpD,YAAY,SACZ,CAAC,YAAY,OAAO;AAIxB,MAHsB,eAAe,QAClC,UAAU,CAAC,eAAe,SAAS,MAAM,CAE3B,CAAC,SAAS,EACzB,OAAM,IAAI,mBACR,4CAA4C,eAAe,KAAK,KAAK,GACtE;;AAGL,QAAO"}
@@ -325,6 +325,18 @@ function convertLegacyContentMessageToGeminiContent(message, messages) {
325
325
  ...etc,
326
326
  functionCall
327
327
  });
328
+ } else if (item?.type === "executableCode") {
329
+ const { type, executableCode, ...etc } = item;
330
+ parts.push({
331
+ ...etc,
332
+ executableCode
333
+ });
334
+ } else if (item?.type === "codeExecutionResult") {
335
+ const { type, codeExecutionResult, ...etc } = item;
336
+ parts.push({
337
+ ...etc,
338
+ codeExecutionResult
339
+ });
328
340
  } else if (isMessageContentImageUrl(item)) parts.push(messageContentImageUrl(item));
329
341
  else if (isMessageContentMedia(item)) parts.push(messageContentMedia(item));
330
342
  else parts.push(item);
@@ -1 +1 @@
1
- {"version":3,"file":"messages.cjs","names":["InvalidInputError","SystemMessage","HumanMessage","AIMessage","ToolMessage","ChatMessage","iife","ToolCallNotFoundError"],"sources":["../../src/converters/messages.ts"],"sourcesContent":["import {\n BaseMessage,\n AIMessage,\n isDataContentBlock,\n ContentBlock,\n parseBase64DataUrl,\n SystemMessage,\n HumanMessage,\n ToolMessage,\n ChatMessage,\n StandardContentBlockConverter,\n convertToProviderContentBlock,\n type Data,\n UsageMetadata,\n InputTokenDetails,\n OutputTokenDetails,\n ModalitiesTokenDetails,\n MessageContentImageUrl,\n MessageContentComplex,\n MessageContentText,\n} from \"@langchain/core/messages\";\nimport { v4 as uuidv4 } from \"@langchain/core/utils/uuid\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { iife } from \"../utils/misc.js\";\nimport { InvalidInputError, ToolCallNotFoundError } from \"../utils/errors.js\";\n\n/**\n * Standard content block converter for Google Gemini API.\n * Converts deprecated Data content blocks to Gemini Part format.\n *\n * @deprecated This converter is for backward compatibility with older message formats.\n * Use ContentBlock.Multimodal.Standard instead.\n */\nexport const geminiContentBlockConverter: StandardContentBlockConverter<{\n text: Gemini.Part;\n image: Gemini.Part;\n audio: Gemini.Part;\n file: Gemini.Part;\n}> = {\n providerName: \"ChatGoogle\",\n\n fromStandardTextBlock(block: Data.StandardTextBlock): Gemini.Part {\n return { text: block.text };\n },\n\n fromStandardImageBlock(block: Data.StandardImageBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 image blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType: parsed.mime_type || block.mime_type || \"image/jpeg\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"image/jpeg\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"image/jpeg\",\n fileUri: block.id,\n },\n };\n }\n }\n\n throw new InvalidInputError(\n `Image content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\".`\n );\n },\n\n fromStandardAudioBlock(block: Data.StandardAudioBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 audio blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType: parsed.mime_type || block.mime_type || \"audio/mpeg\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"audio/mpeg\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"audio/mpeg\",\n fileUri: block.id,\n },\n };\n }\n }\n\n throw new InvalidInputError(\n `Audio content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\".`\n );\n },\n\n fromStandardFileBlock(block: Data.StandardFileBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 file blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType:\n parsed.mime_type ||\n block.mime_type ||\n \"application/octet-stream\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"application/octet-stream\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"application/octet-stream\",\n fileUri: block.id,\n },\n };\n }\n }\n\n // Handle text source type (for plain text file blocks)\n if (\n \"source_type\" in block &&\n block.source_type === \"text\" &&\n \"text\" in block\n ) {\n return {\n text: block.text,\n };\n }\n\n throw new InvalidInputError(\n `File content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\", \"text\".`\n );\n },\n};\n\nfunction convertStandardDataContentBlockToGeminiPart(\n block: ContentBlock.Multimodal.Data\n): Gemini.Part | null {\n function uint8arrayToString(data: Uint8Array): string {\n return btoa(\n Array.from(data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n }\n\n function extractMimeType(str: string): {\n mimeType: string | null;\n data: string | null;\n } {\n if (str.startsWith(\"data:\")) {\n return {\n mimeType: str.split(\":\")[1].split(\";\")[0],\n data: str.split(\",\")[1],\n };\n }\n return {\n mimeType: null,\n data: null,\n };\n }\n\n if (\"mimeType\" in block && \"data\" in block) {\n const mimeType = block.mimeType!;\n const data: string =\n typeof block.data === \"string\"\n ? block.data\n : uint8arrayToString(block.data!);\n return {\n inlineData: {\n mimeType,\n data,\n },\n };\n } else if (\"mimeType\" in block && \"url\" in block) {\n const mimeType = block.mimeType!;\n const fileUri = block.url!;\n return {\n fileData: {\n mimeType,\n fileUri,\n },\n };\n } else if (\"url\" in block && block.url?.startsWith(\"data:\")) {\n const { mimeType, data } = extractMimeType(block.url!);\n if (mimeType && data) {\n return {\n inlineData: {\n mimeType,\n data,\n },\n };\n }\n }\n // FIXME - report this somehow?\n return null;\n}\n\nfunction convertStandardVideoContentBlockToGeminiPart(\n block: ContentBlock.Multimodal.Video\n): Gemini.Part | null {\n const ret: Gemini.Part | null =\n convertStandardDataContentBlockToGeminiPart(block);\n if (ret && block.metadata && \"videoMetadata\" in block.metadata) {\n (ret as Gemini.Part.FileData).videoMetadata = block.metadata.videoMetadata!;\n }\n return ret;\n}\n\n/**\n * Converts a single LangChain standard content block (v1 format)\n * into a Gemini.Part.\n *\n * This is intended to be called from `convertStandardContentMessageToGeminiContent`\n */\nfunction convertStandardContentBlockToGeminiPart(\n block: ContentBlock.Standard\n): Gemini.Part | null {\n switch (block.type) {\n case \"text\":\n return { text: block.text };\n case \"image\":\n case \"audio\":\n case \"text-plain\":\n case \"file\":\n return convertStandardDataContentBlockToGeminiPart(block);\n case \"video\":\n return convertStandardVideoContentBlockToGeminiPart(block);\n default:\n return null;\n }\n}\n\n/**\n * Converts a single LangChain message with standard content blocks (v1 format) to Gemini Content.\n * This handles messages that have `response_metadata.output_version === \"v1\"`.\n *\n * This is intended to be called from `convertMessagesToGeminiContent`\n */\nfunction convertStandardContentMessageToGeminiContent(\n message: BaseMessage,\n messages: BaseMessage[]\n): Gemini.Content | null {\n // Skip system messages - they're handled separately\n if (SystemMessage.isInstance(message)) {\n return null;\n }\n\n let role: Gemini.Role;\n if (HumanMessage.isInstance(message)) {\n role = \"user\";\n } else if (AIMessage.isInstance(message)) {\n role = \"model\";\n } else if (ToolMessage.isInstance(message)) {\n // Tool messages in Gemini were represented as function responses, but now are \"user\"\n role = \"user\";\n } else if (ChatMessage.isInstance(message)) {\n // Map ChatMessage roles to Gemini roles\n const msgRole = message.role.toLowerCase();\n if (msgRole === \"user\" || msgRole === \"human\") {\n role = \"user\";\n } else if (\n msgRole === \"assistant\" ||\n msgRole === \"ai\" ||\n msgRole === \"model\"\n ) {\n role = \"model\";\n } else if (msgRole === \"function\" || msgRole === \"tool\") {\n role = \"user\";\n } else {\n // Default to user for unknown roles\n role = \"user\";\n }\n } else {\n // Unknown message type - default to user\n role = \"user\";\n }\n\n const parts: Gemini.Part[] = [];\n\n // Process standard content blocks\n const contentBlocks = Array.isArray(message.contentBlocks)\n ? message.contentBlocks\n : [];\n contentBlocks.forEach((block: ContentBlock.Standard) => {\n const contentBlock =\n (message.additional_kwargs\n .originalTextContentBlock as ContentBlock.Standard) || block;\n const part: Gemini.Part | null =\n convertStandardContentBlockToGeminiPart(contentBlock);\n if (part) {\n parts.push(part);\n }\n });\n\n // Convert AIMessage tool_calls to functionCall parts\n if (AIMessage.isInstance(message) && message.tool_calls?.length) {\n for (const toolCall of message.tool_calls) {\n parts.push({\n functionCall: {\n name: toolCall.name,\n args: toolCall.args ?? {},\n },\n } as Gemini.Part.FunctionCall);\n }\n }\n\n // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent =\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content);\n // Find the matching tool call in a preceding AIMessage to get the function name\n const aiMsg = messages\n .filter(AIMessage.isInstance)\n .find((msg) =>\n msg.tool_calls?.find((tc) => tc.id === message.tool_call_id)\n );\n const matchedToolCall = aiMsg?.tool_calls?.find(\n (tc) => tc.id === message.tool_call_id\n );\n const isGeneratedId = message.tool_call_id.startsWith(\"lc-tool-call-\");\n parts.push({\n functionResponse: {\n ...(isGeneratedId ? {} : { id: message.tool_call_id }),\n name: matchedToolCall?.name ?? message.name ?? \"unknown\",\n response: { result: responseContent },\n },\n });\n }\n\n // Only return content if we have parts\n if (parts.length === 0) {\n return null;\n }\n\n return { role, parts };\n}\n\n/**\n * Converts a single LangChain message with legacy content blocks (v0 format) to Gemini Content.\n * This handles messages that have `response_metadata.output_version === \"v0\"`.\n *\n * This is intended to be called from `convertMessagesToGeminiContent`\n */\nfunction convertLegacyContentMessageToGeminiContent(\n message: BaseMessage,\n messages: BaseMessage[]\n): Gemini.Content | null {\n // Skip system messages - they're handled separately\n if (SystemMessage.isInstance(message)) {\n return null;\n }\n\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentText(\n content: object\n ): content is MessageContentText {\n return typeof content === \"object\" && content !== null && \"text\" in content;\n }\n\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentImageUrl(\n content: object\n ): content is MessageContentImageUrl {\n return (\n typeof content === \"object\" && content !== null && \"image_url\" in content\n );\n }\n\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentMedia(\n content: object\n ): content is MessageContentComplex {\n return (\n typeof content === \"object\" &&\n content !== null &&\n \"type\" in content &&\n content.type === \"media\"\n );\n }\n\n /**\n * Infers the MIME type from a URL based on its file extension.\n * This is used as a fallback when the MIME type is not provided.\n *\n * @param url - The URL to infer the MIME type from\n * @returns The inferred MIME type or a generic type if it cannot be determined\n */\n function inferMimeTypeFromUrl(url: string): string {\n const mimeTypeMap: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n };\n\n try {\n // Extract the pathname from the URL\n const pathname = new URL(url).pathname;\n // Get the file extension (handle query params and fragments)\n const extension = pathname\n .split(\".\")\n .pop()\n ?.toLowerCase()\n .split(/[?#]/)[0];\n return extension ? mimeTypeMap[extension] : \"application/octet-stream\";\n } catch {\n // If URL parsing fails, try a simple extension extraction\n const match = url.match(/\\.([a-zA-Z0-9]+)(?:[?#]|$)/);\n if (match) {\n const extension = match[1].toLowerCase();\n return mimeTypeMap[extension];\n }\n return \"application/octet-stream\";\n }\n }\n\n function messageContentImageUrlData(\n content: MessageContentImageUrl\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const url: string =\n typeof content.image_url === \"string\"\n ? content.image_url\n : content.image_url.url;\n if (!url) {\n throw new InvalidInputError(\n \"Missing image URL in image_url content block.\"\n );\n }\n\n const dataUrl = parseBase64DataUrl({ dataUrl: url });\n if (dataUrl?.data && dataUrl?.mime_type) {\n return {\n inlineData: {\n data: dataUrl.data,\n mimeType: dataUrl.mime_type,\n },\n };\n } else {\n // Infer MIME type from URL extension\n const mimeType = inferMimeTypeFromUrl(url) || \"image/png\";\n return {\n fileData: {\n mimeType,\n fileUri: url,\n },\n };\n }\n }\n\n function messageContentImageUrl(\n content: MessageContentImageUrl\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const ret = messageContentImageUrlData(content);\n supplementVideoMetadata(content, ret);\n return ret;\n }\n\n function messageContentMediaData(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: Record<string, any>\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n if (\"mimeType\" in content && \"data\" in content) {\n return {\n inlineData: {\n mimeType: content.mimeType,\n data: content.data,\n },\n };\n } else if (\"mimeType\" in content && \"fileUri\" in content) {\n return {\n fileData: {\n mimeType: content.mimeType,\n fileUri: content.fileUri,\n },\n };\n } else {\n // The old version would attempt to read the URL, but we're\n // not in an async function, so we can't do that.\n }\n\n throw new InvalidInputError(\n `Invalid media content: ${JSON.stringify(\n content,\n null,\n 1\n )}. Expected either { mimeType, data } for inline data or { mimeType, fileUri } for file references.`\n );\n }\n\n function supplementVideoMetadata(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: MessageContentImageUrl | Record<string, any>,\n ret: Gemini.Part.InlineData | Gemini.Part.FileData\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n // Add videoMetadata if defined\n if (\"videoMetadata\" in content && typeof ret === \"object\") {\n ret.videoMetadata = content.videoMetadata;\n }\n return ret;\n }\n\n function messageContentMedia(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: Record<string, any>\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const ret = messageContentMediaData(content);\n supplementVideoMetadata(content, ret);\n return ret;\n }\n\n const role: Gemini.Role = iife(() => {\n if (HumanMessage.isInstance(message)) {\n return \"user\";\n } else if (AIMessage.isInstance(message)) {\n return \"model\";\n } else if (ToolMessage.isInstance(message)) {\n // Tool messages in Gemini were represented as function responses, but now are \"user\"\n return \"user\";\n } else if (ChatMessage.isInstance(message)) {\n // Map ChatMessage roles to Gemini roles\n const msgRole = message.role.toLowerCase();\n if (msgRole === \"user\" || msgRole === \"human\") {\n return \"user\";\n } else if (\n msgRole === \"assistant\" ||\n msgRole === \"ai\" ||\n msgRole === \"model\"\n ) {\n return \"model\";\n } else if (msgRole === \"function\" || msgRole === \"tool\") {\n return \"user\";\n } else {\n // Default to user for unknown roles\n return \"user\";\n }\n } else {\n // Unknown message type - skip or default to user\n return \"user\";\n }\n });\n\n let parts: Gemini.Part[] = [];\n\n // Handle legacy content formats\n if (typeof message.content === \"string\") {\n // Simple string content\n if (message.content.trim()) {\n parts.push({ text: message.content });\n }\n } else if (Array.isArray(message.content)) {\n // Array of content blocks (legacy format)\n for (const item of message.content) {\n if (typeof item === \"string\") {\n parts.push({ text: item });\n } else if (typeof item === \"object\" && item !== null) {\n if (isMessageContentText(item)) {\n parts.push({ text: item.text });\n } else if (isDataContentBlock(item)) {\n parts.push(\n convertToProviderContentBlock(item, geminiContentBlockConverter)\n );\n } else if (item?.type === \"functionCall\") {\n const { type, functionCall, ...etc } = item;\n parts.push({\n ...etc,\n functionCall,\n } as Gemini.Part.FunctionCall);\n } else if (isMessageContentImageUrl(item)) {\n parts.push(messageContentImageUrl(item));\n } else if (isMessageContentMedia(item)) {\n parts.push(messageContentMedia(item));\n } else {\n parts.push(item as Gemini.Part);\n }\n }\n }\n }\n\n // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent =\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content);\n // Find the matching tool call in a preceding AIMessage to get the function name\n const aiMsg = messages\n .filter(AIMessage.isInstance)\n .find((msg) =>\n msg.tool_calls?.find((tc) => tc.id === message.tool_call_id)\n );\n if (!aiMsg) {\n throw new ToolCallNotFoundError(message.tool_call_id);\n }\n const isGeneratedId = message.tool_call_id.startsWith(\"lc-tool-call-\");\n const matchedToolCall = aiMsg.tool_calls?.find(\n (tc) => tc.id === message.tool_call_id\n );\n parts.push({\n functionResponse: {\n ...(isGeneratedId ? {} : { id: message.tool_call_id }),\n name: matchedToolCall?.name ?? message.name ?? \"unknown\",\n response: { result: responseContent },\n },\n });\n\n // For tool messages, only keep functionResponse parts since the text content\n // is already included in the functionResponse.response.result\n parts = parts.filter((part) => \"functionResponse\" in part);\n }\n\n // Only add content if we have parts\n if (parts.length > 0) {\n return { role, parts };\n }\n\n return null;\n}\n\n/**\n * Converts an array of LangChain messages to Gemini API content format.\n *\n * This converter transforms LangChain's message types into the content structure\n * expected by Google's Gemini API.\n *\n * @remarks\n * The converter processes messages in two distinct ways based on their format:\n *\n * **V1 Standard Content Format**:\n * Messages with `output_version: \"v1\"` in their response metadata use the\n * standardized content block format. These are processed through\n * `convertStandardContentMessageToGeminiContent`, which extracts text blocks\n * from the `contentBlocks` array and maps message roles appropriately.\n *\n * **Google Format**:\n * Messages without the v1 marker are processed using generic logic that handles:\n * - String content: Simple text strings are converted to text parts\n * - Array content: Arrays of strings or content blocks are processed individually\n * - Tool messages: Converted to function response parts with tool call metadata\n *\n * @example\n * ```typescript\n * // Simple conversation with text messages\n * const messages = [\n * new HumanMessage(\"What is the weather?\"),\n * new AIMessage(\"I'll check that for you.\")\n * ];\n * const contents = convertMessagesToGeminiContents(messages);\n * // Returns: [\n * // { role: \"user\", parts: [{ text: \"What is the weather?\" }] },\n * // { role: \"model\", parts: [{ text: \"I'll check that for you.\" }] }\n * // ]\n * ```\n */\nexport const convertMessagesToGeminiContents: Converter<\n BaseMessage[],\n Gemini.GenerateContentRequest[\"contents\"]\n> = (messages) => {\n const contents: Gemini.Content[] = [];\n\n for (const message of messages) {\n // const content: Gemini.Content | null = convertContentMessageToGeminiContent(message, messages);\n const content: Gemini.Content | null = iife(() => {\n const outputVersion =\n \"output_version\" in message.response_metadata\n ? (message.response_metadata?.output_version as string)\n : \"v0\";\n switch (outputVersion) {\n case \"v1\":\n return convertStandardContentMessageToGeminiContent(\n message,\n messages\n );\n case \"v0\":\n default:\n return convertLegacyContentMessageToGeminiContent(message, messages);\n }\n });\n if (content) {\n const prev = contents[contents.length - 1];\n if (prev && prev.role === content.role) {\n prev.parts.push(...content.parts);\n } else {\n contents.push(content);\n }\n }\n }\n\n return contents;\n};\n\n/**\n * Converts LangChain system messages to Gemini API system instruction format.\n *\n * This converter extracts system messages from a message array and transforms them\n * into the format expected by Google's Gemini API for system instructions. System\n * instructions provide context and guidelines that influence the model's behavior\n * throughout the conversation.\n *\n * @remarks\n * The converter handles two distinct message formats:\n *\n * 1. **V1 Standard Content Format**: Messages with `output_version: \"v1\"` in their\n * response metadata use the standardized content block format. Only text blocks\n * are extracted from the `contentBlocks` array.\n *\n * 2. **Legacy Google Format**: Messages without the v1 marker use the legacy format\n * where content can be:\n * - A simple string (trimmed and converted to a text part)\n * - An array of strings or objects with `type: \"text\"` and a `text` property\n *\n * The converter filters out non-SystemMessage instances and only processes text\n * content, ignoring other content types like images or tool calls that may be\n * present in the message array.\n *\n * @example\n * ```typescript\n * // Simple string system message\n * const messages = [\n * new SystemMessage(\"You are a helpful assistant.\")\n * ];\n * const instruction = convertMessagesToGeminiSystemInstruction(messages);\n * // Returns: { parts: [{ text: \"You are a helpful assistant.\" }] }\n * ```\n *\n * @param messages - Array of LangChain messages to extract system instructions from\n * @returns A Gemini system instruction object with text parts, or undefined if no\n * system messages with text content are found\n */\nexport const convertMessagesToGeminiSystemInstruction: Converter<\n BaseMessage[],\n Gemini.GenerateContentRequest[\"systemInstruction\"]\n> = (messages) => {\n const systemParts: Gemini.Part[] = [];\n\n for (const message of messages) {\n if (SystemMessage.isInstance(message)) {\n if (message.text.trim()) {\n systemParts.push({ text: message.text });\n }\n }\n }\n\n if (systemParts.length === 0) {\n return undefined;\n }\n\n return { parts: systemParts };\n};\n\n/**\n * Converts an array of Gemini API parts into an array of LangChain tool calls.\n *\n * This function iterates through a given array of `Gemini.Part` objects,\n * identifies parts that represent a function call, and transforms them into\n * LangChain's standardized `ToolCall` format. Each resulting tool call is\n * assigned a unique, sequential ID.\n *\n * @param parts - An array of `Gemini.Part` objects, which may or may not\n * contain function calls.\n * @returns An array of `ContentBlock.Tools.ToolCall` objects. If no function\n * call parts are found, an empty array is returned.\n *\n * @remarks\n * The `id` for each tool call is generated sequentially in the format `call_0`,\n * `call_1`, and so on, based on its order of appearance in the input array.\n *\n * @example\n * ```typescript\n * const geminiParts: Gemini.Part[] = [\n * { text: \"Thinking about what to do...\" },\n * {\n * functionCall: {\n * name: \"search_web\",\n * args: { query: \"latest AI news\" }\n * }\n * },\n * {\n * functionCall: {\n * name: \"calculator\",\n * args: { expression: \"2 + 2\" }\n * }\n * }\n * ];\n *\n * const toolCalls = convertGeminiPartsToToolCalls(geminiParts);\n * // toolCalls will be:\n * // [\n * // { type: \"tool_call\", id: \"call_0\", name: \"search_web\", args: { query: \"latest AI news\" } },\n * // { type: \"tool_call\", id: \"call_1\", name: \"calculator\", args: { expression: \"2 + 2\" } }\n * // ]\n * ```\n */\nexport const convertGeminiPartsToToolCalls: Converter<\n Gemini.Part[],\n ContentBlock.Tools.ToolCall<string, Record<string, unknown>>[]\n> = (\n parts: Gemini.Part[]\n): ContentBlock.Tools.ToolCall<string, Record<string, unknown>>[] => {\n const toolCalls: ContentBlock.Tools.ToolCall<\n string,\n Record<string, unknown>\n >[] = [];\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n if (\"functionCall\" in part && part.functionCall) {\n const functionCallPart = part as Gemini.Part.FunctionCall;\n toolCalls.push({\n type: \"tool_call\",\n id:\n functionCallPart.functionCall.id ??\n `lc-tool-call-${uuidv4().replace(/-/g, \"\")}`,\n name: functionCallPart.functionCall.name,\n args: functionCallPart.functionCall.args ?? {},\n thoughtSignature: functionCallPart.thoughtSignature,\n });\n }\n }\n\n return toolCalls;\n};\n\nexport const convertGeminiPartToContentBlock: Converter<\n Gemini.Part,\n ContentBlock\n> = (part: Gemini.Part): ContentBlock => {\n const block: ContentBlock = iife(() => {\n if (\"text\" in part && typeof part.text === \"string\") {\n return {\n type: \"text\",\n text: part.text,\n };\n } else if (\"inlineData\" in part && part.inlineData) {\n return {\n type: \"inlineData\",\n inlineData: part.inlineData,\n };\n } else if (\"fileData\" in part && part.fileData) {\n return {\n type: \"fileData\",\n fileData: part.fileData,\n };\n } else if (\"functionCall\" in part && part.functionCall) {\n return {\n type: \"functionCall\",\n functionCall: part.functionCall,\n };\n } else if (\"functionResponse\" in part && part.functionResponse) {\n return {\n type: \"functionResponse\",\n functionResponse: part.functionResponse,\n };\n } else if (\"executableCode\" in part && part.executableCode) {\n return {\n type: \"executableCode\",\n executableCode: part.executableCode,\n };\n } else if (\"codeExecutionResult\" in part && part.codeExecutionResult) {\n return {\n type: \"codeExecutionResult\",\n codeExecutionResult: part.codeExecutionResult,\n };\n }\n return part as unknown as ContentBlock;\n });\n const ret: ContentBlock = {\n thought: part.thought,\n thoughtSignature: part.thoughtSignature,\n partMetadata: part.partMetadata,\n ...block,\n };\n for (const attribute in ret) {\n if (ret[attribute] === undefined) {\n delete ret[attribute];\n }\n }\n\n return ret;\n};\n\n/**\n * Converts a Gemini API candidate response to a LangChain AIMessage.\n *\n * This converter transforms the response from Google's Gemini API into a standardized\n * AIMessage format that can be used throughout the LangChain ecosystem. It handles\n * various types of content including text, inline data, file data, function calls,\n * and code execution results.\n *\n * @remarks\n * The converter performs the following transformations:\n * - Extracts tool calls from function call parts and assigns sequential IDs\n * - Converts single text responses to string format for simplicity\n * - Converts multi-part responses to ContentBlock array format\n * - Preserves metadata including finish reason, safety ratings, and citations\n * - Handles various Gemini part types (text, inlineData, fileData, functionCall, etc.)\n *\n * @example\n * ```typescript\n * const candidate: Gemini.Candidate = {\n * content: {\n * parts: [\n * { text: \"Hello, how can I help you?\" }\n * ]\n * },\n * finishReason: \"STOP\",\n * safetyRatings: [...]\n * };\n *\n * const aiMessage = convertGeminiCandidateToAIMessage(candidate);\n * // Returns: AIMessage with content \"Hello, how can I help you?\"\n * ```\n *\n * @param candidate - The Gemini API candidate response containing content parts and metadata\n * @returns An AIMessage instance with converted content, tool calls, and metadata\n */\nexport const convertGeminiCandidateToAIMessage: Converter<\n Gemini.Candidate,\n AIMessage\n> = (candidate) => {\n function groundingSupportByPart(\n groundingSupports?: Gemini.GroundingSupport[]\n ): Gemini.GroundingSupport[][] {\n const ret: Gemini.GroundingSupport[][] = [];\n\n if (!groundingSupports || groundingSupports.length === 0) {\n return [[]];\n }\n\n groundingSupports?.forEach((groundingSupport) => {\n const segment = groundingSupport?.segment;\n const partIndex = segment?.partIndex ?? 0;\n if (ret[partIndex]) {\n ret[partIndex].push(groundingSupport);\n } else {\n ret[partIndex] = [groundingSupport];\n }\n });\n\n return ret;\n }\n\n const parts = candidate.content?.parts || [];\n\n // Extract tool calls from function call parts\n const toolCalls = convertGeminiPartsToToolCalls(parts);\n\n // Convert parts to content format that the translator understands\n // Format: array of objects with type field matching the part type\n let content: string | ContentBlock[];\n let originalTextContentBlock: ContentBlock | undefined = undefined;\n\n const groundingMetadata = candidate?.groundingMetadata;\n const citationMetadata = candidate?.citationMetadata;\n const groundingParts = groundingSupportByPart(\n groundingMetadata?.groundingSupports\n );\n\n // Determine if a part carries actual content vs. just metadata.\n // Metadata-only parts (e.g. empty text with only thoughtSignature) should not\n // prevent collapsing a single-text response into a string.\n const hasContentPayload = (p: Gemini.Part): boolean =>\n !!p.text ||\n !!p.thought ||\n !!p.inlineData ||\n !!p.fileData ||\n !!p.functionCall ||\n !!p.functionResponse ||\n !!p.executableCode ||\n !!p.codeExecutionResult;\n\n const contentParts = parts.filter(hasContentPayload);\n\n if (contentParts.length === 0) {\n // No content-bearing parts (may still have metadata-only parts)\n content = \"\";\n if (parts.length > 0) {\n originalTextContentBlock = convertGeminiPartToContentBlock(parts[0]);\n }\n } else if (\n contentParts.length === 1 &&\n \"text\" in contentParts[0] &&\n !(\"thought\" in contentParts[0])\n ) {\n // Single text content part (possibly alongside metadata-only parts like\n // thoughtSignature) - store as string\n content = contentParts[0].text ?? \"\";\n // Merge metadata from any metadata-only parts into the content block\n const mergedPart: Gemini.Part = { ...contentParts[0] };\n for (const p of parts) {\n if (p !== contentParts[0] && p.thoughtSignature) {\n mergedPart.thoughtSignature = p.thoughtSignature;\n }\n }\n originalTextContentBlock = convertGeminiPartToContentBlock(mergedPart);\n } else {\n // Multiple content parts - convert to array format with type fields\n content = parts.map((p: Gemini.Part) => convertGeminiPartToContentBlock(p));\n }\n\n const additional_kwargs: Record<string, unknown> = {\n finishReason: candidate.finishReason,\n finishMessage: candidate.finishMessage,\n safetyRatings: candidate.safetyRatings,\n tokenCount: candidate.tokenCount,\n citationMetadata: candidate.citationMetadata,\n groundingMetadata: candidate.groundingMetadata,\n groundingAttributions: candidate.groundingAttributions,\n urlRetrievalMetadata: candidate.urlRetrievalMetadata,\n urlContextMetadata: candidate.urlContextMetadata,\n avgLogprobs: candidate.avgLogprobs,\n logprobsResult: candidate.logprobsResult,\n originalTextContentBlock,\n };\n\n const response_metadata: Record<string, unknown> = {\n model_provider: \"google\",\n finish_reason: candidate.finishReason,\n safety_ratings: candidate.safetyRatings,\n citation_metadata: candidate.citationMetadata,\n grounding_metadata: candidate.groundingMetadata,\n grounding_attributions: candidate.groundingAttributions,\n url_retrieval_metadata: candidate.urlRetrievalMetadata,\n url_context_metadata: candidate.urlContextMetadata,\n avg_logprobs: candidate.avgLogprobs,\n logprobs_result: candidate.logprobsResult,\n\n // Backwards compatibility\n citationMetadata,\n groundingMetadata,\n groundingSupport: groundingParts[0],\n };\n // Remove any undefined properties, so we don't include them\n for (const attribute in additional_kwargs) {\n if (additional_kwargs[attribute] === undefined) {\n delete additional_kwargs[attribute];\n }\n }\n for (const attribute in response_metadata) {\n if (response_metadata[attribute] === undefined) {\n delete response_metadata[attribute];\n }\n }\n\n return new AIMessage({\n content,\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n additional_kwargs,\n response_metadata,\n });\n};\n\nexport const convertAIMessageToText: Converter<AIMessage, string> = (\n message: AIMessage\n): string => {\n if (typeof message.content === \"string\") {\n return message.content;\n } else if (Array.isArray(message.content)) {\n return message.content\n .filter(\n (c) => typeof c === \"string\" || (c as { type?: string }).type === \"text\"\n )\n .map((c) =>\n typeof c === \"string\" ? c : (c as { text?: string }).text || \"\"\n )\n .join(\"\");\n } else {\n return \"\";\n }\n};\n\nexport const convertGeminiGenerateContentResponseToUsageMetadata: Converter<\n Gemini.GenerateContentResponse,\n UsageMetadata\n> = (data: Gemini.GenerateContentResponse): UsageMetadata => {\n function addModalityCounts(\n modalityTokenCounts: Gemini.ModalityTokenCount[] | undefined,\n details: InputTokenDetails | OutputTokenDetails\n ): void {\n modalityTokenCounts?.forEach((modalityTokenCount) => {\n const { modality, tokenCount } = modalityTokenCount;\n if (\n typeof modality === \"undefined\" ||\n typeof tokenCount === \"undefined\"\n ) {\n return;\n }\n const modalityLc: keyof ModalitiesTokenDetails =\n modality.toLowerCase() as keyof ModalitiesTokenDetails;\n const currentCount = details[modalityLc] ?? 0;\n details[modalityLc] = currentCount + tokenCount;\n });\n }\n\n const usageMetadata: Gemini.UsageMetadata | undefined = data.usageMetadata;\n\n const inputTokenCount = usageMetadata?.promptTokenCount ?? 0;\n const candidatesTokenCount = usageMetadata?.candidatesTokenCount ?? 0;\n const thoughtsTokenCount = usageMetadata?.thoughtsTokenCount ?? 0;\n const outputTokenCount = candidatesTokenCount + thoughtsTokenCount;\n const totalTokens =\n usageMetadata?.totalTokenCount ?? inputTokenCount + outputTokenCount;\n\n const input_token_details: InputTokenDetails = {};\n addModalityCounts(usageMetadata?.promptTokensDetails, input_token_details);\n input_token_details.cache_read = usageMetadata?.cachedContentTokenCount ?? 0;\n\n const output_token_details: OutputTokenDetails = {};\n addModalityCounts(\n usageMetadata?.candidatesTokensDetails,\n output_token_details\n );\n output_token_details.reasoning = usageMetadata?.thoughtsTokenCount ?? 0;\n\n return {\n input_tokens: inputTokenCount,\n output_tokens: outputTokenCount,\n total_tokens: totalTokens,\n input_token_details,\n output_token_details,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"messages.cjs","names":["InvalidInputError","SystemMessage","HumanMessage","AIMessage","ToolMessage","ChatMessage","iife","ToolCallNotFoundError"],"sources":["../../src/converters/messages.ts"],"sourcesContent":["import {\n BaseMessage,\n AIMessage,\n isDataContentBlock,\n ContentBlock,\n parseBase64DataUrl,\n SystemMessage,\n HumanMessage,\n ToolMessage,\n ChatMessage,\n StandardContentBlockConverter,\n convertToProviderContentBlock,\n type Data,\n UsageMetadata,\n InputTokenDetails,\n OutputTokenDetails,\n ModalitiesTokenDetails,\n MessageContentImageUrl,\n MessageContentComplex,\n MessageContentText,\n} from \"@langchain/core/messages\";\nimport { v4 as uuidv4 } from \"@langchain/core/utils/uuid\";\nimport { Converter } from \"@langchain/core/utils/format\";\nimport type { Gemini } from \"../chat_models/types.js\";\nimport { iife } from \"../utils/misc.js\";\nimport { InvalidInputError, ToolCallNotFoundError } from \"../utils/errors.js\";\n\n/**\n * Standard content block converter for Google Gemini API.\n * Converts deprecated Data content blocks to Gemini Part format.\n *\n * @deprecated This converter is for backward compatibility with older message formats.\n * Use ContentBlock.Multimodal.Standard instead.\n */\nexport const geminiContentBlockConverter: StandardContentBlockConverter<{\n text: Gemini.Part;\n image: Gemini.Part;\n audio: Gemini.Part;\n file: Gemini.Part;\n}> = {\n providerName: \"ChatGoogle\",\n\n fromStandardTextBlock(block: Data.StandardTextBlock): Gemini.Part {\n return { text: block.text };\n },\n\n fromStandardImageBlock(block: Data.StandardImageBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 image blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType: parsed.mime_type || block.mime_type || \"image/jpeg\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"image/jpeg\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"image/jpeg\",\n fileUri: block.id,\n },\n };\n }\n }\n\n throw new InvalidInputError(\n `Image content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\".`\n );\n },\n\n fromStandardAudioBlock(block: Data.StandardAudioBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 audio blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType: parsed.mime_type || block.mime_type || \"audio/mpeg\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"audio/mpeg\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"audio/mpeg\",\n fileUri: block.id,\n },\n };\n }\n }\n\n throw new InvalidInputError(\n `Audio content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\".`\n );\n },\n\n fromStandardFileBlock(block: Data.StandardFileBlock): Gemini.Part {\n if (isDataContentBlock(block)) {\n if (block.source_type === \"base64\") {\n if (!block.mime_type) {\n throw new InvalidInputError(\n \"mime_type is required for base64 file blocks\"\n );\n }\n const dataStr =\n typeof block.data === \"string\"\n ? block.data\n : typeof block.data === \"object\" && block.data !== null\n ? // Convert Uint8Array to base64 string\n btoa(\n Array.from(block.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n )\n : String(block.data);\n return {\n inlineData: {\n mimeType: block.mime_type,\n data: dataStr,\n },\n };\n }\n\n if (block.source_type === \"url\") {\n // Check if it's a data URL\n const parsed = parseBase64DataUrl({ dataUrl: block.url });\n if (parsed) {\n const dataStr =\n typeof parsed.data === \"string\"\n ? parsed.data\n : // Convert Uint8Array to base64 string\n btoa(\n Array.from(parsed.data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n return {\n inlineData: {\n mimeType:\n parsed.mime_type ||\n block.mime_type ||\n \"application/octet-stream\",\n data: dataStr,\n },\n };\n }\n // Otherwise, treat as file URI\n return {\n fileData: {\n mimeType: block.mime_type || \"application/octet-stream\",\n fileUri: block.url,\n },\n };\n }\n\n if (block.source_type === \"id\") {\n return {\n fileData: {\n mimeType: block.mime_type || \"application/octet-stream\",\n fileUri: block.id,\n },\n };\n }\n }\n\n // Handle text source type (for plain text file blocks)\n if (\n \"source_type\" in block &&\n block.source_type === \"text\" &&\n \"text\" in block\n ) {\n return {\n text: block.text,\n };\n }\n\n throw new InvalidInputError(\n `File content blocks with source_type \"${\n (block as Data.DataContentBlock).source_type\n }\" are not supported for ChatGoogle. Supported source types are: \"base64\", \"url\", \"id\", \"text\".`\n );\n },\n};\n\nfunction convertStandardDataContentBlockToGeminiPart(\n block: ContentBlock.Multimodal.Data\n): Gemini.Part | null {\n function uint8arrayToString(data: Uint8Array): string {\n return btoa(\n Array.from(data as Uint8Array)\n .map((byte) => String.fromCharCode(byte))\n .join(\"\")\n );\n }\n\n function extractMimeType(str: string): {\n mimeType: string | null;\n data: string | null;\n } {\n if (str.startsWith(\"data:\")) {\n return {\n mimeType: str.split(\":\")[1].split(\";\")[0],\n data: str.split(\",\")[1],\n };\n }\n return {\n mimeType: null,\n data: null,\n };\n }\n\n if (\"mimeType\" in block && \"data\" in block) {\n const mimeType = block.mimeType!;\n const data: string =\n typeof block.data === \"string\"\n ? block.data\n : uint8arrayToString(block.data!);\n return {\n inlineData: {\n mimeType,\n data,\n },\n };\n } else if (\"mimeType\" in block && \"url\" in block) {\n const mimeType = block.mimeType!;\n const fileUri = block.url!;\n return {\n fileData: {\n mimeType,\n fileUri,\n },\n };\n } else if (\"url\" in block && block.url?.startsWith(\"data:\")) {\n const { mimeType, data } = extractMimeType(block.url!);\n if (mimeType && data) {\n return {\n inlineData: {\n mimeType,\n data,\n },\n };\n }\n }\n // FIXME - report this somehow?\n return null;\n}\n\nfunction convertStandardVideoContentBlockToGeminiPart(\n block: ContentBlock.Multimodal.Video\n): Gemini.Part | null {\n const ret: Gemini.Part | null =\n convertStandardDataContentBlockToGeminiPart(block);\n if (ret && block.metadata && \"videoMetadata\" in block.metadata) {\n (ret as Gemini.Part.FileData).videoMetadata = block.metadata.videoMetadata!;\n }\n return ret;\n}\n\n/**\n * Converts a single LangChain standard content block (v1 format)\n * into a Gemini.Part.\n *\n * This is intended to be called from `convertStandardContentMessageToGeminiContent`\n */\nfunction convertStandardContentBlockToGeminiPart(\n block: ContentBlock.Standard\n): Gemini.Part | null {\n switch (block.type) {\n case \"text\":\n return { text: block.text };\n case \"image\":\n case \"audio\":\n case \"text-plain\":\n case \"file\":\n return convertStandardDataContentBlockToGeminiPart(block);\n case \"video\":\n return convertStandardVideoContentBlockToGeminiPart(block);\n default:\n return null;\n }\n}\n\n/**\n * Converts a single LangChain message with standard content blocks (v1 format) to Gemini Content.\n * This handles messages that have `response_metadata.output_version === \"v1\"`.\n *\n * This is intended to be called from `convertMessagesToGeminiContent`\n */\nfunction convertStandardContentMessageToGeminiContent(\n message: BaseMessage,\n messages: BaseMessage[]\n): Gemini.Content | null {\n // Skip system messages - they're handled separately\n if (SystemMessage.isInstance(message)) {\n return null;\n }\n\n let role: Gemini.Role;\n if (HumanMessage.isInstance(message)) {\n role = \"user\";\n } else if (AIMessage.isInstance(message)) {\n role = \"model\";\n } else if (ToolMessage.isInstance(message)) {\n // Tool messages in Gemini were represented as function responses, but now are \"user\"\n role = \"user\";\n } else if (ChatMessage.isInstance(message)) {\n // Map ChatMessage roles to Gemini roles\n const msgRole = message.role.toLowerCase();\n if (msgRole === \"user\" || msgRole === \"human\") {\n role = \"user\";\n } else if (\n msgRole === \"assistant\" ||\n msgRole === \"ai\" ||\n msgRole === \"model\"\n ) {\n role = \"model\";\n } else if (msgRole === \"function\" || msgRole === \"tool\") {\n role = \"user\";\n } else {\n // Default to user for unknown roles\n role = \"user\";\n }\n } else {\n // Unknown message type - default to user\n role = \"user\";\n }\n\n const parts: Gemini.Part[] = [];\n\n // Process standard content blocks\n const contentBlocks = Array.isArray(message.contentBlocks)\n ? message.contentBlocks\n : [];\n contentBlocks.forEach((block: ContentBlock.Standard) => {\n const contentBlock =\n (message.additional_kwargs\n .originalTextContentBlock as ContentBlock.Standard) || block;\n const part: Gemini.Part | null =\n convertStandardContentBlockToGeminiPart(contentBlock);\n if (part) {\n parts.push(part);\n }\n });\n\n // Convert AIMessage tool_calls to functionCall parts\n if (AIMessage.isInstance(message) && message.tool_calls?.length) {\n for (const toolCall of message.tool_calls) {\n parts.push({\n functionCall: {\n name: toolCall.name,\n args: toolCall.args ?? {},\n },\n } as Gemini.Part.FunctionCall);\n }\n }\n\n // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent =\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content);\n // Find the matching tool call in a preceding AIMessage to get the function name\n const aiMsg = messages\n .filter(AIMessage.isInstance)\n .find((msg) =>\n msg.tool_calls?.find((tc) => tc.id === message.tool_call_id)\n );\n const matchedToolCall = aiMsg?.tool_calls?.find(\n (tc) => tc.id === message.tool_call_id\n );\n const isGeneratedId = message.tool_call_id.startsWith(\"lc-tool-call-\");\n parts.push({\n functionResponse: {\n ...(isGeneratedId ? {} : { id: message.tool_call_id }),\n name: matchedToolCall?.name ?? message.name ?? \"unknown\",\n response: { result: responseContent },\n },\n });\n }\n\n // Only return content if we have parts\n if (parts.length === 0) {\n return null;\n }\n\n return { role, parts };\n}\n\n/**\n * Converts a single LangChain message with legacy content blocks (v0 format) to Gemini Content.\n * This handles messages that have `response_metadata.output_version === \"v0\"`.\n *\n * This is intended to be called from `convertMessagesToGeminiContent`\n */\nfunction convertLegacyContentMessageToGeminiContent(\n message: BaseMessage,\n messages: BaseMessage[]\n): Gemini.Content | null {\n // Skip system messages - they're handled separately\n if (SystemMessage.isInstance(message)) {\n return null;\n }\n\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentText(\n content: object\n ): content is MessageContentText {\n return typeof content === \"object\" && content !== null && \"text\" in content;\n }\n\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentImageUrl(\n content: object\n ): content is MessageContentImageUrl {\n return (\n typeof content === \"object\" && content !== null && \"image_url\" in content\n );\n }\n\n /**\n * @deprecated - This is for use by `convertLegacyContentMessageToGeminiContent` only\n */\n function isMessageContentMedia(\n content: object\n ): content is MessageContentComplex {\n return (\n typeof content === \"object\" &&\n content !== null &&\n \"type\" in content &&\n content.type === \"media\"\n );\n }\n\n /**\n * Infers the MIME type from a URL based on its file extension.\n * This is used as a fallback when the MIME type is not provided.\n *\n * @param url - The URL to infer the MIME type from\n * @returns The inferred MIME type or a generic type if it cannot be determined\n */\n function inferMimeTypeFromUrl(url: string): string {\n const mimeTypeMap: Record<string, string> = {\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n png: \"image/png\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n bmp: \"image/bmp\",\n svg: \"image/svg+xml\",\n ico: \"image/x-icon\",\n tiff: \"image/tiff\",\n tif: \"image/tiff\",\n };\n\n try {\n // Extract the pathname from the URL\n const pathname = new URL(url).pathname;\n // Get the file extension (handle query params and fragments)\n const extension = pathname\n .split(\".\")\n .pop()\n ?.toLowerCase()\n .split(/[?#]/)[0];\n return extension ? mimeTypeMap[extension] : \"application/octet-stream\";\n } catch {\n // If URL parsing fails, try a simple extension extraction\n const match = url.match(/\\.([a-zA-Z0-9]+)(?:[?#]|$)/);\n if (match) {\n const extension = match[1].toLowerCase();\n return mimeTypeMap[extension];\n }\n return \"application/octet-stream\";\n }\n }\n\n function messageContentImageUrlData(\n content: MessageContentImageUrl\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const url: string =\n typeof content.image_url === \"string\"\n ? content.image_url\n : content.image_url.url;\n if (!url) {\n throw new InvalidInputError(\n \"Missing image URL in image_url content block.\"\n );\n }\n\n const dataUrl = parseBase64DataUrl({ dataUrl: url });\n if (dataUrl?.data && dataUrl?.mime_type) {\n return {\n inlineData: {\n data: dataUrl.data,\n mimeType: dataUrl.mime_type,\n },\n };\n } else {\n // Infer MIME type from URL extension\n const mimeType = inferMimeTypeFromUrl(url) || \"image/png\";\n return {\n fileData: {\n mimeType,\n fileUri: url,\n },\n };\n }\n }\n\n function messageContentImageUrl(\n content: MessageContentImageUrl\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const ret = messageContentImageUrlData(content);\n supplementVideoMetadata(content, ret);\n return ret;\n }\n\n function messageContentMediaData(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: Record<string, any>\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n if (\"mimeType\" in content && \"data\" in content) {\n return {\n inlineData: {\n mimeType: content.mimeType,\n data: content.data,\n },\n };\n } else if (\"mimeType\" in content && \"fileUri\" in content) {\n return {\n fileData: {\n mimeType: content.mimeType,\n fileUri: content.fileUri,\n },\n };\n } else {\n // The old version would attempt to read the URL, but we're\n // not in an async function, so we can't do that.\n }\n\n throw new InvalidInputError(\n `Invalid media content: ${JSON.stringify(\n content,\n null,\n 1\n )}. Expected either { mimeType, data } for inline data or { mimeType, fileUri } for file references.`\n );\n }\n\n function supplementVideoMetadata(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: MessageContentImageUrl | Record<string, any>,\n ret: Gemini.Part.InlineData | Gemini.Part.FileData\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n // Add videoMetadata if defined\n if (\"videoMetadata\" in content && typeof ret === \"object\") {\n ret.videoMetadata = content.videoMetadata;\n }\n return ret;\n }\n\n function messageContentMedia(\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n content: Record<string, any>\n ): Gemini.Part.InlineData | Gemini.Part.FileData {\n const ret = messageContentMediaData(content);\n supplementVideoMetadata(content, ret);\n return ret;\n }\n\n const role: Gemini.Role = iife(() => {\n if (HumanMessage.isInstance(message)) {\n return \"user\";\n } else if (AIMessage.isInstance(message)) {\n return \"model\";\n } else if (ToolMessage.isInstance(message)) {\n // Tool messages in Gemini were represented as function responses, but now are \"user\"\n return \"user\";\n } else if (ChatMessage.isInstance(message)) {\n // Map ChatMessage roles to Gemini roles\n const msgRole = message.role.toLowerCase();\n if (msgRole === \"user\" || msgRole === \"human\") {\n return \"user\";\n } else if (\n msgRole === \"assistant\" ||\n msgRole === \"ai\" ||\n msgRole === \"model\"\n ) {\n return \"model\";\n } else if (msgRole === \"function\" || msgRole === \"tool\") {\n return \"user\";\n } else {\n // Default to user for unknown roles\n return \"user\";\n }\n } else {\n // Unknown message type - skip or default to user\n return \"user\";\n }\n });\n\n let parts: Gemini.Part[] = [];\n\n // Handle legacy content formats\n if (typeof message.content === \"string\") {\n // Simple string content\n if (message.content.trim()) {\n parts.push({ text: message.content });\n }\n } else if (Array.isArray(message.content)) {\n // Array of content blocks (legacy format)\n for (const item of message.content) {\n if (typeof item === \"string\") {\n parts.push({ text: item });\n } else if (typeof item === \"object\" && item !== null) {\n if (isMessageContentText(item)) {\n parts.push({ text: item.text });\n } else if (isDataContentBlock(item)) {\n parts.push(\n convertToProviderContentBlock(item, geminiContentBlockConverter)\n );\n } else if (item?.type === \"functionCall\") {\n const { type, functionCall, ...etc } = item;\n parts.push({\n ...etc,\n functionCall,\n } as Gemini.Part.FunctionCall);\n } else if (item?.type === \"executableCode\") {\n const { type, executableCode, ...etc } = item;\n parts.push({\n ...etc,\n executableCode,\n } as Gemini.Part.ExecutableCode);\n } else if (item?.type === \"codeExecutionResult\") {\n const { type, codeExecutionResult, ...etc } = item;\n parts.push({\n ...etc,\n codeExecutionResult,\n } as Gemini.Part.CodeExecutionResult);\n } else if (isMessageContentImageUrl(item)) {\n parts.push(messageContentImageUrl(item));\n } else if (isMessageContentMedia(item)) {\n parts.push(messageContentMedia(item));\n } else {\n parts.push(item as Gemini.Part);\n }\n }\n }\n }\n\n // Handle tool messages as function responses\n if (ToolMessage.isInstance(message) && message.tool_call_id) {\n const responseContent =\n typeof message.content === \"string\"\n ? message.content\n : JSON.stringify(message.content);\n // Find the matching tool call in a preceding AIMessage to get the function name\n const aiMsg = messages\n .filter(AIMessage.isInstance)\n .find((msg) =>\n msg.tool_calls?.find((tc) => tc.id === message.tool_call_id)\n );\n if (!aiMsg) {\n throw new ToolCallNotFoundError(message.tool_call_id);\n }\n const isGeneratedId = message.tool_call_id.startsWith(\"lc-tool-call-\");\n const matchedToolCall = aiMsg.tool_calls?.find(\n (tc) => tc.id === message.tool_call_id\n );\n parts.push({\n functionResponse: {\n ...(isGeneratedId ? {} : { id: message.tool_call_id }),\n name: matchedToolCall?.name ?? message.name ?? \"unknown\",\n response: { result: responseContent },\n },\n });\n\n // For tool messages, only keep functionResponse parts since the text content\n // is already included in the functionResponse.response.result\n parts = parts.filter((part) => \"functionResponse\" in part);\n }\n\n // Only add content if we have parts\n if (parts.length > 0) {\n return { role, parts };\n }\n\n return null;\n}\n\n/**\n * Converts an array of LangChain messages to Gemini API content format.\n *\n * This converter transforms LangChain's message types into the content structure\n * expected by Google's Gemini API.\n *\n * @remarks\n * The converter processes messages in two distinct ways based on their format:\n *\n * **V1 Standard Content Format**:\n * Messages with `output_version: \"v1\"` in their response metadata use the\n * standardized content block format. These are processed through\n * `convertStandardContentMessageToGeminiContent`, which extracts text blocks\n * from the `contentBlocks` array and maps message roles appropriately.\n *\n * **Google Format**:\n * Messages without the v1 marker are processed using generic logic that handles:\n * - String content: Simple text strings are converted to text parts\n * - Array content: Arrays of strings or content blocks are processed individually\n * - Tool messages: Converted to function response parts with tool call metadata\n *\n * @example\n * ```typescript\n * // Simple conversation with text messages\n * const messages = [\n * new HumanMessage(\"What is the weather?\"),\n * new AIMessage(\"I'll check that for you.\")\n * ];\n * const contents = convertMessagesToGeminiContents(messages);\n * // Returns: [\n * // { role: \"user\", parts: [{ text: \"What is the weather?\" }] },\n * // { role: \"model\", parts: [{ text: \"I'll check that for you.\" }] }\n * // ]\n * ```\n */\nexport const convertMessagesToGeminiContents: Converter<\n BaseMessage[],\n Gemini.GenerateContentRequest[\"contents\"]\n> = (messages) => {\n const contents: Gemini.Content[] = [];\n\n for (const message of messages) {\n // const content: Gemini.Content | null = convertContentMessageToGeminiContent(message, messages);\n const content: Gemini.Content | null = iife(() => {\n const outputVersion =\n \"output_version\" in message.response_metadata\n ? (message.response_metadata?.output_version as string)\n : \"v0\";\n switch (outputVersion) {\n case \"v1\":\n return convertStandardContentMessageToGeminiContent(\n message,\n messages\n );\n case \"v0\":\n default:\n return convertLegacyContentMessageToGeminiContent(message, messages);\n }\n });\n if (content) {\n const prev = contents[contents.length - 1];\n if (prev && prev.role === content.role) {\n prev.parts.push(...content.parts);\n } else {\n contents.push(content);\n }\n }\n }\n\n return contents;\n};\n\n/**\n * Converts LangChain system messages to Gemini API system instruction format.\n *\n * This converter extracts system messages from a message array and transforms them\n * into the format expected by Google's Gemini API for system instructions. System\n * instructions provide context and guidelines that influence the model's behavior\n * throughout the conversation.\n *\n * @remarks\n * The converter handles two distinct message formats:\n *\n * 1. **V1 Standard Content Format**: Messages with `output_version: \"v1\"` in their\n * response metadata use the standardized content block format. Only text blocks\n * are extracted from the `contentBlocks` array.\n *\n * 2. **Legacy Google Format**: Messages without the v1 marker use the legacy format\n * where content can be:\n * - A simple string (trimmed and converted to a text part)\n * - An array of strings or objects with `type: \"text\"` and a `text` property\n *\n * The converter filters out non-SystemMessage instances and only processes text\n * content, ignoring other content types like images or tool calls that may be\n * present in the message array.\n *\n * @example\n * ```typescript\n * // Simple string system message\n * const messages = [\n * new SystemMessage(\"You are a helpful assistant.\")\n * ];\n * const instruction = convertMessagesToGeminiSystemInstruction(messages);\n * // Returns: { parts: [{ text: \"You are a helpful assistant.\" }] }\n * ```\n *\n * @param messages - Array of LangChain messages to extract system instructions from\n * @returns A Gemini system instruction object with text parts, or undefined if no\n * system messages with text content are found\n */\nexport const convertMessagesToGeminiSystemInstruction: Converter<\n BaseMessage[],\n Gemini.GenerateContentRequest[\"systemInstruction\"]\n> = (messages) => {\n const systemParts: Gemini.Part[] = [];\n\n for (const message of messages) {\n if (SystemMessage.isInstance(message)) {\n if (message.text.trim()) {\n systemParts.push({ text: message.text });\n }\n }\n }\n\n if (systemParts.length === 0) {\n return undefined;\n }\n\n return { parts: systemParts };\n};\n\n/**\n * Converts an array of Gemini API parts into an array of LangChain tool calls.\n *\n * This function iterates through a given array of `Gemini.Part` objects,\n * identifies parts that represent a function call, and transforms them into\n * LangChain's standardized `ToolCall` format. Each resulting tool call is\n * assigned a unique, sequential ID.\n *\n * @param parts - An array of `Gemini.Part` objects, which may or may not\n * contain function calls.\n * @returns An array of `ContentBlock.Tools.ToolCall` objects. If no function\n * call parts are found, an empty array is returned.\n *\n * @remarks\n * The `id` for each tool call is generated sequentially in the format `call_0`,\n * `call_1`, and so on, based on its order of appearance in the input array.\n *\n * @example\n * ```typescript\n * const geminiParts: Gemini.Part[] = [\n * { text: \"Thinking about what to do...\" },\n * {\n * functionCall: {\n * name: \"search_web\",\n * args: { query: \"latest AI news\" }\n * }\n * },\n * {\n * functionCall: {\n * name: \"calculator\",\n * args: { expression: \"2 + 2\" }\n * }\n * }\n * ];\n *\n * const toolCalls = convertGeminiPartsToToolCalls(geminiParts);\n * // toolCalls will be:\n * // [\n * // { type: \"tool_call\", id: \"call_0\", name: \"search_web\", args: { query: \"latest AI news\" } },\n * // { type: \"tool_call\", id: \"call_1\", name: \"calculator\", args: { expression: \"2 + 2\" } }\n * // ]\n * ```\n */\nexport const convertGeminiPartsToToolCalls: Converter<\n Gemini.Part[],\n ContentBlock.Tools.ToolCall<string, Record<string, unknown>>[]\n> = (\n parts: Gemini.Part[]\n): ContentBlock.Tools.ToolCall<string, Record<string, unknown>>[] => {\n const toolCalls: ContentBlock.Tools.ToolCall<\n string,\n Record<string, unknown>\n >[] = [];\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n if (\"functionCall\" in part && part.functionCall) {\n const functionCallPart = part as Gemini.Part.FunctionCall;\n toolCalls.push({\n type: \"tool_call\",\n id:\n functionCallPart.functionCall.id ??\n `lc-tool-call-${uuidv4().replace(/-/g, \"\")}`,\n name: functionCallPart.functionCall.name,\n args: functionCallPart.functionCall.args ?? {},\n thoughtSignature: functionCallPart.thoughtSignature,\n });\n }\n }\n\n return toolCalls;\n};\n\nexport const convertGeminiPartToContentBlock: Converter<\n Gemini.Part,\n ContentBlock\n> = (part: Gemini.Part): ContentBlock => {\n const block: ContentBlock = iife(() => {\n if (\"text\" in part && typeof part.text === \"string\") {\n return {\n type: \"text\",\n text: part.text,\n };\n } else if (\"inlineData\" in part && part.inlineData) {\n return {\n type: \"inlineData\",\n inlineData: part.inlineData,\n };\n } else if (\"fileData\" in part && part.fileData) {\n return {\n type: \"fileData\",\n fileData: part.fileData,\n };\n } else if (\"functionCall\" in part && part.functionCall) {\n return {\n type: \"functionCall\",\n functionCall: part.functionCall,\n };\n } else if (\"functionResponse\" in part && part.functionResponse) {\n return {\n type: \"functionResponse\",\n functionResponse: part.functionResponse,\n };\n } else if (\"executableCode\" in part && part.executableCode) {\n return {\n type: \"executableCode\",\n executableCode: part.executableCode,\n };\n } else if (\"codeExecutionResult\" in part && part.codeExecutionResult) {\n return {\n type: \"codeExecutionResult\",\n codeExecutionResult: part.codeExecutionResult,\n };\n }\n return part as unknown as ContentBlock;\n });\n const ret: ContentBlock = {\n thought: part.thought,\n thoughtSignature: part.thoughtSignature,\n partMetadata: part.partMetadata,\n ...block,\n };\n for (const attribute in ret) {\n if (ret[attribute] === undefined) {\n delete ret[attribute];\n }\n }\n\n return ret;\n};\n\n/**\n * Converts a Gemini API candidate response to a LangChain AIMessage.\n *\n * This converter transforms the response from Google's Gemini API into a standardized\n * AIMessage format that can be used throughout the LangChain ecosystem. It handles\n * various types of content including text, inline data, file data, function calls,\n * and code execution results.\n *\n * @remarks\n * The converter performs the following transformations:\n * - Extracts tool calls from function call parts and assigns sequential IDs\n * - Converts single text responses to string format for simplicity\n * - Converts multi-part responses to ContentBlock array format\n * - Preserves metadata including finish reason, safety ratings, and citations\n * - Handles various Gemini part types (text, inlineData, fileData, functionCall, etc.)\n *\n * @example\n * ```typescript\n * const candidate: Gemini.Candidate = {\n * content: {\n * parts: [\n * { text: \"Hello, how can I help you?\" }\n * ]\n * },\n * finishReason: \"STOP\",\n * safetyRatings: [...]\n * };\n *\n * const aiMessage = convertGeminiCandidateToAIMessage(candidate);\n * // Returns: AIMessage with content \"Hello, how can I help you?\"\n * ```\n *\n * @param candidate - The Gemini API candidate response containing content parts and metadata\n * @returns An AIMessage instance with converted content, tool calls, and metadata\n */\nexport const convertGeminiCandidateToAIMessage: Converter<\n Gemini.Candidate,\n AIMessage\n> = (candidate) => {\n function groundingSupportByPart(\n groundingSupports?: Gemini.GroundingSupport[]\n ): Gemini.GroundingSupport[][] {\n const ret: Gemini.GroundingSupport[][] = [];\n\n if (!groundingSupports || groundingSupports.length === 0) {\n return [[]];\n }\n\n groundingSupports?.forEach((groundingSupport) => {\n const segment = groundingSupport?.segment;\n const partIndex = segment?.partIndex ?? 0;\n if (ret[partIndex]) {\n ret[partIndex].push(groundingSupport);\n } else {\n ret[partIndex] = [groundingSupport];\n }\n });\n\n return ret;\n }\n\n const parts = candidate.content?.parts || [];\n\n // Extract tool calls from function call parts\n const toolCalls = convertGeminiPartsToToolCalls(parts);\n\n // Convert parts to content format that the translator understands\n // Format: array of objects with type field matching the part type\n let content: string | ContentBlock[];\n let originalTextContentBlock: ContentBlock | undefined = undefined;\n\n const groundingMetadata = candidate?.groundingMetadata;\n const citationMetadata = candidate?.citationMetadata;\n const groundingParts = groundingSupportByPart(\n groundingMetadata?.groundingSupports\n );\n\n // Determine if a part carries actual content vs. just metadata.\n // Metadata-only parts (e.g. empty text with only thoughtSignature) should not\n // prevent collapsing a single-text response into a string.\n const hasContentPayload = (p: Gemini.Part): boolean =>\n !!p.text ||\n !!p.thought ||\n !!p.inlineData ||\n !!p.fileData ||\n !!p.functionCall ||\n !!p.functionResponse ||\n !!p.executableCode ||\n !!p.codeExecutionResult;\n\n const contentParts = parts.filter(hasContentPayload);\n\n if (contentParts.length === 0) {\n // No content-bearing parts (may still have metadata-only parts)\n content = \"\";\n if (parts.length > 0) {\n originalTextContentBlock = convertGeminiPartToContentBlock(parts[0]);\n }\n } else if (\n contentParts.length === 1 &&\n \"text\" in contentParts[0] &&\n !(\"thought\" in contentParts[0])\n ) {\n // Single text content part (possibly alongside metadata-only parts like\n // thoughtSignature) - store as string\n content = contentParts[0].text ?? \"\";\n // Merge metadata from any metadata-only parts into the content block\n const mergedPart: Gemini.Part = { ...contentParts[0] };\n for (const p of parts) {\n if (p !== contentParts[0] && p.thoughtSignature) {\n mergedPart.thoughtSignature = p.thoughtSignature;\n }\n }\n originalTextContentBlock = convertGeminiPartToContentBlock(mergedPart);\n } else {\n // Multiple content parts - convert to array format with type fields\n content = parts.map((p: Gemini.Part) => convertGeminiPartToContentBlock(p));\n }\n\n const additional_kwargs: Record<string, unknown> = {\n finishReason: candidate.finishReason,\n finishMessage: candidate.finishMessage,\n safetyRatings: candidate.safetyRatings,\n tokenCount: candidate.tokenCount,\n citationMetadata: candidate.citationMetadata,\n groundingMetadata: candidate.groundingMetadata,\n groundingAttributions: candidate.groundingAttributions,\n urlRetrievalMetadata: candidate.urlRetrievalMetadata,\n urlContextMetadata: candidate.urlContextMetadata,\n avgLogprobs: candidate.avgLogprobs,\n logprobsResult: candidate.logprobsResult,\n originalTextContentBlock,\n };\n\n const response_metadata: Record<string, unknown> = {\n model_provider: \"google\",\n finish_reason: candidate.finishReason,\n safety_ratings: candidate.safetyRatings,\n citation_metadata: candidate.citationMetadata,\n grounding_metadata: candidate.groundingMetadata,\n grounding_attributions: candidate.groundingAttributions,\n url_retrieval_metadata: candidate.urlRetrievalMetadata,\n url_context_metadata: candidate.urlContextMetadata,\n avg_logprobs: candidate.avgLogprobs,\n logprobs_result: candidate.logprobsResult,\n\n // Backwards compatibility\n citationMetadata,\n groundingMetadata,\n groundingSupport: groundingParts[0],\n };\n // Remove any undefined properties, so we don't include them\n for (const attribute in additional_kwargs) {\n if (additional_kwargs[attribute] === undefined) {\n delete additional_kwargs[attribute];\n }\n }\n for (const attribute in response_metadata) {\n if (response_metadata[attribute] === undefined) {\n delete response_metadata[attribute];\n }\n }\n\n return new AIMessage({\n content,\n tool_calls: toolCalls.length > 0 ? toolCalls : undefined,\n additional_kwargs,\n response_metadata,\n });\n};\n\nexport const convertAIMessageToText: Converter<AIMessage, string> = (\n message: AIMessage\n): string => {\n if (typeof message.content === \"string\") {\n return message.content;\n } else if (Array.isArray(message.content)) {\n return message.content\n .filter(\n (c) => typeof c === \"string\" || (c as { type?: string }).type === \"text\"\n )\n .map((c) =>\n typeof c === \"string\" ? c : (c as { text?: string }).text || \"\"\n )\n .join(\"\");\n } else {\n return \"\";\n }\n};\n\nexport const convertGeminiGenerateContentResponseToUsageMetadata: Converter<\n Gemini.GenerateContentResponse,\n UsageMetadata\n> = (data: Gemini.GenerateContentResponse): UsageMetadata => {\n function addModalityCounts(\n modalityTokenCounts: Gemini.ModalityTokenCount[] | undefined,\n details: InputTokenDetails | OutputTokenDetails\n ): void {\n modalityTokenCounts?.forEach((modalityTokenCount) => {\n const { modality, tokenCount } = modalityTokenCount;\n if (\n typeof modality === \"undefined\" ||\n typeof tokenCount === \"undefined\"\n ) {\n return;\n }\n const modalityLc: keyof ModalitiesTokenDetails =\n modality.toLowerCase() as keyof ModalitiesTokenDetails;\n const currentCount = details[modalityLc] ?? 0;\n details[modalityLc] = currentCount + tokenCount;\n });\n }\n\n const usageMetadata: Gemini.UsageMetadata | undefined = data.usageMetadata;\n\n const inputTokenCount = usageMetadata?.promptTokenCount ?? 0;\n const candidatesTokenCount = usageMetadata?.candidatesTokenCount ?? 0;\n const thoughtsTokenCount = usageMetadata?.thoughtsTokenCount ?? 0;\n const outputTokenCount = candidatesTokenCount + thoughtsTokenCount;\n const totalTokens =\n usageMetadata?.totalTokenCount ?? inputTokenCount + outputTokenCount;\n\n const input_token_details: InputTokenDetails = {};\n addModalityCounts(usageMetadata?.promptTokensDetails, input_token_details);\n input_token_details.cache_read = usageMetadata?.cachedContentTokenCount ?? 0;\n\n const output_token_details: OutputTokenDetails = {};\n addModalityCounts(\n usageMetadata?.candidatesTokensDetails,\n output_token_details\n );\n output_token_details.reasoning = usageMetadata?.thoughtsTokenCount ?? 0;\n\n return {\n input_tokens: inputTokenCount,\n output_tokens: outputTokenCount,\n total_tokens: totalTokens,\n input_token_details,\n output_token_details,\n };\n};\n"],"mappings":"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"}
@@ -325,6 +325,18 @@ function convertLegacyContentMessageToGeminiContent(message, messages) {
325
325
  ...etc,
326
326
  functionCall
327
327
  });
328
+ } else if (item?.type === "executableCode") {
329
+ const { type, executableCode, ...etc } = item;
330
+ parts.push({
331
+ ...etc,
332
+ executableCode
333
+ });
334
+ } else if (item?.type === "codeExecutionResult") {
335
+ const { type, codeExecutionResult, ...etc } = item;
336
+ parts.push({
337
+ ...etc,
338
+ codeExecutionResult
339
+ });
328
340
  } else if (isMessageContentImageUrl(item)) parts.push(messageContentImageUrl(item));
329
341
  else if (isMessageContentMedia(item)) parts.push(messageContentMedia(item));
330
342
  else parts.push(item);