feat(workbench): redesign investigation reports and diagrams
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import type { InvestigationDiagram } from "../api/investigation-report";
|
||||
import type { OperationalTask, OperationalTaskReport } from "../api/operational-task-client";
|
||||
|
||||
const diagramLabel = (value: string) =>
|
||||
value
|
||||
.replace(/[\r\n]/g, " ")
|
||||
.replace(/"/g, "#quot;")
|
||||
.replace(/</g, "#lt;")
|
||||
.replace(/>/g, "#gt;")
|
||||
.replace(/`/g, "#96;");
|
||||
|
||||
/** Preserve recorded relationships, without turning plan statuses into execution facts. */
|
||||
export function investigationDiagram(
|
||||
task: OperationalTask,
|
||||
snapshot: OperationalTaskReport | null
|
||||
): InvestigationDiagram | null {
|
||||
if (snapshot?.report.diagram) return snapshot.report.diagram;
|
||||
const plan = task.latest_plan;
|
||||
if (
|
||||
!plan ||
|
||||
(snapshot
|
||||
? snapshot.revision !== task.latest_report?.revision ||
|
||||
plan.execution_generation !== snapshot.execution_generation
|
||||
: plan.execution_generation !== task.execution_generation)
|
||||
)
|
||||
return null;
|
||||
const ids = new Map(plan.plan.nodes.map((node, index) => [node.id, `n${index}`]));
|
||||
const nodeDefinition = (node: (typeof plan.plan.nodes)[number]) => {
|
||||
const label = `\`${diagramLabel(node.title)}\``;
|
||||
return node.kind === "decision" || node.kind === "gate"
|
||||
? `${ids.get(node.id)}{"${label}"}:::tjDecision`
|
||||
: `${ids.get(node.id)}["${label}"]:::${node.owner === "human" ? "tjHuman" : "tjAgent"}`;
|
||||
};
|
||||
const agentNodes = plan.plan.nodes.filter((node) => node.owner === "agent");
|
||||
const humanNodes = plan.plan.nodes.filter((node) => node.owner === "human");
|
||||
const nodes =
|
||||
agentNodes.length && humanNodes.length
|
||||
? [
|
||||
'subgraph tj_agent_group["Agent · 资料核查"]',
|
||||
...agentNodes.map(nodeDefinition),
|
||||
"end",
|
||||
'subgraph tj_human_group["人工 · 现场协作"]',
|
||||
...humanNodes.map(nodeDefinition),
|
||||
"end"
|
||||
]
|
||||
: plan.plan.nodes.map(nodeDefinition);
|
||||
const edges = plan.plan.edges.map((edge) =>
|
||||
edge.condition
|
||||
? `${ids.get(edge.from)} -->|"${diagramLabel(edge.condition)}"| ${ids.get(edge.to)}`
|
||||
: `${ids.get(edge.from)} --> ${ids.get(edge.to)}`
|
||||
);
|
||||
return {
|
||||
title: "调查脉络",
|
||||
mermaid: ["flowchart TD", ...nodes, ...edges].join("\n"),
|
||||
node_refs: plan.plan.nodes
|
||||
.filter((node) => node.evidence_refs?.length || node.action_refs?.length)
|
||||
.map((node) => ({
|
||||
node_id: ids.get(node.id)!,
|
||||
label: node.title,
|
||||
evidence_refs: node.evidence_refs ?? [],
|
||||
action_refs: node.action_refs ?? []
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
export function investigationStatus(task: OperationalTask): string {
|
||||
if (task.active_run?.status === "queued") return "等待调查";
|
||||
if (task.active_run?.status === "claimed") return "调查启动中";
|
||||
if (task.active_run?.status === "running") return "调查中";
|
||||
if (task.lifecycle_status === "failed") return "本轮执行失败";
|
||||
if (task.lifecycle_status === "cancelled") return "本轮已取消";
|
||||
if (task.actions.some((action) => action.approval_status === "pending")) return "待人工审核";
|
||||
if (
|
||||
task.actions.some(
|
||||
(action) =>
|
||||
action.approval_status === "approved" && action.execution_status === "not_submitted"
|
||||
)
|
||||
)
|
||||
return "待执行动作";
|
||||
if (
|
||||
task.actions.some(
|
||||
(action) =>
|
||||
action.approval_status === "approved" &&
|
||||
["submitted", "unknown"].includes(action.execution_status)
|
||||
)
|
||||
)
|
||||
return "待核对回执";
|
||||
if (
|
||||
task.latest_report?.execution_generation === task.execution_generation &&
|
||||
task.latest_report.report.outcome === "insufficient_data" &&
|
||||
task.lifecycle_status === "completed"
|
||||
)
|
||||
return "已结束 · 证据不足";
|
||||
if (task.lifecycle_status === "completed") return "本轮调查完成";
|
||||
return "调查中";
|
||||
}
|
||||
|
||||
export function isPendingInvestigationAction(action: OperationalTask["actions"][number]) {
|
||||
return (
|
||||
action.approval_status === "pending" ||
|
||||
(action.approval_status === "approved" &&
|
||||
["not_submitted", "submitted", "unknown"].includes(action.execution_status))
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user