| 
									
										
										
										
											2024-02-01 18:11:57 +08:00
										 |  |  | import json | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  | from collections.abc import Generator | 
					
						
							| 
									
										
										
										
											2024-04-12 17:46:39 +08:00
										 |  |  | from os import getenv | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  | from typing import Any, Optional | 
					
						
							| 
									
										
										
										
											2024-03-04 14:16:47 +08:00
										 |  |  | from urllib.parse import urlencode | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-01 18:11:57 +08:00
										 |  |  | import httpx | 
					
						
							| 
									
										
										
										
											2024-02-06 13:21:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-20 13:26:42 +08:00
										 |  |  | from core.file.file_manager import download | 
					
						
							| 
									
										
										
										
											2024-09-13 22:42:08 +08:00
										 |  |  | from core.helper import ssrf_proxy | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  | from core.tools.__base.tool import Tool | 
					
						
							|  |  |  | from core.tools.__base.tool_runtime import ToolRuntime | 
					
						
							| 
									
										
										
										
											2024-05-27 22:01:11 +08:00
										 |  |  | from core.tools.entities.tool_bundle import ApiToolBundle | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  | from core.tools.entities.tool_entities import ToolEntity, ToolInvokeMessage, ToolProviderType | 
					
						
							| 
									
										
										
										
											2024-03-19 18:17:12 +08:00
										 |  |  | from core.tools.errors import ToolInvokeError, ToolParameterValidationError, ToolProviderCredentialValidationError | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-12 17:46:39 +08:00
										 |  |  | API_TOOL_DEFAULT_TIMEOUT = ( | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |     int(getenv("API_TOOL_DEFAULT_CONNECT_TIMEOUT", "10")), | 
					
						
							|  |  |  |     int(getenv("API_TOOL_DEFAULT_READ_TIMEOUT", "60")), | 
					
						
							| 
									
										
										
										
											2024-04-12 17:46:39 +08:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | class ApiTool(Tool): | 
					
						
							| 
									
										
										
										
											2024-05-27 22:01:11 +08:00
										 |  |  |     api_bundle: ApiToolBundle | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |     provider_id: str | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     Api tool | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |     def __init__(self, entity: ToolEntity, api_bundle: ApiToolBundle, runtime: ToolRuntime, provider_id: str): | 
					
						
							|  |  |  |         super().__init__(entity, runtime) | 
					
						
							|  |  |  |         self.api_bundle = api_bundle | 
					
						
							|  |  |  |         self.provider_id = provider_id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def fork_tool_runtime(self, runtime: ToolRuntime): | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2025-03-31 13:19:15 +08:00
										 |  |  |         fork a new tool with metadata | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         :return: the new tool | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-12-24 18:38:51 +08:00
										 |  |  |         if self.api_bundle is None: | 
					
						
							|  |  |  |             raise ValueError("api_bundle is required") | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         return self.__class__( | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |             entity=self.entity, | 
					
						
							| 
									
										
										
										
											2024-12-24 18:38:51 +08:00
										 |  |  |             api_bundle=self.api_bundle.model_copy(), | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |             runtime=runtime, | 
					
						
							|  |  |  |             provider_id=self.provider_id, | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |     def validate_credentials( | 
					
						
							|  |  |  |         self, credentials: dict[str, Any], parameters: dict[str, Any], format_only: bool = False | 
					
						
							|  |  |  |     ) -> str: | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         validate the credentials for Api tool | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         # assemble validate request and request parameters | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         headers = self.assembling_request(parameters) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if format_only: | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             return "" | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         response = self.do_http_request(self.api_bundle.server_url, self.api_bundle.method, headers, parameters) | 
					
						
							|  |  |  |         # validate response | 
					
						
							| 
									
										
										
										
											2024-02-05 18:48:30 +08:00
										 |  |  |         return self.validate_and_parse_response(response) | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  |     def tool_provider_type(self) -> ToolProviderType: | 
					
						
							| 
									
										
										
										
											2024-05-27 22:01:11 +08:00
										 |  |  |         return ToolProviderType.API | 
					
						
							| 
									
										
										
										
											2024-04-08 18:51:46 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-09 15:21:33 +08:00
										 |  |  |     def assembling_request(self, parameters: dict[str, Any]) -> dict[str, Any]: | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |         if self.runtime is None: | 
					
						
							|  |  |  |             raise ToolProviderCredentialValidationError("runtime not initialized") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         headers = {} | 
					
						
							| 
									
										
										
										
											2024-12-24 18:38:51 +08:00
										 |  |  |         if self.runtime is None: | 
					
						
							|  |  |  |             raise ValueError("runtime is required") | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         credentials = self.runtime.credentials or {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         if "auth_type" not in credentials: | 
					
						
							|  |  |  |             raise ToolProviderCredentialValidationError("Missing auth_type") | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         if credentials["auth_type"] == "api_key": | 
					
						
							|  |  |  |             api_key_header = "api_key" | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             if "api_key_header" in credentials: | 
					
						
							|  |  |  |                 api_key_header = credentials["api_key_header"] | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             if "api_key_value" not in credentials: | 
					
						
							|  |  |  |                 raise ToolProviderCredentialValidationError("Missing api_key_value") | 
					
						
							|  |  |  |             elif not isinstance(credentials["api_key_value"], str): | 
					
						
							|  |  |  |                 raise ToolProviderCredentialValidationError("api_key_value must be a string") | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             if "api_key_header_prefix" in credentials: | 
					
						
							|  |  |  |                 api_key_header_prefix = credentials["api_key_header_prefix"] | 
					
						
							|  |  |  |                 if api_key_header_prefix == "basic" and credentials["api_key_value"]: | 
					
						
							| 
									
										
										
										
											2025-01-21 10:12:29 +08:00
										 |  |  |                     credentials["api_key_value"] = f"Basic {credentials['api_key_value']}" | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 elif api_key_header_prefix == "bearer" and credentials["api_key_value"]: | 
					
						
							| 
									
										
										
										
											2025-01-21 10:12:29 +08:00
										 |  |  |                     credentials["api_key_value"] = f"Bearer {credentials['api_key_value']}" | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 elif api_key_header_prefix == "custom": | 
					
						
							| 
									
										
										
										
											2024-02-28 23:19:08 +08:00
										 |  |  |                     pass | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             headers[api_key_header] = credentials["api_key_value"] | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-24 18:38:51 +08:00
										 |  |  |         needed_parameters = [parameter for parameter in (self.api_bundle.parameters or []) if parameter.required] | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         for parameter in needed_parameters: | 
					
						
							|  |  |  |             if parameter.required and parameter.name not in parameters: | 
					
						
							| 
									
										
										
										
											2025-02-25 09:43:36 +08:00
										 |  |  |                 if parameter.default is not None: | 
					
						
							|  |  |  |                     parameters[parameter.name] = parameter.default | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     raise ToolParameterValidationError(f"Missing required parameter {parameter.name}") | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return headers | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  |     def validate_and_parse_response(self, response: httpx.Response) -> str: | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         validate the response | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         if isinstance(response, httpx.Response): | 
					
						
							|  |  |  |             if response.status_code >= 400: | 
					
						
							| 
									
										
										
										
											2024-03-19 18:17:12 +08:00
										 |  |  |                 raise ToolInvokeError(f"Request failed with status code {response.status_code} and {response.text}") | 
					
						
							| 
									
										
										
										
											2024-01-30 22:22:58 +08:00
										 |  |  |             if not response.content: | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 return "Empty response from the tool, please check your parameters and try again." | 
					
						
							| 
									
										
										
										
											2024-01-30 22:22:58 +08:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 response = response.json() | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     return json.dumps(response, ensure_ascii=False) | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |                 except Exception: | 
					
						
							| 
									
										
										
										
											2024-01-30 22:22:58 +08:00
										 |  |  |                     return json.dumps(response) | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |             except Exception: | 
					
						
							| 
									
										
										
										
											2024-01-30 22:22:58 +08:00
										 |  |  |                 return response.text | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             raise ValueError(f"Invalid response type {type(response)}") | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def get_parameter_value(parameter, parameters): | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         if parameter["name"] in parameters: | 
					
						
							|  |  |  |             return parameters[parameter["name"]] | 
					
						
							|  |  |  |         elif parameter.get("required", False): | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  |             raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}") | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             return (parameter.get("schema", {}) or {}).get("default", "") | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |     def do_http_request( | 
					
						
							|  |  |  |         self, url: str, method: str, headers: dict[str, Any], parameters: dict[str, Any] | 
					
						
							|  |  |  |     ) -> httpx.Response: | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         do http request depending on api bundle | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         method = method.lower() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         params = {} | 
					
						
							|  |  |  |         path_params = {} | 
					
						
							| 
									
										
										
										
											2024-12-24 18:38:51 +08:00
										 |  |  |         # FIXME: body should be a dict[str, Any] but it changed a lot in this function | 
					
						
							|  |  |  |         body: Any = {} | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         cookies = {} | 
					
						
							| 
									
										
										
										
											2024-11-20 13:26:42 +08:00
										 |  |  |         files = [] | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # check parameters | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         for parameter in self.api_bundle.openapi.get("parameters", []): | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  |             value = self.get_parameter_value(parameter, parameters) | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             if parameter["in"] == "path": | 
					
						
							|  |  |  |                 path_params[parameter["name"]] = value | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             elif parameter["in"] == "query": | 
					
						
							|  |  |  |                 if value != "": | 
					
						
							|  |  |  |                     params[parameter["name"]] = value | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             elif parameter["in"] == "cookie": | 
					
						
							|  |  |  |                 cookies[parameter["name"]] = value | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             elif parameter["in"] == "header": | 
					
						
							| 
									
										
										
										
											2025-06-02 18:09:01 +08:00
										 |  |  |                 headers[parameter["name"]] = str(value) | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # check if there is a request body and handle it | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         if "requestBody" in self.api_bundle.openapi and self.api_bundle.openapi["requestBody"] is not None: | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |             # handle json request body | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             if "content" in self.api_bundle.openapi["requestBody"]: | 
					
						
							|  |  |  |                 for content_type in self.api_bundle.openapi["requestBody"]["content"]: | 
					
						
							|  |  |  |                     headers["Content-Type"] = content_type | 
					
						
							|  |  |  |                     body_schema = self.api_bundle.openapi["requestBody"]["content"][content_type]["schema"] | 
					
						
							| 
									
										
										
										
											2025-03-11 16:16:53 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |                     # handle ref schema | 
					
						
							|  |  |  |                     if "$ref" in body_schema: | 
					
						
							|  |  |  |                         ref_path = body_schema["$ref"].split("/") | 
					
						
							|  |  |  |                         ref_name = ref_path[-1] | 
					
						
							|  |  |  |                         if ( | 
					
						
							|  |  |  |                             "components" in self.api_bundle.openapi | 
					
						
							|  |  |  |                             and "schemas" in self.api_bundle.openapi["components"] | 
					
						
							|  |  |  |                         ): | 
					
						
							|  |  |  |                             if ref_name in self.api_bundle.openapi["components"]["schemas"]: | 
					
						
							|  |  |  |                                 body_schema = self.api_bundle.openapi["components"]["schemas"][ref_name] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                     required = body_schema.get("required", []) | 
					
						
							|  |  |  |                     properties = body_schema.get("properties", {}) | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |                     for name, property in properties.items(): | 
					
						
							|  |  |  |                         if name in parameters: | 
					
						
							| 
									
										
										
										
											2025-03-27 11:47:35 +09:00
										 |  |  |                             # multiple file upload: if the type is array and the items have format as binary | 
					
						
							|  |  |  |                             if property.get("type") == "array" and property.get("items", {}).get("format") == "binary": | 
					
						
							|  |  |  |                                 # parameters[name] should be a list of file objects. | 
					
						
							|  |  |  |                                 for f in parameters[name]: | 
					
						
							|  |  |  |                                     files.append((name, (f.filename, download(f), f.mime_type))) | 
					
						
							|  |  |  |                             elif property.get("format") == "binary": | 
					
						
							| 
									
										
										
										
											2024-11-20 13:26:42 +08:00
										 |  |  |                                 f = parameters[name] | 
					
						
							|  |  |  |                                 files.append((name, (f.filename, download(f), f.mime_type))) | 
					
						
							| 
									
										
										
										
											2025-03-11 16:16:53 +08:00
										 |  |  |                             elif "$ref" in property: | 
					
						
							|  |  |  |                                 body[name] = parameters[name] | 
					
						
							| 
									
										
										
										
											2024-11-20 13:26:42 +08:00
										 |  |  |                             else: | 
					
						
							|  |  |  |                                 # convert type | 
					
						
							|  |  |  |                                 body[name] = self._convert_body_property_type(property, parameters[name]) | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |                         elif name in required: | 
					
						
							| 
									
										
										
										
											2024-03-19 18:17:12 +08:00
										 |  |  |                             raise ToolParameterValidationError( | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |                                 f"Missing required parameter {name} in operation {self.api_bundle.operation_id}" | 
					
						
							|  |  |  |                             ) | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                         elif "default" in property: | 
					
						
							|  |  |  |                             body[name] = property["default"] | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |                         else: | 
					
						
							|  |  |  |                             body[name] = None | 
					
						
							|  |  |  |                     break | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         # replace path parameters | 
					
						
							|  |  |  |         for name, value in path_params.items(): | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             url = url.replace(f"{{{name}}}", f"{value}") | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-20 13:26:42 +08:00
										 |  |  |         # parse http body data if needed | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |         if "Content-Type" in headers: | 
					
						
							|  |  |  |             if headers["Content-Type"] == "application/json": | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  |                 body = json.dumps(body) | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             elif headers["Content-Type"] == "application/x-www-form-urlencoded": | 
					
						
							| 
									
										
										
										
											2024-03-04 14:16:47 +08:00
										 |  |  |                 body = urlencode(body) | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 body = body | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-27 11:47:35 +09:00
										 |  |  |         # if there is a file upload, remove the Content-Type header | 
					
						
							|  |  |  |         # so that httpx can automatically generate the boundary header required for multipart/form-data. | 
					
						
							|  |  |  |         # issue: https://github.com/langgenius/dify/issues/13684 | 
					
						
							|  |  |  |         # reference: https://stackoverflow.com/questions/39280438/fetch-missing-boundary-in-multipart-form-data-post | 
					
						
							|  |  |  |         if files: | 
					
						
							|  |  |  |             headers.pop("Content-Type", None) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-06 20:35:53 +08:00
										 |  |  |         if method in { | 
					
						
							|  |  |  |             "get", | 
					
						
							|  |  |  |             "head", | 
					
						
							|  |  |  |             "post", | 
					
						
							|  |  |  |             "put", | 
					
						
							|  |  |  |             "delete", | 
					
						
							|  |  |  |             "patch", | 
					
						
							|  |  |  |             "options", | 
					
						
							|  |  |  |             "GET", | 
					
						
							|  |  |  |             "POST", | 
					
						
							|  |  |  |             "PUT", | 
					
						
							|  |  |  |             "PATCH", | 
					
						
							|  |  |  |             "DELETE", | 
					
						
							|  |  |  |             "HEAD", | 
					
						
							|  |  |  |             "OPTIONS", | 
					
						
							|  |  |  |         }: | 
					
						
							|  |  |  |             response: httpx.Response = getattr(ssrf_proxy, method.lower())( | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 url, | 
					
						
							|  |  |  |                 params=params, | 
					
						
							|  |  |  |                 headers=headers, | 
					
						
							|  |  |  |                 cookies=cookies, | 
					
						
							|  |  |  |                 data=body, | 
					
						
							| 
									
										
										
										
											2024-11-20 13:26:42 +08:00
										 |  |  |                 files=files, | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 timeout=API_TOOL_DEFAULT_TIMEOUT, | 
					
						
							|  |  |  |                 follow_redirects=True, | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  |             return response | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2024-12-21 21:24:59 +08:00
										 |  |  |             raise ValueError(f"Invalid http method {method}") | 
					
						
							| 
									
										
										
										
											2024-06-24 16:14:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |     def _convert_body_property_any_of( | 
					
						
							|  |  |  |         self, property: dict[str, Any], value: Any, any_of: list[dict[str, Any]], max_recursive=10 | 
					
						
							|  |  |  |     ) -> Any: | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |         if max_recursive <= 0: | 
					
						
							|  |  |  |             raise Exception("Max recursion depth reached") | 
					
						
							|  |  |  |         for option in any_of or []: | 
					
						
							|  |  |  |             try: | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 if "type" in option: | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                     # Attempt to convert the value based on the type. | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                     if option["type"] == "integer" or option["type"] == "int": | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                         return int(value) | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                     elif option["type"] == "number": | 
					
						
							|  |  |  |                         if "." in str(value): | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                             return float(value) | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             return int(value) | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                     elif option["type"] == "string": | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                         return str(value) | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                     elif option["type"] == "boolean": | 
					
						
							| 
									
										
										
										
											2024-09-13 22:42:08 +08:00
										 |  |  |                         if str(value).lower() in {"true", "1"}: | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                             return True | 
					
						
							| 
									
										
										
										
											2024-09-13 22:42:08 +08:00
										 |  |  |                         elif str(value).lower() in {"false", "0"}: | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                             return False | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             continue  # Not a boolean, try next option | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                     elif option["type"] == "null" and not value: | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                         return None | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         continue  # Unsupported type, try next option | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 elif "anyOf" in option and isinstance(option["anyOf"], list): | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                     # Recursive call to handle nested anyOf | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                     return self._convert_body_property_any_of(property, value, option["anyOf"], max_recursive - 1) | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |             except ValueError: | 
					
						
							|  |  |  |                 continue  # Conversion failed, try next option | 
					
						
							|  |  |  |         # If no option succeeded, you might want to return the value as is or raise an error | 
					
						
							|  |  |  |         return value  # or raise ValueError(f"Cannot convert value '{value}' to any specified type in anyOf") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _convert_body_property_type(self, property: dict[str, Any], value: Any) -> Any: | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             if "type" in property: | 
					
						
							|  |  |  |                 if property["type"] == "integer" or property["type"] == "int": | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                     return int(value) | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 elif property["type"] == "number": | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                     # check if it is a float | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                     if "." in str(value): | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                         return float(value) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         return int(value) | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 elif property["type"] == "string": | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                     return str(value) | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 elif property["type"] == "boolean": | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                     return bool(value) | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 elif property["type"] == "null": | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                     if value is None: | 
					
						
							|  |  |  |                         return None | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |                 elif property["type"] == "object" or property["type"] == "array": | 
					
						
							| 
									
										
										
										
											2024-04-16 19:54:17 +08:00
										 |  |  |                     if isinstance(value, str): | 
					
						
							|  |  |  |                         try: | 
					
						
							|  |  |  |                             return json.loads(value) | 
					
						
							|  |  |  |                         except ValueError: | 
					
						
							|  |  |  |                             return value | 
					
						
							|  |  |  |                     elif isinstance(value, dict): | 
					
						
							|  |  |  |                         return value | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         return value | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     raise ValueError(f"Invalid type {property['type']} for property {property}") | 
					
						
							| 
									
										
										
										
											2024-09-10 17:00:20 +08:00
										 |  |  |             elif "anyOf" in property and isinstance(property["anyOf"], list): | 
					
						
							|  |  |  |                 return self._convert_body_property_any_of(property, value, property["anyOf"]) | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |         except ValueError: | 
					
						
							| 
									
										
										
										
											2024-02-29 14:39:05 +08:00
										 |  |  |             return value | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |     def _invoke( | 
					
						
							|  |  |  |         self, | 
					
						
							|  |  |  |         user_id: str, | 
					
						
							|  |  |  |         tool_parameters: dict[str, Any], | 
					
						
							|  |  |  |         conversation_id: Optional[str] = None, | 
					
						
							|  |  |  |         app_id: Optional[str] = None, | 
					
						
							|  |  |  |         message_id: Optional[str] = None, | 
					
						
							|  |  |  |     ) -> Generator[ToolInvokeMessage, None, None]: | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         invoke http request | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2024-12-24 18:38:51 +08:00
										 |  |  |         response: httpx.Response | str = "" | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  |         # assemble request | 
					
						
							| 
									
										
										
										
											2024-01-31 11:58:07 +08:00
										 |  |  |         headers = self.assembling_request(tool_parameters) | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # do http request | 
					
						
							| 
									
										
										
										
											2024-01-31 11:58:07 +08:00
										 |  |  |         response = self.do_http_request(self.api_bundle.server_url, self.api_bundle.method, headers, tool_parameters) | 
					
						
							| 
									
										
										
										
											2024-01-23 19:58:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # validate response | 
					
						
							|  |  |  |         response = self.validate_and_parse_response(response) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # assemble invoke message | 
					
						
							| 
									
										
										
										
											2025-02-17 17:05:13 +08:00
										 |  |  |         yield self.create_text_message(response) |