Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion vite-app/dist/assets/index-D9iVTBbF.css

This file was deleted.

1 change: 1 addition & 0 deletions vite-app/dist/assets/index-DWfIf2rx.css

Large diffs are not rendered by default.

88 changes: 88 additions & 0 deletions vite-app/dist/assets/index-D_nkLTVA.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions vite-app/dist/assets/index-D_nkLTVA.js.map

Large diffs are not rendered by default.

88 changes: 0 additions & 88 deletions vite-app/dist/assets/index-DiF_B1x_.js

This file was deleted.

1 change: 0 additions & 1 deletion vite-app/dist/assets/index-DiF_B1x_.js.map

This file was deleted.

4 changes: 2 additions & 2 deletions vite-app/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>EP | Log Viewer</title>
<link rel="icon" href="/assets/favicon-BkAAWQga.png" />
<script type="module" crossorigin src="/assets/index-DiF_B1x_.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D9iVTBbF.css">
<script type="module" crossorigin src="/assets/index-D_nkLTVA.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DWfIf2rx.css">
</head>
<body>
<div id="root"></div>
Expand Down
17 changes: 15 additions & 2 deletions vite-app/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useRef } from "react";
import { Routes, Route, Navigate } from "react-router-dom";
import { observer } from "mobx-react";
import Dashboard from "./components/Dashboard";
import Button from "./components/Button";
Expand All @@ -15,7 +16,9 @@ const MAX_RECONNECT_ATTEMPTS = 5;

const App = observer(() => {
const wsRef = useRef<WebSocket | null>(null);
const reconnectTimeoutRef = useRef<number | null>(null);
const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
null
);
const reconnectAttemptsRef = useRef(0);

const connectWebSocket = () => {
Expand Down Expand Up @@ -145,7 +148,17 @@ const App = observer(() => {
</nav>

<main className="max-w-7xl mx-auto px-3 py-4">
<Dashboard onRefresh={handleManualRefresh} />
<Routes>
<Route path="/" element={<Navigate to="/table" replace />} />
<Route
path="/table"
element={<Dashboard onRefresh={handleManualRefresh} />}
/>
<Route
path="/pivot"
element={<Dashboard onRefresh={handleManualRefresh} />}
/>
</Routes>
</main>
</div>
);
Expand Down
158 changes: 90 additions & 68 deletions vite-app/src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { observer } from "mobx-react";
import { useMemo, useState } from "react";
import { useMemo, useState, useEffect } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { state } from "../App";
import Button from "./Button";
import { EvaluationTable } from "./EvaluationTable";
import PivotTable from "./PivotTable";
import TabButton from "./TabButton";
import flattenJson from "../util/flatten-json";

interface DashboardProps {
Expand Down Expand Up @@ -52,7 +54,19 @@ const Dashboard = observer(({ onRefresh }: DashboardProps) => {
const expandAll = () => state.setAllRowsExpanded(true);
const collapseAll = () => state.setAllRowsExpanded(false);

const [activeTab, setActiveTab] = useState<"table" | "pivot">("table");
const location = useLocation();
const navigate = useNavigate();

const deriveTabFromPath = (path: string): "table" | "pivot" =>
path.endsWith("/pivot") ? "pivot" : "table";

const [activeTab, setActiveTab] = useState<"table" | "pivot">(
deriveTabFromPath(location.pathname)
);

useEffect(() => {
setActiveTab(deriveTabFromPath(location.pathname));
}, [location.pathname]);

const flattened = useMemo(() => {
const flattenedDataset = state.sortedDataset.map((row) => flattenJson(row));
Expand All @@ -61,80 +75,88 @@ const Dashboard = observer(({ onRefresh }: DashboardProps) => {

return (
<div className="text-sm">
{/* Summary Stats */}
{/* Summary */}
<div className="mb-4 bg-white border border-gray-200 p-3">
<div className="flex justify-between items-center mb-2">
<h2 className="text-sm font-semibold text-gray-900">
Dataset Summary
</h2>
{state.totalCount > 0 && (
<div className="flex gap-2">
<Button
onClick={() => setActiveTab("table")}
size="sm"
variant="secondary"
>
Table
</Button>
<Button
onClick={() => setActiveTab("pivot")}
size="sm"
variant="secondary"
>
Pivot
</Button>
</div>
)}
</div>
<div className="text-xs space-y-1">
<div>
<span className="font-semibold text-gray-700">Total Rows:</span>{" "}
{state.totalCount}
</div>
{activeTab === "table" && state.totalCount > 0 && (
<div className="flex gap-2">
<Button onClick={expandAll} size="sm" variant="secondary">
Expand All
</Button>
<Button onClick={collapseAll} size="sm" variant="secondary">
Collapse All
</Button>
</div>
)}
<h2 className="text-sm font-semibold text-gray-900 mb-2">
Dataset Summary
</h2>
<div className="text-xs">
<span className="font-semibold text-gray-700">Total Rows:</span>{" "}
{state.totalCount}
</div>
</div>

{/* Show empty state or main table */}
{/* Content Area */}
{state.totalCount === 0 ? (
<EmptyState onRefresh={onRefresh} />
) : activeTab === "table" ? (
<EvaluationTable />
) : (
<div className="bg-white border border-gray-200 p-3">
<div className="text-xs text-gray-600 mb-2">
Showing pivot of flattened rows (JSONPath keys). Defaults: rows by
eval name and status; columns by model; values average score.
<div className="bg-white border border-gray-200">
{/* Tabs + contextual actions */}
<div className="px-3 pt-2 border-b border-gray-200">
<div className="flex justify-between h-8">
<div id="tabs" className="flex gap-1">
<TabButton
label="Table"
isActive={activeTab === "table"}
onClick={() => {
setActiveTab("table");
navigate("/table");
}}
title="View table"
/>
<TabButton
label="Pivot"
isActive={activeTab === "pivot"}
onClick={() => {
setActiveTab("pivot");
navigate("/pivot");
}}
title="View pivot"
/>
</div>
{activeTab === "table" && (
<div className="flex gap-2 pb-2">
<Button onClick={expandAll} size="sm" variant="secondary">
Expand All
</Button>
<Button onClick={collapseAll} size="sm" variant="secondary">
Collapse All
</Button>
</div>
)}
</div>
</div>

{/* Tab content */}
<div className="p-3">
{activeTab === "table" ? (
<EvaluationTable />
) : (
<div>
<div className="text-xs text-gray-600 mb-2">
Showing pivot of flattened rows (JSONPath keys). Defaults:
rows by eval name and status; columns by model; values average
score.
</div>
<PivotTable
data={flattened}
rowFields={[
"$.eval_metadata.name" as keyof (typeof flattened)[number],
"$.eval_metadata.status" as keyof (typeof flattened)[number],
]}
columnFields={[
"$.input_metadata.completion_params.model" as keyof (typeof flattened)[number],
]}
valueField={
"$.evaluation_result.score" as keyof (typeof flattened)[number]
}
aggregator="avg"
showRowTotals
showColumnTotals
/>
</div>
)}
</div>
<PivotTable
// Flattened object list
data={flattened}
// Row keys
rowFields={[
"$.eval_metadata.name" as keyof (typeof flattened)[number],
"$.eval_metadata.status" as keyof (typeof flattened)[number],
]}
// Column keys
columnFields={[
"$.input_metadata.completion_params.model" as keyof (typeof flattened)[number],
]}
// Value and aggregation
valueField={
"$.evaluation_result.score" as keyof (typeof flattened)[number]
}
aggregator="avg"
showRowTotals
showColumnTotals
/>
</div>
)}
</div>
Expand Down
38 changes: 18 additions & 20 deletions vite-app/src/components/EvaluationRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ChatInterface } from "./ChatInterface";
import { MetadataSection } from "./MetadataSection";
import StatusIndicator from "./StatusIndicator";
import { state } from "../App";
import { TableCell, TableRowInteractive } from "./TableContainer";

// Small, focused components following "dereference values late" principle
const ExpandIcon = observer(({ rolloutId }: { rolloutId?: string }) => {
Expand Down Expand Up @@ -172,7 +173,7 @@ const ExpandedContent = observer(
input_metadata: EvaluationRowType["input_metadata"];
tools: EvaluationRowType["tools"];
}) => (
<div className="p-4 bg-gray-50 border-t border-gray-200">
<div className="p-4 bg-gray-50">
<div className="flex gap-3 w-fit">
{/* Left Column - Chat Interface */}
<div className="min-w-0">
Expand Down Expand Up @@ -204,48 +205,45 @@ export const EvaluationRow = observer(
return (
<>
{/* Main Table Row */}
<tr
className="hover:bg-gray-50 cursor-pointer text-sm border-b border-gray-200"
onClick={toggleExpanded}
>
<TableRowInteractive onClick={toggleExpanded}>
{/* Expand/Collapse Icon */}
<td className="px-3 py-3 w-8">
<TableCell className="w-8 py-3">
<ExpandIcon rolloutId={rolloutId} />
</td>
</TableCell>

{/* Name */}
<td className="px-3 py-3 text-xs">
<TableCell className="py-3 text-xs">
<RowName name={row.eval_metadata?.name} />
</td>
</TableCell>

{/* Status */}
<td className="px-3 py-3 text-xs">
<TableCell className="py-3 text-xs">
<RowStatus
status={row.eval_metadata?.status}
showSpinner={row.eval_metadata?.status === "running"}
/>
</td>
</TableCell>

{/* Rollout ID */}
<td className="px-3 py-3 text-xs">
<TableCell className="py-3 text-xs">
<RolloutId rolloutId={row.rollout_id} />
</td>
</TableCell>

{/* Model */}
<td className="px-3 py-3 text-xs">
<TableCell className="py-3 text-xs">
<RowModel model={row.input_metadata.completion_params?.model} />
</td>
</TableCell>

{/* Score */}
<td className="px-3 py-3 text-xs">
<TableCell className="py-3 text-xs">
<RowScore score={row.evaluation_result?.score} />
</td>
</TableCell>

{/* Created */}
<td className="px-3 py-3 text-xs">
<TableCell className="py-3 text-xs">
<RowCreated created_at={row.created_at} />
</td>
</tr>
</TableCell>
</TableRowInteractive>

{/* Expanded Content Row */}
{isExpanded && (
Expand Down
Loading
Loading