From 8fca99fee7b48977b0df8547abc99a718c7826f4 Mon Sep 17 00:00:00 2001 From: waleed Date: Tue, 7 Jul 2026 11:26:56 -0700 Subject: [PATCH 1/3] fix(google-maps): validate integration against live API docs, add Places Nearby Search - add google_maps_places_nearby tool (Places API New, searchNearby) for radius/type-based place discovery - add pageToken support to places_search (text search pagination) - add units param to speed_limits (Roads API KPH/MPH) - wire homeMobileCountryCode/homeMobileNetworkCode subblocks for geolocate - split radius subblock so places_nearby's required radius isn't hidden in advanced mode - add default value to rankPreference dropdown - add missing authMode: AuthMode.ApiKey on the block --- apps/sim/blocks/blocks/google_maps.ts | 150 +++++++++++++-- apps/sim/tools/google_maps/index.ts | 2 + apps/sim/tools/google_maps/places_nearby.ts | 203 ++++++++++++++++++++ apps/sim/tools/google_maps/places_search.ts | 9 + apps/sim/tools/google_maps/speed_limits.ts | 10 + apps/sim/tools/google_maps/types.ts | 38 ++++ apps/sim/tools/registry.ts | 2 + 7 files changed, 403 insertions(+), 11 deletions(-) create mode 100644 apps/sim/tools/google_maps/places_nearby.ts diff --git a/apps/sim/blocks/blocks/google_maps.ts b/apps/sim/blocks/blocks/google_maps.ts index 26d3259b7f6..e0010101353 100644 --- a/apps/sim/blocks/blocks/google_maps.ts +++ b/apps/sim/blocks/blocks/google_maps.ts @@ -1,6 +1,6 @@ import { GoogleMapsIcon } from '@/components/icons' import type { BlockConfig, BlockMeta } from '@/blocks/types' -import { IntegrationType } from '@/blocks/types' +import { AuthMode, IntegrationType } from '@/blocks/types' export const GoogleMapsBlock: BlockConfig = { type: 'google_maps', @@ -11,6 +11,7 @@ export const GoogleMapsBlock: BlockConfig = { docsLink: 'https://docs.sim.ai/integrations/google_maps', category: 'tools', integrationType: IntegrationType.Search, + authMode: AuthMode.ApiKey, bgColor: '#FFFFFF', icon: GoogleMapsIcon, @@ -26,6 +27,7 @@ export const GoogleMapsBlock: BlockConfig = { { label: 'Get Directions', id: 'directions' }, { label: 'Distance Matrix', id: 'distance_matrix' }, { label: 'Search Places', id: 'places_search' }, + { label: 'Nearby Places', id: 'places_nearby' }, { label: 'Place Details', id: 'place_details' }, { label: 'Get Elevation', id: 'elevation' }, { label: 'Get Timezone', id: 'timezone' }, @@ -196,6 +198,15 @@ export const GoogleMapsBlock: BlockConfig = { condition: { field: 'operation', value: 'places_search' }, mode: 'advanced', }, + // Radius — required and always visible for Nearby Places (mandatory search area) + { + id: 'radius', + title: 'Radius (meters)', + type: 'short-input', + placeholder: 'Search radius in meters, up to 50000 (e.g., 5000)', + condition: { field: 'operation', value: 'places_nearby' }, + required: { field: 'operation', value: 'places_nearby' }, + }, { id: 'placeType', title: 'Place Type', @@ -222,9 +233,37 @@ export const GoogleMapsBlock: BlockConfig = { { label: 'Bus Station', id: 'bus_station' }, { label: 'Parking', id: 'parking' }, ], + condition: { field: 'operation', value: ['places_search', 'places_nearby'] }, + mode: 'advanced', + }, + { + id: 'pageToken', + title: 'Page Token', + type: 'short-input', + placeholder: 'Token from a previous search to fetch the next page', condition: { field: 'operation', value: 'places_search' }, mode: 'advanced', }, + { + id: 'maxResultCount', + title: 'Max Results', + type: 'short-input', + placeholder: 'Maximum number of results (1-20, defaults to 20)', + condition: { field: 'operation', value: 'places_nearby' }, + mode: 'advanced', + }, + { + id: 'rankPreference', + title: 'Rank By', + type: 'dropdown', + options: [ + { label: 'Popularity', id: 'POPULARITY' }, + { label: 'Distance', id: 'DISTANCE' }, + ], + value: () => 'POPULARITY', + condition: { field: 'operation', value: 'places_nearby' }, + mode: 'advanced', + }, { id: 'placeId', @@ -269,6 +308,18 @@ export const GoogleMapsBlock: BlockConfig = { rows: 2, mode: 'advanced', }, + { + id: 'speedUnits', + title: 'Speed Units', + type: 'dropdown', + options: [ + { label: 'KPH', id: 'KPH' }, + { label: 'MPH', id: 'MPH' }, + ], + value: () => 'KPH', + condition: { field: 'operation', value: 'speed_limits' }, + mode: 'advanced', + }, { id: 'addressToValidate', @@ -284,7 +335,7 @@ export const GoogleMapsBlock: BlockConfig = { title: 'Region Code', type: 'short-input', placeholder: 'ISO country code (e.g., US, CA, GB)', - condition: { field: 'operation', value: 'validate_address' }, + condition: { field: 'operation', value: ['validate_address', 'places_nearby'] }, mode: 'advanced', }, { @@ -332,6 +383,22 @@ export const GoogleMapsBlock: BlockConfig = { condition: { field: 'operation', value: 'geolocate' }, mode: 'advanced', }, + { + id: 'homeMobileCountryCode', + title: 'Home Mobile Country Code', + type: 'short-input', + placeholder: 'Home network MCC (e.g., 310)', + condition: { field: 'operation', value: 'geolocate' }, + mode: 'advanced', + }, + { + id: 'homeMobileNetworkCode', + title: 'Home Mobile Network Code', + type: 'short-input', + placeholder: 'Home network MNC (e.g., 410)', + condition: { field: 'operation', value: 'geolocate' }, + mode: 'advanced', + }, { id: 'wifiAccessPoints', title: 'WiFi Access Points', @@ -356,23 +423,23 @@ export const GoogleMapsBlock: BlockConfig = { title: 'Latitude', type: 'short-input', placeholder: '37.4224764', - condition: { field: 'operation', value: ['air_quality', 'pollen', 'solar'] }, - required: { field: 'operation', value: ['air_quality', 'pollen', 'solar'] }, + condition: { field: 'operation', value: ['air_quality', 'pollen', 'solar', 'places_nearby'] }, + required: { field: 'operation', value: ['air_quality', 'pollen', 'solar', 'places_nearby'] }, }, { id: 'aqLongitude', title: 'Longitude', type: 'short-input', placeholder: '-122.0842499', - condition: { field: 'operation', value: ['air_quality', 'pollen', 'solar'] }, - required: { field: 'operation', value: ['air_quality', 'pollen', 'solar'] }, + condition: { field: 'operation', value: ['air_quality', 'pollen', 'solar', 'places_nearby'] }, + required: { field: 'operation', value: ['air_quality', 'pollen', 'solar', 'places_nearby'] }, }, { id: 'languageCode', title: 'Language Code', type: 'short-input', placeholder: 'Language code (e.g., en, es)', - condition: { field: 'operation', value: ['air_quality', 'pollen'] }, + condition: { field: 'operation', value: ['air_quality', 'pollen', 'places_nearby'] }, mode: 'advanced', }, @@ -412,6 +479,18 @@ export const GoogleMapsBlock: BlockConfig = { type: 'short-input', placeholder: 'Language code (e.g., en, es, fr, de)', mode: 'advanced', + condition: { + field: 'operation', + value: [ + 'geocode', + 'reverse_geocode', + 'directions', + 'distance_matrix', + 'places_search', + 'place_details', + 'timezone', + ], + }, }, { id: 'region', @@ -432,6 +511,7 @@ export const GoogleMapsBlock: BlockConfig = { 'google_maps_geocode', 'google_maps_geolocate', 'google_maps_place_details', + 'google_maps_places_nearby', 'google_maps_places_search', 'google_maps_pollen', 'google_maps_reverse_geocode', @@ -549,6 +629,24 @@ export const GoogleMapsBlock: BlockConfig = { params.plantsDescription === 'true' || params.plantsDescription === true } + let maxResultCount: number | undefined + if (params.maxResultCount) { + const parsedMaxResultCount = Number.parseInt(params.maxResultCount, 10) + maxResultCount = Number.isNaN(parsedMaxResultCount) ? undefined : parsedMaxResultCount + } + + let homeMobileCountryCode: number | undefined + if (params.homeMobileCountryCode) { + const parsed = Number.parseInt(params.homeMobileCountryCode, 10) + homeMobileCountryCode = Number.isNaN(parsed) ? undefined : parsed + } + + let homeMobileNetworkCode: number | undefined + if (params.homeMobileNetworkCode) { + const parsed = Number.parseInt(params.homeMobileNetworkCode, 10) + homeMobileNetworkCode = Number.isNaN(parsed) ? undefined : parsed + } + return { ...rest, address, @@ -567,10 +665,15 @@ export const GoogleMapsBlock: BlockConfig = { considerIp, days, plantsDescription, + maxResultCount, + homeMobileCountryCode, + homeMobileNetworkCode, requiredQuality: params.requiredQuality || undefined, type: params.placeType || undefined, + includedTypes: params.placeType ? [params.placeType] : undefined, avoid: params.avoid || undefined, radioType: params.radioType || undefined, + units: operation === 'speed_limits' ? params.speedUnits || undefined : params.units, } }, }, @@ -593,6 +696,12 @@ export const GoogleMapsBlock: BlockConfig = { locationBias: { type: 'string', description: 'Location bias for search' }, radius: { type: 'string', description: 'Search radius in meters' }, placeType: { type: 'string', description: 'Place type filter' }, + pageToken: { type: 'string', description: 'Token to fetch the next page of place results' }, + maxResultCount: { type: 'string', description: 'Maximum number of nearby results to return' }, + rankPreference: { + type: 'string', + description: 'Nearby results ranking (POPULARITY, DISTANCE)', + }, placeId: { type: 'string', description: 'Google Place ID' }, fields: { type: 'string', description: 'Fields to retrieve' }, units: { type: 'string', description: 'Unit system' }, @@ -601,18 +710,30 @@ export const GoogleMapsBlock: BlockConfig = { path: { type: 'string', description: 'Pipe-separated lat,lng coordinates' }, interpolate: { type: 'boolean', description: 'Interpolate points along road' }, placeIds: { type: 'string', description: 'Pipe-separated Place IDs for speed limits' }, + speedUnits: { type: 'string', description: 'Units for speed limit results (KPH, MPH)' }, addressToValidate: { type: 'string', description: 'Address to validate' }, - regionCode: { type: 'string', description: 'ISO country code for address' }, + regionCode: { type: 'string', description: 'ISO country code for address or nearby search' }, locality: { type: 'string', description: 'City name hint' }, enableUspsCass: { type: 'boolean', description: 'Enable USPS CASS validation' }, considerIp: { type: 'boolean', description: 'Use IP for geolocation' }, + homeMobileCountryCode: { type: 'string', description: 'Home network mobile country code' }, + homeMobileNetworkCode: { type: 'string', description: 'Home network mobile network code' }, radioType: { type: 'string', description: 'Radio type (lte, gsm, etc.)' }, carrier: { type: 'string', description: 'Carrier name' }, wifiAccessPoints: { type: 'string', description: 'WiFi access points JSON' }, cellTowers: { type: 'string', description: 'Cell towers JSON' }, - aqLatitude: { type: 'string', description: 'Latitude for air quality, pollen, or solar' }, - aqLongitude: { type: 'string', description: 'Longitude for air quality, pollen, or solar' }, - languageCode: { type: 'string', description: 'Language code for air quality or pollen' }, + aqLatitude: { + type: 'string', + description: 'Latitude for air quality, pollen, solar, or nearby search', + }, + aqLongitude: { + type: 'string', + description: 'Longitude for air quality, pollen, solar, or nearby search', + }, + languageCode: { + type: 'string', + description: 'Language code for air quality, pollen, or nearby search', + }, days: { type: 'string', description: 'Number of pollen forecast days (1-5)' }, plantsDescription: { type: 'boolean', description: 'Include detailed plant descriptions' }, requiredQuality: { type: 'string', description: 'Minimum solar imagery quality' }, @@ -807,5 +928,12 @@ export const GoogleMapsBlockMeta = { content: '# Calculate Travel Distances\n\nGet a distance matrix from an origin to multiple destinations.\n\n## Steps\n1. Set the Origin and provide Destinations as a pipe-separated list (e.g., "New York, NY|Boston, MA").\n2. Choose Travel Mode and Units; optionally set features to Avoid.\n3. Run the Distance Matrix operation.\n4. Read each row for distance and duration to each destination.\n\n## Output\nA table of destinations sorted by travel time or distance, each with distance text and duration text. Useful for picking the nearest option or planning routes.', }, + { + name: 'find-places-nearby', + description: + 'Find places of a given type within a radius of a coordinate, ranked by popularity or distance, without needing a text query.', + content: + "# Find Places Nearby\n\nDiscover places around a fixed point (e.g., a store, delivery stop, or event venue) by type and radius, without a free-text search.\n\n## Steps\n1. Set the Latitude/Longitude of the center point and a Radius (meters, up to 50000).\n2. Optionally constrain by Place Type (restaurant, hotel, pharmacy, etc.) and choose Rank By (Popularity or Distance).\n3. Set Max Results if you only need the top few.\n4. Run the Nearby Places operation.\n\n## Output\nA list of nearby places with name, address, coordinates, rating, number of ratings, open-now status, and business status, ordered per the chosen ranking. Use Place Details with a result's place ID for phone/website/hours.", + }, ], } as const satisfies BlockMeta diff --git a/apps/sim/tools/google_maps/index.ts b/apps/sim/tools/google_maps/index.ts index 5b3465cc088..59b628f8f6d 100644 --- a/apps/sim/tools/google_maps/index.ts +++ b/apps/sim/tools/google_maps/index.ts @@ -5,6 +5,7 @@ import { googleMapsElevationTool } from '@/tools/google_maps/elevation' import { googleMapsGeocodeTool } from '@/tools/google_maps/geocode' import { googleMapsGeolocateTool } from '@/tools/google_maps/geolocate' import { googleMapsPlaceDetailsTool } from '@/tools/google_maps/place_details' +import { googleMapsPlacesNearbyTool } from '@/tools/google_maps/places_nearby' import { googleMapsPlacesSearchTool } from '@/tools/google_maps/places_search' import { googleMapsPollenTool } from '@/tools/google_maps/pollen' import { googleMapsReverseGeocodeTool } from '@/tools/google_maps/reverse_geocode' @@ -22,6 +23,7 @@ export { googleMapsGeocodeTool, googleMapsGeolocateTool, googleMapsPlaceDetailsTool, + googleMapsPlacesNearbyTool, googleMapsPlacesSearchTool, googleMapsPollenTool, googleMapsReverseGeocodeTool, diff --git a/apps/sim/tools/google_maps/places_nearby.ts b/apps/sim/tools/google_maps/places_nearby.ts new file mode 100644 index 00000000000..2eb40d970f0 --- /dev/null +++ b/apps/sim/tools/google_maps/places_nearby.ts @@ -0,0 +1,203 @@ +import type { + GoogleMapsPlacesNearbyParams, + GoogleMapsPlacesNearbyResponse, +} from '@/tools/google_maps/types' +import type { ToolConfig } from '@/tools/types' + +export const googleMapsPlacesNearbyTool: ToolConfig< + GoogleMapsPlacesNearbyParams, + GoogleMapsPlacesNearbyResponse +> = { + id: 'google_maps_places_nearby', + name: 'Google Maps Places Nearby Search', + description: 'Search for places of a given type within a radius of a location', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Google Maps API key', + }, + lat: { + type: 'number', + required: true, + visibility: 'user-or-llm', + description: 'Latitude of the center point to search around', + }, + lng: { + type: 'number', + required: true, + visibility: 'user-or-llm', + description: 'Longitude of the center point to search around', + }, + radius: { + type: 'number', + required: true, + visibility: 'user-or-llm', + description: 'Search radius in meters (up to 50000)', + }, + includedTypes: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Place types to include in the results (e.g., restaurant, cafe)', + }, + maxResultCount: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Maximum number of results to return (1-20, defaults to 20)', + }, + rankPreference: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'How to rank results: POPULARITY (default) or DISTANCE', + }, + languageCode: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Language code for the response (e.g., en, es)', + }, + regionCode: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Region bias as a ccTLD code (e.g., us, uk)', + }, + }, + + hosting: { + envKeyPrefix: 'GOOGLE_CLOUD_API_KEY', + apiKeyParam: 'apiKey', + byokProviderId: 'google_cloud', + pricing: { + type: 'per_request', + cost: 0.032, + }, + rateLimit: { + mode: 'per_request', + requestsPerMinute: 60, + }, + }, + + request: { + url: () => 'https://places.googleapis.com/v1/places:searchNearby', + method: 'POST', + headers: (params) => ({ + 'Content-Type': 'application/json', + 'X-Goog-Api-Key': params.apiKey.trim(), + 'X-Goog-FieldMask': + 'places.id,places.displayName,places.formattedAddress,places.location,places.types,places.rating,places.userRatingCount,places.priceLevel,places.currentOpeningHours.openNow,places.businessStatus', + }), + body: (params) => { + const body: { + locationRestriction: { + circle: { center: { latitude: number; longitude: number }; radius: number } + } + includedTypes?: string[] + maxResultCount?: number + rankPreference?: string + languageCode?: string + regionCode?: string + } = { + locationRestriction: { + circle: { + center: { latitude: params.lat, longitude: params.lng }, + radius: params.radius, + }, + }, + } + + if (params.includedTypes && params.includedTypes.length > 0) { + body.includedTypes = params.includedTypes + } + if (params.maxResultCount) { + body.maxResultCount = params.maxResultCount + } + if (params.rankPreference) { + body.rankPreference = params.rankPreference + } + if (params.languageCode) { + body.languageCode = params.languageCode.trim() + } + if (params.regionCode) { + body.regionCode = params.regionCode.trim() + } + + return body + }, + }, + + transformResponse: async (response: Response) => { + const data = await response.json() + + if (!response.ok || data.error) { + throw new Error(`Places Nearby Search failed: ${data.error?.message || response.statusText}`) + } + + const places = (data.places || []).map( + (place: { + id: string + displayName?: { text?: string } + formattedAddress?: string + location?: { latitude: number; longitude: number } + types?: string[] + rating?: number + userRatingCount?: number + priceLevel?: string + currentOpeningHours?: { openNow?: boolean } + businessStatus?: string + }) => ({ + placeId: place.id, + name: place.displayName?.text || '', + formattedAddress: place.formattedAddress ?? null, + lat: place.location?.latitude ?? null, + lng: place.location?.longitude ?? null, + types: place.types ?? [], + rating: place.rating ?? null, + userRatingsTotal: place.userRatingCount ?? null, + priceLevel: place.priceLevel ?? null, + openNow: place.currentOpeningHours?.openNow ?? null, + businessStatus: place.businessStatus ?? null, + }) + ) + + return { + success: true, + output: { + places, + }, + } + }, + + outputs: { + places: { + type: 'array', + description: 'List of places found near the given location', + items: { + type: 'object', + properties: { + placeId: { type: 'string', description: 'Google Place resource ID' }, + name: { type: 'string', description: 'Place name' }, + formattedAddress: { type: 'string', description: 'Formatted address', optional: true }, + lat: { type: 'number', description: 'Latitude', optional: true }, + lng: { type: 'number', description: 'Longitude', optional: true }, + types: { type: 'array', description: 'Place types' }, + rating: { type: 'number', description: 'Average rating (1-5)', optional: true }, + userRatingsTotal: { type: 'number', description: 'Number of ratings', optional: true }, + priceLevel: { + type: 'string', + description: 'Price level (e.g., PRICE_LEVEL_MODERATE)', + optional: true, + }, + openNow: { type: 'boolean', description: 'Whether currently open', optional: true }, + businessStatus: { type: 'string', description: 'Business status', optional: true }, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/google_maps/places_search.ts b/apps/sim/tools/google_maps/places_search.ts index 9891114d0a5..b7ed039d4a4 100644 --- a/apps/sim/tools/google_maps/places_search.ts +++ b/apps/sim/tools/google_maps/places_search.ts @@ -56,6 +56,12 @@ export const googleMapsPlacesSearchTool: ToolConfig< visibility: 'user-or-llm', description: 'Region bias as a ccTLD code (e.g., us, uk)', }, + pageToken: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Token from a previous search response to fetch the next page of results', + }, }, hosting: { @@ -93,6 +99,9 @@ export const googleMapsPlacesSearchTool: ToolConfig< if (params.region) { url.searchParams.set('region', params.region.trim()) } + if (params.pageToken) { + url.searchParams.set('pagetoken', params.pageToken.trim()) + } return url.toString() }, diff --git a/apps/sim/tools/google_maps/speed_limits.ts b/apps/sim/tools/google_maps/speed_limits.ts index b0fdc2e49f4..aadc871152d 100644 --- a/apps/sim/tools/google_maps/speed_limits.ts +++ b/apps/sim/tools/google_maps/speed_limits.ts @@ -32,6 +32,12 @@ export const googleMapsSpeedLimitsTool: ToolConfig< visibility: 'user-or-llm', description: 'Array of Place IDs for road segments (required if path not provided)', }, + units: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Units for the returned speed limits: KPH (default) or MPH', + }, }, request: { @@ -58,6 +64,10 @@ export const googleMapsSpeedLimitsTool: ToolConfig< } } + if (params.units) { + url.searchParams.set('units', params.units) + } + return url.toString() }, method: 'GET', diff --git a/apps/sim/tools/google_maps/types.ts b/apps/sim/tools/google_maps/types.ts index d292d40f621..2625b5faa0d 100644 --- a/apps/sim/tools/google_maps/types.ts +++ b/apps/sim/tools/google_maps/types.ts @@ -259,6 +259,7 @@ export interface GoogleMapsPlacesSearchParams { type?: string language?: string region?: string + pageToken?: string } export interface GoogleMapsPlacesSearchResponse extends ToolResponse { @@ -268,6 +269,42 @@ export interface GoogleMapsPlacesSearchResponse extends ToolResponse { } } +// ============================================================================ +// Places Nearby Search +// ============================================================================ + +interface NearbyPlaceResult { + placeId: string + name: string + formattedAddress: string | null + lat: number | null + lng: number | null + types: string[] + rating: number | null + userRatingsTotal: number | null + priceLevel: string | null + openNow: boolean | null + businessStatus: string | null +} + +export interface GoogleMapsPlacesNearbyParams { + apiKey: string + lat: number + lng: number + radius: number + includedTypes?: string[] + maxResultCount?: number + rankPreference?: 'POPULARITY' | 'DISTANCE' + languageCode?: string + regionCode?: string +} + +export interface GoogleMapsPlacesNearbyResponse extends ToolResponse { + output: { + places: NearbyPlaceResult[] + } +} + // ============================================================================ // Place Details // ============================================================================ @@ -388,6 +425,7 @@ export interface GoogleMapsSpeedLimitsParams { apiKey: string path?: string placeIds?: string[] + units?: 'KPH' | 'MPH' } export interface GoogleMapsSpeedLimitsResponse extends ToolResponse { diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index 02c6f7c8e7b..de8e623e582 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -1430,6 +1430,7 @@ import { googleMapsGeocodeTool, googleMapsGeolocateTool, googleMapsPlaceDetailsTool, + googleMapsPlacesNearbyTool, googleMapsPlacesSearchTool, googleMapsPollenTool, googleMapsReverseGeocodeTool, @@ -6381,6 +6382,7 @@ export const tools: Record = { google_maps_geocode: googleMapsGeocodeTool, google_maps_geolocate: googleMapsGeolocateTool, google_maps_place_details: googleMapsPlaceDetailsTool, + google_maps_places_nearby: googleMapsPlacesNearbyTool, google_maps_places_search: googleMapsPlacesSearchTool, google_maps_pollen: googleMapsPollenTool, google_maps_reverse_geocode: googleMapsReverseGeocodeTool, From d2f4af00ff8ec915198058d63e9d651f320491a1 Mon Sep 17 00:00:00 2001 From: waleed Date: Tue, 7 Jul 2026 11:38:26 -0700 Subject: [PATCH 2/3] fix(google-maps): mark elevation resolution optional per API docs Google's Elevation API omits resolution when it can't be determined, but our output schema declared it as a required number. Also fixes a stale comment calling Speed Limits "deprecated" when it's actually Asset Tracking-license restricted. --- apps/sim/blocks/blocks/google_maps.ts | 2 +- apps/sim/tools/google_maps/elevation.ts | 3 ++- apps/sim/tools/google_maps/types.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/sim/blocks/blocks/google_maps.ts b/apps/sim/blocks/blocks/google_maps.ts index e0010101353..5398049c9ed 100644 --- a/apps/sim/blocks/blocks/google_maps.ts +++ b/apps/sim/blocks/blocks/google_maps.ts @@ -53,7 +53,7 @@ export const GoogleMapsBlock: BlockConfig = { hideWhenHosted: true, condition: { field: 'operation', value: 'speed_limits', not: true }, }, - // API Key — always visible for Speed Limits (deprecated API, no hosted key support) + // API Key — always visible for Speed Limits (Asset Tracking-license restricted, no hosted key support) { id: 'apiKey', title: 'API Key', diff --git a/apps/sim/tools/google_maps/elevation.ts b/apps/sim/tools/google_maps/elevation.ts index ea55447cd29..6bd2982168c 100644 --- a/apps/sim/tools/google_maps/elevation.ts +++ b/apps/sim/tools/google_maps/elevation.ts @@ -78,7 +78,7 @@ export const googleMapsElevationTool: ToolConfig< elevation: result.elevation, lat: result.location.lat, lng: result.location.lng, - resolution: result.resolution, + resolution: result.resolution ?? null, }, } }, @@ -100,6 +100,7 @@ export const googleMapsElevationTool: ToolConfig< type: 'number', description: 'Maximum distance between data points (meters) from which elevation was interpolated', + optional: true, }, }, } diff --git a/apps/sim/tools/google_maps/types.ts b/apps/sim/tools/google_maps/types.ts index 2625b5faa0d..03d3bf3d226 100644 --- a/apps/sim/tools/google_maps/types.ts +++ b/apps/sim/tools/google_maps/types.ts @@ -373,7 +373,7 @@ export interface GoogleMapsElevationResponse extends ToolResponse { elevation: number lat: number lng: number - resolution: number + resolution: number | null } } From 4a680f31652bb61032dd3111767947d258227957 Mon Sep 17 00:00:00 2001 From: waleed Date: Tue, 7 Jul 2026 11:40:53 -0700 Subject: [PATCH 3/3] fix(google-maps): guard radius NaN parse, surface pageToken delay caveat - radius parsing in transformParams now guards NaN like every other numeric param in this block, so a non-numeric radius no longer silently sends "radius":null to the required Nearby Search field - pageToken description/placeholder now note the required delay before a token becomes valid, per Places Text Search API behavior --- apps/sim/blocks/blocks/google_maps.ts | 5 +++-- apps/sim/tools/google_maps/places_search.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/sim/blocks/blocks/google_maps.ts b/apps/sim/blocks/blocks/google_maps.ts index 5398049c9ed..833b2dfd8e5 100644 --- a/apps/sim/blocks/blocks/google_maps.ts +++ b/apps/sim/blocks/blocks/google_maps.ts @@ -240,7 +240,7 @@ export const GoogleMapsBlock: BlockConfig = { id: 'pageToken', title: 'Page Token', type: 'short-input', - placeholder: 'Token from a previous search to fetch the next page', + placeholder: 'Token from a previous search (wait ~2s before using it)', condition: { field: 'operation', value: 'places_search' }, mode: 'advanced', }, @@ -570,7 +570,8 @@ export const GoogleMapsBlock: BlockConfig = { let radius: number | undefined if (params.radius) { - radius = Number.parseInt(params.radius, 10) + const parsedRadius = Number.parseInt(params.radius, 10) + radius = Number.isNaN(parsedRadius) ? undefined : parsedRadius } let placeIds: string[] | undefined diff --git a/apps/sim/tools/google_maps/places_search.ts b/apps/sim/tools/google_maps/places_search.ts index b7ed039d4a4..08d9c157dd4 100644 --- a/apps/sim/tools/google_maps/places_search.ts +++ b/apps/sim/tools/google_maps/places_search.ts @@ -60,7 +60,8 @@ export const googleMapsPlacesSearchTool: ToolConfig< type: 'string', required: false, visibility: 'user-or-llm', - description: 'Token from a previous search response to fetch the next page of results', + description: + 'Token from a previous search response to fetch the next page of results. Wait a couple seconds after receiving the token before using it, or the API returns INVALID_REQUEST', }, },