feat: Add flow build cancellation endpoint and functionality (#6813)
* feat: Add flow build cancellation endpoint and support
Implement new functionality to cancel ongoing flow build jobs:
- Add `cancel_flow_build` function in build.py to handle job cancellation
- Create new `/build/{job_id}/cancel` endpoint in chat.py
- Introduce `CancelFlowResponse` schema for cancellation response
- Implement robust error handling and logging for job cancellation process
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
5c81cf4b8c
commit
d02b4573e5
4 changed files with 84 additions and 2 deletions
|
|
@ -426,3 +426,47 @@ async def generate_flow_events(
|
|||
raise
|
||||
event_manager.on_end(data={})
|
||||
await event_manager.queue.put((None, None, time.time()))
|
||||
|
||||
|
||||
async def cancel_flow_build(
|
||||
*,
|
||||
job_id: str,
|
||||
queue_service: JobQueueService,
|
||||
) -> bool:
|
||||
"""Cancel an ongoing flow build job.
|
||||
|
||||
Args:
|
||||
job_id: The unique identifier of the job to cancel
|
||||
queue_service: The service managing job queues
|
||||
|
||||
Returns:
|
||||
True if the job was successfully canceled or doesn't need cancellation
|
||||
False if the cancellation failed
|
||||
|
||||
Raises:
|
||||
ValueError: If the job doesn't exist
|
||||
"""
|
||||
# Get the event task and event manager for the job
|
||||
_, _, event_task = queue_service.get_queue_data(job_id)
|
||||
|
||||
if event_task is None:
|
||||
logger.warning(f"No event task found for job_id {job_id}")
|
||||
return True # Nothing to cancel is still a success
|
||||
|
||||
if event_task.done():
|
||||
logger.info(f"Task for job_id {job_id} is already completed")
|
||||
return True # Nothing to cancel is still a success
|
||||
|
||||
# Store the task reference to check status after cleanup
|
||||
task_before_cleanup = event_task
|
||||
|
||||
# Perform cleanup using the queue service
|
||||
await queue_service.cleanup_job(job_id)
|
||||
|
||||
# Verify that the task was actually cancelled
|
||||
# The task should be done (cancelled) after cleanup
|
||||
if task_before_cleanup.cancelled():
|
||||
logger.info(f"Successfully cancelled flow build for job_id {job_id}")
|
||||
return True
|
||||
logger.error(f"Failed to cancel flow build for job_id {job_id}, task is still running")
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ import traceback
|
|||
import uuid
|
||||
from typing import TYPE_CHECKING, Annotated
|
||||
|
||||
from fastapi import APIRouter, BackgroundTasks, Body, Depends, HTTPException
|
||||
from fastapi import APIRouter, BackgroundTasks, Body, Depends, HTTPException, status
|
||||
from fastapi.responses import StreamingResponse
|
||||
from loguru import logger
|
||||
|
||||
from langflow.api.build import (
|
||||
cancel_flow_build,
|
||||
get_flow_events_response,
|
||||
start_flow_build,
|
||||
)
|
||||
|
|
@ -25,6 +26,7 @@ from langflow.api.utils import (
|
|||
parse_exception,
|
||||
)
|
||||
from langflow.api.v1.schemas import (
|
||||
CancelFlowResponse,
|
||||
FlowDataRequest,
|
||||
InputValueRequest,
|
||||
ResultDataResponse,
|
||||
|
|
@ -177,6 +179,32 @@ async def get_build_events(
|
|||
)
|
||||
|
||||
|
||||
@router.post("/build/{job_id}/cancel", response_model=CancelFlowResponse)
|
||||
async def cancel_build(
|
||||
job_id: str,
|
||||
queue_service: Annotated[JobQueueService, Depends(get_queue_service)],
|
||||
):
|
||||
"""Cancel a specific build job."""
|
||||
try:
|
||||
# Cancel the flow build and check if it was successful
|
||||
cancellation_success = await cancel_flow_build(job_id=job_id, queue_service=queue_service)
|
||||
|
||||
if cancellation_success:
|
||||
# Cancellation succeeded or wasn't needed
|
||||
return CancelFlowResponse(success=True, message="Flow build cancelled successfully")
|
||||
# Cancellation was attempted but failed
|
||||
return CancelFlowResponse(success=False, message="Failed to cancel flow build")
|
||||
|
||||
except ValueError as exc:
|
||||
# Job not found
|
||||
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
|
||||
except Exception as exc:
|
||||
# Any other unexpected error
|
||||
logger.exception(f"Error cancelling flow build for job_id {job_id}: {exc}")
|
||||
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)) from exc
|
||||
|
||||
|
||||
@router.post("/build/{flow_id}/vertices/{vertex_id}", deprecated=True)
|
||||
async def build_vertex(
|
||||
*,
|
||||
|
|
|
|||
|
|
@ -377,3 +377,10 @@ class ConfigResponse(BaseModel):
|
|||
health_check_max_retries: int
|
||||
max_file_size_upload: int
|
||||
event_delivery: Literal["polling", "streaming"]
|
||||
|
||||
|
||||
class CancelFlowResponse(BaseModel):
|
||||
"""Response model for flow build cancellation."""
|
||||
|
||||
success: bool
|
||||
message: str
|
||||
|
|
|
|||
5
uv.lock
generated
5
uv.lock
generated
|
|
@ -1,4 +1,5 @@
|
|||
version = 1
|
||||
revision = 1
|
||||
requires-python = ">=3.10, <3.14"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.13'",
|
||||
|
|
@ -4491,7 +4492,7 @@ requires-dist = [
|
|||
{ name = "pymongo", specifier = "==4.10.1" },
|
||||
{ name = "python-pptx", marker = "extra == 'nv-ingest'", specifier = "==0.6.23" },
|
||||
{ name = "pytube", specifier = "==15.0.0" },
|
||||
{ name = "pywin32", marker = "sys_platform == 'win32'", specifier = ">=307,<308" },
|
||||
{ name = "pywin32", marker = "sys_platform == 'win32'", specifier = "==307" },
|
||||
{ name = "qdrant-client", specifier = "==1.9.2" },
|
||||
{ name = "qianfan", specifier = "==0.3.5" },
|
||||
{ name = "ragstack-ai-knowledge-store", specifier = "==0.2.1" },
|
||||
|
|
@ -4513,6 +4514,7 @@ requires-dist = [
|
|||
{ name = "youtube-transcript-api", specifier = "==0.6.3" },
|
||||
{ name = "zep-python", specifier = "==2.0.2" },
|
||||
]
|
||||
provides-extras = ["deploy", "couchbase", "cassio", "local", "clickhouse-connect", "nv-ingest"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
|
|
@ -4763,6 +4765,7 @@ requires-dist = [
|
|||
{ name = "uvicorn", specifier = ">=0.30.0,<1.0.0" },
|
||||
{ name = "validators", specifier = ">=0.34.0" },
|
||||
]
|
||||
provides-extras = ["deploy", "local", "all"]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue