From 9fcdedc4b4daa0923eac4f19783e87851b0ef67c Mon Sep 17 00:00:00 2001 From: Christian Gick Date: Sun, 8 Mar 2026 17:47:51 +0200 Subject: [PATCH] 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 --- bot.py | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 9268ec4..8f9fdf2 100644 --- a/bot.py +++ b/bot.py @@ -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 = ( "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 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 token = await self.atlassian.get_token(sender) if sender else None if not token: @@ -2080,6 +2107,47 @@ class Bot: else: 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_KEYWORDS = re.compile( r"\b(debug|architecture|algorithm|regex|sql|refactor|optimize|migration"