Skip to content

Commit f0bf397

Browse files
authored
fix: add register skill tool and comment playwright tool (#442)
* fix: add register skill tool and comment playwright tool * fix: correct agent.py
1 parent 14bd9e4 commit f0bf397

File tree

5 files changed

+181
-8
lines changed

5 files changed

+181
-8
lines changed

veadk/agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,13 @@ def load_skills(self):
309309
load_skills_from_cloud,
310310
load_skills_from_directory,
311311
)
312-
from veadk.tools.builtin_tools.playwright import playwright_tools
313312
from veadk.tools.skills_tools import (
314313
SkillsTool,
315314
bash_tool,
316315
edit_file_tool,
317316
read_file_tool,
318317
write_file_tool,
318+
register_skills_tool,
319319
)
320320

321321
skills: Dict[str, Skill] = {}
@@ -343,7 +343,7 @@ def load_skills(self):
343343
self.tools.append(write_file_tool)
344344
self.tools.append(edit_file_tool)
345345
self.tools.append(bash_tool)
346-
self.tools.append(playwright_tools)
346+
self.tools.append(register_skills_tool)
347347

348348
def _prepare_tracers(self):
349349
enable_apmplus_tracer = os.getenv("ENABLE_APMPLUS", "false").lower() == "true"

veadk/tools/builtin_tools/execute_skills.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
import json
1616
import os
17-
from typing import Optional, List
1817

1918
from google.adk.tools import ToolContext
2019

@@ -69,15 +68,13 @@ def _format_execution_result(result_str: str) -> str:
6968

7069
def execute_skills(
7170
workflow_prompt: str,
72-
skills: Optional[List[str]] = None,
7371
tool_context: ToolContext = None,
7472
) -> str:
7573
"""execute skills in a code sandbox and return the output.
7674
For C++ code, don't execute it directly, compile and execute via Python; write sources and object files to /tmp.
7775
7876
Args:
7977
workflow_prompt (str): instruction of workflow
80-
skills (Optional[List[str]]): The skills will be invoked
8178
8279
Returns:
8380
str: The output of the code execution.
@@ -127,8 +124,6 @@ def execute_skills(
127124
logger.debug("Successfully get AK/SK from tool context.")
128125

129126
cmd = ["python", "agent.py", workflow_prompt]
130-
if skills:
131-
cmd.extend(["--skills"] + skills)
132127

133128
skill_space_id = os.getenv("SKILL_SPACE_ID", "")
134129
if not skill_space_id:

veadk/tools/skills_tools/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
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 .register_skills_tool import register_skills_tool
1819
from .session_path import initialize_session_path, get_session_path, clear_session_cache
1920

2021

@@ -23,6 +24,7 @@
2324
"edit_file_tool",
2425
"read_file_tool",
2526
"write_file_tool",
27+
"register_skills_tool",
2628
"SkillsTool",
2729
"initialize_session_path",
2830
"get_session_path",
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
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+
from __future__ import annotations
16+
17+
import json
18+
import os
19+
import zipfile
20+
from pathlib import Path
21+
from datetime import datetime
22+
23+
from google.adk.tools import ToolContext
24+
25+
from veadk.tools.skills_tools.session_path import get_session_path
26+
from veadk.integrations.ve_tos.ve_tos import VeTOS
27+
from veadk.utils.volcengine_sign import ve_request
28+
from veadk.utils.logger import get_logger
29+
30+
logger = get_logger(__name__)
31+
32+
33+
def register_skills_tool(
34+
skill_name: str,
35+
skill_description: str,
36+
skill_local_path: str,
37+
tool_context: ToolContext,
38+
) -> str:
39+
"""Register a skill to the remote skill space by uploading its zip package to TOS and calling the CreateSkill API.
40+
41+
Args:
42+
skill_name (str): The name of the skill.
43+
skill_description (str): The description of the skill.
44+
skill_local_path (str): The local path of the skill directory.
45+
- The format of the skill directory is as follows:
46+
skill_local_path/
47+
SKILL.md
48+
other files...
49+
tool_context (ToolContext): The context of the tool execution.
50+
51+
Returns:
52+
str: Result message indicating success or failure.
53+
"""
54+
working_dir = get_session_path(session_id=tool_context.session.id)
55+
56+
# skill_path = Path(skill_local_path)
57+
raw = Path(skill_local_path).expanduser()
58+
if not raw.is_absolute():
59+
skill_path = (working_dir / raw).resolve()
60+
else:
61+
skill_path = raw.resolve()
62+
if not skill_path.exists() or not skill_path.is_dir():
63+
logger.error(f"Skill path '{skill_path}' does not exist or is not a directory.")
64+
return f"Skill path '{skill_path}' does not exist or is not a directory."
65+
66+
skill_readme = skill_path / "SKILL.md"
67+
if not skill_readme.exists():
68+
logger.error(f"Skill path '{skill_path}' has no SKILL.md file.")
69+
return f"Skill path '{skill_path}' has no SKILL.md file."
70+
71+
zip_file_path = working_dir / "outputs" / f"{skill_name}.zip"
72+
73+
with zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED) as zipf:
74+
for root, dirs, files in os.walk(skill_path):
75+
for file in files:
76+
file_path = Path(root) / file
77+
arcname = Path(skill_name) / file_path.relative_to(skill_path)
78+
zipf.write(file_path, arcname)
79+
80+
try:
81+
from veadk.auth.veauth.utils import get_credential_from_vefaas_iam
82+
83+
service = os.getenv("AGENTKIT_TOOL_SERVICE_CODE", "agentkit")
84+
region = os.getenv("AGENTKIT_TOOL_REGION", "cn-beijing")
85+
host = os.getenv("AGENTKIT_SKILL_HOST", "open.volcengineapi.com")
86+
87+
access_key = os.getenv("VOLCENGINE_ACCESS_KEY")
88+
secret_key = os.getenv("VOLCENGINE_SECRET_KEY")
89+
session_token = ""
90+
region = os.getenv("AGENTKIT_TOOL_REGION", "cn-beijing")
91+
92+
if not (access_key and secret_key):
93+
cred = get_credential_from_vefaas_iam()
94+
access_key = cred.access_key_id
95+
secret_key = cred.secret_access_key
96+
session_token = cred.session_token
97+
98+
res = ve_request(
99+
request_body={},
100+
action="GetCallerIdentity",
101+
ak=access_key,
102+
sk=secret_key,
103+
service="sts",
104+
version="2018-01-01",
105+
region=region,
106+
host="sts.volcengineapi.com",
107+
header={"X-Security-Token": session_token},
108+
)
109+
try:
110+
account_id = res["Result"]["AccountId"]
111+
except KeyError as e:
112+
logger.error(
113+
f"Error occurred while getting account id: {e}, response is {res}"
114+
)
115+
return f"Error: Failed to get account id when registering skill '{skill_name}'."
116+
117+
tos_bucket = f"agentkit-platform-{region}-{account_id}-skill"
118+
119+
tos_client = VeTOS(
120+
ak=access_key,
121+
sk=secret_key,
122+
session_token=session_token,
123+
bucket_name=tos_bucket,
124+
)
125+
126+
object_key = (
127+
f"uploads/{datetime.now().strftime('%Y%m%d_%H%M%S')}/{skill_name}.zip"
128+
)
129+
tos_client.upload_file(
130+
file_path=zip_file_path, bucket_name=tos_bucket, object_key=object_key
131+
)
132+
tos_url = tos_client.build_tos_url(
133+
bucket_name=tos_bucket, object_key=object_key
134+
)
135+
136+
skill_space_ids = os.getenv("SKILL_SPACE_ID", "")
137+
skill_space_ids_list = [
138+
x.strip() for x in skill_space_ids.split(",") if x.strip()
139+
]
140+
141+
response = ve_request(
142+
request_body={
143+
"Name": skill_name,
144+
"Description": skill_description,
145+
"TosUrl": tos_url,
146+
"SkillSpaces": skill_space_ids_list,
147+
},
148+
action="CreateSkill",
149+
ak=access_key,
150+
sk=secret_key,
151+
service=service,
152+
version="2025-10-30",
153+
region=region,
154+
host=host,
155+
header={"X-Security-Token": session_token},
156+
)
157+
158+
if isinstance(response, str):
159+
response = json.loads(response)
160+
161+
logger.debug(f"CreateSkill response: {response}")
162+
163+
if "Error" in response:
164+
logger.error(
165+
f"Failed to register skill '{skill_name}': {response['Error']}"
166+
)
167+
return f"Failed to register skill '{skill_name}': {response['Error']}"
168+
169+
logger.info(
170+
f"Successfully registered skill '{skill_name}' to skill space {skill_space_ids_list}."
171+
)
172+
return f"Successfully registered skill '{skill_name}' to skill space {skill_space_ids_list}."
173+
174+
except Exception as e:
175+
logger.error(f"Failed to register skill '{skill_name}': {e}")
176+
return f"Failed to register skill '{skill_name}'"

veadk/tools/skills_tools/session_path.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def get_session_path(session_id: str) -> Path:
9292
f"Auto-initializing with default /skills. "
9393
f"Install SkillsPlugin for custom skills directories."
9494
)
95-
return initialize_session_path(session_id, "/skills")
95+
return initialize_session_path(session_id)
9696

9797

9898
def clear_session_cache(session_id: str | None = None) -> None:

0 commit comments

Comments
 (0)