inspect-ai 0.3.108__py3-none-any.whl → 0.3.109__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/task/log.py +1 -1
- inspect_ai/_eval/task/run.py +1 -1
- inspect_ai/_util/dateutil.py +40 -0
- inspect_ai/_view/schema.py +11 -0
- inspect_ai/_view/www/CLAUDE.md +1 -1
- inspect_ai/_view/www/dist/assets/index.css +2068 -1796
- inspect_ai/_view/www/dist/assets/index.js +7951 -3643
- inspect_ai/_view/www/package.json +3 -2
- inspect_ai/_view/www/src/@types/log.d.ts +5 -5
- inspect_ai/_view/www/src/app/App.css +71 -4
- inspect_ai/_view/www/src/app/App.tsx +7 -0
- inspect_ai/_view/www/src/app/appearance/icons.ts +18 -2
- inspect_ai/_view/www/src/app/content/RenderedContent.tsx +7 -9
- inspect_ai/_view/www/src/app/log-list/LogItem.ts +18 -0
- inspect_ai/_view/www/src/app/log-list/LogListFooter.module.css +55 -0
- inspect_ai/_view/www/src/app/log-list/LogListFooter.tsx +67 -0
- inspect_ai/_view/www/src/app/log-list/LogPager.module.css +29 -0
- inspect_ai/_view/www/src/app/log-list/LogPager.tsx +134 -0
- inspect_ai/_view/www/src/app/log-list/LogsFilterInput.module.css +5 -0
- inspect_ai/_view/www/src/app/log-list/LogsFilterInput.tsx +31 -0
- inspect_ai/_view/www/src/app/log-list/LogsPanel.module.css +12 -0
- inspect_ai/_view/www/src/app/log-list/LogsPanel.tsx +178 -0
- inspect_ai/_view/www/src/app/log-list/grid/LogListGrid.module.css +115 -0
- inspect_ai/_view/www/src/app/log-list/grid/LogListGrid.tsx +304 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/CompletedDate.module.css +6 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/CompletedDate.tsx +64 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/EmptyCell.module.css +3 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/EmptyCell.tsx +7 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/FileName.module.css +20 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/FileName.tsx +52 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/Icon.module.css +11 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/Icon.tsx +35 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/Model.module.css +6 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/Model.tsx +34 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/Score.module.css +6 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/Score.tsx +61 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/Status.module.css +15 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/Status.tsx +95 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/Task.module.css +20 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/Task.tsx +50 -0
- inspect_ai/_view/www/src/app/log-list/grid/columns/columns.ts +27 -0
- inspect_ai/_view/www/src/app/log-view/LogView.tsx +2 -5
- inspect_ai/_view/www/src/app/log-view/LogViewContainer.tsx +4 -30
- inspect_ai/_view/www/src/app/log-view/LogViewLayout.tsx +5 -30
- inspect_ai/_view/www/src/app/log-view/tabs/TaskTab.tsx +4 -7
- inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/PrimaryBar.module.css +2 -0
- inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/PrimaryBar.tsx +3 -31
- inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ResultsPanel.tsx +7 -57
- inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ScoreGrid.tsx +2 -2
- inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/SecondaryBar.tsx +7 -1
- inspect_ai/_view/www/src/app/log-view/{navbar/Navbar.tsx → title-view/TitleView.tsx} +3 -6
- inspect_ai/_view/www/src/app/navbar/Navbar.module.css +57 -0
- inspect_ai/_view/www/src/app/navbar/Navbar.tsx +117 -0
- inspect_ai/_view/www/src/app/navbar/useBreadcrumbTruncation.ts +128 -0
- inspect_ai/_view/www/src/app/plan/DatasetDetailView.tsx +3 -3
- inspect_ai/_view/www/src/app/plan/DetailStep.tsx +6 -6
- inspect_ai/_view/www/src/app/plan/PlanDetailView.module.css +1 -0
- inspect_ai/_view/www/src/app/plan/ScorerDetailView.tsx +1 -1
- inspect_ai/_view/www/src/app/routing/AppRouter.tsx +28 -4
- inspect_ai/_view/www/src/app/routing/RouteDispatcher.tsx +28 -0
- inspect_ai/_view/www/src/app/routing/sampleNavigation.ts +76 -7
- inspect_ai/_view/www/src/app/routing/url.ts +193 -20
- inspect_ai/_view/www/src/app/samples/SampleDisplay.tsx +3 -17
- inspect_ai/_view/www/src/app/samples/descriptor/score/ScoreDescriptor.tsx +1 -1
- inspect_ai/_view/www/src/app/samples/transcript/SubtaskEventView.tsx +2 -2
- inspect_ai/_view/www/src/app/samples/transcript/TranscriptPanel.tsx +2 -2
- inspect_ai/_view/www/src/app/samples/transcript/outline/tree-visitors.ts +5 -0
- inspect_ai/_view/www/src/app/samples/transcript/transform/treeify.ts +26 -10
- inspect_ai/_view/www/src/app/types.ts +21 -1
- inspect_ai/_view/www/src/client/api/api-http.ts +2 -1
- inspect_ai/_view/www/src/client/api/api-shared.ts +0 -32
- inspect_ai/_view/www/src/client/api/client-api.ts +1 -1
- inspect_ai/_view/www/src/client/remote/remoteLogFile.ts +38 -6
- inspect_ai/_view/www/src/components/TextInput.module.css +45 -0
- inspect_ai/_view/www/src/components/TextInput.tsx +52 -0
- inspect_ai/_view/www/src/constants.ts +18 -0
- inspect_ai/_view/www/src/img/inspect-16.svg +10 -0
- inspect_ai/_view/www/src/img/inspect-back.svg +5 -0
- inspect_ai/_view/www/src/img/inspect-file.svg +26 -0
- inspect_ai/_view/www/src/img/inspect-forward.svg +7 -0
- inspect_ai/_view/www/src/img/inspect-home.svg +18 -0
- inspect_ai/_view/www/src/scoring/metrics.ts +75 -0
- inspect_ai/_view/www/src/scoring/scores.ts +19 -0
- inspect_ai/_view/www/src/scoring/types.ts +11 -0
- inspect_ai/_view/www/src/state/appSlice.ts +27 -7
- inspect_ai/_view/www/src/state/clientEvents.ts +73 -0
- inspect_ai/_view/www/src/state/clientEventsService.ts +105 -0
- inspect_ai/_view/www/src/state/hooks.ts +118 -1
- inspect_ai/_view/www/src/state/log.ts +19 -0
- inspect_ai/_view/www/src/state/logPolling.ts +3 -1
- inspect_ai/_view/www/src/state/logSlice.ts +9 -0
- inspect_ai/_view/www/src/state/logsSlice.ts +157 -15
- inspect_ai/_view/www/src/state/samplePolling.ts +4 -2
- inspect_ai/_view/www/src/tests/utils/path.test.ts +3 -3
- inspect_ai/_view/www/src/utils/evallog.ts +31 -0
- inspect_ai/_view/www/src/utils/path.ts +28 -0
- inspect_ai/_view/www/src/utils/uri.ts +49 -0
- inspect_ai/_view/www/yarn.lock +54 -17
- inspect_ai/analysis/beta/_dataframe/util.py +106 -10
- inspect_ai/log/_recorders/buffer/database.py +55 -16
- inspect_ai/model/_model.py +1 -1
- inspect_ai/model/_providers/providers.py +2 -2
- inspect_ai/model/_providers/vertex.py +3 -0
- inspect_ai/tool/_mcp/_mcp.py +6 -1
- inspect_ai/tool/_mcp/sampling.py +8 -1
- inspect_ai/tool/_tools/_bash_session.py +3 -6
- inspect_ai/tool/_tools/_web_browser/_web_browser.py +3 -8
- inspect_ai/util/_anyio.py +12 -3
- {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/METADATA +2 -2
- {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/RECORD +124 -94
- inspect_ai/_util/datetime.py +0 -10
- inspect_ai/_view/www/src/app/content/MetaDataView.module.css +0 -35
- inspect_ai/_view/www/src/app/content/MetaDataView.tsx +0 -101
- inspect_ai/_view/www/src/app/log-view/utils.ts +0 -34
- inspect_ai/_view/www/src/app/sidebar/EvalStatus.module.css +0 -15
- inspect_ai/_view/www/src/app/sidebar/EvalStatus.tsx +0 -72
- inspect_ai/_view/www/src/app/sidebar/LogDirectoryTitleView.module.css +0 -16
- inspect_ai/_view/www/src/app/sidebar/LogDirectoryTitleView.tsx +0 -70
- inspect_ai/_view/www/src/app/sidebar/Sidebar.module.css +0 -77
- inspect_ai/_view/www/src/app/sidebar/Sidebar.tsx +0 -119
- inspect_ai/_view/www/src/app/sidebar/SidebarLogEntry.module.css +0 -29
- inspect_ai/_view/www/src/app/sidebar/SidebarLogEntry.tsx +0 -96
- inspect_ai/_view/www/src/app/sidebar/SidebarScoreView.module.css +0 -23
- inspect_ai/_view/www/src/app/sidebar/SidebarScoreView.tsx +0 -44
- inspect_ai/_view/www/src/app/sidebar/SidebarScoresView.module.css +0 -35
- inspect_ai/_view/www/src/app/sidebar/SidebarScoresView.tsx +0 -63
- inspect_ai/_view/www/src/state/logsPolling.ts +0 -118
- /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ModelRolesView.module.css +0 -0
- /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ModelRolesView.tsx +0 -0
- /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ResultsPanel.module.css +0 -0
- /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/RunningStatusPanel.module.css +0 -0
- /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/RunningStatusPanel.tsx +0 -0
- /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/ScoreGrid.module.css +0 -0
- /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/SecondaryBar.module.css +0 -0
- /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/StatusPanel.module.css +0 -0
- /inspect_ai/_view/www/src/app/log-view/{navbar → title-view}/StatusPanel.tsx +0 -0
- /inspect_ai/_view/www/src/app/log-view/{navbar/Navbar.module.css → title-view/TitleView.module.css} +0 -0
- {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/WHEEL +0 -0
- {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/entry_points.txt +0 -0
- {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/licenses/LICENSE +0 -0
- {inspect_ai-0.3.108.dist-info → inspect_ai-0.3.109.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
import { FC } from "react";
|
2
|
+
import { LogsPanel } from "../log-list/LogsPanel";
|
3
|
+
import { LogViewContainer } from "../log-view/LogViewContainer";
|
4
|
+
import { useLogRouteParams } from "./url";
|
5
|
+
|
6
|
+
/**
|
7
|
+
* RouteDispatcher component that determines whether to show LogsView or LogViewContainer
|
8
|
+
* based on the logPath parameter. If logPath ends with .eval or .json, it shows the
|
9
|
+
* individual log view. Otherwise, it shows the logs directory view.
|
10
|
+
*/
|
11
|
+
export const RouteDispatcher: FC = () => {
|
12
|
+
const { logPath } = useLogRouteParams();
|
13
|
+
|
14
|
+
// If no logPath is provided, show the logs directory view
|
15
|
+
if (!logPath) {
|
16
|
+
return <LogsPanel />;
|
17
|
+
}
|
18
|
+
|
19
|
+
// Check if the path ends with .eval or .json (indicating it's a log file)
|
20
|
+
const isLogFile = logPath.endsWith(".eval") || logPath.endsWith(".json");
|
21
|
+
|
22
|
+
// Route to the appropriate component
|
23
|
+
if (isLogFile) {
|
24
|
+
return <LogViewContainer />;
|
25
|
+
} else {
|
26
|
+
return <LogsPanel />;
|
27
|
+
}
|
28
|
+
};
|
@@ -1,9 +1,82 @@
|
|
1
1
|
import { useCallback } from "react";
|
2
|
-
import { useNavigate,
|
2
|
+
import { useNavigate, useSearchParams } from "react-router-dom";
|
3
3
|
import { useFilteredSamples } from "../../state/hooks";
|
4
4
|
import { useStore } from "../../state/store";
|
5
5
|
import { directoryRelativeUrl } from "../../utils/uri";
|
6
|
-
import { logUrlRaw, sampleUrl } from "./url";
|
6
|
+
import { logUrl, logUrlRaw, sampleUrl, useLogRouteParams } from "./url";
|
7
|
+
|
8
|
+
export const useLogNavigation = () => {
|
9
|
+
const navigate = useNavigate();
|
10
|
+
const { logPath } = useLogRouteParams();
|
11
|
+
const logs = useStore((state) => state.logs.logs);
|
12
|
+
const loadedLog = useStore((state) => state.log.loadedLog);
|
13
|
+
|
14
|
+
const selectTab = useCallback(
|
15
|
+
(tabId: string) => {
|
16
|
+
// Only update URL if we have a loaded log
|
17
|
+
if (loadedLog && logPath) {
|
18
|
+
// We already have the logPath from params, just navigate to the tab
|
19
|
+
const url = logUrlRaw(logPath, tabId);
|
20
|
+
navigate(url);
|
21
|
+
} else if (loadedLog) {
|
22
|
+
// Fallback to constructing the path if needed
|
23
|
+
const url = logUrl(loadedLog, logs.log_dir, tabId);
|
24
|
+
navigate(url);
|
25
|
+
}
|
26
|
+
},
|
27
|
+
[loadedLog, logPath, logs.log_dir, navigate],
|
28
|
+
);
|
29
|
+
|
30
|
+
return {
|
31
|
+
selectTab,
|
32
|
+
};
|
33
|
+
};
|
34
|
+
|
35
|
+
export const useSampleUrl = () => {
|
36
|
+
const { logPath, tabId, sampleTabId } = useLogRouteParams();
|
37
|
+
|
38
|
+
const logDirectory = useStore((state) => state.logs.logs.log_dir);
|
39
|
+
|
40
|
+
const selectedLogFile = useStore((state) => state.logs.selectedLogFile);
|
41
|
+
|
42
|
+
// Helper function to resolve the log path for URLs
|
43
|
+
const resolveLogPath = useCallback(() => {
|
44
|
+
// If we have a logPath from URL params, use that
|
45
|
+
if (logPath) {
|
46
|
+
return logPath;
|
47
|
+
}
|
48
|
+
|
49
|
+
if (selectedLogFile) {
|
50
|
+
return directoryRelativeUrl(selectedLogFile, logDirectory);
|
51
|
+
}
|
52
|
+
|
53
|
+
return undefined;
|
54
|
+
}, [logPath, selectedLogFile, logDirectory]);
|
55
|
+
|
56
|
+
// Get a sample URL for a specific sample
|
57
|
+
const getSampleUrl = useCallback(
|
58
|
+
(
|
59
|
+
sampleId: string | number,
|
60
|
+
epoch: number,
|
61
|
+
specificSampleTabId?: string,
|
62
|
+
) => {
|
63
|
+
const resolvedPath = resolveLogPath();
|
64
|
+
if (resolvedPath) {
|
65
|
+
const currentSampleTabId = specificSampleTabId || sampleTabId;
|
66
|
+
const url = sampleUrl(
|
67
|
+
resolvedPath,
|
68
|
+
sampleId,
|
69
|
+
epoch,
|
70
|
+
currentSampleTabId,
|
71
|
+
);
|
72
|
+
return url;
|
73
|
+
}
|
74
|
+
return undefined;
|
75
|
+
},
|
76
|
+
[resolveLogPath, tabId, sampleTabId],
|
77
|
+
);
|
78
|
+
return getSampleUrl;
|
79
|
+
};
|
7
80
|
|
8
81
|
/**
|
9
82
|
* Hook that provides sample navigation utilities with proper URL handling
|
@@ -16,11 +89,7 @@ export const useSampleNavigation = () => {
|
|
16
89
|
const logDirectory = useStore((state) => state.logs.logs.log_dir);
|
17
90
|
|
18
91
|
// The log
|
19
|
-
const { logPath, tabId, sampleTabId } =
|
20
|
-
logPath?: string;
|
21
|
-
tabId?: string;
|
22
|
-
sampleTabId?: string;
|
23
|
-
}>();
|
92
|
+
const { logPath, tabId, sampleTabId } = useLogRouteParams();
|
24
93
|
|
25
94
|
// Get the store access values directly in the hook
|
26
95
|
const selectedLogFile = useStore((state) => state.logs.selectedLogFile);
|
@@ -1,12 +1,185 @@
|
|
1
1
|
import { useMemo } from "react";
|
2
2
|
import { useParams } from "react-router-dom";
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
kSampleMessagesTabId,
|
5
|
+
kSampleTabIds,
|
6
|
+
kSampleTranscriptTabId,
|
7
|
+
kWorkspaceTabs,
|
8
|
+
} from "../../constants";
|
4
9
|
import { useStore } from "../../state/store";
|
5
|
-
import { directoryRelativeUrl } from "../../utils/uri";
|
10
|
+
import { directoryRelativeUrl, encodePathParts } from "../../utils/uri";
|
6
11
|
|
7
|
-
|
12
|
+
/**
|
13
|
+
* Decodes a URL parameter that may be URL-encoded.
|
14
|
+
* Safely handles already decoded strings.
|
15
|
+
*/
|
16
|
+
export const decodeUrlParam = (
|
17
|
+
param: string | undefined,
|
18
|
+
): string | undefined => {
|
19
|
+
if (!param) return param;
|
20
|
+
try {
|
21
|
+
return decodeURIComponent(param);
|
22
|
+
} catch {
|
23
|
+
// If decoding fails, return the original string
|
24
|
+
return param;
|
25
|
+
}
|
26
|
+
};
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Hook that provides URL parameters with automatic decoding.
|
30
|
+
* Use this instead of useParams when you need the actual unencoded values.
|
31
|
+
*/
|
32
|
+
export const useDecodedParams = <
|
33
|
+
T extends Record<string, string | undefined>,
|
34
|
+
>() => {
|
35
|
+
const params = useParams<T>();
|
36
|
+
|
37
|
+
const decodedParams = useMemo(() => {
|
38
|
+
const decoded = {} as T;
|
39
|
+
Object.entries(params).forEach(([key, value]) => {
|
40
|
+
(decoded as any)[key] = decodeUrlParam(value as string);
|
41
|
+
});
|
42
|
+
return decoded;
|
43
|
+
}, [params]);
|
44
|
+
|
45
|
+
return decodedParams;
|
46
|
+
};
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Hook that parses log route parameters from the splat route.
|
50
|
+
* Handles nested paths properly by parsing the full path after /logs/
|
51
|
+
*/
|
52
|
+
export const useLogRouteParams = () => {
|
53
|
+
const params = useParams<{
|
54
|
+
"*": string;
|
55
|
+
sampleId?: string;
|
56
|
+
epoch?: string;
|
57
|
+
sampleTabId?: string;
|
58
|
+
}>();
|
59
|
+
|
60
|
+
return useMemo(() => {
|
61
|
+
const splatPath = params["*"] || "";
|
62
|
+
|
63
|
+
// Check for full sample route pattern in splat path (when route params aren't populated)
|
64
|
+
// Pattern: logPath/samples/sample/sampleId/epoch/sampleTabId (with optional trailing slash)
|
65
|
+
const fullSampleUrlMatch = splatPath.match(
|
66
|
+
/^(.+?)\/samples\/sample\/([^/]+)(?:\/([^/]+)(?:\/(.+?))?)?\/?\s*$/,
|
67
|
+
);
|
68
|
+
if (fullSampleUrlMatch) {
|
69
|
+
const [, logPath, sampleId, epoch, sampleTabId] = fullSampleUrlMatch;
|
70
|
+
return {
|
71
|
+
logPath: decodeUrlParam(logPath),
|
72
|
+
tabId: undefined,
|
73
|
+
sampleTabId: decodeUrlParam(sampleTabId),
|
74
|
+
sampleId: decodeUrlParam(sampleId),
|
75
|
+
epoch: epoch ? decodeUrlParam(epoch) : undefined,
|
76
|
+
};
|
77
|
+
}
|
78
|
+
|
79
|
+
// Check for sample URLs that might not match the formal route pattern
|
80
|
+
// (this is the single sample case, where is there is now sampleid/epoch, just sampletabid)
|
81
|
+
// Pattern: /logs/*/samples/sampleId/epoch or /logs/*/samples/sampleId or /logs/*/samples/sampleTabId
|
82
|
+
const sampleUrlMatch = splatPath.match(
|
83
|
+
/^(.+?)\/samples(?:\/([^/]+)(?:\/([^/]+))?)?$/,
|
84
|
+
);
|
85
|
+
if (sampleUrlMatch) {
|
86
|
+
const [, logPath, firstSegment, secondSegment] = sampleUrlMatch;
|
87
|
+
|
88
|
+
if (firstSegment) {
|
89
|
+
// Define known sample tab IDs
|
90
|
+
const validSampleTabIds = new Set(kSampleTabIds);
|
91
|
+
|
92
|
+
if (validSampleTabIds.has(firstSegment) && !secondSegment) {
|
93
|
+
// This is /logs/*/samples/sampleTabId
|
94
|
+
return {
|
95
|
+
logPath: decodeUrlParam(logPath),
|
96
|
+
tabId: "samples",
|
97
|
+
sampleTabId: decodeUrlParam(firstSegment),
|
98
|
+
sampleId: undefined,
|
99
|
+
epoch: undefined,
|
100
|
+
};
|
101
|
+
} else {
|
102
|
+
// This is a sample URL with sampleId (and possibly epoch)
|
103
|
+
return {
|
104
|
+
logPath: decodeUrlParam(logPath),
|
105
|
+
tabId: undefined,
|
106
|
+
sampleTabId: undefined,
|
107
|
+
sampleId: decodeUrlParam(firstSegment),
|
108
|
+
epoch: secondSegment ? decodeUrlParam(secondSegment) : undefined,
|
109
|
+
};
|
110
|
+
}
|
111
|
+
} else {
|
112
|
+
// This is just /logs/*/samples (samples listing)
|
113
|
+
return {
|
114
|
+
logPath: decodeUrlParam(logPath),
|
115
|
+
tabId: "samples",
|
116
|
+
sampleTabId: undefined,
|
117
|
+
sampleId: undefined,
|
118
|
+
epoch: undefined,
|
119
|
+
};
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
// Regular log route pattern: /logs/path/to/file.eval/tabId?
|
124
|
+
// Split the path and check if the last segment might be a tabId
|
125
|
+
const pathSegments = splatPath.split("/").filter(Boolean);
|
126
|
+
|
127
|
+
if (pathSegments.length === 0) {
|
128
|
+
return {
|
129
|
+
logPath: undefined,
|
130
|
+
tabId: undefined,
|
131
|
+
sampleTabId: undefined,
|
132
|
+
sampleId: undefined,
|
133
|
+
epoch: undefined,
|
134
|
+
};
|
135
|
+
}
|
136
|
+
|
137
|
+
// Define valid tab IDs for log view
|
138
|
+
const validTabIds = new Set(kWorkspaceTabs);
|
139
|
+
|
140
|
+
// Look for the first valid tab ID from right to left
|
141
|
+
let tabIdIndex = -1;
|
142
|
+
let foundTabId: string | undefined = undefined;
|
143
|
+
|
144
|
+
for (let i = pathSegments.length - 1; i >= 0; i--) {
|
145
|
+
const segment = pathSegments[i];
|
146
|
+
const decodedSegment = decodeUrlParam(segment) || segment;
|
147
|
+
|
148
|
+
if (validTabIds.has(decodedSegment)) {
|
149
|
+
tabIdIndex = i;
|
150
|
+
foundTabId = decodedSegment;
|
151
|
+
break;
|
152
|
+
}
|
153
|
+
}
|
154
|
+
|
155
|
+
if (foundTabId && tabIdIndex > 0) {
|
156
|
+
// Found a valid tab ID, split the path there
|
157
|
+
const logPath = pathSegments.slice(0, tabIdIndex).join("/");
|
158
|
+
|
159
|
+
return {
|
160
|
+
logPath: decodeUrlParam(logPath),
|
161
|
+
tabId: foundTabId,
|
162
|
+
sampleTabId: undefined,
|
163
|
+
sampleId: undefined,
|
164
|
+
epoch: undefined,
|
165
|
+
};
|
166
|
+
} else {
|
167
|
+
// No valid tab ID found, the entire path is the logPath
|
168
|
+
return {
|
169
|
+
logPath: decodeUrlParam(splatPath),
|
170
|
+
tabId: undefined,
|
171
|
+
sampleTabId: undefined,
|
172
|
+
sampleId: undefined,
|
173
|
+
epoch: undefined,
|
174
|
+
};
|
175
|
+
}
|
176
|
+
}, [params]);
|
177
|
+
};
|
178
|
+
|
179
|
+
export const kLogsRoutUrlPattern = "/logs";
|
180
|
+
export const kLogRouteUrlPattern = "/logs/*";
|
8
181
|
export const kSampleRouteUrlPattern =
|
9
|
-
"/logs
|
182
|
+
"/logs/*/samples/sample/:sampleId/:epoch?/:sampleTabId?";
|
10
183
|
|
11
184
|
export const baseUrl = (
|
12
185
|
logPath: string,
|
@@ -26,10 +199,17 @@ export const sampleUrl = (
|
|
26
199
|
sampleEpoch?: string | number,
|
27
200
|
sampleTabId?: string,
|
28
201
|
) => {
|
202
|
+
// Ensure logPath is decoded before encoding for URL construction
|
203
|
+
const decodedLogPath = decodeUrlParam(logPath) || logPath;
|
204
|
+
|
29
205
|
if (sampleId !== undefined && sampleEpoch !== undefined) {
|
30
|
-
return
|
206
|
+
return encodePathParts(
|
207
|
+
`/logs/${decodedLogPath}/samples/sample/${sampleId}/${sampleEpoch}/${sampleTabId || ""}`,
|
208
|
+
);
|
31
209
|
} else {
|
32
|
-
return
|
210
|
+
return encodePathParts(
|
211
|
+
`/logs/${decodedLogPath}/samples/${sampleTabId || ""}`,
|
212
|
+
);
|
33
213
|
}
|
34
214
|
};
|
35
215
|
|
@@ -58,12 +238,7 @@ export const useSampleMessageUrl = (
|
|
58
238
|
logPath: urlLogPath,
|
59
239
|
sampleId: urlSampleId,
|
60
240
|
epoch: urlEpoch,
|
61
|
-
} =
|
62
|
-
logPath?: string;
|
63
|
-
tabId?: string;
|
64
|
-
sampleId?: string;
|
65
|
-
epoch?: string;
|
66
|
-
}>();
|
241
|
+
} = useLogRouteParams();
|
67
242
|
|
68
243
|
const log_file = useStore((state) => state.logs.selectedLogFile);
|
69
244
|
const log_dir = useStore((state) => state.logs.logs.log_dir);
|
@@ -95,12 +270,7 @@ export const useSampleEventUrl = (
|
|
95
270
|
logPath: urlLogPath,
|
96
271
|
sampleId: urlSampleId,
|
97
272
|
epoch: urlEpoch,
|
98
|
-
} =
|
99
|
-
logPath?: string;
|
100
|
-
tabId?: string;
|
101
|
-
sampleId?: string;
|
102
|
-
epoch?: string;
|
103
|
-
}>();
|
273
|
+
} = useLogRouteParams();
|
104
274
|
|
105
275
|
const log_file = useStore((state) => state.logs.selectedLogFile);
|
106
276
|
const log_dir = useStore((state) => state.logs.logs.log_dir);
|
@@ -149,10 +319,13 @@ export const makeLogPath = (log_file: string, log_dir?: string) => {
|
|
149
319
|
};
|
150
320
|
|
151
321
|
export const logUrlRaw = (log_segment: string, tabId?: string) => {
|
322
|
+
// Ensure log_segment is decoded before encoding for URL construction
|
323
|
+
const decodedLogSegment = decodeUrlParam(log_segment) || log_segment;
|
324
|
+
|
152
325
|
if (tabId) {
|
153
|
-
return `/logs/${
|
326
|
+
return encodePathParts(`/logs/${decodedLogSegment}/${tabId}`);
|
154
327
|
} else {
|
155
|
-
return `/logs/${
|
328
|
+
return encodePathParts(`/logs/${decodedLogSegment}`);
|
156
329
|
}
|
157
330
|
};
|
158
331
|
|
@@ -38,7 +38,7 @@ import { estimateSize } from "../../utils/json";
|
|
38
38
|
import { printHeadingHtml, printHtml } from "../../utils/print";
|
39
39
|
import { RecordTree } from "../content/RecordTree";
|
40
40
|
import { useSampleDetailNavigation } from "../routing/sampleNavigation";
|
41
|
-
import { sampleUrl } from "../routing/url";
|
41
|
+
import { sampleUrl, useLogRouteParams } from "../routing/url";
|
42
42
|
import { ModelTokenTable } from "../usage/ModelTokenTable";
|
43
43
|
import { ChatViewVirtualList } from "./chat/ChatViewVirtualList";
|
44
44
|
import { messagesFromEvents } from "./chat/messages";
|
@@ -113,15 +113,9 @@ export const SampleDisplay: FC<SampleDisplayProps> = ({ id, scrollRef }) => {
|
|
113
113
|
// Get all URL parameters at component level
|
114
114
|
const {
|
115
115
|
logPath: urlLogPath,
|
116
|
-
tabId: urlTabId,
|
117
116
|
sampleId: urlSampleId,
|
118
117
|
epoch: urlEpoch,
|
119
|
-
} =
|
120
|
-
logPath?: string;
|
121
|
-
tabId?: string;
|
122
|
-
sampleId?: string;
|
123
|
-
epoch?: string;
|
124
|
-
}>();
|
118
|
+
} = useLogRouteParams();
|
125
119
|
|
126
120
|
// Tab selection
|
127
121
|
const onSelectedTab = useCallback(
|
@@ -136,15 +130,7 @@ export const SampleDisplay: FC<SampleDisplayProps> = ({ id, scrollRef }) => {
|
|
136
130
|
navigate(url);
|
137
131
|
}
|
138
132
|
},
|
139
|
-
[
|
140
|
-
sampleTabId,
|
141
|
-
urlLogPath,
|
142
|
-
urlTabId,
|
143
|
-
urlSampleId,
|
144
|
-
urlEpoch,
|
145
|
-
navigate,
|
146
|
-
setSelectedTab,
|
147
|
-
],
|
133
|
+
[sampleTabId, urlLogPath, urlSampleId, urlEpoch, navigate, setSelectedTab],
|
148
134
|
);
|
149
135
|
|
150
136
|
const sampleMetadatas = metadataViewsForSample(
|
@@ -2,7 +2,7 @@ import clsx from "clsx";
|
|
2
2
|
import { FC, ReactNode } from "react";
|
3
3
|
import { Input2, Input5, Result2, SubtaskEvent } from "../../../@types/log";
|
4
4
|
import { ApplicationIcons } from "../../appearance/icons";
|
5
|
-
import {
|
5
|
+
import { MetaDataGrid } from "../../content/MetaDataGrid";
|
6
6
|
import { EventPanel } from "./event/EventPanel";
|
7
7
|
import { formatTiming, formatTitle } from "./event/utils";
|
8
8
|
import styles from "./SubtaskEventView.module.css";
|
@@ -106,7 +106,7 @@ const Rendered: FC<RenderedProps> = ({ values }) => {
|
|
106
106
|
if (Object.keys(values).length === 0) {
|
107
107
|
return <None />;
|
108
108
|
} else {
|
109
|
-
return <
|
109
|
+
return <MetaDataGrid entries={values as Record<string, unknown>} />;
|
110
110
|
}
|
111
111
|
} else {
|
112
112
|
return values;
|
@@ -1,10 +1,10 @@
|
|
1
1
|
import clsx from "clsx";
|
2
2
|
import { FC, memo, RefObject } from "react";
|
3
|
-
import { useParams } from "react-router-dom";
|
4
3
|
import { Events } from "../../../@types/log";
|
5
4
|
import { StickyScroll } from "../../../components/StickyScroll";
|
6
5
|
import { useCollapsedState } from "../../../state/hooks";
|
7
6
|
import { ApplicationIcons } from "../../appearance/icons";
|
7
|
+
import { useLogRouteParams } from "../../routing/url";
|
8
8
|
import { TranscriptOutline } from "./outline/TranscriptOutline";
|
9
9
|
import styles from "./TranscriptPanel.module.css";
|
10
10
|
import { TranscriptVirtualList } from "./TranscriptVirtualList";
|
@@ -29,7 +29,7 @@ export const TranscriptPanel: FC<TranscriptPanelProps> = memo((props) => {
|
|
29
29
|
events,
|
30
30
|
running === true,
|
31
31
|
);
|
32
|
-
const { logPath } =
|
32
|
+
const { logPath } = useLogRouteParams();
|
33
33
|
|
34
34
|
const [collapsed, setCollapsed] = useCollapsedState(
|
35
35
|
`transcript-panel-${logPath || "na"}`,
|
@@ -141,6 +141,11 @@ export const collapseTurns = (eventNodes: EventNode[]): EventNode[] => {
|
|
141
141
|
|
142
142
|
for (const node of eventNodes) {
|
143
143
|
if (node.event.event === "span_begin" && node.event.type === kTurnType) {
|
144
|
+
// Check depth to ensure we are collecting turns at the same level
|
145
|
+
if (collecting.length > 0 && collecting[0].depth !== node.depth) {
|
146
|
+
collect();
|
147
|
+
}
|
148
|
+
|
144
149
|
collecting.push(node);
|
145
150
|
} else {
|
146
151
|
collect();
|
@@ -349,15 +349,31 @@ const transformers = () => {
|
|
349
349
|
},
|
350
350
|
{
|
351
351
|
name: "unwrap_handoff",
|
352
|
-
matches: (node) =>
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
352
|
+
matches: (node) => {
|
353
|
+
const isHandoffNode =
|
354
|
+
node.event.event === SPAN_BEGIN &&
|
355
|
+
node.event["type"] === TYPE_HANDOFF;
|
356
|
+
|
357
|
+
if (!isHandoffNode) {
|
358
|
+
return false;
|
359
|
+
}
|
360
|
+
|
361
|
+
if (node.children.length === 1) {
|
362
|
+
return (
|
363
|
+
node.children[0].event.event === TOOL &&
|
364
|
+
!!node.children[0].event.agent
|
365
|
+
);
|
366
|
+
} else {
|
367
|
+
return (
|
368
|
+
node.children.length === 2 &&
|
369
|
+
node.children[0].event.event === TOOL &&
|
370
|
+
node.children[1].event.event === STORE &&
|
371
|
+
node.children[0].children.length === 2 &&
|
372
|
+
node.children[0].children[0].event.event === SPAN_BEGIN &&
|
373
|
+
node.children[0].children[0].event.type === TYPE_AGENT
|
374
|
+
);
|
375
|
+
}
|
376
|
+
},
|
361
377
|
process: (node) => skipThisNode(node),
|
362
378
|
},
|
363
379
|
{
|
@@ -423,7 +439,7 @@ const skipFirstChildNode = (node: EventNode): EventNode => {
|
|
423
439
|
const skipThisNode = (node: EventNode): EventNode => {
|
424
440
|
const newNode = { ...node.children[0] };
|
425
441
|
newNode.depth = node.depth;
|
426
|
-
newNode.children = reduceDepth(newNode.children
|
442
|
+
newNode.children = reduceDepth(newNode.children, 2);
|
427
443
|
return newNode;
|
428
444
|
};
|
429
445
|
|
@@ -1,3 +1,8 @@
|
|
1
|
+
import {
|
2
|
+
ColumnFiltersState,
|
3
|
+
ColumnResizeMode,
|
4
|
+
SortingState,
|
5
|
+
} from "@tanstack/react-table";
|
1
6
|
import { StateSnapshot } from "react-virtuoso";
|
2
7
|
import {
|
3
8
|
ApprovalEvent,
|
@@ -22,6 +27,7 @@ import {
|
|
22
27
|
EvalLogHeader,
|
23
28
|
EvalSummary,
|
24
29
|
EventData,
|
30
|
+
LogFile,
|
25
31
|
LogFiles,
|
26
32
|
PendingSamples,
|
27
33
|
SampleSummary,
|
@@ -30,7 +36,6 @@ import { ScorerInfo } from "../state/scoring";
|
|
30
36
|
|
31
37
|
export interface AppState {
|
32
38
|
status: AppStatus;
|
33
|
-
offcanvas: boolean;
|
34
39
|
showFind: boolean;
|
35
40
|
tabs: {
|
36
41
|
workspace: string;
|
@@ -51,6 +56,8 @@ export interface AppState {
|
|
51
56
|
sample_epoch?: string;
|
52
57
|
};
|
53
58
|
rehydrated?: boolean;
|
59
|
+
pagination: Record<string, { page: number; pageSize: number }>;
|
60
|
+
singleFileMode?: boolean;
|
54
61
|
}
|
55
62
|
|
56
63
|
export interface LogsState {
|
@@ -59,6 +66,19 @@ export interface LogsState {
|
|
59
66
|
headersLoading: boolean;
|
60
67
|
selectedLogIndex: number;
|
61
68
|
selectedLogFile?: string;
|
69
|
+
listing: LogsListing;
|
70
|
+
loadingFiles: Set<string>;
|
71
|
+
pendingRequests: Map<string, Promise<EvalLogHeader | null>>;
|
72
|
+
}
|
73
|
+
|
74
|
+
export interface LogsListing {
|
75
|
+
sorting?: SortingState;
|
76
|
+
filtering?: ColumnFiltersState;
|
77
|
+
globalFilter?: string;
|
78
|
+
columnResizeMode?: ColumnResizeMode;
|
79
|
+
columnSizes?: Record<string, number>;
|
80
|
+
filteredCount?: number;
|
81
|
+
watchedLogs?: LogFile[];
|
62
82
|
}
|
63
83
|
|
64
84
|
export interface LogState {
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import { EvalLog } from "../../@types/log";
|
2
2
|
import { asyncJsonParse } from "../../utils/json-worker";
|
3
|
+
import { encodePathParts } from "../../utils/uri";
|
3
4
|
import { fetchRange, fetchSize } from "../remote/remoteZipFile";
|
4
|
-
import { download_file
|
5
|
+
import { download_file } from "./api-shared";
|
5
6
|
import {
|
6
7
|
Capabilities,
|
7
8
|
LogContents,
|
@@ -13,35 +13,3 @@ export async function download_file(
|
|
13
13
|
link.click();
|
14
14
|
document.body.removeChild(link);
|
15
15
|
}
|
16
|
-
|
17
|
-
/**
|
18
|
-
* Encodes the path segments of a URL or relative path to ensure special characters
|
19
|
-
* (like `+`, spaces, etc.) are properly encoded without affecting legal characters like `/`.
|
20
|
-
*
|
21
|
-
* This function will encode file names and path portions of both absolute URLs and
|
22
|
-
* relative paths. It ensures that components of a full URL, such as the protocol and
|
23
|
-
* query parameters, remain intact, while only encoding the path.
|
24
|
-
*/
|
25
|
-
export function encodePathParts(url: string): string {
|
26
|
-
if (!url) return url; // Handle empty strings
|
27
|
-
|
28
|
-
try {
|
29
|
-
// Parse a full Uri
|
30
|
-
const fullUrl = new URL(url);
|
31
|
-
fullUrl.pathname = fullUrl.pathname
|
32
|
-
.split("/")
|
33
|
-
.map((segment) =>
|
34
|
-
segment ? encodeURIComponent(decodeURIComponent(segment)) : "",
|
35
|
-
)
|
36
|
-
.join("/");
|
37
|
-
return fullUrl.toString();
|
38
|
-
} catch {
|
39
|
-
// This is a relative path that isn't parseable as Uri
|
40
|
-
return url
|
41
|
-
.split("/")
|
42
|
-
.map((segment) =>
|
43
|
-
segment ? encodeURIComponent(decodeURIComponent(segment)) : "",
|
44
|
-
)
|
45
|
-
.join("/");
|
46
|
-
}
|
47
|
-
}
|
@@ -1,11 +1,11 @@
|
|
1
1
|
import { EvalLog, EvalSample } from "../../@types/log";
|
2
|
+
import { encodePathParts } from "../../utils/uri";
|
2
3
|
import {
|
3
4
|
openRemoteLogFile,
|
4
5
|
RemoteLogFile,
|
5
6
|
SampleNotFoundError,
|
6
7
|
} from "../remote/remoteLogFile";
|
7
8
|
import { FileSizeLimitError } from "../remote/remoteZipFile";
|
8
|
-
import { encodePathParts } from "./api-shared";
|
9
9
|
import {
|
10
10
|
ClientAPI,
|
11
11
|
EvalSummary,
|