Merge branch 'dev' of https://github.com/logspace-ai/langflow into 45-implement-agents-as-tools

This commit is contained in:
Gabriel Almeida 2023-03-23 16:48:34 -03:00
commit 1fc05f6f46
28 changed files with 162 additions and 109 deletions

View file

@ -43,9 +43,11 @@ the system we use to tag our issues and pull requests.
You can develop LangFlow using docker compose, or locally. You can develop LangFlow using docker compose, or locally.
#### **Docker compose** #### **Docker compose**
This will run the backend and frontend in separate containers. The frontend will be available at `localhost:3000` and the backend at `localhost:5003`. This will run the backend and frontend in separate containers. The frontend will be available at `localhost:3000` and the backend at `localhost:7860`.
```bash ```bash
docker compose up --build docker compose up --build
# or
make dev build=1
``` ```
#### **Locally** #### **Locally**
@ -58,7 +60,6 @@ Before you start, make sure you have the following installed:
For the backend, you will need to install the dependencies and start the development server. For the backend, you will need to install the dependencies and start the development server.
```bash ```bash
poetry install poetry install
# Port 5003 is required for the backend to work with the frontend
make run_backend make run_backend
``` ```
For the frontend, you will need to install the dependencies and start the development server. For the frontend, you will need to install the dependencies and start the development server.

View file

@ -39,6 +39,17 @@ build:
poetry build --format sdist poetry build --format sdist
rm -rf src/backend/langflow/frontend rm -rf src/backend/langflow/frontend
dev:
make install_frontend
ifeq ($(build),1)
@echo 'Running docker compose up with build'
docker compose up --build
else
@echo 'Running docker compose up without build'
docker compose up
endif
publish: publish:
make build make build
poetry publish poetry publish
@ -49,4 +60,8 @@ help:
@echo 'lint - run linters' @echo 'lint - run linters'
@echo 'install_frontend - install the frontend dependencies' @echo 'install_frontend - install the frontend dependencies'
@echo 'build_frontend - build the frontend static files' @echo 'build_frontend - build the frontend static files'
@echo 'build - build the frontend static files and package the project' @echo 'run_frontend - run the frontend in development mode'
@echo 'run_backend - run the backend in development mode'
@echo 'build - build the frontend static files and package the project'
@echo 'publish - build the frontend static files and package the project and publish it to PyPI'
@echo 'dev - run the project in development mode with docker compose'

View file

@ -34,6 +34,18 @@ Creating flows with LangFlow is easy. Simply drag sidebar components onto the ca
Explore by editing prompt parameters, link chains and agents, track an agent's thought process, and export your flow. Explore by editing prompt parameters, link chains and agents, track an agent's thought process, and export your flow.
Once you're done, you can export your flow as a JSON file to use with LangChain.
To do so, click the "Export" button in the top right corner of the canvas, then
in Python, you can load the flow with:
```python
from langflow import load_flow_from_json
flow = load_flow_from_json("path/to/flow.json")
# Now you can use it like any chain
flow("Hey, have you heard of LangFlow?")
```
## 👋 Contributing ## 👋 Contributing

View file

@ -3,7 +3,7 @@ FROM python:3.10-slim
WORKDIR /app WORKDIR /app
# Install Poetry # Install Poetry
RUN apt-get update && apt-get install -y curl RUN apt-get update && apt-get install gcc curl -y
RUN curl -sSL https://install.python-poetry.org | python3 - RUN curl -sSL https://install.python-poetry.org | python3 -
# # Add Poetry to PATH # # Add Poetry to PATH
ENV PATH="${PATH}:/root/.local/bin" ENV PATH="${PATH}:/root/.local/bin"

View file

@ -15,12 +15,12 @@ services:
build: build:
context: ./src/frontend context: ./src/frontend
dockerfile: ./dev.Dockerfile dockerfile: ./dev.Dockerfile
args:
- BACKEND_URL=http://backend:7860
ports: ports:
- "3000:3000" - "3000:3000"
volumes: volumes:
- ./src/frontend:/app - ./src/frontend/public:/home/node/app/public
# Set process.env.BACKEND to the backend service - ./src/frontend/src:/home/node/app/src
environment: - ./src/frontend/package.json:/home/node/app/package.json
- BACKEND_HOST="http://backend" restart: on-failure
- BACKEND_PORT="7860"
restart: on-failure

9
poetry.lock generated
View file

@ -1378,13 +1378,14 @@ files = [
[[package]] [[package]]
name = "openai" name = "openai"
version = "0.26.5" version = "0.27.2"
description = "Python client library for the OpenAI API" description = "Python client library for the OpenAI API"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7.1" python-versions = ">=3.7.1"
files = [ files = [
{file = "openai-0.26.5.tar.gz", hash = "sha256:2882a59c67ae33c2716a04389a6e6680d061f073424953732f917fde219addfd"}, {file = "openai-0.27.2-py3-none-any.whl", hash = "sha256:6df674cf257e9e0504f1fd191c333d3f6a2442b13218d0eccf06230eb24d320e"},
{file = "openai-0.27.2.tar.gz", hash = "sha256:5869fdfa34b0ec66c39afa22f4a0fb83a135dff81f6505f52834c6ab3113f762"},
] ]
[package.dependencies] [package.dependencies]
@ -1395,7 +1396,7 @@ tqdm = "*"
[package.extras] [package.extras]
datalib = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] datalib = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"]
dev = ["black (>=21.6b0,<22.0)", "pytest (>=6.0.0,<7.0.0)", "pytest-asyncio", "pytest-mock"] dev = ["black (>=21.6b0,<22.0)", "pytest (>=6.0.0,<7.0.0)", "pytest-asyncio", "pytest-mock"]
embeddings = ["matplotlib", "numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "plotly", "scikit-learn (>=1.0.2)", "sklearn", "tenacity (>=8.0.1)"] embeddings = ["matplotlib", "numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "plotly", "scikit-learn (>=1.0.2)", "scipy", "tenacity (>=8.0.1)"]
wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "wandb"] wandb = ["numpy", "openpyxl (>=3.0.7)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "wandb"]
[[package]] [[package]]
@ -2406,4 +2407,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.9" python-versions = "^3.9"
content-hash = "9287450d71ec7c3bd7b1ef2766e22b86fb92bf0e2f51260adf039b1175c74f7c" content-hash = "ebc0a8ca9ea284d8e986306a10f13ff91e7e8aae18341c83329f5b1e1bcc66bd"

View file

@ -1,21 +1,27 @@
[tool.poetry] [tool.poetry]
name = "langflow" name = "langflow"
version = "0.0.40" version = "0.0.45"
description = "A Python package with a built-in web application" description = "A Python package with a built-in web application"
authors = ["Logspace <contact@logspace.ai>"] authors = ["Logspace <contact@logspace.ai>"]
packages = [ maintainers = [
{ include = "langflow", from = "src/backend" }, "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" license = "MIT"
readme = "README.md" 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] [tool.poetry.scripts]
langflow = "langflow.__main__:main" langflow = "langflow.__main__:main"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.9" python = "^3.9"
openai = "^0.26.5"
fastapi = "^0.92.0" fastapi = "^0.92.0"
uvicorn = "^0.20.0" uvicorn = "^0.20.0"
beautifulsoup4 = "^4.11.2" beautifulsoup4 = "^4.11.2"
@ -24,6 +30,7 @@ google-api-python-client = "^2.79.0"
typer = "^0.7.0" typer = "^0.7.0"
gunicorn = "^20.1.0" gunicorn = "^20.1.0"
langchain = "^0.0.113" langchain = "^0.0.113"
openai = "^0.27.2"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
black = "^23.1.0" black = "^23.1.0"

View file

@ -1,12 +1,14 @@
import multiprocessing import multiprocessing
import platform import platform
import re
from langflow.main import create_app from langflow.main import create_app
import typer import typer
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from pathlib import Path from pathlib import Path
import logging
logger = logging.getLogger(__name__)
def get_number_of_workers(workers=None): def get_number_of_workers(workers=None):
@ -15,35 +17,6 @@ def get_number_of_workers(workers=None):
return workers return workers
def replace_port(static_files_dir, host, port):
# Load index.html from frontend directory
# In it there is a script tag that sets the base url
# like so setItem("port", "http://localhost:7860")
# localhost could be anything so we need to verify for string
# we need to set the base url to the port that the server is running on
# so that the frontend can make requests to the backend
# This is a hacky way to do it, but it works
new_string = f'setItem("port","http://{host}:{port}")'
with open(static_files_dir / "index.html", "r") as f:
index_html = f.read()
# using regex to replace the port
index_html = re.sub(
r"setItem\(\"port\",.*\)",
new_string,
index_html,
)
with open(static_files_dir / "index.html", "w") as f:
f.write(index_html)
# Verify that the port was replaced
with open(static_files_dir / "index.html", "r") as f:
index_html = f.read()
if new_string not in index_html:
raise ValueError(
"The port was not replaced in index.html. "
"Please check the regex in main.py"
)
def serve( def serve(
host: str = "127.0.0.1", workers: int = 1, timeout: int = 60, port: int = 7860 host: str = "127.0.0.1", workers: int = 1, timeout: int = 60, port: int = 7860
): ):
@ -63,9 +36,6 @@ def serve(
"timeout": timeout, "timeout": timeout,
} }
# Replace the port in index.html
replace_port(static_files_dir, host, port)
if platform.system() in ["Darwin", "Windows"]: if platform.system() in ["Darwin", "Windows"]:
# Run using uvicorn on MacOS and Windows # Run using uvicorn on MacOS and Windows
# Windows doesn't support gunicorn # Windows doesn't support gunicorn

View file

@ -0,0 +1,6 @@
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

View file

@ -1,4 +1,5 @@
from langchain import chains, agents, prompts, llms from langchain import chains, agents, prompts
from langflow.interface.custom_lists import llm_type_to_cls_dict
from langflow.custom import customs from langflow.custom import customs
from langflow.utils import util, allowed_components from langflow.utils import util, allowed_components
from langchain.agents.load_tools import get_all_tool_names from langchain.agents.load_tools import get_all_tool_names
@ -55,7 +56,7 @@ def list_llms():
"""List all llm types""" """List all llm types"""
return [ return [
llm.__name__ llm.__name__
for llm in llms.type_to_cls_dict.values() for llm in llm_type_to_cls_dict.values()
if llm.__name__ in allowed_components.LLMS if llm.__name__ in allowed_components.LLMS
] ]

View file

@ -1,5 +1,6 @@
from typing import Dict, Any # noqa: F401 from typing import Dict, Any # noqa: F401
from langchain import agents, chains, llms, prompts from langchain import agents, chains, prompts
from langflow.interface.custom_lists import llm_type_to_cls_dict
from langchain.agents.load_tools import ( from langchain.agents.load_tools import (
_BASE_TOOLS, _BASE_TOOLS,
_EXTRA_LLM_TOOLS, _EXTRA_LLM_TOOLS,
@ -56,7 +57,7 @@ def get_prompt_signature(name: str):
def get_llm_signature(name: str): def get_llm_signature(name: str):
"""Get the signature of an llm.""" """Get the signature of an llm."""
try: try:
return util.build_template_from_class(name, llms.type_to_cls_dict) return util.build_template_from_class(name, llm_type_to_cls_dict)
except ValueError as exc: except ValueError as exc:
raise ValueError("LLM not found") from exc raise ValueError("LLM not found") from exc

View file

@ -4,6 +4,6 @@ AGENTS = ["ZeroShotAgent"]
PROMPTS = ["PromptTemplate", "FewShotPromptTemplate"] PROMPTS = ["PromptTemplate", "FewShotPromptTemplate"]
LLMS = ["OpenAI"] LLMS = ["OpenAI", "OpenAIChat"]
TOOLS = ["Search", "PAL-MATH", "Calculator", "Serper Search"] 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

@ -11,6 +11,8 @@ from langchain.agents.load_tools import (
) )
from typing import Optional, Dict from typing import Optional, Dict
from langflow.utils import constants
def build_template_from_function(name: str, type_to_loader_dict: Dict): def build_template_from_function(name: str, type_to_loader_dict: Dict):
classes = [ classes = [
@ -296,7 +298,9 @@ def format_dict(d, name: Optional[str] = None):
# Add options to openai # Add options to openai
if name == "OpenAI" and key == "model_name": 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"] = constants.CHAT_OPENAI_MODELS
return d return d

View file

@ -1,6 +1,23 @@
FROM node:19-alpine as frontend_build #baseline
ARG BACKEND FROM node:19-bullseye-slim AS base
WORKDIR /app RUN mkdir -p /home/node/app
COPY . /app RUN chown -R node:node /home/node && chmod -R 770 /home/node
RUN npm install RUN apt-get update && apt-get install -y jq
WORKDIR /home/node/app
# client build
FROM base AS builder-client
ARG BACKEND_URL
ENV BACKEND_URL $BACKEND_URL
RUN echo "BACKEND_URL: $BACKEND_URL"
WORKDIR /home/node/app
COPY --chown=node:node . ./
COPY ./set_proxy.sh .
RUN chmod +x set_proxy.sh && ./set_proxy.sh
USER node
RUN npm install --loglevel warn
CMD ["npm", "start"] CMD ["npm", "start"]

View file

@ -1,12 +1,12 @@
{ {
"name": "langflow", "name": "langflow",
"version": "0.1.1", "version": "0.1.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "langflow", "name": "langflow",
"version": "0.1.1", "version": "0.1.2",
"dependencies": { "dependencies": {
"@emotion/react": "^11.10.5", "@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5", "@emotion/styled": "^11.10.5",
@ -17080,9 +17080,9 @@
} }
}, },
"node_modules/webpack": { "node_modules/webpack": {
"version": "5.75.0", "version": "5.76.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.75.0.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.2.tgz",
"integrity": "sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==", "integrity": "sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==",
"dependencies": { "dependencies": {
"@types/eslint-scope": "^3.7.3", "@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.51", "@types/estree": "^0.0.51",

View file

@ -1,6 +1,6 @@
{ {
"name": "langflow", "name": "langflow",
"version": "0.1.1", "version": "0.1.2",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@emotion/react": "^11.10.5", "@emotion/react": "^11.10.5",
@ -53,5 +53,6 @@
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
} },
"proxy": "http://backend:7860"
} }

View file

@ -5,13 +5,10 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LangFLow</title> <title>LangFLow</title>
<script>
window.sessionStorage.setItem("port","http://localhost:7860")
</script>
</head> </head>
<body id='body' style="width: 100%; height:100%"> <body id='body' style="width: 100%; height:100%">
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div style="width: 100vw; height:100vh" id='root'> <div style="width: 100vw; height:100vh" id='root'>
</div> </div>
</body> </body>
</html> </html>

10
src/frontend/set_proxy.sh Executable file
View file

@ -0,0 +1,10 @@
#! /bin/bash
# Edit package.json to set proxy
backend_url=$BACKEND_URL
echo "Setting proxy to $backend_url"
# Load package.json file and edit proxy
packagejson=$(cat package.json)
packagejson=$(echo "$packagejson" | jq ".proxy = \"$backend_url\"")
echo "$packagejson" > package.json

View file

@ -39,7 +39,6 @@ export default function ParameterComponent({
const [enabled, setEnabled] = useState( const [enabled, setEnabled] = useState(
data.node.template[name]?.value ?? false data.node.template[name]?.value ?? false
); );
console.log(data.node.template[name]);
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
let disabled = let disabled =
reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false; reactFlowInstance?.getEdges().some((e) => e.targetHandle === id) ?? false;

View file

@ -80,8 +80,8 @@ export default function GenericNode({ data, selected}:{data:NodeDataType,selecte
data={data} data={data}
color={nodeColors[types[data.type]]} color={nodeColors[types[data.type]]}
title={data.type} title={data.type}
tooltipTitle={"Type: str"} tooltipTitle={`Type: ${data.node.base_classes.join(' | ')}`}
id={data.type + "|" + data.id + data.node.base_classes.map((b) => ("|" + b))} id={[data.type, data.id, ...data.node.base_classes].join('|')}
type={'str'} type={'str'}
left={false} left={false}
/> />

View file

@ -7,7 +7,6 @@ import { SingleAlertComponentType } from "../../../../types/alerts";
export default function SingleAlert({ dropItem, removeAlert}:SingleAlertComponentType) { export default function SingleAlert({ dropItem, removeAlert}:SingleAlertComponentType) {
const [show, setShow] = useState(true); const [show, setShow] = useState(true);
const type = dropItem.type; const type = dropItem.type;
console.log(dropItem.id)
return ( return (
<Transition <Transition

View file

@ -16,7 +16,6 @@ export default function AlertDropdown({}: AlertDropdownType) {
componentRef.current && componentRef.current &&
!componentRef.current.contains(event.target as Node) !componentRef.current.contains(event.target as Node)
) { ) {
console.log(event)
closePopUp(); closePopUp();
} }
} }

View file

@ -31,7 +31,6 @@ export default function Chat({ flow, reactFlowInstance }: ChatType) {
setChatHistory((old) => { setChatHistory((old) => {
let newChat = _.cloneDeep(old); let newChat = _.cloneDeep(old);
if(JSON.stringify(flow.chat) !==JSON.stringify(old)){ if(JSON.stringify(flow.chat) !==JSON.stringify(old)){
console.log(old,flow.chat)
tabsChange = true tabsChange = true
return old return old
} }
@ -43,7 +42,6 @@ export default function Chat({ flow, reactFlowInstance }: ChatType) {
return newChat; return newChat;
}); });
if(tabsChange){ if(tabsChange){
console.log(flow.chat)
if(thought){ if(thought){
updateFlow({..._.cloneDeep(flow),chat:[...flow.chat,{isSend,message,thought}]}) updateFlow({..._.cloneDeep(flow),chat:[...flow.chat,{isSend,message,thought}]})
} }
@ -98,11 +96,9 @@ export default function Chat({ flow, reactFlowInstance }: ChatType) {
let message = chatValue; let message = chatValue;
setChatValue(""); setChatValue("");
addChatHistory(message, true); addChatHistory(message, true);
console.log({ ...reactFlowInstance.toObject(), message, chatHistory });
sendAll({ ...reactFlowInstance.toObject(), message, chatHistory}) sendAll({ ...reactFlowInstance.toObject(), message, chatHistory})
.then((r) => { .then((r) => {
console.log(r.data);
addChatHistory(r.data.result, false, r.data.thought); addChatHistory(r.data.result, false, r.data.thought);
setLockChat(false); setLockChat(false);
}) })

View file

@ -131,7 +131,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
// Create a new flow with a default name if no flow is provided. // Create a new flow with a default name if no flow is provided.
let newFlow: FlowType = { let newFlow: FlowType = {
name: flow ? flow.name : "New Flow " + (flows.length===0?"":flows.length), name: "New Flow",
id: id.toString(), id: id.toString(),
data, data,
chat: flow ? flow.chat : [], chat: flow ? flow.chat : [],

View file

@ -1,13 +1,10 @@
import { APIObjectType, sendAllProps } from '../../types/api/index'; import { APIObjectType, sendAllProps } from '../../types/api/index';
import axios, { AxiosResponse } from "axios"; import axios, { AxiosResponse } from "axios";
const backendUrl = window.sessionStorage.getItem('port') || "http://localhost:7860";
export async function getAll():Promise<AxiosResponse<APIObjectType>> { export async function getAll():Promise<AxiosResponse<APIObjectType>> {
return await axios.get(`${backendUrl}/all`); return await axios.get(`/all`);
} }
export async function sendAll(data:sendAllProps) { export async function sendAll(data:sendAllProps) {
console.log(data); return await axios.post(`/predict`, data);
return await axios.post(`${backendUrl}/predict`, data);
} }

View file

@ -4,17 +4,24 @@ import TabComponent from "../tabComponent";
import { TabsContext } from "../../../../contexts/tabsContext"; import { TabsContext } from "../../../../contexts/tabsContext";
import FlowPage from "../.."; import FlowPage from "../..";
import { darkContext } from "../../../../contexts/darkContext"; import { darkContext } from "../../../../contexts/darkContext";
import { BellIcon, MoonIcon, SunIcon } from "@heroicons/react/24/outline"; import {
ArrowDownTrayIcon,
ArrowUpTrayIcon,
BellIcon,
MoonIcon,
SunIcon,
} from "@heroicons/react/24/outline";
import { PopUpContext } from "../../../../contexts/popUpContext"; import { PopUpContext } from "../../../../contexts/popUpContext";
import AlertDropdown from "../../../../alerts/alertDropDown"; import AlertDropdown from "../../../../alerts/alertDropDown";
import { alertContext } from "../../../../contexts/alertContext"; import { alertContext } from "../../../../contexts/alertContext";
export default function TabsManagerComponent() { export default function TabsManagerComponent() {
const { flows, addFlow, tabIndex, setTabIndex } = useContext(TabsContext); const { flows, addFlow, tabIndex, setTabIndex, uploadFlow, downloadFlow } = useContext(TabsContext);
const { openPopUp } = useContext(PopUpContext); const { openPopUp } = useContext(PopUpContext);
const AlertWidth = 256 const AlertWidth = 256;
const { dark, setDark } = useContext(darkContext); const { dark, setDark } = useContext(darkContext);
const {notificationCenter, setNotificationCenter} = useContext(alertContext) const { notificationCenter, setNotificationCenter } =
useContext(alertContext);
useEffect(() => { useEffect(() => {
//create the first flow //create the first flow
if (flows.length === 0) { if (flows.length === 0) {
@ -43,6 +50,12 @@ export default function TabsManagerComponent() {
flow={null} flow={null}
/> />
<div className="ml-auto mr-2 flex gap-3"> <div className="ml-auto mr-2 flex gap-3">
<button onClick={() => uploadFlow()} 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 mr-2 text-sm text-gray-400 hover:text-gray-500">
Export <ArrowDownTrayIcon className="h-5 w-5" />
</button>
<button <button
className="text-gray-400 hover:text-gray-500 " className="text-gray-400 hover:text-gray-500 "
onClick={() => { onClick={() => {
@ -58,13 +71,23 @@ export default function TabsManagerComponent() {
<button <button
className="text-gray-400 hover:text-gray-500 relative" className="text-gray-400 hover:text-gray-500 relative"
onClick={(event: React.MouseEvent<HTMLElement>) => { onClick={(event: React.MouseEvent<HTMLElement>) => {
setNotificationCenter(false) setNotificationCenter(false);
const top = (event.target as Element).getBoundingClientRect().top const top = (event.target as Element).getBoundingClientRect().top;
const left = (event.target as Element).getBoundingClientRect().left const left = (event.target as Element).getBoundingClientRect()
openPopUp(<div className="z-10 absolute" style={{top:top+20, left:left-AlertWidth}}><AlertDropdown/></div>) .left;
openPopUp(
<div
className="z-10 absolute"
style={{ top: top + 20, left: left - AlertWidth }}
>
<AlertDropdown />
</div>
);
}} }}
> >
{notificationCenter&&<div className='absolute w-1.5 h-1.5 rounded-full bg-red-600 right-[3px]'></div>} {notificationCenter && (
<div className="absolute w-1.5 h-1.5 rounded-full bg-red-600 right-[3px]"></div>
)}
<BellIcon className="h-5 w-5" aria-hidden="true" /> <BellIcon className="h-5 w-5" aria-hidden="true" />
</button> </button>
</div> </div>

View file

@ -182,17 +182,6 @@ export default function FlowPage({ flow }:{flow:FlowType}) {
> >
<Background className="dark:bg-gray-900"/> <Background className="dark:bg-gray-900"/>
<Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600"> <Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600">
<ControlButton
onClick={() => uploadFlow()}
>
<ArrowUpTrayIcon />
</ControlButton>
<ControlButton
onClick={() => downloadFlow()}
>
<ArrowDownTrayIcon />
</ControlButton>
</Controls> </Controls>
</ReactFlow> </ReactFlow>
<Chat flow={flow} reactFlowInstance={reactFlowInstance} /> <Chat flow={flow} reactFlowInstance={reactFlowInstance} />