Skip to content
Snippets Groups Projects
Commit e7fca3c0 authored by ali asaria's avatar ali asaria
Browse files

fixes to provider settings

parent 15d2884e
No related branches found
No related tags found
No related merge requests found
import * as React from 'react'; import * as React from 'react';
import { import {
Button, Button,
Modal, Modal,
ModalDialog, ModalDialog,
FormControl, FormControl,
FormLabel, FormLabel,
Input, Input,
List, List,
ListItem, ListItem,
Box, Box,
Typography Typography,
Chip,
} from '@mui/joy'; } from '@mui/joy';
import * as chatAPI from 'renderer/lib/transformerlab-api-sdk'; import * as chatAPI from 'renderer/lib/transformerlab-api-sdk';
import useSWR from 'swr'; import useSWR from 'swr';
import { ChevronLeftIcon } from 'lucide-react';
const fetcher = (url: string) => fetch(url).then((res) => res.json()); const fetcher = (url: string) => fetch(url).then((res) => res.json());
interface Provider { interface Provider {
name: string; name: string;
keyName: string; keyName: string;
setKeyEndpoint: () => string; setKeyEndpoint: () => string;
checkKeyEndpoint: () => string; checkKeyEndpoint: () => string;
} }
const providers: Provider[] = [ const providers: Provider[] = [
{ {
name: 'OpenAI', name: 'OpenAI',
keyName: 'OPENAI_API_KEY', keyName: 'OPENAI_API_KEY',
setKeyEndpoint: () => chatAPI.Endpoints.Models.SetOpenAIKey(), setKeyEndpoint: () => chatAPI.Endpoints.Models.SetOpenAIKey(),
checkKeyEndpoint: () => chatAPI.Endpoints.Models.CheckOpenAIAPIKey(), checkKeyEndpoint: () => chatAPI.Endpoints.Models.CheckOpenAIAPIKey(),
}, },
{ {
name: 'Anthropic', name: 'Anthropic',
keyName: 'ANTHROPIC_API_KEY', keyName: 'ANTHROPIC_API_KEY',
setKeyEndpoint: () => chatAPI.Endpoints.Models.SetAnthropicKey(), setKeyEndpoint: () => chatAPI.Endpoints.Models.SetAnthropicKey(),
checkKeyEndpoint: () => chatAPI.Endpoints.Models.CheckAnthropicAPIKey(), checkKeyEndpoint: () => chatAPI.Endpoints.Models.CheckAnthropicAPIKey(),
}, },
{ {
name: 'Custom API', name: 'Custom API',
keyName: 'CUSTOM_MODEL_API_KEY', keyName: 'CUSTOM_MODEL_API_KEY',
setKeyEndpoint: () => chatAPI.Endpoints.Models.SetCustomAPIKey(), setKeyEndpoint: () => chatAPI.Endpoints.Models.SetCustomAPIKey(),
checkKeyEndpoint: () => chatAPI.Endpoints.Models.CheckCustomAPIKey(), checkKeyEndpoint: () => chatAPI.Endpoints.Models.CheckCustomAPIKey(),
} },
]; ];
interface AIProvidersSettingsProps { interface AIProvidersSettingsProps {
onBack?: () => void; onBack?: () => void;
} }
export default function AIProvidersSettings({ onBack }: AIProvidersSettingsProps) { export default function AIProvidersSettings({
const { data: openaiApiKey, mutate: mutateOpenAI } = useSWR( onBack,
chatAPI.Endpoints.Config.Get('OPENAI_API_KEY'), }: AIProvidersSettingsProps) {
fetcher const { data: openaiApiKey, mutate: mutateOpenAI } = useSWR(
); chatAPI.Endpoints.Config.Get('OPENAI_API_KEY'),
const { data: claudeApiKey, mutate: mutateClaude } = useSWR( fetcher,
chatAPI.Endpoints.Config.Get('ANTHROPIC_API_KEY'), );
fetcher const { data: claudeApiKey, mutate: mutateClaude } = useSWR(
); chatAPI.Endpoints.Config.Get('ANTHROPIC_API_KEY'),
const { data: customAPIStatus, mutate: mutateCustom } = useSWR( fetcher,
chatAPI.Endpoints.Config.Get('CUSTOM_MODEL_API_KEY'), );
fetcher const { data: customAPIStatus, mutate: mutateCustom } = useSWR(
); chatAPI.Endpoints.Config.Get('CUSTOM_MODEL_API_KEY'),
fetcher,
);
const getProviderStatus = (provider: Provider) => { const getProviderStatus = (provider: Provider) => {
if (provider.name === 'OpenAI') return openaiApiKey; if (provider.name === 'OpenAI') return openaiApiKey;
if (provider.name === 'Anthropic') return claudeApiKey; if (provider.name === 'Anthropic') return claudeApiKey;
if (provider.name === 'Custom API') return customAPIStatus; if (provider.name === 'Custom API') return customAPIStatus;
return null; return null;
}; };
const setProviderStatus = async (provider: Provider, token: string) => { const setProviderStatus = async (provider: Provider, token: string) => {
await fetch(chatAPI.Endpoints.Config.Set(provider.keyName, token)); await fetch(chatAPI.Endpoints.Config.Set(provider.keyName, token));
await fetch(provider.setKeyEndpoint()); await fetch(provider.setKeyEndpoint());
const response = await fetch(provider.checkKeyEndpoint()); const response = await fetch(provider.checkKeyEndpoint());
const result = await response.json(); const result = await response.json();
return result.message === 'OK'; return result.message === 'OK';
}; };
const [dialogOpen, setDialogOpen] = React.useState(false); const [dialogOpen, setDialogOpen] = React.useState(false);
const [selectedProvider, setSelectedProvider] = React.useState<Provider | null>(null); const [selectedProvider, setSelectedProvider] =
const [apiKey, setApiKey] = React.useState(''); React.useState<Provider | null>(null);
const [hoveredProvider, setHoveredProvider] = React.useState<string | null>(null); const [apiKey, setApiKey] = React.useState('');
// States for custom API additional fields const [hoveredProvider, setHoveredProvider] = React.useState<string | null>(
const [customApiName, setCustomApiName] = React.useState(''); null,
const [customBaseURL, setCustomBaseURL] = React.useState(''); );
const [customApiKey, setCustomApiKey] = React.useState(''); // States for custom API additional fields
const [customModelName, setCustomModelName] = React.useState(''); const [customApiName, setCustomApiName] = React.useState('');
const [customBaseURL, setCustomBaseURL] = React.useState('');
const [customApiKey, setCustomApiKey] = React.useState('');
const [customModelName, setCustomModelName] = React.useState('');
const handleConnectClick = (provider: Provider) => { const handleConnectClick = (provider: Provider) => {
setSelectedProvider(provider); setSelectedProvider(provider);
if (provider.name === 'Custom API') { if (provider.name === 'Custom API') {
setCustomApiName(''); setCustomApiName('');
setCustomBaseURL(''); setCustomBaseURL('');
setCustomApiKey(''); setCustomApiKey('');
setCustomModelName(''); setCustomModelName('');
} else { } else {
setApiKey(''); setApiKey('');
} }
setDialogOpen(true); setDialogOpen(true);
}; };
const handleDisconnectClick = async (provider: Provider) => { const handleDisconnectClick = async (provider: Provider) => {
await fetch(chatAPI.Endpoints.Config.Set(provider.keyName, '')); await fetch(chatAPI.Endpoints.Config.Set(provider.keyName, ''));
if (provider.name === 'OpenAI') { if (provider.name === 'OpenAI') {
mutateOpenAI();
} else if (provider.name === 'Anthropic') {
mutateClaude();
} else if (provider.name === 'Custom API') {
mutateCustom();
}
};
const handleSave = async () => {
if (selectedProvider) {
let token = '';
if (selectedProvider.name === 'Custom API') {
const customObj = {
apiName: customApiName,
baseURL: customBaseURL,
apiKey: customApiKey,
modelName: customModelName,
};
token = JSON.stringify(customObj);
} else if (apiKey) {
token = apiKey;
}
if (token) {
const success = await setProviderStatus(selectedProvider, token);
if (success) {
alert(`Successfully connected to ${selectedProvider.name}`);
if (selectedProvider.name === 'OpenAI') {
mutateOpenAI(); mutateOpenAI();
} else if (provider.name === 'Anthropic') { } else if (selectedProvider.name === 'Anthropic') {
mutateClaude(); mutateClaude();
} else if (provider.name === 'Custom API') { } else if (selectedProvider.name === 'Custom API') {
mutateCustom(); mutateCustom();
}
} else {
alert(`Failed to connect to ${selectedProvider.name}`);
} }
}; }
}
setDialogOpen(false);
};
const handleSave = async () => { return (
if (selectedProvider) { <Box sx={{ p: 2 }}>
let token = ''; <Button
if (selectedProvider.name === 'Custom API') { onClick={onBack}
const customObj = { startDecorator={<ChevronLeftIcon />}
apiName: customApiName, variant="plain"
baseURL: customBaseURL, >
apiKey: customApiKey, Back to Settings
modelName: customModelName </Button>
}; <Typography level="h3" mb={2} mt={2}>
token = JSON.stringify(customObj); AI Providers
} else if (apiKey) { </Typography>
token = apiKey; <List sx={{ gap: 1 }}>
} {providers.map((provider) => {
if (token) { const status = getProviderStatus(provider);
const success = await setProviderStatus(selectedProvider, token); const isConnected = status && status !== '';
if (success) { return (
alert(`Successfully connected to ${selectedProvider.name}`); <ListItem
if (selectedProvider.name === 'OpenAI') { key={provider.name}
mutateOpenAI(); sx={{
} else if (selectedProvider.name === 'Anthropic') { display: 'flex',
mutateClaude(); justifyContent: 'space-between',
} else if (selectedProvider.name === 'Custom API') { alignItems: 'center',
mutateCustom(); p: 2,
} borderRadius: '8px',
} else { bgcolor: 'neutral.softBg',
alert(`Failed to connect to ${selectedProvider.name}`); }}
} >
} <Typography level="title-md">{provider.name}</Typography>
} <Box>
setDialogOpen(false); {isConnected && (
}; <>
<Chip variant="solid" color="success">
{hoveredProvider === provider.name
? 'Not Connected'
: 'Connected'}
</Chip>
</>
)}
return ( <Button
<Box sx={{ p: 2 }}> variant="soft"
<Button onClick={onBack}>Back to Settings</Button> onClick={() => handleConnectClick(provider)}
<Typography level="h3" mb={2}>
AI Providers
</Typography>
<List sx={{ gap: 1 }}>
{providers.map((provider) => {
const status = getProviderStatus(provider);
const isConnected = status && status !== '';
return (
<ListItem
key={provider.name}
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
p: 1,
borderRadius: '8px',
bgcolor: 'neutral.softBg'
}}
>
<Typography>{provider.name}</Typography>
{isConnected ? (
<Box
onMouseEnter={() => setHoveredProvider(provider.name)}
onMouseLeave={() => setHoveredProvider(null)}
onClick={() => handleDisconnectClick(provider)}
sx={{
cursor: 'pointer',
border: '1px solid',
borderColor: 'neutral.outlinedBorder',
borderRadius: '4px',
px: 1,
py: 0.5,
fontSize: '0.875rem',
color: 'success.600'
}}
>
{hoveredProvider === provider.name ? 'Disconnect' : 'Connected'}
</Box>
) : (
<Button variant="soft" onClick={() => handleConnectClick(provider)}>
Connect
</Button>
)}
</ListItem>
);
})}
</List>
{/* API Key Modal */}
<Modal open={dialogOpen} onClose={() => setDialogOpen(false)}>
<ModalDialog
layout="stack"
aria-labelledby="connect-dialog-title"
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
maxWidth: 400,
width: '90%'
}}
> >
<Typography id="connect-dialog-title" component="h2"> Set API Key
Connect to {selectedProvider?.name} </Button>
</Typography> </Box>
{selectedProvider?.name === 'Custom API' ? ( </ListItem>
<> );
<FormControl sx={{ mt: 2 }}> })}
<FormLabel>API Name</FormLabel> </List>
<Input {/* API Key Modal */}
value={customApiName} <Modal open={dialogOpen} onClose={() => setDialogOpen(false)}>
onChange={(e) => setCustomApiName(e.target.value)} <ModalDialog
/> aria-labelledby="connect-dialog-title"
</FormControl> sx={{
<FormControl sx={{ mt: 2 }}> position: 'absolute',
<FormLabel>Base URL</FormLabel> top: '50%',
<Input left: '50%',
value={customBaseURL} transform: 'translate(-50%, -50%)',
onChange={(e) => setCustomBaseURL(e.target.value)} maxWidth: 400,
/> width: '90%',
</FormControl> }}
<FormControl sx={{ mt: 2 }}> >
<FormLabel>API Key</FormLabel> <Typography id="connect-dialog-title" component="h2">
<Input Connect to {selectedProvider?.name}
type="password" </Typography>
value={customApiKey} {selectedProvider?.name === 'Custom API' ? (
onChange={(e) => setCustomApiKey(e.target.value)} <>
/> <FormControl sx={{ mt: 2 }}>
</FormControl> <FormLabel>API Name</FormLabel>
<FormControl sx={{ mt: 2 }}> <Input
<FormLabel>Model Name</FormLabel> value={customApiName}
<Input onChange={(e) => setCustomApiName(e.target.value)}
value={customModelName} />
onChange={(e) => setCustomModelName(e.target.value)} </FormControl>
/> <FormControl sx={{ mt: 2 }}>
</FormControl> <FormLabel>Base URL</FormLabel>
</> <Input
) : ( value={customBaseURL}
<FormControl sx={{ mt: 2 }}> onChange={(e) => setCustomBaseURL(e.target.value)}
<FormLabel>{selectedProvider?.name} API Key</FormLabel> />
<Input </FormControl>
type="password" <FormControl sx={{ mt: 2 }}>
value={apiKey} <FormLabel>API Key</FormLabel>
onChange={(e) => setApiKey(e.target.value)} <Input
/> type="password"
</FormControl> value={customApiKey}
)} onChange={(e) => setCustomApiKey(e.target.value)}
{/* Conditional help steps */} />
{selectedProvider?.name === 'OpenAI' && ( </FormControl>
<Box sx={{ mt: 2 }}> <FormControl sx={{ mt: 2 }}>
<Typography level="body2"> <FormLabel>Model Name</FormLabel>
Steps to get an OpenAI API Key: <Input
</Typography> value={customModelName}
<ol> onChange={(e) => setCustomModelName(e.target.value)}
<li> />
Visit{' '} </FormControl>
<a </>
href="https://platform.openai.com/account/api-keys" ) : (
target="_blank" <FormControl sx={{ mt: 2 }}>
rel="noreferrer" <FormLabel>{selectedProvider?.name} API Key</FormLabel>
> <Input
OpenAI API website type="password"
</a> value={apiKey}
</li> onChange={(e) => setApiKey(e.target.value)}
<li>Log in to your OpenAI account.</li> />
<li>Create a new API key and copy it.</li> </FormControl>
</ol> )}
</Box> {/* Conditional help steps */}
)} {selectedProvider?.name === 'OpenAI' && (
{selectedProvider?.name === 'Anthropic' && ( <Box sx={{ mt: 2 }}>
<Box sx={{ mt: 2 }}> <Typography level="body2">
<Typography level="body2"> Steps to get an OpenAI API Key:
Steps to get a Anthropic API Key: </Typography>
</Typography> <ol>
<ol> <li>
<li>Visit{' '} Visit{' '}
<a <a
href="https://console.anthropic.com/settings/keys" href="https://platform.openai.com/account/api-keys"
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
> >
Anthropic API Keys Console OpenAI API website
</a></li> </a>
<li>Log in/Create an account.</li> </li>
<li>Follow instructions to generate an API key.</li> <li>Log in to your OpenAI account.</li>
</ol> <li>Create a new API key and copy it.</li>
</Box> </ol>
)} </Box>
<Box sx={{ display: 'flex', justifyContent: 'flex-end', gap: 1, mt: 2 }}> )}
<Button onClick={() => setDialogOpen(false)}>Cancel</Button> {selectedProvider?.name === 'Anthropic' && (
<Button onClick={handleSave}>Save</Button> <Box sx={{ mt: 2 }}>
</Box> <Typography level="body2">
</ModalDialog> Steps to get a Anthropic API Key:
</Modal> </Typography>
</Box> <ol>
); <li>
Visit{' '}
<a
href="https://console.anthropic.com/settings/keys"
target="_blank"
rel="noreferrer"
>
Anthropic API Keys Console
</a>
</li>
<li>Log in/Create an account.</li>
<li>Follow instructions to generate an API key.</li>
</ol>
</Box>
)}
<Box
sx={{ display: 'flex', justifyContent: 'flex-end', gap: 1, mt: 2 }}
>
<Button onClick={() => setDialogOpen(false)}>Cancel</Button>
<Button onClick={handleSave}>Save</Button>
</Box>
</ModalDialog>
</Modal>
</Box>
);
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment