loop-benchmarking

Controlled experiments across agentic coding configurations. Same task, one variable, what actually works.
git clone https://git.shiptheloop.com/loop-benchmarking.git
Log | Files | Refs | README

ConfigTreemap.tsx (9319B)


      1 import React, { useState, useCallback } from "react";
      2 import { Treemap, ResponsiveContainer, Tooltip } from "recharts";
      3 import type { TreemapNode } from "recharts/types/chart/Treemap";
      4 import type { Run, AxisName } from "../lib/types";
      5 import { groupIntoCells, type Cell } from "../lib/analysis";
      6 
      7 interface ConfigTreemapProps {
      8   runs: Run[];
      9 }
     10 
     11 const SECONDARY_AXES: AxisName[] = [
     12   "prompt_style",
     13   "effort",
     14   "language",
     15   "human_language",
     16   "linter",
     17   "playwright",
     18   "context_file",
     19   "web_search",
     20   "max_budget",
     21   "tests_provided",
     22   "strategy",
     23   "design_guidance",
     24   "architecture",
     25   "error_checking",
     26   "context_noise",
     27   "renderer",
     28 ];
     29 
     30 function scoreColor(avgScore: number | null): string {
     31   if (avgScore === null) return "hsl(213 14% 30%)";
     32   const pct = avgScore * 100;
     33   if (pct > 60) return "hsl(92 28% 45%)";
     34   if (pct >= 30) return "hsl(40 71% 50%)";
     35   return "hsl(355 52% 48%)";
     36 }
     37 
     38 interface LeafData {
     39   name: string;
     40   displayName: string;
     41   size: number;
     42   avgScore: number | null;
     43   avgScorePct: string;
     44   model: string;
     45   configValue: string;
     46   color: string;
     47   [key: string]: unknown;
     48 }
     49 
     50 interface GroupData {
     51   name: string;
     52   children: LeafData[];
     53   [key: string]: unknown;
     54 }
     55 
     56 function buildTreeData(runs: Run[], secondaryAxis: AxisName): GroupData[] {
     57   const cells = groupIntoCells(runs);
     58   const byModel: Record<string, Record<string, Cell[]>> = {};
     59 
     60   for (const cell of cells) {
     61     const model = cell.meta.model;
     62     const secondary = String(cell.meta[secondaryAxis]);
     63     if (!byModel[model]) byModel[model] = {};
     64     if (!byModel[model][secondary]) byModel[model][secondary] = [];
     65     byModel[model][secondary].push(cell);
     66   }
     67 
     68   return Object.entries(byModel)
     69     .sort(([a], [b]) => a.localeCompare(b))
     70     .map(([model, configs]) => ({
     71       name: model,
     72       children: Object.entries(configs)
     73         .sort(([a], [b]) => a.localeCompare(b))
     74         .map(([configValue, configCells]) => {
     75           const scoredCells = configCells.filter((c) => c.score.avg > 0);
     76           const avgScore =
     77             scoredCells.length > 0
     78               ? scoredCells.reduce((s, c) => s + c.score.avg, 0) / scoredCells.length
     79               : null;
     80 
     81           return {
     82             name: `${model} / ${configValue}`,
     83             displayName: `${model} / ${configValue}`,
     84             size: configCells.length,
     85             avgScore,
     86             avgScorePct:
     87               avgScore !== null ? `${(avgScore * 100).toFixed(0)}%` : "--",
     88             model,
     89             configValue,
     90             color: scoreColor(avgScore),
     91           };
     92         }),
     93     }));
     94 }
     95 
     96 function CustomContent(props: TreemapNode): React.ReactElement {
     97   const { x, y, width, height, depth, name } = props;
     98 
     99   // Only render leaf nodes (depth === 2 in a two-level hierarchy via 'flat' type)
    100   // depth 1 = model group, depth 2 = leaf
    101   if (depth < 2) return <g />;
    102 
    103   const avgScorePct = (props as unknown as LeafData).avgScorePct ?? "--";
    104   const count = (props as unknown as LeafData).size ?? 0;
    105   const color = (props as unknown as LeafData).color ?? "hsl(213 14% 30%)";
    106 
    107   const showText = width > 50 && height > 36;
    108   const showCount = width > 50 && height > 50;
    109 
    110   return (
    111     <g>
    112       <rect
    113         x={x}
    114         y={y}
    115         width={width}
    116         height={height}
    117         fill={color}
    118         stroke="hsl(213 16% 12%)"
    119         strokeWidth={2}
    120       />
    121       {showText && (
    122         <>
    123           <text
    124             x={x + width / 2}
    125             y={y + height / 2 - (showCount ? 8 : 0)}
    126             textAnchor="middle"
    127             dominantBaseline="central"
    128             fill="hsl(213 27% 95%)"
    129             style={{
    130               fontFamily: "'JetBrains Mono', monospace",
    131               fontSize: "11px",
    132               fontWeight: 600,
    133               textTransform: "uppercase",
    134             }}
    135           >
    136             {width > 100 ? name : (props as unknown as LeafData).configValue}
    137           </text>
    138           <text
    139             x={x + width / 2}
    140             y={y + height / 2 + 8}
    141             textAnchor="middle"
    142             dominantBaseline="central"
    143             fill="hsl(213 27% 95%)"
    144             style={{
    145               fontFamily: "'JetBrains Mono', monospace",
    146               fontSize: "11px",
    147               fontWeight: 500,
    148               textTransform: "uppercase",
    149             }}
    150           >
    151             {avgScorePct}
    152           </text>
    153           {showCount && (
    154             <text
    155               x={x + width / 2}
    156               y={y + height / 2 + 22}
    157               textAnchor="middle"
    158               dominantBaseline="central"
    159               fill="hsla(213, 27%, 95%, 0.65)"
    160               style={{
    161                 fontFamily: "'JetBrains Mono', monospace",
    162                 fontSize: "11px",
    163                 fontWeight: 400,
    164                 textTransform: "uppercase",
    165               }}
    166             >
    167               n={count}
    168             </text>
    169           )}
    170         </>
    171       )}
    172     </g>
    173   );
    174 }
    175 
    176 function CustomTooltip({
    177   active,
    178   payload,
    179 }: {
    180   active?: boolean;
    181   payload?: Array<{ payload: TreemapNode }>;
    182 }) {
    183   if (!active || !payload || payload.length === 0) return null;
    184 
    185   const node = payload[0].payload as unknown as LeafData;
    186   if (!node.displayName) return null;
    187 
    188   return (
    189     <div
    190       style={{
    191         background: "hsl(217 16% 15.5%)",
    192         border: "1px solid hsl(217 17% 28%)",
    193         padding: "8px 12px",
    194         fontFamily: "'JetBrains Mono', monospace",
    195         fontSize: "11px",
    196         textTransform: "uppercase",
    197         letterSpacing: "0.5px",
    198       }}
    199     >
    200       <div style={{ fontWeight: 600, marginBottom: 4, color: "hsl(213 27% 88%)" }}>
    201         {node.displayName}
    202       </div>
    203       <div style={{ color: "hsl(213 14% 65%)" }}>
    204         Score: {node.avgScorePct}
    205       </div>
    206       <div style={{ color: "hsl(213 14% 65%)" }}>
    207         Cells: {node.size}
    208       </div>
    209     </div>
    210   );
    211 }
    212 
    213 export default function ConfigTreemap({ runs }: ConfigTreemapProps) {
    214   const [secondaryAxis, setSecondaryAxis] = useState<AxisName>("prompt_style");
    215 
    216   const handleClick = useCallback(
    217     (node: TreemapNode) => {
    218       const leaf = node as unknown as LeafData;
    219       if (leaf.model && leaf.configValue) {
    220         const params = new URLSearchParams();
    221         params.set("model", leaf.model);
    222         params.set(secondaryAxis, leaf.configValue);
    223         window.location.href = `/?${params.toString()}`;
    224       }
    225     },
    226     [secondaryAxis],
    227   );
    228 
    229   if (runs.length === 0) {
    230     return (
    231       <div
    232         className="card"
    233         style={{
    234           textAlign: "center",
    235           padding: "40px",
    236           color: "var(--text-muted)",
    237         }}
    238       >
    239         No data for treemap.
    240       </div>
    241     );
    242   }
    243 
    244   const treeData = buildTreeData(runs, secondaryAxis);
    245 
    246   return (
    247     <div
    248       className="card"
    249       style={{
    250         background: "var(--surface-1)",
    251         border: "1px solid var(--border)",
    252         borderRadius: 0,
    253         padding: "20px",
    254       }}
    255     >
    256       <div
    257         style={{
    258           display: "flex",
    259           justifyContent: "space-between",
    260           alignItems: "center",
    261           marginBottom: "16px",
    262         }}
    263       >
    264         <h3 style={{ margin: 0 }}>Configuration Treemap</h3>
    265         <div className="filter-group">
    266           <label htmlFor="treemap-axis">Group by</label>
    267           <select
    268             id="treemap-axis"
    269             value={secondaryAxis}
    270             onChange={(e) => setSecondaryAxis(e.target.value as AxisName)}
    271           >
    272             {SECONDARY_AXES.map((axis) => (
    273               <option key={axis} value={axis}>
    274                 {axis}
    275               </option>
    276             ))}
    277           </select>
    278         </div>
    279       </div>
    280 
    281       <div
    282         style={{
    283           display: "flex",
    284           gap: "16px",
    285           marginBottom: "12px",
    286           fontSize: "11px",
    287           fontFamily: "var(--font-mono)",
    288           textTransform: "uppercase",
    289           letterSpacing: "0.5px",
    290           color: "var(--text-muted)",
    291         }}
    292       >
    293         <span>
    294           <span
    295             style={{
    296               display: "inline-block",
    297               width: 10,
    298               height: 10,
    299               background: "hsl(92 28% 45%)",
    300               marginRight: 4,
    301               verticalAlign: "middle",
    302             }}
    303           />
    304           {">"} 60%
    305         </span>
    306         <span>
    307           <span
    308             style={{
    309               display: "inline-block",
    310               width: 10,
    311               height: 10,
    312               background: "hsl(40 71% 50%)",
    313               marginRight: 4,
    314               verticalAlign: "middle",
    315             }}
    316           />
    317           30-60%
    318         </span>
    319         <span>
    320           <span
    321             style={{
    322               display: "inline-block",
    323               width: 10,
    324               height: 10,
    325               background: "hsl(355 52% 48%)",
    326               marginRight: 4,
    327               verticalAlign: "middle",
    328             }}
    329           />
    330           {"<"} 30%
    331         </span>
    332       </div>
    333 
    334       <ResponsiveContainer width="100%" height={400}>
    335         <Treemap
    336           data={treeData}
    337           dataKey="size"
    338           nameKey="name"
    339           type="flat"
    340           content={CustomContent}
    341           onClick={handleClick}
    342           isAnimationActive={false}
    343           stroke="hsl(213 16% 12%)"
    344         >
    345           <Tooltip content={<CustomTooltip />} />
    346         </Treemap>
    347       </ResponsiveContainer>
    348     </div>
    349   );
    350 }

Impressum · Datenschutz