agentscope-runtime 1.0.1__py3-none-any.whl → 1.0.2__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 (58) hide show
  1. agentscope_runtime/adapters/agentscope/message.py +32 -7
  2. agentscope_runtime/adapters/agentscope/stream.py +121 -91
  3. agentscope_runtime/adapters/agno/__init__.py +0 -0
  4. agentscope_runtime/adapters/agno/message.py +30 -0
  5. agentscope_runtime/adapters/agno/stream.py +122 -0
  6. agentscope_runtime/adapters/langgraph/__init__.py +12 -0
  7. agentscope_runtime/adapters/langgraph/message.py +257 -0
  8. agentscope_runtime/adapters/langgraph/stream.py +205 -0
  9. agentscope_runtime/cli/__init__.py +7 -0
  10. agentscope_runtime/cli/cli.py +63 -0
  11. agentscope_runtime/cli/commands/__init__.py +2 -0
  12. agentscope_runtime/cli/commands/chat.py +815 -0
  13. agentscope_runtime/cli/commands/deploy.py +1062 -0
  14. agentscope_runtime/cli/commands/invoke.py +58 -0
  15. agentscope_runtime/cli/commands/list_cmd.py +103 -0
  16. agentscope_runtime/cli/commands/run.py +176 -0
  17. agentscope_runtime/cli/commands/sandbox.py +128 -0
  18. agentscope_runtime/cli/commands/status.py +60 -0
  19. agentscope_runtime/cli/commands/stop.py +185 -0
  20. agentscope_runtime/cli/commands/web.py +166 -0
  21. agentscope_runtime/cli/loaders/__init__.py +6 -0
  22. agentscope_runtime/cli/loaders/agent_loader.py +295 -0
  23. agentscope_runtime/cli/state/__init__.py +10 -0
  24. agentscope_runtime/cli/utils/__init__.py +18 -0
  25. agentscope_runtime/cli/utils/console.py +378 -0
  26. agentscope_runtime/cli/utils/validators.py +118 -0
  27. agentscope_runtime/engine/app/agent_app.py +7 -4
  28. agentscope_runtime/engine/deployers/__init__.py +1 -0
  29. agentscope_runtime/engine/deployers/agentrun_deployer.py +152 -22
  30. agentscope_runtime/engine/deployers/base.py +27 -2
  31. agentscope_runtime/engine/deployers/kubernetes_deployer.py +158 -31
  32. agentscope_runtime/engine/deployers/local_deployer.py +188 -25
  33. agentscope_runtime/engine/deployers/modelstudio_deployer.py +109 -18
  34. agentscope_runtime/engine/deployers/state/__init__.py +9 -0
  35. agentscope_runtime/engine/deployers/state/manager.py +388 -0
  36. agentscope_runtime/engine/deployers/state/schema.py +96 -0
  37. agentscope_runtime/engine/deployers/utils/build_cache.py +736 -0
  38. agentscope_runtime/engine/deployers/utils/detached_app.py +105 -30
  39. agentscope_runtime/engine/deployers/utils/docker_image_utils/docker_image_builder.py +31 -10
  40. agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +15 -8
  41. agentscope_runtime/engine/deployers/utils/docker_image_utils/image_factory.py +30 -2
  42. agentscope_runtime/engine/deployers/utils/k8s_utils.py +241 -0
  43. agentscope_runtime/engine/deployers/utils/package.py +56 -6
  44. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +16 -2
  45. agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +155 -5
  46. agentscope_runtime/engine/deployers/utils/wheel_packager.py +107 -123
  47. agentscope_runtime/engine/runner.py +25 -6
  48. agentscope_runtime/engine/schemas/exception.py +580 -0
  49. agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +113 -39
  50. agentscope_runtime/sandbox/box/shared/routers/mcp_utils.py +20 -4
  51. agentscope_runtime/sandbox/utils.py +2 -0
  52. agentscope_runtime/version.py +1 -1
  53. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/METADATA +24 -7
  54. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/RECORD +58 -28
  55. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/entry_points.txt +1 -0
  56. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/WHEEL +0 -0
  57. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/licenses/LICENSE +0 -0
  58. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,166 @@
1
+ # -*- coding: utf-8 -*-
2
+ """agentscope web command - Launch agent with web UI."""
3
+ # pylint: disable=no-value-for-parameter, unused-argument, unnecessary-pass
4
+
5
+ import atexit
6
+ import logging
7
+ import os
8
+ import signal
9
+ import sys
10
+
11
+ import click
12
+ import psutil
13
+
14
+ from agentscope_runtime.cli.loaders.agent_loader import (
15
+ UnifiedAgentLoader,
16
+ AgentLoadError,
17
+ )
18
+ from agentscope_runtime.cli.utils.console import (
19
+ echo_error,
20
+ echo_info,
21
+ echo_success,
22
+ echo_warning,
23
+ )
24
+ from agentscope_runtime.cli.utils.validators import validate_port
25
+ from agentscope_runtime.engine.deployers.state import DeploymentStateManager
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ # Global variable to track child processes and parent process
30
+ _child_processes = []
31
+ _parent_process = None
32
+
33
+
34
+ def _cleanup_processes():
35
+ """Clean up any child processes when exiting."""
36
+ global _child_processes, _parent_process
37
+
38
+ # Get all current children if we have a parent process reference
39
+ if _parent_process:
40
+ try:
41
+ all_children = _parent_process.children(recursive=True)
42
+ _child_processes = list(set(_child_processes + all_children))
43
+ except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
44
+ logger.error(f"clean up child processes failed with error: {e}")
45
+
46
+ for process in _child_processes:
47
+ try:
48
+ if process.is_running():
49
+ echo_info(f"Terminating child process {process.pid}...")
50
+ process.terminate()
51
+ try:
52
+ process.wait(timeout=5)
53
+ except psutil.TimeoutExpired:
54
+ echo_warning(f"Force killing process {process.pid}...")
55
+ process.kill()
56
+ except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
57
+ logger.error(f"process runtime error: {e}")
58
+
59
+
60
+ def _signal_handler(signum, frame):
61
+ """Handle signals - just set a flag, don't exit immediately."""
62
+ # The KeyboardInterrupt will be raised naturally
63
+ pass
64
+
65
+
66
+ # Register cleanup function to run on exit
67
+ atexit.register(_cleanup_processes)
68
+ # Register signal handler that doesn't exit immediately
69
+ signal.signal(signal.SIGINT, _signal_handler)
70
+ signal.signal(signal.SIGTERM, _signal_handler)
71
+
72
+
73
+ @click.command()
74
+ @click.argument("source", required=True)
75
+ @click.option(
76
+ "--host",
77
+ "-h",
78
+ help="Host address to bind to",
79
+ default="0.0.0.0",
80
+ )
81
+ @click.option(
82
+ "--port",
83
+ "-p",
84
+ help="Port number to serve on",
85
+ default=8080,
86
+ type=int,
87
+ )
88
+ @click.option(
89
+ "--entrypoint",
90
+ "-e",
91
+ help="Entrypoint file name for directory sources (e.g., 'app.py', "
92
+ "'main.py')",
93
+ default=None,
94
+ )
95
+ def web(source: str, host: str, port: int, entrypoint: str = None):
96
+ """
97
+ Launch agent with web UI in single process.
98
+
99
+ SOURCE can be:
100
+ \b
101
+ - Path to Python file (e.g., agent.py)
102
+ - Path to project directory (e.g., ./my-agent)
103
+ - Deployment ID (e.g., local_20250101_120000_abc123)
104
+
105
+ Examples:
106
+ \b
107
+ # Launch with default settings
108
+ $ agentscope web agent.py
109
+
110
+ # Custom host and port
111
+ $ agentscope web agent.py --host 0.0.0.0 --port 8000
112
+
113
+ # Use deployment
114
+ $ agentscope web local_20250101_120000_abc123
115
+
116
+ # Use custom entrypoint for directory source
117
+ $ agentscope web ./my-project --entrypoint custom_app.py
118
+ """
119
+ global _child_processes, _parent_process
120
+
121
+ try:
122
+ # Validate port
123
+ port = validate_port(port)
124
+
125
+ # Initialize state manager
126
+ state_manager = DeploymentStateManager()
127
+
128
+ # Load agent
129
+ echo_info(f"Loading agent from: {source}")
130
+ loader = UnifiedAgentLoader(state_manager=state_manager)
131
+
132
+ try:
133
+ agent_app = loader.load(source, entrypoint=entrypoint)
134
+ echo_success("Agent loaded successfully")
135
+ except AgentLoadError as e:
136
+ echo_error(f"Failed to load agent: {e}")
137
+ sys.exit(1)
138
+
139
+ # Launch with web UI
140
+ echo_info(f"Starting agent service on {host}:{port} with web UI...")
141
+ echo_info(
142
+ "Note: First launch may take longer as web UI dependencies are "
143
+ "installed",
144
+ )
145
+
146
+ # Track parent process for cleanup
147
+ _parent_process = psutil.Process(os.getpid())
148
+
149
+ try:
150
+ agent_app.run(host=host, port=port, web_ui=True)
151
+ except KeyboardInterrupt:
152
+ echo_info("\nShutting down...")
153
+ # Explicitly cleanup children before exit
154
+ _cleanup_processes()
155
+
156
+ except Exception as e:
157
+ echo_error(f"Unexpected error: {e}")
158
+ import traceback
159
+
160
+ traceback.print_exc()
161
+ _cleanup_processes()
162
+ sys.exit(1)
163
+
164
+
165
+ if __name__ == "__main__":
166
+ web()
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Agent loading utilities."""
3
+
4
+ from agentscope_runtime.cli.loaders.agent_loader import AgentLoader
5
+
6
+ __all__ = ["AgentLoader"]
@@ -0,0 +1,295 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Base agent loader class."""
3
+ # pylint: disable=too-many-branches
4
+
5
+ import importlib.util
6
+ import os
7
+ import sys
8
+ from abc import ABC, abstractmethod
9
+ from pathlib import Path
10
+ from typing import Any, Optional
11
+
12
+ from agentscope_runtime.cli.utils.validators import validate_agent_source
13
+ from agentscope_runtime.engine.app.agent_app import AgentApp
14
+
15
+
16
+ class AgentLoadError(Exception):
17
+ """Raised when agent loading fails."""
18
+
19
+
20
+ class AgentLoader(ABC):
21
+ """Base class for agent loaders."""
22
+
23
+ @abstractmethod
24
+ def load(self, source: str) -> AgentApp:
25
+ """Load AgentApp from source."""
26
+
27
+
28
+ class FileLoader:
29
+ """Load AgentApp from Python file."""
30
+
31
+ def load(self, file_path: str) -> AgentApp:
32
+ """
33
+ Load AgentApp from Python file.
34
+
35
+ Args:
36
+ file_path: Path to Python file
37
+
38
+ Returns:
39
+ AgentApp instance
40
+
41
+ Raises:
42
+ AgentLoadError: If loading fails
43
+ """
44
+ if not os.path.isfile(file_path):
45
+ raise AgentLoadError(f"File not found: {file_path}")
46
+
47
+ if not file_path.endswith(".py"):
48
+ raise AgentLoadError(f"File must be a Python file: {file_path}")
49
+
50
+ abs_path = os.path.abspath(file_path)
51
+
52
+ try:
53
+ # Load module from file
54
+ spec = importlib.util.spec_from_file_location(
55
+ "agent_module",
56
+ abs_path,
57
+ )
58
+ if spec is None or spec.loader is None:
59
+ raise AgentLoadError(f"Cannot load module from {abs_path}")
60
+
61
+ module = importlib.util.module_from_spec(spec)
62
+
63
+ # Add parent directory to sys.path temporarily
64
+ parent_dir = str(Path(abs_path).parent)
65
+ if parent_dir not in sys.path:
66
+ sys.path.insert(0, parent_dir)
67
+ remove_from_path = True
68
+ else:
69
+ remove_from_path = False
70
+
71
+ try:
72
+ spec.loader.exec_module(module)
73
+ finally:
74
+ if remove_from_path:
75
+ sys.path.remove(parent_dir)
76
+
77
+ # Look for AgentApp instance
78
+ agent_app = self._find_agent_app(module, file_path)
79
+ return agent_app
80
+
81
+ except Exception as e:
82
+ raise AgentLoadError(
83
+ f"Failed to load agent from {abs_path}: {e}",
84
+ ) from e
85
+
86
+ def _find_agent_app(self, module: Any, file_path: str) -> AgentApp:
87
+ """Find AgentApp instance in module."""
88
+ candidates = []
89
+
90
+ # Look for exported variables
91
+ for attr_name in ["agent_app", "app"]:
92
+ if hasattr(module, attr_name):
93
+ attr_value = getattr(module, attr_name)
94
+ if isinstance(attr_value, AgentApp):
95
+ candidates.append((attr_name, attr_value))
96
+
97
+ # Look for factory functions
98
+ for attr_name in [
99
+ "create_app",
100
+ "create_agent_app",
101
+ "get_app",
102
+ "get_agent_app",
103
+ ]:
104
+ if hasattr(module, attr_name):
105
+ attr_value = getattr(module, attr_name)
106
+ if callable(attr_value):
107
+ try:
108
+ # Try calling with no arguments
109
+ result = attr_value()
110
+ if isinstance(result, AgentApp):
111
+ candidates.append((f"{attr_name}()", result))
112
+ except Exception:
113
+ # Factory function requires arguments, skip
114
+ pass
115
+
116
+ # Look for any AgentApp instances as last resort
117
+ for attr_name in dir(module):
118
+ if not attr_name.startswith("_"): # Skip private attributes
119
+ attr_value = getattr(module, attr_name)
120
+ if isinstance(attr_value, AgentApp):
121
+ if not any(
122
+ attr_name == candidate[0] for candidate in candidates
123
+ ):
124
+ candidates.append((attr_name, attr_value))
125
+
126
+ if not candidates:
127
+ raise AgentLoadError(
128
+ f"No AgentApp found in {file_path}.\n"
129
+ "Expected: 'agent_app' or 'app' variable, "
130
+ "or 'create_app'/'create_agent_app' function",
131
+ )
132
+
133
+ if len(candidates) > 1:
134
+ candidate_names = [name for name, _ in candidates]
135
+ raise AgentLoadError(
136
+ f"Multiple AgentApp instances found in {file_path}: "
137
+ f"{candidate_names}\n"
138
+ "Please ensure only one AgentApp is exported",
139
+ )
140
+
141
+ name, agent_app = candidates[0]
142
+ return agent_app
143
+
144
+
145
+ class ProjectLoader:
146
+ """Load AgentApp from project directory."""
147
+
148
+ def load(
149
+ self,
150
+ project_dir: str,
151
+ entrypoint: Optional[str] = None,
152
+ ) -> AgentApp:
153
+ """
154
+ Load AgentApp from project directory.
155
+
156
+ Args:
157
+ project_dir: Path to project directory
158
+ entrypoint: Optional entrypoint file name (e.g., "app.py",
159
+ "main.py")
160
+
161
+ Returns:
162
+ AgentApp instance
163
+
164
+ Raises:
165
+ AgentLoadError: If loading fails
166
+ """
167
+ if not os.path.isdir(project_dir):
168
+ raise AgentLoadError(f"Directory not found: {project_dir}")
169
+
170
+ # If entrypoint is specified, use it directly
171
+ if entrypoint:
172
+ file_path = os.path.join(project_dir, entrypoint)
173
+ if not os.path.isfile(file_path):
174
+ raise AgentLoadError(
175
+ f"Entrypoint file not found: {file_path}",
176
+ )
177
+ file_loader = FileLoader()
178
+ return file_loader.load(file_path)
179
+
180
+ # Look for entry point files in order of preference
181
+ entry_files = ["app.py", "agent.py", "main.py"]
182
+
183
+ for entry_file in entry_files:
184
+ file_path = os.path.join(project_dir, entry_file)
185
+ if os.path.isfile(file_path):
186
+ file_loader = FileLoader()
187
+ return file_loader.load(file_path)
188
+
189
+ raise AgentLoadError(
190
+ f"No entry point found in {project_dir}.\n"
191
+ f"Expected one of: {entry_files}",
192
+ )
193
+
194
+
195
+ class DeploymentLoader:
196
+ """Load AgentApp from deployment metadata."""
197
+
198
+ def __init__(self, state_manager):
199
+ """Initialize with state manager."""
200
+ self.state_manager = state_manager
201
+
202
+ def load(self, deploy_id: str) -> AgentApp:
203
+ """
204
+ Load AgentApp from deployment ID.
205
+
206
+ Args:
207
+ deploy_id: Deployment ID
208
+
209
+ Returns:
210
+ AgentApp instance
211
+
212
+ Raises:
213
+ AgentLoadError: If loading fails
214
+ """
215
+ # Get deployment metadata
216
+ deployment = self.state_manager.get(deploy_id)
217
+ if deployment is None:
218
+ raise AgentLoadError(f"Deployment not found: {deploy_id}")
219
+
220
+ # Load agent from original source
221
+ source = deployment.agent_source
222
+
223
+ # Determine source type and delegate to appropriate loader
224
+ source_type, normalized_source = validate_agent_source(source)
225
+
226
+ if source_type == "file":
227
+ loader = FileLoader()
228
+ elif source_type == "directory":
229
+ loader = ProjectLoader()
230
+ else:
231
+ raise AgentLoadError(
232
+ f"Cannot load from deployment with source type: {source_type}",
233
+ )
234
+
235
+ return loader.load(normalized_source)
236
+
237
+
238
+ class UnifiedAgentLoader:
239
+ """Unified loader that handles all source types."""
240
+
241
+ def __init__(self, state_manager=None):
242
+ """Initialize with optional state manager."""
243
+ self.state_manager = state_manager
244
+ self.file_loader = FileLoader()
245
+ self.project_loader = ProjectLoader()
246
+ if state_manager:
247
+ self.deployment_loader = DeploymentLoader(state_manager)
248
+ else:
249
+ self.deployment_loader = None
250
+
251
+ def load(self, source: str, entrypoint: Optional[str] = None) -> AgentApp:
252
+ """
253
+ Load AgentApp from any source type.
254
+
255
+ Args:
256
+ source: Path to file/directory or deployment ID
257
+ entrypoint: Optional entrypoint file name for directory sources
258
+
259
+ Returns:
260
+ AgentApp instance
261
+
262
+ Raises:
263
+ AgentLoadError: If loading fails
264
+ """
265
+ try:
266
+ source_type, normalized_source = validate_agent_source(source)
267
+
268
+ if source_type == "deployment_id":
269
+ if self.deployment_loader is None:
270
+ raise AgentLoadError(
271
+ "Cannot load from deployment ID: state manager not "
272
+ "available",
273
+ )
274
+ return self.deployment_loader.load(normalized_source)
275
+ elif source_type == "file":
276
+ if entrypoint:
277
+ raise AgentLoadError(
278
+ "The --entrypoint option is only applicable for "
279
+ "directory sources, not file sources",
280
+ )
281
+ return self.file_loader.load(normalized_source)
282
+ elif source_type == "directory":
283
+ return self.project_loader.load(
284
+ normalized_source,
285
+ entrypoint=entrypoint,
286
+ )
287
+ else:
288
+ raise AgentLoadError(f"Unknown source type: {source_type}")
289
+
290
+ except Exception as e:
291
+ if isinstance(e, AgentLoadError):
292
+ raise
293
+ raise AgentLoadError(
294
+ f"Failed to load agent from {source}: {e}",
295
+ ) from e
@@ -0,0 +1,10 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Deployment state management - re-exports from engine.deployers.state."""
3
+
4
+ # Re-export from new location for backward compatibility
5
+ from agentscope_runtime.engine.deployers.state.manager import (
6
+ DeploymentStateManager,
7
+ )
8
+ from agentscope_runtime.engine.deployers.state.schema import Deployment
9
+
10
+ __all__ = ["DeploymentStateManager", "Deployment"]
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+ """CLI utility functions."""
3
+
4
+ from agentscope_runtime.cli.utils.console import (
5
+ echo_success,
6
+ echo_error,
7
+ echo_info,
8
+ echo_warning,
9
+ )
10
+ from agentscope_runtime.cli.utils.validators import validate_agent_source
11
+
12
+ __all__ = [
13
+ "echo_success",
14
+ "echo_error",
15
+ "echo_info",
16
+ "echo_warning",
17
+ "validate_agent_source",
18
+ ]