inspect-ai 0.3.59__py3-none-any.whl → 0.3.60__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 (74) hide show
  1. inspect_ai/_cli/eval.py +0 -7
  2. inspect_ai/_display/textual/widgets/samples.py +1 -1
  3. inspect_ai/_eval/eval.py +10 -1
  4. inspect_ai/_eval/loader.py +79 -19
  5. inspect_ai/_eval/registry.py +6 -0
  6. inspect_ai/_eval/score.py +2 -1
  7. inspect_ai/_eval/task/results.py +6 -5
  8. inspect_ai/_eval/task/run.py +11 -11
  9. inspect_ai/_view/www/dist/assets/index.js +262 -303
  10. inspect_ai/_view/www/src/App.mjs +6 -6
  11. inspect_ai/_view/www/src/Types.mjs +1 -1
  12. inspect_ai/_view/www/src/api/Types.ts +133 -0
  13. inspect_ai/_view/www/src/api/{api-browser.mjs → api-browser.ts} +25 -13
  14. inspect_ai/_view/www/src/api/api-http.ts +219 -0
  15. inspect_ai/_view/www/src/api/api-shared.ts +47 -0
  16. inspect_ai/_view/www/src/api/{api-vscode.mjs → api-vscode.ts} +22 -19
  17. inspect_ai/_view/www/src/api/{client-api.mjs → client-api.ts} +93 -53
  18. inspect_ai/_view/www/src/api/index.ts +51 -0
  19. inspect_ai/_view/www/src/api/jsonrpc.ts +225 -0
  20. inspect_ai/_view/www/src/components/DownloadButton.mjs +1 -1
  21. inspect_ai/_view/www/src/index.js +2 -2
  22. inspect_ai/_view/www/src/log/{remoteLogFile.mjs → remoteLogFile.ts} +62 -46
  23. inspect_ai/_view/www/src/navbar/Navbar.mjs +1 -1
  24. inspect_ai/_view/www/src/navbar/SecondaryBar.mjs +1 -1
  25. inspect_ai/_view/www/src/samples/SampleList.mjs +1 -1
  26. inspect_ai/_view/www/src/samples/SampleScores.mjs +1 -1
  27. inspect_ai/_view/www/src/samples/SamplesDescriptor.mjs +14 -14
  28. inspect_ai/_view/www/src/samples/SamplesTab.mjs +10 -10
  29. inspect_ai/_view/www/src/samples/tools/SortFilter.mjs +2 -2
  30. inspect_ai/_view/www/src/utils/{Json.mjs → json-worker.ts} +1 -3
  31. inspect_ai/_view/www/src/utils/vscode.ts +36 -0
  32. inspect_ai/_view/www/src/workspace/WorkSpace.mjs +1 -1
  33. inspect_ai/approval/_human/manager.py +1 -1
  34. inspect_ai/model/_call_tools.py +55 -0
  35. inspect_ai/model/_conversation.py +1 -4
  36. inspect_ai/model/_generate_config.py +2 -8
  37. inspect_ai/model/_model_output.py +15 -0
  38. inspect_ai/model/_openai.py +383 -0
  39. inspect_ai/model/_providers/anthropic.py +52 -11
  40. inspect_ai/model/_providers/azureai.py +1 -1
  41. inspect_ai/model/_providers/goodfire.py +248 -0
  42. inspect_ai/model/_providers/groq.py +7 -3
  43. inspect_ai/model/_providers/hf.py +6 -0
  44. inspect_ai/model/_providers/mistral.py +2 -1
  45. inspect_ai/model/_providers/openai.py +36 -202
  46. inspect_ai/model/_providers/openai_o1.py +2 -4
  47. inspect_ai/model/_providers/providers.py +22 -0
  48. inspect_ai/model/_providers/together.py +4 -4
  49. inspect_ai/model/_providers/util/__init__.py +2 -3
  50. inspect_ai/model/_providers/util/hf_handler.py +1 -1
  51. inspect_ai/model/_providers/util/llama31.py +1 -1
  52. inspect_ai/model/_providers/util/util.py +0 -76
  53. inspect_ai/scorer/_metric.py +3 -0
  54. inspect_ai/scorer/_scorer.py +2 -1
  55. inspect_ai/solver/__init__.py +2 -0
  56. inspect_ai/solver/_basic_agent.py +1 -1
  57. inspect_ai/solver/_bridge/__init__.py +3 -0
  58. inspect_ai/solver/_bridge/bridge.py +100 -0
  59. inspect_ai/solver/_bridge/patch.py +170 -0
  60. inspect_ai/solver/_solver.py +6 -0
  61. inspect_ai/util/_display.py +5 -0
  62. inspect_ai/util/_sandbox/docker/prereqs.py +1 -1
  63. {inspect_ai-0.3.59.dist-info → inspect_ai-0.3.60.dist-info}/METADATA +3 -2
  64. {inspect_ai-0.3.59.dist-info → inspect_ai-0.3.60.dist-info}/RECORD +68 -63
  65. inspect_ai/_view/www/src/api/Types.mjs +0 -117
  66. inspect_ai/_view/www/src/api/api-http.mjs +0 -300
  67. inspect_ai/_view/www/src/api/api-shared.mjs +0 -10
  68. inspect_ai/_view/www/src/api/index.mjs +0 -49
  69. inspect_ai/_view/www/src/api/jsonrpc.mjs +0 -208
  70. inspect_ai/_view/www/src/utils/vscode.mjs +0 -16
  71. {inspect_ai-0.3.59.dist-info → inspect_ai-0.3.60.dist-info}/LICENSE +0 -0
  72. {inspect_ai-0.3.59.dist-info → inspect_ai-0.3.60.dist-info}/WHEEL +0 -0
  73. {inspect_ai-0.3.59.dist-info → inspect_ai-0.3.60.dist-info}/entry_points.txt +0 -0
  74. {inspect_ai-0.3.59.dist-info → inspect_ai-0.3.60.dist-info}/top_level.txt +0 -0
@@ -30,7 +30,7 @@ import { Sidebar } from "./sidebar/Sidebar.mjs";
30
30
  import { WorkSpace } from "./workspace/WorkSpace.mjs";
31
31
  import { FindBand } from "./components/FindBand.mjs";
32
32
  import { isVscode } from "./utils/Html.mjs";
33
- import { getVscodeApi } from "./utils/vscode.mjs";
33
+ import { getVscodeApi } from "./utils/vscode";
34
34
  import { kDefaultSort } from "./constants.mjs";
35
35
  import {
36
36
  createEvalDescriptor,
@@ -51,7 +51,7 @@ import {
51
51
  * Renders the Main Application
52
52
  *
53
53
  * @param {Object} props - The parameters for the component.
54
- * @param {import("./api/Types.mjs").ClientAPI} props.api - The api that this view should use
54
+ * @param {import("./api/Types.ts").ClientAPI} props.api - The api that this view should use
55
55
  * @param {Object} [props.initialState] - Initial state for app (optional, used by VS Code extension)
56
56
  * @param {(state: Object) => void} [props.saveInitialState] - Save initial state for app (optional, used by VS Code extension)
57
57
  * @param {boolean} props.pollForLogs - Whether the application should poll for log changes
@@ -507,12 +507,12 @@ export function App({
507
507
  * Determines whether the workspace tab should display samples or info,
508
508
  * depending on the presence of samples and the log status.
509
509
  *
510
- * @param {import("./api/Types.mjs").EvalSummary} log - The log object containing sample summaries and status.
510
+ * @param {import("./api/Types.ts").EvalSummary} log - The log object containing sample summaries and status.
511
511
  * @returns {void}
512
512
  */
513
513
  const resetWorkspace = useCallback(
514
514
  /**
515
- * @param {import("./api/Types.mjs").EvalSummary} log
515
+ * @param {import("./api/Types.ts").EvalSummary} log
516
516
  */
517
517
  (log) => {
518
518
  // Reset the workspace tab
@@ -959,7 +959,7 @@ export function App({
959
959
  /**
960
960
  * Determines the default scorer for a log
961
961
  *
962
- * @param {import("./api/Types.mjs").EvalSummary} log - The log object containing sample summaries and status.
962
+ * @param {import("./api/Types.ts").EvalSummary} log - The log object containing sample summaries and status.
963
963
  * @returns {{name: string, scorer: string} | undefined} A scorer object with name and scorer properties, or undefined
964
964
  */
965
965
  const defaultScorer = (log) => {
@@ -981,7 +981,7 @@ const defaultScorer = (log) => {
981
981
  /**
982
982
  * Determines the default scorers for a log
983
983
  *
984
- * @param {import("./api/Types.mjs").EvalSummary} log - The log object containing sample summaries and status.
984
+ * @param {import("./api/Types.ts").EvalSummary} log - The log object containing sample summaries and status.
985
985
  * @returns {Array<{name: string, scorer: string}>} An array of scorer objects with name and scorer properties, or an empty array if no scorers are found.
986
986
  */
987
987
  const defaultScorers = (log) => {
@@ -7,7 +7,7 @@
7
7
  /**
8
8
  * @typedef {Object} CurrentLog
9
9
  * @property {string} name
10
- * @property {import("./api/Types.mjs").EvalSummary} contents
10
+ * @property {import("./api/Types.ts").EvalSummary} contents
11
11
  */
12
12
 
13
13
  /**
@@ -0,0 +1,133 @@
1
+ import {
2
+ Version,
3
+ Status,
4
+ EvalSpec,
5
+ EvalPlan,
6
+ EvalResults,
7
+ EvalStats,
8
+ EvalError,
9
+ Input,
10
+ Target,
11
+ Scores1,
12
+ Type11,
13
+ EvalLog,
14
+ EvalSample,
15
+ } from "../types/log";
16
+
17
+ export interface EvalSummary {
18
+ version?: Version;
19
+ status?: Status;
20
+ eval: EvalSpec;
21
+ plan?: EvalPlan;
22
+ results?: EvalResults | null;
23
+ stats?: EvalStats;
24
+ error?: EvalError | null;
25
+ sampleSummaries: SampleSummary[];
26
+ }
27
+
28
+ export interface EvalLogHeader {
29
+ version?: Version;
30
+ status?: Status;
31
+ eval: EvalSpec;
32
+ plan?: EvalPlan;
33
+ results?: EvalResults;
34
+ stats?: EvalStats;
35
+ error?: EvalError;
36
+ }
37
+
38
+ export interface SampleSummary {
39
+ id: number | string;
40
+ epoch: number;
41
+ input: Input;
42
+ target: Target;
43
+ scores: Scores1;
44
+ error?: string;
45
+ limit?: Type11;
46
+ }
47
+
48
+ export interface BasicSampleData {
49
+ id: number | string;
50
+ epoch: number;
51
+ target: Target;
52
+ scores: Scores1;
53
+ }
54
+
55
+ export interface Capabilities {
56
+ downloadFiles: boolean;
57
+ webWorkers: boolean;
58
+ }
59
+
60
+ export interface LogViewAPI {
61
+ client_events: () => Promise<any[]>;
62
+ eval_logs: () => Promise<LogFiles | undefined>;
63
+ eval_log: (
64
+ log_file: string,
65
+ headerOnly?: number,
66
+ capabilities?: Capabilities,
67
+ ) => Promise<LogContents>;
68
+ eval_log_size: (log_file: string) => Promise<number>;
69
+ eval_log_bytes: (
70
+ log_file: string,
71
+ start: number,
72
+ end: number,
73
+ ) => Promise<Uint8Array>;
74
+ eval_log_headers: (log_files: string[]) => Promise<EvalLog[]>;
75
+ download_file: (
76
+ filename: string,
77
+ filecontents: string | Blob | ArrayBuffer | ArrayBufferView,
78
+ ) => Promise<void>;
79
+ open_log_file: (logFile: string, log_dir: string) => Promise<void>;
80
+ }
81
+
82
+ export interface ClientAPI {
83
+ client_events: () => Promise<string[]>;
84
+ get_log_paths: () => Promise<LogFiles>;
85
+ get_log_headers: (log_files: string[]) => Promise<EvalLog[]>;
86
+ get_log_summary: (log_file: string) => Promise<EvalSummary>;
87
+ get_log_sample: (
88
+ log_file: string,
89
+ id: string | number,
90
+ epoch: number,
91
+ ) => Promise<EvalSample | undefined>;
92
+ download_file: (
93
+ file_name: string,
94
+ file_contents: string | Blob | ArrayBuffer | ArrayBufferView,
95
+ ) => Promise<void>;
96
+ open_log_file: (log_file: string, log_dir: string) => Promise<void>;
97
+ }
98
+
99
+ export interface FetchResponse {
100
+ raw: string;
101
+ parsed: Record<string, any>;
102
+ }
103
+
104
+ export interface EvalHeader {
105
+ version?: Version;
106
+ status?: Status;
107
+ eval: EvalSpec;
108
+ plan?: EvalPlan;
109
+ results?: EvalResults | null;
110
+ stats?: EvalStats;
111
+ error?: EvalError | null;
112
+ }
113
+
114
+ export interface LogFiles {
115
+ files: LogFile[];
116
+ log_dir?: string;
117
+ }
118
+
119
+ export interface LogFile {
120
+ name: string;
121
+ task: string;
122
+ task_id: string;
123
+ }
124
+
125
+ export interface LogContents {
126
+ raw: string;
127
+ parsed: EvalLog;
128
+ }
129
+
130
+ export interface LogFilesFetchResponse {
131
+ raw: string;
132
+ parsed: Record<string, EvalHeader>;
133
+ }
@@ -1,6 +1,7 @@
1
- //@ts-check
2
- import { asyncJsonParse } from "../utils/Json.mjs";
3
- import { download_file } from "./api-shared.mjs";
1
+ import { Capabilities } from "../Types.mjs";
2
+ import { asyncJsonParse } from "../utils/json-worker";
3
+ import { download_file } from "./api-shared";
4
+ import { LogContents, LogViewAPI } from "./Types";
4
5
 
5
6
  const loaded_time = Date.now();
6
7
  let last_eval_time = 0;
@@ -18,25 +19,29 @@ async function eval_logs() {
18
19
  return logs.parsed;
19
20
  }
20
21
 
21
- async function eval_log(file, headerOnly) {
22
+ async function eval_log(
23
+ file: string,
24
+ headerOnly?: number,
25
+ _capabilities?: Capabilities,
26
+ ): Promise<LogContents> {
22
27
  return await api(
23
28
  "GET",
24
29
  `/api/logs/${encodeURIComponent(file)}?header-only=${headerOnly}`,
25
30
  );
26
31
  }
27
32
 
28
- async function eval_log_size(file) {
33
+ async function eval_log_size(file: string): Promise<number> {
29
34
  return (await api("GET", `/api/log-size/${encodeURIComponent(file)}`)).parsed;
30
35
  }
31
36
 
32
- async function eval_log_bytes(file, start, end) {
37
+ async function eval_log_bytes(file: string, start: number, end: number) {
33
38
  return await api_bytes(
34
39
  "GET",
35
40
  `/api/log-bytes/${encodeURIComponent(file)}?start=${start}&end=${end}`,
36
41
  );
37
42
  }
38
43
 
39
- async function eval_log_headers(files) {
44
+ async function eval_log_headers(files: string[]) {
40
45
  const params = new URLSearchParams();
41
46
  for (const file of files) {
42
47
  params.append("file", file);
@@ -44,9 +49,13 @@ async function eval_log_headers(files) {
44
49
  return (await api("GET", `/api/log-headers?${params.toString()}`)).parsed;
45
50
  }
46
51
 
47
- async function api(method, path, body) {
52
+ async function api(
53
+ method: "GET" | "POST" | "PUT" | "DELETE",
54
+ path: string,
55
+ body?: string,
56
+ ) {
48
57
  // build headers
49
- const headers = {
58
+ const headers: HeadersInit = {
50
59
  Accept: "application/json",
51
60
  Pragma: "no-cache",
52
61
  Expires: "0",
@@ -73,9 +82,12 @@ async function api(method, path, body) {
73
82
  }
74
83
  }
75
84
 
76
- async function api_bytes(method, path) {
85
+ async function api_bytes(
86
+ method: "GET" | "POST" | "PUT" | "DELETE",
87
+ path: string,
88
+ ) {
77
89
  // build headers
78
- const headers = {
90
+ const headers: HeadersInit = {
79
91
  Accept: "application/octet-stream",
80
92
  Pragma: "no-cache",
81
93
  Expires: "0",
@@ -100,8 +112,7 @@ async function open_log_file() {
100
112
  // No op
101
113
  }
102
114
 
103
- /** @type {import("./Types.mjs").LogViewAPI} */
104
- export default {
115
+ const browserApi: LogViewAPI = {
105
116
  client_events,
106
117
  eval_logs,
107
118
  eval_log,
@@ -111,3 +122,4 @@ export default {
111
122
  download_file,
112
123
  open_log_file,
113
124
  };
125
+ export default browserApi;
@@ -0,0 +1,219 @@
1
+ //@ts-check
2
+ import { asyncJsonParse } from "../utils/json-worker";
3
+ import { download_file, encodePathParts } from "./api-shared";
4
+ import { fetchRange, fetchSize } from "../utils/remoteZipFile.mjs";
5
+ import {
6
+ Capabilities,
7
+ LogContents,
8
+ LogFiles,
9
+ LogFilesFetchResponse,
10
+ LogViewAPI,
11
+ } from "./Types";
12
+ import { EvalLog } from "../types/log";
13
+
14
+ interface LogInfo {
15
+ log_dir?: string;
16
+ log_file?: string;
17
+ }
18
+
19
+ /**
20
+ * This provides an API implementation that will serve a single
21
+ * file using an http parameter, designed to be deployed
22
+ * to a webserver without inspect or the ability to enumerate log
23
+ * files
24
+ */
25
+ export default function simpleHttpApi(
26
+ log_dir?: string,
27
+ log_file?: string,
28
+ ): LogViewAPI {
29
+ const resolved_log_dir = log_dir?.replace(" ", "+");
30
+ const resolved_log_path = log_file ? log_file.replace(" ", "+") : undefined;
31
+ return simpleHttpAPI({
32
+ log_file: resolved_log_path,
33
+ log_dir: resolved_log_dir,
34
+ });
35
+ }
36
+
37
+ /**
38
+ * Fetches a file from the specified URL and parses its content.
39
+ */
40
+ function simpleHttpAPI(logInfo: LogInfo): LogViewAPI {
41
+ const log_file = logInfo.log_file;
42
+ const log_dir = logInfo.log_dir;
43
+
44
+ async function open_log_file() {
45
+ // No op
46
+ }
47
+ return {
48
+ client_events: async () => {
49
+ // There are no client events in the case of serving via
50
+ // http
51
+ return Promise.resolve([]);
52
+ },
53
+ eval_logs: async (): Promise<LogFiles | undefined> => {
54
+ // First check based upon the log dir
55
+ if (log_dir) {
56
+ const headers = await fetchLogHeaders(log_dir);
57
+ if (headers) {
58
+ const logRecord = headers.parsed;
59
+ const logs = Object.keys(logRecord).map((key) => {
60
+ return {
61
+ name: joinURI(log_dir, key),
62
+ task: logRecord[key].eval.task,
63
+ task_id: logRecord[key].eval.task_id,
64
+ };
65
+ });
66
+ return Promise.resolve({
67
+ files: logs,
68
+ log_dir,
69
+ });
70
+ }
71
+ }
72
+
73
+ return undefined;
74
+ },
75
+ eval_log: async (
76
+ log_file: string,
77
+ _headerOnly?: number,
78
+ _capabilities?: Capabilities,
79
+ ) => {
80
+ const response = await fetchLogFile(log_file);
81
+ if (response) {
82
+ return response;
83
+ } else {
84
+ throw new Error(`"Unable to load eval log ${log_file}`);
85
+ }
86
+ },
87
+ eval_log_size: async (log_file: string) => {
88
+ return await fetchSize(log_file);
89
+ },
90
+ eval_log_bytes: async (log_file: string, start: number, end: number) => {
91
+ return await fetchRange(log_file, start, end);
92
+ },
93
+ eval_log_headers: async (files: string[]) => {
94
+ if (files.length === 0) {
95
+ return [];
96
+ }
97
+
98
+ if (log_dir) {
99
+ const headers = await fetchLogHeaders(log_dir);
100
+ if (headers) {
101
+ const keys = Object.keys(headers.parsed);
102
+ const result: EvalLog[] = [];
103
+ files.forEach((file) => {
104
+ const fileKey = keys.find((key) => {
105
+ return file.endsWith(key);
106
+ });
107
+ if (fileKey) {
108
+ result.push(headers.parsed[fileKey]);
109
+ }
110
+ });
111
+ return result;
112
+ }
113
+ }
114
+
115
+ // No log.json could be found, and there isn't a log file,
116
+ throw new Error(
117
+ `Failed to load a manifest files using the directory: ${log_dir}. Please be sure you have deployed a manifest file (logs.json).`,
118
+ );
119
+ },
120
+ download_file,
121
+ open_log_file,
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Fetches a file from the specified URL and parses its content.
127
+ */
128
+ async function fetchFile<T>(
129
+ url: string,
130
+ parse: (text: string) => Promise<T>,
131
+ handleError?: (response: Response) => boolean,
132
+ ): Promise<T | undefined> {
133
+ const safe_url = encodePathParts(url);
134
+ const response = await fetch(`${safe_url}`, { method: "GET" });
135
+ if (response.ok) {
136
+ const text = await response.text();
137
+ return await parse(text);
138
+ } else if (response.status !== 200) {
139
+ if (handleError && handleError(response)) {
140
+ return undefined;
141
+ }
142
+ const message = (await response.text()) || response.statusText;
143
+ const error = new Error(`${response.status}: ${message})`);
144
+ throw error;
145
+ } else {
146
+ throw new Error(`${response.status} - ${response.statusText} `);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Fetches a log file and parses its content, updating the log structure if necessary.
152
+ */
153
+ const fetchLogFile = async (file: string): Promise<LogContents | undefined> => {
154
+ return fetchFile<LogContents>(file, async (text): Promise<LogContents> => {
155
+ const log = (await asyncJsonParse(text)) as EvalLog;
156
+ if (log.version === 1) {
157
+ if (log.results) {
158
+ const untypedLog = log as any;
159
+ log.results.scores = [];
160
+ untypedLog.results.scorer.scorer = untypedLog.results.scorer.name;
161
+ log.results.scores.push(untypedLog.results.scorer);
162
+ delete untypedLog.results.scorer;
163
+ log.results.scores[0].metrics = untypedLog.results.metrics;
164
+ delete untypedLog.results.metrics;
165
+
166
+ // migrate samples
167
+ const scorerName = log.results.scores[0].name;
168
+ log.samples?.forEach((sample) => {
169
+ const untypedSample = sample as any;
170
+ sample.scores = { [scorerName]: untypedSample.score };
171
+ delete untypedSample.score;
172
+ });
173
+ }
174
+ }
175
+ return {
176
+ raw: text,
177
+ parsed: log,
178
+ };
179
+ });
180
+ };
181
+
182
+ /**
183
+ * Fetches a log file and parses its content, updating the log structure if necessary.
184
+ */
185
+ const fetchLogHeaders = async (
186
+ log_dir: string,
187
+ ): Promise<LogFilesFetchResponse | undefined> => {
188
+ const logs = await fetchFile<LogFilesFetchResponse>(
189
+ log_dir + "/logs.json",
190
+ async (text) => {
191
+ const parsed = await asyncJsonParse(text);
192
+ return {
193
+ raw: text,
194
+ parsed,
195
+ };
196
+ },
197
+ (response) => {
198
+ if (response.status === 404) {
199
+ // Couldn't find a header file
200
+ return true;
201
+ } else {
202
+ return false;
203
+ }
204
+ },
205
+ );
206
+ return logs;
207
+ };
208
+
209
+ /**
210
+ * Joins multiple URI segments into a single URI string.
211
+ *
212
+ * This function removes any leading or trailing slashes from each segment
213
+ * and then joins them with a single slash (`/`).
214
+ */
215
+ function joinURI(...segments: string[]): string {
216
+ return segments
217
+ .map((segment) => segment.replace(/(^\/+|\/+$)/g, "")) // Remove leading/trailing slashes from each segment
218
+ .join("/");
219
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Downloads the provided content as a file using the browser's DOM API
3
+ */
4
+ export async function download_file(
5
+ filename: string,
6
+ filecontents: string | Blob | ArrayBuffer | ArrayBufferView,
7
+ ): Promise<void> {
8
+ const blob = new Blob([filecontents], { type: "text/plain" });
9
+ const link = document.createElement("a");
10
+ link.href = URL.createObjectURL(blob);
11
+ link.download = filename;
12
+ document.body.appendChild(link);
13
+ link.click();
14
+ document.body.removeChild(link);
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,7 +1,4 @@
1
- //@ts-check
2
-
3
- import { asyncJsonParse } from "../utils/Json.mjs";
4
- // @ts-ignore
1
+ import { asyncJsonParse } from "../utils/json-worker";
5
2
  import JSON5 from "json5";
6
3
 
7
4
  import {
@@ -11,8 +8,9 @@ import {
11
8
  kMethodEvalLogSize,
12
9
  kMethodEvalLogBytes,
13
10
  kMethodEvalLogHeaders,
14
- } from "./jsonrpc.mjs";
15
- import { getVscodeApi } from "../utils/vscode.mjs";
11
+ } from "./jsonrpc";
12
+ import { getVscodeApi } from "../utils/vscode";
13
+ import { Capabilities, LogContents, LogViewAPI } from "./Types";
16
14
 
17
15
  const vscodeClient = webViewJsonRpcClient(getVscodeApi());
18
16
 
@@ -38,8 +36,12 @@ async function eval_logs() {
38
36
  }
39
37
  }
40
38
 
41
- async function eval_log(file, headerOnly, capabilities) {
42
- const response = await vscodeClient(kMethodEvalLog, [file, headerOnly]);
39
+ async function eval_log(
40
+ log_file: string,
41
+ headerOnly?: number,
42
+ capabilities?: Capabilities,
43
+ ): Promise<LogContents> {
44
+ const response = await vscodeClient(kMethodEvalLog, [log_file, headerOnly]);
43
45
  if (response) {
44
46
  let json;
45
47
  if (capabilities?.webWorkers) {
@@ -52,19 +54,19 @@ async function eval_log(file, headerOnly, capabilities) {
52
54
  raw: response,
53
55
  };
54
56
  } else {
55
- return undefined;
57
+ throw new Error(`Unable to load eval log ${log_file}.`);
56
58
  }
57
59
  }
58
60
 
59
- async function eval_log_size(file) {
60
- return await vscodeClient(kMethodEvalLogSize, [file]);
61
+ async function eval_log_size(log_file: string) {
62
+ return await vscodeClient(kMethodEvalLogSize, [log_file]);
61
63
  }
62
64
 
63
- async function eval_log_bytes(file, start, end) {
64
- return await vscodeClient(kMethodEvalLogBytes, [file, start, end]);
65
+ async function eval_log_bytes(log_file: string, start: number, end: number) {
66
+ return await vscodeClient(kMethodEvalLogBytes, [log_file, start, end]);
65
67
  }
66
68
 
67
- async function eval_log_headers(files) {
69
+ async function eval_log_headers(files: string[]) {
68
70
  const response = await vscodeClient(kMethodEvalLogHeaders, [files]);
69
71
  if (response) {
70
72
  return JSON5.parse(response);
@@ -77,17 +79,16 @@ async function download_file() {
77
79
  throw Error("Downloading files is not supported in VS Code");
78
80
  }
79
81
 
80
- async function open_log_file(url, log_dir) {
82
+ async function open_log_file(log_file: string, log_dir: string) {
81
83
  const msg = {
82
84
  type: "displayLogFile",
83
- url: url,
85
+ url: log_file,
84
86
  log_dir: log_dir,
85
87
  };
86
- getVscodeApi().postMessage(msg);
88
+ getVscodeApi()?.postMessage(msg);
87
89
  }
88
90
 
89
- /** @type {import("./Types.mjs").LogViewAPI} */
90
- export default {
91
+ const api: LogViewAPI = {
91
92
  client_events,
92
93
  eval_logs,
93
94
  eval_log,
@@ -97,3 +98,5 @@ export default {
97
98
  download_file,
98
99
  open_log_file,
99
100
  };
101
+
102
+ export default api;