Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/backend/base/langflow/api/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from langflow.api.v1 import (
api_key_router,
chat_router,
deployment_router,
endpoints_router,
files_router,
flow_version_router,
Expand Down Expand Up @@ -62,6 +63,7 @@
router_v1.include_router(openai_responses_router)
router_v1.include_router(models_router)
router_v1.include_router(model_options_router)
router_v1.include_router(deployment_router)


# Agentic flow execution - lazy import to avoid circular dependency
Expand Down
2 changes: 2 additions & 0 deletions src/backend/base/langflow/api/v1/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from langflow.api.v1.api_key import router as api_key_router
from langflow.api.v1.chat import router as chat_router
from langflow.api.v1.deployments import router as deployment_router
from langflow.api.v1.endpoints import router as endpoints_router
from langflow.api.v1.files import router as files_router
from langflow.api.v1.flow_version import router as flow_version_router
Expand All @@ -25,6 +26,7 @@
__all__ = [
"api_key_router",
"chat_router",
"deployment_router",
"endpoints_router",
"files_router",
"flow_version_router",
Expand Down
284 changes: 284 additions & 0 deletions src/backend/base/langflow/api/v1/deployments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
# ruff: noqa: ARG001
# TODO: Remove noqa once endpoint stubs are replaced with real implementations.
from __future__ import annotations

from typing import Annotated
from uuid import UUID

from fastapi import APIRouter, HTTPException, Path, Query, status
from lfx.services.adapters.deployment.schema import (
DeploymentType,
)

from langflow.api.utils import CurrentActiveUser, DbSession, DbSessionReadOnly
from langflow.api.v1.schemas.deployments import (
DeploymentCreateRequest,
DeploymentCreateResponse,
DeploymentDuplicateResponse,
DeploymentGetResponse,
DeploymentListResponse,
DeploymentProviderAccountCreate,
DeploymentProviderAccountListResponse,
DeploymentProviderAccountResponse,
DeploymentProviderAccountUpdate,
DeploymentStatusResponse,
DeploymentTypeListResponse,
DeploymentUpdateRequest,
DeploymentUpdateResponse,
ExecutionCreateRequest,
ExecutionCreateResponse,
ExecutionStatusResponse,
FlowVersionIdsQuery,
RedeployResponse,
)

router = APIRouter(prefix="/deployments", tags=["Deployments"])


DeploymentProviderAccountIdQuery = Annotated[
UUID,
Query(description="Langflow DB provider-account UUID (`deployment_provider_account.id`)."),
]
DeploymentProviderAccountIdPath = Annotated[
UUID,
Path(description="Langflow DB provider-account UUID (`deployment_provider_account.id`)."),
]
DeploymentIdPath = Annotated[
UUID,
Path(description="Langflow DB deployment UUID (`deployment.id`)."),
]


# API provider-context contract matrix:
# - Query/path ``provider_id`` is a Langflow DB UUID referencing deployment_provider_account.
# - Body ``provider_id`` is included on ``DeploymentCreateRequest`` and ``ExecutionCreateRequest``
# to allow provider routing without an extra DB lookup when the caller already has the context.
# - Deployment-scoped routes derive provider context from persisted Langflow relationships.


# ---------------------------------------------------------------------------
# Routes: Provider accounts
# ---------------------------------------------------------------------------


@router.post(
"/providers",
response_model=DeploymentProviderAccountResponse,
status_code=status.HTTP_201_CREATED,
tags=["Deployment Providers"],
)
async def create_provider_account(
session: DbSession,
payload: DeploymentProviderAccountCreate,
current_user: CurrentActiveUser,
):
"""Register a new deployment provider account."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.get("/providers", response_model=DeploymentProviderAccountListResponse, tags=["Deployment Providers"])
async def list_provider_accounts(
session: DbSessionReadOnly,
current_user: CurrentActiveUser,
page: Annotated[int, Query(ge=1)] = 1,
size: Annotated[int, Query(ge=1, le=50)] = 20,
):
"""List all deployment provider accounts for the current user."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.get(
"/providers/{provider_id}",
response_model=DeploymentProviderAccountResponse,
tags=["Deployment Providers"],
)
async def get_provider_account(
provider_id: DeploymentProviderAccountIdPath,
session: DbSessionReadOnly,
current_user: CurrentActiveUser,
):
"""Get a deployment provider account by id."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.delete(
"/providers/{provider_id}",
status_code=status.HTTP_204_NO_CONTENT,
tags=["Deployment Providers"],
)
async def delete_provider_account(
provider_id: DeploymentProviderAccountIdPath,
session: DbSession,
current_user: CurrentActiveUser,
):
"""Delete a deployment provider account."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.patch(
"/providers/{provider_id}",
response_model=DeploymentProviderAccountResponse,
tags=["Deployment Providers"],
)
async def update_provider_account(
provider_id: DeploymentProviderAccountIdPath,
session: DbSession,
payload: DeploymentProviderAccountUpdate,
current_user: CurrentActiveUser,
):
"""Partially update a deployment provider account."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


# ---------------------------------------------------------------------------
# Routes: Deployments
# ---------------------------------------------------------------------------


@router.post("", response_model=DeploymentCreateResponse, status_code=status.HTTP_201_CREATED)
async def create_deployment(
session: DbSession,
payload: DeploymentCreateRequest,
current_user: CurrentActiveUser,
):
"""Create a deployment under the provider account specified in the request body."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.get("/types", response_model=DeploymentTypeListResponse)
async def list_deployment_types(
provider_id: DeploymentProviderAccountIdQuery,
session: DbSessionReadOnly,
current_user: CurrentActiveUser,
):
"""List deployment types for the selected Langflow provider-account UUID."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.get("", response_model=DeploymentListResponse)
async def list_deployments(
provider_id: DeploymentProviderAccountIdQuery,
session: DbSessionReadOnly,
current_user: CurrentActiveUser,
page: Annotated[int, Query(ge=1)] = 1,
size: Annotated[int, Query(ge=1, le=50)] = 20,
deployment_type: Annotated[DeploymentType | None, Query()] = None,
flow_version_ids: Annotated[
FlowVersionIdsQuery,
Query(
description=(
"Optional Langflow flow version ids (pass as repeated query params, "
"e.g. ?flow_version_ids=id1&flow_version_ids=id2). When provided, "
"deployments are filtered to those with at least one matching "
"attachment (OR semantics across ids)."
)
),
] = None,
):
"""List deployments for the selected Langflow provider-account UUID."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


# ---------------------------------------------------------------------------
# Routes: Executions
# ---------------------------------------------------------------------------


@router.post("/executions", response_model=ExecutionCreateResponse, status_code=status.HTTP_201_CREATED)
async def create_deployment_execution(
session: DbSession,
payload: ExecutionCreateRequest,
current_user: CurrentActiveUser,
):
"""Create a deployment execution for Langflow DB deployment/provider identifiers."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.get("/executions/{execution_id}", response_model=ExecutionStatusResponse)
async def get_deployment_execution(
execution_id: Annotated[str, Path(min_length=1, description="Provider-owned opaque execution identifier.")],
provider_id: DeploymentProviderAccountIdQuery,
session: DbSessionReadOnly,
current_user: CurrentActiveUser,
):
"""Get deployment execution status by provider-owned execution id and Langflow provider id."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


# ---------------------------------------------------------------------------
# Routes: Single deployment operations
# ---------------------------------------------------------------------------


@router.get("/{deployment_id}", response_model=DeploymentGetResponse)
async def get_deployment(
deployment_id: DeploymentIdPath,
session: DbSessionReadOnly,
current_user: CurrentActiveUser,
):
"""Get a deployment by id."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.patch(
"/{deployment_id}",
response_model=DeploymentUpdateResponse,
)
async def update_deployment(
deployment_id: DeploymentIdPath,
session: DbSession,
payload: DeploymentUpdateRequest,
current_user: CurrentActiveUser,
):
"""Update a deployment."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.delete("/{deployment_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_deployment(
deployment_id: DeploymentIdPath,
session: DbSession,
current_user: CurrentActiveUser,
):
"""Delete a deployment."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.post(
"/{deployment_id}/redeploy",
response_model=RedeployResponse,
)
async def redeploy_deployment(
deployment_id: DeploymentIdPath,
session: DbSession,
current_user: CurrentActiveUser,
):
"""Redeploy a deployment."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.post(
"/{deployment_id}/duplicate",
response_model=DeploymentDuplicateResponse,
status_code=status.HTTP_201_CREATED,
)
async def duplicate_deployment(
deployment_id: DeploymentIdPath,
session: DbSession,
current_user: CurrentActiveUser,
):
"""Duplicate a deployment."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")


@router.get(
"/{deployment_id}/status",
response_model=DeploymentStatusResponse,
)
async def get_deployment_status(
deployment_id: DeploymentIdPath,
session: DbSessionReadOnly,
current_user: CurrentActiveUser,
):
"""Get deployment status."""
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
Loading
Loading