fix(cost): stamp the bill on a plain reply only where the upstream reported usage.cost

A reply whose upstream sent no usage.cost keeps its usage untouched, as on main, so SDK callers comparing
the usage dict to the upstream's see no added key and logging payloads gain no additional_usage_values.cost
This commit is contained in:
devin-ai-integration[bot] 2026-10-27 01:46:44 +00:00
parent a20609a6f2
commit e4b9584ea6
2 changed files with 30 additions and 2 deletions

View file

@ -185,11 +185,12 @@ class ResponseMetadata:
self._update_hidden_params(new_params)
def _stamp_billed_usage_cost(self, response_cost: object) -> None:
"""A client reads what litellm billed from usage.cost, streamed or not, priced deployment or not."""
"""A usage.cost the upstream reported becomes what litellm billed, so a client and the spend log agree on a
priced deployment; a reply without one keeps its usage as the upstream sent it."""
if not isinstance(self.result, (ModelResponse, ResponsesAPIResponse)):
return
usage: Final = getattr(self.result, "usage", None)
if not isinstance(usage, (Usage, ResponseAPIUsage)):
if not isinstance(usage, (Usage, ResponseAPIUsage)) or getattr(usage, "cost", None) is None:
return
stamp_billable_usage_cost(self.result, usage, response_cost)

View file

@ -10787,6 +10787,33 @@ def test_the_client_reads_what_litellm_billed_from_usage_cost_on_every_route(
assert logging_obj.response_cost_calculator(result=response, ignore_stamped_cost=True) == pytest.approx(bill)
def test_a_plain_reply_without_an_upstream_usage_cost_keeps_none_after_pricing(monkeypatch: pytest.MonkeyPatch) -> None:
"""Clients comparing the usage dict to the upstream's byte for byte must not find a cost litellm added."""
monkeypatch.setitem(
litellm.model_cost,
_PRICED_DEPLOYMENT_ID,
{"litellm_provider": "openai", "mode": "chat", "input_cost_per_token": 2e-6, "output_cost_per_token": 6e-6},
)
response: Final = ModelResponse(
id="chatcmpl-unreported",
model="reported-cost-fixture",
choices=[Choices(index=0, message=Message(role="assistant", content="hi"), finish_reason="stop")],
usage=Usage(prompt_tokens=100, completion_tokens=20, total_tokens=120),
)
update_response_metadata(
result=response,
logging_obj=_priced_logging(False, "completion"),
model=_REPORTED_COST_FIXTURE_MODEL,
kwargs={},
start_time=datetime.datetime(2026, 1, 1),
end_time=datetime.datetime(2026, 1, 1, 0, 0, 1),
)
assert response.usage is not None and getattr(response.usage, "cost", None) is None
assert response._hidden_params["response_cost"] == pytest.approx(100 * 2e-6 + 20 * 6e-6)
def test_provider_reported_usage_cost_splits_input_and_output_by_the_token_math_ratio(
monkeypatch: pytest.MonkeyPatch,
) -> None: