feat: Langflow authoring pipeline with tests
- LangflowClient (list/export, x-api-key auth, error mapping) - import_from_langflow: export -> versioned definition -> LFX deploy - POST /api/flows/import, GET /api/flows/langflow/available - scripts/deploy_flow.py CLI - 25 tests green (Langflow faked via dependency override + respx client tests; respx passthrough proven unreliable for LFX traffic)
This commit is contained in:
parent
96c0da33c7
commit
1034206870
8 changed files with 451 additions and 0 deletions
|
|
@ -15,6 +15,13 @@ from app.db import get_engine, get_session
|
|||
from app.settings import settings
|
||||
|
||||
from . import runner, store
|
||||
from .deploy import import_from_langflow
|
||||
from .langflow_client import (
|
||||
LangflowClient,
|
||||
LangflowError,
|
||||
LangflowNotFound,
|
||||
LangflowUnavailable,
|
||||
)
|
||||
from .lfx_client import (
|
||||
LfxClient,
|
||||
LfxConflict,
|
||||
|
|
@ -29,6 +36,8 @@ from .schemas import (
|
|||
ApprovalRead,
|
||||
FlowDefinitionCreate,
|
||||
FlowDefinitionRead,
|
||||
FlowImportRead,
|
||||
FlowImportRequest,
|
||||
FlowVersionCreate,
|
||||
FlowVersionRead,
|
||||
RunRead,
|
||||
|
|
@ -47,6 +56,12 @@ def get_lfx() -> LfxClient:
|
|||
return LfxClient(settings.lfx_url, api_key=settings.lfx_api_key or None)
|
||||
|
||||
|
||||
def get_langflow() -> LangflowClient:
|
||||
return LangflowClient(
|
||||
settings.langflow_url, api_key=settings.langflow_api_key or ""
|
||||
)
|
||||
|
||||
|
||||
def _read_definition(d) -> FlowDefinitionRead:
|
||||
return FlowDefinitionRead(
|
||||
id=d.id,
|
||||
|
|
@ -282,6 +297,58 @@ def list_runs(flow_id: UUID, session: Session = Depends(get_db)):
|
|||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@flows_router.get("/langflow/available")
|
||||
async def list_langflow_flows(langflow: LangflowClient = Depends(get_langflow)):
|
||||
"""Browse flows on the Langflow authoring server (admin import source)."""
|
||||
try:
|
||||
return await langflow.list_flows()
|
||||
except LangflowUnavailable as exc:
|
||||
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
||||
except LangflowError as exc:
|
||||
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@flows_router.post("/import", response_model=FlowImportRead, status_code=201)
|
||||
async def import_flow(
|
||||
data: FlowImportRequest,
|
||||
session: Session = Depends(get_db),
|
||||
langflow: LangflowClient = Depends(get_langflow),
|
||||
lfx: LfxClient = Depends(get_lfx),
|
||||
):
|
||||
"""Import a flow from Langflow: export -> version -> (deploy to LFX)."""
|
||||
try:
|
||||
definition, version = await import_from_langflow(
|
||||
session,
|
||||
langflow,
|
||||
lfx,
|
||||
data.langflow_flow_id,
|
||||
name=data.name,
|
||||
author=data.author,
|
||||
deploy=data.deploy,
|
||||
)
|
||||
except LangflowNotFound as exc:
|
||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||
except LangflowUnavailable as exc:
|
||||
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
||||
except LangflowError as exc:
|
||||
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
||||
except store.ConflictError as exc:
|
||||
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
||||
except store.InvalidError as exc:
|
||||
raise HTTPException(status_code=422, detail=str(exc)) from exc
|
||||
except LfxInvalidFlow as exc:
|
||||
raise HTTPException(status_code=422, detail=str(exc)) from exc
|
||||
except LfxConflict as exc:
|
||||
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
||||
except LfxUnavailable as exc:
|
||||
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
||||
except LfxError as exc:
|
||||
raise HTTPException(status_code=502, detail=str(exc)) from exc
|
||||
return FlowImportRead(
|
||||
definition=_read_definition(definition), version=_read_version(version)
|
||||
)
|
||||
|
||||
|
||||
@flows_router.get("/runs/{run_id}", response_model=RunRead)
|
||||
def get_run(run_id: UUID, session: Session = Depends(get_db)):
|
||||
try:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue