mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-02-06 02:25:05 +08:00
Compare commits
5 Commits
8d8a5f73b6
...
370c8bc25b
| Author | SHA1 | Date | |
|---|---|---|---|
| 370c8bc25b | |||
| e90a959b4d | |||
| ca320a8c30 | |||
| ae505e6165 | |||
| 63b5c2292d |
@ -532,6 +532,48 @@
|
|||||||
"tags": "LLM,TEXT EMBEDDING,SPEECH2TEXT,MODERATION",
|
"tags": "LLM,TEXT EMBEDDING,SPEECH2TEXT,MODERATION",
|
||||||
"status": "1",
|
"status": "1",
|
||||||
"llm": [
|
"llm": [
|
||||||
|
{
|
||||||
|
"llm_name": "glm-4.5v",
|
||||||
|
"tags": "LLM,CHAT,IMAGE2TEXT,",
|
||||||
|
"max_tokens": 128000,
|
||||||
|
"model_type": "image2text",
|
||||||
|
"is_tools": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"llm_name": "glm-4.5",
|
||||||
|
"tags": "LLM,CHAT,128K,",
|
||||||
|
"max_tokens": 128000,
|
||||||
|
"model_type": "chat",
|
||||||
|
"is_tools": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"llm_name": "glm-4.5-x",
|
||||||
|
"tags": "LLM,CHAT,128K,",
|
||||||
|
"max_tokens": 128000,
|
||||||
|
"model_type": "chat",
|
||||||
|
"is_tools": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"llm_name": "glm-4.5-air",
|
||||||
|
"tags": "LLM,CHAT,128K,",
|
||||||
|
"max_tokens": 128000,
|
||||||
|
"model_type": "chat",
|
||||||
|
"is_tools": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"llm_name": "glm-4.5-airx",
|
||||||
|
"tags": "LLM,CHAT,128K,",
|
||||||
|
"max_tokens": 128000,
|
||||||
|
"model_type": "chat",
|
||||||
|
"is_tools": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"llm_name": "glm-4.5-flash",
|
||||||
|
"tags": "LLM,CHAT,128K,",
|
||||||
|
"max_tokens": 128000,
|
||||||
|
"model_type": "chat",
|
||||||
|
"is_tools": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"llm_name": "glm-4-plus",
|
"llm_name": "glm-4-plus",
|
||||||
"tags": "LLM,CHAT,",
|
"tags": "LLM,CHAT,",
|
||||||
|
|||||||
@ -44,14 +44,17 @@ class Base(ABC):
|
|||||||
raise NotImplementedError("Please implement encode method!")
|
raise NotImplementedError("Please implement encode method!")
|
||||||
|
|
||||||
def total_token_count(self, resp):
|
def total_token_count(self, resp):
|
||||||
try:
|
if hasattr(resp, "usage") and hasattr(resp.usage, "total_tokens"):
|
||||||
return resp.usage.total_tokens
|
try:
|
||||||
except Exception:
|
return resp.usage.total_tokens
|
||||||
pass
|
except Exception:
|
||||||
try:
|
pass
|
||||||
return resp["usage"]["total_tokens"]
|
|
||||||
except Exception:
|
if 'usage' in resp and 'total_tokens' in resp['usage']:
|
||||||
pass
|
try:
|
||||||
|
return resp["usage"]["total_tokens"]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -34,6 +34,7 @@ interface IProps {
|
|||||||
createConversationBeforeUploadDocument?(message: string): Promise<any>;
|
createConversationBeforeUploadDocument?(message: string): Promise<any>;
|
||||||
stopOutputMessage?(): void;
|
stopOutputMessage?(): void;
|
||||||
onUpload?: NonNullable<FileUploadProps['onUpload']>;
|
onUpload?: NonNullable<FileUploadProps['onUpload']>;
|
||||||
|
removeFile?(file: File): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NextMessageInput({
|
export function NextMessageInput({
|
||||||
@ -47,6 +48,7 @@ export function NextMessageInput({
|
|||||||
onInputChange,
|
onInputChange,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
onPressEnter,
|
onPressEnter,
|
||||||
|
removeFile,
|
||||||
}: IProps) {
|
}: IProps) {
|
||||||
const [files, setFiles] = React.useState<File[]>([]);
|
const [files, setFiles] = React.useState<File[]>([]);
|
||||||
|
|
||||||
@ -77,6 +79,13 @@ export function NextMessageInput({
|
|||||||
[submit],
|
[submit],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleRemoveFile = React.useCallback(
|
||||||
|
(file: File) => () => {
|
||||||
|
removeFile?.(file);
|
||||||
|
},
|
||||||
|
[removeFile],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileUpload
|
<FileUpload
|
||||||
value={files}
|
value={files}
|
||||||
@ -121,6 +130,7 @@ export function NextMessageInput({
|
|||||||
variant="secondary"
|
variant="secondary"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="-top-1 -right-1 absolute size-4 shrink-0 cursor-pointer rounded-full"
|
className="-top-1 -right-1 absolute size-4 shrink-0 cursor-pointer rounded-full"
|
||||||
|
onClick={handleRemoveFile(file)}
|
||||||
>
|
>
|
||||||
<X className="size-2.5" />
|
<X className="size-2.5" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -8,7 +8,7 @@ const Table = React.forwardRef<
|
|||||||
>(({ className, rootClassName, ...props }, ref) => (
|
>(({ className, rootClassName, ...props }, ref) => (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'relative w-full overflow-auto rounded-2xl bg-bg-card',
|
'relative w-full overflow-auto rounded-2xl bg-bg-card scrollbar-none',
|
||||||
rootClassName,
|
rootClassName,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@ -27,7 +27,7 @@ const TableHeader = React.forwardRef<
|
|||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<thead
|
<thead
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn('[&_tr]:border-b top-0 sticky', className)}
|
className={cn('[&_tr]:border-b top-0 sticky bg-bg-title z-10', className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
@ -67,7 +67,7 @@ const TableRow = React.forwardRef<
|
|||||||
<tr
|
<tr
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
|
'border-b border-border-button transition-colors hover:bg-bg-card data-[state=selected]:bg-bg-card',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
@ -82,7 +82,7 @@ const TableHead = React.forwardRef<
|
|||||||
<th
|
<th
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-12 px-4 text-left align-middle font-normal text-text-secondary [&:has([role=checkbox])]:pr-0',
|
'h-12 px-4 text-left align-middle font-normal text-text-secondary [&:has([role=checkbox])]:pr-0 ',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -390,7 +390,7 @@ export const useUploadCanvasFileWithProgress = (
|
|||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
onError(file, error as Error);
|
onError(file, error as Error);
|
||||||
});
|
});
|
||||||
message.error('error', error?.message);
|
message.error(error?.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { FileUploadProps } from '@/components/file-upload';
|
||||||
import message from '@/components/ui/message';
|
import message from '@/components/ui/message';
|
||||||
import { ChatSearchParams } from '@/constants/chat';
|
import { ChatSearchParams } from '@/constants/chat';
|
||||||
import {
|
import {
|
||||||
@ -14,7 +15,7 @@ import { buildMessageListWithUuid, getConversationId } 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';
|
||||||
import { has } from 'lodash';
|
import { has } from 'lodash';
|
||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams, useSearchParams } from 'umi';
|
import { useParams, useSearchParams } from 'umi';
|
||||||
import {
|
import {
|
||||||
@ -395,9 +396,14 @@ export const useDeleteMessage = () => {
|
|||||||
return { data, loading, deleteMessage: mutateAsync };
|
return { data, loading, deleteMessage: mutateAsync };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UploadParameters = Parameters<NonNullable<FileUploadProps['onUpload']>>;
|
||||||
|
|
||||||
|
type X = { file: UploadParameters[0][0]; options: UploadParameters[1] };
|
||||||
|
|
||||||
export function useUploadAndParseFile() {
|
export function useUploadAndParseFile() {
|
||||||
const { conversationId } = useGetChatSearchParams();
|
const { conversationId } = useGetChatSearchParams();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const controller = useRef(new AbortController());
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@ -405,22 +411,48 @@ export function useUploadAndParseFile() {
|
|||||||
mutateAsync,
|
mutateAsync,
|
||||||
} = useMutation({
|
} = useMutation({
|
||||||
mutationKey: [ChatApiAction.UploadAndParse],
|
mutationKey: [ChatApiAction.UploadAndParse],
|
||||||
mutationFn: async (file: File) => {
|
mutationFn: async ({
|
||||||
const formData = new FormData();
|
file,
|
||||||
formData.append('file', file);
|
options: { onProgress, onSuccess, onError },
|
||||||
formData.append('conversation_id', conversationId);
|
}: X) => {
|
||||||
|
try {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
formData.append('conversation_id', conversationId);
|
||||||
|
|
||||||
const { data } = await chatService.uploadAndParse(formData);
|
const { data } = await chatService.uploadAndParse(
|
||||||
|
{
|
||||||
|
signal: controller.current.signal,
|
||||||
|
data: formData,
|
||||||
|
onUploadProgress: ({ progress }) => {
|
||||||
|
onProgress(file, (progress || 0) * 100 - 1);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
if (data.code === 0) {
|
onProgress(file, 100);
|
||||||
message.success(t(`message.uploaded`));
|
|
||||||
|
if (data.code === 0) {
|
||||||
|
onSuccess(file);
|
||||||
|
message.success(t(`message.uploaded`));
|
||||||
|
} else {
|
||||||
|
onError(file, new Error(data.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
onError(file, error as Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return { data, loading, uploadAndParseFile: mutateAsync };
|
const cancel = useCallback(() => {
|
||||||
|
controller.current.abort();
|
||||||
|
controller.current = new AbortController();
|
||||||
|
}, [controller]);
|
||||||
|
|
||||||
|
return { data, loading, uploadAndParseFile: mutateAsync, cancel };
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFetchExternalChatInfo = () => {
|
export const useFetchExternalChatInfo = () => {
|
||||||
|
|||||||
@ -837,7 +837,7 @@ This auto-tagging feature enhances retrieval by adding another layer of domain-s
|
|||||||
fileManager: {
|
fileManager: {
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
uploadDate: 'Upload Date',
|
uploadDate: 'Upload Date',
|
||||||
knowledgeBase: 'Knowledge Base',
|
knowledgeBase: 'Dataset',
|
||||||
size: 'Size',
|
size: 'Size',
|
||||||
action: 'Action',
|
action: 'Action',
|
||||||
addToKnowledge: 'Link to Knowledge Base',
|
addToKnowledge: 'Link to Knowledge Base',
|
||||||
|
|||||||
@ -50,9 +50,10 @@ export function DatasetActionCell({
|
|||||||
}, [record, showRenameModal]);
|
}, [record, showRenameModal]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="flex gap-4 items-center text-text-sub-title-invert">
|
<section className="flex gap-4 items-center text-text-sub-title-invert opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
<Button
|
<Button
|
||||||
variant={'ghost'}
|
variant="transparent"
|
||||||
|
className="border-none hover:bg-bg-card text-text-primary"
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
disabled={isRunning}
|
disabled={isRunning}
|
||||||
onClick={handleRename}
|
onClick={handleRename}
|
||||||
@ -61,7 +62,12 @@ export function DatasetActionCell({
|
|||||||
</Button>
|
</Button>
|
||||||
<HoverCard>
|
<HoverCard>
|
||||||
<HoverCardTrigger>
|
<HoverCardTrigger>
|
||||||
<Button variant="ghost" disabled={isRunning} size={'sm'}>
|
<Button
|
||||||
|
variant="transparent"
|
||||||
|
className="border-none hover:bg-bg-card text-text-primary"
|
||||||
|
disabled={isRunning}
|
||||||
|
size={'sm'}
|
||||||
|
>
|
||||||
<Eye />
|
<Eye />
|
||||||
</Button>
|
</Button>
|
||||||
</HoverCardTrigger>
|
</HoverCardTrigger>
|
||||||
@ -88,7 +94,8 @@ export function DatasetActionCell({
|
|||||||
|
|
||||||
{isVirtualDocument || (
|
{isVirtualDocument || (
|
||||||
<Button
|
<Button
|
||||||
variant={'ghost'}
|
variant="transparent"
|
||||||
|
className="border-none hover:bg-bg-card text-text-primary"
|
||||||
onClick={onDownloadDocument}
|
onClick={onDownloadDocument}
|
||||||
disabled={isRunning}
|
disabled={isRunning}
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
@ -97,7 +104,12 @@ export function DatasetActionCell({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<ConfirmDeleteDialog onOk={handleRemove}>
|
<ConfirmDeleteDialog onOk={handleRemove}>
|
||||||
<Button variant={'ghost'} size={'sm'} disabled={isRunning}>
|
<Button
|
||||||
|
variant="transparent"
|
||||||
|
className="border-none hover:bg-bg-card text-text-primary"
|
||||||
|
size={'sm'}
|
||||||
|
disabled={isRunning}
|
||||||
|
>
|
||||||
<Trash2 />
|
<Trash2 />
|
||||||
</Button>
|
</Button>
|
||||||
</ConfirmDeleteDialog>
|
</ConfirmDeleteDialog>
|
||||||
|
|||||||
@ -119,7 +119,7 @@ export function DatasetTable({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Table rootClassName="max-h-[82vh]">
|
<Table rootClassName="max-h-[calc(100vh-222px)]">
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<TableRow key={headerGroup.id}>
|
<TableRow key={headerGroup.id}>
|
||||||
@ -144,6 +144,7 @@ export function DatasetTable({
|
|||||||
<TableRow
|
<TableRow
|
||||||
key={row.id}
|
key={row.id}
|
||||||
data-state={row.getIsSelected() && 'selected'}
|
data-state={row.getIsSelected() && 'selected'}
|
||||||
|
className="group"
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<TableCell
|
<TableCell
|
||||||
|
|||||||
@ -15,9 +15,9 @@ import { Progress } from '@/components/ui/progress';
|
|||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import { IDocumentInfo } from '@/interfaces/database/document';
|
import { IDocumentInfo } from '@/interfaces/database/document';
|
||||||
import { CircleX, RefreshCw } from 'lucide-react';
|
import { CircleX, RefreshCw } from 'lucide-react';
|
||||||
import { useCallback } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { RunningStatus } from './constant';
|
import { DocumentType, RunningStatus } from './constant';
|
||||||
import { ParsingCard, PopoverContent } from './parsing-card';
|
import { ParsingCard, PopoverContent } from './parsing-card';
|
||||||
import { UseChangeDocumentParserShowType } from './use-change-document-parser';
|
import { UseChangeDocumentParserShowType } from './use-change-document-parser';
|
||||||
import { useHandleRunDocumentByIds } from './use-run-document';
|
import { useHandleRunDocumentByIds } from './use-run-document';
|
||||||
@ -61,6 +61,10 @@ export function ParsingStatusCell({
|
|||||||
showSetMetaModal(record);
|
showSetMetaModal(record);
|
||||||
}, [record, showSetMetaModal]);
|
}, [record, showSetMetaModal]);
|
||||||
|
|
||||||
|
const showParse = useMemo(() => {
|
||||||
|
return record.type !== DocumentType.Virtual;
|
||||||
|
}, [record]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="flex gap-8 items-center">
|
<section className="flex gap-8 items-center">
|
||||||
<div className="w-fit flex items-center justify-between">
|
<div className="w-fit flex items-center justify-between">
|
||||||
@ -80,38 +84,42 @@ export function ParsingStatusCell({
|
|||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
<ConfirmDeleteDialog
|
{showParse && (
|
||||||
title={t(`knowledgeDetails.redo`, { chunkNum: chunk_num })}
|
<>
|
||||||
hidden={isZeroChunk || isRunning}
|
<ConfirmDeleteDialog
|
||||||
onOk={handleOperationIconClick(true)}
|
title={t(`knowledgeDetails.redo`, { chunkNum: chunk_num })}
|
||||||
onCancel={handleOperationIconClick(false)}
|
hidden={isZeroChunk || isRunning}
|
||||||
>
|
onOk={handleOperationIconClick(true)}
|
||||||
<div
|
onCancel={handleOperationIconClick(false)}
|
||||||
className="cursor-pointer flex items-center gap-3"
|
>
|
||||||
onClick={
|
<div
|
||||||
isZeroChunk || isRunning
|
className="cursor-pointer flex items-center gap-3"
|
||||||
? handleOperationIconClick(false)
|
onClick={
|
||||||
: () => {}
|
isZeroChunk || isRunning
|
||||||
}
|
? handleOperationIconClick(false)
|
||||||
>
|
: () => {}
|
||||||
<Separator orientation="vertical" className="h-2.5" />
|
}
|
||||||
{operationIcon}
|
>
|
||||||
</div>
|
<Separator orientation="vertical" className="h-2.5" />
|
||||||
</ConfirmDeleteDialog>
|
{operationIcon}
|
||||||
{isParserRunning(run) ? (
|
|
||||||
<HoverCard>
|
|
||||||
<HoverCardTrigger asChild>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Progress value={p} className="h-1 flex-1 min-w-10" />
|
|
||||||
{p}%
|
|
||||||
</div>
|
</div>
|
||||||
</HoverCardTrigger>
|
</ConfirmDeleteDialog>
|
||||||
<HoverCardContent className="w-[40vw]">
|
{isParserRunning(run) ? (
|
||||||
<PopoverContent record={record}></PopoverContent>
|
<HoverCard>
|
||||||
</HoverCardContent>
|
<HoverCardTrigger asChild>
|
||||||
</HoverCard>
|
<div className="flex items-center gap-1">
|
||||||
) : (
|
<Progress value={p} className="h-1 flex-1 min-w-10" />
|
||||||
<ParsingCard record={record}></ParsingCard>
|
{p}%
|
||||||
|
</div>
|
||||||
|
</HoverCardTrigger>
|
||||||
|
<HoverCardContent className="w-[40vw]">
|
||||||
|
<PopoverContent record={record}></PopoverContent>
|
||||||
|
</HoverCardContent>
|
||||||
|
</HoverCard>
|
||||||
|
) : (
|
||||||
|
<ParsingCard record={record}></ParsingCard>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -61,24 +61,40 @@ export function ActionCell({
|
|||||||
}, [record, showMoveFileModal]);
|
}, [record, showMoveFileModal]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="flex gap-4 items-center text-text-sub-title-invert">
|
<section className="flex gap-4 items-center text-text-sub-title-invert opacity-0 group-hover:opacity-100 transition-opacity">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="transparent"
|
||||||
|
className="border-none hover:bg-bg-card text-text-primary"
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
onClick={handleShowConnectToKnowledgeModal}
|
onClick={handleShowConnectToKnowledgeModal}
|
||||||
>
|
>
|
||||||
<Link2 />
|
<Link2 />
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size={'sm'} onClick={handleShowMoveFileModal}>
|
<Button
|
||||||
|
variant="transparent"
|
||||||
|
className="border-none hover:bg-bg-card text-text-primary"
|
||||||
|
size={'sm'}
|
||||||
|
onClick={handleShowMoveFileModal}
|
||||||
|
>
|
||||||
<FolderInput />
|
<FolderInput />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button variant="ghost" size={'sm'} onClick={handleShowFileRenameModal}>
|
<Button
|
||||||
|
variant="transparent"
|
||||||
|
className="border-none hover:bg-bg-card text-text-primary"
|
||||||
|
size={'sm'}
|
||||||
|
onClick={handleShowFileRenameModal}
|
||||||
|
>
|
||||||
<FolderPen />
|
<FolderPen />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{isFolder || (
|
{isFolder || (
|
||||||
<Button variant={'ghost'} size={'sm'} onClick={onDownloadDocument}>
|
<Button
|
||||||
|
variant="transparent"
|
||||||
|
className="border-none hover:bg-bg-card text-text-primary"
|
||||||
|
size={'sm'}
|
||||||
|
onClick={onDownloadDocument}
|
||||||
|
>
|
||||||
<ArrowDownToLine />
|
<ArrowDownToLine />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@ -89,7 +105,11 @@ export function ActionCell({
|
|||||||
documentName={record.name}
|
documentName={record.name}
|
||||||
className="text-text-sub-title-invert"
|
className="text-text-sub-title-invert"
|
||||||
>
|
>
|
||||||
<Button variant={'ghost'} size={'sm'}>
|
<Button
|
||||||
|
variant="transparent"
|
||||||
|
className="border-none hover:bg-bg-card text-text-primary"
|
||||||
|
size={'sm'}
|
||||||
|
>
|
||||||
<Eye />
|
<Eye />
|
||||||
</Button>
|
</Button>
|
||||||
</NewDocumentLink>
|
</NewDocumentLink>
|
||||||
@ -97,7 +117,8 @@ export function ActionCell({
|
|||||||
|
|
||||||
{/* <DropdownMenu>
|
{/* <DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="ghost" size={'sm'}>
|
<Button variant="transparent"
|
||||||
|
className="border-none" size={'sm'}>
|
||||||
<EllipsisVertical />
|
<EllipsisVertical />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
@ -118,7 +139,11 @@ export function ActionCell({
|
|||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu> */}
|
</DropdownMenu> */}
|
||||||
<ConfirmDeleteDialog>
|
<ConfirmDeleteDialog>
|
||||||
<Button variant="ghost" size={'sm'}>
|
<Button
|
||||||
|
variant="transparent"
|
||||||
|
className="border-none hover:bg-bg-card text-text-primary"
|
||||||
|
size={'sm'}
|
||||||
|
>
|
||||||
<Trash2 />
|
<Trash2 />
|
||||||
</Button>
|
</Button>
|
||||||
</ConfirmDeleteDialog>
|
</ConfirmDeleteDialog>
|
||||||
|
|||||||
@ -213,6 +213,7 @@ export function FilesTable({
|
|||||||
id: 'actions',
|
id: 'actions',
|
||||||
header: t('action'),
|
header: t('action'),
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
|
enablePinning: true,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return (
|
||||||
<ActionCell
|
<ActionCell
|
||||||
@ -259,51 +260,56 @@ export function FilesTable({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<>
|
||||||
<Table>
|
<div className="w-full">
|
||||||
<TableHeader>
|
<Table rootClassName="max-h-[calc(100vh-242px)] overflow-auto">
|
||||||
{table.getHeaderGroups().map((headerGroup) => (
|
<TableHeader>
|
||||||
<TableRow key={headerGroup.id}>
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
{headerGroup.headers.map((header) => {
|
<TableRow key={headerGroup.id}>
|
||||||
return (
|
{headerGroup.headers.map((header) => {
|
||||||
<TableHead key={header.id}>
|
return (
|
||||||
{header.isPlaceholder
|
<TableHead key={header.id}>
|
||||||
? null
|
{header.isPlaceholder
|
||||||
: flexRender(
|
? null
|
||||||
header.column.columnDef.header,
|
: flexRender(
|
||||||
header.getContext(),
|
header.column.columnDef.header,
|
||||||
)}
|
header.getContext(),
|
||||||
</TableHead>
|
)}
|
||||||
);
|
</TableHead>
|
||||||
})}
|
);
|
||||||
</TableRow>
|
})}
|
||||||
))}
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{loading ? (
|
|
||||||
<TableSkeleton columnsLength={columns.length}></TableSkeleton>
|
|
||||||
) : table.getRowModel().rows?.length ? (
|
|
||||||
table.getRowModel().rows.map((row) => (
|
|
||||||
<TableRow
|
|
||||||
key={row.id}
|
|
||||||
data-state={row.getIsSelected() && 'selected'}
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell) => (
|
|
||||||
<TableCell
|
|
||||||
key={cell.id}
|
|
||||||
className={cell.column.columnDef.meta?.cellClassName}
|
|
||||||
>
|
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
||||||
</TableCell>
|
|
||||||
))}
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))
|
))}
|
||||||
) : (
|
</TableHeader>
|
||||||
<TableEmpty columnsLength={columns.length}></TableEmpty>
|
<TableBody className="max-h-96 overflow-y-auto">
|
||||||
)}
|
{loading ? (
|
||||||
</TableBody>
|
<TableSkeleton columnsLength={columns.length}></TableSkeleton>
|
||||||
</Table>
|
) : table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && 'selected'}
|
||||||
|
className="group"
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell
|
||||||
|
key={cell.id}
|
||||||
|
className={cell.column.columnDef.meta?.cellClassName}
|
||||||
|
>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext(),
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableEmpty columnsLength={columns.length}></TableEmpty>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
<div className="flex items-center justify-end py-4">
|
<div className="flex items-center justify-end py-4">
|
||||||
<div className="space-x-2">
|
<div className="space-x-2">
|
||||||
<RAGFlowPagination
|
<RAGFlowPagination
|
||||||
@ -331,6 +337,6 @@ export function FilesTable({
|
|||||||
loading={fileRenameLoading}
|
loading={fileRenameLoading}
|
||||||
></RenameDialog>
|
></RenameDialog>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,7 @@ export function SingleChatBox({ controller }: IProps) {
|
|||||||
removeMessageById,
|
removeMessageById,
|
||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
handleUploadFile,
|
handleUploadFile,
|
||||||
|
removeFile,
|
||||||
} = useSendMessage(controller);
|
} = useSendMessage(controller);
|
||||||
const { data: userInfo } = useFetchUserInfo();
|
const { data: userInfo } = useFetchUserInfo();
|
||||||
const { data: currentDialog } = useFetchDialog();
|
const { data: currentDialog } = useFetchDialog();
|
||||||
@ -97,6 +98,7 @@ export function SingleChatBox({ controller }: IProps) {
|
|||||||
stopOutputMessage={stopOutputMessage}
|
stopOutputMessage={stopOutputMessage}
|
||||||
onUpload={handleUploadFile}
|
onUpload={handleUploadFile}
|
||||||
isUploading={isUploading}
|
isUploading={isUploading}
|
||||||
|
removeFile={removeFile}
|
||||||
/>
|
/>
|
||||||
{visible && (
|
{visible && (
|
||||||
<PdfDrawer
|
<PdfDrawer
|
||||||
|
|||||||
@ -138,7 +138,7 @@ export const useSendMessage = (controller: AbortController) => {
|
|||||||
const { conversationId, isNew } = useGetChatSearchParams();
|
const { conversationId, isNew } = useGetChatSearchParams();
|
||||||
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||||
|
|
||||||
const { handleUploadFile, fileIds, clearFileIds, isUploading } =
|
const { handleUploadFile, fileIds, clearFileIds, isUploading, removeFile } =
|
||||||
useUploadFile();
|
useUploadFile();
|
||||||
|
|
||||||
const { send, answer, done } = useSendMessageWithSse(
|
const { send, answer, done } = useSendMessageWithSse(
|
||||||
@ -287,5 +287,6 @@ export const useSendMessage = (controller: AbortController) => {
|
|||||||
stopOutputMessage,
|
stopOutputMessage,
|
||||||
handleUploadFile,
|
handleUploadFile,
|
||||||
isUploading,
|
isUploading,
|
||||||
|
removeFile,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,16 +3,21 @@ import { useUploadAndParseFile } from '@/hooks/use-chat-request';
|
|||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
export function useUploadFile() {
|
export function useUploadFile() {
|
||||||
const { uploadAndParseFile, loading } = useUploadAndParseFile();
|
const { uploadAndParseFile, loading, cancel } = useUploadAndParseFile();
|
||||||
const [fileIds, setFileIds] = useState<string[]>([]);
|
const [fileIds, setFileIds] = useState<string[]>([]);
|
||||||
|
const [fileMap, setFileMap] = useState<Map<File, string>>(new Map());
|
||||||
|
|
||||||
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
const handleUploadFile: NonNullable<FileUploadProps['onUpload']> =
|
||||||
useCallback(
|
useCallback(
|
||||||
async (files) => {
|
async (files, options) => {
|
||||||
if (Array.isArray(files) && files.length) {
|
if (Array.isArray(files) && files.length) {
|
||||||
const ret = await uploadAndParseFile(files[0]);
|
const ret = await uploadAndParseFile({ file: files[0], options });
|
||||||
if (ret.code === 0 && Array.isArray(ret.data)) {
|
if (ret.code === 0 && Array.isArray(ret.data)) {
|
||||||
setFileIds((list) => [...list, ...ret.data]);
|
setFileIds((list) => [...list, ...ret.data]);
|
||||||
|
setFileMap((map) => {
|
||||||
|
map.set(files[0], ret.data[0]);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -21,7 +26,28 @@ export function useUploadFile() {
|
|||||||
|
|
||||||
const clearFileIds = useCallback(() => {
|
const clearFileIds = useCallback(() => {
|
||||||
setFileIds([]);
|
setFileIds([]);
|
||||||
|
setFileMap(new Map());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return { handleUploadFile, clearFileIds, fileIds, isUploading: loading };
|
const removeFile = useCallback(
|
||||||
|
(file: File) => {
|
||||||
|
if (loading) {
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const id = fileMap.get(file);
|
||||||
|
if (id) {
|
||||||
|
setFileIds((list) => list.filter((item) => item !== id));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[cancel, fileMap, loading],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleUploadFile,
|
||||||
|
clearFileIds,
|
||||||
|
fileIds,
|
||||||
|
isUploading: loading,
|
||||||
|
removeFile,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,7 @@ module.exports = {
|
|||||||
'input-border': 'var(--input-border)',
|
'input-border': 'var(--input-border)',
|
||||||
|
|
||||||
/* design colors */
|
/* design colors */
|
||||||
|
'bg-title': 'var(--bg-title)',
|
||||||
'bg-base': 'var(--bg-base)',
|
'bg-base': 'var(--bg-base)',
|
||||||
'bg-card': 'var(--bg-card)',
|
'bg-card': 'var(--bg-card)',
|
||||||
'bg-component': 'var(--bg-component)',
|
'bg-component': 'var(--bg-component)',
|
||||||
|
|||||||
@ -91,6 +91,8 @@
|
|||||||
--input-border: rgba(22, 22, 24, 0.2);
|
--input-border: rgba(22, 22, 24, 0.2);
|
||||||
|
|
||||||
--metallic: #46464a;
|
--metallic: #46464a;
|
||||||
|
|
||||||
|
--bg-title: #f6f6f7;
|
||||||
/* design colors */
|
/* design colors */
|
||||||
|
|
||||||
--bg-base: #ffffff;
|
--bg-base: #ffffff;
|
||||||
@ -235,6 +237,8 @@
|
|||||||
--input-border: rgba(255, 255, 255, 0.2);
|
--input-border: rgba(255, 255, 255, 0.2);
|
||||||
|
|
||||||
--metallic: #fafafa;
|
--metallic: #fafafa;
|
||||||
|
|
||||||
|
--bg-title: #38383a;
|
||||||
/* design colors */
|
/* design colors */
|
||||||
|
|
||||||
--bg-base: #161618;
|
--bg-base: #161618;
|
||||||
|
|||||||
Reference in New Issue
Block a user