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