mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-05 07:37:50 +00:00
fix(cost): keep a $0 stream bill in hidden response_cost so the egress shows it
A free or fully discounted stream used to leave the upstream usage.cost on the client frame while the spend row read 0, because the builder skipped stamping a zero bill Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
e2033586a4
commit
856c22a1cd
3 changed files with 41 additions and 8 deletions
|
|
@ -1415,7 +1415,7 @@ def _stamp_streaming_response_cost(response: ModelResponse, logging_obj: Optiona
|
|||
if logging_obj is None:
|
||||
return
|
||||
computed_cost: Final = logging_obj.response_cost_calculator(result=response)
|
||||
if not isinstance(computed_cost, (int, float)) or computed_cost <= 0:
|
||||
if isinstance(computed_cost, bool) or not isinstance(computed_cost, (int, float)) or computed_cost < 0:
|
||||
return
|
||||
_set_hidden_response_cost(response, float(computed_cost))
|
||||
|
||||
|
|
|
|||
|
|
@ -885,16 +885,29 @@ def test_normalize_responses_usage_never_writes_a_cost_the_provider_did_not_repo
|
|||
def test_normalize_responses_usage_stamps_the_logging_objects_bill_as_hidden_response_cost():
|
||||
from litellm.responses.streaming_iterator import normalize_responses_usage
|
||||
|
||||
response = _responses_api_response_with_usage()
|
||||
logging_obj = MagicMock()
|
||||
logging_obj.response_cost_calculator.return_value = 0.00032
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging
|
||||
|
||||
response: Final = _responses_api_response_with_usage()
|
||||
logging_obj: Final = Logging(
|
||||
model="mantle-claude",
|
||||
messages=[{"role": "user", "content": "q"}],
|
||||
stream=True,
|
||||
call_type="aresponses",
|
||||
start_time=None,
|
||||
litellm_call_id="call",
|
||||
function_id="fn",
|
||||
)
|
||||
logging_obj.update_environment_variables(
|
||||
litellm_params={"custom_llm_provider": "openai", "input_cost_per_token": 2e-6, "output_cost_per_token": 6e-6},
|
||||
optional_params={},
|
||||
)
|
||||
logging_obj.custom_cost_per_token = {"input_cost_per_token": 2e-6, "output_cost_per_token": 6e-6}
|
||||
|
||||
normalize_responses_usage(response, logging_obj)
|
||||
|
||||
hidden: Final = get_hidden_params(response)
|
||||
assert hidden is not None and hidden["response_cost"] == 0.00032
|
||||
assert hidden is not None and hidden["response_cost"] == pytest.approx(20 * 2e-6 + 60 * 6e-6)
|
||||
assert getattr(response.usage, "cost", None) is None, "the client field is written at the proxy egress, not here"
|
||||
assert logging_obj.response_cost_calculator.call_args.kwargs["result"] is response
|
||||
|
||||
|
||||
def _capture_dispatch(logged: list):
|
||||
|
|
|
|||
|
|
@ -4907,6 +4907,26 @@ def test_stream_chunk_builder_bills_a_stream_without_writing_into_its_usage(monk
|
|||
assert response._hidden_params["response_cost"] > 0
|
||||
|
||||
|
||||
def test_stream_chunk_builder_keeps_a_zero_bill_as_hidden_response_cost() -> None:
|
||||
logging_obj: Final = _stream_builder_logging_obj()
|
||||
logging_obj.update_environment_variables(
|
||||
litellm_params={"custom_llm_provider": "openai", "input_cost_per_token": 0.0, "output_cost_per_token": 0.0},
|
||||
optional_params={},
|
||||
)
|
||||
logging_obj.custom_cost_per_token = {"input_cost_per_token": 0.0, "output_cost_per_token": 0.0}
|
||||
chunks: Final = [
|
||||
_stream_builder_text_chunk("gpt-4o", "Hello "),
|
||||
_stream_builder_text_chunk("gpt-4o", "world.", finish_reason="stop"),
|
||||
]
|
||||
|
||||
response: Final = litellm.stream_chunk_builder(
|
||||
chunks=chunks, messages=[{"role": "user", "content": "hi"}], logging_obj=logging_obj
|
||||
)
|
||||
|
||||
assert response is not None
|
||||
assert response._hidden_params["response_cost"] == 0.0, "a free stream still tells the egress what was billed"
|
||||
|
||||
|
||||
def test_stream_chunk_builder_stamped_cost_is_not_re_read_as_xai_reported_cost():
|
||||
import time as time_module
|
||||
|
||||
|
|
@ -4949,7 +4969,7 @@ def test_stream_chunk_builder_stamped_cost_is_not_re_read_as_xai_reported_cost()
|
|||
assert spend_row_cost == pytest.approx(usage_cost)
|
||||
|
||||
|
||||
def test_stream_chunk_builder_skips_stamp_when_cost_is_unpriceable():
|
||||
def test_stream_chunk_builder_stamps_the_zero_bill_when_cost_is_unpriceable():
|
||||
import time as time_module
|
||||
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging
|
||||
|
|
@ -4978,7 +4998,7 @@ def test_stream_chunk_builder_skips_stamp_when_cost_is_unpriceable():
|
|||
|
||||
assert response is not None
|
||||
assert getattr(response.usage, "cost", None) is None
|
||||
assert response._hidden_params.get("response_cost") is None
|
||||
assert response._hidden_params.get("response_cost") == 0.0, "the spend row reads 0, so the egress must too"
|
||||
|
||||
|
||||
def test_stream_chunk_builder_prices_alias_response_at_its_deployment_model():
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue