Skip to content

feat(echo): run on-chain with metered payments - #50

Merged
rickstaa merged 12 commits into
mainfrom
rs/echo-onchain
Aug 6, 2026
Merged

feat(echo): run on-chain with metered payments#50
rickstaa merged 12 commits into
mainfrom
rs/echo-onchain

Conversation

@rickstaa

@rickstaa rickstaa commented Jul 30, 2026

Copy link
Copy Markdown
Member

What

echo was the only example with no on-chain path, and the lone blank in the repo README's pricing column. A live stream is what metered pricing exists for: there is no call boundary to bill against, so the session is billed per second for as long as the client holds it.

  • compose.onchain.yml and .env.example, mirroring tiles, with PRICE as USD per hour.
  • runner.py takes --price and registers it; the base compose still registers free.
  • client.py takes --signer, and holds the session in async with so funding stops before the session is released.
  • README gets a Run on-chain (paid) section explaining why a metered session pays more than once, and the pricing rows are filled in.
  • echo's README also picks up the two sections its siblings have and it lacked: a what this shows section for the held-open session (its one idea, previously visible only inside the on-chain section) and a Run without Docker recipe. Its heading structure now matches hello-world and tiles.

Shared stack changes

Two things in the shared demo stack had to change before the documented commands worked. Both affect every on-chain example, neither changes what the fixed-price ones do.

  • Image. The metered path needs the session-scoped payment URL that Add a session-scoped payment URL to the runner challenge. go-livepeer#4008 adds to the runner payment challenge. It merged after v0.9.0, so on the release tag the challenge carries no payment_url and the SDK fails at reservation with Live runner payment challenge missing payment_url. compose.orchestrator.yml and compose.onchain.yml now pin the master build sha-cc49228. Re-pin to a release tag once one ships with #4008.
  • -ticketEV. The signer signs at most 100 tickets per payment, and a payment needs fee / ticketEV of them. A metered session pays for a 10s window upfront, so at 1e9 the demo orchestrator topped out near 0.067 USD per hour and the advertised PRICE=0.10 asked for 150 tickets: numTickets 150 exceeds maximum of 100. Raised to 1e10, which puts the ceiling near 0.67 USD per hour.

api-proxy and vllm restate the whole orchestrator command to add -liveRunnerConfig, since extends cannot append a flag, so each carried its own -ticketEV and would have missed the bump entirely. vllm is metered, so it would have hit the same ticket cap as soon as its price rose above about 0.067 USD per hour. Both are bumped to match, and the ticket math quoted in the fixed-price examples' .env.example files is corrected (the ceiling is now ~0.0019 USD per call, not ~0.00019).

Why this example matters on-chain

hello-world, tiles and api-proxy are all fixed price: the upfront 402 payment settles the whole bill and the orchestrator starts no ticker. echo is the first example where payment is a lifecycle rather than a transaction, so it is the one that actually exercises the metered path end to end.

Tested

Run end to end on Arbitrum One with a funded signer, using the compose files in this branch and the command from the README, against livepeer/livepeer-python-gateway ja/live-runner @ 995f93f (includes livepeer/livepeer-python-gateway#53):

uv run client.py sample.mp4 --mode blur \
  --discovery https://localhost:8935/discovery \
  --signer http://localhost:7936

A 30s 720p30 clip went in and 900 frames came back out with the blur applied. The session paid nine times, not once: 15 tickets upfront for the signer's 10s preroll, then eight top-ups of 6 tickets each while the orchestrator debited every 5s. On client exit the orchestrator logged live runner session released ... duration=26.69s followed by Done processing payments for session, so funding stopped before release, which is what the client's async with session is for.

Because both shared-stack changes touch every on-chain example, tiles was run on-chain too, on the same pinned stack: 9 paid tile calls, one signed payment each at 6 tickets, no no tickets and no ticket-cap errors. The fixed-price path is unaffected.

Also lint/format via pre-commit.

rickstaa and others added 5 commits July 30, 2026 21:05
echo was the one example without an on-chain path, and the pricing
column in the repo README was blank for it. A live stream is exactly
what metered pricing exists for: the session is billed per second for
as long as the client holds it, with no call boundary to bill against.

Registering a price makes the orchestrator meter the session, so the
client keeps it funded for its lifetime and lets go of the funding
before releasing it. The upfront 402 payment only covers the signer's
preroll, so a stream that runs longer is the case this example puts
on-chain.

Needs livepeer/livepeer-python-gateway#53: before it, a reserved
session could not pay again and died at the preroll.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The commands pointed at ~/samples/bbb_720p.mp4, which only exists on
one machine, so the first thing a reader runs fails. Generate the clip
with ffmpeg instead: no download to rot, and -t sets the length, which
matters on-chain where the stream has to outlive the preroll.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The on-chain section showed only the file command, so running the
ffmpeg or webcam pipe against a paid stack fails at the payment
challenge and ffmpeg reports a broken pipe, which reads like a camera
problem rather than an unpaid caller.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Metered sessions need the session-scoped payment URL that go-livepeer #4008
adds to the runner payment challenge. It landed after v0.9.0, so on the
release tag the SDK cannot keep a session funded and echo stops at
reservation. Pin the orchestrator and signer to the master build that
carries it, and re-pin once a release ships with the change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The signer signs at most 100 tickets per payment, and a payment needs
fee / ticketEV of them. A metered session pays for a 10s window upfront, so
at -ticketEV=1e9 the demo orchestrator topped out around 0.067 USD per hour
and echo's advertised 0.10 asked for 150 tickets, which the signer refused.
Raise it to 1e10: echo now signs 15 tickets upfront and 6 per top-up, and
the ceiling moves to about 0.67 USD per hour. Correct the ticket math in
echo's .env.example, which claimed the old value had room to spare.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@rickstaa
rickstaa marked this pull request as ready for review August 4, 2026 10:25
Copilot AI review requested due to automatic review settings August 4, 2026 10:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 fully on-chain (paid) path for the echo example using metered (per-second) pricing, and updates the shared demo stack configuration to support metered sessions end-to-end.

Changes:

  • Add on-chain compose overlay + env template for echo, and document how metered sessions charge multiple times over the session lifetime.
  • Extend echo runner registration to accept a configurable USD/hour price; extend the echo client to optionally pay via a remote signer and hold funding while the session is in-use.
  • Update shared orchestrator/signer compose configuration (image pin + -ticketEV) to support metered session payments.

Reviewed changes

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

Show a summary per file
File Description
README.md Updates examples table to reflect echo pricing as hourly/metered.
echo/runner.py Adds --price and registers the runner with a USD/hour price for metered billing.
echo/client.py Adds --signer support and keeps the session funded while in an async with block.
echo/README.md Documents on-chain paid run flow and explains why metered sessions pay multiple times.
echo/compose.yml Updates usage comment to match current CLI (--mode).
echo/compose.onchain.yml New overlay to run echo on-chain and register a non-zero price.
echo/.env.example New env template for on-chain setup + ticket/price cap guidance.
compose.orchestrator.yml Pins orchestrator image to master SHA to pick up metered-session payment URL support.
compose.onchain.yml Pins signer image to same SHA and raises -ticketEV to avoid exceeding signer ticket limits.

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

Comment thread compose.orchestrator.yml
Comment thread compose.onchain.yml
api-proxy and vllm restate the whole orchestrator command to add
-liveRunnerConfig, since `extends` cannot append a flag, so each kept its own
-ticketEV and never saw the shared bump. vllm is metered, so it would have hit
the signer's 100 ticket cap the moment its price rose above about 0.067 USD
per hour. Bump both, and correct the ticket math the fixed price examples
quote in their .env.example. Also say in the README that the demos now run a
master build rather than the v0.9.0 release image.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 4, 2026 10:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

hello-world and tiles both explain their one idea in a "what this shows"
section and close with a Run without Docker recipe; echo had neither, so its
distinguishing idea (a session the client holds open, which is what makes
billing a lifecycle) only appeared inside the on-chain section. Add both, in
the same order the other examples use.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

rickstaa and others added 2 commits August 6, 2026 10:37
Same ceiling and the same cap rule, said in half the lines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The on-chain command passes both --discovery and --signer, which reads as
if the signer takes part in discovery. It does not: the SDK only falls
back to signer discovery when no discovery URL is given, and the demo
signer runs without -remoteDiscovery anyway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 6, 2026 08:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

Suppressed comments (2)

echo/client.py:205

  • To support the documented --discovery '' usage (blank meaning “omit discovery so the SDK can fall back to the signer”), normalize args.discovery before calling reserve_session. Right now an empty string is passed through as discovery_url, which is likely treated as an invalid URL rather than “unset.”
        session = await reserve_session(  # Livepeer: 1
            discovery_url=args.discovery,  # omit if the signer does discovery itself
            app=APP_ID,
            signer_url=args.signer.strip() or None,
        )

echo/README.md:105

  • This sentence suggests v0.9.0 is sufficient for "Run without Docker", but a few lines later you note the paid/metered path requires a build after v0.9.0 (#4008). Reword here to avoid implying that v0.9.0 supports the on-chain metered flow.
Start an orchestrator built from go-livepeer `v0.9.0` or newer (see [Build from source](https://docs.livepeer.org/v1/orchestrators/guides/install-go-livepeer#build-from-source)), then the app and client directly:

Keep the rule and one worked number. Why 1e9 was too low belongs in the
commit that raised it, not in the file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 6, 2026 09:17
The prose said "make one if you have nothing to hand" while the block
made one unconditionally. Put the condition on the line it applies to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

Suppressed comments (1)

echo/client.py:202

  • If the user follows the README instruction to disable orchestrator discovery with --discovery '', this currently passes an empty string through to reserve_session(...). That isn’t the same as “omitting” the discovery URL and may be treated as an invalid URL by the SDK. Coerce blank/whitespace-only values to None (as you already do for --signer) so the paid-path fallback works reliably.
        session = await reserve_session(  # Livepeer: 1
            discovery_url=args.discovery,  # omit if the signer does discovery itself
            app=APP_ID,
            signer_url=args.signer.strip() or None,
        )

Copilot AI review requested due to automatic review settings August 6, 2026 09:21
The signer discovery note is true of every example, so it belongs in the
repo README, not here. The closing contrast restated what this section
and tiles already say about metered vs fixed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

Copilot AI review requested due to automatic review settings August 6, 2026 09:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.

@rickstaa
rickstaa merged commit bf7dfe7 into main Aug 6, 2026
5 checks passed
@rickstaa
rickstaa deleted the rs/echo-onchain branch August 6, 2026 09:34
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.

2 participants