feat: inject remembered facts and similar summaries into chat system prompt
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V57jSQPqwkGG8BuAXg59X5
This commit is contained in:
parent
33016bdf0a
commit
2d9bf6a42b
|
|
@ -731,8 +731,14 @@ async def execute_tool(name: str, tool_input: dict) -> str:
|
||||||
MAX_TOOL_ROUNDS = 5
|
MAX_TOOL_ROUNDS = 5
|
||||||
|
|
||||||
|
|
||||||
async def run_chat_completion(claude_messages: list):
|
async def run_chat_completion(claude_messages: list, conversation_id: int):
|
||||||
system_prompt = f"{CLAUDE_SYSTEM_PROMPT}\n\n{CALENDAR_ASSISTANT_INSTRUCTIONS}"
|
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{CALENDAR_ASSISTANT_INSTRUCTIONS}\n\n{MEMORY_ASSISTANT_INSTRUCTIONS}"
|
||||||
|
if memory_context:
|
||||||
|
system_prompt = f"{system_prompt}\n\n{memory_context}"
|
||||||
|
|
||||||
messages = list(claude_messages)
|
messages = list(claude_messages)
|
||||||
total_input = 0
|
total_input = 0
|
||||||
total_output = 0
|
total_output = 0
|
||||||
|
|
@ -743,7 +749,7 @@ async def run_chat_completion(claude_messages: list):
|
||||||
model=CLAUDE_MODEL,
|
model=CLAUDE_MODEL,
|
||||||
max_tokens=1024,
|
max_tokens=1024,
|
||||||
system=system_prompt,
|
system=system_prompt,
|
||||||
tools=CALENDAR_TOOLS,
|
tools=CALENDAR_TOOLS + MEMORY_TOOLS,
|
||||||
messages=messages,
|
messages=messages,
|
||||||
)
|
)
|
||||||
total_input += completion.usage.input_tokens
|
total_input += completion.usage.input_tokens
|
||||||
|
|
@ -900,7 +906,7 @@ async def chat(request: ChatRequest):
|
||||||
claude_messages = [{"role": m["role"], "content": m["content"]} for m in history]
|
claude_messages = [{"role": m["role"], "content": m["content"]} for m in history]
|
||||||
claude_messages.append({"role": "user", "content": request.message})
|
claude_messages.append({"role": "user", "content": request.message})
|
||||||
|
|
||||||
response_text, output_tokens, tokens_used = await run_chat_completion(claude_messages)
|
response_text, output_tokens, tokens_used = await run_chat_completion(claude_messages, conversation_id)
|
||||||
|
|
||||||
await save_message(conversation_id, DEFAULT_USER_ID, "assistant", response_text, output_tokens)
|
await save_message(conversation_id, DEFAULT_USER_ID, "assistant", response_text, output_tokens)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,10 @@ async def test_run_chat_completion_without_tool_use():
|
||||||
main.claude_client = MagicMock()
|
main.claude_client = MagicMock()
|
||||||
main.claude_client.messages.create.return_value = completion
|
main.claude_client.messages.create.return_value = completion
|
||||||
|
|
||||||
text, output_tokens, total_tokens = await main.run_chat_completion([{"role": "user", "content": "Hi"}])
|
with patch.object(main, "build_memory_context", new=AsyncMock(return_value="")):
|
||||||
|
text, output_tokens, total_tokens = await main.run_chat_completion(
|
||||||
|
[{"role": "user", "content": "Hi"}], conversation_id=1
|
||||||
|
)
|
||||||
|
|
||||||
assert text == "Hallo!"
|
assert text == "Hallo!"
|
||||||
assert output_tokens == 5
|
assert output_tokens == 5
|
||||||
|
|
@ -64,9 +67,11 @@ async def test_run_chat_completion_executes_tool_and_returns_followup():
|
||||||
main.claude_client = MagicMock()
|
main.claude_client = MagicMock()
|
||||||
main.claude_client.messages.create.side_effect = [first, second]
|
main.claude_client.messages.create.side_effect = [first, second]
|
||||||
|
|
||||||
with patch.object(main, "list_upcoming_events", new=AsyncMock(return_value=[])):
|
with patch.object(main, "list_upcoming_events", new=AsyncMock(return_value=[])), patch.object(
|
||||||
|
main, "build_memory_context", new=AsyncMock(return_value="")
|
||||||
|
):
|
||||||
text, output_tokens, total_tokens = await main.run_chat_completion(
|
text, output_tokens, total_tokens = await main.run_chat_completion(
|
||||||
[{"role": "user", "content": "Was steht diese Woche an?"}]
|
[{"role": "user", "content": "Was steht diese Woche an?"}], conversation_id=1
|
||||||
)
|
)
|
||||||
|
|
||||||
assert text == "Naechste Woche steht nichts an."
|
assert text == "Naechste Woche steht nichts an."
|
||||||
|
|
@ -102,9 +107,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(
|
with patch.object(main, "list_upcoming_events", new=AsyncMock(return_value=[{"uid": "abc"}])), patch.object(
|
||||||
main, "update_event", new=AsyncMock(return_value={"uid": "abc"})
|
main, "update_event", new=AsyncMock(return_value={"uid": "abc"})
|
||||||
):
|
), patch.object(main, "build_memory_context", new=AsyncMock(return_value="")):
|
||||||
text, output_tokens, total_tokens = await main.run_chat_completion(
|
text, output_tokens, total_tokens = await main.run_chat_completion(
|
||||||
[{"role": "user", "content": "Verschiebe den Termin X"}]
|
[{"role": "user", "content": "Verschiebe den Termin X"}], conversation_id=1
|
||||||
)
|
)
|
||||||
|
|
||||||
assert text == "Termin verschoben."
|
assert text == "Termin verschoben."
|
||||||
|
|
@ -128,9 +133,11 @@ async def test_run_chat_completion_lists_recent_emails():
|
||||||
main.claude_client = MagicMock()
|
main.claude_client = MagicMock()
|
||||||
main.claude_client.messages.create.side_effect = [first, second]
|
main.claude_client.messages.create.side_effect = [first, second]
|
||||||
|
|
||||||
with patch.object(main, "list_recent_emails", new=AsyncMock(return_value=[])):
|
with patch.object(main, "list_recent_emails", new=AsyncMock(return_value=[])), patch.object(
|
||||||
|
main, "build_memory_context", new=AsyncMock(return_value="")
|
||||||
|
):
|
||||||
text, output_tokens, total_tokens = await main.run_chat_completion(
|
text, output_tokens, total_tokens = await main.run_chat_completion(
|
||||||
[{"role": "user", "content": "Was ist neu im Postfach?"}]
|
[{"role": "user", "content": "Was ist neu im Postfach?"}], conversation_id=1
|
||||||
)
|
)
|
||||||
|
|
||||||
assert text == "Du hast 5 neue Mails."
|
assert text == "Du hast 5 neue Mails."
|
||||||
|
|
|
||||||
|
|
@ -186,3 +186,29 @@ async def test_build_memory_context_skips_summaries_when_embedding_fails():
|
||||||
context = await main.build_memory_context("Hallo", conversation_id=1)
|
context = await main.build_memory_context("Hallo", conversation_id=1)
|
||||||
|
|
||||||
assert context == ""
|
assert context == ""
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_run_chat_completion_includes_memory_context_in_system_prompt():
|
||||||
|
completion = MagicMock()
|
||||||
|
completion.stop_reason = "end_turn"
|
||||||
|
text_block = MagicMock()
|
||||||
|
text_block.type = "text"
|
||||||
|
text_block.text = "Bruno geht es gut."
|
||||||
|
completion.content = [text_block]
|
||||||
|
usage = MagicMock()
|
||||||
|
usage.input_tokens = 10
|
||||||
|
usage.output_tokens = 5
|
||||||
|
completion.usage = usage
|
||||||
|
|
||||||
|
main.claude_client = MagicMock()
|
||||||
|
main.claude_client.messages.create.return_value = completion
|
||||||
|
|
||||||
|
with patch.object(
|
||||||
|
main, "build_memory_context", new=AsyncMock(return_value="Bekannte Fakten ueber den Nutzer:\n- Hund heisst Bruno")
|
||||||
|
):
|
||||||
|
await main.run_chat_completion([{"role": "user", "content": "Wie geht es meinem Hund?"}], conversation_id=1)
|
||||||
|
|
||||||
|
_, kwargs = main.claude_client.messages.create.call_args
|
||||||
|
assert "Hund heisst Bruno" in kwargs["system"]
|
||||||
|
assert kwargs["tools"] == main.CALENDAR_TOOLS + main.MEMORY_TOOLS
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue