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:
parent
8a7ac5aad6
commit
9c4ad034e5
|
|
@ -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():
|
def _caldav_calendar():
|
||||||
dav_root = NEXTCLOUD_CALDAV_URL.split("/calendars/")[0] + "/"
|
dav_root = NEXTCLOUD_CALDAV_URL.split("/calendars/")[0] + "/"
|
||||||
client = caldav.DAVClient(url=dav_root, username=NEXTCLOUD_USER, password=NEXTCLOUD_APP_PASSWORD)
|
client = caldav.DAVClient(url=dav_root, username=NEXTCLOUD_USER, password=NEXTCLOUD_APP_PASSWORD)
|
||||||
|
|
|
||||||
|
|
@ -59,3 +59,26 @@ async def test_get_all_memory_facts_returns_rows():
|
||||||
result = await main.get_all_memory_facts()
|
result = await main.get_all_memory_facts()
|
||||||
|
|
||||||
assert result == rows
|
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)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue