From 3b1ebbc7cb9b6431f38b534c439f97d1cfce691c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:55:34 +0000 Subject: [PATCH] fix(responses): warn when spend-log session query hits row cap --- .../session_handler.py | 9 +++++ .../test_session_handler.py | 37 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/litellm/responses/litellm_completion_transformation/session_handler.py b/litellm/responses/litellm_completion_transformation/session_handler.py index af341ed9c09..e9b0450bcc5 100644 --- a/litellm/responses/litellm_completion_transformation/session_handler.py +++ b/litellm/responses/litellm_completion_transformation/session_handler.py @@ -295,6 +295,15 @@ class ResponsesSessionHandler: query, previous_response_id, DEFAULT_MAX_SPEND_LOGS_PER_RESPONSES_SESSION ) + if len(spend_logs) >= DEFAULT_MAX_SPEND_LOGS_PER_RESPONSES_SESSION: + verbose_proxy_logger.warning( + "Responses session for previous_response_id=%s hit the %d-row cap " + "(DEFAULT_MAX_SPEND_LOGS_PER_RESPONSES_SESSION); the oldest turns were dropped. " + "Raise the limit if you need more history.", + previous_response_id, + DEFAULT_MAX_SPEND_LOGS_PER_RESPONSES_SESSION, + ) + verbose_proxy_logger.debug( "Found the following spend logs for previous response id %s: %s", previous_response_id, diff --git a/tests/test_litellm/responses/litellm_completion_transformation/test_session_handler.py b/tests/test_litellm/responses/litellm_completion_transformation/test_session_handler.py index 9cfc8ac2e4c..dd26dfc93e7 100644 --- a/tests/test_litellm/responses/litellm_completion_transformation/test_session_handler.py +++ b/tests/test_litellm/responses/litellm_completion_transformation/test_session_handler.py @@ -491,3 +491,40 @@ async def test_get_all_spend_logs_returns_most_recent_in_ascending_order(): executed_query = mock_prisma_client.db.query_raw.await_args.args[0] assert 'ORDER BY "endTime" DESC' in executed_query assert executed_query.rstrip().rstrip(";").endswith('ORDER BY "endTime" ASC') + + +@pytest.mark.asyncio +async def test_get_all_spend_logs_warns_when_cap_is_hit(): + """ + Operators need a runtime signal when a session is truncated: when the query returns + the full cap of rows (older turns silently dropped), a warning must be emitted. + """ + cap = litellm.constants.DEFAULT_MAX_SPEND_LOGS_PER_RESPONSES_SESSION + mock_prisma_client = AsyncMock() + mock_prisma_client.db.query_raw = AsyncMock(return_value=[{} for _ in range(cap)]) + + with _patched_prisma_client(mock_prisma_client): + with patch.object(session_handler.verbose_proxy_logger, "warning") as mock_warning: + await ResponsesSessionHandler.get_all_spend_logs_for_previous_response_id( + "resp_previous_id" + ) + + mock_warning.assert_called_once() + + +@pytest.mark.asyncio +async def test_get_all_spend_logs_does_not_warn_below_cap(): + """ + Below the cap nothing is truncated, so no truncation warning should be emitted. + """ + cap = litellm.constants.DEFAULT_MAX_SPEND_LOGS_PER_RESPONSES_SESSION + mock_prisma_client = AsyncMock() + mock_prisma_client.db.query_raw = AsyncMock(return_value=[{} for _ in range(cap - 1)]) + + with _patched_prisma_client(mock_prisma_client): + with patch.object(session_handler.verbose_proxy_logger, "warning") as mock_warning: + await ResponsesSessionHandler.get_all_spend_logs_for_previous_response_id( + "resp_previous_id" + ) + + mock_warning.assert_not_called()