123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157 |
- import logging
- import os
- import urllib.parse
- from typing import Dict
- import requests
- from app.schemas.tool.authentication import Authentication, AuthenticationType
- from app.schemas.tool.action import ActionMethod, ActionBodyType, ActionParam
- # This function code from the Open Source Project TaskingAI.
- # The original code can be found at: https://github.com/TaskingAI/TaskingAI
- def _prepare_headers(authentication: Authentication, extra_headers: Dict) -> Dict:
- """
- Prepares the headers for an HTTP request including authentication and additional headers.
- :param authentication: An Authentication object containing the authentication details.
- :param extra_headers: A dictionary of additional headers to include in the request.
- :return: A dictionary of headers for the HTTP request.
- """
- headers = {}
- if extra_headers:
- headers.update(extra_headers)
- if authentication:
- if authentication.type == AuthenticationType.basic:
- # Basic Authentication: Assume the secret is a base64 encoded string
- headers["Authorization"] = f"Basic {authentication.secret}"
- elif authentication.type == AuthenticationType.bearer:
- # Bearer Authentication: Add the secret as a bearer token
- headers["Authorization"] = f"Bearer {authentication.secret}"
- elif authentication.type == AuthenticationType.custom:
- # Custom Authentication: Return the custom content as headers
- headers.update(authentication.content)
- return headers
- # This function code from the Open Source Project TaskingAI.
- # The original code can be found at: https://github.com/TaskingAI/TaskingAI
- def _process_parameters(schema: Dict[str, ActionParam], parameters: Dict) -> Dict:
- """
- Processes parameters based on their schema and provided values.
- :param schema: A dictionary representing the parameter schema.
- :param parameters: A dictionary of provided parameter values.
- :return: A dictionary of processed parameters.
- """
- processed_params = {}
- for param_name, action_param in schema.items():
- param_value = None
- # Check for the presence of 'enum' in the parameter schema
- if action_param.is_single_value_enum():
- param_value = action_param.enum[0]
- elif param_name in parameters:
- param_value = parameters[param_name]
- if param_value is not None:
- processed_params[param_name] = param_value
- # todo: check if required
- # if action_param.required and param_value is None:
- # raise_http_error(ErrorCode.REQUEST_VALIDATION_ERROR, message=f"Missing required parameter {param_name}")
- return processed_params
- # This function code from the Open Source Project TaskingAI.
- # The original code can be found at: https://github.com/TaskingAI/TaskingAI
- def call_action_api(
- url: str,
- method: ActionMethod,
- path_param_schema: Dict[str, ActionParam],
- query_param_schema: Dict[str, ActionParam],
- body_type: ActionBodyType,
- body_param_schema: Dict[str, ActionParam],
- parameters: Dict,
- headers: Dict,
- authentication: Authentication,
- ) -> Dict:
- """
- Call an API according to OpenAPI schema.
- :param url: the URL of the API call
- :param method: the method of the API call
- :param path_param_schema: the path parameters schema
- :param query_param_schema: the query parameters schema
- :param body_type: the body type
- :param body_param_schema: the body parameters schema
- :param parameters: the parameters input by the user
- :param headers: the extra headers to be included in the API call
- :param authentication: the authentication of the action
- :return: Response from the API call
- """
- authentication.decrypt()
- # Update URL with path parameters
- if path_param_schema:
- path_params = _process_parameters(path_param_schema, parameters)
- for param_name, param_value in path_params.items():
- url = url.replace(f"{{{param_name}}}", urllib.parse.quote(str(param_value)))
- # Prepare query parameters
- query_params = {}
- if query_param_schema:
- query_params = _process_parameters(query_param_schema, parameters)
- # cast boolean values to string
- for param_name, param_value in query_params.items():
- if isinstance(param_value, bool):
- query_params[param_name] = str(param_value).lower()
- # Prepare body
- body = None
- if body_type != ActionBodyType.NONE:
- body = _process_parameters(body_param_schema, parameters)
- # Prepare headers
- prepared_headers = _prepare_headers(authentication, headers)
- # Making the API call
- try:
- request_kwargs = {"headers": prepared_headers}
- if query_params:
- request_kwargs["params"] = query_params
- if os.environ.get("HTTP_PROXY_URL"):
- request_kwargs["proxy"] = os.environ.get("HTTP_PROXY_URL")
- if body_type == ActionBodyType.JSON:
- request_kwargs["json"] = body
- prepared_headers["Content-Type"] = "application/json"
- elif body_type == ActionBodyType.FORM:
- request_kwargs["data"] = body
- prepared_headers["Content-Type"] = "application/x-www-form-urlencoded"
- logging.info(f"call_action_api url={url} request kwargs: {request_kwargs}")
- with requests.request(method.value, url, **request_kwargs) as response:
- response_content_type = response.headers.get("Content-Type", "").lower()
- if "application/json" in response_content_type:
- data = response.json()
- else:
- data = response.text
- if response.status_code == 500:
- error_message = f"API call failed with status {response.status_code}"
- if data:
- error_message += f": {data}"
- return {"status": response.status_code, "error": error_message}
- return {"status": response.status_code, "data": data}
- except requests.exceptions.RequestException as e:
- return {"status": 500, "error": f"Failed to make the API call: {e}"}
- except Exception:
- return {"status": 500, "error": "Failed to make the API call"}
|