Skip to content

Commit 2811a80

Browse files
chore: refine and reconstruct cli (#69)
* chore: reconstruct cli * fix deploy codes * fix: fix type errors and add logs (#68) * fix: fix type errors and add logs * fix type error in some files * remove useless tls package * resolve conflicts * fix deploy codes
1 parent a8c2db9 commit 2811a80

34 files changed

Lines changed: 522 additions & 318 deletions

.gitleaks.toml

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
[extend]
2+
useDefault = true
3+
4+
[[rules]]
5+
id = "aklt-key-pattern"
6+
description = "AKLT key pattern"
7+
regex = '''AKLT\w{40,70}'''
8+
9+
[[rules]]
10+
id = "akap-key-pattern"
11+
description = "AKAP key pattern"
12+
regex = '''AKAP\w{40,70}'''
13+
14+
[[rules]]
15+
id = "akip-key-pattern"
16+
description = "AKIP key pattern"
17+
regex = '''AKI\w{40,70}'''
18+
19+
[[rules]]
20+
id = "token-transformer-id-pattern"
21+
description = "Tokenizer/Transformer/Token ID patterns"
22+
regex = '''(tokenizer|transformer|token_id|tokenid|attention_head).{0,20}'''
23+
24+
[[rules]]
25+
id = "aws-style-key-pattern"
26+
description = "AWS-style key pattern"
27+
regex = '''(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}'''
28+
29+
[[rules]]
30+
id = "alibaba-ltai-pattern"
31+
description = "Alibaba LTAI key pattern"
32+
regex = '''(LTAI)[a-z0-9]{20}'''
33+
34+
[[rules]]
35+
id = "aktp-key-pattern"
36+
description = "AKTP key pattern"
37+
regex = '''AKTP\w{40,70}'''
38+
39+
[[rules]]
40+
id = "app-id-pattern"
41+
description = "App ID patterns"
42+
regex = '''([^*<\s|:>]{0,7})(app_id|appid)([^]()!<>;/@&,]{0,10}[(=:]\s{0,6}["']{0,1}[0-9]{6,32}["']{0,1})'''
43+
44+
[[rules]]
45+
id = "byted-org-domains"
46+
description = "byted.org domains"
47+
regex = '''.{0,15}\.?byted.org.{0,20}'''
48+
49+
[[rules]]
50+
id = "bytedance-net-domains"
51+
description = "bytedance.net domains"
52+
regex = '''.{0,15}\.?bytedance.net.{0,20}'''
53+
54+
[[rules]]
55+
id = "feishu-cn-domains"
56+
description = "bytedance.feishu.cn domains"
57+
regex = '''.{0,20}.bytedance\.feishu\.cn.{0,50}'''
58+
59+
[[rules]]
60+
id = "larkoffice-com-domains"
61+
description = "bytedance.larkoffice.com domains"
62+
regex = '''.{0,20}.bytedance\.larkoffice\.com.{0,50}'''
63+
64+
[[rules]]
65+
id = "private-ip-10-range"
66+
description = "Private IP address pattern (10.x.x.x)"
67+
regex = '''(10\.\d{1,3}\.\d{1,3}\.\d{1,3})'''

.pre-commit-config.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ repos:
99
args: [ --fix ]
1010
# Run the formatter.
1111
- id: ruff-format
12-
types_or: [ python, pyi ]
12+
types_or: [ python, pyi ]
13+
- repo: https://github.com/gitleaks/gitleaks
14+
rev: v8.24.2
15+
hooks:
16+
- id: gitleaks

config.yaml.full

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ database:
9898

9999

100100
# [optional] for prompt optimization in cli/app
101-
agent_pilot:
101+
prompt_pilot:
102102
api_key:
103103

104104

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@ dependencies = [
2121
"opentelemetry-exporter-otlp>=1.35.0",
2222
"opentelemetry-instrumentation-logging>=0.56b0",
2323
"wrapt>=1.17.2", # For patching built-in functions
24-
"typer>=0.16.0", # For command-line implementation
2524
]
2625

2726
[project.scripts]
28-
veadk = "veadk.cli.main:app"
27+
veadk = "veadk.cli.cli:veadk"
2928

3029
[project.optional-dependencies]
3130
database = [

tests/test_cloud.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414

1515
import os
1616
import tempfile
17-
import pytest
17+
from unittest.mock import AsyncMock, Mock, patch
1818

19-
from unittest.mock import Mock, patch, AsyncMock
19+
import pytest
2020

2121
os.environ["VOLCENGINE_ACCESS_KEY"] = "test_access_key"
2222
os.environ["VOLCENGINE_SECRET_KEY"] = "test_secret_key"
@@ -110,7 +110,7 @@ async def test_cloud():
110110
# Test CloudApp delete_self functionality
111111
with patch("builtins.input", return_value="y"):
112112
with patch(
113-
"veadk.cli.services.vefaas.vefaas.VeFaaS"
113+
"veadk.integrations.ve_faas.ve_faas.VeFaaS"
114114
) as mock_vefaas_in_app:
115115
mock_vefaas_client = Mock()
116116
mock_vefaas_in_app.return_value = mock_vefaas_client

veadk/cli/cli.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
import click
16+
17+
from veadk.cli.cli_deploy import deploy
18+
from veadk.cli.cli_init import init
19+
from veadk.cli.cli_prompt import prompt
20+
from veadk.cli.cli_studio import studio
21+
from veadk.cli.cli_web import web
22+
from veadk.version import VERSION
23+
24+
25+
@click.group()
26+
@click.version_option(
27+
version=VERSION, prog_name="Volcengine Agent Development Kit (VeADK)"
28+
)
29+
def veadk():
30+
"""Volcengine ADK command line tools"""
31+
pass
32+
33+
34+
veadk.add_command(deploy)
35+
veadk.add_command(init)
36+
veadk.add_command(prompt)
37+
veadk.add_command(studio)
38+
veadk.add_command(web)
39+
40+
if __name__ == "__main__":
41+
veadk()

veadk/cli/cli_deploy.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
import click
16+
17+
18+
@click.command()
19+
@click.option(
20+
"--access-key",
21+
default=None,
22+
help="Volcengine access key",
23+
)
24+
@click.option(
25+
"--secret-key",
26+
default=None,
27+
help="Volcengine secret key",
28+
)
29+
@click.option("--name", help="Expected Volcengine FaaS application name")
30+
@click.option("--path", default=".", help="Local project path")
31+
def deploy(access_key: str, secret_key: str, name: str, path: str) -> None:
32+
"""Deploy a user project to Volcengine FaaS application."""
33+
from pathlib import Path
34+
35+
from veadk.config import getenv
36+
from veadk.integrations.ve_faas.ve_faas import VeFaaS
37+
38+
if not access_key:
39+
access_key = getenv("VOLCENGINE_ACCESS_KEY")
40+
if not secret_key:
41+
secret_key = getenv("VOLCENGINE_SECRET_KEY")
42+
43+
user_proj_abs_path = Path(path).resolve()
44+
45+
ve_faas = VeFaaS(access_key=access_key, secret_key=secret_key)
46+
ve_faas.deploy(name=name, path=str(user_proj_abs_path))

veadk/cli/cli_init.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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 typing import Any
16+
17+
import click
18+
19+
20+
def _set_variable_in_file(file_path: str, setting_values: dict):
21+
import ast
22+
23+
with open(file_path, "r", encoding="utf-8") as f:
24+
source_code = f.read()
25+
26+
tree = ast.parse(source_code)
27+
28+
class VariableTransformer(ast.NodeTransformer):
29+
def visit_Assign(self, node: ast.Assign):
30+
for target in node.targets:
31+
if isinstance(target, ast.Name) and target.id in setting_values:
32+
node.value = ast.Constant(value=setting_values[target.id])
33+
return node
34+
35+
transformer = VariableTransformer()
36+
new_tree = transformer.visit(tree)
37+
ast.fix_missing_locations(new_tree)
38+
new_source_code = ast.unparse(new_tree)
39+
40+
with open(file_path, "w", encoding="utf-8") as f:
41+
f.write(new_source_code)
42+
43+
click.echo("Your project has beed created.")
44+
45+
46+
def _render_prompts() -> dict[str, Any]:
47+
vefaas_application_name = click.prompt(
48+
"Volcengine FaaS application name", default="veadk-cloud-agent"
49+
)
50+
51+
gateway_name = click.prompt(
52+
"Volcengine gateway instance name", default="", show_default=True
53+
)
54+
55+
gateway_service_name = click.prompt(
56+
"Volcengine gateway service name", default="", show_default=True
57+
)
58+
59+
gateway_upstream_name = click.prompt(
60+
"Volcengine gateway upstream name", default="", show_default=True
61+
)
62+
63+
deploy_mode_options = {
64+
"1": "A2A/MCP Server",
65+
"2": "VeADK Studio",
66+
"3": "VeADK Web / Google ADK Web",
67+
}
68+
69+
click.echo("Choose a deploy mode:")
70+
for key, value in deploy_mode_options.items():
71+
click.echo(f" {key}. {value}")
72+
73+
deploy_mode = click.prompt(
74+
"Enter your choice", type=click.Choice(deploy_mode_options.keys())
75+
)
76+
77+
return {
78+
"VEFAAS_APPLICATION_NAME": vefaas_application_name,
79+
"GATEWAY_NAME": gateway_name,
80+
"GATEWAY_SERVICE_NAME": gateway_service_name,
81+
"GATEWAY_UPSTREAM_NAME": gateway_upstream_name,
82+
"USE_STUDIO": deploy_mode == deploy_mode_options["2"],
83+
"USE_ADK_WEB": deploy_mode == deploy_mode_options["3"],
84+
}
85+
86+
87+
@click.command()
88+
def init() -> None:
89+
"""Init a veadk project that can be deployed to Volcengine VeFaaS."""
90+
import shutil
91+
from pathlib import Path
92+
93+
import veadk.integrations.ve_faas as vefaas
94+
95+
cwd = Path.cwd()
96+
local_dir_name = click.prompt("Directory name", default="veadk-cloud-proj")
97+
target_dir_path = cwd / local_dir_name
98+
99+
if target_dir_path.exists():
100+
click.confirm(
101+
f"Directory '{target_dir_path}' already exists, do you want to overwrite it",
102+
abort=True,
103+
)
104+
shutil.rmtree(target_dir_path)
105+
106+
setting_values = _render_prompts()
107+
108+
template_dir_path = Path(vefaas.__file__).parent / "template"
109+
shutil.copytree(template_dir_path, target_dir_path)
110+
_set_variable_in_file(target_dir_path / "deploy.py", setting_values)

veadk/cli/cli_prompt.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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+
import click
16+
17+
18+
@click.command()
19+
@click.option(
20+
"--path", default=".", help="Agent file path with global variable `agent=...`"
21+
)
22+
@click.option("--feedback", default=None, help="Suggestions for prompt optimization")
23+
@click.option("--api-key", default=None, help="API Key of PromptPilot")
24+
@click.option(
25+
"--model-name",
26+
default="doubao-1.5-pro-32k-250115",
27+
help="Model name for prompt optimization",
28+
)
29+
def prompt(path: str, feedback: str, api_key: str, model_name: str) -> None:
30+
"""Optimize agent system prompt from a local file."""
31+
from pathlib import Path
32+
33+
from veadk.agent import Agent
34+
from veadk.config import getenv
35+
from veadk.integrations.ve_prompt_pilot.ve_prompt_pilot import VePromptPilot
36+
from veadk.utils.misc import load_module_from_file
37+
38+
module_name = "agents_for_prompt_pilot"
39+
module_abs_path = Path(path).resolve()
40+
41+
module = load_module_from_file(
42+
module_name=module_name, file_path=str(module_abs_path)
43+
)
44+
45+
# get all global variables from module
46+
globals_in_module = vars(module)
47+
48+
agents = []
49+
for global_variable_name, global_variable_value in globals_in_module.items():
50+
if isinstance(global_variable_value, Agent):
51+
agent = global_variable_value
52+
agents.append(agent)
53+
54+
if len(agents) > 0:
55+
click.echo(f"Found {len(agents)} agents in {module_abs_path}")
56+
57+
if not api_key:
58+
api_key = getenv("PROMPT_PILOT_API_KEY")
59+
ve_prompt_pilot = VePromptPilot(api_key)
60+
ve_prompt_pilot.optimize(
61+
agents=agents, feedback=feedback, model_name=model_name
62+
)
63+
else:
64+
click.echo(f"No agents found in {module_abs_path}")

0 commit comments

Comments
 (0)