feat: add search_room_history tool for deep conversation search

Allows the bot to paginate back up to 500 messages in a room
to find specific content, beyond the default 10-message context window.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Gick
2026-03-08 17:47:51 +02:00
parent d6dae1da8e
commit 9fcdedc4b4

70
bot.py
View File

@@ -311,7 +311,28 @@ WEB_SEARCH_TOOLS = [{
}, },
}] }]
ALL_TOOLS = IMAGE_GEN_TOOLS + WEB_SEARCH_TOOLS + ATLASSIAN_TOOLS ROOM_TOOLS = [{
"type": "function",
"function": {
"name": "search_room_history",
"description": (
"Search the current chat room's message history for specific content. "
"Use when the user asks about something said earlier in the conversation that is beyond "
"the recent messages visible to you, e.g. 'what did I say about X last week', "
"'find the message where I mentioned Y', 'what was the first thing I asked today'."
),
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string", "description": "Search term or phrase to look for in room messages"},
"limit": {"type": "integer", "description": "Max messages to scan (default 200, max 500)", "default": 200},
},
"required": ["query"],
},
},
}]
ALL_TOOLS = IMAGE_GEN_TOOLS + WEB_SEARCH_TOOLS + ATLASSIAN_TOOLS + ROOM_TOOLS
ATLASSIAN_NOT_CONNECTED_MSG = ( ATLASSIAN_NOT_CONNECTED_MSG = (
"Your Atlassian account is not connected. " "Your Atlassian account is not connected. "
@@ -2047,6 +2068,12 @@ class Bot:
return f"Could not reach {url}. The domain may be misspelled — ask the user to clarify." return f"Could not reach {url}. The domain may be misspelled — ask the user to clarify."
return result return result
# Room history search — no auth needed
if tool_name == "search_room_history":
return await self._search_room_history(
room_id, args.get("query", ""), args.get("limit", 200)
)
# Atlassian tools — need per-user token # Atlassian tools — need per-user token
token = await self.atlassian.get_token(sender) if sender else None token = await self.atlassian.get_token(sender) if sender else None
if not token: if not token:
@@ -2080,6 +2107,47 @@ class Bot:
else: else:
return f"Unknown tool: {tool_name}" return f"Unknown tool: {tool_name}"
async def _search_room_history(self, room_id: str, query: str, limit: int = 200) -> str:
"""Search room message history for messages matching a query string."""
limit = min(limit, 500)
query_lower = query.lower()
try:
# Paginate through room history
matches = []
token = self.client.next_batch or ""
fetched = 0
while fetched < limit:
batch_size = min(100, limit - fetched)
resp = await self.client.room_messages(
room_id, start=token, limit=batch_size
)
if not hasattr(resp, "chunk") or not resp.chunk:
break
for evt in resp.chunk:
if not hasattr(evt, "body"):
continue
if query_lower in evt.body.lower():
ts = evt.server_timestamp / 1000
date_str = time.strftime("%Y-%m-%d %H:%M", time.gmtime(ts))
sender_name = evt.sender.split(":")[0].lstrip("@")
matches.append(f"[{date_str}] {sender_name}: {evt.body[:500]}")
fetched += len(resp.chunk)
token = resp.end
if not token:
break
if not matches:
return f"No messages found matching '{query}' in the last {fetched} messages."
# Return newest first (matches are in reverse chronological from pagination)
result = f"Found {len(matches)} message(s) matching '{query}' (scanned {fetched} messages):\n\n"
result += "\n\n---\n\n".join(matches[:20]) # cap at 20 results
if len(matches) > 20:
result += f"\n\n... and {len(matches) - 20} more matches."
return result
except Exception:
logger.warning("Room history search failed", exc_info=True)
return "Failed to search room history."
# -- Escalation patterns for model routing -- # -- Escalation patterns for model routing --
_ESCALATION_KEYWORDS = re.compile( _ESCALATION_KEYWORDS = re.compile(
r"\b(debug|architecture|algorithm|regex|sql|refactor|optimize|migration" r"\b(debug|architecture|algorithm|regex|sql|refactor|optimize|migration"