📦 chore(router.py): add component_router to APIRouter to include component routes
📦 chore(__init__.py): add component_router to __all__ list to expose component routes 📦 feat(components.py): add routes for creating, reading, updating, and deleting components 📦 chore(endpoints.py): import Component model from database.models.component 📦 chore(schemas.py): add ComponentListCreate and ComponentListRead schemas 📦 feat(models/component.py): add Component model with fields for name, description, and data 📦 feat(models/component.py): add ComponentCreate, ComponentRead, and ComponentUpdate models for CRUD operations on components
This commit is contained in:
parent
719015b5bb
commit
fb91b17c51
6 changed files with 561 additions and 694 deletions
1108
poetry.lock
generated
1108
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -6,6 +6,7 @@ from langflow.api.v1 import (
|
||||||
validate_router,
|
validate_router,
|
||||||
flows_router,
|
flows_router,
|
||||||
flow_styles_router,
|
flow_styles_router,
|
||||||
|
component_router,
|
||||||
)
|
)
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
|
|
@ -14,5 +15,6 @@ router = APIRouter(
|
||||||
router.include_router(chat_router)
|
router.include_router(chat_router)
|
||||||
router.include_router(endpoints_router)
|
router.include_router(endpoints_router)
|
||||||
router.include_router(validate_router)
|
router.include_router(validate_router)
|
||||||
|
router.include_router(component_router)
|
||||||
router.include_router(flows_router)
|
router.include_router(flows_router)
|
||||||
router.include_router(flow_styles_router)
|
router.include_router(flow_styles_router)
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,12 @@ from langflow.api.v1.validate import router as validate_router
|
||||||
from langflow.api.v1.chat import router as chat_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.flows import router as flows_router
|
||||||
from langflow.api.v1.flow_styles import router as flow_styles_router
|
from langflow.api.v1.flow_styles import router as flow_styles_router
|
||||||
|
from langflow.api.v1.components import router as component_router
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"chat_router",
|
"chat_router",
|
||||||
"endpoints_router",
|
"endpoints_router",
|
||||||
|
"component_router",
|
||||||
"validate_router",
|
"validate_router",
|
||||||
"flows_router",
|
"flows_router",
|
||||||
"flow_styles_router",
|
"flow_styles_router",
|
||||||
|
|
|
||||||
83
src/backend/langflow/api/v1/components.py
Normal file
83
src/backend/langflow/api/v1/components.py
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
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.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"
|
||||||
|
|
||||||
|
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.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"}
|
||||||
|
|
@ -125,3 +125,11 @@ class CustomComponentCode(BaseModel):
|
||||||
class CustomComponentResponseError(BaseModel):
|
class CustomComponentResponseError(BaseModel):
|
||||||
detail: str
|
detail: str
|
||||||
traceback: str
|
traceback: str
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentListCreate(BaseModel):
|
||||||
|
flows: List[FlowCreate]
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentListRead(BaseModel):
|
||||||
|
flows: List[FlowRead]
|
||||||
|
|
|
||||||
52
src/backend/langflow/database/models/component.py
Normal file
52
src/backend/langflow/database/models/component.py
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentBase(SQLModelSerializable):
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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},
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentCreate(ComponentBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentRead(ComponentBase):
|
||||||
|
id: UUID
|
||||||
|
|
||||||
|
|
||||||
|
class ComponentUpdate(SQLModelSerializable):
|
||||||
|
name: Optional[str] = None
|
||||||
|
description: Optional[str] = None
|
||||||
|
data: Optional[Dict] = None
|
||||||
Loading…
Add table
Add a link
Reference in a new issue