mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-22 03:10:15 +01:00
feat: feat: new endpoint-style endpoint select
fix: a wrong use of bingai params
This commit is contained in:
parent
adcc021c9e
commit
e8e3903b78
18 changed files with 7891 additions and 233 deletions
24
client/src/components/Input/Endpoints/EndpointItem.jsx
Normal file
24
client/src/components/Input/Endpoints/EndpointItem.jsx
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import React from 'react';
|
||||
import { DropdownMenuRadioItem } from '../../ui/DropdownMenu.tsx';
|
||||
import getIcon from '~/utils/getIcon';
|
||||
|
||||
export default function ModelItem({ endpoint, value, onSelect }) {
|
||||
const icon = getIcon({
|
||||
size: 20,
|
||||
endpoint,
|
||||
error: false,
|
||||
className: 'mr-2'
|
||||
});
|
||||
|
||||
// regular model
|
||||
return (
|
||||
<DropdownMenuRadioItem
|
||||
value={value}
|
||||
className="dark:font-semibold dark:text-gray-100 dark:hover:bg-gray-800"
|
||||
>
|
||||
{icon}
|
||||
{endpoint}
|
||||
{endpoint in ['azureOpenAI', 'openAI'] && <sup>$</sup>}
|
||||
</DropdownMenuRadioItem>
|
||||
);
|
||||
}
|
||||
17
client/src/components/Input/Endpoints/EndpointItems.jsx
Normal file
17
client/src/components/Input/Endpoints/EndpointItems.jsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import EndpointItem from './EndpointItem';
|
||||
|
||||
export default function EndpointItems({ endpoints, onSelect }) {
|
||||
return (
|
||||
<>
|
||||
{endpoints.map(endpoint => (
|
||||
<EndpointItem
|
||||
key={endpoint}
|
||||
value={endpoint}
|
||||
onSelect={onSelect}
|
||||
endpoint={endpoint}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
188
client/src/components/Input/Endpoints/EndpointMenu.jsx
Normal file
188
client/src/components/Input/Endpoints/EndpointMenu.jsx
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
// import ModelDialog from './ModelDialog';
|
||||
import EndpointItems from './EndpointItems';
|
||||
import { swr } from '~/utils/fetchers';
|
||||
import getIcon from '~/utils/getIcon';
|
||||
|
||||
import { Button } from '../../ui/Button.tsx';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from '../../ui/DropdownMenu.tsx';
|
||||
import { Dialog } from '../../ui/Dialog.tsx';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
export default function EndpointMenu() {
|
||||
// const [modelSave, setModelSave] = useState(false);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
// const models = useRecoilValue(store.models);
|
||||
const availableEndpoints = useRecoilValue(store.availableEndpoints);
|
||||
// const setCustomGPTModels = useSetRecoilState(store.customGPTModels);
|
||||
|
||||
const conversation = useRecoilValue(store.conversation) || {};
|
||||
const { endpoint, conversationId } = conversation;
|
||||
// const { model, promptPrefix, chatGptLabel, conversationId } = conversation;
|
||||
const { newConversation } = store.useConversation();
|
||||
|
||||
// fetch the list of saved chatgptCustom
|
||||
// const { data, isLoading, mutate } = swr(`/api/customGpts`, res => {
|
||||
// const fetchedModels = res.map(modelItem => ({
|
||||
// ...modelItem,
|
||||
// name: modelItem.chatGptLabel,
|
||||
// model: 'chatgptCustom'
|
||||
// }));
|
||||
|
||||
// setCustomGPTModels(fetchedModels);
|
||||
// });
|
||||
|
||||
// update the default model when availableModels changes
|
||||
// typically, availableModels changes => modelsFilter or customGPTModels changes
|
||||
useEffect(() => {
|
||||
if (conversationId == 'new') {
|
||||
newConversation();
|
||||
}
|
||||
}, [availableEndpoints]);
|
||||
|
||||
// save selected model to localstoreage
|
||||
useEffect(() => {
|
||||
if (endpoint) localStorage.setItem('lastConversationSetup', JSON.stringify(conversation));
|
||||
}, [conversation]);
|
||||
|
||||
// set the current model
|
||||
const onChange = (newEndpoint, value = null) => {
|
||||
setMenuOpen(false);
|
||||
|
||||
if (!newEndpoint) return;
|
||||
else if (newEndpoint === endpoint) return;
|
||||
else {
|
||||
newConversation({}, newEndpoint);
|
||||
}
|
||||
// } else if (newModel === model && value === chatGptLabel) {
|
||||
// // bypass if not changed
|
||||
// return;
|
||||
// } else if (newModel === 'chatgptCustom' && value === null) {
|
||||
// // return;
|
||||
// } else if (newModel !== 'chatgptCustom') {
|
||||
// newConversation({
|
||||
// model: newModel,
|
||||
// chatGptLabel: null,
|
||||
// promptPrefix: null
|
||||
// });
|
||||
// } else if (newModel === 'chatgptCustom') {
|
||||
// const targetModel = models.find(element => element.value == value);
|
||||
// if (targetModel) {
|
||||
// const chatGptLabel = targetModel?.chatGptLabel;
|
||||
// const promptPrefix = targetModel?.promptPrefix;
|
||||
// newConversation({
|
||||
// model: newModel,
|
||||
// chatGptLabel,
|
||||
// promptPrefix
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
// const onOpenChange = open => {
|
||||
// mutate();
|
||||
// if (!open) {
|
||||
// setModelSave(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
// const handleSaveState = value => {
|
||||
// if (!modelSave) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// setCustomGPTModels(value);
|
||||
// setModelSave(false);
|
||||
// };
|
||||
|
||||
// const defaultColorProps = [
|
||||
// 'text-gray-500',
|
||||
// 'hover:bg-gray-100',
|
||||
// 'hover:bg-opacity-20',
|
||||
// 'disabled:hover:bg-transparent',
|
||||
// 'dark:data-[state=open]:bg-gray-800',
|
||||
// 'dark:hover:bg-opacity-20',
|
||||
// 'dark:hover:bg-gray-900',
|
||||
// 'dark:hover:text-gray-400',
|
||||
// 'dark:disabled:hover:bg-transparent'
|
||||
// ];
|
||||
|
||||
// const chatgptColorProps = [
|
||||
// 'text-green-700',
|
||||
// 'data-[state=open]:bg-green-100',
|
||||
// 'dark:text-emerald-300',
|
||||
// 'hover:bg-green-100',
|
||||
// 'disabled:hover:bg-transparent',
|
||||
// 'dark:data-[state=open]:bg-green-900',
|
||||
// 'dark:hover:bg-opacity-50',
|
||||
// 'dark:hover:bg-green-900',
|
||||
// 'dark:hover:text-gray-100',
|
||||
// 'dark:disabled:hover:bg-transparent'
|
||||
// ];
|
||||
|
||||
// const colorProps = model === 'chatgpt' ? chatgptColorProps : defaultColorProps;
|
||||
const icon = getIcon({
|
||||
size: 32,
|
||||
...conversation,
|
||||
isCreatedByUser: false,
|
||||
error: false,
|
||||
button: true
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
// onOpenChange={onOpenChange}
|
||||
>
|
||||
<DropdownMenu
|
||||
open={menuOpen}
|
||||
onOpenChange={setMenuOpen}
|
||||
>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
// style={{backgroundColor: 'rgb(16, 163, 127)'}}
|
||||
className={`absolute top-[0.25px] mb-0 ml-1 items-center rounded-md border-0 p-1 outline-none focus:ring-0 focus:ring-offset-0 disabled:top-[0.25px] dark:data-[state=open]:bg-opacity-50 md:top-1 md:left-1 md:ml-0 md:pl-1 md:disabled:top-1`}
|
||||
>
|
||||
{icon}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="w-56 dark:bg-gray-700"
|
||||
onCloseAutoFocus={event => event.preventDefault()}
|
||||
>
|
||||
<DropdownMenuLabel className="dark:text-gray-300">Select an AI Endpoint</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuRadioGroup
|
||||
value={endpoint}
|
||||
onValueChange={onChange}
|
||||
className="overflow-y-auto"
|
||||
>
|
||||
{availableEndpoints.length ? (
|
||||
<EndpointItems
|
||||
endpoints={availableEndpoints}
|
||||
onSelect={onChange}
|
||||
/>
|
||||
) : (
|
||||
<DropdownMenuLabel className="dark:text-gray-300">No endpoint available.</DropdownMenuLabel>
|
||||
)}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
{/* <ModelDialog
|
||||
mutate={mutate}
|
||||
setModelSave={setModelSave}
|
||||
handleSaveState={handleSaveState}
|
||||
/> */}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import axios from 'axios';
|
||||
import ModelDialog from './ModelDialog';
|
||||
import MenuItems from './MenuItems';
|
||||
import { swr } from '~/utils/fetchers';
|
||||
import getIcon from '~/utils/getIcon';
|
||||
|
||||
import { Button } from '../../ui/Button.tsx';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
} from '../../ui/DropdownMenu.tsx';
|
||||
import { Dialog } from '../../ui/Dialog.tsx';
|
||||
|
||||
import store from '~/store';
|
||||
|
||||
export default function ModelMenu() {
|
||||
const [modelSave, setModelSave] = useState(false);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
|
||||
const models = useRecoilValue(store.models);
|
||||
const availableModels = useRecoilValue(store.availableModels);
|
||||
const setCustomGPTModels = useSetRecoilState(store.customGPTModels);
|
||||
|
||||
const conversation = useRecoilValue(store.conversation) || {};
|
||||
const { model, promptPrefix, chatGptLabel, conversationId } = conversation;
|
||||
const { newConversation } = store.useConversation();
|
||||
|
||||
// fetch the list of saved chatgptCustom
|
||||
const { data, isLoading, mutate } = swr(`/api/customGpts`, res => {
|
||||
const fetchedModels = res.map(modelItem => ({
|
||||
...modelItem,
|
||||
name: modelItem.chatGptLabel,
|
||||
model: 'chatgptCustom'
|
||||
}));
|
||||
|
||||
setCustomGPTModels(fetchedModels);
|
||||
});
|
||||
|
||||
// useEffect(() => {
|
||||
// mutate();
|
||||
// try {
|
||||
// const lastSelected = JSON.parse(localStorage.getItem('model'));
|
||||
|
||||
// if (lastSelected === 'chatgptCustom') {
|
||||
// return;
|
||||
// } else if (initial[lastSelected]) {
|
||||
// dispatch(setModel(lastSelected));
|
||||
// }
|
||||
// } catch (err) {
|
||||
// console.log(err);
|
||||
// }
|
||||
|
||||
// // eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// }, []);
|
||||
|
||||
// update the default model when availableModels changes
|
||||
// typically, availableModels changes => modelsFilter or customGPTModels changes
|
||||
useEffect(() => {
|
||||
if (conversationId == 'new') {
|
||||
newConversation();
|
||||
}
|
||||
}, [availableModels]);
|
||||
|
||||
// save selected model to localstoreage
|
||||
useEffect(() => {
|
||||
if (model) localStorage.setItem('model', JSON.stringify({ model, chatGptLabel, promptPrefix }));
|
||||
}, [model]);
|
||||
|
||||
// set the current model
|
||||
const onChange = (newModel, value = null) => {
|
||||
setMenuOpen(false);
|
||||
|
||||
if (!newModel) {
|
||||
return;
|
||||
} else if (newModel === model && value === chatGptLabel) {
|
||||
// bypass if not changed
|
||||
return;
|
||||
} else if (newModel === 'chatgptCustom' && value === null) {
|
||||
// return;
|
||||
} else if (newModel !== 'chatgptCustom') {
|
||||
newConversation({
|
||||
model: newModel,
|
||||
chatGptLabel: null,
|
||||
promptPrefix: null
|
||||
});
|
||||
} else if (newModel === 'chatgptCustom') {
|
||||
const targetModel = models.find(element => element.value == value);
|
||||
if (targetModel) {
|
||||
const chatGptLabel = targetModel?.chatGptLabel;
|
||||
const promptPrefix = targetModel?.promptPrefix;
|
||||
newConversation({
|
||||
model: newModel,
|
||||
chatGptLabel,
|
||||
promptPrefix
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onOpenChange = open => {
|
||||
mutate();
|
||||
if (!open) {
|
||||
setModelSave(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveState = value => {
|
||||
if (!modelSave) {
|
||||
return;
|
||||
}
|
||||
|
||||
setCustomGPTModels(value);
|
||||
setModelSave(false);
|
||||
};
|
||||
|
||||
const defaultColorProps = [
|
||||
'text-gray-500',
|
||||
'hover:bg-gray-100',
|
||||
'hover:bg-opacity-20',
|
||||
'disabled:hover:bg-transparent',
|
||||
'dark:data-[state=open]:bg-gray-800',
|
||||
'dark:hover:bg-opacity-20',
|
||||
'dark:hover:bg-gray-900',
|
||||
'dark:hover:text-gray-400',
|
||||
'dark:disabled:hover:bg-transparent'
|
||||
];
|
||||
|
||||
const chatgptColorProps = [
|
||||
'text-green-700',
|
||||
'data-[state=open]:bg-green-100',
|
||||
'dark:text-emerald-300',
|
||||
'hover:bg-green-100',
|
||||
'disabled:hover:bg-transparent',
|
||||
'dark:data-[state=open]:bg-green-900',
|
||||
'dark:hover:bg-opacity-50',
|
||||
'dark:hover:bg-green-900',
|
||||
'dark:hover:text-gray-100',
|
||||
'dark:disabled:hover:bg-transparent'
|
||||
];
|
||||
|
||||
const colorProps = model === 'chatgpt' ? chatgptColorProps : defaultColorProps;
|
||||
const icon = getIcon({
|
||||
size: 32,
|
||||
sender: chatGptLabel || model,
|
||||
isCreatedByUser: false,
|
||||
model,
|
||||
chatGptLabel,
|
||||
promptPrefix,
|
||||
error: false,
|
||||
button: true
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog onOpenChange={onOpenChange}>
|
||||
<DropdownMenu
|
||||
open={menuOpen}
|
||||
onOpenChange={setMenuOpen}
|
||||
>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
// style={{backgroundColor: 'rgb(16, 163, 127)'}}
|
||||
className={`absolute top-[0.25px] mb-0 ml-1 items-center rounded-md border-0 p-1 outline-none md:ml-0 ${colorProps.join(
|
||||
' '
|
||||
)} focus:ring-0 focus:ring-offset-0 disabled:top-[0.25px] dark:data-[state=open]:bg-opacity-50 md:top-1 md:left-1 md:pl-1 md:disabled:top-1`}
|
||||
>
|
||||
{icon}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="w-56 dark:bg-gray-700"
|
||||
onCloseAutoFocus={event => event.preventDefault()}
|
||||
>
|
||||
<DropdownMenuLabel className="dark:text-gray-300">Select a Model</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuRadioGroup
|
||||
value={chatGptLabel || model}
|
||||
onValueChange={onChange}
|
||||
className="overflow-y-auto"
|
||||
>
|
||||
{availableModels.length ? (
|
||||
<MenuItems
|
||||
models={availableModels}
|
||||
onSelect={onChange}
|
||||
/>
|
||||
) : (
|
||||
<DropdownMenuLabel className="dark:text-gray-300">No model available.</DropdownMenuLabel>
|
||||
)}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<ModelDialog
|
||||
mutate={mutate}
|
||||
setModelSave={setModelSave}
|
||||
handleSaveState={handleSaveState}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { useRecoilValue, useRecoilState } from 'recoil';
|
|||
import SubmitButton from './SubmitButton';
|
||||
import AdjustToneButton from './AdjustToneButton';
|
||||
import BingStyles from './BingStyles';
|
||||
// import ModelMenu from './Models/ModelMenu';
|
||||
import EndpointMenu from './Endpoints/EndpointMenu';
|
||||
import Footer from './Footer';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import RegenerateIcon from '../svg/RegenerateIcon';
|
||||
|
|
@ -167,7 +167,7 @@ export default function TextChat({ isSearchView = false }) {
|
|||
disabled ? 'dark:bg-gray-900' : 'dark:bg-gray-700'
|
||||
} dark:text-white dark:shadow-[0_0_15px_rgba(0,0,0,0.10)] md:py-3 md:pl-4`}
|
||||
>
|
||||
{/* <ModelMenu /> */}
|
||||
<EndpointMenu />
|
||||
<TextareaAutosize
|
||||
tabIndex="0"
|
||||
autoFocus
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue