Skip to content

Commit 8ee81e1

Browse files
author
RN
committed
feat(requests): Improve usability of RequestsTool with clearer errors and docs
Addresses a common point of confusion where initializing a RequestsTool without the required `requests_wrapper` argument raises an unhelpful Pydantic `ValidationError`. This commit introduces two changes to improve the user experience: 1. A Pydantic `@model_validator` is added to the `BaseRequestsTool` to proactively check for `requests_wrapper` and raise a clear ValueError. 2. The `RequestsGetTool` docstring is updated with a usage example and explicit documentation for the required arguments. Closes: #30498
1 parent 7414609 commit 8ee81e1

File tree

2 files changed

+90
-34
lines changed
  • libs/community

2 files changed

+90
-34
lines changed

libs/community/langchain_community/tools/requests/tool.py

Lines changed: 76 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
import json
55
from typing import Any, Dict, Optional, Union
66

7-
from pydantic import BaseModel
7+
from pydantic import BaseModel, model_validator
8+
89
from langchain_core.callbacks import (
910
AsyncCallbackManagerForToolRun,
1011
CallbackManagerForToolRun,
1112
)
12-
1313
from langchain_community.utilities.requests import GenericRequestsWrapper
1414
from langchain_core.tools import BaseTool
1515

@@ -28,9 +28,20 @@ class BaseRequestsTool(BaseModel):
2828
"""Base class for requests tools."""
2929

3030
requests_wrapper: GenericRequestsWrapper
31-
3231
allow_dangerous_requests: bool = False
3332

33+
@model_validator(mode="before")
34+
@classmethod
35+
def _validate_requests_wrapper(cls, values: Dict[str, Any]) -> Dict[str, Any]:
36+
"""Validate that requests_wrapper is present."""
37+
if "requests_wrapper" not in values:
38+
raise ValueError(
39+
"`requests_wrapper` is a required argument for this tool. "
40+
"Please pass in an instance of a RequestsWrapper, e.g. "
41+
"`TextRequestsWrapper()`."
42+
)
43+
return values
44+
3445
def __init__(self, **kwargs: Any):
3546
"""Initialize the tool."""
3647
if not kwargs.get("allow_dangerous_requests", False):
@@ -48,14 +59,41 @@ def __init__(self, **kwargs: Any):
4859

4960

5061
class RequestsGetTool(BaseRequestsTool, BaseTool):
51-
"""Tool for making a GET request to an API endpoint."""
62+
"""Tool for making a GET request to an API endpoint.
5263
53-
name: str = "requests_get"
54-
description: str = """A portal to the internet. Use this when you need to get specific
55-
content from a website. Input should be a url (i.e. https://www.google.com).
56-
The output will be the text response of the GET request.
64+
.. warning::
65+
This tool can be used to make arbitrary GET requests.
66+
By default, it is unsafe and requires ``allow_dangerous_requests=True``
67+
to be passed to the constructor. Use with caution.
68+
69+
This tool requires that a ``requests_wrapper`` be provided.
70+
71+
Example:
72+
.. code-block:: python
73+
74+
from langchain_community.tools import RequestsGetTool
75+
from langchain_community.utilities import TextRequestsWrapper
76+
77+
requests_wrapper = TextRequestsWrapper()
78+
tool = RequestsGetTool(
79+
requests_wrapper=requests_wrapper,
80+
allow_dangerous_requests=True
81+
)
82+
tool.run("https://www.google.com")
83+
84+
Args:
85+
requests_wrapper: Required wrapper for making HTTP requests.
86+
allow_dangerous_requests: If True, allows potentially dangerous requests.
87+
Defaults to False.
5788
"""
5889

90+
name: str = "requests_get"
91+
description: str = (
92+
"A portal to the internet. Use this when you need to get specific content "
93+
"from a website. Input should be a url (i.e. https://www.google.com). "
94+
"The output will be the text response of the GET request."
95+
)
96+
5997
def _run(
6098
self, url: str, run_manager: Optional[CallbackManagerForToolRun] = None
6199
) -> Union[str, Dict[str, Any]]:
@@ -75,13 +113,14 @@ class RequestsPostTool(BaseRequestsTool, BaseTool):
75113
"""Tool for making a POST request to an API endpoint."""
76114

77115
name: str = "requests_post"
78-
description: str = """Use this when you want to POST to a website.
79-
Input should be a json string with two keys: "url" and "data".
80-
The value of "url" should be a string, and the value of "data" should be a dictionary of
81-
key-value pairs you want to POST to the url.
82-
Be careful to always use double quotes for strings in the json string
83-
The output will be the text response of the POST request.
84-
"""
116+
description: str = (
117+
"Use this when you want to POST to a website. "
118+
'Input should be a json string with two keys: "url" and "data". '
119+
'The value of "url" should be a string, and the value of "data" should be a '
120+
"dictionary of key-value pairs you want to POST to the url. "
121+
"Be careful to always use double quotes for strings in the json string. "
122+
"The output will be the text response of the POST request."
123+
)
85124

86125
def _run(
87126
self, text: str, run_manager: Optional[CallbackManagerForToolRun] = None
@@ -112,13 +151,14 @@ class RequestsPatchTool(BaseRequestsTool, BaseTool):
112151
"""Tool for making a PATCH request to an API endpoint."""
113152

114153
name: str = "requests_patch"
115-
description: str = """Use this when you want to PATCH to a website.
116-
Input should be a json string with two keys: "url" and "data".
117-
The value of "url" should be a string, and the value of "data" should be a dictionary of
118-
key-value pairs you want to PATCH to the url.
119-
Be careful to always use double quotes for strings in the json string
120-
The output will be the text response of the PATCH request.
121-
"""
154+
description: str = (
155+
"Use this when you want to PATCH to a website. "
156+
'Input should be a json string with two keys: "url" and "data". '
157+
'The value of "url" should be a string, and the value of "data" should be a '
158+
"dictionary of key-value pairs you want to PATCH to the url. "
159+
"Be careful to always use double quotes for strings in the json string. "
160+
"The output will be the text response of the PATCH request."
161+
)
122162

123163
def _run(
124164
self, text: str, run_manager: Optional[CallbackManagerForToolRun] = None
@@ -149,13 +189,14 @@ class RequestsPutTool(BaseRequestsTool, BaseTool):
149189
"""Tool for making a PUT request to an API endpoint."""
150190

151191
name: str = "requests_put"
152-
description: str = """Use this when you want to PUT to a website.
153-
Input should be a json string with two keys: "url" and "data".
154-
The value of "url" should be a string, and the value of "data" should be a dictionary of
155-
key-value pairs you want to PUT to the url.
156-
Be careful to always use double quotes for strings in the json string.
157-
The output will be the text response of the PUT request.
158-
"""
192+
description: str = (
193+
"Use this when you want to PUT to a website. "
194+
'Input should be a json string with two keys: "url" and "data". '
195+
'The value of "url" should be a string, and the value of "data" should be a '
196+
"dictionary of key-value pairs you want to PUT to the url. "
197+
"Be careful to always use double quotes for strings in the json string. "
198+
"The output will be the text response of the PUT request."
199+
)
159200

160201
def _run(
161202
self, text: str, run_manager: Optional[CallbackManagerForToolRun] = None
@@ -186,11 +227,12 @@ class RequestsDeleteTool(BaseRequestsTool, BaseTool):
186227
"""Tool for making a DELETE request to an API endpoint."""
187228

188229
name: str = "requests_delete"
189-
description: str = """A portal to the internet.
190-
Use this when you need to make a DELETE request to a URL.
191-
Input should be a specific url, and the output will be the text
192-
response of the DELETE request.
193-
"""
230+
description: str = (
231+
"A portal to the internet. "
232+
"Use this when you need to make a DELETE request to a URL. "
233+
"Input should be a specific url, and the output will be the text "
234+
"response of the DELETE request."
235+
)
194236

195237
def _run(
196238
self,

libs/community/tests/unit_tests/tools/requests/test_tool.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,17 @@ def test_requests_delete_tool_json(
216216
assert asyncio.run(tool.arun("https://example.com")) == {
217217
"response": "adelete_response"
218218
}
219+
220+
221+
def test_requests_tool_missing_wrapper_raises_error() -> None:
222+
"""Test that initializing a requests tool without a wrapper raises a ValueError.
223+
224+
Ensures that our custom Pydantic validator produces a clear error message when
225+
requests_wrapper is missing.
226+
"""
227+
from langchain_community.tools.requests.tool import RequestsGetTool
228+
229+
with pytest.raises(ValueError) as exc_info:
230+
RequestsGetTool(allow_dangerous_requests=True)
231+
232+
assert "`requests_wrapper` is a required argument" in str(exc_info.value)

0 commit comments

Comments
 (0)