Skip to content

Commit 31ac80a

Browse files
authored
Merge pull request #1851 from wayshall/qwen-dashscope
feat: 通义千问使用新版的sdk实现
2 parents c889645 + 5e399c4 commit 31ac80a

File tree

7 files changed

+186
-2
lines changed

7 files changed

+186
-2
lines changed

bot/bot_factory.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ def create_bot(bot_type):
5050
elif bot_type == const.QWEN:
5151
from bot.ali.ali_qwen_bot import AliQwenBot
5252
return AliQwenBot()
53-
53+
elif bot_type == const.QWEN_DASHSCOPE:
54+
from bot.dashscope.dashscope_bot import DashscopeBot
55+
return DashscopeBot()
5456
elif bot_type == const.GEMINI:
5557
from bot.gemini.google_gemini_bot import GoogleGeminiBot
5658
return GoogleGeminiBot()

bot/dashscope/dashscope_bot.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# encoding:utf-8
2+
3+
from bot.bot import Bot
4+
from bot.session_manager import SessionManager
5+
from bridge.context import ContextType
6+
from bridge.reply import Reply, ReplyType
7+
from common.log import logger
8+
from config import conf, load_config
9+
from .dashscope_session import DashscopeSession
10+
import os
11+
import dashscope
12+
from http import HTTPStatus
13+
14+
15+
16+
dashscope_models = {
17+
"qwen-turbo": dashscope.Generation.Models.qwen_turbo,
18+
"qwen-plus": dashscope.Generation.Models.qwen_plus,
19+
"qwen-max": dashscope.Generation.Models.qwen_max,
20+
"qwen-bailian-v1": dashscope.Generation.Models.bailian_v1
21+
}
22+
# ZhipuAI对话模型API
23+
class DashscopeBot(Bot):
24+
def __init__(self):
25+
super().__init__()
26+
self.sessions = SessionManager(DashscopeSession, model=conf().get("model") or "qwen-plus")
27+
self.model_name = conf().get("model") or "qwen-plus"
28+
self.api_key = conf().get("dashscope_api_key")
29+
os.environ["DASHSCOPE_API_KEY"] = self.api_key
30+
self.client = dashscope.Generation
31+
32+
def reply(self, query, context=None):
33+
# acquire reply content
34+
if context.type == ContextType.TEXT:
35+
logger.info("[DASHSCOPE] query={}".format(query))
36+
37+
session_id = context["session_id"]
38+
reply = None
39+
clear_memory_commands = conf().get("clear_memory_commands", ["#清除记忆"])
40+
if query in clear_memory_commands:
41+
self.sessions.clear_session(session_id)
42+
reply = Reply(ReplyType.INFO, "记忆已清除")
43+
elif query == "#清除所有":
44+
self.sessions.clear_all_session()
45+
reply = Reply(ReplyType.INFO, "所有人记忆已清除")
46+
elif query == "#更新配置":
47+
load_config()
48+
reply = Reply(ReplyType.INFO, "配置已更新")
49+
if reply:
50+
return reply
51+
session = self.sessions.session_query(query, session_id)
52+
logger.debug("[DASHSCOPE] session query={}".format(session.messages))
53+
54+
reply_content = self.reply_text(session)
55+
logger.debug(
56+
"[DASHSCOPE] new_query={}, session_id={}, reply_cont={}, completion_tokens={}".format(
57+
session.messages,
58+
session_id,
59+
reply_content["content"],
60+
reply_content["completion_tokens"],
61+
)
62+
)
63+
if reply_content["completion_tokens"] == 0 and len(reply_content["content"]) > 0:
64+
reply = Reply(ReplyType.ERROR, reply_content["content"])
65+
elif reply_content["completion_tokens"] > 0:
66+
self.sessions.session_reply(reply_content["content"], session_id, reply_content["total_tokens"])
67+
reply = Reply(ReplyType.TEXT, reply_content["content"])
68+
else:
69+
reply = Reply(ReplyType.ERROR, reply_content["content"])
70+
logger.debug("[DASHSCOPE] reply {} used 0 tokens.".format(reply_content))
71+
return reply
72+
else:
73+
reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type))
74+
return reply
75+
76+
def reply_text(self, session: DashscopeSession, retry_count=0) -> dict:
77+
"""
78+
call openai's ChatCompletion to get the answer
79+
:param session: a conversation session
80+
:param session_id: session id
81+
:param retry_count: retry count
82+
:return: {}
83+
"""
84+
try:
85+
dashscope.api_key = self.api_key
86+
response = self.client.call(
87+
dashscope_models[self.model_name],
88+
messages=session.messages,
89+
result_format="message"
90+
)
91+
if response.status_code == HTTPStatus.OK:
92+
content = response.output.choices[0]["message"]["content"]
93+
return {
94+
"total_tokens": response.usage["total_tokens"],
95+
"completion_tokens": response.usage["output_tokens"],
96+
"content": content,
97+
}
98+
else:
99+
logger.error('Request id: %s, Status code: %s, error code: %s, error message: %s' % (
100+
response.request_id, response.status_code,
101+
response.code, response.message
102+
))
103+
result = {"completion_tokens": 0, "content": "我现在有点累了,等会再来吧"}
104+
need_retry = retry_count < 2
105+
result = {"completion_tokens": 0, "content": "我现在有点累了,等会再来吧"}
106+
if need_retry:
107+
return self.reply_text(session, retry_count + 1)
108+
else:
109+
return result
110+
except Exception as e:
111+
logger.exception(e)
112+
need_retry = retry_count < 2
113+
result = {"completion_tokens": 0, "content": "我现在有点累了,等会再来吧"}
114+
if need_retry:
115+
return self.reply_text(session, retry_count + 1)
116+
else:
117+
return result

bot/dashscope/dashscope_session.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from bot.session_manager import Session
2+
from common.log import logger
3+
4+
5+
class DashscopeSession(Session):
6+
def __init__(self, session_id, system_prompt=None, model="qwen-turbo"):
7+
super().__init__(session_id)
8+
self.reset()
9+
10+
def discard_exceeding(self, max_tokens, cur_tokens=None):
11+
precise = True
12+
try:
13+
cur_tokens = self.calc_tokens()
14+
except Exception as e:
15+
precise = False
16+
if cur_tokens is None:
17+
raise e
18+
logger.debug("Exception when counting tokens precisely for query: {}".format(e))
19+
while cur_tokens > max_tokens:
20+
if len(self.messages) > 2:
21+
self.messages.pop(1)
22+
elif len(self.messages) == 2 and self.messages[1]["role"] == "assistant":
23+
self.messages.pop(1)
24+
if precise:
25+
cur_tokens = self.calc_tokens()
26+
else:
27+
cur_tokens = cur_tokens - max_tokens
28+
break
29+
elif len(self.messages) == 2 and self.messages[1]["role"] == "user":
30+
logger.warn("user message exceed max_tokens. total_tokens={}".format(cur_tokens))
31+
break
32+
else:
33+
logger.debug("max_tokens={}, total_tokens={}, len(messages)={}".format(max_tokens, cur_tokens,
34+
len(self.messages)))
35+
break
36+
if precise:
37+
cur_tokens = self.calc_tokens()
38+
else:
39+
cur_tokens = cur_tokens - max_tokens
40+
return cur_tokens
41+
42+
def calc_tokens(self):
43+
return num_tokens_from_messages(self.messages)
44+
45+
46+
def num_tokens_from_messages(messages):
47+
# 只是大概,具体计算规则:https://help.aliyun.com/zh/dashscope/developer-reference/token-api?spm=a2c4g.11186623.0.0.4d8b12b0BkP3K9
48+
tokens = 0
49+
for msg in messages:
50+
tokens += len(msg["content"])
51+
return tokens

bridge/bridge.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def __init__(self):
3030
self.btype["chat"] = const.XUNFEI
3131
if model_type in [const.QWEN]:
3232
self.btype["chat"] = const.QWEN
33+
if model_type in [const.QWEN_TURBO, const.QWEN_PLUS, const.QWEN_MAX]:
34+
self.btype["chat"] = const.QWEN_DASHSCOPE
3335
if model_type in [const.GEMINI]:
3436
self.btype["chat"] = const.GEMINI
3537
if model_type in [const.ZHIPU_AI]:

common/const.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
CLAUDEAI = "claude"
99
CLAUDEAPI= "claudeAPI"
1010
QWEN = "qwen"
11+
12+
QWEN_DASHSCOPE = "dashscope"
13+
QWEN_TURBO = "qwen-turbo"
14+
QWEN_PLUS = "qwen-plus"
15+
QWEN_MAX = "qwen-max"
16+
1117
GEMINI = "gemini"
1218
ZHIPU_AI = "glm-4"
1319
MOONSHOT = "moonshot"
@@ -24,7 +30,8 @@
2430
TTS_1_HD = "tts-1-hd"
2531

2632
MODEL_LIST = ["gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-4", "wenxin", "wenxin-4", "xunfei", "claude","claude-3-opus-20240229", "gpt-4-turbo",
27-
"gpt-4-turbo-preview", "gpt-4-1106-preview", GPT4_TURBO_PREVIEW, QWEN, GEMINI, ZHIPU_AI, MOONSHOT]
33+
"gpt-4-turbo-preview", "gpt-4-1106-preview", GPT4_TURBO_PREVIEW, QWEN, GEMINI, ZHIPU_AI, MOONSHOT,
34+
QWEN_TURBO, QWEN_PLUS, QWEN_MAX]
2835

2936
# channel
3037
FEISHU = "feishu"

config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@
7575
"qwen_agent_key": "",
7676
"qwen_app_id": "",
7777
"qwen_node_id": "", # 流程编排模型用到的id,如果没有用到qwen_node_id,请务必保持为空字符串
78+
# 阿里灵积模型api key
79+
"dashscope_api_key": "",
7880
# Google Gemini Api Key
7981
"gemini_api_key": "",
8082
# wework的通用配置

requirements-optional.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,6 @@ dingtalk_stream
4141

4242
# zhipuai
4343
zhipuai>=2.0.1
44+
45+
# tongyi qwen new sdk
46+
dashscope

0 commit comments

Comments
 (0)