@toloka-platform/sdk 0.12.2 → 0.13.1

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 (83) hide show
  1. package/dist/cli/toloka.js +153 -151
  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 +2 -2
  6. package/docs/cli.md +19 -0
  7. package/docs/getting-started.md +1 -1
  8. package/docs/nodes.md +18 -1
  9. package/examples/product-moderation/nodes/moderate-product/node.ts +1 -1
  10. package/examples/video-pair-rating/nodes/adjudicate/node.ts +1 -1
  11. package/examples/video-pair-rating/nodes/adjudicate_op/node.ts +1 -1
  12. package/examples/video-pair-rating/nodes/overall_preference_pick/node.ts +1 -1
  13. package/examples/video-pair-rating/nodes/rate_pair/node.ts +1 -1
  14. package/openapi/gen/api.ts +142 -0
  15. package/openapi/gen/models/batchInferenceMode.ts +3 -0
  16. package/openapi/gen/models/batchInferenceModeOneOf.ts +4 -0
  17. package/openapi/gen/models/batchInferenceModel.ts +4 -0
  18. package/openapi/gen/models/batchInferenceModelOneOf.ts +6 -0
  19. package/openapi/gen/models/batchInferenceModelOneOfBaseModelId.ts +2 -0
  20. package/openapi/gen/models/batchInferenceModelOneOfThree.ts +5 -0
  21. package/openapi/gen/models/batchInferenceRun.ts +35 -0
  22. package/openapi/gen/models/batchInferenceRunBlockedReason.ts +2 -0
  23. package/openapi/gen/models/batchInferenceRunChargedAmount.ts +2 -0
  24. package/openapi/gen/models/batchInferenceRunError.ts +3 -0
  25. package/openapi/gen/models/batchInferenceRunErrorAnyOf.ts +6 -0
  26. package/openapi/gen/models/batchInferenceRunErrorAnyOfCode.ts +2 -0
  27. package/openapi/gen/models/batchInferenceRunId.ts +2 -0
  28. package/openapi/gen/models/batchInferenceRunMessage.ts +2 -0
  29. package/openapi/gen/models/batchInferenceRunOutput.ts +9 -0
  30. package/openapi/gen/models/batchInferenceRunOutputType.ts +2 -0
  31. package/openapi/gen/models/batchInferenceRunProgress.ts +3 -0
  32. package/openapi/gen/models/batchInferenceRunProgressAnyOf.ts +7 -0
  33. package/openapi/gen/models/batchInferenceRunProgressAnyOfPhase.ts +2 -0
  34. package/openapi/gen/models/batchInferenceRunProgressAnyOfRequests.ts +5 -0
  35. package/openapi/gen/models/batchInferenceRunQuote.ts +3 -0
  36. package/openapi/gen/models/batchInferenceRunQuoteAnyOf.ts +9 -0
  37. package/openapi/gen/models/batchInferenceRunQuoteAnyOfConfirmBefore.ts +2 -0
  38. package/openapi/gen/models/batchInferenceRunResult.ts +3 -0
  39. package/openapi/gen/models/batchInferenceRunResultAnyOf.ts +12 -0
  40. package/openapi/gen/models/batchInferenceRunResultAnyOfErrorCounts.ts +2 -0
  41. package/openapi/gen/models/batchInferenceRunResultAnyOfErrorSamplesItem.ts +6 -0
  42. package/openapi/gen/models/batchInferenceRunSampling.ts +9 -0
  43. package/openapi/gen/models/batchInferenceRunStatus.ts +2 -0
  44. package/openapi/gen/models/createBatchInferenceRunBody.ts +14 -0
  45. package/openapi/gen/models/createBatchInferenceRunBodyItemFilter.ts +14 -0
  46. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterExcludePartsItem.ts +5 -0
  47. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterFieldSourcesItem.ts +6 -0
  48. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterFieldSourcesItemSourcesItem.ts +4 -0
  49. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterFieldSourcesItemSourcesItemOneOf.ts +5 -0
  50. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterFieldSourcesItemSourcesItemOneOfTwo.ts +5 -0
  51. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterFieldValuesItem.ts +12 -0
  52. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterFieldValuesItemOperator.ts +2 -0
  53. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterFieldValuesItemSourcesItem.ts +4 -0
  54. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterFieldValuesItemSourcesItemOneOf.ts +5 -0
  55. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterFieldValuesItemSourcesItemOneOfTwo.ts +5 -0
  56. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterFieldValuesItemValue.ts +2 -0
  57. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterParts.ts +5 -0
  58. package/openapi/gen/models/createBatchInferenceRunBodyItemFilterSlice.ts +4 -0
  59. package/openapi/gen/models/createBatchInferenceRunBodyOutput.ts +4 -0
  60. package/openapi/gen/models/createBatchInferenceRunBodyOutputOneOf.ts +5 -0
  61. package/openapi/gen/models/createBatchInferenceRunBodyOutputOneOfTwo.ts +5 -0
  62. package/openapi/gen/models/createBatchInferenceRunBodySampling.ts +9 -0
  63. package/openapi/gen/models/datasetFieldOperation.ts +2 -1
  64. package/openapi/gen/models/datasetFieldWriteOperation.ts +8 -0
  65. package/openapi/gen/models/datasetFieldWriteOperationProgress.ts +3 -0
  66. package/openapi/gen/models/datasetFieldWriteOperationProgressAnyOf.ts +5 -0
  67. package/openapi/gen/models/index.ts +53 -0
  68. package/openapi/gen/models/listBatchInferenceRuns200.ts +7 -0
  69. package/openapi/gen/models/listBatchInferenceRunsParams.ts +5 -0
  70. package/package.json +2 -2
  71. package/skills/catalog.json +6 -6
  72. package/skills/toloka-build/references/human-work.md +5 -5
  73. package/skills/toloka-build/references/workflow-and-cli.md +6 -2
  74. package/src/cli/analytics.ts +8 -0
  75. package/src/cli/batch-inference.ts +267 -0
  76. package/src/cli/commands/batch-inference.ts +116 -0
  77. package/src/cli/context.ts +14 -0
  78. package/src/cli/models.ts +1 -1
  79. package/src/cli/program.ts +2 -0
  80. package/src/cli/splits.ts +40 -0
  81. package/src/cli/trainings.ts +1 -19
  82. package/src/client.ts +49 -0
  83. package/src/pipeline-graph-v1.contract.json +2 -2
@@ -0,0 +1,5 @@
1
+
2
+ export type CreateBatchInferenceRunBodyItemFilterFieldValuesItemSourcesItemOneOfTwo = {
3
+ type: 'system';
4
+ source: string;
5
+ };
@@ -0,0 +1,2 @@
1
+
2
+ export type CreateBatchInferenceRunBodyItemFilterFieldValuesItemValue = string | number | boolean | null;
@@ -0,0 +1,5 @@
1
+
2
+ export type CreateBatchInferenceRunBodyItemFilterParts = {
3
+ fieldId: string;
4
+ names: string[];
5
+ };
@@ -0,0 +1,4 @@
1
+
2
+ export type CreateBatchInferenceRunBodyItemFilterSlice = {
3
+ sliceId: string;
4
+ };
@@ -0,0 +1,4 @@
1
+ import type { CreateBatchInferenceRunBodyOutputOneOf } from './createBatchInferenceRunBodyOutputOneOf';
2
+ import type { CreateBatchInferenceRunBodyOutputOneOfTwo } from './createBatchInferenceRunBodyOutputOneOfTwo';
3
+
4
+ export type CreateBatchInferenceRunBodyOutput = CreateBatchInferenceRunBodyOutputOneOf | CreateBatchInferenceRunBodyOutputOneOfTwo;
@@ -0,0 +1,5 @@
1
+
2
+ export type CreateBatchInferenceRunBodyOutputOneOf = {
3
+ type: 'field';
4
+ fieldName: string;
5
+ };
@@ -0,0 +1,5 @@
1
+
2
+ export type CreateBatchInferenceRunBodyOutputOneOfTwo = {
3
+ type: 'dataset';
4
+ name: string;
5
+ };
@@ -0,0 +1,9 @@
1
+
2
+ export type CreateBatchInferenceRunBodySampling = {
3
+ maxOutputTokens: number;
4
+ temperature?: number;
5
+ topP?: number;
6
+ stop?: string[];
7
+ seed?: number;
8
+ samplesPerRow?: number;
9
+ };
@@ -1,4 +1,5 @@
1
1
  import type { DatasetFieldCopyOperation } from './datasetFieldCopyOperation';
2
2
  import type { DatasetFieldSplitFillOperation } from './datasetFieldSplitFillOperation';
3
+ import type { DatasetFieldWriteOperation } from './datasetFieldWriteOperation';
3
4
 
4
- export type DatasetFieldOperation = DatasetFieldCopyOperation | DatasetFieldSplitFillOperation;
5
+ export type DatasetFieldOperation = DatasetFieldCopyOperation | DatasetFieldSplitFillOperation | DatasetFieldWriteOperation;
@@ -0,0 +1,8 @@
1
+ import type { DatasetFieldWriteOperationProgress } from './datasetFieldWriteOperationProgress';
2
+
3
+ export interface DatasetFieldWriteOperation {
4
+ type: 'write';
5
+ writer: string;
6
+ progress: DatasetFieldWriteOperationProgress;
7
+ blocking: boolean;
8
+ }
@@ -0,0 +1,3 @@
1
+ import type { DatasetFieldWriteOperationProgressAnyOf } from './datasetFieldWriteOperationProgressAnyOf';
2
+
3
+ export type DatasetFieldWriteOperationProgress = DatasetFieldWriteOperationProgressAnyOf | null;
@@ -0,0 +1,5 @@
1
+
2
+ export type DatasetFieldWriteOperationProgressAnyOf = {
3
+ done: number;
4
+ total: number;
5
+ };
@@ -25,6 +25,35 @@ export * from './attachPipelineDatasetBodyItemFilterSlice';
25
25
  export * from './baseModel';
26
26
  export * from './baseModelId';
27
27
  export * from './baseModelIdProperty';
28
+ export * from './batchInferenceMode';
29
+ export * from './batchInferenceModeOneOf';
30
+ export * from './batchInferenceModel';
31
+ export * from './batchInferenceModelOneOf';
32
+ export * from './batchInferenceModelOneOfBaseModelId';
33
+ export * from './batchInferenceModelOneOfThree';
34
+ export * from './batchInferenceRun';
35
+ export * from './batchInferenceRunBlockedReason';
36
+ export * from './batchInferenceRunChargedAmount';
37
+ export * from './batchInferenceRunError';
38
+ export * from './batchInferenceRunErrorAnyOf';
39
+ export * from './batchInferenceRunErrorAnyOfCode';
40
+ export * from './batchInferenceRunId';
41
+ export * from './batchInferenceRunMessage';
42
+ export * from './batchInferenceRunOutput';
43
+ export * from './batchInferenceRunOutputType';
44
+ export * from './batchInferenceRunProgress';
45
+ export * from './batchInferenceRunProgressAnyOf';
46
+ export * from './batchInferenceRunProgressAnyOfPhase';
47
+ export * from './batchInferenceRunProgressAnyOfRequests';
48
+ export * from './batchInferenceRunQuote';
49
+ export * from './batchInferenceRunQuoteAnyOf';
50
+ export * from './batchInferenceRunQuoteAnyOfConfirmBefore';
51
+ export * from './batchInferenceRunResult';
52
+ export * from './batchInferenceRunResultAnyOf';
53
+ export * from './batchInferenceRunResultAnyOfErrorCounts';
54
+ export * from './batchInferenceRunResultAnyOfErrorSamplesItem';
55
+ export * from './batchInferenceRunSampling';
56
+ export * from './batchInferenceRunStatus';
28
57
  export * from './checkJob';
29
58
  export * from './checkJobError';
30
59
  export * from './checkJobResult';
@@ -78,6 +107,25 @@ export * from './codeNodeSettingsFieldProxies';
78
107
  export * from './codeNodeSettingsFieldProxiesInputItem';
79
108
  export * from './codeNodeSettingsFieldProxiesOutputItem';
80
109
  export * from './copyPipelineBody';
110
+ export * from './createBatchInferenceRunBody';
111
+ export * from './createBatchInferenceRunBodyItemFilter';
112
+ export * from './createBatchInferenceRunBodyItemFilterExcludePartsItem';
113
+ export * from './createBatchInferenceRunBodyItemFilterFieldSourcesItem';
114
+ export * from './createBatchInferenceRunBodyItemFilterFieldSourcesItemSourcesItem';
115
+ export * from './createBatchInferenceRunBodyItemFilterFieldSourcesItemSourcesItemOneOf';
116
+ export * from './createBatchInferenceRunBodyItemFilterFieldSourcesItemSourcesItemOneOfTwo';
117
+ export * from './createBatchInferenceRunBodyItemFilterFieldValuesItem';
118
+ export * from './createBatchInferenceRunBodyItemFilterFieldValuesItemOperator';
119
+ export * from './createBatchInferenceRunBodyItemFilterFieldValuesItemSourcesItem';
120
+ export * from './createBatchInferenceRunBodyItemFilterFieldValuesItemSourcesItemOneOf';
121
+ export * from './createBatchInferenceRunBodyItemFilterFieldValuesItemSourcesItemOneOfTwo';
122
+ export * from './createBatchInferenceRunBodyItemFilterFieldValuesItemValue';
123
+ export * from './createBatchInferenceRunBodyItemFilterParts';
124
+ export * from './createBatchInferenceRunBodyItemFilterSlice';
125
+ export * from './createBatchInferenceRunBodyOutput';
126
+ export * from './createBatchInferenceRunBodyOutputOneOf';
127
+ export * from './createBatchInferenceRunBodyOutputOneOfTwo';
128
+ export * from './createBatchInferenceRunBodySampling';
81
129
  export * from './createDatasetBody';
82
130
  export * from './createDatasetBodyProfile';
83
131
  export * from './createDatasetExportBody';
@@ -168,6 +216,9 @@ export * from './datasetFieldSplitAnyOfRecipeAnyOfBalanceByFieldId';
168
216
  export * from './datasetFieldSplitAnyOfRecipeAnyOfSharesItem';
169
217
  export * from './datasetFieldSplitAnyOfStatus';
170
218
  export * from './datasetFieldSplitFillOperation';
219
+ export * from './datasetFieldWriteOperation';
220
+ export * from './datasetFieldWriteOperationProgress';
221
+ export * from './datasetFieldWriteOperationProgressAnyOf';
171
222
  export * from './datasetId';
172
223
  export * from './datasetItem';
173
224
  export * from './datasetItemCurrentSubmissionId';
@@ -356,6 +407,8 @@ export * from './jsonSchemaTypeAnyOfItem';
356
407
  export * from './listAudienceSpecializations200';
357
408
  export * from './listAudienceSpecializationsParams';
358
409
  export * from './listBaseModels200';
410
+ export * from './listBatchInferenceRuns200';
411
+ export * from './listBatchInferenceRunsParams';
359
412
  export * from './listDatasetFields200';
360
413
  export * from './listDatasetFieldsParams';
361
414
  export * from './listDatasetItems200';
@@ -0,0 +1,7 @@
1
+ import type { BatchInferenceRun } from './batchInferenceRun';
2
+ import type { Pagination } from './pagination';
3
+
4
+ export type ListBatchInferenceRuns200 = {
5
+ batchInferenceRuns: BatchInferenceRun[];
6
+ pagination: Pagination;
7
+ };
@@ -0,0 +1,5 @@
1
+
2
+ export type ListBatchInferenceRunsParams = {
3
+ limit?: number;
4
+ offset?: number;
5
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toloka-platform/sdk",
3
- "version": "0.12.2",
3
+ "version": "0.13.1",
4
4
  "private": false,
5
5
  "description": "Build, inspect, and operate Toloka pipeline projects as TypeScript source. The public API may change between minor versions before 1.0.",
6
6
  "license": "Apache-2.0",
@@ -77,6 +77,6 @@
77
77
  "bun": ">=1.4.0"
78
78
  },
79
79
  "tolokaBuild": {
80
- "commit": "72b9ea77f616c7f9c3c564c550eac42f703929b2"
80
+ "commit": "52c25c9c833bf57bbbf04f6cb9847fc577c0116a"
81
81
  }
82
82
  }
@@ -11,8 +11,8 @@
11
11
  "testing",
12
12
  "checks"
13
13
  ],
14
- "version": "0.12.2",
15
- "digest": "7e6b79b9b85fbfc86cf98f33ebd30fdb4617707d22f7c24440bee2fedecb5f71",
14
+ "version": "0.13.1",
15
+ "digest": "d7d6b0e5003031cbd356b64c5341f2db090ebe87986e0eb7e767e0f97aa59345",
16
16
  "files": [
17
17
  "SKILL.md",
18
18
  "references/human-work.md",
@@ -33,7 +33,7 @@
33
33
  "bug",
34
34
  "report"
35
35
  ],
36
- "version": "0.12.2",
36
+ "version": "0.13.1",
37
37
  "digest": "70f0bd2c80b32f87896aeb27c363b8a2d37f40e22424f57fe1aad142e125ad67",
38
38
  "files": [
39
39
  "SKILL.md",
@@ -50,7 +50,7 @@
50
50
  "getting-started",
51
51
  "agent"
52
52
  ],
53
- "version": "0.12.2",
53
+ "version": "0.13.1",
54
54
  "digest": "2278adeb46a792356579395fcf73c42322cb23bcd0c61e20856ccabdad6762d1",
55
55
  "files": [
56
56
  "SKILL.md"
@@ -67,7 +67,7 @@
67
67
  "sft",
68
68
  "models"
69
69
  ],
70
- "version": "0.12.2",
70
+ "version": "0.13.1",
71
71
  "digest": "3ce10dee5d3c283d8b5fdea15134a58fc738c7af2c1bb19eb5a0bf70ea0bca13",
72
72
  "files": [
73
73
  "SKILL.md"
@@ -84,7 +84,7 @@
84
84
  "prompt",
85
85
  "models"
86
86
  ],
87
- "version": "0.12.2",
87
+ "version": "0.13.1",
88
88
  "digest": "be8bce8d9cf7241ccb60d2b2a9b7cc07d7755a76e8ff8ec1b0847c49efbb3469",
89
89
  "files": [
90
90
  "SKILL.md"
@@ -49,7 +49,7 @@ Write at least one criterion for every output entity. Each criterion needs an au
49
49
 
50
50
  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.
51
51
 
52
- 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 30 seconds and at least 3 seconds per quality criterion of the node, whichever is larger; `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.
52
+ 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` in whole seconds: at least 30 and at least 3 per quality criterion of the node, whichever is larger, and at most 359,999 (99:59:59); `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.
53
53
 
54
54
  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. The platform also writes one `*_evaluation_result` field per quality criterion with LLM or Human QA verdicts, `<snake_cased node name>_qa_metadata`, and, on a synthetic node with LLM QA, `<snake_cased node name>_evaluation_summary`. Never declare any of these 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`.
55
55
 
@@ -57,9 +57,9 @@ Keep `instruction` actionable and in English. Include the workflow, concepts, qu
57
57
 
58
58
  #### Audience and pricing
59
59
 
60
- 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.
60
+ 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'] }`. Apply rejects a country code, language, or specialization that is not in the audience catalog, and an audience that matches 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.
61
61
 
62
- `$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.
62
+ You choose `pricePerTask`: apply accepts any value inside the audience's range and rejects one outside it with an error that names the exact bounds. The range is set per hour of work and converted to one task as `rate × timeForTask / 3600`: $12–$90 for ID-verified experts without a specialization (the editor suggests $21) and $3–$24 for the broad crowd (the editor suggests $6). With a specialization it starts at $12 and its top follows that specialization's rates. `timeForTask` is a whole number of seconds from 10 to 359,999 (99:59:59). `$0.40` for 120 seconds is the lowest price an ID-verified audience allows: 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.
63
63
 
64
64
  Use `audienceType: 'synthetic'` only when the user explicitly asks for model-generated output instead of human work; otherwise the audience is human. Explain the quality and cost tradeoff when you set it up. 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. Pick the model, its reasoning effort and the budget from `toloka generation-model list`: copy `ID` into `generationModelId` (it must also be in the `generationModelId` union of the installed SDK; a newer model needs an SDK update), set `reasoningEffort` only for a model that lists reasoning efforts (apply rejects it on any other model), and keep `maxCostPerAttempt` between `MIN` and `MAX`, at or above `TYPICAL`. Use a model suited to the task when there is evidence for that choice, or omit `generationModelId` to use the default model the command names; do not guess IDs or capabilities. Synthetic generation is incompatible with `data_collection`, human QA, audience reuse, expert sourcing, and Training.
65
65
 
@@ -67,7 +67,7 @@ Use `audienceType: 'synthetic'` only when the user explicitly asks for model-gen
67
67
 
68
68
  Treat `onboarding` as the node's Training course. It has `enabled`, `steps`, and `info` (`estimatedTime`, `role`, `goal`, `successCriteria`). Give every new human node a real course built from the instruction, criteria, UI, edge cases, and verified examples; the constructor's placeholder is not one. While it is enabled, validation requires non-empty `info.role`, `info.goal`, and `info.successCriteria`, three to six steps, and at least one quiz block; a course uses either a practice block or an examples block, not both, and an examples block needs a built UI. A practice block together with an enabled Exam draws a warning, because the Exam already tests experts on real tasks. Preserve an explicit `enabled: false`. Never invent golden answers or media URLs. Training teaches with retries; it is not an admission gate.
69
69
 
70
- 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 sets `mode: 'ground_truth' | 'quality_criteria' | 'not_graded'`, `groundTruthType: 'direct' | 'custom'`, `customInstruction` (at least 10 characters for custom ground truth, otherwise `''`), and `disabledCriterionIds` (`[]` to keep every criterion). 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.
70
+ 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 sets `mode: 'ground_truth' | 'quality_criteria' | 'not_graded'`, `groundTruthType: 'direct' | 'custom'`, `customInstruction` (at least 10 characters for custom ground truth, otherwise `''`), and `disabledCriterionIds` (`[]` to keep every criterion). 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. With `verifiedExperts: false`, the exam also limits how many experts can join the node. Deploy sizes that limit from the tasks the run estimate expects at this node (its expected items, times the overlap on a `quorum` node), the per-expert cap, and the measured exam pass rate, between 100 and 10,000. Behind a `match` router the estimate splits items evenly across the branches, so each branch gets a limit for its share, not for the whole run.
71
71
 
72
72
  The `ui` strings are authored directly. Build one expert's task view, ensure component value shapes match entity schemas, read inputs as [Task UI](task-ui.md) describes, and use `postprocess` when a component's native value needs reshaping. Hardware-backed upload and recording controls require real UI verification.
73
73
 
@@ -146,7 +146,7 @@ The authoring helper does not translate `offboardingRules.agreementScale` from `
146
146
 
147
147
  When a requirement depends on one of these capabilities, state the gap instead of inventing source syntax:
148
148
 
149
- - 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`.
149
+ - 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. `toloka validate` does report an audience that matches no experts, and states the allowed range when `pricePerTask` falls outside it. Specializations and their expert counts come from `toloka audience specialization list`.
150
150
  - Audience-reuse candidate discovery has no SDK or CLI path. Source can store a known cross-pipeline pointer, but cannot search candidates.
151
151
  - 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.
152
152
  - 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.
@@ -35,7 +35,7 @@ Run `toloka --help` and `toloka <command> --help` for the exact commands and opt
35
35
  - source reconciliation: `init`, `import`, `check`, `validate`, `plan`, `apply`, `status`, `secret set`;
36
36
  - catalogs: `audience specialization list`, `generation-model list`, `ui components`;
37
37
  - data and runs: `dataset` (`list`, `create`, `fields`, `fields add`, `columns`, `load`, `upload`, `split`, `slice`), `pipeline` (`list`, `run`, `status`, `runs`, `cost`, `results`, `cancel`);
38
- - models and training: `base-model`, `model` (including `model version download`), `training`;
38
+ - models and training: `base-model`, `model` (including `model version download`), `training`, `batch-inference` (`run`, `list`, `status`, `confirm`, `resume`, `cancel`);
39
39
  - skills and reports: `skill` (`list`, `search`, `install`), `report`.
40
40
 
41
41
  `toloka audience specialization list` prints the global specialization catalog: `ID`, `NAME`, and `EXPERTS` columns (`--json` for the array). Run it before setting `audience.specializations` on a node, pick entries by name, and copy `ID` into `qualificationId` and `NAME` into `value`; never invent a `qualificationId`. `EXPERTS` counts ID-verified experts currently eligible for tasks; a `0` means the id is valid but nobody currently qualifies, so prefer another specialization or widen the audience.
@@ -58,6 +58,10 @@ Hyperparameters are optional flags on `training create` and `training update`; t
58
58
 
59
59
  Use `training cost` to validate inputs and show the maximum charge before `training run`. A blocked estimate exits 1 and lists the issues on stderr instead of printing the estimate; fix them and recalculate. `training run` accepts that ready estimate JSON through `--estimate-file`; without it, the command calculates a fresh ready estimate itself before creating the run. `--version-name` defaults to the Training name; pass it when a Model Version with that name already exists. Use `training status`, `training logs`, `training metrics`, `training resume`, and `training cancel` with the returned Training Run ID. `training metrics` lists each metric's latest value while the run is still training; `--key` prints one metric's point history instead. `training cancel` before training starts is free; on a run that is already training it stops the GPU job, and the run is charged only for the GPU seconds used until then and produces no Model Version.
60
60
 
61
+ ## Answer dataset rows with a model
62
+
63
+ `toloka batch-inference run --dataset <id> (--base-model <id> | --model-version <id>) (--output-field <name> | --output-dataset <name>) --max-output-tokens <n>` sends each selected row's `messages` to a qwen3-4b or qwen3-8b Base Model, or a Model Version trained from one, and writes one answer per row into a text column or a new dataset. An existing `--output-field` column must allow strings; one whose schema allows none is refused with "does not accept text", so pick or create a text column. `--filter field=value`, `--slice <id>` and `--parts <names>` (with `--split-column` when the dataset has several) narrow the rows; a slice answers the prompts it froze. `run` waits for the quote and prints the rows and the maximum charge: show them to the user and confirm only with their approval, through `--yes` on `run` or `toloka batch-inference confirm <run-id>` within 24 hours. Follow a run with `status --wait`. A `blocked` run names its reason: after `funds` or `payment`, `resume` continues it once the user adds funds; `billing_review` waits for support to reconcile the charge, and `resume` does not apply to it. `cancel` stops a run and charges the GPU time used, and `list` shows the project's runs. A scoped token needs `inference:deploy` (the `deploy` preset) to run, confirm, resume or cancel. `toloka batch-inference <command> --help` lists every flag.
64
+
61
65
  ## Check locally and validate against the platform
62
66
 
63
67
  Run the complete local gate first, then validate every bound pipeline remotely:
@@ -149,7 +153,7 @@ When rows are added later, run `dataset split` again: it places only the new row
149
153
 
150
154
  ## Run a pipeline and report results
151
155
 
152
- A run needs an applied pipeline. Apply any requested source changes and verify convergence first; an already-applied unchanged pipeline does not need another apply. SKILL.md covers cost approval, exit codes, and bounded monitoring. The commands below are available operations, not a sequence that requires starting or canceling a run.
156
+ A run needs an applied pipeline. Apply any requested source changes and verify convergence first; an already-applied unchanged pipeline does not need another apply. It also needs at least one dataset item matching the root node's dataset and filter: otherwise `toloka pipeline run` fails with `No dataset items match, so a run would have nothing to process.` even though `apply` and `validate` succeeded. Upload items or widen the filter, then run again. SKILL.md covers cost approval, exit codes, and bounded monitoring. The commands below are available operations, not a sequence that requires starting or canceling a run.
153
157
 
154
158
  ```sh
155
159
  toloka pipeline run <name> --wait --timeout 900000
@@ -108,6 +108,14 @@ const COMMAND_GROUPS: Readonly<Record<string, CommandGroup>> = {
108
108
  metrics: true,
109
109
  logs: true,
110
110
  },
111
+ "batch-inference": {
112
+ run: true,
113
+ list: true,
114
+ status: true,
115
+ confirm: true,
116
+ resume: true,
117
+ cancel: true,
118
+ },
111
119
  "base-model": { list: true, get: true },
112
120
  "generation-model": { list: true },
113
121
  model: {
@@ -0,0 +1,267 @@
1
+ import { randomUUID } from "node:crypto";
2
+
3
+ import type { BatchInferenceRun, PotatoClient } from "../client";
4
+ import type { BaseModelId, CreateBatchInferenceRunBody } from "../../openapi/gen/models";
5
+ import { assertCli } from "./cli-error";
6
+ import { resolveFieldValueFilters } from "./filters";
7
+ import { resolveModelVersion } from "./models";
8
+ import { resolveSplitParts } from "./splits";
9
+ import { formatTable, type TableIo as BatchInferenceIo, writeOutput } from "./table";
10
+
11
+ type BatchInferenceApi = Pick<PotatoClient, "batchInference">;
12
+ type BatchInferenceStartApi = Pick<
13
+ PotatoClient,
14
+ "batchInference" | "datasets" | "models" | "baseModels"
15
+ >;
16
+
17
+ const QUOTE_WAIT_MS = 10 * 60 * 1_000;
18
+ const SETTLED_STATUSES: ReadonlySet<BatchInferenceRun["status"]> = new Set([
19
+ "awaiting_confirmation",
20
+ "blocked",
21
+ "completed",
22
+ "failed",
23
+ "canceled",
24
+ ]);
25
+
26
+ const sleep = (ms: number) =>
27
+ new Promise<void>((resolve) => {
28
+ setTimeout(resolve, ms);
29
+ });
30
+
31
+ export const formatBatchInferenceRun = (run: BatchInferenceRun): string =>
32
+ formatTable([
33
+ ["ID", run.id],
34
+ ["Status", run.status],
35
+ ["Dataset", run.datasetId],
36
+ ["Model", run.model.type === "base_model" ? run.model.baseModelId : run.model.modelVersionId],
37
+ [
38
+ "Output",
39
+ run.output.type === "field"
40
+ ? `field ${run.output.fieldId} on dataset ${run.output.datasetId}`
41
+ : `new dataset ${run.output.datasetId}`,
42
+ ],
43
+ ...(run.quote
44
+ ? [
45
+ ["Rows", String(run.quote.requests)],
46
+ ["Skipped rows", String(run.quote.skippedRows)],
47
+ ["Maximum charge", `$${run.quote.maximumCharge}`],
48
+ ...(run.quote.confirmBefore ? [["Confirm before", run.quote.confirmBefore]] : []),
49
+ ]
50
+ : []),
51
+ ...(run.progress?.requests
52
+ ? [["Progress", `${run.progress.requests.done}/${run.progress.requests.total} rows`]]
53
+ : []),
54
+ ...(run.chargedAmount ? [["Charged", `$${run.chargedAmount}`]] : []),
55
+ ...(run.result
56
+ ? [
57
+ ["Written", String(run.result.written)],
58
+ ["Failed", String(run.result.failed)],
59
+ ["Cut at max tokens", String(run.result.truncated)],
60
+ ["Tokens", `${run.result.inputTokens} in, ${run.result.outputTokens} out`],
61
+ ]
62
+ : []),
63
+ ...(run.message ? [["Message", run.message]] : []),
64
+ ]);
65
+
66
+ const waitUntil = async (
67
+ client: BatchInferenceApi,
68
+ batchInferenceRunId: string,
69
+ isReady: (run: BatchInferenceRun) => boolean,
70
+ pollIntervalMs: number,
71
+ timeoutMs: number,
72
+ ): Promise<BatchInferenceRun> => {
73
+ const deadline = Date.now() + timeoutMs;
74
+ for (;;) {
75
+ const run = await client.batchInference.getRun({ batchInferenceRunId });
76
+ if (isReady(run)) return run;
77
+ assertCli(Date.now() < deadline, `Batch Inference Run ${run.id} is still ${run.status}.`, {
78
+ hint: { description: `Check it later with toloka batch-inference status ${run.id}.` },
79
+ });
80
+ await sleep(pollIntervalMs);
81
+ }
82
+ };
83
+
84
+ export type StartBatchInferenceRunInput = {
85
+ projectId: string;
86
+ datasetId: string;
87
+ filter?: string;
88
+ slice?: string;
89
+ parts?: string[];
90
+ splitColumn?: string;
91
+ baseModel?: BaseModelId;
92
+ modelVersion?: string;
93
+ outputField?: string;
94
+ outputDataset?: string;
95
+ maxOutputTokens: number;
96
+ temperature?: number;
97
+ topP?: number;
98
+ stop: string[];
99
+ seed?: number;
100
+ samplesPerRow?: number;
101
+ idempotencyKey?: string;
102
+ yes: boolean;
103
+ json: boolean;
104
+ pollIntervalMs?: number;
105
+ };
106
+
107
+ const resolveItemFilter = async (
108
+ client: BatchInferenceStartApi,
109
+ input: StartBatchInferenceRunInput,
110
+ ): Promise<CreateBatchInferenceRunBody["itemFilter"]> => {
111
+ assertCli(
112
+ input.splitColumn === undefined || input.parts !== undefined,
113
+ "--split-column needs --parts.",
114
+ { hint: { description: "Pass --parts test to answer the rows of the test part." } },
115
+ );
116
+ const itemFilter = {
117
+ ...(input.filter !== undefined &&
118
+ (await resolveFieldValueFilters(
119
+ client,
120
+ { datasetId: input.datasetId, name: input.datasetId, nameIsUnique: false },
121
+ [input.filter],
122
+ ))),
123
+ ...(input.slice !== undefined && { slice: { sliceId: input.slice } }),
124
+ ...(input.parts !== undefined && {
125
+ parts: await resolveSplitParts(client, input.datasetId, {
126
+ splitColumn: input.splitColumn,
127
+ parts: input.parts,
128
+ }),
129
+ }),
130
+ };
131
+ return Object.keys(itemFilter).length > 0 ? itemFilter : undefined;
132
+ };
133
+
134
+ export const startBatchInferenceRun = async (
135
+ client: BatchInferenceStartApi,
136
+ input: StartBatchInferenceRunInput,
137
+ io: BatchInferenceIo,
138
+ ): Promise<void> => {
139
+ assertCli(
140
+ (input.baseModel === undefined) !== (input.modelVersion === undefined),
141
+ "Pass exactly one of --base-model or --model-version.",
142
+ {
143
+ hint: {
144
+ description: "Run toloka base-model list or toloka model versions <model> to pick one.",
145
+ },
146
+ },
147
+ );
148
+ assertCli(
149
+ (input.outputField === undefined) !== (input.outputDataset === undefined),
150
+ "Pass exactly one of --output-field or --output-dataset.",
151
+ {
152
+ hint: {
153
+ description:
154
+ "--output-field writes a column on the dataset; --output-dataset creates a new dataset with the prompts and answers.",
155
+ },
156
+ },
157
+ );
158
+ const itemFilter = await resolveItemFilter(client, input);
159
+ const model: CreateBatchInferenceRunBody["model"] = input.baseModel
160
+ ? { type: "base_model", baseModelId: input.baseModel }
161
+ : {
162
+ type: "model_version",
163
+ modelVersionId: (await resolveModelVersion(client, input.projectId, input.modelVersion!))
164
+ .id,
165
+ };
166
+ const created = await client.batchInference.createRun({
167
+ projectId: input.projectId,
168
+ idempotencyKey: input.idempotencyKey ?? randomUUID(),
169
+ datasetId: input.datasetId,
170
+ ...(itemFilter && { itemFilter }),
171
+ model,
172
+ sampling: {
173
+ maxOutputTokens: input.maxOutputTokens,
174
+ ...(input.temperature !== undefined && { temperature: input.temperature }),
175
+ ...(input.topP !== undefined && { topP: input.topP }),
176
+ ...(input.stop.length > 0 && { stop: input.stop }),
177
+ ...(input.seed !== undefined && { seed: input.seed }),
178
+ ...(input.samplesPerRow !== undefined && { samplesPerRow: input.samplesPerRow }),
179
+ },
180
+ output:
181
+ input.outputField === undefined
182
+ ? { type: "dataset", name: input.outputDataset! }
183
+ : { type: "field", fieldName: input.outputField },
184
+ });
185
+ const quoted = await waitUntil(
186
+ client,
187
+ created.id,
188
+ (run) => run.status !== "preparing",
189
+ input.pollIntervalMs ?? 1_000,
190
+ QUOTE_WAIT_MS,
191
+ );
192
+ assertCli(quoted.status === "awaiting_confirmation", formatBatchInferenceRun(quoted), {
193
+ hint: { description: "Fix the selection or the model, then start a new run." },
194
+ });
195
+ if (!input.yes) {
196
+ writeOutput(
197
+ io,
198
+ input.json,
199
+ quoted,
200
+ `${formatBatchInferenceRun(quoted)}\n\nConfirm the maximum charge with: toloka batch-inference confirm ${quoted.id}`,
201
+ );
202
+ return;
203
+ }
204
+ const confirmed = await client.batchInference.confirmRun({ batchInferenceRunId: quoted.id });
205
+ writeOutput(io, input.json, confirmed, formatBatchInferenceRun(confirmed));
206
+ };
207
+
208
+ export const listBatchInferenceRuns = async (
209
+ client: BatchInferenceApi,
210
+ input: { projectId: string; json: boolean },
211
+ io: BatchInferenceIo,
212
+ ): Promise<void> => {
213
+ const page = await client.batchInference.listRunsPage({
214
+ projectId: input.projectId,
215
+ limit: 100,
216
+ offset: 0,
217
+ });
218
+ writeOutput(
219
+ io,
220
+ input.json,
221
+ page.batchInferenceRuns,
222
+ page.batchInferenceRuns.length === 0
223
+ ? "No Batch Inference Runs found."
224
+ : formatTable([
225
+ ["ID", "STATUS", "DATASET", "MAXIMUM CHARGE", "CHARGED"],
226
+ ...page.batchInferenceRuns.map((run) => [
227
+ run.id,
228
+ run.status,
229
+ run.datasetId,
230
+ run.quote ? `$${run.quote.maximumCharge}` : "-",
231
+ run.chargedAmount ? `$${run.chargedAmount}` : "-",
232
+ ]),
233
+ ]),
234
+ );
235
+ };
236
+
237
+ export const getBatchInferenceRun = async (
238
+ client: BatchInferenceApi,
239
+ input: { batchInferenceRunId: string; wait: boolean; json: boolean; pollIntervalMs?: number },
240
+ io: BatchInferenceIo,
241
+ ): Promise<void> => {
242
+ const run = input.wait
243
+ ? await waitUntil(
244
+ client,
245
+ input.batchInferenceRunId,
246
+ (candidate) => SETTLED_STATUSES.has(candidate.status),
247
+ input.pollIntervalMs ?? 5_000,
248
+ Number.POSITIVE_INFINITY,
249
+ )
250
+ : await client.batchInference.getRun({ batchInferenceRunId: input.batchInferenceRunId });
251
+ writeOutput(io, input.json, run, formatBatchInferenceRun(run));
252
+ };
253
+
254
+ export const changeBatchInferenceRun = async (
255
+ client: BatchInferenceApi,
256
+ input: { batchInferenceRunId: string; action: "confirm" | "cancel" | "resume"; json: boolean },
257
+ io: BatchInferenceIo,
258
+ ): Promise<void> => {
259
+ const request = { batchInferenceRunId: input.batchInferenceRunId };
260
+ const run =
261
+ input.action === "confirm"
262
+ ? await client.batchInference.confirmRun(request)
263
+ : input.action === "cancel"
264
+ ? await client.batchInference.cancelRun(request)
265
+ : await client.batchInference.resumeRun(request);
266
+ writeOutput(io, input.json, run, formatBatchInferenceRun(run));
267
+ };