fix(upstream): reject unusable litellm prices instead of resolving them

A litellm cost entry that lists a model but prices both tokens at 0 (free
moderation/rerank tiers) was resolved as a real price, importing the model
enabled and served for free. Reject a both-zero (or negative) litellm hit so
the caller falls through to the next source or fails closed, mirroring the
_has_valid_pricing check the OpenRouter feed already applies.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Jeroen Ubbink
2026-07-05 15:39:03 +02:00
co-authored by Claude Opus 4.8
parent 2a27fb239e
commit 9f04525c82
2 changed files with 45 additions and 0 deletions
+6
View File
@@ -89,6 +89,12 @@ def _from_litellm(model_id: str) -> ResolvedPricing | None:
completion = info.get("output_cost_per_token")
if not isinstance(prompt, (int, float)) or not isinstance(completion, (int, float)):
return None
# A both-zero entry is litellm listing a model without a real price (free
# moderation/rerank tiers do this) — treating 0/0 as resolved would serve
# the model for free. Reject it (and any negative) so the caller falls
# through, mirroring async_fetch_openrouter_models' _has_valid_pricing.
if prompt < 0 or completion < 0 or (prompt == 0 and completion == 0):
return None
input_modalities = ["text"]
if info.get("supports_vision"):
+39
View File
@@ -145,6 +145,45 @@ async def test_bare_deepseek_resolves_via_litellm() -> None:
or_feed.assert_not_awaited()
@pytest.mark.asyncio
async def test_litellm_zero_price_entry_fails_closed(
caplog: pytest.LogCaptureFixture,
) -> None:
"""A litellm entry that lists a model but prices it at 0/0 (free-tier
moderation/rerank models do this) is not a real price — treating it as one
would silently serve the model for free. The resolver must reject a both-zero
litellm hit and fall through, so the model imports disabled, not at $0."""
payload = {
"data": [
{"id": "omni-moderation-latest", "object": "model", "owned_by": "openai"},
]
}
gen_logger = logging.getLogger("routstr.upstream.generic")
gen_logger.addHandler(caplog.handler)
try:
with _patch_models_endpoint(payload):
or_feed = AsyncMock(return_value=[])
with patch(
"routstr.payment.models.async_fetch_openrouter_models", or_feed
):
models = await GenericUpstreamProvider(
base_url="http://x"
).fetch_models()
finally:
gen_logger.removeHandler(caplog.handler)
model = _model_by_id(models, "omni-moderation-latest")
assert model.enabled is False
assert model.pricing.prompt == 0.0
assert model.pricing.completion == 0.0
assert any(
"omni-moderation-latest" in rec.getMessage()
for rec in caplog.records
if rec.levelno >= logging.WARNING
)
# ---------------------------------------------------------------------------
# OpenRouter fallback — litellm misses, OR carries a full payload
# ---------------------------------------------------------------------------