livepeer / livepeer/go-livepeer
Support per-capability pricing oracle selection for BYOC capabilities
- Dominant language
- Go
- Stars
- 586
- Forks
- 226
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 19
Description
## Problem
BYOC external capabilities can express custom-currency prices today, but the pricing oracle/feed is effectively node-global and BYOC prices are snapshotted into `jobPriceInfo` at registration time. This makes it hard for an orchestrator to use different pricing policies/oracles for different capabilities or models.
This is useful for AI BYOC because different model capabilities may need different pricing policies: provider cost, observed tokens/sec, queue/utilization, target margin, and market floor can differ by model.
## Current behavior / research
Researched against `feat/remote-signer-byoc` at `a78e876ff6c97d935344643c2547d0f84d4b26dd`.
Relevant paths:
- `cmd/livepeer/starter/flags.go`: `-priceFeedAddr` is a single node-level Chainlink feed address used for custom currency conversion on `-pricePerUnit` / `-maxPricePerUnit`.
- `cmd/livepeer/starter/starter.go`: initializes one global `core.PriceFeedWatcher` from `-priceFeedAddr`.
- `core/autoconvertedprice.go`: `NewAutoConvertedPrice()` reads from global `core.PriceFeedWatcher` and updates its internal wei value when feed updates arrive.
- `core/external_capabilities.go`: BYOC capability config accepts `price_per_unit`, `price_scaling`, and `currency`, then creates an `AutoConvertedPrice`.
- `core/ai_orchestrator.go`: `RegisterExternalCapability()` calls `SetPriceForExternalCapability("default", cap.Name, cap.GetPrice())`, which stores the current price value.
- `core/livepeernode.go`: built-in capability/model prices store `*AutoConvertedPrice`, but BYOC `jobPriceInfo` stores `*big.Rat`, so feed updates are not preserved in the advertised BYOC price unless the capability is re-registered or code updates the stored value.
## Proposal
Add per-capability pricing oracle selection for BYOC capabilities. The minimal version should preserve existing registration JSON and add an optional pricing/oracle section.
Possible shape:
```json
{
"name": "openai-chat-completions/Qwen3-Coder-30B-A3B-Instruct",
"url": "http://worker:8000",
"capacity": 1,
"price_per_unit": 1,
"price_scaling": 86400,
"currency": "USD",
"pricing_oracle": {
"source": "chainlink",
"price_feed_addr": "0x...",
"policy": "competitive-v1"
}
}
```
Implementation options:
1. Keep the current fields as the default/backward-compatible path.
2. Add an oracle resolver keyed by capability name/model. For Chainlink-style feeds, deduplicate watchers by feed address.
3. Store a dynamic price handle for BYOC, not only a `*big.Rat` snapshot. Either change `jobPriceInfo` to store `*AutoConvertedPrice`/a pricing source interface, or have the update callback call `SetPriceForExternalCapability(...)` when the oracle price changes.
4. Allow offchain signed oracle records as a future source type, where the oracle emits final `wei/sec` for `orchestrator + capability + model + policy + valid_until`, signed by a configured key.
5. Keep `GetOrchestrator` / `CapabilitiesPrices` and `PriceInfoForCaps` returning the final current wei price so gateway and remote signer behavior remains simple.
## Acceptance criteria
- Existing BYOC capability registration without `pricing_oracle` continues to work.
- A single orchestrator can register capability A using one oracle/feed and capability B using a different oracle/feed.
- Feed/oracle updates change the advertised BYOC `PriceInfo` without requiring capability re-registration.
- Built-in capability pricing behavior is not regressed.
- Tests cover at least:
- two BYOC capabilities using different feed currencies/addresses,
- dynamic update propagation into advertised BYOC price,
- fallback to current node-level `-priceFeedAddr`,
- invalid/missing oracle config errors.
Contributor guide
Research direction
Start with cmd/livepeer/starter/flags.go and starter.go, then trace pricing through core/autoconvertedprice.go, core/external_capabilities.go, core/ai_orchestrator.go, and core/livepeernode.go. Use the stated acceptance criteria to define tests for distinct feeds, dynamic advertised-price updates, node-level fallback, and invalid oracle configuration. Done means existing registrations still work while per-capability oracle selection updates BYOC PriceInfo without re-registration.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend, backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100