swarmauri / swarmauri-sdk

https://swarmauri.com
Apache License 2.0
11 stars 23 forks source link

[Bug]: RequestTool #399

Open MichaelDecent opened 4 days ago

MichaelDecent commented 4 days ago

Describe the Bug

The of value of the key response_content from the return dict value of RequestTool response.content was returning a byte. decoding it to a str solves the problem

Steps to Reproduce

  1. CreateGradio app using the ResquestTool as Tool
  2. ToolAgent as the Agent
  3. GroqModel as the LLM
  4. Toolkit as the Kit

Use the Jupyter notebook, installing the following:

Software Version

Latest (Nightly Build)

Python Version

Python 3.10

Environment Details

Output of pip freeze

Relevant Logs or Error Output

File "/home/michaeldecent/miniconda3/envs/swarmauri-0.4.dev1/lib/python3.10/site-packages/swarmauri/standard/agents/concrete/ToolAgent.py", line 40, in exec
    self.conversation = self.llm.predict(
  File "/tmp/ipykernel_529524/3642738221.py", line 72, in predict
    content=json.dumps(func_result),
  File "/home/michaeldecent/miniconda3/envs/swarmauri-0.4.dev1/lib/python3.10/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/home/michaeldecent/miniconda3/envs/swarmauri-0.4.dev1/lib/python3.10/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/home/michaeldecent/miniconda3/envs/swarmauri-0.4.dev1/lib/python3.10/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/home/michaeldecent/miniconda3/envs/swarmauri-0.4.dev1/lib/python3.10/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type bytes is not JSON serializable

Additional Context (Optional)

Potential solution for the RequestTool

# Import necessary modules
import requests
from typing import Optional, Dict, Any, Literal, List
from pydantic import BaseModel, Field, Extra
from swarmauri.standard.tools.base.ToolBase import (
    ToolBase,
)  # Assuming the location of ToolBase import
from swarmauri.standard.tools.concrete.Parameter import Parameter

class RequestsTool(ToolBase):
    """
    A tool that leverages the `requests` library to perform HTTP operations.

    Attributes:
        name (str): The name of the tool.
        description (str): A brief description of what the tool does.
    """

    name: str = "RequestsTool"
    type: Literal["RequestsTool"] = "RequestsTool"
    description: Optional[str] = (
        "A tool for making HTTP requests using the `requests` library."
    )
    parameters: List[Parameter] = Field(
        default_factory=lambda: [
            Parameter(
                name="method",
                type="string",
                description="The HTTP method to use ('get', 'post', 'put', 'delete').",
                required=True,
                enum=["get", "post", "put", "delete"],
            ),
            Parameter(
                name="url",
                type="string",
                description="The URL for the request.",
                required=True,
            ),
            Parameter(
                name="params",
                type="object",
                description="The query parameters to include in the request.",
                required=False,
            ),
            Parameter(
                name="data",
                type="object",
                description="The form data to include in the request (used in POST and PUT).",
                required=False,
            ),
            Parameter(
                name="json",
                type="object",
                description="The JSON data to include in the request (used in POST and PUT).",
                required=False,
            ),
            Parameter(
                name="headers",
                type="object",
                description="Additional headers to include in the request.",
                required=False,
            ),
        ]
    )

    def get(
        self,
        url: str,
        params: Optional[Dict[str, Any]] = None,
        headers: Optional[Dict[str, str]] = None,
    ) -> requests.Response:
        """
        Perform an HTTP GET request.

        Args:
            url (str): The URL to send the GET request to.
            params (Optional[Dict[str, Any]]): The query parameters to include in the request.
            headers (Optional[Dict[str, str]]): Additional headers to include in the request.

        Returns:
            requests.Response: The response object from the GET request.
        """
        response = requests.get(url, params=params, headers=headers)
        response.raise_for_status()  # Raise an HTTPError for bad responses (4xx and 5xx)
        return response

    def post(
        self,
        url: str,
        data: Optional[Dict[str, Any]] = None,
        json: Optional[Dict[str, Any]] = None,
        headers: Optional[Dict[str, str]] = None,
    ) -> requests.Response:
        """
        Perform an HTTP POST request.

        Args:
            url (str): The URL to send the POST request to.
            data (Optional[Dict[str, Any]]): The form data to include in the request.
            json (Optional[Dict[str, Any]]): The JSON data to include in the request.
            headers (Optional[Dict[str, str]]): Additional headers to include in the request.

        Returns:
            requests.Response: The response object from the POST request.
        """
        response = requests.post(url, data=data, json=json, headers=headers)
        response.raise_for_status()  # Raise an HTTPError for bad responses (4xx and 5xx)
        return response

    def put(
        self,
        url: str,
        data: Optional[Dict[str, Any]] = None,
        json: Optional[Dict[str, Any]] = None,
        headers: Optional[Dict[str, str]] = None,
    ) -> requests.Response:
        """
        Perform an HTTP PUT request.

        Args:
            url (str): The URL to send the PUT request to.
            data (Optional[Dict[str, Any]]): The form data to include in the request.
            json (Optional[Dict[str, Any]]): The JSON data to include in the request.
            headers (Optional[Dict[str, str]]): Additional headers to include in the request.

        Returns:
            requests.Response: The response object from the PUT request.
        """
        response = requests.put(url, data=data, json=json, headers=headers)
        response.raise_for_status()  # Raise an HTTPError for bad responses (4xx and 5xx)
        return response

    def delete(
        self, url: str, headers: Optional[Dict[str, str]] = None
    ) -> requests.Response:
        """
        Perform an HTTP DELETE request.

        Args:
            url (str): The URL to send the DELETE request to.
            headers (Optional[Dict[str, str]]): Additional headers to include in the request.

        Returns:
            requests.Response: The response object from the DELETE request.
        """
        response = requests.delete(url, headers=headers)
        response.raise_for_status()  # Raise an HTTPError for bad responses (4xx and 5xx)
        return response

    def __call__(self, method: str, url: str, **kwargs) -> Dict[str, Any]:
        """
        Calls the appropriate HTTP method (GET, POST, PUT, DELETE) based on the method argument.

        Args:
            method (str): The HTTP method to use ('get', 'post', 'put', 'delete').
            url (str): The URL for the request.
            **kwargs: Additional keyword arguments passed to the respective method.

        Returns:
            requests.Response: The response object from the HTTP request.
        """
        method = method.lower()
        if method == "get":
            response = self.get(url, **kwargs)
        elif method == "post":
            response = self.post(url, **kwargs)
        elif method == "put":
            response = self.put(url, **kwargs)
        elif method == "delete":
            response = self.delete(url, **kwargs)
        else:
            raise ValueError(f"Unsupported HTTP method: {method}")

        return {
            "response_content": (response.content).decode('utf-8'),
            "response_status": response.status_code,
        }

Confirmation