⬆️ feat: Cancel chat file uploads; fix: Assistant uploads (#4433)

* refactor: move file mutations to dedicated file, improve typing

* refactor(ChatForm): utilize FileFormWrapper to consolidate file upload logic/rendering to single parent

* refactor: better TSX heirarchies between AttachFile and FileFormWrapper

* refactor: `abortUpload` WIP

* fix: file debugging and file upload issues

* refactor: reject promise outright if axios intercepted error does not include response property

* chore: bump data-provider version to 0.7.428

* refactor: Add return type to localize function in Translation.ts

* refactor: allow message file attachment upload request cancellations, and add localizations for file upload errors

* refactor: include Azure OpenAI in paramEndpoints set

* fix: assistant form uploads and better typing

* refactor: consolidate logic
This commit is contained in:
Danny Avila 2024-10-16 11:24:40 -04:00 committed by GitHub
parent 0870acd086
commit 65888c274a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 419 additions and 311 deletions

View file

@ -8,9 +8,9 @@ import {
} from 'librechat-data-provider';
import {
useChatContext,
useChatFormContext,
useAddedChatContext,
useAssistantsMapContext,
useChatFormContext,
} from '~/Providers';
import {
useTextarea,
@ -20,18 +20,17 @@ import {
useQueryParams,
useSubmitMessage,
} from '~/hooks';
import FileFormWrapper from './Files/FileFormWrapper';
import { TextareaAutosize } from '~/components/ui';
import { useGetFileConfig } from '~/data-provider';
import { cn, removeFocusRings } from '~/utils';
import TextareaHeader from './TextareaHeader';
import PromptsCommand from './PromptsCommand';
import AttachFile from './Files/AttachFile';
import AudioRecorder from './AudioRecorder';
import { mainTextareaId } from '~/common';
import StreamAudio from './StreamAudio';
import StopButton from './StopButton';
import SendButton from './SendButton';
import FileRow from './Files/FileRow';
import Mention from './Mention';
import store from '~/store';
@ -73,7 +72,6 @@ const ChatForm = ({ index = 0 }) => {
conversation,
isSubmitting,
filesLoading,
setFilesLoading,
newConversation,
handleStopGenerating,
} = useChatContext();
@ -130,6 +128,9 @@ const ChatForm = ({ index = 0 }) => {
}
}, [isSearching, disableInputs]);
const endpointSupportsFiles: boolean = supportsFiles[endpointType ?? endpoint ?? ''] ?? false;
const isUploadDisabled: boolean = endpointFileConfig?.disabled ?? false;
return (
<form
onSubmit={methods.handleSubmit((data) => submitMessage(data))}
@ -157,52 +158,37 @@ const ChatForm = ({ index = 0 }) => {
<PromptsCommand index={index} textAreaRef={textAreaRef} submitPrompt={submitPrompt} />
<div className="bg-token-main-surface-primary relative flex w-full flex-grow flex-col overflow-hidden rounded-2xl border dark:border-gray-600 dark:text-white [&:has(textarea:focus)]:border-gray-300 [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)] dark:[&:has(textarea:focus)]:border-gray-500">
<TextareaHeader addedConvo={addedConvo} setAddedConvo={setAddedConvo} />
<FileRow
files={files}
setFiles={setFiles}
setFilesLoading={setFilesLoading}
isRTL={isRTL}
Wrapper={({ children }) => (
<div className="mx-2 mt-2 flex flex-wrap gap-2 px-2.5 md:pl-0 md:pr-4">
{children}
</div>
<FileFormWrapper disableInputs={disableInputs}>
{endpoint && (
<TextareaAutosize
{...registerProps}
ref={(e) => {
ref(e);
textAreaRef.current = e;
}}
disabled={disableInputs}
onPaste={handlePaste}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
onCompositionStart={handleCompositionStart}
onCompositionEnd={handleCompositionEnd}
id={mainTextareaId}
tabIndex={0}
data-testid="text-input"
style={{ height: 44, overflowY: 'auto' }}
rows={1}
className={cn(
endpointSupportsFiles && !isUploadDisabled
? 'pl-10 md:pl-[55px]'
: 'pl-3 md:pl-4',
'm-0 w-full resize-none border-0 bg-transparent py-[10px] placeholder-black/50 focus:ring-0 focus-visible:ring-0 dark:bg-transparent dark:placeholder-white/50 md:py-3.5 ',
SpeechToText && !isRTL ? 'pr-20 md:pr-[85px]' : 'pr-10 md:pr-12',
'max-h-[65vh] md:max-h-[75vh]',
removeFocusRings,
)}
/>
)}
/>
{endpoint && (
<TextareaAutosize
{...registerProps}
ref={(e) => {
ref(e);
textAreaRef.current = e;
}}
disabled={disableInputs}
onPaste={handlePaste}
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
onCompositionStart={handleCompositionStart}
onCompositionEnd={handleCompositionEnd}
id={mainTextareaId}
tabIndex={0}
data-testid="text-input"
style={{ height: 44, overflowY: 'auto' }}
rows={1}
className={cn(
supportsFiles[endpointType ?? endpoint ?? ''] && !endpointFileConfig?.disabled
? ' pl-10 md:pl-[55px]'
: 'pl-3 md:pl-4',
'm-0 w-full resize-none border-0 bg-transparent py-[10px] placeholder-black/50 focus:ring-0 focus-visible:ring-0 dark:bg-transparent dark:placeholder-white/50 md:py-3.5 ',
SpeechToText && !isRTL ? 'pr-20 md:pr-[85px]' : 'pr-10 md:pr-12',
'max-h-[65vh] md:max-h-[75vh]',
removeFocusRings,
)}
/>
)}
<AttachFile
endpoint={_endpoint ?? ''}
endpointType={endpointType}
isRTL={isRTL}
disabled={disableInputs}
/>
</FileFormWrapper>
{(isSubmitting || isSubmittingAdded) && (showStopButton || showStopAdded) ? (
<StopButton
stop={handleStopGenerating}