From 6beaddcc14b619a2f346a31aa0154f4a73f818d5 Mon Sep 17 00:00:00 2001 From: Adithyan K Date: Sun, 15 Dec 2024 13:30:59 +0530 Subject: [PATCH] fixed #333 --- app/commit.json | 2 +- app/components/chat/APIKeyManager.tsx | 155 +++++++++++++++++++------- app/routes/api.check-env-key.ts | 29 +++++ 3 files changed, 147 insertions(+), 39 deletions(-) create mode 100644 app/routes/api.check-env-key.ts diff --git a/app/commit.json b/app/commit.json index 1636c99bd..c731689e5 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "ece0213500a94a6b29e29512c5040baf57884014" } +{ "commit": "9efc709782ed44a36da6de2222b1d5dd004fb489" } diff --git a/app/components/chat/APIKeyManager.tsx b/app/components/chat/APIKeyManager.tsx index 28847bc19..07f79d06e 100644 --- a/app/components/chat/APIKeyManager.tsx +++ b/app/components/chat/APIKeyManager.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { IconButton } from '~/components/ui/IconButton'; import type { ProviderInfo } from '~/types/model'; @@ -10,10 +10,50 @@ interface APIKeyManagerProps { labelForGetApiKey?: string; } +const ENV_API_KEY_MAP: Record = { + Anthropic: 'ANTHROPIC_API_KEY', + OpenAI: 'OPENAI_API_KEY', + xAI: 'XAI_API_KEY', + Cohere: 'COHERE_API_KEY', + Google: 'GOOGLE_API_KEY', + Groq: 'GROQ_API_KEY', + HuggingFace: 'HUGGINGFACE_API_KEY', + Deepseek: 'DEEPSEEK_API_KEY', + Mistral: 'MISTRAL_API_KEY', + Together: 'TOGETHER_API_KEY', + OpenRouter: 'OPENROUTER_API_KEY', +}; + // eslint-disable-next-line @typescript-eslint/naming-convention export const APIKeyManager: React.FC = ({ provider, apiKey, setApiKey }) => { const [isEditing, setIsEditing] = useState(false); const [tempKey, setTempKey] = useState(apiKey); + const [isEnvKeySet, setIsEnvKeySet] = useState(false); + const previousProvider = useRef(provider.name); + + useEffect(() => { + if (previousProvider.current !== provider.name) { + setTempKey(''); + setApiKey(''); + setIsEditing(false); + previousProvider.current = provider.name; + } + }, [provider.name, setApiKey]); + + useEffect(() => { + const checkEnvApiKey = async () => { + try { + const response = await fetch(`/api/check-env-key?provider=${encodeURIComponent(provider.name)}`); + const data = await response.json(); + setIsEnvKeySet((data as { isSet: boolean }).isSet); + } catch (error) { + console.error('Failed to check environment API key:', error); + setIsEnvKeySet(false); + } + }; + + checkEnvApiKey(); + }, [provider.name]); const handleSave = () => { setApiKey(tempKey); @@ -21,47 +61,86 @@ export const APIKeyManager: React.FC = ({ provider, apiKey, }; return ( -
-
- {provider?.name} API Key: - {!isEditing && ( -
- - {apiKey ? '••••••••' : 'Not set (will still work if set in .env file)'} - - setIsEditing(true)} title="Edit API Key"> -
+
+
+
+ {provider?.name} API Key: + {!isEditing && ( +
+ {isEnvKeySet ? ( + <> +
+ + Set via {ENV_API_KEY_MAP[provider.name]} environment variable + + + ) : apiKey ? ( + <> +
+ Set via UI + + ) : ( + <> +
+ Not Set + + )} +
+ )} +
+
+ +
+ {isEditing && !isEnvKeySet ? ( +
+ setTempKey(e.target.value)} + className="w-[300px] px-3 py-1.5 text-sm rounded border border-bolt-elements-borderColor + bg-bolt-elements-prompt-background text-bolt-elements-textPrimary + focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus" + /> + +
+ + setIsEditing(false)} + title="Cancel" + className="bg-red-500/10 hover:bg-red-500/20 text-red-500" + > +
+ ) : ( + <> + {!isEnvKeySet && ( + setIsEditing(true)} + title="Edit API Key" + className="bg-blue-500/10 hover:bg-blue-500/20 text-blue-500" + > +
+ + )} + {provider?.getApiKeyLink && !isEnvKeySet && ( + window.open(provider?.getApiKeyLink)} + title="Get API Key" + className="bg-purple-500/10 hover:bg-purple-500/20 text-purple-500 flex items-center gap-2" + > + {provider?.labelForGetApiKey || 'Get API Key'} +
+ + )} + )}
- - {isEditing ? ( -
- setTempKey(e.target.value)} - className="flex-1 px-2 py-1 text-xs lg:text-sm rounded border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus" - /> - -
- - setIsEditing(false)} title="Cancel"> -
- -
- ) : ( - <> - {provider?.getApiKeyLink && ( - window.open(provider?.getApiKeyLink)} title="Edit API Key"> - {provider?.labelForGetApiKey || 'Get API Key'} -
- - )} - - )}
); }; diff --git a/app/routes/api.check-env-key.ts b/app/routes/api.check-env-key.ts new file mode 100644 index 000000000..771d3fb8b --- /dev/null +++ b/app/routes/api.check-env-key.ts @@ -0,0 +1,29 @@ +import type { LoaderFunction } from '@remix-run/node'; + +const ENV_API_KEY_MAP: Record = { + Anthropic: 'ANTHROPIC_API_KEY', + OpenAI: 'OPENAI_API_KEY', + xAI: 'XAI_API_KEY', + Cohere: 'COHERE_API_KEY', + Google: 'GOOGLE_API_KEY', + Groq: 'GROQ_API_KEY', + HuggingFace: 'HUGGINGFACE_API_KEY', + Deepseek: 'DEEPSEEK_API_KEY', + Mistral: 'MISTRAL_API_KEY', + Together: 'TOGETHER_API_KEY', + OpenRouter: 'OPENROUTER_API_KEY', +}; + +export const loader: LoaderFunction = async ({ request }) => { + const url = new URL(request.url); + const provider = url.searchParams.get('provider'); + + if (!provider || !ENV_API_KEY_MAP[provider]) { + return Response.json({ isSet: false }); + } + + const envVarName = ENV_API_KEY_MAP[provider]; + const isSet = !!process.env[envVarName]; + + return Response.json({ isSet }); +};