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"