diff --git a/config.json b/config.json index 716228a4..86eff0fd 100644 --- a/config.json +++ b/config.json @@ -308,8 +308,12 @@ "history_max_len": 300 }, "tongyi": { - "type": "web", - "cookie_path": "cookie/tongyi.json" + "cookie_path": "cookie/tongyi.json", + "type": "api", + "preset": "你是一个专业的虚拟主播", + "api_key": "", + "history_enable": true, + "history_max_len": 300 }, "tongyixingchen": { "access_token": "此处填写你的密钥", diff --git a/config.json.bak b/config.json.bak index 716228a4..86eff0fd 100644 --- a/config.json.bak +++ b/config.json.bak @@ -308,8 +308,12 @@ "history_max_len": 300 }, "tongyi": { - "type": "web", - "cookie_path": "cookie/tongyi.json" + "cookie_path": "cookie/tongyi.json", + "type": "api", + "preset": "你是一个专业的虚拟主播", + "api_key": "", + "history_enable": true, + "history_max_len": 300 }, "tongyixingchen": { "access_token": "此处填写你的密钥", diff --git a/requirements.txt b/requirements.txt index 7629673c..31dbc150 100644 --- a/requirements.txt +++ b/requirements.txt @@ -71,4 +71,5 @@ jieba gradio==4.16.0 TikTokLive azure-cognitiveservices-speech -pyjwt \ No newline at end of file +pyjwt +dashscope \ No newline at end of file diff --git a/requirements_common.txt b/requirements_common.txt index 9ac42883..65dc9478 100644 --- a/requirements_common.txt +++ b/requirements_common.txt @@ -209,7 +209,7 @@ qt5-tools==5.15.2.1.3 referencing==0.33.0 regex==2023.6.3 requests==2.31.0 -revTongYi==0.0.1.0 +revTongYi==0.0.5.0 rich==13.7.0 rpds-py==0.17.1 rsa==4.9 @@ -279,4 +279,5 @@ yarl==1.9.4 zhipuai==1.0.7 zstandard==0.19.0 git+https://gitee.com/ikaros-521/WenxinWorkshop-Python-SDK -git+https://gitee.com/ikaros-521/blivedm \ No newline at end of file +git+https://gitee.com/ikaros-521/blivedm +dashscope==1.14.1 \ No newline at end of file diff --git a/tests/test_tongyi/tongyi.py b/tests/test_tongyi/tongyi.py index 173189fb..2cd79792 100644 --- a/tests/test_tongyi/tongyi.py +++ b/tests/test_tongyi/tongyi.py @@ -1,6 +1,5 @@ -import revTongYi -import json, logging - +import json, logging, copy +import traceback def convert_cookies(cookies: list) -> dict: """转换cookies""" @@ -17,14 +16,32 @@ def __init__(self, data): # file_path = "./log/log-" + self.common.get_bj_time(1) + ".txt" # Configure_logger(file_path) + self.config_data = data self.cookie_path = data["cookie_path"] - self.type = data["type"] + self.parentId = None + self.chatbot = None + + self.history = [] self.cookies_dict = {} - with open(self.cookie_path, "r") as f: - self.cookies_dict = convert_cookies(json.load(f)) - + try: + if self.config_data["type"] == "web": + # 非流式模式 + import revTongYi.qianwen as qwen + + with open(self.cookie_path, "r") as f: + self.cookies_dict = convert_cookies(json.load(f)) + self.chatbot = qwen.Chatbot( + cookies=self.cookies_dict # 以dict形式提供cookies + ) + + elif self.config_data["type"] == "api": + import dashscope + + dashscope.api_key = self.config_data["api_key"] + except Exception as e: + logging.error(traceback.format_exc()) def get_resp(self, prompt): """请求对应接口,获取返回值 @@ -36,40 +53,88 @@ def get_resp(self, prompt): str: 返回的文本回答 """ try: - if self.type == "web": - session = revTongYi.Session( - cookies=self.cookies_dict, - firstQuery=prompt - ) - - ret = next( - session.ask( # ask方法实际上是一个迭代器,可以提供参数stream=True并换用for的方式迭代 - prompt=prompt - ) # ask方法接收的详细参数请查看源码 + if self.config_data["type"] == "web": + if self.parentId: + ret = self.chatbot.ask(prompt=prompt, parentId=self.parentId) + else: + ret = self.chatbot.ask(prompt=prompt) + + # logging.info(ret) + + # 是否启用上下文记忆 + if self.config_data['history_enable']: + self.parentId = ret['msgId'] + resp_content = ret['content'][0] + + return resp_content + elif self.config_data["type"] == "api": + from http import HTTPStatus + from dashscope import Generation + from dashscope.api_entities.dashscope_response import Role + + if self.config_data['history_enable'] == False: + messages = [{'role': Role.SYSTEM, 'content': self.config_data["preset"]}, + {'role': Role.USER, 'content': prompt}] + else: + messages = copy.copy(self.history) + messages.append({'role': Role.USER, 'content': prompt}) + messages.insert(0, {'role': Role.SYSTEM, 'content': self.config_data["preset"]}) + + logging.debug(f"messages={messages}") + + response = Generation.call( + Generation.Models.qwen_max, + messages=messages, + result_format='message', # set the result to be "message" format. ) - - return ret["content"][0] + if response.status_code == HTTPStatus.OK: + logging.debug(response) + + resp_content = response.output.choices[0]['message']['content'] + + if self.config_data['history_enable']: + self.history.append({'role': Role.USER, 'content': prompt}) + self.history.append({'role': response.output.choices[0]['message']['role'], + 'content': resp_content}) + while True: + # 获取嵌套列表中所有字符串的字符数 + total_chars = sum(len(item['content']) for item in self.history if 'content' in item) + # 如果大于限定最大历史数,就剔除第一个元素 + if total_chars > int(self.config_data["history_max_len"]): + self.history.pop(0) + self.history.pop(0) + else: + break + + return resp_content + else: + logging.error(f'Request id: {response.request_id}, Status code: {response.status_code}, error code: {response.code}, error message: {response.message}') + return None except Exception as e: - logging.error(e) + logging.error(traceback.format_exc()) return None if __name__ == '__main__': # 配置日志输出格式 logging.basicConfig( - level=logging.DEBUG, # 设置日志级别,可以根据需求调整 + level=logging.INFO, # 设置日志级别,可以根据需求调整 format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) data = { "cookie_path": 'cookies.json', - "type": 'web' + "type": 'api', + "preset": "你是一个专业的虚拟主播", + "api_key": "sk-", + "history_enable": True, + "history_max_len": 20, } tongyi = TongYi(data) - logging.info(tongyi.get_resp("你可以扮演猫娘吗,每句话后面加个喵")) - logging.info(tongyi.get_resp("早上好")) + logging.info(tongyi.get_resp("你现在叫小伊,是个猫娘,每句话后面加个喵")) + logging.info(tongyi.get_resp("早上好,你叫什么")) \ No newline at end of file diff --git a/utils/gpt_model/tongyi.py b/utils/gpt_model/tongyi.py index e0b5469b..e62b1909 100644 --- a/utils/gpt_model/tongyi.py +++ b/utils/gpt_model/tongyi.py @@ -1,10 +1,9 @@ -import revTongYi -import json, logging, traceback +import json, logging, copy +import traceback from utils.common import Common from utils.logger import Configure_logger - def convert_cookies(cookies: list) -> dict: """转换cookies""" cookies_dict = {} @@ -12,7 +11,6 @@ def convert_cookies(cookies: list) -> dict: cookies_dict[cookie["name"]] = cookie["value"] return cookies_dict - class TongYi: def __init__(self, data): self.common = Common() @@ -20,18 +18,32 @@ def __init__(self, data): file_path = "./log/log-" + self.common.get_bj_time(1) + ".txt" Configure_logger(file_path) + self.config_data = data self.cookie_path = data["cookie_path"] - self.type = data["type"] + self.parentId = None + self.chatbot = None + + self.history = [] self.cookies_dict = {} try: - with open(self.cookie_path, "r") as f: - self.cookies_dict = convert_cookies(json.load(f)) + if self.config_data["type"] == "web": + # 非流式模式 + import revTongYi.qianwen as qwen + + with open(self.cookie_path, "r") as f: + self.cookies_dict = convert_cookies(json.load(f)) + self.chatbot = qwen.Chatbot( + cookies=self.cookies_dict # 以dict形式提供cookies + ) + + elif self.config_data["type"] == "api": + import dashscope + + dashscope.api_key = self.config_data["api_key"] except Exception as e: - logging.error(e) - logging.error("通义千问的cookie文件不存在,功能无法正常使用,请检查配置!") - + logging.error(traceback.format_exc()) def get_resp(self, prompt): """请求对应接口,获取返回值 @@ -43,19 +55,63 @@ def get_resp(self, prompt): str: 返回的文本回答 """ try: - if self.type == "web": - session = revTongYi.Session( - cookies=self.cookies_dict, - firstQuery=prompt - ) - - ret = next( - session.ask( # ask方法实际上是一个迭代器,可以提供参数stream=True并换用for的方式迭代 - prompt=prompt - ) # ask方法接收的详细参数请查看源码 + if self.config_data["type"] == "web": + if self.parentId: + ret = self.chatbot.ask(prompt=prompt, parentId=self.parentId) + else: + ret = self.chatbot.ask(prompt=prompt) + + # logging.info(ret) + + # 是否启用上下文记忆 + if self.config_data['history_enable']: + self.parentId = ret['msgId'] + resp_content = ret['content'][0] + + return resp_content + elif self.config_data["type"] == "api": + from http import HTTPStatus + from dashscope import Generation + from dashscope.api_entities.dashscope_response import Role + + if self.config_data['history_enable'] == False: + messages = [{'role': Role.SYSTEM, 'content': self.config_data["preset"]}, + {'role': Role.USER, 'content': prompt}] + else: + messages = copy.copy(self.history) + messages.append({'role': Role.USER, 'content': prompt}) + messages.insert(0, {'role': Role.SYSTEM, 'content': self.config_data["preset"]}) + + logging.debug(f"messages={messages}") + + response = Generation.call( + Generation.Models.qwen_max, + messages=messages, + result_format='message', # set the result to be "message" format. ) - - return ret["content"][0] + if response.status_code == HTTPStatus.OK: + logging.debug(response) + + resp_content = response.output.choices[0]['message']['content'] + + if self.config_data['history_enable']: + self.history.append({'role': Role.USER, 'content': prompt}) + self.history.append({'role': response.output.choices[0]['message']['role'], + 'content': resp_content}) + while True: + # 获取嵌套列表中所有字符串的字符数 + total_chars = sum(len(item['content']) for item in self.history if 'content' in item) + # 如果大于限定最大历史数,就剔除第一个元素 + if total_chars > int(self.config_data["history_max_len"]): + self.history.pop(0) + self.history.pop(0) + else: + break + + return resp_content + else: + logging.error(f'Request id: {response.request_id}, Status code: {response.status_code}, error code: {response.code}, error message: {response.message}') + return None except Exception as e: logging.error(traceback.format_exc()) return None @@ -64,19 +120,23 @@ def get_resp(self, prompt): if __name__ == '__main__': # 配置日志输出格式 logging.basicConfig( - level=logging.DEBUG, # 设置日志级别,可以根据需求调整 + level=logging.INFO, # 设置日志级别,可以根据需求调整 format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) data = { "cookie_path": 'cookies.json', - "type": 'web' + "type": 'api', + "preset": "你是一个专业的虚拟主播", + "api_key": "sk-", + "history_enable": True, + "history_max_len": 20, } tongyi = TongYi(data) - logging.info(tongyi.get_resp("你可以扮演猫娘吗,每句话后面加个喵")) - logging.info(tongyi.get_resp("早上好")) + logging.info(tongyi.get_resp("你现在叫小伊,是个猫娘,每句话后面加个喵")) + logging.info(tongyi.get_resp("早上好,你叫什么")) \ No newline at end of file diff --git a/utils/my_handle.py b/utils/my_handle.py index ee1c50d0..5e6ecdfa 100644 --- a/utils/my_handle.py +++ b/utils/my_handle.py @@ -2277,6 +2277,7 @@ def process_data(self, data, timer_flag): if hasattr(self.timers[timer_flag], 'last_data'): self.timers[timer_flag].last_data.append(data) # 这里需要注意配置命名!!! + # 保留数据数量 if len(self.timers[timer_flag].last_data) > int(My_handle.config.get("filter", timer_flag + "_forget_reserve_num")): self.timers[timer_flag].last_data.pop(0) else: @@ -2285,7 +2286,7 @@ def process_data(self, data, timer_flag): def process_last_data(self, timer_flag): with self.data_lock: timer = self.timers.get(timer_flag) - if timer and timer.last_data is not None and timer.last_data != []: + if timer and timer.项3 is not None and timer.last_data != []: logging.debug(f"预处理定时器触发 type={timer_flag},data={timer.last_data}") My_handle.is_handleing = 1 diff --git a/webui.py b/webui.py index 69cc3114..fae43f62 100644 --- a/webui.py +++ b/webui.py @@ -1165,6 +1165,10 @@ def common_textarea_handle(content): if config.get("webui", "show_card", "llm", "tongyi"): config_data["tongyi"]["type"] = select_tongyi_type.value config_data["tongyi"]["cookie_path"] = input_tongyi_cookie_path.value + config_data["tongyi"]["api_key"] = input_tongyi_api_key.value + config_data["tongyi"]["preset"] = input_tongyi_preset.value + config_data["tongyi"]["history_enable"] = switch_tongyi_history_enable.value + config_data["tongyi"]["history_max_len"] = int(input_tongyi_history_max_len.value) if config.get("webui", "show_card", "llm", "tongyixingchen"): config_data["tongyixingchen"]["access_token"] = input_tongyixingchen_access_token.value @@ -2733,7 +2737,7 @@ def common_textarea_handle(content): ui.label("QAnything") with ui.row(): select_qanything_type = ui.select( - label='模型', + label='类型', options={'online': '在线API', 'local': '本地API'}, value=config.get("qanything", "type") ).style("width:200px") @@ -2773,17 +2777,25 @@ def common_textarea_handle(content): with ui.card().style(card_css): ui.label("通义千问") with ui.row(): - lines = ['web'] + lines = ['web', 'api'] data_json = {} for line in lines: data_json[line] = line select_tongyi_type = ui.select( - label='模型', + label='类型', options=data_json, value=config.get("tongyi", "type") - ) - input_tongyi_cookie_path = ui.input(label='cookie路径', placeholder='通义千问登录后,通过浏览器插件Cookie Editor获取Cookie JSON串,然后将数据保存在这个路径的文件中', value=config.get("tongyi", "cookie_path")) + ).style("width:100px") + input_tongyi_cookie_path = ui.input(label='cookie路径', placeholder='web类型下,通义千问登录后,通过浏览器插件Cookie Editor获取Cookie JSON串,然后将数据保存在这个路径的文件中', value=config.get("tongyi", "cookie_path")) input_tongyi_cookie_path.style("width:400px") + with ui.row(): + input_tongyi_api_key = ui.input(label='密钥', value=config.get("tongyi", "api_key"), placeholder='API类型下,DashScope平台申请的API密钥') + input_tongyi_preset = ui.input(label='预设', placeholder='API类型下,用于指定一组预定义的设置,以便模型更好地适应特定的对话场景。', value=config.get("tongyi", "preset")).style("width:600px") + + with ui.row(): + switch_tongyi_history_enable = ui.switch('上下文记忆', value=config.get("tongyi", "history_enable")).style(switch_internal_css) + input_tongyi_history_max_len = ui.input(label='最大记忆长度', value=config.get("tongyi", "history_max_len"), placeholder='最长能记忆的问答字符串长度,超长会丢弃最早记忆的内容,请慎用!配置过大可能会有丢大米') + with ui.tab_panel(tts_page).style(tab_panel_css): # 通用-合成试听音频 async def tts_common_audio_synthesis():