From e2e4f00dedce86eb007e0e600bd00570e5344ccf Mon Sep 17 00:00:00 2001 From: Roo Code Date: Sun, 7 Sep 2025 10:14:52 +0000 Subject: [PATCH] fix: add free tier (125k tokens) for Gemini 2.5 Pro models - Added a new tier with 125k context window for free tier users - This prevents 429 errors when using Gemini 2.5 Pro with the free tier - The free tier has 0 cost for input/output/cache operations - Added comprehensive tests to verify tier configuration Fixes #7753 --- packages/types/src/providers/gemini.ts | 28 ++++++ .../__tests__/gemini-tier-config.spec.ts | 90 +++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 src/api/providers/__tests__/gemini-tier-config.spec.ts diff --git a/packages/types/src/providers/gemini.ts b/packages/types/src/providers/gemini.ts index a7225c7330..3c0fec1fda 100644 --- a/packages/types/src/providers/gemini.ts +++ b/packages/types/src/providers/gemini.ts @@ -78,6 +78,13 @@ export const geminiModels = { cacheReadsPrice: 0.625, cacheWritesPrice: 4.5, tiers: [ + { + // Free tier: 125k input tokens per minute quota + contextWindow: 125_000, + inputPrice: 0, + outputPrice: 0, + cacheReadsPrice: 0, + }, { contextWindow: 200_000, inputPrice: 1.25, @@ -102,6 +109,13 @@ export const geminiModels = { cacheReadsPrice: 0.625, cacheWritesPrice: 4.5, tiers: [ + { + // Free tier: 125k input tokens per minute quota + contextWindow: 125_000, + inputPrice: 0, + outputPrice: 0, + cacheReadsPrice: 0, + }, { contextWindow: 200_000, inputPrice: 1.25, @@ -128,6 +142,13 @@ export const geminiModels = { maxThinkingTokens: 32_768, supportsReasoningBudget: true, tiers: [ + { + // Free tier: 125k input tokens per minute quota + contextWindow: 125_000, + inputPrice: 0, + outputPrice: 0, + cacheReadsPrice: 0, + }, { contextWindow: 200_000, inputPrice: 1.25, @@ -155,6 +176,13 @@ export const geminiModels = { supportsReasoningBudget: true, requiredReasoningBudget: true, tiers: [ + { + // Free tier: 125k input tokens per minute quota + contextWindow: 125_000, + inputPrice: 0, + outputPrice: 0, + cacheReadsPrice: 0, + }, { contextWindow: 200_000, inputPrice: 1.25, diff --git a/src/api/providers/__tests__/gemini-tier-config.spec.ts b/src/api/providers/__tests__/gemini-tier-config.spec.ts new file mode 100644 index 0000000000..f5760d2b8b --- /dev/null +++ b/src/api/providers/__tests__/gemini-tier-config.spec.ts @@ -0,0 +1,90 @@ +// npx vitest run src/api/providers/__tests__/gemini-tier-config.spec.ts + +import { geminiModels } from "@roo-code/types" + +describe("Gemini 2.5 Pro Tier Configuration", () => { + const gemini25ProModels = [ + "gemini-2.5-pro", + "gemini-2.5-pro-preview-03-25", + "gemini-2.5-pro-preview-05-06", + "gemini-2.5-pro-preview-06-05", + ] as const + + gemini25ProModels.forEach((modelId) => { + describe(`${modelId}`, () => { + it("should have a free tier with 125k context window", () => { + const model = geminiModels[modelId] + expect(model).toBeDefined() + expect(model.tiers).toBeDefined() + expect(model.tiers!.length).toBeGreaterThanOrEqual(3) + + // Check the first tier is the free tier + const freeTier = model.tiers![0] + expect(freeTier.contextWindow).toBe(125_000) + expect(freeTier.inputPrice).toBe(0) + expect(freeTier.outputPrice).toBe(0) + expect(freeTier.cacheReadsPrice).toBe(0) + }) + + it("should have correct tier ordering", () => { + const model = geminiModels[modelId] + const tiers = model.tiers! + + // Verify tier ordering: 125k (free) -> 200k -> Infinity + expect(tiers[0].contextWindow).toBe(125_000) + expect(tiers[1].contextWindow).toBe(200_000) + expect(tiers[2].contextWindow).toBe(Infinity) + }) + + it("should have correct pricing for paid tiers", () => { + const model = geminiModels[modelId] + const tiers = model.tiers! + + // 200k tier pricing + expect(tiers[1].inputPrice).toBe(1.25) + expect(tiers[1].outputPrice).toBe(10) + expect(tiers[1].cacheReadsPrice).toBe(0.31) + + // Infinity tier pricing + expect(tiers[2].inputPrice).toBe(2.5) + expect(tiers[2].outputPrice).toBe(15) + expect(tiers[2].cacheReadsPrice).toBe(0.625) + }) + + it("should trigger context condensing before hitting 125k limit", () => { + const model = geminiModels[modelId] + const freeTierLimit = model.tiers![0].contextWindow + + // With a typical context condensing threshold of 50-80%, + // the condensing should trigger well before reaching 125k tokens + const typicalCondenseThreshold = 0.7 // 70% + const expectedTriggerPoint = freeTierLimit * typicalCondenseThreshold + + // Verify that the free tier limit is correctly set to prevent 429 errors + expect(freeTierLimit).toBe(125_000) + expect(expectedTriggerPoint).toBeLessThan(freeTierLimit) + expect(expectedTriggerPoint).toBe(87_500) // 70% of 125k + }) + }) + }) + + describe("Other Gemini models", () => { + it("should not have free tier for non-2.5-pro models", () => { + // Check a few other models to ensure we didn't accidentally add free tier to them + const otherModels = [ + "gemini-2.0-flash-001", + "gemini-1.5-flash-002", + "gemini-2.0-flash-thinking-exp-01-21", + ] as const + + otherModels.forEach((modelId) => { + const model = geminiModels[modelId] + if ("tiers" in model && model.tiers) { + // If tiers exist, verify none have 125k context window + const has125kTier = model.tiers.some((tier: any) => tier.contextWindow === 125_000) + expect(has125kTier).toBe(false) + } + }) + }) + }) +})