mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-09 07:16:04 +00:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd0438df76 | ||
|
|
9ca1fc59ef | ||
|
|
84a80a5ec8 | ||
|
|
4b2e248646 | ||
|
|
b90e45590a | ||
|
|
ff93d563a8 | ||
|
|
53228498ed | ||
|
|
8ece82e43a | ||
|
|
8b4684675e | ||
|
|
8cca12fff2 | ||
|
|
a74111612e | ||
|
|
c7799a65c4 | ||
|
|
aabb01c50f | ||
|
|
95e2ada965 | ||
|
|
3fe7d53c76 | ||
|
|
e8634bb1ab | ||
|
|
dbe46b5770 | ||
|
|
6d9fba30b1 | ||
|
|
6a866bf871 | ||
|
|
3c961e4652 | ||
|
|
7abd999420 | ||
|
|
fca8fbb135 | ||
|
|
c67caf18df | ||
|
|
fe956fe4a5 | ||
|
|
0e52f679a2 | ||
|
|
b9500283ec | ||
|
|
441b69b528 | ||
|
|
898bcdc96b | ||
|
|
02bc1fc45e | ||
|
|
5585981dc3 | ||
|
|
a4777f194b | ||
|
|
41aeda8dc0 | ||
|
|
2ed522667e | ||
|
|
1932444666 | ||
|
|
b49b7e963d | ||
|
|
435c11ff27 | ||
|
|
2e93600437 | ||
|
|
faecb70d0f | ||
|
|
92e1ac5c3a | ||
|
|
8963a2117b | ||
|
|
aa300258ab | ||
|
|
48841daff5 | ||
|
|
8878f1ed87 | ||
|
|
f6205d79c0 | ||
|
|
d6d5dac6b3 | ||
|
|
05b979e68a | ||
|
|
9f7d9e4c0d |
16
README.md
16
README.md
@@ -9,7 +9,7 @@
|
|||||||
<a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
<a href="https://docs.langbot.app">项目主页</a> |
|
<a href="https://docs.langbot.app">项目主页</a> |
|
||||||
<a href="https://docs.langbot.app/insight/intro.htmll">功能介绍</a> |
|
<a href="https://docs.langbot.app/insight/intro.html">功能介绍</a> |
|
||||||
<a href="https://docs.langbot.app/insight/guide.html">部署文档</a> |
|
<a href="https://docs.langbot.app/insight/guide.html">部署文档</a> |
|
||||||
<a href="https://docs.langbot.app/usage/faq.html">常见问题</a> |
|
<a href="https://docs.langbot.app/usage/faq.html">常见问题</a> |
|
||||||
<a href="https://docs.langbot.app/plugin/plugin-intro.html">插件介绍</a> |
|
<a href="https://docs.langbot.app/plugin/plugin-intro.html">插件介绍</a> |
|
||||||
@@ -115,6 +115,20 @@
|
|||||||
| [阿里云百炼](https://bailian.console.aliyun.com/) | ✅ | 大模型聚合平台, LLMOps 平台 |
|
| [阿里云百炼](https://bailian.console.aliyun.com/) | ✅ | 大模型聚合平台, LLMOps 平台 |
|
||||||
| [火山方舟](https://console.volcengine.com/ark/region:ark+cn-beijing/model?vendor=Bytedance&view=LIST_VIEW) | ✅ | 大模型聚合平台, LLMOps 平台 |
|
| [火山方舟](https://console.volcengine.com/ark/region:ark+cn-beijing/model?vendor=Bytedance&view=LIST_VIEW) | ✅ | 大模型聚合平台, LLMOps 平台 |
|
||||||
|
|
||||||
|
### TTS
|
||||||
|
|
||||||
|
| 平台/模型 | 备注 |
|
||||||
|
| --- | --- |
|
||||||
|
| [FishAudio](https://fish.audio/zh-CN/discovery/) | [插件](https://github.com/the-lazy-me/NewChatVoice) |
|
||||||
|
| [海豚 AI](https://www.ttson.cn/?source=thelazy) | [插件](https://github.com/the-lazy-me/NewChatVoice) |
|
||||||
|
| [AzureTTS](https://portal.azure.com/) | [插件](https://github.com/Ingnaryk/LangBot_AzureTTS) |
|
||||||
|
|
||||||
|
### 文生图
|
||||||
|
|
||||||
|
| 平台/模型 | 备注 |
|
||||||
|
| --- | --- |
|
||||||
|
| 阿里云百炼 | [插件](https://github.com/Thetail001/LangBot_BailianTextToImagePlugin)
|
||||||
|
|
||||||
## 😘 社区贡献
|
## 😘 社区贡献
|
||||||
|
|
||||||
LangBot 离不开以下贡献者和社区内所有人的贡献,我们欢迎任何形式的贡献和反馈。
|
LangBot 离不开以下贡献者和社区内所有人的贡献,我们欢迎任何形式的贡献和反馈。
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
<a href="https://docs.langbot.app">Home</a> |
|
<a href="https://docs.langbot.app">Home</a> |
|
||||||
<a href="https://docs.langbot.app/insight/intro.htmll">Features</a> |
|
<a href="https://docs.langbot.app/insight/intro.html">Features</a> |
|
||||||
<a href="https://docs.langbot.app/insight/guide.html">Deployment</a> |
|
<a href="https://docs.langbot.app/insight/guide.html">Deployment</a> |
|
||||||
<a href="https://docs.langbot.app/usage/faq.html">FAQ</a> |
|
<a href="https://docs.langbot.app/usage/faq.html">FAQ</a> |
|
||||||
<a href="https://docs.langbot.app/plugin/plugin-intro.html">Plugin</a> |
|
<a href="https://docs.langbot.app/plugin/plugin-intro.html">Plugin</a> |
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
<a href="https://docs.langbot.app">ホーム</a> |
|
<a href="https://docs.langbot.app">ホーム</a> |
|
||||||
<a href="https://docs.langbot.app/insight/intro.htmll">機能</a> |
|
<a href="https://docs.langbot.app/insight/intro.html">機能</a> |
|
||||||
<a href="https://docs.langbot.app/insight/guide.html">デプロイ</a> |
|
<a href="https://docs.langbot.app/insight/guide.html">デプロイ</a> |
|
||||||
<a href="https://docs.langbot.app/usage/faq.html">FAQ</a> |
|
<a href="https://docs.langbot.app/usage/faq.html">FAQ</a> |
|
||||||
<a href="https://docs.langbot.app/plugin/plugin-intro.html">プラグイン</a> |
|
<a href="https://docs.langbot.app/plugin/plugin-intro.html">プラグイン</a> |
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class EchoTextHandler(dingtalk_stream.ChatbotHandler):
|
|||||||
"""异步等待消息的到来"""
|
"""异步等待消息的到来"""
|
||||||
while self.incoming_message is None:
|
while self.incoming_message is None:
|
||||||
await asyncio.sleep(0.1) # 异步等待,避免阻塞
|
await asyncio.sleep(0.1) # 异步等待,避免阻塞
|
||||||
|
|
||||||
return self.incoming_message
|
return self.incoming_message
|
||||||
|
|
||||||
async def get_dingtalk_client(client_id, client_secret):
|
async def get_dingtalk_client(client_id, client_secret):
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ class DingTalkClient:
|
|||||||
|
|
||||||
async def get_message(self,incoming_message:dingtalk_stream.chatbot.ChatbotMessage):
|
async def get_message(self,incoming_message:dingtalk_stream.chatbot.ChatbotMessage):
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# print(json.dumps(incoming_message.to_dict(), indent=4, ensure_ascii=False))
|
# print(json.dumps(incoming_message.to_dict(), indent=4, ensure_ascii=False))
|
||||||
message_data = {
|
message_data = {
|
||||||
"IncomingMessage":incoming_message,
|
"IncomingMessage":incoming_message,
|
||||||
@@ -197,6 +198,53 @@ class DingTalkClient:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
return message_data
|
return message_data
|
||||||
|
|
||||||
|
async def send_proactive_message_to_one(self,target_id:str,content:str):
|
||||||
|
if not await self.check_access_token():
|
||||||
|
await self.get_access_token()
|
||||||
|
|
||||||
|
url = 'https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend'
|
||||||
|
|
||||||
|
headers ={
|
||||||
|
"x-acs-dingtalk-access-token":self.access_token,
|
||||||
|
"Content-Type":"application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
data ={
|
||||||
|
"robotCode":self.robot_code,
|
||||||
|
"userIds":[target_id],
|
||||||
|
"msgKey": "sampleText",
|
||||||
|
"msgParam": json.dumps({"content":content}),
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.post(url,headers=headers,json=data)
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
|
async def send_proactive_message_to_group(self,target_id:str,content:str):
|
||||||
|
if not await self.check_access_token():
|
||||||
|
await self.get_access_token()
|
||||||
|
|
||||||
|
url = 'https://api.dingtalk.com/v1.0/robot/groupMessages/send'
|
||||||
|
|
||||||
|
headers ={
|
||||||
|
"x-acs-dingtalk-access-token":self.access_token,
|
||||||
|
"Content-Type":"application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
data ={
|
||||||
|
"robotCode":self.robot_code,
|
||||||
|
"openConversationId":target_id,
|
||||||
|
"msgKey": "sampleText",
|
||||||
|
"msgParam": json.dumps({"content":content}),
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient() as client:
|
||||||
|
response = await client.post(url,headers=headers,json=data)
|
||||||
|
except Exception:
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
"""启动 WebSocket 连接,监听消息"""
|
"""启动 WebSocket 连接,监听消息"""
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
# 微信公众号的加解密算法与企业微信一样,所以直接使用企业微信的加解密算法文件
|
# 微信公众号的加解密算法与企业微信一样,所以直接使用企业微信的加解密算法文件
|
||||||
|
from collections import deque
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from ..wecom_api.WXBizMsgCrypt3 import WXBizMsgCrypt
|
from ..wecom_api.WXBizMsgCrypt3 import WXBizMsgCrypt
|
||||||
@@ -12,6 +13,7 @@ import httpx
|
|||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
|
from pkg.platform.sources import officialaccount as oa
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -25,6 +27,8 @@ xml_template = """
|
|||||||
</xml>
|
</xml>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
user_msg_queue = {}
|
||||||
|
|
||||||
class OAClient():
|
class OAClient():
|
||||||
|
|
||||||
def __init__(self,token:str,EncodingAESKey:str,AppID:str,Appsecret:str):
|
def __init__(self,token:str,EncodingAESKey:str,AppID:str,Appsecret:str):
|
||||||
@@ -169,6 +173,152 @@ class OAClient():
|
|||||||
await handler(event)
|
await handler(event)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class OAClientForLongerResponse():
|
||||||
|
|
||||||
|
def __init__(self,token:str,EncodingAESKey:str,AppID:str,Appsecret:str):
|
||||||
|
self.token = token
|
||||||
|
self.aes = EncodingAESKey
|
||||||
|
self.appid = AppID
|
||||||
|
self.appsecret = Appsecret
|
||||||
|
self.base_url = 'https://api.weixin.qq.com'
|
||||||
|
self.access_token = ''
|
||||||
|
self.app = Quart(__name__)
|
||||||
|
self.app.add_url_rule('/callback/command', 'handle_callback', self.handle_callback_request, methods=['GET', 'POST'])
|
||||||
|
self._message_handlers = {
|
||||||
|
"example":[],
|
||||||
|
}
|
||||||
|
self.access_token_expiry_time = None
|
||||||
|
|
||||||
|
async def handle_callback_request(self):
|
||||||
|
try:
|
||||||
|
start_time = time.time()
|
||||||
|
signature = request.args.get("signature", "")
|
||||||
|
timestamp = request.args.get("timestamp", "")
|
||||||
|
nonce = request.args.get("nonce", "")
|
||||||
|
echostr = request.args.get("echostr", "")
|
||||||
|
msg_signature = request.args.get("msg_signature", "")
|
||||||
|
|
||||||
|
if msg_signature is None:
|
||||||
|
raise Exception("msg_signature不在请求体中")
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
check_str = "".join(sorted([self.token, timestamp, nonce]))
|
||||||
|
check_signature = hashlib.sha1(check_str.encode("utf-8")).hexdigest()
|
||||||
|
return echostr if check_signature == signature else "拒绝请求"
|
||||||
|
|
||||||
|
elif request.method == "POST":
|
||||||
|
encryt_msg = await request.data
|
||||||
|
wxcpt = WXBizMsgCrypt(self.token, self.aes, self.appid)
|
||||||
|
ret, xml_msg = wxcpt.DecryptMsg(encryt_msg, msg_signature, timestamp, nonce)
|
||||||
|
xml_msg = xml_msg.decode('utf-8')
|
||||||
|
|
||||||
|
if ret != 0:
|
||||||
|
raise Exception("消息解密失败")
|
||||||
|
|
||||||
|
# 解析 XML
|
||||||
|
root = ET.fromstring(xml_msg)
|
||||||
|
from_user = root.find("FromUserName").text
|
||||||
|
to_user = root.find("ToUserName").text
|
||||||
|
|
||||||
|
|
||||||
|
from pkg.platform.sources import officialaccount as oa
|
||||||
|
|
||||||
|
|
||||||
|
if oa.msg_queue.get(from_user) and oa.msg_queue[from_user][0]["content"]:
|
||||||
|
queue_top = oa.msg_queue[from_user].pop(0)
|
||||||
|
queue_content = queue_top["content"]
|
||||||
|
|
||||||
|
# 弹出用户消息
|
||||||
|
if user_msg_queue.get(from_user) and user_msg_queue[from_user]:
|
||||||
|
user_msg_queue[from_user].pop(0)
|
||||||
|
|
||||||
|
response_xml = xml_template.format(
|
||||||
|
to_user=from_user,
|
||||||
|
from_user=to_user,
|
||||||
|
create_time=int(time.time()),
|
||||||
|
content=queue_content
|
||||||
|
)
|
||||||
|
return response_xml
|
||||||
|
|
||||||
|
else:
|
||||||
|
response_xml = xml_template.format(
|
||||||
|
to_user=from_user,
|
||||||
|
from_user=to_user,
|
||||||
|
create_time=int(time.time()),
|
||||||
|
content="AI正在思考中,请发送任意内容获取回答。"
|
||||||
|
)
|
||||||
|
|
||||||
|
if user_msg_queue.get(from_user) and user_msg_queue[from_user][0]["content"]:
|
||||||
|
return response_xml
|
||||||
|
else:
|
||||||
|
message_data = await self.get_message(xml_msg)
|
||||||
|
|
||||||
|
if message_data:
|
||||||
|
event = OAEvent.from_payload(message_data)
|
||||||
|
if event:
|
||||||
|
user_msg_queue.setdefault(from_user,[]).append(
|
||||||
|
{
|
||||||
|
"content":event.message,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await self._handle_message(event)
|
||||||
|
|
||||||
|
return response_xml
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def get_message(self, xml_msg: str):
|
||||||
|
|
||||||
|
root = ET.fromstring(xml_msg)
|
||||||
|
|
||||||
|
message_data = {
|
||||||
|
"ToUserName": root.find("ToUserName").text,
|
||||||
|
"FromUserName": root.find("FromUserName").text,
|
||||||
|
"CreateTime": int(root.find("CreateTime").text),
|
||||||
|
"MsgType": root.find("MsgType").text,
|
||||||
|
"Content": root.find("Content").text if root.find("Content") is not None else None,
|
||||||
|
"MsgId": int(root.find("MsgId").text) if root.find("MsgId") is not None else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
return message_data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def run_task(self, host: str, port: int, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
启动 Quart 应用。
|
||||||
|
"""
|
||||||
|
await self.app.run_task(host=host, port=port, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def on_message(self, msg_type: str):
|
||||||
|
"""
|
||||||
|
注册消息类型处理器。
|
||||||
|
"""
|
||||||
|
def decorator(func: Callable[[OAEvent], None]):
|
||||||
|
if msg_type not in self._message_handlers:
|
||||||
|
self._message_handlers[msg_type] = []
|
||||||
|
self._message_handlers[msg_type].append(func)
|
||||||
|
return func
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
async def _handle_message(self, event: OAEvent):
|
||||||
|
"""
|
||||||
|
处理消息事件。
|
||||||
|
"""
|
||||||
|
|
||||||
|
msg_type = event.type
|
||||||
|
if msg_type in self._message_handlers:
|
||||||
|
for handler in self._message_handlers[msg_type]:
|
||||||
|
await handler(event)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class WecomClient():
|
|||||||
else:
|
else:
|
||||||
raise Exception("未获取用户")
|
raise Exception("未获取用户")
|
||||||
|
|
||||||
async def send_to_all(self,content:str):
|
async def send_to_all(self,content:str,agent_id:int):
|
||||||
if not self.check_access_token_for_contacts():
|
if not self.check_access_token_for_contacts():
|
||||||
self.access_token_for_contacts = await self.get_access_token(self.secret_for_contacts)
|
self.access_token_for_contacts = await self.get_access_token(self.secret_for_contacts)
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ class WecomClient():
|
|||||||
params = {
|
params = {
|
||||||
"touser" : user_ids_string,
|
"touser" : user_ids_string,
|
||||||
"msgtype" : "text",
|
"msgtype" : "text",
|
||||||
"agentid" : 1000002,
|
"agentid" : agent_id,
|
||||||
"text" : {
|
"text" : {
|
||||||
"content" : content,
|
"content" : content,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ class Query(pydantic.BaseModel):
|
|||||||
user_message: typing.Optional[llm_entities.Message] = None
|
user_message: typing.Optional[llm_entities.Message] = None
|
||||||
"""此次请求的用户消息对象,由前置处理器阶段设置"""
|
"""此次请求的用户消息对象,由前置处理器阶段设置"""
|
||||||
|
|
||||||
|
variables: typing.Optional[dict[str, typing.Any]] = None
|
||||||
|
"""变量,由前置处理器阶段设置。在prompt中嵌入或由 Runner 传递到 LLMOps 平台。"""
|
||||||
|
|
||||||
use_model: typing.Optional[entities.LLMModelInfo] = None
|
use_model: typing.Optional[entities.LLMModelInfo] = None
|
||||||
"""使用的模型,由前置处理器阶段设置"""
|
"""使用的模型,由前置处理器阶段设置"""
|
||||||
|
|
||||||
@@ -86,10 +89,31 @@ class Query(pydantic.BaseModel):
|
|||||||
|
|
||||||
# ======= 内部保留 =======
|
# ======= 内部保留 =======
|
||||||
current_stage: "pkg.pipeline.stagemgr.StageInstContainer" = None
|
current_stage: "pkg.pipeline.stagemgr.StageInstContainer" = None
|
||||||
|
"""当前所处阶段"""
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
arbitrary_types_allowed = True
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
|
# ========== 插件可调用的 API(请求 API) ==========
|
||||||
|
|
||||||
|
def set_variable(self, key: str, value: typing.Any):
|
||||||
|
"""设置变量"""
|
||||||
|
if self.variables is None:
|
||||||
|
self.variables = {}
|
||||||
|
self.variables[key] = value
|
||||||
|
|
||||||
|
def get_variable(self, key: str) -> typing.Any:
|
||||||
|
"""获取变量"""
|
||||||
|
if self.variables is None:
|
||||||
|
return None
|
||||||
|
return self.variables.get(key)
|
||||||
|
|
||||||
|
def get_variables(self) -> dict[str, typing.Any]:
|
||||||
|
"""获取所有变量"""
|
||||||
|
if self.variables is None:
|
||||||
|
return {}
|
||||||
|
return self.variables
|
||||||
|
|
||||||
|
|
||||||
class Conversation(pydantic.BaseModel):
|
class Conversation(pydantic.BaseModel):
|
||||||
"""对话,包含于 Session 中,一个 Session 可以有多个历史 Conversation,但只有一个当前使用的 Conversation"""
|
"""对话,包含于 Session 中,一个 Session 可以有多个历史 Conversation,但只有一个当前使用的 Conversation"""
|
||||||
|
|||||||
26
pkg/core/migrations/m035_wxoa_mode.py
Normal file
26
pkg/core/migrations/m035_wxoa_mode.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .. import migration
|
||||||
|
|
||||||
|
|
||||||
|
@migration.migration_class("wxoa-mode", 35)
|
||||||
|
class WxoaModeMigration(migration.Migration):
|
||||||
|
"""迁移"""
|
||||||
|
|
||||||
|
async def need_migrate(self) -> bool:
|
||||||
|
"""判断当前环境是否需要运行此迁移"""
|
||||||
|
|
||||||
|
for adapter in self.ap.platform_cfg.data['platform-adapters']:
|
||||||
|
if adapter['adapter'] == 'officialaccount':
|
||||||
|
if 'Mode' not in adapter:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
"""执行迁移"""
|
||||||
|
for adapter in self.ap.platform_cfg.data['platform-adapters']:
|
||||||
|
if adapter['adapter'] == 'officialaccount':
|
||||||
|
if 'Mode' not in adapter:
|
||||||
|
adapter['Mode'] = 'drop'
|
||||||
|
|
||||||
|
await self.ap.platform_cfg.dump_config()
|
||||||
@@ -11,7 +11,7 @@ from ..migrations import m015_gitee_ai_config, m016_dify_service_api, m017_dify_
|
|||||||
from ..migrations import m020_wecom_config, m021_lark_config, m022_lmstudio_config, m023_siliconflow_config, m024_discord_config, m025_gewechat_config
|
from ..migrations import m020_wecom_config, m021_lark_config, m022_lmstudio_config, m023_siliconflow_config, m024_discord_config, m025_gewechat_config
|
||||||
from ..migrations import m026_qqofficial_config, m027_wx_official_account_config, m028_aliyun_requester_config
|
from ..migrations import m026_qqofficial_config, m027_wx_official_account_config, m028_aliyun_requester_config
|
||||||
from ..migrations import m029_dashscope_app_api_config, m030_lark_config_cmpl, m031_dingtalk_config, m032_volcark_config
|
from ..migrations import m029_dashscope_app_api_config, m030_lark_config_cmpl, m031_dingtalk_config, m032_volcark_config
|
||||||
from ..migrations import m033_dify_thinking_config, m034_gewechat_file_url_config
|
from ..migrations import m033_dify_thinking_config, m034_gewechat_file_url_config, m035_wxoa_mode
|
||||||
|
|
||||||
@stage.stage_class("MigrationStage")
|
@stage.stage_class("MigrationStage")
|
||||||
class MigrationStage(stage.BootingStage):
|
class MigrationStage(stage.BootingStage):
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
from .. import stage, entities, stagemgr
|
from .. import stage, entities, stagemgr
|
||||||
from ...core import entities as core_entities
|
from ...core import entities as core_entities
|
||||||
@@ -34,7 +35,7 @@ class PreProcessor(stage.PipelineStage):
|
|||||||
|
|
||||||
conversation = await self.ap.sess_mgr.get_conversation(session)
|
conversation = await self.ap.sess_mgr.get_conversation(session)
|
||||||
|
|
||||||
# 从会话取出消息和情景预设到query
|
# 设置query
|
||||||
query.session = session
|
query.session = session
|
||||||
query.prompt = conversation.prompt.copy()
|
query.prompt = conversation.prompt.copy()
|
||||||
query.messages = conversation.messages.copy()
|
query.messages = conversation.messages.copy()
|
||||||
@@ -43,6 +44,11 @@ class PreProcessor(stage.PipelineStage):
|
|||||||
|
|
||||||
query.use_funcs = conversation.use_funcs if query.use_model.tool_call_supported else None
|
query.use_funcs = conversation.use_funcs if query.use_model.tool_call_supported else None
|
||||||
|
|
||||||
|
query.variables = {
|
||||||
|
"session_id": f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
||||||
|
"conversation_id": conversation.uuid,
|
||||||
|
"msg_create_time": int(query.message_event.time) if query.message_event.time else int(datetime.datetime.now().timestamp()),
|
||||||
|
}
|
||||||
|
|
||||||
# 检查vision是否启用,没启用就删除所有图片
|
# 检查vision是否启用,没启用就删除所有图片
|
||||||
if not self.ap.provider_cfg.data['enable-vision'] or (self.ap.provider_cfg.data['runner'] == 'local-agent' and not query.use_model.vision_supported):
|
if not self.ap.provider_cfg.data['enable-vision'] or (self.ap.provider_cfg.data['runner'] == 'local-agent' and not query.use_model.vision_supported):
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class DingTalkEventConverter(adapter.EventConverter):
|
|||||||
remark=""
|
remark=""
|
||||||
),
|
),
|
||||||
message_chain = message_chain,
|
message_chain = message_chain,
|
||||||
time = datetime.datetime.now(),
|
time = event.incoming_message.create_at,
|
||||||
source_platform_object=event,
|
source_platform_object=event,
|
||||||
)
|
)
|
||||||
elif event.conversation == 'GroupMessage':
|
elif event.conversation == 'GroupMessage':
|
||||||
@@ -95,7 +95,7 @@ class DingTalkEventConverter(adapter.EventConverter):
|
|||||||
last_speak_timestamp=0,
|
last_speak_timestamp=0,
|
||||||
mute_time_remaining=0
|
mute_time_remaining=0
|
||||||
)
|
)
|
||||||
time = datetime.datetime.now(),
|
time = event.incoming_message.create_at
|
||||||
return platform_events.GroupMessage(
|
return platform_events.GroupMessage(
|
||||||
sender =sender,
|
sender =sender,
|
||||||
message_chain = message_chain,
|
message_chain = message_chain,
|
||||||
@@ -152,7 +152,11 @@ class DingTalkAdapter(adapter.MessagePlatformAdapter):
|
|||||||
async def send_message(
|
async def send_message(
|
||||||
self, target_type: str, target_id: str, message: platform_message.MessageChain
|
self, target_type: str, target_id: str, message: platform_message.MessageChain
|
||||||
):
|
):
|
||||||
pass
|
content = await DingTalkMessageConverter.yiri2target(message)
|
||||||
|
if target_type == 'person':
|
||||||
|
await self.bot.send_proactive_message_to_one(target_id,content)
|
||||||
|
if target_type == 'group':
|
||||||
|
await self.bot.send_proactive_message_to_group(target_id,content)
|
||||||
|
|
||||||
def register_listener(
|
def register_listener(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -62,20 +62,29 @@ class GewechatMessageConverter(adapter.MessageConverter):
|
|||||||
bot_account_id: str
|
bot_account_id: str
|
||||||
) -> platform_message.MessageChain:
|
) -> platform_message.MessageChain:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if message["Data"]["MsgType"] == 1:
|
if message["Data"]["MsgType"] == 1:
|
||||||
# 检查消息开头,如果有 wxid_sbitaz0mt65n22:\n 则删掉
|
# 检查消息开头,如果有 wxid_sbitaz0mt65n22:\n 则删掉
|
||||||
regex = re.compile(r"^wxid_.*:")
|
regex = re.compile(r"^wxid_.*:")
|
||||||
|
# print(message)
|
||||||
|
|
||||||
line_split = message["Data"]["Content"]["string"].split("\n")
|
line_split = message["Data"]["Content"]["string"].split("\n")
|
||||||
|
|
||||||
if len(line_split) > 0 and regex.match(line_split[0]):
|
if len(line_split) > 0 and regex.match(line_split[0]):
|
||||||
message["Data"]["Content"]["string"] = "\n".join(line_split[1:])
|
message["Data"]["Content"]["string"] = "\n".join(line_split[1:])
|
||||||
|
|
||||||
at_string = f'@{bot_account_id}'
|
# 正则表达式模式,匹配'@'后跟任意数量的非空白字符
|
||||||
|
pattern = r'@\S+'
|
||||||
|
at_string = f"@{bot_account_id}"
|
||||||
content_list = []
|
content_list = []
|
||||||
if at_string in message["Data"]["Content"]["string"]:
|
if at_string in message["Data"]["Content"]["string"]:
|
||||||
content_list.append(platform_message.At(target=bot_account_id))
|
content_list.append(platform_message.At(target=bot_account_id))
|
||||||
content_list.append(platform_message.Plain(message["Data"]["Content"]["string"].replace(at_string, "", 1)))
|
content_list.append(platform_message.Plain(message["Data"]["Content"]["string"].replace(at_string, '', 1)))
|
||||||
|
# 更优雅的替换改名后@机器人,仅仅限于单独AT的情况
|
||||||
|
elif '在群聊中@了你' in message["Data"]["PushContent"]:
|
||||||
|
content_list.append(platform_message.At(target=bot_account_id))
|
||||||
|
content_list.append(platform_message.Plain(re.sub(pattern, '', message["Data"]["Content"]["string"])))
|
||||||
else:
|
else:
|
||||||
content_list = [platform_message.Plain(message["Data"]["Content"]["string"])]
|
content_list = [platform_message.Plain(message["Data"]["Content"]["string"])]
|
||||||
|
|
||||||
@@ -227,6 +236,13 @@ class GeWeChatAdapter(adapter.MessagePlatformAdapter):
|
|||||||
async def gewechat_callback():
|
async def gewechat_callback():
|
||||||
data = await quart.request.json
|
data = await quart.request.json
|
||||||
# print(json.dumps(data, indent=4, ensure_ascii=False))
|
# print(json.dumps(data, indent=4, ensure_ascii=False))
|
||||||
|
|
||||||
|
if 'data' in data:
|
||||||
|
data['Data'] = data['data']
|
||||||
|
if 'type_name' in data:
|
||||||
|
data['TypeName'] = data['type_name']
|
||||||
|
# print(json.dumps(data, indent=4, ensure_ascii=False))
|
||||||
|
|
||||||
|
|
||||||
if 'testMsg' in data:
|
if 'testMsg' in data:
|
||||||
return 'ok'
|
return 'ok'
|
||||||
@@ -248,17 +264,29 @@ class GeWeChatAdapter(adapter.MessagePlatformAdapter):
|
|||||||
target_id: str,
|
target_id: str,
|
||||||
message: platform_message.MessageChain
|
message: platform_message.MessageChain
|
||||||
):
|
):
|
||||||
geweap_msg = await GewechatMessageConverter.yiri2target(message)
|
geweap_msg = await self.message_converter.yiri2target(message)
|
||||||
# 此处加上群消息at处理
|
# 此处加上群消息at处理
|
||||||
# ats = [item["target"] for item in geweap_msg if item["type"] == "at"]
|
ats = [item["target"] for item in geweap_msg if item["type"] == "at"]
|
||||||
|
|
||||||
|
|
||||||
for msg in geweap_msg:
|
for msg in geweap_msg:
|
||||||
|
# at主动发送消息
|
||||||
if msg['type'] == 'text':
|
if msg['type'] == 'text':
|
||||||
await self.bot.post_text(app_id=self.config['app_id'], to_wxid=target_id, content=msg['content'])
|
if ats:
|
||||||
|
member_info = self.bot.get_chatroom_member_detail(
|
||||||
|
self.config["app_id"],
|
||||||
|
target_id,
|
||||||
|
ats[::-1]
|
||||||
|
)["data"]
|
||||||
|
|
||||||
|
for member in member_info:
|
||||||
|
msg['content'] = f'@{member["nickName"]} {msg["content"]}'
|
||||||
|
self.bot.post_text(app_id=self.config['app_id'], to_wxid=target_id, content=msg['content'],
|
||||||
|
ats=",".join(ats))
|
||||||
|
|
||||||
elif msg['type'] == 'image':
|
elif msg['type'] == 'image':
|
||||||
|
|
||||||
await self.bot.post_image(app_id=self.config['app_id'], to_wxid=target_id, img_url=msg["image"])
|
self.bot.post_image(app_id=self.config['app_id'], to_wxid=target_id, img_url=msg["image"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -289,7 +317,7 @@ class GeWeChatAdapter(adapter.MessagePlatformAdapter):
|
|||||||
app_id=self.config["app_id"],
|
app_id=self.config["app_id"],
|
||||||
to_wxid=message_source.source_platform_object["Data"]["FromUserName"]["string"],
|
to_wxid=message_source.source_platform_object["Data"]["FromUserName"]["string"],
|
||||||
content=msg["content"],
|
content=msg["content"],
|
||||||
ats=','.join(ats)
|
ats=",".join(ats)
|
||||||
)
|
)
|
||||||
|
|
||||||
async def is_muted(self, group_id: int) -> bool:
|
async def is_muted(self, group_id: int) -> bool:
|
||||||
|
|||||||
@@ -2,21 +2,19 @@ from __future__ import annotations
|
|||||||
import typing
|
import typing
|
||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
import traceback
|
||||||
import time
|
|
||||||
import datetime
|
import datetime
|
||||||
from pkg.core import app
|
from pkg.core import app
|
||||||
from pkg.platform.adapter import MessagePlatformAdapter
|
from pkg.platform.adapter import MessagePlatformAdapter
|
||||||
from pkg.platform.types import events as platform_events, message as platform_message
|
from pkg.platform.types import events as platform_events, message as platform_message
|
||||||
|
from collections import deque
|
||||||
import aiocqhttp
|
|
||||||
import aiohttp
|
|
||||||
from libs.official_account_api.oaevent import OAEvent
|
from libs.official_account_api.oaevent import OAEvent
|
||||||
from pkg.platform.adapter import MessagePlatformAdapter
|
from pkg.platform.adapter import MessagePlatformAdapter
|
||||||
from pkg.platform.types import events as platform_events, message as platform_message
|
from pkg.platform.types import events as platform_events, message as platform_message
|
||||||
from libs.official_account_api.api import OAClient
|
from libs.official_account_api.api import OAClient
|
||||||
|
from libs.official_account_api.api import OAClientForLongerResponse
|
||||||
from pkg.core import app
|
from pkg.core import app
|
||||||
from .. import adapter
|
from .. import adapter
|
||||||
from ...pipeline.longtext.strategies import forward
|
|
||||||
from ...core import app
|
from ...core import app
|
||||||
from ..types import message as platform_message
|
from ..types import message as platform_message
|
||||||
from ..types import events as platform_events
|
from ..types import events as platform_events
|
||||||
@@ -26,6 +24,7 @@ from ...command.errors import ParamNotEnoughError
|
|||||||
|
|
||||||
# 生成的ai回答
|
# 生成的ai回答
|
||||||
generated_content = {}
|
generated_content = {}
|
||||||
|
msg_queue = {}
|
||||||
|
|
||||||
class OAMessageConverter(adapter.MessageConverter):
|
class OAMessageConverter(adapter.MessageConverter):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -88,17 +87,30 @@ class OfficialAccountAdapter(adapter.MessagePlatformAdapter):
|
|||||||
"EncodingAESKey",
|
"EncodingAESKey",
|
||||||
"AppSecret",
|
"AppSecret",
|
||||||
"AppID",
|
"AppID",
|
||||||
|
"Mode",
|
||||||
]
|
]
|
||||||
missing_keys = [key for key in required_keys if key not in config]
|
missing_keys = [key for key in required_keys if key not in config]
|
||||||
if missing_keys:
|
if missing_keys:
|
||||||
raise ParamNotEnoughError("企业微信缺少相关配置项,请查看文档或联系管理员")
|
raise ParamNotEnoughError("微信公众号缺少相关配置项,请查看文档或联系管理员")
|
||||||
|
|
||||||
self.bot = OAClient(
|
|
||||||
token=config['token'],
|
if self.config['Mode'] == "drop":
|
||||||
EncodingAESKey=config['EncodingAESKey'],
|
self.bot = OAClient(
|
||||||
Appsecret=config['AppSecret'],
|
token=config['token'],
|
||||||
AppID=config['AppID'],
|
EncodingAESKey=config['EncodingAESKey'],
|
||||||
)
|
Appsecret=config['AppSecret'],
|
||||||
|
AppID=config['AppID'],
|
||||||
|
)
|
||||||
|
elif self.config['Mode'] == "passive":
|
||||||
|
self.bot = OAClientForLongerResponse(
|
||||||
|
token=config['token'],
|
||||||
|
EncodingAESKey=config['EncodingAESKey'],
|
||||||
|
Appsecret=config['AppSecret'],
|
||||||
|
AppID=config['AppID'],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise KeyError("请设置微信公众号通信模式")
|
||||||
|
|
||||||
|
|
||||||
async def reply_message(self, message_source: platform_events.FriendMessage, message: platform_message.MessageChain, quote_origin: bool = False):
|
async def reply_message(self, message_source: platform_events.FriendMessage, message: platform_message.MessageChain, quote_origin: bool = False):
|
||||||
global generated_content
|
global generated_content
|
||||||
@@ -109,6 +121,20 @@ class OfficialAccountAdapter(adapter.MessagePlatformAdapter):
|
|||||||
|
|
||||||
generated_content[message_source.message_chain.message_id] = content
|
generated_content[message_source.message_chain.message_id] = content
|
||||||
|
|
||||||
|
from_user = message_source.sender.id
|
||||||
|
|
||||||
|
|
||||||
|
if from_user not in msg_queue:
|
||||||
|
msg_queue[from_user] = []
|
||||||
|
|
||||||
|
msg_queue[from_user].append(
|
||||||
|
{
|
||||||
|
"msg_id":message_source.message_chain.message_id,
|
||||||
|
"content":content,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def send_message(
|
async def send_message(
|
||||||
self, target_type: str, target_id: str, message: platform_message.MessageChain
|
self, target_type: str, target_id: str, message: platform_message.MessageChain
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ from __future__ import annotations
|
|||||||
import typing
|
import typing
|
||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
import traceback
|
||||||
import time
|
|
||||||
import datetime
|
import datetime
|
||||||
import aiocqhttp
|
|
||||||
import aiohttp
|
|
||||||
from pkg.platform.adapter import MessagePlatformAdapter
|
from pkg.platform.adapter import MessagePlatformAdapter
|
||||||
from pkg.platform.types import events as platform_events, message as platform_message
|
from pkg.platform.types import events as platform_events, message as platform_message
|
||||||
from pkg.core import app
|
from pkg.core import app
|
||||||
from .. import adapter
|
from .. import adapter
|
||||||
from ...pipeline.longtext.strategies import forward
|
|
||||||
from ...core import app
|
from ...core import app
|
||||||
from ..types import message as platform_message
|
from ..types import message as platform_message
|
||||||
from ..types import events as platform_events
|
from ..types import events as platform_events
|
||||||
@@ -74,7 +73,11 @@ class QQOfficialEventConverter(adapter.EventConverter):
|
|||||||
remark = "",
|
remark = "",
|
||||||
)
|
)
|
||||||
return platform_events.FriendMessage(
|
return platform_events.FriendMessage(
|
||||||
sender = friend,message_chain = yiri_chain,time = event.timestamp,
|
sender = friend,message_chain = yiri_chain,time = int(
|
||||||
|
datetime.datetime.strptime(
|
||||||
|
event.timestamp, "%Y-%m-%dT%H:%M:%S%z"
|
||||||
|
).timestamp()
|
||||||
|
),
|
||||||
source_platform_object=event
|
source_platform_object=event
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -96,7 +99,7 @@ class QQOfficialEventConverter(adapter.EventConverter):
|
|||||||
member_name= event.t,
|
member_name= event.t,
|
||||||
permission= 'MEMBER',
|
permission= 'MEMBER',
|
||||||
group = platform_entities.Group(
|
group = platform_entities.Group(
|
||||||
id = 0,
|
id = event.group_openid,
|
||||||
name = 'MEMBER',
|
name = 'MEMBER',
|
||||||
permission= platform_entities.Permission.Member
|
permission= platform_entities.Permission.Member
|
||||||
),
|
),
|
||||||
@@ -105,7 +108,11 @@ class QQOfficialEventConverter(adapter.EventConverter):
|
|||||||
last_speak_timestamp=0,
|
last_speak_timestamp=0,
|
||||||
mute_time_remaining=0
|
mute_time_remaining=0
|
||||||
)
|
)
|
||||||
time = event.timestamp
|
time = int(
|
||||||
|
datetime.datetime.strptime(
|
||||||
|
event.timestamp, "%Y-%m-%dT%H:%M:%S%z"
|
||||||
|
).timestamp()
|
||||||
|
)
|
||||||
return platform_events.GroupMessage(
|
return platform_events.GroupMessage(
|
||||||
sender = sender,
|
sender = sender,
|
||||||
message_chain=yiri_chain,
|
message_chain=yiri_chain,
|
||||||
@@ -119,7 +126,7 @@ class QQOfficialEventConverter(adapter.EventConverter):
|
|||||||
member_name=event.t,
|
member_name=event.t,
|
||||||
permission= 'MEMBER',
|
permission= 'MEMBER',
|
||||||
group = platform_entities.Group(
|
group = platform_entities.Group(
|
||||||
id = 0,
|
id = event.channel_id,
|
||||||
name = 'MEMBER',
|
name = 'MEMBER',
|
||||||
permission=platform_entities.Permission.Member
|
permission=platform_entities.Permission.Member
|
||||||
),
|
),
|
||||||
@@ -128,7 +135,11 @@ class QQOfficialEventConverter(adapter.EventConverter):
|
|||||||
last_speak_timestamp=0,
|
last_speak_timestamp=0,
|
||||||
mute_time_remaining=0
|
mute_time_remaining=0
|
||||||
)
|
)
|
||||||
time = event.timestamp,
|
time = int(
|
||||||
|
datetime.datetime.strptime(
|
||||||
|
event.timestamp, "%Y-%m-%dT%H:%M:%S%z"
|
||||||
|
).timestamp()
|
||||||
|
)
|
||||||
return platform_events.GroupMessage(
|
return platform_events.GroupMessage(
|
||||||
sender =sender,
|
sender =sender,
|
||||||
message_chain = yiri_chain,
|
message_chain = yiri_chain,
|
||||||
|
|||||||
@@ -2,18 +2,15 @@ from __future__ import annotations
|
|||||||
import typing
|
import typing
|
||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
import traceback
|
||||||
import time
|
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import aiocqhttp
|
|
||||||
import aiohttp
|
|
||||||
from libs.wecom_api.api import WecomClient
|
from libs.wecom_api.api import WecomClient
|
||||||
from pkg.platform.adapter import MessagePlatformAdapter
|
from pkg.platform.adapter import MessagePlatformAdapter
|
||||||
from pkg.platform.types import events as platform_events, message as platform_message
|
from pkg.platform.types import events as platform_events, message as platform_message
|
||||||
from libs.wecom_api.wecomevent import WecomEvent
|
from libs.wecom_api.wecomevent import WecomEvent
|
||||||
from pkg.core import app
|
from pkg.core import app
|
||||||
from .. import adapter
|
from .. import adapter
|
||||||
from ...pipeline.longtext.strategies import forward
|
|
||||||
from ...core import app
|
from ...core import app
|
||||||
from ..types import message as platform_message
|
from ..types import message as platform_message
|
||||||
from ..types import events as platform_events
|
from ..types import events as platform_events
|
||||||
@@ -200,7 +197,19 @@ class WecomAdapter(adapter.MessagePlatformAdapter):
|
|||||||
async def send_message(
|
async def send_message(
|
||||||
self, target_type: str, target_id: str, message: platform_message.MessageChain
|
self, target_type: str, target_id: str, message: platform_message.MessageChain
|
||||||
):
|
):
|
||||||
pass
|
"""企业微信目前只有发送给个人的方法,
|
||||||
|
构造target_id的方式为前半部分为账户id,后半部分为agent_id,中间使用“|”符号隔开。
|
||||||
|
"""
|
||||||
|
content_list = await WecomMessageConverter.yiri2target(message, self.bot)
|
||||||
|
parts = target_id.split("|")
|
||||||
|
user_id = parts[0]
|
||||||
|
agent_id = int(parts[1])
|
||||||
|
if target_type == 'person':
|
||||||
|
for content in content_list:
|
||||||
|
if content["type"] == "text":
|
||||||
|
await self.bot.send_private_msg(user_id,agent_id,content["content"])
|
||||||
|
if content["type"] == "image":
|
||||||
|
await self.bot.send_image(user_id,agent_id,content["media"])
|
||||||
|
|
||||||
def register_listener(
|
def register_listener(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ class MessageEvent(Event):
|
|||||||
message_chain: platform_message.MessageChain
|
message_chain: platform_message.MessageChain
|
||||||
"""消息内容。"""
|
"""消息内容。"""
|
||||||
|
|
||||||
|
time: float | None = None
|
||||||
|
"""消息发送时间戳。"""
|
||||||
|
|
||||||
source_platform_object: typing.Optional[typing.Any] = None
|
source_platform_object: typing.Optional[typing.Any] = None
|
||||||
"""原消息平台对象。
|
"""原消息平台对象。
|
||||||
供消息平台适配器开发者使用,如果回复用户时需要使用原消息事件对象的信息,
|
供消息平台适配器开发者使用,如果回复用户时需要使用原消息事件对象的信息,
|
||||||
|
|||||||
@@ -167,6 +167,10 @@ class DashScopeAPIRunner(runner.RequestRunner):
|
|||||||
image_ids = [] # 用户输入的图片ID列表 (暂不支持)
|
image_ids = [] # 用户输入的图片ID列表 (暂不支持)
|
||||||
|
|
||||||
plain_text, image_ids = await self._preprocess_user_message(query)
|
plain_text, image_ids = await self._preprocess_user_message(query)
|
||||||
|
|
||||||
|
biz_params = {}
|
||||||
|
biz_params.update(self.biz_params)
|
||||||
|
biz_params.update(query.variables)
|
||||||
|
|
||||||
#发送对话请求
|
#发送对话请求
|
||||||
response = dashscope.Application.call(
|
response = dashscope.Application.call(
|
||||||
@@ -176,7 +180,7 @@ class DashScopeAPIRunner(runner.RequestRunner):
|
|||||||
stream=True, # 流式输出
|
stream=True, # 流式输出
|
||||||
incremental_output=True, # 增量输出,使用流式输出需要开启增量输出
|
incremental_output=True, # 增量输出,使用流式输出需要开启增量输出
|
||||||
session_id=query.session.using_conversation.uuid, # 会话ID用于,多轮对话
|
session_id=query.session.using_conversation.uuid, # 会话ID用于,多轮对话
|
||||||
biz_params=self.biz_params # 工作流应用的自定义输入参数传递
|
biz_params=biz_params, # 工作流应用的自定义输入参数传递
|
||||||
# rag_options={ # 主要用于文件交互,暂不支持
|
# rag_options={ # 主要用于文件交互,暂不支持
|
||||||
# "session_file_ids": ["FILE_ID1"], # FILE_ID1 替换为实际的临时文件ID,逗号隔开多个
|
# "session_file_ids": ["FILE_ID1"], # FILE_ID1 替换为实际的临时文件ID,逗号隔开多个
|
||||||
# }
|
# }
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import json
|
|||||||
import uuid
|
import uuid
|
||||||
import re
|
import re
|
||||||
import base64
|
import base64
|
||||||
|
import datetime
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
@@ -69,6 +70,7 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
|||||||
"""
|
"""
|
||||||
plain_text = ""
|
plain_text = ""
|
||||||
image_ids = []
|
image_ids = []
|
||||||
|
|
||||||
if isinstance(query.user_message.content, list):
|
if isinstance(query.user_message.content, list):
|
||||||
for ce in query.user_message.content:
|
for ce in query.user_message.content:
|
||||||
if ce.type == "text":
|
if ce.type == "text":
|
||||||
@@ -109,8 +111,12 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
|||||||
|
|
||||||
basic_mode_pending_chunk = ''
|
basic_mode_pending_chunk = ''
|
||||||
|
|
||||||
|
inputs = {}
|
||||||
|
|
||||||
|
inputs.update(query.variables)
|
||||||
|
|
||||||
async for chunk in self.dify_client.chat_messages(
|
async for chunk in self.dify_client.chat_messages(
|
||||||
inputs={},
|
inputs=inputs,
|
||||||
query=plain_text,
|
query=plain_text,
|
||||||
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
||||||
conversation_id=cov_id,
|
conversation_id=cov_id,
|
||||||
@@ -160,8 +166,12 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
|||||||
|
|
||||||
ignored_events = ["agent_message"]
|
ignored_events = ["agent_message"]
|
||||||
|
|
||||||
|
inputs = {}
|
||||||
|
|
||||||
|
inputs.update(query.variables)
|
||||||
|
|
||||||
async for chunk in self.dify_client.chat_messages(
|
async for chunk in self.dify_client.chat_messages(
|
||||||
inputs={},
|
inputs=inputs,
|
||||||
query=plain_text,
|
query=plain_text,
|
||||||
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
||||||
response_mode="streaming",
|
response_mode="streaming",
|
||||||
@@ -225,8 +235,8 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
|||||||
|
|
||||||
if not query.session.using_conversation.uuid:
|
if not query.session.using_conversation.uuid:
|
||||||
query.session.using_conversation.uuid = str(uuid.uuid4())
|
query.session.using_conversation.uuid = str(uuid.uuid4())
|
||||||
|
|
||||||
cov_id = query.session.using_conversation.uuid
|
query.variables["conversation_id"] = query.session.using_conversation.uuid
|
||||||
|
|
||||||
plain_text, image_ids = await self._preprocess_user_message(query)
|
plain_text, image_ids = await self._preprocess_user_message(query)
|
||||||
|
|
||||||
@@ -241,12 +251,17 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
|||||||
|
|
||||||
ignored_events = ["text_chunk", "workflow_started"]
|
ignored_events = ["text_chunk", "workflow_started"]
|
||||||
|
|
||||||
|
inputs = { # these variables are legacy variables, we need to keep them for compatibility
|
||||||
|
"langbot_user_message_text": plain_text,
|
||||||
|
"langbot_session_id": query.variables["session_id"],
|
||||||
|
"langbot_conversation_id": query.variables["conversation_id"],
|
||||||
|
"langbot_msg_create_time": query.variables["msg_create_time"],
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs.update(query.variables)
|
||||||
|
|
||||||
async for chunk in self.dify_client.workflow_run(
|
async for chunk in self.dify_client.workflow_run(
|
||||||
inputs={
|
inputs=inputs,
|
||||||
"langbot_user_message_text": plain_text,
|
|
||||||
"langbot_session_id": f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
|
||||||
"langbot_conversation_id": cov_id,
|
|
||||||
},
|
|
||||||
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
||||||
files=files,
|
files=files,
|
||||||
timeout=self.ap.provider_cfg.data["dify-service-api"]["workflow"]["timeout"],
|
timeout=self.ap.provider_cfg.data["dify-service-api"]["workflow"]["timeout"],
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
semantic_version = "v3.4.10"
|
semantic_version = "v3.4.10.4"
|
||||||
|
|
||||||
debug_mode = False
|
debug_mode = False
|
||||||
|
|
||||||
|
|||||||
@@ -166,61 +166,72 @@
|
|||||||
{
|
{
|
||||||
"name": "glm-4-plus",
|
"name": "glm-4-plus",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai"
|
"token_mgr": "zhipuai",
|
||||||
|
"tool_call_supported": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glm-4-0520",
|
"name": "glm-4-0520",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai"
|
"token_mgr": "zhipuai",
|
||||||
|
"tool_call_supported": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glm-4-air",
|
"name": "glm-4-air",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai"
|
"token_mgr": "zhipuai",
|
||||||
|
"tool_call_supported": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glm-4-airx",
|
"name": "glm-4-airx",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai"
|
"token_mgr": "zhipuai",
|
||||||
|
"tool_call_supported": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glm-4-long",
|
"name": "glm-4-long",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai"
|
"token_mgr": "zhipuai",
|
||||||
|
"tool_call_supported": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glm-4-flashx",
|
"name": "glm-4-flashx",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai"
|
"token_mgr": "zhipuai",
|
||||||
|
"tool_call_supported": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glm-4-flash",
|
"name": "glm-4-flash",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai"
|
"token_mgr": "zhipuai",
|
||||||
|
"tool_call_supported": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glm-4v-plus",
|
"name": "glm-4v-plus",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai",
|
"token_mgr": "zhipuai",
|
||||||
"vision_supported": true
|
"vision_supported": true,
|
||||||
|
"tool_call_supported": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glm-4v",
|
"name": "glm-4v",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai",
|
"token_mgr": "zhipuai",
|
||||||
"vision_supported": true
|
"vision_supported": true,
|
||||||
|
"tool_call_supported": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glm-4v-flash",
|
"name": "glm-4v-flash",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai",
|
"token_mgr": "zhipuai",
|
||||||
"vision_supported": true
|
"vision_supported": true,
|
||||||
|
"tool_call_supported": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "glm-zero-preview",
|
"name": "glm-zero-preview",
|
||||||
"requester": "zhipuai-chat-completions",
|
"requester": "zhipuai-chat-completions",
|
||||||
"token_mgr": "zhipuai",
|
"token_mgr": "zhipuai",
|
||||||
"vision_supported": true
|
"vision_supported": true,
|
||||||
|
"tool_call_supported": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
},
|
},
|
||||||
"income-msg-check": true,
|
"income-msg-check": true,
|
||||||
"ignore-rules": {
|
"ignore-rules": {
|
||||||
"prefix": ["/"],
|
"prefix": [],
|
||||||
"regexp": []
|
"regexp": []
|
||||||
},
|
},
|
||||||
"check-sensitive-words": true,
|
"check-sensitive-words": true,
|
||||||
|
|||||||
@@ -77,6 +77,7 @@
|
|||||||
"EncodingAESKey":"",
|
"EncodingAESKey":"",
|
||||||
"AppID":"",
|
"AppID":"",
|
||||||
"AppSecret":"",
|
"AppSecret":"",
|
||||||
|
"Mode":"drop",
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"port": 2287
|
"port": 2287
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"items": {
|
"items": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "regex",
|
"format": "regex",
|
||||||
"pattern": "^(person|group)_(\\d)*$"
|
"pattern": "^(person|group)_.*$"
|
||||||
},
|
},
|
||||||
"default": []
|
"default": []
|
||||||
},
|
},
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
"items": {
|
"items": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "regex",
|
"format": "regex",
|
||||||
"pattern": "^(person|group)_(\\d)*$"
|
"pattern": "^(person|group)_.*$"
|
||||||
},
|
},
|
||||||
"default": []
|
"default": []
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"patternProperties": {
|
"patternProperties": {
|
||||||
"^\\d+$": {
|
"^.*$": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"at": {
|
"at": {
|
||||||
|
|||||||
@@ -385,6 +385,12 @@
|
|||||||
"default": "",
|
"default": "",
|
||||||
"description": "微信公众号的AppSecret"
|
"description": "微信公众号的AppSecret"
|
||||||
},
|
},
|
||||||
|
"Mode": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "drop",
|
||||||
|
"description": "对于超过15s的响应的处理模式",
|
||||||
|
"enum": ["drop", "passive"]
|
||||||
|
},
|
||||||
"host": {
|
"host": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "0.0.0.0",
|
"default": "0.0.0.0",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"items": {
|
"items": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "regex",
|
"format": "regex",
|
||||||
"pattern": "^(person|group)_(\\d+)$"
|
"pattern": "^(person|group)_.*$"
|
||||||
},
|
},
|
||||||
"default": []
|
"default": []
|
||||||
},
|
},
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"patternProperties": {
|
"patternProperties": {
|
||||||
"^(person|group)_(\\d+)$": {
|
"^(person|group)_.*$": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user