feat: build chat identity line from settings instead of a static env var

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V57jSQPqwkGG8BuAXg59X5
This commit is contained in:
Jonny 2026-09-14 08:11:07 +02:00
parent a904f2f8e0
commit 9505bc2eb3
5 changed files with 34 additions and 13 deletions

View File

@ -56,11 +56,6 @@ EMAIL_SMTP_HOST = os.getenv("EMAIL_SMTP_HOST")
EMAIL_SMTP_PORT = int(os.getenv("EMAIL_SMTP_PORT", "465"))
EMAIL_USER = os.getenv("EMAIL_USER")
EMAIL_PASSWORD = os.getenv("EMAIL_PASSWORD")
CLAUDE_SYSTEM_PROMPT = os.getenv(
"CLAUDE_SYSTEM_PROMPT",
"Du bist JARVIS, ein KI-Assistent fuer Business-Automatisierung.",
)
# ============ INITIALIZATION ============
app = FastAPI(
title="JARVIS API",
@ -1070,12 +1065,21 @@ async def execute_tool(name: str, tool_input: dict) -> str:
MAX_TOOL_ROUNDS = 5
async def _identity_system_prompt() -> str:
settings = await get_all_settings()
return (
f"Du bist {settings['assistant_name']}, der KI-Assistent von "
f"{settings['company_name']}. Bei Fragen zur Erreichbarkeit kannst "
f"du auf {settings['contact_email']} verweisen."
)
async def run_chat_completion(claude_messages: list, conversation_id: int):
latest_user_message = claude_messages[-1]["content"]
memory_context = await build_memory_context(latest_user_message, conversation_id)
system_prompt = (
f"{CLAUDE_SYSTEM_PROMPT}\n\n{_current_datetime_context()}\n\n{CALENDAR_ASSISTANT_INSTRUCTIONS}\n\n"
f"{await _identity_system_prompt()}\n\n{_current_datetime_context()}\n\n{CALENDAR_ASSISTANT_INSTRUCTIONS}\n\n"
f"{MEMORY_ASSISTANT_INSTRUCTIONS}\n\n{ORDER_ASSISTANT_INSTRUCTIONS}"
)
if memory_context:

View File

@ -43,7 +43,9 @@ async def test_run_chat_completion_without_tool_use():
main.claude_client = MagicMock()
main.claude_client.messages.create.return_value = completion
with patch.object(main, "build_memory_context", new=AsyncMock(return_value="")):
with patch.object(main, "build_memory_context", new=AsyncMock(return_value="")), patch.object(
main, "_identity_system_prompt", new=AsyncMock(return_value="Du bist JARVIS.")
):
text, output_tokens, total_tokens = await main.run_chat_completion(
[{"role": "user", "content": "Hi"}], conversation_id=1
)
@ -70,7 +72,7 @@ async def test_run_chat_completion_executes_tool_and_returns_followup():
with patch.object(main, "list_upcoming_events", new=AsyncMock(return_value=[])), patch.object(
main, "build_memory_context", new=AsyncMock(return_value="")
):
), patch.object(main, "_identity_system_prompt", new=AsyncMock(return_value="Du bist JARVIS.")):
text, output_tokens, total_tokens = await main.run_chat_completion(
[{"role": "user", "content": "Was steht diese Woche an?"}], conversation_id=1
)
@ -108,7 +110,9 @@ async def test_run_chat_completion_handles_two_sequential_tool_calls():
with patch.object(main, "list_upcoming_events", new=AsyncMock(return_value=[{"uid": "abc"}])), patch.object(
main, "update_event", new=AsyncMock(return_value={"uid": "abc"})
), patch.object(main, "build_memory_context", new=AsyncMock(return_value="")):
), patch.object(main, "build_memory_context", new=AsyncMock(return_value="")), patch.object(
main, "_identity_system_prompt", new=AsyncMock(return_value="Du bist JARVIS.")
):
text, output_tokens, total_tokens = await main.run_chat_completion(
[{"role": "user", "content": "Verschiebe den Termin X"}], conversation_id=1
)
@ -136,7 +140,7 @@ async def test_run_chat_completion_lists_recent_emails():
with patch.object(main, "list_recent_emails", new=AsyncMock(return_value=[])), patch.object(
main, "build_memory_context", new=AsyncMock(return_value="")
):
), patch.object(main, "_identity_system_prompt", new=AsyncMock(return_value="Du bist JARVIS.")):
text, output_tokens, total_tokens = await main.run_chat_completion(
[{"role": "user", "content": "Was ist neu im Postfach?"}], conversation_id=1
)
@ -168,7 +172,7 @@ async def test_run_chat_completion_includes_current_datetime_in_system_prompt():
with patch.object(main, "build_memory_context", new=AsyncMock(return_value="")), patch.object(
main, "_current_datetime_context", return_value="Aktuelles Datum und Uhrzeit: TESTMARKER"
):
), patch.object(main, "_identity_system_prompt", new=AsyncMock(return_value="Du bist JARVIS.")):
await main.run_chat_completion([{"role": "user", "content": "Hi"}], conversation_id=1)
_, kwargs = main.claude_client.messages.create.call_args

View File

@ -206,7 +206,7 @@ async def test_run_chat_completion_includes_memory_context_in_system_prompt():
with patch.object(
main, "build_memory_context", new=AsyncMock(return_value="Bekannte Fakten ueber den Nutzer:\n- Hund heisst Bruno")
):
), patch.object(main, "_identity_system_prompt", new=AsyncMock(return_value="Du bist JARVIS.")):
await main.run_chat_completion([{"role": "user", "content": "Wie geht es meinem Hund?"}], conversation_id=1)
_, kwargs = main.claude_client.messages.create.call_args

View File

@ -328,7 +328,9 @@ async def test_run_chat_completion_includes_order_tools_and_instructions():
main.claude_client = MagicMock()
main.claude_client.messages.create.return_value = completion
with patch.object(main, "build_memory_context", new=AsyncMock(return_value="")):
with patch.object(main, "build_memory_context", new=AsyncMock(return_value="")), patch.object(
main, "_identity_system_prompt", new=AsyncMock(return_value="Du bist JARVIS.")
):
await main.run_chat_completion(
[{"role": "user", "content": "Neue Bestellung fuer Feuerwehr Onza"}], conversation_id=1
)

View File

@ -113,3 +113,14 @@ def test_put_settings_endpoint_rejects_unknown_key(client):
def test_put_settings_endpoint_requires_admin_key(client):
response = client.put("/api/v1/settings", json={"assistant_name": "X"})
assert response.status_code == 401
@pytest.mark.asyncio
async def test_identity_system_prompt_uses_settings():
settings = {"assistant_name": "FRIDAY", "company_name": "Stark Industries", "contact_email": "info@stark.example"}
with patch.object(main, "get_all_settings", new=AsyncMock(return_value=settings)):
prompt = await main._identity_system_prompt()
assert "FRIDAY" in prompt
assert "Stark Industries" in prompt
assert "info@stark.example" in prompt