Skip to content

add flux-klein trickle example with USD per hour pricing - #35

Open
rickstaa wants to merge 2 commits into
mainfrom
rs/add-flux-klein
Open

add flux-klein trickle example with USD per hour pricing#35
rickstaa wants to merge 2 commits into
mainfrom
rs/add-flux-klein

Conversation

@rickstaa

Copy link
Copy Markdown
Member

What

Carries #32 (flux-klein trickle example by @zargarzadehm, commit preserved) forward with the decimal USD per hour pricing scheme and the repo's current conventions, so it lands consistent with #34.

On top of the original example:

  • runners.json: price_info is now { "price": 0.01 } (decimal USD per hour) instead of price_per_unit / pixels_per_unit.
  • .env.example: drops the retired PRICE_PER_UNIT / PIXELS_PER_UNIT vars; the price lives in runners.json (static runner), MAX_PRICE_PER_UNIT stays as the signer cap.
  • compose.onchain.yml: adds the -maxFaceValue=4e15 floor from the shared file (a faceValue below the redemption tx cost makes the orchestrator reject every session with "insufficient sender reserve"; see refactor(examples): move to USD per hour pricing #34).
  • docker-compose*.yml renamed to compose*.yml to match refactor(examples): move to USD per hour pricing #34's convention, with all README and header references updated.

SDK note

The example keeps its rs/live-runner-session-payments pin: a long-lived streaming session needs session.start_payments(), which ja/live-runner does not have. That branch now carries the pricing refactor (merged from ja/live-runner at 89ac0f7, all SDK tests passing), so the pin and the new price format work together. Client imports verified against the merged branch.

Not yet done

Runtime not re-tested here (needs the CUDA image build and a webcam/video input); the original functionality is untouched apart from the pricing/naming alignment.

Supersedes #32.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VtVeDdep8i9UrNKbm9yr8G

zargarzadehm and others added 2 commits July 19, 2026 23:04
runners.json advertises the new decimal price format, the env example drops
the retired PRICE_PER_UNIT and PIXELS_PER_UNIT vars, the onchain overlay
gains the maxFaceValue floor from the shared file, and the compose files
move to the compose.yml naming. The SDK pin stays on
rs/live-runner-session-payments, which now carries the pricing refactor
(merged from ja/live-runner).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VtVeDdep8i9UrNKbm9yr8G
Copilot AI review requested due to automatic review settings July 26, 2026 13:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new flux-klein example app demonstrating realtime trickle video-to-video using FLUX.2-klein-4B, aligned with the repo’s static-runner pattern and USD/hour pricing format.

Changes:

  • Introduces the flux-klein example (runner, client, model wrapper, Docker/Compose, static runners.json, and usage docs/scripts).
  • Adds off-chain and on-chain compose configurations for running the orchestrator + app (and signer overlay on-chain).
  • Updates the repo root README to include flux-klein in the supported schema list and examples table.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
README.md Lists flux-klein among trickle/static examples and updates the examples table.
flux-klein/view.sh Adds a turnkey “webcam → runner → player” helper script.
flux-klein/runners.json Adds static-runner config with price_info.price (USD/hour).
flux-klein/runner.py Implements the aiohttp runner with warm-up gating, trickle channels, stats, and live /update.
flux-klein/README.md Documents wiring, operation, parameters, and usage for off-chain/on-chain runs.
flux-klein/pyproject.toml Defines host-side deps and pins livepeer-gateway to the required branch.
flux-klein/flux_klein.py Provides the self-contained FLUX Klein feedback-loop inference implementation.
flux-klein/Dockerfile Builds a CUDA image with torch + diffusers-from-git and the pinned gateway SDK.
flux-klein/compose.yml Off-chain compose setup extending the shared orchestrator config with -liveRunnerConfig.
flux-klein/compose.onchain.yml On-chain overlay adding signer + on-chain orchestrator flags and -maxFaceValue.
flux-klein/client.py Adds a client that reserves sessions, streams media, and live-updates prompt/seed/blend.
flux-klein/.gitignore Ignores local output TS and cached model weights directory.
flux-klein/.env.example Adds on-chain env placeholders (keystores/accounts) and signer price cap.
Comments suppressed due to low confidence (5)

flux-klein/runner.py:80

  • Introduce a process-wide model lock so inference running in a worker thread (via asyncio.to_thread) can’t race with /update or session teardown mutating MODEL state (prompt cache, noise cache, reset).
MODEL = FluxKleinModel()                # single, container-wide pipeline (loaded once at warm-up)
_ORCHESTRATOR_URL = "http://localhost:8935"

flux-klein/runner.py:152

  • _close_session() sets the global session to None immediately and then awaits cleanup. A new /stream request can start another session while the old one is still shutting down, and MODEL.reset() can race with an in-flight asyncio.to_thread(_transform, ...) call (task cancellation won’t stop the underlying thread). Holding _session_lock through cleanup and using _model_lock around MODEL.reset() avoids these races.
async def _close_session() -> None:
    global session
    if session is None:
        return
    current, session = session, None

flux-klein/runner.py:205

  • /stream parses the request body with json.loads(...) but doesn’t handle malformed JSON. That will currently raise and return a 500 instead of a 400. Also, since inference runs in a background thread, updates to MODEL parameters should be synchronized with _model_lock to avoid racing with in-flight inference.
    body = json.loads(await request.read() or "{}")
    prompt = str(body.get("prompt", DEFAULT_PROMPT))
    MODEL.update_prompt(prompt)  # model/res fixed per container; this is one assignment
    if "seed" in body:
        MODEL.update_seed(int(body["seed"]))  # -1 random; fixed = steadier output

flux-klein/runner.py:400

  • /update has the same malformed-JSON issue as /stream (500 instead of 400). Additionally, it can run concurrently with the inference thread, so model updates and reads of MODEL.seed/MODEL.input_blend should be done under _model_lock to avoid races and inconsistent responses.
    body = json.loads(await request.read() or "{}")
    prompt = str(body.get("prompt", session.prompt))
    MODEL.update_prompt(prompt)
    session.prompt = prompt
    if "seed" in body:

flux-klein/runner.py:253

  • Inference is executed in a worker thread, but MODEL.process_batch(...) is called without synchronization. Since /update and session teardown can mutate/reset MODEL concurrently, protect the inference call with _model_lock to avoid races (task cancellation won’t stop the underlying thread).
        rgbs = [f.to_ndarray(format="rgb24") for f in frames]
        outs = []
        for src, out_rgb in zip(frames, MODEL.process_batch(rgbs)):
            out = av.VideoFrame.from_ndarray(out_rgb, format="rgb24")

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread flux-klein/README.md
docker compose -f compose.yml -f compose.onchain.yml down
```

Keep `PIXELS_PER_UNIT` small — if it's too large the per-unit price floors to 0 wei and calls are effectively free despite being on-chain.
Comment thread flux-klein/runner.py
import json
import logging
import os
import time
@rickstaa

Copy link
Copy Markdown
Member Author

Tested on-chain on Arbitrum mainnet (RTX 3090): session reserved and paid through the remote signer (tickets signed, orchestrator credited balance=2e12, clean release + payment processor shutdown), FLUX.2-klein-4B generated frames back through the trickle output. Full logs in the session referenced below.

Two notes:

  1. Merge order: this PR depends on refactor(examples): move to USD per hour pricing #34. The old shared compose.onchain.yml on main passes -pixelsPerUnit=${PIXELS_PER_UNIT} to the signer, which crashes on the now-removed env var. With refactor(examples): move to USD per hour pricing #34's shared file the stack comes up clean.
  2. File inputs publish faster than real time, so the runner hits input EOF and closes the session after a few seconds. The initial payment cycle is verified; the long-running top-up path (next payment due ~10 min at this price/ticketEV ratio) needs a live-paced input (webcam) to observe.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VtVeDdep8i9UrNKbm9yr8G

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants