Merge pull request #748 from Routstr/one-provider-support

feat: drop provider id from model paths and route them to the cheapest provider
This commit is contained in:
9qeklajc
2026-09-20 16:08:26 +02:00
committed by GitHub
5 changed files with 283 additions and 99 deletions
+19 -11
View File
@@ -423,13 +423,13 @@ GET /v1/models/paths
"id": "anthropic/claude-sonnet-4",
"paths": [
{
"path": "url=https%3A%2F%2Fapi.anthropic.com%2Fv1&provider-id=12&model-id=anthropic%2Fclaude-sonnet-4",
"provider": {"id": 12, "slug": "anthropic-primary", "type": "anthropic"},
"path": "url=https%3A%2F%2Fapi.anthropic.com%2Fv1&model-id=anthropic%2Fclaude-sonnet-4",
"provider": {"slug": "anthropic-primary", "type": "anthropic"},
"endpoint": null
},
{
"path": "url=https%3A%2F%2Fopenrouter.ai%2Fapi%2Fv1&provider-id=42&model-id=anthropic%2Fclaude-sonnet-4&endpoint=google-vertex%2Fus",
"provider": {"id": 42, "slug": "openrouter-main", "type": "openrouter"},
"path": "url=https%3A%2F%2Fopenrouter.ai%2Fapi%2Fv1&model-id=anthropic%2Fclaude-sonnet-4&endpoint=google-vertex%2Fus",
"provider": {"slug": "openrouter-main", "type": "openrouter"},
"endpoint": {"tag": "google-vertex/us", "name": "Google"}
}
]
@@ -440,13 +440,21 @@ GET /v1/models/paths
```
`path` is an opaque, percent-encoded selector. Clients must store and return it
unchanged rather than parsing or reconstructing it. It identifies the exact
configured route with `url`, `provider-id`, and `model-id`. To avoid exposing
private network details, a configured private IP address or any URL with an
explicit port is advertised as `http://localhost`. OpenRouter routes additionally
preserve the exact machine-readable endpoint `tag`. Provider slugs/types and
endpoint names remain display data. When request-side selection is implemented,
an endpoint tag must not silently fall back to another backend.
unchanged rather than parsing or reconstructing it. It identifies the route with
`url` and `model-id`. To avoid exposing private network details, a configured
private IP address or any URL with an explicit port is advertised as
`http://localhost`. OpenRouter routes additionally preserve the exact
machine-readable endpoint `tag`. Provider slugs/types and endpoint names remain
display data. When request-side selection is implemented, an endpoint tag must
not silently fall back to another backend.
A path names no provider, so several configured providers sharing an upstream URL
collapse onto a single path. Such a path always routes to the cheapest of those
providers, and the advertised slug, type and pricing describe that same cheapest
provider. A pinned path never fails over: if the selected provider errors, the
error is returned rather than retried elsewhere. Paths issued before this change
still carry `provider-id` and are still honoured, pinning the exact provider they
name.
### List Paths for One Model
+17 -7
View File
@@ -132,12 +132,18 @@ def _candidate_for_selector(
selector: ModelPathSelector,
candidates: list[tuple[Model, BaseUpstreamProvider]],
) -> tuple[Model, BaseUpstreamProvider] | None:
"""Resolve a route selector to one candidate.
``candidates`` is ranked by cost, so the first URL match is the cheapest
provider configured against that URL. A selector still carrying a legacy
``provider-id`` keeps pinning that exact provider instead.
"""
for model_obj, upstream in candidates:
if (
upstream.db_id == selector.provider_id
and public_provider_url(upstream.base_url) == selector.base_url
):
return model_obj, upstream
if public_provider_url(upstream.base_url) != selector.base_url:
continue
if selector.provider_id is not None and upstream.db_id != selector.provider_id:
continue
return model_obj, upstream
return None
@@ -540,10 +546,14 @@ async def _proxy(
if selector is not None:
pinned = _candidate_for_selector(selector, candidates)
if pinned is None:
target = (
f"provider {selector.provider_id}"
if selector.provider_id is not None
else f"'{selector.base_url}'"
)
return create_error_response(
"invalid_model_path",
f"Model '{selector.model_id}' is not routable through provider "
f"{selector.provider_id}",
f"Model '{selector.model_id}' is not routable through {target}",
404,
request=request,
)
+50 -45
View File
@@ -2,11 +2,16 @@
Exposes every selectable upstream route a Routstr model is reachable through.
A path is a standard percent-encoded query string containing the configured
upstream URL, provider ID, client-visible model ID and, for an exact OpenRouter
endpoint, its machine-readable tag::
upstream URL, client-visible model ID and, for an exact OpenRouter endpoint,
its machine-readable tag::
url=https%3A%2F%2Fapi.anthropic.com%2Fv1&provider-id=12&model-id=claude-sonnet-4
url=https%3A%2F%2Fopenrouter.ai%2Fapi%2Fv1&provider-id=42&model-id=claude-sonnet-4&endpoint=google-vertex%2Fus
url=https%3A%2F%2Fapi.anthropic.com%2Fv1&model-id=claude-sonnet-4
url=https%3A%2F%2Fopenrouter.ai%2Fapi%2Fv1&model-id=claude-sonnet-4&endpoint=google-vertex%2Fus
A path names no provider, so several providers sharing an upstream URL collapse
onto one selector that routes to the cheapest of them. ``provider-id`` is still
accepted when decoding so paths issued before that change keep pinning the exact
provider they named.
"""
from __future__ import annotations
@@ -16,6 +21,7 @@ import ipaddress
import json
import random
import time
from collections.abc import Iterable
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Callable
from urllib.parse import parse_qsl, urlencode, urlsplit
@@ -117,16 +123,11 @@ def public_provider_url(base_url: str) -> str:
def encode_model_path(
base_url: str,
provider_id: int,
model_id: str,
endpoint_tag: str | None = None,
) -> str:
"""Encode the complete upstream route selector advertised to clients."""
components: list[tuple[str, str | int]] = [
("url", base_url),
("provider-id", provider_id),
("model-id", model_id),
]
"""Encode the upstream route selector advertised to clients."""
components = [("url", base_url), ("model-id", model_id)]
if endpoint_tag:
components.append(("endpoint", endpoint_tag))
return urlencode(components)
@@ -137,9 +138,9 @@ class ModelPathSelector:
"""Decoded client-supplied route selector."""
base_url: str
provider_id: int
model_id: str
endpoint_tag: str | None = None
provider_id: int | None = None
def decode_model_path(path: str) -> ModelPathSelector | None:
@@ -166,20 +167,21 @@ def decode_model_path(path: str) -> ModelPathSelector | None:
return None
base_url = params.get("url", "")
model_id = params.get("model-id", "")
raw_provider_id = params.get("provider-id", "")
if not base_url or not model_id or not raw_provider_id:
return None
try:
provider_id = int(raw_provider_id)
except ValueError:
return None
if provider_id <= 0:
if not base_url or not model_id:
return None
provider_id: int | None = None
if (raw_provider_id := params.get("provider-id")) is not None:
try:
provider_id = int(raw_provider_id)
except ValueError:
return None
if provider_id <= 0:
return None
return ModelPathSelector(
base_url=base_url,
provider_id=provider_id,
model_id=model_id,
endpoint_tag=params.get("endpoint") or None,
provider_id=provider_id,
)
@@ -490,9 +492,7 @@ async def _collect_provider_paths(
model_id = exposed_model_id(model)
return DiscoveredPath(
model_id=model_id,
path=encode_model_path(
provider_identity.base_url, provider_identity.id, model_id
),
path=encode_model_path(provider_identity.base_url, model_id),
provider=provider_identity,
model_metadata=_serialize_model_metadata(model, model_id),
)
@@ -529,7 +529,6 @@ async def _collect_provider_paths(
model_id=model_id,
path=encode_model_path(
provider_identity.base_url,
provider_identity.id,
model_id,
endpoint.tag,
),
@@ -900,7 +899,6 @@ def _serialize_path(row: ModelPathRow, provider_fee: float) -> dict[str, Any]:
return {
"path": row.path,
"provider": {
"id": row.upstream_provider_id,
"slug": row.provider_slug,
"type": row.provider_type,
},
@@ -914,6 +912,25 @@ async def _provider_fees(session: "AsyncSession") -> dict[int, float]:
return {row.id: row.provider_fee for row in rows if row.id is not None}
def _cheapest_rows_by_path(
rows: Iterable[ModelPathRow], fees: dict[int, float]
) -> list[tuple[ModelPathRow, float]]:
"""Collapse rows sharing a path onto the cheapest provider, with its fee.
Routing sends a provider-less path to the cheapest provider on that URL, so
the advertised slug and pricing must come from that same provider or clients
are quoted a price they will never be charged.
"""
best: dict[tuple[str, str], tuple[ModelPathRow, float]] = {}
for row in rows:
key = (row.model_id, row.path)
fee = fees.get(row.upstream_provider_id, 1.01)
incumbent = best.get(key)
if incumbent is None or fee < incumbent[1]:
best[key] = (row, fee)
return list(best.values())
async def get_all_model_paths() -> dict:
"""All models with exact routes and provider-specific model metadata."""
async with create_session() as session:
@@ -929,16 +946,9 @@ async def get_all_model_paths() -> dict:
fees = await _provider_fees(session)
grouped: dict[str, list[dict[str, Any]]] = {}
seen_paths: dict[str, set[str]] = {}
updated_at = 0
for row in rows:
updated_at = max(updated_at, row.updated_at)
if row.path in seen_paths.setdefault(row.model_id, set()):
continue
seen_paths[row.model_id].add(row.path)
grouped.setdefault(row.model_id, []).append(
_serialize_path(row, fees.get(row.upstream_provider_id, 1.01))
)
updated_at = max((row.updated_at for row in rows), default=0)
for row, fee in _cheapest_rows_by_path(rows, fees):
grouped.setdefault(row.model_id, []).append(_serialize_path(row, fee))
data = [
{
"id": grouped_model_id,
@@ -974,13 +984,8 @@ async def get_paths_for_model(model_id: str) -> dict:
rows = await load_rows(session, unprefixed_id)
fees = await _provider_fees(session)
seen: set[str] = set()
paths: list[dict] = []
updated_at = 0
for row in rows:
updated_at = max(updated_at, row.updated_at)
if row.path in seen:
continue
seen.add(row.path)
paths.append(_serialize_path(row, fees.get(row.upstream_provider_id, 1.01)))
updated_at = max((row.updated_at for row in rows), default=0)
paths = [
_serialize_path(row, fee) for row, fee in _cheapest_rows_by_path(rows, fees)
]
return {"data": paths, "updated_at": updated_at or None}
+88 -27
View File
@@ -75,17 +75,31 @@ async def _run_proxy(
def test_decode_model_path_round_trips_encode() -> None:
selector = decode_model_path(
encode_model_path("https://openrouter.ai/api/v1", 7, MODEL_ID, "deepinfra/fp8")
encode_model_path("https://openrouter.ai/api/v1", MODEL_ID, "deepinfra/fp8")
)
assert selector is not None
assert selector.base_url == "https://openrouter.ai/api/v1"
assert selector.provider_id == 7
assert selector.provider_id is None
assert selector.model_id == MODEL_ID
assert selector.endpoint_tag == "deepinfra/fp8"
def test_encoded_path_carries_no_provider_id() -> None:
assert "provider-id" not in encode_model_path("http://localhost", MODEL_ID)
def test_decode_model_path_still_accepts_a_legacy_provider_id() -> None:
selector = decode_model_path(
"url=http%3A%2F%2Flocalhost&provider-id=7&model-id=test-model"
)
assert selector is not None
assert selector.provider_id == 7
assert selector.base_url == "http://localhost"
assert selector.model_id == MODEL_ID
def test_decode_model_path_without_endpoint_has_no_tag() -> None:
selector = decode_model_path(encode_model_path("http://localhost", 1, MODEL_ID))
selector = decode_model_path(encode_model_path("http://localhost", MODEL_ID))
assert selector is not None
assert selector.endpoint_tag is None
@@ -94,10 +108,12 @@ def test_decode_model_path_without_endpoint_has_no_tag() -> None:
"path",
[
"",
"url=http://localhost&model-id=test-model",
"url=http://localhost&provider-id=abc&model-id=test-model",
"url=http://localhost&provider-id=0&model-id=test-model",
"provider-id=1&model-id=test-model",
"url=http://localhost&provider-id=1",
"model-id=test-model",
"url=http://localhost",
],
)
def test_decode_model_path_rejects_malformed_selectors(path: str) -> None:
@@ -105,20 +121,63 @@ def test_decode_model_path_rejects_malformed_selectors(path: str) -> None:
@pytest.mark.asyncio
async def test_model_path_routes_to_the_selected_provider() -> None:
first, selected = _make_upstream(1), _make_upstream(2)
async def test_model_path_routes_to_the_cheapest_provider_sharing_the_url() -> None:
# get_candidates ranks by cost, so the first match for a provider-less
# selector is the cheapest provider configured against that URL.
cheapest, pricier = _make_upstream(1), _make_upstream(2)
request = _make_request(
{
"authorization": "Bearer sk-mpkey",
"x-routstr-model-path": encode_model_path("http://localhost", 2, MODEL_ID),
"x-routstr-model-path": encode_model_path("http://localhost", MODEL_ID),
},
json.dumps({"model": MODEL_ID}).encode(),
)
await _run_proxy(request, [(MagicMock(), first), (MagicMock(), selected)])
await _run_proxy(request, [(MagicMock(), cheapest), (MagicMock(), pricier)])
selected.forward_request.assert_awaited_once()
first.forward_request.assert_not_awaited()
cheapest.forward_request.assert_awaited_once()
pricier.forward_request.assert_not_awaited()
@pytest.mark.asyncio
async def test_cheapest_provider_failure_does_not_fall_back_to_the_pricier_one() -> (
None
):
cheapest, pricier = _make_upstream(1, status_code=503), _make_upstream(2)
request = _make_request(
{
"authorization": "Bearer sk-mpkey",
"x-routstr-model-path": encode_model_path("http://localhost", MODEL_ID),
},
json.dumps({"model": MODEL_ID}).encode(),
)
response = await _run_proxy(
request, [(MagicMock(), cheapest), (MagicMock(), pricier)]
)
assert response.status_code == 503
cheapest.forward_request.assert_awaited_once()
pricier.forward_request.assert_not_awaited()
@pytest.mark.asyncio
async def test_legacy_provider_id_still_pins_that_exact_provider() -> None:
cheapest, pinned = _make_upstream(1), _make_upstream(2)
request = _make_request(
{
"authorization": "Bearer sk-mpkey",
"x-routstr-model-path": (
f"url=http%3A%2F%2Flocalhost&provider-id=2&model-id={MODEL_ID}"
),
},
json.dumps({"model": MODEL_ID}).encode(),
)
await _run_proxy(request, [(MagicMock(), cheapest), (MagicMock(), pinned)])
pinned.forward_request.assert_awaited_once()
cheapest.forward_request.assert_not_awaited()
@pytest.mark.asyncio
@@ -131,7 +190,7 @@ async def test_model_path_failure_is_returned_without_falling_back(
request = _make_request(
{
"authorization": "Bearer sk-mpkey",
"x-routstr-model-path": encode_model_path("http://localhost", 1, MODEL_ID),
"x-routstr-model-path": encode_model_path("http://localhost", MODEL_ID),
},
json.dumps({"model": MODEL_ID}).encode(),
)
@@ -146,12 +205,14 @@ async def test_model_path_failure_is_returned_without_falling_back(
@pytest.mark.asyncio
async def test_unknown_provider_in_model_path_is_rejected() -> None:
async def test_unknown_legacy_provider_in_model_path_is_rejected() -> None:
upstream = _make_upstream(1)
request = _make_request(
{
"authorization": "Bearer sk-mpkey",
"x-routstr-model-path": encode_model_path("http://localhost", 99, MODEL_ID),
"x-routstr-model-path": (
f"url=http%3A%2F%2Flocalhost&provider-id=99&model-id={MODEL_ID}"
),
},
json.dumps({"model": MODEL_ID}).encode(),
)
@@ -170,7 +231,7 @@ async def test_model_path_disagreeing_with_the_body_model_is_rejected() -> None:
{
"authorization": "Bearer sk-mpkey",
"x-routstr-model-path": encode_model_path(
"http://localhost", 1, "other-model"
"http://localhost", "other-model"
),
},
json.dumps({"model": MODEL_ID}).encode(),
@@ -207,7 +268,7 @@ async def test_endpoint_tag_pins_the_upstream_subprovider() -> None:
{
"authorization": "Bearer sk-mpkey",
"x-routstr-model-path": encode_model_path(
"https://openrouter.ai/api/v1", 1, MODEL_ID, "deepinfra/fp8"
"https://openrouter.ai/api/v1", MODEL_ID, "deepinfra/fp8"
),
},
json.dumps({"model": MODEL_ID}).encode(),
@@ -245,7 +306,7 @@ async def test_selector_url_must_match_configured_provider() -> None:
{
"authorization": "Bearer key",
"x-routstr-model-path": encode_model_path(
"http://169.254.169.254", 1, MODEL_ID
"http://169.254.169.254", MODEL_ID
),
},
json.dumps({"model": MODEL_ID}).encode(),
@@ -266,7 +327,7 @@ async def test_endpoint_pin_cannot_be_stripped_on_retry() -> None:
{
"authorization": "Bearer key",
"x-routstr-model-path": encode_model_path(
upstream.base_url, 1, MODEL_ID, "deepinfra/fp8"
upstream.base_url, MODEL_ID, "deepinfra/fp8"
),
},
json.dumps({"model": MODEL_ID}).encode(),
@@ -298,7 +359,7 @@ async def test_cashu_receives_endpoint_pinned_body(path: str, handler: str) -> N
{
"x-cashu": "test-token",
"x-routstr-model-path": encode_model_path(
upstream.base_url, 1, MODEL_ID, "deepinfra/fp8"
upstream.base_url, MODEL_ID, "deepinfra/fp8"
),
},
json.dumps(
@@ -354,7 +415,7 @@ async def test_cashu_pin_reaches_http_transport(path: str, status_code: int) ->
{
"x-cashu": "test-token",
"x-routstr-model-path": encode_model_path(
upstream.base_url, 1, MODEL_ID, "deepinfra/fp8"
upstream.base_url, MODEL_ID, "deepinfra/fp8"
),
},
json.dumps({"model": MODEL_ID, "provider": {"allow_fallbacks": True}}).encode(),
@@ -418,7 +479,7 @@ async def test_pinned_exception_does_not_fall_back() -> None:
request = _make_request(
{
"authorization": "Bearer key",
"x-routstr-model-path": encode_model_path(first.base_url, 1, MODEL_ID),
"x-routstr-model-path": encode_model_path(first.base_url, MODEL_ID),
},
json.dumps({"model": MODEL_ID}).encode(),
)
@@ -442,7 +503,7 @@ async def test_unsupported_endpoint_pins_fail_before_payment(
headers = {
"x-cashu": "test-token",
"x-routstr-model-path": encode_model_path(
upstream.base_url, 1, MODEL_ID, "deepinfra/fp8"
upstream.base_url, MODEL_ID, "deepinfra/fp8"
),
}
if is_ehbp:
@@ -471,7 +532,7 @@ async def test_ehbp_pin_does_not_fall_back(cashu: bool) -> None:
headers = {
"ehbp-encapsulated-key": "sealed",
"x-routstr-model": MODEL_ID,
"x-routstr-model-path": encode_model_path(selected.base_url, 1, MODEL_ID),
"x-routstr-model-path": encode_model_path(selected.base_url, MODEL_ID),
}
headers.update({"x-cashu": "token"} if cashu else {"authorization": "Bearer key"})
request = _make_request(headers, b"encrypted-body")
@@ -495,7 +556,7 @@ async def test_duplicate_header_fields_are_rejected() -> None:
from starlette.datastructures import Headers
selected = _make_upstream(1)
route = encode_model_path(selected.base_url, 1, MODEL_ID).encode()
route = encode_model_path(selected.base_url, MODEL_ID).encode()
request = _make_request({}, json.dumps({"model": MODEL_ID}).encode())
request.headers = Headers(
raw=[
@@ -512,7 +573,7 @@ async def test_duplicate_header_fields_are_rejected() -> None:
@pytest.mark.asyncio
async def test_attestation_does_not_ignore_model_path() -> None:
request = _make_request(
{"x-routstr-model-path": encode_model_path("http://localhost", 1, MODEL_ID)},
{"x-routstr-model-path": encode_model_path("http://localhost", MODEL_ID)},
b"",
)
request.method = "GET"
@@ -528,7 +589,7 @@ async def test_model_fallback_list_is_rejected_when_pinned() -> None:
request = _make_request(
{
"authorization": "Bearer key",
"x-routstr-model-path": encode_model_path(selected.base_url, 1, MODEL_ID),
"x-routstr-model-path": encode_model_path(selected.base_url, MODEL_ID),
},
json.dumps({"model": MODEL_ID, "models": ["other-model"]}).encode(),
)
@@ -564,7 +625,7 @@ async def test_pinned_recovery_stays_on_selected_provider(
{
"authorization": "Bearer key",
"x-routstr-model-path": encode_model_path(
selected.base_url, 1, MODEL_ID, endpoint
selected.base_url, MODEL_ID, endpoint
),
},
json.dumps(
@@ -608,7 +669,7 @@ async def test_pinned_recovery_preserves_routing_fields(
{
"authorization": "Bearer key",
"x-routstr-model-path": encode_model_path(
selected.base_url, 1, MODEL_ID, endpoint
selected.base_url, MODEL_ID, endpoint
),
},
json.dumps(
+109 -9
View File
@@ -272,6 +272,17 @@ async def _set_provider_fee(engine: AsyncEngine, provider_id: int, fee: float) -
await session.commit()
async def _set_provider_base_url(
engine: AsyncEngine, provider_id: int, base_url: str
) -> None:
async with AsyncSession(engine) as session:
provider = await session.get(UpstreamProviderRow, provider_id)
assert provider is not None
provider.base_url = base_url
session.add(provider)
await session.commit()
def _paths_of(payload: dict, model_id: str) -> set[str]:
for entry in payload["data"]:
if entry["id"] == model_id:
@@ -289,7 +300,7 @@ def _expected_path(
endpoint_tag: str | None = None,
) -> str:
return mp.encode_model_path(
f"https://provider-{provider_id}", provider_id, model_id, endpoint_tag
f"https://provider-{provider_id}", model_id, endpoint_tag
)
@@ -308,7 +319,6 @@ def _path_entry(
return {
"path": _expected_path(provider_id, model_id, endpoint_tag),
"provider": {
"id": provider_id,
"slug": provider_slug or f"p{provider_id}",
"type": provider_type
or ("anthropic" if provider_id == 1 else "openrouter"),
@@ -368,21 +378,20 @@ def test_public_provider_url_preserves_public_urls_without_ports() -> None:
assert mp.public_provider_url("http://localhost") == "http://localhost"
def test_encode_model_path_includes_complete_route_identity() -> None:
def test_encode_model_path_omits_provider_id() -> None:
assert mp.encode_model_path(
"https://openrouter.ai/api/v1", 42, "anthropic/claude-sonnet-4"
"https://openrouter.ai/api/v1", "anthropic/claude-sonnet-4"
) == (
"url=https%3A%2F%2Fopenrouter.ai%2Fapi%2Fv1"
"&provider-id=42&model-id=anthropic%2Fclaude-sonnet-4"
"&model-id=anthropic%2Fclaude-sonnet-4"
)
assert mp.encode_model_path(
"https://openrouter.ai/api/v1",
42,
"anthropic/claude-sonnet-4",
"google-vertex/us-east5",
) == (
"url=https%3A%2F%2Fopenrouter.ai%2Fapi%2Fv1"
"&provider-id=42&model-id=anthropic%2Fclaude-sonnet-4"
"&model-id=anthropic%2Fclaude-sonnet-4"
"&endpoint=google-vertex%2Fus-east5"
)
@@ -506,7 +515,7 @@ async def test_direct_path_masks_private_configured_provider_url(
await mp.refresh_model_paths([provider])
assert _paths_of(await mp.get_all_model_paths(), "local-model") == {
mp.encode_model_path("http://localhost", 1, "local-model")
mp.encode_model_path("http://localhost", "local-model")
}
@@ -886,7 +895,7 @@ async def test_openrouter_provider_adds_endpoint_paths(
assert {
item["endpoint"]["name"] for item in payload["data"] if item["endpoint"]
} == {"Google"}
assert {item["provider"]["id"] for item in payload["data"]} == {2}
assert {item["provider"]["slug"] for item in payload["data"]} == {"p2"}
@pytest.mark.asyncio
@@ -984,6 +993,97 @@ async def test_path_pricing_uses_the_provider_fee_of_its_own_provider(
)
@pytest.mark.asyncio
async def test_providers_sharing_a_url_collapse_onto_the_cheapest_path(
patched_session: AsyncEngine, sats_quote: float
) -> None:
"""One selector per URL, describing the provider routing will actually use."""
shared_url = "https://shared.example/v1"
await _set_provider_base_url(patched_session, 1, shared_url)
await _set_provider_base_url(patched_session, 2, shared_url)
await _set_provider_fee(patched_session, 1, 2.0)
await _set_provider_fee(patched_session, 2, 1.1)
await mp.refresh_model_paths(
[
_FakeProvider(
provider_type="anthropic",
base_url=shared_url,
models=[_model("shared-model")],
db_id=1,
),
_FakeProvider(
provider_type="openrouter",
base_url=shared_url,
models=[_model("shared-model")],
db_id=2,
),
]
)
payload = await mp.get_paths_for_model("shared-model")
assert [item["path"] for item in payload["data"]] == [
mp.encode_model_path(shared_url, "shared-model")
]
assert payload["data"][0]["provider"] == {"slug": "p2", "type": "openrouter"}
@pytest.mark.asyncio
async def test_collapsed_path_advertises_the_cheapest_provider_pricing(
patched_session: AsyncEngine, sats_quote: float
) -> None:
shared_url = "https://shared.example/v1"
await _set_provider_base_url(patched_session, 1, shared_url)
await _set_provider_base_url(patched_session, 2, shared_url)
await _set_provider_fee(patched_session, 1, 2.0)
await _set_provider_fee(patched_session, 2, 1.1)
priced = _model("shared-model")
priced.pricing = {"prompt": "0.000005", "completion": "0.000005"}
await mp.refresh_model_paths(
[
_FakeProvider(
provider_type="anthropic",
base_url=shared_url,
models=[priced],
db_id=1,
),
_FakeProvider(
provider_type="openrouter",
base_url=shared_url,
models=[priced],
db_id=2,
),
]
)
payload = await mp.get_paths_for_model("shared-model")
assert len(payload["data"]) == 1
assert payload["data"][0]["model"]["pricing"]["prompt"] == pytest.approx(
0.000005 * 1.1
)
@pytest.mark.asyncio
async def test_paths_advertise_no_provider_id(patched_session: AsyncEngine) -> None:
await mp.refresh_model_paths(
[
_FakeProvider(
provider_type="anthropic",
base_url="https://provider-1",
models=[_model("claude-x")],
db_id=1,
)
]
)
payload = await mp.get_all_model_paths()
entry = payload["data"][0]["paths"][0]
assert "provider-id" not in entry["path"]
assert "id" not in entry["provider"]
@pytest.mark.asyncio
async def test_paths_keep_upstream_pricing_when_the_quote_is_unavailable(
patched_session: AsyncEngine, monkeypatch: pytest.MonkeyPatch