@toloka-platform/sdk 0.2.0-preview.2 → 0.2.0-preview.4
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.
- package/README.md +10 -4
- package/dist/cli/toloka.js +162 -154
- package/dist/cli/toloka.js.jsc +0 -0
- package/dist/cli/toloka.js.jsc.sha256 +1 -1
- package/dist/cli/toloka.js.sha256 +1 -1
- package/docs/authoring.md +18 -1
- package/docs/cli-v2.md +6 -4
- package/docs/cli.md +58 -11
- package/docs/getting-started.md +3 -3
- package/docs/limitations.md +1 -1
- package/docs/testing.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/examples/face-image-collection/README.md +2 -2
- package/openapi/gen/api.ts +78 -0
- package/openapi/gen/models/baseModel.ts +1 -0
- package/openapi/gen/models/checkReport.ts +2 -1
- package/openapi/gen/models/checkReportAnyOfTwoseven.ts +5 -0
- package/openapi/gen/models/checkReportAnyOfTwosevenResultsItem.ts +6 -0
- package/openapi/gen/models/checkReportAnyOfTwosevenResultsItemVerdict.ts +2 -0
- package/openapi/gen/models/currentUser.ts +3 -0
- package/openapi/gen/models/currentUserTeamsItem.ts +6 -0
- package/openapi/gen/models/deliveryOrder.ts +9 -0
- package/openapi/gen/models/deliveryOrderClientName.ts +2 -0
- package/openapi/gen/models/getTrainingRunMetricHistoryParams.ts +4 -0
- package/openapi/gen/models/gistingTrainingConfiguration.ts +6 -2
- package/openapi/gen/models/gistingTrainingConfigurationBatchSize.ts +2 -0
- package/openapi/gen/models/gistingTrainingConfigurationGistTokens.ts +2 -0
- package/openapi/gen/models/gistingTrainingConfigurationInput.ts +6 -2
- package/openapi/gen/models/gistingTrainingConfigurationInputBatchSize.ts +2 -0
- package/openapi/gen/models/gistingTrainingConfigurationInputGistTokens.ts +2 -0
- package/openapi/gen/models/index.ts +28 -4
- package/openapi/gen/models/listDeliveryOrders200.ts +5 -0
- package/openapi/gen/models/sftTrainingConfiguration.ts +6 -3
- package/openapi/gen/models/sftTrainingConfigurationBatchSize.ts +2 -0
- package/openapi/gen/models/sftTrainingConfigurationInput.ts +6 -3
- package/openapi/gen/models/sftTrainingConfigurationInputBatchSize.ts +2 -0
- package/openapi/gen/models/sftTrainingConfigurationInputLoraRank.ts +2 -0
- package/openapi/gen/models/sftTrainingConfigurationLoraRank.ts +2 -0
- package/openapi/gen/models/trainingRunMetricHistory.ts +9 -0
- package/openapi/gen/models/trainingRunMetricHistoryMetric.ts +2 -0
- package/openapi/gen/models/trainingRunMetricHistoryPointsItem.ts +9 -0
- package/openapi/gen/models/trainingRunMetricHistoryPointsItemNonFinite.ts +2 -0
- package/openapi/gen/models/trainingRunMetricHistoryPointsItemValue.ts +2 -0
- package/openapi/gen/models/trainingRunMetrics.ts +9 -0
- package/openapi/gen/models/trainingRunMetricsMetricsItem.ts +8 -0
- package/openapi/gen/models/trainingRunMetricsMetricsItemLatest.ts +9 -0
- package/openapi/gen/models/trainingRunMetricsMetricsItemLatestNonFinite.ts +2 -0
- package/openapi/gen/models/trainingRunMetricsMetricsItemLatestValue.ts +2 -0
- package/openapi/gen/models/trainingRunMetricsMetricsItemMetric.ts +2 -0
- package/openapi/gen/models/trainingRunMetricsUnavailableReason.ts +2 -0
- package/package.json +2 -2
- package/skills/catalog.json +55 -7
- package/skills/toloka-build/SKILL.md +35 -21
- package/skills/toloka-build/references/node-semantics.md +13 -11
- package/skills/toloka-build/references/workflow-and-cli.md +31 -20
- package/skills/toloka-fine-tuning/SKILL.md +155 -0
- package/skills/toloka-get-started/SKILL.md +70 -0
- package/skills/toloka-gisting/SKILL.md +166 -0
- package/skills/toloka-report-issue/references/report-template.md +1 -1
- package/src/authoring/index.ts +14 -0
- package/src/checks/task-ui-typecheck.ts +12 -10
- package/src/cli/analytics.ts +88 -15
- package/src/cli/auth.ts +93 -21
- package/src/cli/auto-update.ts +36 -39
- package/src/cli/check.ts +88 -39
- package/src/cli/cli-entry.ts +3 -2
- package/src/cli/cli-error.ts +43 -0
- package/src/cli/commands/auth.ts +89 -0
- package/src/cli/commands/config.ts +28 -0
- package/src/cli/commands/dataset.ts +85 -0
- package/src/cli/commands/delivery-order.ts +18 -0
- package/src/cli/commands/models.ts +121 -0
- package/src/cli/commands/pipeline.ts +131 -0
- package/src/cli/commands/project.ts +168 -0
- package/src/cli/commands/report.ts +43 -0
- package/src/cli/commands/secret.ts +40 -0
- package/src/cli/commands/skill.ts +36 -0
- package/src/cli/commands/source.ts +204 -0
- package/src/cli/commands/status.ts +17 -0
- package/src/cli/commands/training.ts +261 -0
- package/src/cli/commands/version.ts +14 -0
- package/src/cli/config.ts +7 -8
- package/src/cli/context.ts +251 -0
- package/src/cli/credentials.ts +12 -4
- package/src/cli/data.ts +96 -34
- package/src/cli/delivery-orders.ts +47 -0
- package/src/cli/environment.ts +26 -0
- package/src/cli/file-system.ts +6 -2
- package/src/cli/format-error.ts +45 -2
- package/src/cli/init.ts +14 -11
- package/src/cli/models.ts +43 -12
- package/src/cli/program.ts +120 -1347
- package/src/cli/project-context.ts +126 -43
- package/src/cli/project.ts +80 -28
- package/src/cli/report.ts +24 -9
- package/src/cli/runs.ts +33 -8
- package/src/cli/sdk-version.ts +38 -0
- package/src/cli/settings.ts +13 -5
- package/src/cli/skill.ts +67 -24
- package/src/cli/source.ts +3 -2
- package/src/cli/terminal-io.ts +110 -0
- package/src/cli/trainings.ts +242 -57
- package/src/cli/version.ts +3 -3
- package/src/client.ts +21 -9
- package/src/importer.ts +32 -2
- package/src/transport.ts +13 -1
- package/openapi/gen/models/gistingTrainingConfigurationInputRegime.ts +0 -2
- package/openapi/gen/models/gistingTrainingConfigurationRegime.ts +0 -2
- package/openapi/gen/models/sftTrainingConfigurationInputRegime.ts +0 -2
- package/openapi/gen/models/sftTrainingConfigurationRegime.ts +0 -2
package/src/cli/format-error.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PotatoApiError } from "../errors";
|
|
2
|
+
import { formatHint, TolokaCliError } from "./cli-error";
|
|
2
3
|
|
|
3
4
|
export const apiErrorDetails = (error: PotatoApiError): string | undefined => {
|
|
4
5
|
const details =
|
|
@@ -10,10 +11,52 @@ export const apiErrorDetails = (error: PotatoApiError): string | undefined => {
|
|
|
10
11
|
return details === undefined || details.length === 0 ? undefined : details;
|
|
11
12
|
};
|
|
12
13
|
|
|
14
|
+
const apiErrorHint = (error: PotatoApiError): string | undefined => {
|
|
15
|
+
if (error.code === "wait_timeout") {
|
|
16
|
+
return "Hint: The job is still running. Check it later with its status command; commands that accept --timeout can wait longer.";
|
|
17
|
+
}
|
|
18
|
+
if (error.status === 401 || error.status === 403) {
|
|
19
|
+
return "Hint: Run toloka whoami to see which key and platform are in use; toloka login stores a new key.";
|
|
20
|
+
}
|
|
21
|
+
if (error.status === 404) {
|
|
22
|
+
return "Hint: Check the id against toloka project current and the matching list command; ids belong to one project on one platform.";
|
|
23
|
+
}
|
|
24
|
+
if (error.status === 429) {
|
|
25
|
+
return "Hint: The platform is rate limiting this key; wait a moment and retry.";
|
|
26
|
+
}
|
|
27
|
+
if (error.status !== undefined && error.status >= 500) {
|
|
28
|
+
return "Hint: The platform failed to handle the request. Retry; if it keeps failing, write a report as the toloka-report-issue skill describes and send it with toloka report <report.md>.";
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const joinLines = (lines: ReadonlyArray<string | undefined>): string =>
|
|
34
|
+
lines.filter((line) => line !== undefined && line.length > 0).join("\n");
|
|
35
|
+
|
|
36
|
+
export const findPotatoApiError = (error: unknown): PotatoApiError | undefined => {
|
|
37
|
+
if (error instanceof PotatoApiError) return error;
|
|
38
|
+
if (error instanceof AggregateError) {
|
|
39
|
+
for (const nested of error.errors) {
|
|
40
|
+
const apiError = findPotatoApiError(nested);
|
|
41
|
+
if (apiError !== undefined) return apiError;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return error instanceof Error ? findPotatoApiError(error.cause) : undefined;
|
|
45
|
+
};
|
|
46
|
+
|
|
13
47
|
export const formatCliError = (error: unknown): string => {
|
|
48
|
+
if (error instanceof TolokaCliError) {
|
|
49
|
+
const cause = findPotatoApiError(error.cause);
|
|
50
|
+
const causeLines =
|
|
51
|
+
cause === undefined
|
|
52
|
+
? []
|
|
53
|
+
: cause.message === error.message
|
|
54
|
+
? [apiErrorDetails(cause)]
|
|
55
|
+
: [cause.message, apiErrorDetails(cause)];
|
|
56
|
+
return joinLines([error.message, ...causeLines, formatHint(error.hint)]);
|
|
57
|
+
}
|
|
14
58
|
if (error instanceof PotatoApiError) {
|
|
15
|
-
|
|
16
|
-
return details === undefined ? error.message : `${error.message}\n${details}`;
|
|
59
|
+
return joinLines([error.message, apiErrorDetails(error), apiErrorHint(error)]);
|
|
17
60
|
}
|
|
18
61
|
return error instanceof Error ? error.message : String(error);
|
|
19
62
|
};
|
package/src/cli/init.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { assert } from "es-toolkit";
|
|
2
1
|
import { lstat, mkdir, readdir, readFile, realpath, rm, rmdir, writeFile } from "node:fs/promises";
|
|
3
2
|
import { dirname, join } from "node:path";
|
|
4
|
-
import packageMetadata from "../../package.json" with { type: "json" };
|
|
5
3
|
import { readTolokaState, writeTolokaState } from "../state";
|
|
6
4
|
import { assertSafeDestination, entryStats, isMissingFileError } from "./file-system";
|
|
5
|
+
import { SDK_PACKAGE_NAME, SDK_VERSION } from "./sdk-version";
|
|
7
6
|
import { SKILL_ENTRY_POINT, SKILL_INSTALL_ROOT } from "./skill";
|
|
7
|
+
import { assertCli } from "./cli-error";
|
|
8
8
|
|
|
9
|
-
export const SDK_VERSION = packageMetadata.version;
|
|
10
|
-
|
|
11
|
-
const SDK_PACKAGE_NAME = "@toloka-platform/sdk";
|
|
12
9
|
const BOOTSTRAP_NAME = "toloka-bootstrap";
|
|
13
10
|
const CLAUDE_POINTER_PATH = `.claude/skills/${BOOTSTRAP_NAME}/SKILL.md`;
|
|
14
11
|
const CURSOR_POINTER_PATH = `.cursor/rules/${BOOTSTRAP_NAME}.mdc`;
|
|
@@ -109,14 +106,16 @@ const locateBlock = (source: string, markers: BlockMarkers, path: string) => {
|
|
|
109
106
|
const beginIndex = source.indexOf(markers.begin);
|
|
110
107
|
const endIndex = source.indexOf(markers.end);
|
|
111
108
|
if (beginIndex === -1 && endIndex === -1) return undefined;
|
|
112
|
-
|
|
109
|
+
assertCli(
|
|
113
110
|
beginIndex !== -1 && endIndex !== -1 && beginIndex < endIndex,
|
|
114
111
|
`Malformed Toloka block in ${path}: expected one ${markers.begin} followed by one ${markers.end}. Repair the file by hand, then re-run \`toloka init\`.`,
|
|
112
|
+
{ hint: { disabled: true } },
|
|
115
113
|
);
|
|
116
|
-
|
|
114
|
+
assertCli(
|
|
117
115
|
!source.includes(markers.begin, beginIndex + markers.begin.length) &&
|
|
118
116
|
!source.includes(markers.end, endIndex + markers.end.length),
|
|
119
117
|
`Multiple Toloka blocks in ${path}: keep exactly one ${markers.begin} ... ${markers.end} block, then re-run \`toloka init\`.`,
|
|
118
|
+
{ hint: { disabled: true } },
|
|
120
119
|
);
|
|
121
120
|
return { beginIndex, endIndex: endIndex + markers.end.length };
|
|
122
121
|
};
|
|
@@ -124,9 +123,10 @@ const locateBlock = (source: string, markers: BlockMarkers, path: string) => {
|
|
|
124
123
|
const mergeBootstrap = (source: string, path: string) => {
|
|
125
124
|
const current = locateBlock(source, CURRENT_BLOCK, path);
|
|
126
125
|
const legacy = locateBlock(source, LEGACY_BLOCK, path);
|
|
127
|
-
|
|
126
|
+
assertCli(
|
|
128
127
|
current === undefined || legacy === undefined,
|
|
129
128
|
`Ambiguous Toloka blocks in ${path}: both ${CURRENT_BLOCK.begin} and ${LEGACY_BLOCK.begin} are present. Keep one, then re-run \`toloka init\`.`,
|
|
129
|
+
{ hint: { disabled: true } },
|
|
130
130
|
);
|
|
131
131
|
const lineEnding = source.includes("\r\n") ? "\r\n" : "\n";
|
|
132
132
|
const block = BOOTSTRAP_BLOCK.replaceAll("\n", lineEnding);
|
|
@@ -149,9 +149,10 @@ const hasSdkDependency = async (targetDirectory: string) => {
|
|
|
149
149
|
const source = await readOptionalFile(join(targetDirectory, "package.json"));
|
|
150
150
|
if (source === undefined) return false;
|
|
151
151
|
const manifest: unknown = JSON.parse(source);
|
|
152
|
-
|
|
152
|
+
assertCli(
|
|
153
153
|
typeof manifest === "object" && manifest !== null,
|
|
154
154
|
"package.json must contain a JSON object.",
|
|
155
|
+
{ hint: { description: "Fix package.json in the source root, then re-run toloka init." } },
|
|
155
156
|
);
|
|
156
157
|
return ["dependencies", "devDependencies"].some((key) => {
|
|
157
158
|
const dependencies = Reflect.get(manifest, key);
|
|
@@ -173,9 +174,10 @@ const writeIfChanged = async (path: string, source: string) => {
|
|
|
173
174
|
const assertRefreshablePointer = async (targetRoot: string, relativePath: string) => {
|
|
174
175
|
const path = join(targetRoot, relativePath);
|
|
175
176
|
const existing = await readOptionalFile(path);
|
|
176
|
-
|
|
177
|
+
assertCli(
|
|
177
178
|
existing === undefined || isGenerated(existing),
|
|
178
179
|
`Refusing to overwrite ${path}: it carries no \`<!-- toloka:sdk-version ... -->\` marker, so it is not a pointer generated by ${SDK_PACKAGE_NAME}. Move it aside, then re-run \`toloka init\`.`,
|
|
180
|
+
{ hint: { disabled: true } },
|
|
179
181
|
);
|
|
180
182
|
};
|
|
181
183
|
|
|
@@ -218,9 +220,10 @@ const bindCheckout = async (
|
|
|
218
220
|
): Promise<string | undefined> => {
|
|
219
221
|
if (projectId === undefined) return undefined;
|
|
220
222
|
const existing = await readTolokaState(targetRoot);
|
|
221
|
-
|
|
223
|
+
assertCli(
|
|
222
224
|
existing === undefined || existing.projectId === projectId,
|
|
223
225
|
`This directory is already bound to ${existing?.projectId}.`,
|
|
226
|
+
{ hint: { description: "Run toloka init without --project-id to keep the current binding, or remove toloka.json to bind another project." } },
|
|
224
227
|
);
|
|
225
228
|
if (existing !== undefined) return undefined;
|
|
226
229
|
await writeTolokaState(targetRoot, { projectId, pipelines: {}, datasets: {} });
|
package/src/cli/models.ts
CHANGED
|
@@ -5,9 +5,9 @@ import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
|
5
5
|
import { Readable, Transform } from "node:stream";
|
|
6
6
|
import { pipeline } from "node:stream/promises";
|
|
7
7
|
|
|
8
|
-
import { assert } from "es-toolkit";
|
|
9
8
|
|
|
10
9
|
import type { BaseModel, BaseModelId, Model, ModelVersion, PotatoClient } from "../client";
|
|
10
|
+
import { assertCli, REPORT_HINT, RETRY_OR_REPORT_HINT } from "./cli-error";
|
|
11
11
|
|
|
12
12
|
type ModelApi = Pick<PotatoClient, "baseModels" | "models">;
|
|
13
13
|
type ModelIo = { readonly stdout: (text: string) => void };
|
|
@@ -46,6 +46,7 @@ const formatBaseModel = (model: BaseModel): string =>
|
|
|
46
46
|
["Provider", model.provider],
|
|
47
47
|
["Upstream model", model.upstreamModel],
|
|
48
48
|
["Upstream revision", model.upstreamRevision],
|
|
49
|
+
["Upstream snapshot", model.upstreamUrl],
|
|
49
50
|
]);
|
|
50
51
|
|
|
51
52
|
const formatModel = (model: Model): string =>
|
|
@@ -62,7 +63,11 @@ const formatModel = (model: Model): string =>
|
|
|
62
63
|
|
|
63
64
|
export const parseBaseModelId = (value: string): BaseModelId => {
|
|
64
65
|
const modelId = BASE_MODEL_IDS.find((candidate) => candidate === value);
|
|
65
|
-
|
|
66
|
+
assertCli(
|
|
67
|
+
modelId,
|
|
68
|
+
`Unknown base model ${value}.`,
|
|
69
|
+
{ hint: { description: "Run toloka base-model list to see the catalog." } },
|
|
70
|
+
);
|
|
66
71
|
return modelId;
|
|
67
72
|
};
|
|
68
73
|
|
|
@@ -76,7 +81,11 @@ const resolveModel = async (
|
|
|
76
81
|
for await (const model of client.models.listAll({ projectId, limit: 100 })) {
|
|
77
82
|
if (model.name === nameOrId) matches.push(model);
|
|
78
83
|
}
|
|
79
|
-
|
|
84
|
+
assertCli(
|
|
85
|
+
matches.length === 1,
|
|
86
|
+
`Model ${nameOrId} was not found or is ambiguous.`,
|
|
87
|
+
{ hint: { description: "Run toloka model list and pass the model id (mdl.…)." } },
|
|
88
|
+
);
|
|
80
89
|
return matches[0]!;
|
|
81
90
|
};
|
|
82
91
|
|
|
@@ -87,8 +96,16 @@ const resolveModelVersion = async (
|
|
|
87
96
|
): Promise<ModelVersion> => {
|
|
88
97
|
if (nameOrId.startsWith("mdv.")) return client.models.getVersion({ modelVersionId: nameOrId });
|
|
89
98
|
const separator = nameOrId.lastIndexOf("@");
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
assertCli(
|
|
100
|
+
separator > 0,
|
|
101
|
+
`Model Version ${nameOrId} was not found. Use mdv.<id> or Model@name.`,
|
|
102
|
+
{ hint: { disabled: true } },
|
|
103
|
+
);
|
|
104
|
+
assertCli(
|
|
105
|
+
projectId,
|
|
106
|
+
"Select a project to resolve Model Version names.",
|
|
107
|
+
{ hint: { description: "Select a project with toloka project add or toloka project switch, or pass the version id (mdv.…) directly." } },
|
|
108
|
+
);
|
|
92
109
|
const model = await resolveModel(client, projectId, nameOrId.slice(0, separator));
|
|
93
110
|
const versionName = nameOrId.slice(separator + 1);
|
|
94
111
|
const matches: ModelVersion[] = [];
|
|
@@ -99,7 +116,11 @@ const resolveModelVersion = async (
|
|
|
99
116
|
})) {
|
|
100
117
|
if (version.name === versionName) matches.push(version);
|
|
101
118
|
}
|
|
102
|
-
|
|
119
|
+
assertCli(
|
|
120
|
+
matches.length === 1,
|
|
121
|
+
`Model Version ${nameOrId} was not found or is ambiguous.`,
|
|
122
|
+
{ hint: { description: "Run toloka model versions <model> and pass the version id (mdv.…)." } },
|
|
123
|
+
);
|
|
103
124
|
return matches[0]!;
|
|
104
125
|
};
|
|
105
126
|
|
|
@@ -116,12 +137,14 @@ export const listBaseModels = async (
|
|
|
116
137
|
models.length === 0
|
|
117
138
|
? "No base models found."
|
|
118
139
|
: formatTable([
|
|
119
|
-
["ID", "NAME", "PROVIDER", "UPSTREAM MODEL"],
|
|
140
|
+
["ID", "NAME", "PROVIDER", "UPSTREAM MODEL", "REVISION", "UPSTREAM SNAPSHOT"],
|
|
120
141
|
...models.map((model) => [
|
|
121
142
|
model.id,
|
|
122
143
|
model.displayName,
|
|
123
144
|
model.provider,
|
|
124
145
|
model.upstreamModel,
|
|
146
|
+
model.upstreamRevision.slice(0, 12),
|
|
147
|
+
model.upstreamUrl,
|
|
125
148
|
]),
|
|
126
149
|
]),
|
|
127
150
|
);
|
|
@@ -238,25 +261,31 @@ export const downloadModelVersion = async (
|
|
|
238
261
|
for (const file of descriptor.files) {
|
|
239
262
|
const target = resolve(root, file.path);
|
|
240
263
|
const relativeTarget = relative(root, target);
|
|
241
|
-
|
|
264
|
+
assertCli(
|
|
242
265
|
relativeTarget !== "" &&
|
|
243
266
|
relativeTarget !== ".." &&
|
|
244
267
|
!relativeTarget.startsWith(`..${sep}`) &&
|
|
245
268
|
!isAbsolute(relativeTarget),
|
|
246
269
|
`Invalid Model Version file path: ${file.path}`,
|
|
270
|
+
{ hint: REPORT_HINT },
|
|
247
271
|
);
|
|
248
272
|
let parent = root;
|
|
249
273
|
for (const segment of file.path.split("/").slice(0, -1)) {
|
|
250
274
|
parent = resolve(parent, segment);
|
|
251
275
|
await mkdir(parent, { recursive: true });
|
|
252
276
|
const realParent = await realpath(parent);
|
|
253
|
-
|
|
277
|
+
assertCli(
|
|
254
278
|
realParent === realRoot || realParent.startsWith(`${realRoot}${sep}`),
|
|
255
279
|
`Invalid Model Version file path: ${file.path}`,
|
|
280
|
+
{ hint: REPORT_HINT },
|
|
256
281
|
);
|
|
257
282
|
}
|
|
258
283
|
const response = await fetch(file.url, { redirect: "error" });
|
|
259
|
-
|
|
284
|
+
assertCli(
|
|
285
|
+
response.ok && response.body,
|
|
286
|
+
`Failed to download Model Version file ${file.path}.`,
|
|
287
|
+
{ hint: RETRY_OR_REPORT_HINT },
|
|
288
|
+
);
|
|
260
289
|
const hash = createHash("sha256");
|
|
261
290
|
let byteSize = 0;
|
|
262
291
|
const verify = new Transform({
|
|
@@ -274,13 +303,15 @@ export const downloadModelVersion = async (
|
|
|
274
303
|
0o600,
|
|
275
304
|
);
|
|
276
305
|
await pipeline(Readable.from(response.body), verify, handle.createWriteStream());
|
|
277
|
-
|
|
306
|
+
assertCli(
|
|
278
307
|
byteSize === file.byteSize,
|
|
279
308
|
`Downloaded Model Version file ${file.path} has an unexpected size.`,
|
|
309
|
+
{ hint: { description: "Delete the partial download and retry; if it keeps failing, run toloka report." } },
|
|
280
310
|
);
|
|
281
|
-
|
|
311
|
+
assertCli(
|
|
282
312
|
hash.digest("hex") === file.sha256,
|
|
283
313
|
`Downloaded Model Version file ${file.path} has an invalid hash.`,
|
|
314
|
+
{ hint: { description: "Delete the partial download and retry; if it keeps failing, run toloka report." } },
|
|
284
315
|
);
|
|
285
316
|
await rename(temporary, target);
|
|
286
317
|
} catch (error) {
|