mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
🪦 refactor: Remove Legacy Code (#10533)
* 🗑️ chore: Remove unused Legacy Provider clients and related helpers * Deleted OpenAIClient and GoogleClient files along with their associated tests. * Removed references to these clients in the clients index file. * Cleaned up typedefs by removing the OpenAISpecClient export. * Updated chat controllers to use the OpenAI SDK directly instead of the removed client classes. * chore/remove-openapi-specs * 🗑️ chore: Remove unused mergeSort and misc utility functions * Deleted mergeSort.js and misc.js files as they are no longer needed. * Removed references to cleanUpPrimaryKeyValue in messages.js and adjusted related logic. * Updated mongoMeili.ts to eliminate local implementations of removed functions. * chore: remove legacy endpoints * chore: remove all plugins endpoint related code * chore: remove unused prompt handling code and clean up imports * Deleted handleInputs.js and instructions.js files as they are no longer needed. * Removed references to these files in the prompts index.js. * Updated docker-compose.yml to simplify reverse proxy configuration. * chore: remove unused LightningIcon import from Icons.tsx * chore: clean up translation.json by removing deprecated and unused keys * chore: update Jest configuration and remove unused mock file * Simplified the setupFiles array in jest.config.js by removing the fetchEventSource mock. * Deleted the fetchEventSource.js mock file as it is no longer needed. * fix: simplify endpoint type check in Landing and ConversationStarters components * Updated the endpoint type check to use strict equality for better clarity and performance. * Ensured consistency in the handling of the azureOpenAI endpoint across both components. * chore: remove unused dependencies from package.json and package-lock.json * chore: remove legacy EditController, associated routes and imports * chore: update banResponse logic to refine request handling for banned users * chore: remove unused validateEndpoint middleware and its references * chore: remove unused 'res' parameter from initializeClient in multiple endpoint files * chore: remove unused 'isSmallScreen' prop from BookmarkNav and NewChat components; clean up imports in ArchivedChatsTable and useSetIndexOptions hooks; enhance localization in PromptVersions * chore: remove unused import of Constants and TMessage from MobileNav; retain only necessary QueryKeys import * chore: remove unused TResPlugin type and related references; clean up imports in types and schemas
This commit is contained in:
parent
b6dcefc53a
commit
656e1abaea
161 changed files with 256 additions and 10513 deletions
|
|
@ -323,10 +323,6 @@ export type TSetOptionsPayload = {
|
|||
setExample: TSetExample;
|
||||
addExample: () => void;
|
||||
removeExample: () => void;
|
||||
setAgentOption: TSetOption;
|
||||
// getConversation: () => t.TConversation | t.TPreset | null;
|
||||
checkPluginSelection: (value: string) => boolean;
|
||||
setTools: (newValue: string, remove?: boolean) => void;
|
||||
setOptions?: TSetOptions;
|
||||
};
|
||||
|
||||
|
|
@ -447,7 +443,7 @@ export type TDialogProps = {
|
|||
onOpenChange: (open: boolean) => void;
|
||||
};
|
||||
|
||||
export type TPluginStoreDialogProps = {
|
||||
export type ToolDialogProps = {
|
||||
isOpen: boolean;
|
||||
setIsOpen: (open: boolean) => void;
|
||||
};
|
||||
|
|
@ -602,7 +598,6 @@ export type NewConversationParams = {
|
|||
export type ConvoGenerator = (params: NewConversationParams) => void | t.TConversation;
|
||||
|
||||
export type TBaseResData = {
|
||||
plugin?: t.TResPlugin;
|
||||
final?: boolean;
|
||||
initial?: boolean;
|
||||
previousMessages?: t.TMessage[];
|
||||
|
|
|
|||
|
|
@ -13,13 +13,7 @@ const ConversationStarters = () => {
|
|||
|
||||
const endpointType = useMemo(() => {
|
||||
let ep = conversation?.endpoint ?? '';
|
||||
if (
|
||||
[
|
||||
EModelEndpoint.chatGPTBrowser,
|
||||
EModelEndpoint.azureOpenAI,
|
||||
EModelEndpoint.gptPlugins,
|
||||
].includes(ep as EModelEndpoint)
|
||||
) {
|
||||
if (ep === EModelEndpoint.azureOpenAI) {
|
||||
ep = EModelEndpoint.openAI;
|
||||
}
|
||||
return getIconEndpoint({
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useRecoilState } from 'recoil';
|
||||
import { EModelEndpoint, SettingsViews } from 'librechat-data-provider';
|
||||
import { Button, MessagesSquared, GPTIcon, AssistantIcon, DataIcon } from '@librechat/client';
|
||||
import { Button, MessagesSquared, AssistantIcon, DataIcon } from '@librechat/client';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useChatContext } from '~/Providers';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { cn } from '~/utils/';
|
||||
import { cn } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
type TPopoverButton = {
|
||||
|
|
@ -28,14 +28,8 @@ export default function PopoverButtons({
|
|||
endpointType?: EModelEndpoint | string | null;
|
||||
model?: string | null;
|
||||
}) {
|
||||
const {
|
||||
conversation,
|
||||
optionSettings,
|
||||
setOptionSettings,
|
||||
showAgentSettings,
|
||||
setShowAgentSettings,
|
||||
} = useChatContext();
|
||||
const localize = useLocalize();
|
||||
const { conversation, optionSettings, setOptionSettings } = useChatContext();
|
||||
const [settingsView, setSettingsView] = useRecoilState(store.currentSettingsView);
|
||||
|
||||
const { model: _model, endpoint: _endpoint, endpointType } = conversation ?? {};
|
||||
|
|
@ -64,19 +58,6 @@ export default function PopoverButtons({
|
|||
icon: <MessagesSquared className={cn('mr-1 w-[14px]', iconClass)} />,
|
||||
},
|
||||
],
|
||||
[EModelEndpoint.gptPlugins]: [
|
||||
{
|
||||
label: localize(
|
||||
showAgentSettings ? 'com_show_completion_settings' : 'com_show_agent_settings',
|
||||
),
|
||||
buttonClass: '',
|
||||
handler: () => {
|
||||
setSettingsView(SettingsViews.default);
|
||||
setShowAgentSettings((prev) => !prev);
|
||||
},
|
||||
icon: <GPTIcon className={cn('mr-1 w-[14px]', iconClass)} size={24} />,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
if (!endpoint) {
|
||||
|
|
|
|||
|
|
@ -43,13 +43,7 @@ export default function Landing({ centerFormOnLanding }: { centerFormOnLanding:
|
|||
|
||||
const endpointType = useMemo(() => {
|
||||
let ep = conversation?.endpoint ?? '';
|
||||
if (
|
||||
[
|
||||
EModelEndpoint.chatGPTBrowser,
|
||||
EModelEndpoint.azureOpenAI,
|
||||
EModelEndpoint.gptPlugins,
|
||||
].includes(ep as EModelEndpoint)
|
||||
) {
|
||||
if (ep === EModelEndpoint.azureOpenAI) {
|
||||
ep = EModelEndpoint.openAI;
|
||||
}
|
||||
return getIconEndpoint({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useMemo } from 'react';
|
||||
import { SettingsIcon } from 'lucide-react';
|
||||
import { TooltipAnchor, Spinner } from '@librechat/client';
|
||||
import { Spinner } from '@librechat/client';
|
||||
import { EModelEndpoint, isAgentsEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { TModelSpec } from 'librechat-data-provider';
|
||||
import type { Endpoint } from '~/common';
|
||||
|
|
@ -82,7 +82,10 @@ export function EndpointItem({ endpoint }: EndpointItemProps) {
|
|||
}, [modelSpecs, endpoint.value]);
|
||||
|
||||
const searchValue = endpointSearchValues[endpoint.value] || '';
|
||||
const isUserProvided = useMemo(() => endpointRequiresUserKey(endpoint.value), [endpoint.value]);
|
||||
const isUserProvided = useMemo(
|
||||
() => endpointRequiresUserKey(endpoint.value),
|
||||
[endpointRequiresUserKey, endpoint.value],
|
||||
);
|
||||
|
||||
const renderIconLabel = () => (
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -99,18 +102,6 @@ export function EndpointItem({ endpoint }: EndpointItemProps) {
|
|||
>
|
||||
{endpoint.label}
|
||||
</span>
|
||||
{/* TODO: remove this after deprecation */}
|
||||
{endpoint.value === 'gptPlugins' && (
|
||||
<TooltipAnchor
|
||||
description={localize('com_endpoint_deprecated_info')}
|
||||
aria-label={localize('com_endpoint_deprecated_info_a11y')}
|
||||
render={
|
||||
<span className="ml-2 rounded bg-amber-600/70 px-2 py-0.5 text-xs font-semibold text-white">
|
||||
{localize('com_endpoint_deprecated')}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const EditPresetDialog = ({
|
|||
const localize = useLocalize();
|
||||
const queryClient = useQueryClient();
|
||||
const { preset, setPreset } = useChatContext();
|
||||
const { setOption, setOptions, setAgentOption } = useSetIndexOptions(preset);
|
||||
const { setOption, setOptions } = useSetIndexOptions(preset);
|
||||
const [onTitleChange, title] = useDebouncedInput({
|
||||
setOption,
|
||||
optionKey: 'title',
|
||||
|
|
@ -87,20 +87,7 @@ const EditPresetDialog = ({
|
|||
console.log('setting model', models[0]);
|
||||
setOption('model')(models[0]);
|
||||
}
|
||||
|
||||
if (preset.agentOptions?.model === models[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
preset.agentOptions?.model != null &&
|
||||
preset.agentOptions.model &&
|
||||
!models.includes(preset.agentOptions.model)
|
||||
) {
|
||||
console.log('setting agent model', models[0]);
|
||||
setAgentOption('model')(models[0]);
|
||||
}
|
||||
}, [preset, queryClient, setOption, setAgentOption]);
|
||||
}, [preset, queryClient, setOption]);
|
||||
|
||||
const switchEndpoint = useCallback(
|
||||
(newEndpoint: string) => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import PlaceholderRow from '~/components/Chat/Messages/ui/PlaceholderRow';
|
|||
import SiblingSwitch from '~/components/Chat/Messages/SiblingSwitch';
|
||||
import HoverButtons from '~/components/Chat/Messages/HoverButtons';
|
||||
import MessageIcon from '~/components/Chat/Messages/MessageIcon';
|
||||
import { Plugin } from '~/components/Messages/Content';
|
||||
import SubRow from '~/components/Chat/Messages/SubRow';
|
||||
import { fontSizeAtom } from '~/store/fontSize';
|
||||
import { MessageContext } from '~/Providers';
|
||||
|
|
@ -178,7 +177,6 @@ const MessageRender = memo(
|
|||
isLatestMessage,
|
||||
}}
|
||||
>
|
||||
{msg.plugin && <Plugin plugin={msg.plugin} />}
|
||||
<MessageContent
|
||||
ask={ask}
|
||||
edit={edit}
|
||||
|
|
|
|||
|
|
@ -57,16 +57,7 @@ function getGoogleModelName(model: string | null | undefined) {
|
|||
}
|
||||
|
||||
const MessageEndpointIcon: React.FC<IconProps> = (props) => {
|
||||
const {
|
||||
error,
|
||||
button,
|
||||
iconURL = '',
|
||||
endpoint,
|
||||
size = 30,
|
||||
model = '',
|
||||
assistantName,
|
||||
agentName,
|
||||
} = props;
|
||||
const { error, iconURL = '', endpoint, size = 30, model = '', assistantName, agentName } = props;
|
||||
|
||||
const assistantsIcon = {
|
||||
icon: iconURL ? (
|
||||
|
|
@ -142,11 +133,6 @@ const MessageEndpointIcon: React.FC<IconProps> = (props) => {
|
|||
bg: getOpenAIColor(model),
|
||||
name: 'ChatGPT',
|
||||
},
|
||||
[EModelEndpoint.gptPlugins]: {
|
||||
icon: <Plugin size={size * 0.7} />,
|
||||
bg: `rgba(69, 89, 164, ${button === true ? 0.75 : 1})`,
|
||||
name: 'Plugins',
|
||||
},
|
||||
[EModelEndpoint.google]: {
|
||||
icon: getGoogleIcon(model, size),
|
||||
name: getGoogleModelName(model),
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
import { Feather } from 'lucide-react';
|
||||
import { EModelEndpoint, alternateName } from 'librechat-data-provider';
|
||||
import {
|
||||
Sparkles,
|
||||
BedrockIcon,
|
||||
AnthropicIcon,
|
||||
AzureMinimalIcon,
|
||||
OpenAIMinimalIcon,
|
||||
LightningIcon,
|
||||
MinimalPlugin,
|
||||
GoogleMinimalIcon,
|
||||
CustomMinimalIcon,
|
||||
AnthropicIcon,
|
||||
BedrockIcon,
|
||||
Sparkles,
|
||||
} from '@librechat/client';
|
||||
import UnknownIcon from '~/hooks/Endpoint/UnknownIcon';
|
||||
import { IconProps } from '~/common';
|
||||
|
|
@ -33,7 +31,6 @@ const MinimalIcon: React.FC<IconProps> = (props) => {
|
|||
icon: <OpenAIMinimalIcon className={iconClassName} />,
|
||||
name: props.chatGptLabel ?? 'ChatGPT',
|
||||
},
|
||||
[EModelEndpoint.gptPlugins]: { icon: <MinimalPlugin />, name: 'Plugins' },
|
||||
[EModelEndpoint.google]: { icon: <GoogleMinimalIcon />, name: props.modelLabel ?? 'Google' },
|
||||
[EModelEndpoint.anthropic]: {
|
||||
icon: <AnthropicIcon className="icon-md shrink-0 dark:text-white" />,
|
||||
|
|
@ -43,7 +40,6 @@ const MinimalIcon: React.FC<IconProps> = (props) => {
|
|||
icon: <CustomMinimalIcon />,
|
||||
name: 'Custom',
|
||||
},
|
||||
[EModelEndpoint.chatGPTBrowser]: { icon: <LightningIcon />, name: 'ChatGPT' },
|
||||
[EModelEndpoint.assistants]: { icon: <Sparkles className="icon-sm" />, name: 'Assistant' },
|
||||
[EModelEndpoint.azureAssistants]: { icon: <Sparkles className="icon-sm" />, name: 'Assistant' },
|
||||
[EModelEndpoint.agents]: {
|
||||
|
|
|
|||
|
|
@ -1,248 +0,0 @@
|
|||
import {
|
||||
Switch,
|
||||
Label,
|
||||
Slider,
|
||||
HoverCard,
|
||||
InputNumber,
|
||||
SelectDropDown,
|
||||
HoverCardTrigger,
|
||||
} from '@librechat/client';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import { cn, optionText, defaultTextProps, removeFocusRings } from '~/utils';
|
||||
import OptionHover from './OptionHover';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { ESide } from '~/common';
|
||||
|
||||
export default function Settings({ conversation, setOption, models, readonly }: TModelSelectProps) {
|
||||
const localize = useLocalize();
|
||||
if (!conversation) {
|
||||
return null;
|
||||
}
|
||||
const { agent, skipCompletion, model, temperature } = conversation.agentOptions ?? {};
|
||||
|
||||
const setModel = setOption('model');
|
||||
const setTemperature = setOption('temperature');
|
||||
const setAgent = setOption('agent');
|
||||
const setSkipCompletion = setOption('skipCompletion');
|
||||
const onCheckedChangeAgent = (checked: boolean) => {
|
||||
setAgent(checked ? 'functions' : 'classic');
|
||||
};
|
||||
|
||||
const onCheckedChangeSkip = (checked: boolean) => {
|
||||
setSkipCompletion(checked);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-5 gap-6">
|
||||
<div className="col-span-5 flex flex-col items-center justify-start gap-6 sm:col-span-3">
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<SelectDropDown
|
||||
title={localize('com_endpoint_agent_model')}
|
||||
value={model ?? ''}
|
||||
setValue={setModel}
|
||||
availableValues={models}
|
||||
disabled={readonly}
|
||||
className={cn(defaultTextProps, 'flex w-full resize-none', removeFocusRings)}
|
||||
containerClassName="flex w-full resize-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-5 flex flex-col items-center justify-start gap-6 px-3 sm:col-span-2">
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label htmlFor="temp-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_temperature')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', { 0: '0' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="temp-int"
|
||||
disabled={readonly}
|
||||
value={temperature}
|
||||
onChange={(value) => setTemperature(Number(value))}
|
||||
max={2}
|
||||
min={0}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
|
||||
),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[temperature ?? 0]}
|
||||
onValueChange={(value: number[]) => setTemperature(value[0])}
|
||||
onDoubleClick={() => setTemperature(1)}
|
||||
max={2}
|
||||
min={0}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
aria-labelledby="temp-int"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover endpoint={conversation.endpoint ?? ''} type="temp" side={ESide.Left} />
|
||||
</HoverCard>
|
||||
<div className="grid w-full grid-cols-2 items-center gap-10">
|
||||
<HoverCard openDelay={500}>
|
||||
<HoverCardTrigger className="flex w-[100px] flex-col items-center space-y-4 text-center">
|
||||
<label
|
||||
htmlFor="functions-agent"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-gray-50"
|
||||
>
|
||||
<small>{localize('com_endpoint_plug_use_functions')}</small>
|
||||
</label>
|
||||
<Switch
|
||||
id="functions-agent"
|
||||
checked={agent === 'functions'}
|
||||
onCheckedChange={onCheckedChangeAgent}
|
||||
disabled={readonly}
|
||||
className="ml-4 mt-2"
|
||||
aria-label={localize('com_endpoint_plug_use_functions')}
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover endpoint={conversation.endpoint ?? ''} type="func" side={ESide.Bottom} />
|
||||
</HoverCard>
|
||||
<HoverCard openDelay={500}>
|
||||
<HoverCardTrigger className="ml-[-60px] flex w-[100px] flex-col items-center space-y-4 text-center">
|
||||
<label
|
||||
htmlFor="skip-completion"
|
||||
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 dark:text-gray-50"
|
||||
>
|
||||
<small>{localize('com_endpoint_plug_skip_completion')}</small>
|
||||
</label>
|
||||
<Switch
|
||||
id="skip-completion"
|
||||
checked={skipCompletion === true}
|
||||
onCheckedChange={onCheckedChangeSkip}
|
||||
disabled={readonly}
|
||||
className="ml-4 mt-2"
|
||||
aria-label={localize('com_endpoint_plug_skip_completion')}
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover endpoint={conversation.endpoint ?? ''} type="skip" side={ESide.Bottom} />
|
||||
</HoverCard>
|
||||
</div>
|
||||
{/* <HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label htmlFor="top-p-int" className="text-left text-sm font-medium">
|
||||
Top P <small className="opacity-40">(default: 1)</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="top-p-int"
|
||||
disabled={readonly}
|
||||
value={topP}
|
||||
onChange={(value) => setTopP(value)}
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200'
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[topP]}
|
||||
onValueChange={(value) => setTopP(value[0])}
|
||||
doubleClickHandler={() => setTopP(1)}
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover type="topp" side="left" />
|
||||
</HoverCard>
|
||||
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label htmlFor="freq-penalty-int" className="text-left text-sm font-medium">
|
||||
Frequency Penalty <small className="opacity-40">(default: 0)</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="freq-penalty-int"
|
||||
disabled={readonly}
|
||||
value={freqP}
|
||||
onChange={(value) => setFreqP(value)}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200'
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[freqP]}
|
||||
onValueChange={(value) => setFreqP(value[0])}
|
||||
doubleClickHandler={() => setFreqP(0)}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover type="freq" side="left" />
|
||||
</HoverCard>
|
||||
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label htmlFor="pres-penalty-int" className="text-left text-sm font-medium">
|
||||
Presence Penalty <small className="opacity-40">(default: 0)</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="pres-penalty-int"
|
||||
disabled={readonly}
|
||||
value={presP}
|
||||
onChange={(value) => setPresP(value)}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200'
|
||||
)
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[presP]}
|
||||
onValueChange={(value) => setPresP(value[0])}
|
||||
doubleClickHandler={() => setPresP(0)}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover type="pres" side="left" />
|
||||
</HoverCard> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
import Settings from '../Plugins';
|
||||
import AgentSettings from '../AgentSettings';
|
||||
import { useSetIndexOptions } from '~/hooks';
|
||||
import { useChatContext } from '~/Providers';
|
||||
|
||||
export default function PluginsView({ conversation, models, isPreset = false }) {
|
||||
const { showAgentSettings } = useChatContext();
|
||||
const { setOption, setTools, setAgentOption, checkPluginSelection } = useSetIndexOptions(
|
||||
isPreset ? conversation : null,
|
||||
);
|
||||
if (!conversation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return showAgentSettings ? (
|
||||
<AgentSettings conversation={conversation} setOption={setAgentOption} models={models} />
|
||||
) : (
|
||||
<Settings
|
||||
conversation={conversation}
|
||||
setOption={setOption}
|
||||
setTools={setTools}
|
||||
checkPluginSelection={checkPluginSelection}
|
||||
models={models}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,2 +1 @@
|
|||
export { default as GoogleSettings } from './GoogleSettings';
|
||||
export { default as PluginSettings } from './PluginSettings';
|
||||
|
|
|
|||
|
|
@ -36,11 +36,6 @@ const types = {
|
|||
},
|
||||
openAI,
|
||||
azureOpenAI: openAI,
|
||||
gptPlugins: {
|
||||
func: 'com_endpoint_func_hover',
|
||||
skip: 'com_endpoint_skip_hover',
|
||||
...openAI,
|
||||
},
|
||||
};
|
||||
|
||||
function OptionHover({ endpoint, type, side }: TOptionHoverProps) {
|
||||
|
|
|
|||
|
|
@ -1,392 +0,0 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query';
|
||||
import {
|
||||
Input,
|
||||
Label,
|
||||
Slider,
|
||||
HoverCard,
|
||||
InputNumber,
|
||||
SelectDropDown,
|
||||
HoverCardTrigger,
|
||||
} from '@librechat/client';
|
||||
import type { TModelSelectProps, OnInputNumberChange } from '~/common';
|
||||
import type { TPlugin } from 'librechat-data-provider';
|
||||
import {
|
||||
removeFocusOutlines,
|
||||
defaultTextProps,
|
||||
removeFocusRings,
|
||||
processPlugins,
|
||||
selectPlugins,
|
||||
optionText,
|
||||
cn,
|
||||
} from '~/utils';
|
||||
import OptionHoverAlt from '~/components/SidePanel/Parameters/OptionHover';
|
||||
import MultiSelectDropDown from '~/components/Input/ModelSelect/MultiSelectDropDown';
|
||||
import { useLocalize, useDebouncedInput } from '~/hooks';
|
||||
import OptionHover from './OptionHover';
|
||||
import { ESide } from '~/common';
|
||||
import store from '~/store';
|
||||
|
||||
export default function Settings({
|
||||
conversation,
|
||||
setOption,
|
||||
setTools,
|
||||
checkPluginSelection,
|
||||
models,
|
||||
readonly,
|
||||
}: TModelSelectProps & {
|
||||
setTools: (newValue: string, remove?: boolean | undefined) => void;
|
||||
checkPluginSelection: (value: string) => boolean;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const availableTools = useRecoilValue(store.availableTools);
|
||||
const { data: allPlugins } = useAvailablePluginsQuery({
|
||||
select: selectPlugins,
|
||||
});
|
||||
|
||||
const conversationTools: TPlugin[] = useMemo(() => {
|
||||
if (!conversation?.tools) {
|
||||
return [];
|
||||
}
|
||||
return processPlugins(conversation.tools, allPlugins?.map);
|
||||
}, [conversation, allPlugins]);
|
||||
|
||||
const availablePlugins = useMemo(() => {
|
||||
if (!availableTools) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.values(availableTools);
|
||||
}, [availableTools]);
|
||||
|
||||
const {
|
||||
model,
|
||||
modelLabel,
|
||||
chatGptLabel,
|
||||
promptPrefix,
|
||||
temperature,
|
||||
top_p: topP,
|
||||
frequency_penalty: freqP,
|
||||
presence_penalty: presP,
|
||||
maxContextTokens,
|
||||
} = conversation ?? {};
|
||||
|
||||
const [setChatGptLabel, chatGptLabelValue] = useDebouncedInput<string | null | undefined>({
|
||||
setOption,
|
||||
optionKey: 'chatGptLabel',
|
||||
initialValue: modelLabel ?? chatGptLabel,
|
||||
});
|
||||
const [setPromptPrefix, promptPrefixValue] = useDebouncedInput<string | null | undefined>({
|
||||
setOption,
|
||||
optionKey: 'promptPrefix',
|
||||
initialValue: promptPrefix,
|
||||
});
|
||||
const [setTemperature, temperatureValue] = useDebouncedInput<number | null | undefined>({
|
||||
setOption,
|
||||
optionKey: 'temperature',
|
||||
initialValue: temperature,
|
||||
});
|
||||
const [setTopP, topPValue] = useDebouncedInput<number | null | undefined>({
|
||||
setOption,
|
||||
optionKey: 'top_p',
|
||||
initialValue: topP,
|
||||
});
|
||||
const [setFreqP, freqPValue] = useDebouncedInput<number | null | undefined>({
|
||||
setOption,
|
||||
optionKey: 'frequency_penalty',
|
||||
initialValue: freqP,
|
||||
});
|
||||
const [setPresP, presPValue] = useDebouncedInput<number | null | undefined>({
|
||||
setOption,
|
||||
optionKey: 'presence_penalty',
|
||||
initialValue: presP,
|
||||
});
|
||||
const [setMaxContextTokens, maxContextTokensValue] = useDebouncedInput<number | null | undefined>(
|
||||
{
|
||||
setOption,
|
||||
optionKey: 'maxContextTokens',
|
||||
initialValue: maxContextTokens,
|
||||
},
|
||||
);
|
||||
|
||||
const setModel = setOption('model');
|
||||
|
||||
if (!conversation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-5 gap-6">
|
||||
<div className="col-span-5 flex flex-col items-center justify-start gap-6 sm:col-span-3">
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<SelectDropDown
|
||||
title={localize('com_endpoint_completion_model')}
|
||||
value={model ?? ''}
|
||||
setValue={setModel}
|
||||
availableValues={models}
|
||||
disabled={readonly}
|
||||
className={cn(defaultTextProps, 'flex w-full resize-none', removeFocusRings)}
|
||||
containerClassName="flex w-full resize-none"
|
||||
/>
|
||||
</div>
|
||||
<>
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<Label htmlFor="chatGptLabel" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_custom_name')}{' '}
|
||||
<small className="opacity-40">{localize('com_endpoint_default_empty')}</small>
|
||||
</Label>
|
||||
<Input
|
||||
id="chatGptLabel"
|
||||
disabled={readonly}
|
||||
value={chatGptLabelValue || ''}
|
||||
onChange={(e) => setChatGptLabel(e.target.value ?? null)}
|
||||
placeholder={localize('com_endpoint_openai_custom_name_placeholder')}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex h-10 max-h-10 w-full resize-none px-3 py-2',
|
||||
removeFocusOutlines,
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid w-full items-center gap-2">
|
||||
<Label htmlFor="promptPrefix" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_prompt_prefix')}{' '}
|
||||
<small className="opacity-40">{localize('com_endpoint_default_empty')}</small>
|
||||
</Label>
|
||||
<TextareaAutosize
|
||||
id="promptPrefix"
|
||||
disabled={readonly}
|
||||
value={promptPrefixValue || ''}
|
||||
onChange={(e) => setPromptPrefix(e.target.value ?? null)}
|
||||
placeholder={localize(
|
||||
'com_endpoint_plug_set_custom_instructions_for_gpt_placeholder',
|
||||
)}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
'flex max-h-[138px] min-h-[100px] w-full resize-none px-3 py-2',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
<div className="col-span-5 flex flex-col items-center justify-start gap-6 px-3 sm:col-span-2">
|
||||
<MultiSelectDropDown
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
setSelected={setTools}
|
||||
value={conversationTools}
|
||||
optionValueKey="pluginKey"
|
||||
availableValues={availablePlugins}
|
||||
isSelected={checkPluginSelection}
|
||||
searchPlaceholder={localize('com_ui_select_search_plugin')}
|
||||
className={cn(defaultTextProps, 'flex w-full resize-none', removeFocusOutlines)}
|
||||
optionsClassName="w-full max-h-[275px] dark:bg-gray-700 z-10 border dark:border-gray-600"
|
||||
containerClassName="flex w-full resize-none border border-transparent"
|
||||
labelClassName="dark:text-white"
|
||||
/>
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="mt-1 flex w-full justify-between">
|
||||
<Label htmlFor="max-context-tokens" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_context_tokens')}{' '}
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="max-context-tokens"
|
||||
stringMode={false}
|
||||
disabled={readonly}
|
||||
value={maxContextTokensValue as number}
|
||||
onChange={setMaxContextTokens as OnInputNumberChange}
|
||||
placeholder={localize('com_nav_theme_system')}
|
||||
min={10}
|
||||
max={2000000}
|
||||
step={1000}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
|
||||
'w-1/3',
|
||||
),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</HoverCardTrigger>
|
||||
<OptionHoverAlt
|
||||
description="com_endpoint_context_info"
|
||||
langCode={true}
|
||||
side={ESide.Left}
|
||||
/>
|
||||
</HoverCard>
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label htmlFor="temp-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_temperature')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', { 0: '0.8' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="temp-int"
|
||||
disabled={readonly}
|
||||
value={temperatureValue}
|
||||
onChange={(value) => setTemperature(Number(value))}
|
||||
max={2}
|
||||
min={0}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
|
||||
),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[temperatureValue ?? 0.8]}
|
||||
onValueChange={(value) => setTemperature(value[0])}
|
||||
onDoubleClick={() => setTemperature(0.8)}
|
||||
max={2}
|
||||
min={0}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
aria-labelledby="temp-int"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover endpoint={conversation.endpoint ?? ''} type="temp" side={ESide.Left} />
|
||||
</HoverCard>
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label htmlFor="top-p-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_top_p')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', { 0: '1' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="top-p-int"
|
||||
disabled={readonly}
|
||||
value={topPValue}
|
||||
onChange={(value) => setTopP(Number(value))}
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
|
||||
),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[topPValue ?? 1]}
|
||||
onValueChange={(value) => setTopP(value[0])}
|
||||
onDoubleClick={() => setTopP(1)}
|
||||
max={1}
|
||||
min={0}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
aria-labelledby="top-p-int"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover endpoint={conversation.endpoint ?? ''} type="topp" side={ESide.Left} />
|
||||
</HoverCard>
|
||||
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label htmlFor="freq-penalty-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_frequency_penalty')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', { 0: '0' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="freq-penalty-int"
|
||||
disabled={readonly}
|
||||
value={freqPValue}
|
||||
onChange={(value) => setFreqP(Number(value))}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
|
||||
),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[freqPValue ?? 0]}
|
||||
onValueChange={(value) => setFreqP(value[0])}
|
||||
onDoubleClick={() => setFreqP(0)}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
aria-labelledby="freq-penalty-int"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover endpoint={conversation.endpoint ?? ''} type="freq" side={ESide.Left} />
|
||||
</HoverCard>
|
||||
|
||||
<HoverCard openDelay={300}>
|
||||
<HoverCardTrigger className="grid w-full items-center gap-2">
|
||||
<div className="flex justify-between">
|
||||
<Label htmlFor="pres-penalty-int" className="text-left text-sm font-medium">
|
||||
{localize('com_endpoint_presence_penalty')}{' '}
|
||||
<small className="opacity-40">
|
||||
({localize('com_endpoint_default_with_num', { 0: '0' })})
|
||||
</small>
|
||||
</Label>
|
||||
<InputNumber
|
||||
id="pres-penalty-int"
|
||||
disabled={readonly}
|
||||
value={presPValue}
|
||||
onChange={(value) => setPresP(Number(value))}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
controls={false}
|
||||
className={cn(
|
||||
defaultTextProps,
|
||||
cn(
|
||||
optionText,
|
||||
'reset-rc-number-input reset-rc-number-input-text-right h-auto w-12 border-0 group-hover/temp:border-gray-200',
|
||||
),
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Slider
|
||||
disabled={readonly}
|
||||
value={[presPValue ?? 0]}
|
||||
onValueChange={(value) => setPresP(value[0])}
|
||||
onDoubleClick={() => setPresP(0)}
|
||||
max={2}
|
||||
min={-2}
|
||||
step={0.01}
|
||||
className="flex h-4 w-full"
|
||||
aria-labelledby="pres-penalty-int"
|
||||
/>
|
||||
</HoverCardTrigger>
|
||||
<OptionHover endpoint={conversation.endpoint ?? ''} type="pres" side={ESide.Left} />
|
||||
</HoverCard>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -3,8 +3,6 @@ export { default as AssistantsSettings } from './Assistants';
|
|||
export { default as BedrockSettings } from './Bedrock';
|
||||
export { default as OpenAISettings } from './OpenAI';
|
||||
export { default as GoogleSettings } from './Google';
|
||||
export { default as PluginsSettings } from './Plugins';
|
||||
export { default as Examples } from './Examples';
|
||||
export { default as AgentSettings } from './AgentSettings';
|
||||
export { default as AnthropicSettings } from './Anthropic';
|
||||
export * from './settings';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { FC } from 'react';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import { GoogleSettings, PluginSettings } from './MultiView';
|
||||
import AssistantsSettings from './Assistants';
|
||||
import { GoogleSettings } from './MultiView';
|
||||
import AnthropicSettings from './Anthropic';
|
||||
import BedrockSettings from './Bedrock';
|
||||
import OpenAISettings from './OpenAI';
|
||||
|
|
@ -23,7 +23,6 @@ export const getSettings = () => {
|
|||
settings,
|
||||
multiViewSettings: {
|
||||
[EModelEndpoint.google]: GoogleSettings,
|
||||
[EModelEndpoint.gptPlugins]: PluginSettings,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,110 +0,0 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import { ChevronDownIcon } from 'lucide-react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query';
|
||||
import {
|
||||
Button,
|
||||
SelectDropDown,
|
||||
SelectDropDownPop,
|
||||
MultiSelectDropDown,
|
||||
useMediaQuery,
|
||||
} from '@librechat/client';
|
||||
import type { TPlugin } from 'librechat-data-provider';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import { useSetIndexOptions, useAuthContext, useLocalize } from '~/hooks';
|
||||
import { cn, cardStyle, selectPlugins, processPlugins } from '~/utils';
|
||||
import MultiSelectPop from './MultiSelectPop';
|
||||
import store from '~/store';
|
||||
|
||||
export default function PluginsByIndex({
|
||||
conversation,
|
||||
setOption,
|
||||
models,
|
||||
showAbove,
|
||||
popover = false,
|
||||
}: TModelSelectProps) {
|
||||
const localize = useLocalize();
|
||||
const { user } = useAuthContext();
|
||||
const [visible, setVisibility] = useState<boolean>(true);
|
||||
const isSmallScreen = useMediaQuery('(max-width: 640px)');
|
||||
const availableTools = useRecoilValue(store.availableTools);
|
||||
const { checkPluginSelection, setTools } = useSetIndexOptions();
|
||||
|
||||
const { data: allPlugins } = useAvailablePluginsQuery({
|
||||
enabled: !!user?.plugins,
|
||||
select: selectPlugins,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isSmallScreen) {
|
||||
setVisibility(false);
|
||||
}
|
||||
}, [isSmallScreen]);
|
||||
|
||||
const conversationTools: TPlugin[] = useMemo(() => {
|
||||
if (!conversation?.tools) {
|
||||
return [];
|
||||
}
|
||||
return processPlugins(conversation.tools, allPlugins?.map);
|
||||
}, [conversation, allPlugins]);
|
||||
|
||||
const availablePlugins = useMemo(() => {
|
||||
if (!availableTools) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.values(availableTools);
|
||||
}, [availableTools]);
|
||||
|
||||
if (!conversation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const Menu = popover ? SelectDropDownPop : SelectDropDown;
|
||||
const PluginsMenu = popover ? MultiSelectPop : MultiSelectDropDown;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
type="button"
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'z-40 flex h-[40px] min-w-4 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>
|
||||
{visible && (
|
||||
<>
|
||||
<Menu
|
||||
value={conversation.model ?? ''}
|
||||
setValue={setOption('model')}
|
||||
availableValues={models}
|
||||
showAbove={showAbove}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
cardStyle,
|
||||
'z-50 flex h-[40px] w-48 min-w-48 flex-none items-center justify-center px-4 hover:cursor-pointer',
|
||||
)}
|
||||
/>
|
||||
<PluginsMenu
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
setSelected={setTools}
|
||||
value={conversationTools}
|
||||
optionValueKey="pluginKey"
|
||||
availableValues={availablePlugins}
|
||||
isSelected={checkPluginSelection}
|
||||
searchPlaceholder={localize('com_ui_select_search_plugin')}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -4,9 +4,7 @@ import type { FC } from 'react';
|
|||
|
||||
import OpenAI from './OpenAI';
|
||||
import Google from './Google';
|
||||
import ChatGPT from './ChatGPT';
|
||||
import Anthropic from './Anthropic';
|
||||
import PluginsByIndex from './PluginsByIndex';
|
||||
|
||||
export const options: { [key: string]: FC<TModelSelectProps> } = {
|
||||
[EModelEndpoint.openAI]: OpenAI,
|
||||
|
|
@ -15,10 +13,8 @@ export const options: { [key: string]: FC<TModelSelectProps> } = {
|
|||
[EModelEndpoint.azureOpenAI]: OpenAI,
|
||||
[EModelEndpoint.google]: Google,
|
||||
[EModelEndpoint.anthropic]: Anthropic,
|
||||
[EModelEndpoint.chatGPTBrowser]: ChatGPT,
|
||||
};
|
||||
|
||||
export const multiChatOptions = {
|
||||
...options,
|
||||
[EModelEndpoint.gptPlugins]: PluginsByIndex,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useForm, FormProvider } from 'react-hook-form';
|
||||
import {
|
||||
OGDialog,
|
||||
OGDialogContent,
|
||||
OGDialogHeader,
|
||||
OGDialogTitle,
|
||||
OGDialogFooter,
|
||||
Dropdown,
|
||||
useToastContext,
|
||||
Button,
|
||||
Label,
|
||||
OGDialogTrigger,
|
||||
Spinner,
|
||||
} from '@librechat/client';
|
||||
import { EModelEndpoint, alternateName, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import {
|
||||
useRevokeAllUserKeysMutation,
|
||||
useRevokeUserKeyMutation,
|
||||
useRevokeAllUserKeysMutation,
|
||||
} from 'librechat-data-provider/react-query';
|
||||
import {
|
||||
Label,
|
||||
Button,
|
||||
Spinner,
|
||||
OGDialog,
|
||||
Dropdown,
|
||||
OGDialogTitle,
|
||||
OGDialogHeader,
|
||||
OGDialogFooter,
|
||||
OGDialogContent,
|
||||
useToastContext,
|
||||
OGDialogTrigger,
|
||||
} from '@librechat/client';
|
||||
import type { TDialogProps } from '~/common';
|
||||
import { useGetEndpointsQuery } from '~/data-provider';
|
||||
import { useUserKey, useLocalize } from '~/hooks';
|
||||
import { NotificationSeverity } from '~/common';
|
||||
import CustomConfig from './CustomEndpoint';
|
||||
|
|
@ -34,7 +33,6 @@ const endpointComponents = {
|
|||
[EModelEndpoint.openAI]: OpenAIConfig,
|
||||
[EModelEndpoint.custom]: CustomConfig,
|
||||
[EModelEndpoint.azureOpenAI]: OpenAIConfig,
|
||||
[EModelEndpoint.gptPlugins]: OpenAIConfig,
|
||||
[EModelEndpoint.assistants]: OpenAIConfig,
|
||||
[EModelEndpoint.azureAssistants]: OpenAIConfig,
|
||||
default: OtherConfig,
|
||||
|
|
@ -44,7 +42,6 @@ const formSet: Set<string> = new Set([
|
|||
EModelEndpoint.openAI,
|
||||
EModelEndpoint.custom,
|
||||
EModelEndpoint.azureOpenAI,
|
||||
EModelEndpoint.gptPlugins,
|
||||
EModelEndpoint.assistants,
|
||||
EModelEndpoint.azureAssistants,
|
||||
]);
|
||||
|
|
@ -174,7 +171,6 @@ const SetKeyDialog = ({
|
|||
});
|
||||
|
||||
const [userKey, setUserKey] = useState('');
|
||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
||||
const [expiresAtLabel, setExpiresAtLabel] = useState(EXPIRY.TWELVE_HOURS.label);
|
||||
const { getExpiry, saveUserKey } = useUserKey(endpoint);
|
||||
const { showToast } = useToastContext();
|
||||
|
|
@ -218,10 +214,7 @@ const SetKeyDialog = ({
|
|||
methods.handleSubmit((data) => {
|
||||
const isAzure = endpoint === EModelEndpoint.azureOpenAI;
|
||||
const isOpenAIBase =
|
||||
isAzure ||
|
||||
endpoint === EModelEndpoint.openAI ||
|
||||
endpoint === EModelEndpoint.gptPlugins ||
|
||||
isAssistantsEndpoint(endpoint);
|
||||
isAzure || endpoint === EModelEndpoint.openAI || isAssistantsEndpoint(endpoint);
|
||||
if (isAzure) {
|
||||
data.apiKey = 'n/a';
|
||||
}
|
||||
|
|
@ -280,7 +273,6 @@ const SetKeyDialog = ({
|
|||
const EndpointComponent =
|
||||
endpointComponents[endpointType ?? endpoint] ?? endpointComponents['default'];
|
||||
const expiryTime = getExpiry();
|
||||
const config = endpointsConfig?.[endpoint];
|
||||
|
||||
return (
|
||||
<OGDialog open={open} onOpenChange={onOpenChange}>
|
||||
|
|
@ -310,12 +302,8 @@ const SetKeyDialog = ({
|
|||
<FormProvider {...methods}>
|
||||
<EndpointComponent
|
||||
userKey={userKey}
|
||||
endpoint={endpoint}
|
||||
setUserKey={setUserKey}
|
||||
endpoint={
|
||||
endpoint === EModelEndpoint.gptPlugins && (config?.azure ?? false)
|
||||
? EModelEndpoint.azureOpenAI
|
||||
: endpoint
|
||||
}
|
||||
userProvideURL={userProvideURL}
|
||||
/>
|
||||
</FormProvider>
|
||||
|
|
|
|||
|
|
@ -1,130 +0,0 @@
|
|||
import { useCallback, memo, ReactNode } from 'react';
|
||||
import { Spinner } from '@librechat/client';
|
||||
import { ChevronDownIcon, LucideProps } from 'lucide-react';
|
||||
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/react';
|
||||
import type { TResPlugin, TInput } from 'librechat-data-provider';
|
||||
import { useGetEndpointsQuery } from '~/data-provider';
|
||||
import { useShareContext } from '~/Providers';
|
||||
import { cn, formatJSON } from '~/utils';
|
||||
import CodeBlock from './CodeBlock';
|
||||
|
||||
type PluginIconProps = LucideProps & {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function formatInputs(inputs: TInput[]) {
|
||||
let output = '';
|
||||
|
||||
for (let i = 0; i < inputs.length; i++) {
|
||||
const input = formatJSON(`${inputs[i]?.inputStr ?? inputs[i]}`);
|
||||
output += input;
|
||||
|
||||
if (inputs.length > 1 && i !== inputs.length - 1) {
|
||||
output += ',\n';
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
type PluginProps = {
|
||||
plugin: TResPlugin;
|
||||
};
|
||||
|
||||
const Plugin: React.FC<PluginProps> = ({ plugin }) => {
|
||||
const { isSharedConvo } = useShareContext();
|
||||
const { data: plugins = {} } = useGetEndpointsQuery({
|
||||
enabled: !isSharedConvo,
|
||||
select: (data) => data?.gptPlugins?.plugins,
|
||||
});
|
||||
|
||||
const getPluginName = useCallback(
|
||||
(pluginKey: string) => {
|
||||
if (!pluginKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (pluginKey === 'n/a' || pluginKey === 'self reflection') {
|
||||
return pluginKey;
|
||||
}
|
||||
return plugins[pluginKey] ?? 'self reflection';
|
||||
},
|
||||
[plugins],
|
||||
);
|
||||
|
||||
if (!plugin || !plugin.latest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const latestPlugin = getPluginName(plugin.latest);
|
||||
|
||||
if (!latestPlugin || (latestPlugin && latestPlugin === 'n/a')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const generateStatus = (): ReactNode => {
|
||||
if (!plugin.loading && latestPlugin === 'self reflection') {
|
||||
return 'Finished';
|
||||
} else if (latestPlugin === 'self reflection') {
|
||||
return "I'm thinking...";
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{plugin.loading ? 'Using' : 'Used'} <b>{latestPlugin}</b>
|
||||
{plugin.loading ? '...' : ''}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="my-2 flex flex-col items-start">
|
||||
<Disclosure>
|
||||
{({ open }) => {
|
||||
const iconProps: PluginIconProps = {
|
||||
className: cn(open ? 'rotate-180 transform' : '', 'h-4 w-4'),
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className={cn(
|
||||
plugin.loading ? 'bg-green-100' : 'bg-gray-20',
|
||||
'my-1 flex items-center rounded p-3 text-xs text-gray-800',
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div>{generateStatus()}</div>
|
||||
</div>
|
||||
</div>
|
||||
{plugin.loading && <Spinner className="ml-1 text-black" />}
|
||||
<DisclosureButton className="ml-12 flex items-center gap-2">
|
||||
<ChevronDownIcon {...iconProps} />
|
||||
</DisclosureButton>
|
||||
</div>
|
||||
|
||||
<DisclosurePanel className="mt-3 flex max-w-full flex-col gap-3">
|
||||
<CodeBlock
|
||||
lang={latestPlugin ? `REQUEST TO ${latestPlugin.toUpperCase()}` : 'REQUEST'}
|
||||
codeChildren={formatInputs(plugin.inputs ?? [])}
|
||||
plugin={true}
|
||||
classProp="max-h-[450px]"
|
||||
/>
|
||||
{plugin.outputs && plugin.outputs.length > 0 && (
|
||||
<CodeBlock
|
||||
lang={latestPlugin ? `RESPONSE FROM ${latestPlugin.toUpperCase()}` : 'RESPONSE'}
|
||||
codeChildren={formatJSON(plugin.outputs ?? '')}
|
||||
plugin={true}
|
||||
classProp="max-h-[450px]"
|
||||
/>
|
||||
)}
|
||||
</DisclosurePanel>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Disclosure>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(Plugin);
|
||||
|
|
@ -1,2 +1 @@
|
|||
export { default as SubRow } from './SubRow';
|
||||
export { default as Plugin } from './Plugin';
|
||||
|
|
|
|||
|
|
@ -12,10 +12,9 @@ import { cn } from '~/utils';
|
|||
type BookmarkNavProps = {
|
||||
tags: string[];
|
||||
setTags: (tags: string[]) => void;
|
||||
isSmallScreen: boolean;
|
||||
};
|
||||
|
||||
const BookmarkNav: FC<BookmarkNavProps> = ({ tags, setTags, isSmallScreen }: BookmarkNavProps) => {
|
||||
const BookmarkNav: FC<BookmarkNavProps> = ({ tags, setTags }: BookmarkNavProps) => {
|
||||
const localize = useLocalize();
|
||||
const { data } = useGetConversationTags();
|
||||
const label = useMemo(
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { QueryKeys } from 'librechat-data-provider';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { QueryKeys, Constants } from 'librechat-data-provider';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import { useLocalize, useNewConvo } from '~/hooks';
|
||||
import { clearMessagesCache } from '~/utils';
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { QueryKeys } from 'librechat-data-provider';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { QueryKeys, Constants } from 'librechat-data-provider';
|
||||
import { TooltipAnchor, NewChatIcon, MobileSidebar, Sidebar, Button } from '@librechat/client';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import { useLocalize, useNewConvo } from '~/hooks';
|
||||
import { clearMessagesCache } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
|
|
|||
|
|
@ -5,27 +5,27 @@ import { useRecoilValue } from 'recoil';
|
|||
import { TrashIcon, ArchiveRestore, ArrowUp, ArrowDown, ArrowUpDown } from 'lucide-react';
|
||||
import {
|
||||
Button,
|
||||
OGDialog,
|
||||
OGDialogContent,
|
||||
OGDialogHeader,
|
||||
OGDialogTitle,
|
||||
Label,
|
||||
TooltipAnchor,
|
||||
Spinner,
|
||||
OGDialog,
|
||||
DataTable,
|
||||
useToastContext,
|
||||
TooltipAnchor,
|
||||
useMediaQuery,
|
||||
OGDialogTitle,
|
||||
OGDialogHeader,
|
||||
useToastContext,
|
||||
OGDialogContent,
|
||||
} from '@librechat/client';
|
||||
import type { ConversationListParams, TConversation } from 'librechat-data-provider';
|
||||
import {
|
||||
useArchiveConvoMutation,
|
||||
useConversationsInfiniteQuery,
|
||||
useDeleteConversationMutation,
|
||||
useArchiveConvoMutation,
|
||||
} from '~/data-provider';
|
||||
import { MinimalIcon } from '~/components/Endpoints';
|
||||
import { NotificationSeverity } from '~/common';
|
||||
import { formatDate, logger } from '~/utils';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { formatDate } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
const DEFAULT_PARAMS: ConversationListParams = {
|
||||
|
|
@ -43,7 +43,7 @@ export default function ArchivedChatsTable({
|
|||
const localize = useLocalize();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 768px)');
|
||||
const { showToast } = useToastContext();
|
||||
const isSearchEnabled = useRecoilValue(store.search);
|
||||
const searchState = useRecoilValue(store.search);
|
||||
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||
const [queryParams, setQueryParams] = useState<ConversationListParams>(DEFAULT_PARAMS);
|
||||
const [deleteConversation, setDeleteConversation] = useState<TConversation | null>(null);
|
||||
|
|
@ -101,6 +101,7 @@ export default function ArchivedChatsTable({
|
|||
});
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
logger.error('Error deleting archived conversation:', error);
|
||||
showToast({
|
||||
message: localize('com_ui_archive_delete_error') as string,
|
||||
severity: NotificationSeverity.ERROR,
|
||||
|
|
@ -113,6 +114,7 @@ export default function ArchivedChatsTable({
|
|||
await refetch();
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
logger.error('Error unarchiving conversation', error);
|
||||
showToast({
|
||||
message: localize('com_ui_unarchive_error') as string,
|
||||
severity: NotificationSeverity.ERROR,
|
||||
|
|
@ -283,7 +285,7 @@ export default function ArchivedChatsTable({
|
|||
isFetchingNextPage={isFetchingNextPage}
|
||||
isLoading={isLoading}
|
||||
showCheckboxes={false}
|
||||
enableSearch={isSearchEnabled}
|
||||
enableSearch={searchState.enabled === true}
|
||||
/>
|
||||
|
||||
<OGDialog open={isDeleteOpen} onOpenChange={onOpenChange}>
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ function PluginAuthForm({ plugin, onSubmit, isEntityTool }: TPluginAuthFormProps
|
|||
</HoverCard>
|
||||
{errors[authField] && (
|
||||
<span role="alert" className="mt-1 text-sm text-red-400">
|
||||
{errors[authField].message as string}
|
||||
{errors?.[authField]?.message ?? ''}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,245 +0,0 @@
|
|||
import { Search, X } from 'lucide-react';
|
||||
import { Dialog, DialogPanel, DialogTitle } from '@headlessui/react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { TError, TPlugin, TPluginAction } from 'librechat-data-provider';
|
||||
import type { TPluginStoreDialogProps } from '~/common/types';
|
||||
import {
|
||||
usePluginDialogHelpers,
|
||||
useSetIndexOptions,
|
||||
usePluginInstall,
|
||||
useAuthContext,
|
||||
useLocalize,
|
||||
} from '~/hooks';
|
||||
import PluginPagination from './PluginPagination';
|
||||
import PluginStoreItem from './PluginStoreItem';
|
||||
import PluginAuthForm from './PluginAuthForm';
|
||||
|
||||
function PluginStoreDialog({ isOpen, setIsOpen }: TPluginStoreDialogProps) {
|
||||
const localize = useLocalize();
|
||||
const { user } = useAuthContext();
|
||||
const { data: availablePlugins } = useAvailablePluginsQuery();
|
||||
const { setTools } = useSetIndexOptions();
|
||||
|
||||
const [userPlugins, setUserPlugins] = useState<string[]>([]);
|
||||
|
||||
const {
|
||||
maxPage,
|
||||
setMaxPage,
|
||||
currentPage,
|
||||
setCurrentPage,
|
||||
itemsPerPage,
|
||||
searchChanged,
|
||||
setSearchChanged,
|
||||
searchValue,
|
||||
setSearchValue,
|
||||
gridRef,
|
||||
handleSearch,
|
||||
handleChangePage,
|
||||
error,
|
||||
setError,
|
||||
errorMessage,
|
||||
setErrorMessage,
|
||||
showPluginAuthForm,
|
||||
setShowPluginAuthForm,
|
||||
selectedPlugin,
|
||||
setSelectedPlugin,
|
||||
} = usePluginDialogHelpers();
|
||||
|
||||
const handleInstallError = useCallback(
|
||||
(error: TError) => {
|
||||
setError(true);
|
||||
if (error.response?.data?.message) {
|
||||
setErrorMessage(error.response.data.message);
|
||||
}
|
||||
setTimeout(() => {
|
||||
setError(false);
|
||||
setErrorMessage('');
|
||||
}, 5000);
|
||||
},
|
||||
[setError, setErrorMessage],
|
||||
);
|
||||
|
||||
const { installPlugin, uninstallPlugin } = usePluginInstall({
|
||||
onInstallError: handleInstallError,
|
||||
onUninstallError: handleInstallError,
|
||||
onUninstallSuccess: (_data, variables) => {
|
||||
setTools(variables.pluginKey, true);
|
||||
},
|
||||
});
|
||||
|
||||
const handleInstall = (pluginAction: TPluginAction, plugin?: TPlugin) => {
|
||||
if (!plugin) {
|
||||
return;
|
||||
}
|
||||
installPlugin(pluginAction, plugin);
|
||||
setShowPluginAuthForm(false);
|
||||
};
|
||||
|
||||
const onPluginInstall = (pluginKey: string) => {
|
||||
const plugin = availablePlugins?.find((p) => p.pluginKey === pluginKey);
|
||||
if (!plugin) {
|
||||
return;
|
||||
}
|
||||
setSelectedPlugin(plugin);
|
||||
|
||||
const { authConfig, authenticated } = plugin ?? {};
|
||||
|
||||
if (authConfig && authConfig.length > 0 && !authenticated) {
|
||||
setShowPluginAuthForm(true);
|
||||
} else {
|
||||
handleInstall({ pluginKey, action: 'install', auth: null }, plugin);
|
||||
}
|
||||
};
|
||||
|
||||
const filteredPlugins = availablePlugins?.filter((plugin) =>
|
||||
plugin.name.toLowerCase().includes(searchValue.toLowerCase()),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (user && user.plugins) {
|
||||
setUserPlugins(user.plugins);
|
||||
}
|
||||
|
||||
if (filteredPlugins) {
|
||||
setMaxPage(Math.ceil(filteredPlugins.length / itemsPerPage));
|
||||
if (searchChanged) {
|
||||
setCurrentPage(1);
|
||||
setSearchChanged(false);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
availablePlugins,
|
||||
itemsPerPage,
|
||||
user,
|
||||
searchValue,
|
||||
filteredPlugins,
|
||||
searchChanged,
|
||||
setMaxPage,
|
||||
setCurrentPage,
|
||||
setSearchChanged,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onClose={() => {
|
||||
setIsOpen(false);
|
||||
setCurrentPage(1);
|
||||
setSearchValue('');
|
||||
}}
|
||||
className="relative z-[102]"
|
||||
>
|
||||
{/* The backdrop, rendered as a fixed sibling to the panel container */}
|
||||
<div className="fixed inset-0 bg-gray-600/65 transition-opacity dark:bg-black/80" />
|
||||
{/* Full-screen container to center the panel */}
|
||||
<div className="fixed inset-0 flex items-center justify-center p-4">
|
||||
<DialogPanel
|
||||
className="relative w-full transform overflow-hidden overflow-y-auto rounded-lg bg-white text-left shadow-xl transition-all dark:bg-gray-700 max-sm:h-full sm:mx-7 sm:my-8 sm:max-w-2xl lg:max-w-5xl xl:max-w-7xl"
|
||||
style={{ minHeight: '610px' }}
|
||||
>
|
||||
<div className="flex items-center justify-between border-b-[1px] border-black/10 p-6 pb-4 dark:border-white/10">
|
||||
<div className="flex items-center">
|
||||
<div className="text-center sm:text-left">
|
||||
<DialogTitle className="text-lg font-medium leading-6 text-gray-800 dark:text-gray-200">
|
||||
{localize('com_nav_plugin_store')}
|
||||
</DialogTitle>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="sm:mt-0">
|
||||
<button
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="inline-block text-gray-500 hover:text-gray-200"
|
||||
tabIndex={0}
|
||||
>
|
||||
<X />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{error && (
|
||||
<div
|
||||
className="relative m-4 rounded border border-red-400 bg-red-100 px-4 py-3 text-red-700"
|
||||
role="alert"
|
||||
>
|
||||
{localize('com_nav_plugin_auth_error')} {errorMessage}
|
||||
</div>
|
||||
)}
|
||||
{showPluginAuthForm && (
|
||||
<div className="p-4 sm:p-6 sm:pt-4">
|
||||
<PluginAuthForm
|
||||
plugin={selectedPlugin}
|
||||
onSubmit={(action: TPluginAction) => handleInstall(action, selectedPlugin)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="p-4 sm:p-6 sm:pt-4">
|
||||
<div className="mt-4 flex flex-col gap-4">
|
||||
<div className="flex items-center">
|
||||
<div className="relative flex items-center">
|
||||
<Search className="absolute left-2 h-6 w-6 text-gray-500" aria-hidden="true" />
|
||||
<input
|
||||
type="text"
|
||||
value={searchValue}
|
||||
onChange={handleSearch}
|
||||
placeholder={localize('com_nav_plugin_search')}
|
||||
className="text-token-text-primary flex rounded-md border border-border-heavy bg-surface-tertiary py-2 pl-10 pr-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref={gridRef}
|
||||
className="grid grid-cols-1 grid-rows-2 gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4"
|
||||
style={{ minHeight: '410px' }}
|
||||
>
|
||||
{filteredPlugins &&
|
||||
filteredPlugins
|
||||
.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage)
|
||||
.map((plugin, index) => (
|
||||
<PluginStoreItem
|
||||
key={index}
|
||||
plugin={plugin}
|
||||
isInstalled={userPlugins.includes(plugin.pluginKey)}
|
||||
onInstall={() => onPluginInstall(plugin.pluginKey)}
|
||||
onUninstall={() => uninstallPlugin(plugin.pluginKey)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2 flex flex-col items-center gap-2 sm:flex-row sm:justify-between">
|
||||
{maxPage > 0 ? (
|
||||
<PluginPagination
|
||||
currentPage={currentPage}
|
||||
maxPage={maxPage}
|
||||
onChangePage={handleChangePage}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ height: '21px' }}></div>
|
||||
)}
|
||||
{/* API not yet implemented: */}
|
||||
{/* <div className="flex flex-col items-center gap-2 sm:flex-row">
|
||||
<PluginStoreLinkButton
|
||||
label="Install an unverified plugin"
|
||||
onClick={onInstallUnverifiedPlugin}
|
||||
/>
|
||||
<div className="hidden h-4 border-l border-black/30 dark:border-white/30 sm:block"></div>
|
||||
<PluginStoreLinkButton
|
||||
label="Develop your own plugin"
|
||||
onClick={onDevelopPluginClick}
|
||||
/>
|
||||
<div className="hidden h-4 border-l border-black/30 dark:border-white/30 sm:block"></div>
|
||||
<PluginStoreLinkButton label="About plugins" onClick={onAboutPluginsClick} />
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</DialogPanel>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default PluginStoreDialog;
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
import { TPlugin } from 'librechat-data-provider';
|
||||
import { XCircle, DownloadCloud } from 'lucide-react';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
type TPluginStoreItemProps = {
|
||||
plugin: TPlugin;
|
||||
onInstall: () => void;
|
||||
onUninstall: () => void;
|
||||
isInstalled?: boolean;
|
||||
};
|
||||
|
||||
function PluginStoreItem({ plugin, onInstall, onUninstall, isInstalled }: TPluginStoreItemProps) {
|
||||
const localize = useLocalize();
|
||||
const handleClick = () => {
|
||||
if (isInstalled) {
|
||||
onUninstall();
|
||||
} else {
|
||||
onInstall();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-4 rounded border border-black/10 bg-white p-6 dark:border-gray-500 dark:bg-gray-700">
|
||||
<div className="flex gap-4">
|
||||
<div className="h-[70px] w-[70px] shrink-0">
|
||||
<div className="relative h-full w-full">
|
||||
<img
|
||||
src={plugin.icon}
|
||||
alt={`${plugin.name} logo`}
|
||||
className="h-full w-full rounded-[5px]"
|
||||
/>
|
||||
<div className="absolute inset-0 rounded-[5px] ring-1 ring-inset ring-black/10"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-col items-start justify-between">
|
||||
<div className="mb-2 line-clamp-1 max-w-full text-lg leading-5 text-gray-700/80 dark:text-gray-50">
|
||||
{plugin.name}
|
||||
</div>
|
||||
{!isInstalled ? (
|
||||
<button
|
||||
className="btn btn-primary relative"
|
||||
aria-label={`${localize('com_nav_plugin_install')} ${plugin.name}`}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
{localize('com_nav_plugin_install')}
|
||||
<DownloadCloud
|
||||
className="flex h-4 w-4 items-center stroke-2"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="btn relative bg-gray-300 hover:bg-gray-400 dark:bg-gray-50 dark:hover:bg-gray-200"
|
||||
onClick={handleClick}
|
||||
aria-label={`${localize('com_nav_plugin_uninstall')} ${plugin.name}`}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
{localize('com_nav_plugin_uninstall')}
|
||||
<XCircle className="flex h-4 w-4 items-center stroke-2" aria-hidden="true" />
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="line-clamp-3 h-[60px] text-sm text-gray-700/70 dark:text-gray-50/70">
|
||||
{plugin.description}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default PluginStoreItem;
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
type TPluginStoreLinkButtonProps = {
|
||||
onClick: () => void;
|
||||
label: string;
|
||||
};
|
||||
|
||||
function PluginStoreLinkButton({ onClick, label }: TPluginStoreLinkButtonProps) {
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
onClick={onClick}
|
||||
className="text-sm text-black/70 hover:text-black/50 dark:text-white/70 dark:hover:text-white/50"
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default PluginStoreLinkButton;
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { HoverCardPortal, HoverCardContent } from '@librechat/client';
|
||||
import './styles.module.css';
|
||||
|
||||
type TPluginTooltipProps = {
|
||||
content: string;
|
||||
|
|
@ -9,11 +8,9 @@ type TPluginTooltipProps = {
|
|||
function PluginTooltip({ content, position }: TPluginTooltipProps) {
|
||||
return (
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent side={position} className="w-80 ">
|
||||
<HoverCardContent side={position} className="w-80">
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm text-gray-600 dark:text-gray-300">
|
||||
{content}
|
||||
</div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-300">{content}</div>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCardPortal>
|
||||
|
|
|
|||
|
|
@ -1,223 +0,0 @@
|
|||
import { render, screen, fireEvent } from 'test/layout-test-utils';
|
||||
import PluginStoreDialog from '../PluginStoreDialog';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import * as mockDataProvider from 'librechat-data-provider/react-query';
|
||||
import * as authMutations from '~/data-provider/Auth/mutations';
|
||||
import * as authQueries from '~/data-provider/Auth/queries';
|
||||
|
||||
jest.mock('librechat-data-provider/react-query');
|
||||
|
||||
class ResizeObserver {
|
||||
observe() {
|
||||
// do nothing
|
||||
}
|
||||
unobserve() {
|
||||
// do nothing
|
||||
}
|
||||
disconnect() {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
window.ResizeObserver = ResizeObserver;
|
||||
|
||||
const pluginsQueryResult = [
|
||||
{
|
||||
name: 'Google',
|
||||
pluginKey: 'google',
|
||||
description: 'Use Google Search to find information',
|
||||
icon: 'https://i.imgur.com/SMmVkNB.png',
|
||||
authConfig: [
|
||||
{
|
||||
authField: 'GOOGLE_CSE_ID',
|
||||
label: 'Google CSE ID',
|
||||
description: 'This is your Google Custom Search Engine ID.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Wolfram',
|
||||
pluginKey: 'wolfram',
|
||||
description:
|
||||
'Access computation, math, curated knowledge & real-time data through Wolfram|Alpha and Wolfram Language.',
|
||||
icon: 'https://www.wolframcdn.com/images/icons/Wolfram.png',
|
||||
authConfig: [
|
||||
{
|
||||
authField: 'WOLFRAM_APP_ID',
|
||||
label: 'Wolfram App ID',
|
||||
description: 'An AppID must be supplied in all calls to the Wolfram|Alpha API.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Calculator',
|
||||
pluginKey: 'calculator',
|
||||
description: 'A simple calculator plugin',
|
||||
icon: 'https://i.imgur.com/SMmVkNB.png',
|
||||
authConfig: [],
|
||||
},
|
||||
{
|
||||
name: 'Plugin 1',
|
||||
pluginKey: 'plugin1',
|
||||
description: 'description for Plugin 1.',
|
||||
icon: 'mock-icon',
|
||||
authConfig: [],
|
||||
},
|
||||
{
|
||||
name: 'Plugin 2',
|
||||
pluginKey: 'plugin2',
|
||||
description: 'description for Plugin 2.',
|
||||
icon: 'mock-icon',
|
||||
authConfig: [],
|
||||
},
|
||||
{
|
||||
name: 'Plugin 3',
|
||||
pluginKey: 'plugin3',
|
||||
description: 'description for Plugin 3.',
|
||||
icon: 'mock-icon',
|
||||
authConfig: [],
|
||||
},
|
||||
{
|
||||
name: 'Plugin 4',
|
||||
pluginKey: 'plugin4',
|
||||
description: 'description for Plugin 4.',
|
||||
icon: 'mock-icon',
|
||||
authConfig: [],
|
||||
},
|
||||
{
|
||||
name: 'Plugin 5',
|
||||
pluginKey: 'plugin5',
|
||||
description: 'description for Plugin 5.',
|
||||
icon: 'mock-icon',
|
||||
authConfig: [],
|
||||
},
|
||||
{
|
||||
name: 'Plugin 6',
|
||||
pluginKey: 'plugin6',
|
||||
description: 'description for Plugin 6.',
|
||||
icon: 'mock-icon',
|
||||
authConfig: [],
|
||||
},
|
||||
{
|
||||
name: 'Plugin 7',
|
||||
pluginKey: 'plugin7',
|
||||
description: 'description for Plugin 7.',
|
||||
icon: 'mock-icon',
|
||||
authConfig: [],
|
||||
},
|
||||
];
|
||||
|
||||
const setup = ({
|
||||
useGetUserQueryReturnValue = {
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
data: {
|
||||
plugins: ['wolfram'],
|
||||
},
|
||||
},
|
||||
useRefreshTokenMutationReturnValue = {
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
mutate: jest.fn(),
|
||||
data: {
|
||||
token: 'mock-token',
|
||||
user: {},
|
||||
},
|
||||
},
|
||||
useAvailablePluginsQueryReturnValue = {
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
data: pluginsQueryResult,
|
||||
},
|
||||
useUpdateUserPluginsMutationReturnValue = {
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
mutate: jest.fn(),
|
||||
data: {},
|
||||
},
|
||||
} = {}) => {
|
||||
const mockUseAvailablePluginsQuery = jest
|
||||
.spyOn(mockDataProvider, 'useAvailablePluginsQuery')
|
||||
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
|
||||
.mockReturnValue(useAvailablePluginsQueryReturnValue);
|
||||
const mockUseUpdateUserPluginsMutation = jest
|
||||
.spyOn(mockDataProvider, 'useUpdateUserPluginsMutation')
|
||||
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
|
||||
.mockReturnValue(useUpdateUserPluginsMutationReturnValue);
|
||||
const mockUseGetUserQuery = jest
|
||||
.spyOn(authQueries, 'useGetUserQuery')
|
||||
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
|
||||
.mockReturnValue(useGetUserQueryReturnValue);
|
||||
const mockUseRefreshTokenMutation = jest
|
||||
.spyOn(authMutations, 'useRefreshTokenMutation')
|
||||
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
|
||||
.mockReturnValue(useRefreshTokenMutationReturnValue);
|
||||
const mockSetIsOpen = jest.fn();
|
||||
const renderResult = render(<PluginStoreDialog isOpen={true} setIsOpen={mockSetIsOpen} />);
|
||||
|
||||
return {
|
||||
...renderResult,
|
||||
mockUseGetUserQuery,
|
||||
mockUseAvailablePluginsQuery,
|
||||
mockUseUpdateUserPluginsMutation,
|
||||
mockUseRefreshTokenMutation,
|
||||
mockSetIsOpen,
|
||||
};
|
||||
};
|
||||
|
||||
test('renders plugin store dialog with plugins from the available plugins query and shows install/uninstall buttons based on user plugins', () => {
|
||||
const { getByText, getByRole } = setup();
|
||||
expect(getByText(/Plugin Store/i)).toBeInTheDocument();
|
||||
expect(getByText(/Use Google Search to find information/i)).toBeInTheDocument();
|
||||
expect(getByRole('button', { name: 'Install Google' })).toBeInTheDocument();
|
||||
expect(getByRole('button', { name: 'Uninstall Wolfram' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Displays the plugin auth form when installing a plugin with auth', async () => {
|
||||
const { getByRole, getByText } = setup();
|
||||
const googleButton = getByRole('button', { name: 'Install Google' });
|
||||
await userEvent.click(googleButton);
|
||||
expect(getByText(/Google CSE ID/i)).toBeInTheDocument();
|
||||
expect(getByRole('button', { name: 'Save' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('allows the user to navigate between pages', async () => {
|
||||
const { getByRole, getByText } = setup();
|
||||
|
||||
expect(getByText('Google')).toBeInTheDocument();
|
||||
expect(getByText('Wolfram')).toBeInTheDocument();
|
||||
expect(getByText('Plugin 1')).toBeInTheDocument();
|
||||
|
||||
const nextPageButton = getByRole('button', { name: 'Next page' });
|
||||
await userEvent.click(nextPageButton);
|
||||
|
||||
expect(getByText('Plugin 6')).toBeInTheDocument();
|
||||
expect(getByText('Plugin 7')).toBeInTheDocument();
|
||||
// expect(getByText('Plugin 3')).toBeInTheDocument();
|
||||
// expect(getByText('Plugin 4')).toBeInTheDocument();
|
||||
// expect(getByText('Plugin 5')).toBeInTheDocument();
|
||||
|
||||
const previousPageButton = getByRole('button', { name: 'Previous page' });
|
||||
await userEvent.click(previousPageButton);
|
||||
|
||||
expect(getByText('Google')).toBeInTheDocument();
|
||||
expect(getByText('Wolfram')).toBeInTheDocument();
|
||||
expect(getByText('Plugin 1')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('allows the user to search for plugins', async () => {
|
||||
setup();
|
||||
|
||||
const searchInput = screen.getByPlaceholderText('Search plugins');
|
||||
fireEvent.change(searchInput, { target: { value: 'Google' } });
|
||||
|
||||
expect(screen.getByText('Google')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Wolfram')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Plugin 1')).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.change(searchInput, { target: { value: 'Plugin 1' } });
|
||||
|
||||
expect(screen.getByText('Plugin 1')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Google')).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Wolfram')).not.toBeInTheDocument();
|
||||
});
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
import 'test/matchMedia.mock';
|
||||
import { render, screen } from 'test/layout-test-utils';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { TPlugin } from 'librechat-data-provider';
|
||||
import PluginStoreItem from '../PluginStoreItem';
|
||||
|
||||
const mockPlugin = {
|
||||
name: 'Test Plugin',
|
||||
description: 'This is a test plugin',
|
||||
icon: 'test-icon.png',
|
||||
};
|
||||
|
||||
describe('PluginStoreItem', () => {
|
||||
it('renders the plugin name and description', () => {
|
||||
render(
|
||||
<PluginStoreItem
|
||||
plugin={mockPlugin as TPlugin}
|
||||
onInstall={() => {
|
||||
return;
|
||||
}}
|
||||
onUninstall={() => {
|
||||
return;
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText('Test Plugin')).toBeInTheDocument();
|
||||
expect(screen.getByText('This is a test plugin')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls onInstall when the install button is clicked', async () => {
|
||||
const onInstall = jest.fn();
|
||||
render(
|
||||
<PluginStoreItem
|
||||
plugin={mockPlugin as TPlugin}
|
||||
onInstall={onInstall}
|
||||
onUninstall={() => {
|
||||
return;
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
await userEvent.click(screen.getByText('Install'));
|
||||
expect(onInstall).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('calls onUninstall when the uninstall button is clicked', async () => {
|
||||
const onUninstall = jest.fn();
|
||||
render(
|
||||
<PluginStoreItem
|
||||
plugin={mockPlugin as TPlugin}
|
||||
onInstall={() => {
|
||||
return;
|
||||
}}
|
||||
onUninstall={onUninstall}
|
||||
isInstalled
|
||||
/>,
|
||||
);
|
||||
await userEvent.click(screen.getByText('Uninstall'));
|
||||
expect(onUninstall).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +1,3 @@
|
|||
export { default as PluginStoreDialog } from './PluginStoreDialog';
|
||||
export { default as PluginStoreItem } from './PluginStoreItem';
|
||||
export { default as PluginPagination } from './PluginPagination';
|
||||
export { default as PluginStoreLinkButton } from './PluginStoreLinkButton';
|
||||
export { default as PluginAuthForm } from './PluginAuthForm';
|
||||
export { default as PluginTooltip } from './PluginTooltip';
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
a {
|
||||
text-decoration: underline;
|
||||
color: white;
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
export * from './Store';
|
||||
|
|
@ -125,7 +125,9 @@ const VersionCard = ({
|
|||
|
||||
<div className="flex items-center gap-1 lg:flex-col xl:flex-row">
|
||||
{authorName && (
|
||||
<Label className="text-left text-xs text-text-secondary">by {authorName}</Label>
|
||||
<Label className="text-left text-xs text-text-secondary">
|
||||
{localize('com_ui_by_author', { 0: authorName })}
|
||||
</Label>
|
||||
)}
|
||||
|
||||
{tags.length > 0 && <VersionTags tags={tags} />}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import MinimalHoverButtons from '~/components/Chat/Messages/MinimalHoverButtons'
|
|||
import MessageContent from '~/components/Chat/Messages/Content/MessageContent';
|
||||
import SearchContent from '~/components/Chat/Messages/Content/SearchContent';
|
||||
import SiblingSwitch from '~/components/Chat/Messages/SiblingSwitch';
|
||||
import { Plugin } from '~/components/Messages/Content';
|
||||
import SubRow from '~/components/Chat/Messages/SubRow';
|
||||
import { fontSizeAtom } from '~/store/fontSize';
|
||||
import { MessageContext } from '~/Providers';
|
||||
|
|
@ -80,8 +79,6 @@ export default function Message(props: TMessageProps) {
|
|||
isLatestMessage: false, // No concept of latest message in share view
|
||||
}}
|
||||
>
|
||||
{/* Legacy Plugins */}
|
||||
{message.plugin && <Plugin plugin={message.plugin} />}
|
||||
{message.content ? (
|
||||
<SearchContent
|
||||
message={message}
|
||||
|
|
|
|||
|
|
@ -50,8 +50,6 @@ jest.mock('librechat-data-provider', () => {
|
|||
},
|
||||
EModelEndpoint: actualModule.EModelEndpoint || {
|
||||
agents: 'agents',
|
||||
chatGPTBrowser: 'chatGPTBrowser',
|
||||
gptPlugins: 'gptPlugins',
|
||||
},
|
||||
ResourceType: actualModule.ResourceType || {
|
||||
AGENT: 'agent',
|
||||
|
|
|
|||
|
|
@ -306,9 +306,7 @@ export default function AgentPanel() {
|
|||
(key) =>
|
||||
!isAssistantsEndpoint(key) &&
|
||||
(allowedProviders.size > 0 ? allowedProviders.has(key) : true) &&
|
||||
key !== EModelEndpoint.agents &&
|
||||
key !== EModelEndpoint.chatGPTBrowser &&
|
||||
key !== EModelEndpoint.gptPlugins,
|
||||
key !== EModelEndpoint.agents,
|
||||
)
|
||||
.map((provider) => createProviderOption(provider)),
|
||||
[endpointsConfig, allowedProviders],
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import type {
|
|||
TPluginAction,
|
||||
TError,
|
||||
} from 'librechat-data-provider';
|
||||
import type { TPluginStoreDialogProps } from '~/common/types';
|
||||
import type { ToolDialogProps } from '~/common/types';
|
||||
import { PluginPagination, PluginAuthForm } from '~/components/Plugins/Store';
|
||||
import { useLocalize, usePluginDialogHelpers } from '~/hooks';
|
||||
import { useAvailableToolsQuery } from '~/data-provider';
|
||||
|
|
@ -20,7 +20,7 @@ function AssistantToolsDialog({
|
|||
isOpen,
|
||||
endpoint,
|
||||
setIsOpen,
|
||||
}: TPluginStoreDialogProps & {
|
||||
}: ToolDialogProps & {
|
||||
endpoint: AssistantsEndpoint | EModelEndpoint.agents;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Constants, EModelEndpoint, QueryKeys } from 'librechat-data-provider';
|
|||
import { Dialog, DialogPanel, DialogTitle, Description } from '@headlessui/react';
|
||||
import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query';
|
||||
import type { TError, AgentToolType } from 'librechat-data-provider';
|
||||
import type { AgentForm, TPluginStoreDialogProps } from '~/common';
|
||||
import type { AgentForm, ToolDialogProps } from '~/common';
|
||||
import {
|
||||
usePluginDialogHelpers,
|
||||
useMCPServerManager,
|
||||
|
|
@ -24,7 +24,7 @@ function MCPToolSelectDialog({
|
|||
agentId,
|
||||
setIsOpen,
|
||||
mcpServerNames,
|
||||
}: TPluginStoreDialogProps & {
|
||||
}: ToolDialogProps & {
|
||||
agentId: string;
|
||||
mcpServerNames?: string[];
|
||||
endpoint: EModelEndpoint.agents;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import type {
|
|||
TPlugin,
|
||||
TError,
|
||||
} from 'librechat-data-provider';
|
||||
import type { AgentForm, TPluginStoreDialogProps } from '~/common';
|
||||
import type { AgentForm, ToolDialogProps } from '~/common';
|
||||
import { PluginPagination, PluginAuthForm } from '~/components/Plugins/Store';
|
||||
import { useAgentPanelContext } from '~/Providers/AgentPanelContext';
|
||||
import { useLocalize, usePluginDialogHelpers } from '~/hooks';
|
||||
|
|
@ -21,7 +21,7 @@ function ToolSelectDialog({
|
|||
isOpen,
|
||||
endpoint,
|
||||
setIsOpen,
|
||||
}: TPluginStoreDialogProps & {
|
||||
}: ToolDialogProps & {
|
||||
endpoint: AssistantsEndpoint | EModelEndpoint.agents;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
|
|
|
|||
|
|
@ -130,13 +130,10 @@ export default function useChatHelpers(index = 0, paramId?: string) {
|
|||
setSiblingIdx(0);
|
||||
};
|
||||
|
||||
const [preset, setPreset] = useRecoilState(store.presetByIndex(index));
|
||||
const [showPopover, setShowPopover] = useRecoilState(store.showPopoverFamily(index));
|
||||
const [abortScroll, setAbortScroll] = useRecoilState(store.abortScrollFamily(index));
|
||||
const [preset, setPreset] = useRecoilState(store.presetByIndex(index));
|
||||
const [optionSettings, setOptionSettings] = useRecoilState(store.optionSettingsFamily(index));
|
||||
const [showAgentSettings, setShowAgentSettings] = useRecoilState(
|
||||
store.showAgentSettingsFamily(index),
|
||||
);
|
||||
|
||||
return {
|
||||
newConversation,
|
||||
|
|
@ -167,8 +164,6 @@ export default function useChatHelpers(index = 0, paramId?: string) {
|
|||
setPreset,
|
||||
optionSettings,
|
||||
setOptionSettings,
|
||||
showAgentSettings,
|
||||
setShowAgentSettings,
|
||||
files,
|
||||
setFiles,
|
||||
filesLoading,
|
||||
|
|
|
|||
|
|
@ -1,25 +1,13 @@
|
|||
import { useEffect } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import TagManager from 'react-gtm-module';
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { LocalStorageKeys } from 'librechat-data-provider';
|
||||
import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { TStartupConfig, TPlugin, TUser } from 'librechat-data-provider';
|
||||
import { mapPlugins, selectPlugins, processPlugins } from '~/utils';
|
||||
import type { TStartupConfig, TUser } from 'librechat-data-provider';
|
||||
import { cleanupTimestampedStorage } from '~/utils/timestamps';
|
||||
import useSpeechSettingsInit from './useSpeechSettingsInit';
|
||||
import { useMCPToolsQuery } from '~/data-provider';
|
||||
import store from '~/store';
|
||||
|
||||
const pluginStore: TPlugin = {
|
||||
name: 'Plugin store',
|
||||
pluginKey: 'pluginStore',
|
||||
isButton: true,
|
||||
description: '',
|
||||
icon: '',
|
||||
authConfig: [],
|
||||
authenticated: false,
|
||||
};
|
||||
|
||||
export default function useAppStartup({
|
||||
startupConfig,
|
||||
user,
|
||||
|
|
@ -27,12 +15,7 @@ export default function useAppStartup({
|
|||
startupConfig?: TStartupConfig;
|
||||
user?: TUser;
|
||||
}) {
|
||||
const setAvailableTools = useSetRecoilState(store.availableTools);
|
||||
const [defaultPreset, setDefaultPreset] = useRecoilState(store.defaultPreset);
|
||||
const { data: allPlugins } = useAvailablePluginsQuery({
|
||||
enabled: !!user?.plugins,
|
||||
select: selectPlugins,
|
||||
});
|
||||
|
||||
useSpeechSettingsInit(!!user);
|
||||
|
||||
|
|
@ -80,43 +63,6 @@ export default function useAppStartup({
|
|||
});
|
||||
}, [defaultPreset, setDefaultPreset, startupConfig?.modelSpecs?.list]);
|
||||
|
||||
/** Set the available Plugins */
|
||||
useEffect(() => {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!allPlugins) {
|
||||
return;
|
||||
}
|
||||
|
||||
const userPlugins = user.plugins ?? [];
|
||||
|
||||
if (userPlugins.length === 0) {
|
||||
setAvailableTools({ pluginStore });
|
||||
return;
|
||||
}
|
||||
|
||||
const tools = [...userPlugins]
|
||||
.map((el) => allPlugins.map[el])
|
||||
.filter((el: TPlugin | undefined): el is TPlugin => el !== undefined);
|
||||
|
||||
/* Filter Last Selected Tools */
|
||||
const localStorageItem = localStorage.getItem(LocalStorageKeys.LAST_TOOLS) ?? '';
|
||||
if (!localStorageItem) {
|
||||
return setAvailableTools({ pluginStore, ...mapPlugins(tools) });
|
||||
}
|
||||
const lastSelectedTools = processPlugins(JSON.parse(localStorageItem) ?? [], allPlugins.map);
|
||||
const filteredTools = lastSelectedTools
|
||||
.filter((tool: TPlugin) =>
|
||||
tools.some((existingTool) => existingTool.pluginKey === tool.pluginKey),
|
||||
)
|
||||
.filter((tool: TPlugin | undefined) => !!tool);
|
||||
localStorage.setItem(LocalStorageKeys.LAST_TOOLS, JSON.stringify(filteredTools));
|
||||
|
||||
setAvailableTools({ pluginStore, ...mapPlugins(tools) });
|
||||
}, [allPlugins, user, setAvailableTools]);
|
||||
|
||||
useEffect(() => {
|
||||
if (startupConfig?.analyticsGtmId != null && typeof window.google_tag_manager === 'undefined') {
|
||||
const tagManagerArgs = {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ export default function useClearStates() {
|
|||
reset(store.abortScrollFamily(key));
|
||||
reset(store.isSubmittingFamily(key));
|
||||
reset(store.optionSettingsFamily(key));
|
||||
reset(store.showAgentSettingsFamily(key));
|
||||
reset(store.showPopoverFamily(key));
|
||||
reset(store.showMentionPopoverFamily(key));
|
||||
reset(store.showPlusPopoverFamily(key));
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import type { TPreset, TPlugin } from 'librechat-data-provider';
|
||||
import type { TPreset } from 'librechat-data-provider';
|
||||
import type { TSetOptionsPayload, TSetExample, TSetOption, TSetOptions } from '~/common';
|
||||
import { useChatContext } from '~/Providers/ChatContext';
|
||||
import { cleanupPreset } from '~/utils';
|
||||
import store from '~/store';
|
||||
|
||||
type TUsePresetOptions = (preset?: TPreset | boolean | null) => TSetOptionsPayload | boolean;
|
||||
|
||||
const usePresetIndexOptions: TUsePresetOptions = (_preset) => {
|
||||
const setShowPluginStoreDialog = useSetRecoilState(store.showPluginStoreDialog);
|
||||
const availableTools = useRecoilValue(store.availableTools);
|
||||
const { preset, setPreset } = useChatContext();
|
||||
|
||||
if (!_preset) {
|
||||
|
|
@ -101,68 +97,6 @@ const usePresetIndexOptions: TUsePresetOptions = (_preset) => {
|
|||
);
|
||||
};
|
||||
|
||||
const setAgentOption: TSetOption = (param) => (newValue) => {
|
||||
const editablePreset = JSON.parse(JSON.stringify(_preset));
|
||||
const { agentOptions } = editablePreset;
|
||||
agentOptions[param] = newValue;
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
agentOptions,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
function checkPluginSelection(value: string) {
|
||||
if (!preset?.tools) {
|
||||
return false;
|
||||
}
|
||||
return preset.tools.find((el) => {
|
||||
if (typeof el === 'string') {
|
||||
return el === value;
|
||||
}
|
||||
return el.pluginKey === value;
|
||||
})
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
const setTools: (newValue: string, remove?: boolean) => void = (newValue, remove) => {
|
||||
if (newValue === 'pluginStore') {
|
||||
setShowPluginStoreDialog(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const update = {};
|
||||
const current =
|
||||
preset?.tools
|
||||
?.map((tool: string | TPlugin) => {
|
||||
if (typeof tool === 'string') {
|
||||
return availableTools[tool];
|
||||
}
|
||||
return tool;
|
||||
})
|
||||
.filter((el) => !!el) || [];
|
||||
const isSelected = checkPluginSelection(newValue);
|
||||
const tool = availableTools[newValue];
|
||||
if (isSelected || remove) {
|
||||
update['tools'] = current.filter((el) => el.pluginKey !== newValue);
|
||||
} else {
|
||||
update['tools'] = [...current, tool];
|
||||
}
|
||||
|
||||
setPreset((prevState) =>
|
||||
cleanupPreset({
|
||||
preset: {
|
||||
...prevState,
|
||||
...update,
|
||||
},
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
setOption,
|
||||
setExample,
|
||||
|
|
@ -170,9 +104,6 @@ const usePresetIndexOptions: TUsePresetOptions = (_preset) => {
|
|||
setOptions,
|
||||
removeExample,
|
||||
getConversation,
|
||||
checkPluginSelection,
|
||||
setAgentOption,
|
||||
setTools,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,16 @@
|
|||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import {
|
||||
TPreset,
|
||||
TPlugin,
|
||||
TConversation,
|
||||
tConvoUpdateSchema,
|
||||
EModelEndpoint,
|
||||
tConvoUpdateSchema,
|
||||
} from 'librechat-data-provider';
|
||||
import type { TSetExample, TSetOption, TSetOptionsPayload } from '~/common';
|
||||
import usePresetIndexOptions from './usePresetIndexOptions';
|
||||
import { useChatContext } from '~/Providers/ChatContext';
|
||||
import store from '~/store';
|
||||
|
||||
type TUseSetOptions = (preset?: TPreset | boolean | null) => TSetOptionsPayload;
|
||||
|
||||
const useSetIndexOptions: TUseSetOptions = (preset = false) => {
|
||||
const setShowPluginStoreDialog = useSetRecoilState(store.showPluginStoreDialog);
|
||||
const availableTools = useRecoilValue(store.availableTools);
|
||||
const { conversation, setConversation } = useChatContext();
|
||||
|
||||
const result = usePresetIndexOptions(preset);
|
||||
|
|
@ -116,76 +111,11 @@ const useSetIndexOptions: TUseSetOptions = (preset = false) => {
|
|||
);
|
||||
};
|
||||
|
||||
function checkPluginSelection(value: string) {
|
||||
if (!conversation?.tools) {
|
||||
return false;
|
||||
}
|
||||
return conversation.tools.find((el) => {
|
||||
if (typeof el === 'string') {
|
||||
return el === value;
|
||||
}
|
||||
return el.pluginKey === value;
|
||||
})
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
const setAgentOption: TSetOption = (param) => (newValue) => {
|
||||
const editableConvo = JSON.stringify(conversation);
|
||||
const convo = JSON.parse(editableConvo);
|
||||
const { agentOptions } = convo;
|
||||
agentOptions[param] = newValue;
|
||||
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
tConvoUpdateSchema.parse({
|
||||
...prevState,
|
||||
agentOptions,
|
||||
}) as TConversation,
|
||||
);
|
||||
};
|
||||
|
||||
const setTools: (newValue: string, remove?: boolean) => void = (newValue, remove) => {
|
||||
if (newValue === 'pluginStore') {
|
||||
setShowPluginStoreDialog(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const update = {};
|
||||
const current =
|
||||
conversation?.tools
|
||||
?.map((tool: string | TPlugin) => {
|
||||
if (typeof tool === 'string') {
|
||||
return availableTools[tool];
|
||||
}
|
||||
return tool;
|
||||
})
|
||||
.filter((el) => !!el) || [];
|
||||
const isSelected = checkPluginSelection(newValue);
|
||||
const tool = availableTools[newValue];
|
||||
if (isSelected || remove) {
|
||||
update['tools'] = current.filter((el) => el.pluginKey !== newValue);
|
||||
} else {
|
||||
update['tools'] = [...current, tool];
|
||||
}
|
||||
|
||||
setConversation(
|
||||
(prevState) =>
|
||||
tConvoUpdateSchema.parse({
|
||||
...prevState,
|
||||
...update,
|
||||
}) as TConversation,
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
setTools,
|
||||
setOption,
|
||||
setExample,
|
||||
addExample,
|
||||
removeExample,
|
||||
setAgentOption,
|
||||
checkPluginSelection,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
import { Feather } from 'lucide-react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import {
|
||||
MinimalPlugin,
|
||||
GPTIcon,
|
||||
Sparkles,
|
||||
BedrockIcon,
|
||||
AssistantIcon,
|
||||
AnthropicIcon,
|
||||
AzureMinimalIcon,
|
||||
GoogleMinimalIcon,
|
||||
CustomMinimalIcon,
|
||||
AssistantIcon,
|
||||
LightningIcon,
|
||||
BedrockIcon,
|
||||
Sparkles,
|
||||
} from '@librechat/client';
|
||||
import type { IconMapProps, AgentIconMapProps, IconsRecord } from '~/common';
|
||||
import UnknownIcon from './UnknownIcon';
|
||||
|
|
@ -63,9 +61,7 @@ const Bedrock = ({ className = '' }: IconMapProps) => {
|
|||
export const icons: IconsRecord = {
|
||||
[EModelEndpoint.azureOpenAI]: AzureMinimalIcon,
|
||||
[EModelEndpoint.openAI]: GPTIcon,
|
||||
[EModelEndpoint.gptPlugins]: MinimalPlugin,
|
||||
[EModelEndpoint.anthropic]: AnthropicIcon,
|
||||
[EModelEndpoint.chatGPTBrowser]: LightningIcon,
|
||||
[EModelEndpoint.google]: GoogleMinimalIcon,
|
||||
[EModelEndpoint.custom]: CustomMinimalIcon,
|
||||
[EModelEndpoint.assistants]: AssistantAvatar,
|
||||
|
|
|
|||
|
|
@ -12,8 +12,6 @@ const useUserKey = (endpoint: string) => {
|
|||
|
||||
if (azure) {
|
||||
keyName = EModelEndpoint.azureOpenAI;
|
||||
} else if (keyName === EModelEndpoint.gptPlugins) {
|
||||
keyName = EModelEndpoint.openAI;
|
||||
}
|
||||
|
||||
const updateKey = useUpdateUserKeysMutation();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
export * from './useToolToggle';
|
||||
export { default as useAuthCodeTool } from './useAuthCodeTool';
|
||||
export { default as usePluginInstall } from './usePluginInstall';
|
||||
export { default as useCodeApiKeyForm } from './useCodeApiKeyForm';
|
||||
export { default as useSearchApiKeyForm } from './useSearchApiKeyForm';
|
||||
export { default as usePluginDialogHelpers } from './usePluginDialogHelpers';
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
// hooks/Plugins/usePluginInstall.ts
|
||||
import { useCallback } from 'react';
|
||||
import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query';
|
||||
import type {
|
||||
TError,
|
||||
TUser,
|
||||
TUpdateUserPlugins,
|
||||
TPlugin,
|
||||
TPluginAction,
|
||||
} from 'librechat-data-provider';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import store from '~/store';
|
||||
|
||||
interface PluginStoreHandlers {
|
||||
onInstallError?: (error: TError) => void;
|
||||
onUninstallError?: (error: TError) => void;
|
||||
onInstallSuccess?: (data: TUser, variables: TUpdateUserPlugins, context: unknown) => void;
|
||||
onUninstallSuccess?: (data: TUser, variables: TUpdateUserPlugins, context: unknown) => void;
|
||||
}
|
||||
|
||||
export default function usePluginInstall(handlers: PluginStoreHandlers = {}) {
|
||||
const setAvailableTools = useSetRecoilState(store.availableTools);
|
||||
const { onInstallError, onInstallSuccess, onUninstallError, onUninstallSuccess } = handlers;
|
||||
const updateUserPlugins = useUpdateUserPluginsMutation();
|
||||
|
||||
const installPlugin = useCallback(
|
||||
(pluginAction: TPluginAction, plugin: TPlugin) => {
|
||||
updateUserPlugins.mutate(pluginAction, {
|
||||
onError: (error: unknown) => {
|
||||
if (onInstallError) {
|
||||
onInstallError(error as TError);
|
||||
}
|
||||
},
|
||||
onSuccess: (...rest) => {
|
||||
setAvailableTools((prev) => {
|
||||
return { ...prev, [plugin.pluginKey]: plugin };
|
||||
});
|
||||
if (onInstallSuccess) {
|
||||
onInstallSuccess(...rest);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
[updateUserPlugins, onInstallError, onInstallSuccess, setAvailableTools],
|
||||
);
|
||||
|
||||
const uninstallPlugin = useCallback(
|
||||
(plugin: string) => {
|
||||
updateUserPlugins.mutate(
|
||||
{ pluginKey: plugin, action: 'uninstall', auth: null },
|
||||
{
|
||||
onError: (error: unknown) => {
|
||||
if (onUninstallError) {
|
||||
onUninstallError(error as TError);
|
||||
}
|
||||
},
|
||||
onSuccess: (...rest) => {
|
||||
setAvailableTools((prev) => {
|
||||
const newAvailableTools = { ...prev };
|
||||
delete newAvailableTools[plugin];
|
||||
return newAvailableTools;
|
||||
});
|
||||
if (onUninstallSuccess) {
|
||||
onUninstallSuccess(...rest);
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
[updateUserPlugins, onUninstallError, onUninstallSuccess, setAvailableTools],
|
||||
);
|
||||
|
||||
return {
|
||||
installPlugin,
|
||||
uninstallPlugin,
|
||||
};
|
||||
}
|
||||
|
|
@ -201,14 +201,7 @@ export default function useEventHandlers({
|
|||
|
||||
const messageHandler = useCallback(
|
||||
(data: string | undefined, submission: EventSubmission) => {
|
||||
const {
|
||||
messages,
|
||||
userMessage,
|
||||
plugin,
|
||||
plugins,
|
||||
initialResponse,
|
||||
isRegenerate = false,
|
||||
} = submission;
|
||||
const { messages, userMessage, initialResponse, isRegenerate = false } = submission;
|
||||
const text = data ?? '';
|
||||
setIsSubmitting(true);
|
||||
|
||||
|
|
@ -224,8 +217,6 @@ export default function useEventHandlers({
|
|||
{
|
||||
...initialResponse,
|
||||
text,
|
||||
plugin: plugin ?? null,
|
||||
plugins: plugins ?? [],
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
|
|
@ -235,8 +226,6 @@ export default function useEventHandlers({
|
|||
{
|
||||
...initialResponse,
|
||||
text,
|
||||
plugin: plugin ?? null,
|
||||
plugins: plugins ?? [],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,9 +123,8 @@ export default function useSSE(
|
|||
|
||||
if (data.final != null) {
|
||||
clearDraft(submission.conversation?.conversationId);
|
||||
const { plugins } = data;
|
||||
try {
|
||||
finalHandler(data, { ...submission, plugins } as EventSubmission);
|
||||
finalHandler(data, submission as EventSubmission);
|
||||
} catch (error) {
|
||||
console.error('Error in finalHandler:', error);
|
||||
setIsSubmitting(false);
|
||||
|
|
@ -160,7 +159,6 @@ export default function useSSE(
|
|||
contentHandler({ data, submission: submission as EventSubmission });
|
||||
} else {
|
||||
const text = data.text ?? data.response;
|
||||
const { plugin, plugins } = data;
|
||||
|
||||
const initialResponse = {
|
||||
...(submission.initialResponse as TMessage),
|
||||
|
|
@ -169,7 +167,7 @@ export default function useSSE(
|
|||
};
|
||||
|
||||
if (data.message != null) {
|
||||
messageHandler(text, { ...submission, plugin, plugins, userMessage, initialResponse });
|
||||
messageHandler(text, { ...submission, userMessage, initialResponse });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ export default function useGenerationsByLatest({
|
|||
EModelEndpoint.agents,
|
||||
EModelEndpoint.bedrock,
|
||||
EModelEndpoint.anthropic,
|
||||
EModelEndpoint.gptPlugins,
|
||||
EModelEndpoint.azureOpenAI,
|
||||
].find((e) => e === endpoint),
|
||||
);
|
||||
|
|
@ -51,9 +50,7 @@ export default function useGenerationsByLatest({
|
|||
EModelEndpoint.custom,
|
||||
EModelEndpoint.agents,
|
||||
EModelEndpoint.bedrock,
|
||||
EModelEndpoint.chatGPTBrowser,
|
||||
EModelEndpoint.google,
|
||||
EModelEndpoint.gptPlugins,
|
||||
EModelEndpoint.anthropic,
|
||||
].find((e) => e === endpoint),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -222,7 +222,6 @@
|
|||
"com_download_expires": "(click here to download - expires {{0}})",
|
||||
"com_endpoint": "Endpoint",
|
||||
"com_endpoint_agent": "Agent",
|
||||
"com_endpoint_agent_model": "Agent Model (Recommended: GPT-3.5)",
|
||||
"com_endpoint_agent_placeholder": "Please select an Agent",
|
||||
"com_endpoint_ai": "AI",
|
||||
"com_endpoint_anthropic_maxoutputtokens": "Maximum number of tokens that can be generated in the response. Specify a lower value for shorter responses and a higher value for longer responses. Note: models may stop before reaching this maximum.",
|
||||
|
|
@ -236,8 +235,6 @@
|
|||
"com_endpoint_assistant": "Assistant",
|
||||
"com_endpoint_assistant_model": "Assistant Model",
|
||||
"com_endpoint_assistant_placeholder": "Please select an Assistant from the right-hand Side Panel",
|
||||
"com_endpoint_completion": "Completion",
|
||||
"com_endpoint_completion_model": "Completion Model (Recommended: GPT-4)",
|
||||
"com_endpoint_config_click_here": "Click Here",
|
||||
"com_endpoint_config_google_api_info": "To get your Generative Language API key (for Gemini),",
|
||||
"com_endpoint_config_google_api_key": "Google API Key",
|
||||
|
|
@ -265,18 +262,13 @@
|
|||
"com_endpoint_custom_name": "Custom Name",
|
||||
"com_endpoint_default": "default",
|
||||
"com_endpoint_default_blank": "default: blank",
|
||||
"com_endpoint_default_empty": "default: empty",
|
||||
"com_endpoint_default_with_num": "default: {{0}}",
|
||||
"com_endpoint_deprecated": "Deprecated",
|
||||
"com_endpoint_deprecated_info": "This endpoint is deprecated and may be removed in future versions, please use the agent endpoint instead",
|
||||
"com_endpoint_deprecated_info_a11y": "The plugin endpoint is deprecated and may be removed in future versions, please use the agent endpoint instead",
|
||||
"com_endpoint_disable_streaming": "Disable streaming responses and receive the complete response at once. Useful for models like o3 that require organization verification for streaming",
|
||||
"com_endpoint_disable_streaming_label": "Disable Streaming",
|
||||
"com_endpoint_examples": " Presets",
|
||||
"com_endpoint_export": "Export",
|
||||
"com_endpoint_export_share": "Export/Share",
|
||||
"com_endpoint_frequency_penalty": "Frequency Penalty",
|
||||
"com_endpoint_func_hover": "Enable use of Plugins as OpenAI Functions",
|
||||
"com_endpoint_google_custom_name_placeholder": "Set a custom name for Google",
|
||||
"com_endpoint_google_maxoutputtokens": "Maximum number of tokens that can be generated in the response. Specify a lower value for shorter responses and a higher value for longer responses. Note: models may stop before reaching this maximum.",
|
||||
"com_endpoint_google_temp": "Higher values = more random, while lower values = more focused and deterministic. We recommend altering this or Top P but not both.",
|
||||
|
|
@ -314,9 +306,6 @@
|
|||
"com_endpoint_output": "Output",
|
||||
"com_endpoint_plug_image_detail": "Image Detail",
|
||||
"com_endpoint_plug_resend_files": "Resend Files",
|
||||
"com_endpoint_plug_set_custom_instructions_for_gpt_placeholder": "Set custom instructions to include in System Message. Default: none",
|
||||
"com_endpoint_plug_skip_completion": "Skip Completion",
|
||||
"com_endpoint_plug_use_functions": "Use Functions",
|
||||
"com_endpoint_presence_penalty": "Presence Penalty",
|
||||
"com_endpoint_preset": "preset",
|
||||
"com_endpoint_preset_custom_name_placeholder": "something needs to go here. was empty",
|
||||
|
|
@ -348,7 +337,6 @@
|
|||
"com_endpoint_search_models": "Search models...",
|
||||
"com_endpoint_search_var": "Search {{0}}...",
|
||||
"com_endpoint_set_custom_name": "Set a custom name, in case you can find this preset",
|
||||
"com_endpoint_skip_hover": "Enable skipping the completion step, which reviews the final answer and generated steps",
|
||||
"com_endpoint_stop": "Stop Sequences",
|
||||
"com_endpoint_stop_placeholder": "Separate values by pressing `Enter`",
|
||||
"com_endpoint_temperature": "Temperature",
|
||||
|
|
@ -550,10 +538,6 @@
|
|||
"com_nav_open_sidebar": "Open sidebar",
|
||||
"com_nav_playback_rate": "Audio Playback Rate",
|
||||
"com_nav_plugin_auth_error": "There was an error attempting to authenticate this plugin. Please try again.",
|
||||
"com_nav_plugin_install": "Install",
|
||||
"com_nav_plugin_search": "Search plugins",
|
||||
"com_nav_plugin_store": "Plugin store",
|
||||
"com_nav_plugin_uninstall": "Uninstall",
|
||||
"com_nav_plus_command": "+-Command",
|
||||
"com_nav_plus_command_description": "Toggle command \"+\" for adding a multi-response setting",
|
||||
"com_nav_profile_picture": "Profile Picture",
|
||||
|
|
@ -594,8 +578,6 @@
|
|||
"com_nav_user_msg_markdown": "Render user messages as markdown",
|
||||
"com_nav_user_name_display": "Display username in messages",
|
||||
"com_nav_voice_select": "Voice",
|
||||
"com_show_agent_settings": "Show Agent Settings",
|
||||
"com_show_completion_settings": "Show Completion Settings",
|
||||
"com_show_examples": "Show Examples",
|
||||
"com_sidepanel_agent_builder": "Agent Builder",
|
||||
"com_sidepanel_assistant_builder": "Assistant Builder",
|
||||
|
|
@ -771,6 +753,7 @@
|
|||
"com_ui_bookmarks_title": "Title",
|
||||
"com_ui_bookmarks_update_error": "There was an error updating the bookmark",
|
||||
"com_ui_bookmarks_update_success": "Bookmark updated successfully",
|
||||
"com_ui_by_author": "by {{0}}",
|
||||
"com_ui_bulk_delete_error": "Failed to delete shared links",
|
||||
"com_ui_callback_url": "Callback URL",
|
||||
"com_ui_cancel": "Cancel",
|
||||
|
|
@ -1223,7 +1206,6 @@
|
|||
"com_ui_select_provider_first": "Select a provider first",
|
||||
"com_ui_select_region": "Select a region",
|
||||
"com_ui_select_search_model": "Search model by name",
|
||||
"com_ui_select_search_plugin": "Search plugin by name",
|
||||
"com_ui_select_search_provider": "Search provider by name",
|
||||
"com_ui_select_search_region": "Search region by name",
|
||||
"com_ui_set": "Set",
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ const defaultConfig: TEndpointsConfig = {
|
|||
[EModelEndpoint.assistants]: null,
|
||||
[EModelEndpoint.agents]: null,
|
||||
[EModelEndpoint.openAI]: null,
|
||||
[EModelEndpoint.chatGPTBrowser]: null,
|
||||
[EModelEndpoint.gptPlugins]: null,
|
||||
[EModelEndpoint.google]: null,
|
||||
[EModelEndpoint.anthropic]: null,
|
||||
[EModelEndpoint.custom]: null,
|
||||
|
|
@ -25,14 +23,6 @@ const endpointsQueryEnabled = atom<boolean>({
|
|||
default: true,
|
||||
});
|
||||
|
||||
const plugins = selector({
|
||||
key: 'plugins',
|
||||
get: ({ get }) => {
|
||||
const config = get(endpointsConfig) || {};
|
||||
return config.gptPlugins?.plugins || {};
|
||||
},
|
||||
});
|
||||
|
||||
const endpointsFilter = selector({
|
||||
key: 'endpointsFilter',
|
||||
get: ({ get }) => {
|
||||
|
|
@ -47,7 +37,6 @@ const endpointsFilter = selector({
|
|||
});
|
||||
|
||||
export default {
|
||||
plugins,
|
||||
endpointsConfig,
|
||||
endpointsFilter,
|
||||
defaultConfig,
|
||||
|
|
|
|||
|
|
@ -203,11 +203,6 @@ const optionSettingsFamily = atomFamily<TOptionSettings, string | number>({
|
|||
default: {},
|
||||
});
|
||||
|
||||
const showAgentSettingsFamily = atomFamily({
|
||||
key: 'showAgentSettingsByIndex',
|
||||
default: false,
|
||||
});
|
||||
|
||||
const showPopoverFamily = atomFamily({
|
||||
key: 'showPopoverByIndex',
|
||||
default: false,
|
||||
|
|
@ -403,7 +398,6 @@ export default {
|
|||
abortScrollFamily,
|
||||
isSubmittingFamily,
|
||||
optionSettingsFamily,
|
||||
showAgentSettingsFamily,
|
||||
showPopoverFamily,
|
||||
latestMessageFamily,
|
||||
messagesSiblingIdxFamily,
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ const staticAtoms = {
|
|||
abortScroll: atom<boolean>({ key: 'abortScroll', default: false }),
|
||||
showFiles: atom<boolean>({ key: 'showFiles', default: false }),
|
||||
optionSettings: atom<TOptionSettings>({ key: 'optionSettings', default: {} }),
|
||||
showPluginStoreDialog: atom<boolean>({ key: 'showPluginStoreDialog', default: false }),
|
||||
showAgentSettings: atom<boolean>({ key: 'showAgentSettings', default: false }),
|
||||
currentSettingsView: atom<SettingsViews>({
|
||||
key: 'currentSettingsView',
|
||||
default: SettingsViews.default,
|
||||
|
|
|
|||
|
|
@ -31,12 +31,8 @@ const buildDefaultConvo = ({
|
|||
|
||||
const availableModels = models;
|
||||
const model = lastConversationSetup?.model ?? lastSelectedModel?.[endpoint] ?? '';
|
||||
const secondaryModel: string | null =
|
||||
endpoint === EModelEndpoint.gptPlugins
|
||||
? (lastConversationSetup?.agentOptions?.model ?? lastSelectedModel?.secondaryModel ?? null)
|
||||
: null;
|
||||
|
||||
let possibleModels: string[], secondaryModels: string[];
|
||||
let possibleModels: string[];
|
||||
|
||||
if (availableModels.includes(model)) {
|
||||
possibleModels = [model, ...availableModels];
|
||||
|
|
@ -44,19 +40,12 @@ const buildDefaultConvo = ({
|
|||
possibleModels = [...availableModels];
|
||||
}
|
||||
|
||||
if (secondaryModel != null && secondaryModel !== '' && availableModels.includes(secondaryModel)) {
|
||||
secondaryModels = [secondaryModel, ...availableModels];
|
||||
} else {
|
||||
secondaryModels = [...availableModels];
|
||||
}
|
||||
|
||||
const convo = parseConvo({
|
||||
endpoint: endpoint as EndpointSchemaKey,
|
||||
endpointType: endpointType as EndpointSchemaKey,
|
||||
conversation: lastConversationSetup,
|
||||
possibleValues: {
|
||||
models: possibleModels,
|
||||
secondaryModels,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -539,19 +539,6 @@ describe('Conversation Utilities', () => {
|
|||
expect([undefined, 'gpt-3']).toContain(stored.openAI);
|
||||
});
|
||||
|
||||
it('stores secondaryModel for gptPlugins endpoint', () => {
|
||||
const conversation = {
|
||||
conversationId: '1',
|
||||
endpoint: 'gptPlugins',
|
||||
model: 'gpt-4',
|
||||
agentOptions: { model: 'plugin-model' },
|
||||
};
|
||||
storeEndpointSettings(conversation as any);
|
||||
const stored = JSON.parse(localStorage.getItem('lastModel') || '{}');
|
||||
expect([undefined, 'gpt-4']).toContain(stored.gptPlugins);
|
||||
expect([undefined, 'plugin-model']).toContain(stored.secondaryModel);
|
||||
});
|
||||
|
||||
it('does nothing if conversation is null', () => {
|
||||
storeEndpointSettings(null);
|
||||
expect(localStorage.getItem('lastModel')).toBeNull();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { QueryClient } from '@tanstack/react-query';
|
||||
import { LocalStorageKeys, QueryKeys } from 'librechat-data-provider';
|
||||
import {
|
||||
format,
|
||||
isToday,
|
||||
|
|
@ -8,8 +10,6 @@ import {
|
|||
startOfYear,
|
||||
isWithinInterval,
|
||||
} from 'date-fns';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
import { EModelEndpoint, LocalStorageKeys, QueryKeys } from 'librechat-data-provider';
|
||||
import type { TConversation, GroupedConversations } from 'librechat-data-provider';
|
||||
import type { InfiniteData } from '@tanstack/react-query';
|
||||
|
||||
|
|
@ -306,15 +306,12 @@ export function storeEndpointSettings(conversation: TConversation | null) {
|
|||
if (!conversation) {
|
||||
return;
|
||||
}
|
||||
const { endpoint, model, agentOptions } = conversation;
|
||||
const { endpoint, model } = conversation;
|
||||
if (!endpoint) {
|
||||
return;
|
||||
}
|
||||
const lastModel = JSON.parse(localStorage.getItem(LocalStorageKeys.LAST_MODEL) ?? '{}');
|
||||
lastModel[endpoint] = model;
|
||||
if (endpoint === EModelEndpoint.gptPlugins) {
|
||||
lastModel.secondaryModel = agentOptions?.model ?? model ?? '';
|
||||
}
|
||||
localStorage.setItem(LocalStorageKeys.LAST_MODEL, JSON.stringify(lastModel));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue