Merge pull request #78 from logspace-ai/dev
This commit is contained in:
commit
e2343e6f12
38 changed files with 906 additions and 238 deletions
14
poetry.lock
generated
14
poetry.lock
generated
|
|
@ -2212,6 +2212,18 @@ dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2
|
|||
doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"]
|
||||
test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "types-pyyaml"
|
||||
version = "6.0.12.8"
|
||||
description = "Typing stubs for PyYAML"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "types-PyYAML-6.0.12.8.tar.gz", hash = "sha256:19304869a89d49af00be681e7b267414df213f4eb89634c4495fa62e8f942b9f"},
|
||||
{file = "types_PyYAML-6.0.12.8-py3-none-any.whl", hash = "sha256:5314a4b2580999b2ea06b2e5f9a7763d860d6e09cdf21c0e9561daa9cbd60178"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.5.0"
|
||||
|
|
@ -2407,4 +2419,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "ebc0a8ca9ea284d8e986306a10f13ff91e7e8aae18341c83329f5b1e1bcc66bd"
|
||||
content-hash = "9acd2b7396be651321ac517873a398d1631a76918fefdb003f7f587f031d9ba1"
|
||||
|
|
|
|||
|
|
@ -1,14 +1,21 @@
|
|||
[tool.poetry]
|
||||
name = "langflow"
|
||||
version = "0.0.45"
|
||||
version = "0.0.46"
|
||||
description = "A Python package with a built-in web application"
|
||||
authors = ["Logspace <contact@logspace.ai>"]
|
||||
packages = [
|
||||
{ include = "langflow", from = "src/backend" },
|
||||
maintainers = [
|
||||
"Gabriel Almeida <gabriel@logspace.ai>",
|
||||
"Ibis Prevedello <ibiscp@gmail.com>",
|
||||
"Lucas Eduoli <lucaseduoli@gmail.com>",
|
||||
"Otávio Anovazzi <otavio2204@gmail.com>",
|
||||
]
|
||||
include = ["src/backend/langflow/*", "src/backend/langflow/**/*"]
|
||||
repository = "https://github.com/logspace-ai/langflow"
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
keywords = ["nlp", "langchain", "openai", "gpt", "gui"]
|
||||
packages = [{ include = "langflow", from = "src/backend" }]
|
||||
include = ["src/backend/langflow/*", "src/backend/langflow/**/*"]
|
||||
|
||||
|
||||
[tool.poetry.scripts]
|
||||
langflow = "langflow.__main__:main"
|
||||
|
|
@ -24,6 +31,7 @@ typer = "^0.7.0"
|
|||
gunicorn = "^20.1.0"
|
||||
langchain = "^0.0.113"
|
||||
openai = "^0.27.2"
|
||||
types-pyyaml = "^6.0.12.8"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
black = "^23.1.0"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
import logging
|
||||
import multiprocessing
|
||||
import platform
|
||||
import re
|
||||
|
||||
from langflow.main import create_app
|
||||
from pathlib import Path
|
||||
|
||||
import typer
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from pathlib import Path
|
||||
import logging
|
||||
|
||||
from langflow.main import create_app
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from fastapi import APIRouter, HTTPException
|
||||
from langflow.interface.types import build_langchain_types_dict
|
||||
from langflow.interface.run import process_data_graph
|
||||
from typing import Any, Dict
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
from langflow.interface.run import process_data_graph
|
||||
from langflow.interface.types import build_langchain_types_dict
|
||||
|
||||
# build router
|
||||
router = APIRouter()
|
||||
|
|
|
|||
27
src/backend/langflow/config.yaml
Normal file
27
src/backend/langflow/config.yaml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
chains:
|
||||
- LLMChain
|
||||
- LLMMathChain
|
||||
- LLMChecker
|
||||
# - ConversationChain
|
||||
|
||||
agents:
|
||||
- ZeroShotAgent
|
||||
|
||||
prompts:
|
||||
- PromptTemplate
|
||||
- FewShotPromptTemplate
|
||||
|
||||
llms:
|
||||
- OpenAI
|
||||
- OpenAIChat
|
||||
|
||||
tools:
|
||||
- Search
|
||||
- PAL-MATH
|
||||
- Calculator
|
||||
- Serper Search
|
||||
|
||||
memories:
|
||||
# - ConversationBufferMemory
|
||||
|
||||
dev: false
|
||||
|
|
@ -1,6 +1,43 @@
|
|||
## LLM
|
||||
from typing import Any
|
||||
|
||||
from langchain import llms
|
||||
from langchain.llms.openai import OpenAIChat
|
||||
|
||||
|
||||
llm_type_to_cls_dict = llms.type_to_cls_dict
|
||||
llm_type_to_cls_dict["openai-chat"] = OpenAIChat
|
||||
|
||||
|
||||
## Memory
|
||||
|
||||
# from langchain.memory.buffer_window import ConversationBufferWindowMemory
|
||||
# from langchain.memory.chat_memory import ChatMessageHistory
|
||||
# from langchain.memory.combined import CombinedMemory
|
||||
# from langchain.memory.entity import ConversationEntityMemory
|
||||
# from langchain.memory.kg import ConversationKGMemory
|
||||
# from langchain.memory.readonly import ReadOnlySharedMemory
|
||||
# from langchain.memory.simple import SimpleMemory
|
||||
# from langchain.memory.summary import ConversationSummaryMemory
|
||||
# from langchain.memory.summary_buffer import ConversationSummaryBufferMemory
|
||||
|
||||
memory_type_to_cls_dict: dict[str, Any] = {
|
||||
# "CombinedMemory": CombinedMemory,
|
||||
# "ConversationBufferWindowMemory": ConversationBufferWindowMemory,
|
||||
# "ConversationBufferMemory": ConversationBufferMemory,
|
||||
# "SimpleMemory": SimpleMemory,
|
||||
# "ConversationSummaryBufferMemory": ConversationSummaryBufferMemory,
|
||||
# "ConversationKGMemory": ConversationKGMemory,
|
||||
# "ConversationEntityMemory": ConversationEntityMemory,
|
||||
# "ConversationSummaryMemory": ConversationSummaryMemory,
|
||||
# "ChatMessageHistory": ChatMessageHistory,
|
||||
# "ConversationStringBufferMemory": ConversationStringBufferMemory,
|
||||
# "ReadOnlySharedMemory": ReadOnlySharedMemory,
|
||||
}
|
||||
|
||||
|
||||
## Chain
|
||||
# from langchain.chains.loading import type_to_loader_dict
|
||||
# from langchain.chains.conversation.base import ConversationChain
|
||||
|
||||
# chain_type_to_cls_dict = type_to_loader_dict
|
||||
# chain_type_to_cls_dict["conversation_chain"] = ConversationChain
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
from langchain import chains, agents, prompts
|
||||
from langflow.interface.custom_lists import llm_type_to_cls_dict
|
||||
from langflow.custom import customs
|
||||
from langflow.utils import util, allowed_components
|
||||
from langchain import agents, chains, prompts
|
||||
from langchain.agents.load_tools import get_all_tool_names
|
||||
from langchain.chains.conversation import memory as memories
|
||||
|
||||
from langflow.custom import customs
|
||||
from langflow.interface.custom_lists import (
|
||||
llm_type_to_cls_dict,
|
||||
memory_type_to_cls_dict,
|
||||
)
|
||||
from langflow.settings import settings
|
||||
from langflow.utils import util
|
||||
|
||||
|
||||
def list_type(object_type: str):
|
||||
|
|
@ -13,18 +17,17 @@ def list_type(object_type: str):
|
|||
"agents": list_agents,
|
||||
"prompts": list_prompts,
|
||||
"llms": list_llms,
|
||||
"tools": list_tools,
|
||||
"memories": list_memories,
|
||||
"tools": list_tools,
|
||||
}.get(object_type, lambda: "Invalid type")()
|
||||
|
||||
|
||||
def list_agents():
|
||||
"""List all agent types"""
|
||||
# return list(agents.loading.AGENT_TO_CLASS.keys())
|
||||
return [
|
||||
agent.__name__
|
||||
for agent in agents.loading.AGENT_TO_CLASS.values()
|
||||
if agent.__name__ in allowed_components.AGENTS
|
||||
if agent.__name__ in settings.agents or settings.dev
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -34,7 +37,7 @@ def list_prompts():
|
|||
library_prompts = [
|
||||
prompt.__annotations__["return"].__name__
|
||||
for prompt in prompts.loading.type_to_loader_dict.values()
|
||||
if prompt.__annotations__["return"].__name__ in allowed_components.PROMPTS
|
||||
if prompt.__annotations__["return"].__name__ in settings.prompts or settings.dev
|
||||
]
|
||||
return library_prompts + list(custom_prompts.keys())
|
||||
|
||||
|
|
@ -46,7 +49,7 @@ def list_tools():
|
|||
|
||||
for tool in get_all_tool_names():
|
||||
tool_params = util.get_tool_params(util.get_tools_dict(tool))
|
||||
if tool_params and tool_params["name"] in allowed_components.TOOLS:
|
||||
if tool_params and tool_params["name"] in settings.tools or settings.dev:
|
||||
tools.append(tool_params["name"])
|
||||
|
||||
return tools
|
||||
|
|
@ -57,7 +60,7 @@ def list_llms():
|
|||
return [
|
||||
llm.__name__
|
||||
for llm in llm_type_to_cls_dict.values()
|
||||
if llm.__name__ in allowed_components.LLMS
|
||||
if llm.__name__ in settings.llms or settings.dev
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -66,10 +69,14 @@ def list_chain_types():
|
|||
return [
|
||||
chain.__annotations__["return"].__name__
|
||||
for chain in chains.loading.type_to_loader_dict.values()
|
||||
if chain.__annotations__["return"].__name__ in allowed_components.CHAINS
|
||||
if chain.__annotations__["return"].__name__ in settings.chains or settings.dev
|
||||
]
|
||||
|
||||
|
||||
def list_memories():
|
||||
"""List all memory types"""
|
||||
return [memory.__name__ for memory in memories.type_to_cls_dict.values()]
|
||||
return [
|
||||
memory.__name__
|
||||
for memory in memory_type_to_cls_dict.values()
|
||||
if memory.__name__ in settings.memories or settings.dev
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
import json
|
||||
from typing import Any, Dict, Optional
|
||||
from langflow.interface.types import get_type_list
|
||||
from langchain.agents.loading import load_agent_from_config
|
||||
from langchain.chains.loading import load_chain_from_config
|
||||
from langchain.llms.loading import load_llm_from_config
|
||||
from langflow.utils import payload
|
||||
from langflow.utils import util
|
||||
from langchain.llms.base import BaseLLM
|
||||
|
||||
from langchain.agents.agent import AgentExecutor
|
||||
from langchain.callbacks.base import BaseCallbackManager
|
||||
from langchain.agents.tools import Tool
|
||||
from langchain.agents.load_tools import (
|
||||
_BASE_TOOLS,
|
||||
_LLM_TOOLS,
|
||||
_EXTRA_LLM_TOOLS,
|
||||
_EXTRA_OPTIONAL_TOOLS,
|
||||
_LLM_TOOLS,
|
||||
)
|
||||
from langchain.agents.loading import load_agent_from_config
|
||||
from langchain.agents.tools import Tool
|
||||
from langchain.callbacks.base import BaseCallbackManager
|
||||
from langchain.chains.loading import load_chain_from_config
|
||||
from langchain.llms.base import BaseLLM
|
||||
from langchain.llms.loading import load_llm_from_config
|
||||
|
||||
from langflow.interface.types import get_type_list
|
||||
from langflow.utils import payload, util
|
||||
|
||||
|
||||
def load_flow_from_json(path: str):
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import contextlib
|
|||
import io
|
||||
import re
|
||||
from typing import Any, Dict
|
||||
|
||||
from langflow.interface import loading
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Dict, Any # noqa: F401
|
||||
from typing import Any, Dict # noqa: F401
|
||||
|
||||
from langchain import agents, chains, prompts
|
||||
from langflow.interface.custom_lists import llm_type_to_cls_dict
|
||||
from langchain.agents.load_tools import (
|
||||
_BASE_TOOLS,
|
||||
_EXTRA_LLM_TOOLS,
|
||||
|
|
@ -9,8 +9,12 @@ from langchain.agents.load_tools import (
|
|||
get_all_tool_names,
|
||||
)
|
||||
|
||||
from langflow.utils import util
|
||||
from langflow.custom import customs
|
||||
from langflow.interface.custom_lists import (
|
||||
llm_type_to_cls_dict,
|
||||
memory_type_to_cls_dict,
|
||||
)
|
||||
from langflow.utils import util
|
||||
|
||||
|
||||
def get_signature(name: str, object_type: str):
|
||||
|
|
@ -20,6 +24,7 @@ def get_signature(name: str, object_type: str):
|
|||
"agents": get_agent_signature,
|
||||
"prompts": get_prompt_signature,
|
||||
"llms": get_llm_signature,
|
||||
"memories": get_memory_signature,
|
||||
"tools": get_tool_signature,
|
||||
}.get(object_type, lambda name: f"Invalid type: {name}")(name)
|
||||
|
||||
|
|
@ -62,6 +67,14 @@ def get_llm_signature(name: str):
|
|||
raise ValueError("LLM not found") from exc
|
||||
|
||||
|
||||
def get_memory_signature(name: str):
|
||||
"""Get the signature of a memory."""
|
||||
try:
|
||||
return util.build_template_from_class(name, memory_type_to_cls_dict)
|
||||
except ValueError as exc:
|
||||
raise ValueError("Memory not found") from exc
|
||||
|
||||
|
||||
def get_tool_signature(name: str):
|
||||
"""Get the signature of a tool."""
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ def get_type_list():
|
|||
|
||||
def build_langchain_types_dict():
|
||||
"""Build a dictionary of all langchain types"""
|
||||
|
||||
return {
|
||||
"chains": {
|
||||
chain: get_signature(chain, "chains") for chain in list_type("chains")
|
||||
|
|
@ -27,5 +28,9 @@ def build_langchain_types_dict():
|
|||
prompt: get_signature(prompt, "prompts") for prompt in list_type("prompts")
|
||||
},
|
||||
"llms": {llm: get_signature(llm, "llms") for llm in list_type("llms")},
|
||||
"memories": {
|
||||
memory: get_signature(memory, "memories")
|
||||
for memory in list_type("memories")
|
||||
},
|
||||
"tools": {tool: get_signature(tool, "tools") for tool in list_type("tools")},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from langflow.api.endpoints import router as endpoints_router
|
||||
from langflow.api.list_endpoints import router as list_router
|
||||
from langflow.api.signature import router as signatures_router
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
|
||||
def create_app():
|
||||
|
|
|
|||
49
src/backend/langflow/settings.py
Normal file
49
src/backend/langflow/settings.py
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import os
|
||||
from typing import List, Optional
|
||||
|
||||
import yaml
|
||||
from pydantic import BaseSettings, Field, root_validator
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
chains: Optional[List[str]] = Field(...)
|
||||
agents: Optional[List[str]] = Field(...)
|
||||
prompts: Optional[List[str]] = Field(...)
|
||||
llms: Optional[List[str]] = Field(...)
|
||||
tools: Optional[List[str]] = Field(...)
|
||||
memories: Optional[List[str]] = Field(...)
|
||||
dev: bool = Field(...)
|
||||
|
||||
class Config:
|
||||
validate_assignment = True
|
||||
|
||||
@root_validator
|
||||
def validate_lists(cls, values):
|
||||
for key, value in values.items():
|
||||
if key != "dev" and not value:
|
||||
values[key] = []
|
||||
return values
|
||||
|
||||
|
||||
def save_settings_to_yaml(settings: Settings, file_path: str):
|
||||
with open(file_path, "w") as f:
|
||||
settings_dict = settings.dict()
|
||||
yaml.dump(settings_dict, f)
|
||||
|
||||
|
||||
def load_settings_from_yaml(file_path: str) -> Settings:
|
||||
# Check if a string is a valid path or a file name
|
||||
if "/" not in file_path:
|
||||
# Get current path
|
||||
current_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
file_path = os.path.join(current_path, file_path)
|
||||
|
||||
with open(file_path, "r") as f:
|
||||
settings_dict = yaml.safe_load(f)
|
||||
a = Settings.parse_obj(settings_dict)
|
||||
|
||||
return a
|
||||
|
||||
|
||||
settings = load_settings_from_yaml("config.yaml")
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
CHAINS = ["LLMChain", "LLMMathChain", "LLMChecker"]
|
||||
|
||||
AGENTS = ["ZeroShotAgent"]
|
||||
|
||||
PROMPTS = ["PromptTemplate", "FewShotPromptTemplate"]
|
||||
|
||||
LLMS = ["OpenAI", "OpenAIChat"]
|
||||
|
||||
TOOLS = ["Search", "PAL-MATH", "Calculator", "Serper Search"]
|
||||
8
src/backend/langflow/utils/constants.py
Normal file
8
src/backend/langflow/utils/constants.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
OPENAI_MODELS = [
|
||||
"text-davinci-003",
|
||||
"text-davinci-002",
|
||||
"text-curie-001",
|
||||
"text-babbage-001",
|
||||
"text-ada-001",
|
||||
]
|
||||
CHAT_OPENAI_MODELS = ["gpt-3.5-turbo", "gpt-4", "gpt-4-32k"]
|
||||
|
|
@ -1,15 +1,17 @@
|
|||
import ast
|
||||
import importlib
|
||||
import inspect
|
||||
import re
|
||||
import importlib
|
||||
from typing import Dict, Optional
|
||||
|
||||
from langchain.agents.load_tools import (
|
||||
_BASE_TOOLS,
|
||||
_LLM_TOOLS,
|
||||
_EXTRA_LLM_TOOLS,
|
||||
_EXTRA_OPTIONAL_TOOLS,
|
||||
_LLM_TOOLS,
|
||||
)
|
||||
from typing import Optional, Dict
|
||||
|
||||
from langflow.utils import constants
|
||||
|
||||
|
||||
def build_template_from_function(name: str, type_to_loader_dict: Dict):
|
||||
|
|
@ -69,6 +71,7 @@ def build_template_from_class(name: str, type_to_cls_dict: Dict):
|
|||
if v.__name__ == name:
|
||||
_class = v
|
||||
|
||||
# Get the docstring
|
||||
docs = get_class_doc(_class)
|
||||
|
||||
variables = {"_type": _type}
|
||||
|
|
@ -190,11 +193,7 @@ def get_class_doc(class_name):
|
|||
A dictionary containing the extracted information, with keys
|
||||
for 'Description', 'Parameters', 'Attributes', and 'Returns'.
|
||||
"""
|
||||
# Get the class docstring
|
||||
docstring = class_name.__doc__
|
||||
|
||||
# Parse the docstring to extract information
|
||||
lines = docstring.split("\n")
|
||||
# Template
|
||||
data = {
|
||||
"Description": "",
|
||||
"Parameters": {},
|
||||
|
|
@ -203,6 +202,15 @@ def get_class_doc(class_name):
|
|||
"Returns": {},
|
||||
}
|
||||
|
||||
# Get the class docstring
|
||||
docstring = class_name.__doc__
|
||||
|
||||
if not docstring:
|
||||
return data
|
||||
|
||||
# Parse the docstring to extract information
|
||||
lines = docstring.split("\n")
|
||||
|
||||
current_section = "Description"
|
||||
|
||||
for line in lines:
|
||||
|
|
@ -296,9 +304,9 @@ def format_dict(d, name: Optional[str] = None):
|
|||
|
||||
# Add options to openai
|
||||
if name == "OpenAI" and key == "model_name":
|
||||
value["options"] = ["text-davinci-003", "text-davinci-002"]
|
||||
value["options"] = constants.OPENAI_MODELS
|
||||
elif name == "OpenAIChat" and key == "model_name":
|
||||
value["options"] = ["gpt-3.5-turbo", "gpt-4"]
|
||||
value["options"] = constants.CHAT_OPENAI_MODELS
|
||||
|
||||
return d
|
||||
|
||||
|
|
|
|||
13
src/frontend/package-lock.json
generated
13
src/frontend/package-lock.json
generated
|
|
@ -22,9 +22,11 @@
|
|||
"@types/react": "^18.0.27",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"axios": "^1.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-cookie": "^4.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-error-boundary": "^4.0.2",
|
||||
"react-icons": "^4.7.1",
|
||||
"react-laag": "^2.0.5",
|
||||
"react-router-dom": "^6.8.1",
|
||||
|
|
@ -14959,6 +14961,17 @@
|
|||
"react": "^18.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-error-boundary": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.2.tgz",
|
||||
"integrity": "sha512-/h21OS80hQ1m/s5UVOp1JKkC8XmUo0rOTRUliGSmWtvswkbbijuQ074K0QLEHwxwwesTt7ksR74/9EHImqWo+A==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-error-overlay": {
|
||||
"version": "6.0.11",
|
||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@
|
|||
"@types/react": "^18.0.27",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"axios": "^1.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"react": "^18.2.0",
|
||||
"react-cookie": "^4.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-error-boundary": "^4.0.2",
|
||||
"react-icons": "^4.7.1",
|
||||
"react-laag": "^2.0.5",
|
||||
"react-router-dom": "^6.8.1",
|
||||
|
|
@ -54,5 +56,5 @@
|
|||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"proxy": "http://localhost:7860"
|
||||
}
|
||||
"proxy": "http://backend:7860"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,6 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>LangFLow</title>
|
||||
<script>
|
||||
window.sessionStorage.setItem("port","http://localhost:7860")
|
||||
</script>
|
||||
</head>
|
||||
<body id='body' style="width: 100%; height:100%">
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import ExtraSidebar from "./components/ExtraSidebarComponent";
|
|||
import { alertContext } from "./contexts/alertContext";
|
||||
import { locationContext } from "./contexts/locationContext";
|
||||
import TabsManagerComponent from "./pages/FlowPage/components/tabsManagerComponent";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import CrashErrorComponent from "./components/CrashErrorComponent";
|
||||
import { TabsContext } from "./contexts/tabsContext";
|
||||
|
||||
export default function App() {
|
||||
var _ = require("lodash");
|
||||
|
|
@ -21,7 +24,7 @@ export default function App() {
|
|||
setShowSideBar(true);
|
||||
setIsStackedOpen(true);
|
||||
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
|
||||
|
||||
const {hardReset} = useContext(TabsContext)
|
||||
const {
|
||||
errorData,
|
||||
errorOpen,
|
||||
|
|
@ -34,45 +37,62 @@ export default function App() {
|
|||
setSuccessOpen,
|
||||
} = useContext(alertContext);
|
||||
|
||||
// Initialize state variable for the list of alerts
|
||||
const [alertsList, setAlertsList] = useState<Array<{type:string,data:{title:string,list?:Array<string>,link?:string},id:string}>>([]);
|
||||
// Initialize state variable for the list of alerts
|
||||
const [alertsList, setAlertsList] = useState<
|
||||
Array<{
|
||||
type: string;
|
||||
data: { title: string; list?: Array<string>; link?: string };
|
||||
id: string;
|
||||
}>
|
||||
>([]);
|
||||
|
||||
// Use effect hook to update alertsList when a new alert is added
|
||||
useEffect(() => {
|
||||
// If there is an error alert open with data, add it to the alertsList
|
||||
if (errorOpen && errorData) {
|
||||
setErrorOpen(false);
|
||||
setAlertsList((old) => {
|
||||
let newAlertsList = [
|
||||
...old,
|
||||
{ type: "error", data: _.cloneDeep(errorData), id: _.uniqueId() },
|
||||
];
|
||||
return newAlertsList;
|
||||
});
|
||||
}
|
||||
// If there is a notice alert open with data, add it to the alertsList
|
||||
else if (noticeOpen && noticeData) {
|
||||
setNoticeOpen(false);
|
||||
setAlertsList((old) => {
|
||||
let newAlertsList = [
|
||||
...old,
|
||||
{ type: "notice", data: _.cloneDeep(noticeData), id: _.uniqueId() },
|
||||
];
|
||||
return newAlertsList;
|
||||
});
|
||||
}
|
||||
// If there is a success alert open with data, add it to the alertsList
|
||||
else if (successOpen && successData) {
|
||||
setSuccessOpen(false);
|
||||
setAlertsList((old) => {
|
||||
let newAlertsList = [
|
||||
...old,
|
||||
{ type: "success", data: _.cloneDeep(successData), id: _.uniqueId() },
|
||||
];
|
||||
return newAlertsList;
|
||||
});
|
||||
}
|
||||
}, [_, errorData, errorOpen, noticeData, noticeOpen, setErrorOpen, setNoticeOpen, setSuccessOpen, successData, successOpen]);
|
||||
// Use effect hook to update alertsList when a new alert is added
|
||||
useEffect(() => {
|
||||
// If there is an error alert open with data, add it to the alertsList
|
||||
if (errorOpen && errorData) {
|
||||
setErrorOpen(false);
|
||||
setAlertsList((old) => {
|
||||
let newAlertsList = [
|
||||
...old,
|
||||
{ type: "error", data: _.cloneDeep(errorData), id: _.uniqueId() },
|
||||
];
|
||||
return newAlertsList;
|
||||
});
|
||||
}
|
||||
// If there is a notice alert open with data, add it to the alertsList
|
||||
else if (noticeOpen && noticeData) {
|
||||
setNoticeOpen(false);
|
||||
setAlertsList((old) => {
|
||||
let newAlertsList = [
|
||||
...old,
|
||||
{ type: "notice", data: _.cloneDeep(noticeData), id: _.uniqueId() },
|
||||
];
|
||||
return newAlertsList;
|
||||
});
|
||||
}
|
||||
// If there is a success alert open with data, add it to the alertsList
|
||||
else if (successOpen && successData) {
|
||||
setSuccessOpen(false);
|
||||
setAlertsList((old) => {
|
||||
let newAlertsList = [
|
||||
...old,
|
||||
{ type: "success", data: _.cloneDeep(successData), id: _.uniqueId() },
|
||||
];
|
||||
return newAlertsList;
|
||||
});
|
||||
}
|
||||
}, [
|
||||
_,
|
||||
errorData,
|
||||
errorOpen,
|
||||
noticeData,
|
||||
noticeOpen,
|
||||
setErrorOpen,
|
||||
setNoticeOpen,
|
||||
setSuccessOpen,
|
||||
successData,
|
||||
successOpen,
|
||||
]);
|
||||
|
||||
const removeAlert = (id: string) => {
|
||||
setAlertsList((prevAlertsList) =>
|
||||
|
|
@ -83,18 +103,27 @@ useEffect(() => {
|
|||
return (
|
||||
//need parent component with width and height
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="flex grow-0 shrink basis-auto">
|
||||
</div>
|
||||
<div className="flex grow shrink basis-auto min-h-0 flex-1 overflow-hidden">
|
||||
<ExtraSidebar />
|
||||
{/* Main area */}
|
||||
<main className="min-w-0 flex-1 border-t border-gray-200 dark:border-gray-700 flex">
|
||||
{/* Primary column */}
|
||||
<div className="w-full h-full">
|
||||
<TabsManagerComponent></TabsManagerComponent>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<div className="flex grow-0 shrink basis-auto"></div>
|
||||
<ErrorBoundary
|
||||
onReset={() => {
|
||||
window.localStorage.removeItem("tabsData");
|
||||
window.localStorage.clear();
|
||||
hardReset()
|
||||
window.location.href = window.location.href;
|
||||
}}
|
||||
FallbackComponent={CrashErrorComponent}
|
||||
>
|
||||
<div className="flex grow shrink basis-auto min-h-0 flex-1 overflow-hidden">
|
||||
<ExtraSidebar />
|
||||
{/* Main area */}
|
||||
<main className="min-w-0 flex-1 border-t border-gray-200 dark:border-gray-700 flex">
|
||||
{/* Primary column */}
|
||||
<div className="w-full h-full">
|
||||
<TabsManagerComponent></TabsManagerComponent>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
<div></div>
|
||||
<div className="flex z-40 flex-col-reverse fixed bottom-5 left-5">
|
||||
{alertsList.map((alert) => (
|
||||
|
|
@ -126,7 +155,13 @@ useEffect(() => {
|
|||
</div>
|
||||
))}
|
||||
</div>
|
||||
<a target={"_blank"} href="https://logspace.ai/" className="absolute bottom-1 left-1 text-gray-500 text-xs cursor-pointer font-sans tracking-wide">Created by Logspace</a>
|
||||
<a
|
||||
target={"_blank"}
|
||||
href="https://logspace.ai/"
|
||||
className="absolute bottom-1 left-1 text-gray-500 text-xs cursor-pointer font-sans tracking-wide"
|
||||
>
|
||||
Created by Logspace
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,92 +1,121 @@
|
|||
import { TrashIcon } from "@heroicons/react/24/outline";
|
||||
import {
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import {
|
||||
classNames,
|
||||
nodeColors,
|
||||
nodeIcons,
|
||||
snakeToNormalCase,
|
||||
classNames,
|
||||
nodeColors,
|
||||
nodeIcons,
|
||||
snakeToNormalCase,
|
||||
} from "../../utils";
|
||||
import ParameterComponent from "./components/parameterComponent";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import { useContext } from "react";
|
||||
import { NodeDataType} from "../../types/flow";
|
||||
import { useContext, useRef } from "react";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
|
||||
export default function GenericNode({ data, selected}:{data:NodeDataType,selected:boolean}) {
|
||||
const {types, deleteNode} = useContext(typesContext);
|
||||
const Icon = nodeIcons[types[data.type]];
|
||||
export default function GenericNode({
|
||||
data,
|
||||
selected,
|
||||
}: {
|
||||
data: NodeDataType;
|
||||
selected: boolean;
|
||||
}) {
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const showError = useRef(true);
|
||||
const { types, deleteNode } = useContext(typesContext);
|
||||
const Icon = nodeIcons[types[data.type]];
|
||||
if (!Icon) {
|
||||
console.log(data);
|
||||
if (showError.current) {
|
||||
setErrorData({
|
||||
title: data.type
|
||||
? `The ${data.type} node could not be rendered, please review your json file`
|
||||
: "There was a node that can't be rendered, please review your json file",
|
||||
});
|
||||
showError.current = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
selected ? "border border-blue-500" : "border dark:border-gray-700",
|
||||
"prompt-node relative bg-white dark:bg-gray-900 w-96 rounded-lg flex flex-col justify-center"
|
||||
)}
|
||||
>
|
||||
<div className="w-full dark:text-white flex items-center justify-between p-4 gap-8 bg-gray-50 rounded-t-lg dark:bg-gray-800 border-b dark:border-b-gray-700 ">
|
||||
<div className="w-full flex items-center truncate gap-4 text-lg">
|
||||
<Icon
|
||||
className="w-10 h-10 p-1 rounded"
|
||||
style={{
|
||||
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
|
||||
}}
|
||||
/>
|
||||
<div className="truncate">{data.type}</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
deleteNode(data.id);
|
||||
}}
|
||||
>
|
||||
<TrashIcon className="w-6 h-6 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-500"></TrashIcon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
return (
|
||||
<div className={ classNames(selected?"border border-blue-500":"border dark:border-gray-700","prompt-node relative bg-white dark:bg-gray-900 w-96 rounded-lg flex flex-col justify-center")}>
|
||||
<div className="w-full dark:text-white flex items-center justify-between p-4 gap-8 bg-gray-50 rounded-t-lg dark:bg-gray-800 border-b dark:border-b-gray-700 ">
|
||||
<div className="w-full flex items-center truncate gap-4 text-lg">
|
||||
<Icon
|
||||
className="w-10 h-10 p-1 rounded"
|
||||
style={{ color: nodeColors[types[data.type]] }}
|
||||
/>
|
||||
<div className="truncate">{data.type}</div>
|
||||
</div>
|
||||
<button onClick={() => {deleteNode(data.id)}}>
|
||||
<TrashIcon className="w-6 h-6 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-500"></TrashIcon>
|
||||
</button>
|
||||
</div>
|
||||
<div className="w-full h-full py-5">
|
||||
<div className="w-full text-gray-500 px-5 text-sm">
|
||||
{data.node.description}
|
||||
</div>
|
||||
|
||||
<div className="w-full h-full py-5">
|
||||
<div className="w-full text-gray-500 px-5 text-sm">
|
||||
{data.node.description}
|
||||
</div>
|
||||
|
||||
<>
|
||||
{Object.keys(data.node.template)
|
||||
.filter((t) => t.charAt(0) !== "_")
|
||||
.map((t:string, idx) => (
|
||||
<div key={idx}>
|
||||
{idx === 0 ? (
|
||||
<div className="px-5 py-2 mt-2 dark:text-white text-center">Inputs:</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{data.node.template[t].show ? (
|
||||
<ParameterComponent
|
||||
|
||||
data={data}
|
||||
color={
|
||||
nodeColors[types[data.node.template[t].type]] ??
|
||||
nodeColors[types[data.node.template[t].type]] ??
|
||||
"black"
|
||||
}
|
||||
title={
|
||||
snakeToNormalCase(t)
|
||||
}
|
||||
name={t}
|
||||
tooltipTitle={
|
||||
"Type: " +
|
||||
data.node.template[t].type +
|
||||
(data.node.template[t].list ? " list" : "")
|
||||
}
|
||||
required={data.node.template[t].required}
|
||||
id={data.node.template[t].type + "|" + t + "|" + data.id}
|
||||
left={true}
|
||||
type={data.node.template[t].type}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="px-5 py-2 mt-2 dark:text-white text-center">Output:</div>
|
||||
<ParameterComponent
|
||||
data={data}
|
||||
color={nodeColors[types[data.type]]}
|
||||
title={data.type}
|
||||
tooltipTitle={`Type: ${data.node.base_classes.join(' | ')}`}
|
||||
id={[data.type, data.id, ...data.node.base_classes].join('|')}
|
||||
type={'str'}
|
||||
left={false}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<>
|
||||
{Object.keys(data.node.template)
|
||||
.filter((t) => t.charAt(0) !== "_")
|
||||
.map((t: string, idx) => (
|
||||
<div key={idx}>
|
||||
{idx === 0 ? (
|
||||
<div className="px-5 py-2 mt-2 dark:text-white text-center">
|
||||
Inputs:
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{data.node.template[t].show ? (
|
||||
<ParameterComponent
|
||||
data={data}
|
||||
color={
|
||||
nodeColors[types[data.node.template[t].type]] ??
|
||||
nodeColors.unknown
|
||||
}
|
||||
title={snakeToNormalCase(t)}
|
||||
name={t}
|
||||
tooltipTitle={
|
||||
"Type: " +
|
||||
data.node.template[t].type +
|
||||
(data.node.template[t].list ? " list" : "")
|
||||
}
|
||||
required={data.node.template[t].required}
|
||||
id={data.node.template[t].type + "|" + t + "|" + data.id}
|
||||
left={true}
|
||||
type={data.node.template[t].type}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="px-5 py-2 mt-2 dark:text-white text-center">
|
||||
Output:
|
||||
</div>
|
||||
<ParameterComponent
|
||||
data={data}
|
||||
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
||||
title={data.type}
|
||||
tooltipTitle={`Type: ${data.node.base_classes.join(" | ")}`}
|
||||
id={[data.type, data.id, ...data.node.base_classes].join("|")}
|
||||
type={"str"}
|
||||
left={false}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
31
src/frontend/src/components/CrashErrorComponent/index.tsx
Normal file
31
src/frontend/src/components/CrashErrorComponent/index.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
export default function CrashErrorComponent({ error, resetErrorBoundary }) {
|
||||
return (
|
||||
<div className="fixed top-0 left-0 w-full h-full flex items-center justify-center bg-gray-800 bg-opacity-50 z-50">
|
||||
<div className="bg-white max-w-4xl h-1/3 min-h-fit rounded-lg shadow-lg p-8 text-start flex flex-col justify-evenly">
|
||||
<h1 className="text-red-500 text-3xl mb-4">Oops! An unknown error has occurred.</h1>
|
||||
<p className="text-gray-700 mb-4 text-xl">
|
||||
Please click the 'Reset Application' button
|
||||
to restore the application's state. If the error persists, please
|
||||
create an issue on our GitHub page. We apologize for any inconvenience
|
||||
this may have caused.
|
||||
</p>
|
||||
<div className="flex justify-center">
|
||||
<button
|
||||
onClick={resetErrorBoundary}
|
||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mr-4"
|
||||
>
|
||||
Reset Application
|
||||
</button>
|
||||
<a
|
||||
href="https://github.com/logspace-ai/langflow/issues/new"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded"
|
||||
>
|
||||
Create Issue
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -97,7 +97,7 @@ export default function Chat({ flow, reactFlowInstance }: ChatType) {
|
|||
setChatValue("");
|
||||
addChatHistory(message, true);
|
||||
|
||||
sendAll({ ...reactFlowInstance.toObject(), message, chatHistory})
|
||||
sendAll({ ...reactFlowInstance.toObject(), message, chatHistory,name:flow.name,description:flow.description})
|
||||
.then((r) => {
|
||||
addChatHistory(r.data.result, false, r.data.thought);
|
||||
setLockChat(false);
|
||||
|
|
|
|||
|
|
@ -13,11 +13,13 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
|
|||
<DarkProvider>
|
||||
<LocationProvider>
|
||||
<AlertProvider>
|
||||
<TabsProvider>
|
||||
<PopUpProvider>
|
||||
<TypesProvider>
|
||||
<TabsProvider>{children}</TabsProvider>
|
||||
{children}
|
||||
</TypesProvider>
|
||||
</PopUpProvider>
|
||||
</TabsProvider>
|
||||
</AlertProvider>
|
||||
</LocationProvider>
|
||||
</DarkProvider>
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ const TabsContextInitialValue: TabsContextType = {
|
|||
addFlow: (flowData?: any) => {},
|
||||
updateFlow: (newFlow: FlowType) => {},
|
||||
incrementNodeId: () => 0,
|
||||
downloadFlow: () => {},
|
||||
downloadFlow: (flow:FlowType) => {},
|
||||
uploadFlow: () => {},
|
||||
lockChat: false,
|
||||
setLockChat:(prevState:boolean)=>{}
|
||||
setLockChat:(prevState:boolean)=>{},
|
||||
hardReset:()=>{}
|
||||
};
|
||||
|
||||
export const TabsContext = createContext<TabsContextType>(
|
||||
|
|
@ -54,14 +55,18 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
newNodeId.current = cookieObject.nodeId;
|
||||
}
|
||||
}, []);
|
||||
function hardReset(){
|
||||
newNodeId.current=0;
|
||||
setTabIndex(0);setFlows([]);setId(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the current flow as a JSON file
|
||||
*/
|
||||
function downloadFlow() {
|
||||
function downloadFlow(flow:FlowType) {
|
||||
// create a data URI with the current flow data
|
||||
const jsonString = `data:text/json;chatset=utf-8,${encodeURIComponent(
|
||||
JSON.stringify(flows[tabIndex])
|
||||
JSON.stringify(flow)
|
||||
)}`;
|
||||
|
||||
// create a link element and set its properties
|
||||
|
|
@ -71,7 +76,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
|
||||
// simulate a click on the link element to trigger the download
|
||||
link.click();
|
||||
setNoticeData({title:"Warning: Critical data, including API keys, in JSON file. Keep secure and do not share."})
|
||||
setNoticeData({title:"Warning: Critical data,JSON file may including API keys."})
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -128,10 +133,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
function addFlow(flow?: FlowType) {
|
||||
// Get data from the flow or set it to null if there's no flow provided.
|
||||
const data = flow?.data ? flow.data : null;
|
||||
const description = flow?.description?flow.description:""
|
||||
|
||||
// Create a new flow with a default name if no flow is provided.
|
||||
let newFlow: FlowType = {
|
||||
name: flow ? flow.name : "New Flow " + (flows.length===0?"":flows.length),
|
||||
description,
|
||||
name: "New Flow",
|
||||
id: id.toString(),
|
||||
data,
|
||||
chat: flow ? flow.chat : [],
|
||||
|
|
@ -158,6 +165,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
const newFlows = [...prevState];
|
||||
const index = newFlows.findIndex((flow) => flow.id === newFlow.id);
|
||||
if (index !== -1) {
|
||||
newFlows[index].description = newFlow.description??""
|
||||
newFlows[index].data = newFlow.data;
|
||||
newFlows[index].name = newFlow.name;
|
||||
newFlows[index].chat = newFlow.chat;
|
||||
|
|
@ -169,6 +177,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
return (
|
||||
<TabsContext.Provider
|
||||
value={{
|
||||
hardReset,
|
||||
lockChat,
|
||||
setLockChat,
|
||||
tabIndex,
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import ContextWrapper from './contexts';
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import App from "./App";
|
||||
import reportWebVitals from "./reportWebVitals";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import ContextWrapper from "./contexts";
|
||||
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
document.getElementById("root") as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<ContextWrapper>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</ContextWrapper>
|
||||
<ContextWrapper>
|
||||
<BrowserRouter>
|
||||
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</ContextWrapper>
|
||||
);
|
||||
reportWebVitals();
|
||||
|
|
|
|||
174
src/frontend/src/modals/exportModal/index.tsx
Normal file
174
src/frontend/src/modals/exportModal/index.tsx
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import {
|
||||
XMarkIcon,
|
||||
ArrowDownTrayIcon,
|
||||
DocumentDuplicateIcon,
|
||||
ComputerDesktopIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { removeApiKeys } from "../../utils";
|
||||
|
||||
export default function ExportModal() {
|
||||
const [open, setOpen] = useState(true);
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
const ref = useRef();
|
||||
const {setErrorData}= useContext(alertContext)
|
||||
const { flows, tabIndex, updateFlow, downloadFlow } = useContext(TabsContext);
|
||||
function setModalOpen(x: boolean) {
|
||||
setOpen(x);
|
||||
if (x === false) {
|
||||
setTimeout(() => {
|
||||
closePopUp();
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
const [checked,setChecked] = useState(true)
|
||||
return (
|
||||
<Transition.Root show={open} appear={true} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="relative z-10"
|
||||
onClose={setModalOpen}
|
||||
initialFocus={ref}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
|
||||
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="h-full w-full flex flex-col justify-center items-center">
|
||||
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
|
||||
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ArrowDownTrayIcon
|
||||
className="h-6 w-6 text-blue-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 text-center sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
|
||||
>
|
||||
Export as
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-16 flex flex-col items-start justify-start h-full w-full bg-gray-200 dark:bg-gray-900 p-4 gap-16">
|
||||
<div className="w-full">
|
||||
<label
|
||||
htmlFor="name"
|
||||
className="block mb-2 font-medium text-gray-700 dark:text-white"
|
||||
>
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
onChange={(event) => {
|
||||
if(event.target.value!=""){
|
||||
let newFlow = flows[tabIndex];
|
||||
newFlow.name = event.target.value;
|
||||
updateFlow(newFlow);
|
||||
}
|
||||
else{
|
||||
setErrorData({title:"Flow name can't be empty"})
|
||||
}
|
||||
}}
|
||||
type="text"
|
||||
name="name"
|
||||
value={flows[tabIndex].name ?? null}
|
||||
placeholder="File name"
|
||||
id="name"
|
||||
className="focus:border focus:border-blue block w-full px-3 py-2 border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:border-gray-600 dark:focus:border-blue-500 dark:focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<label
|
||||
htmlFor="description"
|
||||
className="block mb-2 font-medium text-gray-700 dark:text-white"
|
||||
>
|
||||
Description <span className="text-gray-400 text-sm"> (optional)</span>
|
||||
</label>
|
||||
<textarea
|
||||
name="description"
|
||||
id="description"
|
||||
onChange={(event) => {
|
||||
let newFlow = flows[tabIndex];
|
||||
newFlow.description = event.target.value;
|
||||
updateFlow(newFlow);
|
||||
}}
|
||||
value={flows[tabIndex].description ?? null}
|
||||
placeholder="Flow description"
|
||||
rows={3}
|
||||
className=" focus:border focus:border-blue block w-full px-3 py-2 border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-800 dark:border-gray-600 dark:focus:border-blue-500 dark:focus:ring-blue-500"
|
||||
></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="checkbox" className="flex items-center">
|
||||
<input
|
||||
onChange={(event) => {
|
||||
setChecked(event.target.checked)
|
||||
}}
|
||||
checked={checked}
|
||||
id="checkbox"
|
||||
type="checkbox"
|
||||
className="h-4 w-4 text-blue-600 border-gray-300 rounded dark:bg-gray-800 dark:border-gray-600 dark:focus:border-blue-500 dark:focus:ring-blue-500"
|
||||
/>
|
||||
<span className="ml-2 font-medium text-gray-700 dark:text-white">
|
||||
Save with my API keys
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-full flex justify-end">
|
||||
<button
|
||||
onClick={() => {
|
||||
if(checked) downloadFlow(flows[tabIndex]);
|
||||
else downloadFlow(removeApiKeys(flows[tabIndex]))
|
||||
}}
|
||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
||||
>
|
||||
Download Flow
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
}
|
||||
47
src/frontend/src/modals/importModal/buttonBox/index.tsx
Normal file
47
src/frontend/src/modals/importModal/buttonBox/index.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import React, { ReactNode } from "react";
|
||||
import { DocumentDuplicateIcon } from "@heroicons/react/solid";
|
||||
import { classNames } from "../../../utils";
|
||||
|
||||
export default function ButtonBox({
|
||||
onClick,
|
||||
title,
|
||||
description,
|
||||
icon,
|
||||
bgColor,
|
||||
textColor,
|
||||
deactivate
|
||||
}: {
|
||||
onClick: () => void;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: ReactNode;
|
||||
bgColor: string;
|
||||
textColor: string;
|
||||
deactivate?:boolean;
|
||||
}) {
|
||||
return (
|
||||
<button disabled={deactivate} onClick={onClick}>
|
||||
<div
|
||||
className={classNames(
|
||||
"col-span-1 flex flex-col divide-y divide-gray-200 rounded-lg text-center shadow border border-gray-300 hover:shadow-lg transform hover:scale-105",
|
||||
bgColor
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-1 flex-col p-8">
|
||||
<div className="mx-auto flex items-center justify-center h-20 w-20 bg-white/30 rounded-full">
|
||||
<div className="mx-auto flex items-center justify-center h-16 w-16 bg-white rounded-full">
|
||||
<div className={textColor}>
|
||||
{icon}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="mt-6 text-lg font-semibold text-white">{title}</h3>
|
||||
<div className="mt-1 flex flex-grow flex-col justify-between">
|
||||
<dt className="sr-only">{title}</dt>
|
||||
<dd className="text-sm text-gray-100">{deactivate? "cooming soon":description}</dd>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
122
src/frontend/src/modals/importModal/index.tsx
Normal file
122
src/frontend/src/modals/importModal/index.tsx
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import {
|
||||
XMarkIcon,
|
||||
ArrowDownTrayIcon,
|
||||
DocumentDuplicateIcon,
|
||||
ComputerDesktopIcon,
|
||||
ArrowUpTrayIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import ButtonBox from "./buttonBox";
|
||||
|
||||
export default function ImportModal() {
|
||||
const [open, setOpen] = useState(true);
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
const ref = useRef();
|
||||
const {uploadFlow} = useContext(TabsContext)
|
||||
function setModalOpen(x: boolean) {
|
||||
setOpen(x);
|
||||
if (x === false) {
|
||||
setTimeout(() => {
|
||||
closePopUp();
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Transition.Root show={open} appear={true} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="relative z-10"
|
||||
onClose={setModalOpen}
|
||||
initialFocus={ref}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
|
||||
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="h-full w-full flex flex-col justify-center items-center">
|
||||
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
|
||||
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ArrowUpTrayIcon
|
||||
className="h-6 w-6 text-blue-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 text-center sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
|
||||
>
|
||||
Import from
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-full w-full bg-gray-200 dark:bg-gray-900 p-4 gap-4 flex flex-row justify-center items-center">
|
||||
<div className="flex h-full w-full justify-evenly items-center">
|
||||
<ButtonBox
|
||||
deactivate
|
||||
bgColor="bg-slate-400"
|
||||
description="Prebuilt Examples"
|
||||
icon={
|
||||
<DocumentDuplicateIcon className="h-10 w-10 flex-shrink-0" />
|
||||
}
|
||||
onClick={() => console.log("sdsds")}
|
||||
textColor="text-slate-400"
|
||||
title="Examples"
|
||||
></ButtonBox>
|
||||
<ButtonBox
|
||||
bgColor="bg-blue-500"
|
||||
description="Import from Local"
|
||||
icon={
|
||||
<ComputerDesktopIcon className="h-10 w-10 flex-shrink-0" />
|
||||
}
|
||||
onClick={() => {uploadFlow();setModalOpen(false)}}
|
||||
textColor="text-blue-500"
|
||||
title="Local file"
|
||||
></ButtonBox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ export default function DisclosureComponent({
|
|||
{({ open }) => (
|
||||
<>
|
||||
<div>
|
||||
<div className="select-none bg-gray-50 dark:bg-gray-700 dark:border-y-gray-600 w-full flex justify-between items-center -mt-px px-3 py-2 border-y border-y-gray-200">
|
||||
<Disclosure.Button className="select-none bg-gray-50 dark:bg-gray-700 dark:border-y-gray-600 w-full flex justify-between items-center -mt-px px-3 py-2 border-y border-y-gray-200">
|
||||
<div className="flex gap-4">
|
||||
<Icon className="w-6 text-gray-800 dark:text-white" />
|
||||
<span className="flex items-center text-sm text-gray-800 dark:text-white font-medium">
|
||||
|
|
@ -27,15 +27,15 @@ export default function DisclosureComponent({
|
|||
{x.Icon}
|
||||
</button>
|
||||
))}
|
||||
<Disclosure.Button as="button">
|
||||
<div>
|
||||
<ChevronRightIcon
|
||||
className={`${
|
||||
open ? "rotate-90 transform" : ""
|
||||
} h-4 w-4 text-gray-800 dark:text-white`}
|
||||
/>
|
||||
</Disclosure.Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Disclosure.Button>
|
||||
</div>
|
||||
<Disclosure.Panel as="div" className="-mt-px">
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export default function ExtraSidebar() {
|
|||
{Object.keys(data).map((d:keyof APIObjectType, i) => (
|
||||
<DisclosureComponent
|
||||
key={i}
|
||||
button={{ title: nodeNames[d], Icon: nodeIcons[d] }}
|
||||
button={{ title: nodeNames[d]??nodeNames.unknown, Icon: nodeIcons[d]??nodeIcons.unknown }}
|
||||
>
|
||||
<div className="p-2 flex flex-col gap-2">
|
||||
{Object.keys(data[d]).map((t: string, k) => (
|
||||
|
|
@ -63,7 +63,7 @@ export default function ExtraSidebar() {
|
|||
<div
|
||||
draggable
|
||||
className={" cursor-grab border-l-8 rounded-l-md"}
|
||||
style={{ borderLeftColor: nodeColors[d] }}
|
||||
style={{ borderLeftColor: nodeColors[d]??nodeColors.unknown }}
|
||||
onDragStart={(event) =>
|
||||
onDragStart(event, {
|
||||
type: t,
|
||||
|
|
|
|||
|
|
@ -14,9 +14,12 @@ import {
|
|||
import { PopUpContext } from "../../../../contexts/popUpContext";
|
||||
import AlertDropdown from "../../../../alerts/alertDropDown";
|
||||
import { alertContext } from "../../../../contexts/alertContext";
|
||||
import ImportModal from "../../../../modals/importModal";
|
||||
import ExportModal from "../../../../modals/exportModal";
|
||||
|
||||
export default function TabsManagerComponent() {
|
||||
const { flows, addFlow, tabIndex, setTabIndex, uploadFlow, downloadFlow } = useContext(TabsContext);
|
||||
const { flows, addFlow, tabIndex, setTabIndex, uploadFlow, downloadFlow } =
|
||||
useContext(TabsContext);
|
||||
const { openPopUp } = useContext(PopUpContext);
|
||||
const AlertWidth = 256;
|
||||
const { dark, setDark } = useContext(darkContext);
|
||||
|
|
@ -50,10 +53,21 @@ export default function TabsManagerComponent() {
|
|||
flow={null}
|
||||
/>
|
||||
<div className="ml-auto mr-2 flex gap-3">
|
||||
<button onClick={() => uploadFlow()} className="flex items-center gap-1 text-gray-400 hover:text-gray-500">
|
||||
<button
|
||||
onClick={() =>
|
||||
openPopUp(
|
||||
<ImportModal
|
||||
/>
|
||||
)
|
||||
}
|
||||
className="flex items-center gap-1 pr-2 border-gray-400 border-r text-sm text-gray-400 hover:text-gray-500"
|
||||
>
|
||||
Import <ArrowUpTrayIcon className="w-5 h-5" />
|
||||
</button>
|
||||
<button onClick={() => downloadFlow()} className="flex items-center gap-1 text-gray-400 hover:text-gray-500">
|
||||
<button
|
||||
onClick={() =>openPopUp(<ExportModal/>)}
|
||||
className="flex items-center gap-1 mr-2 text-sm text-gray-400 hover:text-gray-500"
|
||||
>
|
||||
Export <ArrowDownTrayIcon className="h-5 w-5" />
|
||||
</button>
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -17,10 +17,6 @@ import GenericNode from "../../CustomNodes/GenericNode";
|
|||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import {
|
||||
ArrowDownTrayIcon,
|
||||
ArrowUpTrayIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import ConnectionLineComponent from "./components/ConnectionLineComponent";
|
||||
import { FlowType, NodeType } from "../../types/flow";
|
||||
import { APIClassType } from "../../types/api";
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ export type TemplateVariableType = {type:string,required:boolean,placeholder?:st
|
|||
export type sendAllProps={
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
name:string,
|
||||
description:string;
|
||||
viewport: Viewport;
|
||||
message:string;
|
||||
|
||||
chatHistory:{message:string,isSend:boolean}[],
|
||||
};
|
||||
|
|
@ -7,6 +7,7 @@ export type FlowType = {
|
|||
id: string;
|
||||
data: ReactFlowJsonObject;
|
||||
chat: Array<ChatMessageType>;
|
||||
description:string;
|
||||
};
|
||||
export type NodeType = {id:string,type:string,position:XYPosition,data:NodeDataType}
|
||||
export type NodeDataType = {type:string,node?:APIClassType,id:string,value:any}
|
||||
|
|
@ -8,8 +8,9 @@ export type TabsContextType = {
|
|||
addFlow: (flowData?: any) => void;
|
||||
updateFlow: (newFlow: FlowType) => void;
|
||||
incrementNodeId: () => number;
|
||||
downloadFlow: () => void;
|
||||
downloadFlow: (flow:FlowType) => void;
|
||||
uploadFlow: () => void;
|
||||
lockChat:boolean,
|
||||
setLockChat:(prevState:boolean)=>void
|
||||
lockChat:boolean;
|
||||
setLockChat:(prevState:boolean)=>void;
|
||||
hardReset:()=>void;
|
||||
};
|
||||
|
|
@ -7,8 +7,12 @@ import {
|
|||
WrenchScrewdriverIcon,
|
||||
ComputerDesktopIcon,
|
||||
Bars3CenterLeftIcon,
|
||||
PaperClipIcon,
|
||||
QuestionMarkCircleIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Connection, Edge, Node, ReactFlowInstance } from "reactflow";
|
||||
import { FlowType } from "./types/flow";
|
||||
var _ = require('lodash')
|
||||
|
||||
export function classNames(...classes:Array<string>) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
|
|
@ -69,7 +73,9 @@ export const nodeColors: {[char: string]: string} = {
|
|||
memories: "#FF9135",
|
||||
advanced: "#000000",
|
||||
chat: "#454173",
|
||||
thought:"#272541"
|
||||
thought:"#272541",
|
||||
docloaders:"#FF9135",
|
||||
unknown:"#9CA3AF"
|
||||
};
|
||||
|
||||
export const nodeNames:{[char: string]: string} = {
|
||||
|
|
@ -81,7 +87,8 @@ export const nodeNames:{[char: string]: string} = {
|
|||
memories: "Memories",
|
||||
advanced: "Advanced",
|
||||
chat: "Chat",
|
||||
|
||||
docloaders:"Document Loader",
|
||||
unknown:"Unknown"
|
||||
};
|
||||
|
||||
export const nodeIcons:{[char: string]: React.ForwardRefExoticComponent<React.SVGProps<SVGSVGElement>>} = {
|
||||
|
|
@ -93,6 +100,8 @@ export const nodeIcons:{[char: string]: React.ForwardRefExoticComponent<React.SV
|
|||
tools: WrenchScrewdriverIcon,
|
||||
advanced: ComputerDesktopIcon,
|
||||
chat: Bars3CenterLeftIcon,
|
||||
docloaders:Bars3CenterLeftIcon,
|
||||
unknown:QuestionMarkCircleIcon
|
||||
};
|
||||
|
||||
export const bgColors = {
|
||||
|
|
@ -341,3 +350,17 @@ export function isValidConnection(
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function removeApiKeys(flow:FlowType):FlowType{
|
||||
let cleanFLow = _.cloneDeep(flow)
|
||||
cleanFLow.data.nodes.forEach(node=>{
|
||||
for(const key in node.data.node.template)
|
||||
{
|
||||
if(key.includes('api')){
|
||||
console.log(node.data.node.template[key])
|
||||
node.data.node.template[key].value = ''
|
||||
}
|
||||
}
|
||||
})
|
||||
return cleanFLow
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
from pathlib import Path
|
||||
|
||||
from langflow import load_flow_from_json
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue