tool_manager.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. import json
  2. import logging
  3. import mimetypes
  4. from os import listdir, path
  5. from typing import Any, Union
  6. from core.callback_handler.agent_tool_callback_handler import DifyAgentCallbackHandler
  7. from core.entities.application_entities import AgentToolEntity
  8. from core.model_runtime.entities.message_entities import PromptMessage
  9. from core.provider_manager import ProviderManager
  10. from core.tools.entities.common_entities import I18nObject
  11. from core.tools.entities.constant import DEFAULT_PROVIDERS
  12. from core.tools.entities.tool_entities import (
  13. ApiProviderAuthType,
  14. ToolInvokeMessage,
  15. ToolParameter,
  16. ToolProviderCredentials,
  17. )
  18. from core.tools.entities.user_entities import UserToolProvider
  19. from core.tools.errors import ToolProviderNotFoundError
  20. from core.tools.provider.api_tool_provider import ApiBasedToolProviderController
  21. from core.tools.provider.app_tool_provider import AppBasedToolProviderEntity
  22. from core.tools.provider.builtin._positions import BuiltinToolProviderSort
  23. from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
  24. from core.tools.provider.model_tool_provider import ModelToolProviderController
  25. from core.tools.provider.tool_provider import ToolProviderController
  26. from core.tools.tool.api_tool import ApiTool
  27. from core.tools.tool.builtin_tool import BuiltinTool
  28. from core.tools.tool.tool import Tool
  29. from core.tools.utils.configuration import (
  30. ModelToolConfigurationManager,
  31. ToolConfigurationManager,
  32. ToolParameterConfigurationManager,
  33. )
  34. from core.tools.utils.encoder import serialize_base_model_dict
  35. from core.utils.module_import_helper import load_single_subclass_from_source
  36. from extensions.ext_database import db
  37. from models.tools import ApiToolProvider, BuiltinToolProvider
  38. logger = logging.getLogger(__name__)
  39. _builtin_providers = {}
  40. _builtin_tools_labels = {}
  41. class ToolManager:
  42. @staticmethod
  43. def invoke(
  44. provider: str,
  45. tool_id: str,
  46. tool_name: str,
  47. tool_parameters: dict[str, Any],
  48. credentials: dict[str, Any],
  49. prompt_messages: list[PromptMessage],
  50. ) -> list[ToolInvokeMessage]:
  51. """
  52. invoke the assistant
  53. :param provider: the name of the provider
  54. :param tool_id: the id of the tool
  55. :param tool_name: the name of the tool, defined in `get_tools`
  56. :param tool_parameters: the parameters of the tool
  57. :param credentials: the credentials of the tool
  58. :param prompt_messages: the prompt messages that the tool can use
  59. :return: the messages that the tool wants to send to the user
  60. """
  61. provider_entity: ToolProviderController = None
  62. if provider == DEFAULT_PROVIDERS.API_BASED:
  63. provider_entity = ApiBasedToolProviderController()
  64. elif provider == DEFAULT_PROVIDERS.APP_BASED:
  65. provider_entity = AppBasedToolProviderEntity()
  66. if provider_entity is None:
  67. # fetch the provider from .provider.builtin
  68. provider_class = load_single_subclass_from_source(
  69. module_name=f'core.tools.provider.builtin.{provider}.{provider}',
  70. script_path=path.join(path.dirname(path.realpath(__file__)), 'builtin', provider, f'{provider}.py'),
  71. parent_type=ToolProviderController)
  72. provider_entity = provider_class()
  73. return provider_entity.invoke(tool_id, tool_name, tool_parameters, credentials, prompt_messages)
  74. @staticmethod
  75. def get_builtin_provider(provider: str) -> BuiltinToolProviderController:
  76. global _builtin_providers
  77. """
  78. get the builtin provider
  79. :param provider: the name of the provider
  80. :return: the provider
  81. """
  82. if len(_builtin_providers) == 0:
  83. # init the builtin providers
  84. ToolManager.list_builtin_providers()
  85. if provider not in _builtin_providers:
  86. raise ToolProviderNotFoundError(f'builtin provider {provider} not found')
  87. return _builtin_providers[provider]
  88. @staticmethod
  89. def get_builtin_tool(provider: str, tool_name: str) -> BuiltinTool:
  90. """
  91. get the builtin tool
  92. :param provider: the name of the provider
  93. :param tool_name: the name of the tool
  94. :return: the provider, the tool
  95. """
  96. provider_controller = ToolManager.get_builtin_provider(provider)
  97. tool = provider_controller.get_tool(tool_name)
  98. return tool
  99. @staticmethod
  100. def get_tool(provider_type: str, provider_id: str, tool_name: str, tenant_id: str = None) \
  101. -> Union[BuiltinTool, ApiTool]:
  102. """
  103. get the tool
  104. :param provider_type: the type of the provider
  105. :param provider_name: the name of the provider
  106. :param tool_name: the name of the tool
  107. :return: the tool
  108. """
  109. if provider_type == 'builtin':
  110. return ToolManager.get_builtin_tool(provider_id, tool_name)
  111. elif provider_type == 'api':
  112. if tenant_id is None:
  113. raise ValueError('tenant id is required for api provider')
  114. api_provider, _ = ToolManager.get_api_provider_controller(tenant_id, provider_id)
  115. return api_provider.get_tool(tool_name)
  116. elif provider_type == 'app':
  117. raise NotImplementedError('app provider not implemented')
  118. else:
  119. raise ToolProviderNotFoundError(f'provider type {provider_type} not found')
  120. @staticmethod
  121. def get_tool_runtime(provider_type: str, provider_name: str, tool_name: str, tenant_id: str,
  122. agent_callback: DifyAgentCallbackHandler = None) \
  123. -> Union[BuiltinTool, ApiTool]:
  124. """
  125. get the tool runtime
  126. :param provider_type: the type of the provider
  127. :param provider_name: the name of the provider
  128. :param tool_name: the name of the tool
  129. :return: the tool
  130. """
  131. if provider_type == 'builtin':
  132. builtin_tool = ToolManager.get_builtin_tool(provider_name, tool_name)
  133. # check if the builtin tool need credentials
  134. provider_controller = ToolManager.get_builtin_provider(provider_name)
  135. if not provider_controller.need_credentials:
  136. return builtin_tool.fork_tool_runtime(meta={
  137. 'tenant_id': tenant_id,
  138. 'credentials': {},
  139. }, agent_callback=agent_callback)
  140. # get credentials
  141. builtin_provider: BuiltinToolProvider = db.session.query(BuiltinToolProvider).filter(
  142. BuiltinToolProvider.tenant_id == tenant_id,
  143. BuiltinToolProvider.provider == provider_name,
  144. ).first()
  145. if builtin_provider is None:
  146. raise ToolProviderNotFoundError(f'builtin provider {provider_name} not found')
  147. # decrypt the credentials
  148. credentials = builtin_provider.credentials
  149. controller = ToolManager.get_builtin_provider(provider_name)
  150. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=controller)
  151. decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
  152. return builtin_tool.fork_tool_runtime(meta={
  153. 'tenant_id': tenant_id,
  154. 'credentials': decrypted_credentials,
  155. 'runtime_parameters': {}
  156. }, agent_callback=agent_callback)
  157. elif provider_type == 'api':
  158. if tenant_id is None:
  159. raise ValueError('tenant id is required for api provider')
  160. api_provider, credentials = ToolManager.get_api_provider_controller(tenant_id, provider_name)
  161. # decrypt the credentials
  162. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=api_provider)
  163. decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
  164. return api_provider.get_tool(tool_name).fork_tool_runtime(meta={
  165. 'tenant_id': tenant_id,
  166. 'credentials': decrypted_credentials,
  167. })
  168. elif provider_type == 'model':
  169. if tenant_id is None:
  170. raise ValueError('tenant id is required for model provider')
  171. # get model provider
  172. model_provider = ToolManager.get_model_provider(tenant_id, provider_name)
  173. # get tool
  174. model_tool = model_provider.get_tool(tool_name)
  175. return model_tool.fork_tool_runtime(meta={
  176. 'tenant_id': tenant_id,
  177. 'credentials': model_tool.model_configuration['model_instance'].credentials
  178. })
  179. elif provider_type == 'app':
  180. raise NotImplementedError('app provider not implemented')
  181. else:
  182. raise ToolProviderNotFoundError(f'provider type {provider_type} not found')
  183. @staticmethod
  184. def get_agent_tool_runtime(tenant_id: str, agent_tool: AgentToolEntity, agent_callback: DifyAgentCallbackHandler) -> Tool:
  185. """
  186. get the agent tool runtime
  187. """
  188. tool_entity = ToolManager.get_tool_runtime(
  189. provider_type=agent_tool.provider_type, provider_name=agent_tool.provider_id, tool_name=agent_tool.tool_name,
  190. tenant_id=tenant_id,
  191. agent_callback=agent_callback
  192. )
  193. runtime_parameters = {}
  194. parameters = tool_entity.get_all_runtime_parameters()
  195. for parameter in parameters:
  196. if parameter.form == ToolParameter.ToolParameterForm.FORM:
  197. # get tool parameter from form
  198. tool_parameter_config = agent_tool.tool_parameters.get(parameter.name)
  199. if not tool_parameter_config:
  200. # get default value
  201. tool_parameter_config = parameter.default
  202. if not tool_parameter_config and parameter.required:
  203. raise ValueError(f"tool parameter {parameter.name} not found in tool config")
  204. if parameter.type == ToolParameter.ToolParameterType.SELECT:
  205. # check if tool_parameter_config in options
  206. options = list(map(lambda x: x.value, parameter.options))
  207. if tool_parameter_config not in options:
  208. raise ValueError(f"tool parameter {parameter.name} value {tool_parameter_config} not in options {options}")
  209. # convert tool parameter config to correct type
  210. try:
  211. if parameter.type == ToolParameter.ToolParameterType.NUMBER:
  212. # check if tool parameter is integer
  213. if isinstance(tool_parameter_config, int):
  214. tool_parameter_config = tool_parameter_config
  215. elif isinstance(tool_parameter_config, float):
  216. tool_parameter_config = tool_parameter_config
  217. elif isinstance(tool_parameter_config, str):
  218. if '.' in tool_parameter_config:
  219. tool_parameter_config = float(tool_parameter_config)
  220. else:
  221. tool_parameter_config = int(tool_parameter_config)
  222. elif parameter.type == ToolParameter.ToolParameterType.BOOLEAN:
  223. tool_parameter_config = bool(tool_parameter_config)
  224. elif parameter.type not in [ToolParameter.ToolParameterType.SELECT, ToolParameter.ToolParameterType.STRING]:
  225. tool_parameter_config = str(tool_parameter_config)
  226. elif parameter.type == ToolParameter.ToolParameterType:
  227. tool_parameter_config = str(tool_parameter_config)
  228. except Exception as e:
  229. raise ValueError(f"tool parameter {parameter.name} value {tool_parameter_config} is not correct type")
  230. # save tool parameter to tool entity memory
  231. runtime_parameters[parameter.name] = tool_parameter_config
  232. # decrypt runtime parameters
  233. encryption_manager = ToolParameterConfigurationManager(
  234. tenant_id=tenant_id,
  235. tool_runtime=tool_entity,
  236. provider_name=agent_tool.provider_id,
  237. provider_type=agent_tool.provider_type,
  238. )
  239. runtime_parameters = encryption_manager.decrypt_tool_parameters(runtime_parameters)
  240. tool_entity.runtime.runtime_parameters.update(runtime_parameters)
  241. return tool_entity
  242. @staticmethod
  243. def get_builtin_provider_icon(provider: str) -> tuple[str, str]:
  244. """
  245. get the absolute path of the icon of the builtin provider
  246. :param provider: the name of the provider
  247. :return: the absolute path of the icon, the mime type of the icon
  248. """
  249. # get provider
  250. provider_controller = ToolManager.get_builtin_provider(provider)
  251. absolute_path = path.join(path.dirname(path.realpath(__file__)), 'provider', 'builtin', provider, '_assets', provider_controller.identity.icon)
  252. # check if the icon exists
  253. if not path.exists(absolute_path):
  254. raise ToolProviderNotFoundError(f'builtin provider {provider} icon not found')
  255. # get the mime type
  256. mime_type, _ = mimetypes.guess_type(absolute_path)
  257. mime_type = mime_type or 'application/octet-stream'
  258. return absolute_path, mime_type
  259. @staticmethod
  260. def list_builtin_providers() -> list[BuiltinToolProviderController]:
  261. global _builtin_providers
  262. # use cache first
  263. if len(_builtin_providers) > 0:
  264. return list(_builtin_providers.values())
  265. builtin_providers: list[BuiltinToolProviderController] = []
  266. for provider in listdir(path.join(path.dirname(path.realpath(__file__)), 'provider', 'builtin')):
  267. if provider.startswith('__'):
  268. continue
  269. if path.isdir(path.join(path.dirname(path.realpath(__file__)), 'provider', 'builtin', provider)):
  270. if provider.startswith('__'):
  271. continue
  272. # init provider
  273. provider_class = load_single_subclass_from_source(
  274. module_name=f'core.tools.provider.builtin.{provider}.{provider}',
  275. script_path=path.join(path.dirname(path.realpath(__file__)),
  276. 'provider', 'builtin', provider, f'{provider}.py'),
  277. parent_type=BuiltinToolProviderController)
  278. builtin_providers.append(provider_class())
  279. # cache the builtin providers
  280. for provider in builtin_providers:
  281. _builtin_providers[provider.identity.name] = provider
  282. for tool in provider.get_tools():
  283. _builtin_tools_labels[tool.identity.name] = tool.identity.label
  284. return builtin_providers
  285. @staticmethod
  286. def list_model_providers(tenant_id: str = None) -> list[ModelToolProviderController]:
  287. """
  288. list all the model providers
  289. :return: the list of the model providers
  290. """
  291. tenant_id = tenant_id or 'ffffffff-ffff-ffff-ffff-ffffffffffff'
  292. # get configurations
  293. model_configurations = ModelToolConfigurationManager.get_all_configuration()
  294. # get all providers
  295. provider_manager = ProviderManager()
  296. configurations = provider_manager.get_configurations(tenant_id).values()
  297. # get model providers
  298. model_providers: list[ModelToolProviderController] = []
  299. for configuration in configurations:
  300. # all the model tool should be configurated
  301. if configuration.provider.provider not in model_configurations:
  302. continue
  303. if not ModelToolProviderController.is_configuration_valid(configuration):
  304. continue
  305. model_providers.append(ModelToolProviderController.from_db(configuration))
  306. return model_providers
  307. @staticmethod
  308. def get_model_provider(tenant_id: str, provider_name: str) -> ModelToolProviderController:
  309. """
  310. get the model provider
  311. :param provider_name: the name of the provider
  312. :return: the provider
  313. """
  314. # get configurations
  315. provider_manager = ProviderManager()
  316. configurations = provider_manager.get_configurations(tenant_id)
  317. configuration = configurations.get(provider_name)
  318. if configuration is None:
  319. raise ToolProviderNotFoundError(f'model provider {provider_name} not found')
  320. return ModelToolProviderController.from_db(configuration)
  321. @staticmethod
  322. def get_tool_label(tool_name: str) -> Union[I18nObject, None]:
  323. """
  324. get the tool label
  325. :param tool_name: the name of the tool
  326. :return: the label of the tool
  327. """
  328. global _builtin_tools_labels
  329. if len(_builtin_tools_labels) == 0:
  330. # init the builtin providers
  331. ToolManager.list_builtin_providers()
  332. if tool_name not in _builtin_tools_labels:
  333. return None
  334. return _builtin_tools_labels[tool_name]
  335. @staticmethod
  336. def user_list_providers(
  337. user_id: str,
  338. tenant_id: str,
  339. ) -> list[UserToolProvider]:
  340. result_providers: dict[str, UserToolProvider] = {}
  341. # get builtin providers
  342. builtin_providers = ToolManager.list_builtin_providers()
  343. # append builtin providers
  344. for provider in builtin_providers:
  345. result_providers[provider.identity.name] = UserToolProvider(
  346. id=provider.identity.name,
  347. author=provider.identity.author,
  348. name=provider.identity.name,
  349. description=I18nObject(
  350. en_US=provider.identity.description.en_US,
  351. zh_Hans=provider.identity.description.zh_Hans,
  352. ),
  353. icon=provider.identity.icon,
  354. label=I18nObject(
  355. en_US=provider.identity.label.en_US,
  356. zh_Hans=provider.identity.label.zh_Hans,
  357. ),
  358. type=UserToolProvider.ProviderType.BUILTIN,
  359. team_credentials={},
  360. is_team_authorization=False,
  361. )
  362. # get credentials schema
  363. schema = provider.get_credentials_schema()
  364. for name, value in schema.items():
  365. result_providers[provider.identity.name].team_credentials[name] = \
  366. ToolProviderCredentials.CredentialsType.default(value.type)
  367. # check if the provider need credentials
  368. if not provider.need_credentials:
  369. result_providers[provider.identity.name].is_team_authorization = True
  370. result_providers[provider.identity.name].allow_delete = False
  371. # get db builtin providers
  372. db_builtin_providers: list[BuiltinToolProvider] = db.session.query(BuiltinToolProvider). \
  373. filter(BuiltinToolProvider.tenant_id == tenant_id).all()
  374. for db_builtin_provider in db_builtin_providers:
  375. # add provider into providers
  376. credentials = db_builtin_provider.credentials
  377. provider_name = db_builtin_provider.provider
  378. result_providers[provider_name].is_team_authorization = True
  379. # package builtin tool provider controller
  380. controller = ToolManager.get_builtin_provider(provider_name)
  381. # init tool configuration
  382. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=controller)
  383. # decrypt the credentials and mask the credentials
  384. decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials=credentials)
  385. masked_credentials = tool_configuration.mask_tool_credentials(credentials=decrypted_credentials)
  386. result_providers[provider_name].team_credentials = masked_credentials
  387. # get model tool providers
  388. model_providers = ToolManager.list_model_providers(tenant_id=tenant_id)
  389. # append model providers
  390. for provider in model_providers:
  391. result_providers[f'model_provider.{provider.identity.name}'] = UserToolProvider(
  392. id=provider.identity.name,
  393. author=provider.identity.author,
  394. name=provider.identity.name,
  395. description=I18nObject(
  396. en_US=provider.identity.description.en_US,
  397. zh_Hans=provider.identity.description.zh_Hans,
  398. ),
  399. icon=provider.identity.icon,
  400. label=I18nObject(
  401. en_US=provider.identity.label.en_US,
  402. zh_Hans=provider.identity.label.zh_Hans,
  403. ),
  404. type=UserToolProvider.ProviderType.MODEL,
  405. team_credentials={},
  406. is_team_authorization=provider.is_active,
  407. )
  408. # get db api providers
  409. db_api_providers: list[ApiToolProvider] = db.session.query(ApiToolProvider). \
  410. filter(ApiToolProvider.tenant_id == tenant_id).all()
  411. for db_api_provider in db_api_providers:
  412. username = 'Anonymous'
  413. try:
  414. username = db_api_provider.user.name
  415. except Exception as e:
  416. logger.error(f'failed to get user name for api provider {db_api_provider.id}: {str(e)}')
  417. # add provider into providers
  418. credentials = db_api_provider.credentials
  419. provider_name = db_api_provider.name
  420. result_providers[provider_name] = UserToolProvider(
  421. id=db_api_provider.id,
  422. author=username,
  423. name=db_api_provider.name,
  424. description=I18nObject(
  425. en_US=db_api_provider.description,
  426. zh_Hans=db_api_provider.description,
  427. ),
  428. icon=db_api_provider.icon,
  429. label=I18nObject(
  430. en_US=db_api_provider.name,
  431. zh_Hans=db_api_provider.name,
  432. ),
  433. type=UserToolProvider.ProviderType.API,
  434. team_credentials={},
  435. is_team_authorization=True,
  436. )
  437. # package tool provider controller
  438. controller = ApiBasedToolProviderController.from_db(
  439. db_provider=db_api_provider,
  440. auth_type=ApiProviderAuthType.API_KEY if db_api_provider.credentials['auth_type'] == 'api_key' else ApiProviderAuthType.NONE
  441. )
  442. # init tool configuration
  443. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=controller)
  444. # decrypt the credentials and mask the credentials
  445. decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials=credentials)
  446. masked_credentials = tool_configuration.mask_tool_credentials(credentials=decrypted_credentials)
  447. result_providers[provider_name].team_credentials = masked_credentials
  448. return BuiltinToolProviderSort.sort(list(result_providers.values()))
  449. @staticmethod
  450. def get_api_provider_controller(tenant_id: str, provider_id: str) -> tuple[ApiBasedToolProviderController, dict[str, Any]]:
  451. """
  452. get the api provider
  453. :param provider_name: the name of the provider
  454. :return: the provider controller, the credentials
  455. """
  456. provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  457. ApiToolProvider.id == provider_id,
  458. ApiToolProvider.tenant_id == tenant_id,
  459. ).first()
  460. if provider is None:
  461. raise ToolProviderNotFoundError(f'api provider {provider_id} not found')
  462. controller = ApiBasedToolProviderController.from_db(
  463. provider, ApiProviderAuthType.API_KEY if provider.credentials['auth_type'] == 'api_key' else ApiProviderAuthType.NONE
  464. )
  465. controller.load_bundled_tools(provider.tools)
  466. return controller, provider.credentials
  467. @staticmethod
  468. def user_get_api_provider(provider: str, tenant_id: str) -> dict:
  469. """
  470. get api provider
  471. """
  472. """
  473. get tool provider
  474. """
  475. provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  476. ApiToolProvider.tenant_id == tenant_id,
  477. ApiToolProvider.name == provider,
  478. ).first()
  479. if provider is None:
  480. raise ValueError(f'you have not added provider {provider}')
  481. try:
  482. credentials = json.loads(provider.credentials_str) or {}
  483. except:
  484. credentials = {}
  485. # package tool provider controller
  486. controller = ApiBasedToolProviderController.from_db(
  487. provider, ApiProviderAuthType.API_KEY if credentials['auth_type'] == 'api_key' else ApiProviderAuthType.NONE
  488. )
  489. # init tool configuration
  490. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=controller)
  491. decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
  492. masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
  493. try:
  494. icon = json.loads(provider.icon)
  495. except:
  496. icon = {
  497. "background": "#252525",
  498. "content": "\ud83d\ude01"
  499. }
  500. return json.loads(serialize_base_model_dict({
  501. 'schema_type': provider.schema_type,
  502. 'schema': provider.schema,
  503. 'tools': provider.tools,
  504. 'icon': icon,
  505. 'description': provider.description,
  506. 'credentials': masked_credentials,
  507. 'privacy_policy': provider.privacy_policy
  508. }))