
When we say Vivid is a large codebase, we mean it: a desktop app, a Rust backend, Python inference, cloud runtimes, and shared contracts tying them together. One of the hardest problems was not “run a model”—it was making every layer agree on what running a model means.
The React renderer, the Tauri backend, and the Python runtime are three different environments. They do not naturally share types, imports, or assumptions. If each layer invents its own notion of engines, backends, and dependencies, you get configuration drift: the UI shows options the backend will reject, or the runtime discovers missing packages halfway through a job.
We solved that with a single cross-runtime engine catalog and strict consumption at each boundary.
The problem: multiple runtimes, multiple truths
Most AI apps break quietly:
- The frontend assumes a model exposes parameters X
- The backend validates against Y
- The runtime crashes on Z
That is configuration drift, and it destroys reliability and debuggability.
At a systems level, the shape of the fix is familiar:
What we ship: one JSON catalog
In Vivid, the canonical definition lives in src/shared/engine_runtime_catalog.json. It is versioned (for example version: 2), carries the pinned Python runtime expectation, lists upstream repos with dependency pins, and defines an engines array.
Each engine entry describes, among other things:
- engineId, operation, human-facing label and description
- supportedBackends — TensorRT variants, ONNX Runtime paths (CUDA, CoreML, DirectML), OpenVINO, MIGraphX, NCNN, CPU, etc.
- defaultBackendByPlatform — sensible defaults per OS
- requiredPackagesByBackend and requiredUpstreamRepos
- requiredPythonModules
- executionMode (for example VSMLRT, hybrid, native PyTorch)
- supportsOnnx / supportsPytorch and fallbackOrder
That is the contract: model configuration as data, not as three hand-maintained lists.
How each layer consumes the same file
1. TypeScript (renderer)
The catalog is imported as JSON and typed in src/shared/engineRuntimeCatalog.ts. Helpers like getEngineRuntimeCatalog(), getEngineEntry(), and getRequiredPackagesForBackend() give the UI and services a single API over the same data the backend and Python use.
Compatibility logic (for example which backend strings imply NVIDIA vs Apple Silicon) still lives in code—src/renderer/services/engineCompat.ts—but it is informed by the catalog’s backend vocabulary so the UI does not fork a parallel engine list.
2. Rust (Tauri)
The backend embeds the catalog at compile time. src-tauri/src/core/engine_resolver.rs uses include_str! to load engine_runtime_catalog.json, deserializes it into EngineRuntimeEntry / EngineRuntimeCatalog, and resolves requirements such as which package IDs and upstream repos an engine_id + backend pair needs. Invalid combinations can be caught before a long-running job starts.
The same JSON is also bundled with the app via src-tauri/tauri.conf.json so the runtime path stays consistent in packaged builds.
3. Python (local inference)
src/inference/inference_impl/engine_runtime_catalog.py locates the same file (with VIVID_CATALOG_PATH override for tests or container layouts), loads it into frozen dataclasses, and exposes lookups for backend policy and dependency preflight—backend_policy.py and related code consume those definitions instead of hardcoding engine tables.
4. Cloud (Modal)
The cloud image wires the catalog into the container environment so remote runs enforce the same backend and dependency policy as local execution—see paths like VIVID_CATALOG_PATH and the shared catalog copy in src/cloud/modal_runtime/image.py.
Guardrails in the repo
Drift is not only a runtime bug; it is a process bug. The repository includes validation that keeps pins and catalog entries aligned—scripts/validate_engine_runtime_catalog.py and scripts/validate_torch_runtime_stack.py—so dependency bumps do not silently diverge from what the catalog claims.
Early checks in CI or local dev catch catalog mistakes before they become user-facing failures.
Why this matters
Without a single catalog:
- The UI can lie (show engines or backends that cannot run)
- Jobs fail mid-execution with obscure import or backend errors
- Debugging means comparing three different mental models
With one catalog and typed boundaries:
- Jobs are much easier to reason about and reproduce
- Validation can happen early, in the shell and in the queue
- Adding or extending an engine is data + code in known places, not a scavenger hunt across stacks
The insight
Treat model and engine configuration like an API contract—versioned, shared, and enforced—not a suggestion each layer can reinterpret.
Building Vivid was never “just” an upscaler: it was a system that has to run locally, in the cloud, and under failure, without wasting users’ time. The engine catalog is one of the pieces that makes that claim credible.
How the layers connect
We’re shipping soon—follow along for the rest of the story.