From 101845007fb59e6d74bbd12f6408526e8b16cb4f Mon Sep 17 00:00:00 2001 From: Yucheng He Date: Tue, 15 Sep 2026 13:05:18 -0700 Subject: [PATCH] fix(spend_tracking): re-key the create calls that mint a response id too Interaction, video, container, code, MCP tool and A2A calls are charged per call and keyed on the id the provider minted for that call, the same shape as a chat completion, so a reused id would drop their rows as well. List them with the inference calls. --- litellm/proxy/utils.py | 28 ++++++++++++++++--- .../test_proxy_update_spend.py | 12 +++++++- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/litellm/proxy/utils.py b/litellm/proxy/utils.py index 8060698e4be..7c376cabb03 100644 --- a/litellm/proxy/utils.py +++ b/litellm/proxy/utils.py @@ -7268,15 +7268,35 @@ _RESPONSE_ID_KEYED_SPEND_LOG_CALL_TYPES: Final = frozenset( CallTypes.aocr.value, CallTypes.vector_store_search.value, CallTypes.avector_store_search.value, + CallTypes.create_interaction.value, + CallTypes.acreate_interaction.value, + CallTypes.create_video.value, + CallTypes.acreate_video.value, + CallTypes.video_remix.value, + CallTypes.avideo_remix.value, + CallTypes.video_edit.value, + CallTypes.avideo_edit.value, + CallTypes.video_extension.value, + CallTypes.avideo_extension.value, + CallTypes.create_container.value, + CallTypes.acreate_container.value, + CallTypes.run_code.value, + CallTypes.arun_code.value, + CallTypes.code_interpreter_tool.value, + CallTypes.acode_interpreter_tool.value, + CallTypes.call_mcp_tool.value, + CallTypes.send_message.value, + CallTypes.asend_message.value, CallTypes.pass_through.value, CallTypes.llm_passthrough_route.value, CallTypes.allm_passthrough_route.value, } ) -"""The inference calls, whose provider mints a response id per call: a stored row with the same -``request_id`` is another request the provider gave the same id. Every other call type (object -creates, reads, polls and management calls, batch cost claims) is keyed on the id of the object -it addressed, and a second row for one of those collapses on purpose.""" +"""The inference and create calls, whose provider mints a response id per call: a stored row +with the same ``request_id`` is another request the provider gave the same id. Every other call +type (object reads, polls, cancels, lists and deletes, batch cost claims) is keyed on the id of the +object it addressed, and a second row for one of those collapses on purpose. Realtime and +Responses websocket sessions carry no provider id and are keyed on the call id already.""" def _is_transient_spend_log_write_error(e: Exception) -> bool: diff --git a/tests/test_litellm/proxy/utils/prisma_and_spend/test_proxy_update_spend.py b/tests/test_litellm/proxy/utils/prisma_and_spend/test_proxy_update_spend.py index 639d9043111..8cef2afe788 100644 --- a/tests/test_litellm/proxy/utils/prisma_and_spend/test_proxy_update_spend.py +++ b/tests/test_litellm/proxy/utils/prisma_and_spend/test_proxy_update_spend.py @@ -987,7 +987,17 @@ async def test_update_spend_logs_rekeys_the_rows_a_reused_provider_response_id_w @pytest.mark.asyncio @pytest.mark.parametrize( "call_type", - ["acompletion", "atext_completion", "aembedding", "aresponses", "aanthropic_messages", "allm_passthrough_route"], + [ + "acompletion", + "atext_completion", + "aembedding", + "aresponses", + "aanthropic_messages", + "acreate_interaction", + "acreate_video", + "call_mcp_tool", + "allm_passthrough_route", + ], ) async def test_update_spend_logs_rekeys_every_inference_call_type( mock_prisma_client: MagicMock, make_spend_log_row: SpendLogRowFactory, call_type: str