mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 10:20:15 +01:00
WIP: Update UI to match Official Style; Vision and Assistants 👷🏽 (#1190)
* wip: initial client side code * wip: initial api code * refactor: export query keys from own module, export assistant hooks * refactor(SelectDropDown): more customization via props * feat: create Assistant and render real Assistants * refactor: major refactor of UI components to allow multi-chat, working alongside CreationPanel * refactor: move assistant routes to own directory * fix(CreationHeader): state issue with assistant select * refactor: style changes for form, fix setSiblingIdx from useChatHelpers to use latestMessageParentId, fix render issue with ChatView and change location * feat: parseCompactConvo: begin refactor of slimmer JSON payloads between client/api * refactor(endpoints): add assistant endpoint, also use EModelEndpoint as much as possible * refactor(useGetConversationsQuery): use object to access query data easily * fix(MultiMessage): react warning of bad state set, making use of effect during render (instead of useEffect) * fix(useNewConvo): use correct atom key (index instead of convoId) for reset latestMessageFamily * refactor: make routing navigation/conversation change simpler * chore: add removeNullishValues for smaller payloads, remove unused fields, setup frontend pinging of assistant endpoint * WIP: initial complete assistant run handling * fix: CreationPanel form correctly setting internal state * refactor(api/assistants/chat): revise functions to working run handling strategy * refactor(UI): initial major refactor of ChatForm and options * feat: textarea hook * refactor: useAuthRedirect hook and change directory name * feat: add ChatRoute (/c/), make optionsBar absolute and change on textarea height, add temp header * feat: match new toggle Nav open button to ChatGPT's * feat: add OpenAI custom classnames * feat: useOriginNavigate * feat: messages loading view * fix: conversation navigation and effects * refactor: make toggle change nav opacity * WIP: new endpoint menu * feat: NewEndpointsMenu complete * fix: ensure set key dialog shows on endpoint change, and new conversation resets messages * WIP: textarea styling fix, add temp footer, create basic file handling component * feat: image file handling (UI) * feat: PopOver and ModelSelect in Header, remove GenButtons * feat: drop file handling * refactor: bug fixes use SSE at route level add opts to useOriginNavigate delay render of unfinishedMessage to avoid flickering pass params (convoId) to chatHelpers to set messages query data based on param when the route is new (fixes can't continue convo on /new/) style(MessagesView): matches height to official fix(SSE): pass paramId and invalidate convos style(Message): make bg uniform * refactor(useSSE): setStorage within setConversation updates * feat: conversationKeysAtom, allConversationsSelector, update convos query data on created message (if new), correctly handle convo deletion (individual) * feat: add popover select dropdowns to allow options in header while allowing horizontal scroll for mobile * style(pluginsSelect): styling changes * refactor(NewEndpointsMenu): make UI components modular * feat: Presets complete * fix: preset editing, make by index * fix: conversations not setting on inital navigation, fix getMessages() based on query param * fix: changing preset no longer resets latestMessage * feat: useOnClickOutside for OptionsPopover and fix bug that causes selection of preset when deleting * fix: revert /chat/ switchToConvo, also use NewDeleteButton in Convo * fix: Popover correctly closes on close Popover button using custom condition for useOnClickOutside * style: new message and nav styling * style: hover/sibling buttons and preset menu scrolling * feat: new convo header button * style(Textarea): minor style changes to textarea buttons * feat: stop/continue generating and hide hoverbuttons when submitting * feat: compact AI Provider schemas to make json payloads and db saves smaller * style: styling changes for consistency on chat route * fix: created usePresetIndexOptions to prevent bugs between /c/ and /chat/ routes when editing presets, removed redundant code from the new dialog * chore: make /chat/ route default for now since we still lack full image support
This commit is contained in:
parent
adbeb46399
commit
bac1fb67d2
171 changed files with 8380 additions and 468 deletions
|
|
@ -4,9 +4,9 @@ import { Settings } from 'lucide-react';
|
|||
import { DropdownMenuRadioItem } from '~/components';
|
||||
import { Icon } from '~/components/Endpoints';
|
||||
import { SetKeyDialog } from '../SetKeyDialog';
|
||||
import { alternateName } from '~/common';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
import { cn, alternateName } from '~/utils';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
export default function ModelItem({
|
||||
endpoint,
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export default function NewConversationMenu() {
|
|||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [showPresets, setShowPresets] = useState(true);
|
||||
const [showEndpoints, setShowEndpoints] = useState(true);
|
||||
const [presetModelVisible, setPresetModelVisible] = useState(false);
|
||||
const [presetModalVisible, setPresetModalVisible] = useState(false);
|
||||
const [preset, setPreset] = useState(false);
|
||||
const [conversation, setConversation] = useRecoilState(store.conversation) ?? {};
|
||||
const [messages, setMessages] = useRecoilState(store.messages);
|
||||
|
|
@ -131,7 +131,7 @@ export default function NewConversationMenu() {
|
|||
};
|
||||
|
||||
const onChangePreset = (preset) => {
|
||||
setPresetModelVisible(true);
|
||||
setPresetModalVisible(true);
|
||||
setPreset(preset);
|
||||
};
|
||||
|
||||
|
|
@ -269,8 +269,8 @@ export default function NewConversationMenu() {
|
|||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<EditPresetDialog
|
||||
open={presetModelVisible}
|
||||
onOpenChange={setPresetModelVisible}
|
||||
open={presetModalVisible}
|
||||
onOpenChange={setPresetModalVisible}
|
||||
preset={preset}
|
||||
/>
|
||||
</Dialog>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { cn } from '~/utils/';
|
|||
import { useLocalize } from '~/hooks';
|
||||
|
||||
type FileUploadProps = {
|
||||
onFileSelected: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onFileSelected: (jsonData: Record<string, unknown>) => void;
|
||||
className?: string;
|
||||
successText?: string;
|
||||
invalidText?: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { TPresetItemProps } from '~/common';
|
||||
import type { TPreset } from 'librechat-data-provider';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { DropdownMenuRadioItem, EditIcon, TrashIcon } from '~/components';
|
||||
import { Icon } from '~/components/Endpoints';
|
||||
|
||||
|
|
@ -24,32 +25,32 @@ export default function PresetItem({
|
|||
let _title = `${endpoint}`;
|
||||
const { chatGptLabel, modelLabel, model, jailbreak, toneStyle } = preset;
|
||||
|
||||
if (endpoint === 'azureOpenAI' || endpoint === 'openAI') {
|
||||
if (endpoint === EModelEndpoint.azureOpenAI || endpoint === EModelEndpoint.openAI) {
|
||||
if (model) {
|
||||
_title += `: ${model}`;
|
||||
}
|
||||
if (chatGptLabel) {
|
||||
_title += ` as ${chatGptLabel}`;
|
||||
}
|
||||
} else if (endpoint === 'google') {
|
||||
} else if (endpoint === EModelEndpoint.google) {
|
||||
if (model) {
|
||||
_title += `: ${model}`;
|
||||
}
|
||||
if (modelLabel) {
|
||||
_title += ` as ${modelLabel}`;
|
||||
}
|
||||
} else if (endpoint === 'bingAI') {
|
||||
} else if (endpoint === EModelEndpoint.bingAI) {
|
||||
if (toneStyle) {
|
||||
_title += `: ${toneStyle}`;
|
||||
}
|
||||
if (jailbreak) {
|
||||
_title += ' as Sydney';
|
||||
}
|
||||
} else if (endpoint === 'chatGPTBrowser') {
|
||||
} else if (endpoint === EModelEndpoint.chatGPTBrowser) {
|
||||
if (model) {
|
||||
_title += `: ${model}`;
|
||||
}
|
||||
} else if (endpoint === 'gptPlugins') {
|
||||
} else if (endpoint === EModelEndpoint.gptPlugins) {
|
||||
if (model) {
|
||||
_title += `: ${model}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
import { SelectDropDown } from '~/components/ui';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
import { SelectDropDown, SelectDropDownPop } from '~/components/ui';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
|
||||
export default function Anthropic({ conversation, setOption, models }: TModelSelectProps) {
|
||||
export default function Anthropic({
|
||||
conversation,
|
||||
setOption,
|
||||
models,
|
||||
showAbove,
|
||||
popover = false,
|
||||
}: TModelSelectProps) {
|
||||
const Menu = popover ? SelectDropDownPop : SelectDropDown;
|
||||
return (
|
||||
<SelectDropDown
|
||||
<Menu
|
||||
value={conversation?.model ?? ''}
|
||||
setValue={setOption('model')}
|
||||
availableValues={models}
|
||||
showAbove={true}
|
||||
showAbove={showAbove}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import { SelectDropDown, Tabs, TabsList, TabsTrigger } from '~/components/ui';
|
||||
import { SelectDropDown, SelectDropDownPop, Tabs, TabsList, TabsTrigger } from '~/components/ui';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import store from '~/store';
|
||||
|
||||
export default function BingAI({ conversation, setOption, models }: TModelSelectProps) {
|
||||
export default function BingAI({
|
||||
conversation,
|
||||
setOption,
|
||||
models,
|
||||
showAbove,
|
||||
popover = false,
|
||||
}: TModelSelectProps) {
|
||||
// TODO: index family bing tone settings, important for multiview
|
||||
const showBingToneSetting = useRecoilValue(store.showBingToneSetting);
|
||||
if (!conversation) {
|
||||
return null;
|
||||
|
|
@ -21,16 +28,17 @@ export default function BingAI({ conversation, setOption, models }: TModelSelect
|
|||
'font-medium data-[state=active]:text-white text-xs text-white',
|
||||
);
|
||||
const selectedClass = (val: string) => val + '-tab ' + defaultSelected;
|
||||
const Menu = popover ? SelectDropDownPop : SelectDropDown;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SelectDropDown
|
||||
<Menu
|
||||
title="Mode"
|
||||
value={jailbreak ? 'Sydney' : 'BingAI'}
|
||||
data-testid="bing-select-dropdown"
|
||||
setValue={(value) => setOption('jailbreak')(value === 'Sydney')}
|
||||
availableValues={models}
|
||||
showAbove={true}
|
||||
showAbove={showAbove}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
import { SelectDropDown } from '~/components/ui';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
import { SelectDropDown, SelectDropDownPop } from '~/components/ui';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
|
||||
export default function ChatGPT({ conversation, setOption, models }: TModelSelectProps) {
|
||||
export default function ChatGPT({
|
||||
conversation,
|
||||
setOption,
|
||||
models,
|
||||
showAbove,
|
||||
popover = false,
|
||||
}: TModelSelectProps) {
|
||||
if (!conversation) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -10,13 +16,13 @@ export default function ChatGPT({ conversation, setOption, models }: TModelSelec
|
|||
if (conversationId !== 'new') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const Menu = popover ? SelectDropDownPop : SelectDropDown;
|
||||
return (
|
||||
<SelectDropDown
|
||||
<Menu
|
||||
value={model ?? ''}
|
||||
setValue={setOption('model')}
|
||||
availableValues={models}
|
||||
showAbove={true}
|
||||
showAbove={showAbove}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
import { SelectDropDown } from '~/components/ui';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
import { SelectDropDown, SelectDropDownPop } from '~/components/ui';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
|
||||
export default function Google({ conversation, setOption, models }: TModelSelectProps) {
|
||||
export default function Google({
|
||||
conversation,
|
||||
setOption,
|
||||
models,
|
||||
showAbove,
|
||||
popover = false,
|
||||
}: TModelSelectProps) {
|
||||
const Menu = popover ? SelectDropDownPop : SelectDropDown;
|
||||
return (
|
||||
<SelectDropDown
|
||||
<Menu
|
||||
value={conversation?.model ?? ''}
|
||||
setValue={setOption('model')}
|
||||
availableValues={models}
|
||||
showAbove={true}
|
||||
showAbove={showAbove}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,7 @@
|
|||
import React from 'react';
|
||||
import OpenAI from './OpenAI';
|
||||
import BingAI from './BingAI';
|
||||
import Google from './Google';
|
||||
import Plugins from './Plugins';
|
||||
import ChatGPT from './ChatGPT';
|
||||
import Anthropic from './Anthropic';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import type { TConversation } from 'librechat-data-provider';
|
||||
import type { TSetOption, TModelSelectProps } from '~/common';
|
||||
import type { TSetOption } from '~/common';
|
||||
import { options, multiChatOptions } from './options';
|
||||
import store from '~/store';
|
||||
|
||||
type TGoogleProps = {
|
||||
|
|
@ -19,31 +13,36 @@ type TSelectProps = {
|
|||
conversation: TConversation | null;
|
||||
setOption: TSetOption;
|
||||
extraProps?: TGoogleProps;
|
||||
isMultiChat?: boolean;
|
||||
showAbove?: boolean;
|
||||
};
|
||||
|
||||
const optionComponents: { [key: string]: React.FC<TModelSelectProps> } = {
|
||||
openAI: OpenAI,
|
||||
azureOpenAI: OpenAI,
|
||||
bingAI: BingAI,
|
||||
google: Google,
|
||||
gptPlugins: Plugins,
|
||||
anthropic: Anthropic,
|
||||
chatGPTBrowser: ChatGPT,
|
||||
};
|
||||
|
||||
export default function ModelSelect({ conversation, setOption }: TSelectProps) {
|
||||
export default function ModelSelect({
|
||||
conversation,
|
||||
setOption,
|
||||
isMultiChat = false,
|
||||
showAbove = true,
|
||||
}: TSelectProps) {
|
||||
const modelsConfig = useRecoilValue(store.modelsConfig);
|
||||
if (!conversation?.endpoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { endpoint } = conversation;
|
||||
const OptionComponent = optionComponents[endpoint];
|
||||
const OptionComponent = isMultiChat ? multiChatOptions[endpoint] : options[endpoint];
|
||||
const models = modelsConfig?.[endpoint] ?? [];
|
||||
|
||||
if (!OptionComponent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <OptionComponent conversation={conversation} setOption={setOption} models={models} />;
|
||||
return (
|
||||
<OptionComponent
|
||||
conversation={conversation}
|
||||
setOption={setOption}
|
||||
models={models}
|
||||
showAbove={showAbove}
|
||||
popover={isMultiChat}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
import { SelectDropDown } from '~/components/ui';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
import { SelectDropDown, SelectDropDownPop } from '~/components/ui';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
|
||||
export default function OpenAI({ conversation, setOption, models }: TModelSelectProps) {
|
||||
export default function OpenAI({
|
||||
conversation,
|
||||
setOption,
|
||||
models,
|
||||
showAbove = true,
|
||||
popover = false,
|
||||
}: TModelSelectProps) {
|
||||
const Menu = popover ? SelectDropDownPop : SelectDropDown;
|
||||
return (
|
||||
<SelectDropDown
|
||||
<Menu
|
||||
value={conversation?.model ?? ''}
|
||||
setValue={setOption('model')}
|
||||
availableValues={models}
|
||||
showAbove={true}
|
||||
showAbove={showAbove}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useState, useEffect } from 'react';
|
|||
import { ChevronDownIcon } from 'lucide-react';
|
||||
import { useAvailablePluginsQuery, TPlugin } from 'librechat-data-provider';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import { SelectDropDown, MultiSelectDropDown, Button } from '~/components/ui';
|
||||
import { SelectDropDown, MultiSelectDropDown, SelectDropDownPop, Button } from '~/components/ui';
|
||||
import { useSetOptions, useAuthContext, useMediaQuery } from '~/hooks';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
import store from '~/store';
|
||||
|
|
@ -18,13 +18,20 @@ const pluginStore: TPlugin = {
|
|||
authenticated: false,
|
||||
};
|
||||
|
||||
export default function Plugins({ conversation, setOption, models }: TModelSelectProps) {
|
||||
export default function Plugins({
|
||||
conversation,
|
||||
setOption,
|
||||
models,
|
||||
showAbove,
|
||||
popover = false,
|
||||
}: TModelSelectProps) {
|
||||
const { data: allPlugins } = useAvailablePluginsQuery();
|
||||
const [visible, setVisibility] = useState<boolean>(true);
|
||||
const [availableTools, setAvailableTools] = useRecoilState(store.availableTools);
|
||||
const { checkPluginSelection, setTools } = useSetOptions();
|
||||
const { user } = useAuthContext();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 640px)');
|
||||
const Menu = popover ? SelectDropDownPop : SelectDropDown;
|
||||
|
||||
useEffect(() => {
|
||||
if (isSmallScreen) {
|
||||
|
|
@ -87,11 +94,11 @@ export default function Plugins({ conversation, setOption, models }: TModelSelec
|
|||
)}
|
||||
/>
|
||||
</Button>
|
||||
<SelectDropDown
|
||||
<Menu
|
||||
value={conversation.model ?? ''}
|
||||
setValue={setOption('model')}
|
||||
availableValues={models}
|
||||
showAbove={true}
|
||||
showAbove={showAbove}
|
||||
className={cn(cardStyle, 'min-w-60 z-40 flex w-64 sm:w-48', visible ? '' : 'hidden')}
|
||||
/>
|
||||
<MultiSelectDropDown
|
||||
|
|
@ -100,7 +107,7 @@ export default function Plugins({ conversation, setOption, models }: TModelSelec
|
|||
setSelected={setTools}
|
||||
availableValues={availableTools}
|
||||
optionValueKey="pluginKey"
|
||||
showAbove={true}
|
||||
showAbove={showAbove}
|
||||
className={cn(cardStyle, 'min-w-60 z-50 w-64 sm:w-48', visible ? '' : 'hidden')}
|
||||
/>
|
||||
</>
|
||||
|
|
|
|||
125
client/src/components/Input/ModelSelect/PluginsByIndex.tsx
Normal file
125
client/src/components/Input/ModelSelect/PluginsByIndex.tsx
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
import { useRecoilState } from 'recoil';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { ChevronDownIcon } from 'lucide-react';
|
||||
import { useAvailablePluginsQuery, TPlugin } from 'librechat-data-provider';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import {
|
||||
SelectDropDown,
|
||||
SelectDropDownPop,
|
||||
MultiSelectDropDown,
|
||||
MultiSelectPop,
|
||||
Button,
|
||||
} from '~/components/ui';
|
||||
import { useSetIndexOptions, useAuthContext, useMediaQuery } from '~/hooks';
|
||||
import { cn, cardStyle } from '~/utils/';
|
||||
import store from '~/store';
|
||||
|
||||
const pluginStore: TPlugin = {
|
||||
name: 'Plugin store',
|
||||
pluginKey: 'pluginStore',
|
||||
isButton: true,
|
||||
description: '',
|
||||
icon: '',
|
||||
authConfig: [],
|
||||
authenticated: false,
|
||||
};
|
||||
|
||||
export default function PluginsByIndex({
|
||||
conversation,
|
||||
setOption,
|
||||
models,
|
||||
showAbove,
|
||||
popover = false,
|
||||
}: TModelSelectProps) {
|
||||
const { data: allPlugins } = useAvailablePluginsQuery();
|
||||
const [visible, setVisibility] = useState<boolean>(true);
|
||||
const [availableTools, setAvailableTools] = useRecoilState(store.availableTools);
|
||||
const { checkPluginSelection, setTools } = useSetIndexOptions();
|
||||
const { user } = useAuthContext();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 640px)');
|
||||
|
||||
useEffect(() => {
|
||||
if (isSmallScreen) {
|
||||
setVisibility(false);
|
||||
}
|
||||
}, [isSmallScreen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allPlugins) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user.plugins || user.plugins.length === 0) {
|
||||
setAvailableTools([pluginStore]);
|
||||
return;
|
||||
}
|
||||
|
||||
const tools = [...user.plugins]
|
||||
.map((el) => allPlugins.find((plugin: TPlugin) => plugin.pluginKey === el))
|
||||
.filter((el): el is TPlugin => el !== undefined);
|
||||
|
||||
/* Filter Last Selected Tools */
|
||||
const localStorageItem = localStorage.getItem('lastSelectedTools');
|
||||
if (!localStorageItem) {
|
||||
return setAvailableTools([...tools, pluginStore]);
|
||||
}
|
||||
const lastSelectedTools = JSON.parse(localStorageItem);
|
||||
const filteredTools = lastSelectedTools.filter((tool: TPlugin) =>
|
||||
tools.some((existingTool) => existingTool.pluginKey === tool.pluginKey),
|
||||
);
|
||||
localStorage.setItem('lastSelectedTools', JSON.stringify(filteredTools));
|
||||
|
||||
setAvailableTools([...tools, pluginStore]);
|
||||
// setAvailableTools is a recoil state setter, so it's safe to use it in useEffect
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [allPlugins, user]);
|
||||
|
||||
if (!conversation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const Menu = popover ? SelectDropDownPop : SelectDropDown;
|
||||
const PluginsMenu = popover ? MultiSelectPop : MultiSelectDropDown;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
type="button"
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'min-w-4 z-40 flex h-[40px] flex-none items-center justify-center px-3 hover:bg-white focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-700',
|
||||
)}
|
||||
onClick={() => setVisibility((prev) => !prev)}
|
||||
>
|
||||
<ChevronDownIcon
|
||||
className={cn(
|
||||
!visible ? '' : 'rotate-180 transform',
|
||||
'w-4 text-gray-600 dark:text-white',
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
<Menu
|
||||
value={conversation.model ?? ''}
|
||||
setValue={setOption('model')}
|
||||
availableValues={models}
|
||||
showAbove={showAbove}
|
||||
showLabel={false}
|
||||
className={cn(cardStyle, 'min-w-60 z-40 flex w-64 sm:w-48 ', visible ? '' : 'hidden')}
|
||||
/>
|
||||
<PluginsMenu
|
||||
value={conversation.tools || []}
|
||||
isSelected={checkPluginSelection}
|
||||
setSelected={setTools}
|
||||
availableValues={availableTools}
|
||||
optionValueKey="pluginKey"
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
className={cn(cardStyle, 'min-w-60 z-50 w-64 sm:w-48 ', visible ? '' : 'hidden')}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
26
client/src/components/Input/ModelSelect/options.ts
Normal file
26
client/src/components/Input/ModelSelect/options.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import type { FC } from 'react';
|
||||
|
||||
import OpenAI from './OpenAI';
|
||||
import BingAI from './BingAI';
|
||||
import Google from './Google';
|
||||
import Plugins from './Plugins';
|
||||
import ChatGPT from './ChatGPT';
|
||||
import Anthropic from './Anthropic';
|
||||
import PluginsByIndex from './PluginsByIndex';
|
||||
|
||||
export const options: { [key: string]: FC<TModelSelectProps> } = {
|
||||
[EModelEndpoint.openAI]: OpenAI,
|
||||
[EModelEndpoint.azureOpenAI]: OpenAI,
|
||||
[EModelEndpoint.bingAI]: BingAI,
|
||||
[EModelEndpoint.google]: Google,
|
||||
[EModelEndpoint.gptPlugins]: Plugins,
|
||||
[EModelEndpoint.anthropic]: Anthropic,
|
||||
[EModelEndpoint.chatGPTBrowser]: ChatGPT,
|
||||
};
|
||||
|
||||
export const multiChatOptions = {
|
||||
...options,
|
||||
[EModelEndpoint.gptPlugins]: PluginsByIndex,
|
||||
};
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
import { Settings2 } from 'lucide-react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { useRecoilValue, useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { tPresetSchema } from 'librechat-data-provider';
|
||||
import { tPresetSchema, EModelEndpoint } from 'librechat-data-provider';
|
||||
import { PluginStoreDialog } from '~/components';
|
||||
import {
|
||||
PopoverButtons,
|
||||
EndpointSettings,
|
||||
SaveAsPresetDialog,
|
||||
EndpointOptionsPopover,
|
||||
|
|
@ -40,8 +41,8 @@ export default function OptionsBar() {
|
|||
|
||||
const noSettings = useMemo<{ [key: string]: boolean }>(
|
||||
() => ({
|
||||
chatGPTBrowser: true,
|
||||
bingAI: jailbreak ? false : conversationId !== 'new',
|
||||
[EModelEndpoint.chatGPTBrowser]: true,
|
||||
[EModelEndpoint.bingAI]: jailbreak ? false : conversationId !== 'new',
|
||||
}),
|
||||
[jailbreak, conversationId],
|
||||
);
|
||||
|
|
@ -133,10 +134,10 @@ export default function OptionsBar() {
|
|||
)}
|
||||
</div>
|
||||
<EndpointOptionsPopover
|
||||
endpoint={endpoint}
|
||||
visible={showPopover}
|
||||
saveAsPreset={saveAsPreset}
|
||||
closePopover={() => setShowPopover(false)}
|
||||
PopoverButtons={<PopoverButtons endpoint={endpoint} />}
|
||||
>
|
||||
<div className="px-4 py-4">
|
||||
<EndpointSettings conversation={conversation} setOption={setOption} />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react';
|
||||
import { memo } from 'react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
function HelpText({ endpoint }: { endpoint: string }) {
|
||||
const localize = useLocalize();
|
||||
const textMap = {
|
||||
bingAI: (
|
||||
[EModelEndpoint.bingAI]: (
|
||||
<small className="break-all text-gray-600">
|
||||
{localize('com_endpoint_config_key_get_edge_key')}{' '}
|
||||
<a
|
||||
|
|
@ -28,7 +29,7 @@ function HelpText({ endpoint }: { endpoint: string }) {
|
|||
{localize('com_endpoint_config_key_edge_full_token_string')}
|
||||
</small>
|
||||
),
|
||||
chatGPTBrowser: (
|
||||
[EModelEndpoint.chatGPTBrowser]: (
|
||||
<small className="break-all text-gray-600">
|
||||
{localize('com_endpoint_config_key_chatgpt')}{' '}
|
||||
<a
|
||||
|
|
@ -53,7 +54,7 @@ function HelpText({ endpoint }: { endpoint: string }) {
|
|||
{localize('com_endpoint_config_key_chatgpt_copy_token')}
|
||||
</small>
|
||||
),
|
||||
google: (
|
||||
[EModelEndpoint.google]: (
|
||||
<small className="break-all text-gray-600">
|
||||
{localize('com_endpoint_config_key_google_need_to')}{' '}
|
||||
<a
|
||||
|
|
@ -82,4 +83,4 @@ function HelpText({ endpoint }: { endpoint: string }) {
|
|||
return textMap[endpoint] || null;
|
||||
}
|
||||
|
||||
export default React.memo(HelpText);
|
||||
export default memo(HelpText);
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
import React, { useState } from 'react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { TDialogProps } from '~/common';
|
||||
import { Dialog, Dropdown } from '~/components/ui';
|
||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import { RevokeKeysButton } from '~/components/Nav';
|
||||
import { cn, alternateName } from '~/utils';
|
||||
import { Dialog, Dropdown } from '~/components/ui';
|
||||
import { useUserKey, useLocalize } from '~/hooks';
|
||||
import GoogleConfig from './GoogleConfig';
|
||||
import OpenAIConfig from './OpenAIConfig';
|
||||
import { alternateName } from '~/common';
|
||||
import OtherConfig from './OtherConfig';
|
||||
import HelpText from './HelpText';
|
||||
|
||||
const endpointComponents = {
|
||||
google: GoogleConfig,
|
||||
openAI: OpenAIConfig,
|
||||
azureOpenAI: OpenAIConfig,
|
||||
gptPlugins: OpenAIConfig,
|
||||
[EModelEndpoint.google]: GoogleConfig,
|
||||
[EModelEndpoint.openAI]: OpenAIConfig,
|
||||
[EModelEndpoint.azureOpenAI]: OpenAIConfig,
|
||||
[EModelEndpoint.gptPlugins]: OpenAIConfig,
|
||||
default: OtherConfig,
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue