annotation.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. from controllers.console import api
  2. from controllers.console.app.error import NoFileUploadedError
  3. from controllers.console.datasets.error import TooManyFilesError
  4. from controllers.console.setup import setup_required
  5. from controllers.console.wraps import account_initialization_required, cloud_edition_billing_resource_check
  6. from extensions.ext_redis import redis_client
  7. from fields.annotation_fields import (annotation_fields, annotation_hit_history_fields,
  8. annotation_hit_history_list_fields, annotation_list_fields)
  9. from flask import request
  10. from flask_login import current_user
  11. from flask_restful import Resource, marshal, marshal_with, reqparse
  12. from libs.login import login_required
  13. from services.annotation_service import AppAnnotationService
  14. from werkzeug.exceptions import Forbidden
  15. class AnnotationReplyActionApi(Resource):
  16. @setup_required
  17. @login_required
  18. @account_initialization_required
  19. @cloud_edition_billing_resource_check('annotation')
  20. def post(self, app_id, action):
  21. # The role of the current user in the ta table must be admin or owner
  22. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  23. raise Forbidden()
  24. app_id = str(app_id)
  25. parser = reqparse.RequestParser()
  26. parser.add_argument('score_threshold', required=True, type=float, location='json')
  27. parser.add_argument('embedding_provider_name', required=True, type=str, location='json')
  28. parser.add_argument('embedding_model_name', required=True, type=str, location='json')
  29. args = parser.parse_args()
  30. if action == 'enable':
  31. result = AppAnnotationService.enable_app_annotation(args, app_id)
  32. elif action == 'disable':
  33. result = AppAnnotationService.disable_app_annotation(app_id)
  34. else:
  35. raise ValueError('Unsupported annotation reply action')
  36. return result, 200
  37. class AppAnnotationSettingDetailApi(Resource):
  38. @setup_required
  39. @login_required
  40. @account_initialization_required
  41. def get(self, app_id):
  42. # The role of the current user in the ta table must be admin or owner
  43. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  44. raise Forbidden()
  45. app_id = str(app_id)
  46. result = AppAnnotationService.get_app_annotation_setting_by_app_id(app_id)
  47. return result, 200
  48. class AppAnnotationSettingUpdateApi(Resource):
  49. @setup_required
  50. @login_required
  51. @account_initialization_required
  52. def post(self, app_id, annotation_setting_id):
  53. # The role of the current user in the ta table must be admin or owner
  54. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  55. raise Forbidden()
  56. app_id = str(app_id)
  57. annotation_setting_id = str(annotation_setting_id)
  58. parser = reqparse.RequestParser()
  59. parser.add_argument('score_threshold', required=True, type=float, location='json')
  60. args = parser.parse_args()
  61. result = AppAnnotationService.update_app_annotation_setting(app_id, annotation_setting_id, args)
  62. return result, 200
  63. class AnnotationReplyActionStatusApi(Resource):
  64. @setup_required
  65. @login_required
  66. @account_initialization_required
  67. @cloud_edition_billing_resource_check('annotation')
  68. def get(self, app_id, job_id, action):
  69. # The role of the current user in the ta table must be admin or owner
  70. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  71. raise Forbidden()
  72. job_id = str(job_id)
  73. app_annotation_job_key = '{}_app_annotation_job_{}'.format(action, str(job_id))
  74. cache_result = redis_client.get(app_annotation_job_key)
  75. if cache_result is None:
  76. raise ValueError("The job is not exist.")
  77. job_status = cache_result.decode()
  78. error_msg = ''
  79. if job_status == 'error':
  80. app_annotation_error_key = '{}_app_annotation_error_{}'.format(action, str(job_id))
  81. error_msg = redis_client.get(app_annotation_error_key).decode()
  82. return {
  83. 'job_id': job_id,
  84. 'job_status': job_status,
  85. 'error_msg': error_msg
  86. }, 200
  87. class AnnotationListApi(Resource):
  88. @setup_required
  89. @login_required
  90. @account_initialization_required
  91. def get(self, app_id):
  92. # The role of the current user in the ta table must be admin or owner
  93. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  94. raise Forbidden()
  95. page = request.args.get('page', default=1, type=int)
  96. limit = request.args.get('limit', default=20, type=int)
  97. keyword = request.args.get('keyword', default=None, type=str)
  98. app_id = str(app_id)
  99. annotation_list, total = AppAnnotationService.get_annotation_list_by_app_id(app_id, page, limit, keyword)
  100. response = {
  101. 'data': marshal(annotation_list, annotation_fields),
  102. 'has_more': len(annotation_list) == limit,
  103. 'limit': limit,
  104. 'total': total,
  105. 'page': page
  106. }
  107. return response, 200
  108. class AnnotationExportApi(Resource):
  109. @setup_required
  110. @login_required
  111. @account_initialization_required
  112. def get(self, app_id):
  113. # The role of the current user in the ta table must be admin or owner
  114. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  115. raise Forbidden()
  116. app_id = str(app_id)
  117. annotation_list = AppAnnotationService.export_annotation_list_by_app_id(app_id)
  118. response = {
  119. 'data': marshal(annotation_list, annotation_fields)
  120. }
  121. return response, 200
  122. class AnnotationCreateApi(Resource):
  123. @setup_required
  124. @login_required
  125. @account_initialization_required
  126. @cloud_edition_billing_resource_check('annotation')
  127. @marshal_with(annotation_fields)
  128. def post(self, app_id):
  129. # The role of the current user in the ta table must be admin or owner
  130. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  131. raise Forbidden()
  132. app_id = str(app_id)
  133. parser = reqparse.RequestParser()
  134. parser.add_argument('question', required=True, type=str, location='json')
  135. parser.add_argument('answer', required=True, type=str, location='json')
  136. args = parser.parse_args()
  137. annotation = AppAnnotationService.insert_app_annotation_directly(args, app_id)
  138. return annotation
  139. class AnnotationUpdateDeleteApi(Resource):
  140. @setup_required
  141. @login_required
  142. @account_initialization_required
  143. @cloud_edition_billing_resource_check('annotation')
  144. @marshal_with(annotation_fields)
  145. def post(self, app_id, annotation_id):
  146. # The role of the current user in the ta table must be admin or owner
  147. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  148. raise Forbidden()
  149. app_id = str(app_id)
  150. annotation_id = str(annotation_id)
  151. parser = reqparse.RequestParser()
  152. parser.add_argument('question', required=True, type=str, location='json')
  153. parser.add_argument('answer', required=True, type=str, location='json')
  154. args = parser.parse_args()
  155. annotation = AppAnnotationService.update_app_annotation_directly(args, app_id, annotation_id)
  156. return annotation
  157. @setup_required
  158. @login_required
  159. @account_initialization_required
  160. def delete(self, app_id, annotation_id):
  161. # The role of the current user in the ta table must be admin or owner
  162. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  163. raise Forbidden()
  164. app_id = str(app_id)
  165. annotation_id = str(annotation_id)
  166. AppAnnotationService.delete_app_annotation(app_id, annotation_id)
  167. return {'result': 'success'}, 200
  168. class AnnotationBatchImportApi(Resource):
  169. @setup_required
  170. @login_required
  171. @account_initialization_required
  172. @cloud_edition_billing_resource_check('annotation')
  173. def post(self, app_id):
  174. # The role of the current user in the ta table must be admin or owner
  175. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  176. raise Forbidden()
  177. app_id = str(app_id)
  178. # get file from request
  179. file = request.files['file']
  180. # check file
  181. if 'file' not in request.files:
  182. raise NoFileUploadedError()
  183. if len(request.files) > 1:
  184. raise TooManyFilesError()
  185. # check file type
  186. if not file.filename.endswith('.csv'):
  187. raise ValueError("Invalid file type. Only CSV files are allowed")
  188. return AppAnnotationService.batch_import_app_annotations(app_id, file)
  189. class AnnotationBatchImportStatusApi(Resource):
  190. @setup_required
  191. @login_required
  192. @account_initialization_required
  193. @cloud_edition_billing_resource_check('annotation')
  194. def get(self, app_id, job_id):
  195. # The role of the current user in the ta table must be admin or owner
  196. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  197. raise Forbidden()
  198. job_id = str(job_id)
  199. indexing_cache_key = 'app_annotation_batch_import_{}'.format(str(job_id))
  200. cache_result = redis_client.get(indexing_cache_key)
  201. if cache_result is None:
  202. raise ValueError("The job is not exist.")
  203. job_status = cache_result.decode()
  204. error_msg = ''
  205. if job_status == 'error':
  206. indexing_error_msg_key = 'app_annotation_batch_import_error_msg_{}'.format(str(job_id))
  207. error_msg = redis_client.get(indexing_error_msg_key).decode()
  208. return {
  209. 'job_id': job_id,
  210. 'job_status': job_status,
  211. 'error_msg': error_msg
  212. }, 200
  213. class AnnotationHitHistoryListApi(Resource):
  214. @setup_required
  215. @login_required
  216. @account_initialization_required
  217. def get(self, app_id, annotation_id):
  218. # The role of the current user in the table must be admin or owner
  219. if current_user.current_tenant.current_role not in ['admin', 'owner']:
  220. raise Forbidden()
  221. page = request.args.get('page', default=1, type=int)
  222. limit = request.args.get('limit', default=20, type=int)
  223. app_id = str(app_id)
  224. annotation_id = str(annotation_id)
  225. annotation_hit_history_list, total = AppAnnotationService.get_annotation_hit_histories(app_id, annotation_id,
  226. page, limit)
  227. response = {
  228. 'data': marshal(annotation_hit_history_list, annotation_hit_history_fields),
  229. 'has_more': len(annotation_hit_history_list) == limit,
  230. 'limit': limit,
  231. 'total': total,
  232. 'page': page
  233. }
  234. return response
  235. api.add_resource(AnnotationReplyActionApi, '/apps/<uuid:app_id>/annotation-reply/<string:action>')
  236. api.add_resource(AnnotationReplyActionStatusApi,
  237. '/apps/<uuid:app_id>/annotation-reply/<string:action>/status/<uuid:job_id>')
  238. api.add_resource(AnnotationListApi, '/apps/<uuid:app_id>/annotations')
  239. api.add_resource(AnnotationExportApi, '/apps/<uuid:app_id>/annotations/export')
  240. api.add_resource(AnnotationUpdateDeleteApi, '/apps/<uuid:app_id>/annotations/<uuid:annotation_id>')
  241. api.add_resource(AnnotationBatchImportApi, '/apps/<uuid:app_id>/annotations/batch-import')
  242. api.add_resource(AnnotationBatchImportStatusApi, '/apps/<uuid:app_id>/annotations/batch-import-status/<uuid:job_id>')
  243. api.add_resource(AnnotationHitHistoryListApi, '/apps/<uuid:app_id>/annotations/<uuid:annotation_id>/hit-histories')
  244. api.add_resource(AppAnnotationSettingDetailApi, '/apps/<uuid:app_id>/annotation-setting')
  245. api.add_resource(AppAnnotationSettingUpdateApi, '/apps/<uuid:app_id>/annotation-settings/<uuid:annotation_setting_id>')