From be8cc23678e111643b275e3e160a4292ea34fe19 Mon Sep 17 00:00:00 2001 From: Shay Date: Tue, 23 Jun 2026 15:51:36 -0700 Subject: [PATCH] feat(workbench-ui): add practice evidence panel --- .../src/app/trace/PracticeEvidencePanel.tsx | 165 ++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 workbench-ui/src/app/trace/PracticeEvidencePanel.tsx diff --git a/workbench-ui/src/app/trace/PracticeEvidencePanel.tsx b/workbench-ui/src/app/trace/PracticeEvidencePanel.tsx new file mode 100644 index 00000000..d1d518e2 --- /dev/null +++ b/workbench-ui/src/app/trace/PracticeEvidencePanel.tsx @@ -0,0 +1,165 @@ +import { useMemo } from "react"; +import type { PracticeEvidence } from "../../types/practiceEvidence"; +import { MetadataTable } from "../../design/components/MetadataTable/MetadataTable"; +import { StableJsonViewer } from "../../design/components/StableJsonViewer"; +import { EmptyState } from "../../design/components/states/EmptyState"; +import { ErrorState } from "../../design/components/states/ErrorState"; +import { LoadingState } from "../../design/components/states/LoadingState"; +import { tracePracticeReproducer } from "./practiceEvidenceEndpoint"; +import { + type PracticeEvidenceDetailSection, + practiceEvidencePanelModel, +} from "./practiceEvidencePanelModel"; + +export interface PracticeEvidencePanelProps { + evidence?: PracticeEvidence | null; + isLoading: boolean; + error: unknown; + turnId: number; + errorMessage: (error: unknown) => string; +} + +function DetailSection({ section }: { section: PracticeEvidenceDetailSection }) { + return ( +
+

+ {section.title} +

+ {section.items.length === 0 ? ( +

{section.emptyMessage}

+ ) : ( +
+ {section.items.map((item) => ( +
+

+ {item.title} +

+ ({ + key: row.key, + value: row.value, + mono: true, + }))} + /> +
+ ))} +
+ )} +
+ ); +} + +export function PracticeEvidencePanel({ + evidence, + isLoading, + error, + turnId, + errorMessage, +}: PracticeEvidencePanelProps) { + const reproducer = tracePracticeReproducer(turnId); + const rawJson = useMemo( + () => (evidence ? JSON.stringify(evidence, null, 2) : ""), + [evidence], + ); + + if (isLoading) { + return ; + } + if (error) { + return ( + + ); + } + if (!evidence) { + return ( + + ); + } + + const model = practiceEvidencePanelModel(evidence); + return ( +
+ {model.emptyMessage ? ( +
+

{model.status}

+

{model.emptyMessage}

+
+ ) : null} + +
+ ({ + key: row.key, + value: row.value, + mono: true, + }))} + /> + ({ + key: row.key, + value: row.value, + mono: true, + }))} + /> +
+ + {model.chainRows.length > 0 ? ( + ({ + key: row.key, + value: row.value, + mono: true, + }))} + /> + ) : null} + +
+ {model.detailSections.map((section) => ( + + ))} +
+ +
+

+ Source spans +

+ {model.sourceSpanRows.length > 0 ? ( + ({ + key: row.key, + value: row.value, + mono: true, + }))} + /> + ) : ( +

+ No source spans recorded. +

+ )} +
+ +
+
+ reproducer +
+ + {model.reproducer} + +
+ + {model.showRaw ? ( +
+ +
+ ) : null} +
+ ); +}