diff --git a/.changeset/gemini-3-7-flash.md b/.changeset/gemini-3-7-flash.md new file mode 100644 index 000000000..395e8a54a --- /dev/null +++ b/.changeset/gemini-3-7-flash.md @@ -0,0 +1,5 @@ +--- +'@tanstack/ai-gemini': minor +--- + +Add Gemini 3.7 Flash (`gemini-3.7-flash`) with full multimodal input, thinking, structured output, caching, and the same built-in tools as Gemini 3.6 Flash. diff --git a/examples/ts-react-chat/src/lib/model-selection.ts b/examples/ts-react-chat/src/lib/model-selection.ts index 006f5ead4..6c26bf27a 100644 --- a/examples/ts-react-chat/src/lib/model-selection.ts +++ b/examples/ts-react-chat/src/lib/model-selection.ts @@ -65,6 +65,11 @@ export const MODEL_OPTIONS: Array = [ }, // Gemini (stateless `geminiText`) + { + provider: 'gemini', + model: 'gemini-3.7-flash', + label: 'Gemini - 3.7 Flash', + }, { provider: 'gemini', model: 'gemini-3.6-flash', @@ -102,6 +107,11 @@ export const MODEL_OPTIONS: Array = [ }, // Gemini Interactions (stateful, experimental — `@tanstack/ai-gemini/experimental`) + { + provider: 'gemini-interactions', + model: 'gemini-3.7-flash', + label: 'Gemini Interactions - 3.7 Flash (experimental)', + }, { provider: 'gemini-interactions', model: 'gemini-3.6-flash', diff --git a/examples/ts-react-chat/src/routes/api.structured-output.ts b/examples/ts-react-chat/src/routes/api.structured-output.ts index b7d42b553..2c499099c 100644 --- a/examples/ts-react-chat/src/routes/api.structured-output.ts +++ b/examples/ts-react-chat/src/routes/api.structured-output.ts @@ -160,10 +160,10 @@ function adapterFor(provider: Provider, model?: string): AnyTextAdapter { // for the combination and falls back to the engine's legacy // finalization path. // - // Default is `gemini-3.6-flash`: the newest *stable* (non-preview) + // Default is `gemini-3.7-flash`: the newest *stable* (non-preview) // 3.x id, matching the dropdown's first entry. The previous default // (`gemini-3-pro-preview`) was retired by Google and now 404s. - return geminiText((baseModel || 'gemini-3.6-flash') as 'gemini-3.6-flash') + return geminiText((baseModel || 'gemini-3.7-flash') as 'gemini-3.7-flash') case 'grok': return grokText((model || 'grok-build-0.1') as 'grok-build-0.1') case 'groq': diff --git a/examples/ts-react-chat/src/routes/generations.structured-output.tsx b/examples/ts-react-chat/src/routes/generations.structured-output.tsx index 1ce124bce..42abef8ed 100644 --- a/examples/ts-react-chat/src/routes/generations.structured-output.tsx +++ b/examples/ts-react-chat/src/routes/generations.structured-output.tsx @@ -80,6 +80,7 @@ const PROVIDER_MODELS: Record< // keys on the exact string, so any drift here silently breaks // combined-mode routing. gemini: [ + { value: 'gemini-3.7-flash', label: 'Gemini 3.7 Flash' }, { value: 'gemini-3.6-flash', label: 'Gemini 3.6 Flash' }, { value: 'gemini-3.5-flash', label: 'Gemini 3.5 Flash' }, { value: 'gemini-3.5-flash-lite', label: 'Gemini 3.5 Flash Lite' }, diff --git a/packages/ai-gemini/src/model-meta.ts b/packages/ai-gemini/src/model-meta.ts index 0cf19d774..1ec09c5a8 100644 --- a/packages/ai-gemini/src/model-meta.ts +++ b/packages/ai-gemini/src/model-meta.ts @@ -744,6 +744,50 @@ const GEMINI_OMNI_FLASH_PREVIEW = { GeminiCachedContentOptions > +const GEMINI_3_7_FLASH = { + name: 'gemini-3.7-flash', + max_input_tokens: 1_048_576, + max_output_tokens: 65_536, + knowledge_cutoff: '2026-03-01', + supports: { + input: ['text', 'image', 'video', 'audio', 'document'], + output: ['text'], + capabilities: [ + 'batch_api', + 'caching', + 'function_calling', + 'structured_output', + 'thinking', + ], + tools: [ + 'code_execution', + 'file_search', + 'google_search', + 'google_maps', + 'url_context', + 'computer_use', + ], + }, + // Currently 50% off through the end of 2026. + // https://ai.google.dev/gemini-api/docs/pricing#gemini-3.7-flash + pricing: { + input: { + normal: 0.75, + cached: 0.075, + }, + output: { + normal: 3.75, + }, + }, +} as const satisfies ModelMeta< + GeminiToolConfigOptions & + GeminiSafetyOptions & + GeminiCommonConfigOptions & + GeminiCachedContentOptions & + GeminiStructuredOutputOptions & + GeminiThinkingOptions +> + const GEMINI_3_6_FLASH = { name: 'gemini-3.6-flash', max_input_tokens: 1_048_576, @@ -869,6 +913,7 @@ const GEMINI_3_5_FLASH_LITE = { > export const GEMINI_MODELS = [ + GEMINI_3_7_FLASH.name, GEMINI_3_6_FLASH.name, GEMINI_3_5_FLASH.name, GEMINI_3_5_FLASH_LITE.name, @@ -889,6 +934,7 @@ export const GEMINI_MODELS = [ * brittle and keeps the engine's legacy finalization fallback. */ export const GEMINI_COMBINED_TOOLS_AND_SCHEMA_MODELS = new Set([ + GEMINI_3_7_FLASH.name, GEMINI_3_6_FLASH.name, GEMINI_3_5_FLASH.name, GEMINI_3_5_FLASH_LITE.name, @@ -1017,6 +1063,12 @@ export type GeminiEmbeddingModelInputModalitiesByName = { // Manual type map for per-model provider options export type GeminiChatModelProviderOptionsByName = { // Models with thinking and structured output support + [GEMINI_3_7_FLASH.name]: GeminiToolConfigOptions & + GeminiSafetyOptions & + GeminiCommonConfigOptions & + GeminiCachedContentOptions & + GeminiStructuredOutputOptions & + GeminiThinkingOptions [GEMINI_3_6_FLASH.name]: GeminiToolConfigOptions & GeminiSafetyOptions & GeminiCommonConfigOptions & @@ -1084,6 +1136,7 @@ export type GeminiChatModelProviderOptionsByName = { * Based on the 'supports.tools' arrays defined for each model. */ export type GeminiChatModelToolCapabilitiesByName = { + [GEMINI_3_7_FLASH.name]: typeof GEMINI_3_7_FLASH.supports.tools [GEMINI_3_6_FLASH.name]: typeof GEMINI_3_6_FLASH.supports.tools [GEMINI_3_5_FLASH.name]: typeof GEMINI_3_5_FLASH.supports.tools [GEMINI_3_5_FLASH_LITE.name]: typeof GEMINI_3_5_FLASH_LITE.supports.tools @@ -1111,6 +1164,7 @@ export type GeminiChatModelToolCapabilitiesByName = { */ export type GeminiModelInputModalitiesByName = { // Models with full multimodal support (text, image, audio, video, document) + [GEMINI_3_7_FLASH.name]: typeof GEMINI_3_7_FLASH.supports.input [GEMINI_3_6_FLASH.name]: typeof GEMINI_3_6_FLASH.supports.input [GEMINI_3_5_FLASH.name]: typeof GEMINI_3_5_FLASH.supports.input [GEMINI_3_5_FLASH_LITE.name]: typeof GEMINI_3_5_FLASH_LITE.supports.input diff --git a/packages/ai-gemini/tests/model-meta.test.ts b/packages/ai-gemini/tests/model-meta.test.ts index 24c8de9ba..1cdf7da92 100644 --- a/packages/ai-gemini/tests/model-meta.test.ts +++ b/packages/ai-gemini/tests/model-meta.test.ts @@ -196,6 +196,15 @@ describe('Gemini Model Provider Options Type Assertions', () => { expectTypeOf().toExtend() }) + it('gemini-3.7-flash should support thinking options', () => { + type Model = 'gemini-3.7-flash' + type Options = GeminiChatModelProviderOptionsByName[Model] + + expectTypeOf().toExtend() + expectTypeOf().toExtend() + expectTypeOf().toExtend() + }) + it('gemini-3.6-flash should support thinking options', () => { type Model = 'gemini-3.6-flash' type Options = GeminiChatModelProviderOptionsByName[Model] @@ -220,6 +229,7 @@ describe('Gemini Model Provider Options Type Assertions', () => { // Verify the type map has all expected model keys type Keys = keyof GeminiChatModelProviderOptionsByName + expectTypeOf<'gemini-3.7-flash'>().toExtend() expectTypeOf<'gemini-3.6-flash'>().toExtend() expectTypeOf<'gemini-3.5-flash'>().toExtend() expectTypeOf<'gemini-3.5-flash-lite'>().toExtend() @@ -261,6 +271,9 @@ describe('Gemini Model Provider Options Type Assertions', () => { }) it('all models should have safety settings', () => { + expectTypeOf< + GeminiChatModelProviderOptionsByName['gemini-3.7-flash'] + >().toHaveProperty('safetySettings') expectTypeOf< GeminiChatModelProviderOptionsByName['gemini-3.6-flash'] >().toHaveProperty('safetySettings') @@ -294,6 +307,9 @@ describe('Gemini Model Provider Options Type Assertions', () => { }) it('all models should have tool config', () => { + expectTypeOf< + GeminiChatModelProviderOptionsByName['gemini-3.7-flash'] + >().toHaveProperty('toolConfig') expectTypeOf< GeminiChatModelProviderOptionsByName['gemini-3.6-flash'] >().toHaveProperty('toolConfig') @@ -327,6 +343,9 @@ describe('Gemini Model Provider Options Type Assertions', () => { }) it('all models should have cached content option', () => { + expectTypeOf< + GeminiChatModelProviderOptionsByName['gemini-3.7-flash'] + >().toHaveProperty('cachedContent') expectTypeOf< GeminiChatModelProviderOptionsByName['gemini-3.6-flash'] >().toHaveProperty('cachedContent') @@ -362,6 +381,9 @@ describe('Gemini Model Provider Options Type Assertions', () => { describe('Type discrimination between model categories', () => { it('models with thinking should extend GeminiThinkingOptions', () => { + expectTypeOf< + GeminiChatModelProviderOptionsByName['gemini-3.7-flash'] + >().toExtend() expectTypeOf< GeminiChatModelProviderOptionsByName['gemini-3.6-flash'] >().toExtend() @@ -395,6 +417,9 @@ describe('Gemini Model Provider Options Type Assertions', () => { }) it('all models should extend GeminiStructuredOutputOptions', () => { + expectTypeOf< + GeminiChatModelProviderOptionsByName['gemini-3.7-flash'] + >().toExtend() expectTypeOf< GeminiChatModelProviderOptionsByName['gemini-3.6-flash'] >().toExtend() @@ -436,6 +461,7 @@ describe('Gemini Model Provider Options Type Assertions', () => { * content parts based on each Gemini model's supported input modalities. * * Models with full multimodal (text + image + audio + video + document): + * - gemini-3.7-flash * - gemini-3.6-flash * - gemini-3.5-flash * - gemini-3.5-flash-lite @@ -553,6 +579,19 @@ describe('Gemini Model Input Modality Type Assertions', () => { }) }) + describe('gemini-3.7-flash (full multimodal)', () => { + type Modalities = GeminiModelInputModalitiesByName['gemini-3.7-flash'] + type Message = ConstrainedModelMessage> + + it('should allow all content part types', () => { + expectTypeOf>().toExtend() + expectTypeOf>().toExtend() + expectTypeOf>().toExtend() + expectTypeOf>().toExtend() + expectTypeOf>().toExtend() + }) + }) + describe('gemini-3.6-flash (full multimodal)', () => { type Modalities = GeminiModelInputModalitiesByName['gemini-3.6-flash'] type Message = ConstrainedModelMessage> diff --git a/packages/ai-gemini/tests/tools-per-model-type-safety.test.ts b/packages/ai-gemini/tests/tools-per-model-type-safety.test.ts index 4f3721d6d..c6cafa870 100644 --- a/packages/ai-gemini/tests/tools-per-model-type-safety.test.ts +++ b/packages/ai-gemini/tests/tools-per-model-type-safety.test.ts @@ -100,6 +100,24 @@ describe('Gemini per-model tool gating', () => { ]) }) + it('gemini-3.7-flash accepts code_execution, file_search, google_search, google_maps, url_context, computer_use', () => { + const adapter = geminiText('gemini-3.7-flash') + typedTools(adapter, [ + userTool, + codeExecutionTool(), + fileSearchTool({ fileSearchStoreNames: [] }), + googleSearchTool(), + googleMapsTool(), + urlContextTool(), + computerUseTool({ + environment: Environment.ENVIRONMENT_BROWSER, + excludedPredefinedFunctions: [], + }), + // @ts-expect-error - gemini-3.7-flash does not support google_search_retrieval + googleSearchRetrievalTool(), + ]) + }) + it('gemini-3.6-flash accepts code_execution, file_search, google_search, google_maps, url_context, computer_use', () => { const adapter = geminiText('gemini-3.6-flash') typedTools(adapter, [