You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

messages_resource-bk.py 15KB

3 月之前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. from flask_restful import Resource, reqparse
  2. from flask import jsonify,request
  3. from bridge.context import ContextType
  4. import requests,json
  5. from wechat import gewe_chat
  6. from voice.ali.ali_voice import AliVoice
  7. from common import utils,redis_helper,memory,kafka_helper
  8. from common.log import logger
  9. import openai
  10. import xml.etree.ElementTree as ET
  11. import os
  12. from voice import audio_convert
  13. class MessagesBKResource(Resource):
  14. def __init__(self):
  15. self.parser = reqparse.RequestParser()
  16. def post(self):
  17. msg = request.get_json()
  18. logger.debug(f"Received message: {msg}")
  19. if 'Data' in msg:
  20. msg_data=msg.get("Data")
  21. msg_type=msg_data.get("MsgType")
  22. if msg_type == 1:#ContextType.TEXT: # 文字
  23. msg_content=msg_data["Content"]["string"]
  24. #print(msg_content)
  25. logger.info(msg_content)
  26. app_id=msg["Appid"]
  27. wxid=msg["Wxid"]
  28. from_wxid=msg_data["FromUserName"]["string"]
  29. to_wxid=msg_data["ToUserName"]["string"]
  30. token_id="f828cb3c-1039-489f-b9ae-7494d1778a15"
  31. hash_key = f"__AI_OPS_WX__:MESSAGES:{wxid}"
  32. if wxid == from_wxid: #主动发送消息
  33. logger.info("Active message sending detected")
  34. gewe_chat.wxchat.save_contacts_brief_to_cache(token_id,app_id,wxid,[to_wxid])
  35. callback_to_user=msg_data["ToUserName"]["string"]
  36. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  37. input_message=utils.dialogue_message(from_wxid,to_wxid,input_wx_content_dialogue_message)
  38. kafka_helper.kafka_client.produce_message(input_message)
  39. logger.info("发送对话 %s",input_message)
  40. else:
  41. callback_to_user=msg_data["FromUserName"]["string"]
  42. prompt={"role": "user", "content": [{
  43. "type": "text",
  44. "text": msg_content
  45. }]}
  46. messages_to_send=get_messages_from_cache(hash_key, prompt)
  47. # 收到的对话
  48. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  49. input_message=utils.dialogue_message(callback_to_user,wxid,input_wx_content_dialogue_message)
  50. kafka_helper.kafka_client.produce_message(input_message)
  51. logger.info("发送对话 %s",input_message)
  52. cache_data = memory.USER_INTERACTIVE_CACHE.get(wxid)
  53. if cache_data and cache_data.get('interactive'):
  54. messages_to_send=[{"role": "user", "content": msg_content}]
  55. res=fast_gpt_api(messages_to_send,wxid)
  56. reply_content=res["choices"][0]["message"]["content"]
  57. description = ''
  58. userSelectOptions = []
  59. if isinstance(reply_content, list) and any(item.get("type") == "interactive" for item in reply_content):
  60. for item in reply_content:
  61. if item["type"] == "interactive" and item["interactive"]["type"] == "userSelect":
  62. params = item["interactive"]["params"]
  63. description = params.get("description")
  64. userSelectOptions = params.get("userSelectOptions", [])
  65. values_string = "\n".join(option["value"] for option in userSelectOptions)
  66. if description is not None:
  67. memory.USER_INTERACTIVE_CACHE[wxid] = {
  68. "interactive":True
  69. }
  70. reply_content=description + '------------------------------\n'+values_string
  71. elif isinstance(reply_content, list) and any(item.get("type") == "text" for item in reply_content):
  72. memory.USER_INTERACTIVE_CACHE[wxid] = {
  73. "interactive":False
  74. }
  75. text=''
  76. for item in reply_content:
  77. if item["type"] == "text":
  78. text=item["text"]["content"]
  79. if text=='':
  80. # 去除上次上一轮对话再次请求
  81. cache_messages_str=redis_helper.redis_helper.get_hash_field(hash_key,"data")
  82. cache_messages = json.loads(cache_messages_str) if cache_messages_str else []
  83. if len(cache_messages) >= 3:
  84. cache_messages = cache_messages[:-3]
  85. redis_helper.redis_helper.update_hash_field(hash_key,"data",json.dumps(cache_messages,ensure_ascii=False))
  86. messages_to_send=get_messages_from_cache(hash_key, prompt)
  87. res=fast_gpt_api(messages_to_send,wxid)
  88. reply_content=res["choices"][0]["message"]["content"]
  89. else:
  90. reply_content=text
  91. else:
  92. memory.USER_INTERACTIVE_CACHE[wxid] = {
  93. "interactive":False
  94. }
  95. reply_content=res["choices"][0]["message"]["content"]
  96. print(f'token_id {token_id}')
  97. print(f'app_id {app_id}')
  98. print(f'touser: {callback_to_user}')
  99. # print(f'towxuser:{towxuser}')
  100. print(reply_content)
  101. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,reply_content)
  102. get_messages_from_cache(hash_key, {"role": "assistant", "content": reply_content})
  103. # 回复的对话
  104. input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  105. input_message=utils.dialogue_message(wxid,callback_to_user,input_wx_content_dialogue_message,True)
  106. kafka_helper.kafka_client.produce_message(input_message)
  107. logger.info("发送对话 %s",input_message)
  108. if msg_type == 3: #图片
  109. token_id="f828cb3c-1039-489f-b9ae-7494d1778a15"
  110. app_id=msg["Appid"]
  111. callback_to_user=msg_data["FromUserName"]["string"]
  112. msg_content=msg_data["Content"]["string"]
  113. print(f'token_id {token_id}')
  114. print(f'app_id {app_id}')
  115. print(f'touser: {callback_to_user}')
  116. # print(res_content)
  117. wxid=msg["Wxid"]
  118. hash_key = f"__AI_OPS_WX__:MESSAGES:{wxid}"
  119. wx_img_url=gewe_chat.wxchat.download_image_msg(token_id,app_id,msg_content)
  120. oss_access_key_id="LTAI5tRTG6pLhTpKACJYoPR5"
  121. oss_access_key_secret="E7dMzeeMxq4VQvLg7Tq7uKf3XWpYfN"
  122. oss_endpoint="http://oss-cn-shanghai.aliyuncs.com"
  123. oss_bucket_name="cow-agent"
  124. oss_prefix="cow"
  125. img_url=utils.upload_oss(oss_access_key_id, oss_access_key_secret, oss_endpoint, oss_bucket_name, wx_img_url, oss_prefix)
  126. prompt={
  127. "role": "user",
  128. "content": [{
  129. "type": "image_url",
  130. "image_url": {"url": img_url}
  131. }]
  132. }
  133. get_messages_from_cache(hash_key, prompt)
  134. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,'已经上传了图片,有什么可以为您服务')
  135. logger.debug(f"Uploaded image URL: {img_url}")
  136. wx_content_dialogue_message=[{"type": "image_url", "image_url": {"url": img_url}}]
  137. input_message=utils.dialogue_message(wxid,callback_to_user,wx_content_dialogue_message)
  138. kafka_helper.kafka_client.produce_message(input_message)
  139. logger.info("发送对话 %s",input_message)
  140. if msg_type == 34: # 语音
  141. token_id="f828cb3c-1039-489f-b9ae-7494d1778a15"
  142. callback_to_user=msg_data["FromUserName"]["string"]
  143. app_id=msg["Appid"]
  144. msg_content=msg_data["Content"]["string"]
  145. msg_id=msg_data["MsgId"]
  146. wxid=msg["Wxid"]
  147. hash_key = f"__AI_OPS_WX__:MESSAGES:{wxid}"
  148. print(f'token_id {token_id}')
  149. print(f'app_id {app_id}')
  150. print(f'touser: {callback_to_user}')
  151. print(f'msg_id:{msg_id}')
  152. file_url=gewe_chat.wxchat.download_audio_msg(token_id,app_id,msg_id,msg_content)
  153. react_silk_path=utils.save_to_local_from_url(file_url)
  154. react_wav_path = os.path.splitext(react_silk_path)[0] + ".wav"
  155. audio_convert.any_to_wav(react_silk_path,react_wav_path)
  156. react_voice_text=AliVoice().voiceToText(react_wav_path)
  157. messages=get_messages_from_cache(hash_key, {"role": "user", "content": react_voice_text})
  158. ai_res=fast_gpt_api(messages,wxid)
  159. ai_res_content=ai_res["choices"][0]["message"]["content"]
  160. reply_text_voice=AliVoice().textToVoice(ai_res_content)
  161. reply_text_voice_path=os.path.join(os.getcwd(), reply_text_voice)
  162. reply_silk_path = os.path.splitext(reply_text_voice_path)[0] + ".silk"
  163. reply_silk_during=audio_convert.any_to_sil(reply_text_voice_path,reply_silk_path)
  164. # print(int(reply_silk_during))
  165. # print(reply_silk_path)
  166. oss_access_key_id="LTAI5tRTG6pLhTpKACJYoPR5"
  167. oss_access_key_secret="E7dMzeeMxq4VQvLg7Tq7uKf3XWpYfN"
  168. oss_endpoint="http://oss-cn-shanghai.aliyuncs.com"
  169. oss_bucket_name="cow-agent"
  170. oss_prefix="cow"
  171. file_path=reply_silk_path
  172. file_url = utils.upload_oss(oss_access_key_id, oss_access_key_secret, oss_endpoint, oss_bucket_name, file_path, oss_prefix)
  173. print(file_url)
  174. res=gewe_chat.wxchat.post_voice(token_id,app_id,callback_to_user,file_url,int(reply_silk_during))
  175. # 删除临时文件
  176. os.remove(react_silk_path)
  177. os.remove(react_wav_path)
  178. os.remove(reply_text_voice_path)
  179. os.remove(reply_silk_path)
  180. get_messages_from_cache(hash_key, {"role": "assistant", "content": ai_res})
  181. if msg_type == 49:
  182. msg_content_xml=msg_data["Content"]["string"]
  183. root = ET.fromstring(msg_content_xml)
  184. type_value = root.find(".//appmsg/type").text
  185. if type_value==57: # 引用消息
  186. '''
  187. # 判断此类消息的逻辑:$.Data.MsgType=49 并且 解析$.Data.Content.string中的xml msg.appmsg.type=57
  188. '''
  189. wxid=msg["Wxid"]
  190. hash_key = f"__AI_OPS_WX__:MESSAGES:{wxid}"
  191. app_id=msg["Appid"]
  192. callback_to_user=msg_data["FromUserName"]["string"]
  193. # towxuser=touser.get("")
  194. # token_id="ce50e2c376c843a9a281af3a1a0f4420"
  195. token_id="f828cb3c-1039-489f-b9ae-7494d1778a15"
  196. prompt={"role": "user", "content": [{
  197. "type": "text",
  198. "text": msg_content
  199. }]}
  200. # 收到的对话
  201. messages_to_send=get_messages_from_cache(hash_key, prompt)
  202. input_wx_content_dialogue_message=[{"type": "text", "text": msg_content}]
  203. input_message=utils.dialogue_message(callback_to_user,wxid,input_wx_content_dialogue_message)
  204. kafka_helper.kafka_client.produce_message(input_message)
  205. logger.info("发送对话 %s",input_message)
  206. # 回复的对话
  207. res=fast_gpt_api(messages_to_send,wxid)
  208. reply_content=res["choices"][0]["message"]["content"]
  209. input_wx_content_dialogue_message=[{"type": "text", "text": reply_content}]
  210. input_message=utils.dialogue_message(wxid,callback_to_user,input_wx_content_dialogue_message,True)
  211. kafka_helper.kafka_client.produce_message(input_message)
  212. logger.info("发送对话 %s",input_message)
  213. get_messages_from_cache(hash_key, {"role": "assistant", "content": reply_content})
  214. gewe_chat.wxchat.post_text(token_id,app_id,callback_to_user,reply_content)
  215. return jsonify({"message": "微信回调成功"})
  216. def get_messages_from_cache(hash_key,object:object)->object:
  217. messages=redis_helper.redis_helper.get_hash(hash_key)
  218. wxid=hash_key.split(':')[-1]
  219. if not messages:
  220. messages=[{"role": "system", "content": ""}]
  221. messages.append(object)
  222. redis_helper.redis_helper.set_hash(hash_key,{"data":json.dumps(messages,ensure_ascii=False)},3600)
  223. else:
  224. messages_str=redis_helper.redis_helper.get_hash_field(hash_key,"data")
  225. messages = json.loads(messages_str) if messages_str else []
  226. #判断是否含有图片
  227. last_message = messages[-1]
  228. # print(last_message)
  229. # print('~~~~~~~~~~')
  230. # 获取 content 并判断其是否为列表
  231. content = last_message.get("content", [])
  232. if isinstance(content, list) and content:
  233. last_content_type = content[-1].get("type")
  234. if last_content_type == 'image_url':
  235. content.append(object['content'][0])
  236. messages[-1]['content']=content
  237. else:
  238. messages.append(object)
  239. else:
  240. messages.append(object)
  241. # messages.append({"role": "user", "content": msg_content})
  242. #messages.append(object)
  243. # print(messages)
  244. redis_helper.redis_helper.set_hash(hash_key,{"data":json.dumps(messages,ensure_ascii=False)},3600)
  245. return messages
  246. def fast_gpt_api(messages:list,session_id:str):
  247. #api_key="sk-tdi7u0zuLsR0JpPMGBeFZxymOpL0zoFVafX8EEEvEakIDAGQ22NyQ6w"
  248. api_key="sk-uJDBdKmJVb2cmfldGOvlIY6Qx0AzqWMPD3lS1IzgQYzHNOXv9SKNI"
  249. api_url = "http://106.15.182.218:3000/api/v1/chat/completions"
  250. headers = {
  251. "Content-Type": "application/json",
  252. "Authorization": f"Bearer {api_key}"
  253. }
  254. data={
  255. "model": "",
  256. "messages":messages,
  257. "chatId": session_id,
  258. "detail": True
  259. }
  260. print(json.dumps(data,ensure_ascii=False))
  261. logger.info("[CHATGPT] 请求={}".format(json.dumps(data, ensure_ascii=False)))
  262. response = requests.post(url=api_url, headers=headers, data=json.dumps(data), timeout=600)
  263. response.raise_for_status()
  264. response_data = response.json()
  265. logger.info("[CHATGPT] 响应={}".format(json.dumps(response_data, separators=(',', ':'),ensure_ascii=False)))
  266. print(response_data)
  267. return response_data