tool_manager.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  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.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: 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_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 = ToolProviderID(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 == str(provider_id_entity))
  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 = (
  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: list[WorkflowTool] = controller.get_tools(tenant_id=workflow_provider.tenant_id)
  234. if controller_tools is None or len(controller_tools) == 0:
  235. raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
  236. return cast(
  237. WorkflowTool,
  238. controller.get_tools(tenant_id=workflow_provider.tenant_id)[0].fork_tool_runtime(
  239. runtime=ToolRuntime(
  240. tenant_id=tenant_id,
  241. credentials={},
  242. invoke_from=invoke_from,
  243. tool_invoke_from=tool_invoke_from,
  244. )
  245. ),
  246. )
  247. elif provider_type == ToolProviderType.APP:
  248. raise NotImplementedError("app provider not implemented")
  249. elif provider_type == ToolProviderType.PLUGIN:
  250. return cls.get_plugin_provider(provider_id, tenant_id).get_tool(tool_name)
  251. else:
  252. raise ToolProviderNotFoundError(f"provider type {provider_type.value} not found")
  253. @classmethod
  254. def get_agent_tool_runtime(
  255. cls,
  256. tenant_id: str,
  257. app_id: str,
  258. agent_tool: AgentToolEntity,
  259. invoke_from: InvokeFrom = InvokeFrom.DEBUGGER,
  260. ) -> Tool:
  261. """
  262. get the agent tool runtime
  263. """
  264. tool_entity = cls.get_tool_runtime(
  265. provider_type=agent_tool.provider_type,
  266. provider_id=agent_tool.provider_id,
  267. tool_name=agent_tool.tool_name,
  268. tenant_id=tenant_id,
  269. invoke_from=invoke_from,
  270. tool_invoke_from=ToolInvokeFrom.AGENT,
  271. )
  272. runtime_parameters = {}
  273. parameters = tool_entity.get_merged_runtime_parameters()
  274. for parameter in parameters:
  275. # check file types
  276. if (
  277. parameter.type
  278. in {
  279. ToolParameter.ToolParameterType.SYSTEM_FILES,
  280. ToolParameter.ToolParameterType.FILE,
  281. ToolParameter.ToolParameterType.FILES,
  282. }
  283. and parameter.required
  284. ):
  285. raise ValueError(f"file type parameter {parameter.name} not supported in agent")
  286. if parameter.form == ToolParameter.ToolParameterForm.FORM:
  287. # save tool parameter to tool entity memory
  288. value = parameter.init_frontend_parameter(agent_tool.tool_parameters.get(parameter.name))
  289. runtime_parameters[parameter.name] = value
  290. # decrypt runtime parameters
  291. encryption_manager = ToolParameterConfigurationManager(
  292. tenant_id=tenant_id,
  293. tool_runtime=tool_entity,
  294. provider_name=agent_tool.provider_id,
  295. provider_type=agent_tool.provider_type,
  296. identity_id=f"AGENT.{app_id}",
  297. )
  298. runtime_parameters = encryption_manager.decrypt_tool_parameters(runtime_parameters)
  299. if tool_entity.runtime is None or tool_entity.runtime.runtime_parameters is None:
  300. raise ValueError("runtime not found or runtime parameters not found")
  301. tool_entity.runtime.runtime_parameters.update(runtime_parameters)
  302. return tool_entity
  303. @classmethod
  304. def get_workflow_tool_runtime(
  305. cls,
  306. tenant_id: str,
  307. app_id: str,
  308. node_id: str,
  309. workflow_tool: "ToolEntity",
  310. invoke_from: InvokeFrom = InvokeFrom.DEBUGGER,
  311. ) -> Tool:
  312. """
  313. get the workflow tool runtime
  314. """
  315. tool_runtime = cls.get_tool_runtime(
  316. provider_type=workflow_tool.provider_type,
  317. provider_id=workflow_tool.provider_id,
  318. tool_name=workflow_tool.tool_name,
  319. tenant_id=tenant_id,
  320. invoke_from=invoke_from,
  321. tool_invoke_from=ToolInvokeFrom.WORKFLOW,
  322. )
  323. runtime_parameters = {}
  324. parameters = tool_runtime.get_merged_runtime_parameters()
  325. for parameter in parameters:
  326. # save tool parameter to tool entity memory
  327. if parameter.form == ToolParameter.ToolParameterForm.FORM:
  328. value = parameter.init_frontend_parameter(workflow_tool.tool_configurations.get(parameter.name))
  329. runtime_parameters[parameter.name] = value
  330. # decrypt runtime parameters
  331. encryption_manager = ToolParameterConfigurationManager(
  332. tenant_id=tenant_id,
  333. tool_runtime=tool_runtime,
  334. provider_name=workflow_tool.provider_id,
  335. provider_type=workflow_tool.provider_type,
  336. identity_id=f"WORKFLOW.{app_id}.{node_id}",
  337. )
  338. if runtime_parameters:
  339. runtime_parameters = encryption_manager.decrypt_tool_parameters(runtime_parameters)
  340. tool_runtime.runtime.runtime_parameters.update(runtime_parameters)
  341. return tool_runtime
  342. @classmethod
  343. def get_tool_runtime_from_plugin(
  344. cls,
  345. tool_type: ToolProviderType,
  346. tenant_id: str,
  347. provider: str,
  348. tool_name: str,
  349. tool_parameters: dict[str, Any],
  350. ) -> Tool:
  351. """
  352. get tool runtime from plugin
  353. """
  354. tool_entity = cls.get_tool_runtime(
  355. provider_type=tool_type,
  356. provider_id=provider,
  357. tool_name=tool_name,
  358. tenant_id=tenant_id,
  359. invoke_from=InvokeFrom.SERVICE_API,
  360. tool_invoke_from=ToolInvokeFrom.PLUGIN,
  361. )
  362. runtime_parameters = {}
  363. parameters = tool_entity.get_merged_runtime_parameters()
  364. for parameter in parameters:
  365. if parameter.form == ToolParameter.ToolParameterForm.FORM:
  366. # save tool parameter to tool entity memory
  367. value = parameter.init_frontend_parameter(tool_parameters.get(parameter.name))
  368. runtime_parameters[parameter.name] = value
  369. tool_entity.runtime.runtime_parameters.update(runtime_parameters)
  370. return tool_entity
  371. @classmethod
  372. def get_hardcoded_provider_icon(cls, provider: str) -> tuple[str, str]:
  373. """
  374. get the absolute path of the icon of the hardcoded provider
  375. :param provider: the name of the provider
  376. :param tenant_id: the id of the tenant
  377. :return: the absolute path of the icon, the mime type of the icon
  378. """
  379. # get provider
  380. provider_controller = cls.get_hardcoded_provider(provider)
  381. absolute_path = path.join(
  382. path.dirname(path.realpath(__file__)),
  383. "builtin_tool",
  384. "providers",
  385. provider,
  386. "_assets",
  387. provider_controller.entity.identity.icon,
  388. )
  389. # check if the icon exists
  390. if not path.exists(absolute_path):
  391. raise ToolProviderNotFoundError(f"builtin provider {provider} icon not found")
  392. # get the mime type
  393. mime_type, _ = mimetypes.guess_type(absolute_path)
  394. mime_type = mime_type or "application/octet-stream"
  395. return absolute_path, mime_type
  396. @classmethod
  397. def list_hardcoded_providers(cls):
  398. # use cache first
  399. if cls._builtin_providers_loaded:
  400. yield from list(cls._hardcoded_providers.values())
  401. return
  402. with cls._builtin_provider_lock:
  403. if cls._builtin_providers_loaded:
  404. yield from list(cls._hardcoded_providers.values())
  405. return
  406. yield from cls._list_hardcoded_providers()
  407. @classmethod
  408. def list_plugin_providers(cls, tenant_id: str) -> list[PluginToolProviderController]:
  409. """
  410. list all the plugin providers
  411. """
  412. manager = PluginToolManager()
  413. provider_entities = manager.fetch_tool_providers(tenant_id)
  414. return [
  415. PluginToolProviderController(
  416. entity=provider.declaration,
  417. plugin_id=provider.plugin_id,
  418. plugin_unique_identifier=provider.plugin_unique_identifier,
  419. tenant_id=tenant_id,
  420. )
  421. for provider in provider_entities
  422. ]
  423. @classmethod
  424. def list_builtin_providers(
  425. cls, tenant_id: str
  426. ) -> Generator[BuiltinToolProviderController | PluginToolProviderController, None, None]:
  427. """
  428. list all the builtin providers
  429. """
  430. yield from cls.list_hardcoded_providers()
  431. # get plugin providers
  432. yield from cls.list_plugin_providers(tenant_id)
  433. @classmethod
  434. def _list_hardcoded_providers(cls) -> Generator[BuiltinToolProviderController, None, None]:
  435. """
  436. list all the builtin providers
  437. """
  438. for provider_path in listdir(path.join(path.dirname(path.realpath(__file__)), "builtin_tool", "providers")):
  439. if provider_path.startswith("__"):
  440. continue
  441. if path.isdir(path.join(path.dirname(path.realpath(__file__)), "builtin_tool", "providers", provider_path)):
  442. if provider_path.startswith("__"):
  443. continue
  444. # init provider
  445. try:
  446. provider_class = load_single_subclass_from_source(
  447. module_name=f"core.tools.builtin_tool.providers.{provider_path}.{provider_path}",
  448. script_path=path.join(
  449. path.dirname(path.realpath(__file__)),
  450. "builtin_tool",
  451. "providers",
  452. provider_path,
  453. f"{provider_path}.py",
  454. ),
  455. parent_type=BuiltinToolProviderController,
  456. )
  457. provider: BuiltinToolProviderController = provider_class()
  458. cls._hardcoded_providers[provider.entity.identity.name] = provider
  459. for tool in provider.get_tools():
  460. cls._builtin_tools_labels[tool.entity.identity.name] = tool.entity.identity.label
  461. yield provider
  462. except Exception:
  463. logger.exception(f"load builtin provider {provider}")
  464. continue
  465. # set builtin providers loaded
  466. cls._builtin_providers_loaded = True
  467. @classmethod
  468. def load_hardcoded_providers_cache(cls):
  469. for _ in cls.list_hardcoded_providers():
  470. pass
  471. @classmethod
  472. def clear_hardcoded_providers_cache(cls):
  473. cls._hardcoded_providers = {}
  474. cls._builtin_providers_loaded = False
  475. @classmethod
  476. def get_tool_label(cls, tool_name: str) -> Union[I18nObject, None]:
  477. """
  478. get the tool label
  479. :param tool_name: the name of the tool
  480. :return: the label of the tool
  481. """
  482. if len(cls._builtin_tools_labels) == 0:
  483. # init the builtin providers
  484. cls.load_hardcoded_providers_cache()
  485. if tool_name not in cls._builtin_tools_labels:
  486. return None
  487. return cls._builtin_tools_labels[tool_name]
  488. @classmethod
  489. def list_providers_from_api(
  490. cls, user_id: str, tenant_id: str, typ: ToolProviderTypeApiLiteral
  491. ) -> list[ToolProviderApiEntity]:
  492. result_providers: dict[str, ToolProviderApiEntity] = {}
  493. filters = []
  494. if not typ:
  495. filters.extend(["builtin", "api", "workflow"])
  496. else:
  497. filters.append(typ)
  498. with db.session.no_autoflush:
  499. if "builtin" in filters:
  500. # get builtin providers
  501. builtin_providers = cls.list_builtin_providers(tenant_id)
  502. # get db builtin providers
  503. db_builtin_providers: list[BuiltinToolProvider] = (
  504. db.session.query(BuiltinToolProvider).filter(BuiltinToolProvider.tenant_id == tenant_id).all()
  505. )
  506. # rewrite db_builtin_providers
  507. for db_provider in db_builtin_providers:
  508. tool_provider_id = str(ToolProviderID(db_provider.provider))
  509. db_provider.provider = tool_provider_id
  510. def find_db_builtin_provider(provider):
  511. return next((x for x in db_builtin_providers if x.provider == provider), None)
  512. # append builtin providers
  513. for provider in builtin_providers:
  514. # handle include, exclude
  515. if is_filtered(
  516. include_set=cast(set[str], dify_config.POSITION_TOOL_INCLUDES_SET),
  517. exclude_set=cast(set[str], dify_config.POSITION_TOOL_EXCLUDES_SET),
  518. data=provider,
  519. name_func=lambda x: x.identity.name,
  520. ):
  521. continue
  522. user_provider = ToolTransformService.builtin_provider_to_user_provider(
  523. provider_controller=provider,
  524. db_provider=find_db_builtin_provider(provider.entity.identity.name),
  525. decrypt_credentials=False,
  526. )
  527. if isinstance(provider, PluginToolProviderController):
  528. result_providers[f"plugin_provider.{user_provider.name}"] = user_provider
  529. else:
  530. result_providers[f"builtin_provider.{user_provider.name}"] = user_provider
  531. # get db api providers
  532. if "api" in filters:
  533. db_api_providers: list[ApiToolProvider] = (
  534. db.session.query(ApiToolProvider).filter(ApiToolProvider.tenant_id == tenant_id).all()
  535. )
  536. api_provider_controllers: list[dict[str, Any]] = [
  537. {"provider": provider, "controller": ToolTransformService.api_provider_to_controller(provider)}
  538. for provider in db_api_providers
  539. ]
  540. # get labels
  541. labels = ToolLabelManager.get_tools_labels([x["controller"] for x in api_provider_controllers])
  542. for api_provider_controller in api_provider_controllers:
  543. user_provider = ToolTransformService.api_provider_to_user_provider(
  544. provider_controller=api_provider_controller["controller"],
  545. db_provider=api_provider_controller["provider"],
  546. decrypt_credentials=False,
  547. labels=labels.get(api_provider_controller["controller"].provider_id, []),
  548. )
  549. result_providers[f"api_provider.{user_provider.name}"] = user_provider
  550. if "workflow" in filters:
  551. # get workflow providers
  552. workflow_providers: list[WorkflowToolProvider] = (
  553. db.session.query(WorkflowToolProvider).filter(WorkflowToolProvider.tenant_id == tenant_id).all()
  554. )
  555. workflow_provider_controllers: list[WorkflowToolProviderController] = []
  556. for provider in workflow_providers:
  557. try:
  558. workflow_provider_controllers.append(
  559. ToolTransformService.workflow_provider_to_controller(db_provider=provider)
  560. )
  561. except Exception:
  562. # app has been deleted
  563. pass
  564. labels = ToolLabelManager.get_tools_labels(
  565. [cast(ToolProviderController, controller) for controller in workflow_provider_controllers]
  566. )
  567. for provider_controller in workflow_provider_controllers:
  568. user_provider = ToolTransformService.workflow_provider_to_user_provider(
  569. provider_controller=provider_controller,
  570. labels=labels.get(provider_controller.provider_id, []),
  571. )
  572. result_providers[f"workflow_provider.{user_provider.name}"] = user_provider
  573. return BuiltinToolProviderSort.sort(list(result_providers.values()))
  574. @classmethod
  575. def get_api_provider_controller(
  576. cls, tenant_id: str, provider_id: str
  577. ) -> tuple[ApiToolProviderController, dict[str, Any]]:
  578. """
  579. get the api provider
  580. :param provider_name: the name of the provider
  581. :return: the provider controller, the credentials
  582. """
  583. provider: ApiToolProvider | None = (
  584. db.session.query(ApiToolProvider)
  585. .filter(
  586. ApiToolProvider.id == provider_id,
  587. ApiToolProvider.tenant_id == tenant_id,
  588. )
  589. .first()
  590. )
  591. if provider is None:
  592. raise ToolProviderNotFoundError(f"api provider {provider_id} not found")
  593. controller = ApiToolProviderController.from_db(
  594. provider,
  595. ApiProviderAuthType.API_KEY if provider.credentials["auth_type"] == "api_key" else ApiProviderAuthType.NONE,
  596. )
  597. controller.load_bundled_tools(provider.tools)
  598. return controller, provider.credentials
  599. @classmethod
  600. def user_get_api_provider(cls, provider: str, tenant_id: str) -> dict:
  601. """
  602. get api provider
  603. """
  604. """
  605. get tool provider
  606. """
  607. provider_name = provider
  608. provider_obj: ApiToolProvider | None = (
  609. db.session.query(ApiToolProvider)
  610. .filter(
  611. ApiToolProvider.tenant_id == tenant_id,
  612. ApiToolProvider.name == provider,
  613. )
  614. .first()
  615. )
  616. if provider_obj is None:
  617. raise ValueError(f"you have not added provider {provider_name}")
  618. try:
  619. credentials = json.loads(provider_obj.credentials_str) or {}
  620. except Exception:
  621. credentials = {}
  622. # package tool provider controller
  623. controller = ApiToolProviderController.from_db(
  624. provider_obj,
  625. ApiProviderAuthType.API_KEY if credentials["auth_type"] == "api_key" else ApiProviderAuthType.NONE,
  626. )
  627. # init tool configuration
  628. tool_configuration = ProviderConfigEncrypter(
  629. tenant_id=tenant_id,
  630. config=[x.to_basic_provider_config() for x in controller.get_credentials_schema()],
  631. provider_type=controller.provider_type.value,
  632. provider_identity=controller.entity.identity.name,
  633. )
  634. decrypted_credentials = tool_configuration.decrypt(credentials)
  635. masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
  636. try:
  637. icon = json.loads(provider_obj.icon)
  638. except Exception:
  639. icon = {"background": "#252525", "content": "\ud83d\ude01"}
  640. # add tool labels
  641. labels = ToolLabelManager.get_tool_labels(controller)
  642. return cast(
  643. dict,
  644. jsonable_encoder(
  645. {
  646. "schema_type": provider_obj.schema_type,
  647. "schema": provider_obj.schema,
  648. "tools": provider_obj.tools,
  649. "icon": icon,
  650. "description": provider_obj.description,
  651. "credentials": masked_credentials,
  652. "privacy_policy": provider_obj.privacy_policy,
  653. "custom_disclaimer": provider_obj.custom_disclaimer,
  654. "labels": labels,
  655. }
  656. ),
  657. )
  658. @classmethod
  659. def generate_builtin_tool_icon_url(cls, provider_id: str) -> str:
  660. return str(
  661. URL(dify_config.CONSOLE_API_URL or "/")
  662. / "console"
  663. / "api"
  664. / "workspaces"
  665. / "current"
  666. / "tool-provider"
  667. / "builtin"
  668. / provider_id
  669. / "icon"
  670. )
  671. @classmethod
  672. def generate_plugin_tool_icon_url(cls, tenant_id: str, filename: str) -> str:
  673. return str(
  674. URL(dify_config.CONSOLE_API_URL or "/")
  675. / "console"
  676. / "api"
  677. / "workspaces"
  678. / "current"
  679. / "plugin"
  680. / "icon"
  681. % {"tenant_id": tenant_id, "filename": filename}
  682. )
  683. @classmethod
  684. def generate_workflow_tool_icon_url(cls, tenant_id: str, provider_id: str) -> dict:
  685. try:
  686. workflow_provider: WorkflowToolProvider | None = (
  687. db.session.query(WorkflowToolProvider)
  688. .filter(WorkflowToolProvider.tenant_id == tenant_id, WorkflowToolProvider.id == provider_id)
  689. .first()
  690. )
  691. if workflow_provider is None:
  692. raise ToolProviderNotFoundError(f"workflow provider {provider_id} not found")
  693. icon: dict = json.loads(workflow_provider.icon)
  694. return icon
  695. except Exception:
  696. return {"background": "#252525", "content": "\ud83d\ude01"}
  697. @classmethod
  698. def generate_api_tool_icon_url(cls, tenant_id: str, provider_id: str) -> dict:
  699. try:
  700. api_provider: ApiToolProvider | None = (
  701. db.session.query(ApiToolProvider)
  702. .filter(ApiToolProvider.tenant_id == tenant_id, ApiToolProvider.id == provider_id)
  703. .first()
  704. )
  705. if api_provider is None:
  706. raise ToolProviderNotFoundError(f"api provider {provider_id} not found")
  707. icon: dict = json.loads(api_provider.icon)
  708. return icon
  709. except Exception:
  710. return {"background": "#252525", "content": "\ud83d\ude01"}
  711. @classmethod
  712. def get_tool_icon(
  713. cls,
  714. tenant_id: str,
  715. provider_type: ToolProviderType,
  716. provider_id: str,
  717. ) -> Union[str, dict]:
  718. """
  719. get the tool icon
  720. :param tenant_id: the id of the tenant
  721. :param provider_type: the type of the provider
  722. :param provider_id: the id of the provider
  723. :return:
  724. """
  725. provider_type = provider_type
  726. provider_id = provider_id
  727. if provider_type == ToolProviderType.BUILT_IN:
  728. provider = ToolManager.get_builtin_provider(provider_id, tenant_id)
  729. if isinstance(provider, PluginToolProviderController):
  730. try:
  731. return cls.generate_plugin_tool_icon_url(tenant_id, provider.entity.identity.icon)
  732. except Exception:
  733. return {"background": "#252525", "content": "\ud83d\ude01"}
  734. return cls.generate_builtin_tool_icon_url(provider_id)
  735. elif provider_type == ToolProviderType.API:
  736. return cls.generate_api_tool_icon_url(tenant_id, provider_id)
  737. elif provider_type == ToolProviderType.WORKFLOW:
  738. return cls.generate_workflow_tool_icon_url(tenant_id, provider_id)
  739. elif provider_type == ToolProviderType.PLUGIN:
  740. provider = ToolManager.get_builtin_provider(provider_id, tenant_id)
  741. if isinstance(provider, PluginToolProviderController):
  742. try:
  743. return cls.generate_plugin_tool_icon_url(tenant_id, provider.entity.identity.icon)
  744. except Exception:
  745. return {"background": "#252525", "content": "\ud83d\ude01"}
  746. raise ValueError(f"plugin provider {provider_id} not found")
  747. else:
  748. raise ValueError(f"provider type {provider_type} not found")
  749. ToolManager.load_hardcoded_providers_cache()