您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

canvas_app.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. #
  2. # Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. #
  16. import json
  17. import traceback
  18. from flask import request, Response
  19. from flask_login import login_required, current_user
  20. from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService
  21. from api.db.services.user_service import TenantService
  22. from api.db.services.user_canvas_version import UserCanvasVersionService
  23. from api.settings import RetCode
  24. from api.utils import get_uuid
  25. from api.utils.api_utils import get_json_result, server_error_response, validate_request, get_data_error_result
  26. from agent.canvas import Canvas
  27. from peewee import MySQLDatabase, PostgresqlDatabase
  28. from api.db.db_models import APIToken
  29. import time
  30. @manager.route('/templates', methods=['GET']) # noqa: F821
  31. @login_required
  32. def templates():
  33. return get_json_result(data=[c.to_dict() for c in CanvasTemplateService.get_all()])
  34. @manager.route('/list', methods=['GET']) # noqa: F821
  35. @login_required
  36. def canvas_list():
  37. return get_json_result(data=sorted([c.to_dict() for c in \
  38. UserCanvasService.query(user_id=current_user.id)], key=lambda x: x["update_time"]*-1)
  39. )
  40. @manager.route('/rm', methods=['POST']) # noqa: F821
  41. @validate_request("canvas_ids")
  42. @login_required
  43. def rm():
  44. for i in request.json["canvas_ids"]:
  45. if not UserCanvasService.query(user_id=current_user.id,id=i):
  46. return get_json_result(
  47. data=False, message='Only owner of canvas authorized for this operation.',
  48. code=RetCode.OPERATING_ERROR)
  49. UserCanvasService.delete_by_id(i)
  50. return get_json_result(data=True)
  51. @manager.route('/set', methods=['POST']) # noqa: F821
  52. @validate_request("dsl", "title")
  53. @login_required
  54. def save():
  55. req = request.json
  56. req["user_id"] = current_user.id
  57. if not isinstance(req["dsl"], str):
  58. req["dsl"] = json.dumps(req["dsl"], ensure_ascii=False)
  59. req["dsl"] = json.loads(req["dsl"])
  60. if "id" not in req:
  61. if UserCanvasService.query(user_id=current_user.id, title=req["title"].strip()):
  62. return get_data_error_result(message=f"{req['title'].strip()} already exists.")
  63. req["id"] = get_uuid()
  64. if not UserCanvasService.save(**req):
  65. return get_data_error_result(message="Fail to save canvas.")
  66. else:
  67. if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
  68. return get_json_result(
  69. data=False, message='Only owner of canvas authorized for this operation.',
  70. code=RetCode.OPERATING_ERROR)
  71. UserCanvasService.update_by_id(req["id"], req)
  72. # save version
  73. UserCanvasVersionService.insert( user_canvas_id=req["id"], dsl=req["dsl"], title="{0}_{1}".format(req["title"], time.strftime("%Y_%m_%d_%H_%M_%S")))
  74. UserCanvasVersionService.delete_all_versions(req["id"])
  75. return get_json_result(data=req)
  76. @manager.route('/get/<canvas_id>', methods=['GET']) # noqa: F821
  77. @login_required
  78. def get(canvas_id):
  79. e, c = UserCanvasService.get_by_tenant_id(canvas_id)
  80. if not e:
  81. return get_data_error_result(message="canvas not found.")
  82. return get_json_result(data=c)
  83. @manager.route('/getsse/<canvas_id>', methods=['GET']) # type: ignore # noqa: F821
  84. def getsse(canvas_id):
  85. token = request.headers.get('Authorization').split()
  86. if len(token) != 2:
  87. return get_data_error_result(message='Authorization is not valid!"')
  88. token = token[1]
  89. objs = APIToken.query(beta=token)
  90. if not objs:
  91. return get_data_error_result(message='Authentication error: API key is invalid!"')
  92. e, c = UserCanvasService.get_by_id(canvas_id)
  93. if not e:
  94. return get_data_error_result(message="canvas not found.")
  95. return get_json_result(data=c.to_dict())
  96. @manager.route('/completion', methods=['POST']) # noqa: F821
  97. @validate_request("id")
  98. @login_required
  99. def run():
  100. req = request.json
  101. stream = req.get("stream", True)
  102. e, cvs = UserCanvasService.get_by_id(req["id"])
  103. if not e:
  104. return get_data_error_result(message="canvas not found.")
  105. if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
  106. return get_json_result(
  107. data=False, message='Only owner of canvas authorized for this operation.',
  108. code=RetCode.OPERATING_ERROR)
  109. if not isinstance(cvs.dsl, str):
  110. cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False)
  111. final_ans = {"reference": [], "content": ""}
  112. message_id = req.get("message_id", get_uuid())
  113. try:
  114. canvas = Canvas(cvs.dsl, current_user.id)
  115. if "message" in req:
  116. canvas.messages.append({"role": "user", "content": req["message"], "id": message_id})
  117. canvas.add_user_input(req["message"])
  118. except Exception as e:
  119. return server_error_response(e)
  120. if stream:
  121. def sse():
  122. nonlocal answer, cvs
  123. try:
  124. for ans in canvas.run(stream=True):
  125. if ans.get("running_status"):
  126. yield "data:" + json.dumps({"code": 0, "message": "",
  127. "data": {"answer": ans["content"],
  128. "running_status": True}},
  129. ensure_ascii=False) + "\n\n"
  130. continue
  131. for k in ans.keys():
  132. final_ans[k] = ans[k]
  133. ans = {"answer": ans["content"], "reference": ans.get("reference", [])}
  134. yield "data:" + json.dumps({"code": 0, "message": "", "data": ans}, ensure_ascii=False) + "\n\n"
  135. canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id})
  136. canvas.history.append(("assistant", final_ans["content"]))
  137. if not canvas.path[-1]:
  138. canvas.path.pop(-1)
  139. if final_ans.get("reference"):
  140. canvas.reference.append(final_ans["reference"])
  141. cvs.dsl = json.loads(str(canvas))
  142. UserCanvasService.update_by_id(req["id"], cvs.to_dict())
  143. except Exception as e:
  144. cvs.dsl = json.loads(str(canvas))
  145. if not canvas.path[-1]:
  146. canvas.path.pop(-1)
  147. UserCanvasService.update_by_id(req["id"], cvs.to_dict())
  148. traceback.print_exc()
  149. yield "data:" + json.dumps({"code": 500, "message": str(e),
  150. "data": {"answer": "**ERROR**: " + str(e), "reference": []}},
  151. ensure_ascii=False) + "\n\n"
  152. yield "data:" + json.dumps({"code": 0, "message": "", "data": True}, ensure_ascii=False) + "\n\n"
  153. resp = Response(sse(), mimetype="text/event-stream")
  154. resp.headers.add_header("Cache-control", "no-cache")
  155. resp.headers.add_header("Connection", "keep-alive")
  156. resp.headers.add_header("X-Accel-Buffering", "no")
  157. resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8")
  158. return resp
  159. for answer in canvas.run(stream=False):
  160. if answer.get("running_status"):
  161. continue
  162. final_ans["content"] = "\n".join(answer["content"]) if "content" in answer else ""
  163. canvas.messages.append({"role": "assistant", "content": final_ans["content"], "id": message_id})
  164. if final_ans.get("reference"):
  165. canvas.reference.append(final_ans["reference"])
  166. cvs.dsl = json.loads(str(canvas))
  167. UserCanvasService.update_by_id(req["id"], cvs.to_dict())
  168. return get_json_result(data={"answer": final_ans["content"], "reference": final_ans.get("reference", [])})
  169. @manager.route('/reset', methods=['POST']) # noqa: F821
  170. @validate_request("id")
  171. @login_required
  172. def reset():
  173. req = request.json
  174. try:
  175. e, user_canvas = UserCanvasService.get_by_id(req["id"])
  176. if not e:
  177. return get_data_error_result(message="canvas not found.")
  178. if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
  179. return get_json_result(
  180. data=False, message='Only owner of canvas authorized for this operation.',
  181. code=RetCode.OPERATING_ERROR)
  182. canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id)
  183. canvas.reset()
  184. req["dsl"] = json.loads(str(canvas))
  185. UserCanvasService.update_by_id(req["id"], {"dsl": req["dsl"]})
  186. return get_json_result(data=req["dsl"])
  187. except Exception as e:
  188. return server_error_response(e)
  189. @manager.route('/input_elements', methods=['GET']) # noqa: F821
  190. @login_required
  191. def input_elements():
  192. cvs_id = request.args.get("id")
  193. cpn_id = request.args.get("component_id")
  194. try:
  195. e, user_canvas = UserCanvasService.get_by_id(cvs_id)
  196. if not e:
  197. return get_data_error_result(message="canvas not found.")
  198. if not UserCanvasService.query(user_id=current_user.id, id=cvs_id):
  199. return get_json_result(
  200. data=False, message='Only owner of canvas authorized for this operation.',
  201. code=RetCode.OPERATING_ERROR)
  202. canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id)
  203. return get_json_result(data=canvas.get_component_input_elements(cpn_id))
  204. except Exception as e:
  205. return server_error_response(e)
  206. @manager.route('/debug', methods=['POST']) # noqa: F821
  207. @validate_request("id", "component_id", "params")
  208. @login_required
  209. def debug():
  210. req = request.json
  211. for p in req["params"]:
  212. assert p.get("key")
  213. try:
  214. e, user_canvas = UserCanvasService.get_by_id(req["id"])
  215. if not e:
  216. return get_data_error_result(message="canvas not found.")
  217. if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
  218. return get_json_result(
  219. data=False, message='Only owner of canvas authorized for this operation.',
  220. code=RetCode.OPERATING_ERROR)
  221. canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id)
  222. canvas.get_component(req["component_id"])["obj"]._param.debug_inputs = req["params"]
  223. df = canvas.get_component(req["component_id"])["obj"].debug()
  224. return get_json_result(data=df.to_dict(orient="records"))
  225. except Exception as e:
  226. return server_error_response(e)
  227. @manager.route('/test_db_connect', methods=['POST']) # noqa: F821
  228. @validate_request("db_type", "database", "username", "host", "port", "password")
  229. @login_required
  230. def test_db_connect():
  231. req = request.json
  232. try:
  233. if req["db_type"] in ["mysql", "mariadb"]:
  234. db = MySQLDatabase(req["database"], user=req["username"], host=req["host"], port=req["port"],
  235. password=req["password"])
  236. elif req["db_type"] == 'postgresql':
  237. db = PostgresqlDatabase(req["database"], user=req["username"], host=req["host"], port=req["port"],
  238. password=req["password"])
  239. elif req["db_type"] == 'mssql':
  240. import pyodbc
  241. connection_string = (
  242. f"DRIVER={{ODBC Driver 17 for SQL Server}};"
  243. f"SERVER={req['host']},{req['port']};"
  244. f"DATABASE={req['database']};"
  245. f"UID={req['username']};"
  246. f"PWD={req['password']};"
  247. )
  248. db = pyodbc.connect(connection_string)
  249. cursor = db.cursor()
  250. cursor.execute("SELECT 1")
  251. cursor.close()
  252. else:
  253. return server_error_response("Unsupported database type.")
  254. if req["db_type"] != 'mssql':
  255. db.connect()
  256. db.close()
  257. return get_json_result(data="Database Connection Successful!")
  258. except Exception as e:
  259. return server_error_response(e)
  260. #api get list version dsl of canvas
  261. @manager.route('/getlistversion/<canvas_id>', methods=['GET']) # noqa: F821
  262. @login_required
  263. def getlistversion(canvas_id):
  264. try:
  265. list =sorted([c.to_dict() for c in UserCanvasVersionService.list_by_canvas_id(canvas_id)], key=lambda x: x["update_time"]*-1)
  266. return get_json_result(data=list)
  267. except Exception as e:
  268. return get_data_error_result(message=f"Error getting history files: {e}")
  269. #api get version dsl of canvas
  270. @manager.route('/getversion/<version_id>', methods=['GET']) # noqa: F821
  271. @login_required
  272. def getversion( version_id):
  273. try:
  274. e, version = UserCanvasVersionService.get_by_id(version_id)
  275. if version:
  276. return get_json_result(data=version.to_dict())
  277. except Exception as e:
  278. return get_json_result(data=f"Error getting history file: {e}")
  279. @manager.route('/listteam', methods=['GET']) # noqa: F821
  280. @login_required
  281. def list_kbs():
  282. keywords = request.args.get("keywords", "")
  283. page_number = int(request.args.get("page", 1))
  284. items_per_page = int(request.args.get("page_size", 150))
  285. orderby = request.args.get("orderby", "create_time")
  286. desc = request.args.get("desc", True)
  287. try:
  288. tenants = TenantService.get_joined_tenants_by_user_id(current_user.id)
  289. kbs, total = UserCanvasService.get_by_tenant_ids(
  290. [m["tenant_id"] for m in tenants], current_user.id, page_number,
  291. items_per_page, orderby, desc, keywords)
  292. return get_json_result(data={"kbs": kbs, "total": total})
  293. except Exception as e:
  294. return server_error_response(e)
  295. @manager.route('/setting', methods=['POST']) # noqa: F821
  296. @validate_request("id", "title", "permission")
  297. @login_required
  298. def setting():
  299. req = request.json
  300. req["user_id"] = current_user.id
  301. e,flow = UserCanvasService.get_by_id(req["id"])
  302. if not e:
  303. return get_data_error_result(message="canvas not found.")
  304. flow = flow.to_dict()
  305. flow["title"] = req["title"]
  306. if req["description"]:
  307. flow["description"] = req["description"]
  308. if req["permission"]:
  309. flow["permission"] = req["permission"]
  310. if req["avatar"]:
  311. flow["avatar"] = req["avatar"]
  312. if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
  313. return get_json_result(
  314. data=False, message='Only owner of canvas authorized for this operation.',
  315. code=RetCode.OPERATING_ERROR)
  316. num= UserCanvasService.update_by_id(req["id"], flow)
  317. return get_json_result(data=num)