Skip to content

kasbsquall/digital-farm-showrunner

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

71 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🐔 MUCKFLIX — The Digital Farm Showrunner

An autonomous pipeline of AI agents that writes, art-directs, films, quality-checks and packages a brand-new claymation farm micro-drama video every single day — end to end, with no human production team.

Track License Models Cloud Backend Frontend

Hackathon: Qwen Cloud Global AI Hackathon 2026 — Track 2: AI Showrunner. Deliverable: a general engine for daily, on-brand, character-consistent serialized video. The claymation farm ("MUCKFLIX") is the demo channel; the pipeline is the product.


🎬 Demo & live proof

▶ Demo video (2:28): https://youtu.be/afZ2do3w900

Flagship proof — the self-correcting QA loop. The retake loop isn't a claim: it's visible in the "How it was made" retake view. A mock-mode "Wobbly Gold Spheres…" episode illustrates the full rejected → rejected → approved arc shape, and a real, live-infra regeneration run is committed too — "Sheep Drops Hay on Pig" regenerated twice on real vision-grounded qa_notes before the QA gate shipped the best take as a draft ($0.84 blended, consistency 0.8 held across takes). See backend/database/snapshot.json and the honest "loop firing on live infrastructure" section of docs/EVALUATION.md.


1. The problem & the insight

The problem

Short-form video is the oxygen of the modern internet. But producing a serialized daily show is slow, expensive, and needs a whole team — a writer, an art director, a camera/animation crew, a QA pass, and an editor to package and publish. Creators, SMBs and brands are stuck on a treadmill they can't staff.

Generic AI-video tools don't solve this. They generate one-off clips with two fatal gaps for anyone running a show:

Gap in one-off AI video Why it kills a serialized show
No character continuity Your mascot looks different every clip. There is no cast, so there is no "show."
No quality control The model frequently produces something other than what you asked for — and nobody catches it. You cannot publish unsupervised.

The insight

A daily show doesn't need a better clip generator — it needs a studio: a repeatable production line where recurring characters stay on-model, and where a step at the end actually watches the footage and calls a retake when it's wrong. That is exactly what a human showrunner's team provides, and it is what MUCKFLIX automates.

Two techniques make this work (detailed in §5):

  1. Keyframe → image-to-video for character consistency: a still is generated in each character's established look, and that exact still is animated — so the cast stays on-model, and the still doubles as a perfectly coherent thumbnail.
  2. A vision-grounded, self-correcting QA loop: a vision model describes what the actual generated clip shows; QA rejects on mismatch; the rejection note is fed back to the Director to regenerate — bounded so cost can never run away.

2. What it does (the product)

MUCKFLIX takes a one-line idea (or nothing at all) and runs the entire production of a finished, publish-ready episode:

  • Bring your own character. Anyone can add their own claymation character to the cast (with an AI-generated portrait). Point the same engine at your mascot and it runs your daily show.
  • Audience co-creation. A viewer casts the stars, pitches a gag, and gets credited on the resulting episode — the built-in path to virality and monetization (subscriptions per channel + microtransactions to prioritize your idea for tomorrow).
  • Watch it produce, live. The "Studio" wizard streams every production stage over Server-Sent Events, including the QA retake loop, so you can watch the agents work in real time.
  • A finished episode, not a raw clip. Each run yields a viral title, a coherent thumbnail (the keyframe), a QA verdict, and a publish-ready video with consistent characters.

Engine vs. demo framing

The farm — a claymation barnyard of romantic cows, mud philosophers, and an aggressively territorial goose — is a demo channel chosen because it makes character-consistency and comedic QA visually obvious. Swap the cast, the style prompt, and the brand, and the same pipeline produces a daily show for any vertical: brand mascots, kids' channels, educational series.


3. The base cast

Seeded so the Scriptwriter has continuity from day one (see backend/database/seed_characters.py). All plasticine, all Aardman-style.

Character Species Personality (one-liner)
Lola cow Hopeless romantic — falls in love with buckets, fenceposts, clouds.
Pepe pig The mud philosopher; deep-sounding nonsense, never leaves the puddle.
Nina hen Breaking-news reporter treating every triviality as a world-shaking scoop.
Gus goat Chaotic troublemaker who eats everything, zero remorse.
Dora duck Dramatic diva and conspiracy theorist.
Bex sheep Anxious overthinker; agrees with whoever spoke last.
Momo donkey Deadpan pessimist with a gloomy one-liner for everything.
Kiki goose Self-appointed, extremely aggressive pond security guard.

4. Architecture

The backend is a LangGraph StateGraph wiring four agents plus a video sub-stage. State flows through a single typed dict (FarmState) and each node returns a partial update.

The agents

# Agent File Qwen model / surface Input Output
1 Scriptwriter backend/agents/scriptwriter.py qwen3.7-plus (text) Full cast, recent episode events (for continuity), optional user idea event, script (2–4 lines), characters_used
2 Production Director backend/agents/production_director.py qwen3.7-plus (text) The script + the faithful visual description of each cast member; on a retake, the QA rejection note keyframe_prompt (a frozen funniest instant), motion_prompt (one 5s action), video_tool
Video sub-stage backend/pipeline/orchestrator.py → services qwen-image-2.0happyhorse-1.1-i2vqwen3-vl-plus Director's keyframe + motion prompts (a shots list when SHOTS_PER_EPISODE > 1) Persisted video_url (multi-shot clips stitched into one), thumbnail_url (= the keyframe), a vision video_description, and an optional identity-lock consistency score
3 QA Reviewer backend/agents/qa_reviewer.py qwen3.7-plus (text), reasoning over qwen3-vl-plus output Script, motion prompt, and what the clip actually shows qa_status (approved/rejected), qa_notes
4 Packager backend/agents/packager.py qwen3.7-plus (text) Event, script, and the vision description title (with emojis), thumbnail_hint, description (+ hashtags)

Every agent asks the model for strict JSON and parses it through a tolerant extractor (backend/agents/_json.py) that strips code fences, isolates the outer {...}, and repairs trailing commas / smart quotes before a second json.loads. An LLM formatting slip therefore never crashes the pipeline — each agent also falls back to safe defaults on missing keys.

The pipeline flow

recent events + cast (SQLite / Alibaba Cloud RDS PostgreSQL)
                     │
                     ▼
 ┌──────────────── LangGraph StateGraph ─────────────────┐
 │                                                       │
 │  START → scriptwriter → director → video → qa_review  │
 │                           ▲                    │      │
 │                           │                    │      │
 │                  ┌────────┘   rejected &       │      │
 │                  │            attempt≤MAX_REGEN │      │
 │                  └────────────────◄────────────┘      │
 │                                    │                  │
 │                        approved OR out of budget      │
 │                                    ▼                  │
 │                                packager → END         │
 └──────────────────────────┬────────────────────────────┘
                            ▼
        Episode row (DB) + video/thumbnail on Alibaba Cloud OSS
                            │
                            ▼
              Next.js feed  ◄── FastAPI /episodes

The conditional edge after qa_review (_after_qa in the orchestrator) is the heart of the graph:

  • qa_status == "approved" → go to packager.
  • rejected and attempt > MAX_REGEN → go to packager anyway (publish the best take, never loop forever).
  • rejected and budget remains → go back to director, this time with the rejection note.

A rendering of the full architecture lives at docs/architecture_diagram.png (editable source: docs/architecture_diagram.svg), with the agent flow also described in docs/README.md.

How Qwen Cloud connects to backend, DB and frontend

  • Backend ↔ Qwen Cloud. Text agents call the OpenAI-compatible DashScope endpoint via backend/services/qwen_client.py. Image, video and vision calls use the native DashScope endpoint, derived automatically from the same base URL (/compatible-mode/v1/api/v1, see config.dashscope_base). The correct base URL is auto-selected by API-key prefix (see §10).
  • Backend ↔ DB. SQLAlchemy models (Character, Episode) persist to SQLite by default, or Alibaba Cloud RDS PostgreSQL by swapping DATABASE_URL. Every finished run is written as one Episode row.
  • Backend ↔ OSS. Generated media is downloaded from DashScope's temporary URLs and re-uploaded to Alibaba Cloud OSS for a stable public link (backend/services/oss_client.py).
  • Frontend ↔ Backend. Next.js reads /characters and /episodes (REST) and consumes /episodes/generate/stream (SSE) to render the live Studio wizard. OSS images are downsized on the fly with ?x-oss-process=image/resize,... (ossThumb in frontend/lib/api.ts).

5. The two signature techniques

(a) Keyframe → image-to-video for character consistency

Instead of a blind text-to-video prompt, the video sub-stage runs three deliberate steps (video_node in backend/pipeline/orchestrator.py):

  1. Keyframeqwen-image-2.0 paints a single frozen still of the funniest split-second of the gag, describing every character in their established look (the Director is fed each cast member's visual_desc). This still is persisted to OSS.
  2. Animatehappyhorse-1.1-i2v (image-to-video) takes that exact still as frame 0 and animates one continuous ~5-second motion. Because the video starts from the on-model keyframe, the characters inherit their established appearance instead of being re-hallucinated.
  3. Describeqwen3-vl-plus watches the resulting clip (feeds the next technique).

Two consequences fall out for free:

  • The keyframe is the thumbnail. thumbnail_url is set to the keyframe URL — a thumbnail that is guaranteed to match frame 0 of the video, at no extra generation cost.
  • A consistent clay universe. A shared style string (Aardman plasticine, visible clay fingerprints, chunky proportions) plus an anti-artifact negative prompt (no text, no watermark, no distorted limbs, no melted faces…) is appended to every keyframe so episodes look like the same show and avoid the tells that scream "AI."

Two shipped extensions of this technique (both validated on real Alibaba Cloud generation):

  • Multi-shot episodes. An episode can be N chained shots — setup → escalation → punchline — instead of a single gag. Each shot gets its own keyframe (qwen-image-2.0) → i2v clip (happyhorse-1.1-i2v), and all shots are stitched into one continuous video via ffmpeg (video_gen_client.stitch(), oss_client.persist_local(); the Director emits a shots list when SHOTS_PER_EPISODE > 1, and video_node takes the multi-shot branch). The first shot's keyframe becomes the thumbnail. Config flag SHOTS_PER_EPISODE (default 1 = the classic single-gag micro-drama). A real 3-shot Pepe episode was generated and stitched into a single 15.5-second video (3 × ~5s shots) and persisted to OSS.
  • Identity-lock consistency gate. After a keyframe is generated, a qwen3-vl-plus vision pass (vision_client.consistency_score()) scores how well the character in the keyframe matches its canonical reference portrait, 0.0–1.0, stored per take as consistency. This is now an enforcing gate, not just telemetry: when the score falls below IDENTITY_MIN (default 0.55), the take is rejected as off-model — a hard fail that overrides the action-quality verdict and drives the regen loop, and on that retake the keyframe is regenerated fresh (not surgically reused), because the off-model keyframe is the problem. IDENTITY_MIN=0 measures without gating. Because the qwen-image endpoint does not accept a reference image, identity is enforced by scoring the result (vision), not by conditioning the image generator. Config flag IDENTITY_CHECK. It calibrated correctly on real generation. Two distinct measurements (don't conflate them):
    • Keyframe vs. canonical portrait (the production gate): a matching Pepe scores 0.8–0.9, a different character scores 0.0 — so the IDENTITY_MIN gate cleanly separates on-model from off-model. The score is a stochastic qwen3-vl judgment (≈±0.1 per call); a committed real run over committed OSS images is in docs/deploy_proof/identity_calibration.json.
    • Keyframe vs. keyframe, same character across two different episodes (cross-episode consistency, shown in the demo): 1.00 — the cast stays on-model from one episode to the next.

(b) The vision-grounded, self-correcting QA loop

One-off tools generate and hope. MUCKFLIX closes the loop on the real footage:

  1. Vision grounding. qwen3-vl-plus (backend/services/vision_client.py) narrates the actual chronological cause-and-effect of the generated clip — who hits/pushes/throws what, and how the other reacts — observing only what's on screen.
  2. QA judges reality, not intent. The QA agent compares that description against the script and the motion prompt. It is strict but not a perfectionist: it rejects only on serious incoherence or a broken/empty video. And if the vision description comes back empty (vision unavailable), QA rejects (score 0.0, "Vision could not verify the clip — not approved") instead of auto-approving, so nothing publishes unwatched — the loop retries and, if vision never resolves, the episode is published as a draft, never as approved.
  3. Targeted feedback. On rejection, the qa_notes are fed back into the Director (director_node reads state["qa"]["qa_notes"] on any attempt after the first), which redesigns the keyframe and motion to specifically fix that problem.
  4. Bounded by MAX_REGEN. The loop can run at most MAX_REGEN (default 2) Director re-runs. If still rejected, the best take is packaged and published as a draft rather than looping forever — this is the token-budget guard.

Downstream, both the QA verdict and the Packager's title/description are grounded in the vision output, so the published copy describes what the video actually shows ("the rooster smacks the bread into Pepe's mouth"), not merely what the script intended.


6. Qwen Cloud & Alibaba Cloud usage

Qwen Cloud (DashScope) capabilities

Capability Model (default) Endpoint surface File
Text agents (Writer, Director, QA, Packager) qwen3.7-plus OpenAI-compatible /compatible-mode/v1 backend/services/qwen_client.py
Keyframe & portrait generation qwen-image-2.0 Native DashScope multimodal-generation (sync) backend/services/image_gen_client.py
Image-to-video (animate the keyframe) happyhorse-1.1-i2v Native DashScope video-synthesis (async submit/poll) backend/services/video_gen_client.py
Text-to-video (fallback) happyhorse-1.1-t2v / wan2.7-t2v Native DashScope video-synthesis (async submit/poll) backend/services/video_gen_client.py
Video understanding for QA/packaging qwen3-vl-plus OpenAI-compatible (video_url content part) backend/services/vision_client.py

DashScope video generation is genuinely asynchronous: the client POSTs with X-DashScope-Async: enable to get a task_id, then polls GET /tasks/{id} every video_poll_seconds until SUCCEEDED/FAILED, capped by video_timeout_seconds. Transient network/5xx errors are retried with backoff; 4xx errors fail fast.

Alibaba Cloud

Service Role Proof / file
ECS (Docker on Ubuntu) Runs the FastAPI backend backend/deploy/deploy.sh, docs/DEPLOYMENT.md
OSS (Object Storage) Permanent public storage for generated videos, keyframes/thumbnails and portraits backend/services/oss_client.py
OSS deploy-proof A standalone script that uploads a marker object and reads it back — a real service call, not a mention backend/deploy/alibaba_deploy_proof.py
RDS PostgreSQL (optional) Production database (swap DATABASE_URL) docker-compose.yml, backend/database/db.py

Why OSS is mandatory for live generation. DashScope returns temporary signed URLs for generated media that expire. Persisting those would silently break the feed later, so live video generation refuses to run unless OSS is configured (video_node raises if oss_client.is_configured() is false). Every generated asset is downloaded and re-hosted on OSS before it reaches the DB.


7. Tech stack

Layer Technology
Orchestration LangGraph StateGraph (typed state, conditional regen edge)
Backend API FastAPI + Uvicorn (Python 3.11)
AI models Qwen Cloud (DashScope) — text, image, video, vision
HTTP / SDK openai (OpenAI-compatible), httpx (native DashScope async)
Config / validation pydantic-settings, Pydantic v2
Database SQLAlchemy 2 → SQLite (default) or Alibaba Cloud RDS PostgreSQL
Object storage Alibaba Cloud OSS (oss2)
Frontend Next.js 14 (App Router), React 18, TypeScript, SSE (EventSource)
Deploy Docker on Alibaba Cloud ECS; docker-compose for local Postgres

8. Project structure

cloudhackathon/
├── README.md                  # (this file)
├── LICENSE                    # MIT
├── docker-compose.yml         # local Postgres + backend
├── backend/
│   ├── main.py                # FastAPI app: health, characters, episodes, generate, SSE stream
│   ├── config.py              # env config; endpoint auto-select by key prefix; mock/demo flags
│   ├── Dockerfile             # python:3.11-slim → uvicorn
│   ├── requirements.txt
│   ├── pipeline/
│   │   └── orchestrator.py    # LangGraph graph + nodes + regen loop + DB persistence
│   ├── agents/
│   │   ├── scriptwriter.py    # Agent 1 — the day's gag + tiny script
│   │   ├── production_director.py  # Agent 2 — keyframe + motion prompts (+ QA feedback)
│   │   ├── qa_reviewer.py     # Agent 3 — approve/reject on the real clip
│   │   ├── packager.py        # Agent 4 — viral title + description
│   │   └── _json.py           # tolerant LLM-JSON extractor/repair
│   ├── services/
│   │   ├── qwen_client.py     # text (OpenAI-compatible) + mock mode + retries
│   │   ├── image_gen_client.py# keyframe/portrait (sync DashScope multimodal)
│   │   ├── video_gen_client.py# image→video & text→video (async submit/poll)
│   │   ├── vision_client.py   # qwen3-vl video description
│   │   └── oss_client.py      # persist media to Alibaba Cloud OSS
│   ├── database/
│   │   ├── models.py          # Character, Episode
│   │   ├── db.py              # engine/session
│   │   ├── seed_characters.py # base 8-animal cast
│   │   ├── seed_from_snapshot.py  # zero-cost seed of real episodes/portraits
│   │   └── snapshot.json      # committed demo data (media on OSS)
│   └── deploy/
│       ├── deploy.sh          # one-command ECS deploy
│       └── alibaba_deploy_proof.py  # real OSS call (deploy proof)
├── frontend/                  # Next.js 14 App Router
│   ├── app/page.tsx           # landing: hero, Studio, CinemaFeed, CastGrid
│   ├── components/
│   │   ├── Studio.tsx         # live SSE wizard (cast picker, idea, per-stage activity)
│   │   ├── CinemaFeed.tsx     # episode feed + behind-the-scenes modal
│   │   └── CastGrid.tsx       # cast + "create your own character"
│   └── lib/api.ts             # REST + SSE client, OSS thumbnail helper
└── docs/
    ├── DEPLOYMENT.md          # Alibaba Cloud deploy + proof capture
    ├── README.md              # architecture notes
    ├── architecture_diagram.png / .svg
    └── pipeline_graph.md

9. API reference

Base URL defaults to http://localhost:8000. Served by backend/main.py.

Endpoint Method Purpose
/health GET Liveness + current mock flags (mock_text, mock_video).
/characters GET List the cast (name, species, personality, portrait URL).
/characters POST Create your own character (optionally with an AI portrait). Body: name, species, personality, look.
/episodes GET All episodes, newest first (full record incl. video, thumbnail, QA).
/episodes/generate POST Run the full 4-agent pipeline and return the new episode. Body: idea, creator.
/episodes/generate/stream GET Server-Sent Events: emits each pipeline stage live for the Studio wizard. Query: idea, creator.

Concurrency guard. Generation is CPU/IO-heavy and blocking, so a process-wide lock allows only one generation at a time; a second concurrent request gets 409 (POST) or a failed SSE event. The SSE stream emits one event per node (scriptwriter, director, video, qa, packager) and finally done with the new episode id — or failed with a message on error.


10. Local setup & deploy

10.1 Environment variables

All config is env-driven (backend/config.py). Mock mode is the default so the whole app runs with zero keys and zero cost.

Variable Default Purpose
DATABASE_URL sqlite:///./farm.db DB DSN; swap for Alibaba Cloud RDS PostgreSQL.
QWEN_API_KEY (empty) Qwen Cloud key. Empty ⇒ mock mode.
QWEN_BASE_URL_OVERRIDE (empty) Workspace/Model Studio endpoint; wins over auto-selection.
QWEN_BASE_URL_PAYG dashscope-intl…/compatible-mode/v1 Endpoint for pay-as-you-go keys (sk-…).
QWEN_BASE_URL_TOKEN_PLAN token-plan.ap-southeast-1…/compatible-mode/v1 Endpoint for hackathon token-plan keys (sk-sp-…).
QWEN_TEXT_MODEL qwen3.7-plus Text model for the 4 agents.
VISION_MODEL qwen3-vl-plus Video-understanding model.
IMAGE_MODEL qwen-image-2.0 Keyframe + portrait generation.
VIDEO_MODEL_I2V happyhorse-1.1-i2v Image-to-video (the primary path).
VIDEO_MODEL / VIDEO_MODEL_WAN happyhorse-1.1-t2v / wan2.7-t2v Text-to-video fallbacks.
MOCK_VIDEO true Keep video mocked even when text is live (flip to false for real video).
FORCE_MOCK false Force full mock mode even with a key present.
MAX_REGEN 2 Max Director retakes after a QA rejection (cost cap).
SHOTS_PER_EPISODE 1 Shots per episode (setup→escalation→punchline); >1 renders N keyframe→i2v shots stitched into one video. 1 = classic single-gag.
IDENTITY_CHECK false Score each keyframe's character vs. its canonical portrait with qwen3-vl-plus (0.0–1.0), stored per take as a measurable consistency gate.
IDENTITY_MIN 0.55 Reject + regenerate (fresh keyframe) any take scoring below this vs. the canonical portrait. 0 = measure only, don't gate.
IMAGE_COST_USD / VIDEO_COST_USD_PER_SECOND 0.02 / 0.05 List-price estimates for the blended per-episode cost meter (image + video, not just text tokens).
SCHEDULER_ENABLED / SCHEDULER_INTERVAL_HOURS false / 24 Unattended daily-channel loop: run the showrunner on an interval and publish on the QA verdict.
VIDEO_POLL_SECONDS / VIDEO_TIMEOUT_SECONDS 10 / 600 Async video poll interval / timeout.
VIDEO_DURATION 0 Clip length; 0 = model default (~5s).
OSS_ACCESS_KEY_ID / OSS_ACCESS_KEY_SECRET / OSS_BUCKET / OSS_ENDPOINT (empty) Alibaba Cloud OSS (required for live video).
DEMO_VIDEO_URL / DEMO_THUMBNAIL_URL / DEMO_VIDEO_DESC / DEMO_PACE_SECONDS (empty/0) Recording/demo mode: replay a real clip at zero cost and pace the SSE so each stage is visible.
CREATE_REAL_PORTRAITS false Generate a real portrait for user-created characters even in mock mode.
NEXT_PUBLIC_API_URL http://localhost:8000 (frontend) Backend base URL.

10.2 Run modes at a glance

Mode How Cost Behavior
Mock (default) No QWEN_API_KEY, or FORCE_MOCK=true $0 Every agent returns a deterministic canned response; video is a placeholder URL; QA auto-approves. The whole graph runs offline — ideal for tests and CI.
Demo / recording Mock + DEMO_VIDEO_URL (+ DEMO_PACE_SECONDS) $0 Replays a real clip and paces the SSE so the wizard looks live end-to-end.
Text-live, video-mock QWEN_API_KEY set, MOCK_VIDEO=true text tokens only Real Qwen scripts/direction/QA/packaging; placeholder video.
Fully live QWEN_API_KEY + MOCK_VIDEO=false + OSS_* full Real keyframe → i2v → vision → QA loop; media persisted to OSS.

The mock/live switch requires no code change — every service checks settings.use_mock and returns caller-supplied mock data (backend/services/qwen_client.py).

10.3 Backend (local)

cd backend
python -m venv .venv && source .venv/bin/activate   # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env               # optional; empty .env = mock mode, $0
python -m database.seed_characters # seed the base cast
uvicorn main:app --reload

Verify: http://localhost:8000/health · http://localhost:8000/characters

To populate the app with the committed real episodes/portraits (served from OSS) at zero cost:

python -m database.seed_from_snapshot   # idempotent

10.4 Frontend (local)

cd frontend
npm install
npm run dev            # http://localhost:3000  (set NEXT_PUBLIC_API_URL if backend isn't on :8000)

10.5 Local Postgres via Docker

docker compose up --build   # Postgres 16 + backend on :8000

10.6 One-command Alibaba Cloud deploy

On a fresh Ubuntu ECS instance (allow inbound TCP 22 + 80):

ssh root@<ECS_PUBLIC_IP>
curl -fsSL https://raw.githubusercontent.com/kasbsquall/digital-farm-showrunner/main/backend/deploy/deploy.sh | bash

This installs Docker, builds the image, runs it on port 80→8000, writes a FORCE_MOCK=true .env (no keys, no credits), and seeds the cast + real episodes from the snapshot — so the app is fully populated and live for $0. Verify with curl http://<ip>/health and /episodes.

To enable live generation and prove OSS from the instance, set FORCE_MOCK=false, add QWEN_API_KEY + OSS_*, re-run the container, then:

docker exec muckflix python -m deploy.alibaba_deploy_proof   # OSS upload ok=True url=...

Full steps and proof-capture checklist: docs/DEPLOYMENT.md.


11. Testing

The mock harness makes the pipeline fully deterministic and offline-testable: with FORCE_MOCK=true, every agent returns a canned response derived from its own inputs, so the entire LangGraph flow (including the QA→Director regen edge) can be exercised without any network or credits.

A 63-test pytest suite lives under backend/tests/ and runs fully offline:

cd backend
pip install pytest httpx
FORCE_MOCK=true python -m pytest tests/ -q     # 31 passed

Coverage highlights:

Test file What it locks in
test_regen.py The self-correcting loop: a rejected take is regenerated, the corrected take is approved & published, and the auditable take history accumulates — plus the budget-cap path.
test_orchestrator.py _after_qa routing (approve / retake / out-of-budget) and graph node ids.
test_pipeline.py Full mock-mode E2E: run_stream yields every stage in order and persists an Episode.
test_api.py /health, /characters (+409 duplicate), /episodes, the SSE stream, and the in-flight lock.
test_json.py / test_config.py JSON repair (fences, trailing commas, smart quotes) and endpoint auto-selection by key prefix.

12. Cost & token-budget notes

Cost control is a first-class design goal, not an afterthought:

Lever Effect
Keyframe → i2v One image + one short i2v job is cheaper and more controllable than brute-forcing long text-to-video, and the keyframe doubles as the thumbnail (no extra generation).
MAX_REGEN Hard cap on QA retakes (default 2). The loop always terminates into the Packager; it can never run away on tokens/video jobs.
Mock mode No key ⇒ every model call is skipped; the full app runs at $0 for development, CI and demos.
Demo/recording mode Replays a real clip at $0 so the pipeline can be recorded end-to-end without spending credits.
Snapshot seeding A deployed instance shows real episodes/portraits (served from OSS) with zero API calls.
MOCK_VIDEO split Run real text agents while keeping the expensive video step mocked, to iterate on prompts cheaply.
OSS on-the-fly resize ossThumb requests downscaled WebP (x-oss-process), cutting feed payloads from ~1.4 MB to a few KB.

Defensibility & unit economics

The moat is three layers that compound, not one trick.

  • Character consistency by construction — and now enforced. Keyframe → i2v pins the cast to an established look (§5a); competitors doing blind text-to-video can't hold a mascot across a daily series. An optional identity-lock gate (IDENTITY_CHECK) scores each keyframe against its canonical portrait with qwen3-vl-plus (calibrated 0.8–0.9 for a matching character, 0.0 for a mismatch) and rejects any take below IDENTITY_MIN, regenerating a fresh keyframe — so consistency is a number the pipeline acts on, not just reports.
  • A self-correcting QA gate that never publishes unwatched. The vision-grounded retake loop is committed and proven on real Alibaba Cloud generation (snapshot.json): a real 3-attempt run ("Sheep Drops Hay on Pig") regenerated twice on real, vision-grounded qa_notes, held the character on-model across all three takes (consistency 0.8), metered $0.84 blended, and — never satisfying QA — published the best take as a draft rather than going live. Two real episodes on the same infra did clear QA (approved, consistency 0.8–0.9). Unsupervised daily publishing is only safe because a step actually watches the footage — and when vision can't watch the clip at all, QA rejects rather than approving blind. (A mock-mode "Wobbly Gold Spheres…" rejected→rejected→approved episode is also seeded to illustrate the approved-after-retries arc shape.)
  • A per-channel data flywheel (the compounding advantage). Cast and episodes carry a channel_id, and _load_context scopes each channel's recent events and its top-voted "audience favorites" to that channel — so one creator's audience biases only that creator's writing, and continuity compounds per tenant (not one global pool). Voting is idempotent per (episode, voter) via a votes table with a unique constraint, so the signal that steers generation can't be inflated by re-posting (dedup, not full Sybil-resistance — a rotating-identity attacker would still need real auth/rate-limiting to defeat) — an advantage a one-off clip tool can't accumulate.

ICP. First wedge: solo creators and small brand/SMB social teams who need a recurring on-model character series (mascot channels, kids' shorts, brand serials) but can't staff a daily production crew.

Unit economics. Cost per episode stays low by design: the keyframe doubles as the thumbnail (no separate thumbnail render), and MAX_REGEN hard-caps retakes so a bad generation can't burn budget in a loop (§12). Each episode surfaces a blended per-episode cost meter — text tokens plus the image and video calls (usage.add_image() / usage.add_video()), so the receipt reflects the true marginal cost (a real approved multi-shot episode metered at $0.86), not just the cheap text half.


13. Roadmap / productization

The farm proves the engine; the business is serialized daily video as a service.

  • Per-creator channels. Point the pipeline at any cast + style to run a distinct daily show per creator/brand.
  • Monetization. Channel subscriptions + microtransactions to prioritize your submitted idea for tomorrow's episode (audience co-creation is already wired via the creator credit).
  • Richer casts & continuity. Deeper multi-episode story arcs and larger character rosters.
  • Multi-shot episodes — ✅ shipped. Beyond the ~5s single-gag format: an episode can now be N chained shots (setup→escalation→punchline) stitched into one continuous video (SHOTS_PER_EPISODE; a real 3-shot Pepe episode stitched to 15.5s on OSS). Next: even longer multi-beat story arcs.
  • Identity-lock consistency check — ✅ shipped. A qwen3-vl-plus pass scores each keyframe's character against its canonical portrait (IDENTITY_CHECK), turning character consistency into a measurable per-take number.
  • Scheduling & auto-publish — ✅ shipped and demonstrated. An unattended daily-channel loop (backend/scheduler.py, SCHEDULER_ENABLED) runs the showrunner on an interval and publishes on the QA verdict — in-process on app startup, or standalone (python -m scheduler [--once], cron-friendly). Proof: a real unattended run produced 3 episodes with no per-episode human trigger — 2 auto-published (QA approved, ~$0.30 each) and 1 held back as a draft (QA rejected after a real retake) — evidence (timestamps, receipts, verdicts) in docs/deploy_proof/scheduler_run.json. The vision-grounded QA loop is what makes unsupervised publishing safe. Next: even longer multi-beat story arcs.
  • Managed RDS + CDN. Production Postgres on Alibaba Cloud RDS and OSS-backed CDN delivery for the feed.

14. License

MIT — see LICENSE.

Built for the Qwen Cloud Global AI Hackathon 2026 — Track 2: AI Showrunner.

About

MUCKFLIX — an autonomous AI Showrunner: 4 Qwen-powered agents write, film, self-QA and publish a character-consistent claymation episode end-to-end. Qwen Cloud Hackathon 2026 · Track 2. Deployed on Alibaba Cloud ECS + OSS.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors