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,855 @@
1
+ # -*- coding: utf-8 -*-
2
+ # flake8: noqa: E501
3
+ # pylint: disable=line-too-long, arguments-renamed, too-many-branches, too-many-statements
4
+ import json
5
+ import logging
6
+ import random
7
+ import secrets
8
+ import string
9
+ import time
10
+ from http.client import HTTPS_PORT
11
+ from typing import List, Dict, Optional
12
+ from urllib.parse import urlparse
13
+
14
+ from alibabacloud_fc20230330 import models as fc20230330_models
15
+ from alibabacloud_fc20230330.client import Client as FC20230330Client
16
+ from alibabacloud_tea_openapi import models as open_api_models
17
+ from alibabacloud_tea_util import models as util_models
18
+
19
+ from agentscope_runtime.sandbox.model import SandboxManagerEnvConfig
20
+ from .base_client import BaseClient
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class FCSessionManager:
26
+ """Manager for Function Compute sessions that handles creation, retrieval,
27
+ updating, and deletion of sessions.
28
+ """
29
+
30
+ def __init__(self):
31
+ """Initialize the session manager with an empty session dictionary."""
32
+ self.sessions = {}
33
+ logger.debug("FC Session Manager initialized")
34
+
35
+ def create_session(self, session_id: str, session_data: Dict):
36
+ """Create a new session with the given session_id and session_data.
37
+
38
+ Args:
39
+ session_id (str): Unique identifier for the session.
40
+ session_data (Dict): Data to store in the session.
41
+ """
42
+ self.sessions[session_id] = session_data
43
+ logger.debug(f"Created FC session: {session_id}")
44
+
45
+ def get_session(self, session_id: str) -> Optional[Dict]:
46
+ """Retrieve session data by session_id.
47
+
48
+ Args:
49
+ session_id (str): Unique identifier for the session.
50
+
51
+ Returns:
52
+ Optional[Dict]: Session data if found, None otherwise.
53
+ """
54
+ return self.sessions.get(session_id)
55
+
56
+ def update_session(self, session_id: str, updates: Dict):
57
+ """Update an existing session with new data.
58
+
59
+ Args:
60
+ session_id (str): Unique identifier for the session.
61
+ updates (Dict): Data to update in the session.
62
+ """
63
+ if session_id in self.sessions:
64
+ self.sessions[session_id].update(updates)
65
+ logger.debug(f"Updated FC session: {session_id}")
66
+
67
+ def delete_session(self, session_id: str):
68
+ """Delete a session by session_id.
69
+
70
+ Args:
71
+ session_id (str): Unique identifier for the session.
72
+ """
73
+ if session_id in self.sessions:
74
+ del self.sessions[session_id]
75
+ logger.debug(f"Deleted FC session: {session_id}")
76
+
77
+ def list_sessions(self) -> List[str]:
78
+ """List all session IDs.
79
+
80
+ Returns:
81
+ List[str]: List of all session IDs.
82
+ """
83
+ return list(self.sessions.keys())
84
+
85
+
86
+ class FCClient(BaseClient):
87
+ """Client for managing Function Compute containers in the sandbox environment.
88
+
89
+ This client provides methods to create, start, stop, remove,
90
+ and inspect Function Compute sessions. It also handles the underlying
91
+ Function Compute API calls and status polling.
92
+ """
93
+
94
+ HTTPS_PROTOCOL = "https"
95
+
96
+ def __init__(self, config: SandboxManagerEnvConfig):
97
+ """Initialize the Function Compute client with the provided configuration.
98
+
99
+ Args:
100
+ config (SandboxManagerEnvConfig): Configuration object containing
101
+ Function Compute settings.
102
+ """
103
+ self.config = config
104
+ self.fc_client = self._create_fc_client()
105
+ self.session_manager = FCSessionManager()
106
+ self.function_prefix = config.fc_prefix or "agentscope-sandbox"
107
+
108
+ logger.info(
109
+ f"FunctionComputeClient initialized successfully with config: {config}",
110
+ )
111
+
112
+ # Test connection
113
+ self._test_connection()
114
+
115
+ def _create_fc_client(self):
116
+ """Create and configure the Function Compute client.
117
+
118
+ Returns:
119
+ FC20230330Client: Configured Function Compute client instance.
120
+ """
121
+ fc_config = open_api_models.Config(
122
+ access_key_id=self.config.fc_access_key_id,
123
+ access_key_secret=self.config.fc_access_key_secret,
124
+ endpoint=f"{self.config.fc_account_id}.{self.config.fc_region_id}.fc.aliyuncs.com",
125
+ )
126
+ return FC20230330Client(fc_config)
127
+
128
+ def _test_connection(self):
129
+ """Test the connection to Function Compute service."""
130
+ try:
131
+ list_request = fc20230330_models.ListFunctionsRequest(limit=1)
132
+
133
+ response = self.fc_client.list_functions(request=list_request)
134
+
135
+ if hasattr(response, "body") and hasattr(
136
+ response.body,
137
+ "functions",
138
+ ):
139
+ functions = response.body.functions
140
+ func_count = len(functions) if functions else 0
141
+ logger.debug(
142
+ f"FunctionComputeClient FC connection test successful: {func_count} functions",
143
+ )
144
+
145
+ except Exception as e:
146
+ logger.warning(
147
+ f"FunctionComputeClient FC connection test failed: {e}",
148
+ )
149
+ logger.warning(
150
+ "FunctionComputeClient This may not affect normal usage. If there are permission issues, please check AccessKey permission configuration.",
151
+ )
152
+
153
+ def create(
154
+ self,
155
+ image,
156
+ name=None,
157
+ ports=None,
158
+ volumes=None,
159
+ environment=None,
160
+ runtime_config=None,
161
+ ):
162
+ """Create a new Function Compute session with the specified parameters.
163
+
164
+ Args:
165
+ image (str): The container image to use for the Function Compute session.
166
+ name (str, optional): The name for the session. If not provided,
167
+ a random name will be generated.
168
+ ports (list, optional): List of ports to expose.
169
+ volumes (list, optional): List of volumes to mount.
170
+ environment (dict, optional): Environment variables to set in
171
+ the container.
172
+ runtime_config (dict, optional): Additional runtime configuration.
173
+
174
+ Returns:
175
+ tuple: A tuple containing (session_id, ports, endpoint_public_url_domain, protocol).
176
+
177
+ Raises:
178
+ Exception: If the Function Compute session creation fails.
179
+ """
180
+ port = 80
181
+ if ports is not None and len(ports) > 0:
182
+ port = 80 if ports[0] == "80/tcp" else ports[0]
183
+ fc_image = self._replace_fc_images(image)
184
+ try:
185
+ # 1. Generate session ID and function name
186
+ session_id = name or self._generate_session_id()
187
+ function_name = f"{self.function_prefix}-{session_id}"
188
+
189
+ custom_container_config = fc20230330_models.CustomContainerConfig(
190
+ image=fc_image,
191
+ port=port,
192
+ acceleration_type="Default",
193
+ )
194
+
195
+ # 2. Build custom container configuration
196
+ health_check_url = "/"
197
+ health_check_config = fc20230330_models.CustomHealthCheckConfig(
198
+ failure_threshold=60,
199
+ http_get_url=health_check_url,
200
+ initial_delay_seconds=2,
201
+ period_seconds=1,
202
+ success_threshold=1,
203
+ timeout_seconds=1,
204
+ )
205
+ custom_container_config.health_check_config = health_check_config
206
+ logger.info(
207
+ f"FunctionComputeClient building custom health check configuration: {health_check_config}",
208
+ )
209
+ # 3. Build function creation parameters (based on tested successful configuration)
210
+ create_function_kwargs = {
211
+ "function_name": function_name,
212
+ "runtime": "custom-container",
213
+ "custom_container_config": custom_container_config,
214
+ "description": f"AgentScope Runtime Sandbox Function - {session_id}",
215
+ "timeout": 300,
216
+ "memory_size": self.config.fc_memory if self.config else 2048,
217
+ "disk_size": 512,
218
+ "cpu": self.config.fc_cpu if self.config else 2,
219
+ "instance_concurrency": 200,
220
+ "internet_access": True,
221
+ "environment_variables": environment or {},
222
+ "session_affinity": "HEADER_FIELD",
223
+ "instance_isolation_mode": "SESSION_EXCLUSIVE",
224
+ "session_affinity_config": '{"affinityHeaderFieldName":"x-agentscope-runtime-session-id","sessionTTLInSeconds":21600,"sessionConcurrencyPerInstance":1,"sessionIdleTimeoutInSeconds":3600}',
225
+ }
226
+ # 5. If log configuration exists
227
+ if hasattr(self.config, "fc_log_store") and hasattr(
228
+ self.config,
229
+ "fc_log_project",
230
+ ):
231
+ if self.config.fc_log_store and self.config.fc_log_project:
232
+ log_config = fc20230330_models.LogConfig(
233
+ logstore=self.config.fc_log_store,
234
+ project=self.config.fc_log_project,
235
+ enable_request_metrics=True,
236
+ enable_instance_metrics=True,
237
+ log_begin_rule="DefaultRegex",
238
+ )
239
+ create_function_kwargs["log_config"] = log_config
240
+ logger.debug(
241
+ f"Configuring log service: {self.config.fc_log_project}/{self.config.fc_log_store}",
242
+ )
243
+
244
+ # 6. If VPC configuration exists
245
+ if (
246
+ hasattr(self.config, "fc_vpc_id")
247
+ and hasattr(self.config, "fc_vswitch_ids")
248
+ and hasattr(self.config, "fc_security_group_id")
249
+ ):
250
+ if (
251
+ self.config.fc_vpc_id
252
+ and self.config.fc_vswitch_ids
253
+ and self.config.fc_security_group_id
254
+ ):
255
+ vpc_config = fc20230330_models.VPCConfig(
256
+ vpc_id=self.config.fc_vpc_id,
257
+ v_switch_ids=self.config.fc_vswitch_ids,
258
+ security_group_id=self.config.fc_security_group_id,
259
+ )
260
+ create_function_kwargs["vpc_config"] = vpc_config
261
+ logger.debug(
262
+ f"Configuring VPC network: {self.config.fc_vpc_id}",
263
+ )
264
+
265
+ # 7. Create function (based on API call method)
266
+ create_function_input = fc20230330_models.CreateFunctionInput(
267
+ **create_function_kwargs,
268
+ )
269
+ create_function_request = fc20230330_models.CreateFunctionRequest(
270
+ body=create_function_input,
271
+ )
272
+
273
+ runtime_options = util_models.RuntimeOptions()
274
+ headers = {}
275
+
276
+ response = self.fc_client.create_function_with_options(
277
+ create_function_request,
278
+ headers,
279
+ runtime_options,
280
+ )
281
+
282
+ logger.debug(
283
+ "FunctionComputeClient function created successfully!",
284
+ )
285
+ logger.info(
286
+ f"FunctionComputeClient function name: {response.body.function_name}",
287
+ )
288
+ logger.info(
289
+ f"FunctionComputeClient runtime: {response.body.runtime}",
290
+ )
291
+ logger.info(
292
+ f"FunctionComputeClient create time: {response.body.created_time}",
293
+ )
294
+
295
+ # 8. Create HTTP trigger
296
+ trigger_info = self._create_http_trigger(function_name, session_id)
297
+ trigger_name = trigger_info["trigger_name"]
298
+ # 9. Build access URL (using the real URL returned by the trigger)
299
+ endpoint_internet_url = trigger_info["url_internet"]
300
+ endpoint_intranet_url = trigger_info["url_intranet"]
301
+ # Poll for function ready status
302
+ while True:
303
+ if self.check_function_ready(function_name):
304
+ break
305
+ time.sleep(3)
306
+ logger.info(
307
+ f"Check function deployment status, function name: {function_name}",
308
+ )
309
+ # 10. Create session data
310
+ session_data = {
311
+ "session_id": session_id,
312
+ "function_name": function_name,
313
+ "trigger_name": trigger_name,
314
+ "trigger_id": trigger_info["trigger_id"],
315
+ "created_time": time.time(),
316
+ "status": "running",
317
+ "image": fc_image,
318
+ "ports": ports,
319
+ "environment": environment,
320
+ "runtime_token": environment["SECRET_TOKEN"],
321
+ "url_internet": endpoint_internet_url,
322
+ "url_intranet": endpoint_intranet_url,
323
+ }
324
+ # 11. Register session
325
+ self.session_manager.create_session(session_id, session_data)
326
+
327
+ logger.info(
328
+ f"FunctionComputeClient FC function {session_id} created and registered session successfully",
329
+ )
330
+
331
+ parsed_url = urlparse(endpoint_internet_url)
332
+
333
+ endpoint_public_url_domain = parsed_url.netloc
334
+ endpoint_public_url_path = parsed_url.path
335
+
336
+ # FC should adapt for ip and port format
337
+ ports = [f"{HTTPS_PORT}{endpoint_public_url_path}"]
338
+ # If no port needed, we will return None
339
+ return (
340
+ session_id,
341
+ ports,
342
+ endpoint_public_url_domain,
343
+ self.HTTPS_PROTOCOL,
344
+ )
345
+
346
+ except Exception as e:
347
+ logger.error(f"Create FC function failed: {e}")
348
+ # Provide more detailed error information
349
+ if "InvalidAccessKeyId" in str(e):
350
+ logger.error(
351
+ "Authentication failed, please check if FC_ACCESS_KEY_ID is correct",
352
+ )
353
+ elif "SignatureDoesNotMatch" in str(e):
354
+ logger.error(
355
+ "Signature mismatch, please check if FC_ACCESS_KEY_SECRET is correct",
356
+ )
357
+ elif "Forbidden" in str(e):
358
+ logger.error(
359
+ "Insufficient permissions, please check if AccessKey has FC service permissions",
360
+ )
361
+ raise RuntimeError(f"FC function creation failed: {e}") from e
362
+
363
+ def check_function_ready(self, function_name):
364
+ """Check if the function is ready.
365
+
366
+ Args:
367
+ function_name (str): The name of the function to check.
368
+
369
+ Returns:
370
+ bool: True if the function is ready, False otherwise.
371
+ """
372
+ try:
373
+ status = self._get_function_status(function_name)
374
+ return status == "running"
375
+ except Exception as e:
376
+ logger.error(f"Error checking function status: {e}")
377
+ return False
378
+
379
+ def start(self, session_id):
380
+ """Start function (FC functions run automatically).
381
+
382
+ Args:
383
+ session_id (str): The ID of the session to start.
384
+
385
+ Returns:
386
+ bool: True if the function was successfully started, False otherwise.
387
+ """
388
+ session = self.session_manager.get_session(session_id)
389
+ if not session:
390
+ logger.warning(
391
+ f"FunctionComputeClient session record not found: {session_id}",
392
+ )
393
+ return False
394
+
395
+ timeout_seconds = 300
396
+ interval = 2
397
+ max_retries = timeout_seconds // interval
398
+
399
+ for i in range(max_retries):
400
+ time.sleep(interval)
401
+ function_status = self.get_status(session_id)
402
+
403
+ if function_status == "running":
404
+ break
405
+
406
+ logger.debug(
407
+ f"FunctionComputeClient waiting for FC function to be ready... ({i + 1}/{max_retries}) | "
408
+ f"Current status: {function_status}, session: {session_id}",
409
+ )
410
+ else:
411
+ logger.warning(
412
+ f"FunctionComputeClient start timeout: Waiting for FC function to enter running state exceeded {timeout_seconds} seconds, "
413
+ f"final status: {self.get_status(session_id)}, session: {session_id}",
414
+ )
415
+ return False
416
+
417
+ # Update session status to running
418
+ self.session_manager.update_session(session_id, {"status": "running"})
419
+ logger.info(f"FunctionComputeClient FC function started: {session_id}")
420
+ return True
421
+
422
+ def stop(self, session_id, timeout=None):
423
+ """Stop function (update status, FC functions do not need explicit stopping).
424
+
425
+ Args:
426
+ session_id (str): The ID of the session to stop.
427
+ timeout (int, optional): Timeout for the stop operation.
428
+
429
+ Returns:
430
+ bool: True if the function was successfully stopped, False otherwise.
431
+ """
432
+ session = self.session_manager.get_session(session_id)
433
+ if not session:
434
+ logger.warning(
435
+ f"FunctionComputeClient session record not found: {session_id}",
436
+ )
437
+ return False
438
+
439
+ try:
440
+ # FC functions cannot be directly stopped, only update session status
441
+ # TODO Need to call function disable interface
442
+ self.session_manager.update_session(
443
+ session_id,
444
+ {"status": "stopped"},
445
+ )
446
+ logger.info(
447
+ f"FunctionComputeClient FC function status set to stopped: {session_id}",
448
+ )
449
+ return True
450
+
451
+ except Exception as e:
452
+ logger.error(
453
+ f"FunctionComputeClient stop FC function failed {session_id}: {e}",
454
+ )
455
+ return False
456
+
457
+ def remove(self, session_id, force=False):
458
+ """Remove function - Deletion process based on test verification.
459
+
460
+ Args:
461
+ session_id (str): The ID of the session to remove.
462
+ force (bool, optional): Whether to force removal. Defaults to False.
463
+
464
+ Returns:
465
+ bool: True if the function was successfully removed, False otherwise.
466
+ """
467
+ session = self.session_manager.get_session(session_id)
468
+ if not session:
469
+ logger.warning(
470
+ f"FunctionComputeClient session record not found, skipping deletion: {session_id}",
471
+ )
472
+ return True
473
+
474
+ function_name = session.get("function_name")
475
+ trigger_name = session.get("trigger_name")
476
+
477
+ try:
478
+ logger.info(
479
+ f"FunctionComputeClient starting to delete FC function: {function_name}",
480
+ )
481
+
482
+ # 1. Delete trigger first (if exists)
483
+ if trigger_name:
484
+ try:
485
+ # Delete trigger API call based on test verification (without request object)
486
+ self.fc_client.delete_trigger_with_options(
487
+ function_name,
488
+ trigger_name,
489
+ {},
490
+ util_models.RuntimeOptions(),
491
+ )
492
+ logger.debug(
493
+ f"FunctionComputeClient trigger deleted: {trigger_name}",
494
+ )
495
+ except Exception as trigger_error:
496
+ logger.warning(
497
+ f"FunctionComputeClient delete trigger failed (continuing to delete function): {trigger_error}",
498
+ )
499
+
500
+ # 2. Delete function (API call method based on test verification)
501
+ self.fc_client.delete_function_with_options(
502
+ function_name,
503
+ {},
504
+ util_models.RuntimeOptions(),
505
+ )
506
+
507
+ # 3. Delete session record
508
+ self.session_manager.delete_session(session_id)
509
+
510
+ logger.info(
511
+ f"FunctionComputeClient FC function deleted successfully: {function_name}",
512
+ )
513
+ return True
514
+
515
+ except Exception as e:
516
+ logger.error(
517
+ f"FunctionComputeClient delete FC function failed: {e}",
518
+ )
519
+ if force:
520
+ # Force cleanup - delete session record even if API call fails
521
+ self.session_manager.delete_session(session_id)
522
+ logger.warning(
523
+ f"FunctionComputeClient force delete session record: {session_id}",
524
+ )
525
+ return True
526
+ raise RuntimeError(
527
+ f"FunctionComputeClient FC function removal failed: {e}",
528
+ ) from e
529
+
530
+ def inspect(self, session_id):
531
+ """Get function detailed information - Implementation based on test verification.
532
+
533
+ Args:
534
+ session_id (str): The ID of the session to inspect.
535
+
536
+ Returns:
537
+ dict: A dictionary containing session information, function info, and status.
538
+ """
539
+ session = self.session_manager.get_session(session_id)
540
+ if not session:
541
+ logger.warning(
542
+ f"FunctionComputeClient session record not found: {session_id}",
543
+ )
544
+ return None
545
+
546
+ function_name = session.get("function_name")
547
+
548
+ try:
549
+ # Get function information (API call based on test verification)
550
+ function_query_request = fc20230330_models.GetFunctionRequest(
551
+ qualifier="LATEST",
552
+ )
553
+
554
+ response = self.fc_client.get_function(
555
+ function_name=function_name,
556
+ request=function_query_request,
557
+ )
558
+ logger.info(f"FunctionComputeClient function inspect ${response}")
559
+
560
+ function_info = {
561
+ "function_name": response.body.function_name
562
+ if hasattr(
563
+ response.body,
564
+ "function_name",
565
+ )
566
+ else function_name,
567
+ "runtime": response.body.runtime
568
+ if hasattr(response.body, "runtime")
569
+ else "unknown",
570
+ "state": response.body.state
571
+ if hasattr(response.body, "state")
572
+ else "unknown",
573
+ "created_time": response.body.created_time
574
+ if hasattr(response.body, "created_time")
575
+ else "unknown",
576
+ "last_modified_time": response.body.last_modified_time
577
+ if hasattr(
578
+ response.body,
579
+ "last_modified_time",
580
+ )
581
+ else "unknown",
582
+ "memory_size": response.body.memory_size
583
+ if hasattr(response.body, "memory_size")
584
+ else 0,
585
+ "timeout": response.body.timeout
586
+ if hasattr(response.body, "timeout")
587
+ else 0,
588
+ }
589
+
590
+ return {
591
+ "session": session,
592
+ "function_info": function_info,
593
+ "runtime_token": session.get("runtime_token"),
594
+ "status": session.get("status", "unknown"),
595
+ "endpoint_url": session.get("url_internet"),
596
+ }
597
+
598
+ except Exception as e:
599
+ logger.error(
600
+ f"FunctionComputeClient get FC function information failed: {e}",
601
+ )
602
+ # Even if API call fails, return session data
603
+ return {
604
+ "session": session,
605
+ "function_info": {},
606
+ "runtime_token": session.get("runtime_token"),
607
+ "status": session.get("status", "unknown"),
608
+ "error": str(e),
609
+ }
610
+
611
+ def _get_function_status(self, function_name):
612
+ """Get function status.
613
+
614
+ Args:
615
+ function_name (str): The name of the function to get status for.
616
+
617
+ Returns:
618
+ str: The status of the function.
619
+ """
620
+ try:
621
+ # Get function status (API call based on test verification)
622
+ function_query_request = fc20230330_models.GetFunctionRequest(
623
+ qualifier="LATEST",
624
+ )
625
+ response = self.fc_client.get_function(
626
+ function_name=function_name,
627
+ request=function_query_request,
628
+ )
629
+ # Return function status (Active, Inactive, Pending)
630
+ if hasattr(response.body, "state"):
631
+ state = response.body.state.lower()
632
+ # Map FC status to container status
633
+ if state == "active":
634
+ return "running"
635
+ elif state == "inactive":
636
+ return "exited"
637
+ elif state == "pending":
638
+ return "creating"
639
+ else:
640
+ return state
641
+ else:
642
+ return "unknown"
643
+ except Exception as e:
644
+ logger.error(
645
+ f"FunctionComputeClient get FC function status failed: {e}",
646
+ )
647
+ # If API call fails, return status from session
648
+ return "unknown"
649
+
650
+ def get_status(self, session_id):
651
+ """Get function status - Implementation based on test verification.
652
+
653
+ Args:
654
+ session_id (str): The ID of the session to get status for.
655
+
656
+ Returns:
657
+ str: The status of the function (running, exited, creating, or unknown).
658
+ """
659
+ session = self.session_manager.get_session(session_id)
660
+ if not session:
661
+ logger.warning(
662
+ f"FunctionComputeClient session record not found: {session_id}",
663
+ )
664
+ return "not_found"
665
+
666
+ function_name = session.get("function_name")
667
+ status = self._get_function_status(function_name)
668
+ if status == "unknown":
669
+ return session.get("status", "unknown")
670
+ return status
671
+
672
+ def _generate_session_id(self) -> str:
673
+ """Generate a unique session ID - Method based on test verification.
674
+
675
+ Returns:
676
+ str: A randomly generated 6-character session ID.
677
+ """
678
+ return "".join(
679
+ random.choices(string.ascii_letters + string.digits, k=6),
680
+ )
681
+
682
+ def _generate_runtime_token(self) -> str:
683
+ """Generate a runtime token for authentication.
684
+
685
+ Returns:
686
+ str: A randomly generated 16-byte hex token.
687
+ """
688
+ return secrets.token_hex(16)
689
+
690
+ def _create_http_trigger(
691
+ self,
692
+ function_name: str,
693
+ session_id: str,
694
+ ) -> dict:
695
+ """Create an HTTP trigger for the function - Implementation based on test verification.
696
+
697
+ Args:
698
+ function_name (str): The name of the function to create a trigger for.
699
+ session_id (str): The session ID for this trigger.
700
+
701
+ Returns:
702
+ dict: A dictionary containing trigger information in the format:
703
+ {
704
+ 'trigger_name': str,
705
+ 'url_internet': str,
706
+ 'url_intranet': str,
707
+ 'trigger_id': str
708
+ }
709
+ """
710
+ trigger_name = f"sandbox-http-trigger-{session_id}"
711
+
712
+ try:
713
+ logger.debug(
714
+ f"FunctionComputeClient creating HTTP trigger: {trigger_name}",
715
+ )
716
+
717
+ # Build trigger configuration (based on test verified configuration)
718
+ trigger_config_dict = {
719
+ "authType": "anonymous",
720
+ "methods": ["GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"],
721
+ }
722
+
723
+ # Create trigger input
724
+ trigger_input = fc20230330_models.CreateTriggerInput(
725
+ trigger_name=trigger_name,
726
+ trigger_type="http",
727
+ trigger_config=json.dumps(trigger_config_dict),
728
+ description=f"HTTP trigger for sandbox session {session_id}",
729
+ )
730
+
731
+ # Create trigger request
732
+ create_trigger_request = fc20230330_models.CreateTriggerRequest(
733
+ body=trigger_input,
734
+ )
735
+
736
+ # Call API to create trigger
737
+ response = self.fc_client.create_trigger_with_options(
738
+ function_name=function_name,
739
+ request=create_trigger_request,
740
+ headers={},
741
+ runtime=util_models.RuntimeOptions(),
742
+ )
743
+
744
+ logger.info(
745
+ f"FunctionComputeClient HTTP trigger created successfully: {trigger_name}",
746
+ )
747
+ logger.debug(
748
+ f"FunctionComputeClient HTTP trigger response: {response}",
749
+ )
750
+
751
+ # Extract trigger information from response
752
+ trigger_info = {
753
+ "trigger_name": trigger_name,
754
+ "url_internet": None,
755
+ "url_intranet": None,
756
+ "trigger_id": None,
757
+ "qualifier": "LATEST",
758
+ "last_modified_time": None,
759
+ "created_time": None,
760
+ "status": None,
761
+ }
762
+
763
+ # Parse response body to get URL information
764
+ if hasattr(response, "body") and response.body:
765
+ body = response.body
766
+ if hasattr(body, "http_trigger") and body.http_trigger:
767
+ http_trigger = body.http_trigger
768
+ if hasattr(http_trigger, "url_internet"):
769
+ trigger_info[
770
+ "url_internet"
771
+ ] = http_trigger.url_internet
772
+ if hasattr(http_trigger, "url_intranet"):
773
+ trigger_info[
774
+ "url_intranet"
775
+ ] = http_trigger.url_intranet
776
+
777
+ if hasattr(body, "trigger_id"):
778
+ trigger_info["trigger_id"] = body.trigger_id
779
+ if hasattr(body, "last_modified_time"):
780
+ trigger_info[
781
+ "last_modified_time"
782
+ ] = body.last_modified_time
783
+ if hasattr(body, "createdTime"):
784
+ trigger_info["created_time"] = body.created_time
785
+ if hasattr(body, "status"):
786
+ trigger_info["status"] = body.status
787
+ if hasattr(body, "qualifier"):
788
+ trigger_info["qualifier"] = body.qualifier
789
+
790
+ logger.info("FunctionComputeClient trigger URL information:")
791
+ logger.info(
792
+ f"FunctionComputeClient - Internet URL: {trigger_info['url_internet']}",
793
+ )
794
+ logger.info(
795
+ f"FunctionComputeClient - Intranet URL: {trigger_info['url_intranet']}",
796
+ )
797
+ logger.info(
798
+ f"FunctionComputeClient - Trigger ID: {trigger_info['trigger_id']}",
799
+ )
800
+
801
+ return trigger_info
802
+
803
+ except Exception as e:
804
+ logger.error(
805
+ f"FunctionComputeClient create HTTP trigger failed: {e}",
806
+ )
807
+ # Even if creation fails, return basic information for subsequent cleanup
808
+ return {
809
+ "trigger_name": trigger_name,
810
+ "url_internet": None,
811
+ "url_intranet": None,
812
+ "qualifier": "LATEST",
813
+ "latest_modified_time": None,
814
+ "created_time": None,
815
+ "status": None,
816
+ }
817
+
818
+ def _replace_fc_images(self, image: str) -> str:
819
+ """
820
+ Replace agent runtime images with their corresponding remote images.
821
+
822
+ Args:
823
+ image (str): The original image name.
824
+
825
+ Returns:
826
+ str: The replaced image name with remote registry path.
827
+ """
828
+ replacement_map = {
829
+ "agentscope/runtime-sandbox-base": "serverless-registry.cn-hangzhou.cr.aliyuncs.com/functionai" # noqa: E501
830
+ "/agentscope_runtime-sandbox-base:20251027",
831
+ "agentscope/runtime-sandbox-browser": "serverless-registry.cn-hangzhou.cr.aliyuncs.com/functionai" # noqa: E501
832
+ "/agentscope_runtime-sandbox-browser:20251027",
833
+ "agentscope/runtime-sandbox-filesystem": "serverless-registry.cn-hangzhou.cr.aliyuncs.com/functionai" # noqa: E501
834
+ "/agentscope_runtime-sandbox-filesystem:20251027",
835
+ "agentscope/runtime-sandbox-gui": "serverless-registry.cn-hangzhou.cr.aliyuncs.com/functionai" # noqa: E501
836
+ "/agentscope_runtime-sandbox-gui:20251027",
837
+ }
838
+
839
+ if ":" in image:
840
+ image_name, _ = image.split(":", 1)
841
+ else:
842
+ image_name = image
843
+
844
+ return replacement_map.get(image_name.strip(), image)
845
+
846
+ def _is_browser_image(self, image: str) -> bool:
847
+ """Check if the image is a browser image.
848
+
849
+ Args:
850
+ image (str): The image name to check.
851
+
852
+ Returns:
853
+ bool: True if the image is a browser image, False otherwise.
854
+ """
855
+ return image.startswith("agentscope/runtime-sandbox-browser")