From 956efe6a09405fe920882a23c07ac06bdbc61a9e Mon Sep 17 00:00:00 2001 From: wxy Date: Wed, 18 Mar 2026 12:21:59 +0800 Subject: [PATCH 1/4] fix: read new_name from request body in copy_app endpoint --- api/app/controllers/app_controller.py | 3 ++- api/app/schemas/app_schema.py | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/api/app/controllers/app_controller.py b/api/app/controllers/app_controller.py index d6c71f37..85493500 100644 --- a/api/app/controllers/app_controller.py +++ b/api/app/controllers/app_controller.py @@ -193,7 +193,7 @@ def delete_app( @cur_workspace_access_guard() def copy_app( app_id: uuid.UUID, - new_name: Optional[str] = None, + payload: app_schema.CopyAppRequest = None, db: Session = Depends(get_db), current_user=Depends(get_current_user), ): @@ -205,6 +205,7 @@ def copy_app( - 不影响原应用 """ workspace_id = current_user.current_workspace_id + new_name = payload.new_name if payload else None logger.info( "用户请求复制应用", extra={ diff --git a/api/app/schemas/app_schema.py b/api/app/schemas/app_schema.py index b63c73f5..8bfedd5a 100644 --- a/api/app/schemas/app_schema.py +++ b/api/app/schemas/app_schema.py @@ -525,6 +525,13 @@ class AppRelease(BaseModel): return int(dt.timestamp() * 1000) if dt else None +# ---------- App Copy Schema ---------- + +class CopyAppRequest(BaseModel): + """复制应用请求""" + new_name: Optional[str] = Field(None, description="新应用名称,不填则使用原名称-副本") + + # ---------- App Share Schemas ---------- class AppShareCreate(BaseModel): From 56c98648f90755615b39d4d3339f2b480ab79fed Mon Sep 17 00:00:00 2001 From: wxy Date: Wed, 18 Mar 2026 12:30:50 +0800 Subject: [PATCH 2/4] fix: support both query param and body for new_name in copy_app for backward compatibility --- api/app/controllers/app_controller.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/app/controllers/app_controller.py b/api/app/controllers/app_controller.py index 85493500..9b77fa30 100644 --- a/api/app/controllers/app_controller.py +++ b/api/app/controllers/app_controller.py @@ -193,6 +193,7 @@ def delete_app( @cur_workspace_access_guard() def copy_app( app_id: uuid.UUID, + new_name: Optional[str] = None, payload: app_schema.CopyAppRequest = None, db: Session = Depends(get_db), current_user=Depends(get_current_user), @@ -205,7 +206,8 @@ def copy_app( - 不影响原应用 """ workspace_id = current_user.current_workspace_id - new_name = payload.new_name if payload else None + # body takes precedence over query param for backward compatibility + new_name = (payload.new_name if payload else None) or new_name logger.info( "用户请求复制应用", extra={ From f042f445019c98474b1b7842adb4ad57c3ae31ee Mon Sep 17 00:00:00 2001 From: wxy Date: Wed, 18 Mar 2026 16:38:22 +0800 Subject: [PATCH 3/4] fix: shared app uses release snapshot config instead of draft in draft_run and get_agent_config --- api/app/controllers/app_controller.py | 46 ++++++++++++++------ api/app/services/app_service.py | 60 +++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 12 deletions(-) diff --git a/api/app/controllers/app_controller.py b/api/app/controllers/app_controller.py index 9b77fa30..79ec9116 100644 --- a/api/app/controllers/app_controller.py +++ b/api/app/controllers/app_controller.py @@ -559,18 +559,29 @@ async def draft_run( service._check_agent_config(app_id) # 2. 获取 Agent 配置 - stmt = select(AgentConfig).where(AgentConfig.app_id == app_id) - agent_cfg = db.scalars(stmt).first() - if not agent_cfg: - raise BusinessException("Agent 配置不存在", BizCode.AGENT_CONFIG_MISSING) + # 共享应用:从最新发布版本读配置快照,而非草稿 + is_shared = app.workspace_id != workspace_id + if is_shared: + if not app.current_release_id: + raise BusinessException("该应用尚未发布,无法使用", BizCode.AGENT_CONFIG_MISSING) + release = db.get(AppRelease, app.current_release_id) + if not release: + raise BusinessException("发布版本不存在", BizCode.AGENT_CONFIG_MISSING) + agent_cfg = service._agent_config_from_release(release) + model_config = db.get(ModelConfig, release.default_model_config_id) if release.default_model_config_id else None + else: + stmt = select(AgentConfig).where(AgentConfig.app_id == app_id) + agent_cfg = db.scalars(stmt).first() + if not agent_cfg: + raise BusinessException("Agent 配置不存在", BizCode.AGENT_CONFIG_MISSING) - # 3. 获取模型配置 - model_config = None - if agent_cfg.default_model_config_id: - model_config = db.get(ModelConfig, agent_cfg.default_model_config_id) - if not model_config: - from app.core.exceptions import ResourceNotFoundException - raise ResourceNotFoundException("模型配置", str(agent_cfg.default_model_config_id)) + # 3. 获取模型配置 + model_config = None + if agent_cfg.default_model_config_id: + model_config = db.get(ModelConfig, agent_cfg.default_model_config_id) + if not model_config: + from app.core.exceptions import ResourceNotFoundException + raise ResourceNotFoundException("模型配置", str(agent_cfg.default_model_config_id)) # 流式返回 if payload.stream: @@ -726,7 +737,18 @@ async def draft_run( msg="多 Agent 任务执行成功" ) elif app.type == AppType.WORKFLOW: # 工作流 - config = workflow_service.check_config(app_id) + # 共享应用:从最新发布版本读配置快照,而非草稿 + is_shared = app.workspace_id != workspace_id + if is_shared: + if not app.current_release_id: + raise BusinessException("该应用尚未发布,无法使用", BizCode.AGENT_CONFIG_MISSING) + from app.models import AppRelease + release = db.get(AppRelease, app.current_release_id) + if not release: + raise BusinessException("发布版本不存在", BizCode.AGENT_CONFIG_MISSING) + config = service._workflow_config_from_release(release) + else: + config = workflow_service.check_config(app_id) # 3. 流式返回 if payload.stream: logger.debug( diff --git a/api/app/services/app_service.py b/api/app/services/app_service.py index 98fdf6c9..94ee606e 100644 --- a/api/app/services/app_service.py +++ b/api/app/services/app_service.py @@ -1426,6 +1426,47 @@ class AppService: logger.info("Agent 配置更新成功", extra={"app_id": str(app_id)}) return agent_cfg + def _agent_config_from_release(self, release: "AppRelease") -> "AgentConfig": + """从发布版本快照重建 AgentConfig 对象(不入库,仅用于运行)""" + cfg = release.config or {} + now = release.created_at or datetime.datetime.now() + agent_cfg = AgentConfig( + id=uuid.uuid4(), + app_id=release.app_id, + system_prompt=cfg.get("system_prompt", ""), + default_model_config_id=release.default_model_config_id, + model_parameters=cfg.get("model_parameters"), + knowledge_retrieval=cfg.get("knowledge_retrieval"), + memory=cfg.get("memory", {}), + variables=cfg.get("variables", []), + tools=cfg.get("tools", []), + skills=cfg.get("skills", {}), + features=cfg.get("features", {}), + is_active=True, + created_at=now, + updated_at=now, + ) + return agent_cfg + + def _workflow_config_from_release(self, release: "AppRelease") -> "WorkflowConfig": + """从发布版本快照重建 WorkflowConfig 对象(不入库,仅用于运行)""" + cfg = release.config or {} + now = release.created_at or datetime.datetime.now() + from app.models.workflow_model import WorkflowConfig as WorkflowConfigModel + wf_cfg = WorkflowConfigModel( + id=uuid.uuid4(), + app_id=release.app_id, + nodes=cfg.get("nodes", []), + edges=cfg.get("edges", []), + variables=cfg.get("variables", []), + execution_config=cfg.get("execution_config", {}), + triggers=cfg.get("triggers", []), + is_active=True, + created_at=now, + updated_at=now, + ) + return wf_cfg + def get_agent_config( self, *, @@ -1457,6 +1498,15 @@ class AppService: # 只读操作,允许访问共享应用 self._validate_app_accessible(app, workspace_id) + # 共享应用:返回最新发布版本的配置快照,而非草稿 + if workspace_id and app.workspace_id != workspace_id: + if not app.current_release_id: + raise BusinessException("该应用尚未发布,无法使用", BizCode.AGENT_CONFIG_MISSING) + release = self.db.get(AppRelease, app.current_release_id) + if not release: + raise BusinessException("发布版本不存在", BizCode.AGENT_CONFIG_MISSING) + return self._agent_config_from_release(release) + stmt = select(AgentConfig).where( AgentConfig.app_id == app_id, AgentConfig.is_active.is_(True) @@ -1555,6 +1605,16 @@ class AppService: # 只读操作,允许访问共享应用 self._validate_app_accessible(app, workspace_id) + + # 共享应用:返回最新发布版本的配置快照,而非草稿 + if workspace_id and app.workspace_id != workspace_id: + if not app.current_release_id: + raise BusinessException("该应用尚未发布,无法使用", BizCode.CONFIG_MISSING) + release = self.db.get(AppRelease, app.current_release_id) + if not release: + raise BusinessException("发布版本不存在", BizCode.CONFIG_MISSING) + return self._workflow_config_from_release(release) + repo = WorkflowConfigRepository(self.db) config = repo.get_by_app_id(app_id) if config: From e24217a6baabf417c72ce914f8d58681870bfe9a Mon Sep 17 00:00:00 2001 From: wxy Date: Wed, 18 Mar 2026 17:36:43 +0800 Subject: [PATCH 4/4] fix: remove redundant local AppRelease import causing NameError in draft_run --- api/app/controllers/app_controller.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/app/controllers/app_controller.py b/api/app/controllers/app_controller.py index 79ec9116..e9b539df 100644 --- a/api/app/controllers/app_controller.py +++ b/api/app/controllers/app_controller.py @@ -520,7 +520,7 @@ async def draft_run( # 提前验证和准备(在流式响应开始前完成) from app.services.app_service import AppService from app.services.multi_agent_service import MultiAgentService - from app.models import AgentConfig, ModelConfig + from app.models import AgentConfig, ModelConfig, AppRelease from sqlalchemy import select from app.core.exceptions import BusinessException from app.services.draft_run_service import AgentRunService @@ -742,7 +742,6 @@ async def draft_run( if is_shared: if not app.current_release_id: raise BusinessException("该应用尚未发布,无法使用", BizCode.AGENT_CONFIG_MISSING) - from app.models import AppRelease release = db.get(AppRelease, app.current_release_id) if not release: raise BusinessException("发布版本不存在", BizCode.AGENT_CONFIG_MISSING)