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

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 (40) hide show
  1. package/dist/cli/toloka.js +121 -120
  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/cli.md +26 -1
  6. package/docs/datasets.md +8 -9
  7. package/docs/limitations.md +5 -8
  8. package/docs/troubleshooting.md +0 -11
  9. package/openapi/gen/api.ts +779 -895
  10. package/openapi/gen/models/baseModel.ts +9 -0
  11. package/openapi/gen/models/baseModelId.ts +2 -0
  12. package/openapi/gen/models/baseModelIdProperty.ts +2 -0
  13. package/openapi/gen/models/createProjectModelBody.ts +8 -0
  14. package/openapi/gen/models/createProjectModelBodyDescription.ts +2 -0
  15. package/openapi/gen/models/generationNodeSettingsAudienceAndPricingOneOfOneone.ts +1 -0
  16. package/openapi/gen/models/index.ts +16 -0
  17. package/openapi/gen/models/listBaseModels200.ts +5 -0
  18. package/openapi/gen/models/listModelVersions200.ts +7 -0
  19. package/openapi/gen/models/listModelVersionsParams.ts +5 -0
  20. package/openapi/gen/models/listProjectModels200.ts +7 -0
  21. package/openapi/gen/models/listProjectModelsParams.ts +5 -0
  22. package/openapi/gen/models/model.ts +16 -0
  23. package/openapi/gen/models/modelArchivedAt.ts +2 -0
  24. package/openapi/gen/models/modelDescription.ts +2 -0
  25. package/openapi/gen/models/modelId.ts +2 -0
  26. package/openapi/gen/models/modelVersion.ts +9 -0
  27. package/openapi/gen/models/modelVersionId.ts +2 -0
  28. package/openapi/gen/models/pipelineGraphSnapshotGraphNodesItemOneOfThreeoneSettingsAudienceAndPricingOneOfOneone.ts +1 -0
  29. package/openapi/gen/models/validatePipelineGraphBodyGraphNodesItemOneOfThreeoneSettingsAudienceAndPricingOneOfOneone.ts +1 -0
  30. package/package.json +1 -1
  31. package/skills/catalog.json +4 -4
  32. package/skills/toloka-build/SKILL.md +1 -1
  33. package/skills/toloka-build/references/workflow-and-cli.md +12 -1
  34. package/skills/toloka-report-issue/references/report-template.md +1 -1
  35. package/src/cli/data.ts +148 -25
  36. package/src/cli/models.ts +188 -0
  37. package/src/cli/program.ts +99 -1
  38. package/src/client.ts +109 -35
  39. package/src/errors.ts +25 -10
  40. package/src/transport.ts +16 -13
@@ -0,0 +1,188 @@
1
+ import { assert } from "es-toolkit";
2
+
3
+ import type { BaseModel, BaseModelId, Model, ModelVersion, PotatoClient } from "../client";
4
+
5
+ type ModelApi = Pick<PotatoClient, "baseModels" | "models">;
6
+ type ModelIo = { readonly stdout: (text: string) => void };
7
+ type TableRow = readonly string[];
8
+
9
+ const BASE_MODEL_IDS = [
10
+ "qwen3-4b",
11
+ "qwen3-8b",
12
+ "qwen3-32b",
13
+ "qwen3-235b",
14
+ ] satisfies readonly BaseModelId[];
15
+
16
+ const formatTable = (rows: readonly TableRow[]): string => {
17
+ const widths = rows.reduce<number[]>(
18
+ (current, row) => row.map((cell, index) => Math.max(current[index] ?? 0, cell.length)),
19
+ [],
20
+ );
21
+ return rows
22
+ .map((row) =>
23
+ row
24
+ .map((cell, index) => cell.padEnd(widths[index] ?? cell.length))
25
+ .join(" ")
26
+ .trimEnd(),
27
+ )
28
+ .join("\n");
29
+ };
30
+
31
+ const writeOutput = (io: ModelIo, json: boolean, value: unknown, formatted: string): void => {
32
+ io.stdout(json ? JSON.stringify(value, null, 2) : formatted);
33
+ };
34
+
35
+ const formatBaseModel = (model: BaseModel): string =>
36
+ formatTable([
37
+ ["ID", model.id],
38
+ ["Name", model.displayName],
39
+ ["Provider", model.provider],
40
+ ["Upstream model", model.upstreamModel],
41
+ ["Upstream revision", model.upstreamRevision],
42
+ ]);
43
+
44
+ const formatModel = (model: Model): string =>
45
+ formatTable([
46
+ ["ID", model.id],
47
+ ["Project", model.projectId],
48
+ ["Name", model.name],
49
+ ["Base model", model.baseModelId],
50
+ ["Description", model.description ?? "-"],
51
+ ["Archived at", model.archivedAt ?? "-"],
52
+ ["Created at", model.createdAt],
53
+ ["Updated at", model.updatedAt],
54
+ ]);
55
+
56
+ export const parseBaseModelId = (value: string): BaseModelId => {
57
+ const modelId = BASE_MODEL_IDS.find((candidate) => candidate === value);
58
+ assert(modelId, `Unknown base model ${value}.`);
59
+ return modelId;
60
+ };
61
+
62
+ const resolveModel = async (
63
+ client: ModelApi,
64
+ projectId: string,
65
+ nameOrId: string,
66
+ ): Promise<Model> => {
67
+ if (nameOrId.startsWith("mdl.")) return client.models.get({ projectId, modelId: nameOrId });
68
+ const matches: Model[] = [];
69
+ for await (const model of client.models.listAll({ projectId, limit: 100 })) {
70
+ if (model.name === nameOrId) matches.push(model);
71
+ }
72
+ assert(matches.length === 1, `Model ${nameOrId} was not found or is ambiguous.`);
73
+ return matches[0]!;
74
+ };
75
+
76
+ export const listBaseModels = async (
77
+ client: ModelApi,
78
+ input: { json: boolean },
79
+ io: ModelIo,
80
+ ): Promise<void> => {
81
+ const models = await client.baseModels.list();
82
+ writeOutput(
83
+ io,
84
+ input.json,
85
+ models,
86
+ models.length === 0
87
+ ? "No base models found."
88
+ : formatTable([
89
+ ["ID", "NAME", "PROVIDER", "UPSTREAM MODEL"],
90
+ ...models.map((model) => [
91
+ model.id,
92
+ model.displayName,
93
+ model.provider,
94
+ model.upstreamModel,
95
+ ]),
96
+ ]),
97
+ );
98
+ };
99
+
100
+ export const getBaseModel = async (
101
+ client: ModelApi,
102
+ input: { id: BaseModelId; json: boolean },
103
+ io: ModelIo,
104
+ ): Promise<void> => {
105
+ const model = await client.baseModels.get({ id: input.id });
106
+ writeOutput(io, input.json, model, formatBaseModel(model));
107
+ };
108
+
109
+ export const listModels = async (
110
+ client: ModelApi,
111
+ input: { projectId: string; json: boolean },
112
+ io: ModelIo,
113
+ ): Promise<void> => {
114
+ const page = await client.models.listPage({ projectId: input.projectId, limit: 100, offset: 0 });
115
+ writeOutput(
116
+ io,
117
+ input.json,
118
+ page.models,
119
+ page.models.length === 0
120
+ ? "No models found."
121
+ : formatTable([
122
+ ["ID", "NAME", "BASE MODEL"],
123
+ ...page.models.map((model) => [model.id, model.name, model.baseModelId]),
124
+ ]),
125
+ );
126
+ };
127
+
128
+ export const getModel = async (
129
+ client: ModelApi,
130
+ input: { projectId: string; nameOrId: string; json: boolean },
131
+ io: ModelIo,
132
+ ): Promise<void> => {
133
+ const model = await resolveModel(client, input.projectId, input.nameOrId);
134
+ writeOutput(io, input.json, model, formatModel(model));
135
+ };
136
+
137
+ export const listModelVersions = async (
138
+ client: ModelApi,
139
+ input: { projectId: string; nameOrId: string; json: boolean },
140
+ io: ModelIo,
141
+ ): Promise<void> => {
142
+ const model = await resolveModel(client, input.projectId, input.nameOrId);
143
+ const versions: ModelVersion[] = [];
144
+ for await (const version of client.models.listAllVersions({
145
+ projectId: input.projectId,
146
+ modelId: model.id,
147
+ limit: 100,
148
+ })) {
149
+ versions.push(version);
150
+ }
151
+ writeOutput(
152
+ io,
153
+ input.json,
154
+ versions,
155
+ versions.length === 0
156
+ ? "No Model Versions found."
157
+ : formatTable([
158
+ ["VERSION", "ID", "CREATED AT"],
159
+ ...versions.map((version) => [version.version, version.id, version.createdAt]),
160
+ ]),
161
+ );
162
+ };
163
+
164
+ export const createModel = async (
165
+ client: ModelApi,
166
+ input: {
167
+ projectId: string;
168
+ name: string;
169
+ description?: string;
170
+ baseModelId: BaseModelId;
171
+ json: boolean;
172
+ },
173
+ io: ModelIo,
174
+ ): Promise<void> => {
175
+ const { json, ...body } = input;
176
+ const model = await client.models.create(body);
177
+ writeOutput(io, json, model, formatModel(model));
178
+ };
179
+
180
+ export const archiveModel = async (
181
+ client: ModelApi,
182
+ input: { projectId: string; nameOrId: string; json: boolean },
183
+ io: ModelIo,
184
+ ): Promise<void> => {
185
+ const model = await resolveModel(client, input.projectId, input.nameOrId);
186
+ const archived = await client.models.archive({ projectId: input.projectId, modelId: model.id });
187
+ writeOutput(io, input.json, archived, `Archived Model ${archived.id}.`);
188
+ };
@@ -33,6 +33,16 @@ import {
33
33
  projectStatus,
34
34
  validateProject,
35
35
  } from "./project";
36
+ import {
37
+ archiveModel,
38
+ createModel,
39
+ getBaseModel,
40
+ getModel,
41
+ listBaseModels,
42
+ listModels,
43
+ listModelVersions,
44
+ parseBaseModelId,
45
+ } from "./models";
36
46
  import {
37
47
  MISSING_PROJECT_ID_MESSAGE,
38
48
  addProject,
@@ -533,6 +543,94 @@ export function createProgram(context: ProgramContext): CreatedProgram {
533
543
  }
534
544
  });
535
545
 
546
+ const baseModel = program
547
+ .command("base-model")
548
+ .description("inspect the global base model catalog");
549
+ baseModel
550
+ .command("list")
551
+ .option("--json", "print JSON", false)
552
+ .description("list base models")
553
+ .action(async (options: { json: boolean }) => {
554
+ const { client } = await resolveContext(globals(), context);
555
+ await listBaseModels(client, { json: options.json }, context.io);
556
+ });
557
+ baseModel
558
+ .command("get")
559
+ .argument("<id>", "base model ID")
560
+ .option("--json", "print JSON", false)
561
+ .description("show one base model")
562
+ .action(async (id: string, options: { json: boolean }) => {
563
+ const { client } = await resolveContext(globals(), context);
564
+ await getBaseModel(client, { id: parseBaseModelId(id), json: options.json }, context.io);
565
+ });
566
+
567
+ const model = program
568
+ .command("model")
569
+ .description("manage project Models and immutable versions");
570
+ model
571
+ .command("list")
572
+ .option("--json", "print JSON", false)
573
+ .description("list Models owned by the selected project")
574
+ .action(async (options: { json: boolean }) => {
575
+ const { client, projectId } = await resolveContext(globals(), context);
576
+ assert(projectId, MISSING_PROJECT_ID_MESSAGE);
577
+ await listModels(client, { projectId, json: options.json }, context.io);
578
+ });
579
+ model
580
+ .command("get")
581
+ .argument("<name-or-id>", "project Model name or ID")
582
+ .option("--json", "print JSON", false)
583
+ .description("show one project Model")
584
+ .action(async (nameOrId: string, options: { json: boolean }) => {
585
+ const { client, projectId } = await resolveContext(globals(), context);
586
+ assert(projectId, MISSING_PROJECT_ID_MESSAGE);
587
+ await getModel(client, { projectId, nameOrId, json: options.json }, context.io);
588
+ });
589
+ model
590
+ .command("versions")
591
+ .argument("<name-or-id>", "project Model name or ID")
592
+ .option("--json", "print JSON", false)
593
+ .description("list immutable versions of a model")
594
+ .action(async (nameOrId: string, options: { json: boolean }) => {
595
+ const { client, projectId } = await resolveContext(globals(), context);
596
+ assert(projectId, MISSING_PROJECT_ID_MESSAGE);
597
+ await listModelVersions(client, { projectId, nameOrId, json: options.json }, context.io);
598
+ });
599
+ model
600
+ .command("create")
601
+ .argument("<name>", "new Model name")
602
+ .requiredOption("--base <id>", "base model ID")
603
+ .option("--description <text>", "Model description")
604
+ .option("--json", "print JSON", false)
605
+ .description("create project Model metadata without a Model Version")
606
+ .action(
607
+ async (name: string, options: { base: string; description?: string; json: boolean }) => {
608
+ const { client, projectId } = await resolveContext(globals(), context);
609
+ assert(projectId, MISSING_PROJECT_ID_MESSAGE);
610
+ await createModel(
611
+ client,
612
+ {
613
+ projectId,
614
+ name,
615
+ description: options.description,
616
+ baseModelId: parseBaseModelId(options.base),
617
+ json: options.json,
618
+ },
619
+ context.io,
620
+ );
621
+ },
622
+ );
623
+ model
624
+ .command("archive")
625
+ .option("--json", "print JSON", false)
626
+ .argument("<name-or-id>", "project Model name or ID")
627
+ .description("archive a project Model")
628
+ .action(async (nameOrId: string, options: { json: boolean }) => {
629
+ const { client, projectId } = await resolveContext(globals(), context);
630
+ assert(projectId, MISSING_PROJECT_ID_MESSAGE);
631
+ await archiveModel(client, { projectId, nameOrId, json: options.json }, context.io);
632
+ });
633
+
536
634
  const dataset = program
537
635
  .command("dataset")
538
636
  .description("inspect and move dataset catalog and rows");
@@ -580,7 +678,7 @@ export function createProgram(context: ProgramContext): CreatedProgram {
580
678
  dataset
581
679
  .command("upload")
582
680
  .argument("<name>", "dataset name or id")
583
- .argument("<file>", "JSONL file, one JSON object per line")
681
+ .argument("<file>", "JSON array, JSONL, or CSV file")
584
682
  .description("append rows to a dataset")
585
683
  .action(async (name: string, file: string) => {
586
684
  const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
package/src/client.ts CHANGED
@@ -8,6 +8,7 @@ import type {
8
8
  CopyPipelineBody,
9
9
  CreateDatasetBody,
10
10
  CreateDatasetExportBody,
11
+ CreateProjectModelBody,
11
12
  CreateDatasetFieldBodyAnyOf,
12
13
  CreateDatasetUploadBody,
13
14
  CreatePipelineCheckBody,
@@ -21,13 +22,17 @@ import type {
21
22
  ImportPipelineBody,
22
23
  InferredField as GeneratedInferredField,
23
24
  ListDatasetFieldsParams,
25
+ ListModelVersionsParams,
24
26
  ListDatasetItemsParams,
25
27
  ListPipelineRunItemsParams,
26
28
  ListPipelineRunsParams,
27
29
  ListProjectDatasetsParams,
28
30
  ListProjectPipelinesParams,
31
+ ListProjectModelsParams,
29
32
  ListProjectsParams,
30
33
  Pipeline as GeneratedPipeline,
34
+ Model as GeneratedModel,
35
+ ModelVersion as GeneratedModelVersion,
31
36
  PatchPipelineNodeBody,
32
37
  PipelineGraphSnapshot,
33
38
  Project as GeneratedProject,
@@ -38,6 +43,8 @@ import type {
38
43
  RunJob as GeneratedRunJob,
39
44
  RunSummary as GeneratedRunSummary,
40
45
  SetProjectSecretBody,
46
+ BaseModel as GeneratedBaseModel,
47
+ BaseModelId as GeneratedBaseModelId,
41
48
  UpdatePipelineBody,
42
49
  UpdateProjectBody,
43
50
  UploadJob as GeneratedUploadJob,
@@ -57,6 +64,8 @@ export type DatasetFieldId = Branded<string, "DatasetFieldId">;
57
64
  export type DatasetId = Branded<string, "DatasetId">;
58
65
  export type DatasetItemId = Branded<string, "DatasetItemId">;
59
66
  export type ExportId = Branded<string, "ExportId">;
67
+ export type ModelId = Branded<string, "ModelId">;
68
+ export type ModelVersionId = Branded<string, "ModelVersionId">;
60
69
  export type PipelineId = Branded<string, "PipelineId">;
61
70
  export type ProjectFileId = Branded<string, "ProjectFileId">;
62
71
  export type ProjectId = Branded<string, "ProjectId">;
@@ -76,6 +85,8 @@ type ResourceIdsByProperty = {
76
85
  exportId: ExportId;
77
86
  fieldId: DatasetFieldId;
78
87
  fileId: ProjectFileId;
88
+ modelId: ModelId;
89
+ modelVersionId: ModelVersionId;
79
90
  goldenDatasetId: DatasetId;
80
91
  itemId: RunItemId;
81
92
  pipelineId: PipelineId;
@@ -121,6 +132,10 @@ export type DatasetField = BrandedResource<GeneratedDatasetField, DatasetFieldId
121
132
  export type DatasetItem = BrandedResource<GeneratedDatasetItem, DatasetItemId>;
122
133
  export type ExportJob = BrandedResource<GeneratedExportJob, ExportId>;
123
134
  export type InferredField = GeneratedInferredField;
135
+ export type Model = BrandedResource<GeneratedModel, ModelId>;
136
+ export type ModelVersion = BrandedResource<GeneratedModelVersion, ModelVersionId>;
137
+ export type BaseModel = GeneratedBaseModel;
138
+ export type BaseModelId = GeneratedBaseModelId;
124
139
  export type Pipeline = BrandedResource<GeneratedPipeline, PipelineId>;
125
140
  export type Project = BrandedResource<GeneratedProject, ProjectId>;
126
141
  export type ProjectFile = BrandedResource<GeneratedProjectFile, ProjectFileId>;
@@ -178,6 +193,12 @@ export type WaitForTarget =
178
193
  const createClientImplementation = (config: ClientConfig) => {
179
194
  const requestOptions = createRequestOptions(config.baseUrl, config.apiKey, config.fetch);
180
195
 
196
+ const emptyJsonRequestOptions = (): RequestInit => {
197
+ const headers = new Headers(requestOptions.headers);
198
+ headers.set("Content-Type", "application/json");
199
+ return { ...requestOptions, headers, body: "{}" };
200
+ };
201
+
181
202
  function waitFor(
182
203
  target: DatasetUploadWaitTarget,
183
204
  options?: WaitForOptions,
@@ -296,6 +317,56 @@ const createClientImplementation = (config: ClientConfig) => {
296
317
  wire.copyPipeline(pipelineId, body, requestOptions),
297
318
  };
298
319
 
320
+ const baseModels = {
321
+ list: async () => (await wire.listBaseModels(requestOptions)).baseModels,
322
+ get: ({ id }: { id: BaseModelId }) => wire.getBaseModel(id, requestOptions),
323
+ };
324
+
325
+ const models = {
326
+ listPage: ({ projectId, ...params }: { projectId: ProjectId } & ListProjectModelsParams) =>
327
+ wire.listProjectModels(projectId, params, requestOptions),
328
+ listAll: ({ projectId, ...params }: { projectId: ProjectId } & ListProjectModelsParams) =>
329
+ listAllPages(async (pageOptions) => {
330
+ const page = await wire.listProjectModels(
331
+ projectId,
332
+ { ...params, ...pageOptions },
333
+ requestOptions,
334
+ );
335
+ return { items: page.models, pagination: page.pagination };
336
+ }, params),
337
+ get: ({ projectId, modelId }: { projectId: ProjectId; modelId: ModelId }) =>
338
+ wire.getProjectModel(projectId, modelId, requestOptions),
339
+ listVersionsPage: ({
340
+ projectId,
341
+ modelId,
342
+ ...params
343
+ }: { projectId: ProjectId; modelId: ModelId } & ListModelVersionsParams) =>
344
+ wire.listModelVersions(projectId, modelId, params, requestOptions),
345
+ listAllVersions: ({
346
+ projectId,
347
+ modelId,
348
+ ...params
349
+ }: { projectId: ProjectId; modelId: ModelId } & ListModelVersionsParams) =>
350
+ listAllPages(async (pageOptions) => {
351
+ const page = await wire.listModelVersions(
352
+ projectId,
353
+ modelId,
354
+ { ...params, ...pageOptions },
355
+ requestOptions,
356
+ );
357
+ return { items: page.versions, pagination: page.pagination };
358
+ }, params),
359
+ create: ({ projectId, ...body }: { projectId: ProjectId } & CreateProjectModelBody) =>
360
+ wire.createProjectModel(projectId, body, requestOptions),
361
+ getVersion: ({ modelVersionId }: { modelVersionId: ModelVersionId }) =>
362
+ wire.getModelVersion(modelVersionId, requestOptions),
363
+ archive: ({ projectId, modelId }: { projectId: ProjectId; modelId: ModelId }) => {
364
+ const headers = new Headers(requestOptions.headers);
365
+ headers.set("Content-Type", "application/json");
366
+ return wire.archiveProjectModel(projectId, modelId, { ...requestOptions, headers });
367
+ },
368
+ };
369
+
299
370
  const datasets = {
300
371
  create: ({ projectId, ...body }: { projectId: ProjectId } & CreateDatasetBody) =>
301
372
  wire.createDataset(projectId, body, requestOptions),
@@ -363,7 +434,9 @@ const createClientImplementation = (config: ClientConfig) => {
363
434
  startUpload: ({ datasetId, ...body }: { datasetId: DatasetId } & CreateDatasetUploadBody) =>
364
435
  wire.createDatasetUpload(datasetId, body, requestOptions),
365
436
  finalizeUpload: ({ uploadId }: { uploadId: UploadId }) =>
366
- wire.finalizeDatasetUpload(uploadId, requestOptions),
437
+ wire.finalizeDatasetUpload(uploadId, emptyJsonRequestOptions()),
438
+ cancelUpload: ({ uploadId }: { uploadId: UploadId }) =>
439
+ wire.cancelDatasetUpload(uploadId, emptyJsonRequestOptions()),
367
440
  getUpload: ({ uploadId }: { uploadId: UploadId }) =>
368
441
  wire.getDatasetUpload(uploadId, requestOptions),
369
442
  startExport: ({ datasetId, ...body }: { datasetId: DatasetId } & CreateDatasetExportBody) =>
@@ -373,15 +446,8 @@ const createClientImplementation = (config: ClientConfig) => {
373
446
  };
374
447
 
375
448
  const runs = {
376
- start: ({ pipelineId }: { pipelineId: PipelineId }) => {
377
- const headers = new Headers(requestOptions.headers);
378
- headers.set("Content-Type", "application/json");
379
- return wire.createPipelineRun(pipelineId, {
380
- ...requestOptions,
381
- headers,
382
- body: "{}",
383
- });
384
- },
449
+ start: ({ pipelineId }: { pipelineId: PipelineId }) =>
450
+ wire.createPipelineRun(pipelineId, emptyJsonRequestOptions()),
385
451
  get: ({ runId }: { runId: RunId }) => wire.getPipelineRun(runId, requestOptions),
386
452
  cancel: ({ runId }: { runId: RunId }) => wire.cancelPipelineRun(runId, requestOptions),
387
453
  listPage: ({ pipelineId, ...params }: { pipelineId: PipelineId } & ListPipelineRunsParams) =>
@@ -446,6 +512,8 @@ const createClientImplementation = (config: ClientConfig) => {
446
512
  projects,
447
513
  pipelines,
448
514
  datasets,
515
+ baseModels,
516
+ models,
449
517
  runs,
450
518
  secrets,
451
519
  files,
@@ -462,31 +530,37 @@ type PublicOutput<T> = T extends GeneratedCheckJob
462
530
  ? ExportJob
463
531
  : T extends GeneratedUploadJob
464
532
  ? UploadJob
465
- : T extends GeneratedProject
466
- ? Project
467
- : T extends GeneratedPipeline
468
- ? Pipeline
469
- : T extends GeneratedDataset
470
- ? Dataset
471
- : T extends GeneratedDatasetField
472
- ? DatasetField
473
- : T extends GeneratedDatasetItem
474
- ? DatasetItem
475
- : T extends GeneratedProjectFile
476
- ? ProjectFile
477
- : T extends GeneratedRunItem
478
- ? RunItem
479
- : T extends GeneratedRunSummary
480
- ? RunSummary
481
- : T extends Promise<infer Value>
482
- ? Promise<PublicOutput<Value>>
483
- : T extends AsyncGenerator<infer Value, infer Return, infer Next>
484
- ? AsyncGenerator<PublicOutput<Value>, PublicOutput<Return>, Next>
485
- : T extends readonly unknown[]
486
- ? { [K in keyof T]: PublicOutput<T[K]> }
487
- : T extends object
488
- ? { [K in keyof T]: PublicOutput<T[K]> }
489
- : T;
533
+ : T extends GeneratedModel
534
+ ? Model
535
+ : T extends GeneratedModelVersion
536
+ ? ModelVersion
537
+ : T extends GeneratedProject
538
+ ? Project
539
+ : T extends GeneratedPipeline
540
+ ? Pipeline
541
+ : T extends GeneratedDataset
542
+ ? Dataset
543
+ : T extends GeneratedDatasetField
544
+ ? DatasetField
545
+ : T extends GeneratedDatasetItem
546
+ ? DatasetItem
547
+ : T extends GeneratedProjectFile
548
+ ? ProjectFile
549
+ : T extends GeneratedRunItem
550
+ ? RunItem
551
+ : T extends GeneratedRunSummary
552
+ ? RunSummary
553
+ : T extends Blob
554
+ ? Blob
555
+ : T extends Promise<infer Value>
556
+ ? Promise<PublicOutput<Value>>
557
+ : T extends AsyncGenerator<infer Value, infer Return, infer Next>
558
+ ? AsyncGenerator<PublicOutput<Value>, PublicOutput<Return>, Next>
559
+ : T extends readonly unknown[]
560
+ ? { [K in keyof T]: PublicOutput<T[K]> }
561
+ : T extends object
562
+ ? { [K in keyof T]: PublicOutput<T[K]> }
563
+ : T;
490
564
 
491
565
  type PublicClientMember<T> = T extends (...args: infer Args) => infer Result
492
566
  ? (...args: { [K in keyof Args]: PublicInput<Args[K]> }) => PublicOutput<Result>
package/src/errors.ts CHANGED
@@ -1,28 +1,43 @@
1
- export type PotatoApiErrorCode = 'http' | 'wait_timeout';
1
+ export type PotatoApiErrorCode = "http" | "wait_timeout";
2
2
 
3
3
  export class PotatoApiError extends Error {
4
4
  readonly code: PotatoApiErrorCode;
5
5
  readonly status: number | undefined;
6
6
  readonly body: unknown;
7
+ readonly requestId: string | undefined;
7
8
 
8
- private constructor(message: string, code: PotatoApiErrorCode, status: number | undefined, body: unknown) {
9
+ private constructor(
10
+ message: string,
11
+ code: PotatoApiErrorCode,
12
+ status: number | undefined,
13
+ body: unknown,
14
+ requestId?: string,
15
+ ) {
9
16
  super(message);
10
- this.name = 'PotatoApiError';
17
+ this.name = "PotatoApiError";
11
18
  this.code = code;
12
19
  this.status = status;
13
20
  this.body = body;
21
+ this.requestId = requestId;
14
22
  }
15
23
 
16
- static fromResponse(status: number, body: unknown) {
17
- return new PotatoApiError(`Potato API request failed with status ${status}`, 'http', status, body);
24
+ static fromResponse(status: number, body: unknown, requestId?: string) {
25
+ const requestSuffix = requestId === undefined ? "" : ` (request id: ${requestId})`;
26
+ return new PotatoApiError(
27
+ `Potato API request failed with status ${status}${requestSuffix}`,
28
+ "http",
29
+ status,
30
+ body,
31
+ requestId,
32
+ );
18
33
  }
19
34
 
20
35
  static waitTimeout(timeoutMs: number) {
21
36
  return new PotatoApiError(
22
37
  `Potato API job did not reach a terminal status within ${timeoutMs} ms`,
23
- 'wait_timeout',
38
+ "wait_timeout",
24
39
  undefined,
25
- { timeoutMs }
40
+ { timeoutMs },
26
41
  );
27
42
  }
28
43
  }
@@ -33,17 +48,17 @@ export type PipelineGraphValidationIssue = {
33
48
  };
34
49
 
35
50
  export class PipelineGraphValidationError extends Error {
36
- readonly code = 'invalid_pipeline_graph';
51
+ readonly code = "invalid_pipeline_graph";
37
52
  readonly issues: readonly PipelineGraphValidationIssue[];
38
53
 
39
54
  private constructor(message: string, issues: readonly PipelineGraphValidationIssue[]) {
40
55
  super(message);
41
- this.name = 'PipelineGraphValidationError';
56
+ this.name = "PipelineGraphValidationError";
42
57
  this.issues = issues;
43
58
  }
44
59
 
45
60
  static fromIssues(issues: readonly PipelineGraphValidationIssue[]) {
46
- const details = issues.map(({ path, reason }) => `- ${path}: ${reason}`).join('\n');
61
+ const details = issues.map(({ path, reason }) => `- ${path}: ${reason}`).join("\n");
47
62
  return new PipelineGraphValidationError(`Pipeline graph validation failed\n${details}`, issues);
48
63
  }
49
64
  }
package/src/transport.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { assert } from 'es-toolkit';
2
- import { PotatoApiError } from './errors';
1
+ import { assert } from "es-toolkit";
2
+ import { PotatoApiError } from "./errors";
3
3
 
4
- const BASE_URL_HEADER = 'x-potato-sdk-base-url';
4
+ const BASE_URL_HEADER = "x-potato-sdk-base-url";
5
5
 
6
6
  type TransportRequestInit = RequestInit & {
7
7
  readonly fetch?: typeof globalThis.fetch;
@@ -10,18 +10,18 @@ type TransportRequestInit = RequestInit & {
10
10
  export const createRequestOptions = (
11
11
  baseUrl: string,
12
12
  apiKey: string,
13
- fetch?: typeof globalThis.fetch
13
+ fetch?: typeof globalThis.fetch,
14
14
  ): TransportRequestInit => {
15
- assert(baseUrl.length > 0, 'The SDK base URL cannot be empty');
16
- assert(apiKey.length > 0, 'The SDK API key cannot be empty');
15
+ assert(baseUrl.length > 0, "The SDK base URL cannot be empty");
16
+ assert(apiKey.length > 0, "The SDK API key cannot be empty");
17
17
 
18
18
  return {
19
19
  headers: {
20
- Accept: 'application/json',
20
+ Accept: "application/json",
21
21
  Authorization: `ApiKey ${apiKey}`,
22
- [BASE_URL_HEADER]: new URL(baseUrl).toString()
22
+ [BASE_URL_HEADER]: new URL(baseUrl).toString(),
23
23
  },
24
- fetch
24
+ fetch,
25
25
  };
26
26
  };
27
27
 
@@ -30,7 +30,7 @@ export async function potatoFetch(url: string, options: TransportRequestInit): P
30
30
  const { fetch: fetchImplementation = globalThis.fetch, ...requestOptions } = options;
31
31
  const headers = new Headers(requestOptions.headers);
32
32
  const baseUrl = headers.get(BASE_URL_HEADER);
33
- assert(baseUrl, 'The SDK base URL is missing');
33
+ assert(baseUrl, "The SDK base URL is missing");
34
34
  headers.delete(BASE_URL_HEADER);
35
35
 
36
36
  const response = await fetchImplementation(new URL(url, baseUrl), { ...requestOptions, headers });
@@ -41,13 +41,16 @@ export async function potatoFetch(url: string, options: TransportRequestInit): P
41
41
 
42
42
  if (text.length === 0) {
43
43
  body = undefined;
44
- } else if (response.headers.get('content-type')?.includes('application/json')) {
44
+ } else if (response.headers.get("content-type")?.includes("application/json")) {
45
45
  body = JSON.parse(text);
46
46
  }
47
47
 
48
- throw PotatoApiError.fromResponse(response.status, body);
48
+ throw PotatoApiError.fromResponse(
49
+ response.status,
50
+ body,
51
+ response.headers.get("x-request-id") ?? undefined,
52
+ );
49
53
  }
50
54
 
51
55
  return text.length === 0 ? undefined : JSON.parse(text);
52
56
  }
53
-