@shop-os/modelkit 0.1.0 → 0.1.2

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.d.cts CHANGED
@@ -14,7 +14,8 @@ type VideoModel = Parameters<typeof experimental_generateVideo>[0]["model"];
14
14
  type AspectRatio = "1:1" | "16:9" | "9:16" | "4:3" | "3:4" | "3:2" | "2:3" | "4:5" | "5:4" | "21:9" | "9:21" | "97:30" | "adaptive";
15
15
  type ImageSize = "1K" | "2K" | "4K";
16
16
  type VideoResolution = "360p" | "480p" | "720p" | "768p" | "1080p" | "2k" | "4k";
17
- type ImageQuality = "auto" | "low" | "medium" | "high";
17
+ /** "xhigh" and "max" exist only on gpt-image-2.5; gpt-image-2 rejects them at the provider. */
18
+ type ImageQuality = "auto" | "low" | "medium" | "high" | "xhigh" | "max";
18
19
  type OutputFormat = "png" | "jpeg" | "webp";
19
20
  type MediaRef = {
20
21
  url: string;
package/dist/index.d.ts CHANGED
@@ -14,7 +14,8 @@ type VideoModel = Parameters<typeof experimental_generateVideo>[0]["model"];
14
14
  type AspectRatio = "1:1" | "16:9" | "9:16" | "4:3" | "3:4" | "3:2" | "2:3" | "4:5" | "5:4" | "21:9" | "9:21" | "97:30" | "adaptive";
15
15
  type ImageSize = "1K" | "2K" | "4K";
16
16
  type VideoResolution = "360p" | "480p" | "720p" | "768p" | "1080p" | "2k" | "4k";
17
- type ImageQuality = "auto" | "low" | "medium" | "high";
17
+ /** "xhigh" and "max" exist only on gpt-image-2.5; gpt-image-2 rejects them at the provider. */
18
+ type ImageQuality = "auto" | "low" | "medium" | "high" | "xhigh" | "max";
18
19
  type OutputFormat = "png" | "jpeg" | "webp";
19
20
  type MediaRef = {
20
21
  url: string;
package/dist/index.js CHANGED
@@ -130,6 +130,44 @@ var IMAGE_MODELS = [
130
130
  usage: { ...IMAGE_USAGE },
131
131
  pricingKind: "image_size"
132
132
  },
133
+ {
134
+ // Same Images API surface as gpt-image-2 plus the "xhigh" and "max" quality tiers.
135
+ id: "gpt-image-2.5-sunburst",
136
+ aliases: [],
137
+ label: "GPT Image 2.5 Sunburst",
138
+ type: "image",
139
+ adapter: "openai-image",
140
+ providerModelId: "gpt-image-2.5-sunburst",
141
+ lifecycle: "active",
142
+ constraints: {
143
+ aspectRatios: ["1:1", "3:2", "2:3", "16:9", "9:16", "4:5", "5:4", "adaptive"],
144
+ sizes: ["1K", "2K", "4K"],
145
+ references: { images: 16 },
146
+ supports: ["quality", "count"]
147
+ },
148
+ defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
149
+ usage: { ...IMAGE_USAGE },
150
+ pricingKind: "image_size"
151
+ },
152
+ {
153
+ // Same Images API surface as gpt-image-2 plus the "xhigh" and "max" quality tiers.
154
+ id: "gpt-image-2.5-flare",
155
+ aliases: [],
156
+ label: "GPT Image 2.5 Flare",
157
+ type: "image",
158
+ adapter: "openai-image",
159
+ providerModelId: "gpt-image-2.5-flare",
160
+ lifecycle: "active",
161
+ constraints: {
162
+ aspectRatios: ["1:1", "3:2", "2:3", "16:9", "9:16", "4:5", "5:4", "adaptive"],
163
+ sizes: ["1K", "2K", "4K"],
164
+ references: { images: 16 },
165
+ supports: ["quality", "count"]
166
+ },
167
+ defaults: { size: "1K", aspectRatio: "adaptive", quality: "auto" },
168
+ usage: { ...IMAGE_USAGE },
169
+ pricingKind: "image_size"
170
+ },
133
171
  {
134
172
  // Cowork allows 7 ratios; trigger-workflows allows 9 (adds 3:2, 9:21, drops 4:5/5:4).
135
173
  id: "muse-image-1.0",
@@ -506,77 +544,42 @@ function text(id, label, providerModelId, extra) {
506
544
  ...extra
507
545
  };
508
546
  }
509
- var ANTHROPIC_THINKING = {
510
- anthropic: {
511
- thinking: { type: "enabled", budgetTokens: 4e3 },
512
- sendReasoning: true
513
- }
514
- };
515
- var ANTHROPIC_ADAPTIVE = {
516
- anthropic: {
517
- thinking: { type: "adaptive", display: "summarized" },
518
- sendReasoning: true
519
- }
520
- };
521
- var OPENAI_REASONING = {
522
- openai: {
523
- reasoningEffort: "medium",
524
- reasoningSummary: "auto"
525
- }
526
- };
527
- var DEEPSEEK_THINKING = {
528
- deepseek: {
529
- thinking: { type: "enabled" }
530
- }
531
- };
532
547
  var TEXT_MODELS = [
533
548
  text("gpt-4o", "GPT-4o", "gpt-4o"),
534
549
  text("gpt-4o-mini", "GPT-4o mini", "gpt-4o-mini"),
535
550
  text("gpt-5.1", "GPT-5.1", "gpt-5.1"),
536
- text("gpt-5.1-mini", "GPT-5.1 mini", "gpt-5.1-mini"),
537
551
  text("gpt-5.2", "GPT-5.2", "gpt-5.2"),
538
- text("gpt-5.2-mini", "GPT-5.2 mini", "gpt-5.2-mini"),
539
552
  text("gpt-5.4", "GPT-5.4", "gpt-5.4"),
540
- text("gpt-5.4-mini", "GPT-5.4 mini", "gpt-5.4-mini"),
541
- text("gpt-5.5", "GPT-5.5", "gpt-5.5"),
542
- text("gpt-5.6-luna", "GPT-5.6 Luna", "gpt-5.6-luna", {
543
- providerOptions: { ...OPENAI_REASONING }
544
- }),
545
- text("gpt-5.6-terra", "GPT-5.6 Terra", "gpt-5.6-terra", {
546
- providerOptions: { ...OPENAI_REASONING }
547
- }),
548
- text("gpt-5.6-sol", "GPT-5.6 Sol", "gpt-5.6-sol", {
549
- providerOptions: { ...OPENAI_REASONING }
550
- }),
551
- // Dated 4.0 ids are what vibe-api, pro-admin and the TW geo agents still send.
552
- text("claude-sonnet-4", "Claude Sonnet 4", "claude-sonnet-4-20250514", {
553
- aliases: ["claude-sonnet-4.0", "claude-sonnet-4-20250514"]
554
- }),
555
- text("claude-opus-4", "Claude Opus 4", "claude-opus-4-20250514", {
556
- aliases: ["claude-opus-4.0", "claude-opus-4-20250514"]
553
+ // OpenAI never shipped gpt-5.1-mini or gpt-5.2-mini; callers that still send them get 5.4 mini.
554
+ text("gpt-5.4-mini", "GPT-5.4 mini", "gpt-5.4-mini", {
555
+ aliases: ["gpt-5.1-mini", "gpt-5.2-mini"]
557
556
  }),
557
+ text("gpt-5.5", "GPT-5.5", "gpt-5.5"),
558
+ text("gpt-5.6-luna", "GPT-5.6 Luna", "gpt-5.6-luna"),
559
+ text("gpt-5.6-terra", "GPT-5.6 Terra", "gpt-5.6-terra"),
560
+ text("gpt-5.6-sol", "GPT-5.6 Sol", "gpt-5.6-sol"),
558
561
  // Library key "claude-sonnet-4.5" maps to the SDK id "claude-sonnet-4-5".
559
562
  text("claude-sonnet-4-5", "Claude Sonnet 4.5", "claude-sonnet-4-5", {
560
563
  aliases: ["claude-sonnet-4.5"]
561
564
  }),
565
+ // Anthropic retired the dated 4.0 ids; callers that still send them get 4.6.
562
566
  text("claude-sonnet-4-6", "Claude Sonnet 4.6", "claude-sonnet-4-6", {
563
- aliases: ["claude-sonnet-4.6"],
564
- providerOptions: { ...ANTHROPIC_THINKING }
565
- }),
566
- text("claude-sonnet-5", "Claude Sonnet 5", "claude-sonnet-5", {
567
- providerOptions: { ...ANTHROPIC_ADAPTIVE }
567
+ aliases: [
568
+ "claude-sonnet-4.6",
569
+ "claude-sonnet-4",
570
+ "claude-sonnet-4.0",
571
+ "claude-sonnet-4-20250514"
572
+ ]
568
573
  }),
574
+ text("claude-sonnet-5", "Claude Sonnet 5", "claude-sonnet-5"),
569
575
  text("claude-opus-4-8", "Claude Opus 4.8", "claude-opus-4-8", {
570
- aliases: ["claude-opus-4.8"],
571
- providerOptions: { ...ANTHROPIC_ADAPTIVE }
576
+ aliases: ["claude-opus-4.8", "claude-opus-4", "claude-opus-4.0", "claude-opus-4-20250514"]
572
577
  }),
573
578
  text("claude-haiku-4-5", "Claude Haiku 4.5", "claude-haiku-4-5", {
574
- aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001"],
575
- providerOptions: { ...ANTHROPIC_THINKING }
579
+ aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001"]
576
580
  }),
577
581
  text("claude-opus-5", "Claude Opus 5", "claude-opus-5", {
578
- aliases: ["claude-opus-5.0"],
579
- providerOptions: { ...ANTHROPIC_ADAPTIVE }
582
+ aliases: ["claude-opus-5.0"]
580
583
  }),
581
584
  text("claude-fable-5", "Claude Fable 5", "claude-fable-5"),
582
585
  text("gemini-2.5-flash", "Gemini 2.5 Flash", "gemini-2.5-flash"),
@@ -595,12 +598,8 @@ var TEXT_MODELS = [
595
598
  text("sonar", "Sonar", "sonar"),
596
599
  text("sonar-pro", "Sonar Pro", "sonar-pro"),
597
600
  text("sonar-reasoning-pro", "Sonar Reasoning Pro", "sonar-reasoning-pro"),
598
- text("deepseek-v4-flash", "DeepSeek V4 Flash", "deepseek-v4-flash", {
599
- providerOptions: { ...DEEPSEEK_THINKING }
600
- }),
601
- text("deepseek-v4-pro", "DeepSeek V4 Pro", "deepseek-v4-pro", {
602
- providerOptions: { ...DEEPSEEK_THINKING }
603
- }),
601
+ text("deepseek-v4-flash", "DeepSeek V4 Flash", "deepseek-v4-flash"),
602
+ text("deepseek-v4-pro", "DeepSeek V4 Pro", "deepseek-v4-pro"),
604
603
  text("grok-4.3", "Grok 4.3", "grok-4.3"),
605
604
  text("kimi-k2.5", "Kimi K2.5", "kimi-k2.5"),
606
605
  text("kimi-k3", "Kimi K3", "kimi-k3"),
@@ -745,7 +744,7 @@ var VIDEO_MODELS = [
745
744
  durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
746
745
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
747
746
  ratioIgnoredWith: ["firstFrame"],
748
- supports: ["generateAudio", "seed"]
747
+ supports: ["generateAudio", "seed", "lastFrame"]
749
748
  },
750
749
  defaults: {
751
750
  durationSeconds: 5,
@@ -770,7 +769,7 @@ var VIDEO_MODELS = [
770
769
  durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
771
770
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
772
771
  ratioIgnoredWith: ["firstFrame"],
773
- supports: ["generateAudio", "seed"]
772
+ supports: ["generateAudio", "seed", "lastFrame"]
774
773
  },
775
774
  defaults: {
776
775
  durationSeconds: 5,
@@ -796,7 +795,7 @@ var VIDEO_MODELS = [
796
795
  durations: { min: 4, max: 30 },
797
796
  references: { images: 30, videos: 10, audios: 10 },
798
797
  ratioIgnoredWith: ["firstFrame"],
799
- supports: ["generateAudio", "seed"]
798
+ supports: ["generateAudio", "seed", "lastFrame"]
800
799
  },
801
800
  defaults: {
802
801
  durationSeconds: 5,
@@ -898,7 +897,7 @@ var VIDEO_MODELS = [
898
897
  resolutions: ["720p", "1080p"],
899
898
  durations: { min: 3, max: 15 },
900
899
  references: { images: 7 },
901
- supports: ["generateAudio"]
900
+ supports: ["generateAudio", "lastFrame"]
902
901
  },
903
902
  defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
904
903
  usage: { unit: "compute_seconds", modality: "VIDEO" },
@@ -1431,23 +1430,46 @@ async function withRetry(fn, opts = {}) {
1431
1430
  }
1432
1431
  function sleep(ms, signal) {
1433
1432
  return new Promise((resolve, reject) => {
1434
- const t = setTimeout(resolve, ms);
1435
- signal?.addEventListener(
1436
- "abort",
1437
- () => {
1438
- clearTimeout(t);
1439
- reject(
1440
- new ModelKitError({ code: "timeout", message: "aborted", retryable: false })
1441
- );
1442
- },
1443
- { once: true }
1444
- );
1433
+ const rejectAborted = () => {
1434
+ reject(new ModelKitError({ code: "timeout", message: "aborted", retryable: false }));
1435
+ };
1436
+ if (signal?.aborted) {
1437
+ rejectAborted();
1438
+ return;
1439
+ }
1440
+ let timer;
1441
+ const onAbort = () => {
1442
+ if (timer !== void 0) clearTimeout(timer);
1443
+ rejectAborted();
1444
+ };
1445
+ timer = setTimeout(() => {
1446
+ signal?.removeEventListener("abort", onAbort);
1447
+ resolve();
1448
+ }, ms);
1449
+ signal?.addEventListener("abort", onAbort, { once: true });
1445
1450
  });
1446
1451
  }
1452
+ function throwAborted() {
1453
+ return new ModelKitError({ code: "timeout", message: "aborted", retryable: false });
1454
+ }
1455
+ function transportError(provider, cause, model) {
1456
+ const message = cause instanceof Error ? cause.message : String(cause);
1457
+ const aborted = cause instanceof Error && cause.name === "AbortError";
1458
+ const init = {
1459
+ code: aborted ? "timeout" : "network",
1460
+ message: `${provider} request failed before a response: ${message}`,
1461
+ provider,
1462
+ retryable: !aborted,
1463
+ cause
1464
+ };
1465
+ if (model !== void 0) init.model = model;
1466
+ return new ModelKitError(init);
1467
+ }
1447
1468
  async function pollUntil(check, opts) {
1448
1469
  const start = Date.now();
1449
1470
  let interval = opts.intervalMs;
1450
1471
  for (; ; ) {
1472
+ if (opts.signal?.aborted) throw throwAborted();
1451
1473
  const value = await check();
1452
1474
  if (value !== void 0) return value;
1453
1475
  if (Date.now() - start > opts.timeoutMs)
@@ -1456,6 +1478,7 @@ async function pollUntil(check, opts) {
1456
1478
  message: `poll timed out after ${opts.timeoutMs}ms`,
1457
1479
  retryable: false
1458
1480
  });
1481
+ if (opts.signal?.aborted) throw throwAborted();
1459
1482
  await sleep(interval, opts.signal);
1460
1483
  if (opts.backoff)
1461
1484
  interval = Math.min(opts.maxIntervalMs ?? interval * 4, interval * opts.backoff);
@@ -1640,7 +1663,7 @@ var OPENAI_COMPATIBLE_BASE_URLS = {
1640
1663
  var GATEWAY_SLUGS = {
1641
1664
  openai: "openai",
1642
1665
  anthropic: "anthropic",
1643
- google: "google-ai-studio",
1666
+ google: "google-ai-studio/v1beta",
1644
1667
  xai: "grok",
1645
1668
  perplexity: "perplexity-ai",
1646
1669
  deepseek: "deepseek"
@@ -2066,13 +2089,21 @@ function createModelKit(config, adapters) {
2066
2089
  });
2067
2090
  const job = await adapter.generateVideo(request, ctx);
2068
2091
  const result = job.result.bind(job);
2092
+ let settled;
2069
2093
  return {
2070
2094
  ...job,
2071
2095
  warnings,
2072
- async result(opts) {
2073
- const out = await result(opts);
2074
- await emit({ ...out.usage, model: model.id });
2075
- return out;
2096
+ result(opts) {
2097
+ if (settled) return settled;
2098
+ const pending = result(opts).then(
2099
+ (out) => emit({ ...out.usage, model: model.id }).then(() => out),
2100
+ (cause) => {
2101
+ settled = void 0;
2102
+ throw cause;
2103
+ }
2104
+ );
2105
+ settled = pending;
2106
+ return pending;
2076
2107
  }
2077
2108
  };
2078
2109
  },
@@ -2433,6 +2464,7 @@ async function generateSeedanceVideo(req, ctx) {
2433
2464
  };
2434
2465
  if (!hasFrameAnchor && req.aspectRatio !== void 0) body.ratio = req.aspectRatio;
2435
2466
  if (req.generateAudio !== void 0) body.generate_audio = req.generateAudio;
2467
+ if (req.seed !== void 0) body.seed = req.seed;
2436
2468
  const submit = await withRetry(async () => {
2437
2469
  const res = await ctx.fetch(
2438
2470
  `${ARK_BASE_URL}${SUBMIT_PATH}`,
@@ -2886,7 +2918,7 @@ async function generateFalVideo(req, ctx, deps) {
2886
2918
  if (isKling(providerModelId)) {
2887
2919
  const input = klingO1Input(req);
2888
2920
  if (ctx.routing.fal === "cloudflare-passthrough") {
2889
- return passthroughVideoJob(ctx, req, KLING_O1_ENDPOINT, input);
2921
+ return await passthroughVideoJob(ctx, req, KLING_O1_ENDPOINT, input);
2890
2922
  }
2891
2923
  const result2 = await runJob(ctx, deps, KLING_O1_ENDPOINT, input);
2892
2924
  return completedVideoJob(
@@ -2921,7 +2953,7 @@ async function generateFalVideo(req, ctx, deps) {
2921
2953
  base.aspect_ratio = req.aspectRatio ?? "adaptive";
2922
2954
  }
2923
2955
  if (ctx.routing.fal === "cloudflare-passthrough") {
2924
- return passthroughVideoJob(ctx, req, endpoint, base);
2956
+ return await passthroughVideoJob(ctx, req, endpoint, base);
2925
2957
  }
2926
2958
  const result = await runJob(ctx, deps, endpoint, base);
2927
2959
  return completedVideoJob(ctx, req, extractVideoUrl(result, endpoint, ctx.model.id), result);
@@ -2939,31 +2971,26 @@ function completedVideoJob(ctx, req, url, raw) {
2939
2971
  }
2940
2972
  };
2941
2973
  }
2942
- function passthroughVideoJob(ctx, req, endpoint, input) {
2974
+ async function passthroughVideoJob(ctx, req, endpoint, input) {
2943
2975
  const cfg = gatewayConfig(ctx);
2944
- let statusUrl;
2945
- const submit = (async () => {
2946
- const res = await gatewayFetch(cfg, ctx.fetch, `${FAL_QUEUE_HOST}/${endpoint}`, {
2947
- method: "POST",
2948
- body: JSON.stringify({ ...input, sync_mode: true })
2976
+ const res = await gatewayFetch(cfg, ctx.fetch, `${FAL_QUEUE_HOST}/${endpoint}`, {
2977
+ method: "POST",
2978
+ body: JSON.stringify({ ...input, sync_mode: true })
2979
+ });
2980
+ if (!res.ok) throw falError(res.status, await readBody(res), ctx.model.id);
2981
+ const parsed = falQueueSubmitSchema.safeParse(await res.json());
2982
+ if (!parsed.success) {
2983
+ throw new ModelKitError({
2984
+ code: "provider_rejected",
2985
+ message: `fal ${endpoint} submit returned no status URL`,
2986
+ provider: "fal",
2987
+ model: ctx.model.id,
2988
+ retryable: false
2949
2989
  });
2950
- if (!res.ok) throw falError(res.status, await readBody(res), ctx.model.id);
2951
- const parsed = falQueueSubmitSchema.safeParse(await res.json());
2952
- if (!parsed.success) {
2953
- throw new ModelKitError({
2954
- code: "provider_rejected",
2955
- message: `fal ${endpoint} submit returned no status URL`,
2956
- provider: "fal",
2957
- model: ctx.model.id,
2958
- retryable: false
2959
- });
2960
- }
2961
- statusUrl = parsed.data.status_url;
2962
- return parsed.data;
2963
- })();
2990
+ }
2991
+ const { request_id: jobId, status_url, response_url } = parsed.data;
2964
2992
  const checkStatus = async () => {
2965
- const { response_url } = await submit;
2966
- const statusRes = await gatewayFetch(cfg, ctx.fetch, statusUrl ?? "", { method: "GET" });
2993
+ const statusRes = await gatewayFetch(cfg, ctx.fetch, status_url, { method: "GET" });
2967
2994
  if (!statusRes.ok)
2968
2995
  throw falError(statusRes.status, await readBody(statusRes), ctx.model.id);
2969
2996
  const statusParsed = falQueueStatusSchema.safeParse(await statusRes.json());
@@ -2977,7 +3004,7 @@ function passthroughVideoJob(ctx, req, endpoint, input) {
2977
3004
  };
2978
3005
  const duration = req.durationSeconds ?? 5;
2979
3006
  return {
2980
- jobId: endpoint,
3007
+ jobId,
2981
3008
  async poll() {
2982
3009
  try {
2983
3010
  const url = await checkStatus();
@@ -3838,6 +3865,7 @@ function toProviderError(cause, ctx) {
3838
3865
  });
3839
3866
  }
3840
3867
  const info = describeThrown(cause);
3868
+ if (info.status === void 0) return transportError("google-image", cause, ctx.model.id);
3841
3869
  return classifyHttpError(info.status, info.body, "google-image", ctx.model.id);
3842
3870
  }
3843
3871
  function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
@@ -4545,6 +4573,7 @@ var openaiImageAdapter = {
4545
4573
  const refs = req.references ?? [];
4546
4574
  const isEdit = refs.length > 0;
4547
4575
  const quality = req.quality ?? (isEdit ? "medium" : "auto");
4576
+ const sdkQuality = quality;
4548
4577
  const count = req.count !== void 0 && req.count > 1 ? { n: req.count } : {};
4549
4578
  const retryOptions = {};
4550
4579
  if (ctx.signal !== void 0) retryOptions.signal = ctx.signal;
@@ -4556,14 +4585,14 @@ var openaiImageAdapter = {
4556
4585
  model: ctx.model.providerModelId,
4557
4586
  prompt: req.prompt,
4558
4587
  size,
4559
- quality,
4588
+ quality: sdkQuality,
4560
4589
  ...count
4561
4590
  });
4562
4591
  }
4563
4592
  return await client2.images.generate({
4564
4593
  model: ctx.model.providerModelId,
4565
4594
  prompt: req.prompt,
4566
- quality,
4595
+ quality: sdkQuality,
4567
4596
  ...count
4568
4597
  });
4569
4598
  }
@@ -4582,7 +4611,7 @@ var openaiImageAdapter = {
4582
4611
  image: imageInput,
4583
4612
  prompt: req.prompt,
4584
4613
  size,
4585
- quality,
4614
+ quality: sdkQuality,
4586
4615
  ...count
4587
4616
  });
4588
4617
  }
@@ -4590,7 +4619,7 @@ var openaiImageAdapter = {
4590
4619
  model: ctx.model.providerModelId,
4591
4620
  image: imageInput,
4592
4621
  prompt: req.prompt,
4593
- quality,
4622
+ quality: sdkQuality,
4594
4623
  ...count
4595
4624
  });
4596
4625
  } catch (cause) {
@@ -4811,7 +4840,7 @@ async function generateProdiaImage(req, ctx) {
4811
4840
  });
4812
4841
  } catch (err) {
4813
4842
  if (isModelKitError(err)) throw err;
4814
- throw prodiaError(void 0, String(err), ctx.model.id);
4843
+ throw transportError("prodia", err, ctx.model.id);
4815
4844
  }
4816
4845
  if (!attemptRes.ok) {
4817
4846
  const text2 = await attemptRes.text().catch(() => "");
@@ -4821,7 +4850,7 @@ async function generateProdiaImage(req, ctx) {
4821
4850
  }, retryOpts);
4822
4851
  } catch (err) {
4823
4852
  if (isModelKitError(err)) throw err;
4824
- throw prodiaError(void 0, String(err), ctx.model.id);
4853
+ throw transportError("prodia", err, ctx.model.id);
4825
4854
  }
4826
4855
  const bytes = parseImageResponse(
4827
4856
  new Uint8Array(await res.arrayBuffer()),
@@ -5109,6 +5138,27 @@ function klingInput(req) {
5109
5138
  if (req.negativePrompt !== void 0) input.negative_prompt = req.negativePrompt;
5110
5139
  return input;
5111
5140
  }
5141
+ function klingOmniInput(req) {
5142
+ const images = (req.firstFrame ? [req.firstFrame] : []).map(refToUrl3);
5143
+ if (req.lastFrame) images.push(refToUrl3(req.lastFrame));
5144
+ const input = {
5145
+ prompt: req.prompt,
5146
+ duration: req.durationSeconds ?? 10,
5147
+ mode: req.mode ?? "standard",
5148
+ generate_audio: req.generateAudio ?? false
5149
+ };
5150
+ const startImage = images[0];
5151
+ if (startImage !== void 0) {
5152
+ input.start_image = startImage;
5153
+ const endImage = images[1];
5154
+ if (endImage !== void 0) input.end_image = endImage;
5155
+ } else {
5156
+ input.aspect_ratio = req.aspectRatio ?? "16:9";
5157
+ }
5158
+ const refs = (req.referenceImages ?? []).map(refToUrl3);
5159
+ if (refs.length > 0) input.reference_images = refs;
5160
+ return input;
5161
+ }
5112
5162
  function hailuoInput(req) {
5113
5163
  const input = {
5114
5164
  prompt: req.prompt,
@@ -5131,7 +5181,7 @@ function wanInput(req) {
5131
5181
  }
5132
5182
  async function generateReplicateVideo(req, ctx, deps) {
5133
5183
  const id = isLower(ctx.model.providerModelId);
5134
- const input = id.includes("kling") ? klingInput(req) : id.includes("hailuo") ? hailuoInput(req) : wanInput(req);
5184
+ const input = id.includes("kling") ? id.includes("omni") ? klingOmniInput(req) : klingInput(req) : id.includes("hailuo") ? hailuoInput(req) : wanInput(req);
5135
5185
  const api = client(ctx, deps);
5136
5186
  const createOptions = { ...route(ctx.model.providerModelId), input };
5137
5187
  let prediction;
@@ -5221,6 +5271,9 @@ function lyriaAudioInput(req, ctx) {
5221
5271
  if (negative) input.negative_prompt = negative;
5222
5272
  return input;
5223
5273
  }
5274
+ function elevenLabsMusicDuration(seconds) {
5275
+ return Math.min(300, Math.max(5, seconds ?? 15));
5276
+ }
5224
5277
  function elevenLabsMusicInput(req, ctx) {
5225
5278
  const prompt = req.prompt.trim();
5226
5279
  if (!prompt) {
@@ -5232,10 +5285,9 @@ function elevenLabsMusicInput(req, ctx) {
5232
5285
  retryable: false
5233
5286
  });
5234
5287
  }
5235
- const seconds = Math.min(300, Math.max(5, req.durationSeconds ?? 15));
5236
5288
  return {
5237
5289
  prompt,
5238
- music_length_ms: seconds * 1e3,
5290
+ music_length_ms: elevenLabsMusicDuration(req.durationSeconds) * 1e3,
5239
5291
  force_instrumental: req.instrumental ?? true,
5240
5292
  output_format: "wav_cd_quality"
5241
5293
  };
@@ -5275,7 +5327,13 @@ async function generateReplicateAudio(req, ctx, deps) {
5275
5327
  const input = id.includes("mmaudio") ? mmaudioInput(req, ctx) : id.includes("elevenlabs") ? elevenLabsMusicInput(req, ctx) : lyriaAudioInput(req, ctx);
5276
5328
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5277
5329
  const usage2 = audioUsage(ctx, req);
5278
- usage2.quantity = ctx.model.usage.unit === "seconds" ? req.durationSeconds ?? 0 : predictTime;
5330
+ if (ctx.model.usage.unit === "seconds") {
5331
+ const seconds = elevenLabsMusicDuration(req.durationSeconds);
5332
+ usage2.quantity = seconds;
5333
+ usage2.durationSeconds = seconds;
5334
+ } else {
5335
+ usage2.quantity = predictTime;
5336
+ }
5279
5337
  return { audio: { url }, usage: usage2, raw: predictionRaw(raw) };
5280
5338
  }
5281
5339