File size: 11,540 Bytes
3c84178 |
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 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
from crewai.tools import BaseTool
from typing import Optional, Type, Any
from pydantic import BaseModel, Field
import logging
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class SearchInput(BaseModel):
"""Input schema for the search tool"""
query: str = Field(..., description="The search query to find information")
class DuckDuckGoSearchTool(BaseTool):
"""DuckDuckGo search tool that uses MCP server"""
name: str = "duckduckgo_search"
description: str = "Search the web using DuckDuckGo search engine via MCP server. Use this tool to find current information about locations, attractions, restaurants, weather, and travel-related topics."
args_schema: Type[BaseModel] = SearchInput
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Initialize MCP connection after parent initialization
self._initialize_mcp()
def _initialize_mcp(self) -> None:
"""Initialize connection to MCP server and get search tool"""
try:
logger.info("π Initializing MCP client...")
# Try to import and initialize MCP client
try:
from smolagents.mcp_client import MCPClient
self._mcp_client = MCPClient(
{"url": "https://agents-mcp-hackathon-duckduckgo-mcp-server.hf.space/gradio_api/mcp/sse",
"transport": "sse"}
)
# Get available tools from MCP server
tools = self._mcp_client.get_tools()
logger.info(f"π MCP returned {len(tools) if isinstance(tools, list) else 0} tools")
if isinstance(tools, list) and len(tools) > 0:
# Use the first tool (should be the DuckDuckGo search tool)
self._search_tool = tools[0]
logger.info("β
MCP search tool initialized successfully")
else:
raise Exception("No tools available from MCP server")
except ImportError:
logger.error("β smolagents not available")
self._mcp_client = None
self._search_tool = None
except Exception as e:
logger.error(f"β Failed to initialize MCP client: {e}")
self._mcp_client = None
self._search_tool = None
def _run(self, query: str, **kwargs: Any) -> str:
"""Execute the search using MCP DuckDuckGo tool"""
try:
if not hasattr(self, '_mcp_client') or not self._mcp_client or not hasattr(self, '_search_tool') or not self._search_tool:
return f"β MCP server not available. Cannot search for: {query}"
logger.info(f"π Executing search for: {query}")
# Call the MCP search tool
result = self._search_tool(query=query, max_results=5)
logger.info(f"π Search completed, processing results...")
# Format the results for better readability
if isinstance(result, (list, tuple)):
if not result:
return f"π No results found for query: {query}"
formatted_results = []
for i, item in enumerate(result[:5], 1):
if isinstance(item, dict):
title = item.get('title', 'No title available')
url = item.get('url', item.get('link', 'No URL available'))
snippet = item.get('snippet', item.get('description', item.get('body', 'No description available')))
formatted_results.append(
f"π Result {i}:\n"
f" π Title: {title}\n"
f" π URL: {url}\n"
f" π Description: {snippet}\n"
)
else:
formatted_results.append(f"π Result {i}: {str(item)}\n")
final_result = "\n".join(formatted_results)
logger.info(f"β
Formatted {len(formatted_results)} search results")
return final_result
elif isinstance(result, str):
return f"π Search Result:\n{result}"
else:
return f"π Search completed:\n{str(result)}"
except Exception as e:
error_msg = f"β Search failed: {str(e)}"
logger.error(error_msg)
return f"{error_msg}\nπ Query: {query}"
# Fallback search tool for when MCP is not available
class FallbackSearchTool(BaseTool):
name: str = "fallback_search"
description: str = "Fallback search tool that provides general travel information"
args_schema: Type[BaseModel] = SearchInput
def _run(self, query: str, **kwargs: Any) -> str:
"""Provide fallback search results with general travel information"""
logger.info(f"π Fallback search for: {query}")
# Basic travel information based on common queries
fallback_info = {
"vizag": {
"title": "Visakhapatnam (Vizag), Andhra Pradesh, India",
"info": """
## Top Attractions
- **RK Beach (Ramakrishna Beach)** - Popular beach with shopping and dining
- **Submarine Museum (INS Kurusura)** - Unique submarine converted to museum
- **Kailasagiri Hill Park** - Scenic hilltop park with city views
- **Araku Valley** - Beautiful hill station 3 hours away
- **Borra Caves** - Stunning limestone caves near Araku
- **Simhachalam Temple** - Ancient temple dedicated to Lord Narasimha
## Local Cuisine
- Andhra-style spicy seafood
- Traditional South Indian breakfast items
- Local street food at Beach Road
## Best Time to Visit
October to March (pleasant weather)
## Transportation
- Auto-rickshaws and taxis available
- Train connectivity to Araku Valley
- Airport: Visakhapatnam Airport (VTZ)
"""
},
"goa": {
"title": "Goa, India",
"info": """
## Popular Beaches
- **North Goa**: Baga, Calangute, Anjuna, Vagator
- **South Goa**: Palolem, Colva, Benaulim (quieter beaches)
## Attractions
- **Old Goa Churches** - UNESCO World Heritage Sites
- **Dudhsagar Falls** - Spectacular waterfall (seasonal)
- **Spice Plantations** - Guided tours available
- **Flea Markets** - Anjuna and Mapusa markets
## Activities
- Water sports, casino cruises, dolphin watching
- Portuguese heritage tours
- Beach parties and nightlife
## Best Time
November to February (dry season)
"""
},
"dubai": {
"title": "Dubai, United Arab Emirates",
"info": """
## Iconic Attractions
- **Burj Khalifa** - World's tallest building with observation decks
- **Dubai Mall** - Massive shopping and entertainment complex
- **Palm Jumeirah** - Artificial palm-shaped island
- **Dubai Fountain** - World's largest choreographed fountain
- **Burj Al Arab** - Iconic sail-shaped luxury hotel
- **Dubai Marina** - Modern waterfront district
## Activities
- Desert safari with dune bashing
- Dubai Creek dhow cruise
- Ski Dubai (indoor skiing)
- Gold and Spice Souks
## Best Time
November to March (cooler weather)
"""
}
}
# Try to match query with fallback info
query_lower = query.lower()
for location, info in fallback_info.items():
if location in query_lower:
return f"""
# Search Results for: {query}
## {info['title']}
{info['info']}
---
**Note**: This is fallback information as the live search service is currently unavailable.
For the most current information, please verify details from official tourism websites.
**Technical Note**: To enable live search, ensure MCP server is running on https://huggingface.co/spaces/Agents-MCP-Hackathon/DuckDuckGo-MCP-Server
"""
# Generic fallback response
return f"""
# Search Results for: {query}
## Search Service Currently Unavailable
The live search service is not currently available, but here are some general recommendations:
### Planning Your Trip
1. **Research official tourism websites** for your destination
2. **Check travel advisories** and entry requirements
3. **Compare prices** on multiple booking platforms
4. **Read recent reviews** from other travelers
5. **Contact local tourism boards** for current information
### Popular Travel Resources
- **Booking Platforms**: Booking.com, Expedia, MakeMyTrip
- **Reviews**: TripAdvisor, Google Reviews
- **Transportation**: Official airline and railway websites
- **Local Info**: Tourism board websites
---
**Technical Note**: To enable live search functionality, please ensure the MCP server is running on https://huggingface.co/spaces/Agents-MCP-Hackathon/DuckDuckGo-MCP-Server
"""
# Create the search tool instance
try:
search = DuckDuckGoSearchTool()
logger.info("β
DuckDuckGoSearchTool created successfully")
except Exception as e:
logger.error(f"β Failed to create DuckDuckGoSearchTool: {e}")
search = FallbackSearchTool()
logger.warning("β οΈ Using fallback search tool")
# Test function for debugging
def test_mcp_connection():
"""Test MCP connection independently"""
try:
logger.info("π§ͺ Testing MCP connection...")
try:
from smolagents.mcp_client import MCPClient
mcp_client = MCPClient(
{"url": "https://agents-mcp-hackathon-duckduckgo-mcp-server.hf.space/gradio_api/mcp/sse",
"transport": "sse"}
)
tools = mcp_client.get_tools()
logger.info(f"π§ͺ Test successful: {len(tools) if isinstance(tools, list) else 0} tools available")
if isinstance(tools, list) and len(tools) > 0:
# Test a simple search
search_func = tools[0]
result = search_func(query="test", max_results=2)
logger.info(f"π§ͺ Test search result type: {type(result)}")
return True
else:
logger.warning("π§ͺ No tools available in MCP server")
return False
except ImportError:
logger.error("π§ͺ smolagents package not available")
return False
except Exception as e:
logger.error(f"π§ͺ MCP connection test failed: {e}")
return False
if __name__ == "__main__":
print("π Testing DuckDuckGo MCP Tool")
print("=" * 50)
# Test MCP connection
if test_mcp_connection():
print("β
MCP connection test passed")
# Test the actual tool
test_result = search._run("Vizag attractions")
print("\nπ Tool Test Result:")
print(test_result)
else:
print("β MCP connection test failed")
print("π‘ Make sure your MCP server is running on https://huggingface.co/spaces/Agents-MCP-Hackathon/DuckDuckGo-MCP-Server")
# Test fallback tool
print("\nπ Testing Fallback Tool:")
fallback_result = search._run("Vizag attractions")
print(fallback_result) |