@warlock.js/ai-google 5.31.0 → 5.32.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.
package/cjs/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["InvalidRequestError","AIError","ProviderTimeoutError","ProviderAuthError","ProviderRateLimitError","ContextLengthExceededError","InvalidRequestError","ProviderOverloadedError","ProviderError","ApiError","LOG_MODULE","log","LOG_MODULE","log","ContentFilterError","ProviderError","LOG_MODULE","log","ContentFilterError","ProviderError","InvalidRequestError","log","InvalidRequestError","GoogleGenAI"],"sources":["../../../../../../../packages/ai-google/src/utils/apply-google-usage.ts","../../../../../../../packages/ai-google/src/utils/map-finish-reason.ts","../../../../../../../packages/ai-google/src/utils/to-google-contents.ts","../../../../../../../packages/ai-google/src/utils/to-google-tools.ts","../../../../../../../packages/ai-google/src/utils/wrap-google-error.ts","../../../../../../../packages/ai-google/src/embedder.ts","../../../../../../../packages/ai-google/src/gemini-image.ts","../../../../../../../packages/ai-google/src/image.ts","../../../../../../../packages/ai-google/src/known-pricing.ts","../../../../../../../packages/ai-google/src/provider-options.ts","../../../../../../../packages/ai-google/src/known-vision-models.ts","../../../../../../../packages/ai-google/src/model.ts","../../../../../../../packages/ai-google/src/sdk.ts"],"sourcesContent":["import type { Usage } from \"@warlock.js/ai\";\nimport type { GenerateContentResponse } from \"@google/genai\";\n\n/** Gemini's per-response token accounting block, as the SDK types it. */\nexport type GoogleUsageMetadata = NonNullable<GenerateContentResponse[\"usageMetadata\"]>;\n\n/**\n * Fold a Gemini `usageMetadata` block into a running neutral `Usage`\n * accumulator. Shared by every `generateContent`-backed surface — the\n * chat model's `complete()`, its streaming loop (where the final chunk\n * carries cumulative totals), and the Gemini image model — so one\n * mapping decides what a Gemini token report means package-wide.\n *\n * Cache-read hits (`cachedContentTokenCount`, implicit or explicit\n * context caching) surface as `cachedTokens`; the thinking-phase tokens\n * of a reasoning model (`thoughtsTokenCount`) surface as\n * `reasoningTokens`. Both are emitted only when reported `> 0` so an\n * absent channel leaves the field undefined rather than a false zero.\n *\n * `total` falls back to `input + output` when Google omits\n * `totalTokenCount`.\n *\n * `promptTokenCount` \"includes the number of tokens in the cached\n * content\" (https://ai.google.dev/api/generate-content#UsageMetadata), so\n * the usage is marked `inputIncludesCache: true`.\n */\nexport function applyGoogleUsage(usage: Usage, raw: GoogleUsageMetadata): void {\n usage.inputIncludesCache = true;\n usage.input = raw.promptTokenCount ?? usage.input;\n usage.output = raw.candidatesTokenCount ?? usage.output;\n usage.total = raw.totalTokenCount ?? usage.input + usage.output;\n\n const cached = raw.cachedContentTokenCount;\n\n if (cached && cached > 0) {\n usage.cachedTokens = cached;\n }\n\n const reasoning = raw.thoughtsTokenCount;\n\n if (reasoning && reasoning > 0) {\n usage.reasoningTokens = reasoning;\n }\n}\n","import type { FinishReason } from \"@warlock.js/ai\";\n\nconst finishReasonMap: Record<string, FinishReason> = {\n STOP: \"stop\",\n MAX_TOKENS: \"length\",\n};\n\n/**\n * Map Gemini's `FinishReason` enum value to the normalized\n * `FinishReason` union.\n *\n * `STOP` is the natural terminal. `MAX_TOKENS` maps to `length`.\n * Everything else — `SAFETY`, `RECITATION`, `BLOCKLIST`,\n * `PROHIBITED_CONTENT`, `SPII`, `MALFORMED_FUNCTION_CALL`,\n * `UNEXPECTED_TOOL_CALL`, `LANGUAGE`, `OTHER`,\n * `FINISH_REASON_UNSPECIFIED`, `null`, or any future value — falls\n * through to `\"error\"`.\n *\n * Note: Gemini reports `STOP` even when the turn ended in a function\n * call (it has no `tool_use` reason). `GoogleModel` overrides the\n * mapped reason to `\"tool_calls\"` when the response carries function\n * calls — this map intentionally stays purely about the raw signal.\n *\n * @example\n * mapFinishReason(\"STOP\"); // \"stop\"\n * mapFinishReason(\"MAX_TOKENS\"); // \"length\"\n * mapFinishReason(\"SAFETY\"); // \"error\"\n * mapFinishReason(undefined); // \"error\"\n */\nexport function mapFinishReason(raw: string | null | undefined): FinishReason {\n return finishReasonMap[raw ?? \"\"] ?? \"error\";\n}\n","import { InvalidRequestError, safeJsonParse, type ContentPart, type Message } from \"@warlock.js/ai\";\nimport type { Content, Part } from \"@google/genai\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for Gemini's\n * `generateContent`: the system prompt is hoisted to a separate\n * `systemInstruction` string (Gemini has no `\"system\"` role — content\n * roles must be `\"user\"` or `\"model\"`), and the remaining turns map to\n * `Content[]`.\n */\nexport type GoogleContents = {\n systemInstruction: string | undefined;\n contents: Content[];\n};\n\n/**\n * Convert vendor-neutral `Message[]` into Gemini's request shape.\n *\n * Gemini specifics this function absorbs:\n *\n * 1. **No `system` role.** System messages concatenate into the\n * separate `systemInstruction` config field.\n * 2. **Role names differ.** Neutral `assistant` → Gemini `\"model\"`;\n * `user` stays `\"user\"`.\n * 3. **Tool results are `user` turns.** A neutral `tool` message\n * becomes a `\"user\"` content with a single `functionResponse` part.\n * 4. **Tool calls are `functionCall` parts.** An assistant message\n * with `toolCalls` becomes a `\"model\"` content: an optional leading\n * `text` part followed by one `functionCall` part per call.\n * 5. **Adjacent same-role turns merge.** Consecutive contents with the\n * same role become one content with their parts in order, so the\n * results of parallel tool calls travel as one `user` turn (as the\n * `@google/genai` SDK's own automatic function calling sends them)\n * and a compaction summary next to a same-role turn does not break\n * the user/model alternation.\n *\n * @example\n * const { systemInstruction, contents } = toGoogleContents([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n */\nexport function toGoogleContents(messages: Message[]): GoogleContents {\n const systemParts: string[] = [];\n const contents: Content[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n systemParts.push(stringifyContent(message.content));\n\n continue;\n }\n\n if (message.role === \"tool\") {\n contents.push({\n role: \"user\",\n parts: [\n {\n // Gemini matches a `functionResponse` to its `functionCall`\n // by `name` (the Developer API has no call ids). `name` is\n // the neutral `toolCallId`, which `GoogleModel` set to the\n // function name. The wire `id` is intentionally omitted —\n // an empty/synthetic id is rejected as an invalid argument.\n functionResponse: {\n name: message.toolCallId ?? \"\",\n response: toResponseObject(stringifyContent(message.content)),\n },\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const parts: Part[] = [];\n const text = stringifyContent(message.content);\n\n if (text) {\n parts.push({ text });\n }\n\n for (const toolCall of message.toolCalls) {\n // Replay the opaque `thoughtSignature` Gemini attached to this\n // function call on the original turn. Thinking models reject\n // the follow-up request with a 400 if the signature is missing\n // from the echoed `functionCall` part. Captured by\n // `GoogleModel.partToToolCall` into `providerMetadata`.\n const thoughtSignature = toolCall.providerMetadata?.thoughtSignature;\n\n parts.push({\n ...(typeof thoughtSignature === \"string\" ? { thoughtSignature } : {}),\n // `id` omitted deliberately — Gemini Developer API function\n // calls have no ids; echoing an empty/synthetic one is\n // rejected as an invalid argument. Matched by `name`.\n functionCall: {\n name: toolCall.name,\n args: (toolCall.input ?? {}) as Record<string, unknown>,\n },\n });\n }\n\n contents.push({ role: \"model\", parts });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n contents.push({ role: \"user\", parts: message.content.map(toGooglePart) });\n\n continue;\n }\n\n contents.push({\n role: message.role === \"assistant\" ? \"model\" : \"user\",\n parts: [{ text: stringifyContent(message.content) }],\n });\n }\n\n return {\n systemInstruction: systemParts.length > 0 ? systemParts.join(\"\\n\\n\") : undefined,\n contents: mergeAdjacentContents(contents),\n };\n}\n\n/**\n * Join consecutive contents that share a role into one, keeping part\n * order: parallel `functionResponse` parts stay in their `functionCall`\n * order, and each `functionCall` keeps its own `thoughtSignature`.\n * Returns new content objects; the input contents are not modified.\n *\n * Source for the single-turn shape: `@google/genai` 2.19.0\n * `Models.generateContent` (automatic function calling) sends every\n * function response of one model turn as a single `{ role: \"user\",\n * parts }` content, in call order.\n */\nfunction mergeAdjacentContents(contents: Content[]): Content[] {\n const merged: Content[] = [];\n\n for (const content of contents) {\n const previous = merged.at(-1);\n\n if (previous && previous.role === content.role) {\n merged[merged.length - 1] = {\n ...previous,\n parts: [...(previous.parts ?? []), ...(content.parts ?? [])],\n };\n\n continue;\n }\n\n merged.push(content);\n }\n\n return merged;\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any\n * other role collapse a `ContentPart[]` to concatenated text. Plain\n * strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Gemini's `functionResponse.response` must be a JSON object. Tool\n * results arrive as a string (usually stringified JSON) — parse it\n * when it is a JSON object, otherwise wrap the raw string under a\n * `result` key so the model always receives a well-formed object.\n */\nfunction toResponseObject(raw: string): Record<string, unknown> {\n const parsed = safeJsonParse<unknown>(raw, undefined);\n\n if (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n\n return { result: raw };\n}\n\n/**\n * Map a resolved `ContentPart` to a Gemini `Part`. All binary\n * modalities — **image, PDF, and audio** — go to a single\n * `inlineData: { mimeType, data }` block; Gemini's multimodal input is\n * media-agnostic and keys off the IANA `mimeType` (`image/png`,\n * `application/pdf`, `audio/mpeg`, …), so one mapping covers every part\n * type the model's capabilities admit. PDF and audio reach this point\n * only when the model declares the matching capability (`google.model`\n * infers `pdf` / `audio` from the multimodal Gemini families); the\n * agent's modality gate throws upfront otherwise, so capability and\n * behavior stay in lockstep.\n *\n * Gemini's `generateContent` does not fetch arbitrary remote URLs (only\n * Files API / GCS URIs via `fileData`), so a neutral `{ url }` source\n * surfaces a typed `InvalidRequestError` upfront — for any modality —\n * rather than a downstream Gemini fault. The agent resolves attachments\n * before this point, so nothing is read or fetched here.\n */\nfunction toGooglePart(part: ContentPart): Part {\n if (part.type === \"text\") {\n return { text: part.text };\n }\n\n if (\"url\" in part.source) {\n throw new InvalidRequestError(\n `Gemini generateContent cannot fetch remote-URL ${part.type} media; supply base64 bytes instead.`,\n );\n }\n\n return {\n inlineData: { mimeType: part.source.mediaType, data: part.source.base64 },\n };\n}\n","import { extractJsonSchema, type ToolConfig } from \"@warlock.js/ai\";\nimport type { Tool } from \"@google/genai\";\n\n/**\n * Convert vendor-neutral `ToolConfig[]` into Gemini's `tools` array —\n * a single `Tool` carrying one `functionDeclarations` entry per tool.\n *\n * The input schema is forwarded via `parametersJsonSchema` (raw JSON\n * Schema, mutually exclusive with Gemini's typed `parameters`).\n * Non-object extractions degrade to a parameterless object so\n * registration never fails.\n *\n * Returns `undefined` when there are no tools so the caller can omit\n * `config.tools` entirely.\n *\n * @example\n * const tools = toGoogleTools([weatherTool]);\n * await ai.models.generateContent({ model, contents, config: { tools } });\n */\nexport function toGoogleTools(\n tools: ToolConfig<unknown, unknown>[] | undefined,\n): Tool[] | undefined {\n if (!tools || tools.length === 0) {\n return undefined;\n }\n\n return [\n {\n functionDeclarations: tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n parametersJsonSchema: toJsonSchema(tool.input),\n })),\n },\n ];\n}\n\n/**\n * Resolve a tool's input schema to a JSON-Schema object. Gemini wants\n * an object root for function parameters; anything else (or a failed\n * extraction) degrades to a parameterless object.\n */\nfunction toJsonSchema(input: ToolConfig<unknown, unknown>[\"input\"]): Record<string, unknown> {\n const schema = extractJsonSchema(input);\n\n if (schema && schema.type === \"object\") {\n return schema;\n }\n\n return { type: \"object\" };\n}\n","import {\n AIError,\n ContextLengthExceededError,\n InvalidRequestError,\n ProviderAuthError,\n ProviderError,\n ProviderOverloadedError,\n ProviderRateLimitError,\n ProviderTimeoutError,\n} from \"@warlock.js/ai\";\nimport { ApiError } from \"@google/genai\";\n\n/**\n * Raw-error fields the wrapper reads off a Gemini SDK error.\n * `@google/genai`'s `ApiError` exposes `status` (HTTP code) +\n * `message`; transport aborts surface as `AbortError` / `ETIMEDOUT`.\n * We duck-type so proxied / re-thrown errors still classify.\n */\ntype GoogleErrorShape = {\n status?: number;\n message?: string;\n name?: string;\n code?: string;\n};\n\n/**\n * Wrap any thrown value caught inside the Gemini adapter into the\n * appropriate `@warlock.js/ai` `AIError` subclass.\n *\n * **Dispatch strategy.** Gemini has no machine error `code`; the\n * signals are the HTTP `status` and the canonical status phrase Google\n * embeds in `message` (`PERMISSION_DENIED`, `RESOURCE_EXHAUSTED`,\n * `INVALID_ARGUMENT`, …). Dispatch keys on `status`, using the message\n * phrase as the tie-breaker for the two 400 sub-cases\n * (context-length vs generic) and for status-less auth/quota errors.\n *\n * `AIError` instances pass through unchanged so `catch/throw wrap(e)`\n * pipelines never double-wrap.\n *\n * @example\n * try {\n * return await this.ai.models.generateContent(...);\n * } catch (thrown) {\n * throw wrapGoogleError(thrown);\n * }\n */\nexport function wrapGoogleError(thrown: unknown): AIError {\n if (thrown instanceof AIError) {\n return thrown;\n }\n\n const shape = toShape(thrown);\n const context = buildContext(shape);\n const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));\n // \"If your request fails with a 400 or 500 error, you won't be charged\n // for the tokens used.\" — https://ai.google.dev/gemini-api/docs/billing\n // (fetched 2026-10-17). Every other failure keeps the default.\n const billed = shape.status === 400 || shape.status === 500 ? false : undefined;\n\n if (isTimeout(shape)) {\n return new ProviderTimeoutError(message, { cause: thrown, context, billed });\n }\n\n if (\n shape.status === 401 ||\n shape.status === 403 ||\n /permission_denied|api key not valid|unauthenticated/i.test(message)\n ) {\n return new ProviderAuthError(message, { cause: thrown, context, billed });\n }\n\n if (shape.status === 429 || /resource_exhausted|quota/i.test(message)) {\n return new ProviderRateLimitError(message, { cause: thrown, context, billed });\n }\n\n if (shape.status === 400) {\n if (/token count|context length|exceeds the maximum|input is too long/i.test(message)) {\n return new ContextLengthExceededError(message, { cause: thrown, context, billed });\n }\n\n return new InvalidRequestError(message, { cause: thrown, context, billed });\n }\n\n if (shape.status === 404 || isClientStatus(shape.status)) {\n return new InvalidRequestError(message, { cause: thrown, context, billed });\n }\n\n // 503 UNAVAILABLE — \"temporarily overloaded or down\"\n // (https://ai.google.dev/gemini-api/docs/api-errors).\n if (shape.status === 503 || /\\bUNAVAILABLE\\b/.test(message) || /overloaded/i.test(message)) {\n return new ProviderOverloadedError(message, { cause: thrown, context, billed });\n }\n\n return new ProviderError(message, { cause: thrown, context, billed });\n}\n\n/**\n * Read the raw error shape. The Gemini SDK's `ApiError` carries a\n * numeric `status`; flattened/proxied errors may carry it (or `code`)\n * loosely.\n */\nfunction toShape(thrown: unknown): GoogleErrorShape {\n if (thrown instanceof ApiError) {\n return { status: thrown.status, message: thrown.message, name: thrown.name };\n }\n\n if (typeof thrown === \"object\" && thrown !== null) {\n const raw = thrown as Record<string, unknown>;\n\n return {\n status: typeof raw.status === \"number\" ? raw.status : undefined,\n message: typeof raw.message === \"string\" ? raw.message : undefined,\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n code: typeof raw.code === \"string\" ? raw.code : undefined,\n };\n }\n\n return {};\n}\n\n/**\n * Decide whether the error is a timeout. Gemini maps gateway timeouts\n * to HTTP 504 (`DEADLINE_EXCEEDED`); transport aborts surface as\n * `AbortError` / `ETIMEDOUT` / `ECONNABORTED`.\n */\nfunction isTimeout(shape: GoogleErrorShape): boolean {\n if (shape.status === 504) {\n return true;\n }\n\n if (shape.name === \"AbortError\" || /deadline_exceeded/i.test(shape.message ?? \"\")) {\n return true;\n }\n\n return shape.code === \"ETIMEDOUT\" || shape.code === \"ECONNABORTED\";\n}\n\n/** True for HTTP 4xx — a client-side request problem, not a server fault. */\nfunction isClientStatus(status: number | undefined): boolean {\n return typeof status === \"number\" && status >= 400 && status < 500;\n}\n\n/** Attach the diagnostic fields to `error.context`. */\nfunction buildContext(shape: GoogleErrorShape): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n\n if (shape.status !== undefined) {\n context.status = shape.status;\n }\n\n if (shape.name) {\n context.code = shape.name;\n }\n\n return context;\n}\n","import {\n type EmbeddingBatchResult,\n type EmbeddingResult,\n type EmbeddingUsage,\n type EmbedderContract,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type { EmbedContentResponse, GoogleGenAI } from \"@google/genai\";\nimport type { GoogleEmbedderConfig } from \"./config.type\";\nimport { wrapGoogleError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.google\";\n\n/**\n * Token usage is not returned by Gemini's `embedContent`, so every\n * embedding result reports a zeroed `EmbeddingUsage` (honest absence,\n * not a fabricated estimate).\n */\nconst NO_USAGE: EmbeddingUsage = { promptTokens: 0, totalTokens: 0 };\n\n/**\n * Google Gemini-backed implementation of `EmbedderContract`\n * (`gemini-embedding-001`, `text-embedding-004`, …) via\n * `models.embedContent`.\n *\n * **Role.** Converts text into floating-point vectors. Standalone\n * primitive — unrelated to generateContent / tools / the agent loop.\n *\n * **Batch is native.** Gemini's `embedContent` accepts an array of\n * inputs and returns embeddings in the same order, so `embedMany` is\n * a single request (unlike the Bedrock/Titan adapter, which has to\n * loop).\n *\n * **No usage.** Gemini's embed endpoint returns no token counts;\n * `usage` is always `{ promptTokens: 0, totalTokens: 0 }`.\n *\n * **Dimensions.** When no `dimensions` override is given,\n * `this.dimensions` starts at `0` and is populated from the first\n * response's vector length, then cached. Passing `dimensions`\n * forwards Gemini's `outputDimensionality` truncation hint and sets\n * the initial value immediately.\n *\n * @example\n * const embedder = new GoogleEmbedder(ai, { name: \"gemini-embedding-001\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n * const { vectors } = await embedder.embedMany([\"doc 1\", \"doc 2\"]);\n */\nexport class GoogleEmbedder implements EmbedderContract {\n public readonly name: string;\n public readonly provider: string;\n public dimensions: number;\n\n private readonly ai: GoogleGenAI;\n private readonly configuredDimensions: number | undefined;\n private readonly logger: Logger = log;\n\n public constructor(ai: GoogleGenAI, config: GoogleEmbedderConfig, provider: string = \"google\") {\n this.ai = ai;\n this.name = config.name;\n this.provider = provider;\n this.configuredDimensions = config.dimensions;\n this.dimensions = config.dimensions ?? 0;\n }\n\n public async embed(input: string): Promise<EmbeddingResult> {\n const vectors = await this.request([input]);\n\n return { vector: vectors[0], dimensions: this.dimensions, usage: NO_USAGE };\n }\n\n public async embedMany(inputs: string[]): Promise<EmbeddingBatchResult> {\n const vectors = await this.request(inputs);\n\n return { vectors, dimensions: this.dimensions, usage: NO_USAGE };\n }\n\n /**\n * Shared transport: one `embedContent` call for the whole batch,\n * wrap provider errors, cache `dimensions` from the first vector,\n * and return the raw vectors in input order.\n */\n private async request(inputs: string[]): Promise<number[][]> {\n this.logger.debug(LOG_MODULE, \"embedder.request\", \"embedContent\", {\n model: this.name,\n count: inputs.length,\n });\n\n let response: EmbedContentResponse;\n\n try {\n response = await this.ai.models.embedContent({\n model: this.name,\n contents: inputs,\n ...(this.configuredDimensions !== undefined\n ? { config: { outputDimensionality: this.configuredDimensions } }\n : {}),\n });\n } catch (thrown) {\n const wrapped = wrapGoogleError(thrown);\n\n this.logger.error(LOG_MODULE, \"embedder.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const vectors = (response.embeddings ?? []).map((embedding) => embedding.values ?? []);\n\n if (this.dimensions === 0 && vectors[0]) {\n this.dimensions = vectors[0].length;\n }\n\n this.logger.debug(LOG_MODULE, \"embedder.response\", \"embedContent returned\", {\n count: vectors.length,\n dimensions: this.dimensions,\n });\n\n return vectors;\n }\n}\n","import {\n ContentFilterError,\n ProviderError,\n type AIError,\n type GeneratedImage,\n type ImageGenerationOptions,\n type ImageGenerationResponse,\n type ImageModelContract,\n type ImageModelPricing,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type {\n GenerateContentConfig,\n GenerateContentResponse,\n GoogleGenAI,\n ImageConfig,\n Part,\n} from \"@google/genai\";\nimport type { GoogleImageConfig } from \"./config.type\";\nimport { applyGoogleUsage, wrapGoogleError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.google\";\n\n/**\n * Response modalities requested when the caller names none.\n *\n * `IMAGE` is the modality this adapter extracts; `TEXT` rides along so\n * a model that narrates what it drew is not answering outside the set\n * it was granted (the narration is then dropped — only inline image\n * parts become `GeneratedImage`s).\n *\n * *Unverified:* which pairing any individual Gemini image model\n * requires is not established here — no spec or run in this package\n * touches the live API. `options.responseModalities` replaces this list\n * verbatim for a model that wants something else.\n */\nconst DEFAULT_RESPONSE_MODALITIES = [\"TEXT\", \"IMAGE\"];\n\n/**\n * Gemini `finishReason` values that mean generation was stopped by a\n * safety / policy rule rather than by the model simply not drawing.\n * Taken from the `FinishReason` enum in `@google/genai`'s own type\n * declarations, whose doc comments describe each of these as content\n * or image generation being \"stopped\" for safety, prohibited content,\n * recitation, blocklist, or SPII.\n */\nconst FILTERED_FINISH_REASONS = new Set([\n \"SAFETY\",\n \"IMAGE_SAFETY\",\n \"PROHIBITED_CONTENT\",\n \"IMAGE_PROHIBITED_CONTENT\",\n \"RECITATION\",\n \"IMAGE_RECITATION\",\n \"BLOCKLIST\",\n \"SPII\",\n]);\n\n/** How much of a text-only answer to quote back inside the error message. */\nconst TEXT_EXCERPT_LIMIT = 200;\n\n/**\n * Gemini-native implementation of `ImageModelContract`, via\n * `ai.models.generateContent` with `config.responseModalities`\n * including `\"IMAGE\"`.\n *\n * **Why a second image adapter.** `GoogleImageModel` calls\n * `ai.models.generateImages`, which the `@google/genai` bundle routes\n * to `{model}:predict` (`generateImages` → `generateImagesInternal` →\n * `formatMap('{model}:predict', …)`). A Gemini image model is not\n * served there: asking for one returns Google's\n * `404 … is not found for API version v1beta, or is not supported for\n * predict`. `generateContent` is the SDK's own named replacement — its\n * runtime deprecation notice for `generateImages` reads \"Please use the\n * generateContent method with image models instead\" — so that is the\n * transport this class speaks, hence a separate class rather than a\n * branch inside `image.ts`.\n *\n * **Same envelope.** Inline image parts are mapped to the identical\n * `GeneratedImage[]` shape `GoogleImageModel` produces, so `ai.image()`\n * callers see no difference between the two paths.\n *\n * **Token usage is passed through, not zeroed.** The Imagen path\n * returns a hard `{ 0, 0, 0 }` because Imagen reports no tokens at all;\n * here, whatever `usageMetadata` Google attaches is mapped by the same\n * {@link applyGoogleUsage} the chat model uses, and only an absent\n * block collapses to zeros. Price accordingly.\n *\n * **No model-id validation.** `config.name` is forwarded to\n * `generateContent` exactly as given; nothing here inspects it. An id\n * Google does not serve fails at Google, wrapped into the typed\n * `AIError` hierarchy — never with a local throw.\n *\n * **Evidence, in two tiers.** No spec in this package calls Google.\n * *Measured here:* a `gemini-*` image id, which 404s on the `predict`\n * transport, reached the model on this one and came back with a quota\n * error (HTTP 429) — the endpoint accepts the id. *Reported by the\n * maintainer:* once billing was enabled on the project, an image came\n * back end-to-end from an application running a locally linked build.\n * *Still unestablished:* whether these models report token usage — no\n * `usageMetadata` from a successful image call has been observed, so\n * the pass-through above is untested against a real response.\n *\n * @example\n * const model = new GeminiImageModel(ai, { name: \"gemini-3.1-flash-lite-image\" });\n * const { images, usage } = await model.generate(\"a red bicycle on a white background\");\n */\nexport class GeminiImageModel implements ImageModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly pricing?: ImageModelPricing;\n\n private readonly ai: GoogleGenAI;\n private readonly logger: Logger = log;\n\n public constructor(ai: GoogleGenAI, config: GoogleImageConfig, provider: string = \"google\") {\n this.ai = ai;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n }\n\n public async generate(\n prompt: string,\n options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n const config = this.buildConfig(options);\n\n this.logger.debug(LOG_MODULE, \"image.request\", \"models.generateContent\", {\n model: this.name,\n responseModalities: config.responseModalities,\n });\n\n let response: GenerateContentResponse;\n\n try {\n // `contents` takes a bare string: the SDK's own `generateContent`\n // example passes one (`contents: 'Why is the sky blue?'`).\n response = await this.ai.models.generateContent({\n model: this.name,\n contents: prompt,\n config,\n });\n } catch (thrown) {\n const wrapped = wrapGoogleError(thrown);\n\n this.logger.error(LOG_MODULE, \"image.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const parts = collectParts(response);\n const images = toGeneratedImages(parts);\n\n if (images.length === 0) {\n throw this.noImageError(response, parts);\n }\n\n const usage: Usage = { input: 0, output: 0, total: 0 };\n\n if (response.usageMetadata) {\n applyGoogleUsage(usage, response.usageMetadata);\n }\n\n this.logger.debug(LOG_MODULE, \"image.response\", \"models.generateContent succeeded\", {\n images: images.length,\n usage,\n });\n\n return { images, usage };\n }\n\n /**\n * Assemble the `GenerateContentConfig` for an image turn: the\n * requested modalities, the image-specific knobs Gemini exposes under\n * `imageConfig`, and the cancellation handle.\n *\n * Three neutral options are deliberately NOT forwarded, because\n * `GenerateContentConfig` / `ImageConfig` in `@google/genai` expose\n * no equivalent for them on this path: `count` (no per-request image\n * count — every inline image part the model does return is mapped),\n * `negativePrompt` (an Imagen-only field), and `format`\n * (`ImageConfig.outputMimeType` is documented \"not supported in\n * Gemini API\"). Fold those intentions into the prompt instead.\n */\n private buildConfig(options: ImageGenerationOptions | undefined): GenerateContentConfig {\n const imageConfig: ImageConfig = {};\n\n if (options?.aspectRatio !== undefined) {\n imageConfig.aspectRatio = options.aspectRatio;\n }\n\n // Provider passthroughs off the neutral options' index signature —\n // forwarded verbatim, never re-spelled, so the value the caller\n // wrote is the value Google rules on. `ImageConfig` documents\n // `imageSize` as `1K`/`2K`/`4K` and `personGeneration` as\n // `ALLOW_ALL`/`ALLOW_ADULT`/`ALLOW_NONE`.\n if (typeof options?.imageSize === \"string\") {\n imageConfig.imageSize = options.imageSize;\n }\n\n if (typeof options?.personGeneration === \"string\") {\n imageConfig.personGeneration = options.personGeneration;\n }\n\n const requested = options?.responseModalities;\n const responseModalities = Array.isArray(requested)\n ? (requested as string[])\n : DEFAULT_RESPONSE_MODALITIES;\n\n return {\n responseModalities,\n ...(Object.keys(imageConfig).length > 0 ? { imageConfig } : {}),\n ...(options?.signal ? { abortSignal: options.signal } : {}),\n };\n }\n\n /**\n * Build the typed error for a response that carried no inline image\n * part. Never a silent empty success: the caller asked for an image\n * and got something else, so the error names what actually came back.\n *\n * - A blocked prompt (`promptFeedback.blockReason`) or a\n * safety/policy `finishReason` → `ContentFilterError` carrying the\n * reason, matching how the Imagen path reports `raiFilteredReason`.\n * - A text-only answer → `ProviderError` quoting the text, so the\n * log says what the model replied instead of guessing.\n * - Anything else → `ProviderError` naming the finish reason and how\n * many parts arrived.\n */\n private noImageError(response: GenerateContentResponse, parts: Part[]): AIError {\n const blockReason = response.promptFeedback?.blockReason;\n\n if (blockReason) {\n return new ContentFilterError(`Gemini blocked the prompt for ${this.name}: ${blockReason}`, {\n reason: blockReason,\n });\n }\n\n const finishReason = response.candidates?.[0]?.finishReason;\n\n if (finishReason && FILTERED_FINISH_REASONS.has(finishReason)) {\n return new ContentFilterError(`Gemini filtered the image for ${this.name}: ${finishReason}`, {\n reason: finishReason,\n });\n }\n\n const text = collectText(parts);\n\n if (text) {\n return new ProviderError(\n `Gemini returned no image for ${this.name} — the response was text only: \"${excerpt(text)}\"`,\n { context: { model: this.name, ...(finishReason ? { finishReason } : {}) } },\n );\n }\n\n return new ProviderError(\n `Gemini returned no image part for ${this.name} (parts: ${parts.length}${\n finishReason ? `, finishReason: ${finishReason}` : \"\"\n }).`,\n { context: { model: this.name, parts: parts.length } },\n );\n }\n}\n\n/**\n * Flatten every candidate's content parts into one list. Read off\n * `candidates[].content.parts` rather than the response's convenience\n * getters: `response.text` covers only the first candidate's text and\n * there is no getter for inline image data at all.\n */\nfunction collectParts(response: GenerateContentResponse): Part[] {\n const parts: Part[] = [];\n\n for (const candidate of response.candidates ?? []) {\n parts.push(...(candidate.content?.parts ?? []));\n }\n\n return parts;\n}\n\n/**\n * Map the inline image parts to the neutral `GeneratedImage[]` — the\n * same `{ type: \"base64\", base64, mediaType }` shape the Imagen path\n * emits, including its `image/png` fallback for a part that arrives\n * without a declared mime type.\n */\nfunction toGeneratedImages(parts: Part[]): GeneratedImage[] {\n const images: GeneratedImage[] = [];\n\n for (const part of parts) {\n const data = part.inlineData?.data;\n\n if (!data) {\n continue;\n }\n\n images.push({\n type: \"base64\",\n base64: data,\n mediaType: part.inlineData?.mimeType ?? \"image/png\",\n });\n }\n\n return images;\n}\n\n/** Join the text parts of a response — what the model said instead of drawing. */\nfunction collectText(parts: Part[]): string {\n return parts\n .map((part) => part.text)\n .filter((text): text is string => typeof text === \"string\" && text.length > 0)\n .join(\" \")\n .trim();\n}\n\n/** Trim a quoted model answer so an error message stays readable. */\nfunction excerpt(text: string): string {\n return text.length > TEXT_EXCERPT_LIMIT ? `${text.slice(0, TEXT_EXCERPT_LIMIT)}…` : text;\n}\n","import {\n ContentFilterError,\n ProviderError,\n type GeneratedImage,\n type ImageGenerationOptions,\n type ImageGenerationResponse,\n type ImageModelContract,\n type ImageModelPricing,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type { GenerateImagesConfig, GoogleGenAI } from \"@google/genai\";\nimport type { GoogleImageConfig } from \"./config.type\";\nimport { wrapGoogleError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.google\";\n\n/** Map a neutral output container hint to an IANA media type. */\nfunction mediaTypeFor(format: string | undefined): string | undefined {\n switch (format) {\n case \"png\":\n return \"image/png\";\n case \"jpeg\":\n case \"jpg\":\n return \"image/jpeg\";\n case \"webp\":\n return \"image/webp\";\n default:\n return undefined;\n }\n}\n\n/**\n * Google Imagen-backed implementation of `ImageModelContract`, via\n * `ai.models.generateImages`. Imagen is per-image-metered and returns\n * base64 image bytes (no hosted URL, no token usage).\n *\n * **No model-id validation.** `config.name` is passed through to\n * `ai.models.generateImages` exactly as given — the constructor never\n * inspects it. Google adds and retires image model ids on its own\n * schedule, so an id this adapter does not recognize is not the\n * adapter's call to refuse; an unsupported id surfaces as a provider\n * error from Google (wrapped into the typed `AIError` hierarchy by\n * `generate()`), not as a local one.\n *\n * **Safety filtering.** When Imagen filters every candidate for safety\n * (`raiFilteredReason`), this surfaces a typed `ContentFilterError`\n * carrying the reason, rather than returning an empty success.\n *\n * @example\n * const model = new GoogleImageModel(ai, { name: \"imagen-4.0-generate-001\" }, \"google\");\n * const { images } = await model.generate(\"a watercolor lighthouse at dawn\");\n */\nexport class GoogleImageModel implements ImageModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly pricing?: ImageModelPricing;\n\n private readonly ai: GoogleGenAI;\n private readonly logger: Logger = log;\n\n public constructor(ai: GoogleGenAI, config: GoogleImageConfig, provider: string = \"google\") {\n this.ai = ai;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n }\n\n public async generate(\n prompt: string,\n options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n const config: GenerateImagesConfig = {};\n\n if (options?.count !== undefined) config.numberOfImages = options.count;\n if (options?.aspectRatio !== undefined) config.aspectRatio = options.aspectRatio;\n if (options?.negativePrompt !== undefined) config.negativePrompt = options.negativePrompt;\n if (options?.signal !== undefined) config.abortSignal = options.signal;\n\n const outputMimeType = mediaTypeFor(options?.format);\n if (outputMimeType !== undefined) config.outputMimeType = outputMimeType;\n\n // Imagen sizing is `imageSize` (\"1K\"/\"2K\") — a distinct concept from\n // OpenAI's WxH `size`, so we honor only an explicit passthrough.\n if (typeof options?.imageSize === \"string\") config.imageSize = options.imageSize;\n if (typeof options?.personGeneration === \"string\") {\n config.personGeneration =\n options.personGeneration as GenerateImagesConfig[\"personGeneration\"];\n }\n\n this.logger.debug(LOG_MODULE, \"image.request\", \"models.generateImages\", {\n model: this.name,\n count: options?.count ?? 1,\n });\n\n let response: Awaited<ReturnType<GoogleGenAI[\"models\"][\"generateImages\"]>>;\n\n try {\n response = await this.ai.models.generateImages({ model: this.name, prompt, config });\n } catch (thrown) {\n const wrapped = wrapGoogleError(thrown);\n\n this.logger.error(LOG_MODULE, \"image.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const generated = response.generatedImages ?? [];\n const images: GeneratedImage[] = [];\n\n for (const candidate of generated) {\n const bytes = candidate.image?.imageBytes;\n if (!bytes) continue;\n\n images.push({\n type: \"base64\",\n base64: bytes,\n mediaType: candidate.image?.mimeType ?? outputMimeType ?? \"image/png\",\n ...(candidate.enhancedPrompt ? { revisedPrompt: candidate.enhancedPrompt } : {}),\n });\n }\n\n if (images.length === 0) {\n const filtered = generated.find((candidate) => candidate.raiFilteredReason);\n\n if (filtered?.raiFilteredReason) {\n throw new ContentFilterError(\n `Imagen filtered all candidates: ${filtered.raiFilteredReason}`,\n { reason: filtered.raiFilteredReason },\n );\n }\n\n throw new ProviderError(\"Imagen returned no images.\");\n }\n\n this.logger.debug(LOG_MODULE, \"image.response\", \"models.generateImages succeeded\", {\n images: images.length,\n });\n\n // Imagen returns no token usage — honest zero (priced per image).\n return { images, usage: { input: 0, output: 0, total: 0 } };\n }\n}\n","import type { ModelPricing } from \"@warlock.js/ai\";\n\n/**\n * Built-in Gemini API list prices (paid tier, Standard), USD per\n * 1,000,000 tokens, keyed by exact model id. `cachedInput` is the\n * context-cache read rate; the hourly cache-storage charge is not a token\n * rate and is never included.\n *\n * Source: https://ai.google.dev/gemini-api/docs/pricing, fetched\n * 2026-10-17. Only models with one flat token price are listed. Left out\n * on purpose — pass `pricing` yourself for them:\n *\n * - Models priced by prompt length (the Pro models, split at 200k\n * prompt tokens).\n * - Models with a higher audio-input rate (most Flash / Flash-Lite\n * models): `Usage` does not split input by modality.\n * - Models with a scheduled price change (gemini-4-argon, the 3.8 / 3.6\n * Flash models).\n */\nexport const GOOGLE_MODEL_PRICING: Readonly<Record<string, ModelPricing>> = {\n \"gemini-3.5-flash-lite\": { input: 0.3, output: 2.5, cachedInput: 0.03 },\n};\n\n/**\n * Look up the built-in list price for a Gemini model id (exact match),\n * or `undefined` so cost stays unknown rather than wrong.\n *\n * @example\n * googlePricingFor(\"gemini-3.5-flash-lite\"); // { input: 0.3, output: 2.5, cachedInput: 0.03 }\n * googlePricingFor(\"gemini-2.5-pro\"); // undefined — priced by prompt length\n */\nexport function googlePricingFor(modelName: string): ModelPricing | undefined {\n return GOOGLE_MODEL_PRICING[modelName];\n}\n","import { InvalidRequestError, type ModelCallOptions } from \"@warlock.js/ai\";\n\n/** Adapter-only fields merged after the neutral request parameters. */\nexport type GoogleCallOptions = { body?: Record<string, unknown> };\n\nconst RESERVED_FIELDS = new Set([\"systemInstruction\", \"tools\", \"abortSignal\", \"httpOptions\"]);\n\nexport function mergeProviderBody<T extends object>(\n built: T,\n options: ModelCallOptions | undefined,\n): T {\n const provider = options?.providerOptions?.google as GoogleCallOptions | undefined;\n const body = provider?.body ?? {};\n const reserved = Object.keys(body).filter((key) => RESERVED_FIELDS.has(key));\n if (reserved.length > 0) {\n throw new InvalidRequestError(\n `providerOptions.google.body cannot set ${reserved.join(\", \")}: use the call's messages, tools and model options.`,\n { context: { fields: reserved } },\n );\n }\n const defaults = built as Record<string, unknown>;\n const overrides = Object.fromEntries(\n Object.entries(body).map(([key, value]) => {\n const previous = defaults[key];\n return [\n key,\n isPlainObject(previous) && isPlainObject(value) ? { ...previous, ...value } : value,\n ];\n }),\n );\n return { ...built, ...overrides };\n}\n\n/** Merge only plain objects; arrays, null and provider scalar values replace the field. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n","/**\n * Substrings identifying Gemini model ids whose family accepts image\n * input (vision).\n *\n * Every Gemini 1.5, 2.x, and 2.5 model is natively multimodal, as is\n * the legacy `gemini-pro-vision`. Only the original text-only\n * `gemini-pro` / `gemini-1.0-pro` is excluded. A substring match\n * tolerates the date/preview suffixes Google appends\n * (`gemini-2.5-flash-preview-05-20`). Override per-model via\n * `google.model({ name, vision: true | false })`.\n */\nconst VISION_CAPABLE_SUBSTRINGS = [\n \"gemini-1.5\",\n \"gemini-2\",\n \"gemini-exp\",\n \"gemini-pro-vision\",\n \"gemini-flash\",\n];\n\n/**\n * Infer whether a Gemini model id supports vision based on the known\n * multimodal-family substrings. Unknown ids default to `false` so\n * passing an image attachment to an unsupported model surfaces a\n * clear, agent-side capability error instead of an opaque Gemini 400.\n *\n * @example\n * inferVisionCapability(\"gemini-2.5-flash\"); // → true\n * inferVisionCapability(\"gemini-1.5-pro-002\"); // → true\n * inferVisionCapability(\"gemini-1.0-pro\"); // → false\n * inferVisionCapability(\"text-embedding-004\"); // → false\n */\nexport function inferVisionCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n","import {\n attachCost,\n InvalidRequestError,\n resolveStreamChunks,\n selectProviderTools,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type ReasoningEffort,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type {\n GenerateContentConfig,\n GenerateContentResponse,\n GoogleGenAI,\n Part,\n Tool,\n} from \"@google/genai\";\nimport { mergeProviderBody } from \"./provider-options\";\nimport type { GoogleModelConfig } from \"./config.type\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport {\n applyGoogleUsage,\n mapFinishReason,\n toGoogleContents,\n toGoogleTools,\n wrapGoogleError,\n} from \"./utils\";\n\nconst LOG_MODULE = \"ai.google\";\n\n/** `provider` value of `ai.providerTool()` entries this adapter sends verbatim. */\nconst GOOGLE_WIRE_FORMAT = \"google\";\n\n/**\n * Bucketed `thinkingBudget` (token caps) for the neutral\n * `reasoning.effort` levels when the caller gives no explicit\n * `reasoning.maxTokens`. Gemini 2.5 accepts a positive budget as a cap\n * on the thinking phase; these mirror the spread the OpenAI\n * `reasoning_effort` low/medium/high tiers imply. The installed\n * `@google/genai` `ThinkingConfig` declaration documents that allowed\n * ranges are model-dependent, so retain the existing conservative 24,576\n * cap for the new tiers rather than sending a budget unsupported by Flash.\n */\nconst EFFORT_THINKING_BUDGET: Record<Exclude<ReasoningEffort, \"none\">, number> = {\n minimal: 1024,\n low: 1024,\n medium: 8192,\n high: 24576,\n xhigh: 24576,\n max: 24576,\n};\n\n/**\n * Google Gemini-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and the `@google/genai` SDK\n * (`models.generateContent` / `generateContentStream`).\n *\n * **Responsibility.**\n * - Owns: a long-lived `GoogleGenAI` client + frozen `ModelConfig`\n * (name, temperature, maxTokens) used as per-call defaults.\n * - Owns: translating vendor-neutral `Message[]` / `ToolConfig[]` into\n * Gemini shapes (systemInstruction hoisting, `model` role,\n * `functionCall` / `functionResponse` parts, inline image bytes) on\n * the way out, and Gemini's candidate/parts response (text, function\n * calls, finish reason, token usage) back into neutral shapes on the\n * way in.\n * - Does NOT own: dispatching tools, looping, history, retries — those\n * are agent concerns. The model is a per-call protocol adapter.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across calls\"): the `GoogleGenAI` client is reused for the SDK's\n * lifetime.\n *\n * @example\n * import { GoogleGenAI } from \"@google/genai\";\n * const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });\n * const model = new GoogleModel(ai, { name: \"gemini-2.5-flash\" });\n *\n * const myAgent = agent({ model, tools: [searchTool] });\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class GoogleModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly ai: GoogleGenAI;\n private readonly config: GoogleModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(ai: GoogleGenAI, config: GoogleModelConfig, provider: string = \"google\") {\n this.ai = ai;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n const multimodal = config.vision ?? inferVisionCapability(config.name);\n\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? true,\n vision: multimodal,\n // Every Gemini 2.5 model thinks; older families harmlessly ignore\n // an empty thinking budget. Defaulting `true` lets the agent\n // forward reasoning options; an explicit `false` opts a model out.\n reasoning: config.reasoning ?? true,\n // Gemini reports cache-read hits (`cachedContentTokenCount`) on\n // every call via implicit caching, and accepts explicit context\n // caching. Read-side accounting is always honored.\n promptCaching: true,\n // The multimodal Gemini families that accept images also accept\n // audio and PDF/document parts. Mirror the vision inference unless\n // explicitly overridden.\n audio: config.audio ?? multimodal,\n pdf: config.pdf ?? multimodal,\n };\n }\n\n /**\n * Single-shot completion. Sends the full message list to\n * `generateContent`, waits for the terminal response, and reshapes\n * it into a vendor-neutral `ModelResponse`. Per-call `options`\n * override the instance defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting generateContent call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n const { systemInstruction, contents } = toGoogleContents(messages);\n\n let response: GenerateContentResponse;\n\n try {\n response = await this.ai.models.generateContent({\n model: this.name,\n contents,\n config: this.buildConfig(systemInstruction, options),\n });\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const toolCalls = this.extractToolCalls(response);\n const finishReason = toolCalls\n ? \"tool_calls\"\n : mapFinishReason(response.candidates?.[0]?.finishReason);\n const usage = attachCost(this.extractUsage(response), this.pricing);\n\n this.logger.debug(LOG_MODULE, \"response\", \"generateContent call succeeded\", {\n finishReason,\n usage,\n });\n\n return {\n content: response.text ?? \"\",\n finishReason,\n usage,\n toolCalls,\n };\n }\n\n /** Count native input tokens with the same converted content, system and tools. */\n public async countTokens(messages: Message[], options?: ModelCallOptions): Promise<number> {\n const { systemInstruction, contents } = toGoogleContents(messages);\n const config = this.buildConfig(systemInstruction, options);\n if (\n !this.ai.vertexai &&\n (config.systemInstruction !== undefined || config.tools !== undefined)\n ) {\n throw new InvalidRequestError(\n \"The Google SDK countTokens endpoint in Developer API mode cannot count system instructions or tools. Use Vertex mode for this preflight, or SDK.count() for an offline estimate.\",\n );\n }\n if (config.cachedContent !== undefined) {\n throw new InvalidRequestError(\n \"The Google SDK countTokens endpoint cannot count a cachedContent reference.\",\n );\n }\n try {\n const response = await this.ai.models.countTokens({\n model: this.name,\n contents,\n config: {\n ...(config.systemInstruction !== undefined\n ? { systemInstruction: config.systemInstruction }\n : {}),\n ...(config.tools !== undefined ? { tools: config.tools as Tool[] } : {}),\n ...(config.abortSignal ? { abortSignal: config.abortSignal } : {}),\n ...(config.httpOptions ? { httpOptions: config.httpOptions } : {}),\n },\n });\n if (response.totalTokens === undefined)\n throw new InvalidRequestError(\"Google countTokens returned no totalTokens.\");\n return response.totalTokens;\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n }\n\n /**\n * Incremental streaming completion via `generateContentStream`.\n * Yields neutral `ModelStreamChunk`s — `delta` for text, `tool-call`\n * per function call (Gemini emits a fully-formed call, not partial\n * JSON), and a terminal `done` with the final finish reason + usage.\n *\n * With `options.streamChunks` it also yields `reasoning` for thought\n * parts (only sent when `thinkingConfig.includeThoughts` is on — set it\n * through `providerOptions.google.body`) and `usage` whenever a chunk\n * carries `usageMetadata`. No `tool-args`: calls arrive whole.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting generateContentStream call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n const { systemInstruction, contents } = toGoogleContents(messages);\n\n let iterable: AsyncGenerator<GenerateContentResponse>;\n\n try {\n iterable = await this.ai.models.generateContentStream({\n model: this.name,\n contents,\n config: this.buildConfig(systemInstruction, options),\n });\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n let rawFinishReason: string | undefined;\n let sawToolCall = false;\n const usage: Usage = { input: 0, output: 0, total: 0 };\n const want = resolveStreamChunks(options);\n\n try {\n for await (const chunk of iterable) {\n // Thought parts (`thought: true`, sent when `thinkingConfig.includeThoughts`\n // is on) carry the thought summary in `text`; the SDK's `chunk.text`\n // getter already leaves them out of the answer.\n if (want.reasoning) {\n for (const part of chunk.candidates?.[0]?.content?.parts ?? []) {\n if (part.thought === true && typeof part.text === \"string\" && part.text !== \"\") {\n yield { type: \"reasoning\", delta: part.text };\n }\n }\n }\n\n const text = chunk.text;\n\n if (text) {\n yield { type: \"delta\", content: text };\n }\n\n for (const part of chunk.candidates?.[0]?.content?.parts ?? []) {\n const toolCall = this.partToToolCall(part);\n\n if (!toolCall) {\n continue;\n }\n\n sawToolCall = true;\n\n yield {\n type: \"tool-call\",\n id: toolCall.id,\n name: toolCall.name,\n input: toolCall.input,\n ...(toolCall.providerMetadata ? { providerMetadata: toolCall.providerMetadata } : {}),\n };\n }\n\n const candidateFinish = chunk.candidates?.[0]?.finishReason;\n\n if (candidateFinish) {\n rawFinishReason = candidateFinish;\n }\n\n if (chunk.usageMetadata) {\n applyGoogleUsage(usage, chunk.usageMetadata);\n\n if (want.usage) {\n yield { type: \"usage\", usage: { ...usage } };\n }\n }\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const finishReason = sawToolCall ? \"tool_calls\" : mapFinishReason(rawFinishReason);\n\n attachCost(usage, this.pricing);\n\n this.logger.debug(LOG_MODULE, \"response\", \"generateContentStream call succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage };\n }\n\n /**\n * Assemble the `GenerateContentConfig` shared by `complete()` and\n * `stream()`: inference params, hoisted system instruction,\n * cancellation signal, and conditional tools + native structured\n * output.\n */\n private buildConfig(\n systemInstruction: string | undefined,\n options: ModelCallOptions | undefined,\n ): GenerateContentConfig {\n const temperature = options?.temperature ?? this.config.temperature;\n const maxOutputTokens = options?.maxTokens ?? this.config.maxTokens;\n\n return mergeProviderBody<GenerateContentConfig>(\n {\n ...(systemInstruction ? { systemInstruction } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\n ...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),\n ...(options?.signal ? { abortSignal: options.signal } : {}),\n ...this.buildTools(options),\n ...this.buildStructuredOutput(options?.responseSchema),\n ...this.buildThinking(options?.reasoning),\n },\n options,\n );\n }\n\n /**\n * Translate the neutral `reasoning` option into Gemini's\n * `thinkingConfig`. `reasoning.maxTokens` maps directly to\n * `thinkingBudget` (token cap on the thinking phase); when only\n * `reasoning.effort` is given it is bucketed into a budget. Emitted\n * only when the model is `reasoning`-capable — a `false` capability\n * (config override) drops it so a non-thinking model never receives\n * an unsupported `thinkingConfig`.\n *\n * Gemini's `thinkingBudget` semantics: `0` disables thinking, `-1`\n * lets the model decide automatically. A positive value caps the\n * thinking tokens. The neutral `effort: \"none\"` (\"run without\n * reasoning\") maps to `thinkingBudget: 0`.\n */\n private buildThinking(\n reasoning: ModelCallOptions[\"reasoning\"],\n ): Pick<GenerateContentConfig, \"thinkingConfig\"> {\n if (!reasoning || !this.capabilities.reasoning) {\n return {};\n }\n\n // `effort: \"none\"` = explicit \"run without reasoning\". Gemini disables\n // thinking with `thinkingBudget: 0` (its native off switch), so emit\n // that rather than omitting the config — an omitted config lets a\n // thinking model reason at its default budget.\n if (reasoning.effort === \"none\") {\n return { thinkingConfig: { thinkingBudget: 0 } };\n }\n\n const thinkingBudget =\n reasoning.maxTokens ??\n (reasoning.effort ? EFFORT_THINKING_BUDGET[reasoning.effort] : undefined);\n\n if (thinkingBudget === undefined) {\n return {};\n }\n\n return { thinkingConfig: { thinkingBudget } };\n }\n\n /**\n * Spread-friendly tools fragment. Empty object when no tools were\n * supplied so the caller can unconditionally spread it. Function tools\n * come first; `ai.providerTool({ provider: \"google\" })` definitions\n * (`{ googleSearch: {} }`, `{ codeExecution: {} }`, `{ urlContext: {} }`,\n * …) follow verbatim as their own `Tool` entries. Other providers'\n * definitions are skipped with a warning.\n */\n private buildTools(options: ModelCallOptions | undefined): Pick<GenerateContentConfig, \"tools\"> {\n const { tools, definitions } = selectProviderTools(options, {\n accepts: [GOOGLE_WIRE_FORMAT, this.provider],\n module: LOG_MODULE,\n model: this.name,\n logger: this.logger,\n });\n const mapped: Tool[] = [...(toGoogleTools(tools) ?? []), ...(definitions as unknown as Tool[])];\n\n return mapped.length > 0 ? { tools: mapped } : {};\n }\n\n /**\n * Translate the neutral `responseSchema` into Gemini's native JSON\n * structured output (`responseMimeType: \"application/json\"` +\n * `responseJsonSchema`, which takes a raw JSON Schema directly).\n * Emitted only when the model is `structuredOutput`-capable and the\n * schema is an object root — otherwise the agent's soft prompt hint\n * + client-side `validate()` carry shape.\n */\n private buildStructuredOutput(\n responseSchema: Record<string, unknown> | undefined,\n ): Pick<GenerateContentConfig, \"responseMimeType\" | \"responseJsonSchema\"> {\n if (!responseSchema || !this.capabilities.structuredOutput) {\n return {};\n }\n\n if (responseSchema.type !== \"object\" || typeof responseSchema.properties !== \"object\") {\n return {};\n }\n\n return {\n responseMimeType: \"application/json\",\n responseJsonSchema: responseSchema,\n };\n }\n\n /**\n * Reshape Gemini's function-call content parts into the neutral\n * `ModelToolCallRequest[]`. Returns `undefined` when the model\n * requested no functions so callers can branch on presence.\n *\n * Reads `candidates[0].content.parts` directly rather than the\n * `response.functionCalls` getter: the getter discards the\n * part-level `thoughtSignature`, and Gemini \"thinking\" models 400\n * the follow-up turn if that signature is not echoed back. See\n * `partToToolCall`.\n */\n private extractToolCalls(response: GenerateContentResponse): ModelToolCallRequest[] | undefined {\n const parts = response.candidates?.[0]?.content?.parts ?? [];\n const toolCalls = parts\n .map((part) => this.partToToolCall(part))\n .filter((call): call is ModelToolCallRequest => call !== undefined);\n\n return toolCalls.length > 0 ? toolCalls : undefined;\n }\n\n /**\n * Map a single Gemini `Part` to a neutral `ModelToolCallRequest`,\n * or `undefined` when the part is not a function call. The part's\n * `thoughtSignature` (opaque, set by thinking models) is carried on\n * `providerMetadata` so `toGoogleContents` can replay it on the\n * assistant turn — Gemini rejects the next request without it.\n */\n private partToToolCall(part: Part): ModelToolCallRequest | undefined {\n if (!part.functionCall) {\n return undefined;\n }\n\n const call = part.functionCall;\n\n return {\n // The Gemini Developer API does not assign function-call ids\n // (only Vertex parallel-calling does). Fall back to the function\n // name so the neutral `toolCallId` is non-empty and the echoed\n // `functionResponse.name` resolves — Gemini matches a result to\n // its call by name. See decisions §49.\n id: call.id ?? call.name ?? \"\",\n name: call.name ?? \"\",\n input: (call.args ?? {}) as Record<string, unknown>,\n ...(part.thoughtSignature\n ? { providerMetadata: { thoughtSignature: part.thoughtSignature } }\n : {}),\n };\n }\n\n /**\n * Normalize Gemini's `usageMetadata` into the neutral `Usage` shape\n * via the shared {@link applyGoogleUsage} mapper (the same one the\n * streaming loop and the Gemini image model use). Absent usage\n * collapses to zeros.\n */\n private extractUsage(response: GenerateContentResponse): Usage {\n const usage: Usage = { input: 0, output: 0, total: 0 };\n\n if (response.usageMetadata) {\n applyGoogleUsage(usage, response.usageMetadata);\n }\n\n return usage;\n }\n\n /**\n * Wrap a thrown provider error into the typed `AIError` hierarchy\n * and emit the standard error log line before it propagates.\n */\n private logAndWrap(thrown: unknown) {\n const wrapped = wrapGoogleError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n","import { GoogleGenAI } from \"@google/genai\";\nimport type {\n EmbedderContract,\n ImageModelContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n} from \"@warlock.js/ai\";\nimport { approximateTokenCount } from \"@warlock.js/ai\";\nimport type {\n GoogleEmbedderConfig,\n GoogleImageConfig,\n GoogleModelConfig,\n GoogleSDKConfig,\n} from \"./config.type\";\nimport { GoogleEmbedder } from \"./embedder\";\nimport { GeminiImageModel } from \"./gemini-image\";\nimport { GoogleImageModel } from \"./image\";\nimport { googlePricingFor } from \"./known-pricing\";\nimport { GoogleModel } from \"./model\";\n\n/**\n * Pick the transport for an image model id.\n *\n * `ai.models.generateImages` calls `{model}:predict`, and a `gemini-`\n * id sent there comes back `404 … is not supported for predict`\n * (observed verbatim from Google). `generateContent` is what the SDK\n * itself points `generateImages` users at — its deprecation notice\n * reads \"Please use the generateContent method with image models\n * instead\" — so the id has to choose the transport.\n *\n * Runs in this package establish where a `gemini-` id is ACCEPTED, not\n * what it returns: on this transport such an id got as far as a quota\n * error (HTTP 429) instead of the 404. That an image\n * comes back end-to-end once billing is enabled is reported by the\n * maintainer from a locally linked build, not measured here. Whether\n * these models report token usage is still unknown.\n *\n * This is ROUTING, not validation — no id is refused here. An id this\n * function does not recognize takes the `generateImages` route, the\n * only route that existed before Gemini image support landed, so every\n * id that reached Google before still reaches Google the same way and\n * still fails (or succeeds) at the provider.\n *\n * A leading `models/` resource prefix is tolerated, matching the id\n * shapes `inferVisionCapability` already accepts\n * (`models/gemini-1.5-flash-001`).\n */\nfunction usesGeminiImageTransport(name: string): boolean {\n return name\n .toLowerCase()\n .replace(/^models\\//, \"\")\n .startsWith(\"gemini-\");\n}\n\n/**\n * Google Gemini-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for Gemini models via the\n * `@google/genai` SDK. A single `GoogleSDK` holds one live\n * `GoogleGenAI` client, shared by every `ModelContract` /\n * `EmbedderContract` it produces. Construct one SDK per\n * account/project and reuse it everywhere.\n *\n * **Responsibility.**\n * - Owns: a long-lived `GoogleGenAI` client (auth, Vertex vs Gemini\n * API) and its lifetime. Factory for `GoogleModel` /\n * `GoogleEmbedder` instances sharing that client.\n * - Does NOT own: anything per-call — those live in `GoogleModel` /\n * `GoogleEmbedder` and the agent runtime.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"), fronted by FP usage like the other adapters.\n *\n * @example\n * const google = new GoogleSDK({ apiKey: process.env.GEMINI_API_KEY! });\n * const model = google.model({ name: \"gemini-2.5-flash\", temperature: 0.7 });\n * const embedder = google.embedder({ name: \"gemini-embedding-001\" });\n */\nexport class GoogleSDK implements SDKAdapterContract {\n private readonly ai: GoogleGenAI;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: GoogleSDKConfig) {\n const { provider, pricing, ...clientOptions } = config;\n\n this.ai = new GoogleGenAI(clientOptions);\n this.provider = provider ?? \"google\";\n this.pricing = pricing;\n }\n\n /**\n * Build a `GoogleModel` bound to this SDK's client. Each call\n * returns a fresh instance; all instances share the underlying\n * `GoogleGenAI` client. The SDK's `provider` label is forwarded.\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise the\n * SDK-level registry entry keyed by `config.name`; otherwise the\n * built-in list price (`GOOGLE_MODEL_PRICING`); otherwise `undefined`\n * (no cost computed).\n */\n public model(config: GoogleModelConfig): ModelContract {\n const resolvedPricing =\n config.pricing ?? this.pricing?.[config.name] ?? googlePricingFor(config.name);\n const resolvedConfig: GoogleModelConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new GoogleModel(this.ai, resolvedConfig, this.provider);\n }\n\n /**\n * Rough token-count estimate. Uses the character-heuristic\n * (`approximateTokenCount`) from the core package — Gemini's\n * `countTokens` is a network round-trip; `count()` is intentionally\n * offline. Good for budgeting/quota guards, not billing.\n */\n public async count(text: string, _model?: string): Promise<number> {\n return approximateTokenCount(text);\n }\n\n /**\n * Build a `GoogleEmbedder` bound to this SDK's client.\n *\n * @example\n * const embedder = google.embedder({ name: \"gemini-embedding-001\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n */\n public embedder(config: GoogleEmbedderConfig): EmbedderContract {\n return new GoogleEmbedder(this.ai, config, this.provider);\n }\n\n /**\n * Build an image model bound to this SDK's client for use with\n * `ai.image({ model, prompt })`. `config.name` decides the transport\n * (see {@link usesGeminiImageTransport}) — a `gemini-` id gets the\n * `generateContent` implementation, everything else the Imagen\n * `generateImages` one. No id is rejected locally either way, so an\n * unsupported model fails at Google, not here.\n *\n * The two differ in what usage they can report, which is what the\n * caller must price for: the Imagen path always returns a zero token\n * `Usage` (Imagen reports none — price with `{ perImage }`), while the\n * Gemini path passes through whatever `usageMetadata` Google attaches\n * (price with `{ input, output }` when tokens come back).\n *\n * Pricing resolution mirrors `model()`: per-model `config.pricing`\n * wins, otherwise the SDK-level registry entry keyed by `config.name`,\n * otherwise `undefined`.\n *\n * @example\n * const imagen = google.image({ name: \"imagen-4.0-generate-001\" });\n * const gemini = google.image({ name: \"gemini-3.1-flash-lite-image\" });\n * const { data } = await ai.image({ model: gemini, prompt: \"a red bicycle\" });\n */\n public image(config: GoogleImageConfig): ImageModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: GoogleImageConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n if (usesGeminiImageTransport(config.name)) {\n return new GeminiImageModel(this.ai, resolvedConfig, this.provider);\n }\n\n return new GoogleImageModel(this.ai, resolvedConfig, this.provider);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["safeJsonParse","InvalidRequestError","extractJsonSchema","AIError","ProviderTimeoutError","ProviderAuthError","ProviderRateLimitError","ContextLengthExceededError","InvalidRequestError","ProviderOverloadedError","ProviderError","ApiError","LOG_MODULE","log","LOG_MODULE","log","ContentFilterError","ProviderError","LOG_MODULE","log","ContentFilterError","ProviderError","InvalidRequestError","log","InvalidRequestError","resolveStreamChunks","selectProviderTools","GoogleGenAI","approximateTokenCount"],"sources":["../../../../../../../packages/ai-google/src/utils/apply-google-usage.ts","../../../../../../../packages/ai-google/src/utils/map-finish-reason.ts","../../../../../../../packages/ai-google/src/utils/to-google-contents.ts","../../../../../../../packages/ai-google/src/utils/to-google-tools.ts","../../../../../../../packages/ai-google/src/utils/wrap-google-error.ts","../../../../../../../packages/ai-google/src/embedder.ts","../../../../../../../packages/ai-google/src/gemini-image.ts","../../../../../../../packages/ai-google/src/image.ts","../../../../../../../packages/ai-google/src/known-pricing.ts","../../../../../../../packages/ai-google/src/utils/grounding.ts","../../../../../../../packages/ai-google/src/provider-options.ts","../../../../../../../packages/ai-google/src/known-vision-models.ts","../../../../../../../packages/ai-google/src/model.ts","../../../../../../../packages/ai-google/src/sdk.ts"],"sourcesContent":["import type { Usage } from \"@warlock.js/ai\";\nimport type { GenerateContentResponse } from \"@google/genai\";\n\n/** Gemini's per-response token accounting block, as the SDK types it. */\nexport type GoogleUsageMetadata = NonNullable<GenerateContentResponse[\"usageMetadata\"]>;\n\n/**\n * Fold a Gemini `usageMetadata` block into a running neutral `Usage`\n * accumulator. Shared by every `generateContent`-backed surface — the\n * chat model's `complete()`, its streaming loop (where the final chunk\n * carries cumulative totals), and the Gemini image model — so one\n * mapping decides what a Gemini token report means package-wide.\n *\n * Cache-read hits (`cachedContentTokenCount`, implicit or explicit\n * context caching) surface as `cachedTokens`; the thinking-phase tokens\n * of a reasoning model (`thoughtsTokenCount`) surface as\n * `reasoningTokens`. Both are emitted only when reported `> 0` so an\n * absent channel leaves the field undefined rather than a false zero.\n *\n * `total` falls back to `input + output` when Google omits\n * `totalTokenCount`.\n *\n * `promptTokenCount` \"includes the number of tokens in the cached\n * content\" (https://ai.google.dev/api/generate-content#UsageMetadata), so\n * the usage is marked `inputIncludesCache: true`.\n */\nexport function applyGoogleUsage(usage: Usage, raw: GoogleUsageMetadata): void {\n usage.inputIncludesCache = true;\n usage.input = raw.promptTokenCount ?? usage.input;\n usage.output = raw.candidatesTokenCount ?? usage.output;\n usage.total = raw.totalTokenCount ?? usage.input + usage.output;\n\n const cached = raw.cachedContentTokenCount;\n\n if (cached && cached > 0) {\n usage.cachedTokens = cached;\n }\n\n const reasoning = raw.thoughtsTokenCount;\n\n if (reasoning && reasoning > 0) {\n usage.reasoningTokens = reasoning;\n }\n}\n","import type { FinishReason } from \"@warlock.js/ai\";\n\nconst finishReasonMap: Record<string, FinishReason> = {\n STOP: \"stop\",\n MAX_TOKENS: \"length\",\n};\n\n/**\n * Map Gemini's `FinishReason` enum value to the normalized\n * `FinishReason` union.\n *\n * `STOP` is the natural terminal. `MAX_TOKENS` maps to `length`.\n * Everything else — `SAFETY`, `RECITATION`, `BLOCKLIST`,\n * `PROHIBITED_CONTENT`, `SPII`, `MALFORMED_FUNCTION_CALL`,\n * `UNEXPECTED_TOOL_CALL`, `LANGUAGE`, `OTHER`,\n * `FINISH_REASON_UNSPECIFIED`, `null`, or any future value — falls\n * through to `\"error\"`.\n *\n * Note: Gemini reports `STOP` even when the turn ended in a function\n * call (it has no `tool_use` reason). `GoogleModel` overrides the\n * mapped reason to `\"tool_calls\"` when the response carries function\n * calls — this map intentionally stays purely about the raw signal.\n *\n * @example\n * mapFinishReason(\"STOP\"); // \"stop\"\n * mapFinishReason(\"MAX_TOKENS\"); // \"length\"\n * mapFinishReason(\"SAFETY\"); // \"error\"\n * mapFinishReason(undefined); // \"error\"\n */\nexport function mapFinishReason(raw: string | null | undefined): FinishReason {\n return finishReasonMap[raw ?? \"\"] ?? \"error\";\n}\n","import { InvalidRequestError, safeJsonParse, type ContentPart, type Message } from \"@warlock.js/ai\";\nimport type { Content, Part } from \"@google/genai\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for Gemini's\n * `generateContent`: the system prompt is hoisted to a separate\n * `systemInstruction` string (Gemini has no `\"system\"` role — content\n * roles must be `\"user\"` or `\"model\"`), and the remaining turns map to\n * `Content[]`.\n */\nexport type GoogleContents = {\n systemInstruction: string | undefined;\n contents: Content[];\n};\n\n/**\n * Convert vendor-neutral `Message[]` into Gemini's request shape.\n *\n * Gemini specifics this function absorbs:\n *\n * 1. **No `system` role.** System messages concatenate into the\n * separate `systemInstruction` config field.\n * 2. **Role names differ.** Neutral `assistant` → Gemini `\"model\"`;\n * `user` stays `\"user\"`.\n * 3. **Tool results are `user` turns.** A neutral `tool` message\n * becomes a `\"user\"` content with a single `functionResponse` part.\n * 4. **Tool calls are `functionCall` parts.** An assistant message\n * with `toolCalls` becomes a `\"model\"` content: an optional leading\n * `text` part followed by one `functionCall` part per call.\n * 5. **Adjacent same-role turns merge.** Consecutive contents with the\n * same role become one content with their parts in order, so the\n * results of parallel tool calls travel as one `user` turn (as the\n * `@google/genai` SDK's own automatic function calling sends them)\n * and a compaction summary next to a same-role turn does not break\n * the user/model alternation.\n *\n * @example\n * const { systemInstruction, contents } = toGoogleContents([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n */\nexport function toGoogleContents(messages: Message[]): GoogleContents {\n const systemParts: string[] = [];\n const contents: Content[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n systemParts.push(stringifyContent(message.content));\n\n continue;\n }\n\n if (message.role === \"tool\") {\n contents.push({\n role: \"user\",\n parts: [\n {\n // Gemini matches a `functionResponse` to its `functionCall`\n // by `name` (the Developer API has no call ids). `name` is\n // the neutral `toolCallId`, which `GoogleModel` set to the\n // function name. The wire `id` is intentionally omitted —\n // an empty/synthetic id is rejected as an invalid argument.\n functionResponse: {\n name: message.toolCallId ?? \"\",\n response: toResponseObject(stringifyContent(message.content)),\n },\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const parts: Part[] = [];\n const text = stringifyContent(message.content);\n\n if (text) {\n parts.push({ text });\n }\n\n for (const toolCall of message.toolCalls) {\n // Replay the opaque `thoughtSignature` Gemini attached to this\n // function call on the original turn. Thinking models reject\n // the follow-up request with a 400 if the signature is missing\n // from the echoed `functionCall` part. Captured by\n // `GoogleModel.partToToolCall` into `providerMetadata`.\n const thoughtSignature = toolCall.providerMetadata?.thoughtSignature;\n\n parts.push({\n ...(typeof thoughtSignature === \"string\" ? { thoughtSignature } : {}),\n // `id` omitted deliberately — Gemini Developer API function\n // calls have no ids; echoing an empty/synthetic one is\n // rejected as an invalid argument. Matched by `name`.\n functionCall: {\n name: toolCall.name,\n args: (toolCall.input ?? {}) as Record<string, unknown>,\n },\n });\n }\n\n contents.push({ role: \"model\", parts });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n contents.push({ role: \"user\", parts: message.content.map(toGooglePart) });\n\n continue;\n }\n\n contents.push({\n role: message.role === \"assistant\" ? \"model\" : \"user\",\n parts: [{ text: stringifyContent(message.content) }],\n });\n }\n\n return {\n systemInstruction: systemParts.length > 0 ? systemParts.join(\"\\n\\n\") : undefined,\n contents: mergeAdjacentContents(contents),\n };\n}\n\n/**\n * Join consecutive contents that share a role into one, keeping part\n * order: parallel `functionResponse` parts stay in their `functionCall`\n * order, and each `functionCall` keeps its own `thoughtSignature`.\n * Returns new content objects; the input contents are not modified.\n *\n * Source for the single-turn shape: `@google/genai` 2.19.0\n * `Models.generateContent` (automatic function calling) sends every\n * function response of one model turn as a single `{ role: \"user\",\n * parts }` content, in call order.\n */\nfunction mergeAdjacentContents(contents: Content[]): Content[] {\n const merged: Content[] = [];\n\n for (const content of contents) {\n const previous = merged.at(-1);\n\n if (previous && previous.role === content.role) {\n merged[merged.length - 1] = {\n ...previous,\n parts: [...(previous.parts ?? []), ...(content.parts ?? [])],\n };\n\n continue;\n }\n\n merged.push(content);\n }\n\n return merged;\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any\n * other role collapse a `ContentPart[]` to concatenated text. Plain\n * strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Gemini's `functionResponse.response` must be a JSON object. Tool\n * results arrive as a string (usually stringified JSON) — parse it\n * when it is a JSON object, otherwise wrap the raw string under a\n * `result` key so the model always receives a well-formed object.\n */\nfunction toResponseObject(raw: string): Record<string, unknown> {\n const parsed = safeJsonParse<unknown>(raw, undefined);\n\n if (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n\n return { result: raw };\n}\n\n/**\n * Map a resolved `ContentPart` to a Gemini `Part`. All binary\n * modalities — **image, PDF, and audio** — go to a single\n * `inlineData: { mimeType, data }` block; Gemini's multimodal input is\n * media-agnostic and keys off the IANA `mimeType` (`image/png`,\n * `application/pdf`, `audio/mpeg`, …), so one mapping covers every part\n * type the model's capabilities admit. PDF and audio reach this point\n * only when the model declares the matching capability (`google.model`\n * infers `pdf` / `audio` from the multimodal Gemini families); the\n * agent's modality gate throws upfront otherwise, so capability and\n * behavior stay in lockstep.\n *\n * Gemini's `generateContent` does not fetch arbitrary remote URLs (only\n * Files API / GCS URIs via `fileData`), so a neutral `{ url }` source\n * surfaces a typed `InvalidRequestError` upfront — for any modality —\n * rather than a downstream Gemini fault. The agent resolves attachments\n * before this point, so nothing is read or fetched here.\n */\nfunction toGooglePart(part: ContentPart): Part {\n if (part.type === \"text\") {\n return { text: part.text };\n }\n\n if (\"url\" in part.source) {\n throw new InvalidRequestError(\n `Gemini generateContent cannot fetch remote-URL ${part.type} media; supply base64 bytes instead.`,\n );\n }\n\n return {\n inlineData: { mimeType: part.source.mediaType, data: part.source.base64 },\n };\n}\n","import { extractJsonSchema, type ToolConfig } from \"@warlock.js/ai\";\nimport type { Tool } from \"@google/genai\";\n\n/**\n * Convert vendor-neutral `ToolConfig[]` into Gemini's `tools` array —\n * a single `Tool` carrying one `functionDeclarations` entry per tool.\n *\n * The input schema is forwarded via `parametersJsonSchema` (raw JSON\n * Schema, mutually exclusive with Gemini's typed `parameters`).\n * Non-object extractions degrade to a parameterless object so\n * registration never fails.\n *\n * Returns `undefined` when there are no tools so the caller can omit\n * `config.tools` entirely.\n *\n * @example\n * const tools = toGoogleTools([weatherTool]);\n * await ai.models.generateContent({ model, contents, config: { tools } });\n */\nexport function toGoogleTools(\n tools: ToolConfig<unknown, unknown>[] | undefined,\n): Tool[] | undefined {\n if (!tools || tools.length === 0) {\n return undefined;\n }\n\n return [\n {\n functionDeclarations: tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n parametersJsonSchema: toJsonSchema(tool.input),\n })),\n },\n ];\n}\n\n/**\n * Resolve a tool's input schema to a JSON-Schema object. Gemini wants\n * an object root for function parameters; anything else (or a failed\n * extraction) degrades to a parameterless object.\n */\nfunction toJsonSchema(input: ToolConfig<unknown, unknown>[\"input\"]): Record<string, unknown> {\n const schema = extractJsonSchema(input);\n\n if (schema && schema.type === \"object\") {\n return schema;\n }\n\n return { type: \"object\" };\n}\n","import {\n AIError,\n ContextLengthExceededError,\n InvalidRequestError,\n ProviderAuthError,\n ProviderError,\n ProviderOverloadedError,\n ProviderRateLimitError,\n ProviderTimeoutError,\n} from \"@warlock.js/ai\";\nimport { ApiError } from \"@google/genai\";\n\n/**\n * Raw-error fields the wrapper reads off a Gemini SDK error.\n * `@google/genai`'s `ApiError` exposes `status` (HTTP code) +\n * `message`; transport aborts surface as `AbortError` / `ETIMEDOUT`.\n * We duck-type so proxied / re-thrown errors still classify.\n */\ntype GoogleErrorShape = {\n status?: number;\n message?: string;\n name?: string;\n code?: string;\n};\n\n/**\n * Wrap any thrown value caught inside the Gemini adapter into the\n * appropriate `@warlock.js/ai` `AIError` subclass.\n *\n * **Dispatch strategy.** Gemini has no machine error `code`; the\n * signals are the HTTP `status` and the canonical status phrase Google\n * embeds in `message` (`PERMISSION_DENIED`, `RESOURCE_EXHAUSTED`,\n * `INVALID_ARGUMENT`, …). Dispatch keys on `status`, using the message\n * phrase as the tie-breaker for the two 400 sub-cases\n * (context-length vs generic) and for status-less auth/quota errors.\n *\n * `AIError` instances pass through unchanged so `catch/throw wrap(e)`\n * pipelines never double-wrap.\n *\n * @example\n * try {\n * return await this.ai.models.generateContent(...);\n * } catch (thrown) {\n * throw wrapGoogleError(thrown);\n * }\n */\nexport function wrapGoogleError(thrown: unknown): AIError {\n if (thrown instanceof AIError) {\n return thrown;\n }\n\n const shape = toShape(thrown);\n const context = buildContext(shape);\n const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));\n // \"If your request fails with a 400 or 500 error, you won't be charged\n // for the tokens used.\" — https://ai.google.dev/gemini-api/docs/billing\n // (fetched 2026-10-17). Every other failure keeps the default.\n const billed = shape.status === 400 || shape.status === 500 ? false : undefined;\n\n if (isTimeout(shape)) {\n return new ProviderTimeoutError(message, { cause: thrown, context, billed });\n }\n\n if (\n shape.status === 401 ||\n shape.status === 403 ||\n /permission_denied|api key not valid|unauthenticated/i.test(message)\n ) {\n return new ProviderAuthError(message, { cause: thrown, context, billed });\n }\n\n if (shape.status === 429 || /resource_exhausted|quota/i.test(message)) {\n return new ProviderRateLimitError(message, { cause: thrown, context, billed });\n }\n\n if (shape.status === 400) {\n if (/token count|context length|exceeds the maximum|input is too long/i.test(message)) {\n return new ContextLengthExceededError(message, { cause: thrown, context, billed });\n }\n\n return new InvalidRequestError(message, { cause: thrown, context, billed });\n }\n\n if (shape.status === 404 || isClientStatus(shape.status)) {\n return new InvalidRequestError(message, { cause: thrown, context, billed });\n }\n\n // 503 UNAVAILABLE — \"temporarily overloaded or down\"\n // (https://ai.google.dev/gemini-api/docs/api-errors).\n if (shape.status === 503 || /\\bUNAVAILABLE\\b/.test(message) || /overloaded/i.test(message)) {\n return new ProviderOverloadedError(message, { cause: thrown, context, billed });\n }\n\n return new ProviderError(message, { cause: thrown, context, billed });\n}\n\n/**\n * Read the raw error shape. The Gemini SDK's `ApiError` carries a\n * numeric `status`; flattened/proxied errors may carry it (or `code`)\n * loosely.\n */\nfunction toShape(thrown: unknown): GoogleErrorShape {\n if (thrown instanceof ApiError) {\n return { status: thrown.status, message: thrown.message, name: thrown.name };\n }\n\n if (typeof thrown === \"object\" && thrown !== null) {\n const raw = thrown as Record<string, unknown>;\n\n return {\n status: typeof raw.status === \"number\" ? raw.status : undefined,\n message: typeof raw.message === \"string\" ? raw.message : undefined,\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n code: typeof raw.code === \"string\" ? raw.code : undefined,\n };\n }\n\n return {};\n}\n\n/**\n * Decide whether the error is a timeout. Gemini maps gateway timeouts\n * to HTTP 504 (`DEADLINE_EXCEEDED`); transport aborts surface as\n * `AbortError` / `ETIMEDOUT` / `ECONNABORTED`.\n */\nfunction isTimeout(shape: GoogleErrorShape): boolean {\n if (shape.status === 504) {\n return true;\n }\n\n if (shape.name === \"AbortError\" || /deadline_exceeded/i.test(shape.message ?? \"\")) {\n return true;\n }\n\n return shape.code === \"ETIMEDOUT\" || shape.code === \"ECONNABORTED\";\n}\n\n/** True for HTTP 4xx — a client-side request problem, not a server fault. */\nfunction isClientStatus(status: number | undefined): boolean {\n return typeof status === \"number\" && status >= 400 && status < 500;\n}\n\n/** Attach the diagnostic fields to `error.context`. */\nfunction buildContext(shape: GoogleErrorShape): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n\n if (shape.status !== undefined) {\n context.status = shape.status;\n }\n\n if (shape.name) {\n context.code = shape.name;\n }\n\n return context;\n}\n","import {\n type EmbeddingBatchResult,\n type EmbeddingResult,\n type EmbeddingUsage,\n type EmbedderContract,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type { EmbedContentResponse, GoogleGenAI } from \"@google/genai\";\nimport type { GoogleEmbedderConfig } from \"./config.type\";\nimport { wrapGoogleError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.google\";\n\n/**\n * Token usage is not returned by Gemini's `embedContent`, so every\n * embedding result reports a zeroed `EmbeddingUsage` (honest absence,\n * not a fabricated estimate).\n */\nconst NO_USAGE: EmbeddingUsage = { promptTokens: 0, totalTokens: 0 };\n\n/**\n * Google Gemini-backed implementation of `EmbedderContract`\n * (`gemini-embedding-001`, `text-embedding-004`, …) via\n * `models.embedContent`.\n *\n * **Role.** Converts text into floating-point vectors. Standalone\n * primitive — unrelated to generateContent / tools / the agent loop.\n *\n * **Batch is native.** Gemini's `embedContent` accepts an array of\n * inputs and returns embeddings in the same order, so `embedMany` is\n * a single request (unlike the Bedrock/Titan adapter, which has to\n * loop).\n *\n * **No usage.** Gemini's embed endpoint returns no token counts;\n * `usage` is always `{ promptTokens: 0, totalTokens: 0 }`.\n *\n * **Dimensions.** When no `dimensions` override is given,\n * `this.dimensions` starts at `0` and is populated from the first\n * response's vector length, then cached. Passing `dimensions`\n * forwards Gemini's `outputDimensionality` truncation hint and sets\n * the initial value immediately.\n *\n * @example\n * const embedder = new GoogleEmbedder(ai, { name: \"gemini-embedding-001\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n * const { vectors } = await embedder.embedMany([\"doc 1\", \"doc 2\"]);\n */\nexport class GoogleEmbedder implements EmbedderContract {\n public readonly name: string;\n public readonly provider: string;\n public dimensions: number;\n\n private readonly ai: GoogleGenAI;\n private readonly configuredDimensions: number | undefined;\n private readonly logger: Logger = log;\n\n public constructor(ai: GoogleGenAI, config: GoogleEmbedderConfig, provider: string = \"google\") {\n this.ai = ai;\n this.name = config.name;\n this.provider = provider;\n this.configuredDimensions = config.dimensions;\n this.dimensions = config.dimensions ?? 0;\n }\n\n public async embed(input: string): Promise<EmbeddingResult> {\n const vectors = await this.request([input]);\n\n return { vector: vectors[0], dimensions: this.dimensions, usage: NO_USAGE };\n }\n\n public async embedMany(inputs: string[]): Promise<EmbeddingBatchResult> {\n const vectors = await this.request(inputs);\n\n return { vectors, dimensions: this.dimensions, usage: NO_USAGE };\n }\n\n /**\n * Shared transport: one `embedContent` call for the whole batch,\n * wrap provider errors, cache `dimensions` from the first vector,\n * and return the raw vectors in input order.\n */\n private async request(inputs: string[]): Promise<number[][]> {\n this.logger.debug(LOG_MODULE, \"embedder.request\", \"embedContent\", {\n model: this.name,\n count: inputs.length,\n });\n\n let response: EmbedContentResponse;\n\n try {\n response = await this.ai.models.embedContent({\n model: this.name,\n contents: inputs,\n ...(this.configuredDimensions !== undefined\n ? { config: { outputDimensionality: this.configuredDimensions } }\n : {}),\n });\n } catch (thrown) {\n const wrapped = wrapGoogleError(thrown);\n\n this.logger.error(LOG_MODULE, \"embedder.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const vectors = (response.embeddings ?? []).map((embedding) => embedding.values ?? []);\n\n if (this.dimensions === 0 && vectors[0]) {\n this.dimensions = vectors[0].length;\n }\n\n this.logger.debug(LOG_MODULE, \"embedder.response\", \"embedContent returned\", {\n count: vectors.length,\n dimensions: this.dimensions,\n });\n\n return vectors;\n }\n}\n","import {\n ContentFilterError,\n ProviderError,\n type AIError,\n type GeneratedImage,\n type ImageGenerationOptions,\n type ImageGenerationResponse,\n type ImageModelContract,\n type ImageModelPricing,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type {\n GenerateContentConfig,\n GenerateContentResponse,\n GoogleGenAI,\n ImageConfig,\n Part,\n} from \"@google/genai\";\nimport type { GoogleImageConfig } from \"./config.type\";\nimport { applyGoogleUsage, wrapGoogleError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.google\";\n\n/**\n * Response modalities requested when the caller names none.\n *\n * `IMAGE` is the modality this adapter extracts; `TEXT` rides along so\n * a model that narrates what it drew is not answering outside the set\n * it was granted (the narration is then dropped — only inline image\n * parts become `GeneratedImage`s).\n *\n * *Unverified:* which pairing any individual Gemini image model\n * requires is not established here — no spec or run in this package\n * touches the live API. `options.responseModalities` replaces this list\n * verbatim for a model that wants something else.\n */\nconst DEFAULT_RESPONSE_MODALITIES = [\"TEXT\", \"IMAGE\"];\n\n/**\n * Gemini `finishReason` values that mean generation was stopped by a\n * safety / policy rule rather than by the model simply not drawing.\n * Taken from the `FinishReason` enum in `@google/genai`'s own type\n * declarations, whose doc comments describe each of these as content\n * or image generation being \"stopped\" for safety, prohibited content,\n * recitation, blocklist, or SPII.\n */\nconst FILTERED_FINISH_REASONS = new Set([\n \"SAFETY\",\n \"IMAGE_SAFETY\",\n \"PROHIBITED_CONTENT\",\n \"IMAGE_PROHIBITED_CONTENT\",\n \"RECITATION\",\n \"IMAGE_RECITATION\",\n \"BLOCKLIST\",\n \"SPII\",\n]);\n\n/** How much of a text-only answer to quote back inside the error message. */\nconst TEXT_EXCERPT_LIMIT = 200;\n\n/**\n * Gemini-native implementation of `ImageModelContract`, via\n * `ai.models.generateContent` with `config.responseModalities`\n * including `\"IMAGE\"`.\n *\n * **Why a second image adapter.** `GoogleImageModel` calls\n * `ai.models.generateImages`, which the `@google/genai` bundle routes\n * to `{model}:predict` (`generateImages` → `generateImagesInternal` →\n * `formatMap('{model}:predict', …)`). A Gemini image model is not\n * served there: asking for one returns Google's\n * `404 … is not found for API version v1beta, or is not supported for\n * predict`. `generateContent` is the SDK's own named replacement — its\n * runtime deprecation notice for `generateImages` reads \"Please use the\n * generateContent method with image models instead\" — so that is the\n * transport this class speaks, hence a separate class rather than a\n * branch inside `image.ts`.\n *\n * **Same envelope.** Inline image parts are mapped to the identical\n * `GeneratedImage[]` shape `GoogleImageModel` produces, so `ai.image()`\n * callers see no difference between the two paths.\n *\n * **Token usage is passed through, not zeroed.** The Imagen path\n * returns a hard `{ 0, 0, 0 }` because Imagen reports no tokens at all;\n * here, whatever `usageMetadata` Google attaches is mapped by the same\n * {@link applyGoogleUsage} the chat model uses, and only an absent\n * block collapses to zeros. Price accordingly.\n *\n * **No model-id validation.** `config.name` is forwarded to\n * `generateContent` exactly as given; nothing here inspects it. An id\n * Google does not serve fails at Google, wrapped into the typed\n * `AIError` hierarchy — never with a local throw.\n *\n * **Evidence, in two tiers.** No spec in this package calls Google.\n * *Measured here:* a `gemini-*` image id, which 404s on the `predict`\n * transport, reached the model on this one and came back with a quota\n * error (HTTP 429) — the endpoint accepts the id. *Reported by the\n * maintainer:* once billing was enabled on the project, an image came\n * back end-to-end from an application running a locally linked build.\n * *Still unestablished:* whether these models report token usage — no\n * `usageMetadata` from a successful image call has been observed, so\n * the pass-through above is untested against a real response.\n *\n * @example\n * const model = new GeminiImageModel(ai, { name: \"gemini-3.1-flash-lite-image\" });\n * const { images, usage } = await model.generate(\"a red bicycle on a white background\");\n */\nexport class GeminiImageModel implements ImageModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly pricing?: ImageModelPricing;\n\n private readonly ai: GoogleGenAI;\n private readonly logger: Logger = log;\n\n public constructor(ai: GoogleGenAI, config: GoogleImageConfig, provider: string = \"google\") {\n this.ai = ai;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n }\n\n public async generate(\n prompt: string,\n options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n const config = this.buildConfig(options);\n\n this.logger.debug(LOG_MODULE, \"image.request\", \"models.generateContent\", {\n model: this.name,\n responseModalities: config.responseModalities,\n });\n\n let response: GenerateContentResponse;\n\n try {\n // `contents` takes a bare string: the SDK's own `generateContent`\n // example passes one (`contents: 'Why is the sky blue?'`).\n response = await this.ai.models.generateContent({\n model: this.name,\n contents: prompt,\n config,\n });\n } catch (thrown) {\n const wrapped = wrapGoogleError(thrown);\n\n this.logger.error(LOG_MODULE, \"image.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const parts = collectParts(response);\n const images = toGeneratedImages(parts);\n\n if (images.length === 0) {\n throw this.noImageError(response, parts);\n }\n\n const usage: Usage = { input: 0, output: 0, total: 0 };\n\n if (response.usageMetadata) {\n applyGoogleUsage(usage, response.usageMetadata);\n }\n\n this.logger.debug(LOG_MODULE, \"image.response\", \"models.generateContent succeeded\", {\n images: images.length,\n usage,\n });\n\n return { images, usage };\n }\n\n /**\n * Assemble the `GenerateContentConfig` for an image turn: the\n * requested modalities, the image-specific knobs Gemini exposes under\n * `imageConfig`, and the cancellation handle.\n *\n * Three neutral options are deliberately NOT forwarded, because\n * `GenerateContentConfig` / `ImageConfig` in `@google/genai` expose\n * no equivalent for them on this path: `count` (no per-request image\n * count — every inline image part the model does return is mapped),\n * `negativePrompt` (an Imagen-only field), and `format`\n * (`ImageConfig.outputMimeType` is documented \"not supported in\n * Gemini API\"). Fold those intentions into the prompt instead.\n */\n private buildConfig(options: ImageGenerationOptions | undefined): GenerateContentConfig {\n const imageConfig: ImageConfig = {};\n\n if (options?.aspectRatio !== undefined) {\n imageConfig.aspectRatio = options.aspectRatio;\n }\n\n // Provider passthroughs off the neutral options' index signature —\n // forwarded verbatim, never re-spelled, so the value the caller\n // wrote is the value Google rules on. `ImageConfig` documents\n // `imageSize` as `1K`/`2K`/`4K` and `personGeneration` as\n // `ALLOW_ALL`/`ALLOW_ADULT`/`ALLOW_NONE`.\n if (typeof options?.imageSize === \"string\") {\n imageConfig.imageSize = options.imageSize;\n }\n\n if (typeof options?.personGeneration === \"string\") {\n imageConfig.personGeneration = options.personGeneration;\n }\n\n const requested = options?.responseModalities;\n const responseModalities = Array.isArray(requested)\n ? (requested as string[])\n : DEFAULT_RESPONSE_MODALITIES;\n\n return {\n responseModalities,\n ...(Object.keys(imageConfig).length > 0 ? { imageConfig } : {}),\n ...(options?.signal ? { abortSignal: options.signal } : {}),\n };\n }\n\n /**\n * Build the typed error for a response that carried no inline image\n * part. Never a silent empty success: the caller asked for an image\n * and got something else, so the error names what actually came back.\n *\n * - A blocked prompt (`promptFeedback.blockReason`) or a\n * safety/policy `finishReason` → `ContentFilterError` carrying the\n * reason, matching how the Imagen path reports `raiFilteredReason`.\n * - A text-only answer → `ProviderError` quoting the text, so the\n * log says what the model replied instead of guessing.\n * - Anything else → `ProviderError` naming the finish reason and how\n * many parts arrived.\n */\n private noImageError(response: GenerateContentResponse, parts: Part[]): AIError {\n const blockReason = response.promptFeedback?.blockReason;\n\n if (blockReason) {\n return new ContentFilterError(`Gemini blocked the prompt for ${this.name}: ${blockReason}`, {\n reason: blockReason,\n });\n }\n\n const finishReason = response.candidates?.[0]?.finishReason;\n\n if (finishReason && FILTERED_FINISH_REASONS.has(finishReason)) {\n return new ContentFilterError(`Gemini filtered the image for ${this.name}: ${finishReason}`, {\n reason: finishReason,\n });\n }\n\n const text = collectText(parts);\n\n if (text) {\n return new ProviderError(\n `Gemini returned no image for ${this.name} — the response was text only: \"${excerpt(text)}\"`,\n { context: { model: this.name, ...(finishReason ? { finishReason } : {}) } },\n );\n }\n\n return new ProviderError(\n `Gemini returned no image part for ${this.name} (parts: ${parts.length}${\n finishReason ? `, finishReason: ${finishReason}` : \"\"\n }).`,\n { context: { model: this.name, parts: parts.length } },\n );\n }\n}\n\n/**\n * Flatten every candidate's content parts into one list. Read off\n * `candidates[].content.parts` rather than the response's convenience\n * getters: `response.text` covers only the first candidate's text and\n * there is no getter for inline image data at all.\n */\nfunction collectParts(response: GenerateContentResponse): Part[] {\n const parts: Part[] = [];\n\n for (const candidate of response.candidates ?? []) {\n parts.push(...(candidate.content?.parts ?? []));\n }\n\n return parts;\n}\n\n/**\n * Map the inline image parts to the neutral `GeneratedImage[]` — the\n * same `{ type: \"base64\", base64, mediaType }` shape the Imagen path\n * emits, including its `image/png` fallback for a part that arrives\n * without a declared mime type.\n */\nfunction toGeneratedImages(parts: Part[]): GeneratedImage[] {\n const images: GeneratedImage[] = [];\n\n for (const part of parts) {\n const data = part.inlineData?.data;\n\n if (!data) {\n continue;\n }\n\n images.push({\n type: \"base64\",\n base64: data,\n mediaType: part.inlineData?.mimeType ?? \"image/png\",\n });\n }\n\n return images;\n}\n\n/** Join the text parts of a response — what the model said instead of drawing. */\nfunction collectText(parts: Part[]): string {\n return parts\n .map((part) => part.text)\n .filter((text): text is string => typeof text === \"string\" && text.length > 0)\n .join(\" \")\n .trim();\n}\n\n/** Trim a quoted model answer so an error message stays readable. */\nfunction excerpt(text: string): string {\n return text.length > TEXT_EXCERPT_LIMIT ? `${text.slice(0, TEXT_EXCERPT_LIMIT)}…` : text;\n}\n","import {\n ContentFilterError,\n ProviderError,\n type GeneratedImage,\n type ImageGenerationOptions,\n type ImageGenerationResponse,\n type ImageModelContract,\n type ImageModelPricing,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type { GenerateImagesConfig, GoogleGenAI } from \"@google/genai\";\nimport type { GoogleImageConfig } from \"./config.type\";\nimport { wrapGoogleError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.google\";\n\n/** Map a neutral output container hint to an IANA media type. */\nfunction mediaTypeFor(format: string | undefined): string | undefined {\n switch (format) {\n case \"png\":\n return \"image/png\";\n case \"jpeg\":\n case \"jpg\":\n return \"image/jpeg\";\n case \"webp\":\n return \"image/webp\";\n default:\n return undefined;\n }\n}\n\n/**\n * Google Imagen-backed implementation of `ImageModelContract`, via\n * `ai.models.generateImages`. Imagen is per-image-metered and returns\n * base64 image bytes (no hosted URL, no token usage).\n *\n * **No model-id validation.** `config.name` is passed through to\n * `ai.models.generateImages` exactly as given — the constructor never\n * inspects it. Google adds and retires image model ids on its own\n * schedule, so an id this adapter does not recognize is not the\n * adapter's call to refuse; an unsupported id surfaces as a provider\n * error from Google (wrapped into the typed `AIError` hierarchy by\n * `generate()`), not as a local one.\n *\n * **Safety filtering.** When Imagen filters every candidate for safety\n * (`raiFilteredReason`), this surfaces a typed `ContentFilterError`\n * carrying the reason, rather than returning an empty success.\n *\n * @example\n * const model = new GoogleImageModel(ai, { name: \"imagen-4.0-generate-001\" }, \"google\");\n * const { images } = await model.generate(\"a watercolor lighthouse at dawn\");\n */\nexport class GoogleImageModel implements ImageModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly pricing?: ImageModelPricing;\n\n private readonly ai: GoogleGenAI;\n private readonly logger: Logger = log;\n\n public constructor(ai: GoogleGenAI, config: GoogleImageConfig, provider: string = \"google\") {\n this.ai = ai;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n }\n\n public async generate(\n prompt: string,\n options?: ImageGenerationOptions,\n ): Promise<ImageGenerationResponse> {\n const config: GenerateImagesConfig = {};\n\n if (options?.count !== undefined) config.numberOfImages = options.count;\n if (options?.aspectRatio !== undefined) config.aspectRatio = options.aspectRatio;\n if (options?.negativePrompt !== undefined) config.negativePrompt = options.negativePrompt;\n if (options?.signal !== undefined) config.abortSignal = options.signal;\n\n const outputMimeType = mediaTypeFor(options?.format);\n if (outputMimeType !== undefined) config.outputMimeType = outputMimeType;\n\n // Imagen sizing is `imageSize` (\"1K\"/\"2K\") — a distinct concept from\n // OpenAI's WxH `size`, so we honor only an explicit passthrough.\n if (typeof options?.imageSize === \"string\") config.imageSize = options.imageSize;\n if (typeof options?.personGeneration === \"string\") {\n config.personGeneration =\n options.personGeneration as GenerateImagesConfig[\"personGeneration\"];\n }\n\n this.logger.debug(LOG_MODULE, \"image.request\", \"models.generateImages\", {\n model: this.name,\n count: options?.count ?? 1,\n });\n\n let response: Awaited<ReturnType<GoogleGenAI[\"models\"][\"generateImages\"]>>;\n\n try {\n response = await this.ai.models.generateImages({ model: this.name, prompt, config });\n } catch (thrown) {\n const wrapped = wrapGoogleError(thrown);\n\n this.logger.error(LOG_MODULE, \"image.error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n throw wrapped;\n }\n\n const generated = response.generatedImages ?? [];\n const images: GeneratedImage[] = [];\n\n for (const candidate of generated) {\n const bytes = candidate.image?.imageBytes;\n if (!bytes) continue;\n\n images.push({\n type: \"base64\",\n base64: bytes,\n mediaType: candidate.image?.mimeType ?? outputMimeType ?? \"image/png\",\n ...(candidate.enhancedPrompt ? { revisedPrompt: candidate.enhancedPrompt } : {}),\n });\n }\n\n if (images.length === 0) {\n const filtered = generated.find((candidate) => candidate.raiFilteredReason);\n\n if (filtered?.raiFilteredReason) {\n throw new ContentFilterError(\n `Imagen filtered all candidates: ${filtered.raiFilteredReason}`,\n { reason: filtered.raiFilteredReason },\n );\n }\n\n throw new ProviderError(\"Imagen returned no images.\");\n }\n\n this.logger.debug(LOG_MODULE, \"image.response\", \"models.generateImages succeeded\", {\n images: images.length,\n });\n\n // Imagen returns no token usage — honest zero (priced per image).\n return { images, usage: { input: 0, output: 0, total: 0 } };\n }\n}\n","import type { ModelPricing } from \"@warlock.js/ai\";\n\n/**\n * Built-in Gemini API list prices (paid tier, Standard), USD per\n * 1,000,000 tokens, keyed by exact model id. `cachedInput` is the\n * context-cache read rate; the hourly cache-storage charge is not a token\n * rate and is never included.\n *\n * Source: https://ai.google.dev/gemini-api/docs/pricing, fetched\n * 2026-10-17. Only models with one flat token price are listed. Left out\n * on purpose — pass `pricing` yourself for them:\n *\n * - Models priced by prompt length (the Pro models, split at 200k\n * prompt tokens).\n * - Models with a higher audio-input rate (most Flash / Flash-Lite\n * models): `Usage` does not split input by modality.\n * - Models with a scheduled price change (gemini-4-argon, the 3.8 / 3.6\n * Flash models).\n */\nexport const GOOGLE_MODEL_PRICING: Readonly<Record<string, ModelPricing>> = {\n \"gemini-3.5-flash-lite\": { input: 0.3, output: 2.5, cachedInput: 0.03 },\n};\n\n/**\n * Look up the built-in list price for a Gemini model id (exact match),\n * or `undefined` so cost stays unknown rather than wrong.\n *\n * @example\n * googlePricingFor(\"gemini-3.5-flash-lite\"); // { input: 0.3, output: 2.5, cachedInput: 0.03 }\n * googlePricingFor(\"gemini-2.5-pro\"); // undefined — priced by prompt length\n */\nexport function googlePricingFor(modelName: string): ModelPricing | undefined {\n return GOOGLE_MODEL_PRICING[modelName];\n}\n\n/** USD per nonempty unique query for Gemini 3+, before free monthly allowances.\n * Gemini 2.5 and older bill per grounded prompt; do not use this rate for them.\n * https://ai.google.dev/gemini-api/docs/google-search#pricing — verified 2026-10-18. */\nexport const GOOGLE_SERVER_TOOL_PRICING = { webSearch: 0.014 };\n","import type { GenerateContentResponse, GroundingMetadata } from \"@google/genai\";\nimport type { Usage } from \"@warlock.js/ai\";\n\n/** Candidate snapshots are replaced, not added: streamed metadata may repeat.\n * Installed @google/genai 2.19.0 GroundingMetadata.webSearchQueries; REST schema:\n * https://ai.google.dev/api/generate-content#GroundingMetadata\n * Billing ignores empty queries and counts unique queries for Gemini 3+:\n * https://ai.google.dev/gemini-api/docs/google-search#pricing\n * Gemini 2.5 and older bill per grounded prompt; this counter is NOT that unit. */\nexport function applyGrounding(\n usage: Usage,\n snapshots: Map<number, GroundingMetadata>,\n response: GenerateContentResponse,\n): void {\n for (const [position, candidate] of (response.candidates ?? []).entries()) {\n if (candidate.groundingMetadata)\n snapshots.set(candidate.index ?? position, candidate.groundingMetadata);\n }\n const reported = [...snapshots.values()].filter(\n (metadata) => metadata.webSearchQueries !== undefined,\n );\n if (reported.length > 0) {\n const queries = reported.flatMap((metadata) => metadata.webSearchQueries ?? []);\n usage.serverToolUse = {\n ...usage.serverToolUse,\n webSearchRequests: new Set(queries.filter((query) => query !== \"\")).size,\n googleWebSearchQueries: queries.length,\n };\n }\n}\n\n/** Retain chunks, supports, search entry point and queries without flattening them. */\nexport function groundingMetadata(\n snapshots: Map<number, GroundingMetadata>,\n): Record<string, unknown> | undefined {\n return snapshots.size\n ? {\n google: {\n groundingMetadata: [...snapshots].map(([index, metadata]) => ({ index, ...metadata })),\n },\n }\n : undefined;\n}\n","import { InvalidRequestError, type ModelCallOptions } from \"@warlock.js/ai\";\n\n/** Adapter-only fields merged after the neutral request parameters. */\nexport type GoogleCallOptions = { body?: Record<string, unknown> };\n\nconst RESERVED_FIELDS = new Set([\"systemInstruction\", \"tools\", \"abortSignal\", \"httpOptions\"]);\n\nexport function mergeProviderBody<T extends object>(\n built: T,\n options: ModelCallOptions | undefined,\n): T {\n const provider = options?.providerOptions?.google as GoogleCallOptions | undefined;\n const body = provider?.body ?? {};\n const reserved = Object.keys(body).filter((key) => RESERVED_FIELDS.has(key));\n if (reserved.length > 0) {\n throw new InvalidRequestError(\n `providerOptions.google.body cannot set ${reserved.join(\", \")}: use the call's messages, tools and model options.`,\n { context: { fields: reserved } },\n );\n }\n const defaults = built as Record<string, unknown>;\n const overrides = Object.fromEntries(\n Object.entries(body).map(([key, value]) => {\n const previous = defaults[key];\n return [\n key,\n isPlainObject(previous) && isPlainObject(value) ? { ...previous, ...value } : value,\n ];\n }),\n );\n return { ...built, ...overrides };\n}\n\n/** Merge only plain objects; arrays, null and provider scalar values replace the field. */\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== \"object\") return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n","/**\n * Substrings identifying Gemini model ids whose family accepts image\n * input (vision).\n *\n * Every Gemini 1.5, 2.x, and 2.5 model is natively multimodal, as is\n * the legacy `gemini-pro-vision`. Only the original text-only\n * `gemini-pro` / `gemini-1.0-pro` is excluded. A substring match\n * tolerates the date/preview suffixes Google appends\n * (`gemini-2.5-flash-preview-05-20`). Override per-model via\n * `google.model({ name, vision: true | false })`.\n */\nconst VISION_CAPABLE_SUBSTRINGS = [\n \"gemini-1.5\",\n \"gemini-2\",\n \"gemini-exp\",\n \"gemini-pro-vision\",\n \"gemini-flash\",\n];\n\n/**\n * Infer whether a Gemini model id supports vision based on the known\n * multimodal-family substrings. Unknown ids default to `false` so\n * passing an image attachment to an unsupported model surfaces a\n * clear, agent-side capability error instead of an opaque Gemini 400.\n *\n * @example\n * inferVisionCapability(\"gemini-2.5-flash\"); // → true\n * inferVisionCapability(\"gemini-1.5-pro-002\"); // → true\n * inferVisionCapability(\"gemini-1.0-pro\"); // → false\n * inferVisionCapability(\"text-embedding-004\"); // → false\n */\nexport function inferVisionCapability(modelId: string): boolean {\n const normalized = modelId.toLowerCase();\n\n return VISION_CAPABLE_SUBSTRINGS.some((fragment) => normalized.includes(fragment));\n}\n","import {\n attachCost,\n InvalidRequestError,\n resolveStreamChunks,\n selectProviderTools,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type ReasoningEffort,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type {\n GenerateContentConfig,\n GenerateContentResponse,\n GoogleGenAI,\n GroundingMetadata,\n Part,\n Tool,\n} from \"@google/genai\";\nimport { applyGrounding, groundingMetadata } from \"./utils/grounding\";\nimport { mergeProviderBody } from \"./provider-options\";\nimport type { GoogleModelConfig } from \"./config.type\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport {\n applyGoogleUsage,\n mapFinishReason,\n toGoogleContents,\n toGoogleTools,\n wrapGoogleError,\n} from \"./utils\";\n\nconst LOG_MODULE = \"ai.google\";\n\n/** `provider` value of `ai.providerTool()` entries this adapter sends verbatim. */\nconst GOOGLE_WIRE_FORMAT = \"google\";\n\n/**\n * Bucketed `thinkingBudget` (token caps) for the neutral\n * `reasoning.effort` levels when the caller gives no explicit\n * `reasoning.maxTokens`. Gemini 2.5 accepts a positive budget as a cap\n * on the thinking phase; these mirror the spread the OpenAI\n * `reasoning_effort` low/medium/high tiers imply. The installed\n * `@google/genai` `ThinkingConfig` declaration documents that allowed\n * ranges are model-dependent, so retain the existing conservative 24,576\n * cap for the new tiers rather than sending a budget unsupported by Flash.\n */\nconst EFFORT_THINKING_BUDGET: Record<Exclude<ReasoningEffort, \"none\">, number> = {\n minimal: 1024,\n low: 1024,\n medium: 8192,\n high: 24576,\n xhigh: 24576,\n max: 24576,\n};\n\n/**\n * Google Gemini-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and the `@google/genai` SDK\n * (`models.generateContent` / `generateContentStream`).\n *\n * **Responsibility.**\n * - Owns: a long-lived `GoogleGenAI` client + frozen `ModelConfig`\n * (name, temperature, maxTokens) used as per-call defaults.\n * - Owns: translating vendor-neutral `Message[]` / `ToolConfig[]` into\n * Gemini shapes (systemInstruction hoisting, `model` role,\n * `functionCall` / `functionResponse` parts, inline image bytes) on\n * the way out, and Gemini's candidate/parts response (text, function\n * calls, finish reason, token usage) back into neutral shapes on the\n * way in.\n * - Does NOT own: dispatching tools, looping, history, retries — those\n * are agent concerns. The model is a per-call protocol adapter.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across calls\"): the `GoogleGenAI` client is reused for the SDK's\n * lifetime.\n *\n * @example\n * import { GoogleGenAI } from \"@google/genai\";\n * const ai = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });\n * const model = new GoogleModel(ai, { name: \"gemini-2.5-flash\" });\n *\n * const myAgent = agent({ model, tools: [searchTool] });\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class GoogleModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly ai: GoogleGenAI;\n private readonly config: GoogleModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(ai: GoogleGenAI, config: GoogleModelConfig, provider: string = \"google\") {\n this.ai = ai;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n const multimodal = config.vision ?? inferVisionCapability(config.name);\n\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? true,\n vision: multimodal,\n // Every Gemini 2.5 model thinks; older families harmlessly ignore\n // an empty thinking budget. Defaulting `true` lets the agent\n // forward reasoning options; an explicit `false` opts a model out.\n reasoning: config.reasoning ?? true,\n // Gemini reports cache-read hits (`cachedContentTokenCount`) on\n // every call via implicit caching, and accepts explicit context\n // caching. Read-side accounting is always honored.\n promptCaching: true,\n // The multimodal Gemini families that accept images also accept\n // audio and PDF/document parts. Mirror the vision inference unless\n // explicitly overridden.\n audio: config.audio ?? multimodal,\n pdf: config.pdf ?? multimodal,\n };\n }\n\n /**\n * Single-shot completion. Sends the full message list to\n * `generateContent`, waits for the terminal response, and reshapes\n * it into a vendor-neutral `ModelResponse`. Per-call `options`\n * override the instance defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting generateContent call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n const { systemInstruction, contents } = toGoogleContents(messages);\n\n let response: GenerateContentResponse;\n\n try {\n response = await this.ai.models.generateContent({\n model: this.name,\n contents,\n config: this.buildConfig(systemInstruction, options),\n });\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const toolCalls = this.extractToolCalls(response);\n const finishReason = toolCalls\n ? \"tool_calls\"\n : mapFinishReason(response.candidates?.[0]?.finishReason);\n const usage = this.extractUsage(response);\n const grounding = new Map<number, GroundingMetadata>();\n applyGrounding(usage, grounding, response);\n const providerMetadata = groundingMetadata(grounding);\n attachCost(usage, this.pricing);\n\n this.logger.debug(LOG_MODULE, \"response\", \"generateContent call succeeded\", {\n finishReason,\n usage,\n });\n\n return {\n ...(providerMetadata ? { providerMetadata } : {}),\n content: response.text ?? \"\",\n finishReason,\n usage,\n toolCalls,\n };\n }\n\n /** Count native input tokens with the same converted content, system and tools. */\n public async countTokens(messages: Message[], options?: ModelCallOptions): Promise<number> {\n const { systemInstruction, contents } = toGoogleContents(messages);\n const config = this.buildConfig(systemInstruction, options);\n if (config.cachedContent !== undefined) {\n throw new InvalidRequestError(\n \"The Google SDK countTokens endpoint cannot count a cachedContent reference.\",\n { reason: \"count-unsupported\" },\n );\n }\n try {\n const response = this.ai.vertexai\n ? await this.ai.models.countTokens({\n model: this.name,\n contents,\n config: {\n ...(config.systemInstruction !== undefined\n ? { systemInstruction: config.systemInstruction }\n : {}),\n ...(config.tools !== undefined ? { tools: config.tools as Tool[] } : {}),\n ...(config.abortSignal ? { abortSignal: config.abortSignal } : {}),\n ...(config.httpOptions ? { httpOptions: config.httpOptions } : {}),\n },\n })\n : await this.countDeveloperTokens(contents, config);\n if (response.totalTokens === undefined)\n throw new InvalidRequestError(\"Google countTokens returned no totalTokens.\");\n return response.totalTokens;\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n }\n\n /**\n * REST counts the full generation request, unlike the typed Developer API\n * converter in @google/genai 2.19.0. See https://ai.google.dev/api/tokens.\n * extraBody keeps the SDK's auth, fetch, base URL, retries and cancellation.\n */\n private countDeveloperTokens(\n contents: ReturnType<typeof toGoogleContents>[\"contents\"],\n config: GenerateContentConfig,\n ) {\n config.abortSignal?.throwIfAborted();\n\n const {\n systemInstruction,\n serviceTier,\n tools,\n toolConfig,\n safetySettings,\n cachedContent,\n abortSignal,\n httpOptions,\n ...generationConfig\n } = config;\n // Automatic function calling is SDK-only, never a REST generation field.\n delete generationConfig.automaticFunctionCalling;\n const model = this.name.startsWith(\"models/\") ? this.name : `models/${this.name}`;\n\n return this.ai.models.countTokens({\n model: this.name,\n contents,\n config: {\n abortSignal,\n httpOptions: {\n ...httpOptions,\n extraBody: {\n ...httpOptions?.extraBody,\n // The SDK merges extraBody before JSON serialization. undefined\n // removes its contents field: REST accepts only one input form.\n contents: undefined,\n generateContentRequest: {\n model,\n contents,\n ...(serviceTier !== undefined ? { serviceTier } : {}),\n ...(systemInstruction !== undefined\n ? { systemInstruction: { parts: [{ text: systemInstruction }] } }\n : {}),\n ...(tools !== undefined ? { tools } : {}),\n ...(toolConfig !== undefined ? { toolConfig } : {}),\n ...(safetySettings !== undefined ? { safetySettings } : {}),\n ...(cachedContent !== undefined ? { cachedContent } : {}),\n ...(Object.keys(generationConfig).length > 0 ? { generationConfig } : {}),\n },\n },\n },\n },\n });\n }\n\n /**\n * Incremental streaming completion via `generateContentStream`.\n * Yields neutral `ModelStreamChunk`s — `delta` for text, `tool-call`\n * per function call (Gemini emits a fully-formed call, not partial\n * JSON), and a terminal `done` with the final finish reason + usage.\n *\n * With `options.streamChunks` it also yields `reasoning` for thought\n * parts (only sent when `thinkingConfig.includeThoughts` is on — set it\n * through `providerOptions.google.body`) and `usage` whenever a chunk\n * carries `usageMetadata`. No `tool-args`: calls arrive whole.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting generateContentStream call\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n const { systemInstruction, contents } = toGoogleContents(messages);\n\n let iterable: AsyncGenerator<GenerateContentResponse>;\n\n try {\n iterable = await this.ai.models.generateContentStream({\n model: this.name,\n contents,\n config: this.buildConfig(systemInstruction, options),\n });\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n let rawFinishReason: string | undefined;\n let sawToolCall = false;\n const usage: Usage = { input: 0, output: 0, total: 0 };\n const want = resolveStreamChunks(options);\n const grounding = new Map<number, GroundingMetadata>();\n\n try {\n for await (const chunk of iterable) {\n applyGrounding(usage, grounding, chunk);\n // Thought parts (`thought: true`, sent when `thinkingConfig.includeThoughts`\n // is on) carry the thought summary in `text`; the SDK's `chunk.text`\n // getter already leaves them out of the answer.\n if (want.reasoning) {\n for (const part of chunk.candidates?.[0]?.content?.parts ?? []) {\n if (part.thought === true && typeof part.text === \"string\" && part.text !== \"\") {\n yield { type: \"reasoning\", delta: part.text };\n }\n }\n }\n\n const text = chunk.text;\n\n if (text) {\n yield { type: \"delta\", content: text };\n }\n\n for (const part of chunk.candidates?.[0]?.content?.parts ?? []) {\n const toolCall = this.partToToolCall(part);\n\n if (!toolCall) {\n continue;\n }\n\n sawToolCall = true;\n\n yield {\n type: \"tool-call\",\n id: toolCall.id,\n name: toolCall.name,\n input: toolCall.input,\n ...(toolCall.providerMetadata ? { providerMetadata: toolCall.providerMetadata } : {}),\n };\n }\n\n const candidateFinish = chunk.candidates?.[0]?.finishReason;\n\n if (candidateFinish) {\n rawFinishReason = candidateFinish;\n }\n\n if (chunk.usageMetadata) {\n applyGoogleUsage(usage, chunk.usageMetadata);\n\n if (want.usage) {\n yield { type: \"usage\", usage: { ...usage } };\n }\n }\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const finishReason = sawToolCall ? \"tool_calls\" : mapFinishReason(rawFinishReason);\n\n attachCost(usage, this.pricing);\n\n this.logger.debug(LOG_MODULE, \"response\", \"generateContentStream call succeeded\", {\n finishReason,\n usage,\n });\n\n const providerMetadata = groundingMetadata(grounding);\n yield { type: \"done\", finishReason, usage, ...(providerMetadata ? { providerMetadata } : {}) };\n }\n\n /**\n * Assemble the `GenerateContentConfig` shared by `complete()` and\n * `stream()`: inference params, hoisted system instruction,\n * cancellation signal, and conditional tools + native structured\n * output.\n */\n private buildConfig(\n systemInstruction: string | undefined,\n options: ModelCallOptions | undefined,\n ): GenerateContentConfig {\n const temperature = options?.temperature ?? this.config.temperature;\n const maxOutputTokens = options?.maxTokens ?? this.config.maxTokens;\n\n return mergeProviderBody<GenerateContentConfig>(\n {\n ...(systemInstruction ? { systemInstruction } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\n ...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),\n ...(options?.signal ? { abortSignal: options.signal } : {}),\n ...this.buildTools(options),\n ...this.buildStructuredOutput(options?.responseSchema),\n ...this.buildThinking(options?.reasoning),\n },\n options,\n );\n }\n\n /**\n * Translate the neutral `reasoning` option into Gemini's\n * `thinkingConfig`. `reasoning.maxTokens` maps directly to\n * `thinkingBudget` (token cap on the thinking phase); when only\n * `reasoning.effort` is given it is bucketed into a budget. Emitted\n * only when the model is `reasoning`-capable — a `false` capability\n * (config override) drops it so a non-thinking model never receives\n * an unsupported `thinkingConfig`.\n *\n * Gemini's `thinkingBudget` semantics: `0` disables thinking, `-1`\n * lets the model decide automatically. A positive value caps the\n * thinking tokens. The neutral `effort: \"none\"` (\"run without\n * reasoning\") maps to `thinkingBudget: 0`.\n */\n private buildThinking(\n reasoning: ModelCallOptions[\"reasoning\"],\n ): Pick<GenerateContentConfig, \"thinkingConfig\"> {\n if (!reasoning || !this.capabilities.reasoning) {\n return {};\n }\n\n // `effort: \"none\"` = explicit \"run without reasoning\". Gemini disables\n // thinking with `thinkingBudget: 0` (its native off switch), so emit\n // that rather than omitting the config — an omitted config lets a\n // thinking model reason at its default budget.\n if (reasoning.effort === \"none\") {\n return { thinkingConfig: { thinkingBudget: 0 } };\n }\n\n const thinkingBudget =\n reasoning.maxTokens ??\n (reasoning.effort ? EFFORT_THINKING_BUDGET[reasoning.effort] : undefined);\n\n if (thinkingBudget === undefined) {\n return {};\n }\n\n return { thinkingConfig: { thinkingBudget } };\n }\n\n /**\n * Spread-friendly tools fragment. Empty object when no tools were\n * supplied so the caller can unconditionally spread it. Function tools\n * come first; `ai.providerTool({ provider: \"google\" })` definitions\n * (`{ googleSearch: {} }`, `{ codeExecution: {} }`, `{ urlContext: {} }`,\n * …) follow verbatim as their own `Tool` entries. Other providers'\n * definitions are skipped with a warning.\n */\n private buildTools(options: ModelCallOptions | undefined): Pick<GenerateContentConfig, \"tools\"> {\n const { tools, definitions } = selectProviderTools(options, {\n accepts: [GOOGLE_WIRE_FORMAT, this.provider],\n module: LOG_MODULE,\n model: this.name,\n logger: this.logger,\n });\n const mapped: Tool[] = [...(toGoogleTools(tools) ?? []), ...(definitions as unknown as Tool[])];\n\n return mapped.length > 0 ? { tools: mapped } : {};\n }\n\n /**\n * Translate the neutral `responseSchema` into Gemini's native JSON\n * structured output (`responseMimeType: \"application/json\"` +\n * `responseJsonSchema`, which takes a raw JSON Schema directly).\n * Emitted only when the model is `structuredOutput`-capable and the\n * schema is an object root — otherwise the agent's soft prompt hint\n * + client-side `validate()` carry shape.\n */\n private buildStructuredOutput(\n responseSchema: Record<string, unknown> | undefined,\n ): Pick<GenerateContentConfig, \"responseMimeType\" | \"responseJsonSchema\"> {\n if (!responseSchema || !this.capabilities.structuredOutput) {\n return {};\n }\n\n if (responseSchema.type !== \"object\" || typeof responseSchema.properties !== \"object\") {\n return {};\n }\n\n return {\n responseMimeType: \"application/json\",\n responseJsonSchema: responseSchema,\n };\n }\n\n /**\n * Reshape Gemini's function-call content parts into the neutral\n * `ModelToolCallRequest[]`. Returns `undefined` when the model\n * requested no functions so callers can branch on presence.\n *\n * Reads `candidates[0].content.parts` directly rather than the\n * `response.functionCalls` getter: the getter discards the\n * part-level `thoughtSignature`, and Gemini \"thinking\" models 400\n * the follow-up turn if that signature is not echoed back. See\n * `partToToolCall`.\n */\n private extractToolCalls(response: GenerateContentResponse): ModelToolCallRequest[] | undefined {\n const parts = response.candidates?.[0]?.content?.parts ?? [];\n const toolCalls = parts\n .map((part) => this.partToToolCall(part))\n .filter((call): call is ModelToolCallRequest => call !== undefined);\n\n return toolCalls.length > 0 ? toolCalls : undefined;\n }\n\n /**\n * Map a single Gemini `Part` to a neutral `ModelToolCallRequest`,\n * or `undefined` when the part is not a function call. The part's\n * `thoughtSignature` (opaque, set by thinking models) is carried on\n * `providerMetadata` so `toGoogleContents` can replay it on the\n * assistant turn — Gemini rejects the next request without it.\n */\n private partToToolCall(part: Part): ModelToolCallRequest | undefined {\n if (!part.functionCall) {\n return undefined;\n }\n\n const call = part.functionCall;\n\n return {\n // The Gemini Developer API does not assign function-call ids\n // (only Vertex parallel-calling does). Fall back to the function\n // name so the neutral `toolCallId` is non-empty and the echoed\n // `functionResponse.name` resolves — Gemini matches a result to\n // its call by name. See decisions §49.\n id: call.id ?? call.name ?? \"\",\n name: call.name ?? \"\",\n input: (call.args ?? {}) as Record<string, unknown>,\n ...(part.thoughtSignature\n ? { providerMetadata: { thoughtSignature: part.thoughtSignature } }\n : {}),\n };\n }\n\n /**\n * Normalize Gemini's `usageMetadata` into the neutral `Usage` shape\n * via the shared {@link applyGoogleUsage} mapper (the same one the\n * streaming loop and the Gemini image model use). Absent usage\n * collapses to zeros.\n */\n private extractUsage(response: GenerateContentResponse): Usage {\n const usage: Usage = { input: 0, output: 0, total: 0 };\n\n if (response.usageMetadata) {\n applyGoogleUsage(usage, response.usageMetadata);\n }\n\n return usage;\n }\n\n /**\n * Wrap a thrown provider error into the typed `AIError` hierarchy\n * and emit the standard error log line before it propagates.\n */\n private logAndWrap(thrown: unknown) {\n const wrapped = wrapGoogleError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n","import { GoogleGenAI } from \"@google/genai\";\nimport type {\n EmbedderContract,\n ImageModelContract,\n ModelContract,\n ModelPricing,\n SDKAdapterContract,\n} from \"@warlock.js/ai\";\nimport { approximateTokenCount } from \"@warlock.js/ai\";\nimport type {\n GoogleEmbedderConfig,\n GoogleImageConfig,\n GoogleModelConfig,\n GoogleSDKConfig,\n} from \"./config.type\";\nimport { GoogleEmbedder } from \"./embedder\";\nimport { GeminiImageModel } from \"./gemini-image\";\nimport { GoogleImageModel } from \"./image\";\nimport { googlePricingFor } from \"./known-pricing\";\nimport { GoogleModel } from \"./model\";\n\n/**\n * Pick the transport for an image model id.\n *\n * `ai.models.generateImages` calls `{model}:predict`, and a `gemini-`\n * id sent there comes back `404 … is not supported for predict`\n * (observed verbatim from Google). `generateContent` is what the SDK\n * itself points `generateImages` users at — its deprecation notice\n * reads \"Please use the generateContent method with image models\n * instead\" — so the id has to choose the transport.\n *\n * Runs in this package establish where a `gemini-` id is ACCEPTED, not\n * what it returns: on this transport such an id got as far as a quota\n * error (HTTP 429) instead of the 404. That an image\n * comes back end-to-end once billing is enabled is reported by the\n * maintainer from a locally linked build, not measured here. Whether\n * these models report token usage is still unknown.\n *\n * This is ROUTING, not validation — no id is refused here. An id this\n * function does not recognize takes the `generateImages` route, the\n * only route that existed before Gemini image support landed, so every\n * id that reached Google before still reaches Google the same way and\n * still fails (or succeeds) at the provider.\n *\n * A leading `models/` resource prefix is tolerated, matching the id\n * shapes `inferVisionCapability` already accepts\n * (`models/gemini-1.5-flash-001`).\n */\nfunction usesGeminiImageTransport(name: string): boolean {\n return name\n .toLowerCase()\n .replace(/^models\\//, \"\")\n .startsWith(\"gemini-\");\n}\n\n/**\n * Google Gemini-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for Gemini models via the\n * `@google/genai` SDK. A single `GoogleSDK` holds one live\n * `GoogleGenAI` client, shared by every `ModelContract` /\n * `EmbedderContract` it produces. Construct one SDK per\n * account/project and reuse it everywhere.\n *\n * **Responsibility.**\n * - Owns: a long-lived `GoogleGenAI` client (auth, Vertex vs Gemini\n * API) and its lifetime. Factory for `GoogleModel` /\n * `GoogleEmbedder` instances sharing that client.\n * - Does NOT own: anything per-call — those live in `GoogleModel` /\n * `GoogleEmbedder` and the agent runtime.\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"), fronted by FP usage like the other adapters.\n *\n * @example\n * const google = new GoogleSDK({ apiKey: process.env.GEMINI_API_KEY! });\n * const model = google.model({ name: \"gemini-2.5-flash\", temperature: 0.7 });\n * const embedder = google.embedder({ name: \"gemini-embedding-001\" });\n */\nexport class GoogleSDK implements SDKAdapterContract {\n private readonly ai: GoogleGenAI;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: GoogleSDKConfig) {\n const { provider, pricing, ...clientOptions } = config;\n\n this.ai = new GoogleGenAI(clientOptions);\n this.provider = provider ?? \"google\";\n this.pricing = pricing;\n }\n\n /**\n * Build a `GoogleModel` bound to this SDK's client. Each call\n * returns a fresh instance; all instances share the underlying\n * `GoogleGenAI` client. The SDK's `provider` label is forwarded.\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise the\n * SDK-level registry entry keyed by `config.name`; otherwise the\n * built-in list price (`GOOGLE_MODEL_PRICING`); otherwise `undefined`\n * (no cost computed).\n */\n public model(config: GoogleModelConfig): ModelContract {\n const resolvedPricing =\n config.pricing ?? this.pricing?.[config.name] ?? googlePricingFor(config.name);\n const resolvedConfig: GoogleModelConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new GoogleModel(this.ai, resolvedConfig, this.provider);\n }\n\n /**\n * Rough token-count estimate. Uses the character-heuristic\n * (`approximateTokenCount`) from the core package — Gemini's\n * `countTokens` is a network round-trip; `count()` is intentionally\n * offline. Good for budgeting/quota guards, not billing.\n */\n public async count(text: string, _model?: string): Promise<number> {\n return approximateTokenCount(text);\n }\n\n /**\n * Build a `GoogleEmbedder` bound to this SDK's client.\n *\n * @example\n * const embedder = google.embedder({ name: \"gemini-embedding-001\" });\n * const { vector } = await embedder.embed(\"Hello world\");\n */\n public embedder(config: GoogleEmbedderConfig): EmbedderContract {\n return new GoogleEmbedder(this.ai, config, this.provider);\n }\n\n /**\n * Build an image model bound to this SDK's client for use with\n * `ai.image({ model, prompt })`. `config.name` decides the transport\n * (see {@link usesGeminiImageTransport}) — a `gemini-` id gets the\n * `generateContent` implementation, everything else the Imagen\n * `generateImages` one. No id is rejected locally either way, so an\n * unsupported model fails at Google, not here.\n *\n * The two differ in what usage they can report, which is what the\n * caller must price for: the Imagen path always returns a zero token\n * `Usage` (Imagen reports none — price with `{ perImage }`), while the\n * Gemini path passes through whatever `usageMetadata` Google attaches\n * (price with `{ input, output }` when tokens come back).\n *\n * Pricing resolution mirrors `model()`: per-model `config.pricing`\n * wins, otherwise the SDK-level registry entry keyed by `config.name`,\n * otherwise `undefined`.\n *\n * @example\n * const imagen = google.image({ name: \"imagen-4.0-generate-001\" });\n * const gemini = google.image({ name: \"gemini-3.1-flash-lite-image\" });\n * const { data } = await ai.image({ model: gemini, prompt: \"a red bicycle\" });\n */\n public image(config: GoogleImageConfig): ImageModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: GoogleImageConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n if (usesGeminiImageTransport(config.name)) {\n return new GeminiImageModel(this.ai, resolvedConfig, this.provider);\n }\n\n return new GoogleImageModel(this.ai, resolvedConfig, this.provider);\n }\n}\n"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
1
  import { GoogleGenAIOptions } from "@google/genai";
2
2
  import { EmbedderConfig, ImageModelConfig, ModelConfig, ModelPricing } from "@warlock.js/ai";
3
-
4
3
  //#region ../../packages/ai-google/src/config.type.d.ts
5
4
  /**
6
5
  * Configuration for the Google Gemini SDK adapter.
@@ -32,7 +31,7 @@ import { EmbedderConfig, ImageModelConfig, ModelConfig, ModelPricing } from "@wa
32
31
  * pricing: { "gemini-2.5-flash": { input: 0.3, output: 2.5 } },
33
32
  * });
34
33
  */
35
- type GoogleSDKConfig = GoogleGenAIOptions & {
34
+ export type GoogleSDKConfig = GoogleGenAIOptions & {
36
35
  provider?: string;
37
36
  /**
38
37
  * Per-model USD pricing registry, keyed by model name. Surfaced onto
@@ -49,7 +48,7 @@ type GoogleSDKConfig = GoogleGenAIOptions & {
49
48
  * google.model({ name: "gemini-2.5-flash" });
50
49
  * google.model({ name: "gemini-1.0-pro", vision: false });
51
50
  */
52
- type GoogleModelConfig = ModelConfig & {
51
+ export type GoogleModelConfig = ModelConfig & {
53
52
  /**
54
53
  * Override the auto-inferred vision capability. When omitted, the
55
54
  * adapter checks the model id against the known multimodal Gemini
@@ -101,7 +100,7 @@ type GoogleModelConfig = ModelConfig & {
101
100
  * google.embedder({ name: "gemini-embedding-001" });
102
101
  * google.embedder({ name: "gemini-embedding-001", dimensions: 768 });
103
102
  */
104
- type GoogleEmbedderConfig = EmbedderConfig;
103
+ export type GoogleEmbedderConfig = EmbedderConfig;
105
104
  /**
106
105
  * Per-model configuration for `GoogleSDK.image()`. Mirrors the neutral
107
106
  * {@link ImageModelConfig} — `name` is the image model id, never
@@ -119,7 +118,6 @@ type GoogleEmbedderConfig = EmbedderConfig;
119
118
  * google.image({ name: "imagen-4.0-generate-001", pricing: { perImage: 0.04 } });
120
119
  * google.image({ name: "gemini-3.1-flash-lite-image", pricing: { input: 0.3, output: 30 } });
121
120
  */
122
- type GoogleImageConfig = ImageModelConfig;
121
+ export type GoogleImageConfig = ImageModelConfig;
123
122
  //#endregion
124
- export { GoogleEmbedderConfig, GoogleImageConfig, GoogleModelConfig, GoogleSDKConfig };
125
123
  //# sourceMappingURL=config.type.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.type.d.mts","names":[],"sources":["../../../../../../../packages/ai-google/src/config.type.ts"],"mappings":";;;;;;AAiCA;;;;;;;;;;;;;;AAOuC;AAWvC;;;;;;;;;;;AAwCK;AAaL;KAvEY,eAAA,GAAkB,kBAAA;EAC5B,QAAA;EAsE+C;AAAA;AAmBjD;;;EAnFE,OAAA,GAAU,MAAA,SAAe,YAAA;AAAA;;;;;;;;;KAWf,iBAAA,GAAoB,WAAW;;;;;;;EAOzC,MAAA;;;;;;;;;EASA,gBAAA;;;;;;;;;;;EAWA,SAAA;;;;;;EAMA,KAAA;;;;;;;EAOA,GAAA;AAAA;;;;;;;;;;;KAaU,oBAAA,GAAuB,cAAc;;;;;;;;;;;;;;;;;;KAmBrC,iBAAA,GAAoB,gBAAgB"}
1
+ {"version":3,"file":"config.type.d.mts","names":[],"sources":["../../../../../../../packages/ai-google/src/config.type.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAiCY,kBAAkB;EAC5B;;;;;;EAMA,UAAU,eAAe;;;;;;;;;;YAWf,oBAAoB;;;;;;;EAO9B;;;;;;;;;EASA;;;;;;;;;;;EAWA;;;;;;EAMA;;;;;;;EAOA;;;;;;;;;;;;YAaU,uBAAuB;;;;;;;;;;;;;;;;;;YAmBvB,oBAAoB"}
@@ -1,7 +1,6 @@
1
1
  import { GoogleImageConfig } from "./config.type.mjs";
2
2
  import { GoogleGenAI } from "@google/genai";
3
3
  import { ImageGenerationOptions, ImageGenerationResponse, ImageModelContract, ImageModelPricing } from "@warlock.js/ai";
4
-
5
4
  //#region ../../packages/ai-google/src/gemini-image.d.ts
6
5
  /**
7
6
  * Gemini-native implementation of `ImageModelContract`, via
@@ -49,7 +48,7 @@ import { ImageGenerationOptions, ImageGenerationResponse, ImageModelContract, Im
49
48
  * const model = new GeminiImageModel(ai, { name: "gemini-3.1-flash-lite-image" });
50
49
  * const { images, usage } = await model.generate("a red bicycle on a white background");
51
50
  */
52
- declare class GeminiImageModel implements ImageModelContract {
51
+ export declare class GeminiImageModel implements ImageModelContract {
53
52
  readonly name: string;
54
53
  readonly provider: string;
55
54
  readonly pricing?: ImageModelPricing;
@@ -87,5 +86,4 @@ declare class GeminiImageModel implements ImageModelContract {
87
86
  private noImageError;
88
87
  }
89
88
  //#endregion
90
- export { GeminiImageModel };
91
89
  //# sourceMappingURL=gemini-image.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gemini-image.d.mts","names":[],"sources":["../../../../../../../packages/ai-google/src/gemini-image.ts"],"mappings":";;;;;;;AA2GA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8HsB;;;;;;;;;;cA9HT,gBAAA,YAA4B,kBAAA;EAAA,SACvB,IAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA,GAAU,iBAAA;EAAA,iBAET,EAAA;EAAA,iBACA,MAAA;cAEE,EAAA,EAAI,WAAA,EAAa,MAAA,EAAQ,iBAAA,EAAmB,QAAA;EAOlD,QAAA,CACX,MAAA,UACA,OAAA,GAAU,sBAAA,GACT,OAAA,CAAQ,uBAAA;;;;;;;;;;;;;;UA+DH,WAAA;;;;;;;;;;;;;;UA6CA,YAAA;AAAA"}
1
+ {"version":3,"file":"gemini-image.d.mts","names":[],"sources":["../../../../../../../packages/ai-google/src/gemini-image.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA2Ga,4BAA4B;WACvB;WACA;WACA,UAAU;mBAET;mBACA;EAEjB,YAAmB,IAAI,aAAa,QAAQ,mBAAmB;EAOlD,SACX,gBACA,UAAU,yBACT,QAAQ;;;;;;;;;;;;;;UA+DH;;;;;;;;;;;;;;UA6CA"}
@@ -28,7 +28,7 @@ const DEFAULT_RESPONSE_MODALITIES = ["TEXT", "IMAGE"];
28
28
  * or image generation being "stopped" for safety, prohibited content,
29
29
  * recitation, blocklist, or SPII.
30
30
  */
31
- const FILTERED_FINISH_REASONS = new Set([
31
+ const FILTERED_FINISH_REASONS = /* @__PURE__ */ new Set([
32
32
  "SAFETY",
33
33
  "IMAGE_SAFETY",
34
34
  "PROHIBITED_CONTENT",