Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ The orchestrator is a **transparent reverse proxy**: every endpoint you expose i

- **HTTP** request/response — the common case. (`hello-world`, `tiles`)
- **HTTP + SSE** — streamed / token responses. (`vllm`)
- **Trickle** — continuous realtime video in/out. (`echo`)
- **Trickle** — continuous realtime video in/out. (`echo`, `flux-klein`)
- **WebSocket** — long-lived bidirectional sessions. (external: `scope`)

Need a schema that isn't here? [Open an issue](https://github.com/livepeer/live-runner-example-apps/issues).
Expand All @@ -40,12 +40,13 @@ Need a schema that isn't here? [Open an issue](https://github.com/livepeer/live-

## Examples

| Example | Goal | Registration | Mode | Transport |
| ------------------------------ | ----------------------------------------------- | ------------ | ---------------------------------- | ----------------- |
| [`hello-world`](./hello-world) | The simplest app: one request, one response | dynamic | persistent (single-shot by nature) | HTTP (JSON) |
| [`tiles`](./tiles) | Capacity fan-out — one session per tile | dynamic | persistent (single-shot by nature) | HTTP (base64 PNG) |
| [`echo`](./echo) | Realtime video, transformed and echoed back | dynamic | persistent | trickle |
| [`vllm`](./vllm) | Drop-in OpenAI API; the client stays unmodified | static | persistent (single-shot by nature) | HTTP + SSE |
| Example | Goal | Registration | Mode | Transport |
| ------------------------------ | -------------------------------------------------- | ------------ | ---------------------------------- | ----------------- |
| [`hello-world`](./hello-world) | The simplest app: one request, one response | dynamic | persistent (single-shot by nature) | HTTP (JSON) |
| [`tiles`](./tiles) | Capacity fan-out — one session per tile | dynamic | persistent (single-shot by nature) | HTTP (base64 PNG) |
| [`echo`](./echo) | Realtime video, transformed and echoed back | dynamic | persistent | trickle |
| [`vllm`](./vllm) | Drop-in OpenAI API; the client stays unmodified | static | persistent (single-shot by nature) | HTTP + SSE |
| [`flux-klein`](./flux-klein) | Realtime video diffusion, steered by a live prompt | static | persistent | trickle |

Start with `hello-world` (the smallest end-to-end path); the others each layer on one new idea. More will follow, including a full example that exercises every feature. Each is self-contained and runs **offchain** (free, no wallet); most also run **on-chain** (paid) — see each README.

Expand All @@ -54,7 +55,7 @@ Start with `hello-world` (the smallest end-to-end path); the others each layer o
How the app attaches to the orchestrator:

- **Dynamic** — the app self-registers via the SDK (`register_runner`) and heartbeats; the orchestrator drops it when heartbeats stop. Best for apps that come and go. (`hello-world`, `echo`)
- **Static** — the orchestrator is configured with the app's URL in a `runners.json` and health-polls it; the app needs no SDK. Best for fixed, long-running deployments. (`vllm`)
- **Static** — the orchestrator is configured with the app's URL in a `runners.json` and health-polls it; the app needs no SDK. Best for fixed, long-running deployments. (`vllm`, `flux-klein`)

The arrow flips — dynamic, the app announces itself; static, the orchestrator is told about a passive app:

Expand All @@ -74,7 +75,7 @@ flowchart LR

Chosen _at_ registration (above); **defaults to `persistent`** — set on both `register_runner(...)` and in `runners.json`. The examples set it explicitly.

- **Persistent** — a held-open session billed per second of wall-clock. Best for realtime / streaming. (`echo`)
- **Persistent** — a held-open session billed per second of wall-clock. Best for realtime / streaming. (`echo`, `flux-klein`)
- **Single-shot** — one request in, one response out. Best for batch / request-response. (`hello-world`, `vllm` are single-shot by nature.)

> [!IMPORTANT]
Expand Down
24 changes: 24 additions & 0 deletions flux-klein/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copy to .env (gitignored) and fill in. Never commit secrets.
# Keystore dirs: absolute paths OUTSIDE this repo, mounted read-only.
# Only needed for the on-chain (paid) path (docker-compose.onchain.yml).

NETWORK=arbitrum-one-mainnet
ETH_RPC_URL=https://arb1.arbitrum.io/rpc

# Signer (payer): needs an on-chain deposit + reserve.
SIGNER_KEYSTORE_DIR=/absolute/path/to/signer-keystore
SIGNER_ETH_ACCT=0xYourSignerAddress
SIGNER_ETH_PASSWORD=your-signer-keystore-password

# Orchestrator operating key (split-key): needs ETH for gas to redeem tickets.
ORCH_KEYSTORE_DIR=/absolute/path/to/operator-keystore
ORCH_ETH_ACCT=0xYourOperatorAddress
ORCH_ETH_PASSWORD=your-operator-keystore-password
# Registered orch = ticket recipient (-ethOrchAddr); empty = use the operating key.
ORCH_ONCHAIN_ADDR=0xYourRegisteredOrchestrator

# Pricing (on-chain), in USD, converted to wei via the price feed. Keep
# PIXELS_PER_UNIT small — too large and the per-unit price floors to 0 wei.
PRICE_PER_UNIT=1
PIXELS_PER_UNIT=1000
MAX_PRICE_PER_UNIT=0.10USD
5 changes: 5 additions & 0 deletions flux-klein/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Output written by client.py (MPEG-TS stream)
flux-klein-out.ts

# HF weights cache mounted into the container (~15 GB, downloaded on first boot)
models/
47 changes: 47 additions & 0 deletions flux-klein/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Self-contained realtime FLUX Klein app for the new Livepeer runner.
#
# Depends only on pip packages + our flux_klein.py/runner.py — NOT the Daydream
# Scope platform. FLUX.2-klein-4B needs a diffusers build that ships
# Flux2KleinPipeline (not yet in a stable release), so diffusers is installed
# from git. CUDA 12.8 / torch 2.7.1 to match the GPU wheels.
FROM nvidia/cuda:12.8.1-cudnn-devel-ubuntu22.04

ENV DEBIAN_FRONTEND=noninteractive PYTHONUNBUFFERED=1
ENV HF_HUB_ENABLE_HF_TRANSFER=1

# Python 3.12 (the SDK declares >=3.12; FLUX/diffusers run fine there) + git for
# the pip-from-git installs.
RUN apt-get update && apt-get install -y --no-install-recommends \
software-properties-common ca-certificates curl git \
&& add-apt-repository ppa:deadsnakes/ppa \
&& apt-get update && apt-get install -y --no-install-recommends \
python3.12 python3.12-venv python3.12-dev \
&& ln -sf /usr/bin/python3.12 /usr/local/bin/python \
&& curl -sS https://bootstrap.pypa.io/get-pip.py | python \
&& python -m pip --version \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

# Torch (CUDA 12.8 wheels) first so the diffusers install below doesn't pull a
# CPU build over the top.
RUN python -m pip install --no-cache-dir \
torch==2.7.1+cu128 torchvision==0.22.1+cu128 \
--index-url https://download.pytorch.org/whl/cu128

# diffusers from git: Flux2KleinPipeline (and the flux2 internals refine_frame
# reaches into) is not in a stable diffusers release yet. transformers/accelerate
# for the Qwen3 text encoder + device management.
RUN python -m pip install --no-cache-dir \
"diffusers @ git+https://github.com/huggingface/diffusers.git" \
transformers accelerate \
av aiohttp pillow numpy hf_transfer \
"livepeer-gateway @ git+https://github.com/livepeer/livepeer-python-gateway@rs/live-runner-session-payments"

WORKDIR /app
COPY flux_klein.py runner.py ./

# HF model weights are cached under /models/hf; mount a host dir so they persist
# across restarts and don't need to be re-downloaded (~15 GB for klein-4B).
ENV HUGGINGFACE_HUB_CACHE=/models/hf
EXPOSE 8720

ENTRYPOINT ["python", "runner.py"]
Loading