import { useEffect, useState } from "react"; import { Banner, Button, Empty, Toast } from "@douyinfe/semi-ui-19"; import { api } from "../api"; import type { UIAction } from "../ai/types"; import type { Experiment } from "./workspaceTypes"; type Graph = { items: Experiment[]; edges: { child_id: string; parent_kind: string; parent_id: string }[]; truncated: boolean; unresolved_experiment_ids: string[]; }; export function LineagePanel({ alphaId, experimentId, onAction, }: { alphaId?: string; experimentId?: string; onAction: (a: UIAction) => void; }) { const [graph, setGraph] = useState(null); const [focus, setFocus] = useState(experimentId); useEffect(() => { const c = new AbortController(); api( `/research/lineage?${focus ? `experiment_id=${encodeURIComponent(focus)}` : `alpha_id=${encodeURIComponent(alphaId || "")}`}`, { signal: c.signal }, ) .then(setGraph) .catch((e) => { if (!c.signal.aborted) Toast.error(e.message); }); return () => c.abort(); }, [alphaId, focus]); return (

变体关系

每个实验保留所有父来源;每次回测来源另见「研究来源」。选择实验可继续查看输入、设置差异、候选及回测。

{graph?.truncated && ( )} {graph?.items.length === 0 && } {graph?.items.map((e) => (

{e.name}

{e.hypothesis}

方法:{e.evidence.method || e.kind} · 模板{" "} {e.evidence.template?.name || "—"}{" "} {e.evidence.template?.version ? `v${e.evidence.template.version}` : ""}

{e.parents.map((p) => ( ))}

{e.candidates.length} 个候选 · {e.backtest_run_ids.length} 次回测

))} {graph?.unresolved_experiment_ids.map((id) => ( ))}
); }