feat: expand intelligent investigation workspace
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
import type { AnswerUiBlock } from "@/features/agent";
|
||||
import type { InvestigationPresentationCommand } from "../api/investigation-presentation";
|
||||
import type { InvestigationMapTarget } from "../api/investigation-report";
|
||||
import {
|
||||
createInvestigationDevSample,
|
||||
createPresentationRecord,
|
||||
createUiRecord,
|
||||
normalizeDevCenter,
|
||||
serializeInvestigationDevJsonl
|
||||
} from "./investigation-dev-preview";
|
||||
|
||||
export const investigationDevCases = [
|
||||
{
|
||||
id: "combined",
|
||||
label: "组合联动",
|
||||
expected: "合成空间点、压力曲线、候选表格与数据一同展示;有调查地图目标时同时高亮。"
|
||||
},
|
||||
{
|
||||
id: "map_targets",
|
||||
label: "地图定位与高亮",
|
||||
expected: "显示地图中心附近的合成点,并高亮所选调查已有的地图目标;无目标时仅显示合成点。"
|
||||
},
|
||||
{
|
||||
id: "pressure_line",
|
||||
label: "压力趋势对比",
|
||||
expected: "打开两条合成压力曲线,核对时间轴、单位、图例和数据表。"
|
||||
},
|
||||
{
|
||||
id: "impact_bar",
|
||||
label: "影响用户统计",
|
||||
expected: "打开合成用户数柱状图;统计仅供展示测试,不是实际停水户数。"
|
||||
},
|
||||
{
|
||||
id: "impact_area",
|
||||
label: "合成影响范围",
|
||||
expected: "在当前地图中心附近显示一个合成面;不代表真实隔离边界。"
|
||||
},
|
||||
{
|
||||
id: "complaint_heatmap",
|
||||
label: "投诉热力分布",
|
||||
expected: "显示三个带非负权重的合成投诉点,按热力模式渲染。"
|
||||
},
|
||||
{
|
||||
id: "candidates_table",
|
||||
label: "候选管段对比",
|
||||
expected: "展示两条合成候选数据和对比表格,不查询或高亮虚拟管段。"
|
||||
},
|
||||
{
|
||||
id: "valves_table",
|
||||
label: "隔离阀门清单",
|
||||
expected: "展示两条合成阀门数据和核查表格,不执行关阀或发布处置方案。"
|
||||
},
|
||||
{
|
||||
id: "data_only",
|
||||
label: "纯 JSONL 数据",
|
||||
expected: "仅展示候选与影响数据;数据行中出现 action 字段也不会触发地图动作。"
|
||||
},
|
||||
{
|
||||
id: "invalid_json",
|
||||
label: "错误:JSON 语法",
|
||||
expected: "第 2 行报语法错误,整批不执行;可点击执行验证拦截结果。"
|
||||
},
|
||||
{
|
||||
id: "invalid_command",
|
||||
label: "错误:不支持的动作",
|
||||
expected: "第 1 行展示指令被拒绝,不执行任意命令。"
|
||||
},
|
||||
{
|
||||
id: "invalid_chart",
|
||||
label: "错误:图表数据不齐",
|
||||
expected: "第 1 行图表校验失败,时间轴和序列长度不一致时不能执行。"
|
||||
}
|
||||
] as const;
|
||||
|
||||
export type InvestigationDevCaseId = (typeof investigationDevCases)[number]["id"];
|
||||
|
||||
const jsonl = serializeInvestigationDevJsonl;
|
||||
const presentation = createPresentationRecord;
|
||||
// Invalid cases deliberately bypass the validated writer to exercise rejection.
|
||||
const invalidJsonl = (records: unknown[]) =>
|
||||
records.map((record) => JSON.stringify(record)).join("\n");
|
||||
const data = (record_type: string, fields: Record<string, unknown>) => ({
|
||||
record_type,
|
||||
origin: "synthetic",
|
||||
source_evidence_refs: [],
|
||||
...fields
|
||||
});
|
||||
|
||||
function table(id: string, title: string, columns: string[], rows: string[][]): unknown {
|
||||
const block: AnswerUiBlock = {
|
||||
version: "answer-ui@1",
|
||||
block_id: `dev-case-${id}`,
|
||||
fallback_text: `${title},仅供本地展示测试。`,
|
||||
spec: {
|
||||
root: "table",
|
||||
elements: {
|
||||
table: { type: "Table", props: { title, columns, rows }, children: [] }
|
||||
}
|
||||
}
|
||||
};
|
||||
return createUiRecord(block);
|
||||
}
|
||||
|
||||
/** Fixtures use current map coordinates and existing task targets, never invented query IDs. */
|
||||
export function createInvestigationDevCase(
|
||||
caseId: InvestigationDevCaseId,
|
||||
center: [number, number],
|
||||
target?: InvestigationMapTarget
|
||||
): string {
|
||||
const [longitude, latitude] = normalizeDevCenter(center);
|
||||
if (caseId === "combined") return createInvestigationDevSample([longitude, latitude], target);
|
||||
const point = (dx = 0, dy = 0): [number, number] => [
|
||||
Math.max(-180, Math.min(180, longitude + dx)),
|
||||
latitude + dy
|
||||
];
|
||||
const overlay = (
|
||||
mode: "geometry" | "heatmap",
|
||||
title: string,
|
||||
features: Extract<
|
||||
InvestigationPresentationCommand,
|
||||
{ action: "set_overlay" }
|
||||
>["data"]["features"]
|
||||
) =>
|
||||
presentation({
|
||||
action: "set_overlay",
|
||||
overlay_id: `dev-case-${caseId}`,
|
||||
title,
|
||||
mode,
|
||||
evidence_id: "dev:synthetic",
|
||||
data: { type: "FeatureCollection", features }
|
||||
});
|
||||
|
||||
switch (caseId) {
|
||||
case "map_targets":
|
||||
return jsonl([
|
||||
...(target
|
||||
? [
|
||||
presentation({
|
||||
action: "highlight_features",
|
||||
layer: target.source_id,
|
||||
feature_ids: [target.feature_id],
|
||||
effect: "pulse"
|
||||
})
|
||||
]
|
||||
: []),
|
||||
overlay("geometry", "本地合成定位点", [
|
||||
{
|
||||
type: "Feature",
|
||||
properties: { origin: "synthetic" },
|
||||
geometry: { type: "Point", coordinates: point() }
|
||||
}
|
||||
])
|
||||
]);
|
||||
case "pressure_line":
|
||||
return jsonl([
|
||||
presentation({
|
||||
action: "open_chart",
|
||||
chart_id: "dev-case-pressure-line",
|
||||
evidence_refs: ["dev:synthetic"],
|
||||
chart: {
|
||||
title: "本地合成压力对比",
|
||||
chart_type: "line",
|
||||
x_data: ["08:00", "08:15", "08:30", "08:45"],
|
||||
series: [
|
||||
{ name: "异常日压力水头(m)", data: [32, 18, 22, 30] },
|
||||
{ name: "基线压力水头(m)", data: [33, 32, 32, 33] }
|
||||
]
|
||||
}
|
||||
})
|
||||
]);
|
||||
case "impact_bar":
|
||||
return jsonl([
|
||||
presentation({
|
||||
action: "open_chart",
|
||||
chart_id: "dev-case-impact-bar",
|
||||
evidence_refs: ["dev:synthetic"],
|
||||
chart: {
|
||||
title: "本地合成影响用户统计",
|
||||
chart_type: "bar",
|
||||
x_data: ["合成片区 A", "合成片区 B", "合成片区 C"],
|
||||
series: [{ name: "合成用户数(户)", data: [120, 80, 45] }]
|
||||
}
|
||||
})
|
||||
]);
|
||||
case "impact_area":
|
||||
return jsonl([
|
||||
overlay("geometry", "本地合成影响范围", [
|
||||
{
|
||||
type: "Feature",
|
||||
properties: { origin: "synthetic", purpose: "display_test_only" },
|
||||
geometry: {
|
||||
type: "Polygon",
|
||||
coordinates: [
|
||||
[
|
||||
point(-0.002, -0.0015),
|
||||
point(0.002, -0.0015),
|
||||
point(0.002, 0.0015),
|
||||
point(-0.002, 0.0015),
|
||||
point(-0.002, -0.0015)
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
])
|
||||
]);
|
||||
case "complaint_heatmap":
|
||||
return jsonl([
|
||||
overlay(
|
||||
"heatmap",
|
||||
"本地合成投诉热力",
|
||||
[-0.0015, 0, 0.0015].map((offset, index) => ({
|
||||
type: "Feature",
|
||||
properties: { origin: "synthetic", weight: [1, 3, 2][index] },
|
||||
geometry: { type: "Point", coordinates: point(offset, offset / 2) }
|
||||
}))
|
||||
)
|
||||
]);
|
||||
case "candidates_table":
|
||||
return jsonl([
|
||||
table(
|
||||
"candidates",
|
||||
"合成候选管段对比",
|
||||
["候选", "优先级", "核查依据"],
|
||||
[
|
||||
["DEMO-PIPE-01", "1", "合成压力降幅较大,待核查"],
|
||||
["DEMO-PIPE-02", "2", "合成相关性较弱,待排除"]
|
||||
]
|
||||
),
|
||||
data("candidate", { element_id: "DEMO-PIPE-01", rank: 1, status: "unverified" }),
|
||||
data("candidate", { element_id: "DEMO-PIPE-02", rank: 2, status: "unverified" })
|
||||
]);
|
||||
case "valves_table":
|
||||
return jsonl([
|
||||
table(
|
||||
"valves",
|
||||
"合成隔离阀门清单",
|
||||
["阀门", "操作建议", "核查要求"],
|
||||
[
|
||||
["DEMO-VALVE-01", "待评估", "位置与可操作性未核实"],
|
||||
["DEMO-VALVE-02", "待评估", "连通性与影响范围未核实"]
|
||||
]
|
||||
),
|
||||
data("valve", { element_id: "DEMO-VALVE-01", status: "unverified", executed: false }),
|
||||
data("valve", { element_id: "DEMO-VALVE-02", status: "unverified", executed: false })
|
||||
]);
|
||||
case "data_only":
|
||||
return jsonl([
|
||||
data("candidate", {
|
||||
element_id: "DEMO-PIPE-03",
|
||||
action: "clear_highlights",
|
||||
status: "data_only"
|
||||
}),
|
||||
data("impact", {
|
||||
item: "合成片区 A",
|
||||
estimated_users: 120,
|
||||
unit: "户",
|
||||
status: "example_only"
|
||||
})
|
||||
]);
|
||||
case "invalid_json":
|
||||
return `${jsonl([presentation({ action: "clear_highlights" })])}\n{"record_type":"candidate",`;
|
||||
case "invalid_command":
|
||||
return invalidJsonl([
|
||||
{ record_type: "presentation", command: { action: "unsupported_test_action" } }
|
||||
]);
|
||||
case "invalid_chart":
|
||||
return invalidJsonl([
|
||||
{
|
||||
record_type: "presentation",
|
||||
command: {
|
||||
action: "open_chart",
|
||||
chart_id: "dev-invalid-chart",
|
||||
evidence_refs: ["dev:synthetic"],
|
||||
chart: {
|
||||
title: "错误案例:数据长度不齐",
|
||||
chart_type: "line",
|
||||
x_data: ["08:00", "08:15"],
|
||||
series: [{ name: "合成压力水头(m)", data: [32] }]
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user