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
14 changes: 10 additions & 4 deletions docs/content/9.deploy/1.from-scratch.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,19 @@ Run python `deploy.py` for deployment on Volcengine FaaS platform.
它会提示你输入如下几个参数:

- 本地项目名称(即项目的本地目录名称)
- VeFaaS应用名称(不可带下划线)
- 火山引擎网关实例名称(可选)
- 火山引擎网关服务名称(可选)
- 火山引擎网关Upstream名称(可选)
- 火山引擎函数服务应用名称(不可带下划线)
- 火山引擎API网关实例名称(可选)
- 火山引擎API网关服务名称(可选)
- 火山引擎API网关Upstream名称(可选)
- 部署模式
1. A2A / MCP Server
2. VeADK Web
- 认证方式
1. 不认证
2. API key(仅限A2A / MCP Server的部署模式)
3. OAuth2
- 火山引擎Identity用户池名称(可选)
- 火山引擎Identity客户端名称(可选,仅限VeADK Web的部署模式)

生成后的项目结构如下:

Expand Down
11 changes: 7 additions & 4 deletions docs/content/9.deploy/2.from-proj.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ navigation:
| - | - | - |
| `--access-key` | 字符串 | 火山引擎AK |
| `--secret-key` | 字符串 | 火山引擎SK |
| `--vefaas-app-name` | 字符串 | 火山引擎 VeFaaS 平台应用名称 |
| `--veapig-instance-name` | 字符串 | 火山引擎 APIG 实例名称 |
| `--veapig-service-name` | 字符串 | 火山引擎 APIG 服务名称 |
| `--veapig-upstream-name` | 字符串 | 火山引擎 APIG Upstream 名称 |
| `--vefaas-app-name` | 字符串 | 火山引擎函数服务应用名称 |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we need the app name for newly deployed app?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter existed already. I just changed its description.

| `--veapig-instance-name` | 字符串 | 火山引擎 API 网关实例名称 |
| `--veapig-service-name` | 字符串 | 火山引擎 API 网关服务名称 |
| `--veapig-upstream-name` | 字符串 | 火山引擎 API 网关 Upstream 名称 |
| `--short-term-memory-backend` | `local` \| `mysql` | 短期记忆后端 |
| `--use-adk-web` | FLAG | 设置后将会在云端启动 web,否则为 A2A / MCP 模式 |
| `--path` | 字符串 | 本地项目路径,默认为当前目录 |
| `--auth-method` | `none` \| `api-key` \| `oauth2` | 认证方式 |
| `--user-pool-name` | 字符串 | 火山引擎 Identity 用户池名称 |
| `--client-name` | 字符串 | 火山引擎 Identity 客户端名称 |
11 changes: 7 additions & 4 deletions docs/content/9.deploy/3.from-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ cloud_app = engine.deploy(...)
| 参数名称 | 类型 | 说明 |
| --- | --- | --- |
| path | str | 本地Agent项目路径 |
| application_name | str | 云应用名称 |
| gateway_name | str | 火山引擎网关实例名称 |
| gateway_service_name | str | 火山引擎网关服务名称 |
| gateway_upstream_name | str | 火山引擎网关Upstream名称 |
| application_name | str | 函数服务应用名称 |
| gateway_name | str | 火山引擎 API 网关实例名称 |
| gateway_service_name | str | 火山引擎 API 网关服务名称 |
| gateway_upstream_name | str | 火山引擎 API 网关Upstream名称 |
| use_adk_web | bool | 是否在云端使用VeADK Web / Google Web |
| auth_method | str | 认证方式 |
| identity_user_pool_name | str | 火山引擎 Identity 用户池名称 |
| identity_client_name | str | 火山引擎 Identity 客户端名称 |

### Cloud App

Expand Down
6 changes: 6 additions & 0 deletions tests/test_cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ async def test_cloud():
mock_vefaas_service.find_app_id_by_name.return_value = "app-123"
mock_vefaas_service.delete.return_value = None

mock_vefaas_service.get_application_route.return_value = (
"gw-123",
"svc-456",
"route-789",
)

# Test CloudAgentEngine creation and deploy functionality
engine = CloudAgentEngine()

Expand Down
26 changes: 26 additions & 0 deletions veadk/cli/cli_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@
help="Backend for short-term memory",
)
@click.option("--use-adk-web", is_flag=True, help="Whether to use ADK Web")
@click.option(
"--auth-method",
default="none",
type=click.Choice(["none", "api-key", "oauth2"]),
help="=Authentication method for agent",
)
@click.option(
"--user-pool-name",
default="",
help="Expected Volcengine Identity user pool name",
)
@click.option(
"--client-name",
default="",
help="Expected Volcengine Identity client name",
)
@click.option("--path", default=".", help="Local project path")
def deploy(
volcengine_access_key: str,
Expand All @@ -60,6 +76,9 @@ def deploy(
veapig_upstream_name: str,
short_term_memory_backend: str,
use_adk_web: bool,
auth_method: str,
user_pool_name: str,
client_name: str,
path: str,
) -> None:
"""Deploy a user project to Volcengine FaaS application.
Expand Down Expand Up @@ -90,6 +109,10 @@ def deploy(
short_term_memory_backend: Backend type for short-term memory storage.
Choices are 'local' or 'mysql'
use_adk_web: Flag to enable ADK Web interface for the deployed agent
auth_method: Authentication for the agent.
Choices are 'none', 'api-key' or 'oauth2'.
veidentity_user_pool_name: Optional Volcengine Identity user pool name
veidentity_client_name: Optional Volcengine Identity client name
path: Local directory path containing the VeADK project to deploy

Note:
Expand Down Expand Up @@ -131,6 +154,9 @@ def deploy(
"veapig_service_name": veapig_service_name,
"veapig_upstream_name": veapig_upstream_name,
"use_adk_web": use_adk_web,
"auth_method": auth_method,
"veidentity_user_pool_name": user_pool_name,
"veidentity_client_name": client_name,
"veadk_version": VERSION,
}

Expand Down
38 changes: 37 additions & 1 deletion veadk/cli/cli_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,49 @@ def _render_prompts() -> dict[str, Any]:
deploy_mode = click.prompt(
"Enter your choice", type=click.Choice(deploy_mode_options.keys())
)
use_adk_web = deploy_mode == "2"

auth_method_options = {
"1": "None",
"2": "API key",
"3": "OAuth2",
}
auth_methods = {
"1": "none",
"2": "api-key",
"3": "oauth2",
}

click.echo("Choose an authentication method:")
for key, value in auth_method_options.items():
click.echo(f" {key}. {value}")

auth_method_idx = click.prompt(
"Enter your choice", type=click.Choice(auth_method_options.keys())
)
auth_method = auth_methods[auth_method_idx]

veidentity_user_pool_name = ""
veidentity_client_name = ""
if auth_method == "oauth2":
veidentity_user_pool_name = click.prompt(
"Volcengine Identity user pool name", default="", show_default=True
)

if use_adk_web:
veidentity_client_name = click.prompt(
"Volcengine Identity client name", default="", show_default=True
)

return {
"vefaas_application_name": vefaas_application_name,
"veapig_instance_name": veapig_instance_name,
"veapig_service_name": veapig_service_name,
"veapig_upstream_name": veapig_upstream_name,
"use_adk_web": deploy_mode == "2",
"use_adk_web": use_adk_web,
"auth_method": auth_method,
"veidentity_user_pool_name": veidentity_user_pool_name,
"veidentity_client_name": veidentity_client_name,
"veadk_version": VERSION,
}

Expand Down
124 changes: 124 additions & 0 deletions veadk/cloud/cloud_agent_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import json
import os
import socket
import subprocess
Expand All @@ -23,7 +24,9 @@

from veadk.cloud.cloud_app import CloudApp
from veadk.config import getenv, veadk_environments
from veadk.integrations.ve_apig.ve_apig import APIGateway
from veadk.integrations.ve_faas.ve_faas import VeFaaS
from veadk.integrations.ve_identity.identity_client import IdentityClient
from veadk.utils.logger import get_logger
from veadk.utils.misc import formatted_timestamp

Expand All @@ -43,6 +46,8 @@ class CloudAgentEngine(BaseModel):
Defaults to VOLCENGINE_SECRET_KEY environment variable.
region (str): Region for Volcengine services. Defaults to "cn-beijing".
_vefaas_service (VeFaaS): Internal VeFaaS client instance, initialized post-creation.
_veapig_service (APIGateway): Internal VeAPIG client instance, initialized post-creation.
_veidentity_service (IdentityClient): Internal Identity client instance, initialized post-creation.

Note:
Credentials must be set via environment variables for default behavior.
Expand Down Expand Up @@ -85,6 +90,16 @@ def model_post_init(self, context: Any, /) -> None:
secret_key=self.volcengine_secret_key,
region=self.region,
)
self._veapig_service = APIGateway(
access_key=self.volcengine_access_key,
secret_key=self.volcengine_secret_key,
region=self.region,
)
self._veidentity_service = IdentityClient(
access_key=self.volcengine_access_key,
secret_key=self.volcengine_secret_key,
region=self.region,
)

def _prepare(self, path: str, name: str):
"""Prepares the local project for deployment by validating path and name.
Expand Down Expand Up @@ -202,6 +217,9 @@ def deploy(
gateway_service_name: str = "",
gateway_upstream_name: str = "",
use_adk_web: bool = False,
auth_method: str = "none",
identity_user_pool_name: str = "",
identity_client_name: str = "",
local_test: bool = False,
) -> CloudApp:
"""Deploys a local agent project to Volcengine FaaS, creating necessary resources.
Expand All @@ -215,6 +233,9 @@ def deploy(
gateway_service_name (str, optional): Custom service name. Defaults to timestamped.
gateway_upstream_name (str, optional): Custom upstream name. Defaults to timestamped.
use_adk_web (bool): Enable ADK Web configuration. Defaults to False.
auth_method (str, optional): Authentication for the agent. Defaults to none.
identity_user_pool_name (str, optional): Custom user pool name. Defaults to timestamped.
identity_client_name (str, optional): Custom client name. Defaults to timestamped.
local_test (bool): Perform FastAPI server test before deploy. Defaults to False.

Returns:
Expand All @@ -236,6 +257,10 @@ def deploy(
# prevent deepeval writing operations
veadk_environments["DEEPEVAL_TELEMETRY_OPT_OUT"] = "YES"

enable_key_auth = False
if auth_method == "api-key":
enable_key_auth = True

if use_adk_web:
veadk_environments["USE_ADK_WEB"] = "True"
else:
Expand All @@ -254,6 +279,12 @@ def deploy(
gateway_service_name = f"{application_name}-gw-svr-{formatted_timestamp()}"
if not gateway_upstream_name:
gateway_upstream_name = f"{application_name}-gw-us-{formatted_timestamp()}"
if not identity_user_pool_name:
identity_user_pool_name = (
f"{application_name}-id-up-{formatted_timestamp()}"
)
if not identity_client_name:
identity_client_name = f"{application_name}-id-cli-{formatted_timestamp()}"

try:
vefaas_application_url, app_id, function_id = self._vefaas_service.deploy(
Expand All @@ -262,9 +293,102 @@ def deploy(
gateway_name=gateway_name,
gateway_service_name=gateway_service_name,
gateway_upstream_name=gateway_upstream_name,
enable_key_auth=enable_key_auth,
)
_ = function_id # for future use

veapig_gateway_id, _, veapig_route_id = (
self._vefaas_service.get_application_route(app_id=app_id)
)

if auth_method == "oauth2":
# Get or create the Identity user pool.
identity_user_pool_id = self._veidentity_service.get_user_pool(
name=identity_user_pool_name,
)
if not identity_user_pool_id:
identity_user_pool_id = self._veidentity_service.create_user_pool(
name=identity_user_pool_name,
)
issuer = f"https://auth.id.{self.region}.volces.com/userpool/{identity_user_pool_id}"

# Create APIG upstream for Identity.
identity_domain = f"auth.id.{self.region}.volces.com"
veapig_identity_upstream_id = (
self._veapig_service.check_domain_upstream_exist(
domain=identity_domain,
port=443,
gateway_id=veapig_gateway_id,
)
)
if not veapig_identity_upstream_id:
veapig_identity_upstream_id = (
self._veapig_service.create_domain_upstream(
domain=f"auth.id.{self.region}.volces.com",
port=443,
is_https=True,
gateway_id=veapig_gateway_id,
upstream_name=f"id-{formatted_timestamp()}",
)
)

# Create plugin binding.
plugin_name = ""
plugin_config = {}
if use_adk_web:
# Get or create the Identity client.
identity_client_id = ""
identity_client_secret = ""
identity_client = self._veidentity_service.get_user_pool_client(
user_pool_uid=identity_user_pool_id,
name=identity_client_name,
)
if identity_client:
identity_client_id = identity_client[0]
identity_client_secret = identity_client[1]
else:
identity_client_id, identity_client_secret = (
self._veidentity_service.create_user_pool_client(
user_pool_uid=identity_user_pool_id,
name=identity_client_name,
client_type="WEB_APPLICATION",
)
)

self._veidentity_service.register_callback_for_user_pool_client(
user_pool_uid=identity_user_pool_id,
client_uid=identity_client_id,
callback_url=f"{vefaas_application_url}/callback",
web_origin=vefaas_application_url,
)

plugin_name = "wasm-oauth2-sso"
plugin_config = {
"AuthorizationUrl": f"{issuer}/authorize",
"UpstreamId": veapig_identity_upstream_id,
"TokenUrl": f"{issuer}/oauth/token",
"RedirectPath": "/callback",
"SignoutPath": "/signout",
"ClientId": identity_client_id,
"ClientSecret": identity_client_secret,
}
else:
plugin_name = "wasm-jwt-auth"
plugin_config = {
"RemoteJwks": {
"UpstreamId": veapig_identity_upstream_id,
"Url": f"{issuer}/keys",
},
"Issuer": issuer,
"ValidateConsumer": False,
}
self._vefaas_service.apig_client.create_plugin_binding(
scope="ROUTE",
target=veapig_route_id,
plugin_name=plugin_name,
plugin_config=json.dumps(plugin_config),
)

return CloudApp(
vefaas_application_name=application_name,
vefaas_endpoint=vefaas_application_url,
Expand Down
Loading