feat(tools): added google maps and DSPy (#3098)

* feat(tools): added google maps and DSPy

* updated docs

* updated broken import path

* updated icon
This commit is contained in:
Waleed
2026-01-31 11:08:35 -08:00
committed by GitHub
parent cf2f1abcaf
commit e11758fb43
36 changed files with 5136 additions and 15 deletions

View File

@@ -5170,3 +5170,254 @@ export function CalComIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function GoogleMapsIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 92.3 132.3'>
<path
fill='#1a73e8'
d='M60.2 2.2C55.8.8 51 0 46.1 0 32 0 19.3 6.4 10.8 16.5l21.8 18.3L60.2 2.2z'
/>
<path
fill='#ea4335'
d='M10.8 16.5C4.1 24.5 0 34.9 0 46.1c0 8.7 1.7 15.7 4.6 22l28-33.3-21.8-18.3z'
/>
<path
fill='#4285f4'
d='M46.2 28.5c9.8 0 17.7 7.9 17.7 17.7 0 4.3-1.6 8.3-4.2 11.4 0 0 13.9-16.6 27.5-32.7-5.6-10.8-15.3-19-27-22.7L32.6 34.8c3.3-3.8 8.1-6.3 13.6-6.3'
/>
<path
fill='#fbbc04'
d='M46.2 63.8c-9.8 0-17.7-7.9-17.7-17.7 0-4.3 1.5-8.3 4.1-11.3l-28 33.3c4.8 10.6 12.8 19.2 21 29.9l34.1-40.5c-3.3 3.9-8.1 6.3-13.5 6.3'
/>
<path
fill='#34a853'
d='M59.1 109.2c15.4-24.1 33.3-35 33.3-63 0-7.7-1.9-14.9-5.2-21.3L25.6 98c2.6 3.4 5.3 7.3 7.9 11.3 9.4 14.5 6.8 23.1 12.8 23.1s3.4-8.7 12.8-23.2'
/>
</svg>
)
}
export function DsPyIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='30 28 185 175' fill='none'>
<path
fill='#D34C3F'
opacity='1.000000'
stroke='#D34C3F'
strokeWidth='3'
d='
M202.406967,127.017563
C202.179611,146.161911 202.031097,164.809784 201.685379,183.454010
C201.545227,191.012054 200.718399,191.828491 193.289062,191.886826
C172.464081,192.050339 151.635834,192.253342 130.812500,192.056702
C104.669151,191.809830 78.528122,191.241791 52.388767,190.686325
C42.707352,190.480606 42.115833,189.780975 42.157444,180.326309
C42.331207,140.845123 42.538979,101.364082 42.762138,61.883144
C42.787552,57.387417 42.989182,52.892700 43.107479,48.397476
C43.314110,40.545937 44.093277,39.498543 52.078091,39.155457
C62.393070,38.712250 72.725296,38.528290 83.050827,38.499870
C112.706757,38.418251 142.363419,38.390633 172.018936,38.527496
C179.500366,38.562023 186.991394,39.135315 194.454422,39.748272
C201.079926,40.292439 202.319077,41.541882 202.351517,48.097012
C202.453690,68.749702 202.355896,89.403366 202.347412,110.056648
C202.345154,115.544296 202.385666,121.031960 202.406967,127.017563
M105.672386,111.007179
C108.474365,111.345215 111.276726,111.921921 114.078171,111.917526
C115.417778,111.915421 117.727127,111.041771 117.934349,110.168335
C119.145897,105.061768 119.865135,99.838402 120.740562,94.712914
C106.966454,90.837227 103.521248,87.187225 104.005447,77.347023
C104.501411,67.267906 107.785126,64.137291 120.608673,61.878979
C120.934212,58.412746 121.138908,54.797504 121.647324,51.225491
C122.151093,47.686234 120.697983,46.383198 117.276314,46.409954
C96.324112,46.573811 75.371307,46.658936 54.418976,46.809055
C53.319950,46.816925 52.223454,47.178463 50.130283,47.553909
C50.130283,67.101257 50.146877,86.540329 50.112003,105.979309
C50.106895,108.825279 51.497246,109.676353 54.153763,109.668755
C59.309399,109.654015 64.480812,109.664360 69.617935,110.041916
C76.186432,110.524658 77.751175,112.890678 76.103035,119.056091
C73.827133,127.569878 75.807640,129.900314 84.675858,129.143616
C89.877007,128.699814 91.632874,126.489716 90.924309,121.176811
C90.726822,119.696030 90.323967,118.243813 90.082695,116.767479
C89.404724,112.619064 91.327385,110.218483 95.374054,110.068687
C98.493156,109.953224 101.640015,110.587189 105.672386,111.007179
M175.323807,104.571556
C175.695831,107.487671 176.067856,110.403778 176.515991,113.916504
C181.175064,113.615845 185.769730,113.269371 190.370239,113.039795
C193.497421,112.883759 194.626556,111.290581 194.623550,108.249763
C194.604736,89.273628 194.644714,70.297173 194.761490,51.321419
C194.780701,48.197872 193.737167,46.821045 190.443375,46.833015
C186.124878,46.848717 181.806122,46.236942 177.485046,46.202831
C163.331711,46.091091 149.177261,46.121143 135.023239,46.091145
C131.461014,46.083595 129.597000,47.358112 129.753250,51.409149
C129.906189,55.375004 129.409134,59.391159 128.920120,63.351006
C128.045013,70.437523 126.519997,71.374840 119.543953,70.677361
C117.662697,70.489273 114.202606,71.291862 113.953552,72.271683
C113.105225,75.609261 112.453461,79.562759 113.566772,82.608528
C114.178413,84.281853 118.506584,85.459900 121.107094,85.369347
C127.143333,85.159134 129.004593,86.451027 128.853668,92.489960
C128.820602,93.813370 128.587814,95.134392 128.406998,96.451027
C127.646179,101.991020 126.870201,107.528931 126.001221,113.776062
C132.081665,114.221489 137.732315,114.635429 142.703461,114.999588
C143.800079,109.887177 143.707199,105.333397 145.692795,102.028297
C150.972488,93.240074 170.332077,94.650421 175.323807,104.571556
M50.104687,123.749725
C49.990089,141.723785 49.888828,159.697952 49.747101,177.671799
C49.724800,180.499878 49.682011,182.963913 53.679199,183.052307
C73.130913,183.482407 92.579651,184.048767 112.028908,184.586304
C115.011475,184.668732 116.186417,183.286621 116.079460,180.329590
C115.935165,176.339752 115.994858,172.340454 116.043648,168.346161
C116.150414,159.604889 119.485802,156.752869 127.020248,160.172668
C128.593277,160.886642 130.718765,161.088547 132.384796,160.683578
C133.890060,160.317688 136.211853,158.853119 136.264572,157.779327
C136.445145,154.101822 136.576385,149.999817 135.057983,146.868820
C134.450867,145.616928 129.501572,146.445511 126.527672,146.393295
C126.067261,146.385193 125.611229,146.769257 125.135185,146.911957
C118.988312,148.754761 116.259544,146.969559 116.000862,140.529480
C115.780586,135.045670 116.072845,129.542297 115.908310,124.054474
C115.870766,122.802582 115.082245,120.657356 114.306252,120.492638
C109.407043,119.452690 104.413986,118.854897 99.252129,118.081146
C99.141937,120.312668 99.049049,121.462982 99.034004,122.614326
C98.915398,131.689316 96.077385,135.419113 87.137741,136.819183
C83.468414,137.393829 79.501442,137.067627 75.823532,136.356491
C70.123749,135.254395 67.086052,131.133224 66.815964,125.325493
C66.484573,118.199181 66.485611,118.188820 59.287899,117.466980
C58.131813,117.351044 56.947235,117.477257 55.800720,117.315323
C51.507248,116.708862 49.446377,118.335457 50.104687,123.749725
M194.723343,147.470551
C194.723343,138.727798 194.723343,129.985031 194.723343,121.948395
C186.231369,121.948395 178.618958,121.861038 171.010101,121.986786
C167.430191,122.045952 166.142395,120.331360 166.553665,117.068069
C166.780792,115.265877 167.649048,113.504562 167.650467,111.724358
C167.652100,109.685745 167.275787,105.984894 166.609360,105.879517
C162.521988,105.233223 158.118408,104.600838 154.185806,105.523117
C150.292984,106.436058 151.757919,110.455353 152.294373,113.268944
C153.965073,122.031258 153.109619,123.299713 144.169769,123.104790
C137.750290,122.964813 131.340836,122.364502 124.468094,121.941299
C124.468094,127.518242 124.468094,132.561798 124.468094,137.593765
C142.195648,138.207077 145.227814,140.286194 144.781631,152.766998
C144.630615,156.991394 143.550781,161.813782 141.204636,165.186859
C137.266098,170.849335 130.598907,169.013229 124.401016,168.434631
C124.401016,172.463516 124.438629,176.104736 124.389984,179.744781
C124.345184,183.097198 126.018867,184.575378 129.281464,184.573456
C149.265366,184.561676 169.249252,184.565826 189.233154,184.574066
C192.694077,184.575500 194.238464,183.124954 194.235001,179.423798
C194.225342,169.102219 194.537933,158.780350 194.723343,147.470551
z'
/>
<path
fill='none'
opacity='1.000000'
stroke='#D34C3F'
strokeWidth='3'
d='
M105.223457,110.947556
C101.640015,110.587189 98.493156,109.953224 95.374054,110.068687
C91.327385,110.218483 89.404724,112.619064 90.082695,116.767479
C90.323967,118.243813 90.726822,119.696030 90.924309,121.176811
C91.632874,126.489716 89.877007,128.699814 84.675858,129.143616
C75.807640,129.900314 73.827133,127.569878 76.103035,119.056091
C77.751175,112.890678 76.186432,110.524658 69.617935,110.041916
C64.480812,109.664360 59.309399,109.654015 54.153763,109.668755
C51.497246,109.676353 50.106895,108.825279 50.112003,105.979309
C50.146877,86.540329 50.130283,67.101257 50.130283,47.553909
C52.223454,47.178463 53.319950,46.816925 54.418976,46.809055
C75.371307,46.658936 96.324112,46.573811 117.276314,46.409954
C120.697983,46.383198 122.151093,47.686234 121.647324,51.225491
C121.138908,54.797504 120.934212,58.412746 120.608673,61.878979
C107.785126,64.137291 104.501411,67.267906 104.005447,77.347023
C103.521248,87.187225 106.966454,90.837227 120.740562,94.712914
C119.865135,99.838402 119.145897,105.061768 117.934349,110.168335
C117.727127,111.041771 115.417778,111.915421 114.078171,111.917526
C111.276726,111.921921 108.474365,111.345215 105.223457,110.947556
z'
/>
<path
fill='none'
opacity='1.000000'
stroke='#D34C3F'
strokeWidth='3'
d='
M175.227921,104.177216
C170.332077,94.650421 150.972488,93.240074 145.692795,102.028297
C143.707199,105.333397 143.800079,109.887177 142.703461,114.999588
C137.732315,114.635429 132.081665,114.221489 126.001221,113.776062
C126.870201,107.528931 127.646179,101.991020 128.406998,96.451027
C128.587814,95.134392 128.820602,93.813370 128.853668,92.489960
C129.004593,86.451027 127.143333,85.159134 121.107094,85.369347
C118.506584,85.459900 114.178413,84.281853 113.566772,82.608528
C112.453461,79.562759 113.105225,75.609261 113.953552,72.271683
C114.202606,71.291862 117.662697,70.489273 119.543953,70.677361
C126.519997,71.374840 128.045013,70.437523 128.920120,63.351006
C129.409134,59.391159 129.906189,55.375004 129.753250,51.409149
C129.597000,47.358112 131.461014,46.083595 135.023239,46.091145
C149.177261,46.121143 163.331711,46.091091 177.485046,46.202831
C181.806122,46.236942 186.124878,46.848717 190.443375,46.833015
C193.737167,46.821045 194.780701,48.197872 194.761490,51.321419
C194.644714,70.297173 194.604736,89.273628 194.623550,108.249763
C194.626556,111.290581 193.497421,112.883759 190.370239,113.039795
C185.769730,113.269371 181.175064,113.615845 176.515991,113.916504
C176.067856,110.403778 175.695831,107.487671 175.227921,104.177216
z'
/>
<path
fill='none'
opacity='1.000000'
stroke='#D34C3F'
strokeWidth='3'
d='
M50.105820,123.283722
C49.446377,118.335457 51.507248,116.708862 55.800720,117.315323
C56.947235,117.477257 58.131813,117.351044 59.287899,117.466980
C66.485611,118.188820 66.484573,118.199181 66.815964,125.325493
C67.086052,131.133224 70.123749,135.254395 75.823532,136.356491
C79.501442,137.067627 83.468414,137.393829 87.137741,136.819183
C96.077385,135.419113 98.915398,131.689316 99.034004,122.614326
C99.049049,121.462982 99.141937,120.312668 99.252129,118.081146
C104.413986,118.854897 109.407043,119.452690 114.306252,120.492638
C115.082245,120.657356 115.870766,122.802582 115.908310,124.054474
C116.072845,129.542297 115.780586,135.045670 116.000862,140.529480
C116.259544,146.969559 118.988312,148.754761 125.135185,146.911957
C125.611229,146.769257 126.067261,146.385193 126.527672,146.393295
C129.501572,146.445511 134.450867,145.616928 135.057983,146.868820
C136.576385,149.999817 136.445145,154.101822 136.264572,157.779327
C136.211853,158.853119 133.890060,160.317688 132.384796,160.683578
C130.718765,161.088547 128.593277,160.886642 127.020248,160.172668
C119.485802,156.752869 116.150414,159.604889 116.043648,168.346161
C115.994858,172.340454 115.935165,176.339752 116.079460,180.329590
C116.186417,183.286621 115.011475,184.668732 112.028908,184.586304
C92.579651,184.048767 73.130913,183.482407 53.679199,183.052307
C49.682011,182.963913 49.724800,180.499878 49.747101,177.671799
C49.888828,159.697952 49.990089,141.723785 50.105820,123.283722
z'
/>
<path
fill='none'
opacity='1.000000'
stroke='#D34C3F'
strokeWidth='3'
d='
M194.723312,147.964569
C194.537933,158.780350 194.225342,169.102219 194.235001,179.423798
C194.238464,183.124954 192.694077,184.575500 189.233154,184.574066
C169.249252,184.565826 149.265366,184.561676 129.281464,184.573456
C126.018867,184.575378 124.345184,183.097198 124.389984,179.744781
C124.438629,176.104736 124.401016,172.463516 124.401016,168.434631
C130.598907,169.013229 137.266098,170.849335 141.204636,165.186859
C143.550781,161.813782 144.630615,156.991394 144.781631,152.766998
C145.227814,140.286194 142.195648,138.207077 124.468094,137.593765
C124.468094,132.561798 124.468094,127.518242 124.468094,121.941299
C131.340836,122.364502 137.750290,122.964813 144.169769,123.104790
C153.109619,123.299713 153.965073,122.031258 152.294373,113.268944
C151.757919,110.455353 150.292984,106.436058 154.185806,105.523117
C158.118408,104.600838 162.521988,105.233223 166.609360,105.879517
C167.275787,105.984894 167.652100,109.685745 167.650467,111.724358
C167.649048,113.504562 166.780792,115.265877 166.553665,117.068069
C166.142395,120.331360 167.430191,122.045952 171.010101,121.986786
C178.618958,121.861038 186.231369,121.948395 194.723343,121.948395
C194.723343,129.985031 194.723343,138.727798 194.723312,147.964569
z'
/>
</svg>
)
}

View File

@@ -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,

View File

@@ -1,5 +1,5 @@
---
title: CalCom
title: Cal Com
description: Manage Cal.com bookings, event types, schedules, and availability
---

View 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="#E0E0E0"
/>
{/* 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\(\)\) |

View File

@@ -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 organizations 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.

View 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. Requires either path coordinates or placeIds.
#### 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 \(required if placeIds not provided\) |
| `placeIds` | array | No | Array of Place IDs for road segments \(required if path not provided\) |
#### 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 |

View File

@@ -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",

View File

@@ -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`

View File

@@ -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 |

View File

@@ -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,

View 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: '#E0E0E0',
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' },
},
}

View 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' },
},
}

View File

@@ -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,

View File

@@ -5170,3 +5170,254 @@ export function CalComIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}
export function GoogleMapsIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 92.3 132.3'>
<path
fill='#1a73e8'
d='M60.2 2.2C55.8.8 51 0 46.1 0 32 0 19.3 6.4 10.8 16.5l21.8 18.3L60.2 2.2z'
/>
<path
fill='#ea4335'
d='M10.8 16.5C4.1 24.5 0 34.9 0 46.1c0 8.7 1.7 15.7 4.6 22l28-33.3-21.8-18.3z'
/>
<path
fill='#4285f4'
d='M46.2 28.5c9.8 0 17.7 7.9 17.7 17.7 0 4.3-1.6 8.3-4.2 11.4 0 0 13.9-16.6 27.5-32.7-5.6-10.8-15.3-19-27-22.7L32.6 34.8c3.3-3.8 8.1-6.3 13.6-6.3'
/>
<path
fill='#fbbc04'
d='M46.2 63.8c-9.8 0-17.7-7.9-17.7-17.7 0-4.3 1.5-8.3 4.1-11.3l-28 33.3c4.8 10.6 12.8 19.2 21 29.9l34.1-40.5c-3.3 3.9-8.1 6.3-13.5 6.3'
/>
<path
fill='#34a853'
d='M59.1 109.2c15.4-24.1 33.3-35 33.3-63 0-7.7-1.9-14.9-5.2-21.3L25.6 98c2.6 3.4 5.3 7.3 7.9 11.3 9.4 14.5 6.8 23.1 12.8 23.1s3.4-8.7 12.8-23.2'
/>
</svg>
)
}
export function DsPyIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='30 28 185 175' fill='none'>
<path
fill='#D34C3F'
opacity='1.000000'
stroke='#D34C3F'
strokeWidth='3'
d='
M202.406967,127.017563
C202.179611,146.161911 202.031097,164.809784 201.685379,183.454010
C201.545227,191.012054 200.718399,191.828491 193.289062,191.886826
C172.464081,192.050339 151.635834,192.253342 130.812500,192.056702
C104.669151,191.809830 78.528122,191.241791 52.388767,190.686325
C42.707352,190.480606 42.115833,189.780975 42.157444,180.326309
C42.331207,140.845123 42.538979,101.364082 42.762138,61.883144
C42.787552,57.387417 42.989182,52.892700 43.107479,48.397476
C43.314110,40.545937 44.093277,39.498543 52.078091,39.155457
C62.393070,38.712250 72.725296,38.528290 83.050827,38.499870
C112.706757,38.418251 142.363419,38.390633 172.018936,38.527496
C179.500366,38.562023 186.991394,39.135315 194.454422,39.748272
C201.079926,40.292439 202.319077,41.541882 202.351517,48.097012
C202.453690,68.749702 202.355896,89.403366 202.347412,110.056648
C202.345154,115.544296 202.385666,121.031960 202.406967,127.017563
M105.672386,111.007179
C108.474365,111.345215 111.276726,111.921921 114.078171,111.917526
C115.417778,111.915421 117.727127,111.041771 117.934349,110.168335
C119.145897,105.061768 119.865135,99.838402 120.740562,94.712914
C106.966454,90.837227 103.521248,87.187225 104.005447,77.347023
C104.501411,67.267906 107.785126,64.137291 120.608673,61.878979
C120.934212,58.412746 121.138908,54.797504 121.647324,51.225491
C122.151093,47.686234 120.697983,46.383198 117.276314,46.409954
C96.324112,46.573811 75.371307,46.658936 54.418976,46.809055
C53.319950,46.816925 52.223454,47.178463 50.130283,47.553909
C50.130283,67.101257 50.146877,86.540329 50.112003,105.979309
C50.106895,108.825279 51.497246,109.676353 54.153763,109.668755
C59.309399,109.654015 64.480812,109.664360 69.617935,110.041916
C76.186432,110.524658 77.751175,112.890678 76.103035,119.056091
C73.827133,127.569878 75.807640,129.900314 84.675858,129.143616
C89.877007,128.699814 91.632874,126.489716 90.924309,121.176811
C90.726822,119.696030 90.323967,118.243813 90.082695,116.767479
C89.404724,112.619064 91.327385,110.218483 95.374054,110.068687
C98.493156,109.953224 101.640015,110.587189 105.672386,111.007179
M175.323807,104.571556
C175.695831,107.487671 176.067856,110.403778 176.515991,113.916504
C181.175064,113.615845 185.769730,113.269371 190.370239,113.039795
C193.497421,112.883759 194.626556,111.290581 194.623550,108.249763
C194.604736,89.273628 194.644714,70.297173 194.761490,51.321419
C194.780701,48.197872 193.737167,46.821045 190.443375,46.833015
C186.124878,46.848717 181.806122,46.236942 177.485046,46.202831
C163.331711,46.091091 149.177261,46.121143 135.023239,46.091145
C131.461014,46.083595 129.597000,47.358112 129.753250,51.409149
C129.906189,55.375004 129.409134,59.391159 128.920120,63.351006
C128.045013,70.437523 126.519997,71.374840 119.543953,70.677361
C117.662697,70.489273 114.202606,71.291862 113.953552,72.271683
C113.105225,75.609261 112.453461,79.562759 113.566772,82.608528
C114.178413,84.281853 118.506584,85.459900 121.107094,85.369347
C127.143333,85.159134 129.004593,86.451027 128.853668,92.489960
C128.820602,93.813370 128.587814,95.134392 128.406998,96.451027
C127.646179,101.991020 126.870201,107.528931 126.001221,113.776062
C132.081665,114.221489 137.732315,114.635429 142.703461,114.999588
C143.800079,109.887177 143.707199,105.333397 145.692795,102.028297
C150.972488,93.240074 170.332077,94.650421 175.323807,104.571556
M50.104687,123.749725
C49.990089,141.723785 49.888828,159.697952 49.747101,177.671799
C49.724800,180.499878 49.682011,182.963913 53.679199,183.052307
C73.130913,183.482407 92.579651,184.048767 112.028908,184.586304
C115.011475,184.668732 116.186417,183.286621 116.079460,180.329590
C115.935165,176.339752 115.994858,172.340454 116.043648,168.346161
C116.150414,159.604889 119.485802,156.752869 127.020248,160.172668
C128.593277,160.886642 130.718765,161.088547 132.384796,160.683578
C133.890060,160.317688 136.211853,158.853119 136.264572,157.779327
C136.445145,154.101822 136.576385,149.999817 135.057983,146.868820
C134.450867,145.616928 129.501572,146.445511 126.527672,146.393295
C126.067261,146.385193 125.611229,146.769257 125.135185,146.911957
C118.988312,148.754761 116.259544,146.969559 116.000862,140.529480
C115.780586,135.045670 116.072845,129.542297 115.908310,124.054474
C115.870766,122.802582 115.082245,120.657356 114.306252,120.492638
C109.407043,119.452690 104.413986,118.854897 99.252129,118.081146
C99.141937,120.312668 99.049049,121.462982 99.034004,122.614326
C98.915398,131.689316 96.077385,135.419113 87.137741,136.819183
C83.468414,137.393829 79.501442,137.067627 75.823532,136.356491
C70.123749,135.254395 67.086052,131.133224 66.815964,125.325493
C66.484573,118.199181 66.485611,118.188820 59.287899,117.466980
C58.131813,117.351044 56.947235,117.477257 55.800720,117.315323
C51.507248,116.708862 49.446377,118.335457 50.104687,123.749725
M194.723343,147.470551
C194.723343,138.727798 194.723343,129.985031 194.723343,121.948395
C186.231369,121.948395 178.618958,121.861038 171.010101,121.986786
C167.430191,122.045952 166.142395,120.331360 166.553665,117.068069
C166.780792,115.265877 167.649048,113.504562 167.650467,111.724358
C167.652100,109.685745 167.275787,105.984894 166.609360,105.879517
C162.521988,105.233223 158.118408,104.600838 154.185806,105.523117
C150.292984,106.436058 151.757919,110.455353 152.294373,113.268944
C153.965073,122.031258 153.109619,123.299713 144.169769,123.104790
C137.750290,122.964813 131.340836,122.364502 124.468094,121.941299
C124.468094,127.518242 124.468094,132.561798 124.468094,137.593765
C142.195648,138.207077 145.227814,140.286194 144.781631,152.766998
C144.630615,156.991394 143.550781,161.813782 141.204636,165.186859
C137.266098,170.849335 130.598907,169.013229 124.401016,168.434631
C124.401016,172.463516 124.438629,176.104736 124.389984,179.744781
C124.345184,183.097198 126.018867,184.575378 129.281464,184.573456
C149.265366,184.561676 169.249252,184.565826 189.233154,184.574066
C192.694077,184.575500 194.238464,183.124954 194.235001,179.423798
C194.225342,169.102219 194.537933,158.780350 194.723343,147.470551
z'
/>
<path
fill='none'
opacity='1.000000'
stroke='#D34C3F'
strokeWidth='3'
d='
M105.223457,110.947556
C101.640015,110.587189 98.493156,109.953224 95.374054,110.068687
C91.327385,110.218483 89.404724,112.619064 90.082695,116.767479
C90.323967,118.243813 90.726822,119.696030 90.924309,121.176811
C91.632874,126.489716 89.877007,128.699814 84.675858,129.143616
C75.807640,129.900314 73.827133,127.569878 76.103035,119.056091
C77.751175,112.890678 76.186432,110.524658 69.617935,110.041916
C64.480812,109.664360 59.309399,109.654015 54.153763,109.668755
C51.497246,109.676353 50.106895,108.825279 50.112003,105.979309
C50.146877,86.540329 50.130283,67.101257 50.130283,47.553909
C52.223454,47.178463 53.319950,46.816925 54.418976,46.809055
C75.371307,46.658936 96.324112,46.573811 117.276314,46.409954
C120.697983,46.383198 122.151093,47.686234 121.647324,51.225491
C121.138908,54.797504 120.934212,58.412746 120.608673,61.878979
C107.785126,64.137291 104.501411,67.267906 104.005447,77.347023
C103.521248,87.187225 106.966454,90.837227 120.740562,94.712914
C119.865135,99.838402 119.145897,105.061768 117.934349,110.168335
C117.727127,111.041771 115.417778,111.915421 114.078171,111.917526
C111.276726,111.921921 108.474365,111.345215 105.223457,110.947556
z'
/>
<path
fill='none'
opacity='1.000000'
stroke='#D34C3F'
strokeWidth='3'
d='
M175.227921,104.177216
C170.332077,94.650421 150.972488,93.240074 145.692795,102.028297
C143.707199,105.333397 143.800079,109.887177 142.703461,114.999588
C137.732315,114.635429 132.081665,114.221489 126.001221,113.776062
C126.870201,107.528931 127.646179,101.991020 128.406998,96.451027
C128.587814,95.134392 128.820602,93.813370 128.853668,92.489960
C129.004593,86.451027 127.143333,85.159134 121.107094,85.369347
C118.506584,85.459900 114.178413,84.281853 113.566772,82.608528
C112.453461,79.562759 113.105225,75.609261 113.953552,72.271683
C114.202606,71.291862 117.662697,70.489273 119.543953,70.677361
C126.519997,71.374840 128.045013,70.437523 128.920120,63.351006
C129.409134,59.391159 129.906189,55.375004 129.753250,51.409149
C129.597000,47.358112 131.461014,46.083595 135.023239,46.091145
C149.177261,46.121143 163.331711,46.091091 177.485046,46.202831
C181.806122,46.236942 186.124878,46.848717 190.443375,46.833015
C193.737167,46.821045 194.780701,48.197872 194.761490,51.321419
C194.644714,70.297173 194.604736,89.273628 194.623550,108.249763
C194.626556,111.290581 193.497421,112.883759 190.370239,113.039795
C185.769730,113.269371 181.175064,113.615845 176.515991,113.916504
C176.067856,110.403778 175.695831,107.487671 175.227921,104.177216
z'
/>
<path
fill='none'
opacity='1.000000'
stroke='#D34C3F'
strokeWidth='3'
d='
M50.105820,123.283722
C49.446377,118.335457 51.507248,116.708862 55.800720,117.315323
C56.947235,117.477257 58.131813,117.351044 59.287899,117.466980
C66.485611,118.188820 66.484573,118.199181 66.815964,125.325493
C67.086052,131.133224 70.123749,135.254395 75.823532,136.356491
C79.501442,137.067627 83.468414,137.393829 87.137741,136.819183
C96.077385,135.419113 98.915398,131.689316 99.034004,122.614326
C99.049049,121.462982 99.141937,120.312668 99.252129,118.081146
C104.413986,118.854897 109.407043,119.452690 114.306252,120.492638
C115.082245,120.657356 115.870766,122.802582 115.908310,124.054474
C116.072845,129.542297 115.780586,135.045670 116.000862,140.529480
C116.259544,146.969559 118.988312,148.754761 125.135185,146.911957
C125.611229,146.769257 126.067261,146.385193 126.527672,146.393295
C129.501572,146.445511 134.450867,145.616928 135.057983,146.868820
C136.576385,149.999817 136.445145,154.101822 136.264572,157.779327
C136.211853,158.853119 133.890060,160.317688 132.384796,160.683578
C130.718765,161.088547 128.593277,160.886642 127.020248,160.172668
C119.485802,156.752869 116.150414,159.604889 116.043648,168.346161
C115.994858,172.340454 115.935165,176.339752 116.079460,180.329590
C116.186417,183.286621 115.011475,184.668732 112.028908,184.586304
C92.579651,184.048767 73.130913,183.482407 53.679199,183.052307
C49.682011,182.963913 49.724800,180.499878 49.747101,177.671799
C49.888828,159.697952 49.990089,141.723785 50.105820,123.283722
z'
/>
<path
fill='none'
opacity='1.000000'
stroke='#D34C3F'
strokeWidth='3'
d='
M194.723312,147.964569
C194.537933,158.780350 194.225342,169.102219 194.235001,179.423798
C194.238464,183.124954 192.694077,184.575500 189.233154,184.574066
C169.249252,184.565826 149.265366,184.561676 129.281464,184.573456
C126.018867,184.575378 124.345184,183.097198 124.389984,179.744781
C124.438629,176.104736 124.401016,172.463516 124.401016,168.434631
C130.598907,169.013229 137.266098,170.849335 141.204636,165.186859
C143.550781,161.813782 144.630615,156.991394 144.781631,152.766998
C145.227814,140.286194 142.195648,138.207077 124.468094,137.593765
C124.468094,132.561798 124.468094,127.518242 124.468094,121.941299
C131.340836,122.364502 137.750290,122.964813 144.169769,123.104790
C153.109619,123.299713 153.965073,122.031258 152.294373,113.268944
C151.757919,110.455353 150.292984,106.436058 154.185806,105.523117
C158.118408,104.600838 162.521988,105.233223 166.609360,105.879517
C167.275787,105.984894 167.652100,109.685745 167.650467,111.724358
C167.649048,113.504562 166.780792,115.265877 166.553665,117.068069
C166.142395,120.331360 167.430191,122.045952 171.010101,121.986786
C178.618958,121.861038 186.231369,121.948395 194.723343,121.948395
C194.723343,129.985031 194.723343,138.727798 194.723312,147.964569
z'
/>
</svg>
)
}

View 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())',
},
},
}

View 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'

View 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())',
},
},
}

View 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())',
},
},
}

View 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>
}
}

View 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
}

View 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',
},
},
}

View 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',
},
},
}

View 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)',
},
},
},
},
},
},
},
},
}

View 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',
},
},
}

View 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.)',
},
},
}

View 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',
},
},
}

View 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'

View 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,
},
},
}

View 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,
},
},
}

View 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',
},
},
},
}

View 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)',
},
},
}

View File

@@ -0,0 +1,141 @@
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. Requires either path coordinates or placeIds.',
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 (required if placeIds not provided)',
},
placeIds: {
type: 'array',
required: false,
visibility: 'user-or-llm',
description: 'Array of Place IDs for road segments (required if path not provided)',
},
},
request: {
url: (params) => {
const hasPath = params.path && params.path.trim().length > 0
const hasPlaceIds = params.placeIds && params.placeIds.length > 0
if (!hasPath && !hasPlaceIds) {
throw new Error(
'Speed Limits requires either a path (coordinates) or placeIds. Please provide at least one.'
)
}
const url = new URL('https://roads.googleapis.com/v1/speedLimits')
url.searchParams.set('key', params.apiKey.trim())
if (hasPath) {
url.searchParams.set('path', params.path!.trim())
}
if (hasPlaceIds) {
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' },
},
},
},
},
}

View 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)',
},
},
}

View 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
}
}

View 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',
},
},
}

View File

@@ -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,