File size: 17,317 Bytes
b19cd83
 
 
 
 
 
 
 
 
 
af57c82
b19cd83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7648ae7
b19cd83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7648ae7
b19cd83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7648ae7
b19cd83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7648ae7
b19cd83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7648ae7
b19cd83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7648ae7
b19cd83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7648ae7
b19cd83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7648ae7
b19cd83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7648ae7
b19cd83
 
 
 
 
 
 
 
 
 
 
 
 
7648ae7
b19cd83
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
import re
import uuid
from typing import Any, Dict, Union, Optional

from langchain_core.callbacks import Callbacks
from langchain_core.runnables import RunnableConfig
from openai import OpenAI

import requests
from bs4 import BeautifulSoup
from config import selected_language
from serpapi import GoogleSearch
from langchain.tools import BaseTool
import json


def DailyNews():
    dailynews = ""
    url = "https://v.api.aa1.cn/api/zhihu-news/"
    result = requests.get(url, verify=False).json()
    for index in result['news']:
        dailynews = dailynews + index['title'] + "\n"
        # print(index['title'])
    return dailynews


# selected_language = "ch"


class GetLocation(BaseTool):
    # global selected_language
    # language: str = selected_language
    name: str = "获取ip地址"
    description: str = (
        "获取本机IP归属地的工具,输入参数是本机IP地址"
        if selected_language == "ch"
        else "A tool to get the location of the local IP address. The input parameter is the local IP address."
    )

    def _run(self, ip_address: str) -> str:
        api_key = "f962beaf785545d0a90b76d105996cdb"
        url = f"https://ipgeolocation.abstractapi.com/v1/?api_key={api_key}&ip_address={ip_address}"
        try:
            response = requests.get(url)
            response.raise_for_status()
            return response.text
        except requests.exceptions.RequestException as e:
            return f"请求失败: {e}"


class CurrencyConversion(BaseTool):
    name: str = "货币换算"
    description: str =(
        "获取指定基准货币和目标货币之间的实时汇率。输入参数包括基准货币代码和目标货币代码。传参格式:'USD|EUR'(第一个参数为基准货币代码,第二个参数为目标货币代码)。"
        if selected_language == "ch"
        else "Get the real-time exchange rate between the specified benchmark currency and target currency. The input parameters include the benchmark currency code and the target currency code. Parameter transmission format: USD | EUR '(the first parameter is the base currency code, and the second parameter is the target currency code)."
    )

    def _run(self, _input: str) -> str:
        pattern = r"(?P<base_currency>\S+)\|(?P<target_currency>\S+)"
        match = re.match(pattern, _input)
        base = match.group("base_currency")
        target = match.group("target_currency")
        api_key = "3599c1514c554f6da2ae8916151e5770"
        url = f"https://exchange-rates.abstractapi.com/v1/live/?api_key={api_key}&base={base}&target={target}"
        try:
            response = requests.get(url)
            response.raise_for_status()
            return response.content
        except requests.exceptions.RequestException as e:
            return f"请求失败: {e}"


class GetHoliday(BaseTool):
    name: str = "获取假日信息"
    description: str = (
        "获取指定国家在某一天的假期信息。输入参数包括国家代码和日期(年、月、日)。传参格式:'US|2025|1|1'(第一个参数为国家代码,后续三个参数为年份、月份和日期)。"if selected_language == "ch"
        else "Retrieve holiday information for a specified country on a certain day. The input parameters include country code and date (year, month, day). Parameter transmission format: US | 2025 | 1 | 1 '(The first parameter is the country code, and the following three parameters are the year, month, and date)."
    )

    def _run(self, _input: str) -> str:

        pattern = r"(?P<country>[A-Za-z]{2,3})\|(?P<year>\d{4})\|(?P<month>\d{1,2})\|(?P<day>\d{1,2})"
        match = re.match(pattern, _input)
        country = match.group("country")
        year = match.group("year")
        month = match.group("month")
        day = match.group("day")
        base_url = "https://holidays.abstractapi.com/v1/"
        api_key = "fa1dbc3bf696406db3412a7d034ba531"
        params = {"api_key": api_key, "country": country}
        if year:
            params["year"] = year
        if month:
            params["month"] = month
        if day:
            params["day"] = day
        query_string = "&".join([f"{key}={value}" for key, value in params.items() if value is not None])
        url = f"{base_url}?{query_string}"
        try:
            response = requests.get(url)
            response.raise_for_status()  # 检查 HTTP 响应状态
            data = response.json()
            if data:
                holidays = "\n".join(
                    [f"{holiday['name']} - {holiday['type']}" for holiday in data]
                )
                return f"查询日期的节日信息:\n{holidays}"
            else:
                return f"{year}-{month}-{day}{country} 没有节日。"
        except requests.exceptions.RequestException as e:
            return f"请求失败: {e}"


def holiday2(country, year):
    url = f"https://calendarific.com/api/v2/holidays?&api_key=O7fuldKDDJgpl9ckui8lduMWNjx4bCDf&country={country}&year={year}"
    response = requests.get(url).json()
    print(response)


class SafeExpressionEvaluator(BaseTool):
    name: str = "safe expression evaluator"
    description: str = (
        "安全地计算 Python 表达式,输入参数为表达式字符串。"
        if selected_language == "ch"
        else "Safely evaluate Python expressions, with input being an expression string."
    )
    safe_globals: dict = None  # 声明类型
    safe_locals: dict = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        # 定义安全的执行环境
        self.safe_globals = {
            "__builtins__": {
                "abs": abs,
                "min": min,
                "max": max,
                "sum": sum,
                "len": len,
                "round": round,
                "pow": pow,
                "divmod": divmod,
            },
            "math":__import__("math"),
        }
        self.safe_locals = {}

    def _run(self, expression: str) -> Dict[str, Any]:
        """安全地计算一个表达式,并返回结果"""
        try:
            result = eval(expression, self.safe_globals, self.safe_locals)
            return {"result": result, "error": None}
        except Exception as e:
            return {"result": None, "error": str(e)}


class SafeCodeExecutor(BaseTool):
    name: str = "safe code executor"
    description: str = (
        "安全地执行 Python 代码块,输入参数为代码字符串。"
        if selected_language == "ch"
        else "Safely execute Python code blocks, with input being a code string."
    )
    safe_globals: dict = None  # 声明类型
    safe_locals: dict = None

    def __init__(self, **kwargs):
        super().__init__()
        self.safe_globals = {
            "__builtins__": {
                "abs": abs,
                "min": min,
                "max": max,
                "sum": sum,
                "len": len,
                "round": round,
                "pow": pow,
                "divmod": divmod,
                "print" : print
                # 添加更多安全函数
            },
            "math": __import__("math"),
        }
        self.safe_locals = {}

    def _run(self, code: str) -> Dict[str, Any]:
        """安全地执行一段 Python 代码(包括函数、类定义等),无返回值"""
        try:
            exec(code, self.safe_globals, self.safe_locals)
            return {"result": "Code executed successfully.", "error": None}
        except Exception as e:
            return {"result": None, "error": str(e)}

def weather(city):
    """
    高德天气,查询当天的天气
    """
    url = f"https://restapi.amap.com/v3/weather/weatherInfo?city={city}&key=e7ff59e1de1573456145f0d736cc3a2f"
    response = requests.get(url).json()
    print(response)


class WeatherCrossing(BaseTool):
    name: str = "天气查询"
    description: str = "使用 Visual Crossing API 查询指定城市的天气信息,输入参数包括城市名称,以及可选的开始日期和结束日期(格式为 yyyy-MM-dd)。" if selected_language == "ch" else "Use the Visual Crossing API to query weather information for a specified city, with input parameters including the city name and optional start and end dates (in the format yyyy MM dd)."

    def _run(self, city: str, date1: str = None, date2: str = None) -> dict:
        base_url = "https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline"
        api_key = "6LX8E4LWX7ZFSCCA5S3JLFVPS"
        if date1 and date2:
            url = f"{base_url}/{city}/{date1}/{date2}?key={api_key}"
        elif date1:
            url = f"{base_url}/{city}/{date1}?key={api_key}"
        else:
            url = f"{base_url}/{city}?key={api_key}"
        try:
            response = requests.get(url)
            response.raise_for_status()  # 检查 HTTP 响应状态
            data = response.json()
            return {"result": data, "error": None}
        except requests.exceptions.RequestException as e:
            return {"result": None, "error": f"请求失败: {e}"}
        except ValueError:
            return {"result": None, "error": "无法解析返回的 JSON 数据。"}


class RegionInquiryTool(BaseTool):
    name: str = "行政区查询"  # 添加类型注解
    description: str = ("获取指定行政区的信息,包括名称、描述(行政级别)。根据 subdistrict 参数,返回该行政区的下级行政区信息。输入参数包括行政区名称和下级行政区级数(0 - 3)。传参格式:'北京|1'(第一个参数为行政区名称,第二个参数为下级行政区级数)。 "
        if selected_language == "ch"
        else "Retrieve information about the designated administrative region, including its name and description (administrative level). According to the subdistrict parameter, return the information of the subordinate administrative regions of the administrative region. The input parameters include the name of the administrative region and the level of the subordinate administrative region (0-3). Parameter format: 'Beijing | 1' (the first parameter is the name of the administrative region, and the second parameter is the level of the subordinate administrative region)")

    def _run(self, _input: str) -> Dict[str, Any]:

        pattern = r"(?P<district>.+?)\|(?P<level>\d{1,3})"
        match = re.match(pattern, _input)
        district = match.group("district")
        level = match.group("level")
        # keywords = kwargs.get("keywords")
        # subdistrict = kwargs.get("subdistrict", 0)
        api_key = "e7ff59e1de1573456145f0d736cc3a2f"
        base_url = "https://restapi.amap.com/v3/config/district"
        params = {
            "keywords": district,
            "subdistrict": level,
            "key": api_key
        }
        try:
            response = requests.get(base_url, params=params)
            response.raise_for_status()
            return response.json()
        except requests.RequestException as e:
            return {"error": str(e)}


class HTMLTextExtractor(BaseTool):
    name: str = "html提取"
    description: str = "提取 HTML 文档中的纯文本内容,输入参数为 HTML 文档的字符串内容,返回去除标签后的纯文本。" if selected_language == "ch" else  "Extract plain text content from an HTML document, input the string content of the HTML document as the parameter, and return the plain text after removing tags."

    def _run(self, html_content: str) -> dict:
        if not html_content:
            return {"result": None, "error": "HTML 内容不能为空。"}

        try:
            soup = BeautifulSoup(html_content, 'lxml')
            text = soup.get_text()
            return {"result": text.strip(), "error": None}
        except Exception as e:
            return {"result": None, "error": f"提取文本失败: {e}"}


class BaiduNewsSearchTool(BaseTool):
    name: str = "Baidu News Search"  # 添加类型注解
    description: str = "备用搜索引擎,当主要搜索引擎不返回结果时调用,输入是检索query" if selected_language == "ch" else "Backup search engine, called when the primary search engine does not return results, input is search query"# 添加类型注解

    def _run(self, query: str) -> str:
        """
        使用百度新闻引擎进行查询。
        """
        params = {
            "engine": "baidu_news",
            "q": query,
            "ct": "1",
            "api_key": "dcc98b22d5f7d413979a175ff7d75b721c5992a3ee1e2363020b2bbdf4f82404"
        }
        search = GoogleSearch(params)  # 使用GoogleSearch库进行查询
        results = search.get_dict()
        organic_results = results.get("organic_results", [])

        if not organic_results:
            return "No results found."
        return organic_results


class BoChaSearchTool(BaseTool):
    name: str = "网页搜索"  # 添加类型注解
    description: str = "主力搜索引擎,当需要检索信息时调用,输入是检索query。" if selected_language == "ch" else "The main search engine is called when information needs to be retrieved, with the input being a search query." # 添加类型注解
    def _run(self, query: str) -> str:

        url = "https://api.bochaai.com/v1/web-search"
        payload = json.dumps({
            "query": query,
            "count": 3,
            "summary": True
        })
        headers = {
            'Authorization': 'Bearer sk-adeb0353f3434158934a841ac0f8d4a4',
            'Content-Type': 'application/json'
        }
        response = requests.request("POST", url, headers=headers, data=payload)
        print(response.json())
        return response.json()


class ImageSearchTool(BaseTool):
    name: str = "图片搜索"
    description: str = "搜索图片引擎,当你需要检索相关图片的时候调用,输入是检索query,输出是与query有关的图片的信息" if selected_language == "ch" else "Search image engine, called when you need to retrieve relevant images. The input is the search query, and the output is information about the images related to the query"

    def _run(self, query: str) -> str:
        params = {
            "engine": "google_images",
            "q": query,
            "gl": "cn",
        }
        search = GoogleSearch(params)
        results = search.get_dict()
        suggested_searches = results.get('suggested_searches', [])
        if suggested_searches:
            thumbnails = [search['thumbnail'] for search in suggested_searches][:3]
        else:
            thumbnails = []
        information = ""
        client = OpenAI()
        for idx, thumbnail in enumerate(thumbnails):
            response = client.chat.completions.create(
                model="gpt-4o-mini",
                messages=[
                    {
                        "role": "user",
                        "content": [
                            {"type": "text", "text": "What’s in this image?Give a brief answer"},
                            {
                                "type": "image_url",
                                "image_url": {
                                    "url": thumbnail,
                                },
                            },
                        ],
                    }
                ],
                max_tokens=100,
            )
            response_content = response.choices[0].message.content
            information = information + str(idx + 1) + ": " + response_content + "\n"
        return information


# tool = CurrencyConversion()
# print(tool.run("USD|EUR"))

class LocationSearch(BaseTool):
    name: str = "地点查询"
    description: str = (
        "搜索地点的工具,调用参数为一个关键词" if selected_language == "ch"
        else "A tool for searching locations, with a call parameter of a keyword"
    )

    def _run(self, keyword: str) -> str:
        key = "e7ff59e1de1573456145f0d736cc3a2f"
        url = f"https://restapi.amap.com/v3/place/text?keywords={keyword}&offset=20&page=1&key={key}"
        response = requests.get(url)
        return response.json()


class ReverseGeocodingTool(BaseTool):
    name: str = "经纬度获取"
    description: str = (
        "通过给定结构化地址来获取地址经纬度的工具,调用参数为结构化地址信息(规则遵循:国家、省份、城市、区县、城镇、乡村、街道、门牌号码、屋邨、大厦,如:北京市朝阳区阜通东大街6号。)"
        if selected_language == "ch"
        else "A tool that obtains the latitude and longitude of an address by giving it a structured address, with the parameters being structured address information (following the rules: country, province, city, district, town, countryside, street, house number, housing estate, building, such as No. 6 Futong East Street, Chaoyang District, Beijing)"
    )

    def _run(self, address: str) -> str:
        key = "e7ff59e1de1573456145f0d736cc3a2f"
        url = f"https://restapi.amap.com/v3/geocode/geo?address={address}&output=JSON&key={key}"
        response = requests.get(url)
        return response.json()['geocodes'][0]['location']