@tanstack/ai-gemini 0.10.7 → 0.10.8
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/dist/esm/adapters/audio.d.ts +1 -1
- package/dist/esm/adapters/audio.js +3 -2
- package/dist/esm/adapters/audio.js.map +1 -1
- package/dist/esm/adapters/image.d.ts +1 -1
- package/dist/esm/adapters/image.js +13 -8
- package/dist/esm/adapters/image.js.map +1 -1
- package/dist/esm/adapters/text.d.ts +1 -1
- package/dist/esm/adapters/text.js +45 -25
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/adapters/tts.d.ts +1 -1
- package/dist/esm/adapters/tts.js +6 -5
- package/dist/esm/adapters/tts.js.map +1 -1
- package/dist/esm/image/image-provider-options.js +3 -2
- package/dist/esm/image/image-provider-options.js.map +1 -1
- package/dist/esm/tools/code-execution-tool.js +3 -2
- package/dist/esm/tools/code-execution-tool.js.map +1 -1
- package/dist/esm/tools/computer-use-tool.d.ts +2 -2
- package/dist/esm/tools/computer-use-tool.js +15 -6
- package/dist/esm/tools/computer-use-tool.js.map +1 -1
- package/dist/esm/tools/file-search-tool.js +3 -2
- package/dist/esm/tools/file-search-tool.js.map +1 -1
- package/dist/esm/tools/function-declaration-tool.js +1 -2
- package/dist/esm/tools/function-declaration-tool.js.map +1 -1
- package/dist/esm/tools/google-maps-tool.js +3 -2
- package/dist/esm/tools/google-maps-tool.js.map +1 -1
- package/dist/esm/tools/google-search-retriveal-tool.js +3 -2
- package/dist/esm/tools/google-search-retriveal-tool.js.map +1 -1
- package/dist/esm/tools/google-search-tool.js +3 -2
- package/dist/esm/tools/google-search-tool.js.map +1 -1
- package/dist/esm/tools/url-context-tool.js +3 -2
- package/dist/esm/tools/url-context-tool.js.map +1 -1
- package/package.json +3 -3
- package/src/adapters/audio.ts +2 -2
- package/src/adapters/image.ts +13 -6
- package/src/adapters/text.ts +69 -36
- package/src/adapters/tts.ts +7 -6
- package/src/image/image-provider-options.ts +3 -2
- package/src/tools/code-execution-tool.ts +3 -3
- package/src/tools/computer-use-tool.ts +18 -7
- package/src/tools/file-search-tool.ts +3 -3
- package/src/tools/function-declaration-tool.ts +1 -2
- package/src/tools/google-maps-tool.ts +3 -3
- package/src/tools/google-search-retriveal-tool.ts +3 -3
- package/src/tools/google-search-tool.ts +3 -3
- package/src/tools/url-context-tool.ts +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tts.js","sources":["../../../src/adapters/tts.ts"],"sourcesContent":["import { BaseTTSAdapter } from '@tanstack/ai/adapters'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport { GEMINI_TTS_VOICES } from '../model-meta'\nimport type { GEMINI_TTS_MODELS, GeminiTTSVoice } from '../model-meta'\nimport type { TTSOptions, TTSResult } from '@tanstack/ai'\nimport type { GoogleGenAI, SpeechConfig } from '@google/genai'\nimport type { GeminiClientConfig } from '../utils'\n\n/**\n * Configuration for a single speaker in a multi-speaker dialogue.\n * Supported by Gemini 3.1 Flash TTS Preview and the 2.5 TTS models.\n */\nexport interface GeminiSpeakerVoiceConfig {\n /** A name used in the prompt to refer to this speaker */\n speaker: string\n /** Voice configuration for this speaker */\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: GeminiTTSVoice\n }\n }\n}\n\n/**\n * Provider-specific options for Gemini TTS\n *\n * @experimental Gemini TTS is an experimental feature.\n * @see https://ai.google.dev/gemini-api/docs/speech-generation\n * @see https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-tts-preview\n */\nexport interface GeminiTTSProviderOptions {\n /**\n * Voice configuration for single-speaker TTS.\n * Choose from 30 available voices with different characteristics.\n *\n * Use `multiSpeakerVoiceConfig` instead for dialogues.\n */\n voiceConfig?: {\n prebuiltVoiceConfig?: {\n /**\n * The voice name to use for speech synthesis.\n * @see https://ai.google.dev/gemini-api/docs/speech-generation#voices\n */\n voiceName?: GeminiTTSVoice\n }\n }\n\n /**\n * Multi-speaker voice configuration (up to 2 speakers).\n * Supported by Gemini 3.1 Flash TTS Preview and the 2.5 TTS models.\n *\n * Each speaker's lines in the prompt are prefixed with the name defined\n * here, e.g.:\n *\n * ```text\n * Joe: Hey, how's it going?\n * Jane: Not bad, you?\n * ```\n */\n multiSpeakerVoiceConfig?: {\n speakerVoiceConfigs: Array<GeminiSpeakerVoiceConfig>\n }\n\n /**\n * System instruction for controlling speech style.\n * Use natural language to describe the desired speaking style,\n * pace, tone, accent, or other characteristics.\n *\n * With Gemini 3.1 Flash TTS, you can also use inline audio tags like\n * `[whispering]`, `[laughs]`, `[excited]` directly in the input text\n * to control delivery.\n *\n * @example \"Speak slowly and calmly, as if telling a bedtime story\"\n * @example \"Use an upbeat, enthusiastic tone with moderate pace\"\n * @example \"Speak with a British accent\"\n */\n systemInstruction?: string\n\n /**\n * Language code hint for the speech synthesis.\n * Gemini 3.1 Flash TTS supports 70+ languages with auto-detection;\n * the 2.5 TTS models support 24 languages.\n *\n * @example \"en-US\" for American English\n * @example \"es-ES\" for Spanish (Spain)\n * @example \"ja-JP\" for Japanese\n */\n languageCode?: string\n}\n\n/**\n * Configuration for Gemini TTS adapter\n *\n * @experimental Gemini TTS is an experimental feature.\n */\nexport interface GeminiTTSConfig extends GeminiClientConfig {}\n\n/** Model type for Gemini TTS */\nexport type GeminiTTSModel = (typeof GEMINI_TTS_MODELS)[number]\n\n/**\n * Gemini Text-to-Speech Adapter\n *\n * Tree-shakeable adapter for Gemini TTS functionality.\n *\n * **IMPORTANT**: Gemini TTS uses the Live API (WebSocket-based) which requires\n * different handling than traditional REST APIs. This adapter provides a\n * simplified interface but may have limitations.\n *\n * @experimental Gemini TTS is an experimental feature and may change.\n *\n * Models:\n * - gemini-2.5-flash-preview-tts\n */\nexport class GeminiTTSAdapter<\n TModel extends GeminiTTSModel,\n> extends BaseTTSAdapter<TModel, GeminiTTSProviderOptions> {\n readonly name = 'gemini' as const\n\n private client: GoogleGenAI\n\n constructor(config: GeminiTTSConfig, model: TModel) {\n super(model, config)\n this.client = createGeminiClient(config)\n }\n\n /**\n * Generate speech from text using Gemini's TTS model.\n *\n * @experimental This implementation is experimental and may change.\n * @see https://ai.google.dev/gemini-api/docs/speech-generation\n */\n async generateSpeech(\n options: TTSOptions<GeminiTTSProviderOptions>,\n ): Promise<TTSResult> {\n const { model, text, modelOptions, voice, logger } = options\n\n logger.request(`activity=generateSpeech provider=gemini model=${model}`, {\n provider: 'gemini',\n model,\n })\n\n const speechConfig: SpeechConfig = {}\n\n if (modelOptions?.multiSpeakerVoiceConfig) {\n // Validate multi-speaker config: 1 or 2 speakers allowed.\n const speakerConfigs =\n modelOptions.multiSpeakerVoiceConfig.speakerVoiceConfigs\n if (\n !Array.isArray(speakerConfigs) ||\n speakerConfigs.length < 1 ||\n speakerConfigs.length > 2\n ) {\n throw new Error(\n `Gemini TTS multiSpeakerVoiceConfig.speakerVoiceConfigs must contain 1 or 2 speakers; received ${Array.isArray(speakerConfigs) ? speakerConfigs.length : 'non-array'}.`,\n )\n }\n speechConfig.multiSpeakerVoiceConfig =\n modelOptions.multiSpeakerVoiceConfig\n } else {\n // Honor the standard TTSOptions.voice (used by every other TTS adapter)\n // as a fallback for the prebuilt voice name. If an explicit\n // modelOptions.voiceConfig is supplied its values win — but we still\n // fall back to `voice` / 'Kore' if the supplied voiceConfig is missing\n // prebuiltVoiceConfig.voiceName.\n if (\n voice !== undefined &&\n !(GEMINI_TTS_VOICES as ReadonlyArray<string>).includes(voice)\n ) {\n throw new Error(\n `Invalid Gemini TTS voice \"${voice}\". Valid voices are: ${GEMINI_TTS_VOICES.join(', ')}.`,\n )\n }\n const defaultVoiceName = (voice as GeminiTTSVoice | undefined) ?? 'Kore'\n const supplied = modelOptions?.voiceConfig\n const resolvedVoiceName =\n supplied?.prebuiltVoiceConfig?.voiceName ?? defaultVoiceName\n speechConfig.voiceConfig = {\n prebuiltVoiceConfig: { voiceName: resolvedVoiceName },\n }\n }\n\n if (modelOptions?.languageCode) {\n speechConfig.languageCode = modelOptions.languageCode\n }\n\n try {\n const response = await this.client.models.generateContent({\n model,\n contents: [\n {\n role: 'user',\n parts: [{ text }],\n },\n ],\n config: {\n responseModalities: ['AUDIO'],\n speechConfig,\n // systemInstruction belongs inside `config` per the @google/genai\n // contract — matches sibling Gemini adapters (summarize, text).\n ...(modelOptions?.systemInstruction && {\n systemInstruction: modelOptions.systemInstruction,\n }),\n },\n })\n\n // Extract audio data from response\n const candidate = response.candidates?.[0]\n const parts = candidate?.content?.parts\n\n if (!parts || parts.length === 0) {\n throw new Error('No audio output received from Gemini TTS')\n }\n\n // Look for inline data (audio)\n const audioPart = parts.find((part: any) =>\n part.inlineData?.mimeType?.startsWith('audio/'),\n )\n\n if (!audioPart || !audioPart.inlineData || !audioPart.inlineData.data) {\n throw new Error('No audio data in Gemini TTS response')\n }\n\n const audioBase64 = audioPart.inlineData.data\n // mime is guaranteed by the `startsWith('audio/')` find predicate above.\n const mimeType = audioPart.inlineData.mimeType as string\n\n // Gemini TTS models return raw 16-bit LE PCM with a mime type like\n // `audio/L16;codec=pcm;rate=24000`. That isn't playable in an <audio>\n // element and the bare string isn't a usable file extension, so we\n // prepend a RIFF/WAV header here and normalize the result to audio/wav.\n const pcm = parsePcmMimeType(mimeType)\n if (pcm) {\n const wavBase64 = wrapPcmBase64AsWav(\n audioBase64,\n pcm.sampleRate,\n pcm.channels,\n pcm.bitsPerSample,\n )\n return {\n id: generateId(this.name),\n model,\n audio: wavBase64,\n format: 'wav',\n contentType: 'audio/wav',\n }\n }\n\n // Strip any mime parameters (e.g. `audio/ogg;codec=opus`) before pulling\n // the subtype out as the file format.\n const format = mimeType.split(';')[0]!.split('/')[1] || 'wav'\n\n return {\n id: generateId(this.name),\n model,\n audio: audioBase64,\n format,\n contentType: mimeType,\n }\n } catch (error) {\n logger.errors('gemini.generateSpeech fatal', {\n error,\n source: 'gemini.generateSpeech',\n })\n throw error\n }\n }\n}\n\nfunction parsePcmMimeType(\n mimeType: string,\n): { sampleRate: number; channels: number; bitsPerSample: number } | undefined {\n const normalized = mimeType.toLowerCase()\n const subtype = normalized.split(';')[0]!.split('/')[1] ?? ''\n // Exclude containerized wav (e.g. `audio/wav;codec=pcm`) — those already\n // carry a RIFF header and must not be re-wrapped.\n if (subtype.includes('wav')) return undefined\n\n // Accept the variants Gemini and other providers actually emit:\n // - audio/L16;codec=pcm;rate=24000 (IANA PCM with bit depth in the type)\n // - audio/L24 and friends\n // - audio/pcm and audio/x-pcm\n // - anything else that explicitly tags codec=pcm and isn't wav-containered\n const bitDepthMatch = /^audio\\/l(\\d+)/.exec(normalized)\n const isPcm =\n bitDepthMatch !== null ||\n normalized.startsWith('audio/pcm') ||\n normalized.startsWith('audio/x-pcm') ||\n normalized.includes('codec=pcm')\n if (!isPcm) return undefined\n\n const rateMatch = /rate=(\\d+)/.exec(normalized)\n const channelsMatch = /channels=(\\d+)/.exec(normalized)\n // Default to 16-bit when the mime type doesn't specify — matches Gemini's\n // audio/L16;codec=pcm;rate=24000 response.\n const bitsPerSample = bitDepthMatch ? Number(bitDepthMatch[1]) : 16\n return {\n sampleRate: rateMatch ? Number(rateMatch[1]) : 24000,\n channels: channelsMatch ? Number(channelsMatch[1]) : 1,\n bitsPerSample,\n }\n}\n\nfunction wrapPcmBase64AsWav(\n pcmBase64: string,\n sampleRate: number,\n channels = 1,\n bitsPerSample = 16,\n): string {\n // The WAV writer below emits a 16-bit PCM fmt chunk. If the source claims a\n // different bit depth we'd be lying about the payload, so bail out loudly\n // rather than producing a corrupt file.\n if (bitsPerSample !== 16) {\n throw new Error(\n `Unsupported PCM bit depth ${bitsPerSample}: only 16-bit PCM can be wrapped as WAV.`,\n )\n }\n\n const pcmBytes =\n typeof Buffer !== 'undefined'\n ? new Uint8Array(Buffer.from(pcmBase64, 'base64'))\n : decodeBase64(pcmBase64)\n\n const byteRate = (sampleRate * channels * bitsPerSample) / 8\n const blockAlign = (channels * bitsPerSample) / 8\n const dataSize = pcmBytes.byteLength\n const buffer = new ArrayBuffer(44 + dataSize)\n const view = new DataView(buffer)\n\n writeAscii(view, 0, 'RIFF')\n view.setUint32(4, 36 + dataSize, true)\n writeAscii(view, 8, 'WAVE')\n writeAscii(view, 12, 'fmt ')\n view.setUint32(16, 16, true)\n view.setUint16(20, 1, true)\n view.setUint16(22, channels, true)\n view.setUint32(24, sampleRate, true)\n view.setUint32(28, byteRate, true)\n view.setUint16(32, blockAlign, true)\n view.setUint16(34, bitsPerSample, true)\n writeAscii(view, 36, 'data')\n view.setUint32(40, dataSize, true)\n new Uint8Array(buffer, 44).set(pcmBytes)\n\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(buffer).toString('base64')\n }\n let binary = ''\n const bytes = new Uint8Array(buffer)\n for (let i = 0; i < bytes.byteLength; i += 1) {\n binary += String.fromCharCode(bytes[i]!)\n }\n return btoa(binary)\n}\n\nfunction decodeBase64(b64: string): Uint8Array {\n const binary = atob(b64)\n const out = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i)\n return out\n}\n\nfunction writeAscii(view: DataView, offset: number, text: string): void {\n for (let i = 0; i < text.length; i += 1) {\n view.setUint8(offset + i, text.charCodeAt(i))\n }\n}\n\n/**\n * Creates a Gemini TTS adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @experimental Gemini TTS is an experimental feature and may change.\n *\n * @param model - The model name (e.g., 'gemini-2.5-flash-preview-tts')\n * @param apiKey - Your Google API key\n * @param config - Optional additional configuration\n * @returns Configured Gemini TTS adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGeminiSpeech('gemini-2.5-flash-preview-tts', \"your-api-key\");\n *\n * const result = await generateSpeech({\n * adapter,\n * text: 'Hello, world!'\n * });\n * ```\n */\nexport function createGeminiSpeech<TModel extends GeminiTTSModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiTTSConfig, 'apiKey'>,\n): GeminiTTSAdapter<TModel> {\n // Put apiKey LAST so caller-supplied config can't silently override the\n // explicit argument.\n return new GeminiTTSAdapter({ ...config, apiKey }, model)\n}\n\n/**\n * Creates a Gemini speech adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * @experimental Gemini TTS is an experimental feature and may change.\n *\n * Looks for `GOOGLE_API_KEY` or `GEMINI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'gemini-2.5-flash-preview-tts')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Gemini speech adapter instance with resolved types\n * @throws Error if GOOGLE_API_KEY or GEMINI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses GOOGLE_API_KEY from environment\n * const adapter = geminiSpeech('gemini-2.5-flash-preview-tts');\n *\n * const result = await generateSpeech({\n * adapter,\n * text: 'Welcome to TanStack AI!'\n * });\n * ```\n */\nexport function geminiSpeech<TModel extends GeminiTTSModel>(\n model: TModel,\n config?: Omit<GeminiTTSConfig, 'apiKey'>,\n): GeminiTTSAdapter<TModel> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiSpeech(model, apiKey, config)\n}\n"],"names":[],"mappings":";;;AAsHO,MAAM,yBAEH,eAAiD;AAAA,EAKzD,YAAY,QAAyB,OAAe;AAClD,UAAM,OAAO,MAAM;AALrB,SAAS,OAAO;AAMd,SAAK,SAAS,mBAAmB,MAAM;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,eACJ,SACoB;AACpB,UAAM,EAAE,OAAO,MAAM,cAAc,OAAO,WAAW;AAErD,WAAO,QAAQ,iDAAiD,KAAK,IAAI;AAAA,MACvE,UAAU;AAAA,MACV;AAAA,IAAA,CACD;AAED,UAAM,eAA6B,CAAA;AAEnC,QAAI,cAAc,yBAAyB;AAEzC,YAAM,iBACJ,aAAa,wBAAwB;AACvC,UACE,CAAC,MAAM,QAAQ,cAAc,KAC7B,eAAe,SAAS,KACxB,eAAe,SAAS,GACxB;AACA,cAAM,IAAI;AAAA,UACR,iGAAiG,MAAM,QAAQ,cAAc,IAAI,eAAe,SAAS,WAAW;AAAA,QAAA;AAAA,MAExK;AACA,mBAAa,0BACX,aAAa;AAAA,IACjB,OAAO;AAML,UACE,UAAU,UACV,CAAE,kBAA4C,SAAS,KAAK,GAC5D;AACA,cAAM,IAAI;AAAA,UACR,6BAA6B,KAAK,wBAAwB,kBAAkB,KAAK,IAAI,CAAC;AAAA,QAAA;AAAA,MAE1F;AACA,YAAM,mBAAoB,SAAwC;AAClE,YAAM,WAAW,cAAc;AAC/B,YAAM,oBACJ,UAAU,qBAAqB,aAAa;AAC9C,mBAAa,cAAc;AAAA,QACzB,qBAAqB,EAAE,WAAW,kBAAA;AAAA,MAAkB;AAAA,IAExD;AAEA,QAAI,cAAc,cAAc;AAC9B,mBAAa,eAAe,aAAa;AAAA,IAC3C;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,KAAK,OAAO,OAAO,gBAAgB;AAAA,QACxD;AAAA,QACA,UAAU;AAAA,UACR;AAAA,YACE,MAAM;AAAA,YACN,OAAO,CAAC,EAAE,KAAA,CAAM;AAAA,UAAA;AAAA,QAClB;AAAA,QAEF,QAAQ;AAAA,UACN,oBAAoB,CAAC,OAAO;AAAA,UAC5B;AAAA;AAAA;AAAA,UAGA,GAAI,cAAc,qBAAqB;AAAA,YACrC,mBAAmB,aAAa;AAAA,UAAA;AAAA,QAClC;AAAA,MACF,CACD;AAGD,YAAM,YAAY,SAAS,aAAa,CAAC;AACzC,YAAM,QAAQ,WAAW,SAAS;AAElC,UAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,cAAM,IAAI,MAAM,0CAA0C;AAAA,MAC5D;AAGA,YAAM,YAAY,MAAM;AAAA,QAAK,CAAC,SAC5B,KAAK,YAAY,UAAU,WAAW,QAAQ;AAAA,MAAA;AAGhD,UAAI,CAAC,aAAa,CAAC,UAAU,cAAc,CAAC,UAAU,WAAW,MAAM;AACrE,cAAM,IAAI,MAAM,sCAAsC;AAAA,MACxD;AAEA,YAAM,cAAc,UAAU,WAAW;AAEzC,YAAM,WAAW,UAAU,WAAW;AAMtC,YAAM,MAAM,iBAAiB,QAAQ;AACrC,UAAI,KAAK;AACP,cAAM,YAAY;AAAA,UAChB;AAAA,UACA,IAAI;AAAA,UACJ,IAAI;AAAA,UACJ,IAAI;AAAA,QAAA;AAEN,eAAO;AAAA,UACL,IAAI,WAAW,KAAK,IAAI;AAAA,UACxB;AAAA,UACA,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,aAAa;AAAA,QAAA;AAAA,MAEjB;AAIA,YAAM,SAAS,SAAS,MAAM,GAAG,EAAE,CAAC,EAAG,MAAM,GAAG,EAAE,CAAC,KAAK;AAExD,aAAO;AAAA,QACL,IAAI,WAAW,KAAK,IAAI;AAAA,QACxB;AAAA,QACA,OAAO;AAAA,QACP;AAAA,QACA,aAAa;AAAA,MAAA;AAAA,IAEjB,SAAS,OAAO;AACd,aAAO,OAAO,+BAA+B;AAAA,QAC3C;AAAA,QACA,QAAQ;AAAA,MAAA,CACT;AACD,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,SAAS,iBACP,UAC6E;AAC7E,QAAM,aAAa,SAAS,YAAA;AAC5B,QAAM,UAAU,WAAW,MAAM,GAAG,EAAE,CAAC,EAAG,MAAM,GAAG,EAAE,CAAC,KAAK;AAG3D,MAAI,QAAQ,SAAS,KAAK,EAAG,QAAO;AAOpC,QAAM,gBAAgB,iBAAiB,KAAK,UAAU;AACtD,QAAM,QACJ,kBAAkB,QAClB,WAAW,WAAW,WAAW,KACjC,WAAW,WAAW,aAAa,KACnC,WAAW,SAAS,WAAW;AACjC,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,YAAY,aAAa,KAAK,UAAU;AAC9C,QAAM,gBAAgB,iBAAiB,KAAK,UAAU;AAGtD,QAAM,gBAAgB,gBAAgB,OAAO,cAAc,CAAC,CAAC,IAAI;AACjE,SAAO;AAAA,IACL,YAAY,YAAY,OAAO,UAAU,CAAC,CAAC,IAAI;AAAA,IAC/C,UAAU,gBAAgB,OAAO,cAAc,CAAC,CAAC,IAAI;AAAA,IACrD;AAAA,EAAA;AAEJ;AAEA,SAAS,mBACP,WACA,YACA,WAAW,GACX,gBAAgB,IACR;AAIR,MAAI,kBAAkB,IAAI;AACxB,UAAM,IAAI;AAAA,MACR,6BAA6B,aAAa;AAAA,IAAA;AAAA,EAE9C;AAEA,QAAM,WACJ,OAAO,WAAW,cACd,IAAI,WAAW,OAAO,KAAK,WAAW,QAAQ,CAAC,IAC/C,aAAa,SAAS;AAE5B,QAAM,WAAY,aAAa,WAAW,gBAAiB;AAC3D,QAAM,aAAc,WAAW,gBAAiB;AAChD,QAAM,WAAW,SAAS;AAC1B,QAAM,SAAS,IAAI,YAAY,KAAK,QAAQ;AAC5C,QAAM,OAAO,IAAI,SAAS,MAAM;AAEhC,aAAW,MAAM,GAAG,MAAM;AAC1B,OAAK,UAAU,GAAG,KAAK,UAAU,IAAI;AACrC,aAAW,MAAM,GAAG,MAAM;AAC1B,aAAW,MAAM,IAAI,MAAM;AAC3B,OAAK,UAAU,IAAI,IAAI,IAAI;AAC3B,OAAK,UAAU,IAAI,GAAG,IAAI;AAC1B,OAAK,UAAU,IAAI,UAAU,IAAI;AACjC,OAAK,UAAU,IAAI,YAAY,IAAI;AACnC,OAAK,UAAU,IAAI,UAAU,IAAI;AACjC,OAAK,UAAU,IAAI,YAAY,IAAI;AACnC,OAAK,UAAU,IAAI,eAAe,IAAI;AACtC,aAAW,MAAM,IAAI,MAAM;AAC3B,OAAK,UAAU,IAAI,UAAU,IAAI;AACjC,MAAI,WAAW,QAAQ,EAAE,EAAE,IAAI,QAAQ;AAEvC,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,OAAO,KAAK,MAAM,EAAE,SAAS,QAAQ;AAAA,EAC9C;AACA,MAAI,SAAS;AACb,QAAM,QAAQ,IAAI,WAAW,MAAM;AACnC,WAAS,IAAI,GAAG,IAAI,MAAM,YAAY,KAAK,GAAG;AAC5C,cAAU,OAAO,aAAa,MAAM,CAAC,CAAE;AAAA,EACzC;AACA,SAAO,KAAK,MAAM;AACpB;AAEA,SAAS,aAAa,KAAyB;AAC7C,QAAM,SAAS,KAAK,GAAG;AACvB,QAAM,MAAM,IAAI,WAAW,OAAO,MAAM;AACxC,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,EAAG,KAAI,CAAC,IAAI,OAAO,WAAW,CAAC;AACvE,SAAO;AACT;AAEA,SAAS,WAAW,MAAgB,QAAgB,MAAoB;AACtE,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,SAAK,SAAS,SAAS,GAAG,KAAK,WAAW,CAAC,CAAC;AAAA,EAC9C;AACF;AAuBO,SAAS,mBACd,OACA,QACA,QAC0B;AAG1B,SAAO,IAAI,iBAAiB,EAAE,GAAG,QAAQ,OAAA,GAAU,KAAK;AAC1D;AA4BO,SAAS,aACd,OACA,QAC0B;AAC1B,QAAM,SAAS,uBAAA;AACf,SAAO,mBAAmB,OAAO,QAAQ,MAAM;AACjD;"}
|
|
1
|
+
{"version":3,"file":"tts.js","sources":["../../../src/adapters/tts.ts"],"sourcesContent":["import { BaseTTSAdapter } from '@tanstack/ai/adapters'\nimport {\n createGeminiClient,\n generateId,\n getGeminiApiKeyFromEnv,\n} from '../utils'\nimport { GEMINI_TTS_VOICES } from '../model-meta'\nimport type { GEMINI_TTS_MODELS, GeminiTTSVoice } from '../model-meta'\nimport type { TTSOptions, TTSResult } from '@tanstack/ai'\nimport type { GoogleGenAI, SpeechConfig } from '@google/genai'\nimport type { GeminiClientConfig } from '../utils'\n\n/**\n * Configuration for a single speaker in a multi-speaker dialogue.\n * Supported by Gemini 3.1 Flash TTS Preview and the 2.5 TTS models.\n */\nexport interface GeminiSpeakerVoiceConfig {\n /** A name used in the prompt to refer to this speaker */\n speaker: string\n /** Voice configuration for this speaker */\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: GeminiTTSVoice\n }\n }\n}\n\n/**\n * Provider-specific options for Gemini TTS\n *\n * @experimental Gemini TTS is an experimental feature.\n * @see https://ai.google.dev/gemini-api/docs/speech-generation\n * @see https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-tts-preview\n */\nexport interface GeminiTTSProviderOptions {\n /**\n * Voice configuration for single-speaker TTS.\n * Choose from 30 available voices with different characteristics.\n *\n * Use `multiSpeakerVoiceConfig` instead for dialogues.\n */\n voiceConfig?: {\n prebuiltVoiceConfig?: {\n /**\n * The voice name to use for speech synthesis.\n * @see https://ai.google.dev/gemini-api/docs/speech-generation#voices\n */\n voiceName?: GeminiTTSVoice\n }\n }\n\n /**\n * Multi-speaker voice configuration (up to 2 speakers).\n * Supported by Gemini 3.1 Flash TTS Preview and the 2.5 TTS models.\n *\n * Each speaker's lines in the prompt are prefixed with the name defined\n * here, e.g.:\n *\n * ```text\n * Joe: Hey, how's it going?\n * Jane: Not bad, you?\n * ```\n */\n multiSpeakerVoiceConfig?: {\n speakerVoiceConfigs: Array<GeminiSpeakerVoiceConfig>\n }\n\n /**\n * System instruction for controlling speech style.\n * Use natural language to describe the desired speaking style,\n * pace, tone, accent, or other characteristics.\n *\n * With Gemini 3.1 Flash TTS, you can also use inline audio tags like\n * `[whispering]`, `[laughs]`, `[excited]` directly in the input text\n * to control delivery.\n *\n * @example \"Speak slowly and calmly, as if telling a bedtime story\"\n * @example \"Use an upbeat, enthusiastic tone with moderate pace\"\n * @example \"Speak with a British accent\"\n */\n systemInstruction?: string\n\n /**\n * Language code hint for the speech synthesis.\n * Gemini 3.1 Flash TTS supports 70+ languages with auto-detection;\n * the 2.5 TTS models support 24 languages.\n *\n * @example \"en-US\" for American English\n * @example \"es-ES\" for Spanish (Spain)\n * @example \"ja-JP\" for Japanese\n */\n languageCode?: string\n}\n\n/**\n * Configuration for Gemini TTS adapter\n *\n * @experimental Gemini TTS is an experimental feature.\n */\nexport interface GeminiTTSConfig extends GeminiClientConfig {}\n\n/** Model type for Gemini TTS */\nexport type GeminiTTSModel = (typeof GEMINI_TTS_MODELS)[number]\n\n/**\n * Gemini Text-to-Speech Adapter\n *\n * Tree-shakeable adapter for Gemini TTS functionality.\n *\n * **IMPORTANT**: Gemini TTS uses the Live API (WebSocket-based) which requires\n * different handling than traditional REST APIs. This adapter provides a\n * simplified interface but may have limitations.\n *\n * @experimental Gemini TTS is an experimental feature and may change.\n *\n * Models:\n * - gemini-2.5-flash-preview-tts\n */\nexport class GeminiTTSAdapter<\n TModel extends GeminiTTSModel,\n> extends BaseTTSAdapter<TModel, GeminiTTSProviderOptions> {\n readonly name = 'gemini' as const\n\n private readonly client: GoogleGenAI\n\n constructor(config: GeminiTTSConfig, model: TModel) {\n super(model, config)\n this.client = createGeminiClient(config)\n }\n\n /**\n * Generate speech from text using Gemini's TTS model.\n *\n * @experimental This implementation is experimental and may change.\n * @see https://ai.google.dev/gemini-api/docs/speech-generation\n */\n async generateSpeech(\n options: TTSOptions<GeminiTTSProviderOptions>,\n ): Promise<TTSResult> {\n const { model, text, modelOptions, voice, logger } = options\n\n logger.request(`activity=generateSpeech provider=gemini model=${model}`, {\n provider: 'gemini',\n model,\n })\n\n const speechConfig: SpeechConfig = {}\n\n if (modelOptions?.multiSpeakerVoiceConfig) {\n // Validate multi-speaker config: 1 or 2 speakers allowed.\n const speakerConfigs =\n modelOptions.multiSpeakerVoiceConfig.speakerVoiceConfigs\n if (\n !Array.isArray(speakerConfigs) ||\n speakerConfigs.length < 1 ||\n speakerConfigs.length > 2\n ) {\n throw new Error(\n `Gemini TTS multiSpeakerVoiceConfig.speakerVoiceConfigs must contain 1 or 2 speakers; received ${Array.isArray(speakerConfigs) ? speakerConfigs.length : 'non-array'}.`,\n )\n }\n speechConfig.multiSpeakerVoiceConfig =\n modelOptions.multiSpeakerVoiceConfig\n } else {\n // Honor the standard TTSOptions.voice (used by every other TTS adapter)\n // as a fallback for the prebuilt voice name. If an explicit\n // modelOptions.voiceConfig is supplied its values win — but we still\n // fall back to `voice` / 'Kore' if the supplied voiceConfig is missing\n // prebuiltVoiceConfig.voiceName.\n if (\n voice !== undefined &&\n !(GEMINI_TTS_VOICES as ReadonlyArray<string>).includes(voice)\n ) {\n throw new Error(\n `Invalid Gemini TTS voice \"${voice}\". Valid voices are: ${GEMINI_TTS_VOICES.join(', ')}.`,\n )\n }\n const defaultVoiceName = (voice as GeminiTTSVoice | undefined) ?? 'Kore'\n const supplied = modelOptions?.voiceConfig\n const resolvedVoiceName =\n supplied?.prebuiltVoiceConfig?.voiceName ?? defaultVoiceName\n speechConfig.voiceConfig = {\n prebuiltVoiceConfig: { voiceName: resolvedVoiceName },\n }\n }\n\n if (modelOptions?.languageCode) {\n speechConfig.languageCode = modelOptions.languageCode\n }\n\n try {\n const response = await this.client.models.generateContent({\n model,\n contents: [\n {\n role: 'user',\n parts: [{ text }],\n },\n ],\n config: {\n responseModalities: ['AUDIO'],\n speechConfig,\n // systemInstruction belongs inside `config` per the @google/genai\n // contract — matches sibling Gemini adapters (summarize, text).\n ...(modelOptions?.systemInstruction && {\n systemInstruction: modelOptions.systemInstruction,\n }),\n },\n })\n\n // Extract audio data from response\n const candidate = response.candidates?.[0]\n const parts = candidate?.content?.parts\n\n if (!parts || parts.length === 0) {\n throw new Error('No audio output received from Gemini TTS')\n }\n\n // Look for inline data (audio)\n const audioPart = parts.find((part: any) =>\n part.inlineData?.mimeType?.startsWith('audio/'),\n )\n\n if (!audioPart || !audioPart.inlineData || !audioPart.inlineData.data) {\n throw new Error('No audio data in Gemini TTS response')\n }\n\n const audioBase64 = audioPart.inlineData.data\n // mime is guaranteed by the `startsWith('audio/')` find predicate above.\n const mimeType = audioPart.inlineData.mimeType as string\n\n // Gemini TTS models return raw 16-bit LE PCM with a mime type like\n // `audio/L16;codec=pcm;rate=24000`. That isn't playable in an <audio>\n // element and the bare string isn't a usable file extension, so we\n // prepend a RIFF/WAV header here and normalize the result to audio/wav.\n const pcm = parsePcmMimeType(mimeType)\n if (pcm) {\n const wavBase64 = wrapPcmBase64AsWav(\n audioBase64,\n pcm.sampleRate,\n pcm.channels,\n pcm.bitsPerSample,\n )\n return {\n id: generateId(this.name),\n model,\n audio: wavBase64,\n format: 'wav',\n contentType: 'audio/wav',\n }\n }\n\n // Strip any mime parameters (e.g. `audio/ogg;codec=opus`) before pulling\n // the subtype out as the file format. `String.split` always returns at\n // least one element, so [0] is defined.\n const format = (mimeType.split(';')[0] ?? '').split('/')[1] || 'wav'\n\n return {\n id: generateId(this.name),\n model,\n audio: audioBase64,\n format,\n contentType: mimeType,\n }\n } catch (error) {\n logger.errors('gemini.generateSpeech fatal', {\n error,\n source: 'gemini.generateSpeech',\n })\n throw error\n }\n }\n}\n\nfunction parsePcmMimeType(\n mimeType: string,\n): { sampleRate: number; channels: number; bitsPerSample: number } | undefined {\n const normalized = mimeType.toLowerCase()\n const subtype = (normalized.split(';')[0] ?? '').split('/')[1] ?? ''\n // Exclude containerized wav (e.g. `audio/wav;codec=pcm`) — those already\n // carry a RIFF header and must not be re-wrapped.\n if (subtype.includes('wav')) return undefined\n\n // Accept the variants Gemini and other providers actually emit:\n // - audio/L16;codec=pcm;rate=24000 (IANA PCM with bit depth in the type)\n // - audio/L24 and friends\n // - audio/pcm and audio/x-pcm\n // - anything else that explicitly tags codec=pcm and isn't wav-containered\n const bitDepthMatch = /^audio\\/l(\\d+)/.exec(normalized)\n const isPcm =\n bitDepthMatch !== null ||\n normalized.startsWith('audio/pcm') ||\n normalized.startsWith('audio/x-pcm') ||\n normalized.includes('codec=pcm')\n if (!isPcm) return undefined\n\n const rateMatch = /rate=(\\d+)/.exec(normalized)\n const channelsMatch = /channels=(\\d+)/.exec(normalized)\n // Default to 16-bit when the mime type doesn't specify — matches Gemini's\n // audio/L16;codec=pcm;rate=24000 response.\n const bitsPerSample = bitDepthMatch ? Number(bitDepthMatch[1]) : 16\n return {\n sampleRate: rateMatch ? Number(rateMatch[1]) : 24000,\n channels: channelsMatch ? Number(channelsMatch[1]) : 1,\n bitsPerSample,\n }\n}\n\nfunction wrapPcmBase64AsWav(\n pcmBase64: string,\n sampleRate: number,\n channels = 1,\n bitsPerSample = 16,\n): string {\n // The WAV writer below emits a 16-bit PCM fmt chunk. If the source claims a\n // different bit depth we'd be lying about the payload, so bail out loudly\n // rather than producing a corrupt file.\n if (bitsPerSample !== 16) {\n throw new Error(\n `Unsupported PCM bit depth ${bitsPerSample}: only 16-bit PCM can be wrapped as WAV.`,\n )\n }\n\n const pcmBytes =\n typeof Buffer !== 'undefined'\n ? new Uint8Array(Buffer.from(pcmBase64, 'base64'))\n : decodeBase64(pcmBase64)\n\n const byteRate = (sampleRate * channels * bitsPerSample) / 8\n const blockAlign = (channels * bitsPerSample) / 8\n const dataSize = pcmBytes.byteLength\n const buffer = new ArrayBuffer(44 + dataSize)\n const view = new DataView(buffer)\n\n writeAscii(view, 0, 'RIFF')\n view.setUint32(4, 36 + dataSize, true)\n writeAscii(view, 8, 'WAVE')\n writeAscii(view, 12, 'fmt ')\n view.setUint32(16, 16, true)\n view.setUint16(20, 1, true)\n view.setUint16(22, channels, true)\n view.setUint32(24, sampleRate, true)\n view.setUint32(28, byteRate, true)\n view.setUint16(32, blockAlign, true)\n view.setUint16(34, bitsPerSample, true)\n writeAscii(view, 36, 'data')\n view.setUint32(40, dataSize, true)\n new Uint8Array(buffer, 44).set(pcmBytes)\n\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(buffer).toString('base64')\n }\n let binary = ''\n const bytes = new Uint8Array(buffer)\n for (const byte of bytes) {\n binary += String.fromCharCode(byte)\n }\n return btoa(binary)\n}\n\nfunction decodeBase64(b64: string): Uint8Array {\n const binary = atob(b64)\n const out = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i)\n return out\n}\n\nfunction writeAscii(view: DataView, offset: number, text: string): void {\n for (let i = 0; i < text.length; i += 1) {\n view.setUint8(offset + i, text.charCodeAt(i))\n }\n}\n\n/**\n * Creates a Gemini TTS adapter with explicit API key.\n * Type resolution happens here at the call site.\n *\n * @experimental Gemini TTS is an experimental feature and may change.\n *\n * @param model - The model name (e.g., 'gemini-2.5-flash-preview-tts')\n * @param apiKey - Your Google API key\n * @param config - Optional additional configuration\n * @returns Configured Gemini TTS adapter instance with resolved types\n *\n * @example\n * ```typescript\n * const adapter = createGeminiSpeech('gemini-2.5-flash-preview-tts', \"your-api-key\");\n *\n * const result = await generateSpeech({\n * adapter,\n * text: 'Hello, world!'\n * });\n * ```\n */\nexport function createGeminiSpeech<TModel extends GeminiTTSModel>(\n model: TModel,\n apiKey: string,\n config?: Omit<GeminiTTSConfig, 'apiKey'>,\n): GeminiTTSAdapter<TModel> {\n // Put apiKey LAST so caller-supplied config can't silently override the\n // explicit argument.\n return new GeminiTTSAdapter({ ...config, apiKey }, model)\n}\n\n/**\n * Creates a Gemini speech adapter with automatic API key detection from environment variables.\n * Type resolution happens here at the call site.\n *\n * @experimental Gemini TTS is an experimental feature and may change.\n *\n * Looks for `GOOGLE_API_KEY` or `GEMINI_API_KEY` in:\n * - `process.env` (Node.js)\n * - `window.env` (Browser with injected env)\n *\n * @param model - The model name (e.g., 'gemini-2.5-flash-preview-tts')\n * @param config - Optional configuration (excluding apiKey which is auto-detected)\n * @returns Configured Gemini speech adapter instance with resolved types\n * @throws Error if GOOGLE_API_KEY or GEMINI_API_KEY is not found in environment\n *\n * @example\n * ```typescript\n * // Automatically uses GOOGLE_API_KEY from environment\n * const adapter = geminiSpeech('gemini-2.5-flash-preview-tts');\n *\n * const result = await generateSpeech({\n * adapter,\n * text: 'Welcome to TanStack AI!'\n * });\n * ```\n */\nexport function geminiSpeech<TModel extends GeminiTTSModel>(\n model: TModel,\n config?: Omit<GeminiTTSConfig, 'apiKey'>,\n): GeminiTTSAdapter<TModel> {\n const apiKey = getGeminiApiKeyFromEnv()\n return createGeminiSpeech(model, apiKey, config)\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -52,8 +52,9 @@ function validatePrompt(options) {
|
|
|
52
52
|
}
|
|
53
53
|
function parseNativeImageSize(size) {
|
|
54
54
|
const match = size.match(/^(\d+:\d+)_(.+)$/);
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
const [, aspectRatio, resolution] = match ?? [];
|
|
56
|
+
if (aspectRatio === void 0 || resolution === void 0) return void 0;
|
|
57
|
+
return { aspectRatio, resolution };
|
|
57
58
|
}
|
|
58
59
|
export {
|
|
59
60
|
GEMINI_SIZE_TO_ASPECT_RATIO,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-provider-options.js","sources":["../../../src/image/image-provider-options.ts"],"sourcesContent":["import type { GeminiImageModels } from '../model-meta'\nimport type {\n ImagePromptLanguage,\n PersonGeneration,\n SafetyFilterLevel,\n} from '@google/genai'\n\n// Re-export SDK types so users can use them directly\nexport type { ImagePromptLanguage, PersonGeneration, SafetyFilterLevel }\n\n/**\n * Gemini Imagen aspect ratio options\n * Controls the aspect ratio of generated images\n */\nexport type GeminiAspectRatio =\n | '1:1'\n | '3:4'\n | '4:3'\n | '9:16'\n | '16:9'\n | '9:21'\n | '21:9'\n\n/**\n * Provider options for Gemini image generation\n * These options match the @google/genai GenerateImagesConfig interface\n * and can be spread directly into the API request.\n */\nexport interface GeminiImageProviderOptions {\n /**\n * The aspect ratio of generated images\n * @default '1:1'\n */\n aspectRatio?: GeminiAspectRatio\n\n /**\n * Controls whether people can appear in generated images\n * Use PersonGeneration enum values: DONT_ALLOW, ALLOW_ADULT, ALLOW_ALL\n * @default 'ALLOW_ADULT'\n */\n personGeneration?: PersonGeneration\n\n /**\n * Safety filter level for content filtering\n * Use SafetyFilterLevel enum values\n */\n safetyFilterLevel?: SafetyFilterLevel\n\n /**\n * Optional seed for reproducible image generation\n * When the same seed is used with the same prompt and settings,\n * you should get similar (though not identical) results\n */\n seed?: number\n\n /**\n * Whether to add a SynthID watermark to generated images\n * SynthID helps identify AI-generated content\n * @default true\n */\n addWatermark?: boolean\n\n /**\n * Language of the prompt\n * Use ImagePromptLanguage enum values\n */\n language?: ImagePromptLanguage\n\n /**\n * Negative prompt - what to avoid in the generated image\n * Not all models support negative prompts\n */\n negativePrompt?: string\n\n /**\n * Output MIME type for the generated image\n * @default 'image/png'\n */\n outputMimeType?: 'image/png' | 'image/jpeg' | 'image/webp'\n\n /**\n * Compression quality for JPEG outputs (0-100)\n * Higher values mean better quality but larger file sizes\n * @default 75\n */\n outputCompressionQuality?: number\n\n /**\n * Controls how much the model adheres to the text prompt\n * Large values increase output and prompt alignment,\n * but may compromise image quality\n */\n guidanceScale?: number\n\n /**\n * Whether to use the prompt rewriting logic\n */\n enhancePrompt?: boolean\n\n /**\n * Whether to report the safety scores of each generated image\n * and the positive prompt in the response\n */\n includeSafetyAttributes?: boolean\n\n /**\n * Whether to include the Responsible AI filter reason\n * if the image is filtered out of the response\n */\n includeRaiReason?: boolean\n\n /**\n * Cloud Storage URI used to store the generated images\n */\n outputGcsUri?: string\n\n /**\n * User specified labels to track billing usage\n */\n labels?: Record<string, string>\n}\n\n/**\n * Model-specific provider options mapping\n * Currently all Imagen models use the same options structure\n */\nexport type GeminiImageModelProviderOptionsByName = {\n [K in GeminiImageModels]: GeminiImageProviderOptions\n}\n\n/**\n * Supported size strings for Gemini Imagen models\n * These map to aspect ratios internally\n */\nexport type GeminiImageSize =\n | '1024x1024'\n | '512x512'\n | '1024x768'\n | '1536x1024'\n | '1792x1024'\n | '1920x1080'\n | '768x1024'\n | '1024x1536'\n | '1024x1792'\n | '1080x1920'\n\n/**\n * Aspect ratios supported by Gemini native image models (via generateContent API).\n * Matches the SDK's ImageConfig.aspectRatio values.\n */\nexport type GeminiNativeImageAspectRatio =\n | '1:1'\n | '2:3'\n | '3:2'\n | '3:4'\n | '4:3'\n | '9:16'\n | '16:9'\n | '21:9'\n\n/**\n * Resolution tiers for Gemini native image models.\n * Matches the SDK's ImageConfig.imageSize values.\n */\nexport type GeminiNativeImageResolution = '1K' | '2K' | '4K'\n\n/**\n * Template literal size type for Gemini native image models: \"16:9_4K\", \"1:1_2K\", etc.\n */\nexport type GeminiNativeImageSize =\n `${GeminiNativeImageAspectRatio}_${GeminiNativeImageResolution}`\n\n/**\n * Gemini native image models that use the generateContent API path.\n * These models support template literal sizes (aspectRatio_resolution).\n */\nexport type GeminiNativeImageModels =\n | 'gemini-3.1-flash-image-preview'\n | 'gemini-3-pro-image-preview'\n | 'gemini-2.5-flash-image'\n | 'gemini-2.0-flash-preview-image-generation'\n\n/**\n * Model-specific size options mapping.\n * Gemini native image models use template literal sizes, Imagen models use pixel sizes.\n */\nexport type GeminiImageModelSizeByName = {\n [K in GeminiNativeImageModels]: GeminiNativeImageSize\n} & {\n [K in Exclude<GeminiImageModels, GeminiNativeImageModels>]: GeminiImageSize\n}\n\n/**\n * Valid sizes for Gemini Imagen models\n * Gemini uses aspect ratios, but we map common WIDTHxHEIGHT formats to aspect ratios\n * These are approximate mappings based on common image dimensions\n */\nexport const GEMINI_SIZE_TO_ASPECT_RATIO: Record<string, GeminiAspectRatio> = {\n // Square\n '1024x1024': '1:1',\n '512x512': '1:1',\n // Landscape\n '1024x768': '4:3',\n '1536x1024': '4:3',\n '1792x1024': '16:9',\n '1920x1080': '16:9',\n // Portrait\n '768x1024': '3:4',\n '1024x1536': '3:4', // Inverted\n '1024x1792': '9:16',\n '1080x1920': '9:16',\n}\n\n/**\n * Maps a WIDTHxHEIGHT size string to a Gemini aspect ratio\n * Returns undefined if the size cannot be mapped\n */\nexport function sizeToAspectRatio(\n size: string | undefined,\n): GeminiAspectRatio | undefined {\n if (!size) return undefined\n return GEMINI_SIZE_TO_ASPECT_RATIO[size]\n}\n\n/**\n * Validates that the provided size can be mapped to an aspect ratio\n * Throws an error if the size is invalid\n */\nexport function validateImageSize(\n model: string,\n size: string | undefined,\n): void {\n if (!size) return\n\n const aspectRatio = sizeToAspectRatio(size)\n if (!aspectRatio) {\n const validSizes = Object.keys(GEMINI_SIZE_TO_ASPECT_RATIO)\n throw new Error(\n `Invalid size \"${size}\" for model \"${model}\". ` +\n `Gemini Imagen uses aspect ratios. Valid sizes that map to aspect ratios: ${validSizes.join(', ')}. ` +\n `Alternatively, use providerOptions.aspectRatio directly with values: 1:1, 3:4, 4:3, 9:16, 16:9, 9:21, 21:9`,\n )\n }\n}\n\n/**\n * Per-model caps on images per request.\n * Imagen 3 and the Imagen 4 family all support up to 4 images per request\n * via the Gemini API (the rumored 8-image tier is Vertex-only and isn't\n * reachable through @google/genai today). Unknown models fall through to\n * the shared cap defined below.\n *\n * @see https://ai.google.dev/gemini-api/docs/imagen\n */\nconst IMAGEN_MAX_IMAGES_BY_MODEL: Record<string, number> = {\n 'imagen-3.0-generate-002': 4,\n 'imagen-4.0-generate-001': 4,\n 'imagen-4.0-ultra-generate-001': 4,\n 'imagen-4.0-fast-generate-001': 4,\n}\n\nconst DEFAULT_IMAGEN_MAX_IMAGES = 4\n\n/**\n * Validates the number of images requested against the model's known cap.\n * Uses a per-model table where available and falls back to the shared\n * default otherwise — no more \"some support up to 8\" comments that don't\n * match the error message.\n */\nexport function validateNumberOfImages(\n model: string,\n numberOfImages: number | undefined,\n): void {\n if (numberOfImages === undefined) return\n\n const maxImages =\n IMAGEN_MAX_IMAGES_BY_MODEL[model] ?? DEFAULT_IMAGEN_MAX_IMAGES\n if (numberOfImages < 1 || numberOfImages > maxImages) {\n throw new Error(\n `Invalid numberOfImages \"${numberOfImages}\" for model \"${model}\". ` +\n `Must be between 1 and ${maxImages}.`,\n )\n }\n}\n\n/**\n * Validates the prompt is not empty\n */\nexport function validatePrompt(options: {\n prompt: string\n model: string\n}): void {\n const { prompt, model } = options\n if (!prompt || prompt.trim().length === 0) {\n throw new Error(`Prompt cannot be empty for model \"${model}\".`)\n }\n}\n\n/**\n * Parses a Gemini native image size string into its components.\n * Format: \"aspectRatio_resolution\" e.g. \"16:9_4K\" → { aspectRatio: \"16:9\", resolution: \"4K\" }\n */\nexport function parseNativeImageSize(\n size: string,\n): { aspectRatio: string; resolution: string } | undefined {\n const match = size.match(/^(\\d+:\\d+)_(.+)$/)\n if (
|
|
1
|
+
{"version":3,"file":"image-provider-options.js","sources":["../../../src/image/image-provider-options.ts"],"sourcesContent":["import type { GeminiImageModels } from '../model-meta'\nimport type {\n ImagePromptLanguage,\n PersonGeneration,\n SafetyFilterLevel,\n} from '@google/genai'\n\n// Re-export SDK types so users can use them directly\nexport type { ImagePromptLanguage, PersonGeneration, SafetyFilterLevel }\n\n/**\n * Gemini Imagen aspect ratio options\n * Controls the aspect ratio of generated images\n */\nexport type GeminiAspectRatio =\n | '1:1'\n | '3:4'\n | '4:3'\n | '9:16'\n | '16:9'\n | '9:21'\n | '21:9'\n\n/**\n * Provider options for Gemini image generation\n * These options match the @google/genai GenerateImagesConfig interface\n * and can be spread directly into the API request.\n */\nexport interface GeminiImageProviderOptions {\n /**\n * The aspect ratio of generated images\n * @default '1:1'\n */\n aspectRatio?: GeminiAspectRatio\n\n /**\n * Controls whether people can appear in generated images\n * Use PersonGeneration enum values: DONT_ALLOW, ALLOW_ADULT, ALLOW_ALL\n * @default 'ALLOW_ADULT'\n */\n personGeneration?: PersonGeneration\n\n /**\n * Safety filter level for content filtering\n * Use SafetyFilterLevel enum values\n */\n safetyFilterLevel?: SafetyFilterLevel\n\n /**\n * Optional seed for reproducible image generation\n * When the same seed is used with the same prompt and settings,\n * you should get similar (though not identical) results\n */\n seed?: number\n\n /**\n * Whether to add a SynthID watermark to generated images\n * SynthID helps identify AI-generated content\n * @default true\n */\n addWatermark?: boolean\n\n /**\n * Language of the prompt\n * Use ImagePromptLanguage enum values\n */\n language?: ImagePromptLanguage\n\n /**\n * Negative prompt - what to avoid in the generated image\n * Not all models support negative prompts\n */\n negativePrompt?: string\n\n /**\n * Output MIME type for the generated image\n * @default 'image/png'\n */\n outputMimeType?: 'image/png' | 'image/jpeg' | 'image/webp'\n\n /**\n * Compression quality for JPEG outputs (0-100)\n * Higher values mean better quality but larger file sizes\n * @default 75\n */\n outputCompressionQuality?: number\n\n /**\n * Controls how much the model adheres to the text prompt\n * Large values increase output and prompt alignment,\n * but may compromise image quality\n */\n guidanceScale?: number\n\n /**\n * Whether to use the prompt rewriting logic\n */\n enhancePrompt?: boolean\n\n /**\n * Whether to report the safety scores of each generated image\n * and the positive prompt in the response\n */\n includeSafetyAttributes?: boolean\n\n /**\n * Whether to include the Responsible AI filter reason\n * if the image is filtered out of the response\n */\n includeRaiReason?: boolean\n\n /**\n * Cloud Storage URI used to store the generated images\n */\n outputGcsUri?: string\n\n /**\n * User specified labels to track billing usage\n */\n labels?: Record<string, string>\n}\n\n/**\n * Model-specific provider options mapping\n * Currently all Imagen models use the same options structure\n */\nexport type GeminiImageModelProviderOptionsByName = {\n [K in GeminiImageModels]: GeminiImageProviderOptions\n}\n\n/**\n * Supported size strings for Gemini Imagen models\n * These map to aspect ratios internally\n */\nexport type GeminiImageSize =\n | '1024x1024'\n | '512x512'\n | '1024x768'\n | '1536x1024'\n | '1792x1024'\n | '1920x1080'\n | '768x1024'\n | '1024x1536'\n | '1024x1792'\n | '1080x1920'\n\n/**\n * Aspect ratios supported by Gemini native image models (via generateContent API).\n * Matches the SDK's ImageConfig.aspectRatio values.\n */\nexport type GeminiNativeImageAspectRatio =\n | '1:1'\n | '2:3'\n | '3:2'\n | '3:4'\n | '4:3'\n | '9:16'\n | '16:9'\n | '21:9'\n\n/**\n * Resolution tiers for Gemini native image models.\n * Matches the SDK's ImageConfig.imageSize values.\n */\nexport type GeminiNativeImageResolution = '1K' | '2K' | '4K'\n\n/**\n * Template literal size type for Gemini native image models: \"16:9_4K\", \"1:1_2K\", etc.\n */\nexport type GeminiNativeImageSize =\n `${GeminiNativeImageAspectRatio}_${GeminiNativeImageResolution}`\n\n/**\n * Gemini native image models that use the generateContent API path.\n * These models support template literal sizes (aspectRatio_resolution).\n */\nexport type GeminiNativeImageModels =\n | 'gemini-3.1-flash-image-preview'\n | 'gemini-3-pro-image-preview'\n | 'gemini-2.5-flash-image'\n | 'gemini-2.0-flash-preview-image-generation'\n\n/**\n * Model-specific size options mapping.\n * Gemini native image models use template literal sizes, Imagen models use pixel sizes.\n */\nexport type GeminiImageModelSizeByName = {\n [K in GeminiNativeImageModels]: GeminiNativeImageSize\n} & {\n [K in Exclude<GeminiImageModels, GeminiNativeImageModels>]: GeminiImageSize\n}\n\n/**\n * Valid sizes for Gemini Imagen models\n * Gemini uses aspect ratios, but we map common WIDTHxHEIGHT formats to aspect ratios\n * These are approximate mappings based on common image dimensions\n */\nexport const GEMINI_SIZE_TO_ASPECT_RATIO: Record<string, GeminiAspectRatio> = {\n // Square\n '1024x1024': '1:1',\n '512x512': '1:1',\n // Landscape\n '1024x768': '4:3',\n '1536x1024': '4:3',\n '1792x1024': '16:9',\n '1920x1080': '16:9',\n // Portrait\n '768x1024': '3:4',\n '1024x1536': '3:4', // Inverted\n '1024x1792': '9:16',\n '1080x1920': '9:16',\n}\n\n/**\n * Maps a WIDTHxHEIGHT size string to a Gemini aspect ratio\n * Returns undefined if the size cannot be mapped\n */\nexport function sizeToAspectRatio(\n size: string | undefined,\n): GeminiAspectRatio | undefined {\n if (!size) return undefined\n return GEMINI_SIZE_TO_ASPECT_RATIO[size]\n}\n\n/**\n * Validates that the provided size can be mapped to an aspect ratio\n * Throws an error if the size is invalid\n */\nexport function validateImageSize(\n model: string,\n size: string | undefined,\n): void {\n if (!size) return\n\n const aspectRatio = sizeToAspectRatio(size)\n if (!aspectRatio) {\n const validSizes = Object.keys(GEMINI_SIZE_TO_ASPECT_RATIO)\n throw new Error(\n `Invalid size \"${size}\" for model \"${model}\". ` +\n `Gemini Imagen uses aspect ratios. Valid sizes that map to aspect ratios: ${validSizes.join(', ')}. ` +\n `Alternatively, use providerOptions.aspectRatio directly with values: 1:1, 3:4, 4:3, 9:16, 16:9, 9:21, 21:9`,\n )\n }\n}\n\n/**\n * Per-model caps on images per request.\n * Imagen 3 and the Imagen 4 family all support up to 4 images per request\n * via the Gemini API (the rumored 8-image tier is Vertex-only and isn't\n * reachable through @google/genai today). Unknown models fall through to\n * the shared cap defined below.\n *\n * @see https://ai.google.dev/gemini-api/docs/imagen\n */\nconst IMAGEN_MAX_IMAGES_BY_MODEL: Record<string, number> = {\n 'imagen-3.0-generate-002': 4,\n 'imagen-4.0-generate-001': 4,\n 'imagen-4.0-ultra-generate-001': 4,\n 'imagen-4.0-fast-generate-001': 4,\n}\n\nconst DEFAULT_IMAGEN_MAX_IMAGES = 4\n\n/**\n * Validates the number of images requested against the model's known cap.\n * Uses a per-model table where available and falls back to the shared\n * default otherwise — no more \"some support up to 8\" comments that don't\n * match the error message.\n */\nexport function validateNumberOfImages(\n model: string,\n numberOfImages: number | undefined,\n): void {\n if (numberOfImages === undefined) return\n\n const maxImages =\n IMAGEN_MAX_IMAGES_BY_MODEL[model] ?? DEFAULT_IMAGEN_MAX_IMAGES\n if (numberOfImages < 1 || numberOfImages > maxImages) {\n throw new Error(\n `Invalid numberOfImages \"${numberOfImages}\" for model \"${model}\". ` +\n `Must be between 1 and ${maxImages}.`,\n )\n }\n}\n\n/**\n * Validates the prompt is not empty\n */\nexport function validatePrompt(options: {\n prompt: string\n model: string\n}): void {\n const { prompt, model } = options\n if (!prompt || prompt.trim().length === 0) {\n throw new Error(`Prompt cannot be empty for model \"${model}\".`)\n }\n}\n\n/**\n * Parses a Gemini native image size string into its components.\n * Format: \"aspectRatio_resolution\" e.g. \"16:9_4K\" → { aspectRatio: \"16:9\", resolution: \"4K\" }\n */\nexport function parseNativeImageSize(\n size: string,\n): { aspectRatio: string; resolution: string } | undefined {\n const match = size.match(/^(\\d+:\\d+)_(.+)$/)\n const [, aspectRatio, resolution] = match ?? []\n if (aspectRatio === undefined || resolution === undefined) return undefined\n return { aspectRatio, resolution }\n}\n"],"names":[],"mappings":"AAqMO,MAAM,8BAAiE;AAAA;AAAA,EAE5E,aAAa;AAAA,EACb,WAAW;AAAA;AAAA,EAEX,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AAAA;AAAA,EAEb,YAAY;AAAA,EACZ,aAAa;AAAA;AAAA,EACb,aAAa;AAAA,EACb,aAAa;AACf;AAMO,SAAS,kBACd,MAC+B;AAC/B,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,4BAA4B,IAAI;AACzC;AAMO,SAAS,kBACd,OACA,MACM;AACN,MAAI,CAAC,KAAM;AAEX,QAAM,cAAc,kBAAkB,IAAI;AAC1C,MAAI,CAAC,aAAa;AAChB,UAAM,aAAa,OAAO,KAAK,2BAA2B;AAC1D,UAAM,IAAI;AAAA,MACR,iBAAiB,IAAI,gBAAgB,KAAK,+EACoC,WAAW,KAAK,IAAI,CAAC;AAAA,IAAA;AAAA,EAGvG;AACF;AAWA,MAAM,6BAAqD;AAAA,EACzD,2BAA2B;AAAA,EAC3B,2BAA2B;AAAA,EAC3B,iCAAiC;AAAA,EACjC,gCAAgC;AAClC;AAEA,MAAM,4BAA4B;AAQ3B,SAAS,uBACd,OACA,gBACM;AACN,MAAI,mBAAmB,OAAW;AAElC,QAAM,YACJ,2BAA2B,KAAK,KAAK;AACvC,MAAI,iBAAiB,KAAK,iBAAiB,WAAW;AACpD,UAAM,IAAI;AAAA,MACR,2BAA2B,cAAc,gBAAgB,KAAK,4BACnC,SAAS;AAAA,IAAA;AAAA,EAExC;AACF;AAKO,SAAS,eAAe,SAGtB;AACP,QAAM,EAAE,QAAQ,MAAA,IAAU;AAC1B,MAAI,CAAC,UAAU,OAAO,KAAA,EAAO,WAAW,GAAG;AACzC,UAAM,IAAI,MAAM,qCAAqC,KAAK,IAAI;AAAA,EAChE;AACF;AAMO,SAAS,qBACd,MACyD;AACzD,QAAM,QAAQ,KAAK,MAAM,kBAAkB;AAC3C,QAAM,GAAG,aAAa,UAAU,IAAI,SAAS,CAAA;AAC7C,MAAI,gBAAgB,UAAa,eAAe,OAAW,QAAO;AAClE,SAAO,EAAE,aAAa,WAAA;AACxB;"}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import { brandProviderTool } from "@tanstack/ai";
|
|
1
2
|
function convertCodeExecutionToolToAdapterFormat(_tool) {
|
|
2
3
|
return {
|
|
3
4
|
codeExecution: {}
|
|
4
5
|
};
|
|
5
6
|
}
|
|
6
7
|
function codeExecutionTool() {
|
|
7
|
-
return {
|
|
8
|
+
return brandProviderTool({
|
|
8
9
|
name: "code_execution",
|
|
9
10
|
description: "",
|
|
10
11
|
metadata: {}
|
|
11
|
-
};
|
|
12
|
+
});
|
|
12
13
|
}
|
|
13
14
|
export {
|
|
14
15
|
codeExecutionTool,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-execution-tool.js","sources":["../../../src/tools/code-execution-tool.ts"],"sourcesContent":["import type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport interface CodeExecutionToolConfig {}\n\n/** @deprecated Renamed to `CodeExecutionToolConfig`. Will be removed in a future release. */\nexport type CodeExecutionTool = CodeExecutionToolConfig\n\nexport type GeminiCodeExecutionTool = ProviderTool<'gemini', 'code_execution'>\n\nexport function convertCodeExecutionToolToAdapterFormat(_tool: Tool) {\n return {\n codeExecution: {},\n }\n}\n\nexport function codeExecutionTool(): GeminiCodeExecutionTool {\n
|
|
1
|
+
{"version":3,"file":"code-execution-tool.js","sources":["../../../src/tools/code-execution-tool.ts"],"sourcesContent":["import { brandProviderTool } from '@tanstack/ai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport interface CodeExecutionToolConfig {}\n\n/** @deprecated Renamed to `CodeExecutionToolConfig`. Will be removed in a future release. */\nexport type CodeExecutionTool = CodeExecutionToolConfig\n\nexport type GeminiCodeExecutionTool = ProviderTool<'gemini', 'code_execution'>\n\nexport function convertCodeExecutionToolToAdapterFormat(_tool: Tool) {\n return {\n codeExecution: {},\n }\n}\n\nexport function codeExecutionTool(): GeminiCodeExecutionTool {\n return brandProviderTool<GeminiCodeExecutionTool>({\n name: 'code_execution',\n description: '',\n metadata: {},\n })\n}\n"],"names":[],"mappings":";AAUO,SAAS,wCAAwC,OAAa;AACnE,SAAO;AAAA,IACL,eAAe,CAAA;AAAA,EAAC;AAEpB;AAEO,SAAS,oBAA6C;AAC3D,SAAO,kBAA2C;AAAA,IAChD,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU,CAAA;AAAA,EAAC,CACZ;AACH;"}
|
|
@@ -6,8 +6,8 @@ export type ComputerUseTool = ComputerUseToolConfig;
|
|
|
6
6
|
export type GeminiComputerUseTool = ProviderTool<'gemini', 'computer_use'>;
|
|
7
7
|
export declare function convertComputerUseToolToAdapterFormat(tool: Tool): {
|
|
8
8
|
computerUse: {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
excludedPredefinedFunctions?: string[] | undefined;
|
|
10
|
+
environment?: import('@google/genai').Environment | undefined;
|
|
11
11
|
};
|
|
12
12
|
};
|
|
13
13
|
export declare function computerUseTool(config: ComputerUseToolConfig): GeminiComputerUseTool;
|
|
@@ -1,21 +1,30 @@
|
|
|
1
|
+
import { brandProviderTool } from "@tanstack/ai";
|
|
1
2
|
function convertComputerUseToolToAdapterFormat(tool) {
|
|
2
3
|
const metadata = tool.metadata;
|
|
3
4
|
return {
|
|
4
5
|
computerUse: {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
...metadata.environment !== void 0 && {
|
|
7
|
+
environment: metadata.environment
|
|
8
|
+
},
|
|
9
|
+
...metadata.excludedPredefinedFunctions !== void 0 && {
|
|
10
|
+
excludedPredefinedFunctions: metadata.excludedPredefinedFunctions
|
|
11
|
+
}
|
|
7
12
|
}
|
|
8
13
|
};
|
|
9
14
|
}
|
|
10
15
|
function computerUseTool(config) {
|
|
11
|
-
return {
|
|
16
|
+
return brandProviderTool({
|
|
12
17
|
name: "computer_use",
|
|
13
18
|
description: "",
|
|
14
19
|
metadata: {
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
...config.environment !== void 0 && {
|
|
21
|
+
environment: config.environment
|
|
22
|
+
},
|
|
23
|
+
...config.excludedPredefinedFunctions !== void 0 && {
|
|
24
|
+
excludedPredefinedFunctions: config.excludedPredefinedFunctions
|
|
25
|
+
}
|
|
17
26
|
}
|
|
18
|
-
};
|
|
27
|
+
});
|
|
19
28
|
}
|
|
20
29
|
export {
|
|
21
30
|
computerUseTool,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"computer-use-tool.js","sources":["../../../src/tools/computer-use-tool.ts"],"sourcesContent":["import type { ComputerUse } from '@google/genai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type ComputerUseToolConfig = ComputerUse\n\n/** @deprecated Renamed to `ComputerUseToolConfig`. Will be removed in a future release. */\nexport type ComputerUseTool = ComputerUseToolConfig\n\nexport type GeminiComputerUseTool = ProviderTool<'gemini', 'computer_use'>\n\nexport function convertComputerUseToolToAdapterFormat(tool: Tool) {\n const metadata = tool.metadata as ComputerUseToolConfig\n return {\n computerUse: {\n environment: metadata.environment,\n excludedPredefinedFunctions: metadata.excludedPredefinedFunctions,\n },\n }\n}\n\nexport function computerUseTool(\n config: ComputerUseToolConfig,\n): GeminiComputerUseTool {\n
|
|
1
|
+
{"version":3,"file":"computer-use-tool.js","sources":["../../../src/tools/computer-use-tool.ts"],"sourcesContent":["import { brandProviderTool } from '@tanstack/ai'\nimport type { ComputerUse } from '@google/genai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type ComputerUseToolConfig = ComputerUse\n\n/** @deprecated Renamed to `ComputerUseToolConfig`. Will be removed in a future release. */\nexport type ComputerUseTool = ComputerUseToolConfig\n\nexport type GeminiComputerUseTool = ProviderTool<'gemini', 'computer_use'>\n\nexport function convertComputerUseToolToAdapterFormat(tool: Tool) {\n const metadata = tool.metadata as ComputerUseToolConfig\n // Vendor `ComputerUse` fields are `field?: T` (no `| undefined`) under EOPT,\n // so spread each field conditionally rather than emitting explicit\n // `undefined`s on the wire payload.\n return {\n computerUse: {\n ...(metadata.environment !== undefined && {\n environment: metadata.environment,\n }),\n ...(metadata.excludedPredefinedFunctions !== undefined && {\n excludedPredefinedFunctions: metadata.excludedPredefinedFunctions,\n }),\n },\n }\n}\n\nexport function computerUseTool(\n config: ComputerUseToolConfig,\n): GeminiComputerUseTool {\n return brandProviderTool<GeminiComputerUseTool>({\n name: 'computer_use',\n description: '',\n metadata: {\n ...(config.environment !== undefined && {\n environment: config.environment,\n }),\n ...(config.excludedPredefinedFunctions !== undefined && {\n excludedPredefinedFunctions: config.excludedPredefinedFunctions,\n }),\n },\n })\n}\n"],"names":[],"mappings":";AAWO,SAAS,sCAAsC,MAAY;AAChE,QAAM,WAAW,KAAK;AAItB,SAAO;AAAA,IACL,aAAa;AAAA,MACX,GAAI,SAAS,gBAAgB,UAAa;AAAA,QACxC,aAAa,SAAS;AAAA,MAAA;AAAA,MAExB,GAAI,SAAS,gCAAgC,UAAa;AAAA,QACxD,6BAA6B,SAAS;AAAA,MAAA;AAAA,IACxC;AAAA,EACF;AAEJ;AAEO,SAAS,gBACd,QACuB;AACvB,SAAO,kBAAyC;AAAA,IAC9C,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,MACR,GAAI,OAAO,gBAAgB,UAAa;AAAA,QACtC,aAAa,OAAO;AAAA,MAAA;AAAA,MAEtB,GAAI,OAAO,gCAAgC,UAAa;AAAA,QACtD,6BAA6B,OAAO;AAAA,MAAA;AAAA,IACtC;AAAA,EACF,CACD;AACH;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from "@tanstack/ai";
|
|
1
2
|
function convertFileSearchToolToAdapterFormat(tool) {
|
|
2
3
|
const metadata = tool.metadata;
|
|
3
4
|
return {
|
|
@@ -5,11 +6,11 @@ function convertFileSearchToolToAdapterFormat(tool) {
|
|
|
5
6
|
};
|
|
6
7
|
}
|
|
7
8
|
function fileSearchTool(config) {
|
|
8
|
-
return {
|
|
9
|
+
return brandProviderTool({
|
|
9
10
|
name: "file_search",
|
|
10
11
|
description: "",
|
|
11
12
|
metadata: config
|
|
12
|
-
};
|
|
13
|
+
});
|
|
13
14
|
}
|
|
14
15
|
export {
|
|
15
16
|
convertFileSearchToolToAdapterFormat,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-search-tool.js","sources":["../../../src/tools/file-search-tool.ts"],"sourcesContent":["import type { FileSearch } from '@google/genai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type FileSearchToolConfig = FileSearch\n\n/** @deprecated Renamed to `FileSearchToolConfig`. Will be removed in a future release. */\nexport type FileSearchTool = FileSearchToolConfig\n\nexport type GeminiFileSearchTool = ProviderTool<'gemini', 'file_search'>\n\nexport function convertFileSearchToolToAdapterFormat(tool: Tool) {\n const metadata = tool.metadata as FileSearchToolConfig\n return {\n fileSearch: metadata,\n }\n}\n\nexport function fileSearchTool(\n config: FileSearchToolConfig,\n): GeminiFileSearchTool {\n
|
|
1
|
+
{"version":3,"file":"file-search-tool.js","sources":["../../../src/tools/file-search-tool.ts"],"sourcesContent":["import { brandProviderTool } from '@tanstack/ai'\nimport type { FileSearch } from '@google/genai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type FileSearchToolConfig = FileSearch\n\n/** @deprecated Renamed to `FileSearchToolConfig`. Will be removed in a future release. */\nexport type FileSearchTool = FileSearchToolConfig\n\nexport type GeminiFileSearchTool = ProviderTool<'gemini', 'file_search'>\n\nexport function convertFileSearchToolToAdapterFormat(tool: Tool) {\n const metadata = tool.metadata as FileSearchToolConfig\n return {\n fileSearch: metadata,\n }\n}\n\nexport function fileSearchTool(\n config: FileSearchToolConfig,\n): GeminiFileSearchTool {\n return brandProviderTool<GeminiFileSearchTool>({\n name: 'file_search',\n description: '',\n metadata: config,\n })\n}\n"],"names":[],"mappings":";AAWO,SAAS,qCAAqC,MAAY;AAC/D,QAAM,WAAW,KAAK;AACtB,SAAO;AAAA,IACL,YAAY;AAAA,EAAA;AAEhB;AAEO,SAAS,eACd,QACsB;AACtB,SAAO,kBAAwC;AAAA,IAC7C,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EAAA,CACX;AACH;"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const validateFunctionDeclarationTool = (tool) => {
|
|
2
2
|
const nameRegex = /^[a-zA-Z0-9_:.-]{1,64}$/;
|
|
3
|
-
|
|
4
|
-
if (!valid) {
|
|
3
|
+
if (tool.name === void 0 || !nameRegex.test(tool.name)) {
|
|
5
4
|
throw new Error(
|
|
6
5
|
`Invalid function name: ${tool.name}. Must be 1-64 characters long and contain only a-z, A-Z, 0-9, underscores, colons, dots, and dashes.`
|
|
7
6
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"function-declaration-tool.js","sources":["../../../src/tools/function-declaration-tool.ts"],"sourcesContent":["import type { FunctionDeclaration } from '@google/genai'\n\nexport type FunctionDeclarationTool = FunctionDeclaration\n\nconst validateFunctionDeclarationTool = (tool: FunctionDeclarationTool) => {\n const nameRegex = /^[a-zA-Z0-9_:.-]{1,64}$/\n
|
|
1
|
+
{"version":3,"file":"function-declaration-tool.js","sources":["../../../src/tools/function-declaration-tool.ts"],"sourcesContent":["import type { FunctionDeclaration } from '@google/genai'\n\nexport type FunctionDeclarationTool = FunctionDeclaration\n\nconst validateFunctionDeclarationTool = (tool: FunctionDeclarationTool) => {\n const nameRegex = /^[a-zA-Z0-9_:.-]{1,64}$/\n if (tool.name === undefined || !nameRegex.test(tool.name)) {\n throw new Error(\n `Invalid function name: ${tool.name}. Must be 1-64 characters long and contain only a-z, A-Z, 0-9, underscores, colons, dots, and dashes.`,\n )\n }\n\n if (tool.parameters && tool.parametersJsonSchema) {\n throw new Error(\n `FunctionDeclarationTool cannot have both 'parameters' and 'parametersJsonSchema' defined. Please use only one.`,\n )\n }\n\n if (tool.response && tool.responseJsonSchema) {\n throw new Error(\n `FunctionDeclarationTool cannot have both 'response' and 'responseJsonSchema' defined. Please use only one.`,\n )\n }\n}\n\nexport function functionDeclarationTools(\n tools: Array<FunctionDeclarationTool>,\n) {\n tools.forEach(validateFunctionDeclarationTool)\n return {\n functionDeclarations: tools,\n }\n}\n"],"names":[],"mappings":"AAIA,MAAM,kCAAkC,CAAC,SAAkC;AACzE,QAAM,YAAY;AAClB,MAAI,KAAK,SAAS,UAAa,CAAC,UAAU,KAAK,KAAK,IAAI,GAAG;AACzD,UAAM,IAAI;AAAA,MACR,0BAA0B,KAAK,IAAI;AAAA,IAAA;AAAA,EAEvC;AAEA,MAAI,KAAK,cAAc,KAAK,sBAAsB;AAChD,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAEA,MAAI,KAAK,YAAY,KAAK,oBAAoB;AAC5C,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACF;AAEO,SAAS,yBACd,OACA;AACA,QAAM,QAAQ,+BAA+B;AAC7C,SAAO;AAAA,IACL,sBAAsB;AAAA,EAAA;AAE1B;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from "@tanstack/ai";
|
|
1
2
|
function convertGoogleMapsToolToAdapterFormat(tool) {
|
|
2
3
|
const metadata = tool.metadata;
|
|
3
4
|
return {
|
|
@@ -5,11 +6,11 @@ function convertGoogleMapsToolToAdapterFormat(tool) {
|
|
|
5
6
|
};
|
|
6
7
|
}
|
|
7
8
|
function googleMapsTool(config) {
|
|
8
|
-
return {
|
|
9
|
+
return brandProviderTool({
|
|
9
10
|
name: "google_maps",
|
|
10
11
|
description: "",
|
|
11
12
|
metadata: config
|
|
12
|
-
};
|
|
13
|
+
});
|
|
13
14
|
}
|
|
14
15
|
export {
|
|
15
16
|
convertGoogleMapsToolToAdapterFormat,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-maps-tool.js","sources":["../../../src/tools/google-maps-tool.ts"],"sourcesContent":["import type { GoogleMaps } from '@google/genai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type GoogleMapsToolConfig = GoogleMaps\n\n/** @deprecated Renamed to `GoogleMapsToolConfig`. Will be removed in a future release. */\nexport type GoogleMapsTool = GoogleMapsToolConfig\n\nexport type GeminiGoogleMapsTool = ProviderTool<'gemini', 'google_maps'>\n\nexport function convertGoogleMapsToolToAdapterFormat(tool: Tool) {\n const metadata = tool.metadata as GoogleMapsToolConfig\n return {\n googleMaps: metadata,\n }\n}\n\nexport function googleMapsTool(\n config?: GoogleMapsToolConfig,\n): GeminiGoogleMapsTool {\n
|
|
1
|
+
{"version":3,"file":"google-maps-tool.js","sources":["../../../src/tools/google-maps-tool.ts"],"sourcesContent":["import { brandProviderTool } from '@tanstack/ai'\nimport type { GoogleMaps } from '@google/genai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type GoogleMapsToolConfig = GoogleMaps\n\n/** @deprecated Renamed to `GoogleMapsToolConfig`. Will be removed in a future release. */\nexport type GoogleMapsTool = GoogleMapsToolConfig\n\nexport type GeminiGoogleMapsTool = ProviderTool<'gemini', 'google_maps'>\n\nexport function convertGoogleMapsToolToAdapterFormat(tool: Tool) {\n const metadata = tool.metadata as GoogleMapsToolConfig\n return {\n googleMaps: metadata,\n }\n}\n\nexport function googleMapsTool(\n config?: GoogleMapsToolConfig,\n): GeminiGoogleMapsTool {\n return brandProviderTool<GeminiGoogleMapsTool>({\n name: 'google_maps',\n description: '',\n metadata: config,\n })\n}\n"],"names":[],"mappings":";AAWO,SAAS,qCAAqC,MAAY;AAC/D,QAAM,WAAW,KAAK;AACtB,SAAO;AAAA,IACL,YAAY;AAAA,EAAA;AAEhB;AAEO,SAAS,eACd,QACsB;AACtB,SAAO,kBAAwC;AAAA,IAC7C,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EAAA,CACX;AACH;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from "@tanstack/ai";
|
|
1
2
|
function convertGoogleSearchRetrievalToolToAdapterFormat(tool) {
|
|
2
3
|
const metadata = tool.metadata;
|
|
3
4
|
return {
|
|
@@ -5,11 +6,11 @@ function convertGoogleSearchRetrievalToolToAdapterFormat(tool) {
|
|
|
5
6
|
};
|
|
6
7
|
}
|
|
7
8
|
function googleSearchRetrievalTool(config) {
|
|
8
|
-
return {
|
|
9
|
+
return brandProviderTool({
|
|
9
10
|
name: "google_search_retrieval",
|
|
10
11
|
description: "",
|
|
11
12
|
metadata: config
|
|
12
|
-
};
|
|
13
|
+
});
|
|
13
14
|
}
|
|
14
15
|
export {
|
|
15
16
|
convertGoogleSearchRetrievalToolToAdapterFormat,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-search-retriveal-tool.js","sources":["../../../src/tools/google-search-retriveal-tool.ts"],"sourcesContent":["import type { GoogleSearchRetrieval } from '@google/genai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type GoogleSearchRetrievalToolConfig = GoogleSearchRetrieval\n\n/** @deprecated Renamed to `GoogleSearchRetrievalToolConfig`. Will be removed in a future release. */\nexport type GoogleSearchRetrievalTool = GoogleSearchRetrievalToolConfig\n\nexport type GeminiGoogleSearchRetrievalTool = ProviderTool<\n 'gemini',\n 'google_search_retrieval'\n>\n\nexport function convertGoogleSearchRetrievalToolToAdapterFormat(tool: Tool) {\n const metadata = tool.metadata as GoogleSearchRetrievalToolConfig\n return {\n googleSearchRetrieval: metadata,\n }\n}\n\nexport function googleSearchRetrievalTool(\n config?: GoogleSearchRetrievalToolConfig,\n): GeminiGoogleSearchRetrievalTool {\n
|
|
1
|
+
{"version":3,"file":"google-search-retriveal-tool.js","sources":["../../../src/tools/google-search-retriveal-tool.ts"],"sourcesContent":["import { brandProviderTool } from '@tanstack/ai'\nimport type { GoogleSearchRetrieval } from '@google/genai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type GoogleSearchRetrievalToolConfig = GoogleSearchRetrieval\n\n/** @deprecated Renamed to `GoogleSearchRetrievalToolConfig`. Will be removed in a future release. */\nexport type GoogleSearchRetrievalTool = GoogleSearchRetrievalToolConfig\n\nexport type GeminiGoogleSearchRetrievalTool = ProviderTool<\n 'gemini',\n 'google_search_retrieval'\n>\n\nexport function convertGoogleSearchRetrievalToolToAdapterFormat(tool: Tool) {\n const metadata = tool.metadata as GoogleSearchRetrievalToolConfig\n return {\n googleSearchRetrieval: metadata,\n }\n}\n\nexport function googleSearchRetrievalTool(\n config?: GoogleSearchRetrievalToolConfig,\n): GeminiGoogleSearchRetrievalTool {\n return brandProviderTool<GeminiGoogleSearchRetrievalTool>({\n name: 'google_search_retrieval',\n description: '',\n metadata: config,\n })\n}\n"],"names":[],"mappings":";AAcO,SAAS,gDAAgD,MAAY;AAC1E,QAAM,WAAW,KAAK;AACtB,SAAO;AAAA,IACL,uBAAuB;AAAA,EAAA;AAE3B;AAEO,SAAS,0BACd,QACiC;AACjC,SAAO,kBAAmD;AAAA,IACxD,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EAAA,CACX;AACH;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { brandProviderTool } from "@tanstack/ai";
|
|
1
2
|
function convertGoogleSearchToolToAdapterFormat(tool) {
|
|
2
3
|
const metadata = tool.metadata;
|
|
3
4
|
return {
|
|
@@ -5,11 +6,11 @@ function convertGoogleSearchToolToAdapterFormat(tool) {
|
|
|
5
6
|
};
|
|
6
7
|
}
|
|
7
8
|
function googleSearchTool(config) {
|
|
8
|
-
return {
|
|
9
|
+
return brandProviderTool({
|
|
9
10
|
name: "google_search",
|
|
10
11
|
description: "",
|
|
11
12
|
metadata: config
|
|
12
|
-
};
|
|
13
|
+
});
|
|
13
14
|
}
|
|
14
15
|
export {
|
|
15
16
|
convertGoogleSearchToolToAdapterFormat,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"google-search-tool.js","sources":["../../../src/tools/google-search-tool.ts"],"sourcesContent":["import type { GoogleSearch } from '@google/genai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type GoogleSearchToolConfig = GoogleSearch\n\n/** @deprecated Renamed to `GoogleSearchToolConfig`. Will be removed in a future release. */\nexport type GoogleSearchTool = GoogleSearchToolConfig\n\nexport type GeminiGoogleSearchTool = ProviderTool<'gemini', 'google_search'>\n\nexport function convertGoogleSearchToolToAdapterFormat(tool: Tool) {\n const metadata = tool.metadata as GoogleSearchToolConfig\n return {\n googleSearch: metadata,\n }\n}\n\nexport function googleSearchTool(\n config?: GoogleSearchToolConfig,\n): GeminiGoogleSearchTool {\n
|
|
1
|
+
{"version":3,"file":"google-search-tool.js","sources":["../../../src/tools/google-search-tool.ts"],"sourcesContent":["import { brandProviderTool } from '@tanstack/ai'\nimport type { GoogleSearch } from '@google/genai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type GoogleSearchToolConfig = GoogleSearch\n\n/** @deprecated Renamed to `GoogleSearchToolConfig`. Will be removed in a future release. */\nexport type GoogleSearchTool = GoogleSearchToolConfig\n\nexport type GeminiGoogleSearchTool = ProviderTool<'gemini', 'google_search'>\n\nexport function convertGoogleSearchToolToAdapterFormat(tool: Tool) {\n const metadata = tool.metadata as GoogleSearchToolConfig\n return {\n googleSearch: metadata,\n }\n}\n\nexport function googleSearchTool(\n config?: GoogleSearchToolConfig,\n): GeminiGoogleSearchTool {\n return brandProviderTool<GeminiGoogleSearchTool>({\n name: 'google_search',\n description: '',\n metadata: config,\n })\n}\n"],"names":[],"mappings":";AAWO,SAAS,uCAAuC,MAAY;AACjE,QAAM,WAAW,KAAK;AACtB,SAAO;AAAA,IACL,cAAc;AAAA,EAAA;AAElB;AAEO,SAAS,iBACd,QACwB;AACxB,SAAO,kBAA0C;AAAA,IAC/C,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EAAA,CACX;AACH;"}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import { brandProviderTool } from "@tanstack/ai";
|
|
1
2
|
function convertUrlContextToolToAdapterFormat(_tool) {
|
|
2
3
|
return {
|
|
3
4
|
urlContext: {}
|
|
4
5
|
};
|
|
5
6
|
}
|
|
6
7
|
function urlContextTool() {
|
|
7
|
-
return {
|
|
8
|
+
return brandProviderTool({
|
|
8
9
|
name: "url_context",
|
|
9
10
|
description: "",
|
|
10
11
|
metadata: {}
|
|
11
|
-
};
|
|
12
|
+
});
|
|
12
13
|
}
|
|
13
14
|
export {
|
|
14
15
|
convertUrlContextToolToAdapterFormat,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"url-context-tool.js","sources":["../../../src/tools/url-context-tool.ts"],"sourcesContent":["import type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport interface UrlContextToolConfig {}\n\n/** @deprecated Renamed to `UrlContextToolConfig`. Will be removed in a future release. */\nexport type UrlContextTool = UrlContextToolConfig\n\nexport type GeminiUrlContextTool = ProviderTool<'gemini', 'url_context'>\n\nexport function convertUrlContextToolToAdapterFormat(_tool: Tool) {\n return {\n urlContext: {},\n }\n}\n\nexport function urlContextTool(): GeminiUrlContextTool {\n
|
|
1
|
+
{"version":3,"file":"url-context-tool.js","sources":["../../../src/tools/url-context-tool.ts"],"sourcesContent":["import { brandProviderTool } from '@tanstack/ai'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport interface UrlContextToolConfig {}\n\n/** @deprecated Renamed to `UrlContextToolConfig`. Will be removed in a future release. */\nexport type UrlContextTool = UrlContextToolConfig\n\nexport type GeminiUrlContextTool = ProviderTool<'gemini', 'url_context'>\n\nexport function convertUrlContextToolToAdapterFormat(_tool: Tool) {\n return {\n urlContext: {},\n }\n}\n\nexport function urlContextTool(): GeminiUrlContextTool {\n return brandProviderTool<GeminiUrlContextTool>({\n name: 'url_context',\n description: '',\n metadata: {},\n })\n}\n"],"names":[],"mappings":";AAUO,SAAS,qCAAqC,OAAa;AAChE,SAAO;AAAA,IACL,YAAY,CAAA;AAAA,EAAC;AAEjB;AAEO,SAAS,iBAAuC;AACrD,SAAO,kBAAwC;AAAA,IAC7C,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU,CAAA;AAAA,EAAC,CACZ;AACH;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-gemini",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.8",
|
|
4
4
|
"description": "Google Gemini adapter for TanStack AI",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"@tanstack/ai-utils": "0.2.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@tanstack/ai": "^0.20.
|
|
41
|
+
"@tanstack/ai": "^0.20.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@vitest/coverage-v8": "4.0.14",
|
|
45
45
|
"vite": "^7.3.3",
|
|
46
46
|
"zod": "^4.2.0",
|
|
47
|
-
"@tanstack/ai": "0.20.
|
|
47
|
+
"@tanstack/ai": "0.20.1"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "vite build",
|
package/src/adapters/audio.ts
CHANGED
|
@@ -66,7 +66,7 @@ export class GeminiAudioAdapter<
|
|
|
66
66
|
> extends BaseAudioAdapter<TModel, GeminiAudioProviderOptions> {
|
|
67
67
|
readonly name = 'gemini' as const
|
|
68
68
|
|
|
69
|
-
private client: GoogleGenAI
|
|
69
|
+
private readonly client: GoogleGenAI
|
|
70
70
|
|
|
71
71
|
constructor(config: GeminiAudioConfig, model: TModel) {
|
|
72
72
|
super(model, config)
|
|
@@ -120,7 +120,7 @@ export class GeminiAudioAdapter<
|
|
|
120
120
|
model,
|
|
121
121
|
audio: {
|
|
122
122
|
b64Json: audioPart.inlineData.data,
|
|
123
|
-
contentType,
|
|
123
|
+
...(contentType !== undefined && { contentType }),
|
|
124
124
|
},
|
|
125
125
|
}
|
|
126
126
|
} catch (error) {
|
package/src/adapters/image.ts
CHANGED
|
@@ -61,7 +61,7 @@ export class GeminiImageAdapter<
|
|
|
61
61
|
GeminiImageModelProviderOptionsByName,
|
|
62
62
|
GeminiImageModelSizeByName
|
|
63
63
|
> {
|
|
64
|
-
readonly kind = 'image' as const
|
|
64
|
+
override readonly kind = 'image' as const
|
|
65
65
|
readonly name = 'gemini' as const
|
|
66
66
|
|
|
67
67
|
// Type-only property - never assigned at runtime
|
|
@@ -71,7 +71,7 @@ export class GeminiImageAdapter<
|
|
|
71
71
|
modelSizeByName: GeminiImageModelSizeByName
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
private client: GoogleGenAI
|
|
74
|
+
private readonly client: GoogleGenAI
|
|
75
75
|
|
|
76
76
|
constructor(config: GeminiImageConfig, model: TModel) {
|
|
77
77
|
super(model, config)
|
|
@@ -214,7 +214,6 @@ export class GeminiImageAdapter<
|
|
|
214
214
|
id: generateId(this.name),
|
|
215
215
|
model,
|
|
216
216
|
images,
|
|
217
|
-
usage: undefined,
|
|
218
217
|
}
|
|
219
218
|
}
|
|
220
219
|
|
|
@@ -223,10 +222,14 @@ export class GeminiImageAdapter<
|
|
|
223
222
|
): GenerateImagesConfig {
|
|
224
223
|
const { size, numberOfImages, modelOptions } = options
|
|
225
224
|
|
|
225
|
+
// Build with conditional spreads — under exactOptionalPropertyTypes the
|
|
226
|
+
// vendor `GenerateImagesConfig` fields are `field?: T` (no `| undefined`),
|
|
227
|
+
// so we can only assign the property when we actually have a value.
|
|
228
|
+
const sizeAspectRatio = size ? sizeToAspectRatio(size) : undefined
|
|
226
229
|
return {
|
|
227
230
|
numberOfImages: numberOfImages ?? 1,
|
|
228
231
|
// Map size to aspect ratio if provided (modelOptions.aspectRatio will override)
|
|
229
|
-
|
|
232
|
+
...(sizeAspectRatio !== undefined && { aspectRatio: sizeAspectRatio }),
|
|
230
233
|
...modelOptions,
|
|
231
234
|
}
|
|
232
235
|
}
|
|
@@ -242,7 +245,12 @@ export class GeminiImageAdapter<
|
|
|
242
245
|
for (const item of entries) {
|
|
243
246
|
const b64Json = item.image?.imageBytes
|
|
244
247
|
if (b64Json) {
|
|
245
|
-
images.push({
|
|
248
|
+
images.push({
|
|
249
|
+
b64Json,
|
|
250
|
+
...(item.enhancedPrompt !== undefined && {
|
|
251
|
+
revisedPrompt: item.enhancedPrompt,
|
|
252
|
+
}),
|
|
253
|
+
})
|
|
246
254
|
continue
|
|
247
255
|
}
|
|
248
256
|
// Imagen can drop individual entries with a raiFilteredReason when
|
|
@@ -277,7 +285,6 @@ export class GeminiImageAdapter<
|
|
|
277
285
|
id: generateId(this.name),
|
|
278
286
|
model,
|
|
279
287
|
images,
|
|
280
|
-
usage: undefined,
|
|
281
288
|
}
|
|
282
289
|
}
|
|
283
290
|
}
|