from typing import Type, Optional from pydantic import BaseModel, Field import requests import os from urllib.parse import urlencode from crewai.tools import BaseTool from pydantic import BaseModel import requests import geocoder FSQ_API_BASE = "https://api.foursquare.com/v3" FSQ_SERVICE_TOKEN = os.getenv("FOURSQUARE_SERVICE_TOKEN") # Wrapper function to execute the API call def submit_request(endpoint, params): headers = { "accept": "application/json", "Authorization": f"{FSQ_SERVICE_TOKEN}", # "X-Places-Api-Version": "2025-02-05" } encoded_params = urlencode(params) url = f"{FSQ_API_BASE}{endpoint}?{encoded_params}" print(url) try: response = requests.get(url, headers=headers) print(response) if response.status_code == 200: return response.json() else: print('Error:', response.status_code) return None except requests.exceptions.RequestException as e: print('Error:', e) return None class SeachNearRegionSchema(BaseModel): query: str near: str limit: Optional[int] = Field(default=5, description="Maximum number of results to return") class SearchNearRegionTool(BaseTool): name: str = 'Search Near a Named Region Tool' description: str ="""Search for places near a particular named region. Has a geographic region (e.g., New Delhi, India) and looks for specific concepts (e.g., coffee shop, restaurants) in that geographic region""" args_schema: Type[BaseModel] = SeachNearRegionSchema def _run(self, query: str, near: str, limit: Optional[int] = 5): params = { "query": query, "near": near, "limit": limit } return submit_request("/places/search", params) class SeachNearPointSchema(BaseModel): query: str ll: str = Field(description="Comma separated latitude and longitude pair (e.g., 40.74,-74.0)") radius: int = Field(default=2000, description="Maximum radius within which to search", limit=100000) limit: Optional[int] = Field(default=5, description="Maximum number of results to return") class SearchNearPointTool(BaseTool): name: str = 'Search Near a Point Tool' description: str ="""Search for places near a particular point. Looks for concepts (e.g., coffee shop, Hard Rock Cafe) using comma separated latitude and longitude pair (e.g., 40.74,-74.0) and radius which is defined in metres (e.g., 1000)""" args_schema: Type[BaseModel] = SeachNearPointSchema def _run(self, query: str, ll: str, radius: Optional[int] = 2000, limit: Optional[int] = 5): params = { "query": query, "ll": ll, "radius": radius, "limit": limit } return submit_request("/places/search", params) class PlaceSnapSchema(BaseModel): ll: str = Field(description="Comma separated latitude and longitude pair (e.g., 40.74,-74.0)") class PlaceSnapTool(BaseTool): name: str = "Get the place tool" description: str = """Get the most likely place the user is at based on their reported location. Takes in a comma separated latitude and longitude pair as input""" args_schema: Type[BaseModel] = PlaceSnapSchema def _run(self, ll: str): params = { "ll": ll, "limit": 1 } return submit_request("/geotagging/candidates", params) class PlaceDetailsSchema(BaseModel): id: str class GetPlaceDetailsTool(BaseTool): name: str = "Get details of a place tool" description: str = """Get detailed information about a place based on the fsq_id (foursquare id), including: description, phone, website, social media, hours, popular hours, rating (out of 10), price, menu, top photos, top tips (mini-reviews from users), top tastes, and features such as takes reservations.""" args_schema: Type[BaseModel] = PlaceDetailsSchema def _run(self, id: str): params = { "fields": "name,description,location,distance,tel,email,website" } return submit_request(f"/places/{id}", params) class GetUserLocationTool(BaseTool): name: str = "Get user location tool" description: str = """Get user's location. Returns latitude and longitude, or else reports it could not find location. Tries to guess user's location based on ip address. Useful if the user has not provided their own precise location.""" def _run(self): location = geocoder.ip('me') if not location.ok: return "I don't know where you are" return f"{location.lat},{location.lng}" if __name__ == "__main__": tool = GetUserLocationTool() result = tool._run() print("Result: ", result)