Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/gemini-drop-retired-media-models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'@tanstack/ai-gemini': minor
---

Drop retired Gemini media models and add **Veo 3.1 Lite**.

The following models return `404 NOT_FOUND` from the Gemini Developer API and have been removed from the adapter's model lists and type maps:

- `imagen-3.0-generate-002` (superseded by the Imagen 4 family)
- `veo-2.0-generate-001`
- `veo-3.0-generate-001`
- `veo-3.0-fast-generate-001`

Added `veo-3.1-lite-generate-preview` (Veo 3.1 Lite) β€” the lowest-cost Veo 3.1 tier ($0.05/sec, 720p, video + audio), with the same `4 | 6 | 8` second durations as the rest of the Veo 3.1 family.

If you were referencing one of the removed model ids, switch to a current model (e.g. `imagen-4.0-generate-001`, `veo-3.1-generate-preview`, or `veo-3.1-lite-generate-preview`).
1 change: 0 additions & 1 deletion docs/adapters/gemini.md
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,6 @@ These models use the dedicated `generateImages` API.
| `imagen-4.0-ultra-generate-001` | Best quality Imagen image generation |
| `imagen-4.0-generate-001` | High quality Imagen image generation |
| `imagen-4.0-fast-generate-001` | Fast Imagen image generation |
| `imagen-3.0-generate-002` | Imagen 3 image generation |

## API Reference

Expand Down
7 changes: 4 additions & 3 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,13 @@
"label": "Image Generation",
"to": "media/image-generation",
"addedAt": "2026-04-15",
"updatedAt": "2026-06-08"
"updatedAt": "2026-07-01"
},
{
"label": "Video Generation",
"to": "media/video-generation",
"addedAt": "2026-04-15",
"updatedAt": "2026-06-24"
"updatedAt": "2026-07-01"
},
{
"label": "Generation Hooks",
Expand Down Expand Up @@ -510,7 +510,8 @@
{
"label": "Google Gemini",
"to": "adapters/gemini",
"addedAt": "2026-04-15"
"addedAt": "2026-04-15",
"updatedAt": "2026-07-01"
},
{
"label": "Ollama",
Expand Down
1 change: 0 additions & 1 deletion docs/media/image-generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,6 @@ if (result.usage?.unitsBilled != null) {
| `imagen-4.0-ultra-generate-001` | 1-4 |
| `imagen-4.0-generate-001` | 1-4 |
| `imagen-4.0-fast-generate-001` | 1-4 |
| `imagen-3.0-generate-002` | 1-4 |

## Error Handling

Expand Down
6 changes: 2 additions & 4 deletions docs/media/video-generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -540,9 +540,7 @@ the `duration` option:
|-------|------------------------------|
| `veo-3.1-generate-preview` | `4`, `6`, `8` |
| `veo-3.1-fast-generate-preview` | `4`, `6`, `8` |
| `veo-3.0-generate-001` | `4`, `6`, `8` |
| `veo-3.0-fast-generate-001` | `4`, `6`, `8` |
| `veo-2.0-generate-001` | `5`, `6`, `8` |
| `veo-3.1-lite-generate-preview` | `4`, `6`, `8` |

If you have raw seconds (for example from a UI slider), coerce them with
`snapDuration`, or inspect the full set with `availableDurations`:
Expand All @@ -551,7 +549,7 @@ If you have raw seconds (for example from a UI slider), coerce them with
import { generateVideo } from '@tanstack/ai'
import { geminiVideo } from '@tanstack/ai-gemini'

const adapter = geminiVideo('veo-3.0-generate-001')
const adapter = geminiVideo('veo-3.1-lite-generate-preview')

adapter.availableDurations() // { kind: 'discrete', values: [4, 6, 8] }
adapter.snapDuration(7) // 6 β€” closest valid duration
Expand Down
4 changes: 2 additions & 2 deletions packages/ai-gemini/src/adapters/image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,14 +420,14 @@ export class GeminiImageAdapter<
* Creates a Gemini image adapter with explicit API key.
* Type resolution happens here at the call site.
*
* @param model - The model name (e.g., 'imagen-3.0-generate-002')
* @param model - The model name (e.g., 'imagen-4.0-generate-001')
* @param apiKey - Your Google API key
* @param config - Optional additional configuration
* @returns Configured Gemini image adapter instance with resolved types
*
* @example
* ```typescript
* const adapter = createGeminiImage('imagen-3.0-generate-002', "your-api-key");
* const adapter = createGeminiImage('imagen-4.0-generate-001', "your-api-key");
*
* const result = await generateImage({
* adapter,
Expand Down
9 changes: 4 additions & 5 deletions packages/ai-gemini/src/image/image-provider-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,14 @@ export function validateImageSize(

/**
* Per-model caps on images per request.
* Imagen 3 and the Imagen 4 family all support up to 4 images per request
* via the Gemini API (the rumored 8-image tier is Vertex-only and isn't
* reachable through @google/genai today). Unknown models fall through to
* the shared cap defined below.
* The Imagen 4 family all support up to 4 images per request via the Gemini
* API (the rumored 8-image tier is Vertex-only and isn't reachable through
* @google/genai today). Unknown models fall through to the shared cap
* defined below.
*
* @see https://ai.google.dev/gemini-api/docs/imagen
*/
const IMAGEN_MAX_IMAGES_BY_MODEL: Record<string, number> = {
'imagen-3.0-generate-002': 4,
'imagen-4.0-generate-001': 4,
'imagen-4.0-ultra-generate-001': 4,
'imagen-4.0-fast-generate-001': 4,
Expand Down
77 changes: 4 additions & 73 deletions packages/ai-gemini/src/model-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -610,27 +610,6 @@ const IMAGEN_4_GENERATE_FAST = {
GeminiCachedContentOptions
>

const IMAGEN_3 = {
name: 'imagen-3.0-generate-002',
max_output_tokens: 4,
supports: {
input: ['text'],
output: ['image'],
},
pricing: {
input: {
normal: 0,
},
output: {
normal: 0.03,
},
},
} as const satisfies ModelMeta<
GeminiToolConfigOptions &
GeminiSafetyOptions &
GeminiCommonConfigOptions &
GeminiCachedContentOptions
>
/**
* Veo video generation models. Pricing is per second of generated video
* (audio+video rate where the model supports audio).
Expand Down Expand Up @@ -682,8 +661,8 @@ const VEO_3_1_FAST_PREVIEW = {
GeminiCachedContentOptions
>

const VEO_3 = {
name: 'veo-3.0-generate-001',
const VEO_3_1_LITE_PREVIEW = {
name: 'veo-3.1-lite-generate-preview',
max_input_tokens: 1024,
max_output_tokens: 1,
supports: {
Expand All @@ -695,52 +674,7 @@ const VEO_3 = {
normal: 0,
},
output: {
normal: 0.4,
},
},
} as const satisfies ModelMeta<
GeminiToolConfigOptions &
GeminiSafetyOptions &
GeminiCommonConfigOptions &
GeminiCachedContentOptions
>

const VEO_3_FAST = {
name: 'veo-3.0-fast-generate-001',
max_input_tokens: 1024,
max_output_tokens: 1,
supports: {
input: ['text', 'image'],
output: ['video', 'audio'],
},
pricing: {
input: {
normal: 0,
},
output: {
normal: 0.15,
},
},
} as const satisfies ModelMeta<
GeminiToolConfigOptions &
GeminiSafetyOptions &
GeminiCommonConfigOptions &
GeminiCachedContentOptions
>

const VEO_2 = {
name: 'veo-2.0-generate-001',
max_output_tokens: 2,
supports: {
input: ['text', 'image'],
output: ['video'],
},
pricing: {
input: {
normal: 0,
},
output: {
normal: 0.35,
normal: 0.05,
},
},
} as const satisfies ModelMeta<
Expand Down Expand Up @@ -853,7 +787,6 @@ export const GEMINI_IMAGE_MODELS = [
GEMINI_3_1_FLASH_IMAGE.name,
GEMINI_3_PRO_IMAGE.name,
GEMINI_2_5_FLASH_IMAGE.name,
IMAGEN_3.name,
IMAGEN_4_GENERATE.name,
IMAGEN_4_GENERATE_FAST.name,
IMAGEN_4_GENERATE_ULTRA.name,
Expand Down Expand Up @@ -924,9 +857,7 @@ export type GeminiTTSVoice = (typeof GEMINI_TTS_VOICES)[number]
export const GEMINI_VIDEO_MODELS = [
VEO_3_1_PREVIEW.name,
VEO_3_1_FAST_PREVIEW.name,
VEO_3.name,
VEO_3_FAST.name,
VEO_2.name,
VEO_3_1_LITE_PREVIEW.name,
] as const

// Manual type map for per-model provider options
Expand Down
8 changes: 2 additions & 6 deletions packages/ai-gemini/src/video/video-provider-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ export type GeminiVideoModelInputModalitiesByName = {
export type GeminiVideoModelDurationByName = {
'veo-3.1-generate-preview': 4 | 6 | 8
'veo-3.1-fast-generate-preview': 4 | 6 | 8
'veo-3.0-generate-001': 4 | 6 | 8
'veo-3.0-fast-generate-001': 4 | 6 | 8
'veo-2.0-generate-001': 5 | 6 | 8
'veo-3.1-lite-generate-preview': 4 | 6 | 8
}

/**
Expand All @@ -109,9 +107,7 @@ export const GEMINI_VIDEO_DURATIONS: {
} = {
'veo-3.1-generate-preview': { kind: 'discrete', values: [4, 6, 8] },
'veo-3.1-fast-generate-preview': { kind: 'discrete', values: [4, 6, 8] },
'veo-3.0-generate-001': { kind: 'discrete', values: [4, 6, 8] },
'veo-3.0-fast-generate-001': { kind: 'discrete', values: [4, 6, 8] },
'veo-2.0-generate-001': { kind: 'discrete', values: [5, 6, 8] },
'veo-3.1-lite-generate-preview': { kind: 'discrete', values: [4, 6, 8] },
}

/**
Expand Down
36 changes: 18 additions & 18 deletions packages/ai-gemini/tests/image-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('Gemini Image Adapter', () => {
describe('createGeminiImage', () => {
it('creates an adapter with the provided API key', () => {
const adapter = createGeminiImage(
'imagen-3.0-generate-002',
'imagen-4.0-generate-001',
'test-api-key',
)
expect(adapter).toBeInstanceOf(GeminiImageAdapter)
Expand All @@ -24,10 +24,10 @@ describe('Gemini Image Adapter', () => {

it('has the correct model', () => {
const adapter = createGeminiImage(
'imagen-3.0-generate-002',
'imagen-4.0-generate-001',
'test-api-key',
)
expect(adapter.model).toBe('imagen-3.0-generate-002')
expect(adapter.model).toBe('imagen-4.0-generate-001')
})
})

Expand All @@ -52,7 +52,7 @@ describe('Gemini Image Adapter', () => {
describe('validateImageSize', () => {
it('accepts valid sizes that map to aspect ratios', () => {
expect(() =>
validateImageSize('imagen-3.0-generate-002', '1024x1024'),
validateImageSize('imagen-4.0-generate-001', '1024x1024'),
).not.toThrow()
expect(() =>
validateImageSize('imagen-4.0-generate-001', '1920x1080'),
Expand All @@ -61,30 +61,30 @@ describe('Gemini Image Adapter', () => {

it('rejects invalid sizes', () => {
expect(() =>
validateImageSize('imagen-3.0-generate-002', '999x999'),
validateImageSize('imagen-4.0-generate-001', '999x999'),
).toThrow()
})

it('accepts undefined size', () => {
expect(() =>
validateImageSize('imagen-3.0-generate-002', undefined),
validateImageSize('imagen-4.0-generate-001', undefined),
).not.toThrow()
})
})

describe('validateNumberOfImages', () => {
it('accepts 1-4 images', () => {
expect(() =>
validateNumberOfImages('imagen-3.0-generate-002', 1),
validateNumberOfImages('imagen-4.0-generate-001', 1),
).not.toThrow()
expect(() =>
validateNumberOfImages('imagen-3.0-generate-002', 4),
validateNumberOfImages('imagen-4.0-generate-001', 4),
).not.toThrow()
})

it('rejects more than 4 images', () => {
expect(() =>
validateNumberOfImages('imagen-3.0-generate-002', 5),
validateNumberOfImages('imagen-4.0-generate-001', 5),
).toThrow()
})

Expand All @@ -108,30 +108,30 @@ describe('Gemini Image Adapter', () => {

it('rejects 0 images', () => {
expect(() =>
validateNumberOfImages('imagen-3.0-generate-002', 0),
validateNumberOfImages('imagen-4.0-generate-001', 0),
).toThrow()
})

it('accepts undefined', () => {
expect(() =>
validateNumberOfImages('imagen-3.0-generate-002', undefined),
validateNumberOfImages('imagen-4.0-generate-001', undefined),
).not.toThrow()
})
})

describe('validatePrompt', () => {
it('rejects empty prompts', () => {
expect(() =>
validatePrompt({ prompt: '', model: 'imagen-3.0-generate-002' }),
validatePrompt({ prompt: '', model: 'imagen-4.0-generate-001' }),
).toThrow()
expect(() =>
validatePrompt({ prompt: ' ', model: 'imagen-3.0-generate-002' }),
validatePrompt({ prompt: ' ', model: 'imagen-4.0-generate-001' }),
).toThrow()
})

it('accepts non-empty prompts', () => {
expect(() =>
validatePrompt({ prompt: 'A cat', model: 'imagen-3.0-generate-002' }),
validatePrompt({ prompt: 'A cat', model: 'imagen-4.0-generate-001' }),
).not.toThrow()
})
})
Expand Down Expand Up @@ -175,7 +175,7 @@ describe('Gemini Image Adapter', () => {
const mockGenerateImages = vi.fn().mockResolvedValueOnce(mockResponse)

const adapter = createGeminiImage(
'imagen-3.0-generate-002',
'imagen-4.0-generate-001',
'test-api-key',
)
// Replace the internal Gemini SDK client with our mock
Expand All @@ -197,15 +197,15 @@ describe('Gemini Image Adapter', () => {
})

expect(mockGenerateImages).toHaveBeenCalledWith({
model: 'imagen-3.0-generate-002',
model: 'imagen-4.0-generate-001',
prompt: 'A cat wearing a hat',
config: {
numberOfImages: 1,
aspectRatio: '1:1',
},
})

expect(result.model).toBe('imagen-3.0-generate-002')
expect(result.model).toBe('imagen-4.0-generate-001')
expect(result.images).toHaveLength(1)
expect(result.images[0]!.b64Json).toBe('base64encodedimage')
})
Expand All @@ -218,7 +218,7 @@ describe('Gemini Image Adapter', () => {
const mockGenerateImages = vi.fn().mockResolvedValue(mockResponse)

const adapter = createGeminiImage(
'imagen-3.0-generate-002',
'imagen-4.0-generate-001',
'test-api-key',
)
;(
Expand Down
Loading
Loading