@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.
- package/dist/cli/toloka.js +66 -66
- package/dist/cli/toloka.js.jsc +0 -0
- package/dist/cli/toloka.js.jsc.sha256 +1 -1
- package/dist/cli/toloka.js.sha256 +1 -1
- package/docs/cli.md +1 -1
- package/openapi/gen/api.schemas.ts +5 -0
- package/openapi/gen/api.ts +21 -5
- package/package.json +2 -2
- package/skills/catalog.json +6 -6
- package/skills/toloka-build/references/human-work.md +1 -1
- package/skills/toloka-build/references/workflow-and-cli.md +4 -2
- package/src/cli/commands/pipeline.ts +10 -2
- package/src/cli/cost.ts +3 -9
- package/src/cli/runs.ts +5 -2
- package/src/client.ts +3 -1
package/dist/cli/toloka.js.jsc
CHANGED
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
a7a6b3868d2f0f14340b150ed0d9efedb8278c047f82f0afff0243fd9fe9046a toloka.js.jsc
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
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;
|
package/openapi/gen/api.ts
CHANGED
|
@@ -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,
|
|
1706
|
+
export const cancelPipelineRun = async (runId: RunId,
|
|
1707
|
+
cancelPipelineRunBody: CancelPipelineRunBody, options?: Parameters<typeof potatoFetch>[1]): Promise<RunJob> => {
|
|
1706
1708
|
|
|
1707
|
-
|
|
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.
|
|
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": "
|
|
80
|
+
"commit": "df3f866a1ee20326bfdb49cd547690d352447b1d"
|
|
81
81
|
}
|
|
82
82
|
}
|
package/skills/catalog.json
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"testing",
|
|
12
12
|
"checks"
|
|
13
13
|
],
|
|
14
|
-
"version": "0.
|
|
15
|
-
"digest": "
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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, {
|
|
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[]
|
|
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
|
|
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
|
|
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({
|
|
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 }) =>
|
|
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) =>
|