"""数据文件的接口模型:文件记录、表、列和解析警告,见 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]