Merge branch 'litellm_lakera_v2_gemini_contents' into litellm_passthrough_applied_guardrails_header

This commit is contained in:
yucheng 2026-11-03 13:14:01 -08:00
commit be4f7188eb
2 changed files with 13 additions and 2 deletions

View file

@ -254,11 +254,15 @@ def _gemini_items(value: object) -> Sequence[object]:
return _OBJECT_LIST.validate_python(value) if isinstance(value, list) else ()
def _gemini_part_text(part: object) -> object:
"""A part's ``text`` field, or the part itself when it's a plain string, which the generateContent adapter sends as text"""
return part if isinstance(part, str) else (as_str_mapping(part) or {}).get("text")
def _gemini_text(content: object) -> str:
"""Join the text parts of one Gemini ``Content`` object, skipping inline data, files and function calls."""
parts: Final = _gemini_items((as_str_mapping(content) or {}).get("parts"))
texts: Final = ((as_str_mapping(part) or {}).get("text") for part in parts)
return "\n".join(text for text in texts if isinstance(text, str) and text)
return "\n".join(text for text in map(_gemini_part_text, parts) if isinstance(text, str) and text)
def _gemini_tool_part_values(part: object) -> Iterator[object]:

View file

@ -1829,6 +1829,13 @@ class TestGeminiContents:
{"role": "tool", "content": expected},
]
def test_reads_a_plain_string_part_as_text(self):
data: Final = {"contents": [{"role": "user", "parts": ["hello", {"text": "world"}]}]}
assert _build_lakera_inspection_messages(data, call_type="pass_through_endpoint") == [
{"role": "user", "content": "hello\nworld"}
]
def test_reads_a_single_contents_object_with_a_single_parts_object(self):
data: Final = {"contents": {"role": "user", "parts": {"text": "hello world"}}}
assert _build_lakera_inspection_messages(data, call_type="pass_through_endpoint") == [