mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-21 09:46:12 +01:00
⚡️ refactor: Optimize Rendering Performance for Icons, Conversations (#5234)
* refactor: HoverButtons and Fork components to use explicit props * refactor: improve typing for Fork Component * fix: memoize SpecIcon to avoid unnecessary re-renders * feat: introduce URLIcon component and update SpecIcon for improved icon handling * WIP: optimizing icons * refactor: simplify modelLabel assignment in Message components * refactor: memoize ConvoOptions component to optimize rendering performance
This commit is contained in:
parent
687ab32bd3
commit
0f95604a67
19 changed files with 206 additions and 171 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import { memo } from 'react';
|
||||
import { EModelEndpoint, KnownEndpoints } from 'librechat-data-provider';
|
||||
import { CustomMinimalIcon } from '~/components/svg';
|
||||
import { IconContext } from '~/common';
|
||||
|
|
@ -53,7 +54,7 @@ const getKnownClass = ({
|
|||
return cn(match, defaultClass);
|
||||
};
|
||||
|
||||
export default function UnknownIcon({
|
||||
function UnknownIcon({
|
||||
className = '',
|
||||
endpoint: _endpoint,
|
||||
iconURL = '',
|
||||
|
|
@ -93,3 +94,5 @@ export default function UnknownIcon({
|
|||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(UnknownIcon);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import type { TModelSpec, TEndpointsConfig } from 'librechat-data-provider';
|
||||
import type { IconMapProps } from '~/common';
|
||||
import { getModelSpecIconURL, getIconKey, getEndpointField } from '~/utils';
|
||||
import { icons } from '~/components/Chat/Menus/Endpoints/Icons';
|
||||
import { URLIcon } from '~/components/Endpoints/URLIcon';
|
||||
|
||||
interface SpecIconProps {
|
||||
currentSpec: TModelSpec;
|
||||
|
|
@ -16,24 +17,12 @@ const SpecIcon: React.FC<SpecIconProps> = ({ currentSpec, endpointsConfig }) =>
|
|||
const iconKey = getIconKey({ endpoint, endpointsConfig, endpointIconURL });
|
||||
let Icon: (props: IconMapProps) => React.JSX.Element;
|
||||
|
||||
if (!iconURL?.includes('http')) {
|
||||
if (!iconURL.includes('http')) {
|
||||
Icon = icons[iconKey] ?? icons.unknown;
|
||||
} else if (iconURL) {
|
||||
return <URLIcon iconURL={iconURL} altName={currentSpec.name} />;
|
||||
} else {
|
||||
Icon = iconURL
|
||||
? () => (
|
||||
<div
|
||||
className="icon-xl mr-1 shrink-0 overflow-hidden rounded-full "
|
||||
style={{ width: '20', height: '20' }}
|
||||
>
|
||||
<img
|
||||
src={iconURL}
|
||||
alt={currentSpec.name}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
: icons[endpoint ?? ''] ?? icons.unknown;
|
||||
Icon = icons[endpoint ?? ''] ?? icons.unknown;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
@ -42,9 +31,9 @@ const SpecIcon: React.FC<SpecIconProps> = ({ currentSpec, endpointsConfig }) =>
|
|||
endpoint={endpoint}
|
||||
context="menu-item"
|
||||
iconURL={endpointIconURL}
|
||||
className="icon-lg mr-1 shrink-0 dark:text-white"
|
||||
className="icon-lg mr-1 shrink-0 text-text-primary"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SpecIcon;
|
||||
export default memo(SpecIcon);
|
||||
|
|
|
|||
|
|
@ -50,9 +50,13 @@ export default function HoverButtons({
|
|||
} = useGenerationsByLatest({
|
||||
isEditing,
|
||||
isSubmitting,
|
||||
message,
|
||||
error: message.error,
|
||||
endpoint: endpoint ?? '',
|
||||
latestMessage,
|
||||
messageId: message.messageId,
|
||||
searchResult: message.searchResult,
|
||||
finish_reason: message.finish_reason,
|
||||
isCreatedByUser: message.isCreatedByUser,
|
||||
latestMessageId: latestMessage?.messageId,
|
||||
});
|
||||
if (!conversation) {
|
||||
return null;
|
||||
|
|
@ -146,7 +150,7 @@ export default function HoverButtons({
|
|||
messageId={message.messageId}
|
||||
conversationId={conversation.conversationId}
|
||||
forkingSupported={forkingSupported}
|
||||
latestMessage={latestMessage}
|
||||
latestMessageId={latestMessage?.messageId}
|
||||
/>
|
||||
{continueSupported === true ? (
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,31 +1,38 @@
|
|||
import React, { useMemo, memo } from 'react';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { Assistant, Agent, TMessage } from 'librechat-data-provider';
|
||||
import type { Assistant, Agent } from 'librechat-data-provider';
|
||||
import type { TMessageIcon } from '~/common';
|
||||
import { getEndpointField, getIconEndpoint, logger } from '~/utils';
|
||||
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
|
||||
import { getEndpointField, getIconEndpoint } from '~/utils';
|
||||
import Icon from '~/components/Endpoints/Icon';
|
||||
|
||||
const MessageIcon = memo(
|
||||
(props: {
|
||||
iconData?: TMessage & { modelLabel?: string };
|
||||
({
|
||||
iconData,
|
||||
assistant,
|
||||
agent,
|
||||
}: {
|
||||
iconData?: TMessageIcon;
|
||||
assistant?: Assistant;
|
||||
agent?: Agent;
|
||||
}) => {
|
||||
logger.log('icon_data', iconData, assistant, agent);
|
||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
||||
const { iconData, assistant, agent } = props;
|
||||
|
||||
const agentName = useMemo(() => agent?.name ?? '', [agent]);
|
||||
const agentAvatar = useMemo(() => agent?.avatar?.filepath ?? '', [agent]);
|
||||
const assistantName = useMemo(() => assistant?.name ?? '', [assistant]);
|
||||
const assistantAvatar = useMemo(() => assistant?.metadata?.avatar ?? '', [assistant]);
|
||||
const agentName = useMemo(() => props.agent?.name ?? '', [props.agent]);
|
||||
const agentAvatar = useMemo(() => props.agent?.avatar?.filepath ?? '', [props.agent]);
|
||||
|
||||
let avatarURL = '';
|
||||
|
||||
if (assistant) {
|
||||
avatarURL = assistantAvatar;
|
||||
} else if (agent) {
|
||||
avatarURL = agentAvatar;
|
||||
}
|
||||
const avatarURL = useMemo(() => {
|
||||
let result = '';
|
||||
if (assistant) {
|
||||
result = assistantAvatar;
|
||||
} else if (agent) {
|
||||
result = agentAvatar;
|
||||
}
|
||||
return result;
|
||||
}, [assistant, agent, assistantAvatar, agentAvatar]);
|
||||
|
||||
const iconURL = iconData?.iconURL;
|
||||
const endpoint = useMemo(
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import type { TMessage, TMessageContentParts } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import type { TMessageContentParts } from 'librechat-data-provider';
|
||||
import type { TMessageProps, TMessageIcon } from '~/common';
|
||||
import MessageIcon from '~/components/Chat/Messages/MessageIcon';
|
||||
import { useMessageHelpers, useLocalize } from '~/hooks';
|
||||
import ContentParts from './Content/ContentParts';
|
||||
|
|
@ -35,19 +35,29 @@ export default function Message(props: TMessageProps) {
|
|||
} = useMessageHelpers(props);
|
||||
const fontSize = useRecoilValue(store.fontSize);
|
||||
const { children, messageId = null, isCreatedByUser } = message ?? {};
|
||||
const name = useMemo(() => {
|
||||
let result = '';
|
||||
if (isCreatedByUser === true) {
|
||||
result = localize('com_user_message');
|
||||
} else if (assistant) {
|
||||
result = assistant.name ?? localize('com_ui_assistant');
|
||||
} else if (agent) {
|
||||
result = agent.name ?? localize('com_ui_agent');
|
||||
}
|
||||
|
||||
const iconData = useMemo(
|
||||
() =>
|
||||
({
|
||||
endpoint: message?.endpoint ?? conversation?.endpoint,
|
||||
model: message?.model ?? conversation?.model,
|
||||
iconURL: message?.iconURL ?? conversation?.iconURL,
|
||||
modelLabel: conversation?.chatGptLabel ?? conversation?.modelLabel,
|
||||
isCreatedByUser: message?.isCreatedByUser,
|
||||
} as TMessage & { modelLabel?: string }),
|
||||
return result;
|
||||
}, [assistant, agent, isCreatedByUser, localize]);
|
||||
|
||||
const iconData: TMessageIcon = useMemo(
|
||||
() => ({
|
||||
endpoint: message?.endpoint ?? conversation?.endpoint,
|
||||
model: message?.model ?? conversation?.model,
|
||||
iconURL: message?.iconURL ?? conversation?.iconURL,
|
||||
modelLabel: name,
|
||||
isCreatedByUser: message?.isCreatedByUser,
|
||||
}),
|
||||
[
|
||||
conversation?.chatGptLabel,
|
||||
conversation?.modelLabel,
|
||||
name,
|
||||
conversation?.endpoint,
|
||||
conversation?.iconURL,
|
||||
conversation?.model,
|
||||
|
|
@ -61,16 +71,6 @@ export default function Message(props: TMessageProps) {
|
|||
return null;
|
||||
}
|
||||
|
||||
let name = '';
|
||||
|
||||
if (isCreatedByUser === true) {
|
||||
name = localize('com_user_message');
|
||||
} else if (assistant) {
|
||||
name = assistant.name ?? localize('com_ui_assistant');
|
||||
} else if (agent) {
|
||||
name = agent.name ?? localize('com_ui_agent');
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useAuthContext, useLocalize } from '~/hooks';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import type { TMessageProps, TMessageIcon } from '~/common';
|
||||
import MinimalHoverButtons from '~/components/Chat/Messages/MinimalHoverButtons';
|
||||
import Icon from '~/components/Chat/Messages/MessageIcon';
|
||||
import SearchContent from './Content/SearchContent';
|
||||
|
|
@ -17,14 +16,13 @@ export default function Message({ message }: Pick<TMessageProps, 'message'>) {
|
|||
const { user } = useAuthContext();
|
||||
const localize = useLocalize();
|
||||
|
||||
const iconData = useMemo(
|
||||
() =>
|
||||
({
|
||||
endpoint: message?.endpoint,
|
||||
model: message?.model,
|
||||
iconURL: message?.iconURL ?? '',
|
||||
isCreatedByUser: message?.isCreatedByUser,
|
||||
} as TMessage & { modelLabel?: string }),
|
||||
const iconData: TMessageIcon = useMemo(
|
||||
() => ({
|
||||
endpoint: message?.endpoint,
|
||||
model: message?.model,
|
||||
iconURL: message?.iconURL ?? '',
|
||||
isCreatedByUser: message?.isCreatedByUser,
|
||||
}),
|
||||
[message?.model, message?.iconURL, message?.endpoint, message?.isCreatedByUser],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import { useCallback, useMemo, memo } from 'react';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import type { TMessageProps, TMessageIcon } from '~/common';
|
||||
import MessageContent from '~/components/Chat/Messages/Content/MessageContent';
|
||||
import PlaceholderRow from '~/components/Chat/Messages/ui/PlaceholderRow';
|
||||
import SiblingSwitch from '~/components/Chat/Messages/SiblingSwitch';
|
||||
|
|
@ -66,18 +66,16 @@ const MessageRender = memo(
|
|||
[hasNoChildren, msg?.depth, latestMessage?.depth],
|
||||
);
|
||||
|
||||
const iconData = useMemo(
|
||||
() =>
|
||||
({
|
||||
endpoint: msg?.endpoint ?? conversation?.endpoint,
|
||||
model: msg?.model ?? conversation?.model,
|
||||
iconURL: msg?.iconURL ?? conversation?.iconURL,
|
||||
modelLabel: conversation?.chatGptLabel ?? conversation?.modelLabel,
|
||||
isCreatedByUser: msg?.isCreatedByUser,
|
||||
} as TMessage & { modelLabel?: string }),
|
||||
const iconData: TMessageIcon = useMemo(
|
||||
() => ({
|
||||
endpoint: msg?.endpoint ?? conversation?.endpoint,
|
||||
model: msg?.model ?? conversation?.model,
|
||||
iconURL: msg?.iconURL ?? conversation?.iconURL,
|
||||
modelLabel: messageLabel,
|
||||
isCreatedByUser: msg?.isCreatedByUser,
|
||||
}),
|
||||
[
|
||||
conversation?.chatGptLabel,
|
||||
conversation?.modelLabel,
|
||||
messageLabel,
|
||||
conversation?.endpoint,
|
||||
conversation?.iconURL,
|
||||
conversation?.model,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue