fix(guardrails): send Lakera Gemini function results without the logging sanitizer

The logging sanitizer rewrites keys and truncates deep values, so a result could reach the
provider with text Lakera never saw. Results are now encoded as plain JSON.
This commit is contained in:
yucheng 2026-11-02 13:57:27 -08:00
parent a2a34c4cf4
commit b0145da548
2 changed files with 11 additions and 2 deletions

View file

@ -1,4 +1,5 @@
import copy
import json
import os
from collections.abc import Mapping, Sequence
from datetime import datetime
@ -17,7 +18,6 @@ from litellm.integrations.custom_guardrail import (
CustomGuardrail,
)
from litellm.litellm_core_utils.core_helpers import as_str_mapping
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
from litellm.llms.base_llm.guardrail_translation.utils import (
effective_skip_system_message_for_guardrail,
effective_skip_tool_message_for_guardrail,
@ -253,7 +253,7 @@ def _gemini_tool_results(content: object) -> str:
part_fields: Final = (as_str_mapping(part) or {} for part in parts)
responses: Final = (fields.get("functionResponse") or fields.get("function_response") for fields in part_fields)
results: Final = ((as_str_mapping(response) or {}).get("response") for response in responses)
return "\n".join(safe_dumps(result) for result in results if result)
return "\n".join(json.dumps(result, default=str, ensure_ascii=False) for result in results if result)
def _has_gemini_contents(data: object, call_type: str | None) -> bool:

View file

@ -1782,6 +1782,15 @@ class TestGeminiContents:
== GEMINI_INSPECTION_MESSAGES
)
def test_gemini_function_response_reaches_inspection_exactly_as_the_provider_gets_it(self):
result: Final = {"output": "4111 1111 1111 1111", "\x00output": "clean", "name": "José"}
data: Final = {"contents": [{"parts": [{"functionResponse": {"name": "lookup", "response": result}}]}]}
messages: Final = _build_lakera_inspection_messages(data, call_type="pass_through_endpoint")
assert json.loads(messages[0]["content"]) == result
assert "José" in messages[0]["content"]
@pytest.mark.parametrize("field", ["functionResponse", "function_response"])
def test_gemini_function_response_is_read_as_a_tool_turn(self, field: str):
data: Final = {