midnight-static / docs /ARCHITECTURE.md
cajpany's picture
Upload folder using huggingface_hub
fc9b7e2 verified
|
Raw
History Blame Contribute Delete
6.19 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade

ARCHITECTURE.md β€” Midnight Static

System diagram

                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                      β”‚              GRADIO SPACE (ZeroGPU)         β”‚
                      β”‚                                             β”‚
 mic ──► Nemotron ASR ─┐                                            β”‚
                       β”œβ”€β–Ί Nemotron Nano 4B + LoRA ──► Script JSON  β”‚
 text β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      (vLLM, guided JSON)        β”‚          β”‚
                                                         β–Ό          β”‚
                      β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ fan-out ────────────┐       β”‚
                      β”‚   β”‚ TTS: Kokoro per line             β”‚       β”‚
                      β”‚   β”‚ SFX: embed β†’ cache match ──hit──►│       β”‚
                      β”‚   β”‚        └─miss─► SAO Small gen    β”‚       β”‚
                      β”‚   β”‚ Music: embed β†’ bed/sting library β”‚       β”‚
                      β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚
                      β”‚                  β–Ό                           β”‚
                      β”‚        Mixer (pydub/ffmpeg)                  β”‚
                      β”‚                  β–Ό                           β”‚
                      β”‚   broadcast.mp3 + (stretch) FLUX poster      β”‚
                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ MODAL (training & batch only) ───────────┐
 β”‚ gen_dataset.py  β†’ 400 synthetic scripts (Codex-driven sessions)  β”‚
 β”‚ finetune.py     β†’ LoRA on Nemotron Nano 4B (Unsloth, A100)       β”‚
 β”‚ batch_audio.py  β†’ SFX library (~150 clips) + music beds/stings   β”‚
 β”‚                   (6 genres Γ— 4 beds Γ— 3 stings) + embeddings    β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Model inventory (Tiny Titan evidence β€” keep exact and current)

Model Params Role Runtime location
Nemotron 3 Nano 4B + LoRA 4.0B scriptwriter ZeroGPU (vLLM)
Nemotron 3 ASR <1B call-in transcription ZeroGPU
Kokoro-82M 0.082B TTS, all voices ZeroGPU (near-CPU fast)
MusicGen-small 0.30B music (batch only) Modal batch
Stable Audio Open Small ~0.34B SFX (batch + cache-miss) Modal batch + ZeroGPU
Embedding model (e.g. bge-small, 33M) 0.033B SFX/music matching ZeroGPU CPU
FLUX.2 Klein 4B (stretch) 4.0B episode posters ZeroGPU, async
VoxCPM2 (gate G1) <4B (verify) TTS co-engine ZeroGPU

Ceiling check: every model individually ≀4B βœ… (Tiny Titan), sum β‰ˆ 9B β‰ͺ 32B βœ….

ZeroGPU strategy

  • One Space, @spaces.GPU(duration=...) on innermost calls only:
    • writer.generate() durationβ‰ˆ30
    • sfx.generate_miss() durationβ‰ˆ25 (rare path)
    • poster.generate() durationβ‰ˆ30 (async, never blocks audio)
  • Kokoro + embeddings run on CPU β€” do NOT burn GPU quota on them.
  • Models lazy-load on first use, then persist in process globals.
  • vLLM engine kept warm via module-level init guarded by a flag; if ZeroGPU cold-starts hurt (>20s), switch writer to transformers + xgrammar (slower tokens, faster init) β€” decide empirically Day 1.

Asset library design (assets/)

assets/
  manifest.json        # [{id, kind: sfx|bed|sting, genre?, prompt,
                       #   file, embedding: [..]}]
  sfx/*.wav            # ~150 clips, 24kHz mono, 1–6s
  music/{genre}/*.wav  # 4 beds (30–45s, loopable) + 3 stings (3–6s) per genre
  • Matching: embed the script's text prompt (bge-small), cosine vs manifest.
    • SFX: threshold β‰₯0.62 β†’ use cached; else generate (then append to manifest at runtime β€” the library learns).
    • Music: ALWAYS nearest match within the script's genre, never generate at runtime (beds are too slow to generate live).
  • Manifest + wavs ship in the Space repo via Git LFS (~80MB budget).

Pipeline orchestration (src/pipeline.py)

  • Async stages with a StageEvent(stage, status, detail) queue consumed by the UI frequency-scan loader. Stage order: asr? β†’ write β†’ cast_validate β†’ tts βˆ₯ sfx βˆ₯ music β†’ mix β†’ on_air β†’ poster?.
  • TTS/SFX/music run concurrently (asyncio + thread pool; they're different devices: CPU/GPU/disk).
  • Single retry policy: schema/cross-field validation failure β†’ one retry with error appended; second failure β†’ swap in genre fixture's structure with user's premise woven into title/logline (never show an error page; the station "improvises").

Latency budget (p50, warm)

Stage Target
ASR (if voice) 2s
Script (4B, ~1.4k tok) 12s
TTS (~18 lines, CPU, parallel) 5s
SFX (cache hits) 0.3s
Music (library) 0.1s
Mix + encode 3s
Total (text premise) ~21s
Cache-miss SFX penalty +8s each (≀2 misses typical)

p50 ≀35s target from SPEC.md has ~14s headroom for cold starts.

Failure modes & responses

Failure Response
vLLM OOM / engine crash restart engine once; then transformers fallback path
ZeroGPU quota exhausted (judge traffic) pre-cached showcases still play (CPU); banner: "Station at capacity β€” enjoy a rerun"
ffmpeg mix error re-mix without SFX layer (dialogue+music only)
ASR garbage show transcript for confirm/edit before writing

Security/content

  • Premise input length-capped (300 chars) and passed through the writer's PG-13 system constraints; no user text is ever shell-interpolated (ffmpeg called with arg lists, never shell=True).