mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 01:10:14 +01:00
* chore: remove data-provider, install npm package * chore: replace monorepo package with npm package: librechat-data-provider * chore: remove data-provider scripts * chore: remove data-provider from .eslintrc.js
245 lines
8.2 KiB
JavaScript
245 lines
8.2 KiB
JavaScript
import { useState, useEffect, memo } from 'react';
|
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
|
import { Settings2, ChevronDownIcon } from 'lucide-react';
|
|
import {
|
|
SelectDropDown,
|
|
PluginStoreDialog,
|
|
MultiSelectDropDown,
|
|
Button,
|
|
GPTIcon,
|
|
} from '~/components';
|
|
import EndpointOptionsPopover from '../../Endpoints/EndpointOptionsPopover';
|
|
import SaveAsPresetDialog from '../../Endpoints/SaveAsPresetDialog';
|
|
import { Settings, AgentSettings } from '../../Endpoints/Plugins/';
|
|
import { cn } from '~/utils/';
|
|
import store from '~/store';
|
|
import { useAuthContext } from '~/hooks/AuthContext';
|
|
import { useAvailablePluginsQuery } from 'librechat-data-provider';
|
|
|
|
function PluginsOptions() {
|
|
const { data: allPlugins } = useAvailablePluginsQuery();
|
|
const [visibile, setVisibility] = useState(true);
|
|
const [advancedMode, setAdvancedMode] = useState(false);
|
|
const [availableTools, setAvailableTools] = useState([]);
|
|
const [showAgentSettings, setShowAgentSettings] = useState(false);
|
|
const [showSavePresetDialog, setShowSavePresetDialog] = useState(false);
|
|
const [showPluginStoreDialog, setShowPluginStoreDialog] = useState(false);
|
|
const [opacityClass, setOpacityClass] = useState('full-opacity');
|
|
const [conversation, setConversation] = useRecoilState(store.conversation) || {};
|
|
const endpointsConfig = useRecoilValue(store.endpointsConfig);
|
|
const messagesTree = useRecoilValue(store.messagesTree);
|
|
const { user } = useAuthContext();
|
|
|
|
useEffect(() => {
|
|
if (advancedMode) {
|
|
return;
|
|
} else if (messagesTree?.length >= 1) {
|
|
setOpacityClass('show');
|
|
} else {
|
|
setOpacityClass('full-opacity');
|
|
}
|
|
}, [messagesTree, advancedMode]);
|
|
|
|
useEffect(() => {
|
|
if (allPlugins && user) {
|
|
const pluginStore = { name: 'Plugin store', pluginKey: 'pluginStore', isButton: true };
|
|
if (!user.plugins || user.plugins.length === 0) {
|
|
setAvailableTools([pluginStore]);
|
|
return;
|
|
}
|
|
const tools = [...user.plugins]
|
|
.map((el) => {
|
|
return allPlugins.find((plugin) => plugin.pluginKey === el);
|
|
})
|
|
.filter((el) => el);
|
|
setAvailableTools([...tools, pluginStore]);
|
|
}
|
|
}, [allPlugins, user]);
|
|
|
|
const triggerAgentSettings = () => setShowAgentSettings((prev) => !prev);
|
|
const { endpoint, agentOptions } = conversation;
|
|
|
|
if (endpoint !== 'gptPlugins') {
|
|
return null;
|
|
}
|
|
const models = endpointsConfig?.['gptPlugins']?.['availableModels'] || [];
|
|
|
|
const triggerAdvancedMode = () => setAdvancedMode((prev) => !prev);
|
|
|
|
const switchToSimpleMode = () => {
|
|
setAdvancedMode(false);
|
|
};
|
|
|
|
const saveAsPreset = () => {
|
|
setShowSavePresetDialog(true);
|
|
};
|
|
|
|
function checkIfSelected(value) {
|
|
if (!conversation.tools) {
|
|
return false;
|
|
}
|
|
return conversation.tools.find((el) => el.pluginKey === value) ? true : false;
|
|
}
|
|
|
|
const setOption = (param) => (newValue) => {
|
|
let update = {};
|
|
update[param] = newValue;
|
|
setConversation((prevState) => ({
|
|
...prevState,
|
|
...update,
|
|
}));
|
|
};
|
|
|
|
const setAgentOption = (param) => (newValue) => {
|
|
const editableConvo = JSON.stringify(conversation);
|
|
const convo = JSON.parse(editableConvo);
|
|
let { agentOptions } = convo;
|
|
agentOptions[param] = newValue;
|
|
setConversation((prevState) => ({
|
|
...prevState,
|
|
agentOptions,
|
|
}));
|
|
};
|
|
|
|
const setTools = (newValue) => {
|
|
if (newValue === 'pluginStore') {
|
|
setShowPluginStoreDialog(true);
|
|
return;
|
|
}
|
|
let update = {};
|
|
let current = conversation.tools || [];
|
|
let isSelected = checkIfSelected(newValue);
|
|
let tool = availableTools[availableTools.findIndex((el) => el.pluginKey === newValue)];
|
|
if (isSelected) {
|
|
update.tools = current.filter((el) => el.pluginKey !== newValue);
|
|
} else {
|
|
update.tools = [...current, tool];
|
|
}
|
|
localStorage.setItem('lastSelectedTools', JSON.stringify(update.tools));
|
|
setConversation((prevState) => ({
|
|
...prevState,
|
|
...update,
|
|
}));
|
|
};
|
|
|
|
const cardStyle =
|
|
'transition-colors shadow-md rounded-md min-w-[75px] font-normal bg-white border-black/10 hover:border-black/10 focus:border-black/10 dark:border-black/10 dark:hover:border-black/10 dark:focus:border-black/10 border dark:bg-gray-700 text-black dark:text-white';
|
|
|
|
return (
|
|
<>
|
|
<div
|
|
className={
|
|
'pluginOptions flex w-full flex-wrap items-center justify-center gap-2 ' +
|
|
(!advancedMode ? opacityClass : '')
|
|
}
|
|
onMouseEnter={() => {
|
|
if (advancedMode) {
|
|
return;
|
|
}
|
|
setOpacityClass('full-opacity');
|
|
}}
|
|
onMouseLeave={() => {
|
|
if (advancedMode) {
|
|
return;
|
|
}
|
|
if (!messagesTree || messagesTree.length === 0) {
|
|
return;
|
|
}
|
|
setOpacityClass('show');
|
|
}}
|
|
>
|
|
<Button
|
|
type="button"
|
|
className={cn(
|
|
cardStyle,
|
|
'min-w-4 z-40 flex h-[40px] flex-none items-center justify-center px-4 hover:bg-white focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-700',
|
|
)}
|
|
onClick={() => setVisibility((prev) => !prev)}
|
|
>
|
|
<ChevronDownIcon
|
|
className={cn(
|
|
!visibile ? 'rotate-180 transform' : '',
|
|
'w-4 text-gray-600 dark:text-white',
|
|
)}
|
|
/>
|
|
</Button>
|
|
<SelectDropDown
|
|
value={conversation.model}
|
|
setValue={setOption('model')}
|
|
availableValues={models}
|
|
showAbove={true}
|
|
className={cn(cardStyle, 'min-w-60 z-40 flex w-60', !visibile && 'hidden')}
|
|
/>
|
|
<MultiSelectDropDown
|
|
value={conversation.tools || []}
|
|
isSelected={checkIfSelected}
|
|
setSelected={setTools}
|
|
availableValues={availableTools}
|
|
optionValueKey="pluginKey"
|
|
showAbove={true}
|
|
className={cn(cardStyle, 'min-w-60 z-50 w-60', !visibile && 'hidden')}
|
|
/>
|
|
<Button
|
|
type="button"
|
|
className={cn(
|
|
cardStyle,
|
|
'min-w-4 z-50 flex h-[40px] flex-none items-center justify-center px-4 hover:bg-slate-50 focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-600',
|
|
!visibile && 'hidden',
|
|
)}
|
|
onClick={triggerAdvancedMode}
|
|
>
|
|
<Settings2 className="w-4 text-gray-600 dark:text-white" />
|
|
</Button>
|
|
</div>
|
|
<EndpointOptionsPopover
|
|
content={
|
|
<div className="px-4 py-4">
|
|
{showAgentSettings ? (
|
|
<AgentSettings
|
|
agent={agentOptions.agent}
|
|
skipCompletion={agentOptions.skipCompletion}
|
|
model={agentOptions.model}
|
|
endpoint={agentOptions.endpoint}
|
|
temperature={agentOptions.temperature}
|
|
topP={agentOptions.top_p}
|
|
freqP={agentOptions.presence_penalty}
|
|
presP={agentOptions.frequency_penalty}
|
|
setOption={setAgentOption}
|
|
tools={conversation.tools}
|
|
/>
|
|
) : (
|
|
<Settings
|
|
model={conversation.model}
|
|
endpoint={endpoint}
|
|
chatGptLabel={conversation.chatGptLabel}
|
|
promptPrefix={conversation.promptPrefix}
|
|
temperature={conversation.temperature}
|
|
topP={conversation.top_p}
|
|
freqP={conversation.presence_penalty}
|
|
presP={conversation.frequency_penalty}
|
|
setOption={setOption}
|
|
tools={conversation.tools}
|
|
/>
|
|
)}
|
|
</div>
|
|
}
|
|
visible={advancedMode}
|
|
saveAsPreset={saveAsPreset}
|
|
switchToSimpleMode={switchToSimpleMode}
|
|
additionalButton={{
|
|
label: `Show ${showAgentSettings ? 'Completion' : 'Agent'} Settings`,
|
|
handler: triggerAgentSettings,
|
|
icon: <GPTIcon className="mr-1 mt-[2px] w-[14px]" size={14} />,
|
|
}}
|
|
/>
|
|
<SaveAsPresetDialog
|
|
open={showSavePresetDialog}
|
|
onOpenChange={setShowSavePresetDialog}
|
|
preset={conversation}
|
|
/>
|
|
<PluginStoreDialog isOpen={showPluginStoreDialog} setIsOpen={setShowPluginStoreDialog} />
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default memo(PluginsOptions);
|