butler/tools/toolset_profiles.py
Rama main · réplica de lectura del repositorio original.
"""Global runtime toolset profiles (complements per-project allowlists)."""
from __future__ import annotations
import os
from typing import Any
TOOLSET_FULL = "full"
TOOLSET_WECHAT_MINIMAL = "wechat_minimal"
TOOLSET_CRON = "cron"
_PROFILES: dict[str, frozenset[str] | None] = {
TOOLSET_FULL: None,
TOOLSET_WECHAT_MINIMAL: frozenset({
"read_file",
"search_files",
"list_directory",
"terminal",
"web_search",
"butler_recall",
"butler_remember",
"search_transcript",
"skills_list",
"skill_view",
"delegate_task",
"run_workflow",
"list_runtime_jobs",
}),
TOOLSET_CRON: frozenset({
"read_file",
"butler_recall",
"list_runtime_jobs",
"run_runtime_job",
"search_transcript",
}),
}
def active_toolset() -> str:
raw = os.getenv("BUTLER_TOOLSET", TOOLSET_FULL).strip().lower()
if raw in _PROFILES:
return raw
return TOOLSET_FULL
def toolset_allowed_names(toolset: str | None = None) -> frozenset[str] | None:
"""Return allowed tool names for profile, or None for no global filter."""
key = (toolset or active_toolset()).strip().lower()
return _PROFILES.get(key, _PROFILES[TOOLSET_FULL])
def filter_definitions_by_toolset(
definitions: list[dict[str, Any]],
*,
toolset: str | None = None,
) -> list[dict[str, Any]]:
from butler.execution_context import get_current_loop_role
if get_current_loop_role() == "butler":
return definitions
allowed = toolset_allowed_names(toolset)
if allowed is None:
return definitions
out: list[dict[str, Any]] = []
for spec in definitions:
fn_raw = spec.get("function")
fn = fn_raw if isinstance(fn_raw, dict) else spec
name = str(fn.get("name") or spec.get("name") or "")
if name in allowed or name.startswith("mcp_"):
out.append(spec)
return out
__all__ = [
"TOOLSET_CRON",
"TOOLSET_FULL",
"TOOLSET_WECHAT_MINIMAL",
"active_toolset",
"filter_definitions_by_toolset",
"toolset_allowed_names",
]