agentscope-runtime 0.2.0b2__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. agentscope_runtime/adapters/__init__.py +0 -0
  2. agentscope_runtime/adapters/agentscope/__init__.py +0 -0
  3. agentscope_runtime/adapters/agentscope/long_term_memory/__init__.py +6 -0
  4. agentscope_runtime/adapters/agentscope/long_term_memory/_long_term_memory_adapter.py +258 -0
  5. agentscope_runtime/adapters/agentscope/memory/__init__.py +6 -0
  6. agentscope_runtime/adapters/agentscope/memory/_memory_adapter.py +152 -0
  7. agentscope_runtime/adapters/agentscope/message.py +535 -0
  8. agentscope_runtime/adapters/agentscope/stream.py +506 -0
  9. agentscope_runtime/adapters/agentscope/tool/__init__.py +9 -0
  10. agentscope_runtime/adapters/agentscope/tool/sandbox_tool.py +69 -0
  11. agentscope_runtime/adapters/agentscope/tool/tool.py +233 -0
  12. agentscope_runtime/adapters/autogen/__init__.py +0 -0
  13. agentscope_runtime/adapters/autogen/tool/__init__.py +7 -0
  14. agentscope_runtime/adapters/autogen/tool/tool.py +211 -0
  15. agentscope_runtime/adapters/text/__init__.py +0 -0
  16. agentscope_runtime/adapters/text/stream.py +29 -0
  17. agentscope_runtime/common/collections/redis_mapping.py +4 -1
  18. agentscope_runtime/common/container_clients/fc_client.py +855 -0
  19. agentscope_runtime/common/utils/__init__.py +0 -0
  20. agentscope_runtime/common/utils/lazy_loader.py +57 -0
  21. agentscope_runtime/engine/__init__.py +25 -18
  22. agentscope_runtime/engine/app/agent_app.py +161 -91
  23. agentscope_runtime/engine/app/base_app.py +4 -118
  24. agentscope_runtime/engine/constant.py +8 -0
  25. agentscope_runtime/engine/deployers/__init__.py +8 -0
  26. agentscope_runtime/engine/deployers/adapter/__init__.py +2 -0
  27. agentscope_runtime/engine/deployers/adapter/a2a/a2a_adapter_utils.py +0 -21
  28. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +28 -9
  29. agentscope_runtime/engine/deployers/adapter/responses/__init__.py +2 -0
  30. agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +5 -2
  31. agentscope_runtime/engine/deployers/adapter/responses/response_api_protocol_adapter.py +1 -1
  32. agentscope_runtime/engine/deployers/agentrun_deployer.py +2541 -0
  33. agentscope_runtime/engine/deployers/cli_fc_deploy.py +1 -1
  34. agentscope_runtime/engine/deployers/kubernetes_deployer.py +9 -21
  35. agentscope_runtime/engine/deployers/local_deployer.py +47 -74
  36. agentscope_runtime/engine/deployers/modelstudio_deployer.py +216 -50
  37. agentscope_runtime/engine/deployers/utils/app_runner_utils.py +29 -0
  38. agentscope_runtime/engine/deployers/utils/detached_app.py +510 -0
  39. agentscope_runtime/engine/deployers/utils/docker_image_utils/__init__.py +1 -1
  40. agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +1 -1
  41. agentscope_runtime/engine/deployers/utils/docker_image_utils/{runner_image_factory.py → image_factory.py} +121 -61
  42. agentscope_runtime/engine/deployers/utils/package.py +693 -0
  43. agentscope_runtime/engine/deployers/utils/service_utils/__init__.py +0 -5
  44. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +301 -282
  45. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_templates.py +2 -4
  46. agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +23 -1
  47. agentscope_runtime/engine/deployers/utils/templates/app_main.py.j2 +84 -0
  48. agentscope_runtime/engine/deployers/utils/templates/runner_main.py.j2 +95 -0
  49. agentscope_runtime/engine/deployers/utils/{service_utils → templates}/standalone_main.py.j2 +0 -45
  50. agentscope_runtime/engine/deployers/utils/wheel_packager.py +119 -18
  51. agentscope_runtime/engine/helpers/runner.py +40 -0
  52. agentscope_runtime/engine/runner.py +171 -130
  53. agentscope_runtime/engine/schemas/agent_schemas.py +114 -3
  54. agentscope_runtime/engine/schemas/modelstudio_llm.py +4 -2
  55. agentscope_runtime/engine/schemas/oai_llm.py +23 -23
  56. agentscope_runtime/engine/schemas/response_api.py +65 -0
  57. agentscope_runtime/engine/schemas/session.py +24 -0
  58. agentscope_runtime/engine/services/__init__.py +0 -9
  59. agentscope_runtime/engine/services/agent_state/__init__.py +16 -0
  60. agentscope_runtime/engine/services/agent_state/redis_state_service.py +113 -0
  61. agentscope_runtime/engine/services/agent_state/state_service.py +179 -0
  62. agentscope_runtime/engine/services/memory/__init__.py +24 -0
  63. agentscope_runtime/engine/services/{mem0_memory_service.py → memory/mem0_memory_service.py} +17 -13
  64. agentscope_runtime/engine/services/{memory_service.py → memory/memory_service.py} +28 -7
  65. agentscope_runtime/engine/services/{redis_memory_service.py → memory/redis_memory_service.py} +1 -1
  66. agentscope_runtime/engine/services/{reme_personal_memory_service.py → memory/reme_personal_memory_service.py} +9 -6
  67. agentscope_runtime/engine/services/{reme_task_memory_service.py → memory/reme_task_memory_service.py} +2 -2
  68. agentscope_runtime/engine/services/{tablestore_memory_service.py → memory/tablestore_memory_service.py} +12 -18
  69. agentscope_runtime/engine/services/sandbox/__init__.py +13 -0
  70. agentscope_runtime/engine/services/{sandbox_service.py → sandbox/sandbox_service.py} +86 -71
  71. agentscope_runtime/engine/services/session_history/__init__.py +23 -0
  72. agentscope_runtime/engine/services/{redis_session_history_service.py → session_history/redis_session_history_service.py} +3 -2
  73. agentscope_runtime/engine/services/{session_history_service.py → session_history/session_history_service.py} +44 -34
  74. agentscope_runtime/engine/services/{tablestore_session_history_service.py → session_history/tablestore_session_history_service.py} +14 -19
  75. agentscope_runtime/engine/services/utils/tablestore_service_utils.py +2 -2
  76. agentscope_runtime/engine/tracing/base.py +10 -9
  77. agentscope_runtime/engine/tracing/message_util.py +1 -1
  78. agentscope_runtime/engine/tracing/tracing_util.py +7 -2
  79. agentscope_runtime/engine/tracing/wrapper.py +49 -31
  80. agentscope_runtime/sandbox/__init__.py +10 -2
  81. agentscope_runtime/sandbox/box/agentbay/__init__.py +4 -0
  82. agentscope_runtime/sandbox/box/agentbay/agentbay_sandbox.py +559 -0
  83. agentscope_runtime/sandbox/box/base/base_sandbox.py +12 -0
  84. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +115 -11
  85. agentscope_runtime/sandbox/box/cloud/__init__.py +4 -0
  86. agentscope_runtime/sandbox/box/cloud/cloud_sandbox.py +254 -0
  87. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +66 -0
  88. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +42 -0
  89. agentscope_runtime/sandbox/box/mobile/__init__.py +4 -0
  90. agentscope_runtime/sandbox/box/mobile/box/__init__.py +0 -0
  91. agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +216 -0
  92. agentscope_runtime/sandbox/box/training_box/training_box.py +2 -2
  93. agentscope_runtime/sandbox/client/http_client.py +1 -0
  94. agentscope_runtime/sandbox/enums.py +2 -0
  95. agentscope_runtime/sandbox/manager/sandbox_manager.py +15 -2
  96. agentscope_runtime/sandbox/manager/server/app.py +12 -0
  97. agentscope_runtime/sandbox/manager/server/config.py +19 -0
  98. agentscope_runtime/sandbox/model/manager_config.py +79 -2
  99. agentscope_runtime/sandbox/utils.py +0 -18
  100. agentscope_runtime/tools/RAGs/__init__.py +0 -0
  101. agentscope_runtime/tools/RAGs/modelstudio_rag.py +377 -0
  102. agentscope_runtime/tools/RAGs/modelstudio_rag_lite.py +219 -0
  103. agentscope_runtime/tools/__init__.py +119 -0
  104. agentscope_runtime/tools/_constants.py +18 -0
  105. agentscope_runtime/tools/alipay/__init__.py +4 -0
  106. agentscope_runtime/tools/alipay/base.py +334 -0
  107. agentscope_runtime/tools/alipay/payment.py +835 -0
  108. agentscope_runtime/tools/alipay/subscribe.py +551 -0
  109. agentscope_runtime/tools/base.py +264 -0
  110. agentscope_runtime/tools/cli/__init__.py +0 -0
  111. agentscope_runtime/tools/cli/modelstudio_mcp_server.py +78 -0
  112. agentscope_runtime/tools/generations/__init__.py +75 -0
  113. agentscope_runtime/tools/generations/async_image_to_video.py +350 -0
  114. agentscope_runtime/tools/generations/async_image_to_video_wan25.py +366 -0
  115. agentscope_runtime/tools/generations/async_speech_to_video.py +422 -0
  116. agentscope_runtime/tools/generations/async_text_to_video.py +320 -0
  117. agentscope_runtime/tools/generations/async_text_to_video_wan25.py +334 -0
  118. agentscope_runtime/tools/generations/image_edit.py +208 -0
  119. agentscope_runtime/tools/generations/image_edit_wan25.py +193 -0
  120. agentscope_runtime/tools/generations/image_generation.py +202 -0
  121. agentscope_runtime/tools/generations/image_generation_wan25.py +201 -0
  122. agentscope_runtime/tools/generations/image_style_repaint.py +208 -0
  123. agentscope_runtime/tools/generations/image_to_video.py +233 -0
  124. agentscope_runtime/tools/generations/qwen_image_edit.py +205 -0
  125. agentscope_runtime/tools/generations/qwen_image_generation.py +214 -0
  126. agentscope_runtime/tools/generations/qwen_text_to_speech.py +154 -0
  127. agentscope_runtime/tools/generations/speech_to_text.py +260 -0
  128. agentscope_runtime/tools/generations/speech_to_video.py +314 -0
  129. agentscope_runtime/tools/generations/text_to_video.py +221 -0
  130. agentscope_runtime/tools/mcp_wrapper.py +215 -0
  131. agentscope_runtime/tools/realtime_clients/__init__.py +13 -0
  132. agentscope_runtime/tools/realtime_clients/asr_client.py +27 -0
  133. agentscope_runtime/tools/realtime_clients/azure_asr_client.py +195 -0
  134. agentscope_runtime/tools/realtime_clients/azure_tts_client.py +383 -0
  135. agentscope_runtime/tools/realtime_clients/modelstudio_asr_client.py +151 -0
  136. agentscope_runtime/tools/realtime_clients/modelstudio_tts_client.py +199 -0
  137. agentscope_runtime/tools/realtime_clients/realtime_tool.py +55 -0
  138. agentscope_runtime/tools/realtime_clients/tts_client.py +33 -0
  139. agentscope_runtime/tools/searches/__init__.py +3 -0
  140. agentscope_runtime/tools/searches/modelstudio_search.py +877 -0
  141. agentscope_runtime/tools/searches/modelstudio_search_lite.py +310 -0
  142. agentscope_runtime/tools/utils/__init__.py +0 -0
  143. agentscope_runtime/tools/utils/api_key_util.py +45 -0
  144. agentscope_runtime/tools/utils/crypto_utils.py +99 -0
  145. agentscope_runtime/tools/utils/mcp_util.py +35 -0
  146. agentscope_runtime/version.py +1 -1
  147. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0.dist-info}/METADATA +240 -168
  148. agentscope_runtime-1.0.0.dist-info/RECORD +240 -0
  149. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0.dist-info}/entry_points.txt +1 -0
  150. agentscope_runtime/engine/agents/__init__.py +0 -2
  151. agentscope_runtime/engine/agents/agentscope_agent.py +0 -488
  152. agentscope_runtime/engine/agents/agno_agent.py +0 -220
  153. agentscope_runtime/engine/agents/autogen_agent.py +0 -250
  154. agentscope_runtime/engine/agents/base_agent.py +0 -29
  155. agentscope_runtime/engine/agents/langgraph_agent.py +0 -59
  156. agentscope_runtime/engine/agents/utils.py +0 -53
  157. agentscope_runtime/engine/deployers/utils/package_project_utils.py +0 -1163
  158. agentscope_runtime/engine/deployers/utils/service_utils/service_config.py +0 -75
  159. agentscope_runtime/engine/deployers/utils/service_utils/service_factory.py +0 -220
  160. agentscope_runtime/engine/helpers/helper.py +0 -179
  161. agentscope_runtime/engine/schemas/context.py +0 -54
  162. agentscope_runtime/engine/services/context_manager.py +0 -164
  163. agentscope_runtime/engine/services/environment_manager.py +0 -50
  164. agentscope_runtime/engine/services/manager.py +0 -174
  165. agentscope_runtime/engine/services/rag_service.py +0 -195
  166. agentscope_runtime/engine/services/tablestore_rag_service.py +0 -143
  167. agentscope_runtime/sandbox/tools/__init__.py +0 -12
  168. agentscope_runtime/sandbox/tools/base/__init__.py +0 -8
  169. agentscope_runtime/sandbox/tools/base/tool.py +0 -52
  170. agentscope_runtime/sandbox/tools/browser/__init__.py +0 -57
  171. agentscope_runtime/sandbox/tools/browser/tool.py +0 -597
  172. agentscope_runtime/sandbox/tools/filesystem/__init__.py +0 -32
  173. agentscope_runtime/sandbox/tools/filesystem/tool.py +0 -319
  174. agentscope_runtime/sandbox/tools/function_tool.py +0 -321
  175. agentscope_runtime/sandbox/tools/gui/__init__.py +0 -7
  176. agentscope_runtime/sandbox/tools/gui/tool.py +0 -77
  177. agentscope_runtime/sandbox/tools/mcp_tool.py +0 -195
  178. agentscope_runtime/sandbox/tools/sandbox_tool.py +0 -104
  179. agentscope_runtime/sandbox/tools/tool.py +0 -238
  180. agentscope_runtime/sandbox/tools/utils.py +0 -68
  181. agentscope_runtime-0.2.0b2.dist-info/RECORD +0 -183
  182. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0.dist-info}/WHEEL +0 -0
  183. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0.dist-info}/licenses/LICENSE +0 -0
  184. {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,2541 @@
1
+ # -*- coding: utf-8 -*-
2
+ # flake8: noqa: E501
3
+ # pylint: disable=line-too-long, too-many-branches, too-many-statements
4
+ # pylint: disable=protected-access, too-many-nested-blocks
5
+ import asyncio
6
+ import logging
7
+ import os
8
+ import time
9
+ from dataclasses import dataclass
10
+ from pathlib import Path
11
+ from typing import Dict, Optional, List, Any, Union, Tuple
12
+
13
+ from alibabacloud_agentrun20250910.client import Client as AgentRunClient
14
+ from alibabacloud_agentrun20250910.models import (
15
+ CreateAgentRuntimeRequest,
16
+ CreateAgentRuntimeInput,
17
+ GetAgentRuntimeRequest,
18
+ UpdateAgentRuntimeRequest,
19
+ UpdateAgentRuntimeInput,
20
+ CreateAgentRuntimeEndpointRequest,
21
+ CreateAgentRuntimeEndpointInput,
22
+ UpdateAgentRuntimeEndpointRequest,
23
+ UpdateAgentRuntimeEndpointInput,
24
+ ListAgentRuntimeEndpointsRequest,
25
+ PublishRuntimeVersionRequest,
26
+ PublishRuntimeVersionInput,
27
+ CodeConfiguration,
28
+ LogConfiguration,
29
+ NetworkConfiguration,
30
+ )
31
+ from alibabacloud_tea_openapi import models as open_api_models
32
+ from pydantic import BaseModel, Field
33
+
34
+ from .adapter.protocol_adapter import ProtocolAdapter
35
+ from .base import DeployManager
36
+ from .local_deployer import LocalDeployManager
37
+ from .utils.detached_app import get_bundle_entry_script
38
+ from .utils.wheel_packager import (
39
+ default_deploy_name,
40
+ generate_wrapper_project,
41
+ build_wheel,
42
+ )
43
+
44
+ logger = logging.getLogger(__name__)
45
+
46
+
47
+ @dataclass
48
+ class EndpointConfig:
49
+ """Configuration for agent runtime endpoint."""
50
+
51
+ agent_runtime_endpoint_name: Optional[str] = None
52
+ description: Optional[str] = None
53
+ tags: Optional[List[str]] = None
54
+ target_version: Optional[str] = "LATEST"
55
+
56
+
57
+ @dataclass
58
+ class CodeConfig:
59
+ """Configuration for code-based runtimes."""
60
+
61
+ command: Optional[List[str]] = None
62
+ oss_bucket_name: Optional[str] = None
63
+ oss_object_name: Optional[str] = None
64
+
65
+
66
+ @dataclass
67
+ class LogConfig:
68
+ """Configuration for logging."""
69
+
70
+ logstore: Optional[str] = None
71
+ project: Optional[str] = None
72
+
73
+
74
+ @dataclass
75
+ class NetworkConfig:
76
+ """Network configuration for the runtime."""
77
+
78
+ network_mode: str = "PUBLIC"
79
+ security_group_id: Optional[str] = None
80
+ vpc_id: Optional[str] = None
81
+ vswitch_ids: Optional[list[str]] = None
82
+
83
+
84
+ class AgentRunConfig(BaseModel):
85
+ access_key_id: Optional[str] = None
86
+ access_key_secret: Optional[str] = None
87
+ region_id: str = "cn-hangzhou"
88
+ endpoint: Optional[str] = None
89
+
90
+ log_config: Optional[LogConfig] = None
91
+ network_config: Optional[NetworkConfig] = NetworkConfig()
92
+
93
+ cpu: float = 2.0
94
+ memory: int = 2048
95
+
96
+ execution_role_arn: Optional[str] = None
97
+
98
+ session_concurrency_limit: Optional[int] = 1
99
+ session_idle_timeout_seconds: Optional[int] = 3600
100
+
101
+ @classmethod
102
+ def from_env(cls) -> "AgentRunConfig":
103
+ """Create AgentRunConfig from environment variables.
104
+
105
+ Returns:
106
+ AgentRunConfig: Configuration loaded from environment variables.
107
+ """
108
+ # Read region_id
109
+ region_id = os.environ.get("AGENT_RUN_REGION_ID", "cn-hangzhou")
110
+
111
+ # Read log-related environment variables
112
+ log_store = os.environ.get("AGENT_RUN_LOG_STORE")
113
+ log_project = os.environ.get("AGENT_RUN_LOG_PROJECT")
114
+ log_config = None
115
+ if log_store and log_project:
116
+ log_config = LogConfig(
117
+ logstore=log_store,
118
+ project=log_project,
119
+ )
120
+
121
+ # Read network-related environment variables
122
+ network_mode = os.environ.get("AGENT_RUN_NETWORK_MODE", "PUBLIC")
123
+ vpc_id = os.environ.get("AGENT_RUN_VPC_ID")
124
+ security_group_id = os.environ.get("AGENT_RUN_SECURITY_GROUP_ID")
125
+ vswitch_ids_str = os.environ.get("AGENT_RUN_VSWITCH_IDS")
126
+
127
+ network_config = None
128
+ if network_mode:
129
+ vswitch_ids = None
130
+ if vswitch_ids_str:
131
+ import json
132
+
133
+ vswitch_ids = json.loads(vswitch_ids_str)
134
+ if not isinstance(vswitch_ids, list):
135
+ raise ValueError("vswitch_ids must be a list")
136
+
137
+ network_config = NetworkConfig(
138
+ network_mode=network_mode,
139
+ vpc_id=vpc_id,
140
+ security_group_id=security_group_id,
141
+ vswitch_ids=vswitch_ids,
142
+ )
143
+
144
+ # Read CPU and Memory with type conversion
145
+ cpu_str = os.environ.get("AGENT_RUN_CPU", "2.0")
146
+ memory_str = os.environ.get("AGENT_RUN_MEMORY", "2048")
147
+
148
+ session_concurrency_limit_str = os.environ.get(
149
+ "AGENT_RUN_SESSION_CONCURRENCY_LIMIT",
150
+ "1",
151
+ )
152
+ session_idle_timeout_seconds_str = os.environ.get(
153
+ "AGENT_RUN_SESSION_IDLE_TIMEOUT_SECONDS",
154
+ "600",
155
+ )
156
+
157
+ try:
158
+ cpu = float(cpu_str)
159
+ except (ValueError, TypeError):
160
+ cpu = 2.0
161
+
162
+ try:
163
+ memory = int(memory_str)
164
+ except (ValueError, TypeError):
165
+ memory = 2048
166
+
167
+ execution_role_arn = os.environ.get("AGENT_RUN_EXECUTION_ROLE_ARN")
168
+
169
+ try:
170
+ session_concurrency_limit = int(session_concurrency_limit_str)
171
+ except (ValueError, TypeError):
172
+ session_concurrency_limit = 1
173
+
174
+ try:
175
+ session_idle_timeout_seconds = int(
176
+ session_idle_timeout_seconds_str,
177
+ )
178
+ except (ValueError, TypeError):
179
+ session_idle_timeout_seconds = 600
180
+
181
+ return cls(
182
+ access_key_id=os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID"),
183
+ access_key_secret=os.environ.get(
184
+ "ALIBABA_CLOUD_ACCESS_KEY_SECRET",
185
+ ),
186
+ region_id=region_id,
187
+ endpoint=os.environ.get(
188
+ "AGENT_RUN_ENDPOINT",
189
+ f"agentrun.{region_id}.aliyuncs.com",
190
+ ),
191
+ log_config=log_config,
192
+ network_config=network_config,
193
+ cpu=cpu,
194
+ memory=memory,
195
+ execution_role_arn=execution_role_arn,
196
+ session_concurrency_limit=session_concurrency_limit,
197
+ session_idle_timeout_seconds=session_idle_timeout_seconds,
198
+ )
199
+
200
+ def ensure_valid(self) -> None:
201
+ """Validate that all required configuration fields are present.
202
+
203
+ Raises:
204
+ ValueError: If required environment variables are missing.
205
+ """
206
+ missing = []
207
+ if not self.access_key_id:
208
+ missing.append("ALIBABA_CLOUD_ACCESS_KEY_ID")
209
+ if not self.access_key_secret:
210
+ missing.append("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
211
+ if missing:
212
+ raise ValueError(
213
+ f"Missing required AgentRun env vars: {', '.join(missing)}",
214
+ )
215
+
216
+
217
+ class OSSConfig(BaseModel):
218
+ region: str = Field("cn-hangzhou", description="OSS region")
219
+ access_key_id: Optional[str] = None
220
+ access_key_secret: Optional[str] = None
221
+
222
+ @classmethod
223
+ def from_env(cls) -> "OSSConfig":
224
+ """Create OSSConfig from environment variables.
225
+
226
+ Returns:
227
+ OSSConfig: Configuration loaded from environment variables.
228
+ """
229
+ return cls(
230
+ region=os.environ.get("OSS_REGION", "cn-hangzhou"),
231
+ access_key_id=os.environ.get(
232
+ "OSS_ACCESS_KEY_ID",
233
+ os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_ID"),
234
+ ),
235
+ access_key_secret=os.environ.get(
236
+ "OSS_ACCESS_KEY_SECRET",
237
+ os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET"),
238
+ ),
239
+ )
240
+
241
+ def ensure_valid(self) -> None:
242
+ """Validate that all required OSS configuration fields are present.
243
+
244
+ Raises:
245
+ RuntimeError: If required AccessKey credentials are missing.
246
+ """
247
+ # Allow fallback to Alibaba Cloud AK/SK via from_env()
248
+ if not self.access_key_id or not self.access_key_secret:
249
+ raise RuntimeError(
250
+ "Missing AccessKey for OSS. Set either OSS_ACCESS_KEY_ID/OSS_ACCESS_KEY_SECRET "
251
+ "or ALIBABA_CLOUD_ACCESS_KEY_ID/ALIBABA_CLOUD_ACCESS_KEY_SECRET.",
252
+ )
253
+
254
+
255
+ class AgentRunDeployManager(DeployManager):
256
+ """Manager for deploying agents to Alibaba Cloud AgentRun service.
257
+
258
+ This class handles the complete deployment workflow including:
259
+ - Building and packaging agent projects
260
+ - Uploading artifacts to OSS
261
+ - Creating and managing agent runtimes
262
+ - Creating and managing runtime endpoints
263
+ """
264
+
265
+ # Maximum attempts for polling agent runtime status
266
+ GET_AGENT_RUNTIME_STATUS_MAX_ATTEMPTS = 60
267
+ # Interval in seconds between status polling attempts
268
+ GET_AGENT_RUNTIME_STATUS_INTERVAL = 1
269
+
270
+ # Default version identifier for latest runtime
271
+ LATEST_VERSION = "LATEST"
272
+
273
+ # Default name for runtime endpoints
274
+ DEFAULT_ENDPOINT_NAME = "default-endpoint"
275
+
276
+ def __init__(
277
+ self,
278
+ oss_config: Optional[OSSConfig] = None,
279
+ agentrun_config: Optional[AgentRunConfig] = None,
280
+ build_root: Optional[Union[str, Path]] = None,
281
+ ):
282
+ """Initialize AgentRun deployment manager.
283
+
284
+ Args:
285
+ oss_config: OSS configuration for artifact storage. If None, loads from environment.
286
+ agentrun_config: AgentRun service configuration. If None, loads from environment.
287
+ build_root: Root directory for build artifacts. If None, uses parent directory of current working directory.
288
+ """
289
+ super().__init__()
290
+ self.oss_config = oss_config or OSSConfig.from_env()
291
+ self.agentrun_config = agentrun_config or AgentRunConfig.from_env()
292
+ self.build_root = (
293
+ Path(build_root)
294
+ if build_root
295
+ else Path(os.getcwd()).parent / ".agentscope_runtime_builds"
296
+ )
297
+ self.client = self._create_agent_run_client()
298
+ self._get_agent_runtime_status_max_attempts = (
299
+ self.GET_AGENT_RUNTIME_STATUS_MAX_ATTEMPTS
300
+ )
301
+ self._get_agent_runtime_status_interval = (
302
+ self.GET_AGENT_RUNTIME_STATUS_INTERVAL
303
+ )
304
+
305
+ def _create_agent_run_client(self) -> AgentRunClient:
306
+ """Create and configure AgentRun SDK client.
307
+
308
+ Returns:
309
+ AgentRunClient: Configured client for AgentRun service API calls.
310
+ """
311
+ config = open_api_models.Config(
312
+ access_key_id=self.agentrun_config.access_key_id,
313
+ access_key_secret=self.agentrun_config.access_key_secret,
314
+ region_id=self.agentrun_config.region_id,
315
+ read_timeout=60 * 1000,
316
+ )
317
+ config.endpoint = (
318
+ f"agentrun.{self.agentrun_config.region_id}.aliyuncs.com"
319
+ )
320
+ return AgentRunClient(config)
321
+
322
+ def _adapt_code_config(
323
+ self,
324
+ config: Optional[CodeConfig],
325
+ ) -> Optional[CodeConfiguration]:
326
+ """Convert internal CodeConfig to SDK's CodeConfiguration format.
327
+
328
+ Args:
329
+ config: Internal code configuration object.
330
+
331
+ Returns:
332
+ SDK-compatible CodeConfiguration object, or None if input is None.
333
+ """
334
+ if config is None:
335
+ return None
336
+ return CodeConfiguration(
337
+ language="python3.12",
338
+ command=config.command,
339
+ oss_bucket_name=config.oss_bucket_name,
340
+ oss_object_name=config.oss_object_name,
341
+ )
342
+
343
+ def _adapt_log_config(
344
+ self,
345
+ config: Optional[LogConfig],
346
+ ) -> Optional[LogConfiguration]:
347
+ """Convert internal LogConfig to SDK's LogConfiguration format.
348
+
349
+ Args:
350
+ config: Internal log configuration object.
351
+
352
+ Returns:
353
+ SDK-compatible LogConfiguration object, or None if input is None.
354
+ """
355
+ if config is None:
356
+ return None
357
+ return LogConfiguration(
358
+ logstore=config.logstore,
359
+ project=config.project,
360
+ )
361
+
362
+ def _adapt_network_config(
363
+ self,
364
+ config: Optional[NetworkConfig],
365
+ ) -> Optional[NetworkConfiguration]:
366
+ """Convert internal NetworkConfig to SDK's NetworkConfiguration format.
367
+
368
+ Args:
369
+ config: Internal network configuration object.
370
+
371
+ Returns:
372
+ SDK-compatible NetworkConfiguration object, or None if input is None.
373
+ """
374
+ if config is None:
375
+ return None
376
+ return NetworkConfiguration(
377
+ network_mode=config.network_mode,
378
+ security_group_id=config.security_group_id,
379
+ vpc_id=config.vpc_id,
380
+ vswitch_ids=config.vswitch_ids,
381
+ )
382
+
383
+ async def _generate_wrapper_and_build_wheel(
384
+ self,
385
+ project_dir: Union[Optional[str], Path],
386
+ cmd: Optional[str] = None,
387
+ deploy_name: Optional[str] = None,
388
+ telemetry_enabled: bool = True,
389
+ ) -> Tuple[Path, str]:
390
+ """Generate wrapper project and build wheel package.
391
+
392
+ Args:
393
+ project_dir: Path to the user's project directory.
394
+ cmd: Command to start the agent application.
395
+ deploy_name: Name for the deployment. If None, generates default name.
396
+ telemetry_enabled: Whether to enable telemetry in the wrapper.
397
+
398
+ Returns:
399
+ Tuple containing:
400
+ - wheel_path: Path to the built wheel file
401
+ - name: Deployment name used
402
+
403
+ Raises:
404
+ ValueError: If project_dir or cmd is not provided.
405
+ FileNotFoundError: If project directory does not exist.
406
+ """
407
+ if not project_dir or not cmd:
408
+ raise ValueError(
409
+ "project_dir and cmd are required for deployment",
410
+ )
411
+
412
+ project_dir = Path(project_dir).resolve()
413
+ if not project_dir.is_dir():
414
+ raise FileNotFoundError(
415
+ f"Project directory not found: {project_dir}",
416
+ )
417
+
418
+ name = deploy_name or default_deploy_name()
419
+ proj_root = project_dir.resolve()
420
+ if isinstance(self.build_root, Path):
421
+ effective_build_root = self.build_root.resolve()
422
+ else:
423
+ if self.build_root:
424
+ effective_build_root = Path(self.build_root).resolve()
425
+ else:
426
+ effective_build_root = (
427
+ proj_root.parent / ".agentscope_runtime_builds"
428
+ ).resolve()
429
+
430
+ build_dir = effective_build_root / f"build-{int(time.time())}"
431
+ build_dir.mkdir(parents=True, exist_ok=True)
432
+
433
+ logger.info("Generating wrapper project: %s", name)
434
+ wrapper_project_dir, _ = generate_wrapper_project(
435
+ build_root=build_dir,
436
+ user_project_dir=project_dir,
437
+ start_cmd=cmd,
438
+ deploy_name=name,
439
+ telemetry_enabled=telemetry_enabled,
440
+ )
441
+
442
+ logger.info("Building wheel package from: %s", wrapper_project_dir)
443
+ wheel_path = build_wheel(wrapper_project_dir)
444
+ logger.info("Wheel package created: %s", wheel_path)
445
+ return wheel_path, name
446
+
447
+ def _generate_env_file(
448
+ self,
449
+ project_dir: Union[str, Path],
450
+ environment: Optional[Dict[str, str]] = None,
451
+ env_filename: str = ".env",
452
+ ) -> Optional[Path]:
453
+ """Generate .env file from environment variables dictionary.
454
+
455
+ Args:
456
+ project_dir: Project directory where the .env file will be created.
457
+ environment: Dictionary of environment variables to write to .env file.
458
+ env_filename: Name of the env file (default: ".env").
459
+
460
+ Returns:
461
+ Path to the created .env file, or None if no environment variables provided.
462
+
463
+ Raises:
464
+ FileNotFoundError: If project directory does not exist.
465
+ """
466
+ if not environment:
467
+ return None
468
+
469
+ project_path = Path(project_dir).resolve()
470
+ if not project_path.exists():
471
+ raise FileNotFoundError(
472
+ f"Project directory not found: {project_path}",
473
+ )
474
+
475
+ env_file_path = project_path / env_filename
476
+
477
+ try:
478
+ with env_file_path.open("w", encoding="utf-8") as f:
479
+ f.write("# Environment variables used by AgentScope Runtime\n")
480
+
481
+ for key, value in environment.items():
482
+ # Skip None values
483
+ if value is None:
484
+ continue
485
+
486
+ # Quote values that contain spaces or special characters
487
+ if " " in str(value) or any(
488
+ char in str(value)
489
+ for char in ["$", "`", '"', "'", "\\"]
490
+ ):
491
+ # Escape existing quotes and wrap in double quotes
492
+ escaped_value = (
493
+ str(value)
494
+ .replace("\\", "\\\\")
495
+ .replace('"', '\\"')
496
+ )
497
+ f.write(f'{key}="{escaped_value}"\n')
498
+ else:
499
+ f.write(f"{key}={value}\n")
500
+
501
+ logger.info("Environment file created: %s", env_file_path)
502
+ return env_file_path
503
+
504
+ except Exception as e:
505
+ logger.warning("Failed to create environment file: %s", e)
506
+ return None
507
+
508
+ async def deploy(
509
+ self,
510
+ runner=None,
511
+ endpoint_path: str = "/process",
512
+ protocol_adapters: Optional[list[ProtocolAdapter]] = None,
513
+ requirements: Optional[Union[str, List[str]]] = None,
514
+ extra_packages: Optional[List[str]] = None,
515
+ environment: Optional[Dict[str, str]] = None,
516
+ project_dir: Optional[Union[str, Path]] = None,
517
+ cmd: Optional[str] = None,
518
+ deploy_name: Optional[str] = None,
519
+ skip_upload: bool = False,
520
+ external_whl_path: Optional[str] = None,
521
+ agentrun_id: Optional[str] = None,
522
+ custom_endpoints: Optional[List[Dict]] = None,
523
+ app=None,
524
+ **kwargs,
525
+ ) -> Dict[str, str]:
526
+ """Deploy agent to AgentRun service.
527
+
528
+ Args:
529
+ app: AgentApp instance to deploy.
530
+ runner: Runner instance containing the agent to deploy.
531
+ endpoint_path: HTTP endpoint path for the agent service.
532
+ protocol_adapters: List of protocol adapters for the agent.
533
+ requirements: Python requirements for the agent (file path or list).
534
+ extra_packages: Additional Python packages to install.
535
+ environment: Environment variables for the runtime.
536
+ project_dir: Project directory to deploy.
537
+ cmd: Command to start the agent application.
538
+ deploy_name: Name for this deployment.
539
+ skip_upload: If True, skip uploading to AgentRun service.
540
+ external_whl_path: Path to pre-built wheel file.
541
+ agentrun_id: ID of existing agent to update.
542
+ custom_endpoints: Custom endpoints for the agent.
543
+ **kwargs: Additional deployment parameters.
544
+
545
+ Returns:
546
+ Dictionary containing deployment results with keys:
547
+ - success: Whether deployment succeeded
548
+ - message: Status message
549
+ - agentrun_id: ID of the agent runtime
550
+ - agentrun_endpoint_url: Public endpoint URL
551
+ - build_dir: Build directory path
552
+ - wheel_path: Path to wheel file
553
+ - artifact_url: OSS artifact URL
554
+ - url: Console URL for the deployment
555
+ - deploy_id: Deployment ID
556
+ - resource_name: Resource name
557
+
558
+ Raises:
559
+ ValueError: If required parameters are missing.
560
+ FileNotFoundError: If specified files/directories don't exist.
561
+ """
562
+ if not agentrun_id:
563
+ if not runner and not project_dir and not external_whl_path:
564
+ raise ValueError(
565
+ "Must provide either runner, project_dir, or external_whl_path",
566
+ )
567
+ try:
568
+ if runner or app:
569
+ logger.info("Creating detached project from runner")
570
+ if "agent" in kwargs:
571
+ kwargs.pop("agent")
572
+
573
+ # Create package project for detached deployment
574
+ project_dir = await LocalDeployManager.create_detached_project(
575
+ app=app,
576
+ runner=runner,
577
+ endpoint_path=endpoint_path,
578
+ custom_endpoints=custom_endpoints,
579
+ protocol_adapters=protocol_adapters,
580
+ requirements=requirements,
581
+ extra_packages=extra_packages,
582
+ **kwargs,
583
+ )
584
+ if project_dir:
585
+ self._generate_env_file(project_dir, environment)
586
+ entry_script = get_bundle_entry_script(project_dir)
587
+ cmd = f"python {entry_script}"
588
+ deploy_name = deploy_name or default_deploy_name()
589
+
590
+ if agentrun_id:
591
+ if not external_whl_path:
592
+ raise FileNotFoundError(
593
+ "Wheel file required for agent update. "
594
+ "Specify wheel file path with '--whl-path <path>'",
595
+ )
596
+ # Use external wheel if provided, skip project packaging
597
+ if external_whl_path:
598
+ wheel_path = Path(external_whl_path).resolve()
599
+ if not wheel_path.is_file():
600
+ raise FileNotFoundError(
601
+ f"External wheel file not found: {wheel_path}",
602
+ )
603
+ name = deploy_name or default_deploy_name()
604
+ # Keep existing name when updating agent without specifying deploy_name
605
+ if agentrun_id and (deploy_name is None):
606
+ name = None
607
+ logger.info("Using external wheel file: %s", wheel_path)
608
+ else:
609
+ logger.info("Building wheel package from project")
610
+ (
611
+ wheel_path,
612
+ name,
613
+ ) = await self._generate_wrapper_and_build_wheel(
614
+ project_dir=project_dir,
615
+ cmd=cmd,
616
+ deploy_name=deploy_name,
617
+ )
618
+ logger.info(
619
+ "Wheel file ready: %s (deploy name: %s)",
620
+ wheel_path,
621
+ name,
622
+ )
623
+
624
+ timestamp = time.strftime("%Y%m%d%H%M%S")
625
+
626
+ # Step 1: Build and package in Docker container
627
+ logger.info(
628
+ "Building dependencies and creating zip package in Docker",
629
+ )
630
+ zip_file_path = await self._build_and_zip_in_docker(
631
+ wheel_path=wheel_path,
632
+ output_dir=wheel_path.parent,
633
+ zip_filename=f"{name or agentrun_id}-{timestamp}.zip",
634
+ )
635
+ logger.info("Zip package created: %s", zip_file_path)
636
+
637
+ if skip_upload:
638
+ logger.info(
639
+ "Deployment completed (skipped upload to AgentRun)",
640
+ )
641
+ return {
642
+ "message": "Agent package built successfully (upload skipped)",
643
+ "deploy_name": name,
644
+ }
645
+
646
+ # Step 2: Upload to OSS
647
+ logger.info("Uploading zip package to OSS")
648
+ oss_result = await self._upload_to_fixed_oss_bucket(
649
+ zip_file_path=zip_file_path,
650
+ bucket_name="tmp-agentscope-agentrun-code",
651
+ )
652
+ logger.info("Zip package uploaded to OSS successfully")
653
+
654
+ # Deploy to AgentRun service
655
+ logger.info("Deploying to AgentRun service")
656
+ agentrun_deploy_result = await self.deploy_to_agentrun(
657
+ agentrun_id=agentrun_id,
658
+ agent_runtime_name=name,
659
+ oss_bucket_name=oss_result["bucket_name"],
660
+ oss_object_name=oss_result["object_key"],
661
+ environment=environment,
662
+ )
663
+
664
+ # Return deployment results
665
+ logger.info(
666
+ "Deployment completed successfully. Agent runtime ID: %s",
667
+ agentrun_deploy_result["agent_runtime_id"],
668
+ )
669
+ return {
670
+ "message": "Agent deployed successfully to AgentRun",
671
+ "agentrun_id": agentrun_deploy_result["agent_runtime_id"],
672
+ "agentrun_endpoint_url": agentrun_deploy_result[
673
+ "agent_runtime_public_endpoint_url"
674
+ ],
675
+ "wheel_path": str(wheel_path),
676
+ "artifact_url": oss_result["presigned_url"],
677
+ "url": f'https://functionai.console.aliyun.com/{self.agentrun_config.region_id}/agent/infra/agent-runtime/agent-detail?id={agentrun_deploy_result["agent_runtime_id"]}',
678
+ "deploy_id": agentrun_deploy_result["agent_runtime_id"],
679
+ "resource_name": name,
680
+ }
681
+
682
+ except Exception as e:
683
+ logger.error("Deployment failed: %s", str(e))
684
+ raise
685
+
686
+ async def _build_and_zip_in_docker(
687
+ self,
688
+ wheel_path: Path,
689
+ output_dir: Path,
690
+ zip_filename: str,
691
+ ) -> Path:
692
+ """Build dependencies and create zip package in Docker container.
693
+
694
+ All build logic runs in container, only final zip file is returned to host.
695
+
696
+ Args:
697
+ wheel_path: Path to the wheel file on host machine.
698
+ output_dir: Local directory to save the final zip file.
699
+ zip_filename: Name of the output zip file.
700
+
701
+ Returns:
702
+ Path to the created zip file.
703
+
704
+ Raises:
705
+ RuntimeError: If Docker is not available or build fails.
706
+ FileNotFoundError: If Docker is not installed.
707
+ """
708
+ import subprocess
709
+
710
+ try:
711
+ logger.info("Starting Docker build for wheel: %s", wheel_path)
712
+ logger.debug("Output directory: %s", output_dir)
713
+ logger.debug("Zip filename: %s", zip_filename)
714
+
715
+ # Ensure output directory exists
716
+ output_dir.mkdir(parents=True, exist_ok=True)
717
+
718
+ # Convert paths to absolute paths for Docker volume mounting
719
+ wheel_path_abs = wheel_path.resolve()
720
+ output_dir_abs = output_dir.resolve()
721
+
722
+ # Keep original wheel filename for pip to parse metadata
723
+ wheel_filename = wheel_path.name
724
+ wheel_path_in_container = f"/tmp/{wheel_filename}"
725
+
726
+ # Docker image to use
727
+ docker_image = "registry.cn-beijing.aliyuncs.com/aliyunfc/runtime:custom.debian11-build-3.1.0"
728
+
729
+ # Build script that runs in container:
730
+ # 1. Install wheel and dependencies to /tmp/python
731
+ # 2. Use Python's zipfile module to create zip
732
+ # 3. Save zip to /output
733
+ build_script = f"""
734
+ set -e
735
+ echo "=== Installing dependencies to /tmp/python ==="
736
+ pip install {wheel_path_in_container} -t /tmp/python --no-cache-dir
737
+
738
+ echo "=== Creating zip package using Python ==="
739
+ python3 << 'PYTHON_EOF'
740
+ import os
741
+ import zipfile
742
+ from pathlib import Path
743
+
744
+ python_dir = Path("/tmp/python")
745
+ zip_path = Path("/output/{zip_filename}")
746
+
747
+ print(f"Creating zip from {{python_dir}}")
748
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
749
+ for root, dirs, files in os.walk(python_dir):
750
+ for file in files:
751
+ file_path = Path(root) / file
752
+ arcname = file_path.relative_to(python_dir)
753
+ zipf.write(file_path, arcname)
754
+
755
+ zip_size_mb = zip_path.stat().st_size / (1024 * 1024)
756
+ print(f"Created zip ({{zip_size_mb:.2f}} MB): {{zip_path}}")
757
+ PYTHON_EOF
758
+
759
+ echo "=== Build complete ==="
760
+ ls -lh /output/{zip_filename}
761
+ """
762
+
763
+ # Docker run command with x86_64 platform for AgentRun compatibility
764
+ cmd = [
765
+ "docker",
766
+ "run",
767
+ "--rm",
768
+ "--platform",
769
+ "linux/amd64",
770
+ "-v",
771
+ f"{wheel_path_abs}:{wheel_path_in_container}:ro",
772
+ "-v",
773
+ f"{output_dir_abs}:/output",
774
+ docker_image,
775
+ "bash",
776
+ "-c",
777
+ build_script,
778
+ ]
779
+
780
+ logger.info("Executing Docker build command")
781
+ logger.debug("Build script:\n%s", build_script)
782
+
783
+ result = subprocess.run(
784
+ cmd,
785
+ capture_output=True,
786
+ text=True,
787
+ check=False,
788
+ )
789
+
790
+ if result.returncode != 0:
791
+ logger.error("Docker build failed: %s", result.stderr)
792
+ raise RuntimeError(
793
+ f"Docker build failed: {result.stderr}",
794
+ )
795
+
796
+ logger.info("Docker build completed successfully")
797
+ if result.stdout:
798
+ logger.debug("Docker output:\n%s", result.stdout)
799
+
800
+ # Verify zip file was created
801
+ zip_file_path = output_dir / zip_filename
802
+ if not zip_file_path.exists():
803
+ raise RuntimeError(f"Zip file not created: {zip_file_path}")
804
+
805
+ zip_size_mb = zip_file_path.stat().st_size / (1024 * 1024)
806
+ logger.info(
807
+ "Zip package created successfully (%.2f MB): %s",
808
+ zip_size_mb,
809
+ zip_file_path,
810
+ )
811
+
812
+ return zip_file_path
813
+
814
+ except FileNotFoundError as e:
815
+ if "docker" in str(e).lower():
816
+ logger.error(
817
+ "Docker is not installed or not available in PATH",
818
+ )
819
+ raise RuntimeError(
820
+ "Docker is required for building. "
821
+ "Install Docker Desktop: https://www.docker.com/products/docker-desktop",
822
+ ) from e
823
+ raise
824
+ except Exception as e:
825
+ logger.error("Error during Docker build: %s", str(e))
826
+ raise
827
+
828
+ async def _upload_to_fixed_oss_bucket(
829
+ self,
830
+ zip_file_path: Path,
831
+ bucket_name: str,
832
+ ) -> Dict[str, str]:
833
+ """Upload zip file to a fixed OSS bucket.
834
+
835
+ Args:
836
+ zip_file_path: Path to the zip file to upload.
837
+ bucket_name: Target OSS bucket name (e.g., "tmp-agentscope-agentrun-code").
838
+
839
+ Returns:
840
+ Dictionary containing:
841
+ - bucket_name: OSS bucket name
842
+ - object_key: Object key in OSS
843
+ - presigned_url: Presigned URL for downloading (valid for 3 hours)
844
+
845
+ Raises:
846
+ RuntimeError: If OSS SDK is not installed or upload fails.
847
+ """
848
+ try:
849
+ from alibabacloud_oss_v2 import Client as OSSClient
850
+ from alibabacloud_oss_v2.models import (
851
+ PutObjectRequest,
852
+ GetObjectRequest,
853
+ PutBucketRequest,
854
+ CreateBucketConfiguration,
855
+ PutBucketTagsRequest,
856
+ Tagging,
857
+ TagSet,
858
+ Tag,
859
+ )
860
+ from alibabacloud_oss_v2 import config as oss_config
861
+ from alibabacloud_oss_v2.credentials import (
862
+ StaticCredentialsProvider,
863
+ )
864
+ import datetime
865
+ except ImportError as e:
866
+ logger.error(
867
+ "OSS SDK not available. Install with: pip install alibabacloud-oss-v2",
868
+ )
869
+ raise RuntimeError(
870
+ "OSS SDK not installed. Run: pip install alibabacloud-oss-v2",
871
+ ) from e
872
+
873
+ # Create OSS client
874
+ logger.info("Initializing OSS client")
875
+
876
+ credentials_provider = StaticCredentialsProvider(
877
+ access_key_id=self.oss_config.access_key_id,
878
+ access_key_secret=self.oss_config.access_key_secret,
879
+ )
880
+
881
+ cfg = oss_config.Config(
882
+ credentials_provider=credentials_provider,
883
+ region=self.oss_config.region,
884
+ )
885
+ oss_client = OSSClient(cfg)
886
+
887
+ logger.info("Using OSS bucket: %s", bucket_name)
888
+
889
+ # Create bucket if not exists
890
+ try:
891
+ bucket_exists = oss_client.is_bucket_exist(bucket=bucket_name)
892
+ except Exception:
893
+ bucket_exists = False
894
+
895
+ if not bucket_exists:
896
+ logger.info("OSS bucket does not exist, creating: %s", bucket_name)
897
+ try:
898
+ put_bucket_req = PutBucketRequest(
899
+ bucket=bucket_name,
900
+ acl="private",
901
+ create_bucket_configuration=CreateBucketConfiguration(
902
+ storage_class="IA",
903
+ ),
904
+ )
905
+ put_bucket_result = oss_client.put_bucket(put_bucket_req)
906
+ logger.info(
907
+ "OSS bucket created (Status: %s, Request ID: %s)",
908
+ put_bucket_result.status_code,
909
+ put_bucket_result.request_id,
910
+ )
911
+
912
+ # Add tag for AgentRun access permission
913
+ tag_result = oss_client.put_bucket_tags(
914
+ PutBucketTagsRequest(
915
+ bucket=bucket_name,
916
+ tagging=Tagging(
917
+ tag_set=TagSet(
918
+ tags=[
919
+ Tag(
920
+ key="agentrun-deploy-access",
921
+ value="ReadAndAdd",
922
+ ),
923
+ ],
924
+ ),
925
+ ),
926
+ ),
927
+ )
928
+ logger.info(
929
+ "OSS bucket tags configured (Status: %s)",
930
+ tag_result.status_code,
931
+ )
932
+ except Exception as e:
933
+ logger.error("Failed to create OSS bucket: %s", str(e))
934
+ raise
935
+ else:
936
+ logger.debug("OSS bucket already exists: %s", bucket_name)
937
+
938
+ # Upload zip file
939
+ object_key = zip_file_path.name
940
+ logger.info("Uploading to OSS: %s", object_key)
941
+
942
+ try:
943
+ with open(zip_file_path, "rb") as f:
944
+ file_bytes = f.read()
945
+
946
+ put_obj_req = PutObjectRequest(
947
+ bucket=bucket_name,
948
+ key=object_key,
949
+ body=file_bytes,
950
+ )
951
+ put_obj_result = oss_client.put_object(put_obj_req)
952
+ logger.info(
953
+ "File uploaded to OSS successfully (Status: %s)",
954
+ put_obj_result.status_code,
955
+ )
956
+ except Exception as e:
957
+ logger.error("Failed to upload file to OSS: %s", str(e))
958
+ raise RuntimeError(
959
+ f"Failed to upload file to OSS: {str(e)}",
960
+ ) from e
961
+
962
+ # Generate presigned URL (valid for 3 hours)
963
+ logger.info("Generating presigned URL for artifact")
964
+ try:
965
+ presign_result = oss_client.presign(
966
+ GetObjectRequest(bucket=bucket_name, key=object_key),
967
+ expires=datetime.timedelta(hours=3),
968
+ )
969
+ presigned_url = presign_result.url
970
+ logger.info("Presigned URL generated (valid for 3 hours)")
971
+ except Exception as e:
972
+ logger.error("Failed to generate presigned URL: %s", str(e))
973
+ raise RuntimeError(
974
+ f"Failed to generate presigned URL: {str(e)}",
975
+ ) from e
976
+
977
+ return {
978
+ "bucket_name": bucket_name,
979
+ "object_key": object_key,
980
+ "presigned_url": presigned_url,
981
+ }
982
+
983
+ async def deploy_to_agentrun(
984
+ self,
985
+ agent_runtime_name: str,
986
+ oss_bucket_name: str,
987
+ oss_object_name: str,
988
+ agentrun_id: Optional[str] = None,
989
+ environment: Optional[Dict[str, str]] = None,
990
+ ):
991
+ """Deploy agent runtime and endpoint to AgentRun service.
992
+
993
+ Args:
994
+ agent_runtime_name: Name for the agent runtime.
995
+ oss_bucket_name: OSS bucket containing the code artifact.
996
+ oss_object_name: Object key of the code artifact in OSS.
997
+ agentrun_id: AgentRun ID to update deploy to.
998
+ environment: Environment variables for the runtime.
999
+
1000
+ Returns:
1001
+ Dictionary containing deployment results:
1002
+ - success: Whether deployment succeeded
1003
+ - agent_runtime_id: ID of the created runtime
1004
+ - agent_runtime_endpoint_id: ID of the created endpoint
1005
+ - agent_runtime_endpoint_name: Name of the endpoint
1006
+ - agent_runtime_public_endpoint_url: Public URL of the endpoint
1007
+ - status: Status of the deployment
1008
+ - request_id: Request ID for tracking
1009
+ """
1010
+ try:
1011
+ logger.info("Starting AgentRun deployment: %s", agent_runtime_name)
1012
+
1013
+ if agentrun_id:
1014
+ # Update existing agent runtime
1015
+ logger.info(
1016
+ "Updating agent runtime: %s (ID: %s)",
1017
+ agent_runtime_name,
1018
+ agentrun_id,
1019
+ )
1020
+ update_agent_runtime_resp = await self.update_agent_runtime(
1021
+ agent_runtime_id=agentrun_id,
1022
+ artifact_type="Code",
1023
+ cpu=self.agentrun_config.cpu,
1024
+ memory=self.agentrun_config.memory,
1025
+ port=8090,
1026
+ code_configuration=CodeConfig(
1027
+ command=["python3", "/code/deploy_starter/main.py"],
1028
+ oss_bucket_name=oss_bucket_name,
1029
+ oss_object_name=oss_object_name,
1030
+ ),
1031
+ description=f"AgentScope auto-generated runtime for {agent_runtime_name}",
1032
+ environment_variables=environment,
1033
+ )
1034
+
1035
+ # Verify runtime update
1036
+ if not update_agent_runtime_resp.get("success"):
1037
+ logger.error(
1038
+ "Failed to update agent runtime: %s",
1039
+ update_agent_runtime_resp.get("message"),
1040
+ )
1041
+ return update_agent_runtime_resp
1042
+
1043
+ logger.info(
1044
+ "Listing agent runtime endpoints to find '%s'",
1045
+ self.DEFAULT_ENDPOINT_NAME,
1046
+ )
1047
+ endpoint_id = None
1048
+ endpoint_name = None
1049
+ endpoint_url = None
1050
+ endpoint_status = None
1051
+
1052
+ try:
1053
+ list_endpoints_request = ListAgentRuntimeEndpointsRequest()
1054
+ list_endpoints_response = (
1055
+ await self.client.list_agent_runtime_endpoints_async(
1056
+ agentrun_id,
1057
+ list_endpoints_request,
1058
+ )
1059
+ )
1060
+
1061
+ if (
1062
+ list_endpoints_response.body
1063
+ and list_endpoints_response.body.code == "SUCCESS"
1064
+ and list_endpoints_response.body.data
1065
+ ):
1066
+ endpoints = (
1067
+ list_endpoints_response.body.data.items
1068
+ if hasattr(
1069
+ list_endpoints_response.body.data,
1070
+ "items",
1071
+ )
1072
+ else []
1073
+ )
1074
+
1075
+ for endpoint in endpoints:
1076
+ if (
1077
+ hasattr(
1078
+ endpoint,
1079
+ "agent_runtime_endpoint_name",
1080
+ )
1081
+ and endpoint.agent_runtime_endpoint_name
1082
+ == self.DEFAULT_ENDPOINT_NAME
1083
+ ):
1084
+ endpoint_id = (
1085
+ endpoint.agent_runtime_endpoint_id
1086
+ if hasattr(
1087
+ endpoint,
1088
+ "agent_runtime_endpoint_id",
1089
+ )
1090
+ else None
1091
+ )
1092
+ endpoint_name = (
1093
+ endpoint.agent_runtime_endpoint_name
1094
+ )
1095
+ endpoint_url = (
1096
+ endpoint.endpoint_public_url
1097
+ if hasattr(
1098
+ endpoint,
1099
+ "endpoint_public_url",
1100
+ )
1101
+ else None
1102
+ )
1103
+ endpoint_status = (
1104
+ endpoint.status
1105
+ if hasattr(
1106
+ endpoint,
1107
+ "status",
1108
+ )
1109
+ else None
1110
+ )
1111
+
1112
+ logger.info(
1113
+ "Found endpoint (ID: %s, Name: %s, URL: %s, Status: %s)",
1114
+ endpoint_id,
1115
+ endpoint_name,
1116
+ endpoint_url,
1117
+ endpoint_status,
1118
+ )
1119
+ break
1120
+
1121
+ if not endpoint_id:
1122
+ logger.warning(
1123
+ "Endpoint with name '%s' not found in list",
1124
+ self.DEFAULT_ENDPOINT_NAME,
1125
+ )
1126
+ else:
1127
+ logger.warning(
1128
+ "Failed to list agent runtime endpoints: %s",
1129
+ list_endpoints_response.body.message
1130
+ if list_endpoints_response.body
1131
+ else "Unknown error",
1132
+ )
1133
+ except Exception as e:
1134
+ logger.warning(
1135
+ "Exception occurred while listing endpoints: %s",
1136
+ str(e),
1137
+ )
1138
+
1139
+ result = {
1140
+ "success": True,
1141
+ "agent_runtime_id": agentrun_id,
1142
+ "agent_runtime_endpoint_id": endpoint_id,
1143
+ "agent_runtime_endpoint_name": endpoint_name,
1144
+ "agent_runtime_public_endpoint_url": endpoint_url,
1145
+ "status": endpoint_status
1146
+ or update_agent_runtime_resp.get("status"),
1147
+ "request_id": update_agent_runtime_resp.get("request_id"),
1148
+ "deploy_id": self.deploy_id
1149
+ if hasattr(self, "deploy_id")
1150
+ else None,
1151
+ }
1152
+
1153
+ return result
1154
+
1155
+ # Create new agent runtime
1156
+ logger.info("Creating agent runtime: %s", agent_runtime_name)
1157
+ create_agent_runtime_resp = await self.create_agent_runtime(
1158
+ agent_runtime_name=agent_runtime_name,
1159
+ artifact_type="Code",
1160
+ cpu=self.agentrun_config.cpu,
1161
+ memory=self.agentrun_config.memory,
1162
+ port=8090,
1163
+ code_configuration=CodeConfig(
1164
+ command=["python3", "/code/deploy_starter/main.py"],
1165
+ oss_bucket_name=oss_bucket_name,
1166
+ oss_object_name=oss_object_name,
1167
+ ),
1168
+ description=f"AgentScope auto-generated runtime for {agent_runtime_name}",
1169
+ environment_variables=environment,
1170
+ execution_role_arn=self.agentrun_config.execution_role_arn,
1171
+ log_configuration=self.agentrun_config.log_config,
1172
+ network_configuration=self.agentrun_config.network_config,
1173
+ session_concurrency_limit_per_instance=self.agentrun_config.session_concurrency_limit,
1174
+ session_idle_timeout_seconds=self.agentrun_config.session_idle_timeout_seconds,
1175
+ )
1176
+
1177
+ # Verify runtime creation
1178
+ if not create_agent_runtime_resp.get("success"):
1179
+ logger.error(
1180
+ "Failed to create agent runtime: %s",
1181
+ create_agent_runtime_resp.get("message"),
1182
+ )
1183
+ return create_agent_runtime_resp
1184
+
1185
+ agent_runtime_id = create_agent_runtime_resp["agent_runtime_id"]
1186
+ logger.info(
1187
+ "Agent runtime created successfully (ID: %s)",
1188
+ agent_runtime_id,
1189
+ )
1190
+
1191
+ # Step 2: Create agent runtime endpoint
1192
+ logger.info("Creating agent runtime endpoint")
1193
+ endpoint_config = EndpointConfig(
1194
+ agent_runtime_endpoint_name=self.DEFAULT_ENDPOINT_NAME,
1195
+ target_version=self.LATEST_VERSION,
1196
+ description=f"AgentScope auto-generated endpoint for {agent_runtime_name}",
1197
+ )
1198
+
1199
+ create_agent_runtime_endpoint_resp = (
1200
+ await self.create_agent_runtime_endpoint(
1201
+ agent_runtime_id=agent_runtime_id,
1202
+ endpoint_config=endpoint_config,
1203
+ )
1204
+ )
1205
+
1206
+ # Verify endpoint creation
1207
+ if not create_agent_runtime_endpoint_resp.get("success"):
1208
+ logger.error(
1209
+ "Failed to create agent runtime endpoint: %s",
1210
+ create_agent_runtime_endpoint_resp.get("message"),
1211
+ )
1212
+ return create_agent_runtime_endpoint_resp
1213
+
1214
+ endpoint_id = create_agent_runtime_endpoint_resp.get(
1215
+ "agent_runtime_endpoint_id",
1216
+ )
1217
+ logger.info(
1218
+ "Agent runtime endpoint created successfully (ID: %s)",
1219
+ endpoint_id,
1220
+ )
1221
+
1222
+ # Return success result
1223
+ logger.info(
1224
+ "AgentRun deployment completed successfully: %s",
1225
+ agent_runtime_name,
1226
+ )
1227
+ result = {
1228
+ "success": True,
1229
+ "agent_runtime_id": agent_runtime_id,
1230
+ "agent_runtime_endpoint_id": create_agent_runtime_endpoint_resp.get(
1231
+ "agent_runtime_endpoint_id",
1232
+ ),
1233
+ "agent_runtime_endpoint_name": create_agent_runtime_endpoint_resp.get(
1234
+ "agent_runtime_endpoint_name",
1235
+ ),
1236
+ "agent_runtime_public_endpoint_url": create_agent_runtime_endpoint_resp.get(
1237
+ "agent_runtime_public_endpoint_url",
1238
+ ),
1239
+ "status": create_agent_runtime_endpoint_resp.get("status"),
1240
+ "request_id": create_agent_runtime_endpoint_resp.get(
1241
+ "request_id",
1242
+ ),
1243
+ "deploy_id": self.deploy_id
1244
+ if hasattr(self, "deploy_id")
1245
+ else None,
1246
+ }
1247
+
1248
+ return result
1249
+
1250
+ except Exception as e:
1251
+ logger.error("Exception during AgentRun deployment: %s", str(e))
1252
+ return {
1253
+ "success": False,
1254
+ "error": str(e),
1255
+ "message": f"Exception during AgentRun deployment: {str(e)}",
1256
+ }
1257
+
1258
+ async def delete(self, agent_runtime_id: str):
1259
+ """
1260
+ Delete an agent runtime on AgentRun.
1261
+
1262
+ Args:
1263
+ agent_runtime_id (str): The ID of the agent runtime to delete.
1264
+
1265
+ Returns:
1266
+ Dict[str, Any]: A dictionary containing the delete result with:
1267
+ - success (bool): Whether the operation was successful
1268
+ - message (str): Status message
1269
+ - agent_runtime_id (str): The ID of the deleted agent runtime
1270
+ - status (str): The status of the agent runtime
1271
+ - status_reason (str): The reason for the status
1272
+ - request_id (str): The request ID for tracking
1273
+ """
1274
+ try:
1275
+ logger.info(
1276
+ f"Deleting agent runtime with ID: {agent_runtime_id}",
1277
+ )
1278
+
1279
+ # Call the SDK method
1280
+ response = await self.client.delete_agent_runtime_async(
1281
+ agent_runtime_id,
1282
+ )
1283
+
1284
+ # Check if the response is successful
1285
+ if response.body and response.body.code == "SUCCESS":
1286
+ logger.info(
1287
+ "Agent runtime deletion initiated (ID: %s)",
1288
+ agent_runtime_id,
1289
+ )
1290
+
1291
+ # Poll for status
1292
+ status_result = None
1293
+ status_reason = None
1294
+ if agent_runtime_id:
1295
+ logger.info(
1296
+ "Polling deletion status for agent runtime: %s",
1297
+ agent_runtime_id,
1298
+ )
1299
+ poll_status = await self._poll_agent_runtime_status(
1300
+ agent_runtime_id,
1301
+ )
1302
+ if isinstance(poll_status, dict):
1303
+ status_result = poll_status.get("status")
1304
+ status_reason = poll_status.get("status_reason")
1305
+ logger.info(
1306
+ "Agent runtime deletion status: %s",
1307
+ status_result,
1308
+ )
1309
+
1310
+ # Return a dictionary with relevant information from the response
1311
+ return {
1312
+ "success": True,
1313
+ "message": "Agent runtime deletion initiated successfully",
1314
+ "agent_runtime_id": agent_runtime_id,
1315
+ "status": status_result,
1316
+ "status_reason": status_reason,
1317
+ "request_id": response.body.request_id,
1318
+ }
1319
+ else:
1320
+ logger.error("Failed to delete agent runtime")
1321
+ # Return error information if the request was not successful
1322
+ return {
1323
+ "success": False,
1324
+ "code": response.body.code if response.body else None,
1325
+ "message": "Failed to delete agent runtime",
1326
+ "request_id": response.body.request_id
1327
+ if response.body
1328
+ else None,
1329
+ }
1330
+ except Exception as e:
1331
+ logger.error(
1332
+ f"Exception occurred while deleting agent runtime: {str(e)}",
1333
+ )
1334
+ # Return error information if an exception occurred
1335
+ return {
1336
+ "success": False,
1337
+ "error": str(e),
1338
+ "message": f"Exception occurred while deleting agent runtime: {str(e)}",
1339
+ }
1340
+
1341
+ async def get_agent_runtime(
1342
+ self,
1343
+ agent_runtime_id: str,
1344
+ agent_runtime_version: str = None,
1345
+ ):
1346
+ """
1347
+ Get agent runtime details.
1348
+
1349
+ Args:
1350
+ agent_runtime_id (str): The ID of the agent runtime.
1351
+ agent_runtime_version (str, optional): The version of the agent runtime.
1352
+
1353
+ Returns:
1354
+ Dict[str, Any]: A dictionary containing the agent runtime details with:
1355
+ - success (bool): Whether the operation was successful
1356
+ - data (dict): The agent runtime data
1357
+ - request_id (str): The request ID for tracking
1358
+ """
1359
+ try:
1360
+ logger.info(
1361
+ f"Getting agent runtime details for ID: {agent_runtime_id}",
1362
+ )
1363
+
1364
+ # Create the request object
1365
+ request = GetAgentRuntimeRequest(
1366
+ agent_runtime_version=agent_runtime_version,
1367
+ )
1368
+
1369
+ # Call the SDK method
1370
+ response = await self.client.get_agent_runtime_async(
1371
+ agent_runtime_id,
1372
+ request,
1373
+ )
1374
+
1375
+ # Check if the response is successful
1376
+ if response.body and response.body.code == "SUCCESS":
1377
+ logger.info(
1378
+ "Agent runtime details retrieved successfully (ID: %s)",
1379
+ agent_runtime_id,
1380
+ )
1381
+ # Return the agent runtime data as a dictionary
1382
+ agent_runtime_data = (
1383
+ response.body.data.to_map() if response.body.data else {}
1384
+ )
1385
+ return {
1386
+ "success": True,
1387
+ "data": agent_runtime_data,
1388
+ "request_id": response.body.request_id,
1389
+ }
1390
+ else:
1391
+ logger.error("Failed to get agent runtime details")
1392
+ # Return error information if the request was not successful
1393
+ return {
1394
+ "success": False,
1395
+ "code": response.body.code if response.body else None,
1396
+ "message": "Failed to get agent runtime details",
1397
+ "request_id": response.body.request_id
1398
+ if response.body
1399
+ else None,
1400
+ }
1401
+ except Exception as e:
1402
+ logger.error(
1403
+ f"Exception occurred while getting agent runtime: {str(e)}",
1404
+ )
1405
+ # Return error information if an exception occurred
1406
+ return {
1407
+ "success": False,
1408
+ "error": str(e),
1409
+ "message": f"Exception occurred while getting agent runtime: {str(e)}",
1410
+ }
1411
+
1412
+ async def _get_agent_runtime_status(
1413
+ self,
1414
+ agent_runtime_id: str,
1415
+ agent_runtime_version: str = None,
1416
+ ):
1417
+ """
1418
+ Get agent runtime status.
1419
+
1420
+ Args:
1421
+ agent_runtime_id (str): The ID of the agent runtime.
1422
+ agent_runtime_version (str, optional): The version of the agent runtime.
1423
+
1424
+ Returns:
1425
+ Dict[str, Any]: A dictionary containing the agent runtime status with:
1426
+ - success (bool): Whether the operation was successful
1427
+ - status (str): The status of the agent runtime
1428
+ - status_reason (str): The reason for the status
1429
+ - request_id (str): The request ID for tracking
1430
+ """
1431
+ try:
1432
+ logger.debug(
1433
+ f"Getting agent runtime status for ID: {agent_runtime_id}",
1434
+ )
1435
+
1436
+ # Create the request object
1437
+ request = GetAgentRuntimeRequest(
1438
+ agent_runtime_version=agent_runtime_version,
1439
+ )
1440
+
1441
+ # Call the SDK method
1442
+ response = await self.client.get_agent_runtime_async(
1443
+ agent_runtime_id,
1444
+ request,
1445
+ )
1446
+
1447
+ # Check if the response is successful
1448
+ if (
1449
+ response.body
1450
+ and response.body.code == "SUCCESS"
1451
+ and response.body.data
1452
+ ):
1453
+ status = (
1454
+ response.body.data.status
1455
+ if hasattr(response.body.data, "status")
1456
+ else None
1457
+ )
1458
+ logger.debug(
1459
+ f"Agent runtime status for ID {agent_runtime_id}: {status}",
1460
+ )
1461
+ # Return the status from the agent runtime data
1462
+ return {
1463
+ "success": True,
1464
+ "status": status,
1465
+ "status_reason": response.body.data.status_reason
1466
+ if hasattr(response.body.data, "status_reason")
1467
+ else None,
1468
+ "request_id": response.body.request_id,
1469
+ }
1470
+ else:
1471
+ logger.debug("Failed to get agent runtime status")
1472
+ # Return error information if the request was not successful
1473
+ return {
1474
+ "success": False,
1475
+ "code": response.body.code if response.body else None,
1476
+ "message": "Failed to get agent runtime status",
1477
+ "request_id": response.body.request_id
1478
+ if response.body
1479
+ else None,
1480
+ }
1481
+ except Exception as e:
1482
+ logger.debug(
1483
+ f"Exception occurred while getting agent runtime status: {str(e)}",
1484
+ )
1485
+ # Return error information if an exception occurred
1486
+ return {
1487
+ "success": False,
1488
+ "error": str(e),
1489
+ "message": f"Exception occurred while getting agent runtime status: {str(e)}",
1490
+ }
1491
+
1492
+ async def _get_agent_runtime_endpoint_status(
1493
+ self,
1494
+ agent_runtime_id: str,
1495
+ agent_runtime_endpoint_id: str,
1496
+ ):
1497
+ """
1498
+ Get agent runtime endpoint status.
1499
+
1500
+ Args:
1501
+ agent_runtime_id (str): The ID of the agent runtime.
1502
+ agent_runtime_endpoint_id (str): The ID of the agent runtime endpoint.
1503
+
1504
+ Returns:
1505
+ Dict[str, Any]: A dictionary containing the agent runtime endpoint status with:
1506
+ - success (bool): Whether the operation was successful
1507
+ - status (str): The status of the agent runtime endpoint
1508
+ - status_reason (str): The reason for the status
1509
+ - request_id (str): The request ID for tracking
1510
+ """
1511
+ try:
1512
+ logger.debug(
1513
+ f"Getting agent runtime endpoint status for ID: {agent_runtime_endpoint_id}",
1514
+ )
1515
+
1516
+ # Call the SDK method
1517
+ response = await self.client.get_agent_runtime_endpoint_async(
1518
+ agent_runtime_id,
1519
+ agent_runtime_endpoint_id,
1520
+ )
1521
+
1522
+ # Check if the response is successful
1523
+ if (
1524
+ response.body
1525
+ and response.body.code == "SUCCESS"
1526
+ and response.body.data
1527
+ ):
1528
+ status = (
1529
+ response.body.data.status
1530
+ if hasattr(response.body.data, "status")
1531
+ else None
1532
+ )
1533
+ logger.debug(
1534
+ f"Agent runtime endpoint status for ID {agent_runtime_endpoint_id}: {status}",
1535
+ )
1536
+ # Return the status from the agent runtime endpoint data
1537
+ return {
1538
+ "success": True,
1539
+ "status": status,
1540
+ "status_reason": response.body.data.status_reason
1541
+ if hasattr(response.body.data, "status_reason")
1542
+ else None,
1543
+ "request_id": response.body.request_id,
1544
+ }
1545
+ else:
1546
+ logger.debug(
1547
+ "Failed to get agent runtime endpoint status",
1548
+ )
1549
+ # Return error information if the request was not successful
1550
+ return {
1551
+ "success": False,
1552
+ "code": response.body.code if response.body else None,
1553
+ "message": "Failed to get agent runtime endpoint status",
1554
+ "request_id": response.body.request_id
1555
+ if response.body
1556
+ else None,
1557
+ }
1558
+ except Exception as e:
1559
+ logger.debug(
1560
+ f"Exception occurred while getting agent runtime endpoint status: {str(e)}",
1561
+ )
1562
+ # Return error information if an exception occurred
1563
+ return {
1564
+ "success": False,
1565
+ "error": str(e),
1566
+ "message": f"Exception occurred while getting agent runtime endpoint status: {str(e)}",
1567
+ }
1568
+
1569
+ async def _poll_agent_runtime_status(
1570
+ self,
1571
+ agent_runtime_id: str,
1572
+ agent_runtime_version: str = None,
1573
+ ) -> Dict[str, Any]:
1574
+ """
1575
+ Poll agent runtime status until a terminal state is reached or max attempts exceeded.
1576
+
1577
+ Args:
1578
+ agent_runtime_id (str): The ID of the agent runtime.
1579
+ agent_runtime_version (str, optional): The version of the agent runtime.
1580
+
1581
+ Returns:
1582
+ Dict[str, Any]: A dictionary containing the final agent runtime status with:
1583
+ - success (bool): Whether the operation was successful
1584
+ - status (str): The final status of the agent runtime
1585
+ - status_reason (str): The reason for the status
1586
+ - request_id (str): The request ID for tracking
1587
+ """
1588
+ # Terminal states that indicate the end of polling for agent runtimes
1589
+ terminal_states = {
1590
+ "CREATE_FAILED",
1591
+ "UPDATE_FAILED",
1592
+ "READY",
1593
+ "ACTIVE",
1594
+ "FAILED",
1595
+ "DELETING",
1596
+ }
1597
+
1598
+ # Polling configuration
1599
+ max_attempts = self._get_agent_runtime_status_max_attempts
1600
+ interval_seconds = self._get_agent_runtime_status_interval
1601
+
1602
+ logger.info("Polling agent runtime status (ID: %s)", agent_runtime_id)
1603
+
1604
+ for attempt in range(1, max_attempts + 1):
1605
+ # Get current status
1606
+ status_response = await self._get_agent_runtime_status(
1607
+ agent_runtime_id,
1608
+ agent_runtime_version,
1609
+ )
1610
+
1611
+ # Check if the request was successful
1612
+ if not status_response.get("success"):
1613
+ logger.warning(
1614
+ "Status poll attempt %d/%d failed: %s",
1615
+ attempt,
1616
+ max_attempts,
1617
+ status_response.get("message"),
1618
+ )
1619
+ # Wait before next attempt unless this is the last attempt
1620
+ if attempt < max_attempts:
1621
+ await asyncio.sleep(interval_seconds)
1622
+ continue
1623
+
1624
+ # Extract status information
1625
+ current_status = status_response.get("status")
1626
+ status_reason = status_response.get("status_reason")
1627
+
1628
+ # Log current status
1629
+ logger.debug(
1630
+ "Status poll attempt %d/%d: %s",
1631
+ attempt,
1632
+ max_attempts,
1633
+ current_status,
1634
+ )
1635
+ if status_reason:
1636
+ logger.debug("Status reason: %s", status_reason)
1637
+
1638
+ # Check if we've reached a terminal state
1639
+ if current_status in terminal_states:
1640
+ logger.info(
1641
+ "Agent runtime reached terminal state '%s' (after %d attempts)",
1642
+ current_status,
1643
+ attempt,
1644
+ )
1645
+ return status_response
1646
+
1647
+ # Wait before next attempt unless this is the last attempt
1648
+ if attempt < max_attempts:
1649
+ await asyncio.sleep(interval_seconds)
1650
+
1651
+ # If we've exhausted all attempts without reaching a terminal state
1652
+ logger.warning(
1653
+ "Status polling exceeded maximum attempts (%d) without reaching terminal state",
1654
+ max_attempts,
1655
+ )
1656
+ return await self._get_agent_runtime_status(
1657
+ agent_runtime_id,
1658
+ agent_runtime_version,
1659
+ )
1660
+
1661
+ async def _poll_agent_runtime_endpoint_status(
1662
+ self,
1663
+ agent_runtime_id: str,
1664
+ agent_runtime_endpoint_id: str,
1665
+ ) -> Dict[str, Any]:
1666
+ """
1667
+ Poll agent runtime endpoint status until a terminal state is reached or max attempts exceeded.
1668
+
1669
+ Args:
1670
+ agent_runtime_id (str): The ID of the agent runtime.
1671
+ agent_runtime_endpoint_id (str): The ID of the agent runtime endpoint.
1672
+
1673
+ Returns:
1674
+ Dict[str, Any]: A dictionary containing the final agent runtime endpoint status with:
1675
+ - success (bool): Whether the operation was successful
1676
+ - status (str): The final status of the agent runtime endpoint
1677
+ - status_reason (str): The reason for the status
1678
+ - request_id (str): The request ID for tracking
1679
+ """
1680
+ # Terminal states that indicate the end of polling for endpoints
1681
+ terminal_states = {
1682
+ "CREATE_FAILED",
1683
+ "UPDATE_FAILED",
1684
+ "READY",
1685
+ "ACTIVE",
1686
+ "FAILED",
1687
+ "DELETING",
1688
+ }
1689
+
1690
+ # Polling configuration
1691
+ max_attempts = self._get_agent_runtime_status_max_attempts
1692
+ interval_seconds = self._get_agent_runtime_status_interval
1693
+
1694
+ logger.info(
1695
+ "Polling agent runtime endpoint status (ID: %s)",
1696
+ agent_runtime_endpoint_id,
1697
+ )
1698
+
1699
+ for attempt in range(1, max_attempts + 1):
1700
+ # Get current status
1701
+ status_response = await self._get_agent_runtime_endpoint_status(
1702
+ agent_runtime_id,
1703
+ agent_runtime_endpoint_id,
1704
+ )
1705
+
1706
+ # Check if the request was successful
1707
+ if not status_response.get("success"):
1708
+ logger.warning(
1709
+ "Endpoint status poll attempt %d/%d failed: %s",
1710
+ attempt,
1711
+ max_attempts,
1712
+ status_response.get("message"),
1713
+ )
1714
+ # Wait before next attempt unless this is the last attempt
1715
+ if attempt < max_attempts:
1716
+ await asyncio.sleep(interval_seconds)
1717
+ continue
1718
+
1719
+ # Extract status information
1720
+ current_status = status_response.get("status")
1721
+ status_reason = status_response.get("status_reason")
1722
+
1723
+ # Log current status
1724
+ logger.debug(
1725
+ "Endpoint status poll attempt %d/%d: %s",
1726
+ attempt,
1727
+ max_attempts,
1728
+ current_status,
1729
+ )
1730
+ if status_reason:
1731
+ logger.debug("Status reason: %s", status_reason)
1732
+
1733
+ # Check if we've reached a terminal state
1734
+ if current_status in terminal_states:
1735
+ logger.info(
1736
+ "Endpoint reached terminal state '%s' (after %d attempts)",
1737
+ current_status,
1738
+ attempt,
1739
+ )
1740
+ return status_response
1741
+
1742
+ # Wait before next attempt unless this is the last attempt
1743
+ if attempt < max_attempts:
1744
+ await asyncio.sleep(interval_seconds)
1745
+
1746
+ # If we've exhausted all attempts without reaching a terminal state
1747
+ logger.warning(
1748
+ "Endpoint status polling exceeded maximum attempts (%d) without reaching terminal state",
1749
+ max_attempts,
1750
+ )
1751
+ return await self._get_agent_runtime_endpoint_status(
1752
+ agent_runtime_id,
1753
+ agent_runtime_endpoint_id,
1754
+ )
1755
+
1756
+ async def create_agent_runtime(
1757
+ self,
1758
+ agent_runtime_name: str,
1759
+ artifact_type: str,
1760
+ cpu: float,
1761
+ memory: int,
1762
+ port: int,
1763
+ code_configuration: Optional[CodeConfig] = None,
1764
+ description: Optional[str] = None,
1765
+ environment_variables: Optional[Dict[str, str]] = None,
1766
+ execution_role_arn: Optional[str] = None,
1767
+ log_configuration: Optional[LogConfig] = None,
1768
+ network_configuration: Optional[NetworkConfig] = None,
1769
+ session_concurrency_limit_per_instance: Optional[int] = None,
1770
+ session_idle_timeout_seconds: Optional[int] = None,
1771
+ ):
1772
+ """
1773
+ Create an agent runtime on AgentRun.
1774
+
1775
+ Args:
1776
+ agent_runtime_name (str): The name of the agent runtime.
1777
+ artifact_type (str): The type of the artifact.
1778
+ cpu (float): The CPU allocated to the runtime.
1779
+ memory (int): The memory allocated to the runtime.
1780
+ port (int): The port on which the runtime will listen.
1781
+ code_configuration (Optional[CodeConfig]): Configuration for code-based runtimes.
1782
+ description (Optional[str]): Description of the agent runtime.
1783
+ environment_variables (Optional[Dict[str, str]]): Environment variables for the runtime.
1784
+ execution_role_arn (Optional[str]): The execution role ARN for accessing cloud services.
1785
+ log_configuration (Optional[LogConfig]): Configuration for logging.
1786
+ network_configuration (Optional[NetworkConfig]): Network configuration for the runtime, including:
1787
+ - network_mode: The network mode for the runtime
1788
+ - security_group_id: The security group ID for the runtime
1789
+ - vpc_id: The VPC ID for the runtime
1790
+ - vswitch_ids: List of vswitch IDs for the runtime
1791
+ session_concurrency_limit_per_instance (Optional[int]): Maximum concurrent sessions per instance.
1792
+ session_idle_timeout_seconds (Optional[int]): Maximum idle timeout for sessions.
1793
+
1794
+ Returns:
1795
+ Dict[str, Any]: A dictionary containing the creation result with:
1796
+ - success (bool): Whether the operation was successful
1797
+ - agent_runtime_id (str): The ID of the created agent runtime
1798
+ - status (str): The status of the agent runtime
1799
+ - status_reason (str): The reason for the status
1800
+ - request_id (str): The request ID for tracking
1801
+ """
1802
+ try:
1803
+ logger.info(f"Creating agent runtime: {agent_runtime_name}")
1804
+
1805
+ # Adapt custom configurations to SDK configurations
1806
+ sdk_code_config = self._adapt_code_config(code_configuration)
1807
+ sdk_log_config = self._adapt_log_config(log_configuration)
1808
+ sdk_network_config = self._adapt_network_config(
1809
+ network_configuration,
1810
+ )
1811
+
1812
+ # Create the input object with all provided parameters
1813
+ input_data = CreateAgentRuntimeInput(
1814
+ agent_runtime_name=agent_runtime_name,
1815
+ artifact_type=artifact_type,
1816
+ cpu=cpu,
1817
+ memory=memory,
1818
+ port=port,
1819
+ code_configuration=sdk_code_config,
1820
+ description=description,
1821
+ environment_variables=environment_variables,
1822
+ execution_role_arn=execution_role_arn,
1823
+ log_configuration=sdk_log_config,
1824
+ network_configuration=sdk_network_config,
1825
+ session_concurrency_limit_per_instance=session_concurrency_limit_per_instance,
1826
+ session_idle_timeout_seconds=session_idle_timeout_seconds,
1827
+ )
1828
+
1829
+ # Create the request object
1830
+ request = CreateAgentRuntimeRequest(body=input_data)
1831
+
1832
+ # Call the SDK method
1833
+ response = await self.client.create_agent_runtime_async(request)
1834
+
1835
+ # Check if the response is successful
1836
+ if (
1837
+ response.body
1838
+ and response.body.code == "SUCCESS"
1839
+ and response.body.data
1840
+ ):
1841
+ agent_runtime_id = (
1842
+ response.body.data.agent_runtime_id
1843
+ if hasattr(response.body.data, "agent_runtime_id")
1844
+ else None
1845
+ )
1846
+ logger.info(
1847
+ "Agent runtime created successfully (ID: %s)",
1848
+ agent_runtime_id,
1849
+ )
1850
+
1851
+ # Poll for status if we have an agent_runtime_id
1852
+ status_result = None
1853
+ status_reason = None
1854
+ if agent_runtime_id:
1855
+ logger.info(
1856
+ "Polling status for agent runtime: %s",
1857
+ agent_runtime_id,
1858
+ )
1859
+ poll_status = await self._poll_agent_runtime_status(
1860
+ agent_runtime_id,
1861
+ )
1862
+ if isinstance(poll_status, dict):
1863
+ status_result = poll_status.get("status")
1864
+ status_reason = poll_status.get("status_reason")
1865
+ logger.info("Agent runtime status: %s", status_result)
1866
+
1867
+ # Check if the agent runtime is in a valid state for endpoint creation
1868
+ if status_result not in ["READY", "ACTIVE"]:
1869
+ logger.warning(
1870
+ "Agent runtime not in READY/ACTIVE state: %s",
1871
+ status_result,
1872
+ )
1873
+
1874
+ # Return a dictionary with relevant information from the response
1875
+ return {
1876
+ "success": True,
1877
+ "agent_runtime_id": agent_runtime_id,
1878
+ "status": status_result,
1879
+ "status_reason": status_reason,
1880
+ "request_id": response.body.request_id,
1881
+ }
1882
+ else:
1883
+ logger.error("Failed to create agent runtime")
1884
+ # Return error information if the request was not successful
1885
+ return {
1886
+ "success": False,
1887
+ "code": response.body.code if response.body else None,
1888
+ "message": "Failed to create agent runtime",
1889
+ "request_id": response.body.request_id
1890
+ if response.body
1891
+ else None,
1892
+ }
1893
+ except Exception as e:
1894
+ logger.error(
1895
+ f"Exception occurred while creating agent runtime: {str(e)}",
1896
+ )
1897
+ # Return error information if an exception occurred
1898
+ return {
1899
+ "success": False,
1900
+ "error": str(e),
1901
+ "message": f"Exception occurred while creating agent runtime: {str(e)}",
1902
+ }
1903
+
1904
+ async def update_agent_runtime(
1905
+ self,
1906
+ agent_runtime_id: str,
1907
+ agent_runtime_name: Optional[str] = None,
1908
+ artifact_type: Optional[str] = None,
1909
+ cpu: Optional[float] = None,
1910
+ memory: Optional[int] = None,
1911
+ port: Optional[int] = None,
1912
+ code_configuration: Optional[CodeConfig] = None,
1913
+ description: Optional[str] = None,
1914
+ environment_variables: Optional[Dict[str, str]] = None,
1915
+ execution_role_arn: Optional[str] = None,
1916
+ log_configuration: Optional[LogConfig] = None,
1917
+ network_configuration: Optional[NetworkConfig] = None,
1918
+ session_concurrency_limit_per_instance: Optional[int] = None,
1919
+ session_idle_timeout_seconds: Optional[int] = None,
1920
+ ):
1921
+ """
1922
+ Update an agent runtime on AgentRun.
1923
+
1924
+ Args:
1925
+ agent_runtime_id (str): The ID of the agent runtime to update.
1926
+ agent_runtime_name (Optional[str]): The name of the agent runtime.
1927
+ artifact_type (Optional[str]): The type of the artifact.
1928
+ cpu (Optional[float]): The CPU allocated to the runtime.
1929
+ memory (Optional[int]): The memory allocated to the runtime.
1930
+ port (Optional[int]): The port on which the runtime will listen.
1931
+ code_configuration (Optional[CodeConfig]): Configuration for code-based runtimes.
1932
+ description (Optional[str]): Description of the agent runtime.
1933
+ environment_variables (Optional[Dict[str, str]]): Environment variables for the runtime.
1934
+ execution_role_arn (Optional[str]): The execution role ARN for accessing cloud services.
1935
+ log_configuration (Optional[LogConfig]): Configuration for logging.
1936
+ network_configuration (Optional[NetworkConfig]): Network configuration for the runtime, including:
1937
+ - network_mode: The network mode for the runtime
1938
+ - security_group_id: The security group ID for the runtime
1939
+ - vpc_id: The VPC ID for the runtime
1940
+ - vswitch_ids: List of vswitch IDs for the runtime
1941
+ session_concurrency_limit_per_instance (Optional[int]): Maximum concurrent sessions per instance.
1942
+ session_idle_timeout_seconds (Optional[int]): Maximum idle timeout for sessions.
1943
+
1944
+ Returns:
1945
+ Dict[str, Any]: A dictionary containing the update result with:
1946
+ - success (bool): Whether the operation was successful
1947
+ - agent_runtime_id (str): The ID of the updated agent runtime
1948
+ - status (str): The status of the agent runtime
1949
+ - status_reason (str): The reason for the status
1950
+ - request_id (str): The request ID for tracking
1951
+ """
1952
+ try:
1953
+ logger.info(
1954
+ f"Updating agent runtime with ID: {agent_runtime_id}",
1955
+ )
1956
+
1957
+ # Adapt custom configurations to SDK configurations
1958
+ sdk_code_config = self._adapt_code_config(code_configuration)
1959
+
1960
+ sdk_log_config = self._adapt_log_config(log_configuration)
1961
+ sdk_network_config = self._adapt_network_config(
1962
+ network_configuration,
1963
+ )
1964
+
1965
+ # Create the input object with provided parameters
1966
+ input_data = UpdateAgentRuntimeInput(
1967
+ agent_runtime_name=agent_runtime_name,
1968
+ artifact_type=artifact_type,
1969
+ cpu=cpu,
1970
+ memory=memory,
1971
+ port=port,
1972
+ code_configuration=sdk_code_config,
1973
+ description=description,
1974
+ environment_variables=environment_variables,
1975
+ execution_role_arn=execution_role_arn,
1976
+ log_configuration=sdk_log_config,
1977
+ network_configuration=sdk_network_config,
1978
+ session_concurrency_limit_per_instance=session_concurrency_limit_per_instance,
1979
+ session_idle_timeout_seconds=session_idle_timeout_seconds,
1980
+ )
1981
+
1982
+ # Create the request object
1983
+ request = UpdateAgentRuntimeRequest(body=input_data)
1984
+
1985
+ # Call the SDK method
1986
+ response = await self.client.update_agent_runtime_async(
1987
+ agent_runtime_id,
1988
+ request,
1989
+ )
1990
+
1991
+ # Check if the response is successful
1992
+ if response.body and response.body.code == "SUCCESS":
1993
+ logger.info(
1994
+ "Agent runtime updated successfully (ID: %s)",
1995
+ agent_runtime_id,
1996
+ )
1997
+
1998
+ # Poll for status
1999
+ status_result = None
2000
+ status_reason = None
2001
+ if agent_runtime_id:
2002
+ logger.info(
2003
+ "Polling status for updated agent runtime: %s",
2004
+ agent_runtime_id,
2005
+ )
2006
+ poll_status = await self._poll_agent_runtime_status(
2007
+ agent_runtime_id,
2008
+ )
2009
+ if isinstance(poll_status, dict):
2010
+ status_result = poll_status.get("status")
2011
+ status_reason = poll_status.get("status_reason")
2012
+ logger.info(
2013
+ "Updated agent runtime status: %s",
2014
+ status_result,
2015
+ )
2016
+
2017
+ # Return a dictionary with relevant information from the response
2018
+ return {
2019
+ "success": True,
2020
+ "agent_runtime_id": agent_runtime_id,
2021
+ "status": status_result,
2022
+ "status_reason": status_reason,
2023
+ "request_id": response.body.request_id,
2024
+ }
2025
+ else:
2026
+ logger.error("Failed to update agent runtime")
2027
+ # Return error information if the request was not successful
2028
+ return {
2029
+ "success": False,
2030
+ "code": response.body.code if response.body else None,
2031
+ "message": "Failed to update_agent_runtime agent runtime",
2032
+ "request_id": response.body.request_id
2033
+ if response.body
2034
+ else None,
2035
+ }
2036
+ except Exception as e:
2037
+ logger.error(
2038
+ f"Exception occurred while updating agent runtime: {str(e)}",
2039
+ )
2040
+ # Return error information if an exception occurred
2041
+ return {
2042
+ "success": False,
2043
+ "error": str(e),
2044
+ "message": f"Exception occurred while updating agent runtime: {str(e)}",
2045
+ }
2046
+
2047
+ async def create_agent_runtime_endpoint(
2048
+ self,
2049
+ agent_runtime_id: str,
2050
+ endpoint_config: Optional[EndpointConfig] = None,
2051
+ ):
2052
+ """
2053
+ Create an agent runtime endpoint.
2054
+
2055
+ Args:
2056
+ agent_runtime_id (str): The ID of the agent runtime.
2057
+ endpoint_config (Optional[EndpointConfig]): Configuration for the endpoint, including:
2058
+ - agent_runtime_endpoint_name: The name of the endpoint
2059
+ - description: Description of the endpoint
2060
+ - target_version: Target version for the endpoint
2061
+
2062
+ Returns:
2063
+ Dict[str, Any]: A dictionary containing the creation result with:
2064
+ - success (bool): Whether the operation was successful
2065
+ - agent_runtime_endpoint_id (str): The ID of the created endpoint
2066
+ - agent_runtime_endpoint_name (str): The name of the created endpoint
2067
+ - agent_runtime_public_endpoint_url (str): The public URL of the endpoint
2068
+ - status (str): The status of the endpoint
2069
+ - status_reason (str): The reason for the status
2070
+ - request_id (str): The request ID for tracking
2071
+ """
2072
+ try:
2073
+ endpoint_name = (
2074
+ endpoint_config.agent_runtime_endpoint_name
2075
+ if endpoint_config
2076
+ else "unnamed"
2077
+ )
2078
+ logger.info(
2079
+ f"Creating agent runtime endpoint '{endpoint_name}' for runtime ID: {agent_runtime_id}",
2080
+ )
2081
+
2082
+ # Create the input object with provided parameters
2083
+ input_data = CreateAgentRuntimeEndpointInput(
2084
+ agent_runtime_endpoint_name=endpoint_config.agent_runtime_endpoint_name
2085
+ if endpoint_config
2086
+ else None,
2087
+ description=endpoint_config.description
2088
+ if endpoint_config
2089
+ else None,
2090
+ target_version=endpoint_config.target_version
2091
+ if endpoint_config
2092
+ else None,
2093
+ )
2094
+
2095
+ # Create the request object
2096
+ request = CreateAgentRuntimeEndpointRequest(body=input_data)
2097
+
2098
+ # Call the SDK method
2099
+ response = await self.client.create_agent_runtime_endpoint_async(
2100
+ agent_runtime_id,
2101
+ request,
2102
+ )
2103
+
2104
+ # Check if the response is successful
2105
+ if (
2106
+ response.body
2107
+ and response.body.code == "SUCCESS"
2108
+ and response.body.data
2109
+ ):
2110
+ agent_runtime_endpoint_id = (
2111
+ response.body.data.agent_runtime_endpoint_id
2112
+ if hasattr(response.body.data, "agent_runtime_endpoint_id")
2113
+ else None
2114
+ )
2115
+ logger.info(
2116
+ "Agent runtime endpoint created successfully (ID: %s)",
2117
+ agent_runtime_endpoint_id,
2118
+ )
2119
+
2120
+ # Poll for status if we have an agent_runtime_endpoint_id
2121
+ status_result = None
2122
+ status_reason = None
2123
+ if agent_runtime_endpoint_id:
2124
+ logger.info(
2125
+ "Polling status for agent runtime endpoint: %s",
2126
+ agent_runtime_endpoint_id,
2127
+ )
2128
+ poll_status = (
2129
+ await self._poll_agent_runtime_endpoint_status(
2130
+ agent_runtime_id,
2131
+ agent_runtime_endpoint_id,
2132
+ )
2133
+ )
2134
+ if isinstance(poll_status, dict):
2135
+ status_result = poll_status.get("status")
2136
+ status_reason = poll_status.get("status_reason")
2137
+ logger.info(
2138
+ "Agent runtime endpoint status: %s",
2139
+ status_result,
2140
+ )
2141
+
2142
+ # Return a dictionary with relevant information from the response
2143
+ return {
2144
+ "success": True,
2145
+ "agent_runtime_endpoint_id": agent_runtime_endpoint_id,
2146
+ "agent_runtime_endpoint_name": response.body.data.agent_runtime_endpoint_name
2147
+ if hasattr(
2148
+ response.body.data,
2149
+ "agent_runtime_endpoint_name",
2150
+ )
2151
+ else None,
2152
+ "agent_runtime_public_endpoint_url": response.body.data.endpoint_public_url
2153
+ if hasattr(response.body.data, "endpoint_public_url")
2154
+ else None,
2155
+ "status": status_result,
2156
+ "status_reason": status_reason,
2157
+ "request_id": response.body.request_id,
2158
+ }
2159
+ else:
2160
+ logger.error("Failed to create agent runtime endpoint")
2161
+ # Return error information if the request was not successful
2162
+ return {
2163
+ "success": False,
2164
+ "code": response.body.code if response.body else None,
2165
+ "message": "Failed to create agent runtime endpoint",
2166
+ "request_id": response.body.request_id
2167
+ if response.body
2168
+ else None,
2169
+ }
2170
+ except Exception as e:
2171
+ logger.error(
2172
+ f"Exception occurred while creating agent runtime endpoint: {str(e)}",
2173
+ )
2174
+ # Return error information if an exception occurred
2175
+ return {
2176
+ "success": False,
2177
+ "error": str(e),
2178
+ "message": f"Exception occurred while creating agent runtime endpoint: {str(e)}",
2179
+ }
2180
+
2181
+ async def update_agent_runtime_endpoint(
2182
+ self,
2183
+ agent_runtime_id: str,
2184
+ agent_runtime_endpoint_id: str,
2185
+ endpoint_config: Optional[EndpointConfig] = None,
2186
+ ):
2187
+ """
2188
+ Update an agent runtime endpoint.
2189
+
2190
+ Args:
2191
+ agent_runtime_id (str): The ID of the agent runtime.
2192
+ agent_runtime_endpoint_id (str): The ID of the agent runtime endpoint.
2193
+ endpoint_config (Optional[EndpointConfig]): Configuration for the endpoint, including:
2194
+ - agent_runtime_endpoint_name: The name of the endpoint
2195
+ - description: Description of the endpoint
2196
+ - target_version: Target version for the endpoint
2197
+
2198
+ Returns:
2199
+ Dict[str, Any]: A dictionary containing the update result with:
2200
+ - success (bool): Whether the operation was successful
2201
+ - agent_runtime_endpoint_id (str): The ID of the updated endpoint
2202
+ - status (str): The status of the endpoint
2203
+ - status_reason (str): The reason for the status
2204
+ - request_id (str): The request ID for tracking
2205
+ """
2206
+ try:
2207
+ endpoint_name = (
2208
+ endpoint_config.agent_runtime_endpoint_name
2209
+ if endpoint_config
2210
+ else "unnamed"
2211
+ )
2212
+ logger.info(
2213
+ f"Updating agent runtime endpoint '{endpoint_name}' with ID: {agent_runtime_endpoint_id}",
2214
+ )
2215
+
2216
+ # Create the input object with provided parameters
2217
+ input_data = UpdateAgentRuntimeEndpointInput(
2218
+ agent_runtime_endpoint_name=endpoint_config.agent_runtime_endpoint_name
2219
+ if endpoint_config
2220
+ else None,
2221
+ description=endpoint_config.description
2222
+ if endpoint_config
2223
+ else None,
2224
+ target_version=endpoint_config.target_version
2225
+ if endpoint_config
2226
+ else None,
2227
+ )
2228
+
2229
+ # Create the request object
2230
+ request = UpdateAgentRuntimeEndpointRequest(body=input_data)
2231
+
2232
+ # Call the SDK method
2233
+ response = await self.client.update_agent_runtime_endpoint_async(
2234
+ agent_runtime_id,
2235
+ agent_runtime_endpoint_id,
2236
+ request,
2237
+ )
2238
+
2239
+ # Check if the response is successful
2240
+ if response.body and response.body.code == "SUCCESS":
2241
+ logger.info(
2242
+ "Agent runtime endpoint updated successfully (ID: %s)",
2243
+ agent_runtime_endpoint_id,
2244
+ )
2245
+
2246
+ # Poll for status if we have an agent_runtime_endpoint_id
2247
+ status_result = None
2248
+ status_reason = None
2249
+ if agent_runtime_endpoint_id:
2250
+ logger.info(
2251
+ "Polling status for updated agent runtime endpoint: %s",
2252
+ agent_runtime_endpoint_id,
2253
+ )
2254
+ poll_status = (
2255
+ await self._poll_agent_runtime_endpoint_status(
2256
+ agent_runtime_id,
2257
+ agent_runtime_endpoint_id,
2258
+ )
2259
+ )
2260
+ if isinstance(poll_status, dict):
2261
+ status_result = poll_status.get("status")
2262
+ status_reason = poll_status.get("status_reason")
2263
+ logger.info(
2264
+ "Updated agent runtime endpoint status: %s",
2265
+ status_result,
2266
+ )
2267
+
2268
+ # Return a dictionary with relevant information from the response
2269
+ return {
2270
+ "success": True,
2271
+ "agent_runtime_endpoint_id": agent_runtime_endpoint_id,
2272
+ "status": status_result,
2273
+ "status_reason": status_reason,
2274
+ "request_id": response.body.request_id,
2275
+ }
2276
+ else:
2277
+ logger.error("Failed to update agent runtime endpoint")
2278
+ # Return error information if the request was not successful
2279
+ return {
2280
+ "success": False,
2281
+ "code": response.body.code if response.body else None,
2282
+ "message": "Failed to update agent runtime endpoint",
2283
+ "request_id": response.body.request_id
2284
+ if response.body
2285
+ else None,
2286
+ }
2287
+ except Exception as e:
2288
+ logger.error(
2289
+ f"Exception occurred while updating agent runtime endpoint: {str(e)}",
2290
+ )
2291
+ # Return error information if an exception occurred
2292
+ return {
2293
+ "success": False,
2294
+ "error": str(e),
2295
+ "message": f"Exception occurred while updating agent runtime endpoint: {str(e)}",
2296
+ }
2297
+
2298
+ async def get_agent_runtime_endpoint(
2299
+ self,
2300
+ agent_runtime_id: str,
2301
+ agent_runtime_endpoint_id: str,
2302
+ ):
2303
+ """
2304
+ Get an agent runtime endpoint.
2305
+
2306
+ Args:
2307
+ agent_runtime_id (str): The ID of the agent runtime.
2308
+ agent_runtime_endpoint_id (str): The ID of the agent runtime endpoint.
2309
+
2310
+ Returns:
2311
+ Dict[str, Any]: A dictionary containing the endpoint details with:
2312
+ - success (bool): Whether the operation was successful
2313
+ - agent_runtime_endpoint_id (str): The ID of the endpoint
2314
+ - agent_runtime_endpoint_name (str): The name of the endpoint
2315
+ - agent_runtime_id (str): The ID of the agent runtime
2316
+ - agent_runtime_public_endpoint_url (str): The public URL of the endpoint
2317
+ - status (str): The status of the endpoint
2318
+ - status_reason (str): The reason for the status
2319
+ - request_id (str): The request ID for tracking
2320
+ """
2321
+ try:
2322
+ logger.info(
2323
+ f"Getting agent runtime endpoint details for ID: {agent_runtime_endpoint_id}",
2324
+ )
2325
+
2326
+ # Call the SDK method
2327
+ response = await self.client.get_agent_runtime_endpoint_async(
2328
+ agent_runtime_id,
2329
+ agent_runtime_endpoint_id,
2330
+ )
2331
+
2332
+ # Check if the response is successful
2333
+ if (
2334
+ response.body
2335
+ and response.body.code == "SUCCESS"
2336
+ and response.body.data
2337
+ ):
2338
+ logger.info(
2339
+ "Agent runtime endpoint details retrieved successfully (ID: %s)",
2340
+ agent_runtime_endpoint_id,
2341
+ )
2342
+ # Return the endpoint data as a dictionary
2343
+ return {
2344
+ "success": True,
2345
+ "agent_runtime_endpoint_id": response.body.data.agent_runtime_endpoint_id
2346
+ if hasattr(response.body.data, "agent_runtime_endpoint_id")
2347
+ else None,
2348
+ "agent_runtime_endpoint_name": response.body.data.agent_runtime_endpoint_name
2349
+ if hasattr(
2350
+ response.body.data,
2351
+ "agent_runtime_endpoint_name",
2352
+ )
2353
+ else None,
2354
+ "agent_runtime_id": response.body.data.agent_runtime_id
2355
+ if hasattr(response.body.data, "agent_runtime_id")
2356
+ else None,
2357
+ "agent_runtime_public_endpoint_url": response.body.data.endpoint_public_url
2358
+ if hasattr(response.body.data, "endpoint_public_url")
2359
+ else None,
2360
+ "status": response.body.data.status
2361
+ if hasattr(response.body.data, "status")
2362
+ else None,
2363
+ "status_reason": response.body.data.status_reason
2364
+ if hasattr(response.body.data, "status_reason")
2365
+ else None,
2366
+ "request_id": response.body.request_id,
2367
+ }
2368
+ else:
2369
+ logger.error("Failed to get agent runtime endpoint")
2370
+ # Return error information if the request was not successful
2371
+ return {
2372
+ "success": False,
2373
+ "code": response.body.code if response.body else None,
2374
+ "message": "Failed to get agent runtime endpoint",
2375
+ "request_id": response.body.request_id
2376
+ if response.body
2377
+ else None,
2378
+ }
2379
+ except Exception as e:
2380
+ logger.error(
2381
+ f"Exception occurred while getting agent runtime endpoint: {str(e)}",
2382
+ )
2383
+ # Return error information if an exception occurred
2384
+ return {
2385
+ "success": False,
2386
+ "error": str(e),
2387
+ "message": f"Exception occurred while getting agent runtime endpoint: {str(e)}",
2388
+ }
2389
+
2390
+ async def delete_agent_runtime_endpoint(
2391
+ self,
2392
+ agent_runtime_id: str,
2393
+ agent_runtime_endpoint_id: str,
2394
+ ):
2395
+ """
2396
+ Delete an agent runtime endpoint.
2397
+
2398
+ Args:
2399
+ agent_runtime_id (str): The ID of the agent runtime.
2400
+ agent_runtime_endpoint_id (str): The ID of the agent runtime endpoint.
2401
+
2402
+ Returns:
2403
+ Dict[str, Any]: A dictionary containing the delete result with:
2404
+ - success (bool): Whether the operation was successful
2405
+ - message (str): Status message
2406
+ - agent_runtime_endpoint_id (str): The ID of the deleted endpoint
2407
+ - request_id (str): The request ID for tracking
2408
+ """
2409
+ try:
2410
+ logger.info(
2411
+ f"Deleting agent runtime endpoint with ID: {agent_runtime_endpoint_id}",
2412
+ )
2413
+
2414
+ # Call the SDK method
2415
+ response = await self.client.delete_agent_runtime_endpoint_async(
2416
+ agent_runtime_id,
2417
+ agent_runtime_endpoint_id,
2418
+ )
2419
+
2420
+ # Check if the response is successful
2421
+ if response.body and response.body.code == "SUCCESS":
2422
+ logger.info(
2423
+ "Agent runtime endpoint deletion initiated (ID: %s)",
2424
+ agent_runtime_endpoint_id,
2425
+ )
2426
+ # Return a dictionary with relevant information from the response
2427
+ return {
2428
+ "success": True,
2429
+ "message": "Agent runtime endpoint deletion initiated successfully",
2430
+ "agent_runtime_endpoint_id": agent_runtime_endpoint_id,
2431
+ "request_id": response.body.request_id,
2432
+ }
2433
+ else:
2434
+ logger.error("Failed to delete agent runtime endpoint")
2435
+ # Return error information if the request was not successful
2436
+ return {
2437
+ "success": False,
2438
+ "code": response.body.code if response.body else None,
2439
+ "message": "Failed to delete agent runtime endpoint",
2440
+ "request_id": response.body.request_id
2441
+ if response.body
2442
+ else None,
2443
+ }
2444
+ except Exception as e:
2445
+ logger.error(
2446
+ f"Exception occurred while deleting agent runtime endpoint: {str(e)}",
2447
+ )
2448
+ # Return error information if an exception occurred
2449
+ return {
2450
+ "success": False,
2451
+ "error": str(e),
2452
+ "message": f"Exception occurred while deleting agent runtime endpoint: {str(e)}",
2453
+ }
2454
+
2455
+ async def publish_agent_runtime_version(
2456
+ self,
2457
+ agent_runtime_id: str,
2458
+ description: Optional[str] = None,
2459
+ ):
2460
+ """
2461
+ Publish an agent runtime version.
2462
+
2463
+ Args:
2464
+ agent_runtime_id (str): The ID of the agent runtime.
2465
+ description (Optional[str]): Description of the version.
2466
+
2467
+ Returns:
2468
+ Dict[str, Any]: A dictionary containing the publish result with:
2469
+ - success (bool): Whether the operation was successful
2470
+ - agent_runtime_id (str): The ID of the agent runtime
2471
+ - agent_runtime_version (str): The published version
2472
+ - description (str): Description of the version
2473
+ - request_id (str): The request ID for tracking
2474
+ """
2475
+ try:
2476
+ logger.info(
2477
+ f"Publishing agent runtime version for ID: {agent_runtime_id}",
2478
+ )
2479
+
2480
+ # Create the input object with provided parameters
2481
+ input_data = PublishRuntimeVersionInput(
2482
+ description=description,
2483
+ )
2484
+
2485
+ # Create the request object
2486
+ request = PublishRuntimeVersionRequest(body=input_data)
2487
+
2488
+ # Call the SDK method
2489
+ response = await self.client.publish_runtime_version_async(
2490
+ agent_runtime_id,
2491
+ request,
2492
+ )
2493
+
2494
+ # Check if the response is successful
2495
+ if (
2496
+ response.body
2497
+ and response.body.code == "SUCCESS"
2498
+ and response.body.data
2499
+ ):
2500
+ version = (
2501
+ response.body.data.agent_runtime_version
2502
+ if hasattr(response.body.data, "agent_runtime_version")
2503
+ else None
2504
+ )
2505
+ logger.info(
2506
+ "Agent runtime version published successfully: %s",
2507
+ version,
2508
+ )
2509
+ # Return a dictionary with relevant information from the response
2510
+ return {
2511
+ "success": True,
2512
+ "agent_runtime_id": response.body.data.agent_runtime_id
2513
+ if hasattr(response.body.data, "agent_runtime_id")
2514
+ else None,
2515
+ "agent_runtime_version": version,
2516
+ "description": response.body.data.description
2517
+ if hasattr(response.body.data, "description")
2518
+ else None,
2519
+ "request_id": response.body.request_id,
2520
+ }
2521
+ else:
2522
+ logger.error("Failed to publish agent runtime version")
2523
+ # Return error information if the request was not successful
2524
+ return {
2525
+ "success": False,
2526
+ "code": response.body.code if response.body else None,
2527
+ "message": "Failed to publish agent runtime version",
2528
+ "request_id": response.body.request_id
2529
+ if response.body
2530
+ else None,
2531
+ }
2532
+ except Exception as e:
2533
+ logger.error(
2534
+ f"Exception occurred while publishing agent runtime version: {str(e)}",
2535
+ )
2536
+ # Return error information if an exception occurred
2537
+ return {
2538
+ "success": False,
2539
+ "error": str(e),
2540
+ "message": f"Exception occurred while publishing agent runtime version: {str(e)}",
2541
+ }