diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 26977e64e34..ea383ef4c11 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -79845,5 +79845,27 @@ "supports_tool_choice": true, "supports_vision": false, "supports_web_search": false + }, + "azure_ai/kimi-k2-thinking": { + "input_cost_per_token": 6e-07, + "litellm_provider": "azure_ai", + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "max_tokens": 262144, + "mode": "chat", + "output_cost_per_token": 2.5e-06, + "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/kimi/", + "supported_modalities": [ + "text" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": false, + "supports_reasoning": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": false } } diff --git a/litellm/proxy/db/log_db_metrics.py b/litellm/proxy/db/log_db_metrics.py index 5895610dadf..988f93fb962 100644 --- a/litellm/proxy/db/log_db_metrics.py +++ b/litellm/proxy/db/log_db_metrics.py @@ -13,6 +13,7 @@ from functools import wraps from types import MappingProxyType from typing import Final +from litellm._logging import verbose_proxy_logger from litellm._service_logger import ServiceTypes _PRISMA_CLIENT_CRUD: Final = frozenset({"get_data", "update_data", "delete_data"}) @@ -200,14 +201,17 @@ async def _handle_logging_db_exception( if _is_exception_related_to_db(e) is not True: return False - await proxy_logging_obj.service_logging_obj.async_service_failure_hook( - error=e, - service=ServiceTypes.DB, - call_type=func.__name__, - parent_otel_span=kwargs.get("parent_otel_span"), - duration=(end_time - start_time).total_seconds(), - start_time=start_time, - end_time=end_time, - event_metadata=metadata_of(kwargs), - ) + try: + await proxy_logging_obj.service_logging_obj.async_service_failure_hook( + error=e, + service=ServiceTypes.DB, + call_type=func.__name__, + parent_otel_span=kwargs.get("parent_otel_span"), + duration=(end_time - start_time).total_seconds(), + start_time=start_time, + end_time=end_time, + event_metadata=metadata_of(kwargs), + ) + except Exception as hook_error: + verbose_proxy_logger.debug("log_db_metrics: failure hook raised for %s: %s", func.__name__, hook_error) return True diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 26977e64e34..ea383ef4c11 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -79845,5 +79845,27 @@ "supports_tool_choice": true, "supports_vision": false, "supports_web_search": false + }, + "azure_ai/kimi-k2-thinking": { + "input_cost_per_token": 6e-07, + "litellm_provider": "azure_ai", + "max_input_tokens": 262144, + "max_output_tokens": 262144, + "max_tokens": 262144, + "mode": "chat", + "output_cost_per_token": 2.5e-06, + "source": "https://azure.microsoft.com/en-us/pricing/details/ai-foundry-models/kimi/", + "supported_modalities": [ + "text" + ], + "supported_output_modalities": [ + "text" + ], + "supports_function_calling": true, + "supports_prompt_caching": false, + "supports_reasoning": true, + "supports_tool_choice": true, + "supports_video_input": false, + "supports_vision": false } } diff --git a/tests/unit/proxy/db/test_log_db_metrics.py b/tests/unit/proxy/db/test_log_db_metrics.py index 8aa8d0dabc8..658e5e8f534 100644 --- a/tests/unit/proxy/db/test_log_db_metrics.py +++ b/tests/unit/proxy/db/test_log_db_metrics.py @@ -5,6 +5,7 @@ from typing import Final from unittest.mock import AsyncMock, MagicMock, patch import pytest +from prisma.errors import PrismaError from litellm._service_logger import ServiceTypes from litellm.proxy.db.db_lookup_gate import bounded_db_lookup @@ -27,6 +28,16 @@ def success_hook() -> Iterator[AsyncMock]: yield hook +@pytest.fixture +def failure_hook() -> Iterator[AsyncMock]: + hook: Final = AsyncMock() + with patch( + "litellm.proxy.proxy_server.proxy_logging_obj", + MagicMock(service_logging_obj=MagicMock(async_service_failure_hook=hook)), + ): + yield hook + + async def _db_call_types(hook: AsyncMock) -> tuple[str, ...]: await asyncio.sleep(0) return tuple(call.kwargs["call_type"] for call in hook.await_args_list if call.kwargs["service"] == ServiceTypes.DB) @@ -248,3 +259,18 @@ async def test_a_task_spawned_by_a_decorated_call_that_queries_after_it_returned await background assert await _db_call_types(success_hook) == ("read_key_row", "find_unique") + + +@pytest.mark.asyncio +async def test_a_raising_failure_hook_never_replaces_the_prisma_error(failure_hook: AsyncMock) -> None: + failure_hook.side_effect = RuntimeError("exporter down") + + @log_db_metrics + async def insert_data(**kwargs: object) -> None: + raise PrismaError("connection reset") + + with pytest.raises(PrismaError, match="connection reset"): + await insert_data(table_name="key") + + assert failure_hook.await_count == 1 + assert failure_hook.await_args_list[0].kwargs["call_type"] == "insert_data"