Skip to content

Commit 98dc1ee

Browse files
authored
fix(google-maps): validate integration against live API docs, add Places Nearby Search (#5484)
* 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 * 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. * 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
1 parent 82de725 commit 98dc1ee

8 files changed

Lines changed: 410 additions & 15 deletions

File tree

apps/sim/blocks/blocks/google_maps.ts

Lines changed: 142 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { GoogleMapsIcon } from '@/components/icons'
22
import type { BlockConfig, BlockMeta } from '@/blocks/types'
3-
import { IntegrationType } from '@/blocks/types'
3+
import { AuthMode, IntegrationType } from '@/blocks/types'
44

55
export const GoogleMapsBlock: BlockConfig = {
66
type: 'google_maps',
@@ -11,6 +11,7 @@ export const GoogleMapsBlock: BlockConfig = {
1111
docsLink: 'https://docs.sim.ai/integrations/google_maps',
1212
category: 'tools',
1313
integrationType: IntegrationType.Search,
14+
authMode: AuthMode.ApiKey,
1415
bgColor: '#FFFFFF',
1516
icon: GoogleMapsIcon,
1617

@@ -26,6 +27,7 @@ export const GoogleMapsBlock: BlockConfig = {
2627
{ label: 'Get Directions', id: 'directions' },
2728
{ label: 'Distance Matrix', id: 'distance_matrix' },
2829
{ label: 'Search Places', id: 'places_search' },
30+
{ label: 'Nearby Places', id: 'places_nearby' },
2931
{ label: 'Place Details', id: 'place_details' },
3032
{ label: 'Get Elevation', id: 'elevation' },
3133
{ label: 'Get Timezone', id: 'timezone' },
@@ -51,7 +53,7 @@ export const GoogleMapsBlock: BlockConfig = {
5153
hideWhenHosted: true,
5254
condition: { field: 'operation', value: 'speed_limits', not: true },
5355
},
54-
// API Key — always visible for Speed Limits (deprecated API, no hosted key support)
56+
// API Key — always visible for Speed Limits (Asset Tracking-license restricted, no hosted key support)
5557
{
5658
id: 'apiKey',
5759
title: 'API Key',
@@ -196,6 +198,15 @@ export const GoogleMapsBlock: BlockConfig = {
196198
condition: { field: 'operation', value: 'places_search' },
197199
mode: 'advanced',
198200
},
201+
// Radius — required and always visible for Nearby Places (mandatory search area)
202+
{
203+
id: 'radius',
204+
title: 'Radius (meters)',
205+
type: 'short-input',
206+
placeholder: 'Search radius in meters, up to 50000 (e.g., 5000)',
207+
condition: { field: 'operation', value: 'places_nearby' },
208+
required: { field: 'operation', value: 'places_nearby' },
209+
},
199210
{
200211
id: 'placeType',
201212
title: 'Place Type',
@@ -222,9 +233,37 @@ export const GoogleMapsBlock: BlockConfig = {
222233
{ label: 'Bus Station', id: 'bus_station' },
223234
{ label: 'Parking', id: 'parking' },
224235
],
236+
condition: { field: 'operation', value: ['places_search', 'places_nearby'] },
237+
mode: 'advanced',
238+
},
239+
{
240+
id: 'pageToken',
241+
title: 'Page Token',
242+
type: 'short-input',
243+
placeholder: 'Token from a previous search (wait ~2s before using it)',
225244
condition: { field: 'operation', value: 'places_search' },
226245
mode: 'advanced',
227246
},
247+
{
248+
id: 'maxResultCount',
249+
title: 'Max Results',
250+
type: 'short-input',
251+
placeholder: 'Maximum number of results (1-20, defaults to 20)',
252+
condition: { field: 'operation', value: 'places_nearby' },
253+
mode: 'advanced',
254+
},
255+
{
256+
id: 'rankPreference',
257+
title: 'Rank By',
258+
type: 'dropdown',
259+
options: [
260+
{ label: 'Popularity', id: 'POPULARITY' },
261+
{ label: 'Distance', id: 'DISTANCE' },
262+
],
263+
value: () => 'POPULARITY',
264+
condition: { field: 'operation', value: 'places_nearby' },
265+
mode: 'advanced',
266+
},
228267

229268
{
230269
id: 'placeId',
@@ -269,6 +308,18 @@ export const GoogleMapsBlock: BlockConfig = {
269308
rows: 2,
270309
mode: 'advanced',
271310
},
311+
{
312+
id: 'speedUnits',
313+
title: 'Speed Units',
314+
type: 'dropdown',
315+
options: [
316+
{ label: 'KPH', id: 'KPH' },
317+
{ label: 'MPH', id: 'MPH' },
318+
],
319+
value: () => 'KPH',
320+
condition: { field: 'operation', value: 'speed_limits' },
321+
mode: 'advanced',
322+
},
272323

273324
{
274325
id: 'addressToValidate',
@@ -284,7 +335,7 @@ export const GoogleMapsBlock: BlockConfig = {
284335
title: 'Region Code',
285336
type: 'short-input',
286337
placeholder: 'ISO country code (e.g., US, CA, GB)',
287-
condition: { field: 'operation', value: 'validate_address' },
338+
condition: { field: 'operation', value: ['validate_address', 'places_nearby'] },
288339
mode: 'advanced',
289340
},
290341
{
@@ -332,6 +383,22 @@ export const GoogleMapsBlock: BlockConfig = {
332383
condition: { field: 'operation', value: 'geolocate' },
333384
mode: 'advanced',
334385
},
386+
{
387+
id: 'homeMobileCountryCode',
388+
title: 'Home Mobile Country Code',
389+
type: 'short-input',
390+
placeholder: 'Home network MCC (e.g., 310)',
391+
condition: { field: 'operation', value: 'geolocate' },
392+
mode: 'advanced',
393+
},
394+
{
395+
id: 'homeMobileNetworkCode',
396+
title: 'Home Mobile Network Code',
397+
type: 'short-input',
398+
placeholder: 'Home network MNC (e.g., 410)',
399+
condition: { field: 'operation', value: 'geolocate' },
400+
mode: 'advanced',
401+
},
335402
{
336403
id: 'wifiAccessPoints',
337404
title: 'WiFi Access Points',
@@ -356,23 +423,23 @@ export const GoogleMapsBlock: BlockConfig = {
356423
title: 'Latitude',
357424
type: 'short-input',
358425
placeholder: '37.4224764',
359-
condition: { field: 'operation', value: ['air_quality', 'pollen', 'solar'] },
360-
required: { field: 'operation', value: ['air_quality', 'pollen', 'solar'] },
426+
condition: { field: 'operation', value: ['air_quality', 'pollen', 'solar', 'places_nearby'] },
427+
required: { field: 'operation', value: ['air_quality', 'pollen', 'solar', 'places_nearby'] },
361428
},
362429
{
363430
id: 'aqLongitude',
364431
title: 'Longitude',
365432
type: 'short-input',
366433
placeholder: '-122.0842499',
367-
condition: { field: 'operation', value: ['air_quality', 'pollen', 'solar'] },
368-
required: { field: 'operation', value: ['air_quality', 'pollen', 'solar'] },
434+
condition: { field: 'operation', value: ['air_quality', 'pollen', 'solar', 'places_nearby'] },
435+
required: { field: 'operation', value: ['air_quality', 'pollen', 'solar', 'places_nearby'] },
369436
},
370437
{
371438
id: 'languageCode',
372439
title: 'Language Code',
373440
type: 'short-input',
374441
placeholder: 'Language code (e.g., en, es)',
375-
condition: { field: 'operation', value: ['air_quality', 'pollen'] },
442+
condition: { field: 'operation', value: ['air_quality', 'pollen', 'places_nearby'] },
376443
mode: 'advanced',
377444
},
378445

@@ -412,6 +479,18 @@ export const GoogleMapsBlock: BlockConfig = {
412479
type: 'short-input',
413480
placeholder: 'Language code (e.g., en, es, fr, de)',
414481
mode: 'advanced',
482+
condition: {
483+
field: 'operation',
484+
value: [
485+
'geocode',
486+
'reverse_geocode',
487+
'directions',
488+
'distance_matrix',
489+
'places_search',
490+
'place_details',
491+
'timezone',
492+
],
493+
},
415494
},
416495
{
417496
id: 'region',
@@ -432,6 +511,7 @@ export const GoogleMapsBlock: BlockConfig = {
432511
'google_maps_geocode',
433512
'google_maps_geolocate',
434513
'google_maps_place_details',
514+
'google_maps_places_nearby',
435515
'google_maps_places_search',
436516
'google_maps_pollen',
437517
'google_maps_reverse_geocode',
@@ -490,7 +570,8 @@ export const GoogleMapsBlock: BlockConfig = {
490570

491571
let radius: number | undefined
492572
if (params.radius) {
493-
radius = Number.parseInt(params.radius, 10)
573+
const parsedRadius = Number.parseInt(params.radius, 10)
574+
radius = Number.isNaN(parsedRadius) ? undefined : parsedRadius
494575
}
495576

496577
let placeIds: string[] | undefined
@@ -549,6 +630,24 @@ export const GoogleMapsBlock: BlockConfig = {
549630
params.plantsDescription === 'true' || params.plantsDescription === true
550631
}
551632

633+
let maxResultCount: number | undefined
634+
if (params.maxResultCount) {
635+
const parsedMaxResultCount = Number.parseInt(params.maxResultCount, 10)
636+
maxResultCount = Number.isNaN(parsedMaxResultCount) ? undefined : parsedMaxResultCount
637+
}
638+
639+
let homeMobileCountryCode: number | undefined
640+
if (params.homeMobileCountryCode) {
641+
const parsed = Number.parseInt(params.homeMobileCountryCode, 10)
642+
homeMobileCountryCode = Number.isNaN(parsed) ? undefined : parsed
643+
}
644+
645+
let homeMobileNetworkCode: number | undefined
646+
if (params.homeMobileNetworkCode) {
647+
const parsed = Number.parseInt(params.homeMobileNetworkCode, 10)
648+
homeMobileNetworkCode = Number.isNaN(parsed) ? undefined : parsed
649+
}
650+
552651
return {
553652
...rest,
554653
address,
@@ -567,10 +666,15 @@ export const GoogleMapsBlock: BlockConfig = {
567666
considerIp,
568667
days,
569668
plantsDescription,
669+
maxResultCount,
670+
homeMobileCountryCode,
671+
homeMobileNetworkCode,
570672
requiredQuality: params.requiredQuality || undefined,
571673
type: params.placeType || undefined,
674+
includedTypes: params.placeType ? [params.placeType] : undefined,
572675
avoid: params.avoid || undefined,
573676
radioType: params.radioType || undefined,
677+
units: operation === 'speed_limits' ? params.speedUnits || undefined : params.units,
574678
}
575679
},
576680
},
@@ -593,6 +697,12 @@ export const GoogleMapsBlock: BlockConfig = {
593697
locationBias: { type: 'string', description: 'Location bias for search' },
594698
radius: { type: 'string', description: 'Search radius in meters' },
595699
placeType: { type: 'string', description: 'Place type filter' },
700+
pageToken: { type: 'string', description: 'Token to fetch the next page of place results' },
701+
maxResultCount: { type: 'string', description: 'Maximum number of nearby results to return' },
702+
rankPreference: {
703+
type: 'string',
704+
description: 'Nearby results ranking (POPULARITY, DISTANCE)',
705+
},
596706
placeId: { type: 'string', description: 'Google Place ID' },
597707
fields: { type: 'string', description: 'Fields to retrieve' },
598708
units: { type: 'string', description: 'Unit system' },
@@ -601,18 +711,30 @@ export const GoogleMapsBlock: BlockConfig = {
601711
path: { type: 'string', description: 'Pipe-separated lat,lng coordinates' },
602712
interpolate: { type: 'boolean', description: 'Interpolate points along road' },
603713
placeIds: { type: 'string', description: 'Pipe-separated Place IDs for speed limits' },
714+
speedUnits: { type: 'string', description: 'Units for speed limit results (KPH, MPH)' },
604715
addressToValidate: { type: 'string', description: 'Address to validate' },
605-
regionCode: { type: 'string', description: 'ISO country code for address' },
716+
regionCode: { type: 'string', description: 'ISO country code for address or nearby search' },
606717
locality: { type: 'string', description: 'City name hint' },
607718
enableUspsCass: { type: 'boolean', description: 'Enable USPS CASS validation' },
608719
considerIp: { type: 'boolean', description: 'Use IP for geolocation' },
720+
homeMobileCountryCode: { type: 'string', description: 'Home network mobile country code' },
721+
homeMobileNetworkCode: { type: 'string', description: 'Home network mobile network code' },
609722
radioType: { type: 'string', description: 'Radio type (lte, gsm, etc.)' },
610723
carrier: { type: 'string', description: 'Carrier name' },
611724
wifiAccessPoints: { type: 'string', description: 'WiFi access points JSON' },
612725
cellTowers: { type: 'string', description: 'Cell towers JSON' },
613-
aqLatitude: { type: 'string', description: 'Latitude for air quality, pollen, or solar' },
614-
aqLongitude: { type: 'string', description: 'Longitude for air quality, pollen, or solar' },
615-
languageCode: { type: 'string', description: 'Language code for air quality or pollen' },
726+
aqLatitude: {
727+
type: 'string',
728+
description: 'Latitude for air quality, pollen, solar, or nearby search',
729+
},
730+
aqLongitude: {
731+
type: 'string',
732+
description: 'Longitude for air quality, pollen, solar, or nearby search',
733+
},
734+
languageCode: {
735+
type: 'string',
736+
description: 'Language code for air quality, pollen, or nearby search',
737+
},
616738
days: { type: 'string', description: 'Number of pollen forecast days (1-5)' },
617739
plantsDescription: { type: 'boolean', description: 'Include detailed plant descriptions' },
618740
requiredQuality: { type: 'string', description: 'Minimum solar imagery quality' },
@@ -807,5 +929,12 @@ export const GoogleMapsBlockMeta = {
807929
content:
808930
'# 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.',
809931
},
932+
{
933+
name: 'find-places-nearby',
934+
description:
935+
'Find places of a given type within a radius of a coordinate, ranked by popularity or distance, without needing a text query.',
936+
content:
937+
"# 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.",
938+
},
810939
],
811940
} as const satisfies BlockMeta

apps/sim/tools/google_maps/elevation.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export const googleMapsElevationTool: ToolConfig<
7878
elevation: result.elevation,
7979
lat: result.location.lat,
8080
lng: result.location.lng,
81-
resolution: result.resolution,
81+
resolution: result.resolution ?? null,
8282
},
8383
}
8484
},
@@ -100,6 +100,7 @@ export const googleMapsElevationTool: ToolConfig<
100100
type: 'number',
101101
description:
102102
'Maximum distance between data points (meters) from which elevation was interpolated',
103+
optional: true,
103104
},
104105
},
105106
}

apps/sim/tools/google_maps/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { googleMapsElevationTool } from '@/tools/google_maps/elevation'
55
import { googleMapsGeocodeTool } from '@/tools/google_maps/geocode'
66
import { googleMapsGeolocateTool } from '@/tools/google_maps/geolocate'
77
import { googleMapsPlaceDetailsTool } from '@/tools/google_maps/place_details'
8+
import { googleMapsPlacesNearbyTool } from '@/tools/google_maps/places_nearby'
89
import { googleMapsPlacesSearchTool } from '@/tools/google_maps/places_search'
910
import { googleMapsPollenTool } from '@/tools/google_maps/pollen'
1011
import { googleMapsReverseGeocodeTool } from '@/tools/google_maps/reverse_geocode'
@@ -22,6 +23,7 @@ export {
2223
googleMapsGeocodeTool,
2324
googleMapsGeolocateTool,
2425
googleMapsPlaceDetailsTool,
26+
googleMapsPlacesNearbyTool,
2527
googleMapsPlacesSearchTool,
2628
googleMapsPollenTool,
2729
googleMapsReverseGeocodeTool,

0 commit comments

Comments
 (0)