mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-22 06:00:56 +02:00
🔧 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:
parent
a2fc7d312a
commit
f9d40784f0
10 changed files with 95 additions and 52 deletions
9
.github/workflows/deploy-dev.yml
vendored
9
.github/workflows/deploy-dev.yml
vendored
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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,19 +148,33 @@ export function ExternalTTS({ isLast, index, messageId, content, className }: TM
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
{renderButton ? (
|
||||||
className={className}
|
renderButton({
|
||||||
onClickCapture={() => {
|
onClick: () => {
|
||||||
if (audioRef.current) {
|
if (audioRef.current) {
|
||||||
audioRef.current.muted = false;
|
audioRef.current.muted = false;
|
||||||
}
|
}
|
||||||
toggleSpeech();
|
toggleSpeech();
|
||||||
}}
|
},
|
||||||
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')}
|
icon: renderIcon(),
|
||||||
>
|
isActive: isSpeaking,
|
||||||
{renderIcon('19')}
|
className,
|
||||||
</button>
|
})
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClickCapture={() => {
|
||||||
|
if (audioRef.current) {
|
||||||
|
audioRef.current.muted = false;
|
||||||
|
}
|
||||||
|
toggleSpeech();
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
title={isSpeaking === true ? localize('com_ui_stop') : localize('com_ui_read_aloud')}
|
||||||
|
>
|
||||||
|
{renderIcon()}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
<audio
|
<audio
|
||||||
ref={audioRef}
|
ref={audioRef}
|
||||||
controls
|
controls
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)}
|
||||||
|
|
|
@ -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}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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.",
|
||||||
|
|
|
@ -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',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue