|
@@ -1,8 +1,10 @@
|
|
|
import json
|
|
|
import logging
|
|
|
+from typing import cast
|
|
|
|
|
|
from flask import abort, request
|
|
|
from flask_restful import Resource, inputs, marshal_with, reqparse # type: ignore
|
|
|
+from sqlalchemy.orm import Session
|
|
|
from werkzeug.exceptions import Forbidden, InternalServerError, NotFound
|
|
|
|
|
|
import services
|
|
@@ -13,6 +15,7 @@ from controllers.console.app.wraps import get_app_model
|
|
|
from controllers.console.wraps import account_initialization_required, setup_required
|
|
|
from core.app.apps.base_app_queue_manager import AppQueueManager
|
|
|
from core.app.entities.app_invoke_entities import InvokeFrom
|
|
|
+from extensions.ext_database import db
|
|
|
from factories import variable_factory
|
|
|
from fields.workflow_fields import workflow_fields, workflow_pagination_fields
|
|
|
from fields.workflow_run_fields import workflow_run_node_execution_fields
|
|
@@ -24,7 +27,7 @@ from models.account import Account
|
|
|
from models.model import AppMode
|
|
|
from services.app_generate_service import AppGenerateService
|
|
|
from services.errors.app import WorkflowHashNotEqualError
|
|
|
-from services.workflow_service import WorkflowService
|
|
|
+from services.workflow_service import DraftWorkflowDeletionError, WorkflowInUseError, WorkflowService
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
@@ -439,10 +442,38 @@ class PublishedWorkflowApi(Resource):
|
|
|
if not isinstance(current_user, Account):
|
|
|
raise Forbidden()
|
|
|
|
|
|
+ parser = reqparse.RequestParser()
|
|
|
+ parser.add_argument("marked_name", type=str, required=False, default="", location="json")
|
|
|
+ parser.add_argument("marked_comment", type=str, required=False, default="", location="json")
|
|
|
+ args = parser.parse_args()
|
|
|
+
|
|
|
+ # Validate name and comment length
|
|
|
+ if args.marked_name and len(args.marked_name) > 20:
|
|
|
+ raise ValueError("Marked name cannot exceed 20 characters")
|
|
|
+ if args.marked_comment and len(args.marked_comment) > 100:
|
|
|
+ raise ValueError("Marked comment cannot exceed 100 characters")
|
|
|
+
|
|
|
workflow_service = WorkflowService()
|
|
|
- workflow = workflow_service.publish_workflow(app_model=app_model, account=current_user)
|
|
|
+ with Session(db.engine) as session:
|
|
|
+ workflow = workflow_service.publish_workflow(
|
|
|
+ session=session,
|
|
|
+ app_model=app_model,
|
|
|
+ account=current_user,
|
|
|
+ marked_name=args.marked_name or "",
|
|
|
+ marked_comment=args.marked_comment or "",
|
|
|
+ )
|
|
|
+
|
|
|
+ app_model.workflow_id = workflow.id
|
|
|
+ db.session.commit()
|
|
|
+
|
|
|
+ workflow_created_at = TimestampField().format(workflow.created_at)
|
|
|
|
|
|
- return {"result": "success", "created_at": TimestampField().format(workflow.created_at)}
|
|
|
+ session.commit()
|
|
|
+
|
|
|
+ return {
|
|
|
+ "result": "success",
|
|
|
+ "created_at": workflow_created_at,
|
|
|
+ }
|
|
|
|
|
|
|
|
|
class DefaultBlockConfigsApi(Resource):
|
|
@@ -564,37 +595,193 @@ class PublishedAllWorkflowApi(Resource):
|
|
|
parser = reqparse.RequestParser()
|
|
|
parser.add_argument("page", type=inputs.int_range(1, 99999), required=False, default=1, location="args")
|
|
|
parser.add_argument("limit", type=inputs.int_range(1, 100), required=False, default=20, location="args")
|
|
|
+ parser.add_argument("user_id", type=str, required=False, location="args")
|
|
|
+ parser.add_argument("named_only", type=inputs.boolean, required=False, default=False, location="args")
|
|
|
+ args = parser.parse_args()
|
|
|
+ page = int(args.get("page", 1))
|
|
|
+ limit = int(args.get("limit", 10))
|
|
|
+ user_id = args.get("user_id")
|
|
|
+ named_only = args.get("named_only", False)
|
|
|
+
|
|
|
+ if user_id:
|
|
|
+ if user_id != current_user.id:
|
|
|
+ raise Forbidden()
|
|
|
+ user_id = cast(str, user_id)
|
|
|
+
|
|
|
+ workflow_service = WorkflowService()
|
|
|
+ with Session(db.engine) as session:
|
|
|
+ workflows, has_more = workflow_service.get_all_published_workflow(
|
|
|
+ session=session,
|
|
|
+ app_model=app_model,
|
|
|
+ page=page,
|
|
|
+ limit=limit,
|
|
|
+ user_id=user_id,
|
|
|
+ named_only=named_only,
|
|
|
+ )
|
|
|
+
|
|
|
+ return {
|
|
|
+ "items": workflows,
|
|
|
+ "page": page,
|
|
|
+ "limit": limit,
|
|
|
+ "has_more": has_more,
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+class WorkflowByIdApi(Resource):
|
|
|
+ @setup_required
|
|
|
+ @login_required
|
|
|
+ @account_initialization_required
|
|
|
+ @get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
|
|
+ @marshal_with(workflow_fields)
|
|
|
+ def patch(self, app_model: App, workflow_id: str):
|
|
|
+ """
|
|
|
+ Update workflow attributes
|
|
|
+ """
|
|
|
+ # Check permission
|
|
|
+ if not current_user.is_editor:
|
|
|
+ raise Forbidden()
|
|
|
+
|
|
|
+ if not isinstance(current_user, Account):
|
|
|
+ raise Forbidden()
|
|
|
+
|
|
|
+ parser = reqparse.RequestParser()
|
|
|
+ parser.add_argument("marked_name", type=str, required=False, location="json")
|
|
|
+ parser.add_argument("marked_comment", type=str, required=False, location="json")
|
|
|
args = parser.parse_args()
|
|
|
- page = args.get("page")
|
|
|
- limit = args.get("limit")
|
|
|
+
|
|
|
+ # Validate name and comment length
|
|
|
+ if args.marked_name and len(args.marked_name) > 20:
|
|
|
+ raise ValueError("Marked name cannot exceed 20 characters")
|
|
|
+ if args.marked_comment and len(args.marked_comment) > 100:
|
|
|
+ raise ValueError("Marked comment cannot exceed 100 characters")
|
|
|
+ args = parser.parse_args()
|
|
|
+
|
|
|
+ # Prepare update data
|
|
|
+ update_data = {}
|
|
|
+ if args.get("marked_name") is not None:
|
|
|
+ update_data["marked_name"] = args["marked_name"]
|
|
|
+ if args.get("marked_comment") is not None:
|
|
|
+ update_data["marked_comment"] = args["marked_comment"]
|
|
|
+
|
|
|
+ if not update_data:
|
|
|
+ return {"message": "No valid fields to update"}, 400
|
|
|
+
|
|
|
workflow_service = WorkflowService()
|
|
|
- workflows, has_more = workflow_service.get_all_published_workflow(app_model=app_model, page=page, limit=limit)
|
|
|
|
|
|
- return {"items": workflows, "page": page, "limit": limit, "has_more": has_more}
|
|
|
+ # Create a session and manage the transaction
|
|
|
+ with Session(db.engine, expire_on_commit=False) as session:
|
|
|
+ workflow = workflow_service.update_workflow(
|
|
|
+ session=session,
|
|
|
+ workflow_id=workflow_id,
|
|
|
+ tenant_id=app_model.tenant_id,
|
|
|
+ account_id=current_user.id,
|
|
|
+ data=update_data,
|
|
|
+ )
|
|
|
+
|
|
|
+ if not workflow:
|
|
|
+ raise NotFound("Workflow not found")
|
|
|
+
|
|
|
+ # Commit the transaction in the controller
|
|
|
+ session.commit()
|
|
|
+
|
|
|
+ return workflow
|
|
|
+
|
|
|
+ @setup_required
|
|
|
+ @login_required
|
|
|
+ @account_initialization_required
|
|
|
+ @get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW])
|
|
|
+ def delete(self, app_model: App, workflow_id: str):
|
|
|
+ """
|
|
|
+ Delete workflow
|
|
|
+ """
|
|
|
+ # Check permission
|
|
|
+ if not current_user.is_editor:
|
|
|
+ raise Forbidden()
|
|
|
+
|
|
|
+ if not isinstance(current_user, Account):
|
|
|
+ raise Forbidden()
|
|
|
+
|
|
|
+ workflow_service = WorkflowService()
|
|
|
+
|
|
|
+ # Create a session and manage the transaction
|
|
|
+ with Session(db.engine) as session:
|
|
|
+ try:
|
|
|
+ workflow_service.delete_workflow(
|
|
|
+ session=session, workflow_id=workflow_id, tenant_id=app_model.tenant_id
|
|
|
+ )
|
|
|
+ # Commit the transaction in the controller
|
|
|
+ session.commit()
|
|
|
+ except WorkflowInUseError as e:
|
|
|
+ abort(400, description=str(e))
|
|
|
+ except DraftWorkflowDeletionError as e:
|
|
|
+ abort(400, description=str(e))
|
|
|
+ except ValueError as e:
|
|
|
+ raise NotFound(str(e))
|
|
|
+
|
|
|
+ return None, 204
|
|
|
|
|
|
|
|
|
-api.add_resource(DraftWorkflowApi, "/apps/<uuid:app_id>/workflows/draft")
|
|
|
-api.add_resource(WorkflowConfigApi, "/apps/<uuid:app_id>/workflows/draft/config")
|
|
|
-api.add_resource(AdvancedChatDraftWorkflowRunApi, "/apps/<uuid:app_id>/advanced-chat/workflows/draft/run")
|
|
|
-api.add_resource(DraftWorkflowRunApi, "/apps/<uuid:app_id>/workflows/draft/run")
|
|
|
-api.add_resource(WorkflowTaskStopApi, "/apps/<uuid:app_id>/workflow-runs/tasks/<string:task_id>/stop")
|
|
|
-api.add_resource(DraftWorkflowNodeRunApi, "/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/run")
|
|
|
+api.add_resource(
|
|
|
+ DraftWorkflowApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows/draft",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ WorkflowConfigApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows/draft/config",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ AdvancedChatDraftWorkflowRunApi,
|
|
|
+ "/apps/<uuid:app_id>/advanced-chat/workflows/draft/run",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ DraftWorkflowRunApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows/draft/run",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ WorkflowTaskStopApi,
|
|
|
+ "/apps/<uuid:app_id>/workflow-runs/tasks/<string:task_id>/stop",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ DraftWorkflowNodeRunApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/run",
|
|
|
+)
|
|
|
api.add_resource(
|
|
|
AdvancedChatDraftRunIterationNodeApi,
|
|
|
"/apps/<uuid:app_id>/advanced-chat/workflows/draft/iteration/nodes/<string:node_id>/run",
|
|
|
)
|
|
|
api.add_resource(
|
|
|
- WorkflowDraftRunIterationNodeApi, "/apps/<uuid:app_id>/workflows/draft/iteration/nodes/<string:node_id>/run"
|
|
|
+ WorkflowDraftRunIterationNodeApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows/draft/iteration/nodes/<string:node_id>/run",
|
|
|
)
|
|
|
api.add_resource(
|
|
|
AdvancedChatDraftRunLoopNodeApi,
|
|
|
"/apps/<uuid:app_id>/advanced-chat/workflows/draft/loop/nodes/<string:node_id>/run",
|
|
|
)
|
|
|
-api.add_resource(WorkflowDraftRunLoopNodeApi, "/apps/<uuid:app_id>/workflows/draft/loop/nodes/<string:node_id>/run")
|
|
|
-api.add_resource(PublishedWorkflowApi, "/apps/<uuid:app_id>/workflows/publish")
|
|
|
-api.add_resource(PublishedAllWorkflowApi, "/apps/<uuid:app_id>/workflows")
|
|
|
-api.add_resource(DefaultBlockConfigsApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs")
|
|
|
api.add_resource(
|
|
|
- DefaultBlockConfigApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs/<string:block_type>"
|
|
|
+ WorkflowDraftRunLoopNodeApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows/draft/loop/nodes/<string:node_id>/run",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ PublishedWorkflowApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows/publish",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ PublishedAllWorkflowApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ DefaultBlockConfigsApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows/default-workflow-block-configs",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ DefaultBlockConfigApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows/default-workflow-block-configs/<string:block_type>",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ ConvertToWorkflowApi,
|
|
|
+ "/apps/<uuid:app_id>/convert-to-workflow",
|
|
|
+)
|
|
|
+api.add_resource(
|
|
|
+ WorkflowByIdApi,
|
|
|
+ "/apps/<uuid:app_id>/workflows/<string:workflow_id>",
|
|
|
)
|
|
|
-api.add_resource(ConvertToWorkflowApi, "/apps/<uuid:app_id>/convert-to-workflow")
|