@toloka-platform/sdk 0.1.0-preview.5 → 0.1.0-preview.7

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 (127) hide show
  1. package/README.md +7 -11
  2. package/dist/cli/toloka.js +211 -125
  3. package/dist/cli/toloka.js.jsc +0 -0
  4. package/dist/cli/toloka.js.jsc.sha256 +1 -1
  5. package/dist/cli/toloka.js.sha256 +1 -1
  6. package/docs/authoring.md +17 -4
  7. package/docs/cli-v2.md +3 -3
  8. package/docs/cli.md +55 -17
  9. package/docs/datasets.md +8 -9
  10. package/docs/getting-started.md +7 -12
  11. package/docs/limitations.md +10 -13
  12. package/docs/testing-examples.md +248 -0
  13. package/docs/testing.md +115 -0
  14. package/docs/troubleshooting.md +0 -11
  15. package/examples/face-image-collection/README.md +7 -1
  16. package/examples/face-image-collection/fields.ts +20 -8
  17. package/examples/face-image-collection/nodes/annotate-faces/node.ts +1 -9
  18. package/examples/face-image-collection/nodes/order-faces/node.ts +1 -1
  19. package/examples/face-image-collection/pipeline.browser.integration.spec.ts +78 -0
  20. package/examples/video-pair-rating/README.md +14 -6
  21. package/examples/video-pair-rating/nodes/adjudicate/node.ts +1 -3
  22. package/examples/video-pair-rating/nodes/adjudicate_op/node.ts +1 -3
  23. package/examples/video-pair-rating/nodes/overall_preference_pick/node.ts +1 -1
  24. package/examples/video-pair-rating/nodes/rate_pair/node.ts +1 -1
  25. package/examples/video-pair-rating/pipeline.browser.integration.spec.ts +267 -0
  26. package/openapi/gen/api.ts +819 -898
  27. package/openapi/gen/models/baseModel.ts +9 -0
  28. package/openapi/gen/models/baseModelId.ts +2 -0
  29. package/openapi/gen/models/baseModelIdProperty.ts +2 -0
  30. package/openapi/gen/models/createDatasetExportBody.ts +3 -3
  31. package/openapi/gen/models/createDatasetExportBodyOneOf.ts +6 -0
  32. package/openapi/gen/models/createDatasetExportBodyOneOfFormat.ts +2 -0
  33. package/openapi/gen/models/createDatasetExportBodyOneOfThree.ts +10 -0
  34. package/openapi/gen/models/createDatasetExportBodyOneOfThreeContract.ts +2 -0
  35. package/openapi/gen/models/createDatasetExportBodyOneOfThreeFormat.ts +2 -0
  36. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilter.ts +9 -0
  37. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItem.ts +6 -0
  38. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItemSourcesItem.ts +4 -0
  39. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItemSourcesItemOneOf.ts +5 -0
  40. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItemSourcesItemOneOfTwo.ts +5 -0
  41. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItem.ts +12 -0
  42. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemOperator.ts +2 -0
  43. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemSourcesItem.ts +4 -0
  44. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemSourcesItemOneOf.ts +5 -0
  45. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemSourcesItemOneOfTwo.ts +5 -0
  46. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemValue.ts +2 -0
  47. package/openapi/gen/models/createProjectModelBody.ts +8 -0
  48. package/openapi/gen/models/createProjectModelBodyDescription.ts +2 -0
  49. package/openapi/gen/models/datasetExportReport.ts +9 -0
  50. package/openapi/gen/models/datasetExportReportErrorsItem.ts +5 -0
  51. package/openapi/gen/models/datasetExportReportTokens.ts +7 -0
  52. package/openapi/gen/models/exportJob.ts +2 -0
  53. package/openapi/gen/models/exportJobReport.ts +3 -0
  54. package/openapi/gen/models/generationNodeSettingsAudienceAndPricingOneOfOneone.ts +1 -0
  55. package/openapi/gen/models/index.ts +64 -0
  56. package/openapi/gen/models/listBaseModels200.ts +5 -0
  57. package/openapi/gen/models/listModelVersions200.ts +7 -0
  58. package/openapi/gen/models/listModelVersionsParams.ts +5 -0
  59. package/openapi/gen/models/listProjectModels200.ts +7 -0
  60. package/openapi/gen/models/listProjectModelsParams.ts +5 -0
  61. package/openapi/gen/models/model.ts +16 -0
  62. package/openapi/gen/models/modelArchivedAt.ts +2 -0
  63. package/openapi/gen/models/modelDescription.ts +2 -0
  64. package/openapi/gen/models/modelId.ts +2 -0
  65. package/openapi/gen/models/modelVersion.ts +9 -0
  66. package/openapi/gen/models/modelVersionId.ts +2 -0
  67. package/openapi/gen/models/pipelineGraphSnapshotGraphNodesItemOneOfThreeoneSettingsAudienceAndPricingOneOfOneone.ts +1 -0
  68. package/openapi/gen/models/sampleDatasetItemsBody.ts +4 -0
  69. package/openapi/gen/models/sampleDatasetItemsBodyAnyOf.ts +8 -0
  70. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilter.ts +9 -0
  71. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItem.ts +6 -0
  72. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItemSourcesItem.ts +4 -0
  73. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItemSourcesItemOneOf.ts +5 -0
  74. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItemSourcesItemOneOfTwo.ts +5 -0
  75. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItem.ts +12 -0
  76. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemOperator.ts +2 -0
  77. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemSourcesItem.ts +4 -0
  78. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemSourcesItemOneOf.ts +5 -0
  79. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemSourcesItemOneOfTwo.ts +5 -0
  80. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemValue.ts +2 -0
  81. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethree.ts +8 -0
  82. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilter.ts +9 -0
  83. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItem.ts +6 -0
  84. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItemSourcesItem.ts +4 -0
  85. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItemSourcesItemOneOf.ts +5 -0
  86. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItemSourcesItemOneOfTwo.ts +5 -0
  87. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItem.ts +12 -0
  88. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemOperator.ts +2 -0
  89. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemSourcesItem.ts +4 -0
  90. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemSourcesItemOneOf.ts +5 -0
  91. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemSourcesItemOneOfTwo.ts +5 -0
  92. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemValue.ts +2 -0
  93. package/openapi/gen/models/sampleJob.ts +13 -0
  94. package/openapi/gen/models/sampleJobStatus.ts +2 -0
  95. package/openapi/gen/models/sampleResult.ts +5 -0
  96. package/openapi/gen/models/validatePipelineGraphBodyGraphNodesItemOneOfThreeoneSettingsAudienceAndPricingOneOfOneone.ts +1 -0
  97. package/package.json +23 -2
  98. package/skills/catalog.json +14 -6
  99. package/skills/toloka-build/SKILL.md +34 -12
  100. package/skills/toloka-build/references/node-semantics.md +27 -24
  101. package/skills/toloka-build/references/workflow-and-cli.md +67 -27
  102. package/skills/toloka-report-issue/references/report-template.md +1 -1
  103. package/src/checks/ast.ts +32 -0
  104. package/src/checks/index.ts +14 -0
  105. package/src/checks/postprocess-collisions.ts +145 -0
  106. package/src/checks/task-ui-declarations.generated.ts +19 -0
  107. package/src/checks/task-ui-typecheck.ts +345 -0
  108. package/src/checks/typescript-vfs.d.ts +26 -0
  109. package/src/checks/typescript-vfs.js +5 -0
  110. package/src/cli/analytics.ts +1 -0
  111. package/src/cli/check.ts +977 -0
  112. package/src/cli/data.ts +148 -25
  113. package/src/cli/models.ts +188 -0
  114. package/src/cli/program.ts +246 -20
  115. package/src/cli/project-context.ts +14 -4
  116. package/src/cli/runs.ts +21 -11
  117. package/src/cli/skill.ts +17 -6
  118. package/src/client.ts +109 -35
  119. package/src/errors.ts +25 -10
  120. package/src/testing/code-node.ts +160 -0
  121. package/src/testing/index.ts +15 -0
  122. package/src/testing/task-ui-browser.ts +120 -0
  123. package/src/testing/task-ui-validation.ts +135 -0
  124. package/src/testing/task-ui.ts +557 -0
  125. package/src/testing/test-evidence.ts +170 -0
  126. package/src/testing/types.ts +141 -0
  127. package/src/transport.ts +16 -13
@@ -0,0 +1,267 @@
1
+ import { expect, test } from "@toloka-platform/sdk/testing";
2
+ import { describe } from "vitest";
3
+
4
+ import pipeline from "./pipeline";
5
+ import { adjudicate } from "./nodes/adjudicate/node";
6
+ import { adjudicateOp } from "./nodes/adjudicate_op/node";
7
+ import { adjudicationMatch } from "./nodes/adjudication_match/node";
8
+ import { assemble } from "./nodes/assemble/node";
9
+ import { cascade } from "./nodes/cascade/node";
10
+ import { finalizeOverall } from "./nodes/finalize_overall/node";
11
+ import { mergeAdjudication } from "./nodes/merge_adjudication/node";
12
+ import { mergeOpAdjudication } from "./nodes/merge_op_adjudication/node";
13
+ import { opQuorumResolve } from "./nodes/op_quorum_resolve/node";
14
+ import { overallPreferencePick } from "./nodes/overall_preference_pick/node";
15
+ import { ratePair } from "./nodes/rate_pair/node";
16
+ import { shuffleAb } from "./nodes/shuffle_ab/node";
17
+
18
+ const item = {
19
+ prompt: "a fox crossing a snowy field",
20
+ video_a: "https://example.com/video-a.mp4",
21
+ video_b: "https://example.com/video-b.mp4",
22
+ };
23
+ const metricNames = [
24
+ "image_text_alignment",
25
+ "motion_text_alignment",
26
+ "image_quality",
27
+ "motion_quality",
28
+ "subject_background_consistency",
29
+ "video_realism",
30
+ ];
31
+ const canonicalMetrics = Object.fromEntries(metricNames.map((name) => [name, "video_a"]));
32
+ const metricVotes = Object.fromEntries(metricNames.map((name) => [name, ["video_1", "video_1"]]));
33
+
34
+ describe("video pair rating example", () => {
35
+ test.code(pipeline, shuffleAb, "shuffles a pair deterministically", async ({ run }) => {
36
+ const result = await run(item);
37
+
38
+ expect(result).toMatchObject({
39
+ record_id: expect.stringMatching(/^[a-f0-9]{16}$/u),
40
+ swap: expect.any(Boolean),
41
+ });
42
+ expect(new Set([result.left_video, result.right_video])).toEqual(
43
+ new Set([item.video_a, item.video_b]),
44
+ );
45
+ });
46
+
47
+ test.code(pipeline, cascade, "resolves unanimous metric votes", async ({ run }) => {
48
+ await expect(run({ swap: false, ...metricVotes })).resolves.toMatchObject({
49
+ per_metric_resolved: canonicalMetrics,
50
+ needs_adjudication: "false",
51
+ op_value_or_null: "video_a",
52
+ op_source: "alignment",
53
+ needs_overall_manual: "false",
54
+ });
55
+ });
56
+
57
+ test.code(pipeline, mergeAdjudication, "preserves agreed metrics", async ({ run }) => {
58
+ await expect(
59
+ run({
60
+ per_metric_resolved: canonicalMetrics,
61
+ disagreed_metrics: [],
62
+ }),
63
+ ).resolves.toMatchObject({
64
+ final_metrics: canonicalMetrics,
65
+ resolution_method: "agreement",
66
+ op_value_or_null: "video_a",
67
+ needs_overall_manual: "false",
68
+ });
69
+ });
70
+
71
+ test.code(pipeline, opQuorumResolve, "resolves an overall majority", async ({ run }) => {
72
+ await expect(
73
+ run({
74
+ ...item,
75
+ swap: false,
76
+ overall_manual_votes: ["video_a", "video_a", "video_b"],
77
+ }),
78
+ ).resolves.toMatchObject({
79
+ op_manual_value: "video_a",
80
+ op_needs_adjudication: "false",
81
+ rater_op_votes: ["video_a", "video_a", "video_b"],
82
+ });
83
+ });
84
+
85
+ test.code(pipeline, mergeOpAdjudication, "uses the overall quorum majority", async ({ run }) => {
86
+ await expect(
87
+ run({ op_needs_adjudication: "false", op_manual_value: "video_a" }),
88
+ ).resolves.toEqual({
89
+ final_op_value: "video_a",
90
+ final_op_resolution_method: "agreement",
91
+ final_op_adjudicator_id: null,
92
+ });
93
+ });
94
+
95
+ test.code(pipeline, finalizeOverall, "finalizes a cascade preference", async ({ run }) => {
96
+ await expect(
97
+ run({
98
+ op_value_or_null: "video_a",
99
+ op_source: "alignment",
100
+ needs_overall_manual: "false",
101
+ final_metrics: canonicalMetrics,
102
+ resolution_method: "agreement",
103
+ annotator_id_adjudicator: null,
104
+ }),
105
+ ).resolves.toEqual({
106
+ overall_preference: "video_a",
107
+ overall_preference_source: "alignment",
108
+ resolution_method: "agreement",
109
+ annotator_id_adjudicator: null,
110
+ });
111
+ });
112
+
113
+ test.code(
114
+ pipeline,
115
+ adjudicationMatch,
116
+ "grades raters against an adjudicated metric",
117
+ async ({ run }) => {
118
+ await expect(
119
+ run({
120
+ rater_ita_votes: ["video_a", "video_b"],
121
+ rater_iq_votes: ["video_a", "video_a"],
122
+ disagreed_metrics: ["image_text_alignment"],
123
+ final_metrics: canonicalMetrics,
124
+ needs_overall_manual: "false",
125
+ }),
126
+ ).resolves.toEqual({
127
+ rater_ita_matches_adjudication: [true, false],
128
+ rater_iq_matches_adjudication: null,
129
+ rater_op_matches_adjudication: null,
130
+ });
131
+ },
132
+ );
133
+
134
+ test.code(pipeline, assemble, "assembles the delivery record", async ({ run }) => {
135
+ await expect(
136
+ run({
137
+ record_id: "record-1",
138
+ ...item,
139
+ final_metrics: canonicalMetrics,
140
+ overall_preference: "video_a",
141
+ overall_preference_source: "alignment",
142
+ resolution_method: "agreement",
143
+ annotator_id_adjudicator: null,
144
+ rater_ita_votes: ["video_a", "video_a"],
145
+ rater_mta_votes: ["video_a", "video_a"],
146
+ rater_iq_votes: ["video_a", "video_a"],
147
+ rater_mq_votes: ["video_a", "video_a"],
148
+ rater_sbc_votes: ["video_a", "video_a"],
149
+ rater_vr_votes: ["video_a", "video_a"],
150
+ rater_ita_matches_adjudication: null,
151
+ rater_iq_matches_adjudication: null,
152
+ rater_op_matches_adjudication: null,
153
+ }),
154
+ ).resolves.toMatchObject({
155
+ delivery_record: {
156
+ record_id: "record-1",
157
+ video_a_url: item.video_a,
158
+ video_b_url: item.video_b,
159
+ overall_preference: "video_a",
160
+ timestamp: expect.stringMatching(/Z$/u),
161
+ },
162
+ });
163
+ });
164
+
165
+ test.ui(pipeline, ratePair, "submits independent metric ratings", async ({ render }) => {
166
+ const session = await render({
167
+ data: { prompt: item.prompt, left_video: item.video_a, right_video: item.video_b },
168
+ });
169
+ for (const metric of metricNames) await session.setValue(metric, "video_1");
170
+
171
+ await expect(session.submit()).resolves.toMatchObject(
172
+ Object.fromEntries(metricNames.map((name) => [name, "video_1"])),
173
+ );
174
+ await session.close();
175
+ });
176
+
177
+ test.ui(pipeline, adjudicate, "submits disputed metric decisions", async ({ render }) => {
178
+ const session = await render({
179
+ data: { ...item, disagreed_metrics: ["image_quality", "motion_quality"] },
180
+ });
181
+ await session.setValue("adjudicated_metrics.image_quality", "video_a");
182
+ await session.setValue("adjudicated_metrics.motion_quality", "video_b");
183
+
184
+ await expect(session.submit()).resolves.toMatchObject({
185
+ adjudicated_metrics: { image_quality: "video_a", motion_quality: "video_b" },
186
+ });
187
+ await session.close();
188
+ });
189
+
190
+ test.ui(
191
+ pipeline,
192
+ overallPreferencePick,
193
+ "submits an overall preference vote",
194
+ async ({ render }) => {
195
+ const session = await render({ data: item });
196
+ await session.setValue("overall_manual_votes", "video_a");
197
+
198
+ await expect(session.submit()).resolves.toMatchObject({ overall_manual_votes: "video_a" });
199
+ await session.close();
200
+ },
201
+ );
202
+
203
+ test.ui(pipeline, adjudicateOp, "submits an overall adjudication", async ({ render }) => {
204
+ const session = await render({ data: item });
205
+ await session.setValue("op_adjudicated_value", "video_b");
206
+
207
+ await expect(session.submit()).resolves.toMatchObject({ op_adjudicated_value: "video_b" });
208
+ await session.close();
209
+ });
210
+
211
+ test.pipeline(pipeline, "rates and assembles an agreed pair", async ({ renderUi, runCode }) => {
212
+ const shuffled = await runCode(shuffleAb, item);
213
+ const session = await renderUi(ratePair, {
214
+ data: {
215
+ prompt: item.prompt,
216
+ left_video: shuffled.left_video,
217
+ right_video: shuffled.right_video,
218
+ },
219
+ });
220
+ for (const metric of metricNames) await session.setValue(metric, "video_1");
221
+ const rating = await session.submit();
222
+ const votes = Object.fromEntries(
223
+ metricNames.map((name) => [name, [rating[name], rating[name]]]),
224
+ );
225
+ const resolved = await runCode(cascade, { swap: shuffled.swap, ...votes });
226
+ const merged = await runCode(mergeAdjudication, {
227
+ per_metric_resolved: resolved.per_metric_resolved,
228
+ disagreed_metrics: resolved.disagreed_metrics,
229
+ });
230
+ const overall = await runCode(finalizeOverall, {
231
+ op_value_or_null: merged.op_value_or_null,
232
+ op_source: merged.op_source,
233
+ needs_overall_manual: merged.needs_overall_manual,
234
+ final_metrics: merged.final_metrics,
235
+ resolution_method: merged.resolution_method,
236
+ annotator_id_adjudicator: merged.annotator_id_adjudicator,
237
+ });
238
+ const matches = await runCode(adjudicationMatch, {
239
+ rater_ita_votes: resolved.rater_ita_votes,
240
+ rater_iq_votes: resolved.rater_iq_votes,
241
+ disagreed_metrics: resolved.disagreed_metrics,
242
+ final_metrics: merged.final_metrics,
243
+ needs_overall_manual: merged.needs_overall_manual,
244
+ });
245
+ const assembled = await runCode(assemble, {
246
+ record_id: shuffled.record_id,
247
+ ...item,
248
+ final_metrics: merged.final_metrics,
249
+ ...overall,
250
+ rater_ita_votes: resolved.rater_ita_votes,
251
+ rater_mta_votes: resolved.rater_mta_votes,
252
+ rater_iq_votes: resolved.rater_iq_votes,
253
+ rater_mq_votes: resolved.rater_mq_votes,
254
+ rater_sbc_votes: resolved.rater_sbc_votes,
255
+ rater_vr_votes: resolved.rater_vr_votes,
256
+ ...matches,
257
+ });
258
+
259
+ expect(assembled).toMatchObject({
260
+ delivery_record: {
261
+ record_id: shuffled.record_id,
262
+ overall_preference_source: "alignment",
263
+ },
264
+ });
265
+ await session.close();
266
+ });
267
+ });