mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 07:27:15 +08:00
fix: 修复技能加载时误读目录导致 IsADirectoryError 的问题
This commit is contained in:
@@ -78,13 +78,15 @@ class SkillsStateUpdate(TypedDict):
|
|||||||
|
|
||||||
|
|
||||||
def _parse_skill_metadata( # noqa: C901
|
def _parse_skill_metadata( # noqa: C901
|
||||||
content: str,
|
content: str,
|
||||||
skill_path: str,
|
skill_path: str,
|
||||||
skill_id: str,
|
skill_id: str,
|
||||||
) -> SkillMetadata | None:
|
) -> SkillMetadata | None:
|
||||||
"""从 SKILL.md 内容中解析 YAML 前言并验证元数据。"""
|
"""从 SKILL.md 内容中解析 YAML 前言并验证元数据。"""
|
||||||
if len(content) > MAX_SKILL_FILE_SIZE:
|
if len(content) > MAX_SKILL_FILE_SIZE:
|
||||||
logger.warning("Skipping %s: content too large (%d bytes)", skill_path, len(content))
|
logger.warning(
|
||||||
|
"Skipping %s: content too large (%d bytes)", skill_path, len(content)
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# 匹配 --- 分隔的 YAML 前言
|
# 匹配 --- 分隔的 YAML 前言
|
||||||
@@ -110,7 +112,9 @@ def _parse_skill_metadata( # noqa: C901
|
|||||||
name = str(frontmatter_data.get("name", "")).strip()
|
name = str(frontmatter_data.get("name", "")).strip()
|
||||||
description = str(frontmatter_data.get("description", "")).strip()
|
description = str(frontmatter_data.get("description", "")).strip()
|
||||||
if not name or not description:
|
if not name or not description:
|
||||||
logger.warning("Skipping %s: missing required 'name' or 'description'", skill_path)
|
logger.warning(
|
||||||
|
"Skipping %s: missing required 'name' or 'description'", skill_path
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
description_str = description
|
description_str = description
|
||||||
if len(description_str) > MAX_SKILL_DESCRIPTION_LENGTH:
|
if len(description_str) > MAX_SKILL_DESCRIPTION_LENGTH:
|
||||||
@@ -161,8 +165,8 @@ def _parse_skill_metadata( # noqa: C901
|
|||||||
|
|
||||||
|
|
||||||
def _validate_metadata(
|
def _validate_metadata(
|
||||||
raw: object,
|
raw: object,
|
||||||
skill_path: str,
|
skill_path: str,
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
"""验证并规范化 YAML 前言中的元数据字段,确保为 dict[str, str] 类型。"""
|
"""验证并规范化 YAML 前言中的元数据字段,确保为 dict[str, str] 类型。"""
|
||||||
if not isinstance(raw, dict):
|
if not isinstance(raw, dict):
|
||||||
@@ -188,7 +192,7 @@ def _format_skill_annotations(skill: SkillMetadata) -> str:
|
|||||||
|
|
||||||
async def _alist_skills(source_path: AsyncPath) -> list[SkillMetadata]:
|
async def _alist_skills(source_path: AsyncPath) -> list[SkillMetadata]:
|
||||||
"""异步列出指定路径下的所有技能。
|
"""异步列出指定路径下的所有技能。
|
||||||
|
|
||||||
扫描包含 SKILL.md 的目录并解析其元数据。
|
扫描包含 SKILL.md 的目录并解析其元数据。
|
||||||
"""
|
"""
|
||||||
skills: list[SkillMetadata] = []
|
skills: list[SkillMetadata] = []
|
||||||
@@ -206,7 +210,7 @@ async def _alist_skills(source_path: AsyncPath) -> list[SkillMetadata]:
|
|||||||
for skill_path in skill_dirs:
|
for skill_path in skill_dirs:
|
||||||
skill_md_path = skill_path / "SKILL.md"
|
skill_md_path = skill_path / "SKILL.md"
|
||||||
|
|
||||||
skill_content = await skill_path.read_text(encoding="utf-8")
|
skill_content = await skill_md_path.read_text(encoding="utf-8")
|
||||||
|
|
||||||
# 解析元数据
|
# 解析元数据
|
||||||
skill_metadata = _parse_skill_metadata(
|
skill_metadata = _parse_skill_metadata(
|
||||||
@@ -283,7 +287,7 @@ Remember: Skills make you more capable and consistent. When in doubt, check if a
|
|||||||
|
|
||||||
class SkillsMiddleware(AgentMiddleware[SkillsState, ContextT, ResponseT]): # noqa
|
class SkillsMiddleware(AgentMiddleware[SkillsState, ContextT, ResponseT]): # noqa
|
||||||
"""加载并向系统提示词注入 Agent Skill 的中间件。
|
"""加载并向系统提示词注入 Agent Skill 的中间件。
|
||||||
|
|
||||||
按源顺序加载 Skill,后加载的会覆盖重名的。
|
按源顺序加载 Skill,后加载的会覆盖重名的。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -334,18 +338,17 @@ class SkillsMiddleware(AgentMiddleware[SkillsState, ContextT, ResponseT]): # no
|
|||||||
skills_list=skills_list,
|
skills_list=skills_list,
|
||||||
)
|
)
|
||||||
|
|
||||||
new_system_message = append_to_system_message(request.system_message, skills_section)
|
new_system_message = append_to_system_message(
|
||||||
|
request.system_message, skills_section
|
||||||
|
)
|
||||||
|
|
||||||
return request.override(system_message=new_system_message)
|
return request.override(system_message=new_system_message)
|
||||||
|
|
||||||
async def abefore_agent( # noqa
|
async def abefore_agent( # noqa
|
||||||
self,
|
self, state: SkillsState, runtime: Runtime, config: RunnableConfig
|
||||||
state: SkillsState,
|
|
||||||
runtime: Runtime,
|
|
||||||
config: RunnableConfig
|
|
||||||
) -> SkillsStateUpdate | None: # ty: ignore[invalid-method-override]
|
) -> SkillsStateUpdate | None: # ty: ignore[invalid-method-override]
|
||||||
"""在 Agent 执行前异步加载技能元数据。
|
"""在 Agent 执行前异步加载技能元数据。
|
||||||
|
|
||||||
每个会话仅加载一次。若 state 中已有则跳过。
|
每个会话仅加载一次。若 state 中已有则跳过。
|
||||||
"""
|
"""
|
||||||
# 如果 state 中已存在元数据则跳过
|
# 如果 state 中已存在元数据则跳过
|
||||||
@@ -368,9 +371,11 @@ class SkillsMiddleware(AgentMiddleware[SkillsState, ContextT, ResponseT]): # no
|
|||||||
return SkillsStateUpdate(skills_metadata=skills)
|
return SkillsStateUpdate(skills_metadata=skills)
|
||||||
|
|
||||||
async def awrap_model_call(
|
async def awrap_model_call(
|
||||||
self,
|
self,
|
||||||
request: ModelRequest[ContextT],
|
request: ModelRequest[ContextT],
|
||||||
handler: Callable[[ModelRequest[ContextT]], Awaitable[ModelResponse[ResponseT]]],
|
handler: Callable[
|
||||||
|
[ModelRequest[ContextT]], Awaitable[ModelResponse[ResponseT]]
|
||||||
|
],
|
||||||
) -> ModelResponse[ResponseT]:
|
) -> ModelResponse[ResponseT]:
|
||||||
"""在模型调用时注入技能文档。"""
|
"""在模型调用时注入技能文档。"""
|
||||||
modified_request = self.modify_request(request)
|
modified_request = self.modify_request(request)
|
||||||
|
|||||||
Reference in New Issue
Block a user