inspect-ai 0.3.81__py3-none-any.whl → 0.3.83__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 (297) hide show
  1. inspect_ai/__init__.py +2 -1
  2. inspect_ai/_cli/eval.py +35 -2
  3. inspect_ai/_cli/util.py +44 -1
  4. inspect_ai/_display/core/config.py +1 -1
  5. inspect_ai/_display/core/display.py +13 -4
  6. inspect_ai/_display/core/results.py +1 -1
  7. inspect_ai/_display/textual/app.py +14 -3
  8. inspect_ai/_display/textual/display.py +4 -0
  9. inspect_ai/_display/textual/widgets/samples.py +9 -3
  10. inspect_ai/_display/textual/widgets/task_detail.py +8 -8
  11. inspect_ai/_display/textual/widgets/tasks.py +17 -1
  12. inspect_ai/_display/textual/widgets/vscode.py +44 -0
  13. inspect_ai/_eval/eval.py +74 -25
  14. inspect_ai/_eval/evalset.py +22 -18
  15. inspect_ai/_eval/loader.py +34 -11
  16. inspect_ai/_eval/run.py +13 -15
  17. inspect_ai/_eval/score.py +13 -3
  18. inspect_ai/_eval/task/generate.py +8 -9
  19. inspect_ai/_eval/task/log.py +55 -6
  20. inspect_ai/_eval/task/run.py +51 -10
  21. inspect_ai/_eval/task/task.py +23 -9
  22. inspect_ai/_util/constants.py +2 -0
  23. inspect_ai/_util/file.py +30 -1
  24. inspect_ai/_util/json.py +37 -1
  25. inspect_ai/_util/registry.py +1 -0
  26. inspect_ai/_util/vscode.py +37 -0
  27. inspect_ai/_view/server.py +113 -1
  28. inspect_ai/_view/www/App.css +7 -1
  29. inspect_ai/_view/www/dist/assets/index.css +813 -415
  30. inspect_ai/_view/www/dist/assets/index.js +54475 -32003
  31. inspect_ai/_view/www/eslint.config.mjs +1 -1
  32. inspect_ai/_view/www/log-schema.json +137 -31
  33. inspect_ai/_view/www/node_modules/flatted/python/flatted.py +149 -0
  34. inspect_ai/_view/www/package.json +11 -2
  35. inspect_ai/_view/www/src/App.tsx +161 -853
  36. inspect_ai/_view/www/src/api/api-browser.ts +176 -5
  37. inspect_ai/_view/www/src/api/api-vscode.ts +75 -1
  38. inspect_ai/_view/www/src/api/client-api.ts +66 -10
  39. inspect_ai/_view/www/src/api/jsonrpc.ts +2 -0
  40. inspect_ai/_view/www/src/api/types.ts +107 -2
  41. inspect_ai/_view/www/src/appearance/icons.ts +2 -0
  42. inspect_ai/_view/www/src/components/AsciinemaPlayer.tsx +3 -3
  43. inspect_ai/_view/www/src/components/Card.tsx +6 -4
  44. inspect_ai/_view/www/src/components/DownloadPanel.tsx +2 -2
  45. inspect_ai/_view/www/src/components/ExpandablePanel.tsx +56 -61
  46. inspect_ai/_view/www/src/components/FindBand.tsx +17 -9
  47. inspect_ai/_view/www/src/components/HumanBaselineView.tsx +1 -1
  48. inspect_ai/_view/www/src/components/JsonPanel.tsx +14 -24
  49. inspect_ai/_view/www/src/components/LargeModal.tsx +2 -35
  50. inspect_ai/_view/www/src/components/LightboxCarousel.tsx +27 -11
  51. inspect_ai/_view/www/src/components/LinkButton.module.css +16 -0
  52. inspect_ai/_view/www/src/components/LinkButton.tsx +33 -0
  53. inspect_ai/_view/www/src/components/LiveVirtualList.module.css +11 -0
  54. inspect_ai/_view/www/src/components/LiveVirtualList.tsx +177 -0
  55. inspect_ai/_view/www/src/components/MarkdownDiv.tsx +116 -26
  56. inspect_ai/_view/www/src/components/MessageBand.tsx +14 -9
  57. inspect_ai/_view/www/src/components/Modal.module.css +38 -0
  58. inspect_ai/_view/www/src/components/Modal.tsx +77 -0
  59. inspect_ai/_view/www/src/components/MorePopOver.tsx +3 -3
  60. inspect_ai/_view/www/src/components/NavPills.tsx +20 -8
  61. inspect_ai/_view/www/src/components/NoContentsPanel.module.css +12 -0
  62. inspect_ai/_view/www/src/components/NoContentsPanel.tsx +20 -0
  63. inspect_ai/_view/www/src/components/ProgressBar.module.css +5 -4
  64. inspect_ai/_view/www/src/components/ProgressBar.tsx +3 -2
  65. inspect_ai/_view/www/src/components/PulsingDots.module.css +81 -0
  66. inspect_ai/_view/www/src/components/PulsingDots.tsx +45 -0
  67. inspect_ai/_view/www/src/components/TabSet.tsx +4 -37
  68. inspect_ai/_view/www/src/components/ToolButton.tsx +3 -4
  69. inspect_ai/_view/www/src/index.tsx +26 -94
  70. inspect_ai/_view/www/src/logfile/remoteLogFile.ts +9 -1
  71. inspect_ai/_view/www/src/logfile/remoteZipFile.ts +30 -4
  72. inspect_ai/_view/www/src/metadata/RenderedContent.tsx +4 -6
  73. inspect_ai/_view/www/src/plan/DetailStep.module.css +4 -0
  74. inspect_ai/_view/www/src/plan/DetailStep.tsx +6 -3
  75. inspect_ai/_view/www/src/plan/ScorerDetailView.tsx +1 -1
  76. inspect_ai/_view/www/src/plan/SolverDetailView.module.css +2 -1
  77. inspect_ai/_view/www/src/samples/InlineSampleDisplay.module.css +9 -1
  78. inspect_ai/_view/www/src/samples/InlineSampleDisplay.tsx +74 -28
  79. inspect_ai/_view/www/src/samples/SampleDialog.tsx +58 -22
  80. inspect_ai/_view/www/src/samples/SampleDisplay.module.css +4 -0
  81. inspect_ai/_view/www/src/samples/SampleDisplay.tsx +135 -104
  82. inspect_ai/_view/www/src/samples/SampleSummaryView.module.css +10 -0
  83. inspect_ai/_view/www/src/samples/SampleSummaryView.tsx +83 -36
  84. inspect_ai/_view/www/src/samples/SamplesTools.tsx +35 -30
  85. inspect_ai/_view/www/src/samples/chat/ChatMessage.tsx +2 -1
  86. inspect_ai/_view/www/src/samples/chat/ChatMessageRenderer.tsx +1 -1
  87. inspect_ai/_view/www/src/samples/chat/ChatViewVirtualList.tsx +45 -53
  88. inspect_ai/_view/www/src/samples/chat/MessageContent.tsx +6 -1
  89. inspect_ai/_view/www/src/samples/chat/MessageContents.tsx +5 -0
  90. inspect_ai/_view/www/src/samples/chat/messages.ts +36 -0
  91. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.module.css +3 -0
  92. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.tsx +11 -1
  93. inspect_ai/_view/www/src/samples/chat/tools/ToolInput.tsx +22 -46
  94. inspect_ai/_view/www/src/samples/descriptor/samplesDescriptor.tsx +34 -20
  95. inspect_ai/_view/www/src/samples/descriptor/score/BooleanScoreDescriptor.module.css +3 -3
  96. inspect_ai/_view/www/src/samples/descriptor/score/BooleanScoreDescriptor.tsx +1 -1
  97. inspect_ai/_view/www/src/samples/descriptor/score/ObjectScoreDescriptor.module.css +4 -4
  98. inspect_ai/_view/www/src/samples/descriptor/score/ObjectScoreDescriptor.tsx +10 -10
  99. inspect_ai/_view/www/src/samples/descriptor/types.ts +6 -5
  100. inspect_ai/_view/www/src/samples/list/SampleFooter.module.css +22 -3
  101. inspect_ai/_view/www/src/samples/list/SampleFooter.tsx +27 -2
  102. inspect_ai/_view/www/src/samples/list/SampleList.tsx +122 -85
  103. inspect_ai/_view/www/src/samples/list/SampleRow.module.css +6 -0
  104. inspect_ai/_view/www/src/samples/list/SampleRow.tsx +28 -15
  105. inspect_ai/_view/www/src/samples/sample-tools/SelectScorer.tsx +29 -18
  106. inspect_ai/_view/www/src/samples/sample-tools/SortFilter.tsx +28 -28
  107. inspect_ai/_view/www/src/samples/sample-tools/sample-filter/SampleFilter.tsx +19 -9
  108. inspect_ai/_view/www/src/samples/sampleDataAdapter.ts +33 -0
  109. inspect_ai/_view/www/src/samples/sampleLimit.ts +2 -2
  110. inspect_ai/_view/www/src/samples/scores/SampleScores.tsx +12 -27
  111. inspect_ai/_view/www/src/samples/scores/SampleScoresGrid.module.css +38 -0
  112. inspect_ai/_view/www/src/samples/scores/SampleScoresGrid.tsx +118 -0
  113. inspect_ai/_view/www/src/samples/scores/{SampleScoreView.module.css → SampleScoresView.module.css} +10 -1
  114. inspect_ai/_view/www/src/samples/scores/SampleScoresView.tsx +78 -0
  115. inspect_ai/_view/www/src/samples/transcript/ErrorEventView.tsx +0 -13
  116. inspect_ai/_view/www/src/samples/transcript/InfoEventView.tsx +0 -13
  117. inspect_ai/_view/www/src/samples/transcript/InputEventView.tsx +0 -13
  118. inspect_ai/_view/www/src/samples/transcript/ModelEventView.module.css +4 -0
  119. inspect_ai/_view/www/src/samples/transcript/ModelEventView.tsx +10 -24
  120. inspect_ai/_view/www/src/samples/transcript/SampleInitEventView.tsx +0 -13
  121. inspect_ai/_view/www/src/samples/transcript/SampleLimitEventView.tsx +4 -22
  122. inspect_ai/_view/www/src/samples/transcript/SandboxEventView.tsx +15 -24
  123. inspect_ai/_view/www/src/samples/transcript/ScoreEventView.tsx +0 -13
  124. inspect_ai/_view/www/src/samples/transcript/StepEventView.tsx +6 -28
  125. inspect_ai/_view/www/src/samples/transcript/SubtaskEventView.tsx +24 -34
  126. inspect_ai/_view/www/src/samples/transcript/ToolEventView.module.css +4 -0
  127. inspect_ai/_view/www/src/samples/transcript/ToolEventView.tsx +33 -17
  128. inspect_ai/_view/www/src/samples/transcript/TranscriptView.tsx +197 -338
  129. inspect_ai/_view/www/src/samples/transcript/TranscriptVirtualListComponent.module.css +16 -0
  130. inspect_ai/_view/www/src/samples/transcript/TranscriptVirtualListComponent.tsx +44 -0
  131. inspect_ai/_view/www/src/samples/transcript/event/EventNav.tsx +7 -4
  132. inspect_ai/_view/www/src/samples/transcript/event/EventPanel.tsx +81 -60
  133. inspect_ai/_view/www/src/samples/transcript/event/EventProgressPanel.module.css +23 -0
  134. inspect_ai/_view/www/src/samples/transcript/event/EventProgressPanel.tsx +27 -0
  135. inspect_ai/_view/www/src/samples/transcript/state/StateEventRenderers.tsx +29 -1
  136. inspect_ai/_view/www/src/samples/transcript/state/StateEventView.tsx +102 -72
  137. inspect_ai/_view/www/src/scoring/utils.ts +87 -0
  138. inspect_ai/_view/www/src/state/appSlice.ts +244 -0
  139. inspect_ai/_view/www/src/state/hooks.ts +399 -0
  140. inspect_ai/_view/www/src/state/logPolling.ts +200 -0
  141. inspect_ai/_view/www/src/state/logSlice.ts +224 -0
  142. inspect_ai/_view/www/src/state/logsPolling.ts +118 -0
  143. inspect_ai/_view/www/src/state/logsSlice.ts +181 -0
  144. inspect_ai/_view/www/src/state/samplePolling.ts +314 -0
  145. inspect_ai/_view/www/src/state/sampleSlice.ts +140 -0
  146. inspect_ai/_view/www/src/state/sampleUtils.ts +21 -0
  147. inspect_ai/_view/www/src/state/scrolling.ts +206 -0
  148. inspect_ai/_view/www/src/state/store.ts +168 -0
  149. inspect_ai/_view/www/src/state/store_filter.ts +84 -0
  150. inspect_ai/_view/www/src/state/utils.ts +23 -0
  151. inspect_ai/_view/www/src/storage/index.ts +26 -0
  152. inspect_ai/_view/www/src/types/log.d.ts +36 -26
  153. inspect_ai/_view/www/src/types/markdown-it-katex.d.ts +21 -0
  154. inspect_ai/_view/www/src/types.ts +94 -32
  155. inspect_ai/_view/www/src/utils/attachments.ts +58 -23
  156. inspect_ai/_view/www/src/utils/json-worker.ts +79 -12
  157. inspect_ai/_view/www/src/utils/logger.ts +52 -0
  158. inspect_ai/_view/www/src/utils/polling.ts +100 -0
  159. inspect_ai/_view/www/src/utils/react.ts +30 -0
  160. inspect_ai/_view/www/src/utils/vscode.ts +1 -1
  161. inspect_ai/_view/www/src/workspace/WorkSpace.tsx +184 -217
  162. inspect_ai/_view/www/src/workspace/WorkSpaceView.tsx +11 -53
  163. inspect_ai/_view/www/src/workspace/navbar/Navbar.tsx +8 -18
  164. inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.module.css +1 -0
  165. inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.tsx +40 -22
  166. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.module.css +16 -1
  167. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.tsx +159 -103
  168. inspect_ai/_view/www/src/workspace/navbar/RunningStatusPanel.module.css +32 -0
  169. inspect_ai/_view/www/src/workspace/navbar/RunningStatusPanel.tsx +32 -0
  170. inspect_ai/_view/www/src/workspace/navbar/ScoreGrid.module.css +35 -0
  171. inspect_ai/_view/www/src/workspace/navbar/ScoreGrid.tsx +117 -0
  172. inspect_ai/_view/www/src/workspace/navbar/SecondaryBar.tsx +12 -14
  173. inspect_ai/_view/www/src/workspace/navbar/StatusPanel.tsx +6 -2
  174. inspect_ai/_view/www/src/workspace/sidebar/LogDirectoryTitleView.tsx +4 -4
  175. inspect_ai/_view/www/src/workspace/sidebar/Sidebar.module.css +3 -2
  176. inspect_ai/_view/www/src/workspace/sidebar/Sidebar.tsx +28 -13
  177. inspect_ai/_view/www/src/workspace/tabs/InfoTab.tsx +5 -10
  178. inspect_ai/_view/www/src/workspace/tabs/JsonTab.tsx +4 -4
  179. inspect_ai/_view/www/src/workspace/tabs/RunningNoSamples.module.css +22 -0
  180. inspect_ai/_view/www/src/workspace/tabs/RunningNoSamples.tsx +19 -0
  181. inspect_ai/_view/www/src/workspace/tabs/SamplesTab.tsx +128 -115
  182. inspect_ai/_view/www/src/workspace/tabs/grouping.ts +37 -5
  183. inspect_ai/_view/www/src/workspace/tabs/types.ts +4 -0
  184. inspect_ai/_view/www/src/workspace/types.ts +4 -3
  185. inspect_ai/_view/www/src/workspace/utils.ts +4 -4
  186. inspect_ai/_view/www/vite.config.js +6 -0
  187. inspect_ai/_view/www/yarn.lock +464 -355
  188. inspect_ai/agent/__init__.py +36 -0
  189. inspect_ai/agent/_agent.py +268 -0
  190. inspect_ai/agent/_as_solver.py +72 -0
  191. inspect_ai/agent/_as_tool.py +122 -0
  192. inspect_ai/{solver → agent}/_bridge/bridge.py +23 -37
  193. inspect_ai/{solver → agent}/_bridge/patch.py +9 -8
  194. inspect_ai/agent/_filter.py +46 -0
  195. inspect_ai/agent/_handoff.py +93 -0
  196. inspect_ai/{solver/_human_agent → agent/_human}/agent.py +11 -12
  197. inspect_ai/{solver/_human_agent → agent/_human}/commands/__init__.py +2 -3
  198. inspect_ai/{solver/_human_agent → agent/_human}/commands/clock.py +3 -1
  199. inspect_ai/{solver/_human_agent → agent/_human}/commands/score.py +5 -5
  200. inspect_ai/{solver/_human_agent → agent/_human}/install.py +6 -3
  201. inspect_ai/{solver/_human_agent → agent/_human}/service.py +7 -3
  202. inspect_ai/{solver/_human_agent → agent/_human}/state.py +5 -5
  203. inspect_ai/agent/_react.py +241 -0
  204. inspect_ai/agent/_run.py +36 -0
  205. inspect_ai/agent/_types.py +81 -0
  206. inspect_ai/log/_condense.py +26 -0
  207. inspect_ai/log/_log.py +17 -5
  208. inspect_ai/log/_recorders/buffer/__init__.py +14 -0
  209. inspect_ai/log/_recorders/buffer/buffer.py +30 -0
  210. inspect_ai/log/_recorders/buffer/database.py +685 -0
  211. inspect_ai/log/_recorders/buffer/filestore.py +259 -0
  212. inspect_ai/log/_recorders/buffer/types.py +84 -0
  213. inspect_ai/log/_recorders/eval.py +2 -11
  214. inspect_ai/log/_recorders/types.py +30 -0
  215. inspect_ai/log/_transcript.py +32 -2
  216. inspect_ai/model/__init__.py +7 -1
  217. inspect_ai/model/_call_tools.py +257 -52
  218. inspect_ai/model/_chat_message.py +7 -4
  219. inspect_ai/model/_conversation.py +13 -62
  220. inspect_ai/model/_display.py +85 -0
  221. inspect_ai/model/_generate_config.py +2 -2
  222. inspect_ai/model/_model.py +114 -14
  223. inspect_ai/model/_model_output.py +14 -9
  224. inspect_ai/model/_openai.py +16 -4
  225. inspect_ai/model/_openai_computer_use.py +162 -0
  226. inspect_ai/model/_openai_responses.py +319 -165
  227. inspect_ai/model/_providers/anthropic.py +20 -21
  228. inspect_ai/model/_providers/azureai.py +24 -13
  229. inspect_ai/model/_providers/bedrock.py +1 -7
  230. inspect_ai/model/_providers/cloudflare.py +3 -3
  231. inspect_ai/model/_providers/goodfire.py +2 -6
  232. inspect_ai/model/_providers/google.py +11 -10
  233. inspect_ai/model/_providers/groq.py +6 -3
  234. inspect_ai/model/_providers/hf.py +7 -3
  235. inspect_ai/model/_providers/mistral.py +7 -10
  236. inspect_ai/model/_providers/openai.py +47 -17
  237. inspect_ai/model/_providers/openai_o1.py +11 -4
  238. inspect_ai/model/_providers/openai_responses.py +12 -14
  239. inspect_ai/model/_providers/providers.py +2 -2
  240. inspect_ai/model/_providers/together.py +12 -2
  241. inspect_ai/model/_providers/util/chatapi.py +7 -2
  242. inspect_ai/model/_providers/util/hf_handler.py +4 -2
  243. inspect_ai/model/_providers/util/llama31.py +4 -2
  244. inspect_ai/model/_providers/vertex.py +11 -9
  245. inspect_ai/model/_providers/vllm.py +4 -4
  246. inspect_ai/scorer/__init__.py +2 -0
  247. inspect_ai/scorer/_metrics/__init__.py +2 -0
  248. inspect_ai/scorer/_metrics/grouped.py +84 -0
  249. inspect_ai/scorer/_score.py +26 -6
  250. inspect_ai/solver/__init__.py +2 -2
  251. inspect_ai/solver/_basic_agent.py +22 -9
  252. inspect_ai/solver/_bridge.py +31 -0
  253. inspect_ai/solver/_chain.py +20 -12
  254. inspect_ai/solver/_fork.py +5 -1
  255. inspect_ai/solver/_human_agent.py +52 -0
  256. inspect_ai/solver/_prompt.py +3 -1
  257. inspect_ai/solver/_run.py +59 -0
  258. inspect_ai/solver/_solver.py +14 -4
  259. inspect_ai/solver/_task_state.py +5 -3
  260. inspect_ai/tool/_tool_call.py +15 -8
  261. inspect_ai/tool/_tool_def.py +17 -12
  262. inspect_ai/tool/_tool_support_helpers.py +4 -4
  263. inspect_ai/tool/_tool_with.py +14 -11
  264. inspect_ai/tool/_tools/_bash_session.py +11 -2
  265. inspect_ai/tool/_tools/_computer/_common.py +18 -2
  266. inspect_ai/tool/_tools/_computer/_computer.py +18 -2
  267. inspect_ai/tool/_tools/_computer/_resources/tool/_constants.py +2 -0
  268. inspect_ai/tool/_tools/_computer/_resources/tool/_x11_client.py +17 -0
  269. inspect_ai/tool/_tools/_think.py +1 -1
  270. inspect_ai/tool/_tools/_web_browser/_web_browser.py +103 -62
  271. inspect_ai/util/__init__.py +2 -0
  272. inspect_ai/util/_anyio.py +27 -0
  273. inspect_ai/util/_sandbox/__init__.py +2 -1
  274. inspect_ai/util/_sandbox/context.py +32 -7
  275. inspect_ai/util/_sandbox/docker/cleanup.py +4 -0
  276. inspect_ai/util/_sandbox/docker/compose.py +2 -2
  277. inspect_ai/util/_sandbox/docker/docker.py +12 -1
  278. inspect_ai/util/_store_model.py +30 -7
  279. inspect_ai/util/_subprocess.py +13 -3
  280. inspect_ai/util/_subtask.py +1 -0
  281. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/METADATA +1 -1
  282. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/RECORD +295 -229
  283. inspect_ai/_view/www/src/samples/scores/SampleScoreView.tsx +0 -169
  284. inspect_ai/_view/www/src/samples/transcript/SampleTranscript.tsx +0 -22
  285. /inspect_ai/{solver → agent}/_bridge/__init__.py +0 -0
  286. /inspect_ai/{solver/_human_agent → agent/_human}/__init__.py +0 -0
  287. /inspect_ai/{solver/_human_agent → agent/_human}/commands/command.py +0 -0
  288. /inspect_ai/{solver/_human_agent → agent/_human}/commands/instructions.py +0 -0
  289. /inspect_ai/{solver/_human_agent → agent/_human}/commands/note.py +0 -0
  290. /inspect_ai/{solver/_human_agent → agent/_human}/commands/status.py +0 -0
  291. /inspect_ai/{solver/_human_agent → agent/_human}/commands/submit.py +0 -0
  292. /inspect_ai/{solver/_human_agent → agent/_human}/panel.py +0 -0
  293. /inspect_ai/{solver/_human_agent → agent/_human}/view.py +0 -0
  294. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/WHEEL +0 -0
  295. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/entry_points.txt +0 -0
  296. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/licenses/LICENSE +0 -0
  297. {inspect_ai-0.3.81.dist-info → inspect_ai-0.3.83.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,81 @@
1
+ /* PulsingDots.module.css */
2
+ .container {
3
+ display: inline-flex;
4
+ flex-direction: column;
5
+ align-items: center;
6
+ }
7
+
8
+ .dotsContainer {
9
+ padding-top: 8px;
10
+ display: flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ }
14
+
15
+ .small .dotsContainer {
16
+ column-gap: 3px;
17
+ }
18
+
19
+ .medium .dotsContainer {
20
+ column-gap: 5px;
21
+ padding-bottom: 2px;
22
+ }
23
+
24
+ .large .dotsContainer {
25
+ column-gap: 6px;
26
+ padding-bottom: 3px;
27
+ padding-left: 2px;
28
+ }
29
+
30
+ .dot {
31
+ border-radius: 50%;
32
+ display: inline-block;
33
+ animation: pulse 1.5s ease-in-out infinite;
34
+ }
35
+
36
+ .subtle {
37
+ background-color: var(--bs-primary-bg-subtle);
38
+ }
39
+
40
+ .primary {
41
+ background-color: var(--bs-primary);
42
+ }
43
+
44
+ .small .dot {
45
+ width: 4px;
46
+ height: 4px;
47
+ }
48
+
49
+ .medium .dot {
50
+ width: 8px;
51
+ height: 8px;
52
+ }
53
+
54
+ .large .dot {
55
+ width: 12px;
56
+ height: 12px;
57
+ }
58
+
59
+ .visuallyHidden {
60
+ position: absolute;
61
+ width: 1px;
62
+ height: 1px;
63
+ padding: 0;
64
+ margin: -1px;
65
+ overflow: hidden;
66
+ clip: rect(0, 0, 0, 0);
67
+ white-space: nowrap;
68
+ border-width: 0;
69
+ }
70
+
71
+ @keyframes pulse {
72
+ 0%,
73
+ 100% {
74
+ transform: scale(1);
75
+ opacity: 0.3;
76
+ }
77
+ 50% {
78
+ transform: scale(1.2);
79
+ opacity: 1;
80
+ }
81
+ }
@@ -0,0 +1,45 @@
1
+ import clsx from "clsx";
2
+ import { FC } from "react";
3
+ import styles from "./PulsingDots.module.css";
4
+
5
+ interface PulsingDotsProps {
6
+ text?: string;
7
+ dotsCount?: number;
8
+ subtle?: boolean;
9
+ size?: "small" | "medium" | "large";
10
+ }
11
+
12
+ export const PulsingDots: FC<PulsingDotsProps> = ({
13
+ text = "Loading...",
14
+ dotsCount = 3,
15
+ subtle = true,
16
+ size = "small",
17
+ }) => {
18
+ return (
19
+ <div
20
+ className={clsx(
21
+ styles.container,
22
+ size === "small"
23
+ ? styles.small
24
+ : size === "medium"
25
+ ? styles.medium
26
+ : styles.large,
27
+ )}
28
+ role="status"
29
+ >
30
+ <div className={styles.dotsContainer}>
31
+ {[...Array(dotsCount)].map((_, index) => (
32
+ <div
33
+ key={index}
34
+ className={clsx(
35
+ styles.dot,
36
+ subtle ? styles.subtle : styles.primary,
37
+ )}
38
+ style={{ animationDelay: `${index * 0.15}s` }}
39
+ />
40
+ ))}
41
+ </div>
42
+ <span className={styles.visuallyHidden}>{text}</span>
43
+ </div>
44
+ );
45
+ };
@@ -9,11 +9,9 @@ import {
9
9
  ReactElement,
10
10
  ReactNode,
11
11
  RefObject,
12
- UIEvent,
13
- useCallback,
14
- useEffect,
15
12
  useRef,
16
13
  } from "react";
14
+ import { useStatefulScrollPosition } from "../state/scrolling";
17
15
  import moduleStyles from "./TabSet.module.css";
18
16
 
19
17
  interface TabSetProps {
@@ -36,8 +34,6 @@ interface TabPanelProps {
36
34
  scrollable?: boolean;
37
35
  scrollRef?: RefObject<HTMLDivElement | null>;
38
36
  className?: string | string[];
39
- scrollPosition?: number;
40
- setScrollPosition?: (position: number) => void;
41
37
  children?: ReactNode;
42
38
  title: string;
43
39
  icon?: string;
@@ -107,7 +103,7 @@ const Tab: FC<{
107
103
  role="tab"
108
104
  aria-controls={tabContentsId}
109
105
  aria-selected={isActive}
110
- onClick={(e) => tab.props.onSelected(e)}
106
+ onClick={tab.props.onSelected}
111
107
  >
112
108
  {tab.props.icon && (
113
109
  <i className={clsx(tab.props.icon, moduleStyles.tabIcon)} />
@@ -139,42 +135,14 @@ export const TabPanel: FC<TabPanelProps> = ({
139
135
  scrollable = true,
140
136
  scrollRef,
141
137
  className,
142
- scrollPosition,
143
- setScrollPosition,
144
138
  children,
145
139
  }) => {
146
140
  const tabContentsId = computeTabContentsId(id);
147
141
  const panelRef = useRef<HTMLDivElement>(null);
148
142
  const tabContentsRef = scrollRef || panelRef;
149
143
 
150
- useEffect(() => {
151
- if (!selected || scrollPosition === undefined || !tabContentsRef.current)
152
- return;
153
-
154
- const observer = new MutationObserver(() => {
155
- if (tabContentsRef.current) {
156
- tabContentsRef.current.scrollTop = scrollPosition;
157
- }
158
- observer.disconnect(); // Stop observing after first content load
159
- });
160
-
161
- observer.observe(tabContentsRef.current, {
162
- childList: true,
163
- subtree: true,
164
- });
165
-
166
- return () => observer.disconnect();
167
- }, []);
168
-
169
- // Handle scrolling
170
- const onScroll = useCallback(
171
- (e: UIEvent<HTMLDivElement>) => {
172
- if (setScrollPosition) {
173
- setScrollPosition(e.currentTarget.scrollTop);
174
- }
175
- },
176
- [setScrollPosition],
177
- );
144
+ // Attach a scroll listener to this ref to track scrolling
145
+ useStatefulScrollPosition(tabContentsRef, tabContentsId, 1000, scrollable);
178
146
 
179
147
  return (
180
148
  <div
@@ -188,7 +156,6 @@ export const TabPanel: FC<TabPanelProps> = ({
188
156
  scrollable && moduleStyles.scrollable,
189
157
  )}
190
158
  style={style}
191
- onScroll={onScroll}
192
159
  >
193
160
  {children}
194
161
  </div>
@@ -1,14 +1,13 @@
1
- import React, { ReactNode } from "react";
1
+ import { ButtonHTMLAttributes, forwardRef, ReactNode } from "react";
2
2
  import "./ToolButton.css";
3
3
 
4
- interface ToolButtonProps
5
- extends React.ButtonHTMLAttributes<HTMLButtonElement> {
4
+ interface ToolButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
6
5
  label: string | ReactNode;
7
6
  classes?: string;
8
7
  icon?: string;
9
8
  }
10
9
 
11
- export const ToolButton = React.forwardRef<HTMLButtonElement, ToolButtonProps>(
10
+ export const ToolButton = forwardRef<HTMLButtonElement, ToolButtonProps>(
12
11
  ({ label, classes = "", icon, className, ...rest }, ref) => {
13
12
  // Combine class names, ensuring default classes are applied first
14
13
  const combinedClasses =
@@ -1,21 +1,29 @@
1
1
  import { createRoot } from "react-dom/client";
2
- import { App } from "./App";
3
2
  import api from "./api/index";
4
- import { ApplicationState, Capabilities } from "./types";
5
- import { throttle } from "./utils/sync";
3
+ import { Capabilities } from "./api/types";
4
+ import { App } from "./App";
5
+ import { AppErrorBoundary } from "./AppErrorBoundary";
6
+ import { initializeStore } from "./state/store";
7
+ import storage from "./storage";
6
8
  import { getVscodeApi } from "./utils/vscode";
7
9
 
8
- // Read any state from the page itself
10
+ // Resolve the api
11
+ const applicationApi = api;
12
+ const applicationStorage = storage;
13
+
14
+ // Application capabilities
9
15
  const vscode = getVscodeApi();
10
- let initialState = undefined;
11
16
  let capabilities: Capabilities = {
12
17
  downloadFiles: true,
13
18
  webWorkers: true,
19
+ streamSamples: !!applicationApi.get_log_pending_samples,
20
+ streamSampleData: !!applicationApi.get_log_sample_data,
21
+ nativeFind: !vscode,
14
22
  };
15
- if (vscode) {
16
- initialState = filterState(vscode.getState() as ApplicationState);
17
23
 
18
- // Determine the capabilities
24
+ // Initial state / storage
25
+ if (vscode) {
26
+ // Adjust capabilities
19
27
  const extensionVersionEl = document.querySelector(
20
28
  'meta[name="inspect-extension:version"]',
21
29
  );
@@ -24,10 +32,15 @@ if (vscode) {
24
32
  : undefined;
25
33
 
26
34
  if (!extensionVersion) {
27
- capabilities = { downloadFiles: false, webWorkers: false };
35
+ capabilities.downloadFiles = false;
36
+ capabilities.webWorkers = false;
28
37
  }
29
38
  }
30
39
 
40
+ // Inititialize the application store
41
+ initializeStore(applicationApi, capabilities, applicationStorage);
42
+
43
+ // Find the root element and render into it
31
44
  const containerId = "app";
32
45
  const container = document.getElementById(containerId);
33
46
  if (!container) {
@@ -37,91 +50,10 @@ if (!container) {
37
50
  );
38
51
  }
39
52
 
53
+ // Render into the root
40
54
  const root = createRoot(container as HTMLElement);
41
55
  root.render(
42
- <App
43
- api={api}
44
- applicationState={initialState}
45
- saveApplicationState={throttle((state) => {
46
- const vscode = getVscodeApi();
47
- if (vscode) {
48
- vscode.setState(filterState(state));
49
- }
50
- }, 1000)}
51
- capabilities={capabilities}
52
- pollForLogs={false}
53
- />,
56
+ <AppErrorBoundary>
57
+ <App api={applicationApi} />
58
+ </AppErrorBoundary>,
54
59
  );
55
-
56
- function filterState(state: ApplicationState) {
57
- if (!state) {
58
- return state;
59
- }
60
-
61
- // When saving state, we can't store vast amounts of data (like a large sample)
62
- const filters = [filterLargeSample, filterLargeSelectedLog];
63
- return filters.reduce(
64
- (filteredState, filter) => filter(filteredState),
65
- state,
66
- );
67
- }
68
-
69
- // Filters the selected Sample if it is large
70
- function filterLargeSample(state: ApplicationState) {
71
- if (!state || !state.selectedSample) {
72
- return state;
73
- }
74
-
75
- const estimatedTotalSize = estimateSize(state.selectedSample.messages);
76
- if (estimatedTotalSize > 400000) {
77
- const { selectedSample, ...filteredState } = state;
78
- return filteredState;
79
- } else {
80
- return state;
81
- }
82
- }
83
-
84
- // Filters the selectedlog if it is too large
85
- function filterLargeSelectedLog(state: ApplicationState) {
86
- if (!state || !state.selectedLog?.contents) {
87
- return state;
88
- }
89
-
90
- const estimatedSize = estimateSize(
91
- state.selectedLog.contents.sampleSummaries,
92
- );
93
- if (estimatedSize > 400000) {
94
- const { selectedLog, ...filteredState } = state;
95
- return filteredState;
96
- } else {
97
- return state;
98
- }
99
- }
100
-
101
- function estimateSize(list: unknown[], frequency = 0.2) {
102
- if (!list || list.length === 0) {
103
- return 0;
104
- }
105
-
106
- // Total number of samples
107
- const sampleSize = Math.ceil(list.length * frequency);
108
-
109
- // Get a proper random sample without duplicates
110
- const messageIndices = new Set<number>();
111
- while (
112
- messageIndices.size < sampleSize &&
113
- messageIndices.size < list.length
114
- ) {
115
- const randomIndex = Math.floor(Math.random() * list.length);
116
- messageIndices.add(randomIndex);
117
- }
118
-
119
- // Calculate size from sampled messages
120
- const totalSize = Array.from(messageIndices).reduce((size, index) => {
121
- return size + JSON.stringify(list[index]).length;
122
- }, 0);
123
-
124
- // Estimate total size based on sample
125
- const estimatedTotalSize = (totalSize / sampleSize) * list.length;
126
- return estimatedTotalSize;
127
- }
@@ -17,6 +17,14 @@ interface SampleEntry {
17
17
  epoch: number;
18
18
  }
19
19
 
20
+ export class SampleNotFoundError extends Error {
21
+ constructor(message?: string) {
22
+ super(message || "Sample not found");
23
+ this.name = "SampleNotFoundError";
24
+
25
+ Object.setPrototypeOf(this, SampleNotFoundError.prototype);
26
+ }
27
+ }
20
28
  export interface RemoteLogFile {
21
29
  readHeader: () => Promise<EvalHeader>;
22
30
  readLogSummary: () => Promise<EvalSummary>;
@@ -101,7 +109,7 @@ export const openRemoteLogFile = async (
101
109
  if (remoteZipFile.centralDirectory.has(sampleFile)) {
102
110
  return (await readJSONFile(sampleFile, MAX_BYTES)) as EvalSample;
103
111
  } else {
104
- throw new Error(
112
+ throw new SampleNotFoundError(
105
113
  `Unable to read sample file ${sampleFile} - it is not present in the manifest.`,
106
114
  );
107
115
  }
@@ -67,7 +67,15 @@ export const openRemoteZipFile = async (
67
67
 
68
68
  // Check signature to make sure we found the EOCD record
69
69
  if (eocdrView.getUint32(0, true) !== 0x06054b50) {
70
- throw new Error("End of central directory record not found");
70
+ if (eocdrBuffer.length !== 22) {
71
+ // The range request seems like it was ignored because more bytes than
72
+ // were requested were returned.
73
+ throw new Error(
74
+ "Unexpected central directory size - does the HTTP server serving this file support HTTP range requests?",
75
+ );
76
+ } else {
77
+ throw new Error("End of central directory record not found");
78
+ }
71
79
  }
72
80
 
73
81
  let centralDirOffset = eocdrView.getUint32(16, true);
@@ -180,9 +188,27 @@ export const openRemoteZipFile = async (
180
188
  };
181
189
 
182
190
  export const fetchSize = async (url: string): Promise<number> => {
183
- const response = await fetch(`${url}`, { method: "HEAD" });
184
- const contentLength = Number(response.headers.get("Content-Length"));
185
- return contentLength;
191
+ // First try HEAD request to get Content-Length
192
+ const headResponse = await fetch(`${url}`, { method: "HEAD" });
193
+ const contentLength = headResponse.headers.get("Content-Length");
194
+
195
+ if (contentLength !== null) {
196
+ return Number(contentLength);
197
+ }
198
+
199
+ // If Content-Length is not present, use a GET with an 1 byte range request:
200
+ const getResponse = await fetch(`${url}`, {
201
+ method: "GET",
202
+ headers: { Range: "bytes=0-0" },
203
+ });
204
+ const contentRange = getResponse.headers.get("Content-Range");
205
+ if (contentRange !== null) {
206
+ const rangeMatch = contentRange.match(/bytes (\d+)-(\d+)\/(\d+)/);
207
+ if (rangeMatch !== null) {
208
+ return Number(rangeMatch[3]);
209
+ }
210
+ }
211
+ throw new Error("Could not determine content length");
186
212
  };
187
213
 
188
214
  /**
@@ -7,7 +7,7 @@ import { formatNumber } from "../utils/format";
7
7
  import { MetaDataView } from "./MetaDataView";
8
8
 
9
9
  import clsx from "clsx";
10
- import React, { Fragment, JSX } from "react";
10
+ import { FC, Fragment, isValidElement, JSX, ReactNode } from "react";
11
11
  import JSONPanel from "../components/JsonPanel";
12
12
  import { isJson } from "../utils/json";
13
13
  import styles from "./RenderedContent.module.css";
@@ -21,7 +21,7 @@ interface RenderedContentProps {
21
21
  /**
22
22
  * Renders content based on its type using registered content renderers.
23
23
  */
24
- export const RenderedContent: React.FC<RenderedContentProps> = ({
24
+ export const RenderedContent: FC<RenderedContentProps> = ({
25
25
  id,
26
26
  entry,
27
27
  }): JSX.Element => {
@@ -43,8 +43,7 @@ export const RenderedContent: React.FC<RenderedContentProps> = ({
43
43
 
44
44
  if (renderer) {
45
45
  const { rendered } = renderer.render(id, entry);
46
- // Check if rendered is already a valid ReactNode (JSX.Element)
47
- if (rendered !== undefined && React.isValidElement(rendered)) {
46
+ if (rendered !== undefined && isValidElement(rendered)) {
48
47
  return rendered;
49
48
  }
50
49
  }
@@ -185,7 +184,7 @@ const contentRenderers: Record<string, ContentRenderer> = {
185
184
  return typeof entry.value === "object" && entry.name === "web_search";
186
185
  },
187
186
  render: (_id, entry) => {
188
- const results: React.ReactNode[] = [];
187
+ const results: ReactNode[] = [];
189
188
  results.push(
190
189
  <div className={styles.query}>
191
190
  <i className={ApplicationIcons.search}></i> {entry.value.query}
@@ -253,7 +252,6 @@ const contentRenderers: Record<string, ContentRenderer> = {
253
252
  return typeof entry.value === "object";
254
253
  },
255
254
  render: (id, entry) => {
256
- // Generate a json preview
257
255
  return {
258
256
  rendered: (
259
257
  <MetaDataView
@@ -7,3 +7,7 @@
7
7
  margin-top: 0.2rem;
8
8
  margin-bottom: 0.3rem;
9
9
  }
10
+
11
+ .metadata {
12
+ margin-bottom: 0.75em;
13
+ }
@@ -1,7 +1,7 @@
1
1
  import clsx from "clsx";
2
2
  import { FC } from "react";
3
- import { MetaDataView } from "../metadata/MetaDataView";
4
- import styles from "./DatasetDetailView.module.css";
3
+ import { MetaDataGrid } from "../metadata/MetaDataGrid";
4
+ import styles from "./DetailStep.module.css";
5
5
 
6
6
  interface DetailStepProps {
7
7
  icon?: string;
@@ -22,7 +22,10 @@ export const DetailStep: FC<DetailStepProps> = ({
22
22
  {iconHtml} {name}
23
23
  <div className={styles.container}>
24
24
  {params ? (
25
- <MetaDataView entries={params} className={"text-size-small"} />
25
+ <MetaDataGrid
26
+ entries={params}
27
+ className={clsx("text-size-small", styles.metadata)}
28
+ />
26
29
  ) : (
27
30
  ""
28
31
  )}
@@ -17,7 +17,7 @@ export const ScorerDetailView: FC<ScorerDetailViewProps> = ({
17
17
  }) => {
18
18
  // Merge scores into params
19
19
  if (scores.length > 1) {
20
- params["scores"] = scores;
20
+ params = { ...params, ["scores"]: scores };
21
21
  }
22
22
 
23
23
  return (
@@ -1,6 +1,7 @@
1
1
  .container {
2
2
  display: flex;
3
- flex-direction: columns;
3
+ flex-direction: row;
4
+ flex-wrap: wrap;
4
5
  }
5
6
 
6
7
  .item {
@@ -1,8 +1,16 @@
1
1
  .container {
2
- flex-direction: row;
3
2
  width: 100%;
3
+ display: flex;
4
+ flex-direction: column;
4
5
  }
5
6
 
6
7
  .body {
7
8
  margin: 1em 1em 1em 1em;
8
9
  }
10
+
11
+ .scroller {
12
+ overflow-y: auto;
13
+ width: 100%;
14
+ height: 100%;
15
+ position: relative;
16
+ }
@@ -1,21 +1,17 @@
1
- import { FC, RefObject } from "react";
1
+ import { FC, useEffect, useRef } from "react";
2
2
  import { ErrorPanel } from "../components/ErrorPanel";
3
- import { ProgressBar } from "../components/ProgressBar";
4
- import { EvalSample } from "../types/log";
5
3
  import { SampleDisplay } from "./SampleDisplay";
6
- import { SamplesDescriptor } from "./descriptor/samplesDescriptor";
7
4
 
5
+ import clsx from "clsx";
6
+ import { ProgressBar } from "../components/ProgressBar";
7
+ import { useLogSelection, usePrevious, useSampleData } from "../state/hooks";
8
+ import { useStore } from "../state/store";
8
9
  import styles from "./InlineSampleDisplay.module.css";
9
10
 
10
11
  interface InlineSampleDisplayProps {
11
12
  id: string;
12
- sampleStatus: string;
13
- sampleError?: Error;
14
- sample?: EvalSample;
15
- sampleDescriptor: SamplesDescriptor;
16
13
  selectedTab?: string;
17
14
  setSelectedTab: (tab: string) => void;
18
- scrollRef: RefObject<HTMLDivElement | null>;
19
15
  }
20
16
 
21
17
  /**
@@ -23,30 +19,80 @@ interface InlineSampleDisplayProps {
23
19
  */
24
20
  export const InlineSampleDisplay: FC<InlineSampleDisplayProps> = ({
25
21
  id,
26
- sample,
27
- sampleStatus,
28
- sampleError,
29
- sampleDescriptor,
30
22
  selectedTab,
31
23
  setSelectedTab,
32
- scrollRef,
33
24
  }) => {
25
+ // Sample hooks
26
+ const sampleData = useSampleData();
27
+ const loadSample = useStore((state) => state.sampleActions.loadSample);
28
+ const pollSample = useStore((state) => state.sampleActions.pollSample);
29
+ const logSelection = useLogSelection();
30
+
31
+ useEffect(() => {
32
+ if (sampleData.running && logSelection.logFile && logSelection.sample) {
33
+ pollSample(logSelection.logFile, logSelection.sample);
34
+ }
35
+ }, []);
36
+
37
+ // Sample Loading
38
+ const prevCompleted = usePrevious(
39
+ logSelection.sample?.completed !== undefined
40
+ ? logSelection.sample.completed
41
+ : true,
42
+ );
43
+ const prevLogFile = usePrevious<string | undefined>(logSelection.logFile);
44
+ useEffect(() => {
45
+ if (logSelection.logFile && logSelection.sample) {
46
+ const currentSampleCompleted =
47
+ logSelection.sample?.completed !== undefined
48
+ ? logSelection.sample.completed
49
+ : true;
50
+
51
+ if (
52
+ prevLogFile !== logSelection.logFile ||
53
+ sampleData.sample?.id !== logSelection.sample.id ||
54
+ sampleData.sample?.epoch !== logSelection.sample.epoch ||
55
+ currentSampleCompleted !== prevCompleted
56
+ ) {
57
+ loadSample(logSelection.logFile, logSelection.sample);
58
+ }
59
+ }
60
+ }, [
61
+ logSelection.logFile,
62
+ logSelection.sample?.id,
63
+ logSelection.sample?.epoch,
64
+ logSelection.sample?.completed,
65
+ sampleData.sample?.id,
66
+ sampleData.sample?.epoch,
67
+ ]);
68
+
69
+ // Scroll ref
70
+ const scrollRef = useRef<HTMLDivElement>(null);
34
71
  return (
35
72
  <div className={styles.container}>
36
- <ProgressBar animating={sampleStatus === "loading"} />
37
- <div className={styles.body}>
38
- {sampleError ? (
39
- <ErrorPanel title="Unable to load sample" error={sampleError} />
40
- ) : (
41
- <SampleDisplay
42
- id={id}
43
- sample={sample}
44
- sampleDescriptor={sampleDescriptor}
45
- selectedTab={selectedTab}
46
- setSelectedTab={setSelectedTab}
47
- scrollRef={scrollRef}
48
- />
49
- )}
73
+ <ProgressBar
74
+ animating={
75
+ sampleData.status === "loading" || sampleData.status === "streaming"
76
+ }
77
+ />
78
+ <div className={clsx(styles.scroller)} ref={scrollRef}>
79
+ <div className={styles.body}>
80
+ {sampleData.error ? (
81
+ <ErrorPanel
82
+ title="Unable to load sample"
83
+ error={sampleData.error}
84
+ />
85
+ ) : (
86
+ <SampleDisplay
87
+ id={id}
88
+ sample={sampleData.sample}
89
+ runningEvents={sampleData.running}
90
+ selectedTab={selectedTab}
91
+ setSelectedTab={setSelectedTab}
92
+ scrollRef={scrollRef}
93
+ />
94
+ )}
95
+ </div>
50
96
  </div>
51
97
  </div>
52
98
  );