@shop-os/modelkit 0.7.2 → 0.7.4

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/dist/index.cjs CHANGED
@@ -6687,6 +6687,161 @@ var MODEL3D_IMAGE_SLOTS = {
6687
6687
  ordered: ["image", "left", "back", "right"],
6688
6688
  unordered: ["image", "back", "left", "right", "top"]
6689
6689
  };
6690
+ var MODEL3D_FAMILY_RULES = {
6691
+ trellis: {
6692
+ textPath: false,
6693
+ views: ["none"],
6694
+ multiImage: false,
6695
+ texture: null,
6696
+ pbr: false,
6697
+ seed: true,
6698
+ textureRef: false,
6699
+ resolutions: "integer"
6700
+ },
6701
+ hunyuan: {
6702
+ textPath: true,
6703
+ views: ["none", "named"],
6704
+ multiImage: false,
6705
+ // generate_type
6706
+ texture: { none: "Geometry", standard: "Normal" },
6707
+ pbr: true,
6708
+ seed: false,
6709
+ textureRef: false,
6710
+ resolutions: null
6711
+ },
6712
+ tripo: {
6713
+ textPath: true,
6714
+ views: ["none", "ordered"],
6715
+ multiImage: true,
6716
+ // texture_quality; null sends an untextured model.
6717
+ texture: { none: null, standard: "standard", detailed: "detailed" },
6718
+ pbr: true,
6719
+ seed: true,
6720
+ textureRef: false,
6721
+ resolutions: null
6722
+ },
6723
+ rodin: {
6724
+ textPath: true,
6725
+ views: ["none", "unordered"],
6726
+ multiImage: false,
6727
+ texture: null,
6728
+ pbr: false,
6729
+ seed: true,
6730
+ textureRef: false,
6731
+ resolutions: null
6732
+ },
6733
+ meshy: {
6734
+ textPath: true,
6735
+ views: ["none", "unordered"],
6736
+ multiImage: true,
6737
+ // The text endpoint's mode; the image endpoints take should_texture.
6738
+ texture: { none: "preview", standard: "full" },
6739
+ pbr: true,
6740
+ seed: true,
6741
+ textureRef: true,
6742
+ resolutions: null
6743
+ },
6744
+ hi3d: {
6745
+ textPath: false,
6746
+ views: ["none", "named"],
6747
+ multiImage: true,
6748
+ // enable_texture
6749
+ texture: { none: false, standard: true },
6750
+ pbr: true,
6751
+ seed: false,
6752
+ textureRef: false,
6753
+ resolutions: { quality: "2048quality", master: "2048master" }
6754
+ },
6755
+ sam: {
6756
+ textPath: false,
6757
+ views: ["none"],
6758
+ multiImage: false,
6759
+ texture: null,
6760
+ pbr: false,
6761
+ seed: true,
6762
+ textureRef: false,
6763
+ resolutions: null
6764
+ }
6765
+ };
6766
+ function model3dSpecIssues(spec) {
6767
+ const issues = [];
6768
+ const add = (path, message) => issues.push({ path, message });
6769
+ const rules = MODEL3D_FAMILY_RULES[spec.family];
6770
+ const promptOnly = spec.text && !spec.imageRequired;
6771
+ if (spec.endpoints.image === void 0)
6772
+ add(["endpoints", "image"], "every row needs an image endpoint");
6773
+ if (promptOnly && !rules.textPath) {
6774
+ add(
6775
+ ["imageRequired"],
6776
+ `${spec.family} has no text-to-3D endpoint; a text row needs an image`
6777
+ );
6778
+ }
6779
+ if (promptOnly && spec.endpoints.text === void 0) {
6780
+ add(["endpoints", "text"], "a text row needs a text endpoint");
6781
+ }
6782
+ if (!rules.views.includes(spec.views)) {
6783
+ add(["views"], `${spec.family} takes views ${rules.views.join(" or ")}`);
6784
+ }
6785
+ if (spec.views === "none" && spec.maxImages !== 1) {
6786
+ add(["maxImages"], "a row without views takes 1 image");
6787
+ }
6788
+ if (spec.views === "unordered" && spec.maxImages < 2) {
6789
+ add(["maxImages"], "a row with views takes at least 2 images");
6790
+ }
6791
+ const slots = MODEL3D_IMAGE_SLOTS[spec.views].length;
6792
+ if (spec.views !== "none" && spec.maxImages > slots) {
6793
+ add(["maxImages"], `a row with ${spec.views} views sends at most ${slots} images`);
6794
+ }
6795
+ if ((spec.views === "named" || spec.views === "ordered") && spec.maxImages < slots) {
6796
+ add(
6797
+ ["maxImages"],
6798
+ `a row with ${spec.views} views takes ${slots} images (front and three sides)`
6799
+ );
6800
+ }
6801
+ if (spec.views !== "none" && rules.multiImage && spec.endpoints.multiImage === void 0) {
6802
+ add(["endpoints", "multiImage"], `${spec.family} sends views to a multi-image endpoint`);
6803
+ }
6804
+ if (rules.texture === null) {
6805
+ if (spec.texture.length > 0) add(["texture"], `${spec.family} has no texture switch`);
6806
+ } else {
6807
+ const allowed = rules.texture;
6808
+ if (spec.texture.length === 0) add(["texture"], `${spec.family} needs its texture options`);
6809
+ if (new Set(spec.texture).size !== spec.texture.length)
6810
+ add(["texture"], "texture options repeat");
6811
+ for (const t of spec.texture) {
6812
+ if (!Object.hasOwn(allowed, t))
6813
+ add(["texture"], `${spec.family} has no "${t}" texture`);
6814
+ }
6815
+ }
6816
+ if (spec.pbr && !rules.pbr) add(["pbr"], `${spec.family} has no PBR switch`);
6817
+ if (spec.pbr && !spec.texture.some((t) => t !== "none")) {
6818
+ add(["pbr"], "PBR needs a textured option");
6819
+ }
6820
+ if (spec.seed && !rules.seed) add(["seed"], `${spec.family} takes no seed`);
6821
+ if (spec.seedMax !== void 0 && !spec.seed) add(["seedMax"], "seedMax needs seed");
6822
+ if (spec.textureRef && !rules.textureRef)
6823
+ add(["textureRef"], `${spec.family} takes no texture reference`);
6824
+ const res = spec.resolutions;
6825
+ if (rules.resolutions === null) {
6826
+ if (res !== void 0) add(["resolutions"], `${spec.family} has no resolution option`);
6827
+ } else if (res === void 0) {
6828
+ add(["resolutions"], `${spec.family} needs its resolution options`);
6829
+ } else {
6830
+ if (!res.options.includes(res.default)) {
6831
+ add(["resolutions", "default"], "default must be an option");
6832
+ }
6833
+ if (new Set(res.options).size !== res.options.length) {
6834
+ add(["resolutions", "options"], "resolution options repeat");
6835
+ }
6836
+ const allowed = rules.resolutions;
6837
+ for (const option of res.options) {
6838
+ const ok = allowed === "integer" ? /^[1-9][0-9]*$/.test(option) : Object.hasOwn(allowed, option);
6839
+ if (!ok)
6840
+ add(["resolutions", "options"], `${spec.family} has no "${option}" resolution`);
6841
+ }
6842
+ }
6843
+ return issues;
6844
+ }
6690
6845
  function model3dInputRefusal(spec, wired) {
6691
6846
  const top = spec.views === "unordered" && wired.top === true;
6692
6847
  const anyView = spec.views !== "none" && (wired.back || wired.left || wired.right || top);
@@ -7274,156 +7429,6 @@ var LanguageOptionSchema = import_zod3.z.looseObject({
7274
7429
  code: import_zod3.z.string().min(1),
7275
7430
  name: import_zod3.z.string().min(1)
7276
7431
  });
7277
- var MODEL3D_FAMILY_RULES = {
7278
- trellis: {
7279
- textPath: false,
7280
- views: ["none"],
7281
- multiImage: false,
7282
- texture: null,
7283
- pbr: false,
7284
- seed: true,
7285
- textureRef: false,
7286
- resolutions: "integer"
7287
- },
7288
- hunyuan: {
7289
- textPath: true,
7290
- views: ["none", "named"],
7291
- multiImage: false,
7292
- texture: ["none", "standard"],
7293
- pbr: true,
7294
- seed: false,
7295
- textureRef: false,
7296
- resolutions: null
7297
- },
7298
- tripo: {
7299
- textPath: true,
7300
- views: ["none", "ordered"],
7301
- multiImage: true,
7302
- texture: ["none", "standard", "detailed"],
7303
- pbr: true,
7304
- seed: true,
7305
- textureRef: false,
7306
- resolutions: null
7307
- },
7308
- rodin: {
7309
- textPath: true,
7310
- views: ["none", "unordered"],
7311
- multiImage: false,
7312
- texture: null,
7313
- pbr: false,
7314
- seed: true,
7315
- textureRef: false,
7316
- resolutions: null
7317
- },
7318
- meshy: {
7319
- textPath: true,
7320
- views: ["none", "unordered"],
7321
- multiImage: true,
7322
- texture: ["none", "standard"],
7323
- pbr: true,
7324
- seed: true,
7325
- textureRef: true,
7326
- resolutions: null
7327
- },
7328
- hi3d: {
7329
- textPath: false,
7330
- views: ["none", "named"],
7331
- multiImage: true,
7332
- texture: ["none", "standard"],
7333
- pbr: true,
7334
- seed: false,
7335
- textureRef: false,
7336
- resolutions: ["quality", "master"]
7337
- },
7338
- sam: {
7339
- textPath: false,
7340
- views: ["none"],
7341
- multiImage: false,
7342
- texture: null,
7343
- pbr: false,
7344
- seed: true,
7345
- textureRef: false,
7346
- resolutions: null
7347
- }
7348
- };
7349
- function model3dSpecIssues(spec) {
7350
- const issues = [];
7351
- const add = (path, message) => issues.push({ path, message });
7352
- const rules = MODEL3D_FAMILY_RULES[spec.family];
7353
- const promptOnly = spec.text && !spec.imageRequired;
7354
- if (spec.endpoints.image === void 0)
7355
- add(["endpoints", "image"], "every row needs an image endpoint");
7356
- if (promptOnly && !rules.textPath) {
7357
- add(
7358
- ["imageRequired"],
7359
- `${spec.family} has no text-to-3D endpoint; a text row needs an image`
7360
- );
7361
- }
7362
- if (promptOnly && spec.endpoints.text === void 0) {
7363
- add(["endpoints", "text"], "a text row needs a text endpoint");
7364
- }
7365
- if (!rules.views.includes(spec.views)) {
7366
- add(["views"], `${spec.family} takes views ${rules.views.join(" or ")}`);
7367
- }
7368
- if (spec.views === "none" && spec.maxImages !== 1) {
7369
- add(["maxImages"], "a row without views takes 1 image");
7370
- }
7371
- if (spec.views === "unordered" && spec.maxImages < 2) {
7372
- add(["maxImages"], "a row with views takes at least 2 images");
7373
- }
7374
- const slots = MODEL3D_IMAGE_SLOTS[spec.views].length;
7375
- if (spec.views !== "none" && spec.maxImages > slots) {
7376
- add(["maxImages"], `a row with ${spec.views} views sends at most ${slots} images`);
7377
- }
7378
- if ((spec.views === "named" || spec.views === "ordered") && spec.maxImages < slots) {
7379
- add(
7380
- ["maxImages"],
7381
- `a row with ${spec.views} views takes ${slots} images (front and three sides)`
7382
- );
7383
- }
7384
- if (spec.views !== "none" && rules.multiImage && spec.endpoints.multiImage === void 0) {
7385
- add(["endpoints", "multiImage"], `${spec.family} sends views to a multi-image endpoint`);
7386
- }
7387
- if (rules.texture === null) {
7388
- if (spec.texture.length > 0) add(["texture"], `${spec.family} has no texture switch`);
7389
- } else {
7390
- const allowed = rules.texture;
7391
- if (spec.texture.length === 0) add(["texture"], `${spec.family} needs its texture options`);
7392
- if (new Set(spec.texture).size !== spec.texture.length)
7393
- add(["texture"], "texture options repeat");
7394
- for (const t of spec.texture) {
7395
- if (!allowed.includes(t)) add(["texture"], `${spec.family} has no "${t}" texture`);
7396
- }
7397
- }
7398
- if (spec.pbr && !rules.pbr) add(["pbr"], `${spec.family} has no PBR switch`);
7399
- if (spec.pbr && !spec.texture.some((t) => t !== "none")) {
7400
- add(["pbr"], "PBR needs a textured option");
7401
- }
7402
- if (spec.seed && !rules.seed) add(["seed"], `${spec.family} takes no seed`);
7403
- if (spec.seedMax !== void 0 && !spec.seed) add(["seedMax"], "seedMax needs seed");
7404
- if (spec.textureRef && !rules.textureRef)
7405
- add(["textureRef"], `${spec.family} takes no texture reference`);
7406
- const res = spec.resolutions;
7407
- if (rules.resolutions === null) {
7408
- if (res !== void 0) add(["resolutions"], `${spec.family} has no resolution option`);
7409
- } else if (res === void 0) {
7410
- add(["resolutions"], `${spec.family} needs its resolution options`);
7411
- } else {
7412
- if (!res.options.includes(res.default)) {
7413
- add(["resolutions", "default"], "default must be an option");
7414
- }
7415
- if (new Set(res.options).size !== res.options.length) {
7416
- add(["resolutions", "options"], "resolution options repeat");
7417
- }
7418
- const allowed = rules.resolutions;
7419
- for (const option of res.options) {
7420
- const ok = allowed === "integer" ? /^[1-9][0-9]*$/.test(option) : allowed.includes(option);
7421
- if (!ok)
7422
- add(["resolutions", "options"], `${spec.family} has no "${option}" resolution`);
7423
- }
7424
- }
7425
- return issues;
7426
- }
7427
7432
  var VideoToolConstraintsSchema = import_zod3.z.looseObject({
7428
7433
  kind: import_zod3.z.enum(["upscale", "interpolate", "hdr", "bg-remove", "subtitles"]),
7429
7434
  upscaleFactor: RangeSchema.optional(),
@@ -10825,6 +10830,19 @@ function specOf(ctx) {
10825
10830
  return spec;
10826
10831
  }
10827
10832
  var present = (v) => v !== void 0 && v.length > 0;
10833
+ function wireValue(row2, values, option, what) {
10834
+ const value = option !== void 0 && Object.hasOwn(values, option) ? values[option] : void 0;
10835
+ if (value === void 0) {
10836
+ throw new ModelKitError({
10837
+ code: "unsupported_option",
10838
+ message: `${row2.id} has no ${what} "${option ?? ""}" to send`,
10839
+ provider: "fal",
10840
+ model: row2.id,
10841
+ retryable: false
10842
+ });
10843
+ }
10844
+ return value;
10845
+ }
10828
10846
  function model3dCall(row2, spec, req) {
10829
10847
  const prompt = req.prompt?.trim() || void 0;
10830
10848
  const texture = model3dTexture(spec, req.texture);
@@ -10868,7 +10886,15 @@ function model3dCall(row2, spec, req) {
10868
10886
  }
10869
10887
  };
10870
10888
  case "hunyuan": {
10871
- const opts = { generate_type: textured ? "Normal" : "Geometry", enable_pbr: pbr };
10889
+ const opts = {
10890
+ generate_type: wireValue(
10891
+ row2,
10892
+ MODEL3D_FAMILY_RULES.hunyuan.texture,
10893
+ texture,
10894
+ "texture"
10895
+ ),
10896
+ enable_pbr: pbr
10897
+ };
10872
10898
  if (!present(req.image)) {
10873
10899
  return {
10874
10900
  endpoint: endpoint(spec.endpoints.text),
@@ -10887,12 +10913,11 @@ function model3dCall(row2, spec, req) {
10887
10913
  };
10888
10914
  }
10889
10915
  case "tripo": {
10916
+ const quality = wireValue(row2, MODEL3D_FAMILY_RULES.tripo.texture, texture, "texture");
10890
10917
  const opts = {
10891
10918
  texture: textured,
10892
10919
  pbr,
10893
- ...textured && {
10894
- texture_quality: texture === "detailed" ? "detailed" : "standard"
10895
- },
10920
+ ...quality !== null && { texture_quality: quality },
10896
10921
  ...seed !== void 0 && { model_seed: seed }
10897
10922
  };
10898
10923
  if (!present(req.image)) {
@@ -10928,6 +10953,7 @@ function model3dCall(row2, spec, req) {
10928
10953
  };
10929
10954
  }
10930
10955
  case "meshy": {
10956
+ const mode = wireValue(row2, MODEL3D_FAMILY_RULES.meshy.texture, texture, "texture");
10931
10957
  const texturePrompt = req.texturePrompt?.trim() || void 0;
10932
10958
  const opts = {
10933
10959
  enable_pbr: pbr,
@@ -10939,7 +10965,7 @@ function model3dCall(row2, spec, req) {
10939
10965
  endpoint: endpoint(spec.endpoints.text),
10940
10966
  input: {
10941
10967
  prompt: prompt ?? "",
10942
- mode: textured ? "full" : "preview",
10968
+ mode,
10943
10969
  ...opts,
10944
10970
  ...seed !== void 0 && { seed }
10945
10971
  }
@@ -10955,8 +10981,18 @@ function model3dCall(row2, spec, req) {
10955
10981
  }
10956
10982
  case "hi3d": {
10957
10983
  const opts = {
10958
- resolution: resolution === "master" ? "2048master" : "2048quality",
10959
- enable_texture: textured,
10984
+ resolution: wireValue(
10985
+ row2,
10986
+ MODEL3D_FAMILY_RULES.hi3d.resolutions,
10987
+ resolution,
10988
+ "resolution"
10989
+ ),
10990
+ enable_texture: wireValue(
10991
+ row2,
10992
+ MODEL3D_FAMILY_RULES.hi3d.texture,
10993
+ texture,
10994
+ "texture"
10995
+ ),
10960
10996
  enable_pbr: pbr,
10961
10997
  export_format: "glb"
10962
10998
  };
@@ -13808,24 +13844,24 @@ var import_zod25 = require("zod");
13808
13844
  function mp4DurationSeconds(bytes) {
13809
13845
  const moov = findBox(bytes, 0, bytes.length, "moov");
13810
13846
  if (!moov) return void 0;
13811
- const mvhd = findBox(bytes, moov.start + 8, moov.end, "mvhd");
13847
+ const mvhd = findBox(bytes, moov.body, moov.end, "mvhd");
13812
13848
  if (!mvhd) return void 0;
13813
13849
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13814
- const size = mvhd.end - mvhd.start;
13815
- if (size < 8 + 4) return void 0;
13816
- const version = view.getUint8(mvhd.start + 8);
13850
+ const size = mvhd.end - mvhd.body;
13851
+ if (size < 4) return void 0;
13852
+ const version = view.getUint8(mvhd.body);
13817
13853
  if (version === 0) {
13818
- if (size < 8 + 4 + 4 + 4 + 4 + 4) return void 0;
13819
- const timescale = view.getUint32(mvhd.start + 8 + 4 + 4 + 4);
13820
- const duration = view.getUint32(mvhd.start + 8 + 4 + 4 + 4 + 4);
13854
+ if (size < 4 + 4 + 4 + 4 + 4) return void 0;
13855
+ const timescale = view.getUint32(mvhd.body + 4 + 4 + 4);
13856
+ const duration = view.getUint32(mvhd.body + 4 + 4 + 4 + 4);
13821
13857
  if (timescale === 0) return void 0;
13822
13858
  return duration / timescale;
13823
13859
  }
13824
13860
  if (version === 1) {
13825
- if (size < 8 + 4 + 8 + 8 + 4 + 8) return void 0;
13826
- const timescale = view.getUint32(mvhd.start + 8 + 4 + 8 + 8);
13827
- const high = view.getUint32(mvhd.start + 8 + 4 + 8 + 8 + 4);
13828
- const low = view.getUint32(mvhd.start + 8 + 4 + 8 + 8 + 4 + 4);
13861
+ if (size < 4 + 8 + 8 + 4 + 8) return void 0;
13862
+ const timescale = view.getUint32(mvhd.body + 4 + 8 + 8);
13863
+ const high = view.getUint32(mvhd.body + 4 + 8 + 8 + 4);
13864
+ const low = view.getUint32(mvhd.body + 4 + 8 + 8 + 4 + 4);
13829
13865
  if (timescale === 0) return void 0;
13830
13866
  return (high * 2 ** 32 + low) / timescale;
13831
13867
  }
@@ -13835,15 +13871,23 @@ function findBox(bytes, from, to, type) {
13835
13871
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
13836
13872
  let offset = from;
13837
13873
  while (offset + 8 <= to) {
13838
- const size = view.getUint32(offset);
13874
+ let size = view.getUint32(offset);
13875
+ let header = 8;
13839
13876
  const name = String.fromCharCode(
13840
13877
  view.getUint8(offset + 4),
13841
13878
  view.getUint8(offset + 5),
13842
13879
  view.getUint8(offset + 6),
13843
13880
  view.getUint8(offset + 7)
13844
13881
  );
13845
- if (size < 8 || offset + size > to) return void 0;
13846
- if (name === type) return { start: offset, end: offset + size };
13882
+ if (size === 1) {
13883
+ if (offset + 16 > to) return void 0;
13884
+ size = view.getUint32(offset + 8) * 2 ** 32 + view.getUint32(offset + 12);
13885
+ header = 16;
13886
+ } else if (size === 0) {
13887
+ size = to - offset;
13888
+ }
13889
+ if (size < header || offset + size > to) return void 0;
13890
+ if (name === type) return { body: offset + header, end: offset + size };
13847
13891
  offset += size;
13848
13892
  }
13849
13893
  return void 0;
@@ -13856,6 +13900,7 @@ var IMAGE_TIMEOUT_MS = 5 * 6e4;
13856
13900
  var VIDEO_POLL_INTERVAL_MS = 5e3;
13857
13901
  var VIDEO_TIMEOUT_MS = 20 * 6e4;
13858
13902
  var PROMPT_MAX_CHARS = 6e3;
13903
+ var OUTPUT_URL_TTL_MS = 55 * 6e4;
13859
13904
  var LUMA_IMAGE_MODEL_IDS = /* @__PURE__ */ new Set(["uni-1", "uni-1-max"]);
13860
13905
  var LUMA_VIDEO_MODEL_ID = "ray-3.2";
13861
13906
  var LUMA_VIDEO_MODES = /* @__PURE__ */ new Map([
@@ -13874,6 +13919,9 @@ function lumaRowRefusal(row2) {
13874
13919
  }
13875
13920
  return `luma has no ${row2.type} rows`;
13876
13921
  }
13922
+ function lumaModel(ctx) {
13923
+ return ctx.model.providerModelId.toLowerCase();
13924
+ }
13877
13925
  function lumaBase(ctx) {
13878
13926
  return ctx.endpoints?.luma ?? DEFAULT_BASE_URL;
13879
13927
  }
@@ -13916,8 +13964,23 @@ function imageRef(ref) {
13916
13964
  if ("url" in ref) return { url: ref.url };
13917
13965
  return { data: mediaRefToBase64(ref.bytes), media_type: ref.mime };
13918
13966
  }
13967
+ var VIDEO_TYPES_BY_EXTENSION = /* @__PURE__ */ new Map([
13968
+ ["mp4", "video/mp4"],
13969
+ ["m4v", "video/mp4"],
13970
+ ["mov", "video/quicktime"],
13971
+ ["webm", "video/webm"]
13972
+ ]);
13973
+ function videoUrlType(url) {
13974
+ let path = url;
13975
+ try {
13976
+ path = new URL(url).pathname;
13977
+ } catch {
13978
+ }
13979
+ const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase();
13980
+ return VIDEO_TYPES_BY_EXTENSION.get(extension) ?? "video/mp4";
13981
+ }
13919
13982
  function videoRef(ref) {
13920
- if ("url" in ref) return { url: ref.url, media_type: "video/mp4" };
13983
+ if ("url" in ref) return { url: ref.url, media_type: videoUrlType(ref.url) };
13921
13984
  const mediaType = ref.mime.startsWith("video/") ? ref.mime : "video/mp4";
13922
13985
  return { data: mediaRefToBase64(ref.bytes), media_type: mediaType };
13923
13986
  }
@@ -14081,14 +14144,22 @@ async function lumaRequest(ctx, url, body) {
14081
14144
  }
14082
14145
  return res;
14083
14146
  }
14147
+ async function parseGeneration(res) {
14148
+ try {
14149
+ const parsed = lumaGenerationSchema.safeParse(await res.json());
14150
+ return parsed.success ? parsed.data : void 0;
14151
+ } catch {
14152
+ return "not_json";
14153
+ }
14154
+ }
14084
14155
  async function submitGeneration(ctx, body) {
14085
14156
  const retryOptions = mediaRetryOptions(ctx.call, ctx.signal);
14086
14157
  const res = await withRetry(
14087
14158
  () => lumaRequest(ctx, `${lumaBase(ctx)}/v1/generations`, JSON.stringify(body)),
14088
14159
  retryOptions
14089
14160
  );
14090
- const parsed = lumaGenerationSchema.safeParse(await res.json());
14091
- if (!parsed.success) {
14161
+ const generation = await parseGeneration(res);
14162
+ if (generation === void 0 || generation === "not_json") {
14092
14163
  throw new ModelKitError({
14093
14164
  code: "provider_rejected",
14094
14165
  message: "Luma submit returned no generation id",
@@ -14097,13 +14168,20 @@ async function submitGeneration(ctx, body) {
14097
14168
  retryable: false
14098
14169
  });
14099
14170
  }
14100
- return parsed.data;
14171
+ return generation;
14101
14172
  }
14102
14173
  async function readGeneration(ctx, id) {
14103
14174
  const res = await lumaRequest(ctx, `${lumaBase(ctx)}/v1/generations/${id}`);
14104
- const payload = JsonValueSchema.parse(await res.json());
14105
- const parsed = lumaGenerationSchema.safeParse(payload);
14106
- if (!parsed.success) {
14175
+ const generation = await parseGeneration(res);
14176
+ if (generation === "not_json") {
14177
+ throw new ModelKitError({
14178
+ code: "network",
14179
+ message: "Luma returned a poll response that is not JSON",
14180
+ provider: "luma",
14181
+ model: ctx.model.id
14182
+ });
14183
+ }
14184
+ if (generation === void 0) {
14107
14185
  throw new ModelKitError({
14108
14186
  code: "provider_rejected",
14109
14187
  message: "Luma returned an unreadable generation",
@@ -14112,14 +14190,12 @@ async function readGeneration(ctx, id) {
14112
14190
  retryable: false
14113
14191
  });
14114
14192
  }
14115
- return parsed.data;
14193
+ return generation;
14116
14194
  }
14117
14195
  function outputUrl(generation, type) {
14118
14196
  const outputs = generation.output ?? [];
14119
- const match = outputs.find((item) => item.type === type && item.url);
14120
- if (match?.url) return match.url;
14121
- const fallback = outputs.find((item) => item.url);
14122
- return fallback?.url;
14197
+ const match = outputs.find((item) => item.type === type && item.url) ?? outputs.find((item) => item.type === void 0 && item.url);
14198
+ return match?.url;
14123
14199
  }
14124
14200
  async function readGenerationOnce(ctx, id) {
14125
14201
  try {
@@ -14158,21 +14234,22 @@ async function checkGeneration(ctx, id, type) {
14158
14234
  return void 0;
14159
14235
  }
14160
14236
  async function downloadBytes(ctx, url, fallback) {
14161
- let bytes;
14162
- let header;
14163
- try {
14164
- const out = await mediaRefToBytes({ url }, ctx.fetch, {
14165
- ...ctx.signal !== void 0 && { signal: ctx.signal },
14166
- provider: "luma",
14167
- model: ctx.model.id
14168
- });
14169
- bytes = out.bytes;
14170
- header = out.mime;
14171
- } catch (cause) {
14172
- throw classifyThrown("luma", cause, ctx.model.id);
14173
- }
14237
+ const { bytes, mime: header } = await withRetry(
14238
+ async () => {
14239
+ try {
14240
+ return await mediaRefToBytes({ url }, ctx.fetch, {
14241
+ ...ctx.signal !== void 0 && { signal: ctx.signal },
14242
+ provider: "luma",
14243
+ model: ctx.model.id
14244
+ });
14245
+ } catch (cause) {
14246
+ throw classifyThrown("luma", cause, ctx.model.id);
14247
+ }
14248
+ },
14249
+ mediaRetryOptions(ctx.call, ctx.signal)
14250
+ );
14174
14251
  const kind = fallback.split("/")[0];
14175
- const usable = header !== void 0 && header.startsWith(`${kind}/`);
14252
+ const usable = header.startsWith(`${kind}/`);
14176
14253
  return { bytes, mime: usable ? header : fallback };
14177
14254
  }
14178
14255
  function imageUsage2(ctx) {
@@ -14187,15 +14264,12 @@ function imageUsage2(ctx) {
14187
14264
  }
14188
14265
  var GENERATE_SECONDS = 5;
14189
14266
  var MAX_PLAUSIBLE_SECONDS = 60;
14190
- function billedSeconds(req, mode, bytes) {
14191
- if (mode === "generate") return GENERATE_SECONDS;
14267
+ function editedSeconds(req, bytes) {
14192
14268
  const measured = mp4DurationSeconds(bytes);
14193
- const source2 = req.sourceVideo;
14194
- const length = measured !== void 0 && measured > 0 && measured <= MAX_PLAUSIBLE_SECONDS ? measured : source2 !== void 0 && "url" in source2 ? source2.durationSeconds : void 0;
14269
+ const length = measured !== void 0 && measured > 0 && measured <= MAX_PLAUSIBLE_SECONDS ? measured : req.sourceVideo?.durationSeconds;
14195
14270
  return Math.max(length ?? 0, GENERATE_SECONDS);
14196
14271
  }
14197
- function videoUsage4(ctx, req, mode, bytes) {
14198
- const seconds3 = billedSeconds(req, mode, bytes);
14272
+ function videoUsage4(ctx, req, mode, seconds3) {
14199
14273
  const event = {
14200
14274
  model: ctx.model.id,
14201
14275
  provider: "luma",
@@ -14210,7 +14284,7 @@ function videoUsage4(ctx, req, mode, bytes) {
14210
14284
  }
14211
14285
  function imageBody(req, ctx, outputFormat) {
14212
14286
  const prompt = checkPrompt(req, ctx);
14213
- const model = ctx.model.providerModelId;
14287
+ const model = lumaModel(ctx);
14214
14288
  const ratio = req.aspectRatio !== "adaptive" ? req.aspectRatio : void 0;
14215
14289
  const [source2, ...rest] = req.references ?? [];
14216
14290
  if (source2 === void 0) {
@@ -14238,7 +14312,7 @@ function imageBody(req, ctx, outputFormat) {
14238
14312
  }
14239
14313
  function videoBody(req, ctx, mode) {
14240
14314
  const prompt = checkPrompt(req, ctx);
14241
- const model = ctx.model.providerModelId;
14315
+ const model = lumaModel(ctx);
14242
14316
  const hasReferences = (req.referenceImages ?? []).length + (req.referenceVideos ?? []).length + (req.referenceAudios ?? []).length > 0;
14243
14317
  if (hasReferences) {
14244
14318
  refuseOrDrop(
@@ -14320,7 +14394,7 @@ function videoBody(req, ctx, mode) {
14320
14394
  async function generateLumaImage(req, ctx) {
14321
14395
  throwIfAborted(ctx.signal);
14322
14396
  lumaKey(ctx);
14323
- if (!LUMA_IMAGE_MODEL_IDS.has(ctx.model.providerModelId.toLowerCase())) {
14397
+ if (!LUMA_IMAGE_MODEL_IDS.has(lumaModel(ctx))) {
14324
14398
  throw new ModelKitError({
14325
14399
  code: "unsupported_option",
14326
14400
  message: `luma has no image mapping for "${ctx.model.providerModelId}"`,
@@ -14353,16 +14427,25 @@ function lumaVideoJob(ctx, req, mode, id) {
14353
14427
  return { status: "pending" };
14354
14428
  },
14355
14429
  async result(opts) {
14356
- const signal = opts?.signal ?? ctx.signal;
14357
- const url = await pollUntil(checkGeneration.bind(null, ctx, id, "video"), {
14430
+ const signal = combineSignals(ctx.signal, opts?.signal);
14431
+ const jobCtx = { ...ctx, ...signal !== void 0 && { signal } };
14432
+ const url = await pollUntil(checkGeneration.bind(null, jobCtx, id, "video"), {
14358
14433
  intervalMs: opts?.pollIntervalMs ?? VIDEO_POLL_INTERVAL_MS,
14359
14434
  timeoutMs: opts?.timeoutMs ?? VIDEO_TIMEOUT_MS,
14360
14435
  ...signal !== void 0 && { signal }
14361
14436
  });
14362
- const video = await downloadBytes(ctx, url, "video/mp4");
14437
+ if (mode === "generate") {
14438
+ const result2 = {
14439
+ video: { url, expiresAt: new Date(Date.now() + OUTPUT_URL_TTL_MS) },
14440
+ usage: videoUsage4(jobCtx, req, mode, GENERATE_SECONDS),
14441
+ raw: { id }
14442
+ };
14443
+ return result2;
14444
+ }
14445
+ const video = await downloadBytes(jobCtx, url, "video/mp4");
14363
14446
  const result = {
14364
14447
  video,
14365
- usage: videoUsage4(ctx, req, mode, video.bytes),
14448
+ usage: videoUsage4(jobCtx, req, mode, editedSeconds(req, video.bytes)),
14366
14449
  raw: { id }
14367
14450
  };
14368
14451
  return result;
@@ -14372,7 +14455,7 @@ function lumaVideoJob(ctx, req, mode, id) {
14372
14455
  async function generateLumaVideo(req, ctx) {
14373
14456
  throwIfAborted(ctx.signal);
14374
14457
  lumaKey(ctx);
14375
- if (ctx.model.providerModelId.toLowerCase() !== LUMA_VIDEO_MODEL_ID) {
14458
+ if (lumaModel(ctx) !== LUMA_VIDEO_MODEL_ID) {
14376
14459
  throw new ModelKitError({
14377
14460
  code: "unsupported_option",
14378
14461
  message: `luma has no video mapping for "${ctx.model.providerModelId}"`,