Skip to content

Live Runner + Scope support#20

Draft
j0sh wants to merge 35 commits into
mainfrom
ja/live-runner
Draft

Live Runner + Scope support#20
j0sh wants to merge 35 commits into
mainfrom
ja/live-runner

Conversation

@j0sh

@j0sh j0sh commented May 21, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

j0sh added 30 commits May 15, 2026 16:43
The new runner uses `address` as an opaque blob
Registers the app as a runner with an orchestrator.
None of these have anything to do with orchestrators.
Keep only small shims for backwards compatibility.
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

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

Run ID: 3e368b8a-2bc4-482f-a759-4819cc8ce88c

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ja/live-runner

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 and usage tips.


async def _maybe_await(value: object) -> None:
if inspect.isawaitable(value):
await value
Comment thread src/livepeer_gateway/live_runner.py Fixed

async def _maybe_await(value: None | Awaitable[None]) -> None:
if inspect.isawaitable(value):
await value
if wait_callback and (timeout is None or timeout > 0):
try:
await self.wait_callback(timeout=timeout)
except asyncio.TimeoutError:
Comment thread src/livepeer_gateway/live_runner.py Fixed
Comment thread src/livepeer_gateway/live_runner.py Fixed
if wait_callbacks and (timeout is None or timeout > 0):
try:
await self.wait_callbacks(timeout=timeout)
except asyncio.TimeoutError:
@j0sh j0sh force-pushed the ja/live-runner branch from d0d3453 to 2bc02a7 Compare May 27, 2026 16:29
task.cancel()
try:
await task
except asyncio.CancelledError:
def _release_session_id(self, session_id: str) -> None:
try:
self._active_session_ids.remove(session_id)
except ValueError:


class LiveRunnerSessionHeaders(Protocol):
def get(self, key: str, default: str = "") -> str: ...
runner_url: str,
app: str,
price_per_unit: int = 0,
pixels_per_unit: int = 1,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@j0sh can we use something like unit_scale since this runner also allows different pricing schemes. Also see my pull request in go-liveper livepeer/go-livepeer#3942.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I'll look into that idea (and the general move from away from pixels to purely timing) but don't want to block this merge for that

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@j0sh yea no blocker from my side I see this as a nice follow up improvement for consistency. I am tracking this in #27.

secret: str,
runner_url: str,
app: str,
price_per_unit: int = 0,

@rickstaa rickstaa Jun 29, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@j0sh How do you intend pricing to work for dynamic runners? Right now the app self-asserts price_info via register_runner(price_per_unit=…) and go-livepeer trusts it (only > 0 is checked in normalizeHeartbeat). That's fine for operator-deployed/trusted containers — the operator sets the price via env and the app forwards it — but an untrusted image could ignore that and under-report its price. Static sidesteps this (operator sets price_info in runners.json); only dynamic trusts the app.

You can see how I'm currently using this in the hello_world example, which feels a bit strange since it relies on the app to create the argument and forward it.

Two thoughts:

  • If the orchestrator is still meant to set the price in dynamic mode (to make gaming harder), the SDK could auto-read it from env (e.g. PRICE_PER_UNIT) instead of the app passing it to register_runner, keeping pricing a deployment/operator concern, out of app code.
  • I think we'll eventually move to GPU-based pricing (orchestrator states a price per GPU type, workloads auto-run at that rate), so go-livepeer would override the reported price at registration anyway.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

That's correct, the runner itself reports the price because the orchestrator is intended to control the runner.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This way runner provisioning (including pricing) can be configured separately from go-livepeer without having to introduce a mutual dependency on one other. Setting the price on go-livepeer itself introduces a tension with the orchestrator needing to be configured separately with details of the runner / workload, hardware, etc, as opposed to runners being able to simply connect and go.

If you want to keep the configuration / pricing within go-livepeer then static configuration is the way to go.

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