1818
1919import asyncio
2020from pathlib import Path
21+ from typing import Any
2122
22- from google .adk .skills import Frontmatter , load_skill_from_dir
23+ from google .adk .skills import Frontmatter
2324from google .adk .skills import Skill as ADKSkill
25+ from google .adk .skills import models as adk_skill_models
26+ from google .adk .skills import _utils as adk_skill_utils
2427
2528try :
2629 from google .adk .skills import SkillRegistry
@@ -75,7 +78,7 @@ async def get_skill(self, *, name: str) -> ADKSkill:
7578 skill ,
7679 cache_dir = self .cache_dir ,
7780 )
78- return await asyncio .to_thread (load_skill_from_dir , skill_dir )
81+ return await asyncio .to_thread (load_compatible_adk_skill_from_dir , skill_dir )
7982
8083 def search_tool_description (self ) -> str | None :
8184 return (
@@ -98,3 +101,60 @@ def _find_skill(
98101 if skill .name == name :
99102 return skill
100103 return None
104+
105+
106+ def load_compatible_adk_skill_from_dir (skill_dir : str | Path ) -> ADKSkill :
107+ """Load an ADK skill while tolerating community frontmatter extensions."""
108+ resolved_skill_dir = Path (skill_dir ).resolve ()
109+ parsed , body , _ = adk_skill_utils ._parse_skill_md (resolved_skill_dir )
110+ frontmatter = _compatible_frontmatter (parsed )
111+
112+ if resolved_skill_dir .name != frontmatter .name :
113+ raise ValueError (
114+ f"Skill name '{ frontmatter .name } ' does not match directory"
115+ f" name '{ resolved_skill_dir .name } '."
116+ )
117+
118+ references = adk_skill_utils ._load_dir (resolved_skill_dir / "references" )
119+ assets = adk_skill_utils ._load_dir (resolved_skill_dir / "assets" )
120+ raw_scripts = adk_skill_utils ._load_dir (resolved_skill_dir / "scripts" )
121+ scripts = {
122+ name : adk_skill_models .Script (src = content )
123+ for name , content in raw_scripts .items ()
124+ }
125+
126+ return adk_skill_models .Skill (
127+ frontmatter = frontmatter ,
128+ instructions = body ,
129+ resources = adk_skill_models .Resources (
130+ references = references ,
131+ assets = assets ,
132+ scripts = scripts ,
133+ ),
134+ )
135+
136+
137+ def _compatible_frontmatter (parsed : dict [str , Any ]) -> Frontmatter :
138+ metadata = parsed .get ("metadata" )
139+ if not isinstance (metadata , dict ):
140+ metadata = {}
141+ else :
142+ metadata = dict (metadata )
143+
144+ frontmatter_data : dict [str , Any ] = {
145+ "name" : parsed .get ("name" ),
146+ "description" : parsed .get ("description" ),
147+ "metadata" : metadata ,
148+ }
149+ for key in ("license" , "compatibility" , "allowed-tools" ):
150+ value = parsed .get (key )
151+ if isinstance (value , str ):
152+ frontmatter_data [key ] = value
153+ elif value is not None :
154+ metadata .setdefault (key , value )
155+
156+ for key , value in parsed .items ():
157+ if key not in frontmatter_data and key not in metadata :
158+ metadata [key ] = value
159+
160+ return Frontmatter .model_validate (frontmatter_data )
0 commit comments