feat(workflow): add Dify workflow import adapter and related APIs
This commit is contained in:
@@ -321,6 +321,26 @@ class AppService:
|
||||
self.db.add(agent_cfg)
|
||||
logger.debug("Agent 配置已创建", extra={"app_id": str(app_id)})
|
||||
|
||||
def _create_workflow_config(
|
||||
self,
|
||||
app_id: uuid.UUID,
|
||||
data: app_schema.WorkflowConfigCreate,
|
||||
now: datetime.datetime
|
||||
):
|
||||
workflow_cfg = WorkflowConfig(
|
||||
id=uuid.uuid4(),
|
||||
app_id=app_id,
|
||||
nodes=[node.model_dump() for node in data.nodes] if data.nodes else [],
|
||||
edges=[edge.model_dump() for edge in data.edges] if data.edges else [],
|
||||
variables=[var.model_dump() for var in data.variables] if data.variables else [],
|
||||
execution_config=data.execution_config.model_dump() if data.execution_config else {},
|
||||
triggers=[trigger.model_dump() for trigger in data.triggers] if data.triggers else [],
|
||||
is_active=True,
|
||||
created_at=now,
|
||||
updated_at=now
|
||||
)
|
||||
self.db.add(workflow_cfg)
|
||||
|
||||
def _create_multi_agent_config(
|
||||
self,
|
||||
app_id: uuid.UUID,
|
||||
@@ -532,6 +552,9 @@ class AppService:
|
||||
if app.type == "multi_agent" and data.multi_agent_config:
|
||||
self._create_multi_agent_config(app.id, data.multi_agent_config, now)
|
||||
|
||||
if app.type == "workflow" and data.workflow_config:
|
||||
self._create_workflow_config(app.id, data.workflow_config, now)
|
||||
|
||||
self.db.commit()
|
||||
self.db.refresh(app)
|
||||
|
||||
@@ -968,7 +991,7 @@ class AppService:
|
||||
config = self.db.scalars(stmt).first()
|
||||
|
||||
try:
|
||||
config_memory=config.memory
|
||||
config_memory = config.memory
|
||||
if 'memory_content' in config_memory:
|
||||
config.memory['memory_config_id'] = config.memory.pop('memory_content')
|
||||
except:
|
||||
@@ -1189,9 +1212,9 @@ class AppService:
|
||||
# ==================== 记忆配置提取方法 ====================
|
||||
|
||||
def _extract_memory_config_id(
|
||||
self,
|
||||
app_type: str,
|
||||
config: Dict[str, Any]
|
||||
self,
|
||||
app_type: str,
|
||||
config: Dict[str, Any]
|
||||
) -> Tuple[Optional[uuid.UUID], bool]:
|
||||
"""从发布配置中提取 memory_config_id(委托给 MemoryConfigService)
|
||||
|
||||
@@ -1205,13 +1228,13 @@ class AppService:
|
||||
- is_legacy_int: 是否检测到旧格式 int 数据,需要回退到工作空间默认配置
|
||||
"""
|
||||
from app.services.memory_config_service import MemoryConfigService
|
||||
|
||||
|
||||
service = MemoryConfigService(self.db)
|
||||
return service.extract_memory_config_id(app_type, config)
|
||||
|
||||
def _get_workspace_default_memory_config_id(
|
||||
self,
|
||||
workspace_id: uuid.UUID
|
||||
self,
|
||||
workspace_id: uuid.UUID
|
||||
) -> Optional[uuid.UUID]:
|
||||
"""获取工作空间的默认记忆配置ID
|
||||
|
||||
@@ -1222,22 +1245,22 @@ class AppService:
|
||||
Optional[uuid.UUID]: 默认记忆配置ID,如果不存在则返回 None
|
||||
"""
|
||||
from app.services.memory_config_service import MemoryConfigService
|
||||
|
||||
|
||||
service = MemoryConfigService(self.db)
|
||||
config = service.get_workspace_default_config(workspace_id)
|
||||
|
||||
|
||||
if not config:
|
||||
logger.warning(
|
||||
f"工作空间没有可用的记忆配置: workspace_id={workspace_id}"
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
return config.config_id
|
||||
|
||||
def _update_endusers_memory_config(
|
||||
self,
|
||||
app_id: uuid.UUID,
|
||||
memory_config_id: uuid.UUID
|
||||
self,
|
||||
app_id: uuid.UUID,
|
||||
memory_config_id: uuid.UUID
|
||||
) -> int:
|
||||
"""批量更新应用下所有终端用户的 memory_config_id
|
||||
|
||||
@@ -1249,13 +1272,13 @@ class AppService:
|
||||
int: 更新的终端用户数量
|
||||
"""
|
||||
from app.repositories.end_user_repository import EndUserRepository
|
||||
|
||||
|
||||
repo = EndUserRepository(self.db)
|
||||
updated_count = repo.batch_update_memory_config_id(
|
||||
app_id=app_id,
|
||||
memory_config_id=memory_config_id
|
||||
)
|
||||
|
||||
|
||||
return updated_count
|
||||
|
||||
# ==================== 应用发布管理 ====================
|
||||
@@ -1403,7 +1426,7 @@ class AppService:
|
||||
|
||||
# 提取记忆配置ID并更新终端用户
|
||||
memory_config_id, is_legacy_int = self._extract_memory_config_id(app.type, config)
|
||||
|
||||
|
||||
# 如果检测到旧格式 int 数据,回退到工作空间默认配置
|
||||
if is_legacy_int and not memory_config_id:
|
||||
memory_config_id = self._get_workspace_default_memory_config_id(app.workspace_id)
|
||||
@@ -1412,7 +1435,7 @@ class AppService:
|
||||
f"发布时使用工作空间默认记忆配置(旧数据兼容): app_id={app_id}, "
|
||||
f"workspace_id={app.workspace_id}, memory_config_id={memory_config_id}"
|
||||
)
|
||||
|
||||
|
||||
if memory_config_id:
|
||||
updated_count = self._update_endusers_memory_config(app_id, memory_config_id)
|
||||
logger.info(
|
||||
@@ -1537,7 +1560,7 @@ class AppService:
|
||||
|
||||
# 提取记忆配置ID并更新终端用户
|
||||
memory_config_id, is_legacy_int = self._extract_memory_config_id(release.type, release.config)
|
||||
|
||||
|
||||
# 如果检测到旧格式 int 数据,回退到工作空间默认配置
|
||||
if is_legacy_int and not memory_config_id:
|
||||
memory_config_id = self._get_workspace_default_memory_config_id(app.workspace_id)
|
||||
@@ -1546,7 +1569,7 @@ class AppService:
|
||||
f"回滚时使用工作空间默认记忆配置(旧数据兼容): app_id={app_id}, "
|
||||
f"workspace_id={app.workspace_id}, memory_config_id={memory_config_id}"
|
||||
)
|
||||
|
||||
|
||||
if memory_config_id:
|
||||
updated_count = self._update_endusers_memory_config(app_id, memory_config_id)
|
||||
logger.info(
|
||||
|
||||
102
api/app/services/workflow_import_service.py
Normal file
102
api/app/services/workflow_import_service.py
Normal file
@@ -0,0 +1,102 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
# Author: Eternity
|
||||
# @Email: 1533512157@qq.com
|
||||
# @Time : 2026/2/25 14:39
|
||||
import json
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.aioRedis import aio_redis_set, aio_redis_get
|
||||
from app.core.config import settings
|
||||
from app.core.exceptions import BusinessException
|
||||
from app.core.workflow.adapters.base_adapter import WorkflowImportResult, WorkflowParserResult
|
||||
from app.core.workflow.adapters.errors import UnsupportPlatform, InvalidConfiguration
|
||||
from app.core.workflow.adapters.registry import PlatformAdapterRegistry
|
||||
from app.schemas import AppCreate
|
||||
from app.schemas.workflow_schema import WorkflowConfigCreate
|
||||
from app.services.app_service import AppService
|
||||
from app.services.workflow_service import WorkflowService
|
||||
|
||||
|
||||
class WorkflowImportService:
|
||||
def __init__(self, db: Session):
|
||||
self.db = db
|
||||
self.registry = PlatformAdapterRegistry
|
||||
self.cache_timeout = settings.WORKFLOW_IMPORT_CACHE_TIMEOUT
|
||||
|
||||
self.app_service = AppService(db)
|
||||
self.workflow_service = WorkflowService(db)
|
||||
|
||||
async def flush_config(self, temp_id: str, config: WorkflowParserResult):
|
||||
config_cache = await aio_redis_get(temp_id)
|
||||
if not config_cache:
|
||||
raise BusinessException("Workflow configuration has expired. Please re-upload it.")
|
||||
await aio_redis_set(temp_id, config.model_dump_json(), expire=self.cache_timeout)
|
||||
|
||||
async def upload_config(
|
||||
self,
|
||||
platform: str,
|
||||
config: dict[str, Any],
|
||||
):
|
||||
|
||||
if not self.registry.is_supported(platform):
|
||||
return WorkflowImportResult(
|
||||
success=False,
|
||||
temp_id=None,
|
||||
workflow_id=None,
|
||||
errors=[UnsupportPlatform(platform=platform)]
|
||||
)
|
||||
|
||||
adapter = self.registry.get_adapter(platform, config)
|
||||
|
||||
if not adapter.validate_config():
|
||||
return WorkflowImportResult(
|
||||
success=False,
|
||||
temp_id=None,
|
||||
workflow_id=None,
|
||||
errors=[InvalidConfiguration()]
|
||||
)
|
||||
|
||||
workflow_config = adapter.parse_workflow()
|
||||
temp_id = uuid.uuid4().hex
|
||||
await aio_redis_set(temp_id, workflow_config.model_dump(), expire=self.cache_timeout)
|
||||
return WorkflowImportResult(
|
||||
success=True,
|
||||
temp_id=temp_id,
|
||||
workflow_id=None,
|
||||
edges=workflow_config.edges,
|
||||
nodes=workflow_config.nodes,
|
||||
variables=workflow_config.variables,
|
||||
warnings=workflow_config.warnings,
|
||||
errors=workflow_config.errors
|
||||
)
|
||||
|
||||
async def save_workflow(
|
||||
self,
|
||||
user_id: uuid.UUID,
|
||||
workspace_id: uuid.UUID,
|
||||
temp_id: str,
|
||||
name: str,
|
||||
description: str | None,
|
||||
):
|
||||
config = await aio_redis_get(temp_id)
|
||||
if config is None:
|
||||
raise BusinessException("Configuration import timed out. Please try again.")
|
||||
config = json.loads(config)
|
||||
app = self.app_service.create_app(
|
||||
user_id=user_id,
|
||||
workspace_id=workspace_id,
|
||||
data=AppCreate(
|
||||
name=name,
|
||||
description=description,
|
||||
type="workflow",
|
||||
workflow_config=WorkflowConfigCreate(
|
||||
nodes=config["nodes"],
|
||||
edges=config["edges"],
|
||||
variables=config["variables"]
|
||||
)
|
||||
)
|
||||
)
|
||||
return app
|
||||
@@ -6,13 +6,16 @@ import logging
|
||||
import uuid
|
||||
from typing import Any, Annotated, Optional
|
||||
|
||||
import yaml
|
||||
from fastapi import Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.error_codes import BizCode
|
||||
from app.core.exceptions import BusinessException
|
||||
from app.core.workflow.adapters.registry import PlatformAdapterRegistry
|
||||
from app.core.workflow.validator import validate_workflow_config
|
||||
from app.db import get_db
|
||||
from app.models import App
|
||||
from app.models.workflow_model import WorkflowConfig, WorkflowExecution
|
||||
from app.repositories.workflow_repository import (
|
||||
WorkflowConfigRepository,
|
||||
@@ -38,6 +41,8 @@ class WorkflowService:
|
||||
self.conversation_service = ConversationService(db)
|
||||
self.multimodal_service = MultimodalService(db)
|
||||
|
||||
self.registry = PlatformAdapterRegistry
|
||||
|
||||
# ==================== 配置管理 ====================
|
||||
|
||||
def create_workflow_config(
|
||||
@@ -200,6 +205,32 @@ class WorkflowService:
|
||||
logger.info(f"删除工作流配置成功: app_id={app_id}, config_id={config.id}")
|
||||
return True
|
||||
|
||||
def export_workflow_dsl(self, app_id: uuid.UUID):
|
||||
config = self.get_workflow_config(app_id)
|
||||
if not config:
|
||||
raise BusinessException(
|
||||
code=BizCode.NOT_FOUND,
|
||||
message=f"工作流配置不存在: app_id={app_id}"
|
||||
)
|
||||
|
||||
app: App = config.app
|
||||
dsl_info = {
|
||||
"app": {
|
||||
"name": app.name,
|
||||
"description": app.description,
|
||||
"icon": app.icon,
|
||||
"icon_type": app.icon_type
|
||||
},
|
||||
"workflow": {
|
||||
"variables": config.variables,
|
||||
"edges": config.edges,
|
||||
"nodes": config.nodes,
|
||||
"execution_config": config.execution_config,
|
||||
"triggers": config.triggers
|
||||
}
|
||||
}
|
||||
return yaml.dump(dsl_info, default_flow_style=False, allow_unicode=True)
|
||||
|
||||
def check_config(self, app_id: uuid.UUID) -> WorkflowConfig:
|
||||
"""检查工作流配置的完整性
|
||||
|
||||
|
||||
Reference in New Issue
Block a user