From 8bdd61bdea0cb0efcbe1a5e2b860cde8cd6dd843 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 14:13:53 +0000 Subject: [PATCH 1/2] fix(logging): log anthropic `system` prompts sent as content blocks Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/litellm_core_utils/litellm_logging.py | 38 ++++++++++--------- .../test_litellm_logging.py | 33 ++++++++++++++++ 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 99721c3ffa2..5357da7543b 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -4542,25 +4542,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 system or not isinstance(system, (str, list)): + 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 23e0975cd08..cf65c20d74b 100644 --- a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py +++ b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py @@ -2240,6 +2240,39 @@ 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] + + # already-prepended system blocks are not duplicated + result = StandardLoggingPayloadSetup.append_system_prompt_messages( + kwargs={"system": system_blocks}, messages=result + ) + assert len(result) == 2 + + # empty system is not logged as an empty system turn + assert ( + StandardLoggingPayloadSetup.append_system_prompt_messages(kwargs={"system": []}, messages=messages) == messages + ) + + @pytest.mark.asyncio async def test_async_success_handler_sets_standard_logging_object_for_pass_through_endpoints(): """ From 1356309ee9cdc7e8fd0b5597ca2585a129e9138e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 14:51:01 +0000 Subject: [PATCH 2/2] fix(logging): keep logging empty string system prompts Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- litellm/litellm_core_utils/litellm_logging.py | 2 +- .../test_litellm/litellm_core_utils/test_litellm_logging.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/litellm/litellm_core_utils/litellm_logging.py b/litellm/litellm_core_utils/litellm_logging.py index 5357da7543b..d0dcd3e696e 100644 --- a/litellm/litellm_core_utils/litellm_logging.py +++ b/litellm/litellm_core_utils/litellm_logging.py @@ -4549,7 +4549,7 @@ class StandardLoggingPayloadSetup: if kwargs is None: return messages system: Final = kwargs.get("system") - if not system or not isinstance(system, (str, list)): + 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: 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 cf65c20d74b..2e1263a7114 100644 --- a/tests/test_litellm/litellm_core_utils/test_litellm_logging.py +++ b/tests/test_litellm/litellm_core_utils/test_litellm_logging.py @@ -2261,16 +2261,18 @@ def test_append_system_prompt_messages_content_blocks(): ) assert result == [{"role": "system", "content": system_blocks}, *messages] - # already-prepended system blocks are not duplicated result = StandardLoggingPayloadSetup.append_system_prompt_messages( kwargs={"system": system_blocks}, messages=result ) assert len(result) == 2 - # empty system is not logged as an empty system turn 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