mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
fix(spend-logs): trim logged response strings (#16654)
* fix(spend-logs): trim logged response strings - route spend-log responses through the existing string sanitizer so oversized base64/text fields are truncated before persistence - add unit tests covering the truncation path and the feature flag Note: embeddings-specific truncation (numeric vectors) is still pending and will be handled separately. * remove unnecessary comment * add: sanitization unit test for embeddings * fix: simplify sanatization logic I overcomplicated a simple change for lack of understanding, fixed.
This commit is contained in:
parent
69fcedb6d9
commit
2e007505da
2 changed files with 82 additions and 2 deletions
|
|
@ -651,7 +651,20 @@ def _get_response_for_spend_logs_payload(
|
|||
if payload is None:
|
||||
return "{}"
|
||||
if _should_store_prompts_and_responses_in_spend_logs():
|
||||
return json.dumps(payload.get("response", {}))
|
||||
response_obj: Any = payload.get("response")
|
||||
if response_obj is None:
|
||||
return "{}"
|
||||
|
||||
sanitized_wrapper = _sanitize_request_body_for_spend_logs_payload(
|
||||
{"response": response_obj}
|
||||
)
|
||||
sanitized_response = sanitized_wrapper.get("response", response_obj)
|
||||
|
||||
if sanitized_response is None:
|
||||
return "{}"
|
||||
if isinstance(sanitized_response, str):
|
||||
return sanitized_response
|
||||
return safe_dumps(sanitized_response)
|
||||
return "{}"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import json
|
|||
import os
|
||||
import sys
|
||||
from datetime import timezone
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
|
@ -19,10 +19,12 @@ import litellm
|
|||
from litellm.constants import LITELLM_TRUNCATED_PAYLOAD_FIELD, REDACTED_BY_LITELM_STRING
|
||||
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
|
||||
from litellm.proxy.spend_tracking.spend_tracking_utils import (
|
||||
_get_response_for_spend_logs_payload,
|
||||
_get_vector_store_request_for_spend_logs_payload,
|
||||
_sanitize_request_body_for_spend_logs_payload,
|
||||
get_logging_payload,
|
||||
)
|
||||
from litellm.types.utils import StandardLoggingPayload
|
||||
|
||||
|
||||
def test_sanitize_request_body_for_spend_logs_payload_basic():
|
||||
|
|
@ -235,6 +237,71 @@ def test_get_vector_store_request_for_spend_logs_payload_null_input(mock_should_
|
|||
assert result is None
|
||||
|
||||
|
||||
@patch(
|
||||
"litellm.proxy.spend_tracking.spend_tracking_utils._should_store_prompts_and_responses_in_spend_logs"
|
||||
)
|
||||
def test_get_response_for_spend_logs_payload_truncates_large_base64(mock_should_store):
|
||||
from litellm.constants import MAX_STRING_LENGTH_PROMPT_IN_DB
|
||||
|
||||
mock_should_store.return_value = True
|
||||
large_text = "A" * (MAX_STRING_LENGTH_PROMPT_IN_DB + 500)
|
||||
payload = cast(
|
||||
StandardLoggingPayload,
|
||||
{
|
||||
"response": {
|
||||
"data": [
|
||||
{
|
||||
"b64_json": large_text,
|
||||
"other_field": "value",
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
response_json = _get_response_for_spend_logs_payload(payload)
|
||||
parsed = json.loads(response_json)
|
||||
truncated_value = parsed["data"][0]["b64_json"]
|
||||
assert len(truncated_value) < len(large_text)
|
||||
assert LITELLM_TRUNCATED_PAYLOAD_FIELD in truncated_value
|
||||
assert parsed["data"][0]["other_field"] == "value"
|
||||
|
||||
|
||||
@patch(
|
||||
"litellm.proxy.spend_tracking.spend_tracking_utils._should_store_prompts_and_responses_in_spend_logs"
|
||||
)
|
||||
def test_get_response_for_spend_logs_payload_truncates_large_embedding(mock_should_store):
|
||||
from litellm.constants import MAX_STRING_LENGTH_PROMPT_IN_DB
|
||||
|
||||
mock_should_store.return_value = True
|
||||
embedding_values = [
|
||||
round(i * 0.0001, 6) for i in range(MAX_STRING_LENGTH_PROMPT_IN_DB + 500)
|
||||
]
|
||||
large_embedding = json.dumps(embedding_values)
|
||||
payload = cast(
|
||||
StandardLoggingPayload,
|
||||
{
|
||||
"response": {
|
||||
"data": [
|
||||
{
|
||||
"embedding": large_embedding,
|
||||
"other_field": "value",
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
response_json = _get_response_for_spend_logs_payload(payload)
|
||||
parsed = json.loads(response_json)
|
||||
truncated_value = parsed["data"][0]["embedding"]
|
||||
|
||||
assert isinstance(truncated_value, str)
|
||||
assert len(truncated_value) < len(large_embedding)
|
||||
assert LITELLM_TRUNCATED_PAYLOAD_FIELD in truncated_value
|
||||
assert parsed["data"][0]["other_field"] == "value"
|
||||
|
||||
|
||||
def test_safe_dumps_handles_circular_references():
|
||||
"""Test that safe_dumps can handle circular references without raising exceptions"""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue