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"