mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-31 09:48:06 -05:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b24a47308 | ||
|
|
0e7b8430e9 |
File diff suppressed because one or more lines are too long
@@ -24,6 +24,7 @@ import {
|
||||
DiscordIcon,
|
||||
DocumentIcon,
|
||||
DropboxIcon,
|
||||
DsPyIcon,
|
||||
DuckDuckGoIcon,
|
||||
DynamoDBIcon,
|
||||
ElasticsearchIcon,
|
||||
@@ -41,6 +42,7 @@ import {
|
||||
GoogleFormsIcon,
|
||||
GoogleGroupsIcon,
|
||||
GoogleIcon,
|
||||
GoogleMapsIcon,
|
||||
GoogleSheetsIcon,
|
||||
GoogleSlidesIcon,
|
||||
GoogleVaultIcon,
|
||||
@@ -153,6 +155,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
datadog: DatadogIcon,
|
||||
discord: DiscordIcon,
|
||||
dropbox: DropboxIcon,
|
||||
dspy: DsPyIcon,
|
||||
duckduckgo: DuckDuckGoIcon,
|
||||
dynamodb: DynamoDBIcon,
|
||||
elasticsearch: ElasticsearchIcon,
|
||||
@@ -169,6 +172,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
google_drive: GoogleDriveIcon,
|
||||
google_forms: GoogleFormsIcon,
|
||||
google_groups: GoogleGroupsIcon,
|
||||
google_maps: GoogleMapsIcon,
|
||||
google_search: GoogleIcon,
|
||||
google_sheets_v2: GoogleSheetsIcon,
|
||||
google_slides: GoogleSlidesIcon,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: CalCom
|
||||
title: Cal Com
|
||||
description: Manage Cal.com bookings, event types, schedules, and availability
|
||||
---
|
||||
|
||||
|
||||
112
apps/docs/content/docs/en/tools/dspy.mdx
Normal file
112
apps/docs/content/docs/en/tools/dspy.mdx
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
title: DSPy
|
||||
description: Run predictions using self-hosted DSPy programs
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="dspy"
|
||||
color="#1E293B"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[DSPy](https://github.com/stanford-oval/dspy) is an open-source framework for programming—rather than prompting—language models. DSPy enables you to build interpretable and modular LLM-powered agents using Python functions, structured modules, and declarative signatures, making it easy to compose, debug, and reliably deploy language model applications.
|
||||
|
||||
With DSPy in Sim, you can:
|
||||
|
||||
- **Run custom predictions**: Connect your self-hosted DSPy server and invoke prediction endpoints for a variety of natural language tasks.
|
||||
- **Chain of Thought and ReAct reasoning**: Leverage advanced DSPy modules for step-by-step reasoning, multi-turn dialogs, and action-observation loops.
|
||||
- **Integrate with your workflows**: Automate LLM predictions and reasoning as part of any Sim automation or agent routine.
|
||||
- **Provide custom endpoints and context**: Flexibly call your own DSPy-powered APIs with custom authentication, endpoints, input fields, and context.
|
||||
|
||||
These features let your Sim agents access modular, interpretable LLM-based programs for tasks like question answering, document analysis, decision support, and more—where you remain in control of the model, data, and logic.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate with your self-hosted DSPy programs for LLM-powered predictions. Supports Predict, Chain of Thought, and ReAct agents. DSPy is the framework for programming—not prompting—language models.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `dspy_predict`
|
||||
|
||||
Run a prediction using a self-hosted DSPy program endpoint
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `baseUrl` | string | Yes | Base URL of the DSPy server \(e.g., https://your-dspy-server.com\) |
|
||||
| `apiKey` | string | No | API key for authentication \(if required by your server\) |
|
||||
| `endpoint` | string | No | API endpoint path \(defaults to /predict\) |
|
||||
| `input` | string | Yes | The input text to send to the DSPy program |
|
||||
| `inputField` | string | No | Name of the input field expected by the DSPy program \(defaults to "text"\) |
|
||||
| `context` | string | No | Additional context to provide to the DSPy program |
|
||||
| `additionalInputs` | json | No | Additional key-value pairs to include in the request body |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `answer` | string | The main output/answer from the DSPy program |
|
||||
| `reasoning` | string | The reasoning or rationale behind the answer \(if available\) |
|
||||
| `status` | string | Response status from the DSPy server \(success or error\) |
|
||||
| `rawOutput` | json | The complete raw output from the DSPy program \(result.toDict\(\)\) |
|
||||
|
||||
### `dspy_chain_of_thought`
|
||||
|
||||
Run a Chain of Thought prediction using a self-hosted DSPy ChainOfThought program endpoint
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `baseUrl` | string | Yes | Base URL of the DSPy server \(e.g., https://your-dspy-server.com\) |
|
||||
| `apiKey` | string | No | API key for authentication \(if required by your server\) |
|
||||
| `endpoint` | string | No | API endpoint path \(defaults to /predict\) |
|
||||
| `question` | string | Yes | The question to answer using chain of thought reasoning |
|
||||
| `context` | string | No | Additional context to provide for answering the question |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `answer` | string | The answer generated through chain of thought reasoning |
|
||||
| `reasoning` | string | The step-by-step reasoning that led to the answer |
|
||||
| `status` | string | Response status from the DSPy server \(success or error\) |
|
||||
| `rawOutput` | json | The complete raw output from the DSPy program \(result.toDict\(\)\) |
|
||||
|
||||
### `dspy_react`
|
||||
|
||||
Run a ReAct agent using a self-hosted DSPy ReAct program endpoint for multi-step reasoning and action
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `baseUrl` | string | Yes | Base URL of the DSPy server \(e.g., https://your-dspy-server.com\) |
|
||||
| `apiKey` | string | No | API key for authentication \(if required by your server\) |
|
||||
| `endpoint` | string | No | API endpoint path \(defaults to /predict\) |
|
||||
| `task` | string | Yes | The task or question for the ReAct agent to work on |
|
||||
| `context` | string | No | Additional context to provide for the task |
|
||||
| `maxIterations` | number | No | Maximum number of reasoning iterations \(defaults to server setting\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `answer` | string | The final answer or result from the ReAct agent |
|
||||
| `reasoning` | string | The overall reasoning summary from the agent |
|
||||
| `trajectory` | array | The step-by-step trajectory of thoughts, actions, and observations |
|
||||
| ↳ `thought` | string | The reasoning thought at this step |
|
||||
| ↳ `toolName` | string | The name of the tool/action called |
|
||||
| ↳ `toolArgs` | json | Arguments passed to the tool |
|
||||
| ↳ `observation` | string | The observation/result from the tool execution |
|
||||
| `status` | string | Response status from the DSPy server \(success or error\) |
|
||||
| `rawOutput` | json | The complete raw output from the DSPy program \(result.toDict\(\)\) |
|
||||
|
||||
|
||||
@@ -10,6 +10,23 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
color="#E8F0FE"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Google Groups](https://groups.google.com) is part of Google Workspace, providing email-based group communication, collaboration, and access control for teams and organizations. Google Groups lets you create mailing lists, manage membership, and control permissions for both internal and external users.
|
||||
|
||||
This page explains how you can use Sim to automate the management of Google Groups in your workflows. With Sim, agents can create and configure groups, add or remove members, update group settings, and keep directory lists up-to-date automatically—ideal for onboarding workflows, syncing IT systems, or dynamically managing project teams.
|
||||
|
||||
With Google Groups, you can:
|
||||
|
||||
- **Centralize communications**: Create team or project mailing lists for group conversations
|
||||
- **Manage group membership**: Add, remove, or update members with granular roles (owner, manager, member)
|
||||
- **Control access**: Manage who can view, post, or join; set permissions for public/private visibility
|
||||
- **Collaborate across teams**: Streamline communication and document sharing via group-based access
|
||||
- **Automate IT tasks**: Use Sim to keep group memberships current as teams change
|
||||
|
||||
In Sim, the Google Groups integration gives your agents API-driven control to automate common administrative tasks. Connect directly to your Google Workspace domain to add users to groups, manage lists, audit group settings, and ensure your organization’s access controls are always up-to-date—without manual overhead.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Connect to Google Workspace to create, update, and manage groups and their members using the Admin SDK Directory API.
|
||||
|
||||
450
apps/docs/content/docs/en/tools/google_maps.mdx
Normal file
450
apps/docs/content/docs/en/tools/google_maps.mdx
Normal file
@@ -0,0 +1,450 @@
|
||||
---
|
||||
title: Google Maps
|
||||
description: Geocoding, directions, places, and distance calculations
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="google_maps"
|
||||
color="#E0E0E0"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Google Maps](https://maps.google.com) is a comprehensive platform offering a wide array of APIs for mapping, geocoding, routing, places, environment data, and more. Through Sim, your agents can leverage key Google Maps Platform APIs to automate a variety of location-based workflows.
|
||||
|
||||
**The following Google Maps APIs are included in this integration:**
|
||||
|
||||
- **Geocoding API:** Convert addresses into latitude/longitude coordinates and perform reverse geocoding.
|
||||
- **Directions API:** Calculate driving, walking, cycling, or transit directions and routes between locations.
|
||||
- **Distance Matrix API:** Compute travel distances and times for multiple origin and destination combinations.
|
||||
- **Places API:** Search for places (businesses, landmarks, establishments) by name, type, or proximity.
|
||||
- **Place Details API:** Retrieve detailed information for a specific place, such as address, ratings, hours, and contact info.
|
||||
- **Elevation API:** Obtain elevation data (height above sea level) for any set of locations globally.
|
||||
- **Time Zone API:** Look up time zone information for any geographic location.
|
||||
- **Air Quality API:** Fetch real-time air quality data for specific coordinates.
|
||||
|
||||
With these APIs, your Sim agents can automate location lookup and enrichment, plan optimal routes and deliveries, estimate times and distances, analyze place data, enrich records with geographic context, get environmental conditions, and more—all without manual work or external tools.
|
||||
|
||||
If you need capabilities beyond what's listed here or want to request support for additional Google Maps APIs, let us know!
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Google Maps Platform APIs into your workflow. Supports geocoding addresses to coordinates, reverse geocoding, getting directions between locations, calculating distance matrices, searching for places, retrieving place details, elevation data, and timezone information.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `google_maps_air_quality`
|
||||
|
||||
Get current air quality data for a location
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key with Air Quality API enabled |
|
||||
| `lat` | number | Yes | Latitude coordinate |
|
||||
| `lng` | number | Yes | Longitude coordinate |
|
||||
| `languageCode` | string | No | Language code for the response \(e.g., "en", "es"\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `dateTime` | string | Timestamp of the air quality data |
|
||||
| `regionCode` | string | Region code for the location |
|
||||
| `indexes` | array | Array of air quality indexes |
|
||||
| ↳ `code` | string | Index code \(e.g., "uaqi", "usa_epa"\) |
|
||||
| ↳ `displayName` | string | Display name of the index |
|
||||
| ↳ `aqi` | number | Air quality index value |
|
||||
| ↳ `aqiDisplay` | string | Formatted AQI display string |
|
||||
| ↳ `color` | object | RGB color for the AQI level |
|
||||
| ↳ `category` | string | Category description \(e.g., "Good", "Moderate"\) |
|
||||
| ↳ `dominantPollutant` | string | The dominant pollutant |
|
||||
| `pollutants` | array | Array of pollutant concentrations |
|
||||
| ↳ `code` | string | Pollutant code \(e.g., "pm25", "o3"\) |
|
||||
| ↳ `displayName` | string | Display name |
|
||||
| ↳ `fullName` | string | Full pollutant name |
|
||||
| ↳ `concentration` | object | Concentration info |
|
||||
| ↳ `value` | number | Concentration value |
|
||||
| ↳ `units` | string | Units \(e.g., "PARTS_PER_BILLION"\) |
|
||||
| ↳ `additionalInfo` | object | Additional info about sources and effects |
|
||||
| `healthRecommendations` | object | Health recommendations for different populations |
|
||||
|
||||
### `google_maps_directions`
|
||||
|
||||
Get directions and route information between two locations
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key |
|
||||
| `origin` | string | Yes | Starting location \(address or lat,lng\) |
|
||||
| `destination` | string | Yes | Destination location \(address or lat,lng\) |
|
||||
| `mode` | string | No | Travel mode: driving, walking, bicycling, or transit |
|
||||
| `avoid` | string | No | Features to avoid: tolls, highways, or ferries |
|
||||
| `waypoints` | json | No | Array of intermediate waypoints |
|
||||
| `units` | string | No | Unit system: metric or imperial |
|
||||
| `language` | string | No | Language code for results \(e.g., en, es, fr\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `routes` | array | All available routes |
|
||||
| ↳ `summary` | string | Route summary \(main road names\) |
|
||||
| ↳ `legs` | array | Route legs \(segments between waypoints\) |
|
||||
| ↳ `overviewPolyline` | string | Encoded polyline for the entire route |
|
||||
| ↳ `warnings` | array | Route warnings |
|
||||
| ↳ `waypointOrder` | array | Optimized waypoint order \(if requested\) |
|
||||
| `distanceText` | string | Total distance as human-readable text \(e.g., "5.2 km"\) |
|
||||
| `distanceMeters` | number | Total distance in meters |
|
||||
| `durationText` | string | Total duration as human-readable text \(e.g., "15 mins"\) |
|
||||
| `durationSeconds` | number | Total duration in seconds |
|
||||
| `startAddress` | string | Resolved starting address |
|
||||
| `endAddress` | string | Resolved ending address |
|
||||
| `steps` | array | Turn-by-turn navigation instructions |
|
||||
| ↳ `instruction` | string | Navigation instruction \(HTML stripped\) |
|
||||
| ↳ `distanceText` | string | Step distance as text |
|
||||
| ↳ `distanceMeters` | number | Step distance in meters |
|
||||
| ↳ `durationText` | string | Step duration as text |
|
||||
| ↳ `durationSeconds` | number | Step duration in seconds |
|
||||
| ↳ `startLocation` | object | Step start coordinates |
|
||||
| ↳ `endLocation` | object | Step end coordinates |
|
||||
| ↳ `travelMode` | string | Travel mode for this step |
|
||||
| ↳ `maneuver` | string | Maneuver type \(turn-left, etc.\) |
|
||||
| `polyline` | string | Encoded polyline for the primary route |
|
||||
|
||||
### `google_maps_distance_matrix`
|
||||
|
||||
Calculate travel distance and time between multiple origins and destinations
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key |
|
||||
| `origin` | string | Yes | Origin location \(address or lat,lng\) |
|
||||
| `destinations` | json | Yes | Array of destination locations |
|
||||
| `mode` | string | No | Travel mode: driving, walking, bicycling, or transit |
|
||||
| `avoid` | string | No | Features to avoid: tolls, highways, or ferries |
|
||||
| `units` | string | No | Unit system: metric or imperial |
|
||||
| `language` | string | No | Language code for results \(e.g., en, es, fr\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `originAddresses` | array | Resolved origin addresses |
|
||||
| `destinationAddresses` | array | Resolved destination addresses |
|
||||
| `rows` | array | Distance matrix rows \(one per origin\) |
|
||||
| ↳ `elements` | array | Elements \(one per destination\) |
|
||||
| ↳ `distanceText` | string | Distance as text \(e.g., "5.2 km"\) |
|
||||
| ↳ `distanceMeters` | number | Distance in meters |
|
||||
| ↳ `durationText` | string | Duration as text \(e.g., "15 mins"\) |
|
||||
| ↳ `durationSeconds` | number | Duration in seconds |
|
||||
| ↳ `durationInTrafficText` | string | Duration in traffic as text |
|
||||
| ↳ `durationInTrafficSeconds` | number | Duration in traffic in seconds |
|
||||
| ↳ `status` | string | Element status \(OK, NOT_FOUND, ZERO_RESULTS\) |
|
||||
|
||||
### `google_maps_elevation`
|
||||
|
||||
Get elevation data for a location
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key |
|
||||
| `lat` | number | Yes | Latitude coordinate |
|
||||
| `lng` | number | Yes | Longitude coordinate |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `elevation` | number | Elevation in meters above sea level \(negative for below\) |
|
||||
| `lat` | number | Latitude of the elevation sample |
|
||||
| `lng` | number | Longitude of the elevation sample |
|
||||
| `resolution` | number | Maximum distance between data points \(meters\) from which elevation was interpolated |
|
||||
|
||||
### `google_maps_geocode`
|
||||
|
||||
Convert an address into geographic coordinates (latitude and longitude)
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key |
|
||||
| `address` | string | Yes | The address to geocode |
|
||||
| `language` | string | No | Language code for results \(e.g., en, es, fr\) |
|
||||
| `region` | string | No | Region bias as a ccTLD code \(e.g., us, uk\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `formattedAddress` | string | The formatted address string |
|
||||
| `lat` | number | Latitude coordinate |
|
||||
| `lng` | number | Longitude coordinate |
|
||||
| `location` | json | Location object with lat and lng |
|
||||
| `placeId` | string | Google Place ID for this location |
|
||||
| `addressComponents` | array | Detailed address components |
|
||||
| ↳ `longName` | string | Full name of the component |
|
||||
| ↳ `shortName` | string | Abbreviated name |
|
||||
| ↳ `types` | array | Component types |
|
||||
| `locationType` | string | Location accuracy type \(ROOFTOP, RANGE_INTERPOLATED, etc.\) |
|
||||
|
||||
### `google_maps_geolocate`
|
||||
|
||||
Geolocate a device using WiFi access points, cell towers, or IP address
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key with Geolocation API enabled |
|
||||
| `homeMobileCountryCode` | number | No | Home mobile country code \(MCC\) |
|
||||
| `homeMobileNetworkCode` | number | No | Home mobile network code \(MNC\) |
|
||||
| `radioType` | string | No | Radio type: lte, gsm, cdma, wcdma, or nr |
|
||||
| `carrier` | string | No | Carrier name |
|
||||
| `considerIp` | boolean | No | Whether to use IP address for geolocation \(default: true\) |
|
||||
| `cellTowers` | array | No | Array of cell tower objects with cellId, locationAreaCode, mobileCountryCode, mobileNetworkCode |
|
||||
| `wifiAccessPoints` | array | No | Array of WiFi access point objects with macAddress \(required\), signalStrength, etc. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `lat` | number | Latitude coordinate |
|
||||
| `lng` | number | Longitude coordinate |
|
||||
| `accuracy` | number | Accuracy radius in meters |
|
||||
|
||||
### `google_maps_place_details`
|
||||
|
||||
Get detailed information about a specific place
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key |
|
||||
| `placeId` | string | Yes | Google Place ID |
|
||||
| `fields` | string | No | Comma-separated list of fields to return |
|
||||
| `language` | string | No | Language code for results \(e.g., en, es, fr\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `placeId` | string | Google Place ID |
|
||||
| `name` | string | Place name |
|
||||
| `formattedAddress` | string | Formatted street address |
|
||||
| `lat` | number | Latitude coordinate |
|
||||
| `lng` | number | Longitude coordinate |
|
||||
| `types` | array | Place types \(e.g., restaurant, cafe\) |
|
||||
| `rating` | number | Average rating \(1.0 to 5.0\) |
|
||||
| `userRatingsTotal` | number | Total number of user ratings |
|
||||
| `priceLevel` | number | Price level \(0=Free, 1=Inexpensive, 2=Moderate, 3=Expensive, 4=Very Expensive\) |
|
||||
| `website` | string | Place website URL |
|
||||
| `phoneNumber` | string | Local formatted phone number |
|
||||
| `internationalPhoneNumber` | string | International formatted phone number |
|
||||
| `openNow` | boolean | Whether the place is currently open |
|
||||
| `weekdayText` | array | Opening hours formatted by day of week |
|
||||
| `reviews` | array | User reviews \(up to 5 most relevant\) |
|
||||
| ↳ `authorName` | string | Reviewer name |
|
||||
| ↳ `authorUrl` | string | Reviewer profile URL |
|
||||
| ↳ `profilePhotoUrl` | string | Reviewer photo URL |
|
||||
| ↳ `rating` | number | Rating given \(1-5\) |
|
||||
| ↳ `text` | string | Review text |
|
||||
| ↳ `time` | number | Review timestamp \(Unix epoch\) |
|
||||
| ↳ `relativeTimeDescription` | string | Relative time \(e.g., "a month ago"\) |
|
||||
| `photos` | array | Place photos |
|
||||
| ↳ `photoReference` | string | Photo reference for Place Photos API |
|
||||
| ↳ `height` | number | Photo height in pixels |
|
||||
| ↳ `width` | number | Photo width in pixels |
|
||||
| ↳ `htmlAttributions` | array | Required attributions |
|
||||
| `url` | string | Google Maps URL for the place |
|
||||
| `utcOffset` | number | UTC offset in minutes |
|
||||
| `vicinity` | string | Simplified address \(neighborhood/street\) |
|
||||
| `businessStatus` | string | Business status \(OPERATIONAL, CLOSED_TEMPORARILY, CLOSED_PERMANENTLY\) |
|
||||
|
||||
### `google_maps_places_search`
|
||||
|
||||
Search for places using a text query
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key |
|
||||
| `query` | string | Yes | Search query \(e.g., "restaurants in Times Square"\) |
|
||||
| `location` | json | No | Location to bias results towards \(\{lat, lng\}\) |
|
||||
| `radius` | number | No | Search radius in meters |
|
||||
| `type` | string | No | Place type filter \(e.g., restaurant, cafe, hotel\) |
|
||||
| `language` | string | No | Language code for results \(e.g., en, es, fr\) |
|
||||
| `region` | string | No | Region bias as a ccTLD code \(e.g., us, uk\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `places` | array | List of places found |
|
||||
| ↳ `placeId` | string | Google Place ID |
|
||||
| ↳ `name` | string | Place name |
|
||||
| ↳ `formattedAddress` | string | Formatted address |
|
||||
| ↳ `lat` | number | Latitude |
|
||||
| ↳ `lng` | number | Longitude |
|
||||
| ↳ `types` | array | Place types |
|
||||
| ↳ `rating` | number | Average rating \(1-5\) |
|
||||
| ↳ `userRatingsTotal` | number | Number of ratings |
|
||||
| ↳ `priceLevel` | number | Price level \(0-4\) |
|
||||
| ↳ `openNow` | boolean | Whether currently open |
|
||||
| ↳ `photoReference` | string | Photo reference for Photos API |
|
||||
| ↳ `businessStatus` | string | Business status |
|
||||
| `nextPageToken` | string | Token for fetching the next page of results |
|
||||
|
||||
### `google_maps_reverse_geocode`
|
||||
|
||||
Convert geographic coordinates (latitude and longitude) into a human-readable address
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key |
|
||||
| `lat` | number | Yes | Latitude coordinate |
|
||||
| `lng` | number | Yes | Longitude coordinate |
|
||||
| `language` | string | No | Language code for results \(e.g., en, es, fr\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `formattedAddress` | string | The formatted address string |
|
||||
| `placeId` | string | Google Place ID for this location |
|
||||
| `addressComponents` | array | Detailed address components |
|
||||
| ↳ `longName` | string | Full name of the component |
|
||||
| ↳ `shortName` | string | Abbreviated name |
|
||||
| ↳ `types` | array | Component types |
|
||||
| `types` | array | Address types \(e.g., street_address, route\) |
|
||||
|
||||
### `google_maps_snap_to_roads`
|
||||
|
||||
Snap GPS coordinates to the nearest road segment
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key with Roads API enabled |
|
||||
| `path` | string | Yes | Pipe-separated list of lat,lng coordinates \(e.g., "60.170880,24.942795\|60.170879,24.942796"\) |
|
||||
| `interpolate` | boolean | No | Whether to interpolate additional points along the road |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `snappedPoints` | array | Array of snapped points on roads |
|
||||
| ↳ `location` | object | Snapped location coordinates |
|
||||
| ↳ `lat` | number | Latitude |
|
||||
| ↳ `lng` | number | Longitude |
|
||||
| ↳ `originalIndex` | number | Index in the original path \(if not interpolated\) |
|
||||
| ↳ `placeId` | string | Place ID for this road segment |
|
||||
| `warningMessage` | string | Warning message if any \(e.g., if points could not be snapped\) |
|
||||
|
||||
### `google_maps_speed_limits`
|
||||
|
||||
Get speed limits for road segments
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key with Roads API enabled |
|
||||
| `path` | string | No | Pipe-separated list of lat,lng coordinates \(e.g., "60.170880,24.942795\|60.170879,24.942796"\) |
|
||||
| `placeIds` | array | No | Array of Place IDs for road segments \(alternative to path\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `speedLimits` | array | Array of speed limits for road segments |
|
||||
| ↳ `placeId` | string | Place ID for the road segment |
|
||||
| ↳ `speedLimit` | number | Speed limit value |
|
||||
| ↳ `units` | string | Speed limit units \(KPH or MPH\) |
|
||||
| `snappedPoints` | array | Array of snapped points corresponding to the speed limits |
|
||||
| ↳ `location` | object | Snapped location coordinates |
|
||||
| ↳ `lat` | number | Latitude |
|
||||
| ↳ `lng` | number | Longitude |
|
||||
| ↳ `originalIndex` | number | Index in the original path |
|
||||
| ↳ `placeId` | string | Place ID for this road segment |
|
||||
|
||||
### `google_maps_timezone`
|
||||
|
||||
Get timezone information for a location
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key |
|
||||
| `lat` | number | Yes | Latitude coordinate |
|
||||
| `lng` | number | Yes | Longitude coordinate |
|
||||
| `timestamp` | number | No | Unix timestamp to determine DST offset \(defaults to current time\) |
|
||||
| `language` | string | No | Language code for timezone name \(e.g., en, es, fr\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `timeZoneId` | string | IANA timezone ID \(e.g., "America/New_York", "Europe/London"\) |
|
||||
| `timeZoneName` | string | Localized timezone name \(e.g., "Eastern Daylight Time"\) |
|
||||
| `rawOffset` | number | UTC offset in seconds \(without DST\) |
|
||||
| `dstOffset` | number | Daylight Saving Time offset in seconds \(0 if not in DST\) |
|
||||
| `totalOffsetSeconds` | number | Total UTC offset in seconds \(rawOffset + dstOffset\) |
|
||||
| `totalOffsetHours` | number | Total UTC offset in hours \(e.g., -5 for EST, -4 for EDT\) |
|
||||
|
||||
### `google_maps_validate_address`
|
||||
|
||||
Validate and standardize a postal address
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Google Maps API key with Address Validation API enabled |
|
||||
| `address` | string | Yes | The address to validate \(as a single string\) |
|
||||
| `regionCode` | string | No | ISO 3166-1 alpha-2 country code \(e.g., "US", "CA"\) |
|
||||
| `locality` | string | No | City or locality name |
|
||||
| `enableUspsCass` | boolean | No | Enable USPS CASS validation for US addresses |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `formattedAddress` | string | The standardized formatted address |
|
||||
| `lat` | number | Latitude coordinate |
|
||||
| `lng` | number | Longitude coordinate |
|
||||
| `placeId` | string | Google Place ID for this address |
|
||||
| `addressComplete` | boolean | Whether the address is complete and deliverable |
|
||||
| `hasUnconfirmedComponents` | boolean | Whether some address components could not be confirmed |
|
||||
| `hasInferredComponents` | boolean | Whether some components were inferred \(not in input\) |
|
||||
| `hasReplacedComponents` | boolean | Whether some components were replaced with canonical values |
|
||||
| `validationGranularity` | string | Granularity of validation \(PREMISE, SUB_PREMISE, ROUTE, etc.\) |
|
||||
| `geocodeGranularity` | string | Granularity of the geocode result |
|
||||
| `addressComponents` | array | Detailed address components |
|
||||
| ↳ `longName` | string | Full name of the component |
|
||||
| ↳ `shortName` | string | Abbreviated name |
|
||||
| ↳ `types` | array | Component types |
|
||||
| `missingComponentTypes` | array | Types of address components that are missing |
|
||||
| `unconfirmedComponentTypes` | array | Types of components that could not be confirmed |
|
||||
| `unresolvedTokens` | array | Input tokens that could not be resolved |
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"datadog",
|
||||
"discord",
|
||||
"dropbox",
|
||||
"dspy",
|
||||
"duckduckgo",
|
||||
"dynamodb",
|
||||
"elasticsearch",
|
||||
@@ -35,6 +36,7 @@
|
||||
"google_drive",
|
||||
"google_forms",
|
||||
"google_groups",
|
||||
"google_maps",
|
||||
"google_search",
|
||||
"google_sheets",
|
||||
"google_slides",
|
||||
|
||||
@@ -61,12 +61,12 @@ Get comprehensive website analytics including traffic, rankings, engagement, and
|
||||
| ↳ `country` | string | Country code |
|
||||
| ↳ `share` | number | Traffic share \(0-1\) |
|
||||
| `trafficSources` | json | Traffic source breakdown |
|
||||
| ↳ `direct` | number | Direct traffic share |
|
||||
| ↳ `referrals` | number | Referral traffic share |
|
||||
| ↳ `search` | number | Search traffic share |
|
||||
| ↳ `social` | number | Social traffic share |
|
||||
| ↳ `mail` | number | Email traffic share |
|
||||
| ↳ `paidReferrals` | number | Paid referral traffic share |
|
||||
| ↳ `direct` | number | Direct traffic share |
|
||||
| ↳ `referrals` | number | Referral traffic share |
|
||||
| ↳ `search` | number | Search traffic share |
|
||||
| ↳ `social` | number | Social traffic share |
|
||||
| ↳ `mail` | number | Email traffic share |
|
||||
| ↳ `paidReferrals` | number | Paid referral traffic share |
|
||||
|
||||
### `similarweb_traffic_visits`
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ Search for videos on YouTube using the YouTube Data API. Supports advanced filte
|
||||
| ↳ `channelId` | string | Channel ID that uploaded the video |
|
||||
| ↳ `channelTitle` | string | Channel name |
|
||||
| ↳ `publishedAt` | string | Video publish date |
|
||||
| ↳ `liveBroadcastContent` | string | Live broadcast status: |
|
||||
| ↳ `liveBroadcastContent` | string | Live broadcast status: "none", "live", or "upcoming" |
|
||||
| `totalResults` | number | Total number of search results available |
|
||||
| `nextPageToken` | string | Token for accessing the next page of results |
|
||||
|
||||
@@ -271,7 +271,7 @@ Get a list of video categories available on YouTube. Use this to discover valid
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `items` | array | Array of video categories available in the specified region |
|
||||
| ↳ `categoryId` | string | Category ID to use in search/trending filters \(e.g., |
|
||||
| ↳ `categoryId` | string | Category ID to use in search/trending filters \(e.g., "10" for Music\) |
|
||||
| ↳ `title` | string | Human-readable category name |
|
||||
| ↳ `assignable` | boolean | Whether videos can be tagged with this category |
|
||||
| `totalResults` | number | Total number of categories available |
|
||||
@@ -297,7 +297,7 @@ Get detailed information about a specific YouTube video including statistics, co
|
||||
| `channelId` | string | Channel ID |
|
||||
| `channelTitle` | string | Channel name |
|
||||
| `publishedAt` | string | Published date and time |
|
||||
| `duration` | string | Video duration in ISO 8601 format \(e.g., |
|
||||
| `duration` | string | Video duration in ISO 8601 format \(e.g., "PT4M13S" for 4 min 13 sec\) |
|
||||
| `viewCount` | number | Number of views |
|
||||
| `likeCount` | number | Number of likes |
|
||||
| `commentCount` | number | Number of comments |
|
||||
@@ -305,11 +305,11 @@ Get detailed information about a specific YouTube video including statistics, co
|
||||
| `thumbnail` | string | Video thumbnail URL |
|
||||
| `tags` | array | Video tags |
|
||||
| `categoryId` | string | YouTube video category ID |
|
||||
| `definition` | string | Video definition: |
|
||||
| `caption` | string | Whether captions are available: |
|
||||
| `definition` | string | Video definition: "hd" or "sd" |
|
||||
| `caption` | string | Whether captions are available: "true" or "false" |
|
||||
| `licensedContent` | boolean | Whether the video is licensed content |
|
||||
| `privacyStatus` | string | Video privacy status: |
|
||||
| `liveBroadcastContent` | string | Live broadcast status: |
|
||||
| `privacyStatus` | string | Video privacy status: "public", "private", or "unlisted" |
|
||||
| `liveBroadcastContent` | string | Live broadcast status: "live", "upcoming", or "none" |
|
||||
| `defaultLanguage` | string | Default language of the video metadata |
|
||||
| `defaultAudioLanguage` | string | Default audio language of the video |
|
||||
| `isLiveContent` | boolean | Whether this video is or was a live stream |
|
||||
|
||||
@@ -6,7 +6,7 @@ import { getTrigger } from '@/triggers'
|
||||
|
||||
export const CalComBlock: BlockConfig<ToolResponse> = {
|
||||
type: 'calcom',
|
||||
name: 'CalCom',
|
||||
name: 'Cal Com',
|
||||
description: 'Manage Cal.com bookings, event types, schedules, and availability',
|
||||
authMode: AuthMode.OAuth,
|
||||
triggerAllowed: true,
|
||||
|
||||
175
apps/sim/blocks/blocks/dspy.ts
Normal file
175
apps/sim/blocks/blocks/dspy.ts
Normal file
@@ -0,0 +1,175 @@
|
||||
import { DsPyIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
export const DSPyBlock: BlockConfig = {
|
||||
type: 'dspy',
|
||||
name: 'DSPy',
|
||||
description: 'Run predictions using self-hosted DSPy programs',
|
||||
longDescription:
|
||||
'Integrate with your self-hosted DSPy programs for LLM-powered predictions. Supports Predict, Chain of Thought, and ReAct agents. DSPy is the framework for programming—not prompting—language models.',
|
||||
category: 'tools',
|
||||
bgColor: '#1E293B',
|
||||
icon: DsPyIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Predict', id: 'predict' },
|
||||
{ label: 'Chain of Thought', id: 'chain_of_thought' },
|
||||
{ label: 'ReAct Agent', id: 'react' },
|
||||
],
|
||||
value: () => 'predict',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'baseUrl',
|
||||
title: 'Base URL',
|
||||
type: 'short-input',
|
||||
placeholder: 'https://your-dspy-server.com',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Optional API key for authentication',
|
||||
},
|
||||
{
|
||||
id: 'endpoint',
|
||||
title: 'Endpoint',
|
||||
type: 'short-input',
|
||||
placeholder: '/predict',
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Predict operation fields
|
||||
{
|
||||
id: 'input',
|
||||
title: 'Input',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter your input text',
|
||||
condition: { field: 'operation', value: 'predict' },
|
||||
required: { field: 'operation', value: 'predict' },
|
||||
rows: 4,
|
||||
},
|
||||
{
|
||||
id: 'inputField',
|
||||
title: 'Input Field Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'text (defaults to "text")',
|
||||
condition: { field: 'operation', value: 'predict' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'additionalInputs',
|
||||
title: 'Additional Inputs',
|
||||
type: 'long-input',
|
||||
placeholder: '{"key": "value"} - JSON object with extra fields',
|
||||
condition: { field: 'operation', value: 'predict' },
|
||||
mode: 'advanced',
|
||||
rows: 3,
|
||||
},
|
||||
|
||||
// Chain of Thought operation fields
|
||||
{
|
||||
id: 'question',
|
||||
title: 'Question',
|
||||
type: 'long-input',
|
||||
placeholder: 'Enter your question',
|
||||
condition: { field: 'operation', value: 'chain_of_thought' },
|
||||
required: { field: 'operation', value: 'chain_of_thought' },
|
||||
rows: 4,
|
||||
},
|
||||
|
||||
// ReAct operation fields
|
||||
{
|
||||
id: 'task',
|
||||
title: 'Task',
|
||||
type: 'long-input',
|
||||
placeholder: 'Describe the task for the ReAct agent',
|
||||
condition: { field: 'operation', value: 'react' },
|
||||
required: { field: 'operation', value: 'react' },
|
||||
rows: 4,
|
||||
},
|
||||
{
|
||||
id: 'maxIterations',
|
||||
title: 'Max Iterations',
|
||||
type: 'short-input',
|
||||
placeholder: 'Maximum reasoning iterations',
|
||||
condition: { field: 'operation', value: 'react' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Common optional fields
|
||||
{
|
||||
id: 'context',
|
||||
title: 'Context',
|
||||
type: 'long-input',
|
||||
placeholder: 'Additional context for the DSPy program',
|
||||
mode: 'advanced',
|
||||
rows: 4,
|
||||
},
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: ['dspy_predict', 'dspy_chain_of_thought', 'dspy_react'],
|
||||
config: {
|
||||
tool: (params) => `dspy_${params.operation}`,
|
||||
params: (params) => {
|
||||
const { operation, additionalInputs, maxIterations, ...rest } = params
|
||||
|
||||
let parsedAdditionalInputs: Record<string, unknown> | undefined
|
||||
if (additionalInputs && typeof additionalInputs === 'string') {
|
||||
try {
|
||||
parsedAdditionalInputs = JSON.parse(additionalInputs)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
|
||||
let parsedMaxIterations: number | undefined
|
||||
if (maxIterations) {
|
||||
const parsed = Number.parseInt(maxIterations as string, 10)
|
||||
if (!Number.isNaN(parsed)) {
|
||||
parsedMaxIterations = parsed
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...rest,
|
||||
additionalInputs: parsedAdditionalInputs,
|
||||
maxIterations: parsedMaxIterations,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'DSPy operation to perform' },
|
||||
baseUrl: { type: 'string', description: 'Base URL of the DSPy server' },
|
||||
apiKey: { type: 'string', description: 'API key for authentication' },
|
||||
endpoint: { type: 'string', description: 'API endpoint path' },
|
||||
input: { type: 'string', description: 'Input text for Predict operation' },
|
||||
inputField: { type: 'string', description: 'Name of the input field' },
|
||||
context: { type: 'string', description: 'Additional context for the program' },
|
||||
additionalInputs: { type: 'string', description: 'JSON object with extra fields' },
|
||||
question: { type: 'string', description: 'Question for Chain of Thought' },
|
||||
task: { type: 'string', description: 'Task for ReAct agent' },
|
||||
maxIterations: { type: 'string', description: 'Max iterations for ReAct' },
|
||||
},
|
||||
|
||||
outputs: {
|
||||
answer: { type: 'string', description: 'The answer/output from the DSPy program' },
|
||||
reasoning: { type: 'string', description: 'The reasoning or rationale behind the answer' },
|
||||
trajectory: {
|
||||
type: 'json',
|
||||
description: 'Step-by-step trajectory for ReAct (thoughts, actions, observations)',
|
||||
},
|
||||
status: { type: 'string', description: 'Response status from the DSPy server' },
|
||||
rawOutput: { type: 'json', description: 'Complete raw output from the DSPy program' },
|
||||
},
|
||||
}
|
||||
626
apps/sim/blocks/blocks/google_maps.ts
Normal file
626
apps/sim/blocks/blocks/google_maps.ts
Normal file
@@ -0,0 +1,626 @@
|
||||
import { GoogleMapsIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
export const GoogleMapsBlock: BlockConfig = {
|
||||
type: 'google_maps',
|
||||
name: 'Google Maps',
|
||||
description: 'Geocoding, directions, places, and distance calculations',
|
||||
longDescription:
|
||||
'Integrate Google Maps Platform APIs into your workflow. Supports geocoding addresses to coordinates, reverse geocoding, getting directions between locations, calculating distance matrices, searching for places, retrieving place details, elevation data, and timezone information.',
|
||||
docsLink: 'https://docs.sim.ai/tools/google_maps',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
icon: GoogleMapsIcon,
|
||||
|
||||
subBlocks: [
|
||||
// Operation selector
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Geocode Address', id: 'geocode' },
|
||||
{ label: 'Reverse Geocode', id: 'reverse_geocode' },
|
||||
{ label: 'Get Directions', id: 'directions' },
|
||||
{ label: 'Distance Matrix', id: 'distance_matrix' },
|
||||
{ label: 'Search Places', id: 'places_search' },
|
||||
{ label: 'Place Details', id: 'place_details' },
|
||||
{ label: 'Get Elevation', id: 'elevation' },
|
||||
{ label: 'Get Timezone', id: 'timezone' },
|
||||
{ label: 'Snap to Roads', id: 'snap_to_roads' },
|
||||
{ label: 'Speed Limits', id: 'speed_limits' },
|
||||
{ label: 'Validate Address', id: 'validate_address' },
|
||||
{ label: 'Geolocate (WiFi/Cell)', id: 'geolocate' },
|
||||
{ label: 'Air Quality', id: 'air_quality' },
|
||||
],
|
||||
value: () => 'geocode',
|
||||
},
|
||||
|
||||
// API Key
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'API Key',
|
||||
type: 'short-input',
|
||||
password: true,
|
||||
placeholder: 'Enter your Google Maps API key',
|
||||
required: true,
|
||||
},
|
||||
|
||||
// ========== Geocode ==========
|
||||
{
|
||||
id: 'address',
|
||||
title: 'Address',
|
||||
type: 'long-input',
|
||||
placeholder: '1600 Amphitheatre Parkway, Mountain View, CA',
|
||||
condition: { field: 'operation', value: 'geocode' },
|
||||
required: { field: 'operation', value: 'geocode' },
|
||||
rows: 2,
|
||||
},
|
||||
|
||||
{
|
||||
id: 'latitude',
|
||||
title: 'Latitude',
|
||||
type: 'short-input',
|
||||
placeholder: '37.4224764',
|
||||
condition: { field: 'operation', value: ['reverse_geocode', 'elevation', 'timezone'] },
|
||||
required: { field: 'operation', value: ['reverse_geocode', 'elevation', 'timezone'] },
|
||||
},
|
||||
{
|
||||
id: 'longitude',
|
||||
title: 'Longitude',
|
||||
type: 'short-input',
|
||||
placeholder: '-122.0842499',
|
||||
condition: { field: 'operation', value: ['reverse_geocode', 'elevation', 'timezone'] },
|
||||
required: { field: 'operation', value: ['reverse_geocode', 'elevation', 'timezone'] },
|
||||
},
|
||||
|
||||
{
|
||||
id: 'timestamp',
|
||||
title: 'Timestamp',
|
||||
type: 'short-input',
|
||||
placeholder: 'Unix timestamp (defaults to current time)',
|
||||
condition: { field: 'operation', value: 'timezone' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'origin',
|
||||
title: 'Origin',
|
||||
type: 'short-input',
|
||||
placeholder: 'Starting address or coordinates (lat,lng)',
|
||||
condition: { field: 'operation', value: ['directions', 'distance_matrix'] },
|
||||
required: { field: 'operation', value: ['directions', 'distance_matrix'] },
|
||||
},
|
||||
{
|
||||
id: 'destination',
|
||||
title: 'Destination',
|
||||
type: 'short-input',
|
||||
placeholder: 'Destination address or coordinates (lat,lng)',
|
||||
condition: { field: 'operation', value: 'directions' },
|
||||
required: { field: 'operation', value: 'directions' },
|
||||
},
|
||||
{
|
||||
id: 'destinations',
|
||||
title: 'Destinations',
|
||||
type: 'long-input',
|
||||
placeholder: 'Destination addresses separated by | (e.g., New York, NY|Boston, MA)',
|
||||
condition: { field: 'operation', value: 'distance_matrix' },
|
||||
required: { field: 'operation', value: 'distance_matrix' },
|
||||
rows: 3,
|
||||
},
|
||||
{
|
||||
id: 'mode',
|
||||
title: 'Travel Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Driving', id: 'driving' },
|
||||
{ label: 'Walking', id: 'walking' },
|
||||
{ label: 'Bicycling', id: 'bicycling' },
|
||||
{ label: 'Transit', id: 'transit' },
|
||||
],
|
||||
value: () => 'driving',
|
||||
condition: { field: 'operation', value: ['directions', 'distance_matrix'] },
|
||||
},
|
||||
{
|
||||
id: 'avoid',
|
||||
title: 'Avoid',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'None', id: '' },
|
||||
{ label: 'Tolls', id: 'tolls' },
|
||||
{ label: 'Highways', id: 'highways' },
|
||||
{ label: 'Ferries', id: 'ferries' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['directions', 'distance_matrix'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'waypoints',
|
||||
title: 'Waypoints',
|
||||
type: 'long-input',
|
||||
placeholder: 'Optional stops separated by | (e.g., Stop 1|Stop 2)',
|
||||
condition: { field: 'operation', value: 'directions' },
|
||||
rows: 2,
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'units',
|
||||
title: 'Units',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Metric (km)', id: 'metric' },
|
||||
{ label: 'Imperial (miles)', id: 'imperial' },
|
||||
],
|
||||
value: () => 'metric',
|
||||
condition: { field: 'operation', value: ['directions', 'distance_matrix'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Search Query',
|
||||
type: 'short-input',
|
||||
placeholder: 'restaurants near Times Square',
|
||||
condition: { field: 'operation', value: 'places_search' },
|
||||
required: { field: 'operation', value: 'places_search' },
|
||||
},
|
||||
{
|
||||
id: 'locationBias',
|
||||
title: 'Location Bias',
|
||||
type: 'short-input',
|
||||
placeholder: 'lat,lng to bias results (e.g., 40.7580,-73.9855)',
|
||||
condition: { field: 'operation', value: 'places_search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'radius',
|
||||
title: 'Radius (meters)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Search radius in meters (e.g., 5000)',
|
||||
condition: { field: 'operation', value: 'places_search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'placeType',
|
||||
title: 'Place Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Any', id: '' },
|
||||
{ label: 'Restaurant', id: 'restaurant' },
|
||||
{ label: 'Cafe', id: 'cafe' },
|
||||
{ label: 'Bar', id: 'bar' },
|
||||
{ label: 'Hotel', id: 'lodging' },
|
||||
{ label: 'Gas Station', id: 'gas_station' },
|
||||
{ label: 'Hospital', id: 'hospital' },
|
||||
{ label: 'Pharmacy', id: 'pharmacy' },
|
||||
{ label: 'Bank', id: 'bank' },
|
||||
{ label: 'ATM', id: 'atm' },
|
||||
{ label: 'Grocery Store', id: 'supermarket' },
|
||||
{ label: 'Shopping Mall', id: 'shopping_mall' },
|
||||
{ label: 'Gym', id: 'gym' },
|
||||
{ label: 'Park', id: 'park' },
|
||||
{ label: 'Museum', id: 'museum' },
|
||||
{ label: 'Movie Theater', id: 'movie_theater' },
|
||||
{ label: 'Airport', id: 'airport' },
|
||||
{ label: 'Train Station', id: 'train_station' },
|
||||
{ label: 'Bus Station', id: 'bus_station' },
|
||||
{ label: 'Parking', id: 'parking' },
|
||||
],
|
||||
condition: { field: 'operation', value: 'places_search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'placeId',
|
||||
title: 'Place ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Google Place ID (e.g., ChIJN1t_tDeuEmsRUsoyG83frY4)',
|
||||
condition: { field: 'operation', value: 'place_details' },
|
||||
required: { field: 'operation', value: 'place_details' },
|
||||
},
|
||||
{
|
||||
id: 'fields',
|
||||
title: 'Fields',
|
||||
type: 'short-input',
|
||||
placeholder: 'name,formatted_address,rating,opening_hours',
|
||||
condition: { field: 'operation', value: 'place_details' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'path',
|
||||
title: 'Path',
|
||||
type: 'long-input',
|
||||
placeholder: 'Pipe-separated lat,lng pairs (e.g., 60.170880,24.942795|60.170879,24.942796)',
|
||||
condition: { field: 'operation', value: ['snap_to_roads', 'speed_limits'] },
|
||||
required: { field: 'operation', value: 'snap_to_roads' },
|
||||
rows: 3,
|
||||
},
|
||||
{
|
||||
id: 'interpolate',
|
||||
title: 'Interpolate',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'snap_to_roads' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'placeIds',
|
||||
title: 'Place IDs',
|
||||
type: 'long-input',
|
||||
placeholder: 'Pipe-separated Place IDs (alternative to path)',
|
||||
condition: { field: 'operation', value: 'speed_limits' },
|
||||
rows: 2,
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'addressToValidate',
|
||||
title: 'Address',
|
||||
type: 'long-input',
|
||||
placeholder: '1600 Amphitheatre Parkway, Mountain View, CA 94043',
|
||||
condition: { field: 'operation', value: 'validate_address' },
|
||||
required: { field: 'operation', value: 'validate_address' },
|
||||
rows: 2,
|
||||
},
|
||||
{
|
||||
id: 'regionCode',
|
||||
title: 'Region Code',
|
||||
type: 'short-input',
|
||||
placeholder: 'ISO country code (e.g., US, CA, GB)',
|
||||
condition: { field: 'operation', value: 'validate_address' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'locality',
|
||||
title: 'Locality',
|
||||
type: 'short-input',
|
||||
placeholder: 'City name (optional hint)',
|
||||
condition: { field: 'operation', value: 'validate_address' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'enableUspsCass',
|
||||
title: 'Enable USPS CASS',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'validate_address' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'considerIp',
|
||||
title: 'Use IP Address',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'geolocate' },
|
||||
},
|
||||
{
|
||||
id: 'radioType',
|
||||
title: 'Radio Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'None', id: '' },
|
||||
{ label: 'LTE', id: 'lte' },
|
||||
{ label: 'GSM', id: 'gsm' },
|
||||
{ label: 'CDMA', id: 'cdma' },
|
||||
{ label: 'WCDMA', id: 'wcdma' },
|
||||
{ label: '5G NR', id: 'nr' },
|
||||
],
|
||||
condition: { field: 'operation', value: 'geolocate' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'carrier',
|
||||
title: 'Carrier',
|
||||
type: 'short-input',
|
||||
placeholder: 'Carrier name',
|
||||
condition: { field: 'operation', value: 'geolocate' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'wifiAccessPoints',
|
||||
title: 'WiFi Access Points',
|
||||
type: 'long-input',
|
||||
placeholder: 'JSON array of WiFi APs: [{"macAddress": "..."}]',
|
||||
condition: { field: 'operation', value: 'geolocate' },
|
||||
rows: 3,
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'cellTowers',
|
||||
title: 'Cell Towers',
|
||||
type: 'long-input',
|
||||
placeholder: 'JSON array of cell towers: [{"cellId": ..., "locationAreaCode": ...}]',
|
||||
condition: { field: 'operation', value: 'geolocate' },
|
||||
rows: 3,
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'aqLatitude',
|
||||
title: 'Latitude',
|
||||
type: 'short-input',
|
||||
placeholder: '37.4224764',
|
||||
condition: { field: 'operation', value: 'air_quality' },
|
||||
required: { field: 'operation', value: 'air_quality' },
|
||||
},
|
||||
{
|
||||
id: 'aqLongitude',
|
||||
title: 'Longitude',
|
||||
type: 'short-input',
|
||||
placeholder: '-122.0842499',
|
||||
condition: { field: 'operation', value: 'air_quality' },
|
||||
required: { field: 'operation', value: 'air_quality' },
|
||||
},
|
||||
{
|
||||
id: 'languageCode',
|
||||
title: 'Language Code',
|
||||
type: 'short-input',
|
||||
placeholder: 'Language code (e.g., en, es)',
|
||||
condition: { field: 'operation', value: 'air_quality' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
{
|
||||
id: 'language',
|
||||
title: 'Language',
|
||||
type: 'short-input',
|
||||
placeholder: 'Language code (e.g., en, es, fr, de)',
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'region',
|
||||
title: 'Region Bias',
|
||||
type: 'short-input',
|
||||
placeholder: 'Country code (e.g., us, uk, de)',
|
||||
mode: 'advanced',
|
||||
condition: { field: 'operation', value: ['geocode', 'places_search'] },
|
||||
},
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: [
|
||||
'google_maps_air_quality',
|
||||
'google_maps_directions',
|
||||
'google_maps_distance_matrix',
|
||||
'google_maps_elevation',
|
||||
'google_maps_geocode',
|
||||
'google_maps_geolocate',
|
||||
'google_maps_place_details',
|
||||
'google_maps_places_search',
|
||||
'google_maps_reverse_geocode',
|
||||
'google_maps_snap_to_roads',
|
||||
'google_maps_speed_limits',
|
||||
'google_maps_timezone',
|
||||
'google_maps_validate_address',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => `google_maps_${params.operation}`,
|
||||
params: (params) => {
|
||||
const { operation, locationBias, ...rest } = params
|
||||
|
||||
let location: { lat: number; lng: number } | undefined
|
||||
if (locationBias && typeof locationBias === 'string' && locationBias.includes(',')) {
|
||||
const [lat, lng] = locationBias.split(',').map((s) => Number.parseFloat(s.trim()))
|
||||
if (!Number.isNaN(lat) && !Number.isNaN(lng)) {
|
||||
location = { lat, lng }
|
||||
}
|
||||
}
|
||||
|
||||
let lat: number | undefined
|
||||
let lng: number | undefined
|
||||
if (params.latitude) {
|
||||
lat = Number.parseFloat(params.latitude)
|
||||
}
|
||||
if (params.longitude) {
|
||||
lng = Number.parseFloat(params.longitude)
|
||||
}
|
||||
|
||||
if (params.aqLatitude) {
|
||||
lat = Number.parseFloat(params.aqLatitude)
|
||||
}
|
||||
if (params.aqLongitude) {
|
||||
lng = Number.parseFloat(params.aqLongitude)
|
||||
}
|
||||
|
||||
let timestamp: number | undefined
|
||||
if (params.timestamp) {
|
||||
timestamp = Number.parseInt(params.timestamp, 10)
|
||||
}
|
||||
|
||||
let destinations: string[] | undefined
|
||||
if (params.destinations && typeof params.destinations === 'string') {
|
||||
destinations = params.destinations.split('|').map((d: string) => d.trim())
|
||||
}
|
||||
|
||||
let waypoints: string[] | undefined
|
||||
if (params.waypoints && typeof params.waypoints === 'string') {
|
||||
waypoints = params.waypoints
|
||||
.split('|')
|
||||
.map((w: string) => w.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
let radius: number | undefined
|
||||
if (params.radius) {
|
||||
radius = Number.parseInt(params.radius, 10)
|
||||
}
|
||||
|
||||
let placeIds: string[] | undefined
|
||||
if (params.placeIds && typeof params.placeIds === 'string') {
|
||||
placeIds = params.placeIds
|
||||
.split('|')
|
||||
.map((p: string) => p.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
let wifiAccessPoints: unknown[] | undefined
|
||||
if (params.wifiAccessPoints && typeof params.wifiAccessPoints === 'string') {
|
||||
try {
|
||||
wifiAccessPoints = JSON.parse(params.wifiAccessPoints)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
|
||||
let cellTowers: unknown[] | undefined
|
||||
if (params.cellTowers && typeof params.cellTowers === 'string') {
|
||||
try {
|
||||
cellTowers = JSON.parse(params.cellTowers)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
|
||||
const address = params.addressToValidate || params.address
|
||||
|
||||
// Parse boolean switches (can come as string or boolean from form)
|
||||
let interpolate: boolean | undefined
|
||||
if (params.interpolate !== undefined) {
|
||||
interpolate = params.interpolate === 'true' || params.interpolate === true
|
||||
}
|
||||
|
||||
let enableUspsCass: boolean | undefined
|
||||
if (params.enableUspsCass !== undefined) {
|
||||
enableUspsCass = params.enableUspsCass === 'true' || params.enableUspsCass === true
|
||||
}
|
||||
|
||||
let considerIp: boolean | undefined
|
||||
if (params.considerIp !== undefined) {
|
||||
considerIp = params.considerIp === 'true' || params.considerIp === true
|
||||
}
|
||||
|
||||
return {
|
||||
...rest,
|
||||
address,
|
||||
location,
|
||||
lat,
|
||||
lng,
|
||||
timestamp,
|
||||
destinations,
|
||||
waypoints,
|
||||
radius,
|
||||
placeIds,
|
||||
wifiAccessPoints,
|
||||
cellTowers,
|
||||
interpolate,
|
||||
enableUspsCass,
|
||||
considerIp,
|
||||
type: params.placeType || undefined,
|
||||
avoid: params.avoid || undefined,
|
||||
radioType: params.radioType || undefined,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
apiKey: { type: 'string', description: 'Google Maps API key' },
|
||||
address: { type: 'string', description: 'Address to geocode' },
|
||||
latitude: { type: 'string', description: 'Latitude coordinate' },
|
||||
longitude: { type: 'string', description: 'Longitude coordinate' },
|
||||
timestamp: { type: 'string', description: 'Unix timestamp for timezone' },
|
||||
origin: { type: 'string', description: 'Starting location' },
|
||||
destination: { type: 'string', description: 'Destination location' },
|
||||
destinations: { type: 'string', description: 'Multiple destinations (pipe-separated)' },
|
||||
mode: { type: 'string', description: 'Travel mode' },
|
||||
avoid: { type: 'string', description: 'Features to avoid' },
|
||||
waypoints: { type: 'string', description: 'Waypoints (pipe-separated)' },
|
||||
query: { type: 'string', description: 'Places search query' },
|
||||
locationBias: { type: 'string', description: 'Location bias for search' },
|
||||
radius: { type: 'string', description: 'Search radius in meters' },
|
||||
placeType: { type: 'string', description: 'Place type filter' },
|
||||
placeId: { type: 'string', description: 'Google Place ID' },
|
||||
fields: { type: 'string', description: 'Fields to retrieve' },
|
||||
units: { type: 'string', description: 'Unit system' },
|
||||
language: { type: 'string', description: 'Response language' },
|
||||
region: { type: 'string', description: 'Region bias' },
|
||||
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' },
|
||||
addressToValidate: { type: 'string', description: 'Address to validate' },
|
||||
regionCode: { type: 'string', description: 'ISO country code for address' },
|
||||
locality: { type: 'string', description: 'City name hint' },
|
||||
enableUspsCass: { type: 'boolean', description: 'Enable USPS CASS validation' },
|
||||
considerIp: { type: 'boolean', description: 'Use IP for geolocation' },
|
||||
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' },
|
||||
aqLongitude: { type: 'string', description: 'Longitude for air quality' },
|
||||
languageCode: { type: 'string', description: 'Language code for air quality' },
|
||||
},
|
||||
|
||||
outputs: {
|
||||
formattedAddress: { type: 'string', description: 'Formatted address string' },
|
||||
lat: { type: 'number', description: 'Latitude coordinate' },
|
||||
lng: { type: 'number', description: 'Longitude coordinate' },
|
||||
placeId: { type: 'string', description: 'Google Place ID' },
|
||||
addressComponents: { type: 'json', description: 'Detailed address components' },
|
||||
locationType: { type: 'string', description: 'Location accuracy type' },
|
||||
types: { type: 'json', description: 'Address or place types' },
|
||||
|
||||
routes: { type: 'json', description: 'Available routes' },
|
||||
distanceText: { type: 'string', description: 'Distance as text (e.g., "5.2 km")' },
|
||||
distanceMeters: { type: 'number', description: 'Distance in meters' },
|
||||
durationText: { type: 'string', description: 'Duration as text (e.g., "15 mins")' },
|
||||
durationSeconds: { type: 'number', description: 'Duration in seconds' },
|
||||
startAddress: { type: 'string', description: 'Starting address' },
|
||||
endAddress: { type: 'string', description: 'Ending address' },
|
||||
steps: { type: 'json', description: 'Turn-by-turn directions' },
|
||||
polyline: { type: 'string', description: 'Encoded polyline for the route' },
|
||||
|
||||
rows: { type: 'json', description: 'Distance matrix rows' },
|
||||
originAddresses: { type: 'json', description: 'Resolved origin addresses' },
|
||||
destinationAddresses: { type: 'json', description: 'Resolved destination addresses' },
|
||||
|
||||
places: { type: 'json', description: 'List of places found' },
|
||||
nextPageToken: { type: 'string', description: 'Token for next page of results' },
|
||||
|
||||
name: { type: 'string', description: 'Place name' },
|
||||
rating: { type: 'number', description: 'Place rating (1-5)' },
|
||||
userRatingsTotal: { type: 'number', description: 'Number of user ratings' },
|
||||
priceLevel: { type: 'number', description: 'Price level (0-4)' },
|
||||
website: { type: 'string', description: 'Place website' },
|
||||
phoneNumber: { type: 'string', description: 'Place phone number' },
|
||||
internationalPhoneNumber: { type: 'string', description: 'International phone number' },
|
||||
openNow: { type: 'boolean', description: 'Whether place is currently open' },
|
||||
weekdayText: { type: 'json', description: 'Opening hours by day' },
|
||||
reviews: { type: 'json', description: 'Place reviews' },
|
||||
photos: { type: 'json', description: 'Place photos' },
|
||||
url: { type: 'string', description: 'Google Maps URL for the place' },
|
||||
vicinity: { type: 'string', description: 'Simplified address' },
|
||||
|
||||
elevation: { type: 'number', description: 'Elevation in meters' },
|
||||
resolution: { type: 'number', description: 'Data resolution in meters' },
|
||||
|
||||
timeZoneId: { type: 'string', description: 'Timezone ID (e.g., America/New_York)' },
|
||||
timeZoneName: { type: 'string', description: 'Timezone display name' },
|
||||
rawOffset: { type: 'number', description: 'UTC offset in seconds (without DST)' },
|
||||
dstOffset: { type: 'number', description: 'DST offset in seconds' },
|
||||
|
||||
snappedPoints: { type: 'json', description: 'Snapped road coordinates' },
|
||||
warningMessage: { type: 'string', description: 'Warning message if any' },
|
||||
|
||||
speedLimits: { type: 'json', description: 'Speed limits for road segments' },
|
||||
|
||||
addressComplete: { type: 'boolean', description: 'Whether address is complete' },
|
||||
hasUnconfirmedComponents: { type: 'boolean', description: 'Has unconfirmed components' },
|
||||
hasInferredComponents: { type: 'boolean', description: 'Has inferred components' },
|
||||
hasReplacedComponents: { type: 'boolean', description: 'Has replaced components' },
|
||||
validationGranularity: { type: 'string', description: 'Validation granularity level' },
|
||||
geocodeGranularity: { type: 'string', description: 'Geocode granularity level' },
|
||||
missingComponentTypes: { type: 'json', description: 'Missing address component types' },
|
||||
unconfirmedComponentTypes: { type: 'json', description: 'Unconfirmed component types' },
|
||||
unresolvedTokens: { type: 'json', description: 'Unresolved input tokens' },
|
||||
|
||||
accuracy: { type: 'number', description: 'Location accuracy in meters' },
|
||||
|
||||
dateTime: { type: 'string', description: 'Air quality data timestamp' },
|
||||
regionCode: { type: 'string', description: 'Region code' },
|
||||
indexes: { type: 'json', description: 'Air quality indexes' },
|
||||
pollutants: { type: 'json', description: 'Pollutant concentrations' },
|
||||
healthRecommendations: { type: 'json', description: 'Health recommendations' },
|
||||
},
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import { CursorBlock, CursorV2Block } from '@/blocks/blocks/cursor'
|
||||
import { DatadogBlock } from '@/blocks/blocks/datadog'
|
||||
import { DiscordBlock } from '@/blocks/blocks/discord'
|
||||
import { DropboxBlock } from '@/blocks/blocks/dropbox'
|
||||
import { DSPyBlock } from '@/blocks/blocks/dspy'
|
||||
import { DuckDuckGoBlock } from '@/blocks/blocks/duckduckgo'
|
||||
import { DynamoDBBlock } from '@/blocks/blocks/dynamodb'
|
||||
import { ElasticsearchBlock } from '@/blocks/blocks/elasticsearch'
|
||||
@@ -41,6 +42,7 @@ import { GoogleDocsBlock } from '@/blocks/blocks/google_docs'
|
||||
import { GoogleDriveBlock } from '@/blocks/blocks/google_drive'
|
||||
import { GoogleFormsBlock } from '@/blocks/blocks/google_forms'
|
||||
import { GoogleGroupsBlock } from '@/blocks/blocks/google_groups'
|
||||
import { GoogleMapsBlock } from '@/blocks/blocks/google_maps'
|
||||
import { GoogleSheetsBlock, GoogleSheetsV2Block } from '@/blocks/blocks/google_sheets'
|
||||
import { GoogleSlidesBlock } from '@/blocks/blocks/google_slides'
|
||||
import { GoogleVaultBlock } from '@/blocks/blocks/google_vault'
|
||||
@@ -181,6 +183,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
datadog: DatadogBlock,
|
||||
discord: DiscordBlock,
|
||||
dropbox: DropboxBlock,
|
||||
dspy: DSPyBlock,
|
||||
duckduckgo: DuckDuckGoBlock,
|
||||
dynamodb: DynamoDBBlock,
|
||||
elasticsearch: ElasticsearchBlock,
|
||||
@@ -204,6 +207,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
google_drive: GoogleDriveBlock,
|
||||
google_forms: GoogleFormsBlock,
|
||||
google_groups: GoogleGroupsBlock,
|
||||
google_maps: GoogleMapsBlock,
|
||||
google_search: GoogleSearchBlock,
|
||||
google_sheets: GoogleSheetsBlock,
|
||||
google_sheets_v2: GoogleSheetsV2Block,
|
||||
|
||||
File diff suppressed because one or more lines are too long
109
apps/sim/tools/dspy/chain_of_thought.ts
Normal file
109
apps/sim/tools/dspy/chain_of_thought.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import type { DSPyChainOfThoughtParams, DSPyChainOfThoughtResponse } from '@/tools/dspy/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const chainOfThoughtTool: ToolConfig<DSPyChainOfThoughtParams, DSPyChainOfThoughtResponse> =
|
||||
{
|
||||
id: 'dspy_chain_of_thought',
|
||||
name: 'DSPy Chain of Thought',
|
||||
description:
|
||||
'Run a Chain of Thought prediction using a self-hosted DSPy ChainOfThought program endpoint',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
baseUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Base URL of the DSPy server (e.g., https://your-dspy-server.com)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication (if required by your server)',
|
||||
},
|
||||
endpoint: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'API endpoint path (defaults to /predict)',
|
||||
},
|
||||
question: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The question to answer using chain of thought reasoning',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Additional context to provide for answering the question',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: (params) => {
|
||||
const baseUrl = params.baseUrl.replace(/\/$/, '')
|
||||
const endpoint = params.endpoint || '/predict'
|
||||
return `${baseUrl}${endpoint}`
|
||||
},
|
||||
headers: (params) => {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
if (params.apiKey) {
|
||||
headers.Authorization = `Bearer ${params.apiKey}`
|
||||
}
|
||||
return headers
|
||||
},
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
text: params.question,
|
||||
}
|
||||
|
||||
if (params.context) {
|
||||
body.context = params.context
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const status = data.status ?? 'success'
|
||||
const outputData = data.data ?? data
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
answer: outputData.answer ?? outputData.output ?? outputData.response ?? '',
|
||||
reasoning: outputData.reasoning ?? outputData.rationale ?? outputData.thought ?? '',
|
||||
status,
|
||||
rawOutput: outputData,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
answer: {
|
||||
type: 'string',
|
||||
description: 'The answer generated through chain of thought reasoning',
|
||||
},
|
||||
reasoning: {
|
||||
type: 'string',
|
||||
description: 'The step-by-step reasoning that led to the answer',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Response status from the DSPy server (success or error)',
|
||||
},
|
||||
rawOutput: {
|
||||
type: 'json',
|
||||
description: 'The complete raw output from the DSPy program (result.toDict())',
|
||||
},
|
||||
},
|
||||
}
|
||||
4
apps/sim/tools/dspy/index.ts
Normal file
4
apps/sim/tools/dspy/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { chainOfThoughtTool } from '@/tools/dspy/chain-of-thought'
|
||||
export { predictTool } from '@/tools/dspy/predict'
|
||||
export { reactTool } from '@/tools/dspy/react'
|
||||
export * from '@/tools/dspy/types'
|
||||
125
apps/sim/tools/dspy/predict.ts
Normal file
125
apps/sim/tools/dspy/predict.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import type { DSPyPredictParams, DSPyPredictResponse } from '@/tools/dspy/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const predictTool: ToolConfig<DSPyPredictParams, DSPyPredictResponse> = {
|
||||
id: 'dspy_predict',
|
||||
name: 'DSPy Predict',
|
||||
description: 'Run a prediction using a self-hosted DSPy program endpoint',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
baseUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Base URL of the DSPy server (e.g., https://your-dspy-server.com)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication (if required by your server)',
|
||||
},
|
||||
endpoint: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'API endpoint path (defaults to /predict)',
|
||||
},
|
||||
input: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The input text to send to the DSPy program',
|
||||
},
|
||||
inputField: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Name of the input field expected by the DSPy program (defaults to "text")',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Additional context to provide to the DSPy program',
|
||||
},
|
||||
additionalInputs: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Additional key-value pairs to include in the request body',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: (params) => {
|
||||
const baseUrl = params.baseUrl.replace(/\/$/, '')
|
||||
const endpoint = params.endpoint || '/predict'
|
||||
return `${baseUrl}${endpoint}`
|
||||
},
|
||||
headers: (params) => {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
if (params.apiKey) {
|
||||
headers.Authorization = `Bearer ${params.apiKey}`
|
||||
}
|
||||
return headers
|
||||
},
|
||||
body: (params) => {
|
||||
const inputField = params.inputField || 'text'
|
||||
const body: Record<string, unknown> = {
|
||||
[inputField]: params.input,
|
||||
}
|
||||
|
||||
if (params.context) {
|
||||
body.context = params.context
|
||||
}
|
||||
|
||||
if (params.additionalInputs) {
|
||||
Object.assign(body, params.additionalInputs)
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const status = data.status ?? 'success'
|
||||
const outputData = data.data ?? data
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
answer: outputData.answer ?? outputData.output ?? outputData.response ?? '',
|
||||
reasoning: outputData.reasoning ?? outputData.rationale ?? null,
|
||||
status,
|
||||
rawOutput: outputData,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
answer: {
|
||||
type: 'string',
|
||||
description: 'The main output/answer from the DSPy program',
|
||||
},
|
||||
reasoning: {
|
||||
type: 'string',
|
||||
description: 'The reasoning or rationale behind the answer (if available)',
|
||||
optional: true,
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Response status from the DSPy server (success or error)',
|
||||
},
|
||||
rawOutput: {
|
||||
type: 'json',
|
||||
description: 'The complete raw output from the DSPy program (result.toDict())',
|
||||
},
|
||||
},
|
||||
}
|
||||
156
apps/sim/tools/dspy/react.ts
Normal file
156
apps/sim/tools/dspy/react.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import type { DSPyReActParams, DSPyReActResponse } from '@/tools/dspy/types'
|
||||
import { parseTrajectory } from '@/tools/dspy/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const reactTool: ToolConfig<DSPyReActParams, DSPyReActResponse> = {
|
||||
id: 'dspy_react',
|
||||
name: 'DSPy ReAct',
|
||||
description:
|
||||
'Run a ReAct agent using a self-hosted DSPy ReAct program endpoint for multi-step reasoning and action',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
baseUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Base URL of the DSPy server (e.g., https://your-dspy-server.com)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'API key for authentication (if required by your server)',
|
||||
},
|
||||
endpoint: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'API endpoint path (defaults to /predict)',
|
||||
},
|
||||
task: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The task or question for the ReAct agent to work on',
|
||||
},
|
||||
context: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Additional context to provide for the task',
|
||||
},
|
||||
maxIterations: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Maximum number of reasoning iterations (defaults to server setting)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
method: 'POST',
|
||||
url: (params) => {
|
||||
const baseUrl = params.baseUrl.replace(/\/$/, '')
|
||||
const endpoint = params.endpoint || '/predict'
|
||||
return `${baseUrl}${endpoint}`
|
||||
},
|
||||
headers: (params) => {
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
if (params.apiKey) {
|
||||
headers.Authorization = `Bearer ${params.apiKey}`
|
||||
}
|
||||
return headers
|
||||
},
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
text: params.task,
|
||||
}
|
||||
|
||||
if (params.context) {
|
||||
body.context = params.context
|
||||
}
|
||||
|
||||
if (params.maxIterations !== undefined) {
|
||||
body.max_iters = params.maxIterations
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
const status = data.status ?? 'success'
|
||||
const outputData = data.data ?? data
|
||||
|
||||
const rawTrajectory = outputData.trajectory ?? {}
|
||||
const trajectory = Array.isArray(rawTrajectory)
|
||||
? rawTrajectory.map((step: Record<string, unknown>) => ({
|
||||
thought: (step.thought as string) ?? (step.reasoning as string) ?? '',
|
||||
toolName: (step.tool_name as string) ?? (step.selected_fn as string) ?? '',
|
||||
toolArgs:
|
||||
(step.tool_args as Record<string, unknown>) ??
|
||||
(step.args as Record<string, unknown>) ??
|
||||
{},
|
||||
observation: step.observation !== undefined ? String(step.observation) : null,
|
||||
}))
|
||||
: parseTrajectory(rawTrajectory)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
answer:
|
||||
outputData.answer ??
|
||||
outputData.process_result ??
|
||||
outputData.output ??
|
||||
outputData.response ??
|
||||
'',
|
||||
reasoning: outputData.reasoning ?? null,
|
||||
trajectory,
|
||||
status,
|
||||
rawOutput: outputData,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
answer: {
|
||||
type: 'string',
|
||||
description: 'The final answer or result from the ReAct agent',
|
||||
},
|
||||
reasoning: {
|
||||
type: 'string',
|
||||
description: 'The overall reasoning summary from the agent',
|
||||
optional: true,
|
||||
},
|
||||
trajectory: {
|
||||
type: 'array',
|
||||
description: 'The step-by-step trajectory of thoughts, actions, and observations',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
thought: { type: 'string', description: 'The reasoning thought at this step' },
|
||||
toolName: { type: 'string', description: 'The name of the tool/action called' },
|
||||
toolArgs: { type: 'json', description: 'Arguments passed to the tool' },
|
||||
observation: {
|
||||
type: 'string',
|
||||
description: 'The observation/result from the tool execution',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Response status from the DSPy server (success or error)',
|
||||
},
|
||||
rawOutput: {
|
||||
type: 'json',
|
||||
description: 'The complete raw output from the DSPy program (result.toDict())',
|
||||
},
|
||||
},
|
||||
}
|
||||
84
apps/sim/tools/dspy/types.ts
Normal file
84
apps/sim/tools/dspy/types.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Parameters for running a DSPy prediction
|
||||
*/
|
||||
export interface DSPyPredictParams {
|
||||
baseUrl: string
|
||||
apiKey?: string
|
||||
endpoint?: string
|
||||
input: string
|
||||
inputField?: string
|
||||
context?: string
|
||||
additionalInputs?: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from a DSPy prediction
|
||||
*/
|
||||
export interface DSPyPredictResponse extends ToolResponse {
|
||||
output: {
|
||||
answer: string
|
||||
reasoning: string | null
|
||||
status: string
|
||||
rawOutput: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for running a DSPy Chain of Thought prediction
|
||||
*/
|
||||
export interface DSPyChainOfThoughtParams {
|
||||
baseUrl: string
|
||||
apiKey?: string
|
||||
endpoint?: string
|
||||
question: string
|
||||
context?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from a DSPy Chain of Thought prediction
|
||||
*/
|
||||
export interface DSPyChainOfThoughtResponse extends ToolResponse {
|
||||
output: {
|
||||
answer: string
|
||||
reasoning: string
|
||||
status: string
|
||||
rawOutput: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parameters for running a DSPy ReAct agent
|
||||
*/
|
||||
export interface DSPyReActParams {
|
||||
baseUrl: string
|
||||
apiKey?: string
|
||||
endpoint?: string
|
||||
task: string
|
||||
context?: string
|
||||
maxIterations?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* ReAct trajectory step structure (matches DSPy output format)
|
||||
*/
|
||||
export interface DSPyTrajectoryStep {
|
||||
thought: string
|
||||
toolName: string
|
||||
toolArgs: Record<string, unknown>
|
||||
observation: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Response from a DSPy ReAct agent
|
||||
*/
|
||||
export interface DSPyReActResponse extends ToolResponse {
|
||||
output: {
|
||||
answer: string
|
||||
reasoning: string | null
|
||||
trajectory: DSPyTrajectoryStep[]
|
||||
status: string
|
||||
rawOutput: Record<string, unknown>
|
||||
}
|
||||
}
|
||||
28
apps/sim/tools/dspy/utils.ts
Normal file
28
apps/sim/tools/dspy/utils.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { DSPyTrajectoryStep } from '@/tools/dspy/types'
|
||||
|
||||
/**
|
||||
* Parse DSPy ReAct trajectory format into structured steps
|
||||
* DSPy trajectory format: { thought_0, tool_name_0, tool_args_0, observation_0, thought_1, ... }
|
||||
*/
|
||||
export function parseTrajectory(trajectory: Record<string, unknown>): DSPyTrajectoryStep[] {
|
||||
const steps: DSPyTrajectoryStep[] = []
|
||||
let idx = 0
|
||||
|
||||
while (
|
||||
trajectory[`thought_${idx}`] !== undefined ||
|
||||
trajectory[`tool_name_${idx}`] !== undefined
|
||||
) {
|
||||
steps.push({
|
||||
thought: (trajectory[`thought_${idx}`] as string) ?? '',
|
||||
toolName: (trajectory[`tool_name_${idx}`] as string) ?? '',
|
||||
toolArgs: (trajectory[`tool_args_${idx}`] as Record<string, unknown>) ?? {},
|
||||
observation:
|
||||
trajectory[`observation_${idx}`] !== undefined
|
||||
? String(trajectory[`observation_${idx}`])
|
||||
: null,
|
||||
})
|
||||
idx++
|
||||
}
|
||||
|
||||
return steps
|
||||
}
|
||||
223
apps/sim/tools/google_maps/air_quality.ts
Normal file
223
apps/sim/tools/google_maps/air_quality.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
import type {
|
||||
GoogleMapsAirQualityParams,
|
||||
GoogleMapsAirQualityResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsAirQualityTool: ToolConfig<
|
||||
GoogleMapsAirQualityParams,
|
||||
GoogleMapsAirQualityResponse
|
||||
> = {
|
||||
id: 'google_maps_air_quality',
|
||||
name: 'Google Maps Air Quality',
|
||||
description: 'Get current air quality data for a location',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Google Maps API key with Air Quality API enabled',
|
||||
},
|
||||
lat: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Latitude coordinate',
|
||||
},
|
||||
lng: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Longitude coordinate',
|
||||
},
|
||||
languageCode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Language code for the response (e.g., "en", "es")',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
return `https://airquality.googleapis.com/v1/currentConditions:lookup?key=${params.apiKey.trim()}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: {
|
||||
location: { latitude: number; longitude: number }
|
||||
extraComputations: string[]
|
||||
languageCode?: string
|
||||
} = {
|
||||
location: {
|
||||
latitude: params.lat,
|
||||
longitude: params.lng,
|
||||
},
|
||||
extraComputations: [
|
||||
'HEALTH_RECOMMENDATIONS',
|
||||
'DOMINANT_POLLUTANT_CONCENTRATION',
|
||||
'POLLUTANT_CONCENTRATION',
|
||||
'LOCAL_AQI',
|
||||
'POLLUTANT_ADDITIONAL_INFO',
|
||||
],
|
||||
}
|
||||
|
||||
if (params.languageCode) {
|
||||
body.languageCode = params.languageCode.trim()
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(`Air Quality failed: ${data.error.message || 'Unknown error'}`)
|
||||
}
|
||||
|
||||
const indexes = (data.indexes || []).map(
|
||||
(index: {
|
||||
code: string
|
||||
displayName: string
|
||||
aqi: number
|
||||
aqiDisplay: string
|
||||
color: { red?: number; green?: number; blue?: number }
|
||||
category: string
|
||||
dominantPollutant: string
|
||||
}) => ({
|
||||
code: index.code,
|
||||
displayName: index.displayName,
|
||||
aqi: index.aqi,
|
||||
aqiDisplay: index.aqiDisplay,
|
||||
color: {
|
||||
red: index.color?.red || 0,
|
||||
green: index.color?.green || 0,
|
||||
blue: index.color?.blue || 0,
|
||||
},
|
||||
category: index.category,
|
||||
dominantPollutant: index.dominantPollutant,
|
||||
})
|
||||
)
|
||||
|
||||
const pollutants = (data.pollutants || []).map(
|
||||
(pollutant: {
|
||||
code: string
|
||||
displayName: string
|
||||
fullName: string
|
||||
concentration: { value: number; units: string }
|
||||
additionalInfo?: { sources: string; effects: string }
|
||||
}) => ({
|
||||
code: pollutant.code,
|
||||
displayName: pollutant.displayName,
|
||||
fullName: pollutant.fullName,
|
||||
concentration: {
|
||||
value: pollutant.concentration?.value || 0,
|
||||
units: pollutant.concentration?.units || '',
|
||||
},
|
||||
additionalInfo: pollutant.additionalInfo
|
||||
? {
|
||||
sources: pollutant.additionalInfo.sources,
|
||||
effects: pollutant.additionalInfo.effects,
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
)
|
||||
|
||||
const healthRecs = data.healthRecommendations
|
||||
const healthRecommendations = healthRecs
|
||||
? {
|
||||
generalPopulation: healthRecs.generalPopulation || '',
|
||||
elderly: healthRecs.elderly || '',
|
||||
lungDiseasePopulation: healthRecs.lungDiseasePopulation || '',
|
||||
heartDiseasePopulation: healthRecs.heartDiseasePopulation || '',
|
||||
athletes: healthRecs.athletes || '',
|
||||
pregnantWomen: healthRecs.pregnantWomen || '',
|
||||
children: healthRecs.children || '',
|
||||
}
|
||||
: null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
dateTime: data.dateTime || '',
|
||||
regionCode: data.regionCode || '',
|
||||
indexes,
|
||||
pollutants,
|
||||
healthRecommendations,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
dateTime: {
|
||||
type: 'string',
|
||||
description: 'Timestamp of the air quality data',
|
||||
},
|
||||
regionCode: {
|
||||
type: 'string',
|
||||
description: 'Region code for the location',
|
||||
},
|
||||
indexes: {
|
||||
type: 'array',
|
||||
description: 'Array of air quality indexes',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
code: { type: 'string', description: 'Index code (e.g., "uaqi", "usa_epa")' },
|
||||
displayName: { type: 'string', description: 'Display name of the index' },
|
||||
aqi: { type: 'number', description: 'Air quality index value' },
|
||||
aqiDisplay: { type: 'string', description: 'Formatted AQI display string' },
|
||||
color: {
|
||||
type: 'object',
|
||||
description: 'RGB color for the AQI level',
|
||||
properties: {
|
||||
red: { type: 'number' },
|
||||
green: { type: 'number' },
|
||||
blue: { type: 'number' },
|
||||
},
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
description: 'Category description (e.g., "Good", "Moderate")',
|
||||
},
|
||||
dominantPollutant: { type: 'string', description: 'The dominant pollutant' },
|
||||
},
|
||||
},
|
||||
},
|
||||
pollutants: {
|
||||
type: 'array',
|
||||
description: 'Array of pollutant concentrations',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
code: { type: 'string', description: 'Pollutant code (e.g., "pm25", "o3")' },
|
||||
displayName: { type: 'string', description: 'Display name' },
|
||||
fullName: { type: 'string', description: 'Full pollutant name' },
|
||||
concentration: {
|
||||
type: 'object',
|
||||
description: 'Concentration info',
|
||||
properties: {
|
||||
value: { type: 'number', description: 'Concentration value' },
|
||||
units: { type: 'string', description: 'Units (e.g., "PARTS_PER_BILLION")' },
|
||||
},
|
||||
},
|
||||
additionalInfo: {
|
||||
type: 'object',
|
||||
description: 'Additional info about sources and effects',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
healthRecommendations: {
|
||||
type: 'object',
|
||||
description: 'Health recommendations for different populations',
|
||||
},
|
||||
},
|
||||
}
|
||||
259
apps/sim/tools/google_maps/directions.ts
Normal file
259
apps/sim/tools/google_maps/directions.ts
Normal file
@@ -0,0 +1,259 @@
|
||||
import type {
|
||||
GoogleMapsDirectionsParams,
|
||||
GoogleMapsDirectionsResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsDirectionsTool: ToolConfig<
|
||||
GoogleMapsDirectionsParams,
|
||||
GoogleMapsDirectionsResponse
|
||||
> = {
|
||||
id: 'google_maps_directions',
|
||||
name: 'Google Maps Directions',
|
||||
description: 'Get directions and route information between two locations',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Google Maps API key',
|
||||
},
|
||||
origin: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Starting location (address or lat,lng)',
|
||||
},
|
||||
destination: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Destination location (address or lat,lng)',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Travel mode: driving, walking, bicycling, or transit',
|
||||
},
|
||||
avoid: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Features to avoid: tolls, highways, or ferries',
|
||||
},
|
||||
waypoints: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of intermediate waypoints',
|
||||
},
|
||||
units: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unit system: metric or imperial',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Language code for results (e.g., en, es, fr)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://maps.googleapis.com/maps/api/directions/json')
|
||||
url.searchParams.set('origin', params.origin.trim())
|
||||
url.searchParams.set('destination', params.destination.trim())
|
||||
url.searchParams.set('key', params.apiKey.trim())
|
||||
|
||||
if (params.mode) {
|
||||
url.searchParams.set('mode', params.mode)
|
||||
}
|
||||
if (params.avoid) {
|
||||
url.searchParams.set('avoid', params.avoid)
|
||||
}
|
||||
if (params.waypoints && params.waypoints.length > 0) {
|
||||
url.searchParams.set('waypoints', params.waypoints.join('|'))
|
||||
}
|
||||
if (params.units) {
|
||||
url.searchParams.set('units', params.units)
|
||||
}
|
||||
if (params.language) {
|
||||
url.searchParams.set('language', params.language.trim())
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.status !== 'OK') {
|
||||
throw new Error(
|
||||
`Directions request failed: ${data.status} - ${data.error_message || 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
|
||||
const routes = data.routes.map(
|
||||
(route: {
|
||||
summary: string
|
||||
legs: Array<{
|
||||
start_address: string
|
||||
end_address: string
|
||||
start_location: { lat: number; lng: number }
|
||||
end_location: { lat: number; lng: number }
|
||||
distance: { text: string; value: number }
|
||||
duration: { text: string; value: number }
|
||||
steps: Array<{
|
||||
html_instructions: string
|
||||
distance: { text: string; value: number }
|
||||
duration: { text: string; value: number }
|
||||
start_location: { lat: number; lng: number }
|
||||
end_location: { lat: number; lng: number }
|
||||
travel_mode: string
|
||||
maneuver?: string
|
||||
}>
|
||||
}>
|
||||
overview_polyline: { points: string }
|
||||
warnings: string[]
|
||||
waypoint_order: number[]
|
||||
}) => ({
|
||||
summary: route.summary,
|
||||
legs: route.legs.map((leg) => ({
|
||||
startAddress: leg.start_address,
|
||||
endAddress: leg.end_address,
|
||||
startLocation: {
|
||||
lat: leg.start_location.lat,
|
||||
lng: leg.start_location.lng,
|
||||
},
|
||||
endLocation: {
|
||||
lat: leg.end_location.lat,
|
||||
lng: leg.end_location.lng,
|
||||
},
|
||||
distanceText: leg.distance.text,
|
||||
distanceMeters: leg.distance.value,
|
||||
durationText: leg.duration.text,
|
||||
durationSeconds: leg.duration.value,
|
||||
steps: leg.steps.map((step) => ({
|
||||
instruction: step.html_instructions.replace(/<[^>]*>/g, ''),
|
||||
distanceText: step.distance.text,
|
||||
distanceMeters: step.distance.value,
|
||||
durationText: step.duration.text,
|
||||
durationSeconds: step.duration.value,
|
||||
startLocation: {
|
||||
lat: step.start_location.lat,
|
||||
lng: step.start_location.lng,
|
||||
},
|
||||
endLocation: {
|
||||
lat: step.end_location.lat,
|
||||
lng: step.end_location.lng,
|
||||
},
|
||||
travelMode: step.travel_mode,
|
||||
maneuver: step.maneuver ?? null,
|
||||
})),
|
||||
})),
|
||||
overviewPolyline: route.overview_polyline.points,
|
||||
warnings: route.warnings ?? [],
|
||||
waypointOrder: route.waypoint_order ?? [],
|
||||
})
|
||||
)
|
||||
|
||||
const primaryRoute = routes[0]
|
||||
const primaryLeg = primaryRoute?.legs[0]
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
routes,
|
||||
distanceText: primaryLeg?.distanceText ?? '',
|
||||
distanceMeters: primaryLeg?.distanceMeters ?? 0,
|
||||
durationText: primaryLeg?.durationText ?? '',
|
||||
durationSeconds: primaryLeg?.durationSeconds ?? 0,
|
||||
startAddress: primaryLeg?.startAddress ?? '',
|
||||
endAddress: primaryLeg?.endAddress ?? '',
|
||||
steps: primaryLeg?.steps ?? [],
|
||||
polyline: primaryRoute?.overviewPolyline ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
routes: {
|
||||
type: 'array',
|
||||
description: 'All available routes',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
summary: { type: 'string', description: 'Route summary (main road names)' },
|
||||
legs: { type: 'array', description: 'Route legs (segments between waypoints)' },
|
||||
overviewPolyline: {
|
||||
type: 'string',
|
||||
description: 'Encoded polyline for the entire route',
|
||||
},
|
||||
warnings: { type: 'array', description: 'Route warnings' },
|
||||
waypointOrder: { type: 'array', description: 'Optimized waypoint order (if requested)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
distanceText: {
|
||||
type: 'string',
|
||||
description: 'Total distance as human-readable text (e.g., "5.2 km")',
|
||||
},
|
||||
distanceMeters: {
|
||||
type: 'number',
|
||||
description: 'Total distance in meters',
|
||||
},
|
||||
durationText: {
|
||||
type: 'string',
|
||||
description: 'Total duration as human-readable text (e.g., "15 mins")',
|
||||
},
|
||||
durationSeconds: {
|
||||
type: 'number',
|
||||
description: 'Total duration in seconds',
|
||||
},
|
||||
startAddress: {
|
||||
type: 'string',
|
||||
description: 'Resolved starting address',
|
||||
},
|
||||
endAddress: {
|
||||
type: 'string',
|
||||
description: 'Resolved ending address',
|
||||
},
|
||||
steps: {
|
||||
type: 'array',
|
||||
description: 'Turn-by-turn navigation instructions',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
instruction: { type: 'string', description: 'Navigation instruction (HTML stripped)' },
|
||||
distanceText: { type: 'string', description: 'Step distance as text' },
|
||||
distanceMeters: { type: 'number', description: 'Step distance in meters' },
|
||||
durationText: { type: 'string', description: 'Step duration as text' },
|
||||
durationSeconds: { type: 'number', description: 'Step duration in seconds' },
|
||||
startLocation: { type: 'object', description: 'Step start coordinates' },
|
||||
endLocation: { type: 'object', description: 'Step end coordinates' },
|
||||
travelMode: { type: 'string', description: 'Travel mode for this step' },
|
||||
maneuver: {
|
||||
type: 'string',
|
||||
description: 'Maneuver type (turn-left, etc.)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
polyline: {
|
||||
type: 'string',
|
||||
description: 'Encoded polyline for the primary route',
|
||||
},
|
||||
},
|
||||
}
|
||||
181
apps/sim/tools/google_maps/distance_matrix.ts
Normal file
181
apps/sim/tools/google_maps/distance_matrix.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import type {
|
||||
GoogleMapsDistanceMatrixParams,
|
||||
GoogleMapsDistanceMatrixResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsDistanceMatrixTool: ToolConfig<
|
||||
GoogleMapsDistanceMatrixParams,
|
||||
GoogleMapsDistanceMatrixResponse
|
||||
> = {
|
||||
id: 'google_maps_distance_matrix',
|
||||
name: 'Google Maps Distance Matrix',
|
||||
description: 'Calculate travel distance and time between multiple origins and destinations',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Google Maps API key',
|
||||
},
|
||||
origin: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Origin location (address or lat,lng)',
|
||||
},
|
||||
destinations: {
|
||||
type: 'json',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of destination locations',
|
||||
},
|
||||
mode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Travel mode: driving, walking, bicycling, or transit',
|
||||
},
|
||||
avoid: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Features to avoid: tolls, highways, or ferries',
|
||||
},
|
||||
units: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unit system: metric or imperial',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Language code for results (e.g., en, es, fr)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://maps.googleapis.com/maps/api/distancematrix/json')
|
||||
url.searchParams.set('origins', params.origin.trim())
|
||||
url.searchParams.set('destinations', params.destinations.join('|'))
|
||||
url.searchParams.set('key', params.apiKey.trim())
|
||||
|
||||
if (params.mode) {
|
||||
url.searchParams.set('mode', params.mode)
|
||||
}
|
||||
if (params.avoid) {
|
||||
url.searchParams.set('avoid', params.avoid)
|
||||
}
|
||||
if (params.units) {
|
||||
url.searchParams.set('units', params.units)
|
||||
}
|
||||
if (params.language) {
|
||||
url.searchParams.set('language', params.language.trim())
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.status !== 'OK') {
|
||||
throw new Error(
|
||||
`Distance matrix request failed: ${data.status} - ${data.error_message || 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
|
||||
const rows = data.rows.map(
|
||||
(row: {
|
||||
elements: Array<{
|
||||
distance?: { text: string; value: number }
|
||||
duration?: { text: string; value: number }
|
||||
duration_in_traffic?: { text: string; value: number }
|
||||
status: string
|
||||
}>
|
||||
}) => ({
|
||||
elements: row.elements.map((element) => ({
|
||||
distanceText: element.distance?.text ?? 'N/A',
|
||||
distanceMeters: element.distance?.value ?? 0,
|
||||
durationText: element.duration?.text ?? 'N/A',
|
||||
durationSeconds: element.duration?.value ?? 0,
|
||||
durationInTrafficText: element.duration_in_traffic?.text ?? null,
|
||||
durationInTrafficSeconds: element.duration_in_traffic?.value ?? null,
|
||||
status: element.status,
|
||||
})),
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
originAddresses: data.origin_addresses ?? [],
|
||||
destinationAddresses: data.destination_addresses ?? [],
|
||||
rows,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
originAddresses: {
|
||||
type: 'array',
|
||||
description: 'Resolved origin addresses',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
destinationAddresses: {
|
||||
type: 'array',
|
||||
description: 'Resolved destination addresses',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
rows: {
|
||||
type: 'array',
|
||||
description: 'Distance matrix rows (one per origin)',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
elements: {
|
||||
type: 'array',
|
||||
description: 'Elements (one per destination)',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
distanceText: { type: 'string', description: 'Distance as text (e.g., "5.2 km")' },
|
||||
distanceMeters: { type: 'number', description: 'Distance in meters' },
|
||||
durationText: { type: 'string', description: 'Duration as text (e.g., "15 mins")' },
|
||||
durationSeconds: { type: 'number', description: 'Duration in seconds' },
|
||||
durationInTrafficText: {
|
||||
type: 'string',
|
||||
description: 'Duration in traffic as text',
|
||||
optional: true,
|
||||
},
|
||||
durationInTrafficSeconds: {
|
||||
type: 'number',
|
||||
description: 'Duration in traffic in seconds',
|
||||
optional: true,
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Element status (OK, NOT_FOUND, ZERO_RESULTS)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
91
apps/sim/tools/google_maps/elevation.ts
Normal file
91
apps/sim/tools/google_maps/elevation.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import type {
|
||||
GoogleMapsElevationParams,
|
||||
GoogleMapsElevationResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsElevationTool: ToolConfig<
|
||||
GoogleMapsElevationParams,
|
||||
GoogleMapsElevationResponse
|
||||
> = {
|
||||
id: 'google_maps_elevation',
|
||||
name: 'Google Maps Elevation',
|
||||
description: 'Get elevation data for 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 coordinate',
|
||||
},
|
||||
lng: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Longitude coordinate',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://maps.googleapis.com/maps/api/elevation/json')
|
||||
url.searchParams.set('locations', `${params.lat},${params.lng}`)
|
||||
url.searchParams.set('key', params.apiKey.trim())
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.status !== 'OK') {
|
||||
throw new Error(
|
||||
`Elevation request failed: ${data.status} - ${data.error_message || 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
|
||||
const result = data.results[0]
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
elevation: result.elevation,
|
||||
lat: result.location.lat,
|
||||
lng: result.location.lng,
|
||||
resolution: result.resolution,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
elevation: {
|
||||
type: 'number',
|
||||
description: 'Elevation in meters above sea level (negative for below)',
|
||||
},
|
||||
lat: {
|
||||
type: 'number',
|
||||
description: 'Latitude of the elevation sample',
|
||||
},
|
||||
lng: {
|
||||
type: 'number',
|
||||
description: 'Longitude of the elevation sample',
|
||||
},
|
||||
resolution: {
|
||||
type: 'number',
|
||||
description:
|
||||
'Maximum distance between data points (meters) from which elevation was interpolated',
|
||||
},
|
||||
},
|
||||
}
|
||||
130
apps/sim/tools/google_maps/geocode.ts
Normal file
130
apps/sim/tools/google_maps/geocode.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import type { GoogleMapsGeocodeParams, GoogleMapsGeocodeResponse } from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsGeocodeTool: ToolConfig<GoogleMapsGeocodeParams, GoogleMapsGeocodeResponse> =
|
||||
{
|
||||
id: 'google_maps_geocode',
|
||||
name: 'Google Maps Geocode',
|
||||
description: 'Convert an address into geographic coordinates (latitude and longitude)',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Google Maps API key',
|
||||
},
|
||||
address: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The address to geocode',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Language code for results (e.g., en, es, fr)',
|
||||
},
|
||||
region: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Region bias as a ccTLD code (e.g., us, uk)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://maps.googleapis.com/maps/api/geocode/json')
|
||||
url.searchParams.set('address', params.address.trim())
|
||||
url.searchParams.set('key', params.apiKey.trim())
|
||||
if (params.language) {
|
||||
url.searchParams.set('language', params.language.trim())
|
||||
}
|
||||
if (params.region) {
|
||||
url.searchParams.set('region', params.region.trim())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.status !== 'OK') {
|
||||
throw new Error(
|
||||
`Geocoding failed: ${data.status} - ${data.error_message || 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
|
||||
const result = data.results[0]
|
||||
const location = result.geometry.location
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
formattedAddress: result.formatted_address,
|
||||
lat: location.lat,
|
||||
lng: location.lng,
|
||||
location: {
|
||||
lat: location.lat,
|
||||
lng: location.lng,
|
||||
},
|
||||
placeId: result.place_id,
|
||||
addressComponents: (result.address_components || []).map(
|
||||
(comp: { long_name: string; short_name: string; types: string[] }) => ({
|
||||
longName: comp.long_name,
|
||||
shortName: comp.short_name,
|
||||
types: comp.types,
|
||||
})
|
||||
),
|
||||
locationType: result.geometry.location_type,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
formattedAddress: {
|
||||
type: 'string',
|
||||
description: 'The formatted address string',
|
||||
},
|
||||
lat: {
|
||||
type: 'number',
|
||||
description: 'Latitude coordinate',
|
||||
},
|
||||
lng: {
|
||||
type: 'number',
|
||||
description: 'Longitude coordinate',
|
||||
},
|
||||
location: {
|
||||
type: 'json',
|
||||
description: 'Location object with lat and lng',
|
||||
},
|
||||
placeId: {
|
||||
type: 'string',
|
||||
description: 'Google Place ID for this location',
|
||||
},
|
||||
addressComponents: {
|
||||
type: 'array',
|
||||
description: 'Detailed address components',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
longName: { type: 'string', description: 'Full name of the component' },
|
||||
shortName: { type: 'string', description: 'Abbreviated name' },
|
||||
types: { type: 'array', description: 'Component types' },
|
||||
},
|
||||
},
|
||||
},
|
||||
locationType: {
|
||||
type: 'string',
|
||||
description: 'Location accuracy type (ROOFTOP, RANGE_INTERPOLATED, etc.)',
|
||||
},
|
||||
},
|
||||
}
|
||||
165
apps/sim/tools/google_maps/geolocate.ts
Normal file
165
apps/sim/tools/google_maps/geolocate.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import type {
|
||||
GoogleMapsGeolocateParams,
|
||||
GoogleMapsGeolocateResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsGeolocateTool: ToolConfig<
|
||||
GoogleMapsGeolocateParams,
|
||||
GoogleMapsGeolocateResponse
|
||||
> = {
|
||||
id: 'google_maps_geolocate',
|
||||
name: 'Google Maps Geolocate',
|
||||
description: 'Geolocate a device using WiFi access points, cell towers, or IP address',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Google Maps API key with Geolocation API enabled',
|
||||
},
|
||||
homeMobileCountryCode: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Home mobile country code (MCC)',
|
||||
},
|
||||
homeMobileNetworkCode: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Home mobile network code (MNC)',
|
||||
},
|
||||
radioType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Radio type: lte, gsm, cdma, wcdma, or nr',
|
||||
},
|
||||
carrier: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Carrier name',
|
||||
},
|
||||
considerIp: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to use IP address for geolocation (default: true)',
|
||||
},
|
||||
cellTowers: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Array of cell tower objects with cellId, locationAreaCode, mobileCountryCode, mobileNetworkCode',
|
||||
},
|
||||
wifiAccessPoints: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Array of WiFi access point objects with macAddress (required), signalStrength, etc.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
return `https://www.googleapis.com/geolocation/v1/geolocate?key=${params.apiKey.trim()}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: {
|
||||
homeMobileCountryCode?: number
|
||||
homeMobileNetworkCode?: number
|
||||
radioType?: string
|
||||
carrier?: string
|
||||
considerIp?: boolean
|
||||
cellTowers?: Array<{
|
||||
cellId: number
|
||||
locationAreaCode: number
|
||||
mobileCountryCode: number
|
||||
mobileNetworkCode: number
|
||||
age?: number
|
||||
signalStrength?: number
|
||||
timingAdvance?: number
|
||||
}>
|
||||
wifiAccessPoints?: Array<{
|
||||
macAddress: string
|
||||
signalStrength?: number
|
||||
age?: number
|
||||
channel?: number
|
||||
signalToNoiseRatio?: number
|
||||
}>
|
||||
} = {}
|
||||
|
||||
if (params.homeMobileCountryCode !== undefined) {
|
||||
body.homeMobileCountryCode = params.homeMobileCountryCode
|
||||
}
|
||||
|
||||
if (params.homeMobileNetworkCode !== undefined) {
|
||||
body.homeMobileNetworkCode = params.homeMobileNetworkCode
|
||||
}
|
||||
|
||||
if (params.radioType) {
|
||||
body.radioType = params.radioType
|
||||
}
|
||||
|
||||
if (params.carrier) {
|
||||
body.carrier = params.carrier
|
||||
}
|
||||
|
||||
if (params.considerIp !== undefined) {
|
||||
body.considerIp = params.considerIp
|
||||
}
|
||||
|
||||
if (params.cellTowers && params.cellTowers.length > 0) {
|
||||
body.cellTowers = params.cellTowers
|
||||
}
|
||||
|
||||
if (params.wifiAccessPoints && params.wifiAccessPoints.length > 0) {
|
||||
body.wifiAccessPoints = params.wifiAccessPoints
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(`Geolocation failed: ${data.error.message || 'Unknown error'}`)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
lat: data.location?.lat || 0,
|
||||
lng: data.location?.lng || 0,
|
||||
accuracy: data.accuracy || 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
lat: {
|
||||
type: 'number',
|
||||
description: 'Latitude coordinate',
|
||||
},
|
||||
lng: {
|
||||
type: 'number',
|
||||
description: 'Longitude coordinate',
|
||||
},
|
||||
accuracy: {
|
||||
type: 'number',
|
||||
description: 'Accuracy radius in meters',
|
||||
},
|
||||
},
|
||||
}
|
||||
32
apps/sim/tools/google_maps/index.ts
Normal file
32
apps/sim/tools/google_maps/index.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { googleMapsAirQualityTool } from '@/tools/google_maps/air_quality'
|
||||
import { googleMapsDirectionsTool } from '@/tools/google_maps/directions'
|
||||
import { googleMapsDistanceMatrixTool } from '@/tools/google_maps/distance_matrix'
|
||||
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 { googleMapsPlacesSearchTool } from '@/tools/google_maps/places_search'
|
||||
import { googleMapsReverseGeocodeTool } from '@/tools/google_maps/reverse_geocode'
|
||||
import { googleMapsSnapToRoadsTool } from '@/tools/google_maps/snap_to_roads'
|
||||
import { googleMapsSpeedLimitsTool } from '@/tools/google_maps/speed_limits'
|
||||
import { googleMapsTimezoneTool } from '@/tools/google_maps/timezone'
|
||||
import { googleMapsValidateAddressTool } from '@/tools/google_maps/validate_address'
|
||||
|
||||
export {
|
||||
googleMapsAirQualityTool,
|
||||
googleMapsDirectionsTool,
|
||||
googleMapsDistanceMatrixTool,
|
||||
googleMapsElevationTool,
|
||||
googleMapsGeocodeTool,
|
||||
googleMapsGeolocateTool,
|
||||
googleMapsPlaceDetailsTool,
|
||||
googleMapsPlacesSearchTool,
|
||||
googleMapsReverseGeocodeTool,
|
||||
googleMapsSnapToRoadsTool,
|
||||
googleMapsSpeedLimitsTool,
|
||||
googleMapsTimezoneTool,
|
||||
googleMapsValidateAddressTool,
|
||||
}
|
||||
|
||||
// Export types
|
||||
export * from './types'
|
||||
274
apps/sim/tools/google_maps/place_details.ts
Normal file
274
apps/sim/tools/google_maps/place_details.ts
Normal file
@@ -0,0 +1,274 @@
|
||||
import type {
|
||||
GoogleMapsPlaceDetailsParams,
|
||||
GoogleMapsPlaceDetailsResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsPlaceDetailsTool: ToolConfig<
|
||||
GoogleMapsPlaceDetailsParams,
|
||||
GoogleMapsPlaceDetailsResponse
|
||||
> = {
|
||||
id: 'google_maps_place_details',
|
||||
name: 'Google Maps Place Details',
|
||||
description: 'Get detailed information about a specific place',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Google Maps API key',
|
||||
},
|
||||
placeId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Google Place ID',
|
||||
},
|
||||
fields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated list of fields to return',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Language code for results (e.g., en, es, fr)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://maps.googleapis.com/maps/api/place/details/json')
|
||||
url.searchParams.set('place_id', params.placeId.trim())
|
||||
url.searchParams.set('key', params.apiKey.trim())
|
||||
|
||||
// Default fields if not specified - comprehensive list
|
||||
const fields =
|
||||
params.fields ||
|
||||
'place_id,name,formatted_address,geometry,types,rating,user_ratings_total,price_level,website,formatted_phone_number,international_phone_number,opening_hours,reviews,photos,url,utc_offset,vicinity,business_status'
|
||||
url.searchParams.set('fields', fields)
|
||||
|
||||
if (params.language) {
|
||||
url.searchParams.set('language', params.language.trim())
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.status !== 'OK') {
|
||||
throw new Error(
|
||||
`Place details request failed: ${data.status} - ${data.error_message || 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
|
||||
const place = data.result
|
||||
|
||||
const reviews = (place.reviews || []).map(
|
||||
(review: {
|
||||
author_name: string
|
||||
author_url?: string
|
||||
profile_photo_url?: string
|
||||
rating: number
|
||||
text: string
|
||||
time: number
|
||||
relative_time_description: string
|
||||
}) => ({
|
||||
authorName: review.author_name,
|
||||
authorUrl: review.author_url ?? null,
|
||||
profilePhotoUrl: review.profile_photo_url ?? null,
|
||||
rating: review.rating,
|
||||
text: review.text,
|
||||
time: review.time,
|
||||
relativeTimeDescription: review.relative_time_description,
|
||||
})
|
||||
)
|
||||
|
||||
const photos = (place.photos || []).map(
|
||||
(photo: {
|
||||
photo_reference: string
|
||||
height: number
|
||||
width: number
|
||||
html_attributions: string[]
|
||||
}) => ({
|
||||
photoReference: photo.photo_reference,
|
||||
height: photo.height,
|
||||
width: photo.width,
|
||||
htmlAttributions: photo.html_attributions ?? [],
|
||||
})
|
||||
)
|
||||
|
||||
// Destructure opening hours
|
||||
const openNow = place.opening_hours?.open_now ?? null
|
||||
const weekdayText = place.opening_hours?.weekday_text ?? []
|
||||
|
||||
// Extract location
|
||||
const lat = place.geometry?.location?.lat ?? null
|
||||
const lng = place.geometry?.location?.lng ?? null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
placeId: place.place_id,
|
||||
name: place.name ?? null,
|
||||
formattedAddress: place.formatted_address ?? null,
|
||||
lat,
|
||||
lng,
|
||||
types: place.types ?? [],
|
||||
rating: place.rating ?? null,
|
||||
userRatingsTotal: place.user_ratings_total ?? null,
|
||||
priceLevel: place.price_level ?? null,
|
||||
website: place.website ?? null,
|
||||
phoneNumber: place.formatted_phone_number ?? null,
|
||||
internationalPhoneNumber: place.international_phone_number ?? null,
|
||||
openNow,
|
||||
weekdayText,
|
||||
reviews,
|
||||
photos,
|
||||
url: place.url ?? null,
|
||||
utcOffset: place.utc_offset ?? null,
|
||||
vicinity: place.vicinity ?? null,
|
||||
businessStatus: place.business_status ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
placeId: {
|
||||
type: 'string',
|
||||
description: 'Google Place ID',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
description: 'Place name',
|
||||
optional: true,
|
||||
},
|
||||
formattedAddress: {
|
||||
type: 'string',
|
||||
description: 'Formatted street address',
|
||||
optional: true,
|
||||
},
|
||||
lat: {
|
||||
type: 'number',
|
||||
description: 'Latitude coordinate',
|
||||
optional: true,
|
||||
},
|
||||
lng: {
|
||||
type: 'number',
|
||||
description: 'Longitude coordinate',
|
||||
optional: true,
|
||||
},
|
||||
types: {
|
||||
type: 'array',
|
||||
description: 'Place types (e.g., restaurant, cafe)',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
rating: {
|
||||
type: 'number',
|
||||
description: 'Average rating (1.0 to 5.0)',
|
||||
optional: true,
|
||||
},
|
||||
userRatingsTotal: {
|
||||
type: 'number',
|
||||
description: 'Total number of user ratings',
|
||||
optional: true,
|
||||
},
|
||||
priceLevel: {
|
||||
type: 'number',
|
||||
description: 'Price level (0=Free, 1=Inexpensive, 2=Moderate, 3=Expensive, 4=Very Expensive)',
|
||||
optional: true,
|
||||
},
|
||||
website: {
|
||||
type: 'string',
|
||||
description: 'Place website URL',
|
||||
optional: true,
|
||||
},
|
||||
phoneNumber: {
|
||||
type: 'string',
|
||||
description: 'Local formatted phone number',
|
||||
optional: true,
|
||||
},
|
||||
internationalPhoneNumber: {
|
||||
type: 'string',
|
||||
description: 'International formatted phone number',
|
||||
optional: true,
|
||||
},
|
||||
openNow: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the place is currently open',
|
||||
optional: true,
|
||||
},
|
||||
weekdayText: {
|
||||
type: 'array',
|
||||
description: 'Opening hours formatted by day of week',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
reviews: {
|
||||
type: 'array',
|
||||
description: 'User reviews (up to 5 most relevant)',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
authorName: { type: 'string', description: 'Reviewer name' },
|
||||
authorUrl: { type: 'string', description: 'Reviewer profile URL', optional: true },
|
||||
profilePhotoUrl: { type: 'string', description: 'Reviewer photo URL', optional: true },
|
||||
rating: { type: 'number', description: 'Rating given (1-5)' },
|
||||
text: { type: 'string', description: 'Review text' },
|
||||
time: { type: 'number', description: 'Review timestamp (Unix epoch)' },
|
||||
relativeTimeDescription: {
|
||||
type: 'string',
|
||||
description: 'Relative time (e.g., "a month ago")',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
photos: {
|
||||
type: 'array',
|
||||
description: 'Place photos',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
photoReference: { type: 'string', description: 'Photo reference for Place Photos API' },
|
||||
height: { type: 'number', description: 'Photo height in pixels' },
|
||||
width: { type: 'number', description: 'Photo width in pixels' },
|
||||
htmlAttributions: { type: 'array', description: 'Required attributions' },
|
||||
},
|
||||
},
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
description: 'Google Maps URL for the place',
|
||||
optional: true,
|
||||
},
|
||||
utcOffset: {
|
||||
type: 'number',
|
||||
description: 'UTC offset in minutes',
|
||||
optional: true,
|
||||
},
|
||||
vicinity: {
|
||||
type: 'string',
|
||||
description: 'Simplified address (neighborhood/street)',
|
||||
optional: true,
|
||||
},
|
||||
businessStatus: {
|
||||
type: 'string',
|
||||
description: 'Business status (OPERATIONAL, CLOSED_TEMPORARILY, CLOSED_PERMANENTLY)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
170
apps/sim/tools/google_maps/places_search.ts
Normal file
170
apps/sim/tools/google_maps/places_search.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import type {
|
||||
GoogleMapsPlacesSearchParams,
|
||||
GoogleMapsPlacesSearchResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsPlacesSearchTool: ToolConfig<
|
||||
GoogleMapsPlacesSearchParams,
|
||||
GoogleMapsPlacesSearchResponse
|
||||
> = {
|
||||
id: 'google_maps_places_search',
|
||||
name: 'Google Maps Places Search',
|
||||
description: 'Search for places using a text query',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Google Maps API key',
|
||||
},
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search query (e.g., "restaurants in Times Square")',
|
||||
},
|
||||
location: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Location to bias results towards ({lat, lng})',
|
||||
},
|
||||
radius: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search radius in meters',
|
||||
},
|
||||
type: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Place type filter (e.g., restaurant, cafe, hotel)',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Language code for results (e.g., en, es, fr)',
|
||||
},
|
||||
region: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Region bias as a ccTLD code (e.g., us, uk)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://maps.googleapis.com/maps/api/place/textsearch/json')
|
||||
url.searchParams.set('query', params.query.trim())
|
||||
url.searchParams.set('key', params.apiKey.trim())
|
||||
|
||||
if (params.location) {
|
||||
url.searchParams.set('location', `${params.location.lat},${params.location.lng}`)
|
||||
}
|
||||
if (params.radius) {
|
||||
url.searchParams.set('radius', params.radius.toString())
|
||||
}
|
||||
if (params.type) {
|
||||
url.searchParams.set('type', params.type)
|
||||
}
|
||||
if (params.language) {
|
||||
url.searchParams.set('language', params.language.trim())
|
||||
}
|
||||
if (params.region) {
|
||||
url.searchParams.set('region', params.region.trim())
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.status !== 'OK' && data.status !== 'ZERO_RESULTS') {
|
||||
throw new Error(
|
||||
`Places search failed: ${data.status} - ${data.error_message || 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
|
||||
const places = (data.results || []).map(
|
||||
(place: {
|
||||
place_id: string
|
||||
name: string
|
||||
formatted_address: string
|
||||
geometry: { location: { lat: number; lng: number } }
|
||||
types: string[]
|
||||
rating?: number
|
||||
user_ratings_total?: number
|
||||
price_level?: number
|
||||
opening_hours?: { open_now: boolean }
|
||||
photos?: Array<{ photo_reference: string; height: number; width: number }>
|
||||
business_status?: string
|
||||
}) => ({
|
||||
placeId: place.place_id,
|
||||
name: place.name,
|
||||
formattedAddress: place.formatted_address,
|
||||
lat: place.geometry.location.lat,
|
||||
lng: place.geometry.location.lng,
|
||||
types: place.types ?? [],
|
||||
rating: place.rating ?? null,
|
||||
userRatingsTotal: place.user_ratings_total ?? null,
|
||||
priceLevel: place.price_level ?? null,
|
||||
openNow: place.opening_hours?.open_now ?? null,
|
||||
photoReference: place.photos?.[0]?.photo_reference ?? null,
|
||||
businessStatus: place.business_status ?? null,
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
places,
|
||||
nextPageToken: data.next_page_token ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
places: {
|
||||
type: 'array',
|
||||
description: 'List of places found',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
placeId: { type: 'string', description: 'Google Place ID' },
|
||||
name: { type: 'string', description: 'Place name' },
|
||||
formattedAddress: { type: 'string', description: 'Formatted address' },
|
||||
lat: { type: 'number', description: 'Latitude' },
|
||||
lng: { type: 'number', description: 'Longitude' },
|
||||
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: 'number', description: 'Price level (0-4)', optional: true },
|
||||
openNow: { type: 'boolean', description: 'Whether currently open', optional: true },
|
||||
photoReference: {
|
||||
type: 'string',
|
||||
description: 'Photo reference for Photos API',
|
||||
optional: true,
|
||||
},
|
||||
businessStatus: { type: 'string', description: 'Business status', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextPageToken: {
|
||||
type: 'string',
|
||||
description: 'Token for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
117
apps/sim/tools/google_maps/reverse_geocode.ts
Normal file
117
apps/sim/tools/google_maps/reverse_geocode.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import type {
|
||||
GoogleMapsReverseGeocodeParams,
|
||||
GoogleMapsReverseGeocodeResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsReverseGeocodeTool: ToolConfig<
|
||||
GoogleMapsReverseGeocodeParams,
|
||||
GoogleMapsReverseGeocodeResponse
|
||||
> = {
|
||||
id: 'google_maps_reverse_geocode',
|
||||
name: 'Google Maps Reverse Geocode',
|
||||
description:
|
||||
'Convert geographic coordinates (latitude and longitude) into a human-readable address',
|
||||
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 coordinate',
|
||||
},
|
||||
lng: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Longitude coordinate',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Language code for results (e.g., en, es, fr)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://maps.googleapis.com/maps/api/geocode/json')
|
||||
url.searchParams.set('latlng', `${params.lat},${params.lng}`)
|
||||
url.searchParams.set('key', params.apiKey.trim())
|
||||
if (params.language) {
|
||||
url.searchParams.set('language', params.language.trim())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.status !== 'OK') {
|
||||
throw new Error(
|
||||
`Reverse geocoding failed: ${data.status} - ${data.error_message || 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
|
||||
const result = data.results[0]
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
formattedAddress: result.formatted_address,
|
||||
placeId: result.place_id,
|
||||
addressComponents: (result.address_components || []).map(
|
||||
(comp: { long_name: string; short_name: string; types: string[] }) => ({
|
||||
longName: comp.long_name,
|
||||
shortName: comp.short_name,
|
||||
types: comp.types,
|
||||
})
|
||||
),
|
||||
types: result.types || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
formattedAddress: {
|
||||
type: 'string',
|
||||
description: 'The formatted address string',
|
||||
},
|
||||
placeId: {
|
||||
type: 'string',
|
||||
description: 'Google Place ID for this location',
|
||||
},
|
||||
addressComponents: {
|
||||
type: 'array',
|
||||
description: 'Detailed address components',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
longName: { type: 'string', description: 'Full name of the component' },
|
||||
shortName: { type: 'string', description: 'Abbreviated name' },
|
||||
types: { type: 'array', description: 'Component types' },
|
||||
},
|
||||
},
|
||||
},
|
||||
types: {
|
||||
type: 'array',
|
||||
description: 'Address types (e.g., street_address, route)',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
113
apps/sim/tools/google_maps/snap_to_roads.ts
Normal file
113
apps/sim/tools/google_maps/snap_to_roads.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import type {
|
||||
GoogleMapsSnapToRoadsParams,
|
||||
GoogleMapsSnapToRoadsResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsSnapToRoadsTool: ToolConfig<
|
||||
GoogleMapsSnapToRoadsParams,
|
||||
GoogleMapsSnapToRoadsResponse
|
||||
> = {
|
||||
id: 'google_maps_snap_to_roads',
|
||||
name: 'Google Maps Snap to Roads',
|
||||
description: 'Snap GPS coordinates to the nearest road segment',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Google Maps API key with Roads API enabled',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Pipe-separated list of lat,lng coordinates (e.g., "60.170880,24.942795|60.170879,24.942796")',
|
||||
},
|
||||
interpolate: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether to interpolate additional points along the road',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://roads.googleapis.com/v1/snapToRoads')
|
||||
url.searchParams.set('path', params.path.trim())
|
||||
url.searchParams.set('key', params.apiKey.trim())
|
||||
if (params.interpolate !== undefined) {
|
||||
url.searchParams.set('interpolate', String(params.interpolate))
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(`Snap to Roads failed: ${data.error.message || 'Unknown error'}`)
|
||||
}
|
||||
|
||||
const snappedPoints = (data.snappedPoints || []).map(
|
||||
(point: {
|
||||
location: { latitude: number; longitude: number }
|
||||
originalIndex?: number
|
||||
placeId: string
|
||||
}) => ({
|
||||
location: {
|
||||
lat: point.location.latitude,
|
||||
lng: point.location.longitude,
|
||||
},
|
||||
originalIndex: point.originalIndex,
|
||||
placeId: point.placeId,
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
snappedPoints,
|
||||
warningMessage: data.warningMessage || null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
snappedPoints: {
|
||||
type: 'array',
|
||||
description: 'Array of snapped points on roads',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
location: {
|
||||
type: 'object',
|
||||
description: 'Snapped location coordinates',
|
||||
properties: {
|
||||
lat: { type: 'number', description: 'Latitude' },
|
||||
lng: { type: 'number', description: 'Longitude' },
|
||||
},
|
||||
},
|
||||
originalIndex: {
|
||||
type: 'number',
|
||||
description: 'Index in the original path (if not interpolated)',
|
||||
},
|
||||
placeId: { type: 'string', description: 'Place ID for this road segment' },
|
||||
},
|
||||
},
|
||||
},
|
||||
warningMessage: {
|
||||
type: 'string',
|
||||
description: 'Warning message if any (e.g., if points could not be snapped)',
|
||||
},
|
||||
},
|
||||
}
|
||||
133
apps/sim/tools/google_maps/speed_limits.ts
Normal file
133
apps/sim/tools/google_maps/speed_limits.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
import type {
|
||||
GoogleMapsSpeedLimitsParams,
|
||||
GoogleMapsSpeedLimitsResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsSpeedLimitsTool: ToolConfig<
|
||||
GoogleMapsSpeedLimitsParams,
|
||||
GoogleMapsSpeedLimitsResponse
|
||||
> = {
|
||||
id: 'google_maps_speed_limits',
|
||||
name: 'Google Maps Speed Limits',
|
||||
description: 'Get speed limits for road segments',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Google Maps API key with Roads API enabled',
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Pipe-separated list of lat,lng coordinates (e.g., "60.170880,24.942795|60.170879,24.942796")',
|
||||
},
|
||||
placeIds: {
|
||||
type: 'array',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Array of Place IDs for road segments (alternative to path)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://roads.googleapis.com/v1/speedLimits')
|
||||
url.searchParams.set('key', params.apiKey.trim())
|
||||
|
||||
if (params.path) {
|
||||
url.searchParams.set('path', params.path.trim())
|
||||
}
|
||||
|
||||
if (params.placeIds && params.placeIds.length > 0) {
|
||||
for (const placeId of params.placeIds) {
|
||||
url.searchParams.append('placeId', placeId.trim())
|
||||
}
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(`Speed Limits failed: ${data.error.message || 'Unknown error'}`)
|
||||
}
|
||||
|
||||
const speedLimits = (data.speedLimits || []).map(
|
||||
(limit: { placeId: string; speedLimit: number; units: 'KPH' | 'MPH' }) => ({
|
||||
placeId: limit.placeId,
|
||||
speedLimit: limit.speedLimit,
|
||||
units: limit.units,
|
||||
})
|
||||
)
|
||||
|
||||
const snappedPoints = (data.snappedPoints || []).map(
|
||||
(point: {
|
||||
location: { latitude: number; longitude: number }
|
||||
originalIndex?: number
|
||||
placeId: string
|
||||
}) => ({
|
||||
location: {
|
||||
lat: point.location.latitude,
|
||||
lng: point.location.longitude,
|
||||
},
|
||||
originalIndex: point.originalIndex,
|
||||
placeId: point.placeId,
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
speedLimits,
|
||||
snappedPoints,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
speedLimits: {
|
||||
type: 'array',
|
||||
description: 'Array of speed limits for road segments',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
placeId: { type: 'string', description: 'Place ID for the road segment' },
|
||||
speedLimit: { type: 'number', description: 'Speed limit value' },
|
||||
units: { type: 'string', description: 'Speed limit units (KPH or MPH)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
snappedPoints: {
|
||||
type: 'array',
|
||||
description: 'Array of snapped points corresponding to the speed limits',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
location: {
|
||||
type: 'object',
|
||||
description: 'Snapped location coordinates',
|
||||
properties: {
|
||||
lat: { type: 'number', description: 'Latitude' },
|
||||
lng: { type: 'number', description: 'Longitude' },
|
||||
},
|
||||
},
|
||||
originalIndex: { type: 'number', description: 'Index in the original path' },
|
||||
placeId: { type: 'string', description: 'Place ID for this road segment' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
120
apps/sim/tools/google_maps/timezone.ts
Normal file
120
apps/sim/tools/google_maps/timezone.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import type {
|
||||
GoogleMapsTimezoneParams,
|
||||
GoogleMapsTimezoneResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsTimezoneTool: ToolConfig<
|
||||
GoogleMapsTimezoneParams,
|
||||
GoogleMapsTimezoneResponse
|
||||
> = {
|
||||
id: 'google_maps_timezone',
|
||||
name: 'Google Maps Timezone',
|
||||
description: 'Get timezone information for 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 coordinate',
|
||||
},
|
||||
lng: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Longitude coordinate',
|
||||
},
|
||||
timestamp: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Unix timestamp to determine DST offset (defaults to current time)',
|
||||
},
|
||||
language: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Language code for timezone name (e.g., en, es, fr)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const url = new URL('https://maps.googleapis.com/maps/api/timezone/json')
|
||||
url.searchParams.set('location', `${params.lat},${params.lng}`)
|
||||
// Use provided timestamp or current time
|
||||
const timestamp = params.timestamp ?? Math.floor(Date.now() / 1000)
|
||||
url.searchParams.set('timestamp', timestamp.toString())
|
||||
url.searchParams.set('key', params.apiKey.trim())
|
||||
if (params.language) {
|
||||
url.searchParams.set('language', params.language.trim())
|
||||
}
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.status !== 'OK') {
|
||||
throw new Error(
|
||||
`Timezone request failed: ${data.status} - ${data.errorMessage || 'Unknown error'}`
|
||||
)
|
||||
}
|
||||
|
||||
// Calculate total offset
|
||||
const totalOffsetSeconds = data.rawOffset + data.dstOffset
|
||||
const totalOffsetHours = totalOffsetSeconds / 3600
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
timeZoneId: data.timeZoneId,
|
||||
timeZoneName: data.timeZoneName,
|
||||
rawOffset: data.rawOffset,
|
||||
dstOffset: data.dstOffset,
|
||||
totalOffsetSeconds,
|
||||
totalOffsetHours,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
timeZoneId: {
|
||||
type: 'string',
|
||||
description: 'IANA timezone ID (e.g., "America/New_York", "Europe/London")',
|
||||
},
|
||||
timeZoneName: {
|
||||
type: 'string',
|
||||
description: 'Localized timezone name (e.g., "Eastern Daylight Time")',
|
||||
},
|
||||
rawOffset: {
|
||||
type: 'number',
|
||||
description: 'UTC offset in seconds (without DST)',
|
||||
},
|
||||
dstOffset: {
|
||||
type: 'number',
|
||||
description: 'Daylight Saving Time offset in seconds (0 if not in DST)',
|
||||
},
|
||||
totalOffsetSeconds: {
|
||||
type: 'number',
|
||||
description: 'Total UTC offset in seconds (rawOffset + dstOffset)',
|
||||
},
|
||||
totalOffsetHours: {
|
||||
type: 'number',
|
||||
description: 'Total UTC offset in hours (e.g., -5 for EST, -4 for EDT)',
|
||||
},
|
||||
},
|
||||
}
|
||||
481
apps/sim/tools/google_maps/types.ts
Normal file
481
apps/sim/tools/google_maps/types.ts
Normal file
@@ -0,0 +1,481 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Common location type
|
||||
*/
|
||||
export interface LatLng {
|
||||
lat: number
|
||||
lng: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Address component from geocoding
|
||||
*/
|
||||
export interface AddressComponent {
|
||||
longName: string
|
||||
shortName: string
|
||||
types: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Snapped point from Roads API
|
||||
*/
|
||||
export interface SnappedPoint {
|
||||
location: LatLng
|
||||
originalIndex?: number
|
||||
placeId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Speed limit info from Roads API
|
||||
*/
|
||||
export interface SpeedLimit {
|
||||
placeId: string
|
||||
speedLimit: number
|
||||
units: 'KPH' | 'MPH'
|
||||
}
|
||||
|
||||
/**
|
||||
* Cell tower info for geolocation
|
||||
*/
|
||||
export interface CellTower {
|
||||
cellId: number
|
||||
locationAreaCode: number
|
||||
mobileCountryCode: number
|
||||
mobileNetworkCode: number
|
||||
age?: number
|
||||
signalStrength?: number
|
||||
timingAdvance?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* WiFi access point info for geolocation
|
||||
*/
|
||||
export interface WifiAccessPoint {
|
||||
macAddress: string
|
||||
signalStrength?: number
|
||||
age?: number
|
||||
channel?: number
|
||||
signalToNoiseRatio?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Air quality index info
|
||||
*/
|
||||
export interface AirQualityIndex {
|
||||
code: string
|
||||
displayName: string
|
||||
aqi: number
|
||||
aqiDisplay: string
|
||||
color: {
|
||||
red: number
|
||||
green: number
|
||||
blue: number
|
||||
}
|
||||
category: string
|
||||
dominantPollutant: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Pollutant concentration info
|
||||
*/
|
||||
export interface Pollutant {
|
||||
code: string
|
||||
displayName: string
|
||||
fullName: string
|
||||
concentration: {
|
||||
value: number
|
||||
units: string
|
||||
}
|
||||
additionalInfo?: {
|
||||
sources: string
|
||||
effects: string
|
||||
}
|
||||
}
|
||||
|
||||
// Geocode
|
||||
// ============================================================================
|
||||
|
||||
export interface GoogleMapsGeocodeParams {
|
||||
apiKey: string
|
||||
address: string
|
||||
language?: string
|
||||
region?: string
|
||||
}
|
||||
|
||||
export interface GoogleMapsGeocodeResponse extends ToolResponse {
|
||||
output: {
|
||||
formattedAddress: string
|
||||
lat: number
|
||||
lng: number
|
||||
location: LatLng
|
||||
placeId: string
|
||||
addressComponents: AddressComponent[]
|
||||
locationType: string
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Reverse Geocode
|
||||
// ============================================================================
|
||||
|
||||
export interface GoogleMapsReverseGeocodeParams {
|
||||
apiKey: string
|
||||
lat: number
|
||||
lng: number
|
||||
language?: string
|
||||
}
|
||||
|
||||
export interface GoogleMapsReverseGeocodeResponse extends ToolResponse {
|
||||
output: {
|
||||
formattedAddress: string
|
||||
placeId: string
|
||||
addressComponents: AddressComponent[]
|
||||
types: string[]
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Directions
|
||||
// ============================================================================
|
||||
|
||||
export interface DirectionsStep {
|
||||
instruction: string
|
||||
distanceText: string
|
||||
distanceMeters: number
|
||||
durationText: string
|
||||
durationSeconds: number
|
||||
startLocation: LatLng
|
||||
endLocation: LatLng
|
||||
travelMode: string
|
||||
maneuver: string | null
|
||||
}
|
||||
|
||||
export interface DirectionsLeg {
|
||||
startAddress: string
|
||||
endAddress: string
|
||||
startLocation: LatLng
|
||||
endLocation: LatLng
|
||||
distanceText: string
|
||||
distanceMeters: number
|
||||
durationText: string
|
||||
durationSeconds: number
|
||||
steps: DirectionsStep[]
|
||||
}
|
||||
|
||||
export interface DirectionsRoute {
|
||||
summary: string
|
||||
legs: DirectionsLeg[]
|
||||
overviewPolyline: string
|
||||
warnings: string[]
|
||||
waypointOrder: number[]
|
||||
}
|
||||
|
||||
export interface GoogleMapsDirectionsParams {
|
||||
apiKey: string
|
||||
origin: string
|
||||
destination: string
|
||||
mode?: 'driving' | 'walking' | 'bicycling' | 'transit'
|
||||
avoid?: string
|
||||
waypoints?: string[]
|
||||
units?: 'metric' | 'imperial'
|
||||
language?: string
|
||||
}
|
||||
|
||||
export interface GoogleMapsDirectionsResponse extends ToolResponse {
|
||||
output: {
|
||||
routes: DirectionsRoute[]
|
||||
distanceText: string
|
||||
distanceMeters: number
|
||||
durationText: string
|
||||
durationSeconds: number
|
||||
startAddress: string
|
||||
endAddress: string
|
||||
steps: DirectionsStep[]
|
||||
polyline: string
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Distance Matrix
|
||||
// ============================================================================
|
||||
|
||||
export interface DistanceMatrixElement {
|
||||
distanceText: string
|
||||
distanceMeters: number
|
||||
durationText: string
|
||||
durationSeconds: number
|
||||
durationInTrafficText: string | null
|
||||
durationInTrafficSeconds: number | null
|
||||
status: string
|
||||
}
|
||||
|
||||
export interface DistanceMatrixRow {
|
||||
elements: DistanceMatrixElement[]
|
||||
}
|
||||
|
||||
export interface GoogleMapsDistanceMatrixParams {
|
||||
apiKey: string
|
||||
origin: string
|
||||
destinations: string[]
|
||||
mode?: 'driving' | 'walking' | 'bicycling' | 'transit'
|
||||
avoid?: string
|
||||
units?: 'metric' | 'imperial'
|
||||
language?: string
|
||||
}
|
||||
|
||||
export interface GoogleMapsDistanceMatrixResponse extends ToolResponse {
|
||||
output: {
|
||||
originAddresses: string[]
|
||||
destinationAddresses: string[]
|
||||
rows: DistanceMatrixRow[]
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Places Search
|
||||
// ============================================================================
|
||||
|
||||
export interface PlaceResult {
|
||||
placeId: string
|
||||
name: string
|
||||
formattedAddress: string
|
||||
lat: number
|
||||
lng: number
|
||||
types: string[]
|
||||
rating: number | null
|
||||
userRatingsTotal: number | null
|
||||
priceLevel: number | null
|
||||
openNow: boolean | null
|
||||
photoReference: string | null
|
||||
businessStatus: string | null
|
||||
}
|
||||
|
||||
export interface GoogleMapsPlacesSearchParams {
|
||||
apiKey: string
|
||||
query: string
|
||||
location?: LatLng
|
||||
radius?: number
|
||||
type?: string
|
||||
language?: string
|
||||
region?: string
|
||||
}
|
||||
|
||||
export interface GoogleMapsPlacesSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
places: PlaceResult[]
|
||||
nextPageToken: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Place Details
|
||||
// ============================================================================
|
||||
|
||||
export interface PlaceReview {
|
||||
authorName: string
|
||||
authorUrl: string | null
|
||||
profilePhotoUrl: string | null
|
||||
rating: number
|
||||
text: string
|
||||
time: number
|
||||
relativeTimeDescription: string
|
||||
}
|
||||
|
||||
export interface PlacePhoto {
|
||||
photoReference: string
|
||||
height: number
|
||||
width: number
|
||||
htmlAttributions: string[]
|
||||
}
|
||||
|
||||
export interface GoogleMapsPlaceDetailsParams {
|
||||
apiKey: string
|
||||
placeId: string
|
||||
fields?: string
|
||||
language?: string
|
||||
}
|
||||
|
||||
export interface GoogleMapsPlaceDetailsResponse extends ToolResponse {
|
||||
output: {
|
||||
placeId: string
|
||||
name: string | null
|
||||
formattedAddress: string | null
|
||||
lat: number | null
|
||||
lng: number | null
|
||||
types: string[]
|
||||
rating: number | null
|
||||
userRatingsTotal: number | null
|
||||
priceLevel: number | null
|
||||
website: string | null
|
||||
phoneNumber: string | null
|
||||
internationalPhoneNumber: string | null
|
||||
openNow: boolean | null
|
||||
weekdayText: string[]
|
||||
reviews: PlaceReview[]
|
||||
photos: PlacePhoto[]
|
||||
url: string | null
|
||||
utcOffset: number | null
|
||||
vicinity: string | null
|
||||
businessStatus: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Elevation
|
||||
// ============================================================================
|
||||
|
||||
export interface GoogleMapsElevationParams {
|
||||
apiKey: string
|
||||
lat: number
|
||||
lng: number
|
||||
}
|
||||
|
||||
export interface GoogleMapsElevationResponse extends ToolResponse {
|
||||
output: {
|
||||
elevation: number
|
||||
lat: number
|
||||
lng: number
|
||||
resolution: number
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Timezone
|
||||
// ============================================================================
|
||||
|
||||
export interface GoogleMapsTimezoneParams {
|
||||
apiKey: string
|
||||
lat: number
|
||||
lng: number
|
||||
timestamp?: number
|
||||
language?: string
|
||||
}
|
||||
|
||||
export interface GoogleMapsTimezoneResponse extends ToolResponse {
|
||||
output: {
|
||||
timeZoneId: string
|
||||
timeZoneName: string
|
||||
rawOffset: number
|
||||
dstOffset: number
|
||||
totalOffsetSeconds: number
|
||||
totalOffsetHours: number
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Snap to Roads
|
||||
// ============================================================================
|
||||
|
||||
export interface GoogleMapsSnapToRoadsParams {
|
||||
apiKey: string
|
||||
path: string
|
||||
interpolate?: boolean
|
||||
}
|
||||
|
||||
export interface GoogleMapsSnapToRoadsResponse extends ToolResponse {
|
||||
output: {
|
||||
snappedPoints: SnappedPoint[]
|
||||
warningMessage: string | null
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Speed Limits
|
||||
// ============================================================================
|
||||
|
||||
export interface GoogleMapsSpeedLimitsParams {
|
||||
apiKey: string
|
||||
path?: string
|
||||
placeIds?: string[]
|
||||
}
|
||||
|
||||
export interface GoogleMapsSpeedLimitsResponse extends ToolResponse {
|
||||
output: {
|
||||
speedLimits: SpeedLimit[]
|
||||
snappedPoints: SnappedPoint[]
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Validate Address
|
||||
// ============================================================================
|
||||
|
||||
export interface GoogleMapsValidateAddressParams {
|
||||
apiKey: string
|
||||
address: string
|
||||
regionCode?: string
|
||||
locality?: string
|
||||
enableUspsCass?: boolean
|
||||
}
|
||||
|
||||
export interface GoogleMapsValidateAddressResponse extends ToolResponse {
|
||||
output: {
|
||||
formattedAddress: string
|
||||
lat: number
|
||||
lng: number
|
||||
placeId: string
|
||||
addressComplete: boolean
|
||||
hasUnconfirmedComponents: boolean
|
||||
hasInferredComponents: boolean
|
||||
hasReplacedComponents: boolean
|
||||
validationGranularity: string
|
||||
geocodeGranularity: string
|
||||
addressComponents: AddressComponent[]
|
||||
missingComponentTypes: string[]
|
||||
unconfirmedComponentTypes: string[]
|
||||
unresolvedTokens: string[]
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Geolocate
|
||||
// ============================================================================
|
||||
|
||||
export interface GoogleMapsGeolocateParams {
|
||||
apiKey: string
|
||||
homeMobileCountryCode?: number
|
||||
homeMobileNetworkCode?: number
|
||||
radioType?: 'lte' | 'gsm' | 'cdma' | 'wcdma' | 'nr'
|
||||
carrier?: string
|
||||
considerIp?: boolean
|
||||
cellTowers?: CellTower[]
|
||||
wifiAccessPoints?: WifiAccessPoint[]
|
||||
}
|
||||
|
||||
export interface GoogleMapsGeolocateResponse extends ToolResponse {
|
||||
output: {
|
||||
lat: number
|
||||
lng: number
|
||||
accuracy: number
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Air Quality
|
||||
// ============================================================================
|
||||
|
||||
export interface GoogleMapsAirQualityParams {
|
||||
apiKey: string
|
||||
lat: number
|
||||
lng: number
|
||||
languageCode?: string
|
||||
}
|
||||
|
||||
export interface GoogleMapsAirQualityResponse extends ToolResponse {
|
||||
output: {
|
||||
dateTime: string
|
||||
regionCode: string
|
||||
indexes: AirQualityIndex[]
|
||||
pollutants: Pollutant[]
|
||||
healthRecommendations: {
|
||||
generalPopulation: string
|
||||
elderly: string
|
||||
lungDiseasePopulation: string
|
||||
heartDiseasePopulation: string
|
||||
athletes: string
|
||||
pregnantWomen: string
|
||||
children: string
|
||||
} | null
|
||||
}
|
||||
}
|
||||
194
apps/sim/tools/google_maps/validate_address.ts
Normal file
194
apps/sim/tools/google_maps/validate_address.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
import type {
|
||||
GoogleMapsValidateAddressParams,
|
||||
GoogleMapsValidateAddressResponse,
|
||||
} from '@/tools/google_maps/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const googleMapsValidateAddressTool: ToolConfig<
|
||||
GoogleMapsValidateAddressParams,
|
||||
GoogleMapsValidateAddressResponse
|
||||
> = {
|
||||
id: 'google_maps_validate_address',
|
||||
name: 'Google Maps Validate Address',
|
||||
description: 'Validate and standardize a postal address',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Google Maps API key with Address Validation API enabled',
|
||||
},
|
||||
address: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'The address to validate (as a single string)',
|
||||
},
|
||||
regionCode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ISO 3166-1 alpha-2 country code (e.g., "US", "CA")',
|
||||
},
|
||||
locality: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'City or locality name',
|
||||
},
|
||||
enableUspsCass: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Enable USPS CASS validation for US addresses',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
return `https://addressvalidation.googleapis.com/v1:validateAddress?key=${params.apiKey.trim()}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: {
|
||||
address: { addressLines: string[]; regionCode?: string; locality?: string }
|
||||
enableUspsCass?: boolean
|
||||
} = {
|
||||
address: {
|
||||
addressLines: [params.address.trim()],
|
||||
},
|
||||
}
|
||||
|
||||
if (params.regionCode) {
|
||||
body.address.regionCode = params.regionCode.trim()
|
||||
}
|
||||
|
||||
if (params.locality) {
|
||||
body.address.locality = params.locality.trim()
|
||||
}
|
||||
|
||||
if (params.enableUspsCass !== undefined) {
|
||||
body.enableUspsCass = params.enableUspsCass
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(`Address Validation failed: ${data.error.message || 'Unknown error'}`)
|
||||
}
|
||||
|
||||
const result = data.result
|
||||
const verdict = result?.verdict || {}
|
||||
const address = result?.address || {}
|
||||
const geocode = result?.geocode || {}
|
||||
|
||||
const addressComponents = (address.addressComponents || []).map(
|
||||
(comp: {
|
||||
componentName: { text: string; languageCode?: string }
|
||||
componentType: string
|
||||
confirmationLevel: string
|
||||
}) => ({
|
||||
longName: comp.componentName?.text || '',
|
||||
shortName: comp.componentName?.text || '',
|
||||
types: [comp.componentType],
|
||||
})
|
||||
)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
formattedAddress: address.formattedAddress || '',
|
||||
lat: geocode.location?.latitude || 0,
|
||||
lng: geocode.location?.longitude || 0,
|
||||
placeId: geocode.placeId || '',
|
||||
addressComplete: verdict.addressComplete || false,
|
||||
hasUnconfirmedComponents: verdict.hasUnconfirmedComponents || false,
|
||||
hasInferredComponents: verdict.hasInferredComponents || false,
|
||||
hasReplacedComponents: verdict.hasReplacedComponents || false,
|
||||
validationGranularity: verdict.validationGranularity || '',
|
||||
geocodeGranularity: verdict.geocodeGranularity || '',
|
||||
addressComponents,
|
||||
missingComponentTypes: address.missingComponentTypes || [],
|
||||
unconfirmedComponentTypes: address.unconfirmedComponentTypes || [],
|
||||
unresolvedTokens: address.unresolvedTokens || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
formattedAddress: {
|
||||
type: 'string',
|
||||
description: 'The standardized formatted address',
|
||||
},
|
||||
lat: {
|
||||
type: 'number',
|
||||
description: 'Latitude coordinate',
|
||||
},
|
||||
lng: {
|
||||
type: 'number',
|
||||
description: 'Longitude coordinate',
|
||||
},
|
||||
placeId: {
|
||||
type: 'string',
|
||||
description: 'Google Place ID for this address',
|
||||
},
|
||||
addressComplete: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the address is complete and deliverable',
|
||||
},
|
||||
hasUnconfirmedComponents: {
|
||||
type: 'boolean',
|
||||
description: 'Whether some address components could not be confirmed',
|
||||
},
|
||||
hasInferredComponents: {
|
||||
type: 'boolean',
|
||||
description: 'Whether some components were inferred (not in input)',
|
||||
},
|
||||
hasReplacedComponents: {
|
||||
type: 'boolean',
|
||||
description: 'Whether some components were replaced with canonical values',
|
||||
},
|
||||
validationGranularity: {
|
||||
type: 'string',
|
||||
description: 'Granularity of validation (PREMISE, SUB_PREMISE, ROUTE, etc.)',
|
||||
},
|
||||
geocodeGranularity: {
|
||||
type: 'string',
|
||||
description: 'Granularity of the geocode result',
|
||||
},
|
||||
addressComponents: {
|
||||
type: 'array',
|
||||
description: 'Detailed address components',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
longName: { type: 'string', description: 'Full name of the component' },
|
||||
shortName: { type: 'string', description: 'Abbreviated name' },
|
||||
types: { type: 'array', description: 'Component types' },
|
||||
},
|
||||
},
|
||||
},
|
||||
missingComponentTypes: {
|
||||
type: 'array',
|
||||
description: 'Types of address components that are missing',
|
||||
},
|
||||
unconfirmedComponentTypes: {
|
||||
type: 'array',
|
||||
description: 'Types of components that could not be confirmed',
|
||||
},
|
||||
unresolvedTokens: {
|
||||
type: 'array',
|
||||
description: 'Input tokens that could not be resolved',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -205,6 +205,7 @@ import {
|
||||
dropboxSearchTool,
|
||||
dropboxUploadTool,
|
||||
} from '@/tools/dropbox'
|
||||
import { chainOfThoughtTool, predictTool, reactTool } from '@/tools/dspy'
|
||||
import { duckduckgoSearchTool } from '@/tools/duckduckgo'
|
||||
import {
|
||||
dynamodbDeleteTool,
|
||||
@@ -545,6 +546,21 @@ import {
|
||||
googleGroupsUpdateMemberTool,
|
||||
googleGroupsUpdateSettingsTool,
|
||||
} from '@/tools/google_groups'
|
||||
import {
|
||||
googleMapsAirQualityTool,
|
||||
googleMapsDirectionsTool,
|
||||
googleMapsDistanceMatrixTool,
|
||||
googleMapsElevationTool,
|
||||
googleMapsGeocodeTool,
|
||||
googleMapsGeolocateTool,
|
||||
googleMapsPlaceDetailsTool,
|
||||
googleMapsPlacesSearchTool,
|
||||
googleMapsReverseGeocodeTool,
|
||||
googleMapsSnapToRoadsTool,
|
||||
googleMapsSpeedLimitsTool,
|
||||
googleMapsTimezoneTool,
|
||||
googleMapsValidateAddressTool,
|
||||
} from '@/tools/google_maps'
|
||||
import {
|
||||
googleSheetsAppendTool,
|
||||
googleSheetsAppendV2Tool,
|
||||
@@ -2144,6 +2160,9 @@ export const tools: Record<string, ToolConfig> = {
|
||||
dropbox_create_shared_link: dropboxCreateSharedLinkTool,
|
||||
dropbox_search: dropboxSearchTool,
|
||||
duckduckgo_search: duckduckgoSearchTool,
|
||||
dspy_predict: predictTool,
|
||||
dspy_chain_of_thought: chainOfThoughtTool,
|
||||
dspy_react: reactTool,
|
||||
mongodb_query: mongodbQueryTool,
|
||||
mongodb_insert: mongodbInsertTool,
|
||||
mongodb_update: mongodbUpdateTool,
|
||||
@@ -2427,6 +2446,19 @@ export const tools: Record<string, ToolConfig> = {
|
||||
google_docs_read: googleDocsReadTool,
|
||||
google_docs_write: googleDocsWriteTool,
|
||||
google_docs_create: googleDocsCreateTool,
|
||||
google_maps_air_quality: googleMapsAirQualityTool,
|
||||
google_maps_directions: googleMapsDirectionsTool,
|
||||
google_maps_distance_matrix: googleMapsDistanceMatrixTool,
|
||||
google_maps_elevation: googleMapsElevationTool,
|
||||
google_maps_geocode: googleMapsGeocodeTool,
|
||||
google_maps_geolocate: googleMapsGeolocateTool,
|
||||
google_maps_place_details: googleMapsPlaceDetailsTool,
|
||||
google_maps_places_search: googleMapsPlacesSearchTool,
|
||||
google_maps_reverse_geocode: googleMapsReverseGeocodeTool,
|
||||
google_maps_snap_to_roads: googleMapsSnapToRoadsTool,
|
||||
google_maps_speed_limits: googleMapsSpeedLimitsTool,
|
||||
google_maps_timezone: googleMapsTimezoneTool,
|
||||
google_maps_validate_address: googleMapsValidateAddressTool,
|
||||
google_sheets_read: googleSheetsReadTool,
|
||||
google_sheets_write: googleSheetsWriteTool,
|
||||
google_sheets_update: googleSheetsUpdateTool,
|
||||
|
||||
Reference in New Issue
Block a user