mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-05-01 07:13:00 +00:00
fix: deduplicate coerce_skill_paths and activate pre-loaded METADATA skills
This commit is contained in:
@@ -66,7 +66,7 @@ from crewai.mcp.tool_resolver import MCPToolResolver
|
|||||||
from crewai.rag.embeddings.types import EmbedderConfig
|
from crewai.rag.embeddings.types import EmbedderConfig
|
||||||
from crewai.security.fingerprint import Fingerprint
|
from crewai.security.fingerprint import Fingerprint
|
||||||
from crewai.skills.loader import activate_skill, discover_skills
|
from crewai.skills.loader import activate_skill, discover_skills
|
||||||
from crewai.skills.models import Skill as SkillModel
|
from crewai.skills.models import DisclosureLevel, Skill as SkillModel
|
||||||
from crewai.tools.agent_tools.agent_tools import AgentTools
|
from crewai.tools.agent_tools.agent_tools import AgentTools
|
||||||
from crewai.utilities.agent_utils import (
|
from crewai.utilities.agent_utils import (
|
||||||
get_tool_names,
|
get_tool_names,
|
||||||
@@ -296,11 +296,10 @@ class Agent(BaseAgent):
|
|||||||
raise ValueError(f"Invalid Knowledge Configuration: {e!s}") from e
|
raise ValueError(f"Invalid Knowledge Configuration: {e!s}") from e
|
||||||
|
|
||||||
def set_skills(self) -> None:
|
def set_skills(self) -> None:
|
||||||
"""Resolve skill paths into loaded Skill objects.
|
"""Resolve skill paths and activate skills to INSTRUCTIONS level.
|
||||||
|
|
||||||
Path entries trigger discovery and activation. Skill entries pass through.
|
Path entries trigger discovery and activation. Pre-loaded Skill objects
|
||||||
Crew-level skill paths are merged in. Skips work when all items are
|
below INSTRUCTIONS level are activated. Crew-level skills are merged in.
|
||||||
already resolved and there are no crew-level paths to merge.
|
|
||||||
"""
|
"""
|
||||||
from crewai.crew import Crew
|
from crewai.crew import Crew
|
||||||
|
|
||||||
@@ -313,8 +312,15 @@ class Agent(BaseAgent):
|
|||||||
if not self.skills and not crew_skills:
|
if not self.skills and not crew_skills:
|
||||||
return
|
return
|
||||||
|
|
||||||
has_unresolved = self.skills and any(isinstance(s, Path) for s in self.skills)
|
needs_work = self.skills and any(
|
||||||
if not has_unresolved and not crew_skills:
|
isinstance(s, Path)
|
||||||
|
or (
|
||||||
|
isinstance(s, SkillModel)
|
||||||
|
and s.disclosure_level < DisclosureLevel.INSTRUCTIONS
|
||||||
|
)
|
||||||
|
for s in self.skills
|
||||||
|
)
|
||||||
|
if not needs_work and not crew_skills:
|
||||||
return
|
return
|
||||||
|
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ from crewai.mcp.config import MCPServerConfig
|
|||||||
from crewai.rag.embeddings.types import EmbedderConfig
|
from crewai.rag.embeddings.types import EmbedderConfig
|
||||||
from crewai.security.security_config import SecurityConfig
|
from crewai.security.security_config import SecurityConfig
|
||||||
from crewai.skills.models import Skill
|
from crewai.skills.models import Skill
|
||||||
|
from crewai.skills.validation import coerce_skill_paths as _coerce_skill_paths
|
||||||
from crewai.tools.base_tool import BaseTool, Tool
|
from crewai.tools.base_tool import BaseTool, Tool
|
||||||
from crewai.utilities.config import process_config
|
from crewai.utilities.config import process_config
|
||||||
from crewai.utilities.i18n import I18N, get_i18n
|
from crewai.utilities.i18n import I18N, get_i18n
|
||||||
@@ -225,9 +226,7 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def coerce_skill_paths(cls, v: list[Any] | None) -> list[Path | Skill] | None:
|
def coerce_skill_paths(cls, v: list[Any] | None) -> list[Path | Skill] | None:
|
||||||
"""Coerce string entries to Path objects."""
|
"""Coerce string entries to Path objects."""
|
||||||
if not v:
|
return _coerce_skill_paths(v)
|
||||||
return v
|
|
||||||
return [Path(item) if isinstance(item, str) else item for item in v]
|
|
||||||
|
|
||||||
@model_validator(mode="before")
|
@model_validator(mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ from crewai.rag.types import SearchResult
|
|||||||
from crewai.security.fingerprint import Fingerprint
|
from crewai.security.fingerprint import Fingerprint
|
||||||
from crewai.security.security_config import SecurityConfig
|
from crewai.security.security_config import SecurityConfig
|
||||||
from crewai.skills.models import Skill
|
from crewai.skills.models import Skill
|
||||||
|
from crewai.skills.validation import coerce_skill_paths as _coerce_skill_paths
|
||||||
from crewai.task import Task
|
from crewai.task import Task
|
||||||
from crewai.tasks.conditional_task import ConditionalTask
|
from crewai.tasks.conditional_task import ConditionalTask
|
||||||
from crewai.tasks.task_output import TaskOutput
|
from crewai.tasks.task_output import TaskOutput
|
||||||
@@ -302,9 +303,7 @@ class Crew(FlowTrackable, BaseModel):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def coerce_skill_paths(cls, v: list[Any] | None) -> list[Path | Skill] | None:
|
def coerce_skill_paths(cls, v: list[Any] | None) -> list[Path | Skill] | None:
|
||||||
"""Coerce string entries to Path objects, pass through Skill instances."""
|
"""Coerce string entries to Path objects, pass through Skill instances."""
|
||||||
if not v:
|
return _coerce_skill_paths(v)
|
||||||
return v
|
|
||||||
return [Path(item) if isinstance(item, str) else item for item in v]
|
|
||||||
|
|
||||||
security_config: SecurityConfig = Field(
|
security_config: SecurityConfig = Field(
|
||||||
default_factory=SecurityConfig,
|
default_factory=SecurityConfig,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from crewai.skills.models import (
|
|||||||
SkillFrontmatter,
|
SkillFrontmatter,
|
||||||
)
|
)
|
||||||
from crewai.skills.parser import SkillParseError, parse_skill_md
|
from crewai.skills.parser import SkillParseError, parse_skill_md
|
||||||
|
from crewai.skills.validation import coerce_skill_paths
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
@@ -25,6 +26,7 @@ __all__ = [
|
|||||||
"SkillFrontmatter",
|
"SkillFrontmatter",
|
||||||
"SkillParseError",
|
"SkillParseError",
|
||||||
"activate_skill",
|
"activate_skill",
|
||||||
|
"coerce_skill_paths",
|
||||||
"discover_skills",
|
"discover_skills",
|
||||||
"format_skill_context",
|
"format_skill_context",
|
||||||
"load_resources",
|
"load_resources",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ Validates skill names and directory structures per the Agent Skills standard.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
MAX_SKILL_NAME_LENGTH: int = 64
|
MAX_SKILL_NAME_LENGTH: int = 64
|
||||||
@@ -13,6 +14,20 @@ MIN_SKILL_NAME_LENGTH: int = 1
|
|||||||
SKILL_NAME_PATTERN: str = r"^[a-z0-9]+(?:-[a-z0-9]+)*$"
|
SKILL_NAME_PATTERN: str = r"^[a-z0-9]+(?:-[a-z0-9]+)*$"
|
||||||
|
|
||||||
|
|
||||||
|
def coerce_skill_paths(v: list[Any] | None) -> list[Any] | None:
|
||||||
|
"""Coerce string entries to Path objects, pass through other types.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
v: List of skill paths or Skill objects, or None.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The list with string entries converted to Path objects, or None.
|
||||||
|
"""
|
||||||
|
if not v:
|
||||||
|
return v
|
||||||
|
return [Path(item) if isinstance(item, str) else item for item in v]
|
||||||
|
|
||||||
|
|
||||||
def validate_directory_name(skill_dir: Path, skill_name: str) -> None:
|
def validate_directory_name(skill_dir: Path, skill_name: str) -> None:
|
||||||
"""Validate that a directory name matches the skill name.
|
"""Validate that a directory name matches the skill name.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user