inspect-ai 0.3.95__py3-none-any.whl → 0.3.97__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 (142) hide show
  1. inspect_ai/_eval/eval.py +10 -2
  2. inspect_ai/_eval/task/util.py +32 -3
  3. inspect_ai/_util/local_server.py +16 -0
  4. inspect_ai/_util/registry.py +7 -0
  5. inspect_ai/_util/timer.py +13 -0
  6. inspect_ai/_view/www/dist/assets/index.css +275 -195
  7. inspect_ai/_view/www/dist/assets/index.js +8568 -7376
  8. inspect_ai/_view/www/src/app/App.css +1 -0
  9. inspect_ai/_view/www/src/app/App.tsx +27 -10
  10. inspect_ai/_view/www/src/app/appearance/icons.ts +5 -0
  11. inspect_ai/_view/www/src/app/content/RecordTree.module.css +22 -0
  12. inspect_ai/_view/www/src/app/content/RecordTree.tsx +370 -0
  13. inspect_ai/_view/www/src/app/content/RenderedContent.module.css +5 -0
  14. inspect_ai/_view/www/src/app/content/RenderedContent.tsx +32 -19
  15. inspect_ai/_view/www/src/app/content/record_processors/store.ts +101 -0
  16. inspect_ai/_view/www/src/app/content/record_processors/types.ts +3 -0
  17. inspect_ai/_view/www/src/app/content/types.ts +5 -0
  18. inspect_ai/_view/www/src/app/log-view/LogView.tsx +1 -0
  19. inspect_ai/_view/www/src/app/log-view/LogViewContainer.tsx +35 -28
  20. inspect_ai/_view/www/src/app/log-view/LogViewLayout.tsx +1 -8
  21. inspect_ai/_view/www/src/app/log-view/navbar/PrimaryBar.tsx +2 -4
  22. inspect_ai/_view/www/src/app/log-view/navbar/ResultsPanel.tsx +13 -3
  23. inspect_ai/_view/www/src/app/log-view/navbar/ScoreGrid.module.css +15 -0
  24. inspect_ai/_view/www/src/app/log-view/navbar/ScoreGrid.tsx +14 -10
  25. inspect_ai/_view/www/src/app/log-view/tabs/InfoTab.tsx +9 -3
  26. inspect_ai/_view/www/src/app/log-view/tabs/JsonTab.tsx +1 -3
  27. inspect_ai/_view/www/src/app/log-view/tabs/SamplesTab.tsx +8 -2
  28. inspect_ai/_view/www/src/app/log-view/types.ts +1 -0
  29. inspect_ai/_view/www/src/app/plan/ModelCard.module.css +7 -0
  30. inspect_ai/_view/www/src/app/plan/ModelCard.tsx +5 -2
  31. inspect_ai/_view/www/src/app/plan/PlanCard.tsx +13 -8
  32. inspect_ai/_view/www/src/app/routing/navigationHooks.ts +63 -8
  33. inspect_ai/_view/www/src/app/routing/url.ts +45 -0
  34. inspect_ai/_view/www/src/app/samples/InlineSampleDisplay.module.css +2 -1
  35. inspect_ai/_view/www/src/app/samples/InlineSampleDisplay.tsx +15 -8
  36. inspect_ai/_view/www/src/app/samples/SampleDialog.module.css +3 -0
  37. inspect_ai/_view/www/src/app/samples/SampleDialog.tsx +16 -5
  38. inspect_ai/_view/www/src/app/samples/SampleDisplay.module.css +9 -1
  39. inspect_ai/_view/www/src/app/samples/SampleDisplay.tsx +68 -31
  40. inspect_ai/_view/www/src/app/samples/chat/ChatMessage.module.css +12 -7
  41. inspect_ai/_view/www/src/app/samples/chat/ChatMessage.tsx +17 -5
  42. inspect_ai/_view/www/src/app/samples/chat/ChatMessageRow.module.css +9 -0
  43. inspect_ai/_view/www/src/app/samples/chat/ChatMessageRow.tsx +48 -18
  44. inspect_ai/_view/www/src/app/samples/chat/ChatView.tsx +0 -1
  45. inspect_ai/_view/www/src/app/samples/chat/ChatViewVirtualList.module.css +4 -0
  46. inspect_ai/_view/www/src/app/samples/chat/ChatViewVirtualList.tsx +41 -1
  47. inspect_ai/_view/www/src/app/samples/chat/messages.ts +7 -0
  48. inspect_ai/_view/www/src/app/samples/chat/tools/ToolCallView.module.css +0 -3
  49. inspect_ai/_view/www/src/app/samples/chat/tools/ToolCallView.tsx +1 -1
  50. inspect_ai/_view/www/src/app/samples/chat/tools/ToolInput.module.css +1 -1
  51. inspect_ai/_view/www/src/app/samples/chat/tools/ToolOutput.module.css +1 -1
  52. inspect_ai/_view/www/src/app/samples/descriptor/score/NumericScoreDescriptor.tsx +5 -1
  53. inspect_ai/_view/www/src/app/samples/descriptor/score/PassFailScoreDescriptor.tsx +11 -6
  54. inspect_ai/_view/www/src/app/samples/list/SampleList.tsx +7 -0
  55. inspect_ai/_view/www/src/app/samples/list/SampleRow.tsx +5 -18
  56. inspect_ai/_view/www/src/app/samples/sample-tools/SortFilter.tsx +1 -1
  57. inspect_ai/_view/www/src/app/samples/scores/SampleScoresGrid.tsx +18 -5
  58. inspect_ai/_view/www/src/app/samples/scores/SampleScoresView.module.css +0 -6
  59. inspect_ai/_view/www/src/app/samples/scores/SampleScoresView.tsx +4 -1
  60. inspect_ai/_view/www/src/app/samples/transcript/ApprovalEventView.tsx +4 -2
  61. inspect_ai/_view/www/src/app/samples/transcript/ErrorEventView.tsx +6 -4
  62. inspect_ai/_view/www/src/app/samples/transcript/InfoEventView.module.css +1 -1
  63. inspect_ai/_view/www/src/app/samples/transcript/InfoEventView.tsx +13 -6
  64. inspect_ai/_view/www/src/app/samples/transcript/InputEventView.tsx +6 -4
  65. inspect_ai/_view/www/src/app/samples/transcript/LoggerEventView.tsx +4 -2
  66. inspect_ai/_view/www/src/app/samples/transcript/ModelEventView.tsx +11 -8
  67. inspect_ai/_view/www/src/app/samples/transcript/SampleInitEventView.tsx +14 -8
  68. inspect_ai/_view/www/src/app/samples/transcript/SampleLimitEventView.tsx +13 -8
  69. inspect_ai/_view/www/src/app/samples/transcript/SandboxEventView.tsx +25 -16
  70. inspect_ai/_view/www/src/app/samples/transcript/ScoreEventView.tsx +7 -5
  71. inspect_ai/_view/www/src/app/samples/transcript/SpanEventView.tsx +11 -28
  72. inspect_ai/_view/www/src/app/samples/transcript/StepEventView.tsx +12 -20
  73. inspect_ai/_view/www/src/app/samples/transcript/SubtaskEventView.tsx +12 -31
  74. inspect_ai/_view/www/src/app/samples/transcript/ToolEventView.tsx +25 -29
  75. inspect_ai/_view/www/src/app/samples/transcript/TranscriptVirtualList.tsx +297 -0
  76. inspect_ai/_view/www/src/app/samples/transcript/TranscriptVirtualListComponent.module.css +0 -8
  77. inspect_ai/_view/www/src/app/samples/transcript/TranscriptVirtualListComponent.tsx +43 -25
  78. inspect_ai/_view/www/src/app/samples/transcript/event/EventPanel.module.css +43 -0
  79. inspect_ai/_view/www/src/app/samples/transcript/event/EventPanel.tsx +109 -43
  80. inspect_ai/_view/www/src/app/samples/transcript/state/StateEventView.tsx +19 -8
  81. inspect_ai/_view/www/src/app/samples/transcript/transform/treeify.ts +128 -60
  82. inspect_ai/_view/www/src/app/samples/transcript/transform/utils.ts +14 -4
  83. inspect_ai/_view/www/src/app/samples/transcript/types.ts +6 -4
  84. inspect_ai/_view/www/src/app/types.ts +12 -1
  85. inspect_ai/_view/www/src/components/Card.css +6 -3
  86. inspect_ai/_view/www/src/components/Card.tsx +15 -2
  87. inspect_ai/_view/www/src/components/CopyButton.tsx +4 -6
  88. inspect_ai/_view/www/src/components/ExpandablePanel.module.css +20 -14
  89. inspect_ai/_view/www/src/components/ExpandablePanel.tsx +17 -22
  90. inspect_ai/_view/www/src/components/LargeModal.tsx +5 -1
  91. inspect_ai/_view/www/src/components/LiveVirtualList.tsx +25 -1
  92. inspect_ai/_view/www/src/components/MarkdownDiv.css +4 -0
  93. inspect_ai/_view/www/src/components/MarkdownDiv.tsx +2 -2
  94. inspect_ai/_view/www/src/components/TabSet.module.css +6 -1
  95. inspect_ai/_view/www/src/components/TabSet.tsx +8 -2
  96. inspect_ai/_view/www/src/state/hooks.ts +83 -13
  97. inspect_ai/_view/www/src/state/logPolling.ts +2 -2
  98. inspect_ai/_view/www/src/state/logSlice.ts +1 -2
  99. inspect_ai/_view/www/src/state/logsSlice.ts +9 -9
  100. inspect_ai/_view/www/src/state/samplePolling.ts +1 -1
  101. inspect_ai/_view/www/src/state/sampleSlice.ts +134 -7
  102. inspect_ai/_view/www/src/state/scoring.ts +1 -1
  103. inspect_ai/_view/www/src/state/scrolling.ts +39 -6
  104. inspect_ai/_view/www/src/state/store.ts +5 -0
  105. inspect_ai/_view/www/src/state/store_filter.ts +47 -44
  106. inspect_ai/_view/www/src/utils/debugging.ts +95 -0
  107. inspect_ai/_view/www/src/utils/format.ts +2 -2
  108. inspect_ai/_view/www/src/utils/json.ts +29 -0
  109. inspect_ai/agent/__init__.py +2 -1
  110. inspect_ai/agent/_agent.py +12 -0
  111. inspect_ai/agent/_react.py +184 -48
  112. inspect_ai/agent/_types.py +15 -2
  113. inspect_ai/analysis/beta/__init__.py +11 -3
  114. inspect_ai/analysis/beta/_dataframe/columns.py +11 -16
  115. inspect_ai/analysis/beta/_dataframe/evals/table.py +101 -39
  116. inspect_ai/analysis/beta/_dataframe/events/columns.py +50 -0
  117. inspect_ai/analysis/beta/_dataframe/events/extract.py +26 -0
  118. inspect_ai/analysis/beta/_dataframe/events/table.py +77 -3
  119. inspect_ai/analysis/beta/_dataframe/extract.py +44 -25
  120. inspect_ai/analysis/beta/_dataframe/messages/columns.py +1 -1
  121. inspect_ai/analysis/beta/_dataframe/messages/table.py +30 -29
  122. inspect_ai/analysis/beta/_dataframe/progress.py +56 -0
  123. inspect_ai/analysis/beta/_dataframe/record.py +13 -9
  124. inspect_ai/analysis/beta/_dataframe/samples/columns.py +8 -4
  125. inspect_ai/analysis/beta/_dataframe/samples/extract.py +5 -33
  126. inspect_ai/analysis/beta/_dataframe/samples/table.py +211 -60
  127. inspect_ai/analysis/beta/_dataframe/util.py +33 -28
  128. inspect_ai/log/_file.py +9 -2
  129. inspect_ai/model/_call_tools.py +1 -1
  130. inspect_ai/model/_providers/anthropic.py +18 -5
  131. inspect_ai/model/_providers/azureai.py +7 -2
  132. inspect_ai/model/_providers/util/llama31.py +3 -3
  133. inspect_ai/solver/_task_state.py +1 -1
  134. inspect_ai/tool/_mcp/_sandbox.py +17 -14
  135. {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/METADATA +2 -2
  136. {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/RECORD +140 -133
  137. {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/WHEEL +1 -1
  138. inspect_ai/_view/www/src/app/samples/transcript/TranscriptView.module.css +0 -48
  139. inspect_ai/_view/www/src/app/samples/transcript/TranscriptView.tsx +0 -276
  140. {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/entry_points.txt +0 -0
  141. {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/licenses/LICENSE +0 -0
  142. {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,101 @@
1
+ import { RecordProcessor } from "./types";
2
+
3
+ const kStoreInstanceKey = /^(.+)?:([a-zA-Z0-9]{22}):instance$/;
4
+ const kStoreKey = /^(.+)?:([a-zA-Z0-9]{22}):(.+)$/;
5
+
6
+ // Expands store keys in the record. When an instance key is found, this will create a new node, and subsequent store
7
+ // keys will be added as children to that node. Since instance keys always appear first, we can do this in a single pass.
8
+ export const resolveStoreKeys: RecordProcessor = (
9
+ record: Record<string, unknown>,
10
+ ): Record<string, unknown> => {
11
+ const result: Record<string, unknown> = {};
12
+ const storeInstances: Record<string, Record<string, unknown>> = {};
13
+ const instanceKeys: Set<string> = new Set();
14
+
15
+ const entries = Object.entries(record);
16
+ for (let i = 0; i < entries.length; i++) {
17
+ const [key, value] = entries[i];
18
+
19
+ // First check if it's an instance key
20
+ const instanceInfo = parseStoreInstanceKey(key, value);
21
+ if (instanceInfo) {
22
+ const { storeName, instanceId } = instanceInfo;
23
+ const instanceKey = storeKey(storeName, instanceId);
24
+
25
+ // Create a container for this instance if it doesn't exist
26
+ if (!storeInstances[instanceKey]) {
27
+ storeInstances[instanceKey] = {};
28
+ }
29
+
30
+ instanceKeys.add(key);
31
+ continue;
32
+ } else {
33
+ // Then check if it's a store key that belongs to an instance
34
+ const storeKeyInfo = parseStoreKey(key);
35
+ if (storeKeyInfo) {
36
+ const { storeName, instanceId, keyName } = storeKeyInfo;
37
+ const instanceKey = storeKey(storeName, instanceId);
38
+
39
+ // If we have a container for this instance, add this key as a child
40
+ if (storeInstances[instanceKey]) {
41
+ storeInstances[instanceKey][keyName] = value;
42
+ continue;
43
+ }
44
+ } else {
45
+ // If it's not a store key or instance key, add it directly to the result
46
+ result[key] = value;
47
+ }
48
+ }
49
+ }
50
+
51
+ // Add all store instances to the result
52
+ for (const [instanceKey, children] of Object.entries(storeInstances)) {
53
+ // Recursively process the children to handle nested store keys
54
+ result[instanceKey] = resolveStoreKeys(children);
55
+ }
56
+
57
+ // Process any nested objects recursively
58
+ for (const [key, value] of Object.entries(result)) {
59
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
60
+ result[key] = resolveStoreKeys(value as Record<string, unknown>);
61
+ }
62
+ }
63
+
64
+ return result;
65
+ };
66
+
67
+ // Parses the store instance key
68
+ const parseStoreInstanceKey = (key: string, value: unknown) => {
69
+ const match = key.match(kStoreInstanceKey);
70
+ if (match) {
71
+ const [, storeName, instanceId] = match;
72
+ if (typeof value === "string" && instanceId === value) {
73
+ return {
74
+ storeName,
75
+ instanceId,
76
+ };
77
+ }
78
+ }
79
+ return null;
80
+ };
81
+
82
+ // Parses a store key
83
+ const parseStoreKey = (key: string) => {
84
+ const match = key.match(kStoreKey);
85
+ if (match) {
86
+ const [, storeName, instanceId, keyName] = match;
87
+ if (keyName !== "instance") {
88
+ return {
89
+ storeName,
90
+ instanceId,
91
+ keyName,
92
+ };
93
+ }
94
+ }
95
+ return null;
96
+ };
97
+
98
+ // Create a unique key for the store instance
99
+ const storeKey = (storeName: string, instanceId: string) => {
100
+ return `${storeName || ""} (${instanceId})`;
101
+ };
@@ -0,0 +1,3 @@
1
+ export type RecordProcessor = (
2
+ record: Record<string, unknown>,
3
+ ) => Record<string, unknown>;
@@ -6,12 +6,17 @@ export const Buckets = {
6
6
  final: 1000,
7
7
  };
8
8
 
9
+ export interface RenderOptions {
10
+ renderString: "pre" | "markdown";
11
+ }
12
+
9
13
  export interface ContentRenderer {
10
14
  bucket: number;
11
15
  canRender: (content: any) => boolean;
12
16
  render: (
13
17
  id: string,
14
18
  content: any,
19
+ options: RenderOptions,
15
20
  ) => {
16
21
  rendered: string | number | bigint | boolean | object | ReactNode | null;
17
22
  };
@@ -141,6 +141,7 @@ export const LogView: FC = () => {
141
141
  selected={selectedTab === tab.id}
142
142
  scrollable={!!tab.scrollable}
143
143
  scrollRef={tab.scrollRef}
144
+ className={clsx(tab.className)}
144
145
  style={{ height: tab.scrollable ? "100%" : undefined }}
145
146
  >
146
147
  {createElement(tab.component, tab.componentProps)}
@@ -1,7 +1,11 @@
1
1
  import { FC, useEffect } from "react";
2
2
  import { useNavigate, useParams } from "react-router-dom";
3
3
  import { kLogViewSamplesTabId } from "../../constants";
4
- import { useFilteredSamples, useTotalSampleCount } from "../../state/hooks";
4
+ import {
5
+ useFilteredSamples,
6
+ usePrevious,
7
+ useTotalSampleCount,
8
+ } from "../../state/hooks";
5
9
  import { useStore } from "../../state/store";
6
10
  import { baseUrl } from "../routing/url";
7
11
  import { LogViewLayout } from "./LogViewLayout";
@@ -17,26 +21,34 @@ export const LogViewContainer: FC = () => {
17
21
  epoch?: string;
18
22
  sampleTabId?: string;
19
23
  }>();
20
- const selectLogFile = useStore((state) => state.logsActions.selectLogFile);
21
- const refreshLogs = useStore((state) => state.logsActions.refreshLogs);
22
- const setWorkspaceTab = useStore((state) => state.appActions.setWorkspaceTab);
24
+ const initialState = useStore((state) => state.app.initialState);
25
+ const clearInitialState = useStore(
26
+ (state) => state.appActions.clearInitialState,
27
+ );
28
+ const setSampleTab = useStore((state) => state.appActions.setSampleTab);
23
29
  const setShowingSampleDialog = useStore(
24
30
  (state) => state.appActions.setShowingSampleDialog,
25
31
  );
26
- const selectSample = useStore((state) => state.logActions.selectSample);
27
- const setSampleTab = useStore((state) => state.appActions.setSampleTab);
28
- const filteredSamples = useFilteredSamples();
29
- const totalSampleCount = useTotalSampleCount();
30
32
  const setStatus = useStore((state) => state.appActions.setStatus);
33
+ const setWorkspaceTab = useStore((state) => state.appActions.setWorkspaceTab);
34
+
35
+ const refreshLogs = useStore((state) => state.logsActions.refreshLogs);
36
+ const selectLogFile = useStore((state) => state.logsActions.selectLogFile);
37
+ const selectSample = useStore((state) => state.logActions.selectSample);
31
38
  const setSelectedLogIndex = useStore(
32
39
  (state) => state.logsActions.setSelectedLogIndex,
33
40
  );
34
41
 
35
- const selectedLogIndex = useStore((state) => state.logs.selectedLogIndex);
36
- const initialState = useStore((state) => state.app.initialState);
37
- const clearInitialState = useStore(
38
- (state) => state.appActions.clearInitialState,
42
+ const clearSelectedLogSummary = useStore(
43
+ (state) => state.logActions.clearSelectedLogSummary,
39
44
  );
45
+
46
+ const clearSelectedSample = useStore(
47
+ (state) => state.sampleActions.clearSelectedSample,
48
+ );
49
+
50
+ const filteredSamples = useFilteredSamples();
51
+ const totalSampleCount = useTotalSampleCount();
40
52
  const navigate = useNavigate();
41
53
 
42
54
  useEffect(() => {
@@ -51,6 +63,8 @@ export const LogViewContainer: FC = () => {
51
63
  }
52
64
  }, [initialState]);
53
65
 
66
+ const prevLogPath = usePrevious<string | undefined>(logPath);
67
+
54
68
  useEffect(() => {
55
69
  const loadLogFromPath = async () => {
56
70
  if (logPath) {
@@ -64,6 +78,13 @@ export const LogViewContainer: FC = () => {
64
78
  } else {
65
79
  setWorkspaceTab(kLogViewSamplesTabId);
66
80
  }
81
+
82
+ // Reset the sample
83
+ if (logPath !== prevLogPath) {
84
+ clearSelectedSample();
85
+
86
+ clearSelectedLogSummary();
87
+ }
67
88
  } else {
68
89
  setStatus({
69
90
  loading: true,
@@ -80,10 +101,6 @@ export const LogViewContainer: FC = () => {
80
101
  // Select the first log in the list
81
102
  setSelectedLogIndex(0);
82
103
 
83
- if (!sampleId) {
84
- selectSample(0);
85
- }
86
-
87
104
  setStatus({
88
105
  loading: false,
89
106
  error: undefined,
@@ -102,16 +119,6 @@ export const LogViewContainer: FC = () => {
102
119
  setStatus,
103
120
  ]);
104
121
 
105
- const clearSample = useStore(
106
- (state) => state.sampleActions.clearSelectedSample,
107
- );
108
-
109
- useEffect(() => {
110
- if (selectedLogIndex > -1) {
111
- selectSample(0);
112
- }
113
- }, [selectedLogIndex]);
114
-
115
122
  // Handle sample selection from URL params
116
123
  useEffect(() => {
117
124
  if (sampleId && filteredSamples) {
@@ -140,7 +147,7 @@ export const LogViewContainer: FC = () => {
140
147
  // This handles the case when user navigates back from a sample
141
148
  setShowingSampleDialog(false);
142
149
  if (totalSampleCount > 1) {
143
- clearSample();
150
+ clearSelectedSample();
144
151
  }
145
152
  }
146
153
  }, [
@@ -152,7 +159,7 @@ export const LogViewContainer: FC = () => {
152
159
  selectSample,
153
160
  setSampleTab,
154
161
  setShowingSampleDialog,
155
- clearSample,
162
+ clearSelectedSample,
156
163
  ]);
157
164
 
158
165
  return <LogViewLayout />;
@@ -35,7 +35,6 @@ export const LogViewLayout: FC = () => {
35
35
  // Log Data
36
36
  const selectedLogSummary = useStore((state) => state.log.selectedLogSummary);
37
37
  const resetFiltering = useStore((state) => state.logActions.resetFiltering);
38
- const selectSample = useStore((state) => state.logActions.selectSample);
39
38
 
40
39
  // The main application reference
41
40
  const mainAppRef = useRef<HTMLDivElement>(null);
@@ -49,13 +48,7 @@ export const LogViewLayout: FC = () => {
49
48
  resetFiltering();
50
49
  clearSampleTab();
51
50
  clearWorkspaceTab();
52
- }, [
53
- setOffCanvas,
54
- resetFiltering,
55
- clearSampleTab,
56
- clearWorkspaceTab,
57
- selectSample,
58
- ]);
51
+ }, [setOffCanvas, resetFiltering, clearSampleTab, clearWorkspaceTab]);
59
52
 
60
53
  const handleKeyboard = useCallback(
61
54
  (e: KeyboardEvent) => {
@@ -7,6 +7,7 @@ import { kModelNone } from "../../../constants";
7
7
  import { useStore } from "../../../state/store";
8
8
  import { filename } from "../../../utils/path";
9
9
  import { ApplicationIcons } from "../../appearance/icons";
10
+ import { ModelRolesView } from "./ModelRolesView";
10
11
  import styles from "./PrimaryBar.module.css";
11
12
  import {
12
13
  displayScorersFromRunningMetrics,
@@ -15,7 +16,6 @@ import {
15
16
  } from "./ResultsPanel";
16
17
  import { RunningStatusPanel } from "./RunningStatusPanel";
17
18
  import { CancelledPanel, ErroredPanel } from "./StatusPanel";
18
- import { ModelRolesView } from "./ModelRolesView";
19
19
 
20
20
  interface PrimaryBarProps {
21
21
  showToggle: boolean;
@@ -37,9 +37,7 @@ export const PrimaryBar: FC<PrimaryBarProps> = ({
37
37
  const offCanvas = useStore((state) => state.app.offcanvas);
38
38
  const setOffCanvas = useStore((state) => state.appActions.setOffcanvas);
39
39
  const streamSamples = useStore((state) => state.capabilities.streamSamples);
40
- const selectedLogFile = useStore((state) =>
41
- state.logsActions.getSelectedLogFile(),
42
- );
40
+ const selectedLogFile = useStore((state) => state.logs.selectedLogFile);
43
41
 
44
42
  const logFileName = selectedLogFile ? filename(selectedLogFile) : "";
45
43
 
@@ -22,6 +22,8 @@ export interface ResultsScorer {
22
22
  metrics: ResultsMetric[];
23
23
  }
24
24
 
25
+ const kMaxPrimaryScoreRows = 4;
26
+
25
27
  export const displayScorersFromRunningMetrics = (metrics?: RunningMetric[]) => {
26
28
  if (!metrics) {
27
29
  return [];
@@ -123,19 +125,27 @@ export const ResultsPanel: FC<ResultsPanelProps> = ({ scorers }) => {
123
125
 
124
126
  // Try to select metrics with a group size 5 or less, if possible
125
127
  let primaryResults = grouped[0];
126
- if (primaryResults.length > 5) {
128
+ let showMore = grouped.length > 1;
129
+ if (primaryResults.length > kMaxPrimaryScoreRows) {
127
130
  const shorterResults = grouped.find((g) => {
128
- return g.length <= 5;
131
+ return g.length <= kMaxPrimaryScoreRows;
129
132
  });
130
133
  if (shorterResults) {
131
134
  primaryResults = shorterResults;
132
135
  }
136
+
137
+ // If the primary metrics are still too long, truncate them and
138
+ // show the rest in the modal
139
+ if (primaryResults.length > kMaxPrimaryScoreRows) {
140
+ primaryResults = primaryResults.slice(0, kMaxPrimaryScoreRows);
141
+ showMore = true;
142
+ }
133
143
  }
134
144
 
135
145
  return (
136
146
  <div className={clsx(styles.metricsSummary)}>
137
147
  <ScoreGrid scoreGroups={[primaryResults]} showReducer={showReducer} />
138
- {grouped.length > 1 ? (
148
+ {showMore ? (
139
149
  <>
140
150
  <Modal
141
151
  id="results-metrics"
@@ -33,3 +33,18 @@
33
33
  .tableBody {
34
34
  border-top-color: var(--bs-light-border-subtle);
35
35
  }
36
+
37
+ thead,
38
+ thead tr,
39
+ thead td,
40
+ thead th {
41
+ border: none !important;
42
+ padding: 0 !important;
43
+ }
44
+
45
+ .tableSeparator,
46
+ .tableSeparator tr,
47
+ .tableSeparator td,
48
+ .tableSeparator th {
49
+ border: none !important;
50
+ }
@@ -51,10 +51,12 @@ export const ScoreGrid: FC<ScoreGridProps> = ({
51
51
  }
52
52
 
53
53
  const headerRow = (
54
- <tr className={clsx(styles.headerRow)}>
55
- <td></td>
56
- {cells}
57
- </tr>
54
+ <thead>
55
+ <tr className={clsx(styles.headerRow)}>
56
+ <td></td>
57
+ {cells}
58
+ </tr>
59
+ </thead>
58
60
  );
59
61
  const rows: ReactNode[] = [];
60
62
  scoreGroup.forEach((g) => {
@@ -84,12 +86,14 @@ export const ScoreGrid: FC<ScoreGridProps> = ({
84
86
  subTables.push(
85
87
  <>
86
88
  {index > 0 ? (
87
- <tr>
88
- <td
89
- colSpan={columnCount + 1}
90
- className={clsx(styles.groupSeparator)}
91
- ></td>
92
- </tr>
89
+ <tbody className={clsx(styles.tableSeparator)}>
90
+ <tr>
91
+ <td
92
+ colSpan={columnCount + 1}
93
+ className={clsx(styles.groupSeparator)}
94
+ ></td>
95
+ </tr>
96
+ </tbody>
93
97
  ) : undefined}
94
98
  {headerRow}
95
99
  <tbody className={clsx("table-group-divider", styles.tableBody)}>
@@ -1,4 +1,4 @@
1
- import { FC, useMemo } from "react";
1
+ import { FC, RefObject, useMemo, useRef } from "react";
2
2
  import {
3
3
  EvalError,
4
4
  EvalPlan,
@@ -20,6 +20,7 @@ export const useInfoTabConfig = (
20
20
  evalError: EvalError | undefined | null,
21
21
  evalResults: EvalResults | undefined | null,
22
22
  ) => {
23
+ const scrollRef = useRef<HTMLDivElement | null>(null);
23
24
  const totalSampleCount = useTotalSampleCount();
24
25
  return useMemo(() => {
25
26
  return {
@@ -33,12 +34,14 @@ export const useInfoTabConfig = (
33
34
  evalError,
34
35
  evalResults,
35
36
  sampleCount: totalSampleCount,
37
+ scrollRef,
36
38
  },
39
+ scrollRef,
37
40
  };
38
41
  }, [evalSpec, evalPlan, evalError, evalResults, totalSampleCount]);
39
42
  };
40
43
 
41
- interface PlanTabProps {
44
+ interface InfoTabProps {
42
45
  evalSpec?: EvalSpec;
43
46
  evalPlan?: EvalPlan;
44
47
  evalStats?: EvalStats;
@@ -47,15 +50,17 @@ interface PlanTabProps {
47
50
  evalStatus?: "started" | "error" | "cancelled" | "success";
48
51
  evalError?: EvalError;
49
52
  sampleCount?: number;
53
+ scrollRef: RefObject<HTMLDivElement | null>;
50
54
  }
51
55
 
52
- export const InfoTab: FC<PlanTabProps> = ({
56
+ export const InfoTab: FC<InfoTabProps> = ({
53
57
  evalSpec,
54
58
  evalPlan,
55
59
  evalResults,
56
60
  evalStatus,
57
61
  evalError,
58
62
  sampleCount,
63
+ scrollRef,
59
64
  }) => {
60
65
  const showWarning =
61
66
  sampleCount === 0 &&
@@ -79,6 +84,7 @@ export const InfoTab: FC<PlanTabProps> = ({
79
84
  evalSpec={evalSpec}
80
85
  evalPlan={evalPlan}
81
86
  scores={evalResults?.scores}
87
+ scrollRef={scrollRef}
82
88
  />
83
89
  {evalStatus === "error" && evalError ? (
84
90
  <TaskErrorCard error={evalError} />
@@ -30,9 +30,7 @@ export const useJsonTabConfig = (
30
30
  evalResults: EvalResults | undefined | null,
31
31
  evalStats: EvalStats | undefined,
32
32
  ) => {
33
- const selectedLogFile = useStore((state) =>
34
- state.logsActions.getSelectedLogFile(),
35
- );
33
+ const selectedLogFile = useStore((state) => state.logs.selectedLogFile);
36
34
  const selectedTab = useStore((state) => state.app.tabs.workspace);
37
35
 
38
36
  return useMemo(() => {
@@ -106,7 +106,9 @@ export const SamplesTab: FC<SamplesTabProps> = ({ running }) => {
106
106
  const groupByOrder = useGroupByOrder();
107
107
  const currentScore = useScore();
108
108
 
109
- const selectedSample = useStore((state) => state.sample.selectedSample);
109
+ const selectedSampleIdentifier = useStore(
110
+ (state) => state.sample.sample_identifier,
111
+ );
110
112
 
111
113
  const [items, setItems] = useState<ListItem[]>([]);
112
114
  const [sampleItems, setSampleItems] = useState<ListItem[]>([]);
@@ -213,7 +215,11 @@ export const SamplesTab: FC<SamplesTabProps> = ({ running }) => {
213
215
  ) : undefined}
214
216
  {showingSampleDialog && (
215
217
  <SampleDialog
216
- id={String(selectedSample?.id || "")}
218
+ id={
219
+ selectedSampleIdentifier
220
+ ? `${selectedSampleIdentifier.id}_${selectedSampleIdentifier.epoch}`
221
+ : ""
222
+ }
217
223
  title={title}
218
224
  showingSampleDialog={showingSampleDialog}
219
225
  />
@@ -7,5 +7,6 @@ export interface TabDescriptor<P> {
7
7
  label: string;
8
8
  component: ComponentType<P>;
9
9
  componentProps: P;
10
+ className?: string | string[];
10
11
  tools?: () => ReactNode[] | undefined;
11
12
  }
@@ -9,8 +9,15 @@
9
9
  display: grid;
10
10
  grid-template-columns: max-content auto;
11
11
  column-gap: 1em;
12
+ row-gap: 0.1em;
12
13
  }
13
14
 
14
15
  .role {
15
16
  grid-column: -1/1;
16
17
  }
18
+
19
+ .sep {
20
+ grid-column: -1/1;
21
+ height: 1px;
22
+ background-color: var(--bs-light-border-subtle);
23
+ }
@@ -51,14 +51,15 @@ export const ModelCard: FC<ModelCardProps> = ({ evalSpec }) => {
51
51
  >
52
52
  {modelKey}
53
53
  </div>
54
-
54
+ <div className={clsx(styles.sep)} />
55
55
  <div className={clsx("text-style-label")}>Model</div>
56
56
  <div>{modelInfo.model}</div>
57
-
57
+ <div className={clsx(styles.sep)} />
58
58
  <div className={clsx("text-style-label")}>Base Url</div>
59
59
  <div className="text-size-small">
60
60
  {modelInfo.base_url || noneEl}
61
61
  </div>
62
+ <div className={clsx(styles.sep)} />
62
63
  <div className={clsx("text-style-label")}>Configuration</div>
63
64
  <div className="text-size-small">
64
65
  {modelInfo.config &&
@@ -72,6 +73,7 @@ export const ModelCard: FC<ModelCardProps> = ({ evalSpec }) => {
72
73
  noneEl
73
74
  )}
74
75
  </div>
76
+ <div className={clsx(styles.sep)} />
75
77
  <div className={clsx("text-style-label")}>Args</div>
76
78
  <div className="text-size-small">
77
79
  {Object.keys(modelInfo.args).length > 0 ? (
@@ -82,6 +84,7 @@ export const ModelCard: FC<ModelCardProps> = ({ evalSpec }) => {
82
84
  noneEl
83
85
  )}
84
86
  </div>
87
+ <div className={clsx(styles.sep)} />
85
88
  </div>
86
89
  );
87
90
  })}
@@ -1,19 +1,25 @@
1
- import { FC } from "react";
1
+ import { FC, RefObject } from "react";
2
2
  import { EvalPlan, EvalScore, EvalSpec } from "../../@types/log";
3
3
  import { Card, CardBody, CardHeader } from "../../components/Card";
4
- import { MetaDataView } from "../content/MetaDataView";
4
+ import { RecordTree } from "../content/RecordTree";
5
5
  import { PlanDetailView } from "./PlanDetailView";
6
6
 
7
7
  interface PlanCardProps {
8
8
  evalSpec?: EvalSpec;
9
9
  evalPlan?: EvalPlan;
10
10
  scores?: EvalScore[];
11
+ scrollRef: RefObject<HTMLDivElement | null>;
11
12
  }
12
13
 
13
14
  /**
14
15
  * Renders the plan card
15
16
  */
16
- export const PlanCard: FC<PlanCardProps> = ({ evalSpec, evalPlan, scores }) => {
17
+ export const PlanCard: FC<PlanCardProps> = ({
18
+ evalSpec,
19
+ evalPlan,
20
+ scores,
21
+ scrollRef,
22
+ }) => {
17
23
  const metadata = evalSpec?.metadata || {};
18
24
 
19
25
  return (
@@ -33,11 +39,10 @@ export const PlanCard: FC<PlanCardProps> = ({ evalSpec, evalPlan, scores }) => {
33
39
  <Card>
34
40
  <CardHeader label="Metadata" />
35
41
  <CardBody id={"task-metadata`"}>
36
- <MetaDataView
37
- key={`plan-md-metadata`}
38
- className={"text-size-small"}
39
- entries={metadata}
40
- tableOptions="sm"
42
+ <RecordTree
43
+ id={"plan-md-metadata"}
44
+ record={metadata}
45
+ scrollRef={scrollRef}
41
46
  />
42
47
  </CardBody>
43
48
  </Card>