fix: API Request to use new fields and add DataInputs for Body,Header and Query

This commit is contained in:
Cezar Vasconcelos 2024-06-19 20:54:58 +00:00
commit 3b6060b243

View file

@ -1,20 +1,26 @@
import asyncio import asyncio
import json import json
from typing import Any, List, Optional from typing import Any, List, Optional
from urllib.parse import urlencode, urlparse, parse_qsl, urlunparse
import httpx import httpx
from loguru import logger from loguru import logger
from langflow.base.curl.parse import parse_context from langflow.base.curl.parse import parse_context
from langflow.custom import Component from langflow.custom import Component
from langflow.io import DropdownInput, IntInput, NestedDictInput, Output, TextInput from langflow.io import DropdownInput, IntInput, NestedDictInput, Output, TextInput, DataInput
from langflow.schema import Data from langflow.schema import Data
from langflow.schema.dotdict import dotdict from langflow.schema.dotdict import dotdict
class APIRequestComponent(Component): class APIRequestComponent(Component):
display_name = "API Request" display_name = "API Request"
description = "Make HTTP requests given one or more URLs." 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" icon = "Globe"
inputs = [ inputs = [
@ -27,7 +33,7 @@ class APIRequestComponent(Component):
TextInput( TextInput(
name="curl", name="curl",
display_name="Curl", display_name="Curl",
info="Paste a curl command to populate the fields.", info="Paste a curl command to populate the fields. This will fill in the dictionary fields for headers and body.",
advanced=False, advanced=False,
refresh_button=True, refresh_button=True,
), ),
@ -36,17 +42,34 @@ class APIRequestComponent(Component):
display_name="Method", display_name="Method",
options=["GET", "POST", "PATCH", "PUT"], options=["GET", "POST", "PATCH", "PUT"],
value="GET", value="GET",
info="The HTTP method to use.", info="The HTTP method to use (GET, POST, PATCH, PUT).",
), ),
NestedDictInput( DataInput(
name="headers", name="headers",
display_name="Headers", display_name="Headers Data",
info="The headers to send with the request.", info="The headers to send with the request as a Data object.",
), ),
NestedDictInput( NestedDictInput(
name="headers_dict",
display_name="Headers Dictionary",
info="The headers to send with the request as a dictionary. This is populated when using the CURL field.",
advanced=True,
),
DataInput(
name="body", name="body",
display_name="Body", display_name="Body Data",
info="The body to send with the request (for POST, PATCH, PUT).", info="The body to send with the request as a Data object (for POST, PATCH, PUT).",
),
NestedDictInput(
name="body_dict",
display_name="Body Dictionary",
info="The body to send with the request as a dictionary (for POST, PATCH, PUT). This is populated when using the CURL field.",
advanced=True,
),
DataInput(
name="query_params",
display_name="Query Parameters",
info="The query parameters to append to the URL.",
), ),
IntInput( IntInput(
name="timeout", name="timeout",
@ -65,13 +88,16 @@ class APIRequestComponent(Component):
parsed = parse_context(curl) parsed = parse_context(curl)
build_config["urls"]["value"] = [parsed.url] build_config["urls"]["value"] = [parsed.url]
build_config["method"]["value"] = parsed.method.upper() build_config["method"]["value"] = parsed.method.upper()
build_config["headers"]["value"] = dict(parsed.headers) build_config["headers_dict"]["value"] = dict(parsed.headers)
try: if parsed.data:
json_data = json.loads(parsed.data) try:
build_config["body"]["value"] = json_data json_data = json.loads(parsed.data)
except json.JSONDecodeError as e: build_config["body_dict"]["value"] = json_data
print(e) except json.JSONDecodeError as e:
logger.error(f"Error decoding JSON data: {e}")
else:
build_config["body_dict"]["value"] = None
except Exception as exc: except Exception as exc:
logger.error(f"Error parsing curl: {exc}") logger.error(f"Error parsing curl: {exc}")
raise ValueError(f"Error parsing curl: {exc}") raise ValueError(f"Error parsing curl: {exc}")
@ -80,6 +106,11 @@ class APIRequestComponent(Component):
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None): def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
if field_name == "curl" and field_value: if field_name == "curl" and field_value:
build_config = self.parse_curl(field_value, build_config) build_config = self.parse_curl(field_value, build_config)
# Apply the parsed values to the component fields
self.urls = build_config["urls"]["value"]
self.method = build_config["method"]["value"]
self.headers_dict = build_config["headers"]["value"]
self.body_dict = build_config["body"]["value"]
return build_config return build_config
async def make_request( async def make_request(
@ -96,7 +127,7 @@ class APIRequestComponent(Component):
raise ValueError(f"Unsupported method: {method}") raise ValueError(f"Unsupported method: {method}")
data = body if body else None data = body if body else None
payload = json.dumps(data) payload = json.dumps(data) if data else None
try: try:
response = await client.request(method, url, headers=headers, content=payload, timeout=timeout) response = await client.request(method, url, headers=headers, content=payload, timeout=timeout)
try: try:
@ -130,36 +161,38 @@ class APIRequestComponent(Component):
}, },
) )
def add_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)
return urlunparse(url_parts)
async def make_requests(self) -> List[Data]: async def make_requests(self) -> List[Data]:
method = self.method method = self.method
urls = [url.strip() for url in self.urls if url.strip()] urls = [url.strip() for url in self.urls if url.strip()]
curl = self.curl curl = self.curl
headers = self.headers or {} headers = self.headers if self.headers else (self.headers_dict or {})
body = self.body or {} body = self.body if self.body else (self.body_dict or {})
timeout = self.timeout timeout = self.timeout
query_params = self.query_params.data if self.query_params else {}
if curl: if curl:
self._build_config = self.parse_curl(curl) self._build_config = self.parse_curl(curl, dotdict())
if isinstance(headers, Data): if isinstance(headers, Data):
headers_dict = headers.data headers = headers.data
else:
headers_dict = headers
bodies = [] if isinstance(body, Data):
if body: body = body.data
if not isinstance(body, list):
bodies = [body]
else:
bodies = body
bodies = [b.data if isinstance(b, Data) else b for b in bodies]
if len(urls) != len(bodies): bodies = [body] * len(urls)
bodies += [None] * (len(urls) - len(bodies))
urls = [self.add_query_params(url, query_params) for url in urls]
async with httpx.AsyncClient() as client: async with httpx.AsyncClient() as client:
results = await asyncio.gather( results = await asyncio.gather(
*[self.make_request(client, method, u, headers_dict, rec, timeout) for u, rec in zip(urls, bodies)] *[self.make_request(client, method, u, headers, rec, timeout) for u, rec in zip(urls, bodies)]
) )
self.status = results self.status = results
return results return results