arcade-mcp/toolkits/google_flights/arcade_google_flights/tools/google_flights.py
Eric Gustin 07c52100f3
Split and rename multiple toolkits (#438)
# PR Description
## Split toolkits

This PR splits the `Microsoft`, `Google`, and `Search` toolkits into
multiple toolkits each.
 * `Microsoft` --> `OutlookCalendar`, `OutlookMail`.
* `Google` -----> `GoogleCalendar`, `GoogleContacts`, `GoogleDocs`,
`GoogleDrive`, `Gmail`, `GoogleSheets`
* `Search` -----> `GoogleFinance`, `GoogleFlights`, `GoogleHotels`,
`GoogleJobs`, `GoogleMaps`, `GoogleNews`, `GoogleSearch`,
`GoogleShopping`, `Walmart`, `Youtube`

> The original monolithic toolkits (`Microsoft`, `Google`, `Search`) are
not removed in this PR. The plan is to keep those toolkits around while
we
> 1. Stop documenting the toolkits, 
> 2. Stop displaying the toolkits in the dashboard, and 
> 3. Help customers migrate over to the new split toolkits.

## Rename toolkits
This PR renames the following toolkits 
* `Web` ------------> `Firecrawl`
* `CodeSandbox` ---> `E2B`

> The `Web` and `CodeSandbox` toolkits are not removed in this PR. The
plan is to keep them around while we
> 1. Stop documenting the toolkits, 
> 2. Stop displaying the toolkits in the dashboard, and 
> 3. Help customers migrate over to the new renamed toolkits.

## Rename tools
Since toolkit names were changed, this called for some tools to be
renamed as well.
* `GoogleSearch.SearchGoogle` ----------------> `GoogleSearch.Search`
* `GoogleShopping.SearchShoppingProducts` --->
`GoogleShopping.SearchProducts`
* `Walmart.SearchWalmartProducts` ------------> `Walmart.SearchProducts`
* `Walmart.GetWalmartProductDetails` --------->
`Walmart.GetProductDetails`
* `Youtube.SearchYoutubeVideos` -------------->
`Youtube.SearchForVideos`

## Google File Picker
Improvements to the Google File Picker experience were also added in
this PR.

The following tools will ALWAYS provide llm_instructions in their
response to "let the end-user know that they have the option to select
more files via the file picker url if they want to":
* `GoogleDocs.SearchDocuments`
* `GoogleDocs.SearchAndRetrieveDocuments`
* `GoogleDrive.GetFileTreeStructure`

The following tools will only provide the file picker URL if a 404 or
403 from the Google API:
* `GoogleDocs.InsertTextAtEndOfDocument`
* `GoogleDocs.GetDocumentById`
* `GoogleSheets.GetSpreadsheet`
* `GoogleSheets.WriteToCell`

Also, a standalone `GoogleDrive.GenerateGoogleFilePickerUrl` tool
exists.

## Other
* The `SearchDocuments` and `SearchAndRetrieveDocuments` tools used to
be organized within the Drive portion of the Google toolkit, but I moved
these into the new GoogleDocs toolkit because they are specific to Docs.

# Progress

- [x] `OutlookCalendar`
- [x] `OutlookMail`
- [x] `GoogleFinance`
- [x] `GoogleFlights`
- [x] `GoogleHotels`
- [x] `GoogleJobs`
- [x] `GoogleMaps`
- [x] `GoogleNews`
- [x] `GoogleSearch`
- [x] `GoogleShopping`
- [x] `Walmart`
- [x] `Youtube`
- [x] `GoogleCalendar`
- [x] `GoogleContacts`
- [x] `GoogleDocs`
- [x] `GoogleDrive`
- [x] `Gmail`
- [x] `GoogleSheets`
- [x] `Firecrawl`
- [x] `E2B`
- [x] File picker

# Discussion
* Repeated code is a consequence of splitting toolkits that use the same
provider. I am open to any ideas that would allow multiple toolkits to
reference common code. Comment your ideas in this PR.
2025-07-09 16:00:09 -07:00

61 lines
2.3 KiB
Python

from typing import Annotated, Any
from arcade_tdk import ToolContext, tool
from arcade_google_flights.enums import (
GoogleFlightsMaxStops,
GoogleFlightsSortBy,
GoogleFlightsTravelClass,
)
from arcade_google_flights.utils import call_serpapi, parse_flight_results, prepare_params
@tool(requires_secrets=["SERP_API_KEY"])
async def search_one_way_flights(
context: ToolContext,
departure_airport_code: Annotated[
str, "The departure airport code. An uppercase 3-letter code"
],
arrival_airport_code: Annotated[str, "The arrival airport code. An uppercase 3-letter code"],
outbound_date: Annotated[str, "Flight departure date in YYYY-MM-DD format"],
currency_code: Annotated[
str | None, "Currency of the returned prices. Defaults to 'USD'"
] = "USD",
travel_class: Annotated[
GoogleFlightsTravelClass,
"Travel class of the flight. Defaults to 'ECONOMY'",
] = GoogleFlightsTravelClass.ECONOMY,
num_adults: Annotated[int | None, "Number of adult passengers. Defaults to 1"] = 1,
num_children: Annotated[int | None, "Number of child passengers. Defaults to 0"] = 0,
max_stops: Annotated[
GoogleFlightsMaxStops,
"Maximum number of stops (layovers) for the flight. Defaults to any number of stops",
] = GoogleFlightsMaxStops.ANY,
sort_by: Annotated[
GoogleFlightsSortBy,
"The sorting order of the results. Defaults to TOP_FLIGHTS.",
] = GoogleFlightsSortBy.TOP_FLIGHTS,
) -> Annotated[dict[str, Any], "Flight search results from the Google Flights API"]:
"""Retrieve flight search results for a one-way flight using Google Flights"""
params = prepare_params(
"google_flights",
departure_id=departure_airport_code,
arrival_id=arrival_airport_code,
outbound_date=outbound_date,
currency=currency_code,
travel_class=travel_class.to_api_value(),
adults=num_adults,
children=num_children,
stops=max_stops.to_api_value(),
sort_by=sort_by.to_api_value(),
type=2, # indicates one-way
deep_search=True, # Same search depth as the Google Flights page in the browser
)
# Execute the request
results = call_serpapi(context, params)
# Parse the results
flights = parse_flight_results(results)
return flights