livepeer / livepeer/livepeer-python-gateway
Encapsulate live-runner session payments in a stateful session/client object
- Dominant language
- Python
- Stars
- 1
- Forks
- 7
- PR merge metrics
- No merged PRs in 30d
Description
## Problem
Streamed live-runner sessions (trickle / websocket) must keep paying for as long as the session is open, because the orchestrator meters the open session by wall-clock/segments and drops it when the balance runs dry. Today the SDK exposes this as a free function, `run_session_payments(session, interval=...)`, which the caller must drive manually:
```python
payment_task = asyncio.create_task(run_session_payments(session, interval=...))
...
finally:
payment_task.cancel()
await payment_task
```
`reserve_session` already mints and attaches the `payment_session` credential to the returned `LiveRunnerSession`, but `LiveRunnerSession` is a frozen dataclass with no lifecycle, so it cannot own the background task. That pushes task creation and (critically) cancellation onto every consumer. Forget the `finally` and you silently stop paying or leak a task. The echo example (`example-apps/echo/client.py`) shows the boilerplate this requires.
## Prior art (already in our codebase)
Two better-encapsulated patterns already exist and prove the approach:
- `lv2v.py`: `Lv2vJob.start_payment_sender()` runs a per-output-segment payment sender, auto-started by `start_lv2v(start_payments=True)` and cancelled by `job.aclose()`. The consumer cannot forget it.
- daydreamlive/scope `ja/runner` (`server/livepeer_client.py`): `LivepeerClient` starts an interval `_payment_loop` right after connect, gated on `payment_session is not None`, and cancels it on teardown. This is effectively `run_session_payments` hand-rolled inside a client object.
## Proposal
Make `reserve_session` return a stateful session/client that owns the payment lifecycle, so payments are automatic for on-chain sessions:
```python
async with await reserve_session(app=..., signer_url=signer) as session:
# publish/consume trickle; payments start and stop automatically
```
- Auto-start the payment loop when `payment_session is not None` (on-chain only; no-op offchain).
- Auto-cancel on context exit / `aclose()`, alongside `stop_runner_session`.
- Keep `run_session_payments` as the internal primitive.
- This mirrors `start_lv2v(start_payments=True)` + `job.aclose()`.
Payments still need a scope anchor (the session must know when the stream ended); the context manager / `aclose()` provides it. The object cannot infer that media stopped on its own.
## Open questions
1. One unified `LiveRunnerSession` object that subsumes `Lv2vJob`, or keep lv2v separate and only wrap the general live-runner path? The former removes the parallel implementations; the latter is the minimal change.
2. Cadence semantics to standardize on: lv2v pays per output segment (only while media flows); `run_session_payments` pays on a fixed interval (pays even when paused-but-open). These differ in over-payment behavior and should be decided before standardizing the interface.
## Context
Follow-up from the echo example migration and the `run_session_payments` work (branch `rs/live-runner-session-payments`). Related: gateway PR #25 (SSE streaming).
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.