mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
(feat: attach) handle them locally from message input
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDarkTheme } from '../hooks';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import userService from '../api/services/userService';
|
||||
import endpoints from '../api/endpoints';
|
||||
import PaperPlane from '../assets/paper_plane.svg';
|
||||
@@ -15,14 +15,13 @@ import { selectSelectedDocs, selectToken } from '../preferences/preferenceSlice'
|
||||
import { ActiveState } from '../models/misc';
|
||||
import Upload from '../upload/Upload';
|
||||
import ClipIcon from '../assets/clip.svg';
|
||||
|
||||
import { setAttachments } from '../conversation/conversationSlice';
|
||||
|
||||
interface MessageInputProps {
|
||||
value: string;
|
||||
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
|
||||
onSubmit: () => void;
|
||||
loading: boolean;
|
||||
onAttachmentChange?: (attachments: { fileName: string; id: string }[]) => void;
|
||||
}
|
||||
|
||||
interface UploadState {
|
||||
@@ -39,7 +38,6 @@ export default function MessageInput({
|
||||
onChange,
|
||||
onSubmit,
|
||||
loading,
|
||||
onAttachmentChange,
|
||||
}: MessageInputProps) {
|
||||
const { t } = useTranslation();
|
||||
const [isDarkTheme] = useDarkTheme();
|
||||
@@ -53,6 +51,8 @@ export default function MessageInput({
|
||||
|
||||
const selectedDocs = useSelector(selectSelectedDocs);
|
||||
const token = useSelector(selectToken);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleFileAttachment = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (!e.target.files || e.target.files.length === 0) return;
|
||||
@@ -213,21 +213,18 @@ export default function MessageInput({
|
||||
console.log('Selected document:', doc);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (onAttachmentChange) {
|
||||
const completedAttachments = uploads
|
||||
.filter(upload => upload.status === 'completed' && upload.attachment_id)
|
||||
.map(upload => ({
|
||||
fileName: upload.fileName,
|
||||
id: upload.attachment_id as string
|
||||
}));
|
||||
onAttachmentChange(completedAttachments);
|
||||
}
|
||||
}, [uploads, onAttachmentChange]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
const completedAttachments = uploads
|
||||
.filter(upload => upload.status === 'completed' && upload.attachment_id)
|
||||
.map(upload => ({
|
||||
fileName: upload.fileName,
|
||||
id: upload.attachment_id as string
|
||||
}));
|
||||
|
||||
dispatch(setAttachments(completedAttachments));
|
||||
|
||||
onSubmit();
|
||||
setUploads(prevUploads => prevUploads.filter(upload => upload.status !== 'completed'));
|
||||
};
|
||||
return (
|
||||
<div className="flex flex-col w-full mx-2">
|
||||
|
||||
@@ -29,11 +29,6 @@ import { ActiveState } from '../models/misc';
|
||||
import ConversationMessages from './ConversationMessages';
|
||||
import MessageInput from '../components/MessageInput';
|
||||
|
||||
interface AttachmentState {
|
||||
fileName: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export default function Conversation() {
|
||||
const token = useSelector(selectToken);
|
||||
const queries = useSelector(selectQueries);
|
||||
@@ -50,7 +45,6 @@ export default function Conversation() {
|
||||
useState<ActiveState>('INACTIVE');
|
||||
const [files, setFiles] = useState<File[]>([]);
|
||||
const [handleDragActive, setHandleDragActive] = useState<boolean>(false);
|
||||
const [attachments, setAttachments] = useState<AttachmentState[]>([]);
|
||||
|
||||
const onDrop = useCallback((acceptedFiles: File[]) => {
|
||||
setUploadModalState('ACTIVE');
|
||||
@@ -101,13 +95,11 @@ export default function Conversation() {
|
||||
isRetry = false,
|
||||
updated = null,
|
||||
indx = undefined,
|
||||
attachments = [],
|
||||
}: {
|
||||
question: string;
|
||||
isRetry?: boolean;
|
||||
updated?: boolean | null;
|
||||
indx?: number;
|
||||
attachments?: { fileName: string; id: string }[];
|
||||
}) => {
|
||||
if (updated === true) {
|
||||
!isRetry &&
|
||||
@@ -116,11 +108,8 @@ export default function Conversation() {
|
||||
} else {
|
||||
question = question.trim();
|
||||
if (question === '') return;
|
||||
!isRetry && dispatch(addQuery({ prompt: question, attachments }));
|
||||
fetchStream.current = dispatch(fetchAnswer({
|
||||
question,
|
||||
attachments: attachments.map(a => a.id)
|
||||
}));
|
||||
!isRetry && dispatch(addQuery({ prompt: question }));
|
||||
fetchStream.current = dispatch(fetchAnswer({ question }));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -172,10 +161,8 @@ export default function Conversation() {
|
||||
});
|
||||
} else {
|
||||
handleQuestion({
|
||||
question: input,
|
||||
attachments: attachments
|
||||
question: input,
|
||||
});
|
||||
setAttachments([]);
|
||||
}
|
||||
setInput('');
|
||||
}
|
||||
@@ -260,7 +247,6 @@ export default function Conversation() {
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onSubmit={handleQuestionSubmission}
|
||||
loading={status === 'loading'}
|
||||
onAttachmentChange={setAttachments}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface ConversationState {
|
||||
queries: Query[];
|
||||
status: Status;
|
||||
conversationId: string | null;
|
||||
attachments?: { fileName: string; id: string }[];
|
||||
}
|
||||
|
||||
export interface Answer {
|
||||
|
||||
@@ -13,6 +13,7 @@ const initialState: ConversationState = {
|
||||
queries: [],
|
||||
status: 'idle',
|
||||
conversationId: null,
|
||||
attachments: [],
|
||||
};
|
||||
|
||||
const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true';
|
||||
@@ -27,8 +28,8 @@ export function handleAbort() {
|
||||
|
||||
export const fetchAnswer = createAsyncThunk<
|
||||
Answer,
|
||||
{ question: string; indx?: number; attachments?: string[] }
|
||||
>('fetchAnswer', async ({ question, indx, attachments }, { dispatch, getState }) => {
|
||||
{ question: string; indx?: number }
|
||||
>('fetchAnswer', async ({ question, indx }, { dispatch, getState }) => {
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
}
|
||||
@@ -37,6 +38,8 @@ export const fetchAnswer = createAsyncThunk<
|
||||
|
||||
let isSourceUpdated = false;
|
||||
const state = getState() as RootState;
|
||||
const attachments = state.conversation.attachments?.map(a => a.id) || [];
|
||||
|
||||
if (state.preference) {
|
||||
if (API_STREAMING) {
|
||||
await handleFetchAnswerSteaming(
|
||||
@@ -257,6 +260,9 @@ export const conversationSlice = createSlice({
|
||||
const { index, message } = action.payload;
|
||||
state.queries[index].error = message;
|
||||
},
|
||||
setAttachments: (state, action: PayloadAction<{ fileName: string; id: string }[]>) => {
|
||||
state.attachments = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers(builder) {
|
||||
builder
|
||||
@@ -289,5 +295,6 @@ export const {
|
||||
updateStreamingSource,
|
||||
updateToolCalls,
|
||||
setConversation,
|
||||
setAttachments,
|
||||
} = conversationSlice.actions;
|
||||
export default conversationSlice.reducer;
|
||||
|
||||
Reference in New Issue
Block a user