app.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. import json
  2. from flask_login import current_user
  3. from flask_restful import Resource, inputs, marshal_with, reqparse
  4. from werkzeug.exceptions import Forbidden, BadRequest
  5. from controllers.console import api
  6. from controllers.console.app.wraps import get_app_model
  7. from controllers.console.setup import setup_required
  8. from controllers.console.wraps import account_initialization_required, cloud_edition_billing_resource_check
  9. from core.agent.entities import AgentToolEntity
  10. from extensions.ext_database import db
  11. from fields.app_fields import (
  12. app_detail_fields,
  13. app_detail_fields_with_site,
  14. app_pagination_fields,
  15. )
  16. from libs.login import login_required
  17. from services.app_service import AppService
  18. from models.model import App, AppModelConfig, AppMode
  19. from core.tools.utils.configuration import ToolParameterConfigurationManager
  20. from core.tools.tool_manager import ToolManager
  21. ALLOW_CREATE_APP_MODES = ['chat', 'agent-chat', 'advanced-chat', 'workflow', 'completion']
  22. class AppListApi(Resource):
  23. @setup_required
  24. @login_required
  25. @account_initialization_required
  26. @marshal_with(app_pagination_fields)
  27. def get(self):
  28. """Get app list"""
  29. parser = reqparse.RequestParser()
  30. parser.add_argument('page', type=inputs.int_range(1, 99999), required=False, default=1, location='args')
  31. parser.add_argument('limit', type=inputs.int_range(1, 100), required=False, default=20, location='args')
  32. parser.add_argument('mode', type=str, choices=['chat', 'workflow', 'agent-chat', 'channel', 'all'], default='all', location='args', required=False)
  33. parser.add_argument('name', type=str, location='args', required=False)
  34. args = parser.parse_args()
  35. # get app list
  36. app_service = AppService()
  37. app_pagination = app_service.get_paginate_apps(current_user.current_tenant_id, args)
  38. return app_pagination
  39. @setup_required
  40. @login_required
  41. @account_initialization_required
  42. @marshal_with(app_detail_fields)
  43. @cloud_edition_billing_resource_check('apps')
  44. def post(self):
  45. """Create app"""
  46. parser = reqparse.RequestParser()
  47. parser.add_argument('name', type=str, required=True, location='json')
  48. parser.add_argument('description', type=str, location='json')
  49. parser.add_argument('mode', type=str, choices=ALLOW_CREATE_APP_MODES, location='json')
  50. parser.add_argument('icon', type=str, location='json')
  51. parser.add_argument('icon_background', type=str, location='json')
  52. args = parser.parse_args()
  53. # The role of the current user in the ta table must be admin or owner
  54. if not current_user.is_admin_or_owner:
  55. raise Forbidden()
  56. if 'mode' not in args or args['mode'] is None:
  57. raise BadRequest("mode is required")
  58. app_service = AppService()
  59. app = app_service.create_app(current_user.current_tenant_id, args, current_user)
  60. return app, 201
  61. class AppImportApi(Resource):
  62. @setup_required
  63. @login_required
  64. @account_initialization_required
  65. @marshal_with(app_detail_fields_with_site)
  66. @cloud_edition_billing_resource_check('apps')
  67. def post(self):
  68. """Import app"""
  69. # The role of the current user in the ta table must be admin or owner
  70. if not current_user.is_admin_or_owner:
  71. raise Forbidden()
  72. parser = reqparse.RequestParser()
  73. parser.add_argument('data', type=str, required=True, nullable=False, location='json')
  74. parser.add_argument('name', type=str, location='json')
  75. parser.add_argument('description', type=str, location='json')
  76. parser.add_argument('icon', type=str, location='json')
  77. parser.add_argument('icon_background', type=str, location='json')
  78. args = parser.parse_args()
  79. app_service = AppService()
  80. app = app_service.import_app(current_user.current_tenant_id, args['data'], args, current_user)
  81. return app, 201
  82. class AppApi(Resource):
  83. @setup_required
  84. @login_required
  85. @account_initialization_required
  86. @get_app_model
  87. @marshal_with(app_detail_fields_with_site)
  88. def get(self, app_model):
  89. """Get app detail"""
  90. # get original app model config
  91. if app_model.mode == AppMode.AGENT_CHAT.value or app_model.is_agent:
  92. model_config: AppModelConfig = app_model.app_model_config
  93. agent_mode = model_config.agent_mode_dict
  94. # decrypt agent tool parameters if it's secret-input
  95. for tool in agent_mode.get('tools') or []:
  96. if not isinstance(tool, dict) or len(tool.keys()) <= 3:
  97. continue
  98. agent_tool_entity = AgentToolEntity(**tool)
  99. # get tool
  100. try:
  101. tool_runtime = ToolManager.get_agent_tool_runtime(
  102. tenant_id=current_user.current_tenant_id,
  103. agent_tool=agent_tool_entity,
  104. )
  105. manager = ToolParameterConfigurationManager(
  106. tenant_id=current_user.current_tenant_id,
  107. tool_runtime=tool_runtime,
  108. provider_name=agent_tool_entity.provider_id,
  109. provider_type=agent_tool_entity.provider_type,
  110. )
  111. # get decrypted parameters
  112. if agent_tool_entity.tool_parameters:
  113. parameters = manager.decrypt_tool_parameters(agent_tool_entity.tool_parameters or {})
  114. masked_parameter = manager.mask_tool_parameters(parameters or {})
  115. else:
  116. masked_parameter = {}
  117. # override tool parameters
  118. tool['tool_parameters'] = masked_parameter
  119. except Exception as e:
  120. pass
  121. # override agent mode
  122. model_config.agent_mode = json.dumps(agent_mode)
  123. db.session.commit()
  124. return app_model
  125. @setup_required
  126. @login_required
  127. @account_initialization_required
  128. @get_app_model
  129. @marshal_with(app_detail_fields_with_site)
  130. def put(self, app_model):
  131. """Update app"""
  132. parser = reqparse.RequestParser()
  133. parser.add_argument('name', type=str, required=True, nullable=False, location='json')
  134. parser.add_argument('description', type=str, location='json')
  135. parser.add_argument('icon', type=str, location='json')
  136. parser.add_argument('icon_background', type=str, location='json')
  137. args = parser.parse_args()
  138. app_service = AppService()
  139. app_model = app_service.update_app(app_model, args)
  140. return app_model
  141. @setup_required
  142. @login_required
  143. @account_initialization_required
  144. @get_app_model
  145. def delete(self, app_model):
  146. """Delete app"""
  147. if not current_user.is_admin_or_owner:
  148. raise Forbidden()
  149. app_service = AppService()
  150. app_service.delete_app(app_model)
  151. return {'result': 'success'}, 204
  152. class AppCopyApi(Resource):
  153. @setup_required
  154. @login_required
  155. @account_initialization_required
  156. @get_app_model
  157. @marshal_with(app_detail_fields_with_site)
  158. def post(self, app_model):
  159. """Copy app"""
  160. # The role of the current user in the ta table must be admin or owner
  161. if not current_user.is_admin_or_owner:
  162. raise Forbidden()
  163. parser = reqparse.RequestParser()
  164. parser.add_argument('name', type=str, location='json')
  165. parser.add_argument('description', type=str, location='json')
  166. parser.add_argument('icon', type=str, location='json')
  167. parser.add_argument('icon_background', type=str, location='json')
  168. args = parser.parse_args()
  169. app_service = AppService()
  170. data = app_service.export_app(app_model)
  171. app = app_service.import_app(current_user.current_tenant_id, data, args, current_user)
  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. app_service = AppService()
  181. return {
  182. "data": app_service.export_app(app_model)
  183. }
  184. class AppNameApi(Resource):
  185. @setup_required
  186. @login_required
  187. @account_initialization_required
  188. @get_app_model
  189. @marshal_with(app_detail_fields)
  190. def post(self, app_model):
  191. parser = reqparse.RequestParser()
  192. parser.add_argument('name', type=str, required=True, location='json')
  193. args = parser.parse_args()
  194. app_service = AppService()
  195. app_model = app_service.update_app_name(app_model, args.get('name'))
  196. return app_model
  197. class AppIconApi(Resource):
  198. @setup_required
  199. @login_required
  200. @account_initialization_required
  201. @get_app_model
  202. @marshal_with(app_detail_fields)
  203. def post(self, app_model):
  204. parser = reqparse.RequestParser()
  205. parser.add_argument('icon', type=str, location='json')
  206. parser.add_argument('icon_background', type=str, location='json')
  207. args = parser.parse_args()
  208. app_service = AppService()
  209. app_model = app_service.update_app_icon(app_model, args.get('icon'), args.get('icon_background'))
  210. return app_model
  211. class AppSiteStatus(Resource):
  212. @setup_required
  213. @login_required
  214. @account_initialization_required
  215. @get_app_model
  216. @marshal_with(app_detail_fields)
  217. def post(self, app_model):
  218. parser = reqparse.RequestParser()
  219. parser.add_argument('enable_site', type=bool, required=True, location='json')
  220. args = parser.parse_args()
  221. app_service = AppService()
  222. app_model = app_service.update_app_site_status(app_model, args.get('enable_site'))
  223. return app_model
  224. class AppApiStatus(Resource):
  225. @setup_required
  226. @login_required
  227. @account_initialization_required
  228. @get_app_model
  229. @marshal_with(app_detail_fields)
  230. def post(self, app_model):
  231. parser = reqparse.RequestParser()
  232. parser.add_argument('enable_api', type=bool, required=True, location='json')
  233. args = parser.parse_args()
  234. app_service = AppService()
  235. app_model = app_service.update_app_api_status(app_model, args.get('enable_api'))
  236. return app_model
  237. api.add_resource(AppListApi, '/apps')
  238. api.add_resource(AppImportApi, '/apps/import')
  239. api.add_resource(AppApi, '/apps/<uuid:app_id>')
  240. api.add_resource(AppCopyApi, '/apps/<uuid:app_id>/copy')
  241. api.add_resource(AppExportApi, '/apps/<uuid:app_id>/export')
  242. api.add_resource(AppNameApi, '/apps/<uuid:app_id>/name')
  243. api.add_resource(AppIconApi, '/apps/<uuid:app_id>/icon')
  244. api.add_resource(AppSiteStatus, '/apps/<uuid:app_id>/site-enable')
  245. api.add_resource(AppApiStatus, '/apps/<uuid:app_id>/api-enable')