From d0aa3261f134eabbcf0ce4046ba127db67305b62 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 11:04:41 -0300
Subject: [PATCH 01/32] =?UTF-8?q?=F0=9F=90=9B=20fix(flows.py):=20change=20?=
=?UTF-8?q?json.loads=20to=20orjson.loads=20for=20improved=20performance?=
=?UTF-8?q?=20and=20compatibility=20with=20orjson=20library=20=F0=9F=90=9B?=
=?UTF-8?q?=20fix(schemas.py):=20change=20json.dumps=20to=20orjson=5Fdumps?=
=?UTF-8?q?=20for=20improved=20performance=20and=20compatibility=20with=20?=
=?UTF-8?q?orjson=20library=20=F0=9F=90=9B=20fix(utils.py):=20change=20jso?=
=?UTF-8?q?n.loads=20to=20orjson.loads=20for=20improved=20performance=20an?=
=?UTF-8?q?d=20compatibility=20with=20orjson=20library=20=F0=9F=90=9B=20fi?=
=?UTF-8?q?x(loading.py):=20change=20json.loads=20to=20orjson.loads=20for?=
=?UTF-8?q?=20improved=20performance=20and=20compatibility=20with=20orjson?=
=?UTF-8?q?=20library=20=F0=9F=90=9B=20fix(utils.py):=20change=20json.load?=
=?UTF-8?q?s=20to=20orjson.loads=20for=20improved=20performance=20and=20co?=
=?UTF-8?q?mpatibility=20with=20orjson=20library=20=F0=9F=90=9B=20fix(vect?=
=?UTF-8?q?or=5Fstore.py):=20change=20json.loads=20to=20orjson.loads=20for?=
=?UTF-8?q?=20improved=20performance=20and=20compatibility=20with=20orjson?=
=?UTF-8?q?=20library=20=F0=9F=90=9B=20fix(types.py):=20change=20json.load?=
=?UTF-8?q?s=20to=20orjson.loads=20for=20improved=20performance=20and=20co?=
=?UTF-8?q?mpatibility=20with=20orjson=20library=20=F0=9F=90=9B=20fix(proc?=
=?UTF-8?q?ess.py):=20change=20json.loads=20to=20orjson.loads=20for=20impr?=
=?UTF-8?q?oved=20performance=20and=20compatibility=20with=20orjson=20libr?=
=?UTF-8?q?ary=20=E2=9C=A8=20feat(server.ts):=20change=20port=20variable?=
=?UTF-8?q?=20case=20from=20lowercase=20port=20to=20uppercase=20PORT=20to?=
=?UTF-8?q?=20improve=20semantics=20=E2=9C=A8=20feat(server.ts):=20add=20s?=
=?UTF-8?q?upport=20for=20process.env.PORT=20environment=20variable=20to?=
=?UTF-8?q?=20be=20able=20to=20run=20app=20on=20a=20configurable=20port?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
🔧 fix(base.py): import orjson instead of json to improve performance and compatibility
🔧 fix(frontend_node/llms.py): use orjson_dumps instead of json.dumps to improve performance and compatibility
🔧 fix(frontend_node/utilities.py): use orjson_dumps instead of json.dumps to improve performance and compatibility
🔧 fix(test_cache.py): import orjson and use orjson_dumps instead of json.dumps to improve performance and compatibility
🔧 fix(test_database.py): import correct json encoder and decoder functions to fix import errors
🔧 fix(test_database.py): replace json.dumps and json.loads with orjson_dumps and orjson.loads for better performance and compatibility
🔧 fix(test_loading.py): remove unused import statement
---
src/backend/langflow/api/v1/flows.py | 6 +-
src/backend/langflow/api/v1/schemas.py | 4 +-
src/backend/langflow/cache/utils.py | 5 +-
src/backend/langflow/chat/manager.py | 3 +-
src/backend/langflow/database/models/base.py | 17 +-
.../langflow/interface/agents/prebuilt.py | 3 +-
.../langflow/interface/initialize/loading.py | 6 +-
.../langflow/interface/initialize/utils.py | 6 +-
.../interface/initialize/vector_store.py | 3 +-
src/backend/langflow/interface/types.py | 21 +-
src/backend/langflow/processing/process.py | 2 +-
.../langflow/services/settings/base.py | 222 ++++++++++++++++++
src/backend/langflow/settings.py | 4 +-
.../langflow/template/frontend_node/llms.py | 4 +-
.../template/frontend_node/utilities.py | 4 +-
tests/test_cache.py | 6 +-
tests/test_database.py | 29 +--
tests/test_loading.py | 1 -
18 files changed, 291 insertions(+), 55 deletions(-)
create mode 100644 src/backend/langflow/services/settings/base.py
diff --git a/src/backend/langflow/api/v1/flows.py b/src/backend/langflow/api/v1/flows.py
index 9f5042fcb..2ff57f4ef 100644
--- a/src/backend/langflow/api/v1/flows.py
+++ b/src/backend/langflow/api/v1/flows.py
@@ -1,5 +1,6 @@
from typing import List
from uuid import UUID
+from fastapi.encoders import jsonable_encoder
from langflow.settings import settings
from langflow.api.utils import remove_api_keys
from langflow.api.v1.schemas import FlowListCreate, FlowListRead
@@ -11,12 +12,11 @@ from langflow.database.models.flow import (
FlowUpdate,
)
from langflow.database.base import get_session
+import orjson
from sqlmodel import Session, select
from fastapi import APIRouter, Depends, HTTPException
-from fastapi.encoders import jsonable_encoder
from fastapi import File, UploadFile
-import json
# build router
router = APIRouter(prefix="/flows", tags=["Flows"])
@@ -105,7 +105,7 @@ async def upload_file(
):
"""Upload flows from a file."""
contents = await file.read()
- data = json.loads(contents)
+ data = orjson.loads(contents)
if "flows" in data:
flow_list = FlowListCreate(**data)
else:
diff --git a/src/backend/langflow/api/v1/schemas.py b/src/backend/langflow/api/v1/schemas.py
index 0148dac6d..bbff0de4a 100644
--- a/src/backend/langflow/api/v1/schemas.py
+++ b/src/backend/langflow/api/v1/schemas.py
@@ -1,9 +1,9 @@
from enum import Enum
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
+from langflow.database.models.base import orjson_dumps
from langflow.database.models.flow import FlowCreate, FlowRead
from pydantic import BaseModel, Field, validator
-import json
class BuildStatus(Enum):
@@ -115,7 +115,7 @@ class StreamData(BaseModel):
data: dict
def __str__(self) -> str:
- return f"event: {self.event}\ndata: {json.dumps(self.data)}\n\n"
+ return f"event: {self.event}\ndata: {orjson_dumps(self.data)}\n\n"
class CustomComponentCode(BaseModel):
diff --git a/src/backend/langflow/cache/utils.py b/src/backend/langflow/cache/utils.py
index 3deabe9f4..cd282d2e9 100644
--- a/src/backend/langflow/cache/utils.py
+++ b/src/backend/langflow/cache/utils.py
@@ -2,13 +2,13 @@ import base64
import contextlib
import functools
import hashlib
-import json
import os
import tempfile
from collections import OrderedDict
from pathlib import Path
from typing import Any, Dict
from appdirs import user_cache_dir
+from langflow.database.models.base import orjson_dumps
CACHE: Dict[str, Any] = {}
@@ -76,7 +76,8 @@ def clear_old_cache_files(max_cache_size: int = 3):
def compute_dict_hash(graph_data):
graph_data = filter_json(graph_data)
- cleaned_graph_json = json.dumps(graph_data, sort_keys=True)
+ cleaned_graph_json = orjson_dumps(graph_data, sort_keys=True)
+
return hashlib.sha256(cleaned_graph_json.encode("utf-8")).hexdigest()
diff --git a/src/backend/langflow/chat/manager.py b/src/backend/langflow/chat/manager.py
index 2c3427a12..24e2212b8 100644
--- a/src/backend/langflow/chat/manager.py
+++ b/src/backend/langflow/chat/manager.py
@@ -9,7 +9,6 @@ from langflow.utils.logger import logger
import asyncio
-import json
from typing import Any, Dict, List
from langflow.cache.flow import InMemoryCache
@@ -186,7 +185,7 @@ class ChatManager:
while True:
json_payload = await websocket.receive_json()
try:
- payload = json.loads(json_payload)
+ payload = orjson.loads(json_payload)
except TypeError:
payload = json_payload
if "clear_history" in payload:
diff --git a/src/backend/langflow/database/models/base.py b/src/backend/langflow/database/models/base.py
index e20895b93..a70999206 100644
--- a/src/backend/langflow/database/models/base.py
+++ b/src/backend/langflow/database/models/base.py
@@ -2,9 +2,20 @@ from sqlmodel import SQLModel
import orjson
-def orjson_dumps(v, *, default):
- # orjson.dumps returns bytes, to match standard json.dumps we need to decode
- return orjson.dumps(v, default=default).decode()
+def orjson_dumps(v, *, default=None, sort_keys=False, indent_2=True):
+ option = orjson.OPT_SORT_KEYS if sort_keys else None
+ if indent_2:
+ # orjson.dumps returns bytes, to match standard json.dumps we need to decode
+ # option
+ # To modify how data is serialized, specify option. Each option is an integer constant in orjson.
+ # To specify multiple options, mask them together, e.g., option=orjson.OPT_STRICT_INTEGER | orjson.OPT_NAIVE_UTC
+ if option is None:
+ option = orjson.OPT_INDENT_2
+ else:
+ option |= orjson.OPT_INDENT_2
+ if default is None:
+ return orjson.dumps(v, option=option).decode()
+ return orjson.dumps(v, default=default, option=option).decode()
class SQLModelSerializable(SQLModel):
diff --git a/src/backend/langflow/interface/agents/prebuilt.py b/src/backend/langflow/interface/agents/prebuilt.py
index 5b81b7713..1c62918d9 100644
--- a/src/backend/langflow/interface/agents/prebuilt.py
+++ b/src/backend/langflow/interface/agents/prebuilt.py
@@ -1,7 +1,6 @@
from langchain import LLMChain
from langchain.agents import AgentExecutor, ZeroShotAgent
-from langchain.agents.agent_toolkits.json.prompt import JSON_PREFIX, JSON_SUFFIX
-from langchain.agents.agent_toolkits.json.toolkit import JsonToolkit
+from langchain.agents.agent_toolkits.json.prompt import JSON_SUFFIX
from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
from langchain.base_language import BaseLanguageModel
diff --git a/src/backend/langflow/interface/initialize/loading.py b/src/backend/langflow/interface/initialize/loading.py
index dc8188ea5..6cb565e61 100644
--- a/src/backend/langflow/interface/initialize/loading.py
+++ b/src/backend/langflow/interface/initialize/loading.py
@@ -1,4 +1,4 @@
-import json
+import orjson
from typing import Any, Callable, Dict, Sequence, Type
from langchain.agents import agent as agent_module
@@ -66,7 +66,7 @@ def convert_kwargs(params):
for key in kwargs_keys:
if isinstance(params[key], str):
try:
- params[key] = json.loads(params[key])
+ params[key] = orjson.loads(params[key])
except json.JSONDecodeError:
# if the string is not a valid json string, we will
# remove the key from the params
@@ -306,7 +306,7 @@ def instantiate_documentloader(class_object: Type[BaseLoader], params: Dict):
metadata = params.pop("metadata", None)
if metadata and isinstance(metadata, str):
try:
- metadata = json.loads(metadata)
+ metadata = orjson.loads(metadata)
except json.JSONDecodeError as exc:
raise ValueError(
"The metadata you provided is not a valid JSON string."
diff --git a/src/backend/langflow/interface/initialize/utils.py b/src/backend/langflow/interface/initialize/utils.py
index ceb8a53a1..116673645 100644
--- a/src/backend/langflow/interface/initialize/utils.py
+++ b/src/backend/langflow/interface/initialize/utils.py
@@ -1,5 +1,7 @@
import contextlib
import json
+from langflow.database.models.base import orjson_dumps
+import orjson
from typing import Any, Dict, List
from langchain.agents import ZeroShotAgent
@@ -95,9 +97,11 @@ def format_content(variable):
def try_to_load_json(content):
with contextlib.suppress(json.JSONDecodeError):
- content = json.loads(content)
+ content = orjson.loads(content)
if isinstance(content, list):
content = ",".join([str(item) for item in content])
+ else:
+ content = orjson_dumps(content)
return content
diff --git a/src/backend/langflow/interface/initialize/vector_store.py b/src/backend/langflow/interface/initialize/vector_store.py
index 1bc2d73e0..c8d8f0b02 100644
--- a/src/backend/langflow/interface/initialize/vector_store.py
+++ b/src/backend/langflow/interface/initialize/vector_store.py
@@ -1,4 +1,3 @@
-import json
from typing import Any, Callable, Dict, Type
from langchain.vectorstores import (
Pinecone,
@@ -92,7 +91,7 @@ def initialize_weaviate(class_object: Type[Weaviate], params: dict):
import weaviate # type: ignore
client_kwargs_json = params.get("client_kwargs", "{}")
- client_kwargs = json.loads(client_kwargs_json)
+ client_kwargs = orjson.loads(client_kwargs_json)
client_params = {
"url": params.get("weaviate_url"),
}
diff --git a/src/backend/langflow/interface/types.py b/src/backend/langflow/interface/types.py
index 885e33694..bbdcb4461 100644
--- a/src/backend/langflow/interface/types.py
+++ b/src/backend/langflow/interface/types.py
@@ -190,17 +190,16 @@ def build_frontend_node(custom_component: CustomComponent):
def update_attributes(frontend_node, template_config):
"""Update the display name and description of a frontend node"""
- if "display_name" in template_config:
- frontend_node["display_name"] = template_config["display_name"]
-
- if "description" in template_config:
- frontend_node["description"] = template_config["description"]
-
- if "beta" in template_config:
- frontend_node["beta"] = template_config["beta"]
-
- if "documentation" in template_config:
- frontend_node["documentation"] = template_config["documentation"]
+ attributes = [
+ "display_name",
+ "description",
+ "beta",
+ "documentation",
+ "output_types",
+ ]
+ for attribute in attributes:
+ if attribute in template_config:
+ frontend_node[attribute] = template_config[attribute]
def build_field_config(custom_component: CustomComponent):
diff --git a/src/backend/langflow/processing/process.py b/src/backend/langflow/processing/process.py
index 8cefb1f44..6d62cdd68 100644
--- a/src/backend/langflow/processing/process.py
+++ b/src/backend/langflow/processing/process.py
@@ -1,6 +1,6 @@
+import json
from pathlib import Path
from langchain.schema import AgentAction
-import json
from langflow.interface.run import (
build_sorted_vertices_with_caching,
get_memory_key,
diff --git a/src/backend/langflow/services/settings/base.py b/src/backend/langflow/services/settings/base.py
new file mode 100644
index 000000000..c8e4ec380
--- /dev/null
+++ b/src/backend/langflow/services/settings/base.py
@@ -0,0 +1,222 @@
+import contextlib
+import orjson
+import os
+from shutil import copy2
+from typing import Optional, List
+from pathlib import Path
+
+import yaml
+from pydantic import BaseSettings, root_validator, validator
+from langflow.utils.logger import logger
+
+# BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
+BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components")
+
+
+class Settings(BaseSettings):
+ CHAINS: dict = {}
+ AGENTS: dict = {}
+ PROMPTS: dict = {}
+ LLMS: dict = {}
+ TOOLS: dict = {}
+ MEMORIES: dict = {}
+ EMBEDDINGS: dict = {}
+ VECTORSTORES: dict = {}
+ DOCUMENTLOADERS: dict = {}
+ WRAPPERS: dict = {}
+ RETRIEVERS: dict = {}
+ TOOLKITS: dict = {}
+ TEXTSPLITTERS: dict = {}
+ UTILITIES: dict = {}
+ OUTPUT_PARSERS: dict = {}
+ CUSTOM_COMPONENTS: dict = {}
+
+ # Define the default LANGFLOW_DIR
+ CONFIG_DIR: Optional[str] = None
+
+ DEV: bool = False
+ DATABASE_URL: Optional[str] = None
+ CACHE: str = "InMemoryCache"
+ REMOVE_API_KEYS: bool = False
+ COMPONENTS_PATH: List[str] = []
+
+ @validator("CONFIG_DIR", pre=True, allow_reuse=True)
+ def set_langflow_dir(cls, value):
+ if not value:
+ import appdirs
+
+ # Define the app name and author
+ app_name = "langflow"
+ app_author = "logspace"
+
+ # Get the cache directory for the application
+ cache_dir = appdirs.user_cache_dir(app_name, app_author)
+
+ # Create a .langflow directory inside the cache directory
+ value = Path(cache_dir)
+ value.mkdir(parents=True, exist_ok=True)
+
+ if isinstance(value, str):
+ value = Path(value)
+ if not value.exists():
+ value.mkdir(parents=True, exist_ok=True)
+
+ return str(value)
+
+ @validator("DATABASE_URL", pre=True)
+ def set_database_url(cls, value, values):
+ if not value:
+ logger.debug(
+ "No database_url provided, trying LANGFLOW_DATABASE_URL env variable"
+ )
+ if langflow_database_url := os.getenv("LANGFLOW_DATABASE_URL"):
+ value = langflow_database_url
+ logger.debug("Using LANGFLOW_DATABASE_URL env variable.")
+ else:
+ logger.debug("No DATABASE_URL env variable, using sqlite database")
+ # Originally, we used sqlite:///./langflow.db
+ # so we need to migrate to the new format
+ # if there is a database in that location
+ if not values["CONFIG_DIR"]:
+ raise ValueError(
+ "CONFIG_DIR not set, please set it or provide a DATABASE_URL"
+ )
+
+ new_path = f"{values['CONFIG_DIR']}/langflow.db"
+ if Path("./langflow.db").exists():
+ if Path(new_path).exists():
+ logger.debug(f"Database already exists at {new_path}, using it")
+ else:
+ try:
+ logger.debug("Copying existing database to new location")
+ copy2("./langflow.db", new_path)
+ logger.debug(f"Copied existing database to {new_path}")
+ except Exception:
+ logger.error("Failed to copy database, using default path")
+ new_path = "./langflow.db"
+
+ value = f"sqlite:///{new_path}"
+
+ return value
+
+ @validator("COMPONENTS_PATH", pre=True)
+ def set_components_path(cls, value):
+ if os.getenv("LANGFLOW_COMPONENTS_PATH"):
+ logger.debug("Adding LANGFLOW_COMPONENTS_PATH to components_path")
+ langflow_component_path = os.getenv("LANGFLOW_COMPONENTS_PATH")
+ if (
+ Path(langflow_component_path).exists()
+ and langflow_component_path not in value
+ ):
+ if isinstance(langflow_component_path, list):
+ for path in langflow_component_path:
+ if path not in value:
+ value.append(path)
+ logger.debug(
+ f"Extending {langflow_component_path} to components_path"
+ )
+ elif langflow_component_path not in value:
+ value.append(langflow_component_path)
+ logger.debug(
+ f"Appending {langflow_component_path} to components_path"
+ )
+
+ if not value:
+ value = [BASE_COMPONENTS_PATH]
+ logger.debug("Setting default components path to components_path")
+ elif BASE_COMPONENTS_PATH not in value:
+ value.append(BASE_COMPONENTS_PATH)
+ logger.debug("Adding default components path to components_path")
+
+ logger.debug(f"Components path: {value}")
+ return value
+
+ class Config:
+ validate_assignment = True
+ extra = "ignore"
+ env_prefix = "LANGFLOW_"
+
+ @root_validator(allow_reuse=True)
+ def validate_lists(cls, values):
+ for key, value in values.items():
+ if key != "dev" and not value:
+ values[key] = []
+ return values
+
+ def update_from_yaml(self, file_path: str, dev: bool = False):
+ new_settings = load_settings_from_yaml(file_path)
+ self.CHAINS = new_settings.CHAINS or {}
+ self.AGENTS = new_settings.AGENTS or {}
+ self.PROMPTS = new_settings.PROMPTS or {}
+ self.LLMS = new_settings.LLMS or {}
+ self.TOOLS = new_settings.TOOLS or {}
+ self.MEMORIES = new_settings.MEMORIES or {}
+ self.WRAPPERS = new_settings.WRAPPERS or {}
+ self.TOOLKITS = new_settings.TOOLKITS or {}
+ self.TEXTSPLITTERS = new_settings.TEXTSPLITTERS or {}
+ self.UTILITIES = new_settings.UTILITIES or {}
+ self.EMBEDDINGS = new_settings.EMBEDDINGS or {}
+ self.VECTORSTORES = new_settings.VECTORSTORES or {}
+ self.DOCUMENTLOADERS = new_settings.DOCUMENTLOADERS or {}
+ self.RETRIEVERS = new_settings.RETRIEVERS or {}
+ self.OUTPUT_PARSERS = new_settings.OUTPUT_PARSERS or {}
+ self.CUSTOM_COMPONENTS = new_settings.CUSTOM_COMPONENTS or {}
+ self.COMPONENTS_PATH = new_settings.COMPONENTS_PATH or []
+ self.DEV = dev
+
+ def update_settings(self, **kwargs):
+ logger.debug("Updating settings")
+ for key, value in kwargs.items():
+ # value may contain sensitive information, so we don't want to log it
+ if not hasattr(self, key):
+ logger.debug(f"Key {key} not found in settings")
+ continue
+ logger.debug(f"Updating {key}")
+ if isinstance(getattr(self, key), list):
+ # value might be a '[something]' string
+ with contextlib.suppress(json.decoder.JSONDecodeError):
+ value = orjson.loads(str(value))
+ if isinstance(value, list):
+ for item in value:
+ if isinstance(item, Path):
+ item = str(item)
+ if item not in getattr(self, key):
+ getattr(self, key).append(item)
+ logger.debug(f"Extended {key}")
+ else:
+ if isinstance(value, Path):
+ value = str(value)
+ if value not in getattr(self, key):
+ getattr(self, key).append(value)
+ logger.debug(f"Appended {key}")
+
+ else:
+ setattr(self, key, value)
+ logger.debug(f"Updated {key}")
+ logger.debug(f"{key}: {getattr(self, key)}")
+
+
+def save_settings_to_yaml(settings: Settings, file_path: str):
+ with open(file_path, "w") as f:
+ settings_dict = settings.dict()
+ yaml.dump(settings_dict, f)
+
+
+def load_settings_from_yaml(file_path: str) -> Settings:
+ # Check if a string is a valid path or a file name
+ if "/" not in file_path:
+ # Get current path
+ current_path = os.path.dirname(os.path.abspath(__file__))
+
+ file_path = os.path.join(current_path, file_path)
+
+ with open(file_path, "r") as f:
+ settings_dict = yaml.safe_load(f)
+ settings_dict = {k.upper(): v for k, v in settings_dict.items()}
+
+ for key in settings_dict:
+ if key not in Settings.__fields__.keys():
+ raise KeyError(f"Key {key} not found in settings")
+ logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}")
+
+ return Settings(**settings_dict)
diff --git a/src/backend/langflow/settings.py b/src/backend/langflow/settings.py
index 6a10f0506..9eea696b4 100644
--- a/src/backend/langflow/settings.py
+++ b/src/backend/langflow/settings.py
@@ -1,5 +1,5 @@
import contextlib
-import json
+import orjson
import os
from typing import Optional, List
from pathlib import Path
@@ -126,7 +126,7 @@ class Settings(BaseSettings):
if isinstance(getattr(self, key), list):
# value might be a '[something]' string
with contextlib.suppress(json.decoder.JSONDecodeError):
- value = json.loads(str(value))
+ value = orjson.loads(str(value))
if isinstance(value, list):
for item in value:
if isinstance(item, Path):
diff --git a/src/backend/langflow/template/frontend_node/llms.py b/src/backend/langflow/template/frontend_node/llms.py
index a6a128cfe..fdf0a2b5b 100644
--- a/src/backend/langflow/template/frontend_node/llms.py
+++ b/src/backend/langflow/template/frontend_node/llms.py
@@ -1,5 +1,5 @@
-import json
from typing import Optional
+from langflow.database.models.base import orjson_dumps
from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode
@@ -89,7 +89,7 @@ class LLMFrontendNode(FrontendNode):
if field.name == "config":
field.show = True
field.advanced = True
- field.value = json.dumps(CTRANSFORMERS_DEFAULT_CONFIG, indent=2)
+ field.value = orjson_dumps(CTRANSFORMERS_DEFAULT_CONFIG, indent_2=True)
@staticmethod
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
diff --git a/src/backend/langflow/template/frontend_node/utilities.py b/src/backend/langflow/template/frontend_node/utilities.py
index df993e377..fa0b55332 100644
--- a/src/backend/langflow/template/frontend_node/utilities.py
+++ b/src/backend/langflow/template/frontend_node/utilities.py
@@ -1,6 +1,6 @@
import ast
-import json
from typing import Optional
+from langflow.database.models.base import orjson_dumps
from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode
@@ -22,4 +22,4 @@ class UtilitiesFrontendNode(FrontendNode):
if isinstance(field.value, dict):
field.field_type = "code"
- field.value = json.dumps(field.value, indent=4)
+ field.value = orjson_dumps(field.value)
diff --git a/tests/test_cache.py b/tests/test_cache.py
index 50698c304..f3d0cabda 100644
--- a/tests/test_cache.py
+++ b/tests/test_cache.py
@@ -1,4 +1,6 @@
import json
+from langflow.database.models.base import orjson_dumps
+import orjson
from langflow.graph import Graph
import pytest
@@ -63,9 +65,9 @@ def test_cache_size_limit(basic_data_graph):
nodes = modified_data_graph["nodes"]
node_id = nodes[0]["id"]
# Now we replace all instances ode node_id with a new id in the json
- json_string = json.dumps(modified_data_graph)
+ json_string = orjson_dumps(modified_data_graph)
modified_json_string = json_string.replace(node_id, f"{node_id}_{i}")
- modified_data_graph_new_id = json.loads(modified_json_string)
+ modified_data_graph_new_id = orjson.loads(modified_json_string)
build_langchain_object_with_caching(modified_data_graph_new_id)
assert len(build_langchain_object_with_caching.cache) == 10
diff --git a/tests/test_database.py b/tests/test_database.py
index bc512b6b0..dd5f03933 100644
--- a/tests/test_database.py
+++ b/tests/test_database.py
@@ -1,11 +1,12 @@
-import json
+from fastapi.encoders import jsonable_encoder
+from langflow.database.models.base import orjson_dumps
+import orjson
import pytest
from uuid import UUID, uuid4
from sqlalchemy.orm import Session
from fastapi.testclient import TestClient
-from fastapi.encoders import jsonable_encoder
from langflow.api.v1.schemas import FlowListCreate
from langflow.database.models.flow import Flow, FlowCreate, FlowUpdate
@@ -23,7 +24,7 @@ def json_style():
# color: str = Field(index=True)
# emoji: str = Field(index=False)
# flow_id: UUID = Field(default=None, foreign_key="flow.id")
- return json.dumps(
+ return orjson_dumps(
{
"color": "red",
"emoji": "👍",
@@ -32,7 +33,7 @@ def json_style():
def test_create_flow(client: TestClient, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data)
response = client.post("api/v1/flows/", json=flow.dict())
@@ -48,7 +49,7 @@ def test_create_flow(client: TestClient, json_flow: str):
def test_read_flows(client: TestClient, json_flow: str):
- flow_data = json.loads(json_flow)
+ flow_data = orjson.loads(json_flow)
data = flow_data["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data)
response = client.post("api/v1/flows/", json=flow.dict())
@@ -89,7 +90,7 @@ def test_read_flows(client: TestClient, json_flow: str):
def test_read_flow(client: TestClient, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data)
response = client.post("api/v1/flows/", json=flow.dict())
@@ -115,7 +116,7 @@ def test_read_flow(client: TestClient, json_flow: str):
def test_update_flow(client: TestClient, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data)
@@ -136,7 +137,7 @@ def test_update_flow(client: TestClient, json_flow: str):
def test_delete_flow(client: TestClient, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
flow = FlowCreate(name="Test Flow", description="description", data=data)
response = client.post("api/v1/flows/", json=flow.dict())
@@ -147,7 +148,7 @@ def test_delete_flow(client: TestClient, json_flow: str):
def test_create_flows(client: TestClient, session: Session, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
# Create test data
flow_list = FlowListCreate(
@@ -172,7 +173,7 @@ def test_create_flows(client: TestClient, session: Session, json_flow: str):
def test_upload_file(client: TestClient, session: Session, json_flow: str):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
# Create test data
flow_list = FlowListCreate(
@@ -181,7 +182,7 @@ def test_upload_file(client: TestClient, session: Session, json_flow: str):
FlowCreate(name="Flow 2", description="description", data=data),
]
)
- file_contents = json.dumps(flow_list.dict())
+ file_contents = orjson_dumps(flow_list.dict())
response = client.post(
"api/v1/flows/upload/",
files={"file": ("examples.json", file_contents, "application/json")},
@@ -200,7 +201,7 @@ def test_upload_file(client: TestClient, session: Session, json_flow: str):
def test_download_file(client: TestClient, session: Session, json_flow):
- flow = json.loads(json_flow)
+ flow = orjson.loads(json_flow)
data = flow["data"]
# Create test data
flow_list = FlowListCreate(
@@ -241,7 +242,7 @@ def test_get_nonexistent_flow(client: TestClient):
def test_update_flow_idempotency(client: TestClient, json_flow: str):
- flow_data = json.loads(json_flow)
+ flow_data = orjson.loads(json_flow)
data = flow_data["data"]
flow_data = FlowCreate(name="Test Flow", description="description", data=data)
response = client.post("api/v1/flows/", json=flow_data.dict())
@@ -253,7 +254,7 @@ def test_update_flow_idempotency(client: TestClient, json_flow: str):
def test_update_nonexistent_flow(client: TestClient, json_flow: str):
- flow_data = json.loads(json_flow)
+ flow_data = orjson.loads(json_flow)
data = flow_data["data"]
uuid = uuid4()
updated_flow = FlowCreate(
diff --git a/tests/test_loading.py b/tests/test_loading.py
index 11fa8e471..e5c409c93 100644
--- a/tests/test_loading.py
+++ b/tests/test_loading.py
@@ -1,5 +1,4 @@
import json
-
import pytest
from langchain.chains.base import Chain
from langflow.processing.process import load_flow_from_json
From ecf217dce8f22514de824065765b52bbffa8eb03 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 11:37:51 -0300
Subject: [PATCH 02/32] =?UTF-8?q?=F0=9F=90=9B=20fix(chat.py):=20remove=20u?=
=?UTF-8?q?nnecessary=20try-except=20block=20for=20building=20the=20graph?=
=?UTF-8?q?=20and=20handle=20any=20exceptions=20thrown=20by=20Graph.from?=
=?UTF-8?q?=5Fpayload()=20method=20=F0=9F=94=A7=20refactor(chat.py):=20imp?=
=?UTF-8?q?rove=20logging=20output=20for=20built=20node=20parameters=20in?=
=?UTF-8?q?=20stream=5Fbuild()=20method?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/api/v1/chat.py | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/src/backend/langflow/api/v1/chat.py b/src/backend/langflow/api/v1/chat.py
index 06a2fdda0..e71ddf81f 100644
--- a/src/backend/langflow/api/v1/chat.py
+++ b/src/backend/langflow/api/v1/chat.py
@@ -104,14 +104,9 @@ async def stream_build(flow_id: str):
return
logger.debug("Building langchain object")
- try:
- # Some error could happen when building the graph
- graph = Graph.from_payload(graph_data)
- except Exception as exc:
- logger.exception(exc)
- error_message = str(exc)
- yield str(StreamData(event="error", data={"error": error_message}))
- return
+
+ # Some error could happen when building the graph
+ graph = Graph.from_payload(graph_data)
number_of_nodes = len(graph.nodes)
flow_data_store[flow_id]["status"] = BuildStatus.IN_PROGRESS
@@ -126,7 +121,9 @@ async def stream_build(flow_id: str):
params = vertex._built_object_repr()
valid = True
logger.debug(f"Building node {str(vertex.vertex_type)}")
- logger.debug(f"Output: {params}")
+ logger.debug(
+ f"Output: {params[:100]}{'...' if len(params) > 100 else ''}"
+ )
if vertex.artifacts:
# The artifacts will be prompt variables
# passed to build_input_keys_response
From 44ce4eac5c360483e160eface7af903a4d3ec878 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 11:38:24 -0300
Subject: [PATCH 03/32] =?UTF-8?q?=F0=9F=94=A7=20fix(endpoints.py):=20refac?=
=?UTF-8?q?tor=20logger.debug=20statement=20to=20improve=20readability=20a?=
=?UTF-8?q?nd=20provide=20more=20information=20=F0=9F=94=A7=20fix(base.py)?=
=?UTF-8?q?:=20remove=20redundant=20logger.debug=20statement=20?=
=?UTF-8?q?=F0=9F=94=A7=20fix(directory=5Freader.py):=20refactor=20logger.?=
=?UTF-8?q?debug=20statement=20to=20improve=20readability=20and=20provide?=
=?UTF-8?q?=20more=20information=20=F0=9F=94=A7=20fix(types.py):=20refacto?=
=?UTF-8?q?r=20logger.debug=20statement=20to=20improve=20readability=20and?=
=?UTF-8?q?=20provide=20more=20information?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/api/v1/endpoints.py | 4 +++-
src/backend/langflow/graph/edge/base.py | 1 -
src/backend/langflow/interface/custom/directory_reader.py | 2 +-
src/backend/langflow/interface/types.py | 4 +++-
4 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py
index 1e1524222..2c109224b 100644
--- a/src/backend/langflow/api/v1/endpoints.py
+++ b/src/backend/langflow/api/v1/endpoints.py
@@ -56,7 +56,9 @@ def get_all():
logger.info(f"Loading {len(custom_component_dicts)} category(ies)")
for custom_component_dict in custom_component_dicts:
- logger.debug(custom_component_dict)
+ logger.debug(
+ {key: len(value) for key, value in custom_component_dict.items()}
+ )
custom_components_from_file = merge_nested_dicts_with_renaming(
custom_components_from_file, custom_component_dict
)
diff --git a/src/backend/langflow/graph/edge/base.py b/src/backend/langflow/graph/edge/base.py
index 569d33ec0..dc7eab328 100644
--- a/src/backend/langflow/graph/edge/base.py
+++ b/src/backend/langflow/graph/edge/base.py
@@ -40,7 +40,6 @@ class Edge:
if no_matched_type:
logger.debug(self.source_types)
logger.debug(self.target_reqs)
- if no_matched_type:
raise ValueError(
f"Edge between {self.source.vertex_type} and {self.target.vertex_type} "
f"has no matched type"
diff --git a/src/backend/langflow/interface/custom/directory_reader.py b/src/backend/langflow/interface/custom/directory_reader.py
index 7bff7b5f5..44b2d4f1b 100644
--- a/src/backend/langflow/interface/custom/directory_reader.py
+++ b/src/backend/langflow/interface/custom/directory_reader.py
@@ -77,7 +77,7 @@ class DirectoryReader:
]
filtered = [menu for menu in items if menu["components"]]
logger.debug(
- f'Filtered components {"with errors" if with_errors else ""}: {filtered}'
+ f'Filtered components {"with errors" if with_errors else ""}: {len(filtered)}'
)
return {"menu": filtered}
diff --git a/src/backend/langflow/interface/types.py b/src/backend/langflow/interface/types.py
index bbdcb4461..824b0af50 100644
--- a/src/backend/langflow/interface/types.py
+++ b/src/backend/langflow/interface/types.py
@@ -337,7 +337,9 @@ def build_valid_menu(valid_components):
valid_menu[menu_name] = {}
for component in menu_item["components"]:
- logger.debug(f"Building component: {component}")
+ logger.debug(
+ f"Building component: {component.get('name'), component.get('output_types')}"
+ )
try:
component_name = component["name"]
component_code = component["code"]
From 22072084f8ed51f44379421dd037b147b1347d1d Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 11:38:36 -0300
Subject: [PATCH 04/32] =?UTF-8?q?=F0=9F=90=9B=20fix(schemas.py):=20fix=20i?=
=?UTF-8?q?ndentation=20issue=20in=20the=20=5F=5Fstr=5F=5F=20method=20of?=
=?UTF-8?q?=20StreamData=20class=20to=20improve=20code=20readability?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/api/v1/schemas.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/backend/langflow/api/v1/schemas.py b/src/backend/langflow/api/v1/schemas.py
index bbff0de4a..de0348d38 100644
--- a/src/backend/langflow/api/v1/schemas.py
+++ b/src/backend/langflow/api/v1/schemas.py
@@ -115,7 +115,9 @@ class StreamData(BaseModel):
data: dict
def __str__(self) -> str:
- return f"event: {self.event}\ndata: {orjson_dumps(self.data)}\n\n"
+ return (
+ f"event: {self.event}\ndata: {orjson_dumps(self.data, indent_2=False)}\n\n"
+ )
class CustomComponentCode(BaseModel):
From fb028e55b84fa73a814d4718f34751662e6b6778 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 11:38:48 -0300
Subject: [PATCH 05/32] =?UTF-8?q?=E2=9C=A8=20feat(GetRequest.py):=20add=20?=
=?UTF-8?q?GetRequest=20component=20to=20make=20a=20GET=20request=20to=20a?=
=?UTF-8?q?=20given=20URL=20=E2=9C=A8=20feat(PostRequest.py):=20add=20Post?=
=?UTF-8?q?Request=20component=20to=20make=20a=20POST=20request=20to=20a?=
=?UTF-8?q?=20given=20URL?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/utilities/GetRequest.py | 29 +++++++++++++++++
.../components/utilities/PostRequest.py | 31 +++++++++++++++++++
2 files changed, 60 insertions(+)
create mode 100644 src/backend/langflow/components/utilities/GetRequest.py
create mode 100644 src/backend/langflow/components/utilities/PostRequest.py
diff --git a/src/backend/langflow/components/utilities/GetRequest.py b/src/backend/langflow/components/utilities/GetRequest.py
new file mode 100644
index 000000000..baa2139c2
--- /dev/null
+++ b/src/backend/langflow/components/utilities/GetRequest.py
@@ -0,0 +1,29 @@
+from langflow import CustomComponent
+from langchain.schema import Document
+from langflow.database.models.base import orjson_dumps
+import requests
+from typing import Optional
+
+
+class GetRequest(CustomComponent):
+ display_name: str = "Get Request"
+ description: str = "Make a GET request to the given URL"
+ output_types: list[str] = ["Document"]
+ beta = True
+ field_config = {
+ "url": {"display_name": "URL"},
+ "headers": {"display_name": "Headers", "field_type": "code"},
+ "code": {"show": False},
+ }
+
+ def build(
+ self,
+ url: str,
+ headers: Optional[dict] = None,
+ ) -> Document:
+ if headers is None:
+ headers = {}
+ with requests.get(url, headers=headers) as result:
+ result = result.json()
+ self.repr_value = result
+ return Document(page_content=orjson_dumps(result))
diff --git a/src/backend/langflow/components/utilities/PostRequest.py b/src/backend/langflow/components/utilities/PostRequest.py
new file mode 100644
index 000000000..83ae9db34
--- /dev/null
+++ b/src/backend/langflow/components/utilities/PostRequest.py
@@ -0,0 +1,31 @@
+from langflow import CustomComponent
+from langchain.schema import Document
+from langflow.database.models.base import orjson_dumps
+import requests
+from typing import Optional
+
+
+class PostRequest(CustomComponent):
+ display_name: str = "Post Request"
+ description: str = "Make a POST request to the given URL"
+ output_types: list[str] = ["Document"]
+ beta = True
+ field_config = {
+ "url": {"display_name": "URL"},
+ "headers": {"display_name": "Headers", "field_type": "code"},
+ "code": {"show": False},
+ "document": {"display_name": "Document"},
+ }
+
+ def build(
+ self,
+ document: Document,
+ url: str,
+ headers: Optional[dict] = None,
+ ) -> Document:
+ if headers is None:
+ headers = {}
+ with requests.post(url, headers=headers, json=document.page_content) as result:
+ result = result.json()
+ self.repr_value = result
+ return Document(page_content=orjson_dumps(result))
From b8b9bb25bc253c2eb4e5d19a1470775b2b134dc8 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 11:56:01 -0300
Subject: [PATCH 06/32] =?UTF-8?q?=F0=9F=93=A6=20feat(JSONDocumentBuilder.p?=
=?UTF-8?q?y):=20add=20JSONDocumentBuilder=20component=20to=20build=20a=20?=
=?UTF-8?q?Document=20containing=20a=20JSON=20object=20using=20a=20key=20a?=
=?UTF-8?q?nd=20another=20Document=20page=20content=20=F0=9F=93=9D=20docs(?=
=?UTF-8?q?JSONDocumentBuilder.py):=20add=20description=20and=20field=20co?=
=?UTF-8?q?nfiguration=20for=20JSONDocumentBuilder=20component?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../utilities/JSONDocumentBuilder.py | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 src/backend/langflow/components/utilities/JSONDocumentBuilder.py
diff --git a/src/backend/langflow/components/utilities/JSONDocumentBuilder.py b/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
new file mode 100644
index 000000000..0336dcc1b
--- /dev/null
+++ b/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
@@ -0,0 +1,34 @@
+### JSON Document Builder
+
+# Build a Document containing a JSON object using a key and another Document page content.
+
+# **Params**
+
+# - **Key:** The key to use for the JSON object.
+# - **Document:** The Document page to use for the JSON object.
+
+# **Output**
+
+# - **Document:** The Document containing the JSON object.
+
+from langflow import CustomComponent
+from langchain.schema import Document
+
+
+class JSONDocumentBuilder(CustomComponent):
+ display_name: str = "JSON Document Builder"
+ description: str = "Build a Document containing a JSON object using a key and another Document page content."
+ output_types: list[str] = ["Document"]
+ beta = True
+
+ field_config = {
+ "key": {"display_name": "Key"},
+ "document": {"display_name": "Document"},
+ }
+
+ def build(
+ self,
+ key: str,
+ document: Document,
+ ) -> Document:
+ return Document(page_content={key: document.page_content})
From 9f617ff6f1b0c46e904dbe655c3e439413a8a0a8 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 11:56:14 -0300
Subject: [PATCH 07/32] =?UTF-8?q?=F0=9F=93=9D=20docs(utilities.mdx):=20upd?=
=?UTF-8?q?ate=20Utilities=20documentation=20with=20additional=20sections?=
=?UTF-8?q?=20and=20improve=20formatting=20for=20better=20readability?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/docs/components/utilities.mdx | 49 +++++++++++++++++++++++++++---
1 file changed, 45 insertions(+), 4 deletions(-)
diff --git a/docs/docs/components/utilities.mdx b/docs/docs/components/utilities.mdx
index f510990ce..cb0aa808e 100644
--- a/docs/docs/components/utilities.mdx
+++ b/docs/docs/components/utilities.mdx
@@ -1,10 +1,51 @@
-import Admonition from '@theme/Admonition';
+import Admonition from "@theme/Admonition";
# Utilities
-
- We appreciate your understanding as we polish our documentation – it may contain some rough edges. Share your feedback or report issues to help us improve! 🛠️📝
-
+
+ We appreciate your understanding as we polish our documentation – it may
+ contain some rough edges. Share your feedback or report issues to help us
+ improve! 🛠️📝
+
+### Get Request
+
+Make a GET request to the given URL.
+
+**Params**
+
+- **URL:** The URL to make the request to.
+- **Headers:** A dictionary of headers to send with the request.
+
+**Output**
+
+- **Document:** The JSON response from the request as a Document.
+
+### POST Request
+
+Make a POST request to the given URL.
+
+**Params**
+
+- **URL:** The URL to make the request to.
+- **Headers:** A dictionary of headers to send with the request.
+- **Document:** The Document containing a JSON object to send with the request.
+
+**Output**
+
+- **Document:** The JSON response from the request as a Document.
+
+### JSON Document Builder
+
+Build a Document containing a JSON object using a key and another Document page content.
+
+**Params**
+
+- **Key:** The key to use for the JSON object.
+- **Document:** The Document page to use for the JSON object.
+
+**Output**
+
+- **Document:** The Document containing the JSON object.
From 62b9bb523c7ee20a846bf47bb63ee51c0bc6c5bd Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 14:56:34 -0300
Subject: [PATCH 08/32] =?UTF-8?q?=F0=9F=94=A7=20fix(GetRequest.py):=20impr?=
=?UTF-8?q?ove=20code=20readability=20and=20add=20support=20for=20multiple?=
=?UTF-8?q?=20URLs=20and=20headers=20in=20GetRequest=20component=20?=
=?UTF-8?q?=F0=9F=94=A7=20fix(PostRequest.py):=20improve=20code=20readabil?=
=?UTF-8?q?ity=20and=20add=20support=20for=20multiple=20documents,=20URLs,?=
=?UTF-8?q?=20and=20headers=20in=20PostRequest=20component?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/utilities/GetRequest.py | 55 ++++++++++++++++---
.../components/utilities/PostRequest.py | 55 ++++++++++++++++---
2 files changed, 96 insertions(+), 14 deletions(-)
diff --git a/src/backend/langflow/components/utilities/GetRequest.py b/src/backend/langflow/components/utilities/GetRequest.py
index baa2139c2..424106f31 100644
--- a/src/backend/langflow/components/utilities/GetRequest.py
+++ b/src/backend/langflow/components/utilities/GetRequest.py
@@ -7,15 +7,57 @@ from typing import Optional
class GetRequest(CustomComponent):
display_name: str = "Get Request"
- description: str = "Make a GET request to the given URL"
+ description: str = "Make a GET request to the given URL."
output_types: list[str] = ["Document"]
beta = True
field_config = {
- "url": {"display_name": "URL"},
- "headers": {"display_name": "Headers", "field_type": "code"},
+ "url": {
+ "display_name": "URL",
+ "info": "The URL to make the request to",
+ "is_list": True,
+ },
+ "headers": {
+ "display_name": "Headers",
+ "field_type": "code",
+ "info": "The headers to send with the request.",
+ },
"code": {"show": False},
}
+ def get_document(
+ self,
+ url: str,
+ headers: Optional[dict] = None,
+ ) -> Document:
+ try:
+ if headers is None:
+ headers = {}
+ with requests.Session() as session:
+ response = session.get(url, headers=headers)
+ try:
+ response_json = response.json()
+ result = orjson_dumps(response_json, indent_2=False)
+ except Exception:
+ result = response.text
+ self.repr_value = result
+ return Document(
+ page_content=result,
+ metadata={
+ "source": url,
+ "headers": headers,
+ "status_code": response.status_code,
+ },
+ )
+ except Exception as exc:
+ return Document(
+ page_content=str(exc),
+ metadata={
+ "source": url,
+ "headers": headers,
+ "status_code": 500,
+ },
+ )
+
def build(
self,
url: str,
@@ -23,7 +65,6 @@ class GetRequest(CustomComponent):
) -> Document:
if headers is None:
headers = {}
- with requests.get(url, headers=headers) as result:
- result = result.json()
- self.repr_value = result
- return Document(page_content=orjson_dumps(result))
+ if not isinstance(url, list):
+ url = [url]
+ return [self.get_document(u, headers) for u in url]
diff --git a/src/backend/langflow/components/utilities/PostRequest.py b/src/backend/langflow/components/utilities/PostRequest.py
index 83ae9db34..2f0ab2053 100644
--- a/src/backend/langflow/components/utilities/PostRequest.py
+++ b/src/backend/langflow/components/utilities/PostRequest.py
@@ -7,16 +7,55 @@ from typing import Optional
class PostRequest(CustomComponent):
display_name: str = "Post Request"
- description: str = "Make a POST request to the given URL"
+ description: str = "Make a POST request to the given URL."
output_types: list[str] = ["Document"]
beta = True
field_config = {
- "url": {"display_name": "URL"},
- "headers": {"display_name": "Headers", "field_type": "code"},
+ "url": {"display_name": "URL", "info": "The URL to make the request to."},
+ "headers": {
+ "display_name": "Headers",
+ "field_type": "code",
+ "info": "The headers to send with the request.",
+ },
"code": {"show": False},
"document": {"display_name": "Document"},
}
+ def post_document(
+ self,
+ document: Document,
+ url: str,
+ headers: Optional[dict] = None,
+ ) -> Document:
+ try:
+ with requests.Session() as session:
+ response = session.post(
+ url, headers=headers, data=document.page_content
+ )
+ try:
+ response_json = response.json()
+ result = orjson_dumps(response_json, indent_2=False)
+ except Exception:
+ result = response.text
+ self.repr_value = result
+ return Document(
+ page_content=result,
+ metadata={
+ "source": url,
+ "headers": headers,
+ "status_code": response,
+ },
+ )
+ except Exception as exc:
+ return Document(
+ page_content=str(exc),
+ metadata={
+ "source": url,
+ "headers": headers,
+ "status_code": 500,
+ },
+ )
+
def build(
self,
document: Document,
@@ -25,7 +64,9 @@ class PostRequest(CustomComponent):
) -> Document:
if headers is None:
headers = {}
- with requests.post(url, headers=headers, json=document.page_content) as result:
- result = result.json()
- self.repr_value = result
- return Document(page_content=orjson_dumps(result))
+
+ if not isinstance(document, list):
+ document = [document]
+ documents = [self.post_document(doc, url, headers) for doc in document]
+ self.repr_value = documents
+ return documents
From 6e18e795ff44772f1f82afa77980f69645aa5873 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 14:58:52 -0300
Subject: [PATCH 09/32] =?UTF-8?q?=F0=9F=94=A7=20chore(GetRequest.py):=20re?=
=?UTF-8?q?factor=20GetRequest=20class=20to=20store=20the=20returned=20doc?=
=?UTF-8?q?uments=20in=20a=20repr=5Fvalue=20attribute=20for=20debugging=20?=
=?UTF-8?q?purposes?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/components/utilities/GetRequest.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/backend/langflow/components/utilities/GetRequest.py b/src/backend/langflow/components/utilities/GetRequest.py
index 424106f31..856fd82c5 100644
--- a/src/backend/langflow/components/utilities/GetRequest.py
+++ b/src/backend/langflow/components/utilities/GetRequest.py
@@ -67,4 +67,6 @@ class GetRequest(CustomComponent):
headers = {}
if not isinstance(url, list):
url = [url]
- return [self.get_document(u, headers) for u in url]
+ documents = [self.get_document(u, headers) for u in url]
+ self.repr_value = documents
+ return documents
From 5fa8281e94e9984e379b3ae1d270351c705f309c Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 14:59:00 -0300
Subject: [PATCH 10/32] =?UTF-8?q?=F0=9F=90=9B=20fix(JSONDocumentBuilder.py?=
=?UTF-8?q?):=20handle=20both=20single=20document=20and=20list=20of=20docu?=
=?UTF-8?q?ments=20when=20building=20JSON=20document=20=E2=9C=A8=20feat(JS?=
=?UTF-8?q?ONDocumentBuilder.py):=20add=20support=20for=20building=20JSON?=
=?UTF-8?q?=20document=20from=20a=20list=20of=20documents?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/utilities/JSONDocumentBuilder.py | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/backend/langflow/components/utilities/JSONDocumentBuilder.py b/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
index 0336dcc1b..4880200da 100644
--- a/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
+++ b/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
@@ -31,4 +31,12 @@ class JSONDocumentBuilder(CustomComponent):
key: str,
document: Document,
) -> Document:
- return Document(page_content={key: document.page_content})
+ documents = None
+ if isinstance(document, list):
+ documents = [
+ Document(page_content={key: doc.page_content}) for doc in document
+ ]
+ else:
+ documents = Document(page_content={key: document.page_content})
+ self.repr_value = documents
+ return documents
From 696a139171d82a9df19f84b5b59c17ba898d6eba Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 15:31:47 -0300
Subject: [PATCH 11/32] =?UTF-8?q?=F0=9F=90=9B=20fix(GetRequest.py):=20hand?=
=?UTF-8?q?le=20request=20timeout=20by=20returning=20a=20Document=20with?=
=?UTF-8?q?=20status=20code=20408=20and=20"Request=20Timed=20Out"=20conten?=
=?UTF-8?q?t=20=E2=9C=A8=20feat(GetRequest.py):=20add=20timeout=20paramete?=
=?UTF-8?q?r=20to=20the=20build=20method=20to=20allow=20configuring=20the?=
=?UTF-8?q?=20request=20timeout=20value?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/utilities/GetRequest.py | 50 +++++++++----------
1 file changed, 24 insertions(+), 26 deletions(-)
diff --git a/src/backend/langflow/components/utilities/GetRequest.py b/src/backend/langflow/components/utilities/GetRequest.py
index 856fd82c5..018b92751 100644
--- a/src/backend/langflow/components/utilities/GetRequest.py
+++ b/src/backend/langflow/components/utilities/GetRequest.py
@@ -25,48 +25,46 @@ class GetRequest(CustomComponent):
}
def get_document(
- self,
- url: str,
- headers: Optional[dict] = None,
+ self, session: requests.Session, url: str, headers: Optional[dict], timeout: int
) -> Document:
try:
- if headers is None:
- headers = {}
- with requests.Session() as session:
- response = session.get(url, headers=headers)
- try:
- response_json = response.json()
- result = orjson_dumps(response_json, indent_2=False)
- except Exception:
- result = response.text
- self.repr_value = result
- return Document(
- page_content=result,
- metadata={
- "source": url,
- "headers": headers,
- "status_code": response.status_code,
- },
- )
- except Exception as exc:
+ response = session.get(url, headers=headers, timeout=timeout)
+ try:
+ response_json = response.json()
+ result = orjson_dumps(response_json, indent=2)
+ except Exception:
+ result = response.text
+ self.repr_value = result
return Document(
- page_content=str(exc),
+ page_content=result,
metadata={
"source": url,
"headers": headers,
- "status_code": 500,
+ "status_code": response.status_code,
},
)
+ except requests.Timeout:
+ return Document(
+ page_content="Request Timed Out",
+ metadata={"source": url, "headers": headers, "status_code": 408},
+ )
+ except Exception as exc:
+ return Document(
+ page_content=str(exc),
+ metadata={"source": url, "headers": headers, "status_code": 500},
+ )
def build(
self,
url: str,
headers: Optional[dict] = None,
+ timeout: int = 5,
) -> Document:
if headers is None:
headers = {}
if not isinstance(url, list):
url = [url]
- documents = [self.get_document(u, headers) for u in url]
- self.repr_value = documents
+ with requests.Session() as session:
+ documents = [self.get_document(session, u, headers, timeout) for u in url]
+ self.repr_value = documents
return documents
From 4efe06e4409133f9450b18ec23a4a6cffec8e050 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 15:32:00 -0300
Subject: [PATCH 12/32] =?UTF-8?q?=F0=9F=90=9B=20fix(PostRequest.py):=20rem?=
=?UTF-8?q?ove=20redundant=20session=20creation=20and=20move=20it=20to=20t?=
=?UTF-8?q?he=20calling=20function=20to=20improve=20efficiency=20and=20reu?=
=?UTF-8?q?sability=20=E2=9C=A8=20feat(PostRequest.py):=20add=20support=20?=
=?UTF-8?q?for=20processing=20multiple=20documents=20in=20a=20single=20req?=
=?UTF-8?q?uest=20to=20improve=20performance=20and=20reduce=20network=20ov?=
=?UTF-8?q?erhead?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/utilities/PostRequest.py | 42 ++++++++++---------
1 file changed, 22 insertions(+), 20 deletions(-)
diff --git a/src/backend/langflow/components/utilities/PostRequest.py b/src/backend/langflow/components/utilities/PostRequest.py
index 2f0ab2053..b32c12488 100644
--- a/src/backend/langflow/components/utilities/PostRequest.py
+++ b/src/backend/langflow/components/utilities/PostRequest.py
@@ -23,29 +23,27 @@ class PostRequest(CustomComponent):
def post_document(
self,
+ session: requests.Session,
document: Document,
url: str,
headers: Optional[dict] = None,
) -> Document:
try:
- with requests.Session() as session:
- response = session.post(
- url, headers=headers, data=document.page_content
- )
- try:
- response_json = response.json()
- result = orjson_dumps(response_json, indent_2=False)
- except Exception:
- result = response.text
- self.repr_value = result
- return Document(
- page_content=result,
- metadata={
- "source": url,
- "headers": headers,
- "status_code": response,
- },
- )
+ response = session.post(url, headers=headers, data=document.page_content)
+ try:
+ response_json = response.json()
+ result = orjson_dumps(response_json, indent_2=False)
+ except Exception:
+ result = response.text
+ self.repr_value = result
+ return Document(
+ page_content=result,
+ metadata={
+ "source": url,
+ "headers": headers,
+ "status_code": response,
+ },
+ )
except Exception as exc:
return Document(
page_content=str(exc),
@@ -67,6 +65,10 @@ class PostRequest(CustomComponent):
if not isinstance(document, list):
document = [document]
- documents = [self.post_document(doc, url, headers) for doc in document]
- self.repr_value = documents
+
+ with requests.Session() as session:
+ documents = [
+ self.post_document(session, doc, url, headers) for doc in document
+ ]
+ self.repr_value = documents
return documents
From 0d53db1653e126ece242df76fc068ba466e8181b Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 15:33:44 -0300
Subject: [PATCH 13/32] =?UTF-8?q?=F0=9F=90=9B=20fix(PostRequest.py):=20add?=
=?UTF-8?q?=20validation=20for=20document=20parameter=20to=20only=20accept?=
=?UTF-8?q?=20Document=20or=20list=20of=20Documents=20to=20prevent=20error?=
=?UTF-8?q?s?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/components/utilities/PostRequest.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/backend/langflow/components/utilities/PostRequest.py b/src/backend/langflow/components/utilities/PostRequest.py
index b32c12488..60a239d34 100644
--- a/src/backend/langflow/components/utilities/PostRequest.py
+++ b/src/backend/langflow/components/utilities/PostRequest.py
@@ -63,8 +63,10 @@ class PostRequest(CustomComponent):
if headers is None:
headers = {}
- if not isinstance(document, list):
+ if not isinstance(document, list) and isinstance(document, Document):
document = [document]
+ else:
+ raise ValueError("document must be a Document or a list of Documents")
with requests.Session() as session:
documents = [
From 8098cea4053e0fef77ddf2bf0cbc13c7485ec684 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 15:36:49 -0300
Subject: [PATCH 14/32] =?UTF-8?q?=F0=9F=90=9B=20fix(JSONDocumentBuilder.py?=
=?UTF-8?q?):=20fix=20JSON=20serialization=20issue=20by=20using=20orjson?=
=?UTF-8?q?=5Fdumps=20function=20=F0=9F=94=92=20chore(JSONDocumentBuilder.?=
=?UTF-8?q?py):=20add=20type=20checking=20and=20raise=20TypeError=20for=20?=
=?UTF-8?q?invalid=20input?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/utilities/JSONDocumentBuilder.py | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/backend/langflow/components/utilities/JSONDocumentBuilder.py b/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
index 4880200da..7cd41751f 100644
--- a/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
+++ b/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
@@ -13,6 +13,7 @@
from langflow import CustomComponent
from langchain.schema import Document
+from langflow.database.models.base import orjson_dumps
class JSONDocumentBuilder(CustomComponent):
@@ -34,9 +35,18 @@ class JSONDocumentBuilder(CustomComponent):
documents = None
if isinstance(document, list):
documents = [
- Document(page_content={key: doc.page_content}) for doc in document
+ Document(
+ page_content=orjson_dumps({key: doc.page_content}, indent_2=False)
+ )
+ for doc in document
]
+ elif isinstance(document, Document):
+ documents = Document(
+ page_content=orjson_dumps({key: document.page_content}, indent_2=False)
+ )
else:
- documents = Document(page_content={key: document.page_content})
+ raise TypeError(
+ f"Expected Document or list of Documents, got {type(document)}"
+ )
self.repr_value = documents
return documents
From 59652b887543b7830b0c78124c990433cec00ca1 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:11:10 -0300
Subject: [PATCH 15/32] =?UTF-8?q?=F0=9F=94=A7=20chore(prebuilt.py):=20impo?=
=?UTF-8?q?rt=20missing=20JSON=5FPREFIX=20and=20JsonToolkit=20to=20fix=20u?=
=?UTF-8?q?nresolved=20references=20=F0=9F=94=A7=20chore(prebuilt.py):=20i?=
=?UTF-8?q?mport=20missing=20JSON=5FPREFIX=20and=20JsonToolkit=20to=20fix?=
=?UTF-8?q?=20unresolved=20references=20in=20prebuilt.py?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/interface/agents/prebuilt.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/backend/langflow/interface/agents/prebuilt.py b/src/backend/langflow/interface/agents/prebuilt.py
index 1c62918d9..5b81b7713 100644
--- a/src/backend/langflow/interface/agents/prebuilt.py
+++ b/src/backend/langflow/interface/agents/prebuilt.py
@@ -1,6 +1,7 @@
from langchain import LLMChain
from langchain.agents import AgentExecutor, ZeroShotAgent
-from langchain.agents.agent_toolkits.json.prompt import JSON_SUFFIX
+from langchain.agents.agent_toolkits.json.prompt import JSON_PREFIX, JSON_SUFFIX
+from langchain.agents.agent_toolkits.json.toolkit import JsonToolkit
from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS
from langchain.base_language import BaseLanguageModel
From 6634e8864be090baed13e58fc93a03a1062c0bf1 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:11:24 -0300
Subject: [PATCH 16/32] =?UTF-8?q?=F0=9F=94=A7=20chore(vector=5Fstore.py):?=
=?UTF-8?q?=20import=20orjson=20library=20for=20JSON=20serialization=20and?=
=?UTF-8?q?=20deserialization=20=F0=9F=90=9B=20fix(vector=5Fstore.py):=20f?=
=?UTF-8?q?ix=20typo=20in=20function=20comment,=20change=20"OR"=20to=20"or?=
=?UTF-8?q?"=20for=20better=20readability?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/interface/initialize/vector_store.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/backend/langflow/interface/initialize/vector_store.py b/src/backend/langflow/interface/initialize/vector_store.py
index c8d8f0b02..233292626 100644
--- a/src/backend/langflow/interface/initialize/vector_store.py
+++ b/src/backend/langflow/interface/initialize/vector_store.py
@@ -11,6 +11,8 @@ from langchain.vectorstores import (
import os
+import orjson
+
def docs_in_params(params: dict) -> bool:
"""Check if params has documents OR texts and one of them is not an empty list,
From 39ae660e457de69bdaf21530caa630fd399b9a2e Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:11:37 -0300
Subject: [PATCH 17/32] =?UTF-8?q?=F0=9F=94=A7=20chore(loading.py):=20add?=
=?UTF-8?q?=20import=20statement=20for=20json=20module=20to=20fix=20missin?=
=?UTF-8?q?g=20import=20error?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/interface/initialize/loading.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/backend/langflow/interface/initialize/loading.py b/src/backend/langflow/interface/initialize/loading.py
index 6cb565e61..1cce109ab 100644
--- a/src/backend/langflow/interface/initialize/loading.py
+++ b/src/backend/langflow/interface/initialize/loading.py
@@ -1,3 +1,4 @@
+import json
import orjson
from typing import Any, Callable, Dict, Sequence, Type
From 1dbe776e6adf5045943e409791a7d146b9a887a3 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:11:47 -0300
Subject: [PATCH 18/32] =?UTF-8?q?=E2=9C=A8=20feat(utils.py):=20add=20funct?=
=?UTF-8?q?ion=20extract=5Finner=5Ftype=20to=20extract=20the=20inner=20typ?=
=?UTF-8?q?e=20from=20a=20type=20hint=20that=20is=20a=20list?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/interface/custom/utils.py | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 src/backend/langflow/interface/custom/utils.py
diff --git a/src/backend/langflow/interface/custom/utils.py b/src/backend/langflow/interface/custom/utils.py
new file mode 100644
index 000000000..99b0d4bc6
--- /dev/null
+++ b/src/backend/langflow/interface/custom/utils.py
@@ -0,0 +1,10 @@
+import re
+
+
+def extract_inner_type(return_type: str) -> str:
+ """
+ Extracts the inner type from a type hint that is a list.
+ """
+ if match := re.match(r"list\[(.*)\]", return_type, re.IGNORECASE):
+ return match[1]
+ return return_type
From 70f98f91508221ddddbfb07e2589b783e0f7df03 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:12:30 -0300
Subject: [PATCH 19/32] =?UTF-8?q?=F0=9F=90=9B=20fix(custom=5Fcomponent.py)?=
=?UTF-8?q?:=20extract=20inner=20type=20from=20return=20type=20if=20it=20s?=
=?UTF-8?q?tarts=20with=20'list'=20or=20'List'=20to=20handle=20list=20type?=
=?UTF-8?q?s=20correctly?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/interface/custom/custom_component.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/backend/langflow/interface/custom/custom_component.py b/src/backend/langflow/interface/custom/custom_component.py
index 4151f056d..e8747eac3 100644
--- a/src/backend/langflow/interface/custom/custom_component.py
+++ b/src/backend/langflow/interface/custom/custom_component.py
@@ -3,6 +3,7 @@ from fastapi import HTTPException
from langflow.interface.custom.constants import CUSTOM_COMPONENT_SUPPORTED_TYPES
from langflow.interface.custom.component import Component
from langflow.interface.custom.directory_reader import DirectoryReader
+from langflow.interface.custom.utils import extract_inner_type
from langflow.utils import validate
@@ -122,6 +123,10 @@ class CustomComponent(Component, extra=Extra.allow):
return_type = build_method["return_type"]
if not return_type:
return []
+ # If list or List is in the return type, then we remove it and return the inner type
+ if return_type.startswith("list") or return_type.startswith("List"):
+ return_type = extract_inner_type(return_type)
+
# If the return type is not a Union, then we just return it as a list
if "Union" not in return_type:
return [return_type] if return_type in self.return_type_valid_list else []
From 6fa2258aac30c39759250be146589df0ecaf3160 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:12:42 -0300
Subject: [PATCH 20/32] =?UTF-8?q?=F0=9F=94=A7=20chore(manager.py):=20impor?=
=?UTF-8?q?t=20orjson=20library=20for=20improved=20JSON=20serialization=20?=
=?UTF-8?q?and=20deserialization=20performance?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/chat/manager.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/backend/langflow/chat/manager.py b/src/backend/langflow/chat/manager.py
index 24e2212b8..5cd833c10 100644
--- a/src/backend/langflow/chat/manager.py
+++ b/src/backend/langflow/chat/manager.py
@@ -12,6 +12,7 @@ import asyncio
from typing import Any, Dict, List
from langflow.cache.flow import InMemoryCache
+import orjson
class ChatHistory(Subject):
From 05c8f5a0f8e19e86e9dd649e81f62d8f1952c8c1 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:13:07 -0300
Subject: [PATCH 21/32] =?UTF-8?q?=F0=9F=90=9B=20fix(GetRequest.py):=20upda?=
=?UTF-8?q?te=20orjson=5Fdumps=20function=20call=20to=20set=20indent=5F2?=
=?UTF-8?q?=20parameter=20to=20False=20=F0=9F=90=9B=20fix(GetRequest.py):?=
=?UTF-8?q?=20update=20return=20type=20annotation=20of=20get=5Fdocuments?=
=?UTF-8?q?=20method=20to=20list[Document]=20=F0=9F=90=9B=20fix(PostReques?=
=?UTF-8?q?t.py):=20update=20return=20type=20annotation=20of=20post=5Fdocu?=
=?UTF-8?q?ment=20method=20to=20list[Document]?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/components/utilities/GetRequest.py | 6 +++---
src/backend/langflow/components/utilities/PostRequest.py | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/backend/langflow/components/utilities/GetRequest.py b/src/backend/langflow/components/utilities/GetRequest.py
index 018b92751..40fe8935f 100644
--- a/src/backend/langflow/components/utilities/GetRequest.py
+++ b/src/backend/langflow/components/utilities/GetRequest.py
@@ -31,7 +31,7 @@ class GetRequest(CustomComponent):
response = session.get(url, headers=headers, timeout=timeout)
try:
response_json = response.json()
- result = orjson_dumps(response_json, indent=2)
+ result = orjson_dumps(response_json, indent_2=False)
except Exception:
result = response.text
self.repr_value = result
@@ -59,11 +59,11 @@ class GetRequest(CustomComponent):
url: str,
headers: Optional[dict] = None,
timeout: int = 5,
- ) -> Document:
+ ) -> list[Document]:
if headers is None:
headers = {}
if not isinstance(url, list):
- url = [url]
+ url: list[str] = [url]
with requests.Session() as session:
documents = [self.get_document(session, u, headers, timeout) for u in url]
self.repr_value = documents
diff --git a/src/backend/langflow/components/utilities/PostRequest.py b/src/backend/langflow/components/utilities/PostRequest.py
index 60a239d34..e0687a6f7 100644
--- a/src/backend/langflow/components/utilities/PostRequest.py
+++ b/src/backend/langflow/components/utilities/PostRequest.py
@@ -59,12 +59,12 @@ class PostRequest(CustomComponent):
document: Document,
url: str,
headers: Optional[dict] = None,
- ) -> Document:
+ ) -> list[Document]:
if headers is None:
headers = {}
if not isinstance(document, list) and isinstance(document, Document):
- document = [document]
+ document: list[Document] = [document]
else:
raise ValueError("document must be a Document or a list of Documents")
From 663e648af225ef6eca0ae5ce571c36aec247f283 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:15:02 -0300
Subject: [PATCH 22/32] =?UTF-8?q?=F0=9F=93=9D=20docs(utilities.mdx):=20upd?=
=?UTF-8?q?ate=20documentation=20for=20GET=20and=20POST=20requests=20to=20?=
=?UTF-8?q?reflect=20changes=20in=20the=20response=20format=20=F0=9F=93=9D?=
=?UTF-8?q?=20docs(utilities.mdx):=20clarify=20that=20multiple=20URLs=20ca?=
=?UTF-8?q?n=20be=20provided=20for=20GET=20requests=20and=20the=20response?=
=?UTF-8?q?=20will=20be=20a=20list=20of=20Documents?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/docs/components/utilities.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/docs/components/utilities.mdx b/docs/docs/components/utilities.mdx
index cb0aa808e..573db78e7 100644
--- a/docs/docs/components/utilities.mdx
+++ b/docs/docs/components/utilities.mdx
@@ -16,12 +16,12 @@ Make a GET request to the given URL.
**Params**
-- **URL:** The URL to make the request to.
+- **URL:** The URL to make the request to. There can be more than one URL, in which case the request will be made to each URL in order.
- **Headers:** A dictionary of headers to send with the request.
**Output**
-- **Document:** The JSON response from the request as a Document.
+- **List of Documents:** A list of Documents containing the JSON response from each request.
### POST Request
@@ -48,4 +48,4 @@ Build a Document containing a JSON object using a key and another Document page
**Output**
-- **Document:** The Document containing the JSON object.
+- **List of Documents:** A list containing the Document with the JSON object.
From 938fcbab62a1f4300799d588c448d67b5fbbf31b Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:37:43 -0300
Subject: [PATCH 23/32] =?UTF-8?q?=F0=9F=90=9B=20fix(GetRequest.py):=20chan?=
=?UTF-8?q?ge=20variable=20name=20'url'=20to=20'urls'=20to=20improve=20cla?=
=?UTF-8?q?rity=20and=20semantics=20=E2=9C=A8=20feat(PatchRequest.py):=20a?=
=?UTF-8?q?dd=20new=20component=20'PatchRequest'=20to=20make=20PATCH=20req?=
=?UTF-8?q?uests=20to=20a=20given=20URL=20=F0=9F=90=9B=20fix(PostRequest.p?=
=?UTF-8?q?y):=20change=20variable=20name=20'document'=20to=20'documents'?=
=?UTF-8?q?=20to=20improve=20clarity=20and=20semantics?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/utilities/GetRequest.py | 5 +-
.../components/utilities/PatchRequest.py | 76 +++++++++++++++++++
.../components/utilities/PostRequest.py | 8 +-
3 files changed, 84 insertions(+), 5 deletions(-)
create mode 100644 src/backend/langflow/components/utilities/PatchRequest.py
diff --git a/src/backend/langflow/components/utilities/GetRequest.py b/src/backend/langflow/components/utilities/GetRequest.py
index 40fe8935f..793d5b936 100644
--- a/src/backend/langflow/components/utilities/GetRequest.py
+++ b/src/backend/langflow/components/utilities/GetRequest.py
@@ -62,9 +62,8 @@ class GetRequest(CustomComponent):
) -> list[Document]:
if headers is None:
headers = {}
- if not isinstance(url, list):
- url: list[str] = [url]
+ urls = url if isinstance(url, list) else [url]
with requests.Session() as session:
- documents = [self.get_document(session, u, headers, timeout) for u in url]
+ documents = [self.get_document(session, u, headers, timeout) for u in urls]
self.repr_value = documents
return documents
diff --git a/src/backend/langflow/components/utilities/PatchRequest.py b/src/backend/langflow/components/utilities/PatchRequest.py
new file mode 100644
index 000000000..86fd2e1b4
--- /dev/null
+++ b/src/backend/langflow/components/utilities/PatchRequest.py
@@ -0,0 +1,76 @@
+from typing import List, Optional
+import requests
+from langflow import CustomComponent
+from langchain.schema import Document
+from langflow.database.models.base import orjson_dumps
+
+
+class PatchRequest(CustomComponent):
+ display_name: str = "Patch Request"
+ description: str = "Make a PATCH request to the given URL."
+ output_types: list[str] = ["Document"]
+ beta = True
+ field_config = {
+ "url": {"display_name": "URL", "info": "The URL to make the request to."},
+ "headers": {
+ "display_name": "Headers",
+ "field_type": "code",
+ "info": "The headers to send with the request.",
+ },
+ "code": {"show": False},
+ "document": {"display_name": "Document"},
+ }
+
+ def patch_document(
+ self,
+ session: requests.Session,
+ document: Document,
+ url: str,
+ headers: Optional[dict] = None,
+ ) -> Document:
+ try:
+ response = session.patch(url, headers=headers, data=document.page_content)
+ try:
+ response_json = response.json()
+ result = orjson_dumps(response_json, indent_2=False)
+ except Exception:
+ result = response.text
+ self.repr_value = result
+ return Document(
+ page_content=result,
+ metadata={
+ "source": url,
+ "headers": headers,
+ "status_code": response.status_code,
+ },
+ )
+ except Exception as exc:
+ return Document(
+ page_content=str(exc),
+ metadata={"source": url, "headers": headers, "status_code": 500},
+ )
+
+ def build(
+ self,
+ document: Document,
+ url: str,
+ headers: Optional[dict] = None,
+ ) -> List[Document]:
+ if headers is None:
+ headers = {}
+
+ if not isinstance(document, list) and isinstance(document, Document):
+ documents: list[Document] = [document]
+ elif isinstance(document, list) and all(
+ isinstance(doc, Document) for doc in document
+ ):
+ documents = document
+ else:
+ raise ValueError("document must be a Document or a list of Documents")
+
+ with requests.Session() as session:
+ documents = [
+ self.patch_document(session, doc, url, headers) for doc in documents
+ ]
+ self.repr_value = documents
+ return documents
diff --git a/src/backend/langflow/components/utilities/PostRequest.py b/src/backend/langflow/components/utilities/PostRequest.py
index e0687a6f7..ab02f9412 100644
--- a/src/backend/langflow/components/utilities/PostRequest.py
+++ b/src/backend/langflow/components/utilities/PostRequest.py
@@ -64,13 +64,17 @@ class PostRequest(CustomComponent):
headers = {}
if not isinstance(document, list) and isinstance(document, Document):
- document: list[Document] = [document]
+ documents: list[Document] = [document]
+ elif isinstance(document, list) and all(
+ isinstance(doc, Document) for doc in document
+ ):
+ documents = document
else:
raise ValueError("document must be a Document or a list of Documents")
with requests.Session() as session:
documents = [
- self.post_document(session, doc, url, headers) for doc in document
+ self.post_document(session, doc, url, headers) for doc in documents
]
self.repr_value = documents
return documents
From fb7513f7cb9f612e20ecae64623a6c7a1764b694 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:37:53 -0300
Subject: [PATCH 24/32] =?UTF-8?q?=F0=9F=90=9B=20fix(custom=5Fcomponent.py)?=
=?UTF-8?q?:=20change=20repr=5Fvalue=20type=20from=20str=20to=20Any=20to?=
=?UTF-8?q?=20allow=20any=20type=20of=20value=20to=20be=20assigned?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/interface/custom/custom_component.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/backend/langflow/interface/custom/custom_component.py b/src/backend/langflow/interface/custom/custom_component.py
index e8747eac3..31f8433bc 100644
--- a/src/backend/langflow/interface/custom/custom_component.py
+++ b/src/backend/langflow/interface/custom/custom_component.py
@@ -20,7 +20,7 @@ class CustomComponent(Component, extra=Extra.allow):
function_entrypoint_name = "build"
function: Optional[Callable] = None
return_type_valid_list = list(CUSTOM_COMPONENT_SUPPORTED_TYPES.keys())
- repr_value: Optional[str] = ""
+ repr_value: Optional[Any] = ""
def __init__(self, **data):
super().__init__(**data)
From f69fddb1a0227011561c7d009a1daeca61b8ce68 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:42:09 -0300
Subject: [PATCH 25/32] =?UTF-8?q?=F0=9F=94=A7=20chore(settings.py):=20add?=
=?UTF-8?q?=20missing=20import=20statement=20for=20json=20module?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
🔧 chore(settings.py): remove unused import statement for contextlib module
🔧 chore(settings.py): remove unused import statement for orjson module
🔧 chore(settings.py): remove unused import statement for os module
🔧 chore(settings.py): remove unused import statement for typing module
---
src/backend/langflow/settings.py | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/backend/langflow/settings.py b/src/backend/langflow/settings.py
index 9eea696b4..521f2be77 100644
--- a/src/backend/langflow/settings.py
+++ b/src/backend/langflow/settings.py
@@ -1,4 +1,5 @@
import contextlib
+import json
import orjson
import os
from typing import Optional, List
From b1779e849d90ca4a884ba3ad0991281be2005abb Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 16:46:57 -0300
Subject: [PATCH 26/32] =?UTF-8?q?=F0=9F=94=A5=20refactor(base.py):=20remov?=
=?UTF-8?q?e=20unused=20imports=20and=20code=20in=20base.py=20=F0=9F=94=A5?=
=?UTF-8?q?=20refactor(base.py):=20remove=20unused=20variables=20and=20fun?=
=?UTF-8?q?ctions=20in=20base.py?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../langflow/services/settings/base.py | 222 ------------------
1 file changed, 222 deletions(-)
delete mode 100644 src/backend/langflow/services/settings/base.py
diff --git a/src/backend/langflow/services/settings/base.py b/src/backend/langflow/services/settings/base.py
deleted file mode 100644
index c8e4ec380..000000000
--- a/src/backend/langflow/services/settings/base.py
+++ /dev/null
@@ -1,222 +0,0 @@
-import contextlib
-import orjson
-import os
-from shutil import copy2
-from typing import Optional, List
-from pathlib import Path
-
-import yaml
-from pydantic import BaseSettings, root_validator, validator
-from langflow.utils.logger import logger
-
-# BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
-BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components")
-
-
-class Settings(BaseSettings):
- CHAINS: dict = {}
- AGENTS: dict = {}
- PROMPTS: dict = {}
- LLMS: dict = {}
- TOOLS: dict = {}
- MEMORIES: dict = {}
- EMBEDDINGS: dict = {}
- VECTORSTORES: dict = {}
- DOCUMENTLOADERS: dict = {}
- WRAPPERS: dict = {}
- RETRIEVERS: dict = {}
- TOOLKITS: dict = {}
- TEXTSPLITTERS: dict = {}
- UTILITIES: dict = {}
- OUTPUT_PARSERS: dict = {}
- CUSTOM_COMPONENTS: dict = {}
-
- # Define the default LANGFLOW_DIR
- CONFIG_DIR: Optional[str] = None
-
- DEV: bool = False
- DATABASE_URL: Optional[str] = None
- CACHE: str = "InMemoryCache"
- REMOVE_API_KEYS: bool = False
- COMPONENTS_PATH: List[str] = []
-
- @validator("CONFIG_DIR", pre=True, allow_reuse=True)
- def set_langflow_dir(cls, value):
- if not value:
- import appdirs
-
- # Define the app name and author
- app_name = "langflow"
- app_author = "logspace"
-
- # Get the cache directory for the application
- cache_dir = appdirs.user_cache_dir(app_name, app_author)
-
- # Create a .langflow directory inside the cache directory
- value = Path(cache_dir)
- value.mkdir(parents=True, exist_ok=True)
-
- if isinstance(value, str):
- value = Path(value)
- if not value.exists():
- value.mkdir(parents=True, exist_ok=True)
-
- return str(value)
-
- @validator("DATABASE_URL", pre=True)
- def set_database_url(cls, value, values):
- if not value:
- logger.debug(
- "No database_url provided, trying LANGFLOW_DATABASE_URL env variable"
- )
- if langflow_database_url := os.getenv("LANGFLOW_DATABASE_URL"):
- value = langflow_database_url
- logger.debug("Using LANGFLOW_DATABASE_URL env variable.")
- else:
- logger.debug("No DATABASE_URL env variable, using sqlite database")
- # Originally, we used sqlite:///./langflow.db
- # so we need to migrate to the new format
- # if there is a database in that location
- if not values["CONFIG_DIR"]:
- raise ValueError(
- "CONFIG_DIR not set, please set it or provide a DATABASE_URL"
- )
-
- new_path = f"{values['CONFIG_DIR']}/langflow.db"
- if Path("./langflow.db").exists():
- if Path(new_path).exists():
- logger.debug(f"Database already exists at {new_path}, using it")
- else:
- try:
- logger.debug("Copying existing database to new location")
- copy2("./langflow.db", new_path)
- logger.debug(f"Copied existing database to {new_path}")
- except Exception:
- logger.error("Failed to copy database, using default path")
- new_path = "./langflow.db"
-
- value = f"sqlite:///{new_path}"
-
- return value
-
- @validator("COMPONENTS_PATH", pre=True)
- def set_components_path(cls, value):
- if os.getenv("LANGFLOW_COMPONENTS_PATH"):
- logger.debug("Adding LANGFLOW_COMPONENTS_PATH to components_path")
- langflow_component_path = os.getenv("LANGFLOW_COMPONENTS_PATH")
- if (
- Path(langflow_component_path).exists()
- and langflow_component_path not in value
- ):
- if isinstance(langflow_component_path, list):
- for path in langflow_component_path:
- if path not in value:
- value.append(path)
- logger.debug(
- f"Extending {langflow_component_path} to components_path"
- )
- elif langflow_component_path not in value:
- value.append(langflow_component_path)
- logger.debug(
- f"Appending {langflow_component_path} to components_path"
- )
-
- if not value:
- value = [BASE_COMPONENTS_PATH]
- logger.debug("Setting default components path to components_path")
- elif BASE_COMPONENTS_PATH not in value:
- value.append(BASE_COMPONENTS_PATH)
- logger.debug("Adding default components path to components_path")
-
- logger.debug(f"Components path: {value}")
- return value
-
- class Config:
- validate_assignment = True
- extra = "ignore"
- env_prefix = "LANGFLOW_"
-
- @root_validator(allow_reuse=True)
- def validate_lists(cls, values):
- for key, value in values.items():
- if key != "dev" and not value:
- values[key] = []
- return values
-
- def update_from_yaml(self, file_path: str, dev: bool = False):
- new_settings = load_settings_from_yaml(file_path)
- self.CHAINS = new_settings.CHAINS or {}
- self.AGENTS = new_settings.AGENTS or {}
- self.PROMPTS = new_settings.PROMPTS or {}
- self.LLMS = new_settings.LLMS or {}
- self.TOOLS = new_settings.TOOLS or {}
- self.MEMORIES = new_settings.MEMORIES or {}
- self.WRAPPERS = new_settings.WRAPPERS or {}
- self.TOOLKITS = new_settings.TOOLKITS or {}
- self.TEXTSPLITTERS = new_settings.TEXTSPLITTERS or {}
- self.UTILITIES = new_settings.UTILITIES or {}
- self.EMBEDDINGS = new_settings.EMBEDDINGS or {}
- self.VECTORSTORES = new_settings.VECTORSTORES or {}
- self.DOCUMENTLOADERS = new_settings.DOCUMENTLOADERS or {}
- self.RETRIEVERS = new_settings.RETRIEVERS or {}
- self.OUTPUT_PARSERS = new_settings.OUTPUT_PARSERS or {}
- self.CUSTOM_COMPONENTS = new_settings.CUSTOM_COMPONENTS or {}
- self.COMPONENTS_PATH = new_settings.COMPONENTS_PATH or []
- self.DEV = dev
-
- def update_settings(self, **kwargs):
- logger.debug("Updating settings")
- for key, value in kwargs.items():
- # value may contain sensitive information, so we don't want to log it
- if not hasattr(self, key):
- logger.debug(f"Key {key} not found in settings")
- continue
- logger.debug(f"Updating {key}")
- if isinstance(getattr(self, key), list):
- # value might be a '[something]' string
- with contextlib.suppress(json.decoder.JSONDecodeError):
- value = orjson.loads(str(value))
- if isinstance(value, list):
- for item in value:
- if isinstance(item, Path):
- item = str(item)
- if item not in getattr(self, key):
- getattr(self, key).append(item)
- logger.debug(f"Extended {key}")
- else:
- if isinstance(value, Path):
- value = str(value)
- if value not in getattr(self, key):
- getattr(self, key).append(value)
- logger.debug(f"Appended {key}")
-
- else:
- setattr(self, key, value)
- logger.debug(f"Updated {key}")
- logger.debug(f"{key}: {getattr(self, key)}")
-
-
-def save_settings_to_yaml(settings: Settings, file_path: str):
- with open(file_path, "w") as f:
- settings_dict = settings.dict()
- yaml.dump(settings_dict, f)
-
-
-def load_settings_from_yaml(file_path: str) -> Settings:
- # Check if a string is a valid path or a file name
- if "/" not in file_path:
- # Get current path
- current_path = os.path.dirname(os.path.abspath(__file__))
-
- file_path = os.path.join(current_path, file_path)
-
- with open(file_path, "r") as f:
- settings_dict = yaml.safe_load(f)
- settings_dict = {k.upper(): v for k, v in settings_dict.items()}
-
- for key in settings_dict:
- if key not in Settings.__fields__.keys():
- raise KeyError(f"Key {key} not found in settings")
- logger.debug(f"Loading {len(settings_dict[key])} {key} from {file_path}")
-
- return Settings(**settings_dict)
From 42a268a6789d4e7adeab139739df8ad27c4174af Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 17:35:20 -0300
Subject: [PATCH 27/32] =?UTF-8?q?=F0=9F=93=9D=20docs(components/utilities.?=
=?UTF-8?q?mdx):=20add=20documentation=20for=20Update=20Request=20feature?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
🚀 feat(UpdateRequest.py): add UpdateRequest component to make PATCH or PUT requests to a given URL
---
docs/docs/components/utilities.mdx | 15 +++++++++++
.../{PatchRequest.py => UpdateRequest.py} | 27 +++++++++++++++----
2 files changed, 37 insertions(+), 5 deletions(-)
rename src/backend/langflow/components/utilities/{PatchRequest.py => UpdateRequest.py} (72%)
diff --git a/docs/docs/components/utilities.mdx b/docs/docs/components/utilities.mdx
index 573db78e7..1bd36e035 100644
--- a/docs/docs/components/utilities.mdx
+++ b/docs/docs/components/utilities.mdx
@@ -37,6 +37,21 @@ Make a POST request to the given URL.
- **Document:** The JSON response from the request as a Document.
+### Update Request
+
+Make a PATCH or PUT request to the given URL.
+
+**Params**
+
+- **URL:** The URL to make the request to.
+- **Headers:** A dictionary of headers to send with the request.
+- **Document:** The Document containing a JSON object to send with the request.
+- **Method:** The HTTP method to use for the request. Can be either `PATCH` or `PUT`.
+
+**Output**
+
+- **Document:** The JSON response from the request as a Document.
+
### JSON Document Builder
Build a Document containing a JSON object using a key and another Document page content.
diff --git a/src/backend/langflow/components/utilities/PatchRequest.py b/src/backend/langflow/components/utilities/UpdateRequest.py
similarity index 72%
rename from src/backend/langflow/components/utilities/PatchRequest.py
rename to src/backend/langflow/components/utilities/UpdateRequest.py
index 86fd2e1b4..0e2b5518c 100644
--- a/src/backend/langflow/components/utilities/PatchRequest.py
+++ b/src/backend/langflow/components/utilities/UpdateRequest.py
@@ -5,8 +5,8 @@ from langchain.schema import Document
from langflow.database.models.base import orjson_dumps
-class PatchRequest(CustomComponent):
- display_name: str = "Patch Request"
+class UpdateRequest(CustomComponent):
+ display_name: str = "Update Request"
description: str = "Make a PATCH request to the given URL."
output_types: list[str] = ["Document"]
beta = True
@@ -19,17 +19,32 @@ class PatchRequest(CustomComponent):
},
"code": {"show": False},
"document": {"display_name": "Document"},
+ "method": {
+ "display_name": "Method",
+ "field_type": "str",
+ "info": "The HTTP method to use.",
+ "options": ["PATCH", "PUT"],
+ "value": "PATCH",
+ },
}
- def patch_document(
+ def update_document(
self,
session: requests.Session,
document: Document,
url: str,
headers: Optional[dict] = None,
+ method: str = "PATCH",
) -> Document:
try:
- response = session.patch(url, headers=headers, data=document.page_content)
+ if method == "PATCH":
+ response = session.patch(
+ url, headers=headers, data=document.page_content
+ )
+ elif method == "PUT":
+ response = session.put(url, headers=headers, data=document.page_content)
+ else:
+ raise ValueError(f"Unsupported method: {method}")
try:
response_json = response.json()
result = orjson_dumps(response_json, indent_2=False)
@@ -52,6 +67,7 @@ class PatchRequest(CustomComponent):
def build(
self,
+ method: str,
document: Document,
url: str,
headers: Optional[dict] = None,
@@ -70,7 +86,8 @@ class PatchRequest(CustomComponent):
with requests.Session() as session:
documents = [
- self.patch_document(session, doc, url, headers) for doc in documents
+ self.update_document(session, doc, url, headers, method)
+ for doc in documents
]
self.repr_value = documents
return documents
From 7dc258d580a398f699b01063ea0e0ef9c4323c14 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 17:43:15 -0300
Subject: [PATCH 28/32] =?UTF-8?q?=F0=9F=93=9D=20docs(utilities.mdx):=20upd?=
=?UTF-8?q?ate=20Utilities=20section=20in=20the=20sidebar=20to=20include?=
=?UTF-8?q?=20the=20new=20"Utilities"=20page=20=F0=9F=93=9D=20docs(sidebar?=
=?UTF-8?q?s.js):=20add=20"components/utilities"=20to=20the=20Components?=
=?UTF-8?q?=20section=20in=20the=20sidebar?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/docs/components/utilities.mdx | 10 +++++++++-
docs/sidebars.js | 1 +
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/docs/docs/components/utilities.mdx b/docs/docs/components/utilities.mdx
index 1bd36e035..87974d0bb 100644
--- a/docs/docs/components/utilities.mdx
+++ b/docs/docs/components/utilities.mdx
@@ -10,7 +10,11 @@ import Admonition from "@theme/Admonition";
-### Get Request
+Utilities are a set of actions that can be used to perform common tasks in a flow. They are available in the **Utilities** section in the sidebar.
+
+---
+
+### GET Request
Make a GET request to the given URL.
@@ -23,6 +27,8 @@ Make a GET request to the given URL.
- **List of Documents:** A list of Documents containing the JSON response from each request.
+---
+
### POST Request
Make a POST request to the given URL.
@@ -37,6 +43,8 @@ Make a POST request to the given URL.
- **Document:** The JSON response from the request as a Document.
+---
+
### Update Request
Make a PATCH or PUT request to the given URL.
diff --git a/docs/sidebars.js b/docs/sidebars.js
index fbabab150..e44b1cf4f 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -42,6 +42,7 @@ module.exports = {
"components/text-splitters",
"components/toolkits",
"components/tools",
+ "components/utilities",
"components/vector-stores",
"components/wrappers",
],
From c22a792a7f2edd2fc3b2d7fd1d73c339b6e5b109 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 17:45:45 -0300
Subject: [PATCH 29/32] =?UTF-8?q?=F0=9F=93=9A=20docs(GetRequest.py):=20add?=
=?UTF-8?q?=20documentation=20link=20for=20the=20GET=20Request=20component?=
=?UTF-8?q?=20=F0=9F=93=9A=20docs(JSONDocumentBuilder.py):=20add=20documen?=
=?UTF-8?q?tation=20link=20for=20the=20JSON=20Document=20Builder=20compone?=
=?UTF-8?q?nt=20=F0=9F=93=9A=20docs(PostRequest.py):=20add=20documentation?=
=?UTF-8?q?=20link=20for=20the=20POST=20Request=20component=20=F0=9F=93=9A?=
=?UTF-8?q?=20docs(UpdateRequest.py):=20add=20documentation=20link=20for?=
=?UTF-8?q?=20the=20Update=20Request=20component?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/components/utilities/GetRequest.py | 3 ++-
.../langflow/components/utilities/JSONDocumentBuilder.py | 3 +++
src/backend/langflow/components/utilities/PostRequest.py | 1 +
src/backend/langflow/components/utilities/UpdateRequest.py | 1 +
4 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/backend/langflow/components/utilities/GetRequest.py b/src/backend/langflow/components/utilities/GetRequest.py
index 793d5b936..2b76d2ed8 100644
--- a/src/backend/langflow/components/utilities/GetRequest.py
+++ b/src/backend/langflow/components/utilities/GetRequest.py
@@ -6,9 +6,10 @@ from typing import Optional
class GetRequest(CustomComponent):
- display_name: str = "Get Request"
+ display_name: str = "GET Request"
description: str = "Make a GET request to the given URL."
output_types: list[str] = ["Document"]
+ documentation: str = "https://docs.langflow.org/components/utilities#get-request"
beta = True
field_config = {
"url": {
diff --git a/src/backend/langflow/components/utilities/JSONDocumentBuilder.py b/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
index 7cd41751f..e41b6b6af 100644
--- a/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
+++ b/src/backend/langflow/components/utilities/JSONDocumentBuilder.py
@@ -21,6 +21,9 @@ class JSONDocumentBuilder(CustomComponent):
description: str = "Build a Document containing a JSON object using a key and another Document page content."
output_types: list[str] = ["Document"]
beta = True
+ documentation: str = (
+ "https://docs.langflow.org/components/utilities#json-document-builder"
+ )
field_config = {
"key": {"display_name": "Key"},
diff --git a/src/backend/langflow/components/utilities/PostRequest.py b/src/backend/langflow/components/utilities/PostRequest.py
index ab02f9412..88dd1eba6 100644
--- a/src/backend/langflow/components/utilities/PostRequest.py
+++ b/src/backend/langflow/components/utilities/PostRequest.py
@@ -9,6 +9,7 @@ class PostRequest(CustomComponent):
display_name: str = "Post Request"
description: str = "Make a POST request to the given URL."
output_types: list[str] = ["Document"]
+ documentation: str = "https://docs.langflow.org/components/utilities#post-request"
beta = True
field_config = {
"url": {"display_name": "URL", "info": "The URL to make the request to."},
diff --git a/src/backend/langflow/components/utilities/UpdateRequest.py b/src/backend/langflow/components/utilities/UpdateRequest.py
index 0e2b5518c..569dac013 100644
--- a/src/backend/langflow/components/utilities/UpdateRequest.py
+++ b/src/backend/langflow/components/utilities/UpdateRequest.py
@@ -9,6 +9,7 @@ class UpdateRequest(CustomComponent):
display_name: str = "Update Request"
description: str = "Make a PATCH request to the given URL."
output_types: list[str] = ["Document"]
+ documentation: str = "https://docs.langflow.org/components/utilities#update-request"
beta = True
field_config = {
"url": {"display_name": "URL", "info": "The URL to make the request to."},
From 4f2a14bed5cac455246ab5bcda223ac857671610 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 17:46:07 -0300
Subject: [PATCH 30/32] =?UTF-8?q?=F0=9F=93=9D=20docs(utilities.mdx):=20add?=
=?UTF-8?q?=20horizontal=20rule=20to=20improve=20readability=20and=20separ?=
=?UTF-8?q?ation=20of=20content=20=F0=9F=93=9D=20docs(utilities.mdx):=20ad?=
=?UTF-8?q?d=20description=20for=20JSON=20Document=20Builder=20to=20provid?=
=?UTF-8?q?e=20clarity=20on=20its=20purpose?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/docs/components/utilities.mdx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/docs/components/utilities.mdx b/docs/docs/components/utilities.mdx
index 87974d0bb..593864213 100644
--- a/docs/docs/components/utilities.mdx
+++ b/docs/docs/components/utilities.mdx
@@ -60,6 +60,8 @@ Make a PATCH or PUT request to the given URL.
- **Document:** The JSON response from the request as a Document.
+---
+
### JSON Document Builder
Build a Document containing a JSON object using a key and another Document page content.
From c6fc60d4b2951b1d074a24a0c142d04046b9cea6 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 17:52:17 -0300
Subject: [PATCH 31/32] =?UTF-8?q?=F0=9F=94=A7=20chore(GetRequest.py):=20ad?=
=?UTF-8?q?d=20timeout=20parameter=20to=20the=20GetRequest=20component=20f?=
=?UTF-8?q?or=20configurable=20request=20timeout=20=F0=9F=93=9D=20docs(Get?=
=?UTF-8?q?Request.py):=20update=20component=20documentation=20to=20includ?=
=?UTF-8?q?e=20information=20about=20the=20new=20timeout=20parameter?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/components/utilities/GetRequest.py | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/backend/langflow/components/utilities/GetRequest.py b/src/backend/langflow/components/utilities/GetRequest.py
index 2b76d2ed8..0a819fd38 100644
--- a/src/backend/langflow/components/utilities/GetRequest.py
+++ b/src/backend/langflow/components/utilities/GetRequest.py
@@ -23,6 +23,12 @@ class GetRequest(CustomComponent):
"info": "The headers to send with the request.",
},
"code": {"show": False},
+ "timeout": {
+ "display_name": "Timeout",
+ "field_type": "int",
+ "info": "The timeout to use for the request.",
+ "value": 5,
+ },
}
def get_document(
From cea4063637cbbbb96c7e54eec9633a7868d14987 Mon Sep 17 00:00:00 2001
From: Gabriel Luiz Freitas Almeida
Date: Tue, 22 Aug 2023 17:53:55 -0300
Subject: [PATCH 32/32] =?UTF-8?q?=F0=9F=90=9B=20fix(GetRequest.py):=20conv?=
=?UTF-8?q?ert=20timeout=20to=20integer=20to=20avoid=20potential=20type=20?=
=?UTF-8?q?errors=20=E2=9C=A8=20feat(PostRequest.py):=20update=20display?=
=?UTF-8?q?=20name=20to=20"POST=20Request"=20for=20better=20clarity=20and?=
=?UTF-8?q?=20consistency?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/backend/langflow/components/utilities/GetRequest.py | 2 +-
src/backend/langflow/components/utilities/PostRequest.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/backend/langflow/components/utilities/GetRequest.py b/src/backend/langflow/components/utilities/GetRequest.py
index 0a819fd38..c8182ad0d 100644
--- a/src/backend/langflow/components/utilities/GetRequest.py
+++ b/src/backend/langflow/components/utilities/GetRequest.py
@@ -35,7 +35,7 @@ class GetRequest(CustomComponent):
self, session: requests.Session, url: str, headers: Optional[dict], timeout: int
) -> Document:
try:
- response = session.get(url, headers=headers, timeout=timeout)
+ response = session.get(url, headers=headers, timeout=int(timeout))
try:
response_json = response.json()
result = orjson_dumps(response_json, indent_2=False)
diff --git a/src/backend/langflow/components/utilities/PostRequest.py b/src/backend/langflow/components/utilities/PostRequest.py
index 88dd1eba6..9d5ab504f 100644
--- a/src/backend/langflow/components/utilities/PostRequest.py
+++ b/src/backend/langflow/components/utilities/PostRequest.py
@@ -6,7 +6,7 @@ from typing import Optional
class PostRequest(CustomComponent):
- display_name: str = "Post Request"
+ display_name: str = "POST Request"
description: str = "Make a POST request to the given URL."
output_types: list[str] = ["Document"]
documentation: str = "https://docs.langflow.org/components/utilities#post-request"