@picsart/ai-sdk 6.19.0 → 6.20.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 (3) hide show
  1. package/index.d.ts +11 -0
  2. package/index.js +145 -73
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -2276,6 +2276,8 @@ interface EntryMeta {
2276
2276
  catalogOptions?: readonly unknown[];
2277
2277
  disabled?: boolean;
2278
2278
  disabledReason?: string;
2279
+ /** Why the param is required, when a constraint made it so (see `Restriction`). */
2280
+ requiredReason?: string;
2279
2281
  }
2280
2282
  type EnumEntry = EntryMeta & EnumDescriptor<string | number>;
2281
2283
  type CatalogEntry = EntryMeta & CatalogDescriptor;
@@ -2555,6 +2557,12 @@ type ConstraintCondition = Record<string, ConditionOperator>;
2555
2557
  * - `disabled`: param is flagged as constrained — UI interprets per-param kind
2556
2558
  * (dropdowns grey out; text inputs like `prompt` stay editable but surface
2557
2559
  * the reason as an info banner).
2560
+ * - `required`: param becomes required while the condition holds. Expresses
2561
+ * cross-field rules a static `required` flag cannot, e.g. "a prompt or a
2562
+ * first-frame image": `when` both are absent, `then` the prompt is required.
2563
+ * Enforced by `validate()` and at submit, and reported by `paramsFor()` as
2564
+ * `required: true` with `requiredReason`, so a UI gate reads it like any
2565
+ * other required param.
2558
2566
  */
2559
2567
  type Restriction = {
2560
2568
  allowed: unknown[];
@@ -2562,6 +2570,9 @@ type Restriction = {
2562
2570
  } | {
2563
2571
  disabled: true;
2564
2572
  reason?: string;
2573
+ } | {
2574
+ required: true;
2575
+ reason?: string;
2565
2576
  };
2566
2577
  /** Declarative rule: when condition matches, apply restrictions to params. */
2567
2578
  interface Constraint {
package/index.js CHANGED
@@ -416,6 +416,63 @@ var require_build = __commonJS({
416
416
  }
417
417
  });
418
418
 
419
+ // src/core/constraints.ts
420
+ function normalize(r) {
421
+ if ("required" in r) return null;
422
+ if ("disabled" in r) return { kind: "disabled", reason: r.reason };
423
+ return { kind: "allowed", allowed: r.allowed, reason: r.reason };
424
+ }
425
+ function matchOperator(op, actual) {
426
+ if ("exists" in op) {
427
+ const has = actual != null && (!Array.isArray(actual) || actual.length > 0) && (typeof actual !== "string" || actual.trim().length > 0);
428
+ return op.exists ? has : !has;
429
+ }
430
+ if ("is" in op) return actual === op.is;
431
+ return false;
432
+ }
433
+ function matchCondition(when, values) {
434
+ return Object.entries(when).every(
435
+ ([key, op]) => matchOperator(op, values[key])
436
+ );
437
+ }
438
+ function merge(prev, next) {
439
+ if (!prev) return next;
440
+ if (prev.kind === "disabled" || next.kind === "disabled") {
441
+ return { kind: "disabled", reason: next.kind === "disabled" ? next.reason : prev.kind === "disabled" ? prev.reason : void 0 };
442
+ }
443
+ const allowed = new Set(next.allowed.map(String));
444
+ return {
445
+ kind: "allowed",
446
+ allowed: prev.allowed.filter((o) => allowed.has(String(o))),
447
+ reason: next.reason ?? prev.reason
448
+ };
449
+ }
450
+ function evaluateConstraints(constraints, values) {
451
+ const effects = /* @__PURE__ */ new Map();
452
+ if (!constraints?.length) return effects;
453
+ for (const rule of constraints) {
454
+ if (!matchCondition(rule.when, values)) continue;
455
+ for (const [key, restriction] of Object.entries(rule.then)) {
456
+ const normalized = normalize(restriction);
457
+ if (normalized) effects.set(key, merge(effects.get(key), normalized));
458
+ }
459
+ }
460
+ return effects;
461
+ }
462
+ function evaluateRequirements(constraints, values) {
463
+ const requirements = /* @__PURE__ */ new Map();
464
+ if (!constraints?.length) return requirements;
465
+ for (const rule of constraints) {
466
+ if (!matchCondition(rule.when, values)) continue;
467
+ for (const [key, restriction] of Object.entries(rule.then)) {
468
+ if ("required" in restriction && !requirements.has(key)) {
469
+ requirements.set(key, { reason: restriction.reason });
470
+ }
471
+ }
472
+ }
473
+ return requirements;
474
+ }
475
+
419
476
  // src/core/descriptors/utils.ts
420
477
  function extractDefaults(params2) {
421
478
  const defaults = {};
@@ -521,10 +578,18 @@ function validateDescriptor(key, d, val, required) {
521
578
  }
522
579
  }
523
580
  }
524
- function validateAll(params2, input) {
581
+ function isAbsent(val) {
582
+ return val == null || typeof val === "string" && val.trim().length === 0 || Array.isArray(val) && val.length === 0;
583
+ }
584
+ function validateAll(params2, input, constraints) {
525
585
  for (const [key, entry] of Object.entries(params2)) {
526
586
  validateDescriptor(key, entry.descriptor, input[key], entry.required);
527
587
  }
588
+ for (const [key, requirement] of evaluateRequirements(constraints, input)) {
589
+ if (isAbsent(input[key])) {
590
+ throw new Error(requirement.reason ? `"${key}" is required: ${requirement.reason}` : `"${key}" is required`);
591
+ }
592
+ }
528
593
  }
529
594
  function descriptorsToSchema(params2) {
530
595
  const schema = {};
@@ -2368,6 +2433,14 @@ var { MODELS: MODELS2 } = defineModels("ltx", [
2368
2433
  inputType: "a2v",
2369
2434
  description: "Generate video driven by an audio track \u2014 2-20s, optional image for first frame.",
2370
2435
  features: [feat("Audio Input", "audio"), feat("Image Input", "input"), feat("2\u201320 sec", "duration")],
2436
+ // Vendor: a prompt is required unless a first-frame image drives the scene.
2437
+ // Declared here so `validate()` and a UI gate see it; the builder's own
2438
+ // throw stays for callers that build a payload without validating.
2439
+ constraints: [
2440
+ { when: { prompt: { exists: false }, imageUrls: { exists: false } }, then: {
2441
+ prompt: { required: true, reason: "Add a prompt or a first-frame image." }
2442
+ } }
2443
+ ],
2371
2444
  paramConfig: {
2372
2445
  ...params.prompt({ required: false, maxLength: LTX_PROMPT_MAX }),
2373
2446
  ...params.audioInput("Audio Track", true),
@@ -3510,11 +3583,23 @@ var wanV3Constraints = [
3510
3583
  imageUrls: { disabled: true, reason: WAN_V3_FRAME_REF_REASON },
3511
3584
  videoUrls: { disabled: true, reason: WAN_V3_FRAME_REF_REASON },
3512
3585
  audioUrls: { disabled: true, reason: WAN_V3_FRAME_REF_REASON }
3586
+ } },
3587
+ // Vendor: either a prompt or media. With no media at all, the prompt is
3588
+ // required (the payload builder enforces the same rule for direct callers).
3589
+ { when: {
3590
+ prompt: { exists: false },
3591
+ startFrame: { exists: false },
3592
+ endFrame: { exists: false },
3593
+ imageUrls: { exists: false },
3594
+ videoUrls: { exists: false },
3595
+ audioUrls: { exists: false }
3596
+ }, then: {
3597
+ prompt: { required: true, reason: "Add a prompt or at least one frame or reference." }
3513
3598
  } }
3514
3599
  ];
3515
3600
  var wanV3Features = [feat("Image Input", "input"), feat("Video Input", "input"), feat("Audio", "audio"), feat("Start/End Frame", "frame"), feat("1080P", "resolution"), feat("Adaptive Ratio", "resolution")];
3516
3601
  var wanV3ParamConfig = {
3517
- // Vendor: 'either prompt or media' — the builder enforces the cross-field rule.
3602
+ // Vendor: 'either prompt or media' — declared in wanV3Constraints.
3518
3603
  ...params.prompt({ required: false, maxLength: 5e3 }),
3519
3604
  // Vendor: integer 2-30, or -1 = Smart duration mode (model picks the length).
3520
3605
  duration: {
@@ -4424,6 +4509,20 @@ var seedance25DraftConstraints = [
4424
4509
  then: { resolution: { allowed: [SEEDANCE_25_DRAFT_RESOLUTION], reason: SEEDANCE_25_DRAFT_RESOLUTION_REASON } }
4425
4510
  }
4426
4511
  ];
4512
+ var seedance25ContentRequiredConstraints = [
4513
+ {
4514
+ when: {
4515
+ prompt: { exists: false },
4516
+ imageUrls: { exists: false },
4517
+ videoUrls: { exists: false },
4518
+ audioUrls: { exists: false },
4519
+ startFrame: { exists: false },
4520
+ endFrame: { exists: false },
4521
+ draftTask: { exists: false }
4522
+ },
4523
+ then: { prompt: { required: true, reason: "Add a prompt or at least one image, video, or audio." } }
4524
+ }
4525
+ ];
4427
4526
  var seedance25DraftFinalConstraints = [
4428
4527
  ...seedance25DraftConstraints,
4429
4528
  {
@@ -4574,7 +4673,7 @@ var { MODELS: MODELS13 } = defineModels("seedance", [
4574
4673
  addedAt: "2026-08-06",
4575
4674
  workflow: "seedance",
4576
4675
  buildPayload: buildSeedance25PayloadFor("seedance_2_5"),
4577
- constraints: [...seedance25Constraints, ...seedance25DraftFinalConstraints],
4676
+ constraints: [...seedance25Constraints, ...seedance25DraftFinalConstraints, ...seedance25ContentRequiredConstraints],
4578
4677
  estimatedTime: 20,
4579
4678
  mode: "video",
4580
4679
  inputType: "t2v",
@@ -4614,7 +4713,7 @@ var { MODELS: MODELS13 } = defineModels("seedance", [
4614
4713
  release: "preview",
4615
4714
  workflow: "seedance",
4616
4715
  buildPayload: buildSeedance25PayloadFor("seedance_2_5_without_moderation"),
4617
- constraints: [...seedance25Constraints, ...seedance25DraftFinalConstraints],
4716
+ constraints: [...seedance25Constraints, ...seedance25DraftFinalConstraints, ...seedance25ContentRequiredConstraints],
4618
4717
  estimatedTime: 20,
4619
4718
  mode: "video",
4620
4719
  inputType: "t2v",
@@ -10711,7 +10810,7 @@ function buildInputSchema(model) {
10711
10810
  parse(input) {
10712
10811
  requireObject(input, `Invalid input for model "${model.id}"`);
10713
10812
  try {
10714
- validateAll(model.paramConfig, input);
10813
+ validateAll(model.paramConfig, input, model.constraints);
10715
10814
  } catch (err) {
10716
10815
  if (err instanceof ApiError) throw err;
10717
10816
  throw new ApiError(err instanceof Error ? err.message : String(err), {
@@ -13842,48 +13941,6 @@ var Models = {
13842
13941
  Wan30VideoPrime
13843
13942
  };
13844
13943
 
13845
- // src/core/constraints.ts
13846
- function normalize(r) {
13847
- if ("disabled" in r) return { kind: "disabled", reason: r.reason };
13848
- return { kind: "allowed", allowed: r.allowed, reason: r.reason };
13849
- }
13850
- function matchOperator(op, actual) {
13851
- if ("exists" in op) {
13852
- const has = actual != null && (!Array.isArray(actual) || actual.length > 0) && (typeof actual !== "string" || actual.length > 0);
13853
- return op.exists ? has : !has;
13854
- }
13855
- if ("is" in op) return actual === op.is;
13856
- return false;
13857
- }
13858
- function matchCondition(when, values) {
13859
- return Object.entries(when).every(
13860
- ([key, op]) => matchOperator(op, values[key])
13861
- );
13862
- }
13863
- function merge(prev, next) {
13864
- if (!prev) return next;
13865
- if (prev.kind === "disabled" || next.kind === "disabled") {
13866
- return { kind: "disabled", reason: next.kind === "disabled" ? next.reason : prev.kind === "disabled" ? prev.reason : void 0 };
13867
- }
13868
- const allowed = new Set(next.allowed.map(String));
13869
- return {
13870
- kind: "allowed",
13871
- allowed: prev.allowed.filter((o) => allowed.has(String(o))),
13872
- reason: next.reason ?? prev.reason
13873
- };
13874
- }
13875
- function evaluateConstraints(constraints, values) {
13876
- const effects = /* @__PURE__ */ new Map();
13877
- if (!constraints?.length) return effects;
13878
- for (const rule of constraints) {
13879
- if (!matchCondition(rule.when, values)) continue;
13880
- for (const [key, restriction] of Object.entries(rule.then)) {
13881
- effects.set(key, merge(effects.get(key), normalize(restriction)));
13882
- }
13883
- }
13884
- return effects;
13885
- }
13886
-
13887
13944
  // src/core/descriptors/pricing.ts
13888
13945
  var import_pa_model_pricing_sdk = __toESM(require_build(), 1);
13889
13946
  var _client = null;
@@ -14054,64 +14111,64 @@ var ModelParamsAccessorImpl = class {
14054
14111
  var ConstrainedParamsAccessor = class {
14055
14112
  inner;
14056
14113
  effects;
14057
- constructor(inner, effects) {
14114
+ requirements;
14115
+ constructor(inner, effects, requirements = /* @__PURE__ */ new Map()) {
14058
14116
  this.inner = inner;
14059
14117
  this.effects = effects;
14118
+ this.requirements = requirements;
14060
14119
  }
14061
14120
  // ── Decorated accessors ──────────────────────────────────────────
14062
14121
  enum(key) {
14063
- return this.applyEnum(key, this.inner.enum(key));
14122
+ return this.require(key, this.applyEnum(key, this.inner.enum(key)));
14064
14123
  }
14065
14124
  catalog(key) {
14066
- return this.applyEntry(key, this.inner.catalog(key));
14125
+ return this.require(key, this.applyEntry(key, this.inner.catalog(key)));
14067
14126
  }
14068
14127
  range(key) {
14069
- return this.applyEntry(key, this.inner.range(key));
14128
+ return this.require(key, this.applyEntry(key, this.inner.range(key)));
14070
14129
  }
14071
14130
  boolean(key) {
14072
- return this.applyEntry(key, this.inner.boolean(key));
14131
+ return this.require(key, this.applyEntry(key, this.inner.boolean(key)));
14073
14132
  }
14074
14133
  text(key) {
14075
- return this.applyEntry(key, this.inner.text(key));
14134
+ return this.require(key, this.applyEntry(key, this.inner.text(key)));
14076
14135
  }
14077
14136
  file(key) {
14078
- return this.applyEntry(key, this.inner.file(key));
14137
+ return this.require(key, this.applyEntry(key, this.inner.file(key)));
14079
14138
  }
14080
14139
  prompt() {
14081
- return this.applyEntry("prompt", this.inner.prompt());
14140
+ return this.require("prompt", this.applyEntry("prompt", this.inner.prompt()));
14082
14141
  }
14083
14142
  aspectRatio() {
14084
- return this.applyEnum("aspectRatio", this.inner.aspectRatio());
14143
+ return this.require("aspectRatio", this.applyEnum("aspectRatio", this.inner.aspectRatio()));
14085
14144
  }
14086
14145
  duration() {
14087
14146
  const entry = this.inner.duration();
14088
- return entry?.kind === "enum" ? this.applyEnum("duration", entry) : this.applyEntry("duration", entry);
14147
+ return this.require("duration", entry?.kind === "enum" ? this.applyEnum("duration", entry) : this.applyEntry("duration", entry));
14089
14148
  }
14090
14149
  resolution() {
14091
- return this.applyEnum("resolution", this.inner.resolution());
14150
+ return this.require("resolution", this.applyEnum("resolution", this.inner.resolution()));
14092
14151
  }
14093
14152
  generateAudio() {
14094
- return this.applyEntry("generateAudio", this.inner.generateAudio());
14153
+ return this.require("generateAudio", this.applyEntry("generateAudio", this.inner.generateAudio()));
14095
14154
  }
14096
14155
  startFrame() {
14097
- return this.applyEntry("startFrame", this.inner.startFrame());
14156
+ return this.require("startFrame", this.applyEntry("startFrame", this.inner.startFrame()));
14098
14157
  }
14099
14158
  endFrame() {
14100
- return this.applyEntry("endFrame", this.inner.endFrame());
14159
+ return this.require("endFrame", this.applyEntry("endFrame", this.inner.endFrame()));
14101
14160
  }
14102
14161
  all() {
14103
- return this.inner.all().map((e) => {
14104
- const r = this.effects.get(e.key);
14105
- if (!r) return e;
14106
- if (e.kind === "enum") return this.decorateEnumFlat(e, r);
14107
- if (r.kind === "disabled") return { ...e, disabled: true, disabledReason: r.reason };
14108
- return e;
14109
- });
14162
+ return this.inner.all().map((e) => this.decorateFlat(e.key, e));
14110
14163
  }
14111
- // ── Pass-through delegates ───────────────────────────────────────
14164
+ /** Generic lookup, decorated exactly as `all()` decorates the same key, so a
14165
+ * caller that reads params by key sees the same restrictions and
14166
+ * requirements as one that iterates them. */
14112
14167
  param(key) {
14113
- return this.inner.param(key);
14168
+ const entry = this.inner.param(key);
14169
+ return entry && this.decorateFlat(key, entry);
14114
14170
  }
14171
+ // ── Pass-through delegates ───────────────────────────────────────
14115
14172
  hasParam(key) {
14116
14173
  return this.inner.hasParam(key);
14117
14174
  }
@@ -14131,6 +14188,20 @@ var ConstrainedParamsAccessor = class {
14131
14188
  return this.inner.transferValues(prev);
14132
14189
  }
14133
14190
  // ── Private helpers ──────────────────────────────────────────────
14191
+ decorateFlat(key, entry) {
14192
+ const r = this.effects.get(key);
14193
+ let decorated = entry;
14194
+ if (r && entry.kind === "enum") decorated = this.decorateEnumFlat(entry, r);
14195
+ else if (r?.kind === "disabled") decorated = { ...entry, disabled: true, disabledReason: r.reason };
14196
+ return this.require(key, decorated);
14197
+ }
14198
+ /** Mark the entry required when a matching `required` restriction names it. */
14199
+ require(key, entry) {
14200
+ if (!entry) return void 0;
14201
+ const requirement = this.requirements.get(key);
14202
+ if (!requirement) return entry;
14203
+ return { ...entry, required: true, requiredReason: requirement.reason };
14204
+ }
14134
14205
  applyEntry(key, entry) {
14135
14206
  if (!entry) return void 0;
14136
14207
  const r = this.effects.get(key);
@@ -14213,15 +14284,16 @@ var ModelDescriptorImpl = class {
14213
14284
  paramsFor(values) {
14214
14285
  const inner = this.params();
14215
14286
  const effects = evaluateConstraints(this.def.constraints, values);
14216
- if (!effects.size) return inner;
14217
- return new ConstrainedParamsAccessor(inner, effects);
14287
+ const requirements = evaluateRequirements(this.def.constraints, values);
14288
+ if (!effects.size && !requirements.size) return inner;
14289
+ return new ConstrainedParamsAccessor(inner, effects, requirements);
14218
14290
  }
14219
14291
  validate(input) {
14220
14292
  if (!input || typeof input !== "object" || Array.isArray(input)) {
14221
14293
  return { valid: false, errors: [`Invalid input for model "${this.def.id}"`] };
14222
14294
  }
14223
14295
  try {
14224
- validateAll(this.def.paramConfig, input);
14296
+ validateAll(this.def.paramConfig, input, this.def.constraints);
14225
14297
  return { valid: true };
14226
14298
  } catch (err) {
14227
14299
  return { valid: false, errors: [err instanceof Error ? err.message : String(err)] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picsart/ai-sdk",
3
- "version": "6.19.0",
3
+ "version": "6.20.0",
4
4
  "type": "module",
5
5
  "description": "Type-safe SDK for 100+ AI models — image, video, audio, and text generation with Picsart",
6
6
  "license": "MIT",