Skip to content

Commit 962f04f

Browse files
committed
clean up deployments API surface
- Strip internal implementation details from public-facing Field descriptions and endpoint docstrings (adapter routing, provider snapshots, binding payloads, lazy sync) - Rename flow_versions to flow_version_ids in DeploymentCreateRequest and DeploymentUpdateRequest for clarity - Remove redundant match_limit query parameter from list_deployments; page size already caps results
1 parent 6c29a1c commit 962f04f

2 files changed

Lines changed: 93 additions & 60 deletions

File tree

src/backend/base/langflow/api/v1/deployments.py

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@
66

77
from fastapi import APIRouter, HTTPException, Query, status
88
from lfx.services.deployment.schema import (
9-
DeploymentCreateResult,
10-
DeploymentStatusResult,
119
DeploymentType,
12-
DeploymentUpdateResult,
1310
)
1411

15-
from langflow.api.utils import CurrentActiveUser, DbSession
12+
from langflow.api.utils import CurrentActiveUser, DbSession, DbSessionReadOnly
1613
from langflow.api.v1.schemas.deployments import (
1714
DeploymentCreateRequest,
15+
DeploymentCreateResponse,
1816
DeploymentDuplicateParams,
1917
DeploymentDuplicateResponse,
2018
DeploymentGetResponse,
2119
DeploymentListResponse,
20+
DeploymentStatusResponse,
2221
DeploymentTypeListResponse,
2322
DeploymentUpdateRequest,
23+
DeploymentUpdateResponse,
2424
ExecutionCreateRequest,
2525
ExecutionCreateResponse,
2626
ExecutionStatusResponse,
@@ -36,7 +36,7 @@
3636

3737
ProviderIdQuery = Annotated[
3838
UUID,
39-
Query(description="The registered deployment provider account id."),
39+
Query(description="Deployment provider account id."),
4040
]
4141

4242
# API provider-context contract matrix:
@@ -67,7 +67,9 @@ async def create_provider_account(
6767
@router.get("/providers", response_model=ProviderAccountListResponse, tags=["Deployment Providers"])
6868
async def list_provider_accounts(
6969
user: CurrentActiveUser,
70-
db: DbSession,
70+
db: DbSessionReadOnly,
71+
page: Annotated[int, Query(ge=1)] = 1,
72+
size: Annotated[int, Query(ge=1, le=50)] = 20,
7173
):
7274
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
7375

@@ -80,7 +82,7 @@ async def list_provider_accounts(
8082
async def get_provider_account(
8183
provider_id: UUID,
8284
user: CurrentActiveUser,
83-
db: DbSession,
85+
db: DbSessionReadOnly,
8486
):
8587
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
8688

@@ -117,7 +119,7 @@ async def update_provider_account(
117119
# ---------------------------------------------------------------------------
118120

119121

120-
@router.post("", response_model=DeploymentCreateResult, status_code=status.HTTP_201_CREATED)
122+
@router.post("", response_model=DeploymentCreateResponse, status_code=status.HTTP_201_CREATED)
121123
async def create_deployment(
122124
user: CurrentActiveUser,
123125
payload: DeploymentCreateRequest,
@@ -131,7 +133,7 @@ async def create_deployment(
131133
async def list_deployment_types(
132134
provider_id: ProviderIdQuery,
133135
user: CurrentActiveUser,
134-
db: DbSession,
136+
db: DbSessionReadOnly,
135137
):
136138
"""List deployment types for a provider account."""
137139
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
@@ -141,7 +143,9 @@ async def list_deployment_types(
141143
async def list_deployments(
142144
provider_id: ProviderIdQuery,
143145
user: CurrentActiveUser,
144-
db: DbSession,
146+
db: DbSessionReadOnly,
147+
page: Annotated[int, Query(ge=1)] = 1,
148+
size: Annotated[int, Query(ge=1, le=50)] = 20,
145149
deployment_type: Annotated[DeploymentType | None, Query()] = None,
146150
flow_version_ids: Annotated[
147151
list[str] | None,
@@ -152,18 +156,8 @@ async def list_deployments(
152156
)
153157
),
154158
] = None,
155-
match_limit: Annotated[
156-
int | None,
157-
Query(
158-
ge=1,
159-
le=500,
160-
description=(
161-
"Optional cap on number of matching deployments returned per page when flow_version_ids are provided."
162-
),
163-
),
164-
] = None,
165159
):
166-
"""List deployments for a provider account using lazy provider synchronization."""
160+
"""List deployments for a provider account."""
167161
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
168162

169163

@@ -178,20 +172,18 @@ async def create_deployment_execution(
178172
db: DbSession,
179173
user: CurrentActiveUser,
180174
):
181-
"""Create a provider-agnostic deployment execution."""
175+
"""Create a deployment execution."""
182176
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
183177

184178

185179
@router.get("/executions/{execution_id}", response_model=ExecutionStatusResponse)
186180
async def get_deployment_execution(
187181
execution_id: str,
188182
provider_id: ProviderIdQuery,
189-
deployment_id: Annotated[str, Query(description="Deployment id for execution polling.")],
190-
deployment_type: Annotated[DeploymentType, Query(description="Deployment type for execution polling.")],
191-
db: DbSession,
183+
db: DbSessionReadOnly,
192184
user: CurrentActiveUser,
193185
):
194-
"""Get provider-agnostic deployment execution state/output."""
186+
"""Get deployment execution status."""
195187
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
196188

197189

@@ -204,23 +196,23 @@ async def get_deployment_execution(
204196
async def get_deployment(
205197
deployment_id: str,
206198
user: CurrentActiveUser,
207-
db: DbSession,
199+
db: DbSessionReadOnly,
208200
):
209-
"""Get a deployment and derive provider routing from persisted deployment metadata."""
201+
"""Get a deployment by id."""
210202
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
211203

212204

213205
@router.patch(
214206
"/{deployment_id}",
215-
response_model=DeploymentUpdateResult,
207+
response_model=DeploymentUpdateResponse,
216208
)
217209
async def update_deployment(
218210
deployment_id: str,
219211
payload: DeploymentUpdateRequest,
220212
db: DbSession,
221213
user: CurrentActiveUser,
222214
):
223-
"""Update a deployment and derive provider routing from persisted deployment metadata."""
215+
"""Update a deployment."""
224216
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
225217

226218

@@ -230,7 +222,7 @@ async def delete_deployment(
230222
db: DbSession,
231223
user: CurrentActiveUser,
232224
):
233-
"""Delete a deployment and derive provider routing from persisted deployment metadata."""
225+
"""Delete a deployment."""
234226
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
235227

236228

@@ -244,7 +236,7 @@ async def redeploy_deployment(
244236
db: DbSession,
245237
user: CurrentActiveUser,
246238
):
247-
"""Redeploy a deployment and derive provider routing from persisted deployment metadata."""
239+
"""Redeploy a deployment."""
248240
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
249241

250242

@@ -259,18 +251,18 @@ async def duplicate_deployment(
259251
db: DbSession,
260252
user: CurrentActiveUser,
261253
):
262-
"""Duplicate a deployment and derive provider routing from persisted deployment metadata."""
254+
"""Duplicate a deployment."""
263255
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")
264256

265257

266258
@router.get(
267259
"/{deployment_id}/status",
268-
response_model=DeploymentStatusResult,
260+
response_model=DeploymentStatusResponse,
269261
)
270262
async def get_deployment_status(
271263
deployment_id: str,
272-
db: DbSession,
264+
db: DbSessionReadOnly,
273265
user: CurrentActiveUser,
274266
):
275-
"""Get deployment health and derive provider routing from persisted deployment metadata."""
267+
"""Get deployment status."""
276268
raise HTTPException(status_code=status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented.")

src/backend/base/langflow/api/v1/schemas/deployments.py

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ def _normalize_optional_str(value: str | None) -> str | None:
5252

5353

5454
class ProviderAccountCreate(BaseModel):
55+
model_config = {"extra": "forbid"}
56+
5557
account_id: str | None = Field(default=None, min_length=1, description="Provider tenant/organization identifier.")
56-
provider_key: str = Field(min_length=1, description="Deployment adapter routing key.")
58+
provider_key: str = Field(min_length=1, description="Deployment provider key.")
5759
backend_url: str = Field(min_length=1, description="Deployment provider backend URL.")
5860
api_key: str = Field(min_length=1, description="Deployment provider API key.")
5961

@@ -73,8 +75,10 @@ def normalize_account_id(cls, value: str | None) -> str | None:
7375

7476

7577
class ProviderAccountUpdate(BaseModel):
78+
model_config = {"extra": "forbid"}
79+
7680
account_id: str | None = Field(default=None, min_length=1, description="Provider tenant/organization identifier.")
77-
provider_key: str | None = Field(default=None, min_length=1, description="Deployment adapter routing key.")
81+
provider_key: str | None = Field(default=None, min_length=1, description="Deployment provider key.")
7882
backend_url: str | None = Field(default=None, min_length=1, description="Deployment provider backend URL.")
7983
api_key: str | None = Field(default=None, min_length=1, description="Deployment provider API key.")
8084

@@ -127,15 +131,11 @@ class DeploymentGetResponse(DeploymentSummary):
127131
description: str | None = None
128132

129133

130-
class DeploymentListItem(BaseModel):
131-
id: str
134+
class DeploymentListItem(DeploymentSummary):
135+
"""Extended deployment summary with list-specific fields."""
136+
132137
resource_key: str
133-
type: DeploymentType
134-
name: str
135138
attached_count: int = Field(default=0, ge=0)
136-
created_at: datetime | None = None
137-
updated_at: datetime | None = None
138-
provider_data: dict | None = None
139139

140140

141141
class DeploymentListResponse(BaseModel):
@@ -153,6 +153,30 @@ class ProviderAccountListResponse(BaseModel):
153153
total: int = Field(default=0, ge=0)
154154

155155

156+
class DeploymentCreateResponse(BaseModel):
157+
"""API response for deployment creation, wrapping service-layer fields."""
158+
159+
id: IdLike
160+
name: str
161+
description: str = ""
162+
type: DeploymentType | None = None
163+
provider_result: dict[str, Any] | None = None
164+
165+
166+
class DeploymentUpdateResponse(BaseModel):
167+
"""API response for deployment update."""
168+
169+
id: IdLike
170+
provider_result: dict[str, Any] | None = None
171+
172+
173+
class DeploymentStatusResponse(BaseModel):
174+
"""API response for deployment status/health."""
175+
176+
id: IdLike
177+
provider_data: dict[str, Any] | None = None
178+
179+
156180
class RedeployResponse(DeploymentOperationResult):
157181
pass
158182

@@ -164,6 +188,8 @@ class DeploymentDuplicateResponse(DeploymentSummary):
164188
class DeploymentDuplicateParams(BaseModel):
165189
"""Parameters for duplicating a deployment."""
166190

191+
model_config = {"extra": "forbid"}
192+
167193
deployment_type: DeploymentType
168194

169195

@@ -191,6 +217,8 @@ def validate_ids(cls, values: list[str]) -> list[str]:
191217
class FlowVersionsPatch(BaseModel):
192218
"""Add or remove flow version bindings on an existing deployment."""
193219

220+
model_config = {"extra": "forbid"}
221+
194222
add: list[str] | None = Field(
195223
None,
196224
description="Flow version ids to attach to the deployment. Omit to leave unchanged.",
@@ -211,6 +239,11 @@ def validate_id_lists(cls, values: list[str] | None) -> list[str] | None:
211239
def validate_operations(self):
212240
add_values = self.add or []
213241
remove_values = self.remove or []
242+
243+
if not add_values and not remove_values:
244+
msg = "At least one of 'add' or 'remove' must be provided."
245+
raise ValueError(msg)
246+
214247
overlap = set(add_values).intersection(remove_values)
215248
if overlap:
216249
ids = ", ".join(sorted(overlap))
@@ -225,26 +258,37 @@ def validate_operations(self):
225258

226259

227260
class DeploymentCreateRequest(BaseModel):
228-
provider_id: UUID = Field(description="Deployment provider account id for adapter routing.")
229-
spec: BaseDeploymentData = Field(description="The base metadata of the deployment.")
261+
model_config = {"extra": "forbid"}
262+
263+
provider_id: UUID = Field(description="Deployment provider account id.")
264+
spec: BaseDeploymentData = Field(description="Deployment metadata.")
230265
project_id: UUID | None = Field(
231266
default=None,
232267
description="Langflow Project id to persist the deployment under. Defaults to user's Starter Project.",
233268
)
234-
flow_versions: FlowVersionsAttach | None = Field(
269+
flow_version_ids: FlowVersionsAttach | None = Field(
235270
default=None,
236-
description="Flow version ids used to build provider snapshots during deployment creation.",
271+
description="Flow version ids to attach to the deployment.",
237272
)
238-
config: ConfigItem | None = Field(default=None, description="Deployment config binding/create payload.")
273+
config: ConfigItem | None = Field(default=None, description="Deployment configuration.")
239274

240275

241276
class DeploymentUpdateRequest(BaseModel):
277+
model_config = {"extra": "forbid"}
278+
242279
spec: BaseDeploymentDataUpdate | None = Field(default=None, description="Deployment metadata updates.")
243-
flow_versions: FlowVersionsPatch | None = Field(
280+
flow_version_ids: FlowVersionsPatch | None = Field(
244281
default=None,
245-
description="Flow version attach/detach patch payload.",
282+
description="Flow version attach/detach operations.",
246283
)
247-
config: ConfigDeploymentBindingUpdate | None = Field(default=None, description="Deployment config binding patch.")
284+
config: ConfigDeploymentBindingUpdate | None = Field(default=None, description="Deployment configuration update.")
285+
286+
@model_validator(mode="after")
287+
def ensure_any_field_provided(self) -> DeploymentUpdateRequest:
288+
if self.spec is None and self.flow_version_ids is None and self.config is None:
289+
msg = "At least one of 'spec', 'flow_version_ids', or 'config' must be provided."
290+
raise ValueError(msg)
291+
return self
248292

249293

250294
# ---------------------------------------------------------------------------
@@ -253,21 +297,18 @@ class DeploymentUpdateRequest(BaseModel):
253297

254298

255299
class ExecutionCreateRequest(BaseModel):
256-
provider_id: UUID = Field(description="Deployment provider account id for adapter routing.")
300+
model_config = {"extra": "forbid"}
301+
302+
provider_id: UUID = Field(description="Deployment provider account id.")
257303
deployment_id: IdLike
258-
deployment_type: DeploymentType
259-
input: str | dict[str, Any] | None = None
260304
provider_input: dict[str, Any] | None = None
261305

262306

263307
class _ExecutionResponseBase(BaseModel):
264308
"""Shared fields for execution responses."""
265309

266-
execution_id: str | None = None
310+
execution_id: str
267311
deployment_id: IdLike
268-
deployment_type: DeploymentType | None = None
269-
status: str | None = None
270-
output: str | dict[str, Any] | None = None
271312
provider_result: dict[str, Any] | None = None
272313

273314

0 commit comments

Comments
 (0)