Skip to content

Report each signed payment session from call_runner - #1

Merged
mikezupper merged 4 commits into
releases/m2from
feat/call-runner-manifest-id
Oct 19, 2026
Merged

mikezupper merged 4 commits into
releases/m2from
feat/call-runner-manifest-id

Conversation

@eliteprox

@eliteprox eliteprox commented Oct 15, 2026 •

Copy link
Copy Markdown

Summary

This PR lets a gateway find out what each call_runner call cost. call_runner takes a new, optional on_payment_session callback. Each time the remote signer signs a payment session, the SDK calls it with that session. The session's new manifest_id property names the orchestrator session the payment funds, so the gateway can match it to Clearinghouse usage records and charge the right job.

Callers that don't pass the callback see no change in results, errors or streams.

Why

The signer charges when it signs a payment, not when the job succeeds. So a call can be charged and still fail: the runner errors, the payment turns out to be unusable, or the caller cancels. A gateway that bills its own users has to know which charges belong to which call, including the failed ones. Today call_runner doesn't tell it.

We need this for the Network Engineering SPE build track (project board). Its second milestone defines how the builder engine reports network cost per job (ISS-04). The engine records the manifest of every payment the signer signs, then matches each one to a Clearinghouse usage record, so every job shows what it cost, whether it succeeded or not. livepeer-builder-core pins this branch until it merges.

An earlier version read AuthID from the signer's state. On livepeer#74, Josh pointed out that the state is opaque and shouldn't become a public contract, and suggested ManifestID instead. This version uses ManifestID and doesn't decode the signer's state.

What callers see

manifest_ids: list[str] = []
result = await call_runner(
    ...,
    on_payment_session=lambda s: manifest_ids.append(s.manifest_id),
)
  • When it runs. Once for each payment session, when the signer first signs for it and before the paid request goes to the runner. The callback can be sync or async.
  • What it reports. Every session the signer signs: one whose payment proves unusable, one followed by a runner failure, and each new session after the runner asks for payment again (a repeated 402). Fixed-price calls report too.
  • What it doesn't report. Sessions the signer refuses, including refresh requests.
  • If the callback raises, the paid request isn't sent, and the caller gets the callback's error unchanged.
  • If the call is cancelled while a payment is being signed, the SDK lets the signing finish and reports the session before it passes the cancellation on. Because the caller owns the list, a call cancelled under asyncio.wait_for, for example, still accounts for every session it signed.

Known limits

This replaces livepeer#71, which was closed so the change could target a fork branch.

Test plan

  • pytest -W error::ResourceWarning at e6d243b: 227 passed, 2 failed. Both failures are in tests/test_stats_pull.py and fail the same way on main. The new tests cover sync and async callbacks, repeated 402s, a runner failure after payment, a refused session, an unusable payment, a raising callback, a cancelled call, and manifest_id staying the same across a payment refresh.
  • Confirm that a fresh ManifestID per challenge is behavior callers can rely on (call_runner doesn't report the signer sessions it opens livepeer/livepeer-python-gateway#74).

eliteprox and others added 4 commits October 14, 2026 18:54
call_runner takes on_payment_session, called with each session the signer
signs, before the paid request goes out. A caller can link a charge to the
call that caused it, including a call that fails after the signer has signed.
LivePaymentSession.manifest_id is the manifest from the 402 challenge, so the
session is identified without reading the signer's state.
Replace gathering of payment_task with a drain loop to handle cancellation more gracefully.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@mikezupper
mikezupper merged commit 13edf39 into releases/m2 Oct 19, 2026
1 check failed
@eliteprox
eliteprox deleted the feat/call-runner-manifest-id branch October 19, 2026 15:51
eliteprox added a commit that referenced this pull request Oct 20, 2026
…nifest-id"

This reverts commit 13edf39, reversing
changes made to fc47903.
eliteprox added a commit that referenced this pull request Oct 20, 2026
…nifest-id" (#4)

This reverts commit 13edf39, reversing
changes made to fc47903.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants