From 7791d92711befb0a402e349afad4c3ca30559b96 Mon Sep 17 00:00:00 2001 From: Jacob Lee Date: Mon, 1 Jul 2024 15:02:14 -0700 Subject: [PATCH] community[patch]: Fix requests alias for load_tools (#23734) CC @baskaryan --- docs/docs/integrations/tools/requests.ipynb | 56 +++++++++---------- .../agent_toolkits/load_tools.py | 40 +++++++------ .../agent_toolkits/test_load_tools.py | 18 ++++++ 3 files changed, 66 insertions(+), 48 deletions(-) create mode 100644 libs/community/tests/unit_tests/agent_toolkits/test_load_tools.py diff --git a/docs/docs/integrations/tools/requests.ipynb b/docs/docs/integrations/tools/requests.ipynb index 0f41f8ea989..b0a9ec5ad9d 100644 --- a/docs/docs/integrations/tools/requests.ipynb +++ b/docs/docs/integrations/tools/requests.ipynb @@ -12,38 +12,40 @@ }, { "cell_type": "code", - "execution_count": 3, - "id": "5d8764ba", + "execution_count": null, + "id": "8a84d1fd", "metadata": {}, "outputs": [], "source": [ - "from langchain.agents import load_tools\n", - "\n", - "requests_tools = load_tools([\"requests_all\"])" + "%pip install --upgrade --quiet langchain-community" ] }, { "cell_type": "code", - "execution_count": 6, - "id": "bc5edde2", + "execution_count": 3, + "id": "5d8764ba", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[RequestsGetTool(name='requests_get', description='A portal to the internet. Use this when you need to get specific content from a website. Input should be a url (i.e. https://www.google.com). The output will be the text response of the GET request.', args_schema=None, return_direct=False, verbose=False, callbacks=None, callback_manager=None, requests_wrapper=TextRequestsWrapper(headers=None, aiosession=None)),\n", - " RequestsPostTool(name='requests_post', description='Use this when you want to POST to a website.\\n Input should be a json string with two keys: \"url\" and \"data\".\\n The value of \"url\" should be a string, and the value of \"data\" should be a dictionary of \\n key-value pairs you want to POST to the url.\\n Be careful to always use double quotes for strings in the json string\\n The output will be the text response of the POST request.\\n ', args_schema=None, return_direct=False, verbose=False, callbacks=None, callback_manager=None, requests_wrapper=TextRequestsWrapper(headers=None, aiosession=None)),\n", - " RequestsPatchTool(name='requests_patch', description='Use this when you want to PATCH to a website.\\n Input should be a json string with two keys: \"url\" and \"data\".\\n The value of \"url\" should be a string, and the value of \"data\" should be a dictionary of \\n key-value pairs you want to PATCH to the url.\\n Be careful to always use double quotes for strings in the json string\\n The output will be the text response of the PATCH request.\\n ', args_schema=None, return_direct=False, verbose=False, callbacks=None, callback_manager=None, requests_wrapper=TextRequestsWrapper(headers=None, aiosession=None)),\n", - " RequestsPutTool(name='requests_put', description='Use this when you want to PUT to a website.\\n Input should be a json string with two keys: \"url\" and \"data\".\\n The value of \"url\" should be a string, and the value of \"data\" should be a dictionary of \\n key-value pairs you want to PUT to the url.\\n Be careful to always use double quotes for strings in the json string.\\n The output will be the text response of the PUT request.\\n ', args_schema=None, return_direct=False, verbose=False, callbacks=None, callback_manager=None, requests_wrapper=TextRequestsWrapper(headers=None, aiosession=None)),\n", - " RequestsDeleteTool(name='requests_delete', description='A portal to the internet. Use this when you need to make a DELETE request to a URL. Input should be a specific url, and the output will be the text response of the DELETE request.', args_schema=None, return_direct=False, verbose=False, callbacks=None, callback_manager=None, requests_wrapper=TextRequestsWrapper(headers=None, aiosession=None))]" + "[RequestsGetTool(requests_wrapper=TextRequestsWrapper(headers=None, aiosession=None, auth=None, response_content_type='text', verify=True), allow_dangerous_requests=True),\n", + " RequestsPostTool(requests_wrapper=TextRequestsWrapper(headers=None, aiosession=None, auth=None, response_content_type='text', verify=True), allow_dangerous_requests=True),\n", + " RequestsPatchTool(requests_wrapper=TextRequestsWrapper(headers=None, aiosession=None, auth=None, response_content_type='text', verify=True), allow_dangerous_requests=True),\n", + " RequestsPutTool(requests_wrapper=TextRequestsWrapper(headers=None, aiosession=None, auth=None, response_content_type='text', verify=True), allow_dangerous_requests=True),\n", + " RequestsDeleteTool(requests_wrapper=TextRequestsWrapper(headers=None, aiosession=None, auth=None, response_content_type='text', verify=True), allow_dangerous_requests=True)]" ] }, - "execution_count": 6, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "from langchain_community.agent_toolkits.load_tools import load_tools\n", + "\n", + "requests_tools = load_tools([\"requests_all\"], allow_dangerous_tools=True)\n", + "\n", "requests_tools" ] }, @@ -60,17 +62,17 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 4, "id": "c56d4678", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "TextRequestsWrapper(headers=None, aiosession=None)" + "TextRequestsWrapper(headers=None, aiosession=None, auth=None, response_content_type='text', verify=True)" ] }, - "execution_count": 8, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -80,28 +82,16 @@ "requests_tools[0].requests_wrapper" ] }, - { - "cell_type": "code", - "execution_count": 4, - "id": "81aae09e", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_community.utilities import TextRequestsWrapper\n", - "\n", - "requests = TextRequestsWrapper()" - ] - }, { "cell_type": "code", "execution_count": 5, - "id": "fd210142", + "id": "81aae09e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'Google

\"Google\"

 

Advanced search

© 2023 - Privacy - Terms

'" + "'Google

\"Google\"

 

Advanced search

© 2024 - Privacy - Terms

'" ] }, "execution_count": 5, @@ -110,6 +100,10 @@ } ], "source": [ + "from langchain_community.utilities import TextRequestsWrapper\n", + "\n", + "requests = TextRequestsWrapper()\n", + "\n", "requests.get(\"https://www.google.com\")" ] }, @@ -123,7 +117,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 6, "id": "3f27ee3d", "metadata": {}, "outputs": [ diff --git a/libs/community/langchain_community/agent_toolkits/load_tools.py b/libs/community/langchain_community/agent_toolkits/load_tools.py index cc72afd2325..18df5b3dee1 100644 --- a/libs/community/langchain_community/agent_toolkits/load_tools.py +++ b/libs/community/langchain_community/agent_toolkits/load_tools.py @@ -626,6 +626,25 @@ def load_huggingface_tool( ) +def raise_dangerous_tools_exception(name: str) -> None: + raise ValueError( + f"{name} is a dangerous tool. You cannot use it without opting in " + "by setting allow_dangerous_tools to True. " + "Most tools have some inherit risk to them merely because they are " + 'allowed to interact with the "real world".' + "Please refer to LangChain security guidelines " + "to https://python.langchain.com/docs/security." + "Some tools have been designated as dangerous because they pose " + "risk that is not intuitively obvious. For example, a tool that " + "allows an agent to make requests to the web, can also be used " + "to make requests to a server that is only accessible from the " + "server hosting the code." + "Again, all tools carry some risk, and it's your responsibility to " + "understand which tools you're using and the risks associated with " + "them." + ) + + def load_tools( tool_names: List[str], llm: Optional[BaseLanguageModel] = None, @@ -684,22 +703,7 @@ def load_tools( ) for name in tool_names: if name in DANGEROUS_TOOLS and not allow_dangerous_tools: - raise ValueError( - f"{name} is a dangerous tool. You cannot use it without opting in " - "by setting allow_dangerous_tools to True. " - "Most tools have some inherit risk to them merely because they are " - 'allowed to interact with the "real world".' - "Please refer to LangChain security guidelines " - "to https://python.langchain.com/docs/security." - "Some tools have been designated as dangerous because they pose " - "risk that is not intuitively obvious. For example, a tool that " - "allows an agent to make requests to the web, can also be used " - "to make requests to a server that is only accessible from the " - "server hosting the code." - "Again, all tools carry some risk, and it's your responsibility to " - "understand which tools you're using and the risks associated with " - "them." - ) + raise_dangerous_tools_exception(name) if name in {"requests"}: warnings.warn( @@ -708,8 +712,10 @@ def load_tools( ) if name == "requests_all": # expand requests into various methods + if not allow_dangerous_tools: + raise_dangerous_tools_exception(name) requests_method_tools = [ - _tool for _tool in _BASE_TOOLS if _tool.startswith("requests_") + _tool for _tool in DANGEROUS_TOOLS if _tool.startswith("requests_") ] tool_names.extend(requests_method_tools) elif name in _BASE_TOOLS: diff --git a/libs/community/tests/unit_tests/agent_toolkits/test_load_tools.py b/libs/community/tests/unit_tests/agent_toolkits/test_load_tools.py new file mode 100644 index 00000000000..21c2df7ba36 --- /dev/null +++ b/libs/community/tests/unit_tests/agent_toolkits/test_load_tools.py @@ -0,0 +1,18 @@ +from langchain_community.agent_toolkits.load_tools import load_tools +from langchain_community.tools.requests.tool import ( + RequestsDeleteTool, + RequestsGetTool, + RequestsPatchTool, + RequestsPostTool, + RequestsPutTool, +) + + +def test_load_request_tools() -> None: + request_tools = load_tools(["requests_all"], allow_dangerous_tools=True) + assert len(request_tools) == 5 + assert any(isinstance(tool, RequestsDeleteTool) for tool in request_tools) + assert any(isinstance(tool, RequestsGetTool) for tool in request_tools) + assert any(isinstance(tool, RequestsPatchTool) for tool in request_tools) + assert any(isinstance(tool, RequestsPostTool) for tool in request_tools) + assert any(isinstance(tool, RequestsPutTool) for tool in request_tools)