@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
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
import {
|
|
4
|
+
CHECK_RESULT_FILE_ENV,
|
|
5
|
+
checkProject,
|
|
6
|
+
watchProjectChecks,
|
|
7
|
+
runFreshCheck,
|
|
8
|
+
type CheckProjectOptions,
|
|
9
|
+
type CheckProjectResult,
|
|
10
|
+
} from "../check";
|
|
11
|
+
import { resolveContext, resolveInitRoot, resolveSourceRoot, type CommandHost } from "../context";
|
|
12
|
+
import { initProject } from "../init";
|
|
13
|
+
import { applyProject, importProject, planProject, validateProject } from "../project";
|
|
14
|
+
import { assertProjectSelected, resolveProjectContext } from "../project-context";
|
|
15
|
+
import { readTolokaState } from "../../state";
|
|
16
|
+
import { assertCli, REPORT_HINT } from "../cli-error";
|
|
17
|
+
|
|
18
|
+
const runChecksInterruptibly = async (
|
|
19
|
+
options: CheckProjectOptions,
|
|
20
|
+
runner: (options: CheckProjectOptions) => Promise<CheckProjectResult>,
|
|
21
|
+
): Promise<{ result: CheckProjectResult; interruptedExitCode?: number }> => {
|
|
22
|
+
const abortController = new AbortController();
|
|
23
|
+
let interruptedExitCode: number | undefined;
|
|
24
|
+
const interrupt = (code: number) => {
|
|
25
|
+
interruptedExitCode = code;
|
|
26
|
+
abortController.abort();
|
|
27
|
+
};
|
|
28
|
+
const interruptSigint = () => interrupt(130);
|
|
29
|
+
const interruptSigterm = () => interrupt(143);
|
|
30
|
+
process.on("SIGINT", interruptSigint);
|
|
31
|
+
process.on("SIGTERM", interruptSigterm);
|
|
32
|
+
try {
|
|
33
|
+
const result = await runner({ ...options, signal: abortController.signal });
|
|
34
|
+
return { result, ...(interruptedExitCode === undefined ? {} : { interruptedExitCode }) };
|
|
35
|
+
} finally {
|
|
36
|
+
process.off("SIGINT", interruptSigint);
|
|
37
|
+
process.off("SIGTERM", interruptSigterm);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const registerSourceCommands = (program: Command, host: CommandHost): void => {
|
|
42
|
+
const { context, exitCode, globals } = host;
|
|
43
|
+
const runChecks = context.runChecks ?? checkProject;
|
|
44
|
+
|
|
45
|
+
program
|
|
46
|
+
.command("init")
|
|
47
|
+
.description("initialize this project directory and install the agent bootstrap")
|
|
48
|
+
.action(async () => {
|
|
49
|
+
const flags = globals();
|
|
50
|
+
const sourceRoot = resolveInitRoot(flags);
|
|
51
|
+
const state = await readTolokaState(sourceRoot);
|
|
52
|
+
const selected = resolveProjectContext({
|
|
53
|
+
flags,
|
|
54
|
+
environment: context.environment,
|
|
55
|
+
...(state === undefined ? {} : { state }),
|
|
56
|
+
});
|
|
57
|
+
const result = await initProject(
|
|
58
|
+
sourceRoot,
|
|
59
|
+
selected === undefined ? {} : { projectId: selected.id },
|
|
60
|
+
);
|
|
61
|
+
context.io.stdout(result.output);
|
|
62
|
+
if (result.warning !== undefined) context.io.stderr(result.warning);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
program
|
|
66
|
+
.command("import")
|
|
67
|
+
.argument(
|
|
68
|
+
"[projectId]",
|
|
69
|
+
"existing project to write into source; defaults to the selected project",
|
|
70
|
+
)
|
|
71
|
+
.option("-f, --force", "replace an existing pipelines/ directory", false)
|
|
72
|
+
.description("generate TypeScript source from a live project")
|
|
73
|
+
.action(async (projectId?: string, options?: { force?: boolean }) => {
|
|
74
|
+
const {
|
|
75
|
+
sourceRoot,
|
|
76
|
+
client,
|
|
77
|
+
projectId: selectedProjectId,
|
|
78
|
+
} = await resolveContext(globals(), context);
|
|
79
|
+
const id = projectId ?? selectedProjectId;
|
|
80
|
+
assertProjectSelected(id);
|
|
81
|
+
const result = await importProject(client, {
|
|
82
|
+
sourceRoot,
|
|
83
|
+
projectId: id,
|
|
84
|
+
...(options?.force === true ? { force: true } : {}),
|
|
85
|
+
});
|
|
86
|
+
context.io.stdout(result.output);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
program
|
|
90
|
+
.command("plan")
|
|
91
|
+
.description("show what apply would change")
|
|
92
|
+
.action(async () => {
|
|
93
|
+
const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
|
|
94
|
+
const result = await planProject(client, {
|
|
95
|
+
sourceRoot,
|
|
96
|
+
...(projectId === undefined ? {} : { projectId }),
|
|
97
|
+
});
|
|
98
|
+
context.io.stdout(result.output);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
program
|
|
102
|
+
.command("validate")
|
|
103
|
+
.description("run platform spec validation and report matching checks")
|
|
104
|
+
.action(async () => {
|
|
105
|
+
const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
|
|
106
|
+
const result = await validateProject(client, {
|
|
107
|
+
sourceRoot,
|
|
108
|
+
...(projectId === undefined ? {} : { projectId }),
|
|
109
|
+
});
|
|
110
|
+
context.io.stdout(result.output);
|
|
111
|
+
if (!result.valid) exitCode.value = 1;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
program
|
|
115
|
+
.command("check")
|
|
116
|
+
.argument("[pipeline]", "pipeline source directory or name")
|
|
117
|
+
.option("--node <node>", "check one code or human-work node by name or id")
|
|
118
|
+
.option("--json", "print the structured check report", false)
|
|
119
|
+
.option("--watch", "rerun checks when project files change", false)
|
|
120
|
+
.description("run the complete local source, type, and test gate")
|
|
121
|
+
.action(
|
|
122
|
+
async (
|
|
123
|
+
pipeline: string | undefined,
|
|
124
|
+
options: { node?: string; json: boolean; watch: boolean },
|
|
125
|
+
) => {
|
|
126
|
+
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
127
|
+
const checkOptions = {
|
|
128
|
+
environment: context.environment,
|
|
129
|
+
sourceRoot,
|
|
130
|
+
...(pipeline === undefined ? {} : { pipeline }),
|
|
131
|
+
...(options.node === undefined ? {} : { node: options.node }),
|
|
132
|
+
...(options.json ? { json: true } : {}),
|
|
133
|
+
};
|
|
134
|
+
const writeCheckOutput = async (output: string): Promise<void> => {
|
|
135
|
+
const resultPath = context.environment[CHECK_RESULT_FILE_ENV];
|
|
136
|
+
if (resultPath === undefined) context.io.stdout(output);
|
|
137
|
+
else await writeFile(resultPath, output, "utf8");
|
|
138
|
+
};
|
|
139
|
+
const checkRunner =
|
|
140
|
+
context.runChecks === undefined &&
|
|
141
|
+
context.environment[CHECK_RESULT_FILE_ENV] === undefined
|
|
142
|
+
? runFreshCheck
|
|
143
|
+
: runChecks;
|
|
144
|
+
if (options.watch) {
|
|
145
|
+
const abortController = new AbortController();
|
|
146
|
+
const stop = () => abortController.abort();
|
|
147
|
+
process.on("SIGINT", stop);
|
|
148
|
+
process.on("SIGTERM", stop);
|
|
149
|
+
try {
|
|
150
|
+
await watchProjectChecks(
|
|
151
|
+
checkOptions,
|
|
152
|
+
async (result) => {
|
|
153
|
+
await writeCheckOutput(result.output);
|
|
154
|
+
exitCode.value = result.exitCode;
|
|
155
|
+
},
|
|
156
|
+
undefined,
|
|
157
|
+
abortController.signal,
|
|
158
|
+
);
|
|
159
|
+
} finally {
|
|
160
|
+
process.off("SIGINT", stop);
|
|
161
|
+
process.off("SIGTERM", stop);
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const { result, interruptedExitCode } = await runChecksInterruptibly(
|
|
166
|
+
checkOptions,
|
|
167
|
+
checkRunner,
|
|
168
|
+
);
|
|
169
|
+
await writeCheckOutput(result.output);
|
|
170
|
+
exitCode.value = interruptedExitCode ?? result.exitCode;
|
|
171
|
+
},
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
program
|
|
175
|
+
.command("apply")
|
|
176
|
+
.description("check source, then make the platform match it")
|
|
177
|
+
.action(async () => {
|
|
178
|
+
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
179
|
+
const { result: checked, interruptedExitCode } = await runChecksInterruptibly(
|
|
180
|
+
{
|
|
181
|
+
sourceRoot,
|
|
182
|
+
environment: context.environment,
|
|
183
|
+
},
|
|
184
|
+
runChecks,
|
|
185
|
+
);
|
|
186
|
+
context.io.stdout(checked.output);
|
|
187
|
+
if (interruptedExitCode !== undefined) {
|
|
188
|
+
exitCode.value = interruptedExitCode;
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (checked.exitCode !== 0) {
|
|
192
|
+
exitCode.value = checked.exitCode;
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
assertCli(checked.source, "Passing checks must return project source", { hint: REPORT_HINT });
|
|
196
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
197
|
+
const result = await applyProject(client, {
|
|
198
|
+
sourceRoot,
|
|
199
|
+
source: checked.source,
|
|
200
|
+
...(projectId === undefined ? {} : { projectId }),
|
|
201
|
+
});
|
|
202
|
+
context.io.stdout(result.output);
|
|
203
|
+
});
|
|
204
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { resolveContext, type CommandHost } from "../context";
|
|
3
|
+
import { projectStatus } from "../project";
|
|
4
|
+
import { assertProjectSelected } from "../project-context";
|
|
5
|
+
|
|
6
|
+
export const registerStatusCommand = (program: Command, host: CommandHost): void => {
|
|
7
|
+
const { context, globals } = host;
|
|
8
|
+
program
|
|
9
|
+
.command("status")
|
|
10
|
+
.description("report project health: live counts and pending source changes")
|
|
11
|
+
.action(async () => {
|
|
12
|
+
const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
|
|
13
|
+
assertProjectSelected(projectId);
|
|
14
|
+
const result = await projectStatus(client, { sourceRoot, projectId });
|
|
15
|
+
context.io.stdout(result.output);
|
|
16
|
+
});
|
|
17
|
+
};
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import {
|
|
3
|
+
positiveInteger,
|
|
4
|
+
resolveContext,
|
|
5
|
+
trainingBatchSize,
|
|
6
|
+
trainingDraftOptions,
|
|
7
|
+
trainingEpochs,
|
|
8
|
+
trainingGistTokens,
|
|
9
|
+
trainingLearningRate,
|
|
10
|
+
trainingLoraRank,
|
|
11
|
+
trainingMaxSequenceLength,
|
|
12
|
+
trainingMethod,
|
|
13
|
+
type CommandHost,
|
|
14
|
+
type RawTrainingDraftOptions,
|
|
15
|
+
} from "../context";
|
|
16
|
+
import { parseBaseModelId } from "../models";
|
|
17
|
+
import { assertProjectSelected } from "../project-context";
|
|
18
|
+
import {
|
|
19
|
+
cancelTrainingRun,
|
|
20
|
+
createTraining,
|
|
21
|
+
createTrainingRun,
|
|
22
|
+
estimateTrainingCost,
|
|
23
|
+
getTraining,
|
|
24
|
+
getTrainingRun,
|
|
25
|
+
listTrainingRuns,
|
|
26
|
+
listTrainings,
|
|
27
|
+
readTrainingRunLogs,
|
|
28
|
+
readTrainingRunMetricHistory,
|
|
29
|
+
readTrainingRunMetrics,
|
|
30
|
+
resumeTrainingRun,
|
|
31
|
+
updateTraining,
|
|
32
|
+
} from "../trainings";
|
|
33
|
+
|
|
34
|
+
export const registerTrainingCommands = (program: Command, host: CommandHost): void => {
|
|
35
|
+
const { context, globals } = host;
|
|
36
|
+
const training = program.command("training").description("manage project Training drafts");
|
|
37
|
+
training
|
|
38
|
+
.command("list")
|
|
39
|
+
.option("--json", "print JSON", false)
|
|
40
|
+
.description("list active Trainings owned by the selected project")
|
|
41
|
+
.action(async (options: { json: boolean }) => {
|
|
42
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
43
|
+
assertProjectSelected(projectId);
|
|
44
|
+
await listTrainings(client, { projectId, json: options.json }, context.io);
|
|
45
|
+
});
|
|
46
|
+
training
|
|
47
|
+
.command("get")
|
|
48
|
+
.argument("<name-or-id>", "Training name or ID")
|
|
49
|
+
.option("--json", "print JSON", false)
|
|
50
|
+
.description("show one Training draft")
|
|
51
|
+
.action(async (nameOrId: string, options: { json: boolean }) => {
|
|
52
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
53
|
+
assertProjectSelected(projectId);
|
|
54
|
+
await getTraining(client, { projectId, nameOrId, json: options.json }, context.io);
|
|
55
|
+
});
|
|
56
|
+
training
|
|
57
|
+
.command("create")
|
|
58
|
+
.argument("<name>", "new Training name")
|
|
59
|
+
.option("--method <method>", "Training method", trainingMethod, "sft")
|
|
60
|
+
.option("--base-model <id>", "catalog Base Model source", parseBaseModelId)
|
|
61
|
+
.option("--model-version <id>", "exact Model Version source")
|
|
62
|
+
.requiredOption("--dataset <id>", "required training Dataset ID")
|
|
63
|
+
.option("--validation-dataset <id>", "optional validation Dataset ID")
|
|
64
|
+
.option("--train-filter <field=value>", "training Dataset field equality filter")
|
|
65
|
+
.option("--validation-filter <field=value>", "validation Dataset field equality filter")
|
|
66
|
+
.option("--lora-rank <rank>", "SFT LoRA adapter rank: 8, 16, or 32 (default 16)", trainingLoraRank)
|
|
67
|
+
.option("--gist-tokens <count>", "gisting soft tokens: 8, 32, or 64 (default 32)", trainingGistTokens)
|
|
68
|
+
.option("--epochs <count>", "training epochs, 1 through 5 (default 3)", trainingEpochs)
|
|
69
|
+
.option(
|
|
70
|
+
"--learning-rate <rate>",
|
|
71
|
+
"optimizer learning rate (default 0.00015 for SFT, 0.03 for gisting)",
|
|
72
|
+
trainingLearningRate,
|
|
73
|
+
)
|
|
74
|
+
.option(
|
|
75
|
+
"--batch-size <count>",
|
|
76
|
+
"sequences per optimizer step: 8, 16, 32, or 64 (default 8)",
|
|
77
|
+
trainingBatchSize,
|
|
78
|
+
)
|
|
79
|
+
.option(
|
|
80
|
+
"--max-sequence-length <tokens>",
|
|
81
|
+
"token budget per example, 64 through 16384 (default 12288 for SFT, 3072 for gisting)",
|
|
82
|
+
trainingMaxSequenceLength,
|
|
83
|
+
)
|
|
84
|
+
.option("--prompt-file <path>", "gisting prompt text file")
|
|
85
|
+
.option("--json", "print JSON", false)
|
|
86
|
+
.description("create a normalized Training draft")
|
|
87
|
+
.action(async (name: string, options: RawTrainingDraftOptions & { json: boolean }) => {
|
|
88
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
89
|
+
assertProjectSelected(projectId);
|
|
90
|
+
await createTraining(
|
|
91
|
+
client,
|
|
92
|
+
{
|
|
93
|
+
...trainingDraftOptions(options),
|
|
94
|
+
projectId,
|
|
95
|
+
name,
|
|
96
|
+
json: options.json,
|
|
97
|
+
},
|
|
98
|
+
context.io,
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
training
|
|
102
|
+
.command("update")
|
|
103
|
+
.argument("<name-or-id>", "Training name or ID")
|
|
104
|
+
.option("--base-model <id>", "catalog Base Model source", parseBaseModelId)
|
|
105
|
+
.option("--model-version <id>", "exact Model Version source")
|
|
106
|
+
.option("--dataset <id>", "required training Dataset ID")
|
|
107
|
+
.option("--validation-dataset <id>", "replacement validation Dataset ID")
|
|
108
|
+
.option("--train-filter <field=value>", "replacement training Dataset field equality filter")
|
|
109
|
+
.option(
|
|
110
|
+
"--validation-filter <field=value>",
|
|
111
|
+
"replacement validation Dataset field equality filter",
|
|
112
|
+
)
|
|
113
|
+
.option("--lora-rank <rank>", "SFT LoRA adapter rank: 8, 16, or 32 (default 16)", trainingLoraRank)
|
|
114
|
+
.option("--gist-tokens <count>", "gisting soft tokens: 8, 32, or 64 (default 32)", trainingGistTokens)
|
|
115
|
+
.option("--epochs <count>", "training epochs, 1 through 5 (default 3)", trainingEpochs)
|
|
116
|
+
.option(
|
|
117
|
+
"--learning-rate <rate>",
|
|
118
|
+
"optimizer learning rate (default 0.00015 for SFT, 0.03 for gisting)",
|
|
119
|
+
trainingLearningRate,
|
|
120
|
+
)
|
|
121
|
+
.option(
|
|
122
|
+
"--batch-size <count>",
|
|
123
|
+
"sequences per optimizer step: 8, 16, 32, or 64 (default 8)",
|
|
124
|
+
trainingBatchSize,
|
|
125
|
+
)
|
|
126
|
+
.option(
|
|
127
|
+
"--max-sequence-length <tokens>",
|
|
128
|
+
"token budget per example, 64 through 16384 (default 12288 for SFT, 3072 for gisting)",
|
|
129
|
+
trainingMaxSequenceLength,
|
|
130
|
+
)
|
|
131
|
+
.option("--prompt-file <path>", "replacement gisting prompt text file")
|
|
132
|
+
.option("--json", "print JSON", false)
|
|
133
|
+
.description("replace the complete Training draft")
|
|
134
|
+
.action(async (nameOrId: string, options: RawTrainingDraftOptions & { json: boolean }) => {
|
|
135
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
136
|
+
assertProjectSelected(projectId);
|
|
137
|
+
await updateTraining(
|
|
138
|
+
client,
|
|
139
|
+
{
|
|
140
|
+
...trainingDraftOptions(options),
|
|
141
|
+
projectId,
|
|
142
|
+
nameOrId,
|
|
143
|
+
json: options.json,
|
|
144
|
+
},
|
|
145
|
+
context.io,
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
training
|
|
149
|
+
.command("cost")
|
|
150
|
+
.argument("<name-or-id>", "Training name or ID")
|
|
151
|
+
.option("--idempotency-key <key>", "reuse one Cost Estimate input preparation")
|
|
152
|
+
.option("--json", "print JSON", false)
|
|
153
|
+
.description("validate Training inputs and calculate the maximum charge")
|
|
154
|
+
.action(async (nameOrId: string, options: { idempotencyKey?: string; json: boolean }) => {
|
|
155
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
156
|
+
assertProjectSelected(projectId);
|
|
157
|
+
await estimateTrainingCost(
|
|
158
|
+
client,
|
|
159
|
+
{ projectId, nameOrId, idempotencyKey: options.idempotencyKey, json: options.json },
|
|
160
|
+
context.io,
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
training
|
|
164
|
+
.command("run")
|
|
165
|
+
.argument("<name-or-id>", "Training name or ID")
|
|
166
|
+
.option("--estimate-file <path>", "ready Training Cost Estimate JSON")
|
|
167
|
+
.option("--version-name <name>", "unique Model Version name for this run")
|
|
168
|
+
.option("--idempotency-key <key>", "reuse one Training Run creation")
|
|
169
|
+
.option("--json", "print JSON", false)
|
|
170
|
+
.description("calculate a ready Cost Estimate and create one queued Training Run")
|
|
171
|
+
.action(
|
|
172
|
+
async (
|
|
173
|
+
nameOrId: string,
|
|
174
|
+
options: {
|
|
175
|
+
estimateFile?: string;
|
|
176
|
+
versionName?: string;
|
|
177
|
+
idempotencyKey?: string;
|
|
178
|
+
json: boolean;
|
|
179
|
+
},
|
|
180
|
+
) => {
|
|
181
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
182
|
+
assertProjectSelected(projectId);
|
|
183
|
+
await createTrainingRun(client, { projectId, nameOrId, ...options }, context.io);
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
training
|
|
187
|
+
.command("runs")
|
|
188
|
+
.argument("<name-or-id>", "Training name or ID")
|
|
189
|
+
.option("--json", "print JSON", false)
|
|
190
|
+
.description("list Training Run history")
|
|
191
|
+
.action(async (nameOrId: string, options: { json: boolean }) => {
|
|
192
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
193
|
+
assertProjectSelected(projectId);
|
|
194
|
+
await listTrainingRuns(client, { projectId, nameOrId, json: options.json }, context.io);
|
|
195
|
+
});
|
|
196
|
+
training
|
|
197
|
+
.command("status")
|
|
198
|
+
.argument("<training-run-id>", "Training Run ID")
|
|
199
|
+
.option("--json", "print JSON", false)
|
|
200
|
+
.description("show one Training Run")
|
|
201
|
+
.action(async (trainingRunId: string, options: { json: boolean }) => {
|
|
202
|
+
const { client } = await resolveContext(globals(), context);
|
|
203
|
+
await getTrainingRun(client, { trainingRunId, json: options.json }, context.io);
|
|
204
|
+
});
|
|
205
|
+
training
|
|
206
|
+
.command("resume")
|
|
207
|
+
.argument("<training-run-id>", "Training Run ID")
|
|
208
|
+
.option("--json", "print JSON", false)
|
|
209
|
+
.description("resume a Training Run blocked on funds or payment")
|
|
210
|
+
.action(async (trainingRunId: string, options: { json: boolean }) => {
|
|
211
|
+
const { client } = await resolveContext(globals(), context);
|
|
212
|
+
await resumeTrainingRun(client, { trainingRunId, json: options.json }, context.io);
|
|
213
|
+
});
|
|
214
|
+
training
|
|
215
|
+
.command("cancel")
|
|
216
|
+
.argument("<training-run-id>", "Training Run ID")
|
|
217
|
+
.option("--json", "print JSON", false)
|
|
218
|
+
.description("cancel a Training Run before provider work completes")
|
|
219
|
+
.action(async (trainingRunId: string, options: { json: boolean }) => {
|
|
220
|
+
const { client } = await resolveContext(globals(), context);
|
|
221
|
+
await cancelTrainingRun(client, { trainingRunId, json: options.json }, context.io);
|
|
222
|
+
});
|
|
223
|
+
training
|
|
224
|
+
.command("metrics")
|
|
225
|
+
.argument("<training-run-id>", "Training Run ID")
|
|
226
|
+
.option("--key <metric>", "read the point history of one metric instead of the summary")
|
|
227
|
+
.option("--resolution <count>", "history points to return, 2 to 500 (default 200)", positiveInteger)
|
|
228
|
+
.option("--json", "print JSON", false)
|
|
229
|
+
.description("read Training Run metrics")
|
|
230
|
+
.action(
|
|
231
|
+
async (
|
|
232
|
+
trainingRunId: string,
|
|
233
|
+
options: { key?: string; resolution?: number; json: boolean },
|
|
234
|
+
) => {
|
|
235
|
+
const { client } = await resolveContext(globals(), context);
|
|
236
|
+
if (options.key === undefined) {
|
|
237
|
+
await readTrainingRunMetrics(client, { trainingRunId, json: options.json }, context.io);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
await readTrainingRunMetricHistory(
|
|
241
|
+
client,
|
|
242
|
+
{
|
|
243
|
+
trainingRunId,
|
|
244
|
+
metricKey: options.key,
|
|
245
|
+
resolution: options.resolution,
|
|
246
|
+
json: options.json,
|
|
247
|
+
},
|
|
248
|
+
context.io,
|
|
249
|
+
);
|
|
250
|
+
},
|
|
251
|
+
);
|
|
252
|
+
training
|
|
253
|
+
.command("logs")
|
|
254
|
+
.argument("<training-run-id>", "Training Run ID")
|
|
255
|
+
.option("--follow", "wait for new log entries until complete", false)
|
|
256
|
+
.description("read sanitized Training Run logs")
|
|
257
|
+
.action(async (trainingRunId: string, options: { follow: boolean }) => {
|
|
258
|
+
const { client } = await resolveContext(globals(), context);
|
|
259
|
+
await readTrainingRunLogs(client, { trainingRunId, follow: options.follow }, context.io);
|
|
260
|
+
});
|
|
261
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { CommandHost } from "../context";
|
|
3
|
+
import { cliVersionInfo } from "../version";
|
|
4
|
+
import { SDK_VERSION } from "../sdk-version";
|
|
5
|
+
|
|
6
|
+
export const registerVersionCommand = (program: Command, host: CommandHost): void => {
|
|
7
|
+
program
|
|
8
|
+
.command("version")
|
|
9
|
+
.option("--json", "print version and build provenance as JSON", false)
|
|
10
|
+
.description("print the current CLI version")
|
|
11
|
+
.action((options: { json: boolean }) =>
|
|
12
|
+
host.context.io.stdout(options.json ? JSON.stringify(cliVersionInfo()) : SDK_VERSION),
|
|
13
|
+
);
|
|
14
|
+
};
|
package/src/cli/config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { assert } from "es-toolkit";
|
|
2
1
|
import type { TolokaState } from "../state";
|
|
3
2
|
import type { StoredCredentials } from "./credentials";
|
|
4
3
|
import { resolveProjectContext } from "./project-context";
|
|
4
|
+
import { assertCli } from "./cli-error";
|
|
5
5
|
|
|
6
6
|
export type TolokaCliFlags = {
|
|
7
7
|
readonly apiKey?: string;
|
|
@@ -10,9 +10,8 @@ export type TolokaCliFlags = {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
export type TolokaEnvironment = Readonly<Record<string, string | undefined>> & {
|
|
13
|
-
readonly
|
|
14
|
-
readonly
|
|
15
|
-
readonly POTATO_PROJECT_ID?: string;
|
|
13
|
+
readonly TOLOKA_API_KEY?: string;
|
|
14
|
+
readonly TOLOKA_BASE_URL?: string;
|
|
16
15
|
readonly TOLOKA_PROJECT_ID?: string;
|
|
17
16
|
};
|
|
18
17
|
|
|
@@ -54,7 +53,7 @@ const missingApiKeyMessage = [
|
|
|
54
53
|
" toloka login",
|
|
55
54
|
"",
|
|
56
55
|
"It explains how to generate an API key and where it is saved. To override per command,",
|
|
57
|
-
"pass --api-key or set
|
|
56
|
+
"pass --api-key or set TOLOKA_API_KEY. Run toloka auth-status to see what is in effect.",
|
|
58
57
|
].join("\n");
|
|
59
58
|
|
|
60
59
|
export function resolveTolokaConfig(
|
|
@@ -63,10 +62,10 @@ export function resolveTolokaConfig(
|
|
|
63
62
|
environment: TolokaEnvironment,
|
|
64
63
|
stored?: StoredCredentials,
|
|
65
64
|
): ResolvedTolokaConfig {
|
|
66
|
-
const apiKey = flags.apiKey ?? environment.
|
|
65
|
+
const apiKey = flags.apiKey ?? environment.TOLOKA_API_KEY ?? stored?.apiKey;
|
|
67
66
|
const baseUrl =
|
|
68
67
|
flags.baseUrl ??
|
|
69
|
-
environment.
|
|
68
|
+
environment.TOLOKA_BASE_URL ??
|
|
70
69
|
resolveInternalBaseUrl(environment) ??
|
|
71
70
|
stored?.baseUrl ??
|
|
72
71
|
PRODUCTION_BASE_URL;
|
|
@@ -77,6 +76,6 @@ export function resolveTolokaConfig(
|
|
|
77
76
|
stored,
|
|
78
77
|
effectiveBaseUrl: baseUrl,
|
|
79
78
|
});
|
|
80
|
-
|
|
79
|
+
assertCli(apiKey, missingApiKeyMessage, { hint: { disabled: true } });
|
|
81
80
|
return { apiKey, baseUrl, ...(project === undefined ? {} : { projectId: project.id }) };
|
|
82
81
|
}
|