tool_manager.py 25 KB

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