inspect-ai 0.3.81__py3-none-any.whl → 0.3.82__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 (179) hide show
  1. inspect_ai/_cli/eval.py +35 -2
  2. inspect_ai/_cli/util.py +44 -1
  3. inspect_ai/_display/core/config.py +1 -1
  4. inspect_ai/_display/core/display.py +13 -4
  5. inspect_ai/_display/core/results.py +1 -1
  6. inspect_ai/_display/textual/widgets/task_detail.py +5 -4
  7. inspect_ai/_eval/eval.py +38 -1
  8. inspect_ai/_eval/evalset.py +5 -0
  9. inspect_ai/_eval/run.py +5 -2
  10. inspect_ai/_eval/task/log.py +53 -6
  11. inspect_ai/_eval/task/run.py +51 -10
  12. inspect_ai/_util/constants.py +2 -0
  13. inspect_ai/_util/file.py +17 -1
  14. inspect_ai/_util/json.py +36 -1
  15. inspect_ai/_view/server.py +113 -1
  16. inspect_ai/_view/www/App.css +1 -1
  17. inspect_ai/_view/www/dist/assets/index.css +518 -296
  18. inspect_ai/_view/www/dist/assets/index.js +38803 -36307
  19. inspect_ai/_view/www/eslint.config.mjs +1 -1
  20. inspect_ai/_view/www/log-schema.json +13 -0
  21. inspect_ai/_view/www/node_modules/flatted/python/flatted.py +149 -0
  22. inspect_ai/_view/www/package.json +8 -2
  23. inspect_ai/_view/www/src/App.tsx +151 -855
  24. inspect_ai/_view/www/src/api/api-browser.ts +176 -5
  25. inspect_ai/_view/www/src/api/api-vscode.ts +75 -1
  26. inspect_ai/_view/www/src/api/client-api.ts +66 -10
  27. inspect_ai/_view/www/src/api/jsonrpc.ts +2 -0
  28. inspect_ai/_view/www/src/api/types.ts +107 -2
  29. inspect_ai/_view/www/src/appearance/icons.ts +1 -0
  30. inspect_ai/_view/www/src/components/AsciinemaPlayer.tsx +3 -3
  31. inspect_ai/_view/www/src/components/DownloadPanel.tsx +2 -2
  32. inspect_ai/_view/www/src/components/ExpandablePanel.tsx +56 -61
  33. inspect_ai/_view/www/src/components/FindBand.tsx +17 -9
  34. inspect_ai/_view/www/src/components/HumanBaselineView.tsx +1 -1
  35. inspect_ai/_view/www/src/components/JsonPanel.tsx +14 -24
  36. inspect_ai/_view/www/src/components/LargeModal.tsx +2 -35
  37. inspect_ai/_view/www/src/components/LightboxCarousel.tsx +27 -11
  38. inspect_ai/_view/www/src/components/LiveVirtualList.module.css +11 -0
  39. inspect_ai/_view/www/src/components/LiveVirtualList.tsx +177 -0
  40. inspect_ai/_view/www/src/components/MarkdownDiv.tsx +3 -3
  41. inspect_ai/_view/www/src/components/MessageBand.tsx +14 -9
  42. inspect_ai/_view/www/src/components/MorePopOver.tsx +3 -3
  43. inspect_ai/_view/www/src/components/NavPills.tsx +20 -8
  44. inspect_ai/_view/www/src/components/NoContentsPanel.module.css +12 -0
  45. inspect_ai/_view/www/src/components/NoContentsPanel.tsx +20 -0
  46. inspect_ai/_view/www/src/components/ProgressBar.module.css +5 -4
  47. inspect_ai/_view/www/src/components/ProgressBar.tsx +3 -2
  48. inspect_ai/_view/www/src/components/PulsingDots.module.css +81 -0
  49. inspect_ai/_view/www/src/components/PulsingDots.tsx +45 -0
  50. inspect_ai/_view/www/src/components/TabSet.tsx +4 -37
  51. inspect_ai/_view/www/src/components/ToolButton.tsx +3 -4
  52. inspect_ai/_view/www/src/index.tsx +26 -94
  53. inspect_ai/_view/www/src/logfile/remoteLogFile.ts +9 -1
  54. inspect_ai/_view/www/src/logfile/remoteZipFile.ts +30 -4
  55. inspect_ai/_view/www/src/metadata/RenderedContent.tsx +4 -6
  56. inspect_ai/_view/www/src/plan/ScorerDetailView.tsx +1 -1
  57. inspect_ai/_view/www/src/samples/InlineSampleDisplay.module.css +9 -1
  58. inspect_ai/_view/www/src/samples/InlineSampleDisplay.tsx +67 -28
  59. inspect_ai/_view/www/src/samples/SampleDialog.tsx +51 -22
  60. inspect_ai/_view/www/src/samples/SampleDisplay.module.css +4 -0
  61. inspect_ai/_view/www/src/samples/SampleDisplay.tsx +144 -90
  62. inspect_ai/_view/www/src/samples/SampleSummaryView.module.css +4 -0
  63. inspect_ai/_view/www/src/samples/SampleSummaryView.tsx +82 -35
  64. inspect_ai/_view/www/src/samples/SamplesTools.tsx +23 -30
  65. inspect_ai/_view/www/src/samples/chat/ChatMessage.tsx +2 -1
  66. inspect_ai/_view/www/src/samples/chat/ChatMessageRenderer.tsx +1 -1
  67. inspect_ai/_view/www/src/samples/chat/ChatViewVirtualList.tsx +45 -53
  68. inspect_ai/_view/www/src/samples/chat/MessageContent.tsx +4 -1
  69. inspect_ai/_view/www/src/samples/chat/MessageContents.tsx +3 -0
  70. inspect_ai/_view/www/src/samples/chat/messages.ts +34 -0
  71. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.module.css +3 -0
  72. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.tsx +10 -1
  73. inspect_ai/_view/www/src/samples/chat/tools/ToolInput.tsx +22 -46
  74. inspect_ai/_view/www/src/samples/descriptor/samplesDescriptor.tsx +25 -17
  75. inspect_ai/_view/www/src/samples/descriptor/score/ObjectScoreDescriptor.tsx +2 -1
  76. inspect_ai/_view/www/src/samples/descriptor/types.ts +6 -5
  77. inspect_ai/_view/www/src/samples/list/SampleFooter.module.css +21 -3
  78. inspect_ai/_view/www/src/samples/list/SampleFooter.tsx +20 -1
  79. inspect_ai/_view/www/src/samples/list/SampleList.tsx +105 -85
  80. inspect_ai/_view/www/src/samples/list/SampleRow.module.css +6 -0
  81. inspect_ai/_view/www/src/samples/list/SampleRow.tsx +27 -14
  82. inspect_ai/_view/www/src/samples/sample-tools/SelectScorer.tsx +29 -18
  83. inspect_ai/_view/www/src/samples/sample-tools/SortFilter.tsx +28 -28
  84. inspect_ai/_view/www/src/samples/sample-tools/sample-filter/SampleFilter.tsx +19 -9
  85. inspect_ai/_view/www/src/samples/sampleDataAdapter.ts +33 -0
  86. inspect_ai/_view/www/src/samples/sampleLimit.ts +2 -2
  87. inspect_ai/_view/www/src/samples/scores/SampleScoreView.tsx +7 -9
  88. inspect_ai/_view/www/src/samples/scores/SampleScores.tsx +7 -11
  89. inspect_ai/_view/www/src/samples/transcript/ErrorEventView.tsx +0 -13
  90. inspect_ai/_view/www/src/samples/transcript/InfoEventView.tsx +0 -13
  91. inspect_ai/_view/www/src/samples/transcript/InputEventView.tsx +0 -13
  92. inspect_ai/_view/www/src/samples/transcript/ModelEventView.module.css +4 -0
  93. inspect_ai/_view/www/src/samples/transcript/ModelEventView.tsx +10 -24
  94. inspect_ai/_view/www/src/samples/transcript/SampleInitEventView.tsx +0 -13
  95. inspect_ai/_view/www/src/samples/transcript/SampleLimitEventView.tsx +4 -22
  96. inspect_ai/_view/www/src/samples/transcript/SandboxEventView.tsx +15 -24
  97. inspect_ai/_view/www/src/samples/transcript/ScoreEventView.tsx +0 -13
  98. inspect_ai/_view/www/src/samples/transcript/StepEventView.tsx +6 -28
  99. inspect_ai/_view/www/src/samples/transcript/SubtaskEventView.tsx +24 -34
  100. inspect_ai/_view/www/src/samples/transcript/ToolEventView.module.css +4 -0
  101. inspect_ai/_view/www/src/samples/transcript/ToolEventView.tsx +8 -13
  102. inspect_ai/_view/www/src/samples/transcript/TranscriptView.tsx +197 -338
  103. inspect_ai/_view/www/src/samples/transcript/TranscriptVirtualListComponent.module.css +16 -0
  104. inspect_ai/_view/www/src/samples/transcript/TranscriptVirtualListComponent.tsx +44 -0
  105. inspect_ai/_view/www/src/samples/transcript/event/EventNav.tsx +7 -4
  106. inspect_ai/_view/www/src/samples/transcript/event/EventPanel.tsx +52 -58
  107. inspect_ai/_view/www/src/samples/transcript/event/EventProgressPanel.module.css +23 -0
  108. inspect_ai/_view/www/src/samples/transcript/event/EventProgressPanel.tsx +27 -0
  109. inspect_ai/_view/www/src/samples/transcript/state/StateEventRenderers.tsx +30 -1
  110. inspect_ai/_view/www/src/samples/transcript/state/StateEventView.tsx +102 -72
  111. inspect_ai/_view/www/src/scoring/utils.ts +87 -0
  112. inspect_ai/_view/www/src/state/appSlice.ts +244 -0
  113. inspect_ai/_view/www/src/state/hooks.ts +397 -0
  114. inspect_ai/_view/www/src/state/logPolling.ts +196 -0
  115. inspect_ai/_view/www/src/state/logSlice.ts +214 -0
  116. inspect_ai/_view/www/src/state/logsPolling.ts +118 -0
  117. inspect_ai/_view/www/src/state/logsSlice.ts +181 -0
  118. inspect_ai/_view/www/src/state/samplePolling.ts +311 -0
  119. inspect_ai/_view/www/src/state/sampleSlice.ts +127 -0
  120. inspect_ai/_view/www/src/state/sampleUtils.ts +21 -0
  121. inspect_ai/_view/www/src/state/scrolling.ts +206 -0
  122. inspect_ai/_view/www/src/state/store.ts +168 -0
  123. inspect_ai/_view/www/src/state/store_filter.ts +84 -0
  124. inspect_ai/_view/www/src/state/utils.ts +23 -0
  125. inspect_ai/_view/www/src/storage/index.ts +26 -0
  126. inspect_ai/_view/www/src/types/log.d.ts +2 -0
  127. inspect_ai/_view/www/src/types.ts +94 -32
  128. inspect_ai/_view/www/src/utils/attachments.ts +58 -23
  129. inspect_ai/_view/www/src/utils/logger.ts +52 -0
  130. inspect_ai/_view/www/src/utils/polling.ts +100 -0
  131. inspect_ai/_view/www/src/utils/react.ts +30 -0
  132. inspect_ai/_view/www/src/utils/vscode.ts +1 -1
  133. inspect_ai/_view/www/src/workspace/WorkSpace.tsx +181 -216
  134. inspect_ai/_view/www/src/workspace/WorkSpaceView.tsx +11 -53
  135. inspect_ai/_view/www/src/workspace/navbar/Navbar.tsx +8 -18
  136. inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.module.css +1 -0
  137. inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.tsx +40 -22
  138. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.module.css +0 -1
  139. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.tsx +98 -39
  140. inspect_ai/_view/www/src/workspace/navbar/RunningStatusPanel.module.css +32 -0
  141. inspect_ai/_view/www/src/workspace/navbar/RunningStatusPanel.tsx +32 -0
  142. inspect_ai/_view/www/src/workspace/navbar/SecondaryBar.tsx +11 -13
  143. inspect_ai/_view/www/src/workspace/navbar/StatusPanel.tsx +6 -2
  144. inspect_ai/_view/www/src/workspace/sidebar/LogDirectoryTitleView.tsx +4 -4
  145. inspect_ai/_view/www/src/workspace/sidebar/Sidebar.tsx +28 -13
  146. inspect_ai/_view/www/src/workspace/tabs/InfoTab.tsx +5 -10
  147. inspect_ai/_view/www/src/workspace/tabs/JsonTab.tsx +4 -4
  148. inspect_ai/_view/www/src/workspace/tabs/RunningNoSamples.module.css +22 -0
  149. inspect_ai/_view/www/src/workspace/tabs/RunningNoSamples.tsx +19 -0
  150. inspect_ai/_view/www/src/workspace/tabs/SamplesTab.tsx +110 -115
  151. inspect_ai/_view/www/src/workspace/tabs/grouping.ts +37 -5
  152. inspect_ai/_view/www/src/workspace/tabs/types.ts +4 -0
  153. inspect_ai/_view/www/src/workspace/types.ts +4 -3
  154. inspect_ai/_view/www/src/workspace/utils.ts +4 -4
  155. inspect_ai/_view/www/vite.config.js +6 -0
  156. inspect_ai/_view/www/yarn.lock +370 -354
  157. inspect_ai/log/_condense.py +26 -0
  158. inspect_ai/log/_log.py +6 -3
  159. inspect_ai/log/_recorders/buffer/__init__.py +14 -0
  160. inspect_ai/log/_recorders/buffer/buffer.py +30 -0
  161. inspect_ai/log/_recorders/buffer/database.py +685 -0
  162. inspect_ai/log/_recorders/buffer/filestore.py +259 -0
  163. inspect_ai/log/_recorders/buffer/types.py +84 -0
  164. inspect_ai/log/_recorders/eval.py +2 -11
  165. inspect_ai/log/_recorders/types.py +30 -0
  166. inspect_ai/log/_transcript.py +27 -1
  167. inspect_ai/model/_call_tools.py +1 -0
  168. inspect_ai/model/_generate_config.py +2 -2
  169. inspect_ai/model/_model.py +1 -0
  170. inspect_ai/tool/_tool_support_helpers.py +4 -4
  171. inspect_ai/tool/_tools/_web_browser/_web_browser.py +3 -1
  172. inspect_ai/util/_subtask.py +1 -0
  173. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/METADATA +1 -1
  174. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/RECORD +178 -138
  175. inspect_ai/_view/www/src/samples/transcript/SampleTranscript.tsx +0 -22
  176. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/WHEEL +0 -0
  177. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/entry_points.txt +0 -0
  178. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/licenses/LICENSE +0 -0
  179. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.82.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,244 @@
1
+ import { StateSnapshot } from "react-virtuoso";
2
+ import { Capabilities } from "../api/types";
3
+ import { kEvalWorkspaceTabId, kSampleTranscriptTabId } from "../constants";
4
+ import { AppState, AppStatus } from "../types";
5
+ import { clearDocumentSelection } from "../utils/browser";
6
+ import { StoreState } from "./store";
7
+
8
+ export interface AppSlice {
9
+ app: AppState;
10
+ capabilities: Capabilities;
11
+ appActions: {
12
+ setStatus: (status: AppStatus) => void;
13
+ setOffcanvas: (show: boolean) => void;
14
+ setShowFind: (show: boolean) => void;
15
+ hideFind: () => void;
16
+
17
+ setShowingSampleDialog: (showing: boolean) => void;
18
+ setWorkspaceTab: (tab: string) => void;
19
+ clearWorkspaceTab: () => void;
20
+
21
+ setSampleTab: (tab: string) => void;
22
+ clearSampleTab: () => void;
23
+
24
+ getScrollPosition: (name: string) => number | undefined;
25
+ setScrollPosition: (name: string, value: number) => void;
26
+
27
+ getListPosition: (name: string) => StateSnapshot | undefined;
28
+ setListPosition: (name: string, state: StateSnapshot) => void;
29
+ clearListPosition: (name: string) => void;
30
+
31
+ getCollapsed: (name: string, defaultValue?: boolean) => boolean;
32
+ setCollapsed: (name: string, value: boolean) => void;
33
+
34
+ getMessageVisible: (name: string, defaultValue?: boolean) => boolean;
35
+ setMessageVisible: (name: string, value: boolean) => void;
36
+ clearMessageVisible: (name: string) => void;
37
+
38
+ getPropertyValue: <T>(bagName: string, key: string, defaultValue?: T) => T;
39
+ setPropertyValue: <T>(bagName: string, key: string, value: T) => void;
40
+ removePropertyValue: (bagName: string, key: string) => void;
41
+ };
42
+ }
43
+
44
+ const kDefaultWorkspaceTab = kEvalWorkspaceTabId;
45
+ const kDefaultSampleTab = kSampleTranscriptTabId;
46
+
47
+ const initialState: AppState = {
48
+ status: { loading: false },
49
+ offcanvas: false,
50
+ showFind: false,
51
+ dialogs: {
52
+ sample: false,
53
+ },
54
+ tabs: {
55
+ workspace: kDefaultWorkspaceTab,
56
+ sample: kDefaultSampleTab,
57
+ },
58
+ scrollPositions: {},
59
+ listPositions: {},
60
+ collapsed: {},
61
+ messages: {},
62
+ propertyBags: {},
63
+ };
64
+
65
+ export const createAppSlice = (
66
+ set: (fn: (state: StoreState) => void) => void,
67
+ get: () => StoreState,
68
+ _store: any,
69
+ ): [AppSlice, () => void] => {
70
+ const getBoolRecord = (
71
+ record: Record<string, boolean>,
72
+ name: string,
73
+ defaultValue?: boolean,
74
+ ) => {
75
+ if (Object.keys(record).includes(name)) {
76
+ return record[name];
77
+ } else {
78
+ return defaultValue || false;
79
+ }
80
+ };
81
+
82
+ const slice = {
83
+ // State
84
+ app: initialState,
85
+ capabilities: {} as Capabilities,
86
+
87
+ // Actions
88
+ appActions: {
89
+ setStatus: (status: AppStatus) =>
90
+ set((state) => {
91
+ state.app.status = status;
92
+ }),
93
+
94
+ setOffcanvas: (show: boolean) =>
95
+ set((state) => {
96
+ state.app.offcanvas = show;
97
+ }),
98
+
99
+ setShowFind: (show: boolean) =>
100
+ set((state) => {
101
+ state.app.showFind = show;
102
+ }),
103
+
104
+ hideFind: () => {
105
+ clearDocumentSelection();
106
+ set((state) => {
107
+ state.app.showFind = false;
108
+ });
109
+ },
110
+ setShowingSampleDialog: (showing: boolean) => {
111
+ set((state) => {
112
+ state.app.dialogs.sample = showing;
113
+ });
114
+ if (!showing) {
115
+ const state = get();
116
+ state.appActions.clearSampleTab();
117
+ state.sampleActions.clearSelectedSample();
118
+ }
119
+ },
120
+ setWorkspaceTab: (tab: string) => {
121
+ set((state) => {
122
+ state.app.tabs.workspace = tab;
123
+ });
124
+ },
125
+ clearWorkspaceTab: () => {
126
+ set((state) => {
127
+ state.app.tabs.workspace = kDefaultWorkspaceTab;
128
+ });
129
+ },
130
+ setSampleTab: (tab: string) => {
131
+ set((state) => {
132
+ state.app.tabs.sample = tab;
133
+ });
134
+ },
135
+ clearSampleTab: () => {
136
+ set((state) => {
137
+ state.app.tabs.sample = kDefaultSampleTab;
138
+ });
139
+ },
140
+ getScrollPosition: (name: string) => {
141
+ const state = get();
142
+ return state.app.scrollPositions[name];
143
+ },
144
+ setScrollPosition: (name: string, position: number) => {
145
+ set((state) => {
146
+ state.app.scrollPositions[name] = position;
147
+ });
148
+ },
149
+ getListPosition: (name: string) => {
150
+ const state = get();
151
+ if (Object.keys(state.app.listPositions).includes(name)) {
152
+ return state.app.listPositions[name];
153
+ } else {
154
+ return undefined;
155
+ }
156
+ },
157
+ setListPosition: (name: string, position: StateSnapshot) => {
158
+ set((state) => {
159
+ state.app.listPositions[name] = position;
160
+ });
161
+ },
162
+ clearListPosition: (name: string) => {
163
+ set((state) => {
164
+ // Remove the key
165
+ const newListPositions = { ...state.app.listPositions };
166
+ delete newListPositions[name];
167
+
168
+ return {
169
+ app: {
170
+ ...state.app,
171
+ listPositions: newListPositions,
172
+ },
173
+ };
174
+ });
175
+ },
176
+ getCollapsed: (name: string, defaultValue?: boolean) => {
177
+ return getBoolRecord(get().app.collapsed, name, defaultValue);
178
+ },
179
+ setCollapsed: (name: string, value: boolean) => {
180
+ set((state) => {
181
+ state.app.collapsed[name] = value;
182
+ });
183
+ },
184
+ getMessageVisible: (name: string, defaultValue?: boolean) => {
185
+ return getBoolRecord(get().app.messages, name, defaultValue);
186
+ },
187
+ setMessageVisible: (name: string, value: boolean) => {
188
+ set((state) => {
189
+ state.app.messages[name] = value;
190
+ });
191
+ },
192
+ clearMessageVisible: (name: string) => {
193
+ set((state) => {
194
+ delete state.app.messages[name];
195
+ });
196
+ },
197
+ getPropertyValue: <T>(
198
+ bagName: string,
199
+ key: string,
200
+ defaultValue?: T,
201
+ ): T => {
202
+ const state = get();
203
+ const bag = state.app.propertyBags[bagName] || {};
204
+ return (key in bag ? bag[key] : defaultValue) as T;
205
+ },
206
+
207
+ setPropertyValue: <T>(bagName: string, key: string, value: T) => {
208
+ set((state) => {
209
+ // Create the bag if it doesn't exist
210
+ if (!state.app.propertyBags[bagName]) {
211
+ state.app.propertyBags[bagName] = {};
212
+ }
213
+ // Only update the specific key
214
+ state.app.propertyBags[bagName][key] = value;
215
+ });
216
+ },
217
+
218
+ removePropertyValue: (bagName: string, key: string) => {
219
+ set((state) => {
220
+ if (state.app.propertyBags[bagName]) {
221
+ const { [key]: _, ...rest } = state.app.propertyBags[bagName];
222
+ state.app.propertyBags[bagName] = rest;
223
+ }
224
+ });
225
+ },
226
+ },
227
+ } as const;
228
+
229
+ const cleanup = () => {};
230
+
231
+ return [slice, cleanup];
232
+ };
233
+
234
+ export const initializeAppSlice = (
235
+ set: (fn: (state: StoreState) => void) => void,
236
+ capabilities: Capabilities,
237
+ ) => {
238
+ set((state) => {
239
+ state.capabilities = capabilities;
240
+ if (!state.app) {
241
+ state.app = initialState;
242
+ }
243
+ });
244
+ };
@@ -0,0 +1,397 @@
1
+ import { highlightElement } from "prismjs";
2
+ import { useCallback, useEffect, useMemo, useRef } from "react";
3
+ import { SampleSummary } from "../api/types";
4
+ import { kEpochAscVal, kSampleAscVal, kScoreAscVal } from "../constants";
5
+ import {
6
+ createEvalDescriptor,
7
+ createSamplesDescriptor,
8
+ } from "../samples/descriptor/samplesDescriptor";
9
+ import { filterSamples } from "../samples/sample-tools/filters";
10
+ import {
11
+ byEpoch,
12
+ bySample,
13
+ sortSamples,
14
+ } from "../samples/sample-tools/SortFilter";
15
+ import { getAvailableScorers, getDefaultScorer } from "../scoring/utils";
16
+ import { Events } from "../types/log";
17
+ import { createLogger } from "../utils/logger";
18
+ import { useStore } from "./store";
19
+ import { mergeSampleSummaries } from "./utils";
20
+
21
+ const log = createLogger("hooks");
22
+
23
+ export const useEvalSpec = () => {
24
+ const selectedLogSummary = useStore((state) => state.log.selectedLogSummary);
25
+ return selectedLogSummary?.eval;
26
+ };
27
+
28
+ // Fetches all samples summaries (both completed and incomplete)
29
+ // without applying any filtering
30
+ export const useSampleSummaries = () => {
31
+ const selectedLogSummary = useStore((state) => state.log.selectedLogSummary);
32
+ const pendingSampleSummaries = useStore(
33
+ (state) => state.log.pendingSampleSummaries,
34
+ );
35
+
36
+ return useMemo(() => {
37
+ return mergeSampleSummaries(
38
+ selectedLogSummary?.sampleSummaries || [],
39
+ pendingSampleSummaries?.samples || [],
40
+ );
41
+ }, [selectedLogSummary, pendingSampleSummaries]);
42
+ };
43
+
44
+ // Counts the total number of unfiltered sample summaries (both complete and incomplete)
45
+ export const useTotalSampleCount = () => {
46
+ const sampleSummaries = useSampleSummaries();
47
+ return useMemo(() => {
48
+ return sampleSummaries.length;
49
+ }, [sampleSummaries]);
50
+ };
51
+
52
+ // Provides the currently selected score for this eval, providing a default
53
+ // based upon the configuration (eval + summaries) if no scorer has been
54
+ // selected
55
+ export const useScore = () => {
56
+ const selectedLogSummary = useStore((state) => state.log.selectedLogSummary);
57
+ const sampleSummaries = useSampleSummaries();
58
+ const score = useStore((state) => state.log.score);
59
+ return useMemo(() => {
60
+ if (score) {
61
+ return score;
62
+ } else if (selectedLogSummary) {
63
+ return getDefaultScorer(selectedLogSummary, sampleSummaries);
64
+ } else {
65
+ return undefined;
66
+ }
67
+ }, [selectedLogSummary, sampleSummaries, score]);
68
+ };
69
+
70
+ // Provides the list of available scorers. Will inspect the eval or samples
71
+ // to determine scores (even for in progress evals that don't yet have final
72
+ // metrics)
73
+ export const useScores = () => {
74
+ const selectedLogSummary = useStore((state) => state.log.selectedLogSummary);
75
+ const sampleSummaries = useSampleSummaries();
76
+ return useMemo(() => {
77
+ if (!selectedLogSummary) {
78
+ return [];
79
+ }
80
+
81
+ return getAvailableScorers(selectedLogSummary, sampleSummaries) || [];
82
+ }, [selectedLogSummary, sampleSummaries]);
83
+ };
84
+
85
+ // Provides the eval descriptor
86
+ export const useEvalDescriptor = () => {
87
+ const scores = useScores();
88
+ const sampleSummaries = useSampleSummaries();
89
+ return useMemo(() => {
90
+ return scores ? createEvalDescriptor(scores, sampleSummaries) : null;
91
+ }, [scores, sampleSummaries]);
92
+ };
93
+
94
+ // Provides the sampls descriptor
95
+ export const useSampleDescriptor = () => {
96
+ const evalDescriptor = useEvalDescriptor();
97
+ const sampleSummaries = useSampleSummaries();
98
+ const score = useScore();
99
+ return useMemo(() => {
100
+ return evalDescriptor
101
+ ? createSamplesDescriptor(sampleSummaries, evalDescriptor, score)
102
+ : undefined;
103
+ }, [evalDescriptor, sampleSummaries, score]);
104
+ };
105
+
106
+ // Provides the list of filtered samples
107
+ // (applying sorting, grouping, and filtering)
108
+ export const useFilteredSamples = () => {
109
+ const evalDescriptor = useEvalDescriptor();
110
+ const sampleSummaries = useSampleSummaries();
111
+ const filter = useStore((state) => state.log.filter);
112
+ const epoch = useStore((state) => state.log.epoch);
113
+ const sort = useStore((state) => state.log.sort);
114
+ const samplesDescriptor = useSampleDescriptor();
115
+ const score = useScore();
116
+
117
+ return useMemo(() => {
118
+ // Apply filters
119
+ const prefiltered =
120
+ evalDescriptor && filter.value
121
+ ? filterSamples(evalDescriptor, sampleSummaries, filter.value).result
122
+ : sampleSummaries;
123
+
124
+ // Filter epochs
125
+ const filtered =
126
+ epoch && epoch !== "all"
127
+ ? prefiltered.filter((sample) => epoch === String(sample.epoch))
128
+ : prefiltered;
129
+
130
+ // Sort samples
131
+ const sorted = samplesDescriptor
132
+ ? sortSamples(sort, filtered, samplesDescriptor, score)
133
+ : filtered;
134
+
135
+ return [...sorted];
136
+ }, [
137
+ evalDescriptor,
138
+ sampleSummaries,
139
+ filter,
140
+ epoch,
141
+ sort,
142
+ samplesDescriptor,
143
+ score,
144
+ ]);
145
+ };
146
+
147
+ // Computes the group by to use given a particular sort
148
+ export const useGroupBy = () => {
149
+ const selectedLogSummary = useStore((state) => state.log.selectedLogSummary);
150
+ const sort = useStore((state) => state.log.sort);
151
+ const epoch = useStore((state) => state.log.epoch);
152
+ return useMemo(() => {
153
+ const epochs = selectedLogSummary?.eval?.config?.epochs || 1;
154
+ if (epochs > 1) {
155
+ if (byEpoch(sort) || epoch !== "all") {
156
+ return "epoch";
157
+ } else if (bySample(sort)) {
158
+ return "sample";
159
+ }
160
+ }
161
+
162
+ return "none";
163
+ }, [selectedLogSummary, sort, epoch]);
164
+ };
165
+
166
+ // Computes the ordering for groups based upon the sort
167
+ export const useGroupByOrder = () => {
168
+ const sort = useStore((state) => state.log.sort);
169
+ return useMemo(() => {
170
+ return sort === kSampleAscVal ||
171
+ sort === kEpochAscVal ||
172
+ sort === kScoreAscVal
173
+ ? "asc"
174
+ : "desc";
175
+ }, [sort]);
176
+ };
177
+
178
+ // Provides the currently selected sample summary
179
+ export const useSelectedSampleSummary = (): SampleSummary | undefined => {
180
+ const filteredSamples = useFilteredSamples();
181
+ const selectedIndex = useStore((state) => state.log.selectedSampleIndex);
182
+ return useMemo(() => {
183
+ return filteredSamples[selectedIndex];
184
+ }, [filteredSamples, selectedIndex]);
185
+ };
186
+
187
+ export const useSampleData = () => {
188
+ const sampleStatus = useStore((state) => state.sample.sampleStatus);
189
+ const sampleError = useStore((state) => state.sample.sampleError);
190
+ const selectedSample = useStore((state) => state.sample.selectedSample);
191
+ const runningEvents = useStore(
192
+ (state) => state.sample.runningEvents,
193
+ ) as Events;
194
+ return useMemo(() => {
195
+ return {
196
+ status: sampleStatus,
197
+ error: sampleError,
198
+ sample: selectedSample,
199
+ running: runningEvents,
200
+ };
201
+ }, [sampleStatus, sampleError, selectedSample, runningEvents]);
202
+ };
203
+
204
+ export const useLogSelection = () => {
205
+ const selectedSampleSummary = useSelectedSampleSummary();
206
+ const selectedLogFile = useStore((state) =>
207
+ state.logsActions.getSelectedLogFile(),
208
+ );
209
+
210
+ return useMemo(() => {
211
+ return {
212
+ logFile: selectedLogFile,
213
+ sample: selectedSampleSummary,
214
+ };
215
+ }, [selectedLogFile, selectedSampleSummary]);
216
+ };
217
+
218
+ export const useCollapsedState = (
219
+ id: string,
220
+ defaultValue?: boolean,
221
+ ): [boolean, (value: boolean) => void] => {
222
+ const collapsed = useStore((state) =>
223
+ state.appActions.getCollapsed(id, defaultValue),
224
+ );
225
+ const setCollapsed = useStore((state) => state.appActions.setCollapsed);
226
+ return useMemo(() => {
227
+ const set = (value: boolean) => {
228
+ log.debug("Set collapsed", id, value);
229
+ setCollapsed(id, value);
230
+ };
231
+ return [collapsed, set];
232
+ }, [collapsed, setCollapsed]);
233
+ };
234
+
235
+ export const useMessageVisibility = (
236
+ id: string,
237
+ scope: "sample" | "eval",
238
+ ): [boolean, (visible: boolean) => void] => {
239
+ const visible = useStore((state) =>
240
+ state.appActions.getMessageVisible(id, true),
241
+ );
242
+ const setVisible = useStore((state) => state.appActions.setMessageVisible);
243
+ const clearVisible = useStore(
244
+ (state) => state.appActions.clearMessageVisible,
245
+ );
246
+
247
+ // Track if this is the first render (rehydrate)
248
+ const isFirstRender = useRef(true);
249
+
250
+ // Reset state if the eval changes, but not during initialization
251
+ const selectedLogFile = useStore((state) =>
252
+ state.logsActions.getSelectedLogFile(),
253
+ );
254
+ useEffect(() => {
255
+ // Skip the first effect run
256
+ if (isFirstRender.current) {
257
+ isFirstRender.current = false;
258
+ return;
259
+ }
260
+
261
+ log.debug("clear message (eval)", id);
262
+ clearVisible(id);
263
+ }, [selectedLogFile, clearVisible, id]);
264
+
265
+ // Maybe reset state if sample changes
266
+ const selectedSampleIndex = useStore(
267
+ (state) => state.log.selectedSampleIndex,
268
+ );
269
+ useEffect(() => {
270
+ // Skip the first effect run for sample changes too
271
+ if (isFirstRender.current) {
272
+ return;
273
+ }
274
+
275
+ if (scope === "sample") {
276
+ log.debug("clear message (sample)", id);
277
+ clearVisible(id);
278
+ }
279
+ }, [selectedSampleIndex, clearVisible, id, scope]);
280
+
281
+ return useMemo(() => {
282
+ log.debug("visibility", id, visible);
283
+ const set = (visible: boolean) => {
284
+ log.debug("set visiblity", id);
285
+ setVisible(id, visible);
286
+ };
287
+ return [visible, set];
288
+ }, [visible, setVisible, id]);
289
+ };
290
+
291
+ export function useProperty<T>(
292
+ id: string,
293
+ propertyName: string,
294
+ options?: {
295
+ defaultValue?: T;
296
+ cleanup?: boolean;
297
+ },
298
+ ): [T, (value: T) => void, () => void] {
299
+ options = options || { cleanup: true };
300
+ const setPropertyValue = useStore(
301
+ (state) => state.appActions.setPropertyValue,
302
+ );
303
+ const removePropertyValue = useStore(
304
+ (state) => state.appActions.removePropertyValue,
305
+ );
306
+ const propertyValue = useStore(
307
+ useCallback(
308
+ (state) =>
309
+ state.appActions.getPropertyValue(
310
+ id,
311
+ propertyName,
312
+ options.defaultValue,
313
+ ),
314
+ [id, propertyName, options.defaultValue],
315
+ ),
316
+ );
317
+
318
+ const setValue = useCallback(
319
+ (value: T) => {
320
+ setPropertyValue(id, propertyName, value);
321
+ },
322
+ [id, propertyName, setPropertyValue],
323
+ );
324
+
325
+ const removeValue = useCallback(() => {
326
+ removePropertyValue(id, propertyName);
327
+ }, [id, propertyName, removePropertyValue]);
328
+
329
+ // Clean up on unmount
330
+ useEffect(() => {
331
+ return () => {
332
+ if (options.cleanup) {
333
+ removePropertyValue(id, propertyName);
334
+ }
335
+ };
336
+ }, [id, propertyName, removePropertyValue]);
337
+
338
+ return [propertyValue, setValue, removeValue];
339
+ }
340
+
341
+ export const usePrevious = <T>(value: T) => {
342
+ const ref = useRef<T | undefined>(undefined);
343
+
344
+ useEffect(() => {
345
+ ref.current = value;
346
+ }, [value]);
347
+
348
+ return ref.current;
349
+ };
350
+
351
+ // Syntax highlighting strings larger than this is too slow
352
+ const kPrismRenderMaxSize = 250000;
353
+
354
+ export const usePrismHighlight = (toolCallContent?: string) => {
355
+ const toolViewRef = useRef<HTMLDivElement>(null);
356
+
357
+ useEffect(() => {
358
+ if (
359
+ toolCallContent &&
360
+ toolViewRef.current &&
361
+ toolCallContent.length <= kPrismRenderMaxSize
362
+ ) {
363
+ requestAnimationFrame(() => {
364
+ const codeBlocks = toolViewRef.current!.querySelectorAll("pre code");
365
+ codeBlocks.forEach((block) => {
366
+ if (block.className.includes("language-")) {
367
+ block.classList.add("sourceCode");
368
+ highlightElement(block as HTMLElement);
369
+ }
370
+ });
371
+ });
372
+ }
373
+ }, [toolCallContent]);
374
+
375
+ return toolViewRef;
376
+ };
377
+
378
+ export const useSetSelectedLogIndex = () => {
379
+ const setSelectedLogIndex = useStore(
380
+ (state) => state.logsActions.setSelectedLogIndex,
381
+ );
382
+ const clearSelectedSample = useStore(
383
+ (state) => state.sampleActions.clearSelectedSample,
384
+ );
385
+ const clearSelectedLogSummary = useStore(
386
+ (state) => state.logActions.clearSelectedLogSummary,
387
+ );
388
+
389
+ return useCallback(
390
+ (index: number) => {
391
+ clearSelectedSample();
392
+ clearSelectedLogSummary();
393
+ setSelectedLogIndex(index);
394
+ },
395
+ [setSelectedLogIndex, clearSelectedLogSummary, clearSelectedSample],
396
+ );
397
+ };