tool_manager.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  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, Optional, Union, cast
  8. from yarl import URL
  9. import contexts
  10. from core.plugin.entities.plugin import GenericProviderID
  11. from core.plugin.manager.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 = {}
  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_name: the name of the provider
  139. :param tool_name: the name of the tool
  140. :return: the tool
  141. """
  142. if provider_type == ToolProviderType.BUILT_IN:
  143. # check if the builtin tool need credentials
  144. provider_controller = cls.get_builtin_provider(provider_id, tenant_id)
  145. builtin_tool = provider_controller.get_tool(tool_name)
  146. if not builtin_tool:
  147. raise ToolProviderNotFoundError(f"builtin tool {tool_name} not found")
  148. if not provider_controller.need_credentials:
  149. return cast(
  150. BuiltinTool,
  151. builtin_tool.fork_tool_runtime(
  152. runtime=ToolRuntime(
  153. tenant_id=tenant_id,
  154. credentials={},
  155. invoke_from=invoke_from,
  156. tool_invoke_from=tool_invoke_from,
  157. )
  158. ),
  159. )
  160. if isinstance(provider_controller, PluginToolProviderController):
  161. provider_id_entity = GenericProviderID(provider_id)
  162. # get credentials
  163. builtin_provider: BuiltinToolProvider | None = (
  164. db.session.query(BuiltinToolProvider)
  165. .filter(
  166. BuiltinToolProvider.tenant_id == tenant_id,
  167. (BuiltinToolProvider.provider == provider_id)
  168. | (BuiltinToolProvider.provider == provider_id_entity.provider_name),
  169. )
  170. .first()
  171. )
  172. if builtin_provider is None:
  173. raise ToolProviderNotFoundError(f"builtin provider {provider_id} not found")
  174. else:
  175. builtin_provider: BuiltinToolProvider | None = (
  176. db.session.query(BuiltinToolProvider)
  177. .filter(BuiltinToolProvider.tenant_id == tenant_id, (BuiltinToolProvider.provider == provider_id))
  178. .first()
  179. )
  180. if builtin_provider is None:
  181. raise ToolProviderNotFoundError(f"builtin provider {provider_id} not found")
  182. # decrypt the credentials
  183. credentials = builtin_provider.credentials
  184. tool_configuration = ProviderConfigEncrypter(
  185. tenant_id=tenant_id,
  186. config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
  187. provider_type=provider_controller.provider_type.value,
  188. provider_identity=provider_controller.entity.identity.name,
  189. )
  190. decrypted_credentials = tool_configuration.decrypt(credentials)
  191. return cast(
  192. BuiltinTool,
  193. builtin_tool.fork_tool_runtime(
  194. runtime=ToolRuntime(
  195. tenant_id=tenant_id,
  196. credentials=decrypted_credentials,
  197. runtime_parameters={},
  198. invoke_from=invoke_from,
  199. tool_invoke_from=tool_invoke_from,
  200. )
  201. ),
  202. )
  203. elif provider_type == ToolProviderType.API:
  204. api_provider, credentials = cls.get_api_provider_controller(tenant_id, provider_id)
  205. # decrypt the credentials
  206. tool_configuration = ProviderConfigEncrypter(
  207. tenant_id=tenant_id,
  208. config=[x.to_basic_provider_config() for x in api_provider.get_credentials_schema()],
  209. provider_type=api_provider.provider_type.value,
  210. provider_identity=api_provider.entity.identity.name,
  211. )
  212. decrypted_credentials = tool_configuration.decrypt(credentials)
  213. return cast(
  214. ApiTool,
  215. api_provider.get_tool(tool_name).fork_tool_runtime(
  216. runtime=ToolRuntime(
  217. tenant_id=tenant_id,
  218. credentials=decrypted_credentials,
  219. invoke_from=invoke_from,
  220. tool_invoke_from=tool_invoke_from,
  221. )
  222. ),
  223. )
  224. elif provider_type == ToolProviderType.WORKFLOW:
  225. workflow_provider = (
  226. db.session.query(WorkflowToolProvider)
  227. .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == provider_id)
  228. .first()
  229. )
  230. if workflow_provider is None:
  231. raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
  232. controller = ToolTransformService.workflow_provider_to_controller(db_provider=workflow_provider)
  233. controller_tools: Optional[list[Tool]] = controller.get_tools(
  234. user_id="", tenant_id=workflow_provider.tenant_id
  235. )
  236. if controller_tools is None or len(controller_tools) == 0:
  237. raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
  238. return cast(
  239. WorkflowTool,
  240. controller.get_tools(tenant_id=workflow_provider.tenant_id)[0].fork_tool_runtime(
  241. runtime=ToolRuntime(
  242. tenant_id=tenant_id,
  243. credentials={},
  244. invoke_from=invoke_from,
  245. tool_invoke_from=tool_invoke_from,
  246. )
  247. ),
  248. )
  249. elif provider_type == ToolProviderType.APP:
  250. raise NotImplementedError("app provider not implemented")
  251. elif provider_type == ToolProviderType.PLUGIN:
  252. return cls.get_plugin_provider(provider_id, tenant_id).get_tool(tool_name)
  253. else:
  254. raise ToolProviderNotFoundError(f"provider type {provider_type.value} not found")
  255. @classmethod
  256. def get_agent_tool_runtime(
  257. cls,
  258. tenant_id: str,
  259. app_id: str,
  260. agent_tool: AgentToolEntity,
  261. invoke_from: InvokeFrom = InvokeFrom.DEBUGGER,
  262. ) -> Tool:
  263. """
  264. get the agent tool runtime
  265. """
  266. tool_entity = cls.get_tool_runtime(
  267. provider_type=agent_tool.provider_type,
  268. provider_id=agent_tool.provider_id,
  269. tool_name=agent_tool.tool_name,
  270. tenant_id=tenant_id,
  271. invoke_from=invoke_from,
  272. tool_invoke_from=ToolInvokeFrom.AGENT,
  273. )
  274. runtime_parameters = {}
  275. parameters = tool_entity.get_merged_runtime_parameters()
  276. for parameter in parameters:
  277. # check file types
  278. if (
  279. parameter.type
  280. in {
  281. ToolParameter.ToolParameterType.SYSTEM_FILES,
  282. ToolParameter.ToolParameterType.FILE,
  283. ToolParameter.ToolParameterType.FILES,
  284. }
  285. and parameter.required
  286. ):
  287. raise ValueError(f"file type parameter {parameter.name} not supported in agent")
  288. if parameter.form == ToolParameter.ToolParameterForm.FORM:
  289. # save tool parameter to tool entity memory
  290. value = parameter.init_frontend_parameter(agent_tool.tool_parameters.get(parameter.name))
  291. runtime_parameters[parameter.name] = value
  292. # decrypt runtime parameters
  293. encryption_manager = ToolParameterConfigurationManager(
  294. tenant_id=tenant_id,
  295. tool_runtime=tool_entity,
  296. provider_name=agent_tool.provider_id,
  297. provider_type=agent_tool.provider_type,
  298. identity_id=f"AGENT.{app_id}",
  299. )
  300. runtime_parameters = encryption_manager.decrypt_tool_parameters(runtime_parameters)
  301. if tool_entity.runtime is None or tool_entity.runtime.runtime_parameters is None:
  302. raise ValueError("runtime not found or runtime parameters not found")
  303. tool_entity.runtime.runtime_parameters.update(runtime_parameters)
  304. return tool_entity
  305. @classmethod
  306. def get_workflow_tool_runtime(
  307. cls,
  308. tenant_id: str,
  309. app_id: str,
  310. node_id: str,
  311. workflow_tool: "ToolEntity",
  312. invoke_from: InvokeFrom = InvokeFrom.DEBUGGER,
  313. ) -> Tool:
  314. """
  315. get the workflow tool runtime
  316. """
  317. tool_runtime = cls.get_tool_runtime(
  318. provider_type=workflow_tool.provider_type,
  319. provider_id=workflow_tool.provider_id,
  320. tool_name=workflow_tool.tool_name,
  321. tenant_id=tenant_id,
  322. invoke_from=invoke_from,
  323. tool_invoke_from=ToolInvokeFrom.WORKFLOW,
  324. )
  325. runtime_parameters = {}
  326. parameters = tool_runtime.get_merged_runtime_parameters()
  327. for parameter in parameters:
  328. # save tool parameter to tool entity memory
  329. if parameter.form == ToolParameter.ToolParameterForm.FORM:
  330. value = parameter.init_frontend_parameter(workflow_tool.tool_configurations.get(parameter.name))
  331. runtime_parameters[parameter.name] = value
  332. # decrypt runtime parameters
  333. encryption_manager = ToolParameterConfigurationManager(
  334. tenant_id=tenant_id,
  335. tool_runtime=tool_runtime,
  336. provider_name=workflow_tool.provider_id,
  337. provider_type=workflow_tool.provider_type,
  338. identity_id=f"WORKFLOW.{app_id}.{node_id}",
  339. )
  340. if runtime_parameters:
  341. runtime_parameters = encryption_manager.decrypt_tool_parameters(runtime_parameters)
  342. tool_runtime.runtime.runtime_parameters.update(runtime_parameters)
  343. return tool_runtime
  344. @classmethod
  345. def get_tool_runtime_from_plugin(
  346. cls,
  347. tool_type: ToolProviderType,
  348. tenant_id: str,
  349. provider: str,
  350. tool_name: str,
  351. tool_parameters: dict[str, Any],
  352. ) -> Tool:
  353. """
  354. get tool runtime from plugin
  355. """
  356. tool_entity = cls.get_tool_runtime(
  357. provider_type=tool_type,
  358. provider_id=provider,
  359. tool_name=tool_name,
  360. tenant_id=tenant_id,
  361. invoke_from=InvokeFrom.SERVICE_API,
  362. tool_invoke_from=ToolInvokeFrom.PLUGIN,
  363. )
  364. runtime_parameters = {}
  365. parameters = tool_entity.get_merged_runtime_parameters()
  366. for parameter in parameters:
  367. if parameter.form == ToolParameter.ToolParameterForm.FORM:
  368. # save tool parameter to tool entity memory
  369. value = parameter.init_frontend_parameter(tool_parameters.get(parameter.name))
  370. runtime_parameters[parameter.name] = value
  371. tool_entity.runtime.runtime_parameters.update(runtime_parameters)
  372. return tool_entity
  373. @classmethod
  374. def get_hardcoded_provider_icon(cls, provider: str) -> tuple[str, str]:
  375. """
  376. get the absolute path of the icon of the hardcoded provider
  377. :param provider: the name of the provider
  378. :param tenant_id: the id of the tenant
  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}")
  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. if "builtin" in filters:
  501. # get builtin providers
  502. builtin_providers = cls.list_builtin_providers(tenant_id)
  503. # get db builtin providers
  504. db_builtin_providers: list[BuiltinToolProvider] = (
  505. db.session.query(BuiltinToolProvider).filter(BuiltinToolProvider.tenant_id == tenant_id).all()
  506. )
  507. # rewrite db_builtin_providers
  508. for db_provider in db_builtin_providers:
  509. tool_provider_id = GenericProviderID(db_provider.provider)
  510. db_provider.provider = tool_provider_id.to_string()
  511. def find_db_builtin_provider(provider):
  512. return next((x for x in db_builtin_providers if x.provider == provider), None)
  513. # append builtin providers
  514. for provider in builtin_providers:
  515. # handle include, exclude
  516. if is_filtered(
  517. include_set=cast(set[str], dify_config.POSITION_TOOL_INCLUDES_SET),
  518. exclude_set=cast(set[str], dify_config.POSITION_TOOL_EXCLUDES_SET),
  519. data=provider,
  520. name_func=lambda x: x.identity.name,
  521. ):
  522. continue
  523. user_provider = ToolTransformService.builtin_provider_to_user_provider(
  524. provider_controller=provider,
  525. db_provider=find_db_builtin_provider(provider.entity.identity.name),
  526. decrypt_credentials=False,
  527. )
  528. if isinstance(provider, PluginToolProviderController):
  529. result_providers[f"plugin_provider.{user_provider.name}"] = user_provider
  530. else:
  531. result_providers[f"builtin_provider.{user_provider.name}"] = user_provider
  532. # get db api providers
  533. if "api" in filters:
  534. db_api_providers: list[ApiToolProvider] = (
  535. db.session.query(ApiToolProvider).filter(ApiToolProvider.tenant_id == tenant_id).all()
  536. )
  537. api_provider_controllers: list[dict[str, Any]] = [
  538. {"provider": provider, "controller": ToolTransformService.api_provider_to_controller(provider)}
  539. for provider in db_api_providers
  540. ]
  541. # get labels
  542. labels = ToolLabelManager.get_tools_labels([x["controller"] for x in api_provider_controllers])
  543. for api_provider_controller in api_provider_controllers:
  544. user_provider = ToolTransformService.api_provider_to_user_provider(
  545. provider_controller=api_provider_controller["controller"],
  546. db_provider=api_provider_controller["provider"],
  547. decrypt_credentials=False,
  548. labels=labels.get(api_provider_controller["controller"].provider_id, []),
  549. )
  550. result_providers[f"api_provider.{user_provider.name}"] = user_provider
  551. if "workflow" in filters:
  552. # get workflow providers
  553. workflow_providers: list[WorkflowToolProvider] = (
  554. db.session.query(WorkflowToolProvider).filter(WorkflowToolProvider.tenant_id == tenant_id).all()
  555. )
  556. workflow_provider_controllers: list[WorkflowToolProviderController] = []
  557. for provider in workflow_providers:
  558. try:
  559. workflow_provider_controllers.append(
  560. ToolTransformService.workflow_provider_to_controller(db_provider=provider)
  561. )
  562. except Exception:
  563. # app has been deleted
  564. pass
  565. labels = ToolLabelManager.get_tools_labels(
  566. [cast(ToolProviderController, controller) for controller in workflow_provider_controllers]
  567. )
  568. for provider_controller in workflow_provider_controllers:
  569. user_provider = ToolTransformService.workflow_provider_to_user_provider(
  570. provider_controller=provider_controller,
  571. labels=labels.get(provider_controller.provider_id, []),
  572. )
  573. result_providers[f"workflow_provider.{user_provider.name}"] = user_provider
  574. return BuiltinToolProviderSort.sort(list(result_providers.values()))
  575. @classmethod
  576. def get_api_provider_controller(
  577. cls, tenant_id: str, provider_id: str
  578. ) -> tuple[ApiToolProviderController, dict[str, Any]]:
  579. """
  580. get the api provider
  581. :param provider_name: the name of the provider
  582. :return: the provider controller, the credentials
  583. """
  584. provider: ApiToolProvider | None = (
  585. db.session.query(ApiToolProvider)
  586. .filter(
  587. ApiToolProvider.id == provider_id,
  588. ApiToolProvider.tenant_id == tenant_id,
  589. )
  590. .first()
  591. )
  592. if provider is None:
  593. raise ToolProviderNotFoundError(f"api provider {provider_id} not found")
  594. controller = ApiToolProviderController.from_db(
  595. provider,
  596. ApiProviderAuthType.API_KEY if provider.credentials["auth_type"] == "api_key" else ApiProviderAuthType.NONE,
  597. )
  598. controller.load_bundled_tools(provider.tools)
  599. return controller, provider.credentials
  600. @classmethod
  601. def user_get_api_provider(cls, provider: str, tenant_id: str) -> dict:
  602. """
  603. get api provider
  604. """
  605. """
  606. get tool provider
  607. """
  608. provider_name = provider
  609. provider_obj: ApiToolProvider | None = (
  610. db.session.query(ApiToolProvider)
  611. .filter(
  612. ApiToolProvider.tenant_id == tenant_id,
  613. ApiToolProvider.name == provider,
  614. )
  615. .first()
  616. )
  617. if provider_obj is None:
  618. raise ValueError(f"you have not added provider {provider_name}")
  619. try:
  620. credentials = json.loads(provider_obj.credentials_str) or {}
  621. except Exception:
  622. credentials = {}
  623. # package tool provider controller
  624. controller = ApiToolProviderController.from_db(
  625. provider_obj,
  626. ApiProviderAuthType.API_KEY if credentials["auth_type"] == "api_key" else ApiProviderAuthType.NONE,
  627. )
  628. # init tool configuration
  629. tool_configuration = ProviderConfigEncrypter(
  630. tenant_id=tenant_id,
  631. config=[x.to_basic_provider_config() for x in controller.get_credentials_schema()],
  632. provider_type=controller.provider_type.value,
  633. provider_identity=controller.entity.identity.name,
  634. )
  635. decrypted_credentials = tool_configuration.decrypt(credentials)
  636. masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
  637. try:
  638. icon = json.loads(provider_obj.icon)
  639. except Exception:
  640. icon = {"background": "#252525", "content": "\ud83d\ude01"}
  641. # add tool labels
  642. labels = ToolLabelManager.get_tool_labels(controller)
  643. return jsonable_encoder(
  644. {
  645. "schema_type": provider_obj.schema_type,
  646. "schema": provider_obj.schema,
  647. "tools": provider_obj.tools,
  648. "icon": icon,
  649. "description": provider_obj.description,
  650. "credentials": masked_credentials,
  651. "privacy_policy": provider_obj.privacy_policy,
  652. "custom_disclaimer": provider_obj.custom_disclaimer,
  653. "labels": labels,
  654. }
  655. )
  656. @classmethod
  657. def generate_builtin_tool_icon_url(cls, provider_id: str) -> str:
  658. return (
  659. dify_config.CONSOLE_API_URL
  660. + "/console/api/workspaces/current/tool-provider/builtin/"
  661. + provider_id
  662. + "/icon"
  663. )
  664. @classmethod
  665. def generate_plugin_tool_icon_url(cls, tenant_id: str, filename: str) -> str:
  666. return str(
  667. URL(dify_config.CONSOLE_API_URL)
  668. / "console"
  669. / "api"
  670. / "workspaces"
  671. / "current"
  672. / "plugin"
  673. / "icon"
  674. % {"tenant_id": tenant_id, "filename": filename}
  675. )
  676. @classmethod
  677. def generate_workflow_tool_icon_url(cls, tenant_id: str, provider_id: str) -> dict:
  678. try:
  679. workflow_provider: WorkflowToolProvider | None = (
  680. db.session.query(WorkflowToolProvider)
  681. .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == provider_id)
  682. .first()
  683. )
  684. if workflow_provider is None:
  685. raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
  686. return json.loads(workflow_provider.icon)
  687. except Exception:
  688. return {"background": "#252525", "content": "\ud83d\ude01"}
  689. @classmethod
  690. def generate_api_tool_icon_url(cls, tenant_id: str, provider_id: str) -> dict:
  691. try:
  692. api_provider: ApiToolProvider | None = (
  693. db.session.query(ApiToolProvider)
  694. .filter(ApiToolProvider.tenant_id == tenant_id, ApiToolProvider.id == provider_id)
  695. .first()
  696. )
  697. if api_provider is None:
  698. raise ToolProviderNotFoundError(f"api provider {provider_id} not found")
  699. return json.loads(api_provider.icon)
  700. except Exception:
  701. return {"background": "#252525", "content": "\ud83d\ude01"}
  702. @classmethod
  703. def get_tool_icon(
  704. cls,
  705. tenant_id: str,
  706. provider_type: ToolProviderType,
  707. provider_id: str,
  708. ) -> Union[str, dict]:
  709. """
  710. get the tool icon
  711. :param tenant_id: the id of the tenant
  712. :param provider_type: the type of the provider
  713. :param provider_id: the id of the provider
  714. :return:
  715. """
  716. provider_type = provider_type
  717. provider_id = provider_id
  718. if provider_type == ToolProviderType.BUILT_IN:
  719. provider = ToolManager.get_builtin_provider(provider_id, tenant_id)
  720. if isinstance(provider, PluginToolProviderController):
  721. try:
  722. return cls.generate_plugin_tool_icon_url(tenant_id, provider.entity.identity.icon)
  723. except Exception:
  724. return {"background": "#252525", "content": "\ud83d\ude01"}
  725. return cls.generate_builtin_tool_icon_url(provider_id)
  726. elif provider_type == ToolProviderType.API:
  727. return cls.generate_api_tool_icon_url(tenant_id, provider_id)
  728. elif provider_type == ToolProviderType.WORKFLOW:
  729. return cls.generate_workflow_tool_icon_url(tenant_id, provider_id)
  730. elif provider_type == ToolProviderType.PLUGIN:
  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. raise ValueError(f"plugin provider {provider_id} not found")
  738. else:
  739. raise ValueError(f"provider type {provider_type} not found")
  740. ToolManager.load_hardcoded_providers_cache()