Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Visionary

A generative studio that runs on your own Modal account. Train a LoRA on your photographs, generate stills with it, and animate any of them into a clip — in one interface, on one URL, with nothing to keep alive between sessions.

The canvas holds the screen; the console is a bar beneath it

It is a real interface, not a form in front of a script. The canvas is the largest thing on screen at every moment, because the picture is the reason the page exists. Everything you can change lives in a bar under it — never a rail beside it, which would cost the image 384px of the one dimension it cannot get back.


The interface

Image and video are one workspace. They share the prompt, the canvas and the gallery. The switch is a chip inside the prompt field, and the sentence survives it — because a shot you described as a still is the same sentence you would describe as a clip. There is no mode to navigate to and nothing to retype.

The console rebuilds itself for the model you chose

The controls follow the model. Wan 2.2 takes LoRAs, a negative prompt and CFG; MiniMax-H3 is guidance-distilled and carries its own soundtrack, so it offers none of those and offers references instead. Only the controls the chosen model actually reads are on screen — a control that is present but ignored is worse than one that is absent.

LoRAs are written in the prompt. <lora:my_style:0.8>, the syntax anyone who has trained these models already types. Strength defaults to 1 and the token sits where the LoRA applies, so a fifth LoRA costs the canvas nothing — the rows this replaced cost 380px of it for four filenames. + LoRA still opens a picker, because you cannot type a syntax you have never seen.

Copy is a last resort — but a number is not a value it can show. Design first, then an icon, then words. A control that shows its own value gets no label; the two keyframe tiles put the mark where the frame sits in the clip rather than captioning themselves "first" and "last". Hyperparameters are the exception, and they are the exception on purpose: "32" is a rank, an alpha, an epoch count or a seed with equal plausibility, so every numeric field carries its name and the tooltip says what the number does.

Everything you have made, in one grid

Your work stays beside your work. The gallery is a drawer next to the canvas, not a destination you leave the studio to visit, because the still you made an hour ago is raw material for the clip you are making now. Any image can go straight back to the prompt, or become the first frame of a video, without a download and a re-upload. Open it full-width when you want the whole room.

Captioning is a workspace, not a batch job

Datasets are for reading, not just uploading. Captions are written in prose by Qwen3-VL-8B-Instruct, because the text encoders these models use parse grammar — "red dress, blue jacket" cannot say which garment is which, and a sentence can. The panel beside the contact sheet reads the set back to you: trigger-word coverage, caption length, duplicates, and the clauses your captions repeat, so you can see what the LoRA is about to learn by accident.


Install

pip install modal
modal setup
modal deploy app.py

That is the whole install. The last command prints a URL, and the URL is the application: interface, API and GPU jobs. Nothing runs on your machine, nothing runs when you are not using it, and there is no config file to fill in first.


What runs underneath

Train. LoRA training for Krea 2 on musubi-tuner. Point it at a folder of images, get a .safetensors back.

Caption. Datasets are named folders of images with .txt sidecars beside them, which is exactly what the trainer reads.

Generate stills. Krea 2 inference on a vendored sd-webui-forge-classic backend, with LoRA stacking and regional prompting.

Generate video. Two families through a driven ComfyUI:

MiniMax-H3 Wan 2.2
Audio yes, same latent silent
CFG / negative no — guidance-distilled yes
LoRAs no yes
References ref2va checkpoint no
Experts one two on A14B, one on the 5B

Adding Wan did not add a backend. It reuses the container, the warm ComfyUI process and the job contract; what is per-family is a graph builder and one row of capabilities — which is also the row the composer reads to decide what to show you.


Requirements

  • A Modal account. modal setup walks you through auth in a browser.
  • Python 3.10+ locally, only to run the modal CLI.
  • A HuggingFace account if you want Krea 2 — its weights are gated. Everything else downloads without one.

You do not need a local GPU, Docker, a .env file, or any Modal Secret. The HuggingFace token is pasted into the UI and stored in a Modal Dict.


First run: get the weights

Nothing downloads on its own. A fresh deployment has an empty volume and every model is opt-in, because the full catalogue is ~206 GB and almost nobody wants all of it. Open the deployed URL, click the gear, and pick what you need.

Family Size Gated What it buys
Krea 2 — images 62 GB yes training + still generation
MiniMax-H3 — video 64 GB no video with a soundtrack, references
Wan 2.2 — video 76 GB no silent video, CFG, LoRA support
Wan 2.2 speed LoRAs 5 GB no fewer steps per clip

You do not need a whole family. The smallest useful video setup is Wan 2.2 TI2V 5B at 18 GB — the 5B checkpoint, umT5-XXL and the 2.2 VAE — which does both text-to-video and image-to-video on its own.

Downloads run on CPU containers, never on a GPU. Pulling 26 GB while an A100 idles is money burned for nothing.

A transfer reports the bytes it has and the rate it is getting them at, and if it goes quiet for four minutes it is abandoned and resumed from where it stopped, up to five times. Both exist because of one failure: a 17 GB pull stopping dead at 4 GB and the job staying "running" — no error, no log line, no byte count — until the four-hour timeout collected it. A download that can hang is survivable; one that can hang silently costs you the four hours before you learn anything.

Gated weights

Krea 2 RAW and Krea 2 Turbo need a HuggingFace token, and you must accept the licence with the same account that issued it:

Paste the token under the gear. If the licence has not been accepted, the error says so and links the page rather than failing as a generic 403.

LoRAs from Google Drive

Most LoRAs worth having were never published to HuggingFace — they are a link someone sent you. Paste one under the gear and it lands in loras/, ready to name in a prompt.

  • A file link or a bare id both work; so does a folder link.
  • Only .safetensors is kept. A folder's preview grid and readme are named as skipped rather than quietly copied onto the volume.
  • Leave folder blank and the files drop in loose, each its own entry. Give one and they are grouped as versions of a single LoRA under loras/{folder}/ — which is right for a matched pair and wrong for a bag of unrelated ones, so it stays your call.
  • The link has to be shared with anyone who has it. Drive answers an unshared file with a sign-in page rather than an error, so the failure names that case explicitly instead of surfacing a parse error.

Storage

One Modal Volume, mounted at /workspace:

models/               weights, flat, addressed by exact filename
loras/                trained LoRAs, one folder each; loose files work too
datasets/{name}/      images + .txt caption sidecars
outputs/{job}/        generated media + a visionary.json sidecar
work/, .cache/        disposable

The layout is the contract, not the code. Datasets are folders of images with text files beside them — the same thing the trainer reads — so nothing here is required to get your data back out.

Run a second, isolated copy against its own storage by setting the volume name:

VISIONARY_VOLUME=visionary-test modal deploy app.py

GPUs and what they cost you

Each job type picks its own class, and most are switchable in the UI:

Job Default Options
Training A100-40GB
Captioning A100-40GB
Image generation A100-40GB A100-40GB, A100-80GB, H100
Video generation H100 H100, H200

Containers stay warm between requests (10 minutes for images, 15 for video) so consecutive takes skip the model load, then scale to zero. You are billed for GPU time while a job runs and while a container is warm — not for the deployment sitting idle.

Anything that can fail cheaply does. A bad LoRA path, an unknown aspect ratio or a missing weight is rejected on CPU in milliseconds, before a GPU is rented.


Verifying a deployment

Two smoke tests, both cheap, both runnable against your own account:

modal run tools/smoke_video.py

Checks every video graph — all twelve variants across both families — against the real ComfyUI node schema on a CPU container with no weights present. Catches a renamed node, a moved input, a dangling link, and a sampler the UI offers that ComfyUI does not have. It does not run a sampler, so it says nothing about whether a clip looks right.

modal run tools/smoke_krea2.py --gpu --lora any

Exercises the Krea 2 loader, the LoRA stack and the regional prompting path.

What has actually been run end to end

Being honest about coverage, since "it deploys" is not "it works":

  • Wan 2.2 TI2V 5B — text-to-video and image-to-video both verified on an H100, output inspected frame by frame.
  • MiniMax-H3 — graphs validate structurally; no full run yet, so the audio path is unproven.
  • Wan 2.2 A14B — graphs validate structurally. The two-expert handover cannot be checked structurally: wrong noise flags give a washed-out clip rather than an error, so only a real run will show it.

Working on the UI locally

The front end is one self-contained string in app.py with no build step, so it can be served locally against stubbed JSON instead of paying an image build and a cold start per CSS change:

python3 tools/preview_ui.py 8777

The stubs are shaped to hold the awkward states — a missing model, an uncaptioned dataset, a prompt too long to belong in a gallery card.


Layout

app.py     the whole application — images, jobs, API, and the UI
forge/     vendored sd-webui-forge-classic backend (see forge/VENDOR.md)
tools/     smoke tests and the local UI preview server
CLAUDE.md  the design rationale — why the code is shaped the way it is

app.py is deliberately one file. It is long, but the alternative — a package whose modules are imported by Modal image builds — trades one long file for a build-order problem, and the file is navigable by its banner comments.

If you are going to change anything, read CLAUDE.md first. It explains the tradeoffs the code is holding, including several that look like mistakes until you know what they are avoiding.


Licensing

AGPL-3.0. Not a preference — an inheritance, and worth understanding before you fork this or run it for anyone but yourself.

forge/ is a vendored slice of sd-webui-forge-classic, which is AGPL-3.0, and it is imported and executed as part of the image generation path rather than sitting there unused. AGPL-3.0 is strong copyleft with a network-use clause: section 13 means that if you modify this and let other people use it over a network, you owe those users the corresponding source — deploying rather than distributing is not the loophole it is under the GPL. forge/modules_forge/packages/comfy/ carries GPL-3.0 on top of that.

Since this deploys as a web application by design, that clause is the normal case here, not an edge one. Running your own private instance triggers nothing.

forge/VENDOR.md records the exact upstream commit and every local change, so a sync is a diff rather than an archaeology exercise.

Model weights carry their own separate licences — Krea 2's in particular is gated and has terms you accept on HuggingFace. Nothing here grants you rights to them.

About

Single-user LoRA training and generation platform on Modal. Train Krea 2 LoRAs, generate stills, and turn them into video with MiniMax-H3 (sound and picture in one pass) or Wan 2.2 — one deploy, one URL, no infrastructure to keep alive.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages