Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions veadk/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,13 @@ def load_skills(self):
load_skills_from_cloud,
load_skills_from_directory,
)
from veadk.tools.builtin_tools.playwright import playwright_tools
from veadk.tools.skills_tools import (
SkillsTool,
bash_tool,
edit_file_tool,
read_file_tool,
write_file_tool,
register_skills_tool,
)

skills: Dict[str, Skill] = {}
Expand Down Expand Up @@ -343,7 +343,7 @@ def load_skills(self):
self.tools.append(write_file_tool)
self.tools.append(edit_file_tool)
self.tools.append(bash_tool)
self.tools.append(playwright_tools)
self.tools.append(register_skills_tool)

def _prepare_tracers(self):
enable_apmplus_tracer = os.getenv("ENABLE_APMPLUS", "false").lower() == "true"
Expand Down
5 changes: 0 additions & 5 deletions veadk/tools/builtin_tools/execute_skills.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import json
import os
from typing import Optional, List

from google.adk.tools import ToolContext

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

def execute_skills(
workflow_prompt: str,
skills: Optional[List[str]] = None,
tool_context: ToolContext = None,
) -> str:
"""execute skills in a code sandbox and return the output.
For C++ code, don't execute it directly, compile and execute via Python; write sources and object files to /tmp.

Args:
workflow_prompt (str): instruction of workflow
skills (Optional[List[str]]): The skills will be invoked

Returns:
str: The output of the code execution.
Expand Down Expand Up @@ -127,8 +124,6 @@ def execute_skills(
logger.debug("Successfully get AK/SK from tool context.")

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

skill_space_id = os.getenv("SKILL_SPACE_ID", "")
if not skill_space_id:
Expand Down
2 changes: 2 additions & 0 deletions veadk/tools/skills_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .bash_tool import bash_tool
from .file_tool import edit_file_tool, read_file_tool, write_file_tool
from .skills_tool import SkillsTool
from .register_skills_tool import register_skills_tool
from .session_path import initialize_session_path, get_session_path, clear_session_cache


Expand All @@ -23,6 +24,7 @@
"edit_file_tool",
"read_file_tool",
"write_file_tool",
"register_skills_tool",
"SkillsTool",
"initialize_session_path",
"get_session_path",
Expand Down
176 changes: 176 additions & 0 deletions veadk/tools/skills_tools/register_skills_tool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

import json
import os
import zipfile
from pathlib import Path
from datetime import datetime

from google.adk.tools import ToolContext

from veadk.tools.skills_tools.session_path import get_session_path
from veadk.integrations.ve_tos.ve_tos import VeTOS
from veadk.utils.volcengine_sign import ve_request
from veadk.utils.logger import get_logger

logger = get_logger(__name__)


def register_skills_tool(
skill_name: str,
skill_description: str,
skill_local_path: str,
tool_context: ToolContext,
) -> str:
"""Register a skill to the remote skill space by uploading its zip package to TOS and calling the CreateSkill API.

Args:
skill_name (str): The name of the skill.
skill_description (str): The description of the skill.
skill_local_path (str): The local path of the skill directory.
- The format of the skill directory is as follows:
skill_local_path/
SKILL.md
other files...
tool_context (ToolContext): The context of the tool execution.

Returns:
str: Result message indicating success or failure.
"""
working_dir = get_session_path(session_id=tool_context.session.id)

# skill_path = Path(skill_local_path)
raw = Path(skill_local_path).expanduser()
if not raw.is_absolute():
skill_path = (working_dir / raw).resolve()
else:
skill_path = raw.resolve()
if not skill_path.exists() or not skill_path.is_dir():
logger.error(f"Skill path '{skill_path}' does not exist or is not a directory.")
return f"Skill path '{skill_path}' does not exist or is not a directory."

skill_readme = skill_path / "SKILL.md"
if not skill_readme.exists():
logger.error(f"Skill path '{skill_path}' has no SKILL.md file.")
return f"Skill path '{skill_path}' has no SKILL.md file."

zip_file_path = working_dir / "outputs" / f"{skill_name}.zip"

with zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(skill_path):
for file in files:
file_path = Path(root) / file
arcname = Path(skill_name) / file_path.relative_to(skill_path)
zipf.write(file_path, arcname)

try:
from veadk.auth.veauth.utils import get_credential_from_vefaas_iam

service = os.getenv("AGENTKIT_TOOL_SERVICE_CODE", "agentkit")
region = os.getenv("AGENTKIT_TOOL_REGION", "cn-beijing")
host = os.getenv("AGENTKIT_SKILL_HOST", "open.volcengineapi.com")

access_key = os.getenv("VOLCENGINE_ACCESS_KEY")
secret_key = os.getenv("VOLCENGINE_SECRET_KEY")
session_token = ""
region = os.getenv("AGENTKIT_TOOL_REGION", "cn-beijing")

if not (access_key and secret_key):
cred = get_credential_from_vefaas_iam()
access_key = cred.access_key_id
secret_key = cred.secret_access_key
session_token = cred.session_token

res = ve_request(
request_body={},
action="GetCallerIdentity",
ak=access_key,
sk=secret_key,
service="sts",
version="2018-01-01",
region=region,
host="sts.volcengineapi.com",
header={"X-Security-Token": session_token},
)
try:
account_id = res["Result"]["AccountId"]
except KeyError as e:
logger.error(
f"Error occurred while getting account id: {e}, response is {res}"
)
return f"Error: Failed to get account id when registering skill '{skill_name}'."

tos_bucket = f"agentkit-platform-{region}-{account_id}-skill"

tos_client = VeTOS(
ak=access_key,
sk=secret_key,
session_token=session_token,
bucket_name=tos_bucket,
)

object_key = (
f"uploads/{datetime.now().strftime('%Y%m%d_%H%M%S')}/{skill_name}.zip"
)
tos_client.upload_file(
file_path=zip_file_path, bucket_name=tos_bucket, object_key=object_key
)
tos_url = tos_client.build_tos_url(
bucket_name=tos_bucket, object_key=object_key
)

skill_space_ids = os.getenv("SKILL_SPACE_ID", "")
skill_space_ids_list = [
x.strip() for x in skill_space_ids.split(",") if x.strip()
]

response = ve_request(
request_body={
"Name": skill_name,
"Description": skill_description,
"TosUrl": tos_url,
"SkillSpaces": skill_space_ids_list,
},
action="CreateSkill",
ak=access_key,
sk=secret_key,
service=service,
version="2025-10-30",
region=region,
host=host,
header={"X-Security-Token": session_token},
)

if isinstance(response, str):
response = json.loads(response)

logger.debug(f"CreateSkill response: {response}")

if "Error" in response:
logger.error(
f"Failed to register skill '{skill_name}': {response['Error']}"
)
return f"Failed to register skill '{skill_name}': {response['Error']}"

logger.info(
f"Successfully registered skill '{skill_name}' to skill space {skill_space_ids_list}."
)
return f"Successfully registered skill '{skill_name}' to skill space {skill_space_ids_list}."

except Exception as e:
logger.error(f"Failed to register skill '{skill_name}': {e}")
return f"Failed to register skill '{skill_name}'"
2 changes: 1 addition & 1 deletion veadk/tools/skills_tools/session_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def get_session_path(session_id: str) -> Path:
f"Auto-initializing with default /skills. "
f"Install SkillsPlugin for custom skills directories."
)
return initialize_session_path(session_id, "/skills")
return initialize_session_path(session_id)


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