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.

annotation.py 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. from typing import Literal
  2. from flask import request
  3. from flask_restx import Api, Namespace, Resource, fields, reqparse
  4. from flask_restx.api import HTTPStatus
  5. from werkzeug.exceptions import Forbidden
  6. from controllers.service_api import service_api_ns
  7. from controllers.service_api.wraps import validate_app_token
  8. from extensions.ext_redis import redis_client
  9. from fields.annotation_fields import annotation_fields, build_annotation_model
  10. from libs.login import current_user
  11. from models.model import App
  12. from services.annotation_service import AppAnnotationService
  13. # Define parsers for annotation API
  14. annotation_create_parser = reqparse.RequestParser()
  15. annotation_create_parser.add_argument("question", required=True, type=str, location="json", help="Annotation question")
  16. annotation_create_parser.add_argument("answer", required=True, type=str, location="json", help="Annotation answer")
  17. annotation_reply_action_parser = reqparse.RequestParser()
  18. annotation_reply_action_parser.add_argument(
  19. "score_threshold", required=True, type=float, location="json", help="Score threshold for annotation matching"
  20. )
  21. annotation_reply_action_parser.add_argument(
  22. "embedding_provider_name", required=True, type=str, location="json", help="Embedding provider name"
  23. )
  24. annotation_reply_action_parser.add_argument(
  25. "embedding_model_name", required=True, type=str, location="json", help="Embedding model name"
  26. )
  27. @service_api_ns.route("/apps/annotation-reply/<string:action>")
  28. class AnnotationReplyActionApi(Resource):
  29. @service_api_ns.expect(annotation_reply_action_parser)
  30. @service_api_ns.doc("annotation_reply_action")
  31. @service_api_ns.doc(description="Enable or disable annotation reply feature")
  32. @service_api_ns.doc(params={"action": "Action to perform: 'enable' or 'disable'"})
  33. @service_api_ns.doc(
  34. responses={
  35. 200: "Action completed successfully",
  36. 401: "Unauthorized - invalid API token",
  37. }
  38. )
  39. @validate_app_token
  40. def post(self, app_model: App, action: Literal["enable", "disable"]):
  41. """Enable or disable annotation reply feature."""
  42. args = annotation_reply_action_parser.parse_args()
  43. if action == "enable":
  44. result = AppAnnotationService.enable_app_annotation(args, app_model.id)
  45. elif action == "disable":
  46. result = AppAnnotationService.disable_app_annotation(app_model.id)
  47. return result, 200
  48. @service_api_ns.route("/apps/annotation-reply/<string:action>/status/<uuid:job_id>")
  49. class AnnotationReplyActionStatusApi(Resource):
  50. @service_api_ns.doc("get_annotation_reply_action_status")
  51. @service_api_ns.doc(description="Get the status of an annotation reply action job")
  52. @service_api_ns.doc(params={"action": "Action type", "job_id": "Job ID"})
  53. @service_api_ns.doc(
  54. responses={
  55. 200: "Job status retrieved successfully",
  56. 401: "Unauthorized - invalid API token",
  57. 404: "Job not found",
  58. }
  59. )
  60. @validate_app_token
  61. def get(self, app_model: App, job_id, action):
  62. """Get the status of an annotation reply action job."""
  63. job_id = str(job_id)
  64. app_annotation_job_key = f"{action}_app_annotation_job_{str(job_id)}"
  65. cache_result = redis_client.get(app_annotation_job_key)
  66. if cache_result is None:
  67. raise ValueError("The job does not exist.")
  68. job_status = cache_result.decode()
  69. error_msg = ""
  70. if job_status == "error":
  71. app_annotation_error_key = f"{action}_app_annotation_error_{str(job_id)}"
  72. error_msg = redis_client.get(app_annotation_error_key).decode()
  73. return {"job_id": job_id, "job_status": job_status, "error_msg": error_msg}, 200
  74. # Define annotation list response model
  75. annotation_list_fields = {
  76. "data": fields.List(fields.Nested(annotation_fields)),
  77. "has_more": fields.Boolean,
  78. "limit": fields.Integer,
  79. "total": fields.Integer,
  80. "page": fields.Integer,
  81. }
  82. def build_annotation_list_model(api_or_ns: Api | Namespace):
  83. """Build the annotation list model for the API or Namespace."""
  84. copied_annotation_list_fields = annotation_list_fields.copy()
  85. copied_annotation_list_fields["data"] = fields.List(fields.Nested(build_annotation_model(api_or_ns)))
  86. return api_or_ns.model("AnnotationList", copied_annotation_list_fields)
  87. @service_api_ns.route("/apps/annotations")
  88. class AnnotationListApi(Resource):
  89. @service_api_ns.doc("list_annotations")
  90. @service_api_ns.doc(description="List annotations for the application")
  91. @service_api_ns.doc(
  92. responses={
  93. 200: "Annotations retrieved successfully",
  94. 401: "Unauthorized - invalid API token",
  95. }
  96. )
  97. @validate_app_token
  98. @service_api_ns.marshal_with(build_annotation_list_model(service_api_ns))
  99. def get(self, app_model: App):
  100. """List annotations for the application."""
  101. page = request.args.get("page", default=1, type=int)
  102. limit = request.args.get("limit", default=20, type=int)
  103. keyword = request.args.get("keyword", default="", type=str)
  104. annotation_list, total = AppAnnotationService.get_annotation_list_by_app_id(app_model.id, page, limit, keyword)
  105. return {
  106. "data": annotation_list,
  107. "has_more": len(annotation_list) == limit,
  108. "limit": limit,
  109. "total": total,
  110. "page": page,
  111. }
  112. @service_api_ns.expect(annotation_create_parser)
  113. @service_api_ns.doc("create_annotation")
  114. @service_api_ns.doc(description="Create a new annotation")
  115. @service_api_ns.doc(
  116. responses={
  117. 201: "Annotation created successfully",
  118. 401: "Unauthorized - invalid API token",
  119. }
  120. )
  121. @validate_app_token
  122. @service_api_ns.marshal_with(build_annotation_model(service_api_ns), code=HTTPStatus.CREATED)
  123. def post(self, app_model: App):
  124. """Create a new annotation."""
  125. args = annotation_create_parser.parse_args()
  126. annotation = AppAnnotationService.insert_app_annotation_directly(args, app_model.id)
  127. return annotation, 201
  128. @service_api_ns.route("/apps/annotations/<uuid:annotation_id>")
  129. class AnnotationUpdateDeleteApi(Resource):
  130. @service_api_ns.expect(annotation_create_parser)
  131. @service_api_ns.doc("update_annotation")
  132. @service_api_ns.doc(description="Update an existing annotation")
  133. @service_api_ns.doc(params={"annotation_id": "Annotation ID"})
  134. @service_api_ns.doc(
  135. responses={
  136. 200: "Annotation updated successfully",
  137. 401: "Unauthorized - invalid API token",
  138. 403: "Forbidden - insufficient permissions",
  139. 404: "Annotation not found",
  140. }
  141. )
  142. @validate_app_token
  143. @service_api_ns.marshal_with(build_annotation_model(service_api_ns))
  144. def put(self, app_model: App, annotation_id):
  145. """Update an existing annotation."""
  146. if not current_user.is_editor:
  147. raise Forbidden()
  148. annotation_id = str(annotation_id)
  149. args = annotation_create_parser.parse_args()
  150. annotation = AppAnnotationService.update_app_annotation_directly(args, app_model.id, annotation_id)
  151. return annotation
  152. @service_api_ns.doc("delete_annotation")
  153. @service_api_ns.doc(description="Delete an annotation")
  154. @service_api_ns.doc(params={"annotation_id": "Annotation ID"})
  155. @service_api_ns.doc(
  156. responses={
  157. 204: "Annotation deleted successfully",
  158. 401: "Unauthorized - invalid API token",
  159. 403: "Forbidden - insufficient permissions",
  160. 404: "Annotation not found",
  161. }
  162. )
  163. @validate_app_token
  164. def delete(self, app_model: App, annotation_id):
  165. """Delete an annotation."""
  166. if not current_user.is_editor:
  167. raise Forbidden()
  168. annotation_id = str(annotation_id)
  169. AppAnnotationService.delete_app_annotation(app_model.id, annotation_id)
  170. return {"result": "success"}, 204