@shop-os/modelkit 0.5.0 → 0.6.1

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/catalog.cjs CHANGED
@@ -30,6 +30,8 @@ __export(catalog_entry_exports, {
30
30
  IMAGE_MODELS: () => IMAGE_MODELS,
31
31
  MEDIA_METHODS: () => MEDIA_METHODS,
32
32
  MEDIA_NODE_MODELS: () => MEDIA_NODE_MODELS,
33
+ MODEL3D_IMAGE_SLOTS: () => MODEL3D_IMAGE_SLOTS,
34
+ MODEL3D_MODELS: () => MODEL3D_MODELS,
33
35
  MODELS: () => MODELS,
34
36
  ModelKitError: () => ModelKitError,
35
37
  OTHER_MODELS: () => OTHER_MODELS,
@@ -47,6 +49,13 @@ __export(catalog_entry_exports, {
47
49
  isModelKitError: () => isModelKitError,
48
50
  liveCatalog: () => liveCatalog,
49
51
  liveState: () => liveState,
52
+ model3dConstraints: () => model3dConstraints,
53
+ model3dInputRefusal: () => model3dInputRefusal,
54
+ model3dPricingSku: () => model3dPricingSku,
55
+ model3dResolution: () => model3dResolution,
56
+ model3dSeedFits: () => model3dSeedFits,
57
+ model3dSkus: () => model3dSkus,
58
+ model3dTexture: () => model3dTexture,
50
59
  nearestAspectRatio: () => nearestAspectRatio,
51
60
  normalizeExposure: () => normalizeExposure,
52
61
  outputAudio: () => outputAudio,
@@ -208,11 +217,11 @@ function normalizeExposure(value) {
208
217
  }
209
218
  return value;
210
219
  }
211
- function isListedOn(row, surface) {
212
- return row.lifecycle === "active" && row.exposure.includes(surface);
220
+ function isListedOn(row2, surface) {
221
+ return row2.lifecycle === "active" && row2.exposure.includes(surface);
213
222
  }
214
- function isListed(row) {
215
- return row.lifecycle === "active" && row.exposure.length > 0;
223
+ function isListed(row2) {
224
+ return row2.lifecycle === "active" && row2.exposure.length > 0;
216
225
  }
217
226
 
218
227
  // src/catalog/image-fal.ts
@@ -3614,6 +3623,197 @@ var MEDIA_NODE_MODELS = [
3614
3623
  SAM_3_IMAGE
3615
3624
  ];
3616
3625
 
3626
+ // src/catalog/model3d.ts
3627
+ var REQUEST_USAGE = { unit: "requests" };
3628
+ function row(id, label, spec, display) {
3629
+ return {
3630
+ id,
3631
+ aliases: [],
3632
+ label,
3633
+ type: "model3d",
3634
+ adapter: "fal",
3635
+ // The single-image endpoint; the adapter picks the endpoint a request needs.
3636
+ providerModelId: spec.endpoints.image ?? id,
3637
+ lifecycle: "active",
3638
+ exposure: ["space_builder"],
3639
+ constraints: { supports: [], model3d: spec },
3640
+ defaults: {},
3641
+ usage: REQUEST_USAGE,
3642
+ pricingKind: "flat",
3643
+ display
3644
+ };
3645
+ }
3646
+ var MODEL3D_MODELS = [
3647
+ row(
3648
+ "trellis-2",
3649
+ "Trellis 2",
3650
+ {
3651
+ family: "trellis",
3652
+ endpoints: { image: "fal-ai/trellis-2" },
3653
+ text: false,
3654
+ imageRequired: true,
3655
+ views: "none",
3656
+ maxImages: 1,
3657
+ texture: [],
3658
+ pbr: false,
3659
+ textureRef: false,
3660
+ seed: true,
3661
+ resolutions: { label: "Resolution", options: ["512", "1024", "1536"], default: "1024" }
3662
+ },
3663
+ {
3664
+ provider: "microsoft",
3665
+ description: "One photo to a detailed textured model. Fast and the best value.",
3666
+ sortOrder: 70
3667
+ }
3668
+ ),
3669
+ row(
3670
+ "hunyuan3d-3.1-pro",
3671
+ "Hunyuan3D 3.1 Pro",
3672
+ {
3673
+ family: "hunyuan",
3674
+ endpoints: {
3675
+ text: "fal-ai/hunyuan-3d/v3.1/pro/text-to-3d",
3676
+ image: "fal-ai/hunyuan-3d/v3.1/pro/image-to-3d"
3677
+ },
3678
+ text: true,
3679
+ imageRequired: false,
3680
+ views: "named",
3681
+ maxImages: 4,
3682
+ texture: ["none", "standard"],
3683
+ pbr: true,
3684
+ textureRef: false,
3685
+ seed: false
3686
+ },
3687
+ {
3688
+ provider: "tencent",
3689
+ description: "Photo-faithful models from a prompt, a photo, or a photo with back and side views.",
3690
+ sortOrder: 60
3691
+ }
3692
+ ),
3693
+ row(
3694
+ "tripo-h3.1",
3695
+ "Tripo H3.1",
3696
+ {
3697
+ family: "tripo",
3698
+ endpoints: {
3699
+ text: "tripo3d/h3.1/text-to-3d",
3700
+ image: "tripo3d/h3.1/image-to-3d",
3701
+ multiImage: "tripo3d/h3.1/multiview-to-3d"
3702
+ },
3703
+ text: true,
3704
+ imageRequired: false,
3705
+ views: "ordered",
3706
+ maxImages: 4,
3707
+ texture: ["none", "standard", "detailed"],
3708
+ pbr: true,
3709
+ textureRef: false,
3710
+ seed: true
3711
+ },
3712
+ {
3713
+ provider: "tripo",
3714
+ description: "Clean, game-ready models from a prompt, a photo or four views.",
3715
+ sortOrder: 50
3716
+ }
3717
+ ),
3718
+ row(
3719
+ "rodin-gen-2.5",
3720
+ "Rodin Gen-2.5",
3721
+ {
3722
+ family: "rodin",
3723
+ endpoints: {
3724
+ text: "fal-ai/hyper3d/rodin/v2.5/text-to-3d",
3725
+ image: "fal-ai/hyper3d/rodin/v2.5"
3726
+ },
3727
+ text: true,
3728
+ imageRequired: false,
3729
+ views: "unordered",
3730
+ maxImages: 5,
3731
+ texture: [],
3732
+ pbr: false,
3733
+ textureRef: false,
3734
+ seed: true,
3735
+ seedMax: 65535
3736
+ },
3737
+ {
3738
+ provider: "hyper3d",
3739
+ description: "Detailed PBR models from a prompt or up to five photos.",
3740
+ sortOrder: 40
3741
+ }
3742
+ ),
3743
+ row(
3744
+ "meshy-7.1",
3745
+ "Meshy 7.1",
3746
+ {
3747
+ family: "meshy",
3748
+ endpoints: {
3749
+ text: "meshy/v7.1/text-to-3d",
3750
+ image: "meshy/v7.1/image-to-3d",
3751
+ multiImage: "meshy/v7.1/multi-image-to-3d"
3752
+ },
3753
+ text: true,
3754
+ imageRequired: false,
3755
+ views: "unordered",
3756
+ maxImages: 4,
3757
+ texture: ["none", "standard"],
3758
+ pbr: true,
3759
+ textureRef: true,
3760
+ seed: true
3761
+ },
3762
+ {
3763
+ provider: "meshy",
3764
+ description: "Models from a prompt or photos, with a texture prompt or texture image to restyle.",
3765
+ sortOrder: 30
3766
+ }
3767
+ ),
3768
+ row(
3769
+ "hi3d-3.0",
3770
+ "Hi3D 3.0",
3771
+ {
3772
+ family: "hi3d",
3773
+ endpoints: {
3774
+ image: "hitem3d/hi3d/v3.0/image-to-3d",
3775
+ multiImage: "hitem3d/hi3d/v3.0/multi-view-to-3d"
3776
+ },
3777
+ text: false,
3778
+ imageRequired: true,
3779
+ views: "named",
3780
+ maxImages: 4,
3781
+ texture: ["none", "standard"],
3782
+ pbr: true,
3783
+ textureRef: false,
3784
+ seed: false,
3785
+ resolutions: { label: "Quality", options: ["quality", "master"], default: "quality" }
3786
+ },
3787
+ {
3788
+ provider: "hitem3d",
3789
+ description: "Highest-fidelity product models from a photo or four views. Slowest.",
3790
+ sortOrder: 20
3791
+ }
3792
+ ),
3793
+ row(
3794
+ "sam-3d-objects",
3795
+ "SAM 3D Objects",
3796
+ {
3797
+ family: "sam",
3798
+ endpoints: { image: "fal-ai/sam-3/3d-objects" },
3799
+ text: true,
3800
+ imageRequired: true,
3801
+ promptRequired: true,
3802
+ views: "none",
3803
+ maxImages: 1,
3804
+ texture: [],
3805
+ pbr: false,
3806
+ textureRef: false,
3807
+ seed: true
3808
+ },
3809
+ {
3810
+ provider: "meta",
3811
+ description: "Lifts one named object out of a real photo into a 3D model.",
3812
+ sortOrder: 10
3813
+ }
3814
+ )
3815
+ ];
3816
+
3617
3817
  // src/catalog/other.ts
3618
3818
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
3619
3819
  var ELEVENLABS_LOGO2 = "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/elevenlabs.png";
@@ -5932,6 +6132,7 @@ var MODELS = [
5932
6132
  ...IMAGE_MODELS,
5933
6133
  ...VIDEO_MODELS,
5934
6134
  ...VIDEO_TOOL_MODELS,
6135
+ ...MODEL3D_MODELS,
5935
6136
  ...OTHER_MODELS,
5936
6137
  ...MEDIA_NODE_MODELS
5937
6138
  ];
@@ -5940,11 +6141,11 @@ var MODELS = [
5940
6141
  var import_zod3 = require("zod");
5941
6142
 
5942
6143
  // src/resolve-provider-rules.ts
5943
- function ruleRefusal(row, message, code) {
6144
+ function ruleRefusal(row2, message, code) {
5944
6145
  return new ModelKitError({
5945
6146
  code,
5946
- message: `${row.label}: ${message}`,
5947
- model: row.id,
6147
+ message: `${row2.label}: ${message}`,
6148
+ model: row2.id,
5948
6149
  retryable: false
5949
6150
  });
5950
6151
  }
@@ -6103,27 +6304,27 @@ function collisionIssues(rows) {
6103
6304
  const issues = [];
6104
6305
  const seenIds = /* @__PURE__ */ new Map();
6105
6306
  const seenAliases = /* @__PURE__ */ new Map();
6106
- rows.forEach((row, index) => {
6107
- if (seenIds.has(row.id)) {
6307
+ rows.forEach((row2, index) => {
6308
+ if (seenIds.has(row2.id)) {
6108
6309
  issues.push({
6109
6310
  index,
6110
6311
  path: ["id"],
6111
- message: `Duplicate model id "${row.id}"`
6312
+ message: `Duplicate model id "${row2.id}"`
6112
6313
  });
6113
6314
  } else {
6114
- const aliasOwner = seenAliases.get(row.id);
6315
+ const aliasOwner = seenAliases.get(row2.id);
6115
6316
  if (aliasOwner !== void 0) {
6116
6317
  issues.push({
6117
6318
  index: aliasOwner.index,
6118
6319
  path: ["aliases", aliasOwner.aliasIndex],
6119
- message: `Alias "${row.id}" collides with a model id`
6320
+ message: `Alias "${row2.id}" collides with a model id`
6120
6321
  });
6121
6322
  }
6122
- seenIds.set(row.id, index);
6323
+ seenIds.set(row2.id, index);
6123
6324
  }
6124
6325
  const local = /* @__PURE__ */ new Set();
6125
- for (const [aliasIndex, alias] of row.aliases.entries()) {
6126
- if (alias === row.id || seenIds.has(alias)) {
6326
+ for (const [aliasIndex, alias] of row2.aliases.entries()) {
6327
+ if (alias === row2.id || seenIds.has(alias)) {
6127
6328
  issues.push({
6128
6329
  index,
6129
6330
  path: ["aliases", aliasIndex],
@@ -6162,23 +6363,23 @@ var ConstraintsReadView = import_zod.z.looseObject({
6162
6363
  }).optional().catch(void 0)
6163
6364
  });
6164
6365
  var DEFAULT_CONVENTIONS = { referenceSyntax: "numbered" };
6165
- function outputAudio(row) {
6166
- const view = ConstraintsReadView.safeParse(row.constraints);
6366
+ function outputAudio(row2) {
6367
+ const view = ConstraintsReadView.safeParse(row2.constraints);
6167
6368
  return view.success ? view.data.audio : void 0;
6168
6369
  }
6169
6370
  var DefaultsReadView = import_zod.z.looseObject({
6170
6371
  generateAudio: import_zod.z.boolean().optional().catch(void 0)
6171
6372
  });
6172
- function billedAudio(row, requested) {
6173
- const audio = outputAudio(row);
6373
+ function billedAudio(row2, requested) {
6374
+ const audio = outputAudio(row2);
6174
6375
  if (audio === "never") return false;
6175
6376
  if (audio !== "optional") return true;
6176
6377
  if (requested !== void 0) return requested;
6177
- const defaults = DefaultsReadView.safeParse(row.defaults);
6378
+ const defaults = DefaultsReadView.safeParse(row2.defaults);
6178
6379
  return (defaults.success ? defaults.data.generateAudio : void 0) ?? true;
6179
6380
  }
6180
- function promptConventions(row) {
6181
- const view = ConstraintsReadView.safeParse(row.constraints);
6381
+ function promptConventions(row2) {
6382
+ const view = ConstraintsReadView.safeParse(row2.constraints);
6182
6383
  const prompt = view.success ? view.data.prompt : void 0;
6183
6384
  if (prompt === void 0) return { ...DEFAULT_CONVENTIONS };
6184
6385
  const result = { referenceSyntax: prompt.referenceSyntax };
@@ -6189,22 +6390,111 @@ var RawConventionsView = import_zod.z.looseObject({
6189
6390
  audio: import_zod.z.unknown().optional(),
6190
6391
  prompt: import_zod.z.unknown().optional()
6191
6392
  });
6192
- function normaliseConventions(row) {
6193
- const view = RawConventionsView.safeParse(row.constraints);
6194
- if (!view.success) return { row, invalidKeys: [] };
6393
+ function normaliseConventions(row2) {
6394
+ const view = RawConventionsView.safeParse(row2.constraints);
6395
+ if (!view.success) return { row: row2, invalidKeys: [] };
6195
6396
  const { audio, prompt } = view.data;
6196
6397
  const badAudio = audio !== void 0 && !AudioOutputSchema.safeParse(audio).success;
6197
6398
  const badPrompt = prompt !== void 0 && !PromptConventionsSchema.safeParse(prompt).success;
6198
- if (!badAudio && !badPrompt) return { row, invalidKeys: [] };
6399
+ if (!badAudio && !badPrompt) return { row: row2, invalidKeys: [] };
6199
6400
  const invalidKeys = [];
6200
6401
  if (badAudio) invalidKeys.push("constraints.audio");
6201
6402
  if (badPrompt) invalidKeys.push("constraints.prompt");
6202
- const entries = Object.entries(row.constraints).flatMap(([key, value]) => {
6403
+ const entries = Object.entries(row2.constraints).flatMap(([key, value]) => {
6203
6404
  if (key === "audio" && badAudio) return [];
6204
- if (key === "prompt" && badPrompt) return [[key, promptConventions(row)]];
6405
+ if (key === "prompt" && badPrompt) return [[key, promptConventions(row2)]];
6205
6406
  return [[key, value]];
6206
6407
  });
6207
- return { row: { ...row, constraints: Object.fromEntries(entries) }, invalidKeys };
6408
+ return { row: { ...row2, constraints: Object.fromEntries(entries) }, invalidKeys };
6409
+ }
6410
+
6411
+ // src/catalog/model3d-spec.ts
6412
+ function model3dConstraints(row2) {
6413
+ if (row2.type !== "model3d") return void 0;
6414
+ return row2.constraints.model3d;
6415
+ }
6416
+ var MODEL3D_IMAGE_SLOTS = {
6417
+ none: ["image"],
6418
+ named: ["image", "back", "left", "right"],
6419
+ ordered: ["image", "left", "back", "right"],
6420
+ unordered: ["image", "back", "left", "right", "top"]
6421
+ };
6422
+ function model3dInputRefusal(spec, wired) {
6423
+ const top = spec.views === "unordered" && wired.top === true;
6424
+ const anyView = spec.views !== "none" && (wired.back || wired.left || wired.right || top);
6425
+ if (anyView && !wired.image) return "model3d_views_need_image";
6426
+ if (spec.imageRequired && !wired.image) return "model3d_needs_image";
6427
+ if (spec.promptRequired && !wired.prompt) return "model3d_needs_prompt";
6428
+ if (!wired.image && !(spec.text && wired.prompt)) return "model3d_needs_input";
6429
+ if (spec.views === "ordered") {
6430
+ if ((wired.back || wired.right) && !wired.left) return "model3d_views_out_of_order";
6431
+ if (wired.right && !wired.back) return "model3d_views_out_of_order";
6432
+ }
6433
+ return null;
6434
+ }
6435
+ function model3dSeedFits(spec, seed) {
6436
+ return Number.isSafeInteger(seed) && seed >= 0 && seed <= (spec.seedMax ?? Number.MAX_SAFE_INTEGER);
6437
+ }
6438
+ function model3dTexture(spec, requested) {
6439
+ if (requested !== void 0 && spec.texture.includes(requested)) return requested;
6440
+ return spec.texture.includes("standard") ? "standard" : spec.texture[0] ?? "standard";
6441
+ }
6442
+ function model3dResolution(spec, requested) {
6443
+ const res = spec.resolutions;
6444
+ if (res === void 0) return void 0;
6445
+ return requested !== void 0 && res.options.includes(requested) ? requested : res.default;
6446
+ }
6447
+ function model3dPricingSku(row2, options, inputs) {
6448
+ const spec = model3dConstraints(row2);
6449
+ if (spec === void 0) throw new Error(`${row2.id} is not a 3D model`);
6450
+ const texture = model3dTexture(spec, options.texture);
6451
+ const textured = spec.texture.length > 0 && texture !== "none";
6452
+ const pbr = spec.pbr && textured && options.pbr === true;
6453
+ const resolution = model3dResolution(spec, options.resolution);
6454
+ switch (spec.family) {
6455
+ case "meshy":
6456
+ return `${row2.id}__${textured ? "textured" : "untextured"}`;
6457
+ case "tripo":
6458
+ return `${row2.id}__${inputs.image ? "image" : "text"}__${texture}`;
6459
+ case "hunyuan":
6460
+ return `${row2.id}${pbr ? "__pbr" : ""}${inputs.views ? "__views" : ""}`;
6461
+ case "trellis":
6462
+ return `${row2.id}__${resolution}`;
6463
+ case "hi3d":
6464
+ return `${row2.id}__${resolution}__${textured ? pbr ? "texture-pbr" : "texture" : "geometry"}`;
6465
+ case "rodin":
6466
+ case "sam":
6467
+ return row2.id;
6468
+ }
6469
+ }
6470
+ function model3dSkus(row2) {
6471
+ const spec = model3dConstraints(row2);
6472
+ if (spec === void 0) return [];
6473
+ const textures = spec.texture.length > 0 ? spec.texture : [void 0];
6474
+ const pbrs = spec.pbr ? [false, true] : [false];
6475
+ const resolutions = spec.resolutions?.options ?? [void 0];
6476
+ const images = spec.text && !spec.imageRequired ? [true, false] : [true];
6477
+ const views = spec.views === "none" ? [false] : [false, true];
6478
+ const skus = /* @__PURE__ */ new Set();
6479
+ for (const texture of textures)
6480
+ for (const pbr of pbrs)
6481
+ for (const resolution of resolutions)
6482
+ for (const image of images)
6483
+ for (const view of views) {
6484
+ if (view && !image) continue;
6485
+ skus.add(
6486
+ model3dPricingSku(
6487
+ row2,
6488
+ {
6489
+ ...texture !== void 0 && { texture },
6490
+ pbr,
6491
+ ...resolution !== void 0 && { resolution }
6492
+ },
6493
+ { image, views: view }
6494
+ )
6495
+ );
6496
+ }
6497
+ return [...skus].sort();
6208
6498
  }
6209
6499
 
6210
6500
  // src/catalog/input-spec.ts
@@ -6349,7 +6639,7 @@ var InputSpecSchema = import_zod2.z.strictObject({
6349
6639
  }
6350
6640
  }
6351
6641
  });
6352
- function validateInputSpec(raw, row) {
6642
+ function validateInputSpec(raw, row2) {
6353
6643
  const parsed = InputSpecSchema.safeParse(raw);
6354
6644
  if (!parsed.success) {
6355
6645
  return {
@@ -6361,7 +6651,7 @@ function validateInputSpec(raw, row) {
6361
6651
  };
6362
6652
  }
6363
6653
  const spec = parsed.data;
6364
- const supportedTypes = TYPES_BY_ADAPTER.get(row.adapter);
6654
+ const supportedTypes = TYPES_BY_ADAPTER.get(row2.adapter);
6365
6655
  if (supportedTypes === void 0) {
6366
6656
  return {
6367
6657
  ok: false,
@@ -6374,18 +6664,18 @@ function validateInputSpec(raw, row) {
6374
6664
  };
6375
6665
  }
6376
6666
  const issues = [];
6377
- const allowed = ALLOWED_KEYS_BY_TYPE.get(row.type);
6378
- if (allowed === void 0 || !supportedTypes.includes(row.type)) {
6667
+ const allowed = ALLOWED_KEYS_BY_TYPE.get(row2.type);
6668
+ if (allowed === void 0 || !supportedTypes.includes(row2.type)) {
6379
6669
  issues.push({
6380
6670
  path: [],
6381
- message: row.adapter === "fal" ? `inputSpec on fal rows is only supported for image rows, not "${row.type}"` : `inputSpec is not supported for "${row.type}" rows`
6671
+ message: row2.adapter === "fal" ? `inputSpec on fal rows is only supported for image rows, not "${row2.type}"` : `inputSpec is not supported for "${row2.type}" rows`
6382
6672
  });
6383
6673
  } else {
6384
6674
  for (const key of Object.keys(spec.fields)) {
6385
6675
  if (!allowed.has(key)) {
6386
6676
  issues.push({
6387
6677
  path: ["fields", key],
6388
- message: `inputSpec.fields "${key}" is not supported for ${row.type} rows`
6678
+ message: `inputSpec.fields "${key}" is not supported for ${row2.type} rows`
6389
6679
  });
6390
6680
  }
6391
6681
  }
@@ -6393,13 +6683,13 @@ function validateInputSpec(raw, row) {
6393
6683
  if (!allowed.has(key)) {
6394
6684
  issues.push({
6395
6685
  path: ["values", key],
6396
- message: `inputSpec.values "${key}" is not supported for ${row.type} rows`
6686
+ message: `inputSpec.values "${key}" is not supported for ${row2.type} rows`
6397
6687
  });
6398
6688
  }
6399
6689
  }
6400
6690
  }
6401
6691
  if (spec.editEndpoint !== void 0) {
6402
- if (row.adapter !== "fal") {
6692
+ if (row2.adapter !== "fal") {
6403
6693
  issues.push({
6404
6694
  path: ["editEndpoint"],
6405
6695
  message: "inputSpec.editEndpoint is only supported on fal rows"
@@ -6443,7 +6733,7 @@ function validateInputSpec(raw, row) {
6443
6733
  if (allowed !== void 0 && !allowed.has(key)) {
6444
6734
  issues.push({
6445
6735
  path: ["arrays", name],
6446
- message: `inputSpec.arrays "${key}" is not supported for ${row.type} rows`
6736
+ message: `inputSpec.arrays "${key}" is not supported for ${row2.type} rows`
6447
6737
  });
6448
6738
  }
6449
6739
  if (key in spec.fields) {
@@ -6463,7 +6753,7 @@ function validateInputSpec(raw, row) {
6463
6753
  }
6464
6754
  }
6465
6755
  if (spec.dimensions !== void 0) {
6466
- if (row.type !== "image") {
6756
+ if (row2.type !== "image") {
6467
6757
  issues.push({
6468
6758
  path: ["dimensions"],
6469
6759
  message: "inputSpec.dimensions is only supported on image rows"
@@ -6485,7 +6775,7 @@ function validateInputSpec(raw, row) {
6485
6775
  });
6486
6776
  }
6487
6777
  }
6488
- const supports = row.constraints.supports ?? [];
6778
+ const supports = row2.constraints.supports ?? [];
6489
6779
  for (const key of INPUT_SPEC_SUPPORTS_KEYS) {
6490
6780
  if (supports.includes(key)) continue;
6491
6781
  if (key in spec.fields) {
@@ -6530,8 +6820,8 @@ function listIssues(name, values, fallback, defaultKey) {
6530
6820
  }
6531
6821
  return issues;
6532
6822
  }
6533
- function optionListIssues(row) {
6534
- const { constraints, defaults } = row;
6823
+ function optionListIssues(row2) {
6824
+ const { constraints, defaults } = row2;
6535
6825
  const issues = [
6536
6826
  ...listIssues(
6537
6827
  "voices",
@@ -6589,7 +6879,8 @@ var ModelTypeSchema = import_zod3.z.enum([
6589
6879
  "voice-change",
6590
6880
  "dubbing",
6591
6881
  "lipsync",
6592
- "transcription"
6882
+ "transcription",
6883
+ "model3d"
6593
6884
  ]);
6594
6885
  var LifecycleSchema = import_zod3.z.enum(["active", "deprecated", "retired"]);
6595
6886
  var SurfaceSchema = import_zod3.z.enum(["cowork", "space_builder"]);
@@ -6653,7 +6944,8 @@ var UsageUnitSchema = import_zod3.z.enum([
6653
6944
  "images",
6654
6945
  "seconds",
6655
6946
  "reported_cost_usd",
6656
- "compute_seconds"
6947
+ "compute_seconds",
6948
+ "requests"
6657
6949
  ]);
6658
6950
  var PricingKindSchema = import_zod3.z.enum(["token", "image_size", "video_per_second", "flat"]);
6659
6951
  var ProviderOptionValueSchema = import_zod3.z.lazy(
@@ -6707,6 +6999,156 @@ var LanguageOptionSchema = import_zod3.z.looseObject({
6707
6999
  code: import_zod3.z.string().min(1),
6708
7000
  name: import_zod3.z.string().min(1)
6709
7001
  });
7002
+ var MODEL3D_FAMILY_RULES = {
7003
+ trellis: {
7004
+ textPath: false,
7005
+ views: ["none"],
7006
+ multiImage: false,
7007
+ texture: null,
7008
+ pbr: false,
7009
+ seed: true,
7010
+ textureRef: false,
7011
+ resolutions: "integer"
7012
+ },
7013
+ hunyuan: {
7014
+ textPath: true,
7015
+ views: ["none", "named"],
7016
+ multiImage: false,
7017
+ texture: ["none", "standard"],
7018
+ pbr: true,
7019
+ seed: false,
7020
+ textureRef: false,
7021
+ resolutions: null
7022
+ },
7023
+ tripo: {
7024
+ textPath: true,
7025
+ views: ["none", "ordered"],
7026
+ multiImage: true,
7027
+ texture: ["none", "standard", "detailed"],
7028
+ pbr: true,
7029
+ seed: true,
7030
+ textureRef: false,
7031
+ resolutions: null
7032
+ },
7033
+ rodin: {
7034
+ textPath: true,
7035
+ views: ["none", "unordered"],
7036
+ multiImage: false,
7037
+ texture: null,
7038
+ pbr: false,
7039
+ seed: true,
7040
+ textureRef: false,
7041
+ resolutions: null
7042
+ },
7043
+ meshy: {
7044
+ textPath: true,
7045
+ views: ["none", "unordered"],
7046
+ multiImage: true,
7047
+ texture: ["none", "standard"],
7048
+ pbr: true,
7049
+ seed: true,
7050
+ textureRef: true,
7051
+ resolutions: null
7052
+ },
7053
+ hi3d: {
7054
+ textPath: false,
7055
+ views: ["none", "named"],
7056
+ multiImage: true,
7057
+ texture: ["none", "standard"],
7058
+ pbr: true,
7059
+ seed: false,
7060
+ textureRef: false,
7061
+ resolutions: ["quality", "master"]
7062
+ },
7063
+ sam: {
7064
+ textPath: false,
7065
+ views: ["none"],
7066
+ multiImage: false,
7067
+ texture: null,
7068
+ pbr: false,
7069
+ seed: true,
7070
+ textureRef: false,
7071
+ resolutions: null
7072
+ }
7073
+ };
7074
+ function model3dSpecIssues(spec) {
7075
+ const issues = [];
7076
+ const add = (path, message) => issues.push({ path, message });
7077
+ const rules = MODEL3D_FAMILY_RULES[spec.family];
7078
+ const promptOnly = spec.text && !spec.imageRequired;
7079
+ if (spec.endpoints.image === void 0)
7080
+ add(["endpoints", "image"], "every row needs an image endpoint");
7081
+ if (promptOnly && !rules.textPath) {
7082
+ add(
7083
+ ["imageRequired"],
7084
+ `${spec.family} has no text-to-3D endpoint; a text row needs an image`
7085
+ );
7086
+ }
7087
+ if (promptOnly && spec.endpoints.text === void 0) {
7088
+ add(["endpoints", "text"], "a text row needs a text endpoint");
7089
+ }
7090
+ if (!rules.views.includes(spec.views)) {
7091
+ add(["views"], `${spec.family} takes views ${rules.views.join(" or ")}`);
7092
+ }
7093
+ if (spec.views === "none" && spec.maxImages !== 1) {
7094
+ add(["maxImages"], "a row without views takes 1 image");
7095
+ }
7096
+ if (spec.views === "unordered" && spec.maxImages < 2) {
7097
+ add(["maxImages"], "a row with views takes at least 2 images");
7098
+ }
7099
+ const slots = MODEL3D_IMAGE_SLOTS[spec.views].length;
7100
+ if (spec.views !== "none" && spec.maxImages > slots) {
7101
+ add(["maxImages"], `a row with ${spec.views} views sends at most ${slots} images`);
7102
+ }
7103
+ if ((spec.views === "named" || spec.views === "ordered") && spec.maxImages < slots) {
7104
+ add(
7105
+ ["maxImages"],
7106
+ `a row with ${spec.views} views takes ${slots} images (front and three sides)`
7107
+ );
7108
+ }
7109
+ if (spec.views !== "none" && rules.multiImage && spec.endpoints.multiImage === void 0) {
7110
+ add(["endpoints", "multiImage"], `${spec.family} sends views to a multi-image endpoint`);
7111
+ }
7112
+ if (rules.texture === null) {
7113
+ if (spec.texture.length > 0) add(["texture"], `${spec.family} has no texture switch`);
7114
+ } else {
7115
+ const allowed = rules.texture;
7116
+ if (spec.texture.length === 0) add(["texture"], `${spec.family} needs its texture options`);
7117
+ if (new Set(spec.texture).size !== spec.texture.length)
7118
+ add(["texture"], "texture options repeat");
7119
+ for (const t of spec.texture) {
7120
+ if (!allowed.includes(t)) add(["texture"], `${spec.family} has no "${t}" texture`);
7121
+ }
7122
+ }
7123
+ if (spec.pbr && !rules.pbr) add(["pbr"], `${spec.family} has no PBR switch`);
7124
+ if (spec.pbr && !spec.texture.some((t) => t !== "none")) {
7125
+ add(["pbr"], "PBR needs a textured option");
7126
+ }
7127
+ if (spec.seed && !rules.seed) add(["seed"], `${spec.family} takes no seed`);
7128
+ if (spec.seedMax !== void 0 && !spec.seed) add(["seedMax"], "seedMax needs seed");
7129
+ if (spec.textureRef && !rules.textureRef)
7130
+ add(["textureRef"], `${spec.family} takes no texture reference`);
7131
+ const res = spec.resolutions;
7132
+ if (rules.resolutions === null) {
7133
+ if (res !== void 0) add(["resolutions"], `${spec.family} has no resolution option`);
7134
+ } else if (res === void 0) {
7135
+ add(["resolutions"], `${spec.family} needs its resolution options`);
7136
+ } else {
7137
+ if (!res.options.includes(res.default)) {
7138
+ add(["resolutions", "default"], "default must be an option");
7139
+ }
7140
+ if (new Set(res.options).size !== res.options.length) {
7141
+ add(["resolutions", "options"], "resolution options repeat");
7142
+ }
7143
+ const allowed = rules.resolutions;
7144
+ for (const option of res.options) {
7145
+ const ok = allowed === "integer" ? /^[1-9][0-9]*$/.test(option) : allowed.includes(option);
7146
+ if (!ok)
7147
+ add(["resolutions", "options"], `${spec.family} has no "${option}" resolution`);
7148
+ }
7149
+ }
7150
+ return issues;
7151
+ }
6710
7152
  var VideoToolConstraintsSchema = import_zod3.z.looseObject({
6711
7153
  kind: import_zod3.z.enum(["upscale", "interpolate", "hdr", "bg-remove", "subtitles"]),
6712
7154
  upscaleFactor: RangeSchema.optional(),
@@ -6723,6 +7165,30 @@ var VideoToolConstraintsSchema = import_zod3.z.looseObject({
6723
7165
  translationLanguages: import_zod3.z.array(import_zod3.z.string().min(1)).min(1).optional(),
6724
7166
  maxSecondsAbove1080p: import_zod3.z.number().positive().optional()
6725
7167
  });
7168
+ var Model3dTextureSchema = import_zod3.z.enum(["none", "standard", "detailed"]);
7169
+ var Model3dConstraintsSchema = import_zod3.z.looseObject({
7170
+ family: import_zod3.z.enum(["trellis", "hunyuan", "tripo", "rodin", "meshy", "hi3d", "sam"]),
7171
+ endpoints: import_zod3.z.looseObject({
7172
+ text: import_zod3.z.string().min(1).optional(),
7173
+ image: import_zod3.z.string().min(1).optional(),
7174
+ multiImage: import_zod3.z.string().min(1).optional()
7175
+ }),
7176
+ text: import_zod3.z.boolean(),
7177
+ imageRequired: import_zod3.z.boolean(),
7178
+ promptRequired: import_zod3.z.boolean().optional(),
7179
+ views: import_zod3.z.enum(["none", "unordered", "ordered", "named"]),
7180
+ maxImages: import_zod3.z.number().int().positive(),
7181
+ texture: import_zod3.z.array(Model3dTextureSchema),
7182
+ pbr: import_zod3.z.boolean(),
7183
+ textureRef: import_zod3.z.boolean(),
7184
+ seed: import_zod3.z.boolean(),
7185
+ seedMax: import_zod3.z.number().int().positive().optional(),
7186
+ resolutions: import_zod3.z.looseObject({
7187
+ label: import_zod3.z.string().min(1),
7188
+ options: import_zod3.z.array(import_zod3.z.string().min(1)).min(1),
7189
+ default: import_zod3.z.string().min(1)
7190
+ }).optional()
7191
+ });
6726
7192
  var ModelConstraintsSchema = import_zod3.z.looseObject({
6727
7193
  aspectRatios: import_zod3.z.array(AspectRatioSchema).optional(),
6728
7194
  sizes: import_zod3.z.array(ImageSizeSchema).optional(),
@@ -6750,6 +7216,7 @@ var ModelConstraintsSchema = import_zod3.z.looseObject({
6750
7216
  returns: import_zod3.z.enum(["new", "joined"]).optional()
6751
7217
  }).optional(),
6752
7218
  tool: VideoToolConstraintsSchema.optional(),
7219
+ model3d: Model3dConstraintsSchema.optional(),
6753
7220
  // Loose on purpose: the read path keeps a row whose value is invalid (the accessors in
6754
7221
  // conventions.ts read it as "not set"); CatalogModelSchema below rejects it on write.
6755
7222
  audio: import_zod3.z.unknown().optional(),
@@ -6865,29 +7332,47 @@ var CatalogModelSchemaBase = import_zod3.z.looseObject({
6865
7332
  pricingKind: PricingKindSchema,
6866
7333
  display: CatalogDisplaySchema.optional(),
6867
7334
  capabilities: CatalogCapabilitiesSchema.optional()
6868
- }).superRefine((row, ctx) => {
6869
- if (row.lifecycle === "active" && !row.display) {
7335
+ }).superRefine((row2, ctx) => {
7336
+ if (row2.lifecycle === "active" && !row2.display) {
6870
7337
  ctx.addIssue({
6871
7338
  code: "custom",
6872
7339
  path: ["display"],
6873
7340
  message: "active rows need display"
6874
7341
  });
6875
7342
  }
6876
- if (row.lifecycle === "active" && row.type === "text" && !row.capabilities?.contextWindow) {
7343
+ if (row2.lifecycle === "active" && row2.type === "text" && !row2.capabilities?.contextWindow) {
6877
7344
  ctx.addIssue({
6878
7345
  code: "custom",
6879
7346
  path: ["capabilities", "contextWindow"],
6880
7347
  message: "active text rows need contextWindow"
6881
7348
  });
6882
7349
  }
6883
- if (row.type === "video-tool" && row.constraints.tool === void 0) {
7350
+ if (row2.type === "video-tool" && row2.constraints.tool === void 0) {
6884
7351
  ctx.addIssue({
6885
7352
  code: "custom",
6886
7353
  path: ["constraints", "tool"],
6887
7354
  message: "video-tool rows need constraints.tool"
6888
7355
  });
6889
7356
  }
6890
- if (row.replacedBy !== void 0 && row.replacedBy === row.id) {
7357
+ if (row2.type === "model3d") {
7358
+ const spec = row2.constraints.model3d;
7359
+ if (spec === void 0) {
7360
+ ctx.addIssue({
7361
+ code: "custom",
7362
+ path: ["constraints", "model3d"],
7363
+ message: "model3d rows need constraints.model3d"
7364
+ });
7365
+ } else {
7366
+ for (const issue of model3dSpecIssues(spec)) {
7367
+ ctx.addIssue({
7368
+ code: "custom",
7369
+ path: ["constraints", "model3d", ...issue.path],
7370
+ message: issue.message
7371
+ });
7372
+ }
7373
+ }
7374
+ }
7375
+ if (row2.replacedBy !== void 0 && row2.replacedBy === row2.id) {
6891
7376
  ctx.addIssue({
6892
7377
  code: "custom",
6893
7378
  path: ["replacedBy"],
@@ -6896,8 +7381,8 @@ var CatalogModelSchemaBase = import_zod3.z.looseObject({
6896
7381
  }
6897
7382
  });
6898
7383
  var CatalogModelSchema = CatalogModelSchemaBase.superRefine(
6899
- (row, ctx) => {
6900
- const transport = row.providerOptions?.["modelkit"]?.["transport"];
7384
+ (row2, ctx) => {
7385
+ const transport = row2.providerOptions?.["modelkit"]?.["transport"];
6901
7386
  if (transport !== void 0 && transport !== "direct" && transport !== "bedrock") {
6902
7387
  ctx.addIssue({
6903
7388
  code: "custom",
@@ -6905,9 +7390,9 @@ var CatalogModelSchema = CatalogModelSchemaBase.superRefine(
6905
7390
  message: 'transport must be "direct" or "bedrock"'
6906
7391
  });
6907
7392
  }
6908
- const inputSpecRaw = row.providerOptions?.["modelkit"]?.["inputSpec"];
7393
+ const inputSpecRaw = row2.providerOptions?.["modelkit"]?.["inputSpec"];
6909
7394
  if (inputSpecRaw !== void 0) {
6910
- const result = validateInputSpec(inputSpecRaw, row);
7395
+ const result = validateInputSpec(inputSpecRaw, row2);
6911
7396
  if (!result.ok) {
6912
7397
  for (const issue of result.issues) {
6913
7398
  ctx.addIssue({
@@ -6918,7 +7403,7 @@ var CatalogModelSchema = CatalogModelSchemaBase.superRefine(
6918
7403
  }
6919
7404
  }
6920
7405
  }
6921
- const audio = row.constraints.audio;
7406
+ const audio = row2.constraints.audio;
6922
7407
  if (audio !== void 0 && !AudioOutputSchema.safeParse(audio).success) {
6923
7408
  ctx.addIssue({
6924
7409
  code: "custom",
@@ -6926,7 +7411,7 @@ var CatalogModelSchema = CatalogModelSchemaBase.superRefine(
6926
7411
  message: 'audio must be "always", "optional" or "never"'
6927
7412
  });
6928
7413
  }
6929
- const refs = row.constraints.references;
7414
+ const refs = row2.constraints.references;
6930
7415
  for (const kind of ["images", "videos", "audios"]) {
6931
7416
  const least = refs?.min?.[kind];
6932
7417
  const most = refs?.[kind];
@@ -6938,10 +7423,10 @@ var CatalogModelSchema = CatalogModelSchemaBase.superRefine(
6938
7423
  });
6939
7424
  }
6940
7425
  }
6941
- for (const issue of optionListIssues(row)) {
7426
+ for (const issue of optionListIssues(row2)) {
6942
7427
  ctx.addIssue({ code: "custom", path: issue.path, message: issue.message });
6943
7428
  }
6944
- const prompt = row.constraints.prompt;
7429
+ const prompt = row2.constraints.prompt;
6945
7430
  if (prompt !== void 0) {
6946
7431
  const parsed = PromptConventionsSchema.safeParse(prompt);
6947
7432
  if (!parsed.success) {
@@ -6956,7 +7441,7 @@ var CatalogModelSchema = CatalogModelSchemaBase.superRefine(
6956
7441
  }
6957
7442
  }
6958
7443
  );
6959
- var CatalogSchema = import_zod3.z.array(CatalogModelSchemaBase.transform((row) => normaliseConventions(row).row)).superRefine((rows, ctx) => {
7444
+ var CatalogSchema = import_zod3.z.array(CatalogModelSchemaBase.transform((row2) => normaliseConventions(row2).row)).superRefine((rows, ctx) => {
6960
7445
  for (const issue of collisionIssues(rows)) {
6961
7446
  ctx.addIssue({
6962
7447
  code: "custom",
@@ -7030,22 +7515,22 @@ function quarantine(rawRows, logger) {
7030
7515
  dropped += 1;
7031
7516
  continue;
7032
7517
  }
7033
- const row = parsed.data;
7034
- const inputSpecRaw = row.providerOptions?.["modelkit"]?.["inputSpec"];
7518
+ const row2 = parsed.data;
7519
+ const inputSpecRaw = row2.providerOptions?.["modelkit"]?.["inputSpec"];
7035
7520
  if (inputSpecRaw !== void 0) {
7036
- const result = validateInputSpec(inputSpecRaw, row);
7521
+ const result = validateInputSpec(inputSpecRaw, row2);
7037
7522
  if (!result.ok) {
7038
7523
  const first = result.issues[0];
7039
7524
  const detail = first ? dropReason(first.path, first.message) : "invalid inputSpec";
7040
7525
  logger.warn(
7041
- `[modelkit] catalog row ${row.id} has an invalid providerOptions.modelkit.inputSpec: ${detail}; calls fail until it is fixed`
7526
+ `[modelkit] catalog row ${row2.id} has an invalid providerOptions.modelkit.inputSpec: ${detail}; calls fail until it is fixed`
7042
7527
  );
7043
7528
  }
7044
7529
  }
7045
- const normalised = normaliseConventions(row);
7530
+ const normalised = normaliseConventions(row2);
7046
7531
  if (normalised.invalidKeys.length > 0) {
7047
7532
  logger.warn(
7048
- `[modelkit] catalog row ${row.id} has an invalid ${normalised.invalidKeys.join(", ")}; read as not set`
7533
+ `[modelkit] catalog row ${row2.id} has an invalid ${normalised.invalidKeys.join(", ")}; read as not set`
7049
7534
  );
7050
7535
  }
7051
7536
  valid.push(normalised.row);
@@ -7059,10 +7544,10 @@ function quarantine(rawRows, logger) {
7059
7544
  if (!firstByIndex.has(issue.index)) firstByIndex.set(issue.index, issue);
7060
7545
  }
7061
7546
  for (const [index, issue] of [...firstByIndex.entries()].sort((a, b) => a[0] - b[0])) {
7062
- const row = kept[index];
7063
- if (!row) continue;
7547
+ const row2 = kept[index];
7548
+ if (!row2) continue;
7064
7549
  logger.warn(
7065
- `[modelkit] catalog row dropped id=${row.id} reason=${String(issue.path[0] ?? "")} ${issue.message}`
7550
+ `[modelkit] catalog row dropped id=${row2.id} reason=${String(issue.path[0] ?? "")} ${issue.message}`
7066
7551
  );
7067
7552
  }
7068
7553
  const dropSet = new Set(firstByIndex.keys());
@@ -7281,9 +7766,9 @@ var ToolSchema = import_zod6.z.object({
7281
7766
  translationLanguages: import_zod6.z.array(import_zod6.z.string().min(1)).min(1).optional(),
7282
7767
  maxSecondsAbove1080p: import_zod6.z.number().positive().optional()
7283
7768
  });
7284
- function videoToolConstraints(row) {
7285
- if (row.type !== "video-tool") return void 0;
7286
- const parsed = ToolSchema.safeParse(row.constraints.tool);
7769
+ function videoToolConstraints(row2) {
7770
+ if (row2.type !== "video-tool") return void 0;
7771
+ const parsed = ToolSchema.safeParse(row2.constraints.tool);
7287
7772
  if (!parsed.success) return void 0;
7288
7773
  const {
7289
7774
  kind,
@@ -7330,16 +7815,16 @@ function resolutionTier(width, height) {
7330
7815
  function fail(error) {
7331
7816
  return { ok: false, error };
7332
7817
  }
7333
- function planVideoTool(row, req) {
7334
- const tool = videoToolConstraints(row);
7335
- if (!tool) return fail(`${row.id} is not a video tool`);
7818
+ function planVideoTool(row2, req) {
7819
+ const tool = videoToolConstraints(row2);
7820
+ if (!tool) return fail(`${row2.id} is not a video tool`);
7336
7821
  const { width, height, durationSeconds, fps } = req.source;
7337
7822
  if (!(width > 0 && height > 0 && durationSeconds > 0)) {
7338
7823
  return fail("The source video's width, height and duration are required");
7339
7824
  }
7340
- const maxSeconds = row.constraints.sourceVideoSeconds?.max;
7825
+ const maxSeconds = row2.constraints.sourceVideoSeconds?.max;
7341
7826
  if (maxSeconds !== void 0 && overMax(durationSeconds, maxSeconds)) {
7342
- return fail(`${row.label} takes source videos up to ${maxSeconds} seconds.`);
7827
+ return fail(`${row2.label} takes source videos up to ${maxSeconds} seconds.`);
7343
7828
  }
7344
7829
  const base = {
7345
7830
  durationSeconds,
@@ -7350,7 +7835,7 @@ function planVideoTool(row, req) {
7350
7835
  if (factor === void 0) return fail("An upscale factor is required");
7351
7836
  const range2 = tool.upscaleFactor;
7352
7837
  if (range2 && (factor < range2.min || factor > range2.max)) {
7353
- return fail(`${row.label} upscales by ${range2.min}x to ${range2.max}x.`);
7838
+ return fail(`${row2.label} upscales by ${range2.min}x to ${range2.max}x.`);
7354
7839
  }
7355
7840
  const outWidth = Math.round(width * factor);
7356
7841
  const outHeight = Math.round(height * factor);
@@ -7378,7 +7863,7 @@ function planVideoTool(row, req) {
7378
7863
  }
7379
7864
  const range2 = tool.fps;
7380
7865
  if (range2 && (target < range2.min || target > range2.max)) {
7381
- return fail(`${row.label} outputs ${range2.min} to ${range2.max} fps.`);
7866
+ return fail(`${row2.label} outputs ${range2.min} to ${range2.max} fps.`);
7382
7867
  }
7383
7868
  return {
7384
7869
  ok: true,
@@ -7407,7 +7892,7 @@ function planVideoTool(row, req) {
7407
7892
  if (tool.kind === "subtitles") {
7408
7893
  const resolution = resolutionTier(width, height);
7409
7894
  const problem = subtitleProblem(
7410
- row.label,
7895
+ row2.label,
7411
7896
  tool,
7412
7897
  req.subtitles,
7413
7898
  resolution,
@@ -7419,9 +7904,9 @@ function planVideoTool(row, req) {
7419
7904
  const formats = tool.outputFormats ?? ["h265"];
7420
7905
  const format = req.outputFormat ?? formats[0] ?? "h265";
7421
7906
  if (!formats.includes(format)) {
7422
- return fail(`${row.label} writes ${formats.join(" or ")}.`);
7907
+ return fail(`${row2.label} writes ${formats.join(" or ")}.`);
7423
7908
  }
7424
- const timing = renderedTiming(row, tool, durationSeconds, fps);
7909
+ const timing = renderedTiming(row2, tool, durationSeconds, fps);
7425
7910
  if (!timing.ok) return fail(timing.error);
7426
7911
  const out = shrinkToShortEdge(width, height, tool.maxOutputShortEdge);
7427
7912
  return {
@@ -7437,7 +7922,7 @@ function planVideoTool(row, req) {
7437
7922
  };
7438
7923
  }
7439
7924
  var DEFAULT_FPS = 24;
7440
- function renderedTiming(row, tool, durationSeconds, fps) {
7925
+ function renderedTiming(row2, tool, durationSeconds, fps) {
7441
7926
  const { maxOutputFps, minOutputFrames, maxOutputFrames } = tool;
7442
7927
  if (maxOutputFps === void 0 && minOutputFrames === void 0 && maxOutputFrames === void 0) {
7443
7928
  return fps !== void 0 ? { ok: true, fps } : { ok: true };
@@ -7446,7 +7931,7 @@ function renderedTiming(row, tool, durationSeconds, fps) {
7446
7931
  const frames = Math.max(minOutputFrames ?? 1, Math.round(durationSeconds * rate));
7447
7932
  if (maxOutputFrames !== void 0 && frames > maxOutputFrames) {
7448
7933
  const limit = `${maxOutputFrames} frames (${seconds(maxOutputFrames, rate)})`;
7449
- return { ok: false, error: `${row.label} renders up to ${limit}.` };
7934
+ return { ok: false, error: `${row2.label} renders up to ${limit}.` };
7450
7935
  }
7451
7936
  return { ok: true, fps: rate, frames, durationSeconds: frames / rate };
7452
7937
  }
@@ -7790,6 +8275,30 @@ function applyFalImageRules(model, req, callerSize, warnings) {
7790
8275
  req.size = exact.tier;
7791
8276
  }
7792
8277
 
8278
+ // src/replicate-video-kinds.ts
8279
+ function replicateBaseModelId(providerModelId) {
8280
+ const colon = providerModelId.indexOf(":");
8281
+ return colon === -1 ? providerModelId : providerModelId.slice(0, colon);
8282
+ }
8283
+ var REPLICATE_VIDEO_KINDS = /* @__PURE__ */ new Map([
8284
+ ["kling-video/v2.5-turbo/pro/image-to-video", "kling"],
8285
+ ["kling-video/v3.0/pro/image-to-video", "kling"],
8286
+ ["kwaivgi/kling-v3-video", "kling"],
8287
+ ["kwaivgi/kling-v3-omni-video", "kling-omni"],
8288
+ ["minimax/hailuo-02", "hailuo"]
8289
+ ]);
8290
+ function videoKind(providerModelId) {
8291
+ return REPLICATE_VIDEO_KINDS.get(replicateBaseModelId(providerModelId));
8292
+ }
8293
+ var KLING_TIERS = [
8294
+ { mode: "standard", resolution: "720p" },
8295
+ { mode: "pro", resolution: "1080p" },
8296
+ { mode: "4k", resolution: "4k" }
8297
+ ];
8298
+ function klingTier(req) {
8299
+ return KLING_TIERS.find((tier) => tier.resolution === req.resolution) ?? KLING_TIERS.find((tier) => tier.mode === req.mode) ?? KLING_TIERS[0];
8300
+ }
8301
+
7793
8302
  // src/resolve-video-rules.ts
7794
8303
  var MOTION_PRO = "fal-ai/kling-video/v3/pro/motion-control";
7795
8304
  var MOTION_STANDARD = "fal-ai/kling-video/v3/standard/motion-control";
@@ -7963,6 +8472,16 @@ function h3EndFrameRatioWarning(model, ratio) {
7963
8472
  message: `${model.id} follows the end frame's aspect ratio`
7964
8473
  };
7965
8474
  }
8475
+ function isReplicateKling(model) {
8476
+ if (model.adapter !== "replicate") return false;
8477
+ const kind = videoKind(model.providerModelId);
8478
+ return kind === "kling" || kind === "kling-omni";
8479
+ }
8480
+ function fillReplicateKlingResolution(model, req) {
8481
+ if (isReplicateKling(model) && req.resolution === void 0) {
8482
+ req.resolution = klingTier(req).resolution;
8483
+ }
8484
+ }
7966
8485
  function applyFalVideoRules(model, req, callerRatio, warnings) {
7967
8486
  if (model.adapter !== "fal") return;
7968
8487
  const problem = falVideoProblem(model.providerModelId, withPromptForResolve(req));
@@ -8352,8 +8871,10 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
8352
8871
  warnings
8353
8872
  );
8354
8873
  if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
8874
+ fillReplicateKlingResolution(model, merged);
8355
8875
  const resolution = resolveVideoResolution(merged.resolution, model, onUnsupported, warnings);
8356
8876
  if (resolution !== void 0) merged.resolution = resolution;
8877
+ if (isReplicateKling(model)) delete merged.mode;
8357
8878
  const duration = resolveDuration(
8358
8879
  merged.durationSeconds,
8359
8880
  c.durations,
@@ -8759,7 +9280,10 @@ var MEDIA_METHODS = {
8759
9280
  lipsync: ["lipsync"],
8760
9281
  dubbing: ["dub"],
8761
9282
  transcription: ["transcribe"],
8762
- "video-tool": ["submitVideoTool"]
9283
+ "video-tool": ["submitVideoTool"],
9284
+ // 3D jobs run through submitModel3d, which checks the row type itself; their preflight
9285
+ // is model3dInputRefusal, not resolveOptions.
9286
+ model3d: []
8763
9287
  };
8764
9288
  var METHOD_NOUN = {
8765
9289
  generateImage: "an image model",