merge dev

This commit is contained in:
Cristhian Zanforlin Lousa 2023-09-05 11:44:16 -03:00
commit 20014f4937
79 changed files with 887 additions and 582 deletions

0
.githooks/pre-commit Normal file → Executable file
View file

View file

@ -27,7 +27,7 @@ format:
cd src/frontend && npm run format cd src/frontend && npm run format
lint: lint:
poetry run mypy --exclude .venv . poetry run mypy src/backend/langflow
poetry run black . --check poetry run black . --check
poetry run ruff . --fix poetry run ruff . --fix

21
poetry.lock generated
View file

@ -3047,13 +3047,13 @@ server = ["fastapi (>=0.100.0)", "pydantic-settings (>=2.0.1)", "sse-starlette (
[[package]] [[package]]
name = "loguru" name = "loguru"
version = "0.7.0" version = "0.7.1"
description = "Python logging made (stupidly) simple" description = "Python logging made (stupidly) simple"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [ files = [
{file = "loguru-0.7.0-py3-none-any.whl", hash = "sha256:b93aa30099fa6860d4727f1b81f8718e965bb96253fa190fab2077aaad6d15d3"}, {file = "loguru-0.7.1-py3-none-any.whl", hash = "sha256:046bf970cb3cad77a28d607cbf042ac25a407db987a1e801c7f7e692469982f9"},
{file = "loguru-0.7.0.tar.gz", hash = "sha256:1612053ced6ae84d7959dd7d5e431a0532642237ec21f7fd83ac73fe539e03e1"}, {file = "loguru-0.7.1.tar.gz", hash = "sha256:7ba2a7d81b79a412b0ded69bd921e012335e80fd39937a633570f273a343579e"},
] ]
[package.dependencies] [package.dependencies]
@ -3061,7 +3061,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
[package.extras] [package.extras]
dev = ["Sphinx (==5.3.0)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v0.990)", "pre-commit (==3.2.1)", "pytest (==6.1.2)", "pytest (==7.2.1)", "pytest-cov (==2.12.1)", "pytest-cov (==4.0.0)", "pytest-mypy-plugins (==1.10.1)", "pytest-mypy-plugins (==1.9.3)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.2.0)", "tox (==3.27.1)", "tox (==4.4.6)"] dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "pre-commit (==3.3.1)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
[[package]] [[package]]
name = "lxml" name = "lxml"
@ -7041,6 +7041,17 @@ files = [
{file = "types_pytz-2023.3.0.1-py3-none-any.whl", hash = "sha256:65152e872137926bb67a8fe6cc9cfd794365df86650c5d5fdc7b167b0f38892e"}, {file = "types_pytz-2023.3.0.1-py3-none-any.whl", hash = "sha256:65152e872137926bb67a8fe6cc9cfd794365df86650c5d5fdc7b167b0f38892e"},
] ]
[[package]]
name = "types-pywin32"
version = "306.0.0.4"
description = "Typing stubs for pywin32"
optional = false
python-versions = "*"
files = [
{file = "types-pywin32-306.0.0.4.tar.gz", hash = "sha256:ae4bbec80d535053236d4bebedf55f58dee89cf5883d277f0fa89e857f3ff337"},
{file = "types_pywin32-306.0.0.4-py3-none-any.whl", hash = "sha256:f76a343ed6933008af85e158063963f923e54f2f461e697b2929b4178c7b77a1"},
]
[[package]] [[package]]
name = "types-pyyaml" name = "types-pyyaml"
version = "6.0.12.11" version = "6.0.12.11"
@ -7773,4 +7784,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.9,<3.11" python-versions = ">=3.9,<3.11"
content-hash = "c877b4d713eef71815d858d30976ab21c42e5eadcc2df8159e940e03323681ee" content-hash = "6523f2e35458c6d0b8d281e20dd2233180128805ea07199c073224b0d6f75ee7"

View file

@ -84,6 +84,7 @@ python-jose = "^3.3.0"
metaphor-python = "^0.1.11" metaphor-python = "^0.1.11"
markupsafe = "^2.1.3" markupsafe = "^2.1.3"
pywin32 = { version = "^306", markers = "sys_platform == 'win32'" } pywin32 = { version = "^306", markers = "sys_platform == 'win32'" }
loguru = "^0.7.1"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
black = "^23.1.0" black = "^23.1.0"
@ -103,6 +104,7 @@ types-python-jose = "^3.3.4.8"
types-passlib = "^1.7.7.13" types-passlib = "^1.7.7.13"
pytest-mock = "^3.11.1" pytest-mock = "^3.11.1"
pytest-xdist = "^3.3.1" pytest-xdist = "^3.3.1"
types-pywin32 = "^306.0.0.4"
[tool.poetry.extras] [tool.poetry.extras]

View file

@ -356,7 +356,7 @@ def superuser(
with session_getter(db_manager) as session: with session_getter(db_manager) as session:
from langflow.services.auth.utils import create_super_user from langflow.services.auth.utils import create_super_user
if create_super_user(session, username, password): if create_super_user(db=session, username=username, password=password):
# Verify that the superuser was created # Verify that the superuser was created
from langflow.services.database.models.user.user import User from langflow.services.database.models.user.user import User

View file

@ -30,10 +30,10 @@ def upgrade() -> None:
# and other related indices # and other related indices
if "flowstyle" in existing_tables: if "flowstyle" in existing_tables:
op.drop_table("flowstyle") op.drop_table("flowstyle")
if "ix_flowstyle_flow_id" in [ if "ix_flowstyle_flow_id" in [
index["name"] for index in inspector.get_indexes("flowstyle") index["name"] for index in inspector.get_indexes("flowstyle")
]: ]:
op.drop_index("ix_flowstyle_flow_id", table_name="flowstyle") op.drop_index("ix_flowstyle_flow_id", table_name="flowstyle")
existing_indices_flow = [] existing_indices_flow = []
existing_fks_flow = [] existing_fks_flow = []

View file

@ -21,8 +21,8 @@ class FrontendNodeRequest(FrontendNode):
class ValidatePromptRequest(BaseModel): class ValidatePromptRequest(BaseModel):
name: str name: str
template: str template: str
#optional for tweak call # optional for tweak call
frontend_node: Optional[FrontendNodeRequest] frontend_node: Optional[FrontendNodeRequest] = None
# Build ValidationResponse class for {"imports": {"errors": []}, "function": {"errors": []}} # Build ValidationResponse class for {"imports": {"errors": []}, "function": {"errors": []}}
@ -41,8 +41,8 @@ class CodeValidationResponse(BaseModel):
class PromptValidationResponse(BaseModel): class PromptValidationResponse(BaseModel):
input_variables: list input_variables: list
#object return for tweak call # object return for tweak call
frontend_node: FrontendNodeRequest | object frontend_node: Optional[FrontendNodeRequest] = None
INVALID_CHARACTERS = { INVALID_CHARACTERS = {

View file

@ -10,7 +10,7 @@ from fastapi import WebSocket
from langchain.schema import AgentAction, LLMResult, AgentFinish from langchain.schema import AgentAction, LLMResult, AgentFinish
from langflow.utils.logger import logger from loguru import logger
# https://github.com/hwchase17/chat-langchain/blob/master/callback.py # https://github.com/hwchase17/chat-langchain/blob/master/callback.py

View file

@ -11,17 +11,15 @@ from fastapi.responses import StreamingResponse
from langflow.api.utils import build_input_keys_response from langflow.api.utils import build_input_keys_response
from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData from langflow.api.v1.schemas import BuildStatus, BuiltResponse, InitResponse, StreamData
from langflow.services import service_manager, ServiceType
from langflow.graph.graph.base import Graph from langflow.graph.graph.base import Graph
from langflow.services.auth.utils import get_current_active_user, get_current_user from langflow.services.auth.utils import get_current_active_user, get_current_user
from langflow.services.utils import get_session from langflow.services.utils import get_session
from langflow.utils.logger import logger from loguru import logger
from langflow.services.utils import get_chat_manager, get_session
from cachetools import LRUCache from cachetools import LRUCache
from sqlmodel import Session from sqlmodel import Session
from typing import TYPE_CHECKING from langflow.services.chat.manager import ChatManager
if TYPE_CHECKING:
from langflow.services.chat.manager import ChatManager
router = APIRouter(tags=["Chat"]) router = APIRouter(tags=["Chat"])
@ -34,6 +32,7 @@ async def chat(
websocket: WebSocket, websocket: WebSocket,
token: str = Query(...), token: str = Query(...),
db: Session = Depends(get_session), db: Session = Depends(get_session),
chat_manager: "ChatManager" = Depends(get_chat_manager),
): ):
"""Websocket endpoint for chat.""" """Websocket endpoint for chat."""
try: try:
@ -48,7 +47,6 @@ async def chat(
code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized" code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized"
) )
chat_manager: "ChatManager" = service_manager.get(ServiceType.CHAT_MANAGER)
if client_id in chat_manager.in_memory_cache: if client_id in chat_manager.in_memory_cache:
await chat_manager.handle_websocket(client_id, websocket) await chat_manager.handle_websocket(client_id, websocket)
else: else:
@ -61,19 +59,21 @@ async def chat(
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc)) await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
except Exception as exc: except Exception as exc:
logger.error(f"Error in chat websocket: {exc}") logger.error(f"Error in chat websocket: {exc}")
if isinstance(exc, HTTPException): messsage = exc.detail if isinstance(exc, HTTPException) else str(exc)
exc = exc.detail
if "Could not validate credentials" in str(exc): if "Could not validate credentials" in str(exc):
await websocket.close( await websocket.close(
code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized" code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized"
) )
else: else:
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc)) await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=messsage)
@router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201) @router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201)
async def init_build( async def init_build(
graph_data: dict, flow_id: str, current_user=Depends(get_current_active_user) graph_data: dict,
flow_id: str,
current_user=Depends(get_current_active_user),
chat_manager: "ChatManager" = Depends(get_chat_manager),
): ):
"""Initialize the build by storing graph data and returning a unique session ID.""" """Initialize the build by storing graph data and returning a unique session ID."""
@ -88,7 +88,6 @@ async def init_build(
return InitResponse(flowId=flow_id) return InitResponse(flowId=flow_id)
# Delete from cache if already exists # Delete from cache if already exists
chat_manager = service_manager.get(ServiceType.CHAT_MANAGER)
if flow_id in chat_manager.in_memory_cache: if flow_id in chat_manager.in_memory_cache:
with chat_manager.in_memory_cache._lock: with chat_manager.in_memory_cache._lock:
chat_manager.in_memory_cache.delete(flow_id) chat_manager.in_memory_cache.delete(flow_id)
@ -124,7 +123,9 @@ async def build_status(flow_id: str):
@router.get("/build/stream/{flow_id}", response_class=StreamingResponse) @router.get("/build/stream/{flow_id}", response_class=StreamingResponse)
async def stream_build(flow_id: str): async def stream_build(
flow_id: str, chat_manager: "ChatManager" = Depends(get_chat_manager)
):
"""Stream the build process based on stored flow data.""" """Stream the build process based on stored flow data."""
async def event_stream(flow_id): async def event_stream(flow_id):
@ -203,7 +204,6 @@ async def stream_build(flow_id: str):
"handle_keys": [], "handle_keys": [],
} }
yield str(StreamData(event="message", data=input_keys_response)) yield str(StreamData(event="message", data=input_keys_response))
chat_manager = service_manager.get(ServiceType.CHAT_MANAGER)
chat_manager.set_cache(flow_id, langchain_object) chat_manager.set_cache(flow_id, langchain_object)
# We need to reset the chat history # We need to reset the chat history
chat_manager.chat_history.empty_history(flow_id) chat_manager.chat_history.empty_history(flow_id)

View file

@ -7,7 +7,7 @@ from langflow.services.database.models.flow import Flow
from langflow.processing.process import process_graph_cached, process_tweaks from langflow.processing.process import process_graph_cached, process_tweaks
from langflow.services.database.models.user.user import User from langflow.services.database.models.user.user import User
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.utils.logger import logger from loguru import logger
from fastapi import APIRouter, Depends, HTTPException, UploadFile, Body, status from fastapi import APIRouter, Depends, HTTPException, UploadFile, Body, status
import sqlalchemy as sa import sqlalchemy as sa
from langflow.interface.custom.custom_component import CustomComponent from langflow.interface.custom.custom_component import CustomComponent
@ -34,14 +34,15 @@ from sqlmodel import Session
router = APIRouter(tags=["Base"]) router = APIRouter(tags=["Base"])
@router.get("/all") @router.get("/all", dependencies=[Depends(get_current_active_user)])
def get_all(current_user: User = Depends(get_current_active_user)): def get_all(
settings_manager=Depends(get_settings_manager),
):
logger.debug("Building langchain types dict") logger.debug("Building langchain types dict")
native_components = build_langchain_types_dict() native_components = build_langchain_types_dict()
# custom_components is a list of dicts # custom_components is a list of dicts
# need to merge all the keys into one dict # need to merge all the keys into one dict
custom_components_from_file: dict[str, Any] = {} custom_components_from_file: dict[str, Any] = {}
settings_manager = get_settings_manager()
if settings_manager.settings.COMPONENTS_PATH: if settings_manager.settings.COMPONENTS_PATH:
logger.info( logger.info(
f"Building custom components from {settings_manager.settings.COMPONENTS_PATH}" f"Building custom components from {settings_manager.settings.COMPONENTS_PATH}"

View file

@ -83,6 +83,7 @@ def update_flow(
flow_id: UUID, flow_id: UUID,
flow: FlowUpdate, flow: FlowUpdate,
current_user: User = Depends(get_current_active_user), current_user: User = Depends(get_current_active_user),
settings_manager=Depends(get_settings_manager),
): ):
"""Update a flow.""" """Update a flow."""
@ -90,7 +91,6 @@ def update_flow(
if not db_flow: if not db_flow:
raise HTTPException(status_code=404, detail="Flow not found") raise HTTPException(status_code=404, detail="Flow not found")
flow_data = flow.dict(exclude_unset=True) flow_data = flow.dict(exclude_unset=True)
settings_manager = get_settings_manager()
if settings_manager.settings.REMOVE_API_KEYS: if settings_manager.settings.REMOVE_API_KEYS:
flow_data = remove_api_keys(flow_data) flow_data = remove_api_keys(flow_data)
for key, value in flow_data.items(): for key, value in flow_data.items():

View file

@ -34,9 +34,9 @@ async def login_to_get_access_token(
@router.get("/auto_login") @router.get("/auto_login")
async def auto_login(db: Session = Depends(get_session)): async def auto_login(
settings_manager = get_settings_manager() db: Session = Depends(get_session), settings_manager=Depends(get_settings_manager)
):
if settings_manager.auth_settings.AUTO_LOGIN: if settings_manager.auth_settings.AUTO_LOGIN:
return create_user_longterm_token(db) return create_user_longterm_token(db)

View file

@ -29,7 +29,7 @@ router = APIRouter(tags=["Users"])
@router.post("/user", response_model=UserRead, status_code=201) @router.post("/user", response_model=UserRead, status_code=201)
def add_user( def add_user(
user: UserCreate, user: UserCreate,
db: Session = Depends(get_session), session: Session = Depends(get_session),
) -> User: ) -> User:
""" """
Add a new user to the database. Add a new user to the database.
@ -38,12 +38,14 @@ def add_user(
try: try:
new_user.password = get_password_hash(user.password) new_user.password = get_password_hash(user.password)
db.add(new_user) session.add(new_user)
db.commit() session.commit()
db.refresh(new_user) session.refresh(new_user)
except IntegrityError as e: except IntegrityError as e:
db.rollback() session.rollback()
raise HTTPException(status_code=400, detail="This username is unavailable.") from e raise HTTPException(
status_code=400, detail="This username is unavailable."
) from e
return new_user return new_user
@ -63,16 +65,16 @@ def read_all_users(
skip: int = 0, skip: int = 0,
limit: int = 10, limit: int = 10,
current_user: Session = Depends(get_current_active_superuser), current_user: Session = Depends(get_current_active_superuser),
db: Session = Depends(get_session), session: Session = Depends(get_session),
) -> UsersResponse: ) -> UsersResponse:
""" """
Retrieve a list of users from the database with pagination. Retrieve a list of users from the database with pagination.
""" """
query = select(User).offset(skip).limit(limit) query = select(User).offset(skip).limit(limit)
users = db.execute(query).fetchall() users = session.execute(query).fetchall()
count_query = select(func.count()).select_from(User) # type: ignore count_query = select(func.count()).select_from(User) # type: ignore
total_count = db.execute(count_query).scalar() total_count = session.execute(count_query).scalar()
return UsersResponse( return UsersResponse(
total_count=total_count, # type: ignore total_count=total_count, # type: ignore
@ -85,19 +87,19 @@ def patch_user(
user_id: UUID, user_id: UUID,
user: UserUpdate, user: UserUpdate,
_: Session = Depends(get_current_active_user), _: Session = Depends(get_current_active_user),
db: Session = Depends(get_session), session: Session = Depends(get_session),
) -> User: ) -> User:
""" """
Update an existing user's data. Update an existing user's data.
""" """
return update_user(user_id, user, db) return update_user(user_id, user, session)
@router.delete("/user/{user_id}") @router.delete("/user/{user_id}")
def delete_user( def delete_user(
user_id: UUID, user_id: UUID,
current_user: User = Depends(get_current_active_superuser), current_user: User = Depends(get_current_active_superuser),
db: Session = Depends(get_session), session: Session = Depends(get_session),
) -> dict: ) -> dict:
""" """
Delete a user from the database. Delete a user from the database.
@ -111,12 +113,12 @@ def delete_user(
status_code=403, detail="You don't have the permission to delete this user" status_code=403, detail="You don't have the permission to delete this user"
) )
user_db = db.query(User).filter(User.id == user_id).first() user_db = session.query(User).filter(User.id == user_id).first()
if not user_db: if not user_db:
raise HTTPException(status_code=404, detail="User not found") raise HTTPException(status_code=404, detail="User not found")
db.delete(user_db) session.delete(user_db)
db.commit() session.commit()
return {"detail": "User deleted"} return {"detail": "User deleted"}
@ -124,7 +126,7 @@ def delete_user(
# TODO: REMOVE - Just for testing purposes # TODO: REMOVE - Just for testing purposes
@router.post("/super_user", response_model=User) @router.post("/super_user", response_model=User)
def add_super_user_for_testing_purposes_delete_me_before_merge_into_dev( def add_super_user_for_testing_purposes_delete_me_before_merge_into_dev(
db: Session = Depends(get_session), session: Session = Depends(get_session),
) -> User: ) -> User:
""" """
Add a superuser for testing purposes. Add a superuser for testing purposes.
@ -139,11 +141,11 @@ def add_super_user_for_testing_purposes_delete_me_before_merge_into_dev(
) )
try: try:
db.add(new_user) session.add(new_user)
db.commit() session.commit()
db.refresh(new_user) session.refresh(new_user)
except IntegrityError as e: except IntegrityError as e:
db.rollback() session.rollback()
raise HTTPException(status_code=400, detail="User exists") from e raise HTTPException(status_code=400, detail="User exists") from e
return new_user return new_user

View file

@ -8,7 +8,7 @@ from langflow.api.v1.base import (
validate_prompt, validate_prompt,
) )
from langflow.template.field.base import TemplateField from langflow.template.field.base import TemplateField
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.validate import validate_code from langflow.utils.validate import validate_code
# build router # build router
@ -35,7 +35,7 @@ def post_validate_prompt(prompt_request: ValidatePromptRequest):
if prompt_request.frontend_node is None: if prompt_request.frontend_node is None:
return PromptValidationResponse( return PromptValidationResponse(
input_variables=input_variables, input_variables=input_variables,
frontend_node={}, frontend_node=None,
) )
old_custom_fields = get_old_custom_fields(prompt_request) old_custom_fields = get_old_custom_fields(prompt_request)

View file

@ -1,4 +1,4 @@
from langflow.utils.logger import logger from loguru import logger
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:

View file

@ -10,7 +10,7 @@ from langflow.graph.vertex.types import (
) )
from langflow.interface.tools.constants import FILE_TOOLS from langflow.interface.tools.constants import FILE_TOOLS
from langflow.utils import payload from langflow.utils import payload
from langflow.utils.logger import logger from loguru import logger
from langchain.chains.base import Chain from langchain.chains.base import Chain

View file

@ -3,7 +3,7 @@ from langflow.graph.utils import UnbuiltObject
from langflow.interface.initialize import loading from langflow.interface.initialize import loading
from langflow.interface.listing import lazy_load_dict from langflow.interface.listing import lazy_load_dict
from langflow.utils.constants import DIRECT_TYPES from langflow.utils.constants import DIRECT_TYPES
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import sync_to_async from langflow.utils.util import sync_to_async

View file

@ -8,7 +8,7 @@ from langflow.interface.base import LangChainTypeCreator
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.agents import AgentFrontendNode from langflow.template.frontend_node.agents import AgentFrontendNode
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class, build_template_from_method from langflow.utils.util import build_template_from_class, build_template_from_method

View file

@ -8,7 +8,7 @@ from pydantic import BaseModel
from langflow.template.field.base import TemplateField from langflow.template.field.base import TemplateField
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.template.base import Template from langflow.template.template.base import Template
from langflow.utils.logger import logger from loguru import logger
# Assuming necessary imports for Field, Template, and FrontendNode classes # Assuming necessary imports for Field, Template, and FrontendNode classes

View file

@ -6,7 +6,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.chains import ChainFrontendNode from langflow.template.frontend_node.chains import ChainFrontendNode
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class, build_template_from_method from langflow.utils.util import build_template_from_class, build_template_from_method
from langchain import chains from langchain import chains
from langchain_experimental.sql import SQLDatabaseChain # type: ignore from langchain_experimental.sql import SQLDatabaseChain # type: ignore

View file

@ -8,7 +8,7 @@ from langflow.interface.custom.custom_component import CustomComponent
from langflow.template.frontend_node.custom_components import ( from langflow.template.frontend_node.custom_components import (
CustomComponentFrontendNode, CustomComponentFrontendNode,
) )
from langflow.utils.logger import logger from loguru import logger
# Assuming necessary imports for Field, Template, and FrontendNode classes # Assuming necessary imports for Field, Template, and FrontendNode classes

View file

@ -1,7 +1,7 @@
import os import os
import ast import ast
import zlib import zlib
from langflow.utils.logger import logger from loguru import logger
class CustomComponentPathValueError(ValueError): class CustomComponentPathValueError(ValueError):

View file

@ -5,7 +5,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode from langflow.template.frontend_node.documentloaders import DocumentLoaderFrontNode
from langflow.interface.custom_lists import documentloaders_type_to_cls_dict from langflow.interface.custom_lists import documentloaders_type_to_cls_dict
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class from langflow.utils.util import build_template_from_class

View file

@ -6,7 +6,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.frontend_node.embeddings import EmbeddingFrontendNode from langflow.template.frontend_node.embeddings import EmbeddingFrontendNode
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class from langflow.utils.util import build_template_from_class

View file

@ -34,7 +34,7 @@ from langflow.utils import validate
from langchain.chains.base import Chain from langchain.chains.base import Chain
from langchain.vectorstores.base import VectorStore from langchain.vectorstores.base import VectorStore
from langchain.document_loaders.base import BaseLoader from langchain.document_loaders.base import BaseLoader
from langflow.utils.logger import logger from loguru import logger
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow import CustomComponent from langflow import CustomComponent

View file

@ -5,7 +5,7 @@ from langflow.interface.custom_lists import llm_type_to_cls_dict
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.llms import LLMFrontendNode from langflow.template.frontend_node.llms import LLMFrontendNode
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class from langflow.utils.util import build_template_from_class

View file

@ -6,7 +6,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.base import FrontendNode from langflow.template.frontend_node.base import FrontendNode
from langflow.template.frontend_node.memories import MemoryFrontendNode from langflow.template.frontend_node.memories import MemoryFrontendNode
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class, build_template_from_method from langflow.utils.util import build_template_from_class, build_template_from_method
from langflow.custom.customs import get_custom_nodes from langflow.custom.customs import get_custom_nodes

View file

@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.output_parsers import OutputParserFrontendNode from langflow.template.frontend_node.output_parsers import OutputParserFrontendNode
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class, build_template_from_method from langflow.utils.util import build_template_from_class, build_template_from_method

View file

@ -8,7 +8,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.prompts import PromptFrontendNode from langflow.template.frontend_node.prompts import PromptFrontendNode
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class from langflow.utils.util import build_template_from_class

View file

@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.retrievers import RetrieverFrontendNode from langflow.template.frontend_node.retrievers import RetrieverFrontendNode
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_method, build_template_from_class from langflow.utils.util import build_template_from_method, build_template_from_class

View file

@ -1,7 +1,7 @@
from typing import Any, Dict, Tuple from typing import Any, Dict, Tuple
from langflow.services.cache.utils import memoize_dict from langflow.services.cache.utils import memoize_dict
from langflow.graph import Graph from langflow.graph import Graph
from langflow.utils.logger import logger from loguru import logger
@memoize_dict(maxsize=10) @memoize_dict(maxsize=10)

View file

@ -5,7 +5,7 @@ from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode from langflow.template.frontend_node.textsplitters import TextSplittersFrontendNode
from langflow.interface.custom_lists import textsplitter_type_to_cls_dict from langflow.interface.custom_lists import textsplitter_type_to_cls_dict
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class from langflow.utils.util import build_template_from_class

View file

@ -6,7 +6,7 @@ from langflow.interface.base import LangChainTypeCreator
from langflow.interface.importing.utils import import_class, import_module from langflow.interface.importing.utils import import_class, import_module
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class from langflow.utils.util import build_template_from_class

View file

@ -3,7 +3,7 @@ import inspect
from typing import Dict, Union from typing import Dict, Union
from langchain.agents.tools import Tool from langchain.agents.tools import Tool
from langflow.utils.logger import logger from loguru import logger
def get_func_tool_params(func, **kwargs) -> Union[Dict, None]: def get_func_tool_params(func, **kwargs) -> Union[Dict, None]:

View file

@ -29,7 +29,7 @@ from langflow.template.frontend_node.custom_components import (
from langflow.interface.retrievers.base import retriever_creator from langflow.interface.retrievers.base import retriever_creator
from langflow.interface.custom.directory_reader import DirectoryReader from langflow.interface.custom.directory_reader import DirectoryReader
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import get_base_classes from langflow.utils.util import get_base_classes
import re import re

View file

@ -8,7 +8,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.utilities import UtilitiesFrontendNode from langflow.template.frontend_node.utilities import UtilitiesFrontendNode
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class from langflow.utils.util import build_template_from_class

View file

@ -8,7 +8,7 @@ import re
import yaml import yaml
from langchain.base_language import BaseLanguageModel from langchain.base_language import BaseLanguageModel
from PIL.Image import Image from PIL.Image import Image
from langflow.utils.logger import logger from loguru import logger
from langflow.services.chat.config import ChatConfig from langflow.services.chat.config import ChatConfig
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager

View file

@ -7,7 +7,7 @@ from langflow.interface.importing.utils import import_class
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from langflow.template.frontend_node.vectorstores import VectorStoreFrontendNode from langflow.template.frontend_node.vectorstores import VectorStoreFrontendNode
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_method from langflow.utils.util import build_template_from_method

View file

@ -3,7 +3,7 @@ from typing import Dict, List, Optional
from langchain import requests, sql_database from langchain import requests, sql_database
from langflow.interface.base import LangChainTypeCreator from langflow.interface.base import LangChainTypeCreator
from langflow.utils.logger import logger from loguru import logger
from langflow.utils.util import build_template_from_class, build_template_from_method from langflow.utils.util import build_template_from_class, build_template_from_method

View file

@ -10,7 +10,7 @@ from langflow.api import router
from langflow.interface.utils import setup_llm_caching from langflow.interface.utils import setup_llm_caching
from langflow.services.database.utils import initialize_database from langflow.services.database.utils import initialize_database
from langflow.services.manager import initialize_services from langflow.services.manager import initialize_services, teardown_services
from langflow.utils.logger import configure from langflow.utils.logger import configure
@ -40,6 +40,7 @@ def create_app():
app.on_event("startup")(initialize_services) app.on_event("startup")(initialize_services)
app.on_event("startup")(initialize_database) app.on_event("startup")(initialize_database)
app.on_event("startup")(setup_llm_caching) app.on_event("startup")(setup_llm_caching)
app.on_event("shutdown")(teardown_services)
return app return app

View file

@ -4,7 +4,7 @@ from langflow.api.v1.callback import (
StreamingLLMCallbackHandler, StreamingLLMCallbackHandler,
) )
from langflow.processing.process import fix_memory_inputs, format_actions from langflow.processing.process import fix_memory_inputs, format_actions
from langflow.utils.logger import logger from loguru import logger
from langchain.agents.agent import AgentExecutor from langchain.agents.agent import AgentExecutor

View file

@ -6,7 +6,7 @@ from langflow.interface.run import (
get_memory_key, get_memory_key,
update_memory_keys, update_memory_keys,
) )
from langflow.utils.logger import logger from loguru import logger
from langflow.graph import Graph from langflow.graph import Graph
from langchain.chains.base import Chain from langchain.chains.base import Chain
from langchain.vectorstores.base import VectorStore from langchain.vectorstores.base import VectorStore

View file

@ -37,7 +37,12 @@ async def api_key_security(
result: Optional[Union[ApiKey, User]] = None result: Optional[Union[ApiKey, User]] = None
if settings_manager.auth_settings.AUTO_LOGIN: if settings_manager.auth_settings.AUTO_LOGIN:
# Get the first user # Get the first user
settings_manager.auth_settings.FIRST_SUPERUSER if not settings_manager.auth_settings.FIRST_SUPERUSER:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Missing first superuser credentials",
)
result = get_user_by_username( result = get_user_by_username(
db, settings_manager.auth_settings.FIRST_SUPERUSER db, settings_manager.auth_settings.FIRST_SUPERUSER
) )
@ -80,6 +85,9 @@ async def get_current_user(
if isinstance(token, Coroutine): if isinstance(token, Coroutine):
token = await token token = await token
if settings_manager.auth_settings.SECRET_KEY is None:
raise credentials_exception
try: try:
payload = jwt.decode( payload = jwt.decode(
token, token,
@ -150,22 +158,16 @@ def create_token(data: dict, expires_delta: timedelta):
def create_super_user( def create_super_user(
username: str,
password: str,
db: Session = Depends(get_session), db: Session = Depends(get_session),
username: Optional[str] = None,
password: Optional[str] = None,
) -> User: ) -> User:
settings_manager = get_settings_manager() super_user = get_user_by_username(db, username)
super_user = get_user_by_username(
db, username or settings_manager.auth_settings.FIRST_SUPERUSER
)
if not super_user: if not super_user:
super_user = User( super_user = User(
username=username or settings_manager.auth_settings.FIRST_SUPERUSER, username=username,
password=get_password_hash( password=get_password_hash(password),
password or settings_manager.auth_settings.FIRST_SUPERUSER_PASSWORD
),
is_superuser=True, is_superuser=True,
is_active=True, is_active=True,
last_login_at=None, last_login_at=None,
@ -179,7 +181,15 @@ def create_super_user(
def create_user_longterm_token(db: Session = Depends(get_session)) -> dict: def create_user_longterm_token(db: Session = Depends(get_session)) -> dict:
super_user = create_super_user(db) settings_manager = get_settings_manager()
username = settings_manager.auth_settings.FIRST_SUPERUSER
password = settings_manager.auth_settings.FIRST_SUPERUSER_PASSWORD
if not username or not password:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Missing first superuser credentials",
)
super_user = create_super_user(db=db, username=username, password=password)
access_token_expires_longterm = timedelta(days=365) access_token_expires_longterm = timedelta(days=365)
access_token = create_token( access_token = create_token(

View file

@ -1,2 +1,8 @@
class Service: from abc import ABC
class Service(ABC):
name: str name: str
def teardown(self):
pass

View file

@ -7,7 +7,7 @@ from langflow.services.cache.manager import Subject
from langflow.services.chat.utils import process_graph from langflow.services.chat.utils import process_graph
from langflow.interface.utils import pil_to_base64 from langflow.interface.utils import pil_to_base64
from langflow.services.schema import ServiceType from langflow.services.schema import ServiceType
from langflow.utils.logger import logger from loguru import logger
import asyncio import asyncio

View file

@ -2,7 +2,7 @@ from fastapi import WebSocket
from langflow.api.v1.schemas import ChatMessage from langflow.api.v1.schemas import ChatMessage
from langflow.processing.base import get_result_and_steps from langflow.processing.base import get_result_and_steps
from langflow.interface.utils import try_setting_streaming_options from langflow.interface.utils import try_setting_streaming_options
from langflow.utils.logger import logger from loguru import logger
async def process_graph( async def process_graph(

View file

@ -1,12 +1,13 @@
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.database.models.user.crud import get_user_by_username
from langflow.services.database.utils import Result, TableResults from langflow.services.database.utils import Result, TableResults
from langflow.services.utils import get_settings_manager from langflow.services.utils import get_settings_manager
from sqlalchemy import inspect from sqlalchemy import inspect
import sqlalchemy as sa import sqlalchemy as sa
from sqlmodel import SQLModel, Session, create_engine from sqlmodel import SQLModel, Session, create_engine
from langflow.utils.logger import logger from loguru import logger
from alembic.config import Config from alembic.config import Config
from alembic import command from alembic import command
from langflow.services.database import models # noqa from langflow.services.database import models # noqa
@ -88,7 +89,7 @@ class DatabaseManager(Service):
for table in legacy_tables: for table in legacy_tables:
if table in inspector.get_table_names(): if table in inspector.get_table_names():
logger.warn(f"Legacy table exists: {table}") logger.warning(f"Legacy table exists: {table}")
return True return True
@ -159,3 +160,23 @@ class DatabaseManager(Service):
) )
logger.debug("Database and tables created successfully") logger.debug("Database and tables created successfully")
def teardown(self):
logger.debug("Tearing down database")
try:
settings_manager = get_settings_manager()
# remove the default superuser if auto_login is enabled
# using the FIRST_SUPERUSER to get the user
if settings_manager.auth_settings.AUTO_LOGIN:
logger.debug("Removing default superuser")
username = settings_manager.auth_settings.FIRST_SUPERUSER
with Session(self.engine) as session:
user = get_user_by_username(session, username)
session.delete(user)
session.commit()
logger.debug("Default superuser removed")
except Exception as exc:
logger.error(f"Error tearing down database: {exc}")
self.engine.dispose()

View file

@ -1,6 +1,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from langflow.utils.logger import logger from loguru import logger
from contextlib import contextmanager from contextlib import contextmanager
from alembic.util.exc import CommandError from alembic.util.exc import CommandError
from sqlmodel import Session from sqlmodel import Session

View file

@ -1,5 +1,6 @@
from langflow.services.schema import ServiceType from langflow.services.schema import ServiceType
from typing import TYPE_CHECKING, List, Optional from typing import TYPE_CHECKING, List, Optional
from loguru import logger
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.factory import ServiceFactory from langflow.services.factory import ServiceFactory
@ -42,6 +43,7 @@ class ServiceManager:
""" """
Create a new service given its name, handling dependencies. Create a new service given its name, handling dependencies.
""" """
logger.debug(f"Create service {service_name}")
self._validate_service_creation(service_name) self._validate_service_creation(service_name)
# Create dependencies first # Create dependencies first
@ -74,9 +76,21 @@ class ServiceManager:
Update a service by its name. Update a service by its name.
""" """
if service_name in self.services: if service_name in self.services:
logger.debug(f"Update service {service_name}")
self.services.pop(service_name, None) self.services.pop(service_name, None)
self.get(service_name) self.get(service_name)
def teardown(self):
"""
Teardown all the services.
"""
for service in self.services.values():
logger.debug(f"Teardown service {service.name}")
service.teardown()
self.services = {}
self.factories = {}
self.dependencies = {}
service_manager = ServiceManager() service_manager = ServiceManager()
@ -121,7 +135,7 @@ def initialize_session_manager():
""" """
Initialize the session manager. Initialize the session manager.
""" """
from langflow.services.session import factory as session_manager_factory from langflow.services.session import factory as session_manager_factory # type: ignore
from langflow.services.cache import factory as cache_factory from langflow.services.cache import factory as cache_factory
initialize_settings_manager() initialize_settings_manager()
@ -134,3 +148,10 @@ def initialize_session_manager():
session_manager_factory.SessionManagerFactory(), session_manager_factory.SessionManagerFactory(),
dependencies=[ServiceType.CACHE_MANAGER], dependencies=[ServiceType.CACHE_MANAGER],
) )
def teardown_services():
"""
Teardown all the services.
"""
service_manager.teardown()

View file

@ -5,16 +5,17 @@ from langflow.services.settings.utils import read_secret_from_file, write_secret
from pydantic import BaseSettings, Field, validator from pydantic import BaseSettings, Field, validator
from passlib.context import CryptContext from passlib.context import CryptContext
from langflow.utils.logger import logger from loguru import logger
class AuthSettings(BaseSettings): class AuthSettings(BaseSettings):
# Login settings # Login settings
CONFIG_DIR: str CONFIG_DIR: str
SECRET_KEY: Optional[str] = Field( SECRET_KEY: str = Field(
None, default="",
description="Secret key for JWT. If not provided, a random one will be generated.", description="Secret key for JWT. If not provided, a random one will be generated.",
env="LANGFLOW_SECRET_KEY", env="LANGFLOW_SECRET_KEY",
allow_mutation=False,
) )
ALGORITHM: str = "HS256" ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 ACCESS_TOKEN_EXPIRE_MINUTES: int = 60

View file

@ -8,7 +8,7 @@ from pathlib import Path
import yaml import yaml
from pydantic import BaseSettings, root_validator, validator from pydantic import BaseSettings, root_validator, validator
from langflow.utils.logger import logger from loguru import logger
# BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components") # BASE_COMPONENTS_PATH = str(Path(__file__).parent / "components")
BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components") BASE_COMPONENTS_PATH = str(Path(__file__).parent.parent.parent / "components")

View file

@ -1,7 +1,7 @@
from langflow.services.base import Service from langflow.services.base import Service
from langflow.services.settings.auth import AuthSettings from langflow.services.settings.auth import AuthSettings
from langflow.services.settings.base import Settings from langflow.services.settings.base import Settings
from langflow.utils.logger import logger from loguru import logger
import os import os
import yaml import yaml
@ -35,5 +35,10 @@ class SettingsManager(Service):
) )
settings = Settings(**settings_dict) settings = Settings(**settings_dict)
auth_settings = AuthSettings(CONFIG_DIR=settings.CONFIG_DIR) if not settings.CONFIG_DIR:
raise ValueError("CONFIG_DIR must be set in settings")
auth_settings = AuthSettings(
CONFIG_DIR=settings.CONFIG_DIR,
)
return cls(settings, auth_settings) return cls(settings, auth_settings)

View file

@ -2,7 +2,7 @@ import os
from pathlib import Path from pathlib import Path
import platform import platform
from langflow.utils.logger import logger from loguru import logger
def set_secure_permissions(file_path): def set_secure_permissions(file_path):
@ -43,5 +43,5 @@ def write_secret_to_file(path: Path, value: str) -> None:
def read_secret_from_file(path: Path) -> str: def read_secret_from_file(path: Path) -> str:
with path.open("rb") as f: with path.open("r") as f:
return f.read() return f.read()

View file

@ -1,10 +1,12 @@
from langflow.services import ServiceType, service_manager from langflow.services import ServiceType, service_manager
from typing import TYPE_CHECKING from typing import TYPE_CHECKING, Generator
if TYPE_CHECKING: if TYPE_CHECKING:
from langflow.services.database.manager import DatabaseManager from langflow.services.database.manager import DatabaseManager
from langflow.services.settings.manager import SettingsManager from langflow.services.settings.manager import SettingsManager
from langflow.services.chat.manager import ChatManager
from sqlmodel import Session
def get_settings_manager() -> "SettingsManager": def get_settings_manager() -> "SettingsManager":
@ -15,6 +17,10 @@ def get_db_manager() -> "DatabaseManager":
return service_manager.get(ServiceType.DATABASE_MANAGER) return service_manager.get(ServiceType.DATABASE_MANAGER)
def get_session(): def get_session() -> Generator["Session", None, None]:
db_manager = service_manager.get(ServiceType.DATABASE_MANAGER) db_manager = service_manager.get(ServiceType.DATABASE_MANAGER)
yield from db_manager.get_session() yield from db_manager.get_session()
def get_chat_manager() -> "ChatManager":
return service_manager.get(ServiceType.CHAT_MANAGER)

View file

@ -1,30 +1,35 @@
import logging from typing import Optional
from loguru import logger
from pathlib import Path from pathlib import Path
from rich.logging import RichHandler from rich.logging import RichHandler
logger = logging.getLogger("langflow")
def configure(log_level: str = "DEBUG", log_file: Optional[Path] = None):
log_format = "<green>{time:HH:mm:ss}</green> - <level>{level: <8}</level> - <level>{message}</level>"
logger.remove() # Remove default handlers
def configure(log_level: str = "DEBUG", log_file: Path = None): # type: ignore # Configure loguru to use RichHandler
log_format = "%(asctime)s - %(levelname)s - %(message)s" logger.configure(
log_level_value = getattr(logging, log_level.upper(), logging.INFO) handlers=[
{
logging.basicConfig( "sink": RichHandler(rich_tracebacks=True, markup=True),
level=log_level_value, "format": log_format,
format=log_format, "level": log_level.upper(),
datefmt="[%X]", }
handlers=[RichHandler(rich_tracebacks=True)], ]
) )
if log_file: if log_file:
log_file = Path(log_file) log_file = Path(log_file)
log_file.parent.mkdir(parents=True, exist_ok=True) log_file.parent.mkdir(parents=True, exist_ok=True)
file_handler = logging.FileHandler(log_file) logger.add(
file_handler.setFormatter(logging.Formatter(log_format)) sink=str(log_file),
logger.addHandler(file_handler) level=log_level.upper(),
format=log_format,
rotation="10 MB", # Log rotation based on file size
)
logger.info(f"Logger set up with log level: {log_level_value}({log_level})") logger.info(f"Logger set up with log level: {log_level}")
if log_file: if log_file:
logger.info(f"Log file: {log_file}") logger.info(f"Log file: {log_file}")

View file

@ -0,0 +1,65 @@
import { useState } from "react";
import { dropdownButtonPropsType } from "../../types/components";
import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
export default function DropdownButton({
firstButtonName,
onFirstBtnClick,
options,
}: dropdownButtonPropsType): JSX.Element {
const [showOptions, setShowOptions] = useState<boolean>(false);
return (
<div>
<DropdownMenu open={showOptions}>
<DropdownMenuTrigger asChild>
<Button
variant="primary"
className="relative pr-10"
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
onFirstBtnClick();
}}
>
{firstButtonName}
<div
className="absolute right-2 items-center text-muted-foreground"
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
setShowOptions(!showOptions);
}}
>
{!showOptions ? (
<IconComponent name="ChevronDown" />
) : (
<IconComponent name="ChevronUp" />
)}
</div>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
onPointerDownOutside={(event) => {
event.stopPropagation();
event.preventDefault();
setShowOptions(!showOptions);
}}
>
{options.map(({ name, onBtnClick }, index) => (
<DropdownMenuItem onClick={onBtnClick} key={index}>
{name}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
}

View file

@ -28,7 +28,6 @@ import {
TabsList, TabsList,
TabsTrigger, TabsTrigger,
} from "../../components/ui/tabs"; } from "../../components/ui/tabs";
import { alertContext } from "../../contexts/alertContext";
import { darkContext } from "../../contexts/darkContext"; import { darkContext } from "../../contexts/darkContext";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import { codeTabsPropsType } from "../../types/components"; import { codeTabsPropsType } from "../../types/components";
@ -57,7 +56,7 @@ export default function CodeTabsComponent({
}, [flow]); }, [flow]);
useEffect(() => { useEffect(() => {
if(tweaks){ if (tweaks) {
unselectAllNodes({ unselectAllNodes({
data, data,
updateNodes: (nodes) => { updateNodes: (nodes) => {
@ -181,7 +180,7 @@ export default function CodeTabsComponent({
key={idx} // Remember to add a unique key prop key={idx} // Remember to add a unique key prop
> >
{idx < 4 ? ( {idx < 4 ? (
<> <div className="w-full h-full flex flex-col">
{tab.description && ( {tab.description && (
<div <div
className="mb-2 w-full text-left text-sm" className="mb-2 w-full text-left text-sm"
@ -195,7 +194,7 @@ export default function CodeTabsComponent({
> >
{tab.code} {tab.code}
</SyntaxHighlighter> </SyntaxHighlighter>
</> </div>
) : idx === 4 ? ( ) : idx === 4 ? (
<> <>
<div className="api-modal-according-display"> <div className="api-modal-according-display">

View file

@ -7,8 +7,17 @@ import { alertContext } from "../../contexts/alertContext";
import { AuthContext } from "../../contexts/authContext"; import { AuthContext } from "../../contexts/authContext";
import { darkContext } from "../../contexts/darkContext"; import { darkContext } from "../../contexts/darkContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { gradients } from "../../utils/styleUtils";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
import { Separator } from "../ui/separator"; import { Separator } from "../ui/separator";
import MenuBar from "./components/menuBar"; import MenuBar from "./components/menuBar";
@ -17,7 +26,7 @@ export default function Header(): JSX.Element {
const { dark, setDark } = useContext(darkContext); const { dark, setDark } = useContext(darkContext);
const { notificationCenter } = useContext(alertContext); const { notificationCenter } = useContext(alertContext);
const location = useLocation(); const location = useLocation();
const { logout, autoLogin, isAdmin } = useContext(AuthContext); const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
const { stars } = useContext(darkContext); const { stars } = useContext(darkContext);
const navigate = useNavigate(); const navigate = useNavigate();
@ -31,40 +40,6 @@ export default function Header(): JSX.Element {
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && ( {flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
<MenuBar flows={flows} tabId={tabId} /> <MenuBar flows={flows} tabId={tabId} />
)} )}
{!autoLogin && location.pathname !== `/flow/${tabId}` && (
<a
onClick={() => {
logout();
navigate("/login");
}}
className="text-sm font-medium text-muted-foreground transition-colors hover:text-primary cursor-pointer mx-5"
>
Sign out
</a>
)}
{location.pathname === "/admin" && (
<a
onClick={() => {
navigate("/");
}}
className="text-sm font-medium text-muted-foreground transition-colors hover:text-primary cursor-pointer"
>
Home
</a>
)}
{isAdmin &&
!autoLogin &&
location.pathname !== "/admin" &&
location.pathname !== `/flow/${tabId}` && (
<a
className="text-sm font-medium text-muted-foreground transition-colors hover:text-primary cursor-pointer"
onClick={() => navigate("/admin")}
>
Admin page
</a>
)}
</div> </div>
<div className="round-button-div"> <div className="round-button-div">
<Link to="/"> <Link to="/">
@ -156,6 +131,44 @@ export default function Header(): JSX.Element {
/> />
</button> </button>
)} )}
{!autoLogin && (
<>
<Separator orientation="vertical" />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className={
"h-7 w-7 rounded-full focus-visible:outline-0 " +
gradients[
parseInt(userData?.id ?? "", 10) % gradients.length
]
}
/>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
{isAdmin && (
<DropdownMenuItem
className="cursor-pointer"
onClick={() => navigate("/admin")}
>
Admin Page
</DropdownMenuItem>
)}
<DropdownMenuItem
className="cursor-pointer"
onClick={() => {
logout();
navigate("/login");
}}
>
Sign Out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</>
)}
</div> </div>
</div> </div>
</div> </div>

View file

@ -30,6 +30,7 @@ export default function InputComponent({
{isForm ? ( {isForm ? (
<Form.Control asChild> <Form.Control asChild>
<Input <Input
type={password && !pwdVisible ? "password" : "text"}
value={value} value={value}
disabled={disabled} disabled={disabled}
required={required} required={required}
@ -53,6 +54,7 @@ export default function InputComponent({
</Form.Control> </Form.Control>
) : ( ) : (
<Input <Input
type="text"
value={value} value={value}
disabled={disabled} disabled={disabled}
required={required} required={required}
@ -76,6 +78,8 @@ export default function InputComponent({
)} )}
{password && ( {password && (
<button <button
type="button"
tabIndex={-1}
className={classNames( className={classNames(
editNode editNode
? "input-component-true-button" ? "input-component-true-button"

View file

@ -0,0 +1,16 @@
import { Skeleton } from "../ui/skeleton";
export const SkeletonCardComponent = (): JSX.Element => {
return (
<div className="skeleton-card">
<div className="skeleton-card-wrapper">
<Skeleton className="h-8 w-8 rounded-full" />
<Skeleton className="h-4 w-[120px]" />
</div>
<div className="skeleton-card-text">
<Skeleton className="h-3 w-[250px]" />
<Skeleton className="h-3 w-[200px]" />
</div>
</div>
);
};

View file

@ -27,7 +27,7 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
ref={ref} ref={ref}
className={cn( className={cn(
"fixed inset-0 z-50 bg-blur-shared backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in", "fixed top-0 right-0 left-0 bottom-0 overflow-auto inset-0 z-50 bg-blur-shared backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className className
)} )}
{...props} {...props}
@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content <DialogPrimitive.Content
ref={ref} ref={ref}
className={cn( className={cn(
"noundo nocopy fixed z-50 flex w-full flex-col gap-3 rounded-b-lg border bg-background p-6 shadow-lg animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0", "noundo nocopy flex text-start overflow-auto z-50 justify-center items-left flex-col w-full max-w-lg gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-bottom-[48%] data-[state=open]:slide-in-from-bottom-[48%] sm:rounded-lg md:w-full",
className className
)} )}
{...props} {...props}

View file

@ -0,0 +1,15 @@
import { cn } from "../../utils/utils";
function Skeleton({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={cn("animate-pulse rounded-md bg-border", className)}
{...props}
/>
);
}
export { Skeleton };

View file

@ -25,7 +25,7 @@ const initialValue: alertContextType = {
notificationList: [], notificationList: [],
pushNotificationList: () => {}, pushNotificationList: () => {},
clearNotificationList: () => {}, clearNotificationList: () => {},
removeFromNotificationList: () => {} removeFromNotificationList: () => {},
}; };
export const alertContext = createContext<alertContextType>(initialValue); export const alertContext = createContext<alertContextType>(initialValue);

View file

@ -39,6 +39,7 @@ const TabsContextInitialValue: TabsContextType = {
save: () => {}, save: () => {},
tabId: "", tabId: "",
setTabId: (index: string) => {}, setTabId: (index: string) => {},
isLoading: true,
flows: [], flows: [],
removeFlow: (id: string) => {}, removeFlow: (id: string) => {},
addFlow: async (flowData?: any) => "", addFlow: async (flowData?: any) => "",
@ -47,7 +48,7 @@ const TabsContextInitialValue: TabsContextType = {
downloadFlow: (flow: FlowType) => {}, downloadFlow: (flow: FlowType) => {},
downloadFlows: () => {}, downloadFlows: () => {},
uploadFlows: () => {}, uploadFlows: () => {},
uploadFlow: () => {}, uploadFlow: async () => "",
isBuilt: false, isBuilt: false,
setIsBuilt: (state: boolean) => {}, setIsBuilt: (state: boolean) => {},
hardReset: () => {}, hardReset: () => {},
@ -72,10 +73,12 @@ export const TabsContext = createContext<TabsContextType>(
export function TabsProvider({ children }: { children: ReactNode }) { export function TabsProvider({ children }: { children: ReactNode }) {
const { setErrorData, setNoticeData, setSuccessData } = const { setErrorData, setNoticeData, setSuccessData } =
useContext(alertContext); useContext(alertContext);
const { getAuthentication } = useContext(AuthContext); const { getAuthentication, isAuthenticated } = useContext(AuthContext);
const [tabId, setTabId] = useState(""); const [tabId, setTabId] = useState("");
const [isLoading, setIsLoading] = useState(true);
const [flows, setFlows] = useState<Array<FlowType>>([]); const [flows, setFlows] = useState<Array<FlowType>>([]);
const [id, setId] = useState(uid()); const [id, setId] = useState(uid());
const { templates, reactFlowInstance } = useContext(typesContext); const { templates, reactFlowInstance } = useContext(typesContext);
@ -86,6 +89,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
const [tabsState, setTabsState] = useState<TabsState>({}); const [tabsState, setTabsState] = useState<TabsState>({});
const [getTweak, setTweak] = useState<tweakType>([]); const [getTweak, setTweak] = useState<tweakType>([]);
useEffect(() => {
if (!isAuthenticated) {
hardReset();
}
}, [isAuthenticated]);
const newNodeId = useRef(uid()); const newNodeId = useRef(uid());
function incrementNodeId() { function incrementNodeId() {
newNodeId.current = uid(); newNodeId.current = uid();
@ -116,11 +125,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
} }
function refreshFlows() { function refreshFlows() {
setIsLoading(true);
getTabsDataFromDB().then((DbData) => { getTabsDataFromDB().then((DbData) => {
if (DbData && Object.keys(templates).length > 0) { if (DbData && Object.keys(templates).length > 0) {
try { try {
processDBData(DbData); processDBData(DbData);
updateStateWithDbData(DbData); updateStateWithDbData(DbData);
setIsLoading(false);
} catch (e) {} } catch (e) {}
} }
}); });
@ -229,6 +240,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
setTabId(""); setTabId("");
setFlows([]); setFlows([]);
setIsLoading(true);
setId(uid()); setId(uid());
} }
@ -286,39 +298,40 @@ export function TabsProvider({ children }: { children: ReactNode }) {
* If the file type is application/json, the file is read and parsed into a JSON object. * If the file type is application/json, the file is read and parsed into a JSON object.
* The resulting JSON object is passed to the addFlow function. * The resulting JSON object is passed to the addFlow function.
*/ */
function uploadFlow(newProject?: boolean, file?: File) { async function uploadFlow(
newProject?: boolean,
file?: File
): Promise<String | undefined> {
let id;
if (file) { if (file) {
file.text().then((text) => { let text = await file.text();
// parse the text into a JSON object // parse the text into a JSON object
let flow: FlowType = JSON.parse(text); let flow: FlowType = JSON.parse(text);
addFlow(flow, newProject); id = await addFlow(flow, newProject);
});
} else { } else {
// create a file input // create a file input
const input = document.createElement("input"); const input = document.createElement("input");
input.type = "file"; input.type = "file";
input.accept = ".json"; input.accept = ".json";
// add a change event listener to the file input // add a change event listener to the file input
input.onchange = (e: Event) => { id = await new Promise((resolve) => {
// check if the file type is application/json input.onchange = async (e: Event) => {
if ( if (
(e.target as HTMLInputElement).files![0].type === "application/json" (e.target as HTMLInputElement).files![0].type === "application/json"
) { ) {
// get the file from the file input const currentfile = (e.target as HTMLInputElement).files![0];
const currentfile = (e.target as HTMLInputElement).files![0]; let text = await currentfile.text();
// read the file as text
currentfile.text().then((text) => {
// parse the text into a JSON object
let flow: FlowType = JSON.parse(text); let flow: FlowType = JSON.parse(text);
const flowId = await addFlow(flow, newProject);
addFlow(flow, newProject); resolve(flowId);
}); }
} };
}; // trigger the file input click event to open the file dialog
// trigger the file input click event to open the file dialog input.click();
input.click(); });
} }
return id;
} }
function uploadFlows() { function uploadFlows() {
@ -641,6 +654,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
paste, paste,
getTweak, getTweak,
setTweak, setTweak,
isLoading,
}} }}
> >
{children} {children}

View file

@ -164,7 +164,7 @@ export async function readFlowsFromDatabase() {
try { try {
const response = await api.get(`${BASE_URL_API}flows/`); const response = await api.get(`${BASE_URL_API}flows/`);
if (response?.status !== 200) { if (response?.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response?.status}`);
} }
return response.data; return response.data;
} catch (error) { } catch (error) {
@ -177,7 +177,7 @@ export async function downloadFlowsFromDatabase() {
try { try {
const response = await api.get(`${BASE_URL_API}flows/download/`); const response = await api.get(`${BASE_URL_API}flows/download/`);
if (response?.status !== 200) { if (response?.status !== 200) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response?.status}`);
} }
return response.data; return response.data;
} catch (error) { } catch (error) {
@ -190,8 +190,8 @@ export async function uploadFlowsToDatabase(flows: FormData) {
try { try {
const response = await api.post(`${BASE_URL_API}flows/upload/`, flows); const response = await api.post(`${BASE_URL_API}flows/upload/`, flows);
if (response.status !== 201) { if (response?.status !== 201) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response?.status}`);
} }
return response.data; return response.data;
} catch (error) { } catch (error) {
@ -468,7 +468,7 @@ export async function updateUser(user_id: string, user: Users) {
export async function getApiKey() { export async function getApiKey() {
try { try {
const res = await api.get(`${BASE_URL_API}api_key`); const res = await api.get(`${BASE_URL_API}api_key/`);
if (res.status === 200) { if (res.status === 200) {
return res.data; return res.data;
} }
@ -480,7 +480,7 @@ export async function getApiKey() {
export async function createApiKey(name: string) { export async function createApiKey(name: string) {
try { try {
const res = await api.post(`${BASE_URL_API}api_key`, { name }); const res = await api.post(`${BASE_URL_API}api_key/`, { name });
if (res.status === 200) { if (res.status === 200) {
return res.data; return res.data;
} }

View file

@ -8,7 +8,7 @@ import { classNames } from "../../utils/utils";
import ChatInput from "./chatInput"; import ChatInput from "./chatInput";
import ChatMessage from "./chatMessage"; import ChatMessage from "./chatMessage";
import _, { set } from "lodash"; import _ from "lodash";
import AccordionComponent from "../../components/AccordionComponent"; import AccordionComponent from "../../components/AccordionComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import ToggleShadComponent from "../../components/toggleShadComponent"; import ToggleShadComponent from "../../components/toggleShadComponent";
@ -25,9 +25,9 @@ import { Textarea } from "../../components/ui/textarea";
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants"; import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext"; import { AuthContext } from "../../contexts/authContext";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { getBuildStatus } from "../../controllers/API";
import { TabsState } from "../../types/tabs"; import { TabsState } from "../../types/tabs";
import { validateNodes } from "../../utils/reactflowUtils"; import { validateNodes } from "../../utils/reactflowUtils";
import { getBuildStatus } from "../../controllers/API";
export default function FormModal({ export default function FormModal({
flow, flow,
@ -156,19 +156,21 @@ export default function FormModal({
function handleOnClose(event: CloseEvent): void { function handleOnClose(event: CloseEvent): void {
if (isOpen.current) { if (isOpen.current) {
getBuildStatus(flow.id).then((response) => { getBuildStatus(flow.id)
if (response.data.built) { .then((response) => {
connectWS(); if (response.data.built) {
} connectWS();
else { } else {
setErrorData({
title: "Please build the flow again before using the chat.",
});
}
})
.catch((error) => {
setErrorData({ setErrorData({
title: "Please build the flow again before using the chat." title: error.data?.detail ? error.data.detail : error.message,
}) });
} });
}).catch((error) => {
setErrorData({title:error.data?.detail?error.data.detail:error.message})
});
setErrorData({ title: event.reason }); setErrorData({ title: event.reason });
setTimeout(() => { setTimeout(() => {
setLockChat(false); setLockChat(false);
@ -186,8 +188,9 @@ export default function FormModal({
const host = isDevelopment ? "localhost:7860" : window.location.host; const host = isDevelopment ? "localhost:7860" : window.location.host;
const chatEndpoint = `/api/v1/chat/${chatId}`; const chatEndpoint = `/api/v1/chat/${chatId}`;
return `${isDevelopment ? "ws" : webSocketProtocol return `${
}://${host}${chatEndpoint}?token=${encodeURIComponent(accessToken!)}`; isDevelopment ? "ws" : webSocketProtocol
}://${host}${chatEndpoint}?token=${encodeURIComponent(accessToken!)}`;
} }
function handleWsMessage(data: any) { function handleWsMessage(data: any) {
@ -209,20 +212,20 @@ export default function FormModal({
newChatHistory.push( newChatHistory.push(
chatItem.files chatItem.files
? { ? {
isSend: !chatItem.is_bot, isSend: !chatItem.is_bot,
message: chatItem.message, message: chatItem.message,
template: chatItem.template, template: chatItem.template,
thought: chatItem.intermediate_steps, thought: chatItem.intermediate_steps,
files: chatItem.files, files: chatItem.files,
chatKey: chatItem.chatKey, chatKey: chatItem.chatKey,
} }
: { : {
isSend: !chatItem.is_bot, isSend: !chatItem.is_bot,
message: chatItem.message, message: chatItem.message,
template: chatItem.template, template: chatItem.template,
thought: chatItem.intermediate_steps, thought: chatItem.intermediate_steps,
chatKey: chatItem.chatKey, chatKey: chatItem.chatKey,
} }
); );
} }
} }
@ -442,73 +445,73 @@ export default function FormModal({
{tabsState[id.current]?.formKeysData?.input_keys {tabsState[id.current]?.formKeysData?.input_keys
? Object.keys( ? Object.keys(
tabsState[id.current].formKeysData.input_keys! tabsState[id.current].formKeysData.input_keys!
).map((key, index) => ( ).map((key, index) => (
<div className="file-component-accordion-div" key={index}> <div className="file-component-accordion-div" key={index}>
<AccordionComponent <AccordionComponent
trigger={ trigger={
<div className="file-component-badge-div"> <div className="file-component-badge-div">
<Badge variant="gray" size="md"> <Badge variant="gray" size="md">
{key} {key}
</Badge> </Badge>
<div <div
className="-mb-1" className="-mb-1"
onClick={(event) => { onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
}} }}
> >
<ToggleShadComponent <ToggleShadComponent
enabled={chatKey === key} enabled={chatKey === key}
setEnabled={(value) => setEnabled={(value) =>
handleOnCheckedChange(value, key) handleOnCheckedChange(value, key)
} }
size="small" size="small"
disabled={tabsState[ disabled={tabsState[
id.current id.current
].formKeysData.handle_keys!.some( ].formKeysData.handle_keys!.some(
(t) => t === key (t) => t === key
)} )}
/> />
</div>
</div> </div>
</div> }
} key={index}
key={index} keyValue={key}
keyValue={key} >
> <div className="file-component-tab-column">
<div className="file-component-tab-column"> {tabsState[id.current].formKeysData.handle_keys!.some(
{tabsState[id.current].formKeysData.handle_keys!.some( (t) => t === key
(t) => t === key ) && (
) && (
<div className="font-normal text-muted-foreground "> <div className="font-normal text-muted-foreground ">
Source: Component Source: Component
</div> </div>
)} )}
<Textarea <Textarea
className="custom-scroll" className="custom-scroll"
value={ value={
tabsState[id.current].formKeysData.input_keys![ tabsState[id.current].formKeysData.input_keys![
key key
] ]
} }
onChange={(e) => { onChange={(e) => {
//@ts-ignore //@ts-ignore
setTabsState((old: TabsState) => { setTabsState((old: TabsState) => {
let newTabsState = _.cloneDeep(old); let newTabsState = _.cloneDeep(old);
newTabsState[ newTabsState[
id.current id.current
].formKeysData.input_keys![key] = ].formKeysData.input_keys![key] =
e.target.value; e.target.value;
return newTabsState; return newTabsState;
}); });
}} }}
disabled={chatKey === key} disabled={chatKey === key}
placeholder="Enter text..." placeholder="Enter text..."
></Textarea> ></Textarea>
</div> </div>
</AccordionComponent> </AccordionComponent>
</div> </div>
)) ))
: null} : null}
{tabsState[id.current].formKeysData.memory_keys!.map( {tabsState[id.current].formKeysData.memory_keys!.map(
(key, index) => ( (key, index) => (
@ -522,7 +525,7 @@ export default function FormModal({
<div className="-mb-1"> <div className="-mb-1">
<ToggleShadComponent <ToggleShadComponent
enabled={chatKey === key} enabled={chatKey === key}
setEnabled={() => { }} setEnabled={() => {}}
size="small" size="small"
disabled={true} disabled={true}
/> />

View file

@ -136,7 +136,11 @@ export default function GenericModal({
setSuccessData({ setSuccessData({
title: "Prompt is ready", title: "Prompt is ready",
}); });
if(JSON.stringify(apiReturn.data?.frontend_node)!==JSON.stringify({})) setNodeClass!(apiReturn.data?.frontend_node); if (
JSON.stringify(apiReturn.data?.frontend_node) !==
JSON.stringify({})
)
setNodeClass!(apiReturn.data?.frontend_node);
setModalOpen(closeModal); setModalOpen(closeModal);
setValue(inputValue); setValue(inputValue);
} }

View file

@ -4,6 +4,7 @@ import { useContext, useEffect, useRef, useState } from "react";
import PaginatorComponent from "../../components/PaginatorComponent"; import PaginatorComponent from "../../components/PaginatorComponent";
import ShadTooltip from "../../components/ShadTooltipComponent"; import ShadTooltip from "../../components/ShadTooltipComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import Header from "../../components/headerComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { Checkbox } from "../../components/ui/checkbox"; import { Checkbox } from "../../components/ui/checkbox";
import { Input } from "../../components/ui/input"; import { Input } from "../../components/ui/input";
@ -25,9 +26,8 @@ import {
} from "../../controllers/API"; } from "../../controllers/API";
import ConfirmationModal from "../../modals/ConfirmationModal"; import ConfirmationModal from "../../modals/ConfirmationModal";
import UserManagementModal from "../../modals/UserManagementModal"; import UserManagementModal from "../../modals/UserManagementModal";
import { UserInputType } from "../../types/components";
import Header from "../../components/headerComponent";
import { Users } from "../../types/api"; import { Users } from "../../types/api";
import { UserInputType } from "../../types/components";
export default function AdminPage() { export default function AdminPage() {
const [inputValue, setInputValue] = useState(""); const [inputValue, setInputValue] = useState("");
@ -89,7 +89,7 @@ export default function AdminPage() {
if (input === "") { if (input === "") {
setFilterUserList(userList.current); setFilterUserList(userList.current);
} else { } else {
const filteredList = userList.current.filter((user:Users) => const filteredList = userList.current.filter((user: Users) =>
user.username.toLowerCase().includes(input.toLowerCase()) user.username.toLowerCase().includes(input.toLowerCase())
); );
setFilterUserList(filteredList); setFilterUserList(filteredList);
@ -183,249 +183,254 @@ export default function AdminPage() {
return ( return (
<> <>
<div className="flex flex-col"> <div className="flex flex-col">
<Header /> <Header />
{userData && ( {userData && (
<div className="main-page-panel"> <div className="main-page-panel">
<div className="m-auto flex h-full flex-row justify-center"> <div className="m-auto flex h-full flex-row justify-center">
<div className="basis-5/6"> <div className="basis-5/6">
<div className="m-auto flex h-full flex-col space-y-8 p-8 "> <div className="m-auto flex h-full flex-col space-y-8 p-8 ">
<div className="flex items-center justify-between space-y-2"> <div className="flex items-center justify-between space-y-2">
<div> <div>
<h2 className="text-2xl font-bold tracking-tight"> <h2 className="text-2xl font-bold tracking-tight">
Welcome back! Welcome back!
</h2> </h2>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
Navigate through this section to efficiently oversee all Navigate through this section to efficiently oversee all
application users. From here, you can seamlessly manage application users. From here, you can seamlessly manage
user accounts. user accounts.
</p> </p>
</div>
<div className="flex items-center space-x-2"></div>
</div> </div>
<div className="flex items-center space-x-2"></div>
</div>
{userList.current.length === 0 && !loadingUsers && ( {userList.current.length === 0 && !loadingUsers && (
<>
<div className="flex items-center justify-between">
<h2>There's no users registered :)</h2>
</div>
</>
)}
<> <>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h2>There's no users registered :)</h2> <div className="flex flex-1 items-center space-x-2">
</div> <Input
</> value={inputValue}
)} placeholder="Filter users..."
<> className="h-8 w-[150px] lg:w-[250px]"
<div className="flex items-center justify-between"> onChange={(e) => handleFilterUsers(e.target.value)}
<div className="flex flex-1 items-center space-x-2"> />
<Input {inputValue.length > 0 && (
value={inputValue} <Button
placeholder="Filter users..." onClick={() => {
className="h-8 w-[150px] lg:w-[250px]" setInputValue("");
onChange={(e) => handleFilterUsers(e.target.value)} setFilterUserList(userList.current);
/> }}
{inputValue.length > 0 && ( variant="ghost"
<Button className="h-8 px-2 lg:px-3"
onClick={() => { >
setInputValue(""); Reset
setFilterUserList(userList.current); <X className="ml-2 h-4 w-4" />
</Button>
)}
</div>
<div>
<UserManagementModal
title="New User"
titleHeader={"Add a new user"}
cancelText="Cancel"
confirmationText="Save"
icon={"UserPlus2"}
onConfirm={(index, user) => {
handleNewUser(user);
}} }}
variant="ghost"
className="h-8 px-2 lg:px-3"
> >
Reset <Button>New User</Button>
<X className="ml-2 h-4 w-4" /> </UserManagementModal>
</Button> </div>
)}
</div> </div>
<div> {loadingUsers && (
<UserManagementModal <div>
title="New User" <strong>Loading...</strong>
titleHeader={"Add a new user"} </div>
cancelText="Cancel" )}
confirmationText="Save" <div
icon={"UserPlus2"} className={
onConfirm={(index, user) => { "max-h-[26rem] min-h-[26rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
handleNewUser(user); (loadingUsers ? " border-0" : "")
}} }
> >
<Button>New User</Button> <Table className={"table-fixed bg-muted outline-1"}>
</UserManagementModal> <TableHeader
</div> className={
</div> loadingUsers
{loadingUsers && ( ? "hidden"
<div> : "table-fixed bg-muted outline-1"
<strong>Loading...</strong> }
</div> >
)} <TableRow>
<div <TableHead className="h-10">Id</TableHead>
className={ <TableHead className="h-10">Username</TableHead>
"max-h-[26rem] min-h-[26rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" + <TableHead className="h-10">Active</TableHead>
(loadingUsers ? " border-0" : "") <TableHead className="h-10">Superuser</TableHead>
} <TableHead className="h-10">Created At</TableHead>
> <TableHead className="h-10">Updated At</TableHead>
<Table className={"table-fixed bg-muted outline-1"}> <TableHead className="h-10 w-[100px] text-right"></TableHead>
<TableHeader </TableRow>
className={ </TableHeader>
loadingUsers {!loadingUsers && (
? "hidden" <TableBody>
: "table-fixed bg-muted outline-1" {filterUserList.map(
} (user: UserInputType, index) => (
> <TableRow key={index}>
<TableRow> <TableCell className="truncate py-2 font-medium">
<TableHead className="h-10">Id</TableHead> <ShadTooltip content={user.id}>
<TableHead className="h-10">Username</TableHead> <span className="cursor-default">
<TableHead className="h-10">Active</TableHead> {user.id}
<TableHead className="h-10">Superuser</TableHead> </span>
<TableHead className="h-10">Created At</TableHead>
<TableHead className="h-10">Updated At</TableHead>
<TableHead className="h-10 w-[100px] text-right"></TableHead>
</TableRow>
</TableHeader>
{!loadingUsers && (
<TableBody>
{filterUserList.map((user:UserInputType, index) => (
<TableRow key={index}>
<TableCell className="truncate py-2 font-medium">
<ShadTooltip content={user.id}>
<span className="cursor-default">
{user.id}
</span>
</ShadTooltip>
</TableCell>
<TableCell className="truncate py-2">
<ShadTooltip content={user.username}>
<span className="cursor-default">
{user.username}
</span>
</ShadTooltip>
</TableCell>
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
<ConfirmationModal
title="Edit"
titleHeader={`${user.username}`}
modalContentTitle="Attention!"
modalContent="Are you completely confident about the changes you are making to this user?"
cancelText="Cancel"
confirmationText="Confirm"
icon={"UserCog2"}
data={user}
index={index}
onConfirm={(index, user) => {
handleDisableUser(
user.is_active,
user.id,
user
);
}}
>
<Checkbox
id="is_active"
checked={user.is_active}
/>
</ConfirmationModal>
</TableCell>
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
<ConfirmationModal
title="Edit"
titleHeader={`${user.username}`}
modalContentTitle="Attention!"
modalContent="Are you completely confident about the changes you are making to this user?"
cancelText="Cancel"
confirmationText="Confirm"
icon={"UserCog2"}
data={user}
index={index}
onConfirm={(index, user) => {
handleSuperUserEdit(
user.is_superuser,
user.id,
user
);
}}
>
<Checkbox
id="is_superuser"
checked={user.is_superuser}
/>
</ConfirmationModal>
</TableCell>
<TableCell className="truncate py-2 ">
{
new Date(user.create_at!)
.toISOString()
.split("T")[0]
}
</TableCell>
<TableCell className="truncate py-2">
{
new Date(user.updated_at!)
.toISOString()
.split("T")[0]
}
</TableCell>
<TableCell className="flex w-[100px] py-2 text-right">
<div className="flex">
<UserManagementModal
title="Edit"
titleHeader={`${user.id}`}
cancelText="Cancel"
confirmationText="Save"
icon={"UserPlus2"}
data={user}
index={index}
onConfirm={(index, editUser) => {
handleEditUser(user.id, editUser);
}}
>
<ShadTooltip content="Edit" side="top">
<IconComponent
name="Pencil"
className="h-4 w-4 cursor-pointer"
/>
</ShadTooltip> </ShadTooltip>
</UserManagementModal> </TableCell>
<TableCell className="truncate py-2">
<ConfirmationModal <ShadTooltip content={user.username}>
title="Delete" <span className="cursor-default">
titleHeader="Delete User" {user.username}
modalContentTitle="Attention!" </span>
modalContent="Are you sure you want to delete this user? This action cannot be undone."
cancelText="Cancel"
confirmationText="Delete"
icon={"UserMinus2"}
data={user}
index={index}
onConfirm={(index, user) => {
handleDeleteUser(user);
}}
>
<ShadTooltip content="Delete" side="top">
<IconComponent
name="Trash2"
className="ml-2 h-4 w-4 cursor-pointer"
/>
</ShadTooltip> </ShadTooltip>
</ConfirmationModal> </TableCell>
</div> <TableCell className="relative left-5 truncate py-2 text-align-last-left">
</TableCell> <ConfirmationModal
</TableRow> title="Edit"
))} titleHeader={`${user.username}`}
</TableBody> modalContentTitle="Attention!"
)} modalContent="Are you completely confident about the changes you are making to this user?"
</Table> cancelText="Cancel"
</div> confirmationText="Confirm"
icon={"UserCog2"}
data={user}
index={index}
onConfirm={(index, user) => {
handleDisableUser(
user.is_active,
user.id,
user
);
}}
>
<Checkbox
id="is_active"
checked={user.is_active}
/>
</ConfirmationModal>
</TableCell>
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
<ConfirmationModal
title="Edit"
titleHeader={`${user.username}`}
modalContentTitle="Attention!"
modalContent="Are you completely confident about the changes you are making to this user?"
cancelText="Cancel"
confirmationText="Confirm"
icon={"UserCog2"}
data={user}
index={index}
onConfirm={(index, user) => {
handleSuperUserEdit(
user.is_superuser,
user.id,
user
);
}}
>
<Checkbox
id="is_superuser"
checked={user.is_superuser}
/>
</ConfirmationModal>
</TableCell>
<TableCell className="truncate py-2 ">
{
new Date(user.create_at!)
.toISOString()
.split("T")[0]
}
</TableCell>
<TableCell className="truncate py-2">
{
new Date(user.updated_at!)
.toISOString()
.split("T")[0]
}
</TableCell>
<TableCell className="flex w-[100px] py-2 text-right">
<div className="flex">
<UserManagementModal
title="Edit"
titleHeader={`${user.id}`}
cancelText="Cancel"
confirmationText="Save"
icon={"UserPlus2"}
data={user}
index={index}
onConfirm={(index, editUser) => {
handleEditUser(user.id, editUser);
}}
>
<ShadTooltip content="Edit" side="top">
<IconComponent
name="Pencil"
className="h-4 w-4 cursor-pointer"
/>
</ShadTooltip>
</UserManagementModal>
<PaginatorComponent <ConfirmationModal
pageIndex={index} title="Delete"
pageSize={size} titleHeader="Delete User"
totalRowsCount={totalRowsCount} modalContentTitle="Attention!"
paginate={(pageIndex, pageSize) => { modalContent="Are you sure you want to delete this user? This action cannot be undone."
handleChangePagination(pageSize, pageIndex); cancelText="Cancel"
}} confirmationText="Delete"
></PaginatorComponent> icon={"UserMinus2"}
</> data={user}
index={index}
onConfirm={(index, user) => {
handleDeleteUser(user);
}}
>
<ShadTooltip
content="Delete"
side="top"
>
<IconComponent
name="Trash2"
className="ml-2 h-4 w-4 cursor-pointer"
/>
</ShadTooltip>
</ConfirmationModal>
</div>
</TableCell>
</TableRow>
)
)}
</TableBody>
)}
</Table>
</div>
<PaginatorComponent
pageIndex={index}
pageSize={size}
totalRowsCount={totalRowsCount}
paginate={(pageIndex, pageSize) => {
handleChangePagination(pageSize, pageIndex);
}}
></PaginatorComponent>
</>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> )}
)}
</div> </div>
</> </>
); );

View file

@ -421,7 +421,7 @@ export default function Page({
zoomOnScroll={!view} zoomOnScroll={!view}
zoomOnPinch={!view} zoomOnPinch={!view}
panOnDrag={!view} panOnDrag={!view}
proOptions={{hideAttribution: true}} proOptions={{ hideAttribution: true }}
> >
<Background className="" /> <Background className="" />
{!view && ( {!view && (

View file

@ -21,8 +21,7 @@ export default function ExtraSidebar(): JSX.Element {
const { data, templates } = useContext(typesContext); const { data, templates } = useContext(typesContext);
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } = const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
useContext(TabsContext); useContext(TabsContext);
const { setSuccessData, setErrorData } = const { setSuccessData, setErrorData } = useContext(alertContext);
useContext(alertContext);
const [dataFilter, setFilterData] = useState(data); const [dataFilter, setFilterData] = useState(data);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const isPending = tabsState[tabId]?.isPending; const isPending = tabsState[tabId]?.isPending;
@ -101,9 +100,7 @@ export default function ExtraSidebar(): JSX.Element {
<div className="side-bar-button"> <div className="side-bar-button">
{flow && flow.data && ( {flow && flow.data && (
<ApiModal flow={flow} disable={!isBuilt}> <ApiModal flow={flow} disable={!isBuilt}>
<div <div className={classNames("extra-side-bar-buttons")}>
className={classNames("extra-side-bar-buttons")}
>
<IconComponent <IconComponent
name="Code2" name="Code2"
className={ className={

View file

@ -1,14 +1,33 @@
import { useContext, useEffect } from "react"; import { useContext, useEffect } from "react";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import DropdownButton from "../../components/DropdownButtonComponent";
import { CardComponent } from "../../components/cardComponent"; import { CardComponent } from "../../components/cardComponent";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import Header from "../../components/headerComponent"; import Header from "../../components/headerComponent";
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { USER_PROJECTS_HEADER } from "../../constants/constants"; import { USER_PROJECTS_HEADER } from "../../constants/constants";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
export default function HomePage(): JSX.Element { export default function HomePage(): JSX.Element {
const { flows, setTabId, downloadFlows, uploadFlows, addFlow, removeFlow } = const {
useContext(TabsContext); flows,
setTabId,
downloadFlows,
uploadFlows,
addFlow,
removeFlow,
uploadFlow,
isLoading,
} = useContext(TabsContext);
const dropdownOptions = [
{
name: "Import from JSON",
onBtnClick: () =>
uploadFlow(true).then((id) => {
navigate("/flow/" + id);
}),
},
];
// Set a null id // Set a null id
useEffect(() => { useEffect(() => {
@ -16,6 +35,10 @@ export default function HomePage(): JSX.Element {
}, []); }, []);
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => {
console.log(isLoading);
}, [isLoading]);
// Personal flows display // Personal flows display
return ( return (
<> <>
@ -45,48 +68,55 @@ export default function HomePage(): JSX.Element {
<IconComponent name="Upload" className="main-page-nav-button" /> <IconComponent name="Upload" className="main-page-nav-button" />
Upload Collection Upload Collection
</Button> </Button>
<Button <DropdownButton
variant="primary" firstButtonName="New Project"
onClick={() => { onFirstBtnClick={() => {
addFlow(null!, true).then((id) => { addFlow(null!, true).then((id) => {
navigate("/flow/" + id); navigate("/flow/" + id);
}); });
}} }}
> options={dropdownOptions}
<IconComponent name="Plus" className="main-page-nav-button" /> />
New Project
</Button>
</div> </div>
</div> </div>
<span className="main-page-description-text"> <span className="main-page-description-text">
Manage your personal projects. Download or upload your collection. Manage your personal projects. Download or upload your collection.
</span> </span>
<div className="main-page-flows-display"> <div className="main-page-flows-display">
{flows.map((flow, idx) => ( {isLoading && flows.length == 0 ? (
<CardComponent <>
key={idx} <SkeletonCardComponent />
flow={flow} <SkeletonCardComponent />
id={flow.id} <SkeletonCardComponent />
button={ <SkeletonCardComponent />
<Link to={"/flow/" + flow.id}> </>
<Button ) : (
variant="outline" flows.map((flow, idx) => (
size="sm" <CardComponent
className="whitespace-nowrap " key={idx}
> flow={flow}
<IconComponent id={flow.id}
name="ExternalLink" button={
className="main-page-nav-button" <Link to={"/flow/" + flow.id}>
/> <Button
Edit Flow variant="outline"
</Button> size="sm"
</Link> className="whitespace-nowrap "
} >
onDelete={() => { <IconComponent
removeFlow(flow.id); name="ExternalLink"
}} className="main-page-nav-button"
/> />
))} Edit Flow
</Button>
</Link>
}
onDelete={() => {
removeFlow(flow.id);
}}
/>
))
)}
</div> </div>
</div> </div>
</> </>

View file

@ -19,7 +19,8 @@ export default function LoginPage(): JSX.Element {
useState<loginInputStateType>(CONTROL_LOGIN_STATE); useState<loginInputStateType>(CONTROL_LOGIN_STATE);
const { password, username } = inputState; const { password, username } = inputState;
const { login, getAuthentication, setUserData, setIsAdmin } = useContext(AuthContext); const { login, getAuthentication, setUserData, setIsAdmin } =
useContext(AuthContext);
const navigate = useNavigate(); const navigate = useNavigate();
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
@ -31,8 +32,8 @@ export default function LoginPage(): JSX.Element {
function signIn() { function signIn() {
const user: LoginType = { const user: LoginType = {
username: username, username: username.trim(),
password: password, password: password.trim(),
}; };
onLogin(user) onLogin(user)
.then((user) => { .then((user) => {
@ -89,6 +90,7 @@ export default function LoginPage(): JSX.Element {
<Form.Control asChild> <Form.Control asChild>
<Input <Input
type="username"
onChange={({ target: { value } }) => { onChange={({ target: { value } }) => {
handleInput({ target: { name: "username", value } }); handleInput({ target: { name: "username", value } });
}} }}
@ -129,12 +131,14 @@ export default function LoginPage(): JSX.Element {
</div> </div>
<div className="w-full"> <div className="w-full">
<Form.Submit asChild> <Form.Submit asChild>
<Button className="mr-3 mt-6 w-full">Sign in</Button> <Button className="mr-3 mt-6 w-full" type="submit">
Sign in
</Button>
</Form.Submit> </Form.Submit>
</div> </div>
<div className="w-full"> <div className="w-full">
<Link to="/signup"> <Link to="/signup">
<Button className="w-full" variant="outline"> <Button className="w-full" variant="outline" type="button">
Don't have an account?&nbsp;<b>Sign Up</b> Don't have an account?&nbsp;<b>Sign Up</b>
</Button> </Button>
</Link> </Link>

View file

@ -33,8 +33,8 @@ export default function SignUp(): JSX.Element {
function handleSignup(): void { function handleSignup(): void {
const { username, password } = inputState; const { username, password } = inputState;
const newUser: UserInputType = { const newUser: UserInputType = {
username, username: username.trim(),
password, password: password.trim(),
}; };
addUser(newUser) addUser(newUser)
.then((user) => { .then((user) => {
@ -84,6 +84,7 @@ export default function SignUp(): JSX.Element {
<Form.Control asChild> <Form.Control asChild>
<Input <Input
type="username"
onChange={({ target: { value } }) => { onChange={({ target: { value } }) => {
handleInput({ target: { name: "username", value } }); handleInput({ target: { name: "username", value } });
}} }}
@ -157,6 +158,7 @@ export default function SignUp(): JSX.Element {
<div className="w-full"> <div className="w-full">
<Form.Submit asChild> <Form.Submit asChild>
<Button <Button
type="submit"
className="mr-3 mt-6 w-full" className="mr-3 mt-6 w-full"
onClick={() => { onClick={() => {
handleSignup(); handleSignup();

View file

@ -126,6 +126,18 @@
@apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm; @apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
} }
.skeleton-card {
@apply bg-background h-48 p-4 border rounded-lg flex flex-col gap-6;
}
.skeleton-card-wrapper {
@apply flex items-center space-x-4;
}
.skeleton-card-text {
@apply flex flex-col gap-3;
}
/* The same as primary-input but no-truncate */ /* The same as primary-input but no-truncate */
.textarea-primary { .textarea-primary {
@apply form-input block w-full rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm; @apply form-input block w-full rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;

View file

@ -36,3 +36,18 @@ pre {
.gradient-start { .gradient-start {
animation: gradient-motion-start 4s infinite forwards; animation: gradient-motion-start 4s infinite forwards;
} }
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
-webkit-text-fill-color: black;
-webkit-box-shadow: 0 0 0px 1000px #fff6d0 inset;
box-shadow: 0 0 0px 1000px #fff6d0 inset;
color: black;
}

View file

@ -283,7 +283,7 @@ export type UserInputType = {
is_superuser?: boolean; is_superuser?: boolean;
id?: string; id?: string;
create_at?: string; create_at?: string;
updated_at?:string; updated_at?: string;
}; };
export type ApiKeyType = { export type ApiKeyType = {
@ -559,3 +559,9 @@ export type fetchErrorComponentType = {
message: string; message: string;
description: string; description: string;
}; };
export type dropdownButtonPropsType = {
firstButtonName: string;
onFirstBtnClick: () => void;
options: Array<{ name: string; onBtnClick: () => void }>;
};

View file

@ -5,6 +5,7 @@ export type TabsContextType = {
saveFlow: (flow: FlowType) => Promise<void>; saveFlow: (flow: FlowType) => Promise<void>;
save: () => void; save: () => void;
tabId: string; tabId: string;
isLoading: boolean;
setTabId: (index: string) => void; setTabId: (index: string) => void;
flows: Array<FlowType>; flows: Array<FlowType>;
removeFlow: (id: string) => void; removeFlow: (id: string) => void;
@ -23,7 +24,7 @@ export type TabsContextType = {
uploadFlows: () => void; uploadFlows: () => void;
isBuilt: boolean; isBuilt: boolean;
setIsBuilt: (state: boolean) => void; setIsBuilt: (state: boolean) => void;
uploadFlow: (newFlow?: boolean, file?: File) => void; uploadFlow: (newFlow?: boolean, file?: File) => Promise<String | undefined>;
hardReset: () => void; hardReset: () => void;
getNodeId: (nodeType: string) => string; getNodeId: (nodeType: string) => string;
tabsState: TabsState; tabsState: TabsState;

View file

@ -5,6 +5,7 @@ import {
ChevronDown, ChevronDown,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
ChevronUp,
ChevronsLeft, ChevronsLeft,
ChevronsRight, ChevronsRight,
ChevronsUpDown, ChevronsUpDown,
@ -301,5 +302,6 @@ export const nodeIconsLucide: iconsType = {
UserCog2, UserCog2,
Key, Key,
Unplug, Unplug,
BookMarked BookMarked,
ChevronUp
}; };

View file

@ -9,7 +9,13 @@ from langflow.services.database.models.user import UserUpdate
@pytest.fixture @pytest.fixture
def super_user(client, session): def super_user(client, session):
return create_super_user(session) settings_manager = get_settings_manager()
auth_settings = settings_manager.auth_settings
return create_super_user(
db=session,
username=auth_settings.FIRST_SUPERUSER,
password=auth_settings.FIRST_SUPERUSER_PASSWORD,
)
@pytest.fixture @pytest.fixture