mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-16 15:35:31 +01:00
🤖 Assistants V2 Support: Part 1
- Separated Azure Assistants to its own endpoint - File Search / Vector Store integration is incomplete, but can toggle and use storage from playground - Code Interpreter resource files can be added but not deleted - GPT-4o is supported - Many improvements to the Assistants Endpoint overall data-provider v2 changes copy existing route as v1 chore: rename new endpoint to reduce comparison operations and add new azure filesource api: add azureAssistants part 1 force use of version for assistants/assistantsAzure chore: switch name back to azureAssistants refactor type version: string | number Ensure assistants endpoints have version set fix: isArchived type issue in ConversationListParams refactor: update assistants mutations/queries with endpoint/version definitions, update Assistants Map structure chore: FilePreview component ExtendedFile type assertion feat: isAssistantsEndpoint helper chore: remove unused useGenerations chore(buildTree): type issue chore(Advanced): type issue (unused component, maybe in future) first pass for multi-assistant endpoint rewrite fix(listAssistants): pass params correctly feat: list separate assistants by endpoint fix(useTextarea): access assistantMap correctly fix: assistant endpoint switching, resetting ID fix: broken during rewrite, selecting assistant mention fix: set/invalidate assistants endpoint query data correctly feat: Fix issue with assistant ID not being reset correctly getOpenAIClient helper function feat: add toast for assistant deletion fix: assistants delete right after create issue for azure fix: assistant patching refactor: actions to use getOpenAIClient refactor: consolidate logic into helpers file fix: issue where conversation data was not initially available v1 chat support refactor(spendTokens): only early return if completionTokens isNaN fix(OpenAIClient): ensure spendTokens has all necessary params refactor: route/controller logic fix(assistants/initializeClient): use defaultHeaders field fix: sanitize default operation id chore: bump openai package first pass v2 action service feat: retroactive domain parsing for actions added via v1 feat: delete db records of actions/assistants on openai assistant deletion chore: remove vision tools from v2 assistants feat: v2 upload and delete assistant vision images WIP first pass, thread attachments fix: show assistant vision files (save local/firebase copy) v2 image continue fix: annotations fix: refine annotations show analyze as error if is no longer submitting before progress reaches 1 and show file_search as retrieval tool fix: abort run, undefined endpoint issue refactor: consolidate capabilities logic and anticipate versioning frontend version 2 changes fix: query selection and filter add endpoint to unknown filepath add file ids to resource, deleting in progress enable/disable file search remove version log
This commit is contained in:
parent
f0e8cca5df
commit
2bdbff5141
118 changed files with 3358 additions and 1039 deletions
|
|
@ -4,7 +4,11 @@ import type { Option, ExtendedFile } from './types';
|
|||
|
||||
export type TAssistantOption =
|
||||
| string
|
||||
| (Option & Assistant & { files?: Array<[string, ExtendedFile]> });
|
||||
| (Option &
|
||||
Assistant & {
|
||||
files?: Array<[string, ExtendedFile]>;
|
||||
code_files?: Array<[string, ExtendedFile]>;
|
||||
});
|
||||
|
||||
export type Actions = {
|
||||
[Capabilities.code_interpreter]: boolean;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ import type {
|
|||
TPreset,
|
||||
TPlugin,
|
||||
TMessage,
|
||||
Assistant,
|
||||
TLoginUser,
|
||||
AuthTypeEnum,
|
||||
TConversation,
|
||||
EModelEndpoint,
|
||||
AssistantsEndpoint,
|
||||
AuthorizationTypeEnum,
|
||||
TSetOption as SetOption,
|
||||
TokenExchangeMethodEnum,
|
||||
|
|
@ -19,6 +21,13 @@ import type {
|
|||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import type { LucideIcon } from 'lucide-react';
|
||||
|
||||
export type AssistantListItem = {
|
||||
id: string;
|
||||
name: string;
|
||||
metadata: Assistant['metadata'];
|
||||
model: string;
|
||||
};
|
||||
|
||||
export type TPluginMap = Record<string, TPlugin>;
|
||||
|
||||
export type GenericSetter<T> = (value: T | ((currentValue: T) => T)) => void;
|
||||
|
|
@ -101,6 +110,8 @@ export type AssistantPanelProps = {
|
|||
actions?: Action[];
|
||||
assistant_id?: string;
|
||||
activePanel?: string;
|
||||
endpoint: AssistantsEndpoint;
|
||||
version: number | string;
|
||||
setAction: React.Dispatch<React.SetStateAction<Action | undefined>>;
|
||||
setCurrentAssistantId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
setActivePanel: React.Dispatch<React.SetStateAction<Panel>>;
|
||||
|
|
@ -327,7 +338,11 @@ export type Option = Record<string, unknown> & {
|
|||
};
|
||||
|
||||
export type OptionWithIcon = Option & { icon?: React.ReactNode };
|
||||
export type MentionOption = OptionWithIcon & { type: string; value: string; description?: string };
|
||||
export type MentionOption = OptionWithIcon & {
|
||||
type: string;
|
||||
value: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export type TOptionSettings = {
|
||||
showExamples?: boolean;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ import { useForm } from 'react-hook-form';
|
|||
import { memo, useCallback, useRef, useMemo } from 'react';
|
||||
import {
|
||||
supportsFiles,
|
||||
EModelEndpoint,
|
||||
mergeFileConfig,
|
||||
isAssistantsEndpoint,
|
||||
fileConfig as defaultFileConfig,
|
||||
} from 'librechat-data-provider';
|
||||
import { useChatContext, useAssistantsMapContext } from '~/Providers';
|
||||
|
|
@ -74,8 +74,9 @@ const ChatForm = ({ index = 0 }) => {
|
|||
const endpointFileConfig = fileConfig.endpoints[endpoint ?? ''];
|
||||
const invalidAssistant = useMemo(
|
||||
() =>
|
||||
conversation?.endpoint === EModelEndpoint.assistants &&
|
||||
(!conversation?.assistant_id || !assistantMap?.[conversation?.assistant_id ?? '']),
|
||||
isAssistantsEndpoint(conversation?.endpoint) &&
|
||||
(!conversation?.assistant_id ||
|
||||
!assistantMap?.[conversation?.endpoint ?? '']?.[conversation?.assistant_id ?? '']),
|
||||
[conversation?.assistant_id, conversation?.endpoint, assistantMap],
|
||||
);
|
||||
const disableInputs = useMemo(
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const FilePreview = ({
|
|||
}) => {
|
||||
const radius = 55; // Radius of the SVG circle
|
||||
const circumference = 2 * Math.PI * radius;
|
||||
const progress = useProgress(file?.['progress'] ?? 1, 0.001, file?.size ?? 1);
|
||||
const progress = useProgress(file?.['progress'] ?? 1, 0.001, (file as ExtendedFile)?.size ?? 1);
|
||||
console.log(progress);
|
||||
|
||||
// Calculate the offset based on the loading progress
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ export default function Mention({
|
|||
}) {
|
||||
const localize = useLocalize();
|
||||
const assistantMap = useAssistantsMapContext();
|
||||
const { options, modelsConfig, assistants, onSelectMention } = useMentions({ assistantMap });
|
||||
const { options, modelsConfig, assistantListMap, onSelectMention } = useMentions({
|
||||
assistantMap,
|
||||
});
|
||||
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
|
@ -47,7 +49,12 @@ export default function Mention({
|
|||
|
||||
if (mention.type === 'endpoint' && mention.value === EModelEndpoint.assistants) {
|
||||
setSearchValue('');
|
||||
setInputOptions(assistants);
|
||||
setInputOptions(assistantListMap[EModelEndpoint.assistants]);
|
||||
setActiveIndex(0);
|
||||
inputRef.current?.focus();
|
||||
} else if (mention.type === 'endpoint' && mention.value === EModelEndpoint.azureAssistants) {
|
||||
setSearchValue('');
|
||||
setInputOptions(assistantListMap[EModelEndpoint.azureAssistants]);
|
||||
setActiveIndex(0);
|
||||
inputRef.current?.focus();
|
||||
} else if (mention.type === 'endpoint') {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { EModelEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import { useGetEndpointsQuery, useGetStartupConfig } from 'librechat-data-provider/react-query';
|
||||
import type { ReactNode } from 'react';
|
||||
import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '~/components/ui';
|
||||
|
|
@ -30,7 +30,8 @@ export default function Landing({ Header }: { Header?: ReactNode }) {
|
|||
const iconURL = conversation?.iconURL;
|
||||
endpoint = getIconEndpoint({ endpointsConfig, iconURL, endpoint });
|
||||
|
||||
const assistant = endpoint === EModelEndpoint.assistants && assistantMap?.[assistant_id ?? ''];
|
||||
const isAssistant = isAssistantsEndpoint(endpoint);
|
||||
const assistant = isAssistant && assistantMap?.[endpoint]?.[assistant_id ?? ''];
|
||||
const assistantName = (assistant && assistant?.name) || '';
|
||||
const assistantDesc = (assistant && assistant?.description) || '';
|
||||
const avatar = (assistant && (assistant?.metadata?.avatar as string)) || '';
|
||||
|
|
@ -77,7 +78,7 @@ export default function Landing({ Header }: { Header?: ReactNode }) {
|
|||
</div>
|
||||
) : (
|
||||
<div className="mb-5 max-w-[75vh] px-12 text-center text-lg font-medium dark:text-white md:px-0 md:text-2xl">
|
||||
{endpoint === EModelEndpoint.assistants
|
||||
{isAssistant
|
||||
? conversation?.greeting ?? localize('com_nav_welcome_assistant')
|
||||
: conversation?.greeting ?? localize('com_nav_welcome_message')}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,24 @@ import {
|
|||
import UnknownIcon from './UnknownIcon';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
const AssistantAvatar = ({ className = '', assistantName, avatar, size }: IconMapProps) => {
|
||||
if (assistantName && avatar) {
|
||||
return (
|
||||
<img
|
||||
src={avatar}
|
||||
className="bg-token-surface-secondary dark:bg-token-surface-tertiary h-full w-full rounded-full object-cover"
|
||||
alt={assistantName}
|
||||
width="80"
|
||||
height="80"
|
||||
/>
|
||||
);
|
||||
} else if (assistantName) {
|
||||
return <AssistantIcon className={cn('text-token-secondary', className)} size={size} />;
|
||||
}
|
||||
|
||||
return <Sparkles className={cn(assistantName === '' ? 'icon-2xl' : '', className)} />;
|
||||
};
|
||||
|
||||
export const icons = {
|
||||
[EModelEndpoint.azureOpenAI]: AzureMinimalIcon,
|
||||
[EModelEndpoint.openAI]: GPTIcon,
|
||||
|
|
@ -24,22 +42,7 @@ export const icons = {
|
|||
[EModelEndpoint.google]: GoogleMinimalIcon,
|
||||
[EModelEndpoint.bingAI]: BingAIMinimalIcon,
|
||||
[EModelEndpoint.custom]: CustomMinimalIcon,
|
||||
[EModelEndpoint.assistants]: ({ className = '', assistantName, avatar, size }: IconMapProps) => {
|
||||
if (assistantName && avatar) {
|
||||
return (
|
||||
<img
|
||||
src={avatar}
|
||||
className="bg-token-surface-secondary dark:bg-token-surface-tertiary h-full w-full rounded-full object-cover"
|
||||
alt={assistantName}
|
||||
width="80"
|
||||
height="80"
|
||||
/>
|
||||
);
|
||||
} else if (assistantName) {
|
||||
return <AssistantIcon className={cn('text-token-secondary', className)} size={size} />;
|
||||
}
|
||||
|
||||
return <Sparkles className={cn(assistantName === '' ? 'icon-2xl' : '', className)} />;
|
||||
},
|
||||
[EModelEndpoint.assistants]: AssistantAvatar,
|
||||
[EModelEndpoint.azureAssistants]: AssistantAvatar,
|
||||
unknown: UnknownIcon,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Content, Portal, Root } from '@radix-ui/react-popover';
|
||||
import { alternateName, EModelEndpoint } from 'librechat-data-provider';
|
||||
import { alternateName, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { FC } from 'react';
|
||||
import { useChatContext, useAssistantsMapContext } from '~/Providers';
|
||||
|
|
@ -16,7 +16,8 @@ const EndpointsMenu: FC = () => {
|
|||
const { endpoint = '', assistant_id = null } = conversation ?? {};
|
||||
const assistantMap = useAssistantsMapContext();
|
||||
|
||||
const assistant = endpoint === EModelEndpoint.assistants && assistantMap?.[assistant_id ?? ''];
|
||||
const assistant =
|
||||
isAssistantsEndpoint(endpoint) && assistantMap?.[endpoint ?? '']?.[assistant_id ?? ''];
|
||||
const assistantName = (assistant && assistant?.name) || 'Assistant';
|
||||
|
||||
if (!endpoint) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import ProgressCircle from './ProgressCircle';
|
||||
import CancelledIcon from './CancelledIcon';
|
||||
import ProgressText from './ProgressText';
|
||||
import FinishedIcon from './FinishedIcon';
|
||||
import MarkdownLite from './MarkdownLite';
|
||||
|
|
@ -11,10 +12,12 @@ export default function CodeAnalyze({
|
|||
initialProgress = 0.1,
|
||||
code,
|
||||
outputs = [],
|
||||
isSubmitting,
|
||||
}: {
|
||||
initialProgress: number;
|
||||
code: string;
|
||||
outputs: Record<string, unknown>[];
|
||||
isSubmitting: boolean;
|
||||
}) {
|
||||
const showCodeDefault = useRecoilValue(store.showCode);
|
||||
const [showCode, setShowCode] = useState(showCodeDefault);
|
||||
|
|
@ -35,7 +38,13 @@ export default function CodeAnalyze({
|
|||
<div className="my-2.5 flex items-center gap-2.5">
|
||||
<div className="relative h-5 w-5 shrink-0">
|
||||
{progress < 1 ? (
|
||||
<CodeInProgress offset={offset} circumference={circumference} radius={radius} />
|
||||
<CodeInProgress
|
||||
offset={offset}
|
||||
radius={radius}
|
||||
progress={progress}
|
||||
isSubmitting={isSubmitting}
|
||||
circumference={circumference}
|
||||
/>
|
||||
) : (
|
||||
<FinishedIcon />
|
||||
)}
|
||||
|
|
@ -74,18 +83,25 @@ const CodeInProgress = ({
|
|||
offset,
|
||||
circumference,
|
||||
radius,
|
||||
isSubmitting,
|
||||
progress,
|
||||
}: {
|
||||
progress: number;
|
||||
offset: number;
|
||||
circumference: number;
|
||||
radius: number;
|
||||
isSubmitting: boolean;
|
||||
}) => {
|
||||
if (progress < 1 && !isSubmitting) {
|
||||
return <CancelledIcon />;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className="absolute left-0 top-0 flex h-full w-full items-center justify-center rounded-full bg-transparent text-white"
|
||||
style={{ opacity: 1, transform: 'none' }}
|
||||
data-projection-id="77"
|
||||
>
|
||||
<div className='absolute right-[1.5px] bottom-[1.5px]'>
|
||||
<div className="absolute bottom-[1.5px] right-[1.5px]">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
|
|
|
|||
|
|
@ -79,11 +79,13 @@ export default function Part({
|
|||
initialProgress={toolCall.progress ?? 0.1}
|
||||
code={code_interpreter.input}
|
||||
outputs={code_interpreter.outputs ?? []}
|
||||
isSubmitting={isSubmitting}
|
||||
/>
|
||||
);
|
||||
} else if (
|
||||
part.type === ContentTypes.TOOL_CALL &&
|
||||
part[ContentTypes.TOOL_CALL].type === ToolCallTypes.RETRIEVAL
|
||||
(part[ContentTypes.TOOL_CALL].type === ToolCallTypes.RETRIEVAL ||
|
||||
part[ContentTypes.TOOL_CALL].type === ToolCallTypes.FILE_SEARCH)
|
||||
) {
|
||||
const toolCall = part[ContentTypes.TOOL_CALL];
|
||||
return <RetrievalCall initialProgress={toolCall.progress ?? 0.1} isSubmitting={isSubmitting} />;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { useState } from 'react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { TConversation, TMessage } from 'librechat-data-provider';
|
||||
import { Clipboard, CheckMark, EditIcon, RegenerateIcon, ContinueIcon } from '~/components/svg';
|
||||
import { useGenerationsByLatest, useLocalize } from '~/hooks';
|
||||
|
|
@ -35,14 +34,19 @@ export default function HoverButtons({
|
|||
const { endpoint: _endpoint, endpointType } = conversation ?? {};
|
||||
const endpoint = endpointType ?? _endpoint;
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
const { hideEditButton, regenerateEnabled, continueSupported, forkingSupported } =
|
||||
useGenerationsByLatest({
|
||||
isEditing,
|
||||
isSubmitting,
|
||||
message,
|
||||
endpoint: endpoint ?? '',
|
||||
latestMessage,
|
||||
});
|
||||
const {
|
||||
hideEditButton,
|
||||
regenerateEnabled,
|
||||
continueSupported,
|
||||
forkingSupported,
|
||||
isEditableEndpoint,
|
||||
} = useGenerationsByLatest({
|
||||
isEditing,
|
||||
isSubmitting,
|
||||
message,
|
||||
endpoint: endpoint ?? '',
|
||||
latestMessage,
|
||||
});
|
||||
if (!conversation) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -58,7 +62,7 @@ export default function HoverButtons({
|
|||
|
||||
return (
|
||||
<div className="visible mt-0 flex justify-center gap-1 self-end text-gray-400 lg:justify-start">
|
||||
{endpoint !== EModelEndpoint.assistants && (
|
||||
{isEditableEndpoint && (
|
||||
<button
|
||||
className={cn(
|
||||
'hover-button rounded-md p-1 text-gray-400 hover:text-gray-900 dark:text-gray-400/70 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400 md:group-hover:visible md:group-[.final-completion]:visible',
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { Assistant, TConversation, TEndpointsConfig, TPreset } from 'librechat-data-provider';
|
||||
import { isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type {
|
||||
TAssistantsMap,
|
||||
TConversation,
|
||||
TEndpointsConfig,
|
||||
TPreset,
|
||||
} from 'librechat-data-provider';
|
||||
import { getEndpointField, getIconKey, getIconEndpoint } from '~/utils';
|
||||
import { icons } from '~/components/Chat/Menus/Endpoints/Icons';
|
||||
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
|
||||
import { getEndpointField, getIconKey, getIconEndpoint } from '~/utils';
|
||||
|
||||
export default function ConvoIcon({
|
||||
conversation,
|
||||
|
|
@ -15,7 +20,7 @@ export default function ConvoIcon({
|
|||
}: {
|
||||
conversation: TConversation | TPreset | null;
|
||||
endpointsConfig: TEndpointsConfig;
|
||||
assistantMap: Record<string, Assistant>;
|
||||
assistantMap: TAssistantsMap;
|
||||
containerClassName?: string;
|
||||
context?: 'message' | 'nav' | 'landing' | 'menu-item';
|
||||
className?: string;
|
||||
|
|
@ -25,7 +30,7 @@ export default function ConvoIcon({
|
|||
let endpoint = conversation?.endpoint;
|
||||
endpoint = getIconEndpoint({ endpointsConfig, iconURL, endpoint });
|
||||
const assistant =
|
||||
endpoint === EModelEndpoint.assistants && assistantMap?.[conversation?.assistant_id ?? ''];
|
||||
isAssistantsEndpoint(endpoint) && assistantMap?.[endpoint]?.[conversation?.assistant_id ?? ''];
|
||||
const assistantName = (assistant && assistant?.name) || '';
|
||||
|
||||
const avatar = (assistant && (assistant?.metadata?.avatar as string)) || '';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { Assistant, TConversation, TEndpointsConfig, TPreset } from 'librechat-data-provider';
|
||||
import { isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type {
|
||||
TConversation,
|
||||
TEndpointsConfig,
|
||||
TPreset,
|
||||
TAssistantsMap,
|
||||
} from 'librechat-data-provider';
|
||||
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
|
||||
import MinimalIcon from '~/components/Endpoints/MinimalIcon';
|
||||
import { getEndpointField, getIconEndpoint } from '~/utils';
|
||||
|
|
@ -15,7 +20,7 @@ export default function EndpointIcon({
|
|||
endpointsConfig: TEndpointsConfig;
|
||||
containerClassName?: string;
|
||||
context?: 'message' | 'nav' | 'landing' | 'menu-item';
|
||||
assistantMap?: Record<string, Assistant>;
|
||||
assistantMap?: TAssistantsMap;
|
||||
className?: string;
|
||||
size?: number;
|
||||
}) {
|
||||
|
|
@ -27,7 +32,7 @@ export default function EndpointIcon({
|
|||
const endpointIconURL = getEndpointField(endpointsConfig, endpoint, 'iconURL');
|
||||
|
||||
const assistant =
|
||||
endpoint === EModelEndpoint.assistants && assistantMap?.[conversation?.assistant_id ?? ''];
|
||||
isAssistantsEndpoint(endpoint) && assistantMap?.[endpoint]?.[conversation?.assistant_id ?? ''];
|
||||
const assistantAvatar = (assistant && (assistant?.metadata?.avatar as string)) || '';
|
||||
const assistantName = (assistant && assistant?.name) || '';
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ const MinimalIcon: React.FC<IconProps> = (props) => {
|
|||
[EModelEndpoint.bingAI]: { icon: <BingAIMinimalIcon />, name: 'BingAI' },
|
||||
[EModelEndpoint.chatGPTBrowser]: { icon: <LightningIcon />, name: 'ChatGPT' },
|
||||
[EModelEndpoint.assistants]: { icon: <Sparkles className="icon-sm" />, name: 'Assistant' },
|
||||
[EModelEndpoint.azureAssistants]: { icon: <Sparkles className="icon-sm" />, name: 'Assistant' },
|
||||
default: {
|
||||
icon: (
|
||||
<UnknownIcon
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { ImageDetail, imageDetailNumeric, imageDetailValue } from 'librechat-data-provider';
|
||||
import type { ValueType } from '@rc-component/mini-decimal';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import {
|
||||
Input,
|
||||
Label,
|
||||
|
|
@ -11,7 +13,6 @@ import {
|
|||
} from '~/components/ui';
|
||||
import { cn, defaultTextProps, optionText, removeFocusOutlines } from '~/utils/';
|
||||
import { useLocalize, useDebouncedInput } from '~/hooks';
|
||||
import type { TModelSelectProps } from '~/common';
|
||||
import OptionHover from './OptionHover';
|
||||
import { ESide } from '~/common';
|
||||
|
||||
|
|
@ -127,7 +128,7 @@ export default function Settings({
|
|||
id="temp-int"
|
||||
disabled={readonly}
|
||||
value={temperatureValue as number}
|
||||
onChange={setTemperature}
|
||||
onChange={setTemperature as (value: ValueType | null) => void}
|
||||
max={2}
|
||||
min={0}
|
||||
step={0.01}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import { useState, useMemo, useEffect } from 'react';
|
||||
import TextareaAutosize from 'react-textarea-autosize';
|
||||
import { defaultOrderQuery } from 'librechat-data-provider';
|
||||
import type { TPreset } from 'librechat-data-provider';
|
||||
import type { TModelSelectProps, Option } from '~/common';
|
||||
import { Label, HoverCard, SelectDropDown, HoverCardTrigger } from '~/components/ui';
|
||||
import { cn, defaultTextProps, removeFocusOutlines, mapAssistants } from '~/utils';
|
||||
import { useLocalize, useDebouncedInput } from '~/hooks';
|
||||
import { useListAssistantsQuery } from '~/data-provider';
|
||||
import { useLocalize, useDebouncedInput, useAssistantListMap } from '~/hooks';
|
||||
import OptionHover from './OptionHover';
|
||||
import { ESide } from '~/common';
|
||||
|
||||
|
|
@ -17,23 +15,21 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
[localize],
|
||||
);
|
||||
|
||||
const { data: assistants = [] } = useListAssistantsQuery(defaultOrderQuery, {
|
||||
select: (res) =>
|
||||
[
|
||||
defaultOption,
|
||||
...res.data.map(({ id, name }) => ({
|
||||
label: name,
|
||||
value: id,
|
||||
})),
|
||||
].filter(Boolean),
|
||||
});
|
||||
|
||||
const { data: assistantMap = {} } = useListAssistantsQuery(defaultOrderQuery, {
|
||||
select: (res) => mapAssistants(res.data),
|
||||
});
|
||||
const assistantListMap = useAssistantListMap((res) => mapAssistants(res.data));
|
||||
|
||||
const { model, endpoint, assistant_id, endpointType, promptPrefix, instructions } =
|
||||
conversation ?? {};
|
||||
|
||||
const assistants = useMemo(() => {
|
||||
return [
|
||||
defaultOption,
|
||||
...(assistantListMap[endpoint ?? ''] ?? []).map(({ id, name }) => ({
|
||||
label: name,
|
||||
value: id,
|
||||
})),
|
||||
].filter(Boolean);
|
||||
}, [assistantListMap, endpoint, defaultOption]);
|
||||
|
||||
const [onPromptPrefixChange, promptPrefixValue] = useDebouncedInput({
|
||||
setOption,
|
||||
optionKey: 'promptPrefix',
|
||||
|
|
@ -47,11 +43,11 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
|
||||
const activeAssistant = useMemo(() => {
|
||||
if (assistant_id) {
|
||||
return assistantMap[assistant_id];
|
||||
return assistantListMap[endpoint ?? '']?.[assistant_id];
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [assistant_id, assistantMap]);
|
||||
}, [assistant_id, assistantListMap, endpoint]);
|
||||
|
||||
const modelOptions = useMemo(() => {
|
||||
return models.map((model) => ({
|
||||
|
|
@ -89,7 +85,7 @@ export default function Settings({ conversation, setOption, models, readonly }:
|
|||
return;
|
||||
}
|
||||
|
||||
const assistant = assistantMap[value];
|
||||
const assistant = assistantListMap[endpoint ?? '']?.[value];
|
||||
if (!assistant) {
|
||||
setAssistantValue(defaultOption);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import OpenAISettings from './OpenAI';
|
|||
|
||||
const settings: { [key: string]: FC<TModelSelectProps> } = {
|
||||
[EModelEndpoint.assistants]: AssistantsSettings,
|
||||
[EModelEndpoint.azureAssistants]: AssistantsSettings,
|
||||
[EModelEndpoint.openAI]: OpenAISettings,
|
||||
[EModelEndpoint.custom]: OpenAISettings,
|
||||
[EModelEndpoint.azureOpenAI]: OpenAISettings,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useForm, FormProvider } from 'react-hook-form';
|
||||
import { EModelEndpoint, alternateName } from 'librechat-data-provider';
|
||||
import { EModelEndpoint, alternateName, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { TDialogProps } from '~/common';
|
||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
|
|
@ -21,6 +21,7 @@ const endpointComponents = {
|
|||
[EModelEndpoint.azureOpenAI]: OpenAIConfig,
|
||||
[EModelEndpoint.gptPlugins]: OpenAIConfig,
|
||||
[EModelEndpoint.assistants]: OpenAIConfig,
|
||||
[EModelEndpoint.azureAssistants]: OpenAIConfig,
|
||||
default: OtherConfig,
|
||||
};
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ const formSet: Set<string> = new Set([
|
|||
EModelEndpoint.azureOpenAI,
|
||||
EModelEndpoint.gptPlugins,
|
||||
EModelEndpoint.assistants,
|
||||
EModelEndpoint.azureAssistants,
|
||||
]);
|
||||
|
||||
const EXPIRY = {
|
||||
|
|
@ -97,7 +99,7 @@ const SetKeyDialog = ({
|
|||
isAzure ||
|
||||
endpoint === EModelEndpoint.openAI ||
|
||||
endpoint === EModelEndpoint.gptPlugins ||
|
||||
endpoint === EModelEndpoint.assistants;
|
||||
isAssistantsEndpoint(endpoint);
|
||||
if (isAzure) {
|
||||
data.apiKey = 'n/a';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useEffect, useMemo } from 'react';
|
||||
import { Combobox } from '~/components/ui';
|
||||
import { EModelEndpoint, defaultOrderQuery, LocalStorageKeys } from 'librechat-data-provider';
|
||||
import type { SwitcherProps } from '~/common';
|
||||
import { useSetIndexOptions, useSelectAssistant, useLocalize } from '~/hooks';
|
||||
import { isAssistantsEndpoint, LocalStorageKeys } from 'librechat-data-provider';
|
||||
import type { AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { SwitcherProps, AssistantListItem } from '~/common';
|
||||
import { useSetIndexOptions, useSelectAssistant, useLocalize, useAssistantListMap } from '~/hooks';
|
||||
import { useChatContext, useAssistantsMapContext } from '~/Providers';
|
||||
import { useListAssistantsQuery } from '~/data-provider';
|
||||
import Icon from '~/components/Endpoints/Icon';
|
||||
|
||||
export default function AssistantSwitcher({ isCollapsed }: SwitcherProps) {
|
||||
|
|
@ -15,26 +15,29 @@ export default function AssistantSwitcher({ isCollapsed }: SwitcherProps) {
|
|||
/* `selectedAssistant` must be defined with `null` to cause re-render on update */
|
||||
const { assistant_id: selectedAssistant = null, endpoint } = conversation ?? {};
|
||||
|
||||
const { data: assistants = [] } = useListAssistantsQuery(defaultOrderQuery, {
|
||||
select: (res) => res.data.map(({ id, name, metadata }) => ({ id, name, metadata })),
|
||||
});
|
||||
|
||||
const assistantListMap = useAssistantListMap((res) =>
|
||||
res.data.map(({ id, name, metadata }) => ({ id, name, metadata })),
|
||||
);
|
||||
const assistants: Omit<AssistantListItem, 'model'>[] = useMemo(
|
||||
() => assistantListMap[endpoint ?? ''] ?? [],
|
||||
[endpoint, assistantListMap],
|
||||
);
|
||||
const assistantMap = useAssistantsMapContext();
|
||||
const { onSelect } = useSelectAssistant();
|
||||
const { onSelect } = useSelectAssistant(endpoint as AssistantsEndpoint);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedAssistant && assistants && assistants.length && assistantMap) {
|
||||
const assistant_id =
|
||||
localStorage.getItem(`${LocalStorageKeys.ASST_ID_PREFIX}${index}`) ??
|
||||
localStorage.getItem(`${LocalStorageKeys.ASST_ID_PREFIX}${index}${endpoint}`) ??
|
||||
assistants[0]?.id ??
|
||||
'';
|
||||
const assistant = assistantMap?.[assistant_id];
|
||||
const assistant = assistantMap?.[endpoint ?? '']?.[assistant_id];
|
||||
|
||||
if (!assistant) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (endpoint !== EModelEndpoint.assistants) {
|
||||
if (!isAssistantsEndpoint(endpoint)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +46,7 @@ export default function AssistantSwitcher({ isCollapsed }: SwitcherProps) {
|
|||
}
|
||||
}, [index, assistants, selectedAssistant, assistantMap, endpoint, setOption]);
|
||||
|
||||
const currentAssistant = assistantMap?.[selectedAssistant ?? ''];
|
||||
const currentAssistant = assistantMap?.[endpoint ?? '']?.[selectedAssistant ?? ''];
|
||||
|
||||
const assistantOptions = useMemo(() => {
|
||||
return assistants.map((assistant) => {
|
||||
|
|
@ -53,14 +56,14 @@ export default function AssistantSwitcher({ isCollapsed }: SwitcherProps) {
|
|||
icon: (
|
||||
<Icon
|
||||
isCreatedByUser={false}
|
||||
endpoint={EModelEndpoint.assistants}
|
||||
endpoint={endpoint}
|
||||
assistantName={assistant.name ?? ''}
|
||||
iconURL={(assistant.metadata?.avatar as string) ?? ''}
|
||||
/>
|
||||
),
|
||||
};
|
||||
});
|
||||
}, [assistants]);
|
||||
}, [assistants, endpoint]);
|
||||
|
||||
return (
|
||||
<Combobox
|
||||
|
|
@ -78,7 +81,7 @@ export default function AssistantSwitcher({ isCollapsed }: SwitcherProps) {
|
|||
SelectIcon={
|
||||
<Icon
|
||||
isCreatedByUser={false}
|
||||
endpoint={EModelEndpoint.assistants}
|
||||
endpoint={endpoint}
|
||||
assistantName={currentAssistant?.name ?? ''}
|
||||
iconURL={(currentAssistant?.metadata?.avatar as string) ?? ''}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ import {
|
|||
AuthTypeEnum,
|
||||
} from 'librechat-data-provider';
|
||||
import type {
|
||||
ValidationResult,
|
||||
Action,
|
||||
FunctionTool,
|
||||
ActionMetadata,
|
||||
ValidationResult,
|
||||
AssistantsEndpoint,
|
||||
} from 'librechat-data-provider';
|
||||
import type { ActionAuthForm } from '~/common';
|
||||
import type { Spec } from './ActionsTable';
|
||||
|
|
@ -32,10 +33,14 @@ const debouncedValidation = debounce(
|
|||
export default function ActionsInput({
|
||||
action,
|
||||
assistant_id,
|
||||
endpoint,
|
||||
version,
|
||||
setAction,
|
||||
}: {
|
||||
action?: Action;
|
||||
assistant_id?: string;
|
||||
endpoint: AssistantsEndpoint;
|
||||
version: number | string;
|
||||
setAction: React.Dispatch<React.SetStateAction<Action | undefined>>;
|
||||
}) {
|
||||
const handleResult = (result: ValidationResult) => {
|
||||
|
|
@ -173,7 +178,9 @@ export default function ActionsInput({
|
|||
metadata,
|
||||
functions,
|
||||
assistant_id,
|
||||
model: assistantMap[assistant_id].model,
|
||||
endpoint,
|
||||
version,
|
||||
model: assistantMap[endpoint][assistant_id].model,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -18,9 +18,11 @@ import { Panel } from '~/common';
|
|||
export default function ActionsPanel({
|
||||
// activePanel,
|
||||
action,
|
||||
endpoint,
|
||||
version,
|
||||
setAction,
|
||||
setActivePanel,
|
||||
assistant_id,
|
||||
setActivePanel,
|
||||
}: AssistantPanelProps) {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
|
|
@ -130,9 +132,10 @@ export default function ActionsPanel({
|
|||
const confirmed = confirm('Are you sure you want to delete this action?');
|
||||
if (confirmed) {
|
||||
deleteAction.mutate({
|
||||
model: assistantMap[assistant_id].model,
|
||||
model: assistantMap[endpoint][assistant_id].model,
|
||||
action_id: action.action_id,
|
||||
assistant_id,
|
||||
endpoint,
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
|
@ -185,7 +188,13 @@ export default function ActionsPanel({
|
|||
</DialogTrigger>
|
||||
<ActionsAuth setOpenAuthDialog={setOpenAuthDialog} />
|
||||
</Dialog>
|
||||
<ActionsInput action={action} assistant_id={assistant_id} setAction={setAction} />
|
||||
<ActionsInput
|
||||
action={action}
|
||||
assistant_id={assistant_id}
|
||||
setAction={setAction}
|
||||
endpoint={endpoint}
|
||||
version={version}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import {
|
|||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import type {
|
||||
Metadata,
|
||||
AssistantListResponse,
|
||||
Assistant,
|
||||
AssistantsEndpoint,
|
||||
AssistantCreateParams,
|
||||
AssistantListResponse,
|
||||
} from 'librechat-data-provider';
|
||||
import { useUploadAssistantAvatarMutation, useGetFileConfig } from '~/data-provider';
|
||||
import { AssistantAvatar, NoImage, AvatarMenu } from './Images';
|
||||
|
|
@ -22,10 +23,14 @@ import { useLocalize } from '~/hooks';
|
|||
// import { cn } from '~/utils/';
|
||||
|
||||
function Avatar({
|
||||
endpoint,
|
||||
version,
|
||||
assistant_id,
|
||||
metadata,
|
||||
createMutation,
|
||||
}: {
|
||||
endpoint: AssistantsEndpoint;
|
||||
version: number | string;
|
||||
assistant_id: string | null;
|
||||
metadata: null | Metadata;
|
||||
createMutation: UseMutationResult<Assistant, Error, AssistantCreateParams>;
|
||||
|
|
@ -46,8 +51,8 @@ function Avatar({
|
|||
const { showToast } = useToastContext();
|
||||
|
||||
const activeModel = useMemo(() => {
|
||||
return assistantsMap[assistant_id ?? '']?.model ?? '';
|
||||
}, [assistant_id, assistantsMap]);
|
||||
return assistantsMap[endpoint][assistant_id ?? '']?.model ?? '';
|
||||
}, [assistantsMap, endpoint, assistant_id]);
|
||||
|
||||
const { mutate: uploadAvatar } = useUploadAssistantAvatarMutation({
|
||||
onMutate: () => {
|
||||
|
|
@ -65,6 +70,7 @@ function Avatar({
|
|||
|
||||
const res = queryClient.getQueryData<AssistantListResponse>([
|
||||
QueryKeys.assistants,
|
||||
endpoint,
|
||||
defaultOrderQuery,
|
||||
]);
|
||||
|
||||
|
|
@ -83,10 +89,13 @@ function Avatar({
|
|||
return assistant;
|
||||
}) ?? [];
|
||||
|
||||
queryClient.setQueryData<AssistantListResponse>([QueryKeys.assistants, defaultOrderQuery], {
|
||||
...res,
|
||||
data: assistants,
|
||||
});
|
||||
queryClient.setQueryData<AssistantListResponse>(
|
||||
[QueryKeys.assistants, endpoint, defaultOrderQuery],
|
||||
{
|
||||
...res,
|
||||
data: assistants,
|
||||
},
|
||||
);
|
||||
|
||||
setProgress(1);
|
||||
},
|
||||
|
|
@ -149,9 +158,20 @@ function Avatar({
|
|||
model: activeModel,
|
||||
postCreation: true,
|
||||
formData,
|
||||
endpoint,
|
||||
version,
|
||||
});
|
||||
}
|
||||
}, [createMutation.data, createMutation.isSuccess, input, previewUrl, uploadAvatar, activeModel]);
|
||||
}, [
|
||||
createMutation.data,
|
||||
createMutation.isSuccess,
|
||||
input,
|
||||
previewUrl,
|
||||
uploadAvatar,
|
||||
activeModel,
|
||||
endpoint,
|
||||
version,
|
||||
]);
|
||||
|
||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const file = event.target.files?.[0];
|
||||
|
|
@ -183,6 +203,8 @@ function Avatar({
|
|||
assistant_id,
|
||||
model: activeModel,
|
||||
formData,
|
||||
endpoint,
|
||||
version,
|
||||
});
|
||||
} else {
|
||||
showToast({
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
import { useState, useMemo, useEffect } from 'react';
|
||||
import { useState, useMemo } from 'react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useForm, FormProvider, Controller, useWatch } from 'react-hook-form';
|
||||
import { useGetModelsQuery, useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import { useGetModelsQuery } from 'librechat-data-provider/react-query';
|
||||
import {
|
||||
Tools,
|
||||
QueryKeys,
|
||||
Capabilities,
|
||||
EModelEndpoint,
|
||||
actionDelimiter,
|
||||
ImageVisionTool,
|
||||
defaultAssistantFormValues,
|
||||
} from 'librechat-data-provider';
|
||||
import type { FunctionTool, TConfig, TPlugin } from 'librechat-data-provider';
|
||||
import type { AssistantForm, AssistantPanelProps } from '~/common';
|
||||
import type { FunctionTool, TPlugin, TEndpointsConfig } from 'librechat-data-provider';
|
||||
import { useCreateAssistantMutation, useUpdateAssistantMutation } from '~/data-provider';
|
||||
import { SelectDropDown, Checkbox, QuestionMark } from '~/components/ui';
|
||||
import { useAssistantsMapContext, useToastContext } from '~/Providers';
|
||||
import { useSelectAssistant, useLocalize } from '~/hooks';
|
||||
import { ToolSelectDialog } from '~/components/Tools';
|
||||
import CapabilitiesForm from './CapabilitiesForm';
|
||||
import { SelectDropDown } from '~/components/ui';
|
||||
import AssistantAvatar from './AssistantAvatar';
|
||||
import AssistantSelect from './AssistantSelect';
|
||||
import AssistantAction from './AssistantAction';
|
||||
|
|
@ -35,17 +35,20 @@ const inputClass =
|
|||
export default function AssistantPanel({
|
||||
// index = 0,
|
||||
setAction,
|
||||
endpoint,
|
||||
actions = [],
|
||||
setActivePanel,
|
||||
assistant_id: current_assistant_id,
|
||||
setCurrentAssistantId,
|
||||
}: AssistantPanelProps) {
|
||||
assistantsConfig,
|
||||
version,
|
||||
}: AssistantPanelProps & { assistantsConfig?: TConfig | null }) {
|
||||
const queryClient = useQueryClient();
|
||||
const modelsQuery = useGetModelsQuery();
|
||||
const assistantMap = useAssistantsMapContext();
|
||||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
||||
|
||||
const allTools = queryClient.getQueryData<TPlugin[]>([QueryKeys.tools]) ?? [];
|
||||
const { onSelect: onSelectAssistant } = useSelectAssistant();
|
||||
const { onSelect: onSelectAssistant } = useSelectAssistant(endpoint);
|
||||
const { showToast } = useToastContext();
|
||||
const localize = useLocalize();
|
||||
|
||||
|
|
@ -55,44 +58,31 @@ export default function AssistantPanel({
|
|||
|
||||
const [showToolDialog, setShowToolDialog] = useState(false);
|
||||
|
||||
const { control, handleSubmit, reset, setValue, getValues } = methods;
|
||||
const { control, handleSubmit, reset } = methods;
|
||||
const assistant = useWatch({ control, name: 'assistant' });
|
||||
const functions = useWatch({ control, name: 'functions' });
|
||||
const assistant_id = useWatch({ control, name: 'id' });
|
||||
const model = useWatch({ control, name: 'model' });
|
||||
|
||||
const activeModel = useMemo(() => {
|
||||
return assistantMap?.[assistant_id]?.model;
|
||||
}, [assistantMap, assistant_id]);
|
||||
return assistantMap?.[endpoint]?.[assistant_id]?.model;
|
||||
}, [assistantMap, endpoint, assistant_id]);
|
||||
|
||||
const assistants = useMemo(() => endpointsConfig?.[EModelEndpoint.assistants], [endpointsConfig]);
|
||||
const retrievalModels = useMemo(() => new Set(assistants?.retrievalModels ?? []), [assistants]);
|
||||
const toolsEnabled = useMemo(
|
||||
() => assistants?.capabilities?.includes(Capabilities.tools),
|
||||
[assistants],
|
||||
() => assistantsConfig?.capabilities?.includes(Capabilities.tools),
|
||||
[assistantsConfig],
|
||||
);
|
||||
const actionsEnabled = useMemo(
|
||||
() => assistants?.capabilities?.includes(Capabilities.actions),
|
||||
[assistants],
|
||||
() => assistantsConfig?.capabilities?.includes(Capabilities.actions),
|
||||
[assistantsConfig],
|
||||
);
|
||||
const retrievalEnabled = useMemo(
|
||||
() => assistants?.capabilities?.includes(Capabilities.retrieval),
|
||||
[assistants],
|
||||
() => assistantsConfig?.capabilities?.includes(Capabilities.retrieval),
|
||||
[assistantsConfig],
|
||||
);
|
||||
const codeEnabled = useMemo(
|
||||
() => assistants?.capabilities?.includes(Capabilities.code_interpreter),
|
||||
[assistants],
|
||||
() => assistantsConfig?.capabilities?.includes(Capabilities.code_interpreter),
|
||||
[assistantsConfig],
|
||||
);
|
||||
const imageVisionEnabled = useMemo(
|
||||
() => assistants?.capabilities?.includes(Capabilities.image_vision),
|
||||
[assistants],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (model && !retrievalModels.has(model)) {
|
||||
setValue(Capabilities.retrieval, false);
|
||||
}
|
||||
}, [model, setValue, retrievalModels]);
|
||||
|
||||
/* Mutations */
|
||||
const update = useUpdateAssistantMutation({
|
||||
|
|
@ -145,7 +135,7 @@ export default function AssistantPanel({
|
|||
if (!functionName.includes(actionDelimiter)) {
|
||||
return functionName;
|
||||
} else {
|
||||
const assistant = assistantMap?.[assistant_id];
|
||||
const assistant = assistantMap?.[endpoint]?.[assistant_id];
|
||||
const tool = assistant?.tools?.find((tool) => tool.function?.name === functionName);
|
||||
if (assistant && tool) {
|
||||
return tool;
|
||||
|
|
@ -160,7 +150,7 @@ export default function AssistantPanel({
|
|||
tools.push({ type: Tools.code_interpreter });
|
||||
}
|
||||
if (data.retrieval) {
|
||||
tools.push({ type: Tools.retrieval });
|
||||
tools.push({ type: version == 2 ? Tools.file_search : Tools.retrieval });
|
||||
}
|
||||
if (data.image_vision) {
|
||||
tools.push(ImageVisionTool);
|
||||
|
|
@ -183,6 +173,7 @@ export default function AssistantPanel({
|
|||
instructions,
|
||||
model,
|
||||
tools,
|
||||
endpoint,
|
||||
},
|
||||
});
|
||||
return;
|
||||
|
|
@ -194,6 +185,8 @@ export default function AssistantPanel({
|
|||
instructions,
|
||||
model,
|
||||
tools,
|
||||
endpoint,
|
||||
version,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -211,6 +204,7 @@ export default function AssistantPanel({
|
|||
<AssistantSelect
|
||||
reset={reset}
|
||||
value={field.value}
|
||||
endpoint={endpoint}
|
||||
setCurrentAssistantId={setCurrentAssistantId}
|
||||
selectedAssistant={current_assistant_id ?? null}
|
||||
createMutation={create}
|
||||
|
|
@ -239,6 +233,8 @@ export default function AssistantPanel({
|
|||
createMutation={create}
|
||||
assistant_id={assistant_id ?? null}
|
||||
metadata={assistant?.['metadata'] ?? null}
|
||||
endpoint={endpoint}
|
||||
version={version}
|
||||
/>
|
||||
<label className={labelClass} htmlFor="name">
|
||||
{localize('com_ui_name')}
|
||||
|
|
@ -324,7 +320,7 @@ export default function AssistantPanel({
|
|||
emptyTitle={true}
|
||||
value={field.value}
|
||||
setValue={field.onChange}
|
||||
availableValues={modelsQuery.data?.[EModelEndpoint.assistants] ?? []}
|
||||
availableValues={modelsQuery.data?.[endpoint] ?? []}
|
||||
showAbove={false}
|
||||
showLabel={false}
|
||||
className={cn(
|
||||
|
|
@ -343,120 +339,17 @@ export default function AssistantPanel({
|
|||
/>
|
||||
</div>
|
||||
{/* Knowledge */}
|
||||
{(codeEnabled || retrievalEnabled) && (
|
||||
<Knowledge assistant_id={assistant_id} files={files} />
|
||||
{(codeEnabled || retrievalEnabled) && version == 1 && (
|
||||
<Knowledge assistant_id={assistant_id} files={files} endpoint={endpoint} />
|
||||
)}
|
||||
{/* Capabilities */}
|
||||
<div className="mb-6">
|
||||
<div className="mb-1.5 flex items-center">
|
||||
<span>
|
||||
<label className="text-token-text-primary block font-medium">
|
||||
{localize('com_assistants_capabilities')}
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
{codeEnabled && (
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.code_interpreter}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label text-token-text-primary w-full cursor-pointer"
|
||||
htmlFor={Capabilities.code_interpreter}
|
||||
onClick={() =>
|
||||
setValue(
|
||||
Capabilities.code_interpreter,
|
||||
!getValues(Capabilities.code_interpreter),
|
||||
{
|
||||
shouldDirty: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
{localize('com_assistants_code_interpreter')}
|
||||
<QuestionMark />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{imageVisionEnabled && (
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.image_vision}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label text-token-text-primary w-full cursor-pointer"
|
||||
htmlFor={Capabilities.image_vision}
|
||||
onClick={() =>
|
||||
setValue(Capabilities.image_vision, !getValues(Capabilities.image_vision), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
{localize('com_assistants_image_vision')}
|
||||
<QuestionMark />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
{retrievalEnabled && (
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.retrieval}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
disabled={!retrievalModels.has(model)}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<label
|
||||
className={cn(
|
||||
'form-check-label text-token-text-primary w-full',
|
||||
!retrievalModels.has(model) ? 'cursor-no-drop opacity-50' : 'cursor-pointer',
|
||||
)}
|
||||
htmlFor={Capabilities.retrieval}
|
||||
onClick={() =>
|
||||
retrievalModels.has(model) &&
|
||||
setValue(Capabilities.retrieval, !getValues(Capabilities.retrieval), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
{localize('com_assistants_retrieval')}
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<CapabilitiesForm
|
||||
version={version}
|
||||
endpoint={endpoint}
|
||||
codeEnabled={codeEnabled}
|
||||
assistantsConfig={assistantsConfig}
|
||||
retrievalEnabled={retrievalEnabled}
|
||||
/>
|
||||
{/* Tools */}
|
||||
<div className="mb-6">
|
||||
<label className={labelClass}>
|
||||
|
|
@ -520,6 +413,7 @@ export default function AssistantPanel({
|
|||
activeModel={activeModel}
|
||||
setCurrentAssistantId={setCurrentAssistantId}
|
||||
createMutation={create}
|
||||
endpoint={endpoint}
|
||||
/>
|
||||
{/* Secondary Select Button */}
|
||||
{assistant_id && (
|
||||
|
|
@ -554,6 +448,7 @@ export default function AssistantPanel({
|
|||
isOpen={showToolDialog}
|
||||
setIsOpen={setShowToolDialog}
|
||||
assistant_id={assistant_id}
|
||||
endpoint={endpoint}
|
||||
/>
|
||||
</form>
|
||||
</FormProvider>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
import { Plus } from 'lucide-react';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import {
|
||||
defaultAssistantFormValues,
|
||||
defaultOrderQuery,
|
||||
isImageVisionTool,
|
||||
EModelEndpoint,
|
||||
Capabilities,
|
||||
Tools,
|
||||
FileSources,
|
||||
Capabilities,
|
||||
LocalStorageKeys,
|
||||
isImageVisionTool,
|
||||
defaultAssistantFormValues,
|
||||
} from 'librechat-data-provider';
|
||||
import type { UseFormReset } from 'react-hook-form';
|
||||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import type { Assistant, AssistantCreateParams } from 'librechat-data-provider';
|
||||
import type { Assistant, AssistantCreateParams, AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type {
|
||||
AssistantForm,
|
||||
Actions,
|
||||
TAssistantOption,
|
||||
ExtendedFile,
|
||||
AssistantForm,
|
||||
TAssistantOption,
|
||||
LastSelectedModels,
|
||||
} from '~/common';
|
||||
import SelectDropDown from '~/components/ui/SelectDropDown';
|
||||
|
|
@ -29,12 +29,14 @@ const keys = new Set(['name', 'id', 'description', 'instructions', 'model']);
|
|||
export default function AssistantSelect({
|
||||
reset,
|
||||
value,
|
||||
endpoint,
|
||||
selectedAssistant,
|
||||
setCurrentAssistantId,
|
||||
createMutation,
|
||||
}: {
|
||||
reset: UseFormReset<AssistantForm>;
|
||||
value: TAssistantOption;
|
||||
endpoint: AssistantsEndpoint;
|
||||
selectedAssistant: string | null;
|
||||
setCurrentAssistantId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
createMutation: UseMutationResult<Assistant, Error, AssistantCreateParams>;
|
||||
|
|
@ -43,11 +45,11 @@ export default function AssistantSelect({
|
|||
const fileMap = useFileMapContext();
|
||||
const lastSelectedAssistant = useRef<string | null>(null);
|
||||
const [lastSelectedModels] = useLocalStorage<LastSelectedModels>(
|
||||
'lastSelectedModel',
|
||||
LocalStorageKeys.LAST_MODEL,
|
||||
{} as LastSelectedModels,
|
||||
);
|
||||
|
||||
const assistants = useListAssistantsQuery(defaultOrderQuery, {
|
||||
const assistants = useListAssistantsQuery(endpoint, undefined, {
|
||||
select: (res) =>
|
||||
res.data.map((_assistant) => {
|
||||
const assistant = {
|
||||
|
|
@ -55,30 +57,55 @@ export default function AssistantSelect({
|
|||
label: _assistant?.name ?? '',
|
||||
value: _assistant.id,
|
||||
files: _assistant?.file_ids ? ([] as Array<[string, ExtendedFile]>) : undefined,
|
||||
code_files: _assistant?.tool_resources?.code_interpreter?.file_ids
|
||||
? ([] as Array<[string, ExtendedFile]>)
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const handleFile = (file_id: string, list?: Array<[string, ExtendedFile]>) => {
|
||||
const file = fileMap?.[file_id];
|
||||
if (file) {
|
||||
list?.push([
|
||||
file_id,
|
||||
{
|
||||
file_id: file.file_id,
|
||||
type: file.type,
|
||||
filepath: file.filepath,
|
||||
filename: file.filename,
|
||||
width: file.width,
|
||||
height: file.height,
|
||||
size: file.bytes,
|
||||
preview: file.filepath,
|
||||
progress: 1,
|
||||
source: FileSources.openai,
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
list?.push([
|
||||
file_id,
|
||||
{
|
||||
file_id,
|
||||
type: '',
|
||||
filename: '',
|
||||
size: 1,
|
||||
progress: 1,
|
||||
filepath: endpoint,
|
||||
source: FileSources.openai,
|
||||
},
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
if (assistant.files && _assistant.file_ids) {
|
||||
_assistant.file_ids.forEach((file_id) => {
|
||||
const file = fileMap?.[file_id];
|
||||
if (file) {
|
||||
assistant.files?.push([
|
||||
file_id,
|
||||
{
|
||||
file_id: file.file_id,
|
||||
type: file.type,
|
||||
filepath: file.filepath,
|
||||
filename: file.filename,
|
||||
width: file.width,
|
||||
height: file.height,
|
||||
size: file.bytes,
|
||||
preview: file.filepath,
|
||||
progress: 1,
|
||||
source: FileSources.openai,
|
||||
},
|
||||
]);
|
||||
}
|
||||
});
|
||||
_assistant.file_ids.forEach((file_id) => handleFile(file_id, assistant.files));
|
||||
}
|
||||
|
||||
if (assistant.code_files && _assistant.tool_resources?.code_interpreter?.file_ids) {
|
||||
_assistant.tool_resources?.code_interpreter?.file_ids?.forEach((file_id) =>
|
||||
handleFile(file_id, assistant.code_files),
|
||||
);
|
||||
}
|
||||
|
||||
return assistant;
|
||||
}),
|
||||
});
|
||||
|
|
@ -92,7 +119,7 @@ export default function AssistantSelect({
|
|||
setCurrentAssistantId(undefined);
|
||||
return reset({
|
||||
...defaultAssistantFormValues,
|
||||
model: lastSelectedModels?.[EModelEndpoint.assistants] ?? '',
|
||||
model: lastSelectedModels?.[endpoint] ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +139,9 @@ export default function AssistantSelect({
|
|||
?.filter((tool) => tool.type !== 'function' || isImageVisionTool(tool))
|
||||
?.map((tool) => tool?.function?.name || tool.type)
|
||||
.forEach((tool) => {
|
||||
if (tool === Tools.file_search) {
|
||||
actions[Capabilities.retrieval] = true;
|
||||
}
|
||||
actions[tool] = true;
|
||||
});
|
||||
|
||||
|
|
@ -141,7 +171,7 @@ export default function AssistantSelect({
|
|||
reset(formValues);
|
||||
setCurrentAssistantId(assistant?.id);
|
||||
},
|
||||
[assistants.data, reset, setCurrentAssistantId, createMutation, lastSelectedModels],
|
||||
[assistants.data, reset, setCurrentAssistantId, createMutation, endpoint, lastSelectedModels],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
51
client/src/components/SidePanel/Builder/CapabilitiesForm.tsx
Normal file
51
client/src/components/SidePanel/Builder/CapabilitiesForm.tsx
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import { useMemo } from 'react';
|
||||
import { Capabilities } from 'librechat-data-provider';
|
||||
import type { TConfig, AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import ImageVision from './ImageVision';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import Retrieval from './Retrieval';
|
||||
import Code from './Code';
|
||||
|
||||
export default function CapabilitiesForm({
|
||||
version,
|
||||
endpoint,
|
||||
codeEnabled,
|
||||
retrievalEnabled,
|
||||
assistantsConfig,
|
||||
}: {
|
||||
version: number | string;
|
||||
codeEnabled?: boolean;
|
||||
retrievalEnabled?: boolean;
|
||||
endpoint: AssistantsEndpoint;
|
||||
assistantsConfig?: TConfig | null;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
|
||||
const retrievalModels = useMemo(
|
||||
() => new Set(assistantsConfig?.retrievalModels ?? []),
|
||||
[assistantsConfig],
|
||||
);
|
||||
const imageVisionEnabled = useMemo(
|
||||
() => assistantsConfig?.capabilities?.includes(Capabilities.image_vision),
|
||||
[assistantsConfig],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mb-6">
|
||||
<div className="mb-1.5 flex items-center">
|
||||
<span>
|
||||
<label className="text-token-text-primary block font-medium">
|
||||
{localize('com_assistants_capabilities')}
|
||||
</label>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
{codeEnabled && <Code endpoint={endpoint} version={version} />}
|
||||
{imageVisionEnabled && version == 1 && <ImageVision />}
|
||||
{retrievalEnabled && (
|
||||
<Retrieval endpoint={endpoint} version={version} retrievalModels={retrievalModels} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
71
client/src/components/SidePanel/Builder/Code.tsx
Normal file
71
client/src/components/SidePanel/Builder/Code.tsx
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { useMemo } from 'react';
|
||||
import { Capabilities } from 'librechat-data-provider';
|
||||
import { useFormContext, Controller, useWatch } from 'react-hook-form';
|
||||
import type { AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { AssistantForm } from '~/common';
|
||||
import { Checkbox, QuestionMark } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import CodeFiles from './CodeFiles';
|
||||
|
||||
export default function Code({
|
||||
version,
|
||||
endpoint,
|
||||
}: {
|
||||
version: number | string;
|
||||
endpoint: AssistantsEndpoint;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const methods = useFormContext<AssistantForm>();
|
||||
const { control, setValue, getValues } = methods;
|
||||
const assistant = useWatch({ control, name: 'assistant' });
|
||||
const assistant_id = useWatch({ control, name: 'id' });
|
||||
const files = useMemo(() => {
|
||||
if (typeof assistant === 'string') {
|
||||
return [];
|
||||
}
|
||||
return assistant.code_files;
|
||||
}, [assistant]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.code_interpreter}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label text-token-text-primary w-full cursor-pointer"
|
||||
htmlFor={Capabilities.code_interpreter}
|
||||
onClick={() =>
|
||||
setValue(Capabilities.code_interpreter, !getValues(Capabilities.code_interpreter), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<div className="flex select-none items-center">
|
||||
{localize('com_assistants_code_interpreter')}
|
||||
<QuestionMark />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
{version == 2 && (
|
||||
<CodeFiles
|
||||
assistant_id={assistant_id}
|
||||
version={version}
|
||||
endpoint={endpoint}
|
||||
files={files}
|
||||
tool_resource={Capabilities.code_interpreter}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
93
client/src/components/SidePanel/Builder/CodeFiles.tsx
Normal file
93
client/src/components/SidePanel/Builder/CodeFiles.tsx
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import { useState, useRef, useEffect } from 'react';
|
||||
import { mergeFileConfig, fileConfig as defaultFileConfig } from 'librechat-data-provider';
|
||||
import type { AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { ExtendedFile } from '~/common';
|
||||
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
||||
import { useGetFileConfig } from '~/data-provider';
|
||||
import { useFileHandling } from '~/hooks/Files';
|
||||
import useLocalize from '~/hooks/useLocalize';
|
||||
import { useChatContext } from '~/Providers';
|
||||
|
||||
export default function Knowledge({
|
||||
endpoint,
|
||||
assistant_id,
|
||||
files: _files,
|
||||
tool_resource,
|
||||
}: {
|
||||
version: number | string;
|
||||
endpoint: AssistantsEndpoint;
|
||||
assistant_id: string;
|
||||
files?: [string, ExtendedFile][];
|
||||
tool_resource?: string;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const { setFilesLoading } = useChatContext();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const [files, setFiles] = useState<Map<string, ExtendedFile>>(new Map());
|
||||
const { data: fileConfig = defaultFileConfig } = useGetFileConfig({
|
||||
select: (data) => mergeFileConfig(data),
|
||||
});
|
||||
const { handleFileChange } = useFileHandling({
|
||||
overrideEndpoint: endpoint,
|
||||
additionalMetadata: { assistant_id, tool_resource },
|
||||
fileSetter: setFiles,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (_files) {
|
||||
setFiles(new Map(_files));
|
||||
}
|
||||
}, [_files]);
|
||||
|
||||
const endpointFileConfig = fileConfig.endpoints[endpoint];
|
||||
|
||||
if (endpointFileConfig?.disabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleButtonClick = () => {
|
||||
// necessary to reset the input
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
}
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'mb-2'}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="text-token-text-tertiary rounded-lg text-xs">
|
||||
{assistant_id ? localize('com_assistants_knowledge_info') : ''}
|
||||
</div>
|
||||
<FileRow
|
||||
files={files}
|
||||
setFiles={setFiles}
|
||||
setFilesLoading={setFilesLoading}
|
||||
assistant_id={assistant_id}
|
||||
Wrapper={({ children }) => <div className="flex flex-wrap gap-2">{children}</div>}
|
||||
/>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
disabled={!assistant_id}
|
||||
className="btn btn-neutral border-token-border-light relative h-8 rounded-lg font-medium"
|
||||
onClick={handleButtonClick}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
<input
|
||||
multiple={true}
|
||||
type="file"
|
||||
style={{ display: 'none' }}
|
||||
tabIndex={-1}
|
||||
ref={fileInputRef}
|
||||
disabled={!assistant_id}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
{localize('com_ui_upload_files')}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,26 +1,29 @@
|
|||
import * as Popover from '@radix-ui/react-popover';
|
||||
import type { Assistant, AssistantCreateParams } from 'librechat-data-provider';
|
||||
import type { Assistant, AssistantCreateParams, AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import { Dialog, DialogTrigger, Label } from '~/components/ui';
|
||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import { useChatContext, useToastContext } from '~/Providers';
|
||||
import { useDeleteAssistantMutation } from '~/data-provider';
|
||||
import DialogTemplate from '~/components/ui/DialogTemplate';
|
||||
import { useLocalize, useSetIndexOptions } from '~/hooks';
|
||||
import { cn, removeFocusOutlines } from '~/utils/';
|
||||
import { NewTrashIcon } from '~/components/svg';
|
||||
import { useChatContext } from '~/Providers';
|
||||
|
||||
export default function ContextButton({
|
||||
activeModel,
|
||||
assistant_id,
|
||||
setCurrentAssistantId,
|
||||
createMutation,
|
||||
endpoint,
|
||||
}: {
|
||||
activeModel: string;
|
||||
assistant_id: string;
|
||||
setCurrentAssistantId: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
createMutation: UseMutationResult<Assistant, Error, AssistantCreateParams>;
|
||||
endpoint: AssistantsEndpoint;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const { showToast } = useToastContext();
|
||||
const { conversation } = useChatContext();
|
||||
const { setOption } = useSetIndexOptions();
|
||||
|
||||
|
|
@ -31,6 +34,11 @@ export default function ContextButton({
|
|||
return;
|
||||
}
|
||||
|
||||
showToast({
|
||||
message: localize('com_ui_assistant_deleted'),
|
||||
status: 'success',
|
||||
});
|
||||
|
||||
if (createMutation.data?.id) {
|
||||
console.log('[deleteAssistant] resetting createMutation');
|
||||
createMutation.reset();
|
||||
|
|
@ -55,6 +63,13 @@ export default function ContextButton({
|
|||
|
||||
setCurrentAssistantId(firstAssistant.id);
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(error);
|
||||
showToast({
|
||||
message: localize('com_ui_assistant_delete_error'),
|
||||
status: 'error',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
if (!assistant_id) {
|
||||
|
|
@ -138,7 +153,8 @@ export default function ContextButton({
|
|||
</>
|
||||
}
|
||||
selection={{
|
||||
selectHandler: () => deleteAssistant.mutate({ assistant_id, model: activeModel }),
|
||||
selectHandler: () =>
|
||||
deleteAssistant.mutate({ assistant_id, model: activeModel, endpoint }),
|
||||
selectClasses: 'bg-red-600 hover:bg-red-700 dark:hover:bg-red-800 text-white',
|
||||
selectText: localize('com_ui_delete'),
|
||||
}}
|
||||
|
|
|
|||
43
client/src/components/SidePanel/Builder/ImageVision.tsx
Normal file
43
client/src/components/SidePanel/Builder/ImageVision.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { useFormContext, Controller } from 'react-hook-form';
|
||||
import { Capabilities } from 'librechat-data-provider';
|
||||
import type { AssistantForm } from '~/common';
|
||||
import { Checkbox, QuestionMark } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
|
||||
export default function ImageVision() {
|
||||
const localize = useLocalize();
|
||||
const methods = useFormContext<AssistantForm>();
|
||||
const { control, setValue, getValues } = methods;
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.image_vision}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<label
|
||||
className="form-check-label text-token-text-primary w-full cursor-pointer"
|
||||
htmlFor={Capabilities.image_vision}
|
||||
onClick={() =>
|
||||
setValue(Capabilities.image_vision, !getValues(Capabilities.image_vision), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
{localize('com_assistants_image_vision')}
|
||||
<QuestionMark />
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import { useState, useRef, useEffect } from 'react';
|
||||
import {
|
||||
EModelEndpoint,
|
||||
mergeFileConfig,
|
||||
retrievalMimeTypes,
|
||||
fileConfig as defaultFileConfig,
|
||||
mergeFileConfig,
|
||||
} from 'librechat-data-provider';
|
||||
import type { AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { ExtendedFile } from '~/common';
|
||||
import FileRow from '~/components/Chat/Input/Files/FileRow';
|
||||
import { useGetFileConfig } from '~/data-provider';
|
||||
|
|
@ -26,9 +26,11 @@ const CodeInterpreterFiles = ({ children }: { children: React.ReactNode }) => {
|
|||
};
|
||||
|
||||
export default function Knowledge({
|
||||
endpoint,
|
||||
assistant_id,
|
||||
files: _files,
|
||||
}: {
|
||||
endpoint: AssistantsEndpoint;
|
||||
assistant_id: string;
|
||||
files?: [string, ExtendedFile][];
|
||||
}) {
|
||||
|
|
@ -40,7 +42,7 @@ export default function Knowledge({
|
|||
select: (data) => mergeFileConfig(data),
|
||||
});
|
||||
const { handleFileChange } = useFileHandling({
|
||||
overrideEndpoint: EModelEndpoint.assistants,
|
||||
overrideEndpoint: endpoint,
|
||||
additionalMetadata: { assistant_id },
|
||||
fileSetter: setFiles,
|
||||
});
|
||||
|
|
@ -51,7 +53,7 @@ export default function Knowledge({
|
|||
}
|
||||
}, [_files]);
|
||||
|
||||
const endpointFileConfig = fileConfig.endpoints[EModelEndpoint.assistants];
|
||||
const endpointFileConfig = fileConfig.endpoints[endpoint];
|
||||
|
||||
if (endpointFileConfig?.disabled) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import type { Action } from 'librechat-data-provider';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { defaultAssistantsVersion } from 'librechat-data-provider';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { Action, AssistantsEndpoint, TEndpointsConfig } from 'librechat-data-provider';
|
||||
import { useGetActionsQuery } from '~/data-provider';
|
||||
import AssistantPanel from './AssistantPanel';
|
||||
import { useChatContext } from '~/Providers';
|
||||
|
|
@ -9,11 +11,18 @@ import { Panel } from '~/common';
|
|||
export default function PanelSwitch() {
|
||||
const { conversation, index } = useChatContext();
|
||||
const [activePanel, setActivePanel] = useState(Panel.builder);
|
||||
const [action, setAction] = useState<Action | undefined>(undefined);
|
||||
const [currentAssistantId, setCurrentAssistantId] = useState<string | undefined>(
|
||||
conversation?.assistant_id,
|
||||
);
|
||||
const [action, setAction] = useState<Action | undefined>(undefined);
|
||||
const { data: actions = [] } = useGetActionsQuery();
|
||||
|
||||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
||||
const { data: actions = [] } = useGetActionsQuery(conversation?.endpoint as AssistantsEndpoint);
|
||||
|
||||
const assistantsConfig = useMemo(
|
||||
() => endpointsConfig?.[conversation?.endpoint ?? ''],
|
||||
[conversation?.endpoint, endpointsConfig],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (conversation?.assistant_id) {
|
||||
|
|
@ -21,6 +30,12 @@ export default function PanelSwitch() {
|
|||
}
|
||||
}, [conversation?.assistant_id]);
|
||||
|
||||
if (!conversation?.endpoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const version = assistantsConfig?.version ?? defaultAssistantsVersion[conversation.endpoint];
|
||||
|
||||
if (activePanel === Panel.actions || action) {
|
||||
return (
|
||||
<ActionsPanel
|
||||
|
|
@ -32,6 +47,8 @@ export default function PanelSwitch() {
|
|||
setActivePanel={setActivePanel}
|
||||
assistant_id={currentAssistantId}
|
||||
setCurrentAssistantId={setCurrentAssistantId}
|
||||
endpoint={conversation.endpoint as AssistantsEndpoint}
|
||||
version={version}
|
||||
/>
|
||||
);
|
||||
} else if (activePanel === Panel.builder) {
|
||||
|
|
@ -45,6 +62,9 @@ export default function PanelSwitch() {
|
|||
setActivePanel={setActivePanel}
|
||||
assistant_id={currentAssistantId}
|
||||
setCurrentAssistantId={setCurrentAssistantId}
|
||||
endpoint={conversation.endpoint as AssistantsEndpoint}
|
||||
assistantsConfig={assistantsConfig}
|
||||
version={version}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
70
client/src/components/SidePanel/Builder/Retrieval.tsx
Normal file
70
client/src/components/SidePanel/Builder/Retrieval.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { useEffect, useMemo } from 'react';
|
||||
import { useFormContext, Controller, useWatch } from 'react-hook-form';
|
||||
import { Capabilities } from 'librechat-data-provider';
|
||||
import type { AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { AssistantForm } from '~/common';
|
||||
import { Checkbox } from '~/components/ui';
|
||||
import { useLocalize } from '~/hooks';
|
||||
import { cn } from '~/utils/';
|
||||
|
||||
export default function Retrieval({
|
||||
version,
|
||||
retrievalModels,
|
||||
}: {
|
||||
version: number | string;
|
||||
retrievalModels: Set<string>;
|
||||
endpoint: AssistantsEndpoint;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const methods = useFormContext<AssistantForm>();
|
||||
const { control, setValue, getValues } = methods;
|
||||
const model = useWatch({ control, name: 'model' });
|
||||
const assistant = useWatch({ control, name: 'assistant' });
|
||||
|
||||
const files = useMemo(() => {
|
||||
if (typeof assistant === 'string') {
|
||||
return [];
|
||||
}
|
||||
return assistant.tool_resources?.file_search;
|
||||
}, [assistant]);
|
||||
|
||||
useEffect(() => {
|
||||
if (model && !retrievalModels.has(model)) {
|
||||
setValue(Capabilities.retrieval, false);
|
||||
}
|
||||
}, [model, setValue, retrievalModels]);
|
||||
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<Controller
|
||||
name={Capabilities.retrieval}
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Checkbox
|
||||
{...field}
|
||||
checked={field.value}
|
||||
disabled={!retrievalModels.has(model)}
|
||||
onCheckedChange={field.onChange}
|
||||
className="relative float-left mr-2 inline-flex h-4 w-4 cursor-pointer"
|
||||
value={field?.value?.toString()}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<label
|
||||
className={cn(
|
||||
'form-check-label text-token-text-primary w-full',
|
||||
!retrievalModels.has(model) ? 'cursor-no-drop opacity-50' : 'cursor-pointer',
|
||||
)}
|
||||
htmlFor={Capabilities.retrieval}
|
||||
onClick={() =>
|
||||
retrievalModels.has(model) &&
|
||||
setValue(Capabilities.retrieval, !getValues(Capabilities.retrieval), {
|
||||
shouldDirty: true,
|
||||
})
|
||||
}
|
||||
>
|
||||
{localize('com_assistants_retrieval')}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import throttle from 'lodash/throttle';
|
||||
import { EModelEndpoint, getConfigDefaults } from 'librechat-data-provider';
|
||||
import { getConfigDefaults } from 'librechat-data-provider';
|
||||
import { useState, useRef, useCallback, useEffect, useMemo, memo } from 'react';
|
||||
import {
|
||||
useGetEndpointsQuery,
|
||||
|
|
@ -61,7 +61,7 @@ const SidePanel = ({
|
|||
return activePanel ? activePanel : undefined;
|
||||
}, []);
|
||||
|
||||
const assistants = useMemo(() => endpointsConfig?.[EModelEndpoint.assistants], [endpointsConfig]);
|
||||
const assistants = useMemo(() => endpointsConfig?.[endpoint ?? ''], [endpoint, endpointsConfig]);
|
||||
const userProvidesKey = useMemo(
|
||||
() => !!endpointsConfig?.[endpoint ?? '']?.userProvide,
|
||||
[endpointsConfig, endpoint],
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { SwitcherProps } from '~/common';
|
||||
import { Separator } from '~/components/ui/Separator';
|
||||
import AssistantSwitcher from './AssistantSwitcher';
|
||||
import ModelSwitcher from './ModelSwitcher';
|
||||
|
||||
export default function Switcher(props: SwitcherProps) {
|
||||
if (props.endpoint === EModelEndpoint.assistants && props.endpointKeyProvided) {
|
||||
if (isAssistantsEndpoint(props.endpoint) && props.endpointKeyProvided) {
|
||||
return (
|
||||
<>
|
||||
<AssistantSwitcher {...props} />
|
||||
<Separator className="bg-gray-100/50 dark:bg-gray-600" />
|
||||
</>
|
||||
);
|
||||
} else if (props.endpoint === EModelEndpoint.assistants) {
|
||||
} else if (isAssistantsEndpoint(props.endpoint)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Search, X } from 'lucide-react';
|
|||
import { Dialog } from '@headlessui/react';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { useUpdateUserPluginsMutation } from 'librechat-data-provider/react-query';
|
||||
import type { TError, TPluginAction } from 'librechat-data-provider';
|
||||
import type { AssistantsEndpoint, TError, TPluginAction } from 'librechat-data-provider';
|
||||
import type { TPluginStoreDialogProps } from '~/common/types';
|
||||
import { PluginPagination, PluginAuthForm } from '~/components/Plugins/Store';
|
||||
import { useLocalize, usePluginDialogHelpers } from '~/hooks';
|
||||
|
|
@ -13,10 +13,11 @@ import ToolItem from './ToolItem';
|
|||
function ToolSelectDialog({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
}: TPluginStoreDialogProps & { assistant_id?: string }) {
|
||||
endpoint,
|
||||
}: TPluginStoreDialogProps & { assistant_id?: string; endpoint: AssistantsEndpoint }) {
|
||||
const localize = useLocalize();
|
||||
const { getValues, setValue } = useFormContext();
|
||||
const { data: tools = [] } = useAvailableToolsQuery();
|
||||
const { data: tools = [] } = useAvailableToolsQuery(endpoint);
|
||||
|
||||
const {
|
||||
maxPage,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { LocalStorageKeys } from 'librechat-data-provider';
|
||||
import { Capabilities, LocalStorageKeys, defaultAssistantsVersion } from 'librechat-data-provider';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import type { UseMutationResult } from '@tanstack/react-query';
|
||||
import type t from 'librechat-data-provider';
|
||||
|
|
@ -376,9 +376,10 @@ export const useUploadFileMutation = (
|
|||
const { onSuccess, ...options } = _options || {};
|
||||
return useMutation([MutationKeys.fileUpload], {
|
||||
mutationFn: (body: FormData) => {
|
||||
const height = body.get('height');
|
||||
const width = body.get('width');
|
||||
if (height && width) {
|
||||
const height = body.get('height');
|
||||
const version = body.get('version') as number | string;
|
||||
if (height && width && (!version || version != 2)) {
|
||||
return dataService.uploadImage(body);
|
||||
}
|
||||
|
||||
|
|
@ -391,8 +392,10 @@ export const useUploadFileMutation = (
|
|||
...(_files ?? []),
|
||||
]);
|
||||
|
||||
const endpoint = formData.get('endpoint');
|
||||
const assistant_id = formData.get('assistant_id');
|
||||
const message_file = formData.get('message_file');
|
||||
const tool_resource = formData.get('tool_resource');
|
||||
|
||||
if (!assistant_id || message_file === 'true') {
|
||||
onSuccess?.(data, formData, context);
|
||||
|
|
@ -400,7 +403,7 @@ export const useUploadFileMutation = (
|
|||
}
|
||||
|
||||
queryClient.setQueryData<t.AssistantListResponse>(
|
||||
[QueryKeys.assistants, defaultOrderQuery],
|
||||
[QueryKeys.assistants, endpoint, defaultOrderQuery],
|
||||
(prev) => {
|
||||
if (!prev) {
|
||||
return prev;
|
||||
|
|
@ -409,13 +412,29 @@ export const useUploadFileMutation = (
|
|||
return {
|
||||
...prev,
|
||||
data: prev?.data.map((assistant) => {
|
||||
if (assistant.id === assistant_id) {
|
||||
return {
|
||||
...assistant,
|
||||
file_ids: [...assistant.file_ids, data.file_id],
|
||||
if (assistant.id !== assistant_id) {
|
||||
return assistant;
|
||||
}
|
||||
|
||||
const update = {};
|
||||
if (!tool_resource) {
|
||||
update['file_ids'] = [...assistant.file_ids, data.file_id];
|
||||
}
|
||||
if (tool_resource === Capabilities.code_interpreter) {
|
||||
const prevResources = assistant.tool_resources ?? {};
|
||||
const prevResource = assistant.tool_resources?.[tool_resource as string] ?? {
|
||||
file_ids: [],
|
||||
};
|
||||
prevResource.file_ids.push(data.file_id);
|
||||
update['tool_resources'] = {
|
||||
...prevResources,
|
||||
[tool_resource as string]: prevResource,
|
||||
};
|
||||
}
|
||||
return assistant;
|
||||
return {
|
||||
...assistant,
|
||||
...update,
|
||||
};
|
||||
}),
|
||||
};
|
||||
},
|
||||
|
|
@ -542,6 +561,7 @@ export const useCreateAssistantMutation = (
|
|||
onSuccess: (newAssistant, variables, context) => {
|
||||
const listRes = queryClient.getQueryData<t.AssistantListResponse>([
|
||||
QueryKeys.assistants,
|
||||
variables.endpoint,
|
||||
defaultOrderQuery,
|
||||
]);
|
||||
|
||||
|
|
@ -552,7 +572,7 @@ export const useCreateAssistantMutation = (
|
|||
const currentAssistants = [newAssistant, ...JSON.parse(JSON.stringify(listRes.data))];
|
||||
|
||||
queryClient.setQueryData<t.AssistantListResponse>(
|
||||
[QueryKeys.assistants, defaultOrderQuery],
|
||||
[QueryKeys.assistants, variables.endpoint, defaultOrderQuery],
|
||||
{
|
||||
...listRes,
|
||||
data: currentAssistants,
|
||||
|
|
@ -576,14 +596,23 @@ export const useUpdateAssistantMutation = (
|
|||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
({ assistant_id, data }: { assistant_id: string; data: t.AssistantUpdateParams }) =>
|
||||
dataService.updateAssistant(assistant_id, data),
|
||||
({ assistant_id, data }: { assistant_id: string; data: t.AssistantUpdateParams }) => {
|
||||
const { endpoint } = data;
|
||||
const endpointsConfig = queryClient.getQueryData<t.TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
return dataService.updateAssistant({
|
||||
data,
|
||||
version,
|
||||
assistant_id,
|
||||
});
|
||||
},
|
||||
{
|
||||
onMutate: (variables) => options?.onMutate?.(variables),
|
||||
onError: (error, variables, context) => options?.onError?.(error, variables, context),
|
||||
onSuccess: (updatedAssistant, variables, context) => {
|
||||
const listRes = queryClient.getQueryData<t.AssistantListResponse>([
|
||||
QueryKeys.assistants,
|
||||
variables.data.endpoint,
|
||||
defaultOrderQuery,
|
||||
]);
|
||||
|
||||
|
|
@ -592,7 +621,7 @@ export const useUpdateAssistantMutation = (
|
|||
}
|
||||
|
||||
queryClient.setQueryData<t.AssistantListResponse>(
|
||||
[QueryKeys.assistants, defaultOrderQuery],
|
||||
[QueryKeys.assistants, variables.data.endpoint, defaultOrderQuery],
|
||||
{
|
||||
...listRes,
|
||||
data: listRes.data.map((assistant) => {
|
||||
|
|
@ -617,14 +646,18 @@ export const useDeleteAssistantMutation = (
|
|||
): UseMutationResult<void, Error, t.DeleteAssistantBody> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(
|
||||
({ assistant_id, model }: t.DeleteAssistantBody) =>
|
||||
dataService.deleteAssistant(assistant_id, model),
|
||||
({ assistant_id, model, endpoint }: t.DeleteAssistantBody) => {
|
||||
const endpointsConfig = queryClient.getQueryData<t.TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
return dataService.deleteAssistant({ assistant_id, model, version, endpoint });
|
||||
},
|
||||
{
|
||||
onMutate: (variables) => options?.onMutate?.(variables),
|
||||
onError: (error, variables, context) => options?.onError?.(error, variables, context),
|
||||
onSuccess: (_data, variables, context) => {
|
||||
const listRes = queryClient.getQueryData<t.AssistantListResponse>([
|
||||
QueryKeys.assistants,
|
||||
variables.endpoint,
|
||||
defaultOrderQuery,
|
||||
]);
|
||||
|
||||
|
|
@ -635,7 +668,7 @@ export const useDeleteAssistantMutation = (
|
|||
const data = listRes.data.filter((assistant) => assistant.id !== variables.assistant_id);
|
||||
|
||||
queryClient.setQueryData<t.AssistantListResponse>(
|
||||
[QueryKeys.assistants, defaultOrderQuery],
|
||||
[QueryKeys.assistants, variables.endpoint, defaultOrderQuery],
|
||||
{
|
||||
...listRes,
|
||||
data,
|
||||
|
|
@ -687,6 +720,7 @@ export const useUpdateAction = (
|
|||
onSuccess: (updateActionResponse, variables, context) => {
|
||||
const listRes = queryClient.getQueryData<t.AssistantListResponse>([
|
||||
QueryKeys.assistants,
|
||||
variables.endpoint,
|
||||
defaultOrderQuery,
|
||||
]);
|
||||
|
||||
|
|
@ -696,15 +730,18 @@ export const useUpdateAction = (
|
|||
|
||||
const updatedAssistant = updateActionResponse[1];
|
||||
|
||||
queryClient.setQueryData<t.AssistantListResponse>([QueryKeys.assistants, defaultOrderQuery], {
|
||||
...listRes,
|
||||
data: listRes.data.map((assistant) => {
|
||||
if (assistant.id === variables.assistant_id) {
|
||||
return updatedAssistant;
|
||||
}
|
||||
return assistant;
|
||||
}),
|
||||
});
|
||||
queryClient.setQueryData<t.AssistantListResponse>(
|
||||
[QueryKeys.assistants, variables.endpoint, defaultOrderQuery],
|
||||
{
|
||||
...listRes,
|
||||
data: listRes.data.map((assistant) => {
|
||||
if (assistant.id === variables.assistant_id) {
|
||||
return updatedAssistant;
|
||||
}
|
||||
return assistant;
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
queryClient.setQueryData<t.Action[]>([QueryKeys.actions], (prev) => {
|
||||
return prev
|
||||
|
|
@ -735,8 +772,15 @@ export const useDeleteAction = (
|
|||
> => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation([MutationKeys.deleteAction], {
|
||||
mutationFn: (variables: t.DeleteActionVariables) =>
|
||||
dataService.deleteAction(variables.assistant_id, variables.action_id, variables.model),
|
||||
mutationFn: (variables: t.DeleteActionVariables) => {
|
||||
const { endpoint } = variables;
|
||||
const endpointsConfig = queryClient.getQueryData<t.TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
return dataService.deleteAction({
|
||||
...variables,
|
||||
version,
|
||||
});
|
||||
},
|
||||
|
||||
onMutate: (variables) => options?.onMutate?.(variables),
|
||||
onError: (error, variables, context) => options?.onError?.(error, variables, context),
|
||||
|
|
@ -750,7 +794,7 @@ export const useDeleteAction = (
|
|||
});
|
||||
|
||||
queryClient.setQueryData<t.AssistantListResponse>(
|
||||
[QueryKeys.assistants, defaultOrderQuery],
|
||||
[QueryKeys.assistants, variables.endpoint, defaultOrderQuery],
|
||||
(prev) => {
|
||||
if (!prev) {
|
||||
return prev;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import { EModelEndpoint, QueryKeys, dataService, defaultOrderQuery } from 'librechat-data-provider';
|
||||
import {
|
||||
QueryKeys,
|
||||
dataService,
|
||||
defaultOrderQuery,
|
||||
defaultAssistantsVersion,
|
||||
} from 'librechat-data-provider';
|
||||
import { useQuery, useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import type {
|
||||
UseInfiniteQueryOptions,
|
||||
|
|
@ -194,43 +199,46 @@ export const useSharedLinksInfiniteQuery = (
|
|||
/**
|
||||
* Hook for getting all available tools for Assistants
|
||||
*/
|
||||
export const useAvailableToolsQuery = (): QueryObserverResult<TPlugin[]> => {
|
||||
export const useAvailableToolsQuery = (
|
||||
endpoint: t.AssistantsEndpoint,
|
||||
): QueryObserverResult<TPlugin[]> => {
|
||||
const queryClient = useQueryClient();
|
||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([
|
||||
QueryKeys.name,
|
||||
EModelEndpoint.assistants,
|
||||
]);
|
||||
const userProvidesKey = !!endpointsConfig?.[EModelEndpoint.assistants]?.userProvide;
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([QueryKeys.name, endpoint]);
|
||||
const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide;
|
||||
const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true;
|
||||
const enabled = !!endpointsConfig?.[EModelEndpoint.assistants] && keyProvided;
|
||||
return useQuery<TPlugin[]>([QueryKeys.tools], () => dataService.getAvailableTools(), {
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
enabled,
|
||||
});
|
||||
const enabled = !!endpointsConfig?.[endpoint] && keyProvided;
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
return useQuery<TPlugin[]>(
|
||||
[QueryKeys.tools],
|
||||
() => dataService.getAvailableTools(version, endpoint),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
enabled,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for listing all assistants, with optional parameters provided for pagination and sorting
|
||||
*/
|
||||
export const useListAssistantsQuery = <TData = AssistantListResponse>(
|
||||
params: AssistantListParams = defaultOrderQuery,
|
||||
endpoint: t.AssistantsEndpoint,
|
||||
params: Omit<AssistantListParams, 'endpoint'> = defaultOrderQuery,
|
||||
config?: UseQueryOptions<AssistantListResponse, unknown, TData>,
|
||||
): QueryObserverResult<TData> => {
|
||||
const queryClient = useQueryClient();
|
||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([
|
||||
QueryKeys.name,
|
||||
EModelEndpoint.assistants,
|
||||
]);
|
||||
const userProvidesKey = !!endpointsConfig?.[EModelEndpoint.assistants]?.userProvide;
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([QueryKeys.name, endpoint]);
|
||||
const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide;
|
||||
const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true;
|
||||
const enabled = !!endpointsConfig?.[EModelEndpoint.assistants] && keyProvided;
|
||||
const enabled = !!endpointsConfig?.[endpoint] && keyProvided;
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
return useQuery<AssistantListResponse, unknown, TData>(
|
||||
[QueryKeys.assistants, params],
|
||||
() => dataService.listAssistants(params),
|
||||
[QueryKeys.assistants, endpoint, params],
|
||||
() => dataService.listAssistants({ ...params, endpoint }, version),
|
||||
{
|
||||
// Example selector to sort them by created_at
|
||||
// select: (res) => {
|
||||
|
|
@ -246,6 +254,7 @@ export const useListAssistantsQuery = <TData = AssistantListResponse>(
|
|||
);
|
||||
};
|
||||
|
||||
/*
|
||||
export const useListAssistantsInfiniteQuery = (
|
||||
params?: AssistantListParams,
|
||||
config?: UseInfiniteQueryOptions<AssistantListResponse, Error>,
|
||||
|
|
@ -275,26 +284,31 @@ export const useListAssistantsInfiniteQuery = (
|
|||
},
|
||||
);
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hook for retrieving details about a single assistant
|
||||
*/
|
||||
export const useGetAssistantByIdQuery = (
|
||||
endpoint: t.AssistantsEndpoint,
|
||||
assistant_id: string,
|
||||
config?: UseQueryOptions<Assistant>,
|
||||
): QueryObserverResult<Assistant> => {
|
||||
const queryClient = useQueryClient();
|
||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([
|
||||
QueryKeys.name,
|
||||
EModelEndpoint.assistants,
|
||||
]);
|
||||
const userProvidesKey = !!endpointsConfig?.[EModelEndpoint.assistants]?.userProvide;
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([QueryKeys.name, endpoint]);
|
||||
const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide;
|
||||
const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true;
|
||||
const enabled = !!endpointsConfig?.[EModelEndpoint.assistants] && keyProvided;
|
||||
const enabled = !!endpointsConfig?.[endpoint] && keyProvided;
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
return useQuery<Assistant>(
|
||||
[QueryKeys.assistant, assistant_id],
|
||||
() => dataService.getAssistantById(assistant_id),
|
||||
() =>
|
||||
dataService.getAssistantById({
|
||||
endpoint,
|
||||
assistant_id,
|
||||
version,
|
||||
}),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
|
|
@ -311,43 +325,53 @@ export const useGetAssistantByIdQuery = (
|
|||
* Hook for retrieving user's saved Assistant Actions
|
||||
*/
|
||||
export const useGetActionsQuery = <TData = Action[]>(
|
||||
endpoint: t.AssistantsEndpoint,
|
||||
config?: UseQueryOptions<Action[], unknown, TData>,
|
||||
): QueryObserverResult<TData> => {
|
||||
const queryClient = useQueryClient();
|
||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([
|
||||
QueryKeys.name,
|
||||
EModelEndpoint.assistants,
|
||||
]);
|
||||
const userProvidesKey = !!endpointsConfig?.[EModelEndpoint.assistants]?.userProvide;
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([QueryKeys.name, endpoint]);
|
||||
const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide;
|
||||
const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true;
|
||||
const enabled = !!endpointsConfig?.[EModelEndpoint.assistants] && keyProvided;
|
||||
return useQuery<Action[], unknown, TData>([QueryKeys.actions], () => dataService.getActions(), {
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
...config,
|
||||
enabled: config?.enabled !== undefined ? config?.enabled && enabled : enabled,
|
||||
});
|
||||
const enabled = !!endpointsConfig?.[endpoint] && keyProvided;
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
return useQuery<Action[], unknown, TData>(
|
||||
[QueryKeys.actions],
|
||||
() =>
|
||||
dataService.getActions({
|
||||
endpoint,
|
||||
version,
|
||||
}),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
refetchOnMount: false,
|
||||
...config,
|
||||
enabled: config?.enabled !== undefined ? config?.enabled && enabled : enabled,
|
||||
},
|
||||
);
|
||||
};
|
||||
/**
|
||||
* Hook for retrieving user's saved Assistant Documents (metadata saved to Database)
|
||||
*/
|
||||
export const useGetAssistantDocsQuery = (
|
||||
endpoint: t.AssistantsEndpoint,
|
||||
config?: UseQueryOptions<AssistantDocument[]>,
|
||||
): QueryObserverResult<AssistantDocument[], unknown> => {
|
||||
const queryClient = useQueryClient();
|
||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([
|
||||
QueryKeys.name,
|
||||
EModelEndpoint.assistants,
|
||||
]);
|
||||
const userProvidesKey = !!endpointsConfig?.[EModelEndpoint.assistants]?.userProvide;
|
||||
const keyExpiry = queryClient.getQueryData<TCheckUserKeyResponse>([QueryKeys.name, endpoint]);
|
||||
const userProvidesKey = !!endpointsConfig?.[endpoint]?.userProvide;
|
||||
const keyProvided = userProvidesKey ? !!keyExpiry?.expiresAt : true;
|
||||
const enabled = !!endpointsConfig?.[EModelEndpoint.assistants] && keyProvided;
|
||||
const enabled = !!endpointsConfig?.[endpoint] && keyProvided;
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
return useQuery<AssistantDocument[]>(
|
||||
[QueryKeys.assistantDocs],
|
||||
() => dataService.getAssistantDocs(),
|
||||
() =>
|
||||
dataService.getAssistantDocs({
|
||||
endpoint,
|
||||
version,
|
||||
}),
|
||||
{
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnReconnect: false,
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
export { default as useAssistantsMap } from './useAssistantsMap';
|
||||
export { default as useSelectAssistant } from './useSelectAssistant';
|
||||
export { default as useAssistantListMap } from './useAssistantListMap';
|
||||
|
|
|
|||
44
client/src/hooks/Assistants/useAssistantListMap.ts
Normal file
44
client/src/hooks/Assistants/useAssistantListMap.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { useMemo } from 'react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { AssistantListResponse, AssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { AssistantListItem } from '~/common';
|
||||
import { useListAssistantsQuery } from '~/data-provider';
|
||||
|
||||
const selectAssistantsResponse = (res: AssistantListResponse): AssistantListItem[] =>
|
||||
res.data.map(({ id, name, metadata, model }) => ({
|
||||
id,
|
||||
name: name ?? '',
|
||||
metadata,
|
||||
model,
|
||||
}));
|
||||
|
||||
export default function useAssistantListMap<T = AssistantListItem[] | null>(
|
||||
selector: (res: AssistantListResponse) => T = selectAssistantsResponse as (
|
||||
res: AssistantListResponse,
|
||||
) => T,
|
||||
): Record<AssistantsEndpoint, T> {
|
||||
const { data: assistantsList = null } = useListAssistantsQuery(
|
||||
EModelEndpoint.assistants,
|
||||
undefined,
|
||||
{
|
||||
select: selector,
|
||||
},
|
||||
);
|
||||
|
||||
const { data: azureAssistants = null } = useListAssistantsQuery(
|
||||
EModelEndpoint.azureAssistants,
|
||||
undefined,
|
||||
{
|
||||
select: selector,
|
||||
},
|
||||
);
|
||||
|
||||
const assistantListMap = useMemo(() => {
|
||||
return {
|
||||
[EModelEndpoint.assistants]: assistantsList as T,
|
||||
[EModelEndpoint.azureAssistants]: azureAssistants as T,
|
||||
};
|
||||
}, [assistantsList, azureAssistants]);
|
||||
|
||||
return assistantListMap;
|
||||
}
|
||||
|
|
@ -1,12 +1,28 @@
|
|||
import { defaultOrderQuery } from 'librechat-data-provider';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import type { TAssistantsMap } from 'librechat-data-provider';
|
||||
import { useListAssistantsQuery } from '~/data-provider';
|
||||
import { mapAssistants } from '~/utils';
|
||||
|
||||
export default function useAssistantsMap({ isAuthenticated }: { isAuthenticated: boolean }) {
|
||||
const { data: assistantMap = {} } = useListAssistantsQuery(defaultOrderQuery, {
|
||||
export default function useAssistantsMap({
|
||||
isAuthenticated,
|
||||
}: {
|
||||
isAuthenticated: boolean;
|
||||
}): TAssistantsMap {
|
||||
const { data: assistants = {} } = useListAssistantsQuery(EModelEndpoint.assistants, undefined, {
|
||||
select: (res) => mapAssistants(res.data),
|
||||
enabled: isAuthenticated,
|
||||
});
|
||||
const { data: azureAssistants = {} } = useListAssistantsQuery(
|
||||
EModelEndpoint.azureAssistants,
|
||||
undefined,
|
||||
{
|
||||
select: (res) => mapAssistants(res.data),
|
||||
enabled: isAuthenticated,
|
||||
},
|
||||
);
|
||||
|
||||
return assistantMap;
|
||||
return {
|
||||
[EModelEndpoint.assistants]: assistants,
|
||||
[EModelEndpoint.azureAssistants]: azureAssistants,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,30 @@
|
|||
import { useCallback } from 'react';
|
||||
import { EModelEndpoint, defaultOrderQuery } from 'librechat-data-provider';
|
||||
import type { TConversation, TPreset } from 'librechat-data-provider';
|
||||
import { isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { AssistantsEndpoint, TConversation, TPreset } from 'librechat-data-provider';
|
||||
import useDefaultConvo from '~/hooks/Conversations/useDefaultConvo';
|
||||
import { useListAssistantsQuery } from '~/data-provider';
|
||||
import { useChatContext } from '~/Providers/ChatContext';
|
||||
import useAssistantListMap from './useAssistantListMap';
|
||||
import { mapAssistants } from '~/utils';
|
||||
|
||||
export default function useSelectAssistant() {
|
||||
export default function useSelectAssistant(endpoint: AssistantsEndpoint) {
|
||||
const getDefaultConversation = useDefaultConvo();
|
||||
const { conversation, newConversation } = useChatContext();
|
||||
const { data: assistantMap = {} } = useListAssistantsQuery(defaultOrderQuery, {
|
||||
select: (res) => mapAssistants(res.data),
|
||||
});
|
||||
const assistantMap = useAssistantListMap((res) => mapAssistants(res.data));
|
||||
|
||||
const onSelect = useCallback(
|
||||
(value: string) => {
|
||||
const assistant = assistantMap?.[value];
|
||||
const assistant = assistantMap?.[endpoint]?.[value];
|
||||
if (!assistant) {
|
||||
return;
|
||||
}
|
||||
const template: Partial<TPreset | TConversation> = {
|
||||
endpoint: EModelEndpoint.assistants,
|
||||
endpoint,
|
||||
assistant_id: assistant.id,
|
||||
model: assistant.model,
|
||||
conversationId: 'new',
|
||||
};
|
||||
|
||||
if (conversation?.endpoint === EModelEndpoint.assistants) {
|
||||
if (isAssistantsEndpoint(conversation?.endpoint)) {
|
||||
const currentConvo = getDefaultConversation({
|
||||
conversation: { ...(conversation ?? {}) },
|
||||
preset: template,
|
||||
|
|
@ -44,7 +42,7 @@ export default function useSelectAssistant() {
|
|||
preset: template as Partial<TPreset>,
|
||||
});
|
||||
},
|
||||
[assistantMap, conversation, getDefaultConversation, newConversation],
|
||||
[endpoint, assistantMap, conversation, getDefaultConversation, newConversation],
|
||||
);
|
||||
|
||||
return { onSelect };
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import exportFromJSON from 'export-from-json';
|
|||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useRecoilState, useSetRecoilState, useRecoilValue } from 'recoil';
|
||||
import { QueryKeys, modularEndpoints, EModelEndpoint } from 'librechat-data-provider';
|
||||
import { QueryKeys, modularEndpoints, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import { useCreatePresetMutation, useGetModelsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { TPreset, TEndpointsConfig } from 'librechat-data-provider';
|
||||
import {
|
||||
|
|
@ -174,8 +174,8 @@ export default function usePresets() {
|
|||
const currentEndpointType = getEndpointField(endpointsConfig, endpoint, 'type');
|
||||
const endpointType = getEndpointField(endpointsConfig, newPreset.endpoint, 'type');
|
||||
const isAssistantSwitch =
|
||||
newPreset.endpoint === EModelEndpoint.assistants &&
|
||||
conversation?.endpoint === EModelEndpoint.assistants &&
|
||||
isAssistantsEndpoint(newPreset.endpoint) &&
|
||||
isAssistantsEndpoint(conversation?.endpoint) &&
|
||||
conversation?.endpoint === newPreset.endpoint;
|
||||
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
import { v4 } from 'uuid';
|
||||
import debounce from 'lodash/debounce';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import {
|
||||
megabyte,
|
||||
QueryKeys,
|
||||
EModelEndpoint,
|
||||
codeTypeMapping,
|
||||
mergeFileConfig,
|
||||
isAssistantsEndpoint,
|
||||
defaultAssistantsVersion,
|
||||
fileConfig as defaultFileConfig,
|
||||
} from 'librechat-data-provider';
|
||||
import type { TEndpointsConfig } from 'librechat-data-provider';
|
||||
import type { ExtendedFile, FileSetter } from '~/common';
|
||||
import { useUploadFileMutation, useGetFileConfig } from '~/data-provider';
|
||||
import { useDelayedUploadToast } from './useDelayedUploadToast';
|
||||
|
|
@ -20,10 +25,12 @@ const { checkType } = defaultFileConfig;
|
|||
type UseFileHandling = {
|
||||
overrideEndpoint?: EModelEndpoint;
|
||||
fileSetter?: FileSetter;
|
||||
additionalMetadata?: Record<string, string>;
|
||||
fileFilter?: (file: File) => boolean;
|
||||
additionalMetadata?: Record<string, string | undefined>;
|
||||
};
|
||||
|
||||
const useFileHandling = (params?: UseFileHandling) => {
|
||||
const queryClient = useQueryClient();
|
||||
const { showToast } = useToastContext();
|
||||
const [errors, setErrors] = useState<string[]>([]);
|
||||
const { startUploadTimer, clearUploadTimer } = useDelayedUploadToast();
|
||||
|
|
@ -141,15 +148,20 @@ const useFileHandling = (params?: UseFileHandling) => {
|
|||
|
||||
if (params?.additionalMetadata) {
|
||||
for (const [key, value] of Object.entries(params.additionalMetadata)) {
|
||||
formData.append(key, value);
|
||||
if (value) {
|
||||
formData.append(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
endpoint === EModelEndpoint.assistants &&
|
||||
isAssistantsEndpoint(endpoint) &&
|
||||
!formData.get('assistant_id') &&
|
||||
conversation?.assistant_id
|
||||
) {
|
||||
const endpointsConfig = queryClient.getQueryData<TEndpointsConfig>([QueryKeys.endpoints]);
|
||||
const version = endpointsConfig?.[endpoint]?.version ?? defaultAssistantsVersion[endpoint];
|
||||
formData.append('version', version);
|
||||
formData.append('assistant_id', conversation.assistant_id);
|
||||
formData.append('model', conversation?.model ?? '');
|
||||
formData.append('message_file', 'true');
|
||||
|
|
|
|||
|
|
@ -5,15 +5,42 @@ import {
|
|||
useGetEndpointsQuery,
|
||||
} from 'librechat-data-provider/react-query';
|
||||
import { getConfigDefaults, EModelEndpoint, alternateName } from 'librechat-data-provider';
|
||||
import type { Assistant } from 'librechat-data-provider';
|
||||
import { useGetPresetsQuery, useListAssistantsQuery } from '~/data-provider';
|
||||
import type { AssistantsEndpoint, TAssistantsMap, TEndpointsConfig } from 'librechat-data-provider';
|
||||
import type { MentionOption } from '~/common';
|
||||
import useAssistantListMap from '~/hooks/Assistants/useAssistantListMap';
|
||||
import { mapEndpoints, getPresetTitle } from '~/utils';
|
||||
import { EndpointIcon } from '~/components/Endpoints';
|
||||
import { useGetPresetsQuery } from '~/data-provider';
|
||||
import useSelectMention from './useSelectMention';
|
||||
|
||||
const defaultInterface = getConfigDefaults().interface;
|
||||
|
||||
export default function useMentions({ assistantMap }: { assistantMap: Record<string, Assistant> }) {
|
||||
const assistantMapFn =
|
||||
({
|
||||
endpoint,
|
||||
assistantMap,
|
||||
endpointsConfig,
|
||||
}: {
|
||||
endpoint: AssistantsEndpoint;
|
||||
assistantMap: TAssistantsMap;
|
||||
endpointsConfig: TEndpointsConfig;
|
||||
}) =>
|
||||
({ id, name, description }) => ({
|
||||
type: endpoint,
|
||||
label: name ?? '',
|
||||
value: id,
|
||||
description: description ?? '',
|
||||
icon: EndpointIcon({
|
||||
conversation: { assistant_id: id, endpoint },
|
||||
containerClassName: 'shadow-stroke overflow-hidden rounded-full',
|
||||
endpointsConfig: endpointsConfig,
|
||||
context: 'menu-item',
|
||||
assistantMap,
|
||||
size: 20,
|
||||
}),
|
||||
});
|
||||
|
||||
export default function useMentions({ assistantMap }: { assistantMap: TAssistantsMap }) {
|
||||
const { data: presets } = useGetPresetsQuery();
|
||||
const { data: modelsConfig } = useGetModelsQuery();
|
||||
const { data: startupConfig } = useGetStartupConfig();
|
||||
|
|
@ -21,30 +48,43 @@ export default function useMentions({ assistantMap }: { assistantMap: Record<str
|
|||
const { data: endpoints = [] } = useGetEndpointsQuery({
|
||||
select: mapEndpoints,
|
||||
});
|
||||
const { data: assistants = [] } = useListAssistantsQuery(undefined, {
|
||||
select: (res) =>
|
||||
res.data
|
||||
.map(({ id, name, description }) => ({
|
||||
type: 'assistant',
|
||||
label: name ?? '',
|
||||
value: id,
|
||||
description: description ?? '',
|
||||
icon: EndpointIcon({
|
||||
conversation: { assistant_id: id, endpoint: EModelEndpoint.assistants },
|
||||
containerClassName: 'shadow-stroke overflow-hidden rounded-full',
|
||||
endpointsConfig: endpointsConfig,
|
||||
context: 'menu-item',
|
||||
const listMap = useAssistantListMap((res) =>
|
||||
res.data.map(({ id, name, description }) => ({
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
})),
|
||||
);
|
||||
const assistantListMap = useMemo(
|
||||
() => ({
|
||||
[EModelEndpoint.assistants]: listMap[EModelEndpoint.assistants]
|
||||
.map(
|
||||
assistantMapFn({
|
||||
endpoint: EModelEndpoint.assistants,
|
||||
assistantMap,
|
||||
size: 20,
|
||||
endpointsConfig,
|
||||
}),
|
||||
}))
|
||||
)
|
||||
.filter(Boolean),
|
||||
});
|
||||
[EModelEndpoint.azureAssistants]: listMap[EModelEndpoint.azureAssistants]
|
||||
.map(
|
||||
assistantMapFn({
|
||||
endpoint: EModelEndpoint.azureAssistants,
|
||||
assistantMap,
|
||||
endpointsConfig,
|
||||
}),
|
||||
)
|
||||
.filter(Boolean),
|
||||
}),
|
||||
[listMap, assistantMap, endpointsConfig],
|
||||
);
|
||||
|
||||
const modelSpecs = useMemo(() => startupConfig?.modelSpecs?.list ?? [], [startupConfig]);
|
||||
const interfaceConfig = useMemo(
|
||||
() => startupConfig?.interface ?? defaultInterface,
|
||||
[startupConfig],
|
||||
);
|
||||
|
||||
const { onSelectMention } = useSelectMention({
|
||||
modelSpecs,
|
||||
endpointsConfig,
|
||||
|
|
@ -52,7 +92,7 @@ export default function useMentions({ assistantMap }: { assistantMap: Record<str
|
|||
assistantMap,
|
||||
});
|
||||
|
||||
const options = useMemo(() => {
|
||||
const options: MentionOption[] = useMemo(() => {
|
||||
const mentions = [
|
||||
...(modelSpecs?.length > 0 ? modelSpecs : []).map((modelSpec) => ({
|
||||
value: modelSpec.name,
|
||||
|
|
@ -67,12 +107,12 @@ export default function useMentions({ assistantMap }: { assistantMap: Record<str
|
|||
context: 'menu-item',
|
||||
size: 20,
|
||||
}),
|
||||
type: 'modelSpec',
|
||||
type: 'modelSpec' as const,
|
||||
})),
|
||||
...(interfaceConfig.endpointsMenu ? endpoints : []).map((endpoint) => ({
|
||||
value: endpoint,
|
||||
label: alternateName[endpoint] ?? endpoint ?? '',
|
||||
type: 'endpoint',
|
||||
type: 'endpoint' as const,
|
||||
icon: EndpointIcon({
|
||||
conversation: { endpoint },
|
||||
endpointsConfig,
|
||||
|
|
@ -80,7 +120,12 @@ export default function useMentions({ assistantMap }: { assistantMap: Record<str
|
|||
size: 20,
|
||||
}),
|
||||
})),
|
||||
...(endpointsConfig?.[EModelEndpoint.assistants] ? assistants : []),
|
||||
...(endpointsConfig?.[EModelEndpoint.assistants]
|
||||
? assistantListMap[EModelEndpoint.assistants]
|
||||
: []),
|
||||
...(endpointsConfig?.[EModelEndpoint.azureAssistants]
|
||||
? assistantListMap[EModelEndpoint.azureAssistants]
|
||||
: []),
|
||||
...((interfaceConfig.presets ? presets : [])?.map((preset, index) => ({
|
||||
value: preset.presetId ?? `preset-${index}`,
|
||||
label: preset.title ?? preset.modelLabel ?? preset.chatGptLabel ?? '',
|
||||
|
|
@ -93,7 +138,7 @@ export default function useMentions({ assistantMap }: { assistantMap: Record<str
|
|||
assistantMap,
|
||||
size: 20,
|
||||
}),
|
||||
type: 'preset',
|
||||
type: 'preset' as const,
|
||||
})) ?? []),
|
||||
];
|
||||
|
||||
|
|
@ -102,17 +147,17 @@ export default function useMentions({ assistantMap }: { assistantMap: Record<str
|
|||
presets,
|
||||
endpoints,
|
||||
modelSpecs,
|
||||
assistants,
|
||||
assistantMap,
|
||||
endpointsConfig,
|
||||
assistantListMap,
|
||||
interfaceConfig.presets,
|
||||
interfaceConfig.endpointsMenu,
|
||||
]);
|
||||
|
||||
return {
|
||||
options,
|
||||
assistants,
|
||||
modelsConfig,
|
||||
onSelectMention,
|
||||
assistantListMap,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { useCallback } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { EModelEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type {
|
||||
TPreset,
|
||||
TModelSpec,
|
||||
TConversation,
|
||||
TAssistantsMap,
|
||||
TEndpointsConfig,
|
||||
TPreset,
|
||||
Assistant,
|
||||
} from 'librechat-data-provider';
|
||||
import type { MentionOption } from '~/common';
|
||||
import { getConvoSwitchLogic, getModelSpecIconURL, removeUnavailableTools } from '~/utils';
|
||||
|
|
@ -23,7 +23,7 @@ export default function useSelectMention({
|
|||
presets?: TPreset[];
|
||||
modelSpecs: TModelSpec[];
|
||||
endpointsConfig: TEndpointsConfig;
|
||||
assistantMap: Record<string, Assistant>;
|
||||
assistantMap: TAssistantsMap;
|
||||
}) {
|
||||
const { conversation } = useChatContext();
|
||||
const { newConversation } = useNewConvo();
|
||||
|
|
@ -194,10 +194,10 @@ export default function useSelectMention({
|
|||
onSelectEndpoint(key, { model: option.label });
|
||||
} else if (option.type === 'endpoint') {
|
||||
onSelectEndpoint(key);
|
||||
} else if (option.type === 'assistant') {
|
||||
onSelectEndpoint(EModelEndpoint.assistants, {
|
||||
} else if (isAssistantsEndpoint(option.type)) {
|
||||
onSelectEndpoint(option.type, {
|
||||
assistant_id: key,
|
||||
model: assistantMap?.[key]?.model ?? '',
|
||||
model: assistantMap?.[option.type]?.[key]?.model ?? '',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import debounce from 'lodash/debounce';
|
||||
import { useEffect, useRef, useCallback } from 'react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import type { TEndpointOption } from 'librechat-data-provider';
|
||||
import type { KeyboardEvent } from 'react';
|
||||
|
|
@ -45,10 +45,11 @@ export default function useTextarea({
|
|||
const { conversationId, jailbreak, endpoint = '', assistant_id } = conversation || {};
|
||||
const isNotAppendable =
|
||||
((latestMessage?.unfinished && !isSubmitting) || latestMessage?.error) &&
|
||||
endpoint !== EModelEndpoint.assistants;
|
||||
!isAssistantsEndpoint(endpoint);
|
||||
// && (conversationId?.length ?? 0) > 6; // also ensures that we don't show the wrong placeholder
|
||||
|
||||
const assistant = endpoint === EModelEndpoint.assistants && assistantMap?.[assistant_id ?? ''];
|
||||
const assistant =
|
||||
isAssistantsEndpoint(endpoint) && assistantMap?.[endpoint ?? '']?.[assistant_id ?? ''];
|
||||
const assistantName = (assistant && assistant?.name) || '';
|
||||
|
||||
// auto focus to input, when enter a conversation.
|
||||
|
|
@ -86,9 +87,11 @@ export default function useTextarea({
|
|||
if (disabled) {
|
||||
return localize('com_endpoint_config_placeholder');
|
||||
}
|
||||
const currentEndpoint = conversation?.endpoint ?? '';
|
||||
const currentAssistantId = conversation?.assistant_id ?? '';
|
||||
if (
|
||||
conversation?.endpoint === EModelEndpoint.assistants &&
|
||||
(!conversation?.assistant_id || !assistantMap?.[conversation?.assistant_id ?? ''])
|
||||
isAssistantsEndpoint(currentEndpoint) &&
|
||||
(!currentAssistantId || !assistantMap?.[currentEndpoint]?.[currentAssistantId ?? ''])
|
||||
) {
|
||||
return localize('com_endpoint_assistant_placeholder');
|
||||
}
|
||||
|
|
@ -97,10 +100,9 @@ export default function useTextarea({
|
|||
return localize('com_endpoint_message_not_appendable');
|
||||
}
|
||||
|
||||
const sender =
|
||||
conversation?.endpoint === EModelEndpoint.assistants
|
||||
? getAssistantName({ name: assistantName, localize })
|
||||
: getSender(conversation as TEndpointOption);
|
||||
const sender = isAssistantsEndpoint(currentEndpoint)
|
||||
? getAssistantName({ name: assistantName, localize })
|
||||
: getSender(conversation as TEndpointOption);
|
||||
|
||||
return `${localize('com_endpoint_message')} ${sender ? sender : 'ChatGPT'}…`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useRef, useCallback } from 'react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import { useChatContext, useAssistantsMapContext } from '~/Providers';
|
||||
import useCopyToClipboard from './useCopyToClipboard';
|
||||
|
|
@ -55,7 +55,8 @@ export default function useMessageHelpers(props: TMessageProps) {
|
|||
}, [isSubmitting, setAbortScroll]);
|
||||
|
||||
const assistant =
|
||||
conversation?.endpoint === EModelEndpoint.assistants && assistantMap?.[message?.model ?? ''];
|
||||
isAssistantsEndpoint(conversation?.endpoint) &&
|
||||
assistantMap?.[conversation?.endpoint ?? '']?.[message?.model ?? ''];
|
||||
|
||||
const regenerateMessage = () => {
|
||||
if ((isSubmitting && isCreatedByUser) || !message) {
|
||||
|
|
|
|||
|
|
@ -1,35 +1,44 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useMemo, useCallback } from 'react';
|
||||
|
||||
export default function useProgress(initialProgress = 0.01, increment = 0.007, fileSize?: number) {
|
||||
const calculateIncrement = (size?: number) => {
|
||||
const baseRate = 0.05;
|
||||
const minRate = 0.002;
|
||||
const sizeMB = size ? size / (1024 * 1024) : 0;
|
||||
const calculateIncrement = useCallback(
|
||||
(size?: number) => {
|
||||
const baseRate = 0.05;
|
||||
const minRate = 0.002;
|
||||
const sizeMB = size ? size / (1024 * 1024) : 0;
|
||||
|
||||
if (!size) {
|
||||
return increment;
|
||||
}
|
||||
if (!size) {
|
||||
return increment;
|
||||
}
|
||||
|
||||
if (sizeMB <= 1) {
|
||||
return baseRate * 2;
|
||||
} else {
|
||||
return Math.max(baseRate / Math.sqrt(sizeMB), minRate);
|
||||
}
|
||||
};
|
||||
if (sizeMB <= 1) {
|
||||
return baseRate * 2;
|
||||
} else {
|
||||
return Math.max(baseRate / Math.sqrt(sizeMB), minRate);
|
||||
}
|
||||
},
|
||||
[increment],
|
||||
);
|
||||
|
||||
const incrementValue = calculateIncrement(fileSize);
|
||||
const incrementValue = useMemo(
|
||||
() => calculateIncrement(fileSize),
|
||||
[fileSize, calculateIncrement],
|
||||
);
|
||||
const [progress, setProgress] = useState(initialProgress);
|
||||
|
||||
const getDynamicIncrement = (currentProgress: number) => {
|
||||
if (!fileSize) {
|
||||
return incrementValue;
|
||||
}
|
||||
if (currentProgress < 0.7) {
|
||||
return incrementValue;
|
||||
} else {
|
||||
return Math.max(0.0005, incrementValue * (1 - currentProgress));
|
||||
}
|
||||
};
|
||||
const getDynamicIncrement = useCallback(
|
||||
(currentProgress: number) => {
|
||||
if (!fileSize) {
|
||||
return incrementValue;
|
||||
}
|
||||
if (currentProgress < 0.7) {
|
||||
return incrementValue;
|
||||
} else {
|
||||
return Math.max(0.0005, incrementValue * (1 - currentProgress));
|
||||
}
|
||||
},
|
||||
[incrementValue, fileSize],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
|
|
@ -58,7 +67,7 @@ export default function useProgress(initialProgress = 0.01, increment = 0.007, f
|
|||
clearInterval(timer);
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [progress, initialProgress, incrementValue, fileSize]);
|
||||
}, [progress, initialProgress, incrementValue, fileSize, getDynamicIncrement]);
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
ArrowRightToLine,
|
||||
// Settings2,
|
||||
} from 'lucide-react';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { EModelEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { TConfig, TInterfaceConfig } from 'librechat-data-provider';
|
||||
import type { NavLink } from '~/common';
|
||||
import PanelSwitch from '~/components/SidePanel/Builder/PanelSwitch';
|
||||
|
|
@ -26,7 +26,7 @@ export default function useSideNavLinks({
|
|||
}) {
|
||||
const Links = useMemo(() => {
|
||||
const links: NavLink[] = [];
|
||||
// if (endpoint !== EModelEndpoint.assistants) {
|
||||
// if (!isAssistantsEndpoint(endpoint)) {
|
||||
// links.push({
|
||||
// title: 'com_sidepanel_parameters',
|
||||
// label: '',
|
||||
|
|
@ -36,7 +36,7 @@ export default function useSideNavLinks({
|
|||
// });
|
||||
// }
|
||||
if (
|
||||
endpoint === EModelEndpoint.assistants &&
|
||||
isAssistantsEndpoint(endpoint) &&
|
||||
assistants &&
|
||||
assistants.disableBuilder !== true &&
|
||||
keyProvided &&
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import {
|
|||
createPayload,
|
||||
tPresetSchema,
|
||||
tMessageSchema,
|
||||
EModelEndpoint,
|
||||
LocalStorageKeys,
|
||||
tConvoUpdateSchema,
|
||||
removeNullishValues,
|
||||
isAssistantsEndpoint,
|
||||
} from 'librechat-data-provider';
|
||||
import { useGetUserBalance, useGetStartupConfig } from 'librechat-data-provider/react-query';
|
||||
import type {
|
||||
|
|
@ -441,7 +441,7 @@ export default function useSSE(submission: TSubmission | null, index = 0) {
|
|||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
abortKey: _endpoint === EModelEndpoint.assistants ? runAbortKey : conversationId,
|
||||
abortKey: isAssistantsEndpoint(_endpoint) ? runAbortKey : conversationId,
|
||||
endpoint,
|
||||
}),
|
||||
});
|
||||
|
|
@ -513,7 +513,7 @@ export default function useSSE(submission: TSubmission | null, index = 0) {
|
|||
|
||||
const payloadData = createPayload(submission);
|
||||
let { payload } = payloadData;
|
||||
if (payload.endpoint === EModelEndpoint.assistants) {
|
||||
if (isAssistantsEndpoint(payload.endpoint)) {
|
||||
payload = removeNullishValues(payload);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ export { default as useNewConvo } from './useNewConvo';
|
|||
export { default as useLocalize } from './useLocalize';
|
||||
export { default as useMediaQuery } from './useMediaQuery';
|
||||
export { default as useChatHelpers } from './useChatHelpers';
|
||||
export { default as useGenerations } from './useGenerations';
|
||||
export { default as useScrollToRef } from './useScrollToRef';
|
||||
export { default as useLocalStorage } from './useLocalStorage';
|
||||
export { default as useDelayedRender } from './useDelayedRender';
|
||||
|
|
|
|||
|
|
@ -3,10 +3,10 @@ import { useCallback, useState } from 'react';
|
|||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
Constants,
|
||||
EModelEndpoint,
|
||||
QueryKeys,
|
||||
parseCompactConvo,
|
||||
ContentTypes,
|
||||
parseCompactConvo,
|
||||
isAssistantsEndpoint,
|
||||
} from 'librechat-data-provider';
|
||||
import { useRecoilState, useResetRecoilState, useSetRecoilState } from 'recoil';
|
||||
import { useGetMessagesByConvoId } from 'librechat-data-provider/react-query';
|
||||
|
|
@ -215,7 +215,7 @@ export default function useChatHelpers(index = 0, paramId: string | undefined) {
|
|||
error: false,
|
||||
};
|
||||
|
||||
if (endpoint === EModelEndpoint.assistants) {
|
||||
if (isAssistantsEndpoint(endpoint)) {
|
||||
initialResponse.model = conversation?.assistant_id ?? '';
|
||||
initialResponse.text = '';
|
||||
initialResponse.content = [
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
import type { TMessage } from 'librechat-data-provider';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import store from '~/store';
|
||||
|
||||
type TUseGenerations = {
|
||||
endpoint?: string;
|
||||
message: TMessage;
|
||||
isSubmitting: boolean;
|
||||
isEditing?: boolean;
|
||||
latestMessage?: TMessage | null;
|
||||
};
|
||||
|
||||
export default function useGenerations({
|
||||
endpoint,
|
||||
message,
|
||||
isSubmitting,
|
||||
isEditing = false,
|
||||
latestMessage: _latestMessage,
|
||||
}: TUseGenerations) {
|
||||
const latestMessage = useRecoilValue(store.latestMessage) ?? _latestMessage;
|
||||
|
||||
const { error, messageId, searchResult, finish_reason, isCreatedByUser } = message ?? {};
|
||||
const isEditableEndpoint = !![
|
||||
EModelEndpoint.openAI,
|
||||
EModelEndpoint.google,
|
||||
EModelEndpoint.assistants,
|
||||
EModelEndpoint.anthropic,
|
||||
EModelEndpoint.gptPlugins,
|
||||
EModelEndpoint.azureOpenAI,
|
||||
].find((e) => e === endpoint);
|
||||
|
||||
const continueSupported =
|
||||
latestMessage?.messageId === messageId &&
|
||||
finish_reason &&
|
||||
finish_reason !== 'stop' &&
|
||||
!isEditing &&
|
||||
!searchResult &&
|
||||
isEditableEndpoint;
|
||||
|
||||
const branchingSupported =
|
||||
// 5/21/23: Bing is allowing editing and Message regenerating
|
||||
!![
|
||||
EModelEndpoint.azureOpenAI,
|
||||
EModelEndpoint.openAI,
|
||||
EModelEndpoint.chatGPTBrowser,
|
||||
EModelEndpoint.google,
|
||||
EModelEndpoint.bingAI,
|
||||
EModelEndpoint.gptPlugins,
|
||||
EModelEndpoint.anthropic,
|
||||
].find((e) => e === endpoint);
|
||||
|
||||
const regenerateEnabled =
|
||||
!isCreatedByUser && !searchResult && !isEditing && !isSubmitting && branchingSupported;
|
||||
|
||||
const hideEditButton =
|
||||
isSubmitting ||
|
||||
error ||
|
||||
searchResult ||
|
||||
!branchingSupported ||
|
||||
(!isEditableEndpoint && !isCreatedByUser);
|
||||
|
||||
return {
|
||||
continueSupported,
|
||||
regenerateEnabled,
|
||||
hideEditButton,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import type { TMessage } from 'librechat-data-provider';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import { EModelEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
|
||||
type TUseGenerations = {
|
||||
endpoint?: string;
|
||||
|
|
@ -21,7 +21,6 @@ export default function useGenerationsByLatest({
|
|||
EModelEndpoint.openAI,
|
||||
EModelEndpoint.custom,
|
||||
EModelEndpoint.google,
|
||||
EModelEndpoint.assistants,
|
||||
EModelEndpoint.anthropic,
|
||||
EModelEndpoint.gptPlugins,
|
||||
EModelEndpoint.azureOpenAI,
|
||||
|
|
@ -58,12 +57,13 @@ export default function useGenerationsByLatest({
|
|||
!branchingSupported ||
|
||||
(!isEditableEndpoint && !isCreatedByUser);
|
||||
|
||||
const forkingSupported = endpoint !== EModelEndpoint.assistants && !searchResult;
|
||||
const forkingSupported = !isAssistantsEndpoint(endpoint) && !searchResult;
|
||||
|
||||
return {
|
||||
forkingSupported,
|
||||
continueSupported,
|
||||
regenerateEnabled,
|
||||
isEditableEndpoint,
|
||||
hideEditButton,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,7 @@ import {
|
|||
useGetStartupConfig,
|
||||
useGetEndpointsQuery,
|
||||
} from 'librechat-data-provider/react-query';
|
||||
import {
|
||||
FileSources,
|
||||
EModelEndpoint,
|
||||
LocalStorageKeys,
|
||||
defaultOrderQuery,
|
||||
} from 'librechat-data-provider';
|
||||
import { FileSources, LocalStorageKeys, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import {
|
||||
useRecoilState,
|
||||
useRecoilValue,
|
||||
|
|
@ -24,6 +19,7 @@ import type {
|
|||
TConversation,
|
||||
TEndpointsConfig,
|
||||
} from 'librechat-data-provider';
|
||||
import type { AssistantListItem } from '~/common';
|
||||
import {
|
||||
getEndpointField,
|
||||
buildDefaultConvo,
|
||||
|
|
@ -32,7 +28,8 @@ import {
|
|||
getModelSpecIconURL,
|
||||
updateLastSelectedModel,
|
||||
} from '~/utils';
|
||||
import { useDeleteFilesMutation, useListAssistantsQuery } from '~/data-provider';
|
||||
import useAssistantListMap from './Assistants/useAssistantListMap';
|
||||
import { useDeleteFilesMutation } from '~/data-provider';
|
||||
import useOriginNavigate from './useOriginNavigate';
|
||||
import { mainTextareaId } from '~/common';
|
||||
import store from '~/store';
|
||||
|
|
@ -48,11 +45,7 @@ const useNewConvo = (index = 0) => {
|
|||
const { data: endpointsConfig = {} as TEndpointsConfig } = useGetEndpointsQuery();
|
||||
const modelsQuery = useGetModelsQuery();
|
||||
const timeoutIdRef = useRef<NodeJS.Timeout>();
|
||||
|
||||
const { data: assistants = [] } = useListAssistantsQuery(defaultOrderQuery, {
|
||||
select: (res) =>
|
||||
res.data.map(({ id, name, metadata, model }) => ({ id, name, metadata, model })),
|
||||
});
|
||||
const assistantsListMap = useAssistantListMap();
|
||||
|
||||
const { mutateAsync } = useDeleteFilesMutation({
|
||||
onSuccess: () => {
|
||||
|
|
@ -100,12 +93,21 @@ const useNewConvo = (index = 0) => {
|
|||
conversation.endpointType = undefined;
|
||||
}
|
||||
|
||||
const isAssistantEndpoint = defaultEndpoint === EModelEndpoint.assistants;
|
||||
const isAssistantEndpoint = isAssistantsEndpoint(defaultEndpoint);
|
||||
const assistants: AssistantListItem[] = assistantsListMap[defaultEndpoint] ?? [];
|
||||
|
||||
if (
|
||||
conversation.assistant_id &&
|
||||
!assistantsListMap[defaultEndpoint]?.[conversation.assistant_id]
|
||||
) {
|
||||
conversation.assistant_id = undefined;
|
||||
}
|
||||
|
||||
if (!conversation.assistant_id && isAssistantEndpoint) {
|
||||
conversation.assistant_id =
|
||||
localStorage.getItem(`${LocalStorageKeys.ASST_ID_PREFIX}${index}`) ??
|
||||
assistants[0]?.id;
|
||||
localStorage.getItem(
|
||||
`${LocalStorageKeys.ASST_ID_PREFIX}${index}${defaultEndpoint}`,
|
||||
) ?? assistants[0]?.id;
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
@ -116,7 +118,7 @@ const useNewConvo = (index = 0) => {
|
|||
const assistant = assistants.find((asst) => asst.id === conversation.assistant_id);
|
||||
conversation.model = assistant?.model;
|
||||
updateLastSelectedModel({
|
||||
endpoint: EModelEndpoint.assistants,
|
||||
endpoint: defaultEndpoint,
|
||||
model: conversation.model,
|
||||
});
|
||||
}
|
||||
|
|
@ -156,7 +158,7 @@ const useNewConvo = (index = 0) => {
|
|||
}
|
||||
}, 150);
|
||||
},
|
||||
[endpointsConfig, defaultPreset, assistants, modelsQuery.data],
|
||||
[endpointsConfig, defaultPreset, assistantsListMap, modelsQuery.data],
|
||||
);
|
||||
|
||||
const newConversation = useCallback(
|
||||
|
|
|
|||
|
|
@ -196,6 +196,8 @@ export default {
|
|||
com_ui_result: 'Result',
|
||||
com_ui_image_gen: 'Image Gen',
|
||||
com_ui_assistant: 'Assistant',
|
||||
com_ui_assistant_deleted: 'Successfully deleted assistant',
|
||||
com_ui_assistant_delete_error: 'There was an error deleting the assistant',
|
||||
com_ui_assistants: 'Assistants',
|
||||
com_ui_attachment: 'Attachment',
|
||||
com_ui_assistants_output: 'Assistants Output',
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { useEffect, useRef } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { defaultOrderQuery } from 'librechat-data-provider';
|
||||
import { EModelEndpoint } from 'librechat-data-provider';
|
||||
import {
|
||||
useGetModelsQuery,
|
||||
useGetStartupConfig,
|
||||
useGetEndpointsQuery,
|
||||
} from 'librechat-data-provider/react-query';
|
||||
import type { TPreset } from 'librechat-data-provider';
|
||||
import { useGetConvoIdQuery, useListAssistantsQuery } from '~/data-provider';
|
||||
import { useNewConvo, useAppStartup, useAssistantListMap } from '~/hooks';
|
||||
import { getDefaultModelSpec, getModelSpecIconURL } from '~/utils';
|
||||
import { useNewConvo, useAppStartup } from '~/hooks';
|
||||
import { useGetConvoIdQuery } from '~/data-provider';
|
||||
import ChatView from '~/components/Chat/ChatView';
|
||||
import useAuthRedirect from './useAuthRedirect';
|
||||
import { Spinner } from '~/components/svg';
|
||||
|
|
@ -35,10 +35,7 @@ export default function ChatRoute() {
|
|||
enabled: isAuthenticated && conversationId !== 'new',
|
||||
});
|
||||
const endpointsQuery = useGetEndpointsQuery({ enabled: isAuthenticated });
|
||||
const { data: assistants = null } = useListAssistantsQuery(defaultOrderQuery, {
|
||||
select: (res) =>
|
||||
res.data.map(({ id, name, metadata, model }) => ({ id, name, metadata, model })),
|
||||
});
|
||||
const assistantListMap = useAssistantListMap();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
|
@ -87,7 +84,8 @@ export default function ChatRoute() {
|
|||
!hasSetConversation.current &&
|
||||
!modelsQuery.data?.initial &&
|
||||
conversationId === 'new' &&
|
||||
assistants
|
||||
assistantListMap[EModelEndpoint.assistants] &&
|
||||
assistantListMap[EModelEndpoint.azureAssistants]
|
||||
) {
|
||||
const spec = getDefaultModelSpec(startupConfig.modelSpecs?.list);
|
||||
newConversation({
|
||||
|
|
@ -108,7 +106,8 @@ export default function ChatRoute() {
|
|||
startupConfig &&
|
||||
!hasSetConversation.current &&
|
||||
!modelsQuery.data?.initial &&
|
||||
assistants
|
||||
assistantListMap[EModelEndpoint.assistants] &&
|
||||
assistantListMap[EModelEndpoint.azureAssistants]
|
||||
) {
|
||||
newConversation({
|
||||
template: initialConvoQuery.data,
|
||||
|
|
@ -120,7 +119,13 @@ export default function ChatRoute() {
|
|||
}
|
||||
/* Creates infinite render if all dependencies included due to newConversation invocations exceeding call stack before hasSetConversation.current becomes truthy */
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [startupConfig, initialConvoQuery.data, endpointsQuery.data, modelsQuery.data, assistants]);
|
||||
}, [
|
||||
startupConfig,
|
||||
initialConvoQuery.data,
|
||||
endpointsQuery.data,
|
||||
modelsQuery.data,
|
||||
assistantListMap,
|
||||
]);
|
||||
|
||||
if (endpointsQuery.isLoading || modelsQuery.isLoading) {
|
||||
return <Spinner className="m-auto text-black dark:text-white" />;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import type { TEndpointsConfig } from 'librechat-data-provider';
|
|||
|
||||
const defaultConfig: TEndpointsConfig = {
|
||||
[EModelEndpoint.azureOpenAI]: null,
|
||||
[EModelEndpoint.azureAssistants]: null,
|
||||
[EModelEndpoint.assistants]: null,
|
||||
[EModelEndpoint.openAI]: null,
|
||||
[EModelEndpoint.bingAI]: null,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,10 @@ const conversationByIndex = atomFamily<TConversation | null, string | number>({
|
|||
onSet(async (newValue) => {
|
||||
const index = Number(node.key.split('__')[1]);
|
||||
if (newValue?.assistant_id) {
|
||||
localStorage.setItem(`${LocalStorageKeys.ASST_ID_PREFIX}${index}`, newValue.assistant_id);
|
||||
localStorage.setItem(
|
||||
`${LocalStorageKeys.ASST_ID_PREFIX}${index}${newValue?.endpoint}`,
|
||||
newValue.assistant_id,
|
||||
);
|
||||
}
|
||||
if (newValue?.spec) {
|
||||
localStorage.setItem(LocalStorageKeys.LAST_SPEC, newValue.spec);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { parseConvo, EModelEndpoint } from 'librechat-data-provider';
|
||||
import { parseConvo, EModelEndpoint, isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import type { TConversation } from 'librechat-data-provider';
|
||||
import getLocalStorageItems from './getLocalStorageItems';
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ const buildDefaultConvo = ({
|
|||
};
|
||||
|
||||
// Ensures assistant_id is always defined
|
||||
if (endpoint === EModelEndpoint.assistants && !defaultConvo.assistant_id && convo.assistant_id) {
|
||||
if (isAssistantsEndpoint(endpoint) && !defaultConvo.assistant_id && convo.assistant_id) {
|
||||
defaultConvo.assistant_id = convo.assistant_id;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export default function buildTree({
|
|||
|
||||
if (message.files && fileMap) {
|
||||
messageMap[message.messageId].files = message.files.map(
|
||||
(file) => fileMap[file.file_id] ?? file,
|
||||
(file) => fileMap[file.file_id ?? ''] ?? file,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import {
|
|||
defaultEndpoints,
|
||||
modularEndpoints,
|
||||
LocalStorageKeys,
|
||||
isAssistantsEndpoint,
|
||||
} from 'librechat-data-provider';
|
||||
import type {
|
||||
TConfig,
|
||||
|
|
@ -139,8 +140,8 @@ export function getConvoSwitchLogic(params: ConversationInitParams): InitiatedTe
|
|||
};
|
||||
|
||||
const isAssistantSwitch =
|
||||
newEndpoint === EModelEndpoint.assistants &&
|
||||
currentEndpoint === EModelEndpoint.assistants &&
|
||||
isAssistantsEndpoint(newEndpoint) &&
|
||||
isAssistantsEndpoint(currentEndpoint) &&
|
||||
currentEndpoint === newEndpoint;
|
||||
|
||||
const conversationId = conversation?.conversationId;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue