Skip to content

Commit e311fec

Browse files
committed
feat: add account service client and preflight service checks for build
operations and release 0.1.11 - Add AgentkitAccountClient for querying linked service status - Add ServiceNotEnabledException for handling missing service errors - Add PreflightMode enum (SKIP/WARN/FAIL/PROMPT) to control preflight behavior - Add preflight check logic to BaseExecutor with _preflight_check() and _handle_preflight_result() - Integrate preflight checks into BuildExecutor.execute() before strategy execution - Add get_required_services() class (cherry picked from commit f4901dcebb0dc0c6daa49d14a0dde6c85064e558)
1 parent 8e1f22f commit e311fec

22 files changed

+775
-91
lines changed

agentkit/client/base_agentkit_client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
Provides common initialization and API invocation logic.
1818
"""
1919

20-
from typing import Any, Dict, Union
20+
from typing import Any, Dict, Union, Optional
2121

2222
from agentkit.client.base_service_client import BaseServiceClient, ApiConfig
2323
from agentkit.utils.ve_sign import get_volc_agentkit_host_info
@@ -47,6 +47,7 @@ def __init__(
4747
region: str = "",
4848
session_token: str = "",
4949
service_name: str = "",
50+
header: Optional[Dict[str, Any]] = {"Accept": "application/json"},
5051
) -> None:
5152
"""
5253
Initialize the AgentKit client.
@@ -65,6 +66,7 @@ def __init__(
6566
session_token=session_token,
6667
service_name=service_name,
6768
credential_env_prefix='AGENTKIT',
69+
header=header,
6870
)
6971

7072
def _get_service_config(self) -> Dict[str, str]:

agentkit/client/base_service_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def __init__(
7272
session_token: str = "",
7373
service_name: str = "",
7474
credential_env_prefix: str = "",
75+
header: Optional[Dict[str, Any]] = {"Accept": "application/json"},
7576
) -> None:
7677
"""
7778
Initialize the service client.
@@ -110,7 +111,7 @@ def __init__(
110111
# Create ServiceInfo
111112
self.service_info = ServiceInfo(
112113
host=self.host,
113-
header={'Accept': 'application/json'},
114+
header=header,
114115
credentials=Credentials(
115116
ak=self.access_key,
116117
sk=self.secret_key,

agentkit/sdk/account/client.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+
# Auto-generated from API JSON definition
16+
# Do not edit manually
17+
18+
from __future__ import annotations
19+
20+
from typing import Dict, List, Optional
21+
from agentkit.client import BaseAgentkitClient
22+
from agentkit.sdk.account.types import (
23+
ListAccountLinkedServicesRequest,
24+
ListAccountLinkedServicesResponse,
25+
)
26+
27+
28+
class AgentkitAccountClient(BaseAgentkitClient):
29+
"""AgentKit Account Management Service"""
30+
API_ACTIONS: Dict[str, str] = {
31+
"ListAccountLinkedServices": "ListAccountLinkedServices",
32+
}
33+
34+
def __init__(
35+
self,
36+
access_key: str = "",
37+
secret_key: str = "",
38+
region: str = "",
39+
session_token: str = "",
40+
) -> None:
41+
super().__init__(
42+
access_key=access_key,
43+
secret_key=secret_key,
44+
region=region,
45+
session_token=session_token,
46+
service_name="account",
47+
)
48+
49+
50+
def list_account_linked_services(self, request: ListAccountLinkedServicesRequest) -> ListAccountLinkedServicesResponse:
51+
return self._invoke_api(
52+
api_action="ListAccountLinkedServices",
53+
request=request,
54+
response_type=ListAccountLinkedServicesResponse,
55+
)
56+
57+
def get_service_status(self, service_name: str) -> Optional[str]:
58+
"""
59+
Query the LinkServices status for a specific service.
60+
61+
Args:
62+
service_name: The name of the service to query (e.g., 'ark', 'vefaas')
63+
64+
Returns:
65+
The status string ('Enabled' or 'Disabled'), or None if service not found.
66+
"""
67+
print(1)
68+
response = self.list_account_linked_services(ListAccountLinkedServicesRequest())
69+
print(2)
70+
if not response.service_statuses:
71+
return None
72+
for svc in response.service_statuses:
73+
if svc.service_name == service_name:
74+
return svc.status
75+
return None
76+
77+
def get_services_status(self, service_names: List[str]) -> Dict[str, Optional[str]]:
78+
"""
79+
Query the LinkServices status for multiple services.
80+
81+
Args:
82+
service_names: List of service names to query (e.g., ['ark', 'vefaas', 'cr'])
83+
84+
Returns:
85+
A dict mapping service_name -> status ('Enabled'/'Disabled'/None if not found).
86+
"""
87+
response = self.list_account_linked_services(ListAccountLinkedServicesRequest())
88+
status_map: Dict[str, Optional[str]] = {name: None for name in service_names}
89+
if response.service_statuses:
90+
for svc in response.service_statuses:
91+
if svc.service_name in status_map:
92+
status_map[svc.service_name] = svc.status
93+
return status_map
94+
95+
def has_disabled_services(self) -> List[str]:
96+
"""
97+
Check if any returned service has a disabled status.
98+
99+
Returns:
100+
A list of service names that are disabled (status != 'Enabled').
101+
Returns empty list if all services are enabled.
102+
"""
103+
response = self.list_account_linked_services(ListAccountLinkedServicesRequest())
104+
disabled = []
105+
if response.service_statuses:
106+
for svc in response.service_statuses:
107+
if svc.status != "Enabled":
108+
disabled.append(svc.service_name)
109+
return disabled
110+

agentkit/sdk/account/types.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
# Auto-generated from API JSON definition
16+
# Do not edit manually
17+
18+
from __future__ import annotations
19+
20+
from typing import Optional
21+
from pydantic import BaseModel, Field
22+
23+
class AccountBaseModel(BaseModel):
24+
"""AgentKit auto-generated base model"""
25+
model_config = {
26+
"populate_by_name": True,
27+
"arbitrary_types_allowed": True
28+
}
29+
30+
31+
# Data Types
32+
class ServiceStatusesForListAccountLinkedServices(AccountBaseModel):
33+
service_name: Optional[str] = Field(default=None, alias="ServiceName")
34+
status: Optional[str] = Field(default=None, alias="Status")
35+
36+
37+
# ListAccountLinkedServices - Request
38+
class ListAccountLinkedServicesRequest(AccountBaseModel):
39+
pass
40+
41+
42+
# ListAccountLinkedServices - Response
43+
class ListAccountLinkedServicesResponse(AccountBaseModel):
44+
service_statuses: Optional[list[ServiceStatusesForListAccountLinkedServices]] = Field(default=None, alias="ServiceStatuses")
45+

agentkit/toolkit/builders/ve_pipeline.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,8 @@ def _upload_to_tos(self, archive_path: str, config: VeCPCRBuilderConfig) -> str:
679679
return tos_url
680680

681681
except Exception as e:
682+
if "AccountDisable" in str(e):
683+
raise Exception(f"Tos Service is not enabled, please enable it in the console. Enable services at: https://console.volcengine.com/agentkit/region:agentkit+cn-beijing/auth")
682684
raise Exception(f"Failed to upload to TOS: {str(e)}")
683685

684686
def _prepare_cr_resources(self, config: VeCPCRBuilderConfig) -> CRServiceConfig:

agentkit/toolkit/executors/__init__.py

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,35 +13,38 @@
1313
# limitations under the License.
1414

1515
"""
16-
Executor - 统一的配置管理和错误处理
16+
Executor layer - Unified configuration management and error handling.
1717
18-
Executor 层职责:
19-
- 配置加载和验证
20-
- Strategy 选择和实例化
21-
- Reporter 注入
22-
- 统一错误处理
23-
- 日志记录
18+
Responsibilities:
19+
- Configuration loading and validation
20+
- Strategy selection and instantiation
21+
- Reporter injection for progress reporting
22+
- Unified error handling and logging
2423
25-
不做:
26-
- 结果转换(Strategy 直接返回标准 Result)
27-
- 进度报告(由 Strategy → Builder/Runner 处理)
24+
Design principle: Executors are thin orchestration layers. They delegate actual
25+
work to Strategies and return results directly without transformation.
2826
"""
2927

30-
from .base_executor import BaseExecutor
28+
from .base_executor import BaseExecutor, ServiceNotEnabledException
3129
from .build_executor import BuildExecutor, BuildOptions
3230
from .deploy_executor import DeployExecutor
3331
from .invoke_executor import InvokeExecutor
3432
from .status_executor import StatusExecutor
3533
from .lifecycle_executor import LifecycleExecutor
3634
from .init_executor import InitExecutor
3735

36+
# Re-export PreflightMode from models for convenience
37+
from agentkit.toolkit.models import PreflightMode
38+
3839
__all__ = [
3940
'BaseExecutor',
4041
'BuildExecutor',
41-
'BuildOptions', # 导出 BuildOptions 供 CLI 使用
42+
'BuildOptions',
4243
'DeployExecutor',
4344
'InvokeExecutor',
4445
'StatusExecutor',
4546
'LifecycleExecutor',
46-
'InitExecutor'
47+
'InitExecutor',
48+
'PreflightMode',
49+
'ServiceNotEnabledException',
4750
]

0 commit comments

Comments
 (0)