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

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