mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
fix(redaction): scrub Vertex provider metadata when message logging is off
Clear vertex_ai_grounding_metadata and related fields from standard logging responses and assembled streaming ModelResponse objects so turn_off_message_logging cannot leak prompt-derived web search queries. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
1add616c3e
commit
9960d1c6b9
2 changed files with 96 additions and 0 deletions
|
|
@ -29,6 +29,14 @@ if TYPE_CHECKING:
|
|||
else:
|
||||
LiteLLMLoggingObject = Any
|
||||
|
||||
VERTEX_PROVIDER_METADATA_FIELDS = (
|
||||
"vertex_ai_grounding_metadata",
|
||||
"vertex_ai_url_context_metadata",
|
||||
"vertex_ai_safety_ratings",
|
||||
"vertex_ai_safety_results",
|
||||
"vertex_ai_citation_metadata",
|
||||
)
|
||||
|
||||
|
||||
def redact_message_input_output_from_custom_logger(
|
||||
litellm_logging_obj: LiteLLMLoggingObject, result, custom_logger: CustomLogger
|
||||
|
|
@ -100,6 +108,26 @@ def _redact_responses_api_output_dict(output_items, redacted_str: str):
|
|||
summary_item["text"] = redacted_str
|
||||
|
||||
|
||||
def _redact_vertex_provider_metadata(obj: Any) -> None:
|
||||
if isinstance(obj, dict):
|
||||
for field in VERTEX_PROVIDER_METADATA_FIELDS:
|
||||
if field in obj:
|
||||
obj[field] = []
|
||||
hidden_params = obj.get("_hidden_params")
|
||||
if isinstance(hidden_params, dict):
|
||||
for field in VERTEX_PROVIDER_METADATA_FIELDS:
|
||||
hidden_params.pop(field, None)
|
||||
return
|
||||
|
||||
for field in VERTEX_PROVIDER_METADATA_FIELDS:
|
||||
if hasattr(obj, field):
|
||||
setattr(obj, field, [])
|
||||
hidden_params = getattr(obj, "_hidden_params", None)
|
||||
if isinstance(hidden_params, dict):
|
||||
for field in VERTEX_PROVIDER_METADATA_FIELDS:
|
||||
hidden_params.pop(field, None)
|
||||
|
||||
|
||||
def _redact_standard_logging_object(model_call_details: dict):
|
||||
"""Redact messages and response inside standard_logging_object if present."""
|
||||
standard_logging_object = model_call_details.get("standard_logging_object")
|
||||
|
|
@ -119,10 +147,12 @@ def _redact_standard_logging_object(model_call_details: dict):
|
|||
# ResponsesAPIResponse format - redact content in output items
|
||||
if isinstance(response.get("output"), list):
|
||||
_redact_responses_api_output_dict(response["output"], redacted_str)
|
||||
_redact_vertex_provider_metadata(response)
|
||||
elif isinstance(response, dict) and "choices" in response:
|
||||
# ModelResponse dict format - redact content in choices
|
||||
if isinstance(response.get("choices"), list):
|
||||
_redact_model_response_dict_choices(response["choices"], redacted_str)
|
||||
_redact_vertex_provider_metadata(response)
|
||||
elif isinstance(response, str):
|
||||
standard_logging_object["response"] = redacted_str
|
||||
else:
|
||||
|
|
@ -174,6 +204,7 @@ def perform_redaction(model_call_details: dict, result):
|
|||
if hasattr(_streaming_response, "choices"):
|
||||
for choice in _streaming_response.choices:
|
||||
_redact_choice_content(choice)
|
||||
_redact_vertex_provider_metadata(_streaming_response)
|
||||
elif hasattr(_streaming_response, "output"):
|
||||
_redact_responses_api_output(_streaming_response.output)
|
||||
# Redact reasoning field in ResponsesAPIResponse
|
||||
|
|
@ -200,12 +231,14 @@ def perform_redaction(model_call_details: dict, result):
|
|||
if hasattr(_result, "choices") and _result.choices is not None:
|
||||
for choice in _result.choices:
|
||||
_redact_choice_content(choice)
|
||||
_redact_vertex_provider_metadata(_result)
|
||||
elif isinstance(_result, dict) and "choices" in _result:
|
||||
# Handle dict representation of ModelResponse (e.g., from model_dump())
|
||||
if _result.get("choices") is not None:
|
||||
_redact_model_response_dict_choices(
|
||||
_result["choices"], "redacted-by-litellm"
|
||||
)
|
||||
_redact_vertex_provider_metadata(_result)
|
||||
elif isinstance(_result, dict) and "output" in _result:
|
||||
if isinstance(_result.get("output"), list):
|
||||
_redact_responses_api_output_dict(
|
||||
|
|
|
|||
|
|
@ -349,3 +349,66 @@ class TestPerformRedaction:
|
|||
|
||||
assert redacted.output[0].content[0].text == "redacted-by-litellm"
|
||||
assert response.output[0].content[0].text == "sensitive output"
|
||||
|
||||
def test_redacts_vertex_provider_metadata_in_standard_logging_response(self):
|
||||
details = {
|
||||
"standard_logging_object": {
|
||||
"messages": [{"role": "user", "content": "sensitive prompt"}],
|
||||
"response": {
|
||||
"choices": [
|
||||
{
|
||||
"message": {
|
||||
"content": "sensitive answer",
|
||||
"role": "assistant",
|
||||
}
|
||||
}
|
||||
],
|
||||
"vertex_ai_grounding_metadata": [
|
||||
{"webSearchQueries": ["sensitive search term"]}
|
||||
],
|
||||
"vertex_ai_url_context_metadata": [
|
||||
{"urlMetadata": [{"retrievedUrl": "https://example.com"}]}
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
perform_redaction(details, None)
|
||||
|
||||
response = details["standard_logging_object"]["response"]
|
||||
assert response["choices"][0]["message"]["content"] == "redacted-by-litellm"
|
||||
assert response["vertex_ai_grounding_metadata"] == []
|
||||
assert response["vertex_ai_url_context_metadata"] == []
|
||||
|
||||
def test_redacts_vertex_provider_metadata_on_streaming_model_response(self):
|
||||
response = litellm.ModelResponse(
|
||||
id="resp-1",
|
||||
choices=[
|
||||
litellm.Choices(
|
||||
message=litellm.Message(
|
||||
content="sensitive answer",
|
||||
role="assistant",
|
||||
)
|
||||
)
|
||||
],
|
||||
model="gemini-2.5-flash",
|
||||
)
|
||||
setattr(
|
||||
response,
|
||||
"vertex_ai_grounding_metadata",
|
||||
[{"webSearchQueries": ["sensitive search term"]}],
|
||||
)
|
||||
response._hidden_params["vertex_ai_grounding_metadata"] = [
|
||||
{"webSearchQueries": ["sensitive search term"]}
|
||||
]
|
||||
|
||||
details = {
|
||||
"stream": True,
|
||||
"complete_streaming_response": response,
|
||||
}
|
||||
|
||||
perform_redaction(details, response)
|
||||
|
||||
assert response.choices[0].message.content == "redacted-by-litellm"
|
||||
assert getattr(response, "vertex_ai_grounding_metadata") == []
|
||||
assert "vertex_ai_grounding_metadata" not in response._hidden_params
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue