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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019iZMEPk2Kt1UC96Lo9bC5w
This commit is contained in:
Jonny 2026-09-13 15:00:44 +02:00
parent 8a7ac5aad6
commit 9c4ad034e5
2 changed files with 49 additions and 0 deletions

View File

@ -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)

View File

@ -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)