diff --git a/litellm/interactions/background_cost_polling.py b/litellm/interactions/background_cost_polling.py index 5fc2a443ce0..896f5db8d87 100644 --- a/litellm/interactions/background_cost_polling.py +++ b/litellm/interactions/background_cost_polling.py @@ -211,7 +211,6 @@ _CARRIED_METADATA_KEYS: Final = frozenset( "spend_logs_metadata", "requester_metadata", "requester_ip_address", - "requester_custom_headers", "user_agent", "agent_id", "session_id", diff --git a/litellm/proxy/spend_tracking/background_interaction_settlement.py b/litellm/proxy/spend_tracking/background_interaction_settlement.py index 5d8a0bdfa3f..096b3d46ab5 100644 --- a/litellm/proxy/spend_tracking/background_interaction_settlement.py +++ b/litellm/proxy/spend_tracking/background_interaction_settlement.py @@ -169,7 +169,13 @@ async def configure_background_interaction_settlement( async def install_background_interaction_settlement(prisma_client: "PrismaClient") -> None: - await configure_background_interaction_settlement( - table=BackgroundInteractionSettlementRepository(prisma_client).table, - claimed_by=f"{socket.gethostname()}:{os.getpid()}", - ) + try: + await configure_background_interaction_settlement( + table=BackgroundInteractionSettlementRepository(prisma_client).table, + claimed_by=f"{socket.gethostname()}:{os.getpid()}", + ) + except Exception as e: # noqa: BLE001 # a boot step must survive any DB error; billing then settles in-process as before + verbose_proxy_logger.warning( + "Durable background interaction settlement is off on this replica, so billing settles in-process only: %s", + e, + ) diff --git a/tests/test_litellm/interactions/test_background_cost_polling.py b/tests/test_litellm/interactions/test_background_cost_polling.py index 15943d1e695..d9da9b50790 100644 --- a/tests/test_litellm/interactions/test_background_cost_polling.py +++ b/tests/test_litellm/interactions/test_background_cost_polling.py @@ -682,6 +682,23 @@ async def test_delete_settles_once_however_many_replicas_try(): assert second_calls == [] +def test_create_context_carries_no_request_headers(): + logging_obj = _logging_obj( + litellm_params={ + "metadata": _create_metadata( + requester_custom_headers={"x-api-key": "sk-customer-secret"}, + proxy_server_request={"headers": {"x-api-key": "sk-customer-secret"}}, + ) + } + ) + + carried = _create_context(logging_obj, "gemini").metadata + + assert carried["user_api_key_team_id"] == "team-1" + assert "requester_custom_headers" not in carried + assert "proxy_server_request" not in carried + + @pytest.mark.asyncio async def test_restart_resumes_only_the_rows_no_replica_claimed(): store = InMemoryBackgroundSettlementStore() diff --git a/tests/test_litellm/proxy/spend_tracking/test_background_interaction_settlement.py b/tests/test_litellm/proxy/spend_tracking/test_background_interaction_settlement.py index 38963f58ad2..fe59d20dec8 100644 --- a/tests/test_litellm/proxy/spend_tracking/test_background_interaction_settlement.py +++ b/tests/test_litellm/proxy/spend_tracking/test_background_interaction_settlement.py @@ -17,6 +17,7 @@ from litellm.interactions.background_cost_polling import ( from litellm.litellm_core_utils.litellm_logging import Logging as LitellmLogging from litellm.proxy.spend_tracking.background_interaction_settlement import ( configure_background_interaction_settlement, + install_background_interaction_settlement, PrismaBackgroundSettlementStore, ) from litellm.types.interactions import InteractionsAPIResponse @@ -233,6 +234,19 @@ async def test_configure_installs_the_store_and_resumes_the_orphaned_rows(): configure_background_settlement_store(previous_store) +class _PrismaClientWithoutSettlementTable: + pass + + +@pytest.mark.asyncio +async def test_install_keeps_booting_when_the_settlement_table_is_unreachable(): + previous_store = bg._STORE.store + + await install_background_interaction_settlement(_PrismaClientWithoutSettlementTable()) + + assert bg._STORE.store is previous_store + + @dataclass(frozen=True) class _JsonLike: data: object