Coverage for src / agent / skills / documentation_index.py: 100%

23 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2025-12-11 14:30 +0000

1# Copyright 2025-2026 Microsoft Corporation 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); 

4# you may not use this file except in compliance with the License. 

5# You may obtain a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, 

11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

12# See the License for the specific language governing permissions and 

13# limitations under the License. 

14 

15"""In-memory skill documentation index for runtime context injection. 

16 

17This module provides SkillDocumentationIndex for runtime documentation management, 

18separate from SkillRegistry which handles persistent install metadata. 

19""" 

20 

21from dataclasses import dataclass 

22from typing import Any 

23 

24from agent.skills.manifest import SkillManifest 

25 

26 

27@dataclass 

28class SkillDocumentation: 

29 """Runtime documentation for a single skill.""" 

30 

31 name: str 

32 brief_description: str 

33 triggers: dict[str, list[str]] # {keywords: [], verbs: [], patterns: []} 

34 instructions: str 

35 

36 def to_dict(self) -> dict[str, Any]: 

37 """Convert to dictionary for context provider.""" 

38 return { 

39 "name": self.name, 

40 "brief_description": self.brief_description, 

41 "triggers": self.triggers, 

42 "instructions": self.instructions, 

43 } 

44 

45 

46class SkillDocumentationIndex: 

47 """In-memory index of skill documentation for context injection. 

48 

49 Separate from SkillRegistry to avoid mixing persistent install 

50 metadata with runtime documentation. This index is built at agent 

51 initialization and used by SkillContextProvider for progressive disclosure. 

52 

53 Example: 

54 >>> skill_docs = SkillDocumentationIndex() 

55 >>> skill_docs.add_skill("osdu-quality", manifest) 

56 >>> skill_docs.has_skills() 

57 True 

58 >>> skill_docs.count() 

59 1 

60 >>> metadata = skill_docs.get_all_metadata() 

61 """ 

62 

63 def __init__(self) -> None: 

64 self._skills: dict[str, SkillDocumentation] = {} 

65 

66 def add_skill(self, name: str, manifest: SkillManifest) -> None: 

67 """Add skill documentation from manifest. 

68 

69 Args: 

70 name: Canonical skill name (normalized) 

71 manifest: Parsed SkillManifest with instructions and triggers 

72 """ 

73 # Convert triggers to dict format with consistent structure 

74 triggers_dict = { 

75 "keywords": manifest.triggers.keywords if manifest.triggers else [], 

76 "verbs": manifest.triggers.verbs if manifest.triggers else [], 

77 "patterns": manifest.triggers.patterns if manifest.triggers else [], 

78 } 

79 

80 self._skills[name] = SkillDocumentation( 

81 name=name, 

82 brief_description=manifest.brief_description or manifest.description[:80], 

83 triggers=triggers_dict, 

84 instructions=manifest.instructions, 

85 ) 

86 

87 def get_all_metadata(self) -> list[dict[str, Any]]: 

88 """Get all skill metadata for matching. 

89 

90 Returns: 

91 List of skill metadata dictionaries with name, brief_description, 

92 triggers, and instructions fields. 

93 """ 

94 return [skill.to_dict() for skill in self._skills.values()] 

95 

96 def has_skills(self) -> bool: 

97 """Check if any skills are loaded. 

98 

99 Returns: 

100 True if at least one skill is in the index. 

101 """ 

102 return bool(self._skills) 

103 

104 def count(self) -> int: 

105 """Get number of loaded skills. 

106 

107 Returns: 

108 Number of skills in the index. 

109 """ 

110 return len(self._skills)