inspect-ai 0.3.63__py3-none-any.whl → 0.3.65__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 (182) hide show
  1. inspect_ai/_cli/cache.py +8 -7
  2. inspect_ai/_cli/common.py +0 -12
  3. inspect_ai/_cli/eval.py +32 -4
  4. inspect_ai/_cli/info.py +1 -0
  5. inspect_ai/_cli/list.py +1 -1
  6. inspect_ai/_cli/log.py +2 -0
  7. inspect_ai/_cli/sandbox.py +4 -1
  8. inspect_ai/_cli/score.py +181 -32
  9. inspect_ai/_cli/trace.py +2 -0
  10. inspect_ai/_cli/view.py +4 -2
  11. inspect_ai/_display/core/config.py +7 -1
  12. inspect_ai/_display/core/progress.py +1 -1
  13. inspect_ai/_display/textual/app.py +8 -4
  14. inspect_ai/_display/textual/widgets/samples.py +6 -5
  15. inspect_ai/_display/textual/widgets/sandbox.py +6 -0
  16. inspect_ai/_eval/__init__.py +0 -0
  17. inspect_ai/_eval/eval.py +100 -97
  18. inspect_ai/_eval/evalset.py +69 -69
  19. inspect_ai/_eval/loader.py +122 -12
  20. inspect_ai/_eval/registry.py +1 -1
  21. inspect_ai/_eval/run.py +14 -0
  22. inspect_ai/_eval/score.py +125 -36
  23. inspect_ai/_eval/task/log.py +105 -4
  24. inspect_ai/_eval/task/results.py +92 -38
  25. inspect_ai/_eval/task/run.py +6 -2
  26. inspect_ai/_eval/task/sandbox.py +35 -2
  27. inspect_ai/_eval/task/task.py +49 -46
  28. inspect_ai/_util/__init__.py +0 -0
  29. inspect_ai/_util/constants.py +1 -1
  30. inspect_ai/_util/content.py +8 -0
  31. inspect_ai/_util/error.py +2 -0
  32. inspect_ai/_util/file.py +15 -1
  33. inspect_ai/_util/logger.py +4 -2
  34. inspect_ai/_util/registry.py +7 -1
  35. inspect_ai/_view/view.py +1 -2
  36. inspect_ai/_view/www/App.css +8 -3
  37. inspect_ai/_view/www/README.md +1 -1
  38. inspect_ai/_view/www/dist/assets/index.css +66 -38
  39. inspect_ai/_view/www/dist/assets/index.js +525 -523
  40. inspect_ai/_view/www/log-schema.json +86 -73
  41. inspect_ai/_view/www/package.json +1 -1
  42. inspect_ai/_view/www/src/App.tsx +1 -0
  43. inspect_ai/_view/www/src/components/AnsiDisplay.tsx +1 -1
  44. inspect_ai/_view/www/src/components/JsonPanel.tsx +1 -1
  45. inspect_ai/_view/www/src/components/LargeModal.tsx +39 -49
  46. inspect_ai/_view/www/src/components/NavPills.tsx +3 -1
  47. inspect_ai/_view/www/src/components/TabSet.tsx +19 -4
  48. inspect_ai/_view/www/src/logfile/remoteLogFile.ts +0 -1
  49. inspect_ai/_view/www/src/metadata/MetaDataGrid.tsx +1 -1
  50. inspect_ai/_view/www/src/metadata/MetaDataView.tsx +1 -1
  51. inspect_ai/_view/www/src/metadata/RenderedContent.tsx +6 -13
  52. inspect_ai/_view/www/src/plan/PlanDetailView.tsx +17 -2
  53. inspect_ai/_view/www/src/plan/SolverDetailView.tsx +1 -1
  54. inspect_ai/_view/www/src/samples/SampleDisplay.tsx +14 -5
  55. inspect_ai/_view/www/src/samples/SampleSummaryView.tsx +4 -2
  56. inspect_ai/_view/www/src/samples/SamplesTools.tsx +16 -24
  57. inspect_ai/_view/www/src/samples/chat/ChatMessage.tsx +1 -1
  58. inspect_ai/_view/www/src/samples/chat/ChatView.tsx +1 -0
  59. inspect_ai/_view/www/src/samples/chat/MessageContent.tsx +27 -13
  60. inspect_ai/_view/www/src/samples/chat/MessageContents.tsx +19 -17
  61. inspect_ai/_view/www/src/samples/chat/tools/ToolCallView.tsx +12 -10
  62. inspect_ai/_view/www/src/samples/chat/tools/ToolInput.tsx +56 -66
  63. inspect_ai/_view/www/src/samples/chat/tools/ToolOutput.tsx +12 -5
  64. inspect_ai/_view/www/src/samples/chat/tools/tool.ts +21 -36
  65. inspect_ai/_view/www/src/samples/descriptor/samplesDescriptor.tsx +3 -1
  66. inspect_ai/_view/www/src/samples/sample-tools/SelectScorer.tsx +27 -25
  67. inspect_ai/_view/www/src/samples/sample-tools/SortFilter.tsx +5 -1
  68. inspect_ai/_view/www/src/samples/scores/SampleScoreView.module.css +13 -13
  69. inspect_ai/_view/www/src/samples/transcript/InfoEventView.tsx +1 -1
  70. inspect_ai/_view/www/src/samples/transcript/ModelEventView.tsx +2 -2
  71. inspect_ai/_view/www/src/samples/transcript/SampleInitEventView.tsx +9 -5
  72. inspect_ai/_view/www/src/samples/transcript/ScoreEventView.tsx +1 -1
  73. inspect_ai/_view/www/src/samples/transcript/ToolEventView.tsx +5 -4
  74. inspect_ai/_view/www/src/samples/transcript/event/EventNavs.tsx +1 -0
  75. inspect_ai/_view/www/src/samples/transcript/event/EventPanel.tsx +1 -0
  76. inspect_ai/_view/www/src/samples/transcript/state/StateEventRenderers.tsx +17 -6
  77. inspect_ai/_view/www/src/samples/transcript/state/StateEventView.tsx +14 -19
  78. inspect_ai/_view/www/src/types/log.d.ts +107 -19
  79. inspect_ai/_view/www/src/usage/ModelTokenTable.tsx +7 -1
  80. inspect_ai/_view/www/src/usage/ModelUsagePanel.tsx +5 -3
  81. inspect_ai/_view/www/src/workspace/WorkSpaceView.tsx +25 -27
  82. inspect_ai/_view/www/src/workspace/navbar/PrimaryBar.tsx +12 -11
  83. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.module.css +25 -2
  84. inspect_ai/_view/www/src/workspace/navbar/ResultsPanel.tsx +60 -36
  85. inspect_ai/_view/www/src/workspace/navbar/SecondaryBar.tsx +4 -0
  86. inspect_ai/_view/www/src/workspace/sidebar/SidebarScoreView.tsx +6 -4
  87. inspect_ai/_view/www/src/workspace/sidebar/SidebarScoresView.tsx +16 -14
  88. inspect_ai/_view/www/src/workspace/tabs/InfoTab.tsx +9 -19
  89. inspect_ai/_view/www/src/workspace/utils.ts +34 -0
  90. inspect_ai/approval/_approval.py +2 -0
  91. inspect_ai/approval/_approver.py +4 -4
  92. inspect_ai/approval/_auto.py +1 -1
  93. inspect_ai/approval/_human/approver.py +3 -0
  94. inspect_ai/approval/_policy.py +5 -0
  95. inspect_ai/approval/_registry.py +2 -2
  96. inspect_ai/dataset/_dataset.py +36 -45
  97. inspect_ai/dataset/_sources/__init__.py +0 -0
  98. inspect_ai/dataset/_sources/csv.py +13 -13
  99. inspect_ai/dataset/_sources/hf.py +29 -29
  100. inspect_ai/dataset/_sources/json.py +10 -10
  101. inspect_ai/log/__init__.py +2 -0
  102. inspect_ai/log/_convert.py +3 -3
  103. inspect_ai/log/_file.py +24 -9
  104. inspect_ai/log/_log.py +98 -7
  105. inspect_ai/log/_message.py +3 -1
  106. inspect_ai/log/_recorders/file.py +4 -0
  107. inspect_ai/log/_recorders/recorder.py +3 -0
  108. inspect_ai/log/_transcript.py +19 -8
  109. inspect_ai/model/__init__.py +2 -0
  110. inspect_ai/model/_cache.py +39 -21
  111. inspect_ai/model/_call_tools.py +2 -2
  112. inspect_ai/model/_chat_message.py +14 -4
  113. inspect_ai/model/_generate_config.py +1 -1
  114. inspect_ai/model/_model.py +31 -24
  115. inspect_ai/model/_model_output.py +14 -1
  116. inspect_ai/model/_openai.py +10 -18
  117. inspect_ai/model/_providers/google.py +9 -5
  118. inspect_ai/model/_providers/openai.py +5 -9
  119. inspect_ai/model/_providers/openrouter.py +1 -1
  120. inspect_ai/scorer/__init__.py +6 -1
  121. inspect_ai/scorer/_answer.py +1 -1
  122. inspect_ai/scorer/_classification.py +4 -0
  123. inspect_ai/scorer/_match.py +4 -5
  124. inspect_ai/scorer/_metric.py +87 -28
  125. inspect_ai/scorer/_metrics/__init__.py +3 -3
  126. inspect_ai/scorer/_metrics/accuracy.py +8 -10
  127. inspect_ai/scorer/_metrics/mean.py +3 -17
  128. inspect_ai/scorer/_metrics/std.py +111 -30
  129. inspect_ai/scorer/_model.py +12 -12
  130. inspect_ai/scorer/_pattern.py +3 -3
  131. inspect_ai/scorer/_reducer/reducer.py +36 -21
  132. inspect_ai/scorer/_reducer/registry.py +2 -2
  133. inspect_ai/scorer/_reducer/types.py +7 -1
  134. inspect_ai/scorer/_score.py +11 -1
  135. inspect_ai/scorer/_scorer.py +110 -16
  136. inspect_ai/solver/__init__.py +1 -1
  137. inspect_ai/solver/_basic_agent.py +19 -22
  138. inspect_ai/solver/_bridge/__init__.py +0 -3
  139. inspect_ai/solver/_bridge/bridge.py +3 -3
  140. inspect_ai/solver/_chain.py +1 -2
  141. inspect_ai/solver/_critique.py +3 -3
  142. inspect_ai/solver/_fork.py +2 -2
  143. inspect_ai/solver/_human_agent/__init__.py +0 -0
  144. inspect_ai/solver/_human_agent/agent.py +5 -8
  145. inspect_ai/solver/_human_agent/commands/clock.py +14 -10
  146. inspect_ai/solver/_human_agent/commands/note.py +1 -1
  147. inspect_ai/solver/_human_agent/commands/score.py +0 -11
  148. inspect_ai/solver/_multiple_choice.py +15 -18
  149. inspect_ai/solver/_prompt.py +7 -7
  150. inspect_ai/solver/_solver.py +53 -52
  151. inspect_ai/solver/_task_state.py +80 -69
  152. inspect_ai/solver/_use_tools.py +9 -9
  153. inspect_ai/tool/__init__.py +2 -1
  154. inspect_ai/tool/_tool.py +43 -14
  155. inspect_ai/tool/_tool_call.py +6 -2
  156. inspect_ai/tool/_tool_choice.py +3 -1
  157. inspect_ai/tool/_tool_def.py +10 -8
  158. inspect_ai/tool/_tool_params.py +24 -0
  159. inspect_ai/tool/_tool_with.py +7 -7
  160. inspect_ai/tool/_tools/__init__.py +0 -0
  161. inspect_ai/tool/_tools/_computer/_common.py +2 -2
  162. inspect_ai/tool/_tools/_computer/_computer.py +11 -0
  163. inspect_ai/tool/_tools/_execute.py +15 -9
  164. inspect_ai/tool/_tools/_web_browser/_resources/README.md +2 -2
  165. inspect_ai/tool/_tools/_web_browser/_web_browser.py +5 -3
  166. inspect_ai/tool/_tools/_web_search.py +7 -5
  167. inspect_ai/util/_concurrency.py +3 -3
  168. inspect_ai/util/_panel.py +2 -0
  169. inspect_ai/util/_resource.py +12 -12
  170. inspect_ai/util/_sandbox/docker/compose.py +23 -20
  171. inspect_ai/util/_sandbox/docker/config.py +2 -1
  172. inspect_ai/util/_sandbox/docker/docker.py +10 -1
  173. inspect_ai/util/_sandbox/docker/service.py +100 -0
  174. inspect_ai/util/_sandbox/environment.py +99 -96
  175. inspect_ai/util/_subprocess.py +5 -3
  176. inspect_ai/util/_subtask.py +15 -16
  177. {inspect_ai-0.3.63.dist-info → inspect_ai-0.3.65.dist-info}/LICENSE +1 -1
  178. {inspect_ai-0.3.63.dist-info → inspect_ai-0.3.65.dist-info}/METADATA +10 -6
  179. {inspect_ai-0.3.63.dist-info → inspect_ai-0.3.65.dist-info}/RECORD +182 -175
  180. {inspect_ai-0.3.63.dist-info → inspect_ai-0.3.65.dist-info}/WHEEL +0 -0
  181. {inspect_ai-0.3.63.dist-info → inspect_ai-0.3.65.dist-info}/entry_points.txt +0 -0
  182. {inspect_ai-0.3.63.dist-info → inspect_ai-0.3.65.dist-info}/top_level.txt +0 -0
@@ -26,7 +26,7 @@ export const MetaDataGrid: React.FC<MetadataGridProps> = ({
26
26
  const entryEls = entryRecords(entries).map((entry, index) => {
27
27
  const id = `${baseId}-value-${index}`;
28
28
  return (
29
- <Fragment>
29
+ <Fragment key={`${baseId}-record-${index}`}>
30
30
  <div
31
31
  style={{
32
32
  gridColumn: "1 / -1",
@@ -35,7 +35,7 @@ export const MetaDataView: React.FC<MetadataViewProps> = ({
35
35
  const entryEls = (coercedEntries || []).map((entry, index) => {
36
36
  const id = `${baseId}-value-${index}`;
37
37
  return (
38
- <tr>
38
+ <tr key={id}>
39
39
  <td
40
40
  className={clsx(
41
41
  styles.cell,
@@ -132,9 +132,11 @@ const contentRenderers: Record<string, ContentRenderer> = {
132
132
  const isArray = Array.isArray(entry.value);
133
133
  if (isArray) {
134
134
  const types = new Set(
135
- entry.value.map((e: unknown) => {
136
- return typeof e;
137
- }),
135
+ entry.value
136
+ .filter((e: unknown) => e !== null)
137
+ .map((e: unknown) => {
138
+ return typeof e;
139
+ }),
138
140
  );
139
141
  return types.size === 1;
140
142
  } else {
@@ -235,16 +237,7 @@ const contentRenderers: Record<string, ContentRenderer> = {
235
237
  },
236
238
  render: (id, entry) => {
237
239
  // Generate a json preview
238
- const summary = [];
239
- const keys = Object.keys(entry.value);
240
- if (keys.length > 4) {
241
- summary.push(...keys.slice(0, 2));
242
- summary.push("...");
243
- summary.push(...keys.slice(keys.length - 2));
244
- } else {
245
- summary.push(...keys);
246
- }
247
-
240
+ console.log({ entry });
248
241
  return {
249
242
  rendered: (
250
243
  <MetaDataView
@@ -125,6 +125,7 @@ export const PlanDetailView: React.FC<PlanDetailViewProps> = ({
125
125
  const scorerPanels = Object.keys(scorers).map((key) => {
126
126
  return (
127
127
  <ScorerDetailView
128
+ key={key}
128
129
  name={key}
129
130
  scores={scorers[key].scores}
130
131
  params={scorers[key].params as Record<string, unknown>}
@@ -159,6 +160,7 @@ export const PlanDetailView: React.FC<PlanDetailViewProps> = ({
159
160
  className: cols === 1 ? styles.oneCol : styles.twoCol,
160
161
  contents: (
161
162
  <MetaDataView
163
+ key={`plan-md-task`}
162
164
  className={"text-size-small"}
163
165
  entries={taskInformation}
164
166
  tableOptions="sm"
@@ -172,6 +174,7 @@ export const PlanDetailView: React.FC<PlanDetailViewProps> = ({
172
174
  className: cols === 1 ? styles.oneCol : styles.twoCol,
173
175
  contents: (
174
176
  <MetaDataView
177
+ key={`plan-md-task-args`}
175
178
  className={"text-size-small"}
176
179
  entries={task_args as Record<string, unknown>}
177
180
  tableOptions="sm"
@@ -185,6 +188,7 @@ export const PlanDetailView: React.FC<PlanDetailViewProps> = ({
185
188
  className: cols === 1 ? styles.oneCol : styles.twoCol,
186
189
  contents: (
187
190
  <MetaDataView
191
+ key={`plan-md-model-args`}
188
192
  className={"text-size-small"}
189
193
  entries={model_args as Record<string, unknown>}
190
194
  tableOptions="sm"
@@ -199,6 +203,7 @@ export const PlanDetailView: React.FC<PlanDetailViewProps> = ({
199
203
  className: cols === 1 ? styles.oneCol : styles.twoCol,
200
204
  contents: (
201
205
  <MetaDataView
206
+ key={`plan-md-config`}
202
207
  className={"text-size-small"}
203
208
  entries={config}
204
209
  tableOptions="sm"
@@ -217,6 +222,7 @@ export const PlanDetailView: React.FC<PlanDetailViewProps> = ({
217
222
  className: cols === 1 ? styles.oneCol : styles.twoCol,
218
223
  contents: (
219
224
  <MetaDataView
225
+ key={`plan-md-generate-config`}
220
226
  className={"text-size-small"}
221
227
  entries={generate_record}
222
228
  tableOptions="sm"
@@ -231,6 +237,7 @@ export const PlanDetailView: React.FC<PlanDetailViewProps> = ({
231
237
  className: cols === 1 ? styles.oneCol : styles.twoCol,
232
238
  contents: (
233
239
  <MetaDataView
240
+ key={`plan-md-metadata`}
234
241
  className={"text-size-small"}
235
242
  entries={metadata}
236
243
  tableOptions="sm"
@@ -249,7 +256,11 @@ export const PlanDetailView: React.FC<PlanDetailViewProps> = ({
249
256
  >
250
257
  {taskColumns.map((col) => {
251
258
  return (
252
- <PlanColumn title={col.title} className={col.className}>
259
+ <PlanColumn
260
+ title={col.title}
261
+ className={col.className}
262
+ key={`plan-col-${col.title}`}
263
+ >
253
264
  {col.contents}
254
265
  </PlanColumn>
255
266
  );
@@ -259,7 +270,11 @@ export const PlanDetailView: React.FC<PlanDetailViewProps> = ({
259
270
  <div className={clsx(styles.row)}>
260
271
  {metadataColumns.map((col) => {
261
272
  return (
262
- <PlanColumn title={col.title} className={col.className}>
273
+ <PlanColumn
274
+ title={col.title}
275
+ className={col.className}
276
+ key={`plan-col-${col.title}`}
277
+ >
263
278
  {col.contents}
264
279
  </PlanColumn>
265
280
  );
@@ -18,7 +18,7 @@ export const SolversDetailView: React.FC<SolversDetailView> = ({ steps }) => {
18
18
 
19
19
  const details = steps?.map((step, index) => {
20
20
  return (
21
- <Fragment>
21
+ <Fragment key={`solver-step-${index}`}>
22
22
  <DetailStep
23
23
  name={step.solver}
24
24
  className={clsx(styles.items, "text-size-small")}
@@ -77,6 +77,7 @@ export const SampleDisplay: React.FC<SampleDisplayProps> = ({
77
77
  if (!isVscode()) {
78
78
  tools.push(
79
79
  <ToolButton
80
+ key="sample-print-tool"
80
81
  label="Print"
81
82
  icon={ApplicationIcons.copy}
82
83
  onClick={() => {
@@ -101,6 +102,7 @@ export const SampleDisplay: React.FC<SampleDisplayProps> = ({
101
102
  >
102
103
  {sample.events && sample.events.length > 0 ? (
103
104
  <TabPanel
105
+ key={kSampleTranscriptTabId}
104
106
  id={kSampleTranscriptTabId}
105
107
  className="sample-tab"
106
108
  title="Transcript"
@@ -120,6 +122,7 @@ export const SampleDisplay: React.FC<SampleDisplayProps> = ({
120
122
  </TabPanel>
121
123
  ) : null}
122
124
  <TabPanel
125
+ key={kSampleMessagesTabId}
123
126
  id={kSampleMessagesTabId}
124
127
  className={clsx("sample-tab", styles.fullWidth)}
125
128
  title="Messages"
@@ -138,6 +141,7 @@ export const SampleDisplay: React.FC<SampleDisplayProps> = ({
138
141
  </TabPanel>
139
142
  {scorerNames.length === 1 ? (
140
143
  <TabPanel
144
+ key={kSampleScoringTabId}
141
145
  id={kSampleScoringTabId}
142
146
  className="sample-tab"
143
147
  title="Scoring"
@@ -156,6 +160,7 @@ export const SampleDisplay: React.FC<SampleDisplayProps> = ({
156
160
  const tabId = `score-${scorer}`;
157
161
  return (
158
162
  <TabPanel
163
+ key={tabId}
159
164
  id={tabId}
160
165
  className="sample-tab"
161
166
  title={scorer}
@@ -208,7 +213,11 @@ export const SampleDisplay: React.FC<SampleDisplayProps> = ({
208
213
  selected={selectedTab === kSampleJsonTabId}
209
214
  >
210
215
  <div className={clsx(styles.padded, styles.fullWidth)}>
211
- <JSONPanel data={sample} simple={true} />
216
+ <JSONPanel
217
+ data={sample}
218
+ simple={true}
219
+ className={clsx("text-size-small")}
220
+ />
212
221
  </div>
213
222
  </TabPanel>
214
223
  ) : null}
@@ -217,11 +226,11 @@ export const SampleDisplay: React.FC<SampleDisplayProps> = ({
217
226
  );
218
227
  };
219
228
 
220
- const metadataViewsForSample = (_id: string, sample: EvalSample) => {
229
+ const metadataViewsForSample = (id: string, sample: EvalSample) => {
221
230
  const sampleMetadatas = [];
222
231
  if (sample.model_usage && Object.keys(sample.model_usage).length > 0) {
223
232
  sampleMetadatas.push(
224
- <Card>
233
+ <Card key={`sample-usage-${id}`}>
225
234
  <CardHeader label="Usage" />
226
235
  <CardBody>
227
236
  <ModelTokenTable
@@ -235,7 +244,7 @@ const metadataViewsForSample = (_id: string, sample: EvalSample) => {
235
244
 
236
245
  if (Object.keys(sample?.metadata).length > 0) {
237
246
  sampleMetadatas.push(
238
- <Card>
247
+ <Card key={`sample-metadata-${id}`}>
239
248
  <CardHeader label="Metadata" />
240
249
  <CardBody>
241
250
  <MetaDataView
@@ -250,7 +259,7 @@ const metadataViewsForSample = (_id: string, sample: EvalSample) => {
250
259
 
251
260
  if (Object.keys(sample?.store).length > 0) {
252
261
  sampleMetadatas.push(
253
- <Card>
262
+ <Card key={`sample-store-${id}`}>
254
263
  <CardHeader label="Store" />
255
264
  <CardBody>
256
265
  <MetaDataView
@@ -143,9 +143,10 @@ export const SampleSummaryView: React.FC<SampleSummaryViewProps> = ({
143
143
  .join(" ")}`,
144
144
  }}
145
145
  >
146
- {columns.map((col) => {
146
+ {columns.map((col, idx) => {
147
147
  return (
148
148
  <div
149
+ key={`sample-summ-lbl-${idx}`}
149
150
  className={clsx(
150
151
  "text-style-label",
151
152
  "text-style-secondary",
@@ -157,9 +158,10 @@ export const SampleSummaryView: React.FC<SampleSummaryViewProps> = ({
157
158
  </div>
158
159
  );
159
160
  })}
160
- {columns.map((col) => {
161
+ {columns.map((col, idx) => {
161
162
  return (
162
163
  <div
164
+ key={`sample-summ-val-${idx}`}
163
165
  className={clsx(
164
166
  styles.wrap,
165
167
  col.clamp ? "three-line-clamp" : undefined,
@@ -1,3 +1,4 @@
1
+ import { Fragment } from "react/jsx-runtime";
1
2
  import { ScoreFilter, ScoreLabel } from "../types";
2
3
  import { SamplesDescriptor } from "./descriptor/samplesDescriptor";
3
4
  import { EpochFilter } from "./sample-tools/EpochFilter";
@@ -32,29 +33,20 @@ export const SampleTools: React.FC<SampleToolsProps> = ({
32
33
  scores,
33
34
  sampleDescriptor,
34
35
  }) => {
35
- const tools = [];
36
-
37
- tools.push(
38
- <SampleFilter
39
- evalDescriptor={sampleDescriptor.evalDescriptor}
40
- scoreFilter={scoreFilter}
41
- setScoreFilter={setScoreFilter}
42
- />,
36
+ return (
37
+ <Fragment>
38
+ <SampleFilter
39
+ evalDescriptor={sampleDescriptor.evalDescriptor}
40
+ scoreFilter={scoreFilter}
41
+ setScoreFilter={setScoreFilter}
42
+ />
43
+ {scores.length > 1 ? (
44
+ <SelectScorer scores={scores} score={score} setScore={setScore} />
45
+ ) : undefined}
46
+ {epochs > 1 ? (
47
+ <EpochFilter epoch={epoch} setEpoch={setEpoch} epochs={epochs} />
48
+ ) : undefined}
49
+ <SortFilter sort={sort} setSort={setSort} epochs={epochs} />
50
+ </Fragment>
43
51
  );
44
-
45
- if (scores.length > 1) {
46
- tools.push(
47
- <SelectScorer scores={scores} score={score} setScore={setScore} />,
48
- );
49
- }
50
-
51
- if (epochs > 1) {
52
- tools.push(
53
- <EpochFilter epoch={epoch} setEpoch={setEpoch} epochs={epochs} />,
54
- );
55
- }
56
-
57
- tools.push(<SortFilter sort={sort} setSort={setSort} epochs={epochs} />);
58
-
59
- return tools;
60
52
  };
@@ -42,7 +42,7 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({
42
42
  {message.role}
43
43
  </div>
44
44
  {message.role === "assistant" && message.reasoning ? (
45
- <Fragment>
45
+ <Fragment key={`${id}-response-label`}>
46
46
  <div className={clsx("text-style-label", "text-style-secondary")}>
47
47
  Reasoning
48
48
  </div>
@@ -32,6 +32,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
32
32
  collapsedMessages.length > 1 && numbered ? index + 1 : undefined;
33
33
  return (
34
34
  <ChatMessageRow
35
+ key={`${id}-msg-${index}`}
35
36
  parentName={id || "chat-view"}
36
37
  number={number}
37
38
  resolvedMessage={msg}
@@ -42,6 +42,7 @@ export const MessageContent: React.FC<MessageContentProps> = ({ contents }) => {
42
42
  return contents.map((content, index) => {
43
43
  if (typeof content === "string") {
44
44
  return messageRenderers["text"].render(
45
+ `text-content-${index}`,
45
46
  {
46
47
  type: "text",
47
48
  text: content,
@@ -52,7 +53,11 @@ export const MessageContent: React.FC<MessageContentProps> = ({ contents }) => {
52
53
  if (content) {
53
54
  const renderer = messageRenderers[content.type];
54
55
  if (renderer) {
55
- return renderer.render(content, index === contents.length - 1);
56
+ return renderer.render(
57
+ `text-${content.type}-${index}`,
58
+ content,
59
+ index === contents.length - 1,
60
+ );
56
61
  } else {
57
62
  console.error(`Unknown message content type '${content.type}'`);
58
63
  }
@@ -65,20 +70,29 @@ export const MessageContent: React.FC<MessageContentProps> = ({ contents }) => {
65
70
  type: "text",
66
71
  text: contents,
67
72
  };
68
- return messageRenderers["text"].render(contentText, true);
73
+ return messageRenderers["text"].render(
74
+ "text-message-content",
75
+ contentText,
76
+ true,
77
+ );
69
78
  }
70
79
  };
71
80
 
72
81
  interface MessageRenderer {
73
- render: (content: ContentType, isLast: boolean) => React.ReactNode;
82
+ render: (
83
+ key: string,
84
+ content: ContentType,
85
+ isLast: boolean,
86
+ ) => React.ReactNode;
74
87
  }
75
88
 
76
89
  const messageRenderers: Record<string, MessageRenderer> = {
77
90
  text: {
78
- render: (content, isLast) => {
91
+ render: (key, content, isLast) => {
79
92
  const c = content as ContentText;
80
93
  return (
81
94
  <MarkdownDiv
95
+ key={key}
82
96
  markdown={c.text}
83
97
  className={isLast ? "no-last-para-padding" : ""}
84
98
  />
@@ -86,39 +100,39 @@ const messageRenderers: Record<string, MessageRenderer> = {
86
100
  },
87
101
  },
88
102
  image: {
89
- render: (content) => {
103
+ render: (key, content) => {
90
104
  const c = content as ContentImage;
91
105
  if (c.image.startsWith("data:")) {
92
- return <img src={c.image} className={styles.contentImage} />;
106
+ return <img src={c.image} className={styles.contentImage} key={key} />;
93
107
  } else {
94
- return <code>{c.image}</code>;
108
+ return <code key={key}>{c.image}</code>;
95
109
  }
96
110
  },
97
111
  },
98
112
  audio: {
99
- render: (content) => {
113
+ render: (key, content) => {
100
114
  const c = content as ContentAudio;
101
115
  return (
102
- <audio controls>
116
+ <audio controls key={key}>
103
117
  <source src={c.audio} type={mimeTypeForFormat(c.format)} />
104
118
  </audio>
105
119
  );
106
120
  },
107
121
  },
108
122
  video: {
109
- render: (content) => {
123
+ render: (key, content) => {
110
124
  const c = content as ContentVideo;
111
125
  return (
112
- <video width="500" height="375" controls>
126
+ <video width="500" height="375" controls key={key}>
113
127
  <source src={c.video} type={mimeTypeForFormat(c.format)} />
114
128
  </video>
115
129
  );
116
130
  },
117
131
  },
118
132
  tool: {
119
- render: (content) => {
133
+ render: (key, content) => {
120
134
  const c = content as ContentTool;
121
- return <ToolOutput output={c.content} />;
135
+ return <ToolOutput output={c.content} key={key} />;
122
136
  },
123
137
  },
124
138
  };
@@ -8,6 +8,7 @@ import { MessageContent } from "./MessageContent";
8
8
  import { resolveToolInput } from "./tools/tool";
9
9
  import { ToolCallView } from "./tools/ToolCallView";
10
10
 
11
+ import { Fragment } from "react";
11
12
  import { ContentTool } from "../../types";
12
13
  import styles from "./MessageContents.module.css";
13
14
 
@@ -27,20 +28,10 @@ export const MessageContents: React.FC<MessageContentsProps> = ({
27
28
  message.tool_calls &&
28
29
  message.tool_calls.length
29
30
  ) {
30
- const result = [];
31
- // If the message contains content, render that.
32
- if (message.content) {
33
- result.push(
34
- <div className={styles.content}>
35
- <MessageContent contents={message.content} />
36
- </div>,
37
- );
38
- }
39
-
40
31
  // Render the tool calls made by this message
41
32
  const toolCalls = message.tool_calls.map((tool_call, idx) => {
42
33
  // Extract tool input
43
- const { input, functionCall, inputType } = resolveToolInput(
34
+ const { input, functionCall, highlightLanguage } = resolveToolInput(
44
35
  tool_call.function,
45
36
  tool_call.arguments,
46
37
  );
@@ -57,23 +48,34 @@ export const MessageContents: React.FC<MessageContentsProps> = ({
57
48
  // Resolve the tool output
58
49
  const resolvedToolOutput = resolveToolMessage(toolMessage);
59
50
  if (toolCallStyle === "compact") {
60
- return <code>tool: {functionCall}</code>;
51
+ return (
52
+ <div key={`tool-call-${idx}`}>
53
+ <code>tool: {functionCall}</code>
54
+ </div>
55
+ );
61
56
  } else {
62
57
  return (
63
58
  <ToolCallView
59
+ key={`tool-call-${idx}`}
64
60
  functionCall={functionCall}
65
61
  input={input}
66
- inputType={inputType}
62
+ highlightLanguage={highlightLanguage}
67
63
  output={resolvedToolOutput}
68
64
  />
69
65
  );
70
66
  }
71
67
  });
72
68
 
73
- if (toolCalls) {
74
- result.push(...toolCalls);
75
- }
76
- return result;
69
+ return (
70
+ <Fragment>
71
+ <div className={styles.content}>
72
+ {message.content ? (
73
+ <MessageContent contents={message.content} />
74
+ ) : undefined}
75
+ </div>
76
+ {toolCalls}
77
+ </Fragment>
78
+ );
77
79
  } else {
78
80
  return <MessageContent contents={message.content} />;
79
81
  }
@@ -1,3 +1,4 @@
1
+ import { useMemo } from "react";
1
2
  import ExpandablePanel from "../../../components/ExpandablePanel";
2
3
  import { ContentTool } from "../../../types";
3
4
  import {
@@ -14,7 +15,7 @@ import { ToolTitle } from "./ToolTitle";
14
15
  interface ToolCallViewProps {
15
16
  functionCall: string;
16
17
  input?: string;
17
- inputType?: string;
18
+ highlightLanguage?: string;
18
19
  view?: ToolCallContent;
19
20
  output:
20
21
  | string
@@ -41,7 +42,7 @@ interface ToolCallViewProps {
41
42
  export const ToolCallView: React.FC<ToolCallViewProps> = ({
42
43
  functionCall,
43
44
  input,
44
- inputType,
45
+ highlightLanguage,
45
46
  view,
46
47
  output,
47
48
  mode,
@@ -76,6 +77,7 @@ export const ToolCallView: React.FC<ToolCallViewProps> = ({
76
77
  const collapse = Array.isArray(output)
77
78
  ? output.every((item) => !isContentImage(item))
78
79
  : !isContentImage(output);
80
+ const normalizedContent = useMemo(() => normalizeContent(output), [output]);
79
81
 
80
82
  return (
81
83
  <div>
@@ -86,14 +88,14 @@ export const ToolCallView: React.FC<ToolCallViewProps> = ({
86
88
  )}
87
89
  <div>
88
90
  <div>
89
- <ToolInput type={inputType} contents={input} view={view} />
90
- {output ? (
91
- <ExpandablePanel collapse={collapse} border={true} lines={15}>
92
- <MessageContent contents={normalizeContent(output)} />
93
- </ExpandablePanel>
94
- ) : (
95
- ""
96
- )}
91
+ <ToolInput
92
+ highlightLanguage={highlightLanguage}
93
+ contents={input}
94
+ toolCallView={view}
95
+ />
96
+ <ExpandablePanel collapse={collapse} border={true} lines={15}>
97
+ <MessageContent contents={normalizedContent} />
98
+ </ExpandablePanel>
97
99
  </div>
98
100
  </div>
99
101
  </div>