diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 3018f0c4d24..84bbbd87f91 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -5077,25 +5077,27 @@ class StandardLoggingPayloadSetup: def append_system_prompt_messages(kwargs: dict | None = None, messages: Any | None = None): """ Append system prompt messages to the messages - """ - if kwargs is not None: - if kwargs.get("system") is not None and isinstance(kwargs.get("system"), str): - if messages is None: - return [{"role": "system", "content": kwargs.get("system")}] - elif isinstance(messages, list): - if len(messages) == 0: - return [{"role": "system", "content": kwargs.get("system")}] - # check for duplicates - if messages[0].get("role") == "system" and messages[0].get("content") == kwargs.get("system"): - return messages - messages = [{"role": "system", "content": kwargs.get("system")}] + messages - elif isinstance(messages, str): - messages = [ - {"role": "system", "content": kwargs.get("system")}, - {"role": "user", "content": messages}, - ] - return messages + Anthropic's /v1/messages accepts ``system`` as a string or as a list of + content blocks; both shapes are logged. + """ + if kwargs is None: + return messages + system: Final = kwargs.get("system") + if not isinstance(system, (str, list)) or (isinstance(system, list) and not system): + return messages + system_message: Final = {"role": "system", "content": system} + if messages is None: + return [system_message] + if isinstance(messages, list): + if len(messages) == 0: + return [system_message] + first: Final = messages[0] + if isinstance(first, dict) and first.get("role") == "system" and first.get("content") == system: + return messages + return [system_message, *messages] + if isinstance(messages, str): + return [system_message, {"role": "user", "content": messages}] return messages @staticmethod diff --git a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py index 0222e756ba1..d236893db13 100644 --- a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py +++ b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py @@ -2524,6 +2524,41 @@ def test_append_system_prompt_messages(): assert result == messages +def test_append_system_prompt_messages_content_blocks(): + """ + Anthropic /v1/messages accepts `system` as a list of content blocks; that shape must be logged + too, not silently dropped. + """ + from litellm.litellm_core_utils.litellm_logging import StandardLoggingPayloadSetup + + system_blocks = [ + { + "type": "text", + "text": "You are a helpful assistant", + "cache_control": {"type": "ephemeral"}, + } + ] + messages = [{"role": "user", "content": "Hello"}] + + result = StandardLoggingPayloadSetup.append_system_prompt_messages( + kwargs={"system": system_blocks}, messages=messages + ) + assert result == [{"role": "system", "content": system_blocks}, *messages] + + result = StandardLoggingPayloadSetup.append_system_prompt_messages( + kwargs={"system": system_blocks}, messages=result + ) + assert len(result) == 2 + + assert ( + StandardLoggingPayloadSetup.append_system_prompt_messages(kwargs={"system": []}, messages=messages) == messages + ) + assert StandardLoggingPayloadSetup.append_system_prompt_messages(kwargs={"system": ""}, messages=messages) == [ + {"role": "system", "content": ""}, + *messages, + ] + + @pytest.mark.asyncio async def test_async_success_handler_sets_standard_logging_object_for_pass_through_endpoints(): """