diff --git a/src/backend/langflow/api/v1/components.py b/src/backend/langflow/api/v1/components.py index 8ca808eef..299f78371 100644 --- a/src/backend/langflow/api/v1/components.py +++ b/src/backend/langflow/api/v1/components.py @@ -1,16 +1,9 @@ +from typing import List from uuid import UUID -from langflow.settings import settings -from langflow.api.utils import remove_api_keys -from langflow.database.models.component import ( - Component, - ComponentCreate, - ComponentRead, - ComponentUpdate, -) +from langflow.database.models.component import Component from langflow.database.base import get_session from sqlmodel import Session, select from fastapi import APIRouter, Depends, HTTPException -from fastapi.encoders import jsonable_encoder COMPONENT_NOT_FOUND = "Component not found" @@ -18,66 +11,112 @@ COMPONENT_NOT_FOUND = "Component not found" router = APIRouter(prefix="/components", tags=["Components"]) -@router.post("/", response_model=ComponentRead, status_code=201) -def create(*, session: Session = Depends(get_session), component: ComponentCreate): - db = Component.from_orm(component) - session.add(db) - session.commit() - session.refresh(db) - - return db +@router.post("/", response_model=Component) +def create_component(component: Component, db: Session = Depends(get_session)): + db.add(component) + db.commit() + db.refresh(component) + return component -@router.get("/", response_model=list[ComponentRead], status_code=200) -def read_all(*, session: Session = Depends(get_session)): - try: - sql = select(Component) - components = session.exec(sql).all() - except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) from e - - return [jsonable_encoder(component) for component in components] - - -@router.get("/{id}", response_model=ComponentRead, status_code=200) -def read(*, session: Session = Depends(get_session), id: UUID): - if component := session.get(Component, id): +@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.patch("/{id}", response_model=ComponentRead, status_code=200) -def update( - *, session: Session = Depends(get_session), id: UUID, component: ComponentUpdate +@router.get("/", response_model=List[Component]) +def read_components(skip: int = 0, limit: int = 50, db: Session = Depends(get_session)): + return db.execute(select(Component).offset(skip).limit(limit)).fetchall() + + +@router.patch("/{component_id}", response_model=Component) +def update_component( + component_id: UUID, component: Component, db: Session = Depends(get_session) ): - db = session.get(Component, id) - if not db: + db_component = db.get(Component, component_id) + if not db_component: raise HTTPException(status_code=404, detail=COMPONENT_NOT_FOUND) - - data = component.dict(exclude_unset=True) - - if settings.remove_api_keys: - data = remove_api_keys(data) - - for key, value in data.items(): - setattr(db, key, value) - - session.add(db) - session.commit() - session.refresh(db) - - return db + component_data = component.dict(exclude_unset=True) + for key, value in component_data.items(): + setattr(db_component, key, value) + db.commit() + db.refresh(db_component) + return db_component -@router.delete("/{id}", status_code=200) -def delete(*, session: Session = Depends(get_session), id: UUID): - component = session.get(Component, id) - +@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"} - session.delete(component) - session.commit() - return {"message": "Component deleted successfully"} +# @router.post("/", response_model=ComponentRead, status_code=201) +# def create(*, session: Session = Depends(get_session), component: ComponentCreate): +# db = Component.from_orm(component) +# session.add(db) +# session.commit() +# session.refresh(db) + +# return db + + +# @router.get("/", response_model=list[ComponentRead], status_code=200) +# def read_all(*, session: Session = Depends(get_session)): +# try: +# sql = select(Component) +# components = session.exec(sql).all() +# except Exception as e: +# raise HTTPException(status_code=500, detail=str(e)) from e + +# return [jsonable_encoder(component) for component in components] + + +# @router.get("/{id}", response_model=ComponentRead, status_code=200) +# def read(*, session: Session = Depends(get_session), id: UUID): +# if component := session.get(Component, id): +# return component +# else: +# raise HTTPException(status_code=404, detail=COMPONENT_NOT_FOUND) + + +# @router.patch("/{id}", response_model=ComponentRead, status_code=200) +# def update( +# *, session: Session = Depends(get_session), id: UUID, component: ComponentUpdate +# ): +# db = session.get(Component, id) +# if not db: +# raise HTTPException(status_code=404, detail=COMPONENT_NOT_FOUND) + +# data = component.dict(exclude_unset=True) + +# if settings.remove_api_keys: +# data = remove_api_keys(data) + +# for key, value in data.items(): +# setattr(db, key, value) + +# session.add(db) +# session.commit() +# session.refresh(db) + +# return db + + +# @router.delete("/{id}", status_code=200) +# def delete(*, session: Session = Depends(get_session), id: UUID): +# component = session.get(Component, id) + +# if not component: +# raise HTTPException(status_code=404, detail=COMPONENT_NOT_FOUND) + +# session.delete(component) +# session.commit() + +# return {"message": "Component deleted successfully"} diff --git a/src/backend/langflow/database/models/component.py b/src/backend/langflow/database/models/component.py index ddac8309b..bad2d7a54 100644 --- a/src/backend/langflow/database/models/component.py +++ b/src/backend/langflow/database/models/component.py @@ -1,52 +1,83 @@ -from uuid import UUID, uuid4 -from pydantic import validator -from typing import Dict, Optional -from sqlmodel import Field, JSON, Column - from langflow.database.models.base import SQLModelSerializable +from sqlmodel import Field +from typing import Optional +from datetime import datetime +import uuid + +# def orjson_dumps(v, *, default): +# # orjson.dumps returns bytes, to match standard json.dumps we need to decode +# return orjson.dumps(v, default=default).decode() + +# class SQLModelSerializable(SQLModel): +# class Config: +# orm_mode = True +# json_loads = orjson.loads +# json_dumps = orjson_dumps + +# DATABASE_URL = "sqlite+pysqlite:///./database.db" + +# engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False}, poolclass=StaticPool) -class ComponentBase(SQLModelSerializable): +class Component(SQLModelSerializable, table=True): + id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) + id_frontend_node: uuid.UUID = Field(index=True) name: str = Field(index=True) description: Optional[str] = Field(index=True) - data: Optional[Dict] = Field(default=None) - - @validator("data") - def validate_json(v): - # dict_keys(['description', 'name', 'id', 'data']) - if not v: - return v - if not isinstance(v, dict): - raise ValueError("Flow must be a valid JSON") - - # data must contain nodes and edges - if "nodes" not in v.keys(): - raise ValueError("Flow must have nodes") - if "edges" not in v.keys(): - raise ValueError("Flow must have edges") - - return v + code_python: Optional[str] = Field(default=None) + return_type: Optional[str] = Field(index=True) + create_at: datetime = Field(default_factory=datetime.utcnow) + update_at: datetime = Field(default_factory=datetime.utcnow) + is_disabled: bool = Field(default=False) + is_read_only: bool = Field(default=False) -class Component(ComponentBase, table=True): - id: UUID = Field(default_factory=uuid4, primary_key=True, unique=True) - data: Optional[Dict] = Field(default=None, sa_column=Column(JSON)) - # style: Optional["FlowStyle"] = Relationship( - # back_populates="flow", - # # use "uselist=False" to make it a one-to-one relationship - # sa_relationship_kwargs={"uselist": False}, - # ) +# app = FastAPI() +# def get_db(): +# with Session(engine) as session: +# yield session -class ComponentCreate(ComponentBase): - pass +# @app.on_event("startup") +# def on_startup(): +# SQLModel.metadata.create_all(engine) +# @app.post("/components/", response_model=Component) +# def create_component(component: Component, db: Session = Depends(get_db)): +# db.add(component) +# db.commit() +# db.refresh(component) +# return component -class ComponentRead(ComponentBase): - id: UUID +# @app.get("/components/{component_id}", response_model=Component) +# def read_component(component_id: uuid.UUID, db: Session = Depends(get_db)): +# component = db.get(Component, component_id) +# if not component: +# raise HTTPException(status_code=404, detail="Component not found") +# return component +# @app.get("/components/", response_model=List[Component]) +# def read_components(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): +# components = db.execute(select(Component).offset(skip).limit(limit)).fetchall() +# return components -class ComponentUpdate(SQLModelSerializable): - name: Optional[str] = None - description: Optional[str] = None - data: Optional[Dict] = None +# @app.put("/components/{component_id}", response_model=Component) +# def update_component(component_id: uuid.UUID, component: Component, db: Session = Depends(get_db)): +# 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.commit() +# db.refresh(db_component) +# return db_component + +# @app.delete("/components/{component_id}") +# def delete_component(component_id: uuid.UUID, db: Session = Depends(get_db)): +# 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"}