From dbfac7d611d87421c2d2abd931a3ccd70282b9e1 Mon Sep 17 00:00:00 2001 From: Bently Date: Wed, 25 Sep 2024 11:07:58 +0100 Subject: [PATCH] Feat(Builder): Add Google Maps Search Block --- autogpt_platform/backend/.env.example | 2 + .../backend/backend/blocks/google_maps.py | 124 ++++++++++++++++++ .../backend/backend/util/settings.py | 2 + autogpt_platform/backend/poetry.lock | 15 ++- autogpt_platform/backend/pyproject.toml | 1 + 5 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 autogpt_platform/backend/backend/blocks/google_maps.py diff --git a/autogpt_platform/backend/.env.example b/autogpt_platform/backend/.env.example index de15de840ab1..ea5cdea6916c 100644 --- a/autogpt_platform/backend/.env.example +++ b/autogpt_platform/backend/.env.example @@ -55,6 +55,8 @@ SMTP_PASSWORD= MEDIUM_API_KEY= MEDIUM_AUTHOR_ID= +# Google Maps +GOOGLE_MAPS_API_KEY= # Logging Configuration LOG_LEVEL=INFO diff --git a/autogpt_platform/backend/backend/blocks/google_maps.py b/autogpt_platform/backend/backend/blocks/google_maps.py new file mode 100644 index 000000000000..fcf1e4072fa5 --- /dev/null +++ b/autogpt_platform/backend/backend/blocks/google_maps.py @@ -0,0 +1,124 @@ +import googlemaps + +from typing import List +from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema +from backend.data.model import BlockSecret, SchemaField, SecretField + + +class GoogleMapsSearchBlock(Block): + class Input(BlockSchema): + api_key: BlockSecret = SecretField( + key="google_maps_api_key", + description="Google Maps API Key", + ) + query: str = SchemaField( + description="Search query for local businesses", + placeholder="e.g., 'restaurants in New York'", + ) + radius: int = SchemaField( + description="Search radius in meters (max 50000)", + default=5000, + ge=1, + le=50000, + ) + max_results: int = SchemaField( + description="Maximum number of results to return", + default=20, + ge=1, + le=60, + ) + + class Output(BlockSchema): + businesses: List[dict] = SchemaField(description="List of businesses found") + error: str = SchemaField(description="Error message if the search failed") + + def __init__(self): + super().__init__( + id="f47ac10b-58cc-4372-a567-0e02b2c3d479", + description="This block searches for local businesses using Google Maps API.", + categories={BlockCategory.SEARCH}, + input_schema=GoogleMapsSearchBlock.Input, + output_schema=GoogleMapsSearchBlock.Output, + test_input={ + "api_key": "your_test_api_key", + "query": "restaurants in new york", + "radius": 5000, + "max_results": 5, + }, + test_output=[ + ( + "businesses", + [ + { + "name": "Test Restaurant", + "address": "123 Test St, New York, NY 10001", + "phone": "+1 (555) 123-4567", + "rating": 4.5, + "reviews": 100, + "website": "https://testrestaurant.com", + } + ], + ), + ], + test_mock={ + "search_places": lambda *args, **kwargs: [ + { + "name": "Test Restaurant", + "address": "123 Test St, New York, NY 10001", + "phone": "+1 (555) 123-4567", + "rating": 4.5, + "reviews": 100, + "website": "https://testrestaurant.com", + } + ] + }, + ) + + def run(self, input_data: Input) -> BlockOutput: + try: + businesses = self.search_places( + input_data.api_key.get_secret_value(), + input_data.query, + input_data.radius, + input_data.max_results, + ) + yield "businesses", businesses + except Exception as e: + yield "error", str(e) + + def search_places(self, api_key, query, radius, max_results): + client = googlemaps.Client(key=api_key) + return self._search_places(client, query, radius, max_results) + + def _search_places(self, client, query, radius, max_results): + results = [] + next_page_token = None + + while len(results) < max_results: + response = client.places( + query=query, + radius=radius, + page_token=next_page_token, + ) + + for place in response["results"]: + if len(results) >= max_results: + break + + place_details = client.place(place["place_id"])["result"] + results.append( + { + "name": place_details.get("name", ""), + "address": place_details.get("formatted_address", ""), + "phone": place_details.get("formatted_phone_number", ""), + "rating": place_details.get("rating", 0), + "reviews": place_details.get("user_ratings_total", 0), + "website": place_details.get("website", ""), + } + ) + + next_page_token = response.get("next_page_token") + if not next_page_token: + break + + return results diff --git a/autogpt_platform/backend/backend/util/settings.py b/autogpt_platform/backend/backend/util/settings.py index 230fa14f5282..59fc46f3d3cf 100644 --- a/autogpt_platform/backend/backend/util/settings.py +++ b/autogpt_platform/backend/backend/util/settings.py @@ -169,6 +169,8 @@ class Secrets(UpdateTrackingModel["Secrets"], BaseSettings): sentry_dsn: str = Field(default="", description="Sentry DSN") + google_maps_api_key: str = Field(default="", description="Google Maps API Key") + # Add more secret fields as needed model_config = SettingsConfigDict( diff --git a/autogpt_platform/backend/poetry.lock b/autogpt_platform/backend/poetry.lock index e76165a14420..b6b9047a2c96 100644 --- a/autogpt_platform/backend/poetry.lock +++ b/autogpt_platform/backend/poetry.lock @@ -989,6 +989,19 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4 [package.extras] grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] +[[package]] +name = "googlemaps" +version = "4.10.0" +description = "Python client library for Google Maps Platform" +optional = false +python-versions = ">=3.5" +files = [ + {file = "googlemaps-4.10.0.tar.gz", hash = "sha256:3055fcbb1aa262a9159b589b5e6af762b10e80634ae11c59495bd44867e47d88"}, +] + +[package.dependencies] +requests = ">=2.20.0,<3.0" + [[package]] name = "gotrue" version = "2.8.1" @@ -3623,4 +3636,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "311c527a1d1947af049dac27c7a2b2f49d7fa4cdede52ef436422a528b0ad866" +content-hash = "f42fdf521b0476048626238ee631b618a71ed079c369c28035a899f2252369c9" diff --git a/autogpt_platform/backend/pyproject.toml b/autogpt_platform/backend/pyproject.toml index c7c3be2005dc..3a58cfa75cc1 100644 --- a/autogpt_platform/backend/pyproject.toml +++ b/autogpt_platform/backend/pyproject.toml @@ -44,6 +44,7 @@ tenacity = "^8.3.0" uvicorn = { extras = ["standard"], version = "^0.30.1" } websockets = "^12.0" youtube-transcript-api = "^0.6.2" +googlemaps = "^4.10.0" [tool.poetry.group.dev.dependencies] poethepoet = "^0.26.1"