feat:LLM 分析文件并生成结构化建议
This commit is contained in:
+121
-1
@@ -7,7 +7,7 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, JsonValue
|
||||
from pydantic import BaseModel, ConfigDict, Field, JsonValue
|
||||
|
||||
DataFormat = Literal["csv", "tsv", "xlsx", "xls", "xlsb", "json", "jsonl", "parquet"]
|
||||
|
||||
@@ -66,6 +66,107 @@ class DataTable(DataTableDetail):
|
||||
parquet: str
|
||||
|
||||
|
||||
Aggregation = Literal["sum", "avg", "min", "max", "count", "count_distinct"]
|
||||
|
||||
# 日期列按粒度分组,结果是字符串:年 2025,季度 2025-Q1,月 2025-01,日 2025-01-31
|
||||
Grain = Literal["year", "quarter", "month", "day"]
|
||||
|
||||
MAX_GROUP_BY = 2
|
||||
MAX_METRICS = 5
|
||||
MAX_AGGREGATE_ROWS = 1000
|
||||
|
||||
|
||||
class GroupBy(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
column: str
|
||||
# 只有日期、日期时间列可以指定
|
||||
grain: Grain | None = None
|
||||
|
||||
|
||||
class Metric(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
agg: Aggregation
|
||||
# count 可以不写,数整张表的行数;其他聚合必填。sum、avg 只能用于数值列
|
||||
column: str | None = None
|
||||
# 结果列名,默认沿用列名(计数类加后缀),按需指定以避免重名
|
||||
alias: str | None = Field(default=None, min_length=1, max_length=100)
|
||||
|
||||
|
||||
class OrderBy(BaseModel):
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
# 必须是结果里的列名(分组列或指标的别名)
|
||||
by: str
|
||||
direction: Literal["asc", "desc"] = "asc"
|
||||
|
||||
|
||||
class AggregateRequest(BaseModel):
|
||||
"""对一张表分组汇总:group_by 为空时整表汇总成一行"""
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
group_by: list[GroupBy] = Field(default_factory=list, max_length=MAX_GROUP_BY)
|
||||
metrics: list[Metric] = Field(min_length=1, max_length=MAX_METRICS)
|
||||
# 不写时有分组按第一个分组列升序
|
||||
order_by: OrderBy | None = None
|
||||
limit: int = Field(default=100, ge=1, le=MAX_AGGREGATE_ROWS)
|
||||
|
||||
|
||||
class AggregateResult(BaseModel):
|
||||
# 结果列名,按分组列、指标的顺序
|
||||
columns: list[str]
|
||||
# 对象数组,键是列名,可直接作为图表物料的 dataset.source
|
||||
rows: list[dict[str, JsonValue]]
|
||||
# 结果超过 limit 行,只返回了前 limit 行
|
||||
truncated: bool
|
||||
|
||||
|
||||
# 前端图表物料支持绑定汇总数据的系列类型,推荐只会给出这几种
|
||||
ChartKind = Literal["bar", "line", "pie", "funnel", "pictorialBar"]
|
||||
|
||||
ColumnRole = Literal["dimension", "measure", "time", "id", "other"]
|
||||
|
||||
MAX_SUGGESTIONS = 3
|
||||
|
||||
|
||||
class ChartSuggestion(BaseModel):
|
||||
"""一条图表推荐:一个可以直接回答的问题,加上能直接跑的汇总请求"""
|
||||
|
||||
# 给用户看的问题,如“各区域销售额对比”,也作为图表标题
|
||||
title: str
|
||||
# 针对哪张表,是文件里的表名(t1、t2……)
|
||||
table: str
|
||||
chart: ChartKind
|
||||
request: AggregateRequest
|
||||
|
||||
|
||||
class ColumnInsight(BaseModel):
|
||||
table: str
|
||||
# 原始列名,与 columns[].name 对应
|
||||
name: str
|
||||
# 中文含义,列名已经够清楚时与列名相同
|
||||
label: str
|
||||
role: ColumnRole
|
||||
# 单位,如“元”“%”“件”;没有或不确定时为空
|
||||
unit: str | None = None
|
||||
|
||||
|
||||
class FileInsights(BaseModel):
|
||||
"""上传后由 LLM 一次分析出的文件洞察:一句话说明、图表推荐、列的含义
|
||||
|
||||
后续要加的分析结果都往这里加带默认值的字段,version 随字段含义变化递增
|
||||
"""
|
||||
|
||||
version: int = 1
|
||||
# pending:正在分析;ready:已完成(推荐可能为空);failed:分析失败,文件本身可用
|
||||
status: Literal["pending", "ready", "failed"]
|
||||
summary: str = ""
|
||||
suggestions: list[ChartSuggestion] = Field(default_factory=list)
|
||||
columns: list[ColumnInsight] = Field(default_factory=list)
|
||||
|
||||
|
||||
class DataFileInfo(BaseModel):
|
||||
file_id: str
|
||||
conversation_id: str
|
||||
@@ -78,6 +179,8 @@ class DataFileInfo(BaseModel):
|
||||
expires_at: datetime
|
||||
tables: list[DataTableInfo]
|
||||
warnings: list[ParseWarning]
|
||||
# 未启用智能分析,或这是功能上线前上传的文件时为空
|
||||
insights: FileInsights | None = None
|
||||
|
||||
|
||||
class DataFileDetail(DataFileInfo):
|
||||
@@ -96,12 +199,29 @@ class DataFileList(BaseModel):
|
||||
files: list[DataFileInfo]
|
||||
|
||||
|
||||
class DataTableRows(BaseModel):
|
||||
"""一张表的一页数据,用于预览时翻页看全表"""
|
||||
|
||||
# 表的总行数(与文件记录里的 rows 一致),前端据此决定滚动长度
|
||||
total: int
|
||||
offset: int
|
||||
# 这一页的行,顺序与 columns 一致;到末尾时可能少于请求的 limit
|
||||
rows: list[list[JsonValue]]
|
||||
|
||||
|
||||
class DataFileLimits(BaseModel):
|
||||
max_size_mb: int
|
||||
max_files_per_conversation: int
|
||||
formats: list[DataFormat]
|
||||
# 文件选择框的 accept,只用于前端过滤;后端按文件内容识别格式
|
||||
accept: str
|
||||
# 同时解析的文件数,其余排队;前端据此决定并发上传几个
|
||||
parse_concurrency: int
|
||||
# 翻页取表数据时单页行数上限
|
||||
rows_max_page: int
|
||||
# 分组汇总的指标个数上限、返回行数上限
|
||||
aggregate_max_metrics: int
|
||||
aggregate_max_rows: int
|
||||
|
||||
|
||||
class RequestDataTable(DataTableInfo):
|
||||
|
||||
Reference in New Issue
Block a user