feat(asset-ai): generate contextual presentations and drilldown plans
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import {
|
||||
copyResultText,
|
||||
resultScope,
|
||||
resultDisplay,
|
||||
queryPath,
|
||||
compactAnalysisPath,
|
||||
} from "./result-presentation.js";
|
||||
import type { AssistantResult } from "../../../shared/assets/assistant.js";
|
||||
|
||||
const result: AssistantResult = {
|
||||
plan: {
|
||||
kind: "assets",
|
||||
category: "available",
|
||||
groupBy: "region",
|
||||
view: "table",
|
||||
filters: { type: ["4.5T"] },
|
||||
},
|
||||
title: "可运营库存 · 按区域统计",
|
||||
summary: "当前范围共有135台可运营库存。",
|
||||
source: "资产台账",
|
||||
fetchedAt: "2026-09-23T08:00:00Z",
|
||||
groups: [],
|
||||
rows: [],
|
||||
metrics: [{ label: "可运营库存", value: 135, unit: "台" }],
|
||||
scope: {
|
||||
company: "演示公司",
|
||||
permission: "当前账号可见车辆",
|
||||
filters: ["4.5T", "可运营库存"],
|
||||
grouping: "区域",
|
||||
period: "当前资产台账",
|
||||
definitions: ["车型按吨位汇总,4.5T包含普货与冷链"],
|
||||
sourceUpdatedAt: null,
|
||||
},
|
||||
};
|
||||
|
||||
test("copy preserves facts, scope, definitions and distinguishes unknown source update from query time", () => {
|
||||
const text = copyResultText(result, null);
|
||||
for (const expected of [
|
||||
"135台",
|
||||
"公司:演示公司",
|
||||
"权限范围:当前账号可见车辆",
|
||||
"筛选条件:4.5T;可运营库存",
|
||||
"分组:区域",
|
||||
"4.5T包含普货与冷链",
|
||||
"源数据更新时间:未提供",
|
||||
"查询时间:",
|
||||
])
|
||||
assert.ok(text.includes(expected), expected);
|
||||
});
|
||||
|
||||
test("partial briefing copies missing answers and inventory evidence without inventing a source timestamp", () => {
|
||||
const partial: AssistantResult = {
|
||||
...result,
|
||||
outcome: "partial",
|
||||
unanswered: [
|
||||
{ question: "闲置损失多少钱", reason: "未接入成本数据,不估算金额。" },
|
||||
],
|
||||
inventoryBreakdown: {
|
||||
title: "库存集中情况",
|
||||
summary: "4.5T冷链库存最多。",
|
||||
groups: [
|
||||
{ name: "4.5T冷链", count: 155, operating: 0, rate: 0, share: 53.3 },
|
||||
],
|
||||
},
|
||||
};
|
||||
const text = copyResultText(partial, null);
|
||||
assert.match(text, /4.5T冷链:155台,占库存53.3%/);
|
||||
assert.match(text, /尚未回答:[\s\S]*未接入成本数据,不估算金额/);
|
||||
assert.match(text, /源数据更新时间:未提供/);
|
||||
});
|
||||
|
||||
test("copying a revoked result keeps the revocation explicit", () => {
|
||||
const text = copyResultText(
|
||||
result,
|
||||
null,
|
||||
"上一条回答的是待运营,不能用于判断待交车。",
|
||||
);
|
||||
assert.match(
|
||||
text,
|
||||
/【此回答已撤回】上一条回答的是待运营,不能用于判断待交车/,
|
||||
);
|
||||
});
|
||||
|
||||
test("fallback scope keeps exclusion and selected vehicle filters", () => {
|
||||
const fallback = resultScope(
|
||||
{
|
||||
...result,
|
||||
scope: undefined,
|
||||
plan: {
|
||||
...result.plan,
|
||||
excludeFilters: { city: ["嘉兴市"] },
|
||||
vehicleIds: ["example-1"],
|
||||
exception: "overdue",
|
||||
},
|
||||
},
|
||||
"演示公司",
|
||||
);
|
||||
assert.deepEqual(fallback.filters, [
|
||||
"可运营库存",
|
||||
"车型:4.5T",
|
||||
"排除城市:嘉兴市",
|
||||
"指定车辆:1台",
|
||||
"已超过预计完成时间",
|
||||
]);
|
||||
assert.equal(fallback.sourceUpdatedAt, null);
|
||||
});
|
||||
|
||||
test("copy includes regional facts and daily flow without duplicating groups or copying vehicle details", () => {
|
||||
const regional: AssistantResult = {
|
||||
...result,
|
||||
groups: [
|
||||
{
|
||||
name: "华东",
|
||||
count: 99,
|
||||
operating: 0,
|
||||
rate: 0,
|
||||
share: 73.3,
|
||||
abnormal: 1,
|
||||
pending: 0,
|
||||
},
|
||||
],
|
||||
insights: ["华东占当前可运营库存73.3%。"],
|
||||
rows: [
|
||||
{
|
||||
id: "private-vehicle",
|
||||
plate: "测试车牌不应复制",
|
||||
vehicleClass: "4.5T普货",
|
||||
type: "4.5T",
|
||||
model: "演示型号",
|
||||
region: "华东",
|
||||
city: "嘉兴市",
|
||||
department: "演示部门",
|
||||
status: "可运营",
|
||||
},
|
||||
],
|
||||
};
|
||||
const regionalText = copyResultText(regional, null);
|
||||
assert.match(regionalText, /华东:99台,占比73.3%/);
|
||||
assert.match(regionalText, /其中异动1台,其中待交车0台/);
|
||||
assert.match(regionalText, /分析发现:[\s\S]*华东占当前可运营库存73.3%/);
|
||||
assert.ok(!regionalText.includes("测试车牌不应复制"));
|
||||
|
||||
const flowText = copyResultText(
|
||||
{
|
||||
...regional,
|
||||
plan: {
|
||||
...regional.plan,
|
||||
kind: "flow",
|
||||
start: "2026-09-23",
|
||||
end: "2026-09-23",
|
||||
},
|
||||
daily: [
|
||||
{
|
||||
date: "2026-09-23",
|
||||
delivered: 5,
|
||||
returned: 2,
|
||||
replaced: 1,
|
||||
total: 7,
|
||||
},
|
||||
],
|
||||
},
|
||||
null,
|
||||
);
|
||||
assert.match(
|
||||
flowText,
|
||||
/2026-09-23:交车5车次,还车2车次,其中替换交车1车次,合计7车次/,
|
||||
);
|
||||
assert.match(flowText, /替换交车包含在交车中,不重复相加/);
|
||||
assert.ok(!flowText.includes("华东:99台"));
|
||||
assert.ok(!flowText.includes("测试车牌不应复制"));
|
||||
});
|
||||
|
||||
test("single totals use metric cards while comparisons, trends and explicit records retain their display", () => {
|
||||
const total = {
|
||||
...result.plan,
|
||||
groupBy: "none" as const,
|
||||
view: "chart" as const,
|
||||
};
|
||||
assert.equal(resultDisplay(total), "metrics");
|
||||
assert.equal(resultDisplay({ ...total, groupBy: "region" }), "chart");
|
||||
assert.equal(resultDisplay({ ...total, secondaryGroupBy: "type" }), "chart");
|
||||
assert.equal(resultDisplay({ ...total, kind: "flow" }), "chart");
|
||||
assert.equal(resultDisplay({ ...total, view: "list" }), "list");
|
||||
assert.equal(resultDisplay({ ...total, view: "table" }), "table");
|
||||
});
|
||||
|
||||
test("current path retains every active slice and highlights the current grouping", () => {
|
||||
const path = queryPath({
|
||||
...result.plan,
|
||||
filters: { region: ["华东"], city: ["嘉兴市"] },
|
||||
excludeFilters: { model: ["演示型号"] },
|
||||
groupBy: "vehicleClass",
|
||||
exception: "overdue",
|
||||
});
|
||||
assert.deepEqual(path, [
|
||||
"可运营库存",
|
||||
"区域:华东",
|
||||
"城市:嘉兴市",
|
||||
"排除型号:演示型号",
|
||||
"已超期",
|
||||
"按车型细分统计",
|
||||
]);
|
||||
});
|
||||
|
||||
test("analysis path collapses display-only steps and preserves the latest node of each scope", () => {
|
||||
const path = compactAnalysisPath([
|
||||
{ resultId: "all", label: "全部库存" },
|
||||
{ resultId: "east-chart", label: "库存 · 华东" },
|
||||
{ resultId: "east-table", label: "库存 · 华东" },
|
||||
{ resultId: "jiaxing", label: "库存 · 华东 / 嘉兴" },
|
||||
]);
|
||||
assert.deepEqual(
|
||||
path.map((crumb) => crumb.resultId),
|
||||
["all", "east-table", "jiaxing"],
|
||||
);
|
||||
assert.equal(path.at(-2)?.label, "库存 · 华东");
|
||||
});
|
||||
Reference in New Issue
Block a user