FaYo
model
d8d694f
import base64
from datetime import datetime
import hashlib
import json
import os
from typing import Optional, Type
import jionlp as jio
import requests
from lagent.actions.base_action import BaseAction, tool_api
from lagent.actions.parser import BaseParser, JsonParser
from lagent.schema import ActionReturn, ActionStatusCode
from utils.web_configs import WEB_CONFIGS
'''
class DeliveryTimeQueryAction(BaseAction):
"""快递时效查询插件,用于根据用户提出的收货地址查询到达期限"""
def __init__(
self,
# departure_place: str,
# delivery_company_name: str,
description: Optional[dict] = None,
parser: Type[BaseParser] = JsonParser,
enable: bool = True,
) -> None:
super().__init__(description, parser, enable)
self.departure_place = departure_place # 发货地
# 天气查询
self.weather_query_handler = WeatherQuery(departure_place, WEB_CONFIGS.AGENT_WEATHER_API_KEY)
self.delivery_time_handler = DeliveryTimeQuery(delivery_company_name, WEB_CONFIGS.AGENT_DELIVERY_TIME_API_KEY)
@tool_api
def run(self, query: str) -> ActionReturn:
"""一个到货时间查询API。可以根据城市名查询到货时间信息。
Args:
query (:class:`str`): 需要查询的城市名。
"""
# 获取文本中收货地,发货地后台设置
# 防止 LLM 将城市识别错误,进行兜底
city_info = jio.parse_location(query, town_village=True)
city_name = city_info["city"]
# 获取收货地代号 -> 天气
destination_weather = self.weather_query_handler(city_name)
# 获取发货地代号 -> 天气
departure_weather = self.weather_query_handler(self.departure_place)
# 获取到达时间
delivery_time = self.delivery_time_handler(self.departure_place, city_name)
final_str = (
f"今天日期:{datetime.now().strftime('%m月%d日')}\n"
f"收货地天气:{destination_weather.result[0]['content']}\n"
f"发货地天气:{departure_weather.result[0]['content']}\n"
f"物流信息:{delivery_time.result[0]['content']}\n"
"回答突出“预计送达时间”和“收货地天气”,如果收货地或者发货地遇到暴雨暴雪等极端天气,须告知用户快递到达时间会有所增加。"
)
tool_return = ActionReturn(type=self.name)
tool_return.result = [dict(type="text", content=final_str)]
return tool_return
'''
'''
class WeatherQuery:
"""快递时效查询插件,用于根据用户提出的收货地址查询到达期限"""
def __init__(
self,
departure_place: str,
api_key: Optional[str] = None,
) -> None:
self.departure_place = departure_place # 发货地
# 天气查询
# api_key = os.environ.get("WEATHER_API_KEY", key)
if api_key is None:
raise ValueError("Please set Weather API key either in the environment as WEATHER_API_KEY")
self.api_key = api_key
self.location_query_url = "https://geoapi.qweather.com/v2/city/lookup"
self.weather_query_url = "https://devapi.qweather.com/v7/weather/now"
def parse_results(self, city_name: str, results: dict) -> str:
"""解析 API 返回的信息
Args:
results (dict): JSON 格式的 API 报文。
Returns:
str: 解析后的结果。
"""
now = results["now"]
data = (
# f'数据观测时间: {now["obsTime"]};'
f"城市名: {city_name};"
f'温度: {now["temp"]}°C;'
f'体感温度: {now["feelsLike"]}°C;'
f'天气: {now["text"]};'
# f'风向: {now["windDir"]},角度为 {now["wind360"]}°;'
f'风力等级: {now["windScale"]},风速为 {now["windSpeed"]} km/h;'
f'相对湿度: {now["humidity"]};'
f'当前小时累计降水量: {now["precip"]} mm;'
# f'大气压强: {now["pressure"]} 百帕;'
f'能见度: {now["vis"]} km。'
)
return data
def __call__(self, query):
tool_return = ActionReturn()
status_code, response = self.search_weather_with_city(query)
if status_code == -1:
tool_return.errmsg = response
tool_return.state = ActionStatusCode.HTTP_ERROR
elif status_code == 200:
parsed_res = self.parse_results(query, response)
tool_return.result = [dict(type="text", content=str(parsed_res))]
tool_return.state = ActionStatusCode.SUCCESS
else:
tool_return.errmsg = str(status_code)
tool_return.state = ActionStatusCode.API_ERROR
return tool_return
def search_weather_with_city(self, query: str):
"""根据城市名获取城市代号,然后进行天气查询
Args:
query (str): 城市名
Returns:
int: 天气接口调用状态码
dict: 天气接口返回信息
"""
# 获取城市代号
try:
city_code_response = requests.get(self.location_query_url, params={"key": self.api_key, "location": query})
except Exception as e:
return -1, str(e)
if city_code_response.status_code != 200:
return city_code_response.status_code, city_code_response.json()
city_code_response = city_code_response.json()
if len(city_code_response["location"]) == 0:
return -1, "未查询到城市"
city_code = city_code_response["location"][0]["id"]
# 获取天气
try:
weather_response = requests.get(self.weather_query_url, params={"key": self.api_key, "location": city_code})
except Exception as e:
return -1, str(e)
return weather_response.status_code, weather_response.json()
'''
class DeliveryTimeQuery:
def __init__(
self,
delivery_company_name: Optional[str] = "中通",
api_key: Optional[str] = None,
) -> None:
# 快递时效查询
# api_key = os.environ.get("DELIVERY_TIME_API_KEY", key)
if api_key is None or "," not in api_key:
raise ValueError(
'Please set Delivery time API key either in the environment as DELIVERY_TIME_API_KEY="${e_business_id},${api_key}"'
)
self.e_business_id = api_key.split(",")[0]
self.api_key = api_key.split(",")[1]
self.api_url = "http://api.kdniao.com/api/dist" # 快递鸟
self.china_location = jio.china_location_loader()
# 快递鸟对应的
DELIVERY_COMPANY_MAP = {
"德邦": "DBL",
"邮政": "EMS",
"京东": "JD",
"极兔速递": "JTSD",
"顺丰": "SF",
"申通": "STO",
"韵达": "YD",
"圆通": "YTO",
"中通": "ZTO",
}
self.delivery_company_name = delivery_company_name
self.delivery_company_id = DELIVERY_COMPANY_MAP[delivery_company_name]
@staticmethod
def data_md5(n):
# md5加密
md5 = hashlib.md5()
md5.update(str(n).encode("utf-8"))
return md5.hexdigest()
def get_data_sign(self, n):
# 签名
md5Data = self.data_md5(json.dumps(n) + self.api_key)
res = str(base64.b64encode(md5Data.encode("utf-8")), "utf-8")
return res
def get_city_detail(self, name):
# 如果是城市名,使用第一个区名
city_info = jio.parse_location(name, town_village=True)
# china_location = jio.china_location_loader()
county_name = ""
for i in self.china_location[city_info["province"]][city_info["city"]].keys():
if "区" == i[-1]:
county_name = i
break
return {
"province": city_info["province"],
"city": city_info["city"],
"county": county_name,
}
def get_params(self, send_city, receive_city):
# 根据市查出省份和区名称
send_city_info = self.get_city_detail(send_city)
receive_city_info = self.get_city_detail(receive_city)
# 预计送达时间接口文档;https://www.yuque.com/kdnjishuzhichi/dfcrg1/ynkmts0e5owsnpvu
# 请求接口指令
RequestType = "6004"
# 组装应用级参数
RequestData = {
"ShipperCode": self.delivery_company_id,
"ReceiveArea": receive_city_info["county"],
"ReceiveCity": receive_city_info["city"],
"ReceiveProvince": receive_city_info["province"],
"SendArea": send_city_info["county"],
"SendCity": send_city_info["city"],
"SendProvince": send_city_info["province"],
}
# 组装系统级参数
data = {
"RequestData": json.dumps(RequestData),
"RequestType": RequestType,
"EBusinessID": self.e_business_id,
"DataSign": self.get_data_sign(RequestData),
"DataType": 2,
}
return data
def parse_results(self, response):
# 返回例子:
# {
# "EBusinessID" : "1000000",
# "Data" : {
# "DeliveryTime" : "06月15日下午可达",
# "SendAddress" : null,
# "ReceiveArea" : "芙蓉区",
# "SendProvince" : "广东省",
# "ReceiveProvince" : "湖南省",
# "ShipperCode" : "DBL",
# "Hour" : "52h",
# "SendArea" : "白云区",
# "ReceiveAddress" : null,
# "SendCity" : "广州市",
# "ReceiveCity" : "长沙市"
# },
# "ResultCode" : "100",
# "Success" : true
# }
response = response["Data"]
data = (
f'发货地点: {response["SendProvince"]} {response["SendCity"]};'
f'收货地点: {response["ReceiveProvince"]} {response["ReceiveCity"]};'
f'预计送达时间: {response["DeliveryTime"]};'
f"快递公司: {self.delivery_company_name};"
f'预计时效: {response["Hour"]}。'
)
return data
def __call__(self, send_city, receive_city):
tool_return = ActionReturn()
try:
res = requests.post(self.api_url, self.get_params(send_city, receive_city))
status_code = res.status_code
response = res.json()
except Exception as e:
tool_return.errmsg = str(e)
tool_return.state = ActionStatusCode.API_ERROR
return tool_return
if status_code == 200:
parsed_res = self.parse_results(response)
tool_return.result = [dict(type="text", content=str(parsed_res))]
tool_return.state = ActionStatusCode.SUCCESS
else:
tool_return.errmsg = str(status_code)
tool_return.state = ActionStatusCode.API_ERROR
return tool_return