inspect-ai 0.3.90__py3-none-any.whl → 0.3.91__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 (370) hide show
  1. inspect_ai/_cli/common.py +13 -0
  2. inspect_ai/_cli/eval.py +40 -0
  3. inspect_ai/_display/textual/widgets/samples.py +49 -4
  4. inspect_ai/_display/textual/widgets/vscode.py +4 -2
  5. inspect_ai/_eval/eval.py +41 -28
  6. inspect_ai/_eval/evalset.py +4 -0
  7. inspect_ai/_eval/loader.py +4 -5
  8. inspect_ai/_eval/registry.py +1 -1
  9. inspect_ai/_eval/run.py +6 -3
  10. inspect_ai/_eval/task/log.py +6 -0
  11. inspect_ai/_eval/task/run.py +108 -41
  12. inspect_ai/_eval/task/sandbox.py +19 -5
  13. inspect_ai/_util/_async.py +1 -1
  14. inspect_ai/_util/constants.py +1 -0
  15. inspect_ai/_util/environ.py +32 -0
  16. inspect_ai/_util/file.py +8 -1
  17. inspect_ai/_util/httpx.py +105 -22
  18. inspect_ai/_util/registry.py +83 -9
  19. inspect_ai/_util/text.py +81 -17
  20. inspect_ai/_util/transcript.py +9 -6
  21. inspect_ai/_util/vscode.py +7 -2
  22. inspect_ai/_view/schema.py +1 -1
  23. inspect_ai/_view/www/babel.config.js +11 -0
  24. inspect_ai/_view/www/dist/assets/index.css +3640 -3563
  25. inspect_ai/_view/www/dist/assets/index.js +59204 -52519
  26. inspect_ai/_view/www/eslint.config.mjs +10 -1
  27. inspect_ai/_view/www/jest.config.mjs +21 -0
  28. inspect_ai/_view/www/log-schema.json +111 -2
  29. inspect_ai/_view/www/package.json +19 -5
  30. inspect_ai/_view/www/src/{types → @types}/log.d.ts +95 -32
  31. inspect_ai/_view/www/{App.css → src/app/App.css} +22 -14
  32. inspect_ai/_view/www/src/app/App.tsx +168 -0
  33. inspect_ai/_view/www/src/{AppErrorBoundary.tsx → app/AppErrorBoundary.tsx} +1 -1
  34. inspect_ai/_view/www/src/{appearance → app/appearance}/icons.ts +1 -0
  35. inspect_ai/_view/www/src/{metadata → app/content}/RenderedContent.tsx +5 -5
  36. inspect_ai/_view/www/src/{workspace/WorkSpaceView.tsx → app/log-view/LogView.tsx} +59 -40
  37. inspect_ai/_view/www/src/app/log-view/LogViewContainer.tsx +159 -0
  38. inspect_ai/_view/www/src/app/log-view/LogViewLayout.tsx +109 -0
  39. inspect_ai/_view/www/src/{workspace → app/log-view}/error/TaskErrorPanel.tsx +3 -3
  40. inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ModelRolesView.tsx +1 -1
  41. inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/Navbar.tsx +4 -4
  42. inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/PrimaryBar.tsx +8 -8
  43. inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ResultsPanel.tsx +6 -6
  44. inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/RunningStatusPanel.tsx +1 -1
  45. inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ScoreGrid.tsx +1 -1
  46. inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/SecondaryBar.tsx +8 -8
  47. inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/InfoTab.tsx +35 -6
  48. inspect_ai/_view/www/src/app/log-view/tabs/JsonTab.tsx +136 -0
  49. inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/SamplesTab.tsx +82 -73
  50. inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/grouping.ts +3 -3
  51. inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/types.ts +1 -1
  52. inspect_ai/_view/www/src/{plan → app/plan}/DatasetDetailView.tsx +2 -2
  53. inspect_ai/_view/www/src/{plan → app/plan}/DetailStep.tsx +1 -1
  54. inspect_ai/_view/www/src/{plan → app/plan}/ModelCard.tsx +4 -4
  55. inspect_ai/_view/www/src/{plan → app/plan}/PlanCard.tsx +2 -2
  56. inspect_ai/_view/www/src/{plan → app/plan}/PlanDetailView.tsx +5 -5
  57. inspect_ai/_view/www/src/{plan → app/plan}/SolverDetailView.tsx +1 -1
  58. inspect_ai/_view/www/src/app/routing/AppRouter.tsx +58 -0
  59. inspect_ai/_view/www/src/app/routing/navigationHooks.ts +182 -0
  60. inspect_ai/_view/www/src/app/routing/url.ts +43 -0
  61. inspect_ai/_view/www/src/{samples → app/samples}/InlineSampleDisplay.tsx +11 -27
  62. inspect_ai/_view/www/src/{samples → app/samples}/SampleDialog.tsx +36 -40
  63. inspect_ai/_view/www/src/{samples → app/samples}/SampleDisplay.module.css +4 -0
  64. inspect_ai/_view/www/src/{samples → app/samples}/SampleDisplay.tsx +116 -49
  65. inspect_ai/_view/www/src/{samples → app/samples}/SampleSummaryView.module.css +1 -1
  66. inspect_ai/_view/www/src/{samples → app/samples}/SampleSummaryView.tsx +29 -26
  67. inspect_ai/_view/www/src/{samples → app/samples}/SamplesTools.tsx +3 -3
  68. inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatMessage.module.css +5 -2
  69. inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatMessage.tsx +12 -4
  70. inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatMessageRenderer.tsx +3 -3
  71. inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatMessageRow.tsx +6 -1
  72. inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatView.tsx +4 -2
  73. inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatViewVirtualList.tsx +5 -3
  74. inspect_ai/_view/www/src/app/samples/chat/MessageContent.module.css +12 -0
  75. inspect_ai/_view/www/src/{samples → app/samples}/chat/MessageContent.tsx +11 -10
  76. inspect_ai/_view/www/src/app/samples/chat/MessageContents.module.css +7 -0
  77. inspect_ai/_view/www/src/{samples → app/samples}/chat/MessageContents.tsx +14 -8
  78. inspect_ai/_view/www/src/{samples → app/samples}/chat/messages.ts +2 -2
  79. inspect_ai/_view/www/src/app/samples/chat/tools/ToolCallView.module.css +7 -0
  80. inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolCallView.tsx +26 -27
  81. inspect_ai/_view/www/src/app/samples/chat/tools/ToolInput.module.css +19 -0
  82. inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolInput.tsx +3 -3
  83. inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolOutput.module.css +1 -0
  84. inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolOutput.tsx +1 -1
  85. inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolTitle.module.css +4 -0
  86. inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/ToolTitle.tsx +2 -2
  87. inspect_ai/_view/www/src/{samples → app/samples}/chat/tools/tool.ts +1 -1
  88. inspect_ai/_view/www/src/app/samples/chat/types.ts +1 -0
  89. inspect_ai/_view/www/src/{samples → app/samples}/descriptor/samplesDescriptor.tsx +38 -15
  90. inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/BooleanScoreDescriptor.tsx +1 -1
  91. inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/CategoricalScoreDescriptor.tsx +2 -2
  92. inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/NumericScoreDescriptor.tsx +3 -3
  93. inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/ObjectScoreDescriptor.tsx +4 -4
  94. inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/OtherScoreDescriptor.tsx +2 -2
  95. inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/PassFailScoreDescriptor.tsx +2 -2
  96. inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/ScoreDescriptor.tsx +1 -1
  97. inspect_ai/_view/www/src/{samples → app/samples}/descriptor/types.ts +4 -3
  98. inspect_ai/_view/www/src/{samples → app/samples}/error/SampleErrorView.module.css +2 -1
  99. inspect_ai/_view/www/src/{samples → app/samples}/list/SampleHeader.tsx +3 -0
  100. inspect_ai/_view/www/src/{samples → app/samples}/list/SampleList.tsx +47 -33
  101. inspect_ai/_view/www/src/{samples → app/samples}/list/SampleRow.module.css +16 -0
  102. inspect_ai/_view/www/src/{samples → app/samples}/list/SampleRow.tsx +47 -20
  103. inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/SelectScorer.tsx +1 -1
  104. inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/SortFilter.tsx +4 -4
  105. inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/filters.ts +8 -6
  106. inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/sample-filter/SampleFilter.tsx +4 -3
  107. inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/sample-filter/completions.ts +1 -1
  108. inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/sample-filter/language.ts +1 -0
  109. inspect_ai/_view/www/src/{samples → app/samples}/sampleDataAdapter.ts +3 -3
  110. inspect_ai/_view/www/src/{samples → app/samples}/sampleLimit.ts +1 -1
  111. inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScores.tsx +1 -1
  112. inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScoresGrid.tsx +12 -11
  113. inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScoresView.tsx +6 -6
  114. inspect_ai/_view/www/src/{samples → app/samples}/transcript/ApprovalEventView.tsx +1 -1
  115. inspect_ai/_view/www/src/{samples → app/samples}/transcript/ErrorEventView.tsx +3 -3
  116. inspect_ai/_view/www/src/{samples → app/samples}/transcript/InfoEventView.tsx +4 -4
  117. inspect_ai/_view/www/src/{samples → app/samples}/transcript/InputEventView.tsx +3 -3
  118. inspect_ai/_view/www/src/{samples → app/samples}/transcript/LoggerEventView.tsx +3 -3
  119. inspect_ai/_view/www/src/{samples → app/samples}/transcript/ModelEventView.module.css +13 -7
  120. inspect_ai/_view/www/src/{samples → app/samples}/transcript/ModelEventView.tsx +49 -21
  121. inspect_ai/_view/www/src/{samples → app/samples}/transcript/SampleInitEventView.tsx +11 -9
  122. inspect_ai/_view/www/src/{samples → app/samples}/transcript/SampleLimitEventView.tsx +1 -1
  123. inspect_ai/_view/www/src/{samples → app/samples}/transcript/SandboxEventView.tsx +8 -6
  124. inspect_ai/_view/www/src/{samples → app/samples}/transcript/ScoreEventView.tsx +4 -4
  125. inspect_ai/_view/www/src/{samples → app/samples}/transcript/StepEventView.tsx +11 -3
  126. inspect_ai/_view/www/src/{samples → app/samples}/transcript/SubtaskEventView.tsx +2 -2
  127. inspect_ai/_view/www/src/{samples → app/samples}/transcript/ToolEventView.tsx +2 -2
  128. inspect_ai/_view/www/src/{samples → app/samples}/transcript/TranscriptView.module.css +8 -7
  129. inspect_ai/_view/www/src/{samples → app/samples}/transcript/TranscriptView.tsx +32 -114
  130. inspect_ai/_view/www/src/{samples → app/samples}/transcript/TranscriptVirtualListComponent.module.css +6 -5
  131. inspect_ai/_view/www/src/{samples → app/samples}/transcript/TranscriptVirtualListComponent.tsx +14 -2
  132. inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventPanel.tsx +2 -2
  133. inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventTimingPanel.tsx +1 -1
  134. inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/utils.ts +1 -1
  135. inspect_ai/_view/www/src/{samples → app/samples}/transcript/state/StateEventRenderers.tsx +23 -21
  136. inspect_ai/_view/www/src/{samples → app/samples}/transcript/state/StateEventRenders.module.css +7 -0
  137. inspect_ai/_view/www/src/{samples → app/samples}/transcript/state/StateEventView.tsx +2 -2
  138. inspect_ai/_view/www/src/app/samples/transcript/transform/fixups.ts +142 -0
  139. inspect_ai/_view/www/src/app/samples/transcript/transform/treeify.ts +39 -0
  140. inspect_ai/_view/www/src/{samples → app/samples}/transcript/types.ts +1 -1
  141. inspect_ai/_view/www/src/{workspace → app}/sidebar/EvalStatus.tsx +1 -1
  142. inspect_ai/_view/www/src/app/sidebar/LogDirectoryTitleView.module.css +16 -0
  143. inspect_ai/_view/www/src/app/sidebar/LogDirectoryTitleView.tsx +70 -0
  144. inspect_ai/_view/www/src/{workspace → app}/sidebar/Sidebar.module.css +8 -0
  145. inspect_ai/_view/www/src/{workspace → app}/sidebar/Sidebar.tsx +35 -17
  146. inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarLogEntry.tsx +1 -1
  147. inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarScoreView.tsx +2 -2
  148. inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarScoresView.tsx +2 -2
  149. inspect_ai/_view/www/src/{types.ts → app/types.ts} +18 -11
  150. inspect_ai/_view/www/src/{usage → app/usage}/ModelTokenTable.tsx +1 -1
  151. inspect_ai/_view/www/src/{usage → app/usage}/ModelUsagePanel.tsx +2 -2
  152. inspect_ai/_view/www/src/{usage → app/usage}/TokenTable.tsx +1 -1
  153. inspect_ai/_view/www/src/{usage → app/usage}/UsageCard.tsx +6 -6
  154. inspect_ai/_view/www/src/{api → client/api}/api-browser.ts +2 -2
  155. inspect_ai/_view/www/src/{api → client/api}/api-http.ts +3 -3
  156. inspect_ai/_view/www/src/{api → client/api}/api-vscode.ts +2 -2
  157. inspect_ai/_view/www/src/{api → client/api}/client-api.ts +6 -5
  158. inspect_ai/_view/www/src/{api → client/api}/index.ts +2 -2
  159. inspect_ai/_view/www/src/{api → client/api}/types.ts +4 -1
  160. inspect_ai/_view/www/src/{logfile → client/remote}/remoteLogFile.ts +3 -3
  161. inspect_ai/_view/www/src/{storage → client/storage}/index.ts +11 -5
  162. inspect_ai/_view/www/src/components/Card.tsx +1 -1
  163. inspect_ai/_view/www/src/components/CopyButton.tsx +1 -1
  164. inspect_ai/_view/www/src/components/DownloadButton.tsx +1 -1
  165. inspect_ai/_view/www/src/components/ErrorPanel.tsx +1 -1
  166. inspect_ai/_view/www/src/components/{ExpandablePanel.css → ExpandablePanel.module.css} +14 -11
  167. inspect_ai/_view/www/src/components/ExpandablePanel.tsx +16 -10
  168. inspect_ai/_view/www/src/components/FindBand.tsx +1 -1
  169. inspect_ai/_view/www/src/components/JsonPanel.css +2 -2
  170. inspect_ai/_view/www/src/components/LargeModal.tsx +12 -1
  171. inspect_ai/_view/www/src/components/LightboxCarousel.tsx +1 -1
  172. inspect_ai/_view/www/src/components/MarkdownDiv.tsx +3 -1
  173. inspect_ai/_view/www/src/components/MessageBand.tsx +1 -1
  174. inspect_ai/_view/www/src/components/NoContentsPanel.tsx +1 -1
  175. inspect_ai/_view/www/src/constants.ts +10 -9
  176. inspect_ai/_view/www/src/index.tsx +27 -11
  177. inspect_ai/_view/www/src/state/appSlice.ts +44 -5
  178. inspect_ai/_view/www/src/state/hooks.ts +30 -7
  179. inspect_ai/_view/www/src/state/logSlice.ts +7 -5
  180. inspect_ai/_view/www/src/state/logsPolling.ts +1 -1
  181. inspect_ai/_view/www/src/state/logsSlice.ts +18 -13
  182. inspect_ai/_view/www/src/state/samplePolling.ts +12 -12
  183. inspect_ai/_view/www/src/state/sampleSlice.ts +3 -5
  184. inspect_ai/_view/www/src/state/sampleUtils.ts +1 -1
  185. inspect_ai/_view/www/src/{scoring/utils.ts → state/scoring.ts} +2 -2
  186. inspect_ai/_view/www/src/state/store.ts +9 -7
  187. inspect_ai/_view/www/src/state/utils.ts +1 -1
  188. inspect_ai/_view/www/src/tests/README.md +49 -0
  189. inspect_ai/_view/www/src/tests/__mocks__/fileMock.js +1 -0
  190. inspect_ai/_view/www/src/tests/__mocks__/styleMock.js +1 -0
  191. inspect_ai/_view/www/src/tests/setupTests.mjs +1 -0
  192. inspect_ai/_view/www/src/tests/utils/base64.test.ts +23 -0
  193. inspect_ai/_view/www/src/tests/utils/format.test.ts +127 -0
  194. inspect_ai/_view/www/src/tests/utils/path.test.ts +54 -0
  195. inspect_ai/_view/www/src/utils/format.ts +8 -2
  196. inspect_ai/_view/www/src/utils/path.ts +14 -2
  197. inspect_ai/_view/www/src/utils/polling.ts +1 -2
  198. inspect_ai/_view/www/src/utils/uri.ts +32 -0
  199. inspect_ai/_view/www/yarn.lock +3310 -382
  200. inspect_ai/agent/_handoff.py +6 -3
  201. inspect_ai/agent/_human/agent.py +5 -3
  202. inspect_ai/agent/_human/install.py +16 -7
  203. inspect_ai/agent/_human/panel.py +14 -1
  204. inspect_ai/agent/_human/service.py +5 -1
  205. inspect_ai/agent/_react.py +161 -128
  206. inspect_ai/agent/_types.py +15 -4
  207. inspect_ai/approval/_policy.py +2 -2
  208. inspect_ai/log/_file.py +30 -11
  209. inspect_ai/log/_log.py +7 -1
  210. inspect_ai/log/_recorders/eval.py +3 -0
  211. inspect_ai/log/_recorders/types.py +1 -0
  212. inspect_ai/log/_samples.py +4 -0
  213. inspect_ai/model/_call_tools.py +33 -17
  214. inspect_ai/model/_generate_config.py +10 -2
  215. inspect_ai/model/_model.py +41 -21
  216. inspect_ai/model/_model_output.py +2 -1
  217. inspect_ai/model/_openai.py +10 -8
  218. inspect_ai/model/_openai_responses.py +83 -42
  219. inspect_ai/model/_providers/anthropic.py +14 -12
  220. inspect_ai/model/_providers/google.py +191 -95
  221. inspect_ai/model/_providers/hf.py +1 -1
  222. inspect_ai/model/_providers/mistral.py +2 -3
  223. inspect_ai/model/_providers/openai.py +54 -17
  224. inspect_ai/model/_providers/openai_o1.py +1 -1
  225. inspect_ai/model/_providers/openai_responses.py +28 -16
  226. inspect_ai/model/_providers/openrouter.py +14 -0
  227. inspect_ai/model/_providers/providers.py +2 -2
  228. inspect_ai/model/_providers/util/chatapi.py +17 -7
  229. inspect_ai/model/_providers/vllm.py +1 -1
  230. inspect_ai/scorer/_metric.py +17 -1
  231. inspect_ai/scorer/_model.py +51 -6
  232. inspect_ai/scorer/_scorer.py +1 -1
  233. inspect_ai/solver/_human_agent.py +3 -0
  234. inspect_ai/solver/_plan.py +1 -1
  235. inspect_ai/solver/_solver.py +1 -1
  236. inspect_ai/solver/_use_tools.py +14 -8
  237. inspect_ai/tool/__init__.py +16 -1
  238. inspect_ai/tool/_json_rpc_helpers.py +285 -0
  239. inspect_ai/tool/_mcp/__init__.py +13 -0
  240. inspect_ai/tool/_mcp/_context.py +14 -0
  241. inspect_ai/tool/_mcp/_mcp.py +293 -0
  242. inspect_ai/tool/_mcp/_sandbox.py +104 -0
  243. inspect_ai/tool/_mcp/_types.py +31 -0
  244. inspect_ai/tool/_mcp/connection.py +60 -0
  245. inspect_ai/tool/_mcp/sampling.py +118 -0
  246. inspect_ai/tool/_mcp/server.py +112 -0
  247. inspect_ai/tool/_mcp/tools.py +34 -0
  248. inspect_ai/tool/_tool.py +13 -0
  249. inspect_ai/tool/_tool_def.py +24 -7
  250. inspect_ai/tool/_tool_support_helpers.py +129 -153
  251. inspect_ai/tool/_tools/_bash_session.py +11 -11
  252. inspect_ai/tool/_tools/_text_editor.py +6 -6
  253. inspect_ai/tool/_tools/_web_browser/_web_browser.py +8 -8
  254. inspect_ai/util/_anyio.py +31 -20
  255. inspect_ai/util/_json.py +20 -2
  256. inspect_ai/util/_sandbox/context.py +18 -7
  257. inspect_ai/util/_sandbox/docker/compose.py +1 -1
  258. inspect_ai/util/_sandbox/docker/docker.py +92 -21
  259. inspect_ai/util/_sandbox/environment.py +33 -2
  260. inspect_ai/util/_sandbox/events.py +2 -2
  261. inspect_ai/util/_sandbox/service.py +13 -3
  262. {inspect_ai-0.3.90.dist-info → inspect_ai-0.3.91.dist-info}/METADATA +6 -2
  263. inspect_ai-0.3.91.dist-info/RECORD +732 -0
  264. {inspect_ai-0.3.90.dist-info → inspect_ai-0.3.91.dist-info}/WHEEL +1 -1
  265. inspect_ai/_view/www/src/App.tsx +0 -316
  266. inspect_ai/_view/www/src/samples/chat/MessageContent.module.css +0 -4
  267. inspect_ai/_view/www/src/samples/chat/MessageContents.module.css +0 -3
  268. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.module.css +0 -3
  269. inspect_ai/_view/www/src/samples/chat/tools/ToolInput.module.css +0 -14
  270. inspect_ai/_view/www/src/workspace/WorkSpace.tsx +0 -292
  271. inspect_ai/_view/www/src/workspace/sidebar/LogDirectoryTitleView.module.css +0 -5
  272. inspect_ai/_view/www/src/workspace/sidebar/LogDirectoryTitleView.tsx +0 -57
  273. inspect_ai/_view/www/src/workspace/tabs/JsonTab.tsx +0 -43
  274. inspect_ai-0.3.90.dist-info/RECORD +0 -705
  275. /inspect_ai/_view/www/src/{types → @types}/asciicinema-player.d.ts +0 -0
  276. /inspect_ai/_view/www/src/{types → @types}/jsondiffpatch.d.ts +0 -0
  277. /inspect_ai/_view/www/src/{types → @types}/markdown-it-katex.d.ts +0 -0
  278. /inspect_ai/_view/www/src/{types → @types}/prism.d.ts +0 -0
  279. /inspect_ai/_view/www/src/{appearance → app/appearance}/colors.ts +0 -0
  280. /inspect_ai/_view/www/src/{appearance → app/appearance}/fonts.ts +0 -0
  281. /inspect_ai/_view/www/src/{appearance → app/appearance}/styles.ts +0 -0
  282. /inspect_ai/_view/www/src/{metadata → app/content}/MetaDataGrid.tsx +0 -0
  283. /inspect_ai/_view/www/src/{metadata → app/content}/MetaDataView.module.css +0 -0
  284. /inspect_ai/_view/www/src/{metadata → app/content}/MetaDataView.tsx +0 -0
  285. /inspect_ai/_view/www/src/{metadata → app/content}/MetadataGrid.module.css +0 -0
  286. /inspect_ai/_view/www/src/{metadata → app/content}/RenderedContent.module.css +0 -0
  287. /inspect_ai/_view/www/src/{metadata → app/content}/types.ts +0 -0
  288. /inspect_ai/_view/www/src/{workspace/WorkSpaceView.module.css → app/log-view/LogView.module.css} +0 -0
  289. /inspect_ai/_view/www/src/{workspace → app/log-view}/error/TaskErrorPanel.module.css +0 -0
  290. /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ModelRolesView.module.css +0 -0
  291. /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/Navbar.module.css +0 -0
  292. /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/PrimaryBar.module.css +0 -0
  293. /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ResultsPanel.module.css +0 -0
  294. /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/RunningStatusPanel.module.css +0 -0
  295. /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/ScoreGrid.module.css +0 -0
  296. /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/SecondaryBar.module.css +0 -0
  297. /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/StatusPanel.module.css +0 -0
  298. /inspect_ai/_view/www/src/{workspace → app/log-view}/navbar/StatusPanel.tsx +0 -0
  299. /inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/InfoTab.module.css +0 -0
  300. /inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/JsonTab.module.css +0 -0
  301. /inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/RunningNoSamples.module.css +0 -0
  302. /inspect_ai/_view/www/src/{workspace → app/log-view}/tabs/RunningNoSamples.tsx +0 -0
  303. /inspect_ai/_view/www/src/{workspace → app/log-view}/types.ts +0 -0
  304. /inspect_ai/_view/www/src/{workspace → app/log-view}/utils.ts +0 -0
  305. /inspect_ai/_view/www/src/{plan → app/plan}/DatasetDetailView.module.css +0 -0
  306. /inspect_ai/_view/www/src/{plan → app/plan}/DetailStep.module.css +0 -0
  307. /inspect_ai/_view/www/src/{plan → app/plan}/ModelCard.module.css +0 -0
  308. /inspect_ai/_view/www/src/{plan → app/plan}/PlanDetailView.module.css +0 -0
  309. /inspect_ai/_view/www/src/{plan → app/plan}/ScorerDetailView.module.css +0 -0
  310. /inspect_ai/_view/www/src/{plan → app/plan}/ScorerDetailView.tsx +0 -0
  311. /inspect_ai/_view/www/src/{plan → app/plan}/SolverDetailView.module.css +0 -0
  312. /inspect_ai/_view/www/src/{samples → app/samples}/InlineSampleDisplay.module.css +0 -0
  313. /inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatMessageRow.module.css +0 -0
  314. /inspect_ai/_view/www/src/{samples → app/samples}/chat/ChatViewVirtualList.module.css +0 -0
  315. /inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/BooleanScoreDescriptor.module.css +0 -0
  316. /inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/ObjectScoreDescriptor.module.css +0 -0
  317. /inspect_ai/_view/www/src/{samples → app/samples}/descriptor/score/PassFailScoreDescriptor.module.css +0 -0
  318. /inspect_ai/_view/www/src/{samples → app/samples}/error/FlatSampleErrorView.module.css +0 -0
  319. /inspect_ai/_view/www/src/{samples → app/samples}/error/FlatSampleErrorView.tsx +0 -0
  320. /inspect_ai/_view/www/src/{samples → app/samples}/error/SampleErrorView.tsx +0 -0
  321. /inspect_ai/_view/www/src/{samples → app/samples}/error/error.ts +0 -0
  322. /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleFooter.module.css +0 -0
  323. /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleFooter.tsx +0 -0
  324. /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleHeader.module.css +0 -0
  325. /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleList.module.css +0 -0
  326. /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleSeparator.module.css +0 -0
  327. /inspect_ai/_view/www/src/{samples → app/samples}/list/SampleSeparator.tsx +0 -0
  328. /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/EpochFilter.module.css +0 -0
  329. /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/EpochFilter.tsx +0 -0
  330. /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/SelectScorer.module.css +0 -0
  331. /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/SortFilter.module.css +0 -0
  332. /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/sample-filter/SampleFilter.module.css +0 -0
  333. /inspect_ai/_view/www/src/{samples → app/samples}/sample-tools/sample-filter/tokenize.ts +0 -0
  334. /inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScores.module.css +0 -0
  335. /inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScoresGrid.module.css +0 -0
  336. /inspect_ai/_view/www/src/{samples → app/samples}/scores/SampleScoresView.module.css +0 -0
  337. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/InfoEventView.module.css +0 -0
  338. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/LoggerEventView.module.css +0 -0
  339. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/SampleInitEventView.module.css +0 -0
  340. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/SandboxEventView.module.css +0 -0
  341. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/ScoreEventView.module.css +0 -0
  342. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/SubtaskEventView.module.css +0 -0
  343. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/ToolEventView.module.css +0 -0
  344. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventNav.module.css +0 -0
  345. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventNav.tsx +0 -0
  346. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventNavs.module.css +0 -0
  347. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventNavs.tsx +0 -0
  348. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventPanel.module.css +0 -0
  349. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventProgressPanel.module.css +0 -0
  350. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventProgressPanel.tsx +0 -0
  351. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventRow.module.css +0 -0
  352. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventRow.tsx +0 -0
  353. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventSection.module.css +0 -0
  354. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventSection.tsx +0 -0
  355. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/event/EventTimingPanel.module.css +0 -0
  356. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/state/StateDiffView.tsx +0 -0
  357. /inspect_ai/_view/www/src/{samples → app/samples}/transcript/state/StateEventView.module.css +0 -0
  358. /inspect_ai/_view/www/src/{workspace → app}/sidebar/EvalStatus.module.css +0 -0
  359. /inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarLogEntry.module.css +0 -0
  360. /inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarScoreView.module.css +0 -0
  361. /inspect_ai/_view/www/src/{workspace → app}/sidebar/SidebarScoresView.module.css +0 -0
  362. /inspect_ai/_view/www/src/{usage → app/usage}/ModelUsagePanel.module.css +0 -0
  363. /inspect_ai/_view/www/src/{usage → app/usage}/TokenTable.module.css +0 -0
  364. /inspect_ai/_view/www/src/{usage → app/usage}/UsageCard.module.css +0 -0
  365. /inspect_ai/_view/www/src/{api → client/api}/api-shared.ts +0 -0
  366. /inspect_ai/_view/www/src/{api → client/api}/jsonrpc.ts +0 -0
  367. /inspect_ai/_view/www/src/{logfile → client/remote}/remoteZipFile.ts +0 -0
  368. {inspect_ai-0.3.90.dist-info → inspect_ai-0.3.91.dist-info}/entry_points.txt +0 -0
  369. {inspect_ai-0.3.90.dist-info → inspect_ai-0.3.91.dist-info}/licenses/LICENSE +0 -0
  370. {inspect_ai-0.3.90.dist-info → inspect_ai-0.3.91.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,16 @@
1
- import { EvalLogHeader, LogFiles } from "../api/types";
2
- import { LogsState } from "../types";
1
+ import { LogsState } from "../app/types";
2
+ import { EvalLogHeader, LogFiles } from "../client/api/types";
3
3
  import { createLogger } from "../utils/logger";
4
4
  import { createLogsPolling } from "./logsPolling";
5
5
  import { StoreState } from "./store";
6
6
 
7
7
  const log = createLogger("Log Slice");
8
8
 
9
+ const kEmptyLogs: LogFiles = {
10
+ log_dir: "",
11
+ files: [],
12
+ };
13
+
9
14
  export interface LogsSlice {
10
15
  logs: LogsState;
11
16
  logsActions: {
@@ -28,7 +33,7 @@ export interface LogsSlice {
28
33
  }
29
34
 
30
35
  const initialState: LogsState = {
31
- logs: { log_dir: "", files: [] },
36
+ logs: kEmptyLogs,
32
37
  logHeaders: {},
33
38
  headersLoading: false,
34
39
  selectedLogIndex: -1,
@@ -83,7 +88,7 @@ export const createLogsSlice = (
83
88
 
84
89
  setSelectedLogFile: (logUrl: string) => {
85
90
  const state = get();
86
- const index = state.logs.logs.files.findIndex((val) =>
91
+ const index = state.logs.logs.files.findIndex((val: { name: string }) =>
87
92
  logUrl.endsWith(val.name),
88
93
  );
89
94
 
@@ -97,7 +102,7 @@ export const createLogsSlice = (
97
102
  const api = get().api;
98
103
  if (!api) {
99
104
  console.error("API not initialized in LogsStore");
100
- return { log_dir: "", files: [] };
105
+ return kEmptyLogs;
101
106
  }
102
107
 
103
108
  try {
@@ -106,7 +111,7 @@ export const createLogsSlice = (
106
111
  } catch (e) {
107
112
  console.log(e);
108
113
  get().appActions.setStatus({ loading: false, error: e as Error });
109
- return { log_dir: "", files: [] };
114
+ return kEmptyLogs;
110
115
  }
111
116
  },
112
117
  refreshLogs: async () => {
@@ -114,15 +119,15 @@ export const createLogsSlice = (
114
119
  const state = get();
115
120
  const refreshedLogs = await state.logsActions.loadLogs();
116
121
 
117
- // Set the logs first
118
- state.logsActions.setLogs(refreshedLogs || { log_dir: "", files: [] });
119
-
120
122
  // Preserve the selected log even if new logs appear
121
123
  const currentLog =
122
- refreshedLogs.files[
124
+ state.logs.logs.files[
123
125
  state.logs.selectedLogIndex > -1 ? state.logs.selectedLogIndex : 0
124
126
  ];
125
127
 
128
+ // Set the logs first
129
+ state.logsActions.setLogs(refreshedLogs || kEmptyLogs);
130
+
126
131
  if (currentLog) {
127
132
  const newIndex = refreshedLogs?.files.findIndex((file) =>
128
133
  currentLog.name.endsWith(file.name),
@@ -136,7 +141,7 @@ export const createLogsSlice = (
136
141
  // Select a specific log file
137
142
  selectLogFile: async (logUrl: string) => {
138
143
  const state = get();
139
- const index = state.logs.logs.files.findIndex((val) =>
144
+ const index = state.logs.logs.files.findIndex((val: { name: string }) =>
140
145
  val.name.endsWith(logUrl),
141
146
  );
142
147
 
@@ -147,10 +152,10 @@ export const createLogsSlice = (
147
152
  // It isn't yet loaded, so refresh the logs and try to load it from there
148
153
  const result = await state.logsActions.loadLogs();
149
154
  const idx = result?.files.findIndex((file) =>
150
- logUrl.endsWith(file.name),
155
+ file.name.endsWith(logUrl),
151
156
  );
152
157
 
153
- state.logsActions.setLogs(result || { log_dir: "", files: [] });
158
+ state.logsActions.setLogs(result || kEmptyLogs);
154
159
  state.logsActions.setSelectedLogIndex(
155
160
  idx !== undefined && idx > -1 ? idx : 0,
156
161
  );
@@ -1,10 +1,10 @@
1
+ import { Event } from "../app/types";
1
2
  import {
2
3
  AttachmentData,
3
4
  EventData,
4
5
  SampleData,
5
6
  SampleSummary,
6
- } from "../api/types";
7
- import { Event } from "../types";
7
+ } from "../client/api/types";
8
8
  import { resolveAttachments } from "../utils/attachments";
9
9
  import { createLogger } from "../utils/logger";
10
10
  import { createPolling } from "../utils/polling";
@@ -98,6 +98,7 @@ export function createSamplePolling(
98
98
  }
99
99
 
100
100
  // Fetch sample data
101
+ state.sampleActions.setSampleStatus("streaming");
101
102
  const eventId = pollingState.eventId;
102
103
  const attachmentId = pollingState.attachmentId;
103
104
  const sampleDataResponse = await api.get_log_sample_data(
@@ -123,10 +124,7 @@ export function createSamplePolling(
123
124
 
124
125
  // Also fetch a fresh sample and clear the runnning Events
125
126
  // (if there were ever running events)
126
- if (
127
- state.sample.runningEvents.length > 0 ||
128
- state.sample.sampleStatus === "streaming"
129
- ) {
127
+ if (state.sample.runningEvents.length > 0) {
130
128
  try {
131
129
  log.debug(
132
130
  `LOADING COMPLETED SAMPLE AFTER FLUSH: ${summary.id}-${summary.epoch}`,
@@ -143,12 +141,12 @@ export function createSamplePolling(
143
141
  // Update the store with the completed sample
144
142
  set((state) => {
145
143
  state.sample.selectedSample = migratedSample;
146
- state.sample.sampleStatus = "ok";
144
+ state.sampleActions.setSampleStatus("ok");
147
145
  state.sample.runningEvents = [];
148
146
  });
149
147
  } else {
150
148
  set((state) => {
151
- state.sample.sampleStatus = "error";
149
+ state.sampleActions.setSampleStatus("error");
152
150
  state.sample.sampleError = new Error(
153
151
  "Unable to load sample - an unknown error occurred",
154
152
  );
@@ -158,12 +156,14 @@ export function createSamplePolling(
158
156
  } catch (e) {
159
157
  set((state) => {
160
158
  state.sample.sampleError = e as Error;
161
- state.sample.sampleStatus = "error";
159
+ state.sampleActions.setSampleStatus("error");
162
160
  state.sample.runningEvents = [];
163
161
  });
164
162
  }
163
+ } else {
164
+ state.sampleActions.setSampleStatus("ok");
165
+ state.sample.runningEvents = [];
165
166
  }
166
-
167
167
  return false;
168
168
  }
169
169
 
@@ -251,8 +251,8 @@ export function createSamplePolling(
251
251
  }
252
252
 
253
253
  const resetPollingState = (state: PollingState) => {
254
- state.eventId = -1;
255
- state.attachmentId = -1;
254
+ state.eventId = kNoId;
255
+ state.attachmentId = kNoId;
256
256
  state.eventMapping = {};
257
257
  state.attachments = {};
258
258
  state.events = [];
@@ -1,7 +1,7 @@
1
- import { SampleSummary } from "../api/types";
1
+ import { EvalSample } from "../@types/log";
2
+ import { SampleState, SampleStatus } from "../app/types";
3
+ import { SampleSummary } from "../client/api/types";
2
4
  import { kSampleMessagesTabId } from "../constants";
3
- import { SampleState, SampleStatus } from "../types";
4
- import { EvalSample } from "../types/log";
5
5
  import { createLogger } from "../utils/logger";
6
6
  import { createSamplePolling } from "./samplePolling";
7
7
  import { resolveSample } from "./sampleUtils"; // Import the shared utility
@@ -78,7 +78,6 @@ export const createSampleSlice = (
78
78
  const state = get();
79
79
  if (state.log.loadedLog && state.sample.selectedSample) {
80
80
  samplePolling.startPolling(logFile, sampleSummary);
81
- state.sampleActions.setSampleStatus("streaming");
82
81
  }
83
82
  },
84
83
  loadSample: async (logFile: string, sampleSummary: SampleSummary) => {
@@ -113,7 +112,6 @@ export const createSampleSlice = (
113
112
 
114
113
  // Poll running sample
115
114
  samplePolling.startPolling(logFile, sampleSummary);
116
- sampleActions.setSampleStatus("streaming");
117
115
  }
118
116
  } catch (e) {
119
117
  sampleActions.setSampleError(e as Error);
@@ -1,4 +1,4 @@
1
- import { EvalSample } from "../types/log";
1
+ import { EvalSample } from "../@types/log";
2
2
  import { resolveAttachments } from "../utils/attachments";
3
3
 
4
4
  /**
@@ -1,5 +1,5 @@
1
- import { EvalSummary, SampleSummary } from "../api/types";
2
- import { EvalResults } from "../types/log";
1
+ import { EvalResults } from "../@types/log";
2
+ import { EvalSummary, SampleSummary } from "../client/api/types";
3
3
 
4
4
  export interface ScorerInfo {
5
5
  name: string;
@@ -1,8 +1,9 @@
1
1
  import { create, StoreApi, UseBoundStore } from "zustand";
2
2
  import { devtools, persist } from "zustand/middleware";
3
3
  import { immer } from "zustand/middleware/immer";
4
- import { Capabilities, ClientAPI, ClientStorage } from "../api/types";
4
+ import { Capabilities, ClientAPI, ClientStorage } from "../client/api/types";
5
5
  import { createLogger } from "../utils/logger";
6
+ import { debounce } from "../utils/sync";
6
7
  import { AppSlice, createAppSlice, initializeAppSlice } from "./appSlice";
7
8
  import { createLogSlice, initalializeLogSlice, LogSlice } from "./logSlice";
8
9
  import { createLogsSlice, initializeLogsSlice, LogsSlice } from "./logsSlice";
@@ -24,6 +25,9 @@ export interface StoreState extends AppSlice, LogsSlice, LogSlice, SampleSlice {
24
25
  cleanup: () => void;
25
26
  }
26
27
 
28
+ export let storeImplementation: UseBoundStore<StoreApi<StoreState>> | null =
29
+ null;
30
+
27
31
  // The data that will actually be persisted
28
32
  export type PersistedState = {
29
33
  app: AppSlice["app"];
@@ -32,9 +36,6 @@ export type PersistedState = {
32
36
  sample: SampleSlice["sample"];
33
37
  };
34
38
 
35
- // The store implementation (this will be set when the store is initialized)
36
- let storeImplementation: UseBoundStore<StoreApi<StoreState>> | null = null;
37
-
38
39
  // Create a proxy store that forwards calls to the real store once initialized
39
40
  export const useStore = ((selector?: any) => {
40
41
  if (!storeImplementation) {
@@ -56,11 +57,11 @@ export const initializeStore = (
56
57
  getItem: <T>(name: string): T | null => {
57
58
  return storage ? (storage.getItem(name) as T) : null;
58
59
  },
59
- setItem: <T>(name: string, value: T): void => {
60
+ setItem: debounce(<T>(name: string, value: T): void => {
60
61
  if (storage) {
61
62
  storage.setItem(name, value);
62
63
  }
63
- },
64
+ }, 1000),
64
65
  removeItem: (name: string): void => {
65
66
  if (storage) {
66
67
  storage.removeItem(name);
@@ -139,11 +140,12 @@ export const initializeStore = (
139
140
  storage: storageImplementation,
140
141
  partialize: (state) => {
141
142
  const persisted: PersistedState = filterState({
142
- app: state.app,
143
+ app: { ...state.app, rehydrated: true },
143
144
  log: state.log,
144
145
  logs: state.logs,
145
146
  sample: state.sample,
146
147
  });
148
+ log.debug("PARTIALIZED STATE", persisted);
147
149
  return persisted as unknown as StoreState;
148
150
  },
149
151
  version: 1,
@@ -1,4 +1,4 @@
1
- import { SampleSummary } from "../api/types";
1
+ import { SampleSummary } from "../client/api/types";
2
2
 
3
3
  // Function to merge log samples with pending samples
4
4
  export const mergeSampleSummaries = (
@@ -0,0 +1,49 @@
1
+ # Testing Structure
2
+
3
+ This directory contains the test files for the application. The test framework is built using Jest and TypeScript.
4
+
5
+ ## Directory Structure
6
+
7
+ - `tests/`: Root directory for all tests
8
+ - `__mocks__/`: Mock files for CSS modules and other assets
9
+ - `setupTests.mjs`: Setup file for Jest tests
10
+
11
+ ## Running Tests
12
+
13
+ To run the tests, use the following commands:
14
+
15
+ ```bash
16
+ # Run all tests
17
+ yarn test
18
+
19
+ # Run tests in watch mode
20
+ yarn test:watch
21
+
22
+ # Run tests with coverage report
23
+ yarn test:coverage
24
+ ```
25
+
26
+ ## Test Philosophy
27
+
28
+ Tests are designed to verify functionality rather than implementation details. This means:
29
+
30
+ - Tests should not break due to minor changes in HTML structure
31
+ - Tests focus on the behavior of functions and components
32
+ - Tests should be fast and reliable
33
+
34
+ ## Adding Tests
35
+
36
+ When adding new tests:
37
+
38
+ 1. Create a new test file with the `.test.ts` or `.test.tsx` extension
39
+ 2. Import the functions or components you want to test
40
+ 3. Write tests that verify behavior without making assumptions about implementation
41
+ 4. Use descriptive test names to make it clear what's being tested
42
+
43
+ ## Mocking
44
+
45
+ For mocking external services or components:
46
+
47
+ 1. Create mock files in the `__mocks__` directory
48
+ 2. Mock only what's necessary for the test
49
+ 3. Use Jest's mocking capabilities to replace dependencies
@@ -0,0 +1 @@
1
+ module.exports = "test-file-stub";
@@ -0,0 +1 @@
1
+ module.exports = {};
@@ -0,0 +1 @@
1
+ import "@testing-library/jest-dom";
@@ -0,0 +1,23 @@
1
+ import { isBase64 } from "../../utils/base64";
2
+
3
+ describe("isBase64", () => {
4
+ test("identifies valid base64 strings", () => {
5
+ expect(isBase64("SGVsbG8gV29ybGQ=")).toBe(true); // "Hello World" in base64
6
+ expect(isBase64("dGVzdA==")).toBe(true); // "test" in base64
7
+ expect(isBase64("YWJjMTIzIT8kKiYoKSctPUB+")).toBe(true); // "abc123!?$*&()'-=@+" in base64
8
+ });
9
+
10
+ test("identifies invalid base64 strings", () => {
11
+ expect(isBase64("not-base64")).toBe(false);
12
+ expect(isBase64("SGVs bG8=")).toBe(false); // Contains space
13
+ expect(isBase64("Hello World")).toBe(false);
14
+ expect(isBase64("123")).toBe(false); // Too short for valid base64
15
+ });
16
+
17
+ test("handles edge cases", () => {
18
+ expect(isBase64("")).toBe(true); // Empty string is technically valid base64
19
+ expect(isBase64("YQ==")).toBe(true); // Single character 'a'
20
+ expect(isBase64("YWI=")).toBe(true); // Two characters 'ab'
21
+ expect(isBase64("YWJj")).toBe(true); // Three characters 'abc'
22
+ });
23
+ });
@@ -0,0 +1,127 @@
1
+ import {
2
+ arrayToString,
3
+ formatTime,
4
+ formatPrettyDecimal,
5
+ formatDecimalNoTrailingZeroes,
6
+ toTitleCase,
7
+ formatNoDecimal,
8
+ formatDuration,
9
+ } from "../../utils/format";
10
+
11
+ describe("arrayToString", () => {
12
+ test("converts array to comma-separated string", () => {
13
+ expect(arrayToString(["one", "two", "three"])).toBe("one, two, three");
14
+ });
15
+
16
+ test("handles single string input", () => {
17
+ expect(arrayToString("single")).toBe("single");
18
+ });
19
+
20
+ test("handles empty array", () => {
21
+ expect(arrayToString([])).toBe("");
22
+ });
23
+ });
24
+
25
+ describe("formatTime", () => {
26
+ test("formats seconds when less than a minute", () => {
27
+ expect(formatTime(45)).toBe("45.0 sec");
28
+ });
29
+
30
+ test("formats minutes and seconds", () => {
31
+ expect(formatTime(125)).toBe("2 min 5 sec");
32
+ });
33
+
34
+ test("formats hours, minutes, and seconds", () => {
35
+ expect(formatTime(3665)).toBe("1 hr 1 min 5 sec");
36
+ });
37
+
38
+ test("formats days, hours, minutes, and seconds", () => {
39
+ expect(formatTime(90061)).toBe("1 days 1 hr 1 min 1 sec");
40
+ });
41
+ });
42
+
43
+ describe("formatPrettyDecimal", () => {
44
+ test("adds one decimal place to whole numbers", () => {
45
+ expect(formatPrettyDecimal(5)).toBe("5.0");
46
+ });
47
+
48
+ test("keeps decimal places if less than maxDecimals", () => {
49
+ expect(formatPrettyDecimal(5.12, 3)).toBe("5.12");
50
+ });
51
+
52
+ test("truncates decimal places if more than maxDecimals", () => {
53
+ expect(formatPrettyDecimal(5.12345, 2)).toBe("5.12");
54
+ });
55
+
56
+ test("uses default maxDecimals if not provided", () => {
57
+ expect(formatPrettyDecimal(5.12345)).toBe("5.123");
58
+ });
59
+ });
60
+
61
+ describe("formatDecimalNoTrailingZeroes", () => {
62
+ test("removes trailing zeroes from decimal", () => {
63
+ expect(formatDecimalNoTrailingZeroes(5.1)).toBe("5.1");
64
+ });
65
+
66
+ test("keeps whole numbers as is", () => {
67
+ expect(formatDecimalNoTrailingZeroes(5)).toBe("5");
68
+ });
69
+
70
+ test("handles non-numbers (should be fixed later)", () => {
71
+ const nonNumber = "test" as unknown as number;
72
+ expect(formatDecimalNoTrailingZeroes(nonNumber)).toBe(nonNumber);
73
+ });
74
+ });
75
+
76
+ describe("toTitleCase", () => {
77
+ test("converts string to title case", () => {
78
+ expect(toTitleCase("hello world")).toBe("Hello World");
79
+ });
80
+
81
+ test("handles uppercase strings", () => {
82
+ expect(toTitleCase("HELLO WORLD")).toBe("Hello World");
83
+ });
84
+
85
+ test("handles mixed case strings", () => {
86
+ expect(toTitleCase("hElLo WoRlD")).toBe("Hello World");
87
+ });
88
+
89
+ test("handles empty string", () => {
90
+ expect(toTitleCase("")).toBe("");
91
+ });
92
+ });
93
+
94
+ describe("formatNoDecimal", () => {
95
+ test("rounds number to whole number", () => {
96
+ expect(formatNoDecimal(5.6)).toBe("6");
97
+ });
98
+
99
+ test("handles integer values", () => {
100
+ expect(formatNoDecimal(5)).toBe("5");
101
+ });
102
+
103
+ test("handles non-numbers (should be fixed later)", () => {
104
+ const nonNumber = "test" as unknown as number;
105
+ expect(formatNoDecimal(nonNumber)).toBe(nonNumber);
106
+ });
107
+ });
108
+
109
+ describe("formatDuration", () => {
110
+ test("formats duration between two dates", () => {
111
+ const start = new Date("2023-01-01T00:00:00Z");
112
+ const end = new Date("2023-01-01T00:01:30Z");
113
+ expect(formatDuration(start, end)).toBe("1 min 30 sec");
114
+ });
115
+
116
+ test("handles short durations", () => {
117
+ const start = new Date("2023-01-01T00:00:00Z");
118
+ const end = new Date("2023-01-01T00:00:10Z");
119
+ expect(formatDuration(start, end)).toBe("10.0 sec");
120
+ });
121
+
122
+ test("handles long durations", () => {
123
+ const start = new Date("2023-01-01T00:00:00Z");
124
+ const end = new Date("2023-01-02T01:01:01Z");
125
+ expect(formatDuration(start, end)).toBe("1 days 1 hr 1 min 1 sec");
126
+ });
127
+ });
@@ -0,0 +1,54 @@
1
+ import { filename, dirname } from "../../utils/path";
2
+
3
+ describe("filename", () => {
4
+ test("extracts filename without extension from a path", () => {
5
+ expect(filename("/path/to/file.txt")).toBe("file");
6
+ expect(filename("file.txt")).toBe("file");
7
+ expect(filename("/path/to/document.pdf")).toBe("document");
8
+ });
9
+
10
+ test("handles paths without extensions", () => {
11
+ expect(filename("/path/to/file")).toBe("/path/to/file");
12
+ expect(filename("file")).toBe("file");
13
+ });
14
+
15
+ test("handles paths with multiple dots", () => {
16
+ expect(filename("/path/to/file.name.txt")).toBe("file.name");
17
+ expect(filename("archive.tar.gz")).toBe("archive.tar");
18
+ });
19
+
20
+ test("handles edge cases", () => {
21
+ expect(filename("")).toBe("");
22
+ expect(filename(".")).toBe(".");
23
+ // Special case for .hidden files - there's no extension to remove
24
+ expect(filename(".hidden")).toBe(".hidden");
25
+ // Dot files with extensions should have the extension removed
26
+ expect(filename(".hidden.txt")).toBe(".hidden");
27
+ });
28
+ });
29
+
30
+ describe("dirname", () => {
31
+ test("extracts directory name from a path", () => {
32
+ expect(dirname("/path/to/file.txt")).toBe("/path/to");
33
+ expect(dirname("/path/to/directory/")).toBe("/path/to/directory");
34
+ expect(dirname("/path/to/file")).toBe("/path/to");
35
+ });
36
+
37
+ test("handles paths without directories", () => {
38
+ expect(dirname("file.txt")).toBe("");
39
+ expect(dirname("file")).toBe("");
40
+ });
41
+
42
+ test("handles root directory", () => {
43
+ expect(dirname("/file.txt")).toBe("");
44
+ expect(dirname("/file")).toBe("");
45
+ });
46
+
47
+ test("handles empty input", () => {
48
+ expect(dirname("")).toBe("");
49
+ });
50
+
51
+ test("handles paths with trailing slash", () => {
52
+ expect(dirname("/path/to/directory/")).toBe("/path/to/directory");
53
+ });
54
+ });
@@ -3,7 +3,7 @@ import {
3
3
  ChatMessageSystem,
4
4
  ChatMessageTool,
5
5
  ChatMessageUser,
6
- } from "../types/log";
6
+ } from "../@types/log";
7
7
 
8
8
  /**
9
9
  * Converts an array or a single value to a comma-separated string.
@@ -137,9 +137,15 @@ export function formatDecimalNoTrailingZeroes(num: number): string {
137
137
  * Converts a string to title case.
138
138
  */
139
139
  export function toTitleCase(str: string): string {
140
+ if (!str) {
141
+ return str;
142
+ }
143
+
140
144
  return str
141
145
  .split(" ")
142
- .map((w) => w[0].toUpperCase() + w.substr(1).toLowerCase())
146
+ .map((w) =>
147
+ w.length > 0 ? w[0].toUpperCase() + w.substr(1).toLowerCase() : w,
148
+ )
143
149
  .join(" ");
144
150
  }
145
151
 
@@ -2,8 +2,18 @@
2
2
  * Extracts the filename without extension from a given path.
3
3
  */
4
4
  export const filename = (path: string): string => {
5
+ if (!path) {
6
+ return "";
7
+ }
8
+
5
9
  const pathparts = path.split("/");
6
10
  const basename = pathparts.slice(-1)[0];
11
+
12
+ // Special case for .hidden files
13
+ if (basename.startsWith(".") && !basename.substring(1).includes(".")) {
14
+ return basename;
15
+ }
16
+
7
17
  const match = basename.match(/(.*)\.\S+$/);
8
18
  if (match) {
9
19
  return match[1];
@@ -21,8 +31,10 @@ export const dirname = (path: string): string => {
21
31
  // If the path ends with a filename (or no slashes), remove the last part (filename)
22
32
  if (pathparts.length > 1) {
23
33
  pathparts.pop();
34
+ // Join the remaining parts to form the directory path
35
+ return pathparts.join("/");
24
36
  }
25
37
 
26
- // Join the remaining parts to form the directory path
27
- return pathparts.join("/");
38
+ // If no slashes, return empty string (no directory)
39
+ return "";
28
40
  };
@@ -1,4 +1,3 @@
1
- import { Timeout } from "../types/log";
2
1
  import { createLogger } from "./logger";
3
2
 
4
3
  export interface PollingOptions {
@@ -20,7 +19,7 @@ export const createPolling = (
20
19
  const log = createLogger(`Polling ${name}`);
21
20
 
22
21
  const { maxRetries, interval } = options;
23
- let timeoutId: Timeout = null;
22
+ let timeoutId: ReturnType<typeof setTimeout> | null = null;
24
23
  let retryCount = 0;
25
24
  // Are we currently polling
26
25
  let isPolling = false;
@@ -0,0 +1,32 @@
1
+ export const directoryRelativeUrl = (file: string, dir?: string): string => {
2
+ if (!dir) {
3
+ return encodeURIComponent(file);
4
+ }
5
+
6
+ // Normalize paths to ensure consistent directory separators
7
+ const normalizedFile = file.replace(/\\/g, "/");
8
+ const normalizedLogDir = dir.replace(/\\/g, "/");
9
+
10
+ // Ensure log_dir ends with a trailing slash
11
+ const dirWithSlash = normalizedLogDir.endsWith("/")
12
+ ? normalizedLogDir
13
+ : normalizedLogDir + "/";
14
+
15
+ // Check if file is within the log directory
16
+ if (normalizedFile.startsWith(dirWithSlash)) {
17
+ // Get the relative path
18
+ const relativePath = normalizedFile.substring(dirWithSlash.length);
19
+
20
+ // Split the path into segments and encode each segment
21
+ const segments = relativePath.split("/");
22
+ const encodedSegments = segments.map((segment) =>
23
+ encodeURIComponent(segment),
24
+ );
25
+
26
+ // Join the encoded segments back together
27
+ return encodedSegments.join("/");
28
+ }
29
+
30
+ // If path can't be made relative, return undefined
31
+ return encodeURIComponent(file);
32
+ };