From 850c2273eec189f6b0243636a46e2802c3852d76 Mon Sep 17 00:00:00 2001 From: Charles Zhou Date: Mon, 1 Jul 2024 03:34:32 -0500 Subject: [PATCH] feat: Nominatim OpenStreetMap search tool (#5789) --- api/core/tools/provider/_position.yaml | 1 + .../builtin/nominatim/_assets/icon.svg | 277 ++++++++++++++++++ .../provider/builtin/nominatim/nominatim.py | 23 ++ .../provider/builtin/nominatim/nominatim.yaml | 43 +++ .../nominatim/tools/nominatim_lookup.py | 47 +++ .../nominatim/tools/nominatim_lookup.yaml | 31 ++ .../nominatim/tools/nominatim_reverse.py | 49 ++++ .../nominatim/tools/nominatim_reverse.yaml | 47 +++ .../nominatim/tools/nominatim_search.py | 49 ++++ .../nominatim/tools/nominatim_search.yaml | 51 ++++ 10 files changed, 618 insertions(+) create mode 100644 api/core/tools/provider/builtin/nominatim/_assets/icon.svg create mode 100644 api/core/tools/provider/builtin/nominatim/nominatim.py create mode 100644 api/core/tools/provider/builtin/nominatim/nominatim.yaml create mode 100644 api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.py create mode 100644 api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.yaml create mode 100644 api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.py create mode 100644 api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.yaml create mode 100644 api/core/tools/provider/builtin/nominatim/tools/nominatim_search.py create mode 100644 api/core/tools/provider/builtin/nominatim/tools/nominatim_search.yaml diff --git a/api/core/tools/provider/_position.yaml b/api/core/tools/provider/_position.yaml index 74940e819f..fa13629ef7 100644 --- a/api/core/tools/provider/_position.yaml +++ b/api/core/tools/provider/_position.yaml @@ -7,6 +7,7 @@ - azuredalle - stability - wikipedia +- nominatim - yahoo - arxiv - pubmed diff --git a/api/core/tools/provider/builtin/nominatim/_assets/icon.svg b/api/core/tools/provider/builtin/nominatim/_assets/icon.svg new file mode 100644 index 0000000000..db5a4eb868 --- /dev/null +++ b/api/core/tools/provider/builtin/nominatim/_assets/icon.svg @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 010110010011010110010011 + 010110010011010110010011 + + \ No newline at end of file diff --git a/api/core/tools/provider/builtin/nominatim/nominatim.py b/api/core/tools/provider/builtin/nominatim/nominatim.py new file mode 100644 index 0000000000..b6f29b5feb --- /dev/null +++ b/api/core/tools/provider/builtin/nominatim/nominatim.py @@ -0,0 +1,23 @@ +from typing import Any + +from core.tools.errors import ToolProviderCredentialValidationError +from core.tools.provider.builtin.nominatim.tools.nominatim_search import NominatimSearchTool +from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController + + +class NominatimProvider(BuiltinToolProviderController): + def _validate_credentials(self, credentials: dict[str, Any]) -> None: + try: + result = NominatimSearchTool().fork_tool_runtime( + runtime={ + "credentials": credentials, + } + ).invoke( + user_id='', + tool_parameters={ + 'query': 'London', + 'limit': 1, + }, + ) + except Exception as e: + raise ToolProviderCredentialValidationError(str(e)) diff --git a/api/core/tools/provider/builtin/nominatim/nominatim.yaml b/api/core/tools/provider/builtin/nominatim/nominatim.yaml new file mode 100644 index 0000000000..7d014bd78c --- /dev/null +++ b/api/core/tools/provider/builtin/nominatim/nominatim.yaml @@ -0,0 +1,43 @@ +identity: + author: Charles Zhou + name: nominatim + label: + en_US: Nominatim + zh_Hans: Nominatim + de_DE: Nominatim + ja_JP: Nominatim + description: + en_US: Nominatim is a search engine for OpenStreetMap data + zh_Hans: Nominatim是OpenStreetMap数据的搜索引擎 + de_DE: Nominatim ist eine Suchmaschine für OpenStreetMap-Daten + ja_JP: NominatimはOpenStreetMapデータの検索エンジンです + icon: icon.svg + tags: + - search + - utilities +credentials_for_provider: + base_url: + type: text-input + required: false + default: https://nominatim.openstreetmap.org + label: + en_US: Nominatim Base URL + zh_Hans: Nominatim 基础 URL + de_DE: Nominatim Basis-URL + ja_JP: Nominatim ベースURL + placeholder: + en_US: "Enter your Nominatim instance URL (default: + https://nominatim.openstreetmap.org)" + zh_Hans: 输入您的Nominatim实例URL(默认:https://nominatim.openstreetmap.org) + de_DE: "Geben Sie Ihre Nominatim-Instanz-URL ein (Standard: + https://nominatim.openstreetmap.org)" + ja_JP: NominatimインスタンスのURLを入力してください(デフォルト:https://nominatim.openstreetmap.org) + help: + en_US: The base URL for the Nominatim instance. Use the default for the public + service or enter your self-hosted instance URL. + zh_Hans: Nominatim实例的基础URL。使用默认值可访问公共服务,或输入您的自托管实例URL。 + de_DE: Die Basis-URL für die Nominatim-Instanz. Verwenden Sie den Standardwert + für den öffentlichen Dienst oder geben Sie die URL Ihrer selbst + gehosteten Instanz ein. + ja_JP: NominatimインスタンスのベースURL。公共サービスにはデフォルトを使用するか、自己ホスティングインスタンスのURLを入力してください。 + url: https://nominatim.org/ diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.py b/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.py new file mode 100644 index 0000000000..e21ce14f54 --- /dev/null +++ b/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.py @@ -0,0 +1,47 @@ +import json +from typing import Any, Union + +import requests + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class NominatimLookupTool(BuiltinTool): + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + osm_ids = tool_parameters.get('osm_ids', '') + + if not osm_ids: + return self.create_text_message('Please provide OSM IDs') + + params = { + 'osm_ids': osm_ids, + 'format': 'json', + 'addressdetails': 1 + } + + return self._make_request(user_id, 'lookup', params) + + def _make_request(self, user_id: str, endpoint: str, params: dict) -> ToolInvokeMessage: + base_url = self.runtime.credentials.get('base_url', 'https://nominatim.openstreetmap.org') + + try: + headers = { + "User-Agent": "DifyNominatimTool/1.0" + } + s = requests.session() + response = s.request( + method='GET', + headers=headers, + url=f"{base_url}/{endpoint}", + params=params + ) + response_data = response.json() + + if response.status_code == 200: + s.close() + return self.create_text_message(self.summary(user_id=user_id, content=json.dumps(response_data, ensure_ascii=False))) + else: + return self.create_text_message(f"Error: {response.status_code} - {response.text}") + except Exception as e: + return self.create_text_message(f"An error occurred: {str(e)}") \ No newline at end of file diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.yaml b/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.yaml new file mode 100644 index 0000000000..508c4dcd88 --- /dev/null +++ b/api/core/tools/provider/builtin/nominatim/tools/nominatim_lookup.yaml @@ -0,0 +1,31 @@ +identity: + name: nominatim_lookup + author: Charles Zhou + label: + en_US: Nominatim OSM Lookup + zh_Hans: Nominatim OSM 对象查找 + de_DE: Nominatim OSM-Objektsuche + ja_JP: Nominatim OSM ルックアップ +description: + human: + en_US: Look up OSM objects using their IDs with Nominatim + zh_Hans: 使用Nominatim通过ID查找OSM对象 + de_DE: Suchen Sie OSM-Objekte anhand ihrer IDs mit Nominatim + ja_JP: Nominatimを使用してIDでOSMオブジェクトを検索 + llm: A tool for looking up OpenStreetMap objects using their IDs with Nominatim. +parameters: + - name: osm_ids + type: string + required: true + label: + en_US: OSM IDs + zh_Hans: OSM ID + de_DE: OSM-IDs + ja_JP: OSM ID + human_description: + en_US: Comma-separated list of OSM IDs to lookup (e.g., N123,W456,R789) + zh_Hans: 要查找的OSM ID的逗号分隔列表(例如:N123,W456,R789) + de_DE: Kommagetrennte Liste von OSM-IDs für die Suche (z.B. N123,W456,R789) + ja_JP: 検索するOSM IDのカンマ区切りリスト(例:N123,W456,R789) + llm_description: A comma-separated list of OSM IDs (prefixed with N, W, or R) for lookup. + form: llm diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.py b/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.py new file mode 100644 index 0000000000..438d5219e9 --- /dev/null +++ b/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.py @@ -0,0 +1,49 @@ +import json +from typing import Any, Union + +import requests + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class NominatimReverseTool(BuiltinTool): + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + lat = tool_parameters.get('lat') + lon = tool_parameters.get('lon') + + if lat is None or lon is None: + return self.create_text_message('Please provide both latitude and longitude') + + params = { + 'lat': lat, + 'lon': lon, + 'format': 'json', + 'addressdetails': 1 + } + + return self._make_request(user_id, 'reverse', params) + + def _make_request(self, user_id: str, endpoint: str, params: dict) -> ToolInvokeMessage: + base_url = self.runtime.credentials.get('base_url', 'https://nominatim.openstreetmap.org') + + try: + headers = { + "User-Agent": "DifyNominatimTool/1.0" + } + s = requests.session() + response = s.request( + method='GET', + headers=headers, + url=f"{base_url}/{endpoint}", + params=params + ) + response_data = response.json() + + if response.status_code == 200: + s.close() + return self.create_text_message(self.summary(user_id=user_id, content=json.dumps(response_data, ensure_ascii=False))) + else: + return self.create_text_message(f"Error: {response.status_code} - {response.text}") + except Exception as e: + return self.create_text_message(f"An error occurred: {str(e)}") \ No newline at end of file diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.yaml b/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.yaml new file mode 100644 index 0000000000..f1a2dd09fb --- /dev/null +++ b/api/core/tools/provider/builtin/nominatim/tools/nominatim_reverse.yaml @@ -0,0 +1,47 @@ +identity: + name: nominatim_reverse + author: Charles Zhou + label: + en_US: Nominatim Reverse Geocoding + zh_Hans: Nominatim 反向地理编码 + de_DE: Nominatim Rückwärts-Geocodierung + ja_JP: Nominatim リバースジオコーディング +description: + human: + en_US: Convert coordinates to addresses using Nominatim + zh_Hans: 使用Nominatim将坐标转换为地址 + de_DE: Konvertieren Sie Koordinaten in Adressen mit Nominatim + ja_JP: Nominatimを使用して座標を住所に変換 + llm: A tool for reverse geocoding using Nominatim, which can convert latitude + and longitude coordinates to an address. +parameters: + - name: lat + type: number + required: true + label: + en_US: Latitude + zh_Hans: 纬度 + de_DE: Breitengrad + ja_JP: 緯度 + human_description: + en_US: Latitude coordinate for reverse geocoding + zh_Hans: 用于反向地理编码的纬度坐标 + de_DE: Breitengrad-Koordinate für die Rückwärts-Geocodierung + ja_JP: リバースジオコーディングの緯度座標 + llm_description: The latitude coordinate for reverse geocoding. + form: llm + - name: lon + type: number + required: true + label: + en_US: Longitude + zh_Hans: 经度 + de_DE: Längengrad + ja_JP: 経度 + human_description: + en_US: Longitude coordinate for reverse geocoding + zh_Hans: 用于反向地理编码的经度坐标 + de_DE: Längengrad-Koordinate für die Rückwärts-Geocodierung + ja_JP: リバースジオコーディングの経度座標 + llm_description: The longitude coordinate for reverse geocoding. + form: llm diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.py b/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.py new file mode 100644 index 0000000000..983cbc0e34 --- /dev/null +++ b/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.py @@ -0,0 +1,49 @@ +import json +from typing import Any, Union + +import requests + +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class NominatimSearchTool(BuiltinTool): + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]: + query = tool_parameters.get('query', '') + limit = tool_parameters.get('limit', 10) + + if not query: + return self.create_text_message('Please input a search query') + + params = { + 'q': query, + 'format': 'json', + 'limit': limit, + 'addressdetails': 1 + } + + return self._make_request(user_id, 'search', params) + + def _make_request(self, user_id: str, endpoint: str, params: dict) -> ToolInvokeMessage: + base_url = self.runtime.credentials.get('base_url', 'https://nominatim.openstreetmap.org') + + try: + headers = { + "User-Agent": "DifyNominatimTool/1.0" + } + s = requests.session() + response = s.request( + method='GET', + headers=headers, + url=f"{base_url}/{endpoint}", + params=params + ) + response_data = response.json() + + if response.status_code == 200: + s.close() + return self.create_text_message(self.summary(user_id=user_id, content=json.dumps(response_data, ensure_ascii=False))) + else: + return self.create_text_message(f"Error: {response.status_code} - {response.text}") + except Exception as e: + return self.create_text_message(f"An error occurred: {str(e)}") \ No newline at end of file diff --git a/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.yaml b/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.yaml new file mode 100644 index 0000000000..e0c53c046a --- /dev/null +++ b/api/core/tools/provider/builtin/nominatim/tools/nominatim_search.yaml @@ -0,0 +1,51 @@ +identity: + name: nominatim_search + author: Charles Zhou + label: + en_US: Nominatim Search + zh_Hans: Nominatim 搜索 + de_DE: Nominatim Suche + ja_JP: Nominatim 検索 +description: + human: + en_US: Search for locations using Nominatim + zh_Hans: 使用Nominatim搜索位置 + de_DE: Suche nach Orten mit Nominatim + ja_JP: Nominatimを使用して場所を検索 + llm: A tool for geocoding using Nominatim, which can search for locations based + on addresses or place names. +parameters: + - name: query + type: string + required: true + label: + en_US: Search Query + zh_Hans: 搜索查询 + de_DE: Suchanfrage + ja_JP: 検索クエリ + human_description: + en_US: Enter an address or place name to search for + zh_Hans: 输入要搜索的地址或地名 + de_DE: Geben Sie eine Adresse oder einen Ortsnamen für die Suche ein + ja_JP: 検索する住所または場所の名前を入力してください + llm_description: The search query for Nominatim, which can be an address or place name. + form: llm + - name: limit + type: number + default: 10 + min: 1 + max: 40 + required: false + label: + en_US: Result Limit + zh_Hans: 结果限制 + de_DE: Ergebnislimit + ja_JP: 結果の制限 + human_description: + en_US: "Maximum number of results to return (default: 10, max: 40)" + zh_Hans: 要返回的最大结果数(默认:10,最大:40) + de_DE: "Maximale Anzahl der zurückzugebenden Ergebnisse (Standard: 10, max: 40)" + ja_JP: 返す結果の最大数(デフォルト:10、最大:40) + llm_description: Limit the number of returned results. The default is 10, and + the maximum is 40. + form: form