You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When using API Request as a Tool connected to an Agent the API Request component wasn't able to process a cURL request that worked when the API Request was not in Tool mode (grabbed the formatted cURL request from a LangFuse trace to make sure I was testing the exact same request.
Motivation
Fixing the API Request to work as in Tool mode.
Your Contribution
Here is the complete code for the modified API Request that works when running Tool mode. I have not tested this code in non-tool mode.
importasyncioimportjsonfromtypingimportAnyfromurllib.parseimportparse_qsl, urlencode, urlparse, urlunparseimporthttpxfromloguruimportloggerfromlangflow.base.curl.parseimportparse_contextfromlangflow.customimportComponentfromlangflow.ioimportDataInput, DropdownInput, IntInput, MessageTextInput, NestedDictInput, Outputfromlangflow.schemaimportDatafromlangflow.schema.dotdictimportdotdictclassAPIRequestComponent(Component):
display_name="API Request - Fixed"description= (
"This component allows you to make HTTP requests to one or more URLs. ""You can provide headers and body as either dictionaries or Data objects. ""Additionally, you can append query parameters to the URLs.\n\n""**Note:** Check advanced options for more settings."
)
icon="Globe"name="APIRequest"inputs= [
MessageTextInput(
name="urls",
display_name="URLs",
is_list=True,
info="Enter one or more URLs, separated by commas.",
),
MessageTextInput(
name="curl",
display_name="cURL",
info="Paste a curl command to populate the fields. ""This will fill in the dictionary fields for headers and body.",
advanced=False,
refresh_button=True,
real_time_refresh=True,
tool_mode=True,
),
DropdownInput(
name="method",
display_name="Method",
options=["GET", "POST", "PATCH", "PUT"],
value="GET",
info="The HTTP method to use (GET, POST, PATCH, PUT).",
),
NestedDictInput(
name="headers",
display_name="Headers",
info="The headers to send with the request as a dictionary. This is populated when using the CURL field.",
input_types=["Data"],
),
NestedDictInput(
name="body",
display_name="Body",
info="The body to send with the request as a dictionary (for POST, PATCH, PUT). ""This is populated when using the CURL field.",
input_types=["Data"],
),
DataInput(
name="query_params",
display_name="Query Parameters",
info="The query parameters to append to the URL.",
tool_mode=True,
),
IntInput(
name="timeout",
display_name="Timeout",
value=5,
info="The timeout to use for the request.",
),
]
outputs= [
Output(display_name="Data", name="data", method="make_requests"),
]
defparse_curl(self, curl: str, build_config: dotdict) ->dotdict:
try:
parsed=parse_context(curl)
build_config["urls"]["value"] = [parsed.url]
build_config["method"]["value"] =parsed.method.upper()
build_config["headers"]["value"] =dict(parsed.headers)
ifparsed.data:
try:
json_data=json.loads(parsed.data)
build_config["body"]["value"] =json_dataexceptjson.JSONDecodeError:
logger.exception("Error decoding JSON data")
else:
build_config["body"]["value"] = {}
exceptExceptionasexc:
msg=f"Error parsing curl: {exc}"logger.exception(msg)
raiseValueError(msg) fromexcreturnbuild_configdefupdate_build_config(self, build_config: dotdict, field_value: Any, field_name: str|None=None):
iffield_name=="curl"andfield_value:
build_config=self.parse_curl(field_value, build_config)
returnbuild_configasyncdefmake_request(
self,
client: httpx.AsyncClient,
method: str,
url: str,
headers: dict|None=None,
body: dict|None=None,
timeout: int=5,
) ->Data:
method=method.upper()
ifmethodnotin {"GET", "POST", "PATCH", "PUT", "DELETE"}:
msg=f"Unsupported method: {method}"raiseValueError(msg)
ifisinstance(body, str) andbody:
try:
body=json.loads(body)
exceptExceptionase:
msg=f"Error decoding JSON data: {e}"logger.exception(msg)
body=NoneraiseValueError(msg) fromedata=bodyorNonetry:
response=awaitclient.request(method, url, headers=headers, json=data, timeout=timeout)
try:
result=response.json()
exceptException: # noqa: BLE001logger.opt(exception=True).debug("Error decoding JSON response")
result=response.textreturnData(
data={
"source": url,
"headers": headers,
"status_code": response.status_code,
"result": result,
},
)
excepthttpx.TimeoutException:
returnData(
data={
"source": url,
"headers": headers,
"status_code": 408,
"error": "Request timed out",
},
)
exceptExceptionasexc: # noqa: BLE001logger.opt(exception=True).debug(f"Error making request to {url}")
returnData(
data={
"source": url,
"headers": headers,
"status_code": 500,
"error": str(exc),
},
)
defadd_query_params(self, url: str, params: dict) ->str:
url_parts=list(urlparse(url))
query=dict(parse_qsl(url_parts[4]))
query.update(params)
url_parts[4] =urlencode(query)
returnurlunparse(url_parts)
asyncdefmake_requests(self) ->list[Data]:
# Parse curl first if presentcurl=self.curlifcurl:
try:
parsed=parse_context(curl)
# Directly set the values instead of using build_configmethod=parsed.method.upper()
urls= [parsed.url] ifparsed.urlelse []
headers=dict(parsed.headers) ifparsed.headerselse {}
ifparsed.data:
try:
body=json.loads(parsed.data)
exceptjson.JSONDecodeErrorase:
logger.error(f"Error decoding JSON data: {e}")
body= {}
else:
body= {}
exceptExceptionasexc:
logger.exception(f"Error parsing curl: {exc}")
# Fall back to regular parameters if curl parsing failsmethod=self.methodurls= [url.strip() forurlinself.urlsifurl.strip()]
headers=self.headersor {}
body=self.bodyor {}
else:
# Use regular parameters if no curlmethod=self.methodurls= [url.strip() forurlinself.urlsifurl.strip()]
headers=self.headersor {}
body=self.bodyor {}
timeout=self.timeout# Handle query parametersifisinstance(self.query_params, str):
query_params=dict(parse_qsl(self.query_params))
else:
query_params=self.query_params.dataifself.query_paramselse {}
# Handle Data objectsifisinstance(headers, Data):
headers=headers.dataifisinstance(body, Data):
body=body.databodies= [body] *len(urls)
urls= [self.add_query_params(url, query_params) forurlinurls]
asyncwithhttpx.AsyncClient() asclient:
results=awaitasyncio.gather(
*[
self.make_request(client, method, u, headers, rec, timeout)
foru, recinzip(urls, bodies, strict=True)
]
)
self.status=resultsreturnresultsdefupdate_build_config(self, build_config: dotdict, field_value: Any, field_name: str|None=None):
iffield_name=="curl"andfield_value:
try:
parsed=parse_context(field_value)
# Update the build_config values directlybuild_config.urls.value= [parsed.url] ifparsed.urlelse []
build_config.method.value=parsed.method.upper()
build_config.headers.value=dict(parsed.headers) ifparsed.headerselse {}
ifparsed.data:
try:
json_data=json.loads(parsed.data)
build_config.body.value=json_dataexceptjson.JSONDecodeError:
logger.exception("Error decoding JSON data")
build_config.body.value= {}
else:
build_config.body.value= {}
exceptExceptionasexc:
logger.exception(f"Error parsing curl: {exc}")
# Don't modify build_config if parsing failspassreturnbuild_config
The text was updated successfully, but these errors were encountered:
Feature Request
When using API Request as a Tool connected to an Agent the API Request component wasn't able to process a cURL request that worked when the API Request was not in Tool mode (grabbed the formatted cURL request from a LangFuse trace to make sure I was testing the exact same request.
Motivation
Fixing the API Request to work as in Tool mode.
Your Contribution
Here is the complete code for the modified API Request that works when running Tool mode. I have not tested this code in non-tool mode.
The text was updated successfully, but these errors were encountered: