tool_manager.py 28 KB

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