|
1 | 1 | import json
|
2 | 2 | import logging
|
| 3 | +from typing import cast |
3 | 4 |
|
4 | 5 | from flask import abort, request
|
5 | 6 | from flask_restful import Resource, inputs, marshal_with, reqparse # type: ignore
|
| 7 | +from sqlalchemy.orm import Session |
6 | 8 | from werkzeug.exceptions import Forbidden, InternalServerError, NotFound
|
7 | 9 |
|
8 | 10 | import services
|
|
13 | 15 | from controllers.console.wraps import account_initialization_required, setup_required
|
14 | 16 | from core.app.apps.base_app_queue_manager import AppQueueManager
|
15 | 17 | from core.app.entities.app_invoke_entities import InvokeFrom
|
| 18 | +from extensions.ext_database import db |
16 | 19 | from factories import variable_factory
|
17 | 20 | from fields.workflow_fields import workflow_fields, workflow_pagination_fields
|
18 | 21 | from fields.workflow_run_fields import workflow_run_node_execution_fields
|
|
24 | 27 | from models.model import AppMode
|
25 | 28 | from services.app_generate_service import AppGenerateService
|
26 | 29 | from services.errors.app import WorkflowHashNotEqualError
|
27 |
| -from services.workflow_service import WorkflowService |
| 30 | +from services.workflow_service import DraftWorkflowDeletionError, WorkflowInUseError, WorkflowService |
28 | 31 |
|
29 | 32 | logger = logging.getLogger(__name__)
|
30 | 33 |
|
@@ -439,10 +442,38 @@ def post(self, app_model: App):
|
439 | 442 | if not isinstance(current_user, Account):
|
440 | 443 | raise Forbidden()
|
441 | 444 |
|
| 445 | + parser = reqparse.RequestParser() |
| 446 | + parser.add_argument("marked_name", type=str, required=False, default="", location="json") |
| 447 | + parser.add_argument("marked_comment", type=str, required=False, default="", location="json") |
| 448 | + args = parser.parse_args() |
| 449 | + |
| 450 | + # Validate name and comment length |
| 451 | + if args.marked_name and len(args.marked_name) > 20: |
| 452 | + raise ValueError("Marked name cannot exceed 20 characters") |
| 453 | + if args.marked_comment and len(args.marked_comment) > 100: |
| 454 | + raise ValueError("Marked comment cannot exceed 100 characters") |
| 455 | + |
442 | 456 | workflow_service = WorkflowService()
|
443 |
| - workflow = workflow_service.publish_workflow(app_model=app_model, account=current_user) |
| 457 | + with Session(db.engine) as session: |
| 458 | + workflow = workflow_service.publish_workflow( |
| 459 | + session=session, |
| 460 | + app_model=app_model, |
| 461 | + account=current_user, |
| 462 | + marked_name=args.marked_name or "", |
| 463 | + marked_comment=args.marked_comment or "", |
| 464 | + ) |
| 465 | + |
| 466 | + app_model.workflow_id = workflow.id |
| 467 | + db.session.commit() |
| 468 | + |
| 469 | + workflow_created_at = TimestampField().format(workflow.created_at) |
444 | 470 |
|
445 |
| - return {"result": "success", "created_at": TimestampField().format(workflow.created_at)} |
| 471 | + session.commit() |
| 472 | + |
| 473 | + return { |
| 474 | + "result": "success", |
| 475 | + "created_at": workflow_created_at, |
| 476 | + } |
446 | 477 |
|
447 | 478 |
|
448 | 479 | class DefaultBlockConfigsApi(Resource):
|
@@ -564,37 +595,193 @@ def get(self, app_model: App):
|
564 | 595 | parser = reqparse.RequestParser()
|
565 | 596 | parser.add_argument("page", type=inputs.int_range(1, 99999), required=False, default=1, location="args")
|
566 | 597 | parser.add_argument("limit", type=inputs.int_range(1, 100), required=False, default=20, location="args")
|
| 598 | + parser.add_argument("user_id", type=str, required=False, location="args") |
| 599 | + parser.add_argument("named_only", type=inputs.boolean, required=False, default=False, location="args") |
| 600 | + args = parser.parse_args() |
| 601 | + page = int(args.get("page", 1)) |
| 602 | + limit = int(args.get("limit", 10)) |
| 603 | + user_id = args.get("user_id") |
| 604 | + named_only = args.get("named_only", False) |
| 605 | + |
| 606 | + if user_id: |
| 607 | + if user_id != current_user.id: |
| 608 | + raise Forbidden() |
| 609 | + user_id = cast(str, user_id) |
| 610 | + |
| 611 | + workflow_service = WorkflowService() |
| 612 | + with Session(db.engine) as session: |
| 613 | + workflows, has_more = workflow_service.get_all_published_workflow( |
| 614 | + session=session, |
| 615 | + app_model=app_model, |
| 616 | + page=page, |
| 617 | + limit=limit, |
| 618 | + user_id=user_id, |
| 619 | + named_only=named_only, |
| 620 | + ) |
| 621 | + |
| 622 | + return { |
| 623 | + "items": workflows, |
| 624 | + "page": page, |
| 625 | + "limit": limit, |
| 626 | + "has_more": has_more, |
| 627 | + } |
| 628 | + |
| 629 | + |
| 630 | +class WorkflowByIdApi(Resource): |
| 631 | + @setup_required |
| 632 | + @login_required |
| 633 | + @account_initialization_required |
| 634 | + @get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]) |
| 635 | + @marshal_with(workflow_fields) |
| 636 | + def patch(self, app_model: App, workflow_id: str): |
| 637 | + """ |
| 638 | + Update workflow attributes |
| 639 | + """ |
| 640 | + # Check permission |
| 641 | + if not current_user.is_editor: |
| 642 | + raise Forbidden() |
| 643 | + |
| 644 | + if not isinstance(current_user, Account): |
| 645 | + raise Forbidden() |
| 646 | + |
| 647 | + parser = reqparse.RequestParser() |
| 648 | + parser.add_argument("marked_name", type=str, required=False, location="json") |
| 649 | + parser.add_argument("marked_comment", type=str, required=False, location="json") |
567 | 650 | args = parser.parse_args()
|
568 |
| - page = args.get("page") |
569 |
| - limit = args.get("limit") |
| 651 | + |
| 652 | + # Validate name and comment length |
| 653 | + if args.marked_name and len(args.marked_name) > 20: |
| 654 | + raise ValueError("Marked name cannot exceed 20 characters") |
| 655 | + if args.marked_comment and len(args.marked_comment) > 100: |
| 656 | + raise ValueError("Marked comment cannot exceed 100 characters") |
| 657 | + args = parser.parse_args() |
| 658 | + |
| 659 | + # Prepare update data |
| 660 | + update_data = {} |
| 661 | + if args.get("marked_name") is not None: |
| 662 | + update_data["marked_name"] = args["marked_name"] |
| 663 | + if args.get("marked_comment") is not None: |
| 664 | + update_data["marked_comment"] = args["marked_comment"] |
| 665 | + |
| 666 | + if not update_data: |
| 667 | + return {"message": "No valid fields to update"}, 400 |
| 668 | + |
570 | 669 | workflow_service = WorkflowService()
|
571 |
| - workflows, has_more = workflow_service.get_all_published_workflow(app_model=app_model, page=page, limit=limit) |
572 | 670 |
|
573 |
| - return {"items": workflows, "page": page, "limit": limit, "has_more": has_more} |
| 671 | + # Create a session and manage the transaction |
| 672 | + with Session(db.engine, expire_on_commit=False) as session: |
| 673 | + workflow = workflow_service.update_workflow( |
| 674 | + session=session, |
| 675 | + workflow_id=workflow_id, |
| 676 | + tenant_id=app_model.tenant_id, |
| 677 | + account_id=current_user.id, |
| 678 | + data=update_data, |
| 679 | + ) |
| 680 | + |
| 681 | + if not workflow: |
| 682 | + raise NotFound("Workflow not found") |
| 683 | + |
| 684 | + # Commit the transaction in the controller |
| 685 | + session.commit() |
| 686 | + |
| 687 | + return workflow |
| 688 | + |
| 689 | + @setup_required |
| 690 | + @login_required |
| 691 | + @account_initialization_required |
| 692 | + @get_app_model(mode=[AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]) |
| 693 | + def delete(self, app_model: App, workflow_id: str): |
| 694 | + """ |
| 695 | + Delete workflow |
| 696 | + """ |
| 697 | + # Check permission |
| 698 | + if not current_user.is_editor: |
| 699 | + raise Forbidden() |
| 700 | + |
| 701 | + if not isinstance(current_user, Account): |
| 702 | + raise Forbidden() |
| 703 | + |
| 704 | + workflow_service = WorkflowService() |
| 705 | + |
| 706 | + # Create a session and manage the transaction |
| 707 | + with Session(db.engine) as session: |
| 708 | + try: |
| 709 | + workflow_service.delete_workflow( |
| 710 | + session=session, workflow_id=workflow_id, tenant_id=app_model.tenant_id |
| 711 | + ) |
| 712 | + # Commit the transaction in the controller |
| 713 | + session.commit() |
| 714 | + except WorkflowInUseError as e: |
| 715 | + abort(400, description=str(e)) |
| 716 | + except DraftWorkflowDeletionError as e: |
| 717 | + abort(400, description=str(e)) |
| 718 | + except ValueError as e: |
| 719 | + raise NotFound(str(e)) |
| 720 | + |
| 721 | + return None, 204 |
574 | 722 |
|
575 | 723 |
|
576 |
| -api.add_resource(DraftWorkflowApi, "/apps/<uuid:app_id>/workflows/draft") |
577 |
| -api.add_resource(WorkflowConfigApi, "/apps/<uuid:app_id>/workflows/draft/config") |
578 |
| -api.add_resource(AdvancedChatDraftWorkflowRunApi, "/apps/<uuid:app_id>/advanced-chat/workflows/draft/run") |
579 |
| -api.add_resource(DraftWorkflowRunApi, "/apps/<uuid:app_id>/workflows/draft/run") |
580 |
| -api.add_resource(WorkflowTaskStopApi, "/apps/<uuid:app_id>/workflow-runs/tasks/<string:task_id>/stop") |
581 |
| -api.add_resource(DraftWorkflowNodeRunApi, "/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/run") |
| 724 | +api.add_resource( |
| 725 | + DraftWorkflowApi, |
| 726 | + "/apps/<uuid:app_id>/workflows/draft", |
| 727 | +) |
| 728 | +api.add_resource( |
| 729 | + WorkflowConfigApi, |
| 730 | + "/apps/<uuid:app_id>/workflows/draft/config", |
| 731 | +) |
| 732 | +api.add_resource( |
| 733 | + AdvancedChatDraftWorkflowRunApi, |
| 734 | + "/apps/<uuid:app_id>/advanced-chat/workflows/draft/run", |
| 735 | +) |
| 736 | +api.add_resource( |
| 737 | + DraftWorkflowRunApi, |
| 738 | + "/apps/<uuid:app_id>/workflows/draft/run", |
| 739 | +) |
| 740 | +api.add_resource( |
| 741 | + WorkflowTaskStopApi, |
| 742 | + "/apps/<uuid:app_id>/workflow-runs/tasks/<string:task_id>/stop", |
| 743 | +) |
| 744 | +api.add_resource( |
| 745 | + DraftWorkflowNodeRunApi, |
| 746 | + "/apps/<uuid:app_id>/workflows/draft/nodes/<string:node_id>/run", |
| 747 | +) |
582 | 748 | api.add_resource(
|
583 | 749 | AdvancedChatDraftRunIterationNodeApi,
|
584 | 750 | "/apps/<uuid:app_id>/advanced-chat/workflows/draft/iteration/nodes/<string:node_id>/run",
|
585 | 751 | )
|
586 | 752 | api.add_resource(
|
587 |
| - WorkflowDraftRunIterationNodeApi, "/apps/<uuid:app_id>/workflows/draft/iteration/nodes/<string:node_id>/run" |
| 753 | + WorkflowDraftRunIterationNodeApi, |
| 754 | + "/apps/<uuid:app_id>/workflows/draft/iteration/nodes/<string:node_id>/run", |
588 | 755 | )
|
589 | 756 | api.add_resource(
|
590 | 757 | AdvancedChatDraftRunLoopNodeApi,
|
591 | 758 | "/apps/<uuid:app_id>/advanced-chat/workflows/draft/loop/nodes/<string:node_id>/run",
|
592 | 759 | )
|
593 |
| -api.add_resource(WorkflowDraftRunLoopNodeApi, "/apps/<uuid:app_id>/workflows/draft/loop/nodes/<string:node_id>/run") |
594 |
| -api.add_resource(PublishedWorkflowApi, "/apps/<uuid:app_id>/workflows/publish") |
595 |
| -api.add_resource(PublishedAllWorkflowApi, "/apps/<uuid:app_id>/workflows") |
596 |
| -api.add_resource(DefaultBlockConfigsApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs") |
597 | 760 | api.add_resource(
|
598 |
| - DefaultBlockConfigApi, "/apps/<uuid:app_id>/workflows/default-workflow-block-configs/<string:block_type>" |
| 761 | + WorkflowDraftRunLoopNodeApi, |
| 762 | + "/apps/<uuid:app_id>/workflows/draft/loop/nodes/<string:node_id>/run", |
| 763 | +) |
| 764 | +api.add_resource( |
| 765 | + PublishedWorkflowApi, |
| 766 | + "/apps/<uuid:app_id>/workflows/publish", |
| 767 | +) |
| 768 | +api.add_resource( |
| 769 | + PublishedAllWorkflowApi, |
| 770 | + "/apps/<uuid:app_id>/workflows", |
| 771 | +) |
| 772 | +api.add_resource( |
| 773 | + DefaultBlockConfigsApi, |
| 774 | + "/apps/<uuid:app_id>/workflows/default-workflow-block-configs", |
| 775 | +) |
| 776 | +api.add_resource( |
| 777 | + DefaultBlockConfigApi, |
| 778 | + "/apps/<uuid:app_id>/workflows/default-workflow-block-configs/<string:block_type>", |
| 779 | +) |
| 780 | +api.add_resource( |
| 781 | + ConvertToWorkflowApi, |
| 782 | + "/apps/<uuid:app_id>/convert-to-workflow", |
| 783 | +) |
| 784 | +api.add_resource( |
| 785 | + WorkflowByIdApi, |
| 786 | + "/apps/<uuid:app_id>/workflows/<string:workflow_id>", |
599 | 787 | )
|
600 |
| -api.add_resource(ConvertToWorkflowApi, "/apps/<uuid:app_id>/convert-to-workflow") |
|
0 commit comments