From 90dd87edeb45a46c93cf04fd90240836c6bf782e Mon Sep 17 00:00:00 2001 From: Claude Code Date: Wed, 1 Apr 2026 16:10:24 -0500 Subject: [PATCH] fix: system prompt appends to caller's system message, empty = passthrough Handle all 4 combinations of caller system message and systemprompt.md correctly: append when both exist, passthrough when only one exists, omit when neither exists. Fixes leading \n\n when no caller system msg. Co-Authored-By: Claude Sonnet 4.6 --- app/utils.py | 19 ++++++++---- tests/test_utils.py | 73 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/app/utils.py b/app/utils.py index 2dce7b9..793b356 100644 --- a/app/utils.py +++ b/app/utils.py @@ -219,15 +219,22 @@ async def build_augmented_messages(incoming_messages: List[Dict]) -> List[Dict]: } # === LAYER 1: System Prompt === - system_content = "" + # Caller's system message passes through; systemprompt.md appends if non-empty. + caller_system = "" for msg in incoming_messages: if msg.get("role") == "system": - system_content = msg.get("content", "") + caller_system = msg.get("content", "") break - - if system_prompt: - system_content += "\n\n" + system_prompt - + + if caller_system and system_prompt: + system_content = caller_system + "\n\n" + system_prompt + elif caller_system: + system_content = caller_system + elif system_prompt: + system_content = system_prompt + else: + system_content = "" + if system_content: messages.append({"role": "system", "content": system_content}) logger.info(f"Layer 1 (system): {count_tokens(system_content)} tokens") diff --git a/tests/test_utils.py b/tests/test_utils.py index 0ac436e..89a9a36 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,7 @@ """Tests for utility functions.""" import pytest -from app.utils import count_tokens, truncate_by_tokens, parse_curated_turn +from unittest.mock import AsyncMock, MagicMock, patch +from app.utils import count_tokens, truncate_by_tokens, parse_curated_turn, build_augmented_messages class TestCountTokens: @@ -326,4 +327,72 @@ class TestBuildAugmentedMessages: ) contents = [m["content"] for m in result] - assert any("Old question" in c or "Old answer" in c for c in contents) \ No newline at end of file + assert any("Old question" in c or "Old answer" in c for c in contents) + + @pytest.mark.asyncio + async def test_system_prompt_appends_to_caller_system(self): + """systemprompt.md content appends to caller's system message.""" + import app.utils as utils_module + + mock_qdrant = self._make_qdrant_mock() + + with patch.object(utils_module, "load_system_prompt", return_value="Vera memory context"), \ + patch.object(utils_module, "get_qdrant_service", return_value=mock_qdrant): + incoming = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello"} + ] + result = await build_augmented_messages(incoming) + + system_msg = result[0] + assert system_msg["role"] == "system" + assert system_msg["content"] == "You are a helpful assistant.\n\nVera memory context" + + @pytest.mark.asyncio + async def test_empty_system_prompt_passthrough(self): + """When systemprompt.md is empty, only caller's system message passes through.""" + import app.utils as utils_module + + mock_qdrant = self._make_qdrant_mock() + + with patch.object(utils_module, "load_system_prompt", return_value=""), \ + patch.object(utils_module, "get_qdrant_service", return_value=mock_qdrant): + incoming = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Hello"} + ] + result = await build_augmented_messages(incoming) + + system_msg = result[0] + assert system_msg["content"] == "You are a helpful assistant." + + @pytest.mark.asyncio + async def test_no_caller_system_with_vera_prompt(self): + """When caller sends no system message but systemprompt.md exists, use vera prompt.""" + import app.utils as utils_module + + mock_qdrant = self._make_qdrant_mock() + + with patch.object(utils_module, "load_system_prompt", return_value="Vera memory context"), \ + patch.object(utils_module, "get_qdrant_service", return_value=mock_qdrant): + incoming = [{"role": "user", "content": "Hello"}] + result = await build_augmented_messages(incoming) + + system_msg = result[0] + assert system_msg["role"] == "system" + assert system_msg["content"] == "Vera memory context" + + @pytest.mark.asyncio + async def test_no_system_anywhere(self): + """When neither caller nor systemprompt.md provides system content, no system message.""" + import app.utils as utils_module + + mock_qdrant = self._make_qdrant_mock() + + with patch.object(utils_module, "load_system_prompt", return_value=""), \ + patch.object(utils_module, "get_qdrant_service", return_value=mock_qdrant): + incoming = [{"role": "user", "content": "Hello"}] + result = await build_augmented_messages(incoming) + + # First message should be user, not system + assert result[0]["role"] == "user" \ No newline at end of file