You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

completion.py 10KB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. import logging
  2. from flask import request
  3. from flask_restx import Resource, fields, reqparse
  4. from werkzeug.exceptions import Forbidden, InternalServerError, NotFound
  5. import services
  6. from controllers.console import api, console_ns
  7. from controllers.console.app.error import (
  8. AppUnavailableError,
  9. CompletionRequestError,
  10. ConversationCompletedError,
  11. ProviderModelCurrentlyNotSupportError,
  12. ProviderNotInitializeError,
  13. ProviderQuotaExceededError,
  14. )
  15. from controllers.console.app.wraps import get_app_model
  16. from controllers.console.wraps import account_initialization_required, setup_required
  17. from controllers.web.error import InvokeRateLimitError as InvokeRateLimitHttpError
  18. from core.app.apps.base_app_queue_manager import AppQueueManager
  19. from core.app.entities.app_invoke_entities import InvokeFrom
  20. from core.errors.error import (
  21. ModelCurrentlyNotSupportError,
  22. ProviderTokenNotInitError,
  23. QuotaExceededError,
  24. )
  25. from core.helper.trace_id_helper import get_external_trace_id
  26. from core.model_runtime.errors.invoke import InvokeError
  27. from libs import helper
  28. from libs.helper import uuid_value
  29. from libs.login import current_user, login_required
  30. from models import Account
  31. from models.model import AppMode
  32. from services.app_generate_service import AppGenerateService
  33. from services.errors.llm import InvokeRateLimitError
  34. logger = logging.getLogger(__name__)
  35. # define completion message api for user
  36. @console_ns.route("/apps/<uuid:app_id>/completion-messages")
  37. class CompletionMessageApi(Resource):
  38. @api.doc("create_completion_message")
  39. @api.doc(description="Generate completion message for debugging")
  40. @api.doc(params={"app_id": "Application ID"})
  41. @api.expect(
  42. api.model(
  43. "CompletionMessageRequest",
  44. {
  45. "inputs": fields.Raw(required=True, description="Input variables"),
  46. "query": fields.String(description="Query text", default=""),
  47. "files": fields.List(fields.Raw(), description="Uploaded files"),
  48. "model_config": fields.Raw(required=True, description="Model configuration"),
  49. "response_mode": fields.String(enum=["blocking", "streaming"], description="Response mode"),
  50. "retriever_from": fields.String(default="dev", description="Retriever source"),
  51. },
  52. )
  53. )
  54. @api.response(200, "Completion generated successfully")
  55. @api.response(400, "Invalid request parameters")
  56. @api.response(404, "App not found")
  57. @setup_required
  58. @login_required
  59. @account_initialization_required
  60. @get_app_model(mode=AppMode.COMPLETION)
  61. def post(self, app_model):
  62. parser = reqparse.RequestParser()
  63. parser.add_argument("inputs", type=dict, required=True, location="json")
  64. parser.add_argument("query", type=str, location="json", default="")
  65. parser.add_argument("files", type=list, required=False, location="json")
  66. parser.add_argument("model_config", type=dict, required=True, location="json")
  67. parser.add_argument("response_mode", type=str, choices=["blocking", "streaming"], location="json")
  68. parser.add_argument("retriever_from", type=str, required=False, default="dev", location="json")
  69. args = parser.parse_args()
  70. streaming = args["response_mode"] != "blocking"
  71. args["auto_generate_name"] = False
  72. try:
  73. if not isinstance(current_user, Account):
  74. raise ValueError("current_user must be an Account or EndUser instance")
  75. response = AppGenerateService.generate(
  76. app_model=app_model, user=current_user, args=args, invoke_from=InvokeFrom.DEBUGGER, streaming=streaming
  77. )
  78. return helper.compact_generate_response(response)
  79. except services.errors.conversation.ConversationNotExistsError:
  80. raise NotFound("Conversation Not Exists.")
  81. except services.errors.conversation.ConversationCompletedError:
  82. raise ConversationCompletedError()
  83. except services.errors.app_model_config.AppModelConfigBrokenError:
  84. logger.exception("App model config broken.")
  85. raise AppUnavailableError()
  86. except ProviderTokenNotInitError as ex:
  87. raise ProviderNotInitializeError(ex.description)
  88. except QuotaExceededError:
  89. raise ProviderQuotaExceededError()
  90. except ModelCurrentlyNotSupportError:
  91. raise ProviderModelCurrentlyNotSupportError()
  92. except InvokeError as e:
  93. raise CompletionRequestError(e.description)
  94. except ValueError as e:
  95. raise e
  96. except Exception as e:
  97. logger.exception("internal server error.")
  98. raise InternalServerError()
  99. @console_ns.route("/apps/<uuid:app_id>/completion-messages/<string:task_id>/stop")
  100. class CompletionMessageStopApi(Resource):
  101. @api.doc("stop_completion_message")
  102. @api.doc(description="Stop a running completion message generation")
  103. @api.doc(params={"app_id": "Application ID", "task_id": "Task ID to stop"})
  104. @api.response(200, "Task stopped successfully")
  105. @setup_required
  106. @login_required
  107. @account_initialization_required
  108. @get_app_model(mode=AppMode.COMPLETION)
  109. def post(self, app_model, task_id):
  110. if not isinstance(current_user, Account):
  111. raise ValueError("current_user must be an Account instance")
  112. AppQueueManager.set_stop_flag(task_id, InvokeFrom.DEBUGGER, current_user.id)
  113. return {"result": "success"}, 200
  114. @console_ns.route("/apps/<uuid:app_id>/chat-messages")
  115. class ChatMessageApi(Resource):
  116. @api.doc("create_chat_message")
  117. @api.doc(description="Generate chat message for debugging")
  118. @api.doc(params={"app_id": "Application ID"})
  119. @api.expect(
  120. api.model(
  121. "ChatMessageRequest",
  122. {
  123. "inputs": fields.Raw(required=True, description="Input variables"),
  124. "query": fields.String(required=True, description="User query"),
  125. "files": fields.List(fields.Raw(), description="Uploaded files"),
  126. "model_config": fields.Raw(required=True, description="Model configuration"),
  127. "conversation_id": fields.String(description="Conversation ID"),
  128. "parent_message_id": fields.String(description="Parent message ID"),
  129. "response_mode": fields.String(enum=["blocking", "streaming"], description="Response mode"),
  130. "retriever_from": fields.String(default="dev", description="Retriever source"),
  131. },
  132. )
  133. )
  134. @api.response(200, "Chat message generated successfully")
  135. @api.response(400, "Invalid request parameters")
  136. @api.response(404, "App or conversation not found")
  137. @setup_required
  138. @login_required
  139. @account_initialization_required
  140. @get_app_model(mode=[AppMode.CHAT, AppMode.AGENT_CHAT])
  141. def post(self, app_model):
  142. if not isinstance(current_user, Account):
  143. raise Forbidden()
  144. if not current_user.has_edit_permission:
  145. raise Forbidden()
  146. parser = reqparse.RequestParser()
  147. parser.add_argument("inputs", type=dict, required=True, location="json")
  148. parser.add_argument("query", type=str, required=True, location="json")
  149. parser.add_argument("files", type=list, required=False, location="json")
  150. parser.add_argument("model_config", type=dict, required=True, location="json")
  151. parser.add_argument("conversation_id", type=uuid_value, location="json")
  152. parser.add_argument("parent_message_id", type=uuid_value, required=False, location="json")
  153. parser.add_argument("response_mode", type=str, choices=["blocking", "streaming"], location="json")
  154. parser.add_argument("retriever_from", type=str, required=False, default="dev", location="json")
  155. args = parser.parse_args()
  156. streaming = args["response_mode"] != "blocking"
  157. args["auto_generate_name"] = False
  158. external_trace_id = get_external_trace_id(request)
  159. if external_trace_id:
  160. args["external_trace_id"] = external_trace_id
  161. try:
  162. if not isinstance(current_user, Account):
  163. raise ValueError("current_user must be an Account or EndUser instance")
  164. response = AppGenerateService.generate(
  165. app_model=app_model, user=current_user, args=args, invoke_from=InvokeFrom.DEBUGGER, streaming=streaming
  166. )
  167. return helper.compact_generate_response(response)
  168. except services.errors.conversation.ConversationNotExistsError:
  169. raise NotFound("Conversation Not Exists.")
  170. except services.errors.conversation.ConversationCompletedError:
  171. raise ConversationCompletedError()
  172. except services.errors.app_model_config.AppModelConfigBrokenError:
  173. logger.exception("App model config broken.")
  174. raise AppUnavailableError()
  175. except ProviderTokenNotInitError as ex:
  176. raise ProviderNotInitializeError(ex.description)
  177. except QuotaExceededError:
  178. raise ProviderQuotaExceededError()
  179. except ModelCurrentlyNotSupportError:
  180. raise ProviderModelCurrentlyNotSupportError()
  181. except InvokeRateLimitError as ex:
  182. raise InvokeRateLimitHttpError(ex.description)
  183. except InvokeError as e:
  184. raise CompletionRequestError(e.description)
  185. except ValueError as e:
  186. raise e
  187. except Exception as e:
  188. logger.exception("internal server error.")
  189. raise InternalServerError()
  190. @console_ns.route("/apps/<uuid:app_id>/chat-messages/<string:task_id>/stop")
  191. class ChatMessageStopApi(Resource):
  192. @api.doc("stop_chat_message")
  193. @api.doc(description="Stop a running chat message generation")
  194. @api.doc(params={"app_id": "Application ID", "task_id": "Task ID to stop"})
  195. @api.response(200, "Task stopped successfully")
  196. @setup_required
  197. @login_required
  198. @account_initialization_required
  199. @get_app_model(mode=[AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT])
  200. def post(self, app_model, task_id):
  201. if not isinstance(current_user, Account):
  202. raise ValueError("current_user must be an Account instance")
  203. AppQueueManager.set_stop_flag(task_id, InvokeFrom.DEBUGGER, current_user.id)
  204. return {"result": "success"}, 200