mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-10 12:17:11 +00:00
feat(search): unify session transcript search (#2877)
This commit is contained in:
+16
-43
@@ -548,7 +548,7 @@ async def do_suggest_document(content: str, doc_id: str = None, owner: Optional[
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
async def do_search_chats(query: str, limit: int = 20, owner: str | None = None) -> Dict:
|
||||
"""Search past chat messages for the calling user's sessions only.
|
||||
"""Search past session transcripts for the calling user's sessions only.
|
||||
|
||||
Without an owner filter this used to leak EVERY user's chat history
|
||||
into the agent's `search_chats` results (v2 review HIGH-11). The
|
||||
@@ -556,63 +556,36 @@ async def do_search_chats(query: str, limit: int = 20, owner: str | None = None)
|
||||
through; legacy callers without owner pass through as before but
|
||||
will only see legacy/null-owner rows.
|
||||
"""
|
||||
from src.database import SessionLocal, ChatMessage as DBChatMessage, Session as DBSession
|
||||
# Escape LIKE wildcards in the user-supplied query so a stray % or _
|
||||
# doesn't widen the match (and to keep the response deterministic).
|
||||
safe_q = query.replace("\\", "\\\\").replace("%", "\\%").replace("_", "\\_")
|
||||
db = SessionLocal()
|
||||
try:
|
||||
q = (
|
||||
db.query(DBChatMessage, DBSession.id, DBSession.name)
|
||||
.join(DBSession, DBChatMessage.session_id == DBSession.id)
|
||||
.filter(
|
||||
DBSession.archived == False,
|
||||
DBChatMessage.content.ilike(f"%{safe_q}%", escape="\\"),
|
||||
DBChatMessage.role.in_(["user", "assistant"]),
|
||||
)
|
||||
)
|
||||
if owner is not None:
|
||||
# Restrict to this user's sessions plus legacy null-owner
|
||||
# rows (so single-user upgrades keep seeing their own data).
|
||||
q = q.filter((DBSession.owner == owner) | (DBSession.owner.is_(None)))
|
||||
rows = q.order_by(DBChatMessage.timestamp.desc()).limit(limit).all()
|
||||
from src.session_search import search_session_messages
|
||||
|
||||
if not rows:
|
||||
results = search_session_messages(query, limit=limit, owner=owner)
|
||||
if not results:
|
||||
return {"results": f"No chats found matching \"{query}\"."}
|
||||
|
||||
# Group by session to avoid duplicate links
|
||||
seen_sessions = {}
|
||||
for msg, session_id, session_name in rows:
|
||||
if session_id not in seen_sessions:
|
||||
content = msg.content or ""
|
||||
lower_content = content.lower()
|
||||
idx = lower_content.find(query.lower())
|
||||
if idx == -1:
|
||||
snippet = content[:150]
|
||||
else:
|
||||
start = max(0, idx - 60)
|
||||
end = min(len(content), idx + len(query) + 60)
|
||||
snippet = ("..." if start > 0 else "") + content[start:end] + ("..." if end < len(content) else "")
|
||||
seen_sessions[session_id] = {
|
||||
"name": session_name or "Untitled",
|
||||
"snippet": snippet,
|
||||
"role": msg.role,
|
||||
"timestamp": msg.timestamp.isoformat() if msg.timestamp else None,
|
||||
}
|
||||
for result in results:
|
||||
if result.session_id not in seen_sessions:
|
||||
seen_sessions[result.session_id] = result
|
||||
|
||||
lines = [f"Found {len(seen_sessions)} session(s) matching \"{query}\":\n"]
|
||||
for sid, info in seen_sessions.items():
|
||||
lines.append(f"- **{info['name']}** (#{sid})")
|
||||
for sid, result in seen_sessions.items():
|
||||
lines.append(f"- **{result.session_name}** (#{sid})")
|
||||
lines.append(f" Link: [Open chat](#{sid})")
|
||||
lines.append(f" > {info['snippet']}")
|
||||
lines.append(f" Match ({result.role}): {result.content_snippet}")
|
||||
if result.context_before:
|
||||
before = result.context_before[-1]
|
||||
lines.append(f" Before ({before['role']}): {before['content'][:180]}")
|
||||
if result.context_after:
|
||||
after = result.context_after[0]
|
||||
lines.append(f" After ({after['role']}): {after['content'][:180]}")
|
||||
lines.append("")
|
||||
|
||||
return {"results": "\n".join(lines)}
|
||||
except Exception as e:
|
||||
logger.error(f"search_chats failed: {e}")
|
||||
return {"error": str(e), "exit_code": 1}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user