mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-02-03 00:55:10 +08:00
Compare commits
12 Commits
15fff5724e
...
af6eabad0e
| Author | SHA1 | Date | |
|---|---|---|---|
| af6eabad0e | |||
| 5fb5a51b2e | |||
| 37004ecfb3 | |||
| 6d333ec4bc | |||
| ac188b0486 | |||
| adeb9d87e2 | |||
| d121033208 | |||
| 494f84cd69 | |||
| f24d464a53 | |||
| 484c536f2e | |||
| f7112acd97 | |||
| de4f75dcd8 |
@ -70,6 +70,7 @@ def create():
|
||||
e, t = TenantService.get_by_id(current_user.id)
|
||||
if not e:
|
||||
return get_data_error_result(message="Tenant not found.")
|
||||
|
||||
req["parser_config"] = {
|
||||
"layout_recognize": "DeepDOC",
|
||||
"chunk_token_num": 512,
|
||||
|
||||
@ -173,7 +173,7 @@ def filename_type(filename):
|
||||
if re.match(r".*\.(wav|flac|ape|alac|wavpack|wv|mp3|aac|ogg|vorbis|opus)$", filename):
|
||||
return FileType.AURAL.value
|
||||
|
||||
if re.match(r".*\.(jpg|jpeg|png|tif|gif|pcx|tga|exif|fpx|svg|psd|cdr|pcd|dxf|ufo|eps|ai|raw|WMF|webp|avif|apng|icon|ico|mpg|mpeg|avi|rm|rmvb|mov|wmv|asf|dat|asx|wvx|mpe|mpa|mp4)$", filename):
|
||||
if re.match(r".*\.(jpg|jpeg|png|tif|gif|pcx|tga|exif|fpx|svg|psd|cdr|pcd|dxf|ufo|eps|ai|raw|WMF|webp|avif|apng|icon|ico|mpg|mpeg|avi|rm|rmvb|mov|wmv|asf|dat|asx|wvx|mpe|mpa|mp4|avi|mkv)$", filename):
|
||||
return FileType.VISUAL.value
|
||||
|
||||
return FileType.OTHER.value
|
||||
|
||||
@ -77,7 +77,7 @@ services:
|
||||
container_name: ragflow-infinity
|
||||
profiles:
|
||||
- infinity
|
||||
image: infiniflow/infinity:v0.6.0
|
||||
image: infiniflow/infinity:v0.6.1
|
||||
volumes:
|
||||
- infinity_data:/var/infinity
|
||||
- ./infinity_conf.toml:/infinity_conf.toml
|
||||
|
||||
@ -34,7 +34,7 @@ Click **+ Add** to add heading levels here or update the corresponding **Regular
|
||||
|
||||
### Output
|
||||
|
||||
The global variable name for the output of the **Title chunkder** component, which can be referenced by subsequent components in the ingestion pipeline.
|
||||
The global variable name for the output of the **Title chunker** component, which can be referenced by subsequent components in the ingestion pipeline.
|
||||
|
||||
- Default: `chunks`
|
||||
- Type: `Array<Object>`
|
||||
@ -37,7 +37,7 @@ Defaults to `\n`. Click the right-hand **Recycle bin** button to remove it, or c
|
||||
|
||||
### Output
|
||||
|
||||
The global variable name for the output of the **Token chunkder** component, which can be referenced by subsequent components in the ingestion pipeline.
|
||||
The global variable name for the output of the **Token chunker** component, which can be referenced by subsequent components in the ingestion pipeline.
|
||||
|
||||
- Default: `chunks`
|
||||
- Type: `Array<Object>`
|
||||
@ -22,6 +22,23 @@ The embedding models included in a full edition are:
|
||||
These two embedding models are optimized specifically for English and Chinese, so performance may be compromised if you use them to embed documents in other languages.
|
||||
:::
|
||||
|
||||
## v0.21.1
|
||||
|
||||
Released on October 23, 2025.
|
||||
|
||||
### New features
|
||||
|
||||
- Experimental: Adds support for PDF document parsing using MinerU.
|
||||
|
||||
### Improvements
|
||||
|
||||
- Enhances UI/UX for the dataset and personal center pages.
|
||||
- Upgrades RAGFlow's document engine, Infinity, to v0.6.1.
|
||||
|
||||
### Fixed issues
|
||||
|
||||
- An issue with video parsing.
|
||||
|
||||
## v0.21.0
|
||||
|
||||
Released on October 15, 2025.
|
||||
|
||||
@ -29,7 +29,7 @@ from rag.utils import clean_markdown_block
|
||||
ocr = OCR()
|
||||
|
||||
# Gemini supported MIME types
|
||||
VIDEO_EXTS = [".mp4", ".mov", ".avi", ".flv", ".mpeg", ".mpg", ".webm", ".wmv", ".3gp", ".3gpp"]
|
||||
VIDEO_EXTS = [".mp4", ".mov", ".avi", ".flv", ".mpeg", ".mpg", ".webm", ".wmv", ".3gp", ".3gpp", ".mkv"]
|
||||
|
||||
|
||||
def chunk(filename, binary, tenant_id, lang, callback=None, **kwargs):
|
||||
|
||||
@ -29,6 +29,7 @@ from api.db.services.llm_service import LLMBundle
|
||||
from api.utils import get_uuid
|
||||
from api.utils.base64_image import image2id
|
||||
from deepdoc.parser import ExcelParser
|
||||
from deepdoc.parser.mineru_parser import MinerUParser
|
||||
from deepdoc.parser.pdf_parser import PlainParser, RAGFlowPdfParser, VisionParser
|
||||
from rag.app.naive import Docx
|
||||
from rag.flow.base import ProcessBase, ProcessParamBase
|
||||
@ -138,9 +139,16 @@ class ParserParam(ProcessParamBase):
|
||||
"oggvorbis",
|
||||
"ape"
|
||||
],
|
||||
"output_format": "json",
|
||||
"output_format": "text",
|
||||
},
|
||||
"video": {
|
||||
"suffix":[
|
||||
"mp4",
|
||||
"avi",
|
||||
"mkv"
|
||||
],
|
||||
"output_format": "text",
|
||||
},
|
||||
"video": {},
|
||||
}
|
||||
|
||||
def check(self):
|
||||
@ -149,7 +157,7 @@ class ParserParam(ProcessParamBase):
|
||||
pdf_parse_method = pdf_config.get("parse_method", "")
|
||||
self.check_empty(pdf_parse_method, "Parse method abnormal.")
|
||||
|
||||
if pdf_parse_method.lower() not in ["deepdoc", "plain_text"]:
|
||||
if pdf_parse_method.lower() not in ["deepdoc", "plain_text", "mineru"]:
|
||||
self.check_empty(pdf_config.get("lang", ""), "PDF VLM language")
|
||||
|
||||
pdf_output_format = pdf_config.get("output_format", "")
|
||||
@ -185,6 +193,10 @@ class ParserParam(ProcessParamBase):
|
||||
if audio_config:
|
||||
self.check_empty(audio_config.get("llm_id"), "Audio VLM")
|
||||
|
||||
video_config = self.setups.get("video", "")
|
||||
if video_config:
|
||||
self.check_empty(video_config.get("llm_id"), "Video VLM")
|
||||
|
||||
email_config = self.setups.get("email", "")
|
||||
if email_config:
|
||||
email_output_format = email_config.get("output_format", "")
|
||||
@ -207,13 +219,34 @@ class Parser(ProcessBase):
|
||||
elif conf.get("parse_method").lower() == "plain_text":
|
||||
lines, _ = PlainParser()(blob)
|
||||
bboxes = [{"text": t} for t, _ in lines]
|
||||
elif conf.get("parse_method").lower() == "mineru":
|
||||
mineru_executable = os.environ.get("MINERU_EXECUTABLE", "mineru")
|
||||
pdf_parser = MinerUParser(mineru_path=mineru_executable)
|
||||
if not pdf_parser.check_installation():
|
||||
raise RuntimeError("MinerU not found. Please install it via: pip install -U 'mineru[core]'.")
|
||||
|
||||
lines, _ = pdf_parser.parse_pdf(
|
||||
filepath=name,
|
||||
binary=blob,
|
||||
callback=self.callback,
|
||||
output_dir=os.environ.get("MINERU_OUTPUT_DIR", ""),
|
||||
delete_output=bool(int(os.environ.get("MINERU_DELETE_OUTPUT", 1))),
|
||||
)
|
||||
bboxes = []
|
||||
for t, poss in lines:
|
||||
box = {
|
||||
"image": pdf_parser.crop(poss, 1),
|
||||
"positions": [[pos[0][-1], *pos[1:]] for pos in pdf_parser.extract_positions(poss)],
|
||||
"text": t,
|
||||
}
|
||||
bboxes.append(box)
|
||||
else:
|
||||
vision_model = LLMBundle(self._canvas._tenant_id, LLMType.IMAGE2TEXT, llm_name=conf.get("parse_method"), lang=self._param.setups["pdf"].get("lang"))
|
||||
lines, _ = VisionParser(vision_model=vision_model)(blob, callback=self.callback)
|
||||
bboxes = []
|
||||
for t, poss in lines:
|
||||
pn, x0, x1, top, bott = poss.split(" ")
|
||||
bboxes.append({"page_number": int(pn), "x0": float(x0), "x1": float(x1), "top": float(top), "bottom": float(bott), "text": t})
|
||||
for pn, x0, x1, top, bott in RAGFlowPdfParser.extract_positions(poss):
|
||||
bboxes.append({"page_number": int(pn[0]), "x0": float(x0), "x1": float(x1), "top": float(top), "bottom": float(bott), "text": t})
|
||||
|
||||
if conf.get("output_format") == "json":
|
||||
self.set_output("json", bboxes)
|
||||
@ -357,6 +390,17 @@ class Parser(ProcessBase):
|
||||
|
||||
self.set_output("text", txt)
|
||||
|
||||
def _video(self, name, blob):
|
||||
self.callback(random.randint(1, 5) / 100.0, "Start to work on an video.")
|
||||
|
||||
conf = self._param.setups["video"]
|
||||
self.set_output("output_format", conf["output_format"])
|
||||
|
||||
cv_mdl = LLMBundle(self._canvas.get_tenant_id(), LLMType.IMAGE2TEXT)
|
||||
txt = cv_mdl.chat(system="", history=[], gen_conf={}, video_bytes=blob, filename=name)
|
||||
|
||||
self.set_output("text", txt)
|
||||
|
||||
def _email(self, name, blob):
|
||||
self.callback(random.randint(1, 5) / 100.0, "Start to work on an email.")
|
||||
|
||||
@ -483,6 +527,7 @@ class Parser(ProcessBase):
|
||||
"word": self._word,
|
||||
"image": self._image,
|
||||
"audio": self._audio,
|
||||
"video": self._video,
|
||||
"email": self._email,
|
||||
}
|
||||
try:
|
||||
|
||||
@ -257,25 +257,32 @@ export const useSendMessageWithSse = (
|
||||
.getReader();
|
||||
|
||||
while (true) {
|
||||
const x = await reader?.read();
|
||||
if (x) {
|
||||
const { done, value } = x;
|
||||
if (done) {
|
||||
resetAnswer();
|
||||
break;
|
||||
}
|
||||
try {
|
||||
const val = JSON.parse(value?.data || '');
|
||||
const d = val?.data;
|
||||
if (typeof d !== 'boolean') {
|
||||
setAnswer({
|
||||
...d,
|
||||
conversationId: body?.conversation_id,
|
||||
chatBoxId: body.chatBoxId,
|
||||
});
|
||||
try {
|
||||
const x = await reader?.read();
|
||||
if (x) {
|
||||
const { done, value } = x;
|
||||
if (done) {
|
||||
resetAnswer();
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
// Swallow parse errors silently
|
||||
try {
|
||||
const val = JSON.parse(value?.data || '');
|
||||
const d = val?.data;
|
||||
if (typeof d !== 'boolean') {
|
||||
setAnswer({
|
||||
...d,
|
||||
conversationId: body?.conversation_id,
|
||||
chatBoxId: body.chatBoxId,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// Swallow parse errors silently
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof DOMException && e.name === 'AbortError') {
|
||||
console.log('Request was aborted by user or logic.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,29 +126,36 @@ export const useSendMessageBySSE = (url: string = api.completeConversation) => {
|
||||
.getReader();
|
||||
|
||||
while (true) {
|
||||
const x = await reader?.read();
|
||||
if (x) {
|
||||
const { done, value } = x;
|
||||
if (done) {
|
||||
console.info('done');
|
||||
resetAnswerList();
|
||||
break;
|
||||
}
|
||||
try {
|
||||
const val = JSON.parse(value?.data || '');
|
||||
|
||||
console.info('data:', val);
|
||||
if (val.code === 500) {
|
||||
message.error(val.message);
|
||||
try {
|
||||
const x = await reader?.read();
|
||||
if (x) {
|
||||
const { done, value } = x;
|
||||
if (done) {
|
||||
console.info('done');
|
||||
resetAnswerList();
|
||||
break;
|
||||
}
|
||||
try {
|
||||
const val = JSON.parse(value?.data || '');
|
||||
|
||||
setAnswerList((list) => {
|
||||
const nextList = [...list];
|
||||
nextList.push(val);
|
||||
return nextList;
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
console.info('data:', val);
|
||||
if (val.code === 500) {
|
||||
message.error(val.message);
|
||||
}
|
||||
|
||||
setAnswerList((list) => {
|
||||
const nextList = [...list];
|
||||
nextList.push(val);
|
||||
return nextList;
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof DOMException && e.name === 'AbortError') {
|
||||
console.log('Request was aborted by user or logic.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -430,7 +430,7 @@ export default {
|
||||
`,
|
||||
useRaptor: 'RAPTOR',
|
||||
useRaptorTip:
|
||||
'Enable RAPTOR for multi-hop question-answering tasks. See https://ragflow.io/docs/dev/enable_raptor for details.',
|
||||
'RAPTOR can be used for multi-hop question-answering tasks. Navigate to the Files page, click Generate > RAPTOR to enable it. See https://ragflow.io/docs/dev/enable_raptor for details.',
|
||||
prompt: 'Prompt',
|
||||
promptTip:
|
||||
'Use the system prompt to describe the task for the LLM, specify how it should respond, and outline other miscellaneous requirements. The system prompt is often used in conjunction with keys (variables), which serve as various data inputs for the LLM. Use a forward slash `/` or the (x) button to show the keys to use.',
|
||||
|
||||
@ -425,7 +425,7 @@ export default {
|
||||
`,
|
||||
useRaptor: '使用召回增强 RAPTOR 策略',
|
||||
useRaptorTip:
|
||||
'为多跳问答任务启用 RAPTOR,详情请见 : https://ragflow.io/docs/dev/enable_raptor。',
|
||||
'RAPTOR 常应用于复杂的多跳问答任务。如需打开,请跳转至知识库的文件页面,点击生成 > RAPTOR 开启。详见: https://ragflow.io/docs/dev/enable_raptor。',
|
||||
prompt: '提示词',
|
||||
promptMessage: '提示词是必填项',
|
||||
promptText: `请总结以下段落。 小心数字,不要编造。 段落如下:
|
||||
|
||||
@ -2,10 +2,13 @@ import { Sheet, SheetContent, SheetTitle } from '@/components/ui/sheet';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useIsTaskMode } from '../hooks/use-get-begin-query';
|
||||
import AgentChatBox from './box';
|
||||
|
||||
export function ChatSheet({ hideModal }: IModalProps<any>) {
|
||||
const { t } = useTranslation();
|
||||
const isTaskMode = useIsTaskMode();
|
||||
|
||||
return (
|
||||
<Sheet open modal={false} onOpenChange={hideModal}>
|
||||
<SheetContent
|
||||
@ -13,7 +16,9 @@ export function ChatSheet({ hideModal }: IModalProps<any>) {
|
||||
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(isTaskMode ? 'flow.task' : 'chat.chat')}
|
||||
</div>
|
||||
<AgentChatBox></AgentChatBox>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
|
||||
@ -382,9 +382,9 @@ export const useSendAgentMessage = ({
|
||||
const { content, id } = findMessageFromList(answerList);
|
||||
const inputAnswer = findInputFromList(answerList);
|
||||
const answer = content || getLatestError(answerList);
|
||||
if (answerList.length > 0 && answer) {
|
||||
if (answerList.length > 0) {
|
||||
addNewestOneAnswer({
|
||||
answer: answer,
|
||||
answer: answer ?? '',
|
||||
id: id,
|
||||
...inputAnswer,
|
||||
});
|
||||
|
||||
@ -49,7 +49,7 @@ export enum PptOutputFormat {
|
||||
}
|
||||
|
||||
export enum VideoOutputFormat {
|
||||
Json = 'json',
|
||||
Text = 'text',
|
||||
}
|
||||
|
||||
export enum AudioOutputFormat {
|
||||
@ -76,7 +76,7 @@ export const InitialOutputFormatMap = {
|
||||
[FileType.TextMarkdown]: TextMarkdownOutputFormat.Text,
|
||||
[FileType.Docx]: DocxOutputFormat.Json,
|
||||
[FileType.PowerPoint]: PptOutputFormat.Json,
|
||||
[FileType.Video]: VideoOutputFormat.Json,
|
||||
[FileType.Video]: VideoOutputFormat.Text,
|
||||
[FileType.Audio]: AudioOutputFormat.Text,
|
||||
};
|
||||
|
||||
@ -244,7 +244,7 @@ export const FileTypeSuffixMap = {
|
||||
[FileType.TextMarkdown]: ['md', 'markdown', 'mdx', 'txt'],
|
||||
[FileType.Docx]: ['doc', 'docx'],
|
||||
[FileType.PowerPoint]: ['pptx'],
|
||||
[FileType.Video]: [],
|
||||
[FileType.Video]: ['mp4', 'avi', 'mkv'],
|
||||
[FileType.Audio]: [
|
||||
'da',
|
||||
'wave',
|
||||
|
||||
@ -2,7 +2,7 @@ import message from '@/components/ui/message';
|
||||
import { Spin } from '@/components/ui/spin';
|
||||
import request from '@/utils/request';
|
||||
import classNames from 'classnames';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
interface ImagePreviewerProps {
|
||||
className?: string;
|
||||
@ -17,7 +17,7 @@ export const ImagePreviewer: React.FC<ImagePreviewerProps> = ({
|
||||
const [imageSrc, setImageSrc] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
const fetchImage = async () => {
|
||||
const fetchImage = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
const res = await request(url, {
|
||||
method: 'GET',
|
||||
@ -30,12 +30,13 @@ export const ImagePreviewer: React.FC<ImagePreviewerProps> = ({
|
||||
const objectUrl = URL.createObjectURL(res.data);
|
||||
setImageSrc(objectUrl);
|
||||
setIsLoading(false);
|
||||
};
|
||||
}, [url]);
|
||||
|
||||
useEffect(() => {
|
||||
if (url) {
|
||||
fetchImage();
|
||||
}
|
||||
}, [url]);
|
||||
}, [url, fetchImage]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
|
||||
@ -8,6 +8,7 @@ import styles from './index.less';
|
||||
import PdfPreviewer, { IProps } from './pdf-preview';
|
||||
import { PptPreviewer } from './ppt-preview';
|
||||
import { TxtPreviewer } from './txt-preview';
|
||||
import { VideoPreviewer } from './video-preview';
|
||||
|
||||
type PreviewProps = {
|
||||
fileType: string;
|
||||
@ -42,11 +43,30 @@ const Preview = ({
|
||||
<TxtPreviewer className={className} url={url} />
|
||||
</section>
|
||||
)}
|
||||
{['visual'].indexOf(fileType) > -1 && (
|
||||
{['jpg', 'png', 'gif', 'jpeg', 'svg', 'bmp', 'ico', 'tif'].indexOf(
|
||||
fileType,
|
||||
) > -1 && (
|
||||
<section>
|
||||
<ImagePreviewer className={className} url={url} />
|
||||
</section>
|
||||
)}
|
||||
{[
|
||||
'mp4',
|
||||
'avi',
|
||||
'mov',
|
||||
'mkv',
|
||||
'wmv',
|
||||
'flv',
|
||||
'mpeg',
|
||||
'mpg',
|
||||
'asf',
|
||||
'rm',
|
||||
'rmvb',
|
||||
].indexOf(fileType) > -1 && (
|
||||
<section>
|
||||
<VideoPreviewer className={className} url={url} />
|
||||
</section>
|
||||
)}
|
||||
{['pptx'].indexOf(fileType) > -1 && (
|
||||
<section>
|
||||
<PptPreviewer className={className} url={url} />
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
import message from '@/components/ui/message';
|
||||
import { Spin } from '@/components/ui/spin';
|
||||
import request from '@/utils/request';
|
||||
import classNames from 'classnames';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
interface VideoPreviewerProps {
|
||||
className?: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export const VideoPreviewer: React.FC<VideoPreviewerProps> = ({
|
||||
className,
|
||||
url,
|
||||
}) => {
|
||||
// const url = useGetDocumentUrl();
|
||||
const [videoSrc, setVideoSrc] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
|
||||
const fetchVideo = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
const res = await request(url, {
|
||||
method: 'GET',
|
||||
responseType: 'blob',
|
||||
onError: () => {
|
||||
message.error('Failed to load video');
|
||||
setIsLoading(false);
|
||||
},
|
||||
});
|
||||
const objectUrl = URL.createObjectURL(res.data);
|
||||
setVideoSrc(objectUrl);
|
||||
setIsLoading(false);
|
||||
}, [url]);
|
||||
|
||||
useEffect(() => {
|
||||
if (url) {
|
||||
fetchVideo();
|
||||
}
|
||||
}, [url, fetchVideo]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (videoSrc) {
|
||||
URL.revokeObjectURL(videoSrc);
|
||||
}
|
||||
};
|
||||
}, [videoSrc]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'relative w-full h-full p-4 bg-background-paper border border-border-normal rounded-md video-previewer',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<Spin />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isLoading && videoSrc && (
|
||||
<div className="max-h-[80vh] overflow-auto p-2">
|
||||
<video
|
||||
src={videoSrc}
|
||||
controls
|
||||
className="w-full h-auto max-w-full object-contain"
|
||||
onLoadedData={() => URL.revokeObjectURL(videoSrc!)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -166,6 +166,7 @@ const Chunk = () => {
|
||||
case 'doc':
|
||||
return documentInfo?.name.split('.').pop() || 'doc';
|
||||
case 'visual':
|
||||
return documentInfo?.name.split('.').pop() || 'visual';
|
||||
case 'docx':
|
||||
case 'txt':
|
||||
case 'md':
|
||||
|
||||
@ -28,6 +28,7 @@ import {
|
||||
} from '@/components/ui/breadcrumb';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Modal } from '@/components/ui/modal/modal';
|
||||
import { AgentCategory } from '@/constants/agent';
|
||||
import { Images } from '@/constants/common';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
|
||||
@ -178,8 +179,8 @@ const Chunk = () => {
|
||||
if (knowledgeId) {
|
||||
navigateToDatasetOverview(knowledgeId)();
|
||||
}
|
||||
if (agentId) {
|
||||
navigateToAgent(agentId)();
|
||||
if (isAgent) {
|
||||
navigateToAgent(agentId, AgentCategory.DataflowCanvas)();
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@ -14,6 +14,7 @@ import { useRowSelection } from '@/hooks/logic-hooks/use-row-selection';
|
||||
import { useFetchDocumentList } from '@/hooks/use-document-request';
|
||||
import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-request';
|
||||
import { Upload } from 'lucide-react';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DatasetTable } from './dataset-table';
|
||||
import Generate from './generate-button/generate';
|
||||
@ -31,7 +32,6 @@ export default function Dataset() {
|
||||
onDocumentUploadOk,
|
||||
documentUploadLoading,
|
||||
} = useHandleUploadDocument();
|
||||
const { data: dataSetData } = useFetchKnowledgeBaseConfiguration();
|
||||
|
||||
const {
|
||||
searchString,
|
||||
@ -43,6 +43,14 @@ export default function Dataset() {
|
||||
handleFilterSubmit,
|
||||
loading,
|
||||
} = useFetchDocumentList();
|
||||
|
||||
const refreshCount = useMemo(() => {
|
||||
return documents.findIndex((doc) => doc.run === '1') + documents.length;
|
||||
}, [documents]);
|
||||
|
||||
const { data: dataSetData } = useFetchKnowledgeBaseConfiguration({
|
||||
refreshCount,
|
||||
});
|
||||
const { filters, onOpenChange } = useSelectDatasetFilters();
|
||||
|
||||
const {
|
||||
|
||||
@ -20,7 +20,7 @@ export function useUploadFile() {
|
||||
if (Array.isArray(files) && files.length) {
|
||||
const file = files[0];
|
||||
const ret = await uploadAndParseFile({ file, options, conversationId });
|
||||
if (ret.code === 0 && Array.isArray(ret.data)) {
|
||||
if (ret?.code === 0 && Array.isArray(ret?.data)) {
|
||||
setFileIds((list) => [...list, ...ret.data]);
|
||||
setFileMap((map) => {
|
||||
map.set(files[0], ret.data[0]);
|
||||
|
||||
Reference in New Issue
Block a user