app.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. import uuid
  2. from typing import cast
  3. from flask_login import current_user # type: ignore
  4. from flask_restful import Resource, inputs, marshal, marshal_with, reqparse # type: ignore
  5. from sqlalchemy import select
  6. from sqlalchemy.orm import Session
  7. from werkzeug.exceptions import BadRequest, Forbidden, abort
  8. from controllers.console import api
  9. from controllers.console.app.wraps import get_app_model
  10. from controllers.console.wraps import (
  11. account_initialization_required,
  12. cloud_edition_billing_resource_check,
  13. enterprise_license_required,
  14. setup_required,
  15. )
  16. from core.ops.ops_trace_manager import OpsTraceManager
  17. from extensions.ext_database import db
  18. from fields.app_fields import (
  19. app_detail_fields,
  20. app_detail_fields_with_site,
  21. app_pagination_fields,
  22. )
  23. from libs.login import login_required
  24. from models import Account, App
  25. from services.app_dsl_service import AppDslService, ImportMode
  26. from services.app_service import AppService
  27. ALLOW_CREATE_APP_MODES = ["chat", "agent-chat", "advanced-chat", "workflow", "completion"]
  28. class AppListApi(Resource):
  29. @setup_required
  30. @login_required
  31. @account_initialization_required
  32. @enterprise_license_required
  33. def get(self):
  34. """Get app list"""
  35. def uuid_list(value):
  36. try:
  37. return [str(uuid.UUID(v)) for v in value.split(",")]
  38. except ValueError:
  39. abort(400, message="Invalid UUID format in tag_ids.")
  40. parser = reqparse.RequestParser()
  41. parser.add_argument("page", type=inputs.int_range(1, 99999), required=False, default=1, location="args")
  42. parser.add_argument("limit", type=inputs.int_range(1, 100), required=False, default=20, location="args")
  43. parser.add_argument(
  44. "mode",
  45. type=str,
  46. choices=[
  47. "completion",
  48. "chat",
  49. "advanced-chat",
  50. "workflow",
  51. "agent-chat",
  52. "channel",
  53. "all",
  54. ],
  55. default="all",
  56. location="args",
  57. required=False,
  58. )
  59. parser.add_argument("name", type=str, location="args", required=False)
  60. parser.add_argument("tag_ids", type=uuid_list, location="args", required=False)
  61. parser.add_argument("is_created_by_me", type=inputs.boolean, location="args", required=False)
  62. args = parser.parse_args()
  63. # get app list
  64. app_service = AppService()
  65. app_pagination = app_service.get_paginate_apps(current_user.id, current_user.current_tenant_id, args)
  66. if not app_pagination:
  67. return {"data": [], "total": 0, "page": 1, "limit": 20, "has_more": False}
  68. return marshal(app_pagination, app_pagination_fields)
  69. @setup_required
  70. @login_required
  71. @account_initialization_required
  72. @marshal_with(app_detail_fields)
  73. @cloud_edition_billing_resource_check("apps")
  74. def post(self):
  75. """Create app"""
  76. parser = reqparse.RequestParser()
  77. parser.add_argument("name", type=str, required=True, location="json")
  78. parser.add_argument("description", type=str, location="json")
  79. parser.add_argument("mode", type=str, choices=ALLOW_CREATE_APP_MODES, location="json")
  80. parser.add_argument("icon_type", type=str, location="json")
  81. parser.add_argument("icon", type=str, location="json")
  82. parser.add_argument("icon_background", type=str, location="json")
  83. args = parser.parse_args()
  84. # The role of the current user in the ta table must be admin, owner, or editor
  85. if not current_user.is_editor:
  86. raise Forbidden()
  87. if "mode" not in args or args["mode"] is None:
  88. raise BadRequest("mode is required")
  89. app_service = AppService()
  90. app = app_service.create_app(current_user.current_tenant_id, args, current_user)
  91. return app, 201
  92. class AppApi(Resource):
  93. @setup_required
  94. @login_required
  95. @account_initialization_required
  96. @enterprise_license_required
  97. @get_app_model
  98. @marshal_with(app_detail_fields_with_site)
  99. def get(self, app_model):
  100. """Get app detail"""
  101. app_service = AppService()
  102. app_model = app_service.get_app(app_model)
  103. return app_model
  104. @setup_required
  105. @login_required
  106. @account_initialization_required
  107. @get_app_model
  108. @marshal_with(app_detail_fields_with_site)
  109. def put(self, app_model):
  110. """Update app"""
  111. # The role of the current user in the ta table must be admin, owner, or editor
  112. if not current_user.is_editor:
  113. raise Forbidden()
  114. parser = reqparse.RequestParser()
  115. parser.add_argument("name", type=str, required=True, nullable=False, location="json")
  116. parser.add_argument("description", type=str, location="json")
  117. parser.add_argument("icon_type", type=str, location="json")
  118. parser.add_argument("icon", type=str, location="json")
  119. parser.add_argument("icon_background", type=str, location="json")
  120. parser.add_argument("use_icon_as_answer_icon", type=bool, location="json")
  121. args = parser.parse_args()
  122. app_service = AppService()
  123. app_model = app_service.update_app(app_model, args)
  124. return app_model
  125. @setup_required
  126. @login_required
  127. @account_initialization_required
  128. @get_app_model
  129. def delete(self, app_model):
  130. """Delete app"""
  131. # The role of the current user in the ta table must be admin, owner, or editor
  132. if not current_user.is_editor:
  133. raise Forbidden()
  134. app_service = AppService()
  135. app_service.delete_app(app_model)
  136. return {"result": "success"}, 204
  137. class AppCopyApi(Resource):
  138. @setup_required
  139. @login_required
  140. @account_initialization_required
  141. @get_app_model
  142. @marshal_with(app_detail_fields_with_site)
  143. def post(self, app_model):
  144. """Copy app"""
  145. # The role of the current user in the ta table must be admin, owner, or editor
  146. if not current_user.is_editor:
  147. raise Forbidden()
  148. parser = reqparse.RequestParser()
  149. parser.add_argument("name", type=str, location="json")
  150. parser.add_argument("description", type=str, location="json")
  151. parser.add_argument("icon_type", type=str, location="json")
  152. parser.add_argument("icon", type=str, location="json")
  153. parser.add_argument("icon_background", type=str, location="json")
  154. args = parser.parse_args()
  155. with Session(db.engine) as session:
  156. import_service = AppDslService(session)
  157. yaml_content = import_service.export_dsl(app_model=app_model, include_secret=True)
  158. account = cast(Account, current_user)
  159. result = import_service.import_app(
  160. account=account,
  161. import_mode=ImportMode.YAML_CONTENT.value,
  162. yaml_content=yaml_content,
  163. name=args.get("name"),
  164. description=args.get("description"),
  165. icon_type=args.get("icon_type"),
  166. icon=args.get("icon"),
  167. icon_background=args.get("icon_background"),
  168. )
  169. session.commit()
  170. stmt = select(App).where(App.id == result.app_id)
  171. app = session.scalar(stmt)
  172. return app, 201
  173. class AppExportApi(Resource):
  174. @setup_required
  175. @login_required
  176. @account_initialization_required
  177. @get_app_model
  178. def get(self, app_model):
  179. """Export app"""
  180. # The role of the current user in the ta table must be admin, owner, or editor
  181. if not current_user.is_editor:
  182. raise Forbidden()
  183. # Add include_secret params
  184. parser = reqparse.RequestParser()
  185. parser.add_argument("include_secret", type=inputs.boolean, default=False, location="args")
  186. args = parser.parse_args()
  187. return {"data": AppDslService.export_dsl(app_model=app_model, include_secret=args["include_secret"])}
  188. class AppNameApi(Resource):
  189. @setup_required
  190. @login_required
  191. @account_initialization_required
  192. @get_app_model
  193. @marshal_with(app_detail_fields)
  194. def post(self, app_model):
  195. # The role of the current user in the ta table must be admin, owner, or editor
  196. if not current_user.is_editor:
  197. raise Forbidden()
  198. parser = reqparse.RequestParser()
  199. parser.add_argument("name", type=str, required=True, location="json")
  200. args = parser.parse_args()
  201. app_service = AppService()
  202. app_model = app_service.update_app_name(app_model, args.get("name"))
  203. return app_model
  204. class AppIconApi(Resource):
  205. @setup_required
  206. @login_required
  207. @account_initialization_required
  208. @get_app_model
  209. @marshal_with(app_detail_fields)
  210. def post(self, app_model):
  211. # The role of the current user in the ta table must be admin, owner, or editor
  212. if not current_user.is_editor:
  213. raise Forbidden()
  214. parser = reqparse.RequestParser()
  215. parser.add_argument("icon", type=str, location="json")
  216. parser.add_argument("icon_background", type=str, location="json")
  217. args = parser.parse_args()
  218. app_service = AppService()
  219. app_model = app_service.update_app_icon(app_model, args.get("icon"), args.get("icon_background"))
  220. return app_model
  221. class AppSiteStatus(Resource):
  222. @setup_required
  223. @login_required
  224. @account_initialization_required
  225. @get_app_model
  226. @marshal_with(app_detail_fields)
  227. def post(self, app_model):
  228. # The role of the current user in the ta table must be admin, owner, or editor
  229. if not current_user.is_editor:
  230. raise Forbidden()
  231. parser = reqparse.RequestParser()
  232. parser.add_argument("enable_site", type=bool, required=True, location="json")
  233. args = parser.parse_args()
  234. app_service = AppService()
  235. app_model = app_service.update_app_site_status(app_model, args.get("enable_site"))
  236. return app_model
  237. class AppApiStatus(Resource):
  238. @setup_required
  239. @login_required
  240. @account_initialization_required
  241. @get_app_model
  242. @marshal_with(app_detail_fields)
  243. def post(self, app_model):
  244. # The role of the current user in the ta table must be admin or owner
  245. if not current_user.is_admin_or_owner:
  246. raise Forbidden()
  247. parser = reqparse.RequestParser()
  248. parser.add_argument("enable_api", type=bool, required=True, location="json")
  249. args = parser.parse_args()
  250. app_service = AppService()
  251. app_model = app_service.update_app_api_status(app_model, args.get("enable_api"))
  252. return app_model
  253. class AppTraceApi(Resource):
  254. @setup_required
  255. @login_required
  256. @account_initialization_required
  257. def get(self, app_id):
  258. """Get app trace"""
  259. app_trace_config = OpsTraceManager.get_app_tracing_config(app_id=app_id)
  260. return app_trace_config
  261. @setup_required
  262. @login_required
  263. @account_initialization_required
  264. def post(self, app_id):
  265. # add app trace
  266. if not current_user.is_editor:
  267. raise Forbidden()
  268. parser = reqparse.RequestParser()
  269. parser.add_argument("enabled", type=bool, required=True, location="json")
  270. parser.add_argument("tracing_provider", type=str, required=True, location="json")
  271. args = parser.parse_args()
  272. OpsTraceManager.update_app_tracing_config(
  273. app_id=app_id,
  274. enabled=args["enabled"],
  275. tracing_provider=args["tracing_provider"],
  276. )
  277. return {"result": "success"}
  278. api.add_resource(AppListApi, "/apps")
  279. api.add_resource(AppApi, "/apps/<uuid:app_id>")
  280. api.add_resource(AppCopyApi, "/apps/<uuid:app_id>/copy")
  281. api.add_resource(AppExportApi, "/apps/<uuid:app_id>/export")
  282. api.add_resource(AppNameApi, "/apps/<uuid:app_id>/name")
  283. api.add_resource(AppIconApi, "/apps/<uuid:app_id>/icon")
  284. api.add_resource(AppSiteStatus, "/apps/<uuid:app_id>/site-enable")
  285. api.add_resource(AppApiStatus, "/apps/<uuid:app_id>/api-enable")
  286. api.add_resource(AppTraceApi, "/apps/<uuid:app_id>/trace")