@marlinjai/studio-mcp 0.4.0 → 0.5.0
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/README.md +34 -9
- package/dist/cli.js +402 -37
- package/dist/index.d.ts +53 -8
- package/dist/index.js +402 -37
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -42,7 +42,11 @@ function apiMessageFromBody(body, fallback) {
|
|
|
42
42
|
if (typeof o.error === "string" && o.error.length > 0) {
|
|
43
43
|
const detail = typeof o.message === "string" ? o.message : "";
|
|
44
44
|
const adds = detail.length > 0 && !o.error.includes(detail) && !detail.includes(o.error);
|
|
45
|
-
|
|
45
|
+
let headline = adds ? `${o.error}: ${detail}` : o.error;
|
|
46
|
+
const hint = typeof o.hint === "string" ? o.hint.trim() : "";
|
|
47
|
+
if (hint.length > 0 && !headline.includes(hint)) headline = `${headline}${/[.!?]$/.test(headline) ? "" : "."} ${hint}`;
|
|
48
|
+
const code2 = typeof o.code === "string" ? o.code.trim() : "";
|
|
49
|
+
if (code2.length > 0 && !headline.includes(code2)) headline = `${headline} (code: ${code2})`;
|
|
46
50
|
const quota = quotaDetail(o);
|
|
47
51
|
return quota ? `${headline} (${quota})` : headline;
|
|
48
52
|
}
|
|
@@ -102,6 +106,14 @@ var StudioClient = class {
|
|
|
102
106
|
return this.json("GET", "/api/v1/projects");
|
|
103
107
|
}
|
|
104
108
|
// ── Generation jobs ──
|
|
109
|
+
/**
|
|
110
|
+
* One image job. `brandSlug` is optional: without it the image is generated
|
|
111
|
+
* with no brand. A `brandSlug` or `characterSlug` the key's company cannot
|
|
112
|
+
* use is refused with 404 `input_not_found` before anything is spent (since
|
|
113
|
+
* 2026-10-11; it used to generate without the brand). `projectId` chooses the
|
|
114
|
+
* project; without it the job lands in the project named after the brand, or
|
|
115
|
+
* in `default`.
|
|
116
|
+
*/
|
|
105
117
|
generate(body) {
|
|
106
118
|
return this.json("POST", "/api/generate", { json: body });
|
|
107
119
|
}
|
|
@@ -435,7 +447,124 @@ function studio3dCreditsToUsd(credits) {
|
|
|
435
447
|
return Math.round(credits * STUDIO3D_USD_PER_CREDIT * 1e3) / 1e3;
|
|
436
448
|
}
|
|
437
449
|
var FAL_MESHY_RIG_USD = 0.2;
|
|
450
|
+
var veo = (tier, label) => {
|
|
451
|
+
const slug2 = tier ? `veo-3-1-${tier}` : "veo-3-1";
|
|
452
|
+
return {
|
|
453
|
+
kind: "video",
|
|
454
|
+
id: `route/${slug2}`,
|
|
455
|
+
label: `${label} (KIE, fal fallback)`,
|
|
456
|
+
primary: `kie/${slug2}`,
|
|
457
|
+
fallback: { start: `fal/${slug2}-image-to-video`, startAndEnd: `fal/${slug2}-first-last-frame` }
|
|
458
|
+
};
|
|
459
|
+
};
|
|
460
|
+
var image = (slug2, label, defaultTier) => ({
|
|
461
|
+
kind: "image",
|
|
462
|
+
id: `route/${slug2}`,
|
|
463
|
+
label: `${label} (KIE, fal fallback)`,
|
|
464
|
+
primary: `kie/${slug2}`,
|
|
465
|
+
fallback: `fal/${slug2}`,
|
|
466
|
+
defaultTier
|
|
467
|
+
});
|
|
468
|
+
var ROUTES = [
|
|
469
|
+
veo("lite", "Veo 3.1 Lite"),
|
|
470
|
+
veo("fast", "Veo 3.1 Fast"),
|
|
471
|
+
veo("", "Veo 3.1"),
|
|
472
|
+
{
|
|
473
|
+
kind: "video",
|
|
474
|
+
id: "route/grok-imagine",
|
|
475
|
+
label: "Grok Imagine (KIE, fal fallback)",
|
|
476
|
+
primary: "kie/grok-imagine-image-to-video",
|
|
477
|
+
fallback: { start: "fal/grok-imagine-image-to-video" }
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
kind: "video",
|
|
481
|
+
id: "route/grok-imagine-1-5",
|
|
482
|
+
label: "Grok Imagine 1.5 (KIE, fal fallback)",
|
|
483
|
+
primary: "kie/grok-imagine-1-5-image-to-video",
|
|
484
|
+
fallback: { start: "fal/grok-imagine-1-5-image-to-video" }
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
kind: "video",
|
|
488
|
+
id: "route/kling-2-5-turbo-pro",
|
|
489
|
+
label: "Kling 2.5 Turbo Pro (KIE, fal fallback)",
|
|
490
|
+
primary: "kie/kling-v2-5-turbo-pro-image-to-video",
|
|
491
|
+
fallback: { start: "fal/kling-v2-5-image-to-video" },
|
|
492
|
+
// fal needs an aspect ratio (16:9, 9:16 or 1:1) and KIE takes none, so a
|
|
493
|
+
// fallback would crop a start frame of another shape. The ratio comes from
|
|
494
|
+
// the start frame once that is built (decision 7 of the plan).
|
|
495
|
+
noFallbackYet: "fal renders Kling 2.5 at a set aspect ratio (16:9, 9:16 or 1:1) and KIE takes none, so a fallback could crop the start frame. Pin the fal Kling 2.5 entry to choose the ratio yourself."
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
kind: "video",
|
|
499
|
+
id: "route/kling-2-6",
|
|
500
|
+
label: "Kling 2.6 (KIE, fal fallback)",
|
|
501
|
+
primary: "kie/kling-2-6-image-to-video",
|
|
502
|
+
fallback: { start: "fal/kling-2-6-image-to-video" }
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
kind: "video",
|
|
506
|
+
id: "route/kling-o3",
|
|
507
|
+
label: "Kling O3 (KIE, fal fallback)",
|
|
508
|
+
primary: "kie/kling-o3-image-to-video",
|
|
509
|
+
// fal sells Kling O3 as tiers and takes no resolution. Its schema (read
|
|
510
|
+
// 2026-10-10) declares length, audio and the frames but states no output
|
|
511
|
+
// size, so Standard for 720p and Pro for 1080p follows Kling's own tier
|
|
512
|
+
// naming and is still to be confirmed by measuring a rendered clip.
|
|
513
|
+
fallback: {
|
|
514
|
+
byResolution: {
|
|
515
|
+
"720p": "fal/kling-o3-standard-image-to-video",
|
|
516
|
+
"1080p": "fal/kling-o3-pro-image-to-video"
|
|
517
|
+
},
|
|
518
|
+
label: "fal renders Kling O3 at 720p (Standard) or 1080p (Pro)"
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
kind: "video",
|
|
523
|
+
id: "route/seedance-2-0",
|
|
524
|
+
label: "Seedance 2.0 (KIE, fal fallback)",
|
|
525
|
+
primary: "kie/seedance-2-0-image-to-video",
|
|
526
|
+
fallback: { start: "fal/seedance-2-0-image-to-video" }
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
kind: "video",
|
|
530
|
+
id: "route/seedance-2-5",
|
|
531
|
+
label: "Seedance 2.5 (KIE, fal fallback)",
|
|
532
|
+
primary: "kie/seedance-2-5-image-to-video",
|
|
533
|
+
fallback: { start: "fal/seedance-2-5-image-to-video" }
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
kind: "video",
|
|
537
|
+
id: "route/wan-3-0",
|
|
538
|
+
label: "Wan 3.0 (KIE, fal fallback)",
|
|
539
|
+
primary: "kie/wan-3-0-image-to-video",
|
|
540
|
+
fallback: { start: "fal/wan-3-0-image-to-video" }
|
|
541
|
+
},
|
|
542
|
+
image("nano-banana-2", "Nano Banana 2", "1K"),
|
|
543
|
+
image("nano-banana-2-edit", "Nano Banana 2 Edit", "1K"),
|
|
544
|
+
image("nano-banana-pro", "Nano Banana Pro", "1K"),
|
|
545
|
+
image("nano-banana-pro-edit", "Nano Banana Pro Edit", "1K"),
|
|
546
|
+
image("seedream-5-lite", "Seedream 5 Lite", "2K"),
|
|
547
|
+
image("seedream-5-lite-edit", "Seedream 5 Lite Edit", "2K"),
|
|
548
|
+
image("seedream-5-pro", "Seedream 5 Pro", "1K"),
|
|
549
|
+
image("seedream-5-pro-edit", "Seedream 5 Pro Edit", "1K")
|
|
550
|
+
];
|
|
551
|
+
var MODEL_ROUTES = Object.fromEntries(ROUTES.map((r) => [r.id, r]));
|
|
552
|
+
function getRoute(modelId) {
|
|
553
|
+
return modelId ? MODEL_ROUTES[modelId] : void 0;
|
|
554
|
+
}
|
|
438
555
|
var MODEL_CATALOG = [
|
|
556
|
+
// LIST PRICES (`listPrice`, pricing plan of 2026-10-10). Every KIE entry
|
|
557
|
+
// names where the official list price of its requests is found, because KIE
|
|
558
|
+
// is the reseller route that sells below list and the price floor needs the
|
|
559
|
+
// figure. The rule: the model owner's own published price where the catalog
|
|
560
|
+
// records one (Google's Veo price, billed unchanged by the OpenRouter
|
|
561
|
+
// entries; Vertex AI's Imagen 4 Fast price), else the fal entry for the same
|
|
562
|
+
// upstream model. `unrecorded` means neither exists in this catalog, and the
|
|
563
|
+
// floor then does not apply: Seedream V4, MiniMax H3 and Gemini Omni Flash
|
|
564
|
+
// have no fal entry, Kling O3 on KIE does not say whether it is fal's
|
|
565
|
+
// Standard or Pro tier, Suno has no per-song list price, and the Claude
|
|
566
|
+
// entries bill per token. Entries of every other provider carry no
|
|
567
|
+
// reference: their route bills the list price itself.
|
|
439
568
|
// ── 3D AI Studio: 3D generation ────────────────────────────────────────
|
|
440
569
|
// `credits` is what the model's DEFAULT request bills (the options its
|
|
441
570
|
// builder sends when a node sets none), priced by providers/studio3d/
|
|
@@ -532,6 +661,7 @@ var MODEL_CATALOG = [
|
|
|
532
661
|
label: "Nano Banana 2 (KIE)",
|
|
533
662
|
description: "Google Gemini 3.1 Flash Image via KIE. $0.04 at 1K, $0.06 at 2K, $0.09 at 4K. Kept as fallback aggregator path; default flipped to fal/nano-banana-2 in 2026-06-04 fal slice.",
|
|
534
663
|
cost: { usd: 0.04, usdByResolution: { "1K": 0.04, "2K": 0.06, "4K": 0.09 } },
|
|
664
|
+
listPrice: { source: "fal", model: "fal/nano-banana-2" },
|
|
535
665
|
capabilities: { resolutions: ["1K", "2K", "4K"] }
|
|
536
666
|
},
|
|
537
667
|
{
|
|
@@ -544,6 +674,7 @@ var MODEL_CATALOG = [
|
|
|
544
674
|
label: "Imagen 4 Fast (KIE)",
|
|
545
675
|
description: "Google Imagen 4 Fast via KIE. No price advantage vs Vertex direct. No IP indemnity. Vertex AI lists Imagen 4 Fast at $0.02 per image (2026-09-27), which matches the estimate; the KIE price itself is still unmeasured.",
|
|
546
676
|
cost: { usd: 0.02 },
|
|
677
|
+
listPrice: { source: "owner", usd: 0.02 },
|
|
547
678
|
capabilities: { resolutions: [] }
|
|
548
679
|
},
|
|
549
680
|
{
|
|
@@ -553,11 +684,15 @@ var MODEL_CATALOG = [
|
|
|
553
684
|
task: "text-to-image",
|
|
554
685
|
label: "Seedream V4 (KIE)",
|
|
555
686
|
description: "ByteDance Seedream V4 via KIE. Stylized output not available on Vertex. No IP indemnity.",
|
|
556
|
-
cost: { usd: 0.0175 }
|
|
687
|
+
cost: { usd: 0.0175 },
|
|
688
|
+
listPrice: { source: "unrecorded" }
|
|
557
689
|
},
|
|
558
690
|
// ── KIE.ai: newer image models, the cheaper route (price feed 2026-09-23) ─
|
|
559
691
|
// Billed from the credits KIE reports on each task. KIE offers no data
|
|
560
|
-
// processing agreement
|
|
692
|
+
// processing agreement. Decided 2026-09-23 (Q5 C of the routing plan,
|
|
693
|
+
// docs/plans/2026-09-26-kie-first-fal-fallback.md): the automatic routes try
|
|
694
|
+
// KIE first for everything, a recorded risk; pinning the fal entry of a model
|
|
695
|
+
// is how a job with a real person's photo is kept off KIE.
|
|
561
696
|
{
|
|
562
697
|
id: "kie/nano-banana-2-edit",
|
|
563
698
|
provider: "kie",
|
|
@@ -566,6 +701,7 @@ var MODEL_CATALOG = [
|
|
|
566
701
|
label: "Nano Banana 2 Edit (KIE)",
|
|
567
702
|
description: "Nano Banana 2 instruction edit via KIE with up to 8 reference images, half the fal price: $0.04 at 1K, $0.06 at 2K, $0.09 at 4K.",
|
|
568
703
|
cost: { usd: 0.04, usdByResolution: { "1K": 0.04, "2K": 0.06, "4K": 0.09 } },
|
|
704
|
+
listPrice: { source: "fal", model: "fal/nano-banana-2-edit" },
|
|
569
705
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
570
706
|
},
|
|
571
707
|
{
|
|
@@ -576,6 +712,7 @@ var MODEL_CATALOG = [
|
|
|
576
712
|
label: "Nano Banana Pro (KIE)",
|
|
577
713
|
description: "Google Gemini 3 Pro Image via KIE: stronger on complex scenes, text in images and new characters than Nano Banana 2 (a relighting edit showed no difference on 2026-09-23). $0.09 at 1K or 2K, $0.12 at 4K (fal: $0.15, $0.30).",
|
|
578
714
|
cost: { usd: 0.09, usdByResolution: { "1K": 0.09, "2K": 0.09, "4K": 0.12 } },
|
|
715
|
+
listPrice: { source: "fal", model: "fal/nano-banana-pro" },
|
|
579
716
|
capabilities: { resolutions: ["1K", "2K", "4K"], maxInputImages: 0 }
|
|
580
717
|
},
|
|
581
718
|
{
|
|
@@ -586,6 +723,7 @@ var MODEL_CATALOG = [
|
|
|
586
723
|
label: "Nano Banana Pro Edit (KIE)",
|
|
587
724
|
description: "Nano Banana Pro instruction edit via KIE with up to 8 reference images. $0.09 at 1K or 2K, $0.12 at 4K (fal: $0.15, $0.30).",
|
|
588
725
|
cost: { usd: 0.09, usdByResolution: { "1K": 0.09, "2K": 0.09, "4K": 0.12 } },
|
|
726
|
+
listPrice: { source: "fal", model: "fal/nano-banana-pro-edit" },
|
|
589
727
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
590
728
|
},
|
|
591
729
|
{
|
|
@@ -596,6 +734,7 @@ var MODEL_CATALOG = [
|
|
|
596
734
|
label: "Seedream 5 Lite (KIE)",
|
|
597
735
|
description: "ByteDance Seedream 5 Lite via KIE, 2K or 4K. $0.0275 per image (fal: $0.035).",
|
|
598
736
|
cost: { usd: 0.0275 },
|
|
737
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite" },
|
|
599
738
|
capabilities: { resolutions: ["2K", "4K"], maxInputImages: 0 }
|
|
600
739
|
},
|
|
601
740
|
{
|
|
@@ -606,6 +745,7 @@ var MODEL_CATALOG = [
|
|
|
606
745
|
label: "Seedream 5 Lite Edit (KIE)",
|
|
607
746
|
description: "ByteDance Seedream 5 Lite image-to-image via KIE, up to 10 reference images, 2K or 4K. $0.0275 per image (fal: $0.035).",
|
|
608
747
|
cost: { usd: 0.0275 },
|
|
748
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite-edit" },
|
|
609
749
|
capabilities: { resolutions: ["2K", "4K"], needsImageInput: true, maxInputImages: 10 }
|
|
610
750
|
},
|
|
611
751
|
{
|
|
@@ -616,6 +756,7 @@ var MODEL_CATALOG = [
|
|
|
616
756
|
label: "Seedream 5 Pro (KIE)",
|
|
617
757
|
description: "ByteDance Seedream 5 Pro via KIE, 1K or 2K. $0.035 at 1K, $0.07 at 2K (fal: $0.0675 to $0.135).",
|
|
618
758
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
759
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro" },
|
|
619
760
|
capabilities: { resolutions: ["1K", "2K"], maxInputImages: 0 }
|
|
620
761
|
},
|
|
621
762
|
{
|
|
@@ -626,6 +767,7 @@ var MODEL_CATALOG = [
|
|
|
626
767
|
label: "Seedream 5 Pro Edit (KIE)",
|
|
627
768
|
description: "ByteDance Seedream 5 Pro image-to-image via KIE, up to 10 reference images, 1K or 2K. $0.035 at 1K, $0.07 at 2K, plus $0.0025 per extra reference.",
|
|
628
769
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
770
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro-edit" },
|
|
629
771
|
capabilities: { resolutions: ["1K", "2K"], needsImageInput: true, maxInputImages: 10 }
|
|
630
772
|
},
|
|
631
773
|
// ── fal.ai: image gen ─────────────────────────────────────────────────
|
|
@@ -920,8 +1062,11 @@ var MODEL_CATALOG = [
|
|
|
920
1062
|
// ── KIE.ai video: the cheaper route to the same upstream models ──────────
|
|
921
1063
|
// Async via KIE's Veo and createTask APIs (providers/kie-video.ts). 20 to 85%
|
|
922
1064
|
// below fal (KIE price feed, 2026-09-23). KIE offers no data processing
|
|
923
|
-
// agreement
|
|
924
|
-
//
|
|
1065
|
+
// agreement. Decided 2026-09-23 (Q5 C of the routing plan): the automatic
|
|
1066
|
+
// routes try KIE first for everything, a recorded risk; pinning the fal
|
|
1067
|
+
// entry of a model is how a job with a real person's photo is kept off KIE.
|
|
1068
|
+
// Anchors are the model's default clip at 720p (Kling: its fixed size). KIE
|
|
1069
|
+
// docs vetted 2026-09-23.
|
|
925
1070
|
{
|
|
926
1071
|
id: "kie/veo-3-1-lite",
|
|
927
1072
|
provider: "kie",
|
|
@@ -930,6 +1075,7 @@ var MODEL_CATALOG = [
|
|
|
930
1075
|
label: "Veo 3.1 Lite (KIE)",
|
|
931
1076
|
description: "Google Veo 3.1 Lite via KIE. A start frame, or a start and an end frame; 4, 6 or 8 s; 720p, 1080p or 4K; always with audio. Accepted a minted character's photoreal stills on 2026-09-23. Priced per clip: $0.15 at 720p, $0.175 at 1080p, $0.75 at 4K (fal: $0.24 silent, $0.40 with audio, for 8 s at 720p). KIE's Auto aspect letterboxes, so pick 16:9 or 9:16.",
|
|
932
1077
|
cost: { usd: 0.15 },
|
|
1078
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-lite" },
|
|
933
1079
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
934
1080
|
},
|
|
935
1081
|
{
|
|
@@ -940,6 +1086,7 @@ var MODEL_CATALOG = [
|
|
|
940
1086
|
label: "Veo 3.1 Fast (KIE)",
|
|
941
1087
|
description: "Google Veo 3.1 Fast via KIE. A start frame, or a start and an end frame; 4, 6 or 8 s; 720p, 1080p or 4K; always with audio. Accepted a minted character's photoreal stills on 2026-09-23. Priced per clip: $0.30 at 720p, $0.325 at 1080p, $0.90 at 4K (fal: $1.20 with audio for 8 s). Pick 16:9 or 9:16.",
|
|
942
1088
|
cost: { usd: 0.3 },
|
|
1089
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-fast" },
|
|
943
1090
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
944
1091
|
},
|
|
945
1092
|
{
|
|
@@ -950,6 +1097,7 @@ var MODEL_CATALOG = [
|
|
|
950
1097
|
label: "Veo 3.1 (KIE)",
|
|
951
1098
|
description: "Google Veo 3.1 (quality tier) via KIE. A start frame, or a start and an end frame; 4, 6 or 8 s; 720p, 1080p or 4K; always with audio. Same upstream model as the tested Lite and Fast tiers. Priced per clip: $1.25 at 720p, $1.275 at 1080p, $1.85 at 4K (fal: $3.20 with audio).",
|
|
952
1099
|
cost: { usd: 1.25 },
|
|
1100
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1" },
|
|
953
1101
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
954
1102
|
},
|
|
955
1103
|
// ── OpenRouter video: Google Veo 3.1 at Google's own prices ───────────────
|
|
@@ -996,6 +1144,7 @@ var MODEL_CATALOG = [
|
|
|
996
1144
|
label: "Grok Imagine (KIE)",
|
|
997
1145
|
description: "xAI Grok Imagine via KIE. A start frame, 6 to 30 s, 480p, 720p or 1080p, with audio. Accepted a minted character's photoreal stills on 2026-09-23. $0.012 per second at 480p, $0.0225 at 720p, $0.04 at 1080p (fal: $0.07 at 720p). No end frame.",
|
|
998
1146
|
cost: { usd: 0.135 },
|
|
1147
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-image-to-video" },
|
|
999
1148
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
1000
1149
|
},
|
|
1001
1150
|
{
|
|
@@ -1006,6 +1155,7 @@ var MODEL_CATALOG = [
|
|
|
1006
1155
|
label: "Grok Imagine 1.5 (KIE, preview)",
|
|
1007
1156
|
description: "xAI Grok Imagine 1.5 preview via KIE. A start frame, 1 to 15 s, 480p or 720p. Not yet tested with a minted character. $0.012 per second at 480p, $0.0225 at 720p (fal: $0.14 at 720p). No end frame.",
|
|
1008
1157
|
cost: { usd: 0.18 },
|
|
1158
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-1-5-image-to-video" },
|
|
1009
1159
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
1010
1160
|
},
|
|
1011
1161
|
{
|
|
@@ -1016,6 +1166,7 @@ var MODEL_CATALOG = [
|
|
|
1016
1166
|
label: "Kling 2.5 Turbo Pro (KIE)",
|
|
1017
1167
|
description: "Kuaishou Kling 2.5 Turbo Pro via KIE. A start frame and an optional end frame, 5 or 10 s, fixed size. The fal route accepted minted characters; this KIE route is not yet tested with a minted character. $0.21 for 5 s, $0.42 for 10 s (fal: $0.35 for 5 s).",
|
|
1018
1168
|
cost: { usd: 0.21 },
|
|
1169
|
+
listPrice: { source: "fal", model: "fal/kling-v2-5-image-to-video" },
|
|
1019
1170
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1020
1171
|
},
|
|
1021
1172
|
{
|
|
@@ -1026,6 +1177,7 @@ var MODEL_CATALOG = [
|
|
|
1026
1177
|
label: "Kling 2.6 (KIE)",
|
|
1027
1178
|
description: "Kuaishou Kling 2.6 via KIE. A start frame, 5 or 10 s, fixed size, silent unless the job sets generateAudio. Not yet tested with a minted character. $0.275 for 5 s silent, $0.55 with sound, 10 s double (fal: $0.35 or $0.70).",
|
|
1028
1179
|
cost: { usd: 0.275 },
|
|
1180
|
+
listPrice: { source: "fal", model: "fal/kling-2-6-image-to-video" },
|
|
1029
1181
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
1030
1182
|
},
|
|
1031
1183
|
{
|
|
@@ -1037,6 +1189,7 @@ var MODEL_CATALOG = [
|
|
|
1037
1189
|
refusesPhotorealPeople: true,
|
|
1038
1190
|
description: "ByteDance Seedance 2.0 via KIE. A start frame and an optional end frame, 4 to 15 s, 480p to 4K. Seedance 2.0 refuses photoreal people, generated ones included: on fal it refused them in every mode (observed 2026-09-23). The refusal comes from the moderation of the model itself, so this route carries the same caution; the KIE route itself has not been run with a person. $0.095 per second at 480p, $0.205 at 720p, $0.51 at 1080p, $1.04 at 4K (fal: about $0.30 at 720p).",
|
|
1039
1191
|
cost: { usd: 1.025 },
|
|
1192
|
+
listPrice: { source: "fal", model: "fal/seedance-2-0-image-to-video" },
|
|
1040
1193
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1041
1194
|
},
|
|
1042
1195
|
{
|
|
@@ -1048,6 +1201,7 @@ var MODEL_CATALOG = [
|
|
|
1048
1201
|
refusesPhotorealPeople: true,
|
|
1049
1202
|
description: "ByteDance Seedance 2.5 via KIE. A start frame and an optional end frame, 4 to 30 s, 480p to 1080p. Seedance 2.5 refuses photoreal people, generated ones included: on fal it refused them in every mode (observed 2026-08-21 and 2026-09-23). The refusal comes from the moderation of the model itself, so this route carries the same caution; the KIE route itself has not been run with a person. $0.14 per second at 480p, $0.315 at 720p, $0.79 at 1080p.",
|
|
1050
1203
|
cost: { usd: 1.575 },
|
|
1204
|
+
listPrice: { source: "fal", model: "fal/seedance-2-5-image-to-video" },
|
|
1051
1205
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1052
1206
|
},
|
|
1053
1207
|
{
|
|
@@ -1058,6 +1212,7 @@ var MODEL_CATALOG = [
|
|
|
1058
1212
|
label: "Wan 3.0 (KIE)",
|
|
1059
1213
|
description: "Alibaba Wan 3.0 via KIE (hosted only, no downloadable weights). A start frame and an optional end frame, 2 to 30 s, 480p, 720p or 1080p, audio included. Accepted a minted character's photoreal stills on 2026-09-24. $0.04 per second at 480p, $0.08 at 720p, $0.16 at 1080p (fal and Alibaba: $0.10 at 720p).",
|
|
1060
1214
|
cost: { usd: 0.4 },
|
|
1215
|
+
listPrice: { source: "fal", model: "fal/wan-3-0-image-to-video" },
|
|
1061
1216
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1062
1217
|
},
|
|
1063
1218
|
{
|
|
@@ -1068,6 +1223,7 @@ var MODEL_CATALOG = [
|
|
|
1068
1223
|
label: "MiniMax H3 (KIE)",
|
|
1069
1224
|
description: "MiniMax H3 via KIE. A start frame and an optional end frame, 4 to 15 s, 768p (the 720p setting) or 2K (the 1080p setting). Accepted a minted character's photoreal stills on 2026-09-24. Its open weights exclude the EU by license; this is KIE's hosted service. $0.04 per second at 768p, $0.065 at 2K.",
|
|
1070
1225
|
cost: { usd: 0.24 },
|
|
1226
|
+
listPrice: { source: "unrecorded" },
|
|
1071
1227
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1072
1228
|
},
|
|
1073
1229
|
{
|
|
@@ -1078,6 +1234,7 @@ var MODEL_CATALOG = [
|
|
|
1078
1234
|
label: "Gemini Omni 1.1 Flash (KIE)",
|
|
1079
1235
|
description: "Google Gemini Omni 1.1 Flash via KIE, first-and-last-frame mode. A start frame and an optional end frame, 4, 6, 8 or 10 s, 720p to 4K, 16:9 or 9:16. KIE refuses registered characters in this mode, so identity comes from the frames alone. Accepted a minted character's photoreal stills on 2026-09-24 (set 9:16 for a portrait still: it defaults to 16:9 and crops). $0.315 for 4 s up to $0.63 for 10 s at 720p or 1080p, $0.735 to $1.05 at 4K. No fal equivalent.",
|
|
1080
1236
|
cost: { usd: 0.525 },
|
|
1237
|
+
listPrice: { source: "unrecorded" },
|
|
1081
1238
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1082
1239
|
},
|
|
1083
1240
|
{
|
|
@@ -1088,6 +1245,7 @@ var MODEL_CATALOG = [
|
|
|
1088
1245
|
label: "Kling O3 (KIE)",
|
|
1089
1246
|
description: "Kuaishou Kling O3 (KIE calls it Kling 3.0 Omni) via KIE. A start frame and an optional end frame, 3 to 15 s, 720p to 4K in the start frame's shape, silent unless the job sets generateAudio. Accepted a minted character's photoreal stills on 2026-09-24. $0.07 per second at 720p, $0.09 at 1080p, $0.335 at 4K; with sound $0.10 and $0.135.",
|
|
1090
1247
|
cost: { usd: 0.35 },
|
|
1248
|
+
listPrice: { source: "unrecorded" },
|
|
1091
1249
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1092
1250
|
},
|
|
1093
1251
|
{
|
|
@@ -1406,6 +1564,7 @@ var MODEL_CATALOG = [
|
|
|
1406
1564
|
label: "Suno V5 (KIE)",
|
|
1407
1565
|
description: "Suno V5 music generation via KIE. Text-to-music with or without lyrics; ~4 min cap per generation. Default text-to-audio. No IP indemnity (KIE aggregator). KIE's site now features Suno v5.5 and no longer lists a V5 price (checked 2026-09-27), so the $0.04 is an unverified hint.",
|
|
1408
1566
|
cost: { usd: 0.04 },
|
|
1567
|
+
listPrice: { source: "unrecorded" },
|
|
1409
1568
|
isDefault: true
|
|
1410
1569
|
},
|
|
1411
1570
|
// ── audio: voice via ElevenLabs (B2) ───────────────────────────────────
|
|
@@ -1454,6 +1613,7 @@ var MODEL_CATALOG = [
|
|
|
1454
1613
|
label: "Claude Sonnet 4.5 (KIE)",
|
|
1455
1614
|
description: "Anthropic Claude Sonnet 4.5 via KIE (Anthropic Messages API at a lower rate). Default LLM director: prompt improvement + shot descriptions. Cost billed per token; catalog usd is a hint.",
|
|
1456
1615
|
cost: { usd: 0.01 },
|
|
1616
|
+
listPrice: { source: "unrecorded" },
|
|
1457
1617
|
isDefault: true
|
|
1458
1618
|
},
|
|
1459
1619
|
{
|
|
@@ -1463,7 +1623,8 @@ var MODEL_CATALOG = [
|
|
|
1463
1623
|
task: "text-generation",
|
|
1464
1624
|
label: "Claude Opus 4.5 (KIE)",
|
|
1465
1625
|
description: "Anthropic Claude Opus 4.5 via KIE. Higher quality, higher cost; use for harder director reasoning. Cost billed per token; catalog usd is a hint.",
|
|
1466
|
-
cost: { usd: 0.05 }
|
|
1626
|
+
cost: { usd: 0.05 },
|
|
1627
|
+
listPrice: { source: "unrecorded" }
|
|
1467
1628
|
},
|
|
1468
1629
|
{
|
|
1469
1630
|
id: "openrouter/claude-sonnet-4-6",
|
|
@@ -1528,8 +1689,26 @@ var MODEL_CATALOG = [
|
|
|
1528
1689
|
description: "Vertex AI direct, newest flash variant. Global region. Preview status."
|
|
1529
1690
|
}
|
|
1530
1691
|
];
|
|
1692
|
+
var routeEntries = /* @__PURE__ */ new Map();
|
|
1693
|
+
function routeEntry(id) {
|
|
1694
|
+
const cached = routeEntries.get(id);
|
|
1695
|
+
if (cached) return cached;
|
|
1696
|
+
const route = getRoute(id);
|
|
1697
|
+
const primary = route ? MODEL_CATALOG.find((m) => m.id === route.primary) : void 0;
|
|
1698
|
+
if (!route || !primary) return void 0;
|
|
1699
|
+
const entry = {
|
|
1700
|
+
...primary,
|
|
1701
|
+
// A route is never the default of its task (that is slice 3's switch).
|
|
1702
|
+
isDefault: void 0,
|
|
1703
|
+
id: route.id,
|
|
1704
|
+
label: route.label,
|
|
1705
|
+
description: `Runs on KIE first and, when KIE fails on its side, again on fal with the same settings. ${primary.description ?? ""}`.trim()
|
|
1706
|
+
};
|
|
1707
|
+
routeEntries.set(id, entry);
|
|
1708
|
+
return entry;
|
|
1709
|
+
}
|
|
1531
1710
|
function tryGetModel(id) {
|
|
1532
|
-
return MODEL_CATALOG.find((m) => m.id === id);
|
|
1711
|
+
return MODEL_CATALOG.find((m) => m.id === id) ?? routeEntry(id);
|
|
1533
1712
|
}
|
|
1534
1713
|
var ROWS = [
|
|
1535
1714
|
[0, "Idle", "DailyActions", "Idle"],
|
|
@@ -2457,7 +2636,10 @@ var VIDEO_UNSUPPORTED_PARAMS = {
|
|
|
2457
2636
|
...Object.fromEntries(KLING_O3_I2V_ENDPOINTS.map((e) => [e, ["resolution", "aspectRatio", "seed", "negativePrompt"]])),
|
|
2458
2637
|
...Object.fromEntries(LTX_25_I2V_ENDPOINTS.map((e) => [e, ["seed", "negativePrompt"]]))
|
|
2459
2638
|
};
|
|
2460
|
-
var perSecond = (rates) => (s, r) =>
|
|
2639
|
+
var perSecond = (rates) => (s, r) => {
|
|
2640
|
+
const rate = rates[r];
|
|
2641
|
+
return rate === void 0 ? void 0 : rate * s;
|
|
2642
|
+
};
|
|
2461
2643
|
function veoSpec(model, label, perClip) {
|
|
2462
2644
|
return {
|
|
2463
2645
|
family: "veo",
|
|
@@ -2471,7 +2653,7 @@ function veoSpec(model, label, perClip) {
|
|
|
2471
2653
|
audioSwitch: false,
|
|
2472
2654
|
defaultAudio: true,
|
|
2473
2655
|
// KIE prices Veo per clip whatever its length, and always renders audio.
|
|
2474
|
-
price: (_s, r) => perClip[r]
|
|
2656
|
+
price: (_s, r) => perClip[r],
|
|
2475
2657
|
build: (i) => ({
|
|
2476
2658
|
prompt: i.prompt,
|
|
2477
2659
|
model,
|
|
@@ -2678,7 +2860,7 @@ var KIE_VIDEO_SPECS = {
|
|
|
2678
2860
|
audioSwitch: false,
|
|
2679
2861
|
defaultAudio: false,
|
|
2680
2862
|
// Priced per clip by length; 4K carries a surcharge.
|
|
2681
|
-
price: (s, r) => (r === "4k" ? { 4: 0.735, 6: 0.84, 8: 0.945, 10: 1.05 } : { 4: 0.315, 6: 0.42, 8: 0.525, 10: 0.63 })[s]
|
|
2863
|
+
price: (s, r) => (r === "4k" ? { 4: 0.735, 6: 0.84, 8: 0.945, 10: 1.05 } : { 4: 0.315, 6: 0.42, 8: 0.525, 10: 0.63 })[s],
|
|
2682
2864
|
// The first-and-last-frame mode: KIE refuses it combined with registered
|
|
2683
2865
|
// characters, reference images, audio or video, so none are sent.
|
|
2684
2866
|
build: (i) => ({
|
|
@@ -2704,7 +2886,7 @@ var KIE_VIDEO_SPECS = {
|
|
|
2704
2886
|
audioSwitch: true,
|
|
2705
2887
|
defaultAudio: false,
|
|
2706
2888
|
// With-audio rates from KIE's price feed, the higher of its two listings.
|
|
2707
|
-
price: (s, r, audio) => (
|
|
2889
|
+
price: (s, r, audio) => perSecond(audio ? { "720p": 0.1, "1080p": 0.135, "4k": 0.335 } : { "720p": 0.07, "1080p": 0.09, "4k": 0.335 })(s, r),
|
|
2708
2890
|
build: (i) => ({
|
|
2709
2891
|
prompt: i.prompt,
|
|
2710
2892
|
image_urls: i.end ? [i.start, i.end] : [i.start],
|
|
@@ -3146,7 +3328,124 @@ function imageResolutionTier(value) {
|
|
|
3146
3328
|
return null;
|
|
3147
3329
|
}
|
|
3148
3330
|
}
|
|
3331
|
+
var veo2 = (tier, label) => {
|
|
3332
|
+
const slug2 = tier ? `veo-3-1-${tier}` : "veo-3-1";
|
|
3333
|
+
return {
|
|
3334
|
+
kind: "video",
|
|
3335
|
+
id: `route/${slug2}`,
|
|
3336
|
+
label: `${label} (KIE, fal fallback)`,
|
|
3337
|
+
primary: `kie/${slug2}`,
|
|
3338
|
+
fallback: { start: `fal/${slug2}-image-to-video`, startAndEnd: `fal/${slug2}-first-last-frame` }
|
|
3339
|
+
};
|
|
3340
|
+
};
|
|
3341
|
+
var image2 = (slug2, label, defaultTier) => ({
|
|
3342
|
+
kind: "image",
|
|
3343
|
+
id: `route/${slug2}`,
|
|
3344
|
+
label: `${label} (KIE, fal fallback)`,
|
|
3345
|
+
primary: `kie/${slug2}`,
|
|
3346
|
+
fallback: `fal/${slug2}`,
|
|
3347
|
+
defaultTier
|
|
3348
|
+
});
|
|
3349
|
+
var ROUTES2 = [
|
|
3350
|
+
veo2("lite", "Veo 3.1 Lite"),
|
|
3351
|
+
veo2("fast", "Veo 3.1 Fast"),
|
|
3352
|
+
veo2("", "Veo 3.1"),
|
|
3353
|
+
{
|
|
3354
|
+
kind: "video",
|
|
3355
|
+
id: "route/grok-imagine",
|
|
3356
|
+
label: "Grok Imagine (KIE, fal fallback)",
|
|
3357
|
+
primary: "kie/grok-imagine-image-to-video",
|
|
3358
|
+
fallback: { start: "fal/grok-imagine-image-to-video" }
|
|
3359
|
+
},
|
|
3360
|
+
{
|
|
3361
|
+
kind: "video",
|
|
3362
|
+
id: "route/grok-imagine-1-5",
|
|
3363
|
+
label: "Grok Imagine 1.5 (KIE, fal fallback)",
|
|
3364
|
+
primary: "kie/grok-imagine-1-5-image-to-video",
|
|
3365
|
+
fallback: { start: "fal/grok-imagine-1-5-image-to-video" }
|
|
3366
|
+
},
|
|
3367
|
+
{
|
|
3368
|
+
kind: "video",
|
|
3369
|
+
id: "route/kling-2-5-turbo-pro",
|
|
3370
|
+
label: "Kling 2.5 Turbo Pro (KIE, fal fallback)",
|
|
3371
|
+
primary: "kie/kling-v2-5-turbo-pro-image-to-video",
|
|
3372
|
+
fallback: { start: "fal/kling-v2-5-image-to-video" },
|
|
3373
|
+
// fal needs an aspect ratio (16:9, 9:16 or 1:1) and KIE takes none, so a
|
|
3374
|
+
// fallback would crop a start frame of another shape. The ratio comes from
|
|
3375
|
+
// the start frame once that is built (decision 7 of the plan).
|
|
3376
|
+
noFallbackYet: "fal renders Kling 2.5 at a set aspect ratio (16:9, 9:16 or 1:1) and KIE takes none, so a fallback could crop the start frame. Pin the fal Kling 2.5 entry to choose the ratio yourself."
|
|
3377
|
+
},
|
|
3378
|
+
{
|
|
3379
|
+
kind: "video",
|
|
3380
|
+
id: "route/kling-2-6",
|
|
3381
|
+
label: "Kling 2.6 (KIE, fal fallback)",
|
|
3382
|
+
primary: "kie/kling-2-6-image-to-video",
|
|
3383
|
+
fallback: { start: "fal/kling-2-6-image-to-video" }
|
|
3384
|
+
},
|
|
3385
|
+
{
|
|
3386
|
+
kind: "video",
|
|
3387
|
+
id: "route/kling-o3",
|
|
3388
|
+
label: "Kling O3 (KIE, fal fallback)",
|
|
3389
|
+
primary: "kie/kling-o3-image-to-video",
|
|
3390
|
+
// fal sells Kling O3 as tiers and takes no resolution. Its schema (read
|
|
3391
|
+
// 2026-10-10) declares length, audio and the frames but states no output
|
|
3392
|
+
// size, so Standard for 720p and Pro for 1080p follows Kling's own tier
|
|
3393
|
+
// naming and is still to be confirmed by measuring a rendered clip.
|
|
3394
|
+
fallback: {
|
|
3395
|
+
byResolution: {
|
|
3396
|
+
"720p": "fal/kling-o3-standard-image-to-video",
|
|
3397
|
+
"1080p": "fal/kling-o3-pro-image-to-video"
|
|
3398
|
+
},
|
|
3399
|
+
label: "fal renders Kling O3 at 720p (Standard) or 1080p (Pro)"
|
|
3400
|
+
}
|
|
3401
|
+
},
|
|
3402
|
+
{
|
|
3403
|
+
kind: "video",
|
|
3404
|
+
id: "route/seedance-2-0",
|
|
3405
|
+
label: "Seedance 2.0 (KIE, fal fallback)",
|
|
3406
|
+
primary: "kie/seedance-2-0-image-to-video",
|
|
3407
|
+
fallback: { start: "fal/seedance-2-0-image-to-video" }
|
|
3408
|
+
},
|
|
3409
|
+
{
|
|
3410
|
+
kind: "video",
|
|
3411
|
+
id: "route/seedance-2-5",
|
|
3412
|
+
label: "Seedance 2.5 (KIE, fal fallback)",
|
|
3413
|
+
primary: "kie/seedance-2-5-image-to-video",
|
|
3414
|
+
fallback: { start: "fal/seedance-2-5-image-to-video" }
|
|
3415
|
+
},
|
|
3416
|
+
{
|
|
3417
|
+
kind: "video",
|
|
3418
|
+
id: "route/wan-3-0",
|
|
3419
|
+
label: "Wan 3.0 (KIE, fal fallback)",
|
|
3420
|
+
primary: "kie/wan-3-0-image-to-video",
|
|
3421
|
+
fallback: { start: "fal/wan-3-0-image-to-video" }
|
|
3422
|
+
},
|
|
3423
|
+
image2("nano-banana-2", "Nano Banana 2", "1K"),
|
|
3424
|
+
image2("nano-banana-2-edit", "Nano Banana 2 Edit", "1K"),
|
|
3425
|
+
image2("nano-banana-pro", "Nano Banana Pro", "1K"),
|
|
3426
|
+
image2("nano-banana-pro-edit", "Nano Banana Pro Edit", "1K"),
|
|
3427
|
+
image2("seedream-5-lite", "Seedream 5 Lite", "2K"),
|
|
3428
|
+
image2("seedream-5-lite-edit", "Seedream 5 Lite Edit", "2K"),
|
|
3429
|
+
image2("seedream-5-pro", "Seedream 5 Pro", "1K"),
|
|
3430
|
+
image2("seedream-5-pro-edit", "Seedream 5 Pro Edit", "1K")
|
|
3431
|
+
];
|
|
3432
|
+
var MODEL_ROUTES2 = Object.fromEntries(ROUTES2.map((r) => [r.id, r]));
|
|
3433
|
+
function getRoute2(modelId) {
|
|
3434
|
+
return modelId ? MODEL_ROUTES2[modelId] : void 0;
|
|
3435
|
+
}
|
|
3149
3436
|
var MODEL_CATALOG2 = [
|
|
3437
|
+
// LIST PRICES (`listPrice`, pricing plan of 2026-10-10). Every KIE entry
|
|
3438
|
+
// names where the official list price of its requests is found, because KIE
|
|
3439
|
+
// is the reseller route that sells below list and the price floor needs the
|
|
3440
|
+
// figure. The rule: the model owner's own published price where the catalog
|
|
3441
|
+
// records one (Google's Veo price, billed unchanged by the OpenRouter
|
|
3442
|
+
// entries; Vertex AI's Imagen 4 Fast price), else the fal entry for the same
|
|
3443
|
+
// upstream model. `unrecorded` means neither exists in this catalog, and the
|
|
3444
|
+
// floor then does not apply: Seedream V4, MiniMax H3 and Gemini Omni Flash
|
|
3445
|
+
// have no fal entry, Kling O3 on KIE does not say whether it is fal's
|
|
3446
|
+
// Standard or Pro tier, Suno has no per-song list price, and the Claude
|
|
3447
|
+
// entries bill per token. Entries of every other provider carry no
|
|
3448
|
+
// reference: their route bills the list price itself.
|
|
3150
3449
|
// ── 3D AI Studio: 3D generation ────────────────────────────────────────
|
|
3151
3450
|
// `credits` is what the model's DEFAULT request bills (the options its
|
|
3152
3451
|
// builder sends when a node sets none), priced by providers/studio3d/
|
|
@@ -3243,6 +3542,7 @@ var MODEL_CATALOG2 = [
|
|
|
3243
3542
|
label: "Nano Banana 2 (KIE)",
|
|
3244
3543
|
description: "Google Gemini 3.1 Flash Image via KIE. $0.04 at 1K, $0.06 at 2K, $0.09 at 4K. Kept as fallback aggregator path; default flipped to fal/nano-banana-2 in 2026-06-04 fal slice.",
|
|
3245
3544
|
cost: { usd: 0.04, usdByResolution: { "1K": 0.04, "2K": 0.06, "4K": 0.09 } },
|
|
3545
|
+
listPrice: { source: "fal", model: "fal/nano-banana-2" },
|
|
3246
3546
|
capabilities: { resolutions: ["1K", "2K", "4K"] }
|
|
3247
3547
|
},
|
|
3248
3548
|
{
|
|
@@ -3255,6 +3555,7 @@ var MODEL_CATALOG2 = [
|
|
|
3255
3555
|
label: "Imagen 4 Fast (KIE)",
|
|
3256
3556
|
description: "Google Imagen 4 Fast via KIE. No price advantage vs Vertex direct. No IP indemnity. Vertex AI lists Imagen 4 Fast at $0.02 per image (2026-09-27), which matches the estimate; the KIE price itself is still unmeasured.",
|
|
3257
3557
|
cost: { usd: 0.02 },
|
|
3558
|
+
listPrice: { source: "owner", usd: 0.02 },
|
|
3258
3559
|
capabilities: { resolutions: [] }
|
|
3259
3560
|
},
|
|
3260
3561
|
{
|
|
@@ -3264,11 +3565,15 @@ var MODEL_CATALOG2 = [
|
|
|
3264
3565
|
task: "text-to-image",
|
|
3265
3566
|
label: "Seedream V4 (KIE)",
|
|
3266
3567
|
description: "ByteDance Seedream V4 via KIE. Stylized output not available on Vertex. No IP indemnity.",
|
|
3267
|
-
cost: { usd: 0.0175 }
|
|
3568
|
+
cost: { usd: 0.0175 },
|
|
3569
|
+
listPrice: { source: "unrecorded" }
|
|
3268
3570
|
},
|
|
3269
3571
|
// ── KIE.ai: newer image models, the cheaper route (price feed 2026-09-23) ─
|
|
3270
3572
|
// Billed from the credits KIE reports on each task. KIE offers no data
|
|
3271
|
-
// processing agreement
|
|
3573
|
+
// processing agreement. Decided 2026-09-23 (Q5 C of the routing plan,
|
|
3574
|
+
// docs/plans/2026-09-26-kie-first-fal-fallback.md): the automatic routes try
|
|
3575
|
+
// KIE first for everything, a recorded risk; pinning the fal entry of a model
|
|
3576
|
+
// is how a job with a real person's photo is kept off KIE.
|
|
3272
3577
|
{
|
|
3273
3578
|
id: "kie/nano-banana-2-edit",
|
|
3274
3579
|
provider: "kie",
|
|
@@ -3277,6 +3582,7 @@ var MODEL_CATALOG2 = [
|
|
|
3277
3582
|
label: "Nano Banana 2 Edit (KIE)",
|
|
3278
3583
|
description: "Nano Banana 2 instruction edit via KIE with up to 8 reference images, half the fal price: $0.04 at 1K, $0.06 at 2K, $0.09 at 4K.",
|
|
3279
3584
|
cost: { usd: 0.04, usdByResolution: { "1K": 0.04, "2K": 0.06, "4K": 0.09 } },
|
|
3585
|
+
listPrice: { source: "fal", model: "fal/nano-banana-2-edit" },
|
|
3280
3586
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
3281
3587
|
},
|
|
3282
3588
|
{
|
|
@@ -3287,6 +3593,7 @@ var MODEL_CATALOG2 = [
|
|
|
3287
3593
|
label: "Nano Banana Pro (KIE)",
|
|
3288
3594
|
description: "Google Gemini 3 Pro Image via KIE: stronger on complex scenes, text in images and new characters than Nano Banana 2 (a relighting edit showed no difference on 2026-09-23). $0.09 at 1K or 2K, $0.12 at 4K (fal: $0.15, $0.30).",
|
|
3289
3595
|
cost: { usd: 0.09, usdByResolution: { "1K": 0.09, "2K": 0.09, "4K": 0.12 } },
|
|
3596
|
+
listPrice: { source: "fal", model: "fal/nano-banana-pro" },
|
|
3290
3597
|
capabilities: { resolutions: ["1K", "2K", "4K"], maxInputImages: 0 }
|
|
3291
3598
|
},
|
|
3292
3599
|
{
|
|
@@ -3297,6 +3604,7 @@ var MODEL_CATALOG2 = [
|
|
|
3297
3604
|
label: "Nano Banana Pro Edit (KIE)",
|
|
3298
3605
|
description: "Nano Banana Pro instruction edit via KIE with up to 8 reference images. $0.09 at 1K or 2K, $0.12 at 4K (fal: $0.15, $0.30).",
|
|
3299
3606
|
cost: { usd: 0.09, usdByResolution: { "1K": 0.09, "2K": 0.09, "4K": 0.12 } },
|
|
3607
|
+
listPrice: { source: "fal", model: "fal/nano-banana-pro-edit" },
|
|
3300
3608
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
3301
3609
|
},
|
|
3302
3610
|
{
|
|
@@ -3307,6 +3615,7 @@ var MODEL_CATALOG2 = [
|
|
|
3307
3615
|
label: "Seedream 5 Lite (KIE)",
|
|
3308
3616
|
description: "ByteDance Seedream 5 Lite via KIE, 2K or 4K. $0.0275 per image (fal: $0.035).",
|
|
3309
3617
|
cost: { usd: 0.0275 },
|
|
3618
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite" },
|
|
3310
3619
|
capabilities: { resolutions: ["2K", "4K"], maxInputImages: 0 }
|
|
3311
3620
|
},
|
|
3312
3621
|
{
|
|
@@ -3317,6 +3626,7 @@ var MODEL_CATALOG2 = [
|
|
|
3317
3626
|
label: "Seedream 5 Lite Edit (KIE)",
|
|
3318
3627
|
description: "ByteDance Seedream 5 Lite image-to-image via KIE, up to 10 reference images, 2K or 4K. $0.0275 per image (fal: $0.035).",
|
|
3319
3628
|
cost: { usd: 0.0275 },
|
|
3629
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite-edit" },
|
|
3320
3630
|
capabilities: { resolutions: ["2K", "4K"], needsImageInput: true, maxInputImages: 10 }
|
|
3321
3631
|
},
|
|
3322
3632
|
{
|
|
@@ -3327,6 +3637,7 @@ var MODEL_CATALOG2 = [
|
|
|
3327
3637
|
label: "Seedream 5 Pro (KIE)",
|
|
3328
3638
|
description: "ByteDance Seedream 5 Pro via KIE, 1K or 2K. $0.035 at 1K, $0.07 at 2K (fal: $0.0675 to $0.135).",
|
|
3329
3639
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
3640
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro" },
|
|
3330
3641
|
capabilities: { resolutions: ["1K", "2K"], maxInputImages: 0 }
|
|
3331
3642
|
},
|
|
3332
3643
|
{
|
|
@@ -3337,6 +3648,7 @@ var MODEL_CATALOG2 = [
|
|
|
3337
3648
|
label: "Seedream 5 Pro Edit (KIE)",
|
|
3338
3649
|
description: "ByteDance Seedream 5 Pro image-to-image via KIE, up to 10 reference images, 1K or 2K. $0.035 at 1K, $0.07 at 2K, plus $0.0025 per extra reference.",
|
|
3339
3650
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
3651
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro-edit" },
|
|
3340
3652
|
capabilities: { resolutions: ["1K", "2K"], needsImageInput: true, maxInputImages: 10 }
|
|
3341
3653
|
},
|
|
3342
3654
|
// ── fal.ai: image gen ─────────────────────────────────────────────────
|
|
@@ -3631,8 +3943,11 @@ var MODEL_CATALOG2 = [
|
|
|
3631
3943
|
// ── KIE.ai video: the cheaper route to the same upstream models ──────────
|
|
3632
3944
|
// Async via KIE's Veo and createTask APIs (providers/kie-video.ts). 20 to 85%
|
|
3633
3945
|
// below fal (KIE price feed, 2026-09-23). KIE offers no data processing
|
|
3634
|
-
// agreement
|
|
3635
|
-
//
|
|
3946
|
+
// agreement. Decided 2026-09-23 (Q5 C of the routing plan): the automatic
|
|
3947
|
+
// routes try KIE first for everything, a recorded risk; pinning the fal
|
|
3948
|
+
// entry of a model is how a job with a real person's photo is kept off KIE.
|
|
3949
|
+
// Anchors are the model's default clip at 720p (Kling: its fixed size). KIE
|
|
3950
|
+
// docs vetted 2026-09-23.
|
|
3636
3951
|
{
|
|
3637
3952
|
id: "kie/veo-3-1-lite",
|
|
3638
3953
|
provider: "kie",
|
|
@@ -3641,6 +3956,7 @@ var MODEL_CATALOG2 = [
|
|
|
3641
3956
|
label: "Veo 3.1 Lite (KIE)",
|
|
3642
3957
|
description: "Google Veo 3.1 Lite via KIE. A start frame, or a start and an end frame; 4, 6 or 8 s; 720p, 1080p or 4K; always with audio. Accepted a minted character's photoreal stills on 2026-09-23. Priced per clip: $0.15 at 720p, $0.175 at 1080p, $0.75 at 4K (fal: $0.24 silent, $0.40 with audio, for 8 s at 720p). KIE's Auto aspect letterboxes, so pick 16:9 or 9:16.",
|
|
3643
3958
|
cost: { usd: 0.15 },
|
|
3959
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-lite" },
|
|
3644
3960
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3645
3961
|
},
|
|
3646
3962
|
{
|
|
@@ -3651,6 +3967,7 @@ var MODEL_CATALOG2 = [
|
|
|
3651
3967
|
label: "Veo 3.1 Fast (KIE)",
|
|
3652
3968
|
description: "Google Veo 3.1 Fast via KIE. A start frame, or a start and an end frame; 4, 6 or 8 s; 720p, 1080p or 4K; always with audio. Accepted a minted character's photoreal stills on 2026-09-23. Priced per clip: $0.30 at 720p, $0.325 at 1080p, $0.90 at 4K (fal: $1.20 with audio for 8 s). Pick 16:9 or 9:16.",
|
|
3653
3969
|
cost: { usd: 0.3 },
|
|
3970
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-fast" },
|
|
3654
3971
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3655
3972
|
},
|
|
3656
3973
|
{
|
|
@@ -3661,6 +3978,7 @@ var MODEL_CATALOG2 = [
|
|
|
3661
3978
|
label: "Veo 3.1 (KIE)",
|
|
3662
3979
|
description: "Google Veo 3.1 (quality tier) via KIE. A start frame, or a start and an end frame; 4, 6 or 8 s; 720p, 1080p or 4K; always with audio. Same upstream model as the tested Lite and Fast tiers. Priced per clip: $1.25 at 720p, $1.275 at 1080p, $1.85 at 4K (fal: $3.20 with audio).",
|
|
3663
3980
|
cost: { usd: 1.25 },
|
|
3981
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1" },
|
|
3664
3982
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3665
3983
|
},
|
|
3666
3984
|
// ── OpenRouter video: Google Veo 3.1 at Google's own prices ───────────────
|
|
@@ -3707,6 +4025,7 @@ var MODEL_CATALOG2 = [
|
|
|
3707
4025
|
label: "Grok Imagine (KIE)",
|
|
3708
4026
|
description: "xAI Grok Imagine via KIE. A start frame, 6 to 30 s, 480p, 720p or 1080p, with audio. Accepted a minted character's photoreal stills on 2026-09-23. $0.012 per second at 480p, $0.0225 at 720p, $0.04 at 1080p (fal: $0.07 at 720p). No end frame.",
|
|
3709
4027
|
cost: { usd: 0.135 },
|
|
4028
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-image-to-video" },
|
|
3710
4029
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
3711
4030
|
},
|
|
3712
4031
|
{
|
|
@@ -3717,6 +4036,7 @@ var MODEL_CATALOG2 = [
|
|
|
3717
4036
|
label: "Grok Imagine 1.5 (KIE, preview)",
|
|
3718
4037
|
description: "xAI Grok Imagine 1.5 preview via KIE. A start frame, 1 to 15 s, 480p or 720p. Not yet tested with a minted character. $0.012 per second at 480p, $0.0225 at 720p (fal: $0.14 at 720p). No end frame.",
|
|
3719
4038
|
cost: { usd: 0.18 },
|
|
4039
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-1-5-image-to-video" },
|
|
3720
4040
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
3721
4041
|
},
|
|
3722
4042
|
{
|
|
@@ -3727,6 +4047,7 @@ var MODEL_CATALOG2 = [
|
|
|
3727
4047
|
label: "Kling 2.5 Turbo Pro (KIE)",
|
|
3728
4048
|
description: "Kuaishou Kling 2.5 Turbo Pro via KIE. A start frame and an optional end frame, 5 or 10 s, fixed size. The fal route accepted minted characters; this KIE route is not yet tested with a minted character. $0.21 for 5 s, $0.42 for 10 s (fal: $0.35 for 5 s).",
|
|
3729
4049
|
cost: { usd: 0.21 },
|
|
4050
|
+
listPrice: { source: "fal", model: "fal/kling-v2-5-image-to-video" },
|
|
3730
4051
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3731
4052
|
},
|
|
3732
4053
|
{
|
|
@@ -3737,6 +4058,7 @@ var MODEL_CATALOG2 = [
|
|
|
3737
4058
|
label: "Kling 2.6 (KIE)",
|
|
3738
4059
|
description: "Kuaishou Kling 2.6 via KIE. A start frame, 5 or 10 s, fixed size, silent unless the job sets generateAudio. Not yet tested with a minted character. $0.275 for 5 s silent, $0.55 with sound, 10 s double (fal: $0.35 or $0.70).",
|
|
3739
4060
|
cost: { usd: 0.275 },
|
|
4061
|
+
listPrice: { source: "fal", model: "fal/kling-2-6-image-to-video" },
|
|
3740
4062
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
3741
4063
|
},
|
|
3742
4064
|
{
|
|
@@ -3748,6 +4070,7 @@ var MODEL_CATALOG2 = [
|
|
|
3748
4070
|
refusesPhotorealPeople: true,
|
|
3749
4071
|
description: "ByteDance Seedance 2.0 via KIE. A start frame and an optional end frame, 4 to 15 s, 480p to 4K. Seedance 2.0 refuses photoreal people, generated ones included: on fal it refused them in every mode (observed 2026-09-23). The refusal comes from the moderation of the model itself, so this route carries the same caution; the KIE route itself has not been run with a person. $0.095 per second at 480p, $0.205 at 720p, $0.51 at 1080p, $1.04 at 4K (fal: about $0.30 at 720p).",
|
|
3750
4072
|
cost: { usd: 1.025 },
|
|
4073
|
+
listPrice: { source: "fal", model: "fal/seedance-2-0-image-to-video" },
|
|
3751
4074
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3752
4075
|
},
|
|
3753
4076
|
{
|
|
@@ -3759,6 +4082,7 @@ var MODEL_CATALOG2 = [
|
|
|
3759
4082
|
refusesPhotorealPeople: true,
|
|
3760
4083
|
description: "ByteDance Seedance 2.5 via KIE. A start frame and an optional end frame, 4 to 30 s, 480p to 1080p. Seedance 2.5 refuses photoreal people, generated ones included: on fal it refused them in every mode (observed 2026-08-21 and 2026-09-23). The refusal comes from the moderation of the model itself, so this route carries the same caution; the KIE route itself has not been run with a person. $0.14 per second at 480p, $0.315 at 720p, $0.79 at 1080p.",
|
|
3761
4084
|
cost: { usd: 1.575 },
|
|
4085
|
+
listPrice: { source: "fal", model: "fal/seedance-2-5-image-to-video" },
|
|
3762
4086
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3763
4087
|
},
|
|
3764
4088
|
{
|
|
@@ -3769,6 +4093,7 @@ var MODEL_CATALOG2 = [
|
|
|
3769
4093
|
label: "Wan 3.0 (KIE)",
|
|
3770
4094
|
description: "Alibaba Wan 3.0 via KIE (hosted only, no downloadable weights). A start frame and an optional end frame, 2 to 30 s, 480p, 720p or 1080p, audio included. Accepted a minted character's photoreal stills on 2026-09-24. $0.04 per second at 480p, $0.08 at 720p, $0.16 at 1080p (fal and Alibaba: $0.10 at 720p).",
|
|
3771
4095
|
cost: { usd: 0.4 },
|
|
4096
|
+
listPrice: { source: "fal", model: "fal/wan-3-0-image-to-video" },
|
|
3772
4097
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3773
4098
|
},
|
|
3774
4099
|
{
|
|
@@ -3779,6 +4104,7 @@ var MODEL_CATALOG2 = [
|
|
|
3779
4104
|
label: "MiniMax H3 (KIE)",
|
|
3780
4105
|
description: "MiniMax H3 via KIE. A start frame and an optional end frame, 4 to 15 s, 768p (the 720p setting) or 2K (the 1080p setting). Accepted a minted character's photoreal stills on 2026-09-24. Its open weights exclude the EU by license; this is KIE's hosted service. $0.04 per second at 768p, $0.065 at 2K.",
|
|
3781
4106
|
cost: { usd: 0.24 },
|
|
4107
|
+
listPrice: { source: "unrecorded" },
|
|
3782
4108
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3783
4109
|
},
|
|
3784
4110
|
{
|
|
@@ -3789,6 +4115,7 @@ var MODEL_CATALOG2 = [
|
|
|
3789
4115
|
label: "Gemini Omni 1.1 Flash (KIE)",
|
|
3790
4116
|
description: "Google Gemini Omni 1.1 Flash via KIE, first-and-last-frame mode. A start frame and an optional end frame, 4, 6, 8 or 10 s, 720p to 4K, 16:9 or 9:16. KIE refuses registered characters in this mode, so identity comes from the frames alone. Accepted a minted character's photoreal stills on 2026-09-24 (set 9:16 for a portrait still: it defaults to 16:9 and crops). $0.315 for 4 s up to $0.63 for 10 s at 720p or 1080p, $0.735 to $1.05 at 4K. No fal equivalent.",
|
|
3791
4117
|
cost: { usd: 0.525 },
|
|
4118
|
+
listPrice: { source: "unrecorded" },
|
|
3792
4119
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3793
4120
|
},
|
|
3794
4121
|
{
|
|
@@ -3799,6 +4126,7 @@ var MODEL_CATALOG2 = [
|
|
|
3799
4126
|
label: "Kling O3 (KIE)",
|
|
3800
4127
|
description: "Kuaishou Kling O3 (KIE calls it Kling 3.0 Omni) via KIE. A start frame and an optional end frame, 3 to 15 s, 720p to 4K in the start frame's shape, silent unless the job sets generateAudio. Accepted a minted character's photoreal stills on 2026-09-24. $0.07 per second at 720p, $0.09 at 1080p, $0.335 at 4K; with sound $0.10 and $0.135.",
|
|
3801
4128
|
cost: { usd: 0.35 },
|
|
4129
|
+
listPrice: { source: "unrecorded" },
|
|
3802
4130
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3803
4131
|
},
|
|
3804
4132
|
{
|
|
@@ -4117,6 +4445,7 @@ var MODEL_CATALOG2 = [
|
|
|
4117
4445
|
label: "Suno V5 (KIE)",
|
|
4118
4446
|
description: "Suno V5 music generation via KIE. Text-to-music with or without lyrics; ~4 min cap per generation. Default text-to-audio. No IP indemnity (KIE aggregator). KIE's site now features Suno v5.5 and no longer lists a V5 price (checked 2026-09-27), so the $0.04 is an unverified hint.",
|
|
4119
4447
|
cost: { usd: 0.04 },
|
|
4448
|
+
listPrice: { source: "unrecorded" },
|
|
4120
4449
|
isDefault: true
|
|
4121
4450
|
},
|
|
4122
4451
|
// ── audio: voice via ElevenLabs (B2) ───────────────────────────────────
|
|
@@ -4165,6 +4494,7 @@ var MODEL_CATALOG2 = [
|
|
|
4165
4494
|
label: "Claude Sonnet 4.5 (KIE)",
|
|
4166
4495
|
description: "Anthropic Claude Sonnet 4.5 via KIE (Anthropic Messages API at a lower rate). Default LLM director: prompt improvement + shot descriptions. Cost billed per token; catalog usd is a hint.",
|
|
4167
4496
|
cost: { usd: 0.01 },
|
|
4497
|
+
listPrice: { source: "unrecorded" },
|
|
4168
4498
|
isDefault: true
|
|
4169
4499
|
},
|
|
4170
4500
|
{
|
|
@@ -4174,7 +4504,8 @@ var MODEL_CATALOG2 = [
|
|
|
4174
4504
|
task: "text-generation",
|
|
4175
4505
|
label: "Claude Opus 4.5 (KIE)",
|
|
4176
4506
|
description: "Anthropic Claude Opus 4.5 via KIE. Higher quality, higher cost; use for harder director reasoning. Cost billed per token; catalog usd is a hint.",
|
|
4177
|
-
cost: { usd: 0.05 }
|
|
4507
|
+
cost: { usd: 0.05 },
|
|
4508
|
+
listPrice: { source: "unrecorded" }
|
|
4178
4509
|
},
|
|
4179
4510
|
{
|
|
4180
4511
|
id: "openrouter/claude-sonnet-4-6",
|
|
@@ -4239,8 +4570,26 @@ var MODEL_CATALOG2 = [
|
|
|
4239
4570
|
description: "Vertex AI direct, newest flash variant. Global region. Preview status."
|
|
4240
4571
|
}
|
|
4241
4572
|
];
|
|
4573
|
+
var routeEntries2 = /* @__PURE__ */ new Map();
|
|
4574
|
+
function routeEntry2(id) {
|
|
4575
|
+
const cached = routeEntries2.get(id);
|
|
4576
|
+
if (cached) return cached;
|
|
4577
|
+
const route = getRoute2(id);
|
|
4578
|
+
const primary = route ? MODEL_CATALOG2.find((m) => m.id === route.primary) : void 0;
|
|
4579
|
+
if (!route || !primary) return void 0;
|
|
4580
|
+
const entry = {
|
|
4581
|
+
...primary,
|
|
4582
|
+
// A route is never the default of its task (that is slice 3's switch).
|
|
4583
|
+
isDefault: void 0,
|
|
4584
|
+
id: route.id,
|
|
4585
|
+
label: route.label,
|
|
4586
|
+
description: `Runs on KIE first and, when KIE fails on its side, again on fal with the same settings. ${primary.description ?? ""}`.trim()
|
|
4587
|
+
};
|
|
4588
|
+
routeEntries2.set(id, entry);
|
|
4589
|
+
return entry;
|
|
4590
|
+
}
|
|
4242
4591
|
function tryGetModel2(id) {
|
|
4243
|
-
return MODEL_CATALOG2.find((m) => m.id === id);
|
|
4592
|
+
return MODEL_CATALOG2.find((m) => m.id === id) ?? routeEntry2(id);
|
|
4244
4593
|
}
|
|
4245
4594
|
function supportedResolutions(model) {
|
|
4246
4595
|
const declared = model?.capabilities?.resolutions;
|
|
@@ -5890,7 +6239,7 @@ function nodeUrl(res, nodeId) {
|
|
|
5890
6239
|
}
|
|
5891
6240
|
var studioListProjects = defineTool({
|
|
5892
6241
|
name: "studio_list_projects",
|
|
5893
|
-
description: 'List the Studio projects visible to this API key (id, slug, name). A projectId is required by studio_run_workflow, studio_mint_character and studio_replace_character_in_video; call this first to find it (the usual one is slug "default").',
|
|
6242
|
+
description: 'List the Studio projects visible to this API key (id, slug, name). A projectId is required by studio_run_workflow, studio_run_published, studio_mint_character, studio_rig_3d and studio_replace_character_in_video; call this first to find it (the usual one is slug "default"). It is optional on studio_generate_image and studio_generate_3d, where it chooses the project the result is filed in.',
|
|
5894
6243
|
inputSchema: {},
|
|
5895
6244
|
annotations: { readOnlyHint: true },
|
|
5896
6245
|
handler: async (_args, ctx) => {
|
|
@@ -5900,17 +6249,26 @@ var studioListProjects = defineTool({
|
|
|
5900
6249
|
});
|
|
5901
6250
|
var studioGenerateImage = defineTool({
|
|
5902
6251
|
name: "studio_generate_image",
|
|
5903
|
-
description:
|
|
6252
|
+
description: 'Generate an image from a prompt. A brand is optional: pass brandSlug to have the Studio apply that brand\'s style prefix and reference images server-side, or leave it out for an image without a brand. Optionally pin a character (characterSlug) so its identity references are injected too. A brandSlug or characterSlug that names nothing your company can use is refused before anything is spent (404, code input_not_found), and the error text says what to do next: act on it or relay it as written. The image is filed in projectId when given, otherwise in the project named after the brand (created on first use), otherwise in the project "default". Submits a job and, when wait=true (default), polls until it finishes and returns the signed image URL and costUsd (the provider cost of the job; what a company on prepaid credits pays for it is at least that cost times its rate, see studio_get_usage). With wait=false it returns the jobId immediately for studio_get_job. candidateCount asks for up to four candidates, but some models render one image regardless of the count: the result carries `candidates: { requested, rendered, mayRender? }`, and a `notice` to pass on to the user whenever fewer images will be made than were asked for. Typical cost ~$0.04 per image.',
|
|
5904
6253
|
inputSchema: {
|
|
5905
|
-
brandSlug: slug.
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
6254
|
+
brandSlug: slug.optional().describe(
|
|
6255
|
+
"Optional. The slug of a brand of your company (create one with studio_create_brand). Omit it to generate without a brand. A slug that names no brand of your company is refused with 404 input_not_found; it is never used as a project name."
|
|
6256
|
+
),
|
|
6257
|
+
brandMode: z8.string().optional().describe('Optional brand mode (e.g. "illustration"); modes are defined per brand. Only meaningful with brandSlug.'),
|
|
6258
|
+
prompt: z8.string().min(1).describe("What to generate. With brandSlug, the brand style prefix is prepended server-side."),
|
|
6259
|
+
characterSlug: slug.optional().describe(
|
|
6260
|
+
"Optional character whose identity references are injected: one of your company's, or one another company shared (see studio_list_characters). A slug your company cannot use is refused with 404 input_not_found."
|
|
6261
|
+
),
|
|
6262
|
+
projectId: z8.string().min(1).optional().describe(
|
|
6263
|
+
'Optional project to file the image under (an id from studio_list_projects; it must be a project of your company, anything else is a 404). Omit it to use the project named after brandSlug, or "default" when there is no brand.'
|
|
6264
|
+
),
|
|
5909
6265
|
model: z8.string().optional().describe('Image model id (e.g. "kie/nano-banana-2", "fal/nano-banana-2"). Omit for the Studio default.'),
|
|
5910
6266
|
aspectRatio: z8.string().optional().describe('Aspect ratio such as "1:1", "16:9", "3:4".'),
|
|
5911
6267
|
imageSize: z8.string().optional().describe('Provider size hint, e.g. "1K", "2K".'),
|
|
5912
6268
|
seed: z8.number().int().optional(),
|
|
5913
|
-
candidateCount: z8.number().int().min(1).max(4).optional().describe(
|
|
6269
|
+
candidateCount: z8.number().int().min(1).max(4).optional().describe(
|
|
6270
|
+
"Number of candidates (1 to 4). Not every model takes a count: most KIE image models render one image whatever is asked, and a `route/...` model runs on KIE first, so expect one there too (its fal fallback may render up to four). The result carries `candidates: { requested, rendered, mayRender? }`, plus a `notice` whenever fewer will be rendered than were asked for; only rendered images are charged."
|
|
6271
|
+
),
|
|
5914
6272
|
wait: z8.boolean().default(true).describe("Poll until the job finishes (default true)."),
|
|
5915
6273
|
waitSeconds
|
|
5916
6274
|
},
|
|
@@ -5922,19 +6280,24 @@ var studioGenerateImage = defineTool({
|
|
|
5922
6280
|
if (args.seed !== void 0) settings.seed = args.seed;
|
|
5923
6281
|
if (args.candidateCount !== void 0) settings.candidateCount = args.candidateCount;
|
|
5924
6282
|
const submitted = await ctx.client.generate({
|
|
5925
|
-
brandSlug: args.brandSlug,
|
|
6283
|
+
...args.brandSlug ? { brandSlug: args.brandSlug } : {},
|
|
5926
6284
|
...args.brandMode ? { brandMode: args.brandMode } : {},
|
|
5927
6285
|
prompt: args.prompt,
|
|
5928
6286
|
...args.characterSlug ? { characterSlug: args.characterSlug } : {},
|
|
6287
|
+
...args.projectId ? { projectId: args.projectId } : {},
|
|
5929
6288
|
...Object.keys(settings).length > 0 ? { settings } : {}
|
|
5930
6289
|
});
|
|
5931
|
-
|
|
6290
|
+
const plan = {
|
|
6291
|
+
...submitted.candidates ? { candidates: submitted.candidates } : {},
|
|
6292
|
+
...typeof submitted.notice === "string" && submitted.notice.length > 0 ? { notice: submitted.notice } : {}
|
|
6293
|
+
};
|
|
6294
|
+
if (!args.wait) return { jobId: submitted.jobId, kind: submitted.kind, status: "queued", ...plan };
|
|
5932
6295
|
const done = await waitForJob(ctx.client, submitted.jobId, pollOpts(ctx, args.waitSeconds));
|
|
5933
6296
|
const out = projectJob(done);
|
|
5934
6297
|
if (done.job.status !== "succeeded") {
|
|
5935
6298
|
throw new Error(`Job ${submitted.jobId} ${done.job.status}: ${done.job.errorMessage ?? "no error message from the API"}`);
|
|
5936
6299
|
}
|
|
5937
|
-
return { ...out, kind: submitted.kind };
|
|
6300
|
+
return { ...out, kind: submitted.kind, ...plan };
|
|
5938
6301
|
}
|
|
5939
6302
|
});
|
|
5940
6303
|
var GENERATE_3D_TASKS = /* @__PURE__ */ new Set(["text-to-3d", "image-to-3d"]);
|
|
@@ -6080,7 +6443,7 @@ var studioGetJob = defineTool({
|
|
|
6080
6443
|
});
|
|
6081
6444
|
var studioListCharacters = defineTool({
|
|
6082
6445
|
name: "studio_list_characters",
|
|
6083
|
-
description: "List
|
|
6446
|
+
description: "List the characters this API key can use (slug, name, descriptor prompt, default model, shared, owned): the ones its company minted, plus the ones another company shared with everyone (owned: false, usable in generations but read-only). A slug is unique within one company; when your company and a shared character use the same slug, yours is the one listed and used. Use a slug with studio_get_character for its reference images, or with studio_generate_image to render that character.",
|
|
6084
6447
|
inputSchema: {},
|
|
6085
6448
|
annotations: { readOnlyHint: true },
|
|
6086
6449
|
handler: async (_args, ctx) => {
|
|
@@ -6090,7 +6453,7 @@ var studioListCharacters = defineTool({
|
|
|
6090
6453
|
});
|
|
6091
6454
|
var studioGetCharacter = defineTool({
|
|
6092
6455
|
name: "studio_get_character",
|
|
6093
|
-
description: "Fetch one character by slug with its full context: descriptor and negative prompts plus every frozen reference image (category such as face, three-quarter, profile, full-body, close-up, lighting-warm, lighting-cool;
|
|
6456
|
+
description: "Fetch one character by slug with its full context: descriptor and negative prompts plus every frozen reference image (category such as face, three-quarter, profile, full-body, close-up, lighting-warm, lighting-cool; URL). The full-body or face reference URL is what studio_replace_character_in_video needs as characterImageUrl. For a character your company owns the URL is a direct image URL. For a character another company shared (owned: false) it is a Studio address: pass it unchanged as an image input to any Studio tool and the Studio resolves it when the job runs, but it is not a public image URL, and it stops working the moment the owner stops sharing.",
|
|
6094
6457
|
inputSchema: { slug },
|
|
6095
6458
|
annotations: { readOnlyHint: true },
|
|
6096
6459
|
handler: async (args, ctx) => {
|
|
@@ -6105,7 +6468,7 @@ function assertLikenessRightsConfirmed(value) {
|
|
|
6105
6468
|
}
|
|
6106
6469
|
var studioCreateCharacter = defineTool({
|
|
6107
6470
|
name: "studio_create_character",
|
|
6108
|
-
description: "Create an empty character record (no reference images yet). Prefer studio_mint_character, which generates the 7-view identity sheet and freezes it as references in one call. Use this only when you will upload your own reference images. Requires likenessRightsConfirmed: true, which you may only pass after the human you act for confirmed it for this character. Fails with 409 if
|
|
6471
|
+
description: "Create an empty character record (no reference images yet). Prefer studio_mint_character, which generates the 7-view identity sheet and freezes it as references in one call. Use this only when you will upload your own reference images. Requires likenessRightsConfirmed: true, which you may only pass after the human you act for confirmed it for this character. Fails with 409 (code slug_taken) only if your own company already has a character with this slug; a slug another company uses is free to take.",
|
|
6109
6472
|
inputSchema: {
|
|
6110
6473
|
name: z8.string().min(1).describe('Display name, e.g. "Bruno Hartmann".'),
|
|
6111
6474
|
slug,
|
|
@@ -6197,7 +6560,7 @@ var studioMintCharacter = defineTool({
|
|
|
6197
6560
|
});
|
|
6198
6561
|
var studioCreateBrand = defineTool({
|
|
6199
6562
|
name: "studio_create_brand",
|
|
6200
|
-
description: "Create a brand (a style preset: name, slug, optional style prefix prepended to every prompt, optional free-text context). Upload reference images afterwards with studio_upload_brand_reference. Fails with 409 if
|
|
6563
|
+
description: "Create a brand (a style preset: name, slug, optional style prefix prepended to every prompt, optional free-text context). Upload reference images afterwards with studio_upload_brand_reference. Fails with 409 (code slug_taken) only if your own company already has a brand with this slug; a slug another company uses is free to take.",
|
|
6201
6564
|
inputSchema: {
|
|
6202
6565
|
name: z8.string().min(1),
|
|
6203
6566
|
slug,
|
|
@@ -6222,7 +6585,7 @@ var studioUploadBrandReference = defineTool({
|
|
|
6222
6585
|
name: "studio_upload_brand_reference",
|
|
6223
6586
|
description: `Upload one reference image to a brand's library from a local file path or an http(s) URL. The Studio converts it to a 512px WebP and injects it into generations under that brand (up to the brand's maxReferenceImages). Category groups references (default "general"); tags and a 1 to 10 quality score are optional.`,
|
|
6224
6587
|
inputSchema: {
|
|
6225
|
-
brandSlug: slug,
|
|
6588
|
+
brandSlug: slug.describe("The slug of a brand of your company (from studio_create_brand). A slug that names no brand of your company is a 404, and nothing is uploaded."),
|
|
6226
6589
|
source: z8.string().min(1).describe("Local file path or http(s) URL of an image."),
|
|
6227
6590
|
category: z8.string().optional().describe('Library category, default "general".'),
|
|
6228
6591
|
label: z8.string().optional().describe("Optional file name / label."),
|
|
@@ -6344,7 +6707,9 @@ var studioReplaceCharacterInVideo = defineTool({
|
|
|
6344
6707
|
inputSchema: {
|
|
6345
6708
|
projectId: z8.string().min(1).describe("Project id from studio_list_projects."),
|
|
6346
6709
|
sceneFrameUrl: httpUrl.describe("Public or signed URL of ONE frame of the target shot (png/jpg)."),
|
|
6347
|
-
characterSlug: slug.optional().describe(
|
|
6710
|
+
characterSlug: slug.optional().describe(
|
|
6711
|
+
"Character to insert: one of your company's, or one another company shared. Its full-body reference (fallback three-quarter, face) is resolved automatically. A slug your company cannot use is a 404 before the run starts, so nothing is spent."
|
|
6712
|
+
),
|
|
6348
6713
|
characterReferenceUrl: httpUrl.optional().describe("Explicit character reference image URL (use instead of characterSlug)."),
|
|
6349
6714
|
endFrameUrl: httpUrl.optional().describe("Optional second approved keyframe for the END of the clip. Switches the video model to Kling (start + end frame) unless videoModel is set explicitly."),
|
|
6350
6715
|
scope: z8.enum(SWAP_SCOPES).optional().describe("How much of the person to replace. identity (default): face, head and hair from the character, the footage keeps its body. full_body: also the character body proportions; the silhouette changes, so the background behind the old body and every contact point must be re-derived."),
|
|
@@ -6417,7 +6782,7 @@ var studioReplaceCharacterInVideo = defineTool({
|
|
|
6417
6782
|
});
|
|
6418
6783
|
var studioGetSpend = defineTool({
|
|
6419
6784
|
name: "studio_get_spend",
|
|
6420
|
-
description:
|
|
6785
|
+
description: "Read the spend ledger: total USD and job counts for today, the last 7 days and the last 30 days, plus the most recent paid actions. The amounts are provider cost (each job's costUsd); for a company on prepaid credits, what left its balance is in studio_get_usage (`credits`). tzOffsetMinutes is the viewer's Date.getTimezoneOffset() so \"today\" rolls at local midnight (default UTC).",
|
|
6421
6786
|
inputSchema: {
|
|
6422
6787
|
tzOffsetMinutes: z8.number().int().min(-840).max(840).optional()
|
|
6423
6788
|
},
|
|
@@ -6429,7 +6794,7 @@ var studioGetSpend = defineTool({
|
|
|
6429
6794
|
});
|
|
6430
6795
|
var studioGetUsage = defineTool({
|
|
6431
6796
|
name: "studio_get_usage",
|
|
6432
|
-
description:
|
|
6797
|
+
description: "Read what limits the company this key belongs to, and what it has used: month-to-date spend (usedUsd, realizedUsd, inFlightEstimatedUsd) and job count at provider cost, the monthly cap (capUsd) and requests-per-minute limit (rpm), null meaning none, and a per-model breakdown, most expensive first. A company on prepaid credits (every new company; a company the Studio already knew keeps the caps it had) has no monthly cap unless the Lumitra operator set one; `credits` says what limits it instead: `balanceUsd` (the prepaid balance), `dailyLimitUsd` (null: none), `usedTodayUsd` (what left the balance today, running jobs included) and `dailyResetsAt` (midnight UTC). Each finished generation is debited from the balance at provider cost times the company's rate (1.50 on pay as you go), or 80 percent of the model's list price where that is higher, so the credit figures are higher than the provider-cost figures beside them. balanceUsd does not yet subtract what is reserved for requests that are still starting or running, so the room for a new request can be smaller than balanceUsd. A company not on credits has `credits` null and is held to its monthly cap (20 USD by default, capSource \"default\", unless the Lumitra operator set a plan). Call this before a large batch to check remaining room, or after a 402 or 429 to see what tripped. The refusal's own error text says which limit it was and what to do, so relay it as written; it also names the code: `credits_exhausted` (the balance does not cover the request: add credits on the Billing page, or wait a few seconds when part of the balance is only reserved), `daily_cap_reached` (on credits a company owner or admin raises the daily limit on the Billing page; it resets at midnight UTC), `monthly_cap_reached` (only the Lumitra operator can raise it) or `velocity_limit_reached` (too many paid requests in a short time: wait as long as the text says). usedUsd includes in-flight (not yet settled) job estimates, so it can go DOWN as well as up: a job that later fails releases its estimate.",
|
|
6433
6798
|
inputSchema: {},
|
|
6434
6799
|
annotations: { readOnlyHint: true },
|
|
6435
6800
|
handler: async (_args, ctx) => {
|
|
@@ -6549,7 +6914,7 @@ function registerTools(server, ctx) {
|
|
|
6549
6914
|
|
|
6550
6915
|
// src/server.ts
|
|
6551
6916
|
var SERVER_NAME = "lumitra-studio";
|
|
6552
|
-
var SERVER_VERSION = "0.
|
|
6917
|
+
var SERVER_VERSION = "0.5.0";
|
|
6553
6918
|
function createStudioMcpServer(opts = {}) {
|
|
6554
6919
|
const config = opts.config ?? resolveConfig(opts.env ?? process.env);
|
|
6555
6920
|
const client = new StudioClient(config, {
|
|
@@ -6561,7 +6926,7 @@ function createStudioMcpServer(opts = {}) {
|
|
|
6561
6926
|
{
|
|
6562
6927
|
instructions: [
|
|
6563
6928
|
"Lumitra Studio: brand-consistent image generation, character minting, workflow runs and the still-swap video recipe over the hosted Studio API.",
|
|
6564
|
-
"Start with studio_list_projects to get a projectId. Every generation costs real money (roughly $0.04 per image, $0.30 per character mint, $0.70 per video take); check studio_get_spend when in doubt.",
|
|
6929
|
+
"Start with studio_list_projects to get a projectId. Every generation costs real money (roughly $0.04 per image, $0.30 per character mint, $0.70 per video take); check studio_get_spend when in doubt, and studio_get_usage for what limits the company (a prepaid credit balance with a daily limit, or a monthly cap). A refusal carries the Studio's own sentence with the next step in it: act on it or relay it as written.",
|
|
6565
6930
|
"Long jobs: tools wait by default (up to timeoutSeconds, 600 by default) and return ids you can poll with studio_get_job / studio_get_run if they time out.",
|
|
6566
6931
|
"Authoring a workflow is free and revisioned: studio_workflow_describe for the vocabulary, studio_workflow_create to build one from ops, studio_workflow_apply to change it on top of a baseRevision (a stale_revision result means re-read, rebase, retry), studio_workflow_validate to dry-run before spending, then studio_run_workflow with its savedId.",
|
|
6567
6932
|
"Workflow hygiene: one deliverable is ONE saved workflow with a node per item, and a refinement of an item (edit, upscale, variation) is a NEW NODE appended to that workflow with studio_workflow_apply (addNode plus a connect from the node it refines, so the earlier generation is its input), then studio_run_workflow with savedId and nodeIds limited to the new node. Never send a fresh inline definition for a follow-up: every listed inline run is saved as its own workflow, which scatters one deliverable across duplicates."
|