mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-02-18 00:18:09 +01:00
Added functionality to allow users to set custom api keys (#276)
* Added functionality to allow users to set custom api keys * Added error handling * Changed token to apiKey * Changed apiKey to oaiApiKey * added azure openai ui * Removed logging * Changed configure to Use * Made checked position more rounded * Made setting api key optional if it is openai * Modified error handling * Add support for insufficient_quota errors * Fixed faulty error detection * removed logging
This commit is contained in:
parent
08f3a77d58
commit
14104b276f
11 changed files with 207 additions and 32 deletions
|
|
@ -26,7 +26,7 @@ export default function ModelItem({ endpoint, value, onSelect }) {
|
|||
className: 'mr-2'
|
||||
});
|
||||
|
||||
const isuserProvide = endpointsConfig?.[endpoint]?.userProvide;
|
||||
const isUserProvided = endpointsConfig?.[endpoint]?.userProvide;
|
||||
|
||||
// regular model
|
||||
return (
|
||||
|
|
@ -39,7 +39,7 @@ export default function ModelItem({ endpoint, value, onSelect }) {
|
|||
{alternateName[endpoint] || endpoint}
|
||||
{!!['azureOpenAI', 'openAI'].find(e => e === endpoint) && <sup>$</sup>}
|
||||
<div className="flex w-4 flex-1" />
|
||||
{isuserProvide ? (
|
||||
{isUserProvided ? (
|
||||
<button
|
||||
className="invisible m-0 mr-1 flex-initial rounded-md p-0 text-xs font-medium text-gray-400 hover:text-gray-700 group-hover:visible dark:font-normal dark:text-gray-400 dark:hover:text-gray-200"
|
||||
onClick={e => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
import React from 'react';
|
||||
import { Input } from '../../ui/Input.tsx';
|
||||
import { Label } from '../../ui/Label.tsx';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
function InputWithLabel({ value, onChange, label, id }) {
|
||||
const defaultTextProps =
|
||||
'rounded-md border border-gray-300 bg-transparent text-sm shadow-[0_0_10px_rgba(0,0,0,0.10)] outline-none placeholder:text-gray-400 focus:outline-none focus:ring-gray-400 focus:ring-opacity-20 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-400 dark:bg-gray-700 dark:text-gray-50 dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] dark:focus:border-gray-400 dark:focus:outline-none dark:focus:ring-0 dark:focus:ring-gray-400 dark:focus:ring-offset-0';
|
||||
|
||||
return (
|
||||
<>
|
||||
<Label
|
||||
htmlFor={id}
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
{label}
|
||||
<br />
|
||||
</Label>
|
||||
|
||||
<Input
|
||||
id={id}
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
placeholder={`Enter ${label}`}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex h-10 max-h-10 w-full resize-none px-3 py-2 focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0'
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default InputWithLabel;
|
||||
|
|
@ -4,11 +4,24 @@ import { Dialog } from '../../ui/Dialog.tsx';
|
|||
import { Input } from '../../ui/Input.tsx';
|
||||
import { Label } from '../../ui/Label.tsx';
|
||||
import { cn } from '~/utils/';
|
||||
import * as Checkbox from '@radix-ui/react-checkbox';
|
||||
import { CheckIcon } from '@radix-ui/react-icons';
|
||||
import FileUpload from '../NewConversationMenu/FileUpload';
|
||||
import store from '~/store';
|
||||
import InputWithLabel from './InputWithLabel';
|
||||
|
||||
function isJson(str) {
|
||||
try {
|
||||
JSON.parse(str);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const SetTokenDialog = ({ open, onOpenChange, endpoint }) => {
|
||||
const [token, setToken] = useState('');
|
||||
const [showPanel, setShowPanel] = useState(false);
|
||||
const { getToken, saveToken } = store.useToken(endpoint);
|
||||
|
||||
const defaultTextProps =
|
||||
|
|
@ -20,9 +33,19 @@ const SetTokenDialog = ({ open, onOpenChange, endpoint }) => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
setToken(getToken() ?? '');
|
||||
let oldToken = getToken();
|
||||
if (isJson(token)) {
|
||||
setShowPanel(true);
|
||||
}
|
||||
setToken(oldToken ?? '');
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showPanel && isJson(token)) {
|
||||
setToken('');
|
||||
}
|
||||
}, [showPanel]);
|
||||
|
||||
const helpText = {
|
||||
bingAI: (
|
||||
<small className="break-all text-gray-600">
|
||||
|
|
@ -79,6 +102,25 @@ const SetTokenDialog = ({ open, onOpenChange, endpoint }) => {
|
|||
)
|
||||
};
|
||||
|
||||
function getAzure(name) {
|
||||
if (isJson(token)) {
|
||||
let newToken = JSON.parse(token);
|
||||
return newToken[name];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function setAzure(name, value) {
|
||||
let newToken = {};
|
||||
if (isJson(token)) {
|
||||
newToken = JSON.parse(token);
|
||||
}
|
||||
newToken[name] = value;
|
||||
|
||||
setToken(JSON.stringify(newToken));
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
|
|
@ -88,16 +130,9 @@ const SetTokenDialog = ({ open, onOpenChange, endpoint }) => {
|
|||
title={`Set Token of ${endpoint}`}
|
||||
main={
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<Label
|
||||
htmlFor="chatGptLabel"
|
||||
className="text-left text-sm font-medium"
|
||||
>
|
||||
Token Name
|
||||
<br />
|
||||
</Label>
|
||||
{endpoint === 'google' ? (
|
||||
<FileUpload
|
||||
id="googleKey"
|
||||
id="googleKey"
|
||||
className="w-full"
|
||||
text="Import Service Account JSON Key"
|
||||
successText="Successfully Imported Service Account JSON Key"
|
||||
|
|
@ -137,17 +172,77 @@ const SetTokenDialog = ({ open, onOpenChange, endpoint }) => {
|
|||
setToken(JSON.stringify(data));
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
id="chatGptLabel"
|
||||
value={token || ''}
|
||||
onChange={e => setToken(e.target.value || '')}
|
||||
placeholder="Set the token."
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex h-10 max-h-10 w-full resize-none px-3 py-2 focus:outline-none focus:ring-0 focus:ring-opacity-0 focus:ring-offset-0'
|
||||
) : endpoint === 'openAI' ? (
|
||||
<>
|
||||
{!showPanel ? (
|
||||
<>
|
||||
<InputWithLabel
|
||||
id={'chatGPTLabel'}
|
||||
value={token || ''}
|
||||
onChange={e => setToken(e.target.value || '')}
|
||||
label={'OpenAI API Key'}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<InputWithLabel
|
||||
id={'instanceNameLabel'}
|
||||
value={getAzure('instanceName') || ''}
|
||||
onChange={e => setAzure('instanceName', e.target.value || '')}
|
||||
label={'Azure OpenAI Instance Name'}
|
||||
/>
|
||||
|
||||
<InputWithLabel
|
||||
id={'deploymentNameLabel'}
|
||||
value={getAzure('deploymentName') || ''}
|
||||
onChange={e => setAzure('deploymentName', e.target.value || '')}
|
||||
label={'Azure OpenAI Deployment Name'}
|
||||
/>
|
||||
|
||||
<InputWithLabel
|
||||
id={'versionLabel'}
|
||||
value={getAzure('version') || ''}
|
||||
onChange={e => setAzure('version', e.target.value || '')}
|
||||
label={'Azure OpenAI API Version'}
|
||||
/>
|
||||
|
||||
<InputWithLabel
|
||||
id={'apiKeyLabel'}
|
||||
value={getAzure('apiKey') || ''}
|
||||
onChange={e => setAzure('apiKey', e.target.value || '')}
|
||||
label={'Azure OpenAI API Key'}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<div className="flex items-center">
|
||||
<Checkbox.Root
|
||||
className="flex h-[20px] w-[20px] appearance-none items-center justify-center rounded-[4px] bg-gray-100 text-white outline-none hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-900"
|
||||
id="azureOpenAI"
|
||||
checked={showPanel}
|
||||
onCheckedChange={() => setShowPanel(!showPanel)}
|
||||
>
|
||||
<Checkbox.Indicator className="flex h-[20px] w-[20px] items-center justify-center rounded-[3.5px] bg-green-600">
|
||||
<CheckIcon />
|
||||
</Checkbox.Indicator>
|
||||
</Checkbox.Root>
|
||||
|
||||
<label
|
||||
className="pl-[8px] text-[15px] leading-none dark:text-white"
|
||||
htmlFor="azureOpenAI"
|
||||
>
|
||||
Use Azure OpenAI.
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<InputWithLabel
|
||||
id={'chatGPTLabel'}
|
||||
value={token || ''}
|
||||
onChange={e => setToken(e.target.value || '')}
|
||||
label={'Token Name'}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<small className="text-red-600">Your token will be sent to the server, but not saved.</small>
|
||||
{helpText?.[endpoint]}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ export default function SubmitButton({
|
|||
type="button"
|
||||
className="group absolute bottom-0 right-0 flex h-[100%] w-[50px] items-center justify-center bg-transparent p-1 text-gray-500"
|
||||
>
|
||||
<div className="m-1 mr-0 rounded-md p-2 pt-[10px] pb-[10px] group-hover:bg-gray-100 group-disabled:hover:bg-transparent dark:group-hover:bg-gray-900 dark:group-hover:text-gray-400 dark:group-disabled:hover:bg-transparent">
|
||||
<div className="m-1 mr-0 rounded-md p-2 pb-[10px] pt-[10px] group-hover:bg-gray-100 group-disabled:hover:bg-transparent dark:group-hover:bg-gray-900 dark:group-hover:text-gray-400 dark:group-disabled:hover:bg-transparent">
|
||||
<StopGeneratingIcon />
|
||||
</div>
|
||||
</button>
|
||||
|
|
@ -61,7 +61,7 @@ export default function SubmitButton({
|
|||
// </div>
|
||||
// </button>
|
||||
// );
|
||||
else if (!isTokenProvided) {
|
||||
else if (!isTokenProvided && endpoint !== 'openAI') {
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
|
|
@ -69,7 +69,7 @@ export default function SubmitButton({
|
|||
type="button"
|
||||
className="group absolute bottom-0 right-0 flex h-[100%] w-auto items-center justify-center bg-transparent p-1 text-gray-500"
|
||||
>
|
||||
<div className="m-1 mr-0 rounded-md p-2 pt-[10px] pb-[10px] align-middle text-xs group-hover:bg-gray-100 group-disabled:hover:bg-transparent dark:group-hover:bg-gray-900 dark:group-hover:text-gray-400 dark:group-disabled:hover:bg-transparent">
|
||||
<div className="m-1 mr-0 rounded-md p-2 pb-[10px] pt-[10px] align-middle text-xs group-hover:bg-gray-100 group-disabled:hover:bg-transparent dark:group-hover:bg-gray-900 dark:group-hover:text-gray-400 dark:group-disabled:hover:bg-transparent">
|
||||
<Settings className="mr-1 inline-block w-[18px]" />
|
||||
Set Token First
|
||||
</div>
|
||||
|
|
@ -88,7 +88,7 @@ export default function SubmitButton({
|
|||
disabled={disabled}
|
||||
className="group absolute bottom-0 right-0 flex h-[100%] w-[50px] items-center justify-center bg-transparent p-1 text-gray-500"
|
||||
>
|
||||
<div className="m-1 mr-0 rounded-md p-2 pt-[10px] pb-[10px] group-hover:bg-gray-100 group-disabled:hover:bg-transparent dark:group-hover:bg-gray-900 dark:group-hover:text-gray-400 dark:group-disabled:hover:bg-transparent">
|
||||
<div className="m-1 mr-0 rounded-md p-2 pb-[10px] pt-[10px] group-hover:bg-gray-100 group-disabled:hover:bg-transparent dark:group-hover:bg-gray-900 dark:group-hover:text-gray-400 dark:group-disabled:hover:bg-transparent">
|
||||
<svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue