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:
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
|
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")
|
@router.get("/curator/config", response_model=CuratorConfigResponse, summary="Get Skill Evolution Config")
|
||||||
async def get_curator_config(config: AppConfig = Depends(get_config)) -> CuratorConfigResponse:
|
async def get_curator_config(config: AppConfig = Depends(get_config)) -> CuratorConfigResponse:
|
||||||
"""Return the current effective skill_evolution config (yaml defaults + skill_evolution.json overrides)."""
|
"""Return the current effective skill_evolution config (yaml defaults + skill_evolution.json overrides)."""
|
||||||
try:
|
try:
|
||||||
evo = config.skill_evolution
|
return _build_curator_config_response(config)
|
||||||
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,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("Failed to read curator config: %s", e, exc_info=True)
|
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)}")
|
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.
|
fields are rejected by the Pydantic model_validate call.
|
||||||
"""
|
"""
|
||||||
try:
|
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_config import SkillEvolutionConfig
|
||||||
from deerflow.config.skill_evolution_runtime import save_overrides
|
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)
|
SkillEvolutionConfig.model_validate(payload)
|
||||||
save_overrides(payload)
|
save_overrides(payload)
|
||||||
|
|
||||||
# Force the next get_app_config() call to pick up the new overrides.
|
# Reset cache so the next get_app_config() picks up the new overrides
|
||||||
# mtime check inside get_app_config() will trigger reload automatically,
|
# (the mtime check would catch it too, but resetting makes the response
|
||||||
# but resetting here makes the response below reflect the new state.
|
# below deterministically reflect the new state).
|
||||||
from deerflow.config.app_config import reset_app_config
|
|
||||||
reset_app_config()
|
reset_app_config()
|
||||||
|
fresh_config = get_app_config()
|
||||||
return await get_curator_config()
|
return _build_curator_config_response(fresh_config)
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -78,6 +78,10 @@ async def _skill_manage_impl(
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
action: One of create, patch, edit, delete, write_file, remove_file.
|
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.
|
name: Skill name in hyphen-case.
|
||||||
content: New file content for create, edit, or write_file.
|
content: New file content for create, edit, or write_file.
|
||||||
path: Supporting file path for write_file or remove_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.
|
replace: Replacement text for patch.
|
||||||
expected_count: Optional expected number of replacements 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)
|
name = SkillStorage.validate_skill_name(name)
|
||||||
lock = _get_lock(name)
|
lock = _get_lock(name)
|
||||||
thread_id = _get_thread_id(runtime)
|
thread_id = _get_thread_id(runtime)
|
||||||
|
|||||||
Reference in new issue
Block a user