kagent-adk 0.7.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- kagent/adk/__init__.py +8 -0
- kagent/adk/_a2a.py +178 -0
- kagent/adk/_agent_executor.py +335 -0
- kagent/adk/_lifespan.py +36 -0
- kagent/adk/_session_service.py +178 -0
- kagent/adk/_token.py +80 -0
- kagent/adk/artifacts/__init__.py +13 -0
- kagent/adk/artifacts/artifacts_toolset.py +56 -0
- kagent/adk/artifacts/return_artifacts_tool.py +160 -0
- kagent/adk/artifacts/session_path.py +106 -0
- kagent/adk/artifacts/stage_artifacts_tool.py +170 -0
- kagent/adk/cli.py +249 -0
- kagent/adk/converters/__init__.py +0 -0
- kagent/adk/converters/error_mappings.py +60 -0
- kagent/adk/converters/event_converter.py +322 -0
- kagent/adk/converters/part_converter.py +206 -0
- kagent/adk/converters/request_converter.py +35 -0
- kagent/adk/models/__init__.py +3 -0
- kagent/adk/models/_openai.py +564 -0
- kagent/adk/models/_ssl.py +245 -0
- kagent/adk/sandbox_code_executer.py +77 -0
- kagent/adk/skill_fetcher.py +103 -0
- kagent/adk/tools/README.md +217 -0
- kagent/adk/tools/__init__.py +15 -0
- kagent/adk/tools/bash_tool.py +74 -0
- kagent/adk/tools/file_tools.py +192 -0
- kagent/adk/tools/skill_tool.py +104 -0
- kagent/adk/tools/skills_plugin.py +49 -0
- kagent/adk/tools/skills_toolset.py +68 -0
- kagent/adk/types.py +268 -0
- kagent_adk-0.7.11.dist-info/METADATA +35 -0
- kagent_adk-0.7.11.dist-info/RECORD +34 -0
- kagent_adk-0.7.11.dist-info/WHEEL +4 -0
- kagent_adk-0.7.11.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Simplified bash tool for executing shell commands in skills context."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
from google.adk.tools import BaseTool, ToolContext
|
|
10
|
+
from google.genai import types
|
|
11
|
+
|
|
12
|
+
from kagent.skills import execute_command, get_bash_description, get_session_path
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger("kagent_adk." + __name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BashTool(BaseTool):
|
|
18
|
+
"""Execute bash commands safely in the skills environment.
|
|
19
|
+
|
|
20
|
+
This tool uses the Anthropic Sandbox Runtime (srt) to execute commands with:
|
|
21
|
+
- Filesystem restrictions (controlled read/write access)
|
|
22
|
+
- Network restrictions (controlled domain access)
|
|
23
|
+
- Process isolation at the OS level
|
|
24
|
+
|
|
25
|
+
Use it for command-line operations like running scripts, installing packages, etc.
|
|
26
|
+
For file operations (read/write/edit), use the dedicated file tools instead.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, skills_directory: str | Path):
|
|
30
|
+
super().__init__(
|
|
31
|
+
name="bash",
|
|
32
|
+
description=get_bash_description(),
|
|
33
|
+
)
|
|
34
|
+
self.skills_directory = Path(skills_directory).resolve()
|
|
35
|
+
if not self.skills_directory.exists():
|
|
36
|
+
raise ValueError(f"Skills directory does not exist: {self.skills_directory}")
|
|
37
|
+
|
|
38
|
+
def _get_declaration(self) -> types.FunctionDeclaration:
|
|
39
|
+
return types.FunctionDeclaration(
|
|
40
|
+
name=self.name,
|
|
41
|
+
description=self.description,
|
|
42
|
+
parameters=types.Schema(
|
|
43
|
+
type=types.Type.OBJECT,
|
|
44
|
+
properties={
|
|
45
|
+
"command": types.Schema(
|
|
46
|
+
type=types.Type.STRING,
|
|
47
|
+
description="Bash command to execute. Use && to chain commands.",
|
|
48
|
+
),
|
|
49
|
+
"description": types.Schema(
|
|
50
|
+
type=types.Type.STRING,
|
|
51
|
+
description="Clear, concise description of what this command does (5-10 words)",
|
|
52
|
+
),
|
|
53
|
+
},
|
|
54
|
+
required=["command"],
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
async def run_async(self, *, args: Dict[str, Any], tool_context: ToolContext) -> str:
|
|
59
|
+
"""Execute a bash command safely using the Anthropic Sandbox Runtime."""
|
|
60
|
+
command = args.get("command", "").strip()
|
|
61
|
+
description = args.get("description", "")
|
|
62
|
+
|
|
63
|
+
if not command:
|
|
64
|
+
return "Error: No command provided"
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
working_dir = get_session_path(session_id=tool_context.session.id)
|
|
68
|
+
result = await execute_command(command, working_dir)
|
|
69
|
+
logger.info(f"Executed bash command: {command}, description: {description}")
|
|
70
|
+
return result
|
|
71
|
+
except Exception as e:
|
|
72
|
+
error_msg = f"Error executing command '{command}': {e}"
|
|
73
|
+
logger.error(error_msg)
|
|
74
|
+
return error_msg
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""File operation tools for agent skills.
|
|
2
|
+
|
|
3
|
+
This module provides Read, Write, and Edit tools that agents can use to work with
|
|
4
|
+
files on the filesystem within the sandbox environment.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Dict
|
|
12
|
+
|
|
13
|
+
from google.adk.tools import BaseTool, ToolContext
|
|
14
|
+
from google.genai import types
|
|
15
|
+
|
|
16
|
+
from kagent.skills import (
|
|
17
|
+
edit_file_content,
|
|
18
|
+
get_edit_file_description,
|
|
19
|
+
get_read_file_description,
|
|
20
|
+
get_session_path,
|
|
21
|
+
get_write_file_description,
|
|
22
|
+
read_file_content,
|
|
23
|
+
write_file_content,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger("kagent_adk." + __name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ReadFileTool(BaseTool):
|
|
30
|
+
"""Read files with line numbers for precise editing."""
|
|
31
|
+
|
|
32
|
+
def __init__(self):
|
|
33
|
+
super().__init__(
|
|
34
|
+
name="read_file",
|
|
35
|
+
description=get_read_file_description(),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def _get_declaration(self) -> types.FunctionDeclaration:
|
|
39
|
+
return types.FunctionDeclaration(
|
|
40
|
+
name=self.name,
|
|
41
|
+
description=self.description,
|
|
42
|
+
parameters=types.Schema(
|
|
43
|
+
type=types.Type.OBJECT,
|
|
44
|
+
properties={
|
|
45
|
+
"file_path": types.Schema(
|
|
46
|
+
type=types.Type.STRING,
|
|
47
|
+
description="Path to the file to read (absolute or relative to working directory)",
|
|
48
|
+
),
|
|
49
|
+
"offset": types.Schema(
|
|
50
|
+
type=types.Type.INTEGER,
|
|
51
|
+
description="Optional line number to start reading from (1-indexed)",
|
|
52
|
+
),
|
|
53
|
+
"limit": types.Schema(
|
|
54
|
+
type=types.Type.INTEGER,
|
|
55
|
+
description="Optional number of lines to read",
|
|
56
|
+
),
|
|
57
|
+
},
|
|
58
|
+
required=["file_path"],
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
async def run_async(self, *, args: Dict[str, Any], tool_context: ToolContext) -> str:
|
|
63
|
+
"""Read a file with line numbers."""
|
|
64
|
+
file_path_str = args.get("file_path", "").strip()
|
|
65
|
+
offset = args.get("offset")
|
|
66
|
+
limit = args.get("limit")
|
|
67
|
+
|
|
68
|
+
if not file_path_str:
|
|
69
|
+
return "Error: No file path provided"
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
working_dir = get_session_path(session_id=tool_context.session.id)
|
|
73
|
+
path = Path(file_path_str)
|
|
74
|
+
if not path.is_absolute():
|
|
75
|
+
path = working_dir / path
|
|
76
|
+
path = path.resolve()
|
|
77
|
+
|
|
78
|
+
return read_file_content(path, offset, limit)
|
|
79
|
+
except (FileNotFoundError, IsADirectoryError, IOError) as e:
|
|
80
|
+
return f"Error reading file {file_path_str}: {e}"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class WriteFileTool(BaseTool):
|
|
84
|
+
"""Write content to files (overwrites existing files)."""
|
|
85
|
+
|
|
86
|
+
def __init__(self):
|
|
87
|
+
super().__init__(
|
|
88
|
+
name="write_file",
|
|
89
|
+
description=get_write_file_description(),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
def _get_declaration(self) -> types.FunctionDeclaration:
|
|
93
|
+
return types.FunctionDeclaration(
|
|
94
|
+
name=self.name,
|
|
95
|
+
description=self.description,
|
|
96
|
+
parameters=types.Schema(
|
|
97
|
+
type=types.Type.OBJECT,
|
|
98
|
+
properties={
|
|
99
|
+
"file_path": types.Schema(
|
|
100
|
+
type=types.Type.STRING,
|
|
101
|
+
description="Path to the file to write (absolute or relative to working directory)",
|
|
102
|
+
),
|
|
103
|
+
"content": types.Schema(
|
|
104
|
+
type=types.Type.STRING,
|
|
105
|
+
description="Content to write to the file",
|
|
106
|
+
),
|
|
107
|
+
},
|
|
108
|
+
required=["file_path", "content"],
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
async def run_async(self, *, args: Dict[str, Any], tool_context: ToolContext) -> str:
|
|
113
|
+
"""Write content to a file."""
|
|
114
|
+
file_path_str = args.get("file_path", "").strip()
|
|
115
|
+
content = args.get("content", "")
|
|
116
|
+
|
|
117
|
+
if not file_path_str:
|
|
118
|
+
return "Error: No file path provided"
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
working_dir = get_session_path(session_id=tool_context.session.id)
|
|
122
|
+
path = Path(file_path_str)
|
|
123
|
+
if not path.is_absolute():
|
|
124
|
+
path = working_dir / path
|
|
125
|
+
path = path.resolve()
|
|
126
|
+
|
|
127
|
+
return write_file_content(path, content)
|
|
128
|
+
except IOError as e:
|
|
129
|
+
error_msg = f"Error writing file {file_path_str}: {e}"
|
|
130
|
+
logger.error(error_msg)
|
|
131
|
+
return error_msg
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class EditFileTool(BaseTool):
|
|
135
|
+
"""Edit files by replacing exact string matches."""
|
|
136
|
+
|
|
137
|
+
def __init__(self):
|
|
138
|
+
super().__init__(
|
|
139
|
+
name="edit_file",
|
|
140
|
+
description=get_edit_file_description(),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def _get_declaration(self) -> types.FunctionDeclaration:
|
|
144
|
+
return types.FunctionDeclaration(
|
|
145
|
+
name=self.name,
|
|
146
|
+
description=self.description,
|
|
147
|
+
parameters=types.Schema(
|
|
148
|
+
type=types.Type.OBJECT,
|
|
149
|
+
properties={
|
|
150
|
+
"file_path": types.Schema(
|
|
151
|
+
type=types.Type.STRING,
|
|
152
|
+
description="Path to the file to edit (absolute or relative to working directory)",
|
|
153
|
+
),
|
|
154
|
+
"old_string": types.Schema(
|
|
155
|
+
type=types.Type.STRING,
|
|
156
|
+
description="The exact text to replace (must exist in file)",
|
|
157
|
+
),
|
|
158
|
+
"new_string": types.Schema(
|
|
159
|
+
type=types.Type.STRING,
|
|
160
|
+
description="The text to replace it with (must be different from old_string)",
|
|
161
|
+
),
|
|
162
|
+
"replace_all": types.Schema(
|
|
163
|
+
type=types.Type.BOOLEAN,
|
|
164
|
+
description="Replace all occurrences (default: false, only replaces first occurrence)",
|
|
165
|
+
),
|
|
166
|
+
},
|
|
167
|
+
required=["file_path", "old_string", "new_string"],
|
|
168
|
+
),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
async def run_async(self, *, args: Dict[str, Any], tool_context: ToolContext) -> str:
|
|
172
|
+
"""Edit a file by replacing old_string with new_string."""
|
|
173
|
+
file_path_str = args.get("file_path", "").strip()
|
|
174
|
+
old_string = args.get("old_string", "")
|
|
175
|
+
new_string = args.get("new_string", "")
|
|
176
|
+
replace_all = args.get("replace_all", False)
|
|
177
|
+
|
|
178
|
+
if not file_path_str:
|
|
179
|
+
return "Error: No file path provided"
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
working_dir = get_session_path(session_id=tool_context.session.id)
|
|
183
|
+
path = Path(file_path_str)
|
|
184
|
+
if not path.is_absolute():
|
|
185
|
+
path = working_dir / path
|
|
186
|
+
path = path.resolve()
|
|
187
|
+
|
|
188
|
+
return edit_file_content(path, old_string, new_string, replace_all)
|
|
189
|
+
except (FileNotFoundError, IsADirectoryError, ValueError, IOError) as e:
|
|
190
|
+
error_msg = f"Error editing file {file_path_str}: {e}"
|
|
191
|
+
logger.error(error_msg)
|
|
192
|
+
return error_msg
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Tool for discovering and loading skills."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
from google.adk.tools import BaseTool, ToolContext
|
|
10
|
+
from google.genai import types
|
|
11
|
+
|
|
12
|
+
from kagent.skills import (
|
|
13
|
+
discover_skills,
|
|
14
|
+
generate_skills_tool_description,
|
|
15
|
+
load_skill_content,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger("kagent_adk." + __name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SkillsTool(BaseTool):
|
|
22
|
+
"""Discover and load skill instructions.
|
|
23
|
+
|
|
24
|
+
This tool dynamically discovers available skills and embeds their metadata in the
|
|
25
|
+
tool description. Agent invokes a skill by name to load its full instructions.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, skills_directory: str | Path):
|
|
29
|
+
self.skills_directory = Path(skills_directory).resolve()
|
|
30
|
+
if not self.skills_directory.exists():
|
|
31
|
+
raise ValueError(f"Skills directory does not exist: {self.skills_directory}")
|
|
32
|
+
|
|
33
|
+
self._skill_cache: Dict[str, str] = {}
|
|
34
|
+
|
|
35
|
+
# Generate description with available skills embedded
|
|
36
|
+
description = self._generate_description_with_skills()
|
|
37
|
+
|
|
38
|
+
super().__init__(
|
|
39
|
+
name="skills",
|
|
40
|
+
description=description,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def _generate_description_with_skills(self) -> str:
|
|
44
|
+
"""Generate tool description with available skills embedded."""
|
|
45
|
+
skills = discover_skills(self.skills_directory)
|
|
46
|
+
return generate_skills_tool_description(skills)
|
|
47
|
+
|
|
48
|
+
def _get_declaration(self) -> types.FunctionDeclaration:
|
|
49
|
+
return types.FunctionDeclaration(
|
|
50
|
+
name=self.name,
|
|
51
|
+
description=self.description,
|
|
52
|
+
parameters=types.Schema(
|
|
53
|
+
type=types.Type.OBJECT,
|
|
54
|
+
properties={
|
|
55
|
+
"command": types.Schema(
|
|
56
|
+
type=types.Type.STRING,
|
|
57
|
+
description='The skill name (no arguments). E.g., "data-analysis" or "pdf-processing"',
|
|
58
|
+
),
|
|
59
|
+
},
|
|
60
|
+
required=["command"],
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
async def run_async(self, *, args: Dict[str, Any], tool_context: ToolContext) -> str:
|
|
65
|
+
"""Execute skill loading by name."""
|
|
66
|
+
skill_name = args.get("command", "").strip()
|
|
67
|
+
|
|
68
|
+
if not skill_name:
|
|
69
|
+
return "Error: No skill name provided"
|
|
70
|
+
|
|
71
|
+
return self._invoke_skill(skill_name)
|
|
72
|
+
|
|
73
|
+
def _invoke_skill(self, skill_name: str) -> str:
|
|
74
|
+
"""Load and return the full content of a skill."""
|
|
75
|
+
# Check cache first
|
|
76
|
+
if skill_name in self._skill_cache:
|
|
77
|
+
return self._skill_cache[skill_name]
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
content = load_skill_content(self.skills_directory, skill_name)
|
|
81
|
+
formatted_content = self._format_skill_content(skill_name, content)
|
|
82
|
+
|
|
83
|
+
# Cache the formatted content
|
|
84
|
+
self._skill_cache[skill_name] = formatted_content
|
|
85
|
+
|
|
86
|
+
return formatted_content
|
|
87
|
+
except (FileNotFoundError, IOError) as e:
|
|
88
|
+
logger.error(f"Failed to load skill {skill_name}: {e}")
|
|
89
|
+
return f"Error loading skill '{skill_name}': {e}"
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.error(f"An unexpected error occurred while loading skill {skill_name}: {e}")
|
|
92
|
+
return f"An unexpected error occurred while loading skill '{skill_name}': {e}"
|
|
93
|
+
|
|
94
|
+
def _format_skill_content(self, skill_name: str, content: str) -> str:
|
|
95
|
+
"""Format skill content for display to the agent."""
|
|
96
|
+
header = (
|
|
97
|
+
f'<command-message>The "{skill_name}" skill is loading</command-message>\n\n'
|
|
98
|
+
f"Base directory for this skill: {self.skills_directory}/{skill_name}\n\n"
|
|
99
|
+
)
|
|
100
|
+
footer = (
|
|
101
|
+
"\n\n---\n"
|
|
102
|
+
"The skill has been loaded. Follow the instructions above and use the bash tool to execute commands."
|
|
103
|
+
)
|
|
104
|
+
return header + content + footer
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from google.adk.agents import BaseAgent, LlmAgent
|
|
8
|
+
|
|
9
|
+
from ..tools import BashTool, EditFileTool, ReadFileTool, WriteFileTool
|
|
10
|
+
from .skill_tool import SkillsTool
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger("kagent_adk." + __name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def add_skills_tool_to_agent(skills_directory: str | Path, agent: BaseAgent) -> None:
|
|
16
|
+
"""Utility function to add Skills and Bash tools to a given agent.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
agent: The LlmAgent instance to which the tools will be added.
|
|
20
|
+
skills_directory: Path to directory containing skill folders.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
if not isinstance(agent, LlmAgent):
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
skills_directory = Path(skills_directory)
|
|
27
|
+
existing_tool_names = {getattr(t, "name", None) for t in agent.tools}
|
|
28
|
+
|
|
29
|
+
# Add SkillsTool if not already present
|
|
30
|
+
if "skills" not in existing_tool_names:
|
|
31
|
+
agent.tools.append(SkillsTool(skills_directory))
|
|
32
|
+
logger.debug(f"Added skills invoke tool to agent: {agent.name}")
|
|
33
|
+
|
|
34
|
+
# Add BashTool if not already present
|
|
35
|
+
if "bash" not in existing_tool_names:
|
|
36
|
+
agent.tools.append(BashTool(skills_directory))
|
|
37
|
+
logger.debug(f"Added bash tool to agent: {agent.name}")
|
|
38
|
+
|
|
39
|
+
if "read_file" not in existing_tool_names:
|
|
40
|
+
agent.tools.append(ReadFileTool())
|
|
41
|
+
logger.debug(f"Added read file tool to agent: {agent.name}")
|
|
42
|
+
|
|
43
|
+
if "write_file" not in existing_tool_names:
|
|
44
|
+
agent.tools.append(WriteFileTool())
|
|
45
|
+
logger.debug(f"Added write file tool to agent: {agent.name}")
|
|
46
|
+
|
|
47
|
+
if "edit_file" not in existing_tool_names:
|
|
48
|
+
agent.tools.append(EditFileTool())
|
|
49
|
+
logger.debug(f"Added edit file tool to agent: {agent.name}")
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from typing_extensions import override
|
|
9
|
+
except ImportError:
|
|
10
|
+
from typing import override
|
|
11
|
+
|
|
12
|
+
from google.adk.agents.readonly_context import ReadonlyContext
|
|
13
|
+
from google.adk.tools import BaseTool
|
|
14
|
+
from google.adk.tools.base_toolset import BaseToolset
|
|
15
|
+
|
|
16
|
+
from ..tools import BashTool, EditFileTool, ReadFileTool, WriteFileTool
|
|
17
|
+
from .skill_tool import SkillsTool
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger("kagent_adk." + __name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SkillsToolset(BaseToolset):
|
|
23
|
+
"""Toolset that provides Skills functionality for domain expertise execution.
|
|
24
|
+
|
|
25
|
+
This toolset provides skills access through specialized tools:
|
|
26
|
+
1. SkillsTool - Discover and load skill instructions
|
|
27
|
+
2. ReadFileTool - Read files with line numbers
|
|
28
|
+
3. WriteFileTool - Write/create files
|
|
29
|
+
4. EditFileTool - Edit files with precise replacements
|
|
30
|
+
5. BashTool - Execute shell commands
|
|
31
|
+
|
|
32
|
+
Skills provide specialized domain knowledge and scripts that the agent can use
|
|
33
|
+
to solve complex tasks. The toolset enables discovery of available skills,
|
|
34
|
+
file manipulation, and command execution.
|
|
35
|
+
|
|
36
|
+
Note: For file upload/download, use the ArtifactsToolset separately.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, skills_directory: str | Path):
|
|
40
|
+
"""Initialize the skills toolset.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
skills_directory: Path to directory containing skill folders.
|
|
44
|
+
"""
|
|
45
|
+
super().__init__()
|
|
46
|
+
self.skills_directory = Path(skills_directory)
|
|
47
|
+
|
|
48
|
+
# Create skills tools
|
|
49
|
+
self.skills_tool = SkillsTool(skills_directory)
|
|
50
|
+
self.read_file_tool = ReadFileTool()
|
|
51
|
+
self.write_file_tool = WriteFileTool()
|
|
52
|
+
self.edit_file_tool = EditFileTool()
|
|
53
|
+
self.bash_tool = BashTool(skills_directory)
|
|
54
|
+
|
|
55
|
+
@override
|
|
56
|
+
async def get_tools(self, readonly_context: Optional[ReadonlyContext] = None) -> List[BaseTool]:
|
|
57
|
+
"""Get all skills tools.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
List containing all skills tools: skills, read, write, edit, and bash.
|
|
61
|
+
"""
|
|
62
|
+
return [
|
|
63
|
+
self.skills_tool,
|
|
64
|
+
self.read_file_tool,
|
|
65
|
+
self.write_file_tool,
|
|
66
|
+
self.edit_file_tool,
|
|
67
|
+
self.bash_tool,
|
|
68
|
+
]
|