@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/cli.js
CHANGED
|
@@ -95,7 +95,11 @@ function apiMessageFromBody(body, fallback) {
|
|
|
95
95
|
if (typeof o.error === "string" && o.error.length > 0) {
|
|
96
96
|
const detail = typeof o.message === "string" ? o.message : "";
|
|
97
97
|
const adds = detail.length > 0 && !o.error.includes(detail) && !detail.includes(o.error);
|
|
98
|
-
|
|
98
|
+
let headline = adds ? `${o.error}: ${detail}` : o.error;
|
|
99
|
+
const hint = typeof o.hint === "string" ? o.hint.trim() : "";
|
|
100
|
+
if (hint.length > 0 && !headline.includes(hint)) headline = `${headline}${/[.!?]$/.test(headline) ? "" : "."} ${hint}`;
|
|
101
|
+
const code2 = typeof o.code === "string" ? o.code.trim() : "";
|
|
102
|
+
if (code2.length > 0 && !headline.includes(code2)) headline = `${headline} (code: ${code2})`;
|
|
99
103
|
const quota = quotaDetail(o);
|
|
100
104
|
return quota ? `${headline} (${quota})` : headline;
|
|
101
105
|
}
|
|
@@ -155,6 +159,14 @@ var StudioClient = class {
|
|
|
155
159
|
return this.json("GET", "/api/v1/projects");
|
|
156
160
|
}
|
|
157
161
|
// ── Generation jobs ──
|
|
162
|
+
/**
|
|
163
|
+
* One image job. `brandSlug` is optional: without it the image is generated
|
|
164
|
+
* with no brand. A `brandSlug` or `characterSlug` the key's company cannot
|
|
165
|
+
* use is refused with 404 `input_not_found` before anything is spent (since
|
|
166
|
+
* 2026-10-11; it used to generate without the brand). `projectId` chooses the
|
|
167
|
+
* project; without it the job lands in the project named after the brand, or
|
|
168
|
+
* in `default`.
|
|
169
|
+
*/
|
|
158
170
|
generate(body) {
|
|
159
171
|
return this.json("POST", "/api/generate", { json: body });
|
|
160
172
|
}
|
|
@@ -443,7 +455,124 @@ function studio3dCreditsToUsd(credits) {
|
|
|
443
455
|
return Math.round(credits * STUDIO3D_USD_PER_CREDIT * 1e3) / 1e3;
|
|
444
456
|
}
|
|
445
457
|
var FAL_MESHY_RIG_USD = 0.2;
|
|
458
|
+
var veo = (tier, label) => {
|
|
459
|
+
const slug2 = tier ? `veo-3-1-${tier}` : "veo-3-1";
|
|
460
|
+
return {
|
|
461
|
+
kind: "video",
|
|
462
|
+
id: `route/${slug2}`,
|
|
463
|
+
label: `${label} (KIE, fal fallback)`,
|
|
464
|
+
primary: `kie/${slug2}`,
|
|
465
|
+
fallback: { start: `fal/${slug2}-image-to-video`, startAndEnd: `fal/${slug2}-first-last-frame` }
|
|
466
|
+
};
|
|
467
|
+
};
|
|
468
|
+
var image = (slug2, label, defaultTier) => ({
|
|
469
|
+
kind: "image",
|
|
470
|
+
id: `route/${slug2}`,
|
|
471
|
+
label: `${label} (KIE, fal fallback)`,
|
|
472
|
+
primary: `kie/${slug2}`,
|
|
473
|
+
fallback: `fal/${slug2}`,
|
|
474
|
+
defaultTier
|
|
475
|
+
});
|
|
476
|
+
var ROUTES = [
|
|
477
|
+
veo("lite", "Veo 3.1 Lite"),
|
|
478
|
+
veo("fast", "Veo 3.1 Fast"),
|
|
479
|
+
veo("", "Veo 3.1"),
|
|
480
|
+
{
|
|
481
|
+
kind: "video",
|
|
482
|
+
id: "route/grok-imagine",
|
|
483
|
+
label: "Grok Imagine (KIE, fal fallback)",
|
|
484
|
+
primary: "kie/grok-imagine-image-to-video",
|
|
485
|
+
fallback: { start: "fal/grok-imagine-image-to-video" }
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
kind: "video",
|
|
489
|
+
id: "route/grok-imagine-1-5",
|
|
490
|
+
label: "Grok Imagine 1.5 (KIE, fal fallback)",
|
|
491
|
+
primary: "kie/grok-imagine-1-5-image-to-video",
|
|
492
|
+
fallback: { start: "fal/grok-imagine-1-5-image-to-video" }
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
kind: "video",
|
|
496
|
+
id: "route/kling-2-5-turbo-pro",
|
|
497
|
+
label: "Kling 2.5 Turbo Pro (KIE, fal fallback)",
|
|
498
|
+
primary: "kie/kling-v2-5-turbo-pro-image-to-video",
|
|
499
|
+
fallback: { start: "fal/kling-v2-5-image-to-video" },
|
|
500
|
+
// fal needs an aspect ratio (16:9, 9:16 or 1:1) and KIE takes none, so a
|
|
501
|
+
// fallback would crop a start frame of another shape. The ratio comes from
|
|
502
|
+
// the start frame once that is built (decision 7 of the plan).
|
|
503
|
+
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."
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
kind: "video",
|
|
507
|
+
id: "route/kling-2-6",
|
|
508
|
+
label: "Kling 2.6 (KIE, fal fallback)",
|
|
509
|
+
primary: "kie/kling-2-6-image-to-video",
|
|
510
|
+
fallback: { start: "fal/kling-2-6-image-to-video" }
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
kind: "video",
|
|
514
|
+
id: "route/kling-o3",
|
|
515
|
+
label: "Kling O3 (KIE, fal fallback)",
|
|
516
|
+
primary: "kie/kling-o3-image-to-video",
|
|
517
|
+
// fal sells Kling O3 as tiers and takes no resolution. Its schema (read
|
|
518
|
+
// 2026-10-10) declares length, audio and the frames but states no output
|
|
519
|
+
// size, so Standard for 720p and Pro for 1080p follows Kling's own tier
|
|
520
|
+
// naming and is still to be confirmed by measuring a rendered clip.
|
|
521
|
+
fallback: {
|
|
522
|
+
byResolution: {
|
|
523
|
+
"720p": "fal/kling-o3-standard-image-to-video",
|
|
524
|
+
"1080p": "fal/kling-o3-pro-image-to-video"
|
|
525
|
+
},
|
|
526
|
+
label: "fal renders Kling O3 at 720p (Standard) or 1080p (Pro)"
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
kind: "video",
|
|
531
|
+
id: "route/seedance-2-0",
|
|
532
|
+
label: "Seedance 2.0 (KIE, fal fallback)",
|
|
533
|
+
primary: "kie/seedance-2-0-image-to-video",
|
|
534
|
+
fallback: { start: "fal/seedance-2-0-image-to-video" }
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
kind: "video",
|
|
538
|
+
id: "route/seedance-2-5",
|
|
539
|
+
label: "Seedance 2.5 (KIE, fal fallback)",
|
|
540
|
+
primary: "kie/seedance-2-5-image-to-video",
|
|
541
|
+
fallback: { start: "fal/seedance-2-5-image-to-video" }
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
kind: "video",
|
|
545
|
+
id: "route/wan-3-0",
|
|
546
|
+
label: "Wan 3.0 (KIE, fal fallback)",
|
|
547
|
+
primary: "kie/wan-3-0-image-to-video",
|
|
548
|
+
fallback: { start: "fal/wan-3-0-image-to-video" }
|
|
549
|
+
},
|
|
550
|
+
image("nano-banana-2", "Nano Banana 2", "1K"),
|
|
551
|
+
image("nano-banana-2-edit", "Nano Banana 2 Edit", "1K"),
|
|
552
|
+
image("nano-banana-pro", "Nano Banana Pro", "1K"),
|
|
553
|
+
image("nano-banana-pro-edit", "Nano Banana Pro Edit", "1K"),
|
|
554
|
+
image("seedream-5-lite", "Seedream 5 Lite", "2K"),
|
|
555
|
+
image("seedream-5-lite-edit", "Seedream 5 Lite Edit", "2K"),
|
|
556
|
+
image("seedream-5-pro", "Seedream 5 Pro", "1K"),
|
|
557
|
+
image("seedream-5-pro-edit", "Seedream 5 Pro Edit", "1K")
|
|
558
|
+
];
|
|
559
|
+
var MODEL_ROUTES = Object.fromEntries(ROUTES.map((r) => [r.id, r]));
|
|
560
|
+
function getRoute(modelId) {
|
|
561
|
+
return modelId ? MODEL_ROUTES[modelId] : void 0;
|
|
562
|
+
}
|
|
446
563
|
var MODEL_CATALOG = [
|
|
564
|
+
// LIST PRICES (`listPrice`, pricing plan of 2026-10-10). Every KIE entry
|
|
565
|
+
// names where the official list price of its requests is found, because KIE
|
|
566
|
+
// is the reseller route that sells below list and the price floor needs the
|
|
567
|
+
// figure. The rule: the model owner's own published price where the catalog
|
|
568
|
+
// records one (Google's Veo price, billed unchanged by the OpenRouter
|
|
569
|
+
// entries; Vertex AI's Imagen 4 Fast price), else the fal entry for the same
|
|
570
|
+
// upstream model. `unrecorded` means neither exists in this catalog, and the
|
|
571
|
+
// floor then does not apply: Seedream V4, MiniMax H3 and Gemini Omni Flash
|
|
572
|
+
// have no fal entry, Kling O3 on KIE does not say whether it is fal's
|
|
573
|
+
// Standard or Pro tier, Suno has no per-song list price, and the Claude
|
|
574
|
+
// entries bill per token. Entries of every other provider carry no
|
|
575
|
+
// reference: their route bills the list price itself.
|
|
447
576
|
// ── 3D AI Studio: 3D generation ────────────────────────────────────────
|
|
448
577
|
// `credits` is what the model's DEFAULT request bills (the options its
|
|
449
578
|
// builder sends when a node sets none), priced by providers/studio3d/
|
|
@@ -540,6 +669,7 @@ var MODEL_CATALOG = [
|
|
|
540
669
|
label: "Nano Banana 2 (KIE)",
|
|
541
670
|
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.",
|
|
542
671
|
cost: { usd: 0.04, usdByResolution: { "1K": 0.04, "2K": 0.06, "4K": 0.09 } },
|
|
672
|
+
listPrice: { source: "fal", model: "fal/nano-banana-2" },
|
|
543
673
|
capabilities: { resolutions: ["1K", "2K", "4K"] }
|
|
544
674
|
},
|
|
545
675
|
{
|
|
@@ -552,6 +682,7 @@ var MODEL_CATALOG = [
|
|
|
552
682
|
label: "Imagen 4 Fast (KIE)",
|
|
553
683
|
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.",
|
|
554
684
|
cost: { usd: 0.02 },
|
|
685
|
+
listPrice: { source: "owner", usd: 0.02 },
|
|
555
686
|
capabilities: { resolutions: [] }
|
|
556
687
|
},
|
|
557
688
|
{
|
|
@@ -561,11 +692,15 @@ var MODEL_CATALOG = [
|
|
|
561
692
|
task: "text-to-image",
|
|
562
693
|
label: "Seedream V4 (KIE)",
|
|
563
694
|
description: "ByteDance Seedream V4 via KIE. Stylized output not available on Vertex. No IP indemnity.",
|
|
564
|
-
cost: { usd: 0.0175 }
|
|
695
|
+
cost: { usd: 0.0175 },
|
|
696
|
+
listPrice: { source: "unrecorded" }
|
|
565
697
|
},
|
|
566
698
|
// ── KIE.ai: newer image models, the cheaper route (price feed 2026-09-23) ─
|
|
567
699
|
// Billed from the credits KIE reports on each task. KIE offers no data
|
|
568
|
-
// processing agreement
|
|
700
|
+
// processing agreement. Decided 2026-09-23 (Q5 C of the routing plan,
|
|
701
|
+
// docs/plans/2026-09-26-kie-first-fal-fallback.md): the automatic routes try
|
|
702
|
+
// KIE first for everything, a recorded risk; pinning the fal entry of a model
|
|
703
|
+
// is how a job with a real person's photo is kept off KIE.
|
|
569
704
|
{
|
|
570
705
|
id: "kie/nano-banana-2-edit",
|
|
571
706
|
provider: "kie",
|
|
@@ -574,6 +709,7 @@ var MODEL_CATALOG = [
|
|
|
574
709
|
label: "Nano Banana 2 Edit (KIE)",
|
|
575
710
|
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.",
|
|
576
711
|
cost: { usd: 0.04, usdByResolution: { "1K": 0.04, "2K": 0.06, "4K": 0.09 } },
|
|
712
|
+
listPrice: { source: "fal", model: "fal/nano-banana-2-edit" },
|
|
577
713
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
578
714
|
},
|
|
579
715
|
{
|
|
@@ -584,6 +720,7 @@ var MODEL_CATALOG = [
|
|
|
584
720
|
label: "Nano Banana Pro (KIE)",
|
|
585
721
|
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).",
|
|
586
722
|
cost: { usd: 0.09, usdByResolution: { "1K": 0.09, "2K": 0.09, "4K": 0.12 } },
|
|
723
|
+
listPrice: { source: "fal", model: "fal/nano-banana-pro" },
|
|
587
724
|
capabilities: { resolutions: ["1K", "2K", "4K"], maxInputImages: 0 }
|
|
588
725
|
},
|
|
589
726
|
{
|
|
@@ -594,6 +731,7 @@ var MODEL_CATALOG = [
|
|
|
594
731
|
label: "Nano Banana Pro Edit (KIE)",
|
|
595
732
|
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).",
|
|
596
733
|
cost: { usd: 0.09, usdByResolution: { "1K": 0.09, "2K": 0.09, "4K": 0.12 } },
|
|
734
|
+
listPrice: { source: "fal", model: "fal/nano-banana-pro-edit" },
|
|
597
735
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
598
736
|
},
|
|
599
737
|
{
|
|
@@ -604,6 +742,7 @@ var MODEL_CATALOG = [
|
|
|
604
742
|
label: "Seedream 5 Lite (KIE)",
|
|
605
743
|
description: "ByteDance Seedream 5 Lite via KIE, 2K or 4K. $0.0275 per image (fal: $0.035).",
|
|
606
744
|
cost: { usd: 0.0275 },
|
|
745
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite" },
|
|
607
746
|
capabilities: { resolutions: ["2K", "4K"], maxInputImages: 0 }
|
|
608
747
|
},
|
|
609
748
|
{
|
|
@@ -614,6 +753,7 @@ var MODEL_CATALOG = [
|
|
|
614
753
|
label: "Seedream 5 Lite Edit (KIE)",
|
|
615
754
|
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).",
|
|
616
755
|
cost: { usd: 0.0275 },
|
|
756
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite-edit" },
|
|
617
757
|
capabilities: { resolutions: ["2K", "4K"], needsImageInput: true, maxInputImages: 10 }
|
|
618
758
|
},
|
|
619
759
|
{
|
|
@@ -624,6 +764,7 @@ var MODEL_CATALOG = [
|
|
|
624
764
|
label: "Seedream 5 Pro (KIE)",
|
|
625
765
|
description: "ByteDance Seedream 5 Pro via KIE, 1K or 2K. $0.035 at 1K, $0.07 at 2K (fal: $0.0675 to $0.135).",
|
|
626
766
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
767
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro" },
|
|
627
768
|
capabilities: { resolutions: ["1K", "2K"], maxInputImages: 0 }
|
|
628
769
|
},
|
|
629
770
|
{
|
|
@@ -634,6 +775,7 @@ var MODEL_CATALOG = [
|
|
|
634
775
|
label: "Seedream 5 Pro Edit (KIE)",
|
|
635
776
|
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.",
|
|
636
777
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
778
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro-edit" },
|
|
637
779
|
capabilities: { resolutions: ["1K", "2K"], needsImageInput: true, maxInputImages: 10 }
|
|
638
780
|
},
|
|
639
781
|
// ── fal.ai: image gen ─────────────────────────────────────────────────
|
|
@@ -928,8 +1070,11 @@ var MODEL_CATALOG = [
|
|
|
928
1070
|
// ── KIE.ai video: the cheaper route to the same upstream models ──────────
|
|
929
1071
|
// Async via KIE's Veo and createTask APIs (providers/kie-video.ts). 20 to 85%
|
|
930
1072
|
// below fal (KIE price feed, 2026-09-23). KIE offers no data processing
|
|
931
|
-
// agreement
|
|
932
|
-
//
|
|
1073
|
+
// agreement. Decided 2026-09-23 (Q5 C of the routing plan): the automatic
|
|
1074
|
+
// routes try KIE first for everything, a recorded risk; pinning the fal
|
|
1075
|
+
// entry of a model is how a job with a real person's photo is kept off KIE.
|
|
1076
|
+
// Anchors are the model's default clip at 720p (Kling: its fixed size). KIE
|
|
1077
|
+
// docs vetted 2026-09-23.
|
|
933
1078
|
{
|
|
934
1079
|
id: "kie/veo-3-1-lite",
|
|
935
1080
|
provider: "kie",
|
|
@@ -938,6 +1083,7 @@ var MODEL_CATALOG = [
|
|
|
938
1083
|
label: "Veo 3.1 Lite (KIE)",
|
|
939
1084
|
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.",
|
|
940
1085
|
cost: { usd: 0.15 },
|
|
1086
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-lite" },
|
|
941
1087
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
942
1088
|
},
|
|
943
1089
|
{
|
|
@@ -948,6 +1094,7 @@ var MODEL_CATALOG = [
|
|
|
948
1094
|
label: "Veo 3.1 Fast (KIE)",
|
|
949
1095
|
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.",
|
|
950
1096
|
cost: { usd: 0.3 },
|
|
1097
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-fast" },
|
|
951
1098
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
952
1099
|
},
|
|
953
1100
|
{
|
|
@@ -958,6 +1105,7 @@ var MODEL_CATALOG = [
|
|
|
958
1105
|
label: "Veo 3.1 (KIE)",
|
|
959
1106
|
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).",
|
|
960
1107
|
cost: { usd: 1.25 },
|
|
1108
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1" },
|
|
961
1109
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
962
1110
|
},
|
|
963
1111
|
// ── OpenRouter video: Google Veo 3.1 at Google's own prices ───────────────
|
|
@@ -1004,6 +1152,7 @@ var MODEL_CATALOG = [
|
|
|
1004
1152
|
label: "Grok Imagine (KIE)",
|
|
1005
1153
|
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.",
|
|
1006
1154
|
cost: { usd: 0.135 },
|
|
1155
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-image-to-video" },
|
|
1007
1156
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
1008
1157
|
},
|
|
1009
1158
|
{
|
|
@@ -1014,6 +1163,7 @@ var MODEL_CATALOG = [
|
|
|
1014
1163
|
label: "Grok Imagine 1.5 (KIE, preview)",
|
|
1015
1164
|
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.",
|
|
1016
1165
|
cost: { usd: 0.18 },
|
|
1166
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-1-5-image-to-video" },
|
|
1017
1167
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
1018
1168
|
},
|
|
1019
1169
|
{
|
|
@@ -1024,6 +1174,7 @@ var MODEL_CATALOG = [
|
|
|
1024
1174
|
label: "Kling 2.5 Turbo Pro (KIE)",
|
|
1025
1175
|
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).",
|
|
1026
1176
|
cost: { usd: 0.21 },
|
|
1177
|
+
listPrice: { source: "fal", model: "fal/kling-v2-5-image-to-video" },
|
|
1027
1178
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1028
1179
|
},
|
|
1029
1180
|
{
|
|
@@ -1034,6 +1185,7 @@ var MODEL_CATALOG = [
|
|
|
1034
1185
|
label: "Kling 2.6 (KIE)",
|
|
1035
1186
|
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).",
|
|
1036
1187
|
cost: { usd: 0.275 },
|
|
1188
|
+
listPrice: { source: "fal", model: "fal/kling-2-6-image-to-video" },
|
|
1037
1189
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
1038
1190
|
},
|
|
1039
1191
|
{
|
|
@@ -1045,6 +1197,7 @@ var MODEL_CATALOG = [
|
|
|
1045
1197
|
refusesPhotorealPeople: true,
|
|
1046
1198
|
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).",
|
|
1047
1199
|
cost: { usd: 1.025 },
|
|
1200
|
+
listPrice: { source: "fal", model: "fal/seedance-2-0-image-to-video" },
|
|
1048
1201
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1049
1202
|
},
|
|
1050
1203
|
{
|
|
@@ -1056,6 +1209,7 @@ var MODEL_CATALOG = [
|
|
|
1056
1209
|
refusesPhotorealPeople: true,
|
|
1057
1210
|
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.",
|
|
1058
1211
|
cost: { usd: 1.575 },
|
|
1212
|
+
listPrice: { source: "fal", model: "fal/seedance-2-5-image-to-video" },
|
|
1059
1213
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1060
1214
|
},
|
|
1061
1215
|
{
|
|
@@ -1066,6 +1220,7 @@ var MODEL_CATALOG = [
|
|
|
1066
1220
|
label: "Wan 3.0 (KIE)",
|
|
1067
1221
|
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).",
|
|
1068
1222
|
cost: { usd: 0.4 },
|
|
1223
|
+
listPrice: { source: "fal", model: "fal/wan-3-0-image-to-video" },
|
|
1069
1224
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1070
1225
|
},
|
|
1071
1226
|
{
|
|
@@ -1076,6 +1231,7 @@ var MODEL_CATALOG = [
|
|
|
1076
1231
|
label: "MiniMax H3 (KIE)",
|
|
1077
1232
|
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.",
|
|
1078
1233
|
cost: { usd: 0.24 },
|
|
1234
|
+
listPrice: { source: "unrecorded" },
|
|
1079
1235
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1080
1236
|
},
|
|
1081
1237
|
{
|
|
@@ -1086,6 +1242,7 @@ var MODEL_CATALOG = [
|
|
|
1086
1242
|
label: "Gemini Omni 1.1 Flash (KIE)",
|
|
1087
1243
|
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.",
|
|
1088
1244
|
cost: { usd: 0.525 },
|
|
1245
|
+
listPrice: { source: "unrecorded" },
|
|
1089
1246
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1090
1247
|
},
|
|
1091
1248
|
{
|
|
@@ -1096,6 +1253,7 @@ var MODEL_CATALOG = [
|
|
|
1096
1253
|
label: "Kling O3 (KIE)",
|
|
1097
1254
|
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.",
|
|
1098
1255
|
cost: { usd: 0.35 },
|
|
1256
|
+
listPrice: { source: "unrecorded" },
|
|
1099
1257
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1100
1258
|
},
|
|
1101
1259
|
{
|
|
@@ -1414,6 +1572,7 @@ var MODEL_CATALOG = [
|
|
|
1414
1572
|
label: "Suno V5 (KIE)",
|
|
1415
1573
|
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.",
|
|
1416
1574
|
cost: { usd: 0.04 },
|
|
1575
|
+
listPrice: { source: "unrecorded" },
|
|
1417
1576
|
isDefault: true
|
|
1418
1577
|
},
|
|
1419
1578
|
// ── audio: voice via ElevenLabs (B2) ───────────────────────────────────
|
|
@@ -1462,6 +1621,7 @@ var MODEL_CATALOG = [
|
|
|
1462
1621
|
label: "Claude Sonnet 4.5 (KIE)",
|
|
1463
1622
|
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.",
|
|
1464
1623
|
cost: { usd: 0.01 },
|
|
1624
|
+
listPrice: { source: "unrecorded" },
|
|
1465
1625
|
isDefault: true
|
|
1466
1626
|
},
|
|
1467
1627
|
{
|
|
@@ -1471,7 +1631,8 @@ var MODEL_CATALOG = [
|
|
|
1471
1631
|
task: "text-generation",
|
|
1472
1632
|
label: "Claude Opus 4.5 (KIE)",
|
|
1473
1633
|
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.",
|
|
1474
|
-
cost: { usd: 0.05 }
|
|
1634
|
+
cost: { usd: 0.05 },
|
|
1635
|
+
listPrice: { source: "unrecorded" }
|
|
1475
1636
|
},
|
|
1476
1637
|
{
|
|
1477
1638
|
id: "openrouter/claude-sonnet-4-6",
|
|
@@ -1536,8 +1697,26 @@ var MODEL_CATALOG = [
|
|
|
1536
1697
|
description: "Vertex AI direct, newest flash variant. Global region. Preview status."
|
|
1537
1698
|
}
|
|
1538
1699
|
];
|
|
1700
|
+
var routeEntries = /* @__PURE__ */ new Map();
|
|
1701
|
+
function routeEntry(id) {
|
|
1702
|
+
const cached = routeEntries.get(id);
|
|
1703
|
+
if (cached) return cached;
|
|
1704
|
+
const route = getRoute(id);
|
|
1705
|
+
const primary = route ? MODEL_CATALOG.find((m) => m.id === route.primary) : void 0;
|
|
1706
|
+
if (!route || !primary) return void 0;
|
|
1707
|
+
const entry = {
|
|
1708
|
+
...primary,
|
|
1709
|
+
// A route is never the default of its task (that is slice 3's switch).
|
|
1710
|
+
isDefault: void 0,
|
|
1711
|
+
id: route.id,
|
|
1712
|
+
label: route.label,
|
|
1713
|
+
description: `Runs on KIE first and, when KIE fails on its side, again on fal with the same settings. ${primary.description ?? ""}`.trim()
|
|
1714
|
+
};
|
|
1715
|
+
routeEntries.set(id, entry);
|
|
1716
|
+
return entry;
|
|
1717
|
+
}
|
|
1539
1718
|
function tryGetModel(id) {
|
|
1540
|
-
return MODEL_CATALOG.find((m) => m.id === id);
|
|
1719
|
+
return MODEL_CATALOG.find((m) => m.id === id) ?? routeEntry(id);
|
|
1541
1720
|
}
|
|
1542
1721
|
var ROWS = [
|
|
1543
1722
|
[0, "Idle", "DailyActions", "Idle"],
|
|
@@ -2465,7 +2644,10 @@ var VIDEO_UNSUPPORTED_PARAMS = {
|
|
|
2465
2644
|
...Object.fromEntries(KLING_O3_I2V_ENDPOINTS.map((e) => [e, ["resolution", "aspectRatio", "seed", "negativePrompt"]])),
|
|
2466
2645
|
...Object.fromEntries(LTX_25_I2V_ENDPOINTS.map((e) => [e, ["seed", "negativePrompt"]]))
|
|
2467
2646
|
};
|
|
2468
|
-
var perSecond = (rates) => (s, r) =>
|
|
2647
|
+
var perSecond = (rates) => (s, r) => {
|
|
2648
|
+
const rate = rates[r];
|
|
2649
|
+
return rate === void 0 ? void 0 : rate * s;
|
|
2650
|
+
};
|
|
2469
2651
|
function veoSpec(model, label, perClip) {
|
|
2470
2652
|
return {
|
|
2471
2653
|
family: "veo",
|
|
@@ -2479,7 +2661,7 @@ function veoSpec(model, label, perClip) {
|
|
|
2479
2661
|
audioSwitch: false,
|
|
2480
2662
|
defaultAudio: true,
|
|
2481
2663
|
// KIE prices Veo per clip whatever its length, and always renders audio.
|
|
2482
|
-
price: (_s, r) => perClip[r]
|
|
2664
|
+
price: (_s, r) => perClip[r],
|
|
2483
2665
|
build: (i) => ({
|
|
2484
2666
|
prompt: i.prompt,
|
|
2485
2667
|
model,
|
|
@@ -2686,7 +2868,7 @@ var KIE_VIDEO_SPECS = {
|
|
|
2686
2868
|
audioSwitch: false,
|
|
2687
2869
|
defaultAudio: false,
|
|
2688
2870
|
// Priced per clip by length; 4K carries a surcharge.
|
|
2689
|
-
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]
|
|
2871
|
+
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],
|
|
2690
2872
|
// The first-and-last-frame mode: KIE refuses it combined with registered
|
|
2691
2873
|
// characters, reference images, audio or video, so none are sent.
|
|
2692
2874
|
build: (i) => ({
|
|
@@ -2712,7 +2894,7 @@ var KIE_VIDEO_SPECS = {
|
|
|
2712
2894
|
audioSwitch: true,
|
|
2713
2895
|
defaultAudio: false,
|
|
2714
2896
|
// With-audio rates from KIE's price feed, the higher of its two listings.
|
|
2715
|
-
price: (s, r, audio) => (
|
|
2897
|
+
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),
|
|
2716
2898
|
build: (i) => ({
|
|
2717
2899
|
prompt: i.prompt,
|
|
2718
2900
|
image_urls: i.end ? [i.start, i.end] : [i.start],
|
|
@@ -3154,7 +3336,124 @@ function imageResolutionTier(value) {
|
|
|
3154
3336
|
return null;
|
|
3155
3337
|
}
|
|
3156
3338
|
}
|
|
3339
|
+
var veo2 = (tier, label) => {
|
|
3340
|
+
const slug2 = tier ? `veo-3-1-${tier}` : "veo-3-1";
|
|
3341
|
+
return {
|
|
3342
|
+
kind: "video",
|
|
3343
|
+
id: `route/${slug2}`,
|
|
3344
|
+
label: `${label} (KIE, fal fallback)`,
|
|
3345
|
+
primary: `kie/${slug2}`,
|
|
3346
|
+
fallback: { start: `fal/${slug2}-image-to-video`, startAndEnd: `fal/${slug2}-first-last-frame` }
|
|
3347
|
+
};
|
|
3348
|
+
};
|
|
3349
|
+
var image2 = (slug2, label, defaultTier) => ({
|
|
3350
|
+
kind: "image",
|
|
3351
|
+
id: `route/${slug2}`,
|
|
3352
|
+
label: `${label} (KIE, fal fallback)`,
|
|
3353
|
+
primary: `kie/${slug2}`,
|
|
3354
|
+
fallback: `fal/${slug2}`,
|
|
3355
|
+
defaultTier
|
|
3356
|
+
});
|
|
3357
|
+
var ROUTES2 = [
|
|
3358
|
+
veo2("lite", "Veo 3.1 Lite"),
|
|
3359
|
+
veo2("fast", "Veo 3.1 Fast"),
|
|
3360
|
+
veo2("", "Veo 3.1"),
|
|
3361
|
+
{
|
|
3362
|
+
kind: "video",
|
|
3363
|
+
id: "route/grok-imagine",
|
|
3364
|
+
label: "Grok Imagine (KIE, fal fallback)",
|
|
3365
|
+
primary: "kie/grok-imagine-image-to-video",
|
|
3366
|
+
fallback: { start: "fal/grok-imagine-image-to-video" }
|
|
3367
|
+
},
|
|
3368
|
+
{
|
|
3369
|
+
kind: "video",
|
|
3370
|
+
id: "route/grok-imagine-1-5",
|
|
3371
|
+
label: "Grok Imagine 1.5 (KIE, fal fallback)",
|
|
3372
|
+
primary: "kie/grok-imagine-1-5-image-to-video",
|
|
3373
|
+
fallback: { start: "fal/grok-imagine-1-5-image-to-video" }
|
|
3374
|
+
},
|
|
3375
|
+
{
|
|
3376
|
+
kind: "video",
|
|
3377
|
+
id: "route/kling-2-5-turbo-pro",
|
|
3378
|
+
label: "Kling 2.5 Turbo Pro (KIE, fal fallback)",
|
|
3379
|
+
primary: "kie/kling-v2-5-turbo-pro-image-to-video",
|
|
3380
|
+
fallback: { start: "fal/kling-v2-5-image-to-video" },
|
|
3381
|
+
// fal needs an aspect ratio (16:9, 9:16 or 1:1) and KIE takes none, so a
|
|
3382
|
+
// fallback would crop a start frame of another shape. The ratio comes from
|
|
3383
|
+
// the start frame once that is built (decision 7 of the plan).
|
|
3384
|
+
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."
|
|
3385
|
+
},
|
|
3386
|
+
{
|
|
3387
|
+
kind: "video",
|
|
3388
|
+
id: "route/kling-2-6",
|
|
3389
|
+
label: "Kling 2.6 (KIE, fal fallback)",
|
|
3390
|
+
primary: "kie/kling-2-6-image-to-video",
|
|
3391
|
+
fallback: { start: "fal/kling-2-6-image-to-video" }
|
|
3392
|
+
},
|
|
3393
|
+
{
|
|
3394
|
+
kind: "video",
|
|
3395
|
+
id: "route/kling-o3",
|
|
3396
|
+
label: "Kling O3 (KIE, fal fallback)",
|
|
3397
|
+
primary: "kie/kling-o3-image-to-video",
|
|
3398
|
+
// fal sells Kling O3 as tiers and takes no resolution. Its schema (read
|
|
3399
|
+
// 2026-10-10) declares length, audio and the frames but states no output
|
|
3400
|
+
// size, so Standard for 720p and Pro for 1080p follows Kling's own tier
|
|
3401
|
+
// naming and is still to be confirmed by measuring a rendered clip.
|
|
3402
|
+
fallback: {
|
|
3403
|
+
byResolution: {
|
|
3404
|
+
"720p": "fal/kling-o3-standard-image-to-video",
|
|
3405
|
+
"1080p": "fal/kling-o3-pro-image-to-video"
|
|
3406
|
+
},
|
|
3407
|
+
label: "fal renders Kling O3 at 720p (Standard) or 1080p (Pro)"
|
|
3408
|
+
}
|
|
3409
|
+
},
|
|
3410
|
+
{
|
|
3411
|
+
kind: "video",
|
|
3412
|
+
id: "route/seedance-2-0",
|
|
3413
|
+
label: "Seedance 2.0 (KIE, fal fallback)",
|
|
3414
|
+
primary: "kie/seedance-2-0-image-to-video",
|
|
3415
|
+
fallback: { start: "fal/seedance-2-0-image-to-video" }
|
|
3416
|
+
},
|
|
3417
|
+
{
|
|
3418
|
+
kind: "video",
|
|
3419
|
+
id: "route/seedance-2-5",
|
|
3420
|
+
label: "Seedance 2.5 (KIE, fal fallback)",
|
|
3421
|
+
primary: "kie/seedance-2-5-image-to-video",
|
|
3422
|
+
fallback: { start: "fal/seedance-2-5-image-to-video" }
|
|
3423
|
+
},
|
|
3424
|
+
{
|
|
3425
|
+
kind: "video",
|
|
3426
|
+
id: "route/wan-3-0",
|
|
3427
|
+
label: "Wan 3.0 (KIE, fal fallback)",
|
|
3428
|
+
primary: "kie/wan-3-0-image-to-video",
|
|
3429
|
+
fallback: { start: "fal/wan-3-0-image-to-video" }
|
|
3430
|
+
},
|
|
3431
|
+
image2("nano-banana-2", "Nano Banana 2", "1K"),
|
|
3432
|
+
image2("nano-banana-2-edit", "Nano Banana 2 Edit", "1K"),
|
|
3433
|
+
image2("nano-banana-pro", "Nano Banana Pro", "1K"),
|
|
3434
|
+
image2("nano-banana-pro-edit", "Nano Banana Pro Edit", "1K"),
|
|
3435
|
+
image2("seedream-5-lite", "Seedream 5 Lite", "2K"),
|
|
3436
|
+
image2("seedream-5-lite-edit", "Seedream 5 Lite Edit", "2K"),
|
|
3437
|
+
image2("seedream-5-pro", "Seedream 5 Pro", "1K"),
|
|
3438
|
+
image2("seedream-5-pro-edit", "Seedream 5 Pro Edit", "1K")
|
|
3439
|
+
];
|
|
3440
|
+
var MODEL_ROUTES2 = Object.fromEntries(ROUTES2.map((r) => [r.id, r]));
|
|
3441
|
+
function getRoute2(modelId) {
|
|
3442
|
+
return modelId ? MODEL_ROUTES2[modelId] : void 0;
|
|
3443
|
+
}
|
|
3157
3444
|
var MODEL_CATALOG2 = [
|
|
3445
|
+
// LIST PRICES (`listPrice`, pricing plan of 2026-10-10). Every KIE entry
|
|
3446
|
+
// names where the official list price of its requests is found, because KIE
|
|
3447
|
+
// is the reseller route that sells below list and the price floor needs the
|
|
3448
|
+
// figure. The rule: the model owner's own published price where the catalog
|
|
3449
|
+
// records one (Google's Veo price, billed unchanged by the OpenRouter
|
|
3450
|
+
// entries; Vertex AI's Imagen 4 Fast price), else the fal entry for the same
|
|
3451
|
+
// upstream model. `unrecorded` means neither exists in this catalog, and the
|
|
3452
|
+
// floor then does not apply: Seedream V4, MiniMax H3 and Gemini Omni Flash
|
|
3453
|
+
// have no fal entry, Kling O3 on KIE does not say whether it is fal's
|
|
3454
|
+
// Standard or Pro tier, Suno has no per-song list price, and the Claude
|
|
3455
|
+
// entries bill per token. Entries of every other provider carry no
|
|
3456
|
+
// reference: their route bills the list price itself.
|
|
3158
3457
|
// ── 3D AI Studio: 3D generation ────────────────────────────────────────
|
|
3159
3458
|
// `credits` is what the model's DEFAULT request bills (the options its
|
|
3160
3459
|
// builder sends when a node sets none), priced by providers/studio3d/
|
|
@@ -3251,6 +3550,7 @@ var MODEL_CATALOG2 = [
|
|
|
3251
3550
|
label: "Nano Banana 2 (KIE)",
|
|
3252
3551
|
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.",
|
|
3253
3552
|
cost: { usd: 0.04, usdByResolution: { "1K": 0.04, "2K": 0.06, "4K": 0.09 } },
|
|
3553
|
+
listPrice: { source: "fal", model: "fal/nano-banana-2" },
|
|
3254
3554
|
capabilities: { resolutions: ["1K", "2K", "4K"] }
|
|
3255
3555
|
},
|
|
3256
3556
|
{
|
|
@@ -3263,6 +3563,7 @@ var MODEL_CATALOG2 = [
|
|
|
3263
3563
|
label: "Imagen 4 Fast (KIE)",
|
|
3264
3564
|
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.",
|
|
3265
3565
|
cost: { usd: 0.02 },
|
|
3566
|
+
listPrice: { source: "owner", usd: 0.02 },
|
|
3266
3567
|
capabilities: { resolutions: [] }
|
|
3267
3568
|
},
|
|
3268
3569
|
{
|
|
@@ -3272,11 +3573,15 @@ var MODEL_CATALOG2 = [
|
|
|
3272
3573
|
task: "text-to-image",
|
|
3273
3574
|
label: "Seedream V4 (KIE)",
|
|
3274
3575
|
description: "ByteDance Seedream V4 via KIE. Stylized output not available on Vertex. No IP indemnity.",
|
|
3275
|
-
cost: { usd: 0.0175 }
|
|
3576
|
+
cost: { usd: 0.0175 },
|
|
3577
|
+
listPrice: { source: "unrecorded" }
|
|
3276
3578
|
},
|
|
3277
3579
|
// ── KIE.ai: newer image models, the cheaper route (price feed 2026-09-23) ─
|
|
3278
3580
|
// Billed from the credits KIE reports on each task. KIE offers no data
|
|
3279
|
-
// processing agreement
|
|
3581
|
+
// processing agreement. Decided 2026-09-23 (Q5 C of the routing plan,
|
|
3582
|
+
// docs/plans/2026-09-26-kie-first-fal-fallback.md): the automatic routes try
|
|
3583
|
+
// KIE first for everything, a recorded risk; pinning the fal entry of a model
|
|
3584
|
+
// is how a job with a real person's photo is kept off KIE.
|
|
3280
3585
|
{
|
|
3281
3586
|
id: "kie/nano-banana-2-edit",
|
|
3282
3587
|
provider: "kie",
|
|
@@ -3285,6 +3590,7 @@ var MODEL_CATALOG2 = [
|
|
|
3285
3590
|
label: "Nano Banana 2 Edit (KIE)",
|
|
3286
3591
|
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.",
|
|
3287
3592
|
cost: { usd: 0.04, usdByResolution: { "1K": 0.04, "2K": 0.06, "4K": 0.09 } },
|
|
3593
|
+
listPrice: { source: "fal", model: "fal/nano-banana-2-edit" },
|
|
3288
3594
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
3289
3595
|
},
|
|
3290
3596
|
{
|
|
@@ -3295,6 +3601,7 @@ var MODEL_CATALOG2 = [
|
|
|
3295
3601
|
label: "Nano Banana Pro (KIE)",
|
|
3296
3602
|
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).",
|
|
3297
3603
|
cost: { usd: 0.09, usdByResolution: { "1K": 0.09, "2K": 0.09, "4K": 0.12 } },
|
|
3604
|
+
listPrice: { source: "fal", model: "fal/nano-banana-pro" },
|
|
3298
3605
|
capabilities: { resolutions: ["1K", "2K", "4K"], maxInputImages: 0 }
|
|
3299
3606
|
},
|
|
3300
3607
|
{
|
|
@@ -3305,6 +3612,7 @@ var MODEL_CATALOG2 = [
|
|
|
3305
3612
|
label: "Nano Banana Pro Edit (KIE)",
|
|
3306
3613
|
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).",
|
|
3307
3614
|
cost: { usd: 0.09, usdByResolution: { "1K": 0.09, "2K": 0.09, "4K": 0.12 } },
|
|
3615
|
+
listPrice: { source: "fal", model: "fal/nano-banana-pro-edit" },
|
|
3308
3616
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
3309
3617
|
},
|
|
3310
3618
|
{
|
|
@@ -3315,6 +3623,7 @@ var MODEL_CATALOG2 = [
|
|
|
3315
3623
|
label: "Seedream 5 Lite (KIE)",
|
|
3316
3624
|
description: "ByteDance Seedream 5 Lite via KIE, 2K or 4K. $0.0275 per image (fal: $0.035).",
|
|
3317
3625
|
cost: { usd: 0.0275 },
|
|
3626
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite" },
|
|
3318
3627
|
capabilities: { resolutions: ["2K", "4K"], maxInputImages: 0 }
|
|
3319
3628
|
},
|
|
3320
3629
|
{
|
|
@@ -3325,6 +3634,7 @@ var MODEL_CATALOG2 = [
|
|
|
3325
3634
|
label: "Seedream 5 Lite Edit (KIE)",
|
|
3326
3635
|
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).",
|
|
3327
3636
|
cost: { usd: 0.0275 },
|
|
3637
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite-edit" },
|
|
3328
3638
|
capabilities: { resolutions: ["2K", "4K"], needsImageInput: true, maxInputImages: 10 }
|
|
3329
3639
|
},
|
|
3330
3640
|
{
|
|
@@ -3335,6 +3645,7 @@ var MODEL_CATALOG2 = [
|
|
|
3335
3645
|
label: "Seedream 5 Pro (KIE)",
|
|
3336
3646
|
description: "ByteDance Seedream 5 Pro via KIE, 1K or 2K. $0.035 at 1K, $0.07 at 2K (fal: $0.0675 to $0.135).",
|
|
3337
3647
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
3648
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro" },
|
|
3338
3649
|
capabilities: { resolutions: ["1K", "2K"], maxInputImages: 0 }
|
|
3339
3650
|
},
|
|
3340
3651
|
{
|
|
@@ -3345,6 +3656,7 @@ var MODEL_CATALOG2 = [
|
|
|
3345
3656
|
label: "Seedream 5 Pro Edit (KIE)",
|
|
3346
3657
|
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.",
|
|
3347
3658
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
3659
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro-edit" },
|
|
3348
3660
|
capabilities: { resolutions: ["1K", "2K"], needsImageInput: true, maxInputImages: 10 }
|
|
3349
3661
|
},
|
|
3350
3662
|
// ── fal.ai: image gen ─────────────────────────────────────────────────
|
|
@@ -3639,8 +3951,11 @@ var MODEL_CATALOG2 = [
|
|
|
3639
3951
|
// ── KIE.ai video: the cheaper route to the same upstream models ──────────
|
|
3640
3952
|
// Async via KIE's Veo and createTask APIs (providers/kie-video.ts). 20 to 85%
|
|
3641
3953
|
// below fal (KIE price feed, 2026-09-23). KIE offers no data processing
|
|
3642
|
-
// agreement
|
|
3643
|
-
//
|
|
3954
|
+
// agreement. Decided 2026-09-23 (Q5 C of the routing plan): the automatic
|
|
3955
|
+
// routes try KIE first for everything, a recorded risk; pinning the fal
|
|
3956
|
+
// entry of a model is how a job with a real person's photo is kept off KIE.
|
|
3957
|
+
// Anchors are the model's default clip at 720p (Kling: its fixed size). KIE
|
|
3958
|
+
// docs vetted 2026-09-23.
|
|
3644
3959
|
{
|
|
3645
3960
|
id: "kie/veo-3-1-lite",
|
|
3646
3961
|
provider: "kie",
|
|
@@ -3649,6 +3964,7 @@ var MODEL_CATALOG2 = [
|
|
|
3649
3964
|
label: "Veo 3.1 Lite (KIE)",
|
|
3650
3965
|
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.",
|
|
3651
3966
|
cost: { usd: 0.15 },
|
|
3967
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-lite" },
|
|
3652
3968
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3653
3969
|
},
|
|
3654
3970
|
{
|
|
@@ -3659,6 +3975,7 @@ var MODEL_CATALOG2 = [
|
|
|
3659
3975
|
label: "Veo 3.1 Fast (KIE)",
|
|
3660
3976
|
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.",
|
|
3661
3977
|
cost: { usd: 0.3 },
|
|
3978
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-fast" },
|
|
3662
3979
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3663
3980
|
},
|
|
3664
3981
|
{
|
|
@@ -3669,6 +3986,7 @@ var MODEL_CATALOG2 = [
|
|
|
3669
3986
|
label: "Veo 3.1 (KIE)",
|
|
3670
3987
|
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).",
|
|
3671
3988
|
cost: { usd: 1.25 },
|
|
3989
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1" },
|
|
3672
3990
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3673
3991
|
},
|
|
3674
3992
|
// ── OpenRouter video: Google Veo 3.1 at Google's own prices ───────────────
|
|
@@ -3715,6 +4033,7 @@ var MODEL_CATALOG2 = [
|
|
|
3715
4033
|
label: "Grok Imagine (KIE)",
|
|
3716
4034
|
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.",
|
|
3717
4035
|
cost: { usd: 0.135 },
|
|
4036
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-image-to-video" },
|
|
3718
4037
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
3719
4038
|
},
|
|
3720
4039
|
{
|
|
@@ -3725,6 +4044,7 @@ var MODEL_CATALOG2 = [
|
|
|
3725
4044
|
label: "Grok Imagine 1.5 (KIE, preview)",
|
|
3726
4045
|
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.",
|
|
3727
4046
|
cost: { usd: 0.18 },
|
|
4047
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-1-5-image-to-video" },
|
|
3728
4048
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
3729
4049
|
},
|
|
3730
4050
|
{
|
|
@@ -3735,6 +4055,7 @@ var MODEL_CATALOG2 = [
|
|
|
3735
4055
|
label: "Kling 2.5 Turbo Pro (KIE)",
|
|
3736
4056
|
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).",
|
|
3737
4057
|
cost: { usd: 0.21 },
|
|
4058
|
+
listPrice: { source: "fal", model: "fal/kling-v2-5-image-to-video" },
|
|
3738
4059
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3739
4060
|
},
|
|
3740
4061
|
{
|
|
@@ -3745,6 +4066,7 @@ var MODEL_CATALOG2 = [
|
|
|
3745
4066
|
label: "Kling 2.6 (KIE)",
|
|
3746
4067
|
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).",
|
|
3747
4068
|
cost: { usd: 0.275 },
|
|
4069
|
+
listPrice: { source: "fal", model: "fal/kling-2-6-image-to-video" },
|
|
3748
4070
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
3749
4071
|
},
|
|
3750
4072
|
{
|
|
@@ -3756,6 +4078,7 @@ var MODEL_CATALOG2 = [
|
|
|
3756
4078
|
refusesPhotorealPeople: true,
|
|
3757
4079
|
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).",
|
|
3758
4080
|
cost: { usd: 1.025 },
|
|
4081
|
+
listPrice: { source: "fal", model: "fal/seedance-2-0-image-to-video" },
|
|
3759
4082
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3760
4083
|
},
|
|
3761
4084
|
{
|
|
@@ -3767,6 +4090,7 @@ var MODEL_CATALOG2 = [
|
|
|
3767
4090
|
refusesPhotorealPeople: true,
|
|
3768
4091
|
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.",
|
|
3769
4092
|
cost: { usd: 1.575 },
|
|
4093
|
+
listPrice: { source: "fal", model: "fal/seedance-2-5-image-to-video" },
|
|
3770
4094
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3771
4095
|
},
|
|
3772
4096
|
{
|
|
@@ -3777,6 +4101,7 @@ var MODEL_CATALOG2 = [
|
|
|
3777
4101
|
label: "Wan 3.0 (KIE)",
|
|
3778
4102
|
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).",
|
|
3779
4103
|
cost: { usd: 0.4 },
|
|
4104
|
+
listPrice: { source: "fal", model: "fal/wan-3-0-image-to-video" },
|
|
3780
4105
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3781
4106
|
},
|
|
3782
4107
|
{
|
|
@@ -3787,6 +4112,7 @@ var MODEL_CATALOG2 = [
|
|
|
3787
4112
|
label: "MiniMax H3 (KIE)",
|
|
3788
4113
|
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.",
|
|
3789
4114
|
cost: { usd: 0.24 },
|
|
4115
|
+
listPrice: { source: "unrecorded" },
|
|
3790
4116
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3791
4117
|
},
|
|
3792
4118
|
{
|
|
@@ -3797,6 +4123,7 @@ var MODEL_CATALOG2 = [
|
|
|
3797
4123
|
label: "Gemini Omni 1.1 Flash (KIE)",
|
|
3798
4124
|
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.",
|
|
3799
4125
|
cost: { usd: 0.525 },
|
|
4126
|
+
listPrice: { source: "unrecorded" },
|
|
3800
4127
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3801
4128
|
},
|
|
3802
4129
|
{
|
|
@@ -3807,6 +4134,7 @@ var MODEL_CATALOG2 = [
|
|
|
3807
4134
|
label: "Kling O3 (KIE)",
|
|
3808
4135
|
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.",
|
|
3809
4136
|
cost: { usd: 0.35 },
|
|
4137
|
+
listPrice: { source: "unrecorded" },
|
|
3810
4138
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3811
4139
|
},
|
|
3812
4140
|
{
|
|
@@ -4125,6 +4453,7 @@ var MODEL_CATALOG2 = [
|
|
|
4125
4453
|
label: "Suno V5 (KIE)",
|
|
4126
4454
|
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.",
|
|
4127
4455
|
cost: { usd: 0.04 },
|
|
4456
|
+
listPrice: { source: "unrecorded" },
|
|
4128
4457
|
isDefault: true
|
|
4129
4458
|
},
|
|
4130
4459
|
// ── audio: voice via ElevenLabs (B2) ───────────────────────────────────
|
|
@@ -4173,6 +4502,7 @@ var MODEL_CATALOG2 = [
|
|
|
4173
4502
|
label: "Claude Sonnet 4.5 (KIE)",
|
|
4174
4503
|
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.",
|
|
4175
4504
|
cost: { usd: 0.01 },
|
|
4505
|
+
listPrice: { source: "unrecorded" },
|
|
4176
4506
|
isDefault: true
|
|
4177
4507
|
},
|
|
4178
4508
|
{
|
|
@@ -4182,7 +4512,8 @@ var MODEL_CATALOG2 = [
|
|
|
4182
4512
|
task: "text-generation",
|
|
4183
4513
|
label: "Claude Opus 4.5 (KIE)",
|
|
4184
4514
|
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.",
|
|
4185
|
-
cost: { usd: 0.05 }
|
|
4515
|
+
cost: { usd: 0.05 },
|
|
4516
|
+
listPrice: { source: "unrecorded" }
|
|
4186
4517
|
},
|
|
4187
4518
|
{
|
|
4188
4519
|
id: "openrouter/claude-sonnet-4-6",
|
|
@@ -4247,8 +4578,26 @@ var MODEL_CATALOG2 = [
|
|
|
4247
4578
|
description: "Vertex AI direct, newest flash variant. Global region. Preview status."
|
|
4248
4579
|
}
|
|
4249
4580
|
];
|
|
4581
|
+
var routeEntries2 = /* @__PURE__ */ new Map();
|
|
4582
|
+
function routeEntry2(id) {
|
|
4583
|
+
const cached = routeEntries2.get(id);
|
|
4584
|
+
if (cached) return cached;
|
|
4585
|
+
const route = getRoute2(id);
|
|
4586
|
+
const primary = route ? MODEL_CATALOG2.find((m) => m.id === route.primary) : void 0;
|
|
4587
|
+
if (!route || !primary) return void 0;
|
|
4588
|
+
const entry = {
|
|
4589
|
+
...primary,
|
|
4590
|
+
// A route is never the default of its task (that is slice 3's switch).
|
|
4591
|
+
isDefault: void 0,
|
|
4592
|
+
id: route.id,
|
|
4593
|
+
label: route.label,
|
|
4594
|
+
description: `Runs on KIE first and, when KIE fails on its side, again on fal with the same settings. ${primary.description ?? ""}`.trim()
|
|
4595
|
+
};
|
|
4596
|
+
routeEntries2.set(id, entry);
|
|
4597
|
+
return entry;
|
|
4598
|
+
}
|
|
4250
4599
|
function tryGetModel2(id) {
|
|
4251
|
-
return MODEL_CATALOG2.find((m) => m.id === id);
|
|
4600
|
+
return MODEL_CATALOG2.find((m) => m.id === id) ?? routeEntry2(id);
|
|
4252
4601
|
}
|
|
4253
4602
|
function supportedResolutions(model) {
|
|
4254
4603
|
const declared = model?.capabilities?.resolutions;
|
|
@@ -5895,7 +6244,7 @@ function nodeUrl(res, nodeId) {
|
|
|
5895
6244
|
}
|
|
5896
6245
|
var studioListProjects = defineTool({
|
|
5897
6246
|
name: "studio_list_projects",
|
|
5898
|
-
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").',
|
|
6247
|
+
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.',
|
|
5899
6248
|
inputSchema: {},
|
|
5900
6249
|
annotations: { readOnlyHint: true },
|
|
5901
6250
|
handler: async (_args, ctx) => {
|
|
@@ -5905,17 +6254,26 @@ var studioListProjects = defineTool({
|
|
|
5905
6254
|
});
|
|
5906
6255
|
var studioGenerateImage = defineTool({
|
|
5907
6256
|
name: "studio_generate_image",
|
|
5908
|
-
description:
|
|
6257
|
+
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.',
|
|
5909
6258
|
inputSchema: {
|
|
5910
|
-
brandSlug: slug.
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
6259
|
+
brandSlug: slug.optional().describe(
|
|
6260
|
+
"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."
|
|
6261
|
+
),
|
|
6262
|
+
brandMode: z8.string().optional().describe('Optional brand mode (e.g. "illustration"); modes are defined per brand. Only meaningful with brandSlug.'),
|
|
6263
|
+
prompt: z8.string().min(1).describe("What to generate. With brandSlug, the brand style prefix is prepended server-side."),
|
|
6264
|
+
characterSlug: slug.optional().describe(
|
|
6265
|
+
"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."
|
|
6266
|
+
),
|
|
6267
|
+
projectId: z8.string().min(1).optional().describe(
|
|
6268
|
+
'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.'
|
|
6269
|
+
),
|
|
5914
6270
|
model: z8.string().optional().describe('Image model id (e.g. "kie/nano-banana-2", "fal/nano-banana-2"). Omit for the Studio default.'),
|
|
5915
6271
|
aspectRatio: z8.string().optional().describe('Aspect ratio such as "1:1", "16:9", "3:4".'),
|
|
5916
6272
|
imageSize: z8.string().optional().describe('Provider size hint, e.g. "1K", "2K".'),
|
|
5917
6273
|
seed: z8.number().int().optional(),
|
|
5918
|
-
candidateCount: z8.number().int().min(1).max(4).optional().describe(
|
|
6274
|
+
candidateCount: z8.number().int().min(1).max(4).optional().describe(
|
|
6275
|
+
"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."
|
|
6276
|
+
),
|
|
5919
6277
|
wait: z8.boolean().default(true).describe("Poll until the job finishes (default true)."),
|
|
5920
6278
|
waitSeconds
|
|
5921
6279
|
},
|
|
@@ -5927,19 +6285,24 @@ var studioGenerateImage = defineTool({
|
|
|
5927
6285
|
if (args.seed !== void 0) settings.seed = args.seed;
|
|
5928
6286
|
if (args.candidateCount !== void 0) settings.candidateCount = args.candidateCount;
|
|
5929
6287
|
const submitted = await ctx.client.generate({
|
|
5930
|
-
brandSlug: args.brandSlug,
|
|
6288
|
+
...args.brandSlug ? { brandSlug: args.brandSlug } : {},
|
|
5931
6289
|
...args.brandMode ? { brandMode: args.brandMode } : {},
|
|
5932
6290
|
prompt: args.prompt,
|
|
5933
6291
|
...args.characterSlug ? { characterSlug: args.characterSlug } : {},
|
|
6292
|
+
...args.projectId ? { projectId: args.projectId } : {},
|
|
5934
6293
|
...Object.keys(settings).length > 0 ? { settings } : {}
|
|
5935
6294
|
});
|
|
5936
|
-
|
|
6295
|
+
const plan = {
|
|
6296
|
+
...submitted.candidates ? { candidates: submitted.candidates } : {},
|
|
6297
|
+
...typeof submitted.notice === "string" && submitted.notice.length > 0 ? { notice: submitted.notice } : {}
|
|
6298
|
+
};
|
|
6299
|
+
if (!args.wait) return { jobId: submitted.jobId, kind: submitted.kind, status: "queued", ...plan };
|
|
5937
6300
|
const done = await waitForJob(ctx.client, submitted.jobId, pollOpts(ctx, args.waitSeconds));
|
|
5938
6301
|
const out = projectJob(done);
|
|
5939
6302
|
if (done.job.status !== "succeeded") {
|
|
5940
6303
|
throw new Error(`Job ${submitted.jobId} ${done.job.status}: ${done.job.errorMessage ?? "no error message from the API"}`);
|
|
5941
6304
|
}
|
|
5942
|
-
return { ...out, kind: submitted.kind };
|
|
6305
|
+
return { ...out, kind: submitted.kind, ...plan };
|
|
5943
6306
|
}
|
|
5944
6307
|
});
|
|
5945
6308
|
var GENERATE_3D_TASKS = /* @__PURE__ */ new Set(["text-to-3d", "image-to-3d"]);
|
|
@@ -6085,7 +6448,7 @@ var studioGetJob = defineTool({
|
|
|
6085
6448
|
});
|
|
6086
6449
|
var studioListCharacters = defineTool({
|
|
6087
6450
|
name: "studio_list_characters",
|
|
6088
|
-
description: "List
|
|
6451
|
+
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.",
|
|
6089
6452
|
inputSchema: {},
|
|
6090
6453
|
annotations: { readOnlyHint: true },
|
|
6091
6454
|
handler: async (_args, ctx) => {
|
|
@@ -6095,7 +6458,7 @@ var studioListCharacters = defineTool({
|
|
|
6095
6458
|
});
|
|
6096
6459
|
var studioGetCharacter = defineTool({
|
|
6097
6460
|
name: "studio_get_character",
|
|
6098
|
-
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;
|
|
6461
|
+
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.",
|
|
6099
6462
|
inputSchema: { slug },
|
|
6100
6463
|
annotations: { readOnlyHint: true },
|
|
6101
6464
|
handler: async (args, ctx) => {
|
|
@@ -6110,7 +6473,7 @@ function assertLikenessRightsConfirmed(value) {
|
|
|
6110
6473
|
}
|
|
6111
6474
|
var studioCreateCharacter = defineTool({
|
|
6112
6475
|
name: "studio_create_character",
|
|
6113
|
-
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
|
|
6476
|
+
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.",
|
|
6114
6477
|
inputSchema: {
|
|
6115
6478
|
name: z8.string().min(1).describe('Display name, e.g. "Bruno Hartmann".'),
|
|
6116
6479
|
slug,
|
|
@@ -6202,7 +6565,7 @@ var studioMintCharacter = defineTool({
|
|
|
6202
6565
|
});
|
|
6203
6566
|
var studioCreateBrand = defineTool({
|
|
6204
6567
|
name: "studio_create_brand",
|
|
6205
|
-
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
|
|
6568
|
+
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.",
|
|
6206
6569
|
inputSchema: {
|
|
6207
6570
|
name: z8.string().min(1),
|
|
6208
6571
|
slug,
|
|
@@ -6227,7 +6590,7 @@ var studioUploadBrandReference = defineTool({
|
|
|
6227
6590
|
name: "studio_upload_brand_reference",
|
|
6228
6591
|
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.`,
|
|
6229
6592
|
inputSchema: {
|
|
6230
|
-
brandSlug: slug,
|
|
6593
|
+
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."),
|
|
6231
6594
|
source: z8.string().min(1).describe("Local file path or http(s) URL of an image."),
|
|
6232
6595
|
category: z8.string().optional().describe('Library category, default "general".'),
|
|
6233
6596
|
label: z8.string().optional().describe("Optional file name / label."),
|
|
@@ -6349,7 +6712,9 @@ var studioReplaceCharacterInVideo = defineTool({
|
|
|
6349
6712
|
inputSchema: {
|
|
6350
6713
|
projectId: z8.string().min(1).describe("Project id from studio_list_projects."),
|
|
6351
6714
|
sceneFrameUrl: httpUrl.describe("Public or signed URL of ONE frame of the target shot (png/jpg)."),
|
|
6352
|
-
characterSlug: slug.optional().describe(
|
|
6715
|
+
characterSlug: slug.optional().describe(
|
|
6716
|
+
"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."
|
|
6717
|
+
),
|
|
6353
6718
|
characterReferenceUrl: httpUrl.optional().describe("Explicit character reference image URL (use instead of characterSlug)."),
|
|
6354
6719
|
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."),
|
|
6355
6720
|
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."),
|
|
@@ -6422,7 +6787,7 @@ var studioReplaceCharacterInVideo = defineTool({
|
|
|
6422
6787
|
});
|
|
6423
6788
|
var studioGetSpend = defineTool({
|
|
6424
6789
|
name: "studio_get_spend",
|
|
6425
|
-
description:
|
|
6790
|
+
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).",
|
|
6426
6791
|
inputSchema: {
|
|
6427
6792
|
tzOffsetMinutes: z8.number().int().min(-840).max(840).optional()
|
|
6428
6793
|
},
|
|
@@ -6434,7 +6799,7 @@ var studioGetSpend = defineTool({
|
|
|
6434
6799
|
});
|
|
6435
6800
|
var studioGetUsage = defineTool({
|
|
6436
6801
|
name: "studio_get_usage",
|
|
6437
|
-
description:
|
|
6802
|
+
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.",
|
|
6438
6803
|
inputSchema: {},
|
|
6439
6804
|
annotations: { readOnlyHint: true },
|
|
6440
6805
|
handler: async (_args, ctx) => {
|
|
@@ -6554,7 +6919,7 @@ function registerTools(server, ctx) {
|
|
|
6554
6919
|
|
|
6555
6920
|
// src/server.ts
|
|
6556
6921
|
var SERVER_NAME = "lumitra-studio";
|
|
6557
|
-
var SERVER_VERSION = "0.
|
|
6922
|
+
var SERVER_VERSION = "0.5.0";
|
|
6558
6923
|
function createStudioMcpServer(opts = {}) {
|
|
6559
6924
|
const config = opts.config ?? resolveConfig(opts.env ?? process.env);
|
|
6560
6925
|
const client = new StudioClient(config, {
|
|
@@ -6566,7 +6931,7 @@ function createStudioMcpServer(opts = {}) {
|
|
|
6566
6931
|
{
|
|
6567
6932
|
instructions: [
|
|
6568
6933
|
"Lumitra Studio: brand-consistent image generation, character minting, workflow runs and the still-swap video recipe over the hosted Studio API.",
|
|
6569
|
-
"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.",
|
|
6934
|
+
"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.",
|
|
6570
6935
|
"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.",
|
|
6571
6936
|
"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.",
|
|
6572
6937
|
"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."
|