feat:LLM 分析文件并生成结构化建议
This commit is contained in:
+43
-12
@@ -44,6 +44,41 @@ class ParseWarning(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
class TopValue(BaseModel):
|
||||
value: JsonValue
|
||||
count: int
|
||||
|
||||
|
||||
class ColumnProfile(DataColumn):
|
||||
"""一列的概况,上传时在全表上算出;不适用的统计为空,交给模型时省略"""
|
||||
|
||||
null_ratio: float
|
||||
# 去重值个数,是估算值
|
||||
distinct: int
|
||||
# 数值列是数字,日期、日期时间列是 ISO 写法的字符串
|
||||
min: int | float | str | None = None
|
||||
max: int | float | str | None = None
|
||||
# 只有数值列有
|
||||
avg: int | float | None = None
|
||||
q25: int | float | None = None
|
||||
q50: int | float | None = None
|
||||
q75: int | float | None = None
|
||||
# 去重值不多的文本、布尔列才有,按出现次数从多到少
|
||||
top_values: list[TopValue] | None = None
|
||||
# 开头几行里不同的取值
|
||||
examples: list[JsonValue]
|
||||
# 保留为文本的原因等说明
|
||||
note: str | None = None
|
||||
|
||||
|
||||
class TableProfile(BaseModel):
|
||||
"""表的列概况,给 describe_data 和上传后的分析用"""
|
||||
|
||||
columns: list[ColumnProfile]
|
||||
# 随机抽的几行,顺序与 columns 一致
|
||||
sample: list[list[JsonValue]]
|
||||
|
||||
|
||||
class DataTableInfo(BaseModel):
|
||||
# 会话内的表名 t1、t2……,模型写 SQL 时用
|
||||
table: str
|
||||
@@ -60,8 +95,7 @@ class DataTableDetail(DataTableInfo):
|
||||
|
||||
|
||||
class DataTable(DataTableDetail):
|
||||
# 列概况,给 describe_data 用
|
||||
profile: dict[str, JsonValue]
|
||||
profile: TableProfile
|
||||
# 文件目录下的 Parquet 文件名
|
||||
parquet: str
|
||||
|
||||
@@ -162,6 +196,8 @@ class FileInsights(BaseModel):
|
||||
version: int = 1
|
||||
# pending:正在分析;ready:已完成(推荐可能为空);failed:分析失败,文件本身可用
|
||||
status: Literal["pending", "ready", "failed"]
|
||||
# pending 时是开始分析的时间;服务启动前开始、至今仍是 pending 的分析已随重启丢失,读取时按失败返回
|
||||
started_at: datetime | None = None
|
||||
summary: str = ""
|
||||
suggestions: list[ChartSuggestion] = Field(default_factory=list)
|
||||
columns: list[ColumnInsight] = Field(default_factory=list)
|
||||
@@ -224,19 +260,14 @@ class DataFileLimits(BaseModel):
|
||||
aggregate_max_rows: int
|
||||
|
||||
|
||||
class RequestDataTable(DataTableInfo):
|
||||
"""本次请求可用的表:多出工具要用的列概况和 Parquet 路径,不带预览"""
|
||||
|
||||
profile: dict[str, JsonValue]
|
||||
path: str
|
||||
|
||||
|
||||
class RequestDataFile(BaseModel):
|
||||
"""本次请求可用的文件:服务端按会话读取后放进请求,前端传的同名字段会被覆盖"""
|
||||
"""本次请求可用的文件:服务端按会话读取后放进请求,前端传的同名字段会被覆盖
|
||||
|
||||
请求会进检查点,所以只放表名、行数和列;列概况和文件路径在图的运行上下文里,见 processor/state.py
|
||||
"""
|
||||
|
||||
file_id: str
|
||||
name: str
|
||||
# 本轮消息的 attachments 里带了这个文件,通常是刚上传的
|
||||
attached_this_turn: bool
|
||||
tables: list[RequestDataTable]
|
||||
warnings: list[ParseWarning]
|
||||
tables: list[DataTableInfo]
|
||||
|
||||
+20
-1
@@ -1,6 +1,6 @@
|
||||
"""前端设计请求:原始需求、当前大屏及可选补充上下文"""
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic import Field, field_validator
|
||||
|
||||
from .base import ExtensibleModel, JsonObject, NonBlankString
|
||||
from .screen import Screen
|
||||
@@ -20,3 +20,22 @@ class DesignRequest(ExtensibleModel):
|
||||
# 本条消息附带的材料;{"kind": "data_file", "file_id": "..."} 引用本会话上传的数据文件,
|
||||
# 其他种类只原样进入摘要,不读取文件或远程资源
|
||||
attachments: list[JsonObject] = Field(default_factory=list)
|
||||
|
||||
@field_validator("attachments")
|
||||
@classmethod
|
||||
def _data_files_have_ids(cls, value: list[JsonObject]) -> list[JsonObject]:
|
||||
for item in value:
|
||||
if item.get("kind") == "data_file" and not isinstance(
|
||||
item.get("file_id"), str
|
||||
):
|
||||
raise ValueError("data_file 附件缺少 file_id")
|
||||
return value
|
||||
|
||||
@property
|
||||
def data_file_ids(self) -> set[str]:
|
||||
"""本条消息附带的数据文件"""
|
||||
return {
|
||||
str(item["file_id"])
|
||||
for item in self.attachments
|
||||
if item.get("kind") == "data_file"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user