@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.js CHANGED
@@ -93,7 +93,8 @@ var GEMINI_RATIOS = [
93
93
  "3:4",
94
94
  "21:9",
95
95
  "4:5",
96
- "5:4"
96
+ "5:4",
97
+ "adaptive"
97
98
  ];
98
99
  function geminiImage(id, label, aliases, sizes) {
99
100
  const model = {
@@ -104,7 +105,11 @@ function geminiImage(id, label, aliases, sizes) {
104
105
  adapter: "google-image",
105
106
  providerModelId: id,
106
107
  lifecycle: "active",
107
- constraints: { aspectRatios: [...GEMINI_RATIOS], sizes },
108
+ constraints: {
109
+ aspectRatios: [...GEMINI_RATIOS],
110
+ sizes,
111
+ supports: ["safetySettings", "temperature"]
112
+ },
108
113
  defaults: { size: "1K", aspectRatio: "1:1" },
109
114
  usage: { ...IMAGE_USAGE },
110
115
  pricingKind: "image_size"
@@ -201,7 +206,8 @@ var IMAGE_MODELS = [
201
206
  "9:16",
202
207
  "4:5",
203
208
  "5:4",
204
- "9:21"
209
+ "9:21",
210
+ "adaptive"
205
211
  ],
206
212
  sizes: ["1K"],
207
213
  references: { images: 10 },
@@ -242,8 +248,20 @@ var IMAGE_MODELS = [
242
248
  constraints: {
243
249
  // 97:30 has no Ark size spelling; the A+ flow pads inputs to 97:30 and
244
250
  // omits the ratio instead. Advertise only sizes the adapter can send.
245
- aspectRatios: ["1:1", "2:3", "3:2", "4:3", "3:4", "4:5", "5:4", "9:16", "16:9", "21:9"],
246
- sizes: ["1K", "2K"],
251
+ aspectRatios: [
252
+ "1:1",
253
+ "2:3",
254
+ "3:2",
255
+ "4:3",
256
+ "3:4",
257
+ "4:5",
258
+ "5:4",
259
+ "9:16",
260
+ "16:9",
261
+ "21:9",
262
+ "9:21"
263
+ ],
264
+ sizes: ["1K", "2K", "4K"],
247
265
  references: { images: 10 },
248
266
  supports: ["seed"]
249
267
  },
@@ -262,9 +280,10 @@ var IMAGE_MODELS = [
262
280
  constraints: {
263
281
  aspectRatios: ["1:1", "2:3", "3:2", "16:9", "9:16", "4:3", "3:4", "4:5", "5:4", "21:9"],
264
282
  sizes: ["1K"],
265
- supports: ["outputFormat"]
283
+ // flux-2-pro's job type always returns image/png; only the seed is forwarded.
284
+ supports: ["seed"]
266
285
  },
267
- defaults: { size: "1K", aspectRatio: "1:1", outputFormat: "png" },
286
+ defaults: { size: "1K", aspectRatio: "1:1" },
268
287
  usage: { ...IMAGE_USAGE },
269
288
  pricingKind: "image_size"
270
289
  },
@@ -282,7 +301,8 @@ var IMAGE_MODELS = [
282
301
  constraints: {
283
302
  aspectRatios: ["1:1", "2:3", "3:2", "4:3", "3:4", "4:5", "5:4", "9:16", "16:9", "21:9"],
284
303
  sizes: ["1K", "2K", "4K"],
285
- requires: ["references"]
304
+ requires: ["references"],
305
+ supports: ["seed"]
286
306
  },
287
307
  defaults: { size: "1K", aspectRatio: "1:1" },
288
308
  usage: { ...IMAGE_USAGE },
@@ -301,7 +321,8 @@ var IMAGE_MODELS = [
301
321
  constraints: {
302
322
  aspectRatios: ["1:1", "2:3", "3:4", "4:3", "9:16", "16:9", "21:9"],
303
323
  sizes: ["1K"],
304
- requires: ["references"]
324
+ requires: ["references"],
325
+ supports: ["seed"]
305
326
  },
306
327
  defaults: { size: "1K", aspectRatio: "1:1" },
307
328
  usage: { ...IMAGE_USAGE },
@@ -321,7 +342,8 @@ var IMAGE_MODELS = [
321
342
  constraints: {
322
343
  aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4"],
323
344
  sizes: ["1K", "2K", "4K"],
324
- references: { images: 9 }
345
+ references: { images: 9 },
346
+ supports: []
325
347
  },
326
348
  defaults: { size: "1K", aspectRatio: "1:1" },
327
349
  usage: { unit: "compute_seconds", modality: "IMAGE" },
@@ -342,7 +364,8 @@ var IMAGE_MODELS = [
342
364
  aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"],
343
365
  sizes: [],
344
366
  references: { images: 1 },
345
- requires: ["references"]
367
+ requires: ["references"],
368
+ supports: []
346
369
  },
347
370
  defaults: { aspectRatio: "1:1" },
348
371
  usage: { unit: "compute_seconds", modality: "IMAGE" },
@@ -384,7 +407,8 @@ var OTHER_MODELS = [
384
407
  lifecycle: "active",
385
408
  constraints: {
386
409
  durations: [30],
387
- references: { images: 10 }
410
+ references: { images: 10 },
411
+ supports: []
388
412
  },
389
413
  defaults: {},
390
414
  usage: { unit: "images", modality: "AUDIO" },
@@ -399,7 +423,8 @@ var OTHER_MODELS = [
399
423
  providerModelId: "lyria-3-pro-preview",
400
424
  lifecycle: "active",
401
425
  constraints: {
402
- references: { images: 10 }
426
+ references: { images: 10 },
427
+ supports: []
403
428
  },
404
429
  defaults: {},
405
430
  usage: { unit: "images", modality: "AUDIO" },
@@ -466,7 +491,7 @@ var OTHER_MODELS = [
466
491
  constraints: {
467
492
  references: { images: 1 },
468
493
  requires: ["references"],
469
- supports: ["scaleFactor", "outputFormat"]
494
+ supports: ["scaleFactor", "outputFormat", "upscale"]
470
495
  },
471
496
  defaults: { scaleFactor: 2, outputFormat: "png" },
472
497
  usage: { unit: "compute_seconds", modality: "IMAGE" },
@@ -482,7 +507,8 @@ var OTHER_MODELS = [
482
507
  lifecycle: "active",
483
508
  constraints: {
484
509
  references: { images: 1 },
485
- requires: ["references"]
510
+ requires: ["references"],
511
+ supports: []
486
512
  },
487
513
  defaults: {},
488
514
  usage: { unit: "images", modality: "IMAGE" },
@@ -501,7 +527,7 @@ function text(id, label, providerModelId, extra) {
501
527
  adapter: "ai-sdk-text",
502
528
  providerModelId,
503
529
  lifecycle: "active",
504
- constraints: {},
530
+ constraints: { supports: [] },
505
531
  defaults: {},
506
532
  usage: { ...TEXT_USAGE },
507
533
  pricingKind: "token",
@@ -574,7 +600,7 @@ var TEXT_MODELS = [
574
600
  // src/catalog/video.ts
575
601
  var VIDEO_USAGE = { unit: "seconds", modality: "VIDEO" };
576
602
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
577
- var KLING_RATIOS = ["16:9", "9:16", "1:1"];
603
+ var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
578
604
  var OMNI_RATIOS = ["16:9", "9:16"];
579
605
  var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
580
606
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
@@ -666,12 +692,15 @@ var VIDEO_MODELS = [
666
692
  providerModelId: "gemini-omni-flash-preview",
667
693
  lifecycle: "active",
668
694
  constraints: {
695
+ // 1.0 rejects `resolution`; it renders 720p. No defaults: the adapter
696
+ // sends ratio and duration only when the caller set them, so a
697
+ // reference clip keeps its own framing.
669
698
  aspectRatios: [...OMNI_RATIOS],
670
- resolutions: ["720p"],
671
699
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
672
- references: { images: 6, videos: 1 }
700
+ references: { images: 6, videos: 1 },
701
+ supports: []
673
702
  },
674
- defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
703
+ defaults: {},
675
704
  usage: { ...VIDEO_USAGE },
676
705
  pricingKind: "video_per_second"
677
706
  },
@@ -687,9 +716,10 @@ var VIDEO_MODELS = [
687
716
  aspectRatios: [...OMNI_RATIOS],
688
717
  resolutions: ["360p", "720p", "1080p", "4k"],
689
718
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
690
- references: { images: 6, videos: 1 }
719
+ references: { images: 6, videos: 1 },
720
+ supports: []
691
721
  },
692
- defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
722
+ defaults: {},
693
723
  usage: { ...VIDEO_USAGE },
694
724
  pricingKind: "video_per_second"
695
725
  },
@@ -708,7 +738,7 @@ var VIDEO_MODELS = [
708
738
  durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
709
739
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
710
740
  ratioIgnoredWith: ["firstFrame"],
711
- supports: ["generateAudio", "seed", "lastFrame"]
741
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
712
742
  },
713
743
  defaults: {
714
744
  durationSeconds: 5,
@@ -733,7 +763,7 @@ var VIDEO_MODELS = [
733
763
  durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
734
764
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
735
765
  ratioIgnoredWith: ["firstFrame"],
736
- supports: ["generateAudio", "seed", "lastFrame"]
766
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
737
767
  },
738
768
  defaults: {
739
769
  durationSeconds: 5,
@@ -759,7 +789,7 @@ var VIDEO_MODELS = [
759
789
  durations: { min: 4, max: 30 },
760
790
  references: { images: 30, videos: 10, audios: 10 },
761
791
  ratioIgnoredWith: ["firstFrame"],
762
- supports: ["generateAudio", "seed", "lastFrame"]
792
+ supports: ["generateAudio", "seed", "lastFrame", "watermark"]
763
793
  },
764
794
  defaults: {
765
795
  durationSeconds: 5,
@@ -783,9 +813,11 @@ var VIDEO_MODELS = [
783
813
  aspectRatios: [...GROK_RATIOS],
784
814
  resolutions: ["480p", "720p"],
785
815
  durations: { min: 1, max: 15 },
786
- references: { images: 7 }
816
+ references: { images: 7 },
817
+ supports: []
787
818
  },
788
- defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
819
+ // No resolution default: xAI picks its own unless the caller asks.
820
+ defaults: { durationSeconds: 8, aspectRatio: "16:9" },
789
821
  usage: { unit: "reported_cost_usd", modality: "VIDEO" },
790
822
  pricingKind: "video_per_second"
791
823
  },
@@ -879,7 +911,7 @@ var VIDEO_MODELS = [
879
911
  aspectRatios: [...H3_RATIOS],
880
912
  resolutions: ["480p", "768p", "2k", "4k"],
881
913
  durations: { min: 5, max: 15 },
882
- supports: ["seed", "promptExpansion"]
914
+ supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"]
883
915
  },
884
916
  defaults: {
885
917
  durationSeconds: 5,
@@ -902,7 +934,7 @@ var VIDEO_MODELS = [
902
934
  aspectRatios: [...H3_RATIOS],
903
935
  resolutions: ["480p", "768p"],
904
936
  durations: { min: 5, max: 15 },
905
- supports: ["seed", "promptExpansion"]
937
+ supports: ["seed", "promptExpansion", "lastFrame", "safetyChecker"]
906
938
  },
907
939
  defaults: {
908
940
  durationSeconds: 5,
@@ -926,7 +958,7 @@ var VIDEO_MODELS = [
926
958
  resolutions: ["480p", "768p", "2k", "4k"],
927
959
  durations: { min: 5, max: 15 },
928
960
  references: { images: 9, videos: 3, audios: 3, total: 12, audioOnly: false },
929
- supports: ["seed", "promptExpansion"]
961
+ supports: ["seed", "promptExpansion", "safetyChecker"]
930
962
  },
931
963
  defaults: {
932
964
  durationSeconds: 5,
@@ -950,7 +982,7 @@ var VIDEO_MODELS = [
950
982
  resolutions: ["480p", "768p"],
951
983
  durations: { min: 5, max: 15 },
952
984
  references: { images: 9, videos: 3, audios: 3, total: 12, audioOnly: false },
953
- supports: ["seed", "promptExpansion"]
985
+ supports: ["seed", "promptExpansion", "safetyChecker"]
954
986
  },
955
987
  defaults: {
956
988
  durationSeconds: 5,
@@ -973,7 +1005,8 @@ var VIDEO_MODELS = [
973
1005
  constraints: {
974
1006
  aspectRatios: [...KLING_RATIOS],
975
1007
  durations: { min: 3, max: 10 },
976
- requires: ["sourceVideo"]
1008
+ requires: ["sourceVideo"],
1009
+ supports: []
977
1010
  },
978
1011
  defaults: { durationSeconds: 5, aspectRatio: "16:9" },
979
1012
  usage: { ...VIDEO_USAGE },
@@ -1006,7 +1039,7 @@ var VIDEO_MODELS = [
1006
1039
  lifecycle: "active",
1007
1040
  constraints: {
1008
1041
  requires: ["firstFrame"],
1009
- supports: ["seed"]
1042
+ supports: ["seed", "lastFrame"]
1010
1043
  },
1011
1044
  defaults: {},
1012
1045
  usage: { ...VIDEO_USAGE },
@@ -1077,19 +1110,22 @@ function resolveTier(field, code, requested, supported, order, model, onUnsuppor
1077
1110
  });
1078
1111
  return to;
1079
1112
  }
1080
- function resolveDuration(requested, durations, model, warnings) {
1113
+ function resolveDuration(requested, durations, model, onUnsupported, warnings) {
1081
1114
  if (requested === void 0 || !durations) return requested;
1082
- let to = requested;
1083
- if (Array.isArray(durations)) {
1084
- if (durations.includes(requested)) return requested;
1085
- to = durations.reduce(
1086
- (best, d) => Math.abs(d - requested) < Math.abs(best - requested) ? d : best,
1087
- durations[0] ?? requested
1088
- );
1089
- } else {
1090
- to = Math.min(Math.max(requested, durations.min), durations.max);
1091
- if (to === requested) return requested;
1115
+ const inSet = Array.isArray(durations) ? durations.includes(requested) : requested >= durations.min && requested <= durations.max;
1116
+ if (inSet) return requested;
1117
+ if (onUnsupported === "error") {
1118
+ const supported = Array.isArray(durations) ? `Supported: ${durations.join(", ")}.` : `Supported range: ${durations.min}-${durations.max}.`;
1119
+ throw new ModelKitError({
1120
+ code: "unsupported_option",
1121
+ message: `${model.id} does not support durationSeconds ${requested}. ${supported}`,
1122
+ model: model.id
1123
+ });
1092
1124
  }
1125
+ const to = Array.isArray(durations) ? durations.reduce(
1126
+ (best, d) => Math.abs(d - requested) < Math.abs(best - requested) ? d : best,
1127
+ durations[0] ?? requested
1128
+ ) : Math.min(Math.max(requested, durations.min), durations.max);
1093
1129
  warnings.push({
1094
1130
  code: "duration_coerced",
1095
1131
  field: "durationSeconds",
@@ -1099,6 +1135,26 @@ function resolveDuration(requested, durations, model, warnings) {
1099
1135
  });
1100
1136
  return to;
1101
1137
  }
1138
+ function resolveAspectRatio(requested, supported, model, onUnsupported, warnings) {
1139
+ if (requested === void 0 || requested === "adaptive" || !supported) return requested;
1140
+ if (supported.includes(requested)) return requested;
1141
+ if (onUnsupported === "error") {
1142
+ throw new ModelKitError({
1143
+ code: "unsupported_option",
1144
+ message: `${model.id} does not support aspectRatio "${requested}". Supported: ${supported.join(", ")}.`,
1145
+ model: model.id
1146
+ });
1147
+ }
1148
+ const to = nearestAspectRatio(requested, supported);
1149
+ warnings.push({
1150
+ code: "ratio_coerced",
1151
+ field: "aspectRatio",
1152
+ from: requested,
1153
+ to,
1154
+ message: `aspect ratio ${requested} coerced to ${to} for ${model.id}`
1155
+ });
1156
+ return to;
1157
+ }
1102
1158
  function compact(refs) {
1103
1159
  return (refs ?? []).filter((r) => Boolean(r));
1104
1160
  }
@@ -1117,17 +1173,14 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1117
1173
  const warnings = [];
1118
1174
  const c = model.constraints;
1119
1175
  const merged = { ...model.defaults, ...stripUndefined(request), model: model.id };
1120
- if (merged.aspectRatio && c.aspectRatios && !c.aspectRatios.includes(merged.aspectRatio)) {
1121
- const to = nearestAspectRatio(merged.aspectRatio, c.aspectRatios);
1122
- warnings.push({
1123
- code: "ratio_coerced",
1124
- field: "aspectRatio",
1125
- from: merged.aspectRatio,
1126
- to,
1127
- message: `aspect ratio ${merged.aspectRatio} coerced to ${to} for ${model.id}`
1128
- });
1129
- merged.aspectRatio = to;
1130
- }
1176
+ const aspectRatio = resolveAspectRatio(
1177
+ merged.aspectRatio,
1178
+ c.aspectRatios,
1179
+ model,
1180
+ onUnsupported,
1181
+ warnings
1182
+ );
1183
+ if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
1131
1184
  const size = resolveTier(
1132
1185
  "size",
1133
1186
  "size_coerced",
@@ -1160,7 +1213,10 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1160
1213
  "quality",
1161
1214
  "count",
1162
1215
  "outputFormat",
1163
- "scaleFactor"
1216
+ "scaleFactor",
1217
+ "safetySettings",
1218
+ "temperature",
1219
+ "upscale"
1164
1220
  ]);
1165
1221
  return { request: merged, warnings };
1166
1222
  }
@@ -1190,17 +1246,14 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1190
1246
  delete merged.aspectRatio;
1191
1247
  }
1192
1248
  }
1193
- if (merged.aspectRatio && c.aspectRatios && !c.aspectRatios.includes(merged.aspectRatio)) {
1194
- const to = nearestAspectRatio(merged.aspectRatio, c.aspectRatios);
1195
- warnings.push({
1196
- code: "ratio_coerced",
1197
- field: "aspectRatio",
1198
- from: merged.aspectRatio,
1199
- to,
1200
- message: `aspect ratio ${merged.aspectRatio} coerced to ${to} for ${model.id}`
1201
- });
1202
- merged.aspectRatio = to;
1203
- }
1249
+ const aspectRatio = resolveAspectRatio(
1250
+ merged.aspectRatio,
1251
+ c.aspectRatios,
1252
+ model,
1253
+ onUnsupported,
1254
+ warnings
1255
+ );
1256
+ if (aspectRatio !== void 0) merged.aspectRatio = aspectRatio;
1204
1257
  const resolution = resolveTier(
1205
1258
  "resolution",
1206
1259
  "resolution_coerced",
@@ -1212,7 +1265,13 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1212
1265
  warnings
1213
1266
  );
1214
1267
  if (resolution !== void 0) merged.resolution = resolution;
1215
- const duration = resolveDuration(merged.durationSeconds, c.durations, model, warnings);
1268
+ const duration = resolveDuration(
1269
+ merged.durationSeconds,
1270
+ c.durations,
1271
+ model,
1272
+ onUnsupported,
1273
+ warnings
1274
+ );
1216
1275
  if (duration !== void 0) merged.durationSeconds = duration;
1217
1276
  const limits = c.references ?? {};
1218
1277
  let images = trim(
@@ -1267,13 +1326,47 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1267
1326
  "cfgScale",
1268
1327
  "generateAudio",
1269
1328
  "promptExpansion",
1270
- "lastFrame"
1329
+ "lastFrame",
1330
+ "watermark",
1331
+ "safetyChecker"
1271
1332
  ]);
1272
1333
  return { request: merged, warnings };
1273
1334
  }
1335
+ function resolveAudioOptions(model, request, onUnsupported = "error") {
1336
+ const warnings = [];
1337
+ const c = model.constraints;
1338
+ const merged = { ...model.defaults, ...stripUndefined(request), model: model.id };
1339
+ for (const input of c.requires ?? []) {
1340
+ const present = input === "references" ? compact(merged.references).length > 0 : input === "sourceVideo" && merged.sourceVideo !== void 0;
1341
+ if (!present)
1342
+ throw new ModelKitError({
1343
+ code: "missing_input",
1344
+ message: `${model.id} requires ${input}`,
1345
+ model: model.id
1346
+ });
1347
+ }
1348
+ const duration = resolveDuration(
1349
+ merged.durationSeconds,
1350
+ c.durations,
1351
+ model,
1352
+ onUnsupported,
1353
+ warnings
1354
+ );
1355
+ if (duration !== void 0) merged.durationSeconds = duration;
1356
+ const references = trim(
1357
+ "references",
1358
+ compact(merged.references),
1359
+ c.references?.images ?? c.references?.total,
1360
+ model,
1361
+ warnings
1362
+ );
1363
+ if (references.length > 0) merged.references = references;
1364
+ else delete merged.references;
1365
+ dropUnsupported(merged, model, warnings, ["seed", "negativePrompt", "instrumental"]);
1366
+ return { request: merged, warnings };
1367
+ }
1274
1368
  function dropUnsupported(req, model, warnings, fields) {
1275
- const supports = model.constraints.supports;
1276
- if (!supports) return;
1369
+ const supports = model.constraints.supports ?? [];
1277
1370
  for (const field of fields) {
1278
1371
  if (req[field] !== void 0 && !supports.includes(field)) {
1279
1372
  warnings.push({
@@ -1313,6 +1406,9 @@ function hasAll(keys, names) {
1313
1406
  (n) => Boolean(keys[n]) && (n !== "higgsfield" || Boolean(keys.higgsfieldSecret))
1314
1407
  );
1315
1408
  }
1409
+ function missingEnterpriseKeys(config, names) {
1410
+ return names.filter((name) => !hasAll(config.keys.enterprise, [name]));
1411
+ }
1316
1412
  function resolveKeys(config, names, tier = "pro") {
1317
1413
  if (tier === "enterprise" && hasAll(config.keys.enterprise, names))
1318
1414
  return config.keys.enterprise;
@@ -1345,18 +1441,56 @@ function keysFromEnv(env, prefix = "") {
1345
1441
  set("prodia", pick("PRODIA_API_KEY", "PRODIA_TOKEN"));
1346
1442
  set("higgsfield", pick("HIGGSFIELD_API_KEY"));
1347
1443
  set("higgsfieldSecret", pick("HIGGSFIELD_API_SECRET"));
1348
- set("openrouter", pick("OPENROUTER_API_KEY"));
1349
1444
  set("cloudflare", pick("CLOUDFLARE_AI_GATEWAY_TOKEN", "CF_AIG_TOKEN"));
1350
1445
  return keys;
1351
1446
  }
1352
1447
 
1353
1448
  // src/retry.ts
1449
+ import { z as z2 } from "zod";
1450
+
1451
+ // src/json.ts
1354
1452
  import { z } from "zod";
1355
- var textBody = z.string();
1453
+ var JsonValueSchema = z.lazy(
1454
+ () => z.union([
1455
+ z.string(),
1456
+ z.number(),
1457
+ z.boolean(),
1458
+ z.null(),
1459
+ z.array(JsonValueSchema),
1460
+ z.record(z.string(), JsonValueSchema)
1461
+ ])
1462
+ );
1463
+ var JsonObjectSchema = z.record(z.string(), JsonValueSchema);
1464
+ async function readErrorBody(res) {
1465
+ const text2 = await res.text().catch(() => "");
1466
+ try {
1467
+ return JsonValueSchema.parse(JSON.parse(text2));
1468
+ } catch {
1469
+ return text2;
1470
+ }
1471
+ }
1472
+ var thrownErrorSchema = z.object({
1473
+ message: z.string().optional(),
1474
+ status: z.number().optional(),
1475
+ statusCode: z.number().optional(),
1476
+ responseBody: z.string().optional()
1477
+ });
1478
+ function describeThrown(cause) {
1479
+ const parsed = thrownErrorSchema.safeParse(cause);
1480
+ if (!parsed.success) return { message: String(cause), status: void 0, body: String(cause) };
1481
+ const { message, status, statusCode, responseBody } = parsed.data;
1482
+ const body = responseBody ?? message ?? String(cause);
1483
+ return { message: message ?? String(cause), status: status ?? statusCode, body };
1484
+ }
1485
+
1486
+ // src/retry.ts
1487
+ var textBody = z2.string();
1356
1488
  function classifyHttpError(status, body, provider, model) {
1357
1489
  const asText = textBody.safeParse(body);
1358
1490
  const text2 = asText.success ? asText.data : JSON.stringify(body);
1359
- const blocked = /content_policy|safety|blockReason|PROHIBITED_CONTENT|moderation/i.test(text2);
1491
+ const blocked = /content_policy_violation|blockReason|PROHIBITED_CONTENT|moderation_blocked|safety(?![_A-Za-z])/i.test(
1492
+ text2
1493
+ );
1360
1494
  let code = "provider_rejected";
1361
1495
  if (blocked) code = "content_blocked";
1362
1496
  else if (status === 401 || status === 403) code = "auth";
@@ -1377,8 +1511,8 @@ function classifyHttpError(status, body, provider, model) {
1377
1511
  }
1378
1512
  function shouldRetry(err) {
1379
1513
  if (err instanceof ModelKitError) return err.retryable;
1380
- if (err.name === "AbortError" || /ECONNRESET|ETIMEDOUT|fetch failed|socket hang up/i.test(err.message))
1381
- return true;
1514
+ if (err.name === "AbortError") return false;
1515
+ if (/ECONNRESET|ETIMEDOUT|fetch failed|socket hang up/i.test(err.message)) return true;
1382
1516
  return false;
1383
1517
  }
1384
1518
  async function withRetry(fn, opts = {}) {
@@ -1435,6 +1569,13 @@ function transportError(provider, cause, model) {
1435
1569
  if (model !== void 0) init.model = model;
1436
1570
  return new ModelKitError(init);
1437
1571
  }
1572
+ function classifyThrown(provider, cause, model, override = {}) {
1573
+ if (cause instanceof ModelKitError) return cause;
1574
+ const info = describeThrown(cause);
1575
+ const status = override.status ?? info.status;
1576
+ if (status === void 0) return transportError(provider, cause, model);
1577
+ return classifyHttpError(status, override.body ?? info.body, provider, model);
1578
+ }
1438
1579
  async function pollUntil(check, opts) {
1439
1580
  const start = Date.now();
1440
1581
  let interval = opts.intervalMs;
@@ -1557,12 +1698,18 @@ import {
1557
1698
  streamObject as aiStreamObject2,
1558
1699
  streamText as aiStreamText2
1559
1700
  } from "ai";
1560
- import { z as z3 } from "zod";
1701
+ import { z as z4 } from "zod";
1561
1702
 
1562
1703
  // src/adapters/media-ref.ts
1563
1704
  function isUrlRef(ref) {
1564
1705
  return "url" in ref;
1565
1706
  }
1707
+ function abortedError() {
1708
+ return new ModelKitError({ code: "timeout", message: "aborted", retryable: false });
1709
+ }
1710
+ function throwIfAborted(signal) {
1711
+ if (signal?.aborted) throw abortedError();
1712
+ }
1566
1713
  function mediaRefToBase64(bytes) {
1567
1714
  let binary = "";
1568
1715
  const CHUNK = 32768;
@@ -1577,10 +1724,34 @@ function base64ToBytes(b64) {
1577
1724
  for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);
1578
1725
  return out;
1579
1726
  }
1580
- async function mediaRefToBytes(ref, fetch) {
1727
+ function mediaRefToDataUrl(ref) {
1728
+ if (isUrlRef(ref)) return ref.url;
1729
+ return `data:${ref.mime};base64,${mediaRefToBase64(ref.bytes)}`;
1730
+ }
1731
+ var refToUrl = mediaRefToDataUrl;
1732
+ async function readBody(res) {
1733
+ const text2 = await res.text().catch(() => "");
1734
+ try {
1735
+ const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
1736
+ return decoded.success ? decoded.data : text2;
1737
+ } catch {
1738
+ return text2;
1739
+ }
1740
+ }
1741
+ async function mediaRefToBytes(ref, fetch, options = {}) {
1581
1742
  if (!isUrlRef(ref)) return { bytes: ref.bytes, mime: ref.mime };
1582
- const res = await fetch(ref.url);
1583
- if (!res.ok) throw new Error(`reference fetch failed (${res.status}): ${ref.url}`);
1743
+ const init = {};
1744
+ if (options.signal !== void 0) init.signal = options.signal;
1745
+ const res = await fetch(ref.url, init);
1746
+ if (!res.ok) {
1747
+ const text2 = await res.text().catch(() => "");
1748
+ throw classifyHttpError(
1749
+ res.status,
1750
+ text2,
1751
+ options.provider ?? "media-reference",
1752
+ options.model
1753
+ );
1754
+ }
1584
1755
  const mime = res.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase() || "image/png";
1585
1756
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
1586
1757
  }
@@ -1612,7 +1783,7 @@ import { createPerplexity } from "@ai-sdk/perplexity";
1612
1783
  import { createXai } from "@ai-sdk/xai";
1613
1784
  import { createDeepSeek } from "@ai-sdk/deepseek";
1614
1785
  import { createMoonshotAI } from "@ai-sdk/moonshotai";
1615
- import { z as z2 } from "zod";
1786
+ import { z as z3 } from "zod";
1616
1787
  var TEXT_PROVIDERS = [
1617
1788
  "openai",
1618
1789
  "anthropic",
@@ -1623,7 +1794,7 @@ var TEXT_PROVIDERS = [
1623
1794
  "moonshot",
1624
1795
  "minimax"
1625
1796
  ];
1626
- var textProviderSchema = z2.enum(TEXT_PROVIDERS);
1797
+ var textProviderSchema = z3.enum(TEXT_PROVIDERS);
1627
1798
  function isTextProvider(value) {
1628
1799
  return textProviderSchema.safeParse(value).success;
1629
1800
  }
@@ -1632,22 +1803,12 @@ var OPENAI_COMPATIBLE_BASE_URLS = {
1632
1803
  };
1633
1804
  var GATEWAY_SLUGS = {
1634
1805
  openai: "openai",
1635
- anthropic: "anthropic",
1806
+ anthropic: "anthropic/v1",
1636
1807
  google: "google-ai-studio/v1beta",
1637
- xai: "grok",
1808
+ xai: "grok/v1",
1638
1809
  perplexity: "perplexity-ai",
1639
1810
  deepseek: "deepseek"
1640
1811
  };
1641
- var OPENROUTER_VENDORS = {
1642
- openai: "openai",
1643
- anthropic: "anthropic",
1644
- google: "google",
1645
- xai: "x-ai",
1646
- perplexity: "perplexity",
1647
- deepseek: "deepseek",
1648
- moonshot: "moonshotai",
1649
- minimax: "minimax"
1650
- };
1651
1812
  function detectTextProvider(modelId) {
1652
1813
  const id = modelId.toLowerCase();
1653
1814
  if (id.startsWith("claude") || id.includes("anthropic.")) return "anthropic";
@@ -1664,14 +1825,17 @@ function detectTextProvider(modelId) {
1664
1825
  message: `ai-sdk-text cannot detect a provider for "${modelId}". Set providerOptions.provider to one of ${TEXT_PROVIDERS.join(", ")}.`
1665
1826
  });
1666
1827
  }
1667
- function textKeyName(model, routing) {
1668
- if (routing.text === "openrouter") return "openrouter";
1828
+ function textKeyName(model, _routing) {
1669
1829
  return textProviderOf(model);
1670
1830
  }
1671
1831
  function textProviderOf(model) {
1672
1832
  const explicit = model.providerOptions?.["modelkit"]?.["provider"];
1673
1833
  return isTextProvider(explicit) ? explicit : detectTextProvider(model.providerModelId);
1674
1834
  }
1835
+ function textUsesCloudflareGateway(model, routing) {
1836
+ if (routing.text !== "cloudflare") return false;
1837
+ return GATEWAY_SLUGS[textProviderOf(model)] !== void 0;
1838
+ }
1675
1839
  function nativeClientFor(provider) {
1676
1840
  switch (provider) {
1677
1841
  case "openai":
@@ -1704,16 +1868,6 @@ function resolveTextRoute(ctx) {
1704
1868
  retryable: false
1705
1869
  });
1706
1870
  }
1707
- if (mode === "openrouter") {
1708
- const modelId = ctx.model.providerModelId.includes("/") ? ctx.model.providerModelId : `${OPENROUTER_VENDORS[provider]}/${ctx.model.providerModelId}`;
1709
- return {
1710
- provider,
1711
- client: "openai-compatible",
1712
- modelId,
1713
- keyName: "openrouter",
1714
- baseURL: "https://openrouter.ai/api/v1"
1715
- };
1716
- }
1717
1871
  const compatBase = OPENAI_COMPATIBLE_BASE_URLS[provider];
1718
1872
  const route2 = {
1719
1873
  provider,
@@ -1781,7 +1935,7 @@ var aiSdkTextAdapter = {
1781
1935
  case "openai-compatible":
1782
1936
  return createOpenAI({
1783
1937
  ...settings,
1784
- name: route2.provider === "openai" ? "openai" : ctx.routing.text === "openrouter" ? "openrouter" : route2.provider
1938
+ name: route2.provider
1785
1939
  }).chat(route2.modelId);
1786
1940
  case "openai":
1787
1941
  return createOpenAI(settings)(route2.modelId);
@@ -1794,8 +1948,9 @@ function requiredKeys(model, config) {
1794
1948
  if (model.adapter === "fal" && config.routing?.fal === "cloudflare-passthrough")
1795
1949
  return ["cloudflare"];
1796
1950
  if (model.adapter === "ai-sdk-text") {
1797
- const names = [textKeyName(model, config.routing ?? {})];
1798
- if (config.routing?.text === "cloudflare") names.push("cloudflare");
1951
+ const routing = config.routing ?? {};
1952
+ const names = [textKeyName(model, routing)];
1953
+ if (textUsesCloudflareGateway(model, routing)) names.push("cloudflare");
1799
1954
  return names;
1800
1955
  }
1801
1956
  return ADAPTER_KEYS[model.adapter];
@@ -1809,10 +1964,10 @@ function textAbortOptions(sdk, context) {
1809
1964
  if (abortSignal) return { abortSignal };
1810
1965
  return {};
1811
1966
  }
1812
- var catalogId = z3.string();
1813
- var exactSize = z3.templateLiteral([z3.number(), "x", z3.number()]);
1814
- var sdkRatio = z3.templateLiteral([z3.number(), ":", z3.number()]);
1815
- var sizeTier = z3.enum(["1K", "2K", "4K"]);
1967
+ var catalogId = z4.string();
1968
+ var exactSize = z4.templateLiteral([z4.number(), "x", z4.number()]);
1969
+ var sdkRatio = z4.templateLiteral([z4.number(), ":", z4.number()]);
1970
+ var sizeTier = z4.enum(["512", "1K", "2K", "4K"]);
1816
1971
  function isCatalogId(model) {
1817
1972
  return catalogId.safeParse(model).success;
1818
1973
  }
@@ -1869,6 +2024,15 @@ function pushIgnored(warnings, field, from, modelId) {
1869
2024
  message: `${modelId} provider instance does not support ${field}; ignored`
1870
2025
  });
1871
2026
  }
2027
+ function withoutModelkit(options) {
2028
+ if (!options) return {};
2029
+ const { modelkit: _modelkit, ...rest } = options;
2030
+ return rest;
2031
+ }
2032
+ function logCoercions(logger, modelId, warnings) {
2033
+ if (warnings.length === 0) return;
2034
+ logger.warn(`[modelkit] ${modelId} request coerced`, { model: modelId, warnings });
2035
+ }
1872
2036
  function createModelKit(config, adapters) {
1873
2037
  const catalog = new Catalog(MODELS).withOverrides(config.catalogOverride ?? []);
1874
2038
  const logger = config.logger ?? console;
@@ -1881,7 +2045,16 @@ function createModelKit(config, adapters) {
1881
2045
  message: `No adapter registered for ${model.adapter}`,
1882
2046
  model: model.id
1883
2047
  });
1884
- const keys = resolveKeys(config, requiredKeys(model, config), call.tier);
2048
+ const names = requiredKeys(model, config);
2049
+ if (call.tier === "enterprise") {
2050
+ const missing = missingEnterpriseKeys(config, names);
2051
+ if (missing.length > 0)
2052
+ logger.warn(
2053
+ `[modelkit] enterprise keys incomplete for ${model.adapter}; using pro`,
2054
+ { missing }
2055
+ );
2056
+ }
2057
+ const keys = resolveKeys(config, names, call.tier);
1885
2058
  const ctx = {
1886
2059
  model,
1887
2060
  keys,
@@ -1902,6 +2075,10 @@ function createModelKit(config, adapters) {
1902
2075
  logger.warn("[modelkit] usage emit failed", err);
1903
2076
  }
1904
2077
  }
2078
+ function withRowUnit(event) {
2079
+ const row = catalog.get(event.model);
2080
+ return row ? { ...event, unit: row.usage.unit } : event;
2081
+ }
1905
2082
  function textTarget(model, call) {
1906
2083
  if (!isCatalogId(model)) {
1907
2084
  return {
@@ -1970,6 +2147,7 @@ function createModelKit(config, adapters) {
1970
2147
  pushIgnored(warnings, "outputFormat", _outputFormat, model.modelId);
1971
2148
  if (scaleFactor !== void 0)
1972
2149
  pushIgnored(warnings, "scaleFactor", scaleFactor, model.modelId);
2150
+ logCoercions(logger, model.modelId, warnings);
1973
2151
  const abortSignal = combineSignals(req.abortSignal, call.signal);
1974
2152
  if (abortSignal) options.abortSignal = abortSignal;
1975
2153
  const result = await aiGenerateImage(options);
@@ -2053,6 +2231,7 @@ function createModelKit(config, adapters) {
2053
2231
  if (_mode !== void 0) pushIgnored(warnings, "mode", _mode, model.modelId);
2054
2232
  if (providerOptions) options.providerOptions = providerOptions;
2055
2233
  if (abortSignal) options.abortSignal = abortSignal;
2234
+ logCoercions(logger, model.modelId, warnings);
2056
2235
  const pending = aiGenerateVideo(options);
2057
2236
  let settled;
2058
2237
  let failed = false;
@@ -2111,6 +2290,7 @@ function createModelKit(config, adapters) {
2111
2290
  call.onUnsupported ?? "error"
2112
2291
  );
2113
2292
  warnings.push(...inputWarnings);
2293
+ logCoercions(logger, model.id, warnings);
2114
2294
  const signal = combineSignals(req.abortSignal, call.signal);
2115
2295
  const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
2116
2296
  if (!adapter.generateImage)
@@ -2120,7 +2300,7 @@ function createModelKit(config, adapters) {
2120
2300
  model: model.id
2121
2301
  });
2122
2302
  const result = await adapter.generateImage(request, ctx);
2123
- const usage2 = { ...result.usage, model: model.id };
2303
+ const usage2 = { ...result.usage, model: model.id, unit: model.usage.unit };
2124
2304
  await emit(usage2);
2125
2305
  const images = result.images.map(toGeneratedImage);
2126
2306
  const first = images[0];
@@ -2153,6 +2333,7 @@ function createModelKit(config, adapters) {
2153
2333
  call.onUnsupported ?? "error"
2154
2334
  );
2155
2335
  warnings.push(...inputWarnings);
2336
+ logCoercions(logger, model.id, warnings);
2156
2337
  const signal = combineSignals(req.abortSignal, call.signal);
2157
2338
  const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
2158
2339
  if (!adapter.generateVideo)
@@ -2170,7 +2351,11 @@ function createModelKit(config, adapters) {
2170
2351
  result(opts) {
2171
2352
  if (settled) return settled;
2172
2353
  const pending = result(opts).then(
2173
- (out) => emit({ ...out.usage, model: model.id }).then(() => out),
2354
+ (out) => emit({
2355
+ ...out.usage,
2356
+ model: model.id,
2357
+ unit: model.usage.unit
2358
+ }).then(() => out),
2174
2359
  (cause) => {
2175
2360
  settled = void 0;
2176
2361
  throw cause;
@@ -2190,7 +2375,15 @@ function createModelKit(config, adapters) {
2190
2375
  model: model.id
2191
2376
  });
2192
2377
  }
2193
- const { adapter, ctx } = context(model, call, []);
2378
+ const { abortSignal: _signal, ...request } = req;
2379
+ const { request: resolved, warnings } = resolveAudioOptions(
2380
+ model,
2381
+ request,
2382
+ call.onUnsupported ?? "error"
2383
+ );
2384
+ logCoercions(logger, model.id, warnings);
2385
+ const signal = combineSignals(req.abortSignal, call.signal);
2386
+ const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
2194
2387
  if (!adapter.generateAudio) {
2195
2388
  throw new ModelKitError({
2196
2389
  code: "unsupported_option",
@@ -2198,11 +2391,8 @@ function createModelKit(config, adapters) {
2198
2391
  model: model.id
2199
2392
  });
2200
2393
  }
2201
- const result = await adapter.generateAudio(
2202
- { ...model.defaults, ...req, model: model.id },
2203
- ctx
2204
- );
2205
- await emit({ ...result.usage, model: model.id });
2394
+ const result = await adapter.generateAudio(resolved, ctx);
2395
+ await emit({ ...result.usage, model: model.id, unit: model.usage.unit });
2206
2396
  return result;
2207
2397
  },
2208
2398
  async prepareMediaPart(modelId, ref, mediaType, call = {}) {
@@ -2222,7 +2412,7 @@ function createModelKit(config, adapters) {
2222
2412
  });
2223
2413
  return {
2224
2414
  model,
2225
- providerOptions: entry.providerOptions ?? {},
2415
+ providerOptions: withoutModelkit(entry.providerOptions),
2226
2416
  catalog: entry
2227
2417
  };
2228
2418
  },
@@ -2242,7 +2432,7 @@ function createModelKit(config, adapters) {
2242
2432
  options.prepareStep ?? options.experimental_prepareStep
2243
2433
  )
2244
2434
  });
2245
- for (const event of steps.usage(result.steps)) await emit(event);
2435
+ for (const event of steps.usage(result.steps)) await emit(withRowUnit(event));
2246
2436
  return result;
2247
2437
  },
2248
2438
  streamText(options, call = {}) {
@@ -2258,9 +2448,11 @@ function createModelKit(config, adapters) {
2258
2448
  ...textAbortOptions(options.abortSignal, call.signal),
2259
2449
  model: target.languageModel,
2260
2450
  providerOptions: {},
2261
- prepareStep: steps.wrap(options.prepareStep),
2451
+ prepareStep: steps.wrap(
2452
+ options.prepareStep ?? options.experimental_prepareStep
2453
+ ),
2262
2454
  onFinish: async (event) => {
2263
- for (const usage2 of steps.usage(event.steps)) await emit(usage2);
2455
+ for (const usage2 of steps.usage(event.steps)) await emit(withRowUnit(usage2));
2264
2456
  await callerOnFinish?.(event);
2265
2457
  }
2266
2458
  });
@@ -2274,7 +2466,7 @@ function createModelKit(config, adapters) {
2274
2466
  providerOptions: mergeTextProviderOptions(entry, options.providerOptions)
2275
2467
  };
2276
2468
  const result = await aiGenerateObject2(sdkOptions);
2277
- await emit(textUsageEvent(entry, provider, result.usage));
2469
+ await emit(withRowUnit(textUsageEvent(entry, provider, result.usage)));
2278
2470
  return result;
2279
2471
  },
2280
2472
  streamObject(options, call = {}) {
@@ -2286,7 +2478,7 @@ function createModelKit(config, adapters) {
2286
2478
  model: languageModel2,
2287
2479
  providerOptions: mergeTextProviderOptions(entry, options.providerOptions),
2288
2480
  onFinish: async (event) => {
2289
- await emit(textUsageEvent(entry, provider, event.usage));
2481
+ await emit(withRowUnit(textUsageEvent(entry, provider, event.usage)));
2290
2482
  await callerOnFinish?.(event);
2291
2483
  }
2292
2484
  };
@@ -2296,7 +2488,7 @@ function createModelKit(config, adapters) {
2296
2488
  }
2297
2489
 
2298
2490
  // src/adapters/ark.ts
2299
- import { z as z4 } from "zod";
2491
+ import { z as z5 } from "zod";
2300
2492
  var ARK_BASE_URL = "https://ark.ap-southeast.bytepluses.com/api/v3";
2301
2493
  var SUBMIT_PATH = "/contents/generations/tasks";
2302
2494
  var SEEDANCE_POLL_INTERVAL_MS = 15e3;
@@ -2315,6 +2507,7 @@ var SEEDREAM_SIZES = {
2315
2507
  "3:2": "1248x832",
2316
2508
  "2:3": "832x1248",
2317
2509
  "21:9": "1568x672",
2510
+ "9:21": "672x1568",
2318
2511
  "4:5": "864x1080",
2319
2512
  "5:4": "1080x864"
2320
2513
  },
@@ -2327,16 +2520,34 @@ var SEEDREAM_SIZES = {
2327
2520
  "3:2": "2496x1664",
2328
2521
  "2:3": "1664x2496",
2329
2522
  "21:9": "3136x1344",
2523
+ "9:21": "1344x3136",
2330
2524
  "4:5": "1728x2160",
2331
2525
  "5:4": "2160x1728"
2526
+ },
2527
+ // 4K keeps the long edge at Ark's 4096 cap (a doubled 2K entry would exceed
2528
+ // it); sides are multiples of 16.
2529
+ "4K": {
2530
+ "1:1": "4096x4096",
2531
+ "4:3": "4096x3072",
2532
+ "3:4": "3072x4096",
2533
+ "16:9": "4096x2304",
2534
+ "9:16": "2304x4096",
2535
+ "3:2": "4096x2736",
2536
+ "2:3": "2736x4096",
2537
+ "21:9": "4096x1760",
2538
+ "9:21": "1760x4096",
2539
+ "4:5": "3280x4096",
2540
+ "5:4": "4096x3280"
2332
2541
  }
2333
2542
  };
2334
2543
  var arkAdapter = {
2335
2544
  id: "ark",
2336
2545
  async generateImage(req, ctx) {
2546
+ throwIfAborted(ctx.signal);
2337
2547
  return generateSeedreamImage(req, ctx);
2338
2548
  },
2339
2549
  async generateVideo(req, ctx) {
2550
+ throwIfAborted(ctx.signal);
2340
2551
  return generateSeedanceVideo(req, ctx);
2341
2552
  }
2342
2553
  };
@@ -2354,9 +2565,9 @@ function arkKey(ctx) {
2354
2565
  return key;
2355
2566
  }
2356
2567
  function throwArkError(status, body, model) {
2357
- const parsed = z4.string().safeParse(body);
2568
+ const parsed = z5.string().safeParse(body);
2358
2569
  const text2 = parsed.success ? parsed.data : JSON.stringify(body ?? "");
2359
- if (/sensitive.?content|inputimage|human.?face/i.test(text2)) {
2570
+ if (/sensitive.?content|human.?face/i.test(text2)) {
2360
2571
  const init = {
2361
2572
  code: "content_blocked",
2362
2573
  message: `ark rejected the request: ${text2.slice(0, 500)}`,
@@ -2368,10 +2579,20 @@ function throwArkError(status, body, model) {
2368
2579
  if (status !== void 0) init.status = status;
2369
2580
  throw new ModelKitError(init);
2370
2581
  }
2582
+ if (status === void 0) {
2583
+ throw new ModelKitError({
2584
+ code: "provider_rejected",
2585
+ message: `ark rejected the request: ${text2.slice(0, 500)}`,
2586
+ provider: "ark",
2587
+ model,
2588
+ body,
2589
+ retryable: false
2590
+ });
2591
+ }
2371
2592
  throw classifyHttpError(status, body, "ark", model);
2372
2593
  }
2373
2594
  function seedreamSize(aspect, size) {
2374
- const tier = size === "1K" ? "1K" : "2K";
2595
+ const tier = size === "4K" || size === "1K" ? size : "2K";
2375
2596
  const table = SEEDREAM_SIZES[tier];
2376
2597
  return table[aspect ?? ""] ?? table["1:1"] ?? "2048x2048";
2377
2598
  }
@@ -2382,17 +2603,13 @@ function sniffedImageMime(bytes) {
2382
2603
  return "image/webp";
2383
2604
  return "image/jpeg";
2384
2605
  }
2385
- function arkUrl(ref) {
2386
- if ("url" in ref) return ref.url;
2387
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
2388
- }
2389
- var SeedreamImageSchema = z4.object({
2390
- url: z4.string().optional(),
2391
- b64_json: z4.string().optional()
2606
+ var SeedreamImageSchema = z5.object({
2607
+ url: z5.string().optional(),
2608
+ b64_json: z5.string().optional()
2392
2609
  });
2393
- var SeedreamResponseSchema = z4.object({
2394
- data: z4.array(SeedreamImageSchema).optional(),
2395
- error: z4.object({ message: z4.string().optional(), code: z4.string().optional() }).optional()
2610
+ var SeedreamResponseSchema = z5.object({
2611
+ data: z5.array(SeedreamImageSchema).optional(),
2612
+ error: z5.object({ message: z5.string().optional(), code: z5.string().optional() }).optional()
2396
2613
  });
2397
2614
  function retryOptsFor(ctx, attempts = 3) {
2398
2615
  const opts = { attempts };
@@ -2403,11 +2620,16 @@ function fetchInitFor(ctx, init) {
2403
2620
  if (ctx.signal !== void 0) init.signal = ctx.signal;
2404
2621
  return init;
2405
2622
  }
2623
+ function arkFetch(ctx, url, init) {
2624
+ return ctx.fetch(url, fetchInitFor(ctx, init)).catch((cause) => {
2625
+ throw classifyThrown("ark", cause, ctx.model.id);
2626
+ });
2627
+ }
2406
2628
  async function generateSeedreamImage(req, ctx) {
2407
2629
  const key = arkKey(ctx);
2408
2630
  const modelId = ctx.model.id;
2409
2631
  const size = seedreamSize(req.aspectRatio, req.size);
2410
- const refs = (req.references ?? []).map(arkUrl);
2632
+ const refs = (req.references ?? []).map(refToUrl);
2411
2633
  const body = {
2412
2634
  model: ctx.model.providerModelId,
2413
2635
  prompt: req.prompt,
@@ -2420,14 +2642,11 @@ async function generateSeedreamImage(req, ctx) {
2420
2642
  if (refs.length === 1 && firstRef !== void 0) body.image = firstRef;
2421
2643
  else if (refs.length > 1) body.image = refs;
2422
2644
  const data = await withRetry(async () => {
2423
- const res = await ctx.fetch(
2424
- `${arkBaseUrl(ctx)}/images/generations`,
2425
- fetchInitFor(ctx, {
2426
- method: "POST",
2427
- headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2428
- body: JSON.stringify(body)
2429
- })
2430
- );
2645
+ const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}/images/generations`, {
2646
+ method: "POST",
2647
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2648
+ body: JSON.stringify(body)
2649
+ });
2431
2650
  if (!res.ok) {
2432
2651
  const text2 = await res.text().catch(() => "");
2433
2652
  throwArkError(res.status, text2, modelId);
@@ -2442,7 +2661,7 @@ async function generateSeedreamImage(req, ctx) {
2442
2661
  bytes = Buffer.from(first.b64_json, "base64");
2443
2662
  } else if (first?.url) {
2444
2663
  const imageUrl = first.url;
2445
- const res = await ctx.fetch(imageUrl, fetchInitFor(ctx, {}));
2664
+ const res = await arkFetch(ctx, imageUrl, {});
2446
2665
  if (!res.ok) {
2447
2666
  const text2 = await res.text().catch(() => "");
2448
2667
  throwArkError(res.status, text2, modelId);
@@ -2462,13 +2681,21 @@ async function generateSeedreamImage(req, ctx) {
2462
2681
  if (req.quality !== void 0) usage2.imageQuality = req.quality;
2463
2682
  return { images: [{ bytes, mime: sniffedImageMime(bytes) }], usage: usage2, raw: data };
2464
2683
  }
2465
- var SeedanceResponseSchema = z4.object({
2466
- id: z4.string().optional(),
2467
- status: z4.string().optional(),
2468
- content: z4.object({ video_url: z4.string().optional() }).optional(),
2469
- error: z4.union([z4.string(), z4.object({ message: z4.string().optional() })]).optional()
2684
+ var SeedanceUsageSchema = z5.object({
2685
+ total_tokens: z5.number().optional(),
2686
+ completion_tokens: z5.number().optional()
2470
2687
  });
2471
- var SeedanceErrorMessageSchema = z4.object({ message: z4.string().optional() });
2688
+ var SeedanceResponseSchema = z5.object({
2689
+ id: z5.string().optional(),
2690
+ status: z5.string().optional(),
2691
+ content: z5.object({ video_url: z5.string().optional() }).optional(),
2692
+ usage: SeedanceUsageSchema.optional(),
2693
+ duration: z5.number().optional(),
2694
+ framespersecond: z5.number().optional(),
2695
+ resolution: z5.string().optional(),
2696
+ error: z5.union([z5.string(), z5.object({ message: z5.string().optional() })]).optional()
2697
+ });
2698
+ var SeedanceErrorMessageSchema = z5.object({ message: z5.string().optional() });
2472
2699
  function seedanceUsage(ctx, req, duration) {
2473
2700
  const usage2 = {
2474
2701
  model: ctx.model.id,
@@ -2482,7 +2709,7 @@ function seedanceUsage(ctx, req, duration) {
2482
2709
  return usage2;
2483
2710
  }
2484
2711
  function seedanceErrorMessage(error, status) {
2485
- const asString = z4.string().safeParse(error);
2712
+ const asString = z5.string().safeParse(error);
2486
2713
  if (asString.success) return asString.data;
2487
2714
  const asObject = SeedanceErrorMessageSchema.safeParse(error);
2488
2715
  if (asObject.success && asObject.data.message !== void 0) return asObject.data.message;
@@ -2497,42 +2724,42 @@ async function generateSeedanceVideo(req, ctx) {
2497
2724
  if (req.firstFrame) {
2498
2725
  content.push({
2499
2726
  type: "image_url",
2500
- image_url: { url: arkUrl(req.firstFrame) },
2727
+ image_url: { url: refToUrl(req.firstFrame) },
2501
2728
  role: "first_frame"
2502
2729
  });
2503
2730
  }
2504
2731
  if (req.lastFrame) {
2505
2732
  content.push({
2506
2733
  type: "image_url",
2507
- image_url: { url: arkUrl(req.lastFrame) },
2734
+ image_url: { url: refToUrl(req.lastFrame) },
2508
2735
  role: "last_frame"
2509
2736
  });
2510
2737
  }
2511
2738
  for (const ref of req.referenceImages ?? []) {
2512
2739
  content.push({
2513
2740
  type: "image_url",
2514
- image_url: { url: arkUrl(ref) },
2741
+ image_url: { url: refToUrl(ref) },
2515
2742
  role: "reference_image"
2516
2743
  });
2517
2744
  }
2518
2745
  if (req.sourceVideo) {
2519
2746
  content.push({
2520
2747
  type: "video_url",
2521
- video_url: { url: arkUrl(req.sourceVideo) },
2748
+ video_url: { url: refToUrl(req.sourceVideo) },
2522
2749
  role: "reference_video"
2523
2750
  });
2524
2751
  }
2525
2752
  for (const ref of req.referenceVideos ?? []) {
2526
2753
  content.push({
2527
2754
  type: "video_url",
2528
- video_url: { url: arkUrl(ref) },
2755
+ video_url: { url: refToUrl(ref) },
2529
2756
  role: "reference_video"
2530
2757
  });
2531
2758
  }
2532
2759
  for (const ref of req.referenceAudios ?? []) {
2533
2760
  content.push({
2534
2761
  type: "audio_url",
2535
- audio_url: { url: arkUrl(ref) },
2762
+ audio_url: { url: refToUrl(ref) },
2536
2763
  role: "reference_audio"
2537
2764
  });
2538
2765
  }
@@ -2546,15 +2773,13 @@ async function generateSeedanceVideo(req, ctx) {
2546
2773
  if (!hasFrameAnchor && req.aspectRatio !== void 0) body.ratio = req.aspectRatio;
2547
2774
  if (req.generateAudio !== void 0) body.generate_audio = req.generateAudio;
2548
2775
  if (req.seed !== void 0) body.seed = req.seed;
2776
+ if (req.watermark !== void 0) body.watermark = req.watermark;
2549
2777
  const submit = await withRetry(async () => {
2550
- const res = await ctx.fetch(
2551
- `${arkBaseUrl(ctx)}${SUBMIT_PATH}`,
2552
- fetchInitFor(ctx, {
2553
- method: "POST",
2554
- headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2555
- body: JSON.stringify(body)
2556
- })
2557
- );
2778
+ const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}`, {
2779
+ method: "POST",
2780
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2781
+ body: JSON.stringify(body)
2782
+ });
2558
2783
  if (!res.ok) {
2559
2784
  const text2 = await res.text().catch(() => "");
2560
2785
  throwArkError(res.status, text2, modelId);
@@ -2567,12 +2792,9 @@ async function generateSeedanceVideo(req, ctx) {
2567
2792
  const jobId = submit.id;
2568
2793
  const usage2 = seedanceUsage(ctx, req, duration);
2569
2794
  const fetchStatus = async () => {
2570
- const res = await ctx.fetch(
2571
- `${arkBaseUrl(ctx)}${SUBMIT_PATH}/${jobId}`,
2572
- fetchInitFor(ctx, {
2573
- headers: { Authorization: `Bearer ${key}` }
2574
- })
2575
- );
2795
+ const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}/${jobId}`, {
2796
+ headers: { Authorization: `Bearer ${key}` }
2797
+ });
2576
2798
  if (!res.ok) {
2577
2799
  if (res.status === 429 || res.status >= 500) return void 0;
2578
2800
  const text2 = await res.text().catch(() => "");
@@ -2615,6 +2837,11 @@ async function generateSeedanceVideo(req, ctx) {
2615
2837
  "Seedance task succeeded but returned no video_url",
2616
2838
  modelId
2617
2839
  );
2840
+ const tokens = polled.usage;
2841
+ if (tokens?.total_tokens !== void 0 && tokens.completion_tokens !== void 0) {
2842
+ usage2.inputTokens = tokens.total_tokens - tokens.completion_tokens;
2843
+ usage2.outputTokens = tokens.completion_tokens;
2844
+ }
2618
2845
  return {
2619
2846
  video: { url, expiresAt: new Date(Date.now() + VIDEO_URL_TTL_MS) },
2620
2847
  usage: usage2,
@@ -2633,43 +2860,6 @@ async function generateSeedanceVideo(req, ctx) {
2633
2860
  // src/adapters/fal.ts
2634
2861
  import { z as z6 } from "zod";
2635
2862
  import { createFalClient } from "@fal-ai/client";
2636
-
2637
- // src/json.ts
2638
- import { z as z5 } from "zod";
2639
- var JsonValueSchema = z5.lazy(
2640
- () => z5.union([
2641
- z5.string(),
2642
- z5.number(),
2643
- z5.boolean(),
2644
- z5.null(),
2645
- z5.array(JsonValueSchema),
2646
- z5.record(z5.string(), JsonValueSchema)
2647
- ])
2648
- );
2649
- var JsonObjectSchema = z5.record(z5.string(), JsonValueSchema);
2650
- async function readErrorBody(res) {
2651
- const text2 = await res.text().catch(() => "");
2652
- try {
2653
- return JsonValueSchema.parse(JSON.parse(text2));
2654
- } catch {
2655
- return text2;
2656
- }
2657
- }
2658
- var thrownErrorSchema = z5.object({
2659
- message: z5.string().optional(),
2660
- status: z5.number().optional(),
2661
- statusCode: z5.number().optional(),
2662
- responseBody: z5.string().optional()
2663
- });
2664
- function describeThrown(cause) {
2665
- const parsed = thrownErrorSchema.safeParse(cause);
2666
- if (!parsed.success) return { message: String(cause), status: void 0, body: String(cause) };
2667
- const { message, status, statusCode, responseBody } = parsed.data;
2668
- const body = responseBody ?? message ?? String(cause);
2669
- return { message: message ?? String(cause), status: status ?? statusCode, body };
2670
- }
2671
-
2672
- // src/adapters/fal.ts
2673
2863
  var MUSE_T2I_ENDPOINT = "meta/muse-image/text-to-image";
2674
2864
  var MUSE_EDIT_ENDPOINT = "meta/muse-image/edit";
2675
2865
  var KLING_O1_ENDPOINT = "fal-ai/kling-video/o1/video-to-video/reference";
@@ -2681,10 +2871,33 @@ function defaultCreateFalClient(opts) {
2681
2871
  const client2 = createFalClient(opts);
2682
2872
  return {
2683
2873
  async subscribe(endpoint, options) {
2684
- const outcome = await client2.subscribe(endpoint, { input: options.input });
2874
+ const outcome = await client2.subscribe(endpoint, options);
2685
2875
  const unwrapped = falSubscribePayloadSchema.safeParse(outcome);
2686
2876
  if (unwrapped.success) return unwrapped.data.data;
2687
2877
  return JsonValueSchema.parse(outcome);
2878
+ },
2879
+ queue: {
2880
+ async submit(endpoint, options) {
2881
+ const submitted = await client2.queue.submit(endpoint, {
2882
+ input: options.input,
2883
+ ...options.abortSignal !== void 0 ? { abortSignal: options.abortSignal } : {}
2884
+ });
2885
+ return { requestId: submitted.request_id };
2886
+ },
2887
+ async status(endpoint, options) {
2888
+ const current = await client2.queue.status(endpoint, {
2889
+ requestId: options.requestId,
2890
+ ...options.abortSignal !== void 0 ? { abortSignal: options.abortSignal } : {}
2891
+ });
2892
+ return { status: current.status };
2893
+ },
2894
+ async result(endpoint, options) {
2895
+ const outcome = await client2.queue.result(endpoint, {
2896
+ requestId: options.requestId,
2897
+ ...options.abortSignal !== void 0 ? { abortSignal: options.abortSignal } : {}
2898
+ });
2899
+ return JsonValueSchema.parse(outcome.data);
2900
+ }
2688
2901
  }
2689
2902
  };
2690
2903
  }
@@ -2692,24 +2905,17 @@ var defaultDeps = { createFalClient: defaultCreateFalClient };
2692
2905
  function createFalAdapter(deps = defaultDeps) {
2693
2906
  return {
2694
2907
  id: "fal",
2695
- generateImage: (req, ctx) => generateMuseImage(req, ctx, deps),
2696
- generateVideo: (req, ctx) => generateFalVideo(req, ctx, deps)
2908
+ generateImage: (req, ctx) => {
2909
+ throwIfAborted(ctx.signal);
2910
+ return generateMuseImage(req, ctx, deps);
2911
+ },
2912
+ generateVideo: (req, ctx) => {
2913
+ throwIfAborted(ctx.signal);
2914
+ return generateFalVideo(req, ctx, deps);
2915
+ }
2697
2916
  };
2698
2917
  }
2699
2918
  var falAdapter = createFalAdapter();
2700
- function refToUrl(ref) {
2701
- if ("url" in ref) return ref.url;
2702
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
2703
- }
2704
- async function readBody(res) {
2705
- const text2 = await res.text().catch(() => "");
2706
- try {
2707
- const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
2708
- return decoded.success ? decoded.data : text2;
2709
- } catch {
2710
- return text2;
2711
- }
2712
- }
2713
2919
  var falDetailSchema = z6.object({ detail: JsonValueSchema });
2714
2920
  function liftDetail(body) {
2715
2921
  const parsed = falDetailSchema.safeParse(body);
@@ -2726,15 +2932,17 @@ var falSdkThrownSchema = z6.object({
2726
2932
  message: z6.string().optional()
2727
2933
  });
2728
2934
  function falSdkError(cause, model) {
2729
- if (isModelKitError(cause)) return cause;
2730
2935
  const parsed = falSdkThrownSchema.safeParse(cause);
2731
2936
  if (parsed.success) {
2732
2937
  const status = parsed.data.status ?? parsed.data.statusCode;
2733
2938
  const body = parsed.data.body ?? parsed.data.message ?? String(cause);
2734
- return falError(status, body, model);
2939
+ return classifyThrown("fal", cause, model, { status, body: liftDetail(body) });
2735
2940
  }
2736
2941
  const info = describeThrown(cause);
2737
- return falError(info.status, info.body, model);
2942
+ return classifyThrown("fal", cause, model, {
2943
+ status: info.status,
2944
+ body: liftDetail(info.body)
2945
+ });
2738
2946
  }
2739
2947
  function gatewayConfig(ctx) {
2740
2948
  const cf = ctx.routing.cloudflare;
@@ -2816,7 +3024,7 @@ async function passthroughRequest(ctx, endpoint, input) {
2816
3024
  }
2817
3025
  return JsonValueSchema.parse(await resultRes.json());
2818
3026
  }
2819
- async function subscribe(ctx, deps, endpoint, input) {
3027
+ function falClientOf(ctx, deps) {
2820
3028
  const falKey = ctx.keys.fal;
2821
3029
  if (!falKey) {
2822
3030
  throw new ModelKitError({
@@ -2827,12 +3035,19 @@ async function subscribe(ctx, deps, endpoint, input) {
2827
3035
  retryable: false
2828
3036
  });
2829
3037
  }
2830
- const client2 = deps.createFalClient({ credentials: falKey });
3038
+ return deps.createFalClient({ credentials: falKey, fetch: ctx.fetch });
3039
+ }
3040
+ async function subscribe(ctx, deps, endpoint, input) {
3041
+ const client2 = falClientOf(ctx, deps);
3042
+ const options = {
3043
+ input,
3044
+ ...ctx.signal !== void 0 ? { abortSignal: ctx.signal } : {}
3045
+ };
2831
3046
  try {
2832
3047
  return await withRetry(
2833
3048
  async () => {
2834
3049
  try {
2835
- return await client2.subscribe(endpoint, { input });
3050
+ return await client2.subscribe(endpoint, options);
2836
3051
  } catch (cause) {
2837
3052
  throw falSdkError(cause, ctx.model.id);
2838
3053
  }
@@ -2937,8 +3152,12 @@ async function generateMuseImage(req, ctx, deps) {
2937
3152
  raw: payload
2938
3153
  };
2939
3154
  }
3155
+ var H3_MAX_MODEL_IDS = /* @__PURE__ */ new Set([
3156
+ "minimax/h3-max",
3157
+ "minimax/h3-max/reference-to-video"
3158
+ ]);
2940
3159
  function minimaxFamily(providerModelId) {
2941
- return providerModelId.toLowerCase().includes("h3-max") ? "h3-max" : "h3";
3160
+ return H3_MAX_MODEL_IDS.has(providerModelId.toLowerCase()) ? "h3-max" : "h3";
2942
3161
  }
2943
3162
  function minimaxBase(req, family) {
2944
3163
  const resolution = (family === "h3-max" ? req.resolution ?? "768p" : req.resolution ?? "2k").toUpperCase();
@@ -2946,14 +3165,17 @@ function minimaxBase(req, family) {
2946
3165
  prompt: req.prompt.trim(),
2947
3166
  duration: req.durationSeconds ?? 5,
2948
3167
  resolution,
2949
- prompt_expansion_mode: req.promptExpansion ?? "balanced",
2950
- enable_safety_checker: true
3168
+ prompt_expansion_mode: req.promptExpansion ?? "balanced"
2951
3169
  };
3170
+ if (req.safetyChecker !== void 0) input.enable_safety_checker = req.safetyChecker;
2952
3171
  if (req.seed !== void 0) input.seed = req.seed;
2953
3172
  return input;
2954
3173
  }
3174
+ var KLING_MODEL_IDS = /* @__PURE__ */ new Set([
3175
+ "fal-ai/kling-video/o1/video-to-video/reference"
3176
+ ]);
2955
3177
  function isKling(providerModelId) {
2956
- return providerModelId.toLowerCase().includes("kling");
3178
+ return KLING_MODEL_IDS.has(providerModelId.toLowerCase());
2957
3179
  }
2958
3180
  function klingO1Input(req) {
2959
3181
  const source = req.sourceVideo ? refToUrl(req.sourceVideo) : void 0;
@@ -2966,13 +3188,14 @@ function klingO1Input(req) {
2966
3188
  });
2967
3189
  }
2968
3190
  const duration = Math.min(10, Math.max(3, Math.round(req.durationSeconds ?? 5)));
2969
- const ratio = req.aspectRatio === "16:9" || req.aspectRatio === "9:16" || req.aspectRatio === "1:1" ? req.aspectRatio : "auto";
2970
3191
  const input = {
2971
3192
  prompt: req.prompt,
2972
3193
  video_url: source,
2973
- duration: String(duration),
2974
- aspect_ratio: ratio
3194
+ duration: String(duration)
2975
3195
  };
3196
+ if (req.aspectRatio !== "adaptive") {
3197
+ input.aspect_ratio = req.aspectRatio === "16:9" || req.aspectRatio === "9:16" || req.aspectRatio === "1:1" ? req.aspectRatio : "auto";
3198
+ }
2976
3199
  const refs = (req.referenceImages ?? []).map(refToUrl);
2977
3200
  if (refs.length > 0) input.image_urls = refs;
2978
3201
  return input;
@@ -3001,13 +3224,7 @@ async function generateFalVideo(req, ctx, deps) {
3001
3224
  if (ctx.routing.fal === "cloudflare-passthrough") {
3002
3225
  return await passthroughVideoJob(ctx, req, KLING_O1_ENDPOINT, input);
3003
3226
  }
3004
- const result2 = await runJob(ctx, deps, KLING_O1_ENDPOINT, input);
3005
- return completedVideoJob(
3006
- ctx,
3007
- req,
3008
- extractVideoUrl(result2, KLING_O1_ENDPOINT, ctx.model.id),
3009
- result2
3010
- );
3227
+ return await submitQueueJob(ctx, req, deps, KLING_O1_ENDPOINT, input);
3011
3228
  }
3012
3229
  const family = minimaxFamily(providerModelId);
3013
3230
  const refs = {
@@ -3031,24 +3248,88 @@ async function generateFalVideo(req, ctx, deps) {
3031
3248
  if (lastFrame) base.end_image_url = lastFrame;
3032
3249
  } else {
3033
3250
  endpoint = `minimax/${family}/text-to-video`;
3034
- base.aspect_ratio = req.aspectRatio ?? "adaptive";
3251
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
3252
+ base.aspect_ratio = req.aspectRatio;
3253
+ }
3035
3254
  }
3036
3255
  if (ctx.routing.fal === "cloudflare-passthrough") {
3037
3256
  return await passthroughVideoJob(ctx, req, endpoint, base);
3038
3257
  }
3039
- const result = await runJob(ctx, deps, endpoint, base);
3040
- return completedVideoJob(ctx, req, extractVideoUrl(result, endpoint, ctx.model.id), result);
3258
+ return await submitQueueJob(ctx, req, deps, endpoint, base);
3259
+ }
3260
+ async function submitQueueJob(ctx, req, deps, endpoint, input) {
3261
+ const client2 = falClientOf(ctx, deps);
3262
+ let submitted;
3263
+ try {
3264
+ submitted = await withRetry(
3265
+ async () => {
3266
+ try {
3267
+ return await client2.queue.submit(endpoint, {
3268
+ input,
3269
+ ...ctx.signal !== void 0 ? { abortSignal: ctx.signal } : {}
3270
+ });
3271
+ } catch (cause) {
3272
+ throw falSdkError(cause, ctx.model.id);
3273
+ }
3274
+ },
3275
+ ctx.signal !== void 0 ? { signal: ctx.signal } : {}
3276
+ );
3277
+ } catch (cause) {
3278
+ throw falSdkError(cause, ctx.model.id);
3279
+ }
3280
+ return queuedVideoJob(ctx, req, client2, endpoint, submitted.requestId);
3041
3281
  }
3042
- function completedVideoJob(ctx, req, url, raw) {
3282
+ function queuedVideoJob(ctx, req, client2, endpoint, requestId) {
3283
+ const readStatus = async () => {
3284
+ try {
3285
+ const options = {
3286
+ requestId,
3287
+ ...ctx.signal !== void 0 ? { abortSignal: ctx.signal } : {}
3288
+ };
3289
+ return (await client2.queue.status(endpoint, options)).status;
3290
+ } catch (cause) {
3291
+ throw falSdkError(cause, ctx.model.id);
3292
+ }
3293
+ };
3043
3294
  const duration = req.durationSeconds ?? 5;
3044
- const result = { video: { url }, usage: videoUsage(ctx, req, duration), raw };
3045
3295
  return {
3046
- jobId: url,
3296
+ jobId: requestId,
3047
3297
  async poll() {
3048
- return { status: "succeeded", raw };
3298
+ try {
3299
+ const status = await readStatus();
3300
+ if (status === "COMPLETED") return { status: "succeeded" };
3301
+ if (status === "IN_QUEUE") return { status: "pending" };
3302
+ return { status: "running" };
3303
+ } catch (cause) {
3304
+ if (isModelKitError(cause) && !cause.retryable)
3305
+ return { status: "failed" };
3306
+ return { status: "pending" };
3307
+ }
3049
3308
  },
3050
- async result() {
3051
- return result;
3309
+ async result(opts) {
3310
+ const signal = opts?.signal ?? ctx.signal;
3311
+ const pollOptions = {
3312
+ intervalMs: opts?.pollIntervalMs ?? PASSTHROUGH_POLL_MS,
3313
+ timeoutMs: opts?.timeoutMs ?? 15 * 6e4
3314
+ };
3315
+ if (signal !== void 0) pollOptions.signal = signal;
3316
+ const queueOptions = {
3317
+ requestId,
3318
+ ...signal !== void 0 ? { abortSignal: signal } : {}
3319
+ };
3320
+ const payload = await pollUntil(async () => {
3321
+ try {
3322
+ const current = await client2.queue.status(endpoint, queueOptions);
3323
+ if (current.status !== "COMPLETED") return void 0;
3324
+ return await client2.queue.result(endpoint, queueOptions);
3325
+ } catch (cause) {
3326
+ const err = falSdkError(cause, ctx.model.id);
3327
+ if (err.retryable) return void 0;
3328
+ throw err;
3329
+ }
3330
+ }, pollOptions);
3331
+ const url = extractVideoUrl(payload, endpoint, ctx.model.id);
3332
+ return { video: { url }, usage: videoUsage(ctx, req, duration), raw: payload };
3052
3333
  }
3053
3334
  };
3054
3335
  }
@@ -3401,9 +3682,7 @@ function sdkStatus(cause) {
3401
3682
  return describeThrown(cause).status;
3402
3683
  }
3403
3684
  function throwSdkError(cause, model) {
3404
- if (cause instanceof ModelKitError) throw cause;
3405
- const info = describeThrown(cause);
3406
- throw classifyHttpError(info.status, info.body, "google-genai", model);
3685
+ throw classifyThrown("google-genai", cause, model);
3407
3686
  }
3408
3687
  function toBase64(bytes) {
3409
3688
  return Buffer.from(bytes).toString("base64");
@@ -3459,6 +3738,14 @@ async function generateVeoVideo(req, ctx, createGenAIClient) {
3459
3738
  model: modelId
3460
3739
  });
3461
3740
  }
3741
+ if (req.lastFrame !== void 0 && req.firstFrame === void 0) {
3742
+ throw new ModelKitError({
3743
+ code: "missing_input",
3744
+ message: `${modelId} requires firstFrame when lastFrame is set`,
3745
+ provider: "google-genai",
3746
+ model: modelId
3747
+ });
3748
+ }
3462
3749
  const duration = req.durationSeconds ?? 8;
3463
3750
  const personGeneration = req.firstFrame ?? refs.length > 0 ? "allow_adult" : "allow_all";
3464
3751
  let image;
@@ -3525,7 +3812,7 @@ async function generateVeoVideo(req, ctx, createGenAIClient) {
3525
3812
  throwSdkError(cause, modelId);
3526
3813
  }
3527
3814
  };
3528
- const jobId = operation.name ?? "veo-operation";
3815
+ const jobId = operation.name ?? crypto.randomUUID();
3529
3816
  const usage2 = videoUsage2(ctx, req, duration);
3530
3817
  return {
3531
3818
  jobId,
@@ -3607,12 +3894,17 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3607
3894
  const duration = req.durationSeconds ?? 8;
3608
3895
  const images = req.referenceImages ?? [];
3609
3896
  const videos = req.referenceVideos ?? [];
3610
- 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";
3611
- const responseFormat = { type: "video" };
3612
- if (req.sourceVideo === void 0) responseFormat.aspect_ratio = toOmniAspect(req.aspectRatio);
3613
- const omniResolution = toOmniResolution(req.resolution);
3614
- if (omniResolution !== void 0) responseFormat.resolution = omniResolution;
3615
- responseFormat.duration = `${duration}s`;
3897
+ const task = req.sourceVideo ? "edit" : req.firstFrame ? "image_to_video" : images.length > 0 || videos.length > 0 ? "reference_to_video" : "text_to_video";
3898
+ const isOmniV1 = providerModelId === "gemini-omni-flash-preview";
3899
+ const responseFormat = { type: "video", delivery: "uri" };
3900
+ if (req.sourceVideo === void 0 && req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
3901
+ responseFormat.aspect_ratio = toOmniAspect(req.aspectRatio);
3902
+ }
3903
+ if (!isOmniV1) {
3904
+ const omniResolution = toOmniResolution(req.resolution);
3905
+ if (omniResolution !== void 0) responseFormat.resolution = omniResolution;
3906
+ }
3907
+ if (req.durationSeconds !== void 0) responseFormat.duration = `${req.durationSeconds}s`;
3616
3908
  const client2 = createGenAIClient({ apiKey });
3617
3909
  const uploadedByUri = /* @__PURE__ */ new Map();
3618
3910
  const uploadedNames = [];
@@ -3636,7 +3928,8 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3636
3928
  model: providerModelId,
3637
3929
  input,
3638
3930
  response_format: responseFormat,
3639
- generation_config: { video_config: { task } }
3931
+ generation_config: { video_config: { task } },
3932
+ store: true
3640
3933
  });
3641
3934
  const createRetryOptions = { attempts: 3 };
3642
3935
  if (ctx.signal !== void 0) createRetryOptions.signal = ctx.signal;
@@ -3658,6 +3951,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3658
3951
  });
3659
3952
  }
3660
3953
  for (const ref of videos) {
3954
+ if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
3661
3955
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "video/mp4", modelId);
3662
3956
  parts.push({
3663
3957
  type: "video",
@@ -3666,6 +3960,8 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3666
3960
  });
3667
3961
  }
3668
3962
  if (req.firstFrame) {
3963
+ if (req.firstFrame.label !== void 0)
3964
+ parts.push({ type: "text", text: req.firstFrame.label });
3669
3965
  const media = await resolveMedia(
3670
3966
  req.firstFrame,
3671
3967
  ctx.fetch,
@@ -3684,6 +3980,7 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3684
3980
  }
3685
3981
  }
3686
3982
  for (const ref of images) {
3983
+ if (ref.label !== void 0) parts.push({ type: "text", text: ref.label });
3687
3984
  const media = await resolveMedia(ref, ctx.fetch, ctx.signal, "image/png", modelId);
3688
3985
  if (media.bytes.length > OMNI_INLINE_LIMIT_BYTES) {
3689
3986
  parts.push({
@@ -3718,25 +4015,16 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3718
4015
  };
3719
4016
  return inline;
3720
4017
  });
3721
- const inlineRetryOptions = { attempts: 3 };
3722
- if (ctx.signal !== void 0) inlineRetryOptions.signal = ctx.signal;
3723
4018
  try {
3724
- interaction = await withRetry(async () => {
3725
- try {
3726
- return await callCreate(inlineParts);
3727
- } catch (inlineCause) {
3728
- throwSdkError(inlineCause, modelId);
3729
- }
3730
- }, inlineRetryOptions);
4019
+ interaction = await callCreate(inlineParts);
3731
4020
  } catch (inlineCause) {
3732
4021
  throwSdkError(inlineCause, modelId);
3733
4022
  }
3734
4023
  }
3735
- } catch (cause) {
4024
+ } finally {
3736
4025
  await cleanupUploads();
3737
- throw cause;
3738
4026
  }
3739
- const jobId = interaction.id ?? "gemini-omni-interaction";
4027
+ const jobId = interaction.id ?? crypto.randomUUID();
3740
4028
  const usage2 = videoUsage2(ctx, req, duration);
3741
4029
  const getInteraction = async () => {
3742
4030
  try {
@@ -3763,47 +4051,43 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3763
4051
  timeoutMs: opts?.timeoutMs ?? RESULT_TIMEOUT_MS2
3764
4052
  };
3765
4053
  if (signal !== void 0) pollOptions.signal = signal;
3766
- try {
3767
- const done = await pollUntil(async () => {
3768
- const current = await getInteraction();
3769
- const status = (current.status ?? "").toUpperCase();
3770
- if (status === "COMPLETED") return current;
3771
- if (status === "FAILED") {
3772
- throw classifyHttpError(
3773
- void 0,
3774
- `Gemini Omni interaction failed: ${JSON.stringify(current).slice(0, 500)}`,
3775
- "google-genai",
3776
- modelId
3777
- );
3778
- }
3779
- return void 0;
3780
- }, pollOptions);
3781
- const video = extractOmniVideo(done, modelId);
3782
- if (video.data) {
3783
- return {
3784
- video: {
3785
- bytes: Buffer.from(video.data, "base64"),
3786
- mime: video.mime_type ?? "video/mp4"
3787
- },
3788
- usage: usage2,
3789
- raw: done
3790
- };
4054
+ const done = await pollUntil(async () => {
4055
+ const current = await getInteraction();
4056
+ const status = (current.status ?? "").toUpperCase();
4057
+ if (status === "COMPLETED") return current;
4058
+ if (status === "FAILED") {
4059
+ throw classifyHttpError(
4060
+ void 0,
4061
+ `Gemini Omni interaction failed: ${JSON.stringify(current).slice(0, 500)}`,
4062
+ "google-genai",
4063
+ modelId
4064
+ );
3791
4065
  }
3792
- if (video.uri)
3793
- return {
3794
- video: await downloadVideo(ctx, video.uri, apiKey, signal),
3795
- usage: usage2,
3796
- raw: done
3797
- };
3798
- throw classifyHttpError(
3799
- void 0,
3800
- "No video content in Gemini Omni response",
3801
- "google-genai",
3802
- modelId
3803
- );
3804
- } finally {
3805
- await cleanupUploads();
4066
+ return void 0;
4067
+ }, pollOptions);
4068
+ const video = extractOmniVideo(done, modelId);
4069
+ if (video.data) {
4070
+ return {
4071
+ video: {
4072
+ bytes: Buffer.from(video.data, "base64"),
4073
+ mime: video.mime_type ?? "video/mp4"
4074
+ },
4075
+ usage: usage2,
4076
+ raw: done
4077
+ };
3806
4078
  }
4079
+ if (video.uri)
4080
+ return {
4081
+ video: await downloadVideo(ctx, video.uri, apiKey, signal),
4082
+ usage: usage2,
4083
+ raw: done
4084
+ };
4085
+ throw classifyHttpError(
4086
+ void 0,
4087
+ "No video content in Gemini Omni response",
4088
+ "google-genai",
4089
+ modelId
4090
+ );
3807
4091
  }
3808
4092
  };
3809
4093
  }
@@ -3966,44 +4250,37 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
3966
4250
  const model = google(ctx.model.providerModelId);
3967
4251
  const content = [];
3968
4252
  for (const ref of req.references ?? []) {
4253
+ if (ref.label !== void 0) content.push({ type: "text", text: ref.label });
3969
4254
  if ("url" in ref) content.push({ type: "image", image: ref.url });
3970
4255
  else content.push({ type: "image", image: ref.bytes, mediaType: ref.mime });
3971
4256
  }
3972
4257
  content.push({ type: "text", text: req.prompt });
3973
- void ctx.call.postCropToAspect;
3974
4258
  const imageConfig = {};
3975
- if (req.aspectRatio !== void 0) imageConfig.aspectRatio = req.aspectRatio;
4259
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
4260
+ imageConfig.aspectRatio = req.aspectRatio;
3976
4261
  if (req.size !== void 0) imageConfig.imageSize = req.size;
4262
+ const googleOptions = {
4263
+ imageConfig,
4264
+ responseModalities: ["IMAGE"]
4265
+ };
4266
+ if (req.safetySettings !== void 0) googleOptions.safetySettings = req.safetySettings;
3977
4267
  const retryOptions = {};
3978
4268
  if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
3979
4269
  const result = await withRetry(async () => {
3980
4270
  const timeout = AbortSignal.timeout(GEMINI_IMAGE_TIMEOUT_MS);
3981
4271
  const signal = ctx.signal !== void 0 ? AbortSignal.any([ctx.signal, timeout]) : timeout;
3982
4272
  try {
3983
- return await deps.runTextGeneration({
4273
+ const genOptions = {
3984
4274
  model,
3985
4275
  messages: [{ role: "user", content }],
3986
4276
  maxRetries: 0,
3987
4277
  providerOptions: {
3988
- google: {
3989
- imageConfig,
3990
- responseModalities: ["IMAGE"],
3991
- safetySettings: [
3992
- {
3993
- category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
3994
- threshold: "OFF"
3995
- },
3996
- { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "OFF" },
3997
- {
3998
- category: "HARM_CATEGORY_DANGEROUS_CONTENT",
3999
- threshold: "OFF"
4000
- },
4001
- { category: "HARM_CATEGORY_HARASSMENT", threshold: "OFF" }
4002
- ]
4003
- }
4278
+ google: googleOptions
4004
4279
  },
4005
4280
  abortSignal: signal
4006
- });
4281
+ };
4282
+ if (req.temperature !== void 0) genOptions.temperature = req.temperature;
4283
+ return await deps.runTextGeneration(genOptions);
4007
4284
  } catch (cause) {
4008
4285
  throw toProviderError(cause, ctx);
4009
4286
  }
@@ -4187,6 +4464,7 @@ var KLING_MAX_INTERVAL_MS = 1e4;
4187
4464
  var KLING_TIMEOUT_MS = 15 * 6e4;
4188
4465
  var DOP_INTERVAL_MS = 5e3;
4189
4466
  var DOP_TIMEOUT_MS = 5 * 6e4;
4467
+ var DOP_FALLBACK_DURATION_SECONDS = 5;
4190
4468
  var DEFAULT_NEGATIVE_PROMPT = "text, watermark, copyright, blur, low quality, distortion, abstract, weird, deformed";
4191
4469
  function credentials(ctx) {
4192
4470
  const key = ctx.keys.higgsfield;
@@ -4206,26 +4484,14 @@ function headers(ctx) {
4206
4484
  const { key, secret } = credentials(ctx);
4207
4485
  return { "Content-Type": "application/json", "hf-api-key": key, "hf-secret": secret };
4208
4486
  }
4209
- function refToUrl2(ref) {
4210
- if ("url" in ref) return ref.url;
4211
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
4212
- }
4213
- async function readBody2(res) {
4214
- const text2 = await res.text().catch(() => "");
4215
- try {
4216
- const decoded = JsonValueSchema.safeParse(JSON.parse(text2));
4217
- return decoded.success ? decoded.data : text2;
4218
- } catch {
4219
- return text2;
4220
- }
4221
- }
4222
4487
  function klingEndpoint(providerModelId) {
4223
4488
  if (providerModelId.startsWith("http")) return providerModelId;
4224
4489
  const path = providerModelId.startsWith("/") ? providerModelId.slice(1) : providerModelId;
4225
4490
  return `${PLATFORM}/${path}`;
4226
4491
  }
4227
- function isDop(providerModelId) {
4228
- return providerModelId.toLowerCase().includes("dop");
4492
+ var DOP_MODEL_IDS = /* @__PURE__ */ new Set(["dop", "dop-preview"]);
4493
+ function higgsfieldVideoKind(providerModelId) {
4494
+ return DOP_MODEL_IDS.has(providerModelId.toLowerCase()) ? "dop" : "kling";
4229
4495
  }
4230
4496
  function videoUsage3(ctx, req, duration) {
4231
4497
  const event = {
@@ -4261,8 +4527,20 @@ function requestInit(ctx, init) {
4261
4527
  if (ctx.signal !== void 0) request.signal = ctx.signal;
4262
4528
  return request;
4263
4529
  }
4530
+ async function createRequest(ctx, url, init) {
4531
+ let res;
4532
+ try {
4533
+ res = await ctx.fetch(url, requestInit(ctx, init));
4534
+ } catch (cause) {
4535
+ throw classifyThrown("higgsfield", cause, ctx.model.id);
4536
+ }
4537
+ if (!res.ok) {
4538
+ throw classifyHttpError(res.status, await readBody(res), "higgsfield", ctx.model.id);
4539
+ }
4540
+ return res;
4541
+ }
4264
4542
  async function generateKlingVideo(req, ctx) {
4265
- const firstFrame = req.firstFrame ? refToUrl2(req.firstFrame) : void 0;
4543
+ const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
4266
4544
  if (!firstFrame) {
4267
4545
  throw new ModelKitError({
4268
4546
  code: "missing_input",
@@ -4279,26 +4557,16 @@ async function generateKlingVideo(req, ctx) {
4279
4557
  negative_prompt: req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT
4280
4558
  };
4281
4559
  if (req.durationSeconds !== void 0) body.duration = req.durationSeconds;
4282
- if (req.aspectRatio !== void 0) body.aspect_ratio = req.aspectRatio;
4283
- if (req.lastFrame) body.last_image_url = refToUrl2(req.lastFrame);
4560
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
4561
+ body.aspect_ratio = req.aspectRatio;
4562
+ if (req.lastFrame) body.last_image_url = refToUrl(req.lastFrame);
4284
4563
  const endpoint = klingEndpoint(ctx.model.providerModelId);
4285
- let createRes;
4286
- try {
4287
- createRes = await ctx.fetch(
4288
- endpoint,
4289
- requestInit(ctx, { method: "POST", body: JSON.stringify(body) })
4290
- );
4291
- } catch (cause) {
4292
- throw networkError(cause, ctx);
4293
- }
4294
- if (!createRes.ok) {
4295
- throw classifyHttpError(
4296
- createRes.status,
4297
- await readBody2(createRes),
4298
- "higgsfield",
4299
- ctx.model.id
4300
- );
4301
- }
4564
+ const retryOptions = {};
4565
+ if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
4566
+ const createRes = await withRetry(
4567
+ () => createRequest(ctx, endpoint, { method: "POST", body: JSON.stringify(body) }),
4568
+ retryOptions
4569
+ );
4302
4570
  const created = klingCreateSchema.safeParse(await createRes.json());
4303
4571
  if (!created.success) {
4304
4572
  throw new ModelKitError({
@@ -4317,9 +4585,9 @@ async function generateKlingVideo(req, ctx) {
4317
4585
  } catch {
4318
4586
  return void 0;
4319
4587
  }
4320
- if (res.status >= 500) return void 0;
4588
+ if (res.status === 429 || res.status >= 500) return void 0;
4321
4589
  if (!res.ok) {
4322
- throw classifyHttpError(res.status, await readBody2(res), "higgsfield", ctx.model.id);
4590
+ throw classifyHttpError(res.status, await readBody(res), "higgsfield", ctx.model.id);
4323
4591
  }
4324
4592
  const payload = JsonValueSchema.parse(await res.json());
4325
4593
  const parsed = klingStatusSchema.safeParse(payload);
@@ -4378,11 +4646,6 @@ async function generateKlingVideo(req, ctx) {
4378
4646
  }
4379
4647
  };
4380
4648
  }
4381
- function networkError(cause, ctx) {
4382
- if (isModelKitError(cause)) return cause;
4383
- const info = describeThrown(cause);
4384
- return classifyHttpError(info.status, info.body, "higgsfield", ctx.model.id);
4385
- }
4386
4649
  var dopMotionOptionsSchema = z10.object({
4387
4650
  modelkit: z10.object({ motion: z10.string().optional() }).optional()
4388
4651
  });
@@ -4431,7 +4694,7 @@ function dopUrl(body) {
4431
4694
  var dopCreateSchema = z10.object({ id: z10.string() });
4432
4695
  var dopStatusSchema = z10.object({ status: z10.string().optional() });
4433
4696
  async function generateDopVideo(req, ctx) {
4434
- const firstFrame = req.firstFrame ? refToUrl2(req.firstFrame) : void 0;
4697
+ const firstFrame = req.firstFrame ? refToUrl(req.firstFrame) : void 0;
4435
4698
  if (!firstFrame) {
4436
4699
  throw new ModelKitError({
4437
4700
  code: "missing_input",
@@ -4451,29 +4714,18 @@ async function generateDopVideo(req, ctx) {
4451
4714
  webhook: null
4452
4715
  };
4453
4716
  if (req.lastFrame) {
4454
- params.input_images_end = [{ type: "image_url", image_url: refToUrl2(req.lastFrame) }];
4717
+ params.input_images_end = [{ type: "image_url", image_url: refToUrl(req.lastFrame) }];
4455
4718
  }
4456
4719
  if (req.seed !== void 0) params.seed = req.seed;
4457
- let createRes;
4458
- try {
4459
- createRes = await ctx.fetch(
4460
- DOP_ENDPOINT,
4461
- requestInit(ctx, {
4462
- method: "POST",
4463
- body: JSON.stringify({ params, webhook: null })
4464
- })
4465
- );
4466
- } catch (cause) {
4467
- throw networkError(cause, ctx);
4468
- }
4469
- if (!createRes.ok) {
4470
- throw classifyHttpError(
4471
- createRes.status,
4472
- await readBody2(createRes),
4473
- "higgsfield",
4474
- ctx.model.id
4475
- );
4476
- }
4720
+ const retryOptions = {};
4721
+ if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
4722
+ const createRes = await withRetry(
4723
+ () => createRequest(ctx, DOP_ENDPOINT, {
4724
+ method: "POST",
4725
+ body: JSON.stringify({ params, webhook: null })
4726
+ }),
4727
+ retryOptions
4728
+ );
4477
4729
  const created = dopCreateSchema.safeParse(await createRes.json());
4478
4730
  if (!created.success) {
4479
4731
  throw new ModelKitError({
@@ -4542,14 +4794,20 @@ async function generateDopVideo(req, ctx) {
4542
4794
  };
4543
4795
  if (dopSignal !== void 0) pollSettings.signal = dopSignal;
4544
4796
  const url = await pollUntil(check, pollSettings);
4545
- const result = { video: { url }, usage: videoUsage3(ctx, req) };
4797
+ const result = {
4798
+ video: { url },
4799
+ usage: videoUsage3(ctx, req, req.durationSeconds ?? DOP_FALLBACK_DURATION_SECONDS)
4800
+ };
4546
4801
  return result;
4547
4802
  }
4548
4803
  };
4549
4804
  }
4550
4805
  var higgsfieldAdapter = {
4551
4806
  id: "higgsfield",
4552
- generateVideo: (req, ctx) => isDop(ctx.model.providerModelId) ? generateDopVideo(req, ctx) : generateKlingVideo(req, ctx)
4807
+ generateVideo: (req, ctx) => {
4808
+ throwIfAborted(ctx.signal);
4809
+ return higgsfieldVideoKind(ctx.model.providerModelId) === "dop" ? generateDopVideo(req, ctx) : generateKlingVideo(req, ctx);
4810
+ }
4553
4811
  };
4554
4812
 
4555
4813
  // src/adapters/openai-image.ts
@@ -4644,6 +4902,7 @@ var openaiUsageSchema = z11.object({
4644
4902
  var openaiImageAdapter = {
4645
4903
  id: "openai-image",
4646
4904
  async generateImage(req, ctx) {
4905
+ throwIfAborted(ctx.signal);
4647
4906
  const apiKey = ctx.keys.openai;
4648
4907
  if (!apiKey)
4649
4908
  throw new ModelKitError({
@@ -4653,6 +4912,13 @@ var openaiImageAdapter = {
4653
4912
  model: ctx.model.id,
4654
4913
  retryable: false
4655
4914
  });
4915
+ if ((req.references ?? []).some((ref) => ref.label !== void 0)) {
4916
+ ctx.warnings.push({
4917
+ code: "option_ignored",
4918
+ field: "references.label",
4919
+ message: "openai-image cannot express reference labels; ignoring them"
4920
+ });
4921
+ }
4656
4922
  const client2 = new OpenAI({ apiKey, fetch: ctx.fetch, maxRetries: 0 });
4657
4923
  const size = req.exactSize !== void 0 ? req.exactSize : req.aspectRatio === void 0 || req.aspectRatio === "adaptive" ? void 0 : aspectRatioToSize(req.aspectRatio, req.size ?? "1K");
4658
4924
  const refs = req.references ?? [];
@@ -4666,24 +4932,34 @@ var openaiImageAdapter = {
4666
4932
  try {
4667
4933
  if (!isEdit) {
4668
4934
  if (size !== void 0) {
4669
- return await client2.images.generate({
4935
+ return await client2.images.generate(
4936
+ {
4937
+ model: ctx.model.providerModelId,
4938
+ prompt: req.prompt,
4939
+ size,
4940
+ quality: sdkQuality,
4941
+ ...count
4942
+ },
4943
+ { signal: ctx.signal }
4944
+ );
4945
+ }
4946
+ return await client2.images.generate(
4947
+ {
4670
4948
  model: ctx.model.providerModelId,
4671
4949
  prompt: req.prompt,
4672
- size,
4673
4950
  quality: sdkQuality,
4674
4951
  ...count
4675
- });
4676
- }
4677
- return await client2.images.generate({
4678
- model: ctx.model.providerModelId,
4679
- prompt: req.prompt,
4680
- quality: sdkQuality,
4681
- ...count
4682
- });
4952
+ },
4953
+ { signal: ctx.signal }
4954
+ );
4683
4955
  }
4684
4956
  const files = await Promise.all(
4685
4957
  refs.slice(0, MAX_EDIT_REFERENCES).map(async (ref, i) => {
4686
- const { bytes, mime } = await mediaRefToBytes(ref, ctx.fetch);
4958
+ const { bytes, mime } = await mediaRefToBytes(ref, ctx.fetch, {
4959
+ signal: ctx.signal,
4960
+ provider: "openai-image",
4961
+ model: ctx.model.id
4962
+ });
4687
4963
  const ext = mime.includes("jpeg") ? "jpg" : mime.includes("webp") ? "webp" : "png";
4688
4964
  return new File([bytes], `ref-${i}.${ext}`, { type: mime });
4689
4965
  })
@@ -4691,22 +4967,28 @@ var openaiImageAdapter = {
4691
4967
  const single = files.length === 1 ? files[0] : void 0;
4692
4968
  const imageInput = single ?? files;
4693
4969
  if (size !== void 0) {
4694
- return await client2.images.edit({
4970
+ return await client2.images.edit(
4971
+ {
4972
+ model: ctx.model.providerModelId,
4973
+ image: imageInput,
4974
+ prompt: req.prompt,
4975
+ size,
4976
+ quality: sdkQuality,
4977
+ ...count
4978
+ },
4979
+ { signal: ctx.signal }
4980
+ );
4981
+ }
4982
+ return await client2.images.edit(
4983
+ {
4695
4984
  model: ctx.model.providerModelId,
4696
4985
  image: imageInput,
4697
4986
  prompt: req.prompt,
4698
- size,
4699
4987
  quality: sdkQuality,
4700
4988
  ...count
4701
- });
4702
- }
4703
- return await client2.images.edit({
4704
- model: ctx.model.providerModelId,
4705
- image: imageInput,
4706
- prompt: req.prompt,
4707
- quality: sdkQuality,
4708
- ...count
4709
- });
4989
+ },
4990
+ { signal: ctx.signal }
4991
+ );
4710
4992
  } catch (cause) {
4711
4993
  throw toProviderError2(cause, ctx);
4712
4994
  }
@@ -4724,28 +5006,16 @@ var openaiImageAdapter = {
4724
5006
  const usageParsed = openaiUsageSchema.safeParse(response.usage);
4725
5007
  const inputTokens = usageParsed.success ? usageParsed.data.input_tokens : void 0;
4726
5008
  const outputTokens = usageParsed.success ? usageParsed.data.output_tokens : void 0;
4727
- let usage2;
4728
- if (inputTokens !== void 0 || outputTokens !== void 0) {
4729
- usage2 = {
4730
- model: ctx.model.id,
4731
- provider: "openai-image",
4732
- modality: "IMAGE",
4733
- unit: "tokens",
4734
- quantity: (inputTokens ?? 0) + (outputTokens ?? 0),
4735
- imageQuality: quality
4736
- };
4737
- if (inputTokens !== void 0) usage2.inputTokens = inputTokens;
4738
- if (outputTokens !== void 0) usage2.outputTokens = outputTokens;
4739
- } else {
4740
- usage2 = {
4741
- model: ctx.model.id,
4742
- provider: "openai-image",
4743
- modality: "IMAGE",
4744
- unit: "images",
4745
- quantity: images.length,
4746
- imageQuality: quality
4747
- };
4748
- }
5009
+ const usage2 = {
5010
+ model: ctx.model.id,
5011
+ provider: "openai-image",
5012
+ modality: "IMAGE",
5013
+ unit: "images",
5014
+ quantity: images.length,
5015
+ imageQuality: quality
5016
+ };
5017
+ if (inputTokens !== void 0) usage2.inputTokens = inputTokens;
5018
+ if (outputTokens !== void 0) usage2.outputTokens = outputTokens;
4749
5019
  if (req.size !== void 0) usage2.imageSize = req.size;
4750
5020
  return { images, usage: usage2, raw: response };
4751
5021
  }
@@ -4798,7 +5068,12 @@ var NANO_BANANA_RATIOS = /* @__PURE__ */ new Set([
4798
5068
  ]);
4799
5069
  async function refToBytes(ctx, ref) {
4800
5070
  if ("bytes" in ref) return { bytes: ref.bytes, mime: ref.mime };
4801
- const res = await ctx.fetch(ref.url);
5071
+ let res;
5072
+ try {
5073
+ res = await ctx.fetch(ref.url, ctx.signal ? { signal: ctx.signal } : {});
5074
+ } catch (cause) {
5075
+ throw classifyThrown("prodia", cause, ctx.model.id);
5076
+ }
4802
5077
  if (!res.ok) {
4803
5078
  const text2 = await res.text().catch(() => "");
4804
5079
  throw classifyHttpError(res.status, text2, "prodia", ctx.model.id);
@@ -4841,6 +5116,12 @@ function emptyImage() {
4841
5116
  retryable: false
4842
5117
  });
4843
5118
  }
5119
+ function inputExtension(mime) {
5120
+ if (mime.includes("jpeg") || mime.includes("jpg")) return "jpg";
5121
+ if (mime.includes("webp")) return "webp";
5122
+ if (mime.includes("gif")) return "gif";
5123
+ return "png";
5124
+ }
4844
5125
  function isBgRemove(providerModelId) {
4845
5126
  const id = providerModelId.toLowerCase();
4846
5127
  return id.includes("remove-background") || id.includes("bg-remove");
@@ -4849,6 +5130,7 @@ function isKlein(providerModelId) {
4849
5130
  return providerModelId.toLowerCase().includes("klein");
4850
5131
  }
4851
5132
  async function generateProdiaImage(req, ctx) {
5133
+ throwIfAborted(ctx.signal);
4852
5134
  const key = ctx.keys.prodia;
4853
5135
  if (!key) {
4854
5136
  throw new ModelKitError({
@@ -4947,7 +5229,9 @@ async function generateProdiaImage(req, ctx) {
4947
5229
  "input",
4948
5230
  // Uint8Array<ArrayBufferLike> is not a valid BlobPart under the
4949
5231
  // strict lib types; copy into a fresh ArrayBuffer-backed view.
4950
- new File([new Uint8Array(input.bytes)], `input-${i}.jpg`, { type: "image/jpeg" })
5232
+ new File([new Uint8Array(input.bytes)], `input-${i}.${inputExtension(input.mime)}`, {
5233
+ type: input.mime
5234
+ })
4951
5235
  );
4952
5236
  });
4953
5237
  const retryOpts = {
@@ -4962,14 +5246,15 @@ async function generateProdiaImage(req, ctx) {
4962
5246
  res = await withRetry(async () => {
4963
5247
  let attemptRes;
4964
5248
  try {
4965
- attemptRes = await ctx.fetch(PRODIA_JOB_URL, {
5249
+ const init = {
4966
5250
  method: "POST",
4967
5251
  headers: { Authorization: `Bearer ${key}`, Accept: accept },
4968
5252
  body: form
4969
- });
5253
+ };
5254
+ if (ctx.signal !== void 0) init.signal = ctx.signal;
5255
+ attemptRes = await ctx.fetch(PRODIA_JOB_URL, init);
4970
5256
  } catch (err) {
4971
- if (isModelKitError(err)) throw err;
4972
- throw transportError("prodia", err, ctx.model.id);
5257
+ throw classifyThrown("prodia", err, ctx.model.id);
4973
5258
  }
4974
5259
  if (!attemptRes.ok) {
4975
5260
  const text2 = await attemptRes.text().catch(() => "");
@@ -4978,8 +5263,7 @@ async function generateProdiaImage(req, ctx) {
4978
5263
  return attemptRes;
4979
5264
  }, retryOpts);
4980
5265
  } catch (err) {
4981
- if (isModelKitError(err)) throw err;
4982
- throw transportError("prodia", err, ctx.model.id);
5266
+ throw classifyThrown("prodia", err, ctx.model.id);
4983
5267
  }
4984
5268
  const bytes = parseImageResponse(
4985
5269
  new Uint8Array(await res.arrayBuffer()),
@@ -5029,11 +5313,7 @@ function client(ctx, deps) {
5029
5313
  retryable: false
5030
5314
  });
5031
5315
  }
5032
- return deps.createReplicateClient({ auth: token });
5033
- }
5034
- function refToUrl3(ref) {
5035
- if ("url" in ref) return ref.url;
5036
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
5316
+ return deps.createReplicateClient({ auth: token, fetch: ctx.fetch });
5037
5317
  }
5038
5318
  function route(providerModelId) {
5039
5319
  if (providerModelId.includes(":")) {
@@ -5051,10 +5331,11 @@ function replicateError(cause, ctx) {
5051
5331
  const info = describeThrown(cause);
5052
5332
  const nested = replicateResponseStatusSchema.safeParse(cause);
5053
5333
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
5054
- return classifyHttpError(status, info.body, "replicate", ctx.model.id);
5334
+ return classifyThrown("replicate", cause, ctx.model.id, { status, body: info.body });
5055
5335
  }
5056
- function isLower(id) {
5057
- return id.toLowerCase();
5336
+ function baseModelId(providerModelId) {
5337
+ const colon = providerModelId.indexOf(":");
5338
+ return colon === -1 ? providerModelId : providerModelId.slice(0, colon);
5058
5339
  }
5059
5340
  function predictionErrorBody(cause) {
5060
5341
  const parsed = JsonValueSchema.safeParse(cause);
@@ -5102,6 +5383,7 @@ function noOutput() {
5102
5383
  });
5103
5384
  }
5104
5385
  async function runPrediction(ctx, input, deps) {
5386
+ throwIfAborted(ctx.signal);
5105
5387
  const api = client(ctx, deps);
5106
5388
  const createOptions = { ...route(ctx.model.providerModelId), input };
5107
5389
  let prediction;
@@ -5109,12 +5391,14 @@ async function runPrediction(ctx, input, deps) {
5109
5391
  prediction = await withRetry(
5110
5392
  async () => {
5111
5393
  try {
5112
- return await api.predictions.create(createOptions);
5394
+ return await api.predictions.create(
5395
+ ctx.signal ? { ...createOptions, signal: ctx.signal } : createOptions
5396
+ );
5113
5397
  } catch (cause) {
5114
5398
  throw replicateError(cause, ctx);
5115
5399
  }
5116
5400
  },
5117
- ctx.signal ? { signal: ctx.signal } : {}
5401
+ { attempts: 1 }
5118
5402
  );
5119
5403
  } catch (cause) {
5120
5404
  throw replicateError(cause, ctx);
@@ -5127,7 +5411,10 @@ async function runPrediction(ctx, input, deps) {
5127
5411
  const done = await pollUntil(async () => {
5128
5412
  let current;
5129
5413
  try {
5130
- current = await api.predictions.get(prediction.id);
5414
+ current = await api.predictions.get(
5415
+ prediction.id,
5416
+ ctx.signal ? { signal: ctx.signal } : void 0
5417
+ );
5131
5418
  } catch (cause) {
5132
5419
  throw replicateError(cause, ctx);
5133
5420
  }
@@ -5194,21 +5481,32 @@ async function download(ctx, url) {
5194
5481
  const mime = res.headers.get("content-type") ?? "image/png";
5195
5482
  return { bytes: new Uint8Array(await res.arrayBuffer()), mime };
5196
5483
  }
5197
- function isUpscaler(providerModelId) {
5198
- const id = isLower(providerModelId);
5199
- return id.includes("upscal") || id.includes("crystal") || id.includes("topaz");
5200
- }
5201
- function isTopazUpscaler(providerModelId) {
5202
- return isLower(providerModelId).includes("topazlabs/image-upscale");
5203
- }
5204
- function isQwen(providerModelId) {
5205
- return isLower(providerModelId).includes("qwen");
5206
- }
5207
- function isWanImage(providerModelId) {
5208
- return isLower(providerModelId).includes("wan-2.7");
5484
+ var REPLICATE_IMAGE_KINDS = {
5485
+ "topazlabs/image-upscale": "topaz",
5486
+ "philz1337x/crystal-upscaler": "upscaler",
5487
+ "wan-video/wan-2.7-image-pro": "wan",
5488
+ "qwen/qwen-image-2-pro": "qwen-image",
5489
+ "qwen-edit-apps/qwen-image-edit-plus-lora-skin": "qwen-edit"
5490
+ };
5491
+ function imageKind(providerModelId) {
5492
+ return REPLICATE_IMAGE_KINDS[baseModelId(providerModelId)] ?? "generic";
5493
+ }
5494
+ var REPLICATE_VIDEO_KINDS = {
5495
+ "kling-video/v2.5-turbo/pro/image-to-video": "kling",
5496
+ "kling-video/v3.0/pro/image-to-video": "kling",
5497
+ "kwaivgi/kling-v3-video": "kling",
5498
+ "kwaivgi/kling-v3-omni-video": "kling-omni",
5499
+ "minimax/hailuo-02": "hailuo"
5500
+ };
5501
+ function videoKind(providerModelId) {
5502
+ return REPLICATE_VIDEO_KINDS[baseModelId(providerModelId)] ?? "wan";
5209
5503
  }
5210
- function isQwenEditPlus(providerModelId) {
5211
- return isLower(providerModelId).includes("edit");
5504
+ var REPLICATE_AUDIO_KINDS = {
5505
+ "zsxkib/mmaudio": "mmaudio",
5506
+ "elevenlabs/music": "elevenlabs"
5507
+ };
5508
+ function audioKind(providerModelId) {
5509
+ return REPLICATE_AUDIO_KINDS[baseModelId(providerModelId)] ?? "lyria";
5212
5510
  }
5213
5511
  var QWEN_IMAGE_RATIOS = /* @__PURE__ */ new Set([
5214
5512
  "1:1",
@@ -5251,87 +5549,113 @@ function wanImageSize(aspectRatio, size) {
5251
5549
  async function generateReplicateImage(req, ctx, deps) {
5252
5550
  const id = ctx.model.providerModelId;
5253
5551
  let input;
5254
- if (isTopazUpscaler(id)) {
5255
- const first = (req.references ?? [])[0];
5256
- if (!first) {
5257
- throw new ModelKitError({
5258
- code: "missing_input",
5259
- message: `${ctx.model.id} requires a reference image to upscale`,
5260
- provider: "replicate",
5261
- model: ctx.model.id,
5262
- retryable: false
5263
- });
5552
+ switch (imageKind(id)) {
5553
+ case "topaz": {
5554
+ const first = (req.references ?? [])[0];
5555
+ if (!first) {
5556
+ throw new ModelKitError({
5557
+ code: "missing_input",
5558
+ message: `${ctx.model.id} requires a reference image to upscale`,
5559
+ provider: "replicate",
5560
+ model: ctx.model.id,
5561
+ retryable: false
5562
+ });
5563
+ }
5564
+ let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
5565
+ if (format !== "png" && format !== "jpg") {
5566
+ ctx.warnings.push({
5567
+ code: "option_ignored",
5568
+ field: "outputFormat",
5569
+ from: format,
5570
+ message: "topazlabs/image-upscale supports only png and jpg; using png"
5571
+ });
5572
+ format = "png";
5573
+ }
5574
+ const upscale = req.upscale;
5575
+ const topaz = {
5576
+ image: refToUrl(first),
5577
+ enhance_model: upscale?.enhanceModel ?? "Standard V2",
5578
+ output_format: format,
5579
+ upscale_factor: `${req.scaleFactor ?? 2}x`,
5580
+ face_enhancement: upscale?.faceEnhancement ?? false
5581
+ };
5582
+ if (upscale?.subjectDetection !== void 0)
5583
+ topaz.subject_detection = upscale.subjectDetection;
5584
+ if (upscale?.faceEnhancementCreativity !== void 0)
5585
+ topaz.face_enhancement_creativity = upscale.faceEnhancementCreativity;
5586
+ if (upscale?.faceEnhancementStrength !== void 0)
5587
+ topaz.face_enhancement_strength = upscale.faceEnhancementStrength;
5588
+ input = topaz;
5589
+ break;
5264
5590
  }
5265
- let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
5266
- if (format !== "png" && format !== "jpg") {
5267
- ctx.warnings.push({
5268
- code: "option_ignored",
5269
- field: "outputFormat",
5270
- from: format,
5271
- message: "topazlabs/image-upscale supports only png and jpg; using png"
5272
- });
5273
- format = "png";
5591
+ case "upscaler": {
5592
+ const first = (req.references ?? [])[0];
5593
+ if (!first) {
5594
+ throw new ModelKitError({
5595
+ code: "missing_input",
5596
+ message: `${ctx.model.id} requires a reference image to upscale`,
5597
+ provider: "replicate",
5598
+ model: ctx.model.id,
5599
+ retryable: false
5600
+ });
5601
+ }
5602
+ const imageInput = { prompt: req.prompt };
5603
+ imageInput.image = refToUrl(first);
5604
+ imageInput.creativity = 0;
5605
+ imageInput.scale_factor = 2;
5606
+ imageInput.output_format = "png";
5607
+ input = imageInput;
5608
+ break;
5274
5609
  }
5275
- input = {
5276
- image: refToUrl3(first),
5277
- enhance_model: "Standard V2",
5278
- output_format: format,
5279
- upscale_factor: `${req.scaleFactor ?? 2}x`,
5280
- face_enhancement: true
5281
- };
5282
- } else if (isUpscaler(id)) {
5283
- const refs = req.references ?? [];
5284
- const first = refs[0];
5285
- if (!first) {
5286
- throw new ModelKitError({
5287
- code: "missing_input",
5288
- message: `${ctx.model.id} requires a reference image to upscale`,
5289
- provider: "replicate",
5290
- model: ctx.model.id,
5291
- retryable: false
5292
- });
5610
+ case "wan": {
5611
+ const refs = (req.references ?? []).map(refToUrl);
5612
+ const imageInput = { prompt: req.prompt };
5613
+ if (refs.length > 0) imageInput.images = refs;
5614
+ imageInput.size = wanImageSize(req.aspectRatio, req.size);
5615
+ input = imageInput;
5616
+ break;
5293
5617
  }
5294
- const imageInput = { prompt: req.prompt };
5295
- imageInput.image = refToUrl3(first);
5296
- imageInput.creativity = 0;
5297
- imageInput.scale_factor = 2;
5298
- imageInput.output_format = "png";
5299
- input = imageInput;
5300
- } else if (isWanImage(id)) {
5301
- const refs = (req.references ?? []).map(refToUrl3);
5302
- const imageInput = { prompt: req.prompt };
5303
- if (refs.length > 0) imageInput.images = refs;
5304
- imageInput.size = wanImageSize(req.aspectRatio, req.size);
5305
- input = imageInput;
5306
- } else if (isQwen(id)) {
5307
- const refs = (req.references ?? []).map(refToUrl3);
5308
- const firstUrl = refs[0];
5309
- const imageInput = { prompt: req.prompt };
5310
- if (firstUrl !== void 0) {
5311
- imageInput.image = firstUrl;
5312
- if (!isQwenEditPlus(id)) imageInput.match_input_image = true;
5618
+ case "qwen-image": {
5619
+ const refs = (req.references ?? []).map(refToUrl);
5620
+ const firstUrl = refs[0];
5621
+ const imageInput = { prompt: req.prompt };
5622
+ if (firstUrl !== void 0) {
5623
+ imageInput.image = firstUrl;
5624
+ imageInput.match_input_image = true;
5625
+ } else if (req.aspectRatio !== void 0 && QWEN_IMAGE_RATIOS.has(req.aspectRatio)) {
5626
+ imageInput.aspect_ratio = req.aspectRatio;
5627
+ }
5628
+ input = imageInput;
5629
+ break;
5313
5630
  }
5314
- const ratios = isQwenEditPlus(id) ? QWEN_EDIT_RATIOS : QWEN_IMAGE_RATIOS;
5315
- if (req.aspectRatio !== void 0) {
5316
- if (ratios.has(req.aspectRatio)) imageInput.aspect_ratio = req.aspectRatio;
5317
- else
5318
- ctx.warnings.push({
5319
- code: "option_ignored",
5320
- field: "aspectRatio",
5321
- from: req.aspectRatio,
5322
- message: `${id} has no ${req.aspectRatio} aspect_ratio; using the provider default`
5323
- });
5631
+ case "qwen-edit": {
5632
+ const refs = (req.references ?? []).map(refToUrl);
5633
+ const firstUrl = refs[0];
5634
+ const imageInput = { prompt: req.prompt };
5635
+ if (firstUrl !== void 0) imageInput.image = firstUrl;
5636
+ if (req.aspectRatio !== void 0) {
5637
+ if (QWEN_EDIT_RATIOS.has(req.aspectRatio))
5638
+ imageInput.aspect_ratio = req.aspectRatio;
5639
+ else
5640
+ ctx.warnings.push({
5641
+ code: "option_ignored",
5642
+ field: "aspectRatio",
5643
+ from: req.aspectRatio,
5644
+ message: `${id} has no ${req.aspectRatio} aspect_ratio; using the provider default`
5645
+ });
5646
+ }
5647
+ if (req.outputFormat !== void 0) {
5648
+ imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
5649
+ }
5650
+ input = imageInput;
5651
+ break;
5324
5652
  }
5325
- if (isQwenEditPlus(id) && req.outputFormat !== void 0) {
5326
- imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
5653
+ default: {
5654
+ const first = (req.references ?? [])[0];
5655
+ const imageInput = { prompt: req.prompt };
5656
+ if (first) imageInput.image = refToUrl(first);
5657
+ input = imageInput;
5327
5658
  }
5328
- input = imageInput;
5329
- } else {
5330
- const refs = req.references ?? [];
5331
- const first = refs[0];
5332
- const imageInput = { prompt: req.prompt };
5333
- if (first) imageInput.image = refToUrl3(first);
5334
- input = imageInput;
5335
5659
  }
5336
5660
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5337
5661
  const file = await download(ctx, url);
@@ -5348,8 +5672,8 @@ function wanSize(aspectRatio) {
5348
5672
  return w > h ? "2048*1152" : "1536*2048";
5349
5673
  }
5350
5674
  function klingInput(req) {
5351
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl3);
5352
- if (req.lastFrame) images.push(refToUrl3(req.lastFrame));
5675
+ const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
5676
+ if (req.lastFrame) images.push(refToUrl(req.lastFrame));
5353
5677
  const input = {
5354
5678
  prompt: req.prompt,
5355
5679
  duration: req.durationSeconds ?? 10,
@@ -5361,19 +5685,24 @@ function klingInput(req) {
5361
5685
  input.start_image = startImage;
5362
5686
  const endImage = images[1];
5363
5687
  if (endImage !== void 0) input.end_image = endImage;
5364
- } else {
5688
+ } else if (req.aspectRatio !== "adaptive") {
5365
5689
  input.aspect_ratio = req.aspectRatio ?? "16:9";
5366
5690
  }
5367
5691
  if (req.negativePrompt !== void 0) input.negative_prompt = req.negativePrompt;
5368
5692
  return input;
5369
5693
  }
5694
+ function klingOmniMode(req) {
5695
+ if (req.resolution === "1080p") return "pro";
5696
+ if (req.resolution === "720p") return "standard";
5697
+ return req.mode ?? "standard";
5698
+ }
5370
5699
  function klingOmniInput(req) {
5371
- const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl3);
5372
- if (req.lastFrame) images.push(refToUrl3(req.lastFrame));
5700
+ const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl);
5701
+ if (req.lastFrame) images.push(refToUrl(req.lastFrame));
5373
5702
  const input = {
5374
5703
  prompt: req.prompt,
5375
5704
  duration: req.durationSeconds ?? 10,
5376
- mode: req.mode ?? "standard",
5705
+ mode: klingOmniMode(req),
5377
5706
  generate_audio: req.generateAudio ?? false
5378
5707
  };
5379
5708
  const startImage = images[0];
@@ -5381,10 +5710,10 @@ function klingOmniInput(req) {
5381
5710
  input.start_image = startImage;
5382
5711
  const endImage = images[1];
5383
5712
  if (endImage !== void 0) input.end_image = endImage;
5384
- } else {
5713
+ } else if (req.aspectRatio !== "adaptive") {
5385
5714
  input.aspect_ratio = req.aspectRatio ?? "16:9";
5386
5715
  }
5387
- const refs = (req.referenceImages ?? []).map(refToUrl3);
5716
+ const refs = (req.referenceImages ?? []).map(refToUrl);
5388
5717
  if (refs.length > 0) input.reference_images = refs;
5389
5718
  return input;
5390
5719
  }
@@ -5395,8 +5724,8 @@ function hailuoInput(req) {
5395
5724
  resolution: req.resolution ?? "768p",
5396
5725
  prompt_optimizer: true
5397
5726
  };
5398
- if (req.firstFrame) input.first_frame_image = refToUrl3(req.firstFrame);
5399
- if (req.lastFrame) input.last_frame_image = refToUrl3(req.lastFrame);
5727
+ if (req.firstFrame) input.first_frame_image = refToUrl(req.firstFrame);
5728
+ if (req.lastFrame) input.last_frame_image = refToUrl(req.lastFrame);
5400
5729
  return input;
5401
5730
  }
5402
5731
  function wanInput(req) {
@@ -5405,12 +5734,13 @@ function wanInput(req) {
5405
5734
  duration: req.durationSeconds ?? 5,
5406
5735
  size: wanSize(req.aspectRatio)
5407
5736
  };
5408
- if (req.firstFrame) input.image = refToUrl3(req.firstFrame);
5737
+ if (req.firstFrame) input.image = refToUrl(req.firstFrame);
5409
5738
  return input;
5410
5739
  }
5411
5740
  async function generateReplicateVideo(req, ctx, deps) {
5412
- const id = isLower(ctx.model.providerModelId);
5413
- const input = id.includes("kling") ? id.includes("omni") ? klingOmniInput(req) : klingInput(req) : id.includes("hailuo") ? hailuoInput(req) : wanInput(req);
5741
+ throwIfAborted(ctx.signal);
5742
+ const kind = videoKind(ctx.model.providerModelId);
5743
+ const input = kind === "kling" ? klingInput(req) : kind === "kling-omni" ? klingOmniInput(req) : kind === "hailuo" ? hailuoInput(req) : wanInput(req);
5414
5744
  const api = client(ctx, deps);
5415
5745
  const createOptions = { ...route(ctx.model.providerModelId), input };
5416
5746
  let prediction;
@@ -5418,12 +5748,14 @@ async function generateReplicateVideo(req, ctx, deps) {
5418
5748
  prediction = await withRetry(
5419
5749
  async () => {
5420
5750
  try {
5421
- return await api.predictions.create(createOptions);
5751
+ return await api.predictions.create(
5752
+ ctx.signal ? { ...createOptions, signal: ctx.signal } : createOptions
5753
+ );
5422
5754
  } catch (cause) {
5423
5755
  throw replicateError(cause, ctx);
5424
5756
  }
5425
5757
  },
5426
- ctx.signal ? { signal: ctx.signal } : {}
5758
+ { attempts: 1 }
5427
5759
  );
5428
5760
  } catch (cause) {
5429
5761
  throw replicateError(cause, ctx);
@@ -5432,7 +5764,10 @@ async function generateReplicateVideo(req, ctx, deps) {
5432
5764
  const check = async () => {
5433
5765
  let current;
5434
5766
  try {
5435
- current = await api.predictions.get(jobId);
5767
+ current = await api.predictions.get(
5768
+ jobId,
5769
+ ctx.signal ? { signal: ctx.signal } : void 0
5770
+ );
5436
5771
  } catch (cause) {
5437
5772
  throw replicateError(cause, ctx);
5438
5773
  }
@@ -5533,7 +5868,7 @@ function mmaudioInput(req, ctx) {
5533
5868
  });
5534
5869
  }
5535
5870
  const input = {
5536
- video: refToUrl3(video),
5871
+ video: refToUrl(video),
5537
5872
  duration: req.durationSeconds ?? 8,
5538
5873
  seed: req.seed ?? -1
5539
5874
  };
@@ -5552,8 +5887,8 @@ function predictionRaw(prediction) {
5552
5887
  return raw;
5553
5888
  }
5554
5889
  async function generateReplicateAudio(req, ctx, deps) {
5555
- const id = isLower(ctx.model.providerModelId);
5556
- const input = id.includes("mmaudio") ? mmaudioInput(req, ctx) : id.includes("elevenlabs") ? elevenLabsMusicInput(req, ctx) : lyriaAudioInput(req, ctx);
5890
+ const kind = audioKind(ctx.model.providerModelId);
5891
+ const input = kind === "mmaudio" ? mmaudioInput(req, ctx) : kind === "elevenlabs" ? elevenLabsMusicInput(req, ctx) : lyriaAudioInput(req, ctx);
5557
5892
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5558
5893
  const usage2 = audioUsage(ctx, req);
5559
5894
  if (ctx.model.usage.unit === "seconds") {
@@ -5582,19 +5917,14 @@ function createXaiVideoAdapter(deps = defaultXaiDeps) {
5582
5917
  };
5583
5918
  }
5584
5919
  var xaiVideoAdapter = createXaiVideoAdapter();
5585
- function refToUrl4(ref) {
5586
- if ("url" in ref) return ref.url;
5587
- return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
5588
- }
5589
5920
  var xaiResponseStatusSchema = z13.object({
5590
5921
  response: z13.object({ status: z13.number() }).partial()
5591
5922
  });
5592
5923
  function xaiError(cause, ctx) {
5593
- if (isModelKitError(cause)) return cause;
5594
5924
  const info = describeThrown(cause);
5595
5925
  const nested = xaiResponseStatusSchema.safeParse(cause);
5596
5926
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
5597
- return classifyHttpError(status, info.body, "xai-video", ctx.model.id);
5927
+ return classifyThrown("xai-video", cause, ctx.model.id, { status, body: info.body });
5598
5928
  }
5599
5929
  var xaiMetaSchema = z13.object({
5600
5930
  videoUrl: JsonValueSchema.optional(),
@@ -5618,27 +5948,35 @@ async function generateXaiVideo(req, ctx, deps) {
5618
5948
  retryable: false
5619
5949
  });
5620
5950
  }
5621
- const frames = [req.firstFrame, req.lastFrame, ...req.referenceImages ?? []].filter(
5622
- (r) => r !== void 0
5623
- );
5624
- const refUrls = frames.map(refToUrl4).slice(0, 5);
5625
- const resolution = req.resolution === "480p" ? "480p" : "720p";
5951
+ const refUrls = (req.referenceImages ?? []).map(refToUrl).slice(0, 7);
5626
5952
  const xaiOptions = {
5627
- resolution,
5628
5953
  pollIntervalMs: POLL_INTERVAL_MS2,
5629
5954
  pollTimeoutMs: POLL_TIMEOUT_MS2
5630
5955
  };
5956
+ if (req.resolution === "480p" || req.resolution === "720p" || req.resolution === "1080p") {
5957
+ xaiOptions.resolution = req.resolution;
5958
+ }
5631
5959
  if (refUrls.length > 0) {
5632
5960
  xaiOptions.mode = "reference-to-video";
5633
5961
  xaiOptions.referenceImageUrls = refUrls;
5634
5962
  }
5963
+ if (req.lastFrame !== void 0) {
5964
+ ctx.warnings.push({
5965
+ code: "option_ignored",
5966
+ field: "lastFrame",
5967
+ message: "xai-video does not support lastFrame; ignored"
5968
+ });
5969
+ }
5635
5970
  const client2 = deps.createXaiClient({ apiKey });
5636
- const model = createXai2({ apiKey }).video(ctx.model.providerModelId);
5971
+ const model = createXai2({ apiKey, fetch: ctx.fetch }).video(ctx.model.providerModelId);
5637
5972
  const callBody = {
5638
5973
  model,
5639
5974
  prompt: req.prompt,
5640
5975
  providerOptions: { xai: xaiOptions }
5641
5976
  };
5977
+ if (req.firstFrame) {
5978
+ callBody.frameImages = [{ image: refToUrl(req.firstFrame), frameType: "first_frame" }];
5979
+ }
5642
5980
  if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
5643
5981
  callBody.aspectRatio = req.aspectRatio;
5644
5982
  }
@@ -5673,7 +6011,8 @@ async function generateXaiVideo(req, ctx, deps) {
5673
6011
  return settled;
5674
6012
  };
5675
6013
  return {
5676
- jobId: `${ctx.model.id}-grok`,
6014
+ // xAI exposes no request id on the synchronous handle, so each job gets a fresh one.
6015
+ jobId: crypto.randomUUID(),
5677
6016
  async poll() {
5678
6017
  if (failed !== void 0) return { status: "failed" };
5679
6018
  return settled ? { status: "succeeded" } : { status: "pending" };
@@ -5724,26 +6063,14 @@ async function mapResult(ctx, req, result) {
5724
6063
  function usage(ctx, req, meta) {
5725
6064
  const costTicks = z13.number().safeParse(meta.costInUsdTicks);
5726
6065
  const cost = costTicks.success ? costTicks.data / 1e10 : void 0;
5727
- if (cost !== void 0) {
5728
- const event2 = {
5729
- model: ctx.model.id,
5730
- provider: "xai-video",
5731
- modality: "VIDEO",
5732
- unit: "reported_cost_usd",
5733
- quantity: cost,
5734
- reportedCostUsd: cost
5735
- };
5736
- if (req.durationSeconds !== void 0) event2.durationSeconds = req.durationSeconds;
5737
- if (req.resolution !== void 0) event2.resolution = req.resolution;
5738
- return event2;
5739
- }
5740
6066
  const event = {
5741
6067
  model: ctx.model.id,
5742
6068
  provider: "xai-video",
5743
6069
  modality: "VIDEO",
5744
- unit: "seconds",
5745
- quantity: req.durationSeconds ?? 0
6070
+ unit: "reported_cost_usd",
6071
+ quantity: cost ?? 0
5746
6072
  };
6073
+ if (cost !== void 0) event.reportedCostUsd = cost;
5747
6074
  if (req.durationSeconds !== void 0) event.durationSeconds = req.durationSeconds;
5748
6075
  if (req.resolution !== void 0) event.resolution = req.resolution;
5749
6076
  return event;
@@ -5819,6 +6146,7 @@ export {
5819
6146
  prodiaAdapter,
5820
6147
  readErrorBody,
5821
6148
  replicateAdapter,
6149
+ resolveAudioOptions,
5822
6150
  resolveImageOptions,
5823
6151
  resolveKeys,
5824
6152
  resolveVideoOptions,