feat:完成大部分数据文件上传解析功能

This commit is contained in:
2026-10-08 08:23:01 -07:00
parent 05c050d2ba
commit bb6a9405db
27 changed files with 2675 additions and 18 deletions
+122
View File
@@ -0,0 +1,122 @@
"""数据文件的接口模型:文件记录、表、列和解析警告,见 docs/screen-agent/data-files.md 5.4、6.6、7
文件和表各有三层,后一层在前一层上加字段:列表接口返回的 Info、单个文件返回的 Detail、存储用的完整记录
接口返回时用对应的模型重新校验存储记录,多出的字段自然丢掉
"""
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, JsonValue
DataFormat = Literal["csv", "tsv", "xlsx", "xls", "xlsb", "json", "jsonl", "parquet"]
ColumnType = Literal["text", "number", "date", "datetime", "boolean"]
WarningCode = Literal[
"title_skipped",
"note_skipped",
"block_dropped",
"header_merged",
"header_uncertain",
"total_rows_removed",
"column_renamed",
"kept_as_text",
"bad_lines_skipped",
"json_path_selected",
"empty_sheet_skipped",
"hidden_sheet_skipped",
]
class DataColumn(BaseModel):
name: str
type: ColumnType
# 原文是百分比时为 percent,值已存为小数
format: Literal["percent"] | None = None
class ParseWarning(BaseModel):
code: WarningCode
# 表名;为空表示针对整个文件。解析时还没有分配表名,由上传流程填写
table: str | None = None
# 可以直接展示的中文说明
message: str
class DataTableInfo(BaseModel):
# 会话内的表名 t1、t2……,模型写 SQL 时用
table: str
# 文件名 / sheet,同一 sheet 切出多张表时再加序号
label: str
sheet: str | None
rows: int
columns: list[DataColumn]
class DataTableDetail(DataTableInfo):
# 前 20 行,顺序与 columns 一致
preview: list[list[JsonValue]]
class DataTable(DataTableDetail):
# 列概况,给 describe_data 用
profile: dict[str, JsonValue]
# 文件目录下的 Parquet 文件名
parquet: str
class DataFileInfo(BaseModel):
file_id: str
conversation_id: str
name: str
format: DataFormat
# 文本文件识别出的编码,二进制格式为空
encoding: str | None
size: int
created_at: datetime
expires_at: datetime
tables: list[DataTableInfo]
warnings: list[ParseWarning]
class DataFileDetail(DataFileInfo):
tables: list[DataTableDetail]
class DataFileRecord(DataFileDetail):
owner_id: int
sha256: str
tables: list[DataTable]
# 删除只做标记,记录留到过期,会话内的表名不会被新文件重复使用
deleted: bool = False
class DataFileList(BaseModel):
files: list[DataFileInfo]
class DataFileLimits(BaseModel):
max_size_mb: int
max_files_per_conversation: int
formats: list[DataFormat]
# 文件选择框的 accept,只用于前端过滤;后端按文件内容识别格式
accept: str
class RequestDataTable(DataTableInfo):
"""本次请求可用的表:多出工具要用的列概况和 Parquet 路径,不带预览"""
profile: dict[str, JsonValue]
path: str
class RequestDataFile(BaseModel):
"""本次请求可用的文件:服务端按会话读取后放进请求,前端传的同名字段会被覆盖"""
file_id: str
name: str
# 本轮消息的 attachments 里带了这个文件,通常是刚上传的
attached_this_turn: bool
tables: list[RequestDataTable]
warnings: list[ParseWarning]