Update tools/WebSearchTool.py
Browse files- tools/WebSearchTool.py +47 -29
tools/WebSearchTool.py
CHANGED
|
@@ -6,7 +6,8 @@ class SearxngSearchTool(Tool):
|
|
| 6 |
"""
|
| 7 |
A metasearch tool that queries multiple search engines (including Google, Bing, etc.)
|
| 8 |
through a free, public Searxng instance. This is a robust, keyless alternative
|
| 9 |
-
when specific search APIs are overloaded or require payment/keys.
|
|
|
|
| 10 |
"""
|
| 11 |
name = "searxng_search"
|
| 12 |
description = "Use a free, keyless metasearch engine (Searxng) to query the web for general knowledge and current information when other search tools are unavailable."
|
|
@@ -16,12 +17,18 @@ class SearxngSearchTool(Tool):
|
|
| 16 |
}
|
| 17 |
output_type = "string"
|
| 18 |
|
| 19 |
-
#
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
def forward(self, query: str) -> str:
|
| 23 |
"""
|
| 24 |
-
Executes a Searxng search query
|
| 25 |
|
| 26 |
Args:
|
| 27 |
query: The search term provided by the agent.
|
|
@@ -29,7 +36,6 @@ class SearxngSearchTool(Tool):
|
|
| 29 |
Returns:
|
| 30 |
A formatted string of search results, or an error message.
|
| 31 |
"""
|
| 32 |
-
print(f"Executing Searxng Search for: '{query}'")
|
| 33 |
|
| 34 |
params = {
|
| 35 |
"q": query,
|
|
@@ -38,32 +44,44 @@ class SearxngSearchTool(Tool):
|
|
| 38 |
"pageno": 1,
|
| 39 |
}
|
| 40 |
|
| 41 |
-
|
| 42 |
-
# Make the API request
|
| 43 |
-
response = requests.get(self.DEFAULT_INSTANCE, params=params)
|
| 44 |
-
response.raise_for_status()
|
| 45 |
-
data = response.json()
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
return "XX record info: No results found."
|
| 53 |
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
f"RESULT {i+1}: '{item.get('title', 'N/A')}'\n"
|
| 59 |
-
f"CONTENT: {item.get('content', 'No snippet available.')}\n"
|
| 60 |
-
f"SOURCE: {item.get('url', 'N/A')}"
|
| 61 |
-
)
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
"""
|
| 7 |
A metasearch tool that queries multiple search engines (including Google, Bing, etc.)
|
| 8 |
through a free, public Searxng instance. This is a robust, keyless alternative
|
| 9 |
+
when specific search APIs are overloaded or require payment/keys. It attempts
|
| 10 |
+
multiple public instances for high resilience against temporary server outages or blocks.
|
| 11 |
"""
|
| 12 |
name = "searxng_search"
|
| 13 |
description = "Use a free, keyless metasearch engine (Searxng) to query the web for general knowledge and current information when other search tools are unavailable."
|
|
|
|
| 17 |
}
|
| 18 |
output_type = "string"
|
| 19 |
|
| 20 |
+
# List of popular, generally reliable public instances.
|
| 21 |
+
# The order attempts to prioritize faster or more stable instances first.
|
| 22 |
+
PUBLIC_INSTANCES = [
|
| 23 |
+
"https://searx.be/search", # The original instance
|
| 24 |
+
"https://searx.prvcy.eu/search",
|
| 25 |
+
"https://searx.tieko.co/search",
|
| 26 |
+
"https://searx.baczek.me/search",
|
| 27 |
+
]
|
| 28 |
|
| 29 |
def forward(self, query: str) -> str:
|
| 30 |
"""
|
| 31 |
+
Executes a Searxng search query, cycling through available instances until one succeeds.
|
| 32 |
|
| 33 |
Args:
|
| 34 |
query: The search term provided by the agent.
|
|
|
|
| 36 |
Returns:
|
| 37 |
A formatted string of search results, or an error message.
|
| 38 |
"""
|
|
|
|
| 39 |
|
| 40 |
params = {
|
| 41 |
"q": query,
|
|
|
|
| 44 |
"pageno": 1,
|
| 45 |
}
|
| 46 |
|
| 47 |
+
last_error = ""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
+
# Loop through each public instance until a successful connection is made
|
| 50 |
+
for instance_url in self.PUBLIC_INSTANCES:
|
| 51 |
+
print(f"Executing Searxng Search for: '{query}' using instance: {instance_url}")
|
| 52 |
+
try:
|
| 53 |
+
# Make the API request
|
| 54 |
+
response = requests.get(instance_url, params=params, timeout=5) # Added timeout
|
| 55 |
+
response.raise_for_status()
|
| 56 |
+
data = response.json()
|
| 57 |
|
| 58 |
+
# Extract the search results
|
| 59 |
+
items = data.get('results', [])
|
|
|
|
| 60 |
|
| 61 |
+
if not items:
|
| 62 |
+
# If the instance worked but returned no results, continue to the next instance
|
| 63 |
+
print(f"Instance {instance_url} returned no results. Trying next instance.")
|
| 64 |
+
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
|
| 66 |
+
search_results = []
|
| 67 |
+
# Process the top 3 results
|
| 68 |
+
for i, item in enumerate(items[:3]):
|
| 69 |
+
search_results.append(
|
| 70 |
+
f"RESULT {i+1}: '{item.get('title', 'N/A')}'\n"
|
| 71 |
+
f"CONTENT: {item.get('content', 'No snippet available.')}\n"
|
| 72 |
+
f"SOURCE: {item.get('url', 'N/A')}"
|
| 73 |
+
)
|
| 74 |
|
| 75 |
+
# If successful, return the results immediately
|
| 76 |
+
return "\n\n---SEPARATOR---\n\n".join(search_results)
|
| 77 |
+
|
| 78 |
+
except requests.exceptions.RequestException as e:
|
| 79 |
+
# Record the error and try the next instance
|
| 80 |
+
last_error = f"Instance {instance_url} failed: {e}"
|
| 81 |
+
print(last_error)
|
| 82 |
+
except Exception as e:
|
| 83 |
+
last_error = f"Error processing Searxng results for {instance_url}: {e}"
|
| 84 |
+
print(last_error)
|
| 85 |
+
|
| 86 |
+
# If the loop finishes without returning, all instances failed.
|
| 87 |
+
return f"XX record info: No results found. All Searxng instances failed. Last error: {last_error}"
|