fix(byoc): send real capability + model_id for usage attribution#33
fix(byoc): send real capability + model_id for usage attribution#33seanhanca wants to merge 2 commits into
Conversation
`_create_byoc_payment` sent the BYOC `capability` only as a bare JSON field that the remote signer dropped (its RemotePaymentRequest had no such field) and sent no model id at all. Combined with the hardcoded `type:"lv2v"`, the signer's create_signed_ticket metering event recorded pipeline=live-video-to-video / model_id=unknown for every BYOC job. Thread the REAL capability (already present) and the model id into the `/generate-live-payment` body so the signer can attribute usage to the true pipeline + model. `type` stays "lv2v" so fee/pixel routing is unchanged. A new `_extract_model_id` helper locates the model in the request payload (`model_id`/`model`), falling back to job parameters; when no model id is found the field is omitted so behavior is byte-identical to today (the signer falls back). Adds tests covering the wire contract (capability + model_id present for a nano-banana request), model extraction precedence, and the backward-compatible omit-when-absent path.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughBYOC job submission now extracts an optional provider model identifier from payloads or parameters and includes it in the signer payment request when present. The new tests cover extraction behavior, request-body construction, and forwarding through both BYOC and training submission paths. ChangesBYOC model attribution
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/test_byoc_usage_attribution.py (1)
158-209: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a training-path attribution regression.
This file verifies inference forwarding, but the PR also changes
submit_training_job. Add a test wherereq.model_iddiffers fromreq.params["model_id"]and assert the signer payment body uses the top-level training model ID.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_byoc_usage_attribution.py` around lines 158 - 209, Add a regression test for the training path in submit_training_job, since the current coverage only checks submit_byoc_job. Create a case where the request’s top-level model_id differs from req.params["model_id"], then verify the signer payment request body is built from the top-level training model_id rather than the nested params value. Use the existing submit_training_job flow and the same request/signing capture pattern to locate the behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/livepeer_gateway/byoc.py`:
- Line 719: The payment attribution is using a model id extracted from
byoc_req.payload, which can be overridden by req.params and diverge from the
actual training model. Update the signer-attribution path in byoc.py to use the
explicit req.model_id from the training request instead of
_extract_model_id(byoc_req.payload, byoc_req.parameters), while leaving the
orchestrator body construction unchanged. Locate the call site building the
signer request near the byoc_req payload handling and switch it to the top-level
model_id source.
---
Nitpick comments:
In `@tests/test_byoc_usage_attribution.py`:
- Around line 158-209: Add a regression test for the training path in
submit_training_job, since the current coverage only checks submit_byoc_job.
Create a case where the request’s top-level model_id differs from
req.params["model_id"], then verify the signer payment request body is built
from the top-level training model_id rather than the nested params value. Use
the existing submit_training_job flow and the same request/signing capture
pattern to locate the behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bb3eafc0-fe21-4816-8dce-e10cda18c678
📒 Files selected for processing (2)
src/livepeer_gateway/byoc.pytests/test_byoc_usage_attribution.py
There was a problem hiding this comment.
Pull request overview
This PR fixes BYOC usage attribution by ensuring the gateway includes the real BYOC capability and a best-effort extracted model_id in the remote signer /generate-live-payment request body, so downstream metering can correctly group usage by pipeline + model_id without changing fee routing.
Changes:
- Add
_extract_model_id(payload, parameters)helper and thread the extractedmodel_idthrough BYOC inference and training payment creation. - Extend
_create_byoc_paymentto optionally includemodel_idin the signer request body (while keepingtype="lv2v"unchanged). - Add focused unit tests covering model-id extraction precedence and the
/generate-live-paymentwire payload.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/livepeer_gateway/byoc.py |
Adds model-id extraction and includes optional model_id in signer payment payload for correct attribution. |
tests/test_byoc_usage_attribution.py |
Adds tests validating the signer request body carries real capability + model_id and remains backward-compatible when absent. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if model_id: | ||
| payment_payload["model_id"] = model_id |
There was a problem hiding this comment.
Fixed in 57e9e16. _create_byoc_payment now does model_id = model_id.strip() if isinstance(model_id, str) else "" before the if model_id: guard, so whitespace-only or non-string values are omitted and the wire body stays byte-identical to today when no real model id is present.
…wire body Addresses review feedback on PR livepeer#33: - submit_training_job built the signer attribution model_id from the merged byoc_req.payload ({"model_id": req.model_id, **req.params}), so a stray `model_id` inside req.params could override the real training model and diverge from the orchestrator body (which uses req.model_id). Resolve it from the explicit top-level req.model_id, falling back to req.params, so payment attribution matches the orchestrator body. (CodeRabbit, byoc.py:719) - _create_byoc_payment now defensively strips (and ignores non-string) model_id so whitespace-only values are omitted from the wire body, keeping it byte-identical to today when no real model id is present. (Copilot, byoc.py:242) - Add a submit_training_job regression test asserting the signer body uses the top-level training model_id, not the nested params value. (CodeRabbit nitpick)
Problem
BYOC usage reaches OpenMeter via the remote signer's
create_signed_ticketmetering event. For every BYOC capability (e.g.
nano-banana) that eventwas recorded as
pipeline=live-video-to-video/model_id=unknowninstead ofthe real pipeline + model.
Root cause (model attribution lost at
_create_byoc_payment)src/livepeer_gateway/byoc.py→_create_byoc_paymentbuilds the/generate-live-paymentrequest and:type: "lv2v"for every BYOC capability → the go-livepeer signeremits
pipeline = PipelineLiveVideoToVideo("live-video-to-video"): thecoarse mislabel;
capabilityonly as a bare JSON field the signer neverread (its
RemotePaymentRequesthad no such field → dropped) and sendsno model id at all → the signer emits an empty
model_id, which thepymthouse collector defaults to
"unknown".The real capability is the
capabilityarg; the real model id lives in theinference request (
req.payload/req.parameters). A pymthouse-only fix isimpossible — the data is gone before it reaches Redpanda.
The additive wire contract
Two explicit, optional string fields are added to the
/generate-live-paymentrequest body (the signer's
RemotePaymentRequest):capabilitynano-banana) — already sent today; the signer now reads itmodel_idtypestays"lv2v"so the signer keeps reusing the live fee/pixel routing(BYOC has no per-job pixel count). Usage attribution is thereby decoupled
from fee routing. Empty/absent fields ⇒ today's exact behavior.
The signer side of this contract is implemented in the cross-linked go-livepeer
PR (see below). The meter already groups by
pipeline+model_id, so nometer change is needed — the values simply become correct.
Changes
_create_byoc_paymentaccepts amodel_idand includes it in the body(omitted when empty → byte-identical to today).
_extract_model_id(payload, parameters)helper locates the model id inthe request (
model_idthenmodel, payload before parameters).submit_byoc_jobandsubmit_training_jobthread the extracted model idthrough. Model source:
submit_byoc_job→req.payload/req.parameters(
byoc.py_extract_model_idcall), training →byoc_req.payload(which carries
model_id).Zero-regression
typeis unchanged → fee/pixel math unchanged.model_idis omitted entirely → the bodyis byte-identical to today and the signer falls back.
Test plan
pytest tests/(full suite green locally, 17 passed). Newtests/test_byoc_usage_attribution.pyproves:/generate-live-paymentbody carries the realcapability+model_idfor a representative BYOC capability (nano-banana);typestays"lv2v";model_idis omitted when not determinable (backward-compatible);model_id> payloadmodel>parameters);
submit_byoc_job.End-to-end verification recipe
With both this PR and the go-livepeer signer change deployed: drive a
nano-bananageneration against the preview chain, then query OpenMetergroupBy=pipeline_model— it should show the real pipeline + model id(
nano-banana/ the provider model), notlive-video-to-video/unknown.Cross-link
Signer side (reads these fields): livepeer/go-livepeer PR — livepeer/go-livepeer#3966
Does not modify pymthouse or NaaP; the collector/read
unknownfallbacks areleft in place as safety nets.
Made with Cursor
Summary by CodeRabbit
New Features
Bug Fixes
Tests