Skip to content

Commit 9d7cff3

Browse files
fix(gemini): honor custom model ids instead of falling back to default (#227)
Selecting a custom Gemini model id not present in geminiModels (e.g. gemini-3.5-flash) silently invoked geminiDefaultModelId. The settings ModelPicker exposes a 'use custom model' option and useSelectedModel keeps the configured id, so the UI and the actual request disagreed. getModel() now honors a custom id when it looks like a Gemini model (gemini- prefix), using the default model's structural info as a baseline with pricing/tiers dropped so cost shows as unknown rather than the default model's rates. Ids that don't look like Gemini still fall back to default.
1 parent 9d022d4 commit 9d7cff3

2 files changed

Lines changed: 47 additions & 2 deletions

File tree

src/api/providers/__tests__/gemini.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,24 @@ describe("GeminiHandler", () => {
174174
expect(modelInfo.id).toBe(geminiDefaultModelId) // Default model
175175
})
176176

177+
it("should honor a custom gemini model id not present in geminiModels (#227)", () => {
178+
const customHandler = new GeminiHandler({
179+
apiModelId: "gemini-3.5-flash",
180+
geminiApiKey: "test-key",
181+
})
182+
const modelInfo = customHandler.getModel()
183+
// The configured id must be invoked, not silently swapped for the default.
184+
expect(modelInfo.id).toBe("gemini-3.5-flash")
185+
expect(modelInfo.id).not.toBe(geminiDefaultModelId)
186+
// A baseline ModelInfo is provided so downstream params resolve.
187+
expect(modelInfo.info).toBeDefined()
188+
// Pricing is unknown for a custom model, so cost should not be reported
189+
// against the default model's rates.
190+
expect(modelInfo.info.inputPrice).toBeUndefined()
191+
expect(modelInfo.info.outputPrice).toBeUndefined()
192+
expect(modelInfo.info.tiers).toBeUndefined()
193+
})
194+
177195
it("should exclude apply_diff and include edit in tool preferences", () => {
178196
const modelInfo = handler.getModel()
179197
expect(modelInfo.info.excludedTools).toContain("apply_diff")

src/api/providers/gemini.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,8 +490,35 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
490490

491491
override getModel() {
492492
const modelId = this.options.apiModelId
493-
let id = modelId && modelId in geminiModels ? (modelId as GeminiModelId) : geminiDefaultModelId
494-
let info: ModelInfo = geminiModels[id]
493+
let id: string
494+
let info: ModelInfo
495+
496+
if (modelId && modelId in geminiModels) {
497+
id = modelId
498+
info = geminiModels[modelId as GeminiModelId]
499+
} else if (modelId && modelId.toLowerCase().startsWith("gemini-")) {
500+
// Honor a custom/unlisted Gemini model id (e.g. a newly released model
501+
// not yet in `geminiModels`) instead of silently falling back to the
502+
// default. This mirrors the settings UI's "use custom model" option and
503+
// the `useSelectedModel` hook, which both keep the configured id. Ids
504+
// that don't look like Gemini models still fall back below.
505+
id = modelId
506+
// Use the default model's structural info as a baseline, but drop the
507+
// pricing fields we can't verify for an unknown model so cost reporting
508+
// shows "unknown" (calculateCost returns undefined) instead of charging
509+
// the default model's rates against a different model.
510+
info = {
511+
...geminiModels[geminiDefaultModelId],
512+
inputPrice: undefined,
513+
outputPrice: undefined,
514+
cacheReadsPrice: undefined,
515+
cacheWritesPrice: undefined,
516+
tiers: undefined,
517+
}
518+
} else {
519+
id = geminiDefaultModelId
520+
info = geminiModels[geminiDefaultModelId]
521+
}
495522

496523
const params = getModelParams({
497524
format: "gemini",

0 commit comments

Comments
 (0)