Spaces:
Paused
Paused
File size: 6,825 Bytes
5965cc0 5a6288c 5e803ec 5965cc0 5a6288c 5965cc0 5e803ec 5965cc0 5e803ec 5a6288c 5e803ec 5a6288c 5e803ec 5a6288c 5e803ec 5a6288c 5e803ec 5a6288c 5e803ec 5a6288c 5e803ec 5a6288c 5965cc0 5a6288c 5965cc0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | """
Tool definitions (HF/OpenAI schema) and dispatcher for the VL agent.
Three tools:
- search_web: search the internet and return snippets
- final_output: signal the agent loop to stop and return an answer
- abort: signal the agent loop to stop and report a failure/reason
"""
import json
import logging
import os
import urllib.error
import urllib.parse
import urllib.request
from dotenv import load_dotenv
load_dotenv()
_BRAVE_WEB_SEARCH_URL = "https://api.search.brave.com/res/v1/web/search"
logger = logging.getLogger(__name__)
if not logger.handlers:
_handler = logging.StreamHandler()
_handler.setFormatter(logging.Formatter("%(levelname)s %(name)s: %(message)s"))
logger.addHandler(_handler)
logger.setLevel(logging.INFO)
logger.propagate = False
# ---------------------------------------------------------------------------
# Tool schemas (passed to InferenceClient.chat_completion(tools=...))
# ---------------------------------------------------------------------------
AGENT_TOOLS = [
{
"type": "function",
"function": {
"name": "search_web",
"description": (
"Search the internet for up-to-date information. "
"Use this when you need current facts, news, or data that you are "
"not confident about from your training knowledge."
),
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query to look up on the web.",
}
},
"required": ["query"],
},
},
},
{
"type": "function",
"function": {
"name": "final_output",
"description": (
"Deliver the final answer to the user. "
"Call this once you have gathered enough information and are ready "
"to give a complete, accurate response. The 'answer' field will be "
"shown directly to the user."
),
"parameters": {
"type": "object",
"properties": {
"answer": {
"type": "string",
"description": "The complete final answer to present to the user.",
}
},
"required": ["answer"],
},
},
},
{
"type": "function",
"function": {
"name": "abort",
"description": (
"Abort the current task when it cannot be completed. "
"Use this if the task is impossible, unsafe, or the user asked to stop."
),
"parameters": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"description": "Explanation of why the task is being aborted.",
}
},
"required": [],
},
},
},
]
# ---------------------------------------------------------------------------
# Tool implementations
# ---------------------------------------------------------------------------
def _brave_api_key() -> str | None:
return os.environ.get("BRAVE_SEARCH_API_KEY") or os.environ.get("BRAVE_API_KEY")
def _search_web(query: str, max_results: int = 5) -> str:
"""Run a Brave Web Search and return formatted snippets."""
count = max(1, min(max_results, 20))
logger.info("search_web input: query=%r max_results=%d", query, count)
def _finish(out: str) -> str:
logger.info("search_web output:\n%s", out)
return out
token = _brave_api_key()
if not token:
return _finish(
"Search unavailable: set BRAVE_SEARCH_API_KEY or BRAVE_API_KEY "
"in a .env file or the process environment."
)
params = urllib.parse.urlencode({"q": query, "count": str(count)})
url = f"{_BRAVE_WEB_SEARCH_URL}?{params}"
req = urllib.request.Request(
url,
headers={
"X-Subscription-Token": token,
"Accept": "application/json",
},
method="GET",
)
try:
with urllib.request.urlopen(req, timeout=30) as resp:
payload = json.loads(resp.read().decode())
except urllib.error.HTTPError as exc:
detail = exc.read().decode(errors="replace")[:800]
return _finish(f"Brave Search API error ({exc.code}): {detail}")
except urllib.error.URLError as exc:
return _finish(f"Search request failed: {exc.reason or exc}")
except json.JSONDecodeError as exc:
return _finish(f"Search returned invalid JSON: {exc}")
results = (payload.get("web") or {}).get("results") or []
if not results:
return _finish("No results found for the given query.")
blocks = []
for item in results[:count]:
title = item.get("title") or ""
body = item.get("description") or ""
href = item.get("url") or ""
blocks.append(f"**{title}**\n{body}\nSource: {href}")
return _finish("\n\n---\n\n".join(blocks))
def _final_output(answer: str) -> str:
"""No-op implementation; the orchestrator reads 'answer' directly."""
return "Answer delivered."
def _abort(reason: str = "") -> str:
"""No-op implementation; the orchestrator reads 'reason' directly."""
return f"Aborted: {reason}" if reason else "Task aborted."
# ---------------------------------------------------------------------------
# Dispatcher
# ---------------------------------------------------------------------------
def dispatch_tool(tool_name: str, arguments: dict | str) -> str:
"""
Call the named tool with the given arguments and return the result string.
`arguments` may arrive as a JSON string (from the model) or already as a dict.
"""
if isinstance(arguments, str):
try:
arguments = json.loads(arguments)
except json.JSONDecodeError:
arguments = {}
if tool_name == "search_web":
query = arguments.get("query", "")
if not query:
msg = "Error: 'query' parameter is required for search_web."
logger.info("search_web input: query=%r (missing)", query)
logger.info("search_web output:\n%s", msg)
return msg
return _search_web(query)
if tool_name == "final_output":
answer = arguments.get("answer", "")
return _final_output(answer)
if tool_name == "abort":
reason = arguments.get("reason", "")
return _abort(reason)
return f"Error: unknown tool '{tool_name}'."
|