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.

tool_manager.py 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. import json
  2. import logging
  3. import mimetypes
  4. from collections.abc import Generator
  5. from os import listdir, path
  6. from threading import Lock
  7. from typing import TYPE_CHECKING, Any, Union, cast
  8. from yarl import URL
  9. import contexts
  10. from core.plugin.entities.plugin import ToolProviderID
  11. from core.plugin.impl.tool import PluginToolManager
  12. from core.tools.__base.tool_provider import ToolProviderController
  13. from core.tools.__base.tool_runtime import ToolRuntime
  14. from core.tools.plugin_tool.provider import PluginToolProviderController
  15. from core.tools.plugin_tool.tool import PluginTool
  16. from core.tools.workflow_as_tool.provider import WorkflowToolProviderController
  17. if TYPE_CHECKING:
  18. from core.workflow.nodes.tool.entities import ToolEntity
  19. from configs import dify_config
  20. from core.agent.entities import AgentToolEntity
  21. from core.app.entities.app_invoke_entities import InvokeFrom
  22. from core.helper.module_import_helper import load_single_subclass_from_source
  23. from core.helper.position_helper import is_filtered
  24. from core.model_runtime.utils.encoders import jsonable_encoder
  25. from core.tools.__base.tool import Tool
  26. from core.tools.builtin_tool.provider import BuiltinToolProviderController
  27. from core.tools.builtin_tool.providers._positions import BuiltinToolProviderSort
  28. from core.tools.builtin_tool.tool import BuiltinTool
  29. from core.tools.custom_tool.provider import ApiToolProviderController
  30. from core.tools.custom_tool.tool import ApiTool
  31. from core.tools.entities.api_entities import ToolProviderApiEntity, ToolProviderTypeApiLiteral
  32. from core.tools.entities.common_entities import I18nObject
  33. from core.tools.entities.tool_entities import (
  34. ApiProviderAuthType,
  35. ToolInvokeFrom,
  36. ToolParameter,
  37. ToolProviderType,
  38. )
  39. from core.tools.errors import ToolNotFoundError, ToolProviderNotFoundError
  40. from core.tools.tool_label_manager import ToolLabelManager
  41. from core.tools.utils.configuration import (
  42. ProviderConfigEncrypter,
  43. ToolParameterConfigurationManager,
  44. )
  45. from core.tools.workflow_as_tool.tool import WorkflowTool
  46. from extensions.ext_database import db
  47. from models.tools import ApiToolProvider, BuiltinToolProvider, WorkflowToolProvider
  48. from services.tools.tools_transform_service import ToolTransformService
  49. logger = logging.getLogger(__name__)
  50. class ToolManager:
  51. _builtin_provider_lock = Lock()
  52. _hardcoded_providers: dict[str, BuiltinToolProviderController] = {}
  53. _builtin_providers_loaded = False
  54. _builtin_tools_labels: dict[str, Union[I18nObject, None]] = {}
  55. @classmethod
  56. def get_hardcoded_provider(cls, provider: str) -> BuiltinToolProviderController:
  57. """
  58. get the hardcoded provider
  59. """
  60. if len(cls._hardcoded_providers) == 0:
  61. # init the builtin providers
  62. cls.load_hardcoded_providers_cache()
  63. return cls._hardcoded_providers[provider]
  64. @classmethod
  65. def get_builtin_provider(
  66. cls, provider: str, tenant_id: str
  67. ) -> BuiltinToolProviderController | PluginToolProviderController:
  68. """
  69. get the builtin provider
  70. :param provider: the name of the provider
  71. :param tenant_id: the id of the tenant
  72. :return: the provider
  73. """
  74. # split provider to
  75. if len(cls._hardcoded_providers) == 0:
  76. # init the builtin providers
  77. cls.load_hardcoded_providers_cache()
  78. if provider not in cls._hardcoded_providers:
  79. # get plugin provider
  80. plugin_provider = cls.get_plugin_provider(provider, tenant_id)
  81. if plugin_provider:
  82. return plugin_provider
  83. return cls._hardcoded_providers[provider]
  84. @classmethod
  85. def get_plugin_provider(cls, provider: str, tenant_id: str) -> PluginToolProviderController:
  86. """
  87. get the plugin provider
  88. """
  89. # check if context is set
  90. try:
  91. contexts.plugin_tool_providers.get()
  92. except LookupError:
  93. contexts.plugin_tool_providers.set({})
  94. contexts.plugin_tool_providers_lock.set(Lock())
  95. with contexts.plugin_tool_providers_lock.get():
  96. plugin_tool_providers = contexts.plugin_tool_providers.get()
  97. if provider in plugin_tool_providers:
  98. return plugin_tool_providers[provider]
  99. manager = PluginToolManager()
  100. provider_entity = manager.fetch_tool_provider(tenant_id, provider)
  101. if not provider_entity:
  102. raise ToolProviderNotFoundError(f"plugin provider {provider} not found")
  103. controller = PluginToolProviderController(
  104. entity=provider_entity.declaration,
  105. plugin_id=provider_entity.plugin_id,
  106. plugin_unique_identifier=provider_entity.plugin_unique_identifier,
  107. tenant_id=tenant_id,
  108. )
  109. plugin_tool_providers[provider] = controller
  110. return controller
  111. @classmethod
  112. def get_builtin_tool(cls, provider: str, tool_name: str, tenant_id: str) -> BuiltinTool | PluginTool | None:
  113. """
  114. get the builtin tool
  115. :param provider: the name of the provider
  116. :param tool_name: the name of the tool
  117. :param tenant_id: the id of the tenant
  118. :return: the provider, the tool
  119. """
  120. provider_controller = cls.get_builtin_provider(provider, tenant_id)
  121. tool = provider_controller.get_tool(tool_name)
  122. if tool is None:
  123. raise ToolNotFoundError(f"tool {tool_name} not found")
  124. return tool
  125. @classmethod
  126. def get_tool_runtime(
  127. cls,
  128. provider_type: ToolProviderType,
  129. provider_id: str,
  130. tool_name: str,
  131. tenant_id: str,
  132. invoke_from: InvokeFrom = InvokeFrom.DEBUGGER,
  133. tool_invoke_from: ToolInvokeFrom = ToolInvokeFrom.AGENT,
  134. ) -> Union[BuiltinTool, PluginTool, ApiTool, WorkflowTool]:
  135. """
  136. get the tool runtime
  137. :param provider_type: the type of the provider
  138. :param provider_id: the id of the provider
  139. :param tool_name: the name of the tool
  140. :param tenant_id: the tenant id
  141. :param invoke_from: invoke from
  142. :param tool_invoke_from: the tool invoke from
  143. :return: the tool
  144. """
  145. if provider_type == ToolProviderType.BUILT_IN:
  146. # check if the builtin tool need credentials
  147. provider_controller = cls.get_builtin_provider(provider_id, tenant_id)
  148. builtin_tool = provider_controller.get_tool(tool_name)
  149. if not builtin_tool:
  150. raise ToolProviderNotFoundError(f"builtin tool {tool_name} not found")
  151. if not provider_controller.need_credentials:
  152. return cast(
  153. BuiltinTool,
  154. builtin_tool.fork_tool_runtime(
  155. runtime=ToolRuntime(
  156. tenant_id=tenant_id,
  157. credentials={},
  158. invoke_from=invoke_from,
  159. tool_invoke_from=tool_invoke_from,
  160. )
  161. ),
  162. )
  163. if isinstance(provider_controller, PluginToolProviderController):
  164. provider_id_entity = ToolProviderID(provider_id)
  165. # get credentials
  166. builtin_provider: BuiltinToolProvider | None = (
  167. db.session.query(BuiltinToolProvider)
  168. .filter(
  169. BuiltinToolProvider.tenant_id == tenant_id,
  170. (BuiltinToolProvider.provider == str(provider_id_entity))
  171. | (BuiltinToolProvider.provider == provider_id_entity.provider_name),
  172. )
  173. .first()
  174. )
  175. if builtin_provider is None:
  176. raise ToolProviderNotFoundError(f"builtin provider {provider_id} not found")
  177. else:
  178. builtin_provider = (
  179. db.session.query(BuiltinToolProvider)
  180. .filter(BuiltinToolProvider.tenant_id == tenant_id, (BuiltinToolProvider.provider == provider_id))
  181. .first()
  182. )
  183. if builtin_provider is None:
  184. raise ToolProviderNotFoundError(f"builtin provider {provider_id} not found")
  185. # decrypt the credentials
  186. credentials = builtin_provider.credentials
  187. tool_configuration = ProviderConfigEncrypter(
  188. tenant_id=tenant_id,
  189. config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
  190. provider_type=provider_controller.provider_type.value,
  191. provider_identity=provider_controller.entity.identity.name,
  192. )
  193. decrypted_credentials = tool_configuration.decrypt(credentials)
  194. return cast(
  195. BuiltinTool,
  196. builtin_tool.fork_tool_runtime(
  197. runtime=ToolRuntime(
  198. tenant_id=tenant_id,
  199. credentials=decrypted_credentials,
  200. runtime_parameters={},
  201. invoke_from=invoke_from,
  202. tool_invoke_from=tool_invoke_from,
  203. )
  204. ),
  205. )
  206. elif provider_type == ToolProviderType.API:
  207. api_provider, credentials = cls.get_api_provider_controller(tenant_id, provider_id)
  208. # decrypt the credentials
  209. tool_configuration = ProviderConfigEncrypter(
  210. tenant_id=tenant_id,
  211. config=[x.to_basic_provider_config() for x in api_provider.get_credentials_schema()],
  212. provider_type=api_provider.provider_type.value,
  213. provider_identity=api_provider.entity.identity.name,
  214. )
  215. decrypted_credentials = tool_configuration.decrypt(credentials)
  216. return cast(
  217. ApiTool,
  218. api_provider.get_tool(tool_name).fork_tool_runtime(
  219. runtime=ToolRuntime(
  220. tenant_id=tenant_id,
  221. credentials=decrypted_credentials,
  222. invoke_from=invoke_from,
  223. tool_invoke_from=tool_invoke_from,
  224. )
  225. ),
  226. )
  227. elif provider_type == ToolProviderType.WORKFLOW:
  228. workflow_provider = (
  229. db.session.query(WorkflowToolProvider)
  230. .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == provider_id)
  231. .first()
  232. )
  233. if workflow_provider is None:
  234. raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
  235. controller = ToolTransformService.workflow_provider_to_controller(db_provider=workflow_provider)
  236. controller_tools: list[WorkflowTool] = controller.get_tools(tenant_id=workflow_provider.tenant_id)
  237. if controller_tools is None or len(controller_tools) == 0:
  238. raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
  239. return cast(
  240. WorkflowTool,
  241. controller.get_tools(tenant_id=workflow_provider.tenant_id)[0].fork_tool_runtime(
  242. runtime=ToolRuntime(
  243. tenant_id=tenant_id,
  244. credentials={},
  245. invoke_from=invoke_from,
  246. tool_invoke_from=tool_invoke_from,
  247. )
  248. ),
  249. )
  250. elif provider_type == ToolProviderType.APP:
  251. raise NotImplementedError("app provider not implemented")
  252. elif provider_type == ToolProviderType.PLUGIN:
  253. return cls.get_plugin_provider(provider_id, tenant_id).get_tool(tool_name)
  254. else:
  255. raise ToolProviderNotFoundError(f"provider type {provider_type.value} not found")
  256. @classmethod
  257. def get_agent_tool_runtime(
  258. cls,
  259. tenant_id: str,
  260. app_id: str,
  261. agent_tool: AgentToolEntity,
  262. invoke_from: InvokeFrom = InvokeFrom.DEBUGGER,
  263. ) -> Tool:
  264. """
  265. get the agent tool runtime
  266. """
  267. tool_entity = cls.get_tool_runtime(
  268. provider_type=agent_tool.provider_type,
  269. provider_id=agent_tool.provider_id,
  270. tool_name=agent_tool.tool_name,
  271. tenant_id=tenant_id,
  272. invoke_from=invoke_from,
  273. tool_invoke_from=ToolInvokeFrom.AGENT,
  274. )
  275. runtime_parameters = {}
  276. parameters = tool_entity.get_merged_runtime_parameters()
  277. for parameter in parameters:
  278. # check file types
  279. if (
  280. parameter.type
  281. in {
  282. ToolParameter.ToolParameterType.SYSTEM_FILES,
  283. ToolParameter.ToolParameterType.FILE,
  284. ToolParameter.ToolParameterType.FILES,
  285. }
  286. and parameter.required
  287. ):
  288. raise ValueError(f"file type parameter {parameter.name} not supported in agent")
  289. if parameter.form == ToolParameter.ToolParameterForm.FORM:
  290. # save tool parameter to tool entity memory
  291. value = parameter.init_frontend_parameter(agent_tool.tool_parameters.get(parameter.name))
  292. runtime_parameters[parameter.name] = value
  293. # decrypt runtime parameters
  294. encryption_manager = ToolParameterConfigurationManager(
  295. tenant_id=tenant_id,
  296. tool_runtime=tool_entity,
  297. provider_name=agent_tool.provider_id,
  298. provider_type=agent_tool.provider_type,
  299. identity_id=f"AGENT.{app_id}",
  300. )
  301. runtime_parameters = encryption_manager.decrypt_tool_parameters(runtime_parameters)
  302. if tool_entity.runtime is None or tool_entity.runtime.runtime_parameters is None:
  303. raise ValueError("runtime not found or runtime parameters not found")
  304. tool_entity.runtime.runtime_parameters.update(runtime_parameters)
  305. return tool_entity
  306. @classmethod
  307. def get_workflow_tool_runtime(
  308. cls,
  309. tenant_id: str,
  310. app_id: str,
  311. node_id: str,
  312. workflow_tool: "ToolEntity",
  313. invoke_from: InvokeFrom = InvokeFrom.DEBUGGER,
  314. ) -> Tool:
  315. """
  316. get the workflow tool runtime
  317. """
  318. tool_runtime = cls.get_tool_runtime(
  319. provider_type=workflow_tool.provider_type,
  320. provider_id=workflow_tool.provider_id,
  321. tool_name=workflow_tool.tool_name,
  322. tenant_id=tenant_id,
  323. invoke_from=invoke_from,
  324. tool_invoke_from=ToolInvokeFrom.WORKFLOW,
  325. )
  326. runtime_parameters = {}
  327. parameters = tool_runtime.get_merged_runtime_parameters()
  328. for parameter in parameters:
  329. # save tool parameter to tool entity memory
  330. if parameter.form == ToolParameter.ToolParameterForm.FORM:
  331. value = parameter.init_frontend_parameter(workflow_tool.tool_configurations.get(parameter.name))
  332. runtime_parameters[parameter.name] = value
  333. # decrypt runtime parameters
  334. encryption_manager = ToolParameterConfigurationManager(
  335. tenant_id=tenant_id,
  336. tool_runtime=tool_runtime,
  337. provider_name=workflow_tool.provider_id,
  338. provider_type=workflow_tool.provider_type,
  339. identity_id=f"WORKFLOW.{app_id}.{node_id}",
  340. )
  341. if runtime_parameters:
  342. runtime_parameters = encryption_manager.decrypt_tool_parameters(runtime_parameters)
  343. tool_runtime.runtime.runtime_parameters.update(runtime_parameters)
  344. return tool_runtime
  345. @classmethod
  346. def get_tool_runtime_from_plugin(
  347. cls,
  348. tool_type: ToolProviderType,
  349. tenant_id: str,
  350. provider: str,
  351. tool_name: str,
  352. tool_parameters: dict[str, Any],
  353. ) -> Tool:
  354. """
  355. get tool runtime from plugin
  356. """
  357. tool_entity = cls.get_tool_runtime(
  358. provider_type=tool_type,
  359. provider_id=provider,
  360. tool_name=tool_name,
  361. tenant_id=tenant_id,
  362. invoke_from=InvokeFrom.SERVICE_API,
  363. tool_invoke_from=ToolInvokeFrom.PLUGIN,
  364. )
  365. runtime_parameters = {}
  366. parameters = tool_entity.get_merged_runtime_parameters()
  367. for parameter in parameters:
  368. if parameter.form == ToolParameter.ToolParameterForm.FORM:
  369. # save tool parameter to tool entity memory
  370. value = parameter.init_frontend_parameter(tool_parameters.get(parameter.name))
  371. runtime_parameters[parameter.name] = value
  372. tool_entity.runtime.runtime_parameters.update(runtime_parameters)
  373. return tool_entity
  374. @classmethod
  375. def get_hardcoded_provider_icon(cls, provider: str) -> tuple[str, str]:
  376. """
  377. get the absolute path of the icon of the hardcoded provider
  378. :param provider: the name of the provider
  379. :return: the absolute path of the icon, the mime type of the icon
  380. """
  381. # get provider
  382. provider_controller = cls.get_hardcoded_provider(provider)
  383. absolute_path = path.join(
  384. path.dirname(path.realpath(__file__)),
  385. "builtin_tool",
  386. "providers",
  387. provider,
  388. "_assets",
  389. provider_controller.entity.identity.icon,
  390. )
  391. # check if the icon exists
  392. if not path.exists(absolute_path):
  393. raise ToolProviderNotFoundError(f"builtin provider {provider} icon not found")
  394. # get the mime type
  395. mime_type, _ = mimetypes.guess_type(absolute_path)
  396. mime_type = mime_type or "application/octet-stream"
  397. return absolute_path, mime_type
  398. @classmethod
  399. def list_hardcoded_providers(cls):
  400. # use cache first
  401. if cls._builtin_providers_loaded:
  402. yield from list(cls._hardcoded_providers.values())
  403. return
  404. with cls._builtin_provider_lock:
  405. if cls._builtin_providers_loaded:
  406. yield from list(cls._hardcoded_providers.values())
  407. return
  408. yield from cls._list_hardcoded_providers()
  409. @classmethod
  410. def list_plugin_providers(cls, tenant_id: str) -> list[PluginToolProviderController]:
  411. """
  412. list all the plugin providers
  413. """
  414. manager = PluginToolManager()
  415. provider_entities = manager.fetch_tool_providers(tenant_id)
  416. return [
  417. PluginToolProviderController(
  418. entity=provider.declaration,
  419. plugin_id=provider.plugin_id,
  420. plugin_unique_identifier=provider.plugin_unique_identifier,
  421. tenant_id=tenant_id,
  422. )
  423. for provider in provider_entities
  424. ]
  425. @classmethod
  426. def list_builtin_providers(
  427. cls, tenant_id: str
  428. ) -> Generator[BuiltinToolProviderController | PluginToolProviderController, None, None]:
  429. """
  430. list all the builtin providers
  431. """
  432. yield from cls.list_hardcoded_providers()
  433. # get plugin providers
  434. yield from cls.list_plugin_providers(tenant_id)
  435. @classmethod
  436. def _list_hardcoded_providers(cls) -> Generator[BuiltinToolProviderController, None, None]:
  437. """
  438. list all the builtin providers
  439. """
  440. for provider_path in listdir(path.join(path.dirname(path.realpath(__file__)), "builtin_tool", "providers")):
  441. if provider_path.startswith("__"):
  442. continue
  443. if path.isdir(path.join(path.dirname(path.realpath(__file__)), "builtin_tool", "providers", provider_path)):
  444. if provider_path.startswith("__"):
  445. continue
  446. # init provider
  447. try:
  448. provider_class = load_single_subclass_from_source(
  449. module_name=f"core.tools.builtin_tool.providers.{provider_path}.{provider_path}",
  450. script_path=path.join(
  451. path.dirname(path.realpath(__file__)),
  452. "builtin_tool",
  453. "providers",
  454. provider_path,
  455. f"{provider_path}.py",
  456. ),
  457. parent_type=BuiltinToolProviderController,
  458. )
  459. provider: BuiltinToolProviderController = provider_class()
  460. cls._hardcoded_providers[provider.entity.identity.name] = provider
  461. for tool in provider.get_tools():
  462. cls._builtin_tools_labels[tool.entity.identity.name] = tool.entity.identity.label
  463. yield provider
  464. except Exception:
  465. logger.exception(f"load builtin provider {provider_path}")
  466. continue
  467. # set builtin providers loaded
  468. cls._builtin_providers_loaded = True
  469. @classmethod
  470. def load_hardcoded_providers_cache(cls):
  471. for _ in cls.list_hardcoded_providers():
  472. pass
  473. @classmethod
  474. def clear_hardcoded_providers_cache(cls):
  475. cls._hardcoded_providers = {}
  476. cls._builtin_providers_loaded = False
  477. @classmethod
  478. def get_tool_label(cls, tool_name: str) -> Union[I18nObject, None]:
  479. """
  480. get the tool label
  481. :param tool_name: the name of the tool
  482. :return: the label of the tool
  483. """
  484. if len(cls._builtin_tools_labels) == 0:
  485. # init the builtin providers
  486. cls.load_hardcoded_providers_cache()
  487. if tool_name not in cls._builtin_tools_labels:
  488. return None
  489. return cls._builtin_tools_labels[tool_name]
  490. @classmethod
  491. def list_providers_from_api(
  492. cls, user_id: str, tenant_id: str, typ: ToolProviderTypeApiLiteral
  493. ) -> list[ToolProviderApiEntity]:
  494. result_providers: dict[str, ToolProviderApiEntity] = {}
  495. filters = []
  496. if not typ:
  497. filters.extend(["builtin", "api", "workflow"])
  498. else:
  499. filters.append(typ)
  500. with db.session.no_autoflush:
  501. if "builtin" in filters:
  502. # get builtin providers
  503. builtin_providers = cls.list_builtin_providers(tenant_id)
  504. # get db builtin providers
  505. db_builtin_providers: list[BuiltinToolProvider] = (
  506. db.session.query(BuiltinToolProvider).filter(BuiltinToolProvider.tenant_id == tenant_id).all()
  507. )
  508. # rewrite db_builtin_providers
  509. for db_provider in db_builtin_providers:
  510. tool_provider_id = str(ToolProviderID(db_provider.provider))
  511. db_provider.provider = tool_provider_id
  512. def find_db_builtin_provider(provider):
  513. return next((x for x in db_builtin_providers if x.provider == provider), None)
  514. # append builtin providers
  515. for provider in builtin_providers:
  516. # handle include, exclude
  517. if is_filtered(
  518. include_set=cast(set[str], dify_config.POSITION_TOOL_INCLUDES_SET),
  519. exclude_set=cast(set[str], dify_config.POSITION_TOOL_EXCLUDES_SET),
  520. data=provider,
  521. name_func=lambda x: x.identity.name,
  522. ):
  523. continue
  524. user_provider = ToolTransformService.builtin_provider_to_user_provider(
  525. provider_controller=provider,
  526. db_provider=find_db_builtin_provider(provider.entity.identity.name),
  527. decrypt_credentials=False,
  528. )
  529. if isinstance(provider, PluginToolProviderController):
  530. result_providers[f"plugin_provider.{user_provider.name}"] = user_provider
  531. else:
  532. result_providers[f"builtin_provider.{user_provider.name}"] = user_provider
  533. # get db api providers
  534. if "api" in filters:
  535. db_api_providers: list[ApiToolProvider] = (
  536. db.session.query(ApiToolProvider).filter(ApiToolProvider.tenant_id == tenant_id).all()
  537. )
  538. api_provider_controllers: list[dict[str, Any]] = [
  539. {"provider": provider, "controller": ToolTransformService.api_provider_to_controller(provider)}
  540. for provider in db_api_providers
  541. ]
  542. # get labels
  543. labels = ToolLabelManager.get_tools_labels([x["controller"] for x in api_provider_controllers])
  544. for api_provider_controller in api_provider_controllers:
  545. user_provider = ToolTransformService.api_provider_to_user_provider(
  546. provider_controller=api_provider_controller["controller"],
  547. db_provider=api_provider_controller["provider"],
  548. decrypt_credentials=False,
  549. labels=labels.get(api_provider_controller["controller"].provider_id, []),
  550. )
  551. result_providers[f"api_provider.{user_provider.name}"] = user_provider
  552. if "workflow" in filters:
  553. # get workflow providers
  554. workflow_providers: list[WorkflowToolProvider] = (
  555. db.session.query(WorkflowToolProvider).filter(WorkflowToolProvider.tenant_id == tenant_id).all()
  556. )
  557. workflow_provider_controllers: list[WorkflowToolProviderController] = []
  558. for workflow_provider in workflow_providers:
  559. try:
  560. workflow_provider_controllers.append(
  561. ToolTransformService.workflow_provider_to_controller(db_provider=workflow_provider)
  562. )
  563. except Exception:
  564. # app has been deleted
  565. pass
  566. labels = ToolLabelManager.get_tools_labels(
  567. [cast(ToolProviderController, controller) for controller in workflow_provider_controllers]
  568. )
  569. for provider_controller in workflow_provider_controllers:
  570. user_provider = ToolTransformService.workflow_provider_to_user_provider(
  571. provider_controller=provider_controller,
  572. labels=labels.get(provider_controller.provider_id, []),
  573. )
  574. result_providers[f"workflow_provider.{user_provider.name}"] = user_provider
  575. return BuiltinToolProviderSort.sort(list(result_providers.values()))
  576. @classmethod
  577. def get_api_provider_controller(
  578. cls, tenant_id: str, provider_id: str
  579. ) -> tuple[ApiToolProviderController, dict[str, Any]]:
  580. """
  581. get the api provider
  582. :param tenant_id: the id of the tenant
  583. :param provider_id: the id of the provider
  584. :return: the provider controller, the credentials
  585. """
  586. provider: ApiToolProvider | None = (
  587. db.session.query(ApiToolProvider)
  588. .filter(
  589. ApiToolProvider.id == provider_id,
  590. ApiToolProvider.tenant_id == tenant_id,
  591. )
  592. .first()
  593. )
  594. if provider is None:
  595. raise ToolProviderNotFoundError(f"api provider {provider_id} not found")
  596. controller = ApiToolProviderController.from_db(
  597. provider,
  598. ApiProviderAuthType.API_KEY if provider.credentials["auth_type"] == "api_key" else ApiProviderAuthType.NONE,
  599. )
  600. controller.load_bundled_tools(provider.tools)
  601. return controller, provider.credentials
  602. @classmethod
  603. def user_get_api_provider(cls, provider: str, tenant_id: str) -> dict:
  604. """
  605. get api provider
  606. """
  607. """
  608. get tool provider
  609. """
  610. provider_name = provider
  611. provider_obj: ApiToolProvider | None = (
  612. db.session.query(ApiToolProvider)
  613. .filter(
  614. ApiToolProvider.tenant_id == tenant_id,
  615. ApiToolProvider.name == provider,
  616. )
  617. .first()
  618. )
  619. if provider_obj is None:
  620. raise ValueError(f"you have not added provider {provider_name}")
  621. try:
  622. credentials = json.loads(provider_obj.credentials_str) or {}
  623. except Exception:
  624. credentials = {}
  625. # package tool provider controller
  626. controller = ApiToolProviderController.from_db(
  627. provider_obj,
  628. ApiProviderAuthType.API_KEY if credentials["auth_type"] == "api_key" else ApiProviderAuthType.NONE,
  629. )
  630. # init tool configuration
  631. tool_configuration = ProviderConfigEncrypter(
  632. tenant_id=tenant_id,
  633. config=[x.to_basic_provider_config() for x in controller.get_credentials_schema()],
  634. provider_type=controller.provider_type.value,
  635. provider_identity=controller.entity.identity.name,
  636. )
  637. decrypted_credentials = tool_configuration.decrypt(credentials)
  638. masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
  639. try:
  640. icon = json.loads(provider_obj.icon)
  641. except Exception:
  642. icon = {"background": "#252525", "content": "\ud83d\ude01"}
  643. # add tool labels
  644. labels = ToolLabelManager.get_tool_labels(controller)
  645. return cast(
  646. dict,
  647. jsonable_encoder(
  648. {
  649. "schema_type": provider_obj.schema_type,
  650. "schema": provider_obj.schema,
  651. "tools": provider_obj.tools,
  652. "icon": icon,
  653. "description": provider_obj.description,
  654. "credentials": masked_credentials,
  655. "privacy_policy": provider_obj.privacy_policy,
  656. "custom_disclaimer": provider_obj.custom_disclaimer,
  657. "labels": labels,
  658. }
  659. ),
  660. )
  661. @classmethod
  662. def generate_builtin_tool_icon_url(cls, provider_id: str) -> str:
  663. return str(
  664. URL(dify_config.CONSOLE_API_URL or "/")
  665. / "console"
  666. / "api"
  667. / "workspaces"
  668. / "current"
  669. / "tool-provider"
  670. / "builtin"
  671. / provider_id
  672. / "icon"
  673. )
  674. @classmethod
  675. def generate_plugin_tool_icon_url(cls, tenant_id: str, filename: str) -> str:
  676. return str(
  677. URL(dify_config.CONSOLE_API_URL or "/")
  678. / "console"
  679. / "api"
  680. / "workspaces"
  681. / "current"
  682. / "plugin"
  683. / "icon"
  684. % {"tenant_id": tenant_id, "filename": filename}
  685. )
  686. @classmethod
  687. def generate_workflow_tool_icon_url(cls, tenant_id: str, provider_id: str) -> dict:
  688. try:
  689. workflow_provider: WorkflowToolProvider | None = (
  690. db.session.query(WorkflowToolProvider)
  691. .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == provider_id)
  692. .first()
  693. )
  694. if workflow_provider is None:
  695. raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
  696. icon: dict = json.loads(workflow_provider.icon)
  697. return icon
  698. except Exception:
  699. return {"background": "#252525", "content": "\ud83d\ude01"}
  700. @classmethod
  701. def generate_api_tool_icon_url(cls, tenant_id: str, provider_id: str) -> dict:
  702. try:
  703. api_provider: ApiToolProvider | None = (
  704. db.session.query(ApiToolProvider)
  705. .filter(ApiToolProvider.tenant_id == tenant_id, ApiToolProvider.id == provider_id)
  706. .first()
  707. )
  708. if api_provider is None:
  709. raise ToolProviderNotFoundError(f"api provider {provider_id} not found")
  710. icon: dict = json.loads(api_provider.icon)
  711. return icon
  712. except Exception:
  713. return {"background": "#252525", "content": "\ud83d\ude01"}
  714. @classmethod
  715. def get_tool_icon(
  716. cls,
  717. tenant_id: str,
  718. provider_type: ToolProviderType,
  719. provider_id: str,
  720. ) -> Union[str, dict]:
  721. """
  722. get the tool icon
  723. :param tenant_id: the id of the tenant
  724. :param provider_type: the type of the provider
  725. :param provider_id: the id of the provider
  726. :return:
  727. """
  728. provider_type = provider_type
  729. provider_id = provider_id
  730. if provider_type == ToolProviderType.BUILT_IN:
  731. provider = ToolManager.get_builtin_provider(provider_id, tenant_id)
  732. if isinstance(provider, PluginToolProviderController):
  733. try:
  734. return cls.generate_plugin_tool_icon_url(tenant_id, provider.entity.identity.icon)
  735. except Exception:
  736. return {"background": "#252525", "content": "\ud83d\ude01"}
  737. return cls.generate_builtin_tool_icon_url(provider_id)
  738. elif provider_type == ToolProviderType.API:
  739. return cls.generate_api_tool_icon_url(tenant_id, provider_id)
  740. elif provider_type == ToolProviderType.WORKFLOW:
  741. return cls.generate_workflow_tool_icon_url(tenant_id, provider_id)
  742. elif provider_type == ToolProviderType.PLUGIN:
  743. provider = ToolManager.get_builtin_provider(provider_id, tenant_id)
  744. if isinstance(provider, PluginToolProviderController):
  745. try:
  746. return cls.generate_plugin_tool_icon_url(tenant_id, provider.entity.identity.icon)
  747. except Exception:
  748. return {"background": "#252525", "content": "\ud83d\ude01"}
  749. raise ValueError(f"plugin provider {provider_id} not found")
  750. else:
  751. raise ValueError(f"provider type {provider_type} not found")
  752. ToolManager.load_hardcoded_providers_cache()