botty-mcbotface / tools.py
tsilva's picture
Upload folder using huggingface_hub
c4d4675 verified
raw
history blame
19.7 kB
import os
TOOL_SAVE_MEMORY = {
"name" : "tool_save_memory",
"description": "Used to store information the user requested to remember. Can optionally specify index to overwrite existing memories. Memorized information will be used in system prompt.",
"input_schema": {
"type": "object",
"properties": {
"memory_data": {
"type": "string",
"description": "Summarized version of the information to remember, compressed to use the least tokens possible while preserving all relevant facts"
},
"index": {
"type": "integer",
"description": "Optional index where to store the memory. If provided, overwrites existing memory at that index. If not provided, appends to end of memory list.",
"minimum": 0
}
},
"required": ["memory_data"]
}
}
def tool_save_memory(app_context, memory_data: str, index: int = None):
system_memory = app_context["system_memory"]
system_memory_max_size = app_context["system_memory_max_size"]
if index is not None and index < len(system_memory):
system_memory[index] = memory_data
status = f"βœ… Updated memory `{index}`: `{memory_data}`."
else:
system_memory.append(memory_data)
system_memory[:] = system_memory[:system_memory_max_size]
status = f"βœ… Added new memory: `{memory_data}`."
yield {
"status" : status,
"result" : None
}
TOOL_DELETE_MEMORY = {
"name": "tool_delete_memory",
"description": "Used to discard information that was previously stored in memory.",
"input_schema": {
"type": "object",
"properties": {
"memory_index": {
"type": "integer",
"description": "The index of the memory slot to discard. The system prompt enumerates all memories at all times, prefixed by their memory slot, this is what should be referenced."
}
},
"required": ["memory_index"]
}
}
def tool_delete_memory(app_context, memory_index: int):
system_memory = app_context["system_memory"]
memory_data = system_memory[memory_index]
system_memory.pop(memory_index)
yield {
"status" : f"βœ… Deleted memory: `{memory_data}`.",
"result" : None
}
TOOL_PLACES_NEARBY = {
"name": "tool_places_nearby",
"description": "Search for places using Google Places API with various filtering options",
"input_schema": {
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "Type of place to search for",
"enum": [
# Automotive
"car_dealer", "car_rental", "car_repair", "car_wash", "electric_vehicle_charging_station", "gas_station", "parking", "rest_stop",
# Business
"corporate_office", "farm", "ranch",
# Culture
"art_gallery", "art_studio", "auditorium", "cultural_landmark", "historical_place", "monument", "museum", "performing_arts_theater", "sculpture",
# Education
"library", "preschool", "primary_school", "school", "secondary_school", "university",
# Entertainment and Recreation
"adventure_sports_center", "amphitheatre", "amusement_center", "amusement_park", "aquarium", "banquet_hall", "barbecue_area", "botanical_garden",
"bowling_alley", "casino", "childrens_camp", "comedy_club", "community_center", "concert_hall", "convention_center", "cultural_center",
"cycling_park", "dance_hall", "dog_park", "event_venue", "ferris_wheel", "garden", "hiking_area", "historical_landmark", "internet_cafe",
"karaoke", "marina", "movie_rental", "movie_theater", "national_park", "night_club", "observation_deck", "off_roading_area", "opera_house",
"park", "philharmonic_hall", "picnic_ground", "planetarium", "plaza", "roller_coaster", "skateboard_park", "state_park", "tourist_attraction",
"video_arcade", "visitor_center", "water_park", "wedding_venue", "wildlife_park", "wildlife_refuge", "zoo",
# Facilities
"public_bath", "public_bathroom", "stable",
# Finance
"accounting", "atm", "bank",
# Food and Drink
"acai_shop", "afghani_restaurant", "african_restaurant", "american_restaurant", "asian_restaurant", "bagel_shop", "bakery", "bar",
"bar_and_grill", "barbecue_restaurant", "brazilian_restaurant", "breakfast_restaurant", "brunch_restaurant", "buffet_restaurant", "cafe",
"cafeteria", "candy_store", "cat_cafe", "chinese_restaurant", "chocolate_factory", "chocolate_shop", "coffee_shop", "confectionery",
"deli", "dessert_restaurant", "dessert_shop", "diner", "dog_cafe", "donut_shop", "fast_food_restaurant", "fine_dining_restaurant",
"food_court", "french_restaurant", "greek_restaurant", "hamburger_restaurant", "ice_cream_shop", "indian_restaurant", "indonesian_restaurant",
"italian_restaurant", "japanese_restaurant", "juice_shop", "korean_restaurant", "lebanese_restaurant", "meal_delivery", "meal_takeaway",
"mediterranean_restaurant", "mexican_restaurant", "middle_eastern_restaurant", "pizza_restaurant", "pub", "ramen_restaurant", "restaurant",
"sandwich_shop", "seafood_restaurant", "spanish_restaurant", "steak_house", "sushi_restaurant", "tea_house", "thai_restaurant",
"turkish_restaurant", "vegan_restaurant", "vegetarian_restaurant", "vietnamese_restaurant", "wine_bar",
# Geographical Areas
"administrative_area_level_1", "administrative_area_level_2", "country", "locality", "postal_code", "school_district",
# Government
"city_hall", "courthouse", "embassy", "fire_station", "government_office", "local_government_office", "neighborhood_police_station",
"police", "post_office",
# Health and Wellness
"chiropractor", "dental_clinic", "dentist", "doctor", "drugstore", "hospital", "massage", "medical_lab", "pharmacy", "physiotherapist",
"sauna", "skin_care_clinic", "spa", "tanning_studio", "wellness_center", "yoga_studio",
# Housing
"apartment_building", "apartment_complex", "condominium_complex", "housing_complex",
# Lodging
"bed_and_breakfast", "budget_japanese_inn", "campground", "camping_cabin", "cottage", "extended_stay_hotel", "farmstay", "guest_house",
"hostel", "hotel", "inn", "japanese_inn", "lodging", "mobile_home_park", "motel", "private_guest_room", "resort_hotel", "rv_park",
# Natural Features
"beach",
# Places of Worship
"church", "hindu_temple", "mosque", "synagogue",
# Services
"astrologer", "barber_shop", "beautician", "beauty_salon", "body_art_service", "catering_service", "cemetery", "child_care_agency",
"consultant", "courier_service", "electrician", "florist", "food_delivery", "foot_care", "funeral_home", "hair_care", "hair_salon",
"insurance_agency", "laundry", "lawyer", "locksmith", "makeup_artist", "moving_company", "nail_salon", "painter", "plumber",
"psychic", "real_estate_agency", "roofing_contractor", "storage", "summer_camp_organizer", "tailor", "telecommunications_service_provider",
"tour_agency", "tourist_information_center", "travel_agency", "veterinary_care",
# Shopping
"asian_grocery_store", "auto_parts_store", "bicycle_store", "book_store", "butcher_shop", "cell_phone_store", "clothing_store",
"convenience_store", "department_store", "discount_store", "electronics_store", "food_store", "furniture_store", "gift_shop",
"grocery_store", "hardware_store", "home_goods_store", "home_improvement_store", "jewelry_store", "liquor_store", "market", "pet_store",
"shoe_store", "shopping_mall", "sporting_goods_store", "store", "supermarket", "warehouse_store", "wholesaler",
# Sports
"arena", "athletic_field", "fishing_charter", "fishing_pond", "fitness_center", "golf_course", "gym", "ice_skating_rink", "playground",
"ski_resort", "sports_activity_location", "sports_club", "sports_coaching", "sports_complex", "stadium", "swimming_pool",
# Transportation
"airport", "airstrip", "bus_station", "bus_stop", "ferry_terminal", "heliport", "international_airport", "light_rail_station",
"park_and_ride", "subway_station", "taxi_stand", "train_station", "transit_depot", "transit_station", "truck_stop",
# Table B Additional Types
"administrative_area_level_3", "administrative_area_level_4", "administrative_area_level_5", "administrative_area_level_6",
"administrative_area_level_7", "archipelago", "colloquial_area", "continent", "establishment", "finance", "floor", "food",
"general_contractor", "geocode", "health", "intersection", "landmark", "natural_feature", "neighborhood", "place_of_worship",
"plus_code", "point_of_interest", "political", "post_box", "postal_code_prefix", "postal_code_suffix", "postal_town", "premise",
"room", "route", "street_address", "street_number", "sublocality", "sublocality_level_1", "sublocality_level_2", "sublocality_level_3",
"sublocality_level_4", "sublocality_level_5", "subpremise", "town_square"
]
},
"location": {
"type": "object",
"properties": {
"latitude": {"type": "number", "minimum": -90, "maximum": 90},
"longitude": {"type": "number", "minimum": -180, "maximum": 180}
},
"required": ["latitude", "longitude"],
"description": "Geographic coordinates of the search center point"
},
"radius": {
"type": "integer",
"description": "Search radius in meters",
"minimum": 1,
"maximum": 50000
},
"keyword": {
"type": "string",
"description": "Term to match against all content indexed for this place"
},
"language": {
"type": "string",
"description": "The language code for the results (e.g., 'en', 'pt')"
},
"min_price": {
"type": "integer",
"minimum": 0,
"maximum": 4,
"description": "Minimum price level (0=most affordable, 4=most expensive)"
},
"max_price": {
"type": "integer",
"minimum": 0,
"maximum": 4,
"description": "Maximum price level (0=most affordable, 4=most expensive)"
},
"name": {
"type": "string",
"description": "Terms to match against place names"
},
"open_now": {
"type": "boolean",
"description": "Return only places that are currently open"
},
"rank_by": {
"type": "string",
"enum": ["prominence", "distance"],
"description": "Order in which to rank results"
},
"page_token": {
"type": "string",
"description": "Token for retrieving the next page of results"
}
},
"required": ["location"]
}
}
def tool_places_nearby(
app_context,
location: dict,
type: str = None,
radius: int = None,
keyword: str = None,
language: str = None,
min_price: int = None,
max_price: int = None,
name: str = None,
open_now: bool = False,
rank_by: str = None,
page_token: str = None
) -> dict:
import googlemaps
yield {"status" : f"⏳ Searching for locations..."}
gmaps = googlemaps.Client(key=os.getenv('GOOGLE_MAPS_API_KEY'))
# Convert location dict to tuple
location_tuple = (location['latitude'], location['longitude'])
# Build params dict with only non-None values
params = {
'type': type,
'location': location_tuple,
'keyword': keyword,
'language': language,
'min_price': min_price,
'max_price': max_price,
'name': name,
'open_now': open_now,
'rank_by': rank_by,
'page_token': page_token
}
# Add radius if specified (required unless rank_by=distance)
if radius is not None: params['radius'] = radius
elif rank_by != 'distance': params['radius'] = 1000 # Default radius
# Remove None values
params = {k: v for k, v in params.items() if v is not None}
# Make the API call
result = gmaps.places_nearby(**params)
locations = result.get('results', [])
yield {
"status" : f"βœ… Found `{len(locations)}` locations.",
"result" : locations
}
TOOL_CALCULATOR = {
"name": "tool_calculator",
"description": "Perform mathematical operations with error handling and precision tracking",
"input_schema": {
"type": "object",
"properties": {
"first_number": {
"type": "number",
"description": "First operand for the calculation"
},
"second_number": {
"type": "number",
"description": "Second operand for the calculation"
},
"operation": {
"type": "string",
"description": "Mathematical operation to perform",
"enum": ["add", "subtract", "multiply", "divide"]
}
},
"required": ["first_number", "second_number", "operation"]
}
}
def tool_calculator(app_context, first_number, second_number, operation: str):
x, y = first_number, second_number
result = None
if operation == "add": result = x + y
elif operation == "subtract": result = x - y
elif operation == "multiply": result = x * y
elif operation == "divide": result = x / y if y != 0 else None
yield {"result" : f"⏳ Searching for locations..."}
return result
TOOL_GEOCODE = {
"name": "tool_geocode",
"description": "Convert addresses into latitude and longitude coordinates using Google Geocoding API",
"input_schema": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "Address to convert to coordinates (e.g. 'Porto, Portugal' or 'Avenida dos Aliados, Porto')"
}
},
"required": ["address"]
}
}
def tool_geocode(app_context, address: str) -> dict:
# Haversine formula to calculate the great-circle distance
def _haversine(lat1, lon1, lat2, lon2):
import math
# Convert latitude and longitude from degrees to radians
lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])
# Radius of Earth in kilometers
R = 6371.0
# Differences in coordinates
dlat = lat2 - lat1
dlon = lon2 - lon1
# Haversine formula
a = math.sin(dlat / 2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2)**2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
distance = R * c
return distance
import googlemaps
yield {"status" : f"⏳ Geocoding '{address}'..."}
# TODO: reuse gmaps client
gmaps = googlemaps.Client(key=os.getenv('GOOGLE_MAPS_API_KEY'))
result = gmaps.geocode(address)
# Retrieve coordinates
#location = result[0]['geometry']['location']
#latitude = location["lat"]
#longitude = location["lng"]
# Retrieve bounding box
bounds = result[0]['geometry']['bounds']
northeast = bounds['northeast']
southwest = bounds['southwest']
# Calculate the center of the bounding box
center_lat = (northeast['lat'] + southwest['lat']) / 2
center_lng = (northeast['lng'] + southwest['lng']) / 2
center = {"lat": center_lat, "lng": center_lng}
# Calculate the radius of the bounding box
radius = _haversine(center_lat, center_lng, northeast['lat'], northeast['lng'])
yield {
"status" : f"βœ… Geocoded `{address}` to center=`({center_lat},{center_lng}), radius={radius}m`.",
"result" : {
"center": center,
"radius" : radius
}
}
TOOL_PLACE_DETAILS = {
"name": "tool_place_details",
"description": "Get detailed information about a specific place using its place_id from Google Places API",
"input_schema": {
"type": "object",
"properties": {
"place_id": {
"type": "string",
"description": "The place_id of the location to get details for. This can be obtained from the results of places_nearby searches."
},
"language": {
"type": "string",
"description": "The language code for the results (e.g., 'en', 'pt')"
},
"fields": {
"type": "array",
"description": "List of specific fields to return. If empty, returns all available fields.",
"items": {
"type": "string",
"enum": [
"address_component", "adr_address", "business_status",
"formatted_address", "geometry", "icon", "name",
"photo", "place_id", "plus_code", "type",
"url", "utc_offset", "vicinity", "formatted_phone_number",
"international_phone_number", "opening_hours",
"website", "price_level", "rating", "review",
"user_ratings_total"
]
}
}
},
"required": ["place_id"]
}
}
def tool_place_details(app_context, place_id: str, language: str = None, fields: list = None) -> dict:
import googlemaps
yield {"status" : f"⏳ Looking up details on location..."}
gmaps = googlemaps.Client(key=os.getenv('GOOGLE_MAPS_API_KEY'))
params = {'place_id': place_id}
if language: params['language'] = language
if fields: params['fields'] = fields
result = gmaps.place(**params)
details = result.get('result', {})
yield {
"status" : f"βœ… Location details fetched.",
"result" : details
}
TOOLS = (
(TOOL_SAVE_MEMORY, tool_save_memory),
(TOOL_DELETE_MEMORY, tool_delete_memory),
(TOOL_CALCULATOR, tool_calculator),
(TOOL_PLACES_NEARBY, tool_places_nearby),
(TOOL_GEOCODE, tool_geocode),
(TOOL_PLACE_DETAILS, tool_place_details)
)
TOOLS_SPECS = {tool[0]["name"]: tool[0] for tool in TOOLS}
TOOLS_FUNCTIONS = {tool[0]["name"]: tool[1] for tool in TOOLS}