Fix: table parser metadata (#15127)

### What problem does this PR solve?

This PR improves the table upload flow for CSV/Excel files by allowing
table column role configuration at upload time.

Previously, users had to:
1. Upload and parse a table file.
2. Open parser settings and manually set table column roles.
3. Re-parse the file for the roles to take effect.

This was inefficient and required an unnecessary second parse.

With this change:
1. When the knowledge base uses table parsing, the upload dialog
extracts CSV/Excel headers client-side.
2. Users can choose Auto mode or Manual mode.
3. In Manual mode, users can assign per-column roles before upload.
4. The selected parser config is sent with the upload request and
applied server-side during document creation.

Result: configured table column roles are applied from the first parse.

### Type of change

- [x] New Feature (non-breaking change which adds functionality)

Co-authored-by: Ahmad Intisar <ahmadintisar@Ahmads-MacBook-M4-Pro.local>
This commit is contained in:
Ahmad Intisar
2026-05-25 13:05:38 +05:00
committed by GitHub
parent e7d45dd645
commit e6068a7f7e
7 changed files with 316 additions and 26 deletions

View File

@@ -585,9 +585,25 @@ async def _upload_local_documents(kb, tenant_id):
logging.error(msg)
return get_error_data_result(message=msg, code=RetCode.ARGUMENT_ERROR)
# Parse optional parser_config overrides from form data
parser_config_override = None
raw_parser_config = form.get("parser_config")
if raw_parser_config:
try:
parsed = json.loads(raw_parser_config)
if isinstance(parsed, dict):
# Only allow known table column config keys to prevent arbitrary overrides
allowed_keys = {"table_column_mode", "table_column_roles"}
parser_config_override = {k: v for k, v in parsed.items() if k in allowed_keys}
if not parser_config_override:
parser_config_override = None
except (json.JSONDecodeError, TypeError):
parser_config_override = None
err, files = await thread_pool_exec(
FileService.upload_document, kb, file_objs, tenant_id,
parent_path=form.get("parent_path")
parent_path=form.get("parent_path"),
parser_config_override=parser_config_override,
)
if err:
msg = "\n".join(err)

View File

@@ -455,7 +455,7 @@ class FileService(CommonService):
@classmethod
@DB.connection_context()
def upload_document(self, kb, file_objs, user_id, src="local", parent_path: str | None = None):
def upload_document(self, kb, file_objs, user_id, src="local", parent_path: str | None = None, parser_config_override: dict | None = None):
root_folder = self.get_root_folder(user_id)
pf_id = root_folder["id"]
self.init_knowledgebase_docs(pf_id, user_id)
@@ -464,6 +464,13 @@ class FileService(CommonService):
safe_parent_path = sanitize_path(parent_path)
# Merge parser_config_override with KB parser_config if provided
base_parser_config = kb.parser_config or {}
if parser_config_override and isinstance(parser_config_override, dict):
merged_parser_config = {**base_parser_config, **parser_config_override}
else:
merged_parser_config = base_parser_config
err, files = [], []
for file in file_objs:
doc_id = file.id if hasattr(file, "id") else get_uuid()
@@ -529,7 +536,7 @@ class FileService(CommonService):
"kb_id": kb.id,
"parser_id": self.get_parser(filetype, filename, kb.parser_id),
"pipeline_id": kb.pipeline_id,
"parser_config": kb.parser_config,
"parser_config": merged_parser_config,
"created_by": user_id,
"type": filetype,
"name": filename,