File size: 4,040 Bytes
8b41737 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | import { useEffect, useState } from "react";
import type { GepaIterData, PanelNav } from "../types";
interface GepaIterLevelProps {
datasetId: string;
gepaIter: number;
fetchGepaIter: (dsId: string, gepaIter: number) => Promise<GepaIterData>;
onDrillDown: (nav: PanelNav) => void;
}
export default function GepaIterLevel({
datasetId,
gepaIter,
fetchGepaIter,
onDrillDown,
}: GepaIterLevelProps) {
const [data, setData] = useState<GepaIterData | null>(null);
useEffect(() => {
fetchGepaIter(datasetId, gepaIter).then(setData).catch(() => {});
}, [datasetId, gepaIter, fetchGepaIter]);
if (!data) return <div className="p-4 text-gray-400">Loading GEPA iteration...</div>;
return (
<div className="p-4 space-y-4">
{/* Stats row */}
<div className="flex gap-4 text-xs text-gray-400">
<span>
Total tokens:{" "}
<span className="text-gray-200">
{((data.total_input_tokens + data.total_output_tokens) / 1000).toFixed(1)}k
</span>
</span>
<span>
Time: <span className="text-gray-200">{data.total_execution_time.toFixed(1)}s</span>
</span>
<span>
RLM Calls: <span className="text-gray-200">{data.rlm_calls.length}</span>
</span>
</div>
{/* RLM iteration timeline */}
{data.rlm_calls.map((call) => (
<div key={call.rlm_call_idx}>
{data.rlm_calls.length > 1 && (
<div className="text-xs font-semibold text-gray-400 mb-2">
RLM Call {call.rlm_call_idx}
</div>
)}
<div className="flex gap-2 overflow-x-auto pb-2">
{call.iterations.map((it) => (
<div
key={it.rlm_iter}
className={`flex-shrink-0 w-56 bg-gray-800 border rounded-lg p-3 cursor-pointer transition-colors hover:border-orange-500 ${
it.has_final_answer ? "border-emerald-600" : "border-gray-700"
}`}
onClick={() =>
onDrillDown({
datasetId,
level: 3,
gepaIter,
rlmCallIdx: call.rlm_call_idx,
rlmIter: it.rlm_iter,
})
}
>
<div className="flex items-center justify-between mb-2">
<span className="bg-gray-700 text-gray-200 text-xs font-mono px-2 py-0.5 rounded">
iter {it.rlm_iter}
</span>
<div className="flex gap-1">
{it.has_code_blocks && (
<span className="bg-emerald-900 text-emerald-300 text-xs px-1.5 py-0.5 rounded">
{it.n_code_blocks} code
</span>
)}
{it.has_final_answer && (
<span className="bg-amber-900 text-amber-300 text-xs px-1.5 py-0.5 rounded">
FINAL
</span>
)}
</div>
</div>
<div className="flex justify-between text-xs text-gray-500 mb-2">
<span>{((it.input_tokens + it.output_tokens) / 1000).toFixed(1)}k tok</span>
<span>{it.execution_time.toFixed(1)}s</span>
</div>
<div className="text-xs text-gray-400 line-clamp-3 leading-relaxed">
{it.response_preview || "(empty)"}
</div>
</div>
))}
</div>
</div>
))}
{/* Final answer if present */}
{data.final_answer && (
<div className="bg-emerald-950 border border-emerald-700 rounded-lg p-4">
<div className="text-xs font-semibold text-emerald-400 mb-2">Final Answer</div>
<div className="text-sm text-gray-200 whitespace-pre-wrap max-h-60 overflow-y-auto">
{data.final_answer}
</div>
</div>
)}
</div>
);
}
|