Merge branch 'saveComponent' into feature/store
This commit is contained in:
commit
e473570c5d
58 changed files with 6451 additions and 1281 deletions
|
|
@ -0,0 +1,48 @@
|
|||
"""Store updates
|
||||
|
||||
Revision ID: 7843803a87b5
|
||||
Revises: eb5866d51fd2
|
||||
Create Date: 2023-10-18 23:08:57.744906
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "7843803a87b5"
|
||||
down_revision: Union[str, None] = "eb5866d51fd2"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
try:
|
||||
with op.batch_alter_table("flow", schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column("is_component", sa.Boolean(), nullable=True))
|
||||
|
||||
with op.batch_alter_table("user", schema=None) as batch_op:
|
||||
batch_op.add_column(
|
||||
sa.Column(
|
||||
"store_api_key", sqlmodel.sql.sqltypes.AutoString(), nullable=True
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("user", schema=None) as batch_op:
|
||||
batch_op.drop_column("store_api_key")
|
||||
|
||||
with op.batch_alter_table("flow", schema=None) as batch_op:
|
||||
batch_op.drop_column("is_component")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
"""User id can be null in Flow
|
||||
|
||||
Revision ID: f5ee9749d1a6
|
||||
Revises: 7843803a87b5
|
||||
Create Date: 2023-10-18 23:12:27.297016
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "f5ee9749d1a6"
|
||||
down_revision: Union[str, None] = "7843803a87b5"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
try:
|
||||
with op.batch_alter_table("flow", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"user_id", existing_type=sa.CHAR(length=32), nullable=True
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("flow", schema=None) as batch_op:
|
||||
batch_op.alter_column(
|
||||
"user_id", existing_type=sa.CHAR(length=32), nullable=False
|
||||
)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -5,7 +5,7 @@ from langflow.api.v1 import (
|
|||
endpoints_router,
|
||||
validate_router,
|
||||
flows_router,
|
||||
component_router,
|
||||
store_router,
|
||||
users_router,
|
||||
api_key_router,
|
||||
login_router,
|
||||
|
|
@ -17,7 +17,7 @@ router = APIRouter(
|
|||
router.include_router(chat_router)
|
||||
router.include_router(endpoints_router)
|
||||
router.include_router(validate_router)
|
||||
router.include_router(component_router)
|
||||
router.include_router(store_router)
|
||||
router.include_router(flows_router)
|
||||
router.include_router(users_router)
|
||||
router.include_router(api_key_router)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from langflow.api.v1.endpoints import router as endpoints_router
|
|||
from langflow.api.v1.validate import router as validate_router
|
||||
from langflow.api.v1.chat import router as chat_router
|
||||
from langflow.api.v1.flows import router as flows_router
|
||||
from langflow.api.v1.components import router as component_router
|
||||
from langflow.api.v1.store import router as store_router
|
||||
from langflow.api.v1.users import router as users_router
|
||||
from langflow.api.v1.api_key import router as api_key_router
|
||||
from langflow.api.v1.login import router as login_router
|
||||
|
|
@ -10,7 +10,7 @@ from langflow.api.v1.login import router as login_router
|
|||
__all__ = [
|
||||
"chat_router",
|
||||
"endpoints_router",
|
||||
"component_router",
|
||||
"store_router",
|
||||
"validate_router",
|
||||
"flows_router",
|
||||
"users_router",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from uuid import UUID
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from langflow.api.v1.schemas import ApiKeysResponse
|
||||
from langflow.services.auth.utils import get_current_active_user
|
||||
from langflow.api.v1.schemas import ApiKeysResponse, ApiKeyCreateRequest
|
||||
from langflow.services.auth import utils as auth_utils
|
||||
from langflow.services.database.models.api_key.api_key import (
|
||||
ApiKeyCreate,
|
||||
UnmaskedApiKeyRead,
|
||||
|
|
@ -14,9 +14,17 @@ from langflow.services.database.models.api_key.crud import (
|
|||
delete_api_key,
|
||||
)
|
||||
from langflow.services.database.models.user.user import User
|
||||
from langflow.services.deps import get_session
|
||||
from langflow.services.deps import (
|
||||
get_session,
|
||||
get_settings_service,
|
||||
)
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
from sqlmodel import Session
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
|
||||
router = APIRouter(tags=["APIKey"], prefix="/api_key")
|
||||
|
||||
|
|
@ -24,7 +32,7 @@ router = APIRouter(tags=["APIKey"], prefix="/api_key")
|
|||
@router.get("/", response_model=ApiKeysResponse)
|
||||
def get_api_keys_route(
|
||||
db: Session = Depends(get_session),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
current_user: User = Depends(auth_utils.get_current_active_user),
|
||||
):
|
||||
try:
|
||||
user_id = current_user.id
|
||||
|
|
@ -38,7 +46,7 @@ def get_api_keys_route(
|
|||
@router.post("/", response_model=UnmaskedApiKeyRead)
|
||||
def create_api_key_route(
|
||||
req: ApiKeyCreate,
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
current_user: User = Depends(auth_utils.get_current_active_user),
|
||||
db: Session = Depends(get_session),
|
||||
):
|
||||
try:
|
||||
|
|
@ -51,7 +59,7 @@ def create_api_key_route(
|
|||
@router.delete("/{api_key_id}")
|
||||
def delete_api_key_route(
|
||||
api_key_id: UUID,
|
||||
current_user=Depends(get_current_active_user),
|
||||
current_user=Depends(auth_utils.get_current_active_user),
|
||||
db: Session = Depends(get_session),
|
||||
):
|
||||
try:
|
||||
|
|
@ -59,3 +67,22 @@ def delete_api_key_route(
|
|||
return {"detail": "API Key deleted"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
|
||||
|
||||
@router.post("/store")
|
||||
def save_store_api_key(
|
||||
api_key: ApiKeyCreateRequest,
|
||||
current_user: User = Depends(auth_utils.get_current_active_user),
|
||||
db: Session = Depends(get_session),
|
||||
settings_service=Depends(get_settings_service),
|
||||
):
|
||||
try:
|
||||
# Encrypt the API key
|
||||
encrypted = auth_utils.encrypt_api_key(
|
||||
api_key, fernet=auth_utils.get_fernet(settings_service)
|
||||
)
|
||||
current_user.store_api_key = encrypted
|
||||
db.commit()
|
||||
return {"detail": "API Key saved"}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=400, detail=str(e)) from e
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
from datetime import timezone
|
||||
from typing import List
|
||||
from uuid import UUID
|
||||
from langflow.services.database.models.component import Component, ComponentModel
|
||||
from langflow.services.deps import get_session
|
||||
from sqlmodel import Session, select
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
COMPONENT_NOT_FOUND = "Component not found"
|
||||
COMPONENT_ALREADY_EXISTS = "A component with the same id already exists."
|
||||
COMPONENT_DELETED = "Component deleted"
|
||||
|
||||
|
||||
router = APIRouter(prefix="/components", tags=["Components"])
|
||||
|
||||
|
||||
@router.post("/", response_model=Component)
|
||||
def create_component(component: ComponentModel, db: Session = Depends(get_session)):
|
||||
db_component = Component(**component.dict())
|
||||
try:
|
||||
db.add(db_component)
|
||||
db.commit()
|
||||
db.refresh(db_component)
|
||||
except IntegrityError as e:
|
||||
db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=COMPONENT_ALREADY_EXISTS,
|
||||
) from e
|
||||
return db_component
|
||||
|
||||
|
||||
@router.get("/{component_id}", response_model=Component)
|
||||
def read_component(component_id: UUID, db: Session = Depends(get_session)):
|
||||
if component := db.get(Component, component_id):
|
||||
return component
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=COMPONENT_NOT_FOUND)
|
||||
|
||||
|
||||
@router.get("/", response_model=List[Component])
|
||||
def read_components(skip: int = 0, limit: int = 50, db: Session = Depends(get_session)):
|
||||
query = select(Component)
|
||||
query = query.offset(skip).limit(limit)
|
||||
|
||||
return db.execute(query).fetchall()
|
||||
|
||||
|
||||
@router.patch("/{component_id}", response_model=Component)
|
||||
def update_component(
|
||||
component_id: UUID, component: ComponentModel, db: Session = Depends(get_session)
|
||||
):
|
||||
db_component = db.get(Component, component_id)
|
||||
if not db_component:
|
||||
raise HTTPException(status_code=404, detail=COMPONENT_NOT_FOUND)
|
||||
component_data = component.dict(exclude_unset=True)
|
||||
|
||||
for key, value in component_data.items():
|
||||
setattr(db_component, key, value)
|
||||
|
||||
db_component.update_at = datetime.now(timezone.utc)
|
||||
db.commit()
|
||||
db.refresh(db_component)
|
||||
return db_component
|
||||
|
||||
|
||||
@router.delete("/{component_id}")
|
||||
def delete_component(component_id: UUID, db: Session = Depends(get_session)):
|
||||
component = db.get(Component, component_id)
|
||||
if not component:
|
||||
raise HTTPException(status_code=404, detail=COMPONENT_NOT_FOUND)
|
||||
db.delete(component)
|
||||
db.commit()
|
||||
return {"detail": COMPONENT_DELETED}
|
||||
|
|
@ -198,3 +198,7 @@ class Token(BaseModel):
|
|||
access_token: str
|
||||
refresh_token: str
|
||||
token_type: str
|
||||
|
||||
|
||||
class ApiKeyCreateRequest(BaseModel):
|
||||
api_key: str
|
||||
|
|
|
|||
120
src/backend/langflow/api/v1/store.py
Normal file
120
src/backend/langflow/api/v1/store.py
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
from typing import List, Optional
|
||||
from uuid import UUID
|
||||
from langflow.services.auth import utils as auth_utils
|
||||
from langflow.services.database.models.flow.flow import Flow
|
||||
from langflow.services.database.models.user.user import User
|
||||
from langflow.services.deps import (
|
||||
get_session,
|
||||
get_store_service,
|
||||
get_settings_service,
|
||||
)
|
||||
from langflow.services.store.schema import ComponentResponse
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from datetime import datetime
|
||||
|
||||
from langflow.services.store.service import StoreService
|
||||
|
||||
|
||||
router = APIRouter(prefix="/store", tags=["Components Store"])
|
||||
|
||||
|
||||
@router.post("/", response_model=ComponentResponse)
|
||||
def create_component(
|
||||
component: Flow,
|
||||
store_service: StoreService = Depends(get_store_service),
|
||||
user=Depends(auth_utils.get_current_active_user),
|
||||
settings_service=Depends(get_settings_service),
|
||||
):
|
||||
try:
|
||||
api_key = user.store_api_key
|
||||
decrypted = auth_utils.decrypt_api_key(api_key, settings_service)
|
||||
return store_service.upload(decrypted, component.dict())
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc))
|
||||
|
||||
|
||||
@router.get("/{component_id}", response_model=ComponentResponse)
|
||||
def read_component(
|
||||
component_id: UUID,
|
||||
store_service: StoreService = Depends(get_store_service),
|
||||
user: User = Depends(auth_utils.get_current_active_user),
|
||||
session=Depends(get_session),
|
||||
):
|
||||
if not user.store_api_key:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="You must have a store API key set."
|
||||
)
|
||||
# If the component is from the store, we need to get it from the store
|
||||
try:
|
||||
api_key = user.store_api_key
|
||||
component = store_service.get(api_key, component_id)
|
||||
if component is not None:
|
||||
# Turn component into a Flow
|
||||
required_fields = ["data", "name", "description", "is_component"]
|
||||
if all(field in component for field in required_fields):
|
||||
component = Flow(
|
||||
name=component["name"],
|
||||
description=component["description"],
|
||||
data=component["data"],
|
||||
user_id=user.id,
|
||||
)
|
||||
session.add(component)
|
||||
session.commit()
|
||||
session.refresh(component)
|
||||
except Exception as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
|
||||
if component is None:
|
||||
raise HTTPException(status_code=400, detail="Component not found")
|
||||
return component
|
||||
|
||||
|
||||
@router.get("/", response_model=List[ComponentResponse])
|
||||
def list_components(
|
||||
page: int = 1,
|
||||
limit: int = 10,
|
||||
store_service: StoreService = Depends(get_store_service),
|
||||
user=Depends(auth_utils.get_current_active_user),
|
||||
settings_service=Depends(get_settings_service),
|
||||
):
|
||||
if user.store_api_key:
|
||||
decrypted = auth_utils.decrypt_api_key(user.store_api_key, settings_service)
|
||||
else:
|
||||
decrypted = None
|
||||
return store_service.list_components(decrypted, page, limit)
|
||||
|
||||
|
||||
@router.get("/search", response_model=List[ComponentResponse])
|
||||
async def search_endpoint(
|
||||
api_key: Optional[str] = Query(None),
|
||||
query: str = Query(...),
|
||||
page: int = Query(1),
|
||||
limit: int = Query(10),
|
||||
status: Optional[str] = Query(None),
|
||||
tags: Optional[List[str]] = Query(None),
|
||||
date_from: Optional[datetime] = Query(None),
|
||||
date_to: Optional[datetime] = Query(None),
|
||||
sort_by: Optional[str] = Query("likes"),
|
||||
sort: Optional[List[str]] = Query(None),
|
||||
fields: Optional[List[str]] = Query(None),
|
||||
store_service: "StoreService" = Depends(get_store_service),
|
||||
):
|
||||
try:
|
||||
return await store_service.search(
|
||||
api_key=api_key,
|
||||
query=query,
|
||||
page=page,
|
||||
limit=limit,
|
||||
status=status,
|
||||
tags=tags,
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
sort_by=sort_by,
|
||||
sort=sort,
|
||||
fields=fields,
|
||||
)
|
||||
except Exception as exc:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)
|
||||
)
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
from typing import Optional
|
||||
from langflow import CustomComponent
|
||||
from langchain.chat_models.baidu_qianfan_endpoint import QianfanChatEndpoint
|
||||
from langchain.llms.base import BaseLLM
|
||||
|
||||
|
||||
class QianfanChatEndpointComponent(CustomComponent):
|
||||
display_name: str = "QianfanChatEndpoint"
|
||||
description: str = (
|
||||
"Baidu Qianfan chat models. Get more detail from "
|
||||
"https://python.langchain.com/docs/integrations/chat/baidu_qianfan_endpoint."
|
||||
)
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"model": {
|
||||
"display_name": "Model Name",
|
||||
"options": [
|
||||
"ERNIE-Bot",
|
||||
"ERNIE-Bot-turbo",
|
||||
"BLOOMZ-7B",
|
||||
"Llama-2-7b-chat",
|
||||
"Llama-2-13b-chat",
|
||||
"Llama-2-70b-chat",
|
||||
"Qianfan-BLOOMZ-7B-compressed",
|
||||
"Qianfan-Chinese-Llama-2-7B",
|
||||
"ChatGLM2-6B-32K",
|
||||
"AquilaChat-7B",
|
||||
],
|
||||
"info": "https://python.langchain.com/docs/integrations/chat/baidu_qianfan_endpoint",
|
||||
"required": True,
|
||||
},
|
||||
"qianfan_ak": {
|
||||
"display_name": "Qianfan Ak",
|
||||
"required": True,
|
||||
"password": True,
|
||||
"info": "which you could get from https://cloud.baidu.com/product/wenxinworkshop",
|
||||
},
|
||||
"qianfan_sk": {
|
||||
"display_name": "Qianfan Sk",
|
||||
"required": True,
|
||||
"password": True,
|
||||
"info": "which you could get from https://cloud.baidu.com/product/wenxinworkshop",
|
||||
},
|
||||
"top_p": {
|
||||
"display_name": "Top p",
|
||||
"field_type": "float",
|
||||
"info": "Model params, only supported in ERNIE-Bot and ERNIE-Bot-turbo",
|
||||
"value": 0.8,
|
||||
},
|
||||
"temperature": {
|
||||
"display_name": "Temperature",
|
||||
"field_type": "float",
|
||||
"info": "Model params, only supported in ERNIE-Bot and ERNIE-Bot-turbo",
|
||||
"value": 0.95,
|
||||
},
|
||||
"penalty_score": {
|
||||
"display_name": "Penalty Score",
|
||||
"field_type": "float",
|
||||
"info": "Model params, only supported in ERNIE-Bot and ERNIE-Bot-turbo",
|
||||
"value": 1.0,
|
||||
},
|
||||
"endpoint": {
|
||||
"display_name": "Endpoint",
|
||||
"info": "Endpoint of the Qianfan LLM, required if custom model used.",
|
||||
},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
model: str = "ERNIE-Bot-turbo",
|
||||
qianfan_ak: Optional[str] = None,
|
||||
qianfan_sk: Optional[str] = None,
|
||||
top_p: Optional[float] = None,
|
||||
temperature: Optional[float] = None,
|
||||
penalty_score: Optional[float] = None,
|
||||
endpoint: Optional[str] = None,
|
||||
) -> BaseLLM:
|
||||
try:
|
||||
output = QianfanChatEndpoint( # type: ignore
|
||||
model=model,
|
||||
qianfan_ak=qianfan_ak,
|
||||
qianfan_sk=qianfan_sk,
|
||||
top_p=top_p,
|
||||
temperature=temperature,
|
||||
penalty_score=penalty_score,
|
||||
endpoint=endpoint,
|
||||
)
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to Baidu Qianfan API.") from e
|
||||
return output # type: ignore
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
from typing import Optional
|
||||
from langflow import CustomComponent
|
||||
from langchain.llms.baidu_qianfan_endpoint import QianfanLLMEndpoint
|
||||
from langchain.llms.base import BaseLLM
|
||||
|
||||
|
||||
class QianfanLLMEndpointComponent(CustomComponent):
|
||||
display_name: str = "QianfanLLMEndpoint"
|
||||
description: str = (
|
||||
"Baidu Qianfan hosted open source or customized models. "
|
||||
"Get more detail from https://python.langchain.com/docs/integrations/chat/baidu_qianfan_endpoint"
|
||||
)
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"model": {
|
||||
"display_name": "Model Name",
|
||||
"options": [
|
||||
"ERNIE-Bot",
|
||||
"ERNIE-Bot-turbo",
|
||||
"BLOOMZ-7B",
|
||||
"Llama-2-7b-chat",
|
||||
"Llama-2-13b-chat",
|
||||
"Llama-2-70b-chat",
|
||||
"Qianfan-BLOOMZ-7B-compressed",
|
||||
"Qianfan-Chinese-Llama-2-7B",
|
||||
"ChatGLM2-6B-32K",
|
||||
"AquilaChat-7B",
|
||||
],
|
||||
"info": "https://python.langchain.com/docs/integrations/chat/baidu_qianfan_endpoint",
|
||||
"required": True,
|
||||
},
|
||||
"qianfan_ak": {
|
||||
"display_name": "Qianfan Ak",
|
||||
"required": True,
|
||||
"password": True,
|
||||
"info": "which you could get from https://cloud.baidu.com/product/wenxinworkshop",
|
||||
},
|
||||
"qianfan_sk": {
|
||||
"display_name": "Qianfan Sk",
|
||||
"required": True,
|
||||
"password": True,
|
||||
"info": "which you could get from https://cloud.baidu.com/product/wenxinworkshop",
|
||||
},
|
||||
"top_p": {
|
||||
"display_name": "Top p",
|
||||
"field_type": "float",
|
||||
"info": "Model params, only supported in ERNIE-Bot and ERNIE-Bot-turbo",
|
||||
"value": 0.8,
|
||||
},
|
||||
"temperature": {
|
||||
"display_name": "Temperature",
|
||||
"field_type": "float",
|
||||
"info": "Model params, only supported in ERNIE-Bot and ERNIE-Bot-turbo",
|
||||
"value": 0.95,
|
||||
},
|
||||
"penalty_score": {
|
||||
"display_name": "Penalty Score",
|
||||
"field_type": "float",
|
||||
"info": "Model params, only supported in ERNIE-Bot and ERNIE-Bot-turbo",
|
||||
"value": 1.0,
|
||||
},
|
||||
"endpoint": {
|
||||
"display_name": "Endpoint",
|
||||
"info": "Endpoint of the Qianfan LLM, required if custom model used.",
|
||||
},
|
||||
"code": {"show": False},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
model: str = "ERNIE-Bot-turbo",
|
||||
qianfan_ak: Optional[str] = None,
|
||||
qianfan_sk: Optional[str] = None,
|
||||
top_p: Optional[float] = None,
|
||||
temperature: Optional[float] = None,
|
||||
penalty_score: Optional[float] = None,
|
||||
endpoint: Optional[str] = None,
|
||||
) -> BaseLLM:
|
||||
try:
|
||||
output = QianfanLLMEndpoint( # type: ignore
|
||||
model=model,
|
||||
qianfan_ak=qianfan_ak,
|
||||
qianfan_sk=qianfan_sk,
|
||||
top_p=top_p,
|
||||
temperature=temperature,
|
||||
penalty_score=penalty_score,
|
||||
endpoint=endpoint,
|
||||
)
|
||||
except Exception as e:
|
||||
raise ValueError("Could not connect to Baidu Qianfan API.") from e
|
||||
return output # type: ignore
|
||||
|
|
@ -5,7 +5,6 @@ from langchain.vectorstores import Vectara
|
|||
from langchain.schema import Document
|
||||
from langchain.vectorstores.base import VectorStore
|
||||
from langchain.schema import BaseRetriever
|
||||
from langchain.embeddings.base import Embeddings
|
||||
|
||||
|
||||
class VectaraComponent(CustomComponent):
|
||||
|
|
@ -22,7 +21,6 @@ class VectaraComponent(CustomComponent):
|
|||
"vectara_api_key": {"display_name": "Vectara API Key", "password": True},
|
||||
"code": {"show": False},
|
||||
"documents": {"display_name": "Documents"},
|
||||
"embedding": {"display_name": "Embedding"},
|
||||
}
|
||||
|
||||
def build(
|
||||
|
|
@ -30,21 +28,21 @@ class VectaraComponent(CustomComponent):
|
|||
vectara_customer_id: str,
|
||||
vectara_corpus_id: str,
|
||||
vectara_api_key: str,
|
||||
embedding: Optional[Embeddings] = None,
|
||||
documents: Optional[Document] = None,
|
||||
) -> Union[VectorStore, BaseRetriever]:
|
||||
# If documents, then we need to create a Vectara instance using .from_documents
|
||||
if documents is not None and embedding is not None:
|
||||
if documents is not None:
|
||||
return Vectara.from_documents(
|
||||
documents=documents, # type: ignore
|
||||
vectara_customer_id=vectara_customer_id,
|
||||
vectara_corpus_id=vectara_corpus_id,
|
||||
vectara_api_key=vectara_api_key,
|
||||
embedding=embedding,
|
||||
source="langflow",
|
||||
)
|
||||
|
||||
return Vectara(
|
||||
vectara_customer_id=vectara_customer_id,
|
||||
vectara_corpus_id=vectara_corpus_id,
|
||||
vectara_api_key=vectara_api_key,
|
||||
source="langflow",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from langflow.services.database.models.user.crud import (
|
|||
)
|
||||
from langflow.services.deps import get_session, get_settings_service
|
||||
from sqlmodel import Session
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
oauth2_login = OAuth2PasswordBearer(tokenUrl="api/v1/login")
|
||||
|
||||
|
|
@ -294,3 +295,26 @@ def authenticate_user(
|
|||
raise HTTPException(status_code=400, detail="Inactive user")
|
||||
|
||||
return user if verify_password(password, user.password) else None
|
||||
|
||||
|
||||
def get_fernet(settings_service=Depends(get_settings_service)):
|
||||
SECRET_KEY = settings_service.auth_settings.SECRET_KEY
|
||||
# It's important that your secret key is 32 url-safe base64-encoded bytes
|
||||
fernet = Fernet(SECRET_KEY)
|
||||
return fernet
|
||||
|
||||
|
||||
def encrypt_api_key(api_key: str, settings_service=Depends(get_settings_service)):
|
||||
fernet = get_fernet(settings_service)
|
||||
# Two-way encryption
|
||||
encrypted_key = fernet.encrypt(api_key.encode())
|
||||
return encrypted_key
|
||||
|
||||
|
||||
def decrypt_api_key(
|
||||
encrypted_api_key: str, settings_service=Depends(get_settings_service)
|
||||
):
|
||||
fernet = get_fernet(settings_service)
|
||||
# Two-way decryption
|
||||
decrypted_key = fernet.decrypt(encrypted_api_key.encode()).decode()
|
||||
return decrypted_key
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ class FlowBase(SQLModelSerializable):
|
|||
name: str = Field(index=True)
|
||||
description: Optional[str] = Field(index=True)
|
||||
data: Optional[Dict] = Field(default=None, nullable=True)
|
||||
is_component: bool = Field(default=False, nullable=True)
|
||||
|
||||
@validator("data")
|
||||
def validate_json(v):
|
||||
|
|
@ -35,7 +36,7 @@ class FlowBase(SQLModelSerializable):
|
|||
class Flow(FlowBase, table=True):
|
||||
id: UUID = Field(default_factory=uuid4, primary_key=True, unique=True)
|
||||
data: Optional[Dict] = Field(default=None, sa_column=Column(JSON))
|
||||
user_id: UUID = Field(index=True, foreign_key="user.id")
|
||||
user_id: UUID = Field(index=True, foreign_key="user.id", nullable=True)
|
||||
user: "User" = Relationship(back_populates="flows")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class User(SQLModelSerializable, table=True):
|
|||
back_populates="user",
|
||||
sa_relationship_kwargs={"cascade": "delete"},
|
||||
)
|
||||
store_api_key: str = Field(default=None, nullable=True)
|
||||
flows: list["Flow"] = Relationship(back_populates="user")
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ from sqlalchemy.exc import OperationalError
|
|||
from sqlmodel import SQLModel, Session, create_engine
|
||||
from loguru import logger
|
||||
from alembic.config import Config
|
||||
from alembic import command
|
||||
from alembic import command, util
|
||||
from langflow.services.database import models # noqa
|
||||
import time
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sqlalchemy.engine import Engine
|
||||
|
|
@ -120,7 +121,24 @@ class DatabaseService(Service):
|
|||
alembic_cfg = Config()
|
||||
alembic_cfg.set_main_option("script_location", str(self.script_location))
|
||||
alembic_cfg.set_main_option("sqlalchemy.url", self.database_url)
|
||||
command.upgrade(alembic_cfg, "head")
|
||||
try:
|
||||
command.check(alembic_cfg)
|
||||
except Exception as exc:
|
||||
if isinstance(exc, util.exc.CommandError) or isinstance(
|
||||
exc, util.exc.AutogenerateDiffsDetected
|
||||
):
|
||||
command.upgrade(alembic_cfg, "head")
|
||||
|
||||
# We should check the schema health after running migrations
|
||||
try:
|
||||
command.check(alembic_cfg)
|
||||
except util.exc.AutogenerateDiffsDetected:
|
||||
# downgrade to base and upgrade again
|
||||
logger.warning("Autogenerate diffs detected, downgrading and upgrading")
|
||||
command.downgrade(alembic_cfg, "-1")
|
||||
# wait for the database to be ready
|
||||
time.sleep(5)
|
||||
command.upgrade(alembic_cfg, "head")
|
||||
|
||||
def run_migrations_test(self):
|
||||
# This method is used for testing purposes only
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ if TYPE_CHECKING:
|
|||
from langflow.services.session.service import SessionService
|
||||
from langflow.services.task.service import TaskService
|
||||
from langflow.services.chat.service import ChatService
|
||||
from langflow.services.marketplace.service import MarketplaceService
|
||||
from langflow.services.store.service import StoreService
|
||||
from sqlmodel import Session
|
||||
|
||||
|
||||
|
|
@ -49,5 +49,5 @@ def get_chat_service() -> "ChatService":
|
|||
return service_manager.get(ServiceType.CHAT_SERVICE)
|
||||
|
||||
|
||||
def get_marketplace_service() -> "MarketplaceService":
|
||||
return service_manager.get(ServiceType.MARKETPLACE_SERVICE)
|
||||
def get_store_service() -> "StoreService":
|
||||
return service_manager.get(ServiceType.STORE_SERVICE)
|
||||
|
|
|
|||
|
|
@ -14,4 +14,4 @@ class ServiceType(str, Enum):
|
|||
CHAT_SERVICE = "chat_service"
|
||||
SESSION_SERVICE = "session_service"
|
||||
TASK_SERVICE = "task_service"
|
||||
MARKETPLACE_SERVICE = "marketplace_service"
|
||||
STORE_SERVICE = "store_service"
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class Settings(BaseSettings):
|
|||
LANGFUSE_PUBLIC_KEY: Optional[str] = None
|
||||
LANGFUSE_HOST: Optional[str] = None
|
||||
|
||||
MARKETPLACE_URL: Optional[str] = None
|
||||
STORE_URL: Optional[str] = None
|
||||
|
||||
@validator("CONFIG_DIR", pre=True, allow_reuse=True)
|
||||
def set_langflow_dir(cls, value):
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class StoreService(Service):
|
|||
|
||||
def __init__(self, settings_service: "SettingsService"):
|
||||
self.settings_service = settings_service
|
||||
self.base_url = self.settings_service.settings.MARKETPLACE_URL
|
||||
self.base_url = self.settings_service.settings.STORE_URL
|
||||
self.components_url = f"{self.base_url}/items/components"
|
||||
|
||||
def _get(
|
||||
|
|
@ -101,8 +101,3 @@ class StoreService(Service):
|
|||
return ComponentResponse(**component)
|
||||
except HTTPError as exc:
|
||||
raise ValueError(f"Upload failed: {exc}")
|
||||
|
||||
def get_api_key(self, hashed_api_key: str):
|
||||
# We will use the settings_service.auth_settings.SECRET_KEY to decode the hashed_api_key
|
||||
# and return the api_key
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ def get_factories_and_deps():
|
|||
from langflow.services.auth import factory as auth_factory
|
||||
from langflow.services.task import factory as task_factory
|
||||
from langflow.services.session import factory as session_service_factory # type: ignore
|
||||
from langflow.services.store import factory as store_factory
|
||||
|
||||
return [
|
||||
(settings_factory.SettingsServiceFactory(), []),
|
||||
|
|
@ -40,6 +41,7 @@ def get_factories_and_deps():
|
|||
session_service_factory.SessionServiceFactory(),
|
||||
[ServiceType.CACHE_SERVICE],
|
||||
),
|
||||
(store_factory.StoreServiceFactory(), [ServiceType.SETTINGS_SERVICE]),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class FieldFormatters(BaseModel):
|
|||
|
||||
class FrontendNode(BaseModel):
|
||||
template: Template
|
||||
description: str
|
||||
description: Optional[str] = None
|
||||
base_classes: List[str]
|
||||
name: str = ""
|
||||
display_name: str = ""
|
||||
|
|
|
|||
784
src/frontend/package-lock.json
generated
784
src/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -30,6 +30,9 @@ terminate_process_by_port() {
|
|||
# Trap signals to ensure cleanup on script termination
|
||||
trap 'terminate_process_by_port 7860; terminate_process_by_port 3000' EXIT
|
||||
|
||||
# install playwright if there is not installed yet
|
||||
npx playwright install
|
||||
|
||||
# Navigate to the project root directory (where the Makefile is located)
|
||||
cd ../../
|
||||
|
||||
|
|
@ -56,7 +59,7 @@ cd ../../
|
|||
make backend &
|
||||
|
||||
# Give some time for the backend to start (adjust sleep duration as needed)
|
||||
sleep 10
|
||||
sleep 25
|
||||
|
||||
# Navigate back to the test directory
|
||||
cd src/frontend
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ export default function ParameterComponent({
|
|||
info = "",
|
||||
proxy,
|
||||
showNode,
|
||||
index = "",
|
||||
}: ParameterComponentType): JSX.Element {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const refHtml = useRef<HTMLDivElement & ReactNode>(null);
|
||||
|
|
@ -351,9 +352,11 @@ export default function ParameterComponent({
|
|||
disabled={disabled}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={handleOnNewValue}
|
||||
id={"textarea-" + index}
|
||||
/>
|
||||
) : (
|
||||
<InputComponent
|
||||
id={"input-" + index}
|
||||
disabled={disabled}
|
||||
password={data.node?.template[name].password ?? false}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
|
|
@ -364,6 +367,7 @@ export default function ParameterComponent({
|
|||
) : left === true && type === "bool" ? (
|
||||
<div className="mt-2 w-full">
|
||||
<ToggleShadComponent
|
||||
id={"toggle-" + index}
|
||||
disabled={disabled}
|
||||
enabled={data.node?.template[name].value ?? false}
|
||||
setEnabled={(isEnabled) => {
|
||||
|
|
@ -406,6 +410,7 @@ export default function ParameterComponent({
|
|||
disabled={disabled}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
onChange={handleOnNewValue}
|
||||
id={"code-input-" + index}
|
||||
/>
|
||||
</div>
|
||||
) : left === true && type === "file" ? (
|
||||
|
|
@ -427,6 +432,7 @@ export default function ParameterComponent({
|
|||
disabled={disabled}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
onChange={handleOnNewValue}
|
||||
id={"int-input-" + index}
|
||||
/>
|
||||
</div>
|
||||
) : left === true && type === "prompt" ? (
|
||||
|
|
@ -450,6 +456,7 @@ export default function ParameterComponent({
|
|||
onChange={(e) => {
|
||||
handleOnNewValue(e);
|
||||
}}
|
||||
id={"prompt-input-" + index}
|
||||
/>
|
||||
</div>
|
||||
) : left === true && type === "NestedDict" ? (
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ export default function GenericNode({
|
|||
data.node!.template[templateField].show &&
|
||||
!data.node!.template[templateField].advanced && (
|
||||
<ParameterComponent
|
||||
index={idx.toString()}
|
||||
key={scapedJSONStringfy({
|
||||
inputTypes:
|
||||
data.node!.template[templateField].input_types,
|
||||
|
|
@ -421,11 +422,13 @@ export default function GenericNode({
|
|||
<>
|
||||
{Object.keys(data.node!.template)
|
||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
||||
.sort()
|
||||
.map((templateField: string, idx) => (
|
||||
<div key={idx}>
|
||||
{data.node!.template[templateField].show &&
|
||||
!data.node!.template[templateField].advanced ? (
|
||||
<ParameterComponent
|
||||
index={idx.toString()}
|
||||
key={scapedJSONStringfy({
|
||||
inputTypes:
|
||||
data.node!.template[templateField].input_types,
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export default function CodeAreaComponent({
|
|||
nodeClass,
|
||||
dynamic,
|
||||
setNodeClass,
|
||||
id = "",
|
||||
readonly = false,
|
||||
}: CodeAreaComponentType) {
|
||||
const [myValue, setMyValue] = useState(
|
||||
|
|
@ -43,6 +44,7 @@ export default function CodeAreaComponent({
|
|||
>
|
||||
<div className="flex w-full items-center">
|
||||
<span
|
||||
id={id}
|
||||
className={
|
||||
editNode
|
||||
? "input-edit-node input-dialog"
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ export default function DictComponent({
|
|||
}, [value]);
|
||||
|
||||
const ref = useRef(value);
|
||||
debugger;
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export default function InputComponent({
|
|||
editNode = false,
|
||||
placeholder = "Type something...",
|
||||
className,
|
||||
id = "",
|
||||
blurOnEnter = false,
|
||||
}: InputComponentType): JSX.Element {
|
||||
const [pwdVisible, setPwdVisible] = useState(false);
|
||||
|
|
@ -33,6 +34,7 @@ export default function InputComponent({
|
|||
{isForm ? (
|
||||
<Form.Control asChild>
|
||||
<Input
|
||||
id={"form-" + id}
|
||||
ref={refInput}
|
||||
onBlur={onBlur}
|
||||
autoFocus={autoFocus}
|
||||
|
|
@ -61,6 +63,7 @@ export default function InputComponent({
|
|||
</Form.Control>
|
||||
) : (
|
||||
<Input
|
||||
id={id}
|
||||
ref={refInput}
|
||||
type="text"
|
||||
onBlur={onBlur}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export default function IntComponent({
|
|||
onChange,
|
||||
disabled,
|
||||
editNode = false,
|
||||
id = "",
|
||||
}: FloatComponentType): JSX.Element {
|
||||
const min = 0;
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ export default function IntComponent({
|
|||
return (
|
||||
<div className="w-full">
|
||||
<Input
|
||||
id={id}
|
||||
onKeyDown={(event) => {
|
||||
if (
|
||||
event.key !== "Backspace" &&
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export default function PromptAreaComponent({
|
|||
onChange,
|
||||
disabled,
|
||||
editNode = false,
|
||||
id = "",
|
||||
readonly = false,
|
||||
}: PromptAreaComponentType): JSX.Element {
|
||||
useEffect(() => {
|
||||
|
|
@ -36,6 +37,7 @@ export default function PromptAreaComponent({
|
|||
return (
|
||||
<div className={disabled ? "pointer-events-none w-full " : " w-full"}>
|
||||
<GenericModal
|
||||
id={id}
|
||||
readonly={readonly}
|
||||
type={TypeModal.PROMPT}
|
||||
value={value}
|
||||
|
|
@ -49,6 +51,7 @@ export default function PromptAreaComponent({
|
|||
>
|
||||
<div className="flex w-full items-center">
|
||||
<span
|
||||
id={id}
|
||||
className={
|
||||
editNode
|
||||
? "input-edit-node input-dialog"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ export default function TextAreaComponent({
|
|||
onChange,
|
||||
disabled,
|
||||
editNode = false,
|
||||
id = "",
|
||||
}: TextAreaComponentType): JSX.Element {
|
||||
// Clear text area
|
||||
useEffect(() => {
|
||||
|
|
@ -21,6 +22,7 @@ export default function TextAreaComponent({
|
|||
return (
|
||||
<div className="flex w-full items-center">
|
||||
<Input
|
||||
id={id}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
className={editNode ? "input-edit-node" : ""}
|
||||
|
|
|
|||
|
|
@ -522,3 +522,17 @@ export async function deleteApiKey(api_key: string) {
|
|||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function addApiKeyStore(key: string) {
|
||||
try {
|
||||
const res = await api.post(`${BASE_URL_API}api_key/store`, {
|
||||
api_key: key,
|
||||
});
|
||||
if (res.status === 200) {
|
||||
return res.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,6 +220,7 @@ const EditNodeModal = forwardRef(
|
|||
templateParam
|
||||
].multiline ? (
|
||||
<TextAreaComponent
|
||||
id={"textarea-edit-" + index}
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
|
|
@ -233,6 +234,7 @@ const EditNodeModal = forwardRef(
|
|||
/>
|
||||
) : (
|
||||
<InputComponent
|
||||
id={"input-" + index}
|
||||
editNode={true}
|
||||
disabled={disabled}
|
||||
password={
|
||||
|
|
@ -328,6 +330,7 @@ const EditNodeModal = forwardRef(
|
|||
<div className="ml-auto">
|
||||
{" "}
|
||||
<ToggleShadComponent
|
||||
id={"toggle-edit-" + index}
|
||||
disabled={disabled}
|
||||
enabled={
|
||||
myData.current.node.template[
|
||||
|
|
@ -386,6 +389,7 @@ const EditNodeModal = forwardRef(
|
|||
.type === "int" ? (
|
||||
<div className="mx-auto">
|
||||
<IntComponent
|
||||
id={"int-input-" + index}
|
||||
disabled={disabled}
|
||||
editNode={true}
|
||||
value={
|
||||
|
|
@ -456,6 +460,7 @@ const EditNodeModal = forwardRef(
|
|||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
id={"prompt-area-edit" + index}
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
|
|
@ -488,6 +493,7 @@ const EditNodeModal = forwardRef(
|
|||
onChange={(value: string | string[]) => {
|
||||
handleOnNewValue(value, templateParam);
|
||||
}}
|
||||
id={"code-area-edit" + index}
|
||||
/>
|
||||
</div>
|
||||
) : myData.current.node?.template[templateParam]
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { useContext, useEffect, useState } from "react";
|
|||
import AceEditor from "react-ace";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Input } from "../../components/ui/input";
|
||||
import { CODE_PROMPT_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { darkContext } from "../../contexts/darkContext";
|
||||
|
|
@ -144,6 +145,11 @@ export default function CodeAreaModal({
|
|||
/>
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Content>
|
||||
<Input
|
||||
value={code}
|
||||
className="absolute left-[500%] top-[500%]"
|
||||
id="codeValue"
|
||||
/>
|
||||
<div className="flex h-full w-full flex-col transition-all">
|
||||
<div className="h-full w-full">
|
||||
<AceEditor
|
||||
|
|
@ -183,10 +189,11 @@ export default function CodeAreaModal({
|
|||
</div>
|
||||
<div className="flex h-fit w-full justify-end">
|
||||
<Button
|
||||
disabled={readonly}
|
||||
className="mt-3"
|
||||
onClick={handleClick}
|
||||
type="submit"
|
||||
id="checkAndSaveBtn"
|
||||
disabled={readonly}
|
||||
>
|
||||
Check & Save
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export default function GenericModal({
|
|||
nodeClass,
|
||||
setNodeClass,
|
||||
children,
|
||||
id = "",
|
||||
readonly = false,
|
||||
}: genericModalPropsType): JSX.Element {
|
||||
const [myButtonText] = useState(buttonText);
|
||||
|
|
@ -211,6 +212,7 @@ export default function GenericModal({
|
|||
>
|
||||
{type === TypeModal.PROMPT && isEdit && !readonly ? (
|
||||
<Textarea
|
||||
id={"modal-" + id}
|
||||
ref={divRefPrompt}
|
||||
className="form-input h-full w-full rounded-lg custom-scroll focus-visible:ring-1"
|
||||
value={inputValue}
|
||||
|
|
@ -286,7 +288,7 @@ export default function GenericModal({
|
|||
className="m-1 max-w-[40vw] cursor-default truncate p-2.5 text-sm"
|
||||
>
|
||||
<div className="relative bottom-[1px]">
|
||||
<span>
|
||||
<span id={"badge" + index.toString()}>
|
||||
{word.replace(/[{}]/g, "").length > 59
|
||||
? word.replace(/[{}]/g, "").slice(0, 56) +
|
||||
"..."
|
||||
|
|
@ -306,6 +308,7 @@ export default function GenericModal({
|
|||
)}
|
||||
</div>
|
||||
<Button
|
||||
id="genericModalBtnSave"
|
||||
disabled={readonly}
|
||||
onClick={() => {
|
||||
switch (myModalType) {
|
||||
|
|
|
|||
|
|
@ -57,8 +57,10 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
let ret = {};
|
||||
Object.keys(data).forEach((d: keyof APIObjectType, i) => {
|
||||
ret[d] = {};
|
||||
let keys = Object.keys(data[d]).filter((nd) =>
|
||||
nd.toLowerCase().includes(e.toLowerCase())
|
||||
let keys = Object.keys(data[d]).filter(
|
||||
(nd) =>
|
||||
nd.toLowerCase().includes(e.toLowerCase()) ||
|
||||
data[d][nd].display_name?.toLowerCase().includes(e.toLowerCase())
|
||||
);
|
||||
keys.forEach((element) => {
|
||||
ret[d][element] = data[d][element];
|
||||
|
|
@ -87,14 +89,13 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
setSearch("");
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (getFilterEdge.length === 0 && search === "") {
|
||||
setFilterData(data);
|
||||
setFilterEdge([]);
|
||||
setSearch("");
|
||||
}
|
||||
}, [getFilterEdge]);
|
||||
}, [getFilterEdge, data]);
|
||||
|
||||
useEffect(() => {
|
||||
if (getFilterEdge?.length > 0) {
|
||||
|
|
@ -131,7 +132,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
return ret;
|
||||
});
|
||||
}
|
||||
}, [getFilterEdge]);
|
||||
}, [getFilterEdge, data]);
|
||||
|
||||
return (
|
||||
<div className="side-bar-arrangement">
|
||||
|
|
@ -252,6 +253,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
key={index}
|
||||
>
|
||||
<SidebarDraggableComponent
|
||||
sectionName={SBSectionName as string}
|
||||
apiClass={dataFilter[SBSectionName][SBItemName]}
|
||||
key={SBItemName}
|
||||
onDragStart={(event) =>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import {
|
|||
import { removeCountFromString } from "../../../../../utils/utils";
|
||||
|
||||
export default function SidebarDraggableComponent({
|
||||
sectionName,
|
||||
display_name,
|
||||
itemName,
|
||||
error,
|
||||
|
|
@ -25,6 +26,7 @@ export default function SidebarDraggableComponent({
|
|||
apiClass,
|
||||
official,
|
||||
}: {
|
||||
sectionName: string;
|
||||
apiClass: APIClassType;
|
||||
display_name: string;
|
||||
itemName: string;
|
||||
|
|
@ -84,7 +86,10 @@ export default function SidebarDraggableComponent({
|
|||
);
|
||||
}}
|
||||
>
|
||||
<div id={display_name} className="side-bar-components-div-form">
|
||||
<div
|
||||
id={sectionName + display_name}
|
||||
className="side-bar-components-div-form"
|
||||
>
|
||||
<span className="side-bar-components-text">{display_name}</span>
|
||||
<div>
|
||||
<SelectTrigger>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export type InputComponentType = {
|
|||
showPass?: boolean;
|
||||
placeholder?: string;
|
||||
className?: string;
|
||||
id?: string;
|
||||
blurOnEnter?: boolean;
|
||||
};
|
||||
export type ToggleComponentType = {
|
||||
|
|
@ -52,6 +53,7 @@ export type ParameterComponentType = {
|
|||
info?: string;
|
||||
proxy?: { field: string; id: string };
|
||||
showNode?: boolean;
|
||||
index?: string;
|
||||
};
|
||||
export type InputListComponentType = {
|
||||
value: string[];
|
||||
|
|
@ -85,6 +87,7 @@ export type TextAreaComponentType = {
|
|||
onChange: (value: string[] | string) => void;
|
||||
value: string;
|
||||
editNode?: boolean;
|
||||
id?: string;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
|
|
@ -97,6 +100,7 @@ export type PromptAreaComponentType = {
|
|||
value: string;
|
||||
readonly?: boolean;
|
||||
editNode?: boolean;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export type CodeAreaComponentType = {
|
||||
|
|
@ -107,6 +111,7 @@ export type CodeAreaComponentType = {
|
|||
nodeClass?: APIClassType;
|
||||
setNodeClass?: (value: APIClassType) => void;
|
||||
dynamic?: boolean;
|
||||
id?: string;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
|
|
@ -138,6 +143,7 @@ export type FloatComponentType = {
|
|||
disabled?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
editNode?: boolean;
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export type TooltipComponentType = {
|
||||
|
|
@ -500,6 +506,7 @@ export type genericModalPropsType = {
|
|||
nodeClass?: APIClassType;
|
||||
setNodeClass?: (Class: APIClassType) => void;
|
||||
children: ReactNode;
|
||||
id?: string;
|
||||
readonly?: boolean;
|
||||
};
|
||||
|
||||
|
|
|
|||
140
src/frontend/tests/end-to-end/codeAreaModalComponent.spec.ts
Normal file
140
src/frontend/tests/end-to-end/codeAreaModalComponent.spec.ts
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test("CodeAreaModalComponent", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("pythonfunctiontool");
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page
|
||||
.locator('//*[@id="sidePythonFunctionTool"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
|
||||
await page.locator('//*[@id="code-input-0"]').click();
|
||||
|
||||
let value = await page.locator('//*[@id="codeValue"]').inputValue();
|
||||
|
||||
if (
|
||||
value !=
|
||||
'def python_function(text: str) -> str: """This is a default python function that returns the input text""" return text'
|
||||
) {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="checkAndSaveBtn"]').click();
|
||||
|
||||
await page
|
||||
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="showcode"]').click();
|
||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showdescription"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showdescription"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showname"]').click();
|
||||
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showreturn_direct"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showreturn_direct"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showcode"]').click();
|
||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showdescription"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showdescription"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showname"]').click();
|
||||
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showreturn_direct"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showreturn_direct"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showcode"]').click();
|
||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showdescription"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showdescription"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showname"]').click();
|
||||
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showreturn_direct"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showreturn_direct"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showcode"]').click();
|
||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showdescription"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showdescription"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showname"]').click();
|
||||
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showreturn_direct"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showreturn_direct"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showcode"]').click();
|
||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
const plusButtonLocator = page.locator('//*[@id="code-input-0"]');
|
||||
const elementCount = await plusButtonLocator.count();
|
||||
if (elementCount === 0) {
|
||||
expect(true).toBeTruthy();
|
||||
|
||||
await page
|
||||
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="showcode"]').click();
|
||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="code-area-edit0"]').click();
|
||||
|
||||
let value = await page.locator('//*[@id="codeValue"]').inputValue();
|
||||
|
||||
if (
|
||||
value !=
|
||||
'def python_function(text: str) -> str: """This is a default python function that returns the input text""" return text'
|
||||
) {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="checkAndSaveBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="code-input-0"]').click();
|
||||
}
|
||||
});
|
||||
|
|
@ -1,153 +1,5 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test("KeypairListComponent", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("csv");
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page
|
||||
.locator('//*[@id="sideCSVLoader"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
|
||||
await page.locator('//*[@id="keypair0"]').click();
|
||||
await page.locator('//*[@id="keypair0"]').fill("testtesttesttest");
|
||||
await page.locator('//*[@id="keypair100"]').click();
|
||||
await page.locator('//*[@id="keypair100"]').fill("testtesttesttesttesttest");
|
||||
|
||||
const plusButtonLocatorNode = page.locator('//*[@id="plusbtn0"]');
|
||||
const elementCountNode = await plusButtonLocatorNode.count();
|
||||
if (elementCountNode > 0) {
|
||||
await plusButtonLocatorNode.click();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="keypair1"]').click();
|
||||
await page.locator('//*[@id="keypair1"]').fill("testtesttesttest1");
|
||||
await page.locator('//*[@id="keypair101"]').click();
|
||||
await page.locator('//*[@id="keypair101"]').fill("testtesttesttesttesttest1");
|
||||
await page.locator('//*[@id="plusbtn1"]').click();
|
||||
|
||||
await page.locator('//*[@id="keypair2"]').click();
|
||||
await page.locator('//*[@id="keypair2"]').fill("testtesttesttest2");
|
||||
await page.locator('//*[@id="keypair102"]').click();
|
||||
await page.locator('//*[@id="keypair102"]').fill("testtesttesttesttesttest2");
|
||||
|
||||
await page.locator('//*[@id="minusbtn1"]').click();
|
||||
|
||||
const keyPairVerification = page.locator('//*[@id="keypair102"]');
|
||||
const elementKeyCount = await keyPairVerification.count();
|
||||
|
||||
if (elementKeyCount === 0) {
|
||||
expect(true).toBeTruthy();
|
||||
} else {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page
|
||||
.locator(
|
||||
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
|
||||
)
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="showfile_path"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showfile_path"]').isChecked()
|
||||
).toBeFalsy();
|
||||
await page.locator('//*[@id="showmetadata"]').click();
|
||||
expect(await page.locator('//*[@id="showmetadata"]').isChecked()).toBeFalsy();
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
const plusButtonLocator = page.locator('//*[@id="plusbtn0"]');
|
||||
const elementCount = await plusButtonLocator.count();
|
||||
if (elementCount === 0) {
|
||||
expect(true).toBeTruthy();
|
||||
|
||||
await page
|
||||
.locator(
|
||||
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
|
||||
)
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="showfile_path"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showfile_path"]').isChecked()
|
||||
).toBeTruthy();
|
||||
await page.locator('//*[@id="showmetadata"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showmetadata"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="keypair0"]').click();
|
||||
await page.locator('//*[@id="keypair0"]').fill("testtesttesttest");
|
||||
await page.locator('//*[@id="keypair100"]').click();
|
||||
await page
|
||||
.locator('//*[@id="keypair100"]')
|
||||
.fill("testtesttesttesttesttest");
|
||||
|
||||
const plusButtonLocator = page.locator('//*[@id="plusbtn0"]');
|
||||
const elementCount = await plusButtonLocator.count();
|
||||
if (elementCount > 0) {
|
||||
await plusButtonLocator.click();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="keypair1"]').click();
|
||||
await page.locator('//*[@id="keypair1"]').fill("testtesttesttest1");
|
||||
await page.locator('//*[@id="keypair101"]').click();
|
||||
await page
|
||||
.locator('//*[@id="keypair101"]')
|
||||
.fill("testtesttesttesttesttest1");
|
||||
await page.locator('//*[@id="plusbtn1"]').click();
|
||||
|
||||
await page.locator('//*[@id="keypair2"]').click();
|
||||
await page.locator('//*[@id="keypair2"]').fill("testtesttesttest2");
|
||||
await page.locator('//*[@id="keypair102"]').click();
|
||||
await page
|
||||
.locator('//*[@id="keypair102"]')
|
||||
.fill("testtesttesttesttesttest2");
|
||||
|
||||
await page.locator('//*[@id="minusbtn1"]').click();
|
||||
|
||||
const keyPairVerification = page.locator('//*[@id="keypair102"]');
|
||||
const elementKeyCount = await keyPairVerification.count();
|
||||
|
||||
if (elementKeyCount === 0) {
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
const key1 = await page.locator('//*[@id="keypair0"]').inputValue();
|
||||
const value1 = await page.locator('//*[@id="keypair100"]').inputValue();
|
||||
const key2 = await page.locator('//*[@id="keypair1"]').inputValue();
|
||||
const value2 = await page.locator('//*[@id="keypair101"]').inputValue();
|
||||
|
||||
if (
|
||||
key1 === "testtesttesttest" &&
|
||||
value1 === "testtesttesttesttesttest" &&
|
||||
key2 === "testtesttesttest2" &&
|
||||
value2 === "testtesttesttesttesttest2"
|
||||
) {
|
||||
expect(true).toBeTruthy();
|
||||
} else {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
} else {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
} else {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
test("FloatComponent", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
118
src/frontend/tests/end-to-end/intComponent.spec.ts
Normal file
118
src/frontend/tests/end-to-end/intComponent.spec.ts
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test("IntComponent", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("getrequest");
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page
|
||||
.locator('//*[@id="sideGET Request"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
|
||||
await page.locator('//*[@id="int-input-2"]').click();
|
||||
await page
|
||||
.locator('//*[@id="int-input-2"]')
|
||||
.fill("123456789123456789123456789");
|
||||
|
||||
let value = await page.locator('//*[@id="int-input-2"]').inputValue();
|
||||
|
||||
if (value != "123456789123456789123456789") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="int-input-2"]').click();
|
||||
await page.locator('//*[@id="int-input-2"]').fill("-3");
|
||||
|
||||
value = await page.locator('//*[@id="int-input-2"]').inputValue();
|
||||
|
||||
if (value != "0") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page
|
||||
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
value = await page.locator('//*[@id="int-input-1"]').inputValue();
|
||||
|
||||
if (value != "0") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="int-input-1"]').click();
|
||||
await page
|
||||
.locator('//*[@id="int-input-1"]')
|
||||
.fill("123456789123456789123456789");
|
||||
|
||||
await page.locator('//*[@id="showheaders"]').click();
|
||||
expect(await page.locator('//*[@id="showheaders"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showtimeout"]').click();
|
||||
expect(await page.locator('//*[@id="showtimeout"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showurl"]').click();
|
||||
expect(await page.locator('//*[@id="showurl"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showheaders"]').click();
|
||||
expect(await page.locator('//*[@id="showheaders"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showurl"]').click();
|
||||
expect(await page.locator('//*[@id="showurl"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
const plusButtonLocator = page.locator('//*[@id="int-input-2"]');
|
||||
const elementCount = await plusButtonLocator.count();
|
||||
if (elementCount === 0) {
|
||||
expect(true).toBeTruthy();
|
||||
|
||||
await page
|
||||
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="showtimeout"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showtimeout"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
const valueEditNode = await page
|
||||
.locator('//*[@id="int-input-1"]')
|
||||
.inputValue();
|
||||
|
||||
if (valueEditNode != "123456789123456789123456789") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
await page.locator('//*[@id="int-input-2"]').click();
|
||||
await page.locator('//*[@id="int-input-2"]').fill("3");
|
||||
|
||||
let value = await page.locator('//*[@id="int-input-2"]').inputValue();
|
||||
|
||||
if (value != "3") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="int-input-2"]').click();
|
||||
await page.locator('//*[@id="int-input-2"]').fill("-3");
|
||||
|
||||
value = await page.locator('//*[@id="int-input-2"]').inputValue();
|
||||
|
||||
if (value != "0") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
149
src/frontend/tests/end-to-end/keyPairListComponent.spec.ts
Normal file
149
src/frontend/tests/end-to-end/keyPairListComponent.spec.ts
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test("KeypairListComponent", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("csv");
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page
|
||||
.locator('//*[@id="sideCSVLoader"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
|
||||
await page.locator('//*[@id="keypair0"]').click();
|
||||
await page.locator('//*[@id="keypair0"]').fill("testtesttesttest");
|
||||
await page.locator('//*[@id="keypair100"]').click();
|
||||
await page.locator('//*[@id="keypair100"]').fill("testtesttesttesttesttest");
|
||||
|
||||
const plusButtonLocatorNode = page.locator('//*[@id="plusbtn0"]');
|
||||
const elementCountNode = await plusButtonLocatorNode.count();
|
||||
if (elementCountNode > 0) {
|
||||
await plusButtonLocatorNode.click();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="keypair1"]').click();
|
||||
await page.locator('//*[@id="keypair1"]').fill("testtesttesttest1");
|
||||
await page.locator('//*[@id="keypair101"]').click();
|
||||
await page.locator('//*[@id="keypair101"]').fill("testtesttesttesttesttest1");
|
||||
await page.locator('//*[@id="plusbtn1"]').click();
|
||||
|
||||
await page.locator('//*[@id="keypair2"]').click();
|
||||
await page.locator('//*[@id="keypair2"]').fill("testtesttesttest2");
|
||||
await page.locator('//*[@id="keypair102"]').click();
|
||||
await page.locator('//*[@id="keypair102"]').fill("testtesttesttesttesttest2");
|
||||
|
||||
await page.locator('//*[@id="minusbtn1"]').click();
|
||||
|
||||
const keyPairVerification = page.locator('//*[@id="keypair102"]');
|
||||
const elementKeyCount = await keyPairVerification.count();
|
||||
|
||||
if (elementKeyCount === 0) {
|
||||
expect(true).toBeTruthy();
|
||||
} else {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page
|
||||
.locator(
|
||||
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
|
||||
)
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="showfile_path"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showfile_path"]').isChecked()
|
||||
).toBeFalsy();
|
||||
await page.locator('//*[@id="showmetadata"]').click();
|
||||
expect(await page.locator('//*[@id="showmetadata"]').isChecked()).toBeFalsy();
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
const plusButtonLocator = page.locator('//*[@id="plusbtn0"]');
|
||||
const elementCount = await plusButtonLocator.count();
|
||||
if (elementCount === 0) {
|
||||
expect(true).toBeTruthy();
|
||||
|
||||
await page
|
||||
.locator(
|
||||
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
|
||||
)
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="showfile_path"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showfile_path"]').isChecked()
|
||||
).toBeTruthy();
|
||||
await page.locator('//*[@id="showmetadata"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showmetadata"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="keypair0"]').click();
|
||||
await page.locator('//*[@id="keypair0"]').fill("testtesttesttest");
|
||||
await page.locator('//*[@id="keypair100"]').click();
|
||||
await page
|
||||
.locator('//*[@id="keypair100"]')
|
||||
.fill("testtesttesttesttesttest");
|
||||
|
||||
const plusButtonLocator = page.locator('//*[@id="plusbtn0"]');
|
||||
const elementCount = await plusButtonLocator.count();
|
||||
if (elementCount > 0) {
|
||||
await plusButtonLocator.click();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="keypair1"]').click();
|
||||
await page.locator('//*[@id="keypair1"]').fill("testtesttesttest1");
|
||||
await page.locator('//*[@id="keypair101"]').click();
|
||||
await page
|
||||
.locator('//*[@id="keypair101"]')
|
||||
.fill("testtesttesttesttesttest1");
|
||||
await page.locator('//*[@id="plusbtn1"]').click();
|
||||
|
||||
await page.locator('//*[@id="keypair2"]').click();
|
||||
await page.locator('//*[@id="keypair2"]').fill("testtesttesttest2");
|
||||
await page.locator('//*[@id="keypair102"]').click();
|
||||
await page
|
||||
.locator('//*[@id="keypair102"]')
|
||||
.fill("testtesttesttesttesttest2");
|
||||
|
||||
await page.locator('//*[@id="minusbtn1"]').click();
|
||||
|
||||
const keyPairVerification = page.locator('//*[@id="keypair102"]');
|
||||
const elementKeyCount = await keyPairVerification.count();
|
||||
|
||||
if (elementKeyCount === 0) {
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
const key1 = await page.locator('//*[@id="keypair0"]').inputValue();
|
||||
const value1 = await page.locator('//*[@id="keypair100"]').inputValue();
|
||||
const key2 = await page.locator('//*[@id="keypair1"]').inputValue();
|
||||
const value2 = await page.locator('//*[@id="keypair101"]').inputValue();
|
||||
|
||||
if (
|
||||
key1 === "testtesttesttest" &&
|
||||
value1 === "testtesttesttesttesttest" &&
|
||||
key2 === "testtesttesttest2" &&
|
||||
value2 === "testtesttesttesttesttest2"
|
||||
) {
|
||||
expect(true).toBeTruthy();
|
||||
} else {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
} else {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
} else {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
});
|
||||
174
src/frontend/tests/end-to-end/promptModalComponent.spec.ts
Normal file
174
src/frontend/tests/end-to-end/promptModalComponent.spec.ts
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test("PromptTemplateComponent", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("promptTemplate");
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page
|
||||
.locator('//*[@id="sidePromptTemplate"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
|
||||
await page.locator('//*[@id="prompt-input-4"]').click();
|
||||
await page
|
||||
.locator('//*[@id="modal-prompt-input-4"]')
|
||||
.fill("{prompt} example {prompt1}");
|
||||
|
||||
let value = await page
|
||||
.locator('//*[@id="modal-prompt-input-4"]')
|
||||
.inputValue();
|
||||
|
||||
if (value != "{prompt} example {prompt1}") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
let valueBadgeOne = await page.locator('//*[@id="badge0"]').innerText();
|
||||
if (valueBadgeOne != "prompt") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
let valueBadgeTwo = await page.locator('//*[@id="badge1"]').innerText();
|
||||
if (valueBadgeTwo != "prompt1") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="genericModalBtnSave"]').click();
|
||||
|
||||
await page.locator('//*[@id="textarea-7"]').click();
|
||||
await page.locator('//*[@id="textarea-7"]').fill("prompt_value_!@#!@#");
|
||||
|
||||
value = await page.locator('//*[@id="textarea-7"]').inputValue();
|
||||
|
||||
if (value != "prompt_value_!@#!@#") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="textarea-8"]').click();
|
||||
await page
|
||||
.locator('//*[@id="textarea-8"]')
|
||||
.fill("prompt_name_test_123123!@#!@#");
|
||||
|
||||
value = await page.locator('//*[@id="textarea-8"]').inputValue();
|
||||
|
||||
if (value != "prompt_name_test_123123!@#!@#") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
value = await page.locator('//*[@id="prompt-input-4"]').innerText();
|
||||
|
||||
if (value != "{prompt} example {prompt1}") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="editAdvancedIcon"]').click();
|
||||
|
||||
value = await page.locator('//*[@id="textarea-edit-1"]').inputValue();
|
||||
|
||||
if (value != "prompt_value_!@#!@#") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
value = await page.locator('//*[@id="textarea-edit-2"]').inputValue();
|
||||
|
||||
if (value != "prompt_name_test_123123!@#!@#") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
value = await page.locator('//*[@id="prompt-area-edit0"]').innerText();
|
||||
|
||||
if (value != "{prompt} example {prompt1}") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page
|
||||
.locator('//*[@id="textarea-edit-2"]')
|
||||
.fill("prompt_edit_test_12312312321!@#$");
|
||||
await page
|
||||
.locator('//*[@id="textarea-edit-1"]')
|
||||
.fill("prompt_edit_test_44444444444!@#$");
|
||||
|
||||
await page.locator('//*[@id="showtemplate"]').click();
|
||||
expect(await page.locator('//*[@id="showtemplate"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showprompt"]').click();
|
||||
expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showprompt1"]').click();
|
||||
expect(await page.locator('//*[@id="showprompt1"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showtemplate"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showtemplate"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showprompt"]').click();
|
||||
expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showprompt1"]').click();
|
||||
expect(await page.locator('//*[@id="showprompt1"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showtemplate"]').click();
|
||||
expect(await page.locator('//*[@id="showtemplate"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showprompt"]').click();
|
||||
expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showprompt1"]').click();
|
||||
expect(await page.locator('//*[@id="showprompt1"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showtemplate"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showtemplate"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showprompt"]').click();
|
||||
expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
const plusButtonLocator = page.locator('//*[@id="textarea-8"]');
|
||||
const elementCount = await plusButtonLocator.count();
|
||||
if (elementCount === 0) {
|
||||
expect(true).toBeTruthy();
|
||||
|
||||
await page
|
||||
.locator(
|
||||
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]'
|
||||
)
|
||||
.click();
|
||||
|
||||
await page.locator('//*[@id="editAdvancedIcon"]').click();
|
||||
|
||||
await page.locator('//*[@id="showprompt1"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showprompt1"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
value = await page.locator('//*[@id="textarea-edit-1"]').inputValue();
|
||||
|
||||
if (value != "prompt_edit_test_44444444444!@#$") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
value = await page.locator('//*[@id="textarea-edit-2"]').inputValue();
|
||||
|
||||
if (value != "prompt_edit_test_12312312321!@#$") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
value = await page.locator('//*[@id="prompt-area-edit0"]').innerText();
|
||||
|
||||
if (value != "{prompt} example {prompt1}") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
2684
src/frontend/tests/onlyFront/assets/collection.json
Normal file
2684
src/frontend/tests/onlyFront/assets/collection.json
Normal file
File diff suppressed because it is too large
Load diff
419
src/frontend/tests/onlyFront/assets/flow.json
Normal file
419
src/frontend/tests/onlyFront/assets/flow.json
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
{
|
||||
"description": "Use this Tool on every query",
|
||||
"name": "Getting Started: Simple python function applied to each output",
|
||||
"data": {
|
||||
"nodes": [
|
||||
{
|
||||
"width": 384,
|
||||
"height": 631,
|
||||
"id": "ChatOpenAI-tRw3A",
|
||||
"type": "genericNode",
|
||||
"position": {
|
||||
"x": 543.1816229116944,
|
||||
"y": 942.891611351432
|
||||
},
|
||||
"data": {
|
||||
"type": "ChatOpenAI",
|
||||
"node": {
|
||||
"template": {
|
||||
"lc_kwargs": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": false,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "lc_kwargs",
|
||||
"advanced": true,
|
||||
"type": "code",
|
||||
"list": false
|
||||
},
|
||||
"verbose": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": false,
|
||||
"multiline": false,
|
||||
"value": false,
|
||||
"password": false,
|
||||
"name": "verbose",
|
||||
"advanced": false,
|
||||
"type": "bool",
|
||||
"list": false
|
||||
},
|
||||
"callbacks": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": false,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "callbacks",
|
||||
"advanced": false,
|
||||
"type": "langchain.callbacks.base.BaseCallbackHandler",
|
||||
"list": true
|
||||
},
|
||||
"client": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": false,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "client",
|
||||
"advanced": false,
|
||||
"type": "Any",
|
||||
"list": false
|
||||
},
|
||||
"model_name": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "gpt-3.5-turbo",
|
||||
"password": false,
|
||||
"options": [
|
||||
"gpt-3.5-turbo-0613",
|
||||
"gpt-3.5-turbo",
|
||||
"gpt-3.5-turbo-16k-0613",
|
||||
"gpt-3.5-turbo-16k",
|
||||
"gpt-4-0613",
|
||||
"gpt-4-32k-0613",
|
||||
"gpt-4",
|
||||
"gpt-4-32k"
|
||||
],
|
||||
"name": "model_name",
|
||||
"advanced": false,
|
||||
"type": "str",
|
||||
"list": true
|
||||
},
|
||||
"temperature": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "0.2",
|
||||
"password": false,
|
||||
"name": "temperature",
|
||||
"advanced": false,
|
||||
"type": "float",
|
||||
"list": false
|
||||
},
|
||||
"model_kwargs": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "model_kwargs",
|
||||
"advanced": true,
|
||||
"type": "code",
|
||||
"list": false
|
||||
},
|
||||
"openai_api_key": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "",
|
||||
"password": true,
|
||||
"name": "openai_api_key",
|
||||
"display_name": "OpenAI API Key",
|
||||
"advanced": false,
|
||||
"type": "str",
|
||||
"list": false
|
||||
},
|
||||
"openai_api_base": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "openai_api_base",
|
||||
"display_name": "OpenAI API Base",
|
||||
"advanced": false,
|
||||
"type": "str",
|
||||
"list": false
|
||||
},
|
||||
"openai_organization": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": false,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "openai_organization",
|
||||
"display_name": "OpenAI Organization",
|
||||
"advanced": false,
|
||||
"type": "str",
|
||||
"list": false
|
||||
},
|
||||
"openai_proxy": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": false,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "openai_proxy",
|
||||
"display_name": "OpenAI Proxy",
|
||||
"advanced": false,
|
||||
"type": "str",
|
||||
"list": false
|
||||
},
|
||||
"request_timeout": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": false,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "request_timeout",
|
||||
"advanced": false,
|
||||
"type": "float",
|
||||
"list": false
|
||||
},
|
||||
"max_retries": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": false,
|
||||
"multiline": false,
|
||||
"value": 6,
|
||||
"password": false,
|
||||
"name": "max_retries",
|
||||
"advanced": false,
|
||||
"type": "int",
|
||||
"list": false
|
||||
},
|
||||
"streaming": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": false,
|
||||
"multiline": false,
|
||||
"value": false,
|
||||
"password": false,
|
||||
"name": "streaming",
|
||||
"advanced": false,
|
||||
"type": "bool",
|
||||
"list": false
|
||||
},
|
||||
"n": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": false,
|
||||
"multiline": false,
|
||||
"value": 1,
|
||||
"password": false,
|
||||
"name": "n",
|
||||
"advanced": false,
|
||||
"type": "int",
|
||||
"list": false
|
||||
},
|
||||
"max_tokens": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"password": true,
|
||||
"name": "max_tokens",
|
||||
"advanced": false,
|
||||
"type": "int",
|
||||
"list": false
|
||||
},
|
||||
"_type": "ChatOpenAI"
|
||||
},
|
||||
"description": "Wrapper around OpenAI Chat large language models.",
|
||||
"base_classes": [
|
||||
"Serializable",
|
||||
"BaseChatModel",
|
||||
"ChatOpenAI",
|
||||
"BaseLanguageModel"
|
||||
],
|
||||
"display_name": "ChatOpenAI"
|
||||
},
|
||||
"id": "ChatOpenAI-tRw3A",
|
||||
"value": null
|
||||
},
|
||||
"selected": false,
|
||||
"dragging": false,
|
||||
"positionAbsolute": {
|
||||
"x": 543.1816229116944,
|
||||
"y": 942.891611351432
|
||||
}
|
||||
},
|
||||
{
|
||||
"width": 384,
|
||||
"height": 387,
|
||||
"id": "AgentInitializer-KcVTt",
|
||||
"type": "genericNode",
|
||||
"position": {
|
||||
"x": 1036.6064439140812,
|
||||
"y": 645.1919693466587
|
||||
},
|
||||
"data": {
|
||||
"type": "AgentInitializer",
|
||||
"node": {
|
||||
"template": {
|
||||
"agent": {
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "zero-shot-react-description",
|
||||
"password": false,
|
||||
"options": [
|
||||
"zero-shot-react-description",
|
||||
"react-docstore",
|
||||
"self-ask-with-search",
|
||||
"conversational-react-description",
|
||||
"openai-functions"
|
||||
],
|
||||
"name": "agent",
|
||||
"advanced": false,
|
||||
"type": "str",
|
||||
"list": true
|
||||
},
|
||||
"memory": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "memory",
|
||||
"advanced": false,
|
||||
"type": "BaseChatMemory",
|
||||
"list": false
|
||||
},
|
||||
"tools": {
|
||||
"required": false,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "tools",
|
||||
"advanced": false,
|
||||
"type": "Tool",
|
||||
"list": true
|
||||
},
|
||||
"llm": {
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"password": false,
|
||||
"name": "llm",
|
||||
"display_name": "LLM",
|
||||
"advanced": false,
|
||||
"type": "BaseLanguageModel",
|
||||
"list": false
|
||||
},
|
||||
"_type": "initialize_agent"
|
||||
},
|
||||
"description": "Construct a zero shot agent from an LLM and tools.",
|
||||
"base_classes": ["AgentExecutor", "function"],
|
||||
"display_name": "AgentInitializer"
|
||||
},
|
||||
"id": "AgentInitializer-KcVTt",
|
||||
"value": null
|
||||
},
|
||||
"selected": false,
|
||||
"positionAbsolute": {
|
||||
"x": 1036.6064439140812,
|
||||
"y": 645.1919693466587
|
||||
}
|
||||
},
|
||||
{
|
||||
"width": 384,
|
||||
"height": 437,
|
||||
"id": "PythonFunctionTool-FwZVF",
|
||||
"type": "genericNode",
|
||||
"position": {
|
||||
"x": 553.050119331742,
|
||||
"y": 412.9533535948685
|
||||
},
|
||||
"data": {
|
||||
"type": "PythonFunctionTool",
|
||||
"node": {
|
||||
"template": {
|
||||
"name": {
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": false,
|
||||
"value": "PythonFunction",
|
||||
"password": false,
|
||||
"name": "name",
|
||||
"advanced": false,
|
||||
"type": "str",
|
||||
"list": false
|
||||
},
|
||||
"description": {
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "Returns the Text you send. This is a testing tool.",
|
||||
"password": false,
|
||||
"name": "description",
|
||||
"advanced": false,
|
||||
"type": "str",
|
||||
"list": false
|
||||
},
|
||||
"code": {
|
||||
"required": true,
|
||||
"placeholder": "",
|
||||
"show": true,
|
||||
"multiline": true,
|
||||
"value": "\ndef python_function(text: str) -> str:\n \"\"\"This is a default python function that returns the input text\"\"\"\n return text\n",
|
||||
"password": false,
|
||||
"name": "code",
|
||||
"advanced": false,
|
||||
"type": "code",
|
||||
"list": false
|
||||
},
|
||||
"_type": "PythonFunctionTool"
|
||||
},
|
||||
"description": "Python function to be executed.",
|
||||
"base_classes": ["Tool"],
|
||||
"display_name": "PythonFunctionTool"
|
||||
},
|
||||
"id": "PythonFunctionTool-FwZVF",
|
||||
"value": null
|
||||
},
|
||||
"selected": false,
|
||||
"dragging": false,
|
||||
"positionAbsolute": {
|
||||
"x": 553.050119331742,
|
||||
"y": 412.9533535948685
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "ChatOpenAI-tRw3A",
|
||||
"sourceHandle": "ChatOpenAI|ChatOpenAI-tRw3A|Serializable|BaseChatModel|ChatOpenAI|BaseLanguageModel",
|
||||
"target": "AgentInitializer-KcVTt",
|
||||
"targetHandle": "BaseLanguageModel|llm|AgentInitializer-KcVTt",
|
||||
"style": {
|
||||
"stroke": "inherit"
|
||||
},
|
||||
"className": "stroke-gray-900 dark:stroke-gray-200",
|
||||
"animated": false,
|
||||
"id": "reactflow__edge-ChatOpenAI-tRw3AChatOpenAI|ChatOpenAI-tRw3A|Serializable|BaseChatModel|ChatOpenAI|BaseLanguageModel-AgentInitializer-KcVTtBaseLanguageModel|llm|AgentInitializer-KcVTt",
|
||||
"selected": false
|
||||
},
|
||||
{
|
||||
"source": "PythonFunctionTool-FwZVF",
|
||||
"sourceHandle": "PythonFunctionTool|PythonFunctionTool-FwZVF|Tool",
|
||||
"target": "AgentInitializer-KcVTt",
|
||||
"targetHandle": "Tool|tools|AgentInitializer-KcVTt",
|
||||
"style": {
|
||||
"stroke": "inherit"
|
||||
},
|
||||
"className": "stroke-gray-900 dark:stroke-gray-200",
|
||||
"animated": false,
|
||||
"id": "reactflow__edge-PythonFunctionTool-FwZVFPythonFunctionTool|PythonFunctionTool-FwZVF|Tool-AgentInitializer-KcVTtTool|tools|AgentInitializer-KcVTt",
|
||||
"selected": false
|
||||
}
|
||||
],
|
||||
"viewport": {
|
||||
"x": 4.748095479939138,
|
||||
"y": -155.65184647754464,
|
||||
"zoom": 0.6079953565987085
|
||||
}
|
||||
},
|
||||
"id": "15030b3c-570d-4658-8473-58138077e9b0"
|
||||
}
|
||||
87
src/frontend/tests/onlyFront/dragAndDrop.spec.ts
Normal file
87
src/frontend/tests/onlyFront/dragAndDrop.spec.ts
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
test.describe("drag and drop test", () => {
|
||||
/// <reference lib="dom"/>
|
||||
test("drop collection", async ({ page }) => {
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.route("**/api/v1/flows/", async (route) => {
|
||||
const json = {
|
||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
||||
};
|
||||
await route.fulfill({ json, status: 201 });
|
||||
});
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
||||
// Read your file into a buffer.
|
||||
const jsonContent = readFileSync(
|
||||
"tests/onlyFront/assets/collection.json",
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
// Create the DataTransfer and File
|
||||
const dataTransfer = await page.evaluateHandle((data) => {
|
||||
const dt = new DataTransfer();
|
||||
// Convert the buffer to a hex array
|
||||
const file = new File([data], "collection.json", {
|
||||
type: "application/json",
|
||||
});
|
||||
dt.items.add(file);
|
||||
return dt;
|
||||
}, jsonContent);
|
||||
|
||||
// Now dispatch
|
||||
await page.dispatchEvent('//*[@id="root"]/div/div[2]/div[2]', "drop", {
|
||||
dataTransfer,
|
||||
});
|
||||
expect(
|
||||
await page
|
||||
.locator(".main-page-flows-display")
|
||||
.evaluate((el) => el.children)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test("drop flow", async ({ page }) => {
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.route("**/api/v1/flows/", async (route) => {
|
||||
const json = {
|
||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
||||
};
|
||||
await route.fulfill({ json, status: 201 });
|
||||
});
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
||||
// Read your file into a buffer.
|
||||
const jsonContent = readFileSync(
|
||||
"tests/onlyFront/assets/flow.json",
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
// Create the DataTransfer and File
|
||||
const dataTransfer = await page.evaluateHandle((data) => {
|
||||
const dt = new DataTransfer();
|
||||
// Convert the buffer to a hex array
|
||||
const file = new File([data], "flow.json", {
|
||||
type: "application/json",
|
||||
});
|
||||
dt.items.add(file);
|
||||
return dt;
|
||||
}, jsonContent);
|
||||
|
||||
// Now dispatch
|
||||
await page.dispatchEvent('//*[@id="root"]/div/div[2]/div[2]', "drop", {
|
||||
dataTransfer,
|
||||
});
|
||||
expect(
|
||||
await page
|
||||
.locator(".main-page-flows-display")
|
||||
.evaluate((el) => el.children)
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,14 +1,49 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
test.describe("Group component tests", () => {
|
||||
test("group test", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.getByRole("button", { name: "Community Examples" }).click();
|
||||
await page
|
||||
.locator(
|
||||
"div:nth-child(7) > div:nth-child(2) > .card-component-footer-arrangement > .inline-flex"
|
||||
)
|
||||
.click();
|
||||
test.describe("group node test", () => {
|
||||
/// <reference lib="dom"/>
|
||||
test("group and ungroup updating values", async ({ page }) => {
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.route("**/api/v1/flows/", async (route) => {
|
||||
const json = {
|
||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
||||
};
|
||||
await route.fulfill({ json, status: 201 });
|
||||
});
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
||||
// Read your file into a buffer.
|
||||
const jsonContent = readFileSync(
|
||||
"tests/onlyFront/assets/flow.json",
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
// Create the DataTransfer and File
|
||||
const dataTransfer = await page.evaluateHandle((data) => {
|
||||
const dt = new DataTransfer();
|
||||
// Convert the buffer to a hex array
|
||||
const file = new File([data], "flow.json", {
|
||||
type: "application/json",
|
||||
});
|
||||
dt.items.add(file);
|
||||
return dt;
|
||||
}, jsonContent);
|
||||
|
||||
// Now dispatch
|
||||
await page.dispatchEvent('//*[@id="root"]/div/div[2]/div[2]', "drop", {
|
||||
dataTransfer,
|
||||
});
|
||||
expect(
|
||||
await page
|
||||
.locator(".main-page-flows-display")
|
||||
.evaluate((el) => el.children)
|
||||
).toBeTruthy();
|
||||
await page.getByRole("button", { name: "Edit Flow" }).click();
|
||||
//inside the flow
|
||||
await page
|
||||
.locator(
|
||||
"//html/body/div/div/div[2]/div/main/div/div/div/div[1]/div[1]/div[1]/div/div[2]/div[1]/div/div[1]/div"
|
||||
|
|
@ -84,16 +119,11 @@ test.describe("Group component tests", () => {
|
|||
)
|
||||
.click();
|
||||
await page.getByLabel("Ungroup").click();
|
||||
await expect(
|
||||
page.locator(
|
||||
"//html/body/div/div/div[2]/div/main/div/div/div/div[1]/div[1]/div/div/div[2]/div[3]/div/div[2]/div[4]/div/div[2]/div/input"
|
||||
)
|
||||
).toHaveValue("fieldValue");
|
||||
await expect(page.locator('//*[@id="input-2"]')).toHaveValue("fieldValue");
|
||||
expect(
|
||||
await page
|
||||
.locator(
|
||||
"//html/body/div/div/div[2]/div/main/div/div/div/div[1]/div[1]/div/div/div[2]/div[2]/div/div[2]/div[5]/div/div[2]/div/button/span[1]"
|
||||
)
|
||||
.getByTestId(/.*rf__node-AgentInitializer.*/)
|
||||
.getByRole("button", { name: "openai-functions" })
|
||||
.textContent()
|
||||
).toBe("openai-functions");
|
||||
});
|
||||
172
src/frontend/tests/onlyFront/inputComponent.spec.ts
Normal file
172
src/frontend/tests/onlyFront/inputComponent.spec.ts
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test("InputComponent", async ({ page }) => {
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.route("**/api/v1/flows/", async (route) => {
|
||||
const json = {
|
||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
||||
};
|
||||
await route.fulfill({ json, status: 201 });
|
||||
});
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("Chroma");
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page
|
||||
.locator('//*[@id="sideChroma"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
|
||||
await page.locator("#input-8").click();
|
||||
await page
|
||||
.locator("#input-8")
|
||||
.fill("collection_name_test_123123123!@#$&*(&%$@");
|
||||
|
||||
let value = await page.locator("#input-8").inputValue();
|
||||
|
||||
if (value != "collection_name_test_123123123!@#$&*(&%$@") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page
|
||||
.locator(
|
||||
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
|
||||
)
|
||||
.click();
|
||||
await page.locator('//*[@id="editAdvancedIcon"]').click();
|
||||
|
||||
await page.locator('//*[@id="showchroma_server_cors_allow_origins"]').click();
|
||||
expect(
|
||||
await page
|
||||
.locator('//*[@id="showchroma_server_cors_allow_origins"]')
|
||||
.isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showchroma_server_grpc_port"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showchroma_server_grpc_port"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showchroma_server_host"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showchroma_server_host"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showchroma_server_http_port"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showchroma_server_http_port"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showchroma_server_ssl_enabled"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showcollection_name"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showcollection_name"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showpersist"]').click();
|
||||
expect(await page.locator('//*[@id="showpersist"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showpersist_directory"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showpersist_directory"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showsearch_kwargs"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showchroma_server_cors_allow_origins"]').click();
|
||||
expect(
|
||||
await page
|
||||
.locator('//*[@id="showchroma_server_cors_allow_origins"]')
|
||||
.isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showchroma_server_grpc_port"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showchroma_server_grpc_port"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showchroma_server_host"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showchroma_server_host"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showchroma_server_http_port"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showchroma_server_http_port"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showchroma_server_ssl_enabled"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showpersist"]').click();
|
||||
expect(await page.locator('//*[@id="showpersist"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showpersist_directory"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showpersist_directory"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showsearch_kwargs"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
let valueEditNode = await page.locator('//*[@id="input-5"]').inputValue();
|
||||
|
||||
if (valueEditNode != "collection_name_test_123123123!@#$&*(&%$@") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
|
||||
await page.locator('//*[@id="input-5"]').click();
|
||||
await page
|
||||
.locator('//*[@id="input-5"]')
|
||||
.fill("NEW_collection_name_test_123123123!@#$&*(&%$@");
|
||||
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
const plusButtonLocator = page.locator("#input-8");
|
||||
const elementCount = await plusButtonLocator.count();
|
||||
if (elementCount === 0) {
|
||||
expect(true).toBeTruthy();
|
||||
|
||||
await page
|
||||
.locator(
|
||||
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]/div'
|
||||
)
|
||||
.click();
|
||||
await page.locator('//*[@id="editAdvancedIcon"]').click();
|
||||
|
||||
await page.locator('//*[@id="showcollection_name"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showcollection_name"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
let value = await page.locator("#input-8").inputValue();
|
||||
|
||||
if (value != "NEW_collection_name_test_123123123!@#$&*(&%$@") {
|
||||
expect(false).toBeTruthy();
|
||||
}
|
||||
}
|
||||
});
|
||||
254
src/frontend/tests/onlyFront/saveComponents.spec.ts
Normal file
254
src/frontend/tests/onlyFront/saveComponents.spec.ts
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
import { Page, expect, test } from "@playwright/test";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
test.describe("save component tests", () => {
|
||||
async function saveComponent(page: Page, pattern: RegExp, n: number) {
|
||||
for (let i = 0; i < n; i++) {
|
||||
await page.getByTestId(pattern).click();
|
||||
//more node options
|
||||
await page
|
||||
.locator(
|
||||
"//html/body/div/div/div[2]/div/main/div/div/div/div[1]/div[1]/div[2]/div/span/button[3]/div/div"
|
||||
)
|
||||
.click();
|
||||
await page.getByLabel("Save").click();
|
||||
}
|
||||
}
|
||||
|
||||
/// <reference lib="dom"/>
|
||||
test("save group component tests", async ({ page }) => {
|
||||
//make front work withoput backend
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.route("**/api/v1/flows/", async (route) => {
|
||||
const json = {
|
||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
||||
};
|
||||
await route.fulfill({ json, status: 201 });
|
||||
});
|
||||
await page.goto("http:localhost:3000/");
|
||||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
||||
// Read your file into a buffer.
|
||||
const jsonContent = readFileSync(
|
||||
"tests/onlyFront/assets/flow.json",
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
// Create the DataTransfer and File
|
||||
const dataTransfer = await page.evaluateHandle((data) => {
|
||||
const dt = new DataTransfer();
|
||||
// Convert the buffer to a hex array
|
||||
const file = new File([data], "flow.json", {
|
||||
type: "application/json",
|
||||
});
|
||||
dt.items.add(file);
|
||||
return dt;
|
||||
}, jsonContent);
|
||||
|
||||
// Now dispatch
|
||||
await page.dispatchEvent('//*[@id="root"]/div/div[2]/div[2]', "drop", {
|
||||
dataTransfer,
|
||||
});
|
||||
expect(
|
||||
await page
|
||||
.locator(".main-page-flows-display")
|
||||
.evaluate((el) => el.children)
|
||||
).toBeTruthy();
|
||||
await page.getByRole("button", { name: "Edit Flow" }).click();
|
||||
//inside the flow
|
||||
await page
|
||||
.locator(
|
||||
"//html/body/div/div/div[2]/div/main/div/div/div/div[1]/div[1]/div[1]/div/div[2]/div[1]/div/div[1]/div"
|
||||
)
|
||||
.click({
|
||||
modifiers: ["Control"],
|
||||
});
|
||||
await page
|
||||
.locator(
|
||||
"//html/body/div/div/div[2]/div/main/div/div/div/div[1]/div[1]/div[1]/div/div[2]/div[2]/div/div[1]/div"
|
||||
)
|
||||
.click({
|
||||
modifiers: ["Control"],
|
||||
});
|
||||
await page
|
||||
.locator(
|
||||
"//html/body/div/div/div[2]/div/main/div/div/div/div[1]/div[1]/div[1]/div/div[2]/div[3]/div/div[1]/div"
|
||||
)
|
||||
.click({
|
||||
modifiers: ["Control"],
|
||||
});
|
||||
await page.getByRole("button", { name: "Group" }).click();
|
||||
expect(
|
||||
await page
|
||||
.locator(
|
||||
"//html/body/div/div/div[2]/div/main/div/div/div/div[1]/div[1]/div[1]/div/div[2]/div/div"
|
||||
)
|
||||
.isVisible()
|
||||
).toBeTruthy();
|
||||
await page.getByPlaceholder("Type something...").first().click();
|
||||
await page.getByPlaceholder("Type something...").first().fill("save");
|
||||
await page.locator(".react-flow__pane").click();
|
||||
await page
|
||||
.locator(".side-bar-buttons-arrangement > div:nth-child(3)")
|
||||
.click();
|
||||
//more option click
|
||||
await page
|
||||
.locator(
|
||||
"//html/body/div/div/div[2]/div/main/div/div/div/div[1]/div[1]/div[2]/div/span/button[3]/div/div"
|
||||
)
|
||||
.click();
|
||||
await page.getByLabel("Save").click();
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("save");
|
||||
await page.waitForTimeout(2000);
|
||||
await page
|
||||
.locator('//*[@id="custom_componentssave"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.waitForTimeout(2000);
|
||||
expect(
|
||||
(await page.getByTestId(/.*rf__node-AgentInitializer.*/).all()).length
|
||||
).toBe(2);
|
||||
await page.locator(".isolate > button").first().click();
|
||||
expect(
|
||||
(await page.getByTestId(/.*rf__node-AgentInitializer.*/).all()).length
|
||||
).toBe(1);
|
||||
await page.getByTestId(/.*rf__node-AgentInitializer.*/).click();
|
||||
await page.getByTestId(/.*rf__node-AgentInitializer.*/).press("Backspace");
|
||||
await page
|
||||
.locator('//*[@id="custom_componentssave"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.getByTestId(/.*rf__node-AgentInitializer.*/).click();
|
||||
await page
|
||||
.locator(
|
||||
"//html/body/div/div/div[2]/div/main/div/div/div/div[1]/div[1]/div[2]/div/span/button[3]/div/div"
|
||||
)
|
||||
.click();
|
||||
await page.getByLabel("Ungroup").click();
|
||||
expect((await page.getByTestId(/.*rf__node-.*/).all()).length).toBe(3);
|
||||
expect(
|
||||
(await page.getByTestId(/.*rf__edge-reactflow.*/).all()).length
|
||||
).toBe(2);
|
||||
});
|
||||
|
||||
test("save default component with custom values", async ({ page }) => {
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.route("**/api/v1/flows/", async (route) => {
|
||||
const json = {
|
||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
||||
};
|
||||
await route.fulfill({ json, status: 201 });
|
||||
});
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("Chroma");
|
||||
|
||||
await page
|
||||
.locator('//*[@id="vectorstoresChroma"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.locator("#input-8").click();
|
||||
await page.locator("#input-8").fill("test");
|
||||
await saveComponent(page, /.*rf__node-Chroma.*/, 1);
|
||||
await page.getByTestId(/.*rf__node-Chroma.*/).press("Backspace");
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("");
|
||||
await page.getByPlaceholder("Search").fill("Chroma");
|
||||
await page
|
||||
.locator('//*[@id="custom_componentsChroma"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
expect(await page.locator("#input-8").inputValue()).toBe("test");
|
||||
});
|
||||
|
||||
test("save same component multiple times", async ({ page }) => {
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.route("**/api/v1/flows/", async (route) => {
|
||||
const json = {
|
||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
||||
};
|
||||
await route.fulfill({ json, status: 201 });
|
||||
});
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("Chroma");
|
||||
|
||||
await page
|
||||
.locator('//*[@id="vectorstoresChroma"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await saveComponent(page, /.*rf__node-Chroma.*/, 3);
|
||||
await page.getByTestId(/.*rf__node-Chroma.*/).press("Backspace");
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("");
|
||||
await page.getByPlaceholder("Search").fill("Chroma");
|
||||
expect(
|
||||
await page.locator('//*[@id="custom_componentsChroma"]').isVisible()
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
await page.locator('[id="custom_componentsChroma\\ \\(1\\)"]').isVisible()
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
await page.locator('[id="custom_componentsChroma\\ \\(2\\)"]').isVisible()
|
||||
).toBeTruthy();
|
||||
await page
|
||||
.locator('[id="custom_componentsChroma\\ \\(2\\)"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
expect(
|
||||
(await page.getByTestId(/.*rf__node-Chroma.*/).allInnerTexts()).includes(
|
||||
"Chroma (2)"
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test("save default component and delete it", async ({ page }) => {
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.route("**/api/v1/flows/", async (route) => {
|
||||
const json = {
|
||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
||||
};
|
||||
await route.fulfill({ json, status: 201 });
|
||||
});
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("Chroma");
|
||||
|
||||
await page
|
||||
.locator('//*[@id="vectorstoresChroma"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await saveComponent(page, /.*rf__node-Chroma.*/, 1);
|
||||
await page.getByTestId(/.*rf__node-Chroma.*/).press("Backspace");
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("");
|
||||
await page.getByPlaceholder("Search").fill("Chroma");
|
||||
await page.locator("#custom_componentsChroma").getByRole("combobox").click({
|
||||
button: "right",
|
||||
});
|
||||
await page.getByLabel("Delete").click();
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill(" ");
|
||||
await page.getByPlaceholder("Search").fill("Chroma");
|
||||
expect(
|
||||
await page.locator("#custom_componentsChroma").isVisible()
|
||||
).toBeFalsy();
|
||||
});
|
||||
});
|
||||
176
src/frontend/tests/onlyFront/toggleComponent.spec.ts
Normal file
176
src/frontend/tests/onlyFront/toggleComponent.spec.ts
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
|
||||
test("ToggleComponent", async ({ page }) => {
|
||||
await page.routeFromHAR("harFiles/langflow.har", {
|
||||
url: "**/api/v1/**",
|
||||
update: false,
|
||||
});
|
||||
await page.route("**/api/v1/flows/", async (route) => {
|
||||
const json = {
|
||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
||||
};
|
||||
await route.fulfill({ json, status: 201 });
|
||||
});
|
||||
await page.goto("http://localhost:3000/");
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await page.getByPlaceholder("Search").click();
|
||||
await page.getByPlaceholder("Search").fill("directoryLoader");
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
await page
|
||||
.locator('//*[@id="sideDirectoryLoader"]')
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||
await page.mouse.up();
|
||||
await page.mouse.down();
|
||||
|
||||
await page
|
||||
.locator(
|
||||
'//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[1]/div'
|
||||
)
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="showload_hidden"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showload_hidden"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="toggle-1"]').click();
|
||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="toggle-1"]').click();
|
||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="toggle-1"]').click();
|
||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="toggle-1"]').click();
|
||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="toggle-1"]').click();
|
||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page
|
||||
.locator(
|
||||
'//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[1]/div'
|
||||
)
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
expect(
|
||||
await page.locator('//*[@id="toggle-edit-1"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showglob"]').click();
|
||||
expect(await page.locator('//*[@id="showglob"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showload_hidden"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showload_hidden"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showmax_concurrency"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showmax_concurrency"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showmetadata"]').click();
|
||||
expect(await page.locator('//*[@id="showmetadata"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showpath"]').click();
|
||||
expect(await page.locator('//*[@id="showpath"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showrecursive"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showrecursive"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showsilent_errors"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showsilent_errors"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showuse_multithreading"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showuse_multithreading"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showglob"]').click();
|
||||
expect(await page.locator('//*[@id="showglob"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showmax_concurrency"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showmax_concurrency"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showmetadata"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showmetadata"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showpath"]').click();
|
||||
expect(await page.locator('//*[@id="showpath"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="showrecursive"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showrecursive"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showsilent_errors"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showsilent_errors"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="showuse_multithreading"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showuse_multithreading"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
const plusButtonLocator = page.locator('//*[@id="toggle-1"]');
|
||||
const elementCount = await plusButtonLocator.count();
|
||||
if (elementCount === 0) {
|
||||
expect(true).toBeTruthy();
|
||||
|
||||
await page
|
||||
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
|
||||
.click();
|
||||
await page.locator('//*[@id="advancedIcon"]').click();
|
||||
await page.locator('//*[@id="editAdvancedBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="showload_hidden"]').click();
|
||||
expect(
|
||||
await page.locator('//*[@id="showload_hidden"]').isChecked()
|
||||
).toBeTruthy();
|
||||
|
||||
expect(
|
||||
await page.locator('//*[@id="toggle-edit-1"]').isChecked()
|
||||
).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||
|
||||
await page.locator('//*[@id="toggle-1"]').click();
|
||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="toggle-1"]').click();
|
||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="toggle-1"]').click();
|
||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeTruthy();
|
||||
|
||||
await page.locator('//*[@id="toggle-1"]').click();
|
||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeFalsy();
|
||||
|
||||
await page.locator('//*[@id="toggle-1"]').click();
|
||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import { test } from "@playwright/test";
|
||||
|
||||
test("test", async ({ page }) => {
|
||||
// Recording...
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue