AI-Trip-Planner / tools.py
rajaramesh's picture
Add application files
3c84178
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)