@railtownai/railtracks-visualizer 0.0.68 → 0.0.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -12939,7 +12939,7 @@ const createLucideIcon = (iconName, iconNode)=>{
12939
12939
  return Component;
12940
12940
  };
12941
12941
 
12942
- const __iconNode$z = [
12942
+ const __iconNode$C = [
12943
12943
  [
12944
12944
  "path",
12945
12945
  {
@@ -12955,9 +12955,9 @@ const __iconNode$z = [
12955
12955
  }
12956
12956
  ]
12957
12957
  ];
12958
- const ArrowLeft = createLucideIcon("arrow-left", __iconNode$z);
12958
+ const ArrowLeft = createLucideIcon("arrow-left", __iconNode$C);
12959
12959
 
12960
- const __iconNode$y = [
12960
+ const __iconNode$B = [
12961
12961
  [
12962
12962
  "path",
12963
12963
  {
@@ -12973,9 +12973,9 @@ const __iconNode$y = [
12973
12973
  }
12974
12974
  ]
12975
12975
  ];
12976
- const ArrowRight = createLucideIcon("arrow-right", __iconNode$y);
12976
+ const ArrowRight = createLucideIcon("arrow-right", __iconNode$B);
12977
12977
 
12978
- const __iconNode$x = [
12978
+ const __iconNode$A = [
12979
12979
  [
12980
12980
  "path",
12981
12981
  {
@@ -13023,9 +13023,9 @@ const __iconNode$x = [
13023
13023
  }
13024
13024
  ]
13025
13025
  ];
13026
- const Bot = createLucideIcon("bot", __iconNode$x);
13026
+ const Bot = createLucideIcon("bot", __iconNode$A);
13027
13027
 
13028
- const __iconNode$w = [
13028
+ const __iconNode$z = [
13029
13029
  [
13030
13030
  "path",
13031
13031
  {
@@ -13059,9 +13059,9 @@ const __iconNode$w = [
13059
13059
  }
13060
13060
  ]
13061
13061
  ];
13062
- const Calendar = createLucideIcon("calendar", __iconNode$w);
13062
+ const Calendar = createLucideIcon("calendar", __iconNode$z);
13063
13063
 
13064
- const __iconNode$v = [
13064
+ const __iconNode$y = [
13065
13065
  [
13066
13066
  "path",
13067
13067
  {
@@ -13118,9 +13118,9 @@ const __iconNode$v = [
13118
13118
  }
13119
13119
  ]
13120
13120
  ];
13121
- const ChartNetwork = createLucideIcon("chart-network", __iconNode$v);
13121
+ const ChartNetwork = createLucideIcon("chart-network", __iconNode$y);
13122
13122
 
13123
- const __iconNode$u = [
13123
+ const __iconNode$x = [
13124
13124
  [
13125
13125
  "path",
13126
13126
  {
@@ -13129,9 +13129,9 @@ const __iconNode$u = [
13129
13129
  }
13130
13130
  ]
13131
13131
  ];
13132
- const ChevronDown = createLucideIcon("chevron-down", __iconNode$u);
13132
+ const ChevronDown = createLucideIcon("chevron-down", __iconNode$x);
13133
13133
 
13134
- const __iconNode$t = [
13134
+ const __iconNode$w = [
13135
13135
  [
13136
13136
  "path",
13137
13137
  {
@@ -13140,9 +13140,9 @@ const __iconNode$t = [
13140
13140
  }
13141
13141
  ]
13142
13142
  ];
13143
- const ChevronRight = createLucideIcon("chevron-right", __iconNode$t);
13143
+ const ChevronRight = createLucideIcon("chevron-right", __iconNode$w);
13144
13144
 
13145
- const __iconNode$s = [
13145
+ const __iconNode$v = [
13146
13146
  [
13147
13147
  "path",
13148
13148
  {
@@ -13158,9 +13158,9 @@ const __iconNode$s = [
13158
13158
  }
13159
13159
  ]
13160
13160
  ];
13161
- const ChevronsDown = createLucideIcon("chevrons-down", __iconNode$s);
13161
+ const ChevronsDown = createLucideIcon("chevrons-down", __iconNode$v);
13162
13162
 
13163
- const __iconNode$r = [
13163
+ const __iconNode$u = [
13164
13164
  [
13165
13165
  "path",
13166
13166
  {
@@ -13176,9 +13176,9 @@ const __iconNode$r = [
13176
13176
  }
13177
13177
  ]
13178
13178
  ];
13179
- const ChevronsUp = createLucideIcon("chevrons-up", __iconNode$r);
13179
+ const ChevronsUp = createLucideIcon("chevrons-up", __iconNode$u);
13180
13180
 
13181
- const __iconNode$q = [
13181
+ const __iconNode$t = [
13182
13182
  [
13183
13183
  "circle",
13184
13184
  {
@@ -13209,9 +13209,9 @@ const __iconNode$q = [
13209
13209
  }
13210
13210
  ]
13211
13211
  ];
13212
- const CircleAlert = createLucideIcon("circle-alert", __iconNode$q);
13212
+ const CircleAlert = createLucideIcon("circle-alert", __iconNode$t);
13213
13213
 
13214
- const __iconNode$p = [
13214
+ const __iconNode$s = [
13215
13215
  [
13216
13216
  "path",
13217
13217
  {
@@ -13227,9 +13227,9 @@ const __iconNode$p = [
13227
13227
  }
13228
13228
  ]
13229
13229
  ];
13230
- const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$p);
13230
+ const CircleCheckBig = createLucideIcon("circle-check-big", __iconNode$s);
13231
13231
 
13232
- const __iconNode$o = [
13232
+ const __iconNode$r = [
13233
13233
  [
13234
13234
  "circle",
13235
13235
  {
@@ -13254,9 +13254,9 @@ const __iconNode$o = [
13254
13254
  }
13255
13255
  ]
13256
13256
  ];
13257
- const CircleDollarSign = createLucideIcon("circle-dollar-sign", __iconNode$o);
13257
+ const CircleDollarSign = createLucideIcon("circle-dollar-sign", __iconNode$r);
13258
13258
 
13259
- const __iconNode$n = [
13259
+ const __iconNode$q = [
13260
13260
  [
13261
13261
  "circle",
13262
13262
  {
@@ -13274,9 +13274,9 @@ const __iconNode$n = [
13274
13274
  }
13275
13275
  ]
13276
13276
  ];
13277
- const Clock = createLucideIcon("clock", __iconNode$n);
13277
+ const Clock = createLucideIcon("clock", __iconNode$q);
13278
13278
 
13279
- const __iconNode$m = [
13279
+ const __iconNode$p = [
13280
13280
  [
13281
13281
  "rect",
13282
13282
  {
@@ -13297,9 +13297,9 @@ const __iconNode$m = [
13297
13297
  }
13298
13298
  ]
13299
13299
  ];
13300
- const Copy = createLucideIcon("copy", __iconNode$m);
13300
+ const Copy = createLucideIcon("copy", __iconNode$p);
13301
13301
 
13302
- const __iconNode$l = [
13302
+ const __iconNode$o = [
13303
13303
  [
13304
13304
  "path",
13305
13305
  {
@@ -13407,9 +13407,9 @@ const __iconNode$l = [
13407
13407
  }
13408
13408
  ]
13409
13409
  ];
13410
- const Cpu = createLucideIcon("cpu", __iconNode$l);
13410
+ const Cpu = createLucideIcon("cpu", __iconNode$o);
13411
13411
 
13412
- const __iconNode$k = [
13412
+ const __iconNode$n = [
13413
13413
  [
13414
13414
  "line",
13415
13415
  {
@@ -13428,9 +13428,9 @@ const __iconNode$k = [
13428
13428
  }
13429
13429
  ]
13430
13430
  ];
13431
- const DollarSign = createLucideIcon("dollar-sign", __iconNode$k);
13431
+ const DollarSign = createLucideIcon("dollar-sign", __iconNode$n);
13432
13432
 
13433
- const __iconNode$j = [
13433
+ const __iconNode$m = [
13434
13434
  [
13435
13435
  "path",
13436
13436
  {
@@ -13460,9 +13460,9 @@ const __iconNode$j = [
13460
13460
  }
13461
13461
  ]
13462
13462
  ];
13463
- const FileInput = createLucideIcon("file-input", __iconNode$j);
13463
+ const FileInput = createLucideIcon("file-input", __iconNode$m);
13464
13464
 
13465
- const __iconNode$i = [
13465
+ const __iconNode$l = [
13466
13466
  [
13467
13467
  "path",
13468
13468
  {
@@ -13492,9 +13492,9 @@ const __iconNode$i = [
13492
13492
  }
13493
13493
  ]
13494
13494
  ];
13495
- const FileOutput = createLucideIcon("file-output", __iconNode$i);
13495
+ const FileOutput = createLucideIcon("file-output", __iconNode$l);
13496
13496
 
13497
- const __iconNode$h = [
13497
+ const __iconNode$k = [
13498
13498
  [
13499
13499
  "path",
13500
13500
  {
@@ -13552,9 +13552,27 @@ const __iconNode$h = [
13552
13552
  }
13553
13553
  ]
13554
13554
  ];
13555
- const FoldVertical = createLucideIcon("fold-vertical", __iconNode$h);
13555
+ const FoldVertical = createLucideIcon("fold-vertical", __iconNode$k);
13556
13556
 
13557
- const __iconNode$g = [
13557
+ const __iconNode$j = [
13558
+ [
13559
+ "path",
13560
+ {
13561
+ d: "m12 14 4-4",
13562
+ key: "9kzdfg"
13563
+ }
13564
+ ],
13565
+ [
13566
+ "path",
13567
+ {
13568
+ d: "M3.34 19a10 10 0 1 1 17.32 0",
13569
+ key: "19p75a"
13570
+ }
13571
+ ]
13572
+ ];
13573
+ const Gauge = createLucideIcon("gauge", __iconNode$j);
13574
+
13575
+ const __iconNode$i = [
13558
13576
  [
13559
13577
  "line",
13560
13578
  {
@@ -13596,9 +13614,9 @@ const __iconNode$g = [
13596
13614
  }
13597
13615
  ]
13598
13616
  ];
13599
- const Hash = createLucideIcon("hash", __iconNode$g);
13617
+ const Hash = createLucideIcon("hash", __iconNode$i);
13600
13618
 
13601
- const __iconNode$f = [
13619
+ const __iconNode$h = [
13602
13620
  [
13603
13621
  "circle",
13604
13622
  {
@@ -13623,9 +13641,9 @@ const __iconNode$f = [
13623
13641
  }
13624
13642
  ]
13625
13643
  ];
13626
- const Info = createLucideIcon("info", __iconNode$f);
13644
+ const Info = createLucideIcon("info", __iconNode$h);
13627
13645
 
13628
- const __iconNode$e = [
13646
+ const __iconNode$g = [
13629
13647
  [
13630
13648
  "path",
13631
13649
  {
@@ -13662,9 +13680,9 @@ const __iconNode$e = [
13662
13680
  }
13663
13681
  ]
13664
13682
  ];
13665
- const ListChecks = createLucideIcon("list-checks", __iconNode$e);
13683
+ const ListChecks = createLucideIcon("list-checks", __iconNode$g);
13666
13684
 
13667
- const __iconNode$d = [
13685
+ const __iconNode$f = [
13668
13686
  [
13669
13687
  "path",
13670
13688
  {
@@ -13694,9 +13712,9 @@ const __iconNode$d = [
13694
13712
  }
13695
13713
  ]
13696
13714
  ];
13697
- const MessageSquareText = createLucideIcon("message-square-text", __iconNode$d);
13715
+ const MessageSquareText = createLucideIcon("message-square-text", __iconNode$f);
13698
13716
 
13699
- const __iconNode$c = [
13717
+ const __iconNode$e = [
13700
13718
  [
13701
13719
  "path",
13702
13720
  {
@@ -13712,9 +13730,9 @@ const __iconNode$c = [
13712
13730
  }
13713
13731
  ]
13714
13732
  ];
13715
- const MessagesSquare = createLucideIcon("messages-square", __iconNode$c);
13733
+ const MessagesSquare = createLucideIcon("messages-square", __iconNode$e);
13716
13734
 
13717
- const __iconNode$b = [
13735
+ const __iconNode$d = [
13718
13736
  [
13719
13737
  "rect",
13720
13738
  {
@@ -13734,9 +13752,38 @@ const __iconNode$b = [
13734
13752
  }
13735
13753
  ]
13736
13754
  ];
13737
- const PanelLeft = createLucideIcon("panel-left", __iconNode$b);
13755
+ const PanelLeft = createLucideIcon("panel-left", __iconNode$d);
13738
13756
 
13739
- const __iconNode$a = [
13757
+ const __iconNode$c = [
13758
+ [
13759
+ "rect",
13760
+ {
13761
+ width: "18",
13762
+ height: "18",
13763
+ x: "3",
13764
+ y: "3",
13765
+ rx: "2",
13766
+ key: "afitv7"
13767
+ }
13768
+ ],
13769
+ [
13770
+ "path",
13771
+ {
13772
+ d: "M15 3v18",
13773
+ key: "14nvp0"
13774
+ }
13775
+ ],
13776
+ [
13777
+ "path",
13778
+ {
13779
+ d: "m10 15-3-3 3-3",
13780
+ key: "1pgupc"
13781
+ }
13782
+ ]
13783
+ ];
13784
+ const PanelRightOpen = createLucideIcon("panel-right-open", __iconNode$c);
13785
+
13786
+ const __iconNode$b = [
13740
13787
  [
13741
13788
  "path",
13742
13789
  {
@@ -13766,9 +13813,9 @@ const __iconNode$a = [
13766
13813
  }
13767
13814
  ]
13768
13815
  ];
13769
- const RefreshCw = createLucideIcon("refresh-cw", __iconNode$a);
13816
+ const RefreshCw = createLucideIcon("refresh-cw", __iconNode$b);
13770
13817
 
13771
- const __iconNode$9 = [
13818
+ const __iconNode$a = [
13772
13819
  [
13773
13820
  "path",
13774
13821
  {
@@ -13805,9 +13852,9 @@ const __iconNode$9 = [
13805
13852
  }
13806
13853
  ]
13807
13854
  ];
13808
- const Scale = createLucideIcon("scale", __iconNode$9);
13855
+ const Scale = createLucideIcon("scale", __iconNode$a);
13809
13856
 
13810
- const __iconNode$8 = [
13857
+ const __iconNode$9 = [
13811
13858
  [
13812
13859
  "path",
13813
13860
  {
@@ -13841,9 +13888,9 @@ const __iconNode$8 = [
13841
13888
  }
13842
13889
  ]
13843
13890
  ];
13844
- const Settings2 = createLucideIcon("settings-2", __iconNode$8);
13891
+ const Settings2 = createLucideIcon("settings-2", __iconNode$9);
13845
13892
 
13846
- const __iconNode$7 = [
13893
+ const __iconNode$8 = [
13847
13894
  [
13848
13895
  "path",
13849
13896
  {
@@ -13866,9 +13913,9 @@ const __iconNode$7 = [
13866
13913
  }
13867
13914
  ]
13868
13915
  ];
13869
- const Share = createLucideIcon("share", __iconNode$7);
13916
+ const Share = createLucideIcon("share", __iconNode$8);
13870
13917
 
13871
- const __iconNode$6 = [
13918
+ const __iconNode$7 = [
13872
13919
  [
13873
13920
  "path",
13874
13921
  {
@@ -13877,7 +13924,35 @@ const __iconNode$6 = [
13877
13924
  }
13878
13925
  ]
13879
13926
  ];
13880
- const Shield = createLucideIcon("shield", __iconNode$6);
13927
+ const Shield = createLucideIcon("shield", __iconNode$7);
13928
+
13929
+ const __iconNode$6 = [
13930
+ [
13931
+ "path",
13932
+ {
13933
+ d: "M13.172 2a2 2 0 0 1 1.414.586l6.71 6.71a2.4 2.4 0 0 1 0 3.408l-4.592 4.592a2.4 2.4 0 0 1-3.408 0l-6.71-6.71A2 2 0 0 1 6 9.172V3a1 1 0 0 1 1-1z",
13934
+ key: "16rjxf"
13935
+ }
13936
+ ],
13937
+ [
13938
+ "path",
13939
+ {
13940
+ d: "M2 7v6.172a2 2 0 0 0 .586 1.414l6.71 6.71a2.4 2.4 0 0 0 3.191.193",
13941
+ key: "178nd4"
13942
+ }
13943
+ ],
13944
+ [
13945
+ "circle",
13946
+ {
13947
+ cx: "10.5",
13948
+ cy: "6.5",
13949
+ r: ".5",
13950
+ fill: "currentColor",
13951
+ key: "12ikhr"
13952
+ }
13953
+ ]
13954
+ ];
13955
+ const Tags = createLucideIcon("tags", __iconNode$6);
13881
13956
 
13882
13957
  const __iconNode$5 = [
13883
13958
  [
@@ -112596,7 +112671,32 @@ const GuardrailsPopover = ({ guardrails, title = "Guardrails", placement = "righ
112596
112671
  })));
112597
112672
  };
112598
112673
 
112599
- const getNodeLatency = (node)=>node.details?.internals?.latency?.total_time ?? 0;
112674
+ /** Collect start/end instants (unix seconds) for wall-clock span of a single node. */ const getNodeStampTimes = (node)=>{
112675
+ const start = node.stamp?.time;
112676
+ if (start == null) return [];
112677
+ const times = [
112678
+ start
112679
+ ];
112680
+ const latency = node.details?.internals?.latency?.total_time;
112681
+ if (latency != null && latency > 0) times.push(start + latency);
112682
+ return times;
112683
+ };
112684
+ const wallClockSpanSeconds = (times)=>{
112685
+ if (times.length === 0) return 0;
112686
+ const min = Math.min(...times);
112687
+ const max = Math.max(...times);
112688
+ return Math.max(0, max - min);
112689
+ };
112690
+ const collectSubtreeStampTimes = (nodeId, childrenMap, nodeMap)=>{
112691
+ const node = nodeMap.get(nodeId);
112692
+ if (!node) return [];
112693
+ const times = getNodeStampTimes(node);
112694
+ for (const childId of childrenMap.get(nodeId) ?? []){
112695
+ times.push(...collectSubtreeStampTimes(childId, childrenMap, nodeMap));
112696
+ }
112697
+ return times;
112698
+ };
112699
+ /** Wall-clock elapsed time across a node and all descendants (unix stamp times). */ const getSubtreeWallClockSeconds = (nodeId, childrenMap, nodeMap)=>wallClockSpanSeconds(collectSubtreeStampTimes(nodeId, childrenMap, nodeMap));
112600
112700
  const SessionTreeNodeTitle = ({ displayName, subtreeLatency, guardrails })=>{
112601
112701
  return /*#__PURE__*/ React.createElement("span", {
112602
112702
  style: {
@@ -112724,8 +112824,7 @@ const buildNodeTree = (run)=>{
112724
112824
  const nodeKey = `node::${run.run_id}::${node.identifier}`;
112725
112825
  const childIds = childrenMap.get(node.identifier) || [];
112726
112826
  const children = childIds.map((id)=>nodeMap.get(id)).filter((n)=>n != null).sort((a, b)=>(a.stamp?.step ?? 0) - (b.stamp?.step ?? 0)).map(buildTreeNode);
112727
- const childLatency = children.reduce((s, c)=>s + (c.subtreeLatency ?? 0), 0);
112728
- const subtreeLatency = getNodeLatency(node) + childLatency;
112827
+ const subtreeLatency = getSubtreeWallClockSeconds(node.identifier, childrenMap, nodeMap);
112729
112828
  const guardrails = node.details?.internals?.guard_details ?? [];
112730
112829
  const isTool = node.node_type === "Tool";
112731
112830
  const failed = isNodeFailureFromIncomingEdge(run, node.identifier);
@@ -113347,7 +113446,7 @@ const SessionDetails = ({ session, open, onClose, initialNodeId, initialRunId, l
113347
113446
 
113348
113447
  /**
113349
113448
  * DTOs for evaluation data from .railtracks/data/evaluations/*.json
113350
- */ /** Numeric metric definition (LLMMetric or ToolMetric) */ /** Type guard for LLM result */ function isLLMResult(item) {
113449
+ */ /** Numeric metric definition (LLMMetric, ToolMetric, or Numerical for custom evaluators) */ /** Type guard for LLM result */ function isLLMResult(item) {
113351
113450
  return item.type === "LLM";
113352
113451
  }
113353
113452
  /** Type guard for Tool result */ function isToolResult(item) {
@@ -113365,8 +113464,12 @@ const SessionDetails = ({ session, open, onClose, initialNodeId, initialRunId, l
113365
113464
  /** Type guard for CategoricalAggregate */ function isAggregateCategorical(item) {
113366
113465
  return item.type === "CategoricalAggregate";
113367
113466
  }
113368
- /** Type guard for aggregated stats (numeric - LLMInferenceAggregate or ToolAggregate) */ function isAggregatedStats(item) {
113369
- return item.type === "LLMInferenceAggregate" || item.type === "ToolAggregate";
113467
+ /** Type guard for NumericalAggregate (custom evaluator numeric aggregate) */ function isNumericalAggregate(item) {
113468
+ return item.type === "NumericalAggregate";
113469
+ }
113470
+ /** Type guard for aggregated stats (numeric - LLMInferenceAggregate, ToolAggregate, or NumericalAggregate) */ function isAggregatedStats(item) {
113471
+ const type = item.type;
113472
+ return type === "LLMInferenceAggregate" || type === "ToolAggregate" || type === "NumericalAggregate";
113370
113473
  }
113371
113474
 
113372
113475
  function isCategoricalMetric$1(def) {
@@ -113382,7 +113485,8 @@ function isCategoricalMetric$1(def) {
113382
113485
  const aggregateTypes = [
113383
113486
  "ToolAggregate",
113384
113487
  "LLMInferenceAggregate",
113385
- "CategoricalAggregate"
113488
+ "CategoricalAggregate",
113489
+ "NumericalAggregate"
113386
113490
  ];
113387
113491
  const aggregateItems = Object.values(nodes).filter((n)=>n?.type && aggregateTypes.includes(n.type));
113388
113492
  return [
@@ -117048,7 +117152,7 @@ function buildTreeFromProcessedData(latencyByTool, failureRateByTool, usageCount
117048
117152
 
117049
117153
  const JUDGE_RESULT_PREFIX = "JudgeResult/";
117050
117154
  const JUDGE_REASONING_PREFIX = "JudgeReasoning/";
117051
- /** Map agent node id -> session id from the agents array (new format only). */ function buildSessionByNodeId(agents) {
117155
+ /** Map agent node id -> session id from the agents array (new format only). */ function buildSessionByNodeId$1(agents) {
117052
117156
  const map = new Map();
117053
117157
  for (const agent of agents ?? []){
117054
117158
  for (const entry of agent.agent_node_ids ?? []){
@@ -117072,10 +117176,10 @@ const JUDGE_REASONING_PREFIX = "JudgeReasoning/";
117072
117176
  }
117073
117177
  return map;
117074
117178
  }
117075
- function metricNameOf(agg) {
117179
+ function metricNameOf$1(agg) {
117076
117180
  return agg.metric?.name ?? agg.name?.replace(/^Aggregate\//, "") ?? "Unknown";
117077
117181
  }
117078
- /** Collect the JudgeResult leaf nodes (label + node id) grouped by label. */ function groupNodesByLabel(agg, nodes, rawResults, metricName, sessionByNodeId, reasoningByKey) {
117182
+ /** Collect the leaf nodes (label + node id) grouped by label. */ function groupNodesByLabel(agg, nodes, rawResults, metricName, sessionByNodeId, reasoningByKey) {
117079
117183
  const byLabel = new Map();
117080
117184
  const seen = new Set();
117081
117185
  const push = (label, nodeId)=>{
@@ -117113,15 +117217,15 @@ function metricNameOf(agg) {
117113
117217
  }
117114
117218
  return byLabel;
117115
117219
  }
117116
- /** Build the Judge view model: one entry per categorical metric. */ function buildJudgeMetricsFromAggregate(input) {
117220
+ /** Build the categorical view model: one entry per categorical metric. */ function buildCategoricalMetricsFromAggregate(input) {
117117
117221
  const { roots, nodes, agents, rawResults = [] } = input;
117118
- const sessionByNodeId = buildSessionByNodeId(agents);
117222
+ const sessionByNodeId = buildSessionByNodeId$1(agents);
117119
117223
  const reasoningByKey = buildReasoningByKey(rawResults);
117120
117224
  // Prefer roots; fall back to scanning all nodes for categorical aggregates.
117121
117225
  const rootAggregates = roots.map((id)=>nodes[id]).filter((n)=>isAggregateCategorical(n));
117122
117226
  const aggregates = rootAggregates.length > 0 ? rootAggregates : Object.values(nodes).filter((n)=>isAggregateCategorical(n));
117123
117227
  return aggregates.map((agg, index)=>{
117124
- const metricName = metricNameOf(agg);
117228
+ const metricName = metricNameOf$1(agg);
117125
117229
  const counts = agg.counts ?? {};
117126
117230
  const categories = agg.categories ?? agg.metric?.categories ?? Object.keys(counts);
117127
117231
  const byLabel = groupNodesByLabel(agg, nodes, rawResults, metricName, sessionByNodeId, reasoningByKey);
@@ -117153,13 +117257,139 @@ function metricNameOf(agg) {
117153
117257
  });
117154
117258
  }
117155
117259
 
117156
- const SHORT_ID_LENGTH = 8;
117157
- const shortenId = (id)=>id.length > SHORT_ID_LENGTH ? id.slice(0, SHORT_ID_LENGTH) : id;
117158
- const NodeRow = ({ node, metricName, onAgentNodeClick })=>{
117260
+ /** Map agent node id -> session id from the agents array (new format only). */ function buildSessionByNodeId(agents) {
117261
+ const map = new Map();
117262
+ for (const agent of agents ?? []){
117263
+ for (const entry of agent.agent_node_ids ?? []){
117264
+ if (entry && typeof entry === "object" && "agent_node_id" in entry) {
117265
+ map.set(entry.agent_node_id, entry.session_id);
117266
+ }
117267
+ }
117268
+ }
117269
+ return map;
117270
+ }
117271
+ function metricNameOf(agg) {
117272
+ return agg.metric?.name ?? agg.name?.replace(/^Aggregate\//, "") ?? "Unknown";
117273
+ }
117274
+ /** Collect the per-run leaf values (value + node id) for a numerical aggregate. */ function collectNodes(agg, nodes, rawResults, metricName, sessionByNodeId) {
117275
+ const refs = [];
117276
+ const seen = new Set();
117277
+ const push = (nodeId, value)=>{
117278
+ if (!Number.isFinite(value) || seen.has(nodeId)) return;
117279
+ seen.add(nodeId);
117280
+ refs.push({
117281
+ nodeId,
117282
+ sessionId: sessionByNodeId.get(nodeId),
117283
+ value
117284
+ });
117285
+ };
117286
+ // Preferred: walk the aggregate's children leaf nodes.
117287
+ for (const childId of agg.children ?? []){
117288
+ const child = nodes[childId];
117289
+ if (child && isBaseResult(child)) {
117290
+ const nodeId = child.agent_data_id?.[0];
117291
+ if (nodeId) push(nodeId, Number(child.value));
117292
+ }
117293
+ }
117294
+ // Fallback: derive from raw Base results named after the metric when no children resolve.
117295
+ if (refs.length === 0) {
117296
+ for (const item of rawResults){
117297
+ if (isBaseResult(item) && item.result_name === metricName) {
117298
+ const nodeId = item.agent_data_id?.[0];
117299
+ if (nodeId) push(nodeId, Number(item.value));
117300
+ }
117301
+ }
117302
+ }
117303
+ return refs;
117304
+ }
117305
+ /** Build the custom (numerical) evaluator view model: one entry per numerical metric. */ function buildNumericalMetricsFromAggregate(input) {
117306
+ const { roots, nodes, agents, rawResults = [] } = input;
117307
+ const sessionByNodeId = buildSessionByNodeId(agents);
117308
+ // Prefer roots; fall back to scanning all nodes for numerical aggregates.
117309
+ const rootAggregates = roots.map((id)=>nodes[id]).filter((n)=>isNumericalAggregate(n));
117310
+ const aggregates = rootAggregates.length > 0 ? rootAggregates : Object.values(nodes).filter((n)=>isNumericalAggregate(n));
117311
+ return aggregates.map((agg, index)=>{
117312
+ const metricName = metricNameOf(agg);
117313
+ const nodeRefs = collectNodes(agg, nodes, rawResults, metricName, sessionByNodeId);
117314
+ return {
117315
+ key: agg.identifier ?? `${metricName}-${index}`,
117316
+ metricName,
117317
+ description: agg.metric?.description ?? undefined,
117318
+ mean: agg.mean,
117319
+ minimum: agg.minimum,
117320
+ maximum: agg.maximum,
117321
+ median: agg.median,
117322
+ std: agg.std,
117323
+ count: agg.values?.length ?? nodeRefs.length,
117324
+ nodes: nodeRefs
117325
+ };
117326
+ });
117327
+ }
117328
+
117329
+ const NodeActions = ({ sessionId, nodeId, onAgentNodeClick, onOpenVisualizer })=>{
117330
+ const { theme } = useTheme$1();
117331
+ const baseStyle = {
117332
+ display: "inline-flex",
117333
+ alignItems: "center",
117334
+ gap: 5,
117335
+ padding: "3px 9px",
117336
+ borderRadius: 6,
117337
+ fontSize: 12,
117338
+ fontWeight: 500,
117339
+ cursor: "pointer",
117340
+ background: "transparent",
117341
+ color: theme.colors.primary,
117342
+ border: `1px solid ${theme.colors.primary}`,
117343
+ whiteSpace: "nowrap",
117344
+ lineHeight: 1.4
117345
+ };
117346
+ if (!onAgentNodeClick && !onOpenVisualizer) {
117347
+ // No handlers: fall back to surfacing the (shortened) id so context isn't lost.
117348
+ return /*#__PURE__*/ React.createElement(Tooltip, {
117349
+ title: nodeId
117350
+ }, /*#__PURE__*/ React.createElement("span", {
117351
+ style: {
117352
+ fontFamily: "monospace",
117353
+ fontSize: 12,
117354
+ color: theme.colors.mutedForeground
117355
+ }
117356
+ }, nodeId.length > 8 ? nodeId.slice(0, 8) : nodeId));
117357
+ }
117358
+ return /*#__PURE__*/ React.createElement("span", {
117359
+ style: {
117360
+ display: "inline-flex",
117361
+ alignItems: "center",
117362
+ gap: 8
117363
+ },
117364
+ onClick: (e)=>e.stopPropagation()
117365
+ }, onAgentNodeClick && /*#__PURE__*/ React.createElement(Tooltip, {
117366
+ title: `Open Flow Details · ${nodeId}`
117367
+ }, /*#__PURE__*/ React.createElement("button", {
117368
+ type: "button",
117369
+ style: baseStyle,
117370
+ onClick: ()=>onAgentNodeClick(sessionId, nodeId)
117371
+ }, /*#__PURE__*/ React.createElement(PanelRightOpen, {
117372
+ size: 13,
117373
+ style: {
117374
+ flexShrink: 0
117375
+ }
117376
+ }), "Flow Details")), onOpenVisualizer && /*#__PURE__*/ React.createElement(Tooltip, {
117377
+ title: `Open in Visualizer · ${nodeId}`
117378
+ }, /*#__PURE__*/ React.createElement("button", {
117379
+ type: "button",
117380
+ style: baseStyle,
117381
+ onClick: ()=>onOpenVisualizer(sessionId, nodeId)
117382
+ }, /*#__PURE__*/ React.createElement(ChartNetwork, {
117383
+ size: 13,
117384
+ style: {
117385
+ flexShrink: 0
117386
+ }
117387
+ }), "Visualizer")));
117388
+ };
117389
+
117390
+ const NodeRow$1 = ({ node, metricName, onAgentNodeClick, onOpenVisualizer })=>{
117159
117391
  const { theme } = useTheme$1();
117160
117392
  const [showReasoning, setShowReasoning] = React.useState(false);
117161
- const clickable = !!onAgentNodeClick;
117162
- const open = ()=>onAgentNodeClick?.(node.sessionId, node.nodeId);
117163
117393
  return /*#__PURE__*/ React.createElement("div", {
117164
117394
  style: {
117165
117395
  display: "flex",
@@ -117173,38 +117403,27 @@ const NodeRow = ({ node, metricName, onAgentNodeClick })=>{
117173
117403
  gap: 10,
117174
117404
  flexWrap: "wrap"
117175
117405
  }
117176
- }, /*#__PURE__*/ React.createElement(Tooltip, {
117177
- title: clickable ? `Inspect run · ${node.nodeId}` : node.nodeId
117178
- }, /*#__PURE__*/ React.createElement("span", {
117179
- role: clickable ? "button" : undefined,
117180
- tabIndex: clickable ? 0 : undefined,
117181
- onClick: clickable ? open : undefined,
117182
- onKeyDown: clickable ? (e)=>{
117183
- if (e.key === "Enter" || e.key === " ") {
117184
- e.preventDefault();
117185
- open();
117186
- }
117187
- } : undefined,
117406
+ }, /*#__PURE__*/ React.createElement(Tag, {
117188
117407
  style: {
117408
+ margin: 0,
117189
117409
  display: "inline-flex",
117190
117410
  alignItems: "center",
117191
117411
  gap: 6,
117192
- padding: "3px 10px",
117193
- borderRadius: 6,
117194
- fontFamily: "monospace",
117195
- fontSize: 12,
117196
- fontWeight: 500,
117197
- background: theme.colors.tagLlminference?.bg ?? theme.colors.muted,
117198
- color: theme.colors.tagLlminference?.color ?? theme.colors.foreground,
117199
- border: `1px solid ${theme.colors.tagLlminference?.border ?? theme.colors.border}`,
117200
- cursor: clickable ? "pointer" : "default"
117412
+ background: theme.colors.tagToolLevel2?.bg ?? theme.colors.primary,
117413
+ color: theme.colors.tagToolLevel2?.color ?? theme.colors.primaryForeground,
117414
+ border: "none"
117201
117415
  }
117202
117416
  }, /*#__PURE__*/ React.createElement(Bot, {
117203
- size: 13,
117417
+ size: 12,
117204
117418
  style: {
117205
117419
  flexShrink: 0
117206
117420
  }
117207
- }), shortenId(node.nodeId))), node.reasoning && /*#__PURE__*/ React.createElement("button", {
117421
+ }), "Agent Node"), /*#__PURE__*/ React.createElement(NodeActions, {
117422
+ sessionId: node.sessionId,
117423
+ nodeId: node.nodeId,
117424
+ onAgentNodeClick: onAgentNodeClick,
117425
+ onOpenVisualizer: onOpenVisualizer
117426
+ }), node.reasoning && /*#__PURE__*/ React.createElement("button", {
117208
117427
  type: "button",
117209
117428
  onClick: ()=>setShowReasoning((s)=>!s),
117210
117429
  style: {
@@ -117238,7 +117457,7 @@ const NodeRow = ({ node, metricName, onAgentNodeClick })=>{
117238
117457
  marginBottom: 6,
117239
117458
  color: theme.colors.mutedForeground
117240
117459
  }
117241
- }, "Judge reasoning · ", metricName), node.reasoning));
117460
+ }, "Reasoning · ", metricName), node.reasoning));
117242
117461
  };
117243
117462
  const LabelPanelHeader = ({ label })=>{
117244
117463
  const { theme } = useTheme$1();
@@ -117271,7 +117490,7 @@ const LabelPanelHeader = ({ label })=>{
117271
117490
  }
117272
117491
  }, label.count, " ", label.count === 1 ? "node" : "nodes"));
117273
117492
  };
117274
- const MetricCard = ({ metric, onAgentNodeClick })=>{
117493
+ const MetricCard$1 = ({ metric, icon: Icon, onAgentNodeClick, onOpenVisualizer })=>{
117275
117494
  const { theme } = useTheme$1();
117276
117495
  const items = metric.labels.map((label)=>({
117277
117496
  key: label.labelName,
@@ -117291,11 +117510,12 @@ const MetricCard = ({ metric, onAgentNodeClick })=>{
117291
117510
  flexDirection: "column",
117292
117511
  gap: 12
117293
117512
  }
117294
- }, label.nodes.map((node)=>/*#__PURE__*/ React.createElement(NodeRow, {
117513
+ }, label.nodes.map((node)=>/*#__PURE__*/ React.createElement(NodeRow$1, {
117295
117514
  key: node.nodeId,
117296
117515
  node: node,
117297
117516
  metricName: metric.metricName,
117298
- onAgentNodeClick: onAgentNodeClick
117517
+ onAgentNodeClick: onAgentNodeClick,
117518
+ onOpenVisualizer: onOpenVisualizer
117299
117519
  })))
117300
117520
  }));
117301
117521
  return /*#__PURE__*/ React.createElement("div", {
@@ -117318,7 +117538,7 @@ const MetricCard = ({ metric, onAgentNodeClick })=>{
117318
117538
  gap: 12,
117319
117539
  flexWrap: "wrap"
117320
117540
  }
117321
- }, /*#__PURE__*/ React.createElement(Scale, {
117541
+ }, /*#__PURE__*/ React.createElement(Icon, {
117322
117542
  size: 16,
117323
117543
  style: {
117324
117544
  color: theme.colors.primary,
@@ -117359,13 +117579,13 @@ const MetricCard = ({ metric, onAgentNodeClick })=>{
117359
117579
  marginLeft: "auto",
117360
117580
  flexShrink: 0
117361
117581
  }
117362
- }, /*#__PURE__*/ React.createElement(Meta, {
117582
+ }, /*#__PURE__*/ React.createElement(Meta$1, {
117363
117583
  label: "Most common",
117364
117584
  value: metric.mostCommon
117365
- }), /*#__PURE__*/ React.createElement(Meta, {
117585
+ }), /*#__PURE__*/ React.createElement(Meta$1, {
117366
117586
  label: "Least common",
117367
117587
  value: metric.leastCommon
117368
- }), /*#__PURE__*/ React.createElement(Meta, {
117588
+ }), /*#__PURE__*/ React.createElement(Meta$1, {
117369
117589
  label: "Evaluations",
117370
117590
  value: String(metric.total)
117371
117591
  }))), metric.description && /*#__PURE__*/ React.createElement("div", {
@@ -117382,6 +117602,76 @@ const MetricCard = ({ metric, onAgentNodeClick })=>{
117382
117602
  }
117383
117603
  }));
117384
117604
  };
117605
+ const Meta$1 = ({ label, value })=>{
117606
+ const { theme } = useTheme$1();
117607
+ return /*#__PURE__*/ React.createElement("span", {
117608
+ style: {
117609
+ display: "flex",
117610
+ flexDirection: "column",
117611
+ alignItems: "flex-end",
117612
+ gap: 1
117613
+ }
117614
+ }, /*#__PURE__*/ React.createElement("span", {
117615
+ style: {
117616
+ fontSize: 10,
117617
+ textTransform: "uppercase",
117618
+ letterSpacing: 0.4,
117619
+ color: theme.colors.mutedForeground
117620
+ }
117621
+ }, label), /*#__PURE__*/ React.createElement("span", {
117622
+ style: {
117623
+ fontSize: 13,
117624
+ fontWeight: 500
117625
+ }
117626
+ }, value ?? "—"));
117627
+ };
117628
+ const CategoricalAggregateView = ({ metrics, title = "Categorical Evaluator - Aggregate View", icon = Tags, emptyLabel = "No categorical metrics available", onAgentNodeClick, onOpenVisualizer })=>{
117629
+ const { theme } = useTheme$1();
117630
+ if (!metrics.length) {
117631
+ return /*#__PURE__*/ React.createElement("div", {
117632
+ style: {
117633
+ padding: 24,
117634
+ textAlign: "center",
117635
+ color: theme.colors.mutedForeground,
117636
+ fontSize: 14
117637
+ }
117638
+ }, emptyLabel);
117639
+ }
117640
+ return /*#__PURE__*/ React.createElement("div", {
117641
+ style: {
117642
+ width: "100%",
117643
+ minWidth: 0
117644
+ }
117645
+ }, /*#__PURE__*/ React.createElement("h3", {
117646
+ style: {
117647
+ margin: "0 0 16px",
117648
+ fontSize: 16,
117649
+ fontWeight: 600
117650
+ }
117651
+ }, title), /*#__PURE__*/ React.createElement("div", {
117652
+ style: {
117653
+ display: "flex",
117654
+ flexDirection: "column",
117655
+ gap: 12
117656
+ }
117657
+ }, metrics.map((metric)=>/*#__PURE__*/ React.createElement(MetricCard$1, {
117658
+ key: metric.key,
117659
+ metric: metric,
117660
+ icon: icon,
117661
+ onAgentNodeClick: onAgentNodeClick,
117662
+ onOpenVisualizer: onOpenVisualizer
117663
+ }))));
117664
+ };
117665
+
117666
+ /**
117667
+ * Format a numerical value for display. Custom metric names are arbitrary, so we
117668
+ * use a light heuristic: latency-like metrics render as durations, integers get
117669
+ * thousands separators, everything else gets two decimals.
117670
+ */ function formatStat(value, metricName) {
117671
+ if (value === undefined || !Number.isFinite(value)) return "—";
117672
+ if (/seconds$/i.test(metricName) || /latency/i.test(metricName)) return formatLatency(value);
117673
+ return Number.isInteger(value) ? formatNumberWithCommas(value) : value.toFixed(2);
117674
+ }
117385
117675
  const Meta = ({ label, value })=>{
117386
117676
  const { theme } = useTheme$1();
117387
117677
  return /*#__PURE__*/ React.createElement("span", {
@@ -117405,7 +117695,149 @@ const Meta = ({ label, value })=>{
117405
117695
  }
117406
117696
  }, value ?? "—"));
117407
117697
  };
117408
- const JudgeAggregateView = ({ metrics, onAgentNodeClick })=>{
117698
+ const NodeRow = ({ node, metricName, onAgentNodeClick, onOpenVisualizer })=>{
117699
+ const { theme } = useTheme$1();
117700
+ return /*#__PURE__*/ React.createElement("div", {
117701
+ style: {
117702
+ display: "flex",
117703
+ alignItems: "center",
117704
+ gap: 10,
117705
+ flexWrap: "wrap"
117706
+ }
117707
+ }, /*#__PURE__*/ React.createElement(Tag, {
117708
+ style: {
117709
+ margin: 0,
117710
+ fontFamily: "monospace",
117711
+ fontWeight: 600,
117712
+ background: theme.colors.tagLlminference?.bg ?? theme.colors.muted,
117713
+ color: theme.colors.tagLlminference?.color ?? theme.colors.foreground,
117714
+ border: `1px solid ${theme.colors.tagLlminference?.border ?? theme.colors.border}`
117715
+ }
117716
+ }, formatStat(node.value, metricName)), /*#__PURE__*/ React.createElement(Tag, {
117717
+ style: {
117718
+ margin: 0,
117719
+ display: "inline-flex",
117720
+ alignItems: "center",
117721
+ gap: 6,
117722
+ background: theme.colors.tagToolLevel2?.bg ?? theme.colors.primary,
117723
+ color: theme.colors.tagToolLevel2?.color ?? theme.colors.primaryForeground,
117724
+ border: "none"
117725
+ }
117726
+ }, /*#__PURE__*/ React.createElement(Bot, {
117727
+ size: 12,
117728
+ style: {
117729
+ flexShrink: 0
117730
+ }
117731
+ }), "Agent Node"), /*#__PURE__*/ React.createElement(NodeActions, {
117732
+ sessionId: node.sessionId,
117733
+ nodeId: node.nodeId,
117734
+ onAgentNodeClick: onAgentNodeClick,
117735
+ onOpenVisualizer: onOpenVisualizer
117736
+ }));
117737
+ };
117738
+ const MetricCard = ({ metric, onAgentNodeClick, onOpenVisualizer })=>{
117739
+ const { theme } = useTheme$1();
117740
+ const items = metric.nodes.length > 0 ? [
117741
+ {
117742
+ key: "per-run",
117743
+ label: /*#__PURE__*/ React.createElement("span", {
117744
+ style: {
117745
+ fontSize: 13,
117746
+ fontWeight: 600
117747
+ }
117748
+ }, "Per-run values", " ", /*#__PURE__*/ React.createElement("span", {
117749
+ style: {
117750
+ color: theme.colors.mutedForeground,
117751
+ fontWeight: 400
117752
+ }
117753
+ }, "(", metric.nodes.length, ")")),
117754
+ children: /*#__PURE__*/ React.createElement("div", {
117755
+ style: {
117756
+ display: "flex",
117757
+ flexDirection: "column",
117758
+ gap: 12
117759
+ }
117760
+ }, metric.nodes.map((node)=>/*#__PURE__*/ React.createElement(NodeRow, {
117761
+ key: node.nodeId,
117762
+ node: node,
117763
+ metricName: metric.metricName,
117764
+ onAgentNodeClick: onAgentNodeClick,
117765
+ onOpenVisualizer: onOpenVisualizer
117766
+ })))
117767
+ }
117768
+ ] : [];
117769
+ return /*#__PURE__*/ React.createElement("div", {
117770
+ style: {
117771
+ background: theme.colors.card,
117772
+ border: `1px solid ${theme.colors.border}`,
117773
+ borderRadius: 8,
117774
+ overflow: "hidden"
117775
+ }
117776
+ }, /*#__PURE__*/ React.createElement("div", {
117777
+ style: {
117778
+ padding: "14px 18px",
117779
+ borderBottom: metric.nodes.length > 0 ? `1px solid ${theme.colors.border}` : "none",
117780
+ background: theme.colors.secondary
117781
+ }
117782
+ }, /*#__PURE__*/ React.createElement("div", {
117783
+ style: {
117784
+ display: "flex",
117785
+ alignItems: "center",
117786
+ gap: 12,
117787
+ flexWrap: "wrap"
117788
+ }
117789
+ }, /*#__PURE__*/ React.createElement(Gauge, {
117790
+ size: 16,
117791
+ style: {
117792
+ color: theme.colors.primary,
117793
+ flexShrink: 0
117794
+ }
117795
+ }), /*#__PURE__*/ React.createElement("span", {
117796
+ style: {
117797
+ fontSize: 15,
117798
+ fontWeight: 600
117799
+ }
117800
+ }, metric.metricName), /*#__PURE__*/ React.createElement("div", {
117801
+ style: {
117802
+ display: "flex",
117803
+ gap: 20,
117804
+ marginLeft: "auto",
117805
+ flexShrink: 0,
117806
+ flexWrap: "wrap"
117807
+ }
117808
+ }, /*#__PURE__*/ React.createElement(Meta, {
117809
+ label: "Mean",
117810
+ value: formatStat(metric.mean, metric.metricName)
117811
+ }), /*#__PURE__*/ React.createElement(Meta, {
117812
+ label: "Min",
117813
+ value: formatStat(metric.minimum, metric.metricName)
117814
+ }), /*#__PURE__*/ React.createElement(Meta, {
117815
+ label: "Max",
117816
+ value: formatStat(metric.maximum, metric.metricName)
117817
+ }), /*#__PURE__*/ React.createElement(Meta, {
117818
+ label: "Median",
117819
+ value: formatStat(metric.median, metric.metricName)
117820
+ }), /*#__PURE__*/ React.createElement(Meta, {
117821
+ label: "Std",
117822
+ value: formatStat(metric.std, metric.metricName)
117823
+ }), /*#__PURE__*/ React.createElement(Meta, {
117824
+ label: "Runs",
117825
+ value: String(metric.count)
117826
+ }))), metric.description && /*#__PURE__*/ React.createElement("div", {
117827
+ style: {
117828
+ marginTop: 6,
117829
+ fontSize: 12,
117830
+ color: theme.colors.mutedForeground
117831
+ }
117832
+ }, metric.description)), metric.nodes.length > 0 && /*#__PURE__*/ React.createElement(Collapse, {
117833
+ items: items,
117834
+ bordered: false,
117835
+ style: {
117836
+ background: theme.colors.card
117837
+ }
117838
+ }));
117839
+ };
117840
+ const NumericalAggregateView = ({ metrics, onAgentNodeClick, onOpenVisualizer })=>{
117409
117841
  const { theme } = useTheme$1();
117410
117842
  if (!metrics.length) {
117411
117843
  return /*#__PURE__*/ React.createElement("div", {
@@ -117415,7 +117847,7 @@ const JudgeAggregateView = ({ metrics, onAgentNodeClick })=>{
117415
117847
  color: theme.colors.mutedForeground,
117416
117848
  fontSize: 14
117417
117849
  }
117418
- }, "No judge metrics available");
117850
+ }, "No numerical metrics available");
117419
117851
  }
117420
117852
  return /*#__PURE__*/ React.createElement("div", {
117421
117853
  style: {
@@ -117428,7 +117860,7 @@ const JudgeAggregateView = ({ metrics, onAgentNodeClick })=>{
117428
117860
  fontSize: 16,
117429
117861
  fontWeight: 600
117430
117862
  }
117431
- }, "Judge Evaluator - Aggregate View"), /*#__PURE__*/ React.createElement("div", {
117863
+ }, "Custom Evaluator - Aggregate View"), /*#__PURE__*/ React.createElement("div", {
117432
117864
  style: {
117433
117865
  display: "flex",
117434
117866
  flexDirection: "column",
@@ -117437,7 +117869,8 @@ const JudgeAggregateView = ({ metrics, onAgentNodeClick })=>{
117437
117869
  }, metrics.map((metric)=>/*#__PURE__*/ React.createElement(MetricCard, {
117438
117870
  key: metric.key,
117439
117871
  metric: metric,
117440
- onAgentNodeClick: onAgentNodeClick
117872
+ onAgentNodeClick: onAgentNodeClick,
117873
+ onOpenVisualizer: onOpenVisualizer
117441
117874
  }))));
117442
117875
  };
117443
117876
 
@@ -117445,7 +117878,10 @@ function detectAggregateType(nodes) {
117445
117878
  const values = Object.values(nodes);
117446
117879
  if (values.some((n)=>n?.type === "ToolAggregate")) return "ToolUse";
117447
117880
  if (values.some((n)=>n?.type === "LLMInferenceAggregate")) return "LLMInference";
117448
- if (values.some((n)=>n?.type === "CategoricalAggregate")) return "Judge";
117881
+ // Categorical covers the built-in JudgeEvaluator and any custom evaluator with categorical metrics.
117882
+ if (values.some((n)=>n?.type === "CategoricalAggregate")) return "Categorical";
117883
+ // Custom numerical evaluators (anything that isn't ToolUse/LLMInference/categorical) emit NumericalAggregate.
117884
+ if (values.some((n)=>n?.type === "NumericalAggregate")) return "Numerical";
117449
117885
  return null;
117450
117886
  }
117451
117887
  function buildLLMInferenceFlatRows(nodes) {
@@ -117487,9 +117923,10 @@ function formatAggregateValue(value, metricName) {
117487
117923
  const DEFAULT_TITLES = {
117488
117924
  ToolUse: "Tool Use Evaluator - Aggregate View",
117489
117925
  LLMInference: "LLM Inference Evaluator - Aggregate View",
117490
- Judge: "Judge Evaluator - Aggregate View"
117926
+ Categorical: "Categorical Evaluator - Aggregate View",
117927
+ Numerical: "Custom Evaluator - Aggregate View"
117491
117928
  };
117492
- const AggregateResultsTable = ({ rawResults = [], aggregateResults, agents, title, evaluatorName, onAgentNodeClick })=>{
117929
+ const AggregateResultsTable = ({ rawResults = [], aggregateResults, agents, title, evaluatorName, onAgentNodeClick, onOpenVisualizer })=>{
117493
117930
  const { theme } = useTheme$1();
117494
117931
  const [expandedRowKeys, setExpandedRowKeys] = React.useState([]);
117495
117932
  const { aggregateType, dataSource, llmIsTree, llmExpandableKeys } = React.useMemo(()=>{
@@ -117529,10 +117966,23 @@ const AggregateResultsTable = ({ rawResults = [], aggregateResults, agents, titl
117529
117966
  llmExpandableKeys
117530
117967
  };
117531
117968
  }
117532
- if (type === "Judge") {
117969
+ if (type === "Categorical") {
117970
+ return {
117971
+ aggregateType: type,
117972
+ dataSource: buildCategoricalMetricsFromAggregate({
117973
+ roots: aggregateResults.roots,
117974
+ nodes: aggregateResults.nodes,
117975
+ agents,
117976
+ rawResults
117977
+ }),
117978
+ llmIsTree: false,
117979
+ llmExpandableKeys: []
117980
+ };
117981
+ }
117982
+ if (type === "Numerical") {
117533
117983
  return {
117534
117984
  aggregateType: type,
117535
- dataSource: buildJudgeMetricsFromAggregate({
117985
+ dataSource: buildNumericalMetricsFromAggregate({
117536
117986
  roots: aggregateResults.roots,
117537
117987
  nodes: aggregateResults.nodes,
117538
117988
  agents,
@@ -117657,7 +118107,7 @@ const AggregateResultsTable = ({ rawResults = [], aggregateResults, agents, titl
117657
118107
  dataIndex: "name",
117658
118108
  key: "name",
117659
118109
  render: (name, row)=>{
117660
- const isClickable = row.level === 3 && row.agentNodeId && onAgentNodeClick;
118110
+ const showActions = row.level === 3 && row.agentNodeId && (onAgentNodeClick || onOpenVisualizer);
117661
118111
  return /*#__PURE__*/ React.createElement("span", {
117662
118112
  style: {
117663
118113
  display: "inline-flex",
@@ -117665,27 +118115,16 @@ const AggregateResultsTable = ({ rawResults = [], aggregateResults, agents, titl
117665
118115
  gap: 8,
117666
118116
  fontFamily: "monospace",
117667
118117
  fontSize: 13,
117668
- ...isClickable && {
117669
- cursor: "pointer",
117670
- color: theme.colors.primary
117671
- }
117672
- },
117673
- onClick: isClickable ? (e)=>{
117674
- e.stopPropagation();
117675
- onAgentNodeClick(row.sessionId, row.agentNodeId);
117676
- } : undefined,
117677
- onKeyDown: isClickable ? (e)=>{
117678
- if (e.key === "Enter" || e.key === " ") {
117679
- e.preventDefault();
117680
- e.stopPropagation();
117681
- onAgentNodeClick(row.sessionId, row.agentNodeId);
117682
- }
117683
- } : undefined,
117684
- role: isClickable ? "button" : undefined,
117685
- tabIndex: isClickable ? 0 : undefined
118118
+ flexWrap: "wrap"
118119
+ }
117686
118120
  }, /*#__PURE__*/ React.createElement(LLMTagForLevel, {
117687
118121
  level: row.level
117688
- }), name);
118122
+ }), showActions ? /*#__PURE__*/ React.createElement(NodeActions, {
118123
+ sessionId: row.sessionId,
118124
+ nodeId: row.agentNodeId,
118125
+ onAgentNodeClick: onAgentNodeClick,
118126
+ onOpenVisualizer: onOpenVisualizer
118127
+ }) : name);
117689
118128
  }
117690
118129
  },
117691
118130
  ...METRIC_COLUMNS.map(({ key, title, Icon })=>({
@@ -117869,10 +118308,24 @@ const AggregateResultsTable = ({ rawResults = [], aggregateResults, agents, titl
117869
118308
  }
117870
118309
  }));
117871
118310
  }
117872
- if (aggregateType === "Judge") {
117873
- return /*#__PURE__*/ React.createElement(JudgeAggregateView, {
118311
+ if (aggregateType === "Categorical") {
118312
+ // JudgeEvaluator keeps its branding; any other (custom) categorical evaluator
118313
+ // renders through the same view with neutral categorical branding.
118314
+ const isJudge = (evaluatorName ?? "").includes("Judge");
118315
+ return /*#__PURE__*/ React.createElement(CategoricalAggregateView, {
118316
+ metrics: dataSource,
118317
+ title: title ?? (isJudge ? "Judge Evaluator - Aggregate View" : "Custom Evaluator - Aggregate View"),
118318
+ icon: isJudge ? Scale : Tags,
118319
+ emptyLabel: isJudge ? "No judge metrics available" : "No categorical metrics available",
118320
+ onAgentNodeClick: onAgentNodeClick,
118321
+ onOpenVisualizer: onOpenVisualizer
118322
+ });
118323
+ }
118324
+ if (aggregateType === "Numerical") {
118325
+ return /*#__PURE__*/ React.createElement(NumericalAggregateView, {
117874
118326
  metrics: dataSource,
117875
- onAgentNodeClick: onAgentNodeClick
118327
+ onAgentNodeClick: onAgentNodeClick,
118328
+ onOpenVisualizer: onOpenVisualizer
117876
118329
  });
117877
118330
  }
117878
118331
  // ToolUse (tree table)
@@ -117941,7 +118394,7 @@ const AggregateResultsTable = ({ rawResults = [], aggregateResults, agents, titl
117941
118394
  dataIndex: "name",
117942
118395
  key: "name",
117943
118396
  render: (name, row)=>{
117944
- const isClickable = (row.level === 2 || row.level === 3) && row.nodeId && onAgentNodeClick;
118397
+ const showActions = (row.level === 2 || row.level === 3) && row.nodeId && (onAgentNodeClick || onOpenVisualizer);
117945
118398
  return /*#__PURE__*/ React.createElement("span", {
117946
118399
  style: {
117947
118400
  display: "inline-flex",
@@ -117949,27 +118402,16 @@ const AggregateResultsTable = ({ rawResults = [], aggregateResults, agents, titl
117949
118402
  gap: 8,
117950
118403
  fontFamily: "monospace",
117951
118404
  fontSize: 13,
117952
- ...isClickable && {
117953
- cursor: "pointer",
117954
- color: theme.colors.primary
117955
- }
117956
- },
117957
- onClick: isClickable ? (e)=>{
117958
- e.stopPropagation();
117959
- onAgentNodeClick(row.sessionId, row.nodeId);
117960
- } : undefined,
117961
- onKeyDown: isClickable ? (e)=>{
117962
- if (e.key === "Enter" || e.key === " ") {
117963
- e.preventDefault();
117964
- e.stopPropagation();
117965
- onAgentNodeClick(row.sessionId, row.nodeId);
117966
- }
117967
- } : undefined,
117968
- role: isClickable ? "button" : undefined,
117969
- tabIndex: isClickable ? 0 : undefined
118405
+ flexWrap: "wrap"
118406
+ }
117970
118407
  }, /*#__PURE__*/ React.createElement(TagForLevel, {
117971
118408
  level: row.level
117972
- }), name);
118409
+ }), showActions ? /*#__PURE__*/ React.createElement(NodeActions, {
118410
+ sessionId: row.sessionId,
118411
+ nodeId: row.nodeId,
118412
+ onAgentNodeClick: onAgentNodeClick,
118413
+ onOpenVisualizer: onOpenVisualizer
118414
+ }) : name);
117973
118415
  }
117974
118416
  },
117975
118417
  {
@@ -118147,7 +118589,8 @@ function isCategoricalMetric(def) {
118147
118589
  const aggregateTypes = [
118148
118590
  "ToolAggregate",
118149
118591
  "LLMInferenceAggregate",
118150
- "CategoricalAggregate"
118592
+ "CategoricalAggregate",
118593
+ "NumericalAggregate"
118151
118594
  ];
118152
118595
  const aggregateItems = Object.values(nodes).filter((n)=>n?.type && aggregateTypes.includes(n.type));
118153
118596
  return [
@@ -118287,7 +118730,7 @@ function isCategoricalMetric(def) {
118287
118730
  }, 0) ?? 0;
118288
118731
  }
118289
118732
 
118290
- const EvaluatorResult = ({ evaluation, evaluatorId, backHref, onAgentNodeClick })=>{
118733
+ const EvaluatorResult = ({ evaluation, evaluatorId, backHref, onAgentNodeClick, onOpenVisualizer })=>{
118291
118734
  const { theme } = useTheme$1();
118292
118735
  const labelColor = theme.colors.mutedForeground;
118293
118736
  const derived = evaluation ? deriveEvaluatorResultData(evaluation, evaluatorId) : null;
@@ -118430,7 +118873,8 @@ const EvaluatorResult = ({ evaluation, evaluatorId, backHref, onAgentNodeClick }
118430
118873
  aggregateResults: aggregateResults,
118431
118874
  agents: evaluation.agents,
118432
118875
  evaluatorName: evaluatorName,
118433
- onAgentNodeClick: onAgentNodeClick
118876
+ onAgentNodeClick: onAgentNodeClick,
118877
+ onOpenVisualizer: onOpenVisualizer
118434
118878
  }))));
118435
118879
  };
118436
118880