This document explains what this project is, why it exists, how it works, and the product thinking behind it. It is written for anyone: product managers, engineers, founders, recruiters. No coding background needed to follow it.
If you just want to run it, see the README. If you want to learn the code hands-on, see LEARN.md.
A router that decides which AI video model should make each video, switches vendors when one breaks, stops when a request can’t succeed anywhere, and always charges the user the price they were quoted.
Generative video products (tools that turn a text description into a video clip) don’t run on a single AI model. They run on a portfolio of models, because each one is good at different things and costs a different amount:
| Model (in this demo) | Good at | Relative cost |
|---|---|---|
| Wan 2 (self-hosted, open source) | Basic clips | Very cheap |
| Seedance 2 | Clips with built-in music and sound | Cheap |
| Kling v3 | Good general quality | Medium |
| Kling Omni | Lip-sync, so characters can speak dialogue | Expensive |
| Veo 3 | Premium quality | Very expensive |
This creates three problems a product has to solve on every single request:
A router is the piece of software that answers all three questions in a fraction of a second. This repo is a small, clear, runnable version of that router.
Imagine asking a travel agent: “Delhi to Mumbai tomorrow, and I need a vegetarian meal.”
A good agent:
| Travel agent | This router |
|---|---|
| List of airlines | models.json, the catalog |
| Removing flights that can’t work | Hard constraints in resolver.py |
| Sorting cheapest first | Soft preferences in resolver.py |
| Booking, trying the next airline | runner.py |
| Invalid passport, stop | Permanent errors (content policy, bad prompt) |
| Charging the quoted price | Billing rule in runner.py |
models.json)A plain text file that lists every model. For each one it records:
priority), and at what cost per second.Example, in plain English: “Kling Omni needs the Creator plan or higher. It can do lip-sync and clips up to 10 seconds. Buy it from Kling first at $0.18 per second; if that fails, from the reseller at $0.21 per second.”
Why a config file matters: adding a model, adding a backup vendor, or changing which plan unlocks a model is an edit to this file. The program itself doesn’t change. Packaging and pricing become a product decision, not an engineering project.
router/resolver.py)Hard constraints are must-haves. If a model fails any one, it is removed:
Every removal is recorded with a human-readable reason, such as needs 'pro' plan, user is on 'creator' or no lip-sync.
router/resolver.py)Soft preferences are nice-to-haves: among the options that can do the job, which should we try first?
router/runner.py)The router tries the first option. If it fails, the error is classified:
| Error | What it means | Classified as | What happens |
|---|---|---|---|
503 / 500 |
The vendor is down | Retryable | Try the next vendor |
429 |
The vendor is overloaded | Retryable | Try the next vendor |
| timeout | No response in time | Retryable | Try the next vendor |
400 |
The request itself is invalid | Permanent | Stop |
451 |
Blocked by content policy | Permanent | Stop, bill nothing |
The distinction is the whole point: retryable errors are about the vendor having a bad moment, so another vendor may succeed. Permanent errors are about the request, so every vendor will refuse it, and retrying only wastes money and time.
router/runner.py)vendor_cost_usd.A user on the Creator plan asks for an 8-second talking scene with Kling Omni. Kling’s own service is down.
Catalog Kling Omni has two vendors: official ($1.44 for 8s) and reseller ($1.68 for 8s)
Filter Both can do 8 seconds with lip-sync. Keep both.
Sort The user picked the model, so use fallback order: official, then reseller.
Try 1 Official returns 503 "service unavailable". Retryable. Move on.
Try 2 Reseller succeeds. Video delivered.
Bill User pays $1.44 (the quoted price). We paid $1.68. We absorb $0.24.
The user never knows anything went wrong. That is the goal.
Running python3 demo.py prints a trace for each:
| # | Situation | What the router does |
|---|---|---|
| 1 | Normal 5-second clip, Creator plan | Removes Veo (needs Pro), picks the cheapest option: Wan at $0.05 |
| 2 | Scene needs lip-sync | Removes every model without lip-sync; only Kling Omni remains |
| 3 | Primary vendor down | Fails over to the reseller; user pays the quoted $1.44 |
| 4 | Content policy block | Stops after one attempt; bills $0 |
| 5 | Free-plan user | Only the self-hosted Wan model is allowed |
| 6 | Free user wants audio | No model qualifies: this is the moment to show an upgrade prompt |
The code is intentionally short. The decisions behind it are what make it useful.
A vendor outage is the business’s problem, not the customer’s. Charging more because a backup was used would feel arbitrary and erode trust. Tracking the absorbed cost separately turns it into a signal: over a month, you can see exactly which vendor’s unreliability is costing you margin, and use that in vendor negotiations or routing changes.
If a prompt is blocked for content policy, sending it to two more vendors triples the cost and latency and still fails. Stopping fast, billing nothing, and explaining why is cheaper and more honest.
“Why didn’t it use Veo?” should be answerable by support, sales, or a PM without asking an engineer to read logs. Reasons also make the system easy to debug and audit.
When a free user asks for audio, no free-plan model can deliver it. Instead of a dead end, this is the best possible moment to show “Upgrade to Creator to unlock audio”, because the router already knows exactly which plan would remove the blocker. The interactive playground demonstrates this.
A near-zero marginal cost model is what makes a genuinely useful free tier affordable. Paid tiers unlock vendor models with better quality or special abilities.
Moving a model to a different plan is a one-word change in models.json. Pricing and packaging experiments shouldn’t wait on an engineering sprint.
playground/index.html is a visual, clickable version of the same logic. You can:
Try it live: https://premdutta.github.io/genai-video-router/playground/ (or open the file in any browser; no install needed).
models.json The catalog: models, capabilities, plans, vendors, costs
router/catalog.py Reads the catalog into the program
router/resolver.py Step 2 and 3: filter on hard constraints, sort on soft preferences
router/runner.py Step 4 and 5: try in order, classify errors, bill fairly
router/providers.py Simulated vendors, so everything runs without API keys
router/__init__.py route(): resolve, then run
demo.py Six scenarios printed as readable traces
tests/ Ten automatic checks, one per rule
playground/ Interactive visual demo
README.md Quick start
LEARN.md 30-minute hands-on walkthrough with exercises
OVERVIEW.md This document
You need Python 3.8 or newer. There are no other dependencies.
git clone https://github.com/PremDutta/genai-video-router.git
cd genai-video-router
python3 demo.py # see all six scenarios
python3 -m unittest -v # run the ten tests
open playground/index.html # interactive demo (macOS; or double-click the file)
Each test in tests/test_router.py is named like a product requirement:
test_auto_picks_cheapest_capabletest_lip_sync_filters_to_omnitest_plan_gatingtest_endpoint_limit_excludes_aggregator_for_long_cliptest_every_exclusion_has_a_reasontest_retryable_error_falls_throughtest_user_pays_quoted_price_on_failovertest_permanent_error_stops_and_bills_nothingtest_all_down_fails_cleanlytest_classificationThey work like acceptance criteria in a PRD, except the computer checks them automatically every time the code changes.
This repo shows the core pattern and keeps it deliberately small. A production system also needs:
| Term | Meaning |
|---|---|
| Model | An AI system that generates video, such as Kling or Veo |
| Vendor / endpoint | A specific place to buy a model’s output; one model can have several |
| Catalog | The file listing all models, abilities, plans, vendors and costs |
| Hard constraint | A must-have rule; failing it removes the option |
| Soft preference | A nice-to-have rule used to order the options that remain |
| Failover | Automatically switching to a backup vendor when one fails |
| Retryable error | A temporary vendor problem; another vendor may succeed |
| Permanent error | A problem with the request itself; every vendor would refuse it |
| Quoted price | The price of the first option, which the user is charged |
| Absorbed cost | Extra cost of a pricier backup, paid by the business, not the user |
Built by Prem Dutta, an AI product leader who builds generative AI products and cares about the unit economics behind every generated second. This is a simplified, generic illustration of a routing pattern. All vendors are simulated and all costs are illustrative, not real pricing.
MIT licensed.