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.
- agentscope_runtime/adapters/__init__.py +0 -0
- agentscope_runtime/adapters/agentscope/__init__.py +0 -0
- agentscope_runtime/adapters/agentscope/long_term_memory/__init__.py +6 -0
- agentscope_runtime/adapters/agentscope/long_term_memory/_long_term_memory_adapter.py +258 -0
- agentscope_runtime/adapters/agentscope/memory/__init__.py +6 -0
- agentscope_runtime/adapters/agentscope/memory/_memory_adapter.py +152 -0
- agentscope_runtime/adapters/agentscope/message.py +535 -0
- agentscope_runtime/adapters/agentscope/stream.py +506 -0
- agentscope_runtime/adapters/agentscope/tool/__init__.py +9 -0
- agentscope_runtime/adapters/agentscope/tool/sandbox_tool.py +69 -0
- agentscope_runtime/adapters/agentscope/tool/tool.py +233 -0
- agentscope_runtime/adapters/autogen/__init__.py +0 -0
- agentscope_runtime/adapters/autogen/tool/__init__.py +7 -0
- agentscope_runtime/adapters/autogen/tool/tool.py +211 -0
- agentscope_runtime/adapters/text/__init__.py +0 -0
- agentscope_runtime/adapters/text/stream.py +29 -0
- agentscope_runtime/common/collections/redis_mapping.py +4 -1
- agentscope_runtime/common/container_clients/fc_client.py +855 -0
- agentscope_runtime/common/utils/__init__.py +0 -0
- agentscope_runtime/common/utils/lazy_loader.py +57 -0
- agentscope_runtime/engine/__init__.py +25 -18
- agentscope_runtime/engine/app/agent_app.py +161 -91
- agentscope_runtime/engine/app/base_app.py +4 -118
- agentscope_runtime/engine/constant.py +8 -0
- agentscope_runtime/engine/deployers/__init__.py +8 -0
- agentscope_runtime/engine/deployers/adapter/__init__.py +2 -0
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_adapter_utils.py +0 -21
- agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +28 -9
- agentscope_runtime/engine/deployers/adapter/responses/__init__.py +2 -0
- agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +5 -2
- agentscope_runtime/engine/deployers/adapter/responses/response_api_protocol_adapter.py +1 -1
- agentscope_runtime/engine/deployers/agentrun_deployer.py +2541 -0
- agentscope_runtime/engine/deployers/cli_fc_deploy.py +1 -1
- agentscope_runtime/engine/deployers/kubernetes_deployer.py +9 -21
- agentscope_runtime/engine/deployers/local_deployer.py +47 -74
- agentscope_runtime/engine/deployers/modelstudio_deployer.py +216 -50
- agentscope_runtime/engine/deployers/utils/app_runner_utils.py +29 -0
- agentscope_runtime/engine/deployers/utils/detached_app.py +510 -0
- agentscope_runtime/engine/deployers/utils/docker_image_utils/__init__.py +1 -1
- agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +1 -1
- agentscope_runtime/engine/deployers/utils/docker_image_utils/{runner_image_factory.py → image_factory.py} +121 -61
- agentscope_runtime/engine/deployers/utils/package.py +693 -0
- agentscope_runtime/engine/deployers/utils/service_utils/__init__.py +0 -5
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +301 -282
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_templates.py +2 -4
- agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +23 -1
- agentscope_runtime/engine/deployers/utils/templates/app_main.py.j2 +84 -0
- agentscope_runtime/engine/deployers/utils/templates/runner_main.py.j2 +95 -0
- agentscope_runtime/engine/deployers/utils/{service_utils → templates}/standalone_main.py.j2 +0 -45
- agentscope_runtime/engine/deployers/utils/wheel_packager.py +119 -18
- agentscope_runtime/engine/helpers/runner.py +40 -0
- agentscope_runtime/engine/runner.py +171 -130
- agentscope_runtime/engine/schemas/agent_schemas.py +114 -3
- agentscope_runtime/engine/schemas/modelstudio_llm.py +4 -2
- agentscope_runtime/engine/schemas/oai_llm.py +23 -23
- agentscope_runtime/engine/schemas/response_api.py +65 -0
- agentscope_runtime/engine/schemas/session.py +24 -0
- agentscope_runtime/engine/services/__init__.py +0 -9
- agentscope_runtime/engine/services/agent_state/__init__.py +16 -0
- agentscope_runtime/engine/services/agent_state/redis_state_service.py +113 -0
- agentscope_runtime/engine/services/agent_state/state_service.py +179 -0
- agentscope_runtime/engine/services/memory/__init__.py +24 -0
- agentscope_runtime/engine/services/{mem0_memory_service.py → memory/mem0_memory_service.py} +17 -13
- agentscope_runtime/engine/services/{memory_service.py → memory/memory_service.py} +28 -7
- agentscope_runtime/engine/services/{redis_memory_service.py → memory/redis_memory_service.py} +1 -1
- agentscope_runtime/engine/services/{reme_personal_memory_service.py → memory/reme_personal_memory_service.py} +9 -6
- agentscope_runtime/engine/services/{reme_task_memory_service.py → memory/reme_task_memory_service.py} +2 -2
- agentscope_runtime/engine/services/{tablestore_memory_service.py → memory/tablestore_memory_service.py} +12 -18
- agentscope_runtime/engine/services/sandbox/__init__.py +13 -0
- agentscope_runtime/engine/services/{sandbox_service.py → sandbox/sandbox_service.py} +86 -71
- agentscope_runtime/engine/services/session_history/__init__.py +23 -0
- agentscope_runtime/engine/services/{redis_session_history_service.py → session_history/redis_session_history_service.py} +3 -2
- agentscope_runtime/engine/services/{session_history_service.py → session_history/session_history_service.py} +44 -34
- agentscope_runtime/engine/services/{tablestore_session_history_service.py → session_history/tablestore_session_history_service.py} +14 -19
- agentscope_runtime/engine/services/utils/tablestore_service_utils.py +2 -2
- agentscope_runtime/engine/tracing/base.py +10 -9
- agentscope_runtime/engine/tracing/message_util.py +1 -1
- agentscope_runtime/engine/tracing/tracing_util.py +7 -2
- agentscope_runtime/engine/tracing/wrapper.py +49 -31
- agentscope_runtime/sandbox/__init__.py +10 -2
- agentscope_runtime/sandbox/box/agentbay/__init__.py +4 -0
- agentscope_runtime/sandbox/box/agentbay/agentbay_sandbox.py +559 -0
- agentscope_runtime/sandbox/box/base/base_sandbox.py +12 -0
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +115 -11
- agentscope_runtime/sandbox/box/cloud/__init__.py +4 -0
- agentscope_runtime/sandbox/box/cloud/cloud_sandbox.py +254 -0
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +66 -0
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +42 -0
- agentscope_runtime/sandbox/box/mobile/__init__.py +4 -0
- agentscope_runtime/sandbox/box/mobile/box/__init__.py +0 -0
- agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +216 -0
- agentscope_runtime/sandbox/box/training_box/training_box.py +2 -2
- agentscope_runtime/sandbox/client/http_client.py +1 -0
- agentscope_runtime/sandbox/enums.py +2 -0
- agentscope_runtime/sandbox/manager/sandbox_manager.py +15 -2
- agentscope_runtime/sandbox/manager/server/app.py +12 -0
- agentscope_runtime/sandbox/manager/server/config.py +19 -0
- agentscope_runtime/sandbox/model/manager_config.py +79 -2
- agentscope_runtime/sandbox/utils.py +0 -18
- agentscope_runtime/tools/RAGs/__init__.py +0 -0
- agentscope_runtime/tools/RAGs/modelstudio_rag.py +377 -0
- agentscope_runtime/tools/RAGs/modelstudio_rag_lite.py +219 -0
- agentscope_runtime/tools/__init__.py +119 -0
- agentscope_runtime/tools/_constants.py +18 -0
- agentscope_runtime/tools/alipay/__init__.py +4 -0
- agentscope_runtime/tools/alipay/base.py +334 -0
- agentscope_runtime/tools/alipay/payment.py +835 -0
- agentscope_runtime/tools/alipay/subscribe.py +551 -0
- agentscope_runtime/tools/base.py +264 -0
- agentscope_runtime/tools/cli/__init__.py +0 -0
- agentscope_runtime/tools/cli/modelstudio_mcp_server.py +78 -0
- agentscope_runtime/tools/generations/__init__.py +75 -0
- agentscope_runtime/tools/generations/async_image_to_video.py +350 -0
- agentscope_runtime/tools/generations/async_image_to_video_wan25.py +366 -0
- agentscope_runtime/tools/generations/async_speech_to_video.py +422 -0
- agentscope_runtime/tools/generations/async_text_to_video.py +320 -0
- agentscope_runtime/tools/generations/async_text_to_video_wan25.py +334 -0
- agentscope_runtime/tools/generations/image_edit.py +208 -0
- agentscope_runtime/tools/generations/image_edit_wan25.py +193 -0
- agentscope_runtime/tools/generations/image_generation.py +202 -0
- agentscope_runtime/tools/generations/image_generation_wan25.py +201 -0
- agentscope_runtime/tools/generations/image_style_repaint.py +208 -0
- agentscope_runtime/tools/generations/image_to_video.py +233 -0
- agentscope_runtime/tools/generations/qwen_image_edit.py +205 -0
- agentscope_runtime/tools/generations/qwen_image_generation.py +214 -0
- agentscope_runtime/tools/generations/qwen_text_to_speech.py +154 -0
- agentscope_runtime/tools/generations/speech_to_text.py +260 -0
- agentscope_runtime/tools/generations/speech_to_video.py +314 -0
- agentscope_runtime/tools/generations/text_to_video.py +221 -0
- agentscope_runtime/tools/mcp_wrapper.py +215 -0
- agentscope_runtime/tools/realtime_clients/__init__.py +13 -0
- agentscope_runtime/tools/realtime_clients/asr_client.py +27 -0
- agentscope_runtime/tools/realtime_clients/azure_asr_client.py +195 -0
- agentscope_runtime/tools/realtime_clients/azure_tts_client.py +383 -0
- agentscope_runtime/tools/realtime_clients/modelstudio_asr_client.py +151 -0
- agentscope_runtime/tools/realtime_clients/modelstudio_tts_client.py +199 -0
- agentscope_runtime/tools/realtime_clients/realtime_tool.py +55 -0
- agentscope_runtime/tools/realtime_clients/tts_client.py +33 -0
- agentscope_runtime/tools/searches/__init__.py +3 -0
- agentscope_runtime/tools/searches/modelstudio_search.py +877 -0
- agentscope_runtime/tools/searches/modelstudio_search_lite.py +310 -0
- agentscope_runtime/tools/utils/__init__.py +0 -0
- agentscope_runtime/tools/utils/api_key_util.py +45 -0
- agentscope_runtime/tools/utils/crypto_utils.py +99 -0
- agentscope_runtime/tools/utils/mcp_util.py +35 -0
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0.dist-info}/METADATA +240 -168
- agentscope_runtime-1.0.0.dist-info/RECORD +240 -0
- {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0.dist-info}/entry_points.txt +1 -0
- agentscope_runtime/engine/agents/__init__.py +0 -2
- agentscope_runtime/engine/agents/agentscope_agent.py +0 -488
- agentscope_runtime/engine/agents/agno_agent.py +0 -220
- agentscope_runtime/engine/agents/autogen_agent.py +0 -250
- agentscope_runtime/engine/agents/base_agent.py +0 -29
- agentscope_runtime/engine/agents/langgraph_agent.py +0 -59
- agentscope_runtime/engine/agents/utils.py +0 -53
- agentscope_runtime/engine/deployers/utils/package_project_utils.py +0 -1163
- agentscope_runtime/engine/deployers/utils/service_utils/service_config.py +0 -75
- agentscope_runtime/engine/deployers/utils/service_utils/service_factory.py +0 -220
- agentscope_runtime/engine/helpers/helper.py +0 -179
- agentscope_runtime/engine/schemas/context.py +0 -54
- agentscope_runtime/engine/services/context_manager.py +0 -164
- agentscope_runtime/engine/services/environment_manager.py +0 -50
- agentscope_runtime/engine/services/manager.py +0 -174
- agentscope_runtime/engine/services/rag_service.py +0 -195
- agentscope_runtime/engine/services/tablestore_rag_service.py +0 -143
- agentscope_runtime/sandbox/tools/__init__.py +0 -12
- agentscope_runtime/sandbox/tools/base/__init__.py +0 -8
- agentscope_runtime/sandbox/tools/base/tool.py +0 -52
- agentscope_runtime/sandbox/tools/browser/__init__.py +0 -57
- agentscope_runtime/sandbox/tools/browser/tool.py +0 -597
- agentscope_runtime/sandbox/tools/filesystem/__init__.py +0 -32
- agentscope_runtime/sandbox/tools/filesystem/tool.py +0 -319
- agentscope_runtime/sandbox/tools/function_tool.py +0 -321
- agentscope_runtime/sandbox/tools/gui/__init__.py +0 -7
- agentscope_runtime/sandbox/tools/gui/tool.py +0 -77
- agentscope_runtime/sandbox/tools/mcp_tool.py +0 -195
- agentscope_runtime/sandbox/tools/sandbox_tool.py +0 -104
- agentscope_runtime/sandbox/tools/tool.py +0 -238
- agentscope_runtime/sandbox/tools/utils.py +0 -68
- agentscope_runtime-0.2.0b2.dist-info/RECORD +0 -183
- {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0.dist-info}/WHEEL +0 -0
- {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-0.2.0b2.dist-info → agentscope_runtime-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# pylint:disable=unused-argument
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Project-based packaging utilities for AgentApp and Runner deployment.
|
|
6
|
+
|
|
7
|
+
This module provides packaging utilities that support:
|
|
8
|
+
- Function-based AgentApp deployment with decorators
|
|
9
|
+
- Runner-based deployment with entrypoint files
|
|
10
|
+
- Entire project directory packaging
|
|
11
|
+
- Smart dependency caching
|
|
12
|
+
- CLI-style and object-style deployment patterns
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import inspect
|
|
16
|
+
import logging
|
|
17
|
+
import os
|
|
18
|
+
import shutil
|
|
19
|
+
import tempfile
|
|
20
|
+
import zipfile
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Optional, List, Tuple, Union
|
|
23
|
+
|
|
24
|
+
from jinja2 import Environment, FileSystemLoader, TemplateNotFound
|
|
25
|
+
from pydantic import BaseModel
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
DEPLOYMENT_ZIP = "deployment.zip"
|
|
30
|
+
TEMPLATES_DIR = Path(__file__).parent / "templates"
|
|
31
|
+
DEFAULT_ENTRYPOINT_FILE = "runtime_main.py"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_template_env() -> Environment:
|
|
35
|
+
"""
|
|
36
|
+
Get Jinja2 environment for template rendering.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Jinja2 Environment configured with the templates directory
|
|
40
|
+
"""
|
|
41
|
+
return Environment(
|
|
42
|
+
loader=FileSystemLoader(str(TEMPLATES_DIR)),
|
|
43
|
+
trim_blocks=True,
|
|
44
|
+
lstrip_blocks=True,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ===== Data Models =====
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class RuntimeParameter(BaseModel):
|
|
52
|
+
"""Configuration for a runtime parameter."""
|
|
53
|
+
|
|
54
|
+
name: str # Parameter name (e.g., "log_level")
|
|
55
|
+
type: str # Parameter type: "str", "int", "bool", "float"
|
|
56
|
+
default: Union[str, int, bool, float, None] # Default value
|
|
57
|
+
help: Optional[str] = None # Help text for CLI argument
|
|
58
|
+
cli_name: Optional[str] = None # CLI argument name (defaults to --{name})
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class EntrypointInfo(BaseModel):
|
|
62
|
+
"""Information about the generated entrypoint."""
|
|
63
|
+
|
|
64
|
+
module_name: str # Module to import from (e.g., "app_deploy")
|
|
65
|
+
object_name: str # Object name to import (e.g., "agent_app")
|
|
66
|
+
object_type: str # "app" or "runner"
|
|
67
|
+
host: str = "0.0.0.0" # Default host for the service
|
|
68
|
+
port: int = 8090 # Default port for the service
|
|
69
|
+
extra_parameters: List[
|
|
70
|
+
RuntimeParameter
|
|
71
|
+
] = [] # Additional runtime parameters
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ProjectInfo(BaseModel):
|
|
75
|
+
"""Information about a project to be packaged."""
|
|
76
|
+
|
|
77
|
+
project_dir: str # Absolute path to project root directory
|
|
78
|
+
entrypoint_file: str # Relative path to entrypoint file (if applicable)
|
|
79
|
+
entrypoint_handler: str # actual object name, e.g., "agent_app"
|
|
80
|
+
handler_type: Optional[str] = None # Handler type ("app" or "runner")
|
|
81
|
+
is_directory_entrypoint: bool = False # True if packaging entire directory
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ===== Project Directory Extraction =====
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def project_dir_extractor(
|
|
88
|
+
app=None,
|
|
89
|
+
runner=None,
|
|
90
|
+
) -> ProjectInfo:
|
|
91
|
+
"""
|
|
92
|
+
Extract project directory information from app or runner object.
|
|
93
|
+
|
|
94
|
+
This function inspects the call stack to find where the app or runner
|
|
95
|
+
was defined and extracts the project root directory and object name.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
app: AgentApp instance (optional)
|
|
99
|
+
runner: Runner instance (optional)
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
ProjectInfo with project directory, entrypoint file, and handler name
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
ValueError: If neither app nor runner is provided or project dir
|
|
106
|
+
cannot be determined
|
|
107
|
+
"""
|
|
108
|
+
if app is None and runner is None:
|
|
109
|
+
raise ValueError("Either app or runner must be provided")
|
|
110
|
+
|
|
111
|
+
target_obj = app if app is not None else runner
|
|
112
|
+
target_type = "app" if app is not None else "runner"
|
|
113
|
+
|
|
114
|
+
# Get the source file where the object was defined
|
|
115
|
+
frame = inspect.currentframe()
|
|
116
|
+
caller_frame = frame.f_back if frame else None
|
|
117
|
+
|
|
118
|
+
project_file = None
|
|
119
|
+
object_name = None # Store the actual object name
|
|
120
|
+
|
|
121
|
+
# Try to find the file where the object was created
|
|
122
|
+
while caller_frame:
|
|
123
|
+
try:
|
|
124
|
+
frame_filename = caller_frame.f_code.co_filename
|
|
125
|
+
|
|
126
|
+
# Skip internal/system files and focus on user code
|
|
127
|
+
if (
|
|
128
|
+
not frame_filename.endswith(".py")
|
|
129
|
+
or "site-packages" in frame_filename
|
|
130
|
+
or "agentscope_runtime" in frame_filename
|
|
131
|
+
):
|
|
132
|
+
caller_frame = caller_frame.f_back
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
# Check if this frame contains our target object
|
|
136
|
+
frame_locals = caller_frame.f_locals
|
|
137
|
+
frame_globals = caller_frame.f_globals
|
|
138
|
+
|
|
139
|
+
# Look for the object (by identity) in locals and globals
|
|
140
|
+
for var_name, var_value in list(frame_locals.items()) + list(
|
|
141
|
+
frame_globals.items(),
|
|
142
|
+
):
|
|
143
|
+
if var_value is target_obj:
|
|
144
|
+
project_file = frame_filename
|
|
145
|
+
object_name = var_name # Capture the actual object name!
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
if project_file:
|
|
149
|
+
break
|
|
150
|
+
|
|
151
|
+
except (AttributeError, TypeError) as e:
|
|
152
|
+
logger.warning(
|
|
153
|
+
f"Ignore Attribute or Type error: {e}",
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
caller_frame = caller_frame.f_back
|
|
157
|
+
|
|
158
|
+
if not project_file or not os.path.exists(project_file):
|
|
159
|
+
raise ValueError(
|
|
160
|
+
f"Unable to locate source file for {target_type} object",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# The project directory is the directory containing the file
|
|
164
|
+
project_dir = os.path.dirname(os.path.abspath(project_file))
|
|
165
|
+
entrypoint_file = os.path.basename(project_file)
|
|
166
|
+
|
|
167
|
+
logger.info(
|
|
168
|
+
f"Extracted project dir from {target_type}: {project_dir}",
|
|
169
|
+
)
|
|
170
|
+
logger.info(
|
|
171
|
+
f"Detected {target_type} object name: {object_name}",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return ProjectInfo(
|
|
175
|
+
project_dir=project_dir,
|
|
176
|
+
entrypoint_file=entrypoint_file,
|
|
177
|
+
entrypoint_handler=object_name, # Actual object name (e.g.,
|
|
178
|
+
# "agent_app")
|
|
179
|
+
handler_type=target_type, # Type: "app" or "runner"
|
|
180
|
+
is_directory_entrypoint=False,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ===== Entrypoint Parsing =====
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def parse_entrypoint(spec: str) -> ProjectInfo:
|
|
188
|
+
"""
|
|
189
|
+
Parse entrypoint specification into ProjectInfo.
|
|
190
|
+
|
|
191
|
+
Supported formats:
|
|
192
|
+
- "app.py" - File with default handler name "app"
|
|
193
|
+
- "app.py:my_handler" - File with specific handler name
|
|
194
|
+
- "project_dir/" - Directory (will auto-detect entrypoint)
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
spec: Entrypoint specification string
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
ProjectInfo with parsed information
|
|
201
|
+
|
|
202
|
+
Raises:
|
|
203
|
+
ValueError: If specification format is invalid or file/dir doesn't
|
|
204
|
+
exist
|
|
205
|
+
"""
|
|
206
|
+
spec = spec.strip()
|
|
207
|
+
|
|
208
|
+
# Check if it's a directory entrypoint
|
|
209
|
+
if spec.endswith("/") or os.path.isdir(spec):
|
|
210
|
+
project_dir = os.path.abspath(spec.rstrip("/"))
|
|
211
|
+
if not os.path.exists(project_dir):
|
|
212
|
+
raise ValueError(f"Directory not found: {project_dir}")
|
|
213
|
+
|
|
214
|
+
# Auto-detect entrypoint file in directory
|
|
215
|
+
entrypoint_file = _auto_detect_entrypoint(project_dir)
|
|
216
|
+
|
|
217
|
+
return ProjectInfo(
|
|
218
|
+
project_dir=project_dir,
|
|
219
|
+
entrypoint_file=entrypoint_file,
|
|
220
|
+
entrypoint_handler="app", # Default handler name
|
|
221
|
+
handler_type="app", # Default type
|
|
222
|
+
is_directory_entrypoint=True,
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Parse file-based entrypoint with optional handler
|
|
226
|
+
if ":" in spec:
|
|
227
|
+
file_part, handler = spec.split(":", 1)
|
|
228
|
+
else:
|
|
229
|
+
file_part = spec
|
|
230
|
+
handler = "app" # Default handler name
|
|
231
|
+
|
|
232
|
+
# Resolve file path
|
|
233
|
+
file_path = os.path.abspath(file_part)
|
|
234
|
+
if not os.path.exists(file_path):
|
|
235
|
+
raise ValueError(f"Entrypoint file not found: {file_path}")
|
|
236
|
+
|
|
237
|
+
project_dir = os.path.dirname(file_path)
|
|
238
|
+
entrypoint_file = os.path.basename(file_path)
|
|
239
|
+
|
|
240
|
+
return ProjectInfo(
|
|
241
|
+
project_dir=project_dir,
|
|
242
|
+
entrypoint_file=entrypoint_file,
|
|
243
|
+
entrypoint_handler=handler, # Handler name
|
|
244
|
+
handler_type="app", # Assume app type for entrypoint-style
|
|
245
|
+
is_directory_entrypoint=False,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _auto_detect_entrypoint(project_dir: str) -> str:
|
|
250
|
+
"""
|
|
251
|
+
Auto-detect entrypoint file in a directory.
|
|
252
|
+
|
|
253
|
+
Looks for common entrypoint file names in priority order:
|
|
254
|
+
- app.py
|
|
255
|
+
- main.py
|
|
256
|
+
- __main__.py
|
|
257
|
+
- run.py
|
|
258
|
+
- runner.py
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
project_dir: Directory to search
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Name of detected entrypoint file (relative to project_dir)
|
|
265
|
+
|
|
266
|
+
Raises:
|
|
267
|
+
ValueError: If no entrypoint file is found
|
|
268
|
+
"""
|
|
269
|
+
candidates = [
|
|
270
|
+
"app.py",
|
|
271
|
+
"main.py",
|
|
272
|
+
"__main__.py",
|
|
273
|
+
"run.py",
|
|
274
|
+
"runner.py",
|
|
275
|
+
]
|
|
276
|
+
|
|
277
|
+
for candidate in candidates:
|
|
278
|
+
candidate_path = os.path.join(project_dir, candidate)
|
|
279
|
+
if os.path.exists(candidate_path):
|
|
280
|
+
logger.info(f"Auto-detected entrypoint: {candidate}")
|
|
281
|
+
return candidate
|
|
282
|
+
|
|
283
|
+
raise ValueError(
|
|
284
|
+
f"No entrypoint file found in {project_dir}. "
|
|
285
|
+
f"Expected one of: {', '.join(candidates)}",
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# ===== Main Template Generation =====
|
|
290
|
+
def _generate_app_main_template(entrypoint_info: EntrypointInfo) -> str:
|
|
291
|
+
"""
|
|
292
|
+
Generate main.py template for AgentApp using Jinja2.
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
entrypoint_info: Information about the entrypoint
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
String content for main.py
|
|
299
|
+
|
|
300
|
+
Raises:
|
|
301
|
+
RuntimeError: If template file not found
|
|
302
|
+
"""
|
|
303
|
+
try:
|
|
304
|
+
env = _get_template_env()
|
|
305
|
+
template = env.get_template("app_main.py.j2")
|
|
306
|
+
|
|
307
|
+
# Convert RuntimeParameter objects to dicts for Jinja2
|
|
308
|
+
extra_params_dicts = [
|
|
309
|
+
param.model_dump() for param in entrypoint_info.extra_parameters
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
return template.render(
|
|
313
|
+
module_name=entrypoint_info.module_name,
|
|
314
|
+
object_name=entrypoint_info.object_name,
|
|
315
|
+
host=entrypoint_info.host,
|
|
316
|
+
port=entrypoint_info.port,
|
|
317
|
+
extra_parameters=extra_params_dicts,
|
|
318
|
+
)
|
|
319
|
+
except TemplateNotFound as e:
|
|
320
|
+
raise RuntimeError(
|
|
321
|
+
f"Template 'app_main.py.j2' not found in {TEMPLATES_DIR}",
|
|
322
|
+
) from e
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _generate_runner_main_template(entrypoint_info: EntrypointInfo) -> str:
|
|
326
|
+
"""
|
|
327
|
+
Generate main.py template for Runner using Jinja2.
|
|
328
|
+
|
|
329
|
+
The template wraps the Runner in an AgentApp so it can be deployed as a
|
|
330
|
+
service.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
entrypoint_info: Information about the entrypoint
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
String content for main.py
|
|
337
|
+
|
|
338
|
+
Raises:
|
|
339
|
+
RuntimeError: If template file not found
|
|
340
|
+
"""
|
|
341
|
+
try:
|
|
342
|
+
env = _get_template_env()
|
|
343
|
+
template = env.get_template("runner_main.py.j2")
|
|
344
|
+
|
|
345
|
+
# Use app_name from entrypoint_info or default to object_name
|
|
346
|
+
app_name = (
|
|
347
|
+
entrypoint_info.app_name or f"{entrypoint_info.object_name}_app"
|
|
348
|
+
)
|
|
349
|
+
app_description = (
|
|
350
|
+
entrypoint_info.app_description
|
|
351
|
+
or f"Service for {entrypoint_info.object_name}"
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
# Convert RuntimeParameter objects to dicts for Jinja2
|
|
355
|
+
extra_params_dicts = [
|
|
356
|
+
param.model_dump() for param in entrypoint_info.extra_parameters
|
|
357
|
+
]
|
|
358
|
+
|
|
359
|
+
return template.render(
|
|
360
|
+
module_name=entrypoint_info.module_name,
|
|
361
|
+
object_name=entrypoint_info.object_name,
|
|
362
|
+
app_name=app_name,
|
|
363
|
+
app_description=app_description,
|
|
364
|
+
host=entrypoint_info.host,
|
|
365
|
+
port=entrypoint_info.port,
|
|
366
|
+
extra_parameters=extra_params_dicts,
|
|
367
|
+
)
|
|
368
|
+
except TemplateNotFound as e:
|
|
369
|
+
raise RuntimeError(
|
|
370
|
+
f"Template 'runner_main.py.j2' not found in {TEMPLATES_DIR}",
|
|
371
|
+
) from e
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def generate_main_template(entrypoint_info: EntrypointInfo) -> str:
|
|
375
|
+
"""
|
|
376
|
+
Generate main.py template based on object type using Jinja2 templates.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
entrypoint_info: Information about the entrypoint
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
String content for main.py
|
|
383
|
+
|
|
384
|
+
Raises:
|
|
385
|
+
ValueError: If object_type is not supported
|
|
386
|
+
RuntimeError: If template rendering fails
|
|
387
|
+
"""
|
|
388
|
+
if entrypoint_info.object_type == "app":
|
|
389
|
+
return _generate_app_main_template(entrypoint_info)
|
|
390
|
+
elif entrypoint_info.object_type == "runner":
|
|
391
|
+
return _generate_runner_main_template(entrypoint_info)
|
|
392
|
+
else:
|
|
393
|
+
raise ValueError(
|
|
394
|
+
f"Unsupported object type: {entrypoint_info.object_type}. "
|
|
395
|
+
f"Expected 'app' or 'runner'",
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
# ===== Project Packaging =====
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _get_default_ignore_patterns() -> List[str]:
|
|
403
|
+
"""
|
|
404
|
+
Get default ignore patterns for project packaging.
|
|
405
|
+
|
|
406
|
+
Returns:
|
|
407
|
+
List of ignore patterns (similar to .dockerignore)
|
|
408
|
+
"""
|
|
409
|
+
return [
|
|
410
|
+
"__pycache__",
|
|
411
|
+
"*.pyc",
|
|
412
|
+
"*.pyo",
|
|
413
|
+
".git",
|
|
414
|
+
".gitignore",
|
|
415
|
+
".pytest_cache",
|
|
416
|
+
".mypy_cache",
|
|
417
|
+
".tox",
|
|
418
|
+
"venv",
|
|
419
|
+
"env",
|
|
420
|
+
".venv",
|
|
421
|
+
".env",
|
|
422
|
+
"node_modules",
|
|
423
|
+
".DS_Store",
|
|
424
|
+
"*.egg-info",
|
|
425
|
+
"build",
|
|
426
|
+
"dist",
|
|
427
|
+
".cache",
|
|
428
|
+
"*.swp",
|
|
429
|
+
"*.swo",
|
|
430
|
+
"*~",
|
|
431
|
+
".idea",
|
|
432
|
+
".vscode",
|
|
433
|
+
"*.log",
|
|
434
|
+
"logs",
|
|
435
|
+
]
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _should_ignore(path: str, patterns: List[str]) -> bool:
|
|
439
|
+
"""
|
|
440
|
+
Check if path should be ignored based on patterns.
|
|
441
|
+
|
|
442
|
+
Args:
|
|
443
|
+
path: Path to check (relative)
|
|
444
|
+
patterns: List of ignore patterns
|
|
445
|
+
|
|
446
|
+
Returns:
|
|
447
|
+
True if path should be ignored
|
|
448
|
+
"""
|
|
449
|
+
path_parts = Path(path).parts
|
|
450
|
+
|
|
451
|
+
for pattern in patterns:
|
|
452
|
+
# Check if any part of the path matches the pattern
|
|
453
|
+
if pattern in path_parts:
|
|
454
|
+
return True
|
|
455
|
+
|
|
456
|
+
# Check wildcard patterns
|
|
457
|
+
if "*" in pattern:
|
|
458
|
+
import fnmatch
|
|
459
|
+
|
|
460
|
+
if fnmatch.fnmatch(path, pattern):
|
|
461
|
+
return True
|
|
462
|
+
|
|
463
|
+
return False
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def package_code(
|
|
467
|
+
source_dir: Path,
|
|
468
|
+
output_zip: Path,
|
|
469
|
+
ignore_patterns: Optional[List[str]] = None,
|
|
470
|
+
) -> None:
|
|
471
|
+
"""
|
|
472
|
+
Package project source code into a zip file.
|
|
473
|
+
|
|
474
|
+
Args:
|
|
475
|
+
source_dir: Source directory to package
|
|
476
|
+
output_zip: Output zip file path
|
|
477
|
+
ignore_patterns: Optional ignore patterns (uses defaults if None)
|
|
478
|
+
"""
|
|
479
|
+
if ignore_patterns is None:
|
|
480
|
+
ignore_patterns = _get_default_ignore_patterns()
|
|
481
|
+
|
|
482
|
+
logger.info(f"Packaging source code from {source_dir}")
|
|
483
|
+
|
|
484
|
+
with zipfile.ZipFile(output_zip, "w", zipfile.ZIP_DEFLATED) as zipf:
|
|
485
|
+
for root, dirs, files in os.walk(source_dir):
|
|
486
|
+
# Filter directories
|
|
487
|
+
dirs[:] = [
|
|
488
|
+
d
|
|
489
|
+
for d in dirs
|
|
490
|
+
if not _should_ignore(
|
|
491
|
+
os.path.relpath(os.path.join(root, d), source_dir),
|
|
492
|
+
ignore_patterns,
|
|
493
|
+
)
|
|
494
|
+
]
|
|
495
|
+
|
|
496
|
+
# Add files
|
|
497
|
+
for file in files:
|
|
498
|
+
file_path = os.path.join(root, file)
|
|
499
|
+
arcname = os.path.relpath(file_path, source_dir)
|
|
500
|
+
|
|
501
|
+
if _should_ignore(arcname, ignore_patterns):
|
|
502
|
+
continue
|
|
503
|
+
|
|
504
|
+
zipf.write(file_path, arcname)
|
|
505
|
+
|
|
506
|
+
logger.info(f"Source code packaged: {output_zip}")
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def _merge_zips(
|
|
510
|
+
dependencies_zip: Optional[Path],
|
|
511
|
+
code_zip: Path,
|
|
512
|
+
output_zip: Path,
|
|
513
|
+
) -> None:
|
|
514
|
+
"""
|
|
515
|
+
Merge dependencies and code zips into a deployment package.
|
|
516
|
+
|
|
517
|
+
Args:
|
|
518
|
+
dependencies_zip: Path to dependencies.zip (optional)
|
|
519
|
+
code_zip: Path to code.zip
|
|
520
|
+
output_zip: Path to output deployment.zip
|
|
521
|
+
"""
|
|
522
|
+
logger.info("Merging packages into deployment.zip...")
|
|
523
|
+
|
|
524
|
+
with zipfile.ZipFile(output_zip, "w", zipfile.ZIP_DEFLATED) as out:
|
|
525
|
+
# Layer 1: Dependencies
|
|
526
|
+
if dependencies_zip and dependencies_zip.exists():
|
|
527
|
+
with zipfile.ZipFile(dependencies_zip, "r") as dep:
|
|
528
|
+
for item in dep.namelist():
|
|
529
|
+
out.writestr(item, dep.read(item))
|
|
530
|
+
|
|
531
|
+
# Layer 2: Code (overwrites conflicts)
|
|
532
|
+
with zipfile.ZipFile(code_zip, "r") as code:
|
|
533
|
+
for item in code.namelist():
|
|
534
|
+
out.writestr(item, code.read(item))
|
|
535
|
+
|
|
536
|
+
logger.info(f"Deployment package created: {output_zip}")
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
# ===== Main Package Function =====
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def package(
|
|
543
|
+
app=None,
|
|
544
|
+
runner=None,
|
|
545
|
+
entrypoint: Optional[str] = None,
|
|
546
|
+
output_dir: Optional[str] = None,
|
|
547
|
+
host: str = "0.0.0.0",
|
|
548
|
+
port: int = 8090,
|
|
549
|
+
extra_parameters: Optional[List[RuntimeParameter]] = None,
|
|
550
|
+
**kwargs,
|
|
551
|
+
) -> Tuple[str, ProjectInfo]:
|
|
552
|
+
"""
|
|
553
|
+
Package an AgentApp or Runner for deployment.
|
|
554
|
+
|
|
555
|
+
This function supports two deployment patterns:
|
|
556
|
+
1. Object-style: package(app=my_app) or package(runner=my_runner)
|
|
557
|
+
2. Entrypoint-style: package(entrypoint="app.py") or package(
|
|
558
|
+
entrypoint="project_dir/")
|
|
559
|
+
|
|
560
|
+
For object-style deployment, this function will:
|
|
561
|
+
1. Extract the project directory containing the app/runner
|
|
562
|
+
2. Generate a new main.py that imports and runs the app/runner
|
|
563
|
+
3. Package the project with the generated main.py as entrypoint
|
|
564
|
+
|
|
565
|
+
Args:
|
|
566
|
+
app: AgentApp instance (for object-style deployment)
|
|
567
|
+
runner: Runner instance (for object-style deployment)
|
|
568
|
+
entrypoint: Entrypoint specification (for CLI-style deployment)
|
|
569
|
+
output_dir: Output directory (creates temp dir if None)
|
|
570
|
+
host: Default host for the service (default: "0.0.0.0")
|
|
571
|
+
port: Default port for the service (default: 8090)
|
|
572
|
+
extra_parameters: Additional runtime parameters to expose via CLI
|
|
573
|
+
**kwargs: Additional keyword arguments (ignored)
|
|
574
|
+
|
|
575
|
+
Returns:
|
|
576
|
+
Tuple of (package_path, project_info)
|
|
577
|
+
- package_path: Path to the deployment package directory
|
|
578
|
+
- project_info: ProjectInfo with project metadata
|
|
579
|
+
|
|
580
|
+
Raises:
|
|
581
|
+
ValueError: If neither app/runner nor entrypoint is provided
|
|
582
|
+
RuntimeError: If packaging fails
|
|
583
|
+
|
|
584
|
+
Example:
|
|
585
|
+
>>> # Package with extra parameters
|
|
586
|
+
>>> extra_params = [
|
|
587
|
+
... RuntimeParameter(
|
|
588
|
+
... name="log_level",
|
|
589
|
+
... type="str",
|
|
590
|
+
... default="info",
|
|
591
|
+
... help="Logging level"
|
|
592
|
+
... ),
|
|
593
|
+
... RuntimeParameter(
|
|
594
|
+
... name="workers",
|
|
595
|
+
... type="int",
|
|
596
|
+
... default=4,
|
|
597
|
+
... help="Number of worker threads"
|
|
598
|
+
... ),
|
|
599
|
+
... ]
|
|
600
|
+
>>> package(app=my_app, extra_parameters=extra_params)
|
|
601
|
+
"""
|
|
602
|
+
# Determine project info and target object
|
|
603
|
+
target_obj = None
|
|
604
|
+
if entrypoint:
|
|
605
|
+
project_info = parse_entrypoint(entrypoint)
|
|
606
|
+
elif app or runner:
|
|
607
|
+
project_info = project_dir_extractor(app=app, runner=runner)
|
|
608
|
+
target_obj = app if app is not None else runner
|
|
609
|
+
else:
|
|
610
|
+
raise ValueError(
|
|
611
|
+
"Either app/runner or entrypoint must be provided",
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
logger.info(f"Packaging project from: {project_info.project_dir}")
|
|
615
|
+
|
|
616
|
+
# Create output directory
|
|
617
|
+
if output_dir is None:
|
|
618
|
+
output_dir = tempfile.mkdtemp(prefix="agentscope_package_")
|
|
619
|
+
else:
|
|
620
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
621
|
+
|
|
622
|
+
output_path = Path(output_dir)
|
|
623
|
+
module_name = project_info.entrypoint_file.split(".", maxsplit=1)[0]
|
|
624
|
+
# For object-style deployment, generate main.py template
|
|
625
|
+
generated_main = False
|
|
626
|
+
if target_obj is not None:
|
|
627
|
+
entrypoint_info = EntrypointInfo(
|
|
628
|
+
module_name=module_name,
|
|
629
|
+
object_type=project_info.handler_type,
|
|
630
|
+
object_name=project_info.entrypoint_handler,
|
|
631
|
+
host=host,
|
|
632
|
+
port=port,
|
|
633
|
+
extra_parameters=extra_parameters or [],
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
# Generate main.py content
|
|
637
|
+
main_content = generate_main_template(entrypoint_info)
|
|
638
|
+
|
|
639
|
+
# Create temporary directory for modified source
|
|
640
|
+
temp_source_dir = output_path / "temp_source"
|
|
641
|
+
temp_source_dir.mkdir(exist_ok=True)
|
|
642
|
+
|
|
643
|
+
# Copy original project to temp directory
|
|
644
|
+
shutil.copytree(
|
|
645
|
+
project_info.project_dir,
|
|
646
|
+
temp_source_dir,
|
|
647
|
+
dirs_exist_ok=True,
|
|
648
|
+
ignore=shutil.ignore_patterns(*_get_default_ignore_patterns()),
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
# Write generated main.py to temp directory
|
|
652
|
+
main_py_path = temp_source_dir / DEFAULT_ENTRYPOINT_FILE
|
|
653
|
+
with open(main_py_path, "w", encoding="utf-8") as f:
|
|
654
|
+
f.write(main_content)
|
|
655
|
+
|
|
656
|
+
# Update project_info to use generated main.py
|
|
657
|
+
project_info.entrypoint_file = DEFAULT_ENTRYPOINT_FILE
|
|
658
|
+
project_info.entrypoint_handler = entrypoint_info.object_name
|
|
659
|
+
# Use object name
|
|
660
|
+
project_info.handler_type = entrypoint_info.object_type # Use type
|
|
661
|
+
project_info.project_dir = str(temp_source_dir)
|
|
662
|
+
|
|
663
|
+
generated_main = True
|
|
664
|
+
logger.info(
|
|
665
|
+
f"Generated main.py template for {entrypoint_info.object_type}: "
|
|
666
|
+
f"{entrypoint_info.object_name}",
|
|
667
|
+
)
|
|
668
|
+
logger.info(
|
|
669
|
+
f"Service will start on {host}:{port} by default",
|
|
670
|
+
)
|
|
671
|
+
if extra_parameters:
|
|
672
|
+
logger.info(
|
|
673
|
+
f"Added {len(extra_parameters)} extra runtime parameters",
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
# Package code
|
|
677
|
+
deployment_zip = output_path / DEPLOYMENT_ZIP
|
|
678
|
+
package_code(
|
|
679
|
+
Path(project_info.project_dir),
|
|
680
|
+
deployment_zip,
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
# Clean up temporary directory if created
|
|
684
|
+
if generated_main:
|
|
685
|
+
temp_source_dir = Path(project_info.project_dir)
|
|
686
|
+
if temp_source_dir.exists() and temp_source_dir.parent == output_path:
|
|
687
|
+
shutil.rmtree(temp_source_dir)
|
|
688
|
+
|
|
689
|
+
# Report size
|
|
690
|
+
size_mb = deployment_zip.stat().st_size / (1024 * 1024)
|
|
691
|
+
logger.info(f"Deployment package ready: {size_mb:.2f} MB")
|
|
692
|
+
|
|
693
|
+
return str(output_path), project_info
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
from .fastapi_factory import FastAPIAppFactory
|
|
3
|
-
from .service_config import (
|
|
4
|
-
ServicesConfig,
|
|
5
|
-
ServiceConfig,
|
|
6
|
-
ServiceProvider,
|
|
7
|
-
)
|
|
8
3
|
from .fastapi_templates import FastAPITemplateManager
|
|
9
4
|
from .process_manager import ProcessManager
|