fix(responses): price the completed response, not the terminal event, in post-success hooks

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
kerry 2026-09-22 01:05:48 +00:00
parent 275c0c4d96
commit c798ef6d03
2 changed files with 46 additions and 1 deletions

View file

@ -801,8 +801,13 @@ class BaseResponsesAPIStreamingIterator:
request_payload["litellm_params"] = {}
try:
response_obj: Final = self._get_completed_response_object()
update_response_metadata(
result=self.completed_response,
result=(
type(response_obj).model_validate(response_obj.model_dump())
if response_obj is not None
else self.completed_response
),
logging_obj=self.logging_obj,
model=self.model,
kwargs=request_payload,

View file

@ -342,6 +342,46 @@ def test_run_post_success_hooks_does_not_report_generation_time_as_overhead():
assert "litellm_overhead_time_ms" not in iterator.completed_response._hidden_params
def test_run_post_success_hooks_prices_the_completed_response_not_the_event():
"""The terminal ResponseCompletedEvent carries no usage, so pricing it recomputes
cost as 0 and clobbers the cost_breakdown the stream already stored."""
inner_response: Final = ResponsesAPIResponse(
id="resp_pricing",
created_at=0,
status="completed",
model="gpt-4o-mini",
object="response",
output=[],
usage=ResponseAPIUsage(input_tokens=1840, output_tokens=412, total_tokens=2252),
)
event: Final = ResponseCompletedEvent(
type=ResponsesAPIStreamEvents.RESPONSE_COMPLETED,
response=inner_response,
)
logging_obj = _logging_obj_stub()
priced_results: Final[list[object]] = []
def _cost_calculator(*, result, **_kwargs):
priced_results.append(result)
return 0.0156
logging_obj._response_cost_calculator.side_effect = _cost_calculator
iterator = _make_iterator(sse_events=[], logging_obj=logging_obj)
iterator.completed_response = event
iterator.start_time = datetime(2025, 1, 1, 0, 0, 0)
iterator._run_post_success_hooks(datetime(2025, 1, 1, 0, 0, 10))
assert priced_results and all(
isinstance(result, ResponsesAPIResponse) and result is not event and result.usage is not None
for result in priced_results
)
assert priced_results[0]._hidden_params["response_cost"] == 0.0156
assert event.response._hidden_params == {}
def _mock_config_with_completed_response(response: ResponsesAPIResponse) -> Mock:
mock_config = Mock(spec=BaseResponsesAPIConfig)