From 9c4ad034e5fd084f541b35739ba51ccfe2e10f77 Mon Sep 17 00:00:00 2001 From: Jonny Date: Sun, 13 Sep 2026 15:00:44 +0200 Subject: [PATCH] feat: add conversation_summaries DB helpers Add upsert_conversation_summary() and search_similar_conversation_summaries() functions to manage conversation summaries with vector embeddings. These helpers support cross-chat memory retrieval and background summarization tasks. Co-Authored-By: Claude Haiku 4.5 Claude-Session: https://claude.ai/code/session_019iZMEPk2Kt1UC96Lo9bC5w --- Claude outputs/main.py | 26 ++++++++++++++++++++++++++ Claude outputs/tests/test_memory.py | 23 +++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/Claude outputs/main.py b/Claude outputs/main.py index 3242770..aff3928 100644 --- a/Claude outputs/main.py +++ b/Claude outputs/main.py @@ -302,6 +302,32 @@ async def get_all_memory_facts() -> list: ) +async def upsert_conversation_summary(conversation_id: int, summary: str, embedding: list): + await db_query( + """ + INSERT INTO conversation_summaries (conversation_id, summary, embedding) + VALUES (%s, %s, %s::vector) + ON CONFLICT (conversation_id) DO UPDATE + SET summary = EXCLUDED.summary, embedding = EXCLUDED.embedding, updated_at = CURRENT_TIMESTAMP + """, + (conversation_id, summary, _vector_literal(embedding)), + ) + + +async def search_similar_conversation_summaries(query_embedding: list, exclude_conversation_id: int, limit: int = 3) -> list: + return await db_query( + """ + SELECT conversation_id, summary, embedding <=> %s::vector AS distance + FROM conversation_summaries + WHERE conversation_id != %s + ORDER BY distance ASC + LIMIT %s + """, + (_vector_literal(query_embedding), exclude_conversation_id, limit), + fetch="all", + ) + + def _caldav_calendar(): dav_root = NEXTCLOUD_CALDAV_URL.split("/calendars/")[0] + "/" client = caldav.DAVClient(url=dav_root, username=NEXTCLOUD_USER, password=NEXTCLOUD_APP_PASSWORD) diff --git a/Claude outputs/tests/test_memory.py b/Claude outputs/tests/test_memory.py index 45daece..7222e17 100644 --- a/Claude outputs/tests/test_memory.py +++ b/Claude outputs/tests/test_memory.py @@ -59,3 +59,26 @@ async def test_get_all_memory_facts_returns_rows(): result = await main.get_all_memory_facts() assert result == rows + + +@pytest.mark.asyncio +async def test_upsert_conversation_summary_inserts_with_vector_literal(): + with patch.object(main, "db_query", new=AsyncMock(return_value=None)) as mock_query: + await main.upsert_conversation_summary(5, "Kurze Zusammenfassung", [0.1, 0.2]) + + args, kwargs = mock_query.call_args + assert "INSERT INTO conversation_summaries" in args[0] + assert "ON CONFLICT (conversation_id) DO UPDATE" in args[0] + assert args[1] == (5, "Kurze Zusammenfassung", "[0.1,0.2]") + + +@pytest.mark.asyncio +async def test_search_similar_conversation_summaries_excludes_current_conversation(): + rows = [{"conversation_id": 3, "summary": "Ueber Kalender", "distance": 0.1}] + with patch.object(main, "db_query", new=AsyncMock(return_value=rows)) as mock_query: + result = await main.search_similar_conversation_summaries([0.1, 0.2], exclude_conversation_id=9, limit=3) + + assert result == rows + args, kwargs = mock_query.call_args + assert "conversation_id != %s" in args[0] + assert args[1] == ("[0.1,0.2]", 9, 3)