fix(live-runner): send inPixels:1 for fixed-price runner payments - #49
Conversation
A fixed-price single-shot live-runner generation advertises price_info.unit == "fixed" and the v0.9.0 orchestrator debits exactly one unit per generation (PixelsPerUnit == 1). The remote signer types these payments as "lv2v" and, absent an explicit unit count, recomputes pixels as a continuous 720p30 estimate (720*1280*30*60 = 1,658,880,000), inflating the per-request fixed fee ~1.66e9x and blowing past the max-100 ticket guard (observed e2e: HTTP 400 "numTickets 2721947758 exceeds maximum of 100"). Thread the runner's advertised pricing unit through _get_runner_payment and, for fixed-unit runners, send inPixels:1 to /generate-live-payment so the fee equals the per-request fixed price (numTickets ~1). Continuous runners (720p/hour) send no inPixels, so their payload is byte-identical and the signer keeps its automatic estimate. Gateway half of the paired fix. Requires go-livepeer PR #4006 (commit cd99507), which teaches the signer to honor req.InPixels on the lv2v path; both must ship together. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR updates the gateway’s live-runner payment flow so fixed-price single-shot runners correctly bill exactly one unit by sending inPixels: 1 to the remote signer on the lv2v /generate-live-payment request, avoiding the signer’s large fallback pixel estimate.
Changes:
- Add optional
in_pixelssupport toLivePaymentSessionand emitinPixelsin the signer payload when provided. - Derive pricing unit from discovered runner
price_info.unitand setin_pixels=1only forunit == "fixed"(case-insensitive). - Add unit tests covering fixed vs continuous units and missing pricing metadata.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/livepeer_gateway/remote_signer.py |
Thread optional in_pixels into the signer request payload as inPixels. |
src/livepeer_gateway/live_runner.py |
Determine runner pricing unit and pass in_pixels=1 only for fixed-unit runners. |
tests/test_live_runner_fixed_price.py |
Verify payload includes/omits inPixels based on runner unit and missing metadata. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| self._in_pixels = in_pixels | ||
| self._max_refresh_retries = max(0, int(max_refresh_retries)) |
|
Closing as superseded — this PR is built on the wrong lineage. Its base is
This PR hardcodes Do not reopen or rebase. Closing in favor of deploying the |
Summary
Gateway half of the paired fix that makes native fixed-price single-shot live-runner generations bill correctly.
price_info.unit == "fixed"and the v0.9.0 orchestrator debits exactly one unit per generation (server/ai_http.go reservePaidLiveRunnerSession→AccountPayment units:1, requiringExpectedPrice.PixelsPerUnit == 1).type:"lv2v"path the remote signer, when given no explicit unit count, recomputes pixels as a continuous 720p30 estimate (720*1280*30*60 = 1,658,880,000). Multiplying the per-request fixed price by that volume inflates the fee ~1.66e9x, so payment-mint needs billions of tickets and fails the signer's max-100 guard — observed e2e:HTTP 400 numTickets 2721947758 exceeds maximum of 100._get_runner_paymentand, for fixed-unit runners, sendsinPixels: 1on the/generate-live-paymentrequest so the fee equals the per-request fixed price (numTickets ~1).720p/hour, and any runner with no/unknown unit) send noinPixels, so their payload is byte-identical and the signer keeps its automatic estimate. No behavior change for the daydream live-video / continuous plane.Changes
src/livepeer_gateway/remote_signer.py—LivePaymentSessiongains an optionalin_pixels; when set it is emitted asinPixelson the/generate-live-paymentpayload (beforecapabilities/state).src/livepeer_gateway/live_runner.py— new_runner_price_unit()/_fixed_price_in_pixels()helpers readrunner.raw["price_info"]["unit"](from discovery/offering);_get_runner_paymentpassesin_pixels=1only forunit == "fixed".tests/test_live_runner_fixed_price.py— proves fixed-unit → payload carriesinPixels == 1;720p/hour/missing-price-info/runner=None→ noinPixelsoverride; case-insensitive unit match; helper unit tests.Pairs with go-livepeer PR #4006 (commit
cd99507)This is inert on its own. go-livepeer PR #4006 teaches the signer to honor
req.InPixelson thelv2vpath (instead of unconditionally overriding it with the 720p estimate). This gateway PR makes the gateway actually sendinPixels: 1for fixed-unit runners. Both must be deployed together for the native fixed-price e2e to go green.Based on
feat/byoc-inference-capabilities-protobuf(@426f019), the branch the deployed SDK image pins.Test plan
uv run --extra dev pytest tests/test_live_runner_fixed_price.py— 8 passeduv run --extra dev pytest(full suite) — 28 passed400 numTickets ... exceeds maximum of 100Made with Cursor