diff --git a/src/backend/langflow/__main__.py b/src/backend/langflow/__main__.py index 312285a68..1f16744ae 100644 --- a/src/backend/langflow/__main__.py +++ b/src/backend/langflow/__main__.py @@ -7,7 +7,7 @@ from fastapi.staticfiles import StaticFiles from langflow.main import create_app from langflow.settings import settings -from langflow.utils.logger import configure, logger +from langflow.utils.logger import configure app = typer.Typer() diff --git a/src/backend/langflow/config.yaml b/src/backend/langflow/config.yaml index da314b751..85130c9a0 100644 --- a/src/backend/langflow/config.yaml +++ b/src/backend/langflow/config.yaml @@ -1,8 +1,8 @@ chains: - LLMChain - LLMMathChain - - LLMChecker - # - ConversationChain + - LLMCheckerChain + - ConversationChain agents: - ZeroShotAgent @@ -36,19 +36,16 @@ wrappers: - RequestsWrapper toolkits: - - OpenAPIToolkit - - JsonToolkit + - OpenAPIToolkit + - JsonToolkit memories: - ConversationBufferMemory embeddings: [] - vectorstores: [] - documentloaders: [] - dev: false diff --git a/src/backend/langflow/graph/base.py b/src/backend/langflow/graph/base.py index 45bba3d70..fa93ed8ed 100644 --- a/src/backend/langflow/graph/base.py +++ b/src/backend/langflow/graph/base.py @@ -5,7 +5,7 @@ import types from copy import deepcopy -from typing import Any, Dict, List +from typing import Any, Dict, List, Optional from langflow.graph.constants import DIRECT_TYPES from langflow.graph.utils import load_file @@ -15,11 +15,11 @@ from langflow.utils.logger import logger class Node: - def __init__(self, data: Dict, base_type: str | None = None) -> None: + def __init__(self, data: Dict, base_type: Optional[str] = None) -> None: self.id: str = data["id"] self._data = data self.edges: List[Edge] = [] - self.base_type: str | None = base_type + self.base_type: Optional[str] = base_type self._parse_data() self._built_object = None self._built = False @@ -80,51 +80,44 @@ class Node: continue # If the type is not transformable to a python base class # then we need to get the edge that connects to this node - if value["type"] == "file": + if value.get("type") == "file": # Load the type in value.get('suffixes') using # what is inside value.get('content') # value.get('value') is the file name - type_to_load = value.get("suffixes") file_name = value.get("value") content = value.get("content") + type_to_load = value.get("suffixes") loaded_dict = load_file(file_name, content, type_to_load) params[key] = loaded_dict # We should check if the type is in something not # the opposite - elif value["type"] not in DIRECT_TYPES: + elif value.get("type") not in DIRECT_TYPES: # Get the edge that connects to this node - try: - edge = next( - ( - edge - for edge in self.edges - if edge.target == self - and edge.matched_type in value["type"] - ), - None, - ) + edges = [ + edge + for edge in self.edges + if edge.target == self and edge.matched_type in value["type"] + ] - except Exception as e: - raise e # Get the output of the node that the edge connects to # if the value['list'] is True, then there will be more # than one time setting to params[key] # so we need to append to a list if it exists # or create a new list if it doesn't - if edge is None and value["required"]: - # break line + if value["required"] and not edges: + # If a required parameter is not found, raise an error raise ValueError( f"Required input {key} for module {self.node_type} not found" ) elif value["list"]: - if key not in params: - params[key] = [] - if edge is not None: - params[key].append(edge.source) - elif value["required"] or edge is not None: - params[key] = edge.source + # If this is a list parameter, append all sources to a list + params[key] = [edge.source for edge in edges] + elif edges: + # If a single parameter is found, use its source + params[key] = edges[0].source + elif value["required"] or value.get("value"): params[key] = value["value"] diff --git a/src/backend/langflow/graph/graph.py b/src/backend/langflow/graph/graph.py index 8bd2028ca..022deac95 100644 --- a/src/backend/langflow/graph/graph.py +++ b/src/backend/langflow/graph/graph.py @@ -17,7 +17,8 @@ from langflow.interface.llms.base import llm_creator from langflow.interface.prompts.base import prompt_creator from langflow.interface.toolkits.base import toolkits_creator from langflow.interface.tools.base import tool_creator -from langflow.interface.tools.constants import ALL_TOOLS_NAMES, FILE_TOOLS +from langflow.interface.tools.constants import FILE_TOOLS +from langflow.interface.tools.util import get_tools_dict from langflow.interface.wrappers.base import wrapper_creator from langflow.utils import payload @@ -113,7 +114,10 @@ class Graph: nodes.append(AgentNode(node)) elif node_type in chain_creator.to_list(): nodes.append(ChainNode(node)) - elif node_type in tool_creator.to_list() or node_lc_type in ALL_TOOLS_NAMES: + elif ( + node_type in tool_creator.to_list() + or node_lc_type in get_tools_dict().keys() + ): if node_type in FILE_TOOLS: nodes.append(FileToolNode(node)) nodes.append(ToolNode(node)) diff --git a/src/backend/langflow/interface/agents/base.py b/src/backend/langflow/interface/agents/base.py index 464c04276..94cab1411 100644 --- a/src/backend/langflow/interface/agents/base.py +++ b/src/backend/langflow/interface/agents/base.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Optional from langchain.agents import loading @@ -21,7 +21,7 @@ class AgentCreator(LangChainTypeCreator): self.type_dict[name] = agent return self.type_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: try: if name in get_custom_nodes(self.type_name).keys(): return get_custom_nodes(self.type_name)[name] diff --git a/src/backend/langflow/interface/agents/custom.py b/src/backend/langflow/interface/agents/custom.py index 60975999a..ad9f6c918 100644 --- a/src/backend/langflow/interface/agents/custom.py +++ b/src/backend/langflow/interface/agents/custom.py @@ -1,17 +1,16 @@ from typing import Any, List, Optional from langchain import LLMChain -from langchain.agents import AgentExecutor, ZeroShotAgent +from langchain.agents import AgentExecutor, Tool, ZeroShotAgent, initialize_agent 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.pandas.prompt import PREFIX as PANDAS_PREFIX from langchain.agents.agent_toolkits.pandas.prompt import SUFFIX as PANDAS_SUFFIX from langchain.agents.mrkl.prompt import FORMAT_INSTRUCTIONS -from langchain.schema import BaseLanguageModel from langchain.llms.base import BaseLLM -from langchain.tools.python.tool import PythonAstREPLTool -from langchain.agents import initialize_agent, Tool from langchain.memory.chat_memory import BaseChatMemory +from langchain.schema import BaseLanguageModel +from langchain.tools.python.tool import PythonAstREPLTool class JsonAgent(AgentExecutor): diff --git a/src/backend/langflow/interface/chains/base.py b/src/backend/langflow/interface/chains/base.py index e24cba99b..36542b7d4 100644 --- a/src/backend/langflow/interface/chains/base.py +++ b/src/backend/langflow/interface/chains/base.py @@ -1,10 +1,9 @@ -from typing import Dict, List - -from langchain.chains import loading as chains_loading +from typing import Dict, List, Optional from langflow.interface.base import LangChainTypeCreator +from langflow.interface.custom_lists import chain_type_to_cls_dict from langflow.settings import settings -from langflow.utils.util import build_template_from_function +from langflow.utils.util import build_template_from_class # Assuming necessary imports for Field, Template, and FrontendNode classes @@ -15,25 +14,20 @@ class ChainCreator(LangChainTypeCreator): @property def type_to_loader_dict(self) -> Dict: if self.type_dict is None: - self.type_dict = chains_loading.type_to_loader_dict + self.type_dict = chain_type_to_cls_dict return self.type_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: try: - return build_template_from_function( - name, self.type_to_loader_dict, add_function=True - ) + return build_template_from_class(name, chain_type_to_cls_dict) except ValueError as exc: - raise ValueError("Chain not found") from exc + raise ValueError("Memory not found") from exc def to_list(self) -> List[str]: return [ - chain.__annotations__["return"].__name__ + chain.__name__ for chain in self.type_to_loader_dict.values() - if ( - chain.__annotations__["return"].__name__ in settings.chains - or settings.dev - ) + if chain.__name__ in settings.chains or settings.dev ] diff --git a/src/backend/langflow/interface/custom_lists.py b/src/backend/langflow/interface/custom_lists.py index 32d59619b..746c58325 100644 --- a/src/backend/langflow/interface/custom_lists.py +++ b/src/backend/langflow/interface/custom_lists.py @@ -1,108 +1,29 @@ from typing import Any ## LLM -from langchain import llms, requests +from langchain import ( + chains, + document_loaders, + embeddings, + llms, + memory, + requests, + vectorstores, +) from langchain.agents import agent_toolkits from langchain.chat_models import ChatOpenAI -## Memory -from langchain import memory - -## Document Loaders -from langchain.document_loaders import ( - AirbyteJSONLoader, - AZLyricsLoader, - CollegeConfidentialLoader, - CoNLLULoader, - CSVLoader, - DirectoryLoader, - EverNoteLoader, - FacebookChatLoader, - GCSDirectoryLoader, - GCSFileLoader, - GitbookLoader, - GoogleApiClient, - GoogleApiYoutubeLoader, - GoogleDriveLoader, - GutenbergLoader, - HNLoader, - IFixitLoader, - IMSDbLoader, - NotebookLoader, - NotionDirectoryLoader, - ObsidianLoader, - OnlinePDFLoader, - PagedPDFSplitter, - PDFMinerLoader, - PyMuPDFLoader, - PyPDFLoader, - ReadTheDocsLoader, - RoamLoader, - S3DirectoryLoader, - S3FileLoader, - SRTLoader, - TelegramChatLoader, - TextLoader, - UnstructuredEmailLoader, - UnstructuredFileIOLoader, - UnstructuredFileLoader, - UnstructuredHTMLLoader, - UnstructuredImageLoader, - UnstructuredMarkdownLoader, - UnstructuredPDFLoader, - # BSHTMLLoader, - UnstructuredPowerPointLoader, - UnstructuredURLLoader, - UnstructuredWordDocumentLoader, - WebBaseLoader, - YoutubeLoader, -) - -## Embeddings -from langchain.embeddings import ( - CohereEmbeddings, - FakeEmbeddings, - HuggingFaceEmbeddings, - HuggingFaceHubEmbeddings, - HuggingFaceInstructEmbeddings, - OpenAIEmbeddings, - SelfHostedEmbeddings, - SelfHostedHuggingFaceEmbeddings, - SelfHostedHuggingFaceInstructEmbeddings, - # SagemakerEndpointEmbeddings, - TensorflowHubEmbeddings, -) - -## Vector Stores -from langchain.vectorstores import ( - FAISS, - AtlasDB, - Chroma, - DeepLake, - ElasticVectorSearch, - Milvus, - OpenSearchVectorSearch, - Pinecone, - Qdrant, - VectorStore, - Weaviate, -) - -## Toolkits from langflow.interface.importing.utils import import_class ## LLM - llm_type_to_cls_dict = llms.type_to_cls_dict llm_type_to_cls_dict["openai-chat"] = ChatOpenAI # type: ignore - ## Chain -# from langchain.chains.loading import type_to_loader_dict -# from langchain.chains.conversation.base import ConversationChain - -# chain_type_to_cls_dict = type_to_loader_dict -# chain_type_to_cls_dict["conversation_chain"] = ConversationChain +chain_type_to_cls_dict: dict[str, Any] = { + chain_name: import_class(f"langchain.chains.{chain_name}") + for chain_name in chains.__all__ +} toolkit_type_to_loader_dict: dict[str, Any] = { toolkit_name: import_class(f"langchain.agents.agent_toolkits.{toolkit_name}") @@ -118,99 +39,33 @@ toolkit_type_to_cls_dict: dict[str, Any] = { if not toolkit_name.islower() } -## Memory - - +## Memories memory_type_to_cls_dict: dict[str, Any] = { memory_name: import_class(f"langchain.memory.{memory_name}") for memory_name in memory.__all__ } - +## Wrappers wrapper_type_to_cls_dict: dict[str, Any] = { wrapper.__name__: wrapper for wrapper in [requests.RequestsWrapper] } ## Embeddings - -embedding_type_to_cls_dict = { - "OpenAIEmbeddings": OpenAIEmbeddings, - "HuggingFaceEmbeddings": HuggingFaceEmbeddings, - "CohereEmbeddings": CohereEmbeddings, - "HuggingFaceHubEmbeddings": HuggingFaceHubEmbeddings, - "TensorflowHubEmbeddings": TensorflowHubEmbeddings, - # "SagemakerEndpointEmbeddings": SagemakerEndpointEmbeddings, - "HuggingFaceInstructEmbeddings": HuggingFaceInstructEmbeddings, - "SelfHostedEmbeddings": SelfHostedEmbeddings, - "SelfHostedHuggingFaceEmbeddings": SelfHostedHuggingFaceEmbeddings, - "SelfHostedHuggingFaceInstructEmbeddings": SelfHostedHuggingFaceInstructEmbeddings, - "FakeEmbeddings": FakeEmbeddings, +embedding_type_to_cls_dict: dict[str, Any] = { + embedding_name: import_class(f"langchain.embeddings.{embedding_name}") + for embedding_name in embeddings.__all__ } ## Vector Stores - -vectorstores_type_to_cls_dict = { - "ElasticVectorSearch": ElasticVectorSearch, - "FAISS": FAISS, - "VectorStore": VectorStore, - "Pinecone": Pinecone, - "Weaviate": Weaviate, - "Qdrant": Qdrant, - "Milvus": Milvus, - "Chroma": Chroma, - "OpenSearchVectorSearch": OpenSearchVectorSearch, - "AtlasDB": AtlasDB, - "DeepLake": DeepLake, +vectorstores_type_to_cls_dict: dict[str, Any] = { + vectorstore_name: import_class(f"langchain.vectorstores.{vectorstore_name}") + for vectorstore_name in vectorstores.__all__ } ## Document Loaders - -documentloaders_type_to_cls_dict = { - "UnstructuredFileLoader": UnstructuredFileLoader, - "UnstructuredFileIOLoader": UnstructuredFileIOLoader, - "UnstructuredURLLoader": UnstructuredURLLoader, - "DirectoryLoader": DirectoryLoader, - "NotionDirectoryLoader": NotionDirectoryLoader, - "ReadTheDocsLoader": ReadTheDocsLoader, - "GoogleDriveLoader": GoogleDriveLoader, - "UnstructuredHTMLLoader": UnstructuredHTMLLoader, - # "BSHTMLLoader": BSHTMLLoader, - "UnstructuredPowerPointLoader": UnstructuredPowerPointLoader, - "UnstructuredWordDocumentLoader": UnstructuredWordDocumentLoader, - "UnstructuredPDFLoader": UnstructuredPDFLoader, - "UnstructuredImageLoader": UnstructuredImageLoader, - "ObsidianLoader": ObsidianLoader, - "UnstructuredEmailLoader": UnstructuredEmailLoader, - "UnstructuredMarkdownLoader": UnstructuredMarkdownLoader, - "RoamLoader": RoamLoader, - "YoutubeLoader": YoutubeLoader, - "S3FileLoader": S3FileLoader, - "TextLoader": TextLoader, - "HNLoader": HNLoader, - "GitbookLoader": GitbookLoader, - "S3DirectoryLoader": S3DirectoryLoader, - "GCSFileLoader": GCSFileLoader, - "GCSDirectoryLoader": GCSDirectoryLoader, - "WebBaseLoader": WebBaseLoader, - "IMSDbLoader": IMSDbLoader, - "AZLyricsLoader": AZLyricsLoader, - "CollegeConfidentialLoader": CollegeConfidentialLoader, - "IFixitLoader": IFixitLoader, - "GutenbergLoader": GutenbergLoader, - "PagedPDFSplitter": PagedPDFSplitter, - "PyPDFLoader": PyPDFLoader, - "EverNoteLoader": EverNoteLoader, - "AirbyteJSONLoader": AirbyteJSONLoader, - "OnlinePDFLoader": OnlinePDFLoader, - "PDFMinerLoader": PDFMinerLoader, - "PyMuPDFLoader": PyMuPDFLoader, - "TelegramChatLoader": TelegramChatLoader, - "SRTLoader": SRTLoader, - "FacebookChatLoader": FacebookChatLoader, - "NotebookLoader": NotebookLoader, - "CoNLLULoader": CoNLLULoader, - "GoogleApiYoutubeLoader": GoogleApiYoutubeLoader, - "GoogleApiClient": GoogleApiClient, - "CSVLoader": CSVLoader, - # "BlackboardLoader", +documentloaders_type_to_cls_dict: dict[str, Any] = { + documentloader_name: import_class( + f"langchain.document_loaders.{documentloader_name}" + ) + for documentloader_name in document_loaders.__all__ } diff --git a/src/backend/langflow/interface/documentLoaders/base.py b/src/backend/langflow/interface/documentLoaders/base.py index 494fabfd3..844820d7b 100644 --- a/src/backend/langflow/interface/documentLoaders/base.py +++ b/src/backend/langflow/interface/documentLoaders/base.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Optional from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import documentloaders_type_to_cls_dict @@ -13,7 +13,7 @@ class DocumentLoaderCreator(LangChainTypeCreator): def type_to_loader_dict(self) -> Dict: return documentloaders_type_to_cls_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: """Get the signature of a document loader.""" try: return build_template_from_class(name, documentloaders_type_to_cls_dict) diff --git a/src/backend/langflow/interface/embeddings/base.py b/src/backend/langflow/interface/embeddings/base.py index de1921b5e..25038a8a5 100644 --- a/src/backend/langflow/interface/embeddings/base.py +++ b/src/backend/langflow/interface/embeddings/base.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Optional from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import embedding_type_to_cls_dict @@ -13,7 +13,7 @@ class EmbeddingCreator(LangChainTypeCreator): def type_to_loader_dict(self) -> Dict: return embedding_type_to_cls_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: """Get the signature of an embedding.""" try: return build_template_from_class(name, embedding_type_to_cls_dict) diff --git a/src/backend/langflow/interface/importing/utils.py b/src/backend/langflow/interface/importing/utils.py index 96dadc438..0ada410e4 100644 --- a/src/backend/langflow/interface/importing/utils.py +++ b/src/backend/langflow/interface/importing/utils.py @@ -6,9 +6,10 @@ from typing import Any from langchain import PromptTemplate from langchain.agents import Agent from langchain.chains.base import Chain +from langchain.chat_models.base import BaseChatModel from langchain.llms.base import BaseLLM from langchain.tools import BaseTool -from langchain.chat_models.base import BaseChatModel + from langflow.interface.tools.util import get_tool_by_name diff --git a/src/backend/langflow/interface/llms/base.py b/src/backend/langflow/interface/llms/base.py index 688845301..85f9035db 100644 --- a/src/backend/langflow/interface/llms/base.py +++ b/src/backend/langflow/interface/llms/base.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Optional from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import llm_type_to_cls_dict @@ -15,7 +15,7 @@ class LLMCreator(LangChainTypeCreator): self.type_dict = llm_type_to_cls_dict return self.type_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: """Get the signature of an llm.""" try: return build_template_from_class(name, llm_type_to_cls_dict) diff --git a/src/backend/langflow/interface/loading.py b/src/backend/langflow/interface/loading.py index aca826502..9a8d28d05 100644 --- a/src/backend/langflow/interface/loading.py +++ b/src/backend/langflow/interface/loading.py @@ -22,7 +22,7 @@ from langflow.interface.agents.custom import CUSTOM_AGENTS from langflow.interface.importing.utils import import_by_type from langflow.interface.toolkits.base import toolkits_creator from langflow.interface.types import get_type_list -from langflow.utils import payload, util, validate +from langflow.utils import util, validate def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any: diff --git a/src/backend/langflow/interface/memories/base.py b/src/backend/langflow/interface/memories/base.py index 99af98a1b..1bb4b054b 100644 --- a/src/backend/langflow/interface/memories/base.py +++ b/src/backend/langflow/interface/memories/base.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Optional from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import memory_type_to_cls_dict @@ -15,7 +15,7 @@ class MemoryCreator(LangChainTypeCreator): self.type_dict = memory_type_to_cls_dict return self.type_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: """Get the signature of a memory.""" try: return build_template_from_class(name, memory_type_to_cls_dict) diff --git a/src/backend/langflow/interface/prompts/base.py b/src/backend/langflow/interface/prompts/base.py index b24522d5c..a7de7a611 100644 --- a/src/backend/langflow/interface/prompts/base.py +++ b/src/backend/langflow/interface/prompts/base.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Optional from langchain.prompts import loading from langchain import prompts @@ -28,7 +28,7 @@ class PromptCreator(LangChainTypeCreator): } return self.type_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: try: if name in get_custom_nodes(self.type_name).keys(): return get_custom_nodes(self.type_name)[name] diff --git a/src/backend/langflow/interface/prompts/custom.py b/src/backend/langflow/interface/prompts/custom.py index a33e20e1c..d1bb98c62 100644 --- a/src/backend/langflow/interface/prompts/custom.py +++ b/src/backend/langflow/interface/prompts/custom.py @@ -1,11 +1,11 @@ from typing import List, Optional from langchain.prompts import PromptTemplate +from pydantic import root_validator + from langflow.graph.utils import extract_input_variables_from_prompt from langflow.template.base import Template, TemplateField from langflow.template.nodes import PromptTemplateNode -from pydantic import root_validator - CHARACTER_PROMPT = """I want you to act like {character} from {series}. I want you to respond and answer like {character}. do not write any explanations. only answer like {character}. diff --git a/src/backend/langflow/interface/run.py b/src/backend/langflow/interface/run.py index 797e04091..0da273722 100644 --- a/src/backend/langflow/interface/run.py +++ b/src/backend/langflow/interface/run.py @@ -1,12 +1,10 @@ import contextlib import io -import re -from typing import Any, Dict, List, Tuple +from typing import Any, Dict from langflow.cache.utils import compute_hash, load_cache, save_cache from langflow.graph.graph import Graph from langflow.interface import loading -from langflow.utils import payload from langflow.utils.logger import logger @@ -65,12 +63,7 @@ def process_graph(data_graph: Dict[str, Any]): logger.debug("Saving langchain object to cache") save_cache(computed_hash, langchain_object, is_first_message) logger.debug("Saved langchain object to cache") - return { - "result": str(result), - "thought": re.sub( - r"\x1b\[([0-9,A-Z]{1,2}(;[0-9,A-Z]{1,2})?)?[m|K]", "", thought - ).strip(), - } + return {"result": str(result), "thought": thought.strip()} def get_result_and_thought_using_graph(loaded_langchain, message: str): diff --git a/src/backend/langflow/interface/toolkits/base.py b/src/backend/langflow/interface/toolkits/base.py index c78a9c051..5346ae4b5 100644 --- a/src/backend/langflow/interface/toolkits/base.py +++ b/src/backend/langflow/interface/toolkits/base.py @@ -1,4 +1,4 @@ -from typing import Callable, Dict, List +from typing import Callable, Dict, List, Optional from langchain.agents import agent_toolkits @@ -39,7 +39,7 @@ class ToolkitCreator(LangChainTypeCreator): return self.type_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: try: return build_template_from_class(name, self.type_to_loader_dict) except ValueError as exc: diff --git a/src/backend/langflow/interface/tools/base.py b/src/backend/langflow/interface/tools/base.py index 979e7b8fb..4989a7d4b 100644 --- a/src/backend/langflow/interface/tools/base.py +++ b/src/backend/langflow/interface/tools/base.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Optional from langchain.agents.load_tools import ( _BASE_TOOLS, @@ -10,7 +10,6 @@ from langchain.agents.load_tools import ( from langflow.custom import customs from langflow.interface.base import LangChainTypeCreator from langflow.interface.tools.constants import ( - ALL_TOOLS_NAMES, CUSTOM_TOOLS, FILE_TOOLS, ) @@ -60,7 +59,7 @@ TOOL_INPUTS = { class ToolCreator(LangChainTypeCreator): type_name: str = "tools" - tools_dict: Dict | None = None + tools_dict: Optional[Dict] = None @property def type_to_loader_dict(self) -> Dict: @@ -68,7 +67,7 @@ class ToolCreator(LangChainTypeCreator): self.tools_dict = get_tools_dict() return self.tools_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: """Get the signature of a tool.""" base_classes = ["Tool"] @@ -133,8 +132,8 @@ class ToolCreator(LangChainTypeCreator): tools = [] - for tool in ALL_TOOLS_NAMES: - tool_params = get_tool_params(get_tool_by_name(tool)) + for tool, fcn in get_tools_dict().items(): + tool_params = get_tool_params(fcn) if tool_params and not tool_params.get("name"): tool_params["name"] = tool @@ -145,9 +144,7 @@ class ToolCreator(LangChainTypeCreator): ): tools.append(tool_params["name"]) - # Add Tool - custom_tools = customs.get_custom_nodes("tools") - return tools + list(custom_tools.keys()) + return tools tool_creator = ToolCreator() diff --git a/src/backend/langflow/interface/tools/constants.py b/src/backend/langflow/interface/tools/constants.py index caab662a9..89a6bad01 100644 --- a/src/backend/langflow/interface/tools/constants.py +++ b/src/backend/langflow/interface/tools/constants.py @@ -1,11 +1,21 @@ from langchain.agents import Tool -from langchain.agents.load_tools import get_all_tool_names +from langchain.agents.load_tools import ( + _BASE_TOOLS, + _EXTRA_LLM_TOOLS, + _EXTRA_OPTIONAL_TOOLS, + _LLM_TOOLS, +) from langchain.tools.json.tool import JsonSpec from langflow.interface.custom.types import PythonFunction FILE_TOOLS = {"JsonSpec": JsonSpec} CUSTOM_TOOLS = {"Tool": Tool, "PythonFunction": PythonFunction} -ALL_TOOLS_NAMES = set( - get_all_tool_names() + list(CUSTOM_TOOLS.keys()) + list(FILE_TOOLS.keys()) -) +ALL_TOOLS_NAMES = { + **_BASE_TOOLS, + **_LLM_TOOLS, # type: ignore + **{k: v[0] for k, v in _EXTRA_LLM_TOOLS.items()}, # type: ignore + **{k: v[0] for k, v in _EXTRA_OPTIONAL_TOOLS.items()}, + **CUSTOM_TOOLS, + **FILE_TOOLS, # type: ignore +} diff --git a/src/backend/langflow/interface/tools/util.py b/src/backend/langflow/interface/tools/util.py index 42bc64797..8f8673b6b 100644 --- a/src/backend/langflow/interface/tools/util.py +++ b/src/backend/langflow/interface/tools/util.py @@ -2,28 +2,22 @@ import ast import inspect from typing import Dict, Union -from langchain.agents.load_tools import ( - _BASE_TOOLS, - _EXTRA_LLM_TOOLS, - _EXTRA_OPTIONAL_TOOLS, - _LLM_TOOLS, -) from langchain.agents.tools import Tool -from langflow.interface.tools.constants import CUSTOM_TOOLS, FILE_TOOLS +from langflow.interface.tools.constants import ALL_TOOLS_NAMES def get_tools_dict(): """Get the tools dictionary.""" - return { - **_BASE_TOOLS, - **_LLM_TOOLS, - **{k: v[0] for k, v in _EXTRA_LLM_TOOLS.items()}, - **{k: v[0] for k, v in _EXTRA_OPTIONAL_TOOLS.items()}, - **CUSTOM_TOOLS, - **FILE_TOOLS, - } + all_tools = {} + + for tool, fcn in ALL_TOOLS_NAMES.items(): + if tool_params := get_tool_params(fcn): + tool_name = tool_params.get("name") or str(tool) + all_tools[tool_name] = fcn + + return all_tools def get_tool_by_name(name: str): diff --git a/src/backend/langflow/interface/vectorStore/base.py b/src/backend/langflow/interface/vectorStore/base.py index 01c222e07..6c2baa86d 100644 --- a/src/backend/langflow/interface/vectorStore/base.py +++ b/src/backend/langflow/interface/vectorStore/base.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Optional from langflow.interface.base import LangChainTypeCreator from langflow.interface.custom_lists import vectorstores_type_to_cls_dict @@ -13,7 +13,7 @@ class VectorstoreCreator(LangChainTypeCreator): def type_to_loader_dict(self) -> Dict: return vectorstores_type_to_cls_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: """Get the signature of an embedding.""" try: return build_template_from_class(name, vectorstores_type_to_cls_dict) diff --git a/src/backend/langflow/interface/wrappers/base.py b/src/backend/langflow/interface/wrappers/base.py index abfa559a1..8c5978013 100644 --- a/src/backend/langflow/interface/wrappers/base.py +++ b/src/backend/langflow/interface/wrappers/base.py @@ -1,4 +1,4 @@ -from typing import Dict, List +from typing import Dict, List, Optional from langchain import requests @@ -17,7 +17,7 @@ class WrapperCreator(LangChainTypeCreator): } return self.type_dict - def get_signature(self, name: str) -> Dict | None: + def get_signature(self, name: str) -> Optional[Dict]: try: return build_template_from_class(name, self.type_to_loader_dict) except ValueError as exc: diff --git a/src/backend/langflow/template/nodes.py b/src/backend/langflow/template/nodes.py index b58775a5c..b28a38842 100644 --- a/src/backend/langflow/template/nodes.py +++ b/src/backend/langflow/template/nodes.py @@ -1,10 +1,11 @@ from typing import Optional + +from langchain.agents import loading from langchain.agents.mrkl import prompt from langflow.template.base import FrontendNode, Template, TemplateField from langflow.template.constants import DEFAULT_PROMPT, HUMAN_PROMPT, SYSTEM_PROMPT from langflow.utils.constants import DEFAULT_PYTHON_FUNCTION -from langchain.agents import loading class BasePromptFrontendNode(FrontendNode): @@ -176,6 +177,7 @@ class InitializeAgentNode(FrontendNode): show=True, multiline=False, options=list(loading.AGENT_TO_CLASS.keys()), + value=list(loading.AGENT_TO_CLASS.keys())[0], name="agent", ), TemplateField( diff --git a/src/backend/langflow/utils/logger.py b/src/backend/langflow/utils/logger.py index 2d04a7706..b70a451d4 100644 --- a/src/backend/langflow/utils/logger.py +++ b/src/backend/langflow/utils/logger.py @@ -1,5 +1,6 @@ import logging from pathlib import Path + from rich.logging import RichHandler logger = logging.getLogger("langflow") diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 94c58fc36..df61239c5 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -22,6 +22,7 @@ "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "ace-builds": "^1.16.0", + "ansi-to-html": "^0.7.2", "axios": "^1.3.2", "lodash": "^4.17.21", "react": "^18.2.0", @@ -5315,6 +5316,20 @@ "node": ">=4" } }, + "node_modules/ansi-to-html": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.7.2.tgz", + "integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==", + "dependencies": { + "entities": "^2.2.0" + }, + "bin": { + "ansi-to-html": "bin/ansi-to-html" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", diff --git a/src/frontend/package.json b/src/frontend/package.json index 1af847684..41b9bcdfe 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -17,6 +17,7 @@ "@types/react": "^18.0.27", "@types/react-dom": "^18.0.10", "ace-builds": "^1.16.0", + "ansi-to-html": "^0.7.2", "axios": "^1.3.2", "lodash": "^4.17.21", "react": "^18.2.0", @@ -58,5 +59,5 @@ "last 1 safari version" ] }, - "proxy": "http://backend:7860" -} \ No newline at end of file + "proxy": "http://localhost:7860" +} diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 0f9feeb7e..3d7198857 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -70,7 +70,12 @@ export default function GenericNode({ .map((t: string, idx) => (
{idx === 0 ? ( -
+
!key.startsWith('_') && data.node.template[key].show).length === 0?"hidden":"" + )} + > Inputs
) : ( diff --git a/src/frontend/src/components/chatComponent/chatMessage/index.tsx b/src/frontend/src/components/chatComponent/chatMessage/index.tsx index e26a8c05d..aa2a0d080 100644 --- a/src/frontend/src/components/chatComponent/chatMessage/index.tsx +++ b/src/frontend/src/components/chatComponent/chatMessage/index.tsx @@ -2,6 +2,8 @@ import { ChatBubbleLeftEllipsisIcon, ChatBubbleOvalLeftEllipsisIcon, PlusSmallIc import { useState } from "react"; import { ChatMessageType } from "../../../types/chat"; import { nodeColors } from "../../../utils"; +var Convert = require('ansi-to-html'); +var convert = new Convert({newline:true}); export default function ChatMessage({ chat }: { chat: ChatMessageType }) { const [hidden, setHidden] = useState(true); @@ -27,7 +29,7 @@ export default function ChatMessage({ chat }: { chat: ChatMessageType }) { style={{ backgroundColor: nodeColors["thought"] }} className=" text-start inline-block w-full pb-3 pt-3 px-5 cursor-pointer" dangerouslySetInnerHTML={{ - __html: chat.thought.replace(/\n/g, "
"), + __html: convert.toHtml(chat.thought) }} >
)} diff --git a/src/frontend/src/components/chatComponent/index.tsx b/src/frontend/src/components/chatComponent/index.tsx index 561282291..99aff511e 100644 --- a/src/frontend/src/components/chatComponent/index.tsx +++ b/src/frontend/src/components/chatComponent/index.tsx @@ -13,6 +13,7 @@ import { TabsContext } from "../../contexts/tabsContext"; import { ChatType } from "../../types/chat"; import ChatMessage from "./chatMessage"; + const _ = require("lodash"); export default function Chat({ flow, reactFlowInstance }: ChatType) { @@ -99,7 +100,7 @@ export default function Chat({ flow, reactFlowInstance }: ChatType) { sendAll({ ...reactFlowInstance.toObject(), message, chatHistory,name:flow.name,description:flow.description}) .then((r) => { - addChatHistory(r.data.result, false, r.data.thought); + addChatHistory(r.data.result, false,r.data.thought); setLockChat(false); }) .catch((error) => { diff --git a/src/frontend/src/components/dropdownComponent/index.tsx b/src/frontend/src/components/dropdownComponent/index.tsx index dd44080ba..1bd163310 100644 --- a/src/frontend/src/components/dropdownComponent/index.tsx +++ b/src/frontend/src/components/dropdownComponent/index.tsx @@ -5,7 +5,7 @@ import { DropDownComponentType } from "../../types/components"; import { classNames } from "../../utils"; export default function Dropdown({value, options, onSelect}:DropDownComponentType) { - let [internalValue,setInternalValue] = useState(value??"Choose an option") + let [internalValue,setInternalValue] = useState(value===""||!value?"Choose an option":value) return ( <> { diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx index c1da32946..aa48f6ce1 100644 --- a/src/frontend/src/modals/exportModal/index.tsx +++ b/src/frontend/src/modals/exportModal/index.tsx @@ -26,6 +26,7 @@ export default function ExportModal() { } } const [checked,setChecked] = useState(true) + const [name,setName] = useState(flows[tabIndex].name) return (