inspect-ai 0.3.95__py3-none-any.whl → 0.3.97__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.
- inspect_ai/_eval/eval.py +10 -2
- inspect_ai/_eval/task/util.py +32 -3
- inspect_ai/_util/local_server.py +16 -0
- inspect_ai/_util/registry.py +7 -0
- inspect_ai/_util/timer.py +13 -0
- inspect_ai/_view/www/dist/assets/index.css +275 -195
- inspect_ai/_view/www/dist/assets/index.js +8568 -7376
- inspect_ai/_view/www/src/app/App.css +1 -0
- inspect_ai/_view/www/src/app/App.tsx +27 -10
- inspect_ai/_view/www/src/app/appearance/icons.ts +5 -0
- inspect_ai/_view/www/src/app/content/RecordTree.module.css +22 -0
- inspect_ai/_view/www/src/app/content/RecordTree.tsx +370 -0
- inspect_ai/_view/www/src/app/content/RenderedContent.module.css +5 -0
- inspect_ai/_view/www/src/app/content/RenderedContent.tsx +32 -19
- inspect_ai/_view/www/src/app/content/record_processors/store.ts +101 -0
- inspect_ai/_view/www/src/app/content/record_processors/types.ts +3 -0
- inspect_ai/_view/www/src/app/content/types.ts +5 -0
- inspect_ai/_view/www/src/app/log-view/LogView.tsx +1 -0
- inspect_ai/_view/www/src/app/log-view/LogViewContainer.tsx +35 -28
- inspect_ai/_view/www/src/app/log-view/LogViewLayout.tsx +1 -8
- inspect_ai/_view/www/src/app/log-view/navbar/PrimaryBar.tsx +2 -4
- inspect_ai/_view/www/src/app/log-view/navbar/ResultsPanel.tsx +13 -3
- inspect_ai/_view/www/src/app/log-view/navbar/ScoreGrid.module.css +15 -0
- inspect_ai/_view/www/src/app/log-view/navbar/ScoreGrid.tsx +14 -10
- inspect_ai/_view/www/src/app/log-view/tabs/InfoTab.tsx +9 -3
- inspect_ai/_view/www/src/app/log-view/tabs/JsonTab.tsx +1 -3
- inspect_ai/_view/www/src/app/log-view/tabs/SamplesTab.tsx +8 -2
- inspect_ai/_view/www/src/app/log-view/types.ts +1 -0
- inspect_ai/_view/www/src/app/plan/ModelCard.module.css +7 -0
- inspect_ai/_view/www/src/app/plan/ModelCard.tsx +5 -2
- inspect_ai/_view/www/src/app/plan/PlanCard.tsx +13 -8
- inspect_ai/_view/www/src/app/routing/navigationHooks.ts +63 -8
- inspect_ai/_view/www/src/app/routing/url.ts +45 -0
- inspect_ai/_view/www/src/app/samples/InlineSampleDisplay.module.css +2 -1
- inspect_ai/_view/www/src/app/samples/InlineSampleDisplay.tsx +15 -8
- inspect_ai/_view/www/src/app/samples/SampleDialog.module.css +3 -0
- inspect_ai/_view/www/src/app/samples/SampleDialog.tsx +16 -5
- inspect_ai/_view/www/src/app/samples/SampleDisplay.module.css +9 -1
- inspect_ai/_view/www/src/app/samples/SampleDisplay.tsx +68 -31
- inspect_ai/_view/www/src/app/samples/chat/ChatMessage.module.css +12 -7
- inspect_ai/_view/www/src/app/samples/chat/ChatMessage.tsx +17 -5
- inspect_ai/_view/www/src/app/samples/chat/ChatMessageRow.module.css +9 -0
- inspect_ai/_view/www/src/app/samples/chat/ChatMessageRow.tsx +48 -18
- inspect_ai/_view/www/src/app/samples/chat/ChatView.tsx +0 -1
- inspect_ai/_view/www/src/app/samples/chat/ChatViewVirtualList.module.css +4 -0
- inspect_ai/_view/www/src/app/samples/chat/ChatViewVirtualList.tsx +41 -1
- inspect_ai/_view/www/src/app/samples/chat/messages.ts +7 -0
- inspect_ai/_view/www/src/app/samples/chat/tools/ToolCallView.module.css +0 -3
- inspect_ai/_view/www/src/app/samples/chat/tools/ToolCallView.tsx +1 -1
- inspect_ai/_view/www/src/app/samples/chat/tools/ToolInput.module.css +1 -1
- inspect_ai/_view/www/src/app/samples/chat/tools/ToolOutput.module.css +1 -1
- inspect_ai/_view/www/src/app/samples/descriptor/score/NumericScoreDescriptor.tsx +5 -1
- inspect_ai/_view/www/src/app/samples/descriptor/score/PassFailScoreDescriptor.tsx +11 -6
- inspect_ai/_view/www/src/app/samples/list/SampleList.tsx +7 -0
- inspect_ai/_view/www/src/app/samples/list/SampleRow.tsx +5 -18
- inspect_ai/_view/www/src/app/samples/sample-tools/SortFilter.tsx +1 -1
- inspect_ai/_view/www/src/app/samples/scores/SampleScoresGrid.tsx +18 -5
- inspect_ai/_view/www/src/app/samples/scores/SampleScoresView.module.css +0 -6
- inspect_ai/_view/www/src/app/samples/scores/SampleScoresView.tsx +4 -1
- inspect_ai/_view/www/src/app/samples/transcript/ApprovalEventView.tsx +4 -2
- inspect_ai/_view/www/src/app/samples/transcript/ErrorEventView.tsx +6 -4
- inspect_ai/_view/www/src/app/samples/transcript/InfoEventView.module.css +1 -1
- inspect_ai/_view/www/src/app/samples/transcript/InfoEventView.tsx +13 -6
- inspect_ai/_view/www/src/app/samples/transcript/InputEventView.tsx +6 -4
- inspect_ai/_view/www/src/app/samples/transcript/LoggerEventView.tsx +4 -2
- inspect_ai/_view/www/src/app/samples/transcript/ModelEventView.tsx +11 -8
- inspect_ai/_view/www/src/app/samples/transcript/SampleInitEventView.tsx +14 -8
- inspect_ai/_view/www/src/app/samples/transcript/SampleLimitEventView.tsx +13 -8
- inspect_ai/_view/www/src/app/samples/transcript/SandboxEventView.tsx +25 -16
- inspect_ai/_view/www/src/app/samples/transcript/ScoreEventView.tsx +7 -5
- inspect_ai/_view/www/src/app/samples/transcript/SpanEventView.tsx +11 -28
- inspect_ai/_view/www/src/app/samples/transcript/StepEventView.tsx +12 -20
- inspect_ai/_view/www/src/app/samples/transcript/SubtaskEventView.tsx +12 -31
- inspect_ai/_view/www/src/app/samples/transcript/ToolEventView.tsx +25 -29
- inspect_ai/_view/www/src/app/samples/transcript/TranscriptVirtualList.tsx +297 -0
- inspect_ai/_view/www/src/app/samples/transcript/TranscriptVirtualListComponent.module.css +0 -8
- inspect_ai/_view/www/src/app/samples/transcript/TranscriptVirtualListComponent.tsx +43 -25
- inspect_ai/_view/www/src/app/samples/transcript/event/EventPanel.module.css +43 -0
- inspect_ai/_view/www/src/app/samples/transcript/event/EventPanel.tsx +109 -43
- inspect_ai/_view/www/src/app/samples/transcript/state/StateEventView.tsx +19 -8
- inspect_ai/_view/www/src/app/samples/transcript/transform/treeify.ts +128 -60
- inspect_ai/_view/www/src/app/samples/transcript/transform/utils.ts +14 -4
- inspect_ai/_view/www/src/app/samples/transcript/types.ts +6 -4
- inspect_ai/_view/www/src/app/types.ts +12 -1
- inspect_ai/_view/www/src/components/Card.css +6 -3
- inspect_ai/_view/www/src/components/Card.tsx +15 -2
- inspect_ai/_view/www/src/components/CopyButton.tsx +4 -6
- inspect_ai/_view/www/src/components/ExpandablePanel.module.css +20 -14
- inspect_ai/_view/www/src/components/ExpandablePanel.tsx +17 -22
- inspect_ai/_view/www/src/components/LargeModal.tsx +5 -1
- inspect_ai/_view/www/src/components/LiveVirtualList.tsx +25 -1
- inspect_ai/_view/www/src/components/MarkdownDiv.css +4 -0
- inspect_ai/_view/www/src/components/MarkdownDiv.tsx +2 -2
- inspect_ai/_view/www/src/components/TabSet.module.css +6 -1
- inspect_ai/_view/www/src/components/TabSet.tsx +8 -2
- inspect_ai/_view/www/src/state/hooks.ts +83 -13
- inspect_ai/_view/www/src/state/logPolling.ts +2 -2
- inspect_ai/_view/www/src/state/logSlice.ts +1 -2
- inspect_ai/_view/www/src/state/logsSlice.ts +9 -9
- inspect_ai/_view/www/src/state/samplePolling.ts +1 -1
- inspect_ai/_view/www/src/state/sampleSlice.ts +134 -7
- inspect_ai/_view/www/src/state/scoring.ts +1 -1
- inspect_ai/_view/www/src/state/scrolling.ts +39 -6
- inspect_ai/_view/www/src/state/store.ts +5 -0
- inspect_ai/_view/www/src/state/store_filter.ts +47 -44
- inspect_ai/_view/www/src/utils/debugging.ts +95 -0
- inspect_ai/_view/www/src/utils/format.ts +2 -2
- inspect_ai/_view/www/src/utils/json.ts +29 -0
- inspect_ai/agent/__init__.py +2 -1
- inspect_ai/agent/_agent.py +12 -0
- inspect_ai/agent/_react.py +184 -48
- inspect_ai/agent/_types.py +15 -2
- inspect_ai/analysis/beta/__init__.py +11 -3
- inspect_ai/analysis/beta/_dataframe/columns.py +11 -16
- inspect_ai/analysis/beta/_dataframe/evals/table.py +101 -39
- inspect_ai/analysis/beta/_dataframe/events/columns.py +50 -0
- inspect_ai/analysis/beta/_dataframe/events/extract.py +26 -0
- inspect_ai/analysis/beta/_dataframe/events/table.py +77 -3
- inspect_ai/analysis/beta/_dataframe/extract.py +44 -25
- inspect_ai/analysis/beta/_dataframe/messages/columns.py +1 -1
- inspect_ai/analysis/beta/_dataframe/messages/table.py +30 -29
- inspect_ai/analysis/beta/_dataframe/progress.py +56 -0
- inspect_ai/analysis/beta/_dataframe/record.py +13 -9
- inspect_ai/analysis/beta/_dataframe/samples/columns.py +8 -4
- inspect_ai/analysis/beta/_dataframe/samples/extract.py +5 -33
- inspect_ai/analysis/beta/_dataframe/samples/table.py +211 -60
- inspect_ai/analysis/beta/_dataframe/util.py +33 -28
- inspect_ai/log/_file.py +9 -2
- inspect_ai/model/_call_tools.py +1 -1
- inspect_ai/model/_providers/anthropic.py +18 -5
- inspect_ai/model/_providers/azureai.py +7 -2
- inspect_ai/model/_providers/util/llama31.py +3 -3
- inspect_ai/solver/_task_state.py +1 -1
- inspect_ai/tool/_mcp/_sandbox.py +17 -14
- {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/METADATA +2 -2
- {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/RECORD +140 -133
- {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/WHEEL +1 -1
- inspect_ai/_view/www/src/app/samples/transcript/TranscriptView.module.css +0 -48
- inspect_ai/_view/www/src/app/samples/transcript/TranscriptView.tsx +0 -276
- {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/entry_points.txt +0 -0
- {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/licenses/LICENSE +0 -0
- {inspect_ai-0.3.95.dist-info → inspect_ai-0.3.97.dist-info}/top_level.txt +0 -0
@@ -14,7 +14,7 @@ import "./App.css";
|
|
14
14
|
|
15
15
|
import ClipboardJS from "clipboard";
|
16
16
|
import { FC, useCallback, useEffect } from "react";
|
17
|
-
import { RouterProvider } from "react-router-dom";
|
17
|
+
import { RouterProvider, useParams } from "react-router-dom";
|
18
18
|
import { ClientAPI, HostMessage } from "../client/api/types.ts";
|
19
19
|
import { useStore } from "../state/store.ts";
|
20
20
|
import { AppRouter } from "./routing/AppRouter.tsx";
|
@@ -27,20 +27,33 @@ interface AppProps {
|
|
27
27
|
* Renders the Main Application
|
28
28
|
*/
|
29
29
|
export const App: FC<AppProps> = ({ api }) => {
|
30
|
+
// Whether the app was rehydrated
|
31
|
+
const rehydrated = useStore((state) => state.app.rehydrated);
|
32
|
+
|
33
|
+
const logs = useStore((state) => state.logs.logs);
|
34
|
+
const selectedLogFile = useStore((state) => state.logs.selectedLogFile);
|
35
|
+
|
36
|
+
const loadedLogFile = useStore((state) => state.log.loadedLog);
|
37
|
+
const selectedLogSummary = useStore((state) => state.log.selectedLogSummary);
|
38
|
+
|
39
|
+
const setIntialState = useStore((state) => state.appActions.setInitialState);
|
30
40
|
const setAppStatus = useStore((state) => state.appActions.setStatus);
|
41
|
+
|
42
|
+
const refreshLogs = useStore((state) => state.logsActions.refreshLogs);
|
31
43
|
const setLogs = useStore((state) => state.logsActions.setLogs);
|
32
44
|
const selectLogFile = useStore((state) => state.logsActions.selectLogFile);
|
33
|
-
|
34
|
-
const rehydrated = useStore((state) => state.app.rehydrated);
|
35
|
-
const refreshLogs = useStore((state) => state.logsActions.refreshLogs);
|
45
|
+
|
36
46
|
const loadLog = useStore((state) => state.logActions.loadLog);
|
37
47
|
const pollLog = useStore((state) => state.logActions.pollLog);
|
38
|
-
|
39
|
-
const
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
48
|
+
|
49
|
+
const { sampleId } = useParams<{
|
50
|
+
logPath?: string;
|
51
|
+
tabId?: string;
|
52
|
+
sampleId?: string;
|
53
|
+
epoch?: string;
|
54
|
+
sampleTabId?: string;
|
55
|
+
}>();
|
56
|
+
const selectSample = useStore((state) => state.logActions.selectSample);
|
44
57
|
|
45
58
|
// Load a specific log
|
46
59
|
useEffect(() => {
|
@@ -53,6 +66,10 @@ export const App: FC<AppProps> = ({ api }) => {
|
|
53
66
|
// Then load the log
|
54
67
|
await loadLog(selectedLogFile);
|
55
68
|
|
69
|
+
if (!sampleId) {
|
70
|
+
selectSample(0);
|
71
|
+
}
|
72
|
+
|
56
73
|
// Finally set loading to false
|
57
74
|
setAppStatus({ loading: false, error: undefined });
|
58
75
|
} catch (e) {
|
@@ -62,6 +62,7 @@ export const ApplicationIcons = {
|
|
62
62
|
time: "bi bi-clock",
|
63
63
|
execution: "bi bi-stopwatch",
|
64
64
|
},
|
65
|
+
link: "bi bi-link-45deg",
|
65
66
|
logging: loggingIcons,
|
66
67
|
menu: "bi bi-list",
|
67
68
|
messages: "bi bi-chat-right-text",
|
@@ -101,5 +102,9 @@ export const ApplicationIcons = {
|
|
101
102
|
step: "bi bi-fast-forward-btn",
|
102
103
|
subtask: "bi bi-subtract",
|
103
104
|
transcript: "bi bi-list-columns-reverse",
|
105
|
+
tree: {
|
106
|
+
open: "bi bi-caret-down-fill",
|
107
|
+
closed: "bi bi-caret-right-fill",
|
108
|
+
},
|
104
109
|
usage: "bi bi-stopwatch",
|
105
110
|
};
|
@@ -0,0 +1,22 @@
|
|
1
|
+
.keyPairContainer {
|
2
|
+
display: grid;
|
3
|
+
grid-template-columns: max-content auto;
|
4
|
+
column-gap: 0.5em;
|
5
|
+
padding-top: 4px;
|
6
|
+
padding-bottom: 4px;
|
7
|
+
border-bottom: solid 1px var(--bs-border-color);
|
8
|
+
}
|
9
|
+
|
10
|
+
.key {
|
11
|
+
display: grid;
|
12
|
+
grid-template-columns: 1em auto;
|
13
|
+
cursor: pointer;
|
14
|
+
}
|
15
|
+
|
16
|
+
.pre {
|
17
|
+
margin-bottom: 0;
|
18
|
+
}
|
19
|
+
|
20
|
+
.treeIcon {
|
21
|
+
margin-top: -3px;
|
22
|
+
}
|
@@ -0,0 +1,370 @@
|
|
1
|
+
import clsx from "clsx";
|
2
|
+
import {
|
3
|
+
FC,
|
4
|
+
KeyboardEvent,
|
5
|
+
RefObject,
|
6
|
+
useCallback,
|
7
|
+
useEffect,
|
8
|
+
useMemo,
|
9
|
+
useRef,
|
10
|
+
} from "react";
|
11
|
+
import { Virtuoso, VirtuosoHandle } from "react-virtuoso";
|
12
|
+
import { RenderedContent } from "./RenderedContent";
|
13
|
+
|
14
|
+
import { useCollapsibleIds } from "../../state/hooks";
|
15
|
+
import { useVirtuosoState } from "../../state/scrolling";
|
16
|
+
import { useStore } from "../../state/store";
|
17
|
+
import { ApplicationIcons } from "../appearance/icons";
|
18
|
+
import styles from "./RecordTree.module.css";
|
19
|
+
import { resolveStoreKeys } from "./record_processors/store";
|
20
|
+
import { RecordProcessor } from "./record_processors/types";
|
21
|
+
|
22
|
+
const kRecordTreeKey = "record-tree-key";
|
23
|
+
|
24
|
+
interface RecordTreeProps {
|
25
|
+
id: string;
|
26
|
+
record: Record<string, unknown>;
|
27
|
+
className?: string | string[];
|
28
|
+
scrollRef?: RefObject<HTMLDivElement | null>;
|
29
|
+
defaultExpandLevel?: number;
|
30
|
+
processStore?: boolean;
|
31
|
+
}
|
32
|
+
|
33
|
+
/**
|
34
|
+
* Renders the MetaDataView component.
|
35
|
+
*/
|
36
|
+
export const RecordTree: FC<RecordTreeProps> = ({
|
37
|
+
id,
|
38
|
+
record,
|
39
|
+
className,
|
40
|
+
scrollRef,
|
41
|
+
defaultExpandLevel = 1,
|
42
|
+
processStore = false,
|
43
|
+
}) => {
|
44
|
+
// The virtual list handle and state
|
45
|
+
const listHandle = useRef<VirtuosoHandle | null>(null);
|
46
|
+
const { getRestoreState } = useVirtuosoState(
|
47
|
+
listHandle,
|
48
|
+
`metadata-grid-${id}`,
|
49
|
+
);
|
50
|
+
|
51
|
+
// Collapse state
|
52
|
+
const [collapsedIds, setCollapsed, clearIds] = useCollapsibleIds(id);
|
53
|
+
const setCollapsedIds = useStore(
|
54
|
+
(state) => state.sampleActions.setCollapsedIds,
|
55
|
+
);
|
56
|
+
|
57
|
+
// Clear the collapsed ids when the component unmounts
|
58
|
+
useEffect(() => {
|
59
|
+
return () => {
|
60
|
+
clearIds();
|
61
|
+
};
|
62
|
+
}, [clearIds, id]);
|
63
|
+
|
64
|
+
// Tree-ify the record (creates a flat lsit of items with depth property)
|
65
|
+
const items = useMemo(() => {
|
66
|
+
return toTreeItems(
|
67
|
+
record,
|
68
|
+
collapsedIds || {},
|
69
|
+
processStore ? [resolveStoreKeys] : [],
|
70
|
+
);
|
71
|
+
}, [record, collapsedIds, processStore]);
|
72
|
+
|
73
|
+
// If collapsedIds is not set, we need to set it to the default state
|
74
|
+
useEffect(() => {
|
75
|
+
if (collapsedIds) {
|
76
|
+
return;
|
77
|
+
}
|
78
|
+
|
79
|
+
const defaultCollapsedIds = items.reduce((prev, item) => {
|
80
|
+
if (item.depth >= defaultExpandLevel && item.hasChildren) {
|
81
|
+
return {
|
82
|
+
...prev,
|
83
|
+
[item.id]: true,
|
84
|
+
};
|
85
|
+
}
|
86
|
+
return prev;
|
87
|
+
}, {});
|
88
|
+
setCollapsedIds(id, defaultCollapsedIds);
|
89
|
+
}, [collapsedIds, items]);
|
90
|
+
|
91
|
+
// Keyboard handling for tree
|
92
|
+
const keyUpHandler = useCallback(
|
93
|
+
(itemId: string, index: number) => {
|
94
|
+
return (event: KeyboardEvent) => {
|
95
|
+
switch (event.key) {
|
96
|
+
case "Enter":
|
97
|
+
event.preventDefault();
|
98
|
+
event.stopPropagation();
|
99
|
+
setCollapsed(itemId, !collapsedIds?.[id]);
|
100
|
+
break;
|
101
|
+
case "ArrowDown": {
|
102
|
+
event.preventDefault();
|
103
|
+
event.stopPropagation();
|
104
|
+
// focus next
|
105
|
+
if (index === items.length - 1) {
|
106
|
+
return;
|
107
|
+
}
|
108
|
+
const treeRoot = document.getElementById(id);
|
109
|
+
const nextEl = treeRoot?.querySelector(
|
110
|
+
`.${kRecordTreeKey}[data-index="${index + 1}"]`,
|
111
|
+
);
|
112
|
+
if (nextEl) {
|
113
|
+
(nextEl as HTMLElement).focus();
|
114
|
+
}
|
115
|
+
break;
|
116
|
+
}
|
117
|
+
case "ArrowUp": {
|
118
|
+
event.preventDefault();
|
119
|
+
event.stopPropagation();
|
120
|
+
// focus previous
|
121
|
+
if (index === 0) {
|
122
|
+
return;
|
123
|
+
}
|
124
|
+
const treeRoot = document.getElementById(id);
|
125
|
+
const prevEl = treeRoot?.querySelector(
|
126
|
+
`.${kRecordTreeKey}[data-index="${index - 1}"]`,
|
127
|
+
);
|
128
|
+
if (prevEl) {
|
129
|
+
(prevEl as HTMLElement).focus();
|
130
|
+
}
|
131
|
+
break;
|
132
|
+
}
|
133
|
+
case "ArrowRight":
|
134
|
+
event.preventDefault();
|
135
|
+
event.stopPropagation();
|
136
|
+
setCollapsed(itemId, false);
|
137
|
+
break;
|
138
|
+
case "ArrowLeft":
|
139
|
+
event.preventDefault();
|
140
|
+
event.stopPropagation();
|
141
|
+
setCollapsed(itemId, true);
|
142
|
+
break;
|
143
|
+
}
|
144
|
+
};
|
145
|
+
},
|
146
|
+
[collapsedIds, items],
|
147
|
+
);
|
148
|
+
|
149
|
+
const renderRow = (index: number) => {
|
150
|
+
const item = items[index];
|
151
|
+
|
152
|
+
return (
|
153
|
+
<div
|
154
|
+
key={item.id}
|
155
|
+
className={clsx(styles.keyPairContainer, "text-size-small")}
|
156
|
+
style={{
|
157
|
+
paddingLeft: `${item.depth * 20}px`,
|
158
|
+
}}
|
159
|
+
>
|
160
|
+
<div
|
161
|
+
data-index={index}
|
162
|
+
className={clsx(
|
163
|
+
kRecordTreeKey,
|
164
|
+
styles.key,
|
165
|
+
"font-monospace",
|
166
|
+
"text-style-secondary",
|
167
|
+
)}
|
168
|
+
onKeyUp={keyUpHandler(item.id, index)}
|
169
|
+
tabIndex={0}
|
170
|
+
onClick={() => {
|
171
|
+
setCollapsed(item.id, !collapsedIds?.[item.id]);
|
172
|
+
}}
|
173
|
+
>
|
174
|
+
<div>
|
175
|
+
{item.hasChildren ? (
|
176
|
+
<pre className={clsx(styles.pre)}>
|
177
|
+
<i
|
178
|
+
className={clsx(
|
179
|
+
collapsedIds && collapsedIds[item.id]
|
180
|
+
? ApplicationIcons.tree.closed
|
181
|
+
: ApplicationIcons.tree.open,
|
182
|
+
styles.treeIcon,
|
183
|
+
)}
|
184
|
+
/>
|
185
|
+
</pre>
|
186
|
+
) : undefined}
|
187
|
+
</div>
|
188
|
+
<pre className={clsx(styles.pre)}>{item.key}:</pre>
|
189
|
+
</div>
|
190
|
+
<div>
|
191
|
+
{item.value !== null &&
|
192
|
+
(!item.hasChildren || collapsedIds?.[item.id]) ? (
|
193
|
+
<RenderedContent
|
194
|
+
id={`${id}-value-${item.id}`}
|
195
|
+
entry={{
|
196
|
+
name: item.key,
|
197
|
+
value: item.value,
|
198
|
+
}}
|
199
|
+
renderOptions={{ renderString: "pre" }}
|
200
|
+
/>
|
201
|
+
) : undefined}
|
202
|
+
</div>
|
203
|
+
</div>
|
204
|
+
);
|
205
|
+
};
|
206
|
+
|
207
|
+
return (
|
208
|
+
<Virtuoso
|
209
|
+
ref={listHandle}
|
210
|
+
customScrollParent={scrollRef?.current ? scrollRef.current : undefined}
|
211
|
+
id={id}
|
212
|
+
style={{ width: "100%", height: "100%" }}
|
213
|
+
data={items}
|
214
|
+
defaultItemHeight={50}
|
215
|
+
itemContent={renderRow}
|
216
|
+
atBottomThreshold={30}
|
217
|
+
increaseViewportBy={{ top: 300, bottom: 300 }}
|
218
|
+
overscan={{
|
219
|
+
main: 10,
|
220
|
+
reverse: 10,
|
221
|
+
}}
|
222
|
+
className={clsx(className, "samples-list")}
|
223
|
+
skipAnimationFrameInResizeObserver={true}
|
224
|
+
restoreStateFrom={getRestoreState()}
|
225
|
+
tabIndex={0}
|
226
|
+
/>
|
227
|
+
);
|
228
|
+
};
|
229
|
+
|
230
|
+
interface MetadataItem {
|
231
|
+
id: string;
|
232
|
+
key: string;
|
233
|
+
value: string | number | boolean | null;
|
234
|
+
depth: number;
|
235
|
+
hasChildren: boolean;
|
236
|
+
}
|
237
|
+
|
238
|
+
export const toTreeItems = (
|
239
|
+
record: Record<string, unknown>,
|
240
|
+
collapsedIds: Record<string, boolean>,
|
241
|
+
recordProcessors: RecordProcessor[] = [],
|
242
|
+
currentDepth = 0,
|
243
|
+
currentPath: string[] = [],
|
244
|
+
): MetadataItem[] => {
|
245
|
+
if (!record) {
|
246
|
+
return [];
|
247
|
+
}
|
248
|
+
|
249
|
+
// Apply any record processors
|
250
|
+
if (recordProcessors.length > 0) {
|
251
|
+
for (const processor of recordProcessors) {
|
252
|
+
record = processor(record);
|
253
|
+
}
|
254
|
+
}
|
255
|
+
|
256
|
+
const result: MetadataItem[] = [];
|
257
|
+
|
258
|
+
Object.entries(record).forEach(([key, value], index) => {
|
259
|
+
const itemSegment = index.toString();
|
260
|
+
result.push(
|
261
|
+
...processNodeRecursive(
|
262
|
+
key,
|
263
|
+
value,
|
264
|
+
currentDepth,
|
265
|
+
currentPath,
|
266
|
+
itemSegment,
|
267
|
+
collapsedIds,
|
268
|
+
),
|
269
|
+
);
|
270
|
+
});
|
271
|
+
|
272
|
+
return result;
|
273
|
+
};
|
274
|
+
|
275
|
+
const processNodeRecursive = (
|
276
|
+
key: string,
|
277
|
+
value: unknown,
|
278
|
+
depth: number,
|
279
|
+
parentPath: string[],
|
280
|
+
thisPath: string,
|
281
|
+
collapsedIds: Record<string, boolean>,
|
282
|
+
): MetadataItem[] => {
|
283
|
+
const items: MetadataItem[] = [];
|
284
|
+
const currentItemPath = [...parentPath, thisPath];
|
285
|
+
const id = `${depth}.${currentItemPath.join(".")}`;
|
286
|
+
|
287
|
+
if (isPrimitiveOrNull(value)) {
|
288
|
+
items.push({
|
289
|
+
id,
|
290
|
+
key,
|
291
|
+
value: value === undefined ? null : value,
|
292
|
+
depth,
|
293
|
+
hasChildren: false,
|
294
|
+
});
|
295
|
+
return items;
|
296
|
+
}
|
297
|
+
|
298
|
+
// For non-primitives (objects, arrays, functions, etc.)
|
299
|
+
let displayValue: string | number | boolean | null = null;
|
300
|
+
let processChildren = false;
|
301
|
+
|
302
|
+
if (Array.isArray(value)) {
|
303
|
+
processChildren = true;
|
304
|
+
displayValue = `Array(${value.length})`;
|
305
|
+
} else if (typeof value === "object" && value !== null) {
|
306
|
+
processChildren = true;
|
307
|
+
displayValue = `Object(${Object.keys(value).length})`;
|
308
|
+
} else {
|
309
|
+
// Other types like functions, symbols. These are treated as leaf nodes.
|
310
|
+
displayValue = String(value);
|
311
|
+
processChildren = false;
|
312
|
+
}
|
313
|
+
|
314
|
+
// Add the item
|
315
|
+
items.push({ id, key, value: displayValue, depth, hasChildren: true });
|
316
|
+
|
317
|
+
// Process children
|
318
|
+
if (processChildren && !collapsedIds[id]) {
|
319
|
+
const childDepth = depth + 1;
|
320
|
+
if (Array.isArray(value)) {
|
321
|
+
if (value.length > 0) {
|
322
|
+
value.forEach((element, index) => {
|
323
|
+
const elementKey = `[${index}]`;
|
324
|
+
const elementIdentifier = `[${index}]`;
|
325
|
+
items.push(
|
326
|
+
...processNodeRecursive(
|
327
|
+
elementKey,
|
328
|
+
element,
|
329
|
+
childDepth,
|
330
|
+
currentItemPath,
|
331
|
+
elementIdentifier,
|
332
|
+
collapsedIds,
|
333
|
+
),
|
334
|
+
);
|
335
|
+
});
|
336
|
+
}
|
337
|
+
} else if (typeof value === "object" && value !== null) {
|
338
|
+
// Process object properties
|
339
|
+
Object.entries(value as Record<string, unknown>).forEach(
|
340
|
+
([childKey, childValue], index) => {
|
341
|
+
const childIdentifier = index.toString();
|
342
|
+
items.push(
|
343
|
+
...processNodeRecursive(
|
344
|
+
childKey,
|
345
|
+
childValue,
|
346
|
+
childDepth,
|
347
|
+
currentItemPath,
|
348
|
+
childIdentifier,
|
349
|
+
collapsedIds,
|
350
|
+
),
|
351
|
+
);
|
352
|
+
},
|
353
|
+
);
|
354
|
+
}
|
355
|
+
}
|
356
|
+
|
357
|
+
return items;
|
358
|
+
};
|
359
|
+
|
360
|
+
const isPrimitiveOrNull = (
|
361
|
+
value: unknown,
|
362
|
+
): value is string | number | boolean | null | undefined => {
|
363
|
+
return (
|
364
|
+
value === null ||
|
365
|
+
value === undefined ||
|
366
|
+
typeof value === "string" ||
|
367
|
+
typeof value === "number" ||
|
368
|
+
typeof value === "boolean"
|
369
|
+
);
|
370
|
+
};
|
@@ -11,11 +11,12 @@ import { FC, Fragment, isValidElement, JSX, ReactNode } from "react";
|
|
11
11
|
import JSONPanel from "../../components/JsonPanel";
|
12
12
|
import { isJson } from "../../utils/json";
|
13
13
|
import styles from "./RenderedContent.module.css";
|
14
|
-
import { Buckets, ContentRenderer } from "./types";
|
14
|
+
import { Buckets, ContentRenderer, RenderOptions } from "./types";
|
15
15
|
|
16
16
|
interface RenderedContentProps {
|
17
17
|
id: string;
|
18
18
|
entry: { name: string; value: unknown };
|
19
|
+
renderOptions?: RenderOptions;
|
19
20
|
}
|
20
21
|
|
21
22
|
/**
|
@@ -24,6 +25,7 @@ interface RenderedContentProps {
|
|
24
25
|
export const RenderedContent: FC<RenderedContentProps> = ({
|
25
26
|
id,
|
26
27
|
entry,
|
28
|
+
renderOptions = { renderString: "markdown" },
|
27
29
|
}): JSX.Element => {
|
28
30
|
// Explicitly specify return type
|
29
31
|
if (entry.value === null) {
|
@@ -42,7 +44,7 @@ export const RenderedContent: FC<RenderedContentProps> = ({
|
|
42
44
|
});
|
43
45
|
|
44
46
|
if (renderer) {
|
45
|
-
const { rendered } = renderer.render(id, entry);
|
47
|
+
const { rendered } = renderer.render(id, entry, renderOptions);
|
46
48
|
if (rendered !== undefined && isValidElement(rendered)) {
|
47
49
|
return rendered;
|
48
50
|
}
|
@@ -75,7 +77,7 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
75
77
|
typeof entry.value === "string" && entry.value.indexOf("\u001b") > -1
|
76
78
|
);
|
77
79
|
},
|
78
|
-
render: (_id, entry) => {
|
80
|
+
render: (_id, entry, _options) => {
|
79
81
|
return {
|
80
82
|
rendered: <ANSIDisplay output={entry.value} />,
|
81
83
|
};
|
@@ -90,17 +92,18 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
90
92
|
}
|
91
93
|
return false;
|
92
94
|
},
|
93
|
-
render: (_id, entry) => {
|
95
|
+
render: (_id, entry, _options) => {
|
94
96
|
const obj = JSON5.parse(entry.value);
|
95
97
|
return { rendered: <JSONPanel data={obj as Record<string, unknown>} /> };
|
96
98
|
},
|
97
99
|
},
|
100
|
+
|
98
101
|
Model: {
|
99
102
|
bucket: Buckets.intermediate,
|
100
103
|
canRender: (entry) => {
|
101
104
|
return typeof entry.value === "object" && entry.value._model;
|
102
105
|
},
|
103
|
-
render: (_id, entry) => {
|
106
|
+
render: (_id, entry, _options) => {
|
104
107
|
return {
|
105
108
|
rendered: (
|
106
109
|
<Fragment>
|
@@ -115,9 +118,9 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
115
118
|
canRender: (entry) => {
|
116
119
|
return typeof entry.value === "boolean";
|
117
120
|
},
|
118
|
-
render: (id, entry) => {
|
121
|
+
render: (id, entry, options) => {
|
119
122
|
entry.value = entry.value.toString();
|
120
|
-
return contentRenderers.String.render(id, entry);
|
123
|
+
return contentRenderers.String.render(id, entry, options);
|
121
124
|
},
|
122
125
|
},
|
123
126
|
Number: {
|
@@ -125,9 +128,9 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
125
128
|
canRender: (entry) => {
|
126
129
|
return typeof entry.value === "number";
|
127
130
|
},
|
128
|
-
render: (id, entry) => {
|
131
|
+
render: (id, entry, options) => {
|
129
132
|
entry.value = formatNumber(entry.value);
|
130
|
-
return contentRenderers.String.render(id, entry);
|
133
|
+
return contentRenderers.String.render(id, entry, options);
|
131
134
|
},
|
132
135
|
},
|
133
136
|
String: {
|
@@ -135,11 +138,21 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
135
138
|
canRender: (entry) => {
|
136
139
|
return typeof entry.value === "string";
|
137
140
|
},
|
138
|
-
render: (_id, entry) => {
|
141
|
+
render: (_id, entry, options) => {
|
139
142
|
const rendered = entry.value.trim();
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
+
if (options.renderString === "markdown") {
|
144
|
+
return {
|
145
|
+
rendered: rendered,
|
146
|
+
};
|
147
|
+
} else {
|
148
|
+
return {
|
149
|
+
rendered: (
|
150
|
+
<pre className={clsx(styles.preWrap, styles.preCompact)}>
|
151
|
+
{rendered}
|
152
|
+
</pre>
|
153
|
+
),
|
154
|
+
};
|
155
|
+
}
|
143
156
|
},
|
144
157
|
},
|
145
158
|
Array: {
|
@@ -162,7 +175,7 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
162
175
|
return false;
|
163
176
|
}
|
164
177
|
},
|
165
|
-
render: (id, entry) => {
|
178
|
+
render: (id, entry, _options) => {
|
166
179
|
const arrayMap: Record<string, unknown> = {};
|
167
180
|
entry.value.forEach((e: unknown, index: number) => {
|
168
181
|
arrayMap[`[${index}]`] = e;
|
@@ -186,7 +199,7 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
186
199
|
canRender: (entry) => {
|
187
200
|
return typeof entry.value === "object" && entry.name === "web_search";
|
188
201
|
},
|
189
|
-
render: (_id, entry) => {
|
202
|
+
render: (_id, entry, _options) => {
|
190
203
|
const results: ReactNode[] = [];
|
191
204
|
results.push(
|
192
205
|
<div className={styles.query}>
|
@@ -219,7 +232,7 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
219
232
|
typeof entry.value === "string" && entry.name?.startsWith("web_browser")
|
220
233
|
);
|
221
234
|
},
|
222
|
-
render: (_id, entry) => {
|
235
|
+
render: (_id, entry, _options) => {
|
223
236
|
return {
|
224
237
|
rendered: <pre className={styles.preWrap}>{entry.value}</pre>,
|
225
238
|
};
|
@@ -230,7 +243,7 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
230
243
|
canRender: (entry) => {
|
231
244
|
return typeof entry.value === "object" && entry.value._html;
|
232
245
|
},
|
233
|
-
render: (_id, entry) => {
|
246
|
+
render: (_id, entry, _options) => {
|
234
247
|
return {
|
235
248
|
rendered: entry.value._html,
|
236
249
|
};
|
@@ -243,7 +256,7 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
243
256
|
typeof entry.value === "string" && entry.value.startsWith("data:image/")
|
244
257
|
);
|
245
258
|
},
|
246
|
-
render: (_id, entry) => {
|
259
|
+
render: (_id, entry, _options) => {
|
247
260
|
return {
|
248
261
|
rendered: <img src={entry.value} />,
|
249
262
|
};
|
@@ -254,7 +267,7 @@ const contentRenderers: Record<string, ContentRenderer> = {
|
|
254
267
|
canRender: (entry) => {
|
255
268
|
return typeof entry.value === "object";
|
256
269
|
},
|
257
|
-
render: (id, entry) => {
|
270
|
+
render: (id, entry, _options) => {
|
258
271
|
return {
|
259
272
|
rendered: (
|
260
273
|
<MetaDataView
|