fix: skills 路由两个小修复

skills.py:
- update_curator_config 直接 await get_curator_config() 报错
  ('Depends' object has no attribute 'skill_evolution')。FastAPI 路由
  默认参数 Depends(...) 只在请求时被框架解析,直接调用拿到的是依赖标记。
  抽出 _build_curator_config_response(config) 让 GET 和 PUT 共用。

skill_manage_tool.py:
- 模型把 action='list' 当成可用动作误调 skill_manage 报
  Unsupported action。补两个 read-only 别名 list/view,分别委派到
  skill_list_tool 和 skill_view_tool,docstring 同步更新。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xuhdandClaude Opus 4.7 committed 2026-05-14 15:43:26 +08:00
1 parent c63811367e
commit f5bc02d598
2 files changed
+37 -17

No files matched your search

@@ -608,21 +608,26 @@ class CuratorLogsResponse(BaseModel):
last_run_at: str | None
def _build_curator_config_response(config: AppConfig) -> CuratorConfigResponse:
"""Pure helper so both the GET route and the post-PUT refresh share one shape."""
evo = config.skill_evolution
return CuratorConfigResponse(
enabled=evo.enabled,
moderation_model_name=evo.moderation_model_name,
curator=CuratorSubConfig(
enabled=evo.curator.enabled,
interval_hours=evo.curator.interval_hours,
stale_after_days=evo.curator.stale_after_days,
archive_after_days=evo.curator.archive_after_days,
),
)
@router.get("/curator/config", response_model=CuratorConfigResponse, summary="Get Skill Evolution Config")
async def get_curator_config(config: AppConfig = Depends(get_config)) -> CuratorConfigResponse:
"""Return the current effective skill_evolution config (yaml defaults + skill_evolution.json overrides)."""
try:
evo = config.skill_evolution
return CuratorConfigResponse(
enabled=evo.enabled,
moderation_model_name=evo.moderation_model_name,
curator=CuratorSubConfig(
enabled=evo.curator.enabled,
interval_hours=evo.curator.interval_hours,
stale_after_days=evo.curator.stale_after_days,
archive_after_days=evo.curator.archive_after_days,
),
)
return _build_curator_config_response(config)
except Exception as e:
logger.error("Failed to read curator config: %s", e, exc_info=True)
raise HTTPException(status_code=500, detail=f"Failed to read curator config: {str(e)}")
@@ -636,6 +641,7 @@ async def update_curator_config(body: CuratorConfigRequest) -> CuratorConfigResp
fields are rejected by the Pydantic model_validate call.
"""
try:
from deerflow.config.app_config import get_app_config, reset_app_config
from deerflow.config.skill_evolution_config import SkillEvolutionConfig
from deerflow.config.skill_evolution_runtime import save_overrides
@@ -644,13 +650,12 @@ async def update_curator_config(body: CuratorConfigRequest) -> CuratorConfigResp
SkillEvolutionConfig.model_validate(payload)
save_overrides(payload)
# Force the next get_app_config() call to pick up the new overrides.
# mtime check inside get_app_config() will trigger reload automatically,
# but resetting here makes the response below reflect the new state.
from deerflow.config.app_config import reset_app_config
# Reset cache so the next get_app_config() picks up the new overrides
# (the mtime check would catch it too, but resetting makes the response
# below deterministically reflect the new state).
reset_app_config()
return await get_curator_config()
fresh_config = get_app_config()
return _build_curator_config_response(fresh_config)
except HTTPException:
raise
except Exception as e:
@@ -78,6 +78,10 @@ async def _skill_manage_impl(
Args:
action: One of create, patch, edit, delete, write_file, remove_file.
For listing or viewing skills, prefer the dedicated
``skill_list`` / ``skill_view`` tools — but ``action='list'``
and ``action='view'`` are accepted here as convenience
aliases that delegate to those tools.
name: Skill name in hyphen-case.
content: New file content for create, edit, or write_file.
path: Supporting file path for write_file or remove_file.
@@ -85,6 +89,17 @@ async def _skill_manage_impl(
replace: Replacement text for patch.
expected_count: Optional expected number of replacements for patch.
"""
# Read-only aliases — delegate to the dedicated tools so a model that
# over-uses skill_manage doesn't trip on Unsupported action errors.
if action == "list":
from deerflow.tools.builtins.skill_tools import skill_list_tool
return await _to_thread(skill_list_tool.invoke, {})
if action == "view":
from deerflow.tools.builtins.skill_tools import skill_view_tool
return await _to_thread(skill_view_tool.invoke, {"name": name})
name = SkillStorage.validate_skill_name(name)
lock = _get_lock(name)
thread_id = _get_thread_id(runtime)