inspect-ai 0.3.108__py3-none-any.whl → 0.3.109__py3-none-any.whl

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.
Files changed (141) hide show
  1. inspect_ai/_eval/task/log.py +1 -1
  2. inspect_ai/_eval/task/run.py +1 -1
  3. inspect_ai/_util/dateutil.py +40 -0
  4. inspect_ai/_view/schema.py +11 -0
  5. inspect_ai/_view/www/CLAUDE.md +1 -1
  6. inspect_ai/_view/www/dist/assets/index.css +2068 -1796
  7. inspect_ai/_view/www/dist/assets/index.js +7951 -3643
  8. inspect_ai/_view/www/package.json +3 -2
  9. inspect_ai/_view/www/src/@types/log.d.ts +5 -5
  10. inspect_ai/_view/www/src/app/App.css +71 -4
  11. inspect_ai/_view/www/src/app/App.tsx +7 -0
  12. inspect_ai/_view/www/src/app/appearance/icons.ts +18 -2
  13. inspect_ai/_view/www/src/app/content/RenderedContent.tsx +7 -9
  14. inspect_ai/_view/www/src/app/log-list/LogItem.ts +18 -0
  15. inspect_ai/_view/www/src/app/log-list/LogListFooter.module.css +55 -0
  16. inspect_ai/_view/www/src/app/log-list/LogListFooter.tsx +67 -0
  17. inspect_ai/_view/www/src/app/log-list/LogPager.module.css +29 -0
  18. inspect_ai/_view/www/src/app/log-list/LogPager.tsx +134 -0
  19. inspect_ai/_view/www/src/app/log-list/LogsFilterInput.module.css +5 -0
  20. inspect_ai/_view/www/src/app/log-list/LogsFilterInput.tsx +31 -0
  21. inspect_ai/_view/www/src/app/log-list/LogsPanel.module.css +12 -0
  22. inspect_ai/_view/www/src/app/log-list/LogsPanel.tsx +178 -0
  23. inspect_ai/_view/www/src/app/log-list/grid/LogListGrid.module.css +115 -0
  24. inspect_ai/_view/www/src/app/log-list/grid/LogListGrid.tsx +304 -0
  25. inspect_ai/_view/www/src/app/log-list/grid/columns/CompletedDate.module.css +6 -0
  26. inspect_ai/_view/www/src/app/log-list/grid/columns/CompletedDate.tsx +64 -0
  27. inspect_ai/_view/www/src/app/log-list/grid/columns/EmptyCell.module.css +3 -0
  28. inspect_ai/_view/www/src/app/log-list/grid/columns/EmptyCell.tsx +7 -0
  29. inspect_ai/_view/www/src/app/log-list/grid/columns/FileName.module.css +20 -0
  30. inspect_ai/_view/www/src/app/log-list/grid/columns/FileName.tsx +52 -0
  31. inspect_ai/_view/www/src/app/log-list/grid/columns/Icon.module.css +11 -0
  32. inspect_ai/_view/www/src/app/log-list/grid/columns/Icon.tsx +35 -0
  33. inspect_ai/_view/www/src/app/log-list/grid/columns/Model.module.css +6 -0
  34. inspect_ai/_view/www/src/app/log-list/grid/columns/Model.tsx +34 -0
  35. inspect_ai/_view/www/src/app/log-list/grid/columns/Score.module.css +6 -0
  36. inspect_ai/_view/www/src/app/log-list/grid/columns/Score.tsx +61 -0
  37. inspect_ai/_view/www/src/app/log-list/grid/columns/Status.module.css +15 -0
  38. inspect_ai/_view/www/src/app/log-list/grid/columns/Status.tsx +95 -0
  39. inspect_ai/_view/www/src/app/log-list/grid/columns/Task.module.css +20 -0
  40. inspect_ai/_view/www/src/app/log-list/grid/columns/Task.tsx +50 -0
  41. inspect_ai/_view/www/src/app/log-list/grid/columns/columns.ts +27 -0
  42. inspect_ai/_view/www/src/app/log-view/LogView.tsx +2 -5
  43. inspect_ai/_view/www/src/app/log-view/LogViewContainer.tsx +4 -30
  44. inspect_ai/_view/www/src/app/log-view/LogViewLayout.tsx +5 -30
  45. inspect_ai/_view/www/src/app/log-view/tabs/TaskTab.tsx +4 -7
  46. inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/PrimaryBar.module.css +2 -0
  47. inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/PrimaryBar.tsx +3 -31
  48. inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ResultsPanel.tsx +7 -57
  49. inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ScoreGrid.tsx +2 -2
  50. inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/SecondaryBar.tsx +7 -1
  51. inspect_ai/_view/www/src/app/log-view/{navbar/Navbar.tsx → title-view/TitleView.tsx} +3 -6
  52. inspect_ai/_view/www/src/app/navbar/Navbar.module.css +57 -0
  53. inspect_ai/_view/www/src/app/navbar/Navbar.tsx +117 -0
  54. inspect_ai/_view/www/src/app/navbar/useBreadcrumbTruncation.ts +128 -0
  55. inspect_ai/_view/www/src/app/plan/DatasetDetailView.tsx +3 -3
  56. inspect_ai/_view/www/src/app/plan/DetailStep.tsx +6 -6
  57. inspect_ai/_view/www/src/app/plan/PlanDetailView.module.css +1 -0
  58. inspect_ai/_view/www/src/app/plan/ScorerDetailView.tsx +1 -1
  59. inspect_ai/_view/www/src/app/routing/AppRouter.tsx +28 -4
  60. inspect_ai/_view/www/src/app/routing/RouteDispatcher.tsx +28 -0
  61. inspect_ai/_view/www/src/app/routing/sampleNavigation.ts +76 -7
  62. inspect_ai/_view/www/src/app/routing/url.ts +193 -20
  63. inspect_ai/_view/www/src/app/samples/SampleDisplay.tsx +3 -17
  64. inspect_ai/_view/www/src/app/samples/descriptor/score/ScoreDescriptor.tsx +1 -1
  65. inspect_ai/_view/www/src/app/samples/transcript/SubtaskEventView.tsx +2 -2
  66. inspect_ai/_view/www/src/app/samples/transcript/TranscriptPanel.tsx +2 -2
  67. inspect_ai/_view/www/src/app/samples/transcript/outline/tree-visitors.ts +5 -0
  68. inspect_ai/_view/www/src/app/samples/transcript/transform/treeify.ts +26 -10
  69. inspect_ai/_view/www/src/app/types.ts +21 -1
  70. inspect_ai/_view/www/src/client/api/api-http.ts +2 -1
  71. inspect_ai/_view/www/src/client/api/api-shared.ts +0 -32
  72. inspect_ai/_view/www/src/client/api/client-api.ts +1 -1
  73. inspect_ai/_view/www/src/client/remote/remoteLogFile.ts +38 -6
  74. inspect_ai/_view/www/src/components/TextInput.module.css +45 -0
  75. inspect_ai/_view/www/src/components/TextInput.tsx +52 -0
  76. inspect_ai/_view/www/src/constants.ts +18 -0
  77. inspect_ai/_view/www/src/img/inspect-16.svg +10 -0
  78. inspect_ai/_view/www/src/img/inspect-back.svg +5 -0
  79. inspect_ai/_view/www/src/img/inspect-file.svg +26 -0
  80. inspect_ai/_view/www/src/img/inspect-forward.svg +7 -0
  81. inspect_ai/_view/www/src/img/inspect-home.svg +18 -0
  82. inspect_ai/_view/www/src/scoring/metrics.ts +75 -0
  83. inspect_ai/_view/www/src/scoring/scores.ts +19 -0
  84. inspect_ai/_view/www/src/scoring/types.ts +11 -0
  85. inspect_ai/_view/www/src/state/appSlice.ts +27 -7
  86. inspect_ai/_view/www/src/state/clientEvents.ts +73 -0
  87. inspect_ai/_view/www/src/state/clientEventsService.ts +105 -0
  88. inspect_ai/_view/www/src/state/hooks.ts +118 -1
  89. inspect_ai/_view/www/src/state/log.ts +19 -0
  90. inspect_ai/_view/www/src/state/logPolling.ts +3 -1
  91. inspect_ai/_view/www/src/state/logSlice.ts +9 -0
  92. inspect_ai/_view/www/src/state/logsSlice.ts +157 -15
  93. inspect_ai/_view/www/src/state/samplePolling.ts +4 -2
  94. inspect_ai/_view/www/src/tests/utils/path.test.ts +3 -3
  95. inspect_ai/_view/www/src/utils/evallog.ts +31 -0
  96. inspect_ai/_view/www/src/utils/path.ts +28 -0
  97. inspect_ai/_view/www/src/utils/uri.ts +49 -0
  98. inspect_ai/_view/www/yarn.lock +54 -17
  99. inspect_ai/analysis/beta/_dataframe/util.py +106 -10
  100. inspect_ai/log/_recorders/buffer/database.py +55 -16
  101. inspect_ai/model/_model.py +1 -1
  102. inspect_ai/model/_providers/providers.py +2 -2
  103. inspect_ai/model/_providers/vertex.py +3 -0
  104. inspect_ai/tool/_mcp/_mcp.py +6 -1
  105. inspect_ai/tool/_mcp/sampling.py +8 -1
  106. inspect_ai/tool/_tools/_bash_session.py +3 -6
  107. inspect_ai/tool/_tools/_web_browser/_web_browser.py +3 -8
  108. inspect_ai/util/_anyio.py +12 -3
  109. {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/METADATA +2 -2
  110. {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/RECORD +124 -94
  111. inspect_ai/_util/datetime.py +0 -10
  112. inspect_ai/_view/www/src/app/content/MetaDataView.module.css +0 -35
  113. inspect_ai/_view/www/src/app/content/MetaDataView.tsx +0 -101
  114. inspect_ai/_view/www/src/app/log-view/utils.ts +0 -34
  115. inspect_ai/_view/www/src/app/sidebar/EvalStatus.module.css +0 -15
  116. inspect_ai/_view/www/src/app/sidebar/EvalStatus.tsx +0 -72
  117. inspect_ai/_view/www/src/app/sidebar/LogDirectoryTitleView.module.css +0 -16
  118. inspect_ai/_view/www/src/app/sidebar/LogDirectoryTitleView.tsx +0 -70
  119. inspect_ai/_view/www/src/app/sidebar/Sidebar.module.css +0 -77
  120. inspect_ai/_view/www/src/app/sidebar/Sidebar.tsx +0 -119
  121. inspect_ai/_view/www/src/app/sidebar/SidebarLogEntry.module.css +0 -29
  122. inspect_ai/_view/www/src/app/sidebar/SidebarLogEntry.tsx +0 -96
  123. inspect_ai/_view/www/src/app/sidebar/SidebarScoreView.module.css +0 -23
  124. inspect_ai/_view/www/src/app/sidebar/SidebarScoreView.tsx +0 -44
  125. inspect_ai/_view/www/src/app/sidebar/SidebarScoresView.module.css +0 -35
  126. inspect_ai/_view/www/src/app/sidebar/SidebarScoresView.tsx +0 -63
  127. inspect_ai/_view/www/src/state/logsPolling.ts +0 -118
  128. /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ModelRolesView.module.css +0 -0
  129. /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ModelRolesView.tsx +0 -0
  130. /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ResultsPanel.module.css +0 -0
  131. /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/RunningStatusPanel.module.css +0 -0
  132. /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/RunningStatusPanel.tsx +0 -0
  133. /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ScoreGrid.module.css +0 -0
  134. /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/SecondaryBar.module.css +0 -0
  135. /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/StatusPanel.module.css +0 -0
  136. /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/StatusPanel.tsx +0 -0
  137. /inspect_ai/_view/www/src/app/log-view/{navbar/Navbar.module.css → title-view/TitleView.module.css} +0 -0
  138. {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/WHEEL +0 -0
  139. {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/entry_points.txt +0 -0
  140. {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/licenses/LICENSE +0 -0
  141. {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/top_level.txt +0 -0
@@ -1,24 +1,19 @@
1
1
  import clsx from "clsx";
2
- import { FC, useCallback } from "react";
2
+ import { FC } from "react";
3
3
  import { EvalResults, EvalSpec, Status } from "../../../@types/log";
4
4
  import { RunningMetric } from "../../../client/api/types";
5
5
  import { CopyButton } from "../../../components/CopyButton";
6
6
  import { kModelNone } from "../../../constants";
7
+ import { toDisplayScorers } from "../../../scoring/metrics";
7
8
  import { useStore } from "../../../state/store";
8
9
  import { filename } from "../../../utils/path";
9
- import { ApplicationIcons } from "../../appearance/icons";
10
10
  import { ModelRolesView } from "./ModelRolesView";
11
11
  import styles from "./PrimaryBar.module.css";
12
- import {
13
- displayScorersFromRunningMetrics,
14
- ResultsPanel,
15
- toDisplayScorers,
16
- } from "./ResultsPanel";
12
+ import { displayScorersFromRunningMetrics, ResultsPanel } from "./ResultsPanel";
17
13
  import { RunningStatusPanel } from "./RunningStatusPanel";
18
14
  import { CancelledPanel, ErroredPanel } from "./StatusPanel";
19
15
 
20
16
  interface PrimaryBarProps {
21
- showToggle: boolean;
22
17
  status?: Status;
23
18
  evalResults?: EvalResults | null;
24
19
  runningMetrics?: RunningMetric[];
@@ -27,24 +22,17 @@ interface PrimaryBarProps {
27
22
  }
28
23
 
29
24
  export const PrimaryBar: FC<PrimaryBarProps> = ({
30
- showToggle,
31
25
  status,
32
26
  evalResults,
33
27
  runningMetrics,
34
28
  evalSpec,
35
29
  sampleCount,
36
30
  }) => {
37
- const offCanvas = useStore((state) => state.app.offcanvas);
38
- const setOffCanvas = useStore((state) => state.appActions.setOffcanvas);
39
31
  const streamSamples = useStore((state) => state.capabilities.streamSamples);
40
32
  const selectedLogFile = useStore((state) => state.logs.selectedLogFile);
41
33
 
42
34
  const logFileName = selectedLogFile ? filename(selectedLogFile) : "";
43
35
 
44
- const handleToggle = useCallback(() => {
45
- setOffCanvas(!offCanvas);
46
- }, [offCanvas, setOffCanvas]);
47
-
48
36
  const hasRunningMetrics = runningMetrics && runningMetrics.length > 0;
49
37
 
50
38
  return (
@@ -57,22 +45,6 @@ export const PrimaryBar: FC<PrimaryBarProps> = ({
57
45
  styles.container,
58
46
  )}
59
47
  >
60
- {showToggle ? (
61
- <button
62
- id="sidebarToggle"
63
- onClick={handleToggle}
64
- className={clsx(
65
- "btn",
66
- offCanvas ? "d-md-none" : undefined,
67
- styles.toggle,
68
- )}
69
- type="button"
70
- >
71
- <i className={ApplicationIcons.menu}></i>
72
- </button>
73
- ) : (
74
- ""
75
- )}
76
48
  <div className={styles.body}>
77
49
  <div className={styles.bodyContainer}>
78
50
  <div
@@ -1,27 +1,16 @@
1
1
  import clsx from "clsx";
2
2
  import { FC } from "react";
3
- import { Scores } from "../../../@types/log";
4
3
  import { RunningMetric } from "../../../client/api/types";
5
4
  import { LinkButton } from "../../../components/LinkButton";
6
5
  import { Modal } from "../../../components/Modal";
6
+ import { metricDisplayName } from "../../../scoring/metrics";
7
+ import { groupScorers } from "../../../scoring/scores";
8
+ import { MetricSummary, ScoreSummary } from "../../../scoring/types";
7
9
  import { useProperty } from "../../../state/hooks";
8
10
  import { formatPrettyDecimal } from "../../../utils/format";
9
- import { metricDisplayName } from "../utils";
10
11
  import styles from "./ResultsPanel.module.css";
11
12
  import { ScoreGrid } from "./ScoreGrid";
12
13
 
13
- export interface ResultsMetric {
14
- name: string;
15
- params?: {};
16
- value: number;
17
- }
18
-
19
- export interface ResultsScorer {
20
- scorer: string;
21
- reducer?: string;
22
- metrics: ResultsMetric[];
23
- }
24
-
25
14
  const kMaxPrimaryScoreRows = 4;
26
15
 
27
16
  export const displayScorersFromRunningMetrics = (metrics?: RunningMetric[]) => {
@@ -35,7 +24,7 @@ export const displayScorersFromRunningMetrics = (metrics?: RunningMetric[]) => {
35
24
  : metric.scorer;
36
25
  };
37
26
 
38
- const scorers: Record<string, ResultsScorer> = {};
27
+ const scorers: Record<string, ScoreSummary> = {};
39
28
  metrics.forEach((metric) => {
40
29
  if (metric.value !== undefined && metric.value !== null) {
41
30
  const key = getKey(metric);
@@ -62,29 +51,8 @@ export const displayScorersFromRunningMetrics = (metrics?: RunningMetric[]) => {
62
51
  return Object.values(scorers);
63
52
  };
64
53
 
65
- export const toDisplayScorers = (scores?: Scores): ResultsScorer[] => {
66
- if (!scores) {
67
- return [];
68
- }
69
-
70
- return scores.map((score) => {
71
- return {
72
- scorer: score.name,
73
- reducer: score.reducer === null ? undefined : score.reducer,
74
- metrics: Object.keys(score.metrics).map((key) => {
75
- const metric = score.metrics[key];
76
- return {
77
- name: metric.name,
78
- value: metric.value,
79
- params: metric.params,
80
- };
81
- }),
82
- };
83
- });
84
- };
85
-
86
54
  interface ResultsPanelProps {
87
- scorers?: ResultsScorer[];
55
+ scorers?: ScoreSummary[];
88
56
  }
89
57
 
90
58
  export const ResultsPanel: FC<ResultsPanelProps> = ({ scorers }) => {
@@ -121,7 +89,7 @@ export const ResultsPanel: FC<ResultsPanelProps> = ({ scorers }) => {
121
89
  );
122
90
  } else {
123
91
  const showReducer = scorers.findIndex((score) => !!score.reducer) !== -1;
124
- const grouped = groupMetrics(scorers);
92
+ const grouped = groupScorers(scorers);
125
93
 
126
94
  // Try to select metrics with a group size 5 or less, if possible
127
95
  let primaryResults = grouped[0];
@@ -174,26 +142,8 @@ export const ResultsPanel: FC<ResultsPanelProps> = ({ scorers }) => {
174
142
  }
175
143
  };
176
144
 
177
- const metricsKey = (metrics: ResultsMetric[]): string => {
178
- const metricKey = metrics.map((m) => m.name).join("");
179
- return metricKey;
180
- };
181
-
182
- const groupMetrics = (scorers: ResultsScorer[]): ResultsScorer[][] => {
183
- const results: Record<string, ResultsScorer[]> = {};
184
- scorers.forEach((scorer) => {
185
- if (scorer.metrics.length > 0) {
186
- const key = metricsKey(scorer.metrics);
187
- results[key] = results[key] || [];
188
-
189
- results[key].push(scorer);
190
- }
191
- });
192
- return Object.values(results);
193
- };
194
-
195
145
  interface VerticalMetricProps {
196
- metric: ResultsMetric;
146
+ metric: MetricSummary;
197
147
  reducer?: string;
198
148
  isFirst: boolean;
199
149
  showReducer: boolean;
@@ -1,12 +1,12 @@
1
1
  import clsx from "clsx";
2
2
  import { FC, ReactNode } from "react";
3
3
  import { formatPrettyDecimal } from "../../../utils/format";
4
- import { ResultsScorer } from "./ResultsPanel";
5
4
 
5
+ import { ScoreSummary } from "../../../scoring/types";
6
6
  import styles from "./ScoreGrid.module.css";
7
7
 
8
8
  interface ScoreGridProps {
9
- scoreGroups: ResultsScorer[][];
9
+ scoreGroups: ScoreSummary[][];
10
10
  showReducer?: boolean;
11
11
  className?: string | string[];
12
12
  striped?: boolean;
@@ -215,7 +215,13 @@ const ParamSummary: FC<ParamSummaryProps> = ({ params }) => {
215
215
  });
216
216
  if (paraValues.length > 0) {
217
217
  return (
218
- <code style={{ padding: 0, color: "var(--bs-body-color)" }}>
218
+ <code
219
+ style={{
220
+ padding: 0,
221
+ color: "var(--bs-body-color)",
222
+ overflowWrap: "anywhere",
223
+ }}
224
+ >
219
225
  {paraValues.join(", ")}
220
226
  </code>
221
227
  );
@@ -9,29 +9,27 @@ import {
9
9
  } from "../../../@types/log";
10
10
  import { RunningMetric } from "../../../client/api/types";
11
11
  import { useTotalSampleCount } from "../../../state/hooks";
12
- import styles from "./Navbar.module.css";
13
12
  import { PrimaryBar } from "./PrimaryBar";
14
13
  import { SecondaryBar } from "./SecondaryBar";
14
+ import styles from "./TitleView.module.css";
15
15
 
16
- interface NavBarProps {
16
+ interface TitleViewProps {
17
17
  evalSpec?: EvalSpec;
18
18
  evalResults?: EvalResults | null;
19
19
  runningMetrics?: RunningMetric[];
20
20
  evalPlan?: EvalPlan;
21
21
  evalStats?: EvalStats;
22
22
  status?: Status;
23
- showToggle: boolean;
24
23
  }
25
24
 
26
25
  /**
27
26
  * Renders the Navbar
28
27
  */
29
- export const Navbar: FC<NavBarProps> = ({
28
+ export const TitleView: FC<TitleViewProps> = ({
30
29
  evalSpec,
31
30
  evalPlan,
32
31
  evalResults,
33
32
  evalStats,
34
- showToggle,
35
33
  status,
36
34
  runningMetrics,
37
35
  }) => {
@@ -41,7 +39,6 @@ export const Navbar: FC<NavBarProps> = ({
41
39
  <PrimaryBar
42
40
  evalSpec={evalSpec}
43
41
  evalResults={evalResults}
44
- showToggle={showToggle}
45
42
  status={status}
46
43
  runningMetrics={runningMetrics}
47
44
  sampleCount={totalSampleCount}
@@ -0,0 +1,57 @@
1
+ .header {
2
+ border-bottom: solid var(--bs-border-color) 1px;
3
+ background: var(--bs-light);
4
+ display: grid;
5
+ grid-template-columns: 1fr max-content;
6
+ justify-content: space-between;
7
+ padding: 0.2em 0.5em;
8
+ --bs-breadcrumb-divider: ">";
9
+ overflow: hidden;
10
+ }
11
+
12
+ .breadcrumbs {
13
+ border-left: solid var(--bs-border-color) 1px;
14
+ padding-left: 0.5em;
15
+ margin-bottom: 0;
16
+ min-width: 0;
17
+ overflow: hidden;
18
+ width: max-content;
19
+ display: flex;
20
+ flex-wrap: nowrap;
21
+ }
22
+
23
+ .ellipsis {
24
+ color: var(--bs-secondary);
25
+ font-weight: normal;
26
+ }
27
+
28
+ .left {
29
+ display: grid;
30
+ grid-template-columns: max-content max-content 1fr;
31
+ align-items: center;
32
+ column-gap: 0.75em;
33
+ overflow: hidden;
34
+ min-width: 0;
35
+ max-width: 100%;
36
+ }
37
+
38
+ .right {
39
+ display: grid;
40
+ grid-auto-columns: max-content;
41
+ grid-auto-flow: column;
42
+ justify-content: end;
43
+ align-items: center;
44
+ column-gap: 0.5em;
45
+ }
46
+
47
+ .toolbarButton {
48
+ color: var(--bs-body);
49
+ }
50
+
51
+ .toolbarButton:hover {
52
+ color: var(--bs-link-hover-color);
53
+ }
54
+
55
+ .pathContainer {
56
+ overflow-x: hidden;
57
+ }
@@ -0,0 +1,117 @@
1
+ import clsx from "clsx";
2
+ import { FC, Fragment, ReactNode, useMemo, useRef } from "react";
3
+ import { Link } from "react-router-dom";
4
+ import { useStore } from "../../state/store";
5
+ import { basename, dirname, ensureTrailingSlash } from "../../utils/path";
6
+ import { ApplicationIcons } from "../appearance/icons";
7
+ import { logUrl, useLogRouteParams } from "../routing/url";
8
+ import styles from "./Navbar.module.css";
9
+ import { useBreadcrumbTruncation } from "./useBreadcrumbTruncation";
10
+
11
+ interface NavbarProps {
12
+ children?: ReactNode;
13
+ }
14
+
15
+ export const Navbar: FC<NavbarProps> = ({ children }) => {
16
+ const { logPath } = useLogRouteParams();
17
+ const logs = useStore((state) => state.logs.logs);
18
+ const baseLogDir = dirname(logs.log_dir || "");
19
+ const baseLogName = basename(logs.log_dir || "");
20
+ const pathContainerRef = useRef<HTMLDivElement>(null);
21
+
22
+ const backUrl = logUrl(
23
+ ensureTrailingSlash(dirname(logPath || "")),
24
+ logs.log_dir,
25
+ );
26
+
27
+ const segments = useMemo(() => {
28
+ const pathSegments = logPath ? logPath.split("/") : [];
29
+ const dirSegments: Array<{ text: string; url: string }> = [];
30
+ const currentSegment = [];
31
+ for (const pathSegment of pathSegments) {
32
+ currentSegment.push(pathSegment);
33
+ const segmentUrl = logUrl(currentSegment.join("/"), logs.log_dir);
34
+ dirSegments.push({
35
+ text: pathSegment,
36
+ url: segmentUrl,
37
+ });
38
+ }
39
+
40
+ return [
41
+ { text: prettyDirUri(baseLogDir) },
42
+ { text: baseLogName, url: logUrl("", logs.log_dir) },
43
+ ...dirSegments,
44
+ ];
45
+ }, [baseLogDir, baseLogName, logPath, logs.log_dir]);
46
+
47
+ const { visibleSegments, showEllipsis } = useBreadcrumbTruncation(
48
+ segments,
49
+ pathContainerRef,
50
+ );
51
+
52
+ return (
53
+ <nav
54
+ className={clsx("text-size-smaller", styles.header)}
55
+ aria-label="breadcrumb"
56
+ >
57
+ <div className={clsx(styles.left)}>
58
+ <Link to={backUrl} className={clsx(styles.toolbarButton)}>
59
+ <i className={clsx(ApplicationIcons.navbar.back)} />
60
+ </Link>
61
+ <Link
62
+ to={logUrl("", logs.log_dir)}
63
+ className={clsx(styles.toolbarButton)}
64
+ >
65
+ <i className={clsx(ApplicationIcons.navbar.home)} />
66
+ </Link>
67
+ <div className={clsx(styles.pathContainer)} ref={pathContainerRef}>
68
+ {logs.log_dir ? (
69
+ <ol className={clsx("breadcrumb", styles.breadcrumbs)}>
70
+ {visibleSegments?.map((segment, index) => {
71
+ const isLast = index === visibleSegments.length - 1;
72
+ const shouldShowEllipsis =
73
+ showEllipsis && index === 1 && visibleSegments.length >= 2;
74
+
75
+ return (
76
+ <Fragment key={index}>
77
+ {shouldShowEllipsis && (
78
+ <li className={clsx("breadcrumb-item", styles.ellipsis)}>
79
+ <span>...</span>
80
+ </li>
81
+ )}
82
+ <li
83
+ className={clsx(
84
+ styles.pathLink,
85
+ "breadcrumb-item",
86
+ isLast ? "active" : undefined,
87
+ )}
88
+ >
89
+ {segment.url ? (
90
+ <Link to={segment.url}>{segment.text}</Link>
91
+ ) : (
92
+ <span className={clsx(styles.pathSegment)}>
93
+ {segment.text}
94
+ </span>
95
+ )}
96
+ </li>
97
+ </Fragment>
98
+ );
99
+ })}
100
+ </ol>
101
+ ) : (
102
+ ""
103
+ )}
104
+ </div>
105
+ </div>
106
+ <div className={clsx(styles.right)}>{children}</div>
107
+ </nav>
108
+ );
109
+ };
110
+
111
+ const prettyDirUri = (uri: string) => {
112
+ if (uri.startsWith("file://")) {
113
+ return uri.replace("file://", "");
114
+ } else {
115
+ return uri;
116
+ }
117
+ };
@@ -0,0 +1,128 @@
1
+ import { useEffect, useState, useCallback, RefObject } from "react";
2
+
3
+ interface BreadcrumbSegment {
4
+ text: string;
5
+ url?: string;
6
+ }
7
+
8
+ interface TruncatedBreadcrumbs {
9
+ visibleSegments: BreadcrumbSegment[];
10
+ hiddenCount: number;
11
+ showEllipsis: boolean;
12
+ }
13
+
14
+ export const useBreadcrumbTruncation = (
15
+ segments: BreadcrumbSegment[],
16
+ containerRef: RefObject<HTMLElement | null>,
17
+ ): TruncatedBreadcrumbs => {
18
+ const [truncatedData, setTruncatedData] = useState<TruncatedBreadcrumbs>({
19
+ visibleSegments: segments,
20
+ hiddenCount: 0,
21
+ showEllipsis: false,
22
+ });
23
+
24
+ const measureAndTruncate = useCallback(() => {
25
+ if (!containerRef.current || segments.length <= 3) {
26
+ setTruncatedData({
27
+ visibleSegments: segments,
28
+ hiddenCount: 0,
29
+ showEllipsis: false,
30
+ });
31
+ return;
32
+ }
33
+
34
+ const container = containerRef.current;
35
+ const containerWidth = container.offsetWidth;
36
+
37
+ // Create a test element to measure breadcrumb widths
38
+ const testElement = document.createElement("ol");
39
+ testElement.className = "breadcrumb";
40
+ testElement.style.position = "absolute";
41
+ testElement.style.visibility = "hidden";
42
+ testElement.style.whiteSpace = "nowrap";
43
+ testElement.style.margin = "0";
44
+ testElement.style.padding = "0";
45
+
46
+ container.appendChild(testElement);
47
+
48
+ // Test if all segments fit
49
+ testElement.innerHTML = segments
50
+ .map((segment) => `<li class="breadcrumb-item">${segment.text}</li>`)
51
+ .join("");
52
+
53
+ if (testElement.scrollWidth <= containerWidth) {
54
+ container.removeChild(testElement);
55
+ setTruncatedData({
56
+ visibleSegments: segments,
57
+ hiddenCount: 0,
58
+ showEllipsis: false,
59
+ });
60
+ return;
61
+ }
62
+
63
+ // Find the maximum number of segments we can show
64
+ // Always keep first and last segments
65
+ const firstSegment = segments[0];
66
+ const lastSegment = segments[segments.length - 1];
67
+
68
+ let maxVisible = 2; // Start with just first and last
69
+
70
+ // Try adding segments from the end (most recent path) first
71
+ for (let endCount = 1; endCount < segments.length - 1; endCount++) {
72
+ const candidateSegments = [
73
+ firstSegment,
74
+ ...segments.slice(segments.length - 1 - endCount, -1),
75
+ lastSegment,
76
+ ];
77
+
78
+ // Test with ellipsis
79
+ const testHTML = [
80
+ `<li class="breadcrumb-item">${firstSegment.text}</li>`,
81
+ `<li class="breadcrumb-item">...</li>`,
82
+ ...segments
83
+ .slice(segments.length - 1 - endCount, -1)
84
+ .map((s) => `<li class="breadcrumb-item">${s.text}</li>`),
85
+ `<li class="breadcrumb-item">${lastSegment.text}</li>`,
86
+ ].join("");
87
+
88
+ testElement.innerHTML = testHTML;
89
+
90
+ if (testElement.scrollWidth <= containerWidth) {
91
+ maxVisible = candidateSegments.length;
92
+ setTruncatedData({
93
+ visibleSegments: candidateSegments,
94
+ hiddenCount: segments.length - candidateSegments.length,
95
+ showEllipsis: true,
96
+ });
97
+ } else {
98
+ break;
99
+ }
100
+ }
101
+
102
+ // If we couldn't fit any middle segments, just show first ... last
103
+ if (maxVisible === 2) {
104
+ setTruncatedData({
105
+ visibleSegments: [firstSegment, lastSegment],
106
+ hiddenCount: segments.length - 2,
107
+ showEllipsis: true,
108
+ });
109
+ }
110
+
111
+ container.removeChild(testElement);
112
+ }, [segments, containerRef]);
113
+
114
+ useEffect(() => {
115
+ measureAndTruncate();
116
+
117
+ const resizeObserver = new ResizeObserver(measureAndTruncate);
118
+ if (containerRef.current) {
119
+ resizeObserver.observe(containerRef.current);
120
+ }
121
+
122
+ return () => {
123
+ resizeObserver.disconnect();
124
+ };
125
+ }, [measureAndTruncate]);
126
+
127
+ return truncatedData;
128
+ };
@@ -1,8 +1,8 @@
1
1
  import clsx from "clsx";
2
2
  import { EvalDataset } from "../../@types/log";
3
- import { MetaDataView } from "../content/MetaDataView";
4
3
 
5
4
  import { CSSProperties, FC } from "react";
5
+ import { MetaDataGrid } from "../content/MetaDataGrid";
6
6
  import styles from "./DatasetDetailView.module.css";
7
7
 
8
8
  interface DatasetDetailViewProps {
@@ -28,11 +28,11 @@ export const DatasetDetailView: FC<DatasetDetailViewProps> = ({
28
28
  }
29
29
 
30
30
  return (
31
- <MetaDataView
31
+ <MetaDataGrid
32
32
  className={clsx("text-size-base", styles.item)}
33
33
  entries={filtered}
34
- tableOptions="borderless,sm"
35
34
  style={style}
35
+ plain={true}
36
36
  />
37
37
  );
38
38
  };
@@ -20,16 +20,16 @@ export const DetailStep: FC<DetailStepProps> = ({
20
20
  return (
21
21
  <div className={clsx(className)}>
22
22
  {iconHtml} {name}
23
- <div className={styles.container}>
24
- {params ? (
23
+ {params && Object.keys(params).length > 0 ? (
24
+ <div className={styles.container}>
25
25
  <MetaDataGrid
26
26
  entries={params}
27
27
  className={clsx("text-size-small", styles.metadata)}
28
28
  />
29
- ) : (
30
- ""
31
- )}
32
- </div>
29
+ </div>
30
+ ) : (
31
+ ""
32
+ )}
33
33
  </div>
34
34
  );
35
35
  };
@@ -23,6 +23,7 @@
23
23
 
24
24
  .planCol {
25
25
  margin-top: 0;
26
+ margin-bottom: 0.5em;
26
27
  }
27
28
 
28
29
  .container {
@@ -1,8 +1,8 @@
1
1
  import clsx from "clsx";
2
2
  import { FC } from "react";
3
3
  import { ApplicationIcons } from "../appearance/icons";
4
- import styles from "./DatasetDetailView.module.css";
5
4
  import { DetailStep } from "./DetailStep";
5
+ import styles from "./ScorerDetailView.module.css";
6
6
 
7
7
  interface ScorerDetailViewProps {
8
8
  name: string;
@@ -5,10 +5,16 @@ import {
5
5
  Outlet,
6
6
  useLocation,
7
7
  } from "react-router-dom";
8
- import { storeImplementation } from "../../state/store";
8
+ import { storeImplementation, useStore } from "../../state/store";
9
9
  import { AppErrorBoundary } from "../AppErrorBoundary";
10
+ import { LogsPanel } from "../log-list/LogsPanel";
10
11
  import { LogViewContainer } from "../log-view/LogViewContainer";
11
- import { kLogRouteUrlPattern, kSampleRouteUrlPattern } from "./url";
12
+ import { RouteDispatcher } from "./RouteDispatcher";
13
+ import {
14
+ kLogRouteUrlPattern,
15
+ kLogsRoutUrlPattern as kLogsRouteUrlPattern,
16
+ kSampleRouteUrlPattern,
17
+ } from "./url";
12
18
 
13
19
  // Create a layout component that includes the RouteTracker
14
20
  const AppLayout = () => {
@@ -21,6 +27,20 @@ const AppLayout = () => {
21
27
  }
22
28
  }, [location]);
23
29
 
30
+ // Get log selection state from store
31
+ const singleFileMode = useStore((state) => state.app.singleFileMode);
32
+
33
+ // Single file mode is a legacy mode that is used when an explicit
34
+ // file is passed via URL (task_file or log_file params) or via
35
+ // embedded state (VSCode)
36
+ if (singleFileMode) {
37
+ return (
38
+ <AppErrorBoundary>
39
+ <LogViewContainer />
40
+ </AppErrorBoundary>
41
+ );
42
+ }
43
+
24
44
  return (
25
45
  <AppErrorBoundary>
26
46
  <Outlet />
@@ -37,11 +57,15 @@ export const AppRouter = createHashRouter(
37
57
  children: [
38
58
  {
39
59
  index: true, // This will match exactly the "/" path
40
- element: <LogViewContainer />,
60
+ element: <LogsPanel />,
61
+ },
62
+ {
63
+ path: kLogsRouteUrlPattern,
64
+ element: <LogsPanel />,
41
65
  },
42
66
  {
43
67
  path: kLogRouteUrlPattern,
44
- element: <LogViewContainer />,
68
+ element: <RouteDispatcher />,
45
69
  },
46
70
  {
47
71
  path: kSampleRouteUrlPattern,