Browse Source

feat(backend): support import DSL from URL (#6287)

takatost 9 months ago
parent
commit
46a5294d94

+ 46 - 8
api/controllers/console/app/app.py

@@ -15,6 +15,7 @@ from fields.app_fields import (
     app_pagination_fields,
     app_pagination_fields,
 )
 )
 from libs.login import login_required
 from libs.login import login_required
+from services.app_dsl_service import AppDslService
 from services.app_service import AppService
 from services.app_service import AppService
 
 
 ALLOW_CREATE_APP_MODES = ['chat', 'agent-chat', 'advanced-chat', 'workflow', 'completion']
 ALLOW_CREATE_APP_MODES = ['chat', 'agent-chat', 'advanced-chat', 'workflow', 'completion']
@@ -97,8 +98,42 @@ class AppImportApi(Resource):
         parser.add_argument('icon_background', type=str, location='json')
         parser.add_argument('icon_background', type=str, location='json')
         args = parser.parse_args()
         args = parser.parse_args()
 
 
-        app_service = AppService()
+        app = AppDslService.import_and_create_new_app(
-        app = app_service.import_app(current_user.current_tenant_id, args['data'], args, current_user)
+            tenant_id=current_user.current_tenant_id,
+            data=args['data'],
+            args=args,
+            account=current_user
+        )
+
+        return app, 201
+
+
+class AppImportFromUrlApi(Resource):
+    @setup_required
+    @login_required
+    @account_initialization_required
+    @marshal_with(app_detail_fields_with_site)
+    @cloud_edition_billing_resource_check('apps')
+    def post(self):
+        """Import app from url"""
+        # The role of the current user in the ta table must be admin, owner, or editor
+        if not current_user.is_editor:
+            raise Forbidden()
+
+        parser = reqparse.RequestParser()
+        parser.add_argument('url', type=str, required=True, nullable=False, location='json')
+        parser.add_argument('name', type=str, location='json')
+        parser.add_argument('description', type=str, location='json')
+        parser.add_argument('icon', type=str, location='json')
+        parser.add_argument('icon_background', type=str, location='json')
+        args = parser.parse_args()
+
+        app = AppDslService.import_and_create_new_app_from_url(
+            tenant_id=current_user.current_tenant_id,
+            url=args['url'],
+            args=args,
+            account=current_user
+        )
 
 
         return app, 201
         return app, 201
 
 
@@ -177,9 +212,13 @@ class AppCopyApi(Resource):
         parser.add_argument('icon_background', type=str, location='json')
         parser.add_argument('icon_background', type=str, location='json')
         args = parser.parse_args()
         args = parser.parse_args()
 
 
-        app_service = AppService()
+        data = AppDslService.export_dsl(app_model=app_model)
-        data = app_service.export_app(app_model)
+        app = AppDslService.import_and_create_new_app(
-        app = app_service.import_app(current_user.current_tenant_id, data, args, current_user)
+            tenant_id=current_user.current_tenant_id,
+            data=data,
+            args=args,
+            account=current_user
+        )
 
 
         return app, 201
         return app, 201
 
 
@@ -195,10 +234,8 @@ class AppExportApi(Resource):
         if not current_user.is_editor:
         if not current_user.is_editor:
             raise Forbidden()
             raise Forbidden()
 
 
-        app_service = AppService()
-
         return {
         return {
-            "data": app_service.export_app(app_model)
+            "data": AppDslService.export_dsl(app_model=app_model)
         }
         }
 
 
 
 
@@ -322,6 +359,7 @@ class AppTraceApi(Resource):
 
 
 api.add_resource(AppListApi, '/apps')
 api.add_resource(AppListApi, '/apps')
 api.add_resource(AppImportApi, '/apps/import')
 api.add_resource(AppImportApi, '/apps/import')
+api.add_resource(AppImportFromUrlApi, '/apps/import/url')
 api.add_resource(AppApi, '/apps/<uuid:app_id>')
 api.add_resource(AppApi, '/apps/<uuid:app_id>')
 api.add_resource(AppCopyApi, '/apps/<uuid:app_id>/copy')
 api.add_resource(AppCopyApi, '/apps/<uuid:app_id>/copy')
 api.add_resource(AppExportApi, '/apps/<uuid:app_id>/export')
 api.add_resource(AppExportApi, '/apps/<uuid:app_id>/export')

+ 2 - 2
api/controllers/console/app/workflow.py

@@ -20,6 +20,7 @@ from libs import helper
 from libs.helper import TimestampField, uuid_value
 from libs.helper import TimestampField, uuid_value
 from libs.login import current_user, login_required
 from libs.login import current_user, login_required
 from models.model import App, AppMode
 from models.model import App, AppMode
+from services.app_dsl_service import AppDslService
 from services.app_generate_service import AppGenerateService
 from services.app_generate_service import AppGenerateService
 from services.errors.app import WorkflowHashNotEqualError
 from services.errors.app import WorkflowHashNotEqualError
 from services.workflow_service import WorkflowService
 from services.workflow_service import WorkflowService
@@ -128,8 +129,7 @@ class DraftWorkflowImportApi(Resource):
         parser.add_argument('data', type=str, required=True, nullable=False, location='json')
         parser.add_argument('data', type=str, required=True, nullable=False, location='json')
         args = parser.parse_args()
         args = parser.parse_args()
 
 
-        workflow_service = WorkflowService()
+        workflow = AppDslService.import_and_overwrite_workflow(
-        workflow = workflow_service.import_draft_workflow(
             app_model=app_model,
             app_model=app_model,
             data=args['data'],
             data=args['data'],
             account=current_user
             account=current_user

+ 407 - 0
api/services/app_dsl_service.py

@@ -0,0 +1,407 @@
+import logging
+
+import httpx
+import yaml  # type: ignore
+
+from events.app_event import app_model_config_was_updated, app_was_created
+from extensions.ext_database import db
+from models.account import Account
+from models.model import App, AppMode, AppModelConfig
+from models.workflow import Workflow
+from services.workflow_service import WorkflowService
+
+logger = logging.getLogger(__name__)
+
+current_dsl_version = "0.1.0"
+dsl_to_dify_version_mapping: dict[str, str] = {
+    "0.1.0": "0.6.0",  # dsl version -> from dify version
+}
+
+
+class AppDslService:
+    @classmethod
+    def import_and_create_new_app_from_url(cls, tenant_id: str, url: str, args: dict, account: Account) -> App:
+        """
+        Import app dsl from url and create new app
+        :param tenant_id: tenant id
+        :param url: import url
+        :param args: request args
+        :param account: Account instance
+        """
+        try:
+            max_size = 10 * 1024 * 1024  # 10MB
+            timeout = httpx.Timeout(10.0)
+            with httpx.stream("GET", url.strip(), follow_redirects=True, timeout=timeout) as response:
+                response.raise_for_status()
+                total_size = 0
+                content = b""
+                for chunk in response.iter_bytes():
+                    total_size += len(chunk)
+                    if total_size > max_size:
+                        raise ValueError("File size exceeds the limit of 10MB")
+                    content += chunk
+        except httpx.HTTPStatusError as http_err:
+            raise ValueError(f"HTTP error occurred: {http_err}")
+        except httpx.RequestError as req_err:
+            raise ValueError(f"Request error occurred: {req_err}")
+        except Exception as e:
+            raise ValueError(f"Failed to fetch DSL from URL: {e}")
+
+        if not content:
+            raise ValueError("Empty content from url")
+
+        try:
+            data = content.decode("utf-8")
+        except UnicodeDecodeError as e:
+            raise ValueError(f"Error decoding content: {e}")
+
+        return cls.import_and_create_new_app(tenant_id, data, args, account)
+
+    @classmethod
+    def import_and_create_new_app(cls, tenant_id: str, data: str, args: dict, account: Account) -> App:
+        """
+        Import app dsl and create new app
+        :param tenant_id: tenant id
+        :param data: import data
+        :param args: request args
+        :param account: Account instance
+        """
+        try:
+            import_data = yaml.safe_load(data)
+        except yaml.YAMLError:
+            raise ValueError("Invalid YAML format in data argument.")
+
+        # check or repair dsl version
+        import_data = cls._check_or_fix_dsl(import_data)
+
+        app_data = import_data.get('app')
+        if not app_data:
+            raise ValueError("Missing app in data argument")
+
+        # get app basic info
+        name = args.get("name") if args.get("name") else app_data.get('name')
+        description = args.get("description") if args.get("description") else app_data.get('description', '')
+        icon = args.get("icon") if args.get("icon") else app_data.get('icon')
+        icon_background = args.get("icon_background") if args.get("icon_background") \
+            else app_data.get('icon_background')
+
+        # import dsl and create app
+        app_mode = AppMode.value_of(app_data.get('mode'))
+        if app_mode in [AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]:
+            app = cls._import_and_create_new_workflow_based_app(
+                tenant_id=tenant_id,
+                app_mode=app_mode,
+                workflow_data=import_data.get('workflow'),
+                account=account,
+                name=name,
+                description=description,
+                icon=icon,
+                icon_background=icon_background
+            )
+        elif app_mode in [AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.COMPLETION]:
+            app = cls._import_and_create_new_model_config_based_app(
+                tenant_id=tenant_id,
+                app_mode=app_mode,
+                model_config_data=import_data.get('model_config'),
+                account=account,
+                name=name,
+                description=description,
+                icon=icon,
+                icon_background=icon_background
+            )
+        else:
+            raise ValueError("Invalid app mode")
+
+        return app
+
+    @classmethod
+    def import_and_overwrite_workflow(cls, app_model: App, data: str, account: Account) -> Workflow:
+        """
+        Import app dsl and overwrite workflow
+        :param app_model: App instance
+        :param data: import data
+        :param account: Account instance
+        """
+        try:
+            import_data = yaml.safe_load(data)
+        except yaml.YAMLError:
+            raise ValueError("Invalid YAML format in data argument.")
+
+        # check or repair dsl version
+        import_data = cls._check_or_fix_dsl(import_data)
+
+        app_data = import_data.get('app')
+        if not app_data:
+            raise ValueError("Missing app in data argument")
+
+        # import dsl and overwrite app
+        app_mode = AppMode.value_of(app_data.get('mode'))
+        if app_mode not in [AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]:
+            raise ValueError("Only support import workflow in advanced-chat or workflow app.")
+
+        if app_data.get('mode') != app_model.mode:
+            raise ValueError(
+                f"App mode {app_data.get('mode')} is not matched with current app mode {app_mode.value}")
+
+        return cls._import_and_overwrite_workflow_based_app(
+            app_model=app_model,
+            workflow_data=import_data.get('workflow'),
+            account=account,
+        )
+
+    @classmethod
+    def export_dsl(cls, app_model: App) -> str:
+        """
+        Export app
+        :param app_model: App instance
+        :return:
+        """
+        app_mode = AppMode.value_of(app_model.mode)
+
+        export_data = {
+            "version": current_dsl_version,
+            "kind": "app",
+            "app": {
+                "name": app_model.name,
+                "mode": app_model.mode,
+                "icon": app_model.icon,
+                "icon_background": app_model.icon_background,
+                "description": app_model.description
+            }
+        }
+
+        if app_mode in [AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]:
+            cls._append_workflow_export_data(export_data, app_model)
+        else:
+            cls._append_model_config_export_data(export_data, app_model)
+
+        return yaml.dump(export_data)
+
+    @classmethod
+    def _check_or_fix_dsl(cls, import_data: dict) -> dict:
+        """
+        Check or fix dsl
+
+        :param import_data: import data
+        """
+        if not import_data.get('version'):
+            import_data['version'] = "0.1.0"
+
+        if not import_data.get('kind') or import_data.get('kind') != "app":
+            import_data['kind'] = "app"
+
+        if import_data.get('version') != current_dsl_version:
+            # Currently only one DSL version, so no difference checks or compatibility fixes will be performed.
+            logger.warning(f"DSL version {import_data.get('version')} is not compatible "
+                           f"with current version {current_dsl_version}, related to "
+                           f"Dify version {dsl_to_dify_version_mapping.get(current_dsl_version)}.")
+
+        return import_data
+
+    @classmethod
+    def _import_and_create_new_workflow_based_app(cls,
+                                                  tenant_id: str,
+                                                  app_mode: AppMode,
+                                                  workflow_data: dict,
+                                                  account: Account,
+                                                  name: str,
+                                                  description: str,
+                                                  icon: str,
+                                                  icon_background: str) -> App:
+        """
+        Import app dsl and create new workflow based app
+
+        :param tenant_id: tenant id
+        :param app_mode: app mode
+        :param workflow_data: workflow data
+        :param account: Account instance
+        :param name: app name
+        :param description: app description
+        :param icon: app icon
+        :param icon_background: app icon background
+        """
+        if not workflow_data:
+            raise ValueError("Missing workflow in data argument "
+                             "when app mode is advanced-chat or workflow")
+
+        app = cls._create_app(
+            tenant_id=tenant_id,
+            app_mode=app_mode,
+            account=account,
+            name=name,
+            description=description,
+            icon=icon,
+            icon_background=icon_background
+        )
+
+        # init draft workflow
+        workflow_service = WorkflowService()
+        draft_workflow = workflow_service.sync_draft_workflow(
+            app_model=app,
+            graph=workflow_data.get('graph', {}),
+            features=workflow_data.get('../core/app/features', {}),
+            unique_hash=None,
+            account=account
+        )
+        workflow_service.publish_workflow(
+            app_model=app,
+            account=account,
+            draft_workflow=draft_workflow
+        )
+
+        return app
+
+    @classmethod
+    def _import_and_overwrite_workflow_based_app(cls,
+                                                 app_model: App,
+                                                 workflow_data: dict,
+                                                 account: Account) -> Workflow:
+        """
+        Import app dsl and overwrite workflow based app
+
+        :param app_model: App instance
+        :param workflow_data: workflow data
+        :param account: Account instance
+        """
+        if not workflow_data:
+            raise ValueError("Missing workflow in data argument "
+                             "when app mode is advanced-chat or workflow")
+
+        # fetch draft workflow by app_model
+        workflow_service = WorkflowService()
+        current_draft_workflow = workflow_service.get_draft_workflow(app_model=app_model)
+        if current_draft_workflow:
+            unique_hash = current_draft_workflow.unique_hash
+        else:
+            unique_hash = None
+
+        # sync draft workflow
+        draft_workflow = workflow_service.sync_draft_workflow(
+            app_model=app_model,
+            graph=workflow_data.get('graph', {}),
+            features=workflow_data.get('features', {}),
+            unique_hash=unique_hash,
+            account=account
+        )
+
+        return draft_workflow
+
+    @classmethod
+    def _import_and_create_new_model_config_based_app(cls,
+                                                      tenant_id: str,
+                                                      app_mode: AppMode,
+                                                      model_config_data: dict,
+                                                      account: Account,
+                                                      name: str,
+                                                      description: str,
+                                                      icon: str,
+                                                      icon_background: str) -> App:
+        """
+        Import app dsl and create new model config based app
+
+        :param tenant_id: tenant id
+        :param app_mode: app mode
+        :param model_config_data: model config data
+        :param account: Account instance
+        :param name: app name
+        :param description: app description
+        :param icon: app icon
+        :param icon_background: app icon background
+        """
+        if not model_config_data:
+            raise ValueError("Missing model_config in data argument "
+                             "when app mode is chat, agent-chat or completion")
+
+        app = cls._create_app(
+            tenant_id=tenant_id,
+            app_mode=app_mode,
+            account=account,
+            name=name,
+            description=description,
+            icon=icon,
+            icon_background=icon_background
+        )
+
+        app_model_config = AppModelConfig()
+        app_model_config = app_model_config.from_model_config_dict(model_config_data)
+        app_model_config.app_id = app.id
+
+        db.session.add(app_model_config)
+        db.session.commit()
+
+        app.app_model_config_id = app_model_config.id
+
+        app_model_config_was_updated.send(
+            app,
+            app_model_config=app_model_config
+        )
+
+        return app
+
+    @classmethod
+    def _create_app(cls,
+                    tenant_id: str,
+                    app_mode: AppMode,
+                    account: Account,
+                    name: str,
+                    description: str,
+                    icon: str,
+                    icon_background: str) -> App:
+        """
+        Create new app
+
+        :param tenant_id: tenant id
+        :param app_mode: app mode
+        :param account: Account instance
+        :param name: app name
+        :param description: app description
+        :param icon: app icon
+        :param icon_background: app icon background
+        """
+        app = App(
+            tenant_id=tenant_id,
+            mode=app_mode.value,
+            name=name,
+            description=description,
+            icon=icon,
+            icon_background=icon_background,
+            enable_site=True,
+            enable_api=True
+        )
+
+        db.session.add(app)
+        db.session.commit()
+
+        app_was_created.send(app, account=account)
+
+        return app
+
+    @classmethod
+    def _append_workflow_export_data(cls, export_data: dict, app_model: App) -> None:
+        """
+        Append workflow export data
+        :param export_data: export data
+        :param app_model: App instance
+        """
+        workflow_service = WorkflowService()
+        workflow = workflow_service.get_draft_workflow(app_model)
+        if not workflow:
+            raise ValueError("Missing draft workflow configuration, please check.")
+
+        export_data['workflow'] = {
+            "graph": workflow.graph_dict,
+            "features": workflow.features_dict
+        }
+
+    @classmethod
+    def _append_model_config_export_data(cls, export_data: dict, app_model: App) -> None:
+        """
+        Append model config export data
+        :param export_data: export data
+        :param app_model: App instance
+        """
+        app_model_config = app_model.app_model_config
+        if not app_model_config:
+            raise ValueError("Missing app configuration, please check.")
+
+        export_data['model_config'] = app_model_config.to_dict()

+ 1 - 117
api/services/app_service.py

@@ -3,7 +3,6 @@ import logging
 from datetime import datetime, timezone
 from datetime import datetime, timezone
 from typing import cast
 from typing import cast
 
 
-import yaml
 from flask_login import current_user
 from flask_login import current_user
 from flask_sqlalchemy.pagination import Pagination
 from flask_sqlalchemy.pagination import Pagination
 
 
@@ -17,13 +16,12 @@ from core.model_runtime.entities.model_entities import ModelPropertyKey, ModelTy
 from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
 from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel
 from core.tools.tool_manager import ToolManager
 from core.tools.tool_manager import ToolManager
 from core.tools.utils.configuration import ToolParameterConfigurationManager
 from core.tools.utils.configuration import ToolParameterConfigurationManager
-from events.app_event import app_model_config_was_updated, app_was_created
+from events.app_event import app_was_created
 from extensions.ext_database import db
 from extensions.ext_database import db
 from models.account import Account
 from models.account import Account
 from models.model import App, AppMode, AppModelConfig
 from models.model import App, AppMode, AppModelConfig
 from models.tools import ApiToolProvider
 from models.tools import ApiToolProvider
 from services.tag_service import TagService
 from services.tag_service import TagService
-from services.workflow_service import WorkflowService
 from tasks.remove_app_and_related_data_task import remove_app_and_related_data_task
 from tasks.remove_app_and_related_data_task import remove_app_and_related_data_task
 
 
 
 
@@ -144,120 +142,6 @@ class AppService:
 
 
         return app
         return app
 
 
-    def import_app(self, tenant_id: str, data: str, args: dict, account: Account) -> App:
-        """
-        Import app
-        :param tenant_id: tenant id
-        :param data: import data
-        :param args: request args
-        :param account: Account instance
-        """
-        try:
-            import_data = yaml.safe_load(data)
-        except yaml.YAMLError as e:
-            raise ValueError("Invalid YAML format in data argument.")
-
-        app_data = import_data.get('app')
-        model_config_data = import_data.get('model_config')
-        workflow = import_data.get('workflow')
-
-        if not app_data:
-            raise ValueError("Missing app in data argument")
-
-        app_mode = AppMode.value_of(app_data.get('mode'))
-        if app_mode in [AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]:
-            if not workflow:
-                raise ValueError("Missing workflow in data argument "
-                                 "when app mode is advanced-chat or workflow")
-        elif app_mode in [AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.COMPLETION]:
-            if not model_config_data:
-                raise ValueError("Missing model_config in data argument "
-                                 "when app mode is chat, agent-chat or completion")
-        else:
-            raise ValueError("Invalid app mode")
-
-        app = App(
-            tenant_id=tenant_id,
-            mode=app_data.get('mode'),
-            name=args.get("name") if args.get("name") else app_data.get('name'),
-            description=args.get("description") if args.get("description") else app_data.get('description', ''),
-            icon=args.get("icon") if args.get("icon") else app_data.get('icon'),
-            icon_background=args.get("icon_background") if args.get("icon_background") \
-                else app_data.get('icon_background'),
-            enable_site=True,
-            enable_api=True
-        )
-
-        db.session.add(app)
-        db.session.commit()
-
-        app_was_created.send(app, account=account)
-
-        if workflow:
-            # init draft workflow
-            workflow_service = WorkflowService()
-            draft_workflow = workflow_service.sync_draft_workflow(
-                app_model=app,
-                graph=workflow.get('graph'),
-                features=workflow.get('features'),
-                unique_hash=None,
-                account=account
-            )
-            workflow_service.publish_workflow(
-                app_model=app,
-                account=account,
-                draft_workflow=draft_workflow
-            )
-
-        if model_config_data:
-            app_model_config = AppModelConfig()
-            app_model_config = app_model_config.from_model_config_dict(model_config_data)
-            app_model_config.app_id = app.id
-
-            db.session.add(app_model_config)
-            db.session.commit()
-
-            app.app_model_config_id = app_model_config.id
-
-            app_model_config_was_updated.send(
-                app,
-                app_model_config=app_model_config
-            )
-
-        return app
-
-    def export_app(self, app: App) -> str:
-        """
-        Export app
-        :param app: App instance
-        :return:
-        """
-        app_mode = AppMode.value_of(app.mode)
-
-        export_data = {
-            "app": {
-                "name": app.name,
-                "mode": app.mode,
-                "icon": app.icon,
-                "icon_background": app.icon_background,
-                "description": app.description
-            }
-        }
-
-        if app_mode in [AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]:
-            workflow_service = WorkflowService()
-            workflow = workflow_service.get_draft_workflow(app)
-            export_data['workflow'] = {
-                "graph": workflow.graph_dict,
-                "features": workflow.features_dict
-            }
-        else:
-            app_model_config = app.app_model_config
-
-            export_data['model_config'] = app_model_config.to_dict()
-
-        return yaml.dump(export_data)
-
     def get_app(self, app: App) -> App:
     def get_app(self, app: App) -> App:
         """
         """
         Get App
         Get App

+ 3 - 5
api/services/recommended_app_service.py

@@ -4,12 +4,13 @@ from os import path
 from typing import Optional
 from typing import Optional
 
 
 import requests
 import requests
+from flask import current_app
 
 
 from configs import dify_config
 from configs import dify_config
 from constants.languages import languages
 from constants.languages import languages
 from extensions.ext_database import db
 from extensions.ext_database import db
 from models.model import App, RecommendedApp
 from models.model import App, RecommendedApp
-from services.app_service import AppService
+from services.app_dsl_service import AppDslService
 
 
 logger = logging.getLogger(__name__)
 logger = logging.getLogger(__name__)
 
 
@@ -186,16 +187,13 @@ class RecommendedAppService:
         if not app_model or not app_model.is_public:
         if not app_model or not app_model.is_public:
             return None
             return None
 
 
-        app_service = AppService()
-        export_str = app_service.export_app(app_model)
-
         return {
         return {
             'id': app_model.id,
             'id': app_model.id,
             'name': app_model.name,
             'name': app_model.name,
             'icon': app_model.icon,
             'icon': app_model.icon,
             'icon_background': app_model.icon_background,
             'icon_background': app_model.icon_background,
             'mode': app_model.mode,
             'mode': app_model.mode,
-            'export_data': export_str
+            'export_data': AppDslService.export_dsl(app_model=app_model)
         }
         }
 
 
     @classmethod
     @classmethod

+ 0 - 52
api/services/workflow_service.py

@@ -3,8 +3,6 @@ import time
 from datetime import datetime, timezone
 from datetime import datetime, timezone
 from typing import Optional
 from typing import Optional
 
 
-import yaml
-
 from core.app.apps.advanced_chat.app_config_manager import AdvancedChatAppConfigManager
 from core.app.apps.advanced_chat.app_config_manager import AdvancedChatAppConfigManager
 from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager
 from core.app.apps.workflow.app_config_manager import WorkflowAppConfigManager
 from core.model_runtime.utils.encoders import jsonable_encoder
 from core.model_runtime.utils.encoders import jsonable_encoder
@@ -114,56 +112,6 @@ class WorkflowService:
         # return draft workflow
         # return draft workflow
         return workflow
         return workflow
 
 
-    def import_draft_workflow(self, app_model: App,
-                              data: str,
-                              account: Account) -> Workflow:
-        """
-        Import draft workflow
-        :param app_model: App instance
-        :param data: import data
-        :param account: Account instance
-        :return:
-        """
-        try:
-            import_data = yaml.safe_load(data)
-        except yaml.YAMLError as e:
-            raise ValueError("Invalid YAML format in data argument.")
-
-        app_data = import_data.get('app')
-        workflow = import_data.get('workflow')
-
-        if not app_data:
-            raise ValueError("Missing app in data argument")
-
-        app_mode = AppMode.value_of(app_data.get('mode'))
-        if app_mode not in [AppMode.ADVANCED_CHAT, AppMode.WORKFLOW]:
-            raise ValueError("Only support import workflow in advanced-chat or workflow app.")
-
-        if app_data.get('mode') != app_model.mode:
-            raise ValueError(f"App mode {app_data.get('mode')} is not matched with current app mode {app_model.mode}")
-
-        if not workflow:
-            raise ValueError("Missing workflow in data argument "
-                             "when app mode is advanced-chat or workflow")
-
-        # fetch draft workflow by app_model
-        current_draft_workflow = self.get_draft_workflow(app_model=app_model)
-        if current_draft_workflow:
-            unique_hash = current_draft_workflow.unique_hash
-        else:
-            unique_hash = None
-
-        # sync draft workflow
-        draft_workflow = self.sync_draft_workflow(
-            app_model=app_model,
-            graph=workflow.get('graph'),
-            features=workflow.get('features'),
-            unique_hash=unique_hash,
-            account=account
-        )
-
-        return draft_workflow
-
     def publish_workflow(self, app_model: App,
     def publish_workflow(self, app_model: App,
                          account: Account,
                          account: Account,
                          draft_workflow: Optional[Workflow] = None) -> Workflow:
                          draft_workflow: Optional[Workflow] = None) -> Workflow: