Repository navigation
Conversation
LiveRunnerCallStream.session_id carries the answered payment challenge's
manifest_id, as LiveRunnerCallResult.session_id already does for unary calls;
it is "" when no challenge was answered.
Errors raised from a call_runner attempt carry payment_sent, True when that
attempt's request already carried Livepeer-Payment headers, and manifest_id,
the paid challenge's manifest_id ("" when unpaid). A gateway may fail over to
another runner only while payment_sent is False, and can attribute the
network cost of a paid call that failed. When the challenge retries run out,
payment_sent covers every attempt and manifest_id is the last paid one.
manifest_id is the key the signer's create_signed_ticket Kafka event carries
as manifest_id; it is not the event's session_id (the signer state id) or
pm_session_id (the orchestrator ticket params' recipientRandHash).
Replayed onto livepeer/livepeer-python-gateway main without the multipart
commit this change was previously stacked on.
Selection keeps each failed attempt as capacity, unreachable, or other, and copies payment_sent and the paid manifest id onto the rejection so a gateway can stop after a paid attempt.
This was referenced Oct 6, 2026
Refactor the error handling in LivepeerGatewayError and related classes to replace the manifest_id with auth_ids, which now captures the signer auth IDs for each payment session. This change allows for better tracking of payment sessions across errors and runner rejections, ensuring that the system can accurately attribute costs and manage retries based on the associated auth IDs. Update tests to reflect these changes and ensure proper functionality.
Merge auth ids through one unique_auth_ids helper, attach the signed session's auth id in a single place in _get_runner_payment, decode the signer state's auth id once per payment response, drop the unused PaymentSession.auth_id, and simplify the unreachable exception check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
call_runner now writes every attempt to a RunnerCallRecord as it happens: the challenge answered, the signer auth id, whether the request left with Livepeer-Payment, the runner's status, and how it ended. The result, stream, and any error hold that same record, and a caller can pass its own to read it after cancellation. payment_sent and auth_ids on errors, results, and streams are derived from it, which replaces per-raise-site stamping that missed signer failures, refresh retries, and non-gateway exceptions. Attempt outcomes report "unreachable" only when the request provably never left; a bare timeout reads as "timeout". Unexpected exceptions are wrapped in LivepeerGatewayError so they carry the record, and open_stream now closes its session when reading an error body fails. Runner failover (selection.py, scope.py, RunnerRejection) is reverted to main and moves to a follow-up PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
eliteprox
commented
Oct 12, 2026
Comment on lines
+365
to
+366
| self._state = state | ||
| self._auth_id = auth_id_from_signer_state(state) |
Collaborator
Author
There was a problem hiding this comment.
Should we avoid storing _auth_id separately and keep it within state for downstream parsing by auth_id_from_signer_state
Comment on lines
+4
to
+6
| from typing import TYPE_CHECKING | ||
|
|
||
| if TYPE_CHECKING: |
Collaborator
Author
There was a problem hiding this comment.
TYPE_CHECKING can usually be removed
…tate RunnerAttempt, RunnerAttemptOutcome and RunnerCallRecord now live in errors.py, so LivepeerGatewayError can reference RunnerCallRecord without a TYPE_CHECKING import. LivePaymentSession.auth_id is parsed from the stored signer state instead of being cached separately.
5 tasks done
Collaborator
Author
|
Moved to eliteprox#5, which targets releases/m2. Both review comments are addressed there: the call record types moved into errors.py, so the TYPE_CHECKING import is gone, and LivePaymentSession.auth_id now reads from the stored signer state. |
This was referenced Oct 14, 2026
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.
Summary
call_runnernow writes every attempt to aRunnerCallRecordas it happens. EachRunnerAttemptrecords the payment challenge it answered (manifest_id), the signerauth_idit opened, whether the request went out withLivepeer-Paymentheaders (payment_sent), the runner's HTTP status, and how the attempt ended (outcome).record=so it can still read it after cancelling the call (for example withasyncio.wait_for).payment_sentandauth_idsonLivepeerGatewayError,LiveRunnerCallResultandLiveRunnerCallStreamare read-only properties computed from the record.payment_sentis true once any attempt sent payment, so a gateway may switch to another runner only while it is false.auth_idslists every signer session the call opened. The signer charges when it signs, so an auth id can appear beforepayment_sentis true.LivepeerGatewayError(for example aClientPayloadErrorwhile reading an error body) are re-raised asLivepeerGatewayErrorwith the record attached.unreachableonly when the connection itself failed, so the request never got to the runner;timeoutfor a plain timeout, since the runner may already have the request;succeeded,payment_required,refused(503),http,payment(signer failure),disconnected,invalid_responseandcancelled.LivePaymentSession.auth_idreads the signer'sAuthIDfrom the payment state, decoded once per payment response.open_streamnow closes its HTTP session when reading an error body fails.LiveRunnerCallStream.session_idcarries the paid challenge'smanifest_id, asLiveRunnerCallResult.session_idalready does.Runner selection and failover (
RunnerSelectionCursor, Scope startup,RunnerRejection) are unchanged and matchmain. Making them use the record, and stop failing over once a payment is sent, is planned for a follow-up PR.Test plan
uv run --frozen --group lint ruff check: no new findings on changed linesuv run --frozen --group test pytest -W error::ResourceWarning: 267 passedcall_runneragainst a real aiohttp runner on a local port, with a fake signer, and cover:unreachable) versus a slow runner (timeout);🤖 Generated with Claude Code