fix(cost): mark usage.cost litellm wrote as its own bill and keep a breakdown for a reported cost

This commit is contained in:
devin-ai-integration[bot] 2026-10-29 20:28:05 +00:00
parent 1a4277a7e5
commit b324599f0d
6 changed files with 40 additions and 16 deletions

View file

@ -2334,6 +2334,14 @@ def response_cost_calculator(
)
provider_response_cost: Final = get_response_cost_from_hidden_params(hidden_params)
if provider_response_cost is not None and not custom_pricing:
_store_cost_breakdown_in_logging_obj(
litellm_logging_obj=litellm_logging_obj,
prompt_tokens_cost_usd_dollar=0.0,
completion_tokens_cost_usd_dollar=provider_response_cost,
cost_for_built_in_tools_cost_usd_dollar=0.0,
total_cost_usd_dollar=provider_response_cost,
original_cost=provider_response_cost,
)
return provider_response_cost
response_cost = completion_cost(

View file

@ -1020,17 +1020,28 @@ def chunk_usage(chunk: object) -> object:
OWN_REPORTED_COST_READER_PROVIDERS: Final = frozenset({"xai", "perplexity", "opencode_go"})
def _carries_litellm_bill(response: object) -> bool:
LITELLM_BILLED_USAGE_COST: Final = "litellm_billed_usage_cost"
def mark_usage_cost_as_litellm_bill(response: object, bill: float) -> None:
"""Records litellm's bill as the response's ``response_cost`` and notes that ``usage.cost`` now holds that bill,
so ``billed_usage_cost`` does not read it back as the upstream's charge on a later pricing pass."""
hidden: Final[object] = getattr(response, "_hidden_params", None)
if isinstance(hidden, dict):
cast(dict[str, object], hidden).update({"response_cost": bill, LITELLM_BILLED_USAGE_COST: bill})
def _usage_cost_is_litellm_bill(response: object) -> bool:
hidden: Final = as_str_mapping(getattr(response, "_hidden_params", None))
stamped: Final[object] = None if hidden is None else hidden.get("response_cost")
return isinstance(stamped, (int, float)) and not isinstance(stamped, bool)
bill: Final[object] = None if hidden is None else hidden.get(LITELLM_BILLED_USAGE_COST)
usage_cost: Final[object] = getattr(_usage_field(response, "usage"), "cost", None)
return isinstance(bill, (int, float)) and not isinstance(bill, bool) and bill == usage_cost
def billed_usage_cost(response: object, custom_llm_provider: str | None) -> float | None:
"""The charge an OpenAI-compatible upstream reports in ``usage.cost``, skipped once litellm stamped its own bill
onto the response, because the stream builder and the Responses terminal event then rewrite ``usage.cost`` to
that bill."""
if custom_llm_provider in OWN_REPORTED_COST_READER_PROVIDERS or _carries_litellm_bill(response):
"""The charge an OpenAI-compatible upstream reports in ``usage.cost``, skipped for providers with their own reader
and once litellm rewrote ``usage.cost`` to its own bill (the stream builder and the Responses terminal event do)."""
if custom_llm_provider in OWN_REPORTED_COST_READER_PROVIDERS or _usage_cost_is_litellm_bill(response):
return None
cost: Final = provider_reported_usage_cost(_usage_field(response, "usage"), custom_llm_provider)
return cost if cost is not None and math.isfinite(cost) and cost >= 0 else None

View file

@ -15,6 +15,7 @@ from litellm._logging import verbose_logger
from litellm.litellm_core_utils.core_helpers import (
OWN_REPORTED_COST_READER_PROVIDERS,
chunk_usage,
mark_usage_cost_as_litellm_bill,
provider_reported_usage_cost,
)
from litellm.litellm_core_utils.hidden_params import HIDDEN_PARAMS_ATTR, set_hidden_params
@ -1428,6 +1429,7 @@ def _stamp_streaming_usage_cost(usage: Usage, response: ModelResponse, logging_o
return
setattr(usage, "cost", computed_cost)
_set_hidden_response_cost(response, float(computed_cost))
mark_usage_cost_as_litellm_bill(response, float(computed_cost))
def _field_of(value: object, name: str) -> object:

View file

@ -40,6 +40,7 @@ from litellm.constants import (
from litellm.exceptions import MidStreamFallbackError, RateLimitError
from litellm.litellm_core_utils.asyncify import run_async_function
from litellm.litellm_core_utils.core_helpers import (
mark_usage_cost_as_litellm_bill,
process_response_headers,
provider_reported_usage_cost,
set_response_cost_in_hidden_params,
@ -1870,7 +1871,7 @@ def stamp_responses_usage_cost(
return
if bill > 0 or isinstance(getattr(usage_obj, "cost", None), (int, float)):
setattr(usage_obj, "cost", float(bill))
response_obj.hidden_params = {**response_obj.hidden_params, "response_cost": float(bill)}
mark_usage_cost_as_litellm_bill(response_obj, float(bill))
def opening_item_payload(payload: Mapping[str, object]) -> Mapping[str, object]:

View file

@ -808,7 +808,7 @@ class TestReceiveStampSurvivesASecondMetadataBucket:
def test_billed_usage_cost_bills_any_chat_upstream_without_its_own_reader() -> None:
from types import SimpleNamespace
from litellm.litellm_core_utils.core_helpers import billed_usage_cost
from litellm.litellm_core_utils.core_helpers import billed_usage_cost, mark_usage_cost_as_litellm_bill
from litellm.types.utils import ModelResponse, Usage
usage = Usage(prompt_tokens=100, completion_tokens=20, total_tokens=120, cost=0.037)
@ -817,6 +817,8 @@ def test_billed_usage_cost_bills_any_chat_upstream_without_its_own_reader() -> N
assert billed_usage_cost(chat, "openrouter") == 0.037
assert billed_usage_cost(chat, "xai") is None
assert billed_usage_cost(SimpleNamespace(usage=usage), "openai") == 0.037
stamped = ModelResponse(usage=usage)
stamped._hidden_params["response_cost"] = 0.037
assert billed_usage_cost(stamped, "openai") is None
priced_by_litellm = ModelResponse(usage=usage)
priced_by_litellm._hidden_params["response_cost"] = 0.037
assert billed_usage_cost(priced_by_litellm, "openai") == 0.037
mark_usage_cost_as_litellm_bill(priced_by_litellm, 0.037)
assert billed_usage_cost(priced_by_litellm, "openai") is None

View file

@ -254,7 +254,7 @@ def test_interaction_on_kyma_is_billed_at_the_cost_kyma_reports():
@pytest.mark.parametrize("model", ("openrouter/openai/gpt-4o-mini", "deepseek/deepseek-chat"))
def test_interaction_on_another_provider_keeps_its_upstream_cost_out_of_the_body(model: str):
def test_interaction_on_another_openai_compatible_provider_is_billed_at_its_reported_cost(model: str):
chat_usage: Final = {"prompt_tokens": 4, "completion_tokens": 3, "total_tokens": 7, "cost": 0.00721}
responses_body: Final = {
"id": "resp_upstream",
@ -278,7 +278,7 @@ def test_interaction_on_another_provider_keeps_its_upstream_cost_out_of_the_body
upstream.route(path__regex=r".*/responses$").respond(200, json=responses_body)
interaction: Final = litellm.interactions.create(model=model, input="ping", api_key="k")
assert "cost" not in interaction.model_dump(exclude_none=True)["usage"]
assert interaction.model_dump(exclude_none=True)["usage"]["cost"] == pytest.approx(0.00721)
def test_sdk_completion_cost_prices_kyma_at_the_cost_kyma_reports():
@ -410,8 +410,8 @@ def test_cost_headers_show_the_discount_and_margin_taken_from_the_cost_kyma_repo
assert float(headers["x-litellm-response-cost-discount-amount"] or "nan") == pytest.approx(0.004)
assert float(headers["x-litellm-response-cost-margin-amount"] or "nan") == pytest.approx(0.0004)
assert float(headers["x-litellm-response-cost"] or "nan") == pytest.approx(0.0044)
assert headers["x-litellm-response-cost-input"] is None
assert headers["x-litellm-response-cost-output"] is None
assert float(headers["x-litellm-response-cost-input"] or "nan") == 0.0
assert float(headers["x-litellm-response-cost-output"] or "nan") == pytest.approx(0.0044)
@pytest.mark.parametrize("thinking", ({"type": "enabled", "budget_tokens": 1024}, {"type": "adaptive"}))