Skip to content

fix(live-runner): send inPixels:1 for fixed-price runner payments - #49

Closed
seanhanca wants to merge 1 commit into
feat/byoc-inference-capabilities-protobuffrom
fix/live-runner-fixed-price-inpixels
Closed

fix(live-runner): send inPixels:1 for fixed-price runner payments#49
seanhanca wants to merge 1 commit into
feat/byoc-inference-capabilities-protobuffrom
fix/live-runner-fixed-price-inpixels

Conversation

@seanhanca

Copy link
Copy Markdown
Contributor

Summary

Gateway half of the paired fix that makes native fixed-price single-shot live-runner generations bill correctly.

  • A fixed-price single-shot runner advertises price_info.unit == "fixed" and the v0.9.0 orchestrator debits exactly one unit per generation (server/ai_http.go reservePaidLiveRunnerSessionAccountPayment units:1, requiring ExpectedPrice.PixelsPerUnit == 1).
  • On the 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.
  • This PR threads the runner's advertised pricing unit through _get_runner_payment and, for fixed-unit runners, sends inPixels: 1 on the /generate-live-payment request so the fee equals the per-request fixed price (numTickets ~1).
  • Continuous runners (720p/hour, and any runner with no/unknown unit) send no inPixels, 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.pyLivePaymentSession gains an optional in_pixels; when set it is emitted as inPixels on the /generate-live-payment payload (before capabilities/state).
  • src/livepeer_gateway/live_runner.py — new _runner_price_unit() / _fixed_price_in_pixels() helpers read runner.raw["price_info"]["unit"] (from discovery/offering); _get_runner_payment passes in_pixels=1 only for unit == "fixed".
  • tests/test_live_runner_fixed_price.py — proves fixed-unit → payload carries inPixels == 1; 720p/hour/missing-price-info/runner=None → no inPixels override; 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.InPixels on the lv2v path (instead of unconditionally overriding it with the 720p estimate). This gateway PR makes the gateway actually send inPixels: 1 for 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 passed
  • uv run --extra dev pytest (full suite) — 28 passed
  • Touched modules import cleanly; no new linter errors
  • e2e: deploy this gateway together with go-livepeer #4006 and confirm the native fixed-price single-shot path mints a payment (numTickets ~1) instead of 400 numTickets ... exceeds maximum of 100

Made with Cursor

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>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: de7c0438-9272-45c3-a308-d5fa727113a6

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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

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_pixels support to LivePaymentSession and emit inPixels in the signer payload when provided.
  • Derive pricing unit from discovered runner price_info.unit and set in_pixels=1 only for unit == "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.

Comment on lines +224 to 225
self._in_pixels = in_pixels
self._max_refresh_retries = max(0, int(max_refresh_retries))
@seanhanca

Copy link
Copy Markdown
Contributor Author

Closing as superseded — this PR is built on the wrong lineage.

Its base is feat/byoc-inference-capabilities-protobuf (BYOC), not ja/live-runner. Investigation (LR-AUTHOR-INPUTS-INVESTIGATION.md, Points 1 & 2) confirms the native fixed-price path already exists on ja/live-runner + v0.9.0:

  • The ja/live-runner gateway maps unit=fixed → type=fixed via _RUNNER_PAYMENT_TYPES_BY_UNIT in live_runner.py, and _get_runner_payment sends type=fixed (fixed sessions even skip the metering loop).
  • With type=fixed, the v0.9.0 signer bills billableUnits=1 (numTickets ~1) and the v0.9.0 orch accounts one unit with PixelsPerUnit=1.

This PR hardcodes type="lv2v" and only bolts on inPixels:1, i.e. it makes the lv2v path imitate the native fixed path. On the correct lineage the inPixels change is unnecessary. The real gateway fix is a deployment: rebuild sdk-service from the latest ja/live-runner gateway (which has live_runner.py), replacing the stale BYOC-only sdk-service:optA-lr-multi-2026-07-23 on sdk.daydream.monster. This is a deployment/lineage fix, not code.

Do not reopen or rebase. Closing in favor of deploying the ja/live-runner gateway + a v0.9.0 signer and registering runners with unit=fixed. Branch left intact. See LR-AUTHOR-INPUTS-INVESTIGATION.md for full evidence.

@seanhanca seanhanca closed this Jul 29, 2026
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