tools_manage_service.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. import json
  2. import logging
  3. from httpx import get
  4. from core.model_runtime.utils.encoders import jsonable_encoder
  5. from core.tools.entities.common_entities import I18nObject
  6. from core.tools.entities.tool_bundle import ApiBasedToolBundle
  7. from core.tools.entities.tool_entities import (
  8. ApiProviderAuthType,
  9. ApiProviderSchemaType,
  10. ToolCredentialsOption,
  11. ToolProviderCredentials,
  12. )
  13. from core.tools.entities.user_entities import UserTool, UserToolProvider
  14. from core.tools.errors import ToolNotFoundError, ToolProviderCredentialValidationError, ToolProviderNotFoundError
  15. from core.tools.provider.api_tool_provider import ApiBasedToolProviderController
  16. from core.tools.provider.builtin._positions import BuiltinToolProviderSort
  17. from core.tools.provider.tool_provider import ToolProviderController
  18. from core.tools.tool_manager import ToolManager
  19. from core.tools.utils.configuration import ToolConfigurationManager
  20. from core.tools.utils.parser import ApiBasedToolSchemaParser
  21. from extensions.ext_database import db
  22. from models.tools import ApiToolProvider, BuiltinToolProvider
  23. from services.model_provider_service import ModelProviderService
  24. from services.tools_transform_service import ToolTransformService
  25. logger = logging.getLogger(__name__)
  26. class ToolManageService:
  27. @staticmethod
  28. def list_tool_providers(user_id: str, tenant_id: str):
  29. """
  30. list tool providers
  31. :return: the list of tool providers
  32. """
  33. providers = ToolManager.user_list_providers(
  34. user_id, tenant_id
  35. )
  36. # add icon
  37. for provider in providers:
  38. ToolTransformService.repack_provider(provider)
  39. result = [provider.to_dict() for provider in providers]
  40. return result
  41. @staticmethod
  42. def list_builtin_tool_provider_tools(
  43. user_id: str, tenant_id: str, provider: str
  44. ) -> list[UserTool]:
  45. """
  46. list builtin tool provider tools
  47. """
  48. provider_controller: ToolProviderController = ToolManager.get_builtin_provider(provider)
  49. tools = provider_controller.get_tools()
  50. tool_provider_configurations = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  51. # check if user has added the provider
  52. builtin_provider: BuiltinToolProvider = db.session.query(BuiltinToolProvider).filter(
  53. BuiltinToolProvider.tenant_id == tenant_id,
  54. BuiltinToolProvider.provider == provider,
  55. ).first()
  56. credentials = {}
  57. if builtin_provider is not None:
  58. # get credentials
  59. credentials = builtin_provider.credentials
  60. credentials = tool_provider_configurations.decrypt_tool_credentials(credentials)
  61. result = []
  62. for tool in tools:
  63. result.append(ToolTransformService.tool_to_user_tool(
  64. tool=tool, credentials=credentials, tenant_id=tenant_id
  65. ))
  66. return result
  67. @staticmethod
  68. def list_builtin_provider_credentials_schema(
  69. provider_name
  70. ):
  71. """
  72. list builtin provider credentials schema
  73. :return: the list of tool providers
  74. """
  75. provider = ToolManager.get_builtin_provider(provider_name)
  76. return jsonable_encoder([
  77. v for _, v in (provider.credentials_schema or {}).items()
  78. ])
  79. @staticmethod
  80. def parser_api_schema(schema: str) -> list[ApiBasedToolBundle]:
  81. """
  82. parse api schema to tool bundle
  83. """
  84. try:
  85. warnings = {}
  86. try:
  87. tool_bundles, schema_type = ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema, warning=warnings)
  88. except Exception as e:
  89. raise ValueError(f'invalid schema: {str(e)}')
  90. credentials_schema = [
  91. ToolProviderCredentials(
  92. name='auth_type',
  93. type=ToolProviderCredentials.CredentialsType.SELECT,
  94. required=True,
  95. default='none',
  96. options=[
  97. ToolCredentialsOption(value='none', label=I18nObject(
  98. en_US='None',
  99. zh_Hans='无'
  100. )),
  101. ToolCredentialsOption(value='api_key', label=I18nObject(
  102. en_US='Api Key',
  103. zh_Hans='Api Key'
  104. )),
  105. ],
  106. placeholder=I18nObject(
  107. en_US='Select auth type',
  108. zh_Hans='选择认证方式'
  109. )
  110. ),
  111. ToolProviderCredentials(
  112. name='api_key_header',
  113. type=ToolProviderCredentials.CredentialsType.TEXT_INPUT,
  114. required=False,
  115. placeholder=I18nObject(
  116. en_US='Enter api key header',
  117. zh_Hans='输入 api key header,如:X-API-KEY'
  118. ),
  119. default='api_key',
  120. help=I18nObject(
  121. en_US='HTTP header name for api key',
  122. zh_Hans='HTTP 头部字段名,用于传递 api key'
  123. )
  124. ),
  125. ToolProviderCredentials(
  126. name='api_key_value',
  127. type=ToolProviderCredentials.CredentialsType.TEXT_INPUT,
  128. required=False,
  129. placeholder=I18nObject(
  130. en_US='Enter api key',
  131. zh_Hans='输入 api key'
  132. ),
  133. default=''
  134. ),
  135. ]
  136. return jsonable_encoder({
  137. 'schema_type': schema_type,
  138. 'parameters_schema': tool_bundles,
  139. 'credentials_schema': credentials_schema,
  140. 'warning': warnings
  141. })
  142. except Exception as e:
  143. raise ValueError(f'invalid schema: {str(e)}')
  144. @staticmethod
  145. def convert_schema_to_tool_bundles(schema: str, extra_info: dict = None) -> list[ApiBasedToolBundle]:
  146. """
  147. convert schema to tool bundles
  148. :return: the list of tool bundles, description
  149. """
  150. try:
  151. tool_bundles = ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema, extra_info=extra_info)
  152. return tool_bundles
  153. except Exception as e:
  154. raise ValueError(f'invalid schema: {str(e)}')
  155. @staticmethod
  156. def create_api_tool_provider(
  157. user_id: str, tenant_id: str, provider_name: str, icon: dict, credentials: dict,
  158. schema_type: str, schema: str, privacy_policy: str, custom_disclaimer: str
  159. ):
  160. """
  161. create api tool provider
  162. """
  163. if schema_type not in [member.value for member in ApiProviderSchemaType]:
  164. raise ValueError(f'invalid schema type {schema}')
  165. # check if the provider exists
  166. provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  167. ApiToolProvider.tenant_id == tenant_id,
  168. ApiToolProvider.name == provider_name,
  169. ).first()
  170. if provider is not None:
  171. raise ValueError(f'provider {provider_name} already exists')
  172. # parse openapi to tool bundle
  173. extra_info = {}
  174. # extra info like description will be set here
  175. tool_bundles, schema_type = ToolManageService.convert_schema_to_tool_bundles(schema, extra_info)
  176. if len(tool_bundles) > 100:
  177. raise ValueError('the number of apis should be less than 100')
  178. # create db provider
  179. db_provider = ApiToolProvider(
  180. tenant_id=tenant_id,
  181. user_id=user_id,
  182. name=provider_name,
  183. icon=json.dumps(icon),
  184. schema=schema,
  185. description=extra_info.get('description', ''),
  186. schema_type_str=schema_type,
  187. tools_str=json.dumps(jsonable_encoder(tool_bundles)),
  188. credentials_str={},
  189. privacy_policy=privacy_policy,
  190. custom_disclaimer=custom_disclaimer
  191. )
  192. if 'auth_type' not in credentials:
  193. raise ValueError('auth_type is required')
  194. # get auth type, none or api key
  195. auth_type = ApiProviderAuthType.value_of(credentials['auth_type'])
  196. # create provider entity
  197. provider_controller = ApiBasedToolProviderController.from_db(db_provider, auth_type)
  198. # load tools into provider entity
  199. provider_controller.load_bundled_tools(tool_bundles)
  200. # encrypt credentials
  201. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  202. encrypted_credentials = tool_configuration.encrypt_tool_credentials(credentials)
  203. db_provider.credentials_str = json.dumps(encrypted_credentials)
  204. db.session.add(db_provider)
  205. db.session.commit()
  206. return { 'result': 'success' }
  207. @staticmethod
  208. def get_api_tool_provider_remote_schema(
  209. user_id: str, tenant_id: str, url: str
  210. ):
  211. """
  212. get api tool provider remote schema
  213. """
  214. headers = {
  215. "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0",
  216. "Accept": "*/*",
  217. }
  218. try:
  219. response = get(url, headers=headers, timeout=10)
  220. if response.status_code != 200:
  221. raise ValueError(f'Got status code {response.status_code}')
  222. schema = response.text
  223. # try to parse schema, avoid SSRF attack
  224. ToolManageService.parser_api_schema(schema)
  225. except Exception as e:
  226. logger.error(f"parse api schema error: {str(e)}")
  227. raise ValueError('invalid schema, please check the url you provided')
  228. return {
  229. 'schema': schema
  230. }
  231. @staticmethod
  232. def list_api_tool_provider_tools(
  233. user_id: str, tenant_id: str, provider: str
  234. ) -> list[UserTool]:
  235. """
  236. list api tool provider tools
  237. """
  238. provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  239. ApiToolProvider.tenant_id == tenant_id,
  240. ApiToolProvider.name == provider,
  241. ).first()
  242. if provider is None:
  243. raise ValueError(f'you have not added provider {provider}')
  244. return [
  245. ToolTransformService.tool_to_user_tool(tool_bundle) for tool_bundle in provider.tools
  246. ]
  247. @staticmethod
  248. def update_builtin_tool_provider(
  249. user_id: str, tenant_id: str, provider_name: str, credentials: dict
  250. ):
  251. """
  252. update builtin tool provider
  253. """
  254. # get if the provider exists
  255. provider: BuiltinToolProvider = db.session.query(BuiltinToolProvider).filter(
  256. BuiltinToolProvider.tenant_id == tenant_id,
  257. BuiltinToolProvider.provider == provider_name,
  258. ).first()
  259. try:
  260. # get provider
  261. provider_controller = ToolManager.get_builtin_provider(provider_name)
  262. if not provider_controller.need_credentials:
  263. raise ValueError(f'provider {provider_name} does not need credentials')
  264. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  265. # get original credentials if exists
  266. if provider is not None:
  267. original_credentials = tool_configuration.decrypt_tool_credentials(provider.credentials)
  268. masked_credentials = tool_configuration.mask_tool_credentials(original_credentials)
  269. # check if the credential has changed, save the original credential
  270. for name, value in credentials.items():
  271. if name in masked_credentials and value == masked_credentials[name]:
  272. credentials[name] = original_credentials[name]
  273. # validate credentials
  274. provider_controller.validate_credentials(credentials)
  275. # encrypt credentials
  276. credentials = tool_configuration.encrypt_tool_credentials(credentials)
  277. except (ToolProviderNotFoundError, ToolNotFoundError, ToolProviderCredentialValidationError) as e:
  278. raise ValueError(str(e))
  279. if provider is None:
  280. # create provider
  281. provider = BuiltinToolProvider(
  282. tenant_id=tenant_id,
  283. user_id=user_id,
  284. provider=provider_name,
  285. encrypted_credentials=json.dumps(credentials),
  286. )
  287. db.session.add(provider)
  288. db.session.commit()
  289. else:
  290. provider.encrypted_credentials = json.dumps(credentials)
  291. db.session.add(provider)
  292. db.session.commit()
  293. # delete cache
  294. tool_configuration.delete_tool_credentials_cache()
  295. return { 'result': 'success' }
  296. @staticmethod
  297. def get_builtin_tool_provider_credentials(
  298. user_id: str, tenant_id: str, provider: str
  299. ):
  300. """
  301. get builtin tool provider credentials
  302. """
  303. provider: BuiltinToolProvider = db.session.query(BuiltinToolProvider).filter(
  304. BuiltinToolProvider.tenant_id == tenant_id,
  305. BuiltinToolProvider.provider == provider,
  306. ).first()
  307. if provider is None:
  308. return {}
  309. provider_controller = ToolManager.get_builtin_provider(provider.provider)
  310. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  311. credentials = tool_configuration.decrypt_tool_credentials(provider.credentials)
  312. credentials = tool_configuration.mask_tool_credentials(credentials)
  313. return credentials
  314. @staticmethod
  315. def update_api_tool_provider(
  316. user_id: str, tenant_id: str, provider_name: str, original_provider: str, icon: dict, credentials: dict,
  317. schema_type: str, schema: str, privacy_policy: str, custom_disclaimer: str
  318. ):
  319. """
  320. update api tool provider
  321. """
  322. if schema_type not in [member.value for member in ApiProviderSchemaType]:
  323. raise ValueError(f'invalid schema type {schema}')
  324. # check if the provider exists
  325. provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  326. ApiToolProvider.tenant_id == tenant_id,
  327. ApiToolProvider.name == original_provider,
  328. ).first()
  329. if provider is None:
  330. raise ValueError(f'api provider {provider_name} does not exists')
  331. # parse openapi to tool bundle
  332. extra_info = {}
  333. # extra info like description will be set here
  334. tool_bundles, schema_type = ToolManageService.convert_schema_to_tool_bundles(schema, extra_info)
  335. # update db provider
  336. provider.name = provider_name
  337. provider.icon = json.dumps(icon)
  338. provider.schema = schema
  339. provider.description = extra_info.get('description', '')
  340. provider.schema_type_str = ApiProviderSchemaType.OPENAPI.value
  341. provider.tools_str = json.dumps(jsonable_encoder(tool_bundles))
  342. provider.privacy_policy = privacy_policy
  343. provider.custom_disclaimer = custom_disclaimer
  344. if 'auth_type' not in credentials:
  345. raise ValueError('auth_type is required')
  346. # get auth type, none or api key
  347. auth_type = ApiProviderAuthType.value_of(credentials['auth_type'])
  348. # create provider entity
  349. provider_controller = ApiBasedToolProviderController.from_db(provider, auth_type)
  350. # load tools into provider entity
  351. provider_controller.load_bundled_tools(tool_bundles)
  352. # get original credentials if exists
  353. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  354. original_credentials = tool_configuration.decrypt_tool_credentials(provider.credentials)
  355. masked_credentials = tool_configuration.mask_tool_credentials(original_credentials)
  356. # check if the credential has changed, save the original credential
  357. for name, value in credentials.items():
  358. if name in masked_credentials and value == masked_credentials[name]:
  359. credentials[name] = original_credentials[name]
  360. credentials = tool_configuration.encrypt_tool_credentials(credentials)
  361. provider.credentials_str = json.dumps(credentials)
  362. db.session.add(provider)
  363. db.session.commit()
  364. # delete cache
  365. tool_configuration.delete_tool_credentials_cache()
  366. return { 'result': 'success' }
  367. @staticmethod
  368. def delete_builtin_tool_provider(
  369. user_id: str, tenant_id: str, provider_name: str
  370. ):
  371. """
  372. delete tool provider
  373. """
  374. provider: BuiltinToolProvider = db.session.query(BuiltinToolProvider).filter(
  375. BuiltinToolProvider.tenant_id == tenant_id,
  376. BuiltinToolProvider.provider == provider_name,
  377. ).first()
  378. if provider is None:
  379. raise ValueError(f'you have not added provider {provider_name}')
  380. db.session.delete(provider)
  381. db.session.commit()
  382. # delete cache
  383. provider_controller = ToolManager.get_builtin_provider(provider_name)
  384. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  385. tool_configuration.delete_tool_credentials_cache()
  386. return { 'result': 'success' }
  387. @staticmethod
  388. def get_builtin_tool_provider_icon(
  389. provider: str
  390. ):
  391. """
  392. get tool provider icon and it's mimetype
  393. """
  394. icon_path, mime_type = ToolManager.get_builtin_provider_icon(provider)
  395. with open(icon_path, 'rb') as f:
  396. icon_bytes = f.read()
  397. return icon_bytes, mime_type
  398. @staticmethod
  399. def get_model_tool_provider_icon(
  400. provider: str
  401. ):
  402. """
  403. get tool provider icon and it's mimetype
  404. """
  405. service = ModelProviderService()
  406. icon_bytes, mime_type = service.get_model_provider_icon(provider=provider, icon_type='icon_small', lang='en_US')
  407. if icon_bytes is None:
  408. raise ValueError(f'provider {provider} does not exists')
  409. return icon_bytes, mime_type
  410. @staticmethod
  411. def list_model_tool_provider_tools(
  412. user_id: str, tenant_id: str, provider: str
  413. ) -> list[UserTool]:
  414. """
  415. list model tool provider tools
  416. """
  417. provider_controller = ToolManager.get_model_provider(tenant_id=tenant_id, provider_name=provider)
  418. tools = provider_controller.get_tools(user_id=user_id, tenant_id=tenant_id)
  419. result = [
  420. UserTool(
  421. author=tool.identity.author,
  422. name=tool.identity.name,
  423. label=tool.identity.label,
  424. description=tool.description.human,
  425. parameters=tool.parameters or []
  426. ) for tool in tools
  427. ]
  428. return jsonable_encoder(result)
  429. @staticmethod
  430. def delete_api_tool_provider(
  431. user_id: str, tenant_id: str, provider_name: str
  432. ):
  433. """
  434. delete tool provider
  435. """
  436. provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  437. ApiToolProvider.tenant_id == tenant_id,
  438. ApiToolProvider.name == provider_name,
  439. ).first()
  440. if provider is None:
  441. raise ValueError(f'you have not added provider {provider_name}')
  442. db.session.delete(provider)
  443. db.session.commit()
  444. return { 'result': 'success' }
  445. @staticmethod
  446. def get_api_tool_provider(
  447. user_id: str, tenant_id: str, provider: str
  448. ):
  449. """
  450. get api tool provider
  451. """
  452. return ToolManager.user_get_api_provider(provider=provider, tenant_id=tenant_id)
  453. @staticmethod
  454. def test_api_tool_preview(
  455. tenant_id: str,
  456. provider_name: str,
  457. tool_name: str,
  458. credentials: dict,
  459. parameters: dict,
  460. schema_type: str,
  461. schema: str
  462. ):
  463. """
  464. test api tool before adding api tool provider
  465. """
  466. if schema_type not in [member.value for member in ApiProviderSchemaType]:
  467. raise ValueError(f'invalid schema type {schema_type}')
  468. try:
  469. tool_bundles, _ = ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema)
  470. except Exception as e:
  471. raise ValueError('invalid schema')
  472. # get tool bundle
  473. tool_bundle = next(filter(lambda tb: tb.operation_id == tool_name, tool_bundles), None)
  474. if tool_bundle is None:
  475. raise ValueError(f'invalid tool name {tool_name}')
  476. db_provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  477. ApiToolProvider.tenant_id == tenant_id,
  478. ApiToolProvider.name == provider_name,
  479. ).first()
  480. if not db_provider:
  481. # create a fake db provider
  482. db_provider = ApiToolProvider(
  483. tenant_id='', user_id='', name='', icon='',
  484. schema=schema,
  485. description='',
  486. schema_type_str=ApiProviderSchemaType.OPENAPI.value,
  487. tools_str=json.dumps(jsonable_encoder(tool_bundles)),
  488. credentials_str=json.dumps(credentials),
  489. )
  490. if 'auth_type' not in credentials:
  491. raise ValueError('auth_type is required')
  492. # get auth type, none or api key
  493. auth_type = ApiProviderAuthType.value_of(credentials['auth_type'])
  494. # create provider entity
  495. provider_controller = ApiBasedToolProviderController.from_db(db_provider, auth_type)
  496. # load tools into provider entity
  497. provider_controller.load_bundled_tools(tool_bundles)
  498. # decrypt credentials
  499. if db_provider.id:
  500. tool_configuration = ToolConfigurationManager(
  501. tenant_id=tenant_id,
  502. provider_controller=provider_controller
  503. )
  504. decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
  505. # check if the credential has changed, save the original credential
  506. masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
  507. for name, value in credentials.items():
  508. if name in masked_credentials and value == masked_credentials[name]:
  509. credentials[name] = decrypted_credentials[name]
  510. try:
  511. provider_controller.validate_credentials_format(credentials)
  512. # get tool
  513. tool = provider_controller.get_tool(tool_name)
  514. tool = tool.fork_tool_runtime(meta={
  515. 'credentials': credentials,
  516. 'tenant_id': tenant_id,
  517. })
  518. result = tool.validate_credentials(credentials, parameters)
  519. except Exception as e:
  520. return { 'error': str(e) }
  521. return { 'result': result or 'empty response' }
  522. @staticmethod
  523. def list_builtin_tools(
  524. user_id: str, tenant_id: str
  525. ) -> list[UserToolProvider]:
  526. """
  527. list builtin tools
  528. """
  529. # get all builtin providers
  530. provider_controllers = ToolManager.list_builtin_providers()
  531. # get all user added providers
  532. db_providers: list[BuiltinToolProvider] = db.session.query(BuiltinToolProvider).filter(
  533. BuiltinToolProvider.tenant_id == tenant_id
  534. ).all() or []
  535. # find provider
  536. find_provider = lambda provider: next(filter(lambda db_provider: db_provider.provider == provider, db_providers), None)
  537. result: list[UserToolProvider] = []
  538. for provider_controller in provider_controllers:
  539. # convert provider controller to user provider
  540. user_builtin_provider = ToolTransformService.builtin_provider_to_user_provider(
  541. provider_controller=provider_controller,
  542. db_provider=find_provider(provider_controller.identity.name),
  543. decrypt_credentials=True
  544. )
  545. # add icon
  546. ToolTransformService.repack_provider(user_builtin_provider)
  547. tools = provider_controller.get_tools()
  548. for tool in tools:
  549. user_builtin_provider.tools.append(ToolTransformService.tool_to_user_tool(
  550. tenant_id=tenant_id,
  551. tool=tool,
  552. credentials=user_builtin_provider.original_credentials,
  553. ))
  554. result.append(user_builtin_provider)
  555. return BuiltinToolProviderSort.sort(result)
  556. @staticmethod
  557. def list_api_tools(
  558. user_id: str, tenant_id: str
  559. ) -> list[UserToolProvider]:
  560. """
  561. list api tools
  562. """
  563. # get all api providers
  564. db_providers: list[ApiToolProvider] = db.session.query(ApiToolProvider).filter(
  565. ApiToolProvider.tenant_id == tenant_id
  566. ).all() or []
  567. result: list[UserToolProvider] = []
  568. for provider in db_providers:
  569. # convert provider controller to user provider
  570. provider_controller = ToolTransformService.api_provider_to_controller(db_provider=provider)
  571. user_provider = ToolTransformService.api_provider_to_user_provider(
  572. provider_controller,
  573. db_provider=provider,
  574. decrypt_credentials=True
  575. )
  576. # add icon
  577. ToolTransformService.repack_provider(user_provider)
  578. tools = provider_controller.get_tools(
  579. user_id=user_id, tenant_id=tenant_id
  580. )
  581. for tool in tools:
  582. user_provider.tools.append(ToolTransformService.tool_to_user_tool(
  583. tenant_id=tenant_id,
  584. tool=tool,
  585. credentials=user_provider.original_credentials,
  586. ))
  587. result.append(user_provider)
  588. return result