feat(live-runner): session-owned payment lifecycle for streamed sessions#31
Draft
rickstaa wants to merge 3 commits into
Draft
feat(live-runner): session-owned payment lifecycle for streamed sessions#31rickstaa wants to merge 3 commits into
rickstaa wants to merge 3 commits into
Conversation
reserve_session now returns the LivePaymentSession built during the reserve payment challenge (previously discarded). Add run_session_payments(session), a timer-driven loop that calls LivePaymentSession.send_payment on an interval to keep a long-lived session funded — the orchestrator meters open sessions by wall-clock time and releases them when the balance runs dry. No-op offchain. Reusable across any held-open transport (trickle today, websockets next). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a session-owned payment lifecycle to LiveRunnerSession so streamed (trickle/websocket) sessions stay funded without the caller hand-managing a background task. Mirrors the lv2v Lv2vJob.start_payment_sender/close shape, but uses the transport-agnostic interval driver (run_session_payments) since the general live-runner path has no single output stream to meter and must also cover websocket. - LiveRunnerSession (still frozen) gains start_payments(), aclose(), and async context manager support; _payment_task stored via object.__setattr__. start_payments is idempotent, a no-op offchain, and warns instead of raising when called without a running loop. - run_session_payments now pays immediately before the first sleep (a cold start can leave a long gap after the reservation payment) and logs+retries per-cycle failures instead of dying. - reserve_session threads payment_interval through to the session. Callers can now do `async with await reserve_session(...) as session:` and get automatic start/stop, or use start_payments()/aclose() manually. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
…o server tick Refinements from reviewing the go-livepeer orchestrator design (ai_http.go ReserveLiveRunnerSession): after the reservation 402, the orchestrator holds the session as a prepaid balance debited by a server-side ticker (-livePaymentInterval, 5s default) and silently releases it when underfunded. The client just keeps crediting out-of-band, which is what run_session_payments already does. Two corrections: - Treat HTTP 482 / SkipPaymentCycle as a healthy "balance current" gate (debug, keep looping) instead of logging it as a payment failure. The orchestrator uses it to prevent overpayment; only genuine errors warn. - Document that payment_interval must stay at or below the orchestrator's livePaymentInterval (5s), which is why the 3s default carries margin. Add a test asserting a skip cycle does not kill the loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Draft for review and comparison against Josh's
ja/live-runnerbranch.What this adds
Streamed live-runner sessions (trickle / websocket) must keep paying for as long as the session is open. After the reservation 402, go-livepeer holds the session as a prepaid balance:
ReserveLiveRunnerSession(server/ai_http.go) spawns a server-side ticker (-livePaymentInterval, 5s default) that debits the balance and silently releases the session once it runs dry. There is no further 402; the client must keep crediting out of band viaPOST /payment. Unlikecall_runner(request/response, where the orchestrator pulls payment via a 402 inline), a held-open transport has no request to attach payment to, so the client pushes on a cadence below the server tick.This makes
LiveRunnerSessionown that lifecycle so callers do not hand-manage a background task.Commits
3b60852surface payment session and addrun_session_payments(the interval payment loop).37ee769make the session own start/stop of that loop.81ee7behandle the 482 skip-payment gate and anchor the interval to the server tick.Changes
LiveRunnerSession(still@dataclass(frozen=True)) gainsstart_payments(),aclose(), and async context manager support._payment_taskis stored viaobject.__setattr__, mirroringLv2vJob.start_payment_sender/close.start_paymentsis idempotent, a no-op offchain, and warns instead of raising when called without a running loop.run_session_paymentspays immediately before the first sleep (a cold start can leave a long gap after the reservation payment), treats HTTP 482 /SkipPaymentCycleas a healthy "balance current" gate (debug, keep looping) rather than a failure, and logs and retries other per-cycle failures instead of dying.reserve_sessionthreadspayment_intervalthrough to the session (default 3s, margin under the 5s server tick).Design notes vs lv2v and scope
LivePaymentProcessoronlivePaymentInterval), so interval push aligns more directly with the server than per-segment does.send_paymentPOSTs to{orch}/payment), so one interval loop funds any held-open transport.Usage
Tests
tests/test_live_runner_payments.py(9 cases): offchain no-op, immediate first payment, survives a transient payment error, treats a skip cycle as paid-up, idempotency, no-loop skip,aclose, async context manager. Full suite: 18 passed, ruff clean.Scope and altitude (intentionally left for Josh)
This PR is faithful to the payment-loop mechanics in scope (
LivepeerClient._payment_loop) and lv2v (Lv2vJob), and to the go-livepeer server model. It is intentionally scoped to the payment lifecycle and leaves the larger object-shape decisions open:LivepeerClient.connect()owns three connection-lifetime loops together:_payment_loop,_events_loop,_ping_loop. Here only payments live on the (thin)LiveRunnerSession. If the SDK should grow a scope-styleLiveRunnerClientthat owns payments, events, and heartbeat over one connection lifecycle, these methods move into it unchanged. Open question: is payment lifecycle onLiveRunnerSessionthe intended surface, or a stepping stone to a unified client?_events_loop). Not handled here. A client true to scope would watch events and fail fast instead of discovering the drop via dead media.livePaymentInterval. The reservation challenge does not surface that value (onlypayment_params/orchestrator/manifest_id), so the client guesses. SurfacinglivePaymentIntervalin the challenge would let the client self-tune, which is an orchestrator-side change.async withsugar is an addition beyond scope and lv2v (both use explicit start/close); kept becausereserve_sessionis async-native. Drop it if you prefer to match the existing explicit pattern.Relates to ENG-130. Keeping this as a draft for Josh to take the object-shape pieces.
🤖 Generated with Claude Code