feat(MAT-64): Add web search tool to text bot
The text bot had no websearch capability while the voice agent did. Added Brave Search integration as a web_search tool so the bot can answer questions about current events and look up information. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
48
bot.py
48
bot.py
@@ -76,6 +76,7 @@ CONFLUENCE_USER = os.environ.get("CONFLUENCE_USER", "")
|
|||||||
CONFLUENCE_TOKEN = os.environ.get("CONFLUENCE_TOKEN", "")
|
CONFLUENCE_TOKEN = os.environ.get("CONFLUENCE_TOKEN", "")
|
||||||
PORTAL_URL = os.environ.get("PORTAL_URL", "")
|
PORTAL_URL = os.environ.get("PORTAL_URL", "")
|
||||||
BOT_API_KEY = os.environ.get("BOT_API_KEY", "")
|
BOT_API_KEY = os.environ.get("BOT_API_KEY", "")
|
||||||
|
BRAVE_API_KEY = os.environ.get("BRAVE_API_KEY", "")
|
||||||
MAX_TOOL_ITERATIONS = 5
|
MAX_TOOL_ITERATIONS = 5
|
||||||
|
|
||||||
SYSTEM_PROMPT = """You are a helpful AI assistant in a Matrix chat room.
|
SYSTEM_PROMPT = """You are a helpful AI assistant in a Matrix chat room.
|
||||||
@@ -94,6 +95,7 @@ IMPORTANT RULES — FOLLOW THESE STRICTLY:
|
|||||||
- You can see and analyze images that users send. Describe what you see when asked about an image.
|
- You can see and analyze images that users send. Describe what you see when asked about an image.
|
||||||
- You can read and analyze PDF documents that users send. Summarize content and answer questions about them.
|
- You can read and analyze PDF documents that users send. Summarize content and answer questions about them.
|
||||||
- You can generate images when asked — use the generate_image tool for any image creation, drawing, or illustration requests.
|
- You can generate images when asked — use the generate_image tool for any image creation, drawing, or illustration requests.
|
||||||
|
- You can search the web using the web_search tool. Use it when users ask about current events, facts, or anything that needs up-to-date information.
|
||||||
- You can search Confluence and Jira using tools. When users ask about documentation, wiki pages, tickets, or tasks, use the appropriate tool. Use confluence_recent_pages FIRST to show recently edited pages before searching.
|
- You can search Confluence and Jira using tools. When users ask about documentation, wiki pages, tickets, or tasks, use the appropriate tool. Use confluence_recent_pages FIRST to show recently edited pages before searching.
|
||||||
- When creating Jira issues, always confirm the project key and summary with the user before creating.
|
- When creating Jira issues, always confirm the project key and summary with the user before creating.
|
||||||
- If a user's Atlassian account is not connected, tell them to connect it at matrixhost.eu/settings and provide the link.
|
- If a user's Atlassian account is not connected, tell them to connect it at matrixhost.eu/settings and provide the link.
|
||||||
@@ -268,7 +270,23 @@ ATLASSIAN_TOOLS = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
ALL_TOOLS = IMAGE_GEN_TOOLS + ATLASSIAN_TOOLS
|
WEB_SEARCH_TOOLS = [{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "web_search",
|
||||||
|
"description": "Search the web for current information. Use when the user asks about recent events, facts, or anything that needs up-to-date information from the internet.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"query": {"type": "string", "description": "Search query"},
|
||||||
|
"count": {"type": "integer", "description": "Number of results (default 5)", "default": 5},
|
||||||
|
},
|
||||||
|
"required": ["query"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
|
||||||
|
ALL_TOOLS = IMAGE_GEN_TOOLS + WEB_SEARCH_TOOLS + ATLASSIAN_TOOLS
|
||||||
|
|
||||||
ATLASSIAN_NOT_CONNECTED_MSG = (
|
ATLASSIAN_NOT_CONNECTED_MSG = (
|
||||||
"Your Atlassian account is not connected. "
|
"Your Atlassian account is not connected. "
|
||||||
@@ -1923,6 +1941,30 @@ class Bot:
|
|||||||
finally:
|
finally:
|
||||||
self._pending_connects.pop(sender, None)
|
self._pending_connects.pop(sender, None)
|
||||||
|
|
||||||
|
async def _brave_search(self, query: str, count: int = 5) -> str:
|
||||||
|
"""Call Brave Search API and return formatted results."""
|
||||||
|
if not BRAVE_API_KEY:
|
||||||
|
return "Web search unavailable (no API key configured)."
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||||
|
resp = await client.get(
|
||||||
|
"https://api.search.brave.com/res/v1/web/search",
|
||||||
|
headers={"Accept": "application/json", "X-Subscription-Token": BRAVE_API_KEY},
|
||||||
|
params={"q": query, "count": count, "text_decorations": False},
|
||||||
|
)
|
||||||
|
resp.raise_for_status()
|
||||||
|
data = resp.json()
|
||||||
|
results = data.get("web", {}).get("results", [])
|
||||||
|
if not results:
|
||||||
|
return "No results found."
|
||||||
|
lines = []
|
||||||
|
for r in results[:count]:
|
||||||
|
lines.append(f"- {r.get('title', '')}: {r.get('description', '')} ({r.get('url', '')})")
|
||||||
|
return "\n".join(lines)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("Brave search error: %s", exc)
|
||||||
|
return f"Search failed: {exc}"
|
||||||
|
|
||||||
async def _execute_tool(self, tool_name: str, args: dict, sender: str, room_id: str) -> str:
|
async def _execute_tool(self, tool_name: str, args: dict, sender: str, room_id: str) -> str:
|
||||||
"""Execute a tool call and return the result as a string."""
|
"""Execute a tool call and return the result as a string."""
|
||||||
# Image generation — no Atlassian token needed
|
# Image generation — no Atlassian token needed
|
||||||
@@ -1930,6 +1972,10 @@ class Bot:
|
|||||||
await self._generate_and_send_image(room_id, args.get("prompt", ""))
|
await self._generate_and_send_image(room_id, args.get("prompt", ""))
|
||||||
return "Image generated and sent to the room."
|
return "Image generated and sent to the room."
|
||||||
|
|
||||||
|
# Web search — no auth needed
|
||||||
|
if tool_name == "web_search":
|
||||||
|
return await self._brave_search(args.get("query", ""), args.get("count", 5))
|
||||||
|
|
||||||
# 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:
|
||||||
|
|||||||
Reference in New Issue
Block a user