From 4d7b66a4e23aede9a0efdb333e3b11c652dd2cb6 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 18 Oct 2023 17:01:54 -0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20refactor(components.py):=20remov?= =?UTF-8?q?e=20unused=20code=20and=20endpoints=20related=20to=20components?= =?UTF-8?q?=20=E2=9C=A8=20feat(store.py):=20add=20new=20endpoints=20for=20?= =?UTF-8?q?creating,=20reading,=20and=20listing=20components=20in=20the=20?= =?UTF-8?q?store?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/v1/components.py | 77 -------------- src/backend/langflow/api/v1/store.py | 124 ++++++++++++++++++++++ 2 files changed, 124 insertions(+), 77 deletions(-) delete mode 100644 src/backend/langflow/api/v1/components.py create mode 100644 src/backend/langflow/api/v1/store.py diff --git a/src/backend/langflow/api/v1/components.py b/src/backend/langflow/api/v1/components.py deleted file mode 100644 index 9ac5b9a7f..000000000 --- a/src/backend/langflow/api/v1/components.py +++ /dev/null @@ -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} diff --git a/src/backend/langflow/api/v1/store.py b/src/backend/langflow/api/v1/store.py new file mode 100644 index 000000000..f8d727ea9 --- /dev/null +++ b/src/backend/langflow/api/v1/store.py @@ -0,0 +1,124 @@ +from datetime import timezone +from typing import List, TYPE_CHECKING, 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 sqlmodel import Session, select +from fastapi import APIRouter, Depends, HTTPException, Query +from sqlalchemy.exc import IntegrityError +from datetime import datetime + +if TYPE_CHECKING: + 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) + )