Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

canvas_app.py 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  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 logging
  18. import re
  19. import sys
  20. from functools import partial
  21. import trio
  22. from flask import request, Response
  23. from flask_login import login_required, current_user
  24. from agent.component import LLM
  25. from api.db import FileType
  26. from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService, API4ConversationService
  27. from api.db.services.document_service import DocumentService
  28. from api.db.services.file_service import FileService
  29. from api.db.services.user_service import TenantService, UserTenantService
  30. from api.db.services.user_canvas_version import UserCanvasVersionService
  31. from api.settings import RetCode
  32. from api.utils import get_uuid
  33. from api.utils.api_utils import get_json_result, server_error_response, validate_request, get_data_error_result, \
  34. get_error_data_result
  35. from agent.canvas import Canvas
  36. from peewee import MySQLDatabase, PostgresqlDatabase
  37. from api.db.db_models import APIToken
  38. import time
  39. from api.utils.file_utils import filename_type, read_potential_broken_pdf
  40. from rag.utils.redis_conn import REDIS_CONN
  41. @manager.route('/templates', methods=['GET']) # noqa: F821
  42. @login_required
  43. def templates():
  44. return get_json_result(data=[c.to_dict() for c in CanvasTemplateService.get_all()])
  45. @manager.route('/list', methods=['GET']) # noqa: F821
  46. @login_required
  47. def canvas_list():
  48. return get_json_result(data=sorted([c.to_dict() for c in \
  49. UserCanvasService.query(user_id=current_user.id)], key=lambda x: x["update_time"]*-1)
  50. )
  51. @manager.route('/rm', methods=['POST']) # noqa: F821
  52. @validate_request("canvas_ids")
  53. @login_required
  54. def rm():
  55. for i in request.json["canvas_ids"]:
  56. if not UserCanvasService.query(user_id=current_user.id,id=i):
  57. return get_json_result(
  58. data=False, message='Only owner of canvas authorized for this operation.',
  59. code=RetCode.OPERATING_ERROR)
  60. UserCanvasService.delete_by_id(i)
  61. return get_json_result(data=True)
  62. @manager.route('/set', methods=['POST']) # noqa: F821
  63. @validate_request("dsl", "title")
  64. @login_required
  65. def save():
  66. req = request.json
  67. req["user_id"] = current_user.id
  68. if not isinstance(req["dsl"], str):
  69. req["dsl"] = json.dumps(req["dsl"], ensure_ascii=False)
  70. req["dsl"] = json.loads(req["dsl"])
  71. if "id" not in req:
  72. if UserCanvasService.query(user_id=current_user.id, title=req["title"].strip()):
  73. return get_data_error_result(message=f"{req['title'].strip()} already exists.")
  74. req["id"] = get_uuid()
  75. if not UserCanvasService.save(**req):
  76. return get_data_error_result(message="Fail to save canvas.")
  77. else:
  78. if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
  79. return get_json_result(
  80. data=False, message='Only owner of canvas authorized for this operation.',
  81. code=RetCode.OPERATING_ERROR)
  82. UserCanvasService.update_by_id(req["id"], req)
  83. # save version
  84. UserCanvasVersionService.insert( user_canvas_id=req["id"], dsl=req["dsl"], title="{0}_{1}".format(req["title"], time.strftime("%Y_%m_%d_%H_%M_%S")))
  85. UserCanvasVersionService.delete_all_versions(req["id"])
  86. return get_json_result(data=req)
  87. @manager.route('/get/<canvas_id>', methods=['GET']) # noqa: F821
  88. @login_required
  89. def get(canvas_id):
  90. e, c = UserCanvasService.get_by_tenant_id(canvas_id)
  91. tids = [t.tenant_id for t in UserTenantService.query(user_id=current_user.id)]
  92. if not e or (c["user_id"] != current_user.id and c["user_id"] not in tids):
  93. return get_data_error_result(message="canvas not found.")
  94. return get_json_result(data=c)
  95. @manager.route('/getsse/<canvas_id>', methods=['GET']) # type: ignore # noqa: F821
  96. def getsse(canvas_id):
  97. token = request.headers.get('Authorization').split()
  98. if len(token) != 2:
  99. return get_data_error_result(message='Authorization is not valid!"')
  100. token = token[1]
  101. objs = APIToken.query(beta=token)
  102. if not objs:
  103. return get_data_error_result(message='Authentication error: API key is invalid!"')
  104. tenant_id = objs[0].tenant_id
  105. e, c = UserCanvasService.get_by_id(canvas_id)
  106. if not e or c.user_id != tenant_id:
  107. return get_data_error_result(message="canvas not found.")
  108. return get_json_result(data=c.to_dict())
  109. @manager.route('/completion', methods=['POST']) # noqa: F821
  110. @validate_request("id")
  111. @login_required
  112. def run():
  113. req = request.json
  114. query = req.get("query", "")
  115. files = req.get("files", [])
  116. inputs = req.get("inputs", {})
  117. user_id = req.get("user_id", current_user.id)
  118. e, cvs = UserCanvasService.get_by_id(req["id"])
  119. if not e:
  120. return get_data_error_result(message="canvas not found.")
  121. if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
  122. return get_json_result(
  123. data=False, message='Only owner of canvas authorized for this operation.',
  124. code=RetCode.OPERATING_ERROR)
  125. if not isinstance(cvs.dsl, str):
  126. cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False)
  127. try:
  128. canvas = Canvas(cvs.dsl, current_user.id, req["id"])
  129. except Exception as e:
  130. return server_error_response(e)
  131. def sse():
  132. nonlocal canvas, user_id
  133. try:
  134. for ans in canvas.run(query=query, files=files, user_id=user_id, inputs=inputs):
  135. yield "data:" + json.dumps(ans, ensure_ascii=False) + "\n\n"
  136. cvs.dsl = json.loads(str(canvas))
  137. UserCanvasService.update_by_id(req["id"], cvs.to_dict())
  138. except Exception as e:
  139. logging.exception(e)
  140. yield "data:" + json.dumps({"code": 500, "message": str(e), "data": False}, ensure_ascii=False) + "\n\n"
  141. resp = Response(sse(), mimetype="text/event-stream")
  142. resp.headers.add_header("Cache-control", "no-cache")
  143. resp.headers.add_header("Connection", "keep-alive")
  144. resp.headers.add_header("X-Accel-Buffering", "no")
  145. resp.headers.add_header("Content-Type", "text/event-stream; charset=utf-8")
  146. return resp
  147. @manager.route('/reset', methods=['POST']) # noqa: F821
  148. @validate_request("id")
  149. @login_required
  150. def reset():
  151. req = request.json
  152. try:
  153. e, user_canvas = UserCanvasService.get_by_id(req["id"])
  154. if not e:
  155. return get_data_error_result(message="canvas not found.")
  156. if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
  157. return get_json_result(
  158. data=False, message='Only owner of canvas authorized for this operation.',
  159. code=RetCode.OPERATING_ERROR)
  160. canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id)
  161. canvas.reset()
  162. req["dsl"] = json.loads(str(canvas))
  163. UserCanvasService.update_by_id(req["id"], {"dsl": req["dsl"]})
  164. return get_json_result(data=req["dsl"])
  165. except Exception as e:
  166. return server_error_response(e)
  167. @manager.route("/upload/<canvas_id>", methods=["POST"]) # noqa: F821
  168. def upload(canvas_id):
  169. e, cvs = UserCanvasService.get_by_tenant_id(canvas_id)
  170. if not e:
  171. return get_data_error_result(message="canvas not found.")
  172. user_id = cvs["user_id"]
  173. def structured(filename, filetype, blob, content_type):
  174. nonlocal user_id
  175. if filetype == FileType.PDF.value:
  176. blob = read_potential_broken_pdf(blob)
  177. location = get_uuid()
  178. FileService.put_blob(user_id, location, blob)
  179. return {
  180. "id": location,
  181. "name": filename,
  182. "size": sys.getsizeof(blob),
  183. "extension": filename.split(".")[-1].lower(),
  184. "mime_type": content_type,
  185. "created_by": user_id,
  186. "created_at": time.time(),
  187. "preview_url": None
  188. }
  189. if request.args.get("url"):
  190. from crawl4ai import (
  191. AsyncWebCrawler,
  192. BrowserConfig,
  193. CrawlerRunConfig,
  194. DefaultMarkdownGenerator,
  195. PruningContentFilter,
  196. CrawlResult
  197. )
  198. try:
  199. url = request.args.get("url")
  200. filename = re.sub(r"\?.*", "", url.split("/")[-1])
  201. async def adownload():
  202. browser_config = BrowserConfig(
  203. headless=True,
  204. verbose=False,
  205. )
  206. async with AsyncWebCrawler(config=browser_config) as crawler:
  207. crawler_config = CrawlerRunConfig(
  208. markdown_generator=DefaultMarkdownGenerator(
  209. content_filter=PruningContentFilter()
  210. ),
  211. pdf=True,
  212. screenshot=False
  213. )
  214. result: CrawlResult = await crawler.arun(
  215. url=url,
  216. config=crawler_config
  217. )
  218. return result
  219. page = trio.run(adownload())
  220. if page.pdf:
  221. if filename.split(".")[-1].lower() != "pdf":
  222. filename += ".pdf"
  223. return get_json_result(data=structured(filename, "pdf", page.pdf, page.response_headers["content-type"]))
  224. return get_json_result(data=structured(filename, "html", str(page.markdown).encode("utf-8"), page.response_headers["content-type"], user_id))
  225. except Exception as e:
  226. return server_error_response(e)
  227. file = request.files['file']
  228. try:
  229. DocumentService.check_doc_health(user_id, file.filename)
  230. return get_json_result(data=structured(file.filename, filename_type(file.filename), file.read(), file.content_type))
  231. except Exception as e:
  232. return server_error_response(e)
  233. @manager.route('/input_form', methods=['GET']) # noqa: F821
  234. @login_required
  235. def input_form():
  236. cvs_id = request.args.get("id")
  237. cpn_id = request.args.get("component_id")
  238. try:
  239. e, user_canvas = UserCanvasService.get_by_id(cvs_id)
  240. if not e:
  241. return get_data_error_result(message="canvas not found.")
  242. if not UserCanvasService.query(user_id=current_user.id, id=cvs_id):
  243. return get_json_result(
  244. data=False, message='Only owner of canvas authorized for this operation.',
  245. code=RetCode.OPERATING_ERROR)
  246. canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id)
  247. return get_json_result(data=canvas.get_component_input_form(cpn_id))
  248. except Exception as e:
  249. return server_error_response(e)
  250. @manager.route('/debug', methods=['POST']) # noqa: F821
  251. @validate_request("id", "component_id", "params")
  252. @login_required
  253. def debug():
  254. req = request.json
  255. try:
  256. e, user_canvas = UserCanvasService.get_by_id(req["id"])
  257. if not e:
  258. return get_data_error_result(message="canvas not found.")
  259. if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
  260. return get_json_result(
  261. data=False, message='Only owner of canvas authorized for this operation.',
  262. code=RetCode.OPERATING_ERROR)
  263. canvas = Canvas(json.dumps(user_canvas.dsl), current_user.id)
  264. canvas.reset()
  265. canvas.message_id = get_uuid()
  266. component = canvas.get_component(req["component_id"])["obj"]
  267. component.reset()
  268. if isinstance(component, LLM):
  269. component.set_debug_inputs(req["params"])
  270. component.invoke(**{k: o["value"] for k,o in req["params"].items()})
  271. outputs = component.output()
  272. for k in outputs.keys():
  273. if isinstance(outputs[k], partial):
  274. txt = ""
  275. for c in outputs[k]():
  276. txt += c
  277. outputs[k] = txt
  278. return get_json_result(data=outputs)
  279. except Exception as e:
  280. return server_error_response(e)
  281. @manager.route('/test_db_connect', methods=['POST']) # noqa: F821
  282. @validate_request("db_type", "database", "username", "host", "port", "password")
  283. @login_required
  284. def test_db_connect():
  285. req = request.json
  286. try:
  287. if req["db_type"] in ["mysql", "mariadb"]:
  288. db = MySQLDatabase(req["database"], user=req["username"], host=req["host"], port=req["port"],
  289. password=req["password"])
  290. elif req["db_type"] == 'postgresql':
  291. db = PostgresqlDatabase(req["database"], user=req["username"], host=req["host"], port=req["port"],
  292. password=req["password"])
  293. elif req["db_type"] == 'mssql':
  294. import pyodbc
  295. connection_string = (
  296. f"DRIVER={{ODBC Driver 17 for SQL Server}};"
  297. f"SERVER={req['host']},{req['port']};"
  298. f"DATABASE={req['database']};"
  299. f"UID={req['username']};"
  300. f"PWD={req['password']};"
  301. )
  302. db = pyodbc.connect(connection_string)
  303. cursor = db.cursor()
  304. cursor.execute("SELECT 1")
  305. cursor.close()
  306. else:
  307. return server_error_response("Unsupported database type.")
  308. if req["db_type"] != 'mssql':
  309. db.connect()
  310. db.close()
  311. return get_json_result(data="Database Connection Successful!")
  312. except Exception as e:
  313. return server_error_response(e)
  314. #api get list version dsl of canvas
  315. @manager.route('/getlistversion/<canvas_id>', methods=['GET']) # noqa: F821
  316. @login_required
  317. def getlistversion(canvas_id):
  318. try:
  319. list =sorted([c.to_dict() for c in UserCanvasVersionService.list_by_canvas_id(canvas_id)], key=lambda x: x["update_time"]*-1)
  320. return get_json_result(data=list)
  321. except Exception as e:
  322. return get_data_error_result(message=f"Error getting history files: {e}")
  323. #api get version dsl of canvas
  324. @manager.route('/getversion/<version_id>', methods=['GET']) # noqa: F821
  325. @login_required
  326. def getversion( version_id):
  327. try:
  328. e, version = UserCanvasVersionService.get_by_id(version_id)
  329. if version:
  330. return get_json_result(data=version.to_dict())
  331. except Exception as e:
  332. return get_json_result(data=f"Error getting history file: {e}")
  333. @manager.route('/listteam', methods=['GET']) # noqa: F821
  334. @login_required
  335. def list_kbs():
  336. keywords = request.args.get("keywords", "")
  337. page_number = int(request.args.get("page", 1))
  338. items_per_page = int(request.args.get("page_size", 150))
  339. orderby = request.args.get("orderby", "create_time")
  340. desc = request.args.get("desc", True)
  341. try:
  342. tenants = TenantService.get_joined_tenants_by_user_id(current_user.id)
  343. kbs, total = UserCanvasService.get_by_tenant_ids(
  344. [m["tenant_id"] for m in tenants], current_user.id, page_number,
  345. items_per_page, orderby, desc, keywords)
  346. return get_json_result(data={"kbs": kbs, "total": total})
  347. except Exception as e:
  348. return server_error_response(e)
  349. @manager.route('/setting', methods=['POST']) # noqa: F821
  350. @validate_request("id", "title", "permission")
  351. @login_required
  352. def setting():
  353. req = request.json
  354. req["user_id"] = current_user.id
  355. e,flow = UserCanvasService.get_by_id(req["id"])
  356. if not e:
  357. return get_data_error_result(message="canvas not found.")
  358. flow = flow.to_dict()
  359. flow["title"] = req["title"]
  360. if req["description"]:
  361. flow["description"] = req["description"]
  362. if req["permission"]:
  363. flow["permission"] = req["permission"]
  364. if req["avatar"]:
  365. flow["avatar"] = req["avatar"]
  366. if not UserCanvasService.query(user_id=current_user.id, id=req["id"]):
  367. return get_json_result(
  368. data=False, message='Only owner of canvas authorized for this operation.',
  369. code=RetCode.OPERATING_ERROR)
  370. num= UserCanvasService.update_by_id(req["id"], flow)
  371. return get_json_result(data=num)
  372. @manager.route('/trace', methods=['GET']) # noqa: F821
  373. def trace():
  374. cvs_id = request.args.get("canvas_id")
  375. msg_id = request.args.get("message_id")
  376. try:
  377. bin = REDIS_CONN.get(f"{cvs_id}-{msg_id}-logs")
  378. if not bin:
  379. return get_json_result(data={})
  380. return get_json_result(data=json.loads(bin.encode("utf-8")))
  381. except Exception as e:
  382. logging.exception(e)
  383. @manager.route('/<canvas_id>/sessions', methods=['GET']) # noqa: F821
  384. @login_required
  385. def sessions(canvas_id):
  386. tenant_id = current_user.id
  387. if not UserCanvasService.query(user_id=tenant_id, id=canvas_id):
  388. return get_error_data_result(message=f"You don't own the agent {canvas_id}.")
  389. user_id = request.args.get("user_id")
  390. page_number = int(request.args.get("page", 1))
  391. items_per_page = int(request.args.get("page_size", 30))
  392. keywords = request.args.get("keywords")
  393. from_date = request.args.get("from_date")
  394. to_date = request.args.get("to_date")
  395. orderby = request.args.get("orderby", "update_time")
  396. if request.args.get("desc") == "False" or request.args.get("desc") == "false":
  397. desc = False
  398. else:
  399. desc = True
  400. # dsl defaults to True in all cases except for False and false
  401. include_dsl = request.args.get("dsl") != "False" and request.args.get("dsl") != "false"
  402. total, sess = API4ConversationService.get_list(canvas_id, tenant_id, page_number, items_per_page, orderby, desc,
  403. None, user_id, include_dsl, keywords, from_date, to_date)
  404. try:
  405. return get_json_result(data={"total": total, "sessions": sess})
  406. except Exception as e:
  407. return server_error_response(e)