mirror of
https://github.com/OpenBMB/ChatDev.git
synced 2026-04-26 03:38:12 +00:00
212 lines
7.1 KiB
Python
Executable File
212 lines
7.1 KiB
Python
Executable File
from fastapi import APIRouter, HTTPException
|
|
|
|
from server.models import (
|
|
WorkflowCopyRequest,
|
|
WorkflowRenameRequest,
|
|
WorkflowUpdateContentRequest,
|
|
WorkflowUploadContentRequest,
|
|
)
|
|
from server.services.workflow_storage import (
|
|
copy_workflow,
|
|
persist_workflow,
|
|
rename_workflow,
|
|
validate_workflow_content,
|
|
validate_workflow_filename,
|
|
)
|
|
from server.settings import YAML_DIR
|
|
from utils.exceptions import (
|
|
ResourceConflictError,
|
|
ResourceNotFoundError,
|
|
SecurityError,
|
|
ValidationError,
|
|
WorkflowExecutionError,
|
|
)
|
|
from utils.structured_logger import get_server_logger, LogType
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def _persist_workflow_from_content(
|
|
filename: str,
|
|
content: str,
|
|
*,
|
|
allow_overwrite: bool,
|
|
action: str,
|
|
success_message: str,
|
|
):
|
|
try:
|
|
safe_filename, yaml_content = validate_workflow_content(filename.strip(), content)
|
|
save_path = YAML_DIR / safe_filename
|
|
|
|
if save_path.exists() and not allow_overwrite:
|
|
raise HTTPException(status_code=409, detail="Workflow already exists; use the update API to overwrite")
|
|
|
|
if not save_path.exists() and allow_overwrite:
|
|
raise HTTPException(status_code=404, detail="Workflow file not found")
|
|
|
|
persist_workflow(safe_filename, content, yaml_content, action=action, directory=YAML_DIR)
|
|
return {
|
|
"status": "success",
|
|
"filename": safe_filename,
|
|
"message": success_message.format(filename=safe_filename),
|
|
}
|
|
except ValidationError:
|
|
raise
|
|
except HTTPException:
|
|
raise
|
|
except Exception as exc:
|
|
logger = get_server_logger()
|
|
logger.log_exception(exc, f"Unexpected error during workflow {action}")
|
|
raise WorkflowExecutionError(f"Failed to {action} workflow: {exc}")
|
|
|
|
|
|
@router.get("/api/workflows")
|
|
async def list_workflows():
|
|
if not YAML_DIR.exists():
|
|
return {"workflows": []}
|
|
return {"workflows": [file.name for file in YAML_DIR.glob("*.yaml")]}
|
|
|
|
|
|
@router.post("/api/workflows/upload/content")
|
|
async def upload_workflow_content(request: WorkflowUploadContentRequest):
|
|
return _persist_workflow_from_content(
|
|
request.filename,
|
|
request.content,
|
|
allow_overwrite=False,
|
|
action="upload",
|
|
success_message="Workflow {filename} created successfully from content",
|
|
)
|
|
|
|
|
|
@router.put("/api/workflows/{filename}")
|
|
async def update_workflow_content(filename: str, request: WorkflowUpdateContentRequest):
|
|
return _persist_workflow_from_content(
|
|
filename,
|
|
request.content,
|
|
allow_overwrite=True,
|
|
action="update",
|
|
success_message="Workflow {filename} updated successfully",
|
|
)
|
|
|
|
|
|
@router.delete("/api/workflows/{filename}")
|
|
async def delete_workflow(filename: str):
|
|
try:
|
|
safe_filename = validate_workflow_filename(filename, require_yaml_extension=True)
|
|
file_path = YAML_DIR / safe_filename
|
|
if not file_path.exists() or not file_path.is_file():
|
|
raise ResourceNotFoundError(
|
|
"Workflow file not found",
|
|
resource_type="workflow",
|
|
resource_id=safe_filename,
|
|
)
|
|
|
|
try:
|
|
file_path.unlink()
|
|
except Exception as exc:
|
|
logger = get_server_logger()
|
|
logger.log_exception(exc, f"Failed to delete workflow file: {safe_filename}")
|
|
raise WorkflowExecutionError("Failed to delete workflow file", details={"filename": safe_filename})
|
|
|
|
logger = get_server_logger()
|
|
logger.info(
|
|
"Workflow file deleted",
|
|
log_type=LogType.WORKFLOW,
|
|
filename=safe_filename,
|
|
)
|
|
|
|
return {
|
|
"status": "deleted",
|
|
"filename": safe_filename,
|
|
"message": f"Workflow '{safe_filename}' deleted successfully",
|
|
}
|
|
except ValidationError:
|
|
raise
|
|
except SecurityError:
|
|
raise
|
|
except ResourceNotFoundError:
|
|
raise
|
|
except Exception as exc:
|
|
logger = get_server_logger()
|
|
logger.log_exception(exc, f"Unexpected error deleting workflow: {filename}")
|
|
raise WorkflowExecutionError(f"Failed to delete workflow: {exc}")
|
|
|
|
|
|
@router.post("/api/workflows/{filename}/rename")
|
|
async def rename_workflow_file(filename: str, request: WorkflowRenameRequest):
|
|
try:
|
|
rename_workflow(filename, request.new_filename, directory=YAML_DIR)
|
|
return {
|
|
"status": "success",
|
|
"source": validate_workflow_filename(filename, require_yaml_extension=True),
|
|
"target": validate_workflow_filename(request.new_filename, require_yaml_extension=True),
|
|
"message": f"Workflow renamed to '{request.new_filename}' successfully",
|
|
}
|
|
except ValidationError:
|
|
raise
|
|
except SecurityError:
|
|
raise
|
|
except ResourceConflictError:
|
|
raise
|
|
except ResourceNotFoundError:
|
|
raise
|
|
except Exception as exc:
|
|
logger = get_server_logger()
|
|
logger.log_exception(exc, f"Unexpected error renaming workflow: {filename}")
|
|
raise WorkflowExecutionError(f"Failed to rename workflow: {exc}")
|
|
|
|
|
|
@router.post("/api/workflows/{filename}/copy")
|
|
async def copy_workflow_file(filename: str, request: WorkflowCopyRequest):
|
|
try:
|
|
copy_workflow(filename, request.new_filename, directory=YAML_DIR)
|
|
return {
|
|
"status": "success",
|
|
"source": validate_workflow_filename(filename, require_yaml_extension=True),
|
|
"target": validate_workflow_filename(request.new_filename, require_yaml_extension=True),
|
|
"message": f"Workflow copied to '{request.new_filename}' successfully",
|
|
}
|
|
except ValidationError:
|
|
raise
|
|
except SecurityError:
|
|
raise
|
|
except ResourceConflictError:
|
|
raise
|
|
except ResourceNotFoundError:
|
|
raise
|
|
except Exception as exc:
|
|
logger = get_server_logger()
|
|
logger.log_exception(exc, f"Unexpected error copying workflow: {filename}")
|
|
raise WorkflowExecutionError(f"Failed to copy workflow: {exc}")
|
|
|
|
|
|
@router.get("/api/workflows/{filename}")
|
|
async def get_workflow_raw_content(filename: str):
|
|
try:
|
|
safe_filename = validate_workflow_filename(filename, require_yaml_extension=True)
|
|
|
|
file_path = YAML_DIR / safe_filename
|
|
if not file_path.exists() or not file_path.is_file():
|
|
raise ResourceNotFoundError(
|
|
"Workflow file not found",
|
|
resource_type="workflow",
|
|
resource_id=safe_filename,
|
|
)
|
|
|
|
with open(file_path, "r", encoding="utf-8") as handle:
|
|
raw_content = handle.read()
|
|
|
|
logger = get_server_logger()
|
|
logger.info("Workflow file content retrieved", log_type=LogType.WORKFLOW, filename=safe_filename)
|
|
return {"content": raw_content}
|
|
except ValidationError:
|
|
raise
|
|
except SecurityError:
|
|
raise
|
|
except ResourceNotFoundError:
|
|
raise
|
|
except Exception as exc:
|
|
logger = get_server_logger()
|
|
logger.log_exception(exc, f"Unexpected error retrieving workflow: {filename}")
|
|
raise WorkflowExecutionError(f"Failed to retrieve workflow: {exc}")
|