mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
fix(session_search): restore same-session context when message ids are interleaved
Replaces global id +/- 1 context lookup with CTE-based same-session neighbor queries. When multiple sessions write concurrently, id adjacency does not imply session adjacency — the old query missed real neighbors. Co-authored-by: Junass1 <ysfalweshcan@gmail.com>
This commit is contained in:
@@ -1249,10 +1249,37 @@ class SessionDB:
|
|||||||
try:
|
try:
|
||||||
with self._lock:
|
with self._lock:
|
||||||
ctx_cursor = self._conn.execute(
|
ctx_cursor = self._conn.execute(
|
||||||
"""SELECT role, content FROM messages
|
"""WITH target AS (
|
||||||
WHERE session_id = ? AND id >= ? - 1 AND id <= ? + 1
|
SELECT session_id, timestamp, id
|
||||||
ORDER BY id""",
|
FROM messages
|
||||||
(match["session_id"], match["id"], match["id"]),
|
WHERE id = ?
|
||||||
|
)
|
||||||
|
SELECT role, content
|
||||||
|
FROM (
|
||||||
|
SELECT m.id, m.timestamp, m.role, m.content
|
||||||
|
FROM messages m
|
||||||
|
JOIN target t ON t.session_id = m.session_id
|
||||||
|
WHERE (m.timestamp < t.timestamp)
|
||||||
|
OR (m.timestamp = t.timestamp AND m.id < t.id)
|
||||||
|
ORDER BY m.timestamp DESC, m.id DESC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
UNION ALL
|
||||||
|
SELECT role, content
|
||||||
|
FROM messages
|
||||||
|
WHERE id = ?
|
||||||
|
UNION ALL
|
||||||
|
SELECT role, content
|
||||||
|
FROM (
|
||||||
|
SELECT m.id, m.timestamp, m.role, m.content
|
||||||
|
FROM messages m
|
||||||
|
JOIN target t ON t.session_id = m.session_id
|
||||||
|
WHERE (m.timestamp > t.timestamp)
|
||||||
|
OR (m.timestamp = t.timestamp AND m.id > t.id)
|
||||||
|
ORDER BY m.timestamp ASC, m.id ASC
|
||||||
|
LIMIT 1
|
||||||
|
)""",
|
||||||
|
(match["id"], match["id"]),
|
||||||
)
|
)
|
||||||
context_msgs = [
|
context_msgs = [
|
||||||
{"role": r["role"], "content": (r["content"] or "")[:200]}
|
{"role": r["role"], "content": (r["content"] or "")[:200]}
|
||||||
|
|||||||
@@ -365,6 +365,25 @@ class TestFTS5Search:
|
|||||||
assert isinstance(results[0]["context"], list)
|
assert isinstance(results[0]["context"], list)
|
||||||
assert len(results[0]["context"]) > 0
|
assert len(results[0]["context"]) > 0
|
||||||
|
|
||||||
|
def test_search_context_uses_session_neighbors_when_ids_are_interleaved(self, db):
|
||||||
|
db.create_session(session_id="s1", source="cli")
|
||||||
|
db.create_session(session_id="s2", source="cli")
|
||||||
|
|
||||||
|
db.append_message("s1", role="user", content="before needle")
|
||||||
|
db.append_message("s2", role="user", content="other session message")
|
||||||
|
db.append_message("s1", role="assistant", content="needle match")
|
||||||
|
db.append_message("s2", role="assistant", content="another other session message")
|
||||||
|
db.append_message("s1", role="user", content="after needle")
|
||||||
|
|
||||||
|
results = db.search_messages('"needle match"')
|
||||||
|
needle_result = next(r for r in results if r["session_id"] == "s1" and "needle match" in r["snippet"])
|
||||||
|
|
||||||
|
assert [msg["content"] for msg in needle_result["context"]] == [
|
||||||
|
"before needle",
|
||||||
|
"needle match",
|
||||||
|
"after needle",
|
||||||
|
]
|
||||||
|
|
||||||
def test_search_special_chars_do_not_crash(self, db):
|
def test_search_special_chars_do_not_crash(self, db):
|
||||||
"""FTS5 special characters in queries must not raise OperationalError."""
|
"""FTS5 special characters in queries must not raise OperationalError."""
|
||||||
db.create_session(session_id="s1", source="cli")
|
db.create_session(session_id="s1", source="cli")
|
||||||
|
|||||||
Reference in New Issue
Block a user