Coverage for src / agent / tools / toolset.py: 100%

13 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"""Base class for Agent toolsets. 

16 

17This module provides the abstract base class for creating toolsets. Toolsets 

18encapsulate related tools with shared dependencies, avoiding global state and 

19enabling dependency injection for testing. 

20""" 

21 

22from abc import ABC, abstractmethod 

23from collections.abc import Callable 

24from typing import Any 

25 

26from agent.config.schema import AgentSettings 

27from agent.utils.responses import create_error_response, create_success_response 

28 

29 

30class AgentToolset(ABC): 

31 """Base class for Agent toolsets. 

32 

33 Toolsets encapsulate related tools with shared dependencies. 

34 This avoids global state and enables dependency injection for testing. 

35 

36 Each toolset receives an AgentSettings instance with all necessary 

37 configuration, making it easy to mock in tests. 

38 

39 Example: 

40 >>> class MyTools(AgentToolset): 

41 ... def get_tools(self): 

42 ... return [self.my_tool] 

43 ... 

44 ... async def my_tool(self, arg: str) -> dict: 

45 ... return self._create_success_response( 

46 ... result=f"Processed: {arg}", 

47 ... message="Tool executed successfully" 

48 ... ) 

49 """ 

50 

51 def __init__(self, settings: AgentSettings): 

52 """Initialize toolset with settings. 

53 

54 Args: 

55 settings: Agent settings with provider and agent configuration 

56 

57 Example: 

58 >>> from agent.config import load_config 

59 >>> settings = load_config() 

60 >>> tools = FileSystemTools(settings) 

61 """ 

62 self.settings = settings 

63 # Legacy alias for compatibility 

64 self.config = settings 

65 

66 @abstractmethod 

67 def get_tools(self) -> list[Callable]: 

68 """Get list of tool functions. 

69 

70 Subclasses must implement this method to return their tool functions. 

71 Tools should be async callables with proper type hints and docstrings 

72 for LLM consumption. 

73 

74 Returns: 

75 List of callable tool functions 

76 

77 Example: 

78 >>> def get_tools(self): 

79 ... return [self.tool1, self.tool2] 

80 """ 

81 pass 

82 

83 def _create_success_response(self, result: Any, message: str = "") -> dict: 

84 """Create standardized success response. 

85 

86 All tools should return responses in this format for consistency. 

87 This makes it easy for the agent to handle tool results uniformly. 

88 

89 Args: 

90 result: Tool execution result (can be any type) 

91 message: Optional success message for logging/display 

92 

93 Returns: 

94 Structured response dict with success=True 

95 

96 Example: 

97 >>> response = self._create_success_response( 

98 ... result="Hello, World!", 

99 ... message="Greeting generated" 

100 ... ) 

101 >>> response 

102 {'success': True, 'result': 'Hello, World!', 'message': 'Greeting generated'} 

103 """ 

104 return create_success_response(result, message) 

105 

106 def _create_error_response(self, error: str, message: str) -> dict: 

107 """Create standardized error response. 

108 

109 Tools should use this when they encounter errors rather than raising 

110 exceptions. This allows the agent to handle errors gracefully and 

111 provide better feedback. 

112 

113 Args: 

114 error: Machine-readable error code (e.g., "resource_not_found") 

115 message: Human-friendly error message 

116 

117 Returns: 

118 Structured response dict with success=False 

119 

120 Example: 

121 >>> response = self._create_error_response( 

122 ... error="invalid_input", 

123 ... message="Name cannot be empty" 

124 ... ) 

125 >>> response 

126 {'success': False, 'error': 'invalid_input', 'message': 'Name cannot be empty'} 

127 """ 

128 return create_error_response(error, message)