diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/service/OtaService.java b/main/manager-api/src/main/java/xiaozhi/modules/device/service/OtaService.java index 0f50a582..cd35f6ae 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/device/service/OtaService.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/service/OtaService.java @@ -17,4 +17,6 @@ public interface OtaService extends BaseService { void update(OtaEntity entity); void delete(String[] ids); + + OtaEntity getLatestOta(String type); } \ No newline at end of file diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java index 2006791b..b442275a 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/DeviceServiceImpl.java @@ -6,11 +6,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.TimeZone; -import java.util.concurrent.TimeUnit; +import java.util.UUID; import org.apache.commons.lang3.StringUtils; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -18,10 +19,14 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import cn.hutool.core.util.RandomUtil; +import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import xiaozhi.common.constant.Constant; import xiaozhi.common.exception.RenException; import xiaozhi.common.page.PageData; +import xiaozhi.common.redis.RedisKeys; +import xiaozhi.common.redis.RedisUtils; import xiaozhi.common.service.impl.BaseServiceImpl; import xiaozhi.common.user.UserDetail; import xiaozhi.common.utils.ConvertUtils; @@ -31,7 +36,9 @@ import xiaozhi.modules.device.dto.DevicePageUserDTO; import xiaozhi.modules.device.dto.DeviceReportReqDTO; import xiaozhi.modules.device.dto.DeviceReportRespDTO; import xiaozhi.modules.device.entity.DeviceEntity; +import xiaozhi.modules.device.entity.OtaEntity; import xiaozhi.modules.device.service.DeviceService; +import xiaozhi.modules.device.service.OtaService; import xiaozhi.modules.device.vo.UserShowDeviceListVO; import xiaozhi.modules.security.user.SecurityUser; import xiaozhi.modules.sys.service.SysParamsService; @@ -39,25 +46,14 @@ import xiaozhi.modules.sys.service.SysUserUtilService; @Slf4j @Service +@AllArgsConstructor public class DeviceServiceImpl extends BaseServiceImpl implements DeviceService { private final DeviceDao deviceDao; - private final SysUserUtilService sysUserUtilService; - - private final RedisTemplate redisTemplate; - private final SysParamsService sysParamsService; - - // 添加构造函数来初始化 deviceMapper - public DeviceServiceImpl(DeviceDao deviceDao, SysUserUtilService sysUserUtilService, - SysParamsService sysParamsService, - RedisTemplate redisTemplate) { - this.deviceDao = deviceDao; - this.sysUserUtilService = sysUserUtilService; - this.redisTemplate = redisTemplate; - this.sysParamsService = sysParamsService; - } + private final RedisUtils redisUtils; + private final OtaService otaService; @Override public DeviceEntity getDeviceById(String deviceId) { @@ -72,14 +68,14 @@ public class DeviceServiceImpl extends BaseServiceImpl throw new RenException("激活码不能为空"); } String deviceKey = "ota:activation:code:" + activationCode; - Object cacheDeviceId = redisTemplate.opsForValue().get(deviceKey); + Object cacheDeviceId = redisUtils.get(deviceKey); if (cacheDeviceId == null) { throw new RenException("激活码错误"); } String deviceId = (String) cacheDeviceId; String safeDeviceId = deviceId.replace(":", "_").toLowerCase(); String cacheDeviceKey = String.format("ota:activation:data:%s", safeDeviceId); - Map cacheMap = redisTemplate.opsForHash().entries(cacheDeviceKey); + Map cacheMap = (Map) redisUtils.get(cacheDeviceKey); if (cacheMap == null) { throw new RenException("激活码错误"); } @@ -116,8 +112,8 @@ public class DeviceServiceImpl extends BaseServiceImpl deviceDao.insert(deviceEntity); // 清理redis缓存 - redisTemplate.delete(cacheDeviceKey); - redisTemplate.delete(deviceKey); + redisUtils.delete(cacheDeviceKey); + redisUtils.delete(deviceKey); return true; } @@ -126,12 +122,24 @@ public class DeviceServiceImpl extends BaseServiceImpl DeviceReportReqDTO deviceReport) { DeviceReportRespDTO response = new DeviceReportRespDTO(); response.setServer_time(buildServerTime()); - // todo: 此处是固件信息,目前是针对固件上传上来的版本号再返回回去 - // 在未来开发了固件更新功能,需要更换此处代码, - // 或写定时任务定期请求虾哥的OTA,获取最新的版本讯息保存到服务内 + + String type = deviceReport.getBoard() == null ? null : deviceReport.getBoard().getType(); + OtaEntity ota = otaService.getLatestOta(type); + String downloadUrl = null; + if (ota != null) { + // 获取当前请求的URL + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getRequest(); + String requestUrl = request.getRequestURL().toString(); + // 将URL中的/ota/替换为/otaMag/download/ + String uuid = UUID.randomUUID().toString(); + redisUtils.set(RedisKeys.getOtaIdKey(uuid), ota.getId()); + downloadUrl = requestUrl.replace("/ota/", "/otaMag/download/") + uuid; + } + DeviceReportRespDTO.Firmware firmware = new DeviceReportRespDTO.Firmware(); - firmware.setVersion(deviceReport.getApplication().getVersion()); - firmware.setUrl("http://localhost:8002/xiaozhi/ota/download"); + firmware.setVersion(ota == null ? null : ota.getVersion()); + firmware.setUrl(downloadUrl); response.setFirmware(firmware); // 添加WebSocket配置 @@ -252,7 +260,7 @@ public class DeviceServiceImpl extends BaseServiceImpl public String geCodeByDeviceId(String deviceId) { String dataKey = getDeviceCacheKey(deviceId); - Map cacheMap = redisTemplate.opsForHash().entries(dataKey); + Map cacheMap = (Map) redisUtils.get(dataKey); if (cacheMap != null && cacheMap.containsKey("activation_code")) { String cachedCode = (String) cacheMap.get("activation_code"); return cachedCode; @@ -299,12 +307,11 @@ public class DeviceServiceImpl extends BaseServiceImpl // 写入主数据 key String dataKey = getDeviceCacheKey(deviceId); - redisTemplate.opsForHash().putAll(dataKey, dataMap); - redisTemplate.expire(dataKey, 24, TimeUnit.HOURS); + redisUtils.set(dataKey, dataMap); // 写入反查激活码 key String codeKey = "ota:activation:code:" + newCode; - redisTemplate.opsForValue().set(codeKey, deviceId, 24, TimeUnit.HOURS); + redisUtils.set(codeKey, deviceId); } return code; } diff --git a/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/OtaServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/OtaServiceImpl.java index aca69b9c..ef9ac01d 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/OtaServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/device/service/impl/OtaServiceImpl.java @@ -71,4 +71,13 @@ public class OtaServiceImpl extends BaseServiceImpl implement return baseDao.insert(entity) > 0; } + + @Override + public OtaEntity getLatestOta(String type) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("type", type) + .orderByDesc("update_date") + .last("LIMIT 1"); + return baseDao.selectOne(wrapper); + } } \ No newline at end of file diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index 8fa09318..df751e7c 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -225,13 +225,6 @@ class ConnectionHandler: self._initialize_memory() """加载意图识别""" self._initialize_intent() - """加载位置信息""" - # self.client_ip_info = get_ip_info(self.client_ip, self.logger) - # if self.client_ip_info is not None and "city" in self.client_ip_info: - # self.logger.bind(tag=TAG).info(f"Client ip info: {self.client_ip_info}") - # self.prompt = self.prompt + f"\nuser location:{self.client_ip_info}" - # - # self.dialogue.update_system_message(self.prompt) def _initialize_private_config(self): read_config_from_api = self.config.get("read_config_from_api", False) diff --git a/main/xiaozhi-server/core/providers/llm/AliBL/AliBL.py b/main/xiaozhi-server/core/providers/llm/AliBL/AliBL.py index 769f3117..16057d1d 100644 --- a/main/xiaozhi-server/core/providers/llm/AliBL/AliBL.py +++ b/main/xiaozhi-server/core/providers/llm/AliBL/AliBL.py @@ -2,6 +2,7 @@ from config.logger import setup_logging from http import HTTPStatus from dashscope import Application from core.providers.llm.base import LLMProviderBase +from core.utils.util import check_model_key TAG = __name__ logger = setup_logging() @@ -14,6 +15,7 @@ class LLMProvider(LLMProviderBase): self.base_url = config.get("base_url") self.is_No_prompt = config.get("is_no_prompt") self.memory_id = config.get("ali_memory_id") + check_model_key("AliBLLLM", self.api_key) def response(self, session_id, dialogue): try: diff --git a/main/xiaozhi-server/core/providers/llm/coze/coze.py b/main/xiaozhi-server/core/providers/llm/coze/coze.py index 416d4ce3..60a04667 100644 --- a/main/xiaozhi-server/core/providers/llm/coze/coze.py +++ b/main/xiaozhi-server/core/providers/llm/coze/coze.py @@ -1,14 +1,17 @@ from config.logger import setup_logging -import requests import json -import re from core.providers.llm.base import LLMProviderBase -import os # official coze sdk for Python [cozepy](https://github.com/coze-dev/coze-py) from cozepy import COZE_CN_BASE_URL -from cozepy import Coze, TokenAuth, Message, ChatStatus, MessageContentType, ChatEventType # noqa +from cozepy import ( + Coze, + TokenAuth, + Message, + ChatEventType, +) # noqa from core.providers.llm.system_prompt import get_system_prompt_for_function +from core.utils.util import check_model_key TAG = __name__ logger = setup_logging() @@ -20,6 +23,7 @@ class LLMProvider(LLMProviderBase): self.bot_id = str(config.get("bot_id")) self.user_id = str(config.get("user_id")) self.session_conversation_map = {} # 存储session_id和conversation_id的映射 + check_model_key("CozeLLM", self.personal_access_token) def response(self, session_id, dialogue): coze_api_token = self.personal_access_token @@ -48,22 +52,22 @@ class LLMProvider(LLMProviderBase): print(event.message.content, end="", flush=True) yield event.message.content - def response_with_functions(self, session_id, dialogue, functions=None): + def response_with_functions(self, session_id, dialogue, functions=None): if len(dialogue) == 2 and functions is not None and len(functions) > 0: # 第一次调用llm, 取最后一条用户消息,附加tool提示词 last_msg = dialogue[-1]["content"] function_str = json.dumps(functions, ensure_ascii=False) modify_msg = get_system_prompt_for_function(function_str) + last_msg - dialogue[-1]["content"] = modify_msg + dialogue[-1]["content"] = modify_msg # 如果最后一个是 role="tool",附加到user上 if len(dialogue) > 1 and dialogue[-1]["role"] == "tool": assistant_msg = "\ntool call result: " + dialogue[-1]["content"] + "\n\n" - while len(dialogue) > 1 : + while len(dialogue) > 1: if dialogue[-1]["role"] == "user": dialogue[-1]["content"] = assistant_msg + dialogue[-1]["content"] break dialogue.pop() - + for token in self.response(session_id, dialogue): yield token, None diff --git a/main/xiaozhi-server/core/providers/llm/dify/dify.py b/main/xiaozhi-server/core/providers/llm/dify/dify.py index 09f0739e..52ca9853 100644 --- a/main/xiaozhi-server/core/providers/llm/dify/dify.py +++ b/main/xiaozhi-server/core/providers/llm/dify/dify.py @@ -3,6 +3,7 @@ from config.logger import setup_logging import requests from core.providers.llm.base import LLMProviderBase from core.providers.llm.system_prompt import get_system_prompt_for_function +from core.utils.util import check_model_key TAG = __name__ logger = setup_logging() @@ -14,6 +15,7 @@ class LLMProvider(LLMProviderBase): self.mode = config.get("mode", "chat-messages") self.base_url = config.get("base_url", "https://api.dify.ai/v1").rstrip("/") self.session_conversation_map = {} # 存储session_id和conversation_id的映射 + check_model_key("DifyLLM", self.api_key) def response(self, session_id, dialogue): try: @@ -60,7 +62,9 @@ class LLMProvider(LLMProviderBase): conversation_id # 更新映射 ) # 过滤 message_replace 事件,此事件会全量推一次 - if event.get("event") != "message_replace" and event.get("answer"): + if event.get("event") != "message_replace" and event.get( + "answer" + ): yield event["answer"] elif self.mode == "workflows/run": for line in r.iter_lines(): @@ -76,29 +80,31 @@ class LLMProvider(LLMProviderBase): if line.startswith(b"data: "): event = json.loads(line[6:]) # 过滤 message_replace 事件,此事件会全量推一次 - if event.get("event") != "message_replace" and event.get("answer"): + if event.get("event") != "message_replace" and event.get( + "answer" + ): yield event["answer"] except Exception as e: logger.bind(tag=TAG).error(f"Error in response generation: {e}") yield "【服务响应异常】" - def response_with_functions(self, session_id, dialogue, functions=None): + def response_with_functions(self, session_id, dialogue, functions=None): if len(dialogue) == 2 and functions is not None and len(functions) > 0: # 第一次调用llm, 取最后一条用户消息,附加tool提示词 last_msg = dialogue[-1]["content"] function_str = json.dumps(functions, ensure_ascii=False) modify_msg = get_system_prompt_for_function(function_str) + last_msg - dialogue[-1]["content"] = modify_msg + dialogue[-1]["content"] = modify_msg # 如果最后一个是 role="tool",附加到user上 if len(dialogue) > 1 and dialogue[-1]["role"] == "tool": assistant_msg = "\ntool call result: " + dialogue[-1]["content"] + "\n\n" - while len(dialogue) > 1 : + while len(dialogue) > 1: if dialogue[-1]["role"] == "user": dialogue[-1]["content"] = assistant_msg + dialogue[-1]["content"] break dialogue.pop() - + for token in self.response(session_id, dialogue): - yield token, None \ No newline at end of file + yield token, None diff --git a/main/xiaozhi-server/core/providers/llm/fastgpt/fastgpt.py b/main/xiaozhi-server/core/providers/llm/fastgpt/fastgpt.py index e0e6ccc7..a35f41cc 100644 --- a/main/xiaozhi-server/core/providers/llm/fastgpt/fastgpt.py +++ b/main/xiaozhi-server/core/providers/llm/fastgpt/fastgpt.py @@ -2,6 +2,7 @@ import json from config.logger import setup_logging import requests from core.providers.llm.base import LLMProviderBase +from core.utils.util import check_model_key TAG = __name__ logger = setup_logging() @@ -13,6 +14,7 @@ class LLMProvider(LLMProviderBase): self.base_url = config.get("base_url") self.detail = config.get("detail", False) self.variables = config.get("variables", {}) + check_model_key("FastGPTLLM", self.api_key) def response(self, session_id, dialogue): try: diff --git a/main/xiaozhi-server/core/utils/util.py b/main/xiaozhi-server/core/utils/util.py index e9e6795a..2fc37e85 100644 --- a/main/xiaozhi-server/core/utils/util.py +++ b/main/xiaozhi-server/core/utils/util.py @@ -159,7 +159,6 @@ def check_model_key(modelType, modelKey): raise ValueError( "你还没配置" + modelType + "的密钥,请检查一下所使用的LLM是否配置了密钥" ) - return False return True @@ -211,7 +210,7 @@ def check_ffmpeg_installed(): def extract_json_from_string(input_string): """提取字符串中的 JSON 部分""" pattern = r"(\{.*\})" - match = re.search(pattern, input_string, re.DOTALL) #添加 re.DOTALL + match = re.search(pattern, input_string, re.DOTALL) # 添加 re.DOTALL if match: return match.group(1) # 返回提取的 JSON 字符串 return None diff --git a/main/xiaozhi-server/plugins_func/functions/get_weather.py b/main/xiaozhi-server/plugins_func/functions/get_weather.py index 3764401e..b17755a2 100644 --- a/main/xiaozhi-server/plugins_func/functions/get_weather.py +++ b/main/xiaozhi-server/plugins_func/functions/get_weather.py @@ -12,60 +12,105 @@ GET_WEATHER_FUNCTION_DESC = { "function": { "name": "get_weather", "description": ( - "获取某个地点的天气信息。当用户询问与天气相关的问题(例如'今天的天气怎么样?'、'明天会下雨吗?'、'广州天气如何?'等)," - "或对话中包含'天气'字眼时,调用此功能。用户可以提供具体位置(如城市名)," - "如果未提供位置,则自动获取用户当前位置查询天气。" - "如果用户说的是省份,默认用省会城市。如果用户说的不是省份或城市而是一个地名," - "默认用该地所在省份的省会城市。" - "当IP解析失败会使用默认地址" + "获取某个地点的天气,用户应提供一个位置,比如用户说杭州天气,参数为:杭州。" + "如果用户说的是省份,默认用省会城市。如果用户说的不是省份或城市而是一个地名,默认用该地所在省份的省会城市。" + "如果用户没有指明地点,说“天气怎么样”,”今天天气如何“,location参数为空" ), "parameters": { "type": "object", "properties": { "location": { "type": "string", - "description": "地点名,例如杭州。可选参数,如果不提供则不传" + "description": "地点名,例如杭州。可选参数,如果不提供则不传", }, "lang": { "type": "string", - "description": "返回用户使用的语言code,例如zh_CN/zh_HK/en_US/ja_JP等,默认zh_CN" - } + "description": "返回用户使用的语言code,例如zh_CN/zh_HK/en_US/ja_JP等,默认zh_CN", + }, }, - "required": ["lang"] - } - } + "required": ["lang"], + }, + }, } HEADERS = { - 'User-Agent': ( - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' - '(KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36' + "User-Agent": ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36" ) } # 天气代码 https://dev.qweather.com/docs/resource/icons/#weather-icons WEATHER_CODE_MAP = { - "100": "晴", "101": "多云", "102": "少云", "103": "晴间多云", "104": "阴", - "150": "晴", "151": "多云", "152": "少云", "153": "晴间多云", - "300": "阵雨", "301": "强阵雨", "302": "雷阵雨", "303": "强雷阵雨", "304": "雷阵雨伴有冰雹", - "305": "小雨", "306": "中雨", "307": "大雨", "308": "极端降雨", "309": "毛毛雨/细雨", - "310": "暴雨", "311": "大暴雨", "312": "特大暴雨", "313": "冻雨", "314": "小到中雨", - "315": "中到大雨", "316": "大到暴雨", "317": "暴雨到大暴雨", "318": "大暴雨到特大暴雨", - "350": "阵雨", "351": "强阵雨", "399": "雨", - "400": "小雪", "401": "中雪", "402": "大雪", "403": "暴雪", "404": "雨夹雪", - "405": "雨雪天气", "406": "阵雨夹雪", "407": "阵雪", "408": "小到中雪", "409": "中到大雪", "410": "大到暴雪", - "456": "阵雨夹雪", "457": "阵雪", "499": "雪", - "500": "薄雾", "501": "雾", "502": "霾", "503": "扬沙", "504": "浮尘", - "507": "沙尘暴", "508": "强沙尘暴", - "509": "浓雾", "510": "强浓雾", "511": "中度霾", "512": "重度霾", "513": "严重霾", "514": "大雾", "515": "特强浓雾", - "900": "热", "901": "冷", "999": "未知" + "100": "晴", + "101": "多云", + "102": "少云", + "103": "晴间多云", + "104": "阴", + "150": "晴", + "151": "多云", + "152": "少云", + "153": "晴间多云", + "300": "阵雨", + "301": "强阵雨", + "302": "雷阵雨", + "303": "强雷阵雨", + "304": "雷阵雨伴有冰雹", + "305": "小雨", + "306": "中雨", + "307": "大雨", + "308": "极端降雨", + "309": "毛毛雨/细雨", + "310": "暴雨", + "311": "大暴雨", + "312": "特大暴雨", + "313": "冻雨", + "314": "小到中雨", + "315": "中到大雨", + "316": "大到暴雨", + "317": "暴雨到大暴雨", + "318": "大暴雨到特大暴雨", + "350": "阵雨", + "351": "强阵雨", + "399": "雨", + "400": "小雪", + "401": "中雪", + "402": "大雪", + "403": "暴雪", + "404": "雨夹雪", + "405": "雨雪天气", + "406": "阵雨夹雪", + "407": "阵雪", + "408": "小到中雪", + "409": "中到大雪", + "410": "大到暴雪", + "456": "阵雨夹雪", + "457": "阵雪", + "499": "雪", + "500": "薄雾", + "501": "雾", + "502": "霾", + "503": "扬沙", + "504": "浮尘", + "507": "沙尘暴", + "508": "强沙尘暴", + "509": "浓雾", + "510": "强浓雾", + "511": "中度霾", + "512": "重度霾", + "513": "严重霾", + "514": "大雾", + "515": "特强浓雾", + "900": "热", + "901": "冷", + "999": "未知", } def fetch_city_info(location, api_key): url = f"https://geoapi.qweather.com/v2/city/lookup?key={api_key}&location={location}&lang=zh" response = requests.get(url, headers=HEADERS).json() - return response.get('location', [])[0] if response.get('location') else None + return response.get("location", [])[0] if response.get("location") else None def fetch_weather_page(url): @@ -77,10 +122,14 @@ def parse_weather_info(soup): city_name = soup.select_one("h1.c-submenu__location").get_text(strip=True) current_abstract = soup.select_one(".c-city-weather-current .current-abstract") - current_abstract = current_abstract.get_text(strip=True) if current_abstract else "未知" + current_abstract = ( + current_abstract.get_text(strip=True) if current_abstract else "未知" + ) current_basic = {} - for item in soup.select(".c-city-weather-current .current-basic .current-basic___item"): + for item in soup.select( + ".c-city-weather-current .current-basic .current-basic___item" + ): parts = item.get_text(strip=True, separator=" ").split(" ") if len(parts) == 2: key, value = parts[1], parts[0] @@ -89,7 +138,9 @@ def parse_weather_info(soup): temps_list = [] for row in soup.select(".city-forecast-tabs__row")[:7]: # 取前7天的数据 date = row.select_one(".date-bg .date").get_text(strip=True) - weather_code = row.select_one(".date-bg .icon")["src"].split("/")[-1].split(".")[0] + weather_code = ( + row.select_one(".date-bg .icon")["src"].split("/")[-1].split(".")[0] + ) weather = WEATHER_CODE_MAP.get(weather_code, "未知") temps = [span.get_text(strip=True) for span in row.select(".tmp-cont .temp")] high_temp, low_temp = (temps[0], temps[-1]) if len(temps) >= 2 else (None, None) @@ -98,7 +149,7 @@ def parse_weather_info(soup): return city_name, current_abstract, current_basic, temps_list -@register_function('get_weather', GET_WEATHER_FUNCTION_DESC, ToolType.SYSTEM_CTL) +@register_function("get_weather", GET_WEATHER_FUNCTION_DESC, ToolType.SYSTEM_CTL) def get_weather(conn, location: str = None, lang: str = "zh_CN"): api_key = conn.config["plugins"]["get_weather"]["api_key"] default_location = conn.config["plugins"]["get_weather"]["default_location"] @@ -120,8 +171,10 @@ def get_weather(conn, location: str = None, lang: str = "zh_CN"): city_info = fetch_city_info(location, api_key) if not city_info: - return ActionResponse(Action.REQLLM, f"未找到相关的城市: {location},请确认地点是否正确", None) - soup = fetch_weather_page(city_info['fxLink']) + return ActionResponse( + Action.REQLLM, f"未找到相关的城市: {location},请确认地点是否正确", None + ) + soup = fetch_weather_page(city_info["fxLink"]) if not soup: return ActionResponse(Action.REQLLM, None, "请求失败") city_name, current_abstract, current_basic, temps_list = parse_weather_info(soup)