agentscope-runtime 1.0.1__py3-none-any.whl → 1.0.3__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 (69) 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 +1074 -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/common/collections/redis_mapping.py +4 -1
  28. agentscope_runtime/engine/app/agent_app.py +55 -9
  29. agentscope_runtime/engine/deployers/__init__.py +1 -0
  30. agentscope_runtime/engine/deployers/adapter/a2a/__init__.py +56 -1
  31. agentscope_runtime/engine/deployers/adapter/a2a/a2a_protocol_adapter.py +449 -41
  32. agentscope_runtime/engine/deployers/adapter/a2a/a2a_registry.py +273 -0
  33. agentscope_runtime/engine/deployers/adapter/a2a/nacos_a2a_registry.py +640 -0
  34. agentscope_runtime/engine/deployers/agentrun_deployer.py +152 -22
  35. agentscope_runtime/engine/deployers/base.py +27 -2
  36. agentscope_runtime/engine/deployers/kubernetes_deployer.py +161 -31
  37. agentscope_runtime/engine/deployers/local_deployer.py +188 -25
  38. agentscope_runtime/engine/deployers/modelstudio_deployer.py +109 -18
  39. agentscope_runtime/engine/deployers/state/__init__.py +9 -0
  40. agentscope_runtime/engine/deployers/state/manager.py +388 -0
  41. agentscope_runtime/engine/deployers/state/schema.py +96 -0
  42. agentscope_runtime/engine/deployers/utils/build_cache.py +736 -0
  43. agentscope_runtime/engine/deployers/utils/detached_app.py +105 -30
  44. agentscope_runtime/engine/deployers/utils/docker_image_utils/docker_image_builder.py +31 -10
  45. agentscope_runtime/engine/deployers/utils/docker_image_utils/dockerfile_generator.py +23 -10
  46. agentscope_runtime/engine/deployers/utils/docker_image_utils/image_factory.py +35 -2
  47. agentscope_runtime/engine/deployers/utils/k8s_utils.py +241 -0
  48. agentscope_runtime/engine/deployers/utils/net_utils.py +65 -0
  49. agentscope_runtime/engine/deployers/utils/package.py +56 -6
  50. agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +16 -2
  51. agentscope_runtime/engine/deployers/utils/service_utils/process_manager.py +155 -5
  52. agentscope_runtime/engine/deployers/utils/wheel_packager.py +107 -123
  53. agentscope_runtime/engine/runner.py +30 -9
  54. agentscope_runtime/engine/schemas/exception.py +604 -0
  55. agentscope_runtime/engine/services/agent_state/redis_state_service.py +61 -8
  56. agentscope_runtime/engine/services/agent_state/state_service_factory.py +2 -5
  57. agentscope_runtime/engine/services/memory/redis_memory_service.py +129 -25
  58. agentscope_runtime/engine/services/session_history/redis_session_history_service.py +160 -34
  59. agentscope_runtime/sandbox/box/mobile/mobile_sandbox.py +113 -39
  60. agentscope_runtime/sandbox/box/shared/routers/mcp_utils.py +20 -4
  61. agentscope_runtime/sandbox/build.py +50 -57
  62. agentscope_runtime/sandbox/utils.py +2 -0
  63. agentscope_runtime/version.py +1 -1
  64. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/METADATA +31 -8
  65. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/RECORD +69 -36
  66. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/entry_points.txt +1 -0
  67. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/WHEEL +0 -0
  68. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/licenses/LICENSE +0 -0
  69. {agentscope_runtime-1.0.1.dist-info → agentscope_runtime-1.0.3.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,13 @@
1
1
  # -*- coding: utf-8 -*-
2
- # pylint:disable=too-many-return-statements
2
+ # pylint:disable=too-many-return-statements, too-many-branches
3
3
 
4
4
  """Shared helpers for building detached deployment bundles."""
5
5
 
6
6
  from __future__ import annotations
7
- import os
8
7
 
9
8
  import json
9
+ import os
10
+ import re
10
11
  import shutil
11
12
  import tempfile
12
13
  import zipfile
@@ -14,10 +15,14 @@ from pathlib import Path
14
15
  from typing import Any, Dict, List, Optional, Tuple, Type, Union
15
16
 
16
17
  from .app_runner_utils import ensure_runner_from_app
17
- from .package import package, ProjectInfo, DEFAULT_ENTRYPOINT_FILE
18
+ from .package import (
19
+ package,
20
+ ProjectInfo,
21
+ DEFAULT_ENTRYPOINT_FILE,
22
+ DEPLOYMENT_ZIP,
23
+ generate_build_directory,
24
+ )
18
25
  from ..adapter.protocol_adapter import ProtocolAdapter
19
- from .package import DEPLOYMENT_ZIP
20
- from .wheel_packager import _parse_pyproject_toml
21
26
 
22
27
  try:
23
28
  import tomllib # Python 3.11+
@@ -36,19 +41,25 @@ def build_detached_app(
36
41
  *,
37
42
  app=None,
38
43
  runner=None,
44
+ entrypoint: Optional[str] = None,
39
45
  requirements: Optional[Union[str, List[str]]] = None,
40
46
  extra_packages: Optional[List[str]] = None,
41
47
  output_dir: Optional[str] = None,
42
48
  dockerfile_path: Optional[str] = None,
43
49
  use_local_runtime: Optional[bool] = None,
50
+ platform: str = "unknown",
44
51
  **kwargs,
45
52
  ) -> Tuple[str, ProjectInfo]:
46
53
  """
47
54
  Create a detached bundle directory ready for execution.
48
55
 
56
+ All temporary files are created in cwd/.agentscope_runtime/ by default.
57
+
49
58
  Args:
50
59
  app: AgentApp instance to deploy
51
60
  runner: Runner instance to deploy
61
+ entrypoint: Entrypoint specification (e.g., "app.py" or
62
+ "app.py:handler")
52
63
  requirements: Additional pip requirements (string or list)
53
64
  extra_packages: Additional Python packages to include
54
65
  output_dir: Output directory (creates temp dir if None)
@@ -64,8 +75,8 @@ def build_detached_app(
64
75
  if app is not None and runner is None:
65
76
  runner = ensure_runner_from_app(app)
66
77
 
67
- if runner is None and app is None:
68
- raise ValueError("Either app or runner must be provided")
78
+ if runner is None and app is None and entrypoint is None:
79
+ raise ValueError("Either app or runner or entrypoint must be provided")
69
80
 
70
81
  normalized_requirements = _normalize_requirements(requirements)
71
82
 
@@ -75,17 +86,18 @@ def build_detached_app(
75
86
  shutil.rmtree(build_root)
76
87
  build_root.mkdir(parents=True, exist_ok=True)
77
88
  else:
78
- build_root = Path(
79
- tempfile.mkdtemp(
80
- prefix="agentscope_runtime_detached_",
81
- ),
82
- )
89
+ # Use generate_build_directory for consistent naming
90
+ build_root = generate_build_directory(platform)
91
+ build_root.mkdir(parents=True, exist_ok=True)
83
92
 
84
93
  package_path, project_info = package(
85
94
  app=app,
86
95
  runner=None if app is not None else runner,
96
+ entrypoint=entrypoint,
87
97
  output_dir=str(build_root),
88
98
  extra_packages=extra_packages,
99
+ requirements=normalized_requirements,
100
+ platform=platform,
89
101
  **kwargs,
90
102
  )
91
103
 
@@ -102,12 +114,7 @@ def build_detached_app(
102
114
  with zipfile.ZipFile(deployment_zip, "r") as archive:
103
115
  archive.extractall(project_root)
104
116
 
105
- # Auto-detect if not specified
106
- if use_local_runtime is None:
107
- package_version = _get_package_version()
108
- use_local_runtime = _is_dev_version(package_version)
109
-
110
- _append_additional_requirements(
117
+ append_project_requirements(
111
118
  project_root,
112
119
  normalized_requirements,
113
120
  use_local_runtime=use_local_runtime,
@@ -146,10 +153,10 @@ def _normalize_requirements(
146
153
  return [str(item) for item in requirements]
147
154
 
148
155
 
149
- def _append_additional_requirements(
156
+ def append_project_requirements(
150
157
  extraction_dir: Path,
151
- additional_requirements: List[str],
152
- use_local_runtime: bool = False,
158
+ additional_requirements: Optional[Union[str, list]],
159
+ use_local_runtime: Optional[bool] = False,
153
160
  ) -> None:
154
161
  """
155
162
  Append requirements to requirements.txt.
@@ -163,16 +170,15 @@ def _append_additional_requirements(
163
170
  use_local_runtime: If True, build and use local runtime wheel.
164
171
  Useful for development when runtime is not released.
165
172
  """
173
+ # Auto-detect if not specified
174
+ if use_local_runtime is None:
175
+ use_local_runtime = os.getenv("USE_LOCAL_RUNTIME", "False") == "True"
176
+
166
177
  req_path = extraction_dir / "requirements.txt"
167
178
  package_version = _get_package_version()
168
179
 
169
- # Auto-detect if we should use local runtime
170
- should_use_local = use_local_runtime or (
171
- package_version and _is_dev_version(package_version)
172
- )
173
-
174
180
  with open(str(req_path), "w", encoding="utf-8") as f:
175
- if should_use_local:
181
+ if use_local_runtime:
176
182
  # Create wheels subdirectory
177
183
  # Get base requirements from pyproject.toml
178
184
  runtime_source = _get_runtime_source_path()
@@ -203,8 +209,6 @@ def _append_additional_requirements(
203
209
  "fastapi",
204
210
  "uvicorn",
205
211
  f"agentscope-runtime=={package_version}",
206
- f"agentscope-runtime[sandbox]=={package_version}",
207
- f"agentscope-runtime[deployment]=={package_version}",
208
212
  "pydantic",
209
213
  "jinja2", # For template rendering
210
214
  "psutil", # For process management
@@ -218,6 +222,9 @@ def _append_additional_requirements(
218
222
  if not additional_requirements:
219
223
  additional_requirements = []
220
224
 
225
+ if isinstance(additional_requirements, str):
226
+ additional_requirements = additional_requirements.split(",")
227
+
221
228
  # Combine base requirements with user requirements
222
229
  all_requirements = sorted(
223
230
  list(
@@ -230,6 +237,72 @@ def _append_additional_requirements(
230
237
  f.write(f"{req}\n")
231
238
 
232
239
 
240
+ def _parse_pyproject_toml(pyproject_path: Path) -> List[str]:
241
+ deps: List[str] = []
242
+ if not pyproject_path.is_file():
243
+ return deps
244
+ text = pyproject_path.read_text(encoding="utf-8")
245
+
246
+ try:
247
+ # Prefer stdlib tomllib (Python 3.11+)
248
+ if tomllib is None:
249
+ raise RuntimeError("tomllib not available")
250
+ data = tomllib.loads(text)
251
+ # PEP 621
252
+ proj = data.get("project") or {}
253
+ deps.extend(proj.get("dependencies") or [])
254
+ # Poetry fallback
255
+ poetry = (data.get("tool") or {}).get("poetry") or {}
256
+ poetry_deps = poetry.get("dependencies") or {}
257
+ for name, spec in poetry_deps.items():
258
+ if name.lower() == "python":
259
+ continue
260
+ if isinstance(spec, str):
261
+ deps.append(f"{name}{spec if spec.strip() else ''}")
262
+ elif isinstance(spec, dict):
263
+ version = spec.get("version")
264
+ if version:
265
+ deps.append(f"{name}{version}")
266
+ else:
267
+ deps.append(name)
268
+ except Exception:
269
+ # Minimal non-toml parser fallback
270
+ block_match = re.search(
271
+ r"dependencies\s*=\s*\[(.*?)\]",
272
+ text,
273
+ re.S | re.I,
274
+ )
275
+ if block_match:
276
+ block = block_match.group(1)
277
+ for m in re.finditer(r"['\"]([^'\"]+)['\"]", block):
278
+ deps.append(m.group(1))
279
+ # Poetry fallback: very limited, heuristic
280
+ poetry_block = re.search(
281
+ r"\[tool\.poetry\.dependencies\](.*?)\n\[",
282
+ text,
283
+ re.S,
284
+ )
285
+ if poetry_block:
286
+ for line in poetry_block.group(1).splitlines():
287
+ line = line.strip()
288
+ if not line or line.startswith("#"):
289
+ continue
290
+ if ":" in line:
291
+ # name = "^1.2.3"
292
+ m = re.match(
293
+ r"([A-Za-z0-9_.-]+)\s*=\s*['\"]([^'\"]+)['\"]",
294
+ line,
295
+ )
296
+ if m and m.group(1).lower() != "python":
297
+ deps.append(f"{m.group(1)}{m.group(2)}")
298
+ else:
299
+ # name without version
300
+ name = line.split("#")[0].strip()
301
+ if name and name.lower() != "python":
302
+ deps.append(name)
303
+ return deps
304
+
305
+
233
306
  def _get_package_version() -> str:
234
307
  """
235
308
  Get the package version from pyproject.toml file.
@@ -496,7 +569,9 @@ def _write_bundle_meta(bundle_dir: Path, entry_script: str) -> None:
496
569
  meta_path.write_text(json.dumps(meta, indent=2), encoding="utf-8")
497
570
 
498
571
 
499
- def get_bundle_entry_script(bundle_dir: Union[str, Path]) -> str:
572
+ def get_bundle_entry_script(bundle_dir: Optional[Union[str, Path]]) -> str:
573
+ if bundle_dir is None:
574
+ return DEFAULT_ENTRYPOINT_FILE
500
575
  meta_path = Path(bundle_dir) / META_FILENAME
501
576
  if meta_path.exists():
502
577
  try:
@@ -17,11 +17,13 @@ class RegistryConfig(BaseModel):
17
17
  registry_url: str = ""
18
18
  username: str = None
19
19
  password: str = None
20
- namespace: str = "agentscope-runtime"
20
+ namespace: Optional[str] = "agentscope-runtime"
21
21
  image_pull_secret: str = None
22
22
 
23
23
  def get_full_url(self) -> str:
24
24
  # Handle different registry URL formats
25
+ if self.registry_url == "localhost":
26
+ return self.registry_url
25
27
  return f"{self.registry_url}/{self.namespace}"
26
28
 
27
29
 
@@ -201,27 +203,21 @@ class DockerImageBuilder:
201
203
  error_msg,
202
204
  ) from e
203
205
 
204
- def push_image(
206
+ def tag_image(
205
207
  self,
206
208
  image_name: str,
207
209
  registry_config: Optional[RegistryConfig] = None,
208
- quiet: bool = False,
209
210
  ) -> str:
210
211
  """
211
- Push image to registry.
212
+ Tag image with registry info.
212
213
 
213
214
  Args:
214
215
  image_name: Full image name to push
215
216
  registry_config: Optional registry config
216
217
  (uses instance config if None)
217
- quiet: Whether to suppress output
218
218
 
219
219
  Returns:
220
- str: Full image name that was pushed
221
-
222
- Raises:
223
- subprocess.CalledProcessError: If push fails
224
- ValueError: If no registry configuration is available
220
+ registry_image_name: Full image name with url
225
221
  """
226
222
  config = registry_config
227
223
  if not config:
@@ -240,6 +236,31 @@ class DockerImageBuilder:
240
236
  )
241
237
  else:
242
238
  registry_image_name = image_name
239
+ return registry_image_name
240
+
241
+ def push_image(
242
+ self,
243
+ image_name: str,
244
+ registry_config: Optional[RegistryConfig] = None,
245
+ quiet: bool = False,
246
+ ) -> str:
247
+ """
248
+ Push image to registry.
249
+
250
+ Args:
251
+ image_name: Full image name to push
252
+ registry_config: Optional registry config
253
+ (uses instance config if None)
254
+ quiet: Whether to suppress output
255
+
256
+ Returns:
257
+ str: Full image name that was pushed
258
+
259
+ Raises:
260
+ subprocess.CalledProcessError: If push fails
261
+ ValueError: If no registry configuration is available
262
+ """
263
+ registry_image_name = self.tag_image(image_name, registry_config)
243
264
 
244
265
  try:
245
266
  push_cmd = ["docker", "push", registry_image_name]
@@ -21,6 +21,8 @@ class DockerfileConfig(BaseModel):
21
21
  startup_command: Optional[str] = None
22
22
  health_check_endpoint: str = "/health"
23
23
  custom_template: Optional[str] = None
24
+ platform: Optional[str] = None
25
+ pypi_mirror: Optional[str] = None
24
26
 
25
27
 
26
28
  class DockerfileGenerator:
@@ -31,7 +33,7 @@ class DockerfileGenerator:
31
33
 
32
34
  # Default Dockerfile template for Python applications
33
35
  DEFAULT_TEMPLATE = """# Use official Python runtime as base image
34
- FROM {base_image}
36
+ FROM --platform={platform} {base_image}
35
37
 
36
38
  # Set working directory in container
37
39
  WORKDIR /app
@@ -43,15 +45,20 @@ ENV PYTHONUNBUFFERED=1
43
45
  # Configure package sources for better performance
44
46
  RUN rm -f /etc/apt/sources.list.d/*.list
45
47
 
46
- # 替换主源为阿里云
47
- RUN echo "deb https://mirrors.aliyun.com/debian/ bookworm main contrib " \\
48
- "non-free non-free-firmware" > /etc/apt/sources.list && \\
49
- echo "deb https://mirrors.aliyun.com/debian/ bookworm-updates main " \\
50
- "contrib non-free non-free-firmware" >> /etc/apt/sources.list && \\
51
- echo "deb https://mirrors.aliyun.com/debian-security/ " \\
52
- "bookworm-security main contrib non-free " \\
48
+ # add aliyun mirrors
49
+ RUN echo "deb https://mirrors.aliyun.com/debian/ bookworm main contrib " \
50
+ "non-free non-free-firmware" > /etc/apt/sources.list && \
51
+ echo "deb https://mirrors.aliyun.com/debian/ bookworm-updates main " \
52
+ "contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
53
+ echo "deb https://mirrors.aliyun.com/debian-security/ " \
54
+ "bookworm-security main contrib non-free " \
53
55
  "non-free-firmware" >> /etc/apt/sources.list
54
56
 
57
+ # replace debian to aliyun
58
+ RUN mkdir -p /etc/apt/sources.list.d && \
59
+ cat > /etc/apt/sources.list.d/debian.sources <<'EOF'
60
+ EOF
61
+
55
62
  # Clean up package lists
56
63
  RUN rm -rf /var/lib/apt/lists/*
57
64
 
@@ -67,8 +74,7 @@ COPY . {working_dir}/
67
74
  # Install Python dependencies
68
75
  RUN pip install --no-cache-dir --upgrade pip
69
76
  RUN if [ -f requirements.txt ]; then \\
70
- pip install --no-cache-dir -r requirements.txt \\
71
- -i https://pypi.tuna.tsinghua.edu.cn/simple; fi
77
+ pip install --no-cache-dir -r requirements.txt{pypi_mirror_flag}; fi
72
78
 
73
79
  # Create non-root user for security
74
80
  RUN adduser --disabled-password --gecos '' {user} && \\
@@ -130,6 +136,11 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \\
130
136
  f'"--port", "{config.port}"]'
131
137
  )
132
138
 
139
+ # Prepare PyPI mirror flag
140
+ pypi_mirror_flag = ""
141
+ if config.pypi_mirror:
142
+ pypi_mirror_flag = f" -i {config.pypi_mirror}"
143
+
133
144
  # Format template with configuration values
134
145
  content = template.format(
135
146
  base_image=config.base_image,
@@ -140,6 +151,8 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \\
140
151
  additional_packages_section=additional_packages_section,
141
152
  env_vars_section=env_vars_section,
142
153
  startup_command_section=startup_command_section,
154
+ platform=config.platform,
155
+ pypi_mirror_flag=pypi_mirror_flag,
143
156
  )
144
157
 
145
158
  return content
@@ -28,7 +28,7 @@ class ImageConfig(BaseModel):
28
28
  # Package configuration
29
29
  requirements: Optional[List[str]] = None
30
30
  extra_packages: Optional[List[str]] = None
31
- build_context_dir: str = "/tmp/k8s_build"
31
+ build_context_dir: Optional[str] = None
32
32
  endpoint_path: str = "/process"
33
33
  protocol_adapters: Optional[List] = None # New: protocol adapters
34
34
  custom_endpoints: Optional[
@@ -40,6 +40,7 @@ class ImageConfig(BaseModel):
40
40
  port: int = 8000
41
41
  env_vars: Dict[str, str] = Field(default_factory=lambda: {})
42
42
  startup_command: Optional[str] = None
43
+ pypi_mirror: Optional[str] = None
43
44
 
44
45
  # Runtime configuration
45
46
  host: str = "0.0.0.0" # Container-friendly default
@@ -171,6 +172,8 @@ class ImageFactory:
171
172
  app,
172
173
  runner: Optional[Runner],
173
174
  config: ImageConfig,
175
+ entrypoint: Optional[str] = None,
176
+ use_cache: bool = True,
174
177
  ) -> str:
175
178
  """
176
179
  Build a complete Docker image for the Runner.
@@ -181,9 +184,15 @@ class ImageFactory:
181
184
  3. Build Docker image
182
185
  4. Optionally push to registry
183
186
 
187
+ All temporary files are created in cwd/.agentscope_runtime/ by default.
188
+
184
189
  Args:
190
+ app: Agent app object
185
191
  runner: Runner object containing agent and managers
186
192
  config: Configuration for the image building process
193
+ entrypoint: Entrypoint specification (e.g., "app.py" or
194
+ "app.py:handler")
195
+ use_cache: Enable build cache (default: True)
187
196
 
188
197
  Returns:
189
198
  str: Full image name (with registry if pushed)
@@ -209,6 +218,8 @@ class ImageFactory:
209
218
  port=config.port,
210
219
  env_vars=config.env_vars,
211
220
  startup_command=startup_command,
221
+ platform=config.platform,
222
+ pypi_mirror=config.pypi_mirror,
212
223
  )
213
224
 
214
225
  dockerfile_path = self.dockerfile_generator.create_dockerfile(
@@ -222,10 +233,13 @@ class ImageFactory:
222
233
  project_dir, _ = build_detached_app(
223
234
  app=app,
224
235
  runner=runner,
236
+ entrypoint=entrypoint,
225
237
  requirements=config.requirements,
226
238
  extra_packages=config.extra_packages,
227
239
  output_dir=config.build_context_dir,
228
240
  dockerfile_path=dockerfile_path,
241
+ use_cache=use_cache,
242
+ platform="k8s",
229
243
  )
230
244
  is_updated = True
231
245
  logger.info(f"Project packaged: {project_dir}")
@@ -260,6 +274,15 @@ class ImageFactory:
260
274
  config=build_config,
261
275
  source_updated=is_updated,
262
276
  )
277
+ logger.info(f"Image built: {full_image_name}")
278
+
279
+ # make sure tag the image if not push
280
+ registry_full_name = self.image_builder.tag_image(
281
+ full_image_name,
282
+ config.registry_config,
283
+ )
284
+ logger.info(f"Image tag to: {registry_full_name}")
285
+
263
286
  logger.info(f"Image built locally: {full_image_name}")
264
287
 
265
288
  return full_image_name
@@ -276,6 +299,7 @@ class ImageFactory:
276
299
  self,
277
300
  app=None,
278
301
  runner: Optional[Runner] = None,
302
+ entrypoint: Optional[str] = None,
279
303
  requirements: Optional[Union[str, List[str]]] = None,
280
304
  extra_packages: Optional[List[str]] = None,
281
305
  base_image: str = "python:3.10-slim-bookworm",
@@ -291,14 +315,20 @@ class ImageFactory:
291
315
  host: str = "0.0.0.0",
292
316
  embed_task_processor: bool = True,
293
317
  extra_startup_args: Optional[Dict[str, Union[str, int, bool]]] = None,
318
+ use_cache: bool = True,
319
+ pypi_mirror: Optional[str] = None,
294
320
  **kwargs,
295
321
  ) -> str:
296
322
  """
297
323
  Simplified interface for building Runner images.
298
324
 
325
+ All temporary files are created in cwd/.agentscope_runtime/ by default.
326
+
299
327
  Args:
300
328
  app: agent app object
301
329
  runner: Runner object
330
+ entrypoint: Entrypoint specification (e.g., "app.py" or
331
+ "app.py:handler")
302
332
  requirements: Python requirements
303
333
  extra_packages: Additional files to include
304
334
  base_image: Docker base image
@@ -311,6 +341,8 @@ class ImageFactory:
311
341
  host: Host to bind to (default: 0.0.0.0 for containers)
312
342
  embed_task_processor: Whether to embed task processor
313
343
  extra_startup_args: Additional startup arguments
344
+ use_cache: Enable build cache (default: True)
345
+ pypi_mirror: PyPI mirror URL for pip package installation
314
346
  **kwargs: Additional configuration options
315
347
 
316
348
  Returns:
@@ -345,10 +377,11 @@ class ImageFactory:
345
377
  host=host,
346
378
  embed_task_processor=embed_task_processor,
347
379
  extra_startup_args=extra_startup_args or {},
380
+ pypi_mirror=pypi_mirror,
348
381
  **kwargs,
349
382
  )
350
383
 
351
- return self._build_image(app, runner, config)
384
+ return self._build_image(app, runner, config, entrypoint, use_cache)
352
385
 
353
386
  def cleanup(self):
354
387
  """Clean up all temporary resources"""