tools_transform_service.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. import json
  2. import logging
  3. from typing import Optional, Union
  4. from yarl import URL
  5. from configs import dify_config
  6. from core.tools.__base.tool import Tool
  7. from core.tools.__base.tool_runtime import ToolRuntime
  8. from core.tools.builtin_tool.provider import BuiltinToolProviderController
  9. from core.tools.custom_tool.provider import ApiToolProviderController
  10. from core.tools.entities.api_entities import ToolApiEntity, ToolProviderApiEntity
  11. from core.tools.entities.common_entities import I18nObject
  12. from core.tools.entities.tool_bundle import ApiToolBundle
  13. from core.tools.entities.tool_entities import (
  14. ApiProviderAuthType,
  15. ToolParameter,
  16. ToolProviderType,
  17. )
  18. from core.tools.plugin_tool.provider import PluginToolProviderController
  19. from core.tools.utils.configuration import ProviderConfigEncrypter
  20. from core.tools.workflow_as_tool.provider import WorkflowToolProviderController
  21. from core.tools.workflow_as_tool.tool import WorkflowTool
  22. from models.tools import ApiToolProvider, BuiltinToolProvider, WorkflowToolProvider
  23. logger = logging.getLogger(__name__)
  24. class ToolTransformService:
  25. @classmethod
  26. def get_plugin_icon_url(cls, tenant_id: str, filename: str) -> str:
  27. url_prefix = URL(dify_config.CONSOLE_API_URL) / "console" / "api" / "workspaces" / "current" / "plugin" / "icon"
  28. return str(url_prefix % {"tenant_id": tenant_id, "filename": filename})
  29. @classmethod
  30. def get_tool_provider_icon_url(cls, provider_type: str, provider_name: str, icon: str | dict) -> Union[str, dict]:
  31. """
  32. get tool provider icon url
  33. """
  34. url_prefix = URL(dify_config.CONSOLE_API_URL) / "console" / "api" / "workspaces" / "current" / "tool-provider"
  35. if provider_type == ToolProviderType.BUILT_IN.value:
  36. return str(url_prefix / "builtin" / provider_name / "icon")
  37. elif provider_type in {ToolProviderType.API.value, ToolProviderType.WORKFLOW.value}:
  38. try:
  39. if isinstance(icon, str):
  40. return json.loads(icon)
  41. return icon
  42. except:
  43. return {"background": "#252525", "content": "\ud83d\ude01"}
  44. return ""
  45. @staticmethod
  46. def repack_provider(tenant_id: str, provider: Union[dict, ToolProviderApiEntity]):
  47. """
  48. repack provider
  49. :param provider: the provider dict
  50. """
  51. if isinstance(provider, dict) and "icon" in provider:
  52. provider["icon"] = ToolTransformService.get_tool_provider_icon_url(
  53. provider_type=provider["type"], provider_name=provider["name"], icon=provider["icon"]
  54. )
  55. elif isinstance(provider, ToolProviderApiEntity):
  56. if provider.plugin_id:
  57. provider.icon = ToolTransformService.get_plugin_icon_url(tenant_id=tenant_id, filename=provider.icon)
  58. else:
  59. provider.icon = ToolTransformService.get_tool_provider_icon_url(
  60. provider_type=provider.type.value, provider_name=provider.name, icon=provider.icon
  61. )
  62. @classmethod
  63. def builtin_provider_to_user_provider(
  64. cls,
  65. provider_controller: BuiltinToolProviderController | PluginToolProviderController,
  66. db_provider: Optional[BuiltinToolProvider],
  67. decrypt_credentials: bool = True,
  68. ) -> ToolProviderApiEntity:
  69. """
  70. convert provider controller to user provider
  71. """
  72. result = ToolProviderApiEntity(
  73. id=provider_controller.entity.identity.name,
  74. author=provider_controller.entity.identity.author,
  75. name=provider_controller.entity.identity.name,
  76. description=provider_controller.entity.identity.description,
  77. icon=provider_controller.entity.identity.icon,
  78. label=provider_controller.entity.identity.label,
  79. type=ToolProviderType.BUILT_IN,
  80. masked_credentials={},
  81. is_team_authorization=False,
  82. plugin_id=None,
  83. tools=[],
  84. labels=provider_controller.tool_labels,
  85. )
  86. if isinstance(provider_controller, PluginToolProviderController):
  87. result.plugin_id = provider_controller.plugin_id
  88. # get credentials schema
  89. schema = {x.to_basic_provider_config().name: x for x in provider_controller.get_credentials_schema()}
  90. for name, value in schema.items():
  91. if result.masked_credentials:
  92. result.masked_credentials[name] = ""
  93. # check if the provider need credentials
  94. if not provider_controller.need_credentials:
  95. result.is_team_authorization = True
  96. result.allow_delete = False
  97. elif db_provider:
  98. result.is_team_authorization = True
  99. if decrypt_credentials:
  100. credentials = db_provider.credentials
  101. # init tool configuration
  102. tool_configuration = ProviderConfigEncrypter(
  103. tenant_id=db_provider.tenant_id,
  104. config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
  105. provider_type=provider_controller.provider_type.value,
  106. provider_identity=provider_controller.entity.identity.name,
  107. )
  108. # decrypt the credentials and mask the credentials
  109. decrypted_credentials = tool_configuration.decrypt(data=credentials)
  110. masked_credentials = tool_configuration.mask_tool_credentials(data=decrypted_credentials)
  111. result.masked_credentials = masked_credentials
  112. result.original_credentials = decrypted_credentials
  113. return result
  114. @staticmethod
  115. def api_provider_to_controller(
  116. db_provider: ApiToolProvider,
  117. ) -> ApiToolProviderController:
  118. """
  119. convert provider controller to user provider
  120. """
  121. # package tool provider controller
  122. controller = ApiToolProviderController.from_db(
  123. db_provider=db_provider,
  124. auth_type=ApiProviderAuthType.API_KEY
  125. if db_provider.credentials["auth_type"] == "api_key"
  126. else ApiProviderAuthType.NONE,
  127. )
  128. return controller
  129. @staticmethod
  130. def workflow_provider_to_controller(db_provider: WorkflowToolProvider) -> WorkflowToolProviderController:
  131. """
  132. convert provider controller to provider
  133. """
  134. return WorkflowToolProviderController.from_db(db_provider)
  135. @staticmethod
  136. def workflow_provider_to_user_provider(
  137. provider_controller: WorkflowToolProviderController, labels: list[str] | None = None
  138. ):
  139. """
  140. convert provider controller to user provider
  141. """
  142. return ToolProviderApiEntity(
  143. id=provider_controller.provider_id,
  144. author=provider_controller.entity.identity.author,
  145. name=provider_controller.entity.identity.name,
  146. description=provider_controller.entity.identity.description,
  147. icon=provider_controller.entity.identity.icon,
  148. label=provider_controller.entity.identity.label,
  149. type=ToolProviderType.WORKFLOW,
  150. masked_credentials={},
  151. is_team_authorization=True,
  152. plugin_id=None,
  153. tools=[],
  154. labels=labels or [],
  155. )
  156. @classmethod
  157. def api_provider_to_user_provider(
  158. cls,
  159. provider_controller: ApiToolProviderController,
  160. db_provider: ApiToolProvider,
  161. decrypt_credentials: bool = True,
  162. labels: list[str] | None = None,
  163. ) -> ToolProviderApiEntity:
  164. """
  165. convert provider controller to user provider
  166. """
  167. username = "Anonymous"
  168. try:
  169. user = db_provider.user
  170. if not user:
  171. raise ValueError("user not found")
  172. username = user.name
  173. except Exception as e:
  174. logger.exception(f"failed to get user name for api provider {db_provider.id}: {str(e)}")
  175. # add provider into providers
  176. credentials = db_provider.credentials
  177. result = ToolProviderApiEntity(
  178. id=db_provider.id,
  179. author=username,
  180. name=db_provider.name,
  181. description=I18nObject(
  182. en_US=db_provider.description,
  183. zh_Hans=db_provider.description,
  184. ),
  185. icon=db_provider.icon,
  186. label=I18nObject(
  187. en_US=db_provider.name,
  188. zh_Hans=db_provider.name,
  189. ),
  190. type=ToolProviderType.API,
  191. plugin_id=None,
  192. masked_credentials={},
  193. is_team_authorization=True,
  194. tools=[],
  195. labels=labels or [],
  196. )
  197. if decrypt_credentials:
  198. # init tool configuration
  199. tool_configuration = ProviderConfigEncrypter(
  200. tenant_id=db_provider.tenant_id,
  201. config=[x.to_basic_provider_config() for x in provider_controller.get_credentials_schema()],
  202. provider_type=provider_controller.provider_type.value,
  203. provider_identity=provider_controller.entity.identity.name,
  204. )
  205. # decrypt the credentials and mask the credentials
  206. decrypted_credentials = tool_configuration.decrypt(data=credentials)
  207. masked_credentials = tool_configuration.mask_tool_credentials(data=decrypted_credentials)
  208. result.masked_credentials = masked_credentials
  209. return result
  210. @staticmethod
  211. def convert_tool_entity_to_api_entity(
  212. tool: Union[ApiToolBundle, WorkflowTool, Tool],
  213. tenant_id: str,
  214. credentials: dict | None = None,
  215. labels: list[str] | None = None,
  216. ) -> ToolApiEntity:
  217. """
  218. convert tool to user tool
  219. """
  220. if isinstance(tool, Tool):
  221. # fork tool runtime
  222. tool = tool.fork_tool_runtime(
  223. runtime=ToolRuntime(
  224. credentials=credentials or {},
  225. tenant_id=tenant_id,
  226. )
  227. )
  228. # get tool parameters
  229. parameters = tool.entity.parameters or []
  230. # get tool runtime parameters
  231. runtime_parameters = tool.get_runtime_parameters() or []
  232. # override parameters
  233. current_parameters = parameters.copy()
  234. for runtime_parameter in runtime_parameters:
  235. found = False
  236. for index, parameter in enumerate(current_parameters):
  237. if parameter.name == runtime_parameter.name and parameter.form == runtime_parameter.form:
  238. current_parameters[index] = runtime_parameter
  239. found = True
  240. break
  241. if not found and runtime_parameter.form == ToolParameter.ToolParameterForm.FORM:
  242. current_parameters.append(runtime_parameter)
  243. return ToolApiEntity(
  244. author=tool.entity.identity.author,
  245. name=tool.entity.identity.name,
  246. label=tool.entity.identity.label,
  247. description=tool.entity.description.human if tool.entity.description else I18nObject(en_US=""),
  248. output_schema=tool.entity.output_schema,
  249. parameters=current_parameters,
  250. labels=labels or [],
  251. )
  252. if isinstance(tool, ApiToolBundle):
  253. return ToolApiEntity(
  254. author=tool.author,
  255. name=tool.operation_id,
  256. label=I18nObject(en_US=tool.operation_id, zh_Hans=tool.operation_id),
  257. description=I18nObject(en_US=tool.summary or "", zh_Hans=tool.summary or ""),
  258. parameters=tool.parameters,
  259. labels=labels or [],
  260. )