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, one render at a time); 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.", "Registered but not pursued since 2026-09-09: Dreamina was dropped as too expensive to keep supporting. Use seedance-direct for Seedance 2.0.", ], }, { 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).", ], }, { id: "seedance-modelark", provider: "seedance", displayName: "Seedance 2.5 (BytePlus ModelArk)", path: "direct", summary: "The official Dreamina Seedance 2.5 API on BytePlus ModelArk, billed per second to your BytePlus account. Text, first/last-frame and omni reference-to-video (up to 30 reference images, 10 videos, 10 audio clips; clips of 4–30 seconds) with native audio. VCLAW_MODELARK_MODEL switches to the cheaper Seedance 2.0 fast or mini models (9/3/3 references, 4–15 seconds, no audio-only input). A separate route from seedance-direct on purpose: different host, key, request shape and biller.", controls: ["audio", "first-frame", "last-frame", "reference-images"], operationSupport: [ { operation: "text-to-video", aspectRatios: ["landscape", "portrait"] }, { operation: "image-to-video", aspectRatios: ["landscape", "portrait"], maxReferenceImages: 1 }, { operation: "frames-to-video", aspectRatios: ["landscape", "portrait"], maxReferenceImages: 2 }, { operation: "ingredients-to-video", aspectRatios: ["landscape", "portrait"], maxReferenceImages: 30 }, ], routingHints: { latencyClass: "medium", costClass: "high", trustClass: "direct", preferredWorkflows: ["ad-creative-variants", "product-demo-spokesperson", "generic"], }, escapeHatches: [ { name: "modelArkModel", description: "Pick the ModelArk model; the 2.5 default is the strongest and the most expensive.", options: [ { name: "model", description: "VCLAW_MODELARK_MODEL: dreamina-seedance-2-5-260628 (default), dreamina-seedance-2-0-fast-260128, dreamina-seedance-2-0-mini-260615. Unknown ids throw — the table is src/video/providers/modelark.ts.", }, ], }, ], notes: [ "Native transport src/video/native-modelark.ts (pure Node fetch, Bearer ARK_API_KEY); optional VCLAW_MODELARK_BASE_URL.", "Certified 2026-09-21: one paid 4 s 720p 16:9 text-to-video job on dreamina-seedance-2-5-260628 (vendor task cgt-20260921204846-23vjj) completed in 3.5 min, 1280×720 with audio, billed 87,300 tokens (≈ USD 0.93 at the 720p list price, ≈ USD 0.23/s); audit row in docs/audits/2026-09-21-live-acceptance.md. Never chosen by default routing; pick it with routePreference or --route.", "A first/last-frame scene and omni references cannot share one request. A keyframe that arrives with a voice clip is sent as @Image 1 in omni mode with the prompt told it is the first frame — a soft lock.", "Local video/audio references are hosted on the same temporary public host finish and lipsync use (~3 h); small images are inlined as base64.", ], }, { id: "reapi-seedance", provider: "seedance", displayName: "Seedance 2.5 (Less Restriction) via reAPI", path: "aggregator", summary: "ByteDance Seedance 2.5 with the content filter off, served by reAPI — reached through the treg catalog on the treg token, or with your own reAPI key. Takes a photograph of a real person as the subject reference AND a voice clip as the speech reference in the same render, with native speech, sound effects and music — the one API-keyed route with both (the free Higgsfield engine behind seedance-direct also takes a face plus a voice, through a browser session, at $0). Paid per second of output; illegal content is still refused and refunded. Opt-in only.", controls: [ "audio", "first-frame", "last-frame", "reference-images", "lip-sync", "world-consistency", ], operationSupport: [ { operation: "text-to-video", aspectRatios: ["landscape", "portrait"], }, { operation: "image-to-video", aspectRatios: ["landscape", "portrait"], maxReferenceImages: 30, }, { operation: "frames-to-video", aspectRatios: ["landscape", "portrait"], maxReferenceImages: 2, }, { operation: "ingredients-to-video", aspectRatios: ["landscape", "portrait"], maxReferenceImages: 30, }, ], routingHints: { latencyClass: "medium", costClass: "high", trustClass: "aggregator", preferredWorkflows: ["product-demo-spokesperson"], }, escapeHatches: [ { name: "reapiOptions", description: "Choose the credential path and the probe resolution without touching the shared execution profile.", options: [ { name: "via", description: "VCLAW_REAPI_SEEDANCE_VIA=treg (TREG_TOKEN, billed to the treg balance, references hosted on treg's media host) or =direct (REAPI_API_KEY, billed to your reAPI account, references hosted on Go Bananas R2). Required; the route refuses to run without it and never infers a path from whichever key is set.", }, { name: "resolution", description: "VCLAW_REAPI_SEEDANCE_RESOLUTION=480p|720p|1080p overrides the profile's 720p/1080p for a cheap probe render; it is part of the approval fingerprint.", }, ], }, ], notes: [ "Production native transport (src/video/native-reapi.ts, pure Node fetch + fs). Submit POST /videos/generations, poll GET /tasks/{id} (free, every 10 s); no provider-side cancel — use execute-abandon to stop waiting.", "Every reference must be a public HTTPS URL that returns raw media bytes: the transport hosts local files at submit time (treg POST /media, 7-day TTL, or Go Bananas R2), AFTER the approval hash, so what you approved is still the bytes on disk.", "PAID per second of OUTPUT, refunded on failure; a reference VIDEO is billed on top of the output and the minimum is 4 s. Read the price at treg catalog get reapi.video-gen.seedance-2-5.unrestricted before any --confirm-spend.", ], }, ]; 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", "seedance-modelark", // Last on purpose: paid per second and opt-in. An id missing from this // list scores 0 in router.ts, so it is named here rather than omitted. "reapi-seedance", ], }; 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; }