work.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. from flask import request,Response,stream_with_context
  2. from utils.request import post_request,generate_error
  3. import json
  4. import time
  5. from services.ai_service import ai_service
  6. from services.templateFun import templateData
  7. #防止fetch请求中断
  8. from utils.offFetch import RequestInterruptHandler
  9. #判断用户需求
  10. from utils.verifyInput import messageValidator
  11. #判断是否含有特殊指令
  12. from utils.command import commandObj
  13. def stream_response():
  14. # 0.在重组数据之前,应该先清空原来的数据
  15. ai_service.clear_data()
  16. """第一步:获得必备参数"""
  17. # 1.1获得header中的website_id
  18. website_id = request.get_json().get('website_id')
  19. # 1.2获得header中的Token
  20. token = request.headers.get('Token')
  21. # 1.3获得header中的会话id
  22. session_id = request.get_json().get('session_id')
  23. # 1.4获得用户输入userMessage
  24. userMessage = request.get_json().get('userMessage')
  25. print("接收到前端用户输入:" + userMessage)
  26. # 1.5当前消息是否需要生成标题(摘要)
  27. ifNewMessage = request.get_json().get('new_msg')
  28. """第二步:验证网站是否可编辑"""
  29. checkEdit = post_request("website/checkWebsiteEdit", {
  30. "website_id": website_id
  31. },token)
  32. if checkEdit.get('code') != 200:
  33. # 2.1如果网站不可编辑或者用户未登录,则返回异常信息
  34. return Response(generate_error(checkEdit.get('message')), mimetype='text/plain')
  35. else:
  36. # 2.2开始请求网站皮肤
  37. getAllTemplate = post_request("public/getAITemplate", {
  38. "website_id": website_id
  39. },token)
  40. # 2.3获得皮肤数据
  41. skinData = getAllTemplate.get('data').get('template')
  42. # 判断用户输入是否符合要求
  43. if messageValidator.validate(userMessage):
  44. print("用户输入符合要求,允许进修后续操作")
  45. # 储存用户输入
  46. ai_service.user_input = userMessage
  47. # 1=是新会话,需要修改标题摘要
  48. if(ifNewMessage==1):
  49. # 生成标题
  50. title = ai_service.userMessage_to_title(userMessage)
  51. print("该会话需要生成标题:" + title)
  52. getAllTemplate = post_request("/public/upAiSession", {
  53. "session_id": session_id,
  54. "session_title": title
  55. },token)
  56. if getAllTemplate.get('code') != 200:
  57. # 如果创建会话失败,返回错误
  58. print("修改会话标题失败!" + getAllTemplate.get('message'))
  59. else:
  60. print("修改会话标题成功!" + getAllTemplate.get('message'))
  61. else:
  62. print("该消息不需要生成标题!")
  63. # 2.4使用AI服务找到最匹配的模板
  64. best_match = ai_service.find_best_matching_template(userMessage, skinData)
  65. matched_template_name = "默认模板"
  66. matched_template_id = 0
  67. if best_match:
  68. matched_template_name = best_match['template_info']['template_name']
  69. matched_template_id = best_match['template_info']['template_id']
  70. similarity_score = best_match['similarity_score']
  71. print(f"找到最匹配的模板: {matched_template_name}, 相似度: {similarity_score:.4f}")
  72. else:
  73. print("未找到匹配的模板,使用默认模板")
  74. # 2.5开始执行数据重组操作
  75. templateData.sectors_webSiteData["base"]["websiteId"] = website_id
  76. templateData.sectors_webSiteData["style"]["styleId"] = matched_template_id
  77. """demo : 当前缺少的逻辑:通栏应该是从后端获取的,而不是存在这里的!"""
  78. # 2.6依照用户需求筛选通栏
  79. ai_service.filter_sectors(userMessage,matched_template_id)
  80. # 2.7生成通栏数据
  81. ai_service.get_sectors(website_id,matched_template_id,token)
  82. """demo : 当前缺少的逻辑:通栏应该是从后端获取的,而不是存在这里的!"""
  83. #组合待提交的数据
  84. submit_data = {
  85. "website_id": website_id,
  86. "session_id": session_id,
  87. "template_id": matched_template_id,
  88. "template_data": json.dumps(templateData.sectors_webSiteData, ensure_ascii=False),
  89. "canvas_data": json.dumps(templateData.canvas_data, ensure_ascii=False)
  90. }
  91. """demo:会话编号应该通过创建会话来取得,目前先使用固定的编号进行测试 自助建站标准化测试平台website_id:109 会话id:09504811488333"""
  92. template_ref = post_request("public/addTemplateDraftbox", submit_data ,token)
  93. if template_ref.get('code') != 200:
  94. print("临时模板保存失败!" + template_ref.get('message'))
  95. else:
  96. print("临时模板保存成功!" + template_ref.get('message'))
  97. #2.7判断用户是否需要填充导航
  98. command_number = commandObj.get_command(userMessage)
  99. if command_number == 1:
  100. print("用户需要填充导航")
  101. templateCates = post_request("/public/addTemplateCates", {
  102. "session_id": session_id,
  103. },token)
  104. if templateCates.get('code') != 200:
  105. ai_service.add_reasoning(f"模板导航因远程服务器故障未能完成添加")
  106. print("填充导航失败!" + templateCates.get('message'))
  107. else:
  108. ai_service.add_reasoning(f"我已经按照用户的要求给每个组件都选择了导航")
  109. print("填充导航成功!" + templateCates.get('message'))
  110. else:
  111. print("用户不需要填充导航")
  112. # 2.8获得推理过程
  113. reasoning = ai_service.reasoning
  114. reasoning_str = "。".join(reasoning)
  115. """第三步:返回内容生成器"""
  116. def generate():
  117. with RequestInterruptHandler(request) as handler:
  118. texts = [
  119. {"content": reasoning_str, "class": "computerLi"},
  120. {"content": "👌久等了!您的需求已完成:", "class": "computertextBold"},
  121. {"content": "我使用了皮肤库中名为:"+ matched_template_name +"的皮肤,网站通栏与组件的构建已经完成。", "class": "computertext"},
  122. {"content": best_match['template_info'].get('template_img')[0].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[0].get('name')},
  123. {"content": best_match['template_info'].get('template_img')[1].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[1].get('name')},
  124. {"content": best_match['template_info'].get('template_img')[2].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[2].get('name')},
  125. {"content": best_match['template_info'].get('template_img')[3].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[3].get('name')},
  126. {"content": best_match['template_info'].get('template_img')[4].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[4].get('name')},
  127. {"content": best_match['template_info'].get('template_img')[5].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[5].get('name')},
  128. {"content": best_match['template_info'].get('template_img')[6].get('url') , "class": "computerImgBox" , "pagename" :best_match['template_info'].get('template_img')[6].get('name')},
  129. {"content": "点击下面👇的链接可以查看该模板的设计效果,如果需要修改请告诉我。", "class": "computertext"},
  130. {"content": "/#/templateCreat?website_id=" + str(website_id) + "&style=" + str(matched_template_id) + "&mode=ai&session_id=" + session_id, "class": "computertextLink"}
  131. ]
  132. for text_item in texts:
  133. # 检查客户端是否断开
  134. if not handler.is_client_connected():
  135. print("客户端已断开连接,停止发送")
  136. handler.client_closed = True
  137. break
  138. text = text_item["content"]
  139. text_class = text_item["class"]
  140. pagename = text_item.get('pagename')
  141. if text_class in ["computerImgBox", "computertextLink"]:
  142. data = {
  143. 'code': 200,
  144. 'type': text_class,
  145. 'pagename': pagename,
  146. 'data': text,
  147. 'finished': False,
  148. 'isComplete': True
  149. }
  150. yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
  151. else:
  152. for char in text:
  153. data = {
  154. 'code': 200,
  155. 'type': text_class,
  156. 'data': char,
  157. 'finished': False,
  158. 'isComplete': False
  159. }
  160. yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
  161. time.sleep(0.02)
  162. # 发送完成信号
  163. complete_data = {
  164. 'code': 200,
  165. 'type': text_class,
  166. 'data': '',
  167. 'finished': False,
  168. 'isComplete': True
  169. }
  170. yield f"data: {json.dumps(complete_data, ensure_ascii=False)}\n\n"
  171. # 发送换行符
  172. newline_data = {
  173. 'code': 200,
  174. 'type': 'newline',
  175. 'data': '\n\n',
  176. 'finished': False,
  177. 'isComplete': True
  178. }
  179. yield f"data: {json.dumps(newline_data, ensure_ascii=False)}\n\n"
  180. # 如果连接还在,发送结束信号
  181. if not handler.client_closed:
  182. end_data = {
  183. 'code': 200,
  184. 'data': '',
  185. 'finished': True,
  186. 'full_text': "处理结果已发送完毕!"
  187. }
  188. yield f"data: {json.dumps(end_data, ensure_ascii=False)}\n\n"
  189. return Response(stream_with_context(generate()), mimetype='text/event-stream')
  190. else:
  191. """第四步:处理非模板生成类请求"""
  192. # 如果用户一上来输入非模板问题,这里需要生成一个标题
  193. # 1=是新会话,需要修改标题摘要
  194. if(ifNewMessage==1):
  195. # 生成标题
  196. title = "非模板需求的会话"
  197. print("该会话需要生成标题:" + title)
  198. getAllTemplate = post_request("/public/upAiSession", {
  199. "session_id": session_id,
  200. "session_title": title
  201. },token)
  202. if getAllTemplate.get('code') != 200:
  203. # 如果创建会话失败,返回错误
  204. print("修改会话标题失败!" + getAllTemplate.get('message'))
  205. else:
  206. print("修改会话标题成功!" + getAllTemplate.get('message'))
  207. else:
  208. print("该消息不需要生成标题!")
  209. # 返回错误信息
  210. def generate_error_response():
  211. texts = [
  212. {"content": "对不起,您的指令暂不支持!", "class": "computertext"},
  213. {"content": "小龙包暂时无法处理非网站模板生成类的问题😅,我们将会在未来的版本中增加对更多指令的支持,敬请期待!", "class": "computertext"},
  214. {"content": "推荐的提示词语句:", "class": "computertextBoldLine"},
  215. {"content": "1️⃣ 帮我推荐一套中国传统红色皮肤。", "class": "computertext"},
  216. {"content": "2️⃣ 帮我推荐一套中国传统红色皮肤,把焦点图放到第一个,去掉所有广告。", "class": "computertext"},
  217. {"content": "3️⃣ 帮我推荐一套中国传统红色皮肤,把焦点图放到第一个,去掉所有广告,帮我填好导航。", "class": "computertext"},
  218. ]
  219. for text_item in texts:
  220. text = text_item["content"]
  221. text_class = text_item["class"]
  222. for char in text:
  223. data = {
  224. 'code': 200,
  225. 'type': text_class,
  226. 'data': char,
  227. 'finished': False,
  228. 'isComplete': False
  229. }
  230. yield f"data: {json.dumps(data, ensure_ascii=False)}\n\n"
  231. time.sleep(0.02)
  232. # 发送完成信号
  233. complete_data = {
  234. 'code': 200,
  235. 'type': text_class,
  236. 'data': '',
  237. 'finished': False,
  238. 'isComplete': True
  239. }
  240. yield f"data: {json.dumps(complete_data, ensure_ascii=False)}\n\n"
  241. # 发送换行符
  242. newline_data = {
  243. 'code': 200,
  244. 'type': 'newline',
  245. 'data': '\n\n',
  246. 'finished': False,
  247. 'isComplete': True
  248. }
  249. yield f"data: {json.dumps(newline_data, ensure_ascii=False)}\n\n"
  250. end_data = {
  251. 'code': 200,
  252. 'data': '',
  253. 'finished': True,
  254. 'full_text': "处理结果已发送完毕!"
  255. }
  256. yield f"data: {json.dumps(end_data, ensure_ascii=False)}\n\n"
  257. return Response(stream_with_context(generate_error_response()), mimetype='text/event-stream')