Merge branch 'db' into merge/mp
This commit is contained in:
commit
3d69d76c6f
63 changed files with 833 additions and 646 deletions
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
|
@ -35,7 +35,7 @@
|
|||
"name": "Debug Frontend",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:3000/*",
|
||||
"url": "http://localhost:3000/",
|
||||
"webRoot": "${workspaceRoot}/src/frontend"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -51,7 +51,6 @@ build_and_run:
|
|||
rm -rf dist
|
||||
make build && poetry run pip install dist/*.tar.gz && poetry run langflow
|
||||
|
||||
|
||||
build_frontend:
|
||||
cd src/frontend && CI='' npm run build
|
||||
cp -r src/frontend/build src/backend/langflow/frontend
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
from importlib import metadata
|
||||
from langflow.cache import cache_manager
|
||||
from langflow.processing.process import load_flow_from_json
|
||||
|
||||
try:
|
||||
__version__ = metadata.version(__package__)
|
||||
except metadata.PackageNotFoundError:
|
||||
# Case where package metadata is not available.
|
||||
__version__ = ""
|
||||
del metadata # optional, avoids polluting the results of dir(__package__)
|
||||
|
||||
__all__ = ["load_flow_from_json", "cache_manager"]
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from langflow.main import create_app
|
|||
from langflow.settings import settings
|
||||
from langflow.utils.logger import configure, logger
|
||||
import webbrowser
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
|
|
@ -88,6 +88,10 @@ def serve(
|
|||
timeout: int = typer.Option(60, help="Worker timeout in seconds."),
|
||||
port: int = typer.Option(7860, help="Port to listen on."),
|
||||
config: str = typer.Option("config.yaml", help="Path to the configuration file."),
|
||||
# .env file param
|
||||
env_file: Path = typer.Option(
|
||||
".env", help="Path to the .env file containing environment variables."
|
||||
),
|
||||
log_level: str = typer.Option("critical", help="Logging level."),
|
||||
log_file: Path = typer.Option("logs/langflow.log", help="Path to the log file."),
|
||||
jcloud: bool = typer.Option(False, help="Deploy on Jina AI Cloud"),
|
||||
|
|
@ -106,11 +110,27 @@ def serve(
|
|||
):
|
||||
"""
|
||||
Run the Langflow server.
|
||||
|
||||
Args:
|
||||
host (str): Host to bind the server to.
|
||||
workers (int): Number of worker processes.
|
||||
timeout (int): Worker timeout in seconds.
|
||||
port (int): Port to listen on.
|
||||
config (str): Path to the configuration file.
|
||||
env_file (Path): Path to the .env file containing environment variables.
|
||||
log_level (str): Logging level.
|
||||
log_file (Path): Path to the log file.
|
||||
jcloud (bool): Deploy on Jina AI Cloud.
|
||||
dev (bool): Run in development mode (may contain bugs).
|
||||
path (str): Path to the frontend directory containing build files. This is for development purposes only.
|
||||
open_browser (bool): Open the browser after starting the server.
|
||||
"""
|
||||
|
||||
if jcloud:
|
||||
return serve_on_jcloud()
|
||||
|
||||
load_dotenv(env_file)
|
||||
|
||||
configure(log_level=log_level, log_file=log_file)
|
||||
update_settings(config, dev=dev, database_url=database_url)
|
||||
app = create_app()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
from langflow.database.models.flow import Flow
|
||||
from langflow.processing.process import process_graph_cached, process_tweaks
|
||||
from langflow.utils.logger import logger
|
||||
from importlib.metadata import version
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi.security import HTTPBearer
|
||||
|
|
@ -70,4 +69,6 @@ async def predict_flow(
|
|||
# get endpoint to return version of langflow
|
||||
@router.get("/version")
|
||||
def get_version():
|
||||
return {"version": version("langflow")}
|
||||
from langflow import __version__
|
||||
|
||||
return {"version": __version__}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ chains:
|
|||
- MidJourneyPromptChain
|
||||
- TimeTravelGuideChain
|
||||
- SQLDatabaseChain
|
||||
- RetrievalQA
|
||||
- RetrievalQAWithSourcesChain
|
||||
- ConversationalRetrievalChain
|
||||
- CombineDocsChain
|
||||
documentloaders:
|
||||
- AirbyteJSONLoader
|
||||
- CoNLLULoader
|
||||
|
|
@ -53,7 +57,7 @@ llms:
|
|||
# - AzureOpenAI
|
||||
# - AzureChatOpenAI
|
||||
- ChatOpenAI
|
||||
- LlamaCpp
|
||||
- LlamaCpp
|
||||
- CTransformers
|
||||
- Cohere
|
||||
- Anthropic
|
||||
|
|
@ -69,7 +73,7 @@ prompts:
|
|||
- ZeroShotPrompt
|
||||
textsplitters:
|
||||
- CharacterTextSplitter
|
||||
# - RecursiveCharacterTextSplitter
|
||||
- RecursiveCharacterTextSplitter
|
||||
# - LatexTextSplitter
|
||||
# - PythonCodeTextSplitter
|
||||
toolkits:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ from langflow.template import frontend_node
|
|||
|
||||
# These should always be instantiated
|
||||
CUSTOM_NODES = {
|
||||
"prompts": {"ZeroShotPrompt": frontend_node.prompts.ZeroShotPromptNode()},
|
||||
"prompts": {
|
||||
"ZeroShotPrompt": frontend_node.prompts.ZeroShotPromptNode(),
|
||||
},
|
||||
"tools": {
|
||||
"PythonFunctionTool": frontend_node.tools.PythonFunctionToolNode(),
|
||||
"PythonFunction": frontend_node.tools.PythonFunctionNode(),
|
||||
|
|
@ -23,6 +25,7 @@ CUSTOM_NODES = {
|
|||
"SeriesCharacterChain": frontend_node.chains.SeriesCharacterChainNode(),
|
||||
"TimeTravelGuideChain": frontend_node.chains.TimeTravelGuideChainNode(),
|
||||
"MidJourneyPromptChain": frontend_node.chains.MidJourneyPromptChainNode(),
|
||||
"load_qa_chain": frontend_node.chains.CombineDocsChainNode(),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ class Graph:
|
|||
self._edges = edges
|
||||
self._build_graph()
|
||||
|
||||
@classmethod
|
||||
@classmethod
|
||||
def from_payload(cls, payload: Dict) -> "Graph":
|
||||
"""
|
||||
|
|
@ -43,7 +42,9 @@ class Graph:
|
|||
edges = payload["edges"]
|
||||
return cls(nodes, edges)
|
||||
except KeyError as exc:
|
||||
raise ValueError("Invalid payload") from exc
|
||||
raise ValueError(
|
||||
f"Invalid payload. Expected keys 'nodes' and 'edges'. Found {list(payload.keys())}"
|
||||
) from exc
|
||||
|
||||
def _build_graph(self) -> None:
|
||||
"""Builds the graph from the nodes and edges."""
|
||||
|
|
|
|||
|
|
@ -49,8 +49,11 @@ class Vertex:
|
|||
|
||||
template_dict = self.data["node"]["template"]
|
||||
self.vertex_type = (
|
||||
self.data["type"] if "Tool" not in self.output else template_dict["_type"]
|
||||
self.data["type"]
|
||||
if "Tool" not in self.output or template_dict["_type"].islower()
|
||||
else template_dict["_type"]
|
||||
)
|
||||
|
||||
if self.base_type is None:
|
||||
for base_type, value in ALL_TYPES_DICT.items():
|
||||
if self.vertex_type in value:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
from abc import ABC
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from langchain import LLMChain
|
||||
|
|
@ -33,24 +32,7 @@ from langchain.memory.chat_memory import BaseChatMemory
|
|||
from langchain.sql_database import SQLDatabase
|
||||
from langchain.tools.python.tool import PythonAstREPLTool
|
||||
from langchain.tools.sql_database.prompt import QUERY_CHECKER
|
||||
|
||||
|
||||
class CustomAgentExecutor(AgentExecutor, ABC):
|
||||
"""Custom agent executor"""
|
||||
|
||||
@staticmethod
|
||||
def function_name():
|
||||
return "CustomAgentExecutor"
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def run(self, *args, **kwargs):
|
||||
return super().run(*args, **kwargs)
|
||||
from langflow.interface.base import CustomAgentExecutor
|
||||
|
||||
|
||||
class JsonAgent(CustomAgentExecutor):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict, List, Optional, Type, Union
|
||||
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.agents import AgentExecutor
|
||||
from pydantic import BaseModel
|
||||
|
||||
from langflow.template.field.base import TemplateField
|
||||
|
|
@ -81,5 +82,42 @@ class LangChainTypeCreator(BaseModel, ABC):
|
|||
)
|
||||
|
||||
signature.add_extra_fields()
|
||||
signature.add_extra_base_classes()
|
||||
|
||||
return signature
|
||||
|
||||
|
||||
class CustomChain(Chain, ABC):
|
||||
"""Custom chain"""
|
||||
|
||||
@staticmethod
|
||||
def function_name():
|
||||
return "CustomChain"
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def run(self, *args, **kwargs):
|
||||
return super().run(*args, **kwargs)
|
||||
|
||||
|
||||
class CustomAgentExecutor(AgentExecutor, ABC):
|
||||
"""Custom chain"""
|
||||
|
||||
@staticmethod
|
||||
def function_name():
|
||||
return "CustomChain"
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def run(self, *args, **kwargs):
|
||||
return super().run(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
from typing import Dict, List, Optional, Type
|
||||
from typing import Any, Dict, List, Optional, Type
|
||||
|
||||
from langflow.custom.customs import get_custom_nodes
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.interface.custom_lists import chain_type_to_cls_dict
|
||||
from langflow.interface.importing.utils import import_class
|
||||
from langflow.settings import settings
|
||||
from langflow.template.frontend_node.chains import ChainFrontendNode
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.utils.util import build_template_from_class
|
||||
from langflow.utils.util import build_template_from_class, build_template_from_method
|
||||
from langchain import chains
|
||||
|
||||
# Assuming necessary imports for Field, Template, and FrontendNode classes
|
||||
|
||||
|
|
@ -18,10 +19,16 @@ class ChainCreator(LangChainTypeCreator):
|
|||
def frontend_node_class(self) -> Type[ChainFrontendNode]:
|
||||
return ChainFrontendNode
|
||||
|
||||
#! We need to find a better solution for this
|
||||
from_method_nodes = {"ConversationalRetrievalChain": "from_llm"}
|
||||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
if self.type_dict is None:
|
||||
self.type_dict = chain_type_to_cls_dict
|
||||
self.type_dict: dict[str, Any] = {
|
||||
chain_name: import_class(f"langchain.chains.{chain_name}")
|
||||
for chain_name in chains.__all__
|
||||
}
|
||||
from langflow.interface.chains.custom import CUSTOM_CHAINS
|
||||
|
||||
self.type_dict.update(CUSTOM_CHAINS)
|
||||
|
|
@ -37,20 +44,32 @@ class ChainCreator(LangChainTypeCreator):
|
|||
try:
|
||||
if name in get_custom_nodes(self.type_name).keys():
|
||||
return get_custom_nodes(self.type_name)[name]
|
||||
elif name in self.from_method_nodes.keys():
|
||||
return build_template_from_method(
|
||||
name,
|
||||
type_to_cls_dict=self.type_to_loader_dict,
|
||||
method_name=self.from_method_nodes[name],
|
||||
add_function=True,
|
||||
)
|
||||
return build_template_from_class(
|
||||
name, self.type_to_loader_dict, add_function=True
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise ValueError("Chain not found") from exc
|
||||
raise ValueError(f"Chain {name} not found: {exc}") from exc
|
||||
except AttributeError as exc:
|
||||
logger.error(f"Chain {name} not loaded: {exc}")
|
||||
return None
|
||||
|
||||
def to_list(self) -> List[str]:
|
||||
custom_chains = list(get_custom_nodes("chains").keys())
|
||||
default_chains = list(self.type_to_loader_dict.keys())
|
||||
|
||||
return default_chains + custom_chains
|
||||
names = []
|
||||
for _, chain in self.type_to_loader_dict.items():
|
||||
chain_name = (
|
||||
chain.function_name()
|
||||
if hasattr(chain, "function_name")
|
||||
else chain.__name__
|
||||
)
|
||||
names.append(chain_name)
|
||||
return names
|
||||
|
||||
|
||||
chain_creator = ChainCreator()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
from typing import Dict, Optional, Type
|
||||
from typing import Dict, Optional, Type, Union
|
||||
|
||||
from langchain.chains import ConversationChain
|
||||
from langchain.memory.buffer import ConversationBufferMemory
|
||||
from langchain.schema import BaseMemory
|
||||
from langflow.interface.base import CustomChain
|
||||
from pydantic import Field, root_validator
|
||||
|
||||
from langchain.chains.question_answering import load_qa_chain
|
||||
from langflow.interface.utils import extract_input_variables_from_prompt
|
||||
from langchain.base_language import BaseLanguageModel
|
||||
|
||||
DEFAULT_SUFFIX = """"
|
||||
Current conversation:
|
||||
|
|
@ -14,7 +16,7 @@ Human: {input}
|
|||
{ai_prefix}"""
|
||||
|
||||
|
||||
class BaseCustomChain(ConversationChain):
|
||||
class BaseCustomConversationChain(ConversationChain):
|
||||
"""BaseCustomChain is a chain you can use to have a conversation with a custom character."""
|
||||
|
||||
template: Optional[str]
|
||||
|
|
@ -47,7 +49,7 @@ class BaseCustomChain(ConversationChain):
|
|||
return values
|
||||
|
||||
|
||||
class SeriesCharacterChain(BaseCustomChain):
|
||||
class SeriesCharacterChain(BaseCustomConversationChain):
|
||||
"""SeriesCharacterChain is a chain you can use to have a conversation with a character from a series."""
|
||||
|
||||
character: str
|
||||
|
|
@ -66,7 +68,7 @@ Human: {input}
|
|||
"""Default memory store."""
|
||||
|
||||
|
||||
class MidJourneyPromptChain(BaseCustomChain):
|
||||
class MidJourneyPromptChain(BaseCustomConversationChain):
|
||||
"""MidJourneyPromptChain is a chain you can use to generate new MidJourney prompts."""
|
||||
|
||||
template: Optional[
|
||||
|
|
@ -84,7 +86,7 @@ class MidJourneyPromptChain(BaseCustomChain):
|
|||
AI:""" # noqa: E501
|
||||
|
||||
|
||||
class TimeTravelGuideChain(BaseCustomChain):
|
||||
class TimeTravelGuideChain(BaseCustomConversationChain):
|
||||
template: Optional[
|
||||
str
|
||||
] = """I want you to act as my time travel guide. You are helpful and creative. I will provide you with the historical period or future time I want to visit and you will suggest the best events, sights, or people to experience. Provide the suggestions and any necessary information.
|
||||
|
|
@ -94,7 +96,26 @@ class TimeTravelGuideChain(BaseCustomChain):
|
|||
AI:""" # noqa: E501
|
||||
|
||||
|
||||
CUSTOM_CHAINS: Dict[str, Type[ConversationChain]] = {
|
||||
class CombineDocsChain(CustomChain):
|
||||
"""Implementation of initialize_agent function"""
|
||||
|
||||
@staticmethod
|
||||
def function_name():
|
||||
return "load_qa_chain"
|
||||
|
||||
@classmethod
|
||||
def initialize(cls, llm: BaseLanguageModel, chain_type: str):
|
||||
return load_qa_chain(llm=llm, chain_type=chain_type)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def run(self, *args, **kwargs):
|
||||
return super().run(*args, **kwargs)
|
||||
|
||||
|
||||
CUSTOM_CHAINS: Dict[str, Type[Union[ConversationChain, CustomChain]]] = {
|
||||
"CombineDocsChain": CombineDocsChain,
|
||||
"SeriesCharacterChain": SeriesCharacterChain,
|
||||
"MidJourneyPromptChain": MidJourneyPromptChain,
|
||||
"TimeTravelGuideChain": TimeTravelGuideChain,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import inspect
|
|||
from typing import Any
|
||||
|
||||
from langchain import (
|
||||
chains,
|
||||
document_loaders,
|
||||
embeddings,
|
||||
llms,
|
||||
|
|
@ -15,6 +14,8 @@ from langchain.chat_models import AzureChatOpenAI, ChatOpenAI
|
|||
from langchain.chat_models import ChatAnthropic
|
||||
|
||||
from langflow.interface.importing.utils import import_class
|
||||
from langflow.interface.agents.custom import CUSTOM_AGENTS
|
||||
from langflow.interface.chains.custom import CUSTOM_CHAINS
|
||||
|
||||
## LLMs
|
||||
llm_type_to_cls_dict = llms.type_to_cls_dict
|
||||
|
|
@ -22,11 +23,6 @@ llm_type_to_cls_dict["anthropic-chat"] = ChatAnthropic # type: ignore
|
|||
llm_type_to_cls_dict["azure-chat"] = AzureChatOpenAI # type: ignore
|
||||
llm_type_to_cls_dict["openai-chat"] = ChatOpenAI # type: ignore
|
||||
|
||||
## Chains
|
||||
chain_type_to_cls_dict: dict[str, Any] = {
|
||||
chain_name: import_class(f"langchain.chains.{chain_name}")
|
||||
for chain_name in chains.__all__
|
||||
}
|
||||
|
||||
## Toolkits
|
||||
toolkit_type_to_loader_dict: dict[str, Any] = {
|
||||
|
|
@ -73,3 +69,6 @@ documentloaders_type_to_cls_dict: dict[str, Any] = {
|
|||
textsplitter_type_to_cls_dict: dict[str, Any] = dict(
|
||||
inspect.getmembers(text_splitter, inspect.isclass)
|
||||
)
|
||||
|
||||
# merge CUSTOM_AGENTS and CUSTOM_CHAINS
|
||||
CUSTOM_NODES = {**CUSTOM_AGENTS, **CUSTOM_CHAINS} # type: ignore
|
||||
|
|
|
|||
|
|
@ -1,30 +1,20 @@
|
|||
from typing import Dict, List, Optional
|
||||
from typing import Dict, List, Optional, Type
|
||||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode
|
||||
from langflow.interface.custom_lists import documentloaders_type_to_cls_dict
|
||||
from langflow.settings import settings
|
||||
from langflow.utils.logger import logger
|
||||
from langflow.utils.util import build_template_from_class
|
||||
|
||||
|
||||
def build_file_path_template(
|
||||
suffixes: list, fileTypes: list, name: str = "file_path"
|
||||
) -> Dict:
|
||||
"""Build a file path template for a document loader."""
|
||||
return {
|
||||
"type": "file",
|
||||
"required": True,
|
||||
"show": True,
|
||||
"name": name,
|
||||
"value": "",
|
||||
"suffixes": suffixes,
|
||||
"fileTypes": fileTypes,
|
||||
}
|
||||
|
||||
|
||||
class DocumentLoaderCreator(LangChainTypeCreator):
|
||||
type_name: str = "documentloaders"
|
||||
|
||||
@property
|
||||
def frontend_node_class(self) -> Type[DocumentLoaderFrontNode]:
|
||||
return DocumentLoaderFrontNode
|
||||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
return documentloaders_type_to_cls_dict
|
||||
|
|
@ -32,106 +22,7 @@ class DocumentLoaderCreator(LangChainTypeCreator):
|
|||
def get_signature(self, name: str) -> Optional[Dict]:
|
||||
"""Get the signature of a document loader."""
|
||||
try:
|
||||
signature = build_template_from_class(
|
||||
name, documentloaders_type_to_cls_dict
|
||||
)
|
||||
|
||||
file_path_templates = {
|
||||
"AirbyteJSONLoader": build_file_path_template(
|
||||
suffixes=[".json"], fileTypes=["json"]
|
||||
),
|
||||
"CoNLLULoader": build_file_path_template(
|
||||
suffixes=[".csv"], fileTypes=["csv"]
|
||||
),
|
||||
"CSVLoader": build_file_path_template(
|
||||
suffixes=[".csv"], fileTypes=["csv"]
|
||||
),
|
||||
"UnstructuredEmailLoader": build_file_path_template(
|
||||
suffixes=[".eml"], fileTypes=["eml"]
|
||||
),
|
||||
"EverNoteLoader": build_file_path_template(
|
||||
suffixes=[".xml"], fileTypes=["xml"]
|
||||
),
|
||||
"FacebookChatLoader": build_file_path_template(
|
||||
suffixes=[".json"], fileTypes=["json"]
|
||||
),
|
||||
"GutenbergLoader": build_file_path_template(
|
||||
suffixes=[".txt"], fileTypes=["txt"]
|
||||
),
|
||||
"BSHTMLLoader": build_file_path_template(
|
||||
suffixes=[".html"], fileTypes=["html"]
|
||||
),
|
||||
"UnstructuredHTMLLoader": build_file_path_template(
|
||||
suffixes=[".html"], fileTypes=["html"]
|
||||
),
|
||||
"UnstructuredImageLoader": build_file_path_template(
|
||||
suffixes=[".jpg", ".jpeg", ".png", ".gif", ".bmp"],
|
||||
fileTypes=["jpg", "jpeg", "png", "gif", "bmp"],
|
||||
),
|
||||
"UnstructuredMarkdownLoader": build_file_path_template(
|
||||
suffixes=[".md"], fileTypes=["md"]
|
||||
),
|
||||
"PyPDFLoader": build_file_path_template(
|
||||
suffixes=[".pdf"], fileTypes=["pdf"]
|
||||
),
|
||||
"UnstructuredPowerPointLoader": build_file_path_template(
|
||||
suffixes=[".pptx", ".ppt"], fileTypes=["pptx", "ppt"]
|
||||
),
|
||||
"SRTLoader": build_file_path_template(
|
||||
suffixes=[".srt"], fileTypes=["srt"]
|
||||
),
|
||||
"TelegramChatLoader": build_file_path_template(
|
||||
suffixes=[".json"], fileTypes=["json"]
|
||||
),
|
||||
"TextLoader": build_file_path_template(
|
||||
suffixes=[".txt"], fileTypes=["txt"]
|
||||
),
|
||||
"UnstructuredWordDocumentLoader": build_file_path_template(
|
||||
suffixes=[".docx", ".doc"], fileTypes=["docx", "doc"]
|
||||
),
|
||||
"SlackDirectoryLoader": build_file_path_template(
|
||||
suffixes=[".zip"], fileTypes=["zip"]
|
||||
),
|
||||
}
|
||||
|
||||
if name in file_path_templates:
|
||||
signature["template"]["file_path"] = file_path_templates[name]
|
||||
elif name in {
|
||||
"WebBaseLoader",
|
||||
"AZLyricsLoader",
|
||||
"CollegeConfidentialLoader",
|
||||
"HNLoader",
|
||||
"IFixitLoader",
|
||||
"IMSDbLoader",
|
||||
}:
|
||||
signature["template"]["web_path"] = {
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"show": True,
|
||||
"name": "web_path",
|
||||
"value": "",
|
||||
"display_name": "Web Page",
|
||||
}
|
||||
elif name in {"GitbookLoader"}:
|
||||
signature["template"]["web_page"] = {
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"show": True,
|
||||
"name": "web_page",
|
||||
"value": "",
|
||||
"display_name": "Web Page",
|
||||
}
|
||||
elif name in {"ReadTheDocsLoader", "NotionDirectoryLoader"}:
|
||||
signature["template"]["path"] = {
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"show": True,
|
||||
"name": "path",
|
||||
"value": "",
|
||||
"display_name": "Web Page",
|
||||
}
|
||||
|
||||
return signature
|
||||
return build_template_from_class(name, documentloaders_type_to_cls_dict)
|
||||
except ValueError as exc:
|
||||
raise ValueError(f"Documment Loader {name} not found") from exc
|
||||
except AttributeError as exc:
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@ from langchain.chains.loading import load_chain_from_config
|
|||
from langchain.llms.loading import load_llm_from_config
|
||||
from pydantic import ValidationError
|
||||
|
||||
from langflow.interface.agents.custom import CUSTOM_AGENTS
|
||||
from langflow.interface.custom_lists import CUSTOM_NODES
|
||||
from langflow.interface.importing.utils import get_function, import_by_type
|
||||
from langflow.interface.toolkits.base import toolkits_creator
|
||||
from langflow.interface.chains.base import chain_creator
|
||||
from langflow.interface.types import get_type_list
|
||||
from langflow.interface.utils import load_file_into_dict
|
||||
from langflow.utils import util, validate
|
||||
|
|
@ -31,10 +32,11 @@ def instantiate_class(node_type: str, base_type: str, params: Dict) -> Any:
|
|||
"""Instantiate class from module type and key, and params"""
|
||||
params = convert_params_to_sets(params)
|
||||
params = convert_kwargs(params)
|
||||
if node_type in CUSTOM_AGENTS:
|
||||
custom_agent = CUSTOM_AGENTS.get(node_type)
|
||||
if custom_agent:
|
||||
return custom_agent.initialize(**params)
|
||||
if node_type in CUSTOM_NODES:
|
||||
if custom_node := CUSTOM_NODES.get(node_type):
|
||||
if hasattr(custom_node, "initialize"):
|
||||
return custom_node.initialize(**params)
|
||||
return custom_node(**params)
|
||||
|
||||
class_object = import_by_type(_type=base_type, name=node_type)
|
||||
return instantiate_based_on_type(class_object, base_type, node_type, params)
|
||||
|
|
@ -78,10 +80,24 @@ def instantiate_based_on_type(class_object, base_type, node_type, params):
|
|||
return instantiate_textsplitter(class_object, params)
|
||||
elif base_type == "utilities":
|
||||
return instantiate_utility(node_type, class_object, params)
|
||||
elif base_type == "chains":
|
||||
return instantiate_chains(node_type, class_object, params)
|
||||
else:
|
||||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_chains(node_type, class_object, params):
|
||||
if "retriever" in params and hasattr(params["retriever"], "as_retriever"):
|
||||
params["retriever"] = params["retriever"].as_retriever()
|
||||
if node_type in chain_creator.from_method_nodes:
|
||||
method = chain_creator.from_method_nodes[node_type]
|
||||
if class_method := getattr(class_object, method, None):
|
||||
return class_method(**params)
|
||||
raise ValueError(f"Method {method} not found in {class_object}")
|
||||
|
||||
return class_object(**params)
|
||||
|
||||
|
||||
def instantiate_agent(class_object, params):
|
||||
return load_agent_executor(class_object, params)
|
||||
|
||||
|
|
@ -142,6 +158,14 @@ def instantiate_vectorstore(class_object, params):
|
|||
"The source you provided did not load correctly or was empty."
|
||||
"This may cause an error in the vectorstore."
|
||||
)
|
||||
# Chroma requires all metadata values to not be None
|
||||
if class_object.__name__ == "Chroma":
|
||||
for doc in params["documents"]:
|
||||
if doc.metadata is None:
|
||||
doc.metadata = {}
|
||||
for key, value in doc.metadata.items():
|
||||
if value is None:
|
||||
doc.metadata[key] = ""
|
||||
return class_object.from_documents(**params)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -71,7 +71,3 @@ Human: {input}
|
|||
CUSTOM_PROMPTS: Dict[str, Type[BaseCustomPrompt]] = {
|
||||
"SeriesCharacterPrompt": SeriesCharacterPrompt
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
prompt = SeriesCharacterPrompt(character="Harry Potter", series="Harry Potter")
|
||||
print(prompt.template)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Dict, List, Optional
|
||||
from typing import Dict, List, Optional, Type
|
||||
|
||||
from langflow.interface.base import LangChainTypeCreator
|
||||
from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode
|
||||
from langflow.interface.custom_lists import textsplitter_type_to_cls_dict
|
||||
from langflow.settings import settings
|
||||
from langflow.utils.logger import logger
|
||||
|
|
@ -10,6 +11,10 @@ from langflow.utils.util import build_template_from_class
|
|||
class TextSplitterCreator(LangChainTypeCreator):
|
||||
type_name: str = "textsplitters"
|
||||
|
||||
@property
|
||||
def frontend_node_class(self) -> Type[TextSplittersFrontendNode]:
|
||||
return TextSplittersFrontendNode
|
||||
|
||||
@property
|
||||
def type_to_loader_dict(self) -> Dict:
|
||||
return textsplitter_type_to_cls_dict
|
||||
|
|
@ -17,43 +22,7 @@ class TextSplitterCreator(LangChainTypeCreator):
|
|||
def get_signature(self, name: str) -> Optional[Dict]:
|
||||
"""Get the signature of a text splitter."""
|
||||
try:
|
||||
signature = build_template_from_class(name, textsplitter_type_to_cls_dict)
|
||||
|
||||
signature["template"]["documents"] = {
|
||||
"type": "BaseLoader",
|
||||
"required": True,
|
||||
"show": True,
|
||||
"name": "documents",
|
||||
}
|
||||
|
||||
signature["template"]["separator"] = {
|
||||
"type": "str",
|
||||
"required": True,
|
||||
"show": True,
|
||||
"value": ".",
|
||||
"name": "separator",
|
||||
"display_name": "Separator",
|
||||
}
|
||||
|
||||
signature["template"]["chunk_size"] = {
|
||||
"type": "int",
|
||||
"required": True,
|
||||
"show": True,
|
||||
"value": 1000,
|
||||
"name": "chunk_size",
|
||||
"display_name": "Chunk Size",
|
||||
}
|
||||
|
||||
signature["template"]["chunk_overlap"] = {
|
||||
"type": "int",
|
||||
"required": True,
|
||||
"show": True,
|
||||
"value": 200,
|
||||
"name": "chunk_overlap",
|
||||
"display_name": "Chunk Overlap",
|
||||
}
|
||||
|
||||
return signature
|
||||
return build_template_from_class(name, textsplitter_type_to_cls_dict)
|
||||
except ValueError as exc:
|
||||
raise ValueError(f"Text Splitter {name} not found") from exc
|
||||
except AttributeError as exc:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from langflow.template.frontend_node import (
|
|||
prompts,
|
||||
tools,
|
||||
vectorstores,
|
||||
documentloaders,
|
||||
textsplitters,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
|
@ -18,4 +20,6 @@ __all__ = [
|
|||
"llms",
|
||||
"prompts",
|
||||
"vectorstores",
|
||||
"documentloaders",
|
||||
"textsplitters",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -154,9 +154,9 @@ class CSVAgentNode(FrontendNode):
|
|||
|
||||
|
||||
class InitializeAgentNode(FrontendNode):
|
||||
name: str = "initialize_agent"
|
||||
name: str = "AgentInitializer"
|
||||
template: Template = Template(
|
||||
type_name="initailize_agent",
|
||||
type_name="initialize_agent",
|
||||
fields=[
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class FrontendNode(BaseModel):
|
|||
description: str
|
||||
base_classes: List[str]
|
||||
name: str = ""
|
||||
display_name: str = ""
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
|
|
@ -21,12 +22,16 @@ class FrontendNode(BaseModel):
|
|||
"template": self.template.to_dict(self.format_field),
|
||||
"description": self.description,
|
||||
"base_classes": self.base_classes,
|
||||
}
|
||||
"display_name": self.display_name or self.name,
|
||||
},
|
||||
}
|
||||
|
||||
def add_extra_fields(self) -> None:
|
||||
pass
|
||||
|
||||
def add_extra_base_classes(self) -> None:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
"""Formats a given field based on its attributes and value."""
|
||||
|
|
|
|||
|
|
@ -2,10 +2,24 @@ from typing import Optional
|
|||
|
||||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
from langflow.template.frontend_node.constants import QA_CHAIN_TYPES
|
||||
from langflow.template.template.base import Template
|
||||
|
||||
|
||||
class ChainFrontendNode(FrontendNode):
|
||||
def add_extra_fields(self) -> None:
|
||||
if self.template.type_name == "ConversationalRetrievalChain":
|
||||
# add memory
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="BaseChatMemory",
|
||||
required=False,
|
||||
show=True,
|
||||
name="memory",
|
||||
advanced=False,
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
FrontendNode.format_field(field, name)
|
||||
|
|
@ -155,3 +169,41 @@ class MidJourneyPromptChainNode(FrontendNode):
|
|||
"ConversationChain",
|
||||
"MidJourneyPromptChain",
|
||||
]
|
||||
|
||||
|
||||
class CombineDocsChainNode(FrontendNode):
|
||||
name: str = "CombineDocsChain"
|
||||
template: Template = Template(
|
||||
type_name="load_qa_chain",
|
||||
fields=[
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
is_list=True,
|
||||
show=True,
|
||||
multiline=False,
|
||||
options=QA_CHAIN_TYPES,
|
||||
value=QA_CHAIN_TYPES[0],
|
||||
name="chain_type",
|
||||
advanced=False,
|
||||
),
|
||||
TemplateField(
|
||||
field_type="BaseLanguageModel",
|
||||
required=True,
|
||||
show=True,
|
||||
name="llm",
|
||||
display_name="LLM",
|
||||
advanced=False,
|
||||
),
|
||||
],
|
||||
)
|
||||
description: str = """Construct a zero shot agent from an LLM and tools."""
|
||||
base_classes: list[str] = ["BaseCombineDocumentsChain", "function"]
|
||||
|
||||
def to_dict(self):
|
||||
return super().to_dict()
|
||||
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
# do nothing and don't return anything
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -30,3 +30,5 @@ You are a good listener and you can talk about anything.
|
|||
"""
|
||||
|
||||
HUMAN_PROMPT = "{input}"
|
||||
|
||||
QA_CHAIN_TYPES = ["stuff", "map_reduce", "map_rerank", "refine"]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
|
||||
|
||||
def build_template(
|
||||
suffixes: list, fileTypes: list, name: str = "file_path"
|
||||
) -> TemplateField:
|
||||
"""Build a template field for a document loader."""
|
||||
return TemplateField(
|
||||
field_type="file",
|
||||
required=True,
|
||||
show=True,
|
||||
name=name,
|
||||
value="",
|
||||
suffixes=suffixes,
|
||||
fileTypes=fileTypes,
|
||||
)
|
||||
|
||||
|
||||
class DocumentLoaderFrontNode(FrontendNode):
|
||||
file_path_templates = {
|
||||
"AirbyteJSONLoader": build_template(suffixes=[".json"], fileTypes=["json"]),
|
||||
"CoNLLULoader": build_template(suffixes=[".csv"], fileTypes=["csv"]),
|
||||
"CSVLoader": build_template(suffixes=[".csv"], fileTypes=["csv"]),
|
||||
"UnstructuredEmailLoader": build_template(suffixes=[".eml"], fileTypes=["eml"]),
|
||||
"EverNoteLoader": build_template(suffixes=[".xml"], fileTypes=["xml"]),
|
||||
"FacebookChatLoader": build_template(suffixes=[".json"], fileTypes=["json"]),
|
||||
"GutenbergLoader": build_template(suffixes=[".txt"], fileTypes=["txt"]),
|
||||
"BSHTMLLoader": build_template(suffixes=[".html"], fileTypes=["html"]),
|
||||
"UnstructuredHTMLLoader": build_template(
|
||||
suffixes=[".html"], fileTypes=["html"]
|
||||
),
|
||||
"UnstructuredImageLoader": build_template(
|
||||
suffixes=[".jpg", ".jpeg", ".png", ".gif", ".bmp"],
|
||||
fileTypes=["jpg", "jpeg", "png", "gif", "bmp"],
|
||||
),
|
||||
"UnstructuredMarkdownLoader": build_template(
|
||||
suffixes=[".md"], fileTypes=["md"]
|
||||
),
|
||||
"PyPDFLoader": build_template(suffixes=[".pdf"], fileTypes=["pdf"]),
|
||||
"UnstructuredPowerPointLoader": build_template(
|
||||
suffixes=[".pptx", ".ppt"], fileTypes=["pptx", "ppt"]
|
||||
),
|
||||
"SRTLoader": build_template(suffixes=[".srt"], fileTypes=["srt"]),
|
||||
"TelegramChatLoader": build_template(suffixes=[".json"], fileTypes=["json"]),
|
||||
"TextLoader": build_template(suffixes=[".txt"], fileTypes=["txt"]),
|
||||
"UnstructuredWordDocumentLoader": build_template(
|
||||
suffixes=[".docx", ".doc"], fileTypes=["docx", "doc"]
|
||||
),
|
||||
}
|
||||
|
||||
def add_extra_fields(self) -> None:
|
||||
name = None
|
||||
if self.template.type_name in self.file_path_templates:
|
||||
self.template.add_field(self.file_path_templates[self.template.type_name])
|
||||
elif self.template.type_name in {
|
||||
"WebBaseLoader",
|
||||
"AZLyricsLoader",
|
||||
"CollegeConfidentialLoader",
|
||||
"HNLoader",
|
||||
"IFixitLoader",
|
||||
"IMSDbLoader",
|
||||
}:
|
||||
name = "web_path"
|
||||
elif self.template.type_name in {"GitbookLoader"}:
|
||||
name = "web_page"
|
||||
elif self.template.type_name in {"ReadTheDocsLoader"}:
|
||||
name = "path"
|
||||
if name:
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
show=True,
|
||||
name=name,
|
||||
value="",
|
||||
display_name="Web Page",
|
||||
)
|
||||
)
|
||||
|
|
@ -5,6 +5,20 @@ from langflow.template.frontend_node.base import FrontendNode
|
|||
|
||||
|
||||
class MemoryFrontendNode(FrontendNode):
|
||||
#! Needs testing
|
||||
def add_extra_fields(self) -> None:
|
||||
# add return_messages field
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="bool",
|
||||
required=False,
|
||||
show=True,
|
||||
name="return_messages",
|
||||
advanced=False,
|
||||
value=False,
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
FrontendNode.format_field(field, name)
|
||||
|
|
@ -18,3 +32,7 @@ class MemoryFrontendNode(FrontendNode):
|
|||
field.value = 10
|
||||
field.display_name = "Memory Size"
|
||||
field.password = False
|
||||
if field.name == "return_messages":
|
||||
field.required = False
|
||||
field.show = True
|
||||
field.advanced = False
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ class BasePromptFrontendNode(FrontendNode):
|
|||
class ZeroShotPromptNode(BasePromptFrontendNode):
|
||||
name: str = "ZeroShotPrompt"
|
||||
template: Template = Template(
|
||||
type_name="zero_shot",
|
||||
type_name="ZeroShotPrompt",
|
||||
fields=[
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
|
|
|
|||
49
src/backend/langflow/template/frontend_node/textsplitters.py
Normal file
49
src/backend/langflow/template/frontend_node/textsplitters.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
from langflow.template.field.base import TemplateField
|
||||
from langflow.template.frontend_node.base import FrontendNode
|
||||
|
||||
|
||||
class TextSplittersFrontendNode(FrontendNode):
|
||||
def add_extra_fields(self) -> None:
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="BaseLoader",
|
||||
required=True,
|
||||
show=True,
|
||||
name="documents",
|
||||
)
|
||||
)
|
||||
name = "separator"
|
||||
if self.template.type_name == "CharacterTextSplitter":
|
||||
name = "separator"
|
||||
elif self.template.type_name == "RecursiveCharacterTextSplitter":
|
||||
name = "separators"
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
show=True,
|
||||
value=".",
|
||||
name=name,
|
||||
display_name="Separator",
|
||||
)
|
||||
)
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="int",
|
||||
required=True,
|
||||
show=True,
|
||||
value=1000,
|
||||
name="chunk_size",
|
||||
display_name="Chunk Size",
|
||||
)
|
||||
)
|
||||
self.template.add_field(
|
||||
TemplateField(
|
||||
field_type="int",
|
||||
required=True,
|
||||
show=True,
|
||||
value=200,
|
||||
name="chunk_overlap",
|
||||
display_name="Chunk Overlap",
|
||||
)
|
||||
)
|
||||
|
|
@ -108,7 +108,7 @@ class PythonFunctionToolNode(FrontendNode):
|
|||
class PythonFunctionNode(FrontendNode):
|
||||
name: str = "PythonFunction"
|
||||
template: Template = Template(
|
||||
type_name="python_function",
|
||||
type_name="PythonFunction",
|
||||
fields=[
|
||||
TemplateField(
|
||||
field_type="code",
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ class VectorStoreFrontendNode(FrontendNode):
|
|||
|
||||
self.template.add_field(extra_field)
|
||||
|
||||
def add_extra_base_classes(self) -> None:
|
||||
self.base_classes.append("BaseRetriever")
|
||||
|
||||
@staticmethod
|
||||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
FrontendNode.format_field(field, name)
|
||||
|
|
|
|||
10
src/frontend/package-lock.json
generated
10
src/frontend/package-lock.json
generated
|
|
@ -53,6 +53,7 @@
|
|||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"shadcn-ui": "^0.1.3",
|
||||
"short-unique-id": "^4.4.4",
|
||||
"switch": "^0.0.0",
|
||||
"table": "^6.8.1",
|
||||
"tailwind-merge": "^1.13.0",
|
||||
|
|
@ -8642,6 +8643,15 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/short-unique-id": {
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/short-unique-id/-/short-unique-id-4.4.4.tgz",
|
||||
"integrity": "sha512-oLF1NCmtbiTWl2SqdXZQbo5KM1b7axdp0RgQLq8qCBBLoq+o3A5wmLrNM6bZIh54/a8BJ3l69kTXuxwZ+XCYuw==",
|
||||
"bin": {
|
||||
"short-unique-id": "bin/short-unique-id",
|
||||
"suid": "bin/short-unique-id"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.4",
|
||||
"dev": true,
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"shadcn-ui": "^0.1.3",
|
||||
"short-unique-id": "^4.4.4",
|
||||
"switch": "^0.0.0",
|
||||
"table": "^6.8.1",
|
||||
"tailwind-merge": "^1.13.0",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import React from "react";
|
|||
import { nodeColors } from "../../../../utils";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import { PopUpContext } from "../../../../contexts/popUpContext";
|
||||
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
||||
|
||||
export default function ParameterComponent({
|
||||
left,
|
||||
|
|
@ -39,7 +40,6 @@ export default function ParameterComponent({
|
|||
}: ParameterComponentType) {
|
||||
const ref = useRef(null);
|
||||
const refHtml = useRef(null);
|
||||
const refData = useRef(null);
|
||||
const updateNodeInternals = useUpdateNodeInternals();
|
||||
const [position, setPosition] = useState(0);
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
|
|
@ -64,11 +64,11 @@ export default function ParameterComponent({
|
|||
const { reactFlowInstance } = useContext(typesContext);
|
||||
let disabled =
|
||||
reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false;
|
||||
|
||||
const [myData, setMyData] = useState(useContext(typesContext).data);
|
||||
|
||||
useEffect(() => {
|
||||
const groupedObj = groupByFamily(myData, tooltipTitle);
|
||||
|
||||
refHtml.current = groupedObj.map((item, i) => (
|
||||
<span
|
||||
key={i}
|
||||
|
|
@ -108,169 +108,166 @@ export default function ParameterComponent({
|
|||
}, [tooltipTitle]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
ref={ref}
|
||||
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
|
||||
ref={ref}
|
||||
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")}>
|
||||
{title}
|
||||
<span className="text-red-600">{required ? " *" : ""}</span>
|
||||
</div>
|
||||
{left &&
|
||||
(type === "str" ||
|
||||
type === "bool" ||
|
||||
type === "float" ||
|
||||
type === "code" ||
|
||||
type === "prompt" ||
|
||||
type === "file" ||
|
||||
type === "int") ? (
|
||||
<></>
|
||||
) : (
|
||||
<ShadTooltip
|
||||
delayDuration={0}
|
||||
content={refHtml.current}
|
||||
side={left ? "left" : "right"}
|
||||
open={refHtml?.current?.length > 0}
|
||||
>
|
||||
{title}
|
||||
<span className="text-red-600">{required ? " *" : ""}</span>
|
||||
</div>
|
||||
{left &&
|
||||
(type === "str" ||
|
||||
type === "bool" ||
|
||||
type === "float" ||
|
||||
type === "code" ||
|
||||
type === "prompt" ||
|
||||
type === "file" ||
|
||||
type === "int") ? (
|
||||
<></>
|
||||
) : (
|
||||
<ShadTooltip
|
||||
delayDuration={0}
|
||||
content={refHtml.current}
|
||||
side={left ? "left" : "right"}
|
||||
open={refHtml?.current?.length > 0}
|
||||
>
|
||||
<Handle
|
||||
type={left ? "target" : "source"}
|
||||
position={left ? Position.Left : Position.Right}
|
||||
id={id}
|
||||
isValidConnection={(connection) =>
|
||||
isValidConnection(connection, reactFlowInstance)
|
||||
}
|
||||
className={classNames(
|
||||
left ? "-ml-0.5 " : "-mr-0.5 ",
|
||||
"w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"
|
||||
)}
|
||||
style={{
|
||||
borderColor: color,
|
||||
top: position,
|
||||
}}
|
||||
></Handle>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
|
||||
{left === true &&
|
||||
type === "str" &&
|
||||
!data.node.template[name].options ? (
|
||||
<div className="mt-2 w-full">
|
||||
{data.node.template[name].list ? (
|
||||
<InputListComponent
|
||||
disabled={disabled}
|
||||
value={
|
||||
!data.node.template[name].value ||
|
||||
data.node.template[name].value === ""
|
||||
? [""]
|
||||
: data.node.template[name].value
|
||||
}
|
||||
onChange={(t: string[]) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : data.node.template[name].multiline ? (
|
||||
<TextAreaComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<InputComponent
|
||||
disabled={disabled}
|
||||
disableCopyPaste={true}
|
||||
password={data.node.template[name].password ?? false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
<Handle
|
||||
type={left ? "target" : "source"}
|
||||
position={left ? Position.Left : Position.Right}
|
||||
id={id}
|
||||
isValidConnection={(connection) =>
|
||||
isValidConnection(connection, reactFlowInstance)
|
||||
}
|
||||
className={classNames(
|
||||
left ? "-ml-0.5 " : "-mr-0.5 ",
|
||||
"w-3 h-3 rounded-full border-2 bg-white dark:bg-gray-800"
|
||||
)}
|
||||
</div>
|
||||
) : left === true && type === "bool" ? (
|
||||
<div className="mt-2">
|
||||
<ToggleComponent
|
||||
style={{
|
||||
borderColor: color,
|
||||
top: position,
|
||||
}}
|
||||
></Handle>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
|
||||
{left === true &&
|
||||
type === "str" &&
|
||||
!data.node.template[name].options ? (
|
||||
<div className="mt-2 w-full">
|
||||
{data.node.template[name].list ? (
|
||||
<InputListComponent
|
||||
disabled={disabled}
|
||||
enabled={enabled}
|
||||
setEnabled={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
setEnabled(t);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : left === true && type === "float" ? (
|
||||
<div className="mt-2 w-full">
|
||||
<FloatComponent
|
||||
disabled={disabled}
|
||||
disableCopyPaste={true}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : left === true &&
|
||||
type === "str" &&
|
||||
data.node.template[name].options ? (
|
||||
<div className="w-full">
|
||||
<Dropdown
|
||||
options={data.node.template[name].options}
|
||||
onSelect={(newValue) =>
|
||||
(data.node.template[name].value = newValue)
|
||||
value={
|
||||
!data.node.template[name].value ||
|
||||
data.node.template[name].value === ""
|
||||
? [""]
|
||||
: data.node.template[name].value
|
||||
}
|
||||
value={data.node.template[name].value ?? "Choose an option"}
|
||||
></Dropdown>
|
||||
</div>
|
||||
) : left === true && type === "code" ? (
|
||||
<CodeAreaComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : left === true && type === "file" ? (
|
||||
<InputFileComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
fileTypes={data.node.template[name].fileTypes}
|
||||
suffixes={data.node.template[name].suffixes}
|
||||
onFileChange={(t: string) => {
|
||||
data.node.template[name].content = t;
|
||||
}}
|
||||
></InputFileComponent>
|
||||
) : left === true && type === "int" ? (
|
||||
<div className="mt-2 w-full">
|
||||
<IntComponent
|
||||
onChange={(t: string[]) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : data.node.template[name].multiline ? (
|
||||
<TextAreaComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<InputComponent
|
||||
disabled={disabled}
|
||||
disableCopyPaste={true}
|
||||
password={data.node.template[name].password ?? false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : left === true && type === "prompt" ? (
|
||||
<PromptAreaComponent
|
||||
)}
|
||||
</div>
|
||||
) : left === true && type === "bool" ? (
|
||||
<div className="mt-2">
|
||||
<ToggleShadComponent
|
||||
disabled={disabled}
|
||||
enabled={enabled}
|
||||
setEnabled={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
setEnabled(t);
|
||||
}}
|
||||
size="large"
|
||||
/>
|
||||
</div>
|
||||
) : left === true && type === "float" ? (
|
||||
<div className="mt-2 w-full">
|
||||
<FloatComponent
|
||||
disabled={disabled}
|
||||
disableCopyPaste={true}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
) : left === true &&
|
||||
type === "str" &&
|
||||
data.node.template[name].options ? (
|
||||
<div className="w-full">
|
||||
<Dropdown
|
||||
options={data.node.template[name].options}
|
||||
onSelect={(newValue) =>
|
||||
(data.node.template[name].value = newValue)
|
||||
}
|
||||
value={data.node.template[name].value ?? "Choose an option"}
|
||||
></Dropdown>
|
||||
</div>
|
||||
) : left === true && type === "code" ? (
|
||||
<CodeAreaComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : left === true && type === "file" ? (
|
||||
<InputFileComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
fileTypes={data.node.template[name].fileTypes}
|
||||
suffixes={data.node.template[name].suffixes}
|
||||
onFileChange={(t: string) => {
|
||||
data.node.template[name].content = t;
|
||||
}}
|
||||
></InputFileComponent>
|
||||
) : left === true && type === "int" ? (
|
||||
<div className="mt-2 w-full">
|
||||
<IntComponent
|
||||
disabled={disabled}
|
||||
disableCopyPaste={true}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : left === true && type === "prompt" ? (
|
||||
<PromptAreaComponent
|
||||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,3 @@
|
|||
import {
|
||||
BugAntIcon,
|
||||
Cog6ToothIcon,
|
||||
InformationCircleIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
EllipsisHorizontalCircleIcon,
|
||||
ExclamationCircleIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
|
||||
import {
|
||||
classNames,
|
||||
nodeColors,
|
||||
|
|
@ -33,6 +20,7 @@ import { NodeToolbar } from "reactflow";
|
|||
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
|
||||
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import { postValidateNode } from "../../controllers/API";
|
||||
export default function GenericNode({
|
||||
data,
|
||||
selected,
|
||||
|
|
@ -62,17 +50,13 @@ export default function GenericNode({
|
|||
const validateNode = useCallback(
|
||||
debounce(async () => {
|
||||
try {
|
||||
const response = await fetch(`/validate/node/${data.id}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(reactFlowInstance.toObject()),
|
||||
});
|
||||
const response = await postValidateNode(
|
||||
data.id,
|
||||
reactFlowInstance.toObject()
|
||||
);
|
||||
|
||||
if (response.status === 200) {
|
||||
let jsonResponse = await response.json();
|
||||
let jsonResponseParsed = await JSON.parse(jsonResponse);
|
||||
let jsonResponseParsed = await JSON.parse(response.data);
|
||||
setValidationStatus(jsonResponseParsed);
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -150,11 +134,10 @@ export default function GenericNode({
|
|||
"Validating..."
|
||||
) : (
|
||||
<div className="max-h-96 overflow-auto">
|
||||
{validationStatus.params
|
||||
.split("\n")
|
||||
.map((line, index) => (
|
||||
<div key={index}>{line}</div>
|
||||
))}
|
||||
{validationStatus.params ||
|
||||
""
|
||||
.split("\n")
|
||||
.map((line, index) => <div key={index}>{line}</div>)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -191,7 +174,7 @@ export default function GenericNode({
|
|||
</div>
|
||||
|
||||
<div className="h-full w-full py-5 text-gray-800">
|
||||
<div className="w-full px-5 pb-3 text-sm text-gray-500 dark:text-gray-300">
|
||||
<div className="w-full px-5 pb-3 text-sm text-muted-foreground">
|
||||
{data.node.description}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { useState, ChangeEvent } from "react";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { Label } from "../ui/label";
|
||||
import { Input } from "../ui/input";
|
||||
import { Textarea } from "../../components/ui/textarea";
|
||||
import { Label } from "../../components/ui/label";
|
||||
import { Input } from "../../components/ui/input";
|
||||
|
||||
type InputProps = {
|
||||
name: string | null;
|
||||
|
|
@ -62,7 +62,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
)}
|
||||
</div>
|
||||
<Input
|
||||
className="mt-2"
|
||||
className="mt-2 font-normal"
|
||||
onChange={handleNameChange}
|
||||
type="text"
|
||||
name="name"
|
||||
|
|
@ -80,7 +80,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
|
|||
onChange={handleDescriptionChange}
|
||||
value={description ?? ""}
|
||||
placeholder="Flow description"
|
||||
className="max-h-[100px] mt-2"
|
||||
className="max-h-[100px] mt-2 font-normal"
|
||||
rows={3}
|
||||
/>
|
||||
</Label>
|
||||
|
|
@ -40,7 +40,7 @@ export const CardComponent = ({
|
|||
</div>
|
||||
{onDelete && (
|
||||
<button onClick={onDelete}>
|
||||
<Trash2 className="w-5 text-primary opacity-0 group-hover:opacity-100 transition-all" />
|
||||
<Trash2 className="w-4 h-4 text-primary opacity-0 group-hover:opacity-100 transition-all" />
|
||||
</button>
|
||||
)}
|
||||
</CardTitle>
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ export default function Dropdown({
|
|||
className={({ active }) =>
|
||||
classNames(
|
||||
active
|
||||
? "text-white bg-slate-400 dark:bg-white dark:text-gray-500"
|
||||
? " bg-accent dark:bg-white dark:text-gray-500"
|
||||
: "",
|
||||
editNode
|
||||
? "relative cursor-default select-none py-0.5 pl-3 pr-12 dark:text-gray-300 dark:bg-gray-800"
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export const MenuBar = ({ flows, tabId }) => {
|
|||
return (
|
||||
<div className="flex gap-2 items-center">
|
||||
<Link to="/">
|
||||
<ChevronLeft className="w-5" />
|
||||
<ChevronLeft className="w-4" />
|
||||
</Link>
|
||||
<div className="flex items-center font-medium text-sm rounded-md py-1 px-1.5 gap-0.5">
|
||||
<DropdownMenu>
|
||||
|
|
@ -62,7 +62,7 @@ export const MenuBar = ({ flows, tabId }) => {
|
|||
openPopUp(<FlowSettingsModal />);
|
||||
}}
|
||||
>
|
||||
<Settings2 className="w-4 h-4 mr-2" />
|
||||
<Settings2 className="w-4 h-4 mr-2 dark:text-gray-300" />
|
||||
Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
|
|
@ -70,7 +70,7 @@ export const MenuBar = ({ flows, tabId }) => {
|
|||
undo();
|
||||
}}
|
||||
>
|
||||
<Undo className="w-4 h-4 mr-2" />
|
||||
<Undo className="w-4 h-4 mr-2 dark:text-gray-300" />
|
||||
Undo
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
|
|
@ -78,7 +78,7 @@ export const MenuBar = ({ flows, tabId }) => {
|
|||
redo();
|
||||
}}
|
||||
>
|
||||
<Redo className="w-4 h-4 mr-2" />
|
||||
<Redo className="w-4 h-4 mr-2 dark:text-gray-300" />
|
||||
Redo
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ export default function Header() {
|
|||
Join The Community
|
||||
</a>
|
||||
</Button>
|
||||
<button
|
||||
{/* <button
|
||||
className="text-gray-600 hover:text-gray-500 dark:text-gray-300 dark:hover:text-gray-200"
|
||||
onClick={() => {
|
||||
setDark(!dark);
|
||||
|
|
@ -83,7 +83,7 @@ export default function Header() {
|
|||
) : (
|
||||
<MoonIcon className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
</button> */}
|
||||
<button
|
||||
className="text-gray-600 hover:text-gray-500 dark:text-gray-300 dark:hover:text-gray-200 relative"
|
||||
onClick={(event: React.MouseEvent<HTMLElement>) => {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ export default function IntComponent({
|
|||
if (disableCopyPaste) setDisableCopyPaste(false);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
console.log(event);
|
||||
// console.log(event);
|
||||
if (
|
||||
event.key !== "Backspace" &&
|
||||
event.key !== "Enter" &&
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { classNames } from "../../utils";
|
||||
import { useEffect } from "react";
|
||||
import { ToggleComponentType } from "../../types/components";
|
||||
import { Switch } from "../ui/switch";
|
||||
|
|
@ -7,17 +6,36 @@ export default function ToggleShadComponent({
|
|||
enabled,
|
||||
setEnabled,
|
||||
disabled,
|
||||
size,
|
||||
}: ToggleComponentType) {
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
setEnabled(false);
|
||||
}
|
||||
}, [disabled, setEnabled]);
|
||||
let scaleX, scaleY;
|
||||
switch (size) {
|
||||
case "small":
|
||||
scaleX = 0.6;
|
||||
scaleY = 0.6;
|
||||
break;
|
||||
case "medium":
|
||||
scaleX = 0.8;
|
||||
scaleY = 0.8;
|
||||
break;
|
||||
case "large":
|
||||
scaleX = 1;
|
||||
scaleY = 1;
|
||||
break;
|
||||
default:
|
||||
scaleX = 1;
|
||||
scaleY = 1;
|
||||
}
|
||||
return (
|
||||
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
|
||||
<Switch
|
||||
style={{
|
||||
transform: "scaleX(0.6) scaleY(0.6)",
|
||||
transform: `scaleX(${scaleX}) scaleY(${scaleY})`,
|
||||
}}
|
||||
className="data-[state=unchecked]:bg-slate-500"
|
||||
checked={enabled}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
// src/constants.tsx
|
||||
|
||||
import { FlowType } from "./types/flow";
|
||||
import { buildTweaks } from "./utils";
|
||||
|
||||
/**
|
||||
* The base text for subtitle of Export Dialog (Toolbar)
|
||||
* @constant
|
||||
|
|
@ -51,10 +54,21 @@ export const TEXT_DIALOG_SUBTITLE = "Edit your text.";
|
|||
* @param {string} flowId - The id of the flow
|
||||
* @returns {string} - The python code
|
||||
*/
|
||||
export const getPythonApiCode = (flowId: string): string => {
|
||||
export const getPythonApiCode = (flow: FlowType): string => {
|
||||
const flowId = flow.id;
|
||||
|
||||
// create a dictionary of node ids and the values is an empty dictionary
|
||||
// flow.data.nodes.forEach((node) => {
|
||||
// node.data.id
|
||||
// }
|
||||
const tweaks = buildTweaks(flow);
|
||||
return `import requests
|
||||
|
||||
BASE_API_URL = "${window.location.protocol}//${window.location.host}/predict"
|
||||
BASE_API_URL = "${window.location.protocol}//${
|
||||
window.location.host
|
||||
}/ap1/v1/predict"
|
||||
FLOW_ID = "${flowId}"
|
||||
TWEAKS = ${JSON.stringify(tweaks, null, 2)}
|
||||
|
||||
def run_flow(message: str, flow_id: str, tweaks: dict = None) -> dict:
|
||||
"""
|
||||
|
|
@ -76,32 +90,35 @@ def run_flow(message: str, flow_id: str, tweaks: dict = None) -> dict:
|
|||
return response.json()
|
||||
|
||||
# Setup any tweaks you want to apply to the flow
|
||||
tweaks = {} # {"nodeId": {"key": "value"}, "nodeId2": {"key": "value"}}
|
||||
|
||||
FLOW_ID = "${flowId}"
|
||||
|
||||
print(run_flow("Your message", flow_id=FLOW_ID, tweaks=tweaks))`;
|
||||
print(run_flow("Your message", flow_id=FLOW_ID, tweaks=TWEAKS))`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to get the curl code for the API
|
||||
* @param {string} flowId - The id of the flow
|
||||
* @returns {string} - The curl code
|
||||
*/
|
||||
export const getCurlCode = (flowId: string): string => {
|
||||
export const getCurlCode = (flow: FlowType): string => {
|
||||
const flowId = flow.id;
|
||||
const tweaks = buildTweaks(flow);
|
||||
return `curl -X POST \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "Authorization: Bearer ${flowId}" \\
|
||||
-d '{"message": "Your message"}' \\
|
||||
${window.location.protocol}//${window.location.host}/predict`;
|
||||
${window.location.protocol}//${
|
||||
window.location.host
|
||||
}/api/v1/predict/${flowId} \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
-d '{"message": "Your message", "tweaks": ${JSON.stringify(
|
||||
tweaks,
|
||||
null,
|
||||
2
|
||||
)}}'`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function to get the python code for the API
|
||||
* @param {string} flowName - The name of the flow
|
||||
* @returns {string} - The python code
|
||||
*/
|
||||
export const getPythonCode = (flowName: string): string => {
|
||||
export const getPythonCode = (flow: FlowType): string => {
|
||||
const flowName = flow.name;
|
||||
return `from langflow import load_flow_from_json
|
||||
|
||||
flow = load_flow_from_json("${flowName}.json")
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { updateIds, updateTemplate } from "../utils";
|
|||
import { alertContext } from "./alertContext";
|
||||
import { typesContext } from "./typesContext";
|
||||
import { APITemplateType } from "../types/api";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import ShortUniqueId from "short-unique-id";
|
||||
import { addEdge } from "reactflow";
|
||||
import {
|
||||
readFlowsFromDatabase,
|
||||
|
|
@ -23,14 +23,17 @@ import {
|
|||
} from "../controllers/API";
|
||||
import _ from "lodash";
|
||||
|
||||
const uid = new ShortUniqueId({ length: 5 });
|
||||
|
||||
const TabsContextInitialValue: TabsContextType = {
|
||||
save: () => {},
|
||||
tabId: "",
|
||||
setTabId: (index: string) => {},
|
||||
flows: [],
|
||||
removeFlow: (id: string) => {},
|
||||
addFlow: async (flowData?: any) => "",
|
||||
updateFlow: (newFlow: FlowType) => {},
|
||||
incrementNodeId: () => uuidv4(),
|
||||
incrementNodeId: () => uid(),
|
||||
downloadFlow: (flow: FlowType) => {},
|
||||
downloadFlows: () => {},
|
||||
uploadFlows: () => {},
|
||||
|
|
@ -41,7 +44,7 @@ const TabsContextInitialValue: TabsContextType = {
|
|||
lastCopiedSelection: null,
|
||||
setLastCopiedSelection: (selection: any) => {},
|
||||
|
||||
getNodeId: () => "",
|
||||
getNodeId: (nodeType: string) => "",
|
||||
paste: (
|
||||
selection: { nodes: any; edges: any },
|
||||
position: { x: number; y: number; paneX?: number; paneY?: number }
|
||||
|
|
@ -54,18 +57,46 @@ export const TabsContext = createContext<TabsContextType>(
|
|||
|
||||
export function TabsProvider({ children }: { children: ReactNode }) {
|
||||
const { setErrorData, setNoticeData } = useContext(alertContext);
|
||||
|
||||
const [tabId, setTabId] = useState("");
|
||||
const [flows, setFlows] = useState([]);
|
||||
const [id, setId] = useState(uuidv4());
|
||||
|
||||
const [flows, setFlows] = useState<Array<FlowType>>([]);
|
||||
const [id, setId] = useState(uid());
|
||||
const { templates, reactFlowInstance } = useContext(typesContext);
|
||||
const [lastCopiedSelection, setLastCopiedSelection] = useState(null);
|
||||
|
||||
const newNodeId = useRef(uuidv4());
|
||||
const newNodeId = useRef(uid());
|
||||
function incrementNodeId() {
|
||||
newNodeId.current = uuidv4();
|
||||
newNodeId.current = uid();
|
||||
return newNodeId.current;
|
||||
}
|
||||
|
||||
function save() {
|
||||
// added clone deep to avoid mutating the original object
|
||||
let Saveflows = _.cloneDeep(flows);
|
||||
if (Saveflows.length !== 0) {
|
||||
Saveflows.forEach((flow) => {
|
||||
if (flow.data && flow.data?.nodes)
|
||||
flow.data?.nodes.forEach((node) => {
|
||||
// console.log(node.data.type);
|
||||
//looking for file fields to prevent saving the content and breaking the flow for exceeding the the data limite for local storage
|
||||
Object.keys(node.data.node.template).forEach((key) => {
|
||||
// console.log(node.data.node.template[key].type);
|
||||
if (node.data.node.template[key].type === "file") {
|
||||
// console.log(node.data.node.template[key]);
|
||||
node.data.node.template[key].content = null;
|
||||
node.data.node.template[key].value = "";
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
window.localStorage.setItem(
|
||||
"tabsData",
|
||||
JSON.stringify({ tabId, flows: Saveflows, id })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// function loadCookie(cookie: string) {
|
||||
// if (cookie && Object.keys(templates).length > 0) {
|
||||
// let cookieObject: LangFlowState = JSON.parse(cookie);
|
||||
|
|
@ -206,10 +237,11 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
function hardReset() {
|
||||
newNodeId.current = uuidv4();
|
||||
newNodeId.current = uid();
|
||||
setTabId("");
|
||||
|
||||
setFlows([]);
|
||||
setId(uuidv4());
|
||||
setId(uid());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -249,8 +281,8 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
}
|
||||
|
||||
function getNodeId() {
|
||||
return `dndnode_` + incrementNodeId();
|
||||
function getNodeId(nodeType: string) {
|
||||
return nodeType + "-" + incrementNodeId();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -262,6 +294,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
// create a file input
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = ".json";
|
||||
// add a change event listener to the file input
|
||||
input.onchange = (e: Event) => {
|
||||
// check if the file type is application/json
|
||||
|
|
@ -343,9 +376,9 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
? { x: position.paneX + position.x, y: position.paneY + position.y }
|
||||
: reactFlowInstance.project({ x: position.x, y: position.y });
|
||||
|
||||
selectionInstance.nodes.forEach((n) => {
|
||||
selectionInstance.nodes.forEach((n: NodeType) => {
|
||||
// Generate a unique node ID
|
||||
let newId = getNodeId();
|
||||
let newId = getNodeId(n.data.type);
|
||||
idsMap[n.id] = newId;
|
||||
|
||||
// Create a new node object
|
||||
|
|
@ -500,7 +533,6 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
const createNewFlow = (flowData, flow) => ({
|
||||
description: flowData.description,
|
||||
name: flow?.name ?? "New Flow",
|
||||
id: uuidv4(),
|
||||
data: flowData.data,
|
||||
});
|
||||
|
||||
|
|
@ -540,6 +572,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
tabId,
|
||||
setTabId,
|
||||
flows,
|
||||
save,
|
||||
incrementNodeId,
|
||||
removeFlow,
|
||||
addFlow,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { PromptTypeAPI, errorsTypeAPI } from "./../../types/api/index";
|
|||
import { APIObjectType, sendAllProps } from "../../types/api/index";
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import { FlowStyleType, FlowType } from "../../types/flow";
|
||||
import { ReactFlowJsonObject } from "reactflow";
|
||||
|
||||
/**
|
||||
* Fetches all objects from the API endpoint.
|
||||
|
|
@ -22,18 +23,19 @@ export async function sendAll(data: sendAllProps) {
|
|||
return await axios.post(`/api/v1/predict`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates code by sending it to an API endpoint.
|
||||
*
|
||||
* @param {string} code - The code to validate.
|
||||
* @returns {Promise<AxiosResponse<errorsTypeAPI>>} A promise that resolves to an AxiosResponse containing the validation results.
|
||||
*/
|
||||
export async function checkCode(
|
||||
export async function postValidateCode(
|
||||
code: string
|
||||
): Promise<AxiosResponse<errorsTypeAPI>> {
|
||||
return await axios.post("/api/v1/validate/code", { code });
|
||||
}
|
||||
|
||||
export async function postValidateNode(
|
||||
nodeId: string,
|
||||
data: any
|
||||
): Promise<AxiosResponse<string>> {
|
||||
return await axios.post(`/api/v1/validate/node/${nodeId}`, { data });
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the prompt for the code block by sending it to an API endpoint.
|
||||
*
|
||||
|
|
@ -75,7 +77,13 @@ export async function getExamples(): Promise<FlowType[]> {
|
|||
* @returns {Promise<any>} The saved flow data.
|
||||
* @throws Will throw an error if saving fails.
|
||||
*/
|
||||
export async function saveFlowToDatabase(newFlow: FlowType): Promise<FlowType> {
|
||||
export async function saveFlowToDatabase(newFlow: {
|
||||
name: string;
|
||||
id: string;
|
||||
data: ReactFlowJsonObject;
|
||||
description: string;
|
||||
style?: FlowStyleType;
|
||||
}): Promise<FlowType> {
|
||||
try {
|
||||
const response = await axios.post("/api/v1/flows/", {
|
||||
name: newFlow.name,
|
||||
|
|
|
|||
|
|
@ -72,47 +72,85 @@
|
|||
|
||||
:root {
|
||||
--background: 0 0% 100%;
|
||||
/* hsl(0 0% 100%) */
|
||||
--foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 30% 98.5%;
|
||||
/* hsl(222 47% 11%) */
|
||||
--muted: 210 40% 98%;
|
||||
/* hsl(210 40% 98%) */
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
/* hsl(215 16% 46%) */
|
||||
--popover: 0 0% 100%;
|
||||
/* hsl(0 0% 100%) */
|
||||
--popover-foreground: 222.2 47.4% 11.2%;
|
||||
/* hsl(222 47% 11%) */
|
||||
--card: 0 0% 100%;
|
||||
/* hsl(0 0% 100%) */
|
||||
--card-foreground: 222.2 47.4% 11.2%;
|
||||
--border: 214.3 31.8% 93.4%;
|
||||
/* hsl(222 47% 11%) */
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
/* hsl(214 32% 91%) */
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--primary: 222.2 37.4% 18%;
|
||||
/* hsl(214 32% 91%) */
|
||||
--primary: 222.2 47.4% 18%;
|
||||
/* hsl(222 47% 18%) */
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 30% 96.1%;
|
||||
/* hsl(210 40% 98%) */
|
||||
--secondary: 210 40% 96.1%;
|
||||
/* hsl(210 40% 96%) */
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--accent: 210 30% 96.1%;
|
||||
--accent-foreground: 222.2 37.4% 11.2%;
|
||||
/* hsl(222 47% 11%) */
|
||||
--accent: 210 40% 96.1%;
|
||||
/* hsl(210 40% 96%) */
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
/* hsl(222 47% 11%) */
|
||||
--destructive: 0 100% 50%;
|
||||
/* hsl(0 100% 50%) */
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
/* hsl(210 40% 98%) */
|
||||
--ring: 215 20.2% 65.1%;
|
||||
/* hsl(215 20% 65%) */
|
||||
--radius: 0.5rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
-background: 224 71% 4%;
|
||||
/* hsl(224 71% 4%) */
|
||||
-foreground: 213 31% 91%;
|
||||
/* hsl(213 31% 91%) */
|
||||
-muted: 223 47% 11%;
|
||||
/* hsl(223 47% 11%) */
|
||||
-muted-foreground: 215.4 16.3% 56.9%;
|
||||
/* hsl(215 16% 56%) */
|
||||
-popover: 224 71% 4%;
|
||||
/* hsl(224 71% 4%) */
|
||||
-popover-foreground: 215 20.2% 65.1%;
|
||||
/* hsl(215 20% 65%) */
|
||||
-card: 224 71% 4%;
|
||||
/* hsl(224 71% 4%) */
|
||||
-card-foreground: 213 31% 91%;
|
||||
/* hsl(213 31% 91%) */
|
||||
-border: 216 34% 17%;
|
||||
/* hsl(216 34% 17%) */
|
||||
-input: 216 34% 17%;
|
||||
/* hsl(216 34% 17%) */
|
||||
-primary: 210 40% 98%;
|
||||
/* hsl(210 40% 98%) */
|
||||
-primary-foreground: 222.2 47.4% 1.2%;
|
||||
/* hsl(222 47% 1%) */
|
||||
-secondary: 222.2 47.4% 11.2%;
|
||||
/* hsl(222 47% 11%) */
|
||||
-secondary-foreground: 210 40% 98%;
|
||||
/* hsl(210 40% 98%) */
|
||||
-accent: 216 34% 17%;
|
||||
/* hsl(216 34% 17%) */
|
||||
-accent-foreground: 210 40% 98%;
|
||||
/* hsl(210 40% 98%) */
|
||||
-destructive: 0 63% 31%;
|
||||
/* hsl(0 63% 31%) */
|
||||
-destructive-foreground: 210 40% 98%;
|
||||
/* hsl(210 40% 98%) */
|
||||
-ring: 216 34% 17%;
|
||||
/* hsl(216 34% 17%) */
|
||||
-radius: 0.5rem;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react";
|
||||
import {
|
||||
XMarkIcon,
|
||||
CommandLineIcon,
|
||||
CodeBracketSquareIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import { IconCheck, IconClipboard } from "@tabler/icons-react";
|
||||
import { CodeBracketSquareIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import "ace-builds/src-noconflict/mode-python";
|
||||
import "ace-builds/src-noconflict/theme-github";
|
||||
|
|
@ -18,13 +14,11 @@ import {
|
|||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "../../components/ui/dialog";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { FlowType } from "../../types/flow";
|
||||
import { FlowType } from "../../types/flow/index";
|
||||
import { getCurlCode, getPythonApiCode, getPythonCode } from "../../constants";
|
||||
import { EXPORT_CODE_DIALOG } from "../../constants";
|
||||
|
||||
|
|
@ -55,10 +49,10 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
|||
}
|
||||
}
|
||||
|
||||
const pythonApiCode = getPythonApiCode(flow.id);
|
||||
const pythonApiCode = getPythonApiCode(flow);
|
||||
|
||||
const curl_code = getCurlCode(flow.id);
|
||||
const pythonCode = getPythonCode(flow.name);
|
||||
const curl_code = getCurlCode(flow);
|
||||
const pythonCode = getPythonCode(flow);
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
|
|
@ -96,8 +90,8 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
|||
<DialogDescription>{EXPORT_CODE_DIALOG}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex flex-col h-full w-full ">
|
||||
<div className="flex px-5 z-10">
|
||||
<div className="flex flex-col h-full w-full overflow-hidden">
|
||||
<div className="flex px-5 z-10 w-full">
|
||||
{tabs.map((tab, index) => (
|
||||
<button
|
||||
key={index}
|
||||
|
|
@ -117,7 +111,7 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
|||
))}
|
||||
</div>
|
||||
<div className=" overflow-hidden px-4 sm:p-4 sm:pb-0 sm:pt-0 w-full h-full rounded-lg border-gray-200 border-[1px] bg-muted dark:bg-gray-800">
|
||||
<div className="items-center mb-2">
|
||||
<div className="items-center mb-2 w-full">
|
||||
<div className="float-right">
|
||||
<button
|
||||
className="flex gap-1.5 items-center rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
|
||||
|
|
@ -133,7 +127,7 @@ export default function ApiModal({ flow }: { flow: FlowType }) {
|
|||
</div>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
className="h-[350px] w-full"
|
||||
className="h-[350px] w-full overflow-auto"
|
||||
language={tabs[activeTab].mode}
|
||||
style={oneDark}
|
||||
customStyle={{ margin: 0 }}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,21 @@
|
|||
import {
|
||||
ChevronDoubleLeftIcon,
|
||||
ChevronDoubleRightIcon,
|
||||
PencilSquareIcon,
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useEffect, useRef, useState } from "react";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import { classNames, limitScrollFieldsModal, nodeIcons } from "../../utils";
|
||||
import { classNames, limitScrollFieldsModal } from "../../utils";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "../../components/ui/table";
|
||||
import { Switch } from "../../components/ui/switch";
|
||||
import ToggleShadComponent from "../../components/toggleShadComponent";
|
||||
import { VariableIcon } from "@heroicons/react/24/outline";
|
||||
import InputListComponent from "../../components/inputListComponent";
|
||||
import TextAreaComponent from "../../components/textAreaComponent";
|
||||
import InputComponent from "../../components/inputComponent";
|
||||
import ToggleComponent from "../../components/toggleComponent";
|
||||
import FloatComponent from "../../components/floatComponent";
|
||||
import Dropdown from "../../components/dropdownComponent";
|
||||
import IntComponent from "../../components/intComponent";
|
||||
|
|
@ -42,8 +33,8 @@ import {
|
|||
DialogTrigger,
|
||||
} from "../../components/ui/dialog";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { EDIT_DIALOG_SUBTITLE } from "../../constants";
|
||||
import { Edit } from "lucide-react";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
|
||||
export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
|
@ -95,14 +86,11 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
|||
<DialogContent className="lg:max-w-[700px] ">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center">
|
||||
<span className="pr-2">Edit Node</span>
|
||||
<Edit
|
||||
className="h-5 w-5 text-gray-800 pl-1 dark:text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="pr-2">{data.type}</span>
|
||||
<Badge variant="secondary">ID: {data.id}</Badge>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{EDIT_DIALOG_SUBTITLE}
|
||||
{data.node?.description}
|
||||
<div className="flex pt-4">
|
||||
<VariableIcon className="w-5 h-5 pe-1 text-gray-700 stroke-2 dark:text-slate-200">
|
||||
|
||||
|
|
@ -151,7 +139,7 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
|||
)
|
||||
.map((n, i) => (
|
||||
<TableRow key={i} className="h-10 dark:border-b-muted">
|
||||
<TableCell className="p-0 text-center text-gray-900 text-xs dark:text-gray-300 text-sm">
|
||||
<TableCell className="p-0 text-center text-gray-900 dark:text-gray-300 text-sm">
|
||||
{data.node.template[n].name
|
||||
? data.node.template[n].name
|
||||
: data.node.template[n].display_name}
|
||||
|
|
@ -206,6 +194,7 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
|||
data.node.template[n].value = e;
|
||||
setEnabled(e);
|
||||
}}
|
||||
size="small"
|
||||
disabled={false}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -299,6 +288,7 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
|||
changeAdvanced(data.node.template[n])
|
||||
}
|
||||
disabled={false}
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
|
|
@ -319,7 +309,7 @@ export default function EditNodeModal({ data }: { data: NodeDataType }) {
|
|||
}}
|
||||
type="submit"
|
||||
>
|
||||
Save changes
|
||||
Save Changes
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default function ChatInput({
|
|||
}}
|
||||
className={classNames(
|
||||
lockChat
|
||||
? " bg-gray-300 text-black dark:bg-gray-700 dark:text-gray-300"
|
||||
? " bg-input text-black dark:bg-gray-700 dark:text-gray-300"
|
||||
: " bg-white-200 text-black dark:bg-gray-900 dark:text-gray-300",
|
||||
"form-input block w-full custom-scroll rounded-md border-gray-300 dark:border-gray-600 pr-10 sm:text-sm" +
|
||||
INPUT_STYLE
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ export default function ChatMessage({
|
|||
className={classNames(
|
||||
"w-full py-2 pl-2 flex",
|
||||
chat.isSend
|
||||
? "bg-white dark:bg-gray-900 "
|
||||
: "bg-gray-200 dark:bg-gray-800"
|
||||
? "bg-background dark:bg-gray-900 "
|
||||
: "bg-input dark:bg-gray-800"
|
||||
)}
|
||||
>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import "ace-builds/src-noconflict/theme-twilight";
|
|||
import "ace-builds/src-noconflict/ext-language_tools";
|
||||
// import "ace-builds/webpack-resolver";
|
||||
import { darkContext } from "../../contexts/darkContext";
|
||||
import { checkCode } from "../../controllers/API";
|
||||
import { postValidateCode } from "../../controllers/API";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import {
|
||||
|
|
@ -81,7 +81,7 @@ export default function CodeAreaModal({
|
|||
<Button
|
||||
className="mt-3"
|
||||
onClick={() => {
|
||||
checkCode(code)
|
||||
postValidateCode(code)
|
||||
.then((apiReturn) => {
|
||||
if (apiReturn.data) {
|
||||
let importsErrors = apiReturn.data.imports.errors;
|
||||
|
|
|
|||
|
|
@ -16,11 +16,8 @@ import {
|
|||
import { Button } from "../../components/ui/button";
|
||||
import { Checkbox } from "../../components/ui/checkbox";
|
||||
import { EXPORT_DIALOG_SUBTITLE } from "../../constants";
|
||||
import EditFlowSettings from "../../components/nameInputComponent";
|
||||
import { Label } from "../../components/ui/label";
|
||||
import { Input } from "../../components/ui/input";
|
||||
import { Textarea } from "../../components/ui/textarea";
|
||||
import { Download } from "lucide-react";
|
||||
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
||||
|
||||
export default function ExportModal() {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
|
@ -57,44 +54,15 @@ export default function ExportModal() {
|
|||
<DialogDescription>{EXPORT_DIALOG_SUBTITLE}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<Label>
|
||||
<span className="font-medium">Name</span>
|
||||
|
||||
<Input
|
||||
className="mt-2 focus-visible:ring-1"
|
||||
onChange={(event) => {
|
||||
if (event.target.value != "") {
|
||||
let newFlow = flows.find((f) => f.id === tabId);
|
||||
newFlow.name = event.target.value;
|
||||
setName(event.target.value);
|
||||
updateFlow(newFlow);
|
||||
} else {
|
||||
setName(event.target.value);
|
||||
}
|
||||
}}
|
||||
type="text"
|
||||
name="name"
|
||||
value={name ?? null}
|
||||
placeholder="File name"
|
||||
id="name"
|
||||
/>
|
||||
</Label>
|
||||
<Label>
|
||||
<span className="font-medium">Description (optional)</span>
|
||||
<Textarea
|
||||
name="description"
|
||||
id="description"
|
||||
onChange={(event) => {
|
||||
let newFlow = flows.find((f) => f.id === tabId);
|
||||
newFlow.description = event.target.value;
|
||||
updateFlow(newFlow);
|
||||
}}
|
||||
value={flows.find((f) => f.id === tabId).description ?? null}
|
||||
placeholder="Flow description"
|
||||
className="max-h-[100px] mt-2 focus-visible:ring-1"
|
||||
rows={3}
|
||||
/>
|
||||
</Label>
|
||||
<EditFlowSettings
|
||||
name={name}
|
||||
description={description}
|
||||
flows={flows}
|
||||
tabId={tabId}
|
||||
setName={setName}
|
||||
setDescription={setDescription}
|
||||
updateFlow={updateFlow}
|
||||
/>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="terms"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
import { Button } from "../../components/ui/button";
|
||||
import { SETTINGS_DIALOG_SUBTITLE } from "../../constants";
|
||||
import { updateFlowInDatabase } from "../../controllers/API";
|
||||
import EditFlowSettings from "../../components/nameInputComponent";
|
||||
import EditFlowSettings from "../../components/EditFlowSettingsComponent";
|
||||
|
||||
export default function FlowSettingsModal() {
|
||||
const [open, setOpen] = useState(true);
|
||||
|
|
@ -52,10 +52,6 @@ export default function FlowSettingsModal() {
|
|||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center">
|
||||
<span className="pr-2">Settings</span>
|
||||
<ArrowDownTrayIcon
|
||||
className="h-6 w-6 text-gray-800 pl-1 dark:text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</DialogTitle>
|
||||
<DialogDescription>{SETTINGS_DIALOG_SUBTITLE}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export default function PromptAreaModal({
|
|||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
|
||||
<div className="fixed inset-0 bg-ring dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
|
|
@ -87,7 +87,7 @@ export default function PromptAreaModal({
|
|||
</Dialog.Title>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-full w-full bg-gray-200 overflow-auto dark:bg-gray-900 p-4 gap-4 flex flex-row justify-center items-center">
|
||||
<div className="h-full w-full bg-accent overflow-auto dark:bg-gray-900 p-4 gap-4 flex flex-row justify-center items-center">
|
||||
<div className="flex h-full w-full">
|
||||
<div className="overflow-hidden px-4 py-5 sm:p-6 w-full h-full rounded-lg bg-white dark:bg-gray-800 shadow">
|
||||
<textarea
|
||||
|
|
|
|||
|
|
@ -1,42 +1,10 @@
|
|||
import {
|
||||
Tabs,
|
||||
TabsList,
|
||||
TabsTrigger,
|
||||
TabsContent,
|
||||
} from "../../components/ui/tabs";
|
||||
import ExtraSidebar from "../../components/ExtraSidebarComponent";
|
||||
import { ReactFlowProvider } from "reactflow";
|
||||
import FlowPage from "../FlowPage";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import {
|
||||
SunIcon,
|
||||
MoonIcon,
|
||||
BellIcon,
|
||||
GithubIcon,
|
||||
Download,
|
||||
Upload,
|
||||
Plus,
|
||||
Home,
|
||||
Users2,
|
||||
GitFork,
|
||||
} from "lucide-react";
|
||||
import { GithubIcon, Users2, GitFork } from "lucide-react";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import AlertDropdown from "../../alerts/alertDropDown";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { darkContext } from "../../contexts/darkContext";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { FaGithub } from "react-icons/fa";
|
||||
|
||||
import _ from "lodash";
|
||||
|
||||
import {
|
||||
getExamples,
|
||||
updateFlowInDatabase,
|
||||
uploadFlowsToDatabase,
|
||||
} from "../../controllers/API";
|
||||
import { MenuBar } from "../../components/headerComponent/components/menuBar";
|
||||
import { getExamples } from "../../controllers/API";
|
||||
import { FlowType } from "../../types/flow";
|
||||
import { CardComponent } from "../../components/cardComponent";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
setEdges(flow?.data?.edges ?? []);
|
||||
if (reactFlowInstance) {
|
||||
setViewport(flow?.data?.viewport ?? { x: 1, y: 0, zoom: 0.5 });
|
||||
reactFlowInstance.fitView();
|
||||
}
|
||||
}, [flow, reactFlowInstance, setEdges, setNodes, setViewport]);
|
||||
//set extra sidebar
|
||||
|
|
@ -213,7 +214,8 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
});
|
||||
|
||||
// Generate a unique node ID
|
||||
let newId = getNodeId();
|
||||
let { type } = data;
|
||||
let newId = getNodeId(type);
|
||||
let newNode: NodeType;
|
||||
|
||||
if (data.type !== "groupNode") {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
import {
|
||||
Bars2Icon,
|
||||
PencilSquareIcon,
|
||||
Square2StackIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Bars2Icon } from "@heroicons/react/24/outline";
|
||||
import DisclosureComponent from "../DisclosureComponent";
|
||||
import {
|
||||
classNames,
|
||||
|
|
@ -11,26 +6,27 @@ import {
|
|||
nodeIcons,
|
||||
nodeNames,
|
||||
} from "../../../../utils";
|
||||
import { useContext, useEffect, useState, useRef } from "react";
|
||||
import { useContext, useState } from "react";
|
||||
import { typesContext } from "../../../../contexts/typesContext";
|
||||
import { APIClassType, APIObjectType } from "../../../../types/api";
|
||||
import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import { Code, Code2, FileDown, FileUp, Import, Save } from "lucide-react";
|
||||
import { Code2, FileDown, FileUp, Save } from "lucide-react";
|
||||
import { PopUpContext } from "../../../../contexts/popUpContext";
|
||||
import ImportModal from "../../../../modals/importModal";
|
||||
import ExportModal from "../../../../modals/exportModal";
|
||||
import ApiModal from "../../../../modals/ApiModal";
|
||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
import { Separator } from "../../../../components/ui/separator";
|
||||
import { alertContext } from "../../../../contexts/alertContext";
|
||||
import { updateFlowInDatabase } from "../../../../controllers/API";
|
||||
import { INPUT_STYLE } from "../../../../constants";
|
||||
import { Input } from "../../../../components/ui/input";
|
||||
import { Separator } from "../../../../components/ui/separator";
|
||||
|
||||
export default function ExtraSidebar() {
|
||||
const { data } = useContext(typesContext);
|
||||
const { openPopUp } = useContext(PopUpContext);
|
||||
const { flows, tabId } = useContext(TabsContext);
|
||||
const { flows, tabId, uploadFlow } = useContext(TabsContext);
|
||||
const { setSuccessData, setErrorData } = useContext(alertContext);
|
||||
const [dataFilter, setFilterData] = useState(data);
|
||||
const [search, setSearch] = useState("");
|
||||
|
|
@ -82,7 +78,8 @@ export default function ExtraSidebar() {
|
|||
<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"
|
||||
onClick={() => {
|
||||
openPopUp(<ImportModal />);
|
||||
// openPopUp(<ImportModal />);
|
||||
uploadFlow();
|
||||
}}
|
||||
>
|
||||
<FileUp className="w-5 h-5 dark:text-gray-300"></FileUp>
|
||||
|
|
@ -132,7 +129,7 @@ export default function ExtraSidebar() {
|
|||
type="text"
|
||||
name="search"
|
||||
id="search"
|
||||
placeholder="Search nodes"
|
||||
placeholder="Search Nodes"
|
||||
className={
|
||||
INPUT_STYLE +
|
||||
"border-1 dark:border-slate-600 dark:border-0.5 dark:ring-0 focus-visible:dark:ring-0 focus-visible:dark:ring-offset-1 rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
|
||||
|
|
|
|||
|
|
@ -1,21 +1,9 @@
|
|||
import React, { useContext, useState } from "react";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
import { EllipsisVerticalIcon } from "@heroicons/react/20/solid";
|
||||
import {
|
||||
Cog6ToothIcon,
|
||||
TrashIcon,
|
||||
PencilSquareIcon,
|
||||
DocumentDuplicateIcon,
|
||||
DocumentPlusIcon,
|
||||
Square2StackIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { useContext, useState } from "react";
|
||||
import { Settings2, Copy, Trash2 } from "lucide-react";
|
||||
import { classNames } from "../../../../utils";
|
||||
import { Fragment } from "react";
|
||||
import NodeModal from "../../../../modals/NodeModal";
|
||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
import { useReactFlow } from "reactflow";
|
||||
import EditNodeModal from "../../../../modals/EditNodeModal";
|
||||
import TooltipReact from "../../../../components/ReactTooltipComponent";
|
||||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
|
||||
const NodeToolbarComponent = (props) => {
|
||||
|
|
@ -48,7 +36,7 @@ const NodeToolbarComponent = (props) => {
|
|||
props.deleteNode(props.data.id);
|
||||
}}
|
||||
>
|
||||
<TrashIcon className="w-5 h-5 dark:text-gray-300"></TrashIcon>
|
||||
<Trash2 className="w-4 h-4 dark:text-gray-300"></Trash2>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
|
||||
|
|
@ -61,7 +49,7 @@ const NodeToolbarComponent = (props) => {
|
|||
)}
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
console.log(reactFlowInstance.getNode(props.data.id));
|
||||
// console.log(reactFlowInstance.getNode(props.data.id));
|
||||
paste(
|
||||
{
|
||||
nodes: [reactFlowInstance.getNode(props.data.id)],
|
||||
|
|
@ -76,7 +64,7 @@ const NodeToolbarComponent = (props) => {
|
|||
);
|
||||
}}
|
||||
>
|
||||
<Square2StackIcon className="w-5 h-5 dark:text-gray-300"></Square2StackIcon>
|
||||
<Copy className="w-4 h-4 dark:text-gray-300"></Copy>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
|
||||
|
|
@ -89,12 +77,12 @@ const NodeToolbarComponent = (props) => {
|
|||
props.openPopUp(<EditNodeModal data={props.data} />);
|
||||
}}
|
||||
>
|
||||
<PencilSquareIcon className="w-5 h-5 dark:text-gray-300"></PencilSquareIcon>
|
||||
<Settings2 className="w-4 h-4 dark:text-gray-300"></Settings2>
|
||||
</button>
|
||||
</ShadTooltip>
|
||||
)}
|
||||
|
||||
{/*
|
||||
{/*
|
||||
<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">
|
||||
<div>
|
||||
|
|
@ -133,7 +121,7 @@ const NodeToolbarComponent = (props) => {
|
|||
"w-full group flex items-center px-4 py-2 text-sm"
|
||||
)}
|
||||
>
|
||||
<PencilSquareIcon
|
||||
<Settings
|
||||
className="mr-3 h-5 w-5 text-gray-400 group-hover:text-gray-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export default function HomePage() {
|
|||
}}
|
||||
>
|
||||
<Download className="w-4 mr-2" />
|
||||
Download Database
|
||||
Download Collection
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
|
|
@ -35,7 +35,7 @@ export default function HomePage() {
|
|||
}}
|
||||
>
|
||||
<Upload className="w-4 mr-2" />
|
||||
Upload Database
|
||||
Upload Collection
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export type ToggleComponentType = {
|
|||
enabled: boolean;
|
||||
setEnabled: (state: boolean) => void;
|
||||
disabled: boolean;
|
||||
size: "small" | "medium" | "large";
|
||||
};
|
||||
export type DropDownComponentType = {
|
||||
value: string;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { FlowType } from "../flow";
|
||||
|
||||
export type TabsContextType = {
|
||||
save: () => void;
|
||||
tabId: string;
|
||||
setTabId: (index: string) => void;
|
||||
flows: Array<FlowType>;
|
||||
|
|
@ -16,7 +17,7 @@ export type TabsContextType = {
|
|||
//disable CopyPaste
|
||||
disableCopyPaste: boolean;
|
||||
setDisableCopyPaste: (value: boolean) => void;
|
||||
getNodeId: () => string;
|
||||
getNodeId: (nodeType: string) => string;
|
||||
paste: (
|
||||
selection: { nodes: any; edges: any },
|
||||
position: { x: number; y: number; paneX?: number; paneY?: number }
|
||||
|
|
|
|||
|
|
@ -16,14 +16,13 @@ import {
|
|||
Squares2X2Icon,
|
||||
Bars3CenterLeftIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Connection, Edge, Node, ReactFlowInstance, addEdge } from "reactflow";
|
||||
import { FlowType, NodeType } from "./types/flow";
|
||||
import { APITemplateType, TemplateVariableType } from "./types/api";
|
||||
import { Connection, Edge, Node, ReactFlowInstance } from "reactflow";
|
||||
import { FlowType, NodeDataType, NodeType } from "./types/flow";
|
||||
import { APITemplateType } from "./types/api";
|
||||
import _ from "lodash";
|
||||
import { ChromaIcon } from "./icons/ChromaIcon";
|
||||
import { AnthropicIcon } from "./icons/Anthropic";
|
||||
import { AirbyteIcon } from "./icons/Airbyte";
|
||||
import { AzIcon } from "./icons/AzLogo";
|
||||
import { BingIcon } from "./icons/Bing";
|
||||
import { CohereIcon } from "./icons/Cohere";
|
||||
import { EvernoteIcon } from "./icons/Evernote";
|
||||
|
|
@ -37,17 +36,9 @@ import { MetaIcon } from "./icons/Meta";
|
|||
import { MidjorneyIcon } from "./icons/Midjorney";
|
||||
import { NotionIcon } from "./icons/Notion";
|
||||
import { OpenAiIcon } from "./icons/OpenAi";
|
||||
import { PowerPointIcon } from "./icons/PowerPoint";
|
||||
import { QDrantIcon } from "./icons/QDrant";
|
||||
import { ReadTheDocsIcon } from "./icons/ReadTheDocs";
|
||||
import { SearxIcon } from "./icons/Searx";
|
||||
import { SlackIcon } from "./icons/Slack";
|
||||
import { WeaviateIcon } from "./icons/Weaviate";
|
||||
import { WikipediaIcon } from "./icons/Wikipedia";
|
||||
import { WolframIcon } from "./icons/Wolfram";
|
||||
import { WordIcon } from "./icons/Word";
|
||||
import { SerperIcon } from "./icons/Serper";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import clsx, { ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
|
|
@ -634,9 +625,9 @@ export function checkUpperWords(str: string) {
|
|||
export function updateIds(newFlow, getNodeId) {
|
||||
let idsMap = {};
|
||||
|
||||
newFlow.nodes.forEach((n) => {
|
||||
newFlow.nodes.forEach((n: NodeType) => {
|
||||
// Generate a unique node ID
|
||||
let newId = getNodeId();
|
||||
let newId = getNodeId(n.data.type);
|
||||
idsMap[n.id] = newId;
|
||||
n.id = newId;
|
||||
n.data.id = newId;
|
||||
|
|
@ -720,3 +711,10 @@ export function groupByFamily(data, baseClasses) {
|
|||
|
||||
return groupedObj;
|
||||
}
|
||||
|
||||
export function buildTweaks(flow) {
|
||||
return flow.data.nodes.reduce((acc, node) => {
|
||||
acc[node.data.id] = {};
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react-swc";
|
||||
import svgr from "vite-plugin-svgr";
|
||||
const apiRoutes = ["^/api/v1/"];
|
||||
const apiRoutes = ["^/api/v1/", "/health"];
|
||||
|
||||
// Use environment variable to determine the target.
|
||||
const target = process.env.VITE_PROXY_TARGET || "http://127.0.0.1:7860";
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@
|
|||
"type": "str",
|
||||
"list": false
|
||||
},
|
||||
"_type": "zero_shot"
|
||||
"_type": "ZeroShotPrompt"
|
||||
},
|
||||
"description": "Prompt template for Zero Shot Agent.",
|
||||
"base_classes": [
|
||||
|
|
|
|||
|
|
@ -1,15 +1,4 @@
|
|||
from fastapi.testclient import TestClient
|
||||
from langflow.settings import settings
|
||||
|
||||
|
||||
# check that all agents are in settings.agents
|
||||
# are in json_response["agents"]
|
||||
def test_agents_settings(client: TestClient):
|
||||
response = client.get("api/v1/all")
|
||||
assert response.status_code == 200
|
||||
json_response = response.json()
|
||||
agents = json_response["agents"]
|
||||
assert set(agents.keys()) == set(settings.agents)
|
||||
|
||||
|
||||
def test_zero_shot_agent(client: TestClient):
|
||||
|
|
@ -131,7 +120,7 @@ def test_initialize_agent(client: TestClient):
|
|||
json_response = response.json()
|
||||
agents = json_response["agents"]
|
||||
|
||||
initialize_agent = agents["initialize_agent"]
|
||||
initialize_agent = agents["AgentInitializer"]
|
||||
assert initialize_agent["base_classes"] == ["AgentExecutor", "function"]
|
||||
template = initialize_agent["template"]
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue