Merge pull request #78 from logspace-ai/dev

This commit is contained in:
Gabriel Luiz Freitas Almeida 2023-03-28 09:04:30 -03:00 committed by GitHub
commit e2343e6f12
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 906 additions and 238 deletions

14
poetry.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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__)

View file

@ -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()

View 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

View file

@ -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

View file

@ -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
]

View file

@ -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):

View file

@ -2,6 +2,7 @@ import contextlib
import io
import re
from typing import Any, Dict
from langflow.interface import loading

View file

@ -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."""

View file

@ -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")},
}

View file

@ -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():

View 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")

View file

@ -1,9 +0,0 @@
CHAINS = ["LLMChain", "LLMMathChain", "LLMChecker"]
AGENTS = ["ZeroShotAgent"]
PROMPTS = ["PromptTemplate", "FewShotPromptTemplate"]
LLMS = ["OpenAI", "OpenAIChat"]
TOOLS = ["Search", "PAL-MATH", "Calculator", "Serper Search"]

View 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"]

View file

@ -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

View file

@ -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",

View file

@ -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"
}

View file

@ -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>

View file

@ -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>
);
}

View file

@ -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>
);
}

View 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>
);
}

View file

@ -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);

View file

@ -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>

View file

@ -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,

View file

@ -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();

View 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>
);
}

View 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>
);
}

View 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>
);
}

View file

@ -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}

View file

@ -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,

View file

@ -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

View file

@ -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";

View file

@ -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}[],
};

View file

@ -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}

View file

@ -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;
};

View file

@ -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
}

View file

@ -1,4 +1,5 @@
from pathlib import Path
from langflow import load_flow_from_json