From 62547144843a20cfdd6761ef2e48a9cc4a066f6e Mon Sep 17 00:00:00 2001 From: Reynaldi Chernando <12949382+reynaldichernando@users.noreply.github.com> Date: Sat, 15 Aug 2026 04:27:48 +0700 Subject: [PATCH] deprecate gemini 2 flash and lite (#3581) * deprecate gemini 2 flash and lite * fix test --- .../GeminiChatProvider.integration.test.ts | 6 +- .../gemini/GeminiChatProvider.test.ts | 79 ++++++++++--------- .../ai-chat/providers/gemini/models.ts | 53 ------------- 3 files changed, 45 insertions(+), 93 deletions(-) diff --git a/src/backend/drivers/ai-chat/providers/gemini/GeminiChatProvider.integration.test.ts b/src/backend/drivers/ai-chat/providers/gemini/GeminiChatProvider.integration.test.ts index b6cd55b52..37184a591 100644 --- a/src/backend/drivers/ai-chat/providers/gemini/GeminiChatProvider.integration.test.ts +++ b/src/backend/drivers/ai-chat/providers/gemini/GeminiChatProvider.integration.test.ts @@ -20,7 +20,7 @@ /** * Integration test for the Gemini chat provider. * - * Hits the real Google Gemini API with `gemini-2.0-flash-lite` (one + * Hits the real Google Gemini API with `gemini-2.5-flash-lite` (one * of the cheapest variants). Skipped when `PUTER_TEST_AI_GEMINI_API_KEY` * is unset. */ @@ -40,14 +40,14 @@ const ENV_VAR = 'PUTER_TEST_AI_GEMINI_API_KEY'; describe.skipIf(skipUnlessEnv(ENV_VAR))( 'GeminiChatProvider (integration)', () => { - it('returns a non-empty completion from gemini-2.0-flash-lite', { timeout: INTEGRATION_TEST_TIMEOUT_MS }, async () => { + it('returns a non-empty completion from gemini-2.5-flash-lite', { timeout: INTEGRATION_TEST_TIMEOUT_MS }, async () => { const provider = new GeminiChatProvider(makeMeteringStub(), { apiKey: optionalEnv(ENV_VAR)!, }); const result = await withTestActor(() => provider.complete({ - model: 'gemini-2.0-flash-lite', + model: 'gemini-2.5-flash-lite', messages: [ { role: 'user', content: 'Say hi in one word.' }, ], diff --git a/src/backend/drivers/ai-chat/providers/gemini/GeminiChatProvider.test.ts b/src/backend/drivers/ai-chat/providers/gemini/GeminiChatProvider.test.ts index 193c410cb..7a06ddb62 100644 --- a/src/backend/drivers/ai-chat/providers/gemini/GeminiChatProvider.test.ts +++ b/src/backend/drivers/ai-chat/providers/gemini/GeminiChatProvider.test.ts @@ -400,46 +400,51 @@ describe('GeminiChatProvider.complete non-stream output', () => { }); it('bills cached tokens at the input rate when the model prices no cache read', async () => { - // gemini-2.0-flash-lite's catalogue entry has no cached_tokens rate. - // Cached tokens are subtracted out of prompt_tokens, so pricing them - // at zero bills them nowhere. + // Every shipped entry currently prices cache reads, so drop the rate + // off one for this call. Cached tokens are subtracted out of + // prompt_tokens, so pricing them at zero bills them nowhere. const lite = GEMINI_MODELS.find( - (m) => m.id === 'gemini-2.0-flash-lite', + (m) => m.id === 'gemini-3.1-flash-lite', )!; - expect(lite.costs.cached_tokens).toBeUndefined(); + const cachedRate = lite.costs.cached_tokens; + delete lite.costs.cached_tokens; - const { provider } = makeProvider(); - createMock.mockResolvedValueOnce({ - choices: [ - { - message: { content: 'cached', role: 'assistant' }, - finish_reason: 'stop', + try { + const { provider } = makeProvider(); + createMock.mockResolvedValueOnce({ + choices: [ + { + message: { content: 'cached', role: 'assistant' }, + finish_reason: 'stop', + }, + ], + usage: { + prompt_tokens: 3000, + completion_tokens: 40, + prompt_tokens_details: { cached_tokens: 2900 }, }, - ], - usage: { - prompt_tokens: 3000, - completion_tokens: 40, - prompt_tokens_details: { cached_tokens: 2900 }, - }, - }); + }); - await withTestActor(() => - provider.complete({ - model: 'gemini-2.0-flash-lite', - messages: [{ role: 'user', content: 'hi' }], - }), - ); + await withTestActor(() => + provider.complete({ + model: 'gemini-3.1-flash-lite', + messages: [{ role: 'user', content: 'hi' }], + }), + ); - const [, , , overrides] = recordSpy.mock.calls[0]!; - const inputRate = Number(lite.costs.prompt_tokens); - expect(overrides).toMatchObject({ - prompt_tokens: (3000 - 2900) * inputRate, - completion_tokens: 40 * Number(lite.costs.completion_tokens), - cached_tokens: 2900 * inputRate, - }); - expect( - (overrides as Record).cached_tokens, - ).toBeGreaterThan(0); + const [, , , overrides] = recordSpy.mock.calls[0]!; + const inputRate = Number(lite.costs.prompt_tokens); + expect(overrides).toMatchObject({ + prompt_tokens: (3000 - 2900) * inputRate, + completion_tokens: 40 * Number(lite.costs.completion_tokens), + cached_tokens: 2900 * inputRate, + }); + expect( + (overrides as Record).cached_tokens, + ).toBeGreaterThan(0); + } finally { + lite.costs.cached_tokens = cachedRate; + } }); it('zeroes cached_tokens when prompt_tokens_details is missing', async () => { @@ -643,9 +648,9 @@ describe('GeminiChatProvider.complete grounding request metering', () => { // generation; without its own rate the fee fell through to the input // token rate, which is several orders of magnitude below list. const lite = GEMINI_MODELS.find( - (m) => m.id === 'gemini-2.0-flash-lite', + (m) => m.id === 'gemini-3.1-flash-lite', )!; - expect(lite.costs.grounding_requests).toBe(3_500_000); + expect(lite.costs.grounding_requests).toBe(1_400_000); const { provider } = makeProvider(); createMock.mockResolvedValueOnce({ @@ -666,7 +671,7 @@ describe('GeminiChatProvider.complete grounding request metering', () => { await withTestActor(() => provider.complete({ - model: 'gemini-2.0-flash-lite', + model: 'gemini-3.1-flash-lite', messages: [{ role: 'user', content: 'search for foo' }], }), ); diff --git a/src/backend/drivers/ai-chat/providers/gemini/models.ts b/src/backend/drivers/ai-chat/providers/gemini/models.ts index 515921898..aeb343545 100644 --- a/src/backend/drivers/ai-chat/providers/gemini/models.ts +++ b/src/backend/drivers/ai-chat/providers/gemini/models.ts @@ -49,59 +49,6 @@ export const GEMINI_MODELS: IChatModel[] = [ grounding_requests: 1_400_000, }, }, - { - puterId: 'google:google/gemini-2.0-flash', - id: 'gemini-2.0-flash', - modalities: { - input: ['text', 'image', 'audio', 'video', 'pdf'], - output: ['text'], - }, - open_weights: false, - tool_call: true, - knowledge: '2024-06', - release_date: '2024-12-11', - name: 'Gemini 2.0 Flash', - aliases: ['google/gemini-2.0-flash'], - context: 131072, - costs_currency: 'usd-cents', - input_cost_key: 'prompt_tokens', - output_cost_key: 'completion_tokens', - costs: { - tokens: 1_000_000, - prompt_tokens: 10, - completion_tokens: 40, - cached_tokens: 3, - // Gemini 2.x grounding is $35 / 1,000 requests - grounding_requests: 3_500_000, - }, - max_tokens: 8192, - }, - { - puterId: 'google:google/gemini-2.0-flash-lite', - id: 'gemini-2.0-flash-lite', - modalities: { - input: ['text', 'image', 'audio', 'video', 'pdf'], - output: ['text'], - }, - open_weights: false, - tool_call: true, - knowledge: '2024-06', - release_date: '2024-12-11', - name: 'Gemini 2.0 Flash-Lite', - aliases: ['google/gemini-2.0-flash-lite'], - context: 1_048_576, - costs_currency: 'usd-cents', - input_cost_key: 'prompt_tokens', - output_cost_key: 'completion_tokens', - costs: { - tokens: 1_000_000, - prompt_tokens: 8, - completion_tokens: 30, - // Gemini 2.x grounding is $35 / 1,000 requests - grounding_requests: 3_500_000, - }, - max_tokens: 8192, - }, { puterId: 'google:google/gemini-2.5-flash', id: 'gemini-2.5-flash',