mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 01:10:14 +01:00
* 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
168 lines
5 KiB
TypeScript
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;
|
|
}
|