Adds Info tooltips and links to docs, fixes component duplication (#554)

This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-06-27 16:32:44 -03:00 • committed by GitHub
commit a795632ed6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 670 additions and 395 deletions

View file

@ -13,8 +13,9 @@
<img alt="Github License" src="https://img.shields.io/github/license/logspace-ai/langflow" /> <img alt="Github License" src="https://img.shields.io/github/license/logspace-ai/langflow" />
</p> </p>
<p> <p>
<a href="https://discord.gg/FUhJnnJ9"><img alt="Discord Server" src="https://dcbadge.vercel.app/api/server/FUhJnnJ9?compact=true&style=flat"/></a> <a href="https://discord.gg/EqksyE2EX9"><img alt="Discord Server" src="https://dcbadge.vercel.app/api/server/EqksyE2EX9?compact=true&style=flat"/></a>
<a href="https://huggingface.co/spaces/Logspace/LangFlow"><img src="https://huggingface.co/datasets/huggingface/badges/raw/main/open-in-hf-spaces-sm.svg" alt="HuggingFace Spaces"></a> <a href="https://huggingface.co/spaces/Logspace/LangFlow"><img src="https://huggingface.co/datasets/huggingface/badges/raw/main/open-in-hf-spaces-sm.svg" alt="HuggingFace Spaces"></a>
</p> </p>

12
poetry.lock generated
View file

@ -1565,14 +1565,14 @@ uritemplate = ">=3.0.1,<5"
[[package]] [[package]]
name = "google-auth" name = "google-auth"
version = "2.20.0" version = "2.21.0"
description = "Google Authentication Library" description = "Google Authentication Library"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
{file = "google-auth-2.20.0.tar.gz", hash = "sha256:030af34138909ccde0fbce611afc178f1d65d32fbff281f25738b1fe1c6f3eaa"}, {file = "google-auth-2.21.0.tar.gz", hash = "sha256:b28e8048e57727e7cf0e5bd8e7276b212aef476654a09511354aa82753b45c66"},
{file = "google_auth-2.20.0-py2.py3-none-any.whl", hash = "sha256:23b7b0950fcda519bfb6692bf0d5289d2ea49fc143717cc7188458ec620e63fa"}, {file = "google_auth-2.21.0-py2.py3-none-any.whl", hash = "sha256:da3f18d074fa0f5a7061d99b9af8cee3aa6189c987af7c1b07d94566b6b11268"},
] ]
[package.dependencies] [package.dependencies]
@ -4977,14 +4977,14 @@ files = [
[[package]] [[package]]
name = "pywin32-ctypes" name = "pywin32-ctypes"
version = "0.2.1" version = "0.2.2"
description = "A (partial) reimplementation of pywin32 using ctypes/cffi" description = "A (partial) reimplementation of pywin32 using ctypes/cffi"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
{file = "pywin32-ctypes-0.2.1.tar.gz", hash = "sha256:934a2def1e5cbc472b2b6bf80680c0f03cd87df65dfd58bfd1846969de095b03"}, {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"},
{file = "pywin32_ctypes-0.2.1-py3-none-any.whl", hash = "sha256:b9a53ef754c894a525469933ab2a447c74ec1ea6b9d2ef446f40ec50d3dcec9f"}, {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"},
] ]
[[package]] [[package]]

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "langflow" name = "langflow"
version = "0.2.2" version = "0.2.3"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Logspace <contact@logspace.ai>"] authors = ["Logspace <contact@logspace.ai>"]
maintainers = [ maintainers = [

View file

@ -1,6 +1,5 @@
import sys import sys
import time import time
from fastapi import FastAPI
import httpx import httpx
from multiprocess import Process, cpu_count # type: ignore from multiprocess import Process, cpu_count # type: ignore
import platform import platform
@ -11,9 +10,7 @@ from rich.panel import Panel
from rich import box from rich import box
from rich import print as rprint from rich import print as rprint
import typer import typer
from fastapi.staticfiles import StaticFiles from langflow.main import setup_app
from fastapi.responses import FileResponse
from langflow.main import create_app
from langflow.settings import settings from langflow.settings import settings
from langflow.utils.logger import configure, logger from langflow.utils.logger import configure, logger
import webbrowser import webbrowser
@ -144,15 +141,9 @@ def serve(
remove_api_keys=remove_api_keys, remove_api_keys=remove_api_keys,
cache=cache, cache=cache,
) )
# get the directory of the current file # create path object if path is provided
if not path: static_files_dir: Optional[Path] = Path(path) if path else None
frontend_path = Path(__file__).parent app = setup_app(static_files_dir=static_files_dir)
static_files_dir = frontend_path / "frontend"
else:
static_files_dir = Path(path)
app = create_app()
setup_static_files(app, static_files_dir)
# check if port is being used # check if port is being used
if is_port_in_use(port, host): if is_port_in_use(port, host):
port = get_free_port(port) port = get_free_port(port)
@ -200,29 +191,6 @@ def run_on_windows(host, port, log_level, options, app):
run_langflow(host, port, log_level, options, app) run_langflow(host, port, log_level, options, app)
def setup_static_files(app: FastAPI, static_files_dir: Path):
"""
Setup the static files directory.
Args:
app (FastAPI): FastAPI app.
path (str): Path to the static files directory.
"""
app.mount(
"/",
StaticFiles(directory=static_files_dir, html=True),
name="static",
)
@app.exception_handler(404)
async def custom_404_handler(request, __):
path = static_files_dir / "index.html"
if not path.exists():
raise RuntimeError(f"File at path {path} does not exist.")
return FileResponse(path)
def is_port_in_use(port, host="localhost"): def is_port_in_use(port, host="localhost"):
""" """
Check if a port is in use. Check if a port is in use.

View file

@ -0,0 +1,2 @@
class ChatConfig:
streaming: bool = True

View file

@ -1,141 +1,247 @@
---
agents: agents:
- ZeroShotAgent ZeroShotAgent:
- JsonAgent documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
- CSVAgent JsonAgent:
- AgentInitializer documentation: "https://python.langchain.com/docs/modules/agents/toolkits/openapi"
- VectorStoreAgent CSVAgent:
- VectorStoreRouterAgent documentation: "https://python.langchain.com/docs/modules/agents/toolkits/csv"
- SQLAgent AgentInitializer:
documentation: "https://python.langchain.com/docs/modules/agents/agent_types/"
VectorStoreAgent:
documentation: ""
VectorStoreRouterAgent:
documentation: ""
SQLAgent:
documentation: ""
chains: chains:
- LLMChain LLMChain:
- LLMMathChain documentation: "https://python.langchain.com/docs/modules/chains/foundational/llm_chain"
- LLMCheckerChain LLMMathChain:
- ConversationChain documentation: "https://python.langchain.com/docs/modules/chains/additional/llm_math"
- SeriesCharacterChain LLMCheckerChain:
- MidJourneyPromptChain documentation: "https://python.langchain.com/docs/modules/chains/additional/llm_checker"
- TimeTravelGuideChain ConversationChain:
- SQLDatabaseChain documentation: ""
- RetrievalQA SeriesCharacterChain:
- RetrievalQAWithSourcesChain documentation: ""
- ConversationalRetrievalChain MidJourneyPromptChain:
- CombineDocsChain documentation: ""
TimeTravelGuideChain:
documentation: ""
SQLDatabaseChain:
documentation: ""
RetrievalQA:
documentation: "https://python.langchain.com/docs/modules/chains/popular/vector_db_qa"
RetrievalQAWithSourcesChain:
documentation: ""
ConversationalRetrievalChain:
documentation: "https://python.langchain.com/docs/modules/chains/popular/chat_vector_db"
CombineDocsChain:
documentation: ""
documentloaders: documentloaders:
- AirbyteJSONLoader AirbyteJSONLoader:
- CoNLLULoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/airbyte_json"
- CSVLoader CoNLLULoader:
- UnstructuredEmailLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/conll-u"
- EverNoteLoader CSVLoader:
- FacebookChatLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/csv"
- GutenbergLoader UnstructuredEmailLoader:
- BSHTMLLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/email"
- UnstructuredHTMLLoader EverNoteLoader:
# - UnstructuredImageLoader # Issue with Python 3.11 (https://github.com/Unstructured-IO/unstructured-inference/issues/83) documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/evernote"
- UnstructuredMarkdownLoader FacebookChatLoader:
- PyPDFLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/facebook_chat"
- UnstructuredPowerPointLoader GutenbergLoader:
- SRTLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/gutenberg"
- TelegramChatLoader BSHTMLLoader:
- TextLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/html"
- UnstructuredWordDocumentLoader UnstructuredHTMLLoader:
- WebBaseLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/html"
- AZLyricsLoader UnstructuredMarkdownLoader:
- CollegeConfidentialLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/markdown"
- HNLoader PyPDFLoader:
- IFixitLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/pdf"
- IMSDbLoader UnstructuredPowerPointLoader:
- GitbookLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/microsoft_powerpoint"
- ReadTheDocsLoader SRTLoader:
- SlackDirectoryLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/subtitle"
- NotionDirectoryLoader TelegramChatLoader:
- DirectoryLoader documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/telegram"
- GitLoader TextLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/"
UnstructuredWordDocumentLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/microsoft_word"
WebBaseLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/web_base"
AZLyricsLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/azlyrics"
CollegeConfidentialLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/college_confidential"
HNLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/hacker_news"
IFixitLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/ifixit"
IMSDbLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/imsdb"
GitbookLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/gitbook"
ReadTheDocsLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/readthedocs_documentation"
SlackDirectoryLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/slack"
NotionDirectoryLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/notion"
DirectoryLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/how_to/file_directory"
GitLoader:
documentation: "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/git"
embeddings: embeddings:
- OpenAIEmbeddings OpenAIEmbeddings:
- HuggingFaceEmbeddings documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/openai"
- CohereEmbeddings HuggingFaceEmbeddings:
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/sentence_transformers"
CohereEmbeddings:
documentation: "https://python.langchain.com/docs/modules/data_connection/text_embedding/integrations/cohere"
llms: llms:
- OpenAI OpenAI:
# - AzureOpenAI documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/openai"
# - AzureChatOpenAI ChatOpenAI:
- ChatOpenAI documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/openai"
- LlamaCpp LlamaCpp:
- CTransformers documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/llamacpp"
- Cohere CTransformers:
- Anthropic documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/ctransformers"
- ChatAnthropic Cohere:
- HuggingFaceHub documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/cohere"
Anthropic:
documentation: ""
ChatAnthropic:
documentation: "https://python.langchain.com/docs/modules/model_io/models/chat/integrations/anthropic"
HuggingFaceHub:
documentation: "https://python.langchain.com/docs/modules/model_io/models/llms/integrations/huggingface_hub"
memories: memories:
- ConversationBufferMemory ConversationBufferMemory:
- ConversationSummaryMemory documentation: "https://python.langchain.com/docs/modules/memory/how_to/summary"
- ConversationKGMemory ConversationSummaryMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/summary"
ConversationKGMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/kg"
ConversationBufferWindowMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/buffer_window"
VectorStoreRetrieverMemory:
documentation: "https://python.langchain.com/docs/modules/memory/how_to/vectorstore_retriever_memory"
prompts: prompts:
- PromptTemplate PromptTemplate:
- FewShotPromptTemplate documentation: "https://python.langchain.com/docs/modules/model_io/prompts/prompt_templates/"
- ZeroShotPrompt ZeroShotPrompt:
documentation: "https://python.langchain.com/docs/modules/agents/how_to/custom_mrkl_agent"
textsplitters: textsplitters:
- CharacterTextSplitter CharacterTextSplitter:
- RecursiveCharacterTextSplitter documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/character_text_splitter"
# - LatexTextSplitter RecursiveCharacterTextSplitter:
# - PythonCodeTextSplitter documentation: "https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/recursive_text_splitter"
toolkits: toolkits:
- OpenAPIToolkit OpenAPIToolkit:
- JsonToolkit documentation: ""
- VectorStoreInfo JsonToolkit:
- VectorStoreRouterToolkit documentation: ""
- VectorStoreToolkit VectorStoreInfo:
documentation: ""
VectorStoreRouterToolkit:
documentation: ""
VectorStoreToolkit:
documentation: ""
tools: tools:
- Search Search:
- PAL-MATH documentation: ""
- Calculator PAL-MATH:
- Serper Search documentation: ""
- Tool Calculator:
- PythonFunctionTool documentation: ""
- PythonFunction Serper Search:
- JsonSpec documentation: ""
- News API Tool:
- TMDB API documentation: ""
- Podcast API PythonFunctionTool:
- QuerySQLDataBaseTool documentation: ""
- InfoSQLDatabaseTool PythonFunction:
- ListSQLDatabaseTool documentation: ""
# - QueryCheckerTool JsonSpec:
- BingSearchRun documentation: ""
- GoogleSearchRun News API:
- GoogleSearchResults documentation: ""
- GoogleSerperRun TMDB API:
- JsonListKeysTool documentation: ""
- JsonGetValueTool Podcast API:
- PythonREPLTool documentation: ""
- PythonAstREPLTool QuerySQLDataBaseTool:
- RequestsGetTool documentation: ""
- RequestsPostTool InfoSQLDatabaseTool:
- RequestsPatchTool documentation: ""
- RequestsPutTool ListSQLDatabaseTool:
- RequestsDeleteTool documentation: ""
- WikipediaQueryRun BingSearchRun:
- WolframAlphaQueryRun documentation: ""
GoogleSearchRun:
documentation: ""
GoogleSearchResults:
documentation: ""
GoogleSerperRun:
documentation: ""
JsonListKeysTool:
documentation: ""
JsonGetValueTool:
documentation: ""
PythonREPLTool:
documentation: ""
PythonAstREPLTool:
documentation: ""
RequestsGetTool:
documentation: ""
RequestsPostTool:
documentation: ""
RequestsPatchTool:
documentation: ""
RequestsPutTool:
documentation: ""
RequestsDeleteTool:
documentation: ""
WikipediaQueryRun:
documentation: ""
WolframAlphaQueryRun:
documentation: ""
utilities: utilities:
- BingSearchAPIWrapper BingSearchAPIWrapper:
- GoogleSearchAPIWrapper documentation: ""
- GoogleSerperAPIWrapper GoogleSearchAPIWrapper:
- SearxResults documentation: ""
- SearxSearchWrapper GoogleSerperAPIWrapper:
- SerpAPIWrapper documentation: ""
- WikipediaAPIWrapper SearxResults:
- WolframAlphaAPIWrapper documentation: ""
# - ZapierNLAWrapper SearxSearchWrapper:
- SQLDatabase documentation: ""
SerpAPIWrapper:
documentation: ""
WikipediaAPIWrapper:
documentation: ""
WolframAlphaAPIWrapper:
documentation: ""
vectorstores: vectorstores:
- Chroma Chroma:
- Qdrant documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/chroma"
- Weaviate Qdrant:
- FAISS documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/qdrant"
- Pinecone Weaviate:
- SupabaseVectorStore documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/weaviate"
- MongoDBAtlasVectorSearch FAISS:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/faiss"
Pinecone:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/pinecone"
SupabaseVectorStore:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/supabase"
MongoDBAtlasVectorSearch:
documentation: "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/mongodb_atlas_vector_search"
wrappers: wrappers:
- RequestsWrapper RequestsWrapper:
# - ChatPromptTemplate documentation: ""
# - SystemMessagePromptTemplate
# - HumanMessagePromptTemplate

View file

@ -8,6 +8,7 @@ from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.template.base import Template from langflow.template.template.base import Template
from langflow.utils.logger import logger from langflow.utils.logger import logger
from langflow.settings import settings
# Assuming necessary imports for Field, Template, and FrontendNode classes # Assuming necessary imports for Field, Template, and FrontendNode classes
@ -15,12 +16,29 @@ from langflow.utils.logger import logger
class LangChainTypeCreator(BaseModel, ABC): class LangChainTypeCreator(BaseModel, ABC):
type_name: str type_name: str
type_dict: Optional[Dict] = None type_dict: Optional[Dict] = None
name_docs_dict: Optional[Dict[str, str]] = None
@property @property
def frontend_node_class(self) -> Type[FrontendNode]: def frontend_node_class(self) -> Type[FrontendNode]:
"""The class type of the FrontendNode created in frontend_node.""" """The class type of the FrontendNode created in frontend_node."""
return FrontendNode return FrontendNode
@property
def docs_map(self) -> Dict[str, str]:
"""A dict with the name of the component as key and the documentation link as value."""
if self.name_docs_dict is None:
try:
type_settings = getattr(settings, self.type_name)
self.name_docs_dict = {
name: value_dict["documentation"]
for name, value_dict in type_settings.items()
}
except AttributeError as exc:
logger.error(exc)
self.name_docs_dict = {}
return self.name_docs_dict
@property @property
@abstractmethod @abstractmethod
def type_to_loader_dict(self) -> Dict: def type_to_loader_dict(self) -> Dict:
@ -83,7 +101,7 @@ class LangChainTypeCreator(BaseModel, ABC):
signature.add_extra_fields() signature.add_extra_fields()
signature.add_extra_base_classes() signature.add_extra_base_classes()
signature.set_documentation(self.docs_map.get(name, ""))
return signature return signature

View file

@ -1,11 +1,12 @@
import json import json
from typing import Any, Callable, Dict, Sequence from typing import Any, Callable, Dict, Sequence, Type
from langchain.agents import ZeroShotAgent from langchain.agents import ZeroShotAgent
from langchain.agents import agent as agent_module from langchain.agents import agent as agent_module
from langchain.agents.agent import AgentExecutor from langchain.agents.agent import AgentExecutor
from langchain.agents.agent_toolkits.base import BaseToolkit from langchain.agents.agent_toolkits.base import BaseToolkit
from langchain.agents.tools import BaseTool from langchain.agents.tools import BaseTool
from langflow.interface.initialize.vector_store import vecstore_initializer from langflow.interface.initialize.vector_store import vecstore_initializer
from pydantic import ValidationError from pydantic import ValidationError
@ -16,6 +17,11 @@ from langflow.interface.toolkits.base import toolkits_creator
from langflow.interface.chains.base import chain_creator from langflow.interface.chains.base import chain_creator
from langflow.interface.utils import load_file_into_dict from langflow.interface.utils import load_file_into_dict
from langflow.utils import validate from langflow.utils import validate
from langchain.chains.base import Chain
from langchain.vectorstores.base import VectorStore
from langchain.document_loaders.base import BaseLoader
from langchain.prompts.base import BasePromptTemplate
from langflow.chat.config import ChatConfig
def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any: def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any:
@ -72,11 +78,22 @@ def instantiate_based_on_type(class_object, base_type, node_type, params):
return instantiate_utility(node_type, class_object, params) return instantiate_utility(node_type, class_object, params)
elif base_type == "chains": elif base_type == "chains":
return instantiate_chains(node_type, class_object, params) return instantiate_chains(node_type, class_object, params)
elif base_type == "llms":
return instantiate_llm(node_type, class_object, params)
else: else:
return class_object(**params) return class_object(**params)
def instantiate_chains(node_type, class_object, params): def instantiate_llm(node_type, class_object, params: Dict):
# This is a workaround so JinaChat works until streaming is implemented
# if "openai_api_base" in params and "jina" in params["openai_api_base"]:
# False if condition is True
ChatConfig.streaming = "jina" not in params.get("openai_api_base", "")
return class_object(**params)
def instantiate_chains(node_type, class_object: Type[Chain], params: Dict):
if "retriever" in params and hasattr(params["retriever"], "as_retriever"): if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
params["retriever"] = params["retriever"].as_retriever() params["retriever"] = params["retriever"].as_retriever()
if node_type in chain_creator.from_method_nodes: if node_type in chain_creator.from_method_nodes:
@ -88,11 +105,11 @@ def instantiate_chains(node_type, class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_agent(class_object, params): def instantiate_agent(class_object: Type[agent_module.Agent], params: Dict):
return load_agent_executor(class_object, params) return load_agent_executor(class_object, params)
def instantiate_prompt(node_type, class_object, params): def instantiate_prompt(node_type, class_object: Type[BasePromptTemplate], params: Dict):
if node_type == "ZeroShotPrompt": if node_type == "ZeroShotPrompt":
if "tools" not in params: if "tools" not in params:
params["tools"] = [] params["tools"] = []
@ -100,7 +117,7 @@ def instantiate_prompt(node_type, class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_tool(node_type, class_object, params): def instantiate_tool(node_type, class_object: Type[BaseTool], params: Dict):
if node_type == "JsonSpec": if node_type == "JsonSpec":
params["dict_"] = load_file_into_dict(params.pop("path")) params["dict_"] = load_file_into_dict(params.pop("path"))
return class_object(**params) return class_object(**params)
@ -118,7 +135,7 @@ def instantiate_tool(node_type, class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_toolkit(node_type, class_object, params): def instantiate_toolkit(node_type, class_object: Type[BaseToolkit], params: Dict):
loaded_toolkit = class_object(**params) loaded_toolkit = class_object(**params)
# Commenting this out for now to use toolkits as normal tools # Commenting this out for now to use toolkits as normal tools
# if toolkits_creator.has_create_function(node_type): # if toolkits_creator.has_create_function(node_type):
@ -128,7 +145,7 @@ def instantiate_toolkit(node_type, class_object, params):
return loaded_toolkit return loaded_toolkit
def instantiate_embedding(class_object, params): def instantiate_embedding(class_object, params: Dict):
params.pop("model", None) params.pop("model", None)
params.pop("headers", None) params.pop("headers", None)
try: try:
@ -142,7 +159,7 @@ def instantiate_embedding(class_object, params):
return class_object(**params) return class_object(**params)
def instantiate_vectorstore(class_object, params): def instantiate_vectorstore(class_object: Type[VectorStore], params: Dict):
search_kwargs = params.pop("search_kwargs", {}) search_kwargs = params.pop("search_kwargs", {})
if initializer := vecstore_initializer.get(class_object.__name__): if initializer := vecstore_initializer.get(class_object.__name__):
vecstore = initializer(class_object, params) vecstore = initializer(class_object, params)
@ -158,7 +175,7 @@ def instantiate_vectorstore(class_object, params):
return vecstore return vecstore
def instantiate_documentloader(class_object, params): def instantiate_documentloader(class_object: Type[BaseLoader], params: Dict):
if "file_filter" in params: if "file_filter" in params:
# file_filter will be a string but we need a function # file_filter will be a string but we need a function
# that will be used to filter the files using file_filter # that will be used to filter the files using file_filter
@ -187,19 +204,29 @@ def instantiate_documentloader(class_object, params):
return docs return docs
def instantiate_textsplitter(class_object, params): def instantiate_textsplitter(
class_object,
params: Dict,
):
try: try:
documents = params.pop("documents") documents = params.pop("documents")
except KeyError as e: except KeyError as exc:
raise ValueError( raise ValueError(
"The source you provided did not load correctly or was empty." "The source you provided did not load correctly or was empty."
"Try changing the chunk_size of the Text Splitter." "Try changing the chunk_size of the Text Splitter."
) from e ) from exc
if "separator_type" in params and params["separator_type"] == "Text":
text_splitter = class_object(**params) text_splitter = class_object(**params)
else:
params["language"] = params.pop("separator_type", None)
params.pop("separators", None)
text_splitter = class_object.from_language(**params)
return text_splitter.split_documents(documents) return text_splitter.split_documents(documents)
def instantiate_utility(node_type, class_object, params): def instantiate_utility(node_type, class_object, params: Dict):
if node_type == "SQLDatabase": if node_type == "SQLDatabase":
return class_object.from_uri(params.pop("uri")) return class_object.from_uri(params.pop("uri"))
return class_object(**params) return class_object(**params)

View file

@ -4,10 +4,12 @@ import os
from io import BytesIO from io import BytesIO
import re import re
import yaml import yaml
from langchain.base_language import BaseLanguageModel from langchain.base_language import BaseLanguageModel
from PIL.Image import Image from PIL.Image import Image
from langflow.utils.logger import logger from langflow.utils.logger import logger
from langflow.chat.config import ChatConfig
def load_file_into_dict(file_path: str) -> dict: def load_file_into_dict(file_path: str) -> dict:
@ -49,9 +51,9 @@ def try_setting_streaming_options(langchain_object, websocket):
if isinstance(llm, BaseLanguageModel): if isinstance(llm, BaseLanguageModel):
if hasattr(llm, "streaming") and isinstance(llm.streaming, bool): if hasattr(llm, "streaming") and isinstance(llm.streaming, bool):
llm.streaming = True llm.streaming = ChatConfig.streaming
elif hasattr(llm, "stream") and isinstance(llm.stream, bool): elif hasattr(llm, "stream") and isinstance(llm.stream, bool):
llm.stream = True llm.stream = ChatConfig.streaming
return langchain_object return langchain_object

View file

@ -1,5 +1,9 @@
from pathlib import Path
from typing import Optional
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from langflow.api import router from langflow.api import router
from langflow.database.base import create_db_and_tables from langflow.database.base import create_db_and_tables
@ -33,6 +37,42 @@ def create_app():
return app return app
def setup_static_files(app: FastAPI, static_files_dir: Path):
"""
Setup the static files directory.
Args:
app (FastAPI): FastAPI app.
path (str): Path to the static files directory.
"""
app.mount(
"/",
StaticFiles(directory=static_files_dir, html=True),
name="static",
)
@app.exception_handler(404)
async def custom_404_handler(request, __):
path = static_files_dir / "index.html"
if not path.exists():
raise RuntimeError(f"File at path {path} does not exist.")
return FileResponse(path)
# app = create_app()
# setup_static_files(app, static_files_dir)
def setup_app(static_files_dir: Optional[Path]) -> FastAPI:
"""Setup the FastAPI app."""
# get the directory of the current file
if not static_files_dir:
frontend_path = Path(__file__).parent
static_files_dir = frontend_path / "frontend"
app = create_app()
setup_static_files(app, static_files_dir)
return app
app = create_app() app = create_app()

View file

@ -1,24 +1,23 @@
import os import os
from typing import List
import yaml import yaml
from pydantic import BaseSettings, root_validator from pydantic import BaseSettings, root_validator
class Settings(BaseSettings): class Settings(BaseSettings):
chains: List[str] = [] chains: dict = {}
agents: List[str] = [] agents: dict = {}
prompts: List[str] = [] prompts: dict = {}
llms: List[str] = [] llms: dict = {}
tools: List[str] = [] tools: dict = {}
memories: List[str] = [] memories: dict = {}
embeddings: List[str] = [] embeddings: dict = {}
vectorstores: List[str] = [] vectorstores: dict = {}
documentloaders: List[str] = [] documentloaders: dict = {}
wrappers: List[str] = [] wrappers: dict = {}
toolkits: List[str] = [] toolkits: dict = {}
textsplitters: List[str] = [] textsplitters: dict = {}
utilities: List[str] = [] utilities: dict = {}
dev: bool = False dev: bool = False
database_url: str = "sqlite:///./langflow.db" database_url: str = "sqlite:///./langflow.db"
cache: str = "InMemoryCache" cache: str = "InMemoryCache"
@ -38,16 +37,16 @@ class Settings(BaseSettings):
def update_from_yaml(self, file_path: str, dev: bool = False): def update_from_yaml(self, file_path: str, dev: bool = False):
new_settings = load_settings_from_yaml(file_path) new_settings = load_settings_from_yaml(file_path)
self.chains = new_settings.chains or [] self.chains = new_settings.chains or {}
self.agents = new_settings.agents or [] self.agents = new_settings.agents or {}
self.prompts = new_settings.prompts or [] self.prompts = new_settings.prompts or {}
self.llms = new_settings.llms or [] self.llms = new_settings.llms or {}
self.tools = new_settings.tools or [] self.tools = new_settings.tools or {}
self.memories = new_settings.memories or [] self.memories = new_settings.memories or {}
self.wrappers = new_settings.wrappers or [] self.wrappers = new_settings.wrappers or {}
self.toolkits = new_settings.toolkits or [] self.toolkits = new_settings.toolkits or {}
self.textsplitters = new_settings.textsplitters or [] self.textsplitters = new_settings.textsplitters or {}
self.utilities = new_settings.utilities or [] self.utilities = new_settings.utilities or {}
self.dev = dev self.dev = dev
def update_settings(self, **kwargs): def update_settings(self, **kwargs):

View file

@ -21,6 +21,7 @@ class TemplateFieldCreator(BaseModel, ABC):
name: str = "" name: str = ""
display_name: Optional[str] = None display_name: Optional[str] = None
advanced: bool = False advanced: bool = False
info: Optional[str] = ""
def to_dict(self): def to_dict(self):
result = self.dict() result = self.dict()

View file

@ -15,14 +15,21 @@ class FrontendNode(BaseModel):
base_classes: List[str] base_classes: List[str]
name: str = "" name: str = ""
display_name: str = "" display_name: str = ""
documentation: str = ""
def set_documentation(self, documentation: str) -> None:
"""Sets the documentation of the frontend node."""
self.documentation = documentation
def to_dict(self) -> dict: def to_dict(self) -> dict:
"""Returns a dict representation of the frontend node."""
return { return {
self.name: { self.name: {
"template": self.template.to_dict(self.format_field), "template": self.template.to_dict(self.format_field),
"description": self.description, "description": self.description,
"base_classes": self.base_classes, "base_classes": self.base_classes,
"display_name": self.display_name or self.name, "display_name": self.display_name or self.name,
"documentation": self.documentation,
}, },
} }

View file

@ -32,3 +32,13 @@ You are a good listener and you can talk about anything.
HUMAN_PROMPT = "{input}" HUMAN_PROMPT = "{input}"
QA_CHAIN_TYPES = ["stuff", "map_reduce", "map_rerank", "refine"] QA_CHAIN_TYPES = ["stuff", "map_reduce", "map_rerank", "refine"]
# This variable is used to tell the user
# that it can be changed to use other APIs
# like Prem and LocalAI
OPENAI_API_BASE_INFO = """
The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.
You can change this to use other APIs like JinaChat, LocalAI and Prem.
"""

View file

@ -2,6 +2,7 @@ from typing import Optional
from langflow.template.field.base import TemplateField from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.frontend_node.constants import OPENAI_API_BASE_INFO
class LLMFrontendNode(FrontendNode): class LLMFrontendNode(FrontendNode):
@ -15,6 +16,9 @@ class LLMFrontendNode(FrontendNode):
if "key" not in field.name.lower() and "token" not in field.name.lower(): if "key" not in field.name.lower() and "token" not in field.name.lower():
field.password = False field.password = False
if field.name == "openai_api_base":
field.info = OPENAI_API_BASE_INFO
@staticmethod @staticmethod
def format_azure_field(field: TemplateField): def format_azure_field(field: TemplateField):
if field.name == "model_name": if field.name == "model_name":

View file

@ -1,5 +1,6 @@
from langflow.template.field.base import TemplateField from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langchain.text_splitter import Language
class TextSplittersFrontendNode(FrontendNode): class TextSplittersFrontendNode(FrontendNode):
@ -17,6 +18,22 @@ class TextSplittersFrontendNode(FrontendNode):
name = "separator" name = "separator"
elif self.template.type_name == "RecursiveCharacterTextSplitter": elif self.template.type_name == "RecursiveCharacterTextSplitter":
name = "separators" name = "separators"
# Add a field for type of separator
# which will have Text or any value from the
# Language enum
self.template.add_field(
TemplateField(
field_type="str",
required=True,
show=True,
name="separator_type",
advanced=False,
is_list=True,
options=[x.value for x in Language],
value="Text",
display_name="Separator Type",
)
)
self.template.add_field( self.template.add_field(
TemplateField( TemplateField(
field_type="str", field_type="str",

View file

@ -200,7 +200,7 @@ class VectorStoreFrontendNode(FrontendNode):
self.template.add_field(field) self.template.add_field(field)
def add_extra_base_classes(self) -> None: def add_extra_base_classes(self) -> None:
self.base_classes.append("BaseRetriever") self.base_classes.extend(("BaseRetriever", "VectorStoreRetriever"))
@staticmethod @staticmethod
def format_field(field: TemplateField, name: Optional[str] = None) -> None: def format_field(field: TemplateField, name: Optional[str] = None) -> None:

View file

@ -1212,15 +1212,15 @@
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
}, },
"node_modules/@mui/system": { "node_modules/@mui/system": {
"version": "5.13.5", "version": "5.13.6",
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.5.tgz", "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.6.tgz",
"integrity": "sha512-n0gzUxoZ2ZHZgnExkh2Htvo9uW2oakofgPRQrDoa/GQOWyRD0NH9MDszBwOb6AAoXZb+OV5TE7I4LeZ/dzgHYA==", "integrity": "sha512-G3Xr28uLqU3DyF6r2LQkHGw/ku4P0AHzlKVe7FGXOPl7X1u+hoe2xxj8Vdiq/69II/mh9OP21i38yBWgWb7WgQ==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.21.0", "@babel/runtime": "^7.22.5",
"@mui/private-theming": "^5.13.1", "@mui/private-theming": "^5.13.1",
"@mui/styled-engine": "^5.13.2", "@mui/styled-engine": "^5.13.2",
"@mui/types": "^7.2.4", "@mui/types": "^7.2.4",
"@mui/utils": "^5.13.1", "@mui/utils": "^5.13.6",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"csstype": "^3.1.2", "csstype": "^3.1.2",
"prop-types": "^15.8.1" "prop-types": "^15.8.1"
@ -1280,11 +1280,11 @@
} }
}, },
"node_modules/@mui/utils": { "node_modules/@mui/utils": {
"version": "5.13.1", "version": "5.13.6",
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz", "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.6.tgz",
"integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==", "integrity": "sha512-ggNlxl5NPSbp+kNcQLmSig6WVB0Id+4gOxhx644987v4fsji+CSXc+MFYLocFB/x4oHtzCUlSzbVHlJfP/fXoQ==",
"dependencies": { "dependencies": {
"@babel/runtime": "^7.21.0", "@babel/runtime": "^7.22.5",
"@types/prop-types": "^15.7.5", "@types/prop-types": "^15.7.5",
"@types/react-is": "^18.2.0", "@types/react-is": "^18.2.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
@ -3444,7 +3444,7 @@
"version": "16.18.12", "version": "16.18.12",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.12.tgz",
"integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==", "integrity": "sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==",
"devOptional": true "dev": true
}, },
"node_modules/@types/parse-json": { "node_modules/@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",
@ -3714,9 +3714,9 @@
} }
}, },
"node_modules/aria-query": { "node_modules/aria-query": {
"version": "5.2.1", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.2.1.tgz", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
"integrity": "sha512-7uFg4b+lETFgdaJyETnILsXgnnzVnkHcgRbwbPwevm5x/LmUlt3MjczMRe1zg824iBgXZNRPTBftNYyRSKLp2g==", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"dequal": "^2.0.3" "dequal": "^2.0.3"
@ -5010,9 +5010,9 @@
} }
}, },
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.4.438", "version": "1.4.440",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.438.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.440.tgz",
"integrity": "sha512-x94U0FhphEsHsOloCvlsujHCvoir0ZQ73ZAs/QN4PLx98uNvyEU79F75rq1db75Bx/atvuh7KPeuxelh+xfYJw==" "integrity": "sha512-r6dCgNpRhPwiWlxbHzZQ/d9swfPaEJGi8ekqRBwQYaR3WmA5VkqQfBWSDDjuJU1ntO+W9tHx8OHV/96Q8e0dVw=="
}, },
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
@ -5504,6 +5504,7 @@
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"optional": true, "optional": true,
"os": [ "os": [
@ -6745,9 +6746,9 @@
} }
}, },
"node_modules/katex": { "node_modules/katex": {
"version": "0.16.7", "version": "0.16.8",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.7.tgz", "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz",
"integrity": "sha512-Xk9C6oGKRwJTfqfIbtr0Kes9OSv6IFsuhFGc7tW4urlpMJtuh+7YhzU6YEG9n8gmWKcMAFzkp7nr+r69kV0zrA==", "integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==",
"funding": [ "funding": [
"https://opencollective.com/katex", "https://opencollective.com/katex",
"https://github.com/sponsors/katex" "https://github.com/sponsors/katex"

View file

@ -25,6 +25,7 @@ import { nodeColors } from "../../../../utils";
import ShadTooltip from "../../../../components/ShadTooltipComponent"; import ShadTooltip from "../../../../components/ShadTooltipComponent";
import { PopUpContext } from "../../../../contexts/popUpContext"; import { PopUpContext } from "../../../../contexts/popUpContext";
import ToggleShadComponent from "../../../../components/toggleShadComponent"; import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { Info } from "lucide-react";
export default function ParameterComponent({ export default function ParameterComponent({
left, left,
@ -36,9 +37,11 @@ export default function ParameterComponent({
type, type,
name = "", name = "",
required = false, required = false,
info = "",
}: ParameterComponentType) { }: ParameterComponentType) {
const ref = useRef(null); const ref = useRef(null);
const refHtml = useRef(null); const refHtml = useRef(null);
const infoHtml = useRef(null);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0); const [position, setPosition] = useState(0);
const { closePopUp } = useContext(PopUpContext); const { closePopUp } = useContext(PopUpContext);
@ -79,6 +82,18 @@ export default function ParameterComponent({
}); });
}; };
useEffect(() => {
infoHtml.current = (
<div className="h-full w-full break-words">
{info.split("\n").map((line, i) => (
<p key={i} className="block">
{line}
</p>
))}
</div>
);
}, [info]);
useEffect(() => { useEffect(() => {
const groupedObj = groupByFamily(myData, tooltipTitle); const groupedObj = groupByFamily(myData, tooltipTitle);
@ -126,9 +141,22 @@ export default function ParameterComponent({
className="w-full flex flex-wrap justify-between items-center bg-muted dark:bg-gray-800 dark:text-white mt-1 px-5 py-2" className="w-full flex flex-wrap justify-between items-center bg-muted dark:bg-gray-800 dark:text-white mt-1 px-5 py-2"
> >
<> <>
<div className={"text-sm truncate w-full " + (left ? "" : "text-end")}> <div
className={
"text-sm truncate w-full" +
(left ? "" : " text-end") +
(info !== "" ? " flex items-center" : "")
}
>
{title} {title}
<span className="text-red-600">{required ? " *" : ""}</span> <span className="text-red-600">{required ? " *" : ""}</span>
<div className="">
{info !== "" && (
<ShadTooltip content={infoHtml.current}>
<Info className="ml-2 relative bottom-0.5 w-3 h-3" />
</ShadTooltip>
)}
</div>
</div> </div>
{left && {left &&
(type === "str" || (type === "str" ||

View file

@ -6,16 +6,7 @@ import {
} from "../../utils"; } from "../../utils";
import ParameterComponent from "./components/parameterComponent"; import ParameterComponent from "./components/parameterComponent";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import { import { useContext, useState, useEffect, useRef } from "react";
useContext,
useState,
useEffect,
useRef,
ForwardRefExoticComponent,
ComponentType,
SVGProps,
ReactNode,
} from "react";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { PopUpContext } from "../../contexts/popUpContext"; import { PopUpContext } from "../../contexts/popUpContext";
@ -23,10 +14,8 @@ import NodeModal from "../../modals/NodeModal";
import Tooltip from "../../components/TooltipComponent"; import Tooltip from "../../components/TooltipComponent";
import { NodeToolbar } from "reactflow"; import { NodeToolbar } from "reactflow";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import ShadTooltip from "../../components/ShadTooltipComponent"; import ShadTooltip from "../../components/ShadTooltipComponent";
import { useSSE } from "../../contexts/SSEContext"; import { useSSE } from "../../contexts/SSEContext";
import { ReactElement } from "react-markdown/lib/react-markdown";
export default function GenericNode({ export default function GenericNode({
data, data,
@ -46,6 +35,7 @@ export default function GenericNode({
const [validationStatus, setValidationStatus] = useState(null); const [validationStatus, setValidationStatus] = useState(null);
// State for outline color // State for outline color
const { sseData, isBuilding } = useSSE(); const { sseData, isBuilding } = useSSE();
const refHtml = useRef(null);
// useEffect(() => { // useEffect(() => {
// if (reactFlowInstance) { // if (reactFlowInstance) {
@ -103,11 +93,8 @@ export default function GenericNode({
color: nodeColors[types[data.type]] ?? nodeColors.unknown, color: nodeColors[types[data.type]] ?? nodeColors.unknown,
}} }}
/> />
<div className="ml-2 truncate"> <div className="ml-2 truncate flex">
<ShadTooltip <ShadTooltip content={data.node.display_name}>
delayDuration={1500}
content={data.node.display_name}
>
<div className="ml-2 truncate text-gray-800"> <div className="ml-2 truncate text-gray-800">
{data.node.display_name} {data.node.display_name}
</div> </div>
@ -214,6 +201,7 @@ export default function GenericNode({
? toTitleCase(data.node.template[t].name) ? toTitleCase(data.node.template[t].name)
: toTitleCase(t) : toTitleCase(t)
} }
info={data.node.template[t].info}
name={t} name={t}
tooltipTitle={data.node.template[t].type} tooltipTitle={data.node.template[t].type}
required={data.node.template[t].required} required={data.node.template[t].required}

View file

@ -1,3 +1,4 @@
import { ShadTooltipProps } from "../../types/components";
import { import {
Tooltip, Tooltip,
TooltipContent, TooltipContent,
@ -5,18 +6,19 @@ import {
TooltipTrigger, TooltipTrigger,
} from "../ui/tooltip"; } from "../ui/tooltip";
const ShadTooltip = (props) => { const ShadTooltip = ({
delayDuration = 500,
side,
content,
children,
}: ShadTooltipProps) => {
return ( return (
<TooltipProvider> <TooltipProvider>
<Tooltip delayDuration={props.delayDuration}> <Tooltip delayDuration={delayDuration}>
<TooltipTrigger asChild>{props.children}</TooltipTrigger> <TooltipTrigger asChild>{children}</TooltipTrigger>
<TooltipContent <TooltipContent side={side} avoidCollisions={false} sticky="always">
side={props.side} {content}
avoidCollisions={false}
sticky="always"
>
{props.content}
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>

View file

@ -1,9 +1,11 @@
import { Listbox, Transition } from "@headlessui/react"; import { Listbox, Transition } from "@headlessui/react";
import { Fragment, useEffect, useState } from "react"; import { Fragment, useContext, useEffect, useState } from "react";
import { DropDownComponentType } from "../../types/components"; import { DropDownComponentType } from "../../types/components";
import { classNames } from "../../utils"; import { classNames } from "../../utils";
import { INPUT_STYLE } from "../../constants"; import { INPUT_STYLE } from "../../constants";
import { ChevronsUpDown, Check } from "lucide-react"; import { ChevronsUpDown, Check } from "lucide-react";
import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
export default function Dropdown({ export default function Dropdown({
value, value,
@ -12,12 +14,15 @@ export default function Dropdown({
editNode = false, editNode = false,
numberOfOptions = 0, numberOfOptions = 0,
}: DropDownComponentType) { }: DropDownComponentType) {
const { closePopUp } = useContext(PopUpContext);
let [internalValue, setInternalValue] = useState( let [internalValue, setInternalValue] = useState(
value === "" || !value ? "Choose an option" : value value === "" || !value ? "Choose an option" : value
); );
useEffect(() => { useEffect(() => {
setInternalValue(value === "" || !value ? "Choose an option" : value); setInternalValue(value === "" || !value ? "Choose an option" : value);
}, [value]); }, [closePopUp]);
return ( return (
<> <>

View file

@ -2,6 +2,7 @@ import { useContext, useEffect, useState } from "react";
import { FloatComponentType } from "../../types/components"; import { FloatComponentType } from "../../types/components";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { INPUT_STYLE } from "../../constants"; import { INPUT_STYLE } from "../../constants";
import { PopUpContext } from "../../contexts/popUpContext";
export default function FloatComponent({ export default function FloatComponent({
value, value,
@ -12,6 +13,7 @@ export default function FloatComponent({
}: FloatComponentType) { }: FloatComponentType) {
const [myValue, setMyValue] = useState(value ?? ""); const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext); const { setDisableCopyPaste } = useContext(TabsContext);
const { closePopUp } = useContext(PopUpContext);
const step = 0.1; const step = 0.1;
const min = 0; const min = 0;
@ -26,7 +28,7 @@ export default function FloatComponent({
useEffect(() => { useEffect(() => {
setMyValue(value); setMyValue(value);
}, [value]); }, [closePopUp]);
return ( return (
<div <div

View file

@ -5,6 +5,7 @@ import { TabsContext } from "../../contexts/tabsContext";
import _ from "lodash"; import _ from "lodash";
import { INPUT_STYLE } from "../../constants"; import { INPUT_STYLE } from "../../constants";
import { X, Plus } from "lucide-react"; import { X, Plus } from "lucide-react";
import { PopUpContext } from "../../contexts/popUpContext";
export default function InputListComponent({ export default function InputListComponent({
value, value,
@ -13,12 +14,19 @@ export default function InputListComponent({
editNode = false, editNode = false,
}: InputListComponentType) { }: InputListComponentType) {
const [inputList, setInputList] = useState(value ?? [""]); const [inputList, setInputList] = useState(value ?? [""]);
const { closePopUp } = useContext(PopUpContext);
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
setInputList([""]); setInputList([""]);
onChange([""]); onChange([""]);
} }
}, [disabled, onChange]); }, [disabled, onChange]);
useEffect(() => {
setInputList(value);
}, [closePopUp]);
return ( return (
<div <div
className={ className={
@ -44,9 +52,9 @@ export default function InputListComponent({
setInputList((old) => { setInputList((old) => {
let newInputList = _.cloneDeep(old); let newInputList = _.cloneDeep(old);
newInputList[idx] = e.target.value; newInputList[idx] = e.target.value;
onChange(newInputList);
return newInputList; return newInputList;
}); });
onChange(inputList);
}} }}
/> />
{idx === inputList.length - 1 ? ( {idx === inputList.length - 1 ? (

View file

@ -3,6 +3,7 @@ import { FloatComponentType } from "../../types/components";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { classNames } from "../../utils"; import { classNames } from "../../utils";
import { INPUT_STYLE } from "../../constants"; import { INPUT_STYLE } from "../../constants";
import { PopUpContext } from "../../contexts/popUpContext";
export default function IntComponent({ export default function IntComponent({
value, value,
@ -14,6 +15,7 @@ export default function IntComponent({
const [myValue, setMyValue] = useState(value ?? ""); const [myValue, setMyValue] = useState(value ?? "");
const { setDisableCopyPaste } = useContext(TabsContext); const { setDisableCopyPaste } = useContext(TabsContext);
const min = 0; const min = 0;
const { closePopUp } = useContext(PopUpContext);
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
@ -24,7 +26,7 @@ export default function IntComponent({
useEffect(() => { useEffect(() => {
setMyValue(value); setMyValue(value);
}, [value]); }, [closePopUp]);
return ( return (
<div <div

View file

@ -198,8 +198,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
edge.style = { stroke: "#555555" }; edge.style = { stroke: "#555555" };
}); });
} }
function updateDisplay_name(node: NodeType, template: APIClassType) { function updateDisplay_name(node: NodeType, template: APIClassType) {
node.data.node.display_name = template["display_name"]?template["display_name"]:node.data.type; node.data.node.display_name = template["display_name"] || node.data.type;
}
function updateNodeDocumentation(node: NodeType, template: APIClassType) {
node.data.node.documentation = template["documentation"];
} }
function processFlowNodes(flow) { function processFlowNodes(flow) {
@ -216,6 +221,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
updateNodeEdges(flow, node, template); updateNodeEdges(flow, node, template);
updateNodeDescription(node, template); updateNodeDescription(node, template);
updateNodeTemplate(node, template); updateNodeTemplate(node, template);
updateNodeDocumentation(node, template);
} }
}); });
} }
@ -224,7 +230,11 @@ export function TabsProvider({ children }: { children: ReactNode }) {
node.data.node.base_classes = template["base_classes"]; node.data.node.base_classes = template["base_classes"];
} }
function updateNodeEdges(flow:FlowType, node:NodeType,template:APIClassType) { function updateNodeEdges(
flow: FlowType,
node: NodeType,
template: APIClassType
) {
flow.data.edges.forEach((edge) => { flow.data.edges.forEach((edge) => {
if (edge.source === node.id) { if (edge.source === node.id) {
edge.sourceHandle = edge.sourceHandle edge.sourceHandle = edge.sourceHandle
@ -404,7 +414,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
y: insidePosition.y + n.position.y - minimumY, y: insidePosition.y + n.position.y - minimumY,
}, },
data: { data: {
...n.data, ..._.cloneDeep(n.data),
id: newId, id: newId,
}, },
}; };

View file

@ -103,7 +103,9 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
<div className="flex items-center justify-between px-2"> <div className="flex items-center justify-between px-2">
<TabsList> <TabsList>
{tabs.map((tab, index) => ( {tabs.map((tab, index) => (
<TabsTrigger key={index} value={index.toString()}>{tab.name}</TabsTrigger> <TabsTrigger key={index} value={index.toString()}>
{tab.name}
</TabsTrigger>
))} ))}
</TabsList> </TabsList>
<div className="float-right"> <div className="float-right">

View file

@ -62,7 +62,7 @@ export default function ExtraSidebar() {
return ( return (
<div className="w-52 flex flex-col overflow-hidden scrollbar-hide h-full border-r"> <div className="w-52 flex flex-col overflow-hidden scrollbar-hide h-full border-r">
<div className="mt-2 mb-2 w-full flex gap-2 justify-between px-2 items-center"> <div className="mt-2 mb-2 w-full flex gap-2 justify-between px-2 items-center">
<ShadTooltip delayDuration={1000} content="Import" side="top"> <ShadTooltip content="Import" side="top">
<button <button
className="hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center shadow-sm transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center rounded-md bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50" className="hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center shadow-sm transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center rounded-md bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50"
onClick={() => { onClick={() => {
@ -74,7 +74,7 @@ export default function ExtraSidebar() {
</button> </button>
</ShadTooltip> </ShadTooltip>
<ShadTooltip delayDuration={1000} content="Export" side="top"> <ShadTooltip content="Export" side="top">
<button <button
className={classNames( className={classNames(
"hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center shadow-sm transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 rounded-md" "hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center shadow-sm transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 rounded-md"
@ -86,7 +86,7 @@ export default function ExtraSidebar() {
<FileDown className="w-5 h-5 dark:text-gray-300"></FileDown> <FileDown className="w-5 h-5 dark:text-gray-300"></FileDown>
</button> </button>
</ShadTooltip> </ShadTooltip>
<ShadTooltip delayDuration={1000} content="Code" side="top"> <ShadTooltip content="Code" side="top">
<button <button
className={classNames( className={classNames(
"hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center shadow-sm transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 rounded-md" "hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center shadow-sm transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 rounded-md"
@ -99,7 +99,7 @@ export default function ExtraSidebar() {
</button> </button>
</ShadTooltip> </ShadTooltip>
<ShadTooltip delayDuration={1000} content="Save" side="top"> <ShadTooltip content="Save" side="top">
<button <button
className="hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center transition-all shadow-sm duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 rounded-md" className="hover:dark:hover:bg-[#242f47] text-gray-700 w-full justify-center transition-all shadow-sm duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 relative inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 rounded-md"
onClick={(event) => { onClick={(event) => {
@ -156,7 +156,6 @@ export default function ExtraSidebar() {
.map((t: string, k) => ( .map((t: string, k) => (
<ShadTooltip <ShadTooltip
content={data[d][t].display_name} content={data[d][t].display_name}
delayDuration={1500}
side="right" side="right"
key={data[d][t].display_name} key={data[d][t].display_name}
> >

View file

@ -1,5 +1,5 @@
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { Settings2, Copy, Trash2 } from "lucide-react"; import { Settings2, Copy, Trash2, FileText } from "lucide-react";
import { classNames } from "../../../../utils"; import { classNames } from "../../../../utils";
import { TabsContext } from "../../../../contexts/tabsContext"; import { TabsContext } from "../../../../contexts/tabsContext";
import { useReactFlow } from "reactflow"; import { useReactFlow } from "reactflow";
@ -29,23 +29,21 @@ const NodeToolbarComponent = (props) => {
<> <>
<div className="h-10 w-26"> <div className="h-10 w-26">
<span className="isolate inline-flex rounded-md shadow-sm"> <span className="isolate inline-flex rounded-md shadow-sm">
<ShadTooltip delayDuration={1000} content="Delete" side="top"> <ShadTooltip content="Delete" side="top">
<button <button
className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative inline-flex items-center rounded-l-md bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10" className="hover:dark:hover:bg-[#242f47] text-foreground transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-muted-foreground shadow-md relative inline-flex items-center rounded-l-md bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10"
onClick={() => { onClick={() => {
props.deleteNode(props.data.id); props.deleteNode(props.data.id);
}} }}
> >
<Trash2 className="w-4 h-4 dark:text-gray-300"></Trash2> <Trash2 className="w-4 h-4 dark:text-muted-foreground"></Trash2>
</button> </button>
</ShadTooltip> </ShadTooltip>
<ShadTooltip delayDuration={1000} content="Duplicate" side="top"> <ShadTooltip content="Duplicate" side="top">
<button <button
className={classNames( className={classNames(
nodeLength > 0 "hover:dark:hover:bg-[#242f47] text-foreground transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-muted-foreground shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10"
? "hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10"
: "hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md"
)} )}
onClick={(event) => { onClick={(event) => {
event.preventDefault(); event.preventDefault();
@ -64,31 +62,64 @@ const NodeToolbarComponent = (props) => {
); );
}} }}
> >
<Copy className="w-4 h-4 dark:text-gray-300"></Copy> <Copy className="w-4 h-4 dark:text-muted-foreground"></Copy>
</button> </button>
</ShadTooltip> </ShadTooltip>
{nodeLength > 0 && ( <ShadTooltip
<ShadTooltip delayDuration={1000} content="Edit" side="top"> content={
<button props.data.node.documentation === ""
className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md" ? "Coming Soon"
: "Documentation"
}
side="top"
>
<a
className={classNames(
"hover:dark:hover:bg-[#242f47] transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-muted-foreground shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10" +
(props.data.node.documentation === ""
? " text-muted-foreground"
: " text-foreground")
)}
href={props.data.node.documentation}
// deactivate link if no documentation is provided
onClick={(event) => { onClick={(event) => {
if (props.data.node.documentation === "") {
event.preventDefault();
}
}}
>
<FileText className="w-4 h-4 dark:text-muted-foreground"></FileText>
</a>
</ShadTooltip>
<ShadTooltip content="Edit" side="top">
<button
className={classNames(
"hover:dark:hover:bg-[#242f47] transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-muted-foreground shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md" +
(nodeLength == 0
? " text-muted-foreground"
: " text-foreground")
)}
onClick={(event) => {
if (nodeLength == 0) {
event.preventDefault();
}
event.preventDefault(); event.preventDefault();
props.openPopUp(<EditNodeModal data={props.data} />); props.openPopUp(<EditNodeModal data={props.data} />);
}} }}
> >
<Settings2 className="w-4 h-4 dark:text-gray-300"></Settings2> <Settings2 className="w-4 h-4 dark:text-muted-foreground"></Settings2>
</button> </button>
</ShadTooltip> </ShadTooltip>
)}
{/* {/*
<Menu as="div" className="relative inline-block text-left z-100"> <Menu as="div" className="relative inline-block text-left z-100">
<button className="hover:dark:hover:bg-[#242f47] text-gray-700 transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-gray-300 shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md"> <button className="hover:dark:hover:bg-[#242f47] text-foreground transition-all duration-500 ease-in-out dark:bg-gray-800 dark:text-muted-foreground shadow-md relative -ml-px inline-flex items-center bg-white px-2 py-2 ring-1 ring-inset ring-gray-300 hover:bg-muted focus:z-10 rounded-r-md">
<div> <div>
<Menu.Button className="flex items-center"> <Menu.Button className="flex items-center">
<EllipsisVerticalIcon <EllipsisVerticalIcon
className="w-5 h-5 dark:text-gray-300" className="w-5 h-5 dark:text-muted-foreground"
aria-hidden="true" aria-hidden="true"
/> />
</Menu.Button> </Menu.Button>
@ -117,7 +148,7 @@ const NodeToolbarComponent = (props) => {
className={classNames( className={classNames(
active active
? "bg-muted text-gray-900" ? "bg-muted text-gray-900"
: "text-gray-700", : "text-foreground",
"w-full group flex items-center px-4 py-2 text-sm" "w-full group flex items-center px-4 py-2 text-sm"
)} )}
> >
@ -157,7 +188,7 @@ const NodeToolbarComponent = (props) => {
className={classNames( className={classNames(
active active
? "bg-muted text-gray-900" ? "bg-muted text-gray-900"
: "text-gray-700", : "text-foreground",
"w-full group flex items-center px-4 py-2 text-sm" "w-full group flex items-center px-4 py-2 text-sm"
)} )}
> >

View file

@ -12,6 +12,7 @@ export type APIClassType = {
description: string; description: string;
template: APITemplateType; template: APITemplateType;
display_name: string; display_name: string;
documentation: string;
[key: string]: Array<string> | string | APITemplateType; [key: string]: Array<string> | string | APITemplateType;
}; };
export type TemplateVariableType = { export type TemplateVariableType = {

View file

@ -1,10 +1,4 @@
import { import { ReactElement, ReactNode } from "react";
ComponentType,
ForwardRefExoticComponent,
ReactElement,
ReactNode,
SVGProps,
} from "react";
import { NodeDataType } from "../flow/index"; import { NodeDataType } from "../flow/index";
import { typesContextType } from "../typesContext"; import { typesContextType } from "../typesContext";
export type InputComponentType = { export type InputComponentType = {
@ -41,6 +35,7 @@ export type ParameterComponentType = {
name?: string; name?: string;
tooltipTitle: string; tooltipTitle: string;
dataContext?: typesContextType; dataContext?: typesContextType;
info?: string;
}; };
export type InputListComponentType = { export type InputListComponentType = {
value: string[]; value: string[];
@ -115,3 +110,12 @@ export type RadialProgressType = {
value?: number; value?: number;
color?: string; color?: string;
}; };
export type Side = "top" | "right" | "bottom" | "left";
export type ShadTooltipProps = {
delayDuration?: number;
side?: Side;
content: ReactNode;
children: ReactNode;
};

View file

@ -978,5 +978,5 @@ export function getRandomKeyByssmm(): string {
const now = new Date(); const now = new Date();
const seconds = String(now.getSeconds()).padStart(2, "0"); const seconds = String(now.getSeconds()).padStart(2, "0");
const milliseconds = String(now.getMilliseconds()).padStart(3, "0"); const milliseconds = String(now.getMilliseconds()).padStart(3, "0");
return seconds + milliseconds; return seconds + milliseconds + Math.abs(Math.floor(Math.random() * 10001));
} }

View file

@ -26,6 +26,7 @@ def test_zero_shot_agent(client: TestClient):
"type": "LLMChain", "type": "LLMChain",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["allowed_tools"] == { assert template["allowed_tools"] == {
"required": False, "required": False,
@ -37,6 +38,7 @@ def test_zero_shot_agent(client: TestClient):
"type": "Tool", "type": "Tool",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
@ -60,6 +62,7 @@ def test_json_agent(client: TestClient):
"type": "BaseToolkit", "type": "BaseToolkit",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -72,6 +75,7 @@ def test_json_agent(client: TestClient):
"list": False, "list": False,
"advanced": False, "advanced": False,
"display_name": "LLM", "display_name": "LLM",
"info": "",
} }
@ -99,6 +103,7 @@ def test_csv_agent(client: TestClient):
"list": False, "list": False,
"file_path": None, "file_path": None,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -111,6 +116,7 @@ def test_csv_agent(client: TestClient):
"list": False, "list": False,
"advanced": False, "advanced": False,
"display_name": "LLM", "display_name": "LLM",
"info": "",
} }
@ -143,6 +149,7 @@ def test_initialize_agent(client: TestClient):
"type": "str", "type": "str",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["memory"] == { assert template["memory"] == {
"required": False, "required": False,
@ -154,6 +161,7 @@ def test_initialize_agent(client: TestClient):
"type": "BaseChatMemory", "type": "BaseChatMemory",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["tools"] == { assert template["tools"] == {
"required": False, "required": False,
@ -165,6 +173,7 @@ def test_initialize_agent(client: TestClient):
"type": "Tool", "type": "Tool",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -177,4 +186,5 @@ def test_initialize_agent(client: TestClient):
"list": False, "list": False,
"advanced": False, "advanced": False,
"display_name": "LLM", "display_name": "LLM",
"info": "",
} }

View file

@ -38,6 +38,7 @@ def test_conversation_chain(client: TestClient):
"type": "BaseMemory", "type": "BaseMemory",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["verbose"] == { assert template["verbose"] == {
"required": False, "required": False,
@ -49,6 +50,7 @@ def test_conversation_chain(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -60,6 +62,7 @@ def test_conversation_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["input_key"] == { assert template["input_key"] == {
"required": True, "required": True,
@ -72,6 +75,7 @@ def test_conversation_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["output_key"] == { assert template["output_key"] == {
"required": True, "required": True,
@ -84,6 +88,7 @@ def test_conversation_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["_type"] == "ConversationChain" assert template["_type"] == "ConversationChain"
@ -120,6 +125,7 @@ def test_llm_chain(client: TestClient):
"type": "BaseMemory", "type": "BaseMemory",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["verbose"] == { assert template["verbose"] == {
"required": False, "required": False,
@ -132,6 +138,7 @@ def test_llm_chain(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -143,6 +150,7 @@ def test_llm_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["output_key"] == { assert template["output_key"] == {
"required": True, "required": True,
@ -155,6 +163,7 @@ def test_llm_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
@ -184,6 +193,7 @@ def test_llm_checker_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["_type"] == "LLMCheckerChain" assert template["_type"] == "LLMCheckerChain"
@ -217,6 +227,7 @@ def test_llm_math_chain(client: TestClient):
"type": "BaseMemory", "type": "BaseMemory",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["verbose"] == { assert template["verbose"] == {
"required": False, "required": False,
@ -229,6 +240,7 @@ def test_llm_math_chain(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["llm"] == { assert template["llm"] == {
"required": True, "required": True,
@ -240,6 +252,7 @@ def test_llm_math_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["input_key"] == { assert template["input_key"] == {
"required": True, "required": True,
@ -252,6 +265,7 @@ def test_llm_math_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["output_key"] == { assert template["output_key"] == {
"required": True, "required": True,
@ -264,6 +278,7 @@ def test_llm_math_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["_type"] == "LLMMathChain" assert template["_type"] == "LLMMathChain"
@ -304,6 +319,7 @@ def test_series_character_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["character"] == { assert template["character"] == {
"required": True, "required": True,
@ -315,6 +331,7 @@ def test_series_character_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["series"] == { assert template["series"] == {
"required": True, "required": True,
@ -326,6 +343,7 @@ def test_series_character_chain(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["_type"] == "SeriesCharacterChain" assert template["_type"] == "SeriesCharacterChain"
@ -367,6 +385,7 @@ def test_mid_journey_prompt_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
# Test the description object # Test the description object
assert ( assert (
@ -406,6 +425,7 @@ def test_time_travel_guide_chain(client: TestClient):
"type": "BaseLanguageModel", "type": "BaseLanguageModel",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["memory"] == { assert template["memory"] == {
"required": False, "required": False,
@ -417,6 +437,7 @@ def test_time_travel_guide_chain(client: TestClient):
"type": "BaseChatMemory", "type": "BaseChatMemory",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert chain["description"] == "Time travel guide chain." assert chain["description"] == "Time travel guide chain."

View file

@ -121,6 +121,7 @@ def test_openai(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["verbose"] == { assert template["verbose"] == {
"required": False, "required": False,
@ -132,6 +133,7 @@ def test_openai(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["client"] == { assert template["client"] == {
"required": False, "required": False,
@ -143,6 +145,7 @@ def test_openai(client: TestClient):
"type": "Any", "type": "Any",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["model_name"] == { assert template["model_name"] == {
"required": False, "required": False,
@ -162,6 +165,7 @@ def test_openai(client: TestClient):
"type": "str", "type": "str",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
# Add more assertions for other properties here # Add more assertions for other properties here
assert template["temperature"] == { assert template["temperature"] == {
@ -175,6 +179,7 @@ def test_openai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["max_tokens"] == { assert template["max_tokens"] == {
"required": False, "required": False,
@ -187,6 +192,7 @@ def test_openai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["top_p"] == { assert template["top_p"] == {
"required": False, "required": False,
@ -199,6 +205,7 @@ def test_openai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["frequency_penalty"] == { assert template["frequency_penalty"] == {
"required": False, "required": False,
@ -211,6 +218,7 @@ def test_openai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["presence_penalty"] == { assert template["presence_penalty"] == {
"required": False, "required": False,
@ -223,6 +231,7 @@ def test_openai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["n"] == { assert template["n"] == {
"required": False, "required": False,
@ -235,6 +244,7 @@ def test_openai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["best_of"] == { assert template["best_of"] == {
"required": False, "required": False,
@ -247,6 +257,7 @@ def test_openai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["model_kwargs"] == { assert template["model_kwargs"] == {
"required": False, "required": False,
@ -258,6 +269,7 @@ def test_openai(client: TestClient):
"type": "code", "type": "code",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["openai_api_key"] == { assert template["openai_api_key"] == {
"required": False, "required": False,
@ -271,6 +283,7 @@ def test_openai(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["batch_size"] == { assert template["batch_size"] == {
"required": False, "required": False,
@ -283,6 +296,7 @@ def test_openai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["request_timeout"] == { assert template["request_timeout"] == {
"required": False, "required": False,
@ -294,6 +308,7 @@ def test_openai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["logit_bias"] == { assert template["logit_bias"] == {
"required": False, "required": False,
@ -305,6 +320,7 @@ def test_openai(client: TestClient):
"type": "code", "type": "code",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["max_retries"] == { assert template["max_retries"] == {
"required": False, "required": False,
@ -317,6 +333,7 @@ def test_openai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["streaming"] == { assert template["streaming"] == {
"required": False, "required": False,
@ -329,6 +346,7 @@ def test_openai(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
@ -352,6 +370,7 @@ def test_chat_open_ai(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["client"] == { assert template["client"] == {
"required": False, "required": False,
@ -363,6 +382,7 @@ def test_chat_open_ai(client: TestClient):
"type": "Any", "type": "Any",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["model_name"] == { assert template["model_name"] == {
"required": False, "required": False,
@ -385,6 +405,7 @@ def test_chat_open_ai(client: TestClient):
"type": "str", "type": "str",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["temperature"] == { assert template["temperature"] == {
"required": False, "required": False,
@ -397,6 +418,7 @@ def test_chat_open_ai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["model_kwargs"] == { assert template["model_kwargs"] == {
"required": False, "required": False,
@ -408,6 +430,7 @@ def test_chat_open_ai(client: TestClient):
"type": "code", "type": "code",
"list": False, "list": False,
"advanced": True, "advanced": True,
"info": "",
} }
assert template["openai_api_key"] == { assert template["openai_api_key"] == {
"required": False, "required": False,
@ -421,6 +444,7 @@ def test_chat_open_ai(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["request_timeout"] == { assert template["request_timeout"] == {
"required": False, "required": False,
@ -432,6 +456,7 @@ def test_chat_open_ai(client: TestClient):
"type": "float", "type": "float",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["max_retries"] == { assert template["max_retries"] == {
"required": False, "required": False,
@ -444,6 +469,7 @@ def test_chat_open_ai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["streaming"] == { assert template["streaming"] == {
"required": False, "required": False,
@ -456,6 +482,7 @@ def test_chat_open_ai(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["n"] == { assert template["n"] == {
"required": False, "required": False,
@ -468,6 +495,7 @@ def test_chat_open_ai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["max_tokens"] == { assert template["max_tokens"] == {
@ -480,6 +508,7 @@ def test_chat_open_ai(client: TestClient):
"type": "int", "type": "int",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["_type"] == "ChatOpenAI" assert template["_type"] == "ChatOpenAI"
assert ( assert (

View file

@ -28,6 +28,7 @@ def test_prompt_template(client: TestClient):
"type": "str", "type": "str",
"list": True, "list": True,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["output_parser"] == { assert template["output_parser"] == {
"required": False, "required": False,
@ -39,6 +40,7 @@ def test_prompt_template(client: TestClient):
"type": "BaseOutputParser", "type": "BaseOutputParser",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["partial_variables"] == { assert template["partial_variables"] == {
"required": False, "required": False,
@ -50,6 +52,7 @@ def test_prompt_template(client: TestClient):
"type": "code", "type": "code",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["template"] == { assert template["template"] == {
"required": True, "required": True,
@ -61,6 +64,7 @@ def test_prompt_template(client: TestClient):
"type": "prompt", "type": "prompt",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["template_format"] == { assert template["template_format"] == {
"required": False, "required": False,
@ -73,6 +77,7 @@ def test_prompt_template(client: TestClient):
"type": "str", "type": "str",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["validate_template"] == { assert template["validate_template"] == {
"required": False, "required": False,
@ -85,85 +90,7 @@ def test_prompt_template(client: TestClient):
"type": "bool", "type": "bool",
"list": False, "list": False,
"advanced": False, "advanced": False,
} "info": "",
def test_few_shot_prompt_template(client: TestClient):
response = client.get("api/v1/all")
assert response.status_code == 200
json_response = response.json()
prompts = json_response["prompts"]
prompt = prompts["FewShotPromptTemplate"]
template = prompt["template"]
# Test other fields in the template similar to PromptTemplate
assert template["examples"] == {
"required": False,
"placeholder": "",
"show": True,
"multiline": True,
"password": False,
"name": "examples",
"type": "prompt",
"list": True,
"advanced": False,
}
assert template["example_selector"] == {
"required": False,
"placeholder": "",
"show": False,
"multiline": False,
"password": False,
"name": "example_selector",
"type": "BaseExampleSelector",
"list": False,
"advanced": False,
}
assert template["example_prompt"] == {
"required": True,
"placeholder": "",
"show": True,
"multiline": False,
"password": False,
"name": "example_prompt",
"type": "PromptTemplate",
"list": False,
"advanced": False,
}
assert template["suffix"] == {
"required": True,
"placeholder": "",
"show": True,
"multiline": True,
"password": False,
"name": "suffix",
"type": "prompt",
"list": False,
"advanced": False,
}
assert template["example_separator"] == {
"required": False,
"placeholder": "",
"show": False,
"multiline": False,
"value": "\n\n",
"password": False,
"name": "example_separator",
"type": "str",
"list": False,
"advanced": False,
}
assert template["prefix"] == {
"required": False,
"placeholder": "",
"show": True,
"multiline": True,
"value": "",
"password": False,
"name": "prefix",
"type": "prompt",
"list": False,
"advanced": False,
} }
@ -185,6 +112,7 @@ def test_zero_shot_prompt(client: TestClient):
"type": "prompt", "type": "prompt",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["suffix"] == { assert template["suffix"] == {
"required": True, "required": True,
@ -197,6 +125,7 @@ def test_zero_shot_prompt(client: TestClient):
"type": "prompt", "type": "prompt",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }
assert template["format_instructions"] == { assert template["format_instructions"] == {
"required": True, "required": True,
@ -209,4 +138,5 @@ def test_zero_shot_prompt(client: TestClient):
"type": "prompt", "type": "prompt",
"list": False, "list": False,
"advanced": False, "advanced": False,
"info": "",
} }