@picsart/ai-sdk 5.24.1 → 5.26.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.
package/index.d.ts CHANGED
@@ -49,6 +49,10 @@ interface WorkflowStatusResult<TResult = unknown> {
49
49
  status: WorkflowStatus;
50
50
  result?: TResult;
51
51
  error?: string;
52
+ /** Platform error `reason` on a failed task, when the response carried one. */
53
+ reason?: string;
54
+ /** Numeric status on the error payload, when the response carried one. */
55
+ statusCode?: number;
52
56
  progress?: WorkflowProgress;
53
57
  /** Credit usage reported by the platform, when present on the response. */
54
58
  usage?: CreditUsage;
@@ -806,6 +810,18 @@ type ModelInputById = {
806
810
  seed?: number;
807
811
  enableSafetyChecker?: boolean;
808
812
  };
813
+ "minimax-h3-max-r2v": {
814
+ prompt: string;
815
+ imageUrls?: string[];
816
+ videoUrls?: string[];
817
+ audioUrls?: string[];
818
+ resolution?: "480p" | "768p";
819
+ duration?: number;
820
+ aspectRatio?: "adaptive" | "21:9" | "16:9" | "4:3" | "1:1" | "3:4" | "9:16";
821
+ promptExpansionMode?: "balanced" | "quality";
822
+ seed?: number;
823
+ enableSafetyChecker?: boolean;
824
+ };
809
825
  "minimax-music-v2": {
810
826
  prompt: string;
811
827
  lyricsPrompt?: string;
@@ -2641,6 +2657,7 @@ declare const Models: {
2641
2657
  readonly Minimax02Hd: "minimax-02-hd";
2642
2658
  readonly MinimaxH3: "minimax-h3";
2643
2659
  readonly MinimaxH3Max: "minimax-h3-max";
2660
+ readonly MinimaxH3MaxR2v: "minimax-h3-max-r2v";
2644
2661
  readonly MinimaxMusicV2: "minimax-music-v2";
2645
2662
  readonly MinimaxMusicV3: "minimax-music-v3";
2646
2663
  readonly MuseImage10: "muse-image-1.0";
@@ -2774,6 +2791,59 @@ declare function getVoiceById(id: string): VoiceOption | undefined;
2774
2791
  /** @deprecated Load the model's catalog instead (`ai.catalogs.voices(modelId)`) — loaded voices are searched automatically. */
2775
2792
  declare function getVoiceById(id: string, extra: VoiceOption[] | undefined): VoiceOption | undefined;
2776
2793
 
2794
+ /**
2795
+ * Failure codes the SDK synthesizes when the platform supplies no `reason`.
2796
+ * An API-supplied `reason` passes through unchanged, so the open `string`
2797
+ * member keeps arbitrary platform reasons assignable while preserving
2798
+ * autocomplete on the known set.
2799
+ */
2800
+ type ApiErrorCode = 'unknown_model' | 'wrong_model_mode' | 'validation_error' | 'unsupported_transport' | 'timeout' | 'aborted' | 'canceled' | 'generation_failed' | 'invalid_response' | 'bad_request' | 'unauthorized' | 'payment_required' | 'forbidden' | 'not_found' | 'rate_limited' | 'server_error' | (string & {});
2801
+ /** Everything but the message needed to build a {@link ApiError}. */
2802
+ interface ApiErrorInit {
2803
+ /**
2804
+ * HTTP status of the failing response. When no HTTP exchange took place the
2805
+ * SDK synthesizes the semantically matching code: 400 for input the SDK
2806
+ * itself rejects, 408 for a poll deadline, 499 for an abort or cancel, 502
2807
+ * for a response it cannot make sense of.
2808
+ */
2809
+ status: number;
2810
+ /** Platform `reason` when present, otherwise an SDK-synthesized code. */
2811
+ code: ApiErrorCode;
2812
+ }
2813
+ /**
2814
+ * The single error type thrown by the SDK's generation surface —
2815
+ * `generate()`, `generateText()`, `submit()`, and `result()`.
2816
+ *
2817
+ * Unrelated to the `Api*` types (`ApiResponse`, `ApiRunOptions`, …), which
2818
+ * describe the low-level `ai.apis` surface. `ai.apis.run()` throws the
2819
+ * workflows client's own errors, not this.
2820
+ *
2821
+ * ```ts
2822
+ * try {
2823
+ * await ai.generate(Models.Flux2Pro, { prompt: 'a cat' });
2824
+ * } catch (err) {
2825
+ * if (err instanceof ApiError) {
2826
+ * if (err.status === 402) return topUpCredits();
2827
+ * if (err.status === 429 || err.status >= 500) return retry();
2828
+ * if (err.code === 'validation_error') return showFormError(err.message);
2829
+ * }
2830
+ * throw err;
2831
+ * }
2832
+ * ```
2833
+ *
2834
+ * Aborts raised by `fetch` itself are never wrapped — a caller checking
2835
+ * `err.name === 'AbortError'` on a `DOMException` keeps working.
2836
+ */
2837
+ declare class ApiError extends Error {
2838
+ /** HTTP status, or the synthesized equivalent for non-HTTP failures. */
2839
+ readonly status: number;
2840
+ /** Platform `reason`, or an SDK-synthesized code. Always equal to {@link reason}. */
2841
+ readonly code: ApiErrorCode;
2842
+ /** Alias of {@link code}, named after the platform's own error field. */
2843
+ readonly reason: ApiErrorCode;
2844
+ constructor(message: string, init: ApiErrorInit);
2845
+ }
2846
+
2777
2847
  /**
2778
2848
  * Pricing internals — owns the ModelPricingClient, the per-model cache, and
2779
2849
  * the credit-range lookup. The Model accessor delegates to the helpers below
@@ -2902,4 +2972,4 @@ declare const findModel: (ref: string) => ModelDefinition | undefined;
2902
2972
  */
2903
2973
  declare const KLING_DUAL_IMAGE_EFFECTS: ReadonlySet<string>;
2904
2974
 
2905
- export { ALL_MODELS, type AiClient, type ApiResponse, type ApiRunOptions, type ApiSchemas, type ApisClient, type AppIdentity, type AppType, type AuthenticatedFetch, type AvatarOption, type BooleanDescriptor, type BooleanEntry, type CatalogDescriptor, type CatalogEntry, type CatalogItem, type CatalogKind, type CatalogPage, type CatalogPageOptions, type CatalogPreview, type CatalogQuery, type CatalogResult, type CatalogSource, type CatalogsClient, type CatalogsOptions, type ClientConfig, type CreditRange, type CreditRangeContext, type CreditTier, type CreditUsage, DEFAULT_VISIBLE_RELEASES, type DeepLinkResult, type DriveAttributes, type DriveClient, type DriveConfig, type DriveFile, type DriveFileDetails, type DriveFolder, type DriveMediaItem, type DriveSaveResult, type EntryMeta, type EnumDescriptor, type EnumEntry, type EnumOption, type FileDescriptor, type FileEntry, type FlatParamEntry, type GenerateOptions, type GenerateResult, type GenerateResultItem, type GenerateTextResult, type GenerationContext, type GenerationFile, type GenerationMode, KLING_DUAL_IMAGE_EFFECTS, type ListOptions, type MediaModelId, type MediaTypeFilter, Model, type ModelDefinition, type ModelDescriptor, type ModelFilter$1 as ModelFilter, type ModelInput, type ModelInputById, type ModelMeta, type ModelParams, type ModelParamsAccessor, Models, type ObjectDescriptor, type ObjectEntry, type ParamDescriptor, type ParamEntry, type ParamOption, type PayloadDriveFolderOptions, type PayloadDriveOptions, type PricingOptions, type ProviderInfo, type RangeDescriptor, type RangeEntry, type ReleaseTag, type SaveParams, type SdkPayload, type SdkTransport, type TextDescriptor, type TextEntry, type TextModelId, type TextModelInputById, type ToolUsage, type TypedModelId, type UserReaction, type ValidationResult$1 as ValidationResult, type VoiceOption, type WorkflowJobHandle, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, toAvatarOption, toVoiceOption };
2975
+ export { ALL_MODELS, type AiClient, ApiError, type ApiErrorCode, type ApiErrorInit, type ApiResponse, type ApiRunOptions, type ApiSchemas, type ApisClient, type AppIdentity, type AppType, type AuthenticatedFetch, type AvatarOption, type BooleanDescriptor, type BooleanEntry, type CatalogDescriptor, type CatalogEntry, type CatalogItem, type CatalogKind, type CatalogPage, type CatalogPageOptions, type CatalogPreview, type CatalogQuery, type CatalogResult, type CatalogSource, type CatalogsClient, type CatalogsOptions, type ClientConfig, type CreditRange, type CreditRangeContext, type CreditTier, type CreditUsage, DEFAULT_VISIBLE_RELEASES, type DeepLinkResult, type DriveAttributes, type DriveClient, type DriveConfig, type DriveFile, type DriveFileDetails, type DriveFolder, type DriveMediaItem, type DriveSaveResult, type EntryMeta, type EnumDescriptor, type EnumEntry, type EnumOption, type FileDescriptor, type FileEntry, type FlatParamEntry, type GenerateOptions, type GenerateResult, type GenerateResultItem, type GenerateTextResult, type GenerationContext, type GenerationFile, type GenerationMode, KLING_DUAL_IMAGE_EFFECTS, type ListOptions, type MediaModelId, type MediaTypeFilter, Model, type ModelDefinition, type ModelDescriptor, type ModelFilter$1 as ModelFilter, type ModelInput, type ModelInputById, type ModelMeta, type ModelParams, type ModelParamsAccessor, Models, type ObjectDescriptor, type ObjectEntry, type ParamDescriptor, type ParamEntry, type ParamOption, type PayloadDriveFolderOptions, type PayloadDriveOptions, type PricingOptions, type ProviderInfo, type RangeDescriptor, type RangeEntry, type ReleaseTag, type SaveParams, type SdkPayload, type SdkTransport, type TextDescriptor, type TextEntry, type TextModelId, type TextModelInputById, type ToolUsage, type TypedModelId, type UserReaction, type ValidationResult$1 as ValidationResult, type VoiceOption, type WorkflowJobHandle, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, toAvatarOption, toVoiceOption };
package/index.js CHANGED
@@ -441,6 +441,62 @@ var require_build = __commonJS({
441
441
  }
442
442
  });
443
443
 
444
+ // src/core/errors.ts
445
+ var ApiError = class extends Error {
446
+ /** HTTP status, or the synthesized equivalent for non-HTTP failures. */
447
+ status;
448
+ /** Platform `reason`, or an SDK-synthesized code. Always equal to {@link reason}. */
449
+ code;
450
+ /** Alias of {@link code}, named after the platform's own error field. */
451
+ reason;
452
+ constructor(message, init) {
453
+ super(message);
454
+ this.name = "ApiError";
455
+ this.status = init.status;
456
+ this.code = init.code;
457
+ this.reason = init.code;
458
+ }
459
+ };
460
+ var CODE_BY_STATUS = {
461
+ 400: "bad_request",
462
+ 401: "unauthorized",
463
+ 402: "payment_required",
464
+ 403: "forbidden",
465
+ 404: "not_found",
466
+ 408: "timeout",
467
+ 409: "conflict",
468
+ 413: "payload_too_large",
469
+ 422: "unprocessable_entity",
470
+ 429: "rate_limited",
471
+ 500: "server_error",
472
+ 502: "bad_gateway",
473
+ 503: "service_unavailable",
474
+ 504: "gateway_timeout"
475
+ };
476
+ function codeForStatus(status) {
477
+ return CODE_BY_STATUS[status] ?? (status >= 500 ? "server_error" : `http_${status}`);
478
+ }
479
+ async function readErrorBody(res) {
480
+ let text = "";
481
+ try {
482
+ text = await res.text();
483
+ } catch {
484
+ return { text: "" };
485
+ }
486
+ try {
487
+ const parsed = JSON.parse(text);
488
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
489
+ return { text, json: parsed };
490
+ }
491
+ } catch {
492
+ }
493
+ return { text };
494
+ }
495
+ function reasonFrom(json, status) {
496
+ const raw = json?.reason ?? json?.code;
497
+ return typeof raw === "string" && raw.length > 0 ? raw : codeForStatus(status);
498
+ }
499
+
444
500
  // src/core/workflow.ts
445
501
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
446
502
  var DEFAULT_MAX_ATTEMPTS = 300;
@@ -476,7 +532,9 @@ function parseWorkflowStatus(handle, raw) {
476
532
  const result = pickFirst(raw, [["response", "result"], ["result"]]);
477
533
  const usageRaw = pickFirst(raw, [["response", "usage"], ["usage"]]);
478
534
  const usage = usageRaw && typeof usageRaw === "object" && (typeof usageRaw.credits === "number" || Array.isArray(usageRaw.details)) ? usageRaw : void 0;
479
- const errorRaw = pickFirst(raw, [["response", "error"], ["error"], ["message"], ["reason"]]);
535
+ const errorRaw = pickFirst(raw, [["response", "error"], ["response", "message"], ["error"], ["message"], ["reason"]]);
536
+ const reasonRaw = pickFirst(raw, [["response", "reason"], ["reason"]]);
537
+ const statusCodeRaw = pickFirst(raw, [["response", "statusCode"], ["statusCode"]]);
480
538
  const progressRaw = pickFirst(raw, [["response", "progress"], ["progress"]]);
481
539
  const progress = progressRaw && typeof progressRaw === "object" ? {
482
540
  percent: typeof progressRaw.percent === "number" ? progressRaw.percent : void 0,
@@ -487,6 +545,8 @@ function parseWorkflowStatus(handle, raw) {
487
545
  status,
488
546
  result,
489
547
  error: typeof errorRaw === "string" ? errorRaw : void 0,
548
+ reason: typeof reasonRaw === "string" ? reasonRaw : void 0,
549
+ statusCode: typeof statusCodeRaw === "number" ? statusCodeRaw : void 0,
490
550
  progress,
491
551
  usage,
492
552
  raw
@@ -502,13 +562,19 @@ function createWorkflowClient(transport, options = {}) {
502
562
  const defaultMaxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
503
563
  const submit = async (request) => {
504
564
  if (!transport.submit) {
505
- throw new Error("Transport does not support submit (execute-only transport)");
565
+ throw new ApiError("Transport does not support submit (execute-only transport)", {
566
+ status: 400,
567
+ code: "unsupported_transport"
568
+ });
506
569
  }
507
570
  return transport.submit(request);
508
571
  };
509
572
  const status = async (handle, signal) => {
510
573
  if (!transport.status) {
511
- throw new Error("Transport does not support status (execute-only transport)");
574
+ throw new ApiError("Transport does not support status (execute-only transport)", {
575
+ status: 400,
576
+ code: "unsupported_transport"
577
+ });
512
578
  }
513
579
  const raw = await transport.status(handle, signal);
514
580
  return parseStatus(handle, raw);
@@ -518,13 +584,16 @@ function createWorkflowClient(transport, options = {}) {
518
584
  const maxAttempts = pollOptions.maxAttempts ?? defaultMaxAttempts;
519
585
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
520
586
  if (pollOptions.signal?.aborted) {
521
- throw new Error("Operation aborted");
587
+ throw new ApiError("Operation aborted", { status: 499, code: "aborted" });
522
588
  }
523
589
  const next = await status(handle, pollOptions.signal);
524
590
  if (isTerminal(next.status)) return next;
525
591
  await sleep2(intervalMs);
526
592
  }
527
- throw new Error(`Timed out waiting for workflow ${handle.workflow}:${handle.id}`);
593
+ throw new ApiError(
594
+ `Timed out waiting for workflow ${handle.workflow}:${handle.id}`,
595
+ { status: 408, code: "timeout" }
596
+ );
528
597
  };
529
598
  const run = async (request, runOptions = {}) => {
530
599
  const runMode = runOptions.mode;
@@ -543,14 +612,17 @@ function createWorkflowClient(transport, options = {}) {
543
612
  const maxAttempts = subscribeOptions.maxAttempts ?? defaultMaxAttempts;
544
613
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
545
614
  if (subscribeOptions.signal?.aborted) {
546
- throw new Error("Operation aborted");
615
+ throw new ApiError("Operation aborted", { status: 499, code: "aborted" });
547
616
  }
548
617
  const next = await status(handle, subscribeOptions.signal);
549
618
  yield next;
550
619
  if (isTerminal(next.status)) return next;
551
620
  await sleep2(intervalMs);
552
621
  }
553
- throw new Error(`Timed out waiting for workflow ${handle.workflow}:${handle.id}`);
622
+ throw new ApiError(
623
+ `Timed out waiting for workflow ${handle.workflow}:${handle.id}`,
624
+ { status: 408, code: "timeout" }
625
+ );
554
626
  };
555
627
  return { submit, status, result, run, subscribe };
556
628
  }
@@ -6457,7 +6529,7 @@ var { MODELS: MODELS26 } = defineModels("minimax", [
6457
6529
  addedAt: "2026-08-28",
6458
6530
  workflow: "minimax/h3-max/text-to-video",
6459
6531
  editWorkflow: "minimax/h3-max/image-to-video",
6460
- estimatedTime: 180,
6532
+ estimatedTime: 5,
6461
6533
  mode: "video",
6462
6534
  inputType: "t2v",
6463
6535
  description: "Top-tier MiniMax H3 Max video from text or a start/end frame, with prompt expansion. Up to 15s at 768p.",
@@ -6492,6 +6564,49 @@ var { MODELS: MODELS26 } = defineModels("minimax", [
6492
6564
  aspectRatio: { disabled: true, reason: "Aspect ratio follows the start frame image." }
6493
6565
  } }
6494
6566
  ]
6567
+ },
6568
+ {
6569
+ // Reference-to-video sibling of minimax-h3-max (same fal.ai worker).
6570
+ // The prompt addresses references by modality and order — Image 1,
6571
+ // Video 1, Audio 1, … Reference clips are 2-15s each (≤15s combined per
6572
+ // modality) and images + videos + audios must add up to ≤12 files —
6573
+ // backend-enforced; paramConfig only carries the per-array maxima.
6574
+ id: "minimax-h3-max-r2v",
6575
+ name: "MiniMax H3 Max Ref-to-Video",
6576
+ modelId: "fal-ai-h3-max",
6577
+ addedAt: "2026-09-01",
6578
+ workflow: "minimax/h3-max/reference-to-video",
6579
+ estimatedTime: 5,
6580
+ mode: "video",
6581
+ inputType: "i2v",
6582
+ description: "MiniMax H3 Max video from reference images, videos, and audio \u2014 refer to them in the prompt as Image 1, Video 1, Audio 1, in input order. Up to 15s at 768p.",
6583
+ features: [
6584
+ feat("Multi-Image Input", "input"),
6585
+ feat("Video Input", "input"),
6586
+ feat("Audio Input", "input"),
6587
+ feat("768p", "resolution"),
6588
+ feat("5-15 sec", "duration")
6589
+ ],
6590
+ paramConfig: {
6591
+ ...params.prompt({ placeholder: "Image 1 is the protagonist. Keep her consistent with the reference while she walks through a sunlit garden..." }),
6592
+ ...params.imageInput(9, "Reference Images"),
6593
+ ...params.videoInputs(3, "Reference Videos"),
6594
+ ...params.audioInputs(3, "Reference Audios"),
6595
+ // Lowercase on purpose — same worker normalization as minimax-h3-max.
6596
+ ...params.resolution(["480p", "768p"], "768p"),
6597
+ ...params.durationRange(5, 15, 5),
6598
+ ...params.aspectRatio(["adaptive", "21:9", "16:9", "4:3", "1:1", "3:4", "9:16"], "adaptive"),
6599
+ // Unlike the T2V/I2V entry, this wire has no 'disabled' expansion mode.
6600
+ ...p.enum("promptExpansionMode", ["balanced", "quality"], "balanced", { label: "Prompt Expansion" }),
6601
+ // -1 (sentinel) means "pick a random seed"; the builder drops it.
6602
+ ...p.range("seed", -1, 2147483647, -1),
6603
+ ...p.boolean("enableSafetyChecker", true, "Safety Checker")
6604
+ },
6605
+ constraints: [
6606
+ { when: { imageUrls: { exists: false }, videoUrls: { exists: false } }, then: {
6607
+ audioUrls: { disabled: true, reason: "Audio cannot be the only reference \u2014 add an image or video." }
6608
+ } }
6609
+ ]
6495
6610
  }
6496
6611
  ]);
6497
6612
 
@@ -6523,9 +6638,23 @@ var buildMinimaxH3MaxPayload = (input) => ({
6523
6638
  ...input.seed != null && input.seed !== -1 ? { seed: input.seed } : {},
6524
6639
  enable_safety_checker: input.enableSafetyChecker ?? true
6525
6640
  });
6641
+ var buildMinimaxH3MaxR2VPayload = (input) => ({
6642
+ prompt: input.prompt,
6643
+ prompt_expansion_mode: input.promptExpansionMode ?? "balanced",
6644
+ duration: input.duration ?? 5,
6645
+ resolution: input.resolution ?? "768p",
6646
+ aspect_ratio: input.aspectRatio ?? "adaptive",
6647
+ ...input.imageUrls?.length ? { reference_image_urls: input.imageUrls } : {},
6648
+ ...input.videoUrls?.length ? { reference_video_urls: input.videoUrls } : {},
6649
+ ...input.audioUrls?.length ? { reference_audio_urls: input.audioUrls } : {},
6650
+ // -1 is the paramConfig sentinel for "random seed" — omit it on the wire.
6651
+ ...input.seed != null && input.seed !== -1 ? { seed: input.seed } : {},
6652
+ enable_safety_checker: input.enableSafetyChecker ?? true
6653
+ });
6526
6654
  registerPayloads(MODELS26, {
6527
6655
  "minimax-music-v3": buildMinimaxMusicV3Payload,
6528
- "minimax-h3-max": buildMinimaxH3MaxPayload
6656
+ "minimax-h3-max": buildMinimaxH3MaxPayload,
6657
+ "minimax-h3-max-r2v": buildMinimaxH3MaxR2VPayload
6529
6658
  });
6530
6659
  registerEditPayloads(MODELS26, {
6531
6660
  "minimax-h3-max": buildMinimaxH3MaxPayload
@@ -8676,14 +8805,22 @@ var getModelsByMode = (mode, includeDisabled = false) => ALL_MODELS.filter((m) =
8676
8805
  // src/core/contracts.ts
8677
8806
  function requireObject(value, message) {
8678
8807
  if (!value || typeof value !== "object" || Array.isArray(value)) {
8679
- throw new Error(message);
8808
+ throw new ApiError(message, { status: 400, code: "validation_error" });
8680
8809
  }
8681
8810
  }
8682
8811
  function buildInputSchema(model) {
8683
8812
  return {
8684
8813
  parse(input) {
8685
8814
  requireObject(input, `Invalid input for model "${model.id}"`);
8686
- validateAll(model.paramConfig, input);
8815
+ try {
8816
+ validateAll(model.paramConfig, input);
8817
+ } catch (err) {
8818
+ if (err instanceof ApiError) throw err;
8819
+ throw new ApiError(err instanceof Error ? err.message : String(err), {
8820
+ status: 400,
8821
+ code: "validation_error"
8822
+ });
8823
+ }
8687
8824
  return input;
8688
8825
  }
8689
8826
  };
@@ -8692,7 +8829,10 @@ function buildOutputSchema(model) {
8692
8829
  return {
8693
8830
  parse(output) {
8694
8831
  if (output == null) {
8695
- throw new Error(`Model "${model.id}" returned empty output`);
8832
+ throw new ApiError(`Model "${model.id}" returned empty output`, {
8833
+ status: 502,
8834
+ code: "invalid_response"
8835
+ });
8696
8836
  }
8697
8837
  return output;
8698
8838
  }
@@ -8735,7 +8875,12 @@ function throwIfErrorResult(result, modelName) {
8735
8875
  if (isError) {
8736
8876
  const code = typeof status === "number" ? ` (${status})` : "";
8737
8877
  const detail = message ? String(message) : "unknown error";
8738
- throw new Error(`${modelName} failed${code}: ${detail}`);
8878
+ const httpStatus = typeof status === "number" ? status : 502;
8879
+ const reason = obj.reason;
8880
+ throw new ApiError(`${modelName} failed${code}: ${detail}`, {
8881
+ status: httpStatus,
8882
+ code: typeof reason === "string" && reason.length > 0 ? reason : codeForStatus(httpStatus)
8883
+ });
8739
8884
  }
8740
8885
  }
8741
8886
  function extractSyncResult(raw) {
@@ -8937,7 +9082,9 @@ var findModel = (ref) => {
8937
9082
  // src/core/resolve.ts
8938
9083
  function resolveModel(id) {
8939
9084
  const found = findModel(id);
8940
- if (!found) throw new Error(`Unknown model: "${id}"`);
9085
+ if (!found) {
9086
+ throw new ApiError(`Unknown model: "${id}"`, { status: 400, code: "unknown_model" });
9087
+ }
8941
9088
  return found;
8942
9089
  }
8943
9090
 
@@ -8977,18 +9124,34 @@ function buildTransport(config) {
8977
9124
  { params: request.payload },
8978
9125
  request.signal
8979
9126
  );
8980
- const data = await res.json();
9127
+ const { text, json } = await readErrorBody(res);
8981
9128
  if (!res.ok) {
8982
- throw new Error(`Submit failed (${res.status}): ${data.message ?? JSON.stringify(data)}`);
9129
+ const detail = json ? json.message ?? JSON.stringify(json) : text;
9130
+ throw new ApiError(`Submit failed (${res.status}): ${detail}`, {
9131
+ status: res.status,
9132
+ code: reasonFrom(json, res.status)
9133
+ });
9134
+ }
9135
+ const response = json?.response;
9136
+ const id = response?.id ?? json?.id;
9137
+ if (!id) {
9138
+ throw new ApiError(`No task id in response: ${json ? JSON.stringify(json) : text}`, {
9139
+ status: 502,
9140
+ code: "invalid_response"
9141
+ });
8983
9142
  }
8984
- const response = data.response;
8985
- const id = response?.id ?? data.id;
8986
- if (!id) throw new Error(`No task id in response: ${JSON.stringify(data)}`);
8987
9143
  return { workflow: request.workflow, id: String(id) };
8988
9144
  },
8989
9145
  async status(handle, signal) {
8990
9146
  const res = await f(`${apiUrl}/workflows/${handle.workflow}/${handle.id}/result`, { signal });
8991
- if (!res.ok) throw new Error(`Status check failed (${res.status}): ${await res.text()}`);
9147
+ if (!res.ok) {
9148
+ const { text, json } = await readErrorBody(res);
9149
+ const detail = json ? json.message ?? text : text;
9150
+ throw new ApiError(`Status check failed (${res.status}): ${detail}`, {
9151
+ status: res.status,
9152
+ code: reasonFrom(json, res.status)
9153
+ });
9154
+ }
8992
9155
  return res.json();
8993
9156
  },
8994
9157
  async execute(request) {
@@ -8997,7 +9160,14 @@ function buildTransport(config) {
8997
9160
  { params: request.payload },
8998
9161
  request.signal
8999
9162
  );
9000
- if (!res.ok) throw new Error(`Execute failed (${res.status}): ${await res.text()}`);
9163
+ if (!res.ok) {
9164
+ const { text, json } = await readErrorBody(res);
9165
+ const detail = json ? json.message ?? text : text;
9166
+ throw new ApiError(`Execute failed (${res.status}): ${detail}`, {
9167
+ status: res.status,
9168
+ code: reasonFrom(json, res.status)
9169
+ });
9170
+ }
9001
9171
  return res.json();
9002
9172
  },
9003
9173
  async options(workflow, payload) {
@@ -9035,13 +9205,19 @@ function prepareRequest(model, params2) {
9035
9205
  const payload = resolved.buildPayload(validatedCtx);
9036
9206
  return { ctx, workflow: resolved.workflow, payload, contract };
9037
9207
  }
9038
- function parseResult(completed, model, contract) {
9208
+ function throwIfTerminalFailure(completed, model) {
9039
9209
  if (completed.status === "FAILED") {
9040
- throw new Error(`${model.name} failed: ${completed.error ?? "unknown error"}`);
9210
+ throw new ApiError(`${model.name} failed: ${completed.error ?? "unknown error"}`, {
9211
+ status: completed.statusCode ?? 502,
9212
+ code: completed.reason ?? "generation_failed"
9213
+ });
9041
9214
  }
9042
9215
  if (completed.status === "CANCELED") {
9043
- throw new Error(`${model.name} was canceled`);
9216
+ throw new ApiError(`${model.name} was canceled`, { status: 499, code: "canceled" });
9044
9217
  }
9218
+ }
9219
+ function parseResult(completed, model, contract) {
9220
+ throwIfTerminalFailure(completed, model);
9045
9221
  throwIfErrorResult(completed.result, model.name);
9046
9222
  const parsed = contract?.output ? contract.output.parse(completed.result) : completed.result;
9047
9223
  const multiItems = extractAllResults(parsed);
@@ -9054,22 +9230,23 @@ function parseResult(completed, model, contract) {
9054
9230
  }
9055
9231
  const url = extractUrl(parsed);
9056
9232
  if (!url) {
9057
- throw new Error(`${model.name}: unexpected response \u2014 no result URL`);
9233
+ throw new ApiError(`${model.name}: unexpected response \u2014 no result URL`, {
9234
+ status: 502,
9235
+ code: "invalid_response"
9236
+ });
9058
9237
  }
9059
9238
  return { url, results: [{ url }], model: model.id, handle: completed.handle, raw: parsed, usage: completed.usage };
9060
9239
  }
9061
9240
  function parseTextResult(completed, model) {
9062
- if (completed.status === "FAILED") {
9063
- throw new Error(`${model.name} failed: ${completed.error ?? "unknown error"}`);
9064
- }
9065
- if (completed.status === "CANCELED") {
9066
- throw new Error(`${model.name} was canceled`);
9067
- }
9241
+ throwIfTerminalFailure(completed, model);
9068
9242
  throwIfErrorResult(completed.result, model.name);
9069
9243
  throwIfErrorResult(completed.raw, model.name);
9070
9244
  const text = extractText(completed.result) ?? extractText(completed.raw);
9071
9245
  if (text == null) {
9072
- throw new Error(`${model.name}: unexpected response \u2014 no text`);
9246
+ throw new ApiError(`${model.name}: unexpected response \u2014 no text`, {
9247
+ status: 502,
9248
+ code: "invalid_response"
9249
+ });
9073
9250
  }
9074
9251
  return { text, model: model.id, handle: completed.handle, raw: completed.raw ?? completed.result, usage: completed.usage };
9075
9252
  }
@@ -10204,7 +10381,10 @@ function createClient(config) {
10204
10381
  async generate(model, params2, options) {
10205
10382
  const resolved = resolveModel(model);
10206
10383
  if (resolved.mode === "text") {
10207
- throw new Error(`${resolved.name} is a text model \u2014 use generateText() instead.`);
10384
+ throw new ApiError(`${resolved.name} is a text model \u2014 use generateText() instead.`, {
10385
+ status: 400,
10386
+ code: "wrong_model_mode"
10387
+ });
10208
10388
  }
10209
10389
  const { workflow, payload, contract } = prepareRequest(resolved, params2);
10210
10390
  const drive = buildDrivePayloadOptions(resolved, params2, options);
@@ -10223,7 +10403,10 @@ function createClient(config) {
10223
10403
  async generateText(model, params2, options) {
10224
10404
  const resolved = resolveModel(model);
10225
10405
  if (resolved.mode !== "text") {
10226
- throw new Error(`${resolved.name} is not a text model \u2014 use generate() instead.`);
10406
+ throw new ApiError(`${resolved.name} is not a text model \u2014 use generate() instead.`, {
10407
+ status: 400,
10408
+ code: "wrong_model_mode"
10409
+ });
10227
10410
  }
10228
10411
  const { workflow, payload } = prepareRequest(resolved, params2);
10229
10412
  const completed = await executeModel(resolved, workflow, payload, options);
@@ -10297,10 +10480,16 @@ function createClient(config) {
10297
10480
  options
10298
10481
  );
10299
10482
  if (done.status === "FAILED" || done.status === "CANCELED") {
10300
- throw new Error(done.error ?? `${workflow} failed with status ${done.status}`);
10483
+ throw new ApiError(done.error ?? `${workflow} failed with status ${done.status}`, {
10484
+ status: done.statusCode ?? (done.status === "CANCELED" ? 499 : 502),
10485
+ code: done.reason ?? (done.status === "CANCELED" ? "canceled" : "generation_failed")
10486
+ });
10301
10487
  }
10302
10488
  if (done.result === void 0) {
10303
- throw new Error(`${workflow} completed but returned no result`);
10489
+ throw new ApiError(`${workflow} completed but returned no result`, {
10490
+ status: 502,
10491
+ code: "invalid_response"
10492
+ });
10304
10493
  }
10305
10494
  return done.result;
10306
10495
  },
@@ -10863,6 +11052,7 @@ var Lyria3Pro = "lyria-3-pro";
10863
11052
  var Minimax02Hd = "minimax-02-hd";
10864
11053
  var MinimaxH3 = "minimax-h3";
10865
11054
  var MinimaxH3Max = "minimax-h3-max";
11055
+ var MinimaxH3MaxR2v = "minimax-h3-max-r2v";
10866
11056
  var MinimaxMusicV2 = "minimax-music-v2";
10867
11057
  var MinimaxMusicV3 = "minimax-music-v3";
10868
11058
  var MuseImage10 = "muse-image-1.0";
@@ -11076,6 +11266,7 @@ var Models = {
11076
11266
  Minimax02Hd,
11077
11267
  MinimaxH3,
11078
11268
  MinimaxH3Max,
11269
+ MinimaxH3MaxR2v,
11079
11270
  MinimaxMusicV2,
11080
11271
  MinimaxMusicV3,
11081
11272
  MuseImage10,
@@ -11397,4 +11588,4 @@ function decodeDeepLinkPayload(encoded) {
11397
11588
  return deserializePayload(encoded);
11398
11589
  }
11399
11590
 
11400
- export { ALL_MODELS, ExecutionMode as ApiRunMode, DEFAULT_VISIBLE_RELEASES, KLING_DUAL_IMAGE_EFFECTS, Model, Models, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, toAvatarOption, toVoiceOption };
11591
+ export { ALL_MODELS, ApiError, ExecutionMode as ApiRunMode, DEFAULT_VISIBLE_RELEASES, KLING_DUAL_IMAGE_EFFECTS, Model, Models, buildFilename, buildGenerationAttributes, catalog, createClient, decodeDeepLinkPayload, encodeDeepLinkPayload, findModel, getModel, getModelsByMode, getVoiceById, inferResourceType, isVisibleForReleases, parseGeneration, releaseOf, toAvatarOption, toVoiceOption };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@picsart/ai-sdk",
3
- "version": "5.24.1",
3
+ "version": "5.26.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",