🔧 fix: Dev Deployment, Mistral OCR Error, and UI Consistency (#7668)

* 🔧 fix: Update ProgressText and ToolCall components for improved error handling and localization

* 🔧 chore: Format ESLint configuration for improved readability and remove unused rule

* 🔧 refactor: Simplify ProgressText component logic for better readability and maintainability

* 🔧 refactor: Update ProgressText and ToolCall components for improved layout consistency

* 🔧 refactor: Simplify icon rendering in TTS components and enhance button rendering logic in HoverButtons

* 🔧 refactor: Update placeholder logic in VariableForm component to simply use variable name

* fix: .docx. .pptx Mistral OCR Error with `image_limit=0`

* chore: Update deploy workflow to include conditions for successful dev branch deployment and streamline deployment steps

* ci: Set image_limit to 0 in MistralOCR service tests for consistent behavior
This commit is contained in:
Danny Avila 2025-06-01 17:48:19 -04:00 committed by GitHub
parent a2fc7d312a
commit f9d40784f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 95 additions and 52 deletions

View file

@ -12,7 +12,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: | if: |
github.repository == 'danny-avila/LibreChat' && github.repository == 'danny-avila/LibreChat' &&
(github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success') (github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'dev'))
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -32,9 +33,13 @@ jobs:
sudo -i -u danny bash << EEOF sudo -i -u danny bash << EEOF
cd ~/LibreChat && \ cd ~/LibreChat && \
git fetch origin main && \ git fetch origin main && \
npm run stop:deployed && \
docker images -a | grep "librechat" | awk '{print \$3}' | xargs docker rmi && \
npm run update:deployed && \ npm run update:deployed && \
git checkout dev && \
git pull origin dev && \
git checkout do-deploy && \ git checkout do-deploy && \
git rebase main && \ git rebase dev && \
npm run start:deployed && \ npm run start:deployed && \
echo "Update completed. Application should be running now." echo "Update completed. Application should be running now."
EEOF EEOF

View file

@ -92,6 +92,7 @@ async function performOCR({
`${baseURL}/ocr`, `${baseURL}/ocr`,
{ {
model, model,
image_limit: 0,
include_image_base64: false, include_image_base64: false,
document: { document: {
type: documentType, type: documentType,

View file

@ -186,6 +186,7 @@ describe('MistralOCR Service', () => {
{ {
model: 'mistral-ocr-latest', model: 'mistral-ocr-latest',
include_image_base64: false, include_image_base64: false,
image_limit: 0,
document: { document: {
type: 'document_url', type: 'document_url',
document_url: 'https://document-url.com', document_url: 'https://document-url.com',
@ -221,6 +222,7 @@ describe('MistralOCR Service', () => {
{ {
model: 'mistral-ocr-latest', model: 'mistral-ocr-latest',
include_image_base64: false, include_image_base64: false,
image_limit: 0,
document: { document: {
type: 'image_url', type: 'image_url',
image_url: 'https://image-url.com/image.png', image_url: 'https://image-url.com/image.png',

View file

@ -25,16 +25,16 @@ export function BrowserTTS({
content, content,
}); });
const renderIcon = (size: string) => { const renderIcon = () => {
if (isLoading === true) { if (isLoading === true) {
return <Spinner size={size} />; return <Spinner className="icon-md-heavy h-[18px] w-[18px]" />;
} }
if (isSpeaking === true) { if (isSpeaking === true) {
return <VolumeMuteIcon size={size} />; return <VolumeMuteIcon className="icon-md-heavy h-[18px] w-[18px]" />;
} }
return <VolumeIcon size={size} />; return <VolumeIcon className="icon-md-heavy h-[18px] w-[18px]" />;
}; };
useEffect(() => { useEffect(() => {
@ -68,13 +68,13 @@ export function BrowserTTS({
renderButton({ renderButton({
onClick: handleClick, onClick: handleClick,
title: title, title: title,
icon: renderIcon('19'), icon: renderIcon(),
isActive: isSpeaking, isActive: isSpeaking,
className, className,
}) })
) : ( ) : (
<button className={className} onClickCapture={handleClick} type="button" title={title}> <button className={className} onClickCapture={handleClick} type="button" title={title}>
{renderIcon('19')} {renderIcon()}
</button> </button>
)} )}
<audio <audio
@ -100,7 +100,14 @@ export function BrowserTTS({
); );
} }
export function ExternalTTS({ isLast, index, messageId, content, className }: TMessageAudio) { export function ExternalTTS({
isLast,
index,
messageId,
content,
className,
renderButton,
}: TMessageAudio) {
const localize = useLocalize(); const localize = useLocalize();
const playbackRate = useRecoilValue(store.playbackRate); const playbackRate = useRecoilValue(store.playbackRate);
@ -111,16 +118,16 @@ export function ExternalTTS({ isLast, index, messageId, content, className }: TM
content, content,
}); });
const renderIcon = (size: string) => { const renderIcon = () => {
if (isLoading === true) { if (isLoading === true) {
return <Spinner size={size} />; return <Spinner className="icon-md-heavy h-[18px] w-[18px]" />;
} }
if (isSpeaking === true) { if (isSpeaking === true) {
return <VolumeMuteIcon size={size} />; return <VolumeMuteIcon className="icon-md-heavy h-[18px] w-[18px]" />;
} }
return <VolumeIcon size={size} />; return <VolumeIcon className="icon-md-heavy h-[18px] w-[18px]" />;
}; };
useEffect(() => { useEffect(() => {
@ -141,8 +148,21 @@ export function ExternalTTS({ isLast, index, messageId, content, className }: TM
return ( return (
<> <>
{renderButton ? (
renderButton({
onClick: () => {
if (audioRef.current) {
audioRef.current.muted = false;
}
toggleSpeech();
},
title: isSpeaking === true ? localize('com_ui_stop') : localize('com_ui_read_aloud'),
icon: renderIcon(),
isActive: isSpeaking,
className,
})
) : (
<button <button
className={className}
onClickCapture={() => { onClickCapture={() => {
if (audioRef.current) { if (audioRef.current) {
audioRef.current.muted = false; audioRef.current.muted = false;
@ -152,8 +172,9 @@ export function ExternalTTS({ isLast, index, messageId, content, className }: TM
type="button" type="button"
title={isSpeaking === true ? localize('com_ui_stop') : localize('com_ui_read_aloud')} title={isSpeaking === true ? localize('com_ui_stop') : localize('com_ui_read_aloud')}
> >
{renderIcon('19')} {renderIcon()}
</button> </button>
)}
<audio <audio
ref={audioRef} ref={audioRef}
controls controls

View file

@ -59,7 +59,30 @@ export default function ProgressText({
isExpanded?: boolean; isExpanded?: boolean;
error?: boolean; error?: boolean;
}) { }) {
const text = progress < 1 ? (authText ?? inProgressText) : finishedText; const getText = () => {
if (error) {
return finishedText;
}
if (progress < 1) {
return authText ?? inProgressText;
}
return finishedText;
};
const getIcon = () => {
if (error) {
return <CancelledIcon />;
}
if (progress < 1) {
return <Spinner />;
}
return <FinishedIcon />;
};
const text = getText();
const icon = getIcon();
const showShimmer = progress < 1 && !error;
return ( return (
<Wrapper popover={popover}> <Wrapper popover={popover}>
<button <button
@ -71,13 +94,13 @@ export default function ProgressText({
disabled={!hasInput} disabled={!hasInput}
onClick={hasInput ? onClick : undefined} onClick={hasInput ? onClick : undefined}
> >
{progress < 1 ? <Spinner /> : error ? <CancelledIcon /> : <FinishedIcon />} {icon}
<span className={`${progress < 1 ? 'shimmer' : ''}`}>{text}</span> <span className={showShimmer ? 'shimmer' : ''}>{text}</span>
{hasInput && {hasInput &&
(isExpanded ? ( (isExpanded ? (
<ChevronUp className="size-4 translate-y-[1px]" /> <ChevronUp className="size-4 shrink-0 translate-y-[1px]" />
) : ( ) : (
<ChevronDown className="size-4 translate-y-[1px]" /> <ChevronDown className="size-4 shrink-0 translate-y-[1px]" />
))} ))}
</button> </button>
</Wrapper> </Wrapper>

View file

@ -97,7 +97,7 @@ export default function ToolCall({
const getFinishedText = () => { const getFinishedText = () => {
if (cancelled) { if (cancelled) {
return localize('com_ui_error'); return localize('com_ui_cancelled');
} }
if (isMCPToolCall === true) { if (isMCPToolCall === true) {
return localize('com_assistants_completed_function', { 0: function_name }); return localize('com_assistants_completed_function', { 0: function_name });
@ -153,7 +153,7 @@ export default function ToolCall({
return ( return (
<> <>
<div className="relative my-2.5 flex size-5 shrink-0 items-center gap-2.5"> <div className="relative my-2.5 flex h-5 shrink-0 items-center gap-2.5">
<ProgressText <ProgressText
progress={progress} progress={progress}
onClick={() => setShowInfo((prev) => !prev)} onClick={() => setShowInfo((prev) => !prev)}

View file

@ -162,18 +162,6 @@ const HoverButtons = ({
const { isCreatedByUser, error } = message; const { isCreatedByUser, error } = message;
const buttonStyle = cn(
'hover-button rounded-lg p-1.5',
'hover:bg-gray-100 hover:text-gray-500',
'dark:text-gray-400/70 dark:hover:bg-gray-700 dark:hover:text-gray-200',
'disabled:dark:hover:text-gray-400',
'md:group-hover:visible md:group-focus-within:visible md:group-[.final-completion]:visible',
!isLast && 'md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100',
'focus-visible:ring-2 focus-visible:ring-black dark:focus-visible:ring-white focus-visible:outline-none',
'active text-gray-700 dark:text-gray-200 bg-gray-100 bg-gray-700',
);
// If message has an error, only show regenerate button
if (error === true) { if (error === true) {
return ( return (
<div className="visible flex justify-center self-end lg:justify-start"> <div className="visible flex justify-center self-end lg:justify-start">
@ -204,9 +192,9 @@ const HoverButtons = ({
{TextToSpeech && ( {TextToSpeech && (
<MessageAudio <MessageAudio
index={index} index={index}
isLast={isLast}
messageId={message.messageId} messageId={message.messageId}
content={extractMessageContent(message)} content={extractMessageContent(message)}
isLast={isLast}
renderButton={(props) => ( renderButton={(props) => (
<HoverButton <HoverButton
onClick={props.onClick} onClick={props.onClick}
@ -214,7 +202,6 @@ const HoverButtons = ({
icon={props.icon} icon={props.icon}
isActive={props.isActive} isActive={props.isActive}
isLast={isLast} isLast={isLast}
className={props.className}
/> />
)} )}
/> />

View file

@ -168,7 +168,7 @@ export default function VariableForm({
return ( return (
<InputCombobox <InputCombobox
options={field.config.options || []} options={field.config.options || []}
placeholder={localize('com_ui_enter_var', { 0: field.config.variable })} placeholder={field.config.variable}
className={cn( className={cn(
defaultTextProps, defaultTextProps,
'rounded px-3 py-2 focus:bg-surface-tertiary', 'rounded px-3 py-2 focus:bg-surface-tertiary',
@ -191,7 +191,7 @@ export default function VariableForm({
defaultTextProps, defaultTextProps,
'rounded px-3 py-2 focus:bg-surface-tertiary', 'rounded px-3 py-2 focus:bg-surface-tertiary',
)} )}
placeholder={localize('com_ui_enter_var', { 0: field.config.variable })} placeholder={field.config.variable}
maxRows={8} maxRows={8}
/> />
); );

View file

@ -594,6 +594,7 @@
"com_ui_bulk_delete_error": "Failed to delete shared links", "com_ui_bulk_delete_error": "Failed to delete shared links",
"com_ui_callback_url": "Callback URL", "com_ui_callback_url": "Callback URL",
"com_ui_cancel": "Cancel", "com_ui_cancel": "Cancel",
"com_ui_cancelled": "Cancelled",
"com_ui_category": "Category", "com_ui_category": "Category",
"com_ui_chat": "Chat", "com_ui_chat": "Chat",
"com_ui_chat_history": "Chat History", "com_ui_chat_history": "Chat History",
@ -684,7 +685,6 @@
"com_ui_enter": "Enter", "com_ui_enter": "Enter",
"com_ui_enter_api_key": "Enter API Key", "com_ui_enter_api_key": "Enter API Key",
"com_ui_enter_openapi_schema": "Enter your OpenAPI schema here", "com_ui_enter_openapi_schema": "Enter your OpenAPI schema here",
"com_ui_enter_var": "Enter {{0}}",
"com_ui_error": "Error", "com_ui_error": "Error",
"com_ui_error_connection": "Error connecting to server, try refreshing the page.", "com_ui_error_connection": "Error connecting to server, try refreshing the page.",
"com_ui_error_save_admin_settings": "There was an error saving your admin settings.", "com_ui_error_save_admin_settings": "There was an error saving your admin settings.",

View file

@ -188,7 +188,12 @@ export default [
}, },
}, },
{ {
files: ['client/src/**/*.tsx', 'client/src/**/*.ts', 'client/src/**/*.jsx', 'client/src/**/*.js'], files: [
'client/src/**/*.tsx',
'client/src/**/*.ts',
'client/src/**/*.jsx',
'client/src/**/*.js',
],
rules: { rules: {
// Client a11y // Client a11y
// TODO: maybe later to error. // TODO: maybe later to error.
@ -285,7 +290,6 @@ export default [
// General // General
'no-constant-binary-expression': 'off', 'no-constant-binary-expression': 'off',
'import/no-cycle': 'off', 'import/no-cycle': 'off',
'no-nested-ternary': 'off',
}, },
}, },
{ {