Add error_message search in spend logs endpoint

This commit is contained in:
yuneng-jiang
2026-01-28 15:06:31 -08:00
parent e444199d95
commit cb8ead6013
2 changed files with 92 additions and 0 deletions
@@ -1681,6 +1681,9 @@ async def ui_view_spend_logs( # noqa: PLR0915
error_code: Optional[str] = fastapi.Query(
default=None, description="Filter logs by error code (e.g., '404', '500')"
),
error_message: Optional[str] = fastapi.Query(
default=None, description="Filter logs by error message (partial string match)"
),
):
"""
View spend logs with pagination support.
@@ -1774,6 +1777,12 @@ async def ui_view_spend_logs( # noqa: PLR0915
"equals": f'"{error_code}"',
})
if error_message is not None:
metadata_filters.append({
"path": ["error_information", "error_message"],
"string_contains": error_message,
})
if metadata_filters:
if len(metadata_filters) == 1:
where_conditions["metadata"] = metadata_filters[0]
@@ -1952,6 +1952,89 @@ async def test_ui_view_spend_logs_with_error_code(client):
assert metadata["error_information"]["error_code"] == "404"
@pytest.mark.asyncio
async def test_ui_view_spend_logs_with_error_message(client):
"""Test filtering spend logs by error message"""
mock_spend_logs = [
{
"id": "log1",
"request_id": "req1",
"api_key": "sk-test-key",
"user": "test_user_1",
"team_id": "team1",
"spend": 0.05,
"startTime": datetime.datetime.now(timezone.utc).isoformat(),
"model": "gpt-3.5-turbo",
"metadata": '{"error_information": {"error_message": "Rate limit exceeded"}}',
},
{
"id": "log2",
"request_id": "req2",
"api_key": "sk-test-key",
"user": "test_user_2",
"team_id": "team1",
"spend": 0.10,
"startTime": datetime.datetime.now(timezone.utc).isoformat(),
"model": "gpt-4",
"metadata": '{"error_information": {"error_message": "Invalid API key"}}',
},
]
with patch.object(ps, "prisma_client") as mock_prisma:
# Mock the find_many method to return filtered results
async def mock_find_many(*args, **kwargs):
where_conditions = kwargs.get("where", {})
if "metadata" in where_conditions:
metadata_filter = where_conditions["metadata"]
if metadata_filter.get("path") == ["error_information", "error_message"]:
error_message_filter = metadata_filter.get("string_contains")
# Check if the error message contains the filter string
if error_message_filter == "Rate limit":
return [mock_spend_logs[0]]
elif error_message_filter == "Invalid API":
return [mock_spend_logs[1]]
return mock_spend_logs
async def mock_count(*args, **kwargs):
where_conditions = kwargs.get("where", {})
if "metadata" in where_conditions:
metadata_filter = where_conditions["metadata"]
if metadata_filter.get("path") == ["error_information", "error_message"]:
error_message_filter = metadata_filter.get("string_contains")
if error_message_filter == "Rate limit":
return 1
elif error_message_filter == "Invalid API":
return 1
return len(mock_spend_logs)
mock_prisma.db.litellm_spendlogs.find_many = mock_find_many
mock_prisma.db.litellm_spendlogs.count = mock_count
start_date = (
datetime.datetime.now(timezone.utc) - datetime.timedelta(days=7)
).strftime("%Y-%m-%d %H:%M:%S")
end_date = datetime.datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
response = client.get(
"/spend/logs/ui",
params={
"error_message": "Rate limit",
"start_date": start_date,
"end_date": end_date,
},
headers={"Authorization": "Bearer sk-test"},
)
assert response.status_code == 200
data = response.json()
assert data["total"] == 1
assert len(data["data"]) == 1
assert data["data"][0]["id"] == "log1"
metadata = json.loads(data["data"][0]["metadata"])
assert "error_information" in metadata
assert "Rate limit exceeded" in metadata["error_information"]["error_message"]
@pytest.mark.asyncio
async def test_ui_view_spend_logs_with_error_code_and_key_alias(client):
"""Test merging error_code and key_alias filters with AND logic"""