ai newsDi., 20. Okt.
EilmeldungWegen Anthropic: Eric Schmidt könnte bald 20 Milliarden Dollar reicher werden

Building a Multi-Model AI Video Studio: Notes from Kivide

Building a Multi-Model AI Video Studio: Notes from Kivide What it actually takes to put 19 image/video models behind one interface — routing, credit accounting, and the prompt layer nobody talks about. Try it: kivide.com · Prompt library: kivide.com/prompts Every few weeks a new video model drops.

dev.toDienstag 09:21 · 20.10.6 Min. Lesezeit
Bild: dev.to

What it actually takes to put 19 image/video models behind one interface — routing, credit accounting, and the prompt layer nobody talks about.

Try it: kivide.com · Prompt library: kivide.com/prompts

Every few weeks a new video model drops. Seedance 2.5, Kling 2.5 Turbo, Wan 2.7, MiniMax H3, Veo, Sora. Each one is better at something different — one nails motion, another nails faces, a third is cheap enough to use for iteration.

If you're building a product on top of them, you hit the same wall fast: users don't want to choose a model. They want a result.

This post is about how Kivide is structured around that problem. It's not a launch announcement — it's the engineering notes: what the model layer looks like, how credits are accounted for across 19 models, and why the prompt layer turned out to be the hardest part.

The naive design is "one generate endpoint, a model parameter, done." That breaks immediately, because models disagree on almost everything:

So the model layer can't be a thin proxy. It has to be a capability descriptor that the rest of the system reads from.

Each model is described by data, not by branching code:

type ModelSpec = { id: string kind: ' video ' | ' image ' inputs: ( ' text ' | ' image ' )[] durations?: number [] // video only resolutions?: string [] // video only ratios: string [] // Credit cost per generation, per plan tier cost: { regular: number starterYearly: number proYearly: number unlimited: number | ' Unlimited ' } } Enter fullscreen mode Exit fullscreen mode The UI, the pricing table, and the generation endpoint all read from this. Adding a model means adding a row — not touching the routing logic.

Here's what that actually looks like in production. These are live numbers from kivide.com/api/pricing:

The spread is 8x between the cheapest image model and the most expensive video model. That spread is the whole reason the credit system has to be exact.

The obvious model is "one generation = one credit." It falls apart because a 4K image and a 10-second 1080p video cost the provider wildly different amounts.

So credits are a normalized unit of provider cost, not a unit of user action. A few consequences:

1. Cost has to be decided before the request is sent. You can't tell the user "this cost 150 credits" after the fact. The model spec is resolved first, the cost is computed, and only then is the request made.

2. Balance has to be reserved, then reconciled. Two concurrent requests against the same balance can both pass the check and both spend. The pattern that works:

1. Read balance 2. Check balance >= cost (fail fast, no charge) 3. Deduct cost immediately (reserve) 4. Call the provider 5. On success → keep the deduction On failure → refund the exact amount Enter fullscreen mode Exit fullscreen mode Step 3 before step 4 is deliberate. Deducting first means a crash mid-request can't leave the user with free generations — it leaves them with a refund owed, which is the safer failure direction and is recoverable from logs.

3. Refunds have to be idempotent. Provider webhooks retry. If a refund is keyed only on "this generation failed," a retried webhook refunds twice. Every refund needs a deterministic key derived from the generation id.

4. Plan tier changes the price, not the model. Pro-yearly users pay 60% of the regular rate for the same model. That's a pricing decision, not a routing one — so the cost table is keyed by (model, tier), and the tier is resolved from the subscription at request time.

Once the model layer is abstracted, switching models is cheap. What's not cheap is that a prompt tuned for one model produces garbage on another. The same sentence that gives you a beautiful cinematic shot on Seedance gives you a flat, static image on a cheaper model.

Three things that consistently matter more than style adjectives:

❌ "a beautiful cinematic shot of a motorcycle at night" ✅ "low-angle shot of a rider on a white sport motorcycle speeding down a highway at night. Red taillight streak." Enter fullscreen mode Exit fullscreen mode The second one specifies a camera position ( low-angle ) and a physical artifact ( red taillight streak, which only appears in a long exposure). The model has seen thousands of real photos matching that description, so it has something concrete to reproduce.

"Beautiful" and "cinematic" are not instructions. They're adjectives the model has learned to ignore because they appear in every prompt.

A text-to-video prompt with no motion verb produces a still image with a slow digital zoom. Always include one:

dust trails, steam rising, water droplets frozen mid-air, film grain, soft bokeh. These are rendering instructions, not mood words. They tell the model which optical phenomenon to simulate.

A generation request goes through five stages:

1. Resolve model spec → inputs, ratios, cost 2. Check + reserve credits → fail fast if insufficient 3. Validate input → image dimensions, duration, ratio 4. Dispatch to provider → with a per-provider timeout 5. Reconcile → keep charge on success, refund on failure Enter fullscreen mode Exit fullscreen mode The interesting failures are all in stage 4 and 5:

Let me walk through one prompt from the library to show what "specific" means in practice.

Goal: a cyberpunk night motorcycle shot.

A cool cyberpunk motorcycle rider at night, neon lights, cinematic, 8K, highly detailed

What comes back: a static portrait of a motorcycle. Nice lighting. No sense of motion. Looks like a stock photo.

Low-angle shot of a rider on a white sport motorcycle speeding down a highway at night. Red taillight streak, cyberpunk night vibe, 8K.

The two words doing the most work are low-angle and taillight streak. The first sets the camera below the subject, which makes speed read. The second is a long-exposure artifact — it implies the camera is tracking the bike, which is what makes the image feel like a still from a video rather than a render.

Run it: kivide.com/prompts/cyberpunk-night-motorcycle-prompt

Both of these came from prompts in the public prompt library — each one is a copy-paste-ready prompt with a one-click "use this" action that opens the studio pre-filled.

Start with the cost table, not the UI. The model/cost matrix is the actual product constraint. Building the UI first means rebuilding it when you discover a 4K model costs 8x a lite one.

Make refunds a first-class flow from day one. Retrofitting idempotent refunds onto a system that already has users is painful.

Treat prompts as data, not strings. Once prompts live in a structured library (subject / motion / camera / mood / quality), you can A/B them, version them, and generate landing pages from them. Strings in a textarea can't do any of that.

Don't hide the model choice entirely. Users who care will find out anyway, and the ones who don't care are served fine by a good default. Hiding it completely creates support tickets.

If you're building something similar and want to compare notes on the credit accounting or the provider abstraction, the comments are open.

Disclosure: I build Kivide. The numbers in this post are read live from the production pricing API, not from memory.

Quelle: dev.to (dev.to). Original öffnen