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
@@ -1,6 +1,6 @@
1
1
  import clsx from "clsx";
2
2
 
3
- import { FC } from "react";
3
+ import { FC, useCallback } from "react";
4
4
  import styles from "./EventNav.module.css";
5
5
 
6
6
  interface EventNavProps {
@@ -19,6 +19,11 @@ export const EventNav: FC<EventNavProps> = ({
19
19
  setSelectedNav,
20
20
  }) => {
21
21
  const active = target === selectedNav;
22
+
23
+ const handleClick = useCallback(() => {
24
+ setSelectedNav(target);
25
+ }, [setSelectedNav, target]);
26
+
22
27
  return (
23
28
  <li className="nav-item">
24
29
  <button
@@ -33,9 +38,7 @@ export const EventNav: FC<EventNavProps> = ({
33
38
  "text-size-small",
34
39
  styles.tab,
35
40
  )}
36
- onClick={() => {
37
- setSelectedNav(target);
38
- }}
41
+ onClick={handleClick}
39
42
  >
40
43
  {title}
41
44
  </button>
@@ -1,9 +1,16 @@
1
1
  import clsx from "clsx";
2
- import { ReactNode } from "react";
2
+ import {
3
+ FC,
4
+ isValidElement,
5
+ ReactElement,
6
+ ReactNode,
7
+ useCallback,
8
+ } from "react";
3
9
  import { ApplicationIcons } from "../../../appearance/icons";
4
10
  import { EventNavs } from "./EventNavs";
5
11
 
6
- import React from "react";
12
+ import { ProgressBar } from "../../../components/ProgressBar";
13
+ import { useProperty } from "../../../state/hooks";
7
14
  import styles from "./EventPanel.module.css";
8
15
 
9
16
  interface EventPanelProps {
@@ -14,11 +21,8 @@ interface EventPanelProps {
14
21
  text?: string;
15
22
  icon?: string;
16
23
  collapse?: boolean;
17
- collapsed?: boolean;
18
- setCollapsed: (collapse: boolean) => void;
19
- selectedNav: string;
20
- setSelectedNav: (nav: string) => void;
21
24
  children?: ReactNode | ReactNode[];
25
+ running?: boolean;
22
26
  }
23
27
 
24
28
  interface ChildProps {
@@ -28,7 +32,7 @@ interface ChildProps {
28
32
  /**
29
33
  * Renders the StateEventView component.
30
34
  */
31
- export const EventPanel: React.FC<EventPanelProps> = ({
35
+ export const EventPanel: FC<EventPanelProps> = ({
32
36
  id,
33
37
  className,
34
38
  title,
@@ -36,23 +40,30 @@ export const EventPanel: React.FC<EventPanelProps> = ({
36
40
  text,
37
41
  icon,
38
42
  collapse,
39
- collapsed,
40
- setCollapsed,
41
43
  children,
42
- setSelectedNav,
43
- selectedNav,
44
+ running,
44
45
  }) => {
46
+ const [isCollapsed, setCollapsed] = useProperty(id, "collapsed", {
47
+ defaultValue: !!collapse,
48
+ });
49
+
45
50
  const hasCollapse = collapse !== undefined;
46
- const isCollapsed = collapsed === undefined ? collapse : collapsed;
47
51
 
48
52
  const pillId = (index: number) => {
49
53
  return `${id}-nav-pill-${index}`;
50
54
  };
51
-
52
55
  const filteredArrChildren = (
53
56
  Array.isArray(children) ? children : [children]
54
57
  ).filter((child) => !!child);
55
- const defaultPillId = pillId(0);
58
+
59
+ const defaultPill = filteredArrChildren.findIndex((node) => {
60
+ return hasDataDefault(node) && node.props["data-default"];
61
+ });
62
+ const defaultPillId = defaultPill !== -1 ? pillId(defaultPill) : pillId(0);
63
+
64
+ const [selectedNav, setSelectedNav] = useProperty(id, "selectedNav", {
65
+ defaultValue: defaultPillId,
66
+ });
56
67
 
57
68
  const gridColumns = [];
58
69
 
@@ -72,6 +83,10 @@ export const EventPanel: React.FC<EventPanelProps> = ({
72
83
  gridColumns.push("minmax(0, max-content)");
73
84
  gridColumns.push("minmax(0, max-content)");
74
85
 
86
+ const toggleCollapse = useCallback(() => {
87
+ setCollapsed(!isCollapsed);
88
+ }, [setCollapsed, isCollapsed]);
89
+
75
90
  const titleEl =
76
91
  title || icon || filteredArrChildren.length > 1 ? (
77
92
  <div
@@ -86,9 +101,7 @@ export const EventPanel: React.FC<EventPanelProps> = ({
86
101
  >
87
102
  {hasCollapse ? (
88
103
  <i
89
- onClick={() => {
90
- setCollapsed(!isCollapsed);
91
- }}
104
+ onClick={toggleCollapse}
92
105
  className={
93
106
  isCollapsed
94
107
  ? ApplicationIcons.chevron.right
@@ -104,33 +117,23 @@ export const EventPanel: React.FC<EventPanelProps> = ({
104
117
  icon || ApplicationIcons.metadata,
105
118
  "text-style-secondary",
106
119
  )}
107
- onClick={() => {
108
- setCollapsed(!isCollapsed);
109
- }}
120
+ onClick={toggleCollapse}
110
121
  />
111
122
  ) : (
112
123
  ""
113
124
  )}
114
125
  <div
115
126
  className={clsx("text-style-secondary", "text-style-label")}
116
- onClick={() => {
117
- setCollapsed(!isCollapsed);
118
- }}
127
+ onClick={toggleCollapse}
119
128
  >
120
129
  {title}
121
130
  </div>
122
- <div
123
- onClick={() => {
124
- setCollapsed(!isCollapsed);
125
- }}
126
- ></div>
131
+ <div onClick={toggleCollapse}></div>
127
132
  <div
128
133
  className={clsx("text-style-secondary", styles.label)}
129
- onClick={() => {
130
- setCollapsed(!isCollapsed);
131
- }}
134
+ onClick={toggleCollapse}
132
135
  >
133
- {collapsed ? text : ""}
136
+ {isCollapsed ? text : ""}
134
137
  </div>
135
138
  <div className={styles.navs}>
136
139
  {(!hasCollapse || !isCollapsed) &&
@@ -140,7 +143,7 @@ export const EventPanel: React.FC<EventPanelProps> = ({
140
143
  navs={filteredArrChildren.map((child, index) => {
141
144
  const defaultTitle = `Tab ${index}`;
142
145
  const title =
143
- child && React.isValidElement<ChildProps>(child)
146
+ child && isValidElement<ChildProps>(child)
144
147
  ? (child.props as ChildProps)["data-name"] || defaultTitle
145
148
  : defaultTitle;
146
149
  return {
@@ -149,7 +152,7 @@ export const EventPanel: React.FC<EventPanelProps> = ({
149
152
  target: pillId(index),
150
153
  };
151
154
  })}
152
- selectedNav={selectedNav || defaultPillId}
155
+ selectedNav={selectedNav}
153
156
  setSelectedNav={setSelectedNav}
154
157
  />
155
158
  ) : (
@@ -162,33 +165,51 @@ export const EventPanel: React.FC<EventPanelProps> = ({
162
165
  );
163
166
 
164
167
  const card = (
165
- <div id={id} className={clsx(className, styles.card)}>
166
- {titleEl}
167
- <div
168
- className={clsx(
169
- "tab-content",
170
- styles.cardContent,
171
- hasCollapse && isCollapsed ? styles.hidden : undefined,
172
- )}
173
- >
174
- {filteredArrChildren?.map((child, index) => {
175
- const id = pillId(index);
176
- const isSelected = selectedNav
177
- ? id === selectedNav
178
- : id === defaultPillId;
179
-
180
- return (
181
- <div
182
- key={`children-${id}-${index}`}
183
- id={id}
184
- className={clsx("tab-pane", "show", isSelected ? "active" : "")}
185
- >
186
- {child}
187
- </div>
188
- );
189
- })}
168
+ <>
169
+ <div id={id} className={clsx(className, styles.card)}>
170
+ {titleEl}
171
+ <div
172
+ className={clsx(
173
+ "tab-content",
174
+ styles.cardContent,
175
+ hasCollapse && isCollapsed ? styles.hidden : undefined,
176
+ )}
177
+ >
178
+ {filteredArrChildren?.map((child, index) => {
179
+ const id = pillId(index);
180
+ const isSelected = id === selectedNav;
181
+
182
+ return (
183
+ <div
184
+ key={`children-${id}-${index}`}
185
+ id={id}
186
+ className={clsx("tab-pane", "show", isSelected ? "active" : "")}
187
+ >
188
+ {child}
189
+ </div>
190
+ );
191
+ })}
192
+ </div>
190
193
  </div>
191
- </div>
194
+ <ProgressBar animating={!!running} />
195
+ </>
192
196
  );
193
197
  return card;
194
198
  };
199
+
200
+ // Typeguard for reading default value from pills
201
+ interface DataDefaultProps {
202
+ "data-default"?: boolean;
203
+ [key: string]: any;
204
+ }
205
+
206
+ function hasDataDefault(
207
+ node: ReactNode,
208
+ ): node is ReactElement<DataDefaultProps> {
209
+ return (
210
+ isValidElement(node) &&
211
+ node.props !== null &&
212
+ typeof node.props === "object" &&
213
+ "data-default" in node.props
214
+ );
215
+ }
@@ -0,0 +1,23 @@
1
+ .panel {
2
+ display: flex;
3
+ justify-content: center;
4
+ padding-top: 1em;
5
+ padding-bottom: 1em;
6
+ }
7
+
8
+ .container {
9
+ display: grid;
10
+ grid-template-columns: max-content max-content;
11
+ column-gap: 0.3em;
12
+ }
13
+
14
+ .spinner {
15
+ height: 15px;
16
+ width: 14px;
17
+ border-width: 2px;
18
+ color: var(--bs-secondary);
19
+ }
20
+
21
+ .text {
22
+ margin-top: -2px;
23
+ }
@@ -0,0 +1,27 @@
1
+ import clsx from "clsx";
2
+ import { FC } from "react";
3
+
4
+ import styles from "./EventProgressPanel.module.css";
5
+
6
+ interface EventProgressPanelProps {
7
+ text: string;
8
+ }
9
+
10
+ export const EventProgressPanel: FC<EventProgressPanelProps> = ({ text }) => {
11
+ return (
12
+ <div className={clsx(styles.panel)}>
13
+ <div className={clsx(styles.container)}>
14
+ <Spinner />
15
+ <div className={clsx("text-size-smaller", styles.text)}>{text}</div>
16
+ </div>
17
+ </div>
18
+ );
19
+ };
20
+
21
+ const Spinner: FC = () => {
22
+ return (
23
+ <div className={clsx(styles.spinner, "spinner-border")} role="status">
24
+ <span className={clsx("visually-hidden")}>generating...</span>
25
+ </div>
26
+ );
27
+ };
@@ -17,7 +17,8 @@ interface Signature {
17
17
 
18
18
  interface ChangeType {
19
19
  type: string;
20
- signature: Signature;
20
+ signature?: Signature;
21
+ match?: (changes: JsonChange[]) => boolean;
21
22
  render: (
22
23
  changes: JsonChange[],
23
24
  state: Record<string, unknown>,
@@ -211,8 +212,35 @@ const renderTools = (
211
212
  );
212
213
  };
213
214
 
215
+ const createMessageRenderer = (name: string, role: string): ChangeType => {
216
+ return {
217
+ type: name,
218
+ match: (changes: JsonChange[]) => {
219
+ if (changes.length === 1) {
220
+ const change = changes[0];
221
+ if (change.op === "add" && change.path.match(/\/messages\/\d+/)) {
222
+ return change.value["role"] === role;
223
+ }
224
+ }
225
+ return false;
226
+ },
227
+ render: (changes) => {
228
+ const message = changes[0].value as unknown;
229
+ return (
230
+ <ChatView
231
+ key="system_msg_event_preview"
232
+ id="system_msg_event_preview"
233
+ messages={[message] as Messages}
234
+ />
235
+ );
236
+ },
237
+ };
238
+ };
239
+
214
240
  export const RenderableChangeTypes: ChangeType[] = [
215
241
  system_msg_added_sig,
242
+ createMessageRenderer("assistant_msg", "assistant"),
243
+ createMessageRenderer("user_msg", "user"),
216
244
  use_tools,
217
245
  add_tools,
218
246
  ];
@@ -10,7 +10,6 @@ import {
10
10
  } from "../../../types/log";
11
11
  import { formatDateTime } from "../../../utils/format";
12
12
  import { EventPanel } from "../event/EventPanel";
13
- import { TranscriptEventState } from "../types";
14
13
  import { StateDiffView } from "./StateDiffView";
15
14
  import {
16
15
  RenderableChangeTypes,
@@ -23,8 +22,6 @@ import styles from "./StateEventView.module.css";
23
22
  interface StateEventViewProps {
24
23
  id: string;
25
24
  event: StateEvent | StoreEvent;
26
- eventState: TranscriptEventState;
27
- setEventState: (state: TranscriptEventState) => void;
28
25
  isStore?: boolean;
29
26
  className?: string | string[];
30
27
  }
@@ -35,8 +32,6 @@ interface StateEventViewProps {
35
32
  export const StateEventView: FC<StateEventViewProps> = ({
36
33
  id,
37
34
  event,
38
- eventState,
39
- setEventState,
40
35
  isStore = false,
41
36
  className,
42
37
  }) => {
@@ -46,7 +41,15 @@ export const StateEventView: FC<StateEventViewProps> = ({
46
41
 
47
42
  // Synthesize objects for comparison
48
43
  const [before, after] = useMemo(() => {
49
- return synthesizeComparable(event.changes);
44
+ try {
45
+ return synthesizeComparable(event.changes);
46
+ } catch (e) {
47
+ console.error(
48
+ "Unable to synthesize comparable object to display state diffs.",
49
+ e,
50
+ );
51
+ return [{}, {}];
52
+ }
50
53
  }, [event.changes]);
51
54
 
52
55
  // This clone is important since the state is used by react as potential values that are rendered
@@ -66,14 +69,6 @@ export const StateEventView: FC<StateEventViewProps> = ({
66
69
  subTitle={formatDateTime(new Date(event.timestamp))}
67
70
  text={!changePreview ? summary : undefined}
68
71
  collapse={changePreview === undefined ? true : undefined}
69
- selectedNav={eventState.selectedNav || ""}
70
- setSelectedNav={(selectedNav) => {
71
- setEventState({ ...eventState, selectedNav });
72
- }}
73
- collapsed={eventState.collapsed}
74
- setCollapsed={(collapsed) => {
75
- setEventState({ ...eventState, collapsed });
76
- }}
77
72
  >
78
73
  {changePreview ? (
79
74
  <div data-name="Summary" className={clsx(styles.summary)}>
@@ -103,59 +98,71 @@ const generatePreview = (
103
98
  ...RenderableChangeTypes,
104
99
  ...(isStore ? StoreSpecificRenderableTypes : []),
105
100
  ]) {
106
- // Note that we currently only have renderers that depend upon
107
- // add, remove, replace, but we should likely add
108
- // move, copy, test
109
- const requiredMatchCount =
110
- changeType.signature.remove.length +
111
- changeType.signature.replace.length +
112
- changeType.signature.add.length;
113
- let matchingOps = 0;
114
- for (const change of changes) {
115
- const op = change.op;
116
- switch (op) {
117
- case "add":
118
- if (changeType.signature.add && changeType.signature.add.length > 0) {
119
- changeType.signature.add.forEach((signature) => {
120
- if (change.path.match(signature)) {
121
- matchingOps++;
122
- }
123
- });
124
- }
125
- break;
126
- case "remove":
127
- if (
128
- changeType.signature.remove &&
129
- changeType.signature.remove.length > 0
130
- ) {
131
- changeType.signature.remove.forEach((signature) => {
132
- if (change.path.match(signature)) {
133
- matchingOps++;
134
- }
135
- });
136
- }
137
- break;
138
- case "replace":
139
- if (
140
- changeType.signature.replace &&
141
- changeType.signature.replace.length > 0
142
- ) {
143
- changeType.signature.replace.forEach((signature) => {
144
- if (change.path.match(signature)) {
145
- matchingOps++;
146
- }
147
- });
148
- }
149
- break;
101
+ if (changeType.signature) {
102
+ // Note that we currently only have renderers that depend upon
103
+ // add, remove, replace, but we should likely add
104
+ // move, copy, test
105
+ const requiredMatchCount =
106
+ changeType.signature.remove.length +
107
+ changeType.signature.replace.length +
108
+ changeType.signature.add.length;
109
+ let matchingOps = 0;
110
+ for (const change of changes) {
111
+ const op = change.op;
112
+ switch (op) {
113
+ case "add":
114
+ if (
115
+ changeType.signature.add &&
116
+ changeType.signature.add.length > 0
117
+ ) {
118
+ changeType.signature.add.forEach((signature) => {
119
+ if (change.path.match(signature)) {
120
+ matchingOps++;
121
+ }
122
+ });
123
+ }
124
+ break;
125
+ case "remove":
126
+ if (
127
+ changeType.signature.remove &&
128
+ changeType.signature.remove.length > 0
129
+ ) {
130
+ changeType.signature.remove.forEach((signature) => {
131
+ if (change.path.match(signature)) {
132
+ matchingOps++;
133
+ }
134
+ });
135
+ }
136
+ break;
137
+ case "replace":
138
+ if (
139
+ changeType.signature.replace &&
140
+ changeType.signature.replace.length > 0
141
+ ) {
142
+ changeType.signature.replace.forEach((signature) => {
143
+ if (change.path.match(signature)) {
144
+ matchingOps++;
145
+ }
146
+ });
147
+ }
148
+ break;
149
+ }
150
+ }
151
+ if (matchingOps === requiredMatchCount) {
152
+ const el = changeType.render(changes, resolvedState);
153
+ results.push(el);
154
+ // Only one renderer can process a change
155
+ // TODO: consider changing this to allow many handlers to render (though then we sort of need
156
+ // to match the renderer to the key (e.g. a rendered for `tool_choice` a renderer for `tools` etc..))
157
+ break;
158
+ }
159
+ } else if (changeType.match) {
160
+ const matches = changeType.match(changes);
161
+ if (matches) {
162
+ const el = changeType.render(changes, resolvedState);
163
+ results.push(el);
164
+ break;
150
165
  }
151
- }
152
- if (matchingOps === requiredMatchCount) {
153
- const el = changeType.render(changes, resolvedState);
154
- results.push(el);
155
- // Only one renderer can process a change
156
- // TODO: consider changing this to allow many handlers to render (though then we sort of need
157
- // to match the renderer to the key (e.g. a rendered for `tool_choice` a renderer for `tools` etc..))
158
- break;
159
166
  }
160
167
  }
161
168
  return results.length > 0 ? results : undefined;
@@ -269,21 +276,44 @@ function setPath(
269
276
  value: unknown,
270
277
  ): void {
271
278
  const keys = parsePath(path);
272
- let current: Record<string, unknown> = target;
279
+ let current: Record<string, unknown> | unknown[] = target;
273
280
 
274
281
  for (let i = 0; i < keys.length - 1; i++) {
275
282
  const key = keys[i];
276
- if (!(key in current)) {
277
- // If the next key is a number, create an array, otherwise an object
278
- current[key] = isArrayIndex(keys[i + 1]) ? [] : {};
283
+
284
+ if (Array.isArray(current)) {
285
+ const numericIndex = getIndex(key);
286
+ current[numericIndex] = isArrayIndex(keys[i + 1]) ? [] : {};
287
+ current = current[numericIndex] as
288
+ | Record<string, unknown>
289
+ | Array<unknown>;
290
+ } else {
291
+ if (!(key in current)) {
292
+ // If the next key is a number, create an array, otherwise an object
293
+ current[key] = isArrayIndex(keys[i + 1]) ? [] : {};
294
+ }
295
+ current = current[key] as Record<string, unknown> | Array<unknown>;
279
296
  }
280
- current = current[key] as Record<string, unknown>;
281
297
  }
282
298
 
283
299
  const lastKey = keys[keys.length - 1];
284
- current[lastKey] = value;
300
+ if (Array.isArray(current)) {
301
+ const numericIndex = getIndex(lastKey);
302
+ current[numericIndex] = value;
303
+ } else {
304
+ current[lastKey] = value;
305
+ }
285
306
  }
286
307
 
308
+ const getIndex = (key: string): number => {
309
+ const numericIndex = isArrayIndex(key) ? parseInt(key) : undefined;
310
+ if (numericIndex === undefined) {
311
+ throw new Error(`The key ${key} isn't a valid Array index!`);
312
+ }
313
+
314
+ return numericIndex;
315
+ };
316
+
287
317
  /**
288
318
  * Places structure in an object (without placing values)
289
319
  */
@@ -0,0 +1,87 @@
1
+ import { EvalSummary, SampleSummary } from "../api/types";
2
+ import { EvalResults } from "../types/log";
3
+
4
+ export interface ScorerInfo {
5
+ name: string;
6
+ scorer: string;
7
+ }
8
+
9
+ /**
10
+ * Extracts scorer information from evaluation results
11
+ */
12
+ const getScorersFromResults = (results?: EvalResults): ScorerInfo[] => {
13
+ if (!results?.scores) {
14
+ return [];
15
+ }
16
+
17
+ return results.scores.reduce((uniqueScorers, score) => {
18
+ const isDuplicate = uniqueScorers.some(
19
+ (existing) =>
20
+ existing.scorer === score.scorer && existing.name === score.name,
21
+ );
22
+
23
+ if (!isDuplicate) {
24
+ uniqueScorers.push({
25
+ name: score.name,
26
+ scorer: score.scorer,
27
+ });
28
+ }
29
+
30
+ return uniqueScorers;
31
+ }, [] as ScorerInfo[]);
32
+ };
33
+
34
+ /**
35
+ * Extracts scorer information from sample summaries
36
+ */
37
+ const getScorersFromSamples = (samples: SampleSummary[]): ScorerInfo[] => {
38
+ // Find a sample with scores
39
+ const scoredSample = samples.find((sample) => {
40
+ return !!sample.scores;
41
+ });
42
+
43
+ return Object.keys(scoredSample?.scores || {}).map((key) => ({
44
+ name: key,
45
+ scorer: key,
46
+ }));
47
+ };
48
+
49
+ /**
50
+ * Gets all available scorers for a log, prioritizing results over samples
51
+ */
52
+ export const getAvailableScorers = (
53
+ log: EvalSummary,
54
+ sampleSummaries: SampleSummary[],
55
+ ): ScorerInfo[] | undefined => {
56
+ const resultScorers = log.results ? getScorersFromResults(log.results) : [];
57
+ if (resultScorers.length > 0) {
58
+ return resultScorers;
59
+ }
60
+
61
+ const sampleScorers = getScorersFromSamples(sampleSummaries);
62
+ if (sampleScorers.length > 0) {
63
+ return sampleScorers;
64
+ }
65
+
66
+ return undefined;
67
+ };
68
+
69
+ /**
70
+ * Gets the default scorer to use, preferring the first scorer from results
71
+ * or falling back to the first scorer from samples
72
+ */
73
+ export const getDefaultScorer = (
74
+ log: EvalSummary,
75
+ sampleSummaries: SampleSummary[],
76
+ ): ScorerInfo | undefined => {
77
+ if (sampleSummaries.length === 0) {
78
+ return undefined;
79
+ }
80
+
81
+ const allScorers = getAvailableScorers(log, sampleSummaries);
82
+ if (allScorers) {
83
+ return allScorers[0];
84
+ } else {
85
+ return undefined;
86
+ }
87
+ };