@shop-os/modelkit 0.1.4 → 0.1.6

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
@@ -68,6 +68,7 @@ __export(index_exports, {
68
68
  prodiaAdapter: () => prodiaAdapter,
69
69
  readErrorBody: () => readErrorBody,
70
70
  replicateAdapter: () => replicateAdapter,
71
+ resolveAudioOptions: () => resolveAudioOptions,
71
72
  resolveImageOptions: () => resolveImageOptions,
72
73
  resolveKeys: () => resolveKeys,
73
74
  resolveVideoOptions: () => resolveVideoOptions,
@@ -175,7 +176,8 @@ var GEMINI_RATIOS = [
175
176
  "3:4",
176
177
  "21:9",
177
178
  "4:5",
178
- "5:4"
179
+ "5:4",
180
+ "adaptive"
179
181
  ];
180
182
  function geminiImage(id, label, aliases, sizes) {
181
183
  const model = {
@@ -186,7 +188,11 @@ function geminiImage(id, label, aliases, sizes) {
186
188
  adapter: "google-image",
187
189
  providerModelId: id,
188
190
  lifecycle: "active",
189
- constraints: { aspectRatios: [...GEMINI_RATIOS], sizes },
191
+ constraints: {
192
+ aspectRatios: [...GEMINI_RATIOS],
193
+ sizes,
194
+ supports: ["safetySettings", "temperature"]
195
+ },
190
196
  defaults: { size: "1K", aspectRatio: "1:1" },
191
197
  usage: { ...IMAGE_USAGE },
192
198
  pricingKind: "image_size"
@@ -283,7 +289,8 @@ var IMAGE_MODELS = [
283
289
  "9:16",
284
290
  "4:5",
285
291
  "5:4",
286
- "9:21"
292
+ "9:21",
293
+ "adaptive"
287
294
  ],
288
295
  sizes: ["1K"],
289
296
  references: { images: 10 },
@@ -324,8 +331,20 @@ var IMAGE_MODELS = [
324
331
  constraints: {
325
332
  // 97:30 has no Ark size spelling; the A+ flow pads inputs to 97:30 and
326
333
  // omits the ratio instead. Advertise only sizes the adapter can send.
327
- aspectRatios: ["1:1", "2:3", "3:2", "4:3", "3:4", "4:5", "5:4", "9:16", "16:9", "21:9"],
328
- sizes: ["1K", "2K"],
334
+ aspectRatios: [
335
+ "1:1",
336
+ "2:3",
337
+ "3:2",
338
+ "4:3",
339
+ "3:4",
340
+ "4:5",
341
+ "5:4",
342
+ "9:16",
343
+ "16:9",
344
+ "21:9",
345
+ "9:21"
346
+ ],
347
+ sizes: ["1K", "2K", "4K"],
329
348
  references: { images: 10 },
330
349
  supports: ["seed"]
331
350
  },
@@ -344,9 +363,10 @@ var IMAGE_MODELS = [
344
363
  constraints: {
345
364
  aspectRatios: ["1:1", "2:3", "3:2", "16:9", "9:16", "4:3", "3:4", "4:5", "5:4", "21:9"],
346
365
  sizes: ["1K"],
347
- supports: ["outputFormat"]
366
+ // flux-2-pro's job type always returns image/png; only the seed is forwarded.
367
+ supports: ["seed"]
348
368
  },
349
- defaults: { size: "1K", aspectRatio: "1:1", outputFormat: "png" },
369
+ defaults: { size: "1K", aspectRatio: "1:1" },
350
370
  usage: { ...IMAGE_USAGE },
351
371
  pricingKind: "image_size"
352
372
  },
@@ -364,7 +384,8 @@ var IMAGE_MODELS = [
364
384
  constraints: {
365
385
  aspectRatios: ["1:1", "2:3", "3:2", "4:3", "3:4", "4:5", "5:4", "9:16", "16:9", "21:9"],
366
386
  sizes: ["1K", "2K", "4K"],
367
- requires: ["references"]
387
+ requires: ["references"],
388
+ supports: ["seed"]
368
389
  },
369
390
  defaults: { size: "1K", aspectRatio: "1:1" },
370
391
  usage: { ...IMAGE_USAGE },
@@ -383,7 +404,8 @@ var IMAGE_MODELS = [
383
404
  constraints: {
384
405
  aspectRatios: ["1:1", "2:3", "3:4", "4:3", "9:16", "16:9", "21:9"],
385
406
  sizes: ["1K"],
386
- requires: ["references"]
407
+ requires: ["references"],
408
+ supports: ["seed"]
387
409
  },
388
410
  defaults: { size: "1K", aspectRatio: "1:1" },
389
411
  usage: { ...IMAGE_USAGE },
@@ -403,7 +425,8 @@ var IMAGE_MODELS = [
403
425
  constraints: {
404
426
  aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4"],
405
427
  sizes: ["1K", "2K", "4K"],
406
- references: { images: 9 }
428
+ references: { images: 9 },
429
+ supports: []
407
430
  },
408
431
  defaults: { size: "1K", aspectRatio: "1:1" },
409
432
  usage: { unit: "compute_seconds", modality: "IMAGE" },
@@ -424,7 +447,8 @@ var IMAGE_MODELS = [
424
447
  aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"],
425
448
  sizes: [],
426
449
  references: { images: 1 },
427
- requires: ["references"]
450
+ requires: ["references"],
451
+ supports: []
428
452
  },
429
453
  defaults: { aspectRatio: "1:1" },
430
454
  usage: { unit: "compute_seconds", modality: "IMAGE" },
@@ -466,7 +490,8 @@ var OTHER_MODELS = [
466
490
  lifecycle: "active",
467
491
  constraints: {
468
492
  durations: [30],
469
- references: { images: 10 }
493
+ references: { images: 10 },
494
+ supports: []
470
495
  },
471
496
  defaults: {},
472
497
  usage: { unit: "images", modality: "AUDIO" },
@@ -481,7 +506,8 @@ var OTHER_MODELS = [
481
506
  providerModelId: "lyria-3-pro-preview",
482
507
  lifecycle: "active",
483
508
  constraints: {
484
- references: { images: 10 }
509
+ references: { images: 10 },
510
+ supports: []
485
511
  },
486
512
  defaults: {},
487
513
  usage: { unit: "images", modality: "AUDIO" },
@@ -548,7 +574,7 @@ var OTHER_MODELS = [
548
574
  constraints: {
549
575
  references: { images: 1 },
550
576
  requires: ["references"],
551
- supports: ["scaleFactor", "outputFormat"]
577
+ supports: ["scaleFactor", "outputFormat", "upscale"]
552
578
  },
553
579
  defaults: { scaleFactor: 2, outputFormat: "png" },
554
580
  usage: { unit: "compute_seconds", modality: "IMAGE" },
@@ -564,7 +590,8 @@ var OTHER_MODELS = [
564
590
  lifecycle: "active",
565
591
  constraints: {
566
592
  references: { images: 1 },
567
- requires: ["references"]
593
+ requires: ["references"],
594
+ supports: []
568
595
  },
569
596
  defaults: {},
570
597
  usage: { unit: "images", modality: "IMAGE" },
@@ -583,7 +610,7 @@ function text(id, label, providerModelId, extra) {
583
610
  adapter: "ai-sdk-text",
584
611
  providerModelId,
585
612
  lifecycle: "active",
586
- constraints: {},
613
+ constraints: { supports: [] },
587
614
  defaults: {},
588
615
  usage: { ...TEXT_USAGE },
589
616
  pricingKind: "token",
@@ -656,7 +683,7 @@ var TEXT_MODELS = [
656
683
  // src/catalog/video.ts
657
684
  var VIDEO_USAGE = { unit: "seconds", modality: "VIDEO" };
658
685
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
659
- var KLING_RATIOS = ["16:9", "9:16", "1:1"];
686
+ var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
660
687
  var OMNI_RATIOS = ["16:9", "9:16"];
661
688
  var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
662
689
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
@@ -748,12 +775,15 @@ var VIDEO_MODELS = [
748
775
  providerModelId: "gemini-omni-flash-preview",
749
776
  lifecycle: "active",
750
777
  constraints: {
778
+ // 1.0 rejects `resolution`; it renders 720p. No defaults: the adapter
779
+ // sends ratio and duration only when the caller set them, so a
780
+ // reference clip keeps its own framing.
751
781
  aspectRatios: [...OMNI_RATIOS],
752
- resolutions: ["720p"],
753
782
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
754
- references: { images: 6, videos: 1 }
783
+ references: { images: 6, videos: 1 },
784
+ supports: []
755
785
  },
756
- defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
786
+ defaults: {},
757
787
  usage: { ...VIDEO_USAGE },
758
788
  pricingKind: "video_per_second"
759
789
  },
@@ -769,9 +799,10 @@ var VIDEO_MODELS = [
769
799
  aspectRatios: [...OMNI_RATIOS],
770
800
  resolutions: ["360p", "720p", "1080p", "4k"],
771
801
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
772
- references: { images: 6, videos: 1 }
802
+ references: { images: 6, videos: 1 },
803
+ supports: []
773
804
  },
774
- defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
805
+ defaults: {},
775
806
  usage: { ...VIDEO_USAGE },
776
807
  pricingKind: "video_per_second"
777
808
  },
@@ -790,7 +821,7 @@ var VIDEO_MODELS = [
790
821
  durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
791
822
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
792
823
  ratioIgnoredWith: ["firstFrame"],
793
- supports: ["generateAudio", "seed", "lastFrame"]
824
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
794
825
  },
795
826
  defaults: {
796
827
  durationSeconds: 5,
@@ -815,7 +846,7 @@ var VIDEO_MODELS = [
815
846
  durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
816
847
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
817
848
  ratioIgnoredWith: ["firstFrame"],
818
- supports: ["generateAudio", "seed", "lastFrame"]
849
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
819
850
  },
820
851
  defaults: {
821
852
  durationSeconds: 5,
@@ -841,7 +872,7 @@ var VIDEO_MODELS = [
841
872
  durations: { min: 4, max: 30 },
842
873
  references: { images: 30, videos: 10, audios: 10 },
843
874
  ratioIgnoredWith: ["firstFrame"],
844
- supports: ["generateAudio", "seed", "lastFrame"]
875
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
845
876
  },
846
877
  defaults: {
847
878
  durationSeconds: 5,
@@ -865,9 +896,11 @@ var VIDEO_MODELS = [
865
896
  aspectRatios: [...GROK_RATIOS],
866
897
  resolutions: ["480p", "720p"],
867
898
  durations: { min: 1, max: 15 },
868
- references: { images: 7 }
899
+ references: { images: 7 },
900
+ supports: []
869
901
  },
870
- defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
902
+ // No resolution default: xAI picks its own unless the caller asks.
903
+ defaults: { durationSeconds: 8, aspectRatio: "16:9" },
871
904
  usage: { unit: "reported_cost_usd", modality: "VIDEO" },
872
905
  pricingKind: "video_per_second"
873
906
  },
@@ -961,7 +994,7 @@ var VIDEO_MODELS = [
961
994
  aspectRatios: [...H3_RATIOS],
962
995
  resolutions: ["480p", "768p", "2k", "4k"],
963
996
  durations: { min: 5, max: 15 },
964
- supports: ["seed", "promptExpansion"]
997
+ supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"]
965
998
  },
966
999
  defaults: {
967
1000
  durationSeconds: 5,
@@ -984,7 +1017,7 @@ var VIDEO_MODELS = [
984
1017
  aspectRatios: [...H3_RATIOS],
985
1018
  resolutions: ["480p", "768p"],
986
1019
  durations: { min: 5, max: 15 },
987
- supports: ["seed", "promptExpansion"]
1020
+ supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"]
988
1021
  },
989
1022
  defaults: {
990
1023
  durationSeconds: 5,
@@ -1008,7 +1041,7 @@ var VIDEO_MODELS = [
1008
1041
  resolutions: ["480p", "768p", "2k", "4k"],
1009
1042
  durations: { min: 5, max: 15 },
1010
1043
  references: { images: 9, videos: 3, audios: 3, total: 12, audioOnly: false },
1011
- supports: ["seed", "promptExpansion"]
1044
+ supports: ["seed", "promptExpansion", "safetyChecker"]
1012
1045
  },
1013
1046
  defaults: {
1014
1047
  durationSeconds: 5,
@@ -1032,7 +1065,7 @@ var VIDEO_MODELS = [
1032
1065
  resolutions: ["480p", "768p"],
1033
1066
  durations: { min: 5, max: 15 },
1034
1067
  references: { images: 9, videos: 3, audios: 3, total: 12, audioOnly: false },
1035
- supports: ["seed", "promptExpansion"]
1068
+ supports: ["seed", "promptExpansion", "safetyChecker"]
1036
1069
  },
1037
1070
  defaults: {
1038
1071
  durationSeconds: 5,
@@ -1055,7 +1088,8 @@ var VIDEO_MODELS = [
1055
1088
  constraints: {
1056
1089
  aspectRatios: [...KLING_RATIOS],
1057
1090
  durations: { min: 3, max: 10 },
1058
- requires: ["sourceVideo"]
1091
+ requires: ["sourceVideo"],
1092
+ supports: []
1059
1093
  },
1060
1094
  defaults: { durationSeconds: 5, aspectRatio: "16:9" },
1061
1095
  usage: { ...VIDEO_USAGE },
@@ -1088,7 +1122,7 @@ var VIDEO_MODELS = [
1088
1122
  lifecycle: "active",
1089
1123
  constraints: {
1090
1124
  requires: ["firstFrame"],
1091
- supports: ["seed"]
1125
+ supports: ["seed", "lastFrame"]
1092
1126
  },
1093
1127
  defaults: {},
1094
1128
  usage: { ...VIDEO_USAGE },
@@ -1159,19 +1193,22 @@ function resolveTier(field, code, requested, supported, order, model, onUnsuppor
1159
1193
  });
1160
1194
  return to;
1161
1195
  }
1162
- function resolveDuration(requested, durations, model, warnings) {
1196
+ function resolveDuration(requested, durations, model, onUnsupported, warnings) {
1163
1197
  if (requested === void 0 || !durations) return requested;
1164
- let to = requested;
1165
- if (Array.isArray(durations)) {
1166
- if (durations.includes(requested)) return requested;
1167
- to = durations.reduce(
1168
- (best, d) => Math.abs(d - requested) < Math.abs(best - requested) ? d : best,
1169
- durations[0] ?? requested
1170
- );
1171
- } else {
1172
- to = Math.min(Math.max(requested, durations.min), durations.max);
1173
- if (to === requested) return requested;
1198
+ const inSet = Array.isArray(durations) ? durations.includes(requested) : requested >= durations.min && requested <= durations.max;
1199
+ if (inSet) return requested;
1200
+ if (onUnsupported === "error") {
1201
+ const supported = Array.isArray(durations) ? `Supported: ${durations.join(", ")}.` : `Supported range: ${durations.min}-${durations.max}.`;
1202
+ throw new ModelKitError({
1203
+ code: "unsupported_option",
1204
+ message: `${model.id} does not support durationSeconds ${requested}. ${supported}`,
1205
+ model: model.id
1206
+ });
1174
1207
  }
1208
+ const to = Array.isArray(durations) ? durations.reduce(
1209
+ (best, d) => Math.abs(d - requested) < Math.abs(best - requested) ? d : best,
1210
+ durations[0] ?? requested
1211
+ ) : Math.min(Math.max(requested, durations.min), durations.max);
1175
1212
  warnings.push({
1176
1213
  code: "duration_coerced",
1177
1214
  field: "durationSeconds",
@@ -1181,6 +1218,26 @@ function resolveDuration(requested, durations, model, warnings) {
1181
1218
  });
1182
1219
  return to;
1183
1220
  }
1221
+ function resolveAspectRatio(requested, supported, model, onUnsupported, warnings) {
1222
+ if (requested === void 0 || requested === "adaptive" || !supported) return requested;
1223
+ if (supported.includes(requested)) return requested;
1224
+ if (onUnsupported === "error") {
1225
+ throw new ModelKitError({
1226
+ code: "unsupported_option",
1227
+ message: `${model.id} does not support aspectRatio "${requested}". Supported: ${supported.join(", ")}.`,
1228
+ model: model.id
1229
+ });
1230
+ }
1231
+ const to = nearestAspectRatio(requested, supported);
1232
+ warnings.push({
1233
+ code: "ratio_coerced",
1234
+ field: "aspectRatio",
1235
+ from: requested,
1236
+ to,
1237
+ message: `aspect ratio ${requested} coerced to ${to} for ${model.id}`
1238
+ });
1239
+ return to;
1240
+ }
1184
1241
  function compact(refs) {
1185
1242
  return (refs ?? []).filter((r) => Boolean(r));
1186
1243
  }
@@ -1199,17 +1256,14 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1199
1256
  const warnings = [];
1200
1257
  const c = model.constraints;
1201
1258
  const merged = { ...model.defaults, ...stripUndefined(request), model: model.id };
1202
- if (merged.aspectRatio && c.aspectRatios && !c.aspectRatios.includes(merged.aspectRatio)) {
1203
- const to = nearestAspectRatio(merged.aspectRatio, c.aspectRatios);
1204
- warnings.push({
1205
- code: "ratio_coerced",
1206
- field: "aspectRatio",
1207
- from: merged.aspectRatio,
1208
- to,
1209
- message: `aspect ratio ${merged.aspectRatio} coerced to ${to} for ${model.id}`
1210
- });
1211
- merged.aspectRatio = to;
1212
- }
1259
+ const aspectRatio = resolveAspectRatio(
1260
+ merged.aspectRatio,
1261
+ c.aspectRatios,
1262
+ model,
1263
+ onUnsupported,
1264
+ warnings
1265
+ );
1266
+ if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
1213
1267
  const size = resolveTier(
1214
1268
  "size",
1215
1269
  "size_coerced",
@@ -1242,7 +1296,10 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1242
1296
  "quality",
1243
1297
  "count",
1244
1298
  "outputFormat",
1245
- "scaleFactor"
1299
+ "scaleFactor",
1300
+ "safetySettings",
1301
+ "temperature",
1302
+ "upscale"
1246
1303
  ]);
1247
1304
  return { request: merged, warnings };
1248
1305
  }
@@ -1272,17 +1329,14 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1272
1329
  delete merged.aspectRatio;
1273
1330
  }
1274
1331
  }
1275
- if (merged.aspectRatio && c.aspectRatios && !c.aspectRatios.includes(merged.aspectRatio)) {
1276
- const to = nearestAspectRatio(merged.aspectRatio, c.aspectRatios);
1277
- warnings.push({
1278
- code: "ratio_coerced",
1279
- field: "aspectRatio",
1280
- from: merged.aspectRatio,
1281
- to,
1282
- message: `aspect ratio ${merged.aspectRatio} coerced to ${to} for ${model.id}`
1283
- });
1284
- merged.aspectRatio = to;
1285
- }
1332
+ const aspectRatio = resolveAspectRatio(
1333
+ merged.aspectRatio,
1334
+ c.aspectRatios,
1335
+ model,
1336
+ onUnsupported,
1337
+ warnings
1338
+ );
1339
+ if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
1286
1340
  const resolution = resolveTier(
1287
1341
  "resolution",
1288
1342
  "resolution_coerced",
@@ -1294,7 +1348,13 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1294
1348
  warnings
1295
1349
  );
1296
1350
  if (resolution !== void 0) merged.resolution = resolution;
1297
- const duration = resolveDuration(merged.durationSeconds, c.durations, model, warnings);
1351
+ const duration = resolveDuration(
1352
+ merged.durationSeconds,
1353
+ c.durations,
1354
+ model,
1355
+ onUnsupported,
1356
+ warnings
1357
+ );
1298
1358
  if (duration !== void 0) merged.durationSeconds = duration;
1299
1359
  const limits = c.references ?? {};
1300
1360
  let images = trim(
@@ -1349,13 +1409,47 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1349
1409
  "cfgScale",
1350
1410
  "generateAudio",
1351
1411
  "promptExpansion",
1352
- "lastFrame"
1412
+ "lastFrame",
1413
+ "watermark",
1414
+ "safetyChecker"
1353
1415
  ]);
1354
1416
  return { request: merged, warnings };
1355
1417
  }
1418
+ function resolveAudioOptions(model, request, onUnsupported = "error") {
1419
+ const warnings = [];
1420
+ const c = model.constraints;
1421
+ const merged = { ...model.defaults, ...stripUndefined(request), model: model.id };
1422
+ for (const input of c.requires ?? []) {
1423
+ const present = input === "references" ? compact(merged.references).length > 0 : input === "sourceVideo" && merged.sourceVideo !== void 0;
1424
+ if (!present)
1425
+ throw new ModelKitError({
1426
+ code: "missing_input",
1427
+ message: `${model.id} requires ${input}`,
1428
+ model: model.id
1429
+ });
1430
+ }
1431
+ const duration = resolveDuration(
1432
+ merged.durationSeconds,
1433
+ c.durations,
1434
+ model,
1435
+ onUnsupported,
1436
+ warnings
1437
+ );
1438
+ if (duration !== void 0) merged.durationSeconds = duration;
1439
+ const references = trim(
1440
+ "references",
1441
+ compact(merged.references),
1442
+ c.references?.images ?? c.references?.total,
1443
+ model,
1444
+ warnings
1445
+ );
1446
+ if (references.length > 0) merged.references = references;
1447
+ else delete merged.references;
1448
+ dropUnsupported(merged, model, warnings, ["seed", "negativePrompt", "instrumental"]);
1449
+ return { request: merged, warnings };
1450
+ }
1356
1451
  function dropUnsupported(req, model, warnings, fields) {
1357
- const supports = model.constraints.supports;
1358
- if (!supports) return;
1452
+ const supports = model.constraints.supports ?? [];
1359
1453
  for (const field of fields) {
1360
1454
  if (req[field] !== void 0 && !supports.includes(field)) {
1361
1455
  warnings.push({
@@ -1395,6 +1489,9 @@ function hasAll(keys, names) {
1395
1489
  (n) => Boolean(keys[n]) && (n !== "higgsfield" || Boolean(keys.higgsfieldSecret))
1396
1490
  );
1397
1491
  }
1492
+ function missingEnterpriseKeys(config, names) {
1493
+ return names.filter((name) => !hasAll(config.keys.enterprise, [name]));
1494
+ }
1398
1495
  function resolveKeys(config, names, tier = "pro") {
1399
1496
  if (tier === "enterprise" && hasAll(config.keys.enterprise, names))
1400
1497
  return config.keys.enterprise;
@@ -1427,18 +1524,56 @@ function keysFromEnv(env, prefix = "") {
1427
1524
  set("prodia", pick("PRODIA_API_KEY", "PRODIA_TOKEN"));
1428
1525
  set("higgsfield", pick("HIGGSFIELD_API_KEY"));
1429
1526
  set("higgsfieldSecret", pick("HIGGSFIELD_API_SECRET"));
1430
- set("openrouter", pick("OPENROUTER_API_KEY"));
1431
1527
  set("cloudflare", pick("CLOUDFLARE_AI_GATEWAY_TOKEN", "CF_AIG_TOKEN"));
1432
1528
  return keys;
1433
1529
  }
1434
1530
 
1435
1531
  // src/retry.ts
1532
+ var import_zod2 = require("zod");
1533
+
1534
+ // src/json.ts
1436
1535
  var import_zod = require("zod");
1437
- var textBody = import_zod.z.string();
1536
+ var JsonValueSchema = import_zod.z.lazy(
1537
+ () => import_zod.z.union([
1538
+ import_zod.z.string(),
1539
+ import_zod.z.number(),
1540
+ import_zod.z.boolean(),
1541
+ import_zod.z.null(),
1542
+ import_zod.z.array(JsonValueSchema),
1543
+ import_zod.z.record(import_zod.z.string(), JsonValueSchema)
1544
+ ])
1545
+ );
1546
+ var JsonObjectSchema = import_zod.z.record(import_zod.z.string(), JsonValueSchema);
1547
+ async function readErrorBody(res) {
1548
+ const text2 = await res.text().catch(() => "");
1549
+ try {
1550
+ return JsonValueSchema.parse(JSON.parse(text2));
1551
+ } catch {
1552
+ return text2;
1553
+ }
1554
+ }
1555
+ var thrownErrorSchema = import_zod.z.object({
1556
+ message: import_zod.z.string().optional(),
1557
+ status: import_zod.z.number().optional(),
1558
+ statusCode: import_zod.z.number().optional(),
1559
+ responseBody: import_zod.z.string().optional()
1560
+ });
1561
+ function describeThrown(cause) {
1562
+ const parsed = thrownErrorSchema.safeParse(cause);
1563
+ if (!parsed.success) return { message: String(cause), status: void 0, body: String(cause) };
1564
+ const { message, status, statusCode, responseBody } = parsed.data;
1565
+ const body = responseBody ?? message ?? String(cause);
1566
+ return { message: message ?? String(cause), status: status ?? statusCode, body };
1567
+ }
1568
+
1569
+ // src/retry.ts
1570
+ var textBody = import_zod2.z.string();
1438
1571
  function classifyHttpError(status, body, provider, model) {
1439
1572
  const asText = textBody.safeParse(body);
1440
1573
  const text2 = asText.success ? asText.data : JSON.stringify(body);
1441
- const blocked = /content_policy|safety|blockReason|PROHIBITED_CONTENT|moderation/i.test(text2);
1574
+ const blocked = /content_policy_violation|blockReason|PROHIBITED_CONTENT|moderation_blocked|safety(?![_A-Za-z])/i.test(
1575
+ text2
1576
+ );
1442
1577
  let code = "provider_rejected";
1443
1578
  if (blocked) code = "content_blocked";
1444
1579
  else if (status === 401 || status === 403) code = "auth";
@@ -1459,8 +1594,8 @@ function classifyHttpError(status, body, provider, model) {
1459
1594
  }
1460
1595
  function shouldRetry(err) {
1461
1596
  if (err instanceof ModelKitError) return err.retryable;
1462
- if (err.name === "AbortError" || /ECONNRESET|ETIMEDOUT|fetch failed|socket hang up/i.test(err.message))
1463
- return true;
1597
+ if (err.name === "AbortError") return false;
1598
+ if (/ECONNRESET|ETIMEDOUT|fetch failed|socket hang up/i.test(err.message)) return true;
1464
1599
  return false;
1465
1600
  }
1466
1601
  async function withRetry(fn, opts = {}) {
@@ -1517,6 +1652,13 @@ function transportError(provider, cause, model) {
1517
1652
  if (model !== void 0) init.model = model;
1518
1653
  return new ModelKitError(init);
1519
1654
  }
1655
+ function classifyThrown(provider, cause, model, override = {}) {
1656
+ if (cause instanceof ModelKitError) return cause;
1657
+ const info = describeThrown(cause);
1658
+ const status = override.status ?? info.status;
1659
+ if (status === void 0) return transportError(provider, cause, model);
1660
+ return classifyHttpError(status, override.body ?? info.body, provider, model);
1661
+ }
1520
1662
  async function pollUntil(check, opts) {
1521
1663
  const start = Date.now();
1522
1664
  let interval = opts.intervalMs;
@@ -1632,12 +1774,18 @@ function createTextSteps(initial, resolve, callerProviderOptions) {
1632
1774
 
1633
1775
  // src/client.ts
1634
1776
  var import_ai2 = require("ai");
1635
- var import_zod3 = require("zod");
1777
+ var import_zod4 = require("zod");
1636
1778
 
1637
1779
  // src/adapters/media-ref.ts
1638
1780
  function isUrlRef(ref) {
1639
1781
  return "url" in ref;
1640
1782
  }
1783
+ function abortedError() {
1784
+ return new ModelKitError({ code: "timeout", message: "aborted", retryable: false });
1785
+ }
1786
+ function throwIfAborted(signal) {
1787
+ if (signal?.aborted) throw abortedError();
1788
+ }
1641
1789
  function mediaRefToBase64(bytes) {
1642
1790
  let binary = "";
1643
1791
  const CHUNK = 32768;
@@ -1652,10 +1800,34 @@ function base64ToBytes(b64) {
1652
1800
  for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
1653
1801
  return out;
1654
1802
  }
1655
- async function mediaRefToBytes(ref, fetch) {
1803
+ function mediaRefToDataUrl(ref) {
1804
+ if (isUrlRef(ref)) return ref.url;
1805
+ return `data:${ref.mime};base64,${mediaRefToBase64(ref.bytes)}`;
1806
+ }
1807
+ var refToUrl = mediaRefToDataUrl;
1808
+ async function readBody(res) {
1809
+ const text2 = await res.text().catch(() => "");
1810
+ try {
1811
+ const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
1812
+ return decoded.success ? decoded.data : text2;
1813
+ } catch {
1814
+ return text2;
1815
+ }
1816
+ }
1817
+ async function mediaRefToBytes(ref, fetch, options = {}) {
1656
1818
  if (!isUrlRef(ref)) return { bytes: ref.bytes, mime: ref.mime };
1657
- const res = await fetch(ref.url);
1658
- if (!res.ok) throw new Error(`reference fetch failed (${res.status}): ${ref.url}`);
1819
+ const init = {};
1820
+ if (options.signal !== void 0) init.signal = options.signal;
1821
+ const res = await fetch(ref.url, init);
1822
+ if (!res.ok) {
1823
+ const text2 = await res.text().catch(() => "");
1824
+ throw classifyHttpError(
1825
+ res.status,
1826
+ text2,
1827
+ options.provider ?? "media-reference",
1828
+ options.model
1829
+ );
1830
+ }
1659
1831
  const mime = res.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() || "image/png";
1660
1832
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
1661
1833
  }
@@ -1687,7 +1859,7 @@ var import_perplexity = require("@ai-sdk/perplexity");
1687
1859
  var import_xai = require("@ai-sdk/xai");
1688
1860
  var import_deepseek = require("@ai-sdk/deepseek");
1689
1861
  var import_moonshotai = require("@ai-sdk/moonshotai");
1690
- var import_zod2 = require("zod");
1862
+ var import_zod3 = require("zod");
1691
1863
  var TEXT_PROVIDERS = [
1692
1864
  "openai",
1693
1865
  "anthropic",
@@ -1698,7 +1870,7 @@ var TEXT_PROVIDERS = [
1698
1870
  "moonshot",
1699
1871
  "minimax"
1700
1872
  ];
1701
- var textProviderSchema = import_zod2.z.enum(TEXT_PROVIDERS);
1873
+ var textProviderSchema = import_zod3.z.enum(TEXT_PROVIDERS);
1702
1874
  function isTextProvider(value) {
1703
1875
  return textProviderSchema.safeParse(value).success;
1704
1876
  }
@@ -1707,22 +1879,12 @@ var OPENAI_COMPATIBLE_BASE_URLS = {
1707
1879
  };
1708
1880
  var GATEWAY_SLUGS = {
1709
1881
  openai: "openai",
1710
- anthropic: "anthropic",
1882
+ anthropic: "anthropic/v1",
1711
1883
  google: "google-ai-studio/v1beta",
1712
- xai: "grok",
1884
+ xai: "grok/v1",
1713
1885
  perplexity: "perplexity-ai",
1714
1886
  deepseek: "deepseek"
1715
1887
  };
1716
- var OPENROUTER_VENDORS = {
1717
- openai: "openai",
1718
- anthropic: "anthropic",
1719
- google: "google",
1720
- xai: "x-ai",
1721
- perplexity: "perplexity",
1722
- deepseek: "deepseek",
1723
- moonshot: "moonshotai",
1724
- minimax: "minimax"
1725
- };
1726
1888
  function detectTextProvider(modelId) {
1727
1889
  const id = modelId.toLowerCase();
1728
1890
  if (id.startsWith("claude") || id.includes("anthropic.")) return "anthropic";
@@ -1739,14 +1901,17 @@ function detectTextProvider(modelId) {
1739
1901
  message: `ai-sdk-text cannot detect a provider for "${modelId}". Set providerOptions.provider to one of ${TEXT_PROVIDERS.join(", ")}.`
1740
1902
  });
1741
1903
  }
1742
- function textKeyName(model, routing) {
1743
- if (routing.text === "openrouter") return "openrouter";
1904
+ function textKeyName(model, _routing) {
1744
1905
  return textProviderOf(model);
1745
1906
  }
1746
1907
  function textProviderOf(model) {
1747
1908
  const explicit = model.providerOptions?.["modelkit"]?.["provider"];
1748
1909
  return isTextProvider(explicit) ? explicit : detectTextProvider(model.providerModelId);
1749
1910
  }
1911
+ function textUsesCloudflareGateway(model, routing) {
1912
+ if (routing.text !== "cloudflare") return false;
1913
+ return GATEWAY_SLUGS[textProviderOf(model)] !== void 0;
1914
+ }
1750
1915
  function nativeClientFor(provider) {
1751
1916
  switch (provider) {
1752
1917
  case "openai":
@@ -1779,16 +1944,6 @@ function resolveTextRoute(ctx) {
1779
1944
  retryable: false
1780
1945
  });
1781
1946
  }
1782
- if (mode === "openrouter") {
1783
- const modelId = ctx.model.providerModelId.includes("/") ? ctx.model.providerModelId : `${OPENROUTER_VENDORS[provider]}/${ctx.model.providerModelId}`;
1784
- return {
1785
- provider,
1786
- client: "openai-compatible",
1787
- modelId,
1788
- keyName: "openrouter",
1789
- baseURL: "https://openrouter.ai/api/v1"
1790
- };
1791
- }
1792
1947
  const compatBase = OPENAI_COMPATIBLE_BASE_URLS[provider];
1793
1948
  const route2 = {
1794
1949
  provider,
@@ -1856,7 +2011,7 @@ var aiSdkTextAdapter = {
1856
2011
  case "openai-compatible":
1857
2012
  return (0, import_openai.createOpenAI)({
1858
2013
  ...settings,
1859
- name: route2.provider === "openai" ? "openai" : ctx.routing.text === "openrouter" ? "openrouter" : route2.provider
2014
+ name: route2.provider
1860
2015
  }).chat(route2.modelId);
1861
2016
  case "openai":
1862
2017
  return (0, import_openai.createOpenAI)(settings)(route2.modelId);
@@ -1869,8 +2024,9 @@ function requiredKeys(model, config) {
1869
2024
  if (model.adapter === "fal" && config.routing?.fal === "cloudflare-passthrough")
1870
2025
  return ["cloudflare"];
1871
2026
  if (model.adapter === "ai-sdk-text") {
1872
- const names = [textKeyName(model, config.routing ?? {})];
1873
- if (config.routing?.text === "cloudflare") names.push("cloudflare");
2027
+ const routing = config.routing ?? {};
2028
+ const names = [textKeyName(model, routing)];
2029
+ if (textUsesCloudflareGateway(model, routing)) names.push("cloudflare");
1874
2030
  return names;
1875
2031
  }
1876
2032
  return ADAPTER_KEYS[model.adapter];
@@ -1884,10 +2040,10 @@ function textAbortOptions(sdk, context) {
1884
2040
  if (abortSignal) return { abortSignal };
1885
2041
  return {};
1886
2042
  }
1887
- var catalogId = import_zod3.z.string();
1888
- var exactSize = import_zod3.z.templateLiteral([import_zod3.z.number(), "x", import_zod3.z.number()]);
1889
- var sdkRatio = import_zod3.z.templateLiteral([import_zod3.z.number(), ":", import_zod3.z.number()]);
1890
- var sizeTier = import_zod3.z.enum(["1K", "2K", "4K"]);
2043
+ var catalogId = import_zod4.z.string();
2044
+ var exactSize = import_zod4.z.templateLiteral([import_zod4.z.number(), "x", import_zod4.z.number()]);
2045
+ var sdkRatio = import_zod4.z.templateLiteral([import_zod4.z.number(), ":", import_zod4.z.number()]);
2046
+ var sizeTier = import_zod4.z.enum(["512", "1K", "2K", "4K"]);
1891
2047
  function isCatalogId(model) {
1892
2048
  return catalogId.safeParse(model).success;
1893
2049
  }
@@ -1944,6 +2100,15 @@ function pushIgnored(warnings, field, from, modelId) {
1944
2100
  message: `${modelId} provider instance does not support ${field}; ignored`
1945
2101
  });
1946
2102
  }
2103
+ function withoutModelkit(options) {
2104
+ if (!options) return {};
2105
+ const { modelkit: _modelkit, ...rest } = options;
2106
+ return rest;
2107
+ }
2108
+ function logCoercions(logger, modelId, warnings) {
2109
+ if (warnings.length === 0) return;
2110
+ logger.warn(`[modelkit] ${modelId} request coerced`, { model: modelId, warnings });
2111
+ }
1947
2112
  function createModelKit(config, adapters) {
1948
2113
  const catalog = new Catalog(MODELS).withOverrides(config.catalogOverride ?? []);
1949
2114
  const logger = config.logger ?? console;
@@ -1956,7 +2121,16 @@ function createModelKit(config, adapters) {
1956
2121
  message: `No adapter registered for ${model.adapter}`,
1957
2122
  model: model.id
1958
2123
  });
1959
- const keys = resolveKeys(config, requiredKeys(model, config), call.tier);
2124
+ const names = requiredKeys(model, config);
2125
+ if (call.tier === "enterprise") {
2126
+ const missing = missingEnterpriseKeys(config, names);
2127
+ if (missing.length > 0)
2128
+ logger.warn(
2129
+ `[modelkit] enterprise keys incomplete for ${model.adapter}; using pro`,
2130
+ { missing }
2131
+ );
2132
+ }
2133
+ const keys = resolveKeys(config, names, call.tier);
1960
2134
  const ctx = {
1961
2135
  model,
1962
2136
  keys,
@@ -1977,6 +2151,10 @@ function createModelKit(config, adapters) {
1977
2151
  logger.warn("[modelkit] usage emit failed", err);
1978
2152
  }
1979
2153
  }
2154
+ function withRowUnit(event) {
2155
+ const row = catalog.get(event.model);
2156
+ return row ? { ...event, unit: row.usage.unit } : event;
2157
+ }
1980
2158
  function textTarget(model, call) {
1981
2159
  if (!isCatalogId(model)) {
1982
2160
  return {
@@ -2045,6 +2223,7 @@ function createModelKit(config, adapters) {
2045
2223
  pushIgnored(warnings, "outputFormat", _outputFormat, model.modelId);
2046
2224
  if (scaleFactor !== void 0)
2047
2225
  pushIgnored(warnings, "scaleFactor", scaleFactor, model.modelId);
2226
+ logCoercions(logger, model.modelId, warnings);
2048
2227
  const abortSignal = combineSignals(req.abortSignal, call.signal);
2049
2228
  if (abortSignal) options.abortSignal = abortSignal;
2050
2229
  const result = await (0, import_ai2.generateImage)(options);
@@ -2128,6 +2307,7 @@ function createModelKit(config, adapters) {
2128
2307
  if (_mode !== void 0) pushIgnored(warnings, "mode", _mode, model.modelId);
2129
2308
  if (providerOptions) options.providerOptions = providerOptions;
2130
2309
  if (abortSignal) options.abortSignal = abortSignal;
2310
+ logCoercions(logger, model.modelId, warnings);
2131
2311
  const pending = (0, import_ai2.experimental_generateVideo)(options);
2132
2312
  let settled;
2133
2313
  let failed = false;
@@ -2186,6 +2366,7 @@ function createModelKit(config, adapters) {
2186
2366
  call.onUnsupported ?? "error"
2187
2367
  );
2188
2368
  warnings.push(...inputWarnings);
2369
+ logCoercions(logger, model.id, warnings);
2189
2370
  const signal = combineSignals(req.abortSignal, call.signal);
2190
2371
  const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
2191
2372
  if (!adapter.generateImage)
@@ -2195,7 +2376,7 @@ function createModelKit(config, adapters) {
2195
2376
  model: model.id
2196
2377
  });
2197
2378
  const result = await adapter.generateImage(request, ctx);
2198
- const usage2 = { ...result.usage, model: model.id };
2379
+ const usage2 = { ...result.usage, model: model.id, unit: model.usage.unit };
2199
2380
  await emit(usage2);
2200
2381
  const images = result.images.map(toGeneratedImage);
2201
2382
  const first = images[0];
@@ -2228,6 +2409,7 @@ function createModelKit(config, adapters) {
2228
2409
  call.onUnsupported ?? "error"
2229
2410
  );
2230
2411
  warnings.push(...inputWarnings);
2412
+ logCoercions(logger, model.id, warnings);
2231
2413
  const signal = combineSignals(req.abortSignal, call.signal);
2232
2414
  const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
2233
2415
  if (!adapter.generateVideo)
@@ -2245,7 +2427,11 @@ function createModelKit(config, adapters) {
2245
2427
  result(opts) {
2246
2428
  if (settled) return settled;
2247
2429
  const pending = result(opts).then(
2248
- (out) => emit({ ...out.usage, model: model.id }).then(() => out),
2430
+ (out) => emit({
2431
+ ...out.usage,
2432
+ model: model.id,
2433
+ unit: model.usage.unit
2434
+ }).then(() => out),
2249
2435
  (cause) => {
2250
2436
  settled = void 0;
2251
2437
  throw cause;
@@ -2265,7 +2451,15 @@ function createModelKit(config, adapters) {
2265
2451
  model: model.id
2266
2452
  });
2267
2453
  }
2268
- const { adapter, ctx } = context(model, call, []);
2454
+ const { abortSignal: _signal, ...request } = req;
2455
+ const { request: resolved, warnings } = resolveAudioOptions(
2456
+ model,
2457
+ request,
2458
+ call.onUnsupported ?? "error"
2459
+ );
2460
+ logCoercions(logger, model.id, warnings);
2461
+ const signal = combineSignals(req.abortSignal, call.signal);
2462
+ const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
2269
2463
  if (!adapter.generateAudio) {
2270
2464
  throw new ModelKitError({
2271
2465
  code: "unsupported_option",
@@ -2273,11 +2467,8 @@ function createModelKit(config, adapters) {
2273
2467
  model: model.id
2274
2468
  });
2275
2469
  }
2276
- const result = await adapter.generateAudio(
2277
- { ...model.defaults, ...req, model: model.id },
2278
- ctx
2279
- );
2280
- await emit({ ...result.usage, model: model.id });
2470
+ const result = await adapter.generateAudio(resolved, ctx);
2471
+ await emit({ ...result.usage, model: model.id, unit: model.usage.unit });
2281
2472
  return result;
2282
2473
  },
2283
2474
  async prepareMediaPart(modelId, ref, mediaType, call = {}) {
@@ -2297,7 +2488,7 @@ function createModelKit(config, adapters) {
2297
2488
  });
2298
2489
  return {
2299
2490
  model,
2300
- providerOptions: entry.providerOptions ?? {},
2491
+ providerOptions: withoutModelkit(entry.providerOptions),
2301
2492
  catalog: entry
2302
2493
  };
2303
2494
  },
@@ -2317,7 +2508,7 @@ function createModelKit(config, adapters) {
2317
2508
  options.prepareStep ?? options.experimental_prepareStep
2318
2509
  )
2319
2510
  });
2320
- for (const event of steps.usage(result.steps)) await emit(event);
2511
+ for (const event of steps.usage(result.steps)) await emit(withRowUnit(event));
2321
2512
  return result;
2322
2513
  },
2323
2514
  streamText(options, call = {}) {
@@ -2333,9 +2524,11 @@ function createModelKit(config, adapters) {
2333
2524
  ...textAbortOptions(options.abortSignal, call.signal),
2334
2525
  model: target.languageModel,
2335
2526
  providerOptions: {},
2336
- prepareStep: steps.wrap(options.prepareStep),
2527
+ prepareStep: steps.wrap(
2528
+ options.prepareStep ?? options.experimental_prepareStep
2529
+ ),
2337
2530
  onFinish: async (event) => {
2338
- for (const usage2 of steps.usage(event.steps)) await emit(usage2);
2531
+ for (const usage2 of steps.usage(event.steps)) await emit(withRowUnit(usage2));
2339
2532
  await callerOnFinish?.(event);
2340
2533
  }
2341
2534
  });
@@ -2349,7 +2542,7 @@ function createModelKit(config, adapters) {
2349
2542
  providerOptions: mergeTextProviderOptions(entry, options.providerOptions)
2350
2543
  };
2351
2544
  const result = await (0, import_ai2.generateObject)(sdkOptions);
2352
- await emit(textUsageEvent(entry, provider, result.usage));
2545
+ await emit(withRowUnit(textUsageEvent(entry, provider, result.usage)));
2353
2546
  return result;
2354
2547
  },
2355
2548
  streamObject(options, call = {}) {
@@ -2361,7 +2554,7 @@ function createModelKit(config, adapters) {
2361
2554
  model: languageModel2,
2362
2555
  providerOptions: mergeTextProviderOptions(entry, options.providerOptions),
2363
2556
  onFinish: async (event) => {
2364
- await emit(textUsageEvent(entry, provider, event.usage));
2557
+ await emit(withRowUnit(textUsageEvent(entry, provider, event.usage)));
2365
2558
  await callerOnFinish?.(event);
2366
2559
  }
2367
2560
  };
@@ -2371,7 +2564,7 @@ function createModelKit(config, adapters) {
2371
2564
  }
2372
2565
 
2373
2566
  // src/adapters/ark.ts
2374
- var import_zod4 = require("zod");
2567
+ var import_zod5 = require("zod");
2375
2568
  var ARK_BASE_URL = "https://ark.ap-southeast.bytepluses.com/api/v3";
2376
2569
  var SUBMIT_PATH = "/contents/generations/tasks";
2377
2570
  var SEEDANCE_POLL_INTERVAL_MS = 15e3;
@@ -2390,6 +2583,7 @@ var SEEDREAM_SIZES = {
2390
2583
  "3:2": "1248x832",
2391
2584
  "2:3": "832x1248",
2392
2585
  "21:9": "1568x672",
2586
+ "9:21": "672x1568",
2393
2587
  "4:5": "864x1080",
2394
2588
  "5:4": "1080x864"
2395
2589
  },
@@ -2402,16 +2596,34 @@ var SEEDREAM_SIZES = {
2402
2596
  "3:2": "2496x1664",
2403
2597
  "2:3": "1664x2496",
2404
2598
  "21:9": "3136x1344",
2599
+ "9:21": "1344x3136",
2405
2600
  "4:5": "1728x2160",
2406
2601
  "5:4": "2160x1728"
2602
+ },
2603
+ // 4K keeps the long edge at Ark's 4096 cap (a doubled 2K entry would exceed
2604
+ // it); sides are multiples of 16.
2605
+ "4K": {
2606
+ "1:1": "4096x4096",
2607
+ "4:3": "4096x3072",
2608
+ "3:4": "3072x4096",
2609
+ "16:9": "4096x2304",
2610
+ "9:16": "2304x4096",
2611
+ "3:2": "4096x2736",
2612
+ "2:3": "2736x4096",
2613
+ "21:9": "4096x1760",
2614
+ "9:21": "1760x4096",
2615
+ "4:5": "3280x4096",
2616
+ "5:4": "4096x3280"
2407
2617
  }
2408
2618
  };
2409
2619
  var arkAdapter = {
2410
2620
  id: "ark",
2411
2621
  async generateImage(req, ctx) {
2622
+ throwIfAborted(ctx.signal);
2412
2623
  return generateSeedreamImage(req, ctx);
2413
2624
  },
2414
2625
  async generateVideo(req, ctx) {
2626
+ throwIfAborted(ctx.signal);
2415
2627
  return generateSeedanceVideo(req, ctx);
2416
2628
  }
2417
2629
  };
@@ -2429,9 +2641,9 @@ function arkKey(ctx) {
2429
2641
  return key;
2430
2642
  }
2431
2643
  function throwArkError(status, body, model) {
2432
- const parsed = import_zod4.z.string().safeParse(body);
2644
+ const parsed = import_zod5.z.string().safeParse(body);
2433
2645
  const text2 = parsed.success ? parsed.data : JSON.stringify(body ?? "");
2434
- if (/sensitive.?content|inputimage|human.?face/i.test(text2)) {
2646
+ if (/sensitive.?content|human.?face/i.test(text2)) {
2435
2647
  const init = {
2436
2648
  code: "content_blocked",
2437
2649
  message: `ark rejected the request: ${text2.slice(0, 500)}`,
@@ -2443,10 +2655,20 @@ function throwArkError(status, body, model) {
2443
2655
  if (status !== void 0) init.status = status;
2444
2656
  throw new ModelKitError(init);
2445
2657
  }
2658
+ if (status === void 0) {
2659
+ throw new ModelKitError({
2660
+ code: "provider_rejected",
2661
+ message: `ark rejected the request: ${text2.slice(0, 500)}`,
2662
+ provider: "ark",
2663
+ model,
2664
+ body,
2665
+ retryable: false
2666
+ });
2667
+ }
2446
2668
  throw classifyHttpError(status, body, "ark", model);
2447
2669
  }
2448
2670
  function seedreamSize(aspect, size) {
2449
- const tier = size === "1K" ? "1K" : "2K";
2671
+ const tier = size === "4K" || size === "1K" ? size : "2K";
2450
2672
  const table = SEEDREAM_SIZES[tier];
2451
2673
  return table[aspect ?? ""] ?? table["1:1"] ?? "2048x2048";
2452
2674
  }
@@ -2457,17 +2679,13 @@ function sniffedImageMime(bytes) {
2457
2679
  return "image/webp";
2458
2680
  return "image/jpeg";
2459
2681
  }
2460
- function arkUrl(ref) {
2461
- if ("url" in ref) return ref.url;
2462
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
2463
- }
2464
- var SeedreamImageSchema = import_zod4.z.object({
2465
- url: import_zod4.z.string().optional(),
2466
- b64_json: import_zod4.z.string().optional()
2682
+ var SeedreamImageSchema = import_zod5.z.object({
2683
+ url: import_zod5.z.string().optional(),
2684
+ b64_json: import_zod5.z.string().optional()
2467
2685
  });
2468
- var SeedreamResponseSchema = import_zod4.z.object({
2469
- data: import_zod4.z.array(SeedreamImageSchema).optional(),
2470
- error: import_zod4.z.object({ message: import_zod4.z.string().optional(), code: import_zod4.z.string().optional() }).optional()
2686
+ var SeedreamResponseSchema = import_zod5.z.object({
2687
+ data: import_zod5.z.array(SeedreamImageSchema).optional(),
2688
+ error: import_zod5.z.object({ message: import_zod5.z.string().optional(), code: import_zod5.z.string().optional() }).optional()
2471
2689
  });
2472
2690
  function retryOptsFor(ctx, attempts = 3) {
2473
2691
  const opts = { attempts };
@@ -2478,11 +2696,16 @@ function fetchInitFor(ctx, init) {
2478
2696
  if (ctx.signal !== void 0) init.signal = ctx.signal;
2479
2697
  return init;
2480
2698
  }
2699
+ function arkFetch(ctx, url, init) {
2700
+ return ctx.fetch(url, fetchInitFor(ctx, init)).catch((cause) => {
2701
+ throw classifyThrown("ark", cause, ctx.model.id);
2702
+ });
2703
+ }
2481
2704
  async function generateSeedreamImage(req, ctx) {
2482
2705
  const key = arkKey(ctx);
2483
2706
  const modelId = ctx.model.id;
2484
2707
  const size = seedreamSize(req.aspectRatio, req.size);
2485
- const refs = (req.references ?? []).map(arkUrl);
2708
+ const refs = (req.references ?? []).map(refToUrl);
2486
2709
  const body = {
2487
2710
  model: ctx.model.providerModelId,
2488
2711
  prompt: req.prompt,
@@ -2495,14 +2718,11 @@ async function generateSeedreamImage(req, ctx) {
2495
2718
  if (refs.length === 1 && firstRef !== void 0) body.image = firstRef;
2496
2719
  else if (refs.length > 1) body.image = refs;
2497
2720
  const data = await withRetry(async () => {
2498
- const res = await ctx.fetch(
2499
- `${arkBaseUrl(ctx)}/images/generations`,
2500
- fetchInitFor(ctx, {
2501
- method: "POST",
2502
- headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2503
- body: JSON.stringify(body)
2504
- })
2505
- );
2721
+ const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}/images/generations`, {
2722
+ method: "POST",
2723
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2724
+ body: JSON.stringify(body)
2725
+ });
2506
2726
  if (!res.ok) {
2507
2727
  const text2 = await res.text().catch(() => "");
2508
2728
  throwArkError(res.status, text2, modelId);
@@ -2517,7 +2737,7 @@ async function generateSeedreamImage(req, ctx) {
2517
2737
  bytes = Buffer.from(first.b64_json, "base64");
2518
2738
  } else if (first?.url) {
2519
2739
  const imageUrl = first.url;
2520
- const res = await ctx.fetch(imageUrl, fetchInitFor(ctx, {}));
2740
+ const res = await arkFetch(ctx, imageUrl, {});
2521
2741
  if (!res.ok) {
2522
2742
  const text2 = await res.text().catch(() => "");
2523
2743
  throwArkError(res.status, text2, modelId);
@@ -2537,13 +2757,21 @@ async function generateSeedreamImage(req, ctx) {
2537
2757
  if (req.quality !== void 0) usage2.imageQuality = req.quality;
2538
2758
  return { images: [{ bytes, mime: sniffedImageMime(bytes) }], usage: usage2, raw: data };
2539
2759
  }
2540
- var SeedanceResponseSchema = import_zod4.z.object({
2541
- id: import_zod4.z.string().optional(),
2542
- status: import_zod4.z.string().optional(),
2543
- content: import_zod4.z.object({ video_url: import_zod4.z.string().optional() }).optional(),
2544
- error: import_zod4.z.union([import_zod4.z.string(), import_zod4.z.object({ message: import_zod4.z.string().optional() })]).optional()
2760
+ var SeedanceUsageSchema = import_zod5.z.object({
2761
+ total_tokens: import_zod5.z.number().optional(),
2762
+ completion_tokens: import_zod5.z.number().optional()
2545
2763
  });
2546
- var SeedanceErrorMessageSchema = import_zod4.z.object({ message: import_zod4.z.string().optional() });
2764
+ var SeedanceResponseSchema = import_zod5.z.object({
2765
+ id: import_zod5.z.string().optional(),
2766
+ status: import_zod5.z.string().optional(),
2767
+ content: import_zod5.z.object({ video_url: import_zod5.z.string().optional() }).optional(),
2768
+ usage: SeedanceUsageSchema.optional(),
2769
+ duration: import_zod5.z.number().optional(),
2770
+ framespersecond: import_zod5.z.number().optional(),
2771
+ resolution: import_zod5.z.string().optional(),
2772
+ error: import_zod5.z.union([import_zod5.z.string(), import_zod5.z.object({ message: import_zod5.z.string().optional() })]).optional()
2773
+ });
2774
+ var SeedanceErrorMessageSchema = import_zod5.z.object({ message: import_zod5.z.string().optional() });
2547
2775
  function seedanceUsage(ctx, req, duration) {
2548
2776
  const usage2 = {
2549
2777
  model: ctx.model.id,
@@ -2557,7 +2785,7 @@ function seedanceUsage(ctx, req, duration) {
2557
2785
  return usage2;
2558
2786
  }
2559
2787
  function seedanceErrorMessage(error, status) {
2560
- const asString = import_zod4.z.string().safeParse(error);
2788
+ const asString = import_zod5.z.string().safeParse(error);
2561
2789
  if (asString.success) return asString.data;
2562
2790
  const asObject = SeedanceErrorMessageSchema.safeParse(error);
2563
2791
  if (asObject.success && asObject.data.message !== void 0) return asObject.data.message;
@@ -2572,42 +2800,42 @@ async function generateSeedanceVideo(req, ctx) {
2572
2800
  if (req.firstFrame) {
2573
2801
  content.push({
2574
2802
  type: "image_url",
2575
- image_url: { url: arkUrl(req.firstFrame) },
2803
+ image_url: { url: refToUrl(req.firstFrame) },
2576
2804
  role: "first_frame"
2577
2805
  });
2578
2806
  }
2579
2807
  if (req.lastFrame) {
2580
2808
  content.push({
2581
2809
  type: "image_url",
2582
- image_url: { url: arkUrl(req.lastFrame) },
2810
+ image_url: { url: refToUrl(req.lastFrame) },
2583
2811
  role: "last_frame"
2584
2812
  });
2585
2813
  }
2586
2814
  for (const ref of req.referenceImages ?? []) {
2587
2815
  content.push({
2588
2816
  type: "image_url",
2589
- image_url: { url: arkUrl(ref) },
2817
+ image_url: { url: refToUrl(ref) },
2590
2818
  role: "reference_image"
2591
2819
  });
2592
2820
  }
2593
2821
  if (req.sourceVideo) {
2594
2822
  content.push({
2595
2823
  type: "video_url",
2596
- video_url: { url: arkUrl(req.sourceVideo) },
2824
+ video_url: { url: refToUrl(req.sourceVideo) },
2597
2825
  role: "reference_video"
2598
2826
  });
2599
2827
  }
2600
2828
  for (const ref of req.referenceVideos ?? []) {
2601
2829
  content.push({
2602
2830
  type: "video_url",
2603
- video_url: { url: arkUrl(ref) },
2831
+ video_url: { url: refToUrl(ref) },
2604
2832
  role: "reference_video"
2605
2833
  });
2606
2834
  }
2607
2835
  for (const ref of req.referenceAudios ?? []) {
2608
2836
  content.push({
2609
2837
  type: "audio_url",
2610
- audio_url: { url: arkUrl(ref) },
2838
+ audio_url: { url: refToUrl(ref) },
2611
2839
  role: "reference_audio"
2612
2840
  });
2613
2841
  }
@@ -2621,15 +2849,13 @@ async function generateSeedanceVideo(req, ctx) {
2621
2849
  if (!hasFrameAnchor && req.aspectRatio !== void 0) body.ratio = req.aspectRatio;
2622
2850
  if (req.generateAudio !== void 0) body.generate_audio = req.generateAudio;
2623
2851
  if (req.seed !== void 0) body.seed = req.seed;
2852
+ if (req.watermark !== void 0) body.watermark = req.watermark;
2624
2853
  const submit = await withRetry(async () => {
2625
- const res = await ctx.fetch(
2626
- `${arkBaseUrl(ctx)}${SUBMIT_PATH}`,
2627
- fetchInitFor(ctx, {
2628
- method: "POST",
2629
- headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2630
- body: JSON.stringify(body)
2631
- })
2632
- );
2854
+ const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}`, {
2855
+ method: "POST",
2856
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2857
+ body: JSON.stringify(body)
2858
+ });
2633
2859
  if (!res.ok) {
2634
2860
  const text2 = await res.text().catch(() => "");
2635
2861
  throwArkError(res.status, text2, modelId);
@@ -2642,12 +2868,9 @@ async function generateSeedanceVideo(req, ctx) {
2642
2868
  const jobId = submit.id;
2643
2869
  const usage2 = seedanceUsage(ctx, req, duration);
2644
2870
  const fetchStatus = async () => {
2645
- const res = await ctx.fetch(
2646
- `${arkBaseUrl(ctx)}${SUBMIT_PATH}/${jobId}`,
2647
- fetchInitFor(ctx, {
2648
- headers: { Authorization: `Bearer ${key}` }
2649
- })
2650
- );
2871
+ const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}/${jobId}`, {
2872
+ headers: { Authorization: `Bearer ${key}` }
2873
+ });
2651
2874
  if (!res.ok) {
2652
2875
  if (res.status === 429 || res.status >= 500) return void 0;
2653
2876
  const text2 = await res.text().catch(() => "");
@@ -2690,6 +2913,11 @@ async function generateSeedanceVideo(req, ctx) {
2690
2913
  "Seedance task succeeded but returned no video_url",
2691
2914
  modelId
2692
2915
  );
2916
+ const tokens = polled.usage;
2917
+ if (tokens?.total_tokens !== void 0 && tokens.completion_tokens !== void 0) {
2918
+ usage2.inputTokens = tokens.total_tokens - tokens.completion_tokens;
2919
+ usage2.outputTokens = tokens.completion_tokens;
2920
+ }
2693
2921
  return {
2694
2922
  video: { url, expiresAt: new Date(Date.now() + VIDEO_URL_TTL_MS) },
2695
2923
  usage: usage2,
@@ -2708,43 +2936,6 @@ async function generateSeedanceVideo(req, ctx) {
2708
2936
  // src/adapters/fal.ts
2709
2937
  var import_zod6 = require("zod");
2710
2938
  var import_client = require("@fal-ai/client");
2711
-
2712
- // src/json.ts
2713
- var import_zod5 = require("zod");
2714
- var JsonValueSchema = import_zod5.z.lazy(
2715
- () => import_zod5.z.union([
2716
- import_zod5.z.string(),
2717
- import_zod5.z.number(),
2718
- import_zod5.z.boolean(),
2719
- import_zod5.z.null(),
2720
- import_zod5.z.array(JsonValueSchema),
2721
- import_zod5.z.record(import_zod5.z.string(), JsonValueSchema)
2722
- ])
2723
- );
2724
- var JsonObjectSchema = import_zod5.z.record(import_zod5.z.string(), JsonValueSchema);
2725
- async function readErrorBody(res) {
2726
- const text2 = await res.text().catch(() => "");
2727
- try {
2728
- return JsonValueSchema.parse(JSON.parse(text2));
2729
- } catch {
2730
- return text2;
2731
- }
2732
- }
2733
- var thrownErrorSchema = import_zod5.z.object({
2734
- message: import_zod5.z.string().optional(),
2735
- status: import_zod5.z.number().optional(),
2736
- statusCode: import_zod5.z.number().optional(),
2737
- responseBody: import_zod5.z.string().optional()
2738
- });
2739
- function describeThrown(cause) {
2740
- const parsed = thrownErrorSchema.safeParse(cause);
2741
- if (!parsed.success) return { message: String(cause), status: void 0, body: String(cause) };
2742
- const { message, status, statusCode, responseBody } = parsed.data;
2743
- const body = responseBody ?? message ?? String(cause);
2744
- return { message: message ?? String(cause), status: status ?? statusCode, body };
2745
- }
2746
-
2747
- // src/adapters/fal.ts
2748
2939
  var MUSE_T2I_ENDPOINT = "meta/muse-image/text-to-image";
2749
2940
  var MUSE_EDIT_ENDPOINT = "meta/muse-image/edit";
2750
2941
  var KLING_O1_ENDPOINT = "fal-ai/kling-video/o1/video-to-video/reference";
@@ -2756,10 +2947,33 @@ function defaultCreateFalClient(opts) {
2756
2947
  const client2 = (0, import_client.createFalClient)(opts);
2757
2948
  return {
2758
2949
  async subscribe(endpoint, options) {
2759
- const outcome = await client2.subscribe(endpoint, { input: options.input });
2950
+ const outcome = await client2.subscribe(endpoint, options);
2760
2951
  const unwrapped = falSubscribePayloadSchema.safeParse(outcome);
2761
2952
  if (unwrapped.success) return unwrapped.data.data;
2762
2953
  return JsonValueSchema.parse(outcome);
2954
+ },
2955
+ queue: {
2956
+ async submit(endpoint, options) {
2957
+ const submitted = await client2.queue.submit(endpoint, {
2958
+ input: options.input,
2959
+ ...options.abortSignal !== void 0 ? { abortSignal: options.abortSignal } : {}
2960
+ });
2961
+ return { requestId: submitted.request_id };
2962
+ },
2963
+ async status(endpoint, options) {
2964
+ const current = await client2.queue.status(endpoint, {
2965
+ requestId: options.requestId,
2966
+ ...options.abortSignal !== void 0 ? { abortSignal: options.abortSignal } : {}
2967
+ });
2968
+ return { status: current.status };
2969
+ },
2970
+ async result(endpoint, options) {
2971
+ const outcome = await client2.queue.result(endpoint, {
2972
+ requestId: options.requestId,
2973
+ ...options.abortSignal !== void 0 ? { abortSignal: options.abortSignal } : {}
2974
+ });
2975
+ return JsonValueSchema.parse(outcome.data);
2976
+ }
2763
2977
  }
2764
2978
  };
2765
2979
  }
@@ -2767,24 +2981,17 @@ var defaultDeps = { createFalClient: defaultCreateFalClient };
2767
2981
  function createFalAdapter(deps = defaultDeps) {
2768
2982
  return {
2769
2983
  id: "fal",
2770
- generateImage: (req, ctx) => generateMuseImage(req, ctx, deps),
2771
- generateVideo: (req, ctx) => generateFalVideo(req, ctx, deps)
2984
+ generateImage: (req, ctx) => {
2985
+ throwIfAborted(ctx.signal);
2986
+ return generateMuseImage(req, ctx, deps);
2987
+ },
2988
+ generateVideo: (req, ctx) => {
2989
+ throwIfAborted(ctx.signal);
2990
+ return generateFalVideo(req, ctx, deps);
2991
+ }
2772
2992
  };
2773
2993
  }
2774
2994
  var falAdapter = createFalAdapter();
2775
- function refToUrl(ref) {
2776
- if ("url" in ref) return ref.url;
2777
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
2778
- }
2779
- async function readBody(res) {
2780
- const text2 = await res.text().catch(() => "");
2781
- try {
2782
- const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
2783
- return decoded.success ? decoded.data : text2;
2784
- } catch {
2785
- return text2;
2786
- }
2787
- }
2788
2995
  var falDetailSchema = import_zod6.z.object({ detail: JsonValueSchema });
2789
2996
  function liftDetail(body) {
2790
2997
  const parsed = falDetailSchema.safeParse(body);
@@ -2801,15 +3008,17 @@ var falSdkThrownSchema = import_zod6.z.object({
2801
3008
  message: import_zod6.z.string().optional()
2802
3009
  });
2803
3010
  function falSdkError(cause, model) {
2804
- if (isModelKitError(cause)) return cause;
2805
3011
  const parsed = falSdkThrownSchema.safeParse(cause);
2806
3012
  if (parsed.success) {
2807
3013
  const status = parsed.data.status ?? parsed.data.statusCode;
2808
3014
  const body = parsed.data.body ?? parsed.data.message ?? String(cause);
2809
- return falError(status, body, model);
3015
+ return classifyThrown("fal", cause, model, { status, body: liftDetail(body) });
2810
3016
  }
2811
3017
  const info = describeThrown(cause);
2812
- return falError(info.status, info.body, model);
3018
+ return classifyThrown("fal", cause, model, {
3019
+ status: info.status,
3020
+ body: liftDetail(info.body)
3021
+ });
2813
3022
  }
2814
3023
  function gatewayConfig(ctx) {
2815
3024
  const cf = ctx.routing.cloudflare;
@@ -2891,7 +3100,7 @@ async function passthroughRequest(ctx, endpoint, input) {
2891
3100
  }
2892
3101
  return JsonValueSchema.parse(await resultRes.json());
2893
3102
  }
2894
- async function subscribe(ctx, deps, endpoint, input) {
3103
+ function falClientOf(ctx, deps) {
2895
3104
  const falKey = ctx.keys.fal;
2896
3105
  if (!falKey) {
2897
3106
  throw new ModelKitError({
@@ -2902,12 +3111,19 @@ async function subscribe(ctx, deps, endpoint, input) {
2902
3111
  retryable: false
2903
3112
  });
2904
3113
  }
2905
- const client2 = deps.createFalClient({ credentials: falKey });
3114
+ return deps.createFalClient({ credentials: falKey, fetch: ctx.fetch });
3115
+ }
3116
+ async function subscribe(ctx, deps, endpoint, input) {
3117
+ const client2 = falClientOf(ctx, deps);
3118
+ const options = {
3119
+ input,
3120
+ ...ctx.signal !== void 0 ? { abortSignal: ctx.signal } : {}
3121
+ };
2906
3122
  try {
2907
3123
  return await withRetry(
2908
3124
  async () => {
2909
3125
  try {
2910
- return await client2.subscribe(endpoint, { input });
3126
+ return await client2.subscribe(endpoint, options);
2911
3127
  } catch (cause) {
2912
3128
  throw falSdkError(cause, ctx.model.id);
2913
3129
  }
@@ -3012,8 +3228,12 @@ async function generateMuseImage(req, ctx, deps) {
3012
3228
  raw: payload
3013
3229
  };
3014
3230
  }
3231
+ var H3_MAX_MODEL_IDS = /* @__PURE__ */ new Set([
3232
+ "minimax/h3-max",
3233
+ "minimax/h3-max/reference-to-video"
3234
+ ]);
3015
3235
  function minimaxFamily(providerModelId) {
3016
- return providerModelId.toLowerCase().includes("h3-max") ? "h3-max" : "h3";
3236
+ return H3_MAX_MODEL_IDS.has(providerModelId.toLowerCase()) ? "h3-max" : "h3";
3017
3237
  }
3018
3238
  function minimaxBase(req, family) {
3019
3239
  const resolution = (family === "h3-max" ? req.resolution ?? "768p" : req.resolution ?? "2k").toUpperCase();
@@ -3021,14 +3241,17 @@ function minimaxBase(req, family) {
3021
3241
  prompt: req.prompt.trim(),
3022
3242
  duration: req.durationSeconds ?? 5,
3023
3243
  resolution,
3024
- prompt_expansion_mode: req.promptExpansion ?? "balanced",
3025
- enable_safety_checker: true
3244
+ prompt_expansion_mode: req.promptExpansion ?? "balanced"
3026
3245
  };
3246
+ if (req.safetyChecker !== void 0) input.enable_safety_checker = req.safetyChecker;
3027
3247
  if (req.seed !== void 0) input.seed = req.seed;
3028
3248
  return input;
3029
3249
  }
3250
+ var KLING_MODEL_IDS = /* @__PURE__ */ new Set([
3251
+ "fal-ai/kling-video/o1/video-to-video/reference"
3252
+ ]);
3030
3253
  function isKling(providerModelId) {
3031
- return providerModelId.toLowerCase().includes("kling");
3254
+ return KLING_MODEL_IDS.has(providerModelId.toLowerCase());
3032
3255
  }
3033
3256
  function klingO1Input(req) {
3034
3257
  const source = req.sourceVideo ? refToUrl(req.sourceVideo) : void 0;
@@ -3041,13 +3264,14 @@ function klingO1Input(req) {
3041
3264
  });
3042
3265
  }
3043
3266
  const duration = Math.min(10, Math.max(3, Math.round(req.durationSeconds ?? 5)));
3044
- const ratio = req.aspectRatio === "16:9" || req.aspectRatio === "9:16" || req.aspectRatio === "1:1" ? req.aspectRatio : "auto";
3045
3267
  const input = {
3046
3268
  prompt: req.prompt,
3047
3269
  video_url: source,
3048
- duration: String(duration),
3049
- aspect_ratio: ratio
3270
+ duration: String(duration)
3050
3271
  };
3272
+ if (req.aspectRatio !== "adaptive") {
3273
+ input.aspect_ratio = req.aspectRatio === "16:9" || req.aspectRatio === "9:16" || req.aspectRatio === "1:1" ? req.aspectRatio : "auto";
3274
+ }
3051
3275
  const refs = (req.referenceImages ?? []).map(refToUrl);
3052
3276
  if (refs.length > 0) input.image_urls = refs;
3053
3277
  return input;
@@ -3076,13 +3300,7 @@ async function generateFalVideo(req, ctx, deps) {
3076
3300
  if (ctx.routing.fal === "cloudflare-passthrough") {
3077
3301
  return await passthroughVideoJob(ctx, req, KLING_O1_ENDPOINT, input);
3078
3302
  }
3079
- const result2 = await runJob(ctx, deps, KLING_O1_ENDPOINT, input);
3080
- return completedVideoJob(
3081
- ctx,
3082
- req,
3083
- extractVideoUrl(result2, KLING_O1_ENDPOINT, ctx.model.id),
3084
- result2
3085
- );
3303
+ return await submitQueueJob(ctx, req, deps, KLING_O1_ENDPOINT, input);
3086
3304
  }
3087
3305
  const family = minimaxFamily(providerModelId);
3088
3306
  const refs = {
@@ -3106,24 +3324,88 @@ async function generateFalVideo(req, ctx, deps) {
3106
3324
  if (lastFrame) base.end_image_url = lastFrame;
3107
3325
  } else {
3108
3326
  endpoint = `minimax/${family}/text-to-video`;
3109
- base.aspect_ratio = req.aspectRatio ?? "adaptive";
3327
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
3328
+ base.aspect_ratio = req.aspectRatio;
3329
+ }
3110
3330
  }
3111
3331
  if (ctx.routing.fal === "cloudflare-passthrough") {
3112
3332
  return await passthroughVideoJob(ctx, req, endpoint, base);
3113
3333
  }
3114
- const result = await runJob(ctx, deps, endpoint, base);
3115
- return completedVideoJob(ctx, req, extractVideoUrl(result, endpoint, ctx.model.id), result);
3334
+ return await submitQueueJob(ctx, req, deps, endpoint, base);
3335
+ }
3336
+ async function submitQueueJob(ctx, req, deps, endpoint, input) {
3337
+ const client2 = falClientOf(ctx, deps);
3338
+ let submitted;
3339
+ try {
3340
+ submitted = await withRetry(
3341
+ async () => {
3342
+ try {
3343
+ return await client2.queue.submit(endpoint, {
3344
+ input,
3345
+ ...ctx.signal !== void 0 ? { abortSignal: ctx.signal } : {}
3346
+ });
3347
+ } catch (cause) {
3348
+ throw falSdkError(cause, ctx.model.id);
3349
+ }
3350
+ },
3351
+ ctx.signal !== void 0 ? { signal: ctx.signal } : {}
3352
+ );
3353
+ } catch (cause) {
3354
+ throw falSdkError(cause, ctx.model.id);
3355
+ }
3356
+ return queuedVideoJob(ctx, req, client2, endpoint, submitted.requestId);
3116
3357
  }
3117
- function completedVideoJob(ctx, req, url, raw) {
3358
+ function queuedVideoJob(ctx, req, client2, endpoint, requestId) {
3359
+ const readStatus = async () => {
3360
+ try {
3361
+ const options = {
3362
+ requestId,
3363
+ ...ctx.signal !== void 0 ? { abortSignal: ctx.signal } : {}
3364
+ };
3365
+ return (await client2.queue.status(endpoint, options)).status;
3366
+ } catch (cause) {
3367
+ throw falSdkError(cause, ctx.model.id);
3368
+ }
3369
+ };
3118
3370
  const duration = req.durationSeconds ?? 5;
3119
- const result = { video: { url }, usage: videoUsage(ctx, req, duration), raw };
3120
3371
  return {
3121
- jobId: url,
3372
+ jobId: requestId,
3122
3373
  async poll() {
3123
- return { status: "succeeded", raw };
3374
+ try {
3375
+ const status = await readStatus();
3376
+ if (status === "COMPLETED") return { status: "succeeded" };
3377
+ if (status === "IN_QUEUE") return { status: "pending" };
3378
+ return { status: "running" };
3379
+ } catch (cause) {
3380
+ if (isModelKitError(cause) && !cause.retryable)
3381
+ return { status: "failed" };
3382
+ return { status: "pending" };
3383
+ }
3124
3384
  },
3125
- async result() {
3126
- return result;
3385
+ async result(opts) {
3386
+ const signal = opts?.signal ?? ctx.signal;
3387
+ const pollOptions = {
3388
+ intervalMs: opts?.pollIntervalMs ?? PASSTHROUGH_POLL_MS,
3389
+ timeoutMs: opts?.timeoutMs ?? 15 * 6e4
3390
+ };
3391
+ if (signal !== void 0) pollOptions.signal = signal;
3392
+ const queueOptions = {
3393
+ requestId,
3394
+ ...signal !== void 0 ? { abortSignal: signal } : {}
3395
+ };
3396
+ const payload = await pollUntil(async () => {
3397
+ try {
3398
+ const current = await client2.queue.status(endpoint, queueOptions);
3399
+ if (current.status !== "COMPLETED") return void 0;
3400
+ return await client2.queue.result(endpoint, queueOptions);
3401
+ } catch (cause) {
3402
+ const err = falSdkError(cause, ctx.model.id);
3403
+ if (err.retryable) return void 0;
3404
+ throw err;
3405
+ }
3406
+ }, pollOptions);
3407
+ const url = extractVideoUrl(payload, endpoint, ctx.model.id);
3408
+ return { video: { url }, usage: videoUsage(ctx, req, duration), raw: payload };
3127
3409
  }
3128
3410
  };
3129
3411
  }
@@ -3474,9 +3756,7 @@ function sdkStatus(cause) {
3474
3756
  return describeThrown(cause).status;
3475
3757
  }
3476
3758
  function throwSdkError(cause, model) {
3477
- if (cause instanceof ModelKitError) throw cause;
3478
- const info = describeThrown(cause);
3479
- throw classifyHttpError(info.status, info.body, "google-genai", model);
3759
+ throw classifyThrown("google-genai", cause, model);
3480
3760
  }
3481
3761
  function toBase64(bytes) {
3482
3762
  return Buffer.from(bytes).toString("base64");
@@ -3532,6 +3812,14 @@ async function generateVeoVideo(req, ctx, createGenAIClient) {
3532
3812
  model: modelId
3533
3813
  });
3534
3814
  }
3815
+ if (req.lastFrame !== void 0 && req.firstFrame === void 0) {
3816
+ throw new ModelKitError({
3817
+ code: "missing_input",
3818
+ message: `${modelId} requires firstFrame when lastFrame is set`,
3819
+ provider: "google-genai",
3820
+ model: modelId
3821
+ });
3822
+ }
3535
3823
  const duration = req.durationSeconds ?? 8;
3536
3824
  const personGeneration = req.firstFrame ?? refs.length > 0 ? "allow_adult" : "allow_all";
3537
3825
  let image;
@@ -3598,7 +3886,7 @@ async function generateVeoVideo(req, ctx, createGenAIClient) {
3598
3886
  throwSdkError(cause, modelId);
3599
3887
  }
3600
3888
  };
3601
- const jobId = operation.name ?? "veo-operation";
3889
+ const jobId = operation.name ?? crypto.randomUUID();
3602
3890
  const usage2 = videoUsage2(ctx, req, duration);
3603
3891
  return {
3604
3892
  jobId,
@@ -3680,12 +3968,17 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3680
3968
  const duration = req.durationSeconds ?? 8;
3681
3969
  const images = req.referenceImages ?? [];
3682
3970
  const videos = req.referenceVideos ?? [];
3683
- const task = req.sourceVideo ? "edit" : videos.length > 0 || images.length > 1 ? "reference_to_video" : req.firstFrame || images.length === 1 ? "image_to_video" : "text_to_video";
3684
- const responseFormat = { type: "video" };
3685
- if (req.sourceVideo === void 0) responseFormat.aspect_ratio = toOmniAspect(req.aspectRatio);
3686
- const omniResolution = toOmniResolution(req.resolution);
3687
- if (omniResolution !== void 0) responseFormat.resolution = omniResolution;
3688
- responseFormat.duration = `${duration}s`;
3971
+ const task = req.sourceVideo ? "edit" : req.firstFrame ? "image_to_video" : images.length > 0 || videos.length > 0 ? "reference_to_video" : "text_to_video";
3972
+ const isOmniV1 = providerModelId === "gemini-omni-flash-preview";
3973
+ const responseFormat = { type: "video", delivery: "uri" };
3974
+ if (req.sourceVideo === void 0 && req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
3975
+ responseFormat.aspect_ratio = toOmniAspect(req.aspectRatio);
3976
+ }
3977
+ if (!isOmniV1) {
3978
+ const omniResolution = toOmniResolution(req.resolution);
3979
+ if (omniResolution !== void 0) responseFormat.resolution = omniResolution;
3980
+ }
3981
+ if (req.durationSeconds !== void 0) responseFormat.duration = `${req.durationSeconds}s`;
3689
3982
  const client2 = createGenAIClient({ apiKey });
3690
3983
  const uploadedByUri = /* @__PURE__ */ new Map();
3691
3984
  const uploadedNames = [];
@@ -3709,7 +4002,8 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3709
4002
  model: providerModelId,
3710
4003
  input,
3711
4004
  response_format: responseFormat,
3712
- generation_config: { video_config: { task } }
4005
+ generation_config: { video_config: { task } },
4006
+ store: true
3713
4007
  });
3714
4008
  const createRetryOptions = { attempts: 3 };
3715
4009
  if (ctx.signal !== void 0) createRetryOptions.signal = ctx.signal;
@@ -3731,6 +4025,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3731
4025
  });
3732
4026
  }
3733
4027
  for (const ref of videos) {
4028
+ if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
3734
4029
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "video/mp4", modelId);
3735
4030
  parts.push({
3736
4031
  type: "video",
@@ -3739,6 +4034,8 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3739
4034
  });
3740
4035
  }
3741
4036
  if (req.firstFrame) {
4037
+ if (req.firstFrame.label !== void 0)
4038
+ parts.push({ type: "text", text: req.firstFrame.label });
3742
4039
  const media = await resolveMedia(
3743
4040
  req.firstFrame,
3744
4041
  ctx.fetch,
@@ -3757,6 +4054,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3757
4054
  }
3758
4055
  }
3759
4056
  for (const ref of images) {
4057
+ if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
3760
4058
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "image/png", modelId);
3761
4059
  if (media.bytes.length > OMNI_INLINE_LIMIT_BYTES) {
3762
4060
  parts.push({
@@ -3791,25 +4089,16 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3791
4089
  };
3792
4090
  return inline;
3793
4091
  });
3794
- const inlineRetryOptions = { attempts: 3 };
3795
- if (ctx.signal !== void 0) inlineRetryOptions.signal = ctx.signal;
3796
4092
  try {
3797
- interaction = await withRetry(async () => {
3798
- try {
3799
- return await callCreate(inlineParts);
3800
- } catch (inlineCause) {
3801
- throwSdkError(inlineCause, modelId);
3802
- }
3803
- }, inlineRetryOptions);
4093
+ interaction = await callCreate(inlineParts);
3804
4094
  } catch (inlineCause) {
3805
4095
  throwSdkError(inlineCause, modelId);
3806
4096
  }
3807
4097
  }
3808
- } catch (cause) {
4098
+ } finally {
3809
4099
  await cleanupUploads();
3810
- throw cause;
3811
4100
  }
3812
- const jobId = interaction.id ?? "gemini-omni-interaction";
4101
+ const jobId = interaction.id ?? crypto.randomUUID();
3813
4102
  const usage2 = videoUsage2(ctx, req, duration);
3814
4103
  const getInteraction = async () => {
3815
4104
  try {
@@ -3836,47 +4125,43 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3836
4125
  timeoutMs: opts?.timeoutMs ?? RESULT_TIMEOUT_MS2
3837
4126
  };
3838
4127
  if (signal !== void 0) pollOptions.signal = signal;
3839
- try {
3840
- const done = await pollUntil(async () => {
3841
- const current = await getInteraction();
3842
- const status = (current.status ?? "").toUpperCase();
3843
- if (status === "COMPLETED") return current;
3844
- if (status === "FAILED") {
3845
- throw classifyHttpError(
3846
- void 0,
3847
- `Gemini Omni interaction failed: ${JSON.stringify(current).slice(0, 500)}`,
3848
- "google-genai",
3849
- modelId
3850
- );
3851
- }
3852
- return void 0;
3853
- }, pollOptions);
3854
- const video = extractOmniVideo(done, modelId);
3855
- if (video.data) {
3856
- return {
3857
- video: {
3858
- bytes: Buffer.from(video.data, "base64"),
3859
- mime: video.mime_type ?? "video/mp4"
3860
- },
3861
- usage: usage2,
3862
- raw: done
3863
- };
4128
+ const done = await pollUntil(async () => {
4129
+ const current = await getInteraction();
4130
+ const status = (current.status ?? "").toUpperCase();
4131
+ if (status === "COMPLETED") return current;
4132
+ if (status === "FAILED") {
4133
+ throw classifyHttpError(
4134
+ void 0,
4135
+ `Gemini Omni interaction failed: ${JSON.stringify(current).slice(0, 500)}`,
4136
+ "google-genai",
4137
+ modelId
4138
+ );
3864
4139
  }
3865
- if (video.uri)
3866
- return {
3867
- video: await downloadVideo(ctx, video.uri, apiKey, signal),
3868
- usage: usage2,
3869
- raw: done
3870
- };
3871
- throw classifyHttpError(
3872
- void 0,
3873
- "No video content in Gemini Omni response",
3874
- "google-genai",
3875
- modelId
3876
- );
3877
- } finally {
3878
- await cleanupUploads();
4140
+ return void 0;
4141
+ }, pollOptions);
4142
+ const video = extractOmniVideo(done, modelId);
4143
+ if (video.data) {
4144
+ return {
4145
+ video: {
4146
+ bytes: Buffer.from(video.data, "base64"),
4147
+ mime: video.mime_type ?? "video/mp4"
4148
+ },
4149
+ usage: usage2,
4150
+ raw: done
4151
+ };
3879
4152
  }
4153
+ if (video.uri)
4154
+ return {
4155
+ video: await downloadVideo(ctx, video.uri, apiKey, signal),
4156
+ usage: usage2,
4157
+ raw: done
4158
+ };
4159
+ throw classifyHttpError(
4160
+ void 0,
4161
+ "No video content in Gemini Omni response",
4162
+ "google-genai",
4163
+ modelId
4164
+ );
3880
4165
  }
3881
4166
  };
3882
4167
  }
@@ -4039,44 +4324,37 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
4039
4324
  const model = google(ctx.model.providerModelId);
4040
4325
  const content = [];
4041
4326
  for (const ref of req.references ?? []) {
4327
+ if (ref.label !== void 0) content.push({ type: "text", text: ref.label });
4042
4328
  if ("url" in ref) content.push({ type: "image", image: ref.url });
4043
4329
  else content.push({ type: "image", image: ref.bytes, mediaType: ref.mime });
4044
4330
  }
4045
4331
  content.push({ type: "text", text: req.prompt });
4046
- void ctx.call.postCropToAspect;
4047
4332
  const imageConfig = {};
4048
- if (req.aspectRatio !== void 0) imageConfig.aspectRatio = req.aspectRatio;
4333
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
4334
+ imageConfig.aspectRatio = req.aspectRatio;
4049
4335
  if (req.size !== void 0) imageConfig.imageSize = req.size;
4336
+ const googleOptions = {
4337
+ imageConfig,
4338
+ responseModalities: ["IMAGE"]
4339
+ };
4340
+ if (req.safetySettings !== void 0) googleOptions.safetySettings = req.safetySettings;
4050
4341
  const retryOptions = {};
4051
4342
  if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
4052
4343
  const result = await withRetry(async () => {
4053
4344
  const timeout = AbortSignal.timeout(GEMINI_IMAGE_TIMEOUT_MS);
4054
4345
  const signal = ctx.signal !== void 0 ? AbortSignal.any([ctx.signal, timeout]) : timeout;
4055
4346
  try {
4056
- return await deps.runTextGeneration({
4347
+ const genOptions = {
4057
4348
  model,
4058
4349
  messages: [{ role: "user", content }],
4059
4350
  maxRetries: 0,
4060
4351
  providerOptions: {
4061
- google: {
4062
- imageConfig,
4063
- responseModalities: ["IMAGE"],
4064
- safetySettings: [
4065
- {
4066
- category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
4067
- threshold: "OFF"
4068
- },
4069
- { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "OFF" },
4070
- {
4071
- category: "HARM_CATEGORY_DANGEROUS_CONTENT",
4072
- threshold: "OFF"
4073
- },
4074
- { category: "HARM_CATEGORY_HARASSMENT", threshold: "OFF" }
4075
- ]
4076
- }
4352
+ google: googleOptions
4077
4353
  },
4078
4354
  abortSignal: signal
4079
- });
4355
+ };
4356
+ if (req.temperature !== void 0) genOptions.temperature = req.temperature;
4357
+ return await deps.runTextGeneration(genOptions);
4080
4358
  } catch (cause) {
4081
4359
  throw toProviderError(cause, ctx);
4082
4360
  }
@@ -4260,6 +4538,7 @@ var KLING_MAX_INTERVAL_MS = 1e4;
4260
4538
  var KLING_TIMEOUT_MS = 15 * 6e4;
4261
4539
  var DOP_INTERVAL_MS = 5e3;
4262
4540
  var DOP_TIMEOUT_MS = 5 * 6e4;
4541
+ var DOP_FALLBACK_DURATION_SECONDS = 5;
4263
4542
  var DEFAULT_NEGATIVE_PROMPT = "text, watermark, copyright, blur, low quality, distortion, abstract, weird, deformed";
4264
4543
  function credentials(ctx) {
4265
4544
  const key = ctx.keys.higgsfield;
@@ -4279,26 +4558,14 @@ function headers(ctx) {
4279
4558
  const { key, secret } = credentials(ctx);
4280
4559
  return { "Content-Type": "application/json", "hf-api-key": key, "hf-secret": secret };
4281
4560
  }
4282
- function refToUrl2(ref) {
4283
- if ("url" in ref) return ref.url;
4284
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
4285
- }
4286
- async function readBody2(res) {
4287
- const text2 = await res.text().catch(() => "");
4288
- try {
4289
- const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
4290
- return decoded.success ? decoded.data : text2;
4291
- } catch {
4292
- return text2;
4293
- }
4294
- }
4295
4561
  function klingEndpoint(providerModelId) {
4296
4562
  if (providerModelId.startsWith("http")) return providerModelId;
4297
4563
  const path = providerModelId.startsWith("/") ? providerModelId.slice(1) : providerModelId;
4298
4564
  return `${PLATFORM}/${path}`;
4299
4565
  }
4300
- function isDop(providerModelId) {
4301
- return providerModelId.toLowerCase().includes("dop");
4566
+ var DOP_MODEL_IDS = /* @__PURE__ */ new Set(["dop", "dop-preview"]);
4567
+ function higgsfieldVideoKind(providerModelId) {
4568
+ return DOP_MODEL_IDS.has(providerModelId.toLowerCase()) ? "dop" : "kling";
4302
4569
  }
4303
4570
  function videoUsage3(ctx, req, duration) {
4304
4571
  const event = {
@@ -4334,8 +4601,20 @@ function requestInit(ctx, init) {
4334
4601
  if (ctx.signal !== void 0) request.signal = ctx.signal;
4335
4602
  return request;
4336
4603
  }
4604
+ async function createRequest(ctx, url, init) {
4605
+ let res;
4606
+ try {
4607
+ res = await ctx.fetch(url, requestInit(ctx, init));
4608
+ } catch (cause) {
4609
+ throw classifyThrown("higgsfield", cause, ctx.model.id);
4610
+ }
4611
+ if (!res.ok) {
4612
+ throw classifyHttpError(res.status, await readBody(res), "higgsfield", ctx.model.id);
4613
+ }
4614
+ return res;
4615
+ }
4337
4616
  async function generateKlingVideo(req, ctx) {
4338
- const firstFrame = req.firstFrame ? refToUrl2(req.firstFrame) : void 0;
4617
+ const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
4339
4618
  if (!firstFrame) {
4340
4619
  throw new ModelKitError({
4341
4620
  code: "missing_input",
@@ -4352,26 +4631,16 @@ async function generateKlingVideo(req, ctx) {
4352
4631
  negative_prompt: req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT
4353
4632
  };
4354
4633
  if (req.durationSeconds !== void 0) body.duration = req.durationSeconds;
4355
- if (req.aspectRatio !== void 0) body.aspect_ratio = req.aspectRatio;
4356
- if (req.lastFrame) body.last_image_url = refToUrl2(req.lastFrame);
4634
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
4635
+ body.aspect_ratio = req.aspectRatio;
4636
+ if (req.lastFrame) body.last_image_url = refToUrl(req.lastFrame);
4357
4637
  const endpoint = klingEndpoint(ctx.model.providerModelId);
4358
- let createRes;
4359
- try {
4360
- createRes = await ctx.fetch(
4361
- endpoint,
4362
- requestInit(ctx, { method: "POST", body: JSON.stringify(body) })
4363
- );
4364
- } catch (cause) {
4365
- throw networkError(cause, ctx);
4366
- }
4367
- if (!createRes.ok) {
4368
- throw classifyHttpError(
4369
- createRes.status,
4370
- await readBody2(createRes),
4371
- "higgsfield",
4372
- ctx.model.id
4373
- );
4374
- }
4638
+ const retryOptions = {};
4639
+ if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
4640
+ const createRes = await withRetry(
4641
+ () => createRequest(ctx, endpoint, { method: "POST", body: JSON.stringify(body) }),
4642
+ retryOptions
4643
+ );
4375
4644
  const created = klingCreateSchema.safeParse(await createRes.json());
4376
4645
  if (!created.success) {
4377
4646
  throw new ModelKitError({
@@ -4390,9 +4659,9 @@ async function generateKlingVideo(req, ctx) {
4390
4659
  } catch {
4391
4660
  return void 0;
4392
4661
  }
4393
- if (res.status >= 500) return void 0;
4662
+ if (res.status === 429 || res.status >= 500) return void 0;
4394
4663
  if (!res.ok) {
4395
- throw classifyHttpError(res.status, await readBody2(res), "higgsfield", ctx.model.id);
4664
+ throw classifyHttpError(res.status, await readBody(res), "higgsfield", ctx.model.id);
4396
4665
  }
4397
4666
  const payload = JsonValueSchema.parse(await res.json());
4398
4667
  const parsed = klingStatusSchema.safeParse(payload);
@@ -4451,11 +4720,6 @@ async function generateKlingVideo(req, ctx) {
4451
4720
  }
4452
4721
  };
4453
4722
  }
4454
- function networkError(cause, ctx) {
4455
- if (isModelKitError(cause)) return cause;
4456
- const info = describeThrown(cause);
4457
- return classifyHttpError(info.status, info.body, "higgsfield", ctx.model.id);
4458
- }
4459
4723
  var dopMotionOptionsSchema = import_zod10.z.object({
4460
4724
  modelkit: import_zod10.z.object({ motion: import_zod10.z.string().optional() }).optional()
4461
4725
  });
@@ -4504,7 +4768,7 @@ function dopUrl(body) {
4504
4768
  var dopCreateSchema = import_zod10.z.object({ id: import_zod10.z.string() });
4505
4769
  var dopStatusSchema = import_zod10.z.object({ status: import_zod10.z.string().optional() });
4506
4770
  async function generateDopVideo(req, ctx) {
4507
- const firstFrame = req.firstFrame ? refToUrl2(req.firstFrame) : void 0;
4771
+ const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
4508
4772
  if (!firstFrame) {
4509
4773
  throw new ModelKitError({
4510
4774
  code: "missing_input",
@@ -4524,29 +4788,18 @@ async function generateDopVideo(req, ctx) {
4524
4788
  webhook: null
4525
4789
  };
4526
4790
  if (req.lastFrame) {
4527
- params.input_images_end = [{ type: "image_url", image_url: refToUrl2(req.lastFrame) }];
4791
+ params.input_images_end = [{ type: "image_url", image_url: refToUrl(req.lastFrame) }];
4528
4792
  }
4529
4793
  if (req.seed !== void 0) params.seed = req.seed;
4530
- let createRes;
4531
- try {
4532
- createRes = await ctx.fetch(
4533
- DOP_ENDPOINT,
4534
- requestInit(ctx, {
4535
- method: "POST",
4536
- body: JSON.stringify({ params, webhook: null })
4537
- })
4538
- );
4539
- } catch (cause) {
4540
- throw networkError(cause, ctx);
4541
- }
4542
- if (!createRes.ok) {
4543
- throw classifyHttpError(
4544
- createRes.status,
4545
- await readBody2(createRes),
4546
- "higgsfield",
4547
- ctx.model.id
4548
- );
4549
- }
4794
+ const retryOptions = {};
4795
+ if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
4796
+ const createRes = await withRetry(
4797
+ () => createRequest(ctx, DOP_ENDPOINT, {
4798
+ method: "POST",
4799
+ body: JSON.stringify({ params, webhook: null })
4800
+ }),
4801
+ retryOptions
4802
+ );
4550
4803
  const created = dopCreateSchema.safeParse(await createRes.json());
4551
4804
  if (!created.success) {
4552
4805
  throw new ModelKitError({
@@ -4615,14 +4868,20 @@ async function generateDopVideo(req, ctx) {
4615
4868
  };
4616
4869
  if (dopSignal !== void 0) pollSettings.signal = dopSignal;
4617
4870
  const url = await pollUntil(check, pollSettings);
4618
- const result = { video: { url }, usage: videoUsage3(ctx, req) };
4871
+ const result = {
4872
+ video: { url },
4873
+ usage: videoUsage3(ctx, req, req.durationSeconds ?? DOP_FALLBACK_DURATION_SECONDS)
4874
+ };
4619
4875
  return result;
4620
4876
  }
4621
4877
  };
4622
4878
  }
4623
4879
  var higgsfieldAdapter = {
4624
4880
  id: "higgsfield",
4625
- generateVideo: (req, ctx) => isDop(ctx.model.providerModelId) ? generateDopVideo(req, ctx) : generateKlingVideo(req, ctx)
4881
+ generateVideo: (req, ctx) => {
4882
+ throwIfAborted(ctx.signal);
4883
+ return higgsfieldVideoKind(ctx.model.providerModelId) === "dop" ? generateDopVideo(req, ctx) : generateKlingVideo(req, ctx);
4884
+ }
4626
4885
  };
4627
4886
 
4628
4887
  // src/adapters/openai-image.ts
@@ -4717,6 +4976,7 @@ var openaiUsageSchema = import_zod11.z.object({
4717
4976
  var openaiImageAdapter = {
4718
4977
  id: "openai-image",
4719
4978
  async generateImage(req, ctx) {
4979
+ throwIfAborted(ctx.signal);
4720
4980
  const apiKey = ctx.keys.openai;
4721
4981
  if (!apiKey)
4722
4982
  throw new ModelKitError({
@@ -4726,6 +4986,13 @@ var openaiImageAdapter = {
4726
4986
  model: ctx.model.id,
4727
4987
  retryable: false
4728
4988
  });
4989
+ if ((req.references ?? []).some((ref) => ref.label !== void 0)) {
4990
+ ctx.warnings.push({
4991
+ code: "option_ignored",
4992
+ field: "references.label",
4993
+ message: "openai-image cannot express reference labels; ignoring them"
4994
+ });
4995
+ }
4729
4996
  const client2 = new import_openai2.default({ apiKey, fetch: ctx.fetch, maxRetries: 0 });
4730
4997
  const size = req.exactSize !== void 0 ? req.exactSize : req.aspectRatio === void 0 || req.aspectRatio === "adaptive" ? void 0 : aspectRatioToSize(req.aspectRatio, req.size ?? "1K");
4731
4998
  const refs = req.references ?? [];
@@ -4739,24 +5006,34 @@ var openaiImageAdapter = {
4739
5006
  try {
4740
5007
  if (!isEdit) {
4741
5008
  if (size !== void 0) {
4742
- return await client2.images.generate({
5009
+ return await client2.images.generate(
5010
+ {
5011
+ model: ctx.model.providerModelId,
5012
+ prompt: req.prompt,
5013
+ size,
5014
+ quality: sdkQuality,
5015
+ ...count
5016
+ },
5017
+ { signal: ctx.signal }
5018
+ );
5019
+ }
5020
+ return await client2.images.generate(
5021
+ {
4743
5022
  model: ctx.model.providerModelId,
4744
5023
  prompt: req.prompt,
4745
- size,
4746
5024
  quality: sdkQuality,
4747
5025
  ...count
4748
- });
4749
- }
4750
- return await client2.images.generate({
4751
- model: ctx.model.providerModelId,
4752
- prompt: req.prompt,
4753
- quality: sdkQuality,
4754
- ...count
4755
- });
5026
+ },
5027
+ { signal: ctx.signal }
5028
+ );
4756
5029
  }
4757
5030
  const files = await Promise.all(
4758
5031
  refs.slice(0, MAX_EDIT_REFERENCES).map(async (ref, i) => {
4759
- const { bytes, mime } = await mediaRefToBytes(ref, ctx.fetch);
5032
+ const { bytes, mime } = await mediaRefToBytes(ref, ctx.fetch, {
5033
+ signal: ctx.signal,
5034
+ provider: "openai-image",
5035
+ model: ctx.model.id
5036
+ });
4760
5037
  const ext = mime.includes("jpeg") ? "jpg" : mime.includes("webp") ? "webp" : "png";
4761
5038
  return new File([bytes], `ref-${i}.${ext}`, { type: mime });
4762
5039
  })
@@ -4764,22 +5041,28 @@ var openaiImageAdapter = {
4764
5041
  const single = files.length === 1 ? files[0] : void 0;
4765
5042
  const imageInput = single ?? files;
4766
5043
  if (size !== void 0) {
4767
- return await client2.images.edit({
5044
+ return await client2.images.edit(
5045
+ {
5046
+ model: ctx.model.providerModelId,
5047
+ image: imageInput,
5048
+ prompt: req.prompt,
5049
+ size,
5050
+ quality: sdkQuality,
5051
+ ...count
5052
+ },
5053
+ { signal: ctx.signal }
5054
+ );
5055
+ }
5056
+ return await client2.images.edit(
5057
+ {
4768
5058
  model: ctx.model.providerModelId,
4769
5059
  image: imageInput,
4770
5060
  prompt: req.prompt,
4771
- size,
4772
5061
  quality: sdkQuality,
4773
5062
  ...count
4774
- });
4775
- }
4776
- return await client2.images.edit({
4777
- model: ctx.model.providerModelId,
4778
- image: imageInput,
4779
- prompt: req.prompt,
4780
- quality: sdkQuality,
4781
- ...count
4782
- });
5063
+ },
5064
+ { signal: ctx.signal }
5065
+ );
4783
5066
  } catch (cause) {
4784
5067
  throw toProviderError2(cause, ctx);
4785
5068
  }
@@ -4797,28 +5080,16 @@ var openaiImageAdapter = {
4797
5080
  const usageParsed = openaiUsageSchema.safeParse(response.usage);
4798
5081
  const inputTokens = usageParsed.success ? usageParsed.data.input_tokens : void 0;
4799
5082
  const outputTokens = usageParsed.success ? usageParsed.data.output_tokens : void 0;
4800
- let usage2;
4801
- if (inputTokens !== void 0 || outputTokens !== void 0) {
4802
- usage2 = {
4803
- model: ctx.model.id,
4804
- provider: "openai-image",
4805
- modality: "IMAGE",
4806
- unit: "tokens",
4807
- quantity: (inputTokens ?? 0) + (outputTokens ?? 0),
4808
- imageQuality: quality
4809
- };
4810
- if (inputTokens !== void 0) usage2.inputTokens = inputTokens;
4811
- if (outputTokens !== void 0) usage2.outputTokens = outputTokens;
4812
- } else {
4813
- usage2 = {
4814
- model: ctx.model.id,
4815
- provider: "openai-image",
4816
- modality: "IMAGE",
4817
- unit: "images",
4818
- quantity: images.length,
4819
- imageQuality: quality
4820
- };
4821
- }
5083
+ const usage2 = {
5084
+ model: ctx.model.id,
5085
+ provider: "openai-image",
5086
+ modality: "IMAGE",
5087
+ unit: "images",
5088
+ quantity: images.length,
5089
+ imageQuality: quality
5090
+ };
5091
+ if (inputTokens !== void 0) usage2.inputTokens = inputTokens;
5092
+ if (outputTokens !== void 0) usage2.outputTokens = outputTokens;
4822
5093
  if (req.size !== void 0) usage2.imageSize = req.size;
4823
5094
  return { images, usage: usage2, raw: response };
4824
5095
  }
@@ -4871,7 +5142,12 @@ var NANO_BANANA_RATIOS = /* @__PURE__ */ new Set([
4871
5142
  ]);
4872
5143
  async function refToBytes(ctx, ref) {
4873
5144
  if ("bytes" in ref) return { bytes: ref.bytes, mime: ref.mime };
4874
- const res = await ctx.fetch(ref.url);
5145
+ let res;
5146
+ try {
5147
+ res = await ctx.fetch(ref.url, ctx.signal ? { signal: ctx.signal } : {});
5148
+ } catch (cause) {
5149
+ throw classifyThrown("prodia", cause, ctx.model.id);
5150
+ }
4875
5151
  if (!res.ok) {
4876
5152
  const text2 = await res.text().catch(() => "");
4877
5153
  throw classifyHttpError(res.status, text2, "prodia", ctx.model.id);
@@ -4914,6 +5190,12 @@ function emptyImage() {
4914
5190
  retryable: false
4915
5191
  });
4916
5192
  }
5193
+ function inputExtension(mime) {
5194
+ if (mime.includes("jpeg") || mime.includes("jpg")) return "jpg";
5195
+ if (mime.includes("webp")) return "webp";
5196
+ if (mime.includes("gif")) return "gif";
5197
+ return "png";
5198
+ }
4917
5199
  function isBgRemove(providerModelId) {
4918
5200
  const id = providerModelId.toLowerCase();
4919
5201
  return id.includes("remove-background") || id.includes("bg-remove");
@@ -4922,6 +5204,7 @@ function isKlein(providerModelId) {
4922
5204
  return providerModelId.toLowerCase().includes("klein");
4923
5205
  }
4924
5206
  async function generateProdiaImage(req, ctx) {
5207
+ throwIfAborted(ctx.signal);
4925
5208
  const key = ctx.keys.prodia;
4926
5209
  if (!key) {
4927
5210
  throw new ModelKitError({
@@ -5020,7 +5303,9 @@ async function generateProdiaImage(req, ctx) {
5020
5303
  "input",
5021
5304
  // Uint8Array<ArrayBufferLike> is not a valid BlobPart under the
5022
5305
  // strict lib types; copy into a fresh ArrayBuffer-backed view.
5023
- new File([new Uint8Array(input.bytes)], `input-${i}.jpg`, { type: "image/jpeg" })
5306
+ new File([new Uint8Array(input.bytes)], `input-${i}.${inputExtension(input.mime)}`, {
5307
+ type: input.mime
5308
+ })
5024
5309
  );
5025
5310
  });
5026
5311
  const retryOpts = {
@@ -5035,14 +5320,15 @@ async function generateProdiaImage(req, ctx) {
5035
5320
  res = await withRetry(async () => {
5036
5321
  let attemptRes;
5037
5322
  try {
5038
- attemptRes = await ctx.fetch(PRODIA_JOB_URL, {
5323
+ const init = {
5039
5324
  method: "POST",
5040
5325
  headers: { Authorization: `Bearer ${key}`, Accept: accept },
5041
5326
  body: form
5042
- });
5327
+ };
5328
+ if (ctx.signal !== void 0) init.signal = ctx.signal;
5329
+ attemptRes = await ctx.fetch(PRODIA_JOB_URL, init);
5043
5330
  } catch (err) {
5044
- if (isModelKitError(err)) throw err;
5045
- throw transportError("prodia", err, ctx.model.id);
5331
+ throw classifyThrown("prodia", err, ctx.model.id);
5046
5332
  }
5047
5333
  if (!attemptRes.ok) {
5048
5334
  const text2 = await attemptRes.text().catch(() => "");
@@ -5051,8 +5337,7 @@ async function generateProdiaImage(req, ctx) {
5051
5337
  return attemptRes;
5052
5338
  }, retryOpts);
5053
5339
  } catch (err) {
5054
- if (isModelKitError(err)) throw err;
5055
- throw transportError("prodia", err, ctx.model.id);
5340
+ throw classifyThrown("prodia", err, ctx.model.id);
5056
5341
  }
5057
5342
  const bytes = parseImageResponse(
5058
5343
  new Uint8Array(await res.arrayBuffer()),
@@ -5102,11 +5387,7 @@ function client(ctx, deps) {
5102
5387
  retryable: false
5103
5388
  });
5104
5389
  }
5105
- return deps.createReplicateClient({ auth: token });
5106
- }
5107
- function refToUrl3(ref) {
5108
- if ("url" in ref) return ref.url;
5109
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
5390
+ return deps.createReplicateClient({ auth: token, fetch: ctx.fetch });
5110
5391
  }
5111
5392
  function route(providerModelId) {
5112
5393
  if (providerModelId.includes(":")) {
@@ -5124,10 +5405,11 @@ function replicateError(cause, ctx) {
5124
5405
  const info = describeThrown(cause);
5125
5406
  const nested = replicateResponseStatusSchema.safeParse(cause);
5126
5407
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
5127
- return classifyHttpError(status, info.body, "replicate", ctx.model.id);
5408
+ return classifyThrown("replicate", cause, ctx.model.id, { status, body: info.body });
5128
5409
  }
5129
- function isLower(id) {
5130
- return id.toLowerCase();
5410
+ function baseModelId(providerModelId) {
5411
+ const colon = providerModelId.indexOf(":");
5412
+ return colon === -1 ? providerModelId : providerModelId.slice(0, colon);
5131
5413
  }
5132
5414
  function predictionErrorBody(cause) {
5133
5415
  const parsed = JsonValueSchema.safeParse(cause);
@@ -5175,6 +5457,7 @@ function noOutput() {
5175
5457
  });
5176
5458
  }
5177
5459
  async function runPrediction(ctx, input, deps) {
5460
+ throwIfAborted(ctx.signal);
5178
5461
  const api = client(ctx, deps);
5179
5462
  const createOptions = { ...route(ctx.model.providerModelId), input };
5180
5463
  let prediction;
@@ -5182,12 +5465,14 @@ async function runPrediction(ctx, input, deps) {
5182
5465
  prediction = await withRetry(
5183
5466
  async () => {
5184
5467
  try {
5185
- return await api.predictions.create(createOptions);
5468
+ return await api.predictions.create(
5469
+ ctx.signal ? { ...createOptions, signal: ctx.signal } : createOptions
5470
+ );
5186
5471
  } catch (cause) {
5187
5472
  throw replicateError(cause, ctx);
5188
5473
  }
5189
5474
  },
5190
- ctx.signal ? { signal: ctx.signal } : {}
5475
+ { attempts: 1 }
5191
5476
  );
5192
5477
  } catch (cause) {
5193
5478
  throw replicateError(cause, ctx);
@@ -5200,7 +5485,10 @@ async function runPrediction(ctx, input, deps) {
5200
5485
  const done = await pollUntil(async () => {
5201
5486
  let current;
5202
5487
  try {
5203
- current = await api.predictions.get(prediction.id);
5488
+ current = await api.predictions.get(
5489
+ prediction.id,
5490
+ ctx.signal ? { signal: ctx.signal } : void 0
5491
+ );
5204
5492
  } catch (cause) {
5205
5493
  throw replicateError(cause, ctx);
5206
5494
  }
@@ -5267,21 +5555,32 @@ async function download(ctx, url) {
5267
5555
  const mime = res.headers.get("content-type") ?? "image/png";
5268
5556
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
5269
5557
  }
5270
- function isUpscaler(providerModelId) {
5271
- const id = isLower(providerModelId);
5272
- return id.includes("upscal") || id.includes("crystal") || id.includes("topaz");
5273
- }
5274
- function isTopazUpscaler(providerModelId) {
5275
- return isLower(providerModelId).includes("topazlabs/image-upscale");
5276
- }
5277
- function isQwen(providerModelId) {
5278
- return isLower(providerModelId).includes("qwen");
5279
- }
5280
- function isWanImage(providerModelId) {
5281
- return isLower(providerModelId).includes("wan-2.7");
5558
+ var REPLICATE_IMAGE_KINDS = {
5559
+ "topazlabs/image-upscale": "topaz",
5560
+ "philz1337x/crystal-upscaler": "upscaler",
5561
+ "wan-video/wan-2.7-image-pro": "wan",
5562
+ "qwen/qwen-image-2-pro": "qwen-image",
5563
+ "qwen-edit-apps/qwen-image-edit-plus-lora-skin": "qwen-edit"
5564
+ };
5565
+ function imageKind(providerModelId) {
5566
+ return REPLICATE_IMAGE_KINDS[baseModelId(providerModelId)] ?? "generic";
5567
+ }
5568
+ var REPLICATE_VIDEO_KINDS = {
5569
+ "kling-video/v2.5-turbo/pro/image-to-video": "kling",
5570
+ "kling-video/v3.0/pro/image-to-video": "kling",
5571
+ "kwaivgi/kling-v3-video": "kling",
5572
+ "kwaivgi/kling-v3-omni-video": "kling-omni",
5573
+ "minimax/hailuo-02": "hailuo"
5574
+ };
5575
+ function videoKind(providerModelId) {
5576
+ return REPLICATE_VIDEO_KINDS[baseModelId(providerModelId)] ?? "wan";
5282
5577
  }
5283
- function isQwenEditPlus(providerModelId) {
5284
- return isLower(providerModelId).includes("edit");
5578
+ var REPLICATE_AUDIO_KINDS = {
5579
+ "zsxkib/mmaudio": "mmaudio",
5580
+ "elevenlabs/music": "elevenlabs"
5581
+ };
5582
+ function audioKind(providerModelId) {
5583
+ return REPLICATE_AUDIO_KINDS[baseModelId(providerModelId)] ?? "lyria";
5285
5584
  }
5286
5585
  var QWEN_IMAGE_RATIOS = /* @__PURE__ */ new Set([
5287
5586
  "1:1",
@@ -5324,87 +5623,113 @@ function wanImageSize(aspectRatio, size) {
5324
5623
  async function generateReplicateImage(req, ctx, deps) {
5325
5624
  const id = ctx.model.providerModelId;
5326
5625
  let input;
5327
- if (isTopazUpscaler(id)) {
5328
- const first = (req.references ?? [])[0];
5329
- if (!first) {
5330
- throw new ModelKitError({
5331
- code: "missing_input",
5332
- message: `${ctx.model.id} requires a reference image to upscale`,
5333
- provider: "replicate",
5334
- model: ctx.model.id,
5335
- retryable: false
5336
- });
5626
+ switch (imageKind(id)) {
5627
+ case "topaz": {
5628
+ const first = (req.references ?? [])[0];
5629
+ if (!first) {
5630
+ throw new ModelKitError({
5631
+ code: "missing_input",
5632
+ message: `${ctx.model.id} requires a reference image to upscale`,
5633
+ provider: "replicate",
5634
+ model: ctx.model.id,
5635
+ retryable: false
5636
+ });
5637
+ }
5638
+ let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
5639
+ if (format !== "png" && format !== "jpg") {
5640
+ ctx.warnings.push({
5641
+ code: "option_ignored",
5642
+ field: "outputFormat",
5643
+ from: format,
5644
+ message: "topazlabs/image-upscale supports only png and jpg; using png"
5645
+ });
5646
+ format = "png";
5647
+ }
5648
+ const upscale = req.upscale;
5649
+ const topaz = {
5650
+ image: refToUrl(first),
5651
+ enhance_model: upscale?.enhanceModel ?? "Standard V2",
5652
+ output_format: format,
5653
+ upscale_factor: `${req.scaleFactor ?? 2}x`,
5654
+ face_enhancement: upscale?.faceEnhancement ?? false
5655
+ };
5656
+ if (upscale?.subjectDetection !== void 0)
5657
+ topaz.subject_detection = upscale.subjectDetection;
5658
+ if (upscale?.faceEnhancementCreativity !== void 0)
5659
+ topaz.face_enhancement_creativity = upscale.faceEnhancementCreativity;
5660
+ if (upscale?.faceEnhancementStrength !== void 0)
5661
+ topaz.face_enhancement_strength = upscale.faceEnhancementStrength;
5662
+ input = topaz;
5663
+ break;
5337
5664
  }
5338
- let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
5339
- if (format !== "png" && format !== "jpg") {
5340
- ctx.warnings.push({
5341
- code: "option_ignored",
5342
- field: "outputFormat",
5343
- from: format,
5344
- message: "topazlabs/image-upscale supports only png and jpg; using png"
5345
- });
5346
- format = "png";
5665
+ case "upscaler": {
5666
+ const first = (req.references ?? [])[0];
5667
+ if (!first) {
5668
+ throw new ModelKitError({
5669
+ code: "missing_input",
5670
+ message: `${ctx.model.id} requires a reference image to upscale`,
5671
+ provider: "replicate",
5672
+ model: ctx.model.id,
5673
+ retryable: false
5674
+ });
5675
+ }
5676
+ const imageInput = { prompt: req.prompt };
5677
+ imageInput.image = refToUrl(first);
5678
+ imageInput.creativity = 0;
5679
+ imageInput.scale_factor = 2;
5680
+ imageInput.output_format = "png";
5681
+ input = imageInput;
5682
+ break;
5347
5683
  }
5348
- input = {
5349
- image: refToUrl3(first),
5350
- enhance_model: "Standard V2",
5351
- output_format: format,
5352
- upscale_factor: `${req.scaleFactor ?? 2}x`,
5353
- face_enhancement: true
5354
- };
5355
- } else if (isUpscaler(id)) {
5356
- const refs = req.references ?? [];
5357
- const first = refs[0];
5358
- if (!first) {
5359
- throw new ModelKitError({
5360
- code: "missing_input",
5361
- message: `${ctx.model.id} requires a reference image to upscale`,
5362
- provider: "replicate",
5363
- model: ctx.model.id,
5364
- retryable: false
5365
- });
5684
+ case "wan": {
5685
+ const refs = (req.references ?? []).map(refToUrl);
5686
+ const imageInput = { prompt: req.prompt };
5687
+ if (refs.length > 0) imageInput.images = refs;
5688
+ imageInput.size = wanImageSize(req.aspectRatio, req.size);
5689
+ input = imageInput;
5690
+ break;
5366
5691
  }
5367
- const imageInput = { prompt: req.prompt };
5368
- imageInput.image = refToUrl3(first);
5369
- imageInput.creativity = 0;
5370
- imageInput.scale_factor = 2;
5371
- imageInput.output_format = "png";
5372
- input = imageInput;
5373
- } else if (isWanImage(id)) {
5374
- const refs = (req.references ?? []).map(refToUrl3);
5375
- const imageInput = { prompt: req.prompt };
5376
- if (refs.length > 0) imageInput.images = refs;
5377
- imageInput.size = wanImageSize(req.aspectRatio, req.size);
5378
- input = imageInput;
5379
- } else if (isQwen(id)) {
5380
- const refs = (req.references ?? []).map(refToUrl3);
5381
- const firstUrl = refs[0];
5382
- const imageInput = { prompt: req.prompt };
5383
- if (firstUrl !== void 0) {
5384
- imageInput.image = firstUrl;
5385
- if (!isQwenEditPlus(id)) imageInput.match_input_image = true;
5692
+ case "qwen-image": {
5693
+ const refs = (req.references ?? []).map(refToUrl);
5694
+ const firstUrl = refs[0];
5695
+ const imageInput = { prompt: req.prompt };
5696
+ if (firstUrl !== void 0) {
5697
+ imageInput.image = firstUrl;
5698
+ imageInput.match_input_image = true;
5699
+ } else if (req.aspectRatio !== void 0 && QWEN_IMAGE_RATIOS.has(req.aspectRatio)) {
5700
+ imageInput.aspect_ratio = req.aspectRatio;
5701
+ }
5702
+ input = imageInput;
5703
+ break;
5386
5704
  }
5387
- const ratios = isQwenEditPlus(id) ? QWEN_EDIT_RATIOS : QWEN_IMAGE_RATIOS;
5388
- if (req.aspectRatio !== void 0) {
5389
- if (ratios.has(req.aspectRatio)) imageInput.aspect_ratio = req.aspectRatio;
5390
- else
5391
- ctx.warnings.push({
5392
- code: "option_ignored",
5393
- field: "aspectRatio",
5394
- from: req.aspectRatio,
5395
- message: `${id} has no ${req.aspectRatio} aspect_ratio; using the provider default`
5396
- });
5705
+ case "qwen-edit": {
5706
+ const refs = (req.references ?? []).map(refToUrl);
5707
+ const firstUrl = refs[0];
5708
+ const imageInput = { prompt: req.prompt };
5709
+ if (firstUrl !== void 0) imageInput.image = firstUrl;
5710
+ if (req.aspectRatio !== void 0) {
5711
+ if (QWEN_EDIT_RATIOS.has(req.aspectRatio))
5712
+ imageInput.aspect_ratio = req.aspectRatio;
5713
+ else
5714
+ ctx.warnings.push({
5715
+ code: "option_ignored",
5716
+ field: "aspectRatio",
5717
+ from: req.aspectRatio,
5718
+ message: `${id} has no ${req.aspectRatio} aspect_ratio; using the provider default`
5719
+ });
5720
+ }
5721
+ if (req.outputFormat !== void 0) {
5722
+ imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
5723
+ }
5724
+ input = imageInput;
5725
+ break;
5397
5726
  }
5398
- if (isQwenEditPlus(id) && req.outputFormat !== void 0) {
5399
- imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
5727
+ default: {
5728
+ const first = (req.references ?? [])[0];
5729
+ const imageInput = { prompt: req.prompt };
5730
+ if (first) imageInput.image = refToUrl(first);
5731
+ input = imageInput;
5400
5732
  }
5401
- input = imageInput;
5402
- } else {
5403
- const refs = req.references ?? [];
5404
- const first = refs[0];
5405
- const imageInput = { prompt: req.prompt };
5406
- if (first) imageInput.image = refToUrl3(first);
5407
- input = imageInput;
5408
5733
  }
5409
5734
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5410
5735
  const file = await download(ctx, url);
@@ -5421,8 +5746,8 @@ function wanSize(aspectRatio) {
5421
5746
  return w > h ? "2048*1152" : "1536*2048";
5422
5747
  }
5423
5748
  function klingInput(req) {
5424
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl3);
5425
- if (req.lastFrame) images.push(refToUrl3(req.lastFrame));
5749
+ const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
5750
+ if (req.lastFrame) images.push(refToUrl(req.lastFrame));
5426
5751
  const input = {
5427
5752
  prompt: req.prompt,
5428
5753
  duration: req.durationSeconds ?? 10,
@@ -5434,19 +5759,24 @@ function klingInput(req) {
5434
5759
  input.start_image = startImage;
5435
5760
  const endImage = images[1];
5436
5761
  if (endImage !== void 0) input.end_image = endImage;
5437
- } else {
5762
+ } else if (req.aspectRatio !== "adaptive") {
5438
5763
  input.aspect_ratio = req.aspectRatio ?? "16:9";
5439
5764
  }
5440
5765
  if (req.negativePrompt !== void 0) input.negative_prompt = req.negativePrompt;
5441
5766
  return input;
5442
5767
  }
5768
+ function klingOmniMode(req) {
5769
+ if (req.resolution === "1080p") return "pro";
5770
+ if (req.resolution === "720p") return "standard";
5771
+ return req.mode ?? "standard";
5772
+ }
5443
5773
  function klingOmniInput(req) {
5444
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl3);
5445
- if (req.lastFrame) images.push(refToUrl3(req.lastFrame));
5774
+ const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
5775
+ if (req.lastFrame) images.push(refToUrl(req.lastFrame));
5446
5776
  const input = {
5447
5777
  prompt: req.prompt,
5448
5778
  duration: req.durationSeconds ?? 10,
5449
- mode: req.mode ?? "standard",
5779
+ mode: klingOmniMode(req),
5450
5780
  generate_audio: req.generateAudio ?? false
5451
5781
  };
5452
5782
  const startImage = images[0];
@@ -5454,10 +5784,10 @@ function klingOmniInput(req) {
5454
5784
  input.start_image = startImage;
5455
5785
  const endImage = images[1];
5456
5786
  if (endImage !== void 0) input.end_image = endImage;
5457
- } else {
5787
+ } else if (req.aspectRatio !== "adaptive") {
5458
5788
  input.aspect_ratio = req.aspectRatio ?? "16:9";
5459
5789
  }
5460
- const refs = (req.referenceImages ?? []).map(refToUrl3);
5790
+ const refs = (req.referenceImages ?? []).map(refToUrl);
5461
5791
  if (refs.length > 0) input.reference_images = refs;
5462
5792
  return input;
5463
5793
  }
@@ -5468,8 +5798,8 @@ function hailuoInput(req) {
5468
5798
  resolution: req.resolution ?? "768p",
5469
5799
  prompt_optimizer: true
5470
5800
  };
5471
- if (req.firstFrame) input.first_frame_image = refToUrl3(req.firstFrame);
5472
- if (req.lastFrame) input.last_frame_image = refToUrl3(req.lastFrame);
5801
+ if (req.firstFrame) input.first_frame_image = refToUrl(req.firstFrame);
5802
+ if (req.lastFrame) input.last_frame_image = refToUrl(req.lastFrame);
5473
5803
  return input;
5474
5804
  }
5475
5805
  function wanInput(req) {
@@ -5478,12 +5808,13 @@ function wanInput(req) {
5478
5808
  duration: req.durationSeconds ?? 5,
5479
5809
  size: wanSize(req.aspectRatio)
5480
5810
  };
5481
- if (req.firstFrame) input.image = refToUrl3(req.firstFrame);
5811
+ if (req.firstFrame) input.image = refToUrl(req.firstFrame);
5482
5812
  return input;
5483
5813
  }
5484
5814
  async function generateReplicateVideo(req, ctx, deps) {
5485
- const id = isLower(ctx.model.providerModelId);
5486
- const input = id.includes("kling") ? id.includes("omni") ? klingOmniInput(req) : klingInput(req) : id.includes("hailuo") ? hailuoInput(req) : wanInput(req);
5815
+ throwIfAborted(ctx.signal);
5816
+ const kind = videoKind(ctx.model.providerModelId);
5817
+ const input = kind === "kling" ? klingInput(req) : kind === "kling-omni" ? klingOmniInput(req) : kind === "hailuo" ? hailuoInput(req) : wanInput(req);
5487
5818
  const api = client(ctx, deps);
5488
5819
  const createOptions = { ...route(ctx.model.providerModelId), input };
5489
5820
  let prediction;
@@ -5491,12 +5822,14 @@ async function generateReplicateVideo(req, ctx, deps) {
5491
5822
  prediction = await withRetry(
5492
5823
  async () => {
5493
5824
  try {
5494
- return await api.predictions.create(createOptions);
5825
+ return await api.predictions.create(
5826
+ ctx.signal ? { ...createOptions, signal: ctx.signal } : createOptions
5827
+ );
5495
5828
  } catch (cause) {
5496
5829
  throw replicateError(cause, ctx);
5497
5830
  }
5498
5831
  },
5499
- ctx.signal ? { signal: ctx.signal } : {}
5832
+ { attempts: 1 }
5500
5833
  );
5501
5834
  } catch (cause) {
5502
5835
  throw replicateError(cause, ctx);
@@ -5505,7 +5838,10 @@ async function generateReplicateVideo(req, ctx, deps) {
5505
5838
  const check = async () => {
5506
5839
  let current;
5507
5840
  try {
5508
- current = await api.predictions.get(jobId);
5841
+ current = await api.predictions.get(
5842
+ jobId,
5843
+ ctx.signal ? { signal: ctx.signal } : void 0
5844
+ );
5509
5845
  } catch (cause) {
5510
5846
  throw replicateError(cause, ctx);
5511
5847
  }
@@ -5606,7 +5942,7 @@ function mmaudioInput(req, ctx) {
5606
5942
  });
5607
5943
  }
5608
5944
  const input = {
5609
- video: refToUrl3(video),
5945
+ video: refToUrl(video),
5610
5946
  duration: req.durationSeconds ?? 8,
5611
5947
  seed: req.seed ?? -1
5612
5948
  };
@@ -5625,8 +5961,8 @@ function predictionRaw(prediction) {
5625
5961
  return raw;
5626
5962
  }
5627
5963
  async function generateReplicateAudio(req, ctx, deps) {
5628
- const id = isLower(ctx.model.providerModelId);
5629
- const input = id.includes("mmaudio") ? mmaudioInput(req, ctx) : id.includes("elevenlabs") ? elevenLabsMusicInput(req, ctx) : lyriaAudioInput(req, ctx);
5964
+ const kind = audioKind(ctx.model.providerModelId);
5965
+ const input = kind === "mmaudio" ? mmaudioInput(req, ctx) : kind === "elevenlabs" ? elevenLabsMusicInput(req, ctx) : lyriaAudioInput(req, ctx);
5630
5966
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5631
5967
  const usage2 = audioUsage(ctx, req);
5632
5968
  if (ctx.model.usage.unit === "seconds") {
@@ -5655,19 +5991,14 @@ function createXaiVideoAdapter(deps = defaultXaiDeps) {
5655
5991
  };
5656
5992
  }
5657
5993
  var xaiVideoAdapter = createXaiVideoAdapter();
5658
- function refToUrl4(ref) {
5659
- if ("url" in ref) return ref.url;
5660
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
5661
- }
5662
5994
  var xaiResponseStatusSchema = import_zod13.z.object({
5663
5995
  response: import_zod13.z.object({ status: import_zod13.z.number() }).partial()
5664
5996
  });
5665
5997
  function xaiError(cause, ctx) {
5666
- if (isModelKitError(cause)) return cause;
5667
5998
  const info = describeThrown(cause);
5668
5999
  const nested = xaiResponseStatusSchema.safeParse(cause);
5669
6000
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
5670
- return classifyHttpError(status, info.body, "xai-video", ctx.model.id);
6001
+ return classifyThrown("xai-video", cause, ctx.model.id, { status, body: info.body });
5671
6002
  }
5672
6003
  var xaiMetaSchema = import_zod13.z.object({
5673
6004
  videoUrl: JsonValueSchema.optional(),
@@ -5691,27 +6022,35 @@ async function generateXaiVideo(req, ctx, deps) {
5691
6022
  retryable: false
5692
6023
  });
5693
6024
  }
5694
- const frames = [req.firstFrame, req.lastFrame, ...req.referenceImages ?? []].filter(
5695
- (r) => r !== void 0
5696
- );
5697
- const refUrls = frames.map(refToUrl4).slice(0, 5);
5698
- const resolution = req.resolution === "480p" ? "480p" : "720p";
6025
+ const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 7);
5699
6026
  const xaiOptions = {
5700
- resolution,
5701
6027
  pollIntervalMs: POLL_INTERVAL_MS2,
5702
6028
  pollTimeoutMs: POLL_TIMEOUT_MS2
5703
6029
  };
6030
+ if (req.resolution === "480p" || req.resolution === "720p" || req.resolution === "1080p") {
6031
+ xaiOptions.resolution = req.resolution;
6032
+ }
5704
6033
  if (refUrls.length > 0) {
5705
6034
  xaiOptions.mode = "reference-to-video";
5706
6035
  xaiOptions.referenceImageUrls = refUrls;
5707
6036
  }
6037
+ if (req.lastFrame !== void 0) {
6038
+ ctx.warnings.push({
6039
+ code: "option_ignored",
6040
+ field: "lastFrame",
6041
+ message: "xai-video does not support lastFrame; ignored"
6042
+ });
6043
+ }
5708
6044
  const client2 = deps.createXaiClient({ apiKey });
5709
- const model = (0, import_xai2.createXai)({ apiKey }).video(ctx.model.providerModelId);
6045
+ const model = (0, import_xai2.createXai)({ apiKey, fetch: ctx.fetch }).video(ctx.model.providerModelId);
5710
6046
  const callBody = {
5711
6047
  model,
5712
6048
  prompt: req.prompt,
5713
6049
  providerOptions: { xai: xaiOptions }
5714
6050
  };
6051
+ if (req.firstFrame) {
6052
+ callBody.frameImages = [{ image: refToUrl(req.firstFrame), frameType: "first_frame" }];
6053
+ }
5715
6054
  if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
5716
6055
  callBody.aspectRatio = req.aspectRatio;
5717
6056
  }
@@ -5746,7 +6085,8 @@ async function generateXaiVideo(req, ctx, deps) {
5746
6085
  return settled;
5747
6086
  };
5748
6087
  return {
5749
- jobId: `${ctx.model.id}-grok`,
6088
+ // xAI exposes no request id on the synchronous handle, so each job gets a fresh one.
6089
+ jobId: crypto.randomUUID(),
5750
6090
  async poll() {
5751
6091
  if (failed !== void 0) return { status: "failed" };
5752
6092
  return settled ? { status: "succeeded" } : { status: "pending" };
@@ -5797,26 +6137,14 @@ async function mapResult(ctx, req, result) {
5797
6137
  function usage(ctx, req, meta) {
5798
6138
  const costTicks = import_zod13.z.number().safeParse(meta.costInUsdTicks);
5799
6139
  const cost = costTicks.success ? costTicks.data / 1e10 : void 0;
5800
- if (cost !== void 0) {
5801
- const event2 = {
5802
- model: ctx.model.id,
5803
- provider: "xai-video",
5804
- modality: "VIDEO",
5805
- unit: "reported_cost_usd",
5806
- quantity: cost,
5807
- reportedCostUsd: cost
5808
- };
5809
- if (req.durationSeconds !== void 0) event2.durationSeconds = req.durationSeconds;
5810
- if (req.resolution !== void 0) event2.resolution = req.resolution;
5811
- return event2;
5812
- }
5813
6140
  const event = {
5814
6141
  model: ctx.model.id,
5815
6142
  provider: "xai-video",
5816
6143
  modality: "VIDEO",
5817
- unit: "seconds",
5818
- quantity: req.durationSeconds ?? 0
6144
+ unit: "reported_cost_usd",
6145
+ quantity: cost ?? 0
5819
6146
  };
6147
+ if (cost !== void 0) event.reportedCostUsd = cost;
5820
6148
  if (req.durationSeconds !== void 0) event.durationSeconds = req.durationSeconds;
5821
6149
  if (req.resolution !== void 0) event.resolution = req.resolution;
5822
6150
  return event;
@@ -5893,6 +6221,7 @@ var describe = (modelId) => createDefaultModelKit().describe(modelId);
5893
6221
  prodiaAdapter,
5894
6222
  readErrorBody,
5895
6223
  replicateAdapter,
6224
+ resolveAudioOptions,
5896
6225
  resolveImageOptions,
5897
6226
  resolveKeys,
5898
6227
  resolveVideoOptions,