Compare commits

...

5 Commits

Author SHA1 Message Date
e841b09d63 Remove unused code and fix performance issue (#11284)
### What problem does this PR solve?

1. remove redundant code
2. fix miner performance issue

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
- [x] Refactoring

Signed-off-by: Jin Hai <haijin.chn@gmail.com>
2025-11-14 20:39:54 +08:00
b1a1eedf53 Doc: add default username & pwd (#11283)
### What problem does this PR solve?
Doc: add default username & pwd

### Type of change

- [x] Documentation Update

---------

Co-authored-by: writinwaters <93570324+writinwaters@users.noreply.github.com>
2025-11-14 19:52:58 +08:00
68e3b33ae4 Feat: extract message output to file (#11251)
### What problem does this PR solve?

Feat: extract message output to file

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
2025-11-14 19:52:11 +08:00
cd55f6c1b8 Fix:ListOperations does not support sorting arrays of objects. (#11278)
### What problem does this PR solve?

pr:
#11276
change:
ListOperations does not support sorting arrays of objects.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-11-14 19:50:29 +08:00
996b5fe14e Fix: Added the ability to download files in the agent message reply function. (#11281)
### What problem does this PR solve?

Fix: Added the ability to download files in the agent message reply
function.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
2025-11-14 19:50:01 +08:00
20 changed files with 244 additions and 17 deletions

View File

@ -51,7 +51,9 @@ RUN --mount=type=cache,id=ragflow_apt,target=/var/cache/apt,sharing=locked \
apt install -y libpython3-dev libgtk-4-1 libnss3 xdg-utils libgbm-dev && \ apt install -y libpython3-dev libgtk-4-1 libnss3 xdg-utils libgbm-dev && \
apt install -y libjemalloc-dev && \ apt install -y libjemalloc-dev && \
apt install -y python3-pip pipx nginx unzip curl wget git vim less && \ apt install -y python3-pip pipx nginx unzip curl wget git vim less && \
apt install -y ghostscript apt install -y ghostscript && \
apt install -y pandoc && \
apt install -y texlive
RUN if [ "$NEED_MIRROR" == "1" ]; then \ RUN if [ "$NEED_MIRROR" == "1" ]; then \
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && \ pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && \

View File

@ -298,8 +298,6 @@ class Canvas(Graph):
for kk, vv in kwargs["webhook_payload"].items(): for kk, vv in kwargs["webhook_payload"].items():
self.components[k]["obj"].set_output(kk, vv) self.components[k]["obj"].set_output(kk, vv)
self.components[k]["obj"].reset(True)
for k in kwargs.keys(): for k in kwargs.keys():
if k in ["query", "user_id", "files"] and kwargs[k]: if k in ["query", "user_id", "files"] and kwargs[k]:
if k == "files": if k == "files":
@ -408,6 +406,10 @@ class Canvas(Graph):
else: else:
yield decorate("message", {"content": cpn_obj.output("content")}) yield decorate("message", {"content": cpn_obj.output("content")})
cite = re.search(r"\[ID:[ 0-9]+\]", cpn_obj.output("content")) cite = re.search(r"\[ID:[ 0-9]+\]", cpn_obj.output("content"))
if isinstance(cpn_obj.output("attachment"), tuple):
yield decorate("message", {"attachment": cpn_obj.output("attachment")})
yield decorate("message_end", {"reference": self.get_reference() if cite else None}) yield decorate("message_end", {"reference": self.get_reference() if cite else None})
while partials: while partials:

View File

@ -463,12 +463,15 @@ class ComponentBase(ABC):
return self._param.outputs.get("_ERROR", {}).get("value") return self._param.outputs.get("_ERROR", {}).get("value")
def reset(self, only_output=False): def reset(self, only_output=False):
for k in self._param.outputs.keys(): outputs: dict = self._param.outputs # for better performance
self._param.outputs[k]["value"] = None for k in outputs.keys():
outputs[k]["value"] = None
if only_output: if only_output:
return return
for k in self._param.inputs.keys():
self._param.inputs[k]["value"] = None inputs: dict = self._param.inputs # for better performance
for k in inputs.keys():
inputs[k]["value"] = None
self._param.debug_inputs = {} self._param.debug_inputs = {}
def get_input(self, key: str=None) -> Union[Any, dict[str, Any]]: def get_input(self, key: str=None) -> Union[Any, dict[str, Any]]:

View File

@ -121,10 +121,26 @@ class ListOperations(ComponentBase,ABC):
return False return False
def _sort(self): def _sort(self):
if self._param.sort_method == "asc": items = self.inputs or []
self._set_outputs(sorted(self.inputs)) method = getattr(self._param, "sort_method", "asc") or "asc"
elif self._param.sort_method == "desc": reverse = method == "desc"
self._set_outputs(sorted(self.inputs, reverse=True))
if not items:
self._set_outputs([])
return
first = items[0]
if isinstance(first, dict):
outputs = sorted(
items,
key=lambda x: self._hashable(x),
reverse=reverse,
)
else:
outputs = sorted(items, reverse=reverse)
self._set_outputs(outputs)
def _drop_duplicates(self): def _drop_duplicates(self):
seen = set() seen = set()
@ -145,5 +161,6 @@ class ListOperations(ComponentBase,ABC):
if isinstance(x, set): if isinstance(x, set):
return tuple(sorted(self._hashable(v) for v in x)) return tuple(sorted(self._hashable(v) for v in x))
return x return x
def thoughts(self) -> str: def thoughts(self) -> str:
return "ListOperation in progress" return "ListOperation in progress"

View File

@ -17,6 +17,9 @@ import json
import os import os
import random import random
import re import re
import pypandoc
import logging
import tempfile
from functools import partial from functools import partial
from typing import Any from typing import Any
@ -24,7 +27,8 @@ from agent.component.base import ComponentBase, ComponentParamBase
from jinja2 import Template as Jinja2Template from jinja2 import Template as Jinja2Template
from common.connection_utils import timeout from common.connection_utils import timeout
from common.misc_utils import get_uuid
from common import settings
class MessageParam(ComponentParamBase): class MessageParam(ComponentParamBase):
""" """
@ -34,6 +38,7 @@ class MessageParam(ComponentParamBase):
super().__init__() super().__init__()
self.content = [] self.content = []
self.stream = True self.stream = True
self.output_format = None # default output format
self.outputs = { self.outputs = {
"content": { "content": {
"type": "str" "type": "str"
@ -133,6 +138,7 @@ class Message(ComponentBase):
yield rand_cnt[s: ] yield rand_cnt[s: ]
self.set_output("content", all_content) self.set_output("content", all_content)
self._convert_content(all_content)
def _is_jinjia2(self, content:str) -> bool: def _is_jinjia2(self, content:str) -> bool:
patt = [ patt = [
@ -164,6 +170,68 @@ class Message(ComponentBase):
content = re.sub(n, v, content) content = re.sub(n, v, content)
self.set_output("content", content) self.set_output("content", content)
self._convert_content(content)
def thoughts(self) -> str: def thoughts(self) -> str:
return "" return ""
def _convert_content(self, content):
doc_id = get_uuid()
if self._param.output_format.lower() not in {"markdown", "html", "pdf", "docx"}:
self._param.output_format = "markdown"
try:
if self._param.output_format in {"markdown", "html"}:
if isinstance(content, str):
converted = pypandoc.convert_text(
content,
to=self._param.output_format,
format="markdown",
)
else:
converted = pypandoc.convert_file(
content,
to=self._param.output_format,
format="markdown",
)
binary_content = converted.encode("utf-8")
else: # pdf, docx
with tempfile.NamedTemporaryFile(suffix=f".{self._param.output_format}", delete=False) as tmp:
tmp_name = tmp.name
try:
if isinstance(content, str):
pypandoc.convert_text(
content,
to=self._param.output_format,
format="markdown",
outputfile=tmp_name,
)
else:
pypandoc.convert_file(
content,
to=self._param.output_format,
format="markdown",
outputfile=tmp_name,
)
with open(tmp_name, "rb") as f:
binary_content = f.read()
finally:
if os.path.exists(tmp_name):
os.remove(tmp_name)
settings.STORAGE_IMPL.put(self._canvas._tenant_id, doc_id, binary_content)
self.set_output("attachment", {
"doc_id":doc_id,
"format":self._param.output_format,
"file_name":f"{doc_id[:8]}.{self._param.output_format}"})
logging.info(f"Converted content uploaded as {doc_id} (format={self._param.output_format})")
except Exception as e:
logging.error(f"Error converting content to {self._param.output_format}: {e}")

View File

@ -508,6 +508,7 @@ def get(doc_id):
ext = ext.group(1) if ext else None ext = ext.group(1) if ext else None
if ext: if ext:
if doc.type == FileType.VISUAL.value: if doc.type == FileType.VISUAL.value:
content_type = CONTENT_TYPE_MAP.get(ext, f"image/{ext}") content_type = CONTENT_TYPE_MAP.get(ext, f"image/{ext}")
else: else:
content_type = CONTENT_TYPE_MAP.get(ext, f"application/{ext}") content_type = CONTENT_TYPE_MAP.get(ext, f"application/{ext}")
@ -517,6 +518,22 @@ def get(doc_id):
return server_error_response(e) return server_error_response(e)
@manager.route("/download/<attachment_id>", methods=["GET"]) # noqa: F821
@login_required
def download_attachment(attachment_id):
try:
ext = request.args.get("ext", "markdown")
data = settings.STORAGE_IMPL.get(current_user.id, attachment_id)
# data = settings.STORAGE_IMPL.get("eb500d50bb0411f0907561d2782adda5", attachment_id)
response = flask.make_response(data)
response.headers.set("Content-Type", CONTENT_TYPE_MAP.get(ext, f"application/{ext}"))
return response
except Exception as e:
return server_error_response(e)
@manager.route("/change_parser", methods=["POST"]) # noqa: F821 @manager.route("/change_parser", methods=["POST"]) # noqa: F821
@login_required @login_required
@validate_request("doc_id") @validate_request("doc_id")

View File

@ -12,6 +12,10 @@ The RAGFlow Admin UI is a web-based interface that provides comprehensive system
To access the RAGFlow admin UI, append `/admin` to the web UI's address, e.g. `http://[RAGFLOW_WEB_UI_ADDR]/admin`, replace `[RAGFLOW_WEB_UI_ADDR]` with real RAGFlow web UI address. To access the RAGFlow admin UI, append `/admin` to the web UI's address, e.g. `http://[RAGFLOW_WEB_UI_ADDR]/admin`, replace `[RAGFLOW_WEB_UI_ADDR]` with real RAGFlow web UI address.
### Default Credentials
| Username | Password |
|----------|----------|
| admin@ragflow.io | admin |
## Admin UI Overview ## Admin UI Overview

View File

@ -145,6 +145,7 @@ dependencies = [
"markdownify>=1.2.0", "markdownify>=1.2.0",
"captcha>=0.7.1", "captcha>=0.7.1",
"pip>=25.2", "pip>=25.2",
"pypandoc>=1.16",
] ]
[dependency-groups] [dependency-groups]

10
uv.lock generated
View File

@ -4892,6 +4892,14 @@ wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/28/2659c02301b9500751f8d42f9a6632e1508aa5120de5e43042b8b30f8d5d/pyopenssl-25.1.0-py3-none-any.whl", hash = "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab", size = 56771, upload-time = "2025-05-17T16:28:29.197Z" }, { url = "https://pypi.tuna.tsinghua.edu.cn/packages/80/28/2659c02301b9500751f8d42f9a6632e1508aa5120de5e43042b8b30f8d5d/pyopenssl-25.1.0-py3-none-any.whl", hash = "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab", size = 56771, upload-time = "2025-05-17T16:28:29.197Z" },
] ]
[[package]]
name = "pypandoc"
version = "1.16"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/24/77/af1fc54740a0712988f9518e629d38edc7b8ffccd7549203f19c3d8a2db6/pypandoc-1.16-py3-none-any.whl", hash = "sha256:868f390d48388743e7a5885915cbbaa005dea36a825ecdfd571f8c523416c822", size = 19425, upload-time = "2025-11-08T15:44:38.429Z" },
]
[[package]] [[package]]
name = "pyparsing" name = "pyparsing"
version = "3.2.3" version = "3.2.3"
@ -5292,6 +5300,7 @@ dependencies = [
{ name = "pyicu" }, { name = "pyicu" },
{ name = "pymysql" }, { name = "pymysql" },
{ name = "pyodbc" }, { name = "pyodbc" },
{ name = "pypandoc" },
{ name = "pypdf" }, { name = "pypdf" },
{ name = "pypdf2" }, { name = "pypdf2" },
{ name = "python-calamine" }, { name = "python-calamine" },
@ -5447,6 +5456,7 @@ requires-dist = [
{ name = "pyicu", specifier = ">=2.15.3,<3.0.0" }, { name = "pyicu", specifier = ">=2.15.3,<3.0.0" },
{ name = "pymysql", specifier = ">=1.1.1,<2.0.0" }, { name = "pymysql", specifier = ">=1.1.1,<2.0.0" },
{ name = "pyodbc", specifier = ">=5.2.0,<6.0.0" }, { name = "pyodbc", specifier = ">=5.2.0,<6.0.0" },
{ name = "pypandoc", specifier = ">=1.16" },
{ name = "pypdf", specifier = "==6.0.0" }, { name = "pypdf", specifier = "==6.0.0" },
{ name = "pypdf2", specifier = ">=3.0.1,<4.0.0" }, { name = "pypdf2", specifier = ">=3.0.1,<4.0.0" },
{ name = "python-calamine", specifier = ">=0.4.0" }, { name = "python-calamine", specifier = ">=0.4.0" },

View File

@ -18,8 +18,10 @@ import { cn } from '@/lib/utils';
import { AgentChatContext } from '@/pages/agent/context'; import { AgentChatContext } from '@/pages/agent/context';
import { WorkFlowTimeline } from '@/pages/agent/log-sheet/workflow-timeline'; import { WorkFlowTimeline } from '@/pages/agent/log-sheet/workflow-timeline';
import { IMessage } from '@/pages/chat/interface'; import { IMessage } from '@/pages/chat/interface';
import { downloadFile } from '@/services/file-manager-service';
import { downloadFileFromBlob } from '@/utils/file-util';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { Atom, ChevronDown, ChevronUp } from 'lucide-react'; import { Atom, ChevronDown, ChevronUp, Download } from 'lucide-react';
import MarkdownContent from '../next-markdown-content'; import MarkdownContent from '../next-markdown-content';
import { RAGFlowAvatar } from '../ragflow-avatar'; import { RAGFlowAvatar } from '../ragflow-avatar';
import { useTheme } from '../theme-provider'; import { useTheme } from '../theme-provider';
@ -245,6 +247,32 @@ function MessageItem({
{isUser && ( {isUser && (
<UploadedMessageFiles files={item.files}></UploadedMessageFiles> <UploadedMessageFiles files={item.files}></UploadedMessageFiles>
)} )}
{isAssistant && item.attachment && item.attachment.doc_id && (
<div className="w-full flex items-center justify-end">
<Button
variant="link"
className="p-1 m-0 h-auto text-text-sub-title-invert"
onClick={async () => {
if (item.attachment?.doc_id) {
try {
const response = await downloadFile({
docId: item.attachment.doc_id,
ext: item.attachment.format,
});
const blob = new Blob([response.data], {
type: response.data.type,
});
downloadFileFromBlob(blob, item.attachment.file_name);
} catch (error) {
console.error('Download failed:', error);
}
}
}}
>
<Download size={16} />
</Button>
</div>
)}
</section> </section>
</div> </div>
</section> </section>

View File

@ -44,9 +44,14 @@ export interface IInputData {
inputs: Record<string, BeginQuery>; inputs: Record<string, BeginQuery>;
tips: string; tips: string;
} }
export interface IAttachment {
doc_id: string;
format: string;
file_name: string;
}
export interface IMessageData { export interface IMessageData {
content: string; content: string;
outputs: any;
start_to_think?: boolean; start_to_think?: boolean;
end_to_think?: boolean; end_to_think?: boolean;
} }

View File

@ -1,4 +1,5 @@
import { MessageType } from '@/constants/chat'; import { MessageType } from '@/constants/chat';
import { IAttachment } from '@/hooks/use-send-message';
export interface PromptConfig { export interface PromptConfig {
empty_response: string; empty_response: string;
@ -97,6 +98,7 @@ export interface Message {
data?: any; data?: any;
files?: File[]; files?: File[];
chatBoxId?: string; chatBoxId?: string;
attachment?: IAttachment;
} }
export interface IReferenceChunk { export interface IReferenceChunk {
@ -126,6 +128,7 @@ export interface IReferenceObject {
export interface IAnswer { export interface IAnswer {
answer: string; answer: string;
attachment?: IAttachment;
reference?: IReference; reference?: IReference;
conversationId?: string; conversationId?: string;
prompt?: string; prompt?: string;

View File

@ -1009,6 +1009,8 @@ Example: general/v2/`,
pleaseUploadAtLeastOneFile: 'Please upload at least one file', pleaseUploadAtLeastOneFile: 'Please upload at least one file',
}, },
flow: { flow: {
downloadFileTypeTip: 'The file type to download',
downloadFileType: 'Download file type',
formatTypeError: 'Format or type error', formatTypeError: 'Format or type error',
variableNameMessage: variableNameMessage:
'Variable name can only contain letters and underscores', 'Variable name can only contain letters and underscores',

View File

@ -956,6 +956,8 @@ General实体和关系提取提示来自 GitHub - microsoft/graphrag基于
pleaseUploadAtLeastOneFile: '请上传至少一个文件', pleaseUploadAtLeastOneFile: '请上传至少一个文件',
}, },
flow: { flow: {
downloadFileTypeTip: '文件下载的类型',
downloadFileType: '文件类型',
formatTypeError: '格式或类型错误', formatTypeError: '格式或类型错误',
variableNameMessage: '名称只能包含字母和下划线', variableNameMessage: '名称只能包含字母和下划线',
variableDescription: '变量的描述', variableDescription: '变量的描述',

View File

@ -5,6 +5,7 @@ import {
useSelectDerivedMessages, useSelectDerivedMessages,
} from '@/hooks/logic-hooks'; } from '@/hooks/logic-hooks';
import { import {
IAttachment,
IEventList, IEventList,
IInputEvent, IInputEvent,
IMessageEndData, IMessageEndData,
@ -75,9 +76,13 @@ export function findMessageFromList(eventList: IEventList) {
nextContent += '</think>'; nextContent += '</think>';
} }
const workflowFinished = eventList.find(
(x) => x.event === MessageEventType.WorkflowFinished,
) as IMessageEvent;
return { return {
id: eventList[0]?.message_id, id: eventList[0]?.message_id,
content: nextContent, content: nextContent,
attachment: workflowFinished?.data?.outputs?.attachment || {},
}; };
} }
@ -388,12 +393,13 @@ export const useSendAgentMessage = ({
}, [sendMessageInTaskMode]); }, [sendMessageInTaskMode]);
useEffect(() => { useEffect(() => {
const { content, id } = findMessageFromList(answerList); const { content, id, attachment } = findMessageFromList(answerList);
const inputAnswer = findInputFromList(answerList); const inputAnswer = findInputFromList(answerList);
const answer = content || getLatestError(answerList); const answer = content || getLatestError(answerList);
if (answerList.length > 0) { if (answerList.length > 0) {
addNewestOneAnswer({ addNewestOneAnswer({
answer: answer ?? '', answer: answer ?? '',
attachment: attachment as IAttachment,
id: id, id: id,
...inputAnswer, ...inputAnswer,
}); });

View File

@ -417,6 +417,7 @@ export const initialIterationValues = {
items_ref: '', items_ref: '',
outputs: {}, outputs: {},
}; };
export const initialIterationStartValues = { export const initialIterationStartValues = {
outputs: { outputs: {
item: { item: {
@ -845,3 +846,10 @@ export enum JsonSchemaDataType {
Array = 'array', Array = 'array',
Object = 'object', Object = 'object',
} }
export enum ExportFileType {
PDF = 'pdf',
HTML = 'html',
Markdown = 'md',
DOCX = 'docx',
}

View File

@ -8,12 +8,14 @@ import {
FormLabel, FormLabel,
FormMessage, FormMessage,
} from '@/components/ui/form'; } from '@/components/ui/form';
import { RAGFlowSelect } from '@/components/ui/select';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { X } from 'lucide-react'; import { X } from 'lucide-react';
import { memo } from 'react'; import { memo } from 'react';
import { useFieldArray, useForm } from 'react-hook-form'; import { useFieldArray, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { z } from 'zod'; import { z } from 'zod';
import { ExportFileType } from '../../constant';
import { INextOperatorForm } from '../../interface'; import { INextOperatorForm } from '../../interface';
import { FormWrapper } from '../components/form-wrapper'; import { FormWrapper } from '../components/form-wrapper';
import { PromptEditor } from '../components/prompt-editor'; import { PromptEditor } from '../components/prompt-editor';
@ -33,10 +35,14 @@ function MessageForm({ node }: INextOperatorForm) {
}), }),
) )
.optional(), .optional(),
output_format: z.string().optional(),
}); });
const form = useForm({ const form = useForm({
defaultValues: values, defaultValues: {
...values,
output_format: values.output_format,
},
resolver: zodResolver(FormSchema), resolver: zodResolver(FormSchema),
}); });
@ -50,6 +56,39 @@ function MessageForm({ node }: INextOperatorForm) {
return ( return (
<Form {...form}> <Form {...form}>
<FormWrapper> <FormWrapper>
<FormContainer>
<FormItem>
<FormLabel tooltip={t('flow.downloadFileTypeTip')}>
{t('flow.downloadFileType')}
</FormLabel>
<FormField
control={form.control}
name={`output_format`}
render={({ field }) => (
<FormItem className="flex-1">
<FormControl>
<RAGFlowSelect
options={Object.keys(ExportFileType).map(
(key: string) => {
return {
value:
ExportFileType[
key as keyof typeof ExportFileType
],
label: key,
};
},
)}
{...field}
onValueChange={field.onChange}
placeholder={t('flow.messagePlaceholder')}
></RAGFlowSelect>
</FormControl>
</FormItem>
)}
/>
</FormItem>
</FormContainer>
<FormContainer> <FormContainer>
<FormItem> <FormItem>
<FormLabel tooltip={t('flow.msgTip')}>{t('flow.msg')}</FormLabel> <FormLabel tooltip={t('flow.msgTip')}>{t('flow.msg')}</FormLabel>

View File

@ -1,7 +1,7 @@
import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { RAGFlowNodeType } from '@/interfaces/database/flow';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { initialMessageValues } from '../../constant'; import { ExportFileType, initialMessageValues } from '../../constant';
import { convertToObjectArray } from '../../utils'; import { convertToObjectArray } from '../../utils';
export function useValues(node?: RAGFlowNodeType) { export function useValues(node?: RAGFlowNodeType) {
@ -15,6 +15,7 @@ export function useValues(node?: RAGFlowNodeType) {
return { return {
...formData, ...formData,
content: convertToObjectArray(formData.content), content: convertToObjectArray(formData.content),
output_format: formData.output_format || ExportFileType.PDF,
}; };
}, [node]); }, [node]);

View File

@ -13,6 +13,7 @@ const {
get_document_file, get_document_file,
getFile, getFile,
moveFile, moveFile,
get_document_file_download,
} = api; } = api;
const methods = { const methods = {
@ -65,4 +66,10 @@ const fileManagerService = registerServer<keyof typeof methods>(
request, request,
); );
export const downloadFile = (data: { docId: string; ext: string }) => {
return request.get(get_document_file_download(data.docId), {
params: { ext: data.ext },
responseType: 'blob',
});
};
export default fileManagerService; export default fileManagerService;

View File

@ -100,6 +100,8 @@ export default {
document_change_parser: `${api_host}/document/change_parser`, document_change_parser: `${api_host}/document/change_parser`,
document_thumbnails: `${api_host}/document/thumbnails`, document_thumbnails: `${api_host}/document/thumbnails`,
get_document_file: `${api_host}/document/get`, get_document_file: `${api_host}/document/get`,
get_document_file_download: (docId: string) =>
`${api_host}/document/download/${docId}`,
document_upload: `${api_host}/document/upload`, document_upload: `${api_host}/document/upload`,
web_crawl: `${api_host}/document/web_crawl`, web_crawl: `${api_host}/document/web_crawl`,
document_infos: `${api_host}/document/infos`, document_infos: `${api_host}/document/infos`,