From 60fcec99eb061bdc7f5ca80615acace1eb446a94 Mon Sep 17 00:00:00 2001 From: Slava Trofimov Date: Tue, 2 Jun 2026 09:30:45 -0400 Subject: [PATCH] fix websocket chat history ordering --- src/octopal/infrastructure/store/sqlite.py | 2 +- tests/test_memory_system.py | 37 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/octopal/infrastructure/store/sqlite.py b/src/octopal/infrastructure/store/sqlite.py index e8204450..69f22418 100644 --- a/src/octopal/infrastructure/store/sqlite.py +++ b/src/octopal/infrastructure/store/sqlite.py @@ -918,7 +918,7 @@ def list_memory_entries_for_owner(self, owner_id: str, limit: int = 200) -> list def list_memory_entries_by_chat(self, chat_id: int, limit: int = 50) -> list[MemoryEntry]: cursor = self._conn.execute( - "SELECT * FROM memory_entries WHERE chat_id = ? ORDER BY id DESC LIMIT ?", + "SELECT * FROM memory_entries WHERE chat_id = ? ORDER BY created_at DESC, rowid DESC LIMIT ?", (chat_id, limit), ) return [self._row_to_memory(row) for row in cursor.fetchall()] diff --git a/tests/test_memory_system.py b/tests/test_memory_system.py index 0b613627..36e51d51 100644 --- a/tests/test_memory_system.py +++ b/tests/test_memory_system.py @@ -2,6 +2,7 @@ import asyncio import uuid +from datetime import UTC, datetime, timedelta from pathlib import Path from octopal.infrastructure.store.models import MemoryEntry @@ -58,6 +59,42 @@ def test_memory_owner_filter(tmp_path: Path) -> None: assert rows[0].content == "owned by default" +def test_memory_chat_history_orders_by_created_at_not_uuid(tmp_path: Path) -> None: + store = SQLiteStore(_StoreSettings(tmp_path / "data", tmp_path / "workspace")) + base = datetime(2026, 6, 2, 12, 0, tzinfo=UTC) + entries = [ + MemoryEntry( + id="ffffeeee-dddd-cccc-bbbb-aaaaaaaaaaaa", + role="user", + content="oldest", + embedding=None, + created_at=base, + metadata={"owner_id": "default", "chat_id": 7}, + ), + MemoryEntry( + id="11112222-3333-4444-5555-666677778888", + role="assistant", + content="newest", + embedding=None, + created_at=base + timedelta(minutes=2), + metadata={"owner_id": "default", "chat_id": 7}, + ), + MemoryEntry( + id="9999aaaa-bbbb-cccc-dddd-eeeeffffffff", + role="user", + content="middle", + embedding=None, + created_at=base + timedelta(minutes=1), + metadata={"owner_id": "default", "chat_id": 7}, + ), + ] + for entry in entries: + store.add_memory_entry(entry) + + rows = store.list_memory_entries_by_chat(7, limit=3) + assert [row.content for row in rows] == ["newest", "middle", "oldest"] + + def test_canon_event_log_and_compaction(tmp_path: Path) -> None: canon = CanonService( workspace_dir=tmp_path / "workspace",