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

10 месяцев назад
10 месяцев назад
10 месяцев назад
10 месяцев назад
10 месяцев назад
10 месяцев назад
10 месяцев назад
10 месяцев назад
10 месяцев назад
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. import json
  2. import logging
  3. from datetime import UTC, datetime
  4. from typing import Optional, cast
  5. from flask_login import current_user
  6. from flask_sqlalchemy.pagination import Pagination
  7. from configs import dify_config
  8. from constants.model_template import default_app_templates
  9. from core.agent.entities import AgentToolEntity
  10. from core.errors.error import LLMBadRequestError, ProviderTokenNotInitError
  11. from core.model_manager import ModelManager
  12. from core.model_runtime.entities.model_entities import ModelPropertyKey, ModelType
  13. from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
  14. from core.tools.tool_manager import ToolManager
  15. from core.tools.utils.configuration import ToolParameterConfigurationManager
  16. from events.app_event import app_was_created
  17. from extensions.ext_database import db
  18. from models.account import Account
  19. from models.model import App, AppMode, AppModelConfig
  20. from models.tools import ApiToolProvider
  21. from services.tag_service import TagService
  22. from tasks.remove_app_and_related_data_task import remove_app_and_related_data_task
  23. class AppService:
  24. def get_paginate_apps(self, user_id: str, tenant_id: str, args: dict) -> Pagination | None:
  25. """
  26. Get app list with pagination
  27. :param user_id: user id
  28. :param tenant_id: tenant id
  29. :param args: request args
  30. :return:
  31. """
  32. filters = [App.tenant_id == tenant_id, App.is_universal == False]
  33. if args["mode"] == "workflow":
  34. filters.append(App.mode == AppMode.WORKFLOW.value)
  35. elif args["mode"] == "completion":
  36. filters.append(App.mode == AppMode.COMPLETION.value)
  37. elif args["mode"] == "chat":
  38. filters.append(App.mode == AppMode.CHAT.value)
  39. elif args["mode"] == "advanced-chat":
  40. filters.append(App.mode == AppMode.ADVANCED_CHAT.value)
  41. elif args["mode"] == "agent-chat":
  42. filters.append(App.mode == AppMode.AGENT_CHAT.value)
  43. elif args["mode"] == "channel":
  44. filters.append(App.mode == AppMode.CHANNEL.value)
  45. if args.get("is_created_by_me", False):
  46. filters.append(App.created_by == user_id)
  47. if args.get("name"):
  48. name = args["name"][:30]
  49. filters.append(App.name.ilike(f"%{name}%"))
  50. if args.get("tag_ids"):
  51. target_ids = TagService.get_target_ids_by_tag_ids("app", tenant_id, args["tag_ids"])
  52. if target_ids:
  53. filters.append(App.id.in_(target_ids))
  54. else:
  55. return None
  56. app_models = db.paginate(
  57. db.select(App).where(*filters).order_by(App.created_at.desc()),
  58. page=args["page"],
  59. per_page=args["limit"],
  60. error_out=False,
  61. )
  62. return app_models
  63. def create_app(self, tenant_id: str, args: dict, account: Account) -> App:
  64. """
  65. Create app
  66. :param tenant_id: tenant id
  67. :param args: request args
  68. :param account: Account instance
  69. """
  70. app_mode = AppMode.value_of(args["mode"])
  71. app_template = default_app_templates[app_mode]
  72. # get model config
  73. default_model_config = app_template.get("model_config")
  74. default_model_config = default_model_config.copy() if default_model_config else None
  75. if default_model_config and "model" in default_model_config:
  76. # get model provider
  77. model_manager = ModelManager()
  78. # get default model instance
  79. try:
  80. model_instance = model_manager.get_default_model_instance(
  81. tenant_id=account.current_tenant_id or "", model_type=ModelType.LLM
  82. )
  83. except (ProviderTokenNotInitError, LLMBadRequestError):
  84. model_instance = None
  85. except Exception as e:
  86. logging.exception(f"Get default model instance failed, tenant_id: {tenant_id}")
  87. model_instance = None
  88. if model_instance:
  89. if (
  90. model_instance.model == default_model_config["model"]["name"]
  91. and model_instance.provider == default_model_config["model"]["provider"]
  92. ):
  93. default_model_dict = default_model_config["model"]
  94. else:
  95. llm_model = cast(LargeLanguageModel, model_instance.model_type_instance)
  96. model_schema = llm_model.get_model_schema(model_instance.model, model_instance.credentials)
  97. if model_schema is None:
  98. raise ValueError(f"model schema not found for model {model_instance.model}")
  99. default_model_dict = {
  100. "provider": model_instance.provider,
  101. "name": model_instance.model,
  102. "mode": model_schema.model_properties.get(ModelPropertyKey.MODE),
  103. "completion_params": {},
  104. }
  105. else:
  106. provider, model = model_manager.get_default_provider_model_name(
  107. tenant_id=account.current_tenant_id or "", model_type=ModelType.LLM
  108. )
  109. default_model_config["model"]["provider"] = provider
  110. default_model_config["model"]["name"] = model
  111. default_model_dict = default_model_config["model"]
  112. default_model_config["model"] = json.dumps(default_model_dict)
  113. app = App(**app_template["app"])
  114. app.name = args["name"]
  115. app.description = args.get("description", "")
  116. app.mode = args["mode"]
  117. app.icon_type = args.get("icon_type", "emoji")
  118. app.icon = args["icon"]
  119. app.icon_background = args["icon_background"]
  120. app.tenant_id = tenant_id
  121. app.api_rph = args.get("api_rph", 0)
  122. app.api_rpm = args.get("api_rpm", 0)
  123. app.created_by = account.id
  124. app.updated_by = account.id
  125. db.session.add(app)
  126. db.session.flush()
  127. if default_model_config:
  128. app_model_config = AppModelConfig(**default_model_config)
  129. app_model_config.app_id = app.id
  130. app_model_config.created_by = account.id
  131. app_model_config.updated_by = account.id
  132. db.session.add(app_model_config)
  133. db.session.flush()
  134. app.app_model_config_id = app_model_config.id
  135. db.session.commit()
  136. app_was_created.send(app, account=account)
  137. return app
  138. def get_app(self, app: App) -> App:
  139. """
  140. Get App
  141. """
  142. # get original app model config
  143. if app.mode == AppMode.AGENT_CHAT.value or app.is_agent:
  144. model_config = app.app_model_config
  145. agent_mode = model_config.agent_mode_dict
  146. # decrypt agent tool parameters if it's secret-input
  147. for tool in agent_mode.get("tools") or []:
  148. if not isinstance(tool, dict) or len(tool.keys()) <= 3:
  149. continue
  150. agent_tool_entity = AgentToolEntity(**tool)
  151. # get tool
  152. try:
  153. tool_runtime = ToolManager.get_agent_tool_runtime(
  154. tenant_id=current_user.current_tenant_id,
  155. app_id=app.id,
  156. agent_tool=agent_tool_entity,
  157. )
  158. manager = ToolParameterConfigurationManager(
  159. tenant_id=current_user.current_tenant_id,
  160. tool_runtime=tool_runtime,
  161. provider_name=agent_tool_entity.provider_id,
  162. provider_type=agent_tool_entity.provider_type,
  163. identity_id=f"AGENT.{app.id}",
  164. )
  165. # get decrypted parameters
  166. if agent_tool_entity.tool_parameters:
  167. parameters = manager.decrypt_tool_parameters(agent_tool_entity.tool_parameters or {})
  168. masked_parameter = manager.mask_tool_parameters(parameters or {})
  169. else:
  170. masked_parameter = {}
  171. # override tool parameters
  172. tool["tool_parameters"] = masked_parameter
  173. except Exception as e:
  174. pass
  175. # override agent mode
  176. model_config.agent_mode = json.dumps(agent_mode)
  177. class ModifiedApp(App):
  178. """
  179. Modified App class
  180. """
  181. def __init__(self, app):
  182. self.__dict__.update(app.__dict__)
  183. @property
  184. def app_model_config(self):
  185. return model_config
  186. app = ModifiedApp(app)
  187. return app
  188. def update_app(self, app: App, args: dict) -> App:
  189. """
  190. Update app
  191. :param app: App instance
  192. :param args: request args
  193. :return: App instance
  194. """
  195. app.name = args.get("name")
  196. app.description = args.get("description", "")
  197. app.icon_type = args.get("icon_type", "emoji")
  198. app.icon = args.get("icon")
  199. app.icon_background = args.get("icon_background")
  200. app.use_icon_as_answer_icon = args.get("use_icon_as_answer_icon", False)
  201. app.updated_by = current_user.id
  202. app.updated_at = datetime.now(UTC).replace(tzinfo=None)
  203. db.session.commit()
  204. return app
  205. def update_app_name(self, app: App, name: str) -> App:
  206. """
  207. Update app name
  208. :param app: App instance
  209. :param name: new name
  210. :return: App instance
  211. """
  212. app.name = name
  213. app.updated_by = current_user.id
  214. app.updated_at = datetime.now(UTC).replace(tzinfo=None)
  215. db.session.commit()
  216. return app
  217. def update_app_icon(self, app: App, icon: str, icon_background: str) -> App:
  218. """
  219. Update app icon
  220. :param app: App instance
  221. :param icon: new icon
  222. :param icon_background: new icon_background
  223. :return: App instance
  224. """
  225. app.icon = icon
  226. app.icon_background = icon_background
  227. app.updated_by = current_user.id
  228. app.updated_at = datetime.now(UTC).replace(tzinfo=None)
  229. db.session.commit()
  230. return app
  231. def update_app_site_status(self, app: App, enable_site: bool) -> App:
  232. """
  233. Update app site status
  234. :param app: App instance
  235. :param enable_site: enable site status
  236. :return: App instance
  237. """
  238. if enable_site == app.enable_site:
  239. return app
  240. app.enable_site = enable_site
  241. app.updated_by = current_user.id
  242. app.updated_at = datetime.now(UTC).replace(tzinfo=None)
  243. db.session.commit()
  244. return app
  245. def update_app_api_status(self, app: App, enable_api: bool) -> App:
  246. """
  247. Update app api status
  248. :param app: App instance
  249. :param enable_api: enable api status
  250. :return: App instance
  251. """
  252. if enable_api == app.enable_api:
  253. return app
  254. app.enable_api = enable_api
  255. app.updated_by = current_user.id
  256. app.updated_at = datetime.now(UTC).replace(tzinfo=None)
  257. db.session.commit()
  258. return app
  259. def delete_app(self, app: App) -> None:
  260. """
  261. Delete app
  262. :param app: App instance
  263. """
  264. db.session.delete(app)
  265. db.session.commit()
  266. # Trigger asynchronous deletion of app and related data
  267. remove_app_and_related_data_task.delay(tenant_id=app.tenant_id, app_id=app.id)
  268. def get_app_meta(self, app_model: App) -> dict:
  269. """
  270. Get app meta info
  271. :param app_model: app model
  272. :return:
  273. """
  274. app_mode = AppMode.value_of(app_model.mode)
  275. meta: dict = {"tool_icons": {}}
  276. if app_mode in {AppMode.ADVANCED_CHAT, AppMode.WORKFLOW}:
  277. workflow = app_model.workflow
  278. if workflow is None:
  279. return meta
  280. graph = workflow.graph_dict
  281. nodes = graph.get("nodes", [])
  282. tools = []
  283. for node in nodes:
  284. if node.get("data", {}).get("type") == "tool":
  285. node_data = node.get("data", {})
  286. tools.append(
  287. {
  288. "provider_type": node_data.get("provider_type"),
  289. "provider_id": node_data.get("provider_id"),
  290. "tool_name": node_data.get("tool_name"),
  291. "tool_parameters": {},
  292. }
  293. )
  294. else:
  295. app_model_config: Optional[AppModelConfig] = app_model.app_model_config
  296. if not app_model_config:
  297. return meta
  298. agent_config = app_model_config.agent_mode_dict
  299. # get all tools
  300. tools = agent_config.get("tools", [])
  301. url_prefix = dify_config.CONSOLE_API_URL + "/console/api/workspaces/current/tool-provider/builtin/"
  302. for tool in tools:
  303. keys = list(tool.keys())
  304. if len(keys) >= 4:
  305. # current tool standard
  306. provider_type = tool.get("provider_type", "")
  307. provider_id = tool.get("provider_id", "")
  308. tool_name = tool.get("tool_name", "")
  309. if provider_type == "builtin":
  310. meta["tool_icons"][tool_name] = url_prefix + provider_id + "/icon"
  311. elif provider_type == "api":
  312. try:
  313. provider: Optional[ApiToolProvider] = (
  314. db.session.query(ApiToolProvider).filter(ApiToolProvider.id == provider_id).first()
  315. )
  316. if provider is None:
  317. raise ValueError(f"provider not found for tool {tool_name}")
  318. meta["tool_icons"][tool_name] = json.loads(provider.icon)
  319. except:
  320. meta["tool_icons"][tool_name] = {"background": "#252525", "content": "\ud83d\ude01"}
  321. return meta