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