diff --git a/packages/derisk-core/src/derisk/agent/expand/react_master_agent/react_master_agent.py b/packages/derisk-core/src/derisk/agent/expand/react_master_agent/react_master_agent.py index 83dc570b..3c4b4889 100644 --- a/packages/derisk-core/src/derisk/agent/expand/react_master_agent/react_master_agent.py +++ b/packages/derisk-core/src/derisk/agent/expand/react_master_agent/react_master_agent.py @@ -14,6 +14,8 @@ from typing import Any, Dict, List, Optional, Tuple, Callable, Awaitable from derisk._private.pydantic import Field, PrivateAttr +from derisk.configs.model_config import DATA_DIR +import os from derisk.agent import ( ActionOutput, Agent, @@ -1147,6 +1149,21 @@ async def var_available_knowledges(instance): async def var_skills(instance): logger.info("注入技能资源") + # Sandbox mode: skill_dir comes from the sandbox client + # (e.g. /mnt/derisk/skills set in [sandbox].skill_dir of the toml). + # Local mode: default to DATA_DIR/skill (pilot/data/skill). + sandbox_skill_dir: Optional[str] = None + if instance and getattr(instance, "sandbox_manager", None): + sb_client = getattr(instance.sandbox_manager, "client", None) + if sb_client: + sandbox_skill_dir = getattr(sb_client, "skill_dir", None) + + local_skill_dir = os.path.join(DATA_DIR, "skill") + logger.info( + f"var_skills: sandbox_skill_dir={sandbox_skill_dir!r}, " + f"local_skill_dir={local_skill_dir!r}" + ) + prompts = "" for k, v in self.resource_map.items(): if isinstance(v[0], AgentSkillResource): @@ -1159,11 +1176,34 @@ async def var_skills(instance): skill_meta = skill_item.skill_meta(mode) if not skill_meta: continue - skill_path = ( - skill_item._skill.parent_folder - if hasattr(skill_item, "_skill") and skill_item._skill - else skill_meta.path - ) + + # skill_code is the UUID (DeriskSkillResource) or dir name. + skill_code = getattr( + skill_item, "_skill_code", None + ) or getattr(skill_item, "skill_code", None) + if not skill_code and skill_meta.path: + skill_code = os.path.basename(skill_meta.path) + + # Use sandbox path only when the skill directory actually + # exists inside the sandbox; otherwise fall back to local. + if os.path.isdir(os.path.join(sandbox_skill_dir, skill_code)): + skill_path = os.path.join(sandbox_skill_dir,skill_code) + else: + skill_path = os.path.join(local_skill_dir,skill_item._skill_path) + + + # if skill_code and sandbox_skill_dir: + # sandbox_path = os.path.join(sandbox_skill_dir, skill_code) + # skill_path = ( + # sandbox_path + # if os.path.exists(sandbox_path) + # else os.path.join(local_skill_dir, skill_code) + # ) + # elif skill_code: + # skill_path = os.path.join(local_skill_dir, skill_code) + # else: + # skill_path = skill_meta.path + prompts += ( f"- " f"{skill_meta.name}" diff --git a/packages/derisk-serve/src/derisk_serve/skill/service/service.py b/packages/derisk-serve/src/derisk_serve/skill/service/service.py index 268f6e47..f050552b 100644 --- a/packages/derisk-serve/src/derisk_serve/skill/service/service.py +++ b/packages/derisk-serve/src/derisk_serve/skill/service/service.py @@ -84,6 +84,16 @@ def create(self, request: SkillRequest) -> SkillResponse: request.skill_code = str(uuid.uuid4()) + # Set default path to skill_dir/skill_code if not provided + if not request.path: + project_skill_dir = self._serve_config.get_project_skill_dir() + request.path = os.path.join(project_skill_dir, request.skill_code) + + logger.info( + f"Skill '{request.name}' path is empty, " + f"using default path '{request.path}'" + ) + existing_skill = self.dao.get_one({"skill_code": request.skill_code}) if existing_skill: logger.info(f"Skill {request.skill_code} already exists, updating instead") @@ -109,6 +119,21 @@ def update(self, request: SkillRequest) -> SkillResponse: request_dict.pop("gmt_created", None) request_dict.pop("gmt_modified", None) + # Set default path if not provided + if not request_dict.get("path"): + existing = self.dao.get_one({"skill_code": request.skill_code}) + if existing and existing.path: + request_dict["path"] = existing.path + else: + project_skill_dir = self._serve_config.get_project_skill_dir() + request_dict["path"] = os.path.join( + project_skill_dir, request.skill_code + ) + logger.info( + f"Skill '{request.skill_code}' update path is empty, " + f"using default path '{request_dict['path']}'" + ) + return self.dao.update(query_request, request_dict) def get(self, request: SkillRequest) -> Optional[SkillResponse]: