Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

completion.py 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import logging
  2. from flask_restx import reqparse
  3. from werkzeug.exceptions import InternalServerError, NotFound
  4. import services
  5. from controllers.web import web_ns
  6. from controllers.web.error import (
  7. AppUnavailableError,
  8. CompletionRequestError,
  9. ConversationCompletedError,
  10. NotChatAppError,
  11. NotCompletionAppError,
  12. ProviderModelCurrentlyNotSupportError,
  13. ProviderNotInitializeError,
  14. ProviderQuotaExceededError,
  15. )
  16. from controllers.web.error import InvokeRateLimitError as InvokeRateLimitHttpError
  17. from controllers.web.wraps import WebApiResource
  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.model_runtime.errors.invoke import InvokeError
  26. from libs import helper
  27. from libs.helper import uuid_value
  28. from models.model import AppMode
  29. from services.app_generate_service import AppGenerateService
  30. from services.errors.llm import InvokeRateLimitError
  31. logger = logging.getLogger(__name__)
  32. # define completion api for user
  33. @web_ns.route("/completion-messages")
  34. class CompletionApi(WebApiResource):
  35. @web_ns.doc("Create Completion Message")
  36. @web_ns.doc(description="Create a completion message for text generation applications.")
  37. @web_ns.doc(
  38. params={
  39. "inputs": {"description": "Input variables for the completion", "type": "object", "required": True},
  40. "query": {"description": "Query text for completion", "type": "string", "required": False},
  41. "files": {"description": "Files to be processed", "type": "array", "required": False},
  42. "response_mode": {
  43. "description": "Response mode: blocking or streaming",
  44. "type": "string",
  45. "enum": ["blocking", "streaming"],
  46. "required": False,
  47. },
  48. "retriever_from": {"description": "Source of retriever", "type": "string", "required": False},
  49. }
  50. )
  51. @web_ns.doc(
  52. responses={
  53. 200: "Success",
  54. 400: "Bad Request",
  55. 401: "Unauthorized",
  56. 403: "Forbidden",
  57. 404: "App Not Found",
  58. 500: "Internal Server Error",
  59. }
  60. )
  61. def post(self, app_model, end_user):
  62. if app_model.mode != "completion":
  63. raise NotCompletionAppError()
  64. parser = reqparse.RequestParser()
  65. parser.add_argument("inputs", type=dict, required=True, location="json")
  66. parser.add_argument("query", type=str, location="json", default="")
  67. parser.add_argument("files", type=list, required=False, location="json")
  68. parser.add_argument("response_mode", type=str, choices=["blocking", "streaming"], location="json")
  69. parser.add_argument("retriever_from", type=str, required=False, default="web_app", location="json")
  70. args = parser.parse_args()
  71. streaming = args["response_mode"] == "streaming"
  72. args["auto_generate_name"] = False
  73. try:
  74. response = AppGenerateService.generate(
  75. app_model=app_model, user=end_user, args=args, invoke_from=InvokeFrom.WEB_APP, streaming=streaming
  76. )
  77. return helper.compact_generate_response(response)
  78. except services.errors.conversation.ConversationNotExistsError:
  79. raise NotFound("Conversation Not Exists.")
  80. except services.errors.conversation.ConversationCompletedError:
  81. raise ConversationCompletedError()
  82. except services.errors.app_model_config.AppModelConfigBrokenError:
  83. logger.exception("App model config broken.")
  84. raise AppUnavailableError()
  85. except ProviderTokenNotInitError as ex:
  86. raise ProviderNotInitializeError(ex.description)
  87. except QuotaExceededError:
  88. raise ProviderQuotaExceededError()
  89. except ModelCurrentlyNotSupportError:
  90. raise ProviderModelCurrentlyNotSupportError()
  91. except InvokeError as e:
  92. raise CompletionRequestError(e.description)
  93. except ValueError as e:
  94. raise e
  95. except Exception as e:
  96. logger.exception("internal server error.")
  97. raise InternalServerError()
  98. @web_ns.route("/completion-messages/<string:task_id>/stop")
  99. class CompletionStopApi(WebApiResource):
  100. @web_ns.doc("Stop Completion Message")
  101. @web_ns.doc(description="Stop a running completion message task.")
  102. @web_ns.doc(params={"task_id": {"description": "Task ID to stop", "type": "string", "required": True}})
  103. @web_ns.doc(
  104. responses={
  105. 200: "Success",
  106. 400: "Bad Request",
  107. 401: "Unauthorized",
  108. 403: "Forbidden",
  109. 404: "Task Not Found",
  110. 500: "Internal Server Error",
  111. }
  112. )
  113. def post(self, app_model, end_user, task_id):
  114. if app_model.mode != "completion":
  115. raise NotCompletionAppError()
  116. AppQueueManager.set_stop_flag(task_id, InvokeFrom.WEB_APP, end_user.id)
  117. return {"result": "success"}, 200
  118. @web_ns.route("/chat-messages")
  119. class ChatApi(WebApiResource):
  120. @web_ns.doc("Create Chat Message")
  121. @web_ns.doc(description="Create a chat message for conversational applications.")
  122. @web_ns.doc(
  123. params={
  124. "inputs": {"description": "Input variables for the chat", "type": "object", "required": True},
  125. "query": {"description": "User query/message", "type": "string", "required": True},
  126. "files": {"description": "Files to be processed", "type": "array", "required": False},
  127. "response_mode": {
  128. "description": "Response mode: blocking or streaming",
  129. "type": "string",
  130. "enum": ["blocking", "streaming"],
  131. "required": False,
  132. },
  133. "conversation_id": {"description": "Conversation UUID", "type": "string", "required": False},
  134. "parent_message_id": {"description": "Parent message UUID", "type": "string", "required": False},
  135. "retriever_from": {"description": "Source of retriever", "type": "string", "required": False},
  136. }
  137. )
  138. @web_ns.doc(
  139. responses={
  140. 200: "Success",
  141. 400: "Bad Request",
  142. 401: "Unauthorized",
  143. 403: "Forbidden",
  144. 404: "App Not Found",
  145. 500: "Internal Server Error",
  146. }
  147. )
  148. def post(self, app_model, end_user):
  149. app_mode = AppMode.value_of(app_model.mode)
  150. if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
  151. raise NotChatAppError()
  152. parser = reqparse.RequestParser()
  153. parser.add_argument("inputs", type=dict, required=True, location="json")
  154. parser.add_argument("query", type=str, required=True, location="json")
  155. parser.add_argument("files", type=list, required=False, location="json")
  156. parser.add_argument("response_mode", type=str, choices=["blocking", "streaming"], location="json")
  157. parser.add_argument("conversation_id", type=uuid_value, location="json")
  158. parser.add_argument("parent_message_id", type=uuid_value, required=False, location="json")
  159. parser.add_argument("retriever_from", type=str, required=False, default="web_app", location="json")
  160. args = parser.parse_args()
  161. streaming = args["response_mode"] == "streaming"
  162. args["auto_generate_name"] = False
  163. try:
  164. response = AppGenerateService.generate(
  165. app_model=app_model, user=end_user, args=args, invoke_from=InvokeFrom.WEB_APP, 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. @web_ns.route("/chat-messages/<string:task_id>/stop")
  191. class ChatStopApi(WebApiResource):
  192. @web_ns.doc("Stop Chat Message")
  193. @web_ns.doc(description="Stop a running chat message task.")
  194. @web_ns.doc(params={"task_id": {"description": "Task ID to stop", "type": "string", "required": True}})
  195. @web_ns.doc(
  196. responses={
  197. 200: "Success",
  198. 400: "Bad Request",
  199. 401: "Unauthorized",
  200. 403: "Forbidden",
  201. 404: "Task Not Found",
  202. 500: "Internal Server Error",
  203. }
  204. )
  205. def post(self, app_model, end_user, task_id):
  206. app_mode = AppMode.value_of(app_model.mode)
  207. if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
  208. raise NotChatAppError()
  209. AppQueueManager.set_stop_flag(task_id, InvokeFrom.WEB_APP, end_user.id)
  210. return {"result": "success"}, 200