@toloka-platform/sdk 0.11.2 → 0.11.3

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 (73) hide show
  1. package/dist/cli/toloka.js +157 -154
  2. package/dist/cli/toloka.js.jsc +0 -0
  3. package/dist/cli/toloka.js.jsc.sha256 +1 -1
  4. package/dist/cli/toloka.js.sha256 +1 -1
  5. package/docs/authoring.md +6 -4
  6. package/docs/cli.md +30 -11
  7. package/docs/limitations.md +2 -2
  8. package/docs/testing.md +4 -2
  9. package/examples/README.md +11 -0
  10. package/examples/app-crowdtesting/README.md +41 -0
  11. package/examples/app-crowdtesting/fields.ts +6 -0
  12. package/examples/app-crowdtesting/nodes/app-flow-test/node.ts +381 -0
  13. package/examples/app-crowdtesting/nodes/collection/node.ts +8 -0
  14. package/examples/app-crowdtesting/pipeline.browser.integration.spec.ts +77 -0
  15. package/examples/app-crowdtesting/pipeline.ts +17 -0
  16. package/examples/egocentric-video-collection/README.md +35 -0
  17. package/examples/egocentric-video-collection/fields.ts +24 -0
  18. package/examples/egocentric-video-collection/nodes/collection/node.ts +5 -0
  19. package/examples/egocentric-video-collection/nodes/record-activity/node.ts +760 -0
  20. package/examples/egocentric-video-collection/pipeline.browser.integration.spec.ts +65 -0
  21. package/examples/egocentric-video-collection/pipeline.ts +17 -0
  22. package/examples/product-moderation/README.md +49 -0
  23. package/examples/product-moderation/fields.ts +37 -0
  24. package/examples/product-moderation/nodes/collection/node.ts +16 -0
  25. package/examples/product-moderation/nodes/moderate-product/instruction.ts +77 -0
  26. package/examples/product-moderation/nodes/moderate-product/node.ts +368 -0
  27. package/examples/product-moderation/pipeline.browser.integration.spec.ts +48 -0
  28. package/examples/product-moderation/pipeline.ts +33 -0
  29. package/examples/video-pair-rating/fields.ts +16 -4
  30. package/examples/video-pair-rating/nodes/adjudicate/node.ts +78 -3
  31. package/examples/video-pair-rating/nodes/adjudicate_op/node.ts +28 -3
  32. package/examples/video-pair-rating/nodes/overall_preference_pick/node.ts +28 -3
  33. package/examples/video-pair-rating/nodes/rate_pair/node.ts +65 -2
  34. package/openapi/gen/api.ts +22 -0
  35. package/openapi/gen/models/generationBudget.ts +8 -0
  36. package/openapi/gen/models/generationBudgetReasoningEffort.ts +2 -0
  37. package/openapi/gen/models/generationModel.ts +12 -0
  38. package/openapi/gen/models/generationModelDefaultReasoningEffort.ts +2 -0
  39. package/openapi/gen/models/generationModelId.ts +2 -0
  40. package/openapi/gen/models/index.ts +8 -0
  41. package/openapi/gen/models/invalidFieldSchema.ts +6 -0
  42. package/openapi/gen/models/listGenerationModels200.ts +7 -0
  43. package/openapi/gen/models/listGenerationModels200DefaultGenerationModelId.ts +2 -0
  44. package/openapi/gen/models/pipelineGraphSnapshot.ts +2 -0
  45. package/package.json +2 -2
  46. package/skills/catalog.json +16 -12
  47. package/skills/toloka-build/SKILL.md +44 -58
  48. package/skills/toloka-build/references/human-work.md +156 -0
  49. package/skills/toloka-build/references/local-setup.md +136 -0
  50. package/skills/toloka-build/references/node-checks.md +11 -0
  51. package/skills/toloka-build/references/node-semantics.md +22 -213
  52. package/skills/toloka-build/references/task-ui.md +61 -0
  53. package/skills/toloka-build/references/workflow-and-cli.md +29 -229
  54. package/skills/toloka-fine-tuning/SKILL.md +1 -1
  55. package/skills/toloka-get-started/SKILL.md +1 -1
  56. package/skills/toloka-gisting/SKILL.md +1 -1
  57. package/src/authoring/index.ts +26 -7
  58. package/src/checks/task-ui-typecheck.ts +3 -3
  59. package/src/cli/analytics.ts +1 -0
  60. package/src/cli/check.ts +83 -12
  61. package/src/cli/commands/generation-model.ts +18 -0
  62. package/src/cli/commands/pipeline.ts +1 -1
  63. package/src/cli/commands/source.ts +14 -3
  64. package/src/cli/generation-models.ts +46 -0
  65. package/src/cli/program.ts +2 -0
  66. package/src/cli/project.ts +288 -138
  67. package/src/cli/trainings.ts +12 -0
  68. package/src/cli/ui.ts +3 -1
  69. package/src/client.ts +5 -0
  70. package/src/graph-rules.json +11 -0
  71. package/src/importer.ts +46 -40
  72. package/src/index.ts +4 -0
  73. package/src/validate.ts +183 -0
@@ -0,0 +1,136 @@
1
+ # Local setup
2
+
3
+ Read this file to prepare a machine or a checkout: installing the CLI and SDK, signing in, choosing or creating the platform project, and binding a source root to it. A checkout with a correct `toloka.json` and working credentials does not need it.
4
+
5
+ ## Install and initialize
6
+
7
+ For a first-time guided session, consult `https://platform.toloka.ai/agents/data-labeling.md` for orientation. Adapt the journey to the user's goal and already-complete setup while preserving dependencies, cost approval, and identity/source protection; use this reference for command details.
8
+
9
+ Bun is the recommended runtime and package manager for pipeline authoring. In an existing source root, install its declared dependencies:
10
+
11
+ ```sh
12
+ bun i
13
+ ```
14
+
15
+ For a new pipeline project, add the SDK, TypeScript, and Vitest:
16
+
17
+ ```sh
18
+ bun add -d @toloka-platform/sdk@latest typescript vitest
19
+ ```
20
+
21
+ These are project authoring and test dependencies, not prerequisites for running the standalone CLI or a Training-only workflow. Task UI scenarios additionally require Playwright and Chromium:
22
+
23
+ ```sh
24
+ bun add -d playwright
25
+ bunx playwright install chromium
26
+ ```
27
+
28
+ Select or create the intended platform project before `toloka init` when the checkout should be bound immediately; follow [Choose the platform project](#choose-the-platform-project). Running `init` without explicit or checkout-local project identity installs only the agent entry points and never adopts the machine registry's current project.
29
+
30
+ `toloka init` warns when `@toloka-platform/sdk` is not declared in `dependencies` or `devDependencies`, then maintains one `<toloka>` Agent Bootstrap block in `AGENTS.md` and writes minimal pointers to it: a Claude skill at `.claude/skills/toloka-bootstrap/SKILL.md` when `.claude/` exists (or when no agent configuration exists at all) and an always-applied Cursor rule at `.cursor/rules/toloka-bootstrap.mdc` when `.cursor/rules/` exists. The dependency warning applies to pipeline authoring; Training commands do not require the SDK dependency. `init` writes `toloka.json` only from an explicit project flag/environment value or an existing checkout binding.
31
+
32
+ The bootstrap is small: it tells an agent to run `toloka --help`, search the bundled Skill Catalog with `toloka skill search <query>`, install the match with `toloka skill install <skill-id>`, and read `.agents/skills/<skill-id>/SKILL.md`. This skill is that installed copy. Commands warn on stderr when a generated legacy entry point is still present or the recorded bootstrap version differs from the running CLI's bundled version, independently of any project SDK dependency. Help, `init`, `report`, `project url`, `config`, and version invocations skip this checkout-dependent warning. The warning never blocks a command; after updating the standalone CLI, re-run `toloka init` to refresh the bootstrap and its pointers.
33
+
34
+ ## Sign in
35
+
36
+ ```sh
37
+ toloka login
38
+ ```
39
+
40
+ It asks whether to sign in through the browser or with an API key. The browser choice opens a page in
41
+ the browser where the user continues with Google or Microsoft (testing environments also offer email
42
+ and password and the testing users), hands them to the Toloka login page, waits for the sign-in to finish, verifies access by
43
+ listing one project, and only then saves the browser session under `~/.toloka/cli/credentials/` with
44
+ mode `0600`. Nothing is written when the platform does not accept the account. The session renews
45
+ itself for up to 14 days; after that `toloka login` is needed again. Run `toloka login --browser` yourself
46
+ by default: it skips the question, opens the sign-in page in the user's browser, and waits for them to
47
+ finish; without a terminal it fails fast when no browser can be opened. `toloka login --no-browser` prints
48
+ the link instead of opening a browser.
49
+
50
+ The API key choice does not expire: the key works until it is deleted in the Business Console, which
51
+ suits scripts, CI, and coding agents. Type it at the masked prompt, or pipe it to `toloka login --api-key-stdin`.
52
+ `toloka login --api-key <key>` also works but leaves the key in shell history. Most later commands read the stored login, so
53
+ `--api-key` becomes optional. For networked commands that do not mutate the machine project registry,
54
+ credential precedence is flag, then `TOLOKA_SCOPED_TOKEN`, then `TOLOKA_API_KEY`, then the stored API key,
55
+ then the browser session; CI can use an environment variable without running login. A scoped token is
56
+ minted by the platform for one user and one project; it never grants more than that user has, cannot issue further tokens, and expires on its own. The base URL defaults to the platform.
57
+
58
+ ```sh
59
+ toloka auth-status # or toloka whoami
60
+ toloka logout
61
+ ```
62
+
63
+ `auth-status` reports which platform and login are in effect and where each came from and exits
64
+ non-zero on failure — use it to check a setup rather than guessing. It masks the credential; `--show-token`
65
+ prints the full API key or the browser session's live access token, so pass it only when a command
66
+ genuinely needs the raw value and never paste the output into chat. Its last lines say which failure it was: `Rejected:` means the platform refused the login
67
+ (run `toloka login` again); `Not reachable:` means the platform did not answer, so the login was not
68
+ evaluated (fix network access, do not ask for a new login); `Platform error:` means the platform
69
+ failed (retry later). `logout` ends every stored browser session and deletes the stored files; it does not
70
+ revoke an API key.
71
+
72
+ ## Configure the CLI
73
+
74
+ Provide the credential and base URL on networked workflows through `TOLOKA_SCOPED_TOKEN` or
75
+ `TOLOKA_API_KEY` and `TOLOKA_BASE_URL`, or with `--api-key` and `--base-url`. A base URL with a path (a gateway such as
76
+ `https://gateway.example/ss/pp.1`) keeps that path on every request, with or without a trailing
77
+ slash; use the given URL unchanged. The registry-mutating commands
78
+ `toloka project add`, `toloka project switch`, `toloka project remove`, and `toloka project create`
79
+ require `toloka login` first because they read and write the credentials file. When flags or
80
+ `TOLOKA_*` variables are also present, they must identify the same stored key and platform. Resolve
81
+ project ID in this order: `--project-id`, `TOLOKA_PROJECT_ID`, `toloka.json`,
82
+ then the machine registry `current`.
83
+
84
+ Standalone and Bun-global `toloka` installations check the public npm registry at most once per day and install eligible releases at least 24 hours old before rerunning the command. Project-local installs, CI, and `config`, `version`, and `help` skip the check. Turn it off with `toloka config set auto-update false`. For manual standalone updates, rerun `curl -fsSL https://platform.toloka.ai/cli/install.sh | sh -s -- --native`; Bun-global updates should use `--bun` instead. Preserve the mode and put any original `TOLOKA_CLI_INSTALL_DIR`, `TOLOKA_CLI_CHANNEL`, `TOLOKA_CLI_PACKAGE`, and `TOLOKA_CLI_REGISTRY` overrides on the `sh` side of the pipe again. A package version pin does not disable automatic updates, which always use the public npm registry.
85
+
86
+ Anonymous command telemetry is on by default (`sdk_cli_command_completed` / `sdk_cli_command_failed`). Failed commands with exceptions also emit a sanitized PostHog Error Tracking `$exception`; API failures include the status code and server request ID when available. Raw arguments, messages, stacks, response bodies, and paths are excluded. `toloka report` uses the same PostHog client to send explicitly approved Markdown in `sdk_cli_issue_report_submitted`. Set `TOLOKA_TELEMETRY=0` or `DO_NOT_TRACK=1` to disable all capture; CI is off unless `TOLOKA_TELEMETRY=1`.
87
+
88
+ ## Choose the platform project
89
+
90
+ Resolve the intended platform project before `import`, `plan`, or `apply`; use an existing correct binding rather than registering or creating a project again. For `init`, `toloka --project-id <id> init` and `TOLOKA_PROJECT_ID` bind an empty checkout directly without changing the machine registry. `project create` also writes `toloka.json`; `project add` changes only the machine registry, so follow it with `import <projectId>` for an existing project or an explicit project ID on `init` for an empty one. Confirm `toloka project current` and `toloka.json` resolve to the intended ID before authoring.
91
+
92
+ When the checkout has no `toloka.json`:
93
+
94
+ - If it is absent and the user is adopting an existing project, run `toloka project add <alias> <projectId>`, then run `toloka import <projectId>` in the intended source root before `toloka init`. Import refuses an existing `pipelines/` directory unless you pass `--force`.
95
+ - If it is absent and the user is authoring from scratch, create a project with a task-derived name when a new project is needed. For an intended existing empty project, use `toloka project add <alias> <projectId>` and `toloka --project-id <projectId> init`. Confirm `toloka project current` and the generated `toloka.json` name the intended project before authoring; do not create a duplicate merely because the checkout is unbound.
96
+
97
+ If `toloka project current` reports `from toloka.json` for a project you did not select in this checkout, stop: project create/add/switch does not rebind an existing checkout. A newly generated state file with empty `pipelines` and `datasets` can be removed before selecting the intended project and rerunning `toloka init`; any state file with live bindings may be someone else's checkout and must not be deleted without user approval.
98
+
99
+ An unverified trial account holds one project and one pipeline, and cannot deploy or run. `project create` then fails with `Your trial allows a single project.` and pipeline creation with `Your trial allows a single pipeline.` Adopt the project that already exists instead: register it with `toloka project add <alias> <projectId>`, bind the checkout with `toloka import <projectId>`, and author inside its existing pipeline; the platform has no project delete, so there is nothing to clear. Both limits lift when the user completes identity or business verification from the banner in the platform.
100
+
101
+ `project create`, `project add`, and `project switch` also change the machine-level current-project registry used outside a source root. For temporary or evaluation projects, record the previous alias and restore it before finishing. Without `--team-id`, create uses the account's only writable team; when `toloka whoami` lists several teams, ask the user which one owns the project and pass it explicitly. Confirm the new project's team in `toloka project url`.
102
+
103
+ `toloka delivery-order list` and `project create --delivery-order-id` are always available; the platform accepts them only for internal teams. When `toloka whoami` marks the project's owning team `(internal)` on its `Teams:` line, list active Sunday orders first; if the list is empty, stop. Pass a selected id to `project create` — internal teams require it. For a project in an external team, omit the flag; the platform rejects an order there.
104
+
105
+ `toloka project url` prints only the selected project's platform UI URL. Use it when the user needs a working link to inspect the live project.
106
+
107
+ ### Adopt an existing live project
108
+
109
+ 1. Install `@toloka-platform/sdk` in the intended source root.
110
+ 2. Run `toloka project add <alias> <projectId>`, or `toloka project switch <alias>` when the project is already registered.
111
+ 3. Run `toloka import` into the empty source root.
112
+ 4. Run `toloka init` to install or refresh the agent entry points without rebinding project identity.
113
+ 5. Confirm `toloka.json.projectId` equals `toloka project current`, then run `toloka plan` before editing and require `No changes.`. A non-empty baseline is importer or live-state drift to understand separately from your change.
114
+ 6. Inspect every generated `pipelines/**/pipeline.ts` dependency, preserve constructor `id` options that bind source to the existing graph, and make the smallest source change.
115
+ 7. Run `toloka check` and `toloka validate`, then plan, apply, and plan again. Never copy imported IDs to unrelated declarations or invent replacements.
116
+
117
+ Import refuses an existing `pipelines/` directory. Pass `--force` to replace it with the live graph as TypeScript. Never use `--force` as a harmless refresh over uncommitted source work.
118
+
119
+ ### Start from a new or empty project
120
+
121
+ 1. Run `toloka project create <alias>` for a new project, or `toloka project add <alias> <projectId>` for an existing empty project.
122
+ 2. Run `toloka init` after creation. For an existing empty project, run `toloka --project-id <projectId> init`. Then require `toloka project current` and `toloka.json` to name the same project.
123
+
124
+ Then author the pipeline and follow the reconciliation loop in SKILL.md.
125
+
126
+ ## Work on more than one project
127
+
128
+ `toloka.json` records exactly one `projectId`, so one checkout targets one project. To drive a second project from the same source — a copy for another customer, or a staging and a production instance — do not overwrite the state file in place. Use a separate checkout, or pass `--project-id` explicitly and keep the state files apart. Overwriting the recorded id silently repoints every later `plan` and `apply` at the wrong project.
129
+
130
+ Create a second live project with `toloka project create <alias>` or register an existing one with `toloka project add <alias> <projectId>`. After a copy, prove the original is untouched by planning against it and seeing no changes.
131
+
132
+ ## Hand out a project-scoped token
133
+
134
+ `toloka project token` mints a token that acts as the signed-in user inside the selected project only (`--projects` adds more projects of the same team), with the permissions of `--preset` (default `write`) or an explicit `--permissions` list, for at most six hours. It is printed once and never stored; pass it to another agent or script as `TOLOKA_SCOPED_TOKEN`, as the command says. `toloka project tokens` lists the live tokens of the project; `toloka project revoke-token <tokenId>` ends one and `toloka project revoke-tokens` ends every live token of the project at once, for example when a run finished or a token leaked. One user may hold at most 1000 live tokens per project; when `token` fails with `429 … revoke unused tokens first`, run `revoke-tokens`.
135
+
136
+ A scoped token downloads project files in two steps: a request to `ProjectFile.url` with the token answers `200 {"url": "<storage URL>"}` instead of the redirect a human credential gets. Fetch that `url` as a plain request without the `X-Scoped-Token` header; HTTP clients replay custom headers across redirects, and the token must never reach the storage host.
@@ -0,0 +1,11 @@
1
+ # Node checks
2
+
3
+ Checks are platform-side tests of a node, listed on the node's Checks tab in the platform UI. They are separate from `toloka check`, which runs your local tests. A node's `checks` setting is a record keyed by check type, and each key holds an array of stored test cases:
4
+
5
+ - `annotation_qa` on every `generation` and `quorum` node, human or synthetic. Each case is `{ id, name, input: { taskInput, taskOutput } }`: `taskInput` is a JSON string of the node's input fields by field name, and `taskOutput` is a JSON string of the expected output fields by field name (on a `quorum` node each output value is an array, one element per annotation).
6
+ - `synthgen` on synthetic `generation` nodes, in addition to `annotation_qa`. Each case is `{ id, name, input: { input } }`, where `input` is a JSON string of the node's input fields by field name; running it makes one trial generation.
7
+ - `code` on `code` nodes. Each case is `{ id, name, input: { input } }`, where `input` is a JSON string of the node's input fields by field name.
8
+
9
+ Fill every test check type the node offers: a type left without cases makes `toloka validate` warn `empty_node_checks`. Give each case a short unique slug as `id` and a `name` that says what it covers. Build cases from real dataset rows: read them from the file you upload or from `toloka dataset load <name> --out <file> --limit <n>`, and copy their values as they are, even when you generated the dataset yourself. Never invent names, IDs, URLs, or texts for a case. Add a failing or edge case only by changing a real row the way an expert or a bad input would. Keep the set small: one case per distinct behavior, at most about ten.
10
+
11
+ The other keys are not yours to fill. `qc_lint`, `ui_types_lint`, and `exam_dataset` are lints the platform manages and runs after every apply; leave them out of source. `ui` and `llmqa` are retired check types: the Checks tab ignores them, so never put cases there, and keep any old cases `toloka import` brought in. `toloka import` leaves out every check type with no cases. A check type you leave out of source is not managed by it: `toloka plan` ignores it and `toloka apply` keeps its platform cases, including cases added in the UI. To remove every case of a type, set it to an empty array. Running a check is a platform operation with no `toloka` command: after you apply new cases, ask the user to run them from the node's Checks tab. Until they run, `toloka validate` reports each case as `not_ran_node_check`.
@@ -2,15 +2,18 @@
2
2
 
3
3
  Use this reference to decide the smallest correct graph, then edit the node's TypeScript source and reconcile it with `toloka plan` and `toloka apply`. Configure only properties accepted by the exported authoring types; do not translate an internal platform operation into a made-up SDK method.
4
4
 
5
+ It covers the graph, ports, and the `root`, `code`, and `match` nodes. Read [human work](human-work.md) before writing or changing a `generation` or `quorum` node, and [node checks](node-checks.md) before writing a node's stored test cases.
6
+
5
7
  ## Decide the graph before configuring it
6
8
 
7
9
  Choose by behavior, not by the data format:
8
10
 
9
- - Choose `root` once, to bind dataset columns into and out of the pipeline.
10
- - Choose `code` for deterministic transforms, filtering, parsing, math, string work, scripts, HTTP calls, and programmatic LLM calls such as classification, summarization, or extraction.
11
- - Choose `generation` when a person or synthetic generator produces one annotation: image, video, or audio annotation; text writing or rewriting; ranking, rating, comparison, validation, correction, or original-content collection.
12
- - Choose `quorum` when the same item must go independently to a fixed number of human labelers and the collected disagreement is part of the result. Typical tasks are classification, rating, and short labels.
13
- - Choose `match` for if/then/else routing by predicates. Do not use it for parallel fan-out.
11
+ - `root`, exactly once, binds dataset columns into and out of the pipeline.
12
+ - `code` computes a result from inputs without a human task: deterministic transforms, ETL, filtering, parsing, arithmetic, string work, scripts, HTTP APIs, and programmatic LLM calls such as classification, summarization, or extraction. An LLM call made programmatically is still `code`; choose `generation` only when the platform's human or synthetic generation journey is wanted.
13
+ - `generation` is one annotation produced by a person or a synthetic generator: image, video, or audio annotation; text writing or rewriting; ranking, rating, comparison, validation, correction, or original-content collection.
14
+ - `quorum` sends the same item independently to a fixed number of human labelers when the collected disagreement is part of the result. Classification, rating, and short labels are the common fit. Use `generation` for one annotation, and do not approximate adaptive consensus by chaining human nodes or routing with `match`.
15
+ - `match` routes one item down one conditional path by predicates. Do not use it for parallel fan-out, loops, review cycles, adaptive quorum, or consensus aggregation.
16
+ - A downstream `code` node after `quorum` derives a consensus value, such as a majority vote, an average, or deduplication, when the deliverable needs one; quorum itself returns the raw collected annotations. Choose an aggregation rule grounded in the task, and clarify material ambiguity rather than inventing label precedence.
14
17
 
15
18
  **Cycles live inside components, not in the graph.** Keep the authored graph acyclic. “Add a QA step,” “add a review loop,” and “add a redo” mean configure the owning `generation` node. Its QA, sampling, redo, appeal, and payment-retry cycles are internal behavior, not downstream nodes. Post-generation graph work is either deterministic `code` or predicate routing with `match`.
16
19
 
@@ -20,15 +23,13 @@ Choose by behavior, not by the data format:
20
23
 
21
24
  Use the typed node properties; never write handle strings yourself:
22
25
 
23
- | Node | Input | Output |
24
- | ------------ | ---------------------- | ---------------------------------------------------------- |
25
- | `root` | `collection.completed` | `collection.started` |
26
- | `code` | `transform.input` | `transform.output` |
27
- | `generation` | `annotate.input` | `annotate.output` |
28
- | `quorum` | `rate.input` | `rate.output` |
29
- | `match` | `route.input` | `route.matched(0)`, `route.matched(1)`, …, `route.default` |
26
+ | Node | Input port | Output ports |
27
+ | ------------------------------ | ----------- | ----------------------------------- |
28
+ | `root` | `completed` | `started` |
29
+ | `code`, `generation`, `quorum` | `input` | `output` |
30
+ | `match` | `input` | `matched(0)`, `matched(1)`, …, `default` |
30
31
 
31
- Connect them in the direction of data flow:
32
+ Connect them in the direction of data flow, here with nodes bound to `collection` (root), `transform` (code), `route` (match), and `annotate` (generation):
32
33
 
33
34
  ```ts
34
35
  connect(collection.started, transform.input);
@@ -41,8 +42,6 @@ connect(annotate.output, collection.completed);
41
42
 
42
43
  ## `root`
43
44
 
44
- ### Pick and configure it
45
-
46
45
  Create exactly one `root` node with `root(settings)`. It has no name argument because it represents the pipeline boundary, not a named unit of work. Use `started` for dataset fields the pipeline reads and `completed` for fields it writes back to the same dataset. A completed field is not a separate output destination: the platform creates or uses that dataset column per item.
47
46
 
48
47
  The public authoring shape is `RootAuthoringSettings`:
@@ -54,24 +53,12 @@ The public authoring shape is `RootAuthoringSettings`:
54
53
 
55
54
  The dataset name belongs in `definePipeline({ dataset: 'name', ... })`, not in the root settings. `toloka apply` resolves or creates the named dataset and supplies its ID.
56
55
 
57
- ### Traps
58
-
59
- - Do not author `datasetId` or `itemFilter`; `RootAuthoringSettings` omits both. `toloka apply` binds the dataset and preserves the live `itemFilter` and `itemLimit` unless source declares its own `itemLimit`. Do not add a limit to source to restore one you saw in the UI.
60
-
56
+ - Do not author `datasetId` or `itemFilter`; `RootAuthoringSettings` omits both. `toloka apply` binds the dataset and preserves the live `itemFilter`, and a live `itemLimit` unless source declares its own. Do not add a limit to source to restore one you saw in the UI. Project rename and item-filter authoring have no source path.
61
57
  - `completed` may contain any declared field carried on the incoming branch, whether it originated at root input or an upstream output. Never list a field no upstream node provides. Until a run produces results, write-back of a pass-through input is unobserved and must be reported that way.
62
58
  - Declare every referenced `Field` in `definePipeline({ fields })`.
63
- - Dataset fields are immutable after creation. You may add a column, never alter or remove one. On a schema conflict, use a new column name or a new dataset.
64
59
 
65
60
  ## `code`
66
61
 
67
- ### Pick it
68
-
69
- Use `code` whenever the result can be computed from inputs without a human task. This includes deterministic transforms, ETL, filtering, parsing, arithmetic, string manipulation, scripts, HTTP APIs, and LLM API calls. An LLM call made programmatically is still `code`; choose `generation` only when the platform's human or synthetic generation journey is wanted.
70
-
71
- Use a downstream `code` node after `quorum` when the requested deliverable needs a derived consensus value such as majority vote, average, or deduplication. Quorum itself returns raw collected annotations. Choose an aggregation rule grounded in the task, and clarify material ambiguity rather than inventing label precedence.
72
-
73
- ### Configure it
74
-
75
62
  Set `CodeAuthoringSettings`, derived from `CodeNodeSettings`, through `code(name, settings)`:
76
63
 
77
64
  - `about`: node `name`, optional `description`, and optional `projectType`.
@@ -80,7 +67,7 @@ Set `CodeAuthoringSettings`, derived from `CodeNodeSettings`, through `code(name
80
67
  - `envVars`: optional static values or secret bindings:
81
68
  - `{ type: 'static', name, value }`
82
69
  - `{ type: 'secret', name, secretName }`
83
- - `checks`: optional source-held code check cases grouped by check type.
70
+ - `checks`: optional stored test cases for the node's Checks; see [node checks](node-checks.md).
84
71
 
85
72
  Write Python with this exact entry contract:
86
73
 
@@ -91,9 +78,11 @@ async def main(inputs: Inputs) -> Outputs:
91
78
  return {"normalized": inputs["raw_text"].strip()}
92
79
  ```
93
80
 
94
- The sandbox generates `main_types.py` from the bound fields. Read and return keys by `field()` name, not by minted field ID. Return every required output key; an optional output may be omitted.
81
+ The platform generates `main_types.py` from the bound fields when it runs the node. Read and return keys by `field()` name, not by minted field ID. Return every required output key; an optional output may be omitted.
82
+
83
+ The platform runs code nodes on Python 3.12 with the standard library only, and a node cannot install packages. Call HTTP and LLM APIs with `urllib.request` or another standard module, not a vendor SDK. `test.code` runs your local Python, so an import of a package that happens to be installed locally passes `toloka check` and still fails on the platform.
95
84
 
96
- For third-party SDKs that read the environment implicitly, bind every needed variable in `envVars`. Reference project secrets by name and set their values separately with `toloka secret set <name>`; use `os.environ['NAME']` in Python. The sandbox has no ambient credentials.
85
+ Bind every environment variable the code reads in `envVars`. Reference project secrets by name and set their values separately with `toloka secret set <name>`; use `os.environ['NAME']` in Python. The node's runtime has no ambient credentials.
97
86
 
98
87
  ### Traps
99
88
 
@@ -101,175 +90,15 @@ For third-party SDKs that read the environment implicitly, bind every needed var
101
90
  - Do not omit `async`, the single `inputs` parameter, `Inputs`/`Outputs` annotations, or the `main_types` import.
102
91
  - Do not return field IDs or arbitrary keys. Output keys must match output field names.
103
92
  - Do not put secret values in source or static environment entries.
104
- - Keep dependencies light unless the task requires one; the standard library is available.
105
-
106
- ## `generation`
107
-
108
- ### Pick it
109
-
110
- Use `generation` for a single human-in-the-loop task type:
111
-
112
- - annotating images, video, or audio with boxes, labels, or captions;
113
- - writing or rewriting text;
114
- - ranking, rating, or comparing outputs;
115
- - validating or correcting model output with a human judge;
116
- - collecting original video, audio, photos, files, or other content from experts.
117
-
118
- Use `about.projectType: 'general'` when experts act on existing items: annotation, classification, judgment, ranking, rating, comparison, or rewriting.
119
-
120
- Use `about.projectType: 'data_collection'` when experts produce new original content instead of labeling an existing item. Build the UI with the corresponding upload or recording component.
121
-
122
- ### Configure it
123
-
124
- `generation(name, settings)` accepts the public `GenerationAuthoringSettings`, derived from `GenerationNodeSettings`:
125
-
126
- - `about`: optional `name`, `description`, and `projectType`. The constructor uses its first argument as `about.name` when omitted.
127
- - `publicName`: brand-neutral, human-readable title shown to experts.
128
- - `entities.input` and `.output`: map declared fields into the labeler data model.
129
- - `qualityCriteria`: semantic pass/fail rubrics targeting output `Field` objects.
130
- - `instruction`: English labeler guidance reflecting the actual entities, UI, and criteria.
131
- - `ui`: `{ code, data, preprocess, postprocess?, constants }` for the task interface.
132
- - `qualityAssurance`: `llm_only`, `hybrid`, or `human_only`.
133
- - `audienceAndPricing`: either a human audience or synthetic generation.
134
- - `onboarding`: built-in Training stage.
135
- - `exam`: optional built-in qualification gate.
136
- - `reuseAudienceWithNodeId`, `smartSearch`, and `checks`: source-representable advanced settings.
137
-
138
- #### Entities
139
-
140
- An authored entity references a declared `field`. Choose its cardinality from the task's meaning before choosing the field schema or Task UI component:
141
-
142
- - One logical result from one labeler is static. A caption, verdict, rating, recording, or upload remains one entity even when its value is an object or a component holds it in an array. Omit `dynamic` and `maxEntities`, and use a `jsonPath` without `[*]`.
143
- - Multiple independent results from one labeler are dynamic. Examples include one box per visible face and one defect record per discovered defect. Set `dynamic: true`, use exactly one `[*]` in `jsonPath`, and set `maxEntities` to the tightest upper bound justified by the task. Never use 50 as an unexamined default.
144
- - Do not choose `dynamic` from a TypeScript or JSON container shape. Ask whether the task expects one logical result or a repeated collection; the answer owns the entity model.
145
- - Use `allowedLabels: [{ label, value }]` for fixed choices. Use `SINGLE_LABEL` for exactly one stored value and `MULTI_LABEL` for multiple values. Every `SINGLE_LABEL`/`MULTI_LABEL` output needs at least two entries, and each `value` must be snake_case.
146
- - Set media duration bounds only when the expert produces the media through an upload or recording output. Input media used for annotation does not require duration bounds.
147
- - Give every output entity at least one relation to the input it uses: `{ field, rel_type: 'context' | 'references' | 'depends_on' | 'evaluates' }`, or a conditional relation supported by the public type.
148
-
149
- `entityType` is a closed platform vocabulary even where a generated TypeScript shape is currently broad. Use `CONTEXT` for any non-media input the expert reads, including text, numbers, and objects. Use `SHORT_ANSWER` for short produced text, `RESPONSE` for long produced text, and `EXPLANATION` only for reasoning about another value. Use `SINGLE_LABEL` or `MULTI_LABEL` for fixed choices and the matching media or annotation type for those values. Never invent `TEXT` or another natural-language type name; consult the documented vocabulary when these categories do not fit. The full vocabulary, generated from the server schema into the SDK's `core-entity-types.json`, is `JUDGEMENT`, `EXPLANATION`, `PROMPT`, `USER_PROMPT`, `RESPONSE`, `RESPONSE_EVALUATION`, `RUBRIC`, `RUBRICS_GROUP`, `DIALOG`, `DIALOG_STEP`, `DIALOG_EVALUATION`, `SHORT_ANSWER`, `FEEDBACK`, `CONTEXT`, `IMAGE`, `ANNOTATION`, `SINGLE_LABEL`, `MULTI_LABEL`, `VIDEO`, `DOCUMENT`, `GOLDEN_SET`, `LOGS`, `SOLUTION`, `CODE`, `METADATA`, `AUDIO`, `AGENT_TRAJECTORY`, `MCP_SERVER`, `CONTAINER`, `CUSTOM`. The SDK validates `entityType` and the `allowedLabels` rules when it loads the source, so `toloka check` fails locally with `PipelineGraphValidationError` before anything is sent to the platform.
150
-
151
- Entities enforce format, not meaning. Put types, ranges, cardinality, requiredness, and labels in the field schema and entity. Put accuracy, correctness, completeness, and clarity in quality criteria.
152
-
153
- For a single-item upload or recording control, the component value is still an array, but the task produces one logical entity. Declare a scalar output field, keep the entity static, and use a non-wildcard path to the completed first value, such as `$.video_file[0].downloadLink`. Do not model the component's array container as `dynamic: true`. Use the matching media or document entity type. For video, also set both entity duration bounds and align them with the UI component.
154
-
155
- #### Quality and instructions
156
-
157
- Write at least one criterion for every output entity. Each criterion needs an authored UUID `id`, `qualityElement`, mutually exclusive and exhaustive `thresholds.pass` and `thresholds.fail`, and `targetEntity: outputField`. Every criterion must resolve to pass or fail for every item; express edge cases as conditional clauses inside an always-applicable criterion. Criteria judge meaning, not an exact phrase you authored unless the request fixes that phrase. Before apply, walk every sample row through the instruction and all criteria: each criterion must yield one verdict, and no two authored rules may demand different values for the same row. Invert the task into an observable check: “tag every car” becomes “every visible car has exactly one tag.” Keep format validation out of criteria.
158
-
159
- Use `llmQaInstruction` only when the evaluator needs a specific verification method, authoritative source, tool, or pinned edge case beyond the thresholds. It is evaluator guidance, not labeler guidance, and has no effect in `human_only` mode.
160
-
161
- Default QA is `qualityAssurance: { qaMode: 'llm_only' }`. Keep it when it fits the task. `hybrid` adds sampled Human QA while retaining LLM review; `human_only` is appropriate when LLM grading is prohibited or human review alone is required. Recommend a mode based on the user's quality and privacy requirements, explaining cost and turnaround implications before changing it. Human-bearing modes require `humanQaTimeForTask` of at least 10 seconds; `smartSamplingPercent` defaults to 30 for hybrid and 100 for human-only. Use those defaults unless the task justifies a different rate. Human QA on a node is capped at the node's expected review count from the frozen estimate, which uses the items the estimate expects at that node. Behind a `match`, the estimate assumes an even split across branches, so a branch that receives more items reaches the cap early and its later items skip Human QA; say so in the cost forecast when branch volumes are known to be uneven. QA mode applies to the whole node, not individual criteria. The redo cap is platform-controlled and is not an SDK setting.
162
-
163
- The platform also writes annotator fields on human-expert generation nodes. `<snake_cased node name>_annotator_id` is an optional string: the ID of the expert whose answer was accepted, not of earlier rejected attempts. `<snake_cased node name>_qa_annotator_id` exists only in `human_only` and `hybrid` modes: the ID of the expert who made the last human review decision, missing when sampling skipped human review. Both are missing on canceled items and on items completed before they existed; synthetic nodes have neither. Never declare them with `field()`: the platform owns them, and an authored field with that name collides with it. Read them from the dataset with `toloka dataset load`.
164
-
165
- Keep `instruction` actionable and in English. Include the workflow, concepts, quality rules, and at least one pass and fail example grounded in the task. Do not mention project schedules, dataset volume, workforce requirements, or compensation in labeler instructions.
166
-
167
- #### Audience and pricing
168
-
169
- For humans, set `audienceType: 'human'`, `verifiedExperts`, all three audience filters, `pricePerTask`, and `timeForTask`. Set `verifiedExperts: true` unless the task explicitly asks for the broad unverified crowd. The public shapes differ by filter: languages use `{ type: 'and' | 'or', values: [{ value: 'English' }] }`; specializations use `{ type: 'and' | 'or', values: [{ value, qualificationId }] }`; countries use ISO 3166-1 alpha-2 codes, for example `{ type: 'or', values: ['US'] }`. Verify every country value matches `/^[A-Z]{2}$/`; apply currently accepts arbitrary strings that can match no experts. Derive languages and expertise from the task and its inputs, leaving specialization and country filters unrestricted unless needed. English is a reasonable starting language for an English-language task, not a universal audience default. Ask when an unresolved audience requirement changes who can do the work. Do not put specializations on an unverified audience. A specialization's `qualificationId` is platform identity: preserve an imported value, and discover new ones with `toloka audience specialization list`, copying `ID` into `qualificationId` and `NAME` into `value`; never invent one.
170
-
171
- `$0.20` and 120 seconds are a starting point for a short task, not a universal rate. Estimate effort from the work and required expertise, respect explicit rates, and explain meaningful adjustments in the cost forecast. The per-expert cap is normally off. For collection tasks, choose a cap based on contributor diversity and repetition requirements; 1 fits one-response-per-expert collection. Avoid copying an arbitrary cap into a task that does not need one.
172
-
173
- Choose `audienceType: 'synthetic'` when model-generated data serves the requested outcome; preserve an explicit requirement for human judgments or original human content. Explain the quality and cost tradeoff if recommending synthetic generation. Set the required `maxCostPerAttempt`, the per-attempt ceiling in charged US dollars across fresh input, cached input, and output together, and configure the optional `generationModelId`, `reasoningEffort`, and `llmInstruction`; do not include human pricing or audience fields. Take `generationModelId` only from the union the SDK exports. Use a supported model suited to the task when there is evidence for that choice, or omit the property to use the platform default; do not guess IDs or capabilities. Synthetic generation is incompatible with `data_collection`, human QA, audience reuse, expert sourcing, and Training.
174
-
175
- #### Training, Exam, UI, and advanced settings
176
-
177
- Treat `onboarding` as the node's Training course. It has `enabled`, `steps`, and `info` (`estimatedTime`, `role`, `goal`, `successCriteria`). Build it from the real instruction, criteria, UI, edge cases, and verified examples. Never invent golden answers or media URLs. Training teaches with retries; it is not an admission gate.
178
-
179
- Treat `exam` as the optional one-attempt pass/fail gate after Training and before production. Configure it before finalizing Training. `tasksPerExpert` is 1–10; `passThreshold` is a count from 1 through `tasksPerExpert`. Each `gradedEntities` entry targets an output `Field` and uses public `mode: 'ground_truth' | 'quality_criteria'` plus `groundTruthType: 'direct' | 'custom'`. Exact labels favor direct ground truth; free-form work with criteria favors quality criteria; structured comparisons that need tolerance favor custom ground truth. `goldenDatasetId` is a live dataset ID and cannot be derived from a dataset name by the authoring helper; only preserve an imported ID or obtain a real one. Never fabricate one.
180
-
181
- The `ui` strings are authored directly. Build one expert's task view, ensure component value shapes match entity schemas, read inputs by bare field name, and use `postprocess` when a component's native value needs reshaping. Hardware-backed upload and recording controls require real UI verification.
182
-
183
- Use `reuseAudienceWithNodeId` only with real imported `{ pipelineId, nodeId }` values from a compatible deployed task in another pipeline; never invent IDs. `smartSearch` can hold the public sourcing profile, but `sourceFingerprint` is server-derived. Preserve an imported fingerprint; do not invent one for new source. `checks` can preserve or author stored test cases, but running and interpreting them is a server operation, not an offline constructor feature.
184
-
185
- ### Traps
186
-
187
- - Do not represent QA, review, redo, Training, or Exam as graph nodes.
188
- - Do not omit relations from outputs or confuse format validation with semantic quality.
189
- - Do not add fields, explanations, comments, or labor the task does not require.
190
- - Do not choose synthetic generation merely because a model could do the task; require an explicit request.
191
- - Do not treat the constructor's generated placeholder Training or blank UI as production-ready configuration.
192
- - Do not pair `audience.specializations` with `verifiedExperts: false`. Specializations are qualification-backed, and only verified experts hold them.
193
- - Do not flip an explicitly requested `verifiedExperts: false` to `true`. Every project type accepts either audience mode.
194
- - Do not omit `ui.constants`. It is required alongside `code`, `data`, `preprocess`, and `postprocess`; `'{}'` is the right value when there is nothing to pass. Omitting it fails validation with `must have required property 'constants'`, usually reported together with a misleading `type must be equal to constant` from the failed node discrimination.
195
- - Do not omit `maxCostPerAttempt` from a synthetic `audienceAndPricing`. Omitting it fails validation with `must have required property 'maxCostPerAttempt'`, reported together with a misleading `type must be equal to constant` and the human branch's `must have required property 'verifiedExperts'` from the failed `oneOf` discrimination.
196
- - Do not ship a generation node without Training. `onboarding` is mandatory: at least three `steps`, of which at least one carries a `quiz` block. The server rejects fewer with `Onboarding must have at least 3 steps` and `Onboarding must include at least one quiz block`.
197
- - Do not treat the server's materialized defaults as your configuration. A deployed node reports `qualityAssurance`, `exam`, `reuseAudienceWithNodeId`, and `smartSearch` even when source never set them. Leave them out unless you mean to control them. Omitted top-level object keys are preserved, but defaults materialized inside list elements can still cause drift; use the generic import-and-diff recovery flow.
198
-
199
- ## `quorum`
200
-
201
- ### Pick it
202
-
203
- Use `quorum` only when the same item needs a fixed number of independent human annotations and visible disagreement. Classification, rating, and short-label tasks are the common fit. Use `generation` for one annotation. Do not approximate adaptive consensus by chaining human nodes or routing with `match`.
204
-
205
- ### Configure it
206
-
207
- `quorum(name, settings)` accepts `QuorumAuthoringSettings`, derived from `QuorumNodeSettings`:
208
-
209
- - `about`, `publicName`, `entities`, `qualityCriteria`, `instruction`, `ui`, `audienceAndPricing`, `onboarding`, `exam`, `reuseAudienceWithNodeId`, `smartSearch`, and `checks` follow the human-node rules above.
210
- - `overlap` is the fixed number of independent labelers per item: 1–100. Default to 3 when the user does not specify it; 3 gives a majority, 5 gives stronger consensus, and cost grows linearly.
211
- - `qualityAssurance` is either `llm_only` or `none`.
212
- - `offboardingRules` supports majority-vote agreement rules in No-QA mode.
213
-
214
- Quorum is human-only in the current public shape; its `audienceAndPricing.audienceType` is `'human'`.
215
-
216
- #### The quorum data shape
217
-
218
- **Every quorum output `field()` must have a top-level array JSON schema.** The array is the collected-annotation dimension: one element per labeler, nominally `overlap` elements. Its `items` schema is one labeler's contribution.
219
-
220
- Leave the outer array's length unbounded: it varies per item (fewer than `overlap` on a canceled item, up to `2 × overlap` under `llm_only` escalation). Validation rejects an outer `minItems` above 1 and an outer `maxItems` below `overlap` (below `2 × overlap` with `llm_only` QA). It also rejects `allOf`, `anyOf` or `oneOf` on the outer array; set length limits directly on it and put other constraints in `items`.
221
-
222
- Configure entities for one labeler's contribution, not for the outer array. A single label per person therefore uses `dynamic: false` and `jsonPath: '$.label'`, while the declared output field is `{ type: 'array', items: { type: 'string' } }`. `dynamic: true` means an array inside each labeler's contribution and therefore produces an array of arrays. Build the UI to submit one value at `$.label`, never `$.label[0]`; the platform assembles the outer array.
223
-
224
- When a quorum output entity is `optional: true`, its outer field's `items` schema must also accept `null`, for example `{ type: 'array', items: { type: ['string', 'null'] } }`. The platform reserves a null annotation slot when that labeler omits the value and rejects an optional entity backed by non-nullable items.
225
-
226
- Quorum does not aggregate. It writes all raw annotations back, including rejected ones. Add a downstream `code` node when the requested deliverable needs one consensus value, and make it tolerate arrays longer than `overlap`.
227
-
228
- The platform also writes an annotator field, `<snake_cased node name>_annotator_ids`, on every quorum node in both QA modes. It is an optional array of annotator ID strings, one per collected annotation, aligned by position with every output array and every `*_evaluation_result` array. Rejected annotations keep their positions, and the verdicts at the same position show the review outcome. One labeler can hold more than one position. The field is missing on items completed before it existed and on canceled items with no annotations; a canceled item with some annotations carries their IDs. Never declare it with `field()`: the platform owns it, and an authored field with that name collides with it. Read it from the dataset with `toloka dataset load`.
229
-
230
- #### Explanation-driven quality
231
-
232
- With `llm_only` QA, include a required output entity of type `EXPLANATION` and a corresponding top-level array string field unless the task makes that impossible. Put the strongest criterion on this explanation. Evaluate whether the reasoning is substantive and applies the task's definitions; do not rely primarily on judging the raw label. Different labels can be reasonable, while reasoning reveals annotation quality. Label outputs do not each require their own criterion on quorum.
233
-
234
- With `qaMode: 'none'`, no LLM reads the explanation. Do not require an explanation by default unless the user asks for written reasoning or comments. Keep at least one quality criterion as labeler-facing Quality Panel guidance, even though no model evaluates it.
235
-
236
- #### QA and escalation
237
-
238
- Default to `qualityAssurance: { qaMode: 'llm_only' }`. The LLM accepts or rejects each annotation. If all initial annotations are rejected, the platform adds fresh annotations one at a time until one is accepted or the total reaches `2 × overlap`. Therefore result arrays may be longer than `overlap`.
239
-
240
- **Quorum has no redo cycle.** A rejected annotation is never reopened or sent back to the same expert. Escalation adds a fresh annotation, and all collected annotations—accepted and rejected—remain in the output arrays. Hitting the cap with zero accepted annotations still returns the collected results.
241
-
242
- Use `qaMode: 'none'` when agreement-based quality control fits the task better than LLM grading, explaining the tradeoff before changing an existing QA strategy. This mode requires:
243
-
244
- - set `overlap` to at least 3;
245
- - disable the per-expert cap;
246
- - add at least one `{ id, type: 'mv_agreement', agreementScale }` offboarding rule;
247
- - set `agreementScale` to the authored output field ID for a top-level `SINGLE_LABEL` entity.
248
-
249
- The authoring helper does not translate `offboardingRules.agreementScale` from `Field` to ID. Use `agreementScale: labelField.id`; this is a derived field reference, not an invented raw ID. Do not combine No-QA mode with a per-expert cap.
250
-
251
- ### Traps
252
-
253
- - Do not model the quorum array twice. The field schema owns the outer annotation array; entity `dynamic` owns repetition inside one annotation.
254
- - Do not promise exactly `overlap` results or exactly `overlap` accepted results.
255
- - Do not add majority-vote or averaging logic inside the node.
256
- - Do not call escalation dynamic overlap. Per-item or agreement-threshold-dependent overlap is unsupported.
257
- - Do not configure human-only or hybrid QA; quorum supports only `llm_only` and `none`.
258
- - Do not invent a redo path.
93
+ - Do not import third-party packages such as `requests`, `pandas`, or `numpy`: only the standard library exists when the platform runs the node.
259
94
 
260
95
  ## `match`
261
96
 
262
- ### Pick it
263
-
264
- Use `match` when one item must take one conditional path based on data. Do not use it for parallel fan-out, loops, review cycles, adaptive quorum, or consensus aggregation.
265
-
266
- ### Configure it
267
-
268
97
  Set `MatchAuthoringSettings`, derived from `MatchNodeSettings`, through `match(name, settings)`:
269
98
 
270
99
  - `about`: `name`, optional `description`, optional `projectType`.
271
100
  - `input`: every declared `Field` referenced by a predicate, each with `optional`.
272
- - `conditions`: ordered branches with optional `logic: 'and' | 'or'` and required `predicates`.
101
+ - `conditions`: ordered branches with `logic: 'and' | 'or'` and required `predicates`. `logic` defaults to `'and'`; set `'or'` when any one predicate should match.
273
102
  - Each condition `name` is a unique identifier within its Match node, from 2 through 32 characters: start with a lowercase letter, use only lowercase letters, digits, and underscores, and end with a letter or digit. Use `is_disputed`, not `is disputed` or `field_type = general_info`. `match()` throws locally for invalid or duplicate names.
274
103
  - Each authored predicate uses `field`, `jsonPath`, `operator`, and scalar `value`.
275
104
 
@@ -283,23 +112,3 @@ Ordering is semantic: `conditions[0]` routes to `matched(0)`, the first true con
283
112
  - Do not assume all matching branches run.
284
113
  - Do not use unsupported comparison, set-membership, range, or emptiness operators. Put that logic in `code`, emit a routing key, then compare it with `text_eq` or `text_neq`.
285
114
  - Do not reference a field in a predicate without binding it in `input`.
286
-
287
- ## Public-surface gaps
288
-
289
- When the requirement depends on one of these capabilities, state the gap instead of inventing source syntax:
290
-
291
- - Audience discovery beyond specializations has no SDK or CLI path: valid-language/country suggestions, audience-size statistics for a composed filter, and recommended-rate calculation. Specializations and their expert counts come from `toloka audience specialization list`.
292
- - Audience-reuse candidate discovery has no SDK or CLI path. Source can store a known cross-pipeline pointer, but cannot search candidates.
293
- - Smart Search rendering, source-fingerprint computation, and stale-profile diagnosis have no SDK or CLI path. Source can carry the public profile; it cannot generate the server-owned fingerprint or preview rendered sourcing documents.
294
- - The `@toloka-platform/sdk/testing` entry point provides typed `test.code`, `test.ui`, and `test.pipeline` wrappers. `toloka check` runs their evidence with project TypeScript and automatic Task UI typechecking. Missing Remote Node Checks remain separate from this local gate.
295
- - Training's examples playbooks and automatic course authoring have no SDK/CLI path. Source can set `onboarding`, but cannot request geometry/numeric check templates or validate hands-on examples locally beyond the public graph schema.
296
- - Exam golden-dataset discovery, creation by symbolic dataset name, dataset lint execution, and generated-golden confirmation flow have no high-level SDK authoring path. `exam.goldenDatasetId` requires a real ID.
297
- - Per-node platform validation is not exposed separately. `definePipeline` and `toloka check` enforce local contracts; `toloka validate` runs platform spec validation and reports the latest matching Remote Node Check jobs without writing the graph.
298
- - Stored Remote Node Checks can be represented in settings and the low-level client can list/start them. `toloka check` deliberately runs local source and authored tests; it does not configure or start platform check jobs.
299
- - Project rename and runtime item-filter authoring have no `toloka` source path. The CLI preserves the live item filter and a live `itemLimit` unless source declares its own limit.
300
-
301
- - Generation's platform-controlled redo cap, appeal behavior, payment retry, and exact terminal handling are intentionally not configurable SDK settings.
302
- - Dynamic quorum and per-item overlap are unsupported by both surfaces; this is a platform limitation, not missing TypeScript syntax.
303
- - Server-owned generation outputs (`<node>_annotator_id`, `<node>_qa_annotator_id`) and quorum outputs (`<node>_annotator_ids`, `*_evaluation_result`, `<node>_qa_metadata`) have no authoring path. Source cannot declare them or wire them into a downstream node; read them from the dataset instead.
304
-
305
- For every representable change above, edit the node's source file, run `toloka validate`, inspect `toloka plan`, and apply with `toloka apply`.
@@ -0,0 +1,61 @@
1
+ # Task UI
2
+
3
+ Use this reference whenever you write or change the `ui` of a `generation` or `quorum` node. The Task UI is the screen one expert works in, so it decides both the quality of the collected data and how long each task takes.
4
+
5
+ This file describes how the platform evaluates and validates a Task UI and how to design and verify one. It deliberately names no component props: components change, so take every component fact from the launcher.
6
+
7
+ ## Learn the components from the launcher
8
+
9
+ List the components with `toloka ui components`, pick them by their description and when-to-use line, and read the contract of every component you intend to use, layout containers included, with `toloka ui components <name> [name...]`. Never write a component, prop, or value from memory.
10
+
11
+ - A contract has `propsType` (with defaults), `valueType`, an `example`, and the `whenToUse`, `capabilities`, `limitations`, and `bestPractices` lists. Read all of them: the lists hold rules such as required props, grid scales, and defaults that hide or squeeze content when left unset.
12
+ - Props are checked strictly. An unknown prop or a value of the wrong type replaces the component with an error box in the rendered UI.
13
+ - Besides its listed props, every field accepts the shared props `path`, `label`, `hint`, `required`, `disabled`, `validation`, `onChange`, and `style`. Use `style` for sizing and positioning only, never for colors or typography.
14
+ - A contract's `example` shows the component, not a complete platform-valid configuration. It can omit rules this file requires, such as text-field length limits.
15
+ - A field's `valueType` must match the output entity it answers. A `SINGLE_LABEL` stores one string from its `allowedLabels` and a `MULTI_LABEL` an array of them, so answer each with a choice field of that value type and pass the entity's `allowedLabels` as its options. A yes/no answer, confirmation, or consent that the pipeline keeps is a `SINGLE_LABEL` with values such as `yes` and `no`; a boolean field fails the platform's interface lint after apply, even where its contract recommends it for confirmations.
16
+ - For structured or array-valued fields, derive the dataset field schema from `valueType`, then feed the documented example value through `setValue`, `getValue`, and `submit` in the UI scenario so upstream contract drift fails locally. Never translate undocumented property names or replace missing values with defaults.
17
+
18
+ ## How the platform evaluates `ui`
19
+
20
+ - **`ui.code`** is placed inside `return ( … )` of a component, so it is a single JSX expression with one root element. TSX, including TypeScript annotations, is accepted. Statements (`const`, `if`, loops) fail with `Unexpected token` at the top level; they are allowed only inside callback bodies such as `onChange`, a `validator`, or a render function. Write conditions inline: `{actions.get('verdict', '') === 'other' ? <… /> : null}`.
21
+ - **Scope of `ui.code`:** the component namespaces, `Validation`, `actions`, `CONST`, `data` (the live item, including the expert's answers), `React`, and JavaScript built-ins.
22
+ - **`actions.get(path, fallback)`** reads the item by field name or lodash path (`a.b`, `list.0.text`) and returns `fallback` when the value is missing or `null`. Read inputs with it rather than through `data`. `actions.set(path, value)` writes a value, for example to clear a dependent answer from an `onChange` callback.
23
+ - **`ui.constants`** is required: a JSON object string, read in `ui.code` as `CONST.<key>` (for example a list of options stored as `JSON.stringify({ LABELS: labels })` and used as `CONST.LABELS`). Use `'{}'` when there is nothing to pass. Without it the source fails to load with `must have required property 'constants'` next to a misleading `type must be equal to constant`.
24
+ - **`ui.data`** is the preview item: a JSON object that holds a schema-valid sample value for every required input field. Optional inputs may be left out. Keys that are not input fields are rejected, and apply fails with `Data is missing required input` when a required one is absent.
25
+ - **`preprocess`** and **`postprocess`** are JavaScript function bodies run as `(data) => { … }`. Only `data` is in scope: `CONST` is not available there, and `toloka check` reports `Cannot find name 'CONST'`. Never put TypeScript-only syntax in them.
26
+ - **`preprocess`** returns an object that is merged over the item when the task loads and again after every answer the expert gives. Never return a key that a field is bound to, because it overwrites the expert's answer on every change. A body that contains `return {}` anywhere, even in an early-return branch, is ignored as a whole.
27
+ - **`postprocess`** must return a plain object on every branch. It holds only new, output-only keys derived from `data`. Never return the top-level key of a field's `path` (`path="eval.score"` binds `eval`): the task then can never be submitted.
28
+ - **Submission** is the whole item except `_context`: the inputs, preprocess keys, values written with `actions.set`, every answer (including the last value of a field that is now hidden), and the postprocess output.
29
+ - Bind each field to its own `path`, and prefer flat top-level paths: a dotted path creates a nested object in the output.
30
+
31
+ ## Validation
32
+
33
+ - A field's `validation` is an array of rule objects. Rule keys are `required`, `min`, `max`, `len`, `pattern`, `enum`, `whitespace`, `type`, `message`, and `validator` (`(value) => true | new Error(message)`). `min`, `max`, and `len` count characters for strings and items for arrays, and bound the value for numbers.
34
+ - A rule without `type` is checked as a string. Add `type: 'number'` or `type: 'array'` to a rule that limits a numeric or array value; otherwise every valid value fails that rule and shows its `message`.
35
+ - Without a `required` rule an empty value passes. Keep each limit in its own rule object with its own `message`, because a rule's message is shown for every check in that object.
36
+ - Check relations between answers with the `Validation` wrapper; read its contract.
37
+ - A field that conditional rendering removes is not validated, but its last value stays in the submission. Clear it with `actions.set` when the controlling answer changes, or ignore it in `postprocess`. A field inside a collapsed or inactive container is still mounted and still validated.
38
+ - Every free-text field (`Field.Text`, `Field.Textarea`, `Field.Code`) needs a `max` rule. The platform's interface lint enforces this after apply, and `toloka check` does not catch it:
39
+ - `max` is a positive integer number literal, or the field's whole `validation` comes from a `CONST` rule array;
40
+ - the `max` values of all text fields on one interface add up to at most 262144 characters;
41
+ - a list container that repeats text fields needs a number-literal maximum item count, which multiplies the limits inside it.
42
+ - Keep length and format limits that the expert must respect in the UI `validation`, not in `field()`: a dataset field's schema is fixed once its column exists.
43
+
44
+ ## Design the screen
45
+
46
+ - Use one root container that stacks the sections vertically with consistent spacing, and group related content into sections.
47
+ - Show every input the expert judges: media in a viewer component and text readable in full. A rating or comparison task that does not show what is being rated cannot be done well.
48
+ - Keep the decision controls next to the content they refer to and on the first screen. With media and text, put the media beside the controls when space allows, and cap tall media with a `style` height so it does not push the controls down.
49
+ - Check each layout component's defaults in its contract: unset direction, width, or column values can lay children out in one squeezed row or render nothing at all.
50
+ - Use single-click options for 2 to 7 choices and a dropdown for 8 or more. Show a follow-up field only when an earlier answer requires it.
51
+ - Split an interface with four or more decisions into groups or steps.
52
+ - Keep spacing consistent and use theme colors; never hardcode colors.
53
+ - Do not add tips, instructions, examples, or fields that the task does not ask for. The instruction and Training explain the task; the screen is for doing it.
54
+
55
+ ## Verify it
56
+
57
+ Register each required UI scenario with `test.ui(pipeline, node, name, callback)` and submit through its bound `render` helper. Use `toMatchObject` on `submit()`: it returns the whole item, so `toEqual` against only the outputs fails.
58
+
59
+ Check the result visually. In the scenario, after `render` (and after `setValue` when the filled state matters), save a screenshot with `await session.screenshot('<path>.png')`. Run `toloka check <pipeline> --node <node>` and open the image. The screenshot shows what the expert sees on the first screen: the content and the decision controls should be there, with no error boxes, missing sections, squeezed columns, or cut-off text. Fix what is off before you apply; stop after a few rounds and report anything that still looks wrong.
60
+
61
+ Run `toloka check <pipeline> --node <node>` after every Task UI edit; require passing UI types, empty browser validation errors, valid postprocessed entity output, and the visual check. Never weaken authored validation merely to make a test pass. Upload and recording controls are hardware-backed and need real UI verification.