feat(server): Live runner single shot payments#3975
Conversation
|
Caution Review failedAn error occurred during the review process. Please try again later. ✨ 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 |
rickstaa
left a comment
There was a problem hiding this comment.
@ad-astra-video, the payment logic and its parity with ReserveLiveRunnerSession make sense to me. I left a couple of comments and opened a pull request to address them: ad-astra-video#35.
Once those changes are applied, please update the pull request description and provide a test script for validating the implementation with the SDK. After that, it should be ready for Josh’s final review.
* server: simplify single-shot session release lifecycle Pre-existing working-tree cleanup of ProxyLiveRunnerSingleShot: drop the reservedID/reservedIDInput split, release the reserved session on return, and remove the redundant session/manifest recheck. * server: address single-shot payment review feedback - Drop the ModifyResponse block (Session-Id/Control/Payment response headers). WebSocket clients cannot read upgrade response headers, and SSE/HTTP clients derive the session-scoped payment URL from the 402 challenge (manifest_id + orchestrator), so the headers were redundant. Removes the now-unused Session-Payment constant, sessionPaymentURL, and the response-header test assertions. - Surface payment_interval_ms in the 402 challenge so held-open clients (SSE/websocket) size their payment cadence below the server tick instead of guessing. - Rename startLiveRunnerSessionPaymentMonitor to ...PaymentLoop to match the startAutoConvertLoop convention (a start* method that owns its own goroutine); monitorCtx to loopCtx to match. - Type the payment loop manifestID as core.ManifestID instead of a bare string, converting at the single use site, so it cannot be transposed with the adjacent runnerID/sessionID params. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * server: bound single-shot payment loop to the request context The metering loop is now bound to the ctx passed in rather than always detaching via context.WithoutCancel. Single-shot passes the request ctx (r.Context()), so the loop stops when the request returns instead of lingering up to one LivePaymentInterval; the persistent path passes context.WithoutCancel(ctx) so its loop outlives the reservation request and stops when the session is released. Matches the startAutoConvertLoop convention: the caller owns the lifetime via the context. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
rickstaa
left a comment
There was a problem hiding this comment.
@ad-astra-video thanks for handeling my review comments. Approved from my side. Requested a final review from Josh.
|
@ad-astra-video could you fix the merge conflict. Thanks! |
|
@rickstaa fixed merge conflict on |
What does this pull request do? Explain your changes. (required)
Enables on-chain payments for single-shot live runner calls. Single-shot mode was functional but unpaid:
ProxyLiveRunnerSingleShotreserved a session, proxied, and released with no payment gate. This makes single-shot (request/response and short-lived / batch) calls billable, mirroring the existing persistent (ReserveLiveRunnerSession) payment flow. Parity with the persistent path is intentional. The deliberate differences are the single-shot mode guard, release-on-return, the metering-loop lifetime, and the proxy (vs JSON) response.Specific updates (required)
402payment challenge on the single-shot path (sharedrunnerChallenge), carrying ticket params,manifest_id, andpayment_interval_ms(the orchestrator's debit interval).Livepeer-Payment/Livepeer-Segmentheaders) withProcessPayment, reserve the session (manifest_id= session id), proxy to the runner, and release on return.startLiveRunnerSessionPaymentLoop) that debits the prepaid balance everyLivePaymentIntervalwhile the request is open. Lifetime is set by the caller's context (single-shot: request ctx; persistent:context.WithoutCancel).POST /apps/{runner}/session/{id}/paymentendpoint.How did you test each of these updates (required)
Ensured
go-livepeerbuilds, tests pass and ran the new logic against this branch in the app-examples repo confirming payments now work for single-shot runners.Does this pull request close any open issues?
Fixes #3955
Checklist:
makeruns successfully./test.shpass[x] README and other documentation updated[ ] Pending changelog updatedFollow-ups (out of scope, tracked separately)
LivePaymentProcessor.processOne(currently best-effort). Harden LivePaymentProcessor: enforce payment failures and avoid post-release overcharge #3982call_runner(feat: add SSE/chunked streaming to call_runner livepeer-python-gateway#25), session payment streamer (feat(live-runner): session-owned payment lifecycle for streamed sessions livepeer-python-gateway#31), single-shot WebSocket (ENG-180).single-shot.EDIT: Description updated to explain the PRs behavoir by @rickstaa at 14-07-2026.