From 5ff13581166f402e6f2c236fe3fd603c4ee43a24 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sun, 16 Aug 2026 15:51:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=9A=E7=A7=9F=E6=88=B7=E9=9A=94?= =?UTF-8?q?=E7=A6=BB-=E6=8A=80=E8=83=BD=E8=BF=90=E8=A1=8C=E6=97=B6?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E6=94=B9=E6=9C=BA=E6=9E=84=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E7=9B=AE=E5=BD=95(ensure=5Forg=5Fskills=E4=BB=8E=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E6=A8=A1=E6=9D=BF=E5=A4=8D=E5=88=B6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pipeline_core/skill_pack.py | 31 ++++++++++++++++++++++++++++++- wwwroot/api/skill_pack.dspy | 12 +++++++++--- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/pipeline_core/skill_pack.py b/pipeline_core/skill_pack.py index 97b0f91..e1245fb 100644 --- a/pipeline_core/skill_pack.py +++ b/pipeline_core/skill_pack.py @@ -31,7 +31,36 @@ def get_skills_base(): SKILLS_BASE = get_skills_base() PACKS_DIR = os.path.join(SKILLS_BASE, "packs") -ALL_DIR = os.path.join(SKILLS_BASE, "global") # 公共技能 = 选装的技能源头 +ALL_DIR = os.path.join(SKILLS_BASE, "global") # 公共技能 = 全局模板(源头) + + +def get_org_skills_dir(workspace_base, org_id): + """机构工作目录下的技能根目录(多租户隔离)。 + + workspace_base: 机构工作目录根(如 /d/pipeline/workspaces),从 params 表 workspace_base 读。 + org_id: 机构 ID。 + 返回 //skills + """ + return os.path.normpath(os.path.join(workspace_base, str(org_id or '0'), "skills")) + + +def ensure_org_skills(workspace_base, org_id): + """确保机构工作目录的 skills/ 已初始化(首次从全局模板复制公共技能 + 技能集清单)。 + + 多租户隔离:运行时技能必须落在机构工作目录,不能读全局应用目录的 skills/。 + 返回机构技能根目录。 + """ + org_skills = get_org_skills_dir(workspace_base, org_id) + os.makedirs(org_skills, exist_ok=True) + # 复制公共技能(global) + if not os.path.isdir(os.path.join(org_skills, "global")): + if os.path.isdir(ALL_DIR): + shutil.copytree(ALL_DIR, os.path.join(org_skills, "global")) + # 复制技能集清单(packs) + if not os.path.isdir(os.path.join(org_skills, "packs")): + if os.path.isdir(PACKS_DIR): + shutil.copytree(PACKS_DIR, os.path.join(org_skills, "packs")) + return org_skills def list_packs(): diff --git a/wwwroot/api/skill_pack.dspy b/wwwroot/api/skill_pack.dspy index f67f57a..607b2f0 100644 --- a/wwwroot/api/skill_pack.dspy +++ b/wwwroot/api/skill_pack.dspy @@ -33,10 +33,16 @@ except Exception: if not org_id: return json.dumps({"success": False, "error": "用户未关联机构"}, ensure_ascii=False) -# 技能根目录(skill_loader 的 base_dir,相对进程 cwd) -base_dir = os.environ.get("PIPELINE_SKILLS_BASE", "skills") +from pipeline_core.skill_pack import list_packs, install_pack, uninstall_pack, installed_packs, ensure_org_skills +from pipeline_service.workspace import get_workspace_base -from pipeline_core.skill_pack import list_packs, install_pack, uninstall_pack, installed_packs +# 机构技能根目录(多租户隔离:机构工作目录/skills/,不在全局应用目录) +try: + async with DBPools().sqlorContext(dbname) as sor: + ws_base = await get_workspace_base(sor) + base_dir = ensure_org_skills(ws_base, org_id) +except Exception: + base_dir = os.environ.get("PIPELINE_SKILLS_BASE", "skills") if action == 'list': return json.dumps({"success": True, "is_admin": is_admin, "packs": list_packs()},