From 2816191b9588dd5931361777bef9a0916f933823 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Sun, 20 Jul 2025 21:24:31 +0100 Subject: [PATCH 01/13] fix: creating a list of legacy Gemini models in both gemini.ts and vertex.ts (types) --- packages/types/src/providers/gemini.ts | 215 ++++++++++++++++--------- packages/types/src/providers/vertex.ts | 103 ++++++------ 2 files changed, 189 insertions(+), 129 deletions(-) diff --git a/packages/types/src/providers/gemini.ts b/packages/types/src/providers/gemini.ts index a7225c7330..88f3c7bcf6 100644 --- a/packages/types/src/providers/gemini.ts +++ b/packages/types/src/providers/gemini.ts @@ -60,16 +60,8 @@ export const geminiModels = { maxThinkingTokens: 24_576, supportsReasoningBudget: true, }, - "gemini-2.5-pro-exp-03-25": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, - "gemini-2.5-pro-preview-03-25": { - maxTokens: 65_535, + "gemini-2.5-pro": { + maxTokens: 64_000, contextWindow: 1_048_576, supportsImages: true, supportsPromptCache: true, @@ -77,6 +69,9 @@ export const geminiModels = { outputPrice: 15, cacheReadsPrice: 0.625, cacheWritesPrice: 4.5, + maxThinkingTokens: 32_768, + supportsReasoningBudget: true, + requiredReasoningBudget: true, tiers: [ { contextWindow: 200_000, @@ -92,7 +87,134 @@ export const geminiModels = { }, ], }, - "gemini-2.5-pro-preview-05-06": { + "gemini-2.0-flash-001": { + maxTokens: 8192, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 0.1, + outputPrice: 0.4, + cacheReadsPrice: 0.025, + cacheWritesPrice: 1.0, + }, + "gemini-2.0-flash-lite-preview-02-05": { + maxTokens: 8192, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.0-pro-exp-02-05": { + maxTokens: 8192, + contextWindow: 2_097_152, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.0-flash-thinking-exp-01-21": { + maxTokens: 65_536, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.0-flash-thinking-exp-1219": { + maxTokens: 8192, + contextWindow: 32_767, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.0-flash-exp": { + maxTokens: 8192, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.5-flash-lite-preview-06-17": { + maxTokens: 64_000, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 0.1, + outputPrice: 0.4, + cacheReadsPrice: 0.025, + cacheWritesPrice: 1.0, + supportsReasoningBudget: true, + maxThinkingTokens: 24_576, + }, +} as const satisfies Record + +export const legacyGeminiModels = { + "gemini-2.5-flash-preview-04-17:thinking": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0.15, + outputPrice: 3.5, + maxThinkingTokens: 24_576, + supportsReasoningBudget: true, + requiredReasoningBudget: true, + }, + "gemini-2.5-flash-preview-04-17": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0.15, + outputPrice: 0.6, + }, + "gemini-2.5-flash-preview-05-20:thinking": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 0.15, + outputPrice: 3.5, + cacheReadsPrice: 0.0375, + cacheWritesPrice: 1.0, + maxThinkingTokens: 24_576, + supportsReasoningBudget: true, + requiredReasoningBudget: true, + }, + "gemini-2.5-flash-preview-05-20": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 0.15, + outputPrice: 0.6, + cacheReadsPrice: 0.0375, + cacheWritesPrice: 1.0, + }, + "gemini-2.5-flash": { + maxTokens: 64_000, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 0.3, + outputPrice: 2.5, + cacheReadsPrice: 0.075, + cacheWritesPrice: 1.0, + maxThinkingTokens: 24_576, + supportsReasoningBudget: true, + }, + "gemini-2.5-pro-exp-03-25": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.5-pro-preview-03-25": { maxTokens: 65_535, contextWindow: 1_048_576, supportsImages: true, @@ -116,7 +238,7 @@ export const geminiModels = { }, ], }, - "gemini-2.5-pro-preview-06-05": { + "gemini-2.5-pro-preview-05-06": { maxTokens: 65_535, contextWindow: 1_048_576, supportsImages: true, @@ -125,8 +247,6 @@ export const geminiModels = { outputPrice: 15, cacheReadsPrice: 0.625, cacheWritesPrice: 4.5, - maxThinkingTokens: 32_768, - supportsReasoningBudget: true, tiers: [ { contextWindow: 200_000, @@ -142,8 +262,8 @@ export const geminiModels = { }, ], }, - "gemini-2.5-pro": { - maxTokens: 64_000, + "gemini-2.5-pro-preview-06-05": { + maxTokens: 65_535, contextWindow: 1_048_576, supportsImages: true, supportsPromptCache: true, @@ -153,7 +273,6 @@ export const geminiModels = { cacheWritesPrice: 4.5, maxThinkingTokens: 32_768, supportsReasoningBudget: true, - requiredReasoningBudget: true, tiers: [ { contextWindow: 200_000, @@ -169,56 +288,6 @@ export const geminiModels = { }, ], }, - "gemini-2.0-flash-001": { - maxTokens: 8192, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: true, - inputPrice: 0.1, - outputPrice: 0.4, - cacheReadsPrice: 0.025, - cacheWritesPrice: 1.0, - }, - "gemini-2.0-flash-lite-preview-02-05": { - maxTokens: 8192, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, - "gemini-2.0-pro-exp-02-05": { - maxTokens: 8192, - contextWindow: 2_097_152, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, - "gemini-2.0-flash-thinking-exp-01-21": { - maxTokens: 65_536, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, - "gemini-2.0-flash-thinking-exp-1219": { - maxTokens: 8192, - contextWindow: 32_767, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, - "gemini-2.0-flash-exp": { - maxTokens: 8192, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, "gemini-1.5-flash-002": { maxTokens: 8192, contextWindow: 1_048_576, @@ -283,16 +352,4 @@ export const geminiModels = { inputPrice: 0, outputPrice: 0, }, - "gemini-2.5-flash-lite-preview-06-17": { - maxTokens: 64_000, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: true, - inputPrice: 0.1, - outputPrice: 0.4, - cacheReadsPrice: 0.025, - cacheWritesPrice: 1.0, - supportsReasoningBudget: true, - maxThinkingTokens: 24_576, - }, } as const satisfies Record diff --git a/packages/types/src/providers/vertex.ts b/packages/types/src/providers/vertex.ts index c405621f82..34be4b1a21 100644 --- a/packages/types/src/providers/vertex.ts +++ b/packages/types/src/providers/vertex.ts @@ -56,32 +56,6 @@ export const vertexModels = { inputPrice: 0.15, outputPrice: 0.6, }, - "gemini-2.5-pro-preview-03-25": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: true, - inputPrice: 2.5, - outputPrice: 15, - }, - "gemini-2.5-pro-preview-05-06": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: true, - inputPrice: 2.5, - outputPrice: 15, - }, - "gemini-2.5-pro-preview-06-05": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: true, - inputPrice: 2.5, - outputPrice: 15, - maxThinkingTokens: 32_768, - supportsReasoningBudget: true, - }, "gemini-2.5-pro": { maxTokens: 64_000, contextWindow: 1_048_576, @@ -107,14 +81,6 @@ export const vertexModels = { }, ], }, - "gemini-2.5-pro-exp-03-25": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, "gemini-2.0-pro-exp-02-05": { maxTokens: 8192, contextWindow: 2_097_152, @@ -147,22 +113,6 @@ export const vertexModels = { inputPrice: 0, outputPrice: 0, }, - "gemini-1.5-flash-002": { - maxTokens: 8192, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: true, - inputPrice: 0.075, - outputPrice: 0.3, - }, - "gemini-1.5-pro-002": { - maxTokens: 8192, - contextWindow: 2_097_152, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 1.25, - outputPrice: 5, - }, "claude-sonnet-4@20250514": { maxTokens: 8192, contextWindow: 200_000, @@ -309,3 +259,56 @@ export const VERTEX_REGIONS = [ { value: "me-central1", label: "me-central1" }, { value: "africa-south1", label: "africa-south1" }, ] + +export const legacyVertexModels = { + "gemini-1.5-flash-002": { + maxTokens: 8192, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 0.075, + outputPrice: 0.3, + }, + "gemini-1.5-pro-002": { + maxTokens: 8192, + contextWindow: 2_097_152, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 1.25, + outputPrice: 5, + }, + "gemini-2.5-pro-exp-03-25": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.5-pro-preview-03-25": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 2.5, + outputPrice: 15, + }, + "gemini-2.5-pro-preview-05-06": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 2.5, + outputPrice: 15, + }, + "gemini-2.5-pro-preview-06-05": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: true, + inputPrice: 2.5, + outputPrice: 15, + maxThinkingTokens: 32_768, + supportsReasoningBudget: true, + }, +} as const satisfies Record From 5f326c43309b70236920bd2ff22061e50f7ce515 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Sun, 20 Jul 2025 23:06:48 +0100 Subject: [PATCH 02/13] fix: progressive migration to new Gemini naming conventions on both frontend and backend --- packages/types/src/providers/gemini.ts | 54 -------------- src/api/providers/__tests__/gemini.spec.ts | 56 +++++++++++++++ src/api/providers/__tests__/vertex.spec.ts | 62 ++++++++++++++++ src/api/providers/gemini.ts | 48 ++++++++++++- src/api/providers/vertex.ts | 44 +++++++++++- .../components/ui/hooks/useSelectedModel.ts | 70 ++++++++++++++++++- 6 files changed, 274 insertions(+), 60 deletions(-) diff --git a/packages/types/src/providers/gemini.ts b/packages/types/src/providers/gemini.ts index 88f3c7bcf6..bd6a079883 100644 --- a/packages/types/src/providers/gemini.ts +++ b/packages/types/src/providers/gemini.ts @@ -152,60 +152,6 @@ export const geminiModels = { } as const satisfies Record export const legacyGeminiModels = { - "gemini-2.5-flash-preview-04-17:thinking": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0.15, - outputPrice: 3.5, - maxThinkingTokens: 24_576, - supportsReasoningBudget: true, - requiredReasoningBudget: true, - }, - "gemini-2.5-flash-preview-04-17": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0.15, - outputPrice: 0.6, - }, - "gemini-2.5-flash-preview-05-20:thinking": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: true, - inputPrice: 0.15, - outputPrice: 3.5, - cacheReadsPrice: 0.0375, - cacheWritesPrice: 1.0, - maxThinkingTokens: 24_576, - supportsReasoningBudget: true, - requiredReasoningBudget: true, - }, - "gemini-2.5-flash-preview-05-20": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: true, - inputPrice: 0.15, - outputPrice: 0.6, - cacheReadsPrice: 0.0375, - cacheWritesPrice: 1.0, - }, - "gemini-2.5-flash": { - maxTokens: 64_000, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: true, - inputPrice: 0.3, - outputPrice: 2.5, - cacheReadsPrice: 0.075, - cacheWritesPrice: 1.0, - maxThinkingTokens: 24_576, - supportsReasoningBudget: true, - }, "gemini-2.5-pro-exp-03-25": { maxTokens: 65_535, contextWindow: 1_048_576, diff --git a/src/api/providers/__tests__/gemini.spec.ts b/src/api/providers/__tests__/gemini.spec.ts index 8a7fd24fe3..fd4495c3f7 100644 --- a/src/api/providers/__tests__/gemini.spec.ts +++ b/src/api/providers/__tests__/gemini.spec.ts @@ -163,6 +163,62 @@ describe("GeminiHandler", () => { }) }) + describe("legacy model migration", () => { + it("should map gemini-2.5-pro-preview-{dates} to gemini-2.5-pro", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-2.5-pro-preview-03-25", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-pro") + }) + + it("should map gemini-1.5-pro-{variants} to gemini-2.0-pro-exp-02-05", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-1.5-pro-002", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.0-pro-exp-02-05") + }) + + it("should map gemini-1.5-flash-{variants} to gemini-2.0-flash-001", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-1.5-flash-002", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.0-flash-001") + }) + + it("should map experimental gemini-2.5-pro-exp-03-25 to gemini-2.5-pro", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-2.5-pro-exp-03-25", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-pro") + }) + + it("should map gemini-exp-1206 to gemini-2.0-pro-exp-02-05", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-exp-1206", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.0-pro-exp-02-05") + }) + + it("should keep current models as-is", () => { + const currentHandler = new GeminiHandler({ + apiModelId: "gemini-2.5-pro", + geminiApiKey: "test-key", + }) + const modelInfo = currentHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-pro") + }) + }) + describe("calculateCost", () => { // Mock ModelInfo based on gemini-1.5-flash-latest pricing (per 1M tokens) // Removed 'id' and 'name' as they are not part of ModelInfo type directly diff --git a/src/api/providers/__tests__/vertex.spec.ts b/src/api/providers/__tests__/vertex.spec.ts index 8e9add524d..893ff8310a 100644 --- a/src/api/providers/__tests__/vertex.spec.ts +++ b/src/api/providers/__tests__/vertex.spec.ts @@ -137,4 +137,66 @@ describe("VertexHandler", () => { expect(modelInfo.info.contextWindow).toBe(1048576) }) }) + + describe("legacy model migration", () => { + it("should map gemini-2.5-pro-preview-{dates} to gemini-2.5-pro", () => { + const legacyHandler = new VertexHandler({ + apiModelId: "gemini-2.5-pro-preview-03-25", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-pro") + }) + + it("should map gemini-1.5-pro-{variants} to gemini-2.0-pro-exp-02-05", () => { + const legacyHandler = new VertexHandler({ + apiModelId: "gemini-1.5-pro-002", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.0-pro-exp-02-05") + }) + + it("should map gemini-1.5-flash-{variants} to gemini-2.0-flash-001", () => { + const legacyHandler = new VertexHandler({ + apiModelId: "gemini-1.5-flash-002", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.0-flash-001") + }) + + it("should map experimental gemini-2.5-pro-exp-03-25 to gemini-2.5-pro", () => { + const legacyHandler = new VertexHandler({ + apiModelId: "gemini-2.5-pro-exp-03-25", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-pro") + }) + + it("should keep current vertex models as-is", () => { + const currentHandler = new VertexHandler({ + apiModelId: "gemini-2.5-pro", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + const modelInfo = currentHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-pro") + }) + + it("should keep claude models as-is", () => { + const claudeHandler = new VertexHandler({ + apiModelId: "claude-sonnet-4@20250514", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + const modelInfo = claudeHandler.getModel() + expect(modelInfo.id).toBe("claude-sonnet-4@20250514") + }) + }) }) diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index 6765c8676d..b084ff78a5 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -7,7 +7,13 @@ import { } from "@google/genai" import type { JWTInput } from "google-auth-library" -import { type ModelInfo, type GeminiModelId, geminiDefaultModelId, geminiModels } from "@roo-code/types" +import { + type ModelInfo, + type GeminiModelId, + geminiDefaultModelId, + geminiModels, + legacyGeminiModels, +} from "@roo-code/types" import type { ApiHandlerOptions } from "../../shared/api" import { safeJsonParse } from "../../shared/safeJsonParse" @@ -28,6 +34,39 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl private client: GoogleGenAI + /** + * Maps legacy Gemini model IDs to current supported models + */ + private mapLegacyGeminiModel(modelId: string): GeminiModelId { + if (modelId in geminiModels) { + return modelId as GeminiModelId + } + + if (modelId in legacyGeminiModels) { + if (modelId.startsWith("gemini-2.5-pro-preview-")) { + return "gemini-2.5-pro" + } + + if (modelId.startsWith("gemini-1.5-pro-")) { + return "gemini-2.0-pro-exp-02-05" + } + + if (modelId.startsWith("gemini-1.5-flash-")) { + return "gemini-2.0-flash-001" + } + + if (modelId === "gemini-2.5-pro-exp-") { + return "gemini-2.5-pro" + } + + if (modelId === "gemini-exp-1206") { + return "gemini-2.0-pro-exp-02-05" + } + } + + return geminiDefaultModelId + } + constructor({ isVertex, ...options }: GeminiHandlerOptions) { super() @@ -131,7 +170,12 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl override getModel() { const modelId = this.options.apiModelId - let id = modelId && modelId in geminiModels ? (modelId as GeminiModelId) : geminiDefaultModelId + let id = modelId ? this.mapLegacyGeminiModel(modelId) : geminiDefaultModelId + + if (modelId && modelId !== id) { + this.options.apiModelId = id + } + const info: ModelInfo = geminiModels[id] const params = getModelParams({ format: "gemini", modelId: id, model: info, settings: this.options }) diff --git a/src/api/providers/vertex.ts b/src/api/providers/vertex.ts index 2c077d97b7..a626651c6e 100644 --- a/src/api/providers/vertex.ts +++ b/src/api/providers/vertex.ts @@ -1,4 +1,10 @@ -import { type ModelInfo, type VertexModelId, vertexDefaultModelId, vertexModels } from "@roo-code/types" +import { + type ModelInfo, + type VertexModelId, + vertexDefaultModelId, + vertexModels, + legacyVertexModels, +} from "@roo-code/types" import type { ApiHandlerOptions } from "../../shared/api" @@ -12,9 +18,43 @@ export class VertexHandler extends GeminiHandler implements SingleCompletionHand super({ ...options, isVertex: true }) } + /** + * Maps legacy Vertex model IDs to current supported models + */ + private mapLegacyVertexModel(modelId: string): VertexModelId { + if (modelId in vertexModels) { + return modelId as VertexModelId + } + + if (modelId in legacyVertexModels) { + if (modelId.startsWith("gemini-2.5-pro-preview-")) { + return "gemini-2.5-pro" + } + + if (modelId.startsWith("gemini-1.5-pro-")) { + return "gemini-2.0-pro-exp-02-05" + } + + if (modelId.startsWith("gemini-1.5-flash-")) { + return "gemini-2.0-flash-001" + } + + if (modelId === "gemini-2.5-pro-exp-") { + return "gemini-2.5-pro" + } + } + + return vertexDefaultModelId + } + override getModel() { const modelId = this.options.apiModelId - let id = modelId && modelId in vertexModels ? (modelId as VertexModelId) : vertexDefaultModelId + let id = modelId ? this.mapLegacyVertexModel(modelId) : vertexDefaultModelId + + if (modelId && id && modelId !== id) { + this.options.apiModelId = id + } + const info: ModelInfo = vertexModels[id] const params = getModelParams({ format: "gemini", modelId: id, model: info, settings: this.options }) diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 40c1ff2431..3344391e04 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -10,6 +10,7 @@ import { deepSeekModels, geminiDefaultModelId, geminiModels, + legacyGeminiModels, mistralDefaultModelId, mistralModels, openAiModelInfoSaneDefaults, @@ -17,6 +18,7 @@ import { openAiNativeModels, vertexDefaultModelId, vertexModels, + legacyVertexModels, xaiDefaultModelId, xaiModels, groqModels, @@ -39,6 +41,68 @@ import type { RouterModels } from "@roo/api" import { useRouterModels } from "./useRouterModels" import { useOpenRouterModelProviders } from "./useOpenRouterModelProviders" +/** + * Maps legacy Gemini model IDs to current supported models + */ +function mapLegacyGeminiModel(modelId: string): string { + if (modelId in geminiModels) { + return modelId + } + + if (modelId in legacyGeminiModels) { + if (modelId.startsWith("gemini-2.5-pro-preview-")) { + return "gemini-2.5-pro" + } + + if (modelId.startsWith("gemini-1.5-pro-")) { + return "gemini-2.0-pro-exp-02-05" + } + + if (modelId.startsWith("gemini-1.5-flash-")) { + return "gemini-2.0-flash-001" + } + + if (modelId === "gemini-2.5-pro-exp-03-25") { + return "gemini-2.5-pro" + } + + if (modelId === "gemini-exp-1206") { + return "gemini-2.0-pro-exp-02-05" + } + } + + return geminiDefaultModelId +} + +/** + * Maps legacy Vertex model IDs to current supported models + */ +function mapLegacyVertexModel(modelId: string): string { + if (modelId in vertexModels) { + return modelId + } + + if (modelId in legacyVertexModels) { + if (modelId.startsWith("gemini-2.5-pro-preview-")) { + return "gemini-2.5-pro" + } + + if (modelId.startsWith("gemini-1.5-pro-")) { + return "gemini-2.0-pro-exp-02-05" + } + + if (modelId.startsWith("gemini-1.5-flash-")) { + return "gemini-2.0-flash-001" + } + + if (modelId === "gemini-2.5-pro-exp-03-25") { + return "gemini-2.5-pro" + } + } + + return vertexDefaultModelId +} + export const useSelectedModel = (apiConfiguration?: ProviderSettings) => { const provider = apiConfiguration?.apiProvider || "anthropic" const openRouterModelId = provider === "openrouter" ? apiConfiguration?.openRouterModelId : undefined @@ -148,12 +212,14 @@ function getSelectedModel({ return { id, info } } case "vertex": { - const id = apiConfiguration.apiModelId ?? vertexDefaultModelId + const rawId = apiConfiguration.apiModelId ?? vertexDefaultModelId + const id = mapLegacyVertexModel(rawId) const info = vertexModels[id as keyof typeof vertexModels] return { id, info } } case "gemini": { - const id = apiConfiguration.apiModelId ?? geminiDefaultModelId + const rawId = apiConfiguration.apiModelId ?? geminiDefaultModelId + const id = mapLegacyGeminiModel(rawId) const info = geminiModels[id as keyof typeof geminiModels] return { id, info } } From 6dab2cdd77466a0d1e5d212624339de167288b49 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Sun, 20 Jul 2025 23:26:48 +0100 Subject: [PATCH 03/13] fix: gemini-1.5 and gemini-exp to be migrated to geminiDefaultModelId --- src/api/providers/gemini.ts | 6 +++--- src/api/providers/vertex.ts | 2 +- webview-ui/src/components/ui/hooks/useSelectedModel.ts | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index b084ff78a5..a496b36f22 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -48,11 +48,11 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl } if (modelId.startsWith("gemini-1.5-pro-")) { - return "gemini-2.0-pro-exp-02-05" + return geminiDefaultModelId } if (modelId.startsWith("gemini-1.5-flash-")) { - return "gemini-2.0-flash-001" + return geminiDefaultModelId } if (modelId === "gemini-2.5-pro-exp-") { @@ -60,7 +60,7 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl } if (modelId === "gemini-exp-1206") { - return "gemini-2.0-pro-exp-02-05" + return geminiDefaultModelId } } diff --git a/src/api/providers/vertex.ts b/src/api/providers/vertex.ts index a626651c6e..6afc1bee01 100644 --- a/src/api/providers/vertex.ts +++ b/src/api/providers/vertex.ts @@ -32,7 +32,7 @@ export class VertexHandler extends GeminiHandler implements SingleCompletionHand } if (modelId.startsWith("gemini-1.5-pro-")) { - return "gemini-2.0-pro-exp-02-05" + return "gemini-2.0-flash-001" } if (modelId.startsWith("gemini-1.5-flash-")) { diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 3344391e04..fb5b08646a 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -55,11 +55,11 @@ function mapLegacyGeminiModel(modelId: string): string { } if (modelId.startsWith("gemini-1.5-pro-")) { - return "gemini-2.0-pro-exp-02-05" + return geminiDefaultModelId } if (modelId.startsWith("gemini-1.5-flash-")) { - return "gemini-2.0-flash-001" + return geminiDefaultModelId } if (modelId === "gemini-2.5-pro-exp-03-25") { @@ -67,7 +67,7 @@ function mapLegacyGeminiModel(modelId: string): string { } if (modelId === "gemini-exp-1206") { - return "gemini-2.0-pro-exp-02-05" + return geminiDefaultModelId } } @@ -88,7 +88,7 @@ function mapLegacyVertexModel(modelId: string): string { } if (modelId.startsWith("gemini-1.5-pro-")) { - return "gemini-2.0-pro-exp-02-05" + return "gemini-2.0-flash-001" } if (modelId.startsWith("gemini-1.5-flash-")) { From 3122b41587390056667f542fb094fc409ad11bb0 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Sun, 20 Jul 2025 23:32:59 +0100 Subject: [PATCH 04/13] fix: making changes based on the AI code reviewer --- src/api/providers/gemini.ts | 2 +- src/api/providers/vertex.ts | 2 +- webview-ui/src/components/ui/hooks/useSelectedModel.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index a496b36f22..bf50cb6b59 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -55,7 +55,7 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl return geminiDefaultModelId } - if (modelId === "gemini-2.5-pro-exp-") { + if (modelId.startsWith("gemini-2.5-pro-exp-")) { return "gemini-2.5-pro" } diff --git a/src/api/providers/vertex.ts b/src/api/providers/vertex.ts index 6afc1bee01..f7312aa330 100644 --- a/src/api/providers/vertex.ts +++ b/src/api/providers/vertex.ts @@ -39,7 +39,7 @@ export class VertexHandler extends GeminiHandler implements SingleCompletionHand return "gemini-2.0-flash-001" } - if (modelId === "gemini-2.5-pro-exp-") { + if (modelId.startsWith("gemini-2.5-pro-exp-")) { return "gemini-2.5-pro" } } diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index fb5b08646a..afc3ebbfaa 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -62,7 +62,7 @@ function mapLegacyGeminiModel(modelId: string): string { return geminiDefaultModelId } - if (modelId === "gemini-2.5-pro-exp-03-25") { + if (modelId.startsWith("gemini-2.5-pro-exp-")) { return "gemini-2.5-pro" } @@ -95,7 +95,7 @@ function mapLegacyVertexModel(modelId: string): string { return "gemini-2.0-flash-001" } - if (modelId === "gemini-2.5-pro-exp-03-25") { + if (modelId.startsWith("gemini-2.5-pro-exp-")) { return "gemini-2.5-pro" } } From 642507e145d4b55a2adeba928c0a605839d25999 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Sun, 20 Jul 2025 23:47:34 +0100 Subject: [PATCH 05/13] fix: updating unit tests --- src/api/providers/__tests__/gemini.spec.ts | 8 ++++---- src/api/providers/__tests__/vertex.spec.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api/providers/__tests__/gemini.spec.ts b/src/api/providers/__tests__/gemini.spec.ts index fd4495c3f7..7729742296 100644 --- a/src/api/providers/__tests__/gemini.spec.ts +++ b/src/api/providers/__tests__/gemini.spec.ts @@ -173,13 +173,13 @@ describe("GeminiHandler", () => { expect(modelInfo.id).toBe("gemini-2.5-pro") }) - it("should map gemini-1.5-pro-{variants} to gemini-2.0-pro-exp-02-05", () => { + it("should map gemini-1.5-pro-{variants} to gemini-2.0-flash-001", () => { const legacyHandler = new GeminiHandler({ apiModelId: "gemini-1.5-pro-002", geminiApiKey: "test-key", }) const modelInfo = legacyHandler.getModel() - expect(modelInfo.id).toBe("gemini-2.0-pro-exp-02-05") + expect(modelInfo.id).toBe("gemini-2.0-flash-001") }) it("should map gemini-1.5-flash-{variants} to gemini-2.0-flash-001", () => { @@ -200,13 +200,13 @@ describe("GeminiHandler", () => { expect(modelInfo.id).toBe("gemini-2.5-pro") }) - it("should map gemini-exp-1206 to gemini-2.0-pro-exp-02-05", () => { + it("should map gemini-exp-1206 to gemini-2.0-flash-001", () => { const legacyHandler = new GeminiHandler({ apiModelId: "gemini-exp-1206", geminiApiKey: "test-key", }) const modelInfo = legacyHandler.getModel() - expect(modelInfo.id).toBe("gemini-2.0-pro-exp-02-05") + expect(modelInfo.id).toBe("gemini-2.0-flash-001") }) it("should keep current models as-is", () => { diff --git a/src/api/providers/__tests__/vertex.spec.ts b/src/api/providers/__tests__/vertex.spec.ts index 893ff8310a..117ee9f6ea 100644 --- a/src/api/providers/__tests__/vertex.spec.ts +++ b/src/api/providers/__tests__/vertex.spec.ts @@ -149,14 +149,14 @@ describe("VertexHandler", () => { expect(modelInfo.id).toBe("gemini-2.5-pro") }) - it("should map gemini-1.5-pro-{variants} to gemini-2.0-pro-exp-02-05", () => { + it("should map gemini-1.5-pro-{variants} to gemini-2.0-flash-001", () => { const legacyHandler = new VertexHandler({ apiModelId: "gemini-1.5-pro-002", vertexProjectId: "test-project", vertexRegion: "us-central1", }) const modelInfo = legacyHandler.getModel() - expect(modelInfo.id).toBe("gemini-2.0-pro-exp-02-05") + expect(modelInfo.id).toBe("gemini-2.0-flash-001") }) it("should map gemini-1.5-flash-{variants} to gemini-2.0-flash-001", () => { From c45802b482186aee4898b0924712416ef69074f7 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Thu, 24 Jul 2025 22:25:06 +0100 Subject: [PATCH 06/13] fix: adding more migration changes and unit tests --- packages/types/src/providers/gemini.ts | 102 ++++++++++----------- packages/types/src/providers/vertex.ts | 70 +++++++------- src/api/providers/__tests__/gemini.spec.ts | 45 +++++++++ src/api/providers/__tests__/vertex.spec.ts | 40 ++++++++ src/api/providers/gemini.ts | 16 ++++ src/api/providers/vertex.ts | 12 +++ 6 files changed, 199 insertions(+), 86 deletions(-) diff --git a/packages/types/src/providers/gemini.ts b/packages/types/src/providers/gemini.ts index bd6a079883..4a6a73acbb 100644 --- a/packages/types/src/providers/gemini.ts +++ b/packages/types/src/providers/gemini.ts @@ -6,25 +6,6 @@ export type GeminiModelId = keyof typeof geminiModels export const geminiDefaultModelId: GeminiModelId = "gemini-2.0-flash-001" export const geminiModels = { - "gemini-2.5-flash-preview-04-17:thinking": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0.15, - outputPrice: 3.5, - maxThinkingTokens: 24_576, - supportsReasoningBudget: true, - requiredReasoningBudget: true, - }, - "gemini-2.5-flash-preview-04-17": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0.15, - outputPrice: 0.6, - }, "gemini-2.5-flash-preview-05-20:thinking": { maxTokens: 65_535, contextWindow: 1_048_576, @@ -105,38 +86,6 @@ export const geminiModels = { inputPrice: 0, outputPrice: 0, }, - "gemini-2.0-pro-exp-02-05": { - maxTokens: 8192, - contextWindow: 2_097_152, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, - "gemini-2.0-flash-thinking-exp-01-21": { - maxTokens: 65_536, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, - "gemini-2.0-flash-thinking-exp-1219": { - maxTokens: 8192, - contextWindow: 32_767, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, - "gemini-2.0-flash-exp": { - maxTokens: 8192, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, "gemini-2.5-flash-lite-preview-06-17": { maxTokens: 64_000, contextWindow: 1_048_576, @@ -298,4 +247,55 @@ export const legacyGeminiModels = { inputPrice: 0, outputPrice: 0, }, + "gemini-2.0-pro-exp-02-05": { + maxTokens: 8192, + contextWindow: 2_097_152, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.0-flash-thinking-exp-1219": { + maxTokens: 8192, + contextWindow: 32_767, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.0-flash-thinking-exp-01-21": { + maxTokens: 65_536, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.5-flash-preview-04-17:thinking": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0.15, + outputPrice: 3.5, + maxThinkingTokens: 24_576, + supportsReasoningBudget: true, + requiredReasoningBudget: true, + }, + "gemini-2.5-flash-preview-04-17": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0.15, + outputPrice: 0.6, + }, + "gemini-2.0-flash-exp": { + maxTokens: 8192, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, } as const satisfies Record diff --git a/packages/types/src/providers/vertex.ts b/packages/types/src/providers/vertex.ts index 34be4b1a21..bab0efcff1 100644 --- a/packages/types/src/providers/vertex.ts +++ b/packages/types/src/providers/vertex.ts @@ -37,25 +37,6 @@ export const vertexModels = { maxThinkingTokens: 24_576, supportsReasoningBudget: true, }, - "gemini-2.5-flash-preview-04-17:thinking": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0.15, - outputPrice: 3.5, - maxThinkingTokens: 24_576, - supportsReasoningBudget: true, - requiredReasoningBudget: true, - }, - "gemini-2.5-flash-preview-04-17": { - maxTokens: 65_535, - contextWindow: 1_048_576, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0.15, - outputPrice: 0.6, - }, "gemini-2.5-pro": { maxTokens: 64_000, contextWindow: 1_048_576, @@ -81,14 +62,6 @@ export const vertexModels = { }, ], }, - "gemini-2.0-pro-exp-02-05": { - maxTokens: 8192, - contextWindow: 2_097_152, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, "gemini-2.0-flash-001": { maxTokens: 8192, contextWindow: 1_048_576, @@ -105,14 +78,6 @@ export const vertexModels = { inputPrice: 0.075, outputPrice: 0.3, }, - "gemini-2.0-flash-thinking-exp-01-21": { - maxTokens: 8192, - contextWindow: 32_768, - supportsImages: true, - supportsPromptCache: false, - inputPrice: 0, - outputPrice: 0, - }, "claude-sonnet-4@20250514": { maxTokens: 8192, contextWindow: 200_000, @@ -311,4 +276,39 @@ export const legacyVertexModels = { maxThinkingTokens: 32_768, supportsReasoningBudget: true, }, + "gemini-2.0-pro-exp-02-05": { + maxTokens: 8192, + contextWindow: 2_097_152, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.0-flash-thinking-exp-01-21": { + maxTokens: 8192, + contextWindow: 32_768, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0, + outputPrice: 0, + }, + "gemini-2.5-flash-preview-04-17:thinking": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0.15, + outputPrice: 3.5, + maxThinkingTokens: 24_576, + supportsReasoningBudget: true, + requiredReasoningBudget: true, + }, + "gemini-2.5-flash-preview-04-17": { + maxTokens: 65_535, + contextWindow: 1_048_576, + supportsImages: true, + supportsPromptCache: false, + inputPrice: 0.15, + outputPrice: 0.6, + }, } as const satisfies Record diff --git a/src/api/providers/__tests__/gemini.spec.ts b/src/api/providers/__tests__/gemini.spec.ts index 7729742296..664550d030 100644 --- a/src/api/providers/__tests__/gemini.spec.ts +++ b/src/api/providers/__tests__/gemini.spec.ts @@ -209,6 +209,51 @@ describe("GeminiHandler", () => { expect(modelInfo.id).toBe("gemini-2.0-flash-001") }) + it("should map gemini-2.0-pro-exp-02-05 to gemini-2.5-pro", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-2.0-pro-exp-02-05", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-pro") + }) + + it("should map gemini-2.0-flash-thinking-exp-1219 to gemini-2.5-flash-preview-05-20", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-2.0-flash-thinking-exp-1219", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-flash-preview-05-20") + }) + + it("should map gemini-2.0-flash-thinking-exp-01-21 to gemini-2.5-flash-preview-05-20", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-2.0-flash-thinking-exp-01-21", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-flash-preview-05-20") + }) + + it("should map gemini-2.5-flash-preview-04-17 to gemini-2.5-flash-preview-05-20", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-2.5-flash-preview-04-17", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-flash-preview-05-20") + }) + + it("should map gemini-2.0-flash-exp to gemini-2.0-flash-001", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-2.0-flash-exp", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.0-flash-001") + }) + it("should keep current models as-is", () => { const currentHandler = new GeminiHandler({ apiModelId: "gemini-2.5-pro", diff --git a/src/api/providers/__tests__/vertex.spec.ts b/src/api/providers/__tests__/vertex.spec.ts index 117ee9f6ea..5a7a751c14 100644 --- a/src/api/providers/__tests__/vertex.spec.ts +++ b/src/api/providers/__tests__/vertex.spec.ts @@ -189,6 +189,46 @@ describe("VertexHandler", () => { expect(modelInfo.id).toBe("gemini-2.5-pro") }) + it("should map gemini-2.0-pro-exp-02-05 to gemini-2.5-pro", () => { + const legacyHandler = new VertexHandler({ + apiModelId: "gemini-2.0-pro-exp-02-05", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-pro") + }) + + it("should map gemini-2.0-flash-thinking-exp-1219 to gemini-2.5-flash-preview-05-20", () => { + const legacyHandler = new VertexHandler({ + apiModelId: "gemini-2.0-flash-thinking-exp-1219", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-flash-preview-05-20") + }) + + it("should map gemini-2.0-flash-thinking-exp-01-21 to gemini-2.5-flash-preview-05-20", () => { + const legacyHandler = new VertexHandler({ + apiModelId: "gemini-2.0-flash-thinking-exp-01-21", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-flash-preview-05-20") + }) + + it("should map gemini-2.5-flash-preview-04-17 to gemini-2.5-flash-preview-05-20", () => { + const legacyHandler = new VertexHandler({ + apiModelId: "gemini-2.5-flash-preview-04-17", + vertexProjectId: "test-project", + vertexRegion: "us-central1", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-flash-preview-05-20") + }) + it("should keep claude models as-is", () => { const claudeHandler = new VertexHandler({ apiModelId: "claude-sonnet-4@20250514", diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index bf50cb6b59..a6d0b4f214 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -62,6 +62,22 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl if (modelId === "gemini-exp-1206") { return geminiDefaultModelId } + + if (modelId === "gemini-2.0-pro-exp-02-05") { + return "gemini-2.5-pro" + } + + if ( + modelId === "gemini-2.0-flash-thinking-exp-1219" || + modelId === "gemini-2.0-flash-thinking-exp-01-21" || + modelId === "gemini-2.5-flash-preview-04-17" + ) { + return "gemini-2.5-flash-preview-05-20" + } + + if (modelId === "gemini-2.0-flash-exp") { + return geminiDefaultModelId + } } return geminiDefaultModelId diff --git a/src/api/providers/vertex.ts b/src/api/providers/vertex.ts index f7312aa330..5e5f1ed1c5 100644 --- a/src/api/providers/vertex.ts +++ b/src/api/providers/vertex.ts @@ -42,6 +42,18 @@ export class VertexHandler extends GeminiHandler implements SingleCompletionHand if (modelId.startsWith("gemini-2.5-pro-exp-")) { return "gemini-2.5-pro" } + + if (modelId === "gemini-2.0-pro-exp-02-05") { + return "gemini-2.5-pro" + } + + if ( + modelId === "gemini-2.0-flash-thinking-exp-1219" || + modelId === "gemini-2.0-flash-thinking-exp-01-21" || + modelId === "gemini-2.5-flash-preview-04-17" + ) { + return "gemini-2.5-flash-preview-05-20" + } } return vertexDefaultModelId From 43f10e3a2970eac6058816387f0b80f8a727c4f3 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Thu, 24 Jul 2025 22:50:19 +0100 Subject: [PATCH 07/13] fix: adding mapping on the frontend-side --- .../components/ui/hooks/useSelectedModel.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index afc3ebbfaa..0d74b28a3f 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -69,6 +69,22 @@ function mapLegacyGeminiModel(modelId: string): string { if (modelId === "gemini-exp-1206") { return geminiDefaultModelId } + + if (modelId === "gemini-2.0-pro-exp-02-05") { + return "gemini-2.5-pro" + } + + if ( + modelId === "gemini-2.0-flash-thinking-exp-1219" || + modelId === "gemini-2.0-flash-thinking-exp-01-21" || + modelId === "gemini-2.5-flash-preview-04-17" + ) { + return "gemini-2.5-flash-preview-05-20" + } + + if (modelId === "gemini-2.0-flash-exp") { + return geminiDefaultModelId + } } return geminiDefaultModelId @@ -98,6 +114,18 @@ function mapLegacyVertexModel(modelId: string): string { if (modelId.startsWith("gemini-2.5-pro-exp-")) { return "gemini-2.5-pro" } + + if (modelId === "gemini-2.0-pro-exp-02-05") { + return "gemini-2.5-pro" + } + + if ( + modelId === "gemini-2.0-flash-thinking-exp-1219" || + modelId === "gemini-2.0-flash-thinking-exp-01-21" || + modelId === "gemini-2.5-flash-preview-04-17" + ) { + return "gemini-2.5-flash-preview-05-20" + } } return vertexDefaultModelId From 1a4a7a5d942e20715c22fb4cde1e31aaffdc2015 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Thu, 24 Jul 2025 23:08:01 +0100 Subject: [PATCH 08/13] fix: updaing model id for existing unit tests and handling gemini-2.5-flash-preview-04-17:thinking --- src/api/providers/__tests__/gemini.spec.ts | 21 +++++++++++++------ src/api/providers/__tests__/vertex.spec.ts | 12 +++++------ src/api/providers/gemini.ts | 3 ++- src/api/providers/vertex.ts | 3 ++- .../components/ui/hooks/useSelectedModel.ts | 6 ++++-- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/api/providers/__tests__/gemini.spec.ts b/src/api/providers/__tests__/gemini.spec.ts index 664550d030..f9ee6039d4 100644 --- a/src/api/providers/__tests__/gemini.spec.ts +++ b/src/api/providers/__tests__/gemini.spec.ts @@ -6,7 +6,7 @@ import { type ModelInfo, geminiDefaultModelId } from "@roo-code/types" import { GeminiHandler } from "../gemini" -const GEMINI_20_FLASH_THINKING_NAME = "gemini-2.0-flash-thinking-exp-1219" +const GEMINI_25_FLASH_PREVIEW_05_20_NAME = "gemini-2.5-flash-preview-05-20" describe("GeminiHandler", () => { let handler: GeminiHandler @@ -19,7 +19,7 @@ describe("GeminiHandler", () => { handler = new GeminiHandler({ apiKey: "test-key", - apiModelId: GEMINI_20_FLASH_THINKING_NAME, + apiModelId: GEMINI_25_FLASH_PREVIEW_05_20_NAME, geminiApiKey: "test-key", }) @@ -36,7 +36,7 @@ describe("GeminiHandler", () => { describe("constructor", () => { it("should initialize with provided config", () => { expect(handler["options"].geminiApiKey).toBe("test-key") - expect(handler["options"].apiModelId).toBe(GEMINI_20_FLASH_THINKING_NAME) + expect(handler["options"].apiModelId).toBe(GEMINI_25_FLASH_PREVIEW_05_20_NAME) }) }) @@ -80,7 +80,7 @@ describe("GeminiHandler", () => { // Verify the call to generateContentStream expect(handler["client"].models.generateContentStream).toHaveBeenCalledWith( expect.objectContaining({ - model: GEMINI_20_FLASH_THINKING_NAME, + model: GEMINI_25_FLASH_PREVIEW_05_20_NAME, config: expect.objectContaining({ temperature: 0, systemInstruction: systemPrompt, @@ -115,7 +115,7 @@ describe("GeminiHandler", () => { // Verify the call to generateContent expect(handler["client"].models.generateContent).toHaveBeenCalledWith({ - model: GEMINI_20_FLASH_THINKING_NAME, + model: GEMINI_25_FLASH_PREVIEW_05_20_NAME, contents: [{ role: "user", parts: [{ text: "Test prompt" }] }], config: { httpOptions: undefined, @@ -147,7 +147,7 @@ describe("GeminiHandler", () => { describe("getModel", () => { it("should return correct model info", () => { const modelInfo = handler.getModel() - expect(modelInfo.id).toBe(GEMINI_20_FLASH_THINKING_NAME) + expect(modelInfo.id).toBe(GEMINI_25_FLASH_PREVIEW_05_20_NAME) expect(modelInfo.info).toBeDefined() expect(modelInfo.info.maxTokens).toBe(8192) expect(modelInfo.info.contextWindow).toBe(32_767) @@ -254,6 +254,15 @@ describe("GeminiHandler", () => { expect(modelInfo.id).toBe("gemini-2.0-flash-001") }) + it("should map gemini-2.5-flash-preview-04-17:thinking to gemini-2.5-flash-preview-05-20", () => { + const legacyHandler = new GeminiHandler({ + apiModelId: "gemini-2.5-flash-preview-04-17:thinking", + geminiApiKey: "test-key", + }) + const modelInfo = legacyHandler.getModel() + expect(modelInfo.id).toBe("gemini-2.5-flash-preview-05-20") + }) + it("should keep current models as-is", () => { const currentHandler = new GeminiHandler({ apiModelId: "gemini-2.5-pro", diff --git a/src/api/providers/__tests__/vertex.spec.ts b/src/api/providers/__tests__/vertex.spec.ts index 5a7a751c14..0c53895a29 100644 --- a/src/api/providers/__tests__/vertex.spec.ts +++ b/src/api/providers/__tests__/vertex.spec.ts @@ -199,9 +199,9 @@ describe("VertexHandler", () => { expect(modelInfo.id).toBe("gemini-2.5-pro") }) - it("should map gemini-2.0-flash-thinking-exp-1219 to gemini-2.5-flash-preview-05-20", () => { + it("should map gemini-2.0-flash-thinking-exp-01-21 to gemini-2.5-flash-preview-05-20", () => { const legacyHandler = new VertexHandler({ - apiModelId: "gemini-2.0-flash-thinking-exp-1219", + apiModelId: "gemini-2.0-flash-thinking-exp-01-21", vertexProjectId: "test-project", vertexRegion: "us-central1", }) @@ -209,9 +209,9 @@ describe("VertexHandler", () => { expect(modelInfo.id).toBe("gemini-2.5-flash-preview-05-20") }) - it("should map gemini-2.0-flash-thinking-exp-01-21 to gemini-2.5-flash-preview-05-20", () => { + it("should map gemini-2.5-flash-preview-04-17 to gemini-2.5-flash-preview-05-20", () => { const legacyHandler = new VertexHandler({ - apiModelId: "gemini-2.0-flash-thinking-exp-01-21", + apiModelId: "gemini-2.5-flash-preview-04-17", vertexProjectId: "test-project", vertexRegion: "us-central1", }) @@ -219,9 +219,9 @@ describe("VertexHandler", () => { expect(modelInfo.id).toBe("gemini-2.5-flash-preview-05-20") }) - it("should map gemini-2.5-flash-preview-04-17 to gemini-2.5-flash-preview-05-20", () => { + it("should map gemini-2.5-flash-preview-04-17:thinking to gemini-2.5-flash-preview-05-20", () => { const legacyHandler = new VertexHandler({ - apiModelId: "gemini-2.5-flash-preview-04-17", + apiModelId: "gemini-2.5-flash-preview-04-17:thinking", vertexProjectId: "test-project", vertexRegion: "us-central1", }) diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index a6d0b4f214..66240c7191 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -70,7 +70,8 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl if ( modelId === "gemini-2.0-flash-thinking-exp-1219" || modelId === "gemini-2.0-flash-thinking-exp-01-21" || - modelId === "gemini-2.5-flash-preview-04-17" + modelId === "gemini-2.5-flash-preview-04-17" || + modelId === "gemini-2.5-flash-preview-04-17:thinking" ) { return "gemini-2.5-flash-preview-05-20" } diff --git a/src/api/providers/vertex.ts b/src/api/providers/vertex.ts index 5e5f1ed1c5..a0aa7666ae 100644 --- a/src/api/providers/vertex.ts +++ b/src/api/providers/vertex.ts @@ -50,7 +50,8 @@ export class VertexHandler extends GeminiHandler implements SingleCompletionHand if ( modelId === "gemini-2.0-flash-thinking-exp-1219" || modelId === "gemini-2.0-flash-thinking-exp-01-21" || - modelId === "gemini-2.5-flash-preview-04-17" + modelId === "gemini-2.5-flash-preview-04-17" || + modelId === "gemini-2.5-flash-preview-04-17:thinking" ) { return "gemini-2.5-flash-preview-05-20" } diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index c29316dff2..8410da0f27 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -79,7 +79,8 @@ function mapLegacyGeminiModel(modelId: string): string { if ( modelId === "gemini-2.0-flash-thinking-exp-1219" || modelId === "gemini-2.0-flash-thinking-exp-01-21" || - modelId === "gemini-2.5-flash-preview-04-17" + modelId === "gemini-2.5-flash-preview-04-17" || + modelId === "gemini-2.5-flash-preview-04-17:thinking" ) { return "gemini-2.5-flash-preview-05-20" } @@ -124,7 +125,8 @@ function mapLegacyVertexModel(modelId: string): string { if ( modelId === "gemini-2.0-flash-thinking-exp-1219" || modelId === "gemini-2.0-flash-thinking-exp-01-21" || - modelId === "gemini-2.5-flash-preview-04-17" + modelId === "gemini-2.5-flash-preview-04-17" || + modelId === "gemini-2.5-flash-preview-04-17:thinking" ) { return "gemini-2.5-flash-preview-05-20" } From 2f2df3fef12f1ee15021100c0b3d57c78d6fdc72 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Thu, 24 Jul 2025 23:38:47 +0100 Subject: [PATCH 09/13] fix: updating existing unit tests --- src/api/providers/__tests__/gemini.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/providers/__tests__/gemini.spec.ts b/src/api/providers/__tests__/gemini.spec.ts index f9ee6039d4..8fa426837e 100644 --- a/src/api/providers/__tests__/gemini.spec.ts +++ b/src/api/providers/__tests__/gemini.spec.ts @@ -149,8 +149,8 @@ describe("GeminiHandler", () => { const modelInfo = handler.getModel() expect(modelInfo.id).toBe(GEMINI_25_FLASH_PREVIEW_05_20_NAME) expect(modelInfo.info).toBeDefined() - expect(modelInfo.info.maxTokens).toBe(8192) - expect(modelInfo.info.contextWindow).toBe(32_767) + expect(modelInfo.info.maxTokens).toBe(65_535) + expect(modelInfo.info.contextWindow).toBe(1_048_576) }) it("should return default model if invalid model specified", () => { From 832d2989cb5d93de796141a6b45200809b5d6c46 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Fri, 25 Jul 2025 12:20:13 +0100 Subject: [PATCH 10/13] fix: updating the existing test to adapt to from --- src/api/providers/__tests__/gemini.spec.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/api/providers/__tests__/gemini.spec.ts b/src/api/providers/__tests__/gemini.spec.ts index 8fa426837e..1ae9bdc53b 100644 --- a/src/api/providers/__tests__/gemini.spec.ts +++ b/src/api/providers/__tests__/gemini.spec.ts @@ -75,7 +75,14 @@ describe("GeminiHandler", () => { expect(chunks.length).toBe(3) expect(chunks[0]).toEqual({ type: "text", text: "Hello" }) expect(chunks[1]).toEqual({ type: "text", text: " world!" }) - expect(chunks[2]).toEqual({ type: "usage", inputTokens: 10, outputTokens: 5 }) + expect(chunks[2]).toEqual({ + type: "usage", + inputTokens: 10, + outputTokens: 5, + cacheReadTokens: undefined, + reasoningTokens: undefined, + totalCost: expect.any(Number), + }) // Verify the call to generateContentStream expect(handler["client"].models.generateContentStream).toHaveBeenCalledWith( From 9a1bc430e14d5edf25d837672817d4f7dedd1867 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Fri, 1 Aug 2025 00:26:31 +0100 Subject: [PATCH 11/13] fix: remove duplicated function and tested the functionality in debug mode --- packages/types/src/providers/gemini.ts | 50 ++++++++++ packages/types/src/providers/vertex.ts | 42 ++++++++ src/api/providers/gemini.ts | 60 +----------- src/api/providers/vertex.ts | 52 +--------- .../components/ui/hooks/useSelectedModel.ts | 96 +------------------ 5 files changed, 98 insertions(+), 202 deletions(-) diff --git a/packages/types/src/providers/gemini.ts b/packages/types/src/providers/gemini.ts index 4a6a73acbb..70fcc2cee3 100644 --- a/packages/types/src/providers/gemini.ts +++ b/packages/types/src/providers/gemini.ts @@ -299,3 +299,53 @@ export const legacyGeminiModels = { outputPrice: 0, }, } as const satisfies Record + +/** + * Maps legacy Gemini model IDs to current supported models + */ +export function mapLegacyGeminiModel(modelId: string): GeminiModelId { + if (modelId in geminiModels) { + return modelId as GeminiModelId + } + + if (modelId in legacyGeminiModels) { + if (modelId.startsWith("gemini-2.5-pro-preview-")) { + return "gemini-2.5-pro" + } + + if (modelId.startsWith("gemini-1.5-pro-")) { + return geminiDefaultModelId + } + + if (modelId.startsWith("gemini-1.5-flash-")) { + return geminiDefaultModelId + } + + if (modelId.startsWith("gemini-2.5-pro-exp-")) { + return "gemini-2.5-pro" + } + + if (modelId === "gemini-exp-1206") { + return geminiDefaultModelId + } + + if (modelId === "gemini-2.0-pro-exp-02-05") { + return "gemini-2.5-pro" + } + + if ( + modelId === "gemini-2.0-flash-thinking-exp-1219" || + modelId === "gemini-2.0-flash-thinking-exp-01-21" || + modelId === "gemini-2.5-flash-preview-04-17" || + modelId === "gemini-2.5-flash-preview-04-17:thinking" + ) { + return "gemini-2.5-flash-preview-05-20" + } + + if (modelId === "gemini-2.0-flash-exp") { + return geminiDefaultModelId + } + } + + return geminiDefaultModelId +} diff --git a/packages/types/src/providers/vertex.ts b/packages/types/src/providers/vertex.ts index 18d5b66d68..035b4d7eea 100644 --- a/packages/types/src/providers/vertex.ts +++ b/packages/types/src/providers/vertex.ts @@ -321,3 +321,45 @@ export const legacyVertexModels = { outputPrice: 0.6, }, } as const satisfies Record + +/** + * Maps legacy Vertex model IDs to current supported models + */ +export function mapLegacyVertexModel(modelId: string): VertexModelId { + if (modelId in vertexModels) { + return modelId as VertexModelId + } + + if (modelId in legacyVertexModels) { + if (modelId.startsWith("gemini-2.5-pro-preview-")) { + return "gemini-2.5-pro" + } + + if (modelId.startsWith("gemini-1.5-pro-")) { + return "gemini-2.0-flash-001" + } + + if (modelId.startsWith("gemini-1.5-flash-")) { + return "gemini-2.0-flash-001" + } + + if (modelId.startsWith("gemini-2.5-pro-exp-")) { + return "gemini-2.5-pro" + } + + if (modelId === "gemini-2.0-pro-exp-02-05") { + return "gemini-2.5-pro" + } + + if ( + modelId === "gemini-2.0-flash-thinking-exp-1219" || + modelId === "gemini-2.0-flash-thinking-exp-01-21" || + modelId === "gemini-2.5-flash-preview-04-17" || + modelId === "gemini-2.5-flash-preview-04-17:thinking" + ) { + return "gemini-2.5-flash-preview-05-20" + } + } + + return vertexDefaultModelId +} diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index 0ad34f6ebe..64b7323b68 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -8,13 +8,7 @@ import { } from "@google/genai" import type { JWTInput } from "google-auth-library" -import { - type ModelInfo, - type GeminiModelId, - geminiDefaultModelId, - geminiModels, - legacyGeminiModels, -} from "@roo-code/types" +import { type ModelInfo, geminiDefaultModelId, geminiModels, mapLegacyGeminiModel } from "@roo-code/types" import type { ApiHandlerOptions } from "../../shared/api" import { safeJsonParse } from "../../shared/safeJsonParse" @@ -36,56 +30,6 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl private client: GoogleGenAI - /** - * Maps legacy Gemini model IDs to current supported models - */ - private mapLegacyGeminiModel(modelId: string): GeminiModelId { - if (modelId in geminiModels) { - return modelId as GeminiModelId - } - - if (modelId in legacyGeminiModels) { - if (modelId.startsWith("gemini-2.5-pro-preview-")) { - return "gemini-2.5-pro" - } - - if (modelId.startsWith("gemini-1.5-pro-")) { - return geminiDefaultModelId - } - - if (modelId.startsWith("gemini-1.5-flash-")) { - return geminiDefaultModelId - } - - if (modelId.startsWith("gemini-2.5-pro-exp-")) { - return "gemini-2.5-pro" - } - - if (modelId === "gemini-exp-1206") { - return geminiDefaultModelId - } - - if (modelId === "gemini-2.0-pro-exp-02-05") { - return "gemini-2.5-pro" - } - - if ( - modelId === "gemini-2.0-flash-thinking-exp-1219" || - modelId === "gemini-2.0-flash-thinking-exp-01-21" || - modelId === "gemini-2.5-flash-preview-04-17" || - modelId === "gemini-2.5-flash-preview-04-17:thinking" - ) { - return "gemini-2.5-flash-preview-05-20" - } - - if (modelId === "gemini-2.0-flash-exp") { - return geminiDefaultModelId - } - } - - return geminiDefaultModelId - } - constructor({ isVertex, ...options }: GeminiHandlerOptions) { super() @@ -220,7 +164,7 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl override getModel() { const modelId = this.options.apiModelId - let id = modelId ? this.mapLegacyGeminiModel(modelId) : geminiDefaultModelId + let id = modelId ? mapLegacyGeminiModel(modelId) : geminiDefaultModelId if (modelId && modelId !== id) { this.options.apiModelId = id diff --git a/src/api/providers/vertex.ts b/src/api/providers/vertex.ts index a0aa7666ae..dee7f8c062 100644 --- a/src/api/providers/vertex.ts +++ b/src/api/providers/vertex.ts @@ -1,10 +1,4 @@ -import { - type ModelInfo, - type VertexModelId, - vertexDefaultModelId, - vertexModels, - legacyVertexModels, -} from "@roo-code/types" +import { type ModelInfo, vertexDefaultModelId, vertexModels, mapLegacyVertexModel } from "@roo-code/types" import type { ApiHandlerOptions } from "../../shared/api" @@ -18,51 +12,9 @@ export class VertexHandler extends GeminiHandler implements SingleCompletionHand super({ ...options, isVertex: true }) } - /** - * Maps legacy Vertex model IDs to current supported models - */ - private mapLegacyVertexModel(modelId: string): VertexModelId { - if (modelId in vertexModels) { - return modelId as VertexModelId - } - - if (modelId in legacyVertexModels) { - if (modelId.startsWith("gemini-2.5-pro-preview-")) { - return "gemini-2.5-pro" - } - - if (modelId.startsWith("gemini-1.5-pro-")) { - return "gemini-2.0-flash-001" - } - - if (modelId.startsWith("gemini-1.5-flash-")) { - return "gemini-2.0-flash-001" - } - - if (modelId.startsWith("gemini-2.5-pro-exp-")) { - return "gemini-2.5-pro" - } - - if (modelId === "gemini-2.0-pro-exp-02-05") { - return "gemini-2.5-pro" - } - - if ( - modelId === "gemini-2.0-flash-thinking-exp-1219" || - modelId === "gemini-2.0-flash-thinking-exp-01-21" || - modelId === "gemini-2.5-flash-preview-04-17" || - modelId === "gemini-2.5-flash-preview-04-17:thinking" - ) { - return "gemini-2.5-flash-preview-05-20" - } - } - - return vertexDefaultModelId - } - override getModel() { const modelId = this.options.apiModelId - let id = modelId ? this.mapLegacyVertexModel(modelId) : vertexDefaultModelId + let id = modelId ? mapLegacyVertexModel(modelId) : vertexDefaultModelId if (modelId && id && modelId !== id) { this.options.apiModelId = id diff --git a/webview-ui/src/components/ui/hooks/useSelectedModel.ts b/webview-ui/src/components/ui/hooks/useSelectedModel.ts index 5578662111..7c4273f396 100644 --- a/webview-ui/src/components/ui/hooks/useSelectedModel.ts +++ b/webview-ui/src/components/ui/hooks/useSelectedModel.ts @@ -12,7 +12,6 @@ import { moonshotModels, geminiDefaultModelId, geminiModels, - legacyGeminiModels, mistralDefaultModelId, mistralModels, openAiModelInfoSaneDefaults, @@ -20,7 +19,6 @@ import { openAiNativeModels, vertexDefaultModelId, vertexModels, - legacyVertexModels, xaiDefaultModelId, xaiModels, groqModels, @@ -40,6 +38,8 @@ import { sambaNovaDefaultModelId, doubaoModels, doubaoDefaultModelId, + mapLegacyGeminiModel, + mapLegacyVertexModel, } from "@roo-code/types" import type { ModelRecord, RouterModels } from "@roo/api" @@ -48,98 +48,6 @@ import { useRouterModels } from "./useRouterModels" import { useOpenRouterModelProviders } from "./useOpenRouterModelProviders" import { useLmStudioModels } from "./useLmStudioModels" -/** - * Maps legacy Gemini model IDs to current supported models - */ -function mapLegacyGeminiModel(modelId: string): string { - if (modelId in geminiModels) { - return modelId - } - - if (modelId in legacyGeminiModels) { - if (modelId.startsWith("gemini-2.5-pro-preview-")) { - return "gemini-2.5-pro" - } - - if (modelId.startsWith("gemini-1.5-pro-")) { - return geminiDefaultModelId - } - - if (modelId.startsWith("gemini-1.5-flash-")) { - return geminiDefaultModelId - } - - if (modelId.startsWith("gemini-2.5-pro-exp-")) { - return "gemini-2.5-pro" - } - - if (modelId === "gemini-exp-1206") { - return geminiDefaultModelId - } - - if (modelId === "gemini-2.0-pro-exp-02-05") { - return "gemini-2.5-pro" - } - - if ( - modelId === "gemini-2.0-flash-thinking-exp-1219" || - modelId === "gemini-2.0-flash-thinking-exp-01-21" || - modelId === "gemini-2.5-flash-preview-04-17" || - modelId === "gemini-2.5-flash-preview-04-17:thinking" - ) { - return "gemini-2.5-flash-preview-05-20" - } - - if (modelId === "gemini-2.0-flash-exp") { - return geminiDefaultModelId - } - } - - return geminiDefaultModelId -} - -/** - * Maps legacy Vertex model IDs to current supported models - */ -function mapLegacyVertexModel(modelId: string): string { - if (modelId in vertexModels) { - return modelId - } - - if (modelId in legacyVertexModels) { - if (modelId.startsWith("gemini-2.5-pro-preview-")) { - return "gemini-2.5-pro" - } - - if (modelId.startsWith("gemini-1.5-pro-")) { - return "gemini-2.0-flash-001" - } - - if (modelId.startsWith("gemini-1.5-flash-")) { - return "gemini-2.0-flash-001" - } - - if (modelId.startsWith("gemini-2.5-pro-exp-")) { - return "gemini-2.5-pro" - } - - if (modelId === "gemini-2.0-pro-exp-02-05") { - return "gemini-2.5-pro" - } - - if ( - modelId === "gemini-2.0-flash-thinking-exp-1219" || - modelId === "gemini-2.0-flash-thinking-exp-01-21" || - modelId === "gemini-2.5-flash-preview-04-17" || - modelId === "gemini-2.5-flash-preview-04-17:thinking" - ) { - return "gemini-2.5-flash-preview-05-20" - } - } - - return vertexDefaultModelId -} - export const useSelectedModel = (apiConfiguration?: ProviderSettings) => { const provider = apiConfiguration?.apiProvider || "anthropic" const openRouterModelId = provider === "openrouter" ? apiConfiguration?.openRouterModelId : undefined From ffb42ca2c60efefd9dff2e784a567103a6b8720e Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Fri, 1 Aug 2025 01:07:21 +0100 Subject: [PATCH 12/13] fix: preventing dirty state for Gemini naming convention migration --- webview-ui/src/components/settings/SettingsView.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 630b59485d..107adfac68 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -26,6 +26,7 @@ import { } from "lucide-react" import type { ProviderSettings, ExperimentId } from "@roo-code/types" +import { mapLegacyGeminiModel, mapLegacyVertexModel } from "@roo-code/types" import { TelemetrySetting } from "@roo/TelemetrySetting" @@ -231,7 +232,17 @@ const SettingsView = forwardRef(({ onDone, t // This prevents the dirty state when the component initializes and auto-syncs the model ID const isInitialSync = previousValue === undefined && value !== undefined - if (!isInitialSync) { + let isLegacyGeminiMapping = false + if (field === "apiModelId" && typeof previousValue === "string" && typeof value === "string") { + const provider = prevState.apiConfiguration?.apiProvider + if (provider === "gemini") { + isLegacyGeminiMapping = mapLegacyGeminiModel(previousValue) === value + } else if (provider === "vertex") { + isLegacyGeminiMapping = mapLegacyVertexModel(previousValue) === value + } + } + + if (!isInitialSync && !isLegacyGeminiMapping) { setChangeDetected(true) } return { ...prevState, apiConfiguration: { ...prevState.apiConfiguration, [field]: value } } From 2d11ee3205dbd56c8477e7cec0d6bd0d5f392bb1 Mon Sep 17 00:00:00 2001 From: "Ton Hoang Nguyen (Bill)" <32552798+HahaBill@users.noreply.github.com> Date: Fri, 1 Aug 2025 11:54:29 +0100 Subject: [PATCH 13/13] fix: persistently storing new Gemini migrations --- .../src/components/settings/SettingsView.tsx | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 107adfac68..f47f176f08 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -219,6 +219,38 @@ const SettingsView = forwardRef(({ onDone, t }) }, []) + const handleLegacyGeminiModelMapping = useCallback( + ( + field: K, + previousValue: ProviderSettings[K], + value: ProviderSettings[K], + provider: string | undefined, + updatedApiConfiguration: ProviderSettings, + ): boolean => { + if (field !== "apiModelId" || typeof previousValue !== "string" || typeof value !== "string") { + return false + } + + let isLegacyMapping = false + if (provider === "gemini") { + isLegacyMapping = mapLegacyGeminiModel(previousValue) === value + } else if (provider === "vertex") { + isLegacyMapping = mapLegacyVertexModel(previousValue) === value + } + + if (isLegacyMapping && currentApiConfigName) { + vscode.postMessage({ + type: "upsertApiConfiguration", + text: currentApiConfigName, + apiConfiguration: updatedApiConfiguration, + }) + } + + return isLegacyMapping + }, + [currentApiConfigName], + ) + const setApiConfigurationField = useCallback( (field: K, value: ProviderSettings[K]) => { setCachedState((prevState) => { @@ -232,23 +264,24 @@ const SettingsView = forwardRef(({ onDone, t // This prevents the dirty state when the component initializes and auto-syncs the model ID const isInitialSync = previousValue === undefined && value !== undefined - let isLegacyGeminiMapping = false - if (field === "apiModelId" && typeof previousValue === "string" && typeof value === "string") { - const provider = prevState.apiConfiguration?.apiProvider - if (provider === "gemini") { - isLegacyGeminiMapping = mapLegacyGeminiModel(previousValue) === value - } else if (provider === "vertex") { - isLegacyGeminiMapping = mapLegacyVertexModel(previousValue) === value - } - } + const updatedApiConfiguration = { ...prevState.apiConfiguration, [field]: value } + + const isLegacyMapping = handleLegacyGeminiModelMapping( + field, + previousValue, + value, + prevState.apiConfiguration?.apiProvider, + updatedApiConfiguration, + ) - if (!isInitialSync && !isLegacyGeminiMapping) { + if (!isInitialSync && !isLegacyMapping) { setChangeDetected(true) } - return { ...prevState, apiConfiguration: { ...prevState.apiConfiguration, [field]: value } } + + return { ...prevState, apiConfiguration: updatedApiConfiguration } }) }, - [], + [handleLegacyGeminiModelMapping], ) const setExperimentEnabled: SetExperimentEnabled = useCallback((id: ExperimentId, enabled: boolean) => {