LibreChat/client/src/components/Chat/Messages/Content/Part.tsx
Danny Avila 14ddfec9b7
🧹 chore: address minor issues (#3710)
* refactor(OpenAIClient): improve error handling in chat completion

Improve error handling in the chat completion process to handle scenarios where the response has no choices or the message is undefined. This ensures that the intermediate reply is returned in such cases. Also, add logging statements for better debugging.

* refactor(Markdown): only show blinking cursor for latest message

* refactor: add submitting selector to prevent blinking cursor for empty, non-streaming, non-latest messages
2024-08-20 07:00:43 -04:00

168 lines
5 KiB
TypeScript

import {
ToolCallTypes,
ContentTypes,
imageGenTools,
isImageVisionTool,
} from 'librechat-data-provider';
import { useMemo } from 'react';
import type { TMessageContentParts, TMessage } from 'librechat-data-provider';
import type { TDisplayProps } from '~/common';
import { ErrorMessage } from './MessageContent';
import { useChatContext } from '~/Providers';
import RetrievalCall from './RetrievalCall';
import CodeAnalyze from './CodeAnalyze';
import Container from './Container';
import ToolCall from './ToolCall';
import Markdown from './Markdown';
import ImageGen from './ImageGen';
import Image from './Image';
import { cn } from '~/utils';
// import EditMessage from './EditMessage';
// Display Message Component
const DisplayMessage = ({ text, isCreatedByUser = false, message, showCursor }: TDisplayProps) => {
const { isSubmitting, latestMessage } = useChatContext();
const showCursorState = useMemo(
() => showCursor === true && isSubmitting,
[showCursor, isSubmitting],
);
const isLatestMessage = useMemo(
() => message.messageId === latestMessage?.messageId,
[message.messageId, latestMessage?.messageId],
);
return (
<div
className={cn(
isSubmitting ? 'submitting' : '',
showCursorState && !!text.length ? 'result-streaming' : '',
'markdown prose message-content dark:prose-invert light w-full break-words',
isCreatedByUser ? 'whitespace-pre-wrap dark:text-gray-20' : 'dark:text-gray-70',
)}
>
{!isCreatedByUser ? (
<Markdown
content={text}
isEdited={message.isEdited}
showCursor={showCursorState}
isLatestMessage={isLatestMessage}
/>
) : (
<>{text}</>
)}
</div>
);
};
export default function Part({
part,
showCursor,
isSubmitting,
message,
}: {
part: TMessageContentParts;
isSubmitting: boolean;
showCursor: boolean;
message: TMessage;
}) {
if (!part) {
return null;
}
if (part.type === ContentTypes.ERROR) {
return <ErrorMessage message={message} text={part[ContentTypes.TEXT].value} className="my-2" />;
} else if (part.type === ContentTypes.TEXT) {
// Access the value property
return (
<Container message={message}>
<DisplayMessage
text={part[ContentTypes.TEXT].value}
isCreatedByUser={message.isCreatedByUser}
message={message}
showCursor={showCursor}
/>
</Container>
);
} else if (
part.type === ContentTypes.TOOL_CALL &&
part[ContentTypes.TOOL_CALL].type === ToolCallTypes.CODE_INTERPRETER
) {
const toolCall = part[ContentTypes.TOOL_CALL];
const code_interpreter = toolCall[ToolCallTypes.CODE_INTERPRETER];
return (
<CodeAnalyze
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.FILE_SEARCH)
) {
const toolCall = part[ContentTypes.TOOL_CALL];
return <RetrievalCall initialProgress={toolCall.progress ?? 0.1} isSubmitting={isSubmitting} />;
} else if (
part.type === ContentTypes.TOOL_CALL &&
part[ContentTypes.TOOL_CALL].type === ToolCallTypes.FUNCTION &&
imageGenTools.has(part[ContentTypes.TOOL_CALL].function.name)
) {
const toolCall = part[ContentTypes.TOOL_CALL];
return (
<ImageGen initialProgress={toolCall.progress ?? 0.1} args={toolCall.function.arguments} />
);
} else if (
part.type === ContentTypes.TOOL_CALL &&
part[ContentTypes.TOOL_CALL].type === ToolCallTypes.FUNCTION
) {
const toolCall = part[ContentTypes.TOOL_CALL];
if (isImageVisionTool(toolCall)) {
if (isSubmitting && showCursor) {
return (
<Container message={message}>
<DisplayMessage
text={''}
isCreatedByUser={message.isCreatedByUser}
message={message}
showCursor={showCursor}
/>
</Container>
);
}
return null;
}
return (
<ToolCall
initialProgress={toolCall.progress ?? 0.1}
isSubmitting={isSubmitting}
args={toolCall.function.arguments}
name={toolCall.function.name}
output={toolCall.function.output}
/>
);
} else if (part.type === ContentTypes.IMAGE_FILE) {
const imageFile = part[ContentTypes.IMAGE_FILE];
const height = imageFile.height ?? 1920;
const width = imageFile.width ?? 1080;
return (
<Image
imagePath={imageFile.filepath}
height={height}
width={width}
altText={imageFile.filename ?? 'Uploaded Image'}
placeholderDimensions={{
height: height + 'px',
width: width + 'px',
}}
// n={imageFiles.length}
// i={i}
/>
);
}
return null;
}