From f950a5a8622e46afe2a886e1b1429ee31089a402 Mon Sep 17 00:00:00 2001 From: Shreyansh Jain Date: Wed, 14 Oct 2026 09:25:29 +0530 Subject: [PATCH] chore(typing): keep the prompt injection call-type fix on the type-discipline ceiling --- .../llm_response_utils/get_formatted_prompt.py | 4 +++- litellm/proxy/hooks/prompt_injection_detection.py | 6 +++++- .../llm_response_utils/test_get_formatted_prompt.py | 8 ++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/litellm/litellm_core_utils/llm_response_utils/get_formatted_prompt.py b/litellm/litellm_core_utils/llm_response_utils/get_formatted_prompt.py index 0e87050d74c..0d60a791e57 100644 --- a/litellm/litellm_core_utils/llm_response_utils/get_formatted_prompt.py +++ b/litellm/litellm_core_utils/llm_response_utils/get_formatted_prompt.py @@ -40,7 +40,9 @@ def get_formatted_prompt( if isinstance(data["input"], str): prompt = data["input"] elif isinstance(data["input"], list): - prompt = "".join(item for item in data["input"] if isinstance(item, str)) + for item in data["input"]: + if isinstance(item, str): + prompt += item elif call_type == "image_generation": prompt = data["prompt"] elif call_type == "audio_transcription": diff --git a/litellm/proxy/hooks/prompt_injection_detection.py b/litellm/proxy/hooks/prompt_injection_detection.py index d8615d53dc0..a26c61ab3aa 100644 --- a/litellm/proxy/hooks/prompt_injection_detection.py +++ b/litellm/proxy/hooks/prompt_injection_detection.py @@ -147,7 +147,11 @@ class _OPTIONAL_PromptInjectionDetection(CustomLogger): return True # Found a highly similar substring return False # No substring crossed the threshold - def _formatted_prompt(self, data: dict[str, object], call_type: CallTypesLiteral) -> str | None: + def _formatted_prompt( + self, + data: dict[str, object], # mutable-ok: forwarded as-is to get_formatted_prompt, which takes the request dict + call_type: CallTypesLiteral, + ) -> str | None: if call_type in ("completion", "acompletion"): return get_formatted_prompt(data=data, call_type="completion") if call_type in ("text_completion", "atext_completion"): diff --git a/tests/unit/litellm_core_utils/llm_response_utils/test_get_formatted_prompt.py b/tests/unit/litellm_core_utils/llm_response_utils/test_get_formatted_prompt.py index 64dd79bb918..05c2890169d 100644 --- a/tests/unit/litellm_core_utils/llm_response_utils/test_get_formatted_prompt.py +++ b/tests/unit/litellm_core_utils/llm_response_utils/test_get_formatted_prompt.py @@ -22,3 +22,11 @@ def test_null_tool_calls_are_skipped(call_type: Literal["acompletion", "completi } assert get_formatted_prompt(data=data, call_type=call_type) == 'pingpong{"x":1}' + + +@pytest.mark.parametrize("call_type", ["embedding", "moderation"]) +def test_non_string_list_input_is_skipped(call_type: Literal["embedding", "moderation"]) -> None: + """/v1/embeddings accepts token id arrays, so the input list is not always text.""" + data: Final = {"input": ["scan me", 1234, ["nested"], "and me"]} + + assert get_formatted_prompt(data=data, call_type=call_type) == "scan meand me"