@marlinjai/studio-mcp 0.3.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 +37 -11
- package/dist/cli.js +407 -40
- package/dist/index.d.ts +60 -14
- package/dist/index.js +407 -40
- package/examples/claude-code.sh +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
|
|
|
8
8
|
|
|
9
9
|
// src/config.ts
|
|
10
10
|
var DEFAULT_BASE_URL = "https://studio.lumitra.co";
|
|
11
|
+
var LEGACY_API_KEY_VAR = "LUMITRA_STUDIO_SERVICE_TOKEN";
|
|
11
12
|
var ConfigError = class extends Error {
|
|
12
13
|
constructor(message) {
|
|
13
14
|
super(message);
|
|
@@ -40,10 +41,11 @@ function resolveConfig(env = process.env) {
|
|
|
40
41
|
`LUMITRA_STUDIO_BASE_URL is not a valid http(s) URL (got ${JSON.stringify(rawBase)})`
|
|
41
42
|
);
|
|
42
43
|
}
|
|
43
|
-
const apiKey = readEnvVar("LUMITRA_STUDIO_API_KEY", env)
|
|
44
|
+
const apiKey = readEnvVar("LUMITRA_STUDIO_API_KEY", env);
|
|
44
45
|
if (!apiKey) {
|
|
46
|
+
const legacyHint = readEnvVar(LEGACY_API_KEY_VAR, env) ? ` ${LEGACY_API_KEY_VAR} is set, but that legacy name is no longer read since 0.4.0: provide the same key as LUMITRA_STUDIO_API_KEY.` : "";
|
|
45
47
|
throw new ConfigError(
|
|
46
|
-
|
|
48
|
+
`Missing API key: set LUMITRA_STUDIO_API_KEY to a Lumitra Studio bearer token. An unexpanded "\${...}" placeholder counts as unset.${legacyHint}`
|
|
47
49
|
);
|
|
48
50
|
}
|
|
49
51
|
return { baseUrl, apiKey };
|
|
@@ -93,7 +95,11 @@ function apiMessageFromBody(body, fallback) {
|
|
|
93
95
|
if (typeof o.error === "string" && o.error.length > 0) {
|
|
94
96
|
const detail = typeof o.message === "string" ? o.message : "";
|
|
95
97
|
const adds = detail.length > 0 && !o.error.includes(detail) && !detail.includes(o.error);
|
|
96
|
-
|
|
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})`;
|
|
97
103
|
const quota = quotaDetail(o);
|
|
98
104
|
return quota ? `${headline} (${quota})` : headline;
|
|
99
105
|
}
|
|
@@ -153,6 +159,14 @@ var StudioClient = class {
|
|
|
153
159
|
return this.json("GET", "/api/v1/projects");
|
|
154
160
|
}
|
|
155
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
|
+
*/
|
|
156
170
|
generate(body) {
|
|
157
171
|
return this.json("POST", "/api/generate", { json: body });
|
|
158
172
|
}
|
|
@@ -441,7 +455,124 @@ function studio3dCreditsToUsd(credits) {
|
|
|
441
455
|
return Math.round(credits * STUDIO3D_USD_PER_CREDIT * 1e3) / 1e3;
|
|
442
456
|
}
|
|
443
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
|
+
}
|
|
444
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.
|
|
445
576
|
// ── 3D AI Studio: 3D generation ────────────────────────────────────────
|
|
446
577
|
// `credits` is what the model's DEFAULT request bills (the options its
|
|
447
578
|
// builder sends when a node sets none), priced by providers/studio3d/
|
|
@@ -538,6 +669,7 @@ var MODEL_CATALOG = [
|
|
|
538
669
|
label: "Nano Banana 2 (KIE)",
|
|
539
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.",
|
|
540
671
|
cost: { usd: 0.04, usdByResolution: { "1K": 0.04, "2K": 0.06, "4K": 0.09 } },
|
|
672
|
+
listPrice: { source: "fal", model: "fal/nano-banana-2" },
|
|
541
673
|
capabilities: { resolutions: ["1K", "2K", "4K"] }
|
|
542
674
|
},
|
|
543
675
|
{
|
|
@@ -550,6 +682,7 @@ var MODEL_CATALOG = [
|
|
|
550
682
|
label: "Imagen 4 Fast (KIE)",
|
|
551
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.",
|
|
552
684
|
cost: { usd: 0.02 },
|
|
685
|
+
listPrice: { source: "owner", usd: 0.02 },
|
|
553
686
|
capabilities: { resolutions: [] }
|
|
554
687
|
},
|
|
555
688
|
{
|
|
@@ -559,11 +692,15 @@ var MODEL_CATALOG = [
|
|
|
559
692
|
task: "text-to-image",
|
|
560
693
|
label: "Seedream V4 (KIE)",
|
|
561
694
|
description: "ByteDance Seedream V4 via KIE. Stylized output not available on Vertex. No IP indemnity.",
|
|
562
|
-
cost: { usd: 0.0175 }
|
|
695
|
+
cost: { usd: 0.0175 },
|
|
696
|
+
listPrice: { source: "unrecorded" }
|
|
563
697
|
},
|
|
564
698
|
// ── KIE.ai: newer image models, the cheaper route (price feed 2026-09-23) ─
|
|
565
699
|
// Billed from the credits KIE reports on each task. KIE offers no data
|
|
566
|
-
// 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.
|
|
567
704
|
{
|
|
568
705
|
id: "kie/nano-banana-2-edit",
|
|
569
706
|
provider: "kie",
|
|
@@ -572,6 +709,7 @@ var MODEL_CATALOG = [
|
|
|
572
709
|
label: "Nano Banana 2 Edit (KIE)",
|
|
573
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.",
|
|
574
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" },
|
|
575
713
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
576
714
|
},
|
|
577
715
|
{
|
|
@@ -582,6 +720,7 @@ var MODEL_CATALOG = [
|
|
|
582
720
|
label: "Nano Banana Pro (KIE)",
|
|
583
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).",
|
|
584
722
|
cost: { usd: 0.09, usdByResolution: { "1K": 0.09, "2K": 0.09, "4K": 0.12 } },
|
|
723
|
+
listPrice: { source: "fal", model: "fal/nano-banana-pro" },
|
|
585
724
|
capabilities: { resolutions: ["1K", "2K", "4K"], maxInputImages: 0 }
|
|
586
725
|
},
|
|
587
726
|
{
|
|
@@ -592,6 +731,7 @@ var MODEL_CATALOG = [
|
|
|
592
731
|
label: "Nano Banana Pro Edit (KIE)",
|
|
593
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).",
|
|
594
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" },
|
|
595
735
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
596
736
|
},
|
|
597
737
|
{
|
|
@@ -602,6 +742,7 @@ var MODEL_CATALOG = [
|
|
|
602
742
|
label: "Seedream 5 Lite (KIE)",
|
|
603
743
|
description: "ByteDance Seedream 5 Lite via KIE, 2K or 4K. $0.0275 per image (fal: $0.035).",
|
|
604
744
|
cost: { usd: 0.0275 },
|
|
745
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite" },
|
|
605
746
|
capabilities: { resolutions: ["2K", "4K"], maxInputImages: 0 }
|
|
606
747
|
},
|
|
607
748
|
{
|
|
@@ -612,6 +753,7 @@ var MODEL_CATALOG = [
|
|
|
612
753
|
label: "Seedream 5 Lite Edit (KIE)",
|
|
613
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).",
|
|
614
755
|
cost: { usd: 0.0275 },
|
|
756
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite-edit" },
|
|
615
757
|
capabilities: { resolutions: ["2K", "4K"], needsImageInput: true, maxInputImages: 10 }
|
|
616
758
|
},
|
|
617
759
|
{
|
|
@@ -622,6 +764,7 @@ var MODEL_CATALOG = [
|
|
|
622
764
|
label: "Seedream 5 Pro (KIE)",
|
|
623
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).",
|
|
624
766
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
767
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro" },
|
|
625
768
|
capabilities: { resolutions: ["1K", "2K"], maxInputImages: 0 }
|
|
626
769
|
},
|
|
627
770
|
{
|
|
@@ -632,6 +775,7 @@ var MODEL_CATALOG = [
|
|
|
632
775
|
label: "Seedream 5 Pro Edit (KIE)",
|
|
633
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.",
|
|
634
777
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
778
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro-edit" },
|
|
635
779
|
capabilities: { resolutions: ["1K", "2K"], needsImageInput: true, maxInputImages: 10 }
|
|
636
780
|
},
|
|
637
781
|
// ── fal.ai: image gen ─────────────────────────────────────────────────
|
|
@@ -926,8 +1070,11 @@ var MODEL_CATALOG = [
|
|
|
926
1070
|
// ── KIE.ai video: the cheaper route to the same upstream models ──────────
|
|
927
1071
|
// Async via KIE's Veo and createTask APIs (providers/kie-video.ts). 20 to 85%
|
|
928
1072
|
// below fal (KIE price feed, 2026-09-23). KIE offers no data processing
|
|
929
|
-
// agreement
|
|
930
|
-
//
|
|
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.
|
|
931
1078
|
{
|
|
932
1079
|
id: "kie/veo-3-1-lite",
|
|
933
1080
|
provider: "kie",
|
|
@@ -936,6 +1083,7 @@ var MODEL_CATALOG = [
|
|
|
936
1083
|
label: "Veo 3.1 Lite (KIE)",
|
|
937
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.",
|
|
938
1085
|
cost: { usd: 0.15 },
|
|
1086
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-lite" },
|
|
939
1087
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
940
1088
|
},
|
|
941
1089
|
{
|
|
@@ -946,6 +1094,7 @@ var MODEL_CATALOG = [
|
|
|
946
1094
|
label: "Veo 3.1 Fast (KIE)",
|
|
947
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.",
|
|
948
1096
|
cost: { usd: 0.3 },
|
|
1097
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-fast" },
|
|
949
1098
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
950
1099
|
},
|
|
951
1100
|
{
|
|
@@ -956,6 +1105,7 @@ var MODEL_CATALOG = [
|
|
|
956
1105
|
label: "Veo 3.1 (KIE)",
|
|
957
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).",
|
|
958
1107
|
cost: { usd: 1.25 },
|
|
1108
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1" },
|
|
959
1109
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
960
1110
|
},
|
|
961
1111
|
// ── OpenRouter video: Google Veo 3.1 at Google's own prices ───────────────
|
|
@@ -1002,6 +1152,7 @@ var MODEL_CATALOG = [
|
|
|
1002
1152
|
label: "Grok Imagine (KIE)",
|
|
1003
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.",
|
|
1004
1154
|
cost: { usd: 0.135 },
|
|
1155
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-image-to-video" },
|
|
1005
1156
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
1006
1157
|
},
|
|
1007
1158
|
{
|
|
@@ -1012,6 +1163,7 @@ var MODEL_CATALOG = [
|
|
|
1012
1163
|
label: "Grok Imagine 1.5 (KIE, preview)",
|
|
1013
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.",
|
|
1014
1165
|
cost: { usd: 0.18 },
|
|
1166
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-1-5-image-to-video" },
|
|
1015
1167
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
1016
1168
|
},
|
|
1017
1169
|
{
|
|
@@ -1022,6 +1174,7 @@ var MODEL_CATALOG = [
|
|
|
1022
1174
|
label: "Kling 2.5 Turbo Pro (KIE)",
|
|
1023
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).",
|
|
1024
1176
|
cost: { usd: 0.21 },
|
|
1177
|
+
listPrice: { source: "fal", model: "fal/kling-v2-5-image-to-video" },
|
|
1025
1178
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1026
1179
|
},
|
|
1027
1180
|
{
|
|
@@ -1032,6 +1185,7 @@ var MODEL_CATALOG = [
|
|
|
1032
1185
|
label: "Kling 2.6 (KIE)",
|
|
1033
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).",
|
|
1034
1187
|
cost: { usd: 0.275 },
|
|
1188
|
+
listPrice: { source: "fal", model: "fal/kling-2-6-image-to-video" },
|
|
1035
1189
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
1036
1190
|
},
|
|
1037
1191
|
{
|
|
@@ -1043,6 +1197,7 @@ var MODEL_CATALOG = [
|
|
|
1043
1197
|
refusesPhotorealPeople: true,
|
|
1044
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).",
|
|
1045
1199
|
cost: { usd: 1.025 },
|
|
1200
|
+
listPrice: { source: "fal", model: "fal/seedance-2-0-image-to-video" },
|
|
1046
1201
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1047
1202
|
},
|
|
1048
1203
|
{
|
|
@@ -1054,6 +1209,7 @@ var MODEL_CATALOG = [
|
|
|
1054
1209
|
refusesPhotorealPeople: true,
|
|
1055
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.",
|
|
1056
1211
|
cost: { usd: 1.575 },
|
|
1212
|
+
listPrice: { source: "fal", model: "fal/seedance-2-5-image-to-video" },
|
|
1057
1213
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1058
1214
|
},
|
|
1059
1215
|
{
|
|
@@ -1064,6 +1220,7 @@ var MODEL_CATALOG = [
|
|
|
1064
1220
|
label: "Wan 3.0 (KIE)",
|
|
1065
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).",
|
|
1066
1222
|
cost: { usd: 0.4 },
|
|
1223
|
+
listPrice: { source: "fal", model: "fal/wan-3-0-image-to-video" },
|
|
1067
1224
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1068
1225
|
},
|
|
1069
1226
|
{
|
|
@@ -1074,6 +1231,7 @@ var MODEL_CATALOG = [
|
|
|
1074
1231
|
label: "MiniMax H3 (KIE)",
|
|
1075
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.",
|
|
1076
1233
|
cost: { usd: 0.24 },
|
|
1234
|
+
listPrice: { source: "unrecorded" },
|
|
1077
1235
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1078
1236
|
},
|
|
1079
1237
|
{
|
|
@@ -1084,6 +1242,7 @@ var MODEL_CATALOG = [
|
|
|
1084
1242
|
label: "Gemini Omni 1.1 Flash (KIE)",
|
|
1085
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.",
|
|
1086
1244
|
cost: { usd: 0.525 },
|
|
1245
|
+
listPrice: { source: "unrecorded" },
|
|
1087
1246
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1088
1247
|
},
|
|
1089
1248
|
{
|
|
@@ -1094,6 +1253,7 @@ var MODEL_CATALOG = [
|
|
|
1094
1253
|
label: "Kling O3 (KIE)",
|
|
1095
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.",
|
|
1096
1255
|
cost: { usd: 0.35 },
|
|
1256
|
+
listPrice: { source: "unrecorded" },
|
|
1097
1257
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
1098
1258
|
},
|
|
1099
1259
|
{
|
|
@@ -1412,6 +1572,7 @@ var MODEL_CATALOG = [
|
|
|
1412
1572
|
label: "Suno V5 (KIE)",
|
|
1413
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.",
|
|
1414
1574
|
cost: { usd: 0.04 },
|
|
1575
|
+
listPrice: { source: "unrecorded" },
|
|
1415
1576
|
isDefault: true
|
|
1416
1577
|
},
|
|
1417
1578
|
// ── audio: voice via ElevenLabs (B2) ───────────────────────────────────
|
|
@@ -1460,6 +1621,7 @@ var MODEL_CATALOG = [
|
|
|
1460
1621
|
label: "Claude Sonnet 4.5 (KIE)",
|
|
1461
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.",
|
|
1462
1623
|
cost: { usd: 0.01 },
|
|
1624
|
+
listPrice: { source: "unrecorded" },
|
|
1463
1625
|
isDefault: true
|
|
1464
1626
|
},
|
|
1465
1627
|
{
|
|
@@ -1469,7 +1631,8 @@ var MODEL_CATALOG = [
|
|
|
1469
1631
|
task: "text-generation",
|
|
1470
1632
|
label: "Claude Opus 4.5 (KIE)",
|
|
1471
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.",
|
|
1472
|
-
cost: { usd: 0.05 }
|
|
1634
|
+
cost: { usd: 0.05 },
|
|
1635
|
+
listPrice: { source: "unrecorded" }
|
|
1473
1636
|
},
|
|
1474
1637
|
{
|
|
1475
1638
|
id: "openrouter/claude-sonnet-4-6",
|
|
@@ -1534,8 +1697,26 @@ var MODEL_CATALOG = [
|
|
|
1534
1697
|
description: "Vertex AI direct, newest flash variant. Global region. Preview status."
|
|
1535
1698
|
}
|
|
1536
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
|
+
}
|
|
1537
1718
|
function tryGetModel(id) {
|
|
1538
|
-
return MODEL_CATALOG.find((m) => m.id === id);
|
|
1719
|
+
return MODEL_CATALOG.find((m) => m.id === id) ?? routeEntry(id);
|
|
1539
1720
|
}
|
|
1540
1721
|
var ROWS = [
|
|
1541
1722
|
[0, "Idle", "DailyActions", "Idle"],
|
|
@@ -2463,7 +2644,10 @@ var VIDEO_UNSUPPORTED_PARAMS = {
|
|
|
2463
2644
|
...Object.fromEntries(KLING_O3_I2V_ENDPOINTS.map((e) => [e, ["resolution", "aspectRatio", "seed", "negativePrompt"]])),
|
|
2464
2645
|
...Object.fromEntries(LTX_25_I2V_ENDPOINTS.map((e) => [e, ["seed", "negativePrompt"]]))
|
|
2465
2646
|
};
|
|
2466
|
-
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
|
+
};
|
|
2467
2651
|
function veoSpec(model, label, perClip) {
|
|
2468
2652
|
return {
|
|
2469
2653
|
family: "veo",
|
|
@@ -2477,7 +2661,7 @@ function veoSpec(model, label, perClip) {
|
|
|
2477
2661
|
audioSwitch: false,
|
|
2478
2662
|
defaultAudio: true,
|
|
2479
2663
|
// KIE prices Veo per clip whatever its length, and always renders audio.
|
|
2480
|
-
price: (_s, r) => perClip[r]
|
|
2664
|
+
price: (_s, r) => perClip[r],
|
|
2481
2665
|
build: (i) => ({
|
|
2482
2666
|
prompt: i.prompt,
|
|
2483
2667
|
model,
|
|
@@ -2684,7 +2868,7 @@ var KIE_VIDEO_SPECS = {
|
|
|
2684
2868
|
audioSwitch: false,
|
|
2685
2869
|
defaultAudio: false,
|
|
2686
2870
|
// Priced per clip by length; 4K carries a surcharge.
|
|
2687
|
-
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],
|
|
2688
2872
|
// The first-and-last-frame mode: KIE refuses it combined with registered
|
|
2689
2873
|
// characters, reference images, audio or video, so none are sent.
|
|
2690
2874
|
build: (i) => ({
|
|
@@ -2710,7 +2894,7 @@ var KIE_VIDEO_SPECS = {
|
|
|
2710
2894
|
audioSwitch: true,
|
|
2711
2895
|
defaultAudio: false,
|
|
2712
2896
|
// With-audio rates from KIE's price feed, the higher of its two listings.
|
|
2713
|
-
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),
|
|
2714
2898
|
build: (i) => ({
|
|
2715
2899
|
prompt: i.prompt,
|
|
2716
2900
|
image_urls: i.end ? [i.start, i.end] : [i.start],
|
|
@@ -3152,7 +3336,124 @@ function imageResolutionTier(value) {
|
|
|
3152
3336
|
return null;
|
|
3153
3337
|
}
|
|
3154
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
|
+
}
|
|
3155
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.
|
|
3156
3457
|
// ── 3D AI Studio: 3D generation ────────────────────────────────────────
|
|
3157
3458
|
// `credits` is what the model's DEFAULT request bills (the options its
|
|
3158
3459
|
// builder sends when a node sets none), priced by providers/studio3d/
|
|
@@ -3249,6 +3550,7 @@ var MODEL_CATALOG2 = [
|
|
|
3249
3550
|
label: "Nano Banana 2 (KIE)",
|
|
3250
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.",
|
|
3251
3552
|
cost: { usd: 0.04, usdByResolution: { "1K": 0.04, "2K": 0.06, "4K": 0.09 } },
|
|
3553
|
+
listPrice: { source: "fal", model: "fal/nano-banana-2" },
|
|
3252
3554
|
capabilities: { resolutions: ["1K", "2K", "4K"] }
|
|
3253
3555
|
},
|
|
3254
3556
|
{
|
|
@@ -3261,6 +3563,7 @@ var MODEL_CATALOG2 = [
|
|
|
3261
3563
|
label: "Imagen 4 Fast (KIE)",
|
|
3262
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.",
|
|
3263
3565
|
cost: { usd: 0.02 },
|
|
3566
|
+
listPrice: { source: "owner", usd: 0.02 },
|
|
3264
3567
|
capabilities: { resolutions: [] }
|
|
3265
3568
|
},
|
|
3266
3569
|
{
|
|
@@ -3270,11 +3573,15 @@ var MODEL_CATALOG2 = [
|
|
|
3270
3573
|
task: "text-to-image",
|
|
3271
3574
|
label: "Seedream V4 (KIE)",
|
|
3272
3575
|
description: "ByteDance Seedream V4 via KIE. Stylized output not available on Vertex. No IP indemnity.",
|
|
3273
|
-
cost: { usd: 0.0175 }
|
|
3576
|
+
cost: { usd: 0.0175 },
|
|
3577
|
+
listPrice: { source: "unrecorded" }
|
|
3274
3578
|
},
|
|
3275
3579
|
// ── KIE.ai: newer image models, the cheaper route (price feed 2026-09-23) ─
|
|
3276
3580
|
// Billed from the credits KIE reports on each task. KIE offers no data
|
|
3277
|
-
// 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.
|
|
3278
3585
|
{
|
|
3279
3586
|
id: "kie/nano-banana-2-edit",
|
|
3280
3587
|
provider: "kie",
|
|
@@ -3283,6 +3590,7 @@ var MODEL_CATALOG2 = [
|
|
|
3283
3590
|
label: "Nano Banana 2 Edit (KIE)",
|
|
3284
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.",
|
|
3285
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" },
|
|
3286
3594
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
3287
3595
|
},
|
|
3288
3596
|
{
|
|
@@ -3293,6 +3601,7 @@ var MODEL_CATALOG2 = [
|
|
|
3293
3601
|
label: "Nano Banana Pro (KIE)",
|
|
3294
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).",
|
|
3295
3603
|
cost: { usd: 0.09, usdByResolution: { "1K": 0.09, "2K": 0.09, "4K": 0.12 } },
|
|
3604
|
+
listPrice: { source: "fal", model: "fal/nano-banana-pro" },
|
|
3296
3605
|
capabilities: { resolutions: ["1K", "2K", "4K"], maxInputImages: 0 }
|
|
3297
3606
|
},
|
|
3298
3607
|
{
|
|
@@ -3303,6 +3612,7 @@ var MODEL_CATALOG2 = [
|
|
|
3303
3612
|
label: "Nano Banana Pro Edit (KIE)",
|
|
3304
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).",
|
|
3305
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" },
|
|
3306
3616
|
capabilities: { resolutions: ["1K", "2K", "4K"], needsImageInput: true, maxInputImages: 8 }
|
|
3307
3617
|
},
|
|
3308
3618
|
{
|
|
@@ -3313,6 +3623,7 @@ var MODEL_CATALOG2 = [
|
|
|
3313
3623
|
label: "Seedream 5 Lite (KIE)",
|
|
3314
3624
|
description: "ByteDance Seedream 5 Lite via KIE, 2K or 4K. $0.0275 per image (fal: $0.035).",
|
|
3315
3625
|
cost: { usd: 0.0275 },
|
|
3626
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite" },
|
|
3316
3627
|
capabilities: { resolutions: ["2K", "4K"], maxInputImages: 0 }
|
|
3317
3628
|
},
|
|
3318
3629
|
{
|
|
@@ -3323,6 +3634,7 @@ var MODEL_CATALOG2 = [
|
|
|
3323
3634
|
label: "Seedream 5 Lite Edit (KIE)",
|
|
3324
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).",
|
|
3325
3636
|
cost: { usd: 0.0275 },
|
|
3637
|
+
listPrice: { source: "fal", model: "fal/seedream-5-lite-edit" },
|
|
3326
3638
|
capabilities: { resolutions: ["2K", "4K"], needsImageInput: true, maxInputImages: 10 }
|
|
3327
3639
|
},
|
|
3328
3640
|
{
|
|
@@ -3333,6 +3645,7 @@ var MODEL_CATALOG2 = [
|
|
|
3333
3645
|
label: "Seedream 5 Pro (KIE)",
|
|
3334
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).",
|
|
3335
3647
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
3648
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro" },
|
|
3336
3649
|
capabilities: { resolutions: ["1K", "2K"], maxInputImages: 0 }
|
|
3337
3650
|
},
|
|
3338
3651
|
{
|
|
@@ -3343,6 +3656,7 @@ var MODEL_CATALOG2 = [
|
|
|
3343
3656
|
label: "Seedream 5 Pro Edit (KIE)",
|
|
3344
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.",
|
|
3345
3658
|
cost: { usd: 0.035, usdByResolution: { "1K": 0.035, "2K": 0.07 } },
|
|
3659
|
+
listPrice: { source: "fal", model: "fal/seedream-5-pro-edit" },
|
|
3346
3660
|
capabilities: { resolutions: ["1K", "2K"], needsImageInput: true, maxInputImages: 10 }
|
|
3347
3661
|
},
|
|
3348
3662
|
// ── fal.ai: image gen ─────────────────────────────────────────────────
|
|
@@ -3637,8 +3951,11 @@ var MODEL_CATALOG2 = [
|
|
|
3637
3951
|
// ── KIE.ai video: the cheaper route to the same upstream models ──────────
|
|
3638
3952
|
// Async via KIE's Veo and createTask APIs (providers/kie-video.ts). 20 to 85%
|
|
3639
3953
|
// below fal (KIE price feed, 2026-09-23). KIE offers no data processing
|
|
3640
|
-
// agreement
|
|
3641
|
-
//
|
|
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.
|
|
3642
3959
|
{
|
|
3643
3960
|
id: "kie/veo-3-1-lite",
|
|
3644
3961
|
provider: "kie",
|
|
@@ -3647,6 +3964,7 @@ var MODEL_CATALOG2 = [
|
|
|
3647
3964
|
label: "Veo 3.1 Lite (KIE)",
|
|
3648
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.",
|
|
3649
3966
|
cost: { usd: 0.15 },
|
|
3967
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-lite" },
|
|
3650
3968
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3651
3969
|
},
|
|
3652
3970
|
{
|
|
@@ -3657,6 +3975,7 @@ var MODEL_CATALOG2 = [
|
|
|
3657
3975
|
label: "Veo 3.1 Fast (KIE)",
|
|
3658
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.",
|
|
3659
3977
|
cost: { usd: 0.3 },
|
|
3978
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1-fast" },
|
|
3660
3979
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3661
3980
|
},
|
|
3662
3981
|
{
|
|
@@ -3667,6 +3986,7 @@ var MODEL_CATALOG2 = [
|
|
|
3667
3986
|
label: "Veo 3.1 (KIE)",
|
|
3668
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).",
|
|
3669
3988
|
cost: { usd: 1.25 },
|
|
3989
|
+
listPrice: { source: "owner", model: "openrouter/veo-3-1" },
|
|
3670
3990
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3671
3991
|
},
|
|
3672
3992
|
// ── OpenRouter video: Google Veo 3.1 at Google's own prices ───────────────
|
|
@@ -3713,6 +4033,7 @@ var MODEL_CATALOG2 = [
|
|
|
3713
4033
|
label: "Grok Imagine (KIE)",
|
|
3714
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.",
|
|
3715
4035
|
cost: { usd: 0.135 },
|
|
4036
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-image-to-video" },
|
|
3716
4037
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
3717
4038
|
},
|
|
3718
4039
|
{
|
|
@@ -3723,6 +4044,7 @@ var MODEL_CATALOG2 = [
|
|
|
3723
4044
|
label: "Grok Imagine 1.5 (KIE, preview)",
|
|
3724
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.",
|
|
3725
4046
|
cost: { usd: 0.18 },
|
|
4047
|
+
listPrice: { source: "fal", model: "fal/grok-imagine-1-5-image-to-video" },
|
|
3726
4048
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
3727
4049
|
},
|
|
3728
4050
|
{
|
|
@@ -3733,6 +4055,7 @@ var MODEL_CATALOG2 = [
|
|
|
3733
4055
|
label: "Kling 2.5 Turbo Pro (KIE)",
|
|
3734
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).",
|
|
3735
4057
|
cost: { usd: 0.21 },
|
|
4058
|
+
listPrice: { source: "fal", model: "fal/kling-v2-5-image-to-video" },
|
|
3736
4059
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3737
4060
|
},
|
|
3738
4061
|
{
|
|
@@ -3743,6 +4066,7 @@ var MODEL_CATALOG2 = [
|
|
|
3743
4066
|
label: "Kling 2.6 (KIE)",
|
|
3744
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).",
|
|
3745
4068
|
cost: { usd: 0.275 },
|
|
4069
|
+
listPrice: { source: "fal", model: "fal/kling-2-6-image-to-video" },
|
|
3746
4070
|
capabilities: { needsImageInput: true, maxInputImages: 1 }
|
|
3747
4071
|
},
|
|
3748
4072
|
{
|
|
@@ -3754,6 +4078,7 @@ var MODEL_CATALOG2 = [
|
|
|
3754
4078
|
refusesPhotorealPeople: true,
|
|
3755
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).",
|
|
3756
4080
|
cost: { usd: 1.025 },
|
|
4081
|
+
listPrice: { source: "fal", model: "fal/seedance-2-0-image-to-video" },
|
|
3757
4082
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3758
4083
|
},
|
|
3759
4084
|
{
|
|
@@ -3765,6 +4090,7 @@ var MODEL_CATALOG2 = [
|
|
|
3765
4090
|
refusesPhotorealPeople: true,
|
|
3766
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.",
|
|
3767
4092
|
cost: { usd: 1.575 },
|
|
4093
|
+
listPrice: { source: "fal", model: "fal/seedance-2-5-image-to-video" },
|
|
3768
4094
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3769
4095
|
},
|
|
3770
4096
|
{
|
|
@@ -3775,6 +4101,7 @@ var MODEL_CATALOG2 = [
|
|
|
3775
4101
|
label: "Wan 3.0 (KIE)",
|
|
3776
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).",
|
|
3777
4103
|
cost: { usd: 0.4 },
|
|
4104
|
+
listPrice: { source: "fal", model: "fal/wan-3-0-image-to-video" },
|
|
3778
4105
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3779
4106
|
},
|
|
3780
4107
|
{
|
|
@@ -3785,6 +4112,7 @@ var MODEL_CATALOG2 = [
|
|
|
3785
4112
|
label: "MiniMax H3 (KIE)",
|
|
3786
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.",
|
|
3787
4114
|
cost: { usd: 0.24 },
|
|
4115
|
+
listPrice: { source: "unrecorded" },
|
|
3788
4116
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3789
4117
|
},
|
|
3790
4118
|
{
|
|
@@ -3795,6 +4123,7 @@ var MODEL_CATALOG2 = [
|
|
|
3795
4123
|
label: "Gemini Omni 1.1 Flash (KIE)",
|
|
3796
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.",
|
|
3797
4125
|
cost: { usd: 0.525 },
|
|
4126
|
+
listPrice: { source: "unrecorded" },
|
|
3798
4127
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3799
4128
|
},
|
|
3800
4129
|
{
|
|
@@ -3805,6 +4134,7 @@ var MODEL_CATALOG2 = [
|
|
|
3805
4134
|
label: "Kling O3 (KIE)",
|
|
3806
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.",
|
|
3807
4136
|
cost: { usd: 0.35 },
|
|
4137
|
+
listPrice: { source: "unrecorded" },
|
|
3808
4138
|
capabilities: { needsImageInput: true, maxInputImages: 1, acceptsEndFrame: true }
|
|
3809
4139
|
},
|
|
3810
4140
|
{
|
|
@@ -4123,6 +4453,7 @@ var MODEL_CATALOG2 = [
|
|
|
4123
4453
|
label: "Suno V5 (KIE)",
|
|
4124
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.",
|
|
4125
4455
|
cost: { usd: 0.04 },
|
|
4456
|
+
listPrice: { source: "unrecorded" },
|
|
4126
4457
|
isDefault: true
|
|
4127
4458
|
},
|
|
4128
4459
|
// ── audio: voice via ElevenLabs (B2) ───────────────────────────────────
|
|
@@ -4171,6 +4502,7 @@ var MODEL_CATALOG2 = [
|
|
|
4171
4502
|
label: "Claude Sonnet 4.5 (KIE)",
|
|
4172
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.",
|
|
4173
4504
|
cost: { usd: 0.01 },
|
|
4505
|
+
listPrice: { source: "unrecorded" },
|
|
4174
4506
|
isDefault: true
|
|
4175
4507
|
},
|
|
4176
4508
|
{
|
|
@@ -4180,7 +4512,8 @@ var MODEL_CATALOG2 = [
|
|
|
4180
4512
|
task: "text-generation",
|
|
4181
4513
|
label: "Claude Opus 4.5 (KIE)",
|
|
4182
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.",
|
|
4183
|
-
cost: { usd: 0.05 }
|
|
4515
|
+
cost: { usd: 0.05 },
|
|
4516
|
+
listPrice: { source: "unrecorded" }
|
|
4184
4517
|
},
|
|
4185
4518
|
{
|
|
4186
4519
|
id: "openrouter/claude-sonnet-4-6",
|
|
@@ -4245,8 +4578,26 @@ var MODEL_CATALOG2 = [
|
|
|
4245
4578
|
description: "Vertex AI direct, newest flash variant. Global region. Preview status."
|
|
4246
4579
|
}
|
|
4247
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
|
+
}
|
|
4248
4599
|
function tryGetModel2(id) {
|
|
4249
|
-
return MODEL_CATALOG2.find((m) => m.id === id);
|
|
4600
|
+
return MODEL_CATALOG2.find((m) => m.id === id) ?? routeEntry2(id);
|
|
4250
4601
|
}
|
|
4251
4602
|
function supportedResolutions(model) {
|
|
4252
4603
|
const declared = model?.capabilities?.resolutions;
|
|
@@ -5893,7 +6244,7 @@ function nodeUrl(res, nodeId) {
|
|
|
5893
6244
|
}
|
|
5894
6245
|
var studioListProjects = defineTool({
|
|
5895
6246
|
name: "studio_list_projects",
|
|
5896
|
-
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.',
|
|
5897
6248
|
inputSchema: {},
|
|
5898
6249
|
annotations: { readOnlyHint: true },
|
|
5899
6250
|
handler: async (_args, ctx) => {
|
|
@@ -5903,17 +6254,26 @@ var studioListProjects = defineTool({
|
|
|
5903
6254
|
});
|
|
5904
6255
|
var studioGenerateImage = defineTool({
|
|
5905
6256
|
name: "studio_generate_image",
|
|
5906
|
-
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.',
|
|
5907
6258
|
inputSchema: {
|
|
5908
|
-
brandSlug: slug.
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
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
|
+
),
|
|
5912
6270
|
model: z8.string().optional().describe('Image model id (e.g. "kie/nano-banana-2", "fal/nano-banana-2"). Omit for the Studio default.'),
|
|
5913
6271
|
aspectRatio: z8.string().optional().describe('Aspect ratio such as "1:1", "16:9", "3:4".'),
|
|
5914
6272
|
imageSize: z8.string().optional().describe('Provider size hint, e.g. "1K", "2K".'),
|
|
5915
6273
|
seed: z8.number().int().optional(),
|
|
5916
|
-
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
|
+
),
|
|
5917
6277
|
wait: z8.boolean().default(true).describe("Poll until the job finishes (default true)."),
|
|
5918
6278
|
waitSeconds
|
|
5919
6279
|
},
|
|
@@ -5925,19 +6285,24 @@ var studioGenerateImage = defineTool({
|
|
|
5925
6285
|
if (args.seed !== void 0) settings.seed = args.seed;
|
|
5926
6286
|
if (args.candidateCount !== void 0) settings.candidateCount = args.candidateCount;
|
|
5927
6287
|
const submitted = await ctx.client.generate({
|
|
5928
|
-
brandSlug: args.brandSlug,
|
|
6288
|
+
...args.brandSlug ? { brandSlug: args.brandSlug } : {},
|
|
5929
6289
|
...args.brandMode ? { brandMode: args.brandMode } : {},
|
|
5930
6290
|
prompt: args.prompt,
|
|
5931
6291
|
...args.characterSlug ? { characterSlug: args.characterSlug } : {},
|
|
6292
|
+
...args.projectId ? { projectId: args.projectId } : {},
|
|
5932
6293
|
...Object.keys(settings).length > 0 ? { settings } : {}
|
|
5933
6294
|
});
|
|
5934
|
-
|
|
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 };
|
|
5935
6300
|
const done = await waitForJob(ctx.client, submitted.jobId, pollOpts(ctx, args.waitSeconds));
|
|
5936
6301
|
const out = projectJob(done);
|
|
5937
6302
|
if (done.job.status !== "succeeded") {
|
|
5938
6303
|
throw new Error(`Job ${submitted.jobId} ${done.job.status}: ${done.job.errorMessage ?? "no error message from the API"}`);
|
|
5939
6304
|
}
|
|
5940
|
-
return { ...out, kind: submitted.kind };
|
|
6305
|
+
return { ...out, kind: submitted.kind, ...plan };
|
|
5941
6306
|
}
|
|
5942
6307
|
});
|
|
5943
6308
|
var GENERATE_3D_TASKS = /* @__PURE__ */ new Set(["text-to-3d", "image-to-3d"]);
|
|
@@ -6083,7 +6448,7 @@ var studioGetJob = defineTool({
|
|
|
6083
6448
|
});
|
|
6084
6449
|
var studioListCharacters = defineTool({
|
|
6085
6450
|
name: "studio_list_characters",
|
|
6086
|
-
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.",
|
|
6087
6452
|
inputSchema: {},
|
|
6088
6453
|
annotations: { readOnlyHint: true },
|
|
6089
6454
|
handler: async (_args, ctx) => {
|
|
@@ -6093,7 +6458,7 @@ var studioListCharacters = defineTool({
|
|
|
6093
6458
|
});
|
|
6094
6459
|
var studioGetCharacter = defineTool({
|
|
6095
6460
|
name: "studio_get_character",
|
|
6096
|
-
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.",
|
|
6097
6462
|
inputSchema: { slug },
|
|
6098
6463
|
annotations: { readOnlyHint: true },
|
|
6099
6464
|
handler: async (args, ctx) => {
|
|
@@ -6108,7 +6473,7 @@ function assertLikenessRightsConfirmed(value) {
|
|
|
6108
6473
|
}
|
|
6109
6474
|
var studioCreateCharacter = defineTool({
|
|
6110
6475
|
name: "studio_create_character",
|
|
6111
|
-
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.",
|
|
6112
6477
|
inputSchema: {
|
|
6113
6478
|
name: z8.string().min(1).describe('Display name, e.g. "Bruno Hartmann".'),
|
|
6114
6479
|
slug,
|
|
@@ -6200,7 +6565,7 @@ var studioMintCharacter = defineTool({
|
|
|
6200
6565
|
});
|
|
6201
6566
|
var studioCreateBrand = defineTool({
|
|
6202
6567
|
name: "studio_create_brand",
|
|
6203
|
-
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.",
|
|
6204
6569
|
inputSchema: {
|
|
6205
6570
|
name: z8.string().min(1),
|
|
6206
6571
|
slug,
|
|
@@ -6225,7 +6590,7 @@ var studioUploadBrandReference = defineTool({
|
|
|
6225
6590
|
name: "studio_upload_brand_reference",
|
|
6226
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.`,
|
|
6227
6592
|
inputSchema: {
|
|
6228
|
-
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."),
|
|
6229
6594
|
source: z8.string().min(1).describe("Local file path or http(s) URL of an image."),
|
|
6230
6595
|
category: z8.string().optional().describe('Library category, default "general".'),
|
|
6231
6596
|
label: z8.string().optional().describe("Optional file name / label."),
|
|
@@ -6347,7 +6712,9 @@ var studioReplaceCharacterInVideo = defineTool({
|
|
|
6347
6712
|
inputSchema: {
|
|
6348
6713
|
projectId: z8.string().min(1).describe("Project id from studio_list_projects."),
|
|
6349
6714
|
sceneFrameUrl: httpUrl.describe("Public or signed URL of ONE frame of the target shot (png/jpg)."),
|
|
6350
|
-
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
|
+
),
|
|
6351
6718
|
characterReferenceUrl: httpUrl.optional().describe("Explicit character reference image URL (use instead of characterSlug)."),
|
|
6352
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."),
|
|
6353
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."),
|
|
@@ -6420,7 +6787,7 @@ var studioReplaceCharacterInVideo = defineTool({
|
|
|
6420
6787
|
});
|
|
6421
6788
|
var studioGetSpend = defineTool({
|
|
6422
6789
|
name: "studio_get_spend",
|
|
6423
|
-
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).",
|
|
6424
6791
|
inputSchema: {
|
|
6425
6792
|
tzOffsetMinutes: z8.number().int().min(-840).max(840).optional()
|
|
6426
6793
|
},
|
|
@@ -6432,7 +6799,7 @@ var studioGetSpend = defineTool({
|
|
|
6432
6799
|
});
|
|
6433
6800
|
var studioGetUsage = defineTool({
|
|
6434
6801
|
name: "studio_get_usage",
|
|
6435
|
-
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.",
|
|
6436
6803
|
inputSchema: {},
|
|
6437
6804
|
annotations: { readOnlyHint: true },
|
|
6438
6805
|
handler: async (_args, ctx) => {
|
|
@@ -6552,7 +6919,7 @@ function registerTools(server, ctx) {
|
|
|
6552
6919
|
|
|
6553
6920
|
// src/server.ts
|
|
6554
6921
|
var SERVER_NAME = "lumitra-studio";
|
|
6555
|
-
var SERVER_VERSION = "0.
|
|
6922
|
+
var SERVER_VERSION = "0.5.0";
|
|
6556
6923
|
function createStudioMcpServer(opts = {}) {
|
|
6557
6924
|
const config = opts.config ?? resolveConfig(opts.env ?? process.env);
|
|
6558
6925
|
const client = new StudioClient(config, {
|
|
@@ -6564,7 +6931,7 @@ function createStudioMcpServer(opts = {}) {
|
|
|
6564
6931
|
{
|
|
6565
6932
|
instructions: [
|
|
6566
6933
|
"Lumitra Studio: brand-consistent image generation, character minting, workflow runs and the still-swap video recipe over the hosted Studio API.",
|
|
6567
|
-
"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.",
|
|
6568
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.",
|
|
6569
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.",
|
|
6570
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."
|
|
@@ -6605,7 +6972,7 @@ Usage:
|
|
|
6605
6972
|
studio-mcp --http --host 0.0.0.0 --port 3939
|
|
6606
6973
|
|
|
6607
6974
|
Environment:
|
|
6608
|
-
LUMITRA_STUDIO_API_KEY bearer token (required
|
|
6975
|
+
LUMITRA_STUDIO_API_KEY bearer token (required)
|
|
6609
6976
|
LUMITRA_STUDIO_BASE_URL default https://studio.lumitra.co
|
|
6610
6977
|
|
|
6611
6978
|
Tools: ${TOOL_NAMES.join(", ")}
|