tools_manage_service.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  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
  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. )
  191. if 'auth_type' not in credentials:
  192. raise ValueError('auth_type is required')
  193. # get auth type, none or api key
  194. auth_type = ApiProviderAuthType.value_of(credentials['auth_type'])
  195. # create provider entity
  196. provider_controller = ApiBasedToolProviderController.from_db(db_provider, auth_type)
  197. # load tools into provider entity
  198. provider_controller.load_bundled_tools(tool_bundles)
  199. # encrypt credentials
  200. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  201. encrypted_credentials = tool_configuration.encrypt_tool_credentials(credentials)
  202. db_provider.credentials_str = json.dumps(encrypted_credentials)
  203. db.session.add(db_provider)
  204. db.session.commit()
  205. return { 'result': 'success' }
  206. @staticmethod
  207. def get_api_tool_provider_remote_schema(
  208. user_id: str, tenant_id: str, url: str
  209. ):
  210. """
  211. get api tool provider remote schema
  212. """
  213. headers = {
  214. "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",
  215. "Accept": "*/*",
  216. }
  217. try:
  218. response = get(url, headers=headers, timeout=10)
  219. if response.status_code != 200:
  220. raise ValueError(f'Got status code {response.status_code}')
  221. schema = response.text
  222. # try to parse schema, avoid SSRF attack
  223. ToolManageService.parser_api_schema(schema)
  224. except Exception as e:
  225. logger.error(f"parse api schema error: {str(e)}")
  226. raise ValueError('invalid schema, please check the url you provided')
  227. return {
  228. 'schema': schema
  229. }
  230. @staticmethod
  231. def list_api_tool_provider_tools(
  232. user_id: str, tenant_id: str, provider: str
  233. ) -> list[UserTool]:
  234. """
  235. list api tool provider tools
  236. """
  237. provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  238. ApiToolProvider.tenant_id == tenant_id,
  239. ApiToolProvider.name == provider,
  240. ).first()
  241. if provider is None:
  242. raise ValueError(f'you have not added provider {provider}')
  243. return [
  244. ToolTransformService.tool_to_user_tool(tool_bundle) for tool_bundle in provider.tools
  245. ]
  246. @staticmethod
  247. def update_builtin_tool_provider(
  248. user_id: str, tenant_id: str, provider_name: str, credentials: dict
  249. ):
  250. """
  251. update builtin tool provider
  252. """
  253. # get if the provider exists
  254. provider: BuiltinToolProvider = db.session.query(BuiltinToolProvider).filter(
  255. BuiltinToolProvider.tenant_id == tenant_id,
  256. BuiltinToolProvider.provider == provider_name,
  257. ).first()
  258. try:
  259. # get provider
  260. provider_controller = ToolManager.get_builtin_provider(provider_name)
  261. if not provider_controller.need_credentials:
  262. raise ValueError(f'provider {provider_name} does not need credentials')
  263. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  264. # get original credentials if exists
  265. if provider is not None:
  266. original_credentials = tool_configuration.decrypt_tool_credentials(provider.credentials)
  267. masked_credentials = tool_configuration.mask_tool_credentials(original_credentials)
  268. # check if the credential has changed, save the original credential
  269. for name, value in credentials.items():
  270. if name in masked_credentials and value == masked_credentials[name]:
  271. credentials[name] = original_credentials[name]
  272. # validate credentials
  273. provider_controller.validate_credentials(credentials)
  274. # encrypt credentials
  275. credentials = tool_configuration.encrypt_tool_credentials(credentials)
  276. except (ToolProviderNotFoundError, ToolNotFoundError, ToolProviderCredentialValidationError) as e:
  277. raise ValueError(str(e))
  278. if provider is None:
  279. # create provider
  280. provider = BuiltinToolProvider(
  281. tenant_id=tenant_id,
  282. user_id=user_id,
  283. provider=provider_name,
  284. encrypted_credentials=json.dumps(credentials),
  285. )
  286. db.session.add(provider)
  287. db.session.commit()
  288. else:
  289. provider.encrypted_credentials = json.dumps(credentials)
  290. db.session.add(provider)
  291. db.session.commit()
  292. # delete cache
  293. tool_configuration.delete_tool_credentials_cache()
  294. return { 'result': 'success' }
  295. @staticmethod
  296. def get_builtin_tool_provider_credentials(
  297. user_id: str, tenant_id: str, provider: str
  298. ):
  299. """
  300. get builtin tool provider credentials
  301. """
  302. provider: BuiltinToolProvider = db.session.query(BuiltinToolProvider).filter(
  303. BuiltinToolProvider.tenant_id == tenant_id,
  304. BuiltinToolProvider.provider == provider,
  305. ).first()
  306. if provider is None:
  307. return {}
  308. provider_controller = ToolManager.get_builtin_provider(provider.provider)
  309. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  310. credentials = tool_configuration.decrypt_tool_credentials(provider.credentials)
  311. credentials = tool_configuration.mask_tool_credentials(credentials)
  312. return credentials
  313. @staticmethod
  314. def update_api_tool_provider(
  315. user_id: str, tenant_id: str, provider_name: str, original_provider: str, icon: dict, credentials: dict,
  316. schema_type: str, schema: str, privacy_policy: str
  317. ):
  318. """
  319. update api tool provider
  320. """
  321. if schema_type not in [member.value for member in ApiProviderSchemaType]:
  322. raise ValueError(f'invalid schema type {schema}')
  323. # check if the provider exists
  324. provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  325. ApiToolProvider.tenant_id == tenant_id,
  326. ApiToolProvider.name == original_provider,
  327. ).first()
  328. if provider is None:
  329. raise ValueError(f'api provider {provider_name} does not exists')
  330. # parse openapi to tool bundle
  331. extra_info = {}
  332. # extra info like description will be set here
  333. tool_bundles, schema_type = ToolManageService.convert_schema_to_tool_bundles(schema, extra_info)
  334. # update db provider
  335. provider.name = provider_name
  336. provider.icon = json.dumps(icon)
  337. provider.schema = schema
  338. provider.description = extra_info.get('description', '')
  339. provider.schema_type_str = ApiProviderSchemaType.OPENAPI.value
  340. provider.tools_str = json.dumps(jsonable_encoder(tool_bundles))
  341. provider.privacy_policy = privacy_policy
  342. if 'auth_type' not in credentials:
  343. raise ValueError('auth_type is required')
  344. # get auth type, none or api key
  345. auth_type = ApiProviderAuthType.value_of(credentials['auth_type'])
  346. # create provider entity
  347. provider_controller = ApiBasedToolProviderController.from_db(provider, auth_type)
  348. # load tools into provider entity
  349. provider_controller.load_bundled_tools(tool_bundles)
  350. # get original credentials if exists
  351. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  352. original_credentials = tool_configuration.decrypt_tool_credentials(provider.credentials)
  353. masked_credentials = tool_configuration.mask_tool_credentials(original_credentials)
  354. # check if the credential has changed, save the original credential
  355. for name, value in credentials.items():
  356. if name in masked_credentials and value == masked_credentials[name]:
  357. credentials[name] = original_credentials[name]
  358. credentials = tool_configuration.encrypt_tool_credentials(credentials)
  359. provider.credentials_str = json.dumps(credentials)
  360. db.session.add(provider)
  361. db.session.commit()
  362. # delete cache
  363. tool_configuration.delete_tool_credentials_cache()
  364. return { 'result': 'success' }
  365. @staticmethod
  366. def delete_builtin_tool_provider(
  367. user_id: str, tenant_id: str, provider_name: str
  368. ):
  369. """
  370. delete tool provider
  371. """
  372. provider: BuiltinToolProvider = db.session.query(BuiltinToolProvider).filter(
  373. BuiltinToolProvider.tenant_id == tenant_id,
  374. BuiltinToolProvider.provider == provider_name,
  375. ).first()
  376. if provider is None:
  377. raise ValueError(f'you have not added provider {provider_name}')
  378. db.session.delete(provider)
  379. db.session.commit()
  380. # delete cache
  381. provider_controller = ToolManager.get_builtin_provider(provider_name)
  382. tool_configuration = ToolConfigurationManager(tenant_id=tenant_id, provider_controller=provider_controller)
  383. tool_configuration.delete_tool_credentials_cache()
  384. return { 'result': 'success' }
  385. @staticmethod
  386. def get_builtin_tool_provider_icon(
  387. provider: str
  388. ):
  389. """
  390. get tool provider icon and it's mimetype
  391. """
  392. icon_path, mime_type = ToolManager.get_builtin_provider_icon(provider)
  393. with open(icon_path, 'rb') as f:
  394. icon_bytes = f.read()
  395. return icon_bytes, mime_type
  396. @staticmethod
  397. def get_model_tool_provider_icon(
  398. provider: str
  399. ):
  400. """
  401. get tool provider icon and it's mimetype
  402. """
  403. service = ModelProviderService()
  404. icon_bytes, mime_type = service.get_model_provider_icon(provider=provider, icon_type='icon_small', lang='en_US')
  405. if icon_bytes is None:
  406. raise ValueError(f'provider {provider} does not exists')
  407. return icon_bytes, mime_type
  408. @staticmethod
  409. def list_model_tool_provider_tools(
  410. user_id: str, tenant_id: str, provider: str
  411. ) -> list[UserTool]:
  412. """
  413. list model tool provider tools
  414. """
  415. provider_controller = ToolManager.get_model_provider(tenant_id=tenant_id, provider_name=provider)
  416. tools = provider_controller.get_tools(user_id=user_id, tenant_id=tenant_id)
  417. result = [
  418. UserTool(
  419. author=tool.identity.author,
  420. name=tool.identity.name,
  421. label=tool.identity.label,
  422. description=tool.description.human,
  423. parameters=tool.parameters or []
  424. ) for tool in tools
  425. ]
  426. return jsonable_encoder(result)
  427. @staticmethod
  428. def delete_api_tool_provider(
  429. user_id: str, tenant_id: str, provider_name: str
  430. ):
  431. """
  432. delete tool provider
  433. """
  434. provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  435. ApiToolProvider.tenant_id == tenant_id,
  436. ApiToolProvider.name == provider_name,
  437. ).first()
  438. if provider is None:
  439. raise ValueError(f'you have not added provider {provider_name}')
  440. db.session.delete(provider)
  441. db.session.commit()
  442. return { 'result': 'success' }
  443. @staticmethod
  444. def get_api_tool_provider(
  445. user_id: str, tenant_id: str, provider: str
  446. ):
  447. """
  448. get api tool provider
  449. """
  450. return ToolManager.user_get_api_provider(provider=provider, tenant_id=tenant_id)
  451. @staticmethod
  452. def test_api_tool_preview(
  453. tenant_id: str,
  454. provider_name: str,
  455. tool_name: str,
  456. credentials: dict,
  457. parameters: dict,
  458. schema_type: str,
  459. schema: str
  460. ):
  461. """
  462. test api tool before adding api tool provider
  463. """
  464. if schema_type not in [member.value for member in ApiProviderSchemaType]:
  465. raise ValueError(f'invalid schema type {schema_type}')
  466. try:
  467. tool_bundles, _ = ApiBasedToolSchemaParser.auto_parse_to_tool_bundle(schema)
  468. except Exception as e:
  469. raise ValueError('invalid schema')
  470. # get tool bundle
  471. tool_bundle = next(filter(lambda tb: tb.operation_id == tool_name, tool_bundles), None)
  472. if tool_bundle is None:
  473. raise ValueError(f'invalid tool name {tool_name}')
  474. db_provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
  475. ApiToolProvider.tenant_id == tenant_id,
  476. ApiToolProvider.name == provider_name,
  477. ).first()
  478. if not db_provider:
  479. # create a fake db provider
  480. db_provider = ApiToolProvider(
  481. tenant_id='', user_id='', name='', icon='',
  482. schema=schema,
  483. description='',
  484. schema_type_str=ApiProviderSchemaType.OPENAPI.value,
  485. tools_str=json.dumps(jsonable_encoder(tool_bundles)),
  486. credentials_str=json.dumps(credentials),
  487. )
  488. if 'auth_type' not in credentials:
  489. raise ValueError('auth_type is required')
  490. # get auth type, none or api key
  491. auth_type = ApiProviderAuthType.value_of(credentials['auth_type'])
  492. # create provider entity
  493. provider_controller = ApiBasedToolProviderController.from_db(db_provider, auth_type)
  494. # load tools into provider entity
  495. provider_controller.load_bundled_tools(tool_bundles)
  496. # decrypt credentials
  497. if db_provider.id:
  498. tool_configuration = ToolConfigurationManager(
  499. tenant_id=tenant_id,
  500. provider_controller=provider_controller
  501. )
  502. decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
  503. # check if the credential has changed, save the original credential
  504. masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
  505. for name, value in credentials.items():
  506. if name in masked_credentials and value == masked_credentials[name]:
  507. credentials[name] = decrypted_credentials[name]
  508. try:
  509. provider_controller.validate_credentials_format(credentials)
  510. # get tool
  511. tool = provider_controller.get_tool(tool_name)
  512. tool = tool.fork_tool_runtime(meta={
  513. 'credentials': credentials,
  514. 'tenant_id': tenant_id,
  515. })
  516. result = tool.validate_credentials(credentials, parameters)
  517. except Exception as e:
  518. return { 'error': str(e) }
  519. return { 'result': result or 'empty response' }
  520. @staticmethod
  521. def list_builtin_tools(
  522. user_id: str, tenant_id: str
  523. ) -> list[UserToolProvider]:
  524. """
  525. list builtin tools
  526. """
  527. # get all builtin providers
  528. provider_controllers = ToolManager.list_builtin_providers()
  529. # get all user added providers
  530. db_providers: list[BuiltinToolProvider] = db.session.query(BuiltinToolProvider).filter(
  531. BuiltinToolProvider.tenant_id == tenant_id
  532. ).all() or []
  533. # find provider
  534. find_provider = lambda provider: next(filter(lambda db_provider: db_provider.provider == provider, db_providers), None)
  535. result: list[UserToolProvider] = []
  536. for provider_controller in provider_controllers:
  537. # convert provider controller to user provider
  538. user_builtin_provider = ToolTransformService.builtin_provider_to_user_provider(
  539. provider_controller=provider_controller,
  540. db_provider=find_provider(provider_controller.identity.name),
  541. decrypt_credentials=True
  542. )
  543. # add icon
  544. ToolTransformService.repack_provider(user_builtin_provider)
  545. tools = provider_controller.get_tools()
  546. for tool in tools:
  547. user_builtin_provider.tools.append(ToolTransformService.tool_to_user_tool(
  548. tenant_id=tenant_id,
  549. tool=tool,
  550. credentials=user_builtin_provider.original_credentials,
  551. ))
  552. result.append(user_builtin_provider)
  553. return BuiltinToolProviderSort.sort(result)
  554. @staticmethod
  555. def list_api_tools(
  556. user_id: str, tenant_id: str
  557. ) -> list[UserToolProvider]:
  558. """
  559. list api tools
  560. """
  561. # get all api providers
  562. db_providers: list[ApiToolProvider] = db.session.query(ApiToolProvider).filter(
  563. ApiToolProvider.tenant_id == tenant_id
  564. ).all() or []
  565. result: list[UserToolProvider] = []
  566. for provider in db_providers:
  567. # convert provider controller to user provider
  568. provider_controller = ToolTransformService.api_provider_to_controller(db_provider=provider)
  569. user_provider = ToolTransformService.api_provider_to_user_provider(
  570. provider_controller,
  571. db_provider=provider,
  572. decrypt_credentials=True
  573. )
  574. # add icon
  575. ToolTransformService.repack_provider(user_provider)
  576. tools = provider_controller.get_tools(
  577. user_id=user_id, tenant_id=tenant_id
  578. )
  579. for tool in tools:
  580. user_provider.tools.append(ToolTransformService.tool_to_user_tool(
  581. tenant_id=tenant_id,
  582. tool=tool,
  583. credentials=user_provider.original_credentials,
  584. ))
  585. result.append(user_provider)
  586. return result