app_runner.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import logging
  2. from typing import cast
  3. from sqlalchemy import select
  4. from core.app.apps.base_app_queue_manager import AppQueueManager, PublishFrom
  5. from core.app.apps.base_app_runner import AppRunner
  6. from core.app.apps.chat.app_config_manager import ChatAppConfig
  7. from core.app.entities.app_invoke_entities import (
  8. ChatAppGenerateEntity,
  9. )
  10. from core.app.entities.queue_entities import QueueAnnotationReplyEvent
  11. from core.callback_handler.index_tool_callback_handler import DatasetIndexToolCallbackHandler
  12. from core.memory.token_buffer_memory import TokenBufferMemory
  13. from core.model_manager import ModelInstance
  14. from core.model_runtime.entities.message_entities import ImagePromptMessageContent
  15. from core.moderation.base import ModerationError
  16. from core.rag.retrieval.dataset_retrieval import DatasetRetrieval
  17. from extensions.ext_database import db
  18. from models.model import App, Conversation, Message
  19. logger = logging.getLogger(__name__)
  20. class ChatAppRunner(AppRunner):
  21. """
  22. Chat Application Runner
  23. """
  24. def run(
  25. self,
  26. application_generate_entity: ChatAppGenerateEntity,
  27. queue_manager: AppQueueManager,
  28. conversation: Conversation,
  29. message: Message,
  30. ) -> None:
  31. """
  32. Run application
  33. :param application_generate_entity: application generate entity
  34. :param queue_manager: application queue manager
  35. :param conversation: conversation
  36. :param message: message
  37. :return:
  38. """
  39. app_config = application_generate_entity.app_config
  40. app_config = cast(ChatAppConfig, app_config)
  41. stmt = select(App).where(App.id == app_config.app_id)
  42. app_record = db.session.scalar(stmt)
  43. if not app_record:
  44. raise ValueError("App not found")
  45. inputs = application_generate_entity.inputs
  46. query = application_generate_entity.query
  47. files = application_generate_entity.files
  48. image_detail_config = (
  49. application_generate_entity.file_upload_config.image_config.detail
  50. if (
  51. application_generate_entity.file_upload_config
  52. and application_generate_entity.file_upload_config.image_config
  53. )
  54. else None
  55. )
  56. image_detail_config = image_detail_config or ImagePromptMessageContent.DETAIL.LOW
  57. memory = None
  58. if application_generate_entity.conversation_id:
  59. # get memory of conversation (read-only)
  60. model_instance = ModelInstance(
  61. provider_model_bundle=application_generate_entity.model_conf.provider_model_bundle,
  62. model=application_generate_entity.model_conf.model,
  63. )
  64. memory = TokenBufferMemory(conversation=conversation, model_instance=model_instance)
  65. # organize all inputs and template to prompt messages
  66. # Include: prompt template, inputs, query(optional), files(optional)
  67. # memory(optional)
  68. prompt_messages, stop = self.organize_prompt_messages(
  69. app_record=app_record,
  70. model_config=application_generate_entity.model_conf,
  71. prompt_template_entity=app_config.prompt_template,
  72. inputs=inputs,
  73. files=files,
  74. query=query,
  75. memory=memory,
  76. image_detail_config=image_detail_config,
  77. )
  78. # moderation
  79. try:
  80. # process sensitive_word_avoidance
  81. _, inputs, query = self.moderation_for_inputs(
  82. app_id=app_record.id,
  83. tenant_id=app_config.tenant_id,
  84. app_generate_entity=application_generate_entity,
  85. inputs=inputs,
  86. query=query,
  87. message_id=message.id,
  88. )
  89. except ModerationError as e:
  90. self.direct_output(
  91. queue_manager=queue_manager,
  92. app_generate_entity=application_generate_entity,
  93. prompt_messages=prompt_messages,
  94. text=str(e),
  95. stream=application_generate_entity.stream,
  96. )
  97. return
  98. if query:
  99. # annotation reply
  100. annotation_reply = self.query_app_annotations_to_reply(
  101. app_record=app_record,
  102. message=message,
  103. query=query,
  104. user_id=application_generate_entity.user_id,
  105. invoke_from=application_generate_entity.invoke_from,
  106. )
  107. if annotation_reply:
  108. queue_manager.publish(
  109. QueueAnnotationReplyEvent(message_annotation_id=annotation_reply.id),
  110. PublishFrom.APPLICATION_MANAGER,
  111. )
  112. self.direct_output(
  113. queue_manager=queue_manager,
  114. app_generate_entity=application_generate_entity,
  115. prompt_messages=prompt_messages,
  116. text=annotation_reply.content,
  117. stream=application_generate_entity.stream,
  118. )
  119. return
  120. # fill in variable inputs from external data tools if exists
  121. external_data_tools = app_config.external_data_variables
  122. if external_data_tools:
  123. inputs = self.fill_in_inputs_from_external_data_tools(
  124. tenant_id=app_record.tenant_id,
  125. app_id=app_record.id,
  126. external_data_tools=external_data_tools,
  127. inputs=inputs,
  128. query=query,
  129. )
  130. # get context from datasets
  131. context = None
  132. if app_config.dataset and app_config.dataset.dataset_ids:
  133. hit_callback = DatasetIndexToolCallbackHandler(
  134. queue_manager,
  135. app_record.id,
  136. message.id,
  137. application_generate_entity.user_id,
  138. application_generate_entity.invoke_from,
  139. )
  140. dataset_retrieval = DatasetRetrieval(application_generate_entity)
  141. context = dataset_retrieval.retrieve(
  142. app_id=app_record.id,
  143. user_id=application_generate_entity.user_id,
  144. tenant_id=app_record.tenant_id,
  145. model_config=application_generate_entity.model_conf,
  146. config=app_config.dataset,
  147. query=query,
  148. invoke_from=application_generate_entity.invoke_from,
  149. show_retrieve_source=app_config.additional_features.show_retrieve_source,
  150. hit_callback=hit_callback,
  151. memory=memory,
  152. message_id=message.id,
  153. inputs=inputs,
  154. )
  155. # reorganize all inputs and template to prompt messages
  156. # Include: prompt template, inputs, query(optional), files(optional)
  157. # memory(optional), external data, dataset context(optional)
  158. prompt_messages, stop = self.organize_prompt_messages(
  159. app_record=app_record,
  160. model_config=application_generate_entity.model_conf,
  161. prompt_template_entity=app_config.prompt_template,
  162. inputs=inputs,
  163. files=files,
  164. query=query,
  165. context=context,
  166. memory=memory,
  167. image_detail_config=image_detail_config,
  168. )
  169. # check hosting moderation
  170. hosting_moderation_result = self.check_hosting_moderation(
  171. application_generate_entity=application_generate_entity,
  172. queue_manager=queue_manager,
  173. prompt_messages=prompt_messages,
  174. )
  175. if hosting_moderation_result:
  176. return
  177. # Re-calculate the max tokens if sum(prompt_token + max_tokens) over model token limit
  178. self.recalc_llm_max_tokens(model_config=application_generate_entity.model_conf, prompt_messages=prompt_messages)
  179. # Invoke model
  180. model_instance = ModelInstance(
  181. provider_model_bundle=application_generate_entity.model_conf.provider_model_bundle,
  182. model=application_generate_entity.model_conf.model,
  183. )
  184. db.session.close()
  185. invoke_result = model_instance.invoke_llm(
  186. prompt_messages=prompt_messages,
  187. model_parameters=application_generate_entity.model_conf.parameters,
  188. stop=stop,
  189. stream=application_generate_entity.stream,
  190. user=application_generate_entity.user_id,
  191. )
  192. # handle invoke result
  193. self._handle_invoke_result(
  194. invoke_result=invoke_result, queue_manager=queue_manager, stream=application_generate_entity.stream
  195. )