@milenyumai/film-kit 2.3.3 → 2.3.4

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.
@@ -0,0 +1,136 @@
1
+ # Film-Kit Model Registry
2
+
3
+ Film-Kit model metadata is centralized in `src/lib/model-registry/*`.
4
+
5
+ The registry is TypeScript `as const` metadata, not dynamic JSON. It is the canonical source for model identity, lifecycle, preset compatibility, reference-mode compatibility, capabilities, routing role, prompt grammar key, model profile key, and storyboard adapter resolution.
6
+
7
+ Long prompt grammar, runtime profile prose, production guidance, and generated workflow text stay in the existing template, adapter, skill, and model-profile layers.
8
+
9
+ ## Discovery Summary
10
+
11
+ Phase 0 found model ids in four broad surfaces:
12
+
13
+ - Root validation and runtime selection: `src/lib/types.ts`, `src/lib/defaults.ts`, `src/lib/configure.ts`, `src/lib/cli.ts`, and `src/lib/film-kit.ts`.
14
+ - Storyboard-reference routing: `src/lib/storyboard-reference/defaults.ts`, `request-normalizer.ts`, `prompt-bundle-builder.ts`, and `adapters/*`.
15
+ - Internal preset packages: `packages/*/src/lib/*`, where model ids still appear in private preset defaults, templates, and generated runtime prose.
16
+ - Public/user-facing prose and assertions: `README.md`, `content/*`, `packages/*/content/*`, and tests.
17
+
18
+ Phase 1 moved the root runtime, CLI/config validation, `generate-storyboard --models`, storyboard-reference target validation, display-name lookup, default storyboard model list, and adapter resolution onto the registry.
19
+
20
+ Phase 2 intentionally does not import the root registry directly from `packages/*`: package `tsconfig.json` files use `rootDir: src`, and the internal package sources remain private preset implementations. Public usage is guarded through the root dispatcher and root CLI.
21
+
22
+ Remaining hardcoded model names in templates, content, and tests are prompt grammar, generated docs, explicit examples, or compatibility assertions rather than canonical validation sources.
23
+
24
+ ## Files
25
+
26
+ - `src/lib/model-registry/types.ts` defines registry metadata shapes.
27
+ - `src/lib/model-registry/registry.ts` owns the canonical model entries.
28
+ - `src/lib/model-registry/selectors.ts` exposes lookup and selector helpers.
29
+ - `src/lib/model-registry/validation.ts` owns fail-closed model validation.
30
+ - `src/lib/model-registry/index.ts` is the public registry API barrel.
31
+
32
+ ## Public API
33
+
34
+ - `getModel(id)`
35
+ - `requireModel(id)`
36
+ - `listModels()`
37
+ - `listActiveModels()`
38
+ - `listVideoModels()`
39
+ - `listImageModels()`
40
+ - `listActiveVideoModelIds()`
41
+ - `listRunnableVideoModels()`
42
+ - `listRunnableVideoModelIds()`
43
+ - `listModelsForPreset(preset)`
44
+ - `listModelsForReferenceMode(referenceMode)`
45
+ - `isKnownModelId(value)`
46
+ - `isActiveModelId(value)`
47
+ - `isDeprecatedModelId(value)`
48
+ - `isRemovedModelId(value)`
49
+ - `isVideoModelId(value)`
50
+ - `isImageModelId(value)`
51
+ - `isModelSupportedByPreset(modelId, preset)`
52
+ - `getModelAdapterKey(modelId)`
53
+ - `getModelDisplayName(modelId)`
54
+ - `getReplacementModel(modelId)`
55
+ - `assertModelAllowed(modelId, context)`
56
+ - `validateModelAllowed(modelId, context)`
57
+
58
+ ## Lifecycle Rules
59
+
60
+ - `active`: accepted normally.
61
+ - `experimental`: accepted normally, but may be surfaced as internal or preview metadata.
62
+ - `deprecated`: accepted, emits an explicit warning, and should define `replacementModelId` when possible.
63
+ - `removed`: fail-closed. Removed models must never silently fall back.
64
+
65
+ ## Adapter Routing
66
+
67
+ Storyboard-reference video prompt routing resolves through `adapterKey`.
68
+
69
+ - `veo31` -> Veo adapter
70
+ - `seedance20` -> Seedance 2.0 adapter
71
+ - `kling30` -> Kling 3.0 adapter
72
+ - `generic` -> generic adapter, allowed only when the registry entry explicitly selects it
73
+ - `none`, `gptImage2` -> not valid for video storyboard adapter routing
74
+
75
+ Unknown model strings never receive a generic fallback.
76
+
77
+ ## Adding A Video Model
78
+
79
+ 1. Add one entry in `src/lib/model-registry/registry.ts`.
80
+ 2. Choose `modalities: ["video"]`.
81
+ 3. Set `lifecycleStatus`.
82
+ 4. Set `supportedPresets`.
83
+ 5. Set `supportedReferenceModes`.
84
+ 6. Set `supportedAspects`.
85
+ 7. Add `durationRange` or `durationPolicy`.
86
+ 8. Choose an existing `adapterKey` or add a new adapter.
87
+ 9. Set `promptGrammarKey` and `modelProfileKey`.
88
+ 10. Fill `routingRole` and `capabilities`.
89
+ 11. Add registry selector/validation tests.
90
+ 12. Add storyboard adapter routing tests when the model can generate video prompts.
91
+ 13. Update docs/examples only if the model should be publicly visible.
92
+
93
+ Ideal new-video-model diff:
94
+
95
+ - one registry entry
96
+ - one adapter only if no existing adapter fits
97
+ - focused tests
98
+ - docs update when public
99
+
100
+ ## Adding An Image Model
101
+
102
+ 1. Add one registry entry with `modalities: ["image"]`.
103
+ 2. Do not add it to video-only CLI or storyboard video target validation.
104
+ 3. Pick image-aware `supportedPresets`.
105
+ 4. Use `adapterKey: "gptImage2"` only for GPT Image 2 behavior; use `none` or a future image adapter key otherwise.
106
+ 5. Add tests proving it appears in image selectors and not in video selectors.
107
+
108
+ Image models must not widen video model types or `generate-storyboard --models`.
109
+
110
+ ## Deprecating A Model
111
+
112
+ 1. Change `lifecycleStatus` to `deprecated`.
113
+ 2. Add `deprecationMessage`.
114
+ 3. Add `replacementModelId` when there is a direct replacement.
115
+ 4. Keep compatibility metadata only for paths that still work.
116
+ 5. Add tests proving validation emits a warning.
117
+
118
+ Do not silently rewrite deprecated model ids unless a dedicated migration test covers that behavior.
119
+
120
+ ## Removing A Model
121
+
122
+ 1. Change `lifecycleStatus` to `removed`.
123
+ 2. Keep the id in the registry so validation can fail with a clear message.
124
+ 3. Add `deprecationMessage` and `replacementModelId` when possible.
125
+ 4. Clear runtime compatibility if the model must not be routed.
126
+ 5. Add tests proving `assertModelAllowed` fails closed.
127
+
128
+ Removed models must not fall back to active models or generic adapters.
129
+
130
+ ## Phase Boundaries
131
+
132
+ Phase 1 connects the root runtime, CLI/config validation, and storyboard-reference adapter routing to the registry.
133
+
134
+ Phase 2 keeps internal preset packages private. Package source builds use `rootDir: src`, so direct imports from root `src/lib/model-registry` would break package builds or create circular product coupling. Root `configureFilmKit()` and CLI validation are the canonical guardrails for public usage. Internal package prompt/template prose can continue to contain model names as generated runtime content.
135
+
136
+ Phase 3 is this maintenance documentation plus README linkage.
package/README.md CHANGED
@@ -70,6 +70,8 @@ Storyboard-reference prompt generation is also available when you have a charact
70
70
 
71
71
  ## Model Support
72
72
 
73
+ Canonical model metadata lives in `src/lib/model-registry/*`; see [MODEL_REGISTRY.md](./MODEL_REGISTRY.md) for the add/update/deprecate/remove checklist. Public model selection still happens at init time through the root package and CLI.
74
+
73
75
  ### `veo31`
74
76
 
75
77
  - Default single, multi, and studio model
@@ -484,6 +486,25 @@ Public root exports:
484
486
  - `FILM_KIT_CONFIG_FILE`
485
487
  - `FILM_KIT_PRESETS`
486
488
  - `LEGACY_CONFIG_FILES`
489
+ - `getModel(id)`
490
+ - `requireModel(id)`
491
+ - `listModels()`
492
+ - `listActiveModels()`
493
+ - `listVideoModels()`
494
+ - `listImageModels()`
495
+ - `listModelsForPreset(preset)`
496
+ - `listModelsForReferenceMode(referenceMode)`
497
+ - `isKnownModelId(value)`
498
+ - `isActiveModelId(value)`
499
+ - `isDeprecatedModelId(value)`
500
+ - `isRemovedModelId(value)`
501
+ - `isVideoModelId(value)`
502
+ - `isImageModelId(value)`
503
+ - `isModelSupportedByPreset(modelId, preset)`
504
+ - `getModelAdapterKey(modelId)`
505
+ - `getReplacementModel(modelId)`
506
+ - `assertModelAllowed(id, context)`
507
+ - `validateModelAllowed(id, context)`
487
508
  - `FilmKitPreset`
488
509
  - `SupportedModel`
489
510
  - `SupportedPlatform`
package/build/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { configureFilmKit, detectFilmKitPreset, FILM_KIT_CONFIG_FILE, FILM_KIT_PRESETS, LEGACY_CONFIG_FILES } from "./lib/film-kit.js";
2
2
  export { configureAgents } from "./lib/configure.js";
3
3
  export { buildStoryboardReferencePromptBundles, normalizeVideoPromptRequest, renderShotMarkdown, writeStoryboardReferenceOutputs } from "./lib/storyboard-reference/index.js";
4
+ export { assertModelAllowed, getModel, getModelAdapterKey, getModelDisplayName, getReplacementModel, isActiveModelId, isDeprecatedModelId, isImageModelId, isKnownModelId, isModelSupportedByPreset, isRemovedModelId, isVideoModelId, listActiveModels, listActiveVideoModelIds, listImageModels, listModels, listModelsForPreset, listModelsForReferenceMode, listRunnableVideoModelIds, listRunnableVideoModels, listVideoModels, requireModel, validateModelAllowed } from "./lib/model-registry/index.js";
4
5
  export type { AgentConfigOptions, ConfigureFilmKitResult, ConfigureResult, FilmKitConfigFile, FilmKitConfigOptions, FilmKitPreset, GptImageFormat, GptImageQuality, GptImageSize, HybridAspectRatio, KlingPreset, NanoBananaImageSize, ReferenceMode, StoryboardReferenceConfig, SupportedModel, SupportedPlatform } from "./lib/types.js";
6
+ export type { ActiveModelId, AudioModelId, DeprecatedModelId, ImageModelId, KnownModelId, ModelAdapterKey, ModelAllowedContext, ModelAllowedResult, ModelCapabilities, ModelLifecycleStatus, ModelModality, ModelRegistryEntry, MultimodalModelId, RemovedModelId, VideoModelId } from "./lib/model-registry/index.js";
5
7
  export type { CharacterReferenceSheetPrompt, ModelPromptOutput, ModelRouteMetadata, PromptBundle, ProviderAssetMappingEntry, ProviderFileLimitReport, ProviderReferenceTokenPolicy, ReferenceAsset, ReferenceAssetRequirement, SeedanceContinuityMode, ShotHandoffNote, StoryboardImagePrompt, StoryboardPhaseBudget, StoryboardReferenceBuildResult, StoryboardReferencePolicy, StoryboardReferenceWriteResult, VideoPromptRequest } from "./lib/storyboard-reference/index.js";
package/build/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { configureFilmKit, detectFilmKitPreset, FILM_KIT_CONFIG_FILE, FILM_KIT_PRESETS, LEGACY_CONFIG_FILES } from "./lib/film-kit.js";
2
2
  export { configureAgents } from "./lib/configure.js";
3
3
  export { buildStoryboardReferencePromptBundles, normalizeVideoPromptRequest, renderShotMarkdown, writeStoryboardReferenceOutputs } from "./lib/storyboard-reference/index.js";
4
+ export { assertModelAllowed, getModel, getModelAdapterKey, getModelDisplayName, getReplacementModel, isActiveModelId, isDeprecatedModelId, isImageModelId, isKnownModelId, isModelSupportedByPreset, isRemovedModelId, isVideoModelId, listActiveModels, listActiveVideoModelIds, listImageModels, listModels, listModelsForPreset, listModelsForReferenceMode, listRunnableVideoModelIds, listRunnableVideoModels, listVideoModels, requireModel, validateModelAllowed } from "./lib/model-registry/index.js";
package/build/lib/cli.js CHANGED
@@ -2,7 +2,7 @@ import { FILM_KIT_CONFIG_FILE, FILM_KIT_PRESETS, detectFilmKitPreset, isFilmKitP
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
4
  import { exists } from "./fs.js";
5
- const SUPPORTED_MODELS = ["veo31", "kling-3.0", "seedance-2.0"];
5
+ import { listActiveVideoModelIds, validateModelAllowed } from "./model-registry/index.js";
6
6
  const SUPPORTED_KLING_PRESETS = ["ultra-realism", "balanced", "custom"];
7
7
  const SUPPORTED_ASPECTS = ["16:9", "9:16", "1:1"];
8
8
  const SUPPORTED_REFERENCE_MODES = ["start-end", "storyboard-reference", "hybrid"];
@@ -87,6 +87,9 @@ const PRESET_ALLOWED_FLAGS = {
87
87
  ])
88
88
  };
89
89
  const COMMON_FLAGS = new Set(["--preset", "--output-dir", "--scenario-hint", "--scenario", "--overwrite", "-f"]);
90
+ function supportedVideoModelText() {
91
+ return listActiveVideoModelIds().join(", ");
92
+ }
90
93
  function parseIntegerFlag(raw, flag) {
91
94
  const value = Number.parseInt(raw, 10);
92
95
  if (!Number.isInteger(value) || value <= 0) {
@@ -181,10 +184,10 @@ function parseKnownFlags(args) {
181
184
  options.platforms = parsePlatforms(nextValue);
182
185
  break;
183
186
  case "--model":
184
- if (!SUPPORTED_MODELS.includes(nextValue)) {
185
- throw new Error(`Invalid --model value: ${nextValue}. Supported: ${SUPPORTED_MODELS.join(", ")}`);
186
- }
187
- options.model = nextValue;
187
+ options.model = validateModelAllowed(nextValue, {
188
+ requiredModalities: ["video"],
189
+ usage: "--model"
190
+ }).model.id;
188
191
  break;
189
192
  case "--kling-preset":
190
193
  if (!SUPPORTED_KLING_PRESETS.includes(nextValue)) {
@@ -293,9 +296,11 @@ function parseModels(raw) {
293
296
  throw new Error("Invalid --models value. Expected at least one supported model.");
294
297
  }
295
298
  for (const model of models) {
296
- if (!SUPPORTED_MODELS.includes(model)) {
297
- throw new Error(`Invalid --models value: ${model}. Supported: ${SUPPORTED_MODELS.join(", ")}`);
298
- }
299
+ validateModelAllowed(model, {
300
+ requiredModalities: ["video"],
301
+ referenceMode: "storyboard-reference",
302
+ usage: "--models"
303
+ });
299
304
  }
300
305
  return models;
301
306
  }
@@ -472,6 +477,17 @@ export async function parseCliCommand(args, rootDir = process.cwd()) {
472
477
  `You can also commit ${FILM_KIT_CONFIG_FILE} and rerun init.`);
473
478
  }
474
479
  validatePresetFlags(preset, providedFlags);
480
+ if (options.model) {
481
+ const validationContext = {
482
+ preset,
483
+ requiredModalities: ["video"],
484
+ usage: "--model"
485
+ };
486
+ if (options.referenceMode !== undefined) {
487
+ validationContext.referenceMode = options.referenceMode;
488
+ }
489
+ validateModelAllowed(options.model, validationContext);
490
+ }
475
491
  return {
476
492
  command: "init",
477
493
  options: {
@@ -518,7 +534,7 @@ generate-storyboard flags:
518
534
  --character-ref Character identity reference image path or URL (repeatable or comma-separated)
519
535
  --storyboard-ref Optional storyboard guide image path or URL (repeatable or comma-separated)
520
536
  --brief Inline brief text or a path to a brief file
521
- --models Comma-separated models: veo31,seedance-2.0,kling-3.0
537
+ --models Comma-separated models: ${supportedVideoModelText()}
522
538
  --duration Duration in seconds per generated shot
523
539
  --aspect 16:9|9:16|1:1
524
540
  --output-dir Output root (default: ./outputs)
@@ -3,6 +3,7 @@ import { fileURLToPath } from "node:url";
3
3
  import { readdir, readFile, rm } from "node:fs/promises";
4
4
  import { exists, readJsonIfExists, writeText } from "./fs.js";
5
5
  import { resolveOptions } from "./defaults.js";
6
+ import { getModelDisplayName as getRegistryModelDisplayName } from "./model-registry/index.js";
6
7
  import { buildProjectFiles } from "./templates.js";
7
8
  const __filename = fileURLToPath(import.meta.url);
8
9
  const __dirname = dirname(__filename);
@@ -549,13 +550,6 @@ description: Storyboard-reference prompt structure for GPT Image 2 character she
549
550
  Do not emit main-shot \`ILK FRAME\`, \`İLK FRAME\`, \`SON FRAME\`, \`SHOTNN_START\`, \`SHOTNN_END\`, or exact first-frame reuse instructions in storyboard-reference mode.
550
551
  `;
551
552
  }
552
- function getModelDisplayName(model) {
553
- if (model === "kling-3.0")
554
- return "Kling 3.0";
555
- if (model === "seedance-2.0")
556
- return "Seedance 2.0";
557
- return "Google Flow + Veo 3.1";
558
- }
559
553
  function getKlingPresetDisplay(model, klingPreset) {
560
554
  return model === "kling-3.0" ? klingPreset : "n/a (Kling-only)";
561
555
  }
@@ -622,7 +616,7 @@ export async function configureAgents(options = {}) {
622
616
  scenarioHint: resolved.scenarioHint,
623
617
  model: resolved.model,
624
618
  klingPreset: getKlingPresetDisplay(resolved.model, resolved.klingPreset),
625
- modelDisplayName: getModelDisplayName(resolved.model),
619
+ modelDisplayName: getRegistryModelDisplayName(resolved.model),
626
620
  referenceMode: resolved.referenceMode,
627
621
  storyboardReferenceActive,
628
622
  maxStoryboardPhases: resolved.storyboardReferenceMode.maxStoryboardPhases
@@ -1,3 +1,4 @@
1
+ import { validateModelAllowed } from "./model-registry/index.js";
1
2
  const ALL_PLATFORMS = ["cursor", "claude", "copilot", "antigravity", "codex"];
2
3
  const DEFAULT_MODEL = "veo31";
3
4
  const DEFAULT_KLING_PRESET = "ultra-realism";
@@ -31,12 +32,21 @@ export function normalizeStoryboardReferenceConfig(input, referenceMode) {
31
32
  export function resolveOptions(input) {
32
33
  const warnings = [];
33
34
  const model = input.model ?? DEFAULT_MODEL;
35
+ const modelValidationContext = {
36
+ requiredModalities: ["video"],
37
+ usage: "runtime"
38
+ };
39
+ if (input.referenceMode !== undefined) {
40
+ modelValidationContext.referenceMode = input.referenceMode;
41
+ }
42
+ const modelValidation = validateModelAllowed(model, modelValidationContext);
34
43
  const klingPreset = input.klingPreset ?? DEFAULT_KLING_PRESET;
35
44
  const referenceMode = input.referenceMode ?? DEFAULT_REFERENCE_MODE;
36
45
  const storyboardReferenceMode = normalizeStoryboardReferenceConfig(input.storyboardReferenceMode, referenceMode);
37
46
  if (!input.model) {
38
47
  warnings.push("Model not specified. Falling back to veo31. In the next major release, --model will be required.");
39
48
  }
49
+ warnings.push(...modelValidation.warnings);
40
50
  if (referenceMode === "storyboard-reference" && !storyboardReferenceMode.enabled) {
41
51
  warnings.push("referenceMode is storyboard-reference, but storyboardReferenceMode.enabled is false.");
42
52
  }
@@ -3,6 +3,7 @@ import { fileURLToPath } from "node:url";
3
3
  import { configureAgents } from "./configure.js";
4
4
  import { resolveOptions } from "./defaults.js";
5
5
  import { exists, readJsonIfExists, writeText } from "./fs.js";
6
+ import { validateModelAllowed } from "./model-registry/index.js";
6
7
  export const FILM_KIT_CONFIG_FILE = "film-kit.config.json";
7
8
  export const FILM_KIT_PRESETS = [
8
9
  "single",
@@ -370,6 +371,20 @@ export async function configureFilmKit(options = {}) {
370
371
  assignDefined(merged, options);
371
372
  merged.rootDir = rootDir;
372
373
  merged.preset = preset;
374
+ const rootValidationWarnings = [];
375
+ const mergedModel = readString(merged.model);
376
+ if (mergedModel) {
377
+ const validationContext = {
378
+ preset,
379
+ requiredModalities: ["video"],
380
+ usage: "film-kit.config.json model"
381
+ };
382
+ const mergedReferenceMode = readString(merged.referenceMode);
383
+ if (mergedReferenceMode !== undefined) {
384
+ validationContext.referenceMode = mergedReferenceMode;
385
+ }
386
+ rootValidationWarnings.push(...validateModelAllowed(mergedModel, validationContext).warnings);
387
+ }
373
388
  const runtime = await loadPresetRuntime(preset);
374
389
  const resolved = runtime.resolve(pickPresetOptions(preset, merged));
375
390
  if (resolved.referenceMode === undefined && merged.referenceMode !== undefined) {
@@ -382,7 +397,7 @@ export async function configureFilmKit(options = {}) {
382
397
  const result = await runtime.configure(configureOptions);
383
398
  const configPath = join(rootDir, FILM_KIT_CONFIG_FILE);
384
399
  await writeText(configPath, `${JSON.stringify(buildFilmKitConfigFile(preset, resolved), null, 2)}\n`);
385
- const warnings = [...result.warnings];
400
+ const warnings = [...rootValidationWarnings, ...result.warnings];
386
401
  if (legacyMatch) {
387
402
  warnings.push(`Migrated legacy config '${legacyMatch.filename}' to '${FILM_KIT_CONFIG_FILE}'.`);
388
403
  }
@@ -0,0 +1,4 @@
1
+ export { MODEL_REGISTRY, type ActiveModelId, type AudioModelId, type DeprecatedModelId, type ImageModelId, type KnownModelId, type MultimodalModelId, type RemovedModelId, type VideoModelId } from "./registry.js";
2
+ export { getModel, getModelAdapterKey, getModelDisplayName, getReplacementModel, isActiveModelId, isDeprecatedModelId, isImageModelId, isKnownModelId, isModelSupportedByPreset, isRemovedModelId, isVideoModelId, listActiveModels, listActiveVideoModelIds, listImageModels, listModels, listModelsForPreset, listModelsForReferenceMode, listRunnableVideoModelIds, listRunnableVideoModels, listVideoModels, requireModel } from "./selectors.js";
3
+ export { assertModelAllowed, validateModelAllowed } from "./validation.js";
4
+ export type { ModelAdapterKey, ModelAllowedContext, ModelAllowedResult, ModelCapabilities, ModelDurationRange, ModelLifecycleStatus, ModelModality, ModelProfileKey, ModelRegistryAspectRatio, ModelRegistryEntry, ModelRegistryPreset, ModelRegistryReferenceMode, ModelRoutingRole, PromptGrammarKey } from "./types.js";
@@ -0,0 +1,3 @@
1
+ export { MODEL_REGISTRY } from "./registry.js";
2
+ export { getModel, getModelAdapterKey, getModelDisplayName, getReplacementModel, isActiveModelId, isDeprecatedModelId, isImageModelId, isKnownModelId, isModelSupportedByPreset, isRemovedModelId, isVideoModelId, listActiveModels, listActiveVideoModelIds, listImageModels, listModels, listModelsForPreset, listModelsForReferenceMode, listRunnableVideoModelIds, listRunnableVideoModels, listVideoModels, requireModel } from "./selectors.js";
3
+ export { assertModelAllowed, validateModelAllowed } from "./validation.js";
@@ -0,0 +1,220 @@
1
+ import type { ModelRegistryEntry } from "./types.js";
2
+ export declare const MODEL_REGISTRY: {
3
+ readonly veo31: {
4
+ readonly id: "veo31";
5
+ readonly displayName: "Google Flow + Veo 3.1";
6
+ readonly provider: "google";
7
+ readonly modalities: readonly ["video"];
8
+ readonly lifecycleStatus: "active";
9
+ readonly supportedPresets: readonly ["single", "multi", "studio"];
10
+ readonly supportedReferenceModes: readonly ["start-end", "storyboard-reference"];
11
+ readonly supportedAspects: readonly ["16:9", "9:16", "1:1"];
12
+ readonly durationRange: {
13
+ readonly minSeconds: 4;
14
+ readonly maxSeconds: 8;
15
+ };
16
+ readonly durationPolicy: "Default Film-Kit Veo runtime duration is 8 seconds.";
17
+ readonly adapterKey: "veo31";
18
+ readonly promptGrammarKey: "veo31";
19
+ readonly modelProfileKey: "veo31";
20
+ readonly routingRole: "primary-video";
21
+ readonly capabilities: {
22
+ readonly characterReference: true;
23
+ readonly storyboardReference: true;
24
+ readonly audioPlan: true;
25
+ readonly dialogue: true;
26
+ readonly startEndFrames: true;
27
+ readonly customStoryboardPhases: true;
28
+ readonly maxStoryboardPhases: 4;
29
+ };
30
+ readonly docsVisibility: "public";
31
+ };
32
+ readonly "seedance-2.0": {
33
+ readonly id: "seedance-2.0";
34
+ readonly displayName: "Seedance 2.0";
35
+ readonly provider: "bytedance";
36
+ readonly modalities: readonly ["video"];
37
+ readonly lifecycleStatus: "active";
38
+ readonly supportedPresets: readonly ["single", "multi", "studio"];
39
+ readonly supportedReferenceModes: readonly ["start-end", "storyboard-reference"];
40
+ readonly supportedAspects: readonly ["16:9", "9:16", "1:1"];
41
+ readonly durationRange: {
42
+ readonly minSeconds: 4;
43
+ readonly maxSeconds: 15;
44
+ };
45
+ readonly durationPolicy: "Seedance storyboard-reference policy uses 4-15 second shots.";
46
+ readonly adapterKey: "seedance20";
47
+ readonly promptGrammarKey: "seedance20";
48
+ readonly modelProfileKey: "seedance20";
49
+ readonly routingRole: "storyboard-video";
50
+ readonly capabilities: {
51
+ readonly characterReference: true;
52
+ readonly storyboardReference: true;
53
+ readonly multipleImageReferences: true;
54
+ readonly audioPlan: true;
55
+ readonly dialogue: true;
56
+ readonly customStoryboardPhases: true;
57
+ readonly maxStoryboardPhases: 4;
58
+ readonly maxReferenceImages: 9;
59
+ readonly maxReferenceVideos: 3;
60
+ readonly maxReferenceAudio: 3;
61
+ readonly maxReferenceFiles: 12;
62
+ };
63
+ readonly docsVisibility: "public";
64
+ };
65
+ readonly "kling-3.0": {
66
+ readonly id: "kling-3.0";
67
+ readonly displayName: "Kling 3.0";
68
+ readonly provider: "kuaishou";
69
+ readonly modalities: readonly ["video"];
70
+ readonly lifecycleStatus: "active";
71
+ readonly supportedPresets: readonly ["single", "multi", "hybrid", "hybrid-smart", "gpt-image-smart", "studio"];
72
+ readonly supportedReferenceModes: readonly ["start-end", "storyboard-reference", "hybrid"];
73
+ readonly supportedAspects: readonly ["16:9", "9:16", "1:1"];
74
+ readonly durationRange: {
75
+ readonly minSeconds: 5;
76
+ readonly maxSeconds: 15;
77
+ };
78
+ readonly durationPolicy: "Kling 3.0 supports Film-Kit start/end and smart-routed video generation.";
79
+ readonly adapterKey: "kling30";
80
+ readonly promptGrammarKey: "kling30";
81
+ readonly modelProfileKey: "kling30";
82
+ readonly routingRole: "primary-video";
83
+ readonly capabilities: {
84
+ readonly characterReference: true;
85
+ readonly storyboardReference: true;
86
+ readonly audioPlan: true;
87
+ readonly startEndFrames: true;
88
+ readonly customStoryboardPhases: true;
89
+ readonly maxStoryboardPhases: 4;
90
+ };
91
+ readonly docsVisibility: "public";
92
+ };
93
+ readonly "gpt-image-2": {
94
+ readonly id: "gpt-image-2";
95
+ readonly displayName: "GPT Image 2";
96
+ readonly provider: "openai";
97
+ readonly modalities: readonly ["image"];
98
+ readonly lifecycleStatus: "active";
99
+ readonly supportedPresets: readonly ["hybrid", "hybrid-smart", "gpt-image-smart", "studio"];
100
+ readonly supportedReferenceModes: readonly ["start-end", "storyboard-reference", "hybrid"];
101
+ readonly supportedAspects: readonly ["16:9", "9:16", "1:1"];
102
+ readonly durationPolicy: "Still-image model; video duration is not applicable.";
103
+ readonly adapterKey: "gptImage2";
104
+ readonly promptGrammarKey: "gptImage2";
105
+ readonly modelProfileKey: "gptImage2";
106
+ readonly routingRole: "image-reference";
107
+ readonly capabilities: {
108
+ readonly characterReference: true;
109
+ readonly storyboardReference: true;
110
+ };
111
+ readonly docsVisibility: "public";
112
+ };
113
+ readonly "gemini-3-pro-image-preview": {
114
+ readonly id: "gemini-3-pro-image-preview";
115
+ readonly displayName: "Gemini 3 Pro Image Preview";
116
+ readonly provider: "google";
117
+ readonly modalities: readonly ["image"];
118
+ readonly lifecycleStatus: "experimental";
119
+ readonly supportedPresets: readonly ["hybrid", "hybrid-smart"];
120
+ readonly supportedReferenceModes: readonly ["start-end", "storyboard-reference", "hybrid"];
121
+ readonly supportedAspects: readonly ["16:9", "9:16", "1:1"];
122
+ readonly durationPolicy: "Still-image model used by internal hybrid presets; video duration is not applicable.";
123
+ readonly adapterKey: "none";
124
+ readonly promptGrammarKey: "generic";
125
+ readonly modelProfileKey: "generic";
126
+ readonly routingRole: "image-reference";
127
+ readonly capabilities: {
128
+ readonly characterReference: true;
129
+ readonly storyboardReference: true;
130
+ };
131
+ readonly docsVisibility: "internal";
132
+ };
133
+ readonly "smart-dialogue-router": {
134
+ readonly id: "smart-dialogue-router";
135
+ readonly displayName: "Smart Dialogue Router";
136
+ readonly provider: "film-kit";
137
+ readonly modalities: readonly ["multimodal"];
138
+ readonly lifecycleStatus: "active";
139
+ readonly supportedPresets: readonly ["hybrid-smart", "gpt-image-smart"];
140
+ readonly supportedReferenceModes: readonly ["start-end", "storyboard-reference", "hybrid"];
141
+ readonly supportedAspects: readonly ["16:9", "9:16", "1:1"];
142
+ readonly durationPolicy: "Internal routing policy; routes dialogue/lip-sync to Veo and non-dialogue shots to Kling.";
143
+ readonly adapterKey: "none";
144
+ readonly promptGrammarKey: "generic";
145
+ readonly modelProfileKey: "generic";
146
+ readonly routingRole: "smart-router";
147
+ readonly capabilities: {
148
+ readonly smartRouting: true;
149
+ readonly audioPlan: true;
150
+ readonly dialogue: true;
151
+ };
152
+ readonly docsVisibility: "internal";
153
+ };
154
+ readonly veo2: {
155
+ readonly id: "veo2";
156
+ readonly displayName: "Veo 2";
157
+ readonly provider: "google";
158
+ readonly modalities: readonly ["video"];
159
+ readonly lifecycleStatus: "deprecated";
160
+ readonly supportedPresets: readonly ["single", "multi", "studio"];
161
+ readonly supportedReferenceModes: readonly ["start-end", "storyboard-reference"];
162
+ readonly supportedAspects: readonly ["16:9", "9:16", "1:1"];
163
+ readonly durationRange: {
164
+ readonly minSeconds: 4;
165
+ readonly maxSeconds: 8;
166
+ };
167
+ readonly durationPolicy: "Deprecated legacy video model kept only for migration warnings.";
168
+ readonly adapterKey: "generic";
169
+ readonly promptGrammarKey: "generic";
170
+ readonly modelProfileKey: "generic";
171
+ readonly routingRole: "legacy-video";
172
+ readonly capabilities: {
173
+ readonly characterReference: true;
174
+ readonly storyboardReference: true;
175
+ readonly audioPlan: true;
176
+ readonly dialogue: true;
177
+ readonly startEndFrames: true;
178
+ readonly customStoryboardPhases: true;
179
+ readonly maxStoryboardPhases: 4;
180
+ };
181
+ readonly deprecationMessage: "veo2 is deprecated. Use veo31 for current Film-Kit video generation.";
182
+ readonly replacementModelId: "veo31";
183
+ readonly docsVisibility: "hidden";
184
+ };
185
+ readonly "kling-2.0": {
186
+ readonly id: "kling-2.0";
187
+ readonly displayName: "Kling 2.0";
188
+ readonly provider: "kuaishou";
189
+ readonly modalities: readonly ["video"];
190
+ readonly lifecycleStatus: "removed";
191
+ readonly supportedPresets: readonly [];
192
+ readonly supportedReferenceModes: readonly [];
193
+ readonly supportedAspects: readonly ["16:9", "9:16", "1:1"];
194
+ readonly durationPolicy: "Removed legacy model. Do not route generation here.";
195
+ readonly adapterKey: "none";
196
+ readonly promptGrammarKey: "generic";
197
+ readonly modelProfileKey: "generic";
198
+ readonly routingRole: "removed";
199
+ readonly capabilities: {};
200
+ readonly deprecationMessage: "kling-2.0 has been removed from Film-Kit. Use kling-3.0 instead.";
201
+ readonly replacementModelId: "kling-3.0";
202
+ readonly docsVisibility: "hidden";
203
+ };
204
+ };
205
+ export type KnownModelId = keyof typeof MODEL_REGISTRY;
206
+ type RegistryEntry<K extends KnownModelId = KnownModelId> = typeof MODEL_REGISTRY[K];
207
+ type ModelIdWithLifecycle<Status extends ModelRegistryEntry["lifecycleStatus"]> = {
208
+ [K in KnownModelId]: RegistryEntry<K>["lifecycleStatus"] extends Status ? K : never;
209
+ }[KnownModelId];
210
+ type ModelIdWithModality<Modality extends ModelRegistryEntry["modalities"][number]> = {
211
+ [K in KnownModelId]: Modality extends RegistryEntry<K>["modalities"][number] ? K : never;
212
+ }[KnownModelId];
213
+ export type ActiveModelId = ModelIdWithLifecycle<"active" | "experimental">;
214
+ export type DeprecatedModelId = ModelIdWithLifecycle<"deprecated">;
215
+ export type RemovedModelId = ModelIdWithLifecycle<"removed">;
216
+ export type VideoModelId = ModelIdWithModality<"video">;
217
+ export type ImageModelId = ModelIdWithModality<"image">;
218
+ export type AudioModelId = ModelIdWithModality<"audio">;
219
+ export type MultimodalModelId = ModelIdWithModality<"multimodal">;
220
+ export {};
@@ -0,0 +1,191 @@
1
+ export const MODEL_REGISTRY = {
2
+ veo31: {
3
+ id: "veo31",
4
+ displayName: "Google Flow + Veo 3.1",
5
+ provider: "google",
6
+ modalities: ["video"],
7
+ lifecycleStatus: "active",
8
+ supportedPresets: ["single", "multi", "studio"],
9
+ supportedReferenceModes: ["start-end", "storyboard-reference"],
10
+ supportedAspects: ["16:9", "9:16", "1:1"],
11
+ durationRange: { minSeconds: 4, maxSeconds: 8 },
12
+ durationPolicy: "Default Film-Kit Veo runtime duration is 8 seconds.",
13
+ adapterKey: "veo31",
14
+ promptGrammarKey: "veo31",
15
+ modelProfileKey: "veo31",
16
+ routingRole: "primary-video",
17
+ capabilities: {
18
+ characterReference: true,
19
+ storyboardReference: true,
20
+ audioPlan: true,
21
+ dialogue: true,
22
+ startEndFrames: true,
23
+ customStoryboardPhases: true,
24
+ maxStoryboardPhases: 4
25
+ },
26
+ docsVisibility: "public"
27
+ },
28
+ "seedance-2.0": {
29
+ id: "seedance-2.0",
30
+ displayName: "Seedance 2.0",
31
+ provider: "bytedance",
32
+ modalities: ["video"],
33
+ lifecycleStatus: "active",
34
+ supportedPresets: ["single", "multi", "studio"],
35
+ supportedReferenceModes: ["start-end", "storyboard-reference"],
36
+ supportedAspects: ["16:9", "9:16", "1:1"],
37
+ durationRange: { minSeconds: 4, maxSeconds: 15 },
38
+ durationPolicy: "Seedance storyboard-reference policy uses 4-15 second shots.",
39
+ adapterKey: "seedance20",
40
+ promptGrammarKey: "seedance20",
41
+ modelProfileKey: "seedance20",
42
+ routingRole: "storyboard-video",
43
+ capabilities: {
44
+ characterReference: true,
45
+ storyboardReference: true,
46
+ multipleImageReferences: true,
47
+ audioPlan: true,
48
+ dialogue: true,
49
+ customStoryboardPhases: true,
50
+ maxStoryboardPhases: 4,
51
+ maxReferenceImages: 9,
52
+ maxReferenceVideos: 3,
53
+ maxReferenceAudio: 3,
54
+ maxReferenceFiles: 12
55
+ },
56
+ docsVisibility: "public"
57
+ },
58
+ "kling-3.0": {
59
+ id: "kling-3.0",
60
+ displayName: "Kling 3.0",
61
+ provider: "kuaishou",
62
+ modalities: ["video"],
63
+ lifecycleStatus: "active",
64
+ supportedPresets: ["single", "multi", "hybrid", "hybrid-smart", "gpt-image-smart", "studio"],
65
+ supportedReferenceModes: ["start-end", "storyboard-reference", "hybrid"],
66
+ supportedAspects: ["16:9", "9:16", "1:1"],
67
+ durationRange: { minSeconds: 5, maxSeconds: 15 },
68
+ durationPolicy: "Kling 3.0 supports Film-Kit start/end and smart-routed video generation.",
69
+ adapterKey: "kling30",
70
+ promptGrammarKey: "kling30",
71
+ modelProfileKey: "kling30",
72
+ routingRole: "primary-video",
73
+ capabilities: {
74
+ characterReference: true,
75
+ storyboardReference: true,
76
+ audioPlan: true,
77
+ startEndFrames: true,
78
+ customStoryboardPhases: true,
79
+ maxStoryboardPhases: 4
80
+ },
81
+ docsVisibility: "public"
82
+ },
83
+ "gpt-image-2": {
84
+ id: "gpt-image-2",
85
+ displayName: "GPT Image 2",
86
+ provider: "openai",
87
+ modalities: ["image"],
88
+ lifecycleStatus: "active",
89
+ supportedPresets: ["hybrid", "hybrid-smart", "gpt-image-smart", "studio"],
90
+ supportedReferenceModes: ["start-end", "storyboard-reference", "hybrid"],
91
+ supportedAspects: ["16:9", "9:16", "1:1"],
92
+ durationPolicy: "Still-image model; video duration is not applicable.",
93
+ adapterKey: "gptImage2",
94
+ promptGrammarKey: "gptImage2",
95
+ modelProfileKey: "gptImage2",
96
+ routingRole: "image-reference",
97
+ capabilities: {
98
+ characterReference: true,
99
+ storyboardReference: true
100
+ },
101
+ docsVisibility: "public"
102
+ },
103
+ "gemini-3-pro-image-preview": {
104
+ id: "gemini-3-pro-image-preview",
105
+ displayName: "Gemini 3 Pro Image Preview",
106
+ provider: "google",
107
+ modalities: ["image"],
108
+ lifecycleStatus: "experimental",
109
+ supportedPresets: ["hybrid", "hybrid-smart"],
110
+ supportedReferenceModes: ["start-end", "storyboard-reference", "hybrid"],
111
+ supportedAspects: ["16:9", "9:16", "1:1"],
112
+ durationPolicy: "Still-image model used by internal hybrid presets; video duration is not applicable.",
113
+ adapterKey: "none",
114
+ promptGrammarKey: "generic",
115
+ modelProfileKey: "generic",
116
+ routingRole: "image-reference",
117
+ capabilities: {
118
+ characterReference: true,
119
+ storyboardReference: true
120
+ },
121
+ docsVisibility: "internal"
122
+ },
123
+ "smart-dialogue-router": {
124
+ id: "smart-dialogue-router",
125
+ displayName: "Smart Dialogue Router",
126
+ provider: "film-kit",
127
+ modalities: ["multimodal"],
128
+ lifecycleStatus: "active",
129
+ supportedPresets: ["hybrid-smart", "gpt-image-smart"],
130
+ supportedReferenceModes: ["start-end", "storyboard-reference", "hybrid"],
131
+ supportedAspects: ["16:9", "9:16", "1:1"],
132
+ durationPolicy: "Internal routing policy; routes dialogue/lip-sync to Veo and non-dialogue shots to Kling.",
133
+ adapterKey: "none",
134
+ promptGrammarKey: "generic",
135
+ modelProfileKey: "generic",
136
+ routingRole: "smart-router",
137
+ capabilities: {
138
+ smartRouting: true,
139
+ audioPlan: true,
140
+ dialogue: true
141
+ },
142
+ docsVisibility: "internal"
143
+ },
144
+ "veo2": {
145
+ id: "veo2",
146
+ displayName: "Veo 2",
147
+ provider: "google",
148
+ modalities: ["video"],
149
+ lifecycleStatus: "deprecated",
150
+ supportedPresets: ["single", "multi", "studio"],
151
+ supportedReferenceModes: ["start-end", "storyboard-reference"],
152
+ supportedAspects: ["16:9", "9:16", "1:1"],
153
+ durationRange: { minSeconds: 4, maxSeconds: 8 },
154
+ durationPolicy: "Deprecated legacy video model kept only for migration warnings.",
155
+ adapterKey: "generic",
156
+ promptGrammarKey: "generic",
157
+ modelProfileKey: "generic",
158
+ routingRole: "legacy-video",
159
+ capabilities: {
160
+ characterReference: true,
161
+ storyboardReference: true,
162
+ audioPlan: true,
163
+ dialogue: true,
164
+ startEndFrames: true,
165
+ customStoryboardPhases: true,
166
+ maxStoryboardPhases: 4
167
+ },
168
+ deprecationMessage: "veo2 is deprecated. Use veo31 for current Film-Kit video generation.",
169
+ replacementModelId: "veo31",
170
+ docsVisibility: "hidden"
171
+ },
172
+ "kling-2.0": {
173
+ id: "kling-2.0",
174
+ displayName: "Kling 2.0",
175
+ provider: "kuaishou",
176
+ modalities: ["video"],
177
+ lifecycleStatus: "removed",
178
+ supportedPresets: [],
179
+ supportedReferenceModes: [],
180
+ supportedAspects: ["16:9", "9:16", "1:1"],
181
+ durationPolicy: "Removed legacy model. Do not route generation here.",
182
+ adapterKey: "none",
183
+ promptGrammarKey: "generic",
184
+ modelProfileKey: "generic",
185
+ routingRole: "removed",
186
+ capabilities: {},
187
+ deprecationMessage: "kling-2.0 has been removed from Film-Kit. Use kling-3.0 instead.",
188
+ replacementModelId: "kling-3.0",
189
+ docsVisibility: "hidden"
190
+ }
191
+ };
@@ -0,0 +1,23 @@
1
+ import { type ActiveModelId, type DeprecatedModelId, type ImageModelId, type KnownModelId, type RemovedModelId, type VideoModelId } from "./registry.js";
2
+ import type { ModelAdapterKey, ModelRegistryEntry, ModelRegistryPreset, ModelRegistryReferenceMode } from "./types.js";
3
+ export declare function getModel(id: string): ModelRegistryEntry | undefined;
4
+ export declare function requireModel(id: string): ModelRegistryEntry;
5
+ export declare function listModels(): ModelRegistryEntry[];
6
+ export declare function listActiveModels(): ModelRegistryEntry[];
7
+ export declare function listVideoModels(): ModelRegistryEntry[];
8
+ export declare function listImageModels(): ModelRegistryEntry[];
9
+ export declare function listModelsForPreset(preset: ModelRegistryPreset): ModelRegistryEntry[];
10
+ export declare function listModelsForReferenceMode(referenceMode: ModelRegistryReferenceMode): ModelRegistryEntry[];
11
+ export declare function listRunnableVideoModels(): ModelRegistryEntry[];
12
+ export declare function listRunnableVideoModelIds(): VideoModelId[];
13
+ export declare function listActiveVideoModelIds(): VideoModelId[];
14
+ export declare function isKnownModelId(value: string): value is KnownModelId;
15
+ export declare function isActiveModelId(value: string): value is ActiveModelId;
16
+ export declare function isDeprecatedModelId(value: string): value is DeprecatedModelId;
17
+ export declare function isRemovedModelId(value: string): value is RemovedModelId;
18
+ export declare function isVideoModelId(value: string): value is VideoModelId;
19
+ export declare function isImageModelId(value: string): value is ImageModelId;
20
+ export declare function isModelSupportedByPreset(modelId: string, preset: ModelRegistryPreset): boolean;
21
+ export declare function getModelAdapterKey(modelId: string): ModelAdapterKey;
22
+ export declare function getReplacementModel(modelId: string): ModelRegistryEntry | undefined;
23
+ export declare function getModelDisplayName(modelId: string): string;
@@ -0,0 +1,80 @@
1
+ import { MODEL_REGISTRY } from "./registry.js";
2
+ function entries() {
3
+ return Object.values(MODEL_REGISTRY);
4
+ }
5
+ function hasModality(model, modality) {
6
+ return model.modalities.includes(modality);
7
+ }
8
+ export function getModel(id) {
9
+ return MODEL_REGISTRY[id];
10
+ }
11
+ export function requireModel(id) {
12
+ const model = getModel(id);
13
+ if (!model) {
14
+ throw new Error(`Unknown model: ${id}.`);
15
+ }
16
+ return model;
17
+ }
18
+ export function listModels() {
19
+ return entries();
20
+ }
21
+ export function listActiveModels() {
22
+ return entries().filter(model => model.lifecycleStatus === "active" || model.lifecycleStatus === "experimental");
23
+ }
24
+ export function listVideoModels() {
25
+ return entries().filter(model => hasModality(model, "video"));
26
+ }
27
+ export function listImageModels() {
28
+ return entries().filter(model => hasModality(model, "image"));
29
+ }
30
+ export function listModelsForPreset(preset) {
31
+ return entries().filter(model => model.supportedPresets.includes(preset));
32
+ }
33
+ export function listModelsForReferenceMode(referenceMode) {
34
+ return entries().filter(model => model.supportedReferenceModes.includes(referenceMode));
35
+ }
36
+ export function listRunnableVideoModels() {
37
+ return listVideoModels().filter(model => model.lifecycleStatus !== "removed");
38
+ }
39
+ export function listRunnableVideoModelIds() {
40
+ return listRunnableVideoModels().map(model => model.id);
41
+ }
42
+ export function listActiveVideoModelIds() {
43
+ return listActiveModels()
44
+ .filter(model => hasModality(model, "video"))
45
+ .map(model => model.id);
46
+ }
47
+ export function isKnownModelId(value) {
48
+ return Object.prototype.hasOwnProperty.call(MODEL_REGISTRY, value);
49
+ }
50
+ export function isActiveModelId(value) {
51
+ const model = getModel(value);
52
+ return model?.lifecycleStatus === "active" || model?.lifecycleStatus === "experimental";
53
+ }
54
+ export function isDeprecatedModelId(value) {
55
+ return getModel(value)?.lifecycleStatus === "deprecated";
56
+ }
57
+ export function isRemovedModelId(value) {
58
+ return getModel(value)?.lifecycleStatus === "removed";
59
+ }
60
+ export function isVideoModelId(value) {
61
+ const model = getModel(value);
62
+ return Boolean(model && hasModality(model, "video"));
63
+ }
64
+ export function isImageModelId(value) {
65
+ const model = getModel(value);
66
+ return Boolean(model && hasModality(model, "image"));
67
+ }
68
+ export function isModelSupportedByPreset(modelId, preset) {
69
+ return Boolean(getModel(modelId)?.supportedPresets.includes(preset));
70
+ }
71
+ export function getModelAdapterKey(modelId) {
72
+ return requireModel(modelId).adapterKey;
73
+ }
74
+ export function getReplacementModel(modelId) {
75
+ const replacementModelId = getModel(modelId)?.replacementModelId;
76
+ return replacementModelId ? getModel(replacementModelId) : undefined;
77
+ }
78
+ export function getModelDisplayName(modelId) {
79
+ return requireModel(modelId).displayName;
80
+ }
@@ -0,0 +1,59 @@
1
+ export type ModelModality = "video" | "image" | "audio" | "multimodal";
2
+ export type ModelLifecycleStatus = "active" | "experimental" | "deprecated" | "removed";
3
+ export type ModelRegistryPreset = "single" | "multi" | "hybrid" | "hybrid-smart" | "gpt-image-smart" | "studio";
4
+ export type ModelRegistryReferenceMode = "start-end" | "storyboard-reference" | "hybrid";
5
+ export type ModelRegistryAspectRatio = "16:9" | "9:16" | "1:1";
6
+ export type ModelAdapterKey = "veo31" | "seedance20" | "kling30" | "generic" | "gptImage2" | "none";
7
+ export type PromptGrammarKey = "veo31" | "seedance20" | "kling30" | "gptImage2" | "generic";
8
+ export type ModelProfileKey = "veo31" | "seedance20" | "kling30" | "gptImage2" | "generic";
9
+ export type ModelRoutingRole = "primary-video" | "storyboard-video" | "image-reference" | "smart-router" | "legacy-video" | "removed";
10
+ export interface ModelDurationRange {
11
+ minSeconds: number;
12
+ maxSeconds: number;
13
+ }
14
+ export interface ModelCapabilities {
15
+ characterReference?: boolean;
16
+ storyboardReference?: boolean;
17
+ multipleImageReferences?: boolean;
18
+ audioPlan?: boolean;
19
+ dialogue?: boolean;
20
+ startEndFrames?: boolean;
21
+ smartRouting?: boolean;
22
+ customStoryboardPhases?: boolean;
23
+ maxStoryboardPhases?: number;
24
+ maxReferenceImages?: number;
25
+ maxReferenceVideos?: number;
26
+ maxReferenceAudio?: number;
27
+ maxReferenceFiles?: number;
28
+ }
29
+ export interface ModelRegistryEntry {
30
+ id: string;
31
+ displayName: string;
32
+ provider: string;
33
+ modalities: readonly ModelModality[];
34
+ lifecycleStatus: ModelLifecycleStatus;
35
+ supportedPresets: readonly ModelRegistryPreset[];
36
+ supportedReferenceModes: readonly ModelRegistryReferenceMode[];
37
+ supportedAspects: readonly ModelRegistryAspectRatio[];
38
+ durationRange?: ModelDurationRange;
39
+ durationPolicy?: string;
40
+ adapterKey: ModelAdapterKey;
41
+ promptGrammarKey: PromptGrammarKey;
42
+ modelProfileKey: ModelProfileKey;
43
+ routingRole: ModelRoutingRole;
44
+ capabilities: ModelCapabilities;
45
+ deprecationMessage?: string;
46
+ replacementModelId?: string;
47
+ docsVisibility: "public" | "internal" | "hidden";
48
+ }
49
+ export interface ModelAllowedContext {
50
+ preset?: ModelRegistryPreset;
51
+ referenceMode?: ModelRegistryReferenceMode;
52
+ requiredModalities?: readonly ModelModality[];
53
+ allowDeprecated?: boolean;
54
+ usage?: string;
55
+ }
56
+ export interface ModelAllowedResult {
57
+ model: ModelRegistryEntry;
58
+ warnings: string[];
59
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { ModelAllowedContext, ModelAllowedResult, ModelRegistryEntry } from "./types.js";
2
+ export declare function validateModelAllowed(modelId: string, context?: ModelAllowedContext): ModelAllowedResult;
3
+ export declare function assertModelAllowed(modelId: string, context?: ModelAllowedContext): ModelRegistryEntry;
@@ -0,0 +1,42 @@
1
+ import { getModel, getReplacementModel, listRunnableVideoModelIds } from "./selectors.js";
2
+ function formatModelList(models) {
3
+ return models.join(", ");
4
+ }
5
+ function hasRequiredModalities(model, required) {
6
+ return !required || required.every(modality => model.modalities.includes(modality));
7
+ }
8
+ export function validateModelAllowed(modelId, context = {}) {
9
+ const model = getModel(modelId);
10
+ const usage = context.usage ? `${context.usage} ` : "";
11
+ if (!model) {
12
+ throw new Error(`Unknown ${usage}model: ${modelId}. Supported video models: ${formatModelList(listRunnableVideoModelIds())}`);
13
+ }
14
+ if (model.lifecycleStatus === "removed") {
15
+ const replacement = getReplacementModel(model.id);
16
+ const replacementText = replacement ? ` Use ${replacement.id} instead.` : "";
17
+ throw new Error(`${model.deprecationMessage ?? `${model.id} has been removed.`}${replacementText}`);
18
+ }
19
+ if (!hasRequiredModalities(model, context.requiredModalities)) {
20
+ throw new Error(`Model ${model.id} is not valid for ${usage.trim() || "this usage"}. ` +
21
+ `Required modalities: ${context.requiredModalities?.join(", ") ?? "none"}.`);
22
+ }
23
+ if (context.preset && !model.supportedPresets.includes(context.preset)) {
24
+ throw new Error(`Model ${model.id} is not supported by preset ${context.preset}.`);
25
+ }
26
+ if (context.referenceMode && !model.supportedReferenceModes.includes(context.referenceMode)) {
27
+ throw new Error(`Model ${model.id} is not supported by reference mode ${context.referenceMode}.`);
28
+ }
29
+ const warnings = [];
30
+ if (model.lifecycleStatus === "deprecated") {
31
+ if (context.allowDeprecated === false) {
32
+ throw new Error(model.deprecationMessage ?? `Model ${model.id} is deprecated.`);
33
+ }
34
+ const replacement = getReplacementModel(model.id);
35
+ warnings.push(model.deprecationMessage
36
+ ?? `Model ${model.id} is deprecated.${replacement ? ` Use ${replacement.id} instead.` : ""}`);
37
+ }
38
+ return { model, warnings };
39
+ }
40
+ export function assertModelAllowed(modelId, context = {}) {
41
+ return validateModelAllowed(modelId, context).model;
42
+ }
@@ -1,3 +1,4 @@
1
+ import { getModelDisplayName } from "../../model-registry/index.js";
1
2
  export class BaseVideoModelPromptAdapter {
2
3
  validate(output) {
3
4
  const prompt = output.promptText;
@@ -53,9 +54,5 @@ export function formatPhaseLines(phases) {
53
54
  .join("\n");
54
55
  }
55
56
  export function getDisplayName(model) {
56
- if (model === "seedance-2.0")
57
- return "Seedance 2.0";
58
- if (model === "kling-3.0")
59
- return "Kling 3.0";
60
- return "Veo 3.1";
57
+ return getModelDisplayName(model);
61
58
  }
@@ -1,9 +1,10 @@
1
+ import { listActiveVideoModelIds } from "../model-registry/index.js";
1
2
  export const STORYBOARD_REFERENCE_MODE = "storyboard-reference";
2
3
  export const DEFAULT_STORYBOARD_DURATION_SECONDS = 8;
3
4
  export const DEFAULT_STORYBOARD_ASPECT_RATIO = "16:9";
4
5
  export const DEFAULT_STORYBOARD_LANGUAGE = "NONE";
5
6
  export const DEFAULT_STORYBOARD_OUTPUT_DIR = "./outputs";
6
- export const DEFAULT_STORYBOARD_TARGET_MODELS = ["veo31", "seedance-2.0", "kling-3.0"];
7
+ export const DEFAULT_STORYBOARD_TARGET_MODELS = listActiveVideoModelIds();
7
8
  export const DEFAULT_STORYBOARD_REFERENCE_RUNTIME = {
8
9
  enabled: true,
9
10
  maxStoryboardPhases: 4,
@@ -1,15 +1,47 @@
1
1
  import { resolveAssetRoles } from "./asset-role-resolver.js";
2
+ import { GenericPromptAdapter } from "./adapters/generic.js";
2
3
  import { Kling30PromptAdapter } from "./adapters/kling30.js";
3
4
  import { Seedance20PromptAdapter } from "./adapters/seedance20.js";
4
5
  import { Veo31PromptAdapter } from "./adapters/veo31.js";
6
+ import { getModelAdapterKey, requireModel } from "../model-registry/index.js";
5
7
  import { normalizeVideoPromptRequest } from "./request-normalizer.js";
6
8
  import { buildStoryboardPhaseBudget, inferContinuityMode, interpretStoryboard } from "./storyboard-interpreter.js";
7
9
  import { assertSafeStoryboardReferenceRequest, validateCharacterReferenceSheetPrompts, validateModelPromptOutput, validatePromptBundle } from "./validators.js";
8
10
  const ADAPTERS = {
9
11
  veo31: new Veo31PromptAdapter(),
10
- "seedance-2.0": new Seedance20PromptAdapter(),
11
- "kling-3.0": new Kling30PromptAdapter()
12
+ seedance20: new Seedance20PromptAdapter(),
13
+ kling30: new Kling30PromptAdapter()
12
14
  };
15
+ function buildGenericCapabilities(model) {
16
+ const registryModel = requireModel(model);
17
+ const capabilities = {
18
+ supportsImageReference: Boolean(registryModel.capabilities.characterReference),
19
+ supportsMultipleImageReferences: Boolean(registryModel.capabilities.multipleImageReferences),
20
+ supportsStoryboardReference: Boolean(registryModel.capabilities.storyboardReference),
21
+ supportsAudio: Boolean(registryModel.capabilities.audioPlan),
22
+ supportsDialogue: Boolean(registryModel.capabilities.dialogue),
23
+ supportsCustomStoryboardPhases: Boolean(registryModel.capabilities.customStoryboardPhases),
24
+ preferredPromptShape: "natural-language"
25
+ };
26
+ if (registryModel.durationRange?.maxSeconds !== undefined) {
27
+ capabilities.maxDurationSeconds = registryModel.durationRange.maxSeconds;
28
+ }
29
+ if (registryModel.capabilities.maxStoryboardPhases !== undefined) {
30
+ capabilities.maxStoryboardPhases = registryModel.capabilities.maxStoryboardPhases;
31
+ }
32
+ return capabilities;
33
+ }
34
+ function resolvePromptAdapter(model) {
35
+ const adapterKey = getModelAdapterKey(model);
36
+ if (adapterKey === "generic") {
37
+ return new GenericPromptAdapter(model, buildGenericCapabilities(model));
38
+ }
39
+ const adapter = ADAPTERS[adapterKey];
40
+ if (!adapter) {
41
+ throw new Error(`Model ${model} does not have a storyboard-reference video adapter.`);
42
+ }
43
+ return adapter;
44
+ }
13
45
  function getStoryboardPanelCount(request, phaseBudget) {
14
46
  return request.storyboardPanelCountHint
15
47
  ?? request.shotCountHint
@@ -320,7 +352,7 @@ function buildStoryboardReferencePolicy(request, phaseBudget, providerFileLimits
320
352
  function buildModelPrompts(request, bundleInput) {
321
353
  const modelPrompts = {};
322
354
  for (const model of request.targetModels) {
323
- const adapter = ADAPTERS[model];
355
+ const adapter = resolvePromptAdapter(model);
324
356
  const output = adapter.buildPrompt({ ...bundleInput, request });
325
357
  const validatorQa = validateModelPromptOutput(output);
326
358
  output.qa = {
@@ -1,5 +1,5 @@
1
+ import { validateModelAllowed } from "../model-registry/index.js";
1
2
  import { DEFAULT_STORYBOARD_ASPECT_RATIO, DEFAULT_STORYBOARD_DURATION_SECONDS, DEFAULT_STORYBOARD_LANGUAGE, DEFAULT_STORYBOARD_OUTPUT_DIR, DEFAULT_STORYBOARD_TARGET_MODELS, STORYBOARD_REFERENCE_MODE, resolveStoryboardReferenceRuntime } from "./defaults.js";
2
- const SUPPORTED_MODELS = ["veo31", "seedance-2.0", "kling-3.0"];
3
3
  const SUPPORTED_ASPECT_RATIOS = ["16:9", "9:16", "1:1"];
4
4
  function normalizePositiveInteger(value, fallback, fieldName) {
5
5
  const normalized = value ?? fallback;
@@ -13,9 +13,11 @@ function assertSupportedModels(models) {
13
13
  return DEFAULT_STORYBOARD_TARGET_MODELS.slice();
14
14
  }
15
15
  for (const model of models) {
16
- if (!SUPPORTED_MODELS.includes(model)) {
17
- throw new Error(`Unsupported target model: ${model}. Supported: ${SUPPORTED_MODELS.join(", ")}`);
18
- }
16
+ validateModelAllowed(model, {
17
+ requiredModalities: ["video"],
18
+ referenceMode: STORYBOARD_REFERENCE_MODE,
19
+ usage: "storyboard target"
20
+ });
19
21
  }
20
22
  return Array.from(new Set(models));
21
23
  }
@@ -1,5 +1,6 @@
1
+ import type { VideoModelId } from "./model-registry/index.js";
1
2
  export type SupportedPlatform = "cursor" | "claude" | "copilot" | "antigravity" | "codex";
2
- export type SupportedModel = "veo31" | "kling-3.0" | "seedance-2.0";
3
+ export type SupportedModel = VideoModelId;
3
4
  export type KlingPreset = "ultra-realism" | "balanced" | "custom";
4
5
  export type FilmKitPreset = "single" | "multi" | "hybrid" | "hybrid-smart" | "gpt-image-smart" | "studio";
5
6
  export type ReferenceMode = "start-end" | "storyboard-reference" | "hybrid";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milenyumai/film-kit",
3
- "version": "2.3.3",
3
+ "version": "2.3.4",
4
4
  "description": "Single-package Film-Kit distribution with preset-driven cinematic runtime setup for OpenAI Codex App, Claude Code, Cursor, Copilot, and Antigravity.",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",
@@ -36,6 +36,7 @@
36
36
  "packages/gpt-image-smart/content",
37
37
  "packages/studio/content",
38
38
  "content",
39
+ "MODEL_REGISTRY.md",
39
40
  "README.md",
40
41
  "LICENSE"
41
42
  ],