inspect-ai 0.3.98__py3-none-any.whl → 0.3.100__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 (131) hide show
  1. inspect_ai/__init__.py +2 -0
  2. inspect_ai/_cli/log.py +1 -1
  3. inspect_ai/_display/core/config.py +11 -5
  4. inspect_ai/_display/core/panel.py +66 -2
  5. inspect_ai/_display/core/textual.py +5 -2
  6. inspect_ai/_display/plain/display.py +1 -0
  7. inspect_ai/_display/rich/display.py +2 -2
  8. inspect_ai/_display/textual/widgets/transcript.py +41 -1
  9. inspect_ai/_eval/run.py +12 -4
  10. inspect_ai/_eval/score.py +2 -4
  11. inspect_ai/_eval/task/log.py +1 -1
  12. inspect_ai/_eval/task/run.py +59 -81
  13. inspect_ai/_eval/task/task.py +1 -1
  14. inspect_ai/_util/_async.py +1 -1
  15. inspect_ai/_util/content.py +11 -6
  16. inspect_ai/_util/interrupt.py +2 -2
  17. inspect_ai/_util/text.py +7 -0
  18. inspect_ai/_util/working.py +8 -37
  19. inspect_ai/_view/__init__.py +0 -0
  20. inspect_ai/_view/schema.py +3 -1
  21. inspect_ai/_view/view.py +14 -0
  22. inspect_ai/_view/www/CLAUDE.md +15 -0
  23. inspect_ai/_view/www/dist/assets/index.css +273 -169
  24. inspect_ai/_view/www/dist/assets/index.js +20079 -17019
  25. inspect_ai/_view/www/log-schema.json +122 -8
  26. inspect_ai/_view/www/package.json +5 -1
  27. inspect_ai/_view/www/src/@types/log.d.ts +20 -2
  28. inspect_ai/_view/www/src/app/App.tsx +1 -15
  29. inspect_ai/_view/www/src/app/appearance/icons.ts +4 -1
  30. inspect_ai/_view/www/src/app/content/MetaDataGrid.tsx +24 -6
  31. inspect_ai/_view/www/src/app/content/MetadataGrid.module.css +0 -5
  32. inspect_ai/_view/www/src/app/content/RenderedContent.tsx +221 -205
  33. inspect_ai/_view/www/src/app/log-view/LogViewContainer.tsx +2 -1
  34. inspect_ai/_view/www/src/app/log-view/tabs/SamplesTab.tsx +5 -0
  35. inspect_ai/_view/www/src/app/routing/url.ts +84 -4
  36. inspect_ai/_view/www/src/app/samples/InlineSampleDisplay.module.css +0 -5
  37. inspect_ai/_view/www/src/app/samples/SampleDialog.module.css +1 -1
  38. inspect_ai/_view/www/src/app/samples/SampleDisplay.module.css +7 -0
  39. inspect_ai/_view/www/src/app/samples/SampleDisplay.tsx +26 -19
  40. inspect_ai/_view/www/src/app/samples/SampleSummaryView.module.css +1 -2
  41. inspect_ai/_view/www/src/app/samples/chat/ChatMessage.tsx +8 -6
  42. inspect_ai/_view/www/src/app/samples/chat/ChatMessageRow.tsx +0 -4
  43. inspect_ai/_view/www/src/app/samples/chat/ChatViewVirtualList.tsx +3 -2
  44. inspect_ai/_view/www/src/app/samples/chat/MessageContent.tsx +2 -0
  45. inspect_ai/_view/www/src/app/samples/chat/MessageContents.tsx +2 -0
  46. inspect_ai/_view/www/src/app/samples/chat/messages.ts +1 -0
  47. inspect_ai/_view/www/src/app/samples/chat/tools/ToolCallView.tsx +1 -0
  48. inspect_ai/_view/www/src/app/samples/list/SampleRow.tsx +1 -1
  49. inspect_ai/_view/www/src/app/samples/scores/SampleScoresGrid.module.css +2 -2
  50. inspect_ai/_view/www/src/app/samples/transcript/ErrorEventView.tsx +2 -3
  51. inspect_ai/_view/www/src/app/samples/transcript/InfoEventView.tsx +1 -1
  52. inspect_ai/_view/www/src/app/samples/transcript/InputEventView.tsx +1 -2
  53. inspect_ai/_view/www/src/app/samples/transcript/ModelEventView.module.css +1 -1
  54. inspect_ai/_view/www/src/app/samples/transcript/ModelEventView.tsx +1 -1
  55. inspect_ai/_view/www/src/app/samples/transcript/SampleInitEventView.tsx +1 -1
  56. inspect_ai/_view/www/src/app/samples/transcript/SampleLimitEventView.tsx +3 -2
  57. inspect_ai/_view/www/src/app/samples/transcript/SandboxEventView.tsx +4 -5
  58. inspect_ai/_view/www/src/app/samples/transcript/ScoreEventView.tsx +1 -1
  59. inspect_ai/_view/www/src/app/samples/transcript/SpanEventView.tsx +1 -2
  60. inspect_ai/_view/www/src/app/samples/transcript/StepEventView.tsx +1 -3
  61. inspect_ai/_view/www/src/app/samples/transcript/SubtaskEventView.tsx +1 -2
  62. inspect_ai/_view/www/src/app/samples/transcript/ToolEventView.tsx +3 -4
  63. inspect_ai/_view/www/src/app/samples/transcript/TranscriptPanel.module.css +42 -0
  64. inspect_ai/_view/www/src/app/samples/transcript/TranscriptPanel.tsx +77 -0
  65. inspect_ai/_view/www/src/app/samples/transcript/TranscriptVirtualList.tsx +27 -71
  66. inspect_ai/_view/www/src/app/samples/transcript/TranscriptVirtualListComponent.module.css +13 -3
  67. inspect_ai/_view/www/src/app/samples/transcript/TranscriptVirtualListComponent.tsx +27 -2
  68. inspect_ai/_view/www/src/app/samples/transcript/event/EventPanel.module.css +1 -0
  69. inspect_ai/_view/www/src/app/samples/transcript/event/EventPanel.tsx +21 -22
  70. inspect_ai/_view/www/src/app/samples/transcript/outline/OutlineRow.module.css +45 -0
  71. inspect_ai/_view/www/src/app/samples/transcript/outline/OutlineRow.tsx +223 -0
  72. inspect_ai/_view/www/src/app/samples/transcript/outline/TranscriptOutline.module.css +10 -0
  73. inspect_ai/_view/www/src/app/samples/transcript/outline/TranscriptOutline.tsx +258 -0
  74. inspect_ai/_view/www/src/app/samples/transcript/outline/tree-visitors.ts +187 -0
  75. inspect_ai/_view/www/src/app/samples/transcript/state/StateEventRenderers.tsx +8 -1
  76. inspect_ai/_view/www/src/app/samples/transcript/state/StateEventView.tsx +3 -4
  77. inspect_ai/_view/www/src/app/samples/transcript/transform/hooks.ts +78 -0
  78. inspect_ai/_view/www/src/app/samples/transcript/transform/treeify.ts +340 -135
  79. inspect_ai/_view/www/src/app/samples/transcript/transform/utils.ts +3 -0
  80. inspect_ai/_view/www/src/app/samples/transcript/types.ts +2 -0
  81. inspect_ai/_view/www/src/app/types.ts +5 -1
  82. inspect_ai/_view/www/src/client/api/api-browser.ts +2 -2
  83. inspect_ai/_view/www/src/components/LiveVirtualList.tsx +6 -1
  84. inspect_ai/_view/www/src/components/MarkdownDiv.tsx +1 -1
  85. inspect_ai/_view/www/src/components/PopOver.tsx +422 -0
  86. inspect_ai/_view/www/src/components/PulsingDots.module.css +9 -9
  87. inspect_ai/_view/www/src/components/PulsingDots.tsx +4 -1
  88. inspect_ai/_view/www/src/components/StickyScroll.tsx +183 -0
  89. inspect_ai/_view/www/src/components/TabSet.tsx +4 -0
  90. inspect_ai/_view/www/src/state/hooks.ts +52 -2
  91. inspect_ai/_view/www/src/state/logSlice.ts +4 -3
  92. inspect_ai/_view/www/src/state/samplePolling.ts +8 -0
  93. inspect_ai/_view/www/src/state/sampleSlice.ts +53 -9
  94. inspect_ai/_view/www/src/state/scrolling.ts +152 -0
  95. inspect_ai/_view/www/src/utils/attachments.ts +7 -0
  96. inspect_ai/_view/www/src/utils/python.ts +18 -0
  97. inspect_ai/_view/www/yarn.lock +269 -6
  98. inspect_ai/agent/_react.py +12 -7
  99. inspect_ai/agent/_run.py +46 -11
  100. inspect_ai/analysis/beta/_dataframe/samples/table.py +19 -18
  101. inspect_ai/log/_bundle.py +5 -3
  102. inspect_ai/log/_log.py +3 -3
  103. inspect_ai/log/_recorders/file.py +2 -9
  104. inspect_ai/log/_transcript.py +1 -1
  105. inspect_ai/model/_call_tools.py +6 -2
  106. inspect_ai/model/_openai.py +1 -1
  107. inspect_ai/model/_openai_responses.py +78 -39
  108. inspect_ai/model/_openai_web_search.py +31 -0
  109. inspect_ai/model/_providers/anthropic.py +3 -6
  110. inspect_ai/model/_providers/azureai.py +72 -3
  111. inspect_ai/model/_providers/openai.py +2 -1
  112. inspect_ai/model/_providers/providers.py +1 -1
  113. inspect_ai/scorer/_metric.py +1 -2
  114. inspect_ai/solver/_task_state.py +2 -2
  115. inspect_ai/tool/_tool.py +6 -2
  116. inspect_ai/tool/_tool_def.py +27 -4
  117. inspect_ai/tool/_tool_info.py +2 -0
  118. inspect_ai/tool/_tools/_web_search/_google.py +15 -4
  119. inspect_ai/tool/_tools/_web_search/_tavily.py +35 -12
  120. inspect_ai/tool/_tools/_web_search/_web_search.py +214 -45
  121. inspect_ai/util/__init__.py +6 -0
  122. inspect_ai/util/_json.py +3 -0
  123. inspect_ai/util/_limit.py +374 -141
  124. inspect_ai/util/_sandbox/docker/compose.py +20 -11
  125. inspect_ai/util/_span.py +1 -1
  126. {inspect_ai-0.3.98.dist-info → inspect_ai-0.3.100.dist-info}/METADATA +3 -3
  127. {inspect_ai-0.3.98.dist-info → inspect_ai-0.3.100.dist-info}/RECORD +131 -117
  128. {inspect_ai-0.3.98.dist-info → inspect_ai-0.3.100.dist-info}/WHEEL +1 -1
  129. {inspect_ai-0.3.98.dist-info → inspect_ai-0.3.100.dist-info}/entry_points.txt +0 -0
  130. {inspect_ai-0.3.98.dist-info → inspect_ai-0.3.100.dist-info}/licenses/LICENSE +0 -0
  131. {inspect_ai-0.3.98.dist-info → inspect_ai-0.3.100.dist-info}/top_level.txt +0 -0
@@ -2,7 +2,6 @@ import { FC, memo, RefObject, useEffect, useMemo } from "react";
2
2
  import {
3
3
  ApprovalEvent,
4
4
  ErrorEvent,
5
- Events,
6
5
  InfoEvent,
7
6
  InputEvent,
8
7
  LoggerEvent,
@@ -32,20 +31,22 @@ import { StateEventView } from "./state/StateEventView";
32
31
  import { StepEventView } from "./StepEventView";
33
32
  import { SubtaskEventView } from "./SubtaskEventView";
34
33
  import { ToolEventView } from "./ToolEventView";
35
- import { EventNode } from "./types";
34
+ import { EventNode, kTranscriptCollapseScope } from "./types";
36
35
 
37
36
  import { useStore } from "../../../state/store";
38
37
  import { SpanEventView } from "./SpanEventView";
39
38
  import { TranscriptVirtualListComponent } from "./TranscriptVirtualListComponent";
40
- import { fixupEventStream, kSandboxSignalName } from "./transform/fixups";
41
- import { flatTree, treeifyEvents } from "./transform/treeify";
39
+ import { flatTree } from "./transform/treeify";
42
40
 
43
41
  interface TranscriptVirtualListProps {
44
42
  id: string;
45
- events: Events;
43
+ eventNodes: EventNode[];
44
+ defaultCollapsedIds: Record<string, boolean>;
46
45
  initialEventId: string | null;
46
+ offsetTop?: number;
47
47
  scrollRef: RefObject<HTMLDivElement | null>;
48
48
  running?: boolean;
49
+ className?: string | string[];
49
50
  }
50
51
 
51
52
  /**
@@ -53,7 +54,16 @@ interface TranscriptVirtualListProps {
53
54
  */
54
55
  export const TranscriptVirtualList: FC<TranscriptVirtualListProps> = memo(
55
56
  (props) => {
56
- let { id, scrollRef, events, running, initialEventId } = props;
57
+ let {
58
+ id,
59
+ scrollRef,
60
+ eventNodes,
61
+ defaultCollapsedIds,
62
+ running,
63
+ initialEventId,
64
+ offsetTop,
65
+ className,
66
+ } = props;
57
67
 
58
68
  // The list of events that have been collapsed
59
69
  const collapsedEvents = useStore((state) => state.sample.collapsedEvents);
@@ -61,93 +71,39 @@ export const TranscriptVirtualList: FC<TranscriptVirtualListProps> = memo(
61
71
  (state) => state.sampleActions.setCollapsedEvents,
62
72
  );
63
73
 
64
- // Normalize Events in a flattened filtered list
65
- const { eventNodes, defaultCollapsedIds } = useMemo(() => {
66
- // Apply fixups to the event string
67
- const resolvedEvents = fixupEventStream(events, !running);
68
-
69
- // Build the event tree
70
- const eventTree = treeifyEvents(resolvedEvents, 0);
71
-
72
- // Apply collapse filters to the event tree
73
- const defaultCollapsedIds: Record<string, true> = {};
74
- const findCollapsibleEvents = (nodes: EventNode[]) => {
75
- for (const node of nodes) {
76
- if (
77
- (node.event.event === "step" ||
78
- node.event.event === "span_begin" ||
79
- node.event.event === "tool" ||
80
- node.event.event === "subtask") &&
81
- collapseFilters.some((filter) =>
82
- filter(
83
- node.event as
84
- | StepEvent
85
- | SpanBeginEvent
86
- | ToolEvent
87
- | SubtaskEvent,
88
- ),
89
- )
90
- ) {
91
- defaultCollapsedIds[node.id] = true;
92
- }
93
-
94
- // Recursively check children
95
- findCollapsibleEvents(node.children);
96
- }
97
- };
98
- findCollapsibleEvents(eventTree);
99
-
74
+ const flattenedNodes = useMemo(() => {
100
75
  // flattten the event tree
101
- const eventNodes = flatTree(
102
- eventTree,
103
- collapsedEvents || defaultCollapsedIds,
76
+ return flatTree(
77
+ eventNodes,
78
+ (collapsedEvents
79
+ ? collapsedEvents[kTranscriptCollapseScope]
80
+ : undefined) || defaultCollapsedIds,
104
81
  );
105
-
106
- return { eventNodes, defaultCollapsedIds };
107
- }, [events, running, collapsedEvents]);
82
+ }, [eventNodes, collapsedEvents, defaultCollapsedIds]);
108
83
 
109
84
  // Update the collapsed events when the default collapsed IDs change
110
85
  // This effect only depends on defaultCollapsedIds, not eventNodes
111
86
  useEffect(() => {
112
87
  // Only initialize collapsedEvents if it's empty
113
88
  if (!collapsedEvents && Object.keys(defaultCollapsedIds).length > 0) {
114
- setCollapsedEvents(defaultCollapsedIds);
89
+ setCollapsedEvents(kTranscriptCollapseScope, defaultCollapsedIds);
115
90
  }
116
91
  }, [defaultCollapsedIds, collapsedEvents, setCollapsedEvents]);
117
92
 
118
93
  return (
119
94
  <TranscriptVirtualListComponent
120
95
  id={id}
121
- eventNodes={eventNodes}
96
+ eventNodes={flattenedNodes}
122
97
  initialEventId={initialEventId}
98
+ offsetTop={offsetTop}
123
99
  scrollRef={scrollRef}
124
100
  running={running}
101
+ className={className}
125
102
  />
126
103
  );
127
104
  },
128
105
  );
129
106
 
130
- const collapseFilters: Array<
131
- (event: StepEvent | SpanBeginEvent | ToolEvent | SubtaskEvent) => boolean
132
- > = [
133
- (event: StepEvent | SpanBeginEvent | ToolEvent | SubtaskEvent) =>
134
- event.type === "solver" && event.name === "system_message",
135
- (event: StepEvent | SpanBeginEvent | ToolEvent | SubtaskEvent) => {
136
- if (event.event === "step" || event.event === "span_begin") {
137
- return (
138
- event.name === kSandboxSignalName ||
139
- event.name === "init" ||
140
- event.name === "sample_init"
141
- );
142
- }
143
- return false;
144
- },
145
- (event: StepEvent | SpanBeginEvent | ToolEvent | SubtaskEvent) =>
146
- event.event === "tool" && !event.agent && !event.failed,
147
- (event: StepEvent | SpanBeginEvent | ToolEvent | SubtaskEvent) =>
148
- event.event === "subtask",
149
- ];
150
-
151
107
  interface RenderedEventNodeProps {
152
108
  node: EventNode;
153
109
  className?: string | string[];
@@ -1,9 +1,19 @@
1
1
  .node {
2
- padding-top: 0.7rem;
3
- padding-bottom: 1px;
2
+ padding-top: 0.8rem;
4
3
  }
5
4
 
6
5
  .attached {
7
6
  padding-top: 0rem;
8
- margin-top: -8px;
7
+ }
8
+
9
+ .attachedParent {
10
+ padding-bottom: 0px;
11
+ border-bottom-left-radius: 0;
12
+ border-bottom-right-radius: 0;
13
+ border-bottom: none;
14
+ }
15
+
16
+ .attachedChild {
17
+ border-top-left-radius: 0;
18
+ border-top-right-radius: 0;
9
19
  }
@@ -10,8 +10,10 @@ interface TranscriptVirtualListComponentProps {
10
10
  id: string;
11
11
  eventNodes: EventNode[];
12
12
  initialEventId?: string | null;
13
+ offsetTop?: number;
13
14
  scrollRef?: RefObject<HTMLDivElement | null>;
14
15
  running?: boolean;
16
+ className?: string | string[];
15
17
  }
16
18
 
17
19
  /**
@@ -19,7 +21,15 @@ interface TranscriptVirtualListComponentProps {
19
21
  */
20
22
  export const TranscriptVirtualListComponent: FC<
21
23
  TranscriptVirtualListComponentProps
22
- > = ({ id, eventNodes, scrollRef, running, initialEventId }) => {
24
+ > = ({
25
+ id,
26
+ eventNodes,
27
+ scrollRef,
28
+ running,
29
+ initialEventId,
30
+ offsetTop,
31
+ className,
32
+ }) => {
23
33
  const initialEventIndex = useMemo(() => {
24
34
  if (initialEventId === null || initialEventId === undefined) {
25
35
  return undefined;
@@ -35,14 +45,24 @@ export const TranscriptVirtualListComponent: FC<
35
45
  const paddingClass = index === 0 ? styles.first : undefined;
36
46
 
37
47
  const previousIndex = index - 1;
48
+ const nextIndex = index + 1;
38
49
  const previous =
39
50
  previousIndex > 0 && previousIndex <= eventNodes.length
40
51
  ? eventNodes[previousIndex]
41
52
  : undefined;
53
+ const next =
54
+ nextIndex < eventNodes.length ? eventNodes[nextIndex] : undefined;
42
55
  const attached =
43
56
  item.event.event === "tool" &&
44
57
  (previous?.event.event === "tool" || previous?.event.event === "model");
58
+
59
+ const attachedParent =
60
+ item.event.event === "model" && next?.event.event === "tool";
45
61
  const attachedClass = attached ? styles.attached : undefined;
62
+ const attachedChildClass = attached ? styles.attachedChild : undefined;
63
+ const attachedParentClass = attachedParent
64
+ ? styles.attachedParent
65
+ : undefined;
46
66
 
47
67
  return (
48
68
  <div
@@ -54,7 +74,10 @@ export const TranscriptVirtualListComponent: FC<
54
74
  paddingRight: `${item.depth === 0 ? undefined : ".7em"} `,
55
75
  }}
56
76
  >
57
- <RenderedEventNode node={item} />
77
+ <RenderedEventNode
78
+ node={item}
79
+ className={clsx(attachedParentClass, attachedChildClass)}
80
+ />
58
81
  </div>
59
82
  );
60
83
  },
@@ -63,10 +86,12 @@ export const TranscriptVirtualListComponent: FC<
63
86
 
64
87
  return (
65
88
  <LiveVirtualList<EventNode>
89
+ className={className}
66
90
  id={id}
67
91
  scrollRef={scrollRef}
68
92
  data={eventNodes}
69
93
  initialTopMostItemIndex={initialEventIndex}
94
+ offsetTop={offsetTop}
70
95
  renderRow={renderRow}
71
96
  live={running}
72
97
  />
@@ -32,6 +32,7 @@
32
32
 
33
33
  .copyLink {
34
34
  font-size: 1.2em;
35
+ height: 1em;
35
36
  opacity: 0;
36
37
  padding-left: 0.2em;
37
38
  padding-right: 2em;
@@ -10,18 +10,18 @@ import {
10
10
  import { ApplicationIcons } from "../../../appearance/icons";
11
11
  import { EventNavs } from "./EventNavs";
12
12
 
13
- import { useParams } from "react-router-dom";
14
13
  import { CopyButton } from "../../../../components/CopyButton";
15
14
  import { useCollapseSampleEvent, useProperty } from "../../../../state/hooks";
16
15
  import {
17
- sampleEventUrl,
18
16
  supportsLinking,
19
17
  toFullUrl,
18
+ useSampleEventUrl,
20
19
  } from "../../../routing/url";
20
+ import { kTranscriptCollapseScope } from "../types";
21
21
  import styles from "./EventPanel.module.css";
22
22
 
23
23
  interface EventPanelProps {
24
- id: string;
24
+ eventNodeId: string;
25
25
  depth: number;
26
26
  className?: string | string[];
27
27
  title?: string;
@@ -42,7 +42,7 @@ interface ChildProps {
42
42
  * Renders the StateEventView component.
43
43
  */
44
44
  export const EventPanel: FC<EventPanelProps> = ({
45
- id,
45
+ eventNodeId,
46
46
  depth,
47
47
  className,
48
48
  title,
@@ -54,26 +54,21 @@ export const EventPanel: FC<EventPanelProps> = ({
54
54
  collapsibleContent,
55
55
  collapseControl = "top",
56
56
  }) => {
57
- const [collapsed, setCollapsed] = useCollapseSampleEvent(id);
57
+ const [collapsed, setCollapsed] = useCollapseSampleEvent(
58
+ kTranscriptCollapseScope,
59
+ eventNodeId,
60
+ );
58
61
  const isCollapsible = (childIds || []).length > 0 || collapsibleContent;
59
62
  const useBottomDongle = isCollapsible && collapseControl === "bottom";
60
63
 
61
- // Get all URL parameters at component level
62
- const { logPath, sampleId, epoch } = useParams<{
63
- logPath?: string;
64
- tabId?: string;
65
- sampleId?: string;
66
- epoch?: string;
67
- }>();
68
-
64
+ const sampleEventUrl = useSampleEventUrl(eventNodeId);
69
65
  const url =
70
- logPath && supportsLinking()
71
- ? toFullUrl(sampleEventUrl(id, logPath, sampleId, epoch))
72
- : undefined;
66
+ supportsLinking() && sampleEventUrl ? toFullUrl(sampleEventUrl) : undefined;
73
67
 
74
68
  const pillId = (index: number) => {
75
- return `${id}-nav-pill-${index}`;
69
+ return `${eventNodeId}-nav-pill-${index}`;
76
70
  };
71
+
77
72
  const filteredArrChildren = (
78
73
  Array.isArray(children) ? children : [children]
79
74
  ).filter((child) => !!child);
@@ -83,9 +78,13 @@ export const EventPanel: FC<EventPanelProps> = ({
83
78
  });
84
79
  const defaultPillId = defaultPill !== -1 ? pillId(defaultPill) : pillId(0);
85
80
 
86
- const [selectedNav, setSelectedNav] = useProperty(id, "selectedNav", {
87
- defaultValue: defaultPillId,
88
- });
81
+ const [selectedNav, setSelectedNav] = useProperty(
82
+ eventNodeId,
83
+ "selectedNav",
84
+ {
85
+ defaultValue: defaultPillId,
86
+ },
87
+ );
89
88
 
90
89
  const gridColumns = [];
91
90
 
@@ -186,7 +185,7 @@ export const EventPanel: FC<EventPanelProps> = ({
186
185
  ? (child.props as ChildProps)["data-name"] || defaultTitle
187
186
  : defaultTitle;
188
187
  return {
189
- id: `eventpanel-${id}-${index}`,
188
+ id: `eventpanel-${eventNodeId}-${index}`,
190
189
  title: title,
191
190
  target: pillId(index),
192
191
  };
@@ -205,7 +204,7 @@ export const EventPanel: FC<EventPanelProps> = ({
205
204
 
206
205
  const card = (
207
206
  <div
208
- id={id}
207
+ id={`event-panel-${eventNodeId}`}
209
208
  className={clsx(
210
209
  className,
211
210
  styles.card,
@@ -0,0 +1,45 @@
1
+ .eventRow {
2
+ display: grid;
3
+ grid-template-columns: 10px 1fr;
4
+ column-gap: 3px;
5
+ cursor: pointer;
6
+ }
7
+
8
+ .eventRow.selected {
9
+ font-weight: 800;
10
+ }
11
+
12
+ .eventRow .toggle {
13
+ font-size: 0.7em;
14
+ margin-top: 4px;
15
+ }
16
+
17
+ .eventLink {
18
+ color: var(--bs-body);
19
+ text-decoration: none;
20
+ cursor: pointer;
21
+ }
22
+
23
+ .eventLink:hover {
24
+ text-decoration: underline;
25
+ color: var(--bs-link-hover-color);
26
+ }
27
+
28
+ .label {
29
+ white-space: nowrap;
30
+ overflow: hidden;
31
+ text-overflow: ellipsis;
32
+ }
33
+
34
+ .icon {
35
+ margin-right: 3px;
36
+ }
37
+
38
+ .progress {
39
+ margin-left: 0.3em !important;
40
+ }
41
+
42
+ .popover {
43
+ min-width: 300px;
44
+ max-width: 80%;
45
+ }
@@ -0,0 +1,223 @@
1
+ import clsx from "clsx";
2
+ import { FC, ReactNode, useRef } from "react";
3
+ import { Link } from "react-router-dom";
4
+ import { PopOver } from "../../../../components/PopOver";
5
+ import { PulsingDots } from "../../../../components/PulsingDots";
6
+ import {
7
+ useCollapseSampleEvent,
8
+ useSamplePopover,
9
+ } from "../../../../state/hooks";
10
+ import { formatDateTime, formatTime } from "../../../../utils/format";
11
+ import { parsePackageName } from "../../../../utils/python";
12
+ import { ApplicationIcons } from "../../../appearance/icons";
13
+ import { MetaDataGrid } from "../../../content/MetaDataGrid";
14
+ import { useSampleEventUrl } from "../../../routing/url";
15
+ import { kSandboxSignalName } from "../transform/fixups";
16
+ import { EventNode } from "../types";
17
+ import styles from "./OutlineRow.module.css";
18
+
19
+ export interface OutlineRowProps {
20
+ node: EventNode;
21
+ collapseScope: string;
22
+ running?: boolean;
23
+ selected?: boolean;
24
+ }
25
+
26
+ export const OutlineRow: FC<OutlineRowProps> = ({
27
+ node,
28
+ collapseScope,
29
+ running,
30
+ selected,
31
+ }) => {
32
+ const [collapsed, setCollapsed] = useCollapseSampleEvent(
33
+ collapseScope,
34
+ node.id,
35
+ );
36
+ const icon = iconForNode(node);
37
+ const toggle = toggleIcon(node, collapsed);
38
+
39
+ const popoverId = `${node.id}-popover`;
40
+ const { isShowing } = useSamplePopover(popoverId);
41
+
42
+ const ref = useRef(null);
43
+
44
+ // Get all URL parameters at component level
45
+ const sampleEventUrl = useSampleEventUrl(node.id);
46
+
47
+ return (
48
+ <>
49
+ <div
50
+ className={clsx(
51
+ styles.eventRow,
52
+ "text-size-smallest",
53
+ selected ? styles.selected : "",
54
+ )}
55
+ style={{ paddingLeft: `${node.depth * 0.4}em` }}
56
+ >
57
+ <div
58
+ className={clsx(styles.toggle)}
59
+ onClick={() => {
60
+ setCollapsed(!collapsed);
61
+ }}
62
+ >
63
+ {toggle ? <i className={clsx(toggle)} /> : undefined}
64
+ </div>
65
+ <div className={clsx(styles.label)} data-depth={node.depth}>
66
+ {icon ? <i className={clsx(icon, styles.icon)} /> : undefined}
67
+ {sampleEventUrl ? (
68
+ <Link
69
+ to={sampleEventUrl}
70
+ className={clsx(styles.eventLink)}
71
+ ref={ref}
72
+ >
73
+ {parsePackageName(labelForNode(node)).module}
74
+ </Link>
75
+ ) : (
76
+ <span ref={ref}>{parsePackageName(labelForNode(node)).module}</span>
77
+ )}
78
+ {running ? (
79
+ <PulsingDots
80
+ size="small"
81
+ className={clsx(styles.progress)}
82
+ subtle={false}
83
+ />
84
+ ) : undefined}
85
+ </div>
86
+ </div>
87
+ <PopOver
88
+ id={`${node.id}-popover`}
89
+ positionEl={ref.current}
90
+ isOpen={isShowing}
91
+ className={clsx(styles.popper)}
92
+ placement="auto-end"
93
+ >
94
+ {summarizeNode(node)}
95
+ </PopOver>
96
+ </>
97
+ );
98
+ };
99
+
100
+ const toggleIcon = (
101
+ node: EventNode,
102
+ collapsed: boolean,
103
+ ): string | undefined => {
104
+ if (node.children.length > 0) {
105
+ return collapsed
106
+ ? ApplicationIcons.chevron.right
107
+ : ApplicationIcons.chevron.down;
108
+ }
109
+ };
110
+
111
+ const iconForNode = (node: EventNode): string | undefined => {
112
+ switch (node.event.event) {
113
+ case "sample_limit":
114
+ return ApplicationIcons.limits.custom;
115
+
116
+ case "score":
117
+ return ApplicationIcons.scorer;
118
+
119
+ case "error":
120
+ return ApplicationIcons.error;
121
+ }
122
+ };
123
+
124
+ const labelForNode = (node: EventNode): string => {
125
+ if (node.event.event === "span_begin") {
126
+ switch (node.event.type) {
127
+ case "solver":
128
+ return node.event.name;
129
+ case "tool":
130
+ return node.event.name;
131
+ default: {
132
+ if (node.event.name === kSandboxSignalName) {
133
+ return "sandbox events";
134
+ }
135
+ return node.event.name;
136
+ }
137
+ }
138
+ } else {
139
+ switch (node.event.event) {
140
+ case "subtask":
141
+ return node.event.name;
142
+ case "approval":
143
+ switch (node.event.decision) {
144
+ case "approve":
145
+ return "approved";
146
+ case "reject":
147
+ return "rejected";
148
+ case "escalate":
149
+ return "escalated";
150
+ case "modify":
151
+ return "modified";
152
+ case "terminate":
153
+ return "terminated";
154
+ default:
155
+ return node.event.decision;
156
+ }
157
+ case "model":
158
+ return `model${node.event.role ? ` (${node.event.role})` : ""}`;
159
+ case "score":
160
+ return "scoring";
161
+ case "step":
162
+ if (node.event.name === kSandboxSignalName) {
163
+ return "sandbox events";
164
+ }
165
+ return node.event.name;
166
+
167
+ default:
168
+ return node.event.event;
169
+ }
170
+ }
171
+ };
172
+
173
+ export const summarizeNode = (node: EventNode): ReactNode => {
174
+ let entries: Record<string, unknown> = {};
175
+ switch (node.event.event) {
176
+ case "sample_init":
177
+ entries = {
178
+ sample_id: node.event.sample.id,
179
+ sandbox: node.event.sample.sandbox?.type,
180
+ started: formatDateTime(new Date(node.event.timestamp)),
181
+ working_start: formatTime(node.event.working_start),
182
+ };
183
+ break;
184
+
185
+ case "sample_limit":
186
+ entries = {
187
+ type: node.event.type,
188
+ message: node.event.message,
189
+ limit: node.event.limit,
190
+ started: formatDateTime(new Date(node.event.timestamp)),
191
+ working_start: formatTime(node.event.working_start),
192
+ };
193
+ break;
194
+ case "score":
195
+ entries = {
196
+ answer: node.event.score.answer,
197
+ score: node.event.score.value,
198
+ started: formatDateTime(new Date(node.event.timestamp)),
199
+ working_start: formatTime(node.event.working_start),
200
+ };
201
+ break;
202
+ case "span_begin":
203
+ entries = {
204
+ name: node.event.name,
205
+ started: formatDateTime(new Date(node.event.timestamp)),
206
+ working_start: formatTime(node.event.working_start),
207
+ };
208
+ break;
209
+ default:
210
+ entries = {
211
+ started: formatDateTime(new Date(node.event.timestamp)),
212
+ working_start: formatTime(node.event.working_start),
213
+ };
214
+ }
215
+
216
+ return (
217
+ <MetaDataGrid
218
+ entries={entries}
219
+ size="mini"
220
+ className={clsx(styles.popover, "text-size-smallest")}
221
+ />
222
+ );
223
+ };
@@ -0,0 +1,10 @@
1
+ .node {
2
+ display: grid;
3
+ column-gap: 0.3em;
4
+ grid-template-columns: max-content 1fr;
5
+ }
6
+
7
+ .panel {
8
+ margin-top: 0.65rem;
9
+ overflow: visible;
10
+ }