agentscope-runtime 0.1.6__py3-none-any.whl → 0.2.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 (87) hide show
  1. agentscope_runtime/common/container_clients/__init__.py +0 -0
  2. agentscope_runtime/{sandbox/manager → common}/container_clients/kubernetes_client.py +546 -6
  3. agentscope_runtime/engine/__init__.py +12 -0
  4. agentscope_runtime/engine/agents/agentscope_agent.py +130 -10
  5. agentscope_runtime/engine/agents/agno_agent.py +8 -10
  6. agentscope_runtime/engine/agents/langgraph_agent.py +52 -9
  7. agentscope_runtime/engine/app/__init__.py +6 -0
  8. agentscope_runtime/engine/app/agent_app.py +239 -0
  9. agentscope_runtime/engine/app/base_app.py +181 -0
  10. agentscope_runtime/engine/app/celery_mixin.py +92 -0
  11. agentscope_runtime/engine/deployers/__init__.py +13 -0
  12. agentscope_runtime/engine/deployers/adapter/responses/__init__.py +0 -0
  13. agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +2890 -0
  14. agentscope_runtime/engine/deployers/adapter/responses/response_api_agent_adapter.py +51 -0
  15. agentscope_runtime/engine/deployers/adapter/responses/response_api_protocol_adapter.py +314 -0
  16. agentscope_runtime/engine/deployers/base.py +1 -0
  17. agentscope_runtime/engine/deployers/cli_fc_deploy.py +203 -0
  18. agentscope_runtime/engine/deployers/kubernetes_deployer.py +272 -0
  19. agentscope_runtime/engine/deployers/local_deployer.py +414 -501
  20. agentscope_runtime/engine/deployers/modelstudio_deployer.py +838 -0
  21. agentscope_runtime/engine/deployers/utils/__init__.py +0 -0
  22. agentscope_runtime/engine/deployers/utils/deployment_modes.py +14 -0
  23. agentscope_runtime/engine/deployers/utils/docker_image_utils/__init__.py +8 -0
  24. agentscope_runtime/engine/deployers/utils/docker_image_utils/docker_image_builder.py +429 -0
  25. agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +240 -0
  26. agentscope_runtime/engine/deployers/utils/docker_image_utils/runner_image_factory.py +306 -0
  27. agentscope_runtime/engine/deployers/utils/package_project_utils.py +1163 -0
  28. agentscope_runtime/engine/deployers/utils/service_utils/__init__.py +9 -0
  29. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +1064 -0
  30. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_templates.py +157 -0
  31. agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +268 -0
  32. agentscope_runtime/engine/deployers/utils/service_utils/service_config.py +75 -0
  33. agentscope_runtime/engine/deployers/utils/service_utils/service_factory.py +220 -0
  34. agentscope_runtime/engine/deployers/utils/service_utils/standalone_main.py.j2 +211 -0
  35. agentscope_runtime/engine/deployers/utils/wheel_packager.py +389 -0
  36. agentscope_runtime/engine/helpers/agent_api_builder.py +651 -0
  37. agentscope_runtime/engine/runner.py +76 -35
  38. agentscope_runtime/engine/schemas/agent_schemas.py +112 -2
  39. agentscope_runtime/engine/schemas/embedding.py +37 -0
  40. agentscope_runtime/engine/schemas/modelstudio_llm.py +310 -0
  41. agentscope_runtime/engine/schemas/oai_llm.py +538 -0
  42. agentscope_runtime/engine/schemas/realtime.py +254 -0
  43. agentscope_runtime/engine/services/tablestore_memory_service.py +4 -1
  44. agentscope_runtime/engine/tracing/__init__.py +9 -3
  45. agentscope_runtime/engine/tracing/asyncio_util.py +24 -0
  46. agentscope_runtime/engine/tracing/base.py +66 -34
  47. agentscope_runtime/engine/tracing/local_logging_handler.py +45 -31
  48. agentscope_runtime/engine/tracing/message_util.py +528 -0
  49. agentscope_runtime/engine/tracing/tracing_metric.py +20 -8
  50. agentscope_runtime/engine/tracing/tracing_util.py +130 -0
  51. agentscope_runtime/engine/tracing/wrapper.py +794 -169
  52. agentscope_runtime/sandbox/box/base/base_sandbox.py +2 -1
  53. agentscope_runtime/sandbox/box/browser/browser_sandbox.py +2 -1
  54. agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +2 -1
  55. agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +2 -1
  56. agentscope_runtime/sandbox/box/gui/gui_sandbox.py +2 -1
  57. agentscope_runtime/sandbox/box/training_box/training_box.py +0 -42
  58. agentscope_runtime/sandbox/client/http_client.py +52 -18
  59. agentscope_runtime/sandbox/constant.py +3 -0
  60. agentscope_runtime/sandbox/custom/custom_sandbox.py +2 -1
  61. agentscope_runtime/sandbox/custom/example.py +2 -1
  62. agentscope_runtime/sandbox/enums.py +0 -1
  63. agentscope_runtime/sandbox/manager/sandbox_manager.py +29 -22
  64. agentscope_runtime/sandbox/model/container.py +6 -0
  65. agentscope_runtime/sandbox/registry.py +1 -1
  66. agentscope_runtime/sandbox/tools/tool.py +4 -0
  67. agentscope_runtime/version.py +1 -1
  68. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/METADATA +103 -59
  69. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/RECORD +87 -52
  70. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/entry_points.txt +1 -0
  71. /agentscope_runtime/{sandbox/manager/container_clients → common}/__init__.py +0 -0
  72. /agentscope_runtime/{sandbox/manager → common}/collections/__init__.py +0 -0
  73. /agentscope_runtime/{sandbox/manager → common}/collections/base_mapping.py +0 -0
  74. /agentscope_runtime/{sandbox/manager → common}/collections/base_queue.py +0 -0
  75. /agentscope_runtime/{sandbox/manager → common}/collections/base_set.py +0 -0
  76. /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_mapping.py +0 -0
  77. /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_queue.py +0 -0
  78. /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_set.py +0 -0
  79. /agentscope_runtime/{sandbox/manager → common}/collections/redis_mapping.py +0 -0
  80. /agentscope_runtime/{sandbox/manager → common}/collections/redis_queue.py +0 -0
  81. /agentscope_runtime/{sandbox/manager → common}/collections/redis_set.py +0 -0
  82. /agentscope_runtime/{sandbox/manager → common}/container_clients/agentrun_client.py +0 -0
  83. /agentscope_runtime/{sandbox/manager → common}/container_clients/base_client.py +0 -0
  84. /agentscope_runtime/{sandbox/manager → common}/container_clients/docker_client.py +0 -0
  85. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/WHEEL +0 -0
  86. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/licenses/LICENSE +0 -0
  87. {agentscope_runtime-0.1.6.dist-info → agentscope_runtime-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,51 @@
1
+ # -*- coding: utf-8 -*-
2
+ # pylint: disable=unused-argument,line-too-long
3
+ import logging
4
+ import traceback
5
+ from typing import Callable, Dict
6
+
7
+ from agentscope_runtime.engine.deployers.adapter.responses.response_api_adapter_utils import ( # noqa: E501
8
+ ResponsesAdapter,
9
+ )
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class ResponseAPIExecutor:
15
+ def __init__(self, func: Callable, **kwargs):
16
+ self._func = func
17
+
18
+ async def execute(
19
+ self,
20
+ request: Dict,
21
+ ):
22
+ # Start executing agent
23
+ sequence_counter = 0
24
+ responses_adapter = ResponsesAdapter()
25
+
26
+ # Convert input parameters to agent api
27
+ agent_request = (
28
+ responses_adapter.convert_responses_request_to_agent_request(
29
+ request,
30
+ )
31
+ )
32
+
33
+ try:
34
+ async for event in self._func(request=agent_request):
35
+ # Convert Agent API events to Responses API events
36
+ responses_event = (
37
+ responses_adapter.convert_agent_event_to_responses_event(
38
+ event,
39
+ )
40
+ )
41
+
42
+ event_count = 0
43
+ if responses_event:
44
+ for event in responses_event:
45
+ # Uniformly set sequence_number
46
+ event.sequence_number = sequence_counter
47
+ sequence_counter += 1
48
+ event_count += 1
49
+ yield event
50
+ except Exception as e:
51
+ logger.error(f"An error occurred: {e}, {traceback.format_exc()}")
@@ -0,0 +1,314 @@
1
+ # -*- coding: utf-8 -*-
2
+ import asyncio
3
+ import json
4
+ import logging
5
+ import traceback
6
+ from typing import Callable, Dict, Any, Optional, AsyncGenerator
7
+ from uuid import uuid4
8
+
9
+ from fastapi import FastAPI, Request, HTTPException
10
+ from fastapi.responses import StreamingResponse, JSONResponse
11
+
12
+ # OpenAI Response API official type definition
13
+ from openai.types.responses import ResponseCreateParams
14
+
15
+ from .response_api_adapter_utils import ResponsesAdapter
16
+ from .response_api_agent_adapter import ResponseAPIExecutor
17
+ from ..protocol_adapter import ProtocolAdapter
18
+ from ....schemas.agent_schemas import AgentRequest, BaseResponse
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ RESPONSES_ENDPOINT_PATH = "/compatible-mode/v1/responses"
23
+ SSE_HEADERS = {
24
+ "Cache-Control": "no-cache",
25
+ "Connection": "keep-alive",
26
+ "X-Accel-Buffering": "no", # disable Nginx buffering
27
+ "Access-Control-Allow-Origin": "*",
28
+ "Access-Control-Allow-Methods": "POST, OPTIONS",
29
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
30
+ }
31
+
32
+
33
+ class ResponseAPIDefaultAdapter(ProtocolAdapter):
34
+ def __init__(self, **kwargs):
35
+ super().__init__(**kwargs)
36
+ self._executor: Optional[ResponseAPIExecutor] = None
37
+ self._timeout = kwargs.get("timeout", 300) # seconds
38
+ self._max_concurrent_requests = kwargs.get(
39
+ "max_concurrent_requests",
40
+ 100,
41
+ )
42
+ self._semaphore = asyncio.Semaphore(self._max_concurrent_requests)
43
+
44
+ async def _handle_requests(self, request: Request) -> StreamingResponse:
45
+ """
46
+ Handle OpenAI Response API request.
47
+
48
+ Args:
49
+ request: FastAPI Request object
50
+
51
+ Returns:
52
+ StreamingResponse: SSE streaming response
53
+ """
54
+ # Concurrency guard per request
55
+ await self._semaphore.acquire()
56
+ request_id = f"resp_{uuid4()}"
57
+ logger.info("[ResponseAPI] start request_id=%s", request_id)
58
+ try:
59
+ # Parse request body
60
+ request_data = await request.json()
61
+
62
+ stream = request_data.get("stream", False)
63
+
64
+ # Stream or non-stream
65
+ if stream:
66
+ # Return SSE streaming response with timeout control
67
+ return StreamingResponse(
68
+ self._generate_stream_response_with_timeout(
69
+ request=request_data,
70
+ request_id=request_id,
71
+ ),
72
+ media_type="text/event-stream",
73
+ headers=SSE_HEADERS,
74
+ )
75
+
76
+ # Non-stream JSON response
77
+ response_obj = await self._collect_non_stream_response(
78
+ request=request_data,
79
+ request_id=request_id,
80
+ )
81
+ return JSONResponse(content=response_obj)
82
+
83
+ except HTTPException:
84
+ raise
85
+ except Exception as e:
86
+ logger.error(
87
+ f"Unexpected error in _handle_requests: {e}\n"
88
+ f"{traceback.format_exc()}",
89
+ )
90
+ raise HTTPException(
91
+ status_code=500,
92
+ detail="Internal server error",
93
+ ) from e
94
+ finally:
95
+ self._semaphore.release()
96
+ logger.info("[ResponseAPI] end request_id=%s", request_id)
97
+
98
+ def _convert_response_request_to_agent_request(
99
+ self,
100
+ response_request: ResponseCreateParams,
101
+ ) -> AgentRequest:
102
+ """
103
+ Convert an OpenAI Response API request to Agent API request.
104
+
105
+ Args:
106
+ response_request: OpenAI Response API request
107
+
108
+ Returns:
109
+ AgentRequest: Agent API request
110
+ """
111
+ # Convert with ResponsesAdapter
112
+ adapter = ResponsesAdapter()
113
+ agent_request = adapter.convert_responses_request_to_agent_request(
114
+ response_request.model_dump(),
115
+ )
116
+
117
+ return agent_request
118
+
119
+ async def _collect_non_stream_response(
120
+ self,
121
+ request: Dict,
122
+ request_id: str,
123
+ ) -> Dict[str, Any]:
124
+ """
125
+ Run the agent and build a non-streaming OpenAI Responses API object.
126
+
127
+ This collects events until completion and returns a single JSON object
128
+ representing the final response.
129
+ """
130
+ last_agent_response: Optional[BaseResponse] = None
131
+ try:
132
+ async for event in self._executor.execute(request):
133
+ last_agent_response = event
134
+ except Exception:
135
+ # Map to Responses error shape
136
+ return {
137
+ "id": request_id,
138
+ "object": "response",
139
+ "status": "failed",
140
+ "error": {
141
+ "code": "non_stream_error",
142
+ "message": "Failed to build non-stream response",
143
+ },
144
+ }
145
+
146
+ if not last_agent_response:
147
+ # No response produced
148
+ return {
149
+ "id": request_id,
150
+ "object": "response",
151
+ "status": "failed",
152
+ "error": {
153
+ "code": "empty_response",
154
+ "message": "No response produced by agent",
155
+ },
156
+ }
157
+
158
+ # Convert to dict for JSONResponse
159
+ return last_agent_response.model_dump(exclude_none=True)
160
+
161
+ async def _generate_stream_response_with_timeout(
162
+ self,
163
+ request: Dict,
164
+ request_id: str,
165
+ ) -> AsyncGenerator[str, None]:
166
+ """
167
+ Generate SSE streaming response with timeout.
168
+
169
+ Args:
170
+ agent_request: Agent API request
171
+
172
+ Yields:
173
+ str: SSE data chunks
174
+ """
175
+ try:
176
+ # Add timeout with asyncio.wait_for
177
+ async for chunk in self._generate_stream_response(
178
+ request=request,
179
+ request_id=request_id,
180
+ ):
181
+ yield chunk
182
+ except asyncio.TimeoutError:
183
+ logger.error(f"Request timeout after {self._timeout} seconds")
184
+ # Send timeout error event
185
+ timeout_event = {
186
+ "id": request_id,
187
+ "object": "response",
188
+ "status": "failed",
189
+ "error": {
190
+ "code": "timeout",
191
+ "message": f"Request timed out after "
192
+ f"{self._timeout} seconds",
193
+ },
194
+ }
195
+ yield (
196
+ f"event: response.failed\n"
197
+ f"data: {json.dumps(timeout_event)}\n\n"
198
+ )
199
+ except Exception as e:
200
+ logger.error(
201
+ f"Error in timeout-controlled stream: {e}\n"
202
+ f"{traceback.format_exc()}",
203
+ )
204
+ # Send error event
205
+ error_event = {
206
+ "id": request_id,
207
+ "object": "response",
208
+ "status": "failed",
209
+ "error": {
210
+ "code": "stream_error",
211
+ "message": str(e),
212
+ },
213
+ }
214
+ yield (
215
+ f"event: response.failed\n"
216
+ f"data: {json.dumps(error_event)}\n\n"
217
+ )
218
+
219
+ async def _generate_stream_response(
220
+ self,
221
+ request: Dict,
222
+ request_id: str,
223
+ ) -> AsyncGenerator[str, None]:
224
+ """
225
+ Generate SSE streaming response.
226
+
227
+ Args:
228
+ request: Responses API request
229
+
230
+ Yields:
231
+ str: SSE data chunks
232
+ """
233
+ try:
234
+ # Handle streaming events
235
+ async for event in self._executor.execute(request):
236
+ try:
237
+ if event:
238
+ # Serialize event
239
+ event_data = event.model_dump(exclude_none=True)
240
+ data = json.dumps(event_data, ensure_ascii=False)
241
+
242
+ # Set SSE event type based on event type
243
+ event_type = event_data.get("type", "message")
244
+ yield f"event: {event_type}\ndata: {data}\n\n"
245
+
246
+ except Exception as e:
247
+ logger.error(
248
+ f"Error processing event: {e}\n"
249
+ f"{traceback.format_exc()}",
250
+ )
251
+ # Send error event
252
+ error_event = {
253
+ "id": request_id,
254
+ "object": "response",
255
+ "status": "failed",
256
+ "error": {
257
+ "code": "processing_error",
258
+ "message": str(e),
259
+ },
260
+ }
261
+ yield (
262
+ f"event: response.failed\n"
263
+ f"data: {json.dumps(error_event)}\n\n"
264
+ )
265
+ break
266
+ except Exception as e:
267
+ logger.error(
268
+ f"Error in stream generation: {e}\n{traceback.format_exc()}",
269
+ )
270
+ # Send error event
271
+ error_event = {
272
+ "id": request_id,
273
+ "object": "response",
274
+ "status": "failed",
275
+ "error": {
276
+ "code": "stream_error",
277
+ "message": str(e),
278
+ },
279
+ }
280
+ yield (
281
+ f"event: response.failed\n"
282
+ f"data: {json.dumps(error_event)}\n\n"
283
+ )
284
+
285
+ def add_endpoint(self, app: FastAPI, func: Callable, **kwargs) -> None:
286
+ """
287
+ Add endpoint to FastAPI app.
288
+
289
+ Args:
290
+ app: FastAPI application instance
291
+ func: handler function
292
+ **kwargs: extra options
293
+ """
294
+ # Create executor
295
+ self._executor = ResponseAPIExecutor(func=func)
296
+
297
+ # Register route
298
+ app.post(
299
+ RESPONSES_ENDPOINT_PATH,
300
+ openapi_extra={
301
+ "requestBody": {
302
+ "content": {
303
+ "application/json": {
304
+ "schema": {
305
+ "$ref": "#/components/schemas/ResponseAPI",
306
+ },
307
+ },
308
+ },
309
+ "required": True,
310
+ "description": "OpenAI Response API compatible "
311
+ "request format",
312
+ },
313
+ },
314
+ )(self._handle_requests)
@@ -9,6 +9,7 @@ from typing import Dict
9
9
  class DeployManager(ABC):
10
10
  def __init__(self):
11
11
  self.deploy_id = str(uuid.uuid4())
12
+ self._app = None
12
13
 
13
14
  @abstractmethod
14
15
  async def deploy(self, *args, **kwargs) -> Dict[str, str]:
@@ -0,0 +1,203 @@
1
+ # -*- coding: utf-8 -*-
2
+ import argparse
3
+ import asyncio
4
+ from pathlib import Path
5
+ from typing import Optional
6
+
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+ from rich.table import Table
10
+
11
+ from .modelstudio_deployer import ModelstudioDeployManager
12
+ from .utils.wheel_packager import build_wheel
13
+
14
+
15
+ def _parse_args() -> argparse.Namespace:
16
+ parser = argparse.ArgumentParser(
17
+ description="One-click deploy your service to Alibaba Bailian "
18
+ "Function Compute (FC)",
19
+ )
20
+ parser.add_argument(
21
+ "--mode",
22
+ choices=["wrapper", "native"],
23
+ default="wrapper",
24
+ help="Build mode: wrapper (default) packages your project into a "
25
+ "starter; native builds your current project directly.",
26
+ )
27
+ parser.add_argument(
28
+ "--whl-path",
29
+ dest="whl_path",
30
+ default=None,
31
+ help="Path to an external wheel file to deploy directly (skip build)",
32
+ )
33
+ parser.add_argument(
34
+ "--dir",
35
+ default=None,
36
+ help="Path to your project directory (wrapper mode)",
37
+ )
38
+ parser.add_argument(
39
+ "--cmd",
40
+ default=None,
41
+ help="Command to start your service (wrapper mode), e.g., 'python "
42
+ "app.py'",
43
+ )
44
+ parser.add_argument(
45
+ "--deploy-name",
46
+ dest="deploy_name",
47
+ default=None,
48
+ help="Deploy name (agent_name). Random if omitted",
49
+ )
50
+ parser.add_argument(
51
+ "--skip-upload",
52
+ action="store_true",
53
+ help="Only build wheel, do not upload/deploy",
54
+ )
55
+ parser.add_argument(
56
+ "--telemetry",
57
+ choices=["enable", "disable"],
58
+ default="enable",
59
+ help="Enable or disable telemetry (default: enable)",
60
+ )
61
+ parser.add_argument(
62
+ "--build-root",
63
+ dest="build_root",
64
+ default=None,
65
+ help="Custom directory for temporary build artifacts (optional)",
66
+ )
67
+ parser.add_argument(
68
+ "--update",
69
+ dest="agent_id",
70
+ default=None,
71
+ help="Update an existing agent. "
72
+ "Specify agent_id to update a particular agent (optional)",
73
+ )
74
+ parser.add_argument(
75
+ "--desc",
76
+ dest="agent_desc",
77
+ default=None,
78
+ help="Add description to current agent(optional)",
79
+ )
80
+ return parser.parse_args()
81
+
82
+
83
+ async def _run(
84
+ dir_path: Optional[str],
85
+ cmd: Optional[str],
86
+ deploy_name: Optional[str],
87
+ skip_upload: bool,
88
+ telemetry_enabled: bool,
89
+ build_root: Optional[str],
90
+ mode: str,
91
+ whl_path: Optional[str],
92
+ agent_id: Optional[str],
93
+ agent_desc: Optional[str],
94
+ ):
95
+ deployer = ModelstudioDeployManager(build_root=build_root)
96
+ # If a wheel path is provided, skip local build entirely
97
+ if whl_path:
98
+ return await deployer.deploy(
99
+ project_dir=None,
100
+ cmd=None,
101
+ deploy_name=deploy_name,
102
+ skip_upload=skip_upload,
103
+ telemetry_enabled=telemetry_enabled,
104
+ external_whl_path=whl_path,
105
+ agent_id=agent_id,
106
+ agent_desc=agent_desc,
107
+ )
108
+
109
+ if mode == "native":
110
+ # Build the current project directly as a wheel, then upload/deploy
111
+ project_dir_path = Path.cwd()
112
+ built_whl = await build_wheel(project_dir_path)
113
+ return await deployer.deploy(
114
+ project_dir=None,
115
+ cmd=None,
116
+ deploy_name=deploy_name,
117
+ skip_upload=skip_upload,
118
+ telemetry_enabled=telemetry_enabled,
119
+ external_whl_path=str(built_whl),
120
+ agent_id=agent_id,
121
+ agent_desc=agent_desc,
122
+ )
123
+
124
+ # wrapper mode (default): require dir and cmd
125
+ if not agent_id:
126
+ if not dir_path or not cmd:
127
+ raise SystemExit(
128
+ "In wrapper mode, --dir and --cmd are required. Alternatively "
129
+ "use --mode native or --whl-path.",
130
+ )
131
+ return await deployer.deploy(
132
+ project_dir=dir_path,
133
+ cmd=cmd,
134
+ deploy_name=deploy_name,
135
+ skip_upload=skip_upload,
136
+ telemetry_enabled=telemetry_enabled,
137
+ agent_id=agent_id,
138
+ agent_desc=agent_desc,
139
+ )
140
+
141
+
142
+ def main() -> None:
143
+ args = _parse_args()
144
+ telemetry_enabled = args.telemetry == "enable"
145
+ result = asyncio.run(
146
+ _run(
147
+ dir_path=args.dir,
148
+ cmd=args.cmd,
149
+ deploy_name=args.deploy_name,
150
+ skip_upload=args.skip_upload,
151
+ telemetry_enabled=telemetry_enabled,
152
+ build_root=args.build_root,
153
+ mode=args.mode,
154
+ whl_path=args.whl_path,
155
+ agent_id=args.agent_id,
156
+ agent_desc=args.agent_desc,
157
+ ),
158
+ )
159
+
160
+ console = Console()
161
+
162
+ # Create a table for basic information
163
+ info_table = Table(show_header=False, box=None, padding=(0, 2))
164
+ info_table.add_column("Key", style="bold cyan")
165
+ info_table.add_column("Value", style="white")
166
+
167
+ if result.get("wheel_path"):
168
+ info_table.add_row("Built wheel at", result.get("wheel_path", ""))
169
+
170
+ if result.get("artifact_url"):
171
+ info_table.add_row("Artifact URL", result.get("artifact_url"))
172
+
173
+ if result.get("resource_name"):
174
+ info_table.add_row("Resource Name", result.get("resource_name"))
175
+
176
+ if result.get("workspace_id"):
177
+ info_table.add_row("Workspace", result.get("workspace_id"))
178
+
179
+ console.print(info_table)
180
+
181
+ # Display deploy result in a panel
182
+ console_url = result.get("url")
183
+ deploy_id = result.get("deploy_id")
184
+ if console_url and deploy_id:
185
+ console.print() # Add spacing
186
+ panel_content = (
187
+ f"[bold cyan]Console URL:[/bold cyan] {console_url}\n"
188
+ f"[bold cyan]Deploy ID:[/bold cyan] {deploy_id}"
189
+ )
190
+ console.print(
191
+ Panel(
192
+ panel_content,
193
+ title="[bold green]Deploy Result[/bold green]",
194
+ title_align="center",
195
+ expand=False,
196
+ border_style="green",
197
+ ),
198
+ )
199
+ console.print() # Add spacing
200
+
201
+
202
+ if __name__ == "__main__": # pragma: no cover
203
+ main()