fix(pricing): treat negative catalog pricing sentinels as unknown

Port from Kilo-Org/kilocode#13040: OpenRouter and OpenAI-compatible
model catalogs publish negative pricing sentinels ("-1") for
dynamically priced models such as openrouter/auto. The sentinel flowed
straight through _pricing_entry_from_metadata into PricingEntry as
-$1,000,000/M and produced large negative session costs in the usage
ledger and cost displays.

Negative pricing fields now degrade to None (unknown) via
_to_nonnegative_decimal; an all-sentinel pricing block yields no
pricing entry at all so downstream cost falls back to official-docs
pricing or 'unknown'. Zero (free models) is unaffected.
This commit is contained in:
Teknium 2026-08-10 17:22:09 -07:00
parent e5e2fb8b2d
commit 444c292f9c
No known key found for this signature in database
2 changed files with 91 additions and 5 deletions

View File

@ -980,6 +980,23 @@ def _to_decimal(value: Any) -> Optional[Decimal]:
return None
def _to_nonnegative_decimal(value: Any) -> Optional[Decimal]:
"""Parse a catalog pricing field, treating negative values as unknown.
Dynamic model catalogs use negative sentinels for "pricing varies":
OpenRouter publishes ``"-1"`` for auto-routed models (``openrouter/auto``),
and OpenAI-compatible endpoints mirror that convention. Multiplying a
sentinel through the usage ledger produces large NEGATIVE session costs
(-$1,000,000/M input on the auto router), so an unpriceable field must
degrade to "unknown" (None), never a signed price.
(Port of Kilo-Org/kilocode#13040.)
"""
parsed = _to_decimal(value)
if parsed is None or parsed < 0:
return None
return parsed
def _to_int(value: Any) -> int:
try:
return int(value or 0)
@ -1138,15 +1155,17 @@ def _pricing_entry_from_metadata(
if model_id not in metadata:
return None
pricing = metadata[model_id].get("pricing") or {}
prompt = _to_decimal(pricing.get("prompt"))
completion = _to_decimal(pricing.get("completion"))
request = _to_decimal(pricing.get("request"))
cache_read = _to_decimal(
# Negative values are dynamic-pricing sentinels (e.g. OpenRouter's "-1"
# on openrouter/auto), not real prices — treat them as unknown.
prompt = _to_nonnegative_decimal(pricing.get("prompt"))
completion = _to_nonnegative_decimal(pricing.get("completion"))
request = _to_nonnegative_decimal(pricing.get("request"))
cache_read = _to_nonnegative_decimal(
pricing.get("cache_read")
or pricing.get("cached_prompt")
or pricing.get("input_cache_read")
)
cache_write = _to_decimal(
cache_write = _to_nonnegative_decimal(
pricing.get("cache_write")
or pricing.get("cache_creation")
or pricing.get("input_cache_write")

View File

@ -312,3 +312,70 @@ def test_vertex_default_model_estimates_cached_usage(monkeypatch):
assert result.status == "estimated"
assert result.amount_usd is not None and result.amount_usd > 0
def test_negative_pricing_sentinels_are_treated_as_unknown():
"""OpenRouter publishes "-1" pricing sentinels for dynamically priced
models (e.g. openrouter/auto). Before this fix the sentinel flowed
straight into PricingEntry as -$1,000,000/M and produced large negative
session costs. Negative fields must degrade to None (unknown), and an
all-sentinel pricing block must yield no entry at all.
(Port of Kilo-Org/kilocode#13040.)
"""
from agent.usage_pricing import _pricing_entry_from_metadata
all_sentinel = {
"openrouter/auto": {"pricing": {"prompt": "-1", "completion": "-1"}}
}
assert (
_pricing_entry_from_metadata(
all_sentinel,
"openrouter/auto",
source_url="https://example.test/models",
pricing_version="test",
)
is None
)
def test_negative_pricing_fields_degrade_individually():
"""A mixed pricing block keeps valid non-negative fields and drops only
the negative ones (including cache fields)."""
from decimal import Decimal
from agent.usage_pricing import _pricing_entry_from_metadata
mixed = {
"m": {
"pricing": {
"prompt": "-1",
"completion": "0.000002",
"cache_read": "-0.5",
"request": "-1",
}
}
}
entry = _pricing_entry_from_metadata(
mixed, "m", source_url="https://example.test/models", pricing_version="test"
)
assert entry is not None
assert entry.input_cost_per_million is None
assert entry.output_cost_per_million == Decimal("2.000000")
assert entry.cache_read_cost_per_million is None
assert entry.request_cost is None
def test_zero_pricing_still_produces_entry():
"""Zero is a legitimate price (free models) and must NOT be dropped by
the negative-sentinel guard."""
from decimal import Decimal
from agent.usage_pricing import _pricing_entry_from_metadata
free = {"f": {"pricing": {"prompt": "0", "completion": "0"}}}
entry = _pricing_entry_from_metadata(
free, "f", source_url="https://example.test/models", pricing_version="test"
)
assert entry is not None
assert entry.input_cost_per_million == Decimal("0")
assert entry.output_cost_per_million == Decimal("0")