@toloka-platform/sdk 0.2.0-preview.3 → 0.2.0

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 (75) hide show
  1. package/README.md +5 -6
  2. package/dist/cli/toloka.js +136 -136
  3. package/dist/cli/toloka.js.jsc +0 -0
  4. package/dist/cli/toloka.js.jsc.sha256 +1 -1
  5. package/dist/cli/toloka.js.sha256 +1 -1
  6. package/docs/cli-v2.md +3 -2
  7. package/docs/cli.md +35 -7
  8. package/docs/getting-started.md +4 -2
  9. package/docs/limitations.md +2 -2
  10. package/openapi/gen/models/baseModel.ts +1 -0
  11. package/openapi/gen/models/currentUser.ts +2 -0
  12. package/openapi/gen/models/currentUserTeamsItem.ts +6 -0
  13. package/openapi/gen/models/gistingTrainingConfiguration.ts +6 -2
  14. package/openapi/gen/models/gistingTrainingConfigurationBatchSize.ts +2 -0
  15. package/openapi/gen/models/gistingTrainingConfigurationGistTokens.ts +2 -0
  16. package/openapi/gen/models/gistingTrainingConfigurationInput.ts +6 -2
  17. package/openapi/gen/models/gistingTrainingConfigurationInputBatchSize.ts +2 -0
  18. package/openapi/gen/models/gistingTrainingConfigurationInputGistTokens.ts +2 -0
  19. package/openapi/gen/models/index.ts +9 -4
  20. package/openapi/gen/models/sftTrainingConfiguration.ts +6 -3
  21. package/openapi/gen/models/sftTrainingConfigurationBatchSize.ts +2 -0
  22. package/openapi/gen/models/sftTrainingConfigurationInput.ts +6 -3
  23. package/openapi/gen/models/sftTrainingConfigurationInputBatchSize.ts +2 -0
  24. package/openapi/gen/models/sftTrainingConfigurationInputLoraRank.ts +2 -0
  25. package/openapi/gen/models/sftTrainingConfigurationLoraRank.ts +2 -0
  26. package/package.json +2 -2
  27. package/skills/catalog.json +11 -11
  28. package/skills/toloka-build/SKILL.md +29 -18
  29. package/skills/toloka-build/references/node-semantics.md +7 -7
  30. package/skills/toloka-build/references/workflow-and-cli.md +15 -12
  31. package/skills/toloka-fine-tuning/SKILL.md +145 -62
  32. package/skills/toloka-get-started/SKILL.md +37 -21
  33. package/skills/toloka-gisting/SKILL.md +156 -66
  34. package/skills/toloka-report-issue/references/report-template.md +1 -1
  35. package/src/cli/analytics.ts +50 -23
  36. package/src/cli/auth.ts +31 -12
  37. package/src/cli/auto-update.ts +12 -6
  38. package/src/cli/check.ts +76 -29
  39. package/src/cli/cli-entry.ts +3 -2
  40. package/src/cli/cli-error.ts +43 -0
  41. package/src/cli/commands/dataset.ts +13 -3
  42. package/src/cli/commands/models.ts +6 -7
  43. package/src/cli/commands/pipeline.ts +6 -7
  44. package/src/cli/commands/project.ts +9 -16
  45. package/src/cli/commands/report.ts +6 -2
  46. package/src/cli/commands/secret.ts +5 -4
  47. package/src/cli/commands/source.ts +4 -4
  48. package/src/cli/commands/status.ts +2 -3
  49. package/src/cli/commands/training.ts +50 -17
  50. package/src/cli/config.ts +2 -2
  51. package/src/cli/context.ts +55 -17
  52. package/src/cli/credentials.ts +12 -4
  53. package/src/cli/data.ts +96 -34
  54. package/src/cli/delivery-orders.ts +18 -1
  55. package/src/cli/file-system.ts +6 -2
  56. package/src/cli/format-error.ts +45 -2
  57. package/src/cli/init.ts +13 -7
  58. package/src/cli/models.ts +43 -12
  59. package/src/cli/program.ts +5 -56
  60. package/src/cli/project-context.ts +117 -32
  61. package/src/cli/project.ts +80 -28
  62. package/src/cli/report.ts +24 -9
  63. package/src/cli/runs.ts +33 -8
  64. package/src/cli/settings.ts +13 -5
  65. package/src/cli/skill.ts +61 -22
  66. package/src/cli/source.ts +3 -2
  67. package/src/cli/terminal-io.ts +13 -6
  68. package/src/cli/trainings.ts +129 -35
  69. package/src/cli/version.ts +2 -2
  70. package/src/client.ts +3 -9
  71. package/src/transport.ts +13 -1
  72. package/openapi/gen/models/gistingTrainingConfigurationInputRegime.ts +0 -2
  73. package/openapi/gen/models/gistingTrainingConfigurationRegime.ts +0 -2
  74. package/openapi/gen/models/sftTrainingConfigurationInputRegime.ts +0 -2
  75. package/openapi/gen/models/sftTrainingConfigurationRegime.ts +0 -2
@@ -1,40 +1,47 @@
1
1
  ---
2
2
  name: toloka-get-started
3
- description: Route a first Toloka agent session to the correct workflow and establish safe CLI, authentication, project, and approval checkpoints. Use when the user is new to Toloka, asks what to do first, or has not selected a pipeline, fine-tuning, or gisting workflow.
3
+ description: Orient a Toloka session to the user's outcome and safely reuse or establish the needed CLI, authentication, project, workflow, and approval context. Use when the user is new to Toloka, asks what to do first, or has not selected a pipeline, fine-tuning, or gisting workflow.
4
4
  ---
5
5
 
6
6
  # Get started with Toloka
7
7
 
8
- Use this skill to orient a new session. Choose one workflow before making a platform mutation:
8
+ Use this skill to help the user reach the outcome they asked for. Start with the request, the current checkout, and observed CLI state. If setup, authentication, a project binding, or workflow source is already in place and consistent with the task, continue from it instead of repeating onboarding, replacing the binding, or creating a demo.
9
9
 
10
- - **Label data or build a pipeline:** install and read `toloka-build`, then follow `https://platform.toloka.ai/agents/data-labeling.md`.
11
- - **Fine-tune a model:** install and read `toloka-fine-tuning`, then follow `https://platform.toloka.ai/agents/fine-tuning.md`.
12
- - **Compress a prompt with gisting:** install and read `toloka-gisting`, then follow `https://platform.toloka.ai/agents/gisting.md`.
10
+ ## Choose the relevant workflow
13
11
 
14
- ## Prepare the CLI
12
+ Honor an explicitly requested target. Otherwise choose the workflow that fits the outcome and briefly explain the choice:
15
13
 
16
- Check the required runtime and CLI:
14
+ - **Label data or build a pipeline:** install and read `toloka-build`, then use `https://platform.toloka.ai/agents/data-labeling.md` as a reference.
15
+ - **Fine-tune a model:** install and read `toloka-fine-tuning`, then use `https://platform.toloka.ai/agents/fine-tuning.md` as a reference.
16
+ - **Compress a prompt with gisting:** install and read `toloka-gisting`, then use `https://platform.toloka.ai/agents/gisting.md` as a reference.
17
+
18
+ The workflow sequence preserves important dependencies and gates, but it is not a script: skip steps that current evidence shows are already satisfied or irrelevant. If the user asks only for design, inspection, or an estimate, finish at that outcome rather than creating extra resources or starting a run.
19
+
20
+ ## Prepare missing prerequisites
21
+
22
+ Check the runtime or CLI only when its state is not already known:
17
23
 
18
24
  ```sh
19
25
  bun --version
20
26
  toloka --version
27
+ toloka dataset --help
21
28
  ```
22
29
 
23
- The runbooks above use the `preview` channel of `@toloka-platform/sdk`, so `toloka --version` must print a `-preview` version. If `toloka` is unavailable or comes from another channel, ask the user to run the installer in their own shell:
30
+ The runbooks require a stable Toloka CLI release at version `0.2.0` or newer: `toloka --version` must print `0.2.0` or a newer version with no prerelease suffix, and `toloka dataset --help` must list `create`. If either command is unavailable, the version is older or prerelease, or the capability check fails, install the latest stable version:
24
31
 
25
32
  ```sh
26
- curl -fsSL https://platform.toloka.ai/cli/install.sh | TOLOKA_CLI_CHANNEL=preview sh
33
+ curl -fsSL https://platform.toloka.ai/cli/install.sh | sh
27
34
  ```
28
35
 
29
- Do not request an API key in chat or run an unattended installer. Ask the user to run `toloka login`, then verify the intended account with:
36
+ Never request an API key in chat. Inspect the active identity with:
30
37
 
31
38
  ```sh
32
39
  toloka whoami
33
40
  ```
34
41
 
35
- `Rejected:` means the key was refused (ask for `toloka login` again); `Not reachable:` means the platform did not answer and the key was not evaluated (fix network access, do not ask for a new key); `Platform error:` means the platform failed (retry later).
42
+ If the reachable account is evidently the intended identity from the request, project, team, or established checkout, continue and report it. Ask a focused question when the identity conflicts with that context or more than one material choice remains. For a missing or rejected key, ask the user to run `toloka login`, then verify `toloka whoami` again. `Rejected:` means the key was refused; `Not reachable:` means the platform did not answer and the key was not evaluated, so fix network access rather than requesting another key; `Platform error:` means the platform failed, so retry later.
36
43
 
37
- Use a source root for pipeline work. Run `toloka init` before searching or installing skills:
44
+ Pipeline work needs a source root. Reuse a suitable initialized root when one exists. In a new root, or when the Agent Bootstrap or installed skills genuinely need initialization or refresh, use:
38
45
 
39
46
  ```sh
40
47
  toloka init
@@ -42,14 +49,23 @@ toloka skill search <workflow>
42
49
  toloka skill install <skill-id>
43
50
  ```
44
51
 
45
- Read the installed skill before acting. Refresh a stale skill with `--force`.
52
+ Read the installed skill before acting. Refresh a stale skill with `--force`; do not refresh a current one merely to follow onboarding.
53
+
54
+ ## Operating posture
55
+
56
+ - Inspect current state before writing. Use the user's explicit workflow and project choices first. Make routine, reversible choices from available evidence and briefly report the reasoning. Ask only for material ambiguity, missing authorization or required inputs, conflicting constraints, or a destructive or out-of-scope change.
57
+ - For pipeline work, choose a node approach from the task and explain it briefly: deterministic processing or scripted calls fit code; human judgment or content generation fits generation; independent annotations and visible disagreement fit quorum; conditional routing fits match. Use `toloka-build` for the detailed authoring rules.
58
+ - Resolve a project from explicit user context, CLI output, `toloka.json`, or `toloka project current`; never reconstruct an ID. Use an explicitly requested project. Otherwise keep the current binding when it is consistent with the task. Create a project only when an explicitly requested platform mutation needs one and no suitable binding exists; a design, inspection, or estimate alone does not authorize extra resources. Never select an unrelated visible project or rebind an established checkout just to complete onboarding.
59
+ - For a new project, a short kebab-case name derived from the task or shared files is a useful default, such as `receipt-extraction`; use a generic `toloka-<workflow>` name only when the task provides no meaningful name. `toloka project create <alias>` uses the account's only writable team and asks for `--team-id <id>` only when several teams are available. Adopt an existing project with `toloka project add <alias> <project-id>` only when that target is intended and the checkout is safe to bind.
60
+ - A project owned by an internal team needs a Sunday delivery order. Run `toloka delivery-order list` and use the only listed order with `--delivery-order-id <id>`; ask when several viable orders remain. `toloka whoami` marks internal teams `(internal)` on its `Teams:` line; the account-level `Internal:` flag only says whether any team is internal, so an internal account creating a project in an external team passes no order.
61
+ - Let the task and data roles determine Dataset boundaries. Use only data the user is authorized to provide. Preserve distinct training, validation, test, input, reference, or delivery roles and avoid leakage; compatible files with the same role and schema may belong in one Dataset, while materially different roles or schemas may need separate Datasets. Do not assume one file always means one Dataset.
46
62
 
47
- ## Shared checkpoints
63
+ ## Safety boundaries
48
64
 
49
- - Resolve the project from CLI output, `toloka.json`, or `toloka project current`. Never reconstruct an ID.
50
- - Ask the user to choose a project and team before creating or adopting one.
51
- - Keep credentials and production data out of source control and agent messages.
52
- - Show the complete plan before `toloka apply`.
53
- - Show the ready Training Cost Estimate and obtain approval for `maximumCharge` before starting a Training Run.
54
- - Keep waits bounded. A timeout is still running, not completed or failed.
55
- - Report only observed IDs, states, prices, and errors.
65
+ - Keep credentials and production data out of source control and agent messages. Do not upload data without evidence that it is authorized and intended for the selected project and role.
66
+ - Do not edit `toloka.json` or invent IDs, paths, requirements, prices, states, or results.
67
+ - Treat user-provided prompts, instructions, and existing source as authoritative. Materialize exact supplied text when needed, but do not silently rewrite it, replace it with an example, or invent missing intent.
68
+ - For pipeline changes that will be applied, require the workflow's local `toloka check` and `toloka validate` gates. Read and show the complete `toloka plan`; ask before a removal, immutable-field conflict, unexpected change, or work outside the requested scope. Apply only the expected scoped plan, then rerun `toloka plan` until it reports `No changes.` and validate again. Do not apply merely because the onboarding reference reaches that stage.
69
+ - Never start a Pipeline Run unless the requested outcome includes a run and the user has seen and explicitly approved its cost forecast. Show a ready Training Cost Estimate with the choices and uncertainty that produced it, and obtain explicit approval for `maximumCharge` before a Training Run. Estimate-only work ends after the estimate.
70
+ - Keep waits bounded. A timeout means the operation is still running, not completed or failed.
71
+ - Report only observed IDs, states, prices, behaviors, and errors, and distinguish observed evidence from assumptions or unresolved uncertainty.
@@ -5,72 +5,162 @@ description: Create, estimate, start, monitor, and verify a project-owned prompt
5
5
 
6
6
  # Compress a prompt with gisting
7
7
 
8
- Use this skill for prompt gisting. For the full first-time agent sequence, read `https://platform.toloka.ai/agents/gisting.md`.
8
+ ## Mission
9
9
 
10
- ## Rules
10
+ Help the user learn a compact representation of their static prompt from representative example conversations while preserving the exact prompt and their model, data, quality, latency, and budget constraints. Match the work to the requested outcome: inspect an existing Training or run, prepare a draft, calculate an estimate, start an approved run, monitor it, or retrieve its output. Do not create resources or start a run merely because they appear later in this reference.
11
+
12
+ For first-time setup details, read `https://platform.toloka.ai/agents/gisting.md`.
13
+
14
+ ## Domain model
15
+
16
+ - **Prompt gisting** learns soft prompt tokens that stand in for a static instruction prompt. Training examples should contain the dynamic inputs and desired assistant responses; do not repeat the static prompt in every user message.
17
+ - A **Training** is an editable project-owned draft. A **Training Run** is one immutable, billable execution of a frozen draft, prompt, and Dataset selections.
18
+ - A run can start from a catalog **Base Model** or an exact existing **Model Version**. An explicit Model Version is the source; do not also select a Base Model.
19
+ - A completed run publishes a durable Model Version. It does not prove deployment or inference readiness.
20
+ - Training and validation are data roles, not file-layout rules. Separate Datasets and disjoint top-level filters are both valid. Without a validation selection, the provider reserves 10% of the training rows as a holdout.
21
+
22
+ ## Working posture
23
+
24
+ - Inspect the user's request and current CLI, project, Training, Dataset, prompt, and run state before changing anything. The process below is a dependency map: skip satisfied or irrelevant work and stop when the requested outcome is complete.
25
+ - Honor explicit choices first. If a requested project, source, Dataset, prompt, or configuration is incompatible, explain the evidence and tradeoff before proposing a substitute.
26
+ - Make routine reversible choices and briefly explain them. Ask a focused question only for material ambiguity, missing authorization or inputs, conflicting constraints, or destructive or out-of-scope changes.
27
+ - If the goal is weight adaptation from labeled conversations or data labeling rather than prompt compression, explain the distinction and route to `toloka-fine-tuning` or `toloka-build`; never silently change the method.
28
+
29
+ ## Safety
11
30
 
12
31
  - Never ask for an API key in chat; the user enters it through `toloka login`.
13
- - Never invent project, Dataset, Base Model, Training, Model, Model Version, price, prompt, or run IDs.
14
- - Use authorized project Datasets containing representative examples. Keep production data out of source control.
15
- - The prompt file must be non-empty UTF-8 text no larger than 256 KiB.
16
- - Prefer a separate validation Dataset. Without one, the provider holds out 10% of training rows.
17
- - Do not start a Training Run before the user approves the ready estimate's `maximumCharge`.
18
- - Use `--model-version` only for an existing Model Version source.
19
- - Keep polling bounded and report terminal state only when the CLI shows it.
20
-
21
- ## Workflow
22
-
23
- 1. Verify `bun --version`, `toloka --version`, `toloka whoami`, and `toloka project current`.
24
- 2. Inspect the exact training Dataset ID supplied by the user:
25
- ```sh
26
- toloka dataset fields <training-dataset-id>
27
- ```
28
- 3. Ask the user for an absolute prompt-file path and validate that it is non-empty UTF-8 text within the size limit.
29
- 4. Verify the supplied Base Model ID or existing Model Version source:
30
- ```sh
31
- toloka base-model get <base-model-id>
32
- ```
33
- 5. Ask the user to choose the source, training Dataset, optional validation Dataset, prompt file, and Training name.
34
- 6. Create the normalized gisting Training:
35
- ```sh
36
- toloka training create "<training-name>" \
37
- --method gisting \
38
- --prompt-file <absolute-prompt-path> \
39
- --base-model <base-model-id> \
40
- --dataset <training-dataset-id> \
41
- --validation-dataset <validation-dataset-id> \
42
- --json
43
- ```
44
- Omit `--validation-dataset` when the user chooses provider holdout. Use `--model-version <model-version-id>` instead of `--base-model` for an existing Model Version source.
45
- 7. Verify the draft:
46
- ```sh
47
- toloka training get <training-id> --json
48
- ```
49
- 8. Calculate a ready estimate to an absolute path:
50
- ```sh
51
- toloka training cost <training-id> --json > <absolute-estimate-file>
52
- ```
53
- Report rows, tokens, maximum GPU seconds, `maximumCharge`, currency, and fingerprint. Fix every blocked issue and recalculate after changing inputs.
54
- 9. Ask the user to approve the exact `maximumCharge`. After approval, start one run:
55
- ```sh
56
- toloka training run <training-id> \
57
- --estimate-file <absolute-estimate-file> \
58
- --version-name <unique-version-name> \
59
- --json
60
- ```
61
- Both flags are optional. Without `--estimate-file`, `training run` calculates a fresh ready estimate itself; without `--version-name`, the Model Version takes the Training name. Passing the approved estimate prevents a fresh quote from replacing the reviewed one.
62
- 10. Monitor with bounded commands:
63
- ```sh
64
- toloka training status <training-run-id> --json
65
- toloka training logs <training-run-id>
66
- toloka training metrics <training-run-id> --json
67
- ```
68
- Resume only after the user confirms a billing block is resolved. Cancel only on explicit request.
69
- 11. When completed, verify the output Model and Model Version, then download if requested:
70
- ```sh
71
- toloka training get <training-id> --json
72
- toloka model get <model-id> --json
73
- toloka model version download <model-version-id> --out <absolute-output-directory>
74
- ```
75
-
76
- A completed Training Run produces a durable Model Version. It does not prove deployment or inference readiness.
32
+ - Never invent project, Dataset, Base Model, Training, Model, Model Version, prompt, price, or run IDs. Read identity and source metadata from the CLI.
33
+ - Use only authorized project Datasets containing representative examples. Keep credentials and production data out of source control and agent messages.
34
+ - The prompt file must be non-empty UTF-8 text no larger than 256 KiB. Preserve user-provided prompt text exactly unless they ask to edit it; when the prompt is supplied in chat, materialize that exact text instead of inventing missing intent.
35
+ - Preserve train/validation separation and avoid duplicate or near-duplicate examples across roles.
36
+ - Never start a Training Run before the user approves the ready estimate's exact `maximumCharge`.
37
+ - Keep polling bounded. Report terminal state only when the CLI shows it, and report timeouts as still running.
38
+
39
+ ## Process
40
+
41
+ ### 1. Establish only the context the goal needs
42
+
43
+ When runtime, authentication, or binding is unknown, inspect it:
44
+
45
+ ```sh
46
+ bun --version
47
+ toloka --version
48
+ toloka whoami
49
+ toloka project current
50
+ ```
51
+
52
+ Reuse an authenticated, compatible workspace. For an inspection request, inspect existing resources rather than creating a project, Dataset, Training, prompt file, or run.
53
+
54
+ Use the project the user explicitly requested. Otherwise keep the current binding when it is consistent with the task. If no suitable project is bound and the requested work needs one, inspect `toloka project list --limit 20`, then create a task-named project or register the exact existing project the user identified. Do not silently select an unrelated project or rebind an existing checkout; use an explicit `--project-id` or a separate source root when working against another project without changing the binding.
55
+
56
+ ### 2. Inspect and assign data roles
57
+
58
+ ```sh
59
+ toloka dataset list
60
+ toloka dataset fields <dataset-name-or-id>
61
+ ```
62
+
63
+ Use the user's authorized data and existing role assignments. If local JSONL files must be uploaded, organize Datasets by training and validation role, not automatically by file. `dataset upload` appends, so compatible files for one role can share a Dataset:
64
+
65
+ ```sh
66
+ toloka dataset create <role-dataset-name>
67
+ toloka dataset upload <role-dataset-name> <absolute-jsonl-path>
68
+ toloka dataset fields <role-dataset-name>
69
+ ```
70
+
71
+ CLI uploads to an otherwise undeclared Dataset store each row whole. A nested `split` value in that uploaded object is not a top-level Dataset field and cannot be used by `--train-filter` or `--validation-filter`. In that case, use separate role Datasets or omit validation flags and use the provider's 10% holdout. Existing Datasets with a real top-level split field can use disjoint filters. Never use overlapping selections or mix a supplied validation set back into training.
72
+
73
+ Check that the examples demonstrate the desired behavior under the prompt: dynamic input in user messages, target output in the final assistant message, representative edge cases, and no prompt or answer leakage across validation.
74
+
75
+ ### 3. Preserve the prompt and choose the source
76
+
77
+ Confirm the prompt path contains non-empty UTF-8 text within 256 KiB. Use the supplied file as-is. If the user supplied prompt text in the request instead, write that exact text to a UTF-8 file. Ask only when the required prompt or its intended boundaries are genuinely missing; do not author a replacement prompt from assumptions.
78
+
79
+ Explicit source choices take precedence. An existing Model Version bypasses Base Model selection.
80
+
81
+ Gisting supports only `qwen3-4b` and `qwen3-8b` because it publishes full model weights. This restriction also applies to the underlying Base Model of an existing Model Version. Unsupported tiers are rejected when creating or updating a Training draft; explain the constraint rather than silently substituting a model.
82
+
83
+ ```sh
84
+ toloka base-model list --json
85
+ toloka base-model get <base-model-id> --json
86
+ toloka model list --json
87
+ toloka model get <model-id> --json
88
+ toloka model versions <model-id> --json
89
+ ```
90
+
91
+ For a Base Model, compare available evidence about prompt and language fit, prior quality, data volume, desired compression fidelity, training budget, and serving latency or deployment constraints. Model size alone is not a quality decision; a smaller model is a useful cost and latency baseline when the task permits it, not a mandatory default. Verify the selected catalog ID, upstream model, and pinned revision with `base-model get`, and consult upstream documentation or user-provided evidence for capabilities the catalog does not expose.
92
+
93
+ Gisting starts either from a catalog Base Model or, to compress a prompt for a model you already fine-tuned, from that model's LoRA adapter Model Version via `--model-version` (the adapter is merged into the base and the gist tokens are trained over the fine-tuned weights). Because gisting publishes a full model, it is limited to the qwen3-4b and qwen3-8b tiers, and a `--model-version` source must be a LoRA adapter version (a full-weights version is rejected).
94
+
95
+ ### 4. Reuse or create the draft
96
+
97
+ Inspect before creating another Training:
98
+
99
+ ```sh
100
+ toloka training list --json
101
+ toloka training get <training-id> --json
102
+ ```
103
+
104
+ When a new draft is needed, use a descriptive name and create normalized gisting configuration:
105
+
106
+ ```sh
107
+ toloka training create "<training-name>" \
108
+ --method gisting \
109
+ --prompt-file <absolute-prompt-path> \
110
+ --base-model <base-model-id> \
111
+ --dataset <training-dataset-id> \
112
+ --validation-dataset <validation-dataset-id> \
113
+ --json
114
+ ```
115
+
116
+ Omit `--validation-dataset` when no separate validation selection exists so the provider uses its 10% holdout. For disjoint selections in a Dataset with top-level fields, add `--train-filter <field>=<value>` and `--validation-filter <field>=<value>`.
117
+
118
+ Preserve requested hyperparameters. The current CLI uses `--gist-tokens 8|32|64` (default 32), `--epochs 1..5` (default 3), `--learning-rate` within 0.001–0.3 (default 0.03), `--batch-size 8|16|32|64` (default 8), and `--max-sequence-length 64..16384` (default 3072), rather than `regime`. Use defaults when suitable; 8 gist tokens favor compression for simple prompts, while 64 may help when 32 loses holdout accuracy. Choose sequence length from actual examples within the supported limit. `--lora-rank` is rejected for gisting. `training update <name-or-id>` takes the same flags except `--method`; omitted flags retain current values.
119
+
120
+ Verify the saved method, exact source, prompt file, selections, filters, and configuration with `toloka training get <training-id> --json`.
121
+
122
+ ### 5. Validate and estimate
123
+
124
+ ```sh
125
+ toloka training cost <training-id> --json > <absolute-estimate-file>
126
+ ```
127
+
128
+ A blocked estimate exits 1, leaves the redirected file empty, and prints `Training cannot run with these inputs:` followed by one `- <message>` line per issue on stderr. Report every issue, correct the cause, and calculate a new estimate.
129
+
130
+ For a duration issue, use the reported rows, tokens, epochs, GPU count, and wall time to compare supported options. Gisting publishes full model weights, so only `qwen3-4b` and `qwen3-8b` are supported; switching to the larger 8-GPU tiers is not a valid workaround. Reducing rows can weaken coverage and reducing epochs can weaken compression quality. Do not silently change the requested model, discard data, or alter epochs merely to make the estimate pass. Explain the tradeoff, obtain direction when material, update the draft, re-estimate, and obtain approval for the new quote.
131
+
132
+ For a ready estimate, report the source, prompt, and selections, training and validation row counts, token totals, maximum GPU seconds, `maximumCharge`, currency, and fingerprint. If the user asked only for an estimate, stop here.
133
+
134
+ ### 6. Run only after explicit cost approval
135
+
136
+ Ask the user to approve the exact ready `maximumCharge`. After approval, start only the requested run and pass the approved file so a new unseen quote cannot replace it:
137
+
138
+ ```sh
139
+ toloka training run <training-id> \
140
+ --estimate-file <absolute-estimate-file> \
141
+ --version-name <unique-version-name> \
142
+ --json
143
+ ```
144
+
145
+ Both flags are optional at the CLI level. Without `--estimate-file`, `training run` calculates a fresh ready estimate; do not use that path when the user approved a saved quote. Without `--version-name`, the Model Version takes the Training name.
146
+
147
+ Monitor only as far as the user requested, with bounded commands:
148
+
149
+ ```sh
150
+ toloka training status <training-run-id> --json
151
+ toloka training logs <training-run-id>
152
+ toloka training metrics <training-run-id> --json
153
+ ```
154
+
155
+ Resume only after the user confirms a billing block is resolved. Cancel only on explicit request.
156
+
157
+ For a completed run, verify the observed output and download only when requested:
158
+
159
+ ```sh
160
+ toloka training get <training-id> --json
161
+ toloka model get <model-id> --json
162
+ toloka model versions <model-id> --json
163
+ toloka model version download <model-version-id> --out <absolute-output-directory>
164
+ ```
165
+
166
+ The downloaded folder contains full model weights (`config.json` and `model-*.safetensors`) and tokenizer files, including supported text sidecars such as `merges.txt` and `chat_template.jinja`. Keep the files together. The learned prompt is represented by `<|gist_…|>` tokens that prefix requests in place of the original prompt. Verify serving separately with a compatible runtime; a successful download alone does not establish inference readiness.
@@ -26,7 +26,7 @@ The CLI exits 1 with `Potato API request failed with status 500`. The same comma
26
26
 
27
27
  ## Environment
28
28
 
29
- - toloka CLI 0.2.0-preview.3 (`toloka --version`), Bun 1.4.0, macOS 15
29
+ - toloka CLI 0.2.0 (`toloka --version`), Bun 1.4.0, macOS 15
30
30
  - Platform: https://platform.toloka.ai
31
31
 
32
32
  ## Redactions
@@ -1,14 +1,15 @@
1
- import { assert } from "es-toolkit";
2
1
  import { randomUUID } from "node:crypto";
3
2
  import { chmod, readFile, writeFile } from "node:fs/promises";
4
3
  import { dirname, join } from "node:path";
5
4
  import { PostHog } from "posthog-node";
6
- import { PipelineGraphValidationError, PotatoApiError } from "../errors";
5
+ import { PipelineGraphValidationError } from "../errors";
7
6
  import type { TolokaEnvironment } from "./config";
8
7
  import type { CliEnvironment } from "./credentials";
9
8
  import { credentialsPath, preparePrivateFilePath } from "./credentials";
10
9
  import { isFalseyFlag, isTruthyFlag } from "./boolean-flag";
11
10
  import { SDK_VERSION } from "./sdk-version";
11
+ import { assertCli, TolokaCliError } from "./cli-error";
12
+ import { findPotatoApiError } from "./format-error";
12
13
 
13
14
  export const POSTHOG_PROJECT_KEY = "phc_kqHt0f53B5MVqURyzWpW1aNlbOmYt1YbUYza0JPcg19";
14
15
  export const POSTHOG_HOST = "https://eu.posthog.com";
@@ -77,7 +78,9 @@ export function telemetryEnabled(environment: CliEnvironment): boolean {
77
78
  return true;
78
79
  }
79
80
 
80
- const COMMAND_GROUPS: Readonly<Record<string, Readonly<Record<string, true>>>> = {
81
+ type CommandGroup = Readonly<Record<string, true | Readonly<Record<string, true>>>>;
82
+
83
+ const COMMAND_GROUPS: Readonly<Record<string, CommandGroup>> = {
81
84
  project: { list: true, add: true, switch: true, current: true, remove: true, create: true },
82
85
  pipeline: {
83
86
  list: true,
@@ -88,8 +91,31 @@ const COMMAND_GROUPS: Readonly<Record<string, Readonly<Record<string, true>>>> =
88
91
  cancel: true,
89
92
  results: true,
90
93
  },
91
- dataset: { list: true, fields: true, load: true, upload: true },
94
+ dataset: { list: true, create: true, fields: true, load: true, upload: true },
92
95
  "delivery-order": { list: true },
96
+ training: {
97
+ list: true,
98
+ get: true,
99
+ create: true,
100
+ update: true,
101
+ cost: true,
102
+ run: true,
103
+ runs: true,
104
+ status: true,
105
+ resume: true,
106
+ cancel: true,
107
+ metrics: true,
108
+ logs: true,
109
+ },
110
+ "base-model": { list: true, get: true },
111
+ model: {
112
+ list: true,
113
+ get: true,
114
+ versions: true,
115
+ create: true,
116
+ archive: true,
117
+ version: { download: true },
118
+ },
93
119
  secret: { set: true },
94
120
  skill: { list: true, search: true, install: true },
95
121
  config: { get: true, set: true },
@@ -121,9 +147,13 @@ const nameFromTokens = (tokens: readonly string[]): string => {
121
147
  if (subcommands === undefined) return ROOT_COMMANDS[command] === true ? command : "unknown";
122
148
 
123
149
  const subcommand = tokens[1];
124
- return subcommand !== undefined && subcommands[subcommand] === true
125
- ? `${command} ${subcommand}`
126
- : command;
150
+ const nested = subcommand === undefined ? undefined : subcommands[subcommand];
151
+ if (nested === undefined) return command;
152
+ if (nested === true) return `${command} ${subcommand}`;
153
+ const leaf = tokens[2];
154
+ return leaf !== undefined && nested[leaf] === true
155
+ ? `${command} ${subcommand} ${leaf}`
156
+ : `${command} ${subcommand}`;
127
157
  };
128
158
 
129
159
  export function commandNameFromArgv(argv: readonly string[]): string {
@@ -142,19 +172,6 @@ export function commandNameFromArgv(argv: readonly string[]): string {
142
172
  return nameFromTokens(tokens);
143
173
  }
144
174
 
145
- const findPotatoApiError = (error: unknown): PotatoApiError | undefined => {
146
- if (error instanceof PotatoApiError) return error;
147
-
148
- if (error instanceof AggregateError) {
149
- for (const nested of error.errors) {
150
- const apiError = findPotatoApiError(nested);
151
- if (apiError !== undefined) return apiError;
152
- }
153
- }
154
-
155
- return error instanceof Error ? findPotatoApiError(error.cause) : undefined;
156
- };
157
-
158
175
  export function classifyCliError(error: unknown): { readonly error_code: string } {
159
176
  if (error === undefined) return { error_code: "command_failed" };
160
177
  const apiError = findPotatoApiError(error);
@@ -197,7 +214,11 @@ const noopAnalytics = (): CliAnalytics => ({
197
214
  identifyCurrentUser() {},
198
215
  captureCommand() {},
199
216
  async submitIssueReport() {
200
- assert(false, "Issue reporting is disabled. Re-run with TOLOKA_TELEMETRY=1.");
217
+ assertCli(
218
+ false,
219
+ "Issue reporting is disabled. Re-run with TOLOKA_TELEMETRY=1.",
220
+ { hint: { disabled: true } },
221
+ );
201
222
  },
202
223
  async shutdown() {},
203
224
  });
@@ -270,7 +291,9 @@ export function createCliAnalytics(options: {
270
291
  const errorProperties = cliErrorProperties(event.error);
271
292
 
272
293
  if (event.error !== undefined) {
273
- const exception = new Error(`CLI command failed: ${classified.error_code}`);
294
+ const exception = new TolokaCliError(`CLI command failed: ${classified.error_code}`, {
295
+ hint: { disabled: true },
296
+ });
274
297
  exception.name = "PotatoCliError";
275
298
  client.captureException(exception, distinctId, {
276
299
  ...shared,
@@ -316,7 +339,11 @@ export function createCliAnalytics(options: {
316
339
  },
317
340
  submitIssueReport(report) {
318
341
  const submission = queue.then(async () => {
319
- assert(client && distinctId, "PostHog issue reporting could not be initialized.");
342
+ assertCli(
343
+ client && distinctId,
344
+ "PostHog issue reporting could not be initialized.",
345
+ { hint: { description: "Set TOLOKA_TELEMETRY=1 and check that this machine can reach PostHog, then retry." } },
346
+ );
320
347
  deliveryErrors.length = 0;
321
348
  client.capture({
322
349
  distinctId,
package/src/cli/auth.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { assert } from "es-toolkit";
2
1
  import { createClient } from "../client";
3
2
  import { PotatoApiError } from "../errors";
4
3
  import { PRODUCTION_BASE_URL, consoleUrlFor, resolveInternalBaseUrl } from "./config";
@@ -19,6 +18,7 @@ import type {
19
18
  } from "./credentials";
20
19
  import type { ProjectContext } from "./project-context";
21
20
  import { formatProjectContextLine } from "./project-context";
21
+ import { assertCli, TolokaCliError } from "./cli-error";
22
22
 
23
23
  type ProbeFailureKind = "rejected" | "unreachable" | "platform";
24
24
 
@@ -89,7 +89,14 @@ export type AuthProbe = {
89
89
  readonly pagination: { readonly total: number };
90
90
  }>;
91
91
  };
92
- readonly me?: () => Promise<{ readonly internal: boolean }>;
92
+ readonly me?: () => Promise<{
93
+ readonly internal: boolean;
94
+ readonly teams?: ReadonlyArray<{
95
+ readonly id: string;
96
+ readonly writable: boolean;
97
+ readonly internal?: boolean;
98
+ }>;
99
+ }>;
93
100
  };
94
101
 
95
102
  export type LoginOptions = {
@@ -119,14 +126,19 @@ export async function login(options: LoginOptions = {}): Promise<LoginResult> {
119
126
 
120
127
  let apiKey = options.apiKey ?? environment.TOLOKA_API_KEY;
121
128
  if (apiKey === undefined) {
122
- assert(
129
+ assertCli(
123
130
  options.prompt,
124
131
  "Missing API key. Without a terminal, pipe the key to toloka login --api-key-stdin, pass --api-key, or set TOLOKA_API_KEY.",
132
+ { hint: { disabled: true } },
125
133
  );
126
134
  write(`\n${apiKeyInstructions(baseUrl)}\n`);
127
135
  apiKey = (await options.prompt("API key: ", { mask: true })).trim();
128
136
  }
129
- assert(apiKey.length > 0, "No API key entered.");
137
+ assertCli(
138
+ apiKey.length > 0,
139
+ "No API key entered.",
140
+ { hint: { description: "Re-run toloka login and paste the key at the prompt, or pass --api-key." } },
141
+ );
130
142
  const credentials: StoredCredentials = { baseUrl, apiKey };
131
143
  const probe: AuthProbe = options.probe?.(credentials) ?? createClient(credentials);
132
144
 
@@ -136,13 +148,13 @@ export async function login(options: LoginOptions = {}): Promise<LoginResult> {
136
148
  reachable = `Authenticated against ${baseUrl}: ${page.pagination.total} project(s) visible.`;
137
149
  } catch (error) {
138
150
  const failure = classifyProbeFailure(error);
139
- throw new Error(
151
+ throw new TolokaCliError(
140
152
  [
141
153
  `Could not authenticate against ${baseUrl}: ${failure.reason}`,
142
154
  "",
143
155
  `Nothing was saved. ${LOGIN_FAILURE_ADVICE[failure.kind](baseUrl)}`,
144
156
  ].join("\n"),
145
- { cause: error },
157
+ { cause: error, hint: { disabled: true } },
146
158
  );
147
159
  }
148
160
 
@@ -249,13 +261,21 @@ const resolvePlatform = (
249
261
  );
250
262
  };
251
263
 
252
- const internalStatusLine = async (probe: AuthProbe): Promise<string | undefined> => {
253
- if (probe.me === undefined) return undefined;
264
+ const accountLines = async (probe: AuthProbe): Promise<readonly string[]> => {
265
+ if (probe.me === undefined) return [];
254
266
  try {
255
267
  const me = await probe.me();
256
- return `Internal: ${me.internal}`;
268
+ if (me.teams === undefined) return [`Internal: ${me.internal}`];
269
+ const teams = me.teams.map((team) => {
270
+ const qualifiers = [...(team.internal ? ["internal"] : []), ...(team.writable ? [] : ["read-only"])];
271
+ return qualifiers.length === 0 ? team.id : `${team.id} (${qualifiers.join(", ")})`;
272
+ });
273
+ return [
274
+ `Internal: ${me.internal}`,
275
+ `Teams: ${teams.length === 0 ? "(none)" : teams.join(", ")}`,
276
+ ];
257
277
  } catch {
258
- return undefined;
278
+ return [];
259
279
  }
260
280
  };
261
281
 
@@ -306,8 +326,7 @@ export async function authStatus(options: AuthStatusOptions = {}): Promise<AuthS
306
326
  try {
307
327
  const page = await probe.projects.listPage({ limit: 1 });
308
328
  lines.push("", `Reachable: ${page.pagination.total} project(s) visible.`);
309
- const internalLine = await internalStatusLine(probe);
310
- if (internalLine !== undefined) lines.push(internalLine);
329
+ lines.push(...(await accountLines(probe)));
311
330
  return { authenticated: true, output: lines.join("\n") };
312
331
  } catch (error) {
313
332
  const failure = classifyProbeFailure(error);
@@ -17,6 +17,8 @@ import { commandNameFromArgv } from "./analytics";
17
17
  import { readCliSettings } from "./settings";
18
18
  import { CHECK_RESULT_FILE_ENV } from "./check";
19
19
  import { isFalseyFlag, isTruthyFlag } from "./boolean-flag";
20
+ import { TolokaCliError } from "./cli-error";
21
+ import { formatCliError } from "./format-error";
20
22
 
21
23
  export const AUTO_UPDATE_INTERVAL_MS = 24 * 60 * 60 * 1000;
22
24
  export const AUTO_UPDATE_QUARANTINE_MS = 24 * 60 * 60 * 1000;
@@ -199,9 +201,9 @@ const collectOutput = async (stream: NodeJS.ReadableStream | null): Promise<stri
199
201
  return Buffer.concat(chunks).toString("utf8");
200
202
  };
201
203
 
202
- class RegistryError extends Error {
204
+ class RegistryError extends TolokaCliError {
203
205
  constructor(message: string) {
204
- super(message);
206
+ super(message, { hint: { disabled: true } });
205
207
  this.name = "RegistryError";
206
208
  }
207
209
  }
@@ -239,10 +241,16 @@ const defaultInstall: AutoUpdateInstall = async ({
239
241
  const exitCode = close[0];
240
242
  if (exitCode === 0) return;
241
243
  const detail = [stderr.trim(), stdout.trim()].filter((part) => part.length > 0).join("\n");
242
- throw new Error(
244
+ throw new TolokaCliError(
243
245
  detail.length > 0
244
246
  ? `bun add --global failed: ${detail}`
245
247
  : `bun add --global exited ${String(exitCode)}`,
248
+ {
249
+ hint: {
250
+ description:
251
+ "Run the bun add --global command yourself, or turn the check off with toloka config set auto-update false.",
252
+ },
253
+ },
246
254
  );
247
255
  };
248
256
 
@@ -340,9 +348,7 @@ export async function maybeAutoUpdate(options: {
340
348
  environment,
341
349
  });
342
350
  } catch (error) {
343
- io.stderr(
344
- `Auto-update to ${candidate.version} failed: ${error instanceof Error ? error.message : String(error)}.`,
345
- );
351
+ io.stderr(`Auto-update to ${candidate.version} failed: ${formatCliError(error)}`);
346
352
  return continueResult();
347
353
  }
348
354