feat(asset-ai): generate contextual presentations and drilldown plans
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
shishengliang
2026-09-23 17:52:33 +08:00
parent b5a48b6417
commit dc0923b566
33 changed files with 6193 additions and 1165 deletions
+68 -1
View File
@@ -5,6 +5,7 @@ export const DIMENSIONS = [
"model",
"department",
"status",
"vehicleClass",
] as const;
export const GROUPS = [...DIMENSIONS, "none"] as const;
export const CATEGORIES = [
@@ -23,7 +24,7 @@ export type QueryPlan = {
groupBy: (typeof GROUPS)[number];
secondaryGroupBy?: Dimension;
view: "chart" | "table" | "list";
analysis?: "distribution" | "compare" | "exceptions";
analysis?: "distribution" | "compare" | "exceptions" | "briefing";
filters: Partial<Record<Dimension, string[]>>;
start?: string;
end?: string;
@@ -37,6 +38,7 @@ export type QueryPlan = {
clarification?: string;
};
export type AssistantAction =
| { type: "explore"; interactionId: string; keys?: Partial<Record<Dimension, string>>; date?: string }
| { type: "drill"; keys: Partial<Record<Dimension, string>> }
| {
type: "group";
@@ -68,6 +70,7 @@ export type AssistantRequest = {
export type AssistantRow = {
id: string;
plate: string;
vehicleClass: string;
type: string;
model: string;
region: string;
@@ -83,6 +86,49 @@ export type AssistantRow = {
date?: string;
event?: string;
};
/** A model chooses a query and a renderer; values are always computed by the server. */
export type AnalysisDisplay = "metrics" | "chart" | "table" | "list";
export type PresentationBlock = {
id: string;
title?: string;
kind: "summary" | "narrative" | "metrics" | "chart" | "table";
text?: string;
metrics?: string[];
dataset?: "groups" | "rows" | "daily" | "comparison";
chart?: "bar" | "donut" | "line";
series?: { field: string; label: string }[];
columns?: { field: string; label: string }[];
interactionId?: string;
};
export type PresentationInteraction = {
id: string;
label: string;
kind: "group" | "point" | "followup";
plan: QueryPlan;
};
export type ResultPresentation = {
title?: string;
blocks: PresentationBlock[];
interactions: PresentationInteraction[];
};
export type AnalysisQuery = {
id: string;
title: string;
question: string;
plan: QueryPlan;
display: AnalysisDisplay;
};
export type AnalysisSection = Omit<AnalysisQuery, "plan"> & {
result?: AssistantResult;
error?: string;
};
export type AnalysisContext = {
id: string;
title: string;
plan: QueryPlan;
summary: string;
metrics: AssistantResult["metrics"];
};
export type AssistantGroup = {
name: string;
count: number;
@@ -120,11 +166,31 @@ export type DemandGap = {
};
export type AssistantResult = {
plan: QueryPlan;
presentation?: ResultPresentation;
presentationStatus?: "generated" | "fallback";
presentationWarning?: string;
layout?: "tabs" | "stack";
sections?: AnalysisSection[];
/** Aggregated context only: no vehicle rows are needed to refer across analyses. */
analyses?: AnalysisContext[];
answerKind?: "analysis" | "clarification" | "help";
engine?: "deepseek" | "local";
suggestions?: string[];
interpretation?: string;
contextNote?: string;
scope?: {
company: string;
permission: string;
filters: string[];
grouping: string;
period: string;
definitions: string[];
sourceUpdatedAt: string | null;
};
unanswered?: { question: string; reason: string }[];
correction?: { resultId?: string; message: string };
outcome?: "complete" | "partial" | "empty" | "unsupported" | "clarification" | "help";
inventoryBreakdown?: { title: string; summary: string; groups: AssistantGroup[] };
title: string;
summary: string;
source: string;
@@ -181,6 +247,7 @@ export const GROUP_LABELS: Record<QueryPlan["groupBy"], string> = {
model: "型号",
department: "部门",
status: "运营状态",
vehicleClass: "车型细分",
none: "全部",
};
export const CATEGORY_LABELS: Record<QueryPlan["category"], string> = {