From 8d5d4f89518188087473615f5dbaa13cb5cf0fe5 Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Tue, 27 May 2025 16:44:05 -0400 Subject: [PATCH 01/42] First cut on different UI Signed-off-by: Geoff Wilson --- packages/types/src/provider-settings.ts | 2 ++ scripts/find-missing-translations.js | 33 +++++++++++++++++++ src/api/index.ts | 33 +++++++++++++++++++ .../settings/providers/Anthropic.tsx | 18 ++++++++-- webview-ui/src/utils/validate.ts | 2 +- 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 7076361ea..8e6fe029a 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -73,6 +73,7 @@ const apiModelIdProviderModelSchema = baseProviderSettingsSchema.extend({ const anthropicSchema = apiModelIdProviderModelSchema.extend({ apiKey: z.string().optional(), + anthropicApiKeyUseEnvVar: z.boolean().optional(), anthropicBaseUrl: z.string().optional(), anthropicUseAuthToken: z.boolean().optional(), }) @@ -262,6 +263,7 @@ export const PROVIDER_SETTINGS_KEYS = keysOf()([ // Anthropic "apiModelId", "apiKey", + "anthropicApiKeyUseEnvVar", "anthropicBaseUrl", "anthropicUseAuthToken", // Glama diff --git a/scripts/find-missing-translations.js b/scripts/find-missing-translations.js index 9277d935b..f953f71a9 100755 --- a/scripts/find-missing-translations.js +++ b/scripts/find-missing-translations.js @@ -8,11 +8,14 @@ * --locale= Only check a specific locale (e.g. --locale=fr) * --file= Only check a specific file (e.g. --file=chat.json) * --area= Only check a specific area (core, webview, or both) + * --create-missing Creates missing keys in the other locales with the EN value * --help Show this help message */ +const { set } = require("@dotenvx/dotenvx") const fs = require("fs") const path = require("path") +let createMissing = false // Process command line arguments const args = process.argv.slice(2).reduce( @@ -30,6 +33,8 @@ const args = process.argv.slice(2).reduce( console.error(`Error: Invalid area '${acc.area}'. Must be 'core', 'webview', or 'both'.`) process.exit(1) } + } else if (arg.startsWith("--create-missing")) { + createMissing = true } return acc }, @@ -54,6 +59,7 @@ Options: 'core' = Backend (src/i18n/locales) 'webview' = Frontend UI (webview-ui/src/i18n/locales) 'both' = Check both areas (default) + --create-missing Creates missing keys in the other locales with the EN value --help Show this help message Output: @@ -105,6 +111,27 @@ function getValueAtPath(obj, path) { return current } +// Set value at a dotted path in an object +function setValueAtPath(obj, path, value) { + const parts = path.split(".") + let current = obj + + for (let i = 0; i < parts.length; i++) { + const part = parts[i] + + // If it's the last part, set the value + if (i === parts.length - 1) { + current[part] = value + } else { + // If the key doesn't exist or isn't an object, create an empty object + if (current[part] === undefined || typeof current[part] !== "object") { + current[part] = {} + } + current = current[part] + } + } +} + // Function to check translations for a specific area function checkAreaTranslations(area) { const LOCALES_DIR = LOCALES_DIRS[area] @@ -198,11 +225,17 @@ function checkAreaTranslations(area) { key, englishValue, }) + if (createMissing === true) { + setValueAtPath(localeContent, key, englishValue) // Set the missing key in the locale content + } } } if (missingKeys.length > 0) { missingTranslations[locale][name] = missingKeys + if (createMissing === true) { + fs.writeFileSync(localeFilePath, JSON.stringify(localeContent, null, "\t")) + } } } } diff --git a/src/api/index.ts b/src/api/index.ts index 8b09bf4cf..96ecfa1d0 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -58,15 +58,33 @@ export interface ApiHandler { countTokens(content: Array): Promise } + +/** + * Read an environment variable value, returning a default value if not set. If neither key nor default is set, + * returns undefined + * @param key The environment variable key + * @param defaultValue The default value to return if the variable is not set + * @returns The value of the environment variable or the default value + */ +export function getEnvVar(key: string | undefined, defaultValue?: string | undefined): string | undefined { + if (key === undefined) { + return defaultValue + } + return process.env[key as string] ?? defaultValue +} + export function buildApiHandler(configuration: ProviderSettings): ApiHandler { const { apiProvider, ...options } = configuration switch (apiProvider) { case "anthropic": + options.apiKey = getEnvVar("ANTHROPIC_API_KEY", options.apiKey) return new AnthropicHandler(options) case "glama": + options.glamaApiKey = getEnvVar("GLAMA_API_KEY", options.glamaApiKey) return new GlamaHandler(options) case "openrouter": + options.openRouterApiKey = getEnvVar("OPEN_ROUTER_API_KEY", options.openRouterApiKey) return new OpenRouterHandler(options) case "bedrock": return new AwsBedrockHandler(options) @@ -75,38 +93,53 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { ? new AnthropicVertexHandler(options) : new VertexHandler(options) case "openai": + options.openAiApiKey = getEnvVar("OPEN_AI_API_KEY", options.openAiApiKey) return new OpenAiHandler(options) case "ollama": return new OllamaHandler(options) case "lmstudio": return new LmStudioHandler(options) case "gemini": + options.geminiApiKey = getEnvVar("GEMINI_API_KEY", options.geminiApiKey) return new GeminiHandler(options) case "openai-native": + options.openAiNativeApiKey = getEnvVar( + "OPEN_AI_NATIVE_API_KEY", + options.openAiNativeApiKey + ) return new OpenAiNativeHandler(options) case "deepseek": + options.deepSeekApiKey = getEnvVar("DEEP_SEEK_API_KEY", options.deepSeekApiKey) return new DeepSeekHandler(options) case "vscode-lm": return new VsCodeLmHandler(options) case "mistral": + options.mistralApiKey = getEnvVar("MISTRAL_API_KEY", options.mistralApiKey) return new MistralHandler(options) case "unbound": + options.unboundApiKey = getEnvVar("UNBOUND_API_KEY", options.unboundApiKey) return new UnboundHandler(options) case "requesty": + options.requestyApiKey = getEnvVar("REQUESTY_API_KEY", options.requestyApiKey) return new RequestyHandler(options) case "human-relay": return new HumanRelayHandler() case "fake-ai": return new FakeAIHandler(options) case "xai": + options.xaiApiKey = getEnvVar("XAI_API_KEY", options.xaiApiKey) return new XAIHandler(options) case "groq": + options.groqApiKey = getEnvVar("GROQ_API_KEY", options.groqApiKey) return new GroqHandler(options) case "chutes": + options.chutesApiKey = getEnvVar("CHUTES_API_KEY", options.chutesApiKey) return new ChutesHandler(options) case "litellm": + options.litellmApiKey = getEnvVar("LITELLM_API_KEY", options.litellmApiKey) return new LiteLLMHandler(options) default: + options.apiKey = getEnvVar("ANTROPIC_API_KEY", options.apiKey) return new AnthropicHandler(options) } } diff --git a/webview-ui/src/components/settings/providers/Anthropic.tsx b/webview-ui/src/components/settings/providers/Anthropic.tsx index f340e73f7..c1ad9b9e9 100644 --- a/webview-ui/src/components/settings/providers/Anthropic.tsx +++ b/webview-ui/src/components/settings/providers/Anthropic.tsx @@ -18,6 +18,7 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro const { t } = useAppTranslation() const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl) + const [useApiKeyEnvVar, setUseApiKeyEnvVar] = useState(!!apiConfiguration?.anthropicApiKeyUseEnvVar) const handleInputChange = useCallback( ( @@ -30,6 +31,11 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro [setApiConfigurationField], ) + const handleUseApiKeyEnvVarChange = (checked: boolean) => { + setUseApiKeyEnvVar(checked) + setApiConfigurationField("anthropicApiKeyUseEnvVar", checked) + } + return ( <> + className="w-full" + disabled={useApiKeyEnvVar} + >
{t("settings:providers.apiKeyStorageNotice")}
- {!apiConfiguration?.apiKey && ( + + {t("settings:providers.apiKeyUseEnvVar", { name: "ANTHROPIC_API_KEY"})} + + {(!(apiConfiguration?.apiKey || apiConfiguration?.anthropicApiKeyUseEnvVar)) && ( {t("settings:providers.getAnthropicApiKey")} diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index 69b7590c0..8e973c745 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -32,7 +32,7 @@ export function validateApiConfiguration(apiConfiguration: ProviderSettings): st } break case "anthropic": - if (!apiConfiguration.apiKey) { + if (!(apiConfiguration.apiKey || apiConfiguration.anthropicApiKeyUseEnvVar)) { return i18next.t("settings:validation.apiKey") } break From 9391edeee8f71fd896d8573adfc2106de50514af Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Wed, 28 May 2025 16:18:49 -0400 Subject: [PATCH 02/42] WIP for passing process environment to webview components Signed-off-by: Geoff Wilson --- src/core/webview/ClineProvider.ts | 6 ++++++ webview-ui/src/components/settings/ApiOptions.tsx | 7 ++++++- .../src/components/settings/SettingsView.tsx | 5 +++++ .../settings/__tests__/ApiOptions.test.tsx | 1 + .../components/settings/providers/Anthropic.tsx | 15 ++++++++++----- webview-ui/src/i18n/locales/en/settings.json | 1 + 6 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 13121531a..5e487311d 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -395,6 +395,12 @@ export class ClineProvider // and executes code based on the message that is recieved this.setWebviewMessageListener(webviewView.webview) + // Send process.env to the webview + webviewView.webview.postMessage({ + type: "env", + env: process.env, + }); + // Subscribe to code index status updates if the manager exists if (this.codeIndexManager) { this.codeIndexStatusSubscription = this.codeIndexManager.onProgressUpdate((update: IndexProgressUpdate) => { diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index bef4e45f2..6ef80a755 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -60,6 +60,7 @@ export interface ApiOptionsProps { fromWelcomeView?: boolean errorMessage: string | undefined setErrorMessage: React.Dispatch> + env?: Record } const ApiOptions = ({ @@ -69,6 +70,7 @@ const ApiOptions = ({ fromWelcomeView, errorMessage, setErrorMessage, + env = {} }: ApiOptionsProps) => { const { t } = useAppTranslation() @@ -327,7 +329,10 @@ const ApiOptions = ({ )} {selectedProvider === "anthropic" && ( - + )} {selectedProvider === "openai-native" && ( diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 824363246..40e37e9de 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -114,6 +114,7 @@ const SettingsView = forwardRef(({ onDone, t const confirmDialogHandler = useRef<() => void>() const [cachedState, setCachedState] = useState(extensionState) + const [env, setEnv] = useState>({}) const { alwaysAllowReadOnly, @@ -407,6 +408,9 @@ const SettingsView = forwardRef(({ onDone, t if (message.type === "action" && message.action === "didBecomeVisible") { scrollToActiveTab() } + if (message.type === "env") { + setEnv(message.env) + } } window.addEventListener("message", handleMessage) @@ -551,6 +555,7 @@ const SettingsView = forwardRef(({ onDone, t setApiConfigurationField={setApiConfigurationField} errorMessage={errorMessage} setErrorMessage={setErrorMessage} + env={env} /> diff --git a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx b/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx index ca0eb5f7f..1e0d7b4c1 100644 --- a/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx +++ b/webview-ui/src/components/settings/__tests__/ApiOptions.test.tsx @@ -225,6 +225,7 @@ const renderApiOptions = (props: Partial = {}) => { uriScheme={undefined} apiConfiguration={{}} setApiConfigurationField={() => {}} + env={{}} // Mock environment variables {...props} /> diff --git a/webview-ui/src/components/settings/providers/Anthropic.tsx b/webview-ui/src/components/settings/providers/Anthropic.tsx index c1ad9b9e9..4895f1ffe 100644 --- a/webview-ui/src/components/settings/providers/Anthropic.tsx +++ b/webview-ui/src/components/settings/providers/Anthropic.tsx @@ -12,11 +12,13 @@ import { inputEventTransform, noTransform } from "../transforms" type AnthropicProps = { apiConfiguration: ProviderSettings setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void + env?: Record } -export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: AnthropicProps) => { +export const Anthropic = ({ apiConfiguration, setApiConfigurationField, env = {}}: AnthropicProps) => { const { t } = useAppTranslation() - + + const apiKeyEnvVarExists = !!env["ANTHROPIC_API_KEY"] const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl) const [useApiKeyEnvVar, setUseApiKeyEnvVar] = useState(!!apiConfiguration?.anthropicApiKeyUseEnvVar) @@ -36,11 +38,13 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro setApiConfigurationField("anthropicApiKeyUseEnvVar", checked) } + console.log("Environment from ApiOptions at Anthropic:", env) + return ( <> + className="mb-2" + disabled={!apiKeyEnvVarExists}> {t("settings:providers.apiKeyUseEnvVar", { name: "ANTHROPIC_API_KEY"})} {(!(apiConfiguration?.apiKey || apiConfiguration?.anthropicApiKeyUseEnvVar)) && ( diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index d77e2b308..2cde6bfa5 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -135,6 +135,7 @@ "openRouterApiKey": "OpenRouter API Key", "getOpenRouterApiKey": "Get OpenRouter API Key", "apiKeyStorageNotice": "API keys are stored securely in VSCode's Secret Storage", + "apiKeyUseEnvVar": "Get API key from env var {{name}}", "glamaApiKey": "Glama API Key", "getGlamaApiKey": "Get Glama API Key", "useCustomBaseUrl": "Use custom base URL", From fa0504fe687c248a88bf6978ec63b231944b74cc Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Fri, 30 May 2025 16:44:35 -0400 Subject: [PATCH 03/42] Set process env in react html as script, instead of trying to pass message Signed-off-by: Geoff Wilson --- src/api/index.ts | 8 ++++++-- src/core/webview/ClineProvider.ts | 7 +------ .../src/components/settings/ApiOptions.tsx | 5 +---- .../src/components/settings/SettingsView.tsx | 3 --- .../settings/providers/Anthropic.tsx | 19 ++++++++----------- webview-ui/src/i18n/locales/en/settings.json | 2 +- 6 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/api/index.ts b/src/api/index.ts index 96ecfa1d0..0d74142c4 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -78,7 +78,9 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { switch (apiProvider) { case "anthropic": - options.apiKey = getEnvVar("ANTHROPIC_API_KEY", options.apiKey) + if (options.anthropicApiKeyUseEnvVar) { + options.apiKey = getEnvVar("ANTHROPIC_API_KEY", options.apiKey) + } return new AnthropicHandler(options) case "glama": options.glamaApiKey = getEnvVar("GLAMA_API_KEY", options.glamaApiKey) @@ -93,7 +95,9 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { ? new AnthropicVertexHandler(options) : new VertexHandler(options) case "openai": - options.openAiApiKey = getEnvVar("OPEN_AI_API_KEY", options.openAiApiKey) + if (options.openAiApiKeyUseEnvVar) { + options.openAiApiKey = getEnvVar("OPEN_AI_API_KEY", options.openAiApiKey) + } return new OpenAiHandler(options) case "ollama": return new OllamaHandler(options) diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 5e487311d..a9e552677 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -395,12 +395,6 @@ export class ClineProvider // and executes code based on the message that is recieved this.setWebviewMessageListener(webviewView.webview) - // Send process.env to the webview - webviewView.webview.postMessage({ - type: "env", - env: process.env, - }); - // Subscribe to code index status updates if the manager exists if (this.codeIndexManager) { this.codeIndexStatusSubscription = this.codeIndexManager.onProgressUpdate((update: IndexProgressUpdate) => { @@ -720,6 +714,7 @@ export class ClineProvider window.IMAGES_BASE_URI = "${imagesUri}" window.AUDIO_BASE_URI = "${audioUri}" window.MATERIAL_ICONS_BASE_URI = "${materialIconsUri}" + window.PROCESS_ENV = ${JSON.stringify(process.env)} Roo Code diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 6ef80a755..23e403a12 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -329,10 +329,7 @@ const ApiOptions = ({ )} {selectedProvider === "anthropic" && ( - + )} {selectedProvider === "openai-native" && ( diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 40e37e9de..7c6dc6823 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -408,9 +408,6 @@ const SettingsView = forwardRef(({ onDone, t if (message.type === "action" && message.action === "didBecomeVisible") { scrollToActiveTab() } - if (message.type === "env") { - setEnv(message.env) - } } window.addEventListener("message", handleMessage) diff --git a/webview-ui/src/components/settings/providers/Anthropic.tsx b/webview-ui/src/components/settings/providers/Anthropic.tsx index 4895f1ffe..750e81fd6 100644 --- a/webview-ui/src/components/settings/providers/Anthropic.tsx +++ b/webview-ui/src/components/settings/providers/Anthropic.tsx @@ -12,15 +12,11 @@ import { inputEventTransform, noTransform } from "../transforms" type AnthropicProps = { apiConfiguration: ProviderSettings setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void - env?: Record } -export const Anthropic = ({ apiConfiguration, setApiConfigurationField, env = {}}: AnthropicProps) => { +export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: AnthropicProps) => { const { t } = useAppTranslation() - - const apiKeyEnvVarExists = !!env["ANTHROPIC_API_KEY"] const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl) - const [useApiKeyEnvVar, setUseApiKeyEnvVar] = useState(!!apiConfiguration?.anthropicApiKeyUseEnvVar) const handleInputChange = useCallback( ( @@ -33,23 +29,24 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField, env = {} [setApiConfigurationField], ) + const env = (window as any).PROCESS_ENV || {} + const apiKeyEnvVarExists = !!env["ANTHROPIC_API_KEY"] + const [useApiKeyEnvVar, setUseApiKeyEnvVar] = useState(!!apiConfiguration?.anthropicApiKeyUseEnvVar) + const handleUseApiKeyEnvVarChange = (checked: boolean) => { setUseApiKeyEnvVar(checked) setApiConfigurationField("anthropicApiKeyUseEnvVar", checked) } - console.log("Environment from ApiOptions at Anthropic:", env) - return ( <> + disabled={useApiKeyEnvVar}>
diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 2cde6bfa5..d219e3f19 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -135,7 +135,7 @@ "openRouterApiKey": "OpenRouter API Key", "getOpenRouterApiKey": "Get OpenRouter API Key", "apiKeyStorageNotice": "API keys are stored securely in VSCode's Secret Storage", - "apiKeyUseEnvVar": "Get API key from env var {{name}}", + "apiKeyUseEnvVar": "Use API key from env var {{name}}", "glamaApiKey": "Glama API Key", "getGlamaApiKey": "Get Glama API Key", "useCustomBaseUrl": "Use custom base URL", From 39a1edbbe0ee0e092457f1f86915119b43cab74b Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Fri, 30 May 2025 22:19:20 -0400 Subject: [PATCH 04/42] Fix logic for activating apiKeyEnvVar Signed-off-by: Geoff Wilson --- webview-ui/src/components/settings/providers/Anthropic.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/settings/providers/Anthropic.tsx b/webview-ui/src/components/settings/providers/Anthropic.tsx index 750e81fd6..b3bb1c862 100644 --- a/webview-ui/src/components/settings/providers/Anthropic.tsx +++ b/webview-ui/src/components/settings/providers/Anthropic.tsx @@ -31,7 +31,7 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro const env = (window as any).PROCESS_ENV || {} const apiKeyEnvVarExists = !!env["ANTHROPIC_API_KEY"] - const [useApiKeyEnvVar, setUseApiKeyEnvVar] = useState(!!apiConfiguration?.anthropicApiKeyUseEnvVar) + const [useApiKeyEnvVar, setUseApiKeyEnvVar] = useState(!!apiConfiguration?.anthropicApiKeyUseEnvVar && apiKeyEnvVarExists) const handleUseApiKeyEnvVarChange = (checked: boolean) => { setUseApiKeyEnvVar(checked) From 91725fd3652d62990980d2554805199414dff957 Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Mon, 2 Jun 2025 12:24:45 -0400 Subject: [PATCH 05/42] Add new ApiKey component and use with Anthropic Signed-off-by: Geoff Wilson --- packages/types/src/provider-settings.ts | 2 + webview-ui/src/components/settings/ApiKey.tsx | 68 +++++++++++++++++++ .../settings/providers/Anthropic.tsx | 44 +++--------- 3 files changed, 81 insertions(+), 33 deletions(-) create mode 100644 webview-ui/src/components/settings/ApiKey.tsx diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index 8e6fe029a..d50fa3f4c 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -113,6 +113,7 @@ const vertexSchema = apiModelIdProviderModelSchema.extend({ const openAiSchema = baseProviderSettingsSchema.extend({ openAiBaseUrl: z.string().optional(), openAiApiKey: z.string().optional(), + openAiApiKeyUseEnvVar: z.boolean().optional(), openAiLegacyFormat: z.boolean().optional(), openAiR1FormatEnabled: z.boolean().optional(), openAiModelId: z.string().optional(), @@ -293,6 +294,7 @@ export const PROVIDER_SETTINGS_KEYS = keysOf()([ // OpenAI "openAiBaseUrl", "openAiApiKey", + "openAiApiKeyUseEnvVar", "openAiLegacyFormat", "openAiR1FormatEnabled", "openAiModelId", diff --git a/webview-ui/src/components/settings/ApiKey.tsx b/webview-ui/src/components/settings/ApiKey.tsx new file mode 100644 index 000000000..9d684752e --- /dev/null +++ b/webview-ui/src/components/settings/ApiKey.tsx @@ -0,0 +1,68 @@ +import { useState } from "react" +import { Checkbox } from "vscrui" +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" +import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" +import { t } from "i18next" + +type ApiKeyProps = { + apiKey: string + apiKeyEnvVar: string + setApiKey: (value: string) => void + setApiKeyUseEnvVar: (value: boolean) => void + apiKeyLabel: string + apiKeyUseEnvVar: boolean + getApiKeyUrl?: string + getApiKeyLabel?: string + disabled?: boolean +} + +export const ApiKey = ({ + apiKey, + apiKeyEnvVar, + setApiKey, + setApiKeyUseEnvVar, + apiKeyLabel, + apiKeyUseEnvVar, + getApiKeyUrl, + getApiKeyLabel, + disabled = false, +}: ApiKeyProps) => { + + const env = (window as any).PROCESS_ENV || {} + const apiKeyEnvVarExists = !!env[apiKeyEnvVar] + const [useEnvVar, setUseEnvVar] = useState(apiKeyUseEnvVar && apiKeyEnvVarExists) + + const handleUseEnvVarChange = (checked: boolean) => { + setUseEnvVar(checked) + setApiKeyUseEnvVar(checked) + } + + return ( + <> + setApiKey((e.target as HTMLInputElement).value)} + placeholder={t("settings:placeholders.apiKey")} + className="w-full" + disabled={useEnvVar || disabled}> + + +
+ {t("settings:providers.apiKeyStorageNotice")} +
+ + {t("settings:providers.apiKeyUseEnvVar", { name: apiKeyEnvVar })} + + {!(apiKey || useEnvVar) && getApiKeyUrl && getApiKeyLabel && ( + + {getApiKeyLabel} + + )} + + ) +} \ No newline at end of file diff --git a/webview-ui/src/components/settings/providers/Anthropic.tsx b/webview-ui/src/components/settings/providers/Anthropic.tsx index b3bb1c862..6b9d8cd4e 100644 --- a/webview-ui/src/components/settings/providers/Anthropic.tsx +++ b/webview-ui/src/components/settings/providers/Anthropic.tsx @@ -8,6 +8,7 @@ import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" import { inputEventTransform, noTransform } from "../transforms" +import { ApiKey } from "../ApiKey" type AnthropicProps = { apiConfiguration: ProviderSettings @@ -29,41 +30,18 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro [setApiConfigurationField], ) - const env = (window as any).PROCESS_ENV || {} - const apiKeyEnvVarExists = !!env["ANTHROPIC_API_KEY"] - const [useApiKeyEnvVar, setUseApiKeyEnvVar] = useState(!!apiConfiguration?.anthropicApiKeyUseEnvVar && apiKeyEnvVarExists) - - const handleUseApiKeyEnvVarChange = (checked: boolean) => { - setUseApiKeyEnvVar(checked) - setApiConfigurationField("anthropicApiKeyUseEnvVar", checked) - } - return ( <> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- - {t("settings:providers.apiKeyUseEnvVar", { name: "ANTHROPIC_API_KEY"})} - - {(!(apiConfiguration?.apiKey || apiConfiguration?.anthropicApiKeyUseEnvVar)) && ( - - {t("settings:providers.getAnthropicApiKey")} - - )} + setApiConfigurationField("apiKey", value)} + setApiKeyUseEnvVar={(value: boolean) => setApiConfigurationField("anthropicApiKeyUseEnvVar", value)} + apiKeyLabel={t("settings:providers.anthropicApiKey")} + getApiKeyUrl="https://console.anthropic.com/settings/keys" + getApiKeyLabel={t("settings:providers.getAnthropicApiKey")} + />
Date: Fri, 18 Jul 2025 10:59:06 -0400 Subject: [PATCH 06/42] Add apiKeyUseEnvVar to all providers supporting api key Signed-off-by: Geoff Wilson --- packages/types/src/provider-settings.ts | 24 ++++++++ src/api/index.ts | 56 +++++++++++++------ .../components/settings/providers/Chutes.tsx | 28 ++++------ .../settings/providers/DeepSeek.tsx | 27 ++++----- .../components/settings/providers/Gemini.tsx | 27 ++++----- .../components/settings/providers/Glama.tsx | 27 ++++----- .../components/settings/providers/Groq.tsx | 27 ++++----- .../components/settings/providers/LiteLLM.tsx | 23 ++++---- .../components/settings/providers/Mistral.tsx | 27 ++++----- .../components/settings/providers/OpenAI.tsx | 27 ++++----- .../settings/providers/OpenAICompatible.tsx | 17 +++--- .../settings/providers/OpenRouter.tsx | 35 ++++-------- .../settings/providers/Requesty.tsx | 35 ++++-------- .../components/settings/providers/Unbound.tsx | 27 ++++----- .../src/components/settings/providers/XAI.tsx | 27 ++++----- 15 files changed, 203 insertions(+), 231 deletions(-) diff --git a/packages/types/src/provider-settings.ts b/packages/types/src/provider-settings.ts index d50fa3f4c..1e1d52245 100644 --- a/packages/types/src/provider-settings.ts +++ b/packages/types/src/provider-settings.ts @@ -81,10 +81,12 @@ const anthropicSchema = apiModelIdProviderModelSchema.extend({ const glamaSchema = baseProviderSettingsSchema.extend({ glamaModelId: z.string().optional(), glamaApiKey: z.string().optional(), + glamaApiKeyUseEnvVar: z.boolean().optional(), }) const openRouterSchema = baseProviderSettingsSchema.extend({ openRouterApiKey: z.string().optional(), + openRouterApiKeyUseEnvVar: z.boolean().optional(), openRouterModelId: z.string().optional(), openRouterBaseUrl: z.string().optional(), openRouterSpecificProvider: z.string().optional(), @@ -150,31 +152,37 @@ const lmStudioSchema = baseProviderSettingsSchema.extend({ const geminiSchema = apiModelIdProviderModelSchema.extend({ geminiApiKey: z.string().optional(), + geminiApiKeyUseEnvVar: z.boolean().optional(), googleGeminiBaseUrl: z.string().optional(), }) const openAiNativeSchema = apiModelIdProviderModelSchema.extend({ openAiNativeApiKey: z.string().optional(), + openAiNativeApiKeyUseEnvVar: z.boolean().optional(), openAiNativeBaseUrl: z.string().optional(), }) const mistralSchema = apiModelIdProviderModelSchema.extend({ mistralApiKey: z.string().optional(), + mistralApiKeyUseEnvVar: z.boolean().optional(), mistralCodestralUrl: z.string().optional(), }) const deepSeekSchema = apiModelIdProviderModelSchema.extend({ deepSeekBaseUrl: z.string().optional(), deepSeekApiKey: z.string().optional(), + deepSeekApiKeyUseEnvVar: z.boolean().optional(), }) const unboundSchema = baseProviderSettingsSchema.extend({ unboundApiKey: z.string().optional(), + unboundApiKeyUseEnvVar: z.boolean().optional(), unboundModelId: z.string().optional(), }) const requestySchema = baseProviderSettingsSchema.extend({ requestyApiKey: z.string().optional(), + requestyApiKeyUseEnvVar: z.boolean().optional(), requestyModelId: z.string().optional(), }) @@ -186,19 +194,23 @@ const fakeAiSchema = baseProviderSettingsSchema.extend({ const xaiSchema = apiModelIdProviderModelSchema.extend({ xaiApiKey: z.string().optional(), + xaiApiKeyUseEnvVar: z.boolean().optional(), }) const groqSchema = apiModelIdProviderModelSchema.extend({ groqApiKey: z.string().optional(), + groqApiKeyUseEnvVar: z.boolean().optional(), }) const chutesSchema = apiModelIdProviderModelSchema.extend({ chutesApiKey: z.string().optional(), + chutesApiKeyUseEnvVar: z.boolean().optional(), }) const litellmSchema = baseProviderSettingsSchema.extend({ litellmBaseUrl: z.string().optional(), litellmApiKey: z.string().optional(), + litellmApiKeyUseEnvVar: z.boolean().optional(), litellmModelId: z.string().optional(), }) @@ -270,8 +282,10 @@ export const PROVIDER_SETTINGS_KEYS = keysOf()([ // Glama "glamaModelId", "glamaApiKey", + "glamaApiKeyUseEnvVar", // OpenRouter "openRouterApiKey", + "openRouterApiKeyUseEnvVar", "openRouterModelId", "openRouterBaseUrl", "openRouterSpecificProvider", @@ -315,21 +329,27 @@ export const PROVIDER_SETTINGS_KEYS = keysOf()([ "lmStudioSpeculativeDecodingEnabled", // Gemini "geminiApiKey", + "geminiApiKeyUseEnvVar", "googleGeminiBaseUrl", // OpenAI Native "openAiNativeApiKey", + "openAiNativeApiKeyUseEnvVar", "openAiNativeBaseUrl", // Mistral "mistralApiKey", + "mistralApiKeyUseEnvVar", "mistralCodestralUrl", // DeepSeek "deepSeekBaseUrl", "deepSeekApiKey", + "deepSeekApiKeyUseEnvVar", // Unbound "unboundApiKey", + "unboundApiKeyUseEnvVar", "unboundModelId", // Requesty "requestyApiKey", + "requestyApiKeyUseEnvVar", "requestyModelId", // Code Index "codeIndexOpenAiKey", @@ -349,12 +369,16 @@ export const PROVIDER_SETTINGS_KEYS = keysOf()([ "fakeAi", // X.AI (Grok) "xaiApiKey", + "xaiApiKeyUseEnvVar", // Groq "groqApiKey", + "groqApiKeyUseEnvVar", // Chutes AI "chutesApiKey", + "chutesApiKeyUseEnvVar", // LiteLLM "litellmBaseUrl", "litellmApiKey", + "litellmApiKeyUseEnvVar", "litellmModelId", ]) diff --git a/src/api/index.ts b/src/api/index.ts index 0d74142c4..02d27435d 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -58,7 +58,6 @@ export interface ApiHandler { countTokens(content: Array): Promise } - /** * Read an environment variable value, returning a default value if not set. If neither key nor default is set, * returns undefined @@ -83,10 +82,14 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { } return new AnthropicHandler(options) case "glama": - options.glamaApiKey = getEnvVar("GLAMA_API_KEY", options.glamaApiKey) + if (options.glamaApiKeyUseEnvVar) { + options.glamaApiKey = getEnvVar("GLAMA_API_KEY", options.glamaApiKey) + } return new GlamaHandler(options) case "openrouter": - options.openRouterApiKey = getEnvVar("OPEN_ROUTER_API_KEY", options.openRouterApiKey) + if (options.openRouterApiKeyUseEnvVar) { + options.openRouterApiKey = getEnvVar("OPEN_ROUTER_API_KEY", options.openRouterApiKey) + } return new OpenRouterHandler(options) case "bedrock": return new AwsBedrockHandler(options) @@ -104,46 +107,65 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { case "lmstudio": return new LmStudioHandler(options) case "gemini": - options.geminiApiKey = getEnvVar("GEMINI_API_KEY", options.geminiApiKey) + if (options.geminiApiKeyUseEnvVar) { + options.geminiApiKey = getEnvVar("GEMINI_API_KEY", options.geminiApiKey) + } return new GeminiHandler(options) case "openai-native": - options.openAiNativeApiKey = getEnvVar( - "OPEN_AI_NATIVE_API_KEY", - options.openAiNativeApiKey - ) + if (options.openAiNativeApiKeyUseEnvVar) { + options.openAiNativeApiKey = getEnvVar("OPEN_AI_NATIVE_API_KEY", options.openAiNativeApiKey) + } return new OpenAiNativeHandler(options) case "deepseek": - options.deepSeekApiKey = getEnvVar("DEEP_SEEK_API_KEY", options.deepSeekApiKey) + if (options.deepSeekApiKeyUseEnvVar) { + options.deepSeekApiKey = getEnvVar("DEEP_SEEK_API_KEY", options.deepSeekApiKey) + } return new DeepSeekHandler(options) case "vscode-lm": return new VsCodeLmHandler(options) case "mistral": - options.mistralApiKey = getEnvVar("MISTRAL_API_KEY", options.mistralApiKey) + if (options.mistralApiKeyUseEnvVar) { + options.mistralApiKey = getEnvVar("MISTRAL_API_KEY", options.mistralApiKey) + } return new MistralHandler(options) case "unbound": - options.unboundApiKey = getEnvVar("UNBOUND_API_KEY", options.unboundApiKey) + if (options.unboundApiKeyUseEnvVar) { + options.unboundApiKey = getEnvVar("UNBOUND_API_KEY", options.unboundApiKey) + } return new UnboundHandler(options) case "requesty": - options.requestyApiKey = getEnvVar("REQUESTY_API_KEY", options.requestyApiKey) + if (options.requestyApiKeyUseEnvVar) { + options.requestyApiKey = getEnvVar("REQUESTY_API_KEY", options.requestyApiKey) + } return new RequestyHandler(options) case "human-relay": return new HumanRelayHandler() case "fake-ai": return new FakeAIHandler(options) case "xai": - options.xaiApiKey = getEnvVar("XAI_API_KEY", options.xaiApiKey) + if (options.xaiApiKeyUseEnvVar) { + options.xaiApiKey = getEnvVar("XAI_API_KEY", options.xaiApiKey) + } return new XAIHandler(options) case "groq": - options.groqApiKey = getEnvVar("GROQ_API_KEY", options.groqApiKey) + if (options.groqApiKeyUseEnvVar) { + options.groqApiKey = getEnvVar("GROQ_API_KEY", options.groqApiKey) + } return new GroqHandler(options) case "chutes": - options.chutesApiKey = getEnvVar("CHUTES_API_KEY", options.chutesApiKey) + if (options.chutesApiKeyUseEnvVar) { + options.chutesApiKey = getEnvVar("CHUTES_API_KEY", options.chutesApiKey) + } return new ChutesHandler(options) case "litellm": - options.litellmApiKey = getEnvVar("LITELLM_API_KEY", options.litellmApiKey) + if (options.litellmApiKeyUseEnvVar) { + options.litellmApiKey = getEnvVar("LITELLM_API_KEY", options.litellmApiKey) + } return new LiteLLMHandler(options) default: - options.apiKey = getEnvVar("ANTROPIC_API_KEY", options.apiKey) + if (options.anthropicApiKeyUseEnvVar) { + options.apiKey = getEnvVar("ANTROPIC_API_KEY", options.apiKey) + } return new AnthropicHandler(options) } } diff --git a/webview-ui/src/components/settings/providers/Chutes.tsx b/webview-ui/src/components/settings/providers/Chutes.tsx index c51479421..acffa7089 100644 --- a/webview-ui/src/components/settings/providers/Chutes.tsx +++ b/webview-ui/src/components/settings/providers/Chutes.tsx @@ -7,6 +7,7 @@ import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" import { inputEventTransform } from "../transforms" +import { ApiKey } from "../ApiKey" type ChutesProps = { apiConfiguration: ProviderSettings @@ -26,25 +27,18 @@ export const Chutes = ({ apiConfiguration, setApiConfigurationField }: ChutesPro }, [setApiConfigurationField], ) - return ( <> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.chutesApiKey && ( - - {t("settings:providers.getChutesApiKey")} - - )} + setApiConfigurationField("apiKey", value)} + setApiKeyUseEnvVar={(value: boolean) => setApiConfigurationField("chutesApiKeyUseEnvVar", value)} + apiKeyLabel={t("settings:providers.chutesApiKey")} + getApiKeyUrl="https://chutes.ai/app/api" + getApiKeyLabel={t("settings:providers.getchutesApiKey")} + /> ) } diff --git a/webview-ui/src/components/settings/providers/DeepSeek.tsx b/webview-ui/src/components/settings/providers/DeepSeek.tsx index 6f0ac2d92..fe76f91e8 100644 --- a/webview-ui/src/components/settings/providers/DeepSeek.tsx +++ b/webview-ui/src/components/settings/providers/DeepSeek.tsx @@ -7,6 +7,7 @@ import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" import { inputEventTransform } from "../transforms" +import { ApiKey } from "../ApiKey" type DeepSeekProps = { apiConfiguration: ProviderSettings @@ -29,22 +30,16 @@ export const DeepSeek = ({ apiConfiguration, setApiConfigurationField }: DeepSee return ( <> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.deepSeekApiKey && ( - - {t("settings:providers.getDeepSeekApiKey")} - - )} + setApiConfigurationField("deepSeekApiKey", value)} + setApiKeyUseEnvVar={(value: boolean) => setApiConfigurationField("deepSeekApiKeyUseEnvVar", value)} + apiKeyLabel={t("settings:providers.deepSeekApiKey")} + getApiKeyUrl="https://platform.deepseek.com/" + getApiKeyLabel={t("settings:providers.getDeepSeekApiKey")} + /> ) } diff --git a/webview-ui/src/components/settings/providers/Gemini.tsx b/webview-ui/src/components/settings/providers/Gemini.tsx index 21056f12d..88a254ad1 100644 --- a/webview-ui/src/components/settings/providers/Gemini.tsx +++ b/webview-ui/src/components/settings/providers/Gemini.tsx @@ -8,6 +8,7 @@ import { useAppTranslation } from "@src/i18n/TranslationContext" import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" import { inputEventTransform } from "../transforms" +import { ApiKey } from "../ApiKey" type GeminiProps = { apiConfiguration: ProviderSettings @@ -34,22 +35,16 @@ export const Gemini = ({ apiConfiguration, setApiConfigurationField }: GeminiPro return ( <> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.geminiApiKey && ( - - {t("settings:providers.getGeminiApiKey")} - - )} + setApiConfigurationField("geminiApiKey", value)} + setApiKeyUseEnvVar={(value: boolean) => setApiConfigurationField("geminiApiKeyUseEnvVar", value)} + apiKeyLabel={t("settings:providers.geminiApiKey")} + getApiKeyUrl="https://ai.google.dev/" + getApiKeyLabel={t("settings:providers.getGeminiApiKey")} + />
- - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.glamaApiKey && ( - - {t("settings:providers.getGlamaApiKey")} - - )} + setApiConfigurationField("glamaApiKey", value)} + setApiKeyUseEnvVar={(value: boolean) => setApiConfigurationField("glamaApiKeyUseEnvVar", value)} + apiKeyLabel={t("settings:providers.glamaApiKey")} + getApiKeyUrl={getGlamaAuthUrl(uriScheme)} + getApiKeyLabel={t("settings:providers.getGlamaApiKey")} + /> - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.groqApiKey && ( - - {t("settings:providers.getGroqApiKey")} - - )} + setApiConfigurationField("groqApiKey", value)} + setApiKeyUseEnvVar={(value: boolean) => setApiConfigurationField("groqApiKeyUseEnvVar", value)} + apiKeyLabel={t("settings:providers.groqApiKey")} + getApiKeyUrl="https://console.groq.com/keys" + getApiKeyLabel={t("settings:providers.getGroqApiKey")} + /> ) } diff --git a/webview-ui/src/components/settings/providers/LiteLLM.tsx b/webview-ui/src/components/settings/providers/LiteLLM.tsx index b9ea04e87..6c1e3fee6 100644 --- a/webview-ui/src/components/settings/providers/LiteLLM.tsx +++ b/webview-ui/src/components/settings/providers/LiteLLM.tsx @@ -13,6 +13,7 @@ import { Button } from "@src/components/ui" import { inputEventTransform } from "../transforms" import { ModelPicker } from "../ModelPicker" +import { ApiKey } from "../ApiKey" type LiteLLMProps = { apiConfiguration: ProviderSettings @@ -91,19 +92,15 @@ export const LiteLLM = ({ apiConfiguration, setApiConfigurationField }: LiteLLMP - - - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- + setApiConfigurationField("litellmApiKey", value)} + setApiKeyUseEnvVar={(value: boolean) => setApiConfigurationField("litellmApiKeyUseEnvVar", value)} + apiKeyLabel={t("settings:providers.litellmApiKey")} + getApiKeyLabel={t("settings:providers.getLitellmApiKey")} + />
-
- - - -
- {t("settings:providers.apiKeyStorageNotice")} -
- {!apiConfiguration?.zaiApiKey && ( - - {t("settings:providers.getZaiApiKey")} - - )} -
+ setApiConfigurationField("zaiApiKey", value)} + setConfigUseEnvVars={(value: boolean) => setApiConfigurationField("zaiConfigUseEnvVars", value)} + apiKeyLabel={t("settings:providers.zaiApiKey")} + getApiKeyUrl={ + zaiApiLineConfigs[apiConfiguration.zaiApiLine ?? "international_coding"].isChina + ? "https://open.bigmodel.cn/console/overview" + : "https://z.ai/manage-apikey/apikey-list" + } + getApiKeyLabel={t("settings:providers.getZaiApiKey")} + /> ) } From 47368944fb4ff9d1b2b2233db3e6d333d5ed20e7 Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Fri, 26 Sep 2025 16:30:32 -0400 Subject: [PATCH 40/42] Remove unused imports and defs since using ApiKey component Signed-off-by: Geoff Wilson --- .../components/settings/providers/Cerebras.tsx | 14 -------------- .../components/settings/providers/DeepInfra.tsx | 14 +------------- .../src/components/settings/providers/Doubao.tsx | 14 -------------- .../settings/providers/Featherless.tsx | 14 -------------- .../components/settings/providers/Fireworks.tsx | 14 -------------- .../settings/providers/HuggingFace.tsx | 13 ------------- .../settings/providers/IOIntelligence.tsx | 16 ---------------- .../components/settings/providers/Moonshot.tsx | 2 +- .../src/components/settings/providers/OpenAI.tsx | 1 - .../components/settings/providers/Requesty.tsx | 1 - .../components/settings/providers/SambaNova.tsx | 14 -------------- .../settings/providers/VercelAiGateway.tsx | 15 --------------- .../src/components/settings/providers/ZAi.tsx | 3 +-- 13 files changed, 3 insertions(+), 132 deletions(-) diff --git a/webview-ui/src/components/settings/providers/Cerebras.tsx b/webview-ui/src/components/settings/providers/Cerebras.tsx index c37d1d4f0..dd5b506ef 100644 --- a/webview-ui/src/components/settings/providers/Cerebras.tsx +++ b/webview-ui/src/components/settings/providers/Cerebras.tsx @@ -1,12 +1,8 @@ -import { useCallback } from "react" -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" - import type { ProviderSettings } from "@roo-code/types" import { API_KEYS } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { inputEventTransform } from "../transforms" import { ApiKey } from "../ApiKey" type CerebrasProps = { @@ -17,16 +13,6 @@ type CerebrasProps = { export const Cerebras = ({ apiConfiguration, setApiConfigurationField }: CerebrasProps) => { const { t } = useAppTranslation() - const handleInputChange = useCallback( - ( - field: K, - transform: (event: E) => ProviderSettings[K] = inputEventTransform, - ) => - (event: E | Event) => { - setApiConfigurationField(field, transform(event as E)) - }, - [setApiConfigurationField], - ) return ( <> diff --git a/webview-ui/src/components/settings/providers/DeepInfra.tsx b/webview-ui/src/components/settings/providers/DeepInfra.tsx index f95d1b6c4..fc27faa17 100644 --- a/webview-ui/src/components/settings/providers/DeepInfra.tsx +++ b/webview-ui/src/components/settings/providers/DeepInfra.tsx @@ -1,5 +1,4 @@ -import { useCallback, useEffect, useState } from "react" -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" +import { useEffect, useState } from "react" import { OrganizationAllowList, type ProviderSettings, deepInfraDefaultModelId, API_KEYS } from "@roo-code/types" @@ -9,7 +8,6 @@ import { vscode } from "@src/utils/vscode" import { useAppTranslation } from "@src/i18n/TranslationContext" import { Button } from "@src/components/ui" -import { inputEventTransform } from "../transforms" import { ModelPicker } from "../ModelPicker" import { ApiKey } from "../ApiKey" @@ -34,16 +32,6 @@ export const DeepInfra = ({ const [didRefetch, setDidRefetch] = useState() - const handleInputChange = useCallback( - ( - field: K, - transform: (event: E) => ProviderSettings[K] = inputEventTransform, - ) => - (event: E | Event) => { - setApiConfigurationField(field, transform(event as E)) - }, - [setApiConfigurationField], - ) useEffect(() => { // When base URL or API key changes, trigger a silent refresh of models diff --git a/webview-ui/src/components/settings/providers/Doubao.tsx b/webview-ui/src/components/settings/providers/Doubao.tsx index 3fd1dfd1d..6c6a0e612 100644 --- a/webview-ui/src/components/settings/providers/Doubao.tsx +++ b/webview-ui/src/components/settings/providers/Doubao.tsx @@ -1,12 +1,8 @@ -import { useCallback } from "react" -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" - import type { ProviderSettings } from "@roo-code/types" import { API_KEYS } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { inputEventTransform } from "../transforms" import { ApiKey } from "../ApiKey" type DoubaoProps = { @@ -17,16 +13,6 @@ type DoubaoProps = { export const Doubao = ({ apiConfiguration, setApiConfigurationField }: DoubaoProps) => { const { t } = useAppTranslation() - const handleInputChange = useCallback( - ( - field: K, - transform: (event: E) => ProviderSettings[K] = inputEventTransform, - ) => - (event: E | Event) => { - setApiConfigurationField(field, transform(event as E)) - }, - [setApiConfigurationField], - ) return ( <> diff --git a/webview-ui/src/components/settings/providers/Featherless.tsx b/webview-ui/src/components/settings/providers/Featherless.tsx index a3900d891..69ec17168 100644 --- a/webview-ui/src/components/settings/providers/Featherless.tsx +++ b/webview-ui/src/components/settings/providers/Featherless.tsx @@ -1,12 +1,8 @@ -import { useCallback } from "react" -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" - import type { ProviderSettings } from "@roo-code/types" import { API_KEYS } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { inputEventTransform } from "../transforms" import { ApiKey } from "../ApiKey" type FeatherlessProps = { @@ -17,16 +13,6 @@ type FeatherlessProps = { export const Featherless = ({ apiConfiguration, setApiConfigurationField }: FeatherlessProps) => { const { t } = useAppTranslation() - const handleInputChange = useCallback( - ( - field: K, - transform: (event: E) => ProviderSettings[K] = inputEventTransform, - ) => - (event: E | Event) => { - setApiConfigurationField(field, transform(event as E)) - }, - [setApiConfigurationField], - ) return ( <> diff --git a/webview-ui/src/components/settings/providers/Fireworks.tsx b/webview-ui/src/components/settings/providers/Fireworks.tsx index 08a884db0..c205e1f73 100644 --- a/webview-ui/src/components/settings/providers/Fireworks.tsx +++ b/webview-ui/src/components/settings/providers/Fireworks.tsx @@ -1,12 +1,8 @@ -import { useCallback } from "react" -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" - import type { ProviderSettings } from "@roo-code/types" import { API_KEYS } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { inputEventTransform } from "../transforms" import { ApiKey } from "../ApiKey" type FireworksProps = { @@ -17,16 +13,6 @@ type FireworksProps = { export const Fireworks = ({ apiConfiguration, setApiConfigurationField }: FireworksProps) => { const { t } = useAppTranslation() - const handleInputChange = useCallback( - ( - field: K, - transform: (event: E) => ProviderSettings[K] = inputEventTransform, - ) => - (event: E | Event) => { - setApiConfigurationField(field, transform(event as E)) - }, - [setApiConfigurationField], - ) return ( <> diff --git a/webview-ui/src/components/settings/providers/HuggingFace.tsx b/webview-ui/src/components/settings/providers/HuggingFace.tsx index 02d248c55..b93f5f6fc 100644 --- a/webview-ui/src/components/settings/providers/HuggingFace.tsx +++ b/webview-ui/src/components/settings/providers/HuggingFace.tsx @@ -1,6 +1,5 @@ import { useCallback, useState, useEffect, useMemo } from "react" import { useEvent } from "react-use" -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" import type { ProviderSettings } from "@roo-code/types" import { API_KEYS } from "@roo-code/types" @@ -8,12 +7,10 @@ import { API_KEYS } from "@roo-code/types" import { ExtensionMessage } from "@roo/ExtensionMessage" import { vscode } from "@src/utils/vscode" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" import { SearchableSelect, type SearchableSelectOption } from "@src/components/ui" import { cn } from "@src/lib/utils" import { formatPrice } from "@/utils/formatPrice" -import { inputEventTransform } from "../transforms" import { ApiKey } from "../ApiKey" type HuggingFaceModel = { @@ -51,16 +48,6 @@ export const HuggingFace = ({ apiConfiguration, setApiConfigurationField }: Hugg apiConfiguration?.huggingFaceInferenceProvider || "auto", ) - const handleInputChange = useCallback( - ( - field: K, - transform: (event: E) => ProviderSettings[K] = inputEventTransform, - ) => - (event: E | Event) => { - setApiConfigurationField(field, transform(event as E)) - }, - [setApiConfigurationField], - ) // Fetch models when component mounts. useEffect(() => { diff --git a/webview-ui/src/components/settings/providers/IOIntelligence.tsx b/webview-ui/src/components/settings/providers/IOIntelligence.tsx index 4914e6a8b..a7650684b 100644 --- a/webview-ui/src/components/settings/providers/IOIntelligence.tsx +++ b/webview-ui/src/components/settings/providers/IOIntelligence.tsx @@ -1,6 +1,3 @@ -import { useCallback } from "react" -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" - import { type ProviderSettings, type OrganizationAllowList, @@ -10,12 +7,9 @@ import { } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" import { useExtensionState } from "@src/context/ExtensionStateContext" import { ModelPicker } from "../ModelPicker" - -import { inputEventTransform } from "../transforms" import { ApiKey } from "../ApiKey" type IOIntelligenceProps = { @@ -34,16 +28,6 @@ export const IOIntelligence = ({ const { t } = useAppTranslation() const { routerModels } = useExtensionState() - const handleInputChange = useCallback( - ( - field: K, - transform: (event: E) => ProviderSettings[K] = inputEventTransform, - ) => - (event: E | Event) => { - setApiConfigurationField(field, transform(event as E)) - }, - [setApiConfigurationField], - ) return ( <> diff --git a/webview-ui/src/components/settings/providers/Moonshot.tsx b/webview-ui/src/components/settings/providers/Moonshot.tsx index ba718d8c1..52bbaf0d4 100644 --- a/webview-ui/src/components/settings/providers/Moonshot.tsx +++ b/webview-ui/src/components/settings/providers/Moonshot.tsx @@ -1,5 +1,5 @@ import { useCallback } from "react" -import { VSCodeTextField, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react" +import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react" import type { ProviderSettings } from "@roo-code/types" import { API_KEYS } from "@roo-code/types" diff --git a/webview-ui/src/components/settings/providers/OpenAI.tsx b/webview-ui/src/components/settings/providers/OpenAI.tsx index 85d74a0fc..6c679261a 100644 --- a/webview-ui/src/components/settings/providers/OpenAI.tsx +++ b/webview-ui/src/components/settings/providers/OpenAI.tsx @@ -6,7 +6,6 @@ import type { ModelInfo, ProviderSettings } from "@roo-code/types" import { API_KEYS } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StandardTooltip } from "@src/components/ui" import { inputEventTransform } from "../transforms" diff --git a/webview-ui/src/components/settings/providers/Requesty.tsx b/webview-ui/src/components/settings/providers/Requesty.tsx index 7d7611534..89c462168 100644 --- a/webview-ui/src/components/settings/providers/Requesty.tsx +++ b/webview-ui/src/components/settings/providers/Requesty.tsx @@ -37,7 +37,6 @@ export const Requesty = ({ }: RequestyProps) => { const { t } = useAppTranslation() - const [didRefetch, setDidRefetch] = useState() const [requestyEndpointSelected, setRequestyEndpointSelected] = useState(!!apiConfiguration.requestyBaseUrl) // This ensures that the "Use custom URL" checkbox is hidden when the user deletes the URL. diff --git a/webview-ui/src/components/settings/providers/SambaNova.tsx b/webview-ui/src/components/settings/providers/SambaNova.tsx index 8de84551f..cbb471e7e 100644 --- a/webview-ui/src/components/settings/providers/SambaNova.tsx +++ b/webview-ui/src/components/settings/providers/SambaNova.tsx @@ -1,12 +1,8 @@ -import { useCallback } from "react" -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" - import type { ProviderSettings } from "@roo-code/types" import { API_KEYS } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { inputEventTransform } from "../transforms" import { ApiKey } from "../ApiKey" type SambaNovaProps = { @@ -17,16 +13,6 @@ type SambaNovaProps = { export const SambaNova = ({ apiConfiguration, setApiConfigurationField }: SambaNovaProps) => { const { t } = useAppTranslation() - const handleInputChange = useCallback( - ( - field: K, - transform: (event: E) => ProviderSettings[K] = inputEventTransform, - ) => - (event: E | Event) => { - setApiConfigurationField(field, transform(event as E)) - }, - [setApiConfigurationField], - ) return ( <> diff --git a/webview-ui/src/components/settings/providers/VercelAiGateway.tsx b/webview-ui/src/components/settings/providers/VercelAiGateway.tsx index d21c1137c..5c95c0d64 100644 --- a/webview-ui/src/components/settings/providers/VercelAiGateway.tsx +++ b/webview-ui/src/components/settings/providers/VercelAiGateway.tsx @@ -1,14 +1,9 @@ -import { useCallback } from "react" -import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react" - import { type ProviderSettings, type OrganizationAllowList, vercelAiGatewayDefaultModelId, API_KEYS } from "@roo-code/types" import type { RouterModels } from "@roo/api" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" -import { inputEventTransform } from "../transforms" import { ModelPicker } from "../ModelPicker" import { ApiKey } from "../ApiKey" @@ -29,16 +24,6 @@ export const VercelAiGateway = ({ }: VercelAiGatewayProps) => { const { t } = useAppTranslation() - const handleInputChange = useCallback( - ( - field: K, - transform: (event: E) => ProviderSettings[K] = inputEventTransform, - ) => - (event: E | Event) => { - setApiConfigurationField(field, transform(event as E)) - }, - [setApiConfigurationField], - ) return ( <> diff --git a/webview-ui/src/components/settings/providers/ZAi.tsx b/webview-ui/src/components/settings/providers/ZAi.tsx index caf954642..a853c8a2e 100644 --- a/webview-ui/src/components/settings/providers/ZAi.tsx +++ b/webview-ui/src/components/settings/providers/ZAi.tsx @@ -1,10 +1,9 @@ import { useCallback } from "react" -import { VSCodeTextField, VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react" +import { VSCodeDropdown, VSCodeOption } from "@vscode/webview-ui-toolkit/react" import { zaiApiLineConfigs, zaiApiLineSchema, type ProviderSettings, API_KEYS } from "@roo-code/types" import { useAppTranslation } from "@src/i18n/TranslationContext" -import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink" import { inputEventTransform } from "../transforms" import { cn } from "@/lib/utils" From cc85602428f0e6cace9a20773b84b415727fb061 Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Fri, 26 Sep 2025 22:40:41 -0400 Subject: [PATCH 41/42] Use constants instead of hard-coded strings in buildApiHandler; fix vercel copy/pasta Signed-off-by: Geoff Wilson --- packages/types/src/constants.ts | 2 +- src/api/index.ts | 51 ++++++++++--------- .../settings/providers/VercelAiGateway.tsx | 2 +- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/packages/types/src/constants.ts b/packages/types/src/constants.ts index 48a856963..92fca1150 100644 --- a/packages/types/src/constants.ts +++ b/packages/types/src/constants.ts @@ -24,7 +24,7 @@ export const API_KEYS = { IO_INTELLIGENCE: 'IOINTELLIGENCE_API_KEY', MOONSHOOT: 'MOONSHOT_API_KEY', SAMBA_NOVA: 'SAMBANOVA_API_KEY', - VERCEL_NOVA: 'VERCEL_API_KEY', + VERCEL: 'VERCEL_API_KEY', ZAI: 'ZAI_API_KEY', } as const diff --git a/src/api/index.ts b/src/api/index.ts index 1533b1bac..29c499b9e 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -1,6 +1,7 @@ import { Anthropic } from "@anthropic-ai/sdk" import type { ProviderSettings, ModelInfo } from "@roo-code/types" +import { API_KEYS } from "@roo-code/types" import { ApiStream } from "./transform/stream" @@ -107,19 +108,19 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { switch (apiProvider) { case "anthropic": if (options.anthropicConfigUseEnvVars) { - options.apiKey = getEnvVar("ANTHROPIC_API_KEY", options.apiKey) + options.apiKey = getEnvVar(API_KEYS.ANTHROPIC, options.apiKey) } return new AnthropicHandler(options) case "claude-code": return new ClaudeCodeHandler(options) case "glama": if (options.glamaConfigUseEnvVars) { - options.glamaApiKey = getEnvVar("GLAMA_API_KEY", options.glamaApiKey) + options.glamaApiKey = getEnvVar(API_KEYS.GLAMA, options.glamaApiKey) } return new GlamaHandler(options) case "openrouter": if (options.openRouterConfigUseEnvVars) { - options.openRouterApiKey = getEnvVar("OPENROUTER_API_KEY", options.openRouterApiKey) + options.openRouterApiKey = getEnvVar(API_KEYS.OPEN_ROUTER, options.openRouterApiKey) } return new OpenRouterHandler(options) case "bedrock": @@ -130,7 +131,7 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { : new VertexHandler(options) case "openai": if (options.openAiConfigUseEnvVars) { - options.openAiApiKey = getEnvVar("OPENAI_API_KEY", options.openAiApiKey) + options.openAiApiKey = getEnvVar(API_KEYS.OPENAI, options.openAiApiKey) } return new OpenAiHandler(options) case "ollama": @@ -139,46 +140,46 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { return new LmStudioHandler(options) case "gemini": if (options.geminiConfigUseEnvVars) { - options.geminiApiKey = getEnvVar("GEMINI_API_KEY", options.geminiApiKey) + options.geminiApiKey = getEnvVar(API_KEYS.GEMINI, options.geminiApiKey) } return new GeminiHandler(options) case "openai-native": if (options.openAiNativeConfigUseEnvVars) { - options.openAiNativeApiKey = getEnvVar("OPENAI_API_KEY", options.openAiNativeApiKey) + options.openAiNativeApiKey = getEnvVar(API_KEYS.OPENAI, options.openAiNativeApiKey) } return new OpenAiNativeHandler(options) case "deepseek": if (options.deepSeekConfigUseEnvVars) { - options.deepSeekApiKey = getEnvVar("DEEPSEEK_API_KEY", options.deepSeekApiKey) + options.deepSeekApiKey = getEnvVar(API_KEYS.DEEP_SEEK, options.deepSeekApiKey) } return new DeepSeekHandler(options) case "doubao": if (options.doubaoConfigUseEnvVars) { - options.doubaoApiKey = getEnvVar("DOUBAO_API_KEY", options.doubaoApiKey) + options.doubaoApiKey = getEnvVar(API_KEYS.DOUBAO, options.doubaoApiKey) } return new DoubaoHandler(options) case "qwen-code": return new QwenCodeHandler(options) case "moonshot": if (options.moonshotConfigUseEnvVars) { - options.moonshotApiKey = getEnvVar("MOONSHOT_API_KEY", options.moonshotApiKey) + options.moonshotApiKey = getEnvVar(API_KEYS.MOONSHOOT, options.moonshotApiKey) } return new MoonshotHandler(options) case "vscode-lm": return new VsCodeLmHandler(options) case "mistral": if (options.mistralConfigUseEnvVars) { - options.mistralApiKey = getEnvVar("MISTRAL_API_KEY", options.mistralApiKey) + options.mistralApiKey = getEnvVar(API_KEYS.MISTRAL, options.mistralApiKey) } return new MistralHandler(options) case "unbound": if (options.unboundConfigUseEnvVars) { - options.unboundApiKey = getEnvVar("UNBOUND_API_KEY", options.unboundApiKey) + options.unboundApiKey = getEnvVar(API_KEYS.UNBOUND, options.unboundApiKey) } return new UnboundHandler(options) case "requesty": if (options.requestyConfigUseEnvVars) { - options.requestyApiKey = getEnvVar("REQUESTY_API_KEY", options.requestyApiKey) + options.requestyApiKey = getEnvVar(API_KEYS.REQUESTY, options.requestyApiKey) } return new RequestyHandler(options) case "human-relay": @@ -187,57 +188,57 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { return new FakeAIHandler(options) case "xai": if (options.xaiConfigUseEnvVars) { - options.xaiApiKey = getEnvVar("XAI_API_KEY", options.xaiApiKey) + options.xaiApiKey = getEnvVar(API_KEYS.XAI, options.xaiApiKey) } return new XAIHandler(options) case "groq": if (options.groqConfigUseEnvVars) { - options.groqApiKey = getEnvVar("GROQ_API_KEY", options.groqApiKey) + options.groqApiKey = getEnvVar(API_KEYS.GROQ, options.groqApiKey) } return new GroqHandler(options) case "deepinfra": if (options.deepInfraConfigUseEnvVars) { - options.deepInfraApiKey = getEnvVar("DEEPINFRA_API_KEY", options.deepInfraApiKey) + options.deepInfraApiKey = getEnvVar(API_KEYS.DEEP_INFRA, options.deepInfraApiKey) } return new DeepInfraHandler(options) case "huggingface": if (options.huggingFaceConfigUseEnvVars) { - options.huggingFaceApiKey = getEnvVar("HUGGINGFACE_API_KEY", options.huggingFaceApiKey) + options.huggingFaceApiKey = getEnvVar(API_KEYS.HUGGING_FACE, options.huggingFaceApiKey) } return new HuggingFaceHandler(options) case "chutes": if (options.chutesConfigUseEnvVars) { - options.chutesApiKey = getEnvVar("CHUTES_API_KEY", options.chutesApiKey) + options.chutesApiKey = getEnvVar(API_KEYS.CHUTES, options.chutesApiKey) } return new ChutesHandler(options) case "litellm": if (options.litellmConfigUseEnvVars) { - options.litellmApiKey = getEnvVar("LITELLM_API_KEY", options.litellmApiKey) + options.litellmApiKey = getEnvVar(API_KEYS.LITELLM, options.litellmApiKey) } return new LiteLLMHandler(options) case "cerebras": if (options.cerebrasConfigUseEnvVars) { - options.cerebrasApiKey = getEnvVar("CEREBRAS_API_KEY", options.cerebrasApiKey) + options.cerebrasApiKey = getEnvVar(API_KEYS.CEREBRAS, options.cerebrasApiKey) } return new CerebrasHandler(options) case "sambanova": if (options.sambaNovaConfigUseEnvVars) { - options.sambaNovaApiKey = getEnvVar("SAMBANOVA_API_KEY", options.sambaNovaApiKey) + options.sambaNovaApiKey = getEnvVar(API_KEYS.SAMBA_NOVA, options.sambaNovaApiKey) } return new SambaNovaHandler(options) case "zai": if (options.zaiConfigUseEnvVars) { - options.zaiApiKey = getEnvVar("ZAI_API_KEY", options.zaiApiKey) + options.zaiApiKey = getEnvVar(API_KEYS.ZAI, options.zaiApiKey) } return new ZAiHandler(options) case "fireworks": if (options.fireworksConfigUseEnvVars) { - options.fireworksApiKey = getEnvVar("FIREWORKS_API_KEY", options.fireworksApiKey) + options.fireworksApiKey = getEnvVar(API_KEYS.FIREWORKS, options.fireworksApiKey) } return new FireworksHandler(options) case "io-intelligence": if (options.ioIntelligenceConfigUseEnvVars) { - options.ioIntelligenceApiKey = getEnvVar("IOINTELLIGENCE_API_KEY", options.ioIntelligenceApiKey) + options.ioIntelligenceApiKey = getEnvVar(API_KEYS.IO_INTELLIGENCE, options.ioIntelligenceApiKey) } return new IOIntelligenceHandler(options) case "roo": @@ -246,12 +247,12 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler { return new RooHandler(options) case "featherless": if (options.featherlessConfigUseEnvVars) { - options.featherlessApiKey = getEnvVar("FEATHERLESS_API_KEY", options.featherlessApiKey) + options.featherlessApiKey = getEnvVar(API_KEYS.FEATHERLESS, options.featherlessApiKey) } return new FeatherlessHandler(options) case "vercel-ai-gateway": if (options.vercelConfigUseEnvVars) { - options.vercelAiGatewayApiKey = getEnvVar("VERCEL_API_KEY", options.vercelAiGatewayApiKey) + options.vercelAiGatewayApiKey = getEnvVar(API_KEYS.VERCEL, options.vercelAiGatewayApiKey) } return new VercelAiGatewayHandler(options) default: diff --git a/webview-ui/src/components/settings/providers/VercelAiGateway.tsx b/webview-ui/src/components/settings/providers/VercelAiGateway.tsx index 5c95c0d64..6658d3bf5 100644 --- a/webview-ui/src/components/settings/providers/VercelAiGateway.tsx +++ b/webview-ui/src/components/settings/providers/VercelAiGateway.tsx @@ -29,7 +29,7 @@ export const VercelAiGateway = ({ <> setApiConfigurationField("vercelAiGatewayApiKey", value)} setConfigUseEnvVars={(value: boolean) => setApiConfigurationField("vercelConfigUseEnvVars", value)} From d6f301f003a0305af6d74312024953f794230a49 Mon Sep 17 00:00:00 2001 From: Geoff Wilson Date: Fri, 26 Sep 2025 22:43:55 -0400 Subject: [PATCH 42/42] Fix Chutes translation key Signed-off-by: Geoff Wilson --- webview-ui/src/components/settings/providers/Chutes.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webview-ui/src/components/settings/providers/Chutes.tsx b/webview-ui/src/components/settings/providers/Chutes.tsx index cf75eaca0..3766c3901 100644 --- a/webview-ui/src/components/settings/providers/Chutes.tsx +++ b/webview-ui/src/components/settings/providers/Chutes.tsx @@ -23,7 +23,7 @@ export const Chutes = ({ apiConfiguration, setApiConfigurationField }: ChutesPro setConfigUseEnvVars={(value: boolean) => setApiConfigurationField("chutesConfigUseEnvVars", value)} apiKeyLabel={t("settings:providers.chutesApiKey")} getApiKeyUrl="https://chutes.ai/app/api" - getApiKeyLabel={t("settings:providers.getchutesApiKey")} + getApiKeyLabel={t("settings:providers.getChutesApiKey")} /> )