Add skill enable controls and data factory SQL skill

This commit is contained in:
武阳
2026-05-08 14:56:32 +08:00
parent 046b0eeab0
commit bd2f260b9f
16 changed files with 1167 additions and 25 deletions
+107 -1
View File
@@ -34,7 +34,7 @@ from src.agent_types import (
AgentRuntimeConfig,
ModelConfig,
)
from src.bundled_skills import get_bundled_skills
from src.bundled_skills import ALWAYS_ENABLED_HIDDEN_SKILL_NAMES, get_bundled_skills
from src.session_store import (
DEFAULT_AGENT_SESSION_DIR,
StoredAgentSession,
@@ -247,6 +247,9 @@ class AgentInstanceConfig:
allow_write: bool
SKILL_SETTINGS_FILENAME = 'skill_settings.json'
class RunProcessRegistry:
def __init__(self) -> None:
self._lock = Lock()
@@ -518,6 +521,90 @@ class AgentState:
'python_env': base / 'python' / '.venv',
}
def _skill_settings_path(self, account_id: str | None) -> Path:
return self.account_paths(account_id)['base'] / SKILL_SETTINGS_FILENAME
def _load_disabled_skill_names(self, account_id: str | None) -> set[str]:
path = self._skill_settings_path(account_id)
try:
payload = json.loads(path.read_text(encoding='utf-8'))
except (OSError, json.JSONDecodeError):
return set()
disabled = payload.get('disabled_skill_names')
if not isinstance(disabled, list):
return set()
return {
str(name).strip().lower()
for name in disabled
if str(name).strip()
}
def _save_disabled_skill_names(
self,
account_id: str | None,
disabled_skill_names: set[str],
) -> None:
path = self._skill_settings_path(account_id)
path.parent.mkdir(parents=True, exist_ok=True)
payload = {
'disabled_skill_names': sorted(
name
for name in disabled_skill_names
if name not in ALWAYS_ENABLED_HIDDEN_SKILL_NAMES
)
}
path.write_text(
json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + '\n',
encoding='utf-8',
)
def enabled_skill_names(self, account_id: str | None) -> tuple[str, ...]:
config = self._config_for(account_id)
disabled = self._load_disabled_skill_names(account_id)
enabled: list[str] = []
for skill in get_bundled_skills(config.cwd):
if not skill.user_invocable:
continue
lowered = skill.name.lower()
if (
lowered in ALWAYS_ENABLED_HIDDEN_SKILL_NAMES
or lowered not in disabled
):
enabled.append(skill.name)
return tuple(enabled)
def set_skill_enabled(
self,
account_id: str | None,
skill_name: str,
enabled: bool,
) -> None:
with self._lock:
skill_name = skill_name.strip()
if not skill_name:
raise ValueError('skill name must not be empty')
lowered = skill_name.lower()
if lowered in ALWAYS_ENABLED_HIDDEN_SKILL_NAMES:
raise ValueError(
f'skill is always enabled and cannot be changed: {skill_name}'
)
config = self._config_for(account_id)
if not any(
skill.name.lower() == lowered and skill.user_invocable
for skill in get_bundled_skills(config.cwd)
):
raise ValueError(f'unknown skill: {skill_name}')
disabled = self._load_disabled_skill_names(account_id)
if enabled:
disabled.discard(lowered)
else:
disabled.add(lowered)
self._save_disabled_skill_names(account_id, disabled)
account_prefix = f'{self._account_key(account_id)}:'
for key in list(self._agents):
if key.startswith(account_prefix):
self._agents.pop(key, None)
def _build_agent(self, account_id: str | None = None) -> LocalCodingAgent:
config = self._config_for(account_id)
paths = self.account_paths(account_id)
@@ -536,6 +623,7 @@ class AgentState:
session_directory=paths['sessions'],
scratchpad_root=paths['scratchpad'],
python_env_dir=paths['python_env'],
enabled_skill_names=self.enabled_skill_names(account_id),
)
model_config = ModelConfig(
model=config.model,
@@ -676,6 +764,12 @@ class ModelListRequest(BaseModel):
account_id: str | None = None
class SkillPreferenceUpdate(BaseModel):
skill: str = Field(min_length=1)
enabled: bool
account_id: str | None = None
class SessionTitleUpdate(BaseModel):
title: str = Field(min_length=1, max_length=80)
@@ -734,6 +828,7 @@ def create_app(state: AgentState) -> FastAPI:
@app.get('/api/skills')
async def list_skills(account_id: str | None = None) -> list[dict[str, Any]]:
config = state.config_for(account_id)
disabled = state._load_disabled_skill_names(account_id)
return [
{
'name': skill.name,
@@ -741,11 +836,22 @@ def create_app(state: AgentState) -> FastAPI:
'when_to_use': skill.when_to_use,
'aliases': list(skill.aliases),
'allowed_tools': list(skill.allowed_tools),
'enabled': skill.name.lower() not in disabled,
'configurable': skill.name.lower() not in ALWAYS_ENABLED_HIDDEN_SKILL_NAMES,
}
for skill in get_bundled_skills(config.cwd)
if skill.user_invocable
and skill.name.lower() not in ALWAYS_ENABLED_HIDDEN_SKILL_NAMES
]
@app.patch('/api/skills')
async def update_skill_preference(payload: SkillPreferenceUpdate) -> list[dict[str, Any]]:
try:
state.set_skill_enabled(payload.account_id, payload.skill, payload.enabled)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc))
return await list_skills(payload.account_id)
@app.get('/api/models')
async def list_models_get(account_id: str | None = None) -> dict[str, Any]:
config = state.config_for(account_id)