Google Shopping search tool (#317)

This commit is contained in:
Renato Byrro 2025-03-21 21:09:54 -03:00 committed by GitHub
parent f6765bed67
commit c9ed5ce123
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 285 additions and 0 deletions

View file

@ -37,3 +37,13 @@ DEFAULT_GOOGLE_MAPS_TRAVEL_MODE = GoogleMapsTravelMode(
YOUTUBE_MAX_DESCRIPTION_LENGTH = 500
DEFAULT_YOUTUBE_SEARCH_LANGUAGE = os.getenv("ARCADE_YOUTUBE_SEARCH_LANGUAGE")
DEFAULT_YOUTUBE_SEARCH_COUNTRY = os.getenv("ARCADE_YOUTUBE_SEARCH_COUNTRY")
# ------------------------------------------------------------------------------------------------
# Google Shopping default constants
# ------------------------------------------------------------------------------------------------
DEFAULT_GOOGLE_SHOPPING_LANGUAGE = os.getenv(
"ARCADE_GOOGLE_SHOPPING_LANGUAGE", DEFAULT_GOOGLE_LANGUAGE
)
DEFAULT_GOOGLE_SHOPPING_COUNTRY = os.getenv(
"ARCADE_GOOGLE_SHOPPING_COUNTRY", DEFAULT_GOOGLE_COUNTRY
)

View file

@ -279,3 +279,191 @@ LANGUAGE_CODES = {
"zh-cn": "Chinese (Simplified)",
"zh-tw": "Chinese (Traditional)",
}
GOOGLE_DOMAIN_BY_COUNTRY_CODE = {
"ad": "google.ad",
"ae": "google.ae",
"al": "google.al",
"am": "google.am",
"as": "google.as",
"at": "google.at",
"az": "google.az",
"ba": "google.ba",
"be": "google.be",
"bf": "google.bf",
"bg": "google.bg",
"bi": "google.bi",
"bj": "google.bj",
"bs": "google.bs",
"bt": "google.bt",
"by": "google.by",
"ca": "google.ca",
"cg": "google.cg",
"cf": "google.cf",
"ch": "google.ch",
"ci": "google.ci",
"cl": "google.cl",
"cm": "google.cm",
"ao": "google.co.ao",
"bw": "google.co.bw",
"ck": "google.co.ck",
"cr": "google.co.cr",
"id": "google.co.id",
"il": "google.co.il",
"in": "google.co.in",
"jp": "google.co.jp",
"ke": "google.co.ke",
"kr": "google.co.kr",
"ls": "google.co.ls",
"ma": "google.co.ma",
"mz": "google.co.mz",
"nz": "google.co.nz",
"th": "google.co.th",
"tz": "google.co.tz",
"ug": "google.co.ug",
"uk": "google.co.uk",
"uz": "google.co.uz",
"ve": "google.co.ve",
"vi": "google.co.vi",
"za": "google.co.za",
"zm": "google.co.zm",
"zw": "google.co.zw",
"us": "google.com",
"af": "google.com.af",
"ag": "google.com.ag",
"ai": "google.com.ai",
"ar": "google.com.ar",
"au": "google.com.au",
"bd": "google.com.bd",
"bh": "google.com.bh",
"bn": "google.com.bn",
"bo": "google.com.bo",
"br": "google.com.br",
"bz": "google.com.bz",
"co": "google.com.co",
"cu": "google.com.cu",
"cy": "google.com.cy",
"do": "google.com.do",
"ec": "google.com.ec",
"eg": "google.com.eg",
"et": "google.com.et",
"fj": "google.com.fj",
"gh": "google.com.gh",
"gi": "google.com.gi",
"gt": "google.com.gt",
"hk": "google.com.hk",
"jm": "google.com.jm",
"kh": "google.com.kh",
"kw": "google.com.kw",
"lb": "google.com.lb",
"ly": "google.com.ly",
"mm": "google.com.mm",
"mt": "google.com.mt",
"mx": "google.com.mx",
"my": "google.com.my",
"na": "google.com.na",
"ng": "google.com.ng",
"ni": "google.com.ni",
"np": "google.com.np",
"om": "google.com.om",
"pa": "google.com.pa",
"pe": "google.com.pe",
"pg": "google.com.pg",
"ph": "google.com.ph",
"pk": "google.com.pk",
"pr": "google.com.pr",
"py": "google.com.py",
"qa": "google.com.qa",
"sa": "google.com.sa",
"sb": "google.com.sb",
"sg": "google.com.sg",
"sl": "google.com.sl",
"sv": "google.com.sv",
"tj": "google.com.tj",
"tr": "google.com.tr",
"tw": "google.com.tw",
"ua": "google.com.ua",
"uy": "google.com.uy",
"vc": "google.com.vc",
"vn": "google.com.vn",
"cv": "google.cv",
"cz": "google.cz",
"de": "google.de",
"dj": "google.dj",
"dk": "google.dk",
"dm": "google.dm",
"dz": "google.dz",
"ee": "google.ee",
"es": "google.es",
"fi": "google.fi",
"fm": "google.fm",
"fr": "google.fr",
"ga": "google.ga",
"ge": "google.ge",
"gl": "google.gl",
"gm": "google.gm",
"gp": "google.gp",
"gr": "google.gr",
"gy": "google.gy",
"hn": "google.hn",
"hr": "google.hr",
"ht": "google.ht",
"hu": "google.hu",
"ie": "google.ie",
"iq": "google.iq",
"is": "google.is",
"it": "google.it",
"je": "google.je",
"jo": "google.jo",
"kg": "google.kg",
"ki": "google.ki",
"kz": "google.kz",
"la": "google.la",
"li": "google.li",
"lk": "google.lk",
"lt": "google.lt",
"lu": "google.lu",
"lv": "google.lv",
"md": "google.md",
"mg": "google.mg",
"mk": "google.mk",
"ml": "google.ml",
"mn": "google.mn",
"ms": "google.ms",
"mu": "google.mu",
"mv": "google.mv",
"mw": "google.mw",
"ne": "google.ne",
"nl": "google.nl",
"no": "google.no",
"nr": "google.nr",
"nu": "google.nu",
"pl": "google.pl",
"ps": "google.ps",
"pt": "google.pt",
"ro": "google.ro",
"rs": "google.rs",
"ru": "google.ru",
"rw": "google.rw",
"sc": "google.sc",
"se": "google.se",
"sh": "google.sh",
"si": "google.si",
"sk": "google.sk",
"sm": "google.sm",
"sn": "google.sn",
"so": "google.so",
"sr": "google.sr",
"td": "google.td",
"tg": "google.tg",
"tk": "google.tk",
"tl": "google.tl",
"tm": "google.tm",
"tn": "google.tn",
"to": "google.to",
"tt": "google.tt",
"vg": "google.vg",
"vu": "google.vu",
"ws": "google.ws",
}

View file

@ -0,0 +1,66 @@
from typing import Annotated, Any, Optional
from arcade.sdk import ToolContext, tool
from arcade.sdk.errors import ToolExecutionError
from arcade_search.constants import (
DEFAULT_GOOGLE_SHOPPING_COUNTRY,
DEFAULT_GOOGLE_SHOPPING_LANGUAGE,
)
from arcade_search.google_data import GOOGLE_DOMAIN_BY_COUNTRY_CODE
from arcade_search.utils import (
call_serpapi,
extract_shopping_results,
prepare_params,
resolve_country_code,
resolve_language_code,
)
@tool(requires_secrets=["SERP_API_KEY"])
async def search_shopping_products(
context: ToolContext,
keywords: Annotated[
str,
"Keywords to search for products in Google Shopping. E.g. 'Apple iPhone'.",
],
country_code: Annotated[
Optional[str],
"2-character country code to search for products in Google Shopping. "
f"E.g. 'us' (United States). Defaults to '{DEFAULT_GOOGLE_SHOPPING_COUNTRY or 'us'}'.",
] = DEFAULT_GOOGLE_SHOPPING_COUNTRY,
language_code: Annotated[
Optional[str],
"2-character language code to search for products on Google Shopping. E.g. 'en' (English). "
f"Defaults to '{DEFAULT_GOOGLE_SHOPPING_LANGUAGE or 'en'}'.",
] = DEFAULT_GOOGLE_SHOPPING_LANGUAGE,
) -> Annotated[dict[str, list[dict[str, Any]]], "Products on Google Shopping."]:
"""Search for products on Google Shopping related to a given query."""
country_code = resolve_country_code(country_code, DEFAULT_GOOGLE_SHOPPING_COUNTRY)
language_code = resolve_language_code(language_code, DEFAULT_GOOGLE_SHOPPING_LANGUAGE)
if not isinstance(country_code, str):
country_code = "us"
if not isinstance(language_code, str):
language_code = "en"
google_domain = GOOGLE_DOMAIN_BY_COUNTRY_CODE.get(country_code, "google.com")
params = prepare_params(
"google_shopping",
q=keywords,
gl=country_code,
hl=language_code,
google_domain=google_domain,
)
response = call_serpapi(context, params)
if response.get("error"):
error_msg = response.get("error") or "Unknown Google Shopping Error"
raise ToolExecutionError(error_msg)
return {
"products": extract_shopping_results(response.get("shopping_results", [])),
}

View file

@ -276,6 +276,27 @@ def extract_news_results(
return news_results
# ------------------------------------------------------------------------------------------------
# Google Shopping utils
# ------------------------------------------------------------------------------------------------
def extract_shopping_results(results: list[dict[str, Any]]) -> list[dict[str, Any]]:
return [
{
"title": result.get("title"),
"direct_link": result.get("link"),
"google_link": result.get("product_link"),
"source": result.get("source"),
"price": result.get("price"),
"product_rating": result.get("rating"),
"product_reviews": result.get("reviews"),
"store_rating": result.get("store_rating"),
"store_reviews": result.get("store_reviews"),
"delivery": result.get("delivery"),
}
for result in results
]
# ------------------------------------------------------------------------------------------------
# Walmart utils
# ------------------------------------------------------------------------------------------------