Skip to content

Commit 264e535

Browse files
committed
fix: reconstruct veadk skills from the tool level to the agent level
1 parent d905390 commit 264e535

File tree

11 files changed

+209
-446
lines changed

11 files changed

+209
-446
lines changed

veadk/agent.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from __future__ import annotations
1616

1717
import os
18-
from typing import Optional, Union
18+
from typing import Dict, Optional, Union
1919

2020
# If user didn't set LITELLM_LOCAL_MODEL_COST_MAP, set it to True
2121
# to enable local model cost map.
@@ -297,26 +297,45 @@ def update_model(self, model_name: str):
297297

298298
def load_skills(self):
299299
from pathlib import Path
300+
from veadk.skills.skill import Skill
301+
from veadk.skills.utils import (
302+
load_skills_from_directory,
303+
load_skills_from_cloud,
304+
)
305+
from veadk.tools.builtin_tools.playwright import playwright_tools
306+
from veadk.tools.skills_tools import (
307+
SkillsTool,
308+
read_file_tool,
309+
write_file_tool,
310+
edit_file_tool,
311+
bash_tool,
312+
)
300313

301-
from veadk.skills.utils import load_skills_from_directory
314+
skills: Dict[str, Skill] = {}
302315

303-
skills = []
304-
for skill in self.skills:
305-
path = Path(skill)
306-
if path.is_dir():
307-
skills.extend(load_skills_from_directory(path))
316+
for item in self.skills:
317+
path = Path(item)
318+
if path.exists() and path.is_dir():
319+
for skill in load_skills_from_directory(path):
320+
skills[skill.name] = skill
308321
else:
309-
logger.error(
310-
f"Skill {skill} is not a directory, skip. Loading skills from cloud is WIP."
311-
)
322+
for skill in load_skills_from_cloud(item):
323+
skills[skill.name] = skill
312324
if skills:
313325
self.instruction += "\nYou have the following skills:\n"
314326

315-
for skill in skills:
327+
for skill in skills.values():
316328
self.instruction += (
317329
f"- name: {skill.name}\n- description: {skill.description}\n\n"
318330
)
319331

332+
self.tools.append(SkillsTool(skills))
333+
self.tools.append(read_file_tool)
334+
self.tools.append(write_file_tool)
335+
self.tools.append(edit_file_tool)
336+
self.tools.append(bash_tool)
337+
self.tools.append(playwright_tools)
338+
320339
async def _run(
321340
self,
322341
runner,

veadk/runner.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,11 @@ async def run(
472472
)
473473
logger.info(f"Run config: {run_config}")
474474

475+
if self.agent.skills:
476+
from veadk.tools.skills_tools.session_path import initialize_session_path
477+
478+
initialize_session_path(session_id)
479+
475480
user_id = user_id or self.user_id
476481

477482
converted_messages: list = _convert_messages(

veadk/skills/skill.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
# limitations under the License.
1414

1515
from pydantic import BaseModel
16+
from typing import Optional
1617

1718

1819
class Skill(BaseModel):
1920
name: str
2021
description: str
21-
path: str
22+
path: str # local path or tos path
23+
skill_space_id: Optional[str] = None
24+
bucket_name: Optional[str] = None

veadk/skills/skills_plugin.py

Lines changed: 0 additions & 115 deletions
This file was deleted.

veadk/skills/utils.py

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,25 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import json
1516
from pathlib import Path
16-
17+
import os
1718
import frontmatter
1819

1920
from veadk.skills.skill import Skill
2021
from veadk.utils.logger import get_logger
22+
from veadk.utils.volcengine_sign import ve_request
2123

2224
logger = get_logger(__name__)
2325

2426

2527
def load_skill_from_directory(skill_directory: Path) -> Skill:
2628
logger.info(f"Load skill from {skill_directory}")
2729
skill_readme = skill_directory / "SKILL.md"
30+
if not skill_readme.exists():
31+
logger.error(f"Skill '{skill_directory}' has no SKILL.md file.")
32+
raise ValueError(f"Skill '{skill_directory}' has no SKILL.md file")
33+
2834
skill = frontmatter.load(str(skill_readme))
2935

3036
skill_name = skill.get("name", "")
@@ -39,7 +45,7 @@ def load_skill_from_directory(skill_directory: Path) -> Skill:
3945
)
4046

4147
logger.info(
42-
f"Successfully loaded skill from {skill_readme}, name={skill['name']}, description={skill['description']}"
48+
f"Successfully loaded skill {skill_name} locally from {skill_readme}, name={skill_name}, description={skill_description}"
4349
)
4450
return Skill(
4551
name=skill_name, # type: ignore
@@ -58,4 +64,76 @@ def load_skills_from_directory(skills_directory: Path) -> list[Skill]:
5864
return skills
5965

6066

61-
def load_skills_from_cloud(space_name: str) -> list[Skill]: ...
67+
def load_skills_from_cloud(skill_space_ids: str) -> list[Skill]:
68+
skill_space_ids_list = [x.strip() for x in skill_space_ids.split(",")]
69+
logger.info(f"Load skills from skill spaces: {skill_space_ids_list}")
70+
71+
from veadk.auth.veauth.utils import get_credential_from_vefaas_iam
72+
73+
skills = []
74+
75+
for skill_space_id in skill_space_ids_list:
76+
try:
77+
service = os.getenv("AGENTKIT_TOOL_SERVICE_CODE", "agentkit")
78+
region = os.getenv("AGENTKIT_TOOL_REGION", "cn-beijing")
79+
host = os.getenv("AGENTKIT_SKILL_HOST", "open.volcengineapi.com")
80+
81+
access_key = os.getenv("VOLCENGINE_ACCESS_KEY")
82+
secret_key = os.getenv("VOLCENGINE_SECRET_KEY")
83+
session_token = ""
84+
85+
if not (access_key and secret_key):
86+
# Try to get from vefaas iam
87+
cred = get_credential_from_vefaas_iam()
88+
access_key = cred.access_key_id
89+
secret_key = cred.secret_access_key
90+
session_token = cred.session_token
91+
92+
response = ve_request(
93+
request_body={
94+
"SkillSpaceId": skill_space_id,
95+
"InnerTags": {"source": "sandbox"},
96+
},
97+
action="ListSkillsBySpaceId",
98+
ak=access_key,
99+
sk=secret_key,
100+
service=service,
101+
version="2025-10-30",
102+
region=region,
103+
host=host,
104+
header={"X-Security-Token": session_token},
105+
)
106+
107+
if isinstance(response, str):
108+
response = json.loads(response)
109+
110+
list_skills_result = response.get("Result")
111+
items = list_skills_result.get("Items")
112+
113+
for item in items:
114+
if not isinstance(item, dict):
115+
continue
116+
skill_name = item.get("Name")
117+
skill_description = item.get("Description")
118+
tos_bucket = item.get("BucketName")
119+
tos_path = item.get("TosPath")
120+
if not skill_name:
121+
continue
122+
123+
skill = Skill(
124+
name=skill_name, # type: ignore
125+
description=skill_description, # type: ignore
126+
path=tos_path,
127+
skill_space_id=skill_space_id,
128+
bucket_name=tos_bucket,
129+
)
130+
131+
skills.append(skill)
132+
133+
logger.info(
134+
f"Successfully loaded skill {skill_name} from skill space={skill_space_id}, name={skill_name}, description={skill_description}"
135+
)
136+
except Exception as e:
137+
logger.error(f"Failed to load skill from skill space: {e}")
138+
139+
return skills

veadk/tools/skills_tools/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
from .bash_tool import bash_tool
1616
from .file_tool import edit_file_tool, read_file_tool, write_file_tool
1717
from .skills_tool import SkillsTool
18-
from .skills_toolset import SkillsToolset
1918
from .session_path import initialize_session_path, get_session_path, clear_session_cache
2019

2120

@@ -25,7 +24,6 @@
2524
"read_file_tool",
2625
"write_file_tool",
2726
"SkillsTool",
28-
"SkillsToolset",
2927
"initialize_session_path",
3028
"get_session_path",
3129
"clear_session_cache",

veadk/tools/skills_tools/bash_tool.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ async def bash_tool(command: str, description: str, tool_context: ToolContext):
3838
Execute bash commands in the skills environment with local shell.
3939
Working Directory & Structure:
4040
- Commands run in a temporary session directory: /tmp/veadk/{session_id}/
41-
- skills_directory -> All skills are available here (read-only).
41+
- Working directory structure:
42+
/tmp/veadk/{session_id}/
43+
├── skills/ -> all skills are available here (read-only).
44+
├── uploads/ -> staged user files (temporary)
45+
└── outputs/ -> generated files for return
4246
- Your current working directory is added to PYTHONPATH.
4347
4448
Python Imports (CRITICAL):

veadk/tools/skills_tools/file_tool.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ def read_file_tool(file_path: str, offset: int, limit: int, tool_context: ToolCo
2828
"""Read files with line numbers for precise editing.
2929
3030
Reads a file from the filesystem with line numbers.
31+
32+
Working directory structure:
33+
/tmp/veadk/{session_id}/
34+
├── skills/ -> all skills are available here (read-only).
35+
├── uploads/ -> staged user files (temporary)
36+
└── outputs/ -> generated files for return
37+
3138
Usage:
3239
- Provide a path to the file (absolute or relative to your working directory)
3340
- Returns content with line numbers (format: LINE_NUMBER|CONTENT)
@@ -88,6 +95,13 @@ def write_file_tool(file_path: str, content: str, tool_context: ToolContext):
8895
"""Write content to files (overwrites existing files).
8996
9097
Writes content to a file on the filesystem.
98+
99+
Working directory structure:
100+
/tmp/veadk/{session_id}/
101+
├── skills/ -> all skills are available here (read-only).
102+
├── uploads/ -> staged user files (temporary)
103+
└── outputs/ -> generated files for return
104+
91105
Usage:
92106
- Provide a path (absolute or relative to working directory) and content to write
93107
- Overwrites existing files
@@ -136,6 +150,12 @@ def edit_file_tool(
136150
):
137151
"""Edit files by replacing exact string matches.
138152
153+
Working directory structure:
154+
/tmp/veadk/{session_id}/
155+
├── skills/ -> all skills are available here (read-only).
156+
├── uploads/ -> staged user files (temporary)
157+
└── outputs/ -> generated files for return
158+
139159
Performs exact string replacements in files.
140160
Usage:
141161
- You must read the file first using read_file

0 commit comments

Comments
 (0)