import type { VideoProviderDescriptor, ProviderRoutingPolicy, ProviderRouteId, } from "./types.js"; export const DEFAULT_PROVIDER_REGISTRY: VideoProviderDescriptor[] = [ { id: "veo-useapi", provider: "veo", displayName: "Google Veo (Flow)", path: "useapi", summary: "Google Veo, driven through your Google Flow account. The photoreal route. It can also edit an existing clip and speak narration in the model itself. It keeps a face consistent by using a character you registered in Flow. Needs a Google Flow account, and most models spend Flow credits.", controls: [ "audio", "first-frame", "last-frame", "reference-images", "camera-grammar", "world-consistency", ], operationSupport: [ { operation: "text-to-video", aspectRatios: ["landscape", "portrait"], }, { operation: "image-to-video", aspectRatios: ["landscape", "portrait"], }, { operation: "frames-to-video", aspectRatios: ["landscape", "portrait"], }, { operation: "ingredients-to-video", aspectRatios: ["landscape", "portrait"], maxReferenceImages: 3, }, { operation: "video-to-video", aspectRatios: ["landscape", "portrait"], notes: ["Omni Flash V2V edit via referenceVideo_1. Requires USEAPI_API_TOKEN."], }, { operation: "add-audio", aspectRatios: ["landscape", "portrait"], notes: ["30 voice-narration presets via referenceAudio_1..5."], }, ], routingHints: { latencyClass: "medium", costClass: "low", trustClass: "aggregator", preferredWorkflows: ["ad-creative-variants", "generic"], }, escapeHatches: [ { name: "useapiVeoOptions", description: "Expose UseAPI request-level knobs that are not always safe to normalize globally.", options: [ { name: "captchaRetry", description: "Override CAPTCHA retry count and provider ordering.", }, { name: "replyUrl", description: "Attach a UseAPI webhook callback for async orchestration.", }, ], }, ], notes: [ "Preferred when portrait I2V/F2V support is required.", "Preserves the existing veo-useapi path.", "omni-flash model unlocks video-to-video (V2V) and native add-audio; not available on the direct Flow path.", ], }, { id: "seedance-direct", provider: "seedance", displayName: "Seedance 2.0", path: "direct", summary: "Seedance 2.0 — free through your Higgsfield account using the engine that ships with videoclaw, or paid through the direct API with SUTUI_API_KEY. Best at stylized, illustrated, and product video, and it can animate a still. It refuses photoreal human faces.", controls: [ "first-frame", "last-frame", "reference-images", "motion-control", "camera-grammar", ], operationSupport: [ { operation: "text-to-video", aspectRatios: ["landscape", "portrait"], }, { operation: "image-to-video", aspectRatios: ["landscape", "portrait"], maxReferenceImages: 9, notes: ["Uses @imageN reference syntax. Images must be China-accessible URLs."], }, { operation: "frames-to-video", aspectRatios: ["landscape", "portrait"], notes: ["Start frame + end frame via @image1 to @image2 syntax."], }, { operation: "add-audio", aspectRatios: ["landscape", "portrait"], notes: ["Audio lipsync via @audio1 reference. Max 15s duration."], }, ], routingHints: { latencyClass: "medium", costClass: "low", trustClass: "direct", preferredWorkflows: ["generic"], }, escapeHatches: [ { name: "seedanceOptions", description: "Seedance-native controls for content filtering and quality mode.", options: [ { name: "contentFilterLevel", description: "Content filter sanitization level (0=none, 1=light, 2=aggressive).", }, { name: "qualityMode", description: "Quality mode: fast (seedance_2.0_fast) or quality (seedance_2.0).", }, ], }, ], notes: [ "Direct API via xskill.ai. Requires SUTUI_API_KEY env var.", "15s max per generation. Longer videos use segmented stitching.", "Chinese prompts produce best results.", ], }, { id: "runway-useapi", provider: "runway", displayName: "Runway", path: "useapi", summary: "Runway, driven through your Runway account. The route for editing and extending a clip you already have, and for lip-synced dialogue. Free but slow on an explore-mode account, faster on a paid one.", controls: [ "audio", "first-frame", "last-frame", "multi-shot", "lip-sync", "motion-control", "reusable-elements", "native-extend", "native-edit", "world-consistency", ], operationSupport: [ { operation: "text-to-video", aspectRatios: ["landscape", "portrait"], }, { operation: "image-to-video", aspectRatios: ["landscape", "portrait"], }, { operation: "extend", aspectRatios: ["landscape", "portrait"], }, { operation: "edit", aspectRatios: ["landscape", "portrait"], }, { operation: "add-audio", aspectRatios: ["landscape", "portrait"], }, ], routingHints: { latencyClass: "low", costClass: "medium", trustClass: "aggregator", preferredWorkflows: ["product-demo-spokesperson", "ad-creative-variants"], }, escapeHatches: [ { name: "runwayOptions", description: "Preserve Runway-native controls such as multi-shot, lip-sync, and motion intensity.", options: [ { name: "multiShot", description: "Control shot sequencing and reusable scene elements.", }, { name: "lipSyncProfile", description: "Choose Runway lip-sync / dialogue controls for spokesperson workflows.", }, { name: "audioTrackMode", description: "Retain add-audio / replace-audio intent for edit-first workflows.", }, ], }, ], notes: [ "Production native transport (src/video/native-runway.ts) — Seedance-2 via Runway by default; override with VCLAW_RUNWAY_MODEL.", "Mode defaults to 'explore' (free, queued, single active slot); set VCLAW_RUNWAY_MODE=credits for paid faster path.", "Requires USEAPI_API_TOKEN; account must be pre-registered with UseAPI (see registerRunwayAccount in providers/runway-useapi.ts).", ], }, { id: "dreamina-useapi", provider: "seedance", displayName: "Seedance 2.0 via Dreamina", path: "useapi", summary: "Seedance 2.0 through a Dreamina (CapCut) account — make video from a prompt, from one still, or between a first and last still. Paid, and 1080p needs a CA-region account. Like the other Seedance routes, it refuses photoreal human faces.", controls: [ "first-frame", "last-frame", "reference-images", "world-consistency", ], operationSupport: [ { operation: "text-to-video", aspectRatios: ["landscape", "portrait"], }, { operation: "image-to-video", aspectRatios: ["landscape", "portrait"], }, ], routingHints: { latencyClass: "medium", costClass: "medium", trustClass: "aggregator", preferredWorkflows: ["ad-creative-variants", "product-demo-spokesperson"], }, escapeHatches: [ { name: "dreaminaOptions", description: "Preserve Dreamina-native controls such as model selection and CA-only 1080p output.", options: [ { name: "model", description: "Choose the Dreamina model (seedance-2.0, seedance-2.0-fast, ...); override with VCLAW_DREAMINA_MODEL.", }, { name: "resolution", description: "1080p is CA-only; 720p works on both US and CA regions.", }, ], }, ], notes: [ "Production native transport (src/video/native-dreamina.ts) — Seedance 2.0 via Dreamina by default; override with VCLAW_DREAMINA_MODEL.", "Requires USEAPI_API_TOKEN plus VCLAW_DREAMINA_ACCOUNT (e.g. 'CA:ai@example.com'); region via VCLAW_DREAMINA_REGION (default CA).", "Real human faces are rejected by Seedance content moderation; use illustrated/stylized characters or a Runway-generated start frame.", ], }, { id: "magnific-rest", provider: "magnific", displayName: "Magnific (video models)", path: "direct", summary: "Image-to-video through Magnific's paid catalog, driven by your Magnific/Freepik account. It animates a still on whichever model you pick (MiniMax Live, PixVerse V5, Runway Gen4 Turbo, Kling Standard, LTX 2.0 Pro), defaulting to the cheapest one that fits; the premium Kling O1 Pro is opt-in. The same MAGNIFIC_API_KEY also runs their upscaler, which videoclaw reaches from finish --backend magnific-precision and image-ops.", controls: ["first-frame", "reference-images"], operationSupport: [ { operation: "image-to-video", aspectRatios: ["landscape", "portrait"], maxReferenceImages: 1, }, ], routingHints: { latencyClass: "medium", costClass: "medium", trustClass: "aggregator", preferredWorkflows: ["ad-creative-variants", "product-demo-spokesperson"], }, escapeHatches: [ { name: "magnificModel", description: "Pick a proxied model; premium tiers are opt-in (cheap-by-default otherwise).", options: [ { name: "model", description: "VCLAW_MAGNIFIC_MODEL (e.g. kling-o1-pro). Default = cheapest catalog model that fits the operation; the premium model (kling-o1-pro) is opt-in. Unknown ids throw — the catalog is src/video/magnific/models.ts.", }, ], }, ], notes: [ "Native transport src/video/native-magnific.ts (pure Node fetch + x-magnific-api-key); requires MAGNIFIC_API_KEY.", "Image-to-video only. text-to-video paths are unverified on this API, so the route does not advertise them; a t2v request would resolve no model.", "Seedance generation is not on this REST catalog (seedance-pro-1080p 404s) — it lives on dreamina-useapi/seedance-direct. Magnific's OAuth MCP server does expose the Seedance 2.0 family, but that is an interactive-only surface this route cannot call.", "Request field names are bound against live probes, not the published api-reference — Magnific's docs lag their API (their MCP docs list ~30 of ~97 live tools).", ], }, ]; export const DEFAULT_ROUTING_POLICY: ProviderRoutingPolicy = { tag: "balanced", preferDirectForTrust: true, preferUseApiWhenCapabilitiesUnlock: true, allowDeprecatedProviders: false, allowDegradedProviders: true, providerOrder: [ "veo-useapi", "seedance-direct", "runway-useapi", "dreamina-useapi", "magnific-rest", ], }; export function getProviderDescriptor(routeId: ProviderRouteId): VideoProviderDescriptor { const descriptor = DEFAULT_PROVIDER_REGISTRY.find((route) => route.id === routeId); if (!descriptor) { throw new Error(`Unknown provider route: ${routeId}`); } return descriptor; }