@shop-os/modelkit 0.1.3 → 0.1.5

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
@@ -83,7 +83,19 @@ var Catalog = class _Catalog {
83
83
 
84
84
  // src/catalog/image.ts
85
85
  var IMAGE_USAGE = { unit: "images", modality: "IMAGE" };
86
- var GEMINI_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "21:9", "4:5", "5:4"];
86
+ var GEMINI_RATIOS = [
87
+ "1:1",
88
+ "2:3",
89
+ "3:2",
90
+ "16:9",
91
+ "9:16",
92
+ "4:3",
93
+ "3:4",
94
+ "21:9",
95
+ "4:5",
96
+ "5:4",
97
+ "adaptive"
98
+ ];
87
99
  function geminiImage(id, label, aliases, sizes) {
88
100
  const model = {
89
101
  id,
@@ -93,7 +105,7 @@ function geminiImage(id, label, aliases, sizes) {
93
105
  adapter: "google-image",
94
106
  providerModelId: id,
95
107
  lifecycle: "active",
96
- constraints: { aspectRatios: [...GEMINI_RATIOS], sizes },
108
+ constraints: { aspectRatios: [...GEMINI_RATIOS], sizes, supports: ["safetySettings"] },
97
109
  defaults: { size: "1K", aspectRatio: "1:1" },
98
110
  usage: { ...IMAGE_USAGE },
99
111
  pricingKind: "image_size"
@@ -106,7 +118,8 @@ var IMAGE_MODELS = [
106
118
  "gemini-3.1-flash-image-preview",
107
119
  "Nano Banana Flash",
108
120
  ["nb-2"],
109
- ["1K", "2K", "4K"]
121
+ // Flash 3.1 is the only Gemini image model with the 512 tier.
122
+ ["512", "1K", "2K", "4K"]
110
123
  ),
111
124
  geminiImage("gemini-3.1-flash-lite-image", "Nano Banana Lite", ["nb-lite"], ["1K"]),
112
125
  // trigger-workflows validates 1K only; pricing seeds 2K/4K rows so allow all three.
@@ -189,7 +202,8 @@ var IMAGE_MODELS = [
189
202
  "9:16",
190
203
  "4:5",
191
204
  "5:4",
192
- "9:21"
205
+ "9:21",
206
+ "adaptive"
193
207
  ],
194
208
  sizes: ["1K"],
195
209
  references: { images: 10 },
@@ -250,9 +264,10 @@ var IMAGE_MODELS = [
250
264
  constraints: {
251
265
  aspectRatios: ["1:1", "2:3", "3:2", "16:9", "9:16", "4:3", "3:4", "4:5", "5:4", "21:9"],
252
266
  sizes: ["1K"],
253
- supports: ["outputFormat"]
267
+ // flux-2-pro's job type always returns image/png; only the seed is forwarded.
268
+ supports: ["seed"]
254
269
  },
255
- defaults: { size: "1K", aspectRatio: "1:1", outputFormat: "png" },
270
+ defaults: { size: "1K", aspectRatio: "1:1" },
256
271
  usage: { ...IMAGE_USAGE },
257
272
  pricingKind: "image_size"
258
273
  },
@@ -270,7 +285,8 @@ var IMAGE_MODELS = [
270
285
  constraints: {
271
286
  aspectRatios: ["1:1", "2:3", "3:2", "4:3", "3:4", "4:5", "5:4", "9:16", "16:9", "21:9"],
272
287
  sizes: ["1K", "2K", "4K"],
273
- requires: ["references"]
288
+ requires: ["references"],
289
+ supports: ["seed"]
274
290
  },
275
291
  defaults: { size: "1K", aspectRatio: "1:1" },
276
292
  usage: { ...IMAGE_USAGE },
@@ -289,7 +305,8 @@ var IMAGE_MODELS = [
289
305
  constraints: {
290
306
  aspectRatios: ["1:1", "2:3", "3:4", "4:3", "9:16", "16:9", "21:9"],
291
307
  sizes: ["1K"],
292
- requires: ["references"]
308
+ requires: ["references"],
309
+ supports: ["seed"]
293
310
  },
294
311
  defaults: { size: "1K", aspectRatio: "1:1" },
295
312
  usage: { ...IMAGE_USAGE },
@@ -309,7 +326,8 @@ var IMAGE_MODELS = [
309
326
  constraints: {
310
327
  aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4"],
311
328
  sizes: ["1K", "2K", "4K"],
312
- references: { images: 9 }
329
+ references: { images: 9 },
330
+ supports: []
313
331
  },
314
332
  defaults: { size: "1K", aspectRatio: "1:1" },
315
333
  usage: { unit: "compute_seconds", modality: "IMAGE" },
@@ -330,7 +348,8 @@ var IMAGE_MODELS = [
330
348
  aspectRatios: ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"],
331
349
  sizes: [],
332
350
  references: { images: 1 },
333
- requires: ["references"]
351
+ requires: ["references"],
352
+ supports: []
334
353
  },
335
354
  defaults: { aspectRatio: "1:1" },
336
355
  usage: { unit: "compute_seconds", modality: "IMAGE" },
@@ -372,7 +391,8 @@ var OTHER_MODELS = [
372
391
  lifecycle: "active",
373
392
  constraints: {
374
393
  durations: [30],
375
- references: { images: 10 }
394
+ references: { images: 10 },
395
+ supports: []
376
396
  },
377
397
  defaults: {},
378
398
  usage: { unit: "images", modality: "AUDIO" },
@@ -387,7 +407,8 @@ var OTHER_MODELS = [
387
407
  providerModelId: "lyria-3-pro-preview",
388
408
  lifecycle: "active",
389
409
  constraints: {
390
- references: { images: 10 }
410
+ references: { images: 10 },
411
+ supports: []
391
412
  },
392
413
  defaults: {},
393
414
  usage: { unit: "images", modality: "AUDIO" },
@@ -451,8 +472,12 @@ var OTHER_MODELS = [
451
472
  adapter: "replicate",
452
473
  providerModelId: "topazlabs/image-upscale",
453
474
  lifecycle: "active",
454
- constraints: {},
455
- defaults: {},
475
+ constraints: {
476
+ references: { images: 1 },
477
+ requires: ["references"],
478
+ supports: ["scaleFactor", "outputFormat"]
479
+ },
480
+ defaults: { scaleFactor: 2, outputFormat: "png" },
456
481
  usage: { unit: "compute_seconds", modality: "IMAGE" },
457
482
  pricingKind: "flat"
458
483
  },
@@ -466,7 +491,8 @@ var OTHER_MODELS = [
466
491
  lifecycle: "active",
467
492
  constraints: {
468
493
  references: { images: 1 },
469
- requires: ["references"]
494
+ requires: ["references"],
495
+ supports: []
470
496
  },
471
497
  defaults: {},
472
498
  usage: { unit: "images", modality: "IMAGE" },
@@ -485,7 +511,7 @@ function text(id, label, providerModelId, extra) {
485
511
  adapter: "ai-sdk-text",
486
512
  providerModelId,
487
513
  lifecycle: "active",
488
- constraints: {},
514
+ constraints: { supports: [] },
489
515
  defaults: {},
490
516
  usage: { ...TEXT_USAGE },
491
517
  pricingKind: "token",
@@ -508,7 +534,7 @@ var TEXT_MODELS = [
508
534
  text("gpt-5.6-sol", "GPT-5.6 Sol", "gpt-5.6-sol"),
509
535
  // Library key "claude-sonnet-4.5" maps to the SDK id "claude-sonnet-4-5".
510
536
  text("claude-sonnet-4-5", "Claude Sonnet 4.5", "claude-sonnet-4-5", {
511
- aliases: ["claude-sonnet-4.5"]
537
+ aliases: ["claude-sonnet-4.5", "claude-sonnet-4-5-20250929"]
512
538
  }),
513
539
  // Anthropic retired the dated 4.0 ids; callers that still send them get 4.6.
514
540
  text("claude-sonnet-4-6", "Claude Sonnet 4.6", "claude-sonnet-4-6", {
@@ -524,7 +550,7 @@ var TEXT_MODELS = [
524
550
  aliases: ["claude-opus-4.8", "claude-opus-4", "claude-opus-4.0", "claude-opus-4-20250514"]
525
551
  }),
526
552
  text("claude-haiku-4-5", "Claude Haiku 4.5", "claude-haiku-4-5", {
527
- aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001"]
553
+ aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001", "claude-haiku-4"]
528
554
  }),
529
555
  text("claude-opus-5", "Claude Opus 5", "claude-opus-5", {
530
556
  aliases: ["claude-opus-5.0"]
@@ -558,7 +584,7 @@ var TEXT_MODELS = [
558
584
  // src/catalog/video.ts
559
585
  var VIDEO_USAGE = { unit: "seconds", modality: "VIDEO" };
560
586
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
561
- var KLING_RATIOS = ["16:9", "9:16", "1:1"];
587
+ var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
562
588
  var OMNI_RATIOS = ["16:9", "9:16"];
563
589
  var GROK_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3"];
564
590
  var H3_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16"];
@@ -650,12 +676,15 @@ var VIDEO_MODELS = [
650
676
  providerModelId: "gemini-omni-flash-preview",
651
677
  lifecycle: "active",
652
678
  constraints: {
679
+ // 1.0 rejects `resolution`; it renders 720p. No defaults: the adapter
680
+ // sends ratio and duration only when the caller set them, so a
681
+ // reference clip keeps its own framing.
653
682
  aspectRatios: [...OMNI_RATIOS],
654
- resolutions: ["720p"],
655
683
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
656
- references: { images: 6, videos: 1 }
684
+ references: { images: 6, videos: 1 },
685
+ supports: []
657
686
  },
658
- defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
687
+ defaults: {},
659
688
  usage: { ...VIDEO_USAGE },
660
689
  pricingKind: "video_per_second"
661
690
  },
@@ -671,9 +700,10 @@ var VIDEO_MODELS = [
671
700
  aspectRatios: [...OMNI_RATIOS],
672
701
  resolutions: ["360p", "720p", "1080p", "4k"],
673
702
  durations: [3, 4, 5, 6, 7, 8, 9, 10],
674
- references: { images: 6, videos: 1 }
703
+ references: { images: 6, videos: 1 },
704
+ supports: []
675
705
  },
676
- defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
706
+ defaults: {},
677
707
  usage: { ...VIDEO_USAGE },
678
708
  pricingKind: "video_per_second"
679
709
  },
@@ -767,7 +797,8 @@ var VIDEO_MODELS = [
767
797
  aspectRatios: [...GROK_RATIOS],
768
798
  resolutions: ["480p", "720p"],
769
799
  durations: { min: 1, max: 15 },
770
- references: { images: 7 }
800
+ references: { images: 7 },
801
+ supports: []
771
802
  },
772
803
  defaults: { durationSeconds: 8, resolution: "720p", aspectRatio: "16:9" },
773
804
  usage: { unit: "reported_cost_usd", modality: "VIDEO" },
@@ -957,7 +988,8 @@ var VIDEO_MODELS = [
957
988
  constraints: {
958
989
  aspectRatios: [...KLING_RATIOS],
959
990
  durations: { min: 3, max: 10 },
960
- requires: ["sourceVideo"]
991
+ requires: ["sourceVideo"],
992
+ supports: []
961
993
  },
962
994
  defaults: { durationSeconds: 5, aspectRatio: "16:9" },
963
995
  usage: { ...VIDEO_USAGE },
@@ -990,7 +1022,7 @@ var VIDEO_MODELS = [
990
1022
  lifecycle: "active",
991
1023
  constraints: {
992
1024
  requires: ["firstFrame"],
993
- supports: ["seed"]
1025
+ supports: ["seed", "lastFrame"]
994
1026
  },
995
1027
  defaults: {},
996
1028
  usage: { ...VIDEO_USAGE },
@@ -1007,7 +1039,7 @@ var MODELS = [
1007
1039
  ];
1008
1040
 
1009
1041
  // src/resolve-options.ts
1010
- var SIZE_ORDER = ["1K", "2K", "4K"];
1042
+ var SIZE_ORDER = ["512", "1K", "2K", "4K"];
1011
1043
  var RESOLUTION_ORDER = ["360p", "480p", "720p", "768p", "1080p", "2k", "4k"];
1012
1044
  function ratioValue(r) {
1013
1045
  if (r === "adaptive") return 1;
@@ -1101,7 +1133,7 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1101
1133
  const warnings = [];
1102
1134
  const c = model.constraints;
1103
1135
  const merged = { ...model.defaults, ...stripUndefined(request), model: model.id };
1104
- if (merged.aspectRatio && c.aspectRatios && !c.aspectRatios.includes(merged.aspectRatio)) {
1136
+ if (merged.aspectRatio !== void 0 && merged.aspectRatio !== "adaptive" && c.aspectRatios !== void 0 && !c.aspectRatios.includes(merged.aspectRatio)) {
1105
1137
  const to = nearestAspectRatio(merged.aspectRatio, c.aspectRatios);
1106
1138
  warnings.push({
1107
1139
  code: "ratio_coerced",
@@ -1139,7 +1171,14 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1139
1171
  model: model.id
1140
1172
  });
1141
1173
  }
1142
- dropUnsupported(merged, model, warnings, ["seed", "quality", "count", "outputFormat"]);
1174
+ dropUnsupported(merged, model, warnings, [
1175
+ "seed",
1176
+ "quality",
1177
+ "count",
1178
+ "outputFormat",
1179
+ "scaleFactor",
1180
+ "safetySettings"
1181
+ ]);
1143
1182
  return { request: merged, warnings };
1144
1183
  }
1145
1184
  function resolveVideoOptions(model, request, onUnsupported = "error") {
@@ -1168,7 +1207,7 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1168
1207
  delete merged.aspectRatio;
1169
1208
  }
1170
1209
  }
1171
- if (merged.aspectRatio && c.aspectRatios && !c.aspectRatios.includes(merged.aspectRatio)) {
1210
+ if (merged.aspectRatio !== void 0 && merged.aspectRatio !== "adaptive" && c.aspectRatios !== void 0 && !c.aspectRatios.includes(merged.aspectRatio)) {
1172
1211
  const to = nearestAspectRatio(merged.aspectRatio, c.aspectRatios);
1173
1212
  warnings.push({
1174
1213
  code: "ratio_coerced",
@@ -1250,8 +1289,7 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
1250
1289
  return { request: merged, warnings };
1251
1290
  }
1252
1291
  function dropUnsupported(req, model, warnings, fields) {
1253
- const supports = model.constraints.supports;
1254
- if (!supports) return;
1292
+ const supports = model.constraints.supports ?? [];
1255
1293
  for (const field of fields) {
1256
1294
  if (req[field] !== void 0 && !supports.includes(field)) {
1257
1295
  warnings.push({
@@ -1329,8 +1367,45 @@ function keysFromEnv(env, prefix = "") {
1329
1367
  }
1330
1368
 
1331
1369
  // src/retry.ts
1370
+ import { z as z2 } from "zod";
1371
+
1372
+ // src/json.ts
1332
1373
  import { z } from "zod";
1333
- var textBody = z.string();
1374
+ var JsonValueSchema = z.lazy(
1375
+ () => z.union([
1376
+ z.string(),
1377
+ z.number(),
1378
+ z.boolean(),
1379
+ z.null(),
1380
+ z.array(JsonValueSchema),
1381
+ z.record(z.string(), JsonValueSchema)
1382
+ ])
1383
+ );
1384
+ var JsonObjectSchema = z.record(z.string(), JsonValueSchema);
1385
+ async function readErrorBody(res) {
1386
+ const text2 = await res.text().catch(() => "");
1387
+ try {
1388
+ return JsonValueSchema.parse(JSON.parse(text2));
1389
+ } catch {
1390
+ return text2;
1391
+ }
1392
+ }
1393
+ var thrownErrorSchema = z.object({
1394
+ message: z.string().optional(),
1395
+ status: z.number().optional(),
1396
+ statusCode: z.number().optional(),
1397
+ responseBody: z.string().optional()
1398
+ });
1399
+ function describeThrown(cause) {
1400
+ const parsed = thrownErrorSchema.safeParse(cause);
1401
+ if (!parsed.success) return { message: String(cause), status: void 0, body: String(cause) };
1402
+ const { message, status, statusCode, responseBody } = parsed.data;
1403
+ const body = responseBody ?? message ?? String(cause);
1404
+ return { message: message ?? String(cause), status: status ?? statusCode, body };
1405
+ }
1406
+
1407
+ // src/retry.ts
1408
+ var textBody = z2.string();
1334
1409
  function classifyHttpError(status, body, provider, model) {
1335
1410
  const asText = textBody.safeParse(body);
1336
1411
  const text2 = asText.success ? asText.data : JSON.stringify(body);
@@ -1413,6 +1488,13 @@ function transportError(provider, cause, model) {
1413
1488
  if (model !== void 0) init.model = model;
1414
1489
  return new ModelKitError(init);
1415
1490
  }
1491
+ function classifyThrown(provider, cause, model, override = {}) {
1492
+ if (cause instanceof ModelKitError) return cause;
1493
+ const info = describeThrown(cause);
1494
+ const status = override.status ?? info.status;
1495
+ if (status === void 0) return transportError(provider, cause, model);
1496
+ return classifyHttpError(status, override.body ?? info.body, provider, model);
1497
+ }
1416
1498
  async function pollUntil(check, opts) {
1417
1499
  const start = Date.now();
1418
1500
  let interval = opts.intervalMs;
@@ -1535,7 +1617,7 @@ import {
1535
1617
  streamObject as aiStreamObject2,
1536
1618
  streamText as aiStreamText2
1537
1619
  } from "ai";
1538
- import { z as z3 } from "zod";
1620
+ import { z as z4 } from "zod";
1539
1621
 
1540
1622
  // src/adapters/media-ref.ts
1541
1623
  function isUrlRef(ref) {
@@ -1590,7 +1672,7 @@ import { createPerplexity } from "@ai-sdk/perplexity";
1590
1672
  import { createXai } from "@ai-sdk/xai";
1591
1673
  import { createDeepSeek } from "@ai-sdk/deepseek";
1592
1674
  import { createMoonshotAI } from "@ai-sdk/moonshotai";
1593
- import { z as z2 } from "zod";
1675
+ import { z as z3 } from "zod";
1594
1676
  var TEXT_PROVIDERS = [
1595
1677
  "openai",
1596
1678
  "anthropic",
@@ -1601,7 +1683,7 @@ var TEXT_PROVIDERS = [
1601
1683
  "moonshot",
1602
1684
  "minimax"
1603
1685
  ];
1604
- var textProviderSchema = z2.enum(TEXT_PROVIDERS);
1686
+ var textProviderSchema = z3.enum(TEXT_PROVIDERS);
1605
1687
  function isTextProvider(value) {
1606
1688
  return textProviderSchema.safeParse(value).success;
1607
1689
  }
@@ -1610,9 +1692,9 @@ var OPENAI_COMPATIBLE_BASE_URLS = {
1610
1692
  };
1611
1693
  var GATEWAY_SLUGS = {
1612
1694
  openai: "openai",
1613
- anthropic: "anthropic",
1695
+ anthropic: "anthropic/v1",
1614
1696
  google: "google-ai-studio/v1beta",
1615
- xai: "grok",
1697
+ xai: "grok/v1",
1616
1698
  perplexity: "perplexity-ai",
1617
1699
  deepseek: "deepseek"
1618
1700
  };
@@ -1787,10 +1869,10 @@ function textAbortOptions(sdk, context) {
1787
1869
  if (abortSignal) return { abortSignal };
1788
1870
  return {};
1789
1871
  }
1790
- var catalogId = z3.string();
1791
- var exactSize = z3.templateLiteral([z3.number(), "x", z3.number()]);
1792
- var sdkRatio = z3.templateLiteral([z3.number(), ":", z3.number()]);
1793
- var sizeTier = z3.enum(["1K", "2K", "4K"]);
1872
+ var catalogId = z4.string();
1873
+ var exactSize = z4.templateLiteral([z4.number(), "x", z4.number()]);
1874
+ var sdkRatio = z4.templateLiteral([z4.number(), ":", z4.number()]);
1875
+ var sizeTier = z4.enum(["512", "1K", "2K", "4K"]);
1794
1876
  function isCatalogId(model) {
1795
1877
  return catalogId.safeParse(model).success;
1796
1878
  }
@@ -1847,6 +1929,10 @@ function pushIgnored(warnings, field, from, modelId) {
1847
1929
  message: `${modelId} provider instance does not support ${field}; ignored`
1848
1930
  });
1849
1931
  }
1932
+ function logCoercions(logger, modelId, warnings) {
1933
+ if (warnings.length === 0) return;
1934
+ logger.warn(`[modelkit] ${modelId} request coerced`, { model: modelId, warnings });
1935
+ }
1850
1936
  function createModelKit(config, adapters) {
1851
1937
  const catalog = new Catalog(MODELS).withOverrides(config.catalogOverride ?? []);
1852
1938
  const logger = config.logger ?? console;
@@ -1880,6 +1966,10 @@ function createModelKit(config, adapters) {
1880
1966
  logger.warn("[modelkit] usage emit failed", err);
1881
1967
  }
1882
1968
  }
1969
+ function withRowUnit(event) {
1970
+ const row = catalog.get(event.model);
1971
+ return row ? { ...event, unit: row.usage.unit } : event;
1972
+ }
1883
1973
  function textTarget(model, call) {
1884
1974
  if (!isCatalogId(model)) {
1885
1975
  return {
@@ -1920,6 +2010,7 @@ function createModelKit(config, adapters) {
1920
2010
  providerOptions,
1921
2011
  references,
1922
2012
  exactSize: exactSizeField,
2013
+ scaleFactor,
1923
2014
  quality: _quality,
1924
2015
  outputFormat: _outputFormat
1925
2016
  } = req;
@@ -1945,6 +2036,9 @@ function createModelKit(config, adapters) {
1945
2036
  if (_quality !== void 0) pushIgnored(warnings, "quality", _quality, model.modelId);
1946
2037
  if (_outputFormat !== void 0)
1947
2038
  pushIgnored(warnings, "outputFormat", _outputFormat, model.modelId);
2039
+ if (scaleFactor !== void 0)
2040
+ pushIgnored(warnings, "scaleFactor", scaleFactor, model.modelId);
2041
+ logCoercions(logger, model.modelId, warnings);
1948
2042
  const abortSignal = combineSignals(req.abortSignal, call.signal);
1949
2043
  if (abortSignal) options.abortSignal = abortSignal;
1950
2044
  const result = await aiGenerateImage(options);
@@ -2028,6 +2122,7 @@ function createModelKit(config, adapters) {
2028
2122
  if (_mode !== void 0) pushIgnored(warnings, "mode", _mode, model.modelId);
2029
2123
  if (providerOptions) options.providerOptions = providerOptions;
2030
2124
  if (abortSignal) options.abortSignal = abortSignal;
2125
+ logCoercions(logger, model.modelId, warnings);
2031
2126
  const pending = aiGenerateVideo(options);
2032
2127
  let settled;
2033
2128
  let failed = false;
@@ -2086,6 +2181,7 @@ function createModelKit(config, adapters) {
2086
2181
  call.onUnsupported ?? "error"
2087
2182
  );
2088
2183
  warnings.push(...inputWarnings);
2184
+ logCoercions(logger, model.id, warnings);
2089
2185
  const signal = combineSignals(req.abortSignal, call.signal);
2090
2186
  const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
2091
2187
  if (!adapter.generateImage)
@@ -2095,7 +2191,7 @@ function createModelKit(config, adapters) {
2095
2191
  model: model.id
2096
2192
  });
2097
2193
  const result = await adapter.generateImage(request, ctx);
2098
- const usage2 = { ...result.usage, model: model.id };
2194
+ const usage2 = { ...result.usage, model: model.id, unit: model.usage.unit };
2099
2195
  await emit(usage2);
2100
2196
  const images = result.images.map(toGeneratedImage);
2101
2197
  const first = images[0];
@@ -2128,6 +2224,7 @@ function createModelKit(config, adapters) {
2128
2224
  call.onUnsupported ?? "error"
2129
2225
  );
2130
2226
  warnings.push(...inputWarnings);
2227
+ logCoercions(logger, model.id, warnings);
2131
2228
  const signal = combineSignals(req.abortSignal, call.signal);
2132
2229
  const { adapter, ctx } = context(model, signal ? { ...call, signal } : call, warnings);
2133
2230
  if (!adapter.generateVideo)
@@ -2145,7 +2242,11 @@ function createModelKit(config, adapters) {
2145
2242
  result(opts) {
2146
2243
  if (settled) return settled;
2147
2244
  const pending = result(opts).then(
2148
- (out) => emit({ ...out.usage, model: model.id }).then(() => out),
2245
+ (out) => emit({
2246
+ ...out.usage,
2247
+ model: model.id,
2248
+ unit: model.usage.unit
2249
+ }).then(() => out),
2149
2250
  (cause) => {
2150
2251
  settled = void 0;
2151
2252
  throw cause;
@@ -2165,7 +2266,8 @@ function createModelKit(config, adapters) {
2165
2266
  model: model.id
2166
2267
  });
2167
2268
  }
2168
- const { adapter, ctx } = context(model, call, []);
2269
+ const warnings = [];
2270
+ const { adapter, ctx } = context(model, call, warnings);
2169
2271
  if (!adapter.generateAudio) {
2170
2272
  throw new ModelKitError({
2171
2273
  code: "unsupported_option",
@@ -2177,7 +2279,8 @@ function createModelKit(config, adapters) {
2177
2279
  { ...model.defaults, ...req, model: model.id },
2178
2280
  ctx
2179
2281
  );
2180
- await emit({ ...result.usage, model: model.id });
2282
+ logCoercions(logger, model.id, warnings);
2283
+ await emit({ ...result.usage, model: model.id, unit: model.usage.unit });
2181
2284
  return result;
2182
2285
  },
2183
2286
  async prepareMediaPart(modelId, ref, mediaType, call = {}) {
@@ -2217,7 +2320,7 @@ function createModelKit(config, adapters) {
2217
2320
  options.prepareStep ?? options.experimental_prepareStep
2218
2321
  )
2219
2322
  });
2220
- for (const event of steps.usage(result.steps)) await emit(event);
2323
+ for (const event of steps.usage(result.steps)) await emit(withRowUnit(event));
2221
2324
  return result;
2222
2325
  },
2223
2326
  streamText(options, call = {}) {
@@ -2235,7 +2338,7 @@ function createModelKit(config, adapters) {
2235
2338
  providerOptions: {},
2236
2339
  prepareStep: steps.wrap(options.prepareStep),
2237
2340
  onFinish: async (event) => {
2238
- for (const usage2 of steps.usage(event.steps)) await emit(usage2);
2341
+ for (const usage2 of steps.usage(event.steps)) await emit(withRowUnit(usage2));
2239
2342
  await callerOnFinish?.(event);
2240
2343
  }
2241
2344
  });
@@ -2249,7 +2352,7 @@ function createModelKit(config, adapters) {
2249
2352
  providerOptions: mergeTextProviderOptions(entry, options.providerOptions)
2250
2353
  };
2251
2354
  const result = await aiGenerateObject2(sdkOptions);
2252
- await emit(textUsageEvent(entry, provider, result.usage));
2355
+ await emit(withRowUnit(textUsageEvent(entry, provider, result.usage)));
2253
2356
  return result;
2254
2357
  },
2255
2358
  streamObject(options, call = {}) {
@@ -2261,7 +2364,7 @@ function createModelKit(config, adapters) {
2261
2364
  model: languageModel2,
2262
2365
  providerOptions: mergeTextProviderOptions(entry, options.providerOptions),
2263
2366
  onFinish: async (event) => {
2264
- await emit(textUsageEvent(entry, provider, event.usage));
2367
+ await emit(withRowUnit(textUsageEvent(entry, provider, event.usage)));
2265
2368
  await callerOnFinish?.(event);
2266
2369
  }
2267
2370
  };
@@ -2271,7 +2374,7 @@ function createModelKit(config, adapters) {
2271
2374
  }
2272
2375
 
2273
2376
  // src/adapters/ark.ts
2274
- import { z as z4 } from "zod";
2377
+ import { z as z5 } from "zod";
2275
2378
  var ARK_BASE_URL = "https://ark.ap-southeast.bytepluses.com/api/v3";
2276
2379
  var SUBMIT_PATH = "/contents/generations/tasks";
2277
2380
  var SEEDANCE_POLL_INTERVAL_MS = 15e3;
@@ -2329,7 +2432,7 @@ function arkKey(ctx) {
2329
2432
  return key;
2330
2433
  }
2331
2434
  function throwArkError(status, body, model) {
2332
- const parsed = z4.string().safeParse(body);
2435
+ const parsed = z5.string().safeParse(body);
2333
2436
  const text2 = parsed.success ? parsed.data : JSON.stringify(body ?? "");
2334
2437
  if (/sensitive.?content|inputimage|human.?face/i.test(text2)) {
2335
2438
  const init = {
@@ -2343,6 +2446,16 @@ function throwArkError(status, body, model) {
2343
2446
  if (status !== void 0) init.status = status;
2344
2447
  throw new ModelKitError(init);
2345
2448
  }
2449
+ if (status === void 0) {
2450
+ throw new ModelKitError({
2451
+ code: "provider_rejected",
2452
+ message: `ark rejected the request: ${text2.slice(0, 500)}`,
2453
+ provider: "ark",
2454
+ model,
2455
+ body,
2456
+ retryable: false
2457
+ });
2458
+ }
2346
2459
  throw classifyHttpError(status, body, "ark", model);
2347
2460
  }
2348
2461
  function seedreamSize(aspect, size) {
@@ -2361,13 +2474,13 @@ function arkUrl(ref) {
2361
2474
  if ("url" in ref) return ref.url;
2362
2475
  return `data:${ref.mime};base64,${Buffer.from(ref.bytes).toString("base64")}`;
2363
2476
  }
2364
- var SeedreamImageSchema = z4.object({
2365
- url: z4.string().optional(),
2366
- b64_json: z4.string().optional()
2477
+ var SeedreamImageSchema = z5.object({
2478
+ url: z5.string().optional(),
2479
+ b64_json: z5.string().optional()
2367
2480
  });
2368
- var SeedreamResponseSchema = z4.object({
2369
- data: z4.array(SeedreamImageSchema).optional(),
2370
- error: z4.object({ message: z4.string().optional(), code: z4.string().optional() }).optional()
2481
+ var SeedreamResponseSchema = z5.object({
2482
+ data: z5.array(SeedreamImageSchema).optional(),
2483
+ error: z5.object({ message: z5.string().optional(), code: z5.string().optional() }).optional()
2371
2484
  });
2372
2485
  function retryOptsFor(ctx, attempts = 3) {
2373
2486
  const opts = { attempts };
@@ -2378,6 +2491,11 @@ function fetchInitFor(ctx, init) {
2378
2491
  if (ctx.signal !== void 0) init.signal = ctx.signal;
2379
2492
  return init;
2380
2493
  }
2494
+ function arkFetch(ctx, url, init) {
2495
+ return ctx.fetch(url, fetchInitFor(ctx, init)).catch((cause) => {
2496
+ throw classifyThrown("ark", cause, ctx.model.id);
2497
+ });
2498
+ }
2381
2499
  async function generateSeedreamImage(req, ctx) {
2382
2500
  const key = arkKey(ctx);
2383
2501
  const modelId = ctx.model.id;
@@ -2395,14 +2513,11 @@ async function generateSeedreamImage(req, ctx) {
2395
2513
  if (refs.length === 1 && firstRef !== void 0) body.image = firstRef;
2396
2514
  else if (refs.length > 1) body.image = refs;
2397
2515
  const data = await withRetry(async () => {
2398
- const res = await ctx.fetch(
2399
- `${arkBaseUrl(ctx)}/images/generations`,
2400
- fetchInitFor(ctx, {
2401
- method: "POST",
2402
- headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2403
- body: JSON.stringify(body)
2404
- })
2405
- );
2516
+ const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}/images/generations`, {
2517
+ method: "POST",
2518
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2519
+ body: JSON.stringify(body)
2520
+ });
2406
2521
  if (!res.ok) {
2407
2522
  const text2 = await res.text().catch(() => "");
2408
2523
  throwArkError(res.status, text2, modelId);
@@ -2417,7 +2532,7 @@ async function generateSeedreamImage(req, ctx) {
2417
2532
  bytes = Buffer.from(first.b64_json, "base64");
2418
2533
  } else if (first?.url) {
2419
2534
  const imageUrl = first.url;
2420
- const res = await ctx.fetch(imageUrl, fetchInitFor(ctx, {}));
2535
+ const res = await arkFetch(ctx, imageUrl, {});
2421
2536
  if (!res.ok) {
2422
2537
  const text2 = await res.text().catch(() => "");
2423
2538
  throwArkError(res.status, text2, modelId);
@@ -2437,13 +2552,13 @@ async function generateSeedreamImage(req, ctx) {
2437
2552
  if (req.quality !== void 0) usage2.imageQuality = req.quality;
2438
2553
  return { images: [{ bytes, mime: sniffedImageMime(bytes) }], usage: usage2, raw: data };
2439
2554
  }
2440
- var SeedanceResponseSchema = z4.object({
2441
- id: z4.string().optional(),
2442
- status: z4.string().optional(),
2443
- content: z4.object({ video_url: z4.string().optional() }).optional(),
2444
- error: z4.union([z4.string(), z4.object({ message: z4.string().optional() })]).optional()
2555
+ var SeedanceResponseSchema = z5.object({
2556
+ id: z5.string().optional(),
2557
+ status: z5.string().optional(),
2558
+ content: z5.object({ video_url: z5.string().optional() }).optional(),
2559
+ error: z5.union([z5.string(), z5.object({ message: z5.string().optional() })]).optional()
2445
2560
  });
2446
- var SeedanceErrorMessageSchema = z4.object({ message: z4.string().optional() });
2561
+ var SeedanceErrorMessageSchema = z5.object({ message: z5.string().optional() });
2447
2562
  function seedanceUsage(ctx, req, duration) {
2448
2563
  const usage2 = {
2449
2564
  model: ctx.model.id,
@@ -2457,7 +2572,7 @@ function seedanceUsage(ctx, req, duration) {
2457
2572
  return usage2;
2458
2573
  }
2459
2574
  function seedanceErrorMessage(error, status) {
2460
- const asString = z4.string().safeParse(error);
2575
+ const asString = z5.string().safeParse(error);
2461
2576
  if (asString.success) return asString.data;
2462
2577
  const asObject = SeedanceErrorMessageSchema.safeParse(error);
2463
2578
  if (asObject.success && asObject.data.message !== void 0) return asObject.data.message;
@@ -2522,14 +2637,11 @@ async function generateSeedanceVideo(req, ctx) {
2522
2637
  if (req.generateAudio !== void 0) body.generate_audio = req.generateAudio;
2523
2638
  if (req.seed !== void 0) body.seed = req.seed;
2524
2639
  const submit = await withRetry(async () => {
2525
- const res = await ctx.fetch(
2526
- `${arkBaseUrl(ctx)}${SUBMIT_PATH}`,
2527
- fetchInitFor(ctx, {
2528
- method: "POST",
2529
- headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2530
- body: JSON.stringify(body)
2531
- })
2532
- );
2640
+ const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}`, {
2641
+ method: "POST",
2642
+ headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
2643
+ body: JSON.stringify(body)
2644
+ });
2533
2645
  if (!res.ok) {
2534
2646
  const text2 = await res.text().catch(() => "");
2535
2647
  throwArkError(res.status, text2, modelId);
@@ -2542,12 +2654,9 @@ async function generateSeedanceVideo(req, ctx) {
2542
2654
  const jobId = submit.id;
2543
2655
  const usage2 = seedanceUsage(ctx, req, duration);
2544
2656
  const fetchStatus = async () => {
2545
- const res = await ctx.fetch(
2546
- `${arkBaseUrl(ctx)}${SUBMIT_PATH}/${jobId}`,
2547
- fetchInitFor(ctx, {
2548
- headers: { Authorization: `Bearer ${key}` }
2549
- })
2550
- );
2657
+ const res = await arkFetch(ctx, `${arkBaseUrl(ctx)}${SUBMIT_PATH}/${jobId}`, {
2658
+ headers: { Authorization: `Bearer ${key}` }
2659
+ });
2551
2660
  if (!res.ok) {
2552
2661
  if (res.status === 429 || res.status >= 500) return void 0;
2553
2662
  const text2 = await res.text().catch(() => "");
@@ -2608,43 +2717,6 @@ async function generateSeedanceVideo(req, ctx) {
2608
2717
  // src/adapters/fal.ts
2609
2718
  import { z as z6 } from "zod";
2610
2719
  import { createFalClient } from "@fal-ai/client";
2611
-
2612
- // src/json.ts
2613
- import { z as z5 } from "zod";
2614
- var JsonValueSchema = z5.lazy(
2615
- () => z5.union([
2616
- z5.string(),
2617
- z5.number(),
2618
- z5.boolean(),
2619
- z5.null(),
2620
- z5.array(JsonValueSchema),
2621
- z5.record(z5.string(), JsonValueSchema)
2622
- ])
2623
- );
2624
- var JsonObjectSchema = z5.record(z5.string(), JsonValueSchema);
2625
- async function readErrorBody(res) {
2626
- const text2 = await res.text().catch(() => "");
2627
- try {
2628
- return JsonValueSchema.parse(JSON.parse(text2));
2629
- } catch {
2630
- return text2;
2631
- }
2632
- }
2633
- var thrownErrorSchema = z5.object({
2634
- message: z5.string().optional(),
2635
- status: z5.number().optional(),
2636
- statusCode: z5.number().optional(),
2637
- responseBody: z5.string().optional()
2638
- });
2639
- function describeThrown(cause) {
2640
- const parsed = thrownErrorSchema.safeParse(cause);
2641
- if (!parsed.success) return { message: String(cause), status: void 0, body: String(cause) };
2642
- const { message, status, statusCode, responseBody } = parsed.data;
2643
- const body = responseBody ?? message ?? String(cause);
2644
- return { message: message ?? String(cause), status: status ?? statusCode, body };
2645
- }
2646
-
2647
- // src/adapters/fal.ts
2648
2720
  var MUSE_T2I_ENDPOINT = "meta/muse-image/text-to-image";
2649
2721
  var MUSE_EDIT_ENDPOINT = "meta/muse-image/edit";
2650
2722
  var KLING_O1_ENDPOINT = "fal-ai/kling-video/o1/video-to-video/reference";
@@ -2656,10 +2728,33 @@ function defaultCreateFalClient(opts) {
2656
2728
  const client2 = createFalClient(opts);
2657
2729
  return {
2658
2730
  async subscribe(endpoint, options) {
2659
- const outcome = await client2.subscribe(endpoint, { input: options.input });
2731
+ const outcome = await client2.subscribe(endpoint, options);
2660
2732
  const unwrapped = falSubscribePayloadSchema.safeParse(outcome);
2661
2733
  if (unwrapped.success) return unwrapped.data.data;
2662
2734
  return JsonValueSchema.parse(outcome);
2735
+ },
2736
+ queue: {
2737
+ async submit(endpoint, options) {
2738
+ const submitted = await client2.queue.submit(endpoint, {
2739
+ input: options.input,
2740
+ ...options.abortSignal !== void 0 ? { abortSignal: options.abortSignal } : {}
2741
+ });
2742
+ return { requestId: submitted.request_id };
2743
+ },
2744
+ async status(endpoint, options) {
2745
+ const current = await client2.queue.status(endpoint, {
2746
+ requestId: options.requestId,
2747
+ ...options.abortSignal !== void 0 ? { abortSignal: options.abortSignal } : {}
2748
+ });
2749
+ return { status: current.status };
2750
+ },
2751
+ async result(endpoint, options) {
2752
+ const outcome = await client2.queue.result(endpoint, {
2753
+ requestId: options.requestId,
2754
+ ...options.abortSignal !== void 0 ? { abortSignal: options.abortSignal } : {}
2755
+ });
2756
+ return JsonValueSchema.parse(outcome.data);
2757
+ }
2663
2758
  }
2664
2759
  };
2665
2760
  }
@@ -2701,15 +2796,17 @@ var falSdkThrownSchema = z6.object({
2701
2796
  message: z6.string().optional()
2702
2797
  });
2703
2798
  function falSdkError(cause, model) {
2704
- if (isModelKitError(cause)) return cause;
2705
2799
  const parsed = falSdkThrownSchema.safeParse(cause);
2706
2800
  if (parsed.success) {
2707
2801
  const status = parsed.data.status ?? parsed.data.statusCode;
2708
2802
  const body = parsed.data.body ?? parsed.data.message ?? String(cause);
2709
- return falError(status, body, model);
2803
+ return classifyThrown("fal", cause, model, { status, body: liftDetail(body) });
2710
2804
  }
2711
2805
  const info = describeThrown(cause);
2712
- return falError(info.status, info.body, model);
2806
+ return classifyThrown("fal", cause, model, {
2807
+ status: info.status,
2808
+ body: liftDetail(info.body)
2809
+ });
2713
2810
  }
2714
2811
  function gatewayConfig(ctx) {
2715
2812
  const cf = ctx.routing.cloudflare;
@@ -2791,7 +2888,7 @@ async function passthroughRequest(ctx, endpoint, input) {
2791
2888
  }
2792
2889
  return JsonValueSchema.parse(await resultRes.json());
2793
2890
  }
2794
- async function subscribe(ctx, deps, endpoint, input) {
2891
+ function falClientOf(ctx, deps) {
2795
2892
  const falKey = ctx.keys.fal;
2796
2893
  if (!falKey) {
2797
2894
  throw new ModelKitError({
@@ -2802,12 +2899,19 @@ async function subscribe(ctx, deps, endpoint, input) {
2802
2899
  retryable: false
2803
2900
  });
2804
2901
  }
2805
- const client2 = deps.createFalClient({ credentials: falKey });
2902
+ return deps.createFalClient({ credentials: falKey, fetch: ctx.fetch });
2903
+ }
2904
+ async function subscribe(ctx, deps, endpoint, input) {
2905
+ const client2 = falClientOf(ctx, deps);
2906
+ const options = {
2907
+ input,
2908
+ ...ctx.signal !== void 0 ? { abortSignal: ctx.signal } : {}
2909
+ };
2806
2910
  try {
2807
2911
  return await withRetry(
2808
2912
  async () => {
2809
2913
  try {
2810
- return await client2.subscribe(endpoint, { input });
2914
+ return await client2.subscribe(endpoint, options);
2811
2915
  } catch (cause) {
2812
2916
  throw falSdkError(cause, ctx.model.id);
2813
2917
  }
@@ -2941,13 +3045,14 @@ function klingO1Input(req) {
2941
3045
  });
2942
3046
  }
2943
3047
  const duration = Math.min(10, Math.max(3, Math.round(req.durationSeconds ?? 5)));
2944
- const ratio = req.aspectRatio === "16:9" || req.aspectRatio === "9:16" || req.aspectRatio === "1:1" ? req.aspectRatio : "auto";
2945
3048
  const input = {
2946
3049
  prompt: req.prompt,
2947
3050
  video_url: source,
2948
- duration: String(duration),
2949
- aspect_ratio: ratio
3051
+ duration: String(duration)
2950
3052
  };
3053
+ if (req.aspectRatio !== "adaptive") {
3054
+ input.aspect_ratio = req.aspectRatio === "16:9" || req.aspectRatio === "9:16" || req.aspectRatio === "1:1" ? req.aspectRatio : "auto";
3055
+ }
2951
3056
  const refs = (req.referenceImages ?? []).map(refToUrl);
2952
3057
  if (refs.length > 0) input.image_urls = refs;
2953
3058
  return input;
@@ -2976,13 +3081,7 @@ async function generateFalVideo(req, ctx, deps) {
2976
3081
  if (ctx.routing.fal === "cloudflare-passthrough") {
2977
3082
  return await passthroughVideoJob(ctx, req, KLING_O1_ENDPOINT, input);
2978
3083
  }
2979
- const result2 = await runJob(ctx, deps, KLING_O1_ENDPOINT, input);
2980
- return completedVideoJob(
2981
- ctx,
2982
- req,
2983
- extractVideoUrl(result2, KLING_O1_ENDPOINT, ctx.model.id),
2984
- result2
2985
- );
3084
+ return await submitQueueJob(ctx, req, deps, KLING_O1_ENDPOINT, input);
2986
3085
  }
2987
3086
  const family = minimaxFamily(providerModelId);
2988
3087
  const refs = {
@@ -3011,19 +3110,81 @@ async function generateFalVideo(req, ctx, deps) {
3011
3110
  if (ctx.routing.fal === "cloudflare-passthrough") {
3012
3111
  return await passthroughVideoJob(ctx, req, endpoint, base);
3013
3112
  }
3014
- const result = await runJob(ctx, deps, endpoint, base);
3015
- return completedVideoJob(ctx, req, extractVideoUrl(result, endpoint, ctx.model.id), result);
3113
+ return await submitQueueJob(ctx, req, deps, endpoint, base);
3114
+ }
3115
+ async function submitQueueJob(ctx, req, deps, endpoint, input) {
3116
+ const client2 = falClientOf(ctx, deps);
3117
+ let submitted;
3118
+ try {
3119
+ submitted = await withRetry(
3120
+ async () => {
3121
+ try {
3122
+ return await client2.queue.submit(endpoint, {
3123
+ input,
3124
+ ...ctx.signal !== void 0 ? { abortSignal: ctx.signal } : {}
3125
+ });
3126
+ } catch (cause) {
3127
+ throw falSdkError(cause, ctx.model.id);
3128
+ }
3129
+ },
3130
+ ctx.signal !== void 0 ? { signal: ctx.signal } : {}
3131
+ );
3132
+ } catch (cause) {
3133
+ throw falSdkError(cause, ctx.model.id);
3134
+ }
3135
+ return queuedVideoJob(ctx, req, client2, endpoint, submitted.requestId);
3016
3136
  }
3017
- function completedVideoJob(ctx, req, url, raw) {
3137
+ function queuedVideoJob(ctx, req, client2, endpoint, requestId) {
3138
+ const readStatus = async () => {
3139
+ try {
3140
+ const options = {
3141
+ requestId,
3142
+ ...ctx.signal !== void 0 ? { abortSignal: ctx.signal } : {}
3143
+ };
3144
+ return (await client2.queue.status(endpoint, options)).status;
3145
+ } catch (cause) {
3146
+ throw falSdkError(cause, ctx.model.id);
3147
+ }
3148
+ };
3018
3149
  const duration = req.durationSeconds ?? 5;
3019
- const result = { video: { url }, usage: videoUsage(ctx, req, duration), raw };
3020
3150
  return {
3021
- jobId: url,
3151
+ jobId: requestId,
3022
3152
  async poll() {
3023
- return { status: "succeeded", raw };
3153
+ try {
3154
+ const status = await readStatus();
3155
+ if (status === "COMPLETED") return { status: "succeeded" };
3156
+ if (status === "IN_QUEUE") return { status: "pending" };
3157
+ return { status: "running" };
3158
+ } catch (cause) {
3159
+ if (isModelKitError(cause) && !cause.retryable)
3160
+ return { status: "failed" };
3161
+ return { status: "pending" };
3162
+ }
3024
3163
  },
3025
- async result() {
3026
- return result;
3164
+ async result(opts) {
3165
+ const signal = opts?.signal ?? ctx.signal;
3166
+ const pollOptions = {
3167
+ intervalMs: opts?.pollIntervalMs ?? PASSTHROUGH_POLL_MS,
3168
+ timeoutMs: opts?.timeoutMs ?? 15 * 6e4
3169
+ };
3170
+ if (signal !== void 0) pollOptions.signal = signal;
3171
+ const queueOptions = {
3172
+ requestId,
3173
+ ...signal !== void 0 ? { abortSignal: signal } : {}
3174
+ };
3175
+ const payload = await pollUntil(async () => {
3176
+ try {
3177
+ const current = await client2.queue.status(endpoint, queueOptions);
3178
+ if (current.status !== "COMPLETED") return void 0;
3179
+ return await client2.queue.result(endpoint, queueOptions);
3180
+ } catch (cause) {
3181
+ const err = falSdkError(cause, ctx.model.id);
3182
+ if (err.retryable) return void 0;
3183
+ throw err;
3184
+ }
3185
+ }, pollOptions);
3186
+ const url = extractVideoUrl(payload, endpoint, ctx.model.id);
3187
+ return { video: { url }, usage: videoUsage(ctx, req, duration), raw: payload };
3027
3188
  }
3028
3189
  };
3029
3190
  }
@@ -3376,9 +3537,7 @@ function sdkStatus(cause) {
3376
3537
  return describeThrown(cause).status;
3377
3538
  }
3378
3539
  function throwSdkError(cause, model) {
3379
- if (cause instanceof ModelKitError) throw cause;
3380
- const info = describeThrown(cause);
3381
- throw classifyHttpError(info.status, info.body, "google-genai", model);
3540
+ throw classifyThrown("google-genai", cause, model);
3382
3541
  }
3383
3542
  function toBase64(bytes) {
3384
3543
  return Buffer.from(bytes).toString("base64");
@@ -3582,12 +3741,17 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3582
3741
  const duration = req.durationSeconds ?? 8;
3583
3742
  const images = req.referenceImages ?? [];
3584
3743
  const videos = req.referenceVideos ?? [];
3585
- 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";
3586
- const responseFormat = { type: "video" };
3587
- if (req.sourceVideo === void 0) responseFormat.aspect_ratio = toOmniAspect(req.aspectRatio);
3588
- const omniResolution = toOmniResolution(req.resolution);
3589
- if (omniResolution !== void 0) responseFormat.resolution = omniResolution;
3590
- responseFormat.duration = `${duration}s`;
3744
+ const task = req.sourceVideo ? "edit" : req.firstFrame ? "image_to_video" : images.length > 0 || videos.length > 0 ? "reference_to_video" : "text_to_video";
3745
+ const isOmniV1 = providerModelId === "gemini-omni-flash-preview";
3746
+ const responseFormat = { type: "video", delivery: "uri" };
3747
+ if (req.sourceVideo === void 0 && req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
3748
+ responseFormat.aspect_ratio = toOmniAspect(req.aspectRatio);
3749
+ }
3750
+ if (!isOmniV1) {
3751
+ const omniResolution = toOmniResolution(req.resolution);
3752
+ if (omniResolution !== void 0) responseFormat.resolution = omniResolution;
3753
+ }
3754
+ if (req.durationSeconds !== void 0) responseFormat.duration = `${req.durationSeconds}s`;
3591
3755
  const client2 = createGenAIClient({ apiKey });
3592
3756
  const uploadedByUri = /* @__PURE__ */ new Map();
3593
3757
  const uploadedNames = [];
@@ -3611,7 +3775,8 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3611
3775
  model: providerModelId,
3612
3776
  input,
3613
3777
  response_format: responseFormat,
3614
- generation_config: { video_config: { task } }
3778
+ generation_config: { video_config: { task } },
3779
+ store: true
3615
3780
  });
3616
3781
  const createRetryOptions = { attempts: 3 };
3617
3782
  if (ctx.signal !== void 0) createRetryOptions.signal = ctx.signal;
@@ -3693,23 +3858,14 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3693
3858
  };
3694
3859
  return inline;
3695
3860
  });
3696
- const inlineRetryOptions = { attempts: 3 };
3697
- if (ctx.signal !== void 0) inlineRetryOptions.signal = ctx.signal;
3698
3861
  try {
3699
- interaction = await withRetry(async () => {
3700
- try {
3701
- return await callCreate(inlineParts);
3702
- } catch (inlineCause) {
3703
- throwSdkError(inlineCause, modelId);
3704
- }
3705
- }, inlineRetryOptions);
3862
+ interaction = await callCreate(inlineParts);
3706
3863
  } catch (inlineCause) {
3707
3864
  throwSdkError(inlineCause, modelId);
3708
3865
  }
3709
3866
  }
3710
- } catch (cause) {
3867
+ } finally {
3711
3868
  await cleanupUploads();
3712
- throw cause;
3713
3869
  }
3714
3870
  const jobId = interaction.id ?? "gemini-omni-interaction";
3715
3871
  const usage2 = videoUsage2(ctx, req, duration);
@@ -3738,47 +3894,43 @@ async function generateOmniVideo(req, ctx, createGenAIClient) {
3738
3894
  timeoutMs: opts?.timeoutMs ?? RESULT_TIMEOUT_MS2
3739
3895
  };
3740
3896
  if (signal !== void 0) pollOptions.signal = signal;
3741
- try {
3742
- const done = await pollUntil(async () => {
3743
- const current = await getInteraction();
3744
- const status = (current.status ?? "").toUpperCase();
3745
- if (status === "COMPLETED") return current;
3746
- if (status === "FAILED") {
3747
- throw classifyHttpError(
3748
- void 0,
3749
- `Gemini Omni interaction failed: ${JSON.stringify(current).slice(0, 500)}`,
3750
- "google-genai",
3751
- modelId
3752
- );
3753
- }
3754
- return void 0;
3755
- }, pollOptions);
3756
- const video = extractOmniVideo(done, modelId);
3757
- if (video.data) {
3758
- return {
3759
- video: {
3760
- bytes: Buffer.from(video.data, "base64"),
3761
- mime: video.mime_type ?? "video/mp4"
3762
- },
3763
- usage: usage2,
3764
- raw: done
3765
- };
3897
+ const done = await pollUntil(async () => {
3898
+ const current = await getInteraction();
3899
+ const status = (current.status ?? "").toUpperCase();
3900
+ if (status === "COMPLETED") return current;
3901
+ if (status === "FAILED") {
3902
+ throw classifyHttpError(
3903
+ void 0,
3904
+ `Gemini Omni interaction failed: ${JSON.stringify(current).slice(0, 500)}`,
3905
+ "google-genai",
3906
+ modelId
3907
+ );
3766
3908
  }
3767
- if (video.uri)
3768
- return {
3769
- video: await downloadVideo(ctx, video.uri, apiKey, signal),
3770
- usage: usage2,
3771
- raw: done
3772
- };
3773
- throw classifyHttpError(
3774
- void 0,
3775
- "No video content in Gemini Omni response",
3776
- "google-genai",
3777
- modelId
3778
- );
3779
- } finally {
3780
- await cleanupUploads();
3909
+ return void 0;
3910
+ }, pollOptions);
3911
+ const video = extractOmniVideo(done, modelId);
3912
+ if (video.data) {
3913
+ return {
3914
+ video: {
3915
+ bytes: Buffer.from(video.data, "base64"),
3916
+ mime: video.mime_type ?? "video/mp4"
3917
+ },
3918
+ usage: usage2,
3919
+ raw: done
3920
+ };
3781
3921
  }
3922
+ if (video.uri)
3923
+ return {
3924
+ video: await downloadVideo(ctx, video.uri, apiKey, signal),
3925
+ usage: usage2,
3926
+ raw: done
3927
+ };
3928
+ throw classifyHttpError(
3929
+ void 0,
3930
+ "No video content in Gemini Omni response",
3931
+ "google-genai",
3932
+ modelId
3933
+ );
3782
3934
  }
3783
3935
  };
3784
3936
  }
@@ -3947,8 +4099,14 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
3947
4099
  content.push({ type: "text", text: req.prompt });
3948
4100
  void ctx.call.postCropToAspect;
3949
4101
  const imageConfig = {};
3950
- if (req.aspectRatio !== void 0) imageConfig.aspectRatio = req.aspectRatio;
4102
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
4103
+ imageConfig.aspectRatio = req.aspectRatio;
3951
4104
  if (req.size !== void 0) imageConfig.imageSize = req.size;
4105
+ const googleOptions = {
4106
+ imageConfig,
4107
+ responseModalities: ["IMAGE"]
4108
+ };
4109
+ if (req.safetySettings !== void 0) googleOptions.safetySettings = req.safetySettings;
3952
4110
  const retryOptions = {};
3953
4111
  if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
3954
4112
  const result = await withRetry(async () => {
@@ -3960,22 +4118,7 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
3960
4118
  messages: [{ role: "user", content }],
3961
4119
  maxRetries: 0,
3962
4120
  providerOptions: {
3963
- google: {
3964
- imageConfig,
3965
- responseModalities: ["IMAGE"],
3966
- safetySettings: [
3967
- {
3968
- category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
3969
- threshold: "OFF"
3970
- },
3971
- { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "OFF" },
3972
- {
3973
- category: "HARM_CATEGORY_DANGEROUS_CONTENT",
3974
- threshold: "OFF"
3975
- },
3976
- { category: "HARM_CATEGORY_HARASSMENT", threshold: "OFF" }
3977
- ]
3978
- }
4121
+ google: googleOptions
3979
4122
  },
3980
4123
  abortSignal: signal
3981
4124
  });
@@ -4004,6 +4147,10 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
4004
4147
  imageQuality: "auto"
4005
4148
  };
4006
4149
  if (req.size !== void 0) usage2.imageSize = req.size;
4150
+ if (result.usage?.inputTokens !== void 0)
4151
+ usage2.inputTokens = result.usage.inputTokens;
4152
+ if (result.usage?.outputTokens !== void 0)
4153
+ usage2.outputTokens = result.usage.outputTokens;
4007
4154
  return { images, usage: usage2, raw: result };
4008
4155
  }
4009
4156
  };
@@ -4158,6 +4305,7 @@ var KLING_MAX_INTERVAL_MS = 1e4;
4158
4305
  var KLING_TIMEOUT_MS = 15 * 6e4;
4159
4306
  var DOP_INTERVAL_MS = 5e3;
4160
4307
  var DOP_TIMEOUT_MS = 5 * 6e4;
4308
+ var DOP_FALLBACK_DURATION_SECONDS = 5;
4161
4309
  var DEFAULT_NEGATIVE_PROMPT = "text, watermark, copyright, blur, low quality, distortion, abstract, weird, deformed";
4162
4310
  function credentials(ctx) {
4163
4311
  const key = ctx.keys.higgsfield;
@@ -4232,6 +4380,18 @@ function requestInit(ctx, init) {
4232
4380
  if (ctx.signal !== void 0) request.signal = ctx.signal;
4233
4381
  return request;
4234
4382
  }
4383
+ async function createRequest(ctx, url, init) {
4384
+ let res;
4385
+ try {
4386
+ res = await ctx.fetch(url, requestInit(ctx, init));
4387
+ } catch (cause) {
4388
+ throw classifyThrown("higgsfield", cause, ctx.model.id);
4389
+ }
4390
+ if (!res.ok) {
4391
+ throw classifyHttpError(res.status, await readBody2(res), "higgsfield", ctx.model.id);
4392
+ }
4393
+ return res;
4394
+ }
4235
4395
  async function generateKlingVideo(req, ctx) {
4236
4396
  const firstFrame = req.firstFrame ? refToUrl2(req.firstFrame) : void 0;
4237
4397
  if (!firstFrame) {
@@ -4250,26 +4410,16 @@ async function generateKlingVideo(req, ctx) {
4250
4410
  negative_prompt: req.negativePrompt ?? DEFAULT_NEGATIVE_PROMPT
4251
4411
  };
4252
4412
  if (req.durationSeconds !== void 0) body.duration = req.durationSeconds;
4253
- if (req.aspectRatio !== void 0) body.aspect_ratio = req.aspectRatio;
4413
+ if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive")
4414
+ body.aspect_ratio = req.aspectRatio;
4254
4415
  if (req.lastFrame) body.last_image_url = refToUrl2(req.lastFrame);
4255
4416
  const endpoint = klingEndpoint(ctx.model.providerModelId);
4256
- let createRes;
4257
- try {
4258
- createRes = await ctx.fetch(
4259
- endpoint,
4260
- requestInit(ctx, { method: "POST", body: JSON.stringify(body) })
4261
- );
4262
- } catch (cause) {
4263
- throw networkError(cause, ctx);
4264
- }
4265
- if (!createRes.ok) {
4266
- throw classifyHttpError(
4267
- createRes.status,
4268
- await readBody2(createRes),
4269
- "higgsfield",
4270
- ctx.model.id
4271
- );
4272
- }
4417
+ const retryOptions = {};
4418
+ if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
4419
+ const createRes = await withRetry(
4420
+ () => createRequest(ctx, endpoint, { method: "POST", body: JSON.stringify(body) }),
4421
+ retryOptions
4422
+ );
4273
4423
  const created = klingCreateSchema.safeParse(await createRes.json());
4274
4424
  if (!created.success) {
4275
4425
  throw new ModelKitError({
@@ -4349,11 +4499,6 @@ async function generateKlingVideo(req, ctx) {
4349
4499
  }
4350
4500
  };
4351
4501
  }
4352
- function networkError(cause, ctx) {
4353
- if (isModelKitError(cause)) return cause;
4354
- const info = describeThrown(cause);
4355
- return classifyHttpError(info.status, info.body, "higgsfield", ctx.model.id);
4356
- }
4357
4502
  var dopMotionOptionsSchema = z10.object({
4358
4503
  modelkit: z10.object({ motion: z10.string().optional() }).optional()
4359
4504
  });
@@ -4425,26 +4570,15 @@ async function generateDopVideo(req, ctx) {
4425
4570
  params.input_images_end = [{ type: "image_url", image_url: refToUrl2(req.lastFrame) }];
4426
4571
  }
4427
4572
  if (req.seed !== void 0) params.seed = req.seed;
4428
- let createRes;
4429
- try {
4430
- createRes = await ctx.fetch(
4431
- DOP_ENDPOINT,
4432
- requestInit(ctx, {
4433
- method: "POST",
4434
- body: JSON.stringify({ params, webhook: null })
4435
- })
4436
- );
4437
- } catch (cause) {
4438
- throw networkError(cause, ctx);
4439
- }
4440
- if (!createRes.ok) {
4441
- throw classifyHttpError(
4442
- createRes.status,
4443
- await readBody2(createRes),
4444
- "higgsfield",
4445
- ctx.model.id
4446
- );
4447
- }
4573
+ const retryOptions = {};
4574
+ if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
4575
+ const createRes = await withRetry(
4576
+ () => createRequest(ctx, DOP_ENDPOINT, {
4577
+ method: "POST",
4578
+ body: JSON.stringify({ params, webhook: null })
4579
+ }),
4580
+ retryOptions
4581
+ );
4448
4582
  const created = dopCreateSchema.safeParse(await createRes.json());
4449
4583
  if (!created.success) {
4450
4584
  throw new ModelKitError({
@@ -4513,7 +4647,10 @@ async function generateDopVideo(req, ctx) {
4513
4647
  };
4514
4648
  if (dopSignal !== void 0) pollSettings.signal = dopSignal;
4515
4649
  const url = await pollUntil(check, pollSettings);
4516
- const result = { video: { url }, usage: videoUsage3(ctx, req) };
4650
+ const result = {
4651
+ video: { url },
4652
+ usage: videoUsage3(ctx, req, req.durationSeconds ?? DOP_FALLBACK_DURATION_SECONDS)
4653
+ };
4517
4654
  return result;
4518
4655
  }
4519
4656
  };
@@ -4535,7 +4672,7 @@ function aspectRatioToSize(ratio, tier) {
4535
4672
  };
4536
4673
  const MIN_PIXELS = 655360;
4537
4674
  const MAX_PIXELS = 8294400;
4538
- const maxLong = TIER_LONG_EDGE[tier];
4675
+ const maxLong = TIER_LONG_EDGE[tier] ?? 1024;
4539
4676
  const RATIO_MAP = /* @__PURE__ */ new Map([
4540
4677
  ["1:1", [1, 1]],
4541
4678
  ["16:9", [16, 9]],
@@ -4695,28 +4832,16 @@ var openaiImageAdapter = {
4695
4832
  const usageParsed = openaiUsageSchema.safeParse(response.usage);
4696
4833
  const inputTokens = usageParsed.success ? usageParsed.data.input_tokens : void 0;
4697
4834
  const outputTokens = usageParsed.success ? usageParsed.data.output_tokens : void 0;
4698
- let usage2;
4699
- if (inputTokens !== void 0 || outputTokens !== void 0) {
4700
- usage2 = {
4701
- model: ctx.model.id,
4702
- provider: "openai-image",
4703
- modality: "IMAGE",
4704
- unit: "tokens",
4705
- quantity: (inputTokens ?? 0) + (outputTokens ?? 0),
4706
- imageQuality: quality
4707
- };
4708
- if (inputTokens !== void 0) usage2.inputTokens = inputTokens;
4709
- if (outputTokens !== void 0) usage2.outputTokens = outputTokens;
4710
- } else {
4711
- usage2 = {
4712
- model: ctx.model.id,
4713
- provider: "openai-image",
4714
- modality: "IMAGE",
4715
- unit: "images",
4716
- quantity: images.length,
4717
- imageQuality: quality
4718
- };
4719
- }
4835
+ const usage2 = {
4836
+ model: ctx.model.id,
4837
+ provider: "openai-image",
4838
+ modality: "IMAGE",
4839
+ unit: "images",
4840
+ quantity: images.length,
4841
+ imageQuality: quality
4842
+ };
4843
+ if (inputTokens !== void 0) usage2.inputTokens = inputTokens;
4844
+ if (outputTokens !== void 0) usage2.outputTokens = outputTokens;
4720
4845
  if (req.size !== void 0) usage2.imageSize = req.size;
4721
4846
  return { images, usage: usage2, raw: response };
4722
4847
  }
@@ -4769,7 +4894,12 @@ var NANO_BANANA_RATIOS = /* @__PURE__ */ new Set([
4769
4894
  ]);
4770
4895
  async function refToBytes(ctx, ref) {
4771
4896
  if ("bytes" in ref) return { bytes: ref.bytes, mime: ref.mime };
4772
- const res = await ctx.fetch(ref.url);
4897
+ let res;
4898
+ try {
4899
+ res = await ctx.fetch(ref.url);
4900
+ } catch (cause) {
4901
+ throw classifyThrown("prodia", cause, ctx.model.id);
4902
+ }
4773
4903
  if (!res.ok) {
4774
4904
  const text2 = await res.text().catch(() => "");
4775
4905
  throw classifyHttpError(res.status, text2, "prodia", ctx.model.id);
@@ -4939,8 +5069,7 @@ async function generateProdiaImage(req, ctx) {
4939
5069
  body: form
4940
5070
  });
4941
5071
  } catch (err) {
4942
- if (isModelKitError(err)) throw err;
4943
- throw transportError("prodia", err, ctx.model.id);
5072
+ throw classifyThrown("prodia", err, ctx.model.id);
4944
5073
  }
4945
5074
  if (!attemptRes.ok) {
4946
5075
  const text2 = await attemptRes.text().catch(() => "");
@@ -4949,8 +5078,7 @@ async function generateProdiaImage(req, ctx) {
4949
5078
  return attemptRes;
4950
5079
  }, retryOpts);
4951
5080
  } catch (err) {
4952
- if (isModelKitError(err)) throw err;
4953
- throw transportError("prodia", err, ctx.model.id);
5081
+ throw classifyThrown("prodia", err, ctx.model.id);
4954
5082
  }
4955
5083
  const bytes = parseImageResponse(
4956
5084
  new Uint8Array(await res.arrayBuffer()),
@@ -5022,7 +5150,7 @@ function replicateError(cause, ctx) {
5022
5150
  const info = describeThrown(cause);
5023
5151
  const nested = replicateResponseStatusSchema.safeParse(cause);
5024
5152
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
5025
- return classifyHttpError(status, info.body, "replicate", ctx.model.id);
5153
+ return classifyThrown("replicate", cause, ctx.model.id, { status, body: info.body });
5026
5154
  }
5027
5155
  function isLower(id) {
5028
5156
  return id.toLowerCase();
@@ -5169,6 +5297,9 @@ function isUpscaler(providerModelId) {
5169
5297
  const id = isLower(providerModelId);
5170
5298
  return id.includes("upscal") || id.includes("crystal") || id.includes("topaz");
5171
5299
  }
5300
+ function isTopazUpscaler(providerModelId) {
5301
+ return isLower(providerModelId).includes("topazlabs/image-upscale");
5302
+ }
5172
5303
  function isQwen(providerModelId) {
5173
5304
  return isLower(providerModelId).includes("qwen");
5174
5305
  }
@@ -5218,8 +5349,36 @@ function wanImageSize(aspectRatio, size) {
5218
5349
  }
5219
5350
  async function generateReplicateImage(req, ctx, deps) {
5220
5351
  const id = ctx.model.providerModelId;
5221
- const input = { prompt: req.prompt };
5222
- if (isUpscaler(id)) {
5352
+ let input;
5353
+ if (isTopazUpscaler(id)) {
5354
+ const first = (req.references ?? [])[0];
5355
+ if (!first) {
5356
+ throw new ModelKitError({
5357
+ code: "missing_input",
5358
+ message: `${ctx.model.id} requires a reference image to upscale`,
5359
+ provider: "replicate",
5360
+ model: ctx.model.id,
5361
+ retryable: false
5362
+ });
5363
+ }
5364
+ let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
5365
+ if (format !== "png" && format !== "jpg") {
5366
+ ctx.warnings.push({
5367
+ code: "option_ignored",
5368
+ field: "outputFormat",
5369
+ from: format,
5370
+ message: "topazlabs/image-upscale supports only png and jpg; using png"
5371
+ });
5372
+ format = "png";
5373
+ }
5374
+ input = {
5375
+ image: refToUrl3(first),
5376
+ enhance_model: "Standard V2",
5377
+ output_format: format,
5378
+ upscale_factor: `${req.scaleFactor ?? 2}x`,
5379
+ face_enhancement: true
5380
+ };
5381
+ } else if (isUpscaler(id)) {
5223
5382
  const refs = req.references ?? [];
5224
5383
  const first = refs[0];
5225
5384
  if (!first) {
@@ -5231,24 +5390,29 @@ async function generateReplicateImage(req, ctx, deps) {
5231
5390
  retryable: false
5232
5391
  });
5233
5392
  }
5234
- input.image = refToUrl3(first);
5235
- input.creativity = 0;
5236
- input.scale_factor = 2;
5237
- input.output_format = "png";
5393
+ const imageInput = { prompt: req.prompt };
5394
+ imageInput.image = refToUrl3(first);
5395
+ imageInput.creativity = 0;
5396
+ imageInput.scale_factor = 2;
5397
+ imageInput.output_format = "png";
5398
+ input = imageInput;
5238
5399
  } else if (isWanImage(id)) {
5239
5400
  const refs = (req.references ?? []).map(refToUrl3);
5240
- if (refs.length > 0) input.images = refs;
5241
- input.size = wanImageSize(req.aspectRatio, req.size);
5401
+ const imageInput = { prompt: req.prompt };
5402
+ if (refs.length > 0) imageInput.images = refs;
5403
+ imageInput.size = wanImageSize(req.aspectRatio, req.size);
5404
+ input = imageInput;
5242
5405
  } else if (isQwen(id)) {
5243
5406
  const refs = (req.references ?? []).map(refToUrl3);
5244
5407
  const firstUrl = refs[0];
5408
+ const imageInput = { prompt: req.prompt };
5245
5409
  if (firstUrl !== void 0) {
5246
- input.image = firstUrl;
5247
- if (!isQwenEditPlus(id)) input.match_input_image = true;
5410
+ imageInput.image = firstUrl;
5411
+ if (!isQwenEditPlus(id)) imageInput.match_input_image = true;
5248
5412
  }
5249
5413
  const ratios = isQwenEditPlus(id) ? QWEN_EDIT_RATIOS : QWEN_IMAGE_RATIOS;
5250
5414
  if (req.aspectRatio !== void 0) {
5251
- if (ratios.has(req.aspectRatio)) input.aspect_ratio = req.aspectRatio;
5415
+ if (ratios.has(req.aspectRatio)) imageInput.aspect_ratio = req.aspectRatio;
5252
5416
  else
5253
5417
  ctx.warnings.push({
5254
5418
  code: "option_ignored",
@@ -5258,18 +5422,21 @@ async function generateReplicateImage(req, ctx, deps) {
5258
5422
  });
5259
5423
  }
5260
5424
  if (isQwenEditPlus(id) && req.outputFormat !== void 0) {
5261
- input.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
5425
+ imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
5262
5426
  }
5427
+ input = imageInput;
5263
5428
  } else {
5264
5429
  const refs = req.references ?? [];
5265
5430
  const first = refs[0];
5266
- if (first) input.image = refToUrl3(first);
5431
+ const imageInput = { prompt: req.prompt };
5432
+ if (first) imageInput.image = refToUrl3(first);
5433
+ input = imageInput;
5267
5434
  }
5268
5435
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5269
5436
  const file = await download(ctx, url);
5270
5437
  const usage2 = imageUsage2(ctx, req);
5271
5438
  usage2.quantity = predictTime;
5272
- return { images: [{ bytes: file.bytes, mime: file.mime }], usage: usage2, raw };
5439
+ return { images: [{ bytes: file.bytes, mime: file.mime }], usage: usage2, raw, outputUrl: url };
5273
5440
  }
5274
5441
  function wanSize(aspectRatio) {
5275
5442
  if (!aspectRatio || aspectRatio === "adaptive" || aspectRatio === "1:1") return "2K";
@@ -5293,7 +5460,7 @@ function klingInput(req) {
5293
5460
  input.start_image = startImage;
5294
5461
  const endImage = images[1];
5295
5462
  if (endImage !== void 0) input.end_image = endImage;
5296
- } else {
5463
+ } else if (req.aspectRatio !== "adaptive") {
5297
5464
  input.aspect_ratio = req.aspectRatio ?? "16:9";
5298
5465
  }
5299
5466
  if (req.negativePrompt !== void 0) input.negative_prompt = req.negativePrompt;
@@ -5313,7 +5480,7 @@ function klingOmniInput(req) {
5313
5480
  input.start_image = startImage;
5314
5481
  const endImage = images[1];
5315
5482
  if (endImage !== void 0) input.end_image = endImage;
5316
- } else {
5483
+ } else if (req.aspectRatio !== "adaptive") {
5317
5484
  input.aspect_ratio = req.aspectRatio ?? "16:9";
5318
5485
  }
5319
5486
  const refs = (req.referenceImages ?? []).map(refToUrl3);
@@ -5522,11 +5689,10 @@ var xaiResponseStatusSchema = z13.object({
5522
5689
  response: z13.object({ status: z13.number() }).partial()
5523
5690
  });
5524
5691
  function xaiError(cause, ctx) {
5525
- if (isModelKitError(cause)) return cause;
5526
5692
  const info = describeThrown(cause);
5527
5693
  const nested = xaiResponseStatusSchema.safeParse(cause);
5528
5694
  const status = info.status ?? (nested.success ? nested.data.response?.status : void 0);
5529
- return classifyHttpError(status, info.body, "xai-video", ctx.model.id);
5695
+ return classifyThrown("xai-video", cause, ctx.model.id, { status, body: info.body });
5530
5696
  }
5531
5697
  var xaiMetaSchema = z13.object({
5532
5698
  videoUrl: JsonValueSchema.optional(),
@@ -5550,10 +5716,7 @@ async function generateXaiVideo(req, ctx, deps) {
5550
5716
  retryable: false
5551
5717
  });
5552
5718
  }
5553
- const frames = [req.firstFrame, req.lastFrame, ...req.referenceImages ?? []].filter(
5554
- (r) => r !== void 0
5555
- );
5556
- const refUrls = frames.map(refToUrl4).slice(0, 5);
5719
+ const refUrls = (req.referenceImages ?? []).map(refToUrl4).slice(0, 7);
5557
5720
  const resolution = req.resolution === "480p" ? "480p" : "720p";
5558
5721
  const xaiOptions = {
5559
5722
  resolution,
@@ -5564,13 +5727,23 @@ async function generateXaiVideo(req, ctx, deps) {
5564
5727
  xaiOptions.mode = "reference-to-video";
5565
5728
  xaiOptions.referenceImageUrls = refUrls;
5566
5729
  }
5730
+ if (req.lastFrame !== void 0) {
5731
+ ctx.warnings.push({
5732
+ code: "option_ignored",
5733
+ field: "lastFrame",
5734
+ message: "xai-video does not support lastFrame; ignored"
5735
+ });
5736
+ }
5567
5737
  const client2 = deps.createXaiClient({ apiKey });
5568
- const model = createXai2({ apiKey }).video(ctx.model.providerModelId);
5738
+ const model = createXai2({ apiKey, fetch: ctx.fetch }).video(ctx.model.providerModelId);
5569
5739
  const callBody = {
5570
5740
  model,
5571
5741
  prompt: req.prompt,
5572
5742
  providerOptions: { xai: xaiOptions }
5573
5743
  };
5744
+ if (req.firstFrame) {
5745
+ callBody.frameImages = [{ image: refToUrl4(req.firstFrame), frameType: "first_frame" }];
5746
+ }
5574
5747
  if (req.aspectRatio !== void 0 && req.aspectRatio !== "adaptive") {
5575
5748
  callBody.aspectRatio = req.aspectRatio;
5576
5749
  }
@@ -5656,26 +5829,14 @@ async function mapResult(ctx, req, result) {
5656
5829
  function usage(ctx, req, meta) {
5657
5830
  const costTicks = z13.number().safeParse(meta.costInUsdTicks);
5658
5831
  const cost = costTicks.success ? costTicks.data / 1e10 : void 0;
5659
- if (cost !== void 0) {
5660
- const event2 = {
5661
- model: ctx.model.id,
5662
- provider: "xai-video",
5663
- modality: "VIDEO",
5664
- unit: "reported_cost_usd",
5665
- quantity: cost,
5666
- reportedCostUsd: cost
5667
- };
5668
- if (req.durationSeconds !== void 0) event2.durationSeconds = req.durationSeconds;
5669
- if (req.resolution !== void 0) event2.resolution = req.resolution;
5670
- return event2;
5671
- }
5672
5832
  const event = {
5673
5833
  model: ctx.model.id,
5674
5834
  provider: "xai-video",
5675
5835
  modality: "VIDEO",
5676
- unit: "seconds",
5677
- quantity: req.durationSeconds ?? 0
5836
+ unit: "reported_cost_usd",
5837
+ quantity: cost ?? 0
5678
5838
  };
5839
+ if (cost !== void 0) event.reportedCostUsd = cost;
5679
5840
  if (req.durationSeconds !== void 0) event.durationSeconds = req.durationSeconds;
5680
5841
  if (req.resolution !== void 0) event.resolution = req.resolution;
5681
5842
  return event;