@tanstack/ai-gemini 0.23.0 → 0.24.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/dist/esm/adapters/image.d.ts +14 -7
- package/dist/esm/adapters/image.js +27 -56
- package/dist/esm/adapters/image.js.map +1 -1
- package/dist/esm/adapters/text.js.map +1 -1
- package/dist/esm/adapters/video.js +2 -1
- package/dist/esm/adapters/video.js.map +1 -1
- package/dist/esm/experimental/text-interactions/adapter.js +3 -9
- package/dist/esm/experimental/text-interactions/adapter.js.map +1 -1
- package/dist/esm/image/image-provider-options.d.ts +167 -20
- package/dist/esm/image/image-provider-options.js +52 -5
- package/dist/esm/image/image-provider-options.js.map +1 -1
- package/dist/esm/index.d.ts +3 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/model-meta.d.ts +15 -2
- package/dist/esm/model-meta.js +82 -1
- package/dist/esm/model-meta.js.map +1 -1
- package/dist/esm/realtime/adapter.js +1 -5
- package/dist/esm/realtime/adapter.js.map +1 -1
- package/dist/esm/realtime/client.js.map +1 -1
- package/dist/esm/realtime/token.js +3 -2
- package/dist/esm/realtime/token.js.map +1 -1
- package/dist/esm/realtime/utils.js +4 -1
- package/dist/esm/realtime/utils.js.map +1 -1
- package/dist/esm/tools/tool-converter.js +0 -1
- package/dist/esm/tools/tool-converter.js.map +1 -1
- package/dist/esm/usage.js +1 -3
- package/dist/esm/usage.js.map +1 -1
- package/package.json +5 -5
- package/src/adapters/image.ts +123 -36
- package/src/image/image-provider-options.ts +226 -25
- package/src/index.ts +28 -0
- package/src/model-meta.ts +118 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-meta.js","names":[],"sources":["../../src/model-meta.ts"],"sourcesContent":["import type {\n GeminiCachedContentOptions,\n GeminiCommonConfigOptions,\n GeminiSafetyOptions,\n GeminiStructuredOutputOptions,\n GeminiThinkingOptions,\n GeminiToolConfigOptions,\n} from './text/text-provider-options'\nimport type { GeminiEmbeddingProviderOptions } from './embedding/embedding-provider-options'\n\ninterface ModelMeta<TProviderOptions = unknown> {\n name: string\n supports: {\n input: Array<'text' | 'image' | 'audio' | 'video' | 'document'>\n output: Array<'text' | 'image' | 'audio' | 'video'>\n capabilities?: Array<\n | 'audio_generation'\n | 'batch_api'\n | 'caching'\n | 'function_calling'\n | 'live_api'\n | 'structured_output'\n | 'thinking'\n >\n tools?: Array<\n | 'code_execution'\n | 'file_search'\n | 'google_search'\n | 'google_search_retrieval'\n | 'google_maps'\n | 'url_context'\n | 'computer_use'\n >\n }\n max_input_tokens?: number\n max_output_tokens?: number\n knowledge_cutoff?: string\n pricing?: {\n input: {\n normal: number\n cached?: number\n }\n output: {\n normal: number\n }\n }\n /**\n * Type-level description of which provider options this model supports.\n */\n providerOptions?: TProviderOptions\n}\n\nconst GEMINI_3_1_PRO = {\n name: 'gemini-3.1-pro-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_FLASH = {\n name: 'gemini-3-flash-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.5,\n },\n output: {\n normal: 3,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_PRO_IMAGE = {\n name: 'gemini-3-pro-image-preview',\n max_input_tokens: 65_536,\n max_output_tokens: 32_768,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 2,\n },\n output: {\n normal: 0.134,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_1_FLASH_IMAGE = {\n name: 'gemini-3.1-flash-image-preview',\n max_input_tokens: 65_536,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_1_FLASH_LITE_IMAGE = {\n name: 'gemini-3.1-flash-lite-image',\n max_input_tokens: 65_536,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_1_FLASH_LITE = {\n name: 'gemini-3.1-flash-lite',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_1_FLASH_LITE_PREVIEW = {\n name: 'gemini-3.1-flash-lite-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_PRO = {\n name: 'gemini-2.5-pro',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_maps',\n 'google_search',\n 'url_context',\n ],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_PRO_TTS = {\n name: 'gemini-2.5-pro-preview-tts',\n max_input_tokens: 8_192,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst GEMINI_2_5_FLASH = {\n name: 'gemini-2.5-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_maps',\n 'google_search',\n 'url_context',\n ],\n },\n pricing: {\n input: {\n normal: 1,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_FLASH_IMAGE = {\n name: 'gemini-2.5-flash-image',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-06-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'caching', 'structured_output'],\n tools: ['file_search'],\n },\n pricing: {\n input: {\n normal: 0.3,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n/**\nconst GEMINI_2_5_FLASH_LIVE = {\n name: 'gemini-2.5-flash-native-audio-preview-09-2025',\n max_input_tokens: 141_072,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'audio', 'video'],\n output: ['text', 'audio'],\n capabilities: [\n 'audio_generation',\n 'file_search',\n 'function_calling',\n 'live_api',\n 'search_grounding',\n 'thinking',\n ],\n },\n pricing: {\n // todo find this info\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions &\n GeminiThinkingOptions\n>\n*/\nconst GEMINI_2_5_FLASH_TTS = {\n name: 'gemini-2.5-flash-preview-tts',\n max_input_tokens: 8_192,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation', 'batch_api'],\n },\n pricing: {\n input: {\n normal: 1,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Gemini 3.1 Flash TTS Preview - latest expressive TTS model with\n * 200+ audio tags, 70+ languages, and multi-speaker dialogue support.\n * @see https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-tts-preview\n */\nconst GEMINI_3_1_FLASH_TTS = {\n name: 'gemini-3.1-flash-tts-preview',\n max_input_tokens: 32_768,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation', 'batch_api'],\n },\n pricing: {\n input: {\n normal: 0.5,\n },\n output: {\n normal: 10,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Lyria 3 Pro Preview — Google's flagship music generation model.\n * Generates full-length songs with multiple verses, choruses, and bridges.\n * Outputs MP3 or WAV at 48 kHz stereo.\n * @see https://ai.google.dev/gemini-api/docs/models/lyria-3-pro-preview\n */\nconst LYRIA_3_PRO = {\n name: 'lyria-3-pro-preview',\n max_input_tokens: 131_072,\n supports: {\n input: ['text', 'image'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta\n\n/**\n * Lyria 3 Clip Preview — 30-second music clips in MP3 format.\n * @see https://ai.google.dev/gemini-api/docs/music-generation\n */\nconst LYRIA_3_CLIP = {\n name: 'lyria-3-clip-preview',\n max_input_tokens: 131_072,\n supports: {\n input: ['text', 'image'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta\n\nconst GEMINI_2_5_FLASH_LITE = {\n name: 'gemini-2.5-flash-lite',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'google_maps', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.1,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst IMAGEN_4_GENERATE = {\n name: 'imagen-4.0-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst IMAGEN_4_GENERATE_ULTRA = {\n name: 'imagen-4.0-ultra-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.6,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst IMAGEN_4_GENERATE_FAST = {\n name: 'imagen-4.0-fast-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.2,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Veo video generation models. Pricing is per second of generated video\n * (audio+video rate where the model supports audio).\n * @experimental Veo video generation is an experimental feature and may change.\n */\nconst VEO_3_1_PREVIEW = {\n name: 'veo-3.1-generate-preview',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_3_1_FAST_PREVIEW = {\n name: 'veo-3.1-fast-generate-preview',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_3_1_LITE_PREVIEW = {\n name: 'veo-3.1-lite-generate-preview',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.05,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Gemini Omni Flash — multimodal video generation with conversational\n * editing. Serves only the Interactions API (`generateContent` rejects it),\n * so it routes through the interactions-based path of the video adapter,\n * not Veo's `:predictLongRunning` flow. Pricing is per second of generated\n * video ($0.10/sec). 720p / 24 FPS, 3–10 second clips (default 10s).\n * @experimental Omni video generation is an experimental feature and may change.\n */\nconst GEMINI_OMNI_FLASH_PREVIEW = {\n name: 'gemini-omni-flash-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.1,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst GEMINI_3_7_FLASH = {\n name: 'gemini-3.7-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2026-03-01',\n supports: {\n input: ['text', 'image', 'video', 'audio', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_search',\n 'google_maps',\n 'url_context',\n 'computer_use',\n ],\n },\n // Currently 50% off through the end of 2026.\n // https://ai.google.dev/gemini-api/docs/pricing#gemini-3.7-flash\n pricing: {\n input: {\n normal: 0.75,\n cached: 0.075,\n },\n output: {\n normal: 3.75,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_6_FLASH = {\n name: 'gemini-3.6-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2026-03-01',\n supports: {\n input: ['text', 'image', 'video', 'audio', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_search',\n 'google_maps',\n 'url_context',\n 'computer_use',\n ],\n },\n pricing: {\n input: {\n normal: 1.5,\n cached: 0.15,\n },\n output: {\n normal: 7.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_5_FLASH = {\n name: 'gemini-3.5-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n supports: {\n input: ['text', 'image', 'video', 'document', 'audio'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_search',\n 'google_maps',\n 'url_context',\n 'computer_use',\n ],\n },\n pricing: {\n input: {\n normal: 1.5,\n cached: 0.15,\n },\n output: {\n normal: 9,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_5_FLASH_LITE = {\n name: 'gemini-3.5-flash-lite',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'video', 'audio', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_search',\n 'google_maps',\n 'url_context',\n ],\n },\n pricing: {\n input: {\n normal: 0.3,\n cached: 0.03,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nexport const GEMINI_MODELS = [\n GEMINI_3_7_FLASH.name,\n GEMINI_3_6_FLASH.name,\n GEMINI_3_5_FLASH.name,\n GEMINI_3_5_FLASH_LITE.name,\n GEMINI_3_1_PRO.name,\n GEMINI_3_FLASH.name,\n GEMINI_3_1_FLASH_LITE.name,\n GEMINI_3_1_FLASH_LITE_PREVIEW.name,\n GEMINI_2_5_PRO.name,\n GEMINI_2_5_FLASH.name,\n GEMINI_2_5_FLASH_LITE.name,\n] as const\n\n/**\n * Gemini models that support combining `tools` + `responseSchema` in a\n * single streaming `generateContent` call (per issue #605). Per the\n * provider matrix, Gemini 3.x natively interleaves the schema-constrained\n * answer with function-calling on one pass; Gemini 2.x is unsupported /\n * brittle and keeps the engine's legacy finalization fallback.\n */\nexport const GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS = new Set<string>([\n GEMINI_3_7_FLASH.name,\n GEMINI_3_6_FLASH.name,\n GEMINI_3_5_FLASH.name,\n GEMINI_3_5_FLASH_LITE.name,\n GEMINI_3_1_PRO.name,\n GEMINI_3_FLASH.name,\n GEMINI_3_1_FLASH_LITE.name,\n GEMINI_3_1_FLASH_LITE_PREVIEW.name,\n])\n\nexport type GeminiModels = (typeof GEMINI_MODELS)[number]\n\nexport type GeminiImageModels = (typeof GEMINI_IMAGE_MODELS)[number]\n\nexport const GEMINI_IMAGE_MODELS = [\n GEMINI_3_1_FLASH_IMAGE.name,\n GEMINI_3_1_FLASH_LITE_IMAGE.name,\n GEMINI_3_PRO_IMAGE.name,\n GEMINI_2_5_FLASH_IMAGE.name,\n IMAGEN_4_GENERATE.name,\n IMAGEN_4_GENERATE_FAST.name,\n IMAGEN_4_GENERATE_ULTRA.name,\n] as const\n\n/**\n * Text-to-speech models\n * @experimental Gemini TTS is an experimental feature and may change.\n */\nexport const GEMINI_TTS_MODELS = [\n GEMINI_3_1_FLASH_TTS.name,\n GEMINI_2_5_FLASH_TTS.name,\n GEMINI_2_5_PRO_TTS.name,\n] as const\n\n/**\n * Audio generation models (Lyria music generation).\n * @experimental Lyria music generation is an experimental feature and may change.\n */\nexport const GEMINI_AUDIO_MODELS = [\n LYRIA_3_PRO.name,\n LYRIA_3_CLIP.name,\n] as const\n\n/**\n * Available voice names for Gemini TTS\n * @see https://ai.google.dev/gemini-api/docs/speech-generation\n */\nexport const GEMINI_TTS_VOICES = [\n 'Zephyr',\n 'Puck',\n 'Charon',\n 'Kore',\n 'Fenrir',\n 'Leda',\n 'Orus',\n 'Aoede',\n 'Callirrhoe',\n 'Autonoe',\n 'Enceladus',\n 'Iapetus',\n 'Umbriel',\n 'Algieba',\n 'Despina',\n 'Erinome',\n 'Algenib',\n 'Rasalgethi',\n 'Laomedeia',\n 'Achernar',\n 'Alnilam',\n 'Schedar',\n 'Gacrux',\n 'Pulcherrima',\n 'Achird',\n 'Zubenelgenubi',\n 'Vindemiatrix',\n 'Sadachbia',\n 'Sadaltager',\n 'Sulafat',\n] as const\n\nexport type GeminiTTSVoice = (typeof GEMINI_TTS_VOICES)[number]\n\n/**\n * Video generation models. Veo models run on the long-running\n * `:predictLongRunning` flow; Gemini Omni Flash runs on the Interactions\n * API — the video adapter routes by model.\n * @experimental Video generation is an experimental feature and may change.\n */\nexport const GEMINI_VIDEO_MODELS = [\n VEO_3_1_PREVIEW.name,\n VEO_3_1_FAST_PREVIEW.name,\n VEO_3_1_LITE_PREVIEW.name,\n GEMINI_OMNI_FLASH_PREVIEW.name,\n] as const\n\n/**\n * Video models served by the Interactions API rather than Veo's\n * `:predictLongRunning` operations flow.\n * @experimental Omni video generation is an experimental feature and may change.\n */\nexport const GEMINI_INTERACTIONS_VIDEO_MODELS = [\n GEMINI_OMNI_FLASH_PREVIEW.name,\n] as const\n\n/**\n * Embedding models\n */\nexport const GEMINI_EMBEDDING_MODELS = ['gemini-embedding-001'] as const\n\nexport type GeminiEmbeddingModel = (typeof GEMINI_EMBEDDING_MODELS)[number]\n\n/**\n * Type-only map from embedding model name to its provider options type.\n */\nexport type GeminiEmbeddingModelProviderOptionsByName = {\n 'gemini-embedding-001': GeminiEmbeddingProviderOptions\n}\n\n/**\n * Per-model input modalities for embedding models. Gemini embedding models\n * are text-only, so image inputs fail at compile time.\n */\nexport type GeminiEmbeddingModelInputModalitiesByName = {\n 'gemini-embedding-001': readonly ['text']\n}\n\n// Manual type map for per-model provider options\nexport type GeminiChatModelProviderOptionsByName = {\n // Models with thinking and structured output support\n [GEMINI_3_7_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_6_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_5_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_5_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_1_PRO.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_1_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_1_FLASH_LITE_PREVIEW.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_PRO.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n}\n\n/**\n * Type-only map from chat model name to its supported tool capabilities.\n * Based on the 'supports.tools' arrays defined for each model.\n */\nexport type GeminiChatModelToolCapabilitiesByName = {\n [GEMINI_3_7_FLASH.name]: typeof GEMINI_3_7_FLASH.supports.tools\n [GEMINI_3_6_FLASH.name]: typeof GEMINI_3_6_FLASH.supports.tools\n [GEMINI_3_5_FLASH.name]: typeof GEMINI_3_5_FLASH.supports.tools\n [GEMINI_3_5_FLASH_LITE.name]: typeof GEMINI_3_5_FLASH_LITE.supports.tools\n [GEMINI_3_1_PRO.name]: typeof GEMINI_3_1_PRO.supports.tools\n [GEMINI_3_FLASH.name]: typeof GEMINI_3_FLASH.supports.tools\n [GEMINI_3_1_FLASH_LITE.name]: typeof GEMINI_3_1_FLASH_LITE.supports.tools\n [GEMINI_3_1_FLASH_LITE_PREVIEW.name]: typeof GEMINI_3_1_FLASH_LITE_PREVIEW.supports.tools\n [GEMINI_2_5_PRO.name]: typeof GEMINI_2_5_PRO.supports.tools\n [GEMINI_2_5_FLASH.name]: typeof GEMINI_2_5_FLASH.supports.tools\n [GEMINI_2_5_FLASH_LITE.name]: typeof GEMINI_2_5_FLASH_LITE.supports.tools\n}\n\n/**\n * Type-only map from chat model name to its supported input modalities.\n * Based on the 'supports.input' arrays defined for each model.\n * Note: 'document' in the model meta is mapped to 'document' modality.\n * Used by the core AI types to constrain ContentPart types based on the selected model.\n * Note: These must be inlined as readonly arrays (not typeof) because the model\n * constants are not exported and typeof references don't work in .d.ts files\n * when consumed by external packages.\n *\n * @see https://ai.google.dev/gemini-api/docs/vision\n * @see https://ai.google.dev/gemini-api/docs/audio\n * @see https://ai.google.dev/gemini-api/docs/document-processing\n */\nexport type GeminiModelInputModalitiesByName = {\n // Models with full multimodal support (text, image, audio, video, document)\n [GEMINI_3_7_FLASH.name]: typeof GEMINI_3_7_FLASH.supports.input\n [GEMINI_3_6_FLASH.name]: typeof GEMINI_3_6_FLASH.supports.input\n [GEMINI_3_5_FLASH.name]: typeof GEMINI_3_5_FLASH.supports.input\n [GEMINI_3_5_FLASH_LITE.name]: typeof GEMINI_3_5_FLASH_LITE.supports.input\n [GEMINI_3_1_PRO.name]: typeof GEMINI_3_1_PRO.supports.input\n [GEMINI_3_FLASH.name]: typeof GEMINI_3_FLASH.supports.input\n [GEMINI_3_1_FLASH_LITE.name]: typeof GEMINI_3_1_FLASH_LITE.supports.input\n [GEMINI_3_1_FLASH_LITE_PREVIEW.name]: typeof GEMINI_3_1_FLASH_LITE_PREVIEW.supports.input\n [GEMINI_2_5_PRO.name]: typeof GEMINI_2_5_PRO.supports.input\n [GEMINI_2_5_FLASH_LITE.name]: typeof GEMINI_2_5_FLASH_LITE.supports.input\n\n // Models with text, image, audio, video (no document)\n [GEMINI_2_5_FLASH.name]: typeof GEMINI_2_5_FLASH.supports.input\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"model-meta.js","names":[],"sources":["../../src/model-meta.ts"],"sourcesContent":["import type {\n GeminiCachedContentOptions,\n GeminiCommonConfigOptions,\n GeminiSafetyOptions,\n GeminiStructuredOutputOptions,\n GeminiThinkingOptions,\n GeminiToolConfigOptions,\n} from './text/text-provider-options'\nimport type { GeminiEmbeddingProviderOptions } from './embedding/embedding-provider-options'\n\ninterface ModelMeta<TProviderOptions = unknown> {\n name: string\n supports: {\n input: Array<'text' | 'image' | 'audio' | 'video' | 'document'>\n output: Array<'text' | 'image' | 'audio' | 'video'>\n capabilities?: Array<\n | 'audio_generation'\n | 'batch_api'\n | 'caching'\n | 'function_calling'\n | 'live_api'\n | 'structured_output'\n | 'thinking'\n >\n tools?: Array<\n | 'code_execution'\n | 'file_search'\n | 'google_search'\n | 'google_search_retrieval'\n | 'google_maps'\n | 'url_context'\n | 'computer_use'\n >\n }\n max_input_tokens?: number\n max_output_tokens?: number\n knowledge_cutoff?: string\n pricing?: {\n input: {\n normal: number\n cached?: number\n }\n output: {\n normal: number\n }\n }\n /**\n * Type-level description of which provider options this model supports.\n */\n providerOptions?: TProviderOptions\n}\n\nconst GEMINI_3_1_PRO = {\n name: 'gemini-3.1-pro-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_FLASH = {\n name: 'gemini-3-flash-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.5,\n },\n output: {\n normal: 3,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\n/**\n * Gemini 3 Pro Image (\"Nano Banana Pro\") — GA. Accepts the ten standard\n * aspect ratios at 1K / 2K / 4K.\n * @see https://ai.google.dev/gemini-api/docs/models/gemini-3-pro-image\n */\nconst GEMINI_3_PRO_IMAGE = {\n name: 'gemini-3-pro-image',\n max_input_tokens: 65_536,\n max_output_tokens: 32_768,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 2,\n },\n output: {\n normal: 0.134,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\n/**\n * @deprecated `gemini-3-pro-image-preview` was shut down on 2026-06-25. Use\n * the GA id `gemini-3-pro-image` instead — the preview id now 404s.\n * Kept in the model union so existing code still compiles.\n * @see https://ai.google.dev/gemini-api/docs/deprecations\n */\nconst GEMINI_3_PRO_IMAGE_PREVIEW = {\n name: 'gemini-3-pro-image-preview',\n max_input_tokens: 65_536,\n max_output_tokens: 32_768,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 2,\n },\n output: {\n normal: 0.134,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\n/**\n * Gemini 3.1 Flash Image (\"Nano Banana 2\") — GA. The only native image model\n * that accepts the four extreme banner ratios (1:4, 4:1, 1:8, 8:1) and the\n * 512 (0.5K) resolution tier.\n * @see https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-image\n */\nconst GEMINI_3_1_FLASH_IMAGE = {\n name: 'gemini-3.1-flash-image',\n max_input_tokens: 131_072,\n max_output_tokens: 32_768,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 0.5,\n },\n output: {\n normal: 3,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiThinkingOptions\n>\n\n/**\n * @deprecated `gemini-3.1-flash-image-preview` was shut down on 2026-06-25.\n * Use the GA id `gemini-3.1-flash-image` instead — the preview id now 404s.\n * Kept in the model union so existing code still compiles.\n * @see https://ai.google.dev/gemini-api/docs/deprecations\n */\nconst GEMINI_3_1_FLASH_IMAGE_PREVIEW = {\n name: 'gemini-3.1-flash-image-preview',\n max_input_tokens: 65_536,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\n/**\n * Gemini 3.1 Flash Lite Image (\"Nano Banana 2 Lite\") — GA. 1K output only.\n * @see https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-lite-image\n */\nconst GEMINI_3_1_FLASH_LITE_IMAGE = {\n name: 'gemini-3.1-flash-lite-image',\n max_input_tokens: 65_536,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'structured_output', 'thinking'],\n tools: ['google_search'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_1_FLASH_LITE = {\n name: 'gemini-3.1-flash-lite',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_1_FLASH_LITE_PREVIEW = {\n name: 'gemini-3.1-flash-lite-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'file_search', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.25,\n },\n output: {\n normal: 1.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_PRO = {\n name: 'gemini-2.5-pro',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_maps',\n 'google_search',\n 'url_context',\n ],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_2_5_PRO_TTS = {\n name: 'gemini-2.5-pro-preview-tts',\n max_input_tokens: 8_192,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 2.5,\n },\n output: {\n normal: 15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst GEMINI_2_5_FLASH = {\n name: 'gemini-2.5-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_maps',\n 'google_search',\n 'url_context',\n ],\n },\n pricing: {\n input: {\n normal: 1,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\n/**\n * Gemini 2.5 Flash Image (\"Nano Banana\") — still GA, but documented as the\n * legacy member of the family. Google publishes no `image_size` value for it,\n * so its size type is a bare aspect ratio and the adapter sends no\n * `imageConfig.imageSize`.\n * @deprecated `gemini-2.5-flash-image` shuts down on 2026-10-02. Migrate to\n * `gemini-3.1-flash-lite-image` (cheapest successor) or\n * `gemini-3.1-flash-image`. Google's deprecations table still names the\n * already-dead `gemini-3.1-flash-image-preview` as the replacement.\n * @see https://ai.google.dev/gemini-api/docs/deprecations\n */\nconst GEMINI_2_5_FLASH_IMAGE = {\n name: 'gemini-2.5-flash-image',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-06-01',\n supports: {\n input: ['text', 'image'],\n output: ['text', 'image'],\n capabilities: ['batch_api', 'caching', 'structured_output'],\n tools: ['file_search'],\n },\n pricing: {\n input: {\n normal: 0.3,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n/**\nconst GEMINI_2_5_FLASH_LIVE = {\n name: 'gemini-2.5-flash-native-audio-preview-09-2025',\n max_input_tokens: 141_072,\n max_output_tokens: 8_192,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'audio', 'video'],\n output: ['text', 'audio'],\n capabilities: [\n 'audio_generation',\n 'file_search',\n 'function_calling',\n 'live_api',\n 'search_grounding',\n 'thinking',\n ],\n },\n pricing: {\n // todo find this info\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiGenerationConfigOptions &\n GeminiCachedContentOptions &\n GeminiThinkingOptions\n>\n*/\nconst GEMINI_2_5_FLASH_TTS = {\n name: 'gemini-2.5-flash-preview-tts',\n max_input_tokens: 8_192,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation', 'batch_api'],\n },\n pricing: {\n input: {\n normal: 1,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Gemini 3.1 Flash TTS Preview - latest expressive TTS model with\n * 200+ audio tags, 70+ languages, and multi-speaker dialogue support.\n * @see https://ai.google.dev/gemini-api/docs/models/gemini-3.1-flash-tts-preview\n */\nconst GEMINI_3_1_FLASH_TTS = {\n name: 'gemini-3.1-flash-tts-preview',\n max_input_tokens: 32_768,\n max_output_tokens: 16_384,\n knowledge_cutoff: '2025-05-01',\n supports: {\n input: ['text'],\n output: ['audio'],\n capabilities: ['audio_generation', 'batch_api'],\n },\n pricing: {\n input: {\n normal: 0.5,\n },\n output: {\n normal: 10,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Lyria 3 Pro Preview — Google's flagship music generation model.\n * Generates full-length songs with multiple verses, choruses, and bridges.\n * Outputs MP3 or WAV at 48 kHz stereo.\n * @see https://ai.google.dev/gemini-api/docs/models/lyria-3-pro-preview\n */\nconst LYRIA_3_PRO = {\n name: 'lyria-3-pro-preview',\n max_input_tokens: 131_072,\n supports: {\n input: ['text', 'image'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta\n\n/**\n * Lyria 3 Clip Preview — 30-second music clips in MP3 format.\n * @see https://ai.google.dev/gemini-api/docs/music-generation\n */\nconst LYRIA_3_CLIP = {\n name: 'lyria-3-clip-preview',\n max_input_tokens: 131_072,\n supports: {\n input: ['text', 'image'],\n output: ['audio'],\n capabilities: ['audio_generation'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0,\n },\n },\n} as const satisfies ModelMeta\n\nconst GEMINI_2_5_FLASH_LITE = {\n name: 'gemini-2.5-flash-lite',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'audio', 'video', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: ['code_execution', 'google_maps', 'google_search', 'url_context'],\n },\n pricing: {\n input: {\n normal: 0.1,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst IMAGEN_4_GENERATE = {\n name: 'imagen-4.0-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst IMAGEN_4_GENERATE_ULTRA = {\n name: 'imagen-4.0-ultra-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.6,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst IMAGEN_4_GENERATE_FAST = {\n name: 'imagen-4.0-fast-generate-001',\n max_input_tokens: 480,\n max_output_tokens: 4,\n supports: {\n input: ['text'],\n output: ['image'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.2,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Veo video generation models. Pricing is per second of generated video\n * (audio+video rate where the model supports audio).\n * @experimental Veo video generation is an experimental feature and may change.\n */\nconst VEO_3_1_PREVIEW = {\n name: 'veo-3.1-generate-preview',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.4,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_3_1_FAST_PREVIEW = {\n name: 'veo-3.1-fast-generate-preview',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.15,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst VEO_3_1_LITE_PREVIEW = {\n name: 'veo-3.1-lite-generate-preview',\n max_input_tokens: 1024,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.05,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\n/**\n * Gemini Omni Flash — multimodal video generation with conversational\n * editing. Serves only the Interactions API (`generateContent` rejects it),\n * so it routes through the interactions-based path of the video adapter,\n * not Veo's `:predictLongRunning` flow. Pricing is per second of generated\n * video ($0.10/sec). 720p / 24 FPS, 3–10 second clips (default 10s).\n * @experimental Omni video generation is an experimental feature and may change.\n */\nconst GEMINI_OMNI_FLASH_PREVIEW = {\n name: 'gemini-omni-flash-preview',\n max_input_tokens: 1_048_576,\n max_output_tokens: 1,\n supports: {\n input: ['text', 'image', 'video'],\n output: ['video', 'audio'],\n },\n pricing: {\n input: {\n normal: 0,\n },\n output: {\n normal: 0.1,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions\n>\n\nconst GEMINI_3_7_FLASH = {\n name: 'gemini-3.7-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2026-03-01',\n supports: {\n input: ['text', 'image', 'video', 'audio', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_search',\n 'google_maps',\n 'url_context',\n 'computer_use',\n ],\n },\n // Currently 50% off through the end of 2026.\n // https://ai.google.dev/gemini-api/docs/pricing#gemini-3.7-flash\n pricing: {\n input: {\n normal: 0.75,\n cached: 0.075,\n },\n output: {\n normal: 3.75,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_6_FLASH = {\n name: 'gemini-3.6-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2026-03-01',\n supports: {\n input: ['text', 'image', 'video', 'audio', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_search',\n 'google_maps',\n 'url_context',\n 'computer_use',\n ],\n },\n pricing: {\n input: {\n normal: 1.5,\n cached: 0.15,\n },\n output: {\n normal: 7.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_5_FLASH = {\n name: 'gemini-3.5-flash',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n supports: {\n input: ['text', 'image', 'video', 'document', 'audio'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_search',\n 'google_maps',\n 'url_context',\n 'computer_use',\n ],\n },\n pricing: {\n input: {\n normal: 1.5,\n cached: 0.15,\n },\n output: {\n normal: 9,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nconst GEMINI_3_5_FLASH_LITE = {\n name: 'gemini-3.5-flash-lite',\n max_input_tokens: 1_048_576,\n max_output_tokens: 65_536,\n knowledge_cutoff: '2025-01-01',\n supports: {\n input: ['text', 'image', 'video', 'audio', 'document'],\n output: ['text'],\n capabilities: [\n 'batch_api',\n 'caching',\n 'function_calling',\n 'structured_output',\n 'thinking',\n ],\n tools: [\n 'code_execution',\n 'file_search',\n 'google_search',\n 'google_maps',\n 'url_context',\n ],\n },\n pricing: {\n input: {\n normal: 0.3,\n cached: 0.03,\n },\n output: {\n normal: 2.5,\n },\n },\n} as const satisfies ModelMeta<\n GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n>\n\nexport const GEMINI_MODELS = [\n GEMINI_3_7_FLASH.name,\n GEMINI_3_6_FLASH.name,\n GEMINI_3_5_FLASH.name,\n GEMINI_3_5_FLASH_LITE.name,\n GEMINI_3_1_PRO.name,\n GEMINI_3_FLASH.name,\n GEMINI_3_1_FLASH_LITE.name,\n GEMINI_3_1_FLASH_LITE_PREVIEW.name,\n GEMINI_2_5_PRO.name,\n GEMINI_2_5_FLASH.name,\n GEMINI_2_5_FLASH_LITE.name,\n] as const\n\n/**\n * Gemini models that support combining `tools` + `responseSchema` in a\n * single streaming `generateContent` call (per issue #605). Per the\n * provider matrix, Gemini 3.x natively interleaves the schema-constrained\n * answer with function-calling on one pass; Gemini 2.x is unsupported /\n * brittle and keeps the engine's legacy finalization fallback.\n */\nexport const GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS = new Set<string>([\n GEMINI_3_7_FLASH.name,\n GEMINI_3_6_FLASH.name,\n GEMINI_3_5_FLASH.name,\n GEMINI_3_5_FLASH_LITE.name,\n GEMINI_3_1_PRO.name,\n GEMINI_3_FLASH.name,\n GEMINI_3_1_FLASH_LITE.name,\n GEMINI_3_1_FLASH_LITE_PREVIEW.name,\n])\n\nexport type GeminiModels = (typeof GEMINI_MODELS)[number]\n\n/**\n * @deprecated Shut down 2026-06-25. Use `gemini-3.1-flash-image`.\n */\ntype Gemini31FlashImagePreviewModel = 'gemini-3.1-flash-image-preview'\n\n/**\n * @deprecated Shut down 2026-06-25. Use `gemini-3-pro-image`.\n */\ntype Gemini3ProImagePreviewModel = 'gemini-3-pro-image-preview'\n\nexport type GeminiImageModels =\n | Exclude<\n (typeof GEMINI_IMAGE_MODELS)[number],\n Gemini31FlashImagePreviewModel | Gemini3ProImagePreviewModel\n >\n | Gemini31FlashImagePreviewModel\n | Gemini3ProImagePreviewModel\n\n/**\n * Image generation models. GA ids come first; the trailing `-preview` ids are\n * shut-down aliases kept only so existing code keeps compiling — new code\n * should use the GA id above its alias.\n */\nexport const GEMINI_IMAGE_MODELS = [\n GEMINI_3_1_FLASH_IMAGE.name,\n GEMINI_3_1_FLASH_LITE_IMAGE.name,\n GEMINI_3_PRO_IMAGE.name,\n GEMINI_2_5_FLASH_IMAGE.name,\n IMAGEN_4_GENERATE.name,\n IMAGEN_4_GENERATE_FAST.name,\n IMAGEN_4_GENERATE_ULTRA.name,\n // Deprecated aliases — shut down 2026-06-25.\n GEMINI_3_1_FLASH_IMAGE_PREVIEW.name,\n GEMINI_3_PRO_IMAGE_PREVIEW.name,\n] as const\n\n/**\n * Text-to-speech models\n * @experimental Gemini TTS is an experimental feature and may change.\n */\nexport const GEMINI_TTS_MODELS = [\n GEMINI_3_1_FLASH_TTS.name,\n GEMINI_2_5_FLASH_TTS.name,\n GEMINI_2_5_PRO_TTS.name,\n] as const\n\n/**\n * Audio generation models (Lyria music generation).\n * @experimental Lyria music generation is an experimental feature and may change.\n */\nexport const GEMINI_AUDIO_MODELS = [\n LYRIA_3_PRO.name,\n LYRIA_3_CLIP.name,\n] as const\n\n/**\n * Available voice names for Gemini TTS\n * @see https://ai.google.dev/gemini-api/docs/speech-generation\n */\nexport const GEMINI_TTS_VOICES = [\n 'Zephyr',\n 'Puck',\n 'Charon',\n 'Kore',\n 'Fenrir',\n 'Leda',\n 'Orus',\n 'Aoede',\n 'Callirrhoe',\n 'Autonoe',\n 'Enceladus',\n 'Iapetus',\n 'Umbriel',\n 'Algieba',\n 'Despina',\n 'Erinome',\n 'Algenib',\n 'Rasalgethi',\n 'Laomedeia',\n 'Achernar',\n 'Alnilam',\n 'Schedar',\n 'Gacrux',\n 'Pulcherrima',\n 'Achird',\n 'Zubenelgenubi',\n 'Vindemiatrix',\n 'Sadachbia',\n 'Sadaltager',\n 'Sulafat',\n] as const\n\nexport type GeminiTTSVoice = (typeof GEMINI_TTS_VOICES)[number]\n\n/**\n * Video generation models. Veo models run on the long-running\n * `:predictLongRunning` flow; Gemini Omni Flash runs on the Interactions\n * API — the video adapter routes by model.\n * @experimental Video generation is an experimental feature and may change.\n */\nexport const GEMINI_VIDEO_MODELS = [\n VEO_3_1_PREVIEW.name,\n VEO_3_1_FAST_PREVIEW.name,\n VEO_3_1_LITE_PREVIEW.name,\n GEMINI_OMNI_FLASH_PREVIEW.name,\n] as const\n\n/**\n * Video models served by the Interactions API rather than Veo's\n * `:predictLongRunning` operations flow.\n * @experimental Omni video generation is an experimental feature and may change.\n */\nexport const GEMINI_INTERACTIONS_VIDEO_MODELS = [\n GEMINI_OMNI_FLASH_PREVIEW.name,\n] as const\n\n/**\n * Embedding models\n */\nexport const GEMINI_EMBEDDING_MODELS = ['gemini-embedding-001'] as const\n\nexport type GeminiEmbeddingModel = (typeof GEMINI_EMBEDDING_MODELS)[number]\n\n/**\n * Type-only map from embedding model name to its provider options type.\n */\nexport type GeminiEmbeddingModelProviderOptionsByName = {\n 'gemini-embedding-001': GeminiEmbeddingProviderOptions\n}\n\n/**\n * Per-model input modalities for embedding models. Gemini embedding models\n * are text-only, so image inputs fail at compile time.\n */\nexport type GeminiEmbeddingModelInputModalitiesByName = {\n 'gemini-embedding-001': readonly ['text']\n}\n\n// Manual type map for per-model provider options\nexport type GeminiChatModelProviderOptionsByName = {\n // Models with thinking and structured output support\n [GEMINI_3_7_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_6_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_5_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_5_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_1_PRO.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_1_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_3_1_FLASH_LITE_PREVIEW.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_PRO.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n [GEMINI_2_5_FLASH_LITE.name]: GeminiToolConfigOptions &\n GeminiSafetyOptions &\n GeminiCommonConfigOptions &\n GeminiCachedContentOptions &\n GeminiStructuredOutputOptions &\n GeminiThinkingOptions\n}\n\n/**\n * Type-only map from chat model name to its supported tool capabilities.\n * Based on the 'supports.tools' arrays defined for each model.\n */\nexport type GeminiChatModelToolCapabilitiesByName = {\n [GEMINI_3_7_FLASH.name]: typeof GEMINI_3_7_FLASH.supports.tools\n [GEMINI_3_6_FLASH.name]: typeof GEMINI_3_6_FLASH.supports.tools\n [GEMINI_3_5_FLASH.name]: typeof GEMINI_3_5_FLASH.supports.tools\n [GEMINI_3_5_FLASH_LITE.name]: typeof GEMINI_3_5_FLASH_LITE.supports.tools\n [GEMINI_3_1_PRO.name]: typeof GEMINI_3_1_PRO.supports.tools\n [GEMINI_3_FLASH.name]: typeof GEMINI_3_FLASH.supports.tools\n [GEMINI_3_1_FLASH_LITE.name]: typeof GEMINI_3_1_FLASH_LITE.supports.tools\n [GEMINI_3_1_FLASH_LITE_PREVIEW.name]: typeof GEMINI_3_1_FLASH_LITE_PREVIEW.supports.tools\n [GEMINI_2_5_PRO.name]: typeof GEMINI_2_5_PRO.supports.tools\n [GEMINI_2_5_FLASH.name]: typeof GEMINI_2_5_FLASH.supports.tools\n [GEMINI_2_5_FLASH_LITE.name]: typeof GEMINI_2_5_FLASH_LITE.supports.tools\n}\n\n/**\n * Type-only map from chat model name to its supported input modalities.\n * Based on the 'supports.input' arrays defined for each model.\n * Note: 'document' in the model meta is mapped to 'document' modality.\n * Used by the core AI types to constrain ContentPart types based on the selected model.\n * Note: These must be inlined as readonly arrays (not typeof) because the model\n * constants are not exported and typeof references don't work in .d.ts files\n * when consumed by external packages.\n *\n * @see https://ai.google.dev/gemini-api/docs/vision\n * @see https://ai.google.dev/gemini-api/docs/audio\n * @see https://ai.google.dev/gemini-api/docs/document-processing\n */\nexport type GeminiModelInputModalitiesByName = {\n // Models with full multimodal support (text, image, audio, video, document)\n [GEMINI_3_7_FLASH.name]: typeof GEMINI_3_7_FLASH.supports.input\n [GEMINI_3_6_FLASH.name]: typeof GEMINI_3_6_FLASH.supports.input\n [GEMINI_3_5_FLASH.name]: typeof GEMINI_3_5_FLASH.supports.input\n [GEMINI_3_5_FLASH_LITE.name]: typeof GEMINI_3_5_FLASH_LITE.supports.input\n [GEMINI_3_1_PRO.name]: typeof GEMINI_3_1_PRO.supports.input\n [GEMINI_3_FLASH.name]: typeof GEMINI_3_FLASH.supports.input\n [GEMINI_3_1_FLASH_LITE.name]: typeof GEMINI_3_1_FLASH_LITE.supports.input\n [GEMINI_3_1_FLASH_LITE_PREVIEW.name]: typeof GEMINI_3_1_FLASH_LITE_PREVIEW.supports.input\n [GEMINI_2_5_PRO.name]: typeof GEMINI_2_5_PRO.supports.input\n [GEMINI_2_5_FLASH_LITE.name]: typeof GEMINI_2_5_FLASH_LITE.supports.input\n\n // Models with text, image, audio, video (no document)\n [GEMINI_2_5_FLASH.name]: typeof GEMINI_2_5_FLASH.supports.input\n}\n"],"mappings":";AAoDA,IAAM,iBAAiB;CACrB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;GAAS;EAAU;EACrD,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GAAC;GAAkB;GAAe;GAAiB;EAAa;CACzE;CACA,SAAS;EACP,OAAO,EACL,QAAQ,IACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;AASA,IAAM,iBAAiB;CACrB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;GAAS;EAAU;EACrD,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GAAC;GAAkB;GAAe;GAAiB;EAAa;CACzE;CACA,SAAS;EACP,OAAO,EACL,QAAQ,GACV;EACA,QAAQ,EACN,QAAQ,EACV;CACF;AACF;;;;;;AAcA,IAAM,qBAAqB;CACzB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,QAAQ,OAAO;EACxB,cAAc;GAAC;GAAa;GAAqB;EAAU;EAC3D,OAAO,CAAC,eAAe;CACzB;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,KACV;CACF;AACF;;;;;;;AAeA,IAAM,6BAA6B;CACjC,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,QAAQ,OAAO;EACxB,cAAc;GAAC;GAAa;GAAqB;EAAU;EAC3D,OAAO,CAAC,eAAe;CACzB;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,KACV;CACF;AACF;;;;;;;AAeA,IAAM,yBAAyB;CAC7B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,QAAQ,OAAO;EACxB,cAAc,CAAC,aAAa,UAAU;EACtC,OAAO,CAAC,eAAe;CACzB;CACA,SAAS;EACP,OAAO,EACL,QAAQ,GACV;EACA,QAAQ,EACN,QAAQ,EACV;CACF;AACF;;;;;;;AAcA,IAAM,iCAAiC;CACrC,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,QAAQ,OAAO;EACxB,cAAc;GAAC;GAAa;GAAqB;EAAU;EAC3D,OAAO,CAAC,eAAe;CACzB;CACA,SAAS;EACP,OAAO,EACL,QAAQ,IACV;EACA,QAAQ,EACN,QAAQ,IACV;CACF;AACF;;;;;AAaA,IAAM,8BAA8B;CAClC,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,QAAQ,OAAO;EACxB,cAAc;GAAC;GAAa;GAAqB;EAAU;EAC3D,OAAO,CAAC,eAAe;CACzB;CACA,SAAS;EACP,OAAO,EACL,QAAQ,IACV;EACA,QAAQ,EACN,QAAQ,IACV;CACF;AACF;AASA,IAAM,wBAAwB;CAC5B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;GAAS;EAAU;EACrD,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GAAC;GAAkB;GAAe;GAAiB;EAAa;CACzE;CACA,SAAS;EACP,OAAO,EACL,QAAQ,IACV;EACA,QAAQ,EACN,QAAQ,IACV;CACF;AACF;AASA,IAAM,gCAAgC;CACpC,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;GAAS;EAAU;EACrD,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GAAC;GAAkB;GAAe;GAAiB;EAAa;CACzE;CACA,SAAS;EACP,OAAO,EACL,QAAQ,IACV;EACA,QAAQ,EACN,QAAQ,IACV;CACF;AACF;AASA,IAAM,iBAAiB;CACrB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;GAAS;EAAU;EACrD,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;EACF;CACF;CACA,SAAS;EACP,OAAO,EACL,QAAQ,IACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;AASA,IAAM,qBAAqB;CACzB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,MAAM;EACd,QAAQ,CAAC,OAAO;EAChB,cAAc,CAAC,kBAAkB;CACnC;CACA,SAAS;EACP,OAAO,EACL,QAAQ,IACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;AAOA,IAAM,mBAAmB;CACvB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;EAAO;EACzC,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;EACF;CACF;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,IACV;CACF;AACF;;;;;;;;;;;;AAoBA,IAAM,yBAAyB;CAC7B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,QAAQ,OAAO;EACxB,cAAc;GAAC;GAAa;GAAW;EAAmB;EAC1D,OAAO,CAAC,aAAa;CACvB;CACA,SAAS;EACP,OAAO,EACL,QAAQ,GACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyCA,IAAM,uBAAuB;CAC3B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,MAAM;EACd,QAAQ,CAAC,OAAO;EAChB,cAAc,CAAC,oBAAoB,WAAW;CAChD;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,IACV;CACF;AACF;;;;;;AAYA,IAAM,uBAAuB;CAC3B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,MAAM;EACd,QAAQ,CAAC,OAAO;EAChB,cAAc,CAAC,oBAAoB,WAAW;CAChD;CACA,SAAS;EACP,OAAO,EACL,QAAQ,GACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;;;;;;;AAaA,IAAM,cAAc;CAClB,MAAM;CACN,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,OAAO;EAChB,cAAc,CAAC,kBAAkB;CACnC;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,EACV;CACF;AACF;;;;;AAMA,IAAM,eAAe;CACnB,MAAM;CACN,kBAAkB;CAClB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,OAAO;EAChB,cAAc,CAAC,kBAAkB;CACnC;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,EACV;CACF;AACF;AAEA,IAAM,wBAAwB;CAC5B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;GAAS;EAAU;EACrD,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GAAC;GAAkB;GAAe;GAAiB;EAAa;CACzE;CACA,SAAS;EACP,OAAO,EACL,QAAQ,GACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;AASA,IAAM,oBAAoB;CACxB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,UAAU;EACR,OAAO,CAAC,MAAM;EACd,QAAQ,CAAC,OAAO;CAClB;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;AAOA,IAAM,0BAA0B;CAC9B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,UAAU;EACR,OAAO,CAAC,MAAM;EACd,QAAQ,CAAC,OAAO;CAClB;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;AAOA,IAAM,yBAAyB;CAC7B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,UAAU;EACR,OAAO,CAAC,MAAM;EACd,QAAQ,CAAC,OAAO;CAClB;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;;;;;;AAYA,IAAM,kBAAkB;CACtB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,SAAS,OAAO;CAC3B;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;AAOA,IAAM,uBAAuB;CAC3B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,SAAS,OAAO;CAC3B;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,IACV;CACF;AACF;AAOA,IAAM,uBAAuB;CAC3B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,UAAU;EACR,OAAO,CAAC,QAAQ,OAAO;EACvB,QAAQ,CAAC,SAAS,OAAO;CAC3B;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,IACV;CACF;AACF;;;;;;;;;AAeA,IAAM,4BAA4B;CAChC,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;EAAO;EAChC,QAAQ,CAAC,SAAS,OAAO;CAC3B;CACA,SAAS;EACP,OAAO,EACL,QAAQ,EACV;EACA,QAAQ,EACN,QAAQ,GACV;CACF;AACF;AAOA,IAAM,mBAAmB;CACvB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;GAAS;EAAU;EACrD,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAGA,SAAS;EACP,OAAO;GACL,QAAQ;GACR,QAAQ;EACV;EACA,QAAQ,EACN,QAAQ,KACV;CACF;AACF;AASA,IAAM,mBAAmB;CACvB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;GAAS;EAAU;EACrD,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA,SAAS;EACP,OAAO;GACL,QAAQ;GACR,QAAQ;EACV;EACA,QAAQ,EACN,QAAQ,IACV;CACF;AACF;AASA,IAAM,mBAAmB;CACvB,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;GAAY;EAAO;EACrD,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CACA,SAAS;EACP,OAAO;GACL,QAAQ;GACR,QAAQ;EACV;EACA,QAAQ,EACN,QAAQ,EACV;CACF;AACF;AASA,IAAM,wBAAwB;CAC5B,MAAM;CACN,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,UAAU;EACR,OAAO;GAAC;GAAQ;GAAS;GAAS;GAAS;EAAU;EACrD,QAAQ,CAAC,MAAM;EACf,cAAc;GACZ;GACA;GACA;GACA;GACA;EACF;EACA,OAAO;GACL;GACA;GACA;GACA;GACA;EACF;CACF;CACA,SAAS;EACP,OAAO;GACL,QAAQ;GACR,QAAQ;EACV;EACA,QAAQ,EACN,QAAQ,IACV;CACF;AACF;AASA,IAAa,gBAAgB;CAC3B,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,sBAAsB;CACtB,eAAe;CACf,eAAe;CACf,sBAAsB;CACtB,8BAA8B;CAC9B,eAAe;CACf,iBAAiB;CACjB,sBAAsB;AACxB;;;;;;;;AASA,IAAa,0DAA0C,IAAI,IAAY;CACrE,iBAAiB;CACjB,iBAAiB;CACjB,iBAAiB;CACjB,sBAAsB;CACtB,eAAe;CACf,eAAe;CACf,sBAAsB;CACtB,8BAA8B;AAChC,CAAC;;;;;;AA2BD,IAAa,sBAAsB;CACjC,uBAAuB;CACvB,4BAA4B;CAC5B,mBAAmB;CACnB,uBAAuB;CACvB,kBAAkB;CAClB,uBAAuB;CACvB,wBAAwB;CAExB,+BAA+B;CAC/B,2BAA2B;AAC7B;;;;;AAMA,IAAa,oBAAoB;CAC/B,qBAAqB;CACrB,qBAAqB;CACrB,mBAAmB;AACrB;;;;;AAMA,IAAa,sBAAsB,CACjC,YAAY,MACZ,aAAa,IACf;;;;;AAMA,IAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;;AAUA,IAAa,sBAAsB;CACjC,gBAAgB;CAChB,qBAAqB;CACrB,qBAAqB;CACrB,0BAA0B;AAC5B;;;;;;AAOA,IAAa,mCAAmC,CAC9C,0BAA0B,IAC5B;;;;AAKA,IAAa,0BAA0B,CAAC,sBAAsB"}
|
|
@@ -143,11 +143,7 @@ async function createWebSocketConnection(token, model = "gemini-3.1-flash-live-p
|
|
|
143
143
|
case "setup_complete":
|
|
144
144
|
emit("status_change", { status: "connected" });
|
|
145
145
|
break;
|
|
146
|
-
case "error":
|
|
147
|
-
emit("error", { error: new Error(response.data) });
|
|
148
|
-
break;
|
|
149
|
-
case "thought":
|
|
150
|
-
case "session_resumption_update": break;
|
|
146
|
+
case "error": emit("error", { error: new Error(response.data) });
|
|
151
147
|
}
|
|
152
148
|
};
|
|
153
149
|
await client.connect();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","names":[],"sources":["../../../src/realtime/adapter.ts"],"sourcesContent":["import { createRealtimeEventEmitter } from '@tanstack/ai'\nimport { AudioPlayer, AudioStreamer, base64ToArrayBuffer } from './utils'\nimport { GeminiLiveClient } from './client'\nimport type { LiveResponse } from './client'\nimport type {\n AnyClientTool,\n AudioVisualization,\n RealtimeAdapter,\n RealtimeConnection,\n RealtimeMessage,\n RealtimeMode,\n RealtimeToken,\n} from '@tanstack/ai'\nimport type { GeminiRealtimeModel, GeminiRealtimeOptions } from './types'\n\n/**\n * Creates a Gemini realtime adapter for client-side use.\n *\n * @param options - Optional configuration\n * @returns A RealtimeAdapter for use with RealtimeClient\n *\n * @example\n * ```typescript\n * import { RealtimeClient } from '@tanstack/ai-client'\n * import { geminiRealtime } from '@tanstack/ai-gemini'\n *\n * const client = new RealtimeClient({\n * getToken: () => fetch('/api/realtime-token').then(r => r.json()),\n * adapter: geminiRealtime(),\n * onGoAway: () => client.updateSession({ ... }) // Resume session with new config (available only for Gemini Live adapter)\n * })\n *\n * ```\n */\nexport function geminiRealtime(\n options: GeminiRealtimeOptions = {},\n): RealtimeAdapter {\n return {\n provider: 'gemini',\n\n connect(\n token: RealtimeToken,\n clientTools?: ReadonlyArray<AnyClientTool>,\n ): Promise<RealtimeConnection> {\n return createWebSocketConnection(token, options.model, clientTools)\n },\n }\n}\n\n/**\n * Creates a WebSocket connection to Gemini's realtime API\n */\nasync function createWebSocketConnection(\n token: RealtimeToken,\n model: GeminiRealtimeModel = 'gemini-3.1-flash-live-preview',\n tools?: ReadonlyArray<AnyClientTool>,\n): Promise<RealtimeConnection> {\n const { emit, on: realtimeEventEmitterOn } = createRealtimeEventEmitter()\n\n // Current state\n let currentMode: RealtimeMode = 'idle'\n let currentMessageId: string | null = null\n let messageIdCounter = 0\n\n function generateMessageId(): string {\n return `gemini-msg-${Date.now()}-${++messageIdCounter}`\n }\n\n const client = new GeminiLiveClient(token.token, model, tools)\n\n let message: RealtimeMessage = {\n id: '',\n role: 'assistant',\n timestamp: 0,\n parts: [],\n }\n\n let pendingAssistantResponse = ''\n\n client.onClose = () => {\n emit('status_change', { status: 'idle' })\n emit('mode_change', { mode: 'idle' })\n }\n\n client.onError = (error) => {\n emit('error', { error })\n emit('status_change', { status: 'error' })\n emit('mode_change', { mode: 'idle' })\n }\n\n client.onReceiveResponse = (response: LiveResponse) => {\n switch (response.type) {\n case 'text':\n message.parts.push({\n type: 'text',\n content: response.data,\n })\n break\n case 'audio': {\n // Decode once, then reuse for both the stored message part and playback.\n const pcm = base64ToArrayBuffer(response.data.audioData)\n message.parts.push({\n type: 'audio',\n transcript: response.data.transcript,\n audioData: pcm,\n })\n if (currentMode !== 'speaking') {\n currentMode = 'speaking'\n emit('mode_change', { mode: 'speaking' })\n }\n audioPlayer.play(pcm).catch((error) => emit('error', { error }))\n break\n }\n case 'go_away':\n emit('go_away', { timeLeft: response.data.timeLeft })\n break\n case 'usage_metadata':\n emit('usage', {\n completionTokens: response.data.responseTokenCount ?? 0,\n promptTokens: response.data.promptTokenCount ?? 0,\n totalTokens: response.data.totalTokenCount ?? 0,\n })\n break\n case 'input_transcription':\n if (response.data.finished && currentMode !== 'thinking') {\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n }\n emit('transcript', {\n isFinal: response.data.finished,\n transcript: response.data.text,\n role: 'user',\n })\n break\n case 'output_transcription':\n pendingAssistantResponse += response.data.text\n emit('transcript', {\n isFinal: response.data.finished,\n transcript: pendingAssistantResponse,\n role: 'assistant',\n })\n break\n case 'interrupted':\n audioPlayer.interrupt()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n emit('interrupted', { messageId: currentMessageId ?? undefined })\n break\n case 'tool_call':\n for (const tool of response.data.functionCalls || []) {\n if (tool.id && tool.name) {\n emit('tool_call', {\n toolCallId: tool.id,\n input: tool.args,\n toolName: tool.name,\n })\n }\n }\n break\n case 'turn_complete':\n currentMessageId = generateMessageId()\n message.id = currentMessageId\n message.timestamp = Date.now()\n message.parts.push({\n type: 'text',\n content: pendingAssistantResponse,\n })\n emit('message_complete', { message })\n\n pendingAssistantResponse = ''\n message = {\n id: '',\n role: 'assistant',\n timestamp: 0,\n parts: [],\n }\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n break\n case 'setup_complete':\n emit('status_change', { status: 'connected' })\n break\n case 'error':\n emit('error', {\n error: new Error(response.data),\n })\n break\n case 'thought':\n case 'session_resumption_update':\n // Handled internally by GeminiLiveClient; not surfaced to the client.\n break\n }\n }\n\n await client.connect()\n\n const audioStreamer = new AudioStreamer(client)\n const audioPlayer = new AudioPlayer()\n try {\n await audioPlayer.init()\n await audioStreamer.start()\n } catch (error) {\n // Tear down the socket + audio graph if mic/worklet setup fails (e.g. the\n // user denied microphone access) rather than leaking an open connection.\n audioStreamer.stop()\n audioPlayer.destroy()\n client.disconnect()\n throw error\n }\n\n const connection: RealtimeConnection = {\n async disconnect() {\n audioStreamer.stop()\n audioPlayer.destroy()\n client.disconnect()\n currentMode = 'idle'\n emit('status_change', { status: 'idle' })\n },\n\n async startAudioCapture() {\n // Audio capture is established during connection setup\n audioStreamer.startAudioCapture()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n },\n\n stopAudioCapture() {\n audioStreamer.stopAudioCapture()\n currentMode = 'idle'\n emit('mode_change', { mode: 'idle' })\n },\n\n sendText(text: string) {\n client.sendTextMessage(text)\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n },\n\n sendImage(imageData: string, mimeType: string) {\n client.sendImageMessage(imageData, mimeType)\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n },\n\n sendToolResult(callId: string, result: string) {\n client.sendToolResponse([\n {\n id: callId,\n response: {\n result,\n },\n },\n ])\n },\n\n updateSession(config) {\n client.updateSession(config).catch((error) => emit('error', { error }))\n emit('status_change', { status: 'reconnecting' })\n },\n\n updateToken(newToken) {\n client.updateToken(newToken)\n emit('status_change', { status: 'reconnecting' })\n },\n\n interrupt() {\n audioPlayer.interrupt()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n emit('interrupted', { messageId: currentMessageId ?? undefined })\n },\n on: realtimeEventEmitterOn,\n getAudioVisualization(): AudioVisualization {\n return {\n get inputLevel() {\n return audioStreamer.inputLevel\n },\n\n get outputLevel() {\n return audioPlayer.outputLevel\n },\n\n getInputFrequencyData() {\n return audioStreamer.inputFrequencyData\n },\n\n getOutputFrequencyData() {\n return audioPlayer.outputFrequencyData\n },\n\n getInputTimeDomainData() {\n return audioStreamer.inputTimeDomainData\n },\n\n getOutputTimeDomainData() {\n return audioPlayer.outputTimeDomainData\n },\n\n get inputSampleRate() {\n return audioStreamer.inputSampleRate\n },\n\n get outputSampleRate() {\n return audioPlayer.outputSampleRate\n },\n }\n },\n }\n\n return connection\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"adapter.js","names":[],"sources":["../../../src/realtime/adapter.ts"],"sourcesContent":["import { createRealtimeEventEmitter } from '@tanstack/ai'\nimport { AudioPlayer, AudioStreamer, base64ToArrayBuffer } from './utils'\nimport { GeminiLiveClient } from './client'\nimport type { LiveResponse } from './client'\nimport type {\n AnyClientTool,\n AudioVisualization,\n RealtimeAdapter,\n RealtimeConnection,\n RealtimeMessage,\n RealtimeMode,\n RealtimeToken,\n} from '@tanstack/ai'\nimport type { GeminiRealtimeModel, GeminiRealtimeOptions } from './types'\n\n/**\n * Creates a Gemini realtime adapter for client-side use.\n *\n * @param options - Optional configuration\n * @returns A RealtimeAdapter for use with RealtimeClient\n *\n * @example\n * ```typescript\n * import { RealtimeClient } from '@tanstack/ai-client'\n * import { geminiRealtime } from '@tanstack/ai-gemini'\n *\n * const client = new RealtimeClient({\n * getToken: () => fetch('/api/realtime-token').then(r => r.json()),\n * adapter: geminiRealtime(),\n * onGoAway: () => client.updateSession({ ... }) // Resume session with new config (available only for Gemini Live adapter)\n * })\n *\n * ```\n */\nexport function geminiRealtime(\n options: GeminiRealtimeOptions = {},\n): RealtimeAdapter {\n return {\n provider: 'gemini',\n\n connect(\n token: RealtimeToken,\n clientTools?: ReadonlyArray<AnyClientTool>,\n ): Promise<RealtimeConnection> {\n return createWebSocketConnection(token, options.model, clientTools)\n },\n }\n}\n\n/**\n * Creates a WebSocket connection to Gemini's realtime API\n */\nasync function createWebSocketConnection(\n token: RealtimeToken,\n model: GeminiRealtimeModel = 'gemini-3.1-flash-live-preview',\n tools?: ReadonlyArray<AnyClientTool>,\n): Promise<RealtimeConnection> {\n const { emit, on: realtimeEventEmitterOn } = createRealtimeEventEmitter()\n\n // Current state\n let currentMode: RealtimeMode = 'idle'\n let currentMessageId: string | null = null\n let messageIdCounter = 0\n\n function generateMessageId(): string {\n return `gemini-msg-${Date.now()}-${++messageIdCounter}`\n }\n\n const client = new GeminiLiveClient(token.token, model, tools)\n\n let message: RealtimeMessage = {\n id: '',\n role: 'assistant',\n timestamp: 0,\n parts: [],\n }\n\n let pendingAssistantResponse = ''\n\n client.onClose = () => {\n emit('status_change', { status: 'idle' })\n emit('mode_change', { mode: 'idle' })\n }\n\n client.onError = (error) => {\n emit('error', { error })\n emit('status_change', { status: 'error' })\n emit('mode_change', { mode: 'idle' })\n }\n\n client.onReceiveResponse = (response: LiveResponse) => {\n switch (response.type) {\n case 'text':\n message.parts.push({\n type: 'text',\n content: response.data,\n })\n break\n case 'audio': {\n // Decode once, then reuse for both the stored message part and playback.\n const pcm = base64ToArrayBuffer(response.data.audioData)\n message.parts.push({\n type: 'audio',\n transcript: response.data.transcript,\n audioData: pcm,\n })\n if (currentMode !== 'speaking') {\n currentMode = 'speaking'\n emit('mode_change', { mode: 'speaking' })\n }\n audioPlayer.play(pcm).catch((error) => emit('error', { error }))\n break\n }\n case 'go_away':\n emit('go_away', { timeLeft: response.data.timeLeft })\n break\n case 'usage_metadata':\n emit('usage', {\n completionTokens: response.data.responseTokenCount ?? 0,\n promptTokens: response.data.promptTokenCount ?? 0,\n totalTokens: response.data.totalTokenCount ?? 0,\n })\n break\n case 'input_transcription':\n if (response.data.finished && currentMode !== 'thinking') {\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n }\n emit('transcript', {\n isFinal: response.data.finished,\n transcript: response.data.text,\n role: 'user',\n })\n break\n case 'output_transcription':\n pendingAssistantResponse += response.data.text\n emit('transcript', {\n isFinal: response.data.finished,\n transcript: pendingAssistantResponse,\n role: 'assistant',\n })\n break\n case 'interrupted':\n audioPlayer.interrupt()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n emit('interrupted', { messageId: currentMessageId ?? undefined })\n break\n case 'tool_call':\n for (const tool of response.data.functionCalls || []) {\n if (tool.id && tool.name) {\n emit('tool_call', {\n toolCallId: tool.id,\n input: tool.args,\n toolName: tool.name,\n })\n }\n }\n break\n case 'turn_complete':\n currentMessageId = generateMessageId()\n message.id = currentMessageId\n message.timestamp = Date.now()\n message.parts.push({\n type: 'text',\n content: pendingAssistantResponse,\n })\n emit('message_complete', { message })\n\n pendingAssistantResponse = ''\n message = {\n id: '',\n role: 'assistant',\n timestamp: 0,\n parts: [],\n }\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n break\n case 'setup_complete':\n emit('status_change', { status: 'connected' })\n break\n case 'error':\n emit('error', {\n error: new Error(response.data),\n })\n break\n case 'thought':\n case 'session_resumption_update':\n // Handled internally by GeminiLiveClient; not surfaced to the client.\n break\n }\n }\n\n await client.connect()\n\n const audioStreamer = new AudioStreamer(client)\n const audioPlayer = new AudioPlayer()\n try {\n await audioPlayer.init()\n await audioStreamer.start()\n } catch (error) {\n // Tear down the socket + audio graph if mic/worklet setup fails (e.g. the\n // user denied microphone access) rather than leaking an open connection.\n audioStreamer.stop()\n audioPlayer.destroy()\n client.disconnect()\n throw error\n }\n\n const connection: RealtimeConnection = {\n async disconnect() {\n audioStreamer.stop()\n audioPlayer.destroy()\n client.disconnect()\n currentMode = 'idle'\n emit('status_change', { status: 'idle' })\n },\n\n async startAudioCapture() {\n // Audio capture is established during connection setup\n audioStreamer.startAudioCapture()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n },\n\n stopAudioCapture() {\n audioStreamer.stopAudioCapture()\n currentMode = 'idle'\n emit('mode_change', { mode: 'idle' })\n },\n\n sendText(text: string) {\n client.sendTextMessage(text)\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n },\n\n sendImage(imageData: string, mimeType: string) {\n client.sendImageMessage(imageData, mimeType)\n currentMode = 'thinking'\n emit('mode_change', { mode: 'thinking' })\n },\n\n sendToolResult(callId: string, result: string) {\n client.sendToolResponse([\n {\n id: callId,\n response: {\n result,\n },\n },\n ])\n },\n\n updateSession(config) {\n client.updateSession(config).catch((error) => emit('error', { error }))\n emit('status_change', { status: 'reconnecting' })\n },\n\n updateToken(newToken) {\n client.updateToken(newToken)\n emit('status_change', { status: 'reconnecting' })\n },\n\n interrupt() {\n audioPlayer.interrupt()\n currentMode = 'listening'\n emit('mode_change', { mode: 'listening' })\n emit('interrupted', { messageId: currentMessageId ?? undefined })\n },\n on: realtimeEventEmitterOn,\n getAudioVisualization(): AudioVisualization {\n return {\n get inputLevel() {\n return audioStreamer.inputLevel\n },\n\n get outputLevel() {\n return audioPlayer.outputLevel\n },\n\n getInputFrequencyData() {\n return audioStreamer.inputFrequencyData\n },\n\n getOutputFrequencyData() {\n return audioPlayer.outputFrequencyData\n },\n\n getInputTimeDomainData() {\n return audioStreamer.inputTimeDomainData\n },\n\n getOutputTimeDomainData() {\n return audioPlayer.outputTimeDomainData\n },\n\n get inputSampleRate() {\n return audioStreamer.inputSampleRate\n },\n\n get outputSampleRate() {\n return audioPlayer.outputSampleRate\n },\n }\n },\n }\n\n return connection\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":[],"sources":["../../../src/realtime/client.ts"],"sourcesContent":["import { convertSchemaToJsonSchema } from '@tanstack/ai'\nimport {\n ActivityHandling,\n EndSensitivity,\n Modality,\n StartSensitivity,\n TurnCoverage,\n} from '@google/genai'\nimport type {\n ContextWindowCompressionConfig,\n FunctionDeclaration,\n FunctionResponse,\n LiveClientMessage,\n LiveServerGoAway,\n LiveServerMessage,\n LiveServerSessionResumptionUpdate,\n LiveServerToolCall,\n ThinkingConfig,\n UsageMetadata,\n} from '@google/genai'\nimport type {\n AnyClientTool,\n RealtimeSessionConfig,\n RealtimeToken,\n RealtimeToolConfig,\n} from '@tanstack/ai'\nimport type {\n GeminiRealtimeModel,\n GeminiRealtimeProviderOptions,\n GeminiRealtimeVoice,\n} from './types'\n\n/** Build a Gemini FunctionDeclaration from an isomorphic client tool (Zod). */\nfunction clientToolToDeclaration(tool: AnyClientTool): FunctionDeclaration {\n return {\n name: tool.name,\n description: tool.description,\n parametersJsonSchema: convertSchemaToJsonSchema(tool.inputSchema),\n responseJsonSchema: convertSchemaToJsonSchema(tool.outputSchema),\n }\n}\n\n/** Build a Gemini FunctionDeclaration from an already-serialized tool config. */\nfunction toolConfigToDeclaration(\n tool: RealtimeToolConfig,\n): FunctionDeclaration {\n return {\n name: tool.name,\n description: tool.description,\n parametersJsonSchema: tool.inputSchema,\n responseJsonSchema: tool.outputSchema,\n }\n}\n\ninterface LiveResponsePayloads {\n text: string\n thought: string\n audio: { audioData: string; transcript: string }\n setup_complete: string\n interrupted: string\n turn_complete: string\n tool_call: LiveServerToolCall\n session_resumption_update: LiveServerSessionResumptionUpdate\n go_away: LiveServerGoAway\n usage_metadata: UsageMetadata\n error: string\n input_transcription: { text: string; finished: boolean }\n output_transcription: { text: string; finished: boolean }\n}\n\nexport type MultimodalLiveResponseType = keyof LiveResponsePayloads\n\nexport type LiveResponse = {\n [K in MultimodalLiveResponseType]: {\n type: K\n data: LiveResponsePayloads[K]\n endOfTurn: boolean\n }\n}[MultimodalLiveResponseType]\n\n/**\n * Parses response messages from the Gemini Live API\n */\n/**\n * Parses ALL response types from a single server message.\n * The server can now bundle multiple fields (e.g. audio + transcription)\n * in the same message. Returns an array of response objects.\n */\nexport function parseResponseMessages(\n data: LiveServerMessage,\n): Array<LiveResponse> {\n const responses: Array<LiveResponse> = []\n const serverContent = data.serverContent\n const parts = serverContent?.modelTurn?.parts\n\n // Setup complete (exclusive — no other fields expected)\n if (data.setupComplete) {\n responses.push({ type: 'setup_complete', data: '', endOfTurn: false })\n return responses\n }\n\n // Tool call (exclusive)\n if (data.toolCall) {\n responses.push({\n type: 'tool_call',\n data: data.toolCall,\n endOfTurn: false,\n })\n return responses\n }\n\n if (data.sessionResumptionUpdate) {\n responses.push({\n type: 'session_resumption_update',\n data: data.sessionResumptionUpdate,\n endOfTurn: false,\n })\n }\n\n if (data.goAway) {\n responses.push({ type: 'go_away', data: data.goAway, endOfTurn: false })\n }\n\n if (data.usageMetadata) {\n responses.push({\n type: 'usage_metadata',\n data: data.usageMetadata,\n endOfTurn: false,\n })\n }\n\n // Audio data from model turn parts\n if (parts?.length) {\n for (const part of parts) {\n if (part.inlineData?.data) {\n responses.push({\n type: 'audio',\n data: {\n audioData: part.inlineData.data,\n // The transcription is independent to the model turn, which means it doesn't imply any ordering between transcription and model turn.\n transcript: '',\n },\n endOfTurn: false,\n })\n } else if (part.text) {\n responses.push({\n type: part.thought ? 'thought' : 'text',\n data: part.text,\n endOfTurn: false,\n })\n }\n }\n }\n\n // Transcriptions — checked independently, NOT in else-if with audio\n if (serverContent?.inputTranscription) {\n responses.push({\n type: 'input_transcription',\n data: {\n text: serverContent.inputTranscription.text || '',\n finished: serverContent.inputTranscription.finished || false,\n },\n endOfTurn: false,\n })\n }\n\n if (serverContent?.outputTranscription) {\n responses.push({\n type: 'output_transcription',\n data: {\n text: serverContent.outputTranscription.text || '',\n finished: serverContent.outputTranscription.finished || false,\n },\n endOfTurn: false,\n })\n }\n\n // Interrupted\n if (serverContent?.interrupted) {\n responses.push({ type: 'interrupted', data: '', endOfTurn: false })\n }\n\n // Turn complete\n if (serverContent?.turnComplete) {\n responses.push({ type: 'turn_complete', data: '', endOfTurn: true })\n }\n\n return responses\n}\n\nexport class GeminiLiveClient {\n private token: string | null = null\n private model: GeminiRealtimeModel | null = null\n\n private readonly responseModalities: Array<Modality> = [Modality.AUDIO]\n private systemInstructions = ''\n private googleGrounding = false\n private voiceName: GeminiRealtimeVoice = 'Puck'\n private temperature = 1.0\n private inputAudioTranscription = false\n private outputAudioTranscription = false\n private contextWindowCompression: ContextWindowCompressionConfig | undefined\n private proactiveAudio = false\n private enableAffectiveDialog = false\n private thinkingConfig: ThinkingConfig | undefined\n private speechLanguageCode: string | undefined\n\n private maxOutputTokens: number | undefined\n private functionDeclarations: Array<FunctionDeclaration> = []\n\n private readonly automaticActivityDetection = {\n disabled: false,\n silence_duration_ms: 2000,\n prefix_padding_ms: 500,\n end_of_speech_sensitivity: EndSensitivity.END_SENSITIVITY_UNSPECIFIED,\n start_of_speech_sensitivity: StartSensitivity.START_SENSITIVITY_UNSPECIFIED,\n }\n\n private readonly activityHandling =\n ActivityHandling.ACTIVITY_HANDLING_UNSPECIFIED\n\n private webSocket: WebSocket | null = null\n private lastResumptionUpdate: LiveServerSessionResumptionUpdate | null = null\n private setupComplete = false\n private connected = false\n\n public onReceiveResponse: (response: LiveResponse) => void = () => {}\n public onOpen: () => void = () => {}\n public onClose: () => void = () => {}\n public onError: (error: Error) => void = () => {}\n\n constructor(\n token: string,\n model: GeminiRealtimeModel,\n tools?: ReadonlyArray<AnyClientTool>,\n ) {\n this.token = token\n this.model = model\n\n if (tools) {\n this.functionDeclarations = tools.map(clientToolToDeclaration)\n }\n }\n\n get isConnected() {\n return this.connected\n }\n\n get isSetupComplete() {\n return this.setupComplete\n }\n\n /**\n * Connection management\n */\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = new WebSocket(\n `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContentConstrained?access_token=${this.token}`,\n )\n this.webSocket = socket\n\n // The browser fires `onerror` then `onclose` on an abnormal close; this\n // flag stops the close handler from overwriting the surfaced error with a\n // benign \"closed\" signal.\n let errored = false\n\n socket.onclose = () => {\n this.connected = false\n this.setupComplete = false\n if (!errored) this.onClose()\n reject(new Error('WebSocket closed before setup completed'))\n }\n\n socket.onerror = () => {\n errored = true\n this.connected = false\n this.setupComplete = false\n const error = new Error('Gemini realtime WebSocket connection error')\n this.onError(error)\n reject(error)\n }\n\n socket.onopen = () => {\n this.connected = true\n this.onOpen()\n resolve()\n }\n\n socket.onmessage = (event) => {\n void this.onReceiveMessage(event)\n }\n })\n }\n\n disconnect() {\n if (this.webSocket) {\n // Detach handlers first so a deliberate teardown (e.g. during a\n // reconnect) doesn't emit a spurious close/error to the client.\n this.webSocket.onclose = null\n this.webSocket.onerror = null\n this.webSocket.onopen = null\n this.webSocket.onmessage = null\n this.webSocket.close()\n this.webSocket = null\n }\n this.connected = false\n this.setupComplete = false\n }\n\n /**\n * Session management\n */\n sendInitialSetupMessage(resume = false) {\n const tools = this.functionDeclarations\n\n const setup: NonNullable<LiveClientMessage['setup']> = {\n model: `models/${this.model}`,\n generationConfig: {\n responseModalities: this.responseModalities,\n temperature: this.temperature,\n speechConfig: {\n languageCode: this.speechLanguageCode,\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: this.voiceName,\n },\n },\n },\n enableAffectiveDialog: this.enableAffectiveDialog,\n maxOutputTokens: this.maxOutputTokens,\n thinkingConfig: this.thinkingConfig,\n },\n sessionResumption: {\n transparent: true,\n handle: resume ? this.lastResumptionUpdate?.newHandle : undefined,\n },\n contextWindowCompression: this.contextWindowCompression,\n proactivity: {\n proactiveAudio: this.proactiveAudio,\n },\n systemInstruction: { parts: [{ text: this.systemInstructions }] },\n tools: [{ functionDeclarations: tools }],\n realtimeInputConfig: {\n automaticActivityDetection: {\n disabled: this.automaticActivityDetection.disabled,\n silenceDurationMs:\n this.automaticActivityDetection.silence_duration_ms,\n prefixPaddingMs: this.automaticActivityDetection.prefix_padding_ms,\n endOfSpeechSensitivity:\n this.automaticActivityDetection.end_of_speech_sensitivity,\n startOfSpeechSensitivity:\n this.automaticActivityDetection.start_of_speech_sensitivity,\n },\n activityHandling: this.activityHandling,\n turnCoverage: TurnCoverage.TURN_INCLUDES_ONLY_ACTIVITY,\n },\n }\n\n if (this.inputAudioTranscription) {\n setup.inputAudioTranscription = {}\n }\n\n if (this.outputAudioTranscription) {\n setup.outputAudioTranscription = {}\n }\n\n if (this.googleGrounding) {\n // Currently can't have both Google Search with custom tools.\n console.warn(\n 'Google Grounding enabled, removing custom function calls if any.',\n )\n setup.tools = [{ googleSearch: {} }]\n }\n\n this.sendMessage({ setup })\n }\n\n async restartSession(resume = false) {\n this.disconnect()\n await this.connect()\n this.sendInitialSetupMessage(resume)\n }\n\n updateToken(token: RealtimeToken) {\n this.token = token.token\n\n // Restart completely with the new model, or resume the existing session.\n const resume = !(token.config.model && this.model != token.config.model)\n if (!resume) {\n this.model = token.config.model as GeminiRealtimeModel\n }\n this.restartSession(resume).catch((err) =>\n this.onError(err instanceof Error ? err : new Error(String(err))),\n )\n }\n\n async updateSession(config: Partial<RealtimeSessionConfig>) {\n // model can only be set during initial setup\n if (config.model && !this.setupComplete) {\n this.model = config.model as GeminiRealtimeModel\n }\n\n if (config.instructions) {\n this.systemInstructions = config.instructions\n }\n\n if (config.tools) {\n this.functionDeclarations = config.tools.map(toolConfigToDeclaration)\n }\n\n if (config.maxOutputTokens) {\n // Gemini has no \"inf\" sentinel; treat it as \"no explicit limit\".\n this.maxOutputTokens =\n typeof config.maxOutputTokens === 'number'\n ? config.maxOutputTokens\n : undefined\n }\n\n if (config.temperature) {\n this.temperature = config.temperature\n }\n\n if (config.voice) {\n this.voiceName = config.voice as GeminiRealtimeVoice\n }\n\n const providerOptions = config.providerOptions as\n | GeminiRealtimeProviderOptions\n | undefined\n\n if (providerOptions?.googleGrounding) {\n this.googleGrounding = providerOptions.googleGrounding\n }\n\n if (providerOptions?.proactiveAudio) {\n this.proactiveAudio = providerOptions.proactiveAudio\n }\n\n if (providerOptions?.enableAffectiveDialog) {\n this.enableAffectiveDialog = providerOptions.enableAffectiveDialog\n }\n\n if (providerOptions?.contextWindowCompression) {\n this.contextWindowCompression = providerOptions.contextWindowCompression\n }\n\n if (providerOptions?.thinkingConfig) {\n this.thinkingConfig = providerOptions.thinkingConfig\n }\n\n if (providerOptions?.languageCode) {\n this.speechLanguageCode = providerOptions.languageCode\n }\n\n const includeTranscription =\n config.outputModalities?.includes('text') || false\n this.inputAudioTranscription = includeTranscription\n this.outputAudioTranscription = includeTranscription\n\n if (!this.setupComplete) {\n this.sendInitialSetupMessage()\n } else {\n return this.restartSession(true)\n }\n }\n\n /**\n * Message transmission & receiving\n */\n sendMessage(message: LiveClientMessage) {\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.send(JSON.stringify(message))\n } else {\n this.onError(\n new Error('Cannot send message: Gemini realtime socket is not open'),\n )\n }\n }\n\n async onReceiveMessage(messageEvent: MessageEvent) {\n let jsonData\n if (messageEvent.data instanceof Blob) {\n jsonData = await messageEvent.data.text()\n } else if (messageEvent.data instanceof ArrayBuffer) {\n jsonData = new TextDecoder().decode(messageEvent.data)\n } else {\n jsonData = messageEvent.data\n }\n\n try {\n const messageData = JSON.parse(jsonData)\n // Parse all response types from this message (audio + transcription can coexist)\n const responses = parseResponseMessages(messageData)\n for (const response of responses) {\n if (\n response.type === 'session_resumption_update' &&\n response.data.resumable\n ) {\n this.lastResumptionUpdate = response.data\n }\n if (response.type === 'setup_complete') {\n this.setupComplete = true\n }\n this.onReceiveResponse(response)\n }\n } catch (err) {\n this.onError(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n sendRealtimeInputMessage(data: string, mimeType: string) {\n const blob = { mimeType, data }\n\n if (mimeType.startsWith('audio/')) {\n this.sendMessage({ realtimeInput: { audio: blob } })\n } else if (mimeType.startsWith('image/') || mimeType.startsWith('video/')) {\n this.sendMessage({ realtimeInput: { video: blob } })\n }\n }\n\n sendAudioMessage(base64PCM: string) {\n this.sendRealtimeInputMessage(base64PCM, 'audio/pcm')\n }\n\n sendImageMessage(base64: string, mimeType = 'image/jpeg') {\n this.sendRealtimeInputMessage(base64, mimeType)\n }\n\n sendTextMessage(text: string) {\n const message: LiveClientMessage = {\n realtimeInput: {\n text,\n },\n }\n this.sendMessage(message)\n }\n\n sendToolResponse(functionResponses: Array<FunctionResponse>) {\n const message: LiveClientMessage = {\n toolResponse: {\n functionResponses,\n },\n }\n this.sendMessage(message)\n }\n}\n"],"mappings":";;;;AAiCA,SAAS,wBAAwB,MAA0C;CACzE,OAAO;EACL,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,sBAAsB,0BAA0B,KAAK,WAAW;EAChE,oBAAoB,0BAA0B,KAAK,YAAY;CACjE;AACF;;AAGA,SAAS,wBACP,MACqB;CACrB,OAAO;EACL,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,sBAAsB,KAAK;EAC3B,oBAAoB,KAAK;CAC3B;AACF;;;;;;;;;AAoCA,SAAgB,sBACd,MACqB;CACrB,MAAM,YAAiC,CAAC;CACxC,MAAM,gBAAgB,KAAK;CAC3B,MAAM,QAAQ,eAAe,WAAW;CAGxC,IAAI,KAAK,eAAe;EACtB,UAAU,KAAK;GAAE,MAAM;GAAkB,MAAM;GAAI,WAAW;EAAM,CAAC;EACrE,OAAO;CACT;CAGA,IAAI,KAAK,UAAU;EACjB,UAAU,KAAK;GACb,MAAM;GACN,MAAM,KAAK;GACX,WAAW;EACb,CAAC;EACD,OAAO;CACT;CAEA,IAAI,KAAK,yBACP,UAAU,KAAK;EACb,MAAM;EACN,MAAM,KAAK;EACX,WAAW;CACb,CAAC;CAGH,IAAI,KAAK,QACP,UAAU,KAAK;EAAE,MAAM;EAAW,MAAM,KAAK;EAAQ,WAAW;CAAM,CAAC;CAGzE,IAAI,KAAK,eACP,UAAU,KAAK;EACb,MAAM;EACN,MAAM,KAAK;EACX,WAAW;CACb,CAAC;CAIH,IAAI,OAAO;OACJ,MAAM,QAAQ,OACjB,IAAI,KAAK,YAAY,MACnB,UAAU,KAAK;GACb,MAAM;GACN,MAAM;IACJ,WAAW,KAAK,WAAW;IAE3B,YAAY;GACd;GACA,WAAW;EACb,CAAC;OACI,IAAI,KAAK,MACd,UAAU,KAAK;GACb,MAAM,KAAK,UAAU,YAAY;GACjC,MAAM,KAAK;GACX,WAAW;EACb,CAAC;CAAA;CAMP,IAAI,eAAe,oBACjB,UAAU,KAAK;EACb,MAAM;EACN,MAAM;GACJ,MAAM,cAAc,mBAAmB,QAAQ;GAC/C,UAAU,cAAc,mBAAmB,YAAY;EACzD;EACA,WAAW;CACb,CAAC;CAGH,IAAI,eAAe,qBACjB,UAAU,KAAK;EACb,MAAM;EACN,MAAM;GACJ,MAAM,cAAc,oBAAoB,QAAQ;GAChD,UAAU,cAAc,oBAAoB,YAAY;EAC1D;EACA,WAAW;CACb,CAAC;CAIH,IAAI,eAAe,aACjB,UAAU,KAAK;EAAE,MAAM;EAAe,MAAM;EAAI,WAAW;CAAM,CAAC;CAIpE,IAAI,eAAe,cACjB,UAAU,KAAK;EAAE,MAAM;EAAiB,MAAM;EAAI,WAAW;CAAK,CAAC;CAGrE,OAAO;AACT;AAEA,IAAa,mBAAb,MAA8B;CAC5B,QAA+B;CAC/B,QAA4C;CAE5C,qBAAuD,CAAC,SAAS,KAAK;CACtE,qBAA6B;CAC7B,kBAA0B;CAC1B,YAAyC;CACzC,cAAsB;CACtB,0BAAkC;CAClC,2BAAmC;CACnC;CACA,iBAAyB;CACzB,wBAAgC;CAChC;CACA;CAEA;CACA,uBAA2D,CAAC;CAE5D,6BAA8C;EAC5C,UAAU;EACV,qBAAqB;EACrB,mBAAmB;EACnB,2BAA2B,eAAe;EAC1C,6BAA6B,iBAAiB;CAChD;CAEA,mBACE,iBAAiB;CAEnB,YAAsC;CACtC,uBAAyE;CACzE,gBAAwB;CACxB,YAAoB;CAEpB,0BAAmE,CAAC;CACpE,eAAkC,CAAC;CACnC,gBAAmC,CAAC;CACpC,gBAA+C,CAAC;CAEhD,YACE,OACA,OACA,OACA;EACA,KAAK,QAAQ;EACb,KAAK,QAAQ;EAEb,IAAI,OACF,KAAK,uBAAuB,MAAM,IAAI,uBAAuB;CAEjE;CAEA,IAAI,cAAc;EAChB,OAAO,KAAK;CACd;CAEA,IAAI,kBAAkB;EACpB,OAAO,KAAK;CACd;;;;CAKA,UAAyB;EACvB,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,SAAS,IAAI,UACjB,iJAAiJ,KAAK,OACxJ;GACA,KAAK,YAAY;GAKjB,IAAI,UAAU;GAEd,OAAO,gBAAgB;IACrB,KAAK,YAAY;IACjB,KAAK,gBAAgB;IACrB,IAAI,CAAC,SAAS,KAAK,QAAQ;IAC3B,uBAAO,IAAI,MAAM,yCAAyC,CAAC;GAC7D;GAEA,OAAO,gBAAgB;IACrB,UAAU;IACV,KAAK,YAAY;IACjB,KAAK,gBAAgB;IACrB,MAAM,wBAAQ,IAAI,MAAM,4CAA4C;IACpE,KAAK,QAAQ,KAAK;IAClB,OAAO,KAAK;GACd;GAEA,OAAO,eAAe;IACpB,KAAK,YAAY;IACjB,KAAK,OAAO;IACZ,QAAQ;GACV;GAEA,OAAO,aAAa,UAAU;IAC5B,KAAU,iBAAiB,KAAK;GAClC;EACF,CAAC;CACH;CAEA,aAAa;EACX,IAAI,KAAK,WAAW;GAGlB,KAAK,UAAU,UAAU;GACzB,KAAK,UAAU,UAAU;GACzB,KAAK,UAAU,SAAS;GACxB,KAAK,UAAU,YAAY;GAC3B,KAAK,UAAU,MAAM;GACrB,KAAK,YAAY;EACnB;EACA,KAAK,YAAY;EACjB,KAAK,gBAAgB;CACvB;;;;CAKA,wBAAwB,SAAS,OAAO;EACtC,MAAM,QAAQ,KAAK;EAEnB,MAAM,QAAiD;GACrD,OAAO,UAAU,KAAK;GACtB,kBAAkB;IAChB,oBAAoB,KAAK;IACzB,aAAa,KAAK;IAClB,cAAc;KACZ,cAAc,KAAK;KACnB,aAAa,EACX,qBAAqB,EACnB,WAAW,KAAK,UAClB,EACF;IACF;IACA,uBAAuB,KAAK;IAC5B,iBAAiB,KAAK;IACtB,gBAAgB,KAAK;GACvB;GACA,mBAAmB;IACjB,aAAa;IACb,QAAQ,SAAS,KAAK,sBAAsB,YAAY,KAAA;GAC1D;GACA,0BAA0B,KAAK;GAC/B,aAAa,EACX,gBAAgB,KAAK,eACvB;GACA,mBAAmB,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,mBAAmB,CAAC,EAAE;GAChE,OAAO,CAAC,EAAE,sBAAsB,MAAM,CAAC;GACvC,qBAAqB;IACnB,4BAA4B;KAC1B,UAAU,KAAK,2BAA2B;KAC1C,mBACE,KAAK,2BAA2B;KAClC,iBAAiB,KAAK,2BAA2B;KACjD,wBACE,KAAK,2BAA2B;KAClC,0BACE,KAAK,2BAA2B;IACpC;IACA,kBAAkB,KAAK;IACvB,cAAc,aAAa;GAC7B;EACF;EAEA,IAAI,KAAK,yBACP,MAAM,0BAA0B,CAAC;EAGnC,IAAI,KAAK,0BACP,MAAM,2BAA2B,CAAC;EAGpC,IAAI,KAAK,iBAAiB;GAExB,QAAQ,KACN,kEACF;GACA,MAAM,QAAQ,CAAC,EAAE,cAAc,CAAC,EAAE,CAAC;EACrC;EAEA,KAAK,YAAY,EAAE,MAAM,CAAC;CAC5B;CAEA,MAAM,eAAe,SAAS,OAAO;EACnC,KAAK,WAAW;EAChB,MAAM,KAAK,QAAQ;EACnB,KAAK,wBAAwB,MAAM;CACrC;CAEA,YAAY,OAAsB;EAChC,KAAK,QAAQ,MAAM;EAGnB,MAAM,SAAS,EAAE,MAAM,OAAO,SAAS,KAAK,SAAS,MAAM,OAAO;EAClE,IAAI,CAAC,QACH,KAAK,QAAQ,MAAM,OAAO;EAE5B,KAAK,eAAe,MAAM,CAAC,CAAC,OAAO,QACjC,KAAK,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC,CAClE;CACF;CAEA,MAAM,cAAc,QAAwC;EAE1D,IAAI,OAAO,SAAS,CAAC,KAAK,eACxB,KAAK,QAAQ,OAAO;EAGtB,IAAI,OAAO,cACT,KAAK,qBAAqB,OAAO;EAGnC,IAAI,OAAO,OACT,KAAK,uBAAuB,OAAO,MAAM,IAAI,uBAAuB;EAGtE,IAAI,OAAO,iBAET,KAAK,kBACH,OAAO,OAAO,oBAAoB,WAC9B,OAAO,kBACP,KAAA;EAGR,IAAI,OAAO,aACT,KAAK,cAAc,OAAO;EAG5B,IAAI,OAAO,OACT,KAAK,YAAY,OAAO;EAG1B,MAAM,kBAAkB,OAAO;EAI/B,IAAI,iBAAiB,iBACnB,KAAK,kBAAkB,gBAAgB;EAGzC,IAAI,iBAAiB,gBACnB,KAAK,iBAAiB,gBAAgB;EAGxC,IAAI,iBAAiB,uBACnB,KAAK,wBAAwB,gBAAgB;EAG/C,IAAI,iBAAiB,0BACnB,KAAK,2BAA2B,gBAAgB;EAGlD,IAAI,iBAAiB,gBACnB,KAAK,iBAAiB,gBAAgB;EAGxC,IAAI,iBAAiB,cACnB,KAAK,qBAAqB,gBAAgB;EAG5C,MAAM,uBACJ,OAAO,kBAAkB,SAAS,MAAM,KAAK;EAC/C,KAAK,0BAA0B;EAC/B,KAAK,2BAA2B;EAEhC,IAAI,CAAC,KAAK,eACR,KAAK,wBAAwB;OAE7B,OAAO,KAAK,eAAe,IAAI;CAEnC;;;;CAKA,YAAY,SAA4B;EACtC,IAAI,KAAK,WAAW,eAAe,UAAU,MAC3C,KAAK,UAAU,KAAK,KAAK,UAAU,OAAO,CAAC;OAE3C,KAAK,wBACH,IAAI,MAAM,yDAAyD,CACrE;CAEJ;CAEA,MAAM,iBAAiB,cAA4B;EACjD,IAAI;EACJ,IAAI,aAAa,gBAAgB,MAC/B,WAAW,MAAM,aAAa,KAAK,KAAK;OACnC,IAAI,aAAa,gBAAgB,aACtC,WAAW,IAAI,YAAY,CAAC,CAAC,OAAO,aAAa,IAAI;OAErD,WAAW,aAAa;EAG1B,IAAI;GAGF,MAAM,YAAY,sBAFE,KAAK,MAAM,QAES,CAAW;GACnD,KAAK,MAAM,YAAY,WAAW;IAChC,IACE,SAAS,SAAS,+BAClB,SAAS,KAAK,WAEd,KAAK,uBAAuB,SAAS;IAEvC,IAAI,SAAS,SAAS,kBACpB,KAAK,gBAAgB;IAEvB,KAAK,kBAAkB,QAAQ;GACjC;EACF,SAAS,KAAK;GACZ,KAAK,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;EAClE;CACF;CAEA,yBAAyB,MAAc,UAAkB;EACvD,MAAM,OAAO;GAAE;GAAU;EAAK;EAE9B,IAAI,SAAS,WAAW,QAAQ,GAC9B,KAAK,YAAY,EAAE,eAAe,EAAE,OAAO,KAAK,EAAE,CAAC;OAC9C,IAAI,SAAS,WAAW,QAAQ,KAAK,SAAS,WAAW,QAAQ,GACtE,KAAK,YAAY,EAAE,eAAe,EAAE,OAAO,KAAK,EAAE,CAAC;CAEvD;CAEA,iBAAiB,WAAmB;EAClC,KAAK,yBAAyB,WAAW,WAAW;CACtD;CAEA,iBAAiB,QAAgB,WAAW,cAAc;EACxD,KAAK,yBAAyB,QAAQ,QAAQ;CAChD;CAEA,gBAAgB,MAAc;EAC5B,MAAM,UAA6B,EACjC,eAAe,EACb,KACF,EACF;EACA,KAAK,YAAY,OAAO;CAC1B;CAEA,iBAAiB,mBAA4C;EAC3D,MAAM,UAA6B,EACjC,cAAc,EACZ,kBACF,EACF;EACA,KAAK,YAAY,OAAO;CAC1B;AACF"}
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../../../src/realtime/client.ts"],"sourcesContent":["import { convertSchemaToJsonSchema } from '@tanstack/ai'\nimport {\n ActivityHandling,\n EndSensitivity,\n Modality,\n StartSensitivity,\n TurnCoverage,\n} from '@google/genai'\nimport type {\n ContextWindowCompressionConfig,\n FunctionDeclaration,\n FunctionResponse,\n LiveClientMessage,\n LiveServerGoAway,\n LiveServerMessage,\n LiveServerSessionResumptionUpdate,\n LiveServerToolCall,\n ThinkingConfig,\n UsageMetadata,\n} from '@google/genai'\nimport type {\n AnyClientTool,\n RealtimeSessionConfig,\n RealtimeToken,\n RealtimeToolConfig,\n} from '@tanstack/ai'\nimport type {\n GeminiRealtimeModel,\n GeminiRealtimeProviderOptions,\n GeminiRealtimeVoice,\n} from './types'\n\n/** Build a Gemini FunctionDeclaration from an isomorphic client tool (Zod). */\nfunction clientToolToDeclaration(tool: AnyClientTool): FunctionDeclaration {\n return {\n name: tool.name,\n description: tool.description,\n parametersJsonSchema: convertSchemaToJsonSchema(tool.inputSchema),\n responseJsonSchema: convertSchemaToJsonSchema(tool.outputSchema),\n }\n}\n\n/** Build a Gemini FunctionDeclaration from an already-serialized tool config. */\nfunction toolConfigToDeclaration(\n tool: RealtimeToolConfig,\n): FunctionDeclaration {\n return {\n name: tool.name,\n description: tool.description,\n parametersJsonSchema: tool.inputSchema,\n responseJsonSchema: tool.outputSchema,\n }\n}\n\ninterface LiveResponsePayloads {\n text: string\n thought: string\n audio: { audioData: string; transcript: string }\n setup_complete: string\n interrupted: string\n turn_complete: string\n tool_call: LiveServerToolCall\n session_resumption_update: LiveServerSessionResumptionUpdate\n go_away: LiveServerGoAway\n usage_metadata: UsageMetadata\n error: string\n input_transcription: { text: string; finished: boolean }\n output_transcription: { text: string; finished: boolean }\n}\n\nexport type MultimodalLiveResponseType = keyof LiveResponsePayloads\n\nexport type LiveResponse = {\n [K in MultimodalLiveResponseType]: {\n type: K\n data: LiveResponsePayloads[K]\n endOfTurn: boolean\n }\n}[MultimodalLiveResponseType]\n\n/**\n * Parses response messages from the Gemini Live API\n */\n/**\n * Parses ALL response types from a single server message.\n * The server can now bundle multiple fields (e.g. audio + transcription)\n * in the same message. Returns an array of response objects.\n */\nexport function parseResponseMessages(\n data: LiveServerMessage,\n): Array<LiveResponse> {\n const responses: Array<LiveResponse> = []\n const serverContent = data.serverContent\n const parts = serverContent?.modelTurn?.parts\n\n // Setup complete (exclusive — no other fields expected)\n if (data.setupComplete) {\n responses.push({ type: 'setup_complete', data: '', endOfTurn: false })\n return responses\n }\n\n // Tool call (exclusive)\n if (data.toolCall) {\n responses.push({\n type: 'tool_call',\n data: data.toolCall,\n endOfTurn: false,\n })\n return responses\n }\n\n if (data.sessionResumptionUpdate) {\n responses.push({\n type: 'session_resumption_update',\n data: data.sessionResumptionUpdate,\n endOfTurn: false,\n })\n }\n\n if (data.goAway) {\n responses.push({ type: 'go_away', data: data.goAway, endOfTurn: false })\n }\n\n if (data.usageMetadata) {\n responses.push({\n type: 'usage_metadata',\n data: data.usageMetadata,\n endOfTurn: false,\n })\n }\n\n // Audio data from model turn parts\n if (parts?.length) {\n for (const part of parts) {\n if (part.inlineData?.data) {\n responses.push({\n type: 'audio',\n data: {\n audioData: part.inlineData.data,\n // The transcription is independent to the model turn, which means it doesn't imply any ordering between transcription and model turn.\n transcript: '',\n },\n endOfTurn: false,\n })\n } else if (part.text) {\n responses.push({\n type: part.thought ? 'thought' : 'text',\n data: part.text,\n endOfTurn: false,\n })\n }\n }\n }\n\n // Transcriptions — checked independently, NOT in else-if with audio\n if (serverContent?.inputTranscription) {\n responses.push({\n type: 'input_transcription',\n data: {\n text: serverContent.inputTranscription.text || '',\n finished: serverContent.inputTranscription.finished || false,\n },\n endOfTurn: false,\n })\n }\n\n if (serverContent?.outputTranscription) {\n responses.push({\n type: 'output_transcription',\n data: {\n text: serverContent.outputTranscription.text || '',\n finished: serverContent.outputTranscription.finished || false,\n },\n endOfTurn: false,\n })\n }\n\n // Interrupted\n if (serverContent?.interrupted) {\n responses.push({ type: 'interrupted', data: '', endOfTurn: false })\n }\n\n // Turn complete\n if (serverContent?.turnComplete) {\n responses.push({ type: 'turn_complete', data: '', endOfTurn: true })\n }\n\n return responses\n}\n\nexport class GeminiLiveClient {\n private token: string | null = null\n private model: GeminiRealtimeModel | null = null\n\n private readonly responseModalities: Array<Modality> = [Modality.AUDIO]\n private systemInstructions = ''\n private googleGrounding = false\n private voiceName: GeminiRealtimeVoice = 'Puck'\n private temperature = 1.0\n private inputAudioTranscription = false\n private outputAudioTranscription = false\n private contextWindowCompression: ContextWindowCompressionConfig | undefined\n private proactiveAudio = false\n private enableAffectiveDialog = false\n private thinkingConfig: ThinkingConfig | undefined\n private speechLanguageCode: string | undefined\n\n private maxOutputTokens: number | undefined\n private functionDeclarations: Array<FunctionDeclaration> = []\n\n private readonly automaticActivityDetection = {\n disabled: false,\n silence_duration_ms: 2000,\n prefix_padding_ms: 500,\n end_of_speech_sensitivity: EndSensitivity.END_SENSITIVITY_UNSPECIFIED,\n start_of_speech_sensitivity: StartSensitivity.START_SENSITIVITY_UNSPECIFIED,\n }\n\n private readonly activityHandling =\n ActivityHandling.ACTIVITY_HANDLING_UNSPECIFIED\n\n private webSocket: WebSocket | null = null\n private lastResumptionUpdate: LiveServerSessionResumptionUpdate | null = null\n private setupComplete = false\n private connected = false\n\n public onReceiveResponse: (response: LiveResponse) => void = () => {}\n public onOpen: () => void = () => {}\n public onClose: () => void = () => {}\n public onError: (error: Error) => void = () => {}\n\n constructor(\n token: string,\n model: GeminiRealtimeModel,\n tools?: ReadonlyArray<AnyClientTool>,\n ) {\n this.token = token\n this.model = model\n\n if (tools) {\n this.functionDeclarations = tools.map(clientToolToDeclaration)\n }\n }\n\n get isConnected() {\n return this.connected\n }\n\n get isSetupComplete() {\n return this.setupComplete\n }\n\n /**\n * Connection management\n */\n connect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const socket = new WebSocket(\n `wss://generativelanguage.googleapis.com/ws/google.ai.generativelanguage.v1alpha.GenerativeService.BidiGenerateContentConstrained?access_token=${this.token}`,\n )\n this.webSocket = socket\n\n // The browser fires `onerror` then `onclose` on an abnormal close; this\n // flag stops the close handler from overwriting the surfaced error with a\n // benign \"closed\" signal.\n let errored = false\n\n socket.onclose = () => {\n this.connected = false\n this.setupComplete = false\n if (!errored) this.onClose()\n reject(new Error('WebSocket closed before setup completed'))\n }\n\n socket.onerror = () => {\n errored = true\n this.connected = false\n this.setupComplete = false\n const error = new Error('Gemini realtime WebSocket connection error')\n this.onError(error)\n reject(error)\n }\n\n socket.onopen = () => {\n this.connected = true\n this.onOpen()\n resolve()\n }\n\n socket.onmessage = (event) => {\n void this.onReceiveMessage(event)\n }\n })\n }\n\n disconnect() {\n if (this.webSocket) {\n // Detach handlers first so a deliberate teardown (e.g. during a\n // reconnect) doesn't emit a spurious close/error to the client.\n this.webSocket.onclose = null\n this.webSocket.onerror = null\n this.webSocket.onopen = null\n this.webSocket.onmessage = null\n this.webSocket.close()\n this.webSocket = null\n }\n this.connected = false\n this.setupComplete = false\n }\n\n /**\n * Session management\n */\n sendInitialSetupMessage(resume = false) {\n const tools = this.functionDeclarations\n\n const setup: NonNullable<LiveClientMessage['setup']> = {\n model: `models/${this.model}`,\n generationConfig: {\n responseModalities: this.responseModalities,\n temperature: this.temperature,\n speechConfig: {\n languageCode: this.speechLanguageCode,\n voiceConfig: {\n prebuiltVoiceConfig: {\n voiceName: this.voiceName,\n },\n },\n },\n enableAffectiveDialog: this.enableAffectiveDialog,\n maxOutputTokens: this.maxOutputTokens,\n thinkingConfig: this.thinkingConfig,\n },\n sessionResumption: {\n transparent: true,\n handle: resume ? this.lastResumptionUpdate?.newHandle : undefined,\n },\n contextWindowCompression: this.contextWindowCompression,\n proactivity: {\n proactiveAudio: this.proactiveAudio,\n },\n systemInstruction: { parts: [{ text: this.systemInstructions }] },\n tools: [{ functionDeclarations: tools }],\n realtimeInputConfig: {\n automaticActivityDetection: {\n disabled: this.automaticActivityDetection.disabled,\n silenceDurationMs:\n this.automaticActivityDetection.silence_duration_ms,\n prefixPaddingMs: this.automaticActivityDetection.prefix_padding_ms,\n endOfSpeechSensitivity:\n this.automaticActivityDetection.end_of_speech_sensitivity,\n startOfSpeechSensitivity:\n this.automaticActivityDetection.start_of_speech_sensitivity,\n },\n activityHandling: this.activityHandling,\n turnCoverage: TurnCoverage.TURN_INCLUDES_ONLY_ACTIVITY,\n },\n }\n\n if (this.inputAudioTranscription) {\n setup.inputAudioTranscription = {}\n }\n\n if (this.outputAudioTranscription) {\n setup.outputAudioTranscription = {}\n }\n\n if (this.googleGrounding) {\n // Currently can't have both Google Search with custom tools.\n console.warn(\n 'Google Grounding enabled, removing custom function calls if any.',\n )\n setup.tools = [{ googleSearch: {} }]\n }\n\n this.sendMessage({ setup })\n }\n\n async restartSession(resume = false) {\n this.disconnect()\n await this.connect()\n this.sendInitialSetupMessage(resume)\n }\n\n updateToken(token: RealtimeToken) {\n this.token = token.token\n\n // Restart completely with the new model, or resume the existing session.\n const resume = !(token.config.model && this.model != token.config.model)\n if (!resume) {\n this.model = token.config.model as GeminiRealtimeModel\n }\n this.restartSession(resume).catch((err) =>\n this.onError(err instanceof Error ? err : new Error(String(err))),\n )\n }\n\n async updateSession(config: Partial<RealtimeSessionConfig>) {\n // model can only be set during initial setup\n if (config.model && !this.setupComplete) {\n this.model = config.model as GeminiRealtimeModel\n }\n\n if (config.instructions) {\n this.systemInstructions = config.instructions\n }\n\n if (config.tools) {\n this.functionDeclarations = config.tools.map(toolConfigToDeclaration)\n }\n\n if (config.maxOutputTokens) {\n // Gemini has no \"inf\" sentinel; treat it as \"no explicit limit\".\n this.maxOutputTokens =\n typeof config.maxOutputTokens === 'number'\n ? config.maxOutputTokens\n : undefined\n }\n\n if (config.temperature) {\n this.temperature = config.temperature\n }\n\n if (config.voice) {\n this.voiceName = config.voice as GeminiRealtimeVoice\n }\n\n const providerOptions = config.providerOptions as\n | GeminiRealtimeProviderOptions\n | undefined\n\n if (providerOptions?.googleGrounding) {\n this.googleGrounding = providerOptions.googleGrounding\n }\n\n if (providerOptions?.proactiveAudio) {\n this.proactiveAudio = providerOptions.proactiveAudio\n }\n\n if (providerOptions?.enableAffectiveDialog) {\n this.enableAffectiveDialog = providerOptions.enableAffectiveDialog\n }\n\n if (providerOptions?.contextWindowCompression) {\n this.contextWindowCompression = providerOptions.contextWindowCompression\n }\n\n if (providerOptions?.thinkingConfig) {\n this.thinkingConfig = providerOptions.thinkingConfig\n }\n\n if (providerOptions?.languageCode) {\n this.speechLanguageCode = providerOptions.languageCode\n }\n\n const includeTranscription =\n config.outputModalities?.includes('text') || false\n this.inputAudioTranscription = includeTranscription\n this.outputAudioTranscription = includeTranscription\n\n if (!this.setupComplete) {\n this.sendInitialSetupMessage()\n } else {\n return this.restartSession(true)\n }\n }\n\n /**\n * Message transmission & receiving\n */\n sendMessage(message: LiveClientMessage) {\n if (this.webSocket?.readyState === WebSocket.OPEN) {\n this.webSocket.send(JSON.stringify(message))\n } else {\n this.onError(\n new Error('Cannot send message: Gemini realtime socket is not open'),\n )\n }\n }\n\n async onReceiveMessage(messageEvent: MessageEvent) {\n let jsonData\n if (messageEvent.data instanceof Blob) {\n jsonData = await messageEvent.data.text()\n } else if (messageEvent.data instanceof ArrayBuffer) {\n jsonData = new TextDecoder().decode(messageEvent.data)\n } else {\n jsonData = messageEvent.data\n }\n\n try {\n const messageData = JSON.parse(jsonData)\n // Parse all response types from this message (audio + transcription can coexist)\n const responses = parseResponseMessages(messageData)\n for (const response of responses) {\n if (\n response.type === 'session_resumption_update' &&\n response.data.resumable\n ) {\n this.lastResumptionUpdate = response.data\n }\n if (response.type === 'setup_complete') {\n this.setupComplete = true\n }\n this.onReceiveResponse(response)\n }\n } catch (err) {\n this.onError(err instanceof Error ? err : new Error(String(err)))\n }\n }\n\n sendRealtimeInputMessage(data: string, mimeType: string) {\n const blob = { mimeType, data }\n\n if (mimeType.startsWith('audio/')) {\n this.sendMessage({ realtimeInput: { audio: blob } })\n } else if (mimeType.startsWith('image/') || mimeType.startsWith('video/')) {\n this.sendMessage({ realtimeInput: { video: blob } })\n }\n }\n\n sendAudioMessage(base64PCM: string) {\n this.sendRealtimeInputMessage(base64PCM, 'audio/pcm')\n }\n\n sendImageMessage(base64: string, mimeType = 'image/jpeg') {\n this.sendRealtimeInputMessage(base64, mimeType)\n }\n\n sendTextMessage(text: string) {\n const message: LiveClientMessage = {\n realtimeInput: {\n text,\n },\n }\n this.sendMessage(message)\n }\n\n sendToolResponse(functionResponses: Array<FunctionResponse>) {\n const message: LiveClientMessage = {\n toolResponse: {\n functionResponses,\n },\n }\n this.sendMessage(message)\n }\n}\n"],"mappings":"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"}
|
|
@@ -26,11 +26,12 @@ import { GoogleGenAI } from "@google/genai";
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
function geminiRealtimeToken(options = {}) {
|
|
29
|
-
const
|
|
29
|
+
const apiKey = getGeminiApiKeyFromEnv();
|
|
30
|
+
const client = new GoogleGenAI({ apiKey });
|
|
30
31
|
return {
|
|
31
32
|
provider: "gemini",
|
|
32
33
|
async generateToken() {
|
|
33
|
-
const expireTime = options.expiresAt ?? Date.now() +
|
|
34
|
+
const expireTime = options.expiresAt ?? Date.now() + 18e5;
|
|
34
35
|
const token = await client.authTokens.create({ config: {
|
|
35
36
|
uses: options.uses ?? 1,
|
|
36
37
|
expireTime: new Date(expireTime).toISOString(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token.js","names":[],"sources":["../../../src/realtime/token.ts"],"sourcesContent":["import { GoogleGenAI } from '@google/genai'\nimport { getGeminiApiKeyFromEnv } from '../utils'\nimport type { RealtimeToken, RealtimeTokenAdapter } from '@tanstack/ai'\nimport type { GeminiRealtimeTokenOptions } from './types'\n\n/**\n * Creates a Google Gemini realtime token adapter.\n *\n * This adapter generates ephemeral tokens for client-side WebSocket connections.\n *\n * @param options - Configuration options for the realtime session\n * @returns A RealtimeTokenAdapter for use with realtimeToken()\n *\n * @example\n * ```typescript\n * import { realtimeToken } from '@tanstack/ai'\n * import { geminiRealtimeToken } from '@tanstack/ai-gemini'\n *\n * const token = await realtimeToken({\n * adapter: geminiRealtimeToken({\n * // Optional: constraint model config by token\n * liveConnectConstraints: {\n * model: 'gemini-3.1-flash-live-preview',\n * },\n * }),\n * })\n * ```\n */\nexport function geminiRealtimeToken(\n options: GeminiRealtimeTokenOptions = {},\n): RealtimeTokenAdapter {\n const apiKey = getGeminiApiKeyFromEnv()\n\n const client = new GoogleGenAI({\n apiKey,\n })\n\n return {\n provider: 'gemini',\n async generateToken(): Promise<RealtimeToken> {\n // Computed per call so a reused adapter doesn't mint tokens with a\n // fixed (and eventually past) expiry. Defaults to 30 minutes.\n const expireTime = options.expiresAt ?? Date.now() + 30 * 60 * 1000\n\n const token = await client.authTokens.create({\n config: {\n uses: options.uses ?? 1,\n expireTime: new Date(expireTime).toISOString(),\n liveConnectConstraints: options.liveConnectConstraints,\n httpOptions: {\n apiVersion: 'v1alpha',\n },\n },\n })\n\n if (!token.name) {\n throw new Error('Gemini realtime token creation failed')\n }\n\n return {\n provider: 'gemini',\n token: token.name,\n expiresAt: expireTime,\n config: {\n model: options.liveConnectConstraints?.model,\n },\n }\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,oBACd,UAAsC,CAAC,GACjB;
|
|
1
|
+
{"version":3,"file":"token.js","names":[],"sources":["../../../src/realtime/token.ts"],"sourcesContent":["import { GoogleGenAI } from '@google/genai'\nimport { getGeminiApiKeyFromEnv } from '../utils'\nimport type { RealtimeToken, RealtimeTokenAdapter } from '@tanstack/ai'\nimport type { GeminiRealtimeTokenOptions } from './types'\n\n/**\n * Creates a Google Gemini realtime token adapter.\n *\n * This adapter generates ephemeral tokens for client-side WebSocket connections.\n *\n * @param options - Configuration options for the realtime session\n * @returns A RealtimeTokenAdapter for use with realtimeToken()\n *\n * @example\n * ```typescript\n * import { realtimeToken } from '@tanstack/ai'\n * import { geminiRealtimeToken } from '@tanstack/ai-gemini'\n *\n * const token = await realtimeToken({\n * adapter: geminiRealtimeToken({\n * // Optional: constraint model config by token\n * liveConnectConstraints: {\n * model: 'gemini-3.1-flash-live-preview',\n * },\n * }),\n * })\n * ```\n */\nexport function geminiRealtimeToken(\n options: GeminiRealtimeTokenOptions = {},\n): RealtimeTokenAdapter {\n const apiKey = getGeminiApiKeyFromEnv()\n\n const client = new GoogleGenAI({\n apiKey,\n })\n\n return {\n provider: 'gemini',\n async generateToken(): Promise<RealtimeToken> {\n // Computed per call so a reused adapter doesn't mint tokens with a\n // fixed (and eventually past) expiry. Defaults to 30 minutes.\n const expireTime = options.expiresAt ?? Date.now() + 30 * 60 * 1000\n\n const token = await client.authTokens.create({\n config: {\n uses: options.uses ?? 1,\n expireTime: new Date(expireTime).toISOString(),\n liveConnectConstraints: options.liveConnectConstraints,\n httpOptions: {\n apiVersion: 'v1alpha',\n },\n },\n })\n\n if (!token.name) {\n throw new Error('Gemini realtime token creation failed')\n }\n\n return {\n provider: 'gemini',\n token: token.name,\n expiresAt: expireTime,\n config: {\n model: options.liveConnectConstraints?.model,\n },\n }\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,SAAgB,oBACd,UAAsC,CAAC,GACjB;CACtB,MAAM,SAAS,uBAAuB;CAEtC,MAAM,SAAS,IAAI,YAAY,EAC7B,OACF,CAAC;CAED,OAAO;EACL,UAAU;EACV,MAAM,gBAAwC;GAG5C,MAAM,aAAa,QAAQ,aAAa,KAAK,IAAI,IAAI;GAErD,MAAM,QAAQ,MAAM,OAAO,WAAW,OAAO,EAC3C,QAAQ;IACN,MAAM,QAAQ,QAAQ;IACtB,YAAY,IAAI,KAAK,UAAU,CAAC,CAAC,YAAY;IAC7C,wBAAwB,QAAQ;IAChC,aAAa,EACX,YAAY,UACd;GACF,EACF,CAAC;GAED,IAAI,CAAC,MAAM,MACT,MAAM,IAAI,MAAM,uCAAuC;GAGzD,OAAO;IACL,UAAU;IACV,OAAO,MAAM;IACb,WAAW;IACX,QAAQ,EACN,OAAO,QAAQ,wBAAwB,MACzC;GACF;EACF;CACF;AACF"}
|
|
@@ -219,7 +219,10 @@ var AudioStreamer = class {
|
|
|
219
219
|
}
|
|
220
220
|
convertToPCM16(float32Array) {
|
|
221
221
|
const int16Array = new Int16Array(float32Array.length);
|
|
222
|
-
for (let i = 0; i < float32Array.length; i++)
|
|
222
|
+
for (let i = 0; i < float32Array.length; i++) {
|
|
223
|
+
const sample = Math.max(-1, Math.min(1, float32Array[i] ?? 0));
|
|
224
|
+
int16Array[i] = sample * 32767;
|
|
225
|
+
}
|
|
223
226
|
return int16Array.buffer;
|
|
224
227
|
}
|
|
225
228
|
arrayBufferToBase64(buffer) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../../../src/realtime/utils.ts"],"sourcesContent":["import type { GeminiLiveClient } from './client'\n\n/**\n * Audio Worklet Processor for capturing and processing audio\n */\nconst captureWorkletCode = `\nclass AudioCaptureProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.bufferSize = 512; // 32ms at 16kHz — per Gemini best practices (20-40ms chunks)\n this.buffer = new Float32Array(this.bufferSize);\n this.bufferIndex = 0;\n }\n\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n\n if (input && input.length > 0) {\n const inputChannel = input[0];\n\n // Buffer the incoming audio\n for (let i = 0; i < inputChannel.length; i++) {\n this.buffer[this.bufferIndex++] = inputChannel[i];\n\n // When buffer is full, send it to main thread\n if (this.bufferIndex >= this.bufferSize) {\n // Send the buffered audio to the main thread\n this.port.postMessage({\n type: \"audio\",\n data: this.buffer.slice(),\n });\n\n // Reset buffer\n this.bufferIndex = 0;\n }\n }\n }\n\n // Return true to keep the processor alive\n return true;\n }\n}\n\n// Register the processor\nregisterProcessor(\"audio-capture-processor\", AudioCaptureProcessor);`\n\n/**\n * Audio Playback Worklet Processor for playing PCM audio.\n * Uses an offset tracker instead of slice() to avoid allocations\n * on the real-time audio thread.\n */\nconst playbackWorkletCode = `\nclass PCMProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.audioQueue = [];\n this.currentOffset = 0; // Track position in current buffer (avoids slice())\n\n this.port.onmessage = (event) => {\n if (event.data === \"interrupt\") {\n // Clear the queue on interrupt\n this.audioQueue = [];\n this.currentOffset = 0;\n } else if (event.data instanceof Float32Array) {\n // Add audio data to the queue\n this.audioQueue.push(event.data);\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const output = outputs[0];\n if (output.length === 0) return true;\n\n const channel = output[0];\n let outputIndex = 0;\n\n // Fill the output buffer from the queue\n while (outputIndex < channel.length && this.audioQueue.length > 0) {\n const currentBuffer = this.audioQueue[0];\n\n if (!currentBuffer || currentBuffer.length === 0) {\n this.audioQueue.shift();\n this.currentOffset = 0;\n continue;\n }\n\n const remainingOutput = channel.length - outputIndex;\n const remainingBuffer = currentBuffer.length - this.currentOffset;\n const copyLength = Math.min(remainingOutput, remainingBuffer);\n\n // Copy audio data to output using offset (no slice allocation)\n for (let i = 0; i < copyLength; i++) {\n channel[outputIndex++] = currentBuffer[this.currentOffset++];\n }\n\n // If we've consumed the entire buffer, move to the next one\n if (this.currentOffset >= currentBuffer.length) {\n this.audioQueue.shift();\n this.currentOffset = 0;\n }\n }\n\n // Fill remaining output with silence\n while (outputIndex < channel.length) {\n channel[outputIndex++] = 0;\n }\n\n return true;\n }\n}\n\nregisterProcessor(\"pcm-processor\", PCMProcessor);`\n\nfunction calculateLevel(analyser: AnalyserNode): number {\n const data = new Uint8Array(analyser.fftSize)\n analyser.getByteTimeDomainData(data)\n\n // Find peak deviation from center (128 is silence)\n // This is more responsive than RMS for voice level meters\n let maxDeviation = 0\n for (const sample of data) {\n const deviation = Math.abs(sample - 128)\n if (deviation > maxDeviation) {\n maxDeviation = deviation\n }\n }\n\n // Normalize to 0-1 range (max deviation is 128)\n // Scale by 1.5x so that ~66% amplitude reads as full scale\n // This provides good visual feedback without pegging too early\n const normalized = maxDeviation / 128\n return Math.min(1, normalized * 1.5)\n}\n\nexport function base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binary = atob(base64)\n const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0))\n return bytes.buffer\n}\n\n// Empty arrays for when visualization isn't available\n// frequencyBinCount = fftSize / 2 = 1024\nconst emptyFrequencyData = new Uint8Array(1024)\nconst emptyTimeDomainData = new Uint8Array(2048).fill(128) // 128 is silence\n\nexport class AudioStreamer {\n private audioContext: AudioContext | null = null\n private audioWorklet: AudioWorkletNode | null = null\n private mediaStream: MediaStream | null = null\n private analyser: AnalyserNode | null = null\n private isStreaming = false\n private readonly sampleRate = 16000\n private readonly client: GeminiLiveClient | null = null\n\n constructor(client: GeminiLiveClient) {\n this.client = client\n }\n\n get inputLevel() {\n if (!this.analyser) return 0\n return calculateLevel(this.analyser)\n }\n\n get inputFrequencyData() {\n if (!this.analyser) return emptyFrequencyData\n const data = new Uint8Array(this.analyser.frequencyBinCount)\n this.analyser.getByteFrequencyData(data)\n return data\n }\n\n get inputTimeDomainData() {\n if (!this.analyser) return emptyTimeDomainData\n const data = new Uint8Array(this.analyser.fftSize)\n this.analyser.getByteTimeDomainData(data)\n return data\n }\n\n get inputSampleRate() {\n return this.sampleRate\n }\n\n async start() {\n try {\n const audioConstraints: MediaTrackConstraints = {\n sampleRate: this.sampleRate,\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n }\n\n // Get microphone access\n this.mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: audioConstraints,\n })\n\n // Check if native AGC is active\n const track = this.mediaStream.getAudioTracks()[0]\n const settings = track?.getSettings()\n\n if (settings?.autoGainControl) {\n console.warn('Native AGC not supported.')\n }\n\n // Create audio context\n this.audioContext = new AudioContext({\n sampleRate: this.sampleRate,\n })\n\n if (this.audioContext.state === 'suspended') {\n await this.audioContext.resume()\n }\n\n const workletBlob = new Blob([captureWorkletCode], {\n type: 'application/javascript',\n })\n const workletUrl = URL.createObjectURL(workletBlob)\n\n // Load the audio worklet module, then release the blob URL.\n await this.audioContext.audioWorklet.addModule(workletUrl)\n URL.revokeObjectURL(workletUrl)\n\n // Create the audio worklet node\n this.audioWorklet = new AudioWorkletNode(\n this.audioContext,\n 'audio-capture-processor',\n )\n\n // Set up message handling from the worklet\n this.audioWorklet.port.onmessage = (event) => {\n if (!this.isStreaming) return\n\n if (event.data.type === 'audio') {\n const inputData = event.data.data\n const pcmData = this.convertToPCM16(inputData)\n const base64Audio = this.arrayBufferToBase64(pcmData)\n\n // Send to Gemini only if after setup complete\n if (this.client?.isSetupComplete) {\n this.client.sendAudioMessage(base64Audio)\n }\n }\n }\n\n // Create analyser for volume detection\n this.analyser = this.audioContext.createAnalyser()\n this.analyser.fftSize = 2048 // Larger size for more accurate level detection\n this.analyser.smoothingTimeConstant = 0.3\n\n // Connect the audio graph\n const source = this.audioContext.createMediaStreamSource(this.mediaStream)\n source.connect(this.analyser)\n this.analyser.connect(this.audioWorklet)\n\n // Start streaming\n this.isStreaming = true\n } catch (error) {\n // Clean up the mic + audio context if setup failed partway through.\n this.stop()\n throw error\n }\n }\n\n stop() {\n this.isStreaming = false\n\n if (this.audioWorklet) {\n this.audioWorklet.disconnect()\n this.audioWorklet.port.close()\n this.audioWorklet = null\n }\n\n if (this.audioContext) {\n void this.audioContext.close()\n this.audioContext = null\n }\n\n if (this.mediaStream) {\n this.mediaStream.getTracks().forEach((track) => track.stop())\n this.mediaStream = null\n }\n }\n\n startAudioCapture() {\n if (this.mediaStream) {\n for (const track of this.mediaStream.getAudioTracks()) {\n track.enabled = true\n }\n }\n this.isStreaming = true\n }\n\n stopAudioCapture() {\n if (this.mediaStream) {\n // Disable tracks rather than stopping them to allow re-enabling\n for (const track of this.mediaStream.getAudioTracks()) {\n track.enabled = false\n }\n }\n this.isStreaming = false\n }\n\n private convertToPCM16(float32Array: Float32Array): ArrayBuffer {\n const int16Array = new Int16Array(float32Array.length)\n for (let i = 0; i < float32Array.length; i++) {\n const sample = Math.max(-1, Math.min(1, float32Array[i] ?? 0))\n int16Array[i] = sample * 0x7fff\n }\n return int16Array.buffer\n }\n\n private arrayBufferToBase64(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer)\n const binary = String.fromCharCode(...bytes)\n return btoa(binary)\n }\n}\n\nexport class AudioPlayer {\n private audioContext: AudioContext | null = null\n private workletNode: AudioWorkletNode | null = null\n private gainNode: GainNode | null = null\n private analyser: AnalyserNode | null = null\n private isInitialized = false\n private volume = 1.0\n private readonly sampleRate = 24000\n\n get outputLevel() {\n if (!this.analyser) return 0\n return calculateLevel(this.analyser)\n }\n\n get outputFrequencyData() {\n if (!this.analyser) return emptyFrequencyData\n const data = new Uint8Array(this.analyser.frequencyBinCount)\n this.analyser.getByteFrequencyData(data)\n return data\n }\n\n get outputTimeDomainData() {\n if (!this.analyser) return emptyTimeDomainData\n const data = new Uint8Array(this.analyser.fftSize)\n this.analyser.getByteTimeDomainData(data)\n return data\n }\n\n get outputSampleRate() {\n return this.sampleRate\n }\n\n async init() {\n if (this.isInitialized) return\n\n try {\n // Create audio context at 24kHz to match Gemini\n this.audioContext = new AudioContext({\n sampleRate: this.sampleRate,\n })\n\n const workletBlob = new Blob([playbackWorkletCode], {\n type: 'application/javascript',\n })\n const workletUrl = URL.createObjectURL(workletBlob)\n\n // Load the audio worklet module, then release the blob URL.\n await this.audioContext.audioWorklet.addModule(workletUrl)\n URL.revokeObjectURL(workletUrl)\n\n // Create worklet node\n this.workletNode = new AudioWorkletNode(\n this.audioContext,\n 'pcm-processor',\n )\n\n // Create gain node for volume control\n this.gainNode = this.audioContext.createGain()\n this.gainNode.gain.value = this.volume\n\n // Create analyser for volume detection\n this.analyser = this.audioContext.createAnalyser()\n this.analyser.fftSize = 2048 // Larger size for more accurate level detection\n this.analyser.smoothingTimeConstant = 0.3\n\n // Connect nodes\n this.workletNode.connect(this.gainNode)\n this.gainNode.connect(this.analyser)\n this.analyser.connect(this.audioContext.destination)\n\n this.isInitialized = true\n } catch (error) {\n // Release the audio context if initialization failed partway through.\n this.destroy()\n throw error\n }\n }\n\n async play(pcmData: ArrayBuffer) {\n if (!this.isInitialized) {\n await this.init()\n }\n\n // Resume audio context if suspended\n if (this.audioContext?.state === 'suspended') {\n await this.audioContext.resume()\n }\n\n // Convert PCM16 LE to Float32\n const inputArray = new Int16Array(pcmData)\n const float32Data = new Float32Array(inputArray.length)\n for (let i = 0; i < inputArray.length; i++) {\n float32Data[i] = (inputArray[i] ?? 0) / 32768\n }\n\n // Send to worklet for playback\n this.workletNode?.port.postMessage(float32Data)\n }\n\n /* Interrupt playback */\n interrupt() {\n if (this.workletNode) {\n this.workletNode.port.postMessage('interrupt')\n }\n }\n\n setVolume(volume: number) {\n this.volume = Math.max(0, Math.min(1, volume))\n if (this.gainNode) {\n this.gainNode.gain.value = this.volume\n }\n }\n\n destroy() {\n if (this.audioContext) {\n void this.audioContext.close()\n this.audioContext = null\n }\n this.isInitialized = false\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../../../src/realtime/utils.ts"],"sourcesContent":["import type { GeminiLiveClient } from './client'\n\n/**\n * Audio Worklet Processor for capturing and processing audio\n */\nconst captureWorkletCode = `\nclass AudioCaptureProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.bufferSize = 512; // 32ms at 16kHz — per Gemini best practices (20-40ms chunks)\n this.buffer = new Float32Array(this.bufferSize);\n this.bufferIndex = 0;\n }\n\n process(inputs, outputs, parameters) {\n const input = inputs[0];\n\n if (input && input.length > 0) {\n const inputChannel = input[0];\n\n // Buffer the incoming audio\n for (let i = 0; i < inputChannel.length; i++) {\n this.buffer[this.bufferIndex++] = inputChannel[i];\n\n // When buffer is full, send it to main thread\n if (this.bufferIndex >= this.bufferSize) {\n // Send the buffered audio to the main thread\n this.port.postMessage({\n type: \"audio\",\n data: this.buffer.slice(),\n });\n\n // Reset buffer\n this.bufferIndex = 0;\n }\n }\n }\n\n // Return true to keep the processor alive\n return true;\n }\n}\n\n// Register the processor\nregisterProcessor(\"audio-capture-processor\", AudioCaptureProcessor);`\n\n/**\n * Audio Playback Worklet Processor for playing PCM audio.\n * Uses an offset tracker instead of slice() to avoid allocations\n * on the real-time audio thread.\n */\nconst playbackWorkletCode = `\nclass PCMProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.audioQueue = [];\n this.currentOffset = 0; // Track position in current buffer (avoids slice())\n\n this.port.onmessage = (event) => {\n if (event.data === \"interrupt\") {\n // Clear the queue on interrupt\n this.audioQueue = [];\n this.currentOffset = 0;\n } else if (event.data instanceof Float32Array) {\n // Add audio data to the queue\n this.audioQueue.push(event.data);\n }\n };\n }\n\n process(inputs, outputs, parameters) {\n const output = outputs[0];\n if (output.length === 0) return true;\n\n const channel = output[0];\n let outputIndex = 0;\n\n // Fill the output buffer from the queue\n while (outputIndex < channel.length && this.audioQueue.length > 0) {\n const currentBuffer = this.audioQueue[0];\n\n if (!currentBuffer || currentBuffer.length === 0) {\n this.audioQueue.shift();\n this.currentOffset = 0;\n continue;\n }\n\n const remainingOutput = channel.length - outputIndex;\n const remainingBuffer = currentBuffer.length - this.currentOffset;\n const copyLength = Math.min(remainingOutput, remainingBuffer);\n\n // Copy audio data to output using offset (no slice allocation)\n for (let i = 0; i < copyLength; i++) {\n channel[outputIndex++] = currentBuffer[this.currentOffset++];\n }\n\n // If we've consumed the entire buffer, move to the next one\n if (this.currentOffset >= currentBuffer.length) {\n this.audioQueue.shift();\n this.currentOffset = 0;\n }\n }\n\n // Fill remaining output with silence\n while (outputIndex < channel.length) {\n channel[outputIndex++] = 0;\n }\n\n return true;\n }\n}\n\nregisterProcessor(\"pcm-processor\", PCMProcessor);`\n\nfunction calculateLevel(analyser: AnalyserNode): number {\n const data = new Uint8Array(analyser.fftSize)\n analyser.getByteTimeDomainData(data)\n\n // Find peak deviation from center (128 is silence)\n // This is more responsive than RMS for voice level meters\n let maxDeviation = 0\n for (const sample of data) {\n const deviation = Math.abs(sample - 128)\n if (deviation > maxDeviation) {\n maxDeviation = deviation\n }\n }\n\n // Normalize to 0-1 range (max deviation is 128)\n // Scale by 1.5x so that ~66% amplitude reads as full scale\n // This provides good visual feedback without pegging too early\n const normalized = maxDeviation / 128\n return Math.min(1, normalized * 1.5)\n}\n\nexport function base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binary = atob(base64)\n const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0))\n return bytes.buffer\n}\n\n// Empty arrays for when visualization isn't available\n// frequencyBinCount = fftSize / 2 = 1024\nconst emptyFrequencyData = new Uint8Array(1024)\nconst emptyTimeDomainData = new Uint8Array(2048).fill(128) // 128 is silence\n\nexport class AudioStreamer {\n private audioContext: AudioContext | null = null\n private audioWorklet: AudioWorkletNode | null = null\n private mediaStream: MediaStream | null = null\n private analyser: AnalyserNode | null = null\n private isStreaming = false\n private readonly sampleRate = 16000\n private readonly client: GeminiLiveClient | null = null\n\n constructor(client: GeminiLiveClient) {\n this.client = client\n }\n\n get inputLevel() {\n if (!this.analyser) return 0\n return calculateLevel(this.analyser)\n }\n\n get inputFrequencyData() {\n if (!this.analyser) return emptyFrequencyData\n const data = new Uint8Array(this.analyser.frequencyBinCount)\n this.analyser.getByteFrequencyData(data)\n return data\n }\n\n get inputTimeDomainData() {\n if (!this.analyser) return emptyTimeDomainData\n const data = new Uint8Array(this.analyser.fftSize)\n this.analyser.getByteTimeDomainData(data)\n return data\n }\n\n get inputSampleRate() {\n return this.sampleRate\n }\n\n async start() {\n try {\n const audioConstraints: MediaTrackConstraints = {\n sampleRate: this.sampleRate,\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n }\n\n // Get microphone access\n this.mediaStream = await navigator.mediaDevices.getUserMedia({\n audio: audioConstraints,\n })\n\n // Check if native AGC is active\n const track = this.mediaStream.getAudioTracks()[0]\n const settings = track?.getSettings()\n\n if (settings?.autoGainControl) {\n console.warn('Native AGC not supported.')\n }\n\n // Create audio context\n this.audioContext = new AudioContext({\n sampleRate: this.sampleRate,\n })\n\n if (this.audioContext.state === 'suspended') {\n await this.audioContext.resume()\n }\n\n const workletBlob = new Blob([captureWorkletCode], {\n type: 'application/javascript',\n })\n const workletUrl = URL.createObjectURL(workletBlob)\n\n // Load the audio worklet module, then release the blob URL.\n await this.audioContext.audioWorklet.addModule(workletUrl)\n URL.revokeObjectURL(workletUrl)\n\n // Create the audio worklet node\n this.audioWorklet = new AudioWorkletNode(\n this.audioContext,\n 'audio-capture-processor',\n )\n\n // Set up message handling from the worklet\n this.audioWorklet.port.onmessage = (event) => {\n if (!this.isStreaming) return\n\n if (event.data.type === 'audio') {\n const inputData = event.data.data\n const pcmData = this.convertToPCM16(inputData)\n const base64Audio = this.arrayBufferToBase64(pcmData)\n\n // Send to Gemini only if after setup complete\n if (this.client?.isSetupComplete) {\n this.client.sendAudioMessage(base64Audio)\n }\n }\n }\n\n // Create analyser for volume detection\n this.analyser = this.audioContext.createAnalyser()\n this.analyser.fftSize = 2048 // Larger size for more accurate level detection\n this.analyser.smoothingTimeConstant = 0.3\n\n // Connect the audio graph\n const source = this.audioContext.createMediaStreamSource(this.mediaStream)\n source.connect(this.analyser)\n this.analyser.connect(this.audioWorklet)\n\n // Start streaming\n this.isStreaming = true\n } catch (error) {\n // Clean up the mic + audio context if setup failed partway through.\n this.stop()\n throw error\n }\n }\n\n stop() {\n this.isStreaming = false\n\n if (this.audioWorklet) {\n this.audioWorklet.disconnect()\n this.audioWorklet.port.close()\n this.audioWorklet = null\n }\n\n if (this.audioContext) {\n void this.audioContext.close()\n this.audioContext = null\n }\n\n if (this.mediaStream) {\n this.mediaStream.getTracks().forEach((track) => track.stop())\n this.mediaStream = null\n }\n }\n\n startAudioCapture() {\n if (this.mediaStream) {\n for (const track of this.mediaStream.getAudioTracks()) {\n track.enabled = true\n }\n }\n this.isStreaming = true\n }\n\n stopAudioCapture() {\n if (this.mediaStream) {\n // Disable tracks rather than stopping them to allow re-enabling\n for (const track of this.mediaStream.getAudioTracks()) {\n track.enabled = false\n }\n }\n this.isStreaming = false\n }\n\n private convertToPCM16(float32Array: Float32Array): ArrayBuffer {\n const int16Array = new Int16Array(float32Array.length)\n for (let i = 0; i < float32Array.length; i++) {\n const sample = Math.max(-1, Math.min(1, float32Array[i] ?? 0))\n int16Array[i] = sample * 0x7fff\n }\n return int16Array.buffer\n }\n\n private arrayBufferToBase64(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer)\n const binary = String.fromCharCode(...bytes)\n return btoa(binary)\n }\n}\n\nexport class AudioPlayer {\n private audioContext: AudioContext | null = null\n private workletNode: AudioWorkletNode | null = null\n private gainNode: GainNode | null = null\n private analyser: AnalyserNode | null = null\n private isInitialized = false\n private volume = 1.0\n private readonly sampleRate = 24000\n\n get outputLevel() {\n if (!this.analyser) return 0\n return calculateLevel(this.analyser)\n }\n\n get outputFrequencyData() {\n if (!this.analyser) return emptyFrequencyData\n const data = new Uint8Array(this.analyser.frequencyBinCount)\n this.analyser.getByteFrequencyData(data)\n return data\n }\n\n get outputTimeDomainData() {\n if (!this.analyser) return emptyTimeDomainData\n const data = new Uint8Array(this.analyser.fftSize)\n this.analyser.getByteTimeDomainData(data)\n return data\n }\n\n get outputSampleRate() {\n return this.sampleRate\n }\n\n async init() {\n if (this.isInitialized) return\n\n try {\n // Create audio context at 24kHz to match Gemini\n this.audioContext = new AudioContext({\n sampleRate: this.sampleRate,\n })\n\n const workletBlob = new Blob([playbackWorkletCode], {\n type: 'application/javascript',\n })\n const workletUrl = URL.createObjectURL(workletBlob)\n\n // Load the audio worklet module, then release the blob URL.\n await this.audioContext.audioWorklet.addModule(workletUrl)\n URL.revokeObjectURL(workletUrl)\n\n // Create worklet node\n this.workletNode = new AudioWorkletNode(\n this.audioContext,\n 'pcm-processor',\n )\n\n // Create gain node for volume control\n this.gainNode = this.audioContext.createGain()\n this.gainNode.gain.value = this.volume\n\n // Create analyser for volume detection\n this.analyser = this.audioContext.createAnalyser()\n this.analyser.fftSize = 2048 // Larger size for more accurate level detection\n this.analyser.smoothingTimeConstant = 0.3\n\n // Connect nodes\n this.workletNode.connect(this.gainNode)\n this.gainNode.connect(this.analyser)\n this.analyser.connect(this.audioContext.destination)\n\n this.isInitialized = true\n } catch (error) {\n // Release the audio context if initialization failed partway through.\n this.destroy()\n throw error\n }\n }\n\n async play(pcmData: ArrayBuffer) {\n if (!this.isInitialized) {\n await this.init()\n }\n\n // Resume audio context if suspended\n if (this.audioContext?.state === 'suspended') {\n await this.audioContext.resume()\n }\n\n // Convert PCM16 LE to Float32\n const inputArray = new Int16Array(pcmData)\n const float32Data = new Float32Array(inputArray.length)\n for (let i = 0; i < inputArray.length; i++) {\n float32Data[i] = (inputArray[i] ?? 0) / 32768\n }\n\n // Send to worklet for playback\n this.workletNode?.port.postMessage(float32Data)\n }\n\n /* Interrupt playback */\n interrupt() {\n if (this.workletNode) {\n this.workletNode.port.postMessage('interrupt')\n }\n }\n\n setVolume(volume: number) {\n this.volume = Math.max(0, Math.min(1, volume))\n if (this.gainNode) {\n this.gainNode.gain.value = this.volume\n }\n }\n\n destroy() {\n if (this.audioContext) {\n void this.audioContext.close()\n this.audioContext = null\n }\n this.isInitialized = false\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-converter.js","names":[],"sources":["../../../src/tools/tool-converter.ts"],"sourcesContent":["import { convertCodeExecutionToolToAdapterFormat } from './code-execution-tool'\nimport { convertComputerUseToolToAdapterFormat } from './computer-use-tool'\nimport { convertFileSearchToolToAdapterFormat } from './file-search-tool'\nimport { convertGoogleMapsToolToAdapterFormat } from './google-maps-tool'\nimport { convertGoogleSearchRetrievalToolToAdapterFormat } from './google-search-retriveal-tool'\nimport { convertGoogleSearchToolToAdapterFormat } from './google-search-tool'\nimport { convertUrlContextToolToAdapterFormat } from './url-context-tool'\nimport type { Tool } from '@tanstack/ai'\nimport type { ToolUnion } from '@google/genai'\n\n/**\n * Converts standard Tool format to Gemini-specific tool format\n *\n * @param tools - Array of standard Tool objects\n * @returns Array of Gemini-specific tool definitions\n *\n * @example\n * ```typescript\n * const tools: Tool[] = [{\n * name: \"get_weather\",\n * description: \"Get weather for a location\",\n * inputSchema: z.object({\n * location: z.string()\n * })\n * }];\n *\n * const geminiTools = convertToolsToProviderFormat(tools);\n * ```\n */\nexport function convertToolsToProviderFormat<TTool extends Tool>(\n tools: Array<TTool> | undefined,\n): Array<ToolUnion> {\n if (!tools || tools.length === 0) {\n return []\n }\n const result: Array<ToolUnion> = []\n const functionDeclarations: Array<{\n name: string\n description?: string\n parameters?: any\n }> = []\n\n // Process each tool and group function declarations together\n for (const tool of tools) {\n const name = tool.name\n\n switch (name) {\n case 'code_execution':\n result.push(convertCodeExecutionToolToAdapterFormat(tool))\n break\n case 'computer_use':\n result.push(convertComputerUseToolToAdapterFormat(tool))\n break\n case 'file_search':\n result.push(convertFileSearchToolToAdapterFormat(tool))\n break\n case 'google_maps':\n result.push(convertGoogleMapsToolToAdapterFormat(tool))\n break\n case 'google_search_retrieval':\n result.push(convertGoogleSearchRetrievalToolToAdapterFormat(tool))\n break\n case 'google_search':\n result.push(convertGoogleSearchToolToAdapterFormat(tool))\n break\n case 'url_context':\n result.push(convertUrlContextToolToAdapterFormat(tool))\n break\n default:\n // Collect function declarations to group together\n // Description is required for Gemini function declarations\n if (!tool.description) {\n throw new Error(\n `Tool ${tool.name} requires a description for Gemini adapter`,\n )\n }\n\n // Tool schemas are already converted to JSON Schema in the ai layer\n functionDeclarations.push({\n name: tool.name,\n description: tool.description,\n parameters: tool.inputSchema ?? {\n type: 'object',\n properties: {},\n required: [],\n },\n })\n break\n }\n }\n\n // If we have function declarations, add them as a single tool\n if (functionDeclarations.length > 0) {\n result.push({\n functionDeclarations: functionDeclarations,\n })\n }\n\n return result\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,6BACd,OACkB;CAClB,IAAI,CAAC,SAAS,MAAM,WAAW,GAC7B,OAAO,CAAC;CAEV,MAAM,SAA2B,CAAC;CAClC,MAAM,uBAID,CAAC;CAGN,KAAK,MAAM,QAAQ,OAGjB,QAFa,KAAK,MAElB;EACE,KAAK;GACH,OAAO,KAAK,wCAAwC,IAAI,CAAC;GACzD;EACF,KAAK;GACH,OAAO,KAAK,sCAAsC,IAAI,CAAC;GACvD;EACF,KAAK;GACH,OAAO,KAAK,qCAAqC,IAAI,CAAC;GACtD;EACF,KAAK;GACH,OAAO,KAAK,qCAAqC,IAAI,CAAC;GACtD;EACF,KAAK;GACH,OAAO,KAAK,gDAAgD,IAAI,CAAC;GACjE;EACF,KAAK;GACH,OAAO,KAAK,uCAAuC,IAAI,CAAC;GACxD;EACF,KAAK;GACH,OAAO,KAAK,qCAAqC,IAAI,CAAC;GACtD;EACF;GAGE,IAAI,CAAC,KAAK,aACR,MAAM,IAAI,MACR,QAAQ,KAAK,KAAK,2CACpB;GAIF,qBAAqB,KAAK;IACxB,MAAM,KAAK;IACX,aAAa,KAAK;IAClB,YAAY,KAAK,eAAe;KAC9B,MAAM;KACN,YAAY,CAAC;KACb,UAAU,CAAC;IACb;GACF,CAAC;
|
|
1
|
+
{"version":3,"file":"tool-converter.js","names":[],"sources":["../../../src/tools/tool-converter.ts"],"sourcesContent":["import { convertCodeExecutionToolToAdapterFormat } from './code-execution-tool'\nimport { convertComputerUseToolToAdapterFormat } from './computer-use-tool'\nimport { convertFileSearchToolToAdapterFormat } from './file-search-tool'\nimport { convertGoogleMapsToolToAdapterFormat } from './google-maps-tool'\nimport { convertGoogleSearchRetrievalToolToAdapterFormat } from './google-search-retriveal-tool'\nimport { convertGoogleSearchToolToAdapterFormat } from './google-search-tool'\nimport { convertUrlContextToolToAdapterFormat } from './url-context-tool'\nimport type { Tool } from '@tanstack/ai'\nimport type { ToolUnion } from '@google/genai'\n\n/**\n * Converts standard Tool format to Gemini-specific tool format\n *\n * @param tools - Array of standard Tool objects\n * @returns Array of Gemini-specific tool definitions\n *\n * @example\n * ```typescript\n * const tools: Tool[] = [{\n * name: \"get_weather\",\n * description: \"Get weather for a location\",\n * inputSchema: z.object({\n * location: z.string()\n * })\n * }];\n *\n * const geminiTools = convertToolsToProviderFormat(tools);\n * ```\n */\nexport function convertToolsToProviderFormat<TTool extends Tool>(\n tools: Array<TTool> | undefined,\n): Array<ToolUnion> {\n if (!tools || tools.length === 0) {\n return []\n }\n const result: Array<ToolUnion> = []\n const functionDeclarations: Array<{\n name: string\n description?: string\n parameters?: any\n }> = []\n\n // Process each tool and group function declarations together\n for (const tool of tools) {\n const name = tool.name\n\n switch (name) {\n case 'code_execution':\n result.push(convertCodeExecutionToolToAdapterFormat(tool))\n break\n case 'computer_use':\n result.push(convertComputerUseToolToAdapterFormat(tool))\n break\n case 'file_search':\n result.push(convertFileSearchToolToAdapterFormat(tool))\n break\n case 'google_maps':\n result.push(convertGoogleMapsToolToAdapterFormat(tool))\n break\n case 'google_search_retrieval':\n result.push(convertGoogleSearchRetrievalToolToAdapterFormat(tool))\n break\n case 'google_search':\n result.push(convertGoogleSearchToolToAdapterFormat(tool))\n break\n case 'url_context':\n result.push(convertUrlContextToolToAdapterFormat(tool))\n break\n default:\n // Collect function declarations to group together\n // Description is required for Gemini function declarations\n if (!tool.description) {\n throw new Error(\n `Tool ${tool.name} requires a description for Gemini adapter`,\n )\n }\n\n // Tool schemas are already converted to JSON Schema in the ai layer\n functionDeclarations.push({\n name: tool.name,\n description: tool.description,\n parameters: tool.inputSchema ?? {\n type: 'object',\n properties: {},\n required: [],\n },\n })\n break\n }\n }\n\n // If we have function declarations, add them as a single tool\n if (functionDeclarations.length > 0) {\n result.push({\n functionDeclarations: functionDeclarations,\n })\n }\n\n return result\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,6BACd,OACkB;CAClB,IAAI,CAAC,SAAS,MAAM,WAAW,GAC7B,OAAO,CAAC;CAEV,MAAM,SAA2B,CAAC;CAClC,MAAM,uBAID,CAAC;CAGN,KAAK,MAAM,QAAQ,OAGjB,QAFa,KAAK,MAElB;EACE,KAAK;GACH,OAAO,KAAK,wCAAwC,IAAI,CAAC;GACzD;EACF,KAAK;GACH,OAAO,KAAK,sCAAsC,IAAI,CAAC;GACvD;EACF,KAAK;GACH,OAAO,KAAK,qCAAqC,IAAI,CAAC;GACtD;EACF,KAAK;GACH,OAAO,KAAK,qCAAqC,IAAI,CAAC;GACtD;EACF,KAAK;GACH,OAAO,KAAK,gDAAgD,IAAI,CAAC;GACjE;EACF,KAAK;GACH,OAAO,KAAK,uCAAuC,IAAI,CAAC;GACxD;EACF,KAAK;GACH,OAAO,KAAK,qCAAqC,IAAI,CAAC;GACtD;EACF;GAGE,IAAI,CAAC,KAAK,aACR,MAAM,IAAI,MACR,QAAQ,KAAK,KAAK,2CACpB;GAIF,qBAAqB,KAAK;IACxB,MAAM,KAAK;IACX,aAAa,KAAK;IAClB,YAAY,KAAK,eAAe;KAC9B,MAAM;KACN,YAAY,CAAC;KACb,UAAU,CAAC;IACb;GACF,CAAC;CAEL;CAIF,IAAI,qBAAqB,SAAS,GAChC,OAAO,KAAK,EACY,qBACxB,CAAC;CAGH,OAAO;AACT"}
|
package/dist/esm/usage.js
CHANGED
|
@@ -25,9 +25,7 @@ function flattenModalityTokenCounts(modalities) {
|
|
|
25
25
|
case "VIDEO":
|
|
26
26
|
result.videoTokens = (result.videoTokens ?? 0) + count;
|
|
27
27
|
break;
|
|
28
|
-
case "DOCUMENT":
|
|
29
|
-
result.documentTokens = (result.documentTokens ?? 0) + count;
|
|
30
|
-
break;
|
|
28
|
+
case "DOCUMENT": result.documentTokens = (result.documentTokens ?? 0) + count;
|
|
31
29
|
}
|
|
32
30
|
}
|
|
33
31
|
return result;
|