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.

app.py 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. from collections.abc import Generator, Mapping
  2. from typing import Optional, Union
  3. from sqlalchemy import select
  4. from sqlalchemy.orm import Session
  5. from controllers.service_api.wraps import create_or_update_end_user_for_user_id
  6. from core.app.app_config.common.parameters_mapping import get_parameters_from_feature_dict
  7. from core.app.apps.advanced_chat.app_generator import AdvancedChatAppGenerator
  8. from core.app.apps.agent_chat.app_generator import AgentChatAppGenerator
  9. from core.app.apps.chat.app_generator import ChatAppGenerator
  10. from core.app.apps.completion.app_generator import CompletionAppGenerator
  11. from core.app.apps.workflow.app_generator import WorkflowAppGenerator
  12. from core.app.entities.app_invoke_entities import InvokeFrom
  13. from core.plugin.backwards_invocation.base import BaseBackwardsInvocation
  14. from extensions.ext_database import db
  15. from models.account import Account
  16. from models.model import App, AppMode, EndUser
  17. class PluginAppBackwardsInvocation(BaseBackwardsInvocation):
  18. @classmethod
  19. def fetch_app_info(cls, app_id: str, tenant_id: str) -> Mapping:
  20. """
  21. Fetch app info
  22. """
  23. app = cls._get_app(app_id, tenant_id)
  24. """Retrieve app parameters."""
  25. if app.mode in {AppMode.ADVANCED_CHAT.value, AppMode.WORKFLOW.value}:
  26. workflow = app.workflow
  27. if workflow is None:
  28. raise ValueError("unexpected app type")
  29. features_dict = workflow.features_dict
  30. user_input_form = workflow.user_input_form(to_old_structure=True)
  31. else:
  32. app_model_config = app.app_model_config
  33. if app_model_config is None:
  34. raise ValueError("unexpected app type")
  35. features_dict = app_model_config.to_dict()
  36. user_input_form = features_dict.get("user_input_form", [])
  37. return {
  38. "data": get_parameters_from_feature_dict(features_dict=features_dict, user_input_form=user_input_form),
  39. }
  40. @classmethod
  41. def invoke_app(
  42. cls,
  43. app_id: str,
  44. user_id: str,
  45. tenant_id: str,
  46. conversation_id: Optional[str],
  47. query: Optional[str],
  48. stream: bool,
  49. inputs: Mapping,
  50. files: list[dict],
  51. ) -> Generator[Mapping | str, None, None] | Mapping:
  52. """
  53. invoke app
  54. """
  55. app = cls._get_app(app_id, tenant_id)
  56. if not user_id:
  57. user = create_or_update_end_user_for_user_id(app)
  58. else:
  59. user = cls._get_user(user_id)
  60. conversation_id = conversation_id or ""
  61. if app.mode in {AppMode.ADVANCED_CHAT.value, AppMode.AGENT_CHAT.value, AppMode.CHAT.value}:
  62. if not query:
  63. raise ValueError("missing query")
  64. return cls.invoke_chat_app(app, user, conversation_id, query, stream, inputs, files)
  65. elif app.mode == AppMode.WORKFLOW:
  66. return cls.invoke_workflow_app(app, user, stream, inputs, files)
  67. elif app.mode == AppMode.COMPLETION:
  68. return cls.invoke_completion_app(app, user, stream, inputs, files)
  69. raise ValueError("unexpected app type")
  70. @classmethod
  71. def invoke_chat_app(
  72. cls,
  73. app: App,
  74. user: Account | EndUser,
  75. conversation_id: str,
  76. query: str,
  77. stream: bool,
  78. inputs: Mapping,
  79. files: list[dict],
  80. ) -> Generator[Mapping | str, None, None] | Mapping:
  81. """
  82. invoke chat app
  83. """
  84. if app.mode == AppMode.ADVANCED_CHAT.value:
  85. workflow = app.workflow
  86. if not workflow:
  87. raise ValueError("unexpected app type")
  88. return AdvancedChatAppGenerator().generate(
  89. app_model=app,
  90. workflow=workflow,
  91. user=user,
  92. args={
  93. "inputs": inputs,
  94. "query": query,
  95. "files": files,
  96. "conversation_id": conversation_id,
  97. },
  98. invoke_from=InvokeFrom.SERVICE_API,
  99. streaming=stream,
  100. )
  101. elif app.mode == AppMode.AGENT_CHAT.value:
  102. return AgentChatAppGenerator().generate(
  103. app_model=app,
  104. user=user,
  105. args={
  106. "inputs": inputs,
  107. "query": query,
  108. "files": files,
  109. "conversation_id": conversation_id,
  110. },
  111. invoke_from=InvokeFrom.SERVICE_API,
  112. streaming=stream,
  113. )
  114. elif app.mode == AppMode.CHAT.value:
  115. return ChatAppGenerator().generate(
  116. app_model=app,
  117. user=user,
  118. args={
  119. "inputs": inputs,
  120. "query": query,
  121. "files": files,
  122. "conversation_id": conversation_id,
  123. },
  124. invoke_from=InvokeFrom.SERVICE_API,
  125. streaming=stream,
  126. )
  127. else:
  128. raise ValueError("unexpected app type")
  129. @classmethod
  130. def invoke_workflow_app(
  131. cls,
  132. app: App,
  133. user: EndUser | Account,
  134. stream: bool,
  135. inputs: Mapping,
  136. files: list[dict],
  137. ) -> Generator[Mapping | str, None, None] | Mapping:
  138. """
  139. invoke workflow app
  140. """
  141. workflow = app.workflow
  142. if not workflow:
  143. raise ValueError("unexpected app type")
  144. return WorkflowAppGenerator().generate(
  145. app_model=app,
  146. workflow=workflow,
  147. user=user,
  148. args={"inputs": inputs, "files": files},
  149. invoke_from=InvokeFrom.SERVICE_API,
  150. streaming=stream,
  151. call_depth=1,
  152. workflow_thread_pool_id=None,
  153. )
  154. @classmethod
  155. def invoke_completion_app(
  156. cls,
  157. app: App,
  158. user: EndUser | Account,
  159. stream: bool,
  160. inputs: Mapping,
  161. files: list[dict],
  162. ) -> Generator[Mapping | str, None, None] | Mapping:
  163. """
  164. invoke completion app
  165. """
  166. return CompletionAppGenerator().generate(
  167. app_model=app,
  168. user=user,
  169. args={"inputs": inputs, "files": files},
  170. invoke_from=InvokeFrom.SERVICE_API,
  171. streaming=stream,
  172. )
  173. @classmethod
  174. def _get_user(cls, user_id: str) -> Union[EndUser, Account]:
  175. """
  176. get the user by user id
  177. """
  178. with Session(db.engine, expire_on_commit=False) as session:
  179. stmt = select(EndUser).where(EndUser.id == user_id)
  180. user = session.scalar(stmt)
  181. if not user:
  182. stmt = select(Account).where(Account.id == user_id)
  183. user = session.scalar(stmt)
  184. if not user:
  185. raise ValueError("user not found")
  186. return user
  187. @classmethod
  188. def _get_app(cls, app_id: str, tenant_id: str) -> App:
  189. """
  190. get app
  191. """
  192. try:
  193. app = db.session.query(App).where(App.id == app_id).where(App.tenant_id == tenant_id).first()
  194. except Exception:
  195. raise ValueError("app not found")
  196. if not app:
  197. raise ValueError("app not found")
  198. return app