@toloka-platform/sdk 0.16.0 → 0.17.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.
Binary file
@@ -1 +1 @@
1
- d657cb43672cd6deb574d75ed2a1c7b4a14d36d5b02a833d118bb0cd8d6634e7 toloka.js.jsc
1
+ a7a6b3868d2f0f14340b150ed0d9efedb8278c047f82f0afff0243fd9fe9046a toloka.js.jsc
@@ -1 +1 @@
1
- 59d0e496f8f496fc0fe031360beb5ee033d0eff186c7004914a659d904483cf6 toloka.js
1
+ 7d9c82a638b5ff48a9896b460a80a1b63b1aeda82890520f735f248ad5403422 toloka.js
package/docs/cli.md CHANGED
@@ -343,7 +343,7 @@ toloka pipeline run [name] [--wait] [--timeout <ms>]
343
343
  toloka pipeline status [name]
344
344
  toloka pipeline runs [name] [--limit <n>]
345
345
  toloka pipeline cost [name] [--item-limit <n>]
346
- toloka pipeline cancel <runId>
346
+ toloka pipeline cancel <runId> [--accept-without-review]
347
347
  toloka pipeline results <runId> [--limit <n>] [--failed]
348
348
  ```
349
349
 
@@ -3865,6 +3865,11 @@ export type ListPipelineRuns200 = {
3865
3865
  pagination: Pagination;
3866
3866
  };
3867
3867
 
3868
+ export type CancelPipelineRunBodyPendingHumanQa = 'accept_without_review' | 'wait_for_review';
3869
+ export type CancelPipelineRunBody = {
3870
+ pendingHumanQa?: CancelPipelineRunBodyPendingHumanQa;
3871
+ };
3872
+
3868
3873
  export type SampleDatasetItemsBody = {
3869
3874
  markField: string;
3870
3875
  markValue: string;
@@ -6,6 +6,7 @@ import type {
6
6
  BaseModelId,
7
7
  BatchInferenceRun,
8
8
  BatchInferenceRunId,
9
+ CancelPipelineRunBody,
9
10
  CheckJob,
10
11
  CheckRunId,
11
12
  CopyPipelineBody,
@@ -1702,14 +1703,29 @@ export const getCancelPipelineRunUrl = (runId: RunId,) => {
1702
1703
  return `/api/v2-beta/runs/${runId}/cancel`
1703
1704
  }
1704
1705
 
1705
- export const cancelPipelineRun = async (runId: RunId, options?: Parameters<typeof potatoFetch>[1]): Promise<RunJob> => {
1706
+ export const cancelPipelineRun = async (runId: RunId,
1707
+ cancelPipelineRunBody: CancelPipelineRunBody, options?: Parameters<typeof potatoFetch>[1]): Promise<RunJob> => {
1706
1708
 
1707
- return potatoFetch<RunJob>(getCancelPipelineRunUrl(runId),
1709
+ const getHeaders = (h?: NonNullable<RequestInit['headers']>): Record<string, string | readonly string[]> => {
1710
+ if (!h) return {};
1711
+ if (h instanceof Headers) return Object.fromEntries(h.entries());
1712
+ if (Symbol.iterator in h) {
1713
+ return Object.fromEntries(
1714
+ Array.from(h as Iterable<Iterable<string>>, (entry) => Array.from(entry) as [string, string]),
1715
+ );
1716
+ }
1717
+ const headers: Record<string, string | readonly string[]> = {};
1718
+ for (const [name, value] of Object.entries<string | readonly string[] | undefined>(h)) {
1719
+ if (value !== undefined) headers[name] = value;
1720
+ }
1721
+ return headers;
1722
+ };
1723
+ return potatoFetch<RunJob>(getCancelPipelineRunUrl(runId),
1708
1724
  {
1709
1725
  ...options,
1710
- method: 'POST'
1711
-
1712
-
1726
+ method: 'POST',
1727
+ headers: { 'Content-Type': 'application/json', ...getHeaders(options?.headers) },
1728
+ body: JSON.stringify(cancelPipelineRunBody)
1713
1729
  }
1714
1730
  );}
1715
1731
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toloka-platform/sdk",
3
- "version": "0.16.0",
3
+ "version": "0.17.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": "1b8cbf8c1fd6ce6a32b5c8de98a7772fdcc3afe3"
80
+ "commit": "df3f866a1ee20326bfdb49cd547690d352447b1d"
81
81
  }
82
82
  }
@@ -11,8 +11,8 @@
11
11
  "testing",
12
12
  "checks"
13
13
  ],
14
- "version": "0.16.0",
15
- "digest": "d029c12c76c569ad6ed9f12713890344e82c35445ba0f310d3aa2adf6f0610a5",
14
+ "version": "0.17.1",
15
+ "digest": "4636c4e21c9fc07a4cb402279574b88d54f7ee2db1e4cc8798c3ef9aeccea70c",
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.16.0",
36
+ "version": "0.17.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.16.0",
53
+ "version": "0.17.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.16.0",
70
+ "version": "0.17.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.16.0",
87
+ "version": "0.17.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` 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). A reviewer keeps a Human QA task for `humanQaTimeForTask` plus 1 hour, and for at least 8 hours. A dropped task stays with that reviewer until this time ends, then another reviewer can take it; `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. Before deploy, the platform counts the QA pool: experts who match the node's audience and hold the platform QA qualification and were active in the last 14 days, or have recent QA experience (at least 1 completed QA task and a recent visit; Smart Search invites these at deploy, except on a node that reuses an earlier audience or keeps its workforce request closed). The data is rebuilt once a day. With none, validation returns a blocking error and the deploy is rejected. The reviewers come from the node's own audience, because a node has no separate reviewer audience. So for that error, offer to widen the audience (drop specializations first, then countries) or to switch to `llm_only`; do not suggest waiting, because the count is refreshed once a day and nothing shows whether more reviewers will become active. When the count is too small for the node's expected reviews (the platform assumes that 1 of 10 of these experts reviews, for up to 12 hours each), `toloka validate` returns a `severity: 'warning'` issue on the node whose message starts with `Human QA on "<node name>" may stall`. The warning does not block the deploy. Tell the user the numbers in the message and offer its remedies: widen the audience or switch to `llm_only` (or to `hybrid` when the node is `human_only`); change none of them without the user's approval. A node that reuses the human reviewers of an earlier deployment skips this count. 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). A reviewer keeps a Human QA task for `humanQaTimeForTask` plus 1 hour, and for at least 8 hours. A dropped task stays with that reviewer until this time ends, then another reviewer can take it; `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 Maximum of its Human QA line in the frozen estimate: 3 reviews for each item of the run. The cap counts every item of the run, so a branch behind a `match` that receives more than its even share still gets Human QA. Before deploy, the platform counts the QA pool: experts who match the node's audience and hold the platform QA qualification and were active in the last 14 days, or have recent QA experience (at least 1 completed QA task and a recent visit; Smart Search invites these at deploy, except on a node that reuses an earlier audience or keeps its workforce request closed). The data is rebuilt once a day. With none, validation returns a blocking error and the deploy is rejected. The reviewers come from the node's own audience, because a node has no separate reviewer audience. So for that error, offer to widen the audience (drop specializations first, then countries) or to switch to `llm_only`; do not suggest waiting, because the count is refreshed once a day and nothing shows whether more reviewers will become active. When the count is too small for the node's expected reviews (the platform assumes that 1 of 10 of these experts reviews, for up to 12 hours each), `toloka validate` returns a `severity: 'warning'` issue on the node whose message starts with `Human QA on "<node name>" may stall`. The warning does not block the deploy. Tell the user the numbers in the message and offer its remedies: widen the audience or switch to `llm_only` (or to `hybrid` when the node is `human_only`); change none of them without the user's approval. A node that reuses the human reviewers of an earlier deployment skips this count. 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
 
@@ -42,7 +42,7 @@ Run `toloka --help` and `toloka <command> --help` for the exact commands and opt
42
42
 
43
43
  `toloka generation-model list` prints the synthetic generation models: `ID`, `NAME`, the supported `REASONING EFFORT` values with the default marked, and the `TYPICAL`, `MIN` and `MAX` cost of one attempt, followed by `Default model:` and, when there are any, `Recommended:` lines (`--json` adds descriptions). Run it before configuring a synthetic node.
44
44
 
45
- `toloka pipeline cost [name] [--item-limit <n>]` forecasts what a run of a live, validated pipeline would bill. It does not start a run or change the graph. If any billed node is marked `skipped`, the displayed total is incomplete and must not be presented as the pipeline price. Compute and label a labor floor for each skipped billed node as `items × pricePerTask × annotations per item`, using `1` for generation and `overlap` for quorum. Also report `maxEntities`, quality-criteria count, and only exclusions applicable to the authored QA and workflow. Apply accepting a rate does not prove its final billed value. If the output lists warnings, include them when you present the forecast.
45
+ `toloka pipeline cost [name] [--item-limit <n>]` forecasts what a run of a live, validated pipeline would bill. It does not start a run or change the graph. If any billed node is marked `skipped`, the displayed total is incomplete and must not be presented as the pipeline price. Compute and label a labor floor for each skipped billed node as `items × pricePerTask × annotations per item`, using `1` for generation and `overlap` for quorum. Also report `maxEntities`, quality-criteria count, and only exclusions applicable to the authored QA and workflow. Apply accepting a rate does not prove its final billed value.
46
46
 
47
47
  ## Manage model checkpoints
48
48
 
@@ -162,9 +162,11 @@ toloka pipeline runs <name> --limit 10
162
162
  toloka pipeline status <name>
163
163
  toloka pipeline results <runId> --limit 50
164
164
  toloka pipeline results <runId> --failed
165
- toloka pipeline cancel <runId>
165
+ toloka pipeline cancel <runId> [--accept-without-review]
166
166
  ```
167
167
 
168
+ By default, `toloka pipeline cancel` keeps the items that wait for Human QA in Human QA, and they end with the review result; after 14 days on the node, they are accepted without review. Add `--accept-without-review` only when the user asks to end these items now without review; the user pays for them as for completed items.
169
+
168
170
  `toloka pipeline runs` lists ids and timestamps. `toloka pipeline status` reports the latest run. `toloka status` (no prefix) is project health. When a listing is capped it says so; report the cap rather than presenting a page as the whole picture.
169
171
 
170
172
  A failed item carries an error, so `toloka pipeline results <runId> --failed` is the fastest path to why a run failed. Report counts and states from that output rather than inferring them from the run having started.
@@ -131,10 +131,18 @@ export const registerPipelineCommands = (program: Command, host: CommandHost): v
131
131
  pipeline
132
132
  .command("cancel")
133
133
  .argument("<runId>", "run to cancel")
134
+ .option(
135
+ "--accept-without-review",
136
+ "accept items in Human QA without review now; you pay for them as for completed items",
137
+ false,
138
+ )
134
139
  .description("request cancellation of a run")
135
- .action(async (runId: string) => {
140
+ .action(async (runId: string, options: { acceptWithoutReview: boolean }) => {
136
141
  const { client } = await resolveContext(globals(), context);
137
- const result = await cancelPipelineRun(client, { runId });
142
+ const result = await cancelPipelineRun(client, {
143
+ runId,
144
+ acceptWithoutReview: options.acceptWithoutReview,
145
+ });
138
146
  context.io.stdout(result.output);
139
147
  });
140
148
  pipeline
package/src/cli/cost.ts CHANGED
@@ -56,10 +56,9 @@ const collectLineRows = (line: CostLine, depth: number): TableRow[] => {
56
56
 
57
57
  const collectNodeRows = (
58
58
  nodes: PipelineCostEstimate["nodes"],
59
- ): { billed: TableRow[]; unpriced: string[]; warnings: string[] } => {
59
+ ): { billed: TableRow[]; unpriced: string[] } => {
60
60
  const billed: TableRow[] = [];
61
61
  const unpriced: string[] = [];
62
- const warnings: string[] = [];
63
62
 
64
63
  for (const node of nodes) {
65
64
  if (node.kind === "priced") {
@@ -67,7 +66,6 @@ const collectNodeRows = (
67
66
  for (const line of node.displayBreakdown) {
68
67
  if (isCostLine(line)) billed.push(...collectLineRows(line, 1));
69
68
  }
70
- if (node.warning !== undefined) warnings.push(` ${node.nodeNameDisplay}: ${node.warning}`);
71
69
  continue;
72
70
  }
73
71
 
@@ -78,7 +76,7 @@ const collectNodeRows = (
78
76
  );
79
77
  }
80
78
 
81
- return { billed, unpriced, warnings };
79
+ return { billed, unpriced };
82
80
  };
83
81
 
84
82
  const collectTotalRows = (total: PipelineCostEstimate["total"]): TableRow[] => {
@@ -102,7 +100,7 @@ export const formatCostEstimate = (
102
100
  estimate: PipelineCostEstimate,
103
101
  pipelineName: string,
104
102
  ): string => {
105
- const { billed, unpriced, warnings } = collectNodeRows(estimate.nodes);
103
+ const { billed, unpriced } = collectNodeRows(estimate.nodes);
106
104
  const totals = collectTotalRows(estimate.total);
107
105
  const table = formatTable([["Item", "Expected", "Maximum"], ...billed, ...totals]);
108
106
  const billedRows = table.slice(0, 1 + billed.length);
@@ -124,10 +122,6 @@ export const formatCostEstimate = (
124
122
  output.push("", "Not billed", ...unpriced);
125
123
  }
126
124
 
127
- if (warnings.length > 0) {
128
- output.push("", "Warnings", ...warnings);
129
- }
130
-
131
125
  if (estimate.isInternalPricing === true) {
132
126
  output.push("", "Internal-team pricing.");
133
127
  }
package/src/cli/runs.ts CHANGED
@@ -520,9 +520,12 @@ export async function pipelineRunStatus(
520
520
 
521
521
  export async function cancelPipelineRun(
522
522
  client: PotatoClient,
523
- options: { readonly runId: string },
523
+ options: { readonly runId: string; readonly acceptWithoutReview: boolean },
524
524
  ): Promise<RunCommandResult> {
525
- const canceled = await client.runs.cancel({ runId: options.runId });
525
+ const canceled = await client.runs.cancel({
526
+ runId: options.runId,
527
+ pendingHumanQa: options.acceptWithoutReview ? "accept_without_review" : undefined,
528
+ });
526
529
 
527
530
  return {
528
531
  outcome: "canceled",
package/src/client.ts CHANGED
@@ -5,6 +5,7 @@ import type {
5
5
  AddDatasetItemsBody,
6
6
  AttachPipelineDatasetBody,
7
7
  BatchInferenceRun as GeneratedBatchInferenceRun,
8
+ CancelPipelineRunBody,
8
9
  CheckJob as GeneratedCheckJob,
9
10
  CopyPipelineBody,
10
11
  CreateBatchInferenceRunBody,
@@ -734,7 +735,8 @@ const createClientImplementation = (config: ClientConfig) => {
734
735
  start: ({ pipelineId }: { pipelineId: PipelineId }) =>
735
736
  wire.createPipelineRun(pipelineId, requestOptions),
736
737
  get: ({ runId }: { runId: RunId }) => wire.getPipelineRun(runId, requestOptions),
737
- cancel: ({ runId }: { runId: RunId }) => wire.cancelPipelineRun(runId, requestOptions),
738
+ cancel: ({ runId, ...body }: { runId: RunId } & CancelPipelineRunBody) =>
739
+ wire.cancelPipelineRun(runId, body, requestOptions),
738
740
  listPage: ({ pipelineId, ...params }: { pipelineId: PipelineId } & ListPipelineRunsParams) =>
739
741
  wire.listPipelineRuns(pipelineId, params, requestOptions),
740
742
  listAll: ({ pipelineId, ...params }: { pipelineId: PipelineId } & ListPipelineRunsParams) =>