dataiku-sdk 0.11.0 → 0.12.0

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 (38) hide show
  1. package/LICENSE +21 -0
  2. package/dist/packages/types/src/index.d.ts +4 -0
  3. package/dist/packages/types/src/index.js +4 -0
  4. package/dist/src/cli/commands/analysis.d.ts +2 -0
  5. package/dist/src/cli/commands/analysis.js +66 -0
  6. package/dist/src/cli/commands/api-service.js +19 -0
  7. package/dist/src/cli/commands/index.js +8 -0
  8. package/dist/src/cli/commands/ml-task.d.ts +2 -0
  9. package/dist/src/cli/commands/ml-task.js +171 -0
  10. package/dist/src/cli/commands/model-evaluation-store.d.ts +2 -0
  11. package/dist/src/cli/commands/model-evaluation-store.js +83 -0
  12. package/dist/src/cli/commands/project.js +2 -1
  13. package/dist/src/cli/commands/recipe.js +29 -2
  14. package/dist/src/cli/commands/saved-model.d.ts +2 -0
  15. package/dist/src/cli/commands/saved-model.js +85 -0
  16. package/dist/src/cli/contract.js +11 -1
  17. package/dist/src/cli/flags.js +17 -0
  18. package/dist/src/cli/helpers/cleanup.js +47 -1
  19. package/dist/src/cli/usage.js +4 -0
  20. package/dist/src/client.d.ts +12 -0
  21. package/dist/src/client.js +20 -0
  22. package/dist/src/index.d.ts +4 -0
  23. package/dist/src/index.js +4 -0
  24. package/dist/src/resources/analyses.d.ts +34 -0
  25. package/dist/src/resources/analyses.js +31 -0
  26. package/dist/src/resources/api-services.d.ts +2 -0
  27. package/dist/src/resources/api-services.js +10 -0
  28. package/dist/src/resources/datasets.js +7 -5
  29. package/dist/src/resources/ml-tasks.d.ts +91 -0
  30. package/dist/src/resources/ml-tasks.js +130 -0
  31. package/dist/src/resources/model-evaluation-stores.d.ts +41 -0
  32. package/dist/src/resources/model-evaluation-stores.js +36 -0
  33. package/dist/src/resources/recipes.js +148 -31
  34. package/dist/src/resources/saved-models.d.ts +41 -0
  35. package/dist/src/resources/saved-models.js +40 -0
  36. package/package.json +1 -1
  37. package/packages/types/dist/index.d.ts +4 -0
  38. package/packages/types/dist/index.js +4 -0
@@ -0,0 +1,41 @@
1
+ import { BaseResource } from "./base.js";
2
+ export interface ModelEvaluationStoreListItem extends Record<string, unknown> {
3
+ id?: string;
4
+ name?: string;
5
+ projectKey?: string;
6
+ mesFlavor?: string;
7
+ }
8
+ export interface ModelEvaluationStore extends Record<string, unknown> {
9
+ id?: string;
10
+ name?: string;
11
+ projectKey?: string;
12
+ mesFlavor?: string;
13
+ }
14
+ export interface ModelEvaluationStoreCreateOptions {
15
+ name: string;
16
+ projectKey?: string;
17
+ }
18
+ export interface ModelEvaluationStoreCreateResult extends Record<string, unknown> {
19
+ id?: string;
20
+ name?: string;
21
+ projectKey?: string;
22
+ }
23
+ export interface ModelEvaluationListItem extends Record<string, unknown> {
24
+ id?: string;
25
+ evaluationId?: string;
26
+ createdOn?: number;
27
+ label?: string;
28
+ }
29
+ export declare class ModelEvaluationStoresResource extends BaseResource {
30
+ /** List model evaluation stores in a project. */
31
+ list(projectKey?: string): Promise<ModelEvaluationStoreListItem[]>;
32
+ /** Get model evaluation store settings and metadata. */
33
+ get(modelEvaluationStoreId: string, projectKey?: string): Promise<ModelEvaluationStore>;
34
+ /** Create a tabular model evaluation store. */
35
+ create(opts: ModelEvaluationStoreCreateOptions): Promise<ModelEvaluationStoreCreateResult>;
36
+ /** List evaluations in a model evaluation store. */
37
+ listEvaluations(modelEvaluationStoreId: string, projectKey?: string): Promise<ModelEvaluationListItem[]>;
38
+ /** Delete a model evaluation store and its evaluations. */
39
+ delete(modelEvaluationStoreId: string, projectKey?: string): Promise<void>;
40
+ private storePath;
41
+ }
@@ -0,0 +1,36 @@
1
+ import { UsageError, } from "../cli/usage.js";
2
+ import { BaseResource, } from "./base.js";
3
+ function requireNonEmpty(value, name) {
4
+ if (typeof value !== "string" || value.trim().length === 0) {
5
+ throw new UsageError(`${name} must be a non-empty string.`, "validation_failed");
6
+ }
7
+ return value;
8
+ }
9
+ export class ModelEvaluationStoresResource extends BaseResource {
10
+ /** List model evaluation stores in a project. */
11
+ async list(projectKey) {
12
+ return this.client.get(`/public/api/projects/${this.enc(projectKey)}/evaluationstores/`);
13
+ }
14
+ /** Get model evaluation store settings and metadata. */
15
+ async get(modelEvaluationStoreId, projectKey) {
16
+ return this.client.get(this.storePath(modelEvaluationStoreId, projectKey));
17
+ }
18
+ /** Create a tabular model evaluation store. */
19
+ async create(opts) {
20
+ const name = requireNonEmpty(opts.name, "name");
21
+ const projectKey = this.resolveProjectKey(opts.projectKey);
22
+ return this.client.post(`/public/api/projects/${encodeURIComponent(projectKey)}/evaluationstores/?flavor=TABULAR`, { projectKey, name, });
23
+ }
24
+ /** List evaluations in a model evaluation store. */
25
+ async listEvaluations(modelEvaluationStoreId, projectKey) {
26
+ return this.client.get(`${this.storePath(modelEvaluationStoreId, projectKey)}/evaluations/`);
27
+ }
28
+ /** Delete a model evaluation store and its evaluations. */
29
+ async delete(modelEvaluationStoreId, projectKey) {
30
+ await this.client.del(this.storePath(modelEvaluationStoreId, projectKey));
31
+ }
32
+ storePath(modelEvaluationStoreId, projectKey) {
33
+ const id = encodeURIComponent(requireNonEmpty(modelEvaluationStoreId, "modelEvaluationStoreId"));
34
+ return `/public/api/projects/${this.enc(projectKey)}/evaluationstores/${id}`;
35
+ }
36
+ }
@@ -22,6 +22,67 @@ function asRecord(value) {
22
22
  return undefined;
23
23
  return value;
24
24
  }
25
+ function parseRecipePayload(payload) {
26
+ if (!payload)
27
+ return {};
28
+ const parsed = JSON.parse(payload);
29
+ return asRecord(parsed) ?? {};
30
+ }
31
+ function parseJoinKeyPairs(values, optionName = "joinOn") {
32
+ const pairs = [];
33
+ for (const raw of values) {
34
+ const token = raw.trim();
35
+ if (!token)
36
+ continue;
37
+ const eq = token.indexOf("=");
38
+ if (eq === -1) {
39
+ pairs.push({ left: token, right: token, });
40
+ continue;
41
+ }
42
+ const left = token.slice(0, eq).trim();
43
+ const right = token.slice(eq + 1).trim();
44
+ if (!left || !right)
45
+ throw new Error(`${optionName} values must use COL or LEFT=RIGHT.`);
46
+ pairs.push({ left, right, });
47
+ }
48
+ return pairs;
49
+ }
50
+ function normalizeFuzzyDistance(value) {
51
+ const normalized = typeof value === "string" && value.trim().length > 0
52
+ ? value.trim().toUpperCase()
53
+ : "DAMERAU_LEVENSHTEIN";
54
+ if (normalized === "DAMERAU_LEVENSHTEIN")
55
+ return "LEVENSHTEIN";
56
+ if (normalized === "HAMMING" || normalized === "JACCARD" || normalized === "COSINE"
57
+ || normalized === "EUCLIDEAN") {
58
+ return normalized;
59
+ }
60
+ throw new Error("fuzzyDistance must be one of DAMERAU_LEVENSHTEIN, HAMMING, JACCARD, COSINE, or EUCLIDEAN.");
61
+ }
62
+ function normalizeJoinType(value) {
63
+ const normalized = typeof value === "string" && value.trim().length > 0
64
+ ? value.trim().toUpperCase()
65
+ : "LEFT";
66
+ if (normalized === "LEFT" || normalized === "INNER" || normalized === "RIGHT"
67
+ || normalized === "FULL") {
68
+ return normalized;
69
+ }
70
+ throw new Error("joinType must be one of LEFT, INNER, RIGHT, or FULL.");
71
+ }
72
+ function recipeVirtualInputs(payload, inputCount) {
73
+ const current = Array.isArray(payload.virtualInputs) ? payload.virtualInputs : [];
74
+ const virtualInputs = [];
75
+ for (let i = 0; i < inputCount; i++) {
76
+ virtualInputs.push({
77
+ preFilter: {},
78
+ outputColumnsSelectionMode: "AUTO_NON_CONFLICTING",
79
+ computedColumns: [],
80
+ ...asRecord(current[i]),
81
+ index: i,
82
+ });
83
+ }
84
+ return virtualInputs;
85
+ }
25
86
  const RECIPE_DEFINITION_FIELDS = new Set(["params", "inputs", "outputs", "scriptSettings",]);
26
87
  function rootRecipeDefinitionFields(data) {
27
88
  return Object.keys(data).filter((key) => RECIPE_DEFINITION_FIELDS.has(key));
@@ -467,17 +528,37 @@ export class RecipesResource extends BaseResource {
467
528
  if (!type || !name || !inputs || !outputs) {
468
529
  throw new Error("type and (inputDatasets + outputDataset/outputFolder) or (name + inputs + outputs) are required for create.");
469
530
  }
531
+ const joinCols = typeof opts.joinOn === "string" ? [opts.joinOn,] : asStringArray(opts.joinOn);
532
+ const joinKeys = type === "join" && joinCols?.length
533
+ ? parseJoinKeyPairs(joinCols)
534
+ : undefined;
535
+ const fuzzyOn = opts.fuzzyOn ?? (type === "fuzzyjoin" ? opts.joinOn : undefined);
536
+ const fuzzyCols = typeof fuzzyOn === "string" ? [fuzzyOn,] : asStringArray(fuzzyOn);
537
+ const fuzzyKeys = type === "fuzzyjoin" && fuzzyCols?.length
538
+ ? parseJoinKeyPairs(fuzzyCols, "fuzzyOn")
539
+ : undefined;
540
+ const normalizedJoinType = joinKeys?.length || fuzzyKeys?.length
541
+ ? normalizeJoinType(rawJoinType)
542
+ : undefined;
543
+ const fuzzyDistance = fuzzyKeys?.length ? normalizeFuzzyDistance(opts.fuzzyDistance) : undefined;
544
+ const fuzzyThreshold = opts.fuzzyThreshold ?? 1;
545
+ if (!Number.isFinite(fuzzyThreshold)) {
546
+ throw new Error("fuzzyThreshold must be a finite number.");
547
+ }
470
548
  const recipePrototype = {
471
549
  type,
472
550
  name,
473
551
  projectKey: pk,
474
- inputs,
552
+ ...(type === "fuzzyjoin" ? {} : { inputs, }),
475
553
  outputs,
476
554
  };
477
555
  const creationSettings = {};
478
556
  if (payload !== undefined) {
479
557
  creationSettings.script = payload;
480
558
  }
559
+ if (type === "fuzzyjoin") {
560
+ creationSettings.virtualInputs = recipeInputItems({ inputs, }).map((item) => item.ref);
561
+ }
481
562
  const createRecipe = () => this.client.post(`/public/api/projects/${enc}/recipes/`, {
482
563
  recipePrototype,
483
564
  creationSettings,
@@ -597,37 +678,29 @@ export class RecipesResource extends BaseResource {
597
678
  }
598
679
  // For join recipes: configure join conditions after creation
599
680
  let joinConfigured = false;
600
- const joinCols = typeof opts.joinOn === "string" ? [opts.joinOn,] : asStringArray(opts.joinOn);
601
- const joinType = asString(rawJoinType) ?? "LEFT";
602
- if (type === "join" && joinCols?.length) {
681
+ if (type === "join" && joinKeys?.length) {
603
682
  const rnEnc = encodeURIComponent(name);
604
683
  const full = await this.client.get(`/public/api/projects/${enc}/recipes/${rnEnc}`);
605
- // DSS returns empty payload for fresh join recipes — construct from scratch
606
- const inputCount = inputDatasets?.length
607
- ?? inputs?.main?.items?.length
608
- ?? 2;
609
- const virtualInputs = [{ index: 0, preFilter: {}, },];
610
- for (let i = 1; i < inputCount; i++) {
611
- virtualInputs.push({
612
- index: i,
613
- on: joinCols.map((col) => ({
614
- column: col,
615
- type: "string",
616
- related: col,
617
- relatedType: "string",
618
- maxMatches: 1,
684
+ const joinPayload = parseRecipePayload(full.payload);
685
+ const inputCount = recipeInputItems({ inputs, }).length || inputDatasets?.length || 2;
686
+ const joinType = normalizedJoinType ?? "LEFT";
687
+ joinPayload.virtualInputs = recipeVirtualInputs(joinPayload, inputCount);
688
+ joinPayload.joins = Array.from({ length: Math.max(0, inputCount - 1), }, (_, index) => {
689
+ const table2 = index + 1;
690
+ return {
691
+ table1: 0,
692
+ table2,
693
+ conditionsMode: "AND",
694
+ type: joinType,
695
+ outerJoinOnTheLeft: joinType === "LEFT",
696
+ on: joinKeys.map((key) => ({
697
+ column1: { name: key.left, table: 0, },
698
+ column2: { name: key.right, table: table2, },
699
+ type: "EQ",
619
700
  })),
620
- joinType,
621
- preFilter: {},
622
- });
623
- }
624
- const joinPayload = {
625
- virtualInputs,
626
- computedColumns: [],
627
- postFilter: {},
628
- };
629
- // Ensure inputs/outputs are set — DSS may not persist them from the POST for join recipes
630
- const updatedFull = {
701
+ };
702
+ });
703
+ await this.client.put(`/public/api/projects/${enc}/recipes/${rnEnc}`, {
631
704
  ...full,
632
705
  recipe: {
633
706
  ...full.recipe,
@@ -635,8 +708,52 @@ export class RecipesResource extends BaseResource {
635
708
  outputs,
636
709
  },
637
710
  payload: JSON.stringify(joinPayload),
638
- };
639
- await this.client.put(`/public/api/projects/${enc}/recipes/${rnEnc}`, updatedFull);
711
+ });
712
+ joinConfigured = true;
713
+ }
714
+ if (type === "fuzzyjoin" && fuzzyKeys?.length) {
715
+ const rnEnc = encodeURIComponent(name);
716
+ const full = await this.client.get(`/public/api/projects/${enc}/recipes/${rnEnc}`);
717
+ const fuzzyPayload = parseRecipePayload(full.payload);
718
+ const inputCount = recipeInputItems({ inputs, }).length || inputDatasets?.length || 2;
719
+ const joinType = normalizedJoinType ?? "LEFT";
720
+ fuzzyPayload.virtualInputs = recipeVirtualInputs(fuzzyPayload, inputCount);
721
+ fuzzyPayload.joins = Array.from({ length: Math.max(0, inputCount - 1), }, (_, index) => {
722
+ const table2 = index + 1;
723
+ return {
724
+ table1: 0,
725
+ table2,
726
+ conditionsMode: "AND",
727
+ type: joinType,
728
+ on: fuzzyKeys.map((key) => ({
729
+ column1: { name: key.left, table: 0, },
730
+ column2: { name: key.right, table: table2, },
731
+ type: "EQ",
732
+ fuzzyMatchDesc: {
733
+ distanceType: fuzzyDistance ?? "LEVENSHTEIN",
734
+ threshold: fuzzyThreshold,
735
+ },
736
+ ...(opts.fuzzyNormalize === true
737
+ ? {
738
+ normaliseDesc: {
739
+ caseInsensitive: true,
740
+ normaliseText: true,
741
+ unicodeCasting: true,
742
+ },
743
+ }
744
+ : {}),
745
+ })),
746
+ };
747
+ });
748
+ await this.client.put(`/public/api/projects/${enc}/recipes/${rnEnc}`, {
749
+ ...full,
750
+ recipe: {
751
+ ...full.recipe,
752
+ inputs,
753
+ outputs,
754
+ },
755
+ payload: JSON.stringify(fuzzyPayload),
756
+ });
640
757
  joinConfigured = true;
641
758
  }
642
759
  let temporaryOutputDatasetDeleted;
@@ -0,0 +1,41 @@
1
+ import { BaseResource } from "./base.js";
2
+ export interface SavedModelListItem extends Record<string, unknown> {
3
+ id?: string;
4
+ name?: string;
5
+ projectKey?: string;
6
+ activeVersion?: string;
7
+ }
8
+ export interface SavedModel extends Record<string, unknown> {
9
+ id?: string;
10
+ name?: string;
11
+ projectKey?: string;
12
+ activeVersion?: string;
13
+ }
14
+ export interface SavedModelVersionListItem extends Record<string, unknown> {
15
+ id?: string;
16
+ active?: boolean;
17
+ createdOn?: number;
18
+ }
19
+ export interface SavedModelVersionDetails extends Record<string, unknown> {
20
+ id?: string;
21
+ algorithm?: string;
22
+ predictionType?: string;
23
+ }
24
+ export interface SavedModelActionResult extends Record<string, unknown> {
25
+ message?: string;
26
+ }
27
+ export declare class SavedModelsResource extends BaseResource {
28
+ /** List saved models in a project. */
29
+ list(projectKey?: string): Promise<SavedModelListItem[]>;
30
+ /** Get saved-model settings and metadata. */
31
+ get(savedModelId: string, projectKey?: string): Promise<SavedModel>;
32
+ /** List all versions of a saved model. */
33
+ listVersions(savedModelId: string, projectKey?: string): Promise<SavedModelVersionListItem[]>;
34
+ /** Get full details for one saved-model version. */
35
+ versionDetails(savedModelId: string, versionId: string, projectKey?: string): Promise<SavedModelVersionDetails>;
36
+ /** Make one saved-model version active. */
37
+ setActiveVersion(savedModelId: string, versionId: string, projectKey?: string): Promise<SavedModelActionResult | undefined>;
38
+ /** Delete a saved model and all of its versions. */
39
+ delete(savedModelId: string, projectKey?: string): Promise<void>;
40
+ private savedModelPath;
41
+ }
@@ -0,0 +1,40 @@
1
+ import { UsageError, } from "../cli/usage.js";
2
+ import { BaseResource, } from "./base.js";
3
+ function requireNonEmpty(value, name) {
4
+ if (typeof value !== "string" || value.trim().length === 0) {
5
+ throw new UsageError(`${name} must be a non-empty string.`, "validation_failed");
6
+ }
7
+ return value;
8
+ }
9
+ export class SavedModelsResource extends BaseResource {
10
+ /** List saved models in a project. */
11
+ async list(projectKey) {
12
+ return this.client.get(`/public/api/projects/${this.enc(projectKey)}/savedmodels/`);
13
+ }
14
+ /** Get saved-model settings and metadata. */
15
+ async get(savedModelId, projectKey) {
16
+ return this.client.get(this.savedModelPath(savedModelId, projectKey));
17
+ }
18
+ /** List all versions of a saved model. */
19
+ async listVersions(savedModelId, projectKey) {
20
+ return this.client.get(`${this.savedModelPath(savedModelId, projectKey)}/versions`);
21
+ }
22
+ /** Get full details for one saved-model version. */
23
+ async versionDetails(savedModelId, versionId, projectKey) {
24
+ const version = encodeURIComponent(requireNonEmpty(versionId, "versionId"));
25
+ return this.client.get(`${this.savedModelPath(savedModelId, projectKey)}/versions/${version}/details`);
26
+ }
27
+ /** Make one saved-model version active. */
28
+ async setActiveVersion(savedModelId, versionId, projectKey) {
29
+ const version = encodeURIComponent(requireNonEmpty(versionId, "versionId"));
30
+ return this.client.post(`${this.savedModelPath(savedModelId, projectKey)}/versions/${version}/actions/setActive`, {});
31
+ }
32
+ /** Delete a saved model and all of its versions. */
33
+ async delete(savedModelId, projectKey) {
34
+ await this.client.del(this.savedModelPath(savedModelId, projectKey));
35
+ }
36
+ savedModelPath(savedModelId, projectKey) {
37
+ const id = encodeURIComponent(requireNonEmpty(savedModelId, "savedModelId"));
38
+ return `/public/api/projects/${this.enc(projectKey)}/savedmodels/${id}`;
39
+ }
40
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataiku-sdk",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Dataiku DSS SDK and CLI for programmatic access to DSS REST APIs",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -147,6 +147,10 @@ export declare const RecipeCreateOptionsSchema: import("@sinclair/typebox").TObj
147
147
  outputConnection: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
148
148
  joinOn: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>]>>;
149
149
  joinType: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
150
+ fuzzyOn: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>]>>;
151
+ fuzzyDistance: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
152
+ fuzzyThreshold: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
153
+ fuzzyNormalize: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
150
154
  projectKey: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
151
155
  outputFolder: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
152
156
  }>;
@@ -195,6 +195,10 @@ export const RecipeCreateOptionsSchema = Type.Object({
195
195
  outputConnection: Type.Optional(Type.String()),
196
196
  joinOn: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String()),])),
197
197
  joinType: Type.Optional(Type.String()),
198
+ fuzzyOn: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String()),])),
199
+ fuzzyDistance: Type.Optional(Type.String()),
200
+ fuzzyThreshold: Type.Optional(Type.Number()),
201
+ fuzzyNormalize: Type.Optional(Type.Boolean()),
198
202
  projectKey: Type.Optional(Type.String()),
199
203
  outputFolder: Type.Optional(Type.String()),
200
204
  });