Merge branch 'dev' into bugfix-tweakPage
This commit is contained in:
commit
668387dde8
49 changed files with 1128 additions and 502 deletions
|
|
@ -106,14 +106,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
|
||||
|
|
@ -128,7 +123,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
|
||||
|
|
|
|||
|
|
@ -58,12 +58,9 @@ def get_all():
|
|||
|
||||
logger.info(f"Loading {len(custom_component_dicts)} category(ies)")
|
||||
for custom_component_dict in custom_component_dicts:
|
||||
# custom_component_dict is a dict of dicts
|
||||
category = list(custom_component_dict.keys())[0]
|
||||
logger.info(
|
||||
f"Loading {len(custom_component_dict[category])} component(s) from category {category}"
|
||||
logger.debug(
|
||||
{key: len(value) for key, value in custom_component_dict.items()}
|
||||
)
|
||||
logger.debug(custom_component_dict)
|
||||
custom_components_from_file = merge_nested_dicts_with_renaming(
|
||||
custom_components_from_file, custom_component_dict
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from typing import List
|
||||
from uuid import UUID
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
|
||||
from langflow.api.utils import remove_api_keys
|
||||
from langflow.api.v1.schemas import FlowListCreate, FlowListRead
|
||||
from langflow.services.database.models.flow import (
|
||||
|
|
@ -10,12 +12,11 @@ from langflow.services.database.models.flow import (
|
|||
)
|
||||
from langflow.services.utils import get_session
|
||||
from langflow.services.utils import get_settings_manager
|
||||
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 +106,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:
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ from enum import Enum
|
|||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
from langflow.services.database.models.flow import FlowCreate, FlowRead
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
|
||||
from pydantic import BaseModel, Field, validator
|
||||
import json
|
||||
|
||||
|
||||
class BuildStatus(Enum):
|
||||
|
|
@ -116,7 +117,9 @@ 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, indent_2=False)}\n\n"
|
||||
)
|
||||
|
||||
|
||||
class CustomComponentCode(BaseModel):
|
||||
|
|
|
|||
76
src/backend/langflow/components/utilities/GetRequest.py
Normal file
76
src/backend/langflow/components/utilities/GetRequest.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
from langflow import CustomComponent
|
||||
from langchain.schema import Document
|
||||
from langflow.services.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"]
|
||||
documentation: str = "https://docs.langflow.org/components/utilities#get-request"
|
||||
beta = True
|
||||
field_config = {
|
||||
"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},
|
||||
"timeout": {
|
||||
"display_name": "Timeout",
|
||||
"field_type": "int",
|
||||
"info": "The timeout to use for the request.",
|
||||
"value": 5,
|
||||
},
|
||||
}
|
||||
|
||||
def get_document(
|
||||
self, session: requests.Session, url: str, headers: Optional[dict], timeout: int
|
||||
) -> Document:
|
||||
try:
|
||||
response = session.get(url, headers=headers, timeout=int(timeout))
|
||||
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 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,
|
||||
) -> list[Document]:
|
||||
if headers is None:
|
||||
headers = {}
|
||||
urls = url if isinstance(url, list) else [url]
|
||||
with requests.Session() as session:
|
||||
documents = [self.get_document(session, u, headers, timeout) for u in urls]
|
||||
self.repr_value = documents
|
||||
return documents
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
### 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
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
|
||||
|
||||
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
|
||||
documentation: str = (
|
||||
"https://docs.langflow.org/components/utilities#json-document-builder"
|
||||
)
|
||||
|
||||
field_config = {
|
||||
"key": {"display_name": "Key"},
|
||||
"document": {"display_name": "Document"},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
key: str,
|
||||
document: Document,
|
||||
) -> Document:
|
||||
documents = None
|
||||
if isinstance(document, list):
|
||||
documents = [
|
||||
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:
|
||||
raise TypeError(
|
||||
f"Expected Document or list of Documents, got {type(document)}"
|
||||
)
|
||||
self.repr_value = documents
|
||||
return documents
|
||||
81
src/backend/langflow/components/utilities/PostRequest.py
Normal file
81
src/backend/langflow/components/utilities/PostRequest.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
from langflow import CustomComponent
|
||||
from langchain.schema import Document
|
||||
from langflow.services.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"]
|
||||
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."},
|
||||
"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,
|
||||
session: requests.Session,
|
||||
document: Document,
|
||||
url: str,
|
||||
headers: Optional[dict] = None,
|
||||
) -> Document:
|
||||
try:
|
||||
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,
|
||||
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.post_document(session, doc, url, headers) for doc in documents
|
||||
]
|
||||
self.repr_value = documents
|
||||
return documents
|
||||
94
src/backend/langflow/components/utilities/UpdateRequest.py
Normal file
94
src/backend/langflow/components/utilities/UpdateRequest.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
from typing import List, Optional
|
||||
import requests
|
||||
from langflow import CustomComponent
|
||||
from langchain.schema import Document
|
||||
from langflow.services.database.models.base import orjson_dumps
|
||||
|
||||
|
||||
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."},
|
||||
"headers": {
|
||||
"display_name": "Headers",
|
||||
"field_type": "code",
|
||||
"info": "The headers to send with the request.",
|
||||
},
|
||||
"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 update_document(
|
||||
self,
|
||||
session: requests.Session,
|
||||
document: Document,
|
||||
url: str,
|
||||
headers: Optional[dict] = None,
|
||||
method: str = "PATCH",
|
||||
) -> Document:
|
||||
try:
|
||||
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)
|
||||
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,
|
||||
method: str,
|
||||
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.update_document(session, doc, url, headers, method)
|
||||
for doc in documents
|
||||
]
|
||||
self.repr_value = documents
|
||||
return documents
|
||||
109
src/backend/langflow/components/vectorstores/Chroma.py
Normal file
109
src/backend/langflow/components/vectorstores/Chroma.py
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
from typing import Optional, Union
|
||||
from langflow import CustomComponent
|
||||
|
||||
from langchain.vectorstores import Chroma
|
||||
from langchain.schema import Document
|
||||
from langchain.vectorstores.base import VectorStore
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain.embeddings.base import Embeddings
|
||||
import chromadb
|
||||
|
||||
|
||||
class ChromaComponent(CustomComponent):
|
||||
"""
|
||||
A custom component for implementing a Vector Store using Chroma.
|
||||
"""
|
||||
|
||||
display_name: str = "Chroma (Custom Component)"
|
||||
description: str = "Implementation of Vector Store using Chroma"
|
||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/chroma"
|
||||
beta = True
|
||||
|
||||
def build_config(self):
|
||||
"""
|
||||
Builds the configuration for the component.
|
||||
|
||||
Returns:
|
||||
- dict: A dictionary containing the configuration options for the component.
|
||||
"""
|
||||
return {
|
||||
"collection_name": {"display_name": "Collection Name", "value": "langflow"},
|
||||
"persist": {"display_name": "Persist"},
|
||||
"persist_directory": {"display_name": "Persist Directory"},
|
||||
"code": {"show": False, "display_name": "Code"},
|
||||
"documents": {"display_name": "Documents", "is_list": True},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
"chroma_server_cors_allow_origins": {
|
||||
"display_name": "Server CORS Allow Origins",
|
||||
"advanced": True,
|
||||
},
|
||||
"chroma_server_host": {"display_name": "Server Host", "advanced": True},
|
||||
"chroma_server_port": {"display_name": "Server Port", "advanced": True},
|
||||
"chroma_server_grpc_port": {
|
||||
"display_name": "Server gRPC Port",
|
||||
"advanced": True,
|
||||
},
|
||||
"chroma_server_ssl_enabled": {
|
||||
"display_name": "Server SSL Enabled",
|
||||
"advanced": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
collection_name: str,
|
||||
persist: bool,
|
||||
chroma_server_ssl_enabled: bool,
|
||||
persist_directory: Optional[str] = None,
|
||||
embedding: Optional[Embeddings] = None,
|
||||
documents: Optional[Document] = None,
|
||||
chroma_server_cors_allow_origins: Optional[str] = None,
|
||||
chroma_server_host: Optional[str] = None,
|
||||
chroma_server_port: Optional[int] = None,
|
||||
chroma_server_grpc_port: Optional[int] = None,
|
||||
) -> Union[VectorStore, BaseRetriever]:
|
||||
"""
|
||||
Builds the Vector Store or BaseRetriever object.
|
||||
|
||||
Args:
|
||||
- collection_name (str): The name of the collection.
|
||||
- persist_directory (Optional[str]): The directory to persist the Vector Store to.
|
||||
- chroma_server_ssl_enabled (bool): Whether to enable SSL for the Chroma server.
|
||||
- persist (bool): Whether to persist the Vector Store or not.
|
||||
- embedding (Optional[Embeddings]): The embeddings to use for the Vector Store.
|
||||
- documents (Optional[Document]): The documents to use for the Vector Store.
|
||||
- chroma_server_cors_allow_origins (Optional[str]): The CORS allow origins for the Chroma server.
|
||||
- chroma_server_host (Optional[str]): The host for the Chroma server.
|
||||
- chroma_server_port (Optional[int]): The port for the Chroma server.
|
||||
- chroma_server_grpc_port (Optional[int]): The gRPC port for the Chroma server.
|
||||
|
||||
Returns:
|
||||
- Union[VectorStore, BaseRetriever]: The Vector Store or BaseRetriever object.
|
||||
"""
|
||||
|
||||
# Chroma settings
|
||||
chroma_settings = None
|
||||
|
||||
if chroma_server_host is not None:
|
||||
chroma_settings = chromadb.config.Settings(
|
||||
chroma_server_cors_allow_origins=chroma_server_cors_allow_origins
|
||||
or None,
|
||||
chroma_server_host=chroma_server_host,
|
||||
chroma_server_port=chroma_server_port or None,
|
||||
chroma_server_grpc_port=chroma_server_grpc_port or None,
|
||||
chroma_server_ssl_enabled=chroma_server_ssl_enabled,
|
||||
)
|
||||
|
||||
# If documents, then we need to create a Chroma instance using .from_documents
|
||||
if documents is not None and embedding is not None:
|
||||
return Chroma.from_documents(
|
||||
documents=documents, # type: ignore
|
||||
persist_directory=persist_directory if persist else None,
|
||||
collection_name=collection_name,
|
||||
embedding=embedding,
|
||||
client_settings=chroma_settings,
|
||||
)
|
||||
|
||||
return Chroma(
|
||||
persist_directory=persist_directory, client_settings=chroma_settings
|
||||
)
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ 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.services.utils import get_db_manager
|
||||
from langflow.interface.custom.utils import extract_inner_type
|
||||
|
||||
from langflow.utils import validate
|
||||
|
||||
|
|
@ -20,7 +21,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)
|
||||
|
|
@ -123,6 +124,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 []
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
||||
|
|
|
|||
10
src/backend/langflow/interface/custom/utils.py
Normal file
10
src/backend/langflow/interface/custom/utils.py
Normal file
|
|
@ -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
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import json
|
||||
import orjson
|
||||
from typing import Any, Callable, Dict, Sequence, Type
|
||||
|
||||
from langchain.agents import agent as agent_module
|
||||
|
|
@ -66,7 +67,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
|
||||
|
|
@ -310,7 +311,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."
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import contextlib
|
||||
import json
|
||||
from langflow.services.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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import json
|
||||
from typing import Any, Callable, Dict, Type
|
||||
from langchain.vectorstores import (
|
||||
Pinecone,
|
||||
|
|
@ -12,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,
|
||||
|
|
@ -92,7 +93,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"),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
@ -338,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"]
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
5
src/backend/langflow/services/cache/utils.py
vendored
5
src/backend/langflow/services/cache/utils.py
vendored
|
|
@ -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.services.database.models.base import orjson_dumps
|
||||
|
||||
CACHE: Dict[str, Any] = {}
|
||||
|
||||
|
|
@ -90,7 +90,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()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,10 @@ from langflow.utils.logger import logger
|
|||
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from langflow.services.cache.flow import InMemoryCache
|
||||
import orjson
|
||||
|
||||
|
||||
class ChatHistory(Subject):
|
||||
|
|
@ -190,7 +190,7 @@ class ChatManager(Service):
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import contextlib
|
||||
import json
|
||||
import orjson
|
||||
import os
|
||||
from shutil import copy2
|
||||
import secrets
|
||||
|
|
@ -185,7 +186,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):
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import json
|
||||
from typing import Optional
|
||||
from langflow.services.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:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import ast
|
||||
import json
|
||||
from typing import Optional
|
||||
from langflow.services.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)
|
||||
|
|
|
|||
0
src/frontend/set_proxy.sh
Executable file → Normal file
0
src/frontend/set_proxy.sh
Executable file → Normal file
|
|
@ -9,10 +9,16 @@ import ErrorAlert from "./alerts/error";
|
|||
import NoticeAlert from "./alerts/notice";
|
||||
import SuccessAlert from "./alerts/success";
|
||||
import CrashErrorComponent from "./components/CrashErrorComponent";
|
||||
import FetchErrorComponent from "./components/fetchErrorComponent";
|
||||
import LoadingComponent from "./components/loadingComponent";
|
||||
import {
|
||||
FETCH_ERROR_DESCRIPION,
|
||||
FETCH_ERROR_MESSAGE,
|
||||
} from "./constants/constants";
|
||||
import { alertContext } from "./contexts/alertContext";
|
||||
import { locationContext } from "./contexts/locationContext";
|
||||
import { TabsContext } from "./contexts/tabsContext";
|
||||
import { typesContext } from "./contexts/typesContext";
|
||||
import Router from "./routes";
|
||||
|
||||
export default function App() {
|
||||
|
|
@ -38,6 +44,7 @@ export default function App() {
|
|||
setSuccessOpen,
|
||||
loading,
|
||||
} = useContext(alertContext);
|
||||
const { fetchError } = useContext(typesContext);
|
||||
|
||||
// Initialize state variable for the list of alerts
|
||||
const [alertsList, setAlertsList] = useState<
|
||||
|
|
@ -137,7 +144,14 @@ export default function App() {
|
|||
>
|
||||
{loading ? (
|
||||
<div className="loading-page-panel">
|
||||
<LoadingComponent remSize={50} />
|
||||
{fetchError ? (
|
||||
<FetchErrorComponent
|
||||
description={FETCH_ERROR_DESCRIPION}
|
||||
message={FETCH_ERROR_MESSAGE}
|
||||
></FetchErrorComponent>
|
||||
) : (
|
||||
<LoadingComponent remSize={50} />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
|
|
|
|||
16
src/frontend/src/components/fetchErrorComponent/index.tsx
Normal file
16
src/frontend/src/components/fetchErrorComponent/index.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { fetchErrorComponentType } from "../../types/components";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
|
||||
export default function FetchErrorComponent({
|
||||
message,
|
||||
description,
|
||||
}: fetchErrorComponentType) {
|
||||
return (
|
||||
<div role="status" className="m-auto flex flex-col items-center">
|
||||
<IconComponent className={`h-16 w-16`} name="Unplug"></IconComponent>
|
||||
<br></br>
|
||||
<span className="text-lg text-almost-medium-blue">{message}</span>
|
||||
<span className="text-lg text-almost-medium-blue">{description}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -9,9 +9,9 @@ export default function IconComponent({
|
|||
const TargetIcon = nodeIconsLucide[name] ?? nodeIconsLucide["unknown"];
|
||||
return (
|
||||
<TargetIcon
|
||||
strokeWidth={1.5}
|
||||
className={className}
|
||||
style={{ color: iconColor }}
|
||||
stroke-width={1.5}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -602,3 +602,6 @@ export function tabsArray(codes: string[], method: number) {
|
|||
},
|
||||
];
|
||||
}
|
||||
export const FETCH_ERROR_MESSAGE = "Couldn't establish a connection.";
|
||||
export const FETCH_ERROR_DESCRIPION =
|
||||
"Check if everything is working properly and try again.";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
useState,
|
||||
} from "react";
|
||||
import { Node, ReactFlowInstance } from "reactflow";
|
||||
import { getAll } from "../controllers/API";
|
||||
import { getAll, getHealth } from "../controllers/API";
|
||||
import { APIKindType } from "../types/api";
|
||||
import { typesContextType } from "../types/typesContext";
|
||||
import { alertContext } from "./alertContext";
|
||||
|
|
@ -23,6 +23,8 @@ const initialValue: typesContextType = {
|
|||
setTemplates: () => {},
|
||||
data: {},
|
||||
setData: () => {},
|
||||
setFetchError: () => {},
|
||||
fetchError: false,
|
||||
};
|
||||
|
||||
export const typesContext = createContext<typesContextType>(initialValue);
|
||||
|
|
@ -33,14 +35,10 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
useState<ReactFlowInstance | null>(null);
|
||||
const [templates, setTemplates] = useState({});
|
||||
const [data, setData] = useState({});
|
||||
const [fetchError, setFetchError] = useState(false);
|
||||
const { setLoading } = useContext(alertContext);
|
||||
|
||||
useEffect(() => {
|
||||
let delay = 1000; // Start delay of 1 second
|
||||
let intervalId: NodeJS.Timer;
|
||||
let retryCount = 0; // Count of retry attempts
|
||||
const maxRetryCount = 5; // Max retry attempts
|
||||
|
||||
// We will keep a flag to handle the case where the component is unmounted before the API call resolves.
|
||||
let isMounted = true;
|
||||
|
||||
|
|
@ -48,7 +46,7 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
try {
|
||||
const result = await getAll();
|
||||
// Make sure to only update the state if the component is still mounted.
|
||||
if (isMounted) {
|
||||
if (isMounted && result?.status === 200) {
|
||||
setLoading(false);
|
||||
setData(result.data);
|
||||
setTemplates(
|
||||
|
|
@ -78,21 +76,15 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
}, {})
|
||||
);
|
||||
}
|
||||
// Clear the interval if successful.
|
||||
clearInterval(intervalId!);
|
||||
} catch (error) {
|
||||
console.error("An error has occurred while fetching types.");
|
||||
await getHealth().catch((e) => {
|
||||
setFetchError(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Start the initial interval.
|
||||
intervalId = setInterval(getTypes, delay);
|
||||
return () => {
|
||||
// This will clear the interval when the component unmounts, or when the dependencies of the useEffect hook change.
|
||||
clearInterval(intervalId!);
|
||||
// Indicate that the component has been unmounted.
|
||||
isMounted = false;
|
||||
};
|
||||
getTypes();
|
||||
}, []);
|
||||
|
||||
function deleteNode(idx: string) {
|
||||
|
|
@ -117,6 +109,8 @@ export function TypesProvider({ children }: { children: ReactNode }) {
|
|||
templates,
|
||||
data,
|
||||
setData,
|
||||
fetchError,
|
||||
setFetchError,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -508,3 +508,7 @@ export type validationStatusType = {
|
|||
progress: number;
|
||||
valid: boolean;
|
||||
};
|
||||
export type fetchErrorComponentType = {
|
||||
message: string;
|
||||
description: string;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ export type typesContextType = {
|
|||
setTemplates: (newState: {}) => void;
|
||||
data: APIDataType;
|
||||
setData: (newState: {}) => void;
|
||||
fetchError: boolean;
|
||||
setFetchError: (newState: boolean) => void;
|
||||
};
|
||||
|
||||
export type alertContextType = {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import {
|
|||
TerminalSquare,
|
||||
Trash2,
|
||||
Undo,
|
||||
Unplug,
|
||||
Upload,
|
||||
UserMinus2,
|
||||
UserPlus2,
|
||||
|
|
@ -290,4 +291,5 @@ export const nodeIconsLucide: iconsType = {
|
|||
ChevronsLeft,
|
||||
FaGithub,
|
||||
FaApple,
|
||||
Unplug,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue