From acbe9731067bc1008b519bcebec2154b65fd0496 Mon Sep 17 00:00:00 2001 From: Sebastian Curland Date: Sun, 1 Nov 2026 17:38:08 +0200 Subject: [PATCH] fix(proxy): attribute /v1/messages spend rows to /messages in daily activity Anthropic-native /v1/messages requests are logged with call type anthropic_messages, which had no entry in ROUTE_ENDPOINT_MAPPING. Their daily spend rows were written with no endpoint, so Usage > Endpoint Activity never showed /messages traffic even though model, key and provider views counted it Map anthropic_messages to /messages. The missing-param error for the route now names /messages instead of the raw route type, matching the other mapped routes Co-Authored-By: Claude Opus 5.5 --- litellm/proxy/route_llm_request.py | 1 + .../proxy/db/test_db_spend_update_writer.py | 39 +++++++++++++++++++ tests/unit/proxy/test_route_llm_request.py | 2 +- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/litellm/proxy/route_llm_request.py b/litellm/proxy/route_llm_request.py index ecc31f3b0a7..6e188f40024 100644 --- a/litellm/proxy/route_llm_request.py +++ b/litellm/proxy/route_llm_request.py @@ -155,6 +155,7 @@ ROUTE_ENDPOINT_MAPPING: Final = { "arerank": "/rerank", "aresponses": "/responses", "_aresponses_websocket": "/responses", + "anthropic_messages": "/messages", "alist_input_items": "/responses/{response_id}/input_items", "aimage_edit": "/images/edits", "acancel_responses": "/responses/{response_id}/cancel", diff --git a/tests/unit/proxy/db/test_db_spend_update_writer.py b/tests/unit/proxy/db/test_db_spend_update_writer.py index 298b153f621..d0648bb5034 100644 --- a/tests/unit/proxy/db/test_db_spend_update_writer.py +++ b/tests/unit/proxy/db/test_db_spend_update_writer.py @@ -1852,6 +1852,45 @@ async def test_endpoint_field_maps_retrieve_batch_spend_row_to_batches_endpoint( assert transaction["endpoint"] == "/batches" +@pytest.mark.asyncio +async def test_endpoint_field_maps_anthropic_messages_spend_row_to_messages_endpoint(): + writer = DBSpendUpdateWriter() + mock_prisma = MagicMock() + mock_prisma.get_request_status = MagicMock(return_value="success") + + payload = { + "request_id": "req-anthropic-messages", + "user": "test-user", + "call_type": "anthropic_messages", + "startTime": "2024-01-01T12:00:00", + "api_key": "test-key", + "model": "claude-sonnet-4-5", + "custom_llm_provider": "anthropic", + "model_group": "claude-sonnet-4-5", + "prompt_tokens": 15, + "completion_tokens": 10, + "spend": 0.0175, + "metadata": '{"usage_object": {}}', + } + + writer.daily_spend_update_queue.add_update = AsyncMock() + + await writer.add_spend_log_transaction_to_daily_user_transaction( + payload=payload, + prisma_client=mock_prisma, + ) + + writer.daily_spend_update_queue.add_update.assert_called_once() + + call_args = writer.daily_spend_update_queue.add_update.call_args[1] + update_dict = call_args["update"] + assert len(update_dict) == 1 + + for key, transaction in update_dict.items(): + assert key == "test-user_2024-01-01_test-key_claude-sonnet-4-5_anthropic_/messages" + assert transaction["endpoint"] == "/messages" + + @pytest.mark.asyncio async def test_update_daily_spend_logs_detailed_error_on_batch_upsert_failure(): """ diff --git a/tests/unit/proxy/test_route_llm_request.py b/tests/unit/proxy/test_route_llm_request.py index 71f816ea183..7c2cdd3031a 100644 --- a/tests/unit/proxy/test_route_llm_request.py +++ b/tests/unit/proxy/test_route_llm_request.py @@ -1485,7 +1485,7 @@ async def test_route_request_keeps_key_fallbacks_out_of_pass_through_provider_bo ("atranscription", "file", "/audio/transcriptions"), ("arerank", "query", "/rerank"), ("acompact_responses", "input", "/responses/compact"), - ("anthropic_messages", "messages", "anthropic_messages"), + ("anthropic_messages", "messages", "/messages"), ("agenerate_content", "contents", "agenerate_content"), ("aocr", "document", "/ocr"), ("avector_store_search", "query", "avector_store_search"),