Merge branch 'xinnan-tech:main' into main

This commit is contained in:
taiping520
2025-04-24 09:50:24 +08:00
committed by GitHub
16 changed files with 352 additions and 118 deletions
@@ -172,5 +172,5 @@ public interface Constant {
/**
* 版本号
*/
public static final String VERSION = "0.3.10";
public static final String VERSION = "0.3.11";
}
@@ -1,5 +1,5 @@
<template>
<el-dialog :title="title" :visible.sync="visible" width="500px" @close="handleClose">
<el-dialog :title="title" :visible.sync="visible" width="500px" @close="handleClose" @open="handleOpen">
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form-item label="固件名称" prop="firmwareName">
<el-input v-model="form.firmwareName" placeholder="请输入固件名称(板子+版本号)"></el-input>
@@ -13,11 +13,14 @@
<el-input v-model="form.version" placeholder="请输入版本号(x.x.x格式)"></el-input>
</el-form-item>
<el-form-item label="固件文件" prop="firmwarePath">
<el-upload class="upload-demo" action="#" :http-request="handleUpload" :before-upload="beforeUpload"
:accept="'.bin,.apk'" :limit="1" :multiple="false" :auto-upload="true">
<el-upload ref="upload" class="upload-demo" action="#" :http-request="handleUpload"
:before-upload="beforeUpload" :accept="'.bin,.apk'" :limit="1" :multiple="false" :auto-upload="true"
:on-remove="handleRemove">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传固件文件(.bin/.apk)且不超过100MB</div>
</el-upload>
<el-progress v-if="isUploading || uploadStatus === 'success'" :percentage="uploadProgress"
:status="uploadStatus"></el-progress>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注信息"></el-input>
@@ -53,6 +56,9 @@ export default {
data() {
return {
firmwareTypes: FIRMWARE_TYPES,
uploadProgress: 0,
uploadStatus: '',
isUploading: false,
rules: {
firmwareName: [
{ required: true, message: '请输入固件名称(板子+版本号)', trigger: 'blur' }
@@ -108,16 +114,72 @@ export default {
},
handleUpload(options) {
const { file } = options
this.uploadProgress = 0
this.uploadStatus = ''
this.isUploading = true
// 使用setTimeout实现简单的0-50%过渡
const timer = setTimeout(() => {
if (this.uploadProgress < 50) { // 只有当进度小于50%时才设置
this.uploadProgress = 50
}
}, 1000)
Api.ota.uploadFirmware(file, (res) => {
clearTimeout(timer) // 清除定时器
res = res.data
if (res.code === 0) {
this.form.firmwarePath = res.data
this.form.size = file.size
this.uploadProgress = 100
this.uploadStatus = 'success'
this.$message.success('固件文件上传成功')
// 延迟2秒后隐藏进度条
setTimeout(() => {
this.isUploading = false
}, 2000)
} else {
this.uploadStatus = 'exception'
this.$message.error(res.msg || '文件上传失败')
this.isUploading = false
}
}, (progressEvent) => {
if (progressEvent.total) {
const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total)
// 只有当进度大于50%时才更新
if (progress > 50) {
this.uploadProgress = progress
}
// 如果上传完成但还没收到成功响应,保持进度条显示
if (progress === 100) {
this.uploadStatus = ''
}
}
})
},
handleRemove() {
this.form.firmwarePath = ''
this.form.size = 0
this.uploadProgress = 0
this.uploadStatus = ''
this.isUploading = false
},
handleOpen() {
// 重置上传相关状态
this.uploadProgress = 0
this.uploadStatus = ''
this.isUploading = false
// 重置表单中的文件相关字段
if (!this.form.id) { // 只在新增时重置
this.form.firmwarePath = ''
this.form.size = 0
// 重置上传组件
this.$nextTick(() => {
if (this.$refs.upload) {
this.$refs.upload.clearFiles()
}
})
}
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ import sys
from loguru import logger
from config.config_loader import load_config
SERVER_VERSION = "0.3.10"
SERVER_VERSION = "0.3.11"
def get_module_abbreviation(module_name, module_dict):
+6 -6
View File
@@ -226,12 +226,12 @@ class ConnectionHandler:
"""加载意图识别"""
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)
# 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)
@@ -6,6 +6,7 @@ from core.providers.llm.base import LLMProviderBase
TAG = __name__
logger = setup_logging()
class LLMProvider(LLMProviderBase):
def __init__(self, config):
self.api_key = config["api_key"]
@@ -19,21 +20,25 @@ class LLMProvider(LLMProviderBase):
# 处理dialogue
if self.is_No_prompt:
dialogue.pop(0)
logger.bind(tag=TAG).debug(f"【阿里百练API服务】处理后的dialogue: {dialogue}")
logger.bind(tag=TAG).debug(
f"【阿里百练API服务】处理后的dialogue: {dialogue}"
)
# 构造调用参数
call_params = {
"api_key": self.api_key,
"app_id": self.app_id,
"session_id": session_id,
"messages": dialogue
"messages": dialogue,
}
if self.memory_id != False:
# 百练memory需要prompt参数
prompt = dialogue[-1].get("content")
call_params["memory_id"] = self.memory_id
call_params["prompt"] = prompt
logger.bind(tag=TAG).debug(f"【阿里百练API服务】处理后的prompt: {prompt}")
logger.bind(tag=TAG).debug(
f"【阿里百练API服务】处理后的prompt: {prompt}"
)
responses = Application.call(**call_params)
if responses.status_code != HTTPStatus.OK:
@@ -44,9 +49,15 @@ class LLMProvider(LLMProviderBase):
)
yield "【阿里百练API服务响应异常】"
else:
logger.bind(tag=TAG).debug(f"【阿里百练API服务】构造参数: {call_params}")
logger.bind(tag=TAG).debug(
f"【阿里百练API服务】构造参数: {call_params}"
)
yield responses.output.text
except Exception as e:
logger.bind(tag=TAG).error(f"【阿里百练API服务】响应异常: {e}")
yield "【LLM服务响应异常】"
def response_with_functions(self, session_id, dialogue, functions=None):
logger.bind(tag=TAG).info(f"阿里百练暂未实现完整的工具调用(function call")
return self.response(session_id, dialogue)
@@ -21,37 +21,36 @@ class LLMProvider(LLMProviderBase):
# 发起流式请求
with requests.post(
f"{self.base_url}/chat/completions",
headers={"Authorization": f"Bearer {self.api_key}"},
json={
"stream": True,
"chatId": session_id,
"detail": self.detail,
"variables": self.variables,
"messages": [
{
"role": "user",
"content": last_msg["content"]
}
]
},
stream=True
f"{self.base_url}/chat/completions",
headers={"Authorization": f"Bearer {self.api_key}"},
json={
"stream": True,
"chatId": session_id,
"detail": self.detail,
"variables": self.variables,
"messages": [{"role": "user", "content": last_msg["content"]}],
},
stream=True,
) as r:
for line in r.iter_lines():
if line:
try:
if line.startswith(b'data: '):
if line[6:].decode('utf-8') == '[DONE]':
if line.startswith(b"data: "):
if line[6:].decode("utf-8") == "[DONE]":
break
data = json.loads(line[6:])
if 'choices' in data and len(data['choices']) > 0:
delta = data['choices'][0].get('delta', {})
if delta and 'content' in delta and delta['content'] is not None:
content = delta['content']
if '<think>' in content:
if "choices" in data and len(data["choices"]) > 0:
delta = data["choices"][0].get("delta", {})
if (
delta
and "content" in delta
and delta["content"] is not None
):
content = delta["content"]
if "<think>" in content:
continue
if '</think>' in content:
if "</think>" in content:
continue
yield content
@@ -62,4 +61,8 @@ class LLMProvider(LLMProviderBase):
except Exception as e:
logger.bind(tag=TAG).error(f"Error in response generation: {e}")
yield "【服务响应异常】"
yield "【服务响应异常】"
def response_with_functions(self, session_id, dialogue, functions=None):
logger.bind(tag=TAG).info(f"fastgpt暂未实现完整的工具调用(function call")
return self.response(session_id, dialogue)
@@ -134,3 +134,7 @@ class LLMProvider(LLMProviderBase):
yield f"JSON解码错误:{e}"
except Exception as e:
yield f"发生错误:{e}"
def response_with_functions(self, session_id, dialogue, functions=None):
logger.bind(tag=TAG).info(f"gemini暂未实现完整的工具调用(function call")
return self.response(session_id, dialogue)
@@ -2,6 +2,7 @@ import requests
from bs4 import BeautifulSoup
from config.logger import setup_logging
from plugins_func.register import register_function, ToolType, ActionResponse, Action
from core.utils.util import get_ip_info
TAG = __name__
logger = setup_logging()
@@ -11,9 +12,12 @@ GET_WEATHER_FUNCTION_DESC = {
"function": {
"name": "get_weather",
"description": (
"获取某个地点的天气,用户应提供一个位置,比如用户说杭州天气,参数为:杭州。"
"获取某个地点的天气信息。当用户询问与天气相关的问题(例如'今天的天气怎么样?''明天会下雨吗?''广州天气如何?'等),"
"或对话中包含'天气'字眼时,调用此功能。用户可以提供具体位置(如城市名),"
"如果未提供位置,则自动获取用户当前位置查询天气。"
"如果用户说的是省份,默认用省会城市。如果用户说的不是省份或城市而是一个地名,"
"默认用该地所在省份的省会城市。"
"当IP解析失败会使用默认地址"
),
"parameters": {
"type": "object",
@@ -57,6 +61,7 @@ WEATHER_CODE_MAP = {
"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()
@@ -97,27 +102,47 @@ def parse_weather_info(soup):
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"]
location = location or conn.client_ip_info.get("city") or default_location
logger.bind(tag=TAG).debug(f"获取天气: {location}")
print("用户设置的default_location为:", default_location)
client_ip = conn.client_ip
print("用户提供的location为:", location)
# 优先使用用户提供的location参数
if not location:
# 通过客户端IP解析城市
if client_ip:
# 动态解析IP对应的城市信息
ip_info = get_ip_info(client_ip, logger)
print("解析用户IP后的城市为:", ip_info)
location = ip_info.get("city") if ip_info and "city" in ip_info else None
else:
# 若IP解析失败或无IP,使用默认位置
location = default_location
print("解析失败,将使用默认地址", location)
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'])
if not soup:
return ActionResponse(Action.REQLLM, None, "请求失败")
city_name, current_abstract, current_basic, temps_list = parse_weather_info(soup)
weather_report = f"根据下列数据,用{lang}回应用户的查询天气请求:\n{city_name}未来7天天气:\n"
for i, (date, weather, high, low) in enumerate(temps_list):
if high and low:
weather_report += f"{date}: {low}{high}, {weather}\n"
weather_report += (
f"当前天气: {current_abstract}\n"
f"当前天气参数: {current_basic}\n"
f"(确保只报告指定单日的天气情况,除非未来会出现异常天气;或者用户明确要求想要了解多日天气,如果未指定,默认报告今天的天气。"
"参数为0的值不需要报告给用户,每次都报告体感温度,根据语境选择合适的参数内容告知用户,并对参数给出相应评价)"
)
return ActionResponse(Action.REQLLM, weather_report, None)
print(f"当前查询的城市名称是: {city_name}")
weather_report = f"您查询的位置是:{city_name}\n\n当前天气: {current_abstract}\n"
# 添加有效的当前天气参数
if current_basic:
weather_report += "详细参数:\n"
for key, value in current_basic.items():
if value != "0": # 过滤无效值
weather_report += f" · {key}: {value}\n"
# 添加7天预报
weather_report += "\n未来7天预报:\n"
for date, weather, high, low in temps_list:
weather_report += f"{date}: {weather},气温 {low}~{high}\n"
# 提示语
weather_report += "\n(如需某一天的具体天气,请告诉我日期)"
return ActionResponse(Action.REQLLM, weather_report, None)
@@ -13,7 +13,7 @@ def append_devices_to_prompt(conn):
"functions", []
)
if "hass_get_state" in funcs or "hass_set_state" in funcs:
prompt = "下面是我家智能设备,可以通过homeassistant控制\n"
prompt = "\n下面是我家智能设备列表(位置,设备名,entity_id),可以通过homeassistant控制\n"
devices = conn.config["plugins"]["home_assistant"].get("devices", [])
if len(devices) == 0:
return