mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: Upload files in the chat box on the agent page #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -1,3 +1,5 @@
|
|||||||
|
// https://www.diceui.com/docs/components/file-upload
|
||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|||||||
171
web/src/components/message-input/next.tsx
Normal file
171
web/src/components/message-input/next.tsx
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FileUpload,
|
||||||
|
FileUploadDropzone,
|
||||||
|
FileUploadItem,
|
||||||
|
FileUploadItemDelete,
|
||||||
|
FileUploadItemMetadata,
|
||||||
|
FileUploadItemPreview,
|
||||||
|
FileUploadItemProgress,
|
||||||
|
FileUploadList,
|
||||||
|
FileUploadTrigger,
|
||||||
|
type FileUploadProps,
|
||||||
|
} from '@/components/file-upload';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
|
import { CircleStop, Paperclip, Send, Upload, X } from 'lucide-react';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
disabled: boolean;
|
||||||
|
value: string;
|
||||||
|
sendDisabled: boolean;
|
||||||
|
sendLoading: boolean;
|
||||||
|
conversationId: string;
|
||||||
|
uploadMethod?: string;
|
||||||
|
isShared?: boolean;
|
||||||
|
showUploadIcon?: boolean;
|
||||||
|
isUploading?: boolean;
|
||||||
|
onPressEnter(...prams: any[]): void;
|
||||||
|
onInputChange: React.ChangeEventHandler<HTMLTextAreaElement>;
|
||||||
|
createConversationBeforeUploadDocument?(message: string): Promise<any>;
|
||||||
|
stopOutputMessage?(): void;
|
||||||
|
onUpload?: NonNullable<FileUploadProps['onUpload']>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NextMessageInput({
|
||||||
|
isUploading = false,
|
||||||
|
value,
|
||||||
|
sendDisabled,
|
||||||
|
sendLoading,
|
||||||
|
disabled,
|
||||||
|
showUploadIcon = true,
|
||||||
|
onUpload,
|
||||||
|
onInputChange,
|
||||||
|
stopOutputMessage,
|
||||||
|
onPressEnter,
|
||||||
|
}: IProps) {
|
||||||
|
const [files, setFiles] = React.useState<File[]>([]);
|
||||||
|
|
||||||
|
const onFileReject = React.useCallback((file: File, message: string) => {
|
||||||
|
toast(message, {
|
||||||
|
description: `"${file.name.length > 20 ? `${file.name.slice(0, 20)}...` : file.name}" has been rejected`,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const submit = React.useCallback(() => {
|
||||||
|
if (isUploading) return;
|
||||||
|
onPressEnter();
|
||||||
|
setFiles([]);
|
||||||
|
}, [isUploading, onPressEnter]);
|
||||||
|
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
submit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSubmit = React.useCallback(
|
||||||
|
(event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
submit();
|
||||||
|
},
|
||||||
|
[submit],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FileUpload
|
||||||
|
value={files}
|
||||||
|
onValueChange={setFiles}
|
||||||
|
onUpload={onUpload}
|
||||||
|
onFileReject={onFileReject}
|
||||||
|
className="relative w-full items-center "
|
||||||
|
disabled={isUploading || disabled}
|
||||||
|
>
|
||||||
|
<FileUploadDropzone
|
||||||
|
tabIndex={-1}
|
||||||
|
// Prevents the dropzone from triggering on click
|
||||||
|
onClick={(event) => event.preventDefault()}
|
||||||
|
className="absolute top-0 left-0 z-0 flex size-full items-center justify-center rounded-none border-none bg-background/50 p-0 opacity-0 backdrop-blur transition-opacity duration-200 ease-out data-[dragging]:z-10 data-[dragging]:opacity-100"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center gap-1 text-center">
|
||||||
|
<div className="flex items-center justify-center rounded-full border p-2.5">
|
||||||
|
<Upload className="size-6 text-muted-foreground" />
|
||||||
|
</div>
|
||||||
|
<p className="font-medium text-sm">Drag & drop files here</p>
|
||||||
|
<p className="text-muted-foreground text-xs">
|
||||||
|
Upload max 5 files each up to 5MB
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</FileUploadDropzone>
|
||||||
|
<form
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
className="relative flex w-full max-w-md flex-col gap-2.5 rounded-md border border-input px-3 py-2 outline-none focus-within:ring-1 focus-within:ring-ring/50"
|
||||||
|
>
|
||||||
|
<FileUploadList
|
||||||
|
orientation="horizontal"
|
||||||
|
className="overflow-x-auto px-0 py-1"
|
||||||
|
>
|
||||||
|
{files.map((file, index) => (
|
||||||
|
<FileUploadItem key={index} value={file} className="max-w-52 p-1.5">
|
||||||
|
<FileUploadItemPreview className="size-8 [&>svg]:size-5">
|
||||||
|
<FileUploadItemProgress variant="fill" />
|
||||||
|
</FileUploadItemPreview>
|
||||||
|
<FileUploadItemMetadata size="sm" />
|
||||||
|
<FileUploadItemDelete asChild>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="icon"
|
||||||
|
className="-top-1 -right-1 absolute size-4 shrink-0 cursor-pointer rounded-full"
|
||||||
|
>
|
||||||
|
<X className="size-2.5" />
|
||||||
|
</Button>
|
||||||
|
</FileUploadItemDelete>
|
||||||
|
</FileUploadItem>
|
||||||
|
))}
|
||||||
|
</FileUploadList>
|
||||||
|
<Textarea
|
||||||
|
value={value}
|
||||||
|
onChange={onInputChange}
|
||||||
|
placeholder="Type your message here..."
|
||||||
|
className="field-sizing-content min-h-10 w-full resize-none border-0 bg-transparent p-0 shadow-none focus-visible:ring-0 dark:bg-transparent"
|
||||||
|
disabled={isUploading || disabled}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
<div className="flex items-center justify-between gap-1.5">
|
||||||
|
{showUploadIcon && (
|
||||||
|
<FileUploadTrigger asChild>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
size="icon"
|
||||||
|
variant="ghost"
|
||||||
|
className="size-7 rounded-sm"
|
||||||
|
>
|
||||||
|
<Paperclip className="size-3.5" />
|
||||||
|
<span className="sr-only">Attach file</span>
|
||||||
|
</Button>
|
||||||
|
</FileUploadTrigger>
|
||||||
|
)}
|
||||||
|
{sendLoading ? (
|
||||||
|
<Button onClick={stopOutputMessage} className="size-5 rounded-sm">
|
||||||
|
<CircleStop />
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
className="size-5 rounded-sm"
|
||||||
|
disabled={
|
||||||
|
sendDisabled || isUploading || sendLoading || !value.trim()
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Send />
|
||||||
|
<span className="sr-only">Send message</span>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</FileUpload>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { FileUploadProps } from '@/components/file-upload';
|
||||||
import message from '@/components/ui/message';
|
import message from '@/components/ui/message';
|
||||||
import { AgentGlobals } from '@/constants/agent';
|
import { AgentGlobals } from '@/constants/agent';
|
||||||
import { ITraceData } from '@/interfaces/database/agent';
|
import { ITraceData } from '@/interfaces/database/agent';
|
||||||
@ -7,6 +8,7 @@ import i18n from '@/locales/config';
|
|||||||
import { BeginId } from '@/pages/agent/constant';
|
import { BeginId } from '@/pages/agent/constant';
|
||||||
import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks';
|
import { useGetSharedChatSearchParams } from '@/pages/chat/shared-hooks';
|
||||||
import agentService from '@/services/agent-service';
|
import agentService from '@/services/agent-service';
|
||||||
|
import api from '@/utils/api';
|
||||||
import { buildMessageListWithUuid } from '@/utils/chat';
|
import { buildMessageListWithUuid } from '@/utils/chat';
|
||||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useDebounce } from 'ahooks';
|
import { useDebounce } from 'ahooks';
|
||||||
@ -29,6 +31,7 @@ export const enum AgentApiAction {
|
|||||||
SetAgent = 'setAgent',
|
SetAgent = 'setAgent',
|
||||||
FetchAgentTemplates = 'fetchAgentTemplates',
|
FetchAgentTemplates = 'fetchAgentTemplates',
|
||||||
UploadCanvasFile = 'uploadCanvasFile',
|
UploadCanvasFile = 'uploadCanvasFile',
|
||||||
|
UploadCanvasFileWithProgress = 'uploadCanvasFileWithProgress',
|
||||||
Trace = 'trace',
|
Trace = 'trace',
|
||||||
TestDbConnect = 'testDbConnect',
|
TestDbConnect = 'testDbConnect',
|
||||||
DebugSingle = 'debugSingle',
|
DebugSingle = 'debugSingle',
|
||||||
@ -284,7 +287,9 @@ export const useSetAgent = () => {
|
|||||||
return { data, loading, setAgent: mutateAsync };
|
return { data, loading, setAgent: mutateAsync };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only one file can be uploaded at a time
|
||||||
export const useUploadCanvasFile = () => {
|
export const useUploadCanvasFile = () => {
|
||||||
|
const { id } = useParams();
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
isPending: loading,
|
isPending: loading,
|
||||||
@ -301,7 +306,10 @@ export const useUploadCanvasFile = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await agentService.uploadCanvasFile(nextBody);
|
const { data } = await agentService.uploadCanvasFile(
|
||||||
|
{ url: api.uploadAgentFile(id), data: nextBody },
|
||||||
|
true,
|
||||||
|
);
|
||||||
if (data?.code === 0) {
|
if (data?.code === 0) {
|
||||||
message.success(i18n.t('message.uploaded'));
|
message.success(i18n.t('message.uploaded'));
|
||||||
}
|
}
|
||||||
@ -315,6 +323,73 @@ export const useUploadCanvasFile = () => {
|
|||||||
return { data, loading, uploadCanvasFile: mutateAsync };
|
return { data, loading, uploadCanvasFile: mutateAsync };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useUploadCanvasFileWithProgress = (
|
||||||
|
identifier?: Nullable<string>,
|
||||||
|
) => {
|
||||||
|
const { id } = useParams();
|
||||||
|
|
||||||
|
type UploadParameters = Parameters<NonNullable<FileUploadProps['onUpload']>>;
|
||||||
|
|
||||||
|
type X = { files: UploadParameters[0]; options: UploadParameters[1] };
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: [AgentApiAction.UploadCanvasFileWithProgress],
|
||||||
|
mutationFn: async ({
|
||||||
|
files,
|
||||||
|
options: { onError, onSuccess, onProgress },
|
||||||
|
}: X) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
try {
|
||||||
|
if (Array.isArray(files)) {
|
||||||
|
files.forEach((file: File) => {
|
||||||
|
formData.append('file', file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await agentService.uploadCanvasFile(
|
||||||
|
{
|
||||||
|
url: api.uploadAgentFile(identifier || id),
|
||||||
|
data: formData,
|
||||||
|
onUploadProgress: ({
|
||||||
|
loaded,
|
||||||
|
total,
|
||||||
|
progress,
|
||||||
|
bytes,
|
||||||
|
estimated,
|
||||||
|
rate,
|
||||||
|
upload,
|
||||||
|
lengthComputable,
|
||||||
|
}) => {
|
||||||
|
files.forEach((file) => {
|
||||||
|
onProgress(file, (progress || 0) * 100);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
if (data?.code === 0) {
|
||||||
|
files.forEach((file) => {
|
||||||
|
onSuccess(file);
|
||||||
|
});
|
||||||
|
message.success(i18n.t('message.uploaded'));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
files.forEach((file) => {
|
||||||
|
onError(file, error as Error);
|
||||||
|
});
|
||||||
|
message.error('error', error.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, uploadCanvasFile: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
export const useFetchMessageTrace = () => {
|
export const useFetchMessageTrace = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [messageId, setMessageId] = useState('');
|
const [messageId, setMessageId] = useState('');
|
||||||
|
|||||||
@ -3,11 +3,15 @@ import { useGetFileIcon } from '@/pages/chat/hooks';
|
|||||||
|
|
||||||
import { useSendAgentMessage } from './use-send-agent-message';
|
import { useSendAgentMessage } from './use-send-agent-message';
|
||||||
|
|
||||||
import MessageInput from '@/components/message-input';
|
import { FileUploadProps } from '@/components/file-upload';
|
||||||
|
import { NextMessageInput } from '@/components/message-input/next';
|
||||||
import MessageItem from '@/components/next-message-item';
|
import MessageItem from '@/components/next-message-item';
|
||||||
import PdfDrawer from '@/components/pdf-drawer';
|
import PdfDrawer from '@/components/pdf-drawer';
|
||||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||||
import { useFetchAgent } from '@/hooks/use-agent-request';
|
import {
|
||||||
|
useFetchAgent,
|
||||||
|
useUploadCanvasFileWithProgress,
|
||||||
|
} from '@/hooks/use-agent-request';
|
||||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||||
import { Message } from '@/interfaces/database/chat';
|
import { Message } from '@/interfaces/database/chat';
|
||||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||||
@ -20,15 +24,16 @@ import { buildBeginQueryWithObject } from '../utils';
|
|||||||
|
|
||||||
const AgentChatBox = () => {
|
const AgentChatBox = () => {
|
||||||
const {
|
const {
|
||||||
sendLoading,
|
|
||||||
handleInputChange,
|
|
||||||
handlePressEnter,
|
|
||||||
value,
|
value,
|
||||||
ref,
|
ref,
|
||||||
|
sendLoading,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
|
handleInputChange,
|
||||||
|
handlePressEnter,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
sendFormMessage,
|
sendFormMessage,
|
||||||
findReferenceByMessageId,
|
findReferenceByMessageId,
|
||||||
|
appendUploadResponseList,
|
||||||
} = useSendAgentMessage();
|
} = useSendAgentMessage();
|
||||||
|
|
||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
@ -37,6 +42,7 @@ const AgentChatBox = () => {
|
|||||||
const { data: userInfo } = useFetchUserInfo();
|
const { data: userInfo } = useFetchUserInfo();
|
||||||
const { data: canvasInfo } = useFetchAgent();
|
const { data: canvasInfo } = useFetchAgent();
|
||||||
const { id: canvasId } = useParams();
|
const { id: canvasId } = useParams();
|
||||||
|
const { uploadCanvasFile, loading } = useUploadCanvasFileWithProgress();
|
||||||
|
|
||||||
const getInputs = useCallback((message: Message) => {
|
const getInputs = useCallback((message: Message) => {
|
||||||
return get(message, 'data.inputs', {}) as Record<string, BeginQuery>;
|
return get(message, 'data.inputs', {}) as Record<string, BeginQuery>;
|
||||||
@ -66,6 +72,15 @@ const AgentChatBox = () => {
|
|||||||
[canvasId, getInputs, sendFormMessage],
|
[canvasId, getInputs, sendFormMessage],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||||
|
useCallback(
|
||||||
|
async (files, options) => {
|
||||||
|
const ret = await uploadCanvasFile({ files, options });
|
||||||
|
appendUploadResponseList(ret.data);
|
||||||
|
},
|
||||||
|
[appendUploadResponseList, uploadCanvasFile],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className="flex flex-1 flex-col px-5 h-[90vh]">
|
<section className="flex flex-1 flex-col px-5 h-[90vh]">
|
||||||
@ -104,15 +119,17 @@ const AgentChatBox = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div ref={ref} />
|
<div ref={ref} />
|
||||||
</div>
|
</div>
|
||||||
<MessageInput
|
<NextMessageInput
|
||||||
value={value}
|
value={value}
|
||||||
sendLoading={sendLoading}
|
sendLoading={sendLoading}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
sendDisabled={sendLoading}
|
sendDisabled={sendLoading}
|
||||||
conversationId=""
|
isUploading={loading}
|
||||||
onPressEnter={handlePressEnter}
|
onPressEnter={handlePressEnter}
|
||||||
onInputChange={handleInputChange}
|
onInputChange={handleInputChange}
|
||||||
stopOutputMessage={stopOutputMessage}
|
stopOutputMessage={stopOutputMessage}
|
||||||
|
onUpload={handleUploadFile}
|
||||||
|
conversationId=""
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
<PdfDrawer
|
<PdfDrawer
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Sheet, SheetContent } from '@/components/ui/sheet';
|
import { Sheet, SheetContent, SheetTitle } from '@/components/ui/sheet';
|
||||||
import { IModalProps } from '@/interfaces/common';
|
import { IModalProps } from '@/interfaces/common';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@ -12,6 +12,7 @@ export function ChatSheet({ hideModal }: IModalProps<any>) {
|
|||||||
className={cn('top-20 p-0')}
|
className={cn('top-20 p-0')}
|
||||||
onInteractOutside={(e) => e.preventDefault()}
|
onInteractOutside={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
|
<SheetTitle className="hidden"></SheetTitle>
|
||||||
<div className="pl-5 pt-2">{t('chat.chat')}</div>
|
<div className="pl-5 pt-2">{t('chat.chat')}</div>
|
||||||
<AgentChatBox></AgentChatBox>
|
<AgentChatBox></AgentChatBox>
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
|
|||||||
@ -137,6 +137,38 @@ export function useFindMessageReference(answerList: IEventList) {
|
|||||||
return { findReferenceByMessageId };
|
return { findReferenceByMessageId };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UploadResponseDataType {
|
||||||
|
created_at: number;
|
||||||
|
created_by: string;
|
||||||
|
extension: string;
|
||||||
|
id: string;
|
||||||
|
mime_type: string;
|
||||||
|
name: string;
|
||||||
|
preview_url: null;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSetUploadResponseData() {
|
||||||
|
const [uploadResponseList, setUploadResponseList] = useState<
|
||||||
|
UploadResponseDataType[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const append = useCallback((data: UploadResponseDataType) => {
|
||||||
|
setUploadResponseList((prev) => [...prev, data]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const clear = useCallback(() => {
|
||||||
|
setUploadResponseList([]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
uploadResponseList,
|
||||||
|
setUploadResponseList,
|
||||||
|
appendUploadResponseList: append,
|
||||||
|
clearUploadResponseList: clear,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const useSendAgentMessage = (url?: string) => {
|
export const useSendAgentMessage = (url?: string) => {
|
||||||
const { id: agentId } = useParams();
|
const { id: agentId } = useParams();
|
||||||
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||||
@ -155,6 +187,11 @@ export const useSendAgentMessage = (url?: string) => {
|
|||||||
addNewestOneAnswer,
|
addNewestOneAnswer,
|
||||||
} = useSelectDerivedMessages();
|
} = useSelectDerivedMessages();
|
||||||
const { addEventList } = useContext(AgentChatLogContext);
|
const { addEventList } = useContext(AgentChatLogContext);
|
||||||
|
const {
|
||||||
|
appendUploadResponseList,
|
||||||
|
clearUploadResponseList,
|
||||||
|
uploadResponseList,
|
||||||
|
} = useSetUploadResponseData();
|
||||||
|
|
||||||
const sendMessage = useCallback(
|
const sendMessage = useCallback(
|
||||||
async ({ message }: { message: Message; messages?: Message[] }) => {
|
async ({ message }: { message: Message; messages?: Message[] }) => {
|
||||||
@ -171,9 +208,13 @@ export const useSendAgentMessage = (url?: string) => {
|
|||||||
params.query = message.content;
|
params.query = message.content;
|
||||||
// params.message_id = message.id;
|
// params.message_id = message.id;
|
||||||
params.inputs = transferInputsArrayToObject(query); // begin operator inputs
|
params.inputs = transferInputsArrayToObject(query); // begin operator inputs
|
||||||
|
|
||||||
|
params.files = uploadResponseList;
|
||||||
}
|
}
|
||||||
const res = await send(params);
|
const res = await send(params);
|
||||||
|
|
||||||
|
clearUploadResponseList();
|
||||||
|
|
||||||
if (receiveMessageError(res)) {
|
if (receiveMessageError(res)) {
|
||||||
sonnerMessage.error(res?.data?.message);
|
sonnerMessage.error(res?.data?.message);
|
||||||
|
|
||||||
@ -184,7 +225,15 @@ export const useSendAgentMessage = (url?: string) => {
|
|||||||
// refetch(); // pull the message list after sending the message successfully
|
// refetch(); // pull the message list after sending the message successfully
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[agentId, send, inputs, setValue, removeLatestMessage],
|
[
|
||||||
|
agentId,
|
||||||
|
send,
|
||||||
|
inputs,
|
||||||
|
uploadResponseList,
|
||||||
|
setValue,
|
||||||
|
removeLatestMessage,
|
||||||
|
clearUploadResponseList,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const sendFormMessage = useCallback(
|
const sendFormMessage = useCallback(
|
||||||
@ -243,16 +292,17 @@ export const useSendAgentMessage = (url?: string) => {
|
|||||||
}, [addEventList, answerList]);
|
}, [addEventList, answerList]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handlePressEnter,
|
|
||||||
handleInputChange,
|
|
||||||
value,
|
value,
|
||||||
sendLoading: !done,
|
sendLoading: !done,
|
||||||
derivedMessages,
|
derivedMessages,
|
||||||
ref,
|
ref,
|
||||||
|
handlePressEnter,
|
||||||
|
handleInputChange,
|
||||||
removeMessageById,
|
removeMessageById,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
send,
|
send,
|
||||||
sendFormMessage,
|
sendFormMessage,
|
||||||
findReferenceByMessageId,
|
findReferenceByMessageId,
|
||||||
|
appendUploadResponseList,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -150,7 +150,8 @@ function AgentForm({ node }: INextOperatorForm) {
|
|||||||
<FormContainer>
|
<FormContainer>
|
||||||
<QueryVariable
|
<QueryVariable
|
||||||
name="visual_files_var"
|
name="visual_files_var"
|
||||||
label="Visual files var"
|
label="Visual Input File"
|
||||||
|
type={VariableType.File}
|
||||||
></QueryVariable>
|
></QueryVariable>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
|||||||
@ -1,15 +1,19 @@
|
|||||||
import MessageInput from '@/components/message-input';
|
import { FileUploadProps } from '@/components/file-upload';
|
||||||
|
import { NextMessageInput } from '@/components/message-input/next';
|
||||||
import MessageItem from '@/components/next-message-item';
|
import MessageItem from '@/components/next-message-item';
|
||||||
import PdfDrawer from '@/components/pdf-drawer';
|
import PdfDrawer from '@/components/pdf-drawer';
|
||||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||||
import { MessageType, SharedFrom } from '@/constants/chat';
|
import { MessageType, SharedFrom } from '@/constants/chat';
|
||||||
import { useFetchNextConversationSSE } from '@/hooks/chat-hooks';
|
import { useFetchNextConversationSSE } from '@/hooks/chat-hooks';
|
||||||
import { useFetchAgentAvatar } from '@/hooks/use-agent-request';
|
import {
|
||||||
|
useFetchAgentAvatar,
|
||||||
|
useUploadCanvasFileWithProgress,
|
||||||
|
} from '@/hooks/use-agent-request';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import i18n from '@/locales/config';
|
import i18n from '@/locales/config';
|
||||||
import { useSendButtonDisabled } from '@/pages/chat/hooks';
|
import { useSendButtonDisabled } from '@/pages/chat/hooks';
|
||||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||||
import React, { forwardRef, useMemo } from 'react';
|
import React, { forwardRef, useCallback, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
useGetSharedChatSearchParams,
|
useGetSharedChatSearchParams,
|
||||||
useSendNextSharedMessage,
|
useSendNextSharedMessage,
|
||||||
@ -25,6 +29,9 @@ const ChatContainer = () => {
|
|||||||
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
|
||||||
useClickDrawer();
|
useClickDrawer();
|
||||||
|
|
||||||
|
const { uploadCanvasFile, loading } =
|
||||||
|
useUploadCanvasFileWithProgress(conversationId);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
handlePressEnter,
|
handlePressEnter,
|
||||||
handleInputChange,
|
handleInputChange,
|
||||||
@ -35,6 +42,7 @@ const ChatContainer = () => {
|
|||||||
hasError,
|
hasError,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
findReferenceByMessageId,
|
findReferenceByMessageId,
|
||||||
|
appendUploadResponseList,
|
||||||
} = useSendNextSharedMessage();
|
} = useSendNextSharedMessage();
|
||||||
const sendDisabled = useSendButtonDisabled(value);
|
const sendDisabled = useSendButtonDisabled(value);
|
||||||
|
|
||||||
@ -44,6 +52,15 @@ const ChatContainer = () => {
|
|||||||
: useFetchNextConversationSSE;
|
: useFetchNextConversationSSE;
|
||||||
}, [from]);
|
}, [from]);
|
||||||
|
|
||||||
|
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||||
|
useCallback(
|
||||||
|
async (files, options) => {
|
||||||
|
const ret = await uploadCanvasFile({ files, options });
|
||||||
|
appendUploadResponseList(ret.data);
|
||||||
|
},
|
||||||
|
[appendUploadResponseList, uploadCanvasFile],
|
||||||
|
);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (locale && i18n.language !== locale) {
|
if (locale && i18n.language !== locale) {
|
||||||
i18n.changeLanguage(locale);
|
i18n.changeLanguage(locale);
|
||||||
@ -79,6 +96,7 @@ const ChatContainer = () => {
|
|||||||
showLikeButton={false}
|
showLikeButton={false}
|
||||||
showLoudspeaker={false}
|
showLoudspeaker={false}
|
||||||
showLog={false}
|
showLog={false}
|
||||||
|
sendLoading={sendLoading}
|
||||||
></MessageItem>
|
></MessageItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -86,7 +104,7 @@ const ChatContainer = () => {
|
|||||||
<div ref={ref} />
|
<div ref={ref} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MessageInput
|
<NextMessageInput
|
||||||
isShared
|
isShared
|
||||||
value={value}
|
value={value}
|
||||||
disabled={hasError}
|
disabled={hasError}
|
||||||
@ -95,10 +113,10 @@ const ChatContainer = () => {
|
|||||||
onInputChange={handleInputChange}
|
onInputChange={handleInputChange}
|
||||||
onPressEnter={handlePressEnter}
|
onPressEnter={handlePressEnter}
|
||||||
sendLoading={sendLoading}
|
sendLoading={sendLoading}
|
||||||
uploadMethod="external_upload_and_parse"
|
|
||||||
showUploadIcon={false}
|
|
||||||
stopOutputMessage={stopOutputMessage}
|
stopOutputMessage={stopOutputMessage}
|
||||||
></MessageInput>
|
onUpload={handleUploadFile}
|
||||||
|
isUploading={loading}
|
||||||
|
></NextMessageInput>
|
||||||
</section>
|
</section>
|
||||||
{visible && (
|
{visible && (
|
||||||
<PdfDrawer
|
<PdfDrawer
|
||||||
|
|||||||
@ -152,6 +152,7 @@ export default {
|
|||||||
fetchVersion: (id: string) => `${api_host}/canvas/getversion/${id}`,
|
fetchVersion: (id: string) => `${api_host}/canvas/getversion/${id}`,
|
||||||
fetchCanvas: (id: string) => `${api_host}/canvas/get/${id}`,
|
fetchCanvas: (id: string) => `${api_host}/canvas/get/${id}`,
|
||||||
fetchAgentAvatar: (id: string) => `${api_host}/canvas/getsse/${id}`,
|
fetchAgentAvatar: (id: string) => `${api_host}/canvas/getsse/${id}`,
|
||||||
|
uploadAgentFile: (id?: string) => `${api_host}/canvas/upload/${id}`,
|
||||||
|
|
||||||
// mcp server
|
// mcp server
|
||||||
listMcpServer: `${api_host}/mcp_server/list`,
|
listMcpServer: `${api_host}/mcp_server/list`,
|
||||||
|
|||||||
Reference in New Issue
Block a user