-
Notifications
You must be signed in to change notification settings - Fork 9.2k
Refa: unify document create flows under REST documents API #14345
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3ebc804
95bff61
ccd5722
bc9355c
80e8f9c
241ea0e
77f3d8d
ed7396c
011d619
fe6c581
2839145
5d111f0
3992d40
00adce5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -15,6 +15,8 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import json | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import re | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from pathlib import Path | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from quart import request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from peewee import OperationalError | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -23,8 +25,9 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.apps import login_required | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.apps.services.document_api_service import validate_document_update_fields, map_doc_keys, \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| map_doc_keys_with_run_status, update_document_name_only, update_chunk_method_only, update_document_status_only | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.constants import IMG_BASE64_PREFIX | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.db import VALID_FILE_TYPES | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.constants import FILE_NAME_LEN_LIMIT, IMG_BASE64_PREFIX | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.db import FileType, VALID_FILE_TYPES | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.db.services import duplicate_name | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.db.services.doc_metadata_service import DocMetadataService | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.db.db_models import Task | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.db.services.document_service import DocumentService | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -38,9 +41,11 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| UpdateDocumentReq, format_validation_error_message, validate_and_parse_json_request, DeleteDocumentReq, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from common import settings | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from common.constants import RetCode, TaskStatus | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from common.constants import ParserType, RetCode, TaskStatus | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from common.metadata_utils import convert_conditions, meta_filter, turn2jsonschema | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from common.misc_utils import thread_pool_exec | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from common.misc_utils import get_uuid, thread_pool_exec | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.utils.file_utils import filename_type, thumbnail | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.utils.web_utils import html2pdf, is_valid_url | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from rag.nlp import search | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @manager.route("/datasets/<dataset_id>/documents/<document_id>", methods=["PATCH"]) # noqa: F821 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -348,13 +353,144 @@ async def upload_document(dataset_id, tenant_id): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: string | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: Processing status. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.constants import FILE_NAME_LEN_LIMIT | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from api.db.services.file_service import FileService | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| upload_type = (request.args.get("type") or "local").lower() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e, kb = KnowledgebaseService.get_by_id(dataset_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.error(f"Can't find the dataset with ID {dataset_id}!") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message=f"Can't find the dataset with ID {dataset_id}!", code=RetCode.DATA_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not check_kb_team_permission(kb, tenant_id): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.error("No authorization.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message="No authorization.", code=RetCode.AUTHENTICATION_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if upload_type == "web": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await _upload_web_document(dataset_id, kb, tenant_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if upload_type == "empty": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await _upload_empty_document(dataset_id, kb, tenant_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if upload_type != "local": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message='`type` must be one of "local", "web", or "empty".', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| code=RetCode.ARGUMENT_ERROR, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return await _upload_local_documents(kb, tenant_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def _upload_web_document(dataset_id, kb, tenant_id): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| form = await request.form | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| files = await request.files | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name = (form.get("name") or "").strip() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url = form.get("url") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not name: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message='Lack of "name"', code=RetCode.ARGUMENT_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not url: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message='Lack of "url"', code=RetCode.ARGUMENT_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if len(name.encode("utf-8")) > FILE_NAME_LEN_LIMIT: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message=f"File name must be {FILE_NAME_LEN_LIMIT} bytes or less.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| code=RetCode.ARGUMENT_ERROR, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not is_valid_url(url): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message="The URL format is invalid", code=RetCode.ARGUMENT_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| blob = html2pdf(url) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not blob: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return server_error_response(ValueError("Download failure.")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| root_folder = FileService.get_root_folder(tenant_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FileService.init_knowledgebase_docs(root_folder["id"], tenant_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| kb_root_folder = FileService.get_kb_folder(tenant_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| kb_folder = FileService.new_a_file_from_kb(kb.tenant_id, kb.name, kb_root_folder["id"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
buua436 marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| filename = duplicate_name(DocumentService.query, name=f"{name}.pdf", kb_id=kb.id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| filetype = filename_type(filename) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if filetype == FileType.OTHER.value: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| raise RuntimeError("This type of file has not been supported yet!") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| location = filename | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| while settings.STORAGE_IMPL.obj_exist(dataset_id, location): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| location += "_" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| settings.STORAGE_IMPL.put(dataset_id, location, blob) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| doc = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "id": get_uuid(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "kb_id": kb.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "parser_id": kb.parser_id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "pipeline_id": kb.pipeline_id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "parser_config": kb.parser_config, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "created_by": tenant_id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": filetype, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name": filename, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "location": location, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "size": len(blob), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "thumbnail": thumbnail(filename, blob), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "suffix": Path(filename).suffix.lstrip("."), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if doc["type"] == FileType.VISUAL: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| doc["parser_id"] = ParserType.PICTURE.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if doc["type"] == FileType.AURAL: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| doc["parser_id"] = ParserType.AUDIO.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if re.search(r"\.(ppt|pptx|pages)$", filename): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| doc["parser_id"] = ParserType.PRESENTATION.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if re.search(r"\.(eml)$", filename): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| doc["parser_id"] = ParserType.EMAIL.value | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+432
to
+439
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Inspect FileType enum definition and filename_type return type
ast-grep --pattern $'class FileType($_):
$$$'
rg -nP --type=py -C3 '\bdef\s+filename_type\s*\('Repository: infiniflow/ragflow Length of output: 824 🏁 Script executed: cd api/apps/restful_apis && sed -n '320,360p' document_api.py | cat -nRepository: infiniflow/ragflow Length of output: 2058 🏁 Script executed: rg -n "def filename_type" api/utils/file_utils.py -A 15Repository: infiniflow/ragflow Length of output: 827 🏁 Script executed: grep -n "FileType" api/apps/restful_apis/document_api.py | head -20Repository: infiniflow/ragflow Length of output: 299 🏁 Script executed: sed -n '58,85p' api/utils/file_utils.py | cat -nRepository: infiniflow/ragflow Length of output: 1404 🏁 Script executed: rg "new_kb_from_web_page" api/apps/restful_apis/document_api.py -B 5Repository: infiniflow/ragflow Length of output: 44 Remove this dead code block — filename is always The filename is constructed as
These overrides are dead code and should be removed entirely. The default If presentation/email handling is needed for crawled pages, recover the original source filename from the URL and run the type checks against that instead. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| DocumentService.insert(doc) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FileService.add_file_from_kb(doc, kb_folder["id"], kb.tenant_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_result(data=map_doc_keys_with_run_status(doc, run_status="0")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return server_error_response(e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+407
to
+445
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Orphaned storage blob on partial failure + wrong error code for unsupported type. Two smaller correctness/UX points inside the
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Validation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def _upload_empty_document(dataset_id, kb, tenant_id): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| req = await get_request_json() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name = (req.get("name") or "").strip() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not name: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message="File name can't be empty.", code=RetCode.ARGUMENT_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if len(name.encode("utf-8")) > FILE_NAME_LEN_LIMIT: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message=f"File name must be {FILE_NAME_LEN_LIMIT} bytes or less.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| code=RetCode.ARGUMENT_ERROR, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if DocumentService.query(name=name, kb_id=dataset_id): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message="Duplicated document name in the same dataset.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| kb_root_folder = FileService.get_kb_folder(kb.tenant_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not kb_root_folder: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message="Cannot find the root folder.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| kb_folder = FileService.new_a_file_from_kb(kb.tenant_id, kb.name, kb_root_folder["id"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not kb_folder: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message="Cannot find the kb folder for this file.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| doc = DocumentService.insert( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "id": get_uuid(), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "kb_id": kb.id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "parser_id": kb.parser_id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "pipeline_id": kb.pipeline_id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "parser_config": kb.parser_config, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "created_by": tenant_id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": FileType.VIRTUAL, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name": name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "suffix": Path(name).suffix.lstrip("."), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "location": "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "size": 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+470
to
+484
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Confirm how `type` is persisted / compared for documents across the codebase
ast-grep --pattern $'class FileType($_):
$$$'
rg -nP --type=py -C2 '"type"\s*:\s*FileType\.[A-Z_]+\b'
rg -nP --type=py -C2 '\btype\s*==\s*FileType\.[A-Z_]+\.value\b'Repository: infiniflow/ragflow Length of output: 7291 Store
Suggested change- "type": FileType.VIRTUAL,
+ "type": FileType.VIRTUAL.value,📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FileService.add_file_from_kb(doc.to_dict(), kb_folder["id"], kb.tenant_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_result(data=map_doc_keys(doc)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return server_error_response(e) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def _upload_local_documents(kb, tenant_id): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| form = await request.form | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| files = await request.files | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if "file" not in files: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.error("No file part!") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message="No file part!", code=RetCode.ARGUMENT_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -369,18 +505,6 @@ async def upload_document(dataset_id, tenant_id): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.error(msg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message=msg, code=RetCode.ARGUMENT_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # KB Lookup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e, kb = KnowledgebaseService.get_by_id(dataset_id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.error(f"Can't find the dataset with ID {dataset_id}!") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message=f"Can't find the dataset with ID {dataset_id}!", code=RetCode.DATA_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Permission Check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not check_kb_team_permission(kb, tenant_id): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logging.error("No authorization.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message="No authorization.", code=RetCode.AUTHENTICATION_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # File Upload (async) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err, files = await thread_pool_exec( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FileService.upload_document, kb, file_objs, tenant_id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parent_path=form.get("parent_path") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -396,8 +520,6 @@ async def upload_document(dataset_id, tenant_id): | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return get_error_data_result(message=msg, code=RetCode.DATA_ERROR) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| files = [f[0] for f in files] # remove the blob | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Check if we should return raw files without document key mapping | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return_raw_files = request.args.get("return_raw_files", "false").lower() == "true" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if return_raw_files: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blocking Selenium call on the async event loop.
html2pdf(url)launches headless Chrome via Selenium (seeapi/utils/web_utils.py:157-188) and can take many seconds. Running it synchronously inside thisasynchandler blocks the Quart event loop for every concurrent request._upload_local_documentsalready offloads heavy work viathread_pool_exec— do the same here (and ideally also for the subsequentSTORAGE_IMPL.obj_exist/STORAGE_IMPL.putwhich are blocking I/O).🛠️ Suggested change
📝 Committable suggestion
🤖 Prompt for AI Agents