feat(media-use): derive provider cost tier from the registry (#3155)

The provider registry already declares whether a provider is local, free over
the network, or paid over the network via its A/N/P constructors, but nothing
downstream could read that, so anything needing to know whether resolving
through a provider can spend the user's credit had to re-derive it by
string-matching provider names.

Expose it as providerTierFor(name) over the same table and carry the derived
value on the resolve event alongside the provider it came from. Sparse: absent
when the record carries no provider or the name is not declared. A name declared
under two media types must carry one tier; the index throws at import rather
than silently picking one.

Covered by unit tests on the lookup and by end-to-end cases that spawn the real
CLI and read the value off the payload a local server receives, one per tier.
This commit is contained in:
Miguel Ángel
2026-08-10 13:35:25 -04:00
committed by GitHub
parent bd1c1af291
commit 6de29f5bea
5 changed files with 181 additions and 13 deletions
+38
View File
@@ -127,6 +127,44 @@ export function providerNamesFor(type) {
return listFor(type).map((p) => p.name);
}
/**
* name -> cost tier ("local" | "network_free" | "network_paid") over a collection
* of ordered provider lists, i.e. the A / N / P distinction the constructors above
* already declare. Exported so the conflict rule below is testable against a
* fixture; production reads the REGISTRY-wide index built from it.
*
* A name declared under two media types must carry the same tier in both. If it
* didn't, "did this resolve cost credit" would depend on which type happened to
* serve it, and the telemetry property would mean nothing — so this throws at
* import rather than silently picking one.
*/
export function buildProviderTierIndex(providerLists) {
const tiers = new Map();
for (const list of providerLists) {
for (const p of list) {
const tier = p.paid ? "network_paid" : p.network ? "network_free" : "local";
const prior = tiers.get(p.name);
if (prior && prior !== tier)
throw new Error(
`provider "${p.name}" is declared ${prior} under one media type and ${tier} under another`,
);
tiers.set(p.name, tier);
}
}
return tiers;
}
const PROVIDER_TIERS = buildProviderTierIndex(Object.values(REGISTRY));
/**
* Cost tier of a provider by name, or undefined for a name the registry doesn't
* declare. The registry stays the single owner of "does this cost credit", so
* dashboards and callers never re-derive it from provider-name string matching.
*/
export function providerTierFor(name) {
return PROVIDER_TIERS.get(name);
}
/**
* Does an override token (full name like "codex.image_gen" or a prefix like
* "codex") match any provider declared for the type? Same match rule as