This commit is contained in:
liuhao1024 2026-04-25 00:18:05 +00:00 committed by GitHub
commit 45ff282782
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 45 additions and 14 deletions

View file

@ -761,20 +761,32 @@ async def search_sessions(q: str = "", limit: int = 20):
else:
terms.append(token + "*")
prefix_query = " ".join(terms)
matches = db.search_messages(query=prefix_query, limit=limit)
# Group by session_id — return unique sessions with their best snippet
# search_messages() limits raw message hits, but this endpoint returns
# unique sessions. Overfetch in batches until we either collect the
# requested number of unique sessions or exhaust the match list.
seen: dict = {}
for m in matches:
sid = m["session_id"]
if sid not in seen:
seen[sid] = {
"session_id": sid,
"snippet": m.get("snippet", ""),
"role": m.get("role"),
"source": m.get("source"),
"model": m.get("model"),
"session_started": m.get("session_started"),
}
offset = 0
batch_size = max(limit, 20)
while len(seen) < limit:
matches = db.search_messages(query=prefix_query, limit=batch_size, offset=offset)
if not matches:
break
for m in matches:
sid = m["session_id"]
if sid not in seen:
seen[sid] = {
"session_id": sid,
"snippet": m.get("snippet", ""),
"role": m.get("role"),
"source": m.get("source"),
"model": m.get("model"),
"session_started": m.get("session_started"),
}
if len(seen) >= limit:
break
if len(matches) < batch_size:
break
offset += batch_size
return {"results": list(seen.values())}
finally:
db.close()

View file

@ -1292,7 +1292,7 @@ class SessionDB:
JOIN messages m ON m.id = messages_fts.rowid
JOIN sessions s ON s.id = m.session_id
WHERE {where_sql}
ORDER BY rank
ORDER BY rank, m.id
LIMIT ? OFFSET ?
"""

View file

@ -817,6 +817,25 @@ class TestNewEndpoints:
except Exception:
pass
def test_session_search_returns_limit_unique_sessions(self):
from hermes_state import SessionDB
db = SessionDB()
try:
db.create_session(session_id="session-a", source="cli", model="anthropic/claude-sonnet-4")
db.create_session(session_id="session-b", source="cli", model="anthropic/claude-sonnet-4")
for _ in range(25):
db.append_message("session-a", role="user", content="needle exact phrase")
db.append_message("session-b", role="user", content="needle exact phrase")
finally:
db.close()
resp = self.client.get('/api/sessions/search?q="needle exact phrase"&limit=2')
assert resp.status_code == 200
data = resp.json()
assert [result["session_id"] for result in data["results"]] == ["session-a", "session-b"]
# ---------------------------------------------------------------------------
# Model context length: normalize/denormalize + /api/model/info