From 85b54b33ba2dda8a6c01f6963dfe3c7b247fc158 Mon Sep 17 00:00:00 2001 From: dongqing Date: Mon, 10 Mar 2025 19:06:58 +0800 Subject: [PATCH 1/5] support custom base url for gemini in google AI studio --- src/api/providers/gemini.ts | 24 ++++++++++++----- src/core/webview/ClineProvider.ts | 6 +++++ src/shared/api.ts | 1 + .../src/components/settings/ApiOptions.tsx | 27 +++++++++++++++++++ 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index 0d7179320c9..6bab8cfa6dc 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -17,10 +17,15 @@ export class GeminiHandler implements ApiHandler, SingleCompletionHandler { } async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { - const model = this.client.getGenerativeModel({ - model: this.getModel().id, - systemInstruction: systemPrompt, - }) + const model = this.client.getGenerativeModel( + { + model: this.getModel().id, + systemInstruction: systemPrompt, + }, + { + baseUrl: this.options.googleGeminiBaseUrl || undefined, + }, + ) const result = await model.generateContentStream({ contents: messages.map(convertAnthropicMessageToGemini), generationConfig: { @@ -55,9 +60,14 @@ export class GeminiHandler implements ApiHandler, SingleCompletionHandler { async completePrompt(prompt: string): Promise { try { - const model = this.client.getGenerativeModel({ - model: this.getModel().id, - }) + const model = this.client.getGenerativeModel( + { + model: this.getModel().id, + }, + { + baseUrl: this.options.googleGeminiBaseUrl || undefined, + }, + ) const result = await model.generateContent({ contents: [{ role: "user", parts: [{ text: prompt }] }], diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index bb31be5dce3..aba77f70a4c 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -96,6 +96,7 @@ type GlobalStateKey = | "openRouterModelInfo" | "openRouterBaseUrl" | "openRouterUseMiddleOutTransform" + | "googleGeminiBaseUrl" | "allowedCommands" | "soundEnabled" | "soundVolume" @@ -1657,6 +1658,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { anthropicBaseUrl, anthropicThinking, geminiApiKey, + googleGeminiBaseUrl, openAiNativeApiKey, deepSeekApiKey, azureApiVersion, @@ -1704,6 +1706,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.updateGlobalState("lmStudioBaseUrl", lmStudioBaseUrl), this.updateGlobalState("anthropicBaseUrl", anthropicBaseUrl), this.updateGlobalState("anthropicThinking", anthropicThinking), + this.updateGlobalState("googleGeminiBaseUrl", googleGeminiBaseUrl), this.storeSecret("geminiApiKey", geminiApiKey), this.storeSecret("openAiNativeApiKey", openAiNativeApiKey), this.storeSecret("deepSeekApiKey", deepSeekApiKey), @@ -2516,6 +2519,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { anthropicBaseUrl, anthropicThinking, geminiApiKey, + googleGeminiBaseUrl, openAiNativeApiKey, deepSeekApiKey, mistralApiKey, @@ -2598,6 +2602,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("lmStudioBaseUrl") as Promise, this.getGlobalState("anthropicBaseUrl") as Promise, this.getGlobalState("anthropicThinking") as Promise, + this.getGlobalState("googleGeminiBaseUrl") as Promise, this.getSecret("geminiApiKey") as Promise, this.getSecret("openAiNativeApiKey") as Promise, this.getSecret("deepSeekApiKey") as Promise, @@ -2699,6 +2704,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { anthropicBaseUrl, anthropicThinking, geminiApiKey, + googleGeminiBaseUrl, openAiNativeApiKey, deepSeekApiKey, mistralApiKey, diff --git a/src/shared/api.ts b/src/shared/api.ts index cea760c7760..cfa3e153690 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -51,6 +51,7 @@ export interface ApiHandlerOptions { lmStudioModelId?: string lmStudioBaseUrl?: string geminiApiKey?: string + googleGeminiBaseUrl?: string openAiNativeApiKey?: string mistralApiKey?: string mistralCodestralUrl?: string // New option for Codestral URL diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 0b6a1186562..45dfcf1edac 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -71,6 +71,9 @@ const ApiOptions = ({ const [anthropicThinkingBudget, setAnthropicThinkingBudget] = useState(apiConfiguration?.anthropicThinking) const [azureApiVersionSelected, setAzureApiVersionSelected] = useState(!!apiConfiguration?.azureApiVersion) const [openRouterBaseUrlSelected, setOpenRouterBaseUrlSelected] = useState(!!apiConfiguration?.openRouterBaseUrl) + const [googleGeminiBaseUrlSelected, setGoogleGeminiBaseUrlSelected] = useState( + !!apiConfiguration?.googleGeminiBaseUrl, + ) const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false) const inputEventTransform = (event: E) => (event as { target: HTMLInputElement })?.target?.value as any @@ -574,6 +577,30 @@ const ApiOptions = ({ placeholder="Enter API Key..."> Gemini API Key + { + setGoogleGeminiBaseUrlSelected(checked) + if (!checked) { + handleInputChange("googleGeminiBaseUrl")({ + target: { + value: "", + }, + }) + } + }}> + Use custom base URL + + + {googleGeminiBaseUrlSelected && ( + + )}

Date: Tue, 11 Mar 2025 16:46:40 +0800 Subject: [PATCH 2/5] add config for gemini custom base url --- src/api/providers/gemini.ts | 1 - src/shared/api.ts | 1 + src/shared/globalState.ts | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api/providers/gemini.ts b/src/api/providers/gemini.ts index 12cd6903f35..98117e99a9d 100644 --- a/src/api/providers/gemini.ts +++ b/src/api/providers/gemini.ts @@ -28,7 +28,6 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl baseUrl: this.options.googleGeminiBaseUrl || undefined, }, ) - const result = await model.generateContentStream({ contents: messages.map(convertAnthropicMessageToGemini), generationConfig: { diff --git a/src/shared/api.ts b/src/shared/api.ts index c92bb322869..368ac710f97 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -116,6 +116,7 @@ export const API_CONFIG_KEYS: GlobalStateKey[] = [ "lmStudioBaseUrl", "lmStudioDraftModelId", "lmStudioSpeculativeDecodingEnabled", + "googleGeminiBaseUrl", "mistralCodestralUrl", "azureApiVersion", "openRouterUseMiddleOutTransform", diff --git a/src/shared/globalState.ts b/src/shared/globalState.ts index 40973b3350c..8dc79eb8953 100644 --- a/src/shared/globalState.ts +++ b/src/shared/globalState.ts @@ -59,6 +59,7 @@ export const GLOBAL_STATE_KEYS = [ "openRouterModelInfo", "openRouterBaseUrl", "openRouterUseMiddleOutTransform", + "googleGeminiBaseUrl", "allowedCommands", "soundEnabled", "soundVolume", From 9d0b824b90f581e200bed2adada18378c140cf4b Mon Sep 17 00:00:00 2001 From: dongqing Date: Tue, 11 Mar 2025 17:37:07 +0800 Subject: [PATCH 3/5] fix test error for wrong arguments after additional base url added --- src/api/providers/__tests__/gemini.test.ts | 24 +++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/api/providers/__tests__/gemini.test.ts b/src/api/providers/__tests__/gemini.test.ts index 1e536eaecfc..d12c261b790 100644 --- a/src/api/providers/__tests__/gemini.test.ts +++ b/src/api/providers/__tests__/gemini.test.ts @@ -101,10 +101,15 @@ describe("GeminiHandler", () => { }) // Verify the model configuration - expect(mockGetGenerativeModel).toHaveBeenCalledWith({ - model: "gemini-2.0-flash-thinking-exp-1219", - systemInstruction: systemPrompt, - }) + expect(mockGetGenerativeModel).toHaveBeenCalledWith( + { + model: "gemini-2.0-flash-thinking-exp-1219", + systemInstruction: systemPrompt, + }, + { + baseUrl: undefined, + }, + ) // Verify generation config expect(mockGenerateContentStream).toHaveBeenCalledWith( @@ -149,9 +154,14 @@ describe("GeminiHandler", () => { const result = await handler.completePrompt("Test prompt") expect(result).toBe("Test response") - expect(mockGetGenerativeModel).toHaveBeenCalledWith({ - model: "gemini-2.0-flash-thinking-exp-1219", - }) + expect(mockGetGenerativeModel).toHaveBeenCalledWith( + { + model: "gemini-2.0-flash-thinking-exp-1219", + }, + { + baseUrl: undefined, + }, + ) expect(mockGenerateContent).toHaveBeenCalledWith({ contents: [{ role: "user", parts: [{ text: "Test prompt" }] }], generationConfig: { From 973acf90348e3b52b5e448230c573bcf11de8e51 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 12 Mar 2025 12:13:42 -0400 Subject: [PATCH 4/5] Add googleGeminiBaseUrl to roo-code.d.ts --- src/exports/roo-code.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/exports/roo-code.d.ts b/src/exports/roo-code.d.ts index 4442cdcf34e..ed19fc59ea5 100644 --- a/src/exports/roo-code.d.ts +++ b/src/exports/roo-code.d.ts @@ -154,6 +154,7 @@ export type GlobalStateKey = | "openRouterModelInfo" | "openRouterBaseUrl" | "openRouterUseMiddleOutTransform" + | "googleGeminiBaseUrl" | "allowedCommands" | "soundEnabled" | "soundVolume" From 7ece065f334ef6a2194963230980d5e5ae82b5a3 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 12 Mar 2025 12:16:18 -0400 Subject: [PATCH 5/5] Indentation fix --- webview-ui/src/components/settings/ApiOptions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index b2e1c3e49e3..49756796427 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -649,7 +649,7 @@ const ApiOptions = ({ Get Gemini API Key )} -

+
{