inspect-ai 0.3.80__py3-none-any.whl → 0.3.82__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. inspect_ai/_cli/eval.py +35 -2
  2. inspect_ai/_cli/util.py +44 -1
  3. inspect_ai/_display/core/config.py +1 -1
  4. inspect_ai/_display/core/display.py +13 -4
  5. inspect_ai/_display/core/results.py +1 -1
  6. inspect_ai/_display/textual/widgets/task_detail.py +5 -4
  7. inspect_ai/_eval/eval.py +38 -1
  8. inspect_ai/_eval/evalset.py +5 -0
  9. inspect_ai/_eval/run.py +5 -2
  10. inspect_ai/_eval/task/log.py +53 -6
  11. inspect_ai/_eval/task/run.py +51 -10
  12. inspect_ai/_util/constants.py +2 -0
  13. inspect_ai/_util/file.py +17 -1
  14. inspect_ai/_util/json.py +36 -1
  15. inspect_ai/_view/server.py +113 -1
  16. inspect_ai/_view/www/App.css +1 -1
  17. inspect_ai/_view/www/dist/assets/index.css +518 -296
  18. inspect_ai/_view/www/dist/assets/index.js +38803 -36307
  19. inspect_ai/_view/www/eslint.config.mjs +1 -1
  20. inspect_ai/_view/www/log-schema.json +13 -0
  21. inspect_ai/_view/www/node_modules/flatted/python/flatted.py +149 -0
  22. inspect_ai/_view/www/package.json +8 -2
  23. inspect_ai/_view/www/src/App.tsx +151 -855
  24. inspect_ai/_view/www/src/api/api-browser.ts +176 -5
  25. inspect_ai/_view/www/src/api/api-vscode.ts +75 -1
  26. inspect_ai/_view/www/src/api/client-api.ts +66 -10
  27. inspect_ai/_view/www/src/api/jsonrpc.ts +2 -0
  28. inspect_ai/_view/www/src/api/types.ts +107 -2
  29. inspect_ai/_view/www/src/appearance/icons.ts +1 -0
  30. inspect_ai/_view/www/src/components/AsciinemaPlayer.tsx +3 -3
  31. inspect_ai/_view/www/src/components/DownloadPanel.tsx +2 -2
  32. inspect_ai/_view/www/src/components/ExpandablePanel.tsx +56 -61
  33. inspect_ai/_view/www/src/components/FindBand.tsx +17 -9
  34. inspect_ai/_view/www/src/components/HumanBaselineView.tsx +1 -1
  35. inspect_ai/_view/www/src/components/JsonPanel.tsx +14 -24
  36. inspect_ai/_view/www/src/components/LargeModal.tsx +2 -35
  37. inspect_ai/_view/www/src/components/LightboxCarousel.tsx +27 -11
  38. inspect_ai/_view/www/src/components/LiveVirtualList.module.css +11 -0
  39. inspect_ai/_view/www/src/components/LiveVirtualList.tsx +177 -0
  40. inspect_ai/_view/www/src/components/MarkdownDiv.tsx +3 -3
  41. inspect_ai/_view/www/src/components/MessageBand.tsx +14 -9
  42. inspect_ai/_view/www/src/components/MorePopOver.tsx +3 -3
  43. inspect_ai/_view/www/src/components/NavPills.tsx +20 -8
  44. inspect_ai/_view/www/src/components/NoContentsPanel.module.css +12 -0
  45. inspect_ai/_view/www/src/components/NoContentsPanel.tsx +20 -0
  46. inspect_ai/_view/www/src/components/ProgressBar.module.css +5 -4
  47. inspect_ai/_view/www/src/components/ProgressBar.tsx +3 -2
  48. inspect_ai/_view/www/src/components/PulsingDots.module.css +81 -0
  49. inspect_ai/_view/www/src/components/PulsingDots.tsx +45 -0
  50. inspect_ai/_view/www/src/components/TabSet.tsx +4 -37
  51. inspect_ai/_view/www/src/components/ToolButton.tsx +3 -4
  52. inspect_ai/_view/www/src/index.tsx +26 -94
  53. inspect_ai/_view/www/src/logfile/remoteLogFile.ts +9 -1
  54. inspect_ai/_view/www/src/logfile/remoteZipFile.ts +30 -4
  55. inspect_ai/_view/www/src/metadata/RenderedContent.tsx +4 -6
  56. inspect_ai/_view/www/src/plan/ScorerDetailView.tsx +1 -1
  57. inspect_ai/_view/www/src/samples/InlineSampleDisplay.module.css +9 -1
  58. inspect_ai/_view/www/src/samples/InlineSampleDisplay.tsx +67 -28
  59. inspect_ai/_view/www/src/samples/SampleDialog.tsx +51 -22
  60. inspect_ai/_view/www/src/samples/SampleDisplay.module.css +4 -0
  61. inspect_ai/_view/www/src/samples/SampleDisplay.tsx +144 -90
  62. inspect_ai/_view/www/src/samples/SampleSummaryView.module.css +4 -0
  63. inspect_ai/_view/www/src/samples/SampleSummaryView.tsx +82 -35
  64. inspect_ai/_view/www/src/samples/SamplesTools.tsx +23 -30
  65. inspect_ai/_view/www/src/samples/chat/ChatMessage.tsx +2 -1
  66. inspect_ai/_view/www/src/samples/chat/ChatMessageRenderer.tsx +1 -1
  67. inspect_ai/_view/www/src/samples/chat/ChatViewVirtualList.tsx +45 -53
  68. inspect_ai/_view/www/src/samples/chat/MessageContent.tsx +4 -1
  69. inspect_ai/_view/www/src/samples/chat/MessageContents.tsx +3 -0
  70. inspect_ai/_view/www/src/samples/chat/messages.ts +34 -0
  71. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.module.css +3 -0
  72. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.tsx +10 -1
  73. inspect_ai/_view/www/src/samples/chat/tools/ToolInput.tsx +22 -46
  74. inspect_ai/_view/www/src/samples/descriptor/samplesDescriptor.tsx +25 -17
  75. inspect_ai/_view/www/src/samples/descriptor/score/ObjectScoreDescriptor.tsx +2 -1
  76. inspect_ai/_view/www/src/samples/descriptor/types.ts +6 -5
  77. inspect_ai/_view/www/src/samples/list/SampleFooter.module.css +21 -3
  78. inspect_ai/_view/www/src/samples/list/SampleFooter.tsx +20 -1
  79. inspect_ai/_view/www/src/samples/list/SampleList.tsx +105 -85
  80. inspect_ai/_view/www/src/samples/list/SampleRow.module.css +6 -0
  81. inspect_ai/_view/www/src/samples/list/SampleRow.tsx +27 -14
  82. inspect_ai/_view/www/src/samples/sample-tools/SelectScorer.tsx +29 -18
  83. inspect_ai/_view/www/src/samples/sample-tools/SortFilter.tsx +28 -28
  84. inspect_ai/_view/www/src/samples/sample-tools/sample-filter/SampleFilter.tsx +19 -9
  85. inspect_ai/_view/www/src/samples/sampleDataAdapter.ts +33 -0
  86. inspect_ai/_view/www/src/samples/sampleLimit.ts +2 -2
  87. inspect_ai/_view/www/src/samples/scores/SampleScoreView.tsx +7 -9
  88. inspect_ai/_view/www/src/samples/scores/SampleScores.tsx +7 -11
  89. inspect_ai/_view/www/src/samples/transcript/ErrorEventView.tsx +0 -13
  90. inspect_ai/_view/www/src/samples/transcript/InfoEventView.tsx +0 -13
  91. inspect_ai/_view/www/src/samples/transcript/InputEventView.tsx +0 -13
  92. inspect_ai/_view/www/src/samples/transcript/ModelEventView.module.css +4 -0
  93. inspect_ai/_view/www/src/samples/transcript/ModelEventView.tsx +10 -24
  94. inspect_ai/_view/www/src/samples/transcript/SampleInitEventView.tsx +0 -13
  95. inspect_ai/_view/www/src/samples/transcript/SampleLimitEventView.tsx +4 -22
  96. inspect_ai/_view/www/src/samples/transcript/SandboxEventView.tsx +15 -24
  97. inspect_ai/_view/www/src/samples/transcript/ScoreEventView.tsx +0 -13
  98. inspect_ai/_view/www/src/samples/transcript/StepEventView.tsx +6 -28
  99. inspect_ai/_view/www/src/samples/transcript/SubtaskEventView.tsx +24 -34
  100. inspect_ai/_view/www/src/samples/transcript/ToolEventView.module.css +4 -0
  101. inspect_ai/_view/www/src/samples/transcript/ToolEventView.tsx +8 -13
  102. inspect_ai/_view/www/src/samples/transcript/TranscriptView.tsx +197 -338
  103. inspect_ai/_view/www/src/samples/transcript/TranscriptVirtualListComponent.module.css +16 -0
  104. inspect_ai/_view/www/src/samples/transcript/TranscriptVirtualListComponent.tsx +44 -0
  105. inspect_ai/_view/www/src/samples/transcript/event/EventNav.tsx +7 -4
  106. inspect_ai/_view/www/src/samples/transcript/event/EventPanel.tsx +52 -58
  107. inspect_ai/_view/www/src/samples/transcript/event/EventProgressPanel.module.css +23 -0
  108. inspect_ai/_view/www/src/samples/transcript/event/EventProgressPanel.tsx +27 -0
  109. inspect_ai/_view/www/src/samples/transcript/state/StateEventRenderers.tsx +30 -1
  110. inspect_ai/_view/www/src/samples/transcript/state/StateEventView.tsx +102 -72
  111. inspect_ai/_view/www/src/scoring/utils.ts +87 -0
  112. inspect_ai/_view/www/src/state/appSlice.ts +244 -0
  113. inspect_ai/_view/www/src/state/hooks.ts +397 -0
  114. inspect_ai/_view/www/src/state/logPolling.ts +196 -0
  115. inspect_ai/_view/www/src/state/logSlice.ts +214 -0
  116. inspect_ai/_view/www/src/state/logsPolling.ts +118 -0
  117. inspect_ai/_view/www/src/state/logsSlice.ts +181 -0
  118. inspect_ai/_view/www/src/state/samplePolling.ts +311 -0
  119. inspect_ai/_view/www/src/state/sampleSlice.ts +127 -0
  120. inspect_ai/_view/www/src/state/sampleUtils.ts +21 -0
  121. inspect_ai/_view/www/src/state/scrolling.ts +206 -0
  122. inspect_ai/_view/www/src/state/store.ts +168 -0
  123. inspect_ai/_view/www/src/state/store_filter.ts +84 -0
  124. inspect_ai/_view/www/src/state/utils.ts +23 -0
  125. inspect_ai/_view/www/src/storage/index.ts +26 -0
  126. inspect_ai/_view/www/src/types/log.d.ts +2 -0
  127. inspect_ai/_view/www/src/types.ts +94 -32
  128. inspect_ai/_view/www/src/utils/attachments.ts +58 -23
  129. inspect_ai/_view/www/src/utils/logger.ts +52 -0
  130. inspect_ai/_view/www/src/utils/polling.ts +100 -0
  131. inspect_ai/_view/www/src/utils/react.ts +30 -0
  132. inspect_ai/_view/www/src/utils/vscode.ts +1 -1
  133. inspect_ai/_view/www/src/workspace/WorkSpace.tsx +181 -216
  134. inspect_ai/_view/www/src/workspace/WorkSpaceView.tsx +11 -53
  135. inspect_ai/_view/www/src/workspace/navbar/Navbar.tsx +8 -18
  136. inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.module.css +1 -0
  137. inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.tsx +40 -22
  138. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.module.css +0 -1
  139. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.tsx +98 -39
  140. inspect_ai/_view/www/src/workspace/navbar/RunningStatusPanel.module.css +32 -0
  141. inspect_ai/_view/www/src/workspace/navbar/RunningStatusPanel.tsx +32 -0
  142. inspect_ai/_view/www/src/workspace/navbar/SecondaryBar.tsx +11 -13
  143. inspect_ai/_view/www/src/workspace/navbar/StatusPanel.tsx +6 -2
  144. inspect_ai/_view/www/src/workspace/sidebar/LogDirectoryTitleView.tsx +4 -4
  145. inspect_ai/_view/www/src/workspace/sidebar/Sidebar.tsx +28 -13
  146. inspect_ai/_view/www/src/workspace/tabs/InfoTab.tsx +5 -10
  147. inspect_ai/_view/www/src/workspace/tabs/JsonTab.tsx +4 -4
  148. inspect_ai/_view/www/src/workspace/tabs/RunningNoSamples.module.css +22 -0
  149. inspect_ai/_view/www/src/workspace/tabs/RunningNoSamples.tsx +19 -0
  150. inspect_ai/_view/www/src/workspace/tabs/SamplesTab.tsx +110 -115
  151. inspect_ai/_view/www/src/workspace/tabs/grouping.ts +37 -5
  152. inspect_ai/_view/www/src/workspace/tabs/types.ts +4 -0
  153. inspect_ai/_view/www/src/workspace/types.ts +4 -3
  154. inspect_ai/_view/www/src/workspace/utils.ts +4 -4
  155. inspect_ai/_view/www/vite.config.js +6 -0
  156. inspect_ai/_view/www/yarn.lock +370 -354
  157. inspect_ai/log/_condense.py +26 -0
  158. inspect_ai/log/_log.py +6 -3
  159. inspect_ai/log/_recorders/buffer/__init__.py +14 -0
  160. inspect_ai/log/_recorders/buffer/buffer.py +30 -0
  161. inspect_ai/log/_recorders/buffer/database.py +685 -0
  162. inspect_ai/log/_recorders/buffer/filestore.py +259 -0
  163. inspect_ai/log/_recorders/buffer/types.py +84 -0
  164. inspect_ai/log/_recorders/eval.py +2 -11
  165. inspect_ai/log/_recorders/types.py +30 -0
  166. inspect_ai/log/_transcript.py +27 -1
  167. inspect_ai/model/_call_tools.py +1 -0
  168. inspect_ai/model/_generate_config.py +2 -2
  169. inspect_ai/model/_model.py +1 -0
  170. inspect_ai/tool/_tool_support_helpers.py +4 -4
  171. inspect_ai/tool/_tools/_web_browser/_web_browser.py +3 -1
  172. inspect_ai/util/_subtask.py +1 -0
  173. {inspect_ai-0.3.80.dist-info → inspect_ai-0.3.82.dist-info}/METADATA +2 -2
  174. {inspect_ai-0.3.80.dist-info → inspect_ai-0.3.82.dist-info}/RECORD +178 -138
  175. inspect_ai/_view/www/src/samples/transcript/SampleTranscript.tsx +0 -22
  176. {inspect_ai-0.3.80.dist-info → inspect_ai-0.3.82.dist-info}/WHEEL +0 -0
  177. {inspect_ai-0.3.80.dist-info → inspect_ai-0.3.82.dist-info}/entry_points.txt +0 -0
  178. {inspect_ai-0.3.80.dist-info → inspect_ai-0.3.82.dist-info}/licenses/LICENSE +0 -0
  179. {inspect_ai-0.3.80.dist-info → inspect_ai-0.3.82.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,10 @@
1
1
  import clsx from "clsx";
2
- import { ReactNode } from "react";
2
+ import { FC, isValidElement, ReactNode, useCallback } from "react";
3
3
  import { ApplicationIcons } from "../../../appearance/icons";
4
4
  import { EventNavs } from "./EventNavs";
5
5
 
6
- import React from "react";
6
+ import { ProgressBar } from "../../../components/ProgressBar";
7
+ import { useProperty } from "../../../state/hooks";
7
8
  import styles from "./EventPanel.module.css";
8
9
 
9
10
  interface EventPanelProps {
@@ -14,11 +15,8 @@ interface EventPanelProps {
14
15
  text?: string;
15
16
  icon?: string;
16
17
  collapse?: boolean;
17
- collapsed?: boolean;
18
- setCollapsed: (collapse: boolean) => void;
19
- selectedNav: string;
20
- setSelectedNav: (nav: string) => void;
21
18
  children?: ReactNode | ReactNode[];
19
+ running?: boolean;
22
20
  }
23
21
 
24
22
  interface ChildProps {
@@ -28,7 +26,7 @@ interface ChildProps {
28
26
  /**
29
27
  * Renders the StateEventView component.
30
28
  */
31
- export const EventPanel: React.FC<EventPanelProps> = ({
29
+ export const EventPanel: FC<EventPanelProps> = ({
32
30
  id,
33
31
  className,
34
32
  title,
@@ -36,24 +34,27 @@ export const EventPanel: React.FC<EventPanelProps> = ({
36
34
  text,
37
35
  icon,
38
36
  collapse,
39
- collapsed,
40
- setCollapsed,
41
37
  children,
42
- setSelectedNav,
43
- selectedNav,
38
+ running,
44
39
  }) => {
40
+ const [isCollapsed, setCollapsed] = useProperty(id, "collapsed", {
41
+ defaultValue: !!collapse,
42
+ });
43
+
45
44
  const hasCollapse = collapse !== undefined;
46
- const isCollapsed = collapsed === undefined ? collapse : collapsed;
47
45
 
48
46
  const pillId = (index: number) => {
49
47
  return `${id}-nav-pill-${index}`;
50
48
  };
51
-
52
49
  const filteredArrChildren = (
53
50
  Array.isArray(children) ? children : [children]
54
51
  ).filter((child) => !!child);
55
52
  const defaultPillId = pillId(0);
56
53
 
54
+ const [selectedNav, setSelectedNav] = useProperty(id, "selectedNav", {
55
+ defaultValue: defaultPillId,
56
+ });
57
+
57
58
  const gridColumns = [];
58
59
 
59
60
  // chevron
@@ -72,6 +73,10 @@ export const EventPanel: React.FC<EventPanelProps> = ({
72
73
  gridColumns.push("minmax(0, max-content)");
73
74
  gridColumns.push("minmax(0, max-content)");
74
75
 
76
+ const toggleCollapse = useCallback(() => {
77
+ setCollapsed(!isCollapsed);
78
+ }, [setCollapsed, isCollapsed]);
79
+
75
80
  const titleEl =
76
81
  title || icon || filteredArrChildren.length > 1 ? (
77
82
  <div
@@ -86,9 +91,7 @@ export const EventPanel: React.FC<EventPanelProps> = ({
86
91
  >
87
92
  {hasCollapse ? (
88
93
  <i
89
- onClick={() => {
90
- setCollapsed(!isCollapsed);
91
- }}
94
+ onClick={toggleCollapse}
92
95
  className={
93
96
  isCollapsed
94
97
  ? ApplicationIcons.chevron.right
@@ -104,33 +107,23 @@ export const EventPanel: React.FC<EventPanelProps> = ({
104
107
  icon || ApplicationIcons.metadata,
105
108
  "text-style-secondary",
106
109
  )}
107
- onClick={() => {
108
- setCollapsed(!isCollapsed);
109
- }}
110
+ onClick={toggleCollapse}
110
111
  />
111
112
  ) : (
112
113
  ""
113
114
  )}
114
115
  <div
115
116
  className={clsx("text-style-secondary", "text-style-label")}
116
- onClick={() => {
117
- setCollapsed(!isCollapsed);
118
- }}
117
+ onClick={toggleCollapse}
119
118
  >
120
119
  {title}
121
120
  </div>
122
- <div
123
- onClick={() => {
124
- setCollapsed(!isCollapsed);
125
- }}
126
- ></div>
121
+ <div onClick={toggleCollapse}></div>
127
122
  <div
128
123
  className={clsx("text-style-secondary", styles.label)}
129
- onClick={() => {
130
- setCollapsed(!isCollapsed);
131
- }}
124
+ onClick={toggleCollapse}
132
125
  >
133
- {collapsed ? text : ""}
126
+ {isCollapsed ? text : ""}
134
127
  </div>
135
128
  <div className={styles.navs}>
136
129
  {(!hasCollapse || !isCollapsed) &&
@@ -140,7 +133,7 @@ export const EventPanel: React.FC<EventPanelProps> = ({
140
133
  navs={filteredArrChildren.map((child, index) => {
141
134
  const defaultTitle = `Tab ${index}`;
142
135
  const title =
143
- child && React.isValidElement<ChildProps>(child)
136
+ child && isValidElement<ChildProps>(child)
144
137
  ? (child.props as ChildProps)["data-name"] || defaultTitle
145
138
  : defaultTitle;
146
139
  return {
@@ -149,7 +142,7 @@ export const EventPanel: React.FC<EventPanelProps> = ({
149
142
  target: pillId(index),
150
143
  };
151
144
  })}
152
- selectedNav={selectedNav || defaultPillId}
145
+ selectedNav={selectedNav}
153
146
  setSelectedNav={setSelectedNav}
154
147
  />
155
148
  ) : (
@@ -162,33 +155,34 @@ export const EventPanel: React.FC<EventPanelProps> = ({
162
155
  );
163
156
 
164
157
  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;
158
+ <>
159
+ <div id={id} className={clsx(className, styles.card)}>
160
+ {titleEl}
161
+ <div
162
+ className={clsx(
163
+ "tab-content",
164
+ styles.cardContent,
165
+ hasCollapse && isCollapsed ? styles.hidden : undefined,
166
+ )}
167
+ >
168
+ {filteredArrChildren?.map((child, index) => {
169
+ const id = pillId(index);
170
+ const isSelected = id === selectedNav;
179
171
 
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
- })}
172
+ return (
173
+ <div
174
+ key={`children-${id}-${index}`}
175
+ id={id}
176
+ className={clsx("tab-pane", "show", isSelected ? "active" : "")}
177
+ >
178
+ {child}
179
+ </div>
180
+ );
181
+ })}
182
+ </div>
190
183
  </div>
191
- </div>
184
+ <ProgressBar animating={!!running} />
185
+ </>
192
186
  );
193
187
  return card;
194
188
  };
@@ -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,36 @@ 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
+ console.log(changes);
220
+ if (changes.length === 1) {
221
+ const change = changes[0];
222
+ if (change.op === "add" && change.path.match(/\/messages\/\d+/)) {
223
+ return change.value["role"] === role;
224
+ }
225
+ }
226
+ return false;
227
+ },
228
+ render: (changes) => {
229
+ const message = changes[0].value as unknown;
230
+ return (
231
+ <ChatView
232
+ key="system_msg_event_preview"
233
+ id="system_msg_event_preview"
234
+ messages={[message] as Messages}
235
+ />
236
+ );
237
+ },
238
+ };
239
+ };
240
+
214
241
  export const RenderableChangeTypes: ChangeType[] = [
215
242
  system_msg_added_sig,
243
+ createMessageRenderer("assistant_msg", "assistant"),
244
+ createMessageRenderer("user_msg", "user"),
216
245
  use_tools,
217
246
  add_tools,
218
247
  ];
@@ -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
+ };