Skip to content

Commit 3ac6bf7

Browse files
committed
Add pricing options to Livepeer runner
1 parent 0a042da commit 3ac6bf7

2 files changed

Lines changed: 67 additions & 1 deletion

File tree

src/scope/cloud/livepeer_scope_app.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@
4343
DEFAULT_RETRY_DELAY_SECONDS = 2.5
4444
DEFAULT_MAX_FAILURES_PER_WINDOW = 20
4545
DEFAULT_FAILURE_WINDOW_SECONDS = 60.0
46+
DEFAULT_PRICE_PER_UNIT = 0
47+
DEFAULT_PIXELS_PER_UNIT = 1
48+
DEFAULT_PRICE_UNIT = "USD"
4649
CHANNEL_MIME_JSONL = "application/jsonl"
4750

4851
_registration: LiveRunnerRegistration | None = None
@@ -65,6 +68,9 @@ class ScopeRunnerSettings:
6568
retry_delay_seconds: float = DEFAULT_RETRY_DELAY_SECONDS
6669
max_failures_per_window: int = DEFAULT_MAX_FAILURES_PER_WINDOW
6770
failure_window_seconds: float = DEFAULT_FAILURE_WINDOW_SECONDS
71+
price_per_unit: int = DEFAULT_PRICE_PER_UNIT
72+
pixels_per_unit: int = DEFAULT_PIXELS_PER_UNIT
73+
price_unit: str = DEFAULT_PRICE_UNIT
6874

6975
def resolved_runner_url(self) -> str:
7076
if self.runner_url:
@@ -89,11 +95,35 @@ class ScopeBridgeSession:
8995
settings = ScopeRunnerSettings()
9096

9197

98+
def _env_int(name: str, default: int) -> int:
99+
raw = os.getenv(name)
100+
if raw is None:
101+
return default
102+
try:
103+
return int(raw)
104+
except ValueError as exc:
105+
raise ValueError(f"{name} must be an integer") from exc
106+
107+
108+
def _validate_price_settings(price_per_unit: int, pixels_per_unit: int) -> None:
109+
if pixels_per_unit <= 0:
110+
raise ValueError("LIVEPEER_RUNNER_PIXELS_PER_UNIT must be positive")
111+
112+
92113
def _settings_from_env() -> ScopeRunnerSettings:
93114
port = int(os.getenv("LIVEPEER_RUNNER_PORT", str(DEFAULT_PORT)))
94115
inner_port = int(os.getenv("LIVEPEER_INNER_PORT", str(DEFAULT_INNER_PORT)))
95116
host = os.getenv("LIVEPEER_RUNNER_HOST", DEFAULT_HOST)
96117
runner_url = os.getenv("LIVEPEER_RUNNER_URL", "")
118+
price_per_unit = _env_int(
119+
"LIVEPEER_RUNNER_PRICE_PER_UNIT",
120+
DEFAULT_PRICE_PER_UNIT,
121+
)
122+
pixels_per_unit = _env_int(
123+
"LIVEPEER_RUNNER_PIXELS_PER_UNIT",
124+
DEFAULT_PIXELS_PER_UNIT,
125+
)
126+
_validate_price_settings(price_per_unit, pixels_per_unit)
97127
inner_ws_url = os.getenv(
98128
"LIVEPEER_INNER_WS_URL",
99129
f"ws://127.0.0.1:{inner_port}/ws",
@@ -110,6 +140,9 @@ def _settings_from_env() -> ScopeRunnerSettings:
110140
inner_port=inner_port
111141
if "LIVEPEER_INNER_PORT" in os.environ
112142
else parsed_inner_port,
143+
price_per_unit=price_per_unit,
144+
pixels_per_unit=pixels_per_unit,
145+
price_unit=os.getenv("LIVEPEER_RUNNER_PRICE_UNIT", DEFAULT_PRICE_UNIT),
113146
)
114147

115148

@@ -144,6 +177,9 @@ async def _register_runner(
144177
secret=runner_settings.orch_secret,
145178
runner_url=runner_settings.resolved_runner_url(),
146179
app=LIVEPEER_APP_NAME,
180+
price_per_unit=runner_settings.price_per_unit,
181+
pixels_per_unit=runner_settings.pixels_per_unit,
182+
price_unit=runner_settings.price_unit,
147183
mode="persistent",
148184
capacity=1,
149185
on_session_release=_on_session_release,
@@ -729,17 +765,44 @@ def _on_session_release(event: Any) -> None:
729765
show_default=True,
730766
help="Inner Scope runner websocket URL",
731767
)
768+
@click.option(
769+
"--price-per-unit",
770+
envvar="LIVEPEER_RUNNER_PRICE_PER_UNIT",
771+
default=DEFAULT_PRICE_PER_UNIT,
772+
show_default=True,
773+
type=int,
774+
help="Runner price per unit advertised to the orchestrator",
775+
)
776+
@click.option(
777+
"--pixels-per-unit",
778+
envvar="LIVEPEER_RUNNER_PIXELS_PER_UNIT",
779+
default=DEFAULT_PIXELS_PER_UNIT,
780+
show_default=True,
781+
type=int,
782+
help="Pixels per advertised price unit",
783+
)
784+
@click.option(
785+
"--price-unit",
786+
envvar="LIVEPEER_RUNNER_PRICE_UNIT",
787+
default=DEFAULT_PRICE_UNIT,
788+
show_default=True,
789+
help="Currency unit for advertised runner price",
790+
)
732791
def main(
733792
host: str,
734793
port: int,
735794
orchestrator: str,
736795
orch_secret: str,
737796
runner_url: str,
738797
inner_ws_url: str,
798+
price_per_unit: int,
799+
pixels_per_unit: int,
800+
price_unit: str,
739801
) -> None:
740802
"""Run the Livepeer Scope live-runner app."""
741803
global settings
742804
logging.basicConfig(level=logging.INFO)
805+
_validate_price_settings(price_per_unit, pixels_per_unit)
743806
inner_host, inner_port = _inner_bind_from_ws_url(inner_ws_url)
744807
settings = ScopeRunnerSettings(
745808
host=host,
@@ -750,6 +813,9 @@ def main(
750813
inner_ws_url=inner_ws_url,
751814
inner_host=inner_host,
752815
inner_port=inner_port,
816+
price_per_unit=price_per_unit,
817+
pixels_per_unit=pixels_per_unit,
818+
price_unit=price_unit,
753819
)
754820
uvicorn.run(
755821
"scope.cloud.livepeer_scope_app:app",

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)