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
« 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.
15"""Base class for Agent toolsets.
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"""
22from abc import ABC, abstractmethod
23from collections.abc import Callable
24from typing import Any
26from agent.config.schema import AgentSettings
27from agent.utils.responses import create_error_response, create_success_response
30class AgentToolset(ABC):
31 """Base class for Agent toolsets.
33 Toolsets encapsulate related tools with shared dependencies.
34 This avoids global state and enables dependency injection for testing.
36 Each toolset receives an AgentSettings instance with all necessary
37 configuration, making it easy to mock in tests.
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 """
51 def __init__(self, settings: AgentSettings):
52 """Initialize toolset with settings.
54 Args:
55 settings: Agent settings with provider and agent configuration
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
66 @abstractmethod
67 def get_tools(self) -> list[Callable]:
68 """Get list of tool functions.
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.
74 Returns:
75 List of callable tool functions
77 Example:
78 >>> def get_tools(self):
79 ... return [self.tool1, self.tool2]
80 """
81 pass
83 def _create_success_response(self, result: Any, message: str = "") -> dict:
84 """Create standardized success response.
86 All tools should return responses in this format for consistency.
87 This makes it easy for the agent to handle tool results uniformly.
89 Args:
90 result: Tool execution result (can be any type)
91 message: Optional success message for logging/display
93 Returns:
94 Structured response dict with success=True
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)
106 def _create_error_response(self, error: str, message: str) -> dict:
107 """Create standardized error response.
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.
113 Args:
114 error: Machine-readable error code (e.g., "resource_not_found")
115 message: Human-friendly error message
117 Returns:
118 Structured response dict with success=False
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)