{"version":3,"sources":["../../src/googleai/utils.ts"],"sourcesContent":["/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { GenerateRequest, GenkitError, z, type ActionContext } from 'genkit';\nimport process from 'process';\nimport { ClientOptions, VeoImage, VeoVideo } from './types.mjs';\n\nexport { isObject } from '../common/types.mjs';\nexport {\n  appendToWavData,\n  camelToSnakeCase,\n  checkModelName,\n  cleanSchema,\n  convertObjectKeysToSnakeCase,\n  extractText,\n  extractVersion,\n  isStringArray,\n  modelName,\n  stripLeadingId3Tags,\n  toSnakeCaseObj,\n} from '../common/utils.mjs';\n\n/**\n * Retrieves an API key from environment variables.\n *\n * @returns The API key as a string, or `undefined` if none of the specified\n *          environment variables are set.\n */\nexport function getApiKeyFromEnvVar(): string | undefined {\n  return (\n    process.env.GEMINI_API_KEY ||\n    process.env.GOOGLE_API_KEY ||\n    process.env.GOOGLE_GENAI_API_KEY\n  );\n}\n\nexport const MISSING_API_KEY_ERROR = new GenkitError({\n  status: 'FAILED_PRECONDITION',\n  message:\n    'Please pass in the API key or set the GEMINI_API_KEY or GOOGLE_API_KEY environment variable.\\n' +\n    'For more details see https://genkit.dev/docs/plugins/google-genai/',\n});\n\nexport const API_KEY_FALSE_ERROR = new GenkitError({\n  status: 'INVALID_ARGUMENT',\n  message:\n    'GoogleAI plugin was initialized with {apiKey: false} but no apiKey configuration was passed at call time.',\n});\n\n/**\n * Checks and retrieves an API key based on the provided argument and environment variables.\n *\n * - If `pluginApiKey` is a non-empty string, it's used as the API key.\n * - If `pluginApiKey` is `undefined` or an empty string, it attempts to fetch the API key from environment\n * - If `pluginApiKey` is `false`, key retrieval from the environment is skipped, and the function\n *   will return `undefined`. This mode indicates that the API key is expected to be provided\n *   at a later stage or in a different context.\n *\n * @param pluginApiKey - An optional API key string, `undefined` to check the environment, or `false` to bypass all checks in this function.\n * @returns The resolved API key as a string, or `undefined` if `pluginApiKey` is `false`.\n * @throws {Error} MISSING_API_KEY_ERROR - Thrown if `pluginApiKey` is not `false` and no API key\n *   can be found either in the `pluginApiKey` argument or from the environment.\n */\nexport function checkApiKey(\n  pluginApiKey: string | false | undefined\n): string | undefined {\n  let apiKey: string | undefined;\n\n  // Don't get the key from the environment if pluginApiKey is false\n  if (pluginApiKey !== false) {\n    apiKey = pluginApiKey || getApiKeyFromEnvVar();\n  }\n\n  // If pluginApiKey is false, then we don't throw because we are waiting for\n  // the apiKey passed into the individual call\n  if (pluginApiKey !== false && !apiKey) {\n    throw MISSING_API_KEY_ERROR;\n  }\n  return apiKey;\n}\n\n/**\n * Calculates and returns the effective API key based on multiple potential sources.\n * The order of precedence for determining the API key is:\n * 1. `requestApiKey` (if provided)\n * 2. `pluginApiKey` (if provided and not `false`)\n * 3. Environment variable (if `pluginApiKey` is not `false` and `pluginApiKey` is not provided)\n *\n * @param pluginApiKey - The apiKey value provided during plugin initialization.\n * @param requestApiKey - The apiKey provided to an individual generate call.\n * @returns The resolved API key as a string.\n * @throws {Error} API_KEY_FALSE_ERROR - Thrown if `pluginApiKey` is `false` and `requestApiKey` is not provided\n * @throws {Error} MISSING_API_KEY_ERROR - Thrown if no API key can be resolved from any source\n */\nexport function calculateApiKey(\n  pluginApiKey: string | false | undefined,\n  requestApiKey: string | undefined\n): string | undefined {\n  let apiKey: string | undefined;\n\n  // Don't get the key from the environment if pluginApiKey is false\n  if (pluginApiKey !== false) {\n    apiKey = pluginApiKey || getApiKeyFromEnvVar();\n  }\n\n  apiKey = requestApiKey || apiKey;\n\n  if (pluginApiKey === false && !requestApiKey) {\n    return undefined;\n  }\n\n  if (!apiKey) {\n    throw MISSING_API_KEY_ERROR;\n  }\n  return apiKey;\n}\n\nexport function extractVeoImage(\n  request: GenerateRequest\n): VeoImage | undefined {\n  const media = request.messages.at(-1)?.content.find((p) => !!p.media)?.media;\n  if (!media?.contentType?.startsWith('image/')) {\n    return undefined;\n  }\n  const bytes = media?.url.split(',')[1];\n  if (bytes) {\n    return {\n      bytesBase64Encoded: bytes,\n      mimeType: media.contentType,\n    };\n  }\n  return undefined;\n}\n\nexport function extractVeoVideo(\n  request: GenerateRequest\n): VeoVideo | undefined {\n  const media = request.messages.at(-1)?.content.find((p) => !!p.media)?.media;\n  if (!media?.contentType?.startsWith('video/')) {\n    return undefined;\n  }\n  return {\n    uri: media.url,\n  };\n}\n\n/**\n * For each field in ClientOptions, if the request config object has\n * a matching non-empty/non-null field, it overrides the original.\n */\nexport function calculateRequestOptions<T extends z.ZodObject<any, any, any>>(\n  clientOptions: ClientOptions,\n  reqConfig?: z.infer<T>\n): ClientOptions {\n  if (!reqConfig) {\n    return clientOptions;\n  }\n\n  let newOptions = { ...clientOptions };\n\n  if (typeof reqConfig.timeout == 'number') {\n    newOptions.timeout = reqConfig.timeout;\n  }\n\n  if (typeof reqConfig.apiKey == 'string') {\n    newOptions.apiKey = reqConfig.apiKey;\n  }\n\n  if (typeof reqConfig.apiVersion == 'string') {\n    newOptions.apiVersion = reqConfig.apiVersion;\n  }\n\n  if (typeof reqConfig.apiClient == 'string') {\n    newOptions.apiClient = reqConfig.apiClient;\n  }\n\n  if (typeof reqConfig.baseUrl == 'string') {\n    newOptions.baseUrl = reqConfig.baseUrl;\n  }\n\n  if (reqConfig.customHeaders && typeof reqConfig.customHeaders === 'object') {\n    newOptions.customHeaders = reqConfig.customHeaders;\n  }\n\n  return newOptions;\n}\n\n/**\n * The config can have client option overrides, but they should not\n * be sent with the request like normal config.\n * @param requestConfig\n */\nexport function removeClientOptionOverrides<\n  T extends z.ZodObject<any, any, any>,\n>(requestConfig?: z.infer<T>): z.infer<T> {\n  let newConfig = { ...requestConfig };\n\n  delete newConfig?.timeout;\n  delete newConfig?.apiKey;\n  delete newConfig?.apiVersion;\n  delete newConfig?.apiClient;\n  delete newConfig?.baseUrl;\n  delete newConfig?.customHeaders;\n\n  return newConfig;\n}\n\n/**\n * Applies connection-related overrides carried in the action run context\n * (e.g. `options.context` on `start`/`check`/`cancel` for background models)\n * on top of the given client options. This is the mechanism by which callers\n * can supply per-call overrides for calls (like\n * `checkOperation`/`cancelOperation`) that don't have access to the original\n * request's `config`.\n *\n * The API key is read from `context.secrets.apiKey` (scrubbed from traces).\n * All other overrides (e.g. `baseUrl`, `apiVersion`) are read from\n * `context.config`, which is where `checkOperation`/`cancelOperation` fold\n * their top-level `config` option. This lets callers write\n * `ai.checkOperation(op, { config: { baseUrl } })`.\n *\n * All context-derived values take precedence over whatever is already in\n * `clientOptions`.\n *\n * @param clientOptions The base client options to apply overrides on top of.\n * @param context The action run context (`options.context`), if any.\n */\nexport function applyContextOverrides(\n  clientOptions: ClientOptions,\n  context?: ActionContext\n): ClientOptions {\n  if (!context) {\n    return clientOptions;\n  }\n\n  let newOptions = { ...clientOptions };\n\n  if (typeof context.secrets?.apiKey === 'string') {\n    newOptions.apiKey = context.secrets.apiKey;\n  }\n\n  // All non-secret overrides (e.g. baseUrl, apiVersion) are carried in\n  // `context.config` (from checkOperation/cancelOperation's top-level\n  // `config`).\n  return calculateRequestOptions(newOptions, context.config);\n}\n"],"mappings":"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","names":[]}