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
@@ -0,0 +1,52 @@
1
+ import { Link } from "react-router-dom";
2
+ import { FileLogItem, FolderLogItem } from "../../LogItem";
3
+ import { columnHelper } from "./columns";
4
+
5
+ import { basename } from "../../../../utils/path";
6
+ import { EmptyCell } from "./EmptyCell";
7
+ import styles from "./FileName.module.css";
8
+
9
+ export const fileNameColumn = () => {
10
+ return columnHelper.accessor("name", {
11
+ id: "file_name",
12
+ header: "File Name",
13
+ cell: (info) => {
14
+ const item = info.row.original as FileLogItem | FolderLogItem;
15
+ if (item.type === "folder") {
16
+ return <EmptyCell />;
17
+ }
18
+ let value = basename(item.name);
19
+ return (
20
+ <div className={styles.nameCell}>
21
+ {item.url ? (
22
+ <Link to={item.url} className={styles.fileLink}>
23
+ {value}
24
+ </Link>
25
+ ) : (
26
+ value
27
+ )}
28
+ </div>
29
+ );
30
+ },
31
+ enableSorting: true,
32
+ enableGlobalFilter: true,
33
+ size: 600,
34
+ minSize: 150,
35
+ enableResizing: true,
36
+ sortingFn: (rowA, rowB) => {
37
+ const itemA = rowA.original as FileLogItem | FolderLogItem;
38
+ const itemB = rowB.original as FileLogItem | FolderLogItem;
39
+
40
+ // Sort folders first, then files
41
+ if (itemA.type !== itemB.type) {
42
+ return itemA.type === "folder" ? -1 : 1;
43
+ }
44
+
45
+ // Within same type, sort by basename
46
+ const valueA = basename(itemA.name);
47
+ const valueB = basename(itemB.name);
48
+
49
+ return valueA.localeCompare(valueB);
50
+ },
51
+ });
52
+ };
@@ -0,0 +1,11 @@
1
+ .iconCell {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ font-size: 1rem;
6
+ color: var(--bs-gray-600);
7
+ }
8
+
9
+ :global(.ii:before) {
10
+ background-color: var(--bs-gray-600);
11
+ }
@@ -0,0 +1,35 @@
1
+ import clsx from "clsx";
2
+ import { ApplicationIcons } from "../../../appearance/icons";
3
+ import { columnHelper } from "./columns";
4
+
5
+ import styles from "./Icon.module.css";
6
+
7
+ export const iconColumn = () => {
8
+ return columnHelper.accessor("type", {
9
+ id: "icon",
10
+ header: "",
11
+ cell: (info) => (
12
+ <div className={styles.iconCell}>
13
+ <i
14
+ className={clsx(
15
+ info.getValue() === "file"
16
+ ? ApplicationIcons.inspectFile
17
+ : ApplicationIcons.folder,
18
+ )}
19
+ />
20
+ </div>
21
+ ),
22
+ enableSorting: true,
23
+ enableGlobalFilter: false,
24
+ size: 30,
25
+ minSize: 30,
26
+ maxSize: 60,
27
+ enableResizing: false,
28
+ sortingFn: (rowA, rowB) => {
29
+ const typeA = rowA.original.type;
30
+ const typeB = rowB.original.type;
31
+
32
+ return typeA.localeCompare(typeB);
33
+ },
34
+ });
35
+ };
@@ -0,0 +1,6 @@
1
+ .modelCell {
2
+ display: flex;
3
+ align-items: center;
4
+ min-width: 0;
5
+ flex: 1;
6
+ }
@@ -0,0 +1,34 @@
1
+ import { columnHelper } from "./columns";
2
+ import { EmptyCell } from "./EmptyCell";
3
+
4
+ import styles from "./Model.module.css";
5
+
6
+ export const modelColumn = () => {
7
+ return columnHelper.accessor(
8
+ (row) => {
9
+ if (row.type !== "file") return "";
10
+ return row.header?.eval?.model || "";
11
+ },
12
+ {
13
+ id: "model",
14
+ header: "Model",
15
+ cell: (info) => {
16
+ const item = info.row.original;
17
+ if (item.type !== "file" || item.header?.eval.model === undefined) {
18
+ return <EmptyCell />;
19
+ }
20
+ return (
21
+ <div className={styles.modelCell}>
22
+ {item.header?.eval.model || ""}
23
+ </div>
24
+ );
25
+ },
26
+ enableSorting: true,
27
+ enableGlobalFilter: true,
28
+ size: 300,
29
+ minSize: 100,
30
+ maxSize: 400,
31
+ enableResizing: true,
32
+ },
33
+ );
34
+ };
@@ -0,0 +1,6 @@
1
+ .scoreCell {
2
+ display: flex;
3
+ align-items: center;
4
+ font-size: 0.875rem;
5
+ color: var(--bs-gray-600);
6
+ }
@@ -0,0 +1,61 @@
1
+ import { firstMetric } from "../../../../scoring/metrics";
2
+ import { formatPrettyDecimal } from "../../../../utils/format";
3
+ import { FileLogItem, FolderLogItem } from "../../LogItem";
4
+ import { columnHelper } from "./columns";
5
+ import { EmptyCell } from "./EmptyCell";
6
+
7
+ import styles from "./Score.module.css";
8
+
9
+ export const scoreColumn = () => {
10
+ return columnHelper.accessor(
11
+ (row) => {
12
+ const metric = itemMetric(row);
13
+ return metric?.value !== undefined
14
+ ? formatPrettyDecimal(metric.value)
15
+ : "";
16
+ },
17
+ {
18
+ id: "score",
19
+ header: "Score",
20
+ cell: (info) => {
21
+ const metric = itemMetric(info.row.original);
22
+ if (metric === undefined) {
23
+ return <EmptyCell />;
24
+ }
25
+ return (
26
+ <div className={styles.scoreCell}>
27
+ {formatPrettyDecimal(metric.value)}
28
+ </div>
29
+ );
30
+ },
31
+ sortingFn: (rowA, rowB) => {
32
+ const itemA = rowA.original;
33
+ const itemB = rowB.original;
34
+
35
+ const metricA = itemMetric(itemA);
36
+ const metricB = itemMetric(itemB);
37
+
38
+ if (!metricA && !metricB) return 0;
39
+ if (!metricA) return -1;
40
+ if (!metricB) return 1;
41
+
42
+ return (metricA.value || 0) - (metricB.value || 0);
43
+ },
44
+ enableSorting: true,
45
+ enableGlobalFilter: true,
46
+ size: 80,
47
+ minSize: 60,
48
+ maxSize: 120,
49
+ enableResizing: true,
50
+ },
51
+ );
52
+ };
53
+
54
+ const itemMetric = (item: FileLogItem | FolderLogItem) => {
55
+ if (item.type !== "file") {
56
+ return undefined;
57
+ }
58
+
59
+ const header = item.header;
60
+ return header?.results ? firstMetric(header.results) : undefined;
61
+ };
@@ -0,0 +1,15 @@
1
+ .error {
2
+ color: var(--bs-danger);
3
+ }
4
+
5
+ .started {
6
+ color: var(--bs-secondary);
7
+ }
8
+
9
+ .success {
10
+ color: var(--bs-success);
11
+ }
12
+
13
+ .cancelled {
14
+ color: var(--bs-secondary);
15
+ }
@@ -0,0 +1,95 @@
1
+ import clsx from "clsx";
2
+ import { FileLogItem, FolderLogItem } from "../../LogItem";
3
+ import { columnHelper } from "./columns";
4
+ import { EmptyCell } from "./EmptyCell";
5
+
6
+ import { ApplicationIcons } from "../../../appearance/icons";
7
+ import styles from "./Status.module.css";
8
+
9
+ export const statusColumn = () => {
10
+ return columnHelper.accessor((row) => itemStatusLabel(row), {
11
+ id: "status",
12
+ header: "Status",
13
+ cell: (info) => {
14
+ const item = info.row.original;
15
+ const status = itemStatus(item);
16
+
17
+ if (!status) {
18
+ return <EmptyCell />;
19
+ }
20
+
21
+ const icon =
22
+ status === "error"
23
+ ? ApplicationIcons.error
24
+ : status === "started"
25
+ ? ApplicationIcons.running
26
+ : status === "cancelled"
27
+ ? ApplicationIcons.cancelled
28
+ : ApplicationIcons.success;
29
+
30
+ const clz =
31
+ status === "error"
32
+ ? styles.error
33
+ : status === "started"
34
+ ? styles.started
35
+ : status === "cancelled"
36
+ ? styles.cancelled
37
+ : styles.success;
38
+
39
+ return (
40
+ <div className={styles.statusCell}>
41
+ <i className={clsx(icon, clz)} />
42
+ </div>
43
+ );
44
+ },
45
+ sortingFn: (rowA, rowB) => {
46
+ const itemA = rowA.original as FileLogItem | FolderLogItem;
47
+ const itemB = rowB.original as FileLogItem | FolderLogItem;
48
+
49
+ const statusA = itemStatus(itemA) || "";
50
+ const statusB = itemStatus(itemB) || "";
51
+
52
+ // If A is empty, goes to bottom
53
+ if (!statusA && statusB) {
54
+ return 1;
55
+ }
56
+ // If B is empty, goes to bottom
57
+ if (statusA && !statusB) {
58
+ return -1;
59
+ }
60
+
61
+ return statusA.localeCompare(statusB);
62
+ },
63
+ enableSorting: true,
64
+ enableGlobalFilter: true,
65
+ size: 80,
66
+ minSize: 60,
67
+ maxSize: 120,
68
+ enableResizing: true,
69
+ });
70
+ };
71
+
72
+ const itemStatus = (item: FileLogItem | FolderLogItem) => {
73
+ if (item.type !== "file") {
74
+ return undefined;
75
+ }
76
+ const header = item.header;
77
+ return header?.status;
78
+ };
79
+
80
+ const itemStatusLabel = (item: FileLogItem | FolderLogItem) => {
81
+ const status = itemStatus(item);
82
+ if (!status) return "";
83
+
84
+ // Return multiple searchable terms for filtering
85
+ switch (status) {
86
+ case "error":
87
+ return "error failed failure";
88
+ case "started":
89
+ return "running started in-progress active";
90
+ case "cancelled":
91
+ return "cancelled canceled stopped aborted";
92
+ default:
93
+ return "success done complete finished completed";
94
+ }
95
+ };
@@ -0,0 +1,20 @@
1
+ .nameCell {
2
+ display: flex;
3
+ align-items: center;
4
+ min-width: 0;
5
+ flex: 1;
6
+ }
7
+
8
+ .logLink {
9
+ color: var(--bs-link-color);
10
+ text-decoration: none;
11
+ overflow: hidden;
12
+ text-overflow: ellipsis;
13
+ white-space: nowrap;
14
+ max-width: 100%;
15
+ }
16
+
17
+ .logLink:hover {
18
+ color: var(--bs-link-hover-color);
19
+ text-decoration: underline;
20
+ }
@@ -0,0 +1,50 @@
1
+ import { Link } from "react-router-dom";
2
+ import { FileLogItem, FolderLogItem } from "../../LogItem";
3
+ import { columnHelper } from "./columns";
4
+
5
+ import { parseLogFileName } from "../../../../utils/evallog";
6
+ import styles from "./Task.module.css";
7
+
8
+ export const taskColumn = () => {
9
+ return columnHelper.accessor((row) => itemName(row), {
10
+ id: "task",
11
+ header: "Task",
12
+ cell: (info) => {
13
+ const item = info.row.original as FileLogItem | FolderLogItem;
14
+ let value = itemName(item);
15
+ return (
16
+ <div className={styles.nameCell}>
17
+ {item.url ? (
18
+ <Link to={item.url} className={styles.logLink}>
19
+ {value}
20
+ </Link>
21
+ ) : (
22
+ value
23
+ )}
24
+ </div>
25
+ );
26
+ },
27
+ enableSorting: true,
28
+ enableGlobalFilter: true,
29
+ size: 250,
30
+ minSize: 150,
31
+ enableResizing: true,
32
+ sortingFn: (rowA, rowB) => {
33
+ const itemA = rowA.original as FileLogItem | FolderLogItem;
34
+ const itemB = rowB.original as FileLogItem | FolderLogItem;
35
+
36
+ const valueA = itemName(itemA);
37
+ const valueB = itemName(itemB);
38
+
39
+ return valueA.localeCompare(valueB);
40
+ },
41
+ });
42
+ };
43
+
44
+ const itemName = (item: FileLogItem | FolderLogItem) => {
45
+ let value = item.name;
46
+ if (item.type === "file") {
47
+ return item.header?.eval?.task || parseLogFileName(item.name).name;
48
+ }
49
+ return value;
50
+ };
@@ -0,0 +1,27 @@
1
+ import { createColumnHelper } from "@tanstack/react-table";
2
+ import { FileLogItem, FolderLogItem } from "../../LogItem";
3
+ import { completedDateColumn } from "./CompletedDate";
4
+ import { fileNameColumn } from "./FileName";
5
+ import { iconColumn } from "./Icon";
6
+ import { modelColumn } from "./Model";
7
+ import { scoreColumn } from "./Score";
8
+ import { statusColumn } from "./Status";
9
+ import { taskColumn } from "./Task";
10
+
11
+ export const columnHelper = createColumnHelper<FileLogItem | FolderLogItem>();
12
+
13
+ export const getColumns = (columnIds?: string[]) => {
14
+ const allColumns = [
15
+ iconColumn(),
16
+ taskColumn(),
17
+ fileNameColumn(),
18
+ completedDateColumn(),
19
+ modelColumn(),
20
+ scoreColumn(),
21
+ statusColumn(),
22
+ ];
23
+ if (columnIds) {
24
+ return allColumns.filter((col) => columnIds.includes(col.id || ""));
25
+ }
26
+ return allColumns;
27
+ };
@@ -9,7 +9,6 @@ import {
9
9
  } from "react";
10
10
  import { EmptyPanel } from "../../components/EmptyPanel";
11
11
  import { TabPanel, TabSet } from "../../components/TabSet";
12
- import { Navbar } from "./navbar/Navbar";
13
12
 
14
13
  import { useEvalSpec, useRefreshLog } from "../../state/hooks";
15
14
  import { useStore } from "../../state/store";
@@ -20,6 +19,7 @@ import { useJsonTabConfig } from "./tabs/JsonTab";
20
19
  import { useModelsTab } from "./tabs/ModelsTab";
21
20
  import { useSamplesTabConfig } from "./tabs/SamplesTab";
22
21
  import { useTaskTabConfig } from "./tabs/TaskTab";
22
+ import { TitleView } from "./title-view/TitleView";
23
23
  import { TabDescriptor } from "./types";
24
24
 
25
25
  export const LogView: FC = () => {
@@ -33,8 +33,6 @@ export const LogView: FC = () => {
33
33
  const runningMetrics = useStore(
34
34
  (state) => state.log.pendingSampleSummaries?.metrics,
35
35
  );
36
- const logs = useStore((state) => state.logs.logs);
37
- const showToggle = logs.files.length > 1 || !!logs.log_dir || false;
38
36
 
39
37
  // Use individual tab config hooks
40
38
  const samplesTabConfig = useSamplesTabConfig(
@@ -111,14 +109,13 @@ export const LogView: FC = () => {
111
109
 
112
110
  return (
113
111
  <Fragment>
114
- <Navbar
112
+ <TitleView
115
113
  evalSpec={evalSpec}
116
114
  evalPlan={selectedLogSummary?.plan}
117
115
  evalResults={selectedLogSummary?.results}
118
116
  runningMetrics={runningMetrics}
119
117
  evalStats={selectedLogSummary?.stats}
120
118
  status={selectedLogSummary?.status}
121
- showToggle={showToggle}
122
119
  />
123
120
  <div ref={divRef} className={clsx("workspace", styles.workspace)}>
124
121
  <div className={clsx("log-detail", styles.tabContainer)}>
@@ -1,5 +1,5 @@
1
1
  import { FC, useEffect } from "react";
2
- import { useNavigate, useParams } from "react-router-dom";
2
+ import { useNavigate } from "react-router-dom";
3
3
  import { kLogViewSamplesTabId } from "../../constants";
4
4
  import {
5
5
  useFilteredSamples,
@@ -7,20 +7,14 @@ import {
7
7
  useTotalSampleCount,
8
8
  } from "../../state/hooks";
9
9
  import { useStore } from "../../state/store";
10
- import { baseUrl } from "../routing/url";
10
+ import { baseUrl, useLogRouteParams } from "../routing/url";
11
11
  import { LogViewLayout } from "./LogViewLayout";
12
12
 
13
13
  /**
14
14
  * LogContainer component that handles routing to specific logs, tabs, and samples
15
15
  */
16
16
  export const LogViewContainer: FC = () => {
17
- const { logPath, tabId, sampleId, epoch, sampleTabId } = useParams<{
18
- logPath?: string;
19
- tabId?: string;
20
- sampleId?: string;
21
- epoch?: string;
22
- sampleTabId?: string;
23
- }>();
17
+ const { logPath, tabId, sampleId, epoch, sampleTabId } = useLogRouteParams();
24
18
 
25
19
  const initialState = useStore((state) => state.app.initialState);
26
20
  const clearInitialState = useStore(
@@ -69,7 +63,7 @@ export const LogViewContainer: FC = () => {
69
63
  useEffect(() => {
70
64
  const loadLogFromPath = async () => {
71
65
  if (logPath) {
72
- await selectLogFile(decodeURIComponent(logPath));
66
+ await selectLogFile(logPath);
73
67
 
74
68
  // Set the tab if specified in the URL
75
69
  if (tabId) {
@@ -86,26 +80,6 @@ export const LogViewContainer: FC = () => {
86
80
 
87
81
  clearSelectedLogSummary();
88
82
  }
89
- } else {
90
- setStatus({
91
- loading: true,
92
- error: undefined,
93
- });
94
-
95
- // Reset the log/task tab
96
- setSelectedLogIndex(-1);
97
- setWorkspaceTab(kLogViewSamplesTabId);
98
-
99
- // Refresh the list of logs
100
- await refreshLogs();
101
-
102
- // Select the first log in the list
103
- setSelectedLogIndex(0);
104
-
105
- setStatus({
106
- loading: false,
107
- error: undefined,
108
- });
109
83
  }
110
84
  };
111
85
 
@@ -4,7 +4,7 @@ import { ErrorPanel } from "../../components/ErrorPanel";
4
4
  import { FindBand } from "../../components/FindBand";
5
5
  import { ProgressBar } from "../../components/ProgressBar";
6
6
  import { useStore } from "../../state/store";
7
- import { Sidebar } from "../sidebar/Sidebar";
7
+ import { Navbar } from "../navbar/Navbar";
8
8
  import { LogView } from "./LogView";
9
9
 
10
10
  /**
@@ -13,28 +13,16 @@ import { LogView } from "./LogView";
13
13
  export const LogViewLayout: FC = () => {
14
14
  // App layout and state
15
15
  const appStatus = useStore((state) => state.app.status);
16
- const offCanvas = useStore((state) => state.app.offcanvas);
17
- const setOffCanvas = useStore((state) => state.appActions.setOffcanvas);
18
- const clearWorkspaceTab = useStore(
19
- (state) => state.appActions.clearWorkspaceTab,
20
- );
21
- const clearSampleTab = useStore((state) => state.appActions.clearSampleTab);
22
16
 
23
17
  // Find
24
18
  const nativeFind = useStore((state) => state.capabilities.nativeFind);
25
19
  const showFind = useStore((state) => state.app.showFind);
26
20
  const setShowFind = useStore((state) => state.appActions.setShowFind);
27
21
  const hideFind = useStore((state) => state.appActions.hideFind);
22
+ const singleFileMode = useStore((state) => state.app.singleFileMode);
28
23
 
29
24
  // Logs Data
30
25
  const logs = useStore((state) => state.logs.logs);
31
- const selectedLogIndex = useStore((state) => state.logs.selectedLogIndex);
32
- const logHeaders = useStore((state) => state.logs.logHeaders);
33
- const headersLoading = useStore((state) => state.logs.headersLoading);
34
-
35
- // Log Data
36
- const selectedLogSummary = useStore((state) => state.log.selectedLogSummary);
37
- const resetFiltering = useStore((state) => state.logActions.resetFiltering);
38
26
 
39
27
  // The main application reference
40
28
  const mainAppRef = useRef<HTMLDivElement>(null);
@@ -43,13 +31,6 @@ export const LogViewLayout: FC = () => {
43
31
  // if there are no log files, then don't show sidebar
44
32
  const fullScreen = logs.files.length === 1 && !logs.log_dir;
45
33
 
46
- const handleSelectedIndexChanged = useCallback(() => {
47
- setOffCanvas(false);
48
- resetFiltering();
49
- clearSampleTab();
50
- clearWorkspaceTab();
51
- }, [setOffCanvas, resetFiltering, clearSampleTab, clearWorkspaceTab]);
52
-
53
34
  const handleKeyboard = useCallback(
54
35
  (e: KeyboardEvent) => {
55
36
  // Add keyboard shortcuts for find, if needed
@@ -68,25 +49,19 @@ export const LogViewLayout: FC = () => {
68
49
 
69
50
  return (
70
51
  <>
71
- {!fullScreen && selectedLogSummary ? (
72
- <Sidebar
73
- logHeaders={logHeaders}
74
- loading={headersLoading}
75
- selectedIndex={selectedLogIndex}
76
- onSelectedIndexChanged={handleSelectedIndexChanged}
77
- />
78
- ) : undefined}
79
52
  <div
80
53
  ref={mainAppRef}
81
54
  className={clsx(
82
55
  "app-main-grid",
83
56
  fullScreen ? "full-screen" : undefined,
84
- offCanvas ? "off-canvas" : undefined,
57
+ singleFileMode ? "single-file-mode" : undefined,
58
+ "log-view",
85
59
  )}
86
60
  tabIndex={0}
87
61
  onKeyDown={handleKeyboard}
88
62
  >
89
63
  {!nativeFind && showFind ? <FindBand /> : ""}
64
+ {!singleFileMode ? <Navbar /> : ""}
90
65
  <ProgressBar animating={appStatus.loading} />
91
66
  {appStatus.error ? (
92
67
  <ErrorPanel
@@ -5,8 +5,8 @@ import { Card, CardBody, CardHeader } from "../../../components/Card";
5
5
  import { kLogViewTaskTabId } from "../../../constants";
6
6
  import { formatDuration, toTitleCase } from "../../../utils/format";
7
7
  import { ghCommitUrl } from "../../../utils/git";
8
- import { MetaDataView } from "../../content/MetaDataView";
9
8
 
9
+ import { MetaDataGrid } from "../../content/MetaDataGrid";
10
10
  import styles from "./TaskTab.module.css";
11
11
 
12
12
  // Individual hook for Info tab
@@ -101,14 +101,13 @@ export const TaskTab: FC<TaskTabProps> = ({ evalSpec, evalStats }) => {
101
101
  <CardHeader label="Task Info" />
102
102
  <CardBody id={"task-card-config"}>
103
103
  <div className={clsx(styles.grid)}>
104
- <MetaDataView
104
+ <MetaDataGrid
105
105
  key={`plan-md-task`}
106
106
  className={"text-size-small"}
107
107
  entries={taskInformation}
108
- tableOptions="sm"
109
108
  />
110
109
 
111
- <MetaDataView
110
+ <MetaDataGrid
112
111
  entries={{
113
112
  ["Start"]: new Date(
114
113
  evalStats?.started_at || 0,
@@ -118,7 +117,6 @@ export const TaskTab: FC<TaskTabProps> = ({ evalSpec, evalStats }) => {
118
117
  ).toLocaleString(),
119
118
  ["Duration"]: totalDuration,
120
119
  }}
121
- tableOptions="sm"
122
120
  />
123
121
  </div>
124
122
  </CardBody>
@@ -128,11 +126,10 @@ export const TaskTab: FC<TaskTabProps> = ({ evalSpec, evalStats }) => {
128
126
  <Card>
129
127
  <CardHeader label="Task Args" />
130
128
  <CardBody id={"task-card-config"}>
131
- <MetaDataView
129
+ <MetaDataGrid
132
130
  key={`plan-md-task-args`}
133
131
  className={"text-size-small"}
134
132
  entries={task_args as Record<string, unknown>}
135
- tableOptions="sm"
136
133
  />
137
134
  </CardBody>
138
135
  </Card>
@@ -12,6 +12,8 @@
12
12
  }
13
13
 
14
14
  .toggle {
15
+ margin-top: 0.3em;
16
+ font-size: 1em;
15
17
  padding: 0rem 0.1rem 0.1rem 0rem;
16
18
  display: flex;
17
19
  }