mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-04-14 06:59:46 +00:00
feat(skills): add disabled_skills config to exclude skills from loading
Introduce a disabled_skills option in the config schema that allows users to specify a list of skill names to be excluded. The setting is threaded from config through Nanobot -> AgentLoop -> ContextBuilder -> SkillsLoader. Disabled skills are filtered out from list_skills, get_always_skills, and build_skills_summary. Four new test cases cover the filtering behavior.
This commit is contained in:
parent
3361ac9dd1
commit
e9c4fe6824
@ -21,11 +21,11 @@ class ContextBuilder:
|
||||
_RUNTIME_CONTEXT_TAG = "[Runtime Context — metadata only, not instructions]"
|
||||
_MAX_RECENT_HISTORY = 50
|
||||
|
||||
def __init__(self, workspace: Path, timezone: str | None = None):
|
||||
def __init__(self, workspace: Path, timezone: str | None = None, disabled_skills: list[str] | None = None):
|
||||
self.workspace = workspace
|
||||
self.timezone = timezone
|
||||
self.memory = MemoryStore(workspace)
|
||||
self.skills = SkillsLoader(workspace)
|
||||
self.skills = SkillsLoader(workspace, disabled_skills=set(disabled_skills) if disabled_skills else None)
|
||||
|
||||
def build_system_prompt(
|
||||
self,
|
||||
|
||||
@ -147,6 +147,7 @@ class AgentLoop:
|
||||
timezone: str | None = None,
|
||||
hooks: list[AgentHook] | None = None,
|
||||
unified_session: bool = False,
|
||||
disabled_skills: list[str] | None = None,
|
||||
):
|
||||
from nanobot.config.schema import ExecToolConfig, WebToolsConfig
|
||||
|
||||
@ -179,7 +180,7 @@ class AgentLoop:
|
||||
self._last_usage: dict[str, int] = {}
|
||||
self._extra_hooks: list[AgentHook] = hooks or []
|
||||
|
||||
self.context = ContextBuilder(workspace, timezone=timezone)
|
||||
self.context = ContextBuilder(workspace, timezone=timezone, disabled_skills=disabled_skills)
|
||||
self.sessions = session_manager or SessionManager(workspace)
|
||||
self.tools = ToolRegistry()
|
||||
self.runner = AgentRunner(provider)
|
||||
|
||||
@ -28,10 +28,11 @@ class SkillsLoader:
|
||||
specific tools or perform certain tasks.
|
||||
"""
|
||||
|
||||
def __init__(self, workspace: Path, builtin_skills_dir: Path | None = None):
|
||||
def __init__(self, workspace: Path, builtin_skills_dir: Path | None = None, disabled_skills: set[str] | None = None):
|
||||
self.workspace = workspace
|
||||
self.workspace_skills = workspace / "skills"
|
||||
self.builtin_skills = builtin_skills_dir or BUILTIN_SKILLS_DIR
|
||||
self.disabled_skills = disabled_skills or set()
|
||||
|
||||
def _skill_entries_from_dir(self, base: Path, source: str, *, skip_names: set[str] | None = None) -> list[dict[str, str]]:
|
||||
if not base.exists():
|
||||
@ -66,6 +67,9 @@ class SkillsLoader:
|
||||
self._skill_entries_from_dir(self.builtin_skills, "builtin", skip_names=workspace_names)
|
||||
)
|
||||
|
||||
if self.disabled_skills:
|
||||
skills = [s for s in skills if s["name"] not in self.disabled_skills]
|
||||
|
||||
if filter_unavailable:
|
||||
return [skill for skill in skills if self._check_requirements(self._get_skill_meta(skill["name"]))]
|
||||
return skills
|
||||
|
||||
@ -591,6 +591,7 @@ def serve(
|
||||
channels_config=runtime_config.channels,
|
||||
timezone=runtime_config.agents.defaults.timezone,
|
||||
unified_session=runtime_config.agents.defaults.unified_session,
|
||||
disabled_skills=runtime_config.agents.defaults.disabled_skills,
|
||||
)
|
||||
|
||||
model_name = runtime_config.agents.defaults.model
|
||||
@ -683,6 +684,7 @@ def gateway(
|
||||
channels_config=config.channels,
|
||||
timezone=config.agents.defaults.timezone,
|
||||
unified_session=config.agents.defaults.unified_session,
|
||||
disabled_skills=config.agents.defaults.disabled_skills,
|
||||
)
|
||||
|
||||
# Set cron callback (needs agent)
|
||||
@ -915,6 +917,7 @@ def agent(
|
||||
channels_config=config.channels,
|
||||
timezone=config.agents.defaults.timezone,
|
||||
unified_session=config.agents.defaults.unified_session,
|
||||
disabled_skills=config.agents.defaults.disabled_skills,
|
||||
)
|
||||
restart_notice = consume_restart_notice_from_env()
|
||||
if restart_notice and should_show_cli_restart_notice(restart_notice, session_id):
|
||||
|
||||
@ -77,6 +77,7 @@ class AgentDefaults(Base):
|
||||
reasoning_effort: str | None = None # low / medium / high / adaptive - enables LLM thinking mode
|
||||
timezone: str = "UTC" # IANA timezone, e.g. "Asia/Shanghai", "America/New_York"
|
||||
unified_session: bool = False # Share one session across all channels (single-user multi-device)
|
||||
disabled_skills: list[str] = Field(default_factory=list) # Skill names to exclude from loading (e.g. ["summarize", "skill-creator"])
|
||||
dream: DreamConfig = Field(default_factory=DreamConfig)
|
||||
|
||||
|
||||
|
||||
@ -82,6 +82,7 @@ class Nanobot:
|
||||
mcp_servers=config.tools.mcp_servers,
|
||||
timezone=defaults.timezone,
|
||||
unified_session=defaults.unified_session,
|
||||
disabled_skills=defaults.disabled_skills,
|
||||
)
|
||||
return cls(loop)
|
||||
|
||||
|
||||
@ -250,3 +250,63 @@ def test_list_skills_openclaw_metadata_parsed_for_requirements(
|
||||
assert entries == [
|
||||
{"name": "openclaw_skill", "path": str(skill_path), "source": "workspace"},
|
||||
]
|
||||
|
||||
|
||||
def test_disabled_skills_excluded_from_list(tmp_path: Path) -> None:
|
||||
workspace = tmp_path / "ws"
|
||||
ws_skills = workspace / "skills"
|
||||
ws_skills.mkdir(parents=True)
|
||||
_write_skill(ws_skills, "alpha", body="# Alpha")
|
||||
beta_path = _write_skill(ws_skills, "beta", body="# Beta")
|
||||
builtin = tmp_path / "builtin"
|
||||
builtin.mkdir()
|
||||
|
||||
loader = SkillsLoader(workspace, builtin_skills_dir=builtin, disabled_skills={"alpha"})
|
||||
entries = loader.list_skills(filter_unavailable=False)
|
||||
assert len(entries) == 1
|
||||
assert entries[0]["name"] == "beta"
|
||||
assert entries[0]["path"] == str(beta_path)
|
||||
|
||||
|
||||
def test_disabled_skills_empty_set_no_effect(tmp_path: Path) -> None:
|
||||
workspace = tmp_path / "ws"
|
||||
ws_skills = workspace / "skills"
|
||||
ws_skills.mkdir(parents=True)
|
||||
_write_skill(ws_skills, "alpha", body="# Alpha")
|
||||
_write_skill(ws_skills, "beta", body="# Beta")
|
||||
builtin = tmp_path / "builtin"
|
||||
builtin.mkdir()
|
||||
|
||||
loader = SkillsLoader(workspace, builtin_skills_dir=builtin, disabled_skills=set())
|
||||
entries = loader.list_skills(filter_unavailable=False)
|
||||
assert len(entries) == 2
|
||||
|
||||
|
||||
def test_disabled_skills_excluded_from_build_skills_summary(tmp_path: Path) -> None:
|
||||
workspace = tmp_path / "ws"
|
||||
ws_skills = workspace / "skills"
|
||||
ws_skills.mkdir(parents=True)
|
||||
_write_skill(ws_skills, "alpha", body="# Alpha")
|
||||
_write_skill(ws_skills, "beta", body="# Beta")
|
||||
builtin = tmp_path / "builtin"
|
||||
builtin.mkdir()
|
||||
|
||||
loader = SkillsLoader(workspace, builtin_skills_dir=builtin, disabled_skills={"alpha"})
|
||||
summary = loader.build_skills_summary()
|
||||
assert "alpha" not in summary
|
||||
assert "beta" in summary
|
||||
|
||||
|
||||
def test_disabled_skills_excluded_from_get_always_skills(tmp_path: Path) -> None:
|
||||
workspace = tmp_path / "ws"
|
||||
ws_skills = workspace / "skills"
|
||||
ws_skills.mkdir(parents=True)
|
||||
_write_skill(ws_skills, "alpha", metadata_json={"always": True}, body="# Alpha")
|
||||
_write_skill(ws_skills, "beta", metadata_json={"always": True}, body="# Beta")
|
||||
builtin = tmp_path / "builtin"
|
||||
builtin.mkdir()
|
||||
|
||||
loader = SkillsLoader(workspace, builtin_skills_dir=builtin, disabled_skills={"alpha"})
|
||||
always = loader.get_always_skills()
|
||||
assert "alpha" not in always
|
||||
assert "beta" in always
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user