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
@@ -7,6 +7,7 @@ import logging
7
7
  import os
8
8
  import time
9
9
  from dataclasses import dataclass
10
+ from datetime import datetime
10
11
  from pathlib import Path
11
12
  from typing import Dict, Optional, List, Any, Union, Tuple
12
13
 
@@ -34,7 +35,9 @@ from pydantic import BaseModel, Field
34
35
  from .adapter.protocol_adapter import ProtocolAdapter
35
36
  from .base import DeployManager
36
37
  from .local_deployer import LocalDeployManager
38
+ from .state import Deployment
37
39
  from .utils.detached_app import get_bundle_entry_script
40
+ from .utils.package import generate_build_directory
38
41
  from .utils.wheel_packager import (
39
42
  default_deploy_name,
40
43
  generate_wrapper_project,
@@ -95,7 +98,7 @@ class AgentRunConfig(BaseModel):
95
98
 
96
99
  execution_role_arn: Optional[str] = None
97
100
 
98
- session_concurrency_limit: Optional[int] = 1
101
+ session_concurrency_limit: Optional[int] = 200
99
102
  session_idle_timeout_seconds: Optional[int] = 3600
100
103
 
101
104
  @classmethod
@@ -147,7 +150,7 @@ class AgentRunConfig(BaseModel):
147
150
 
148
151
  session_concurrency_limit_str = os.environ.get(
149
152
  "AGENT_RUN_SESSION_CONCURRENCY_LIMIT",
150
- "1",
153
+ "200",
151
154
  )
152
155
  session_idle_timeout_seconds_str = os.environ.get(
153
156
  "AGENT_RUN_SESSION_IDLE_TIMEOUT_SECONDS",
@@ -218,6 +221,7 @@ class OSSConfig(BaseModel):
218
221
  region: str = Field("cn-hangzhou", description="OSS region")
219
222
  access_key_id: Optional[str] = None
220
223
  access_key_secret: Optional[str] = None
224
+ bucket_name: str
221
225
 
222
226
  @classmethod
223
227
  def from_env(cls) -> "OSSConfig":
@@ -236,6 +240,7 @@ class OSSConfig(BaseModel):
236
240
  "OSS_ACCESS_KEY_SECRET",
237
241
  os.environ.get("ALIBABA_CLOUD_ACCESS_KEY_SECRET"),
238
242
  ),
243
+ bucket_name=os.environ.get("OSS_BUCKET_NAME"),
239
244
  )
240
245
 
241
246
  def ensure_valid(self) -> None:
@@ -245,10 +250,14 @@ class OSSConfig(BaseModel):
245
250
  RuntimeError: If required AccessKey credentials are missing.
246
251
  """
247
252
  # Allow fallback to Alibaba Cloud AK/SK via from_env()
248
- if not self.access_key_id or not self.access_key_secret:
253
+ if (
254
+ not self.access_key_id
255
+ or not self.access_key_secret
256
+ or not self.bucket_name
257
+ ):
249
258
  raise RuntimeError(
250
259
  "Missing AccessKey for OSS. Set either OSS_ACCESS_KEY_ID/OSS_ACCESS_KEY_SECRET "
251
- "or ALIBABA_CLOUD_ACCESS_KEY_ID/ALIBABA_CLOUD_ACCESS_KEY_SECRET.",
260
+ "or ALIBABA_CLOUD_ACCESS_KEY_ID/ALIBABA_CLOUD_ACCESS_KEY_SECRET or OSS_BUCKET_NAME.",
252
261
  )
253
262
 
254
263
 
@@ -278,6 +287,7 @@ class AgentRunDeployManager(DeployManager):
278
287
  oss_config: Optional[OSSConfig] = None,
279
288
  agentrun_config: Optional[AgentRunConfig] = None,
280
289
  build_root: Optional[Union[str, Path]] = None,
290
+ state_manager=None,
281
291
  ):
282
292
  """Initialize AgentRun deployment manager.
283
293
 
@@ -285,8 +295,9 @@ class AgentRunDeployManager(DeployManager):
285
295
  oss_config: OSS configuration for artifact storage. If None, loads from environment.
286
296
  agentrun_config: AgentRun service configuration. If None, loads from environment.
287
297
  build_root: Root directory for build artifacts. If None, uses parent directory of current working directory.
298
+ state_manager: Deployment state manager. If None, creates a new instance.
288
299
  """
289
- super().__init__()
300
+ super().__init__(state_manager=state_manager)
290
301
  self.oss_config = oss_config or OSSConfig.from_env()
291
302
  self.agentrun_config = agentrun_config or AgentRunConfig.from_env()
292
303
  self.build_root = (
@@ -416,18 +427,19 @@ class AgentRunDeployManager(DeployManager):
416
427
  )
417
428
 
418
429
  name = deploy_name or default_deploy_name()
419
- proj_root = project_dir.resolve()
430
+
431
+ # Generate build directory with platform-aware naming
432
+ # proj_root = project_dir.resolve()
420
433
  if isinstance(self.build_root, Path):
421
434
  effective_build_root = self.build_root.resolve()
422
435
  else:
423
436
  if self.build_root:
424
437
  effective_build_root = Path(self.build_root).resolve()
425
438
  else:
426
- effective_build_root = (
427
- proj_root.parent / ".agentscope_runtime_builds"
428
- ).resolve()
439
+ # Use centralized directory generation function
440
+ effective_build_root = generate_build_directory("agentrun")
429
441
 
430
- build_dir = effective_build_root / f"build-{int(time.time())}"
442
+ build_dir = effective_build_root
431
443
  build_dir.mkdir(parents=True, exist_ok=True)
432
444
 
433
445
  logger.info("Generating wrapper project: %s", name)
@@ -442,6 +454,7 @@ class AgentRunDeployManager(DeployManager):
442
454
  logger.info("Building wheel package from: %s", wrapper_project_dir)
443
455
  wheel_path = build_wheel(wrapper_project_dir)
444
456
  logger.info("Wheel package created: %s", wheel_path)
457
+
445
458
  return wheel_path, name
446
459
 
447
460
  def _generate_env_file(
@@ -560,9 +573,14 @@ class AgentRunDeployManager(DeployManager):
560
573
  FileNotFoundError: If specified files/directories don't exist.
561
574
  """
562
575
  if not agentrun_id:
563
- if not runner and not project_dir and not external_whl_path:
576
+ if (
577
+ not app
578
+ and not runner
579
+ and not project_dir
580
+ and not external_whl_path
581
+ ):
564
582
  raise ValueError(
565
- "Must provide either runner, project_dir, or external_whl_path",
583
+ "Must provide either app, runner, project_dir, or external_whl_path",
566
584
  )
567
585
  try:
568
586
  if runner or app:
@@ -579,6 +597,7 @@ class AgentRunDeployManager(DeployManager):
579
597
  protocol_adapters=protocol_adapters,
580
598
  requirements=requirements,
581
599
  extra_packages=extra_packages,
600
+ platform="agentrun",
582
601
  **kwargs,
583
602
  )
584
603
  if project_dir:
@@ -647,7 +666,7 @@ class AgentRunDeployManager(DeployManager):
647
666
  logger.info("Uploading zip package to OSS")
648
667
  oss_result = await self._upload_to_fixed_oss_bucket(
649
668
  zip_file_path=zip_file_path,
650
- bucket_name="tmp-agentscope-agentrun-code",
669
+ bucket_name=self.oss_config.bucket_name,
651
670
  )
652
671
  logger.info("Zip package uploaded to OSS successfully")
653
672
 
@@ -661,21 +680,50 @@ class AgentRunDeployManager(DeployManager):
661
680
  environment=environment,
662
681
  )
663
682
 
683
+ # Use base class UUID deploy_id (already set in __init__)
684
+ deploy_id = self.deploy_id
685
+ agent_runtime_id = agentrun_deploy_result["agent_runtime_id"]
686
+ endpoint_url = agentrun_deploy_result.get(
687
+ "agent_runtime_public_endpoint_url",
688
+ "",
689
+ )
690
+ console_url = (
691
+ f"https://functionai.console.aliyun.com/{self.agentrun_config.region_id}/"
692
+ f"agent/infra/agent-runtime/agent-detail?id={agent_runtime_id}"
693
+ )
694
+
695
+ # Save deployment to state manager
696
+ deployment = Deployment(
697
+ id=deploy_id,
698
+ platform="agentrun",
699
+ url=console_url,
700
+ status="running",
701
+ created_at=datetime.now().isoformat(),
702
+ agent_source=kwargs.get("agent_source"),
703
+ config={
704
+ "agent_runtime_id": agent_runtime_id,
705
+ "agent_runtime_endpoint_url": endpoint_url,
706
+ "resource_name": name,
707
+ "wheel_path": str(wheel_path),
708
+ "artifact_url": oss_result.get("presigned_url", ""),
709
+ "region_id": self.agentrun_config.region_id,
710
+ },
711
+ )
712
+ self.state_manager.save(deployment)
713
+
664
714
  # Return deployment results
665
715
  logger.info(
666
716
  "Deployment completed successfully. Agent runtime ID: %s",
667
- agentrun_deploy_result["agent_runtime_id"],
717
+ agent_runtime_id,
668
718
  )
669
719
  return {
670
720
  "message": "Agent deployed successfully to AgentRun",
671
- "agentrun_id": agentrun_deploy_result["agent_runtime_id"],
672
- "agentrun_endpoint_url": agentrun_deploy_result[
673
- "agent_runtime_public_endpoint_url"
674
- ],
721
+ "agentrun_id": agent_runtime_id,
722
+ "agentrun_endpoint_url": endpoint_url,
675
723
  "wheel_path": str(wheel_path),
676
- "artifact_url": oss_result["presigned_url"],
677
- "url": f'https://functionai.console.aliyun.com/{self.agentrun_config.region_id}/agent/infra/agent-runtime/agent-detail?id={agentrun_deploy_result["agent_runtime_id"]}',
678
- "deploy_id": agentrun_deploy_result["agent_runtime_id"],
724
+ "artifact_url": oss_result.get("presigned_url", ""),
725
+ "url": console_url,
726
+ "deploy_id": deploy_id,
679
727
  "resource_name": name,
680
728
  }
681
729
 
@@ -861,7 +909,6 @@ ls -lh /output/{zip_filename}
861
909
  from alibabacloud_oss_v2.credentials import (
862
910
  StaticCredentialsProvider,
863
911
  )
864
- import datetime
865
912
  except ImportError as e:
866
913
  logger.error(
867
914
  "OSS SDK not available. Install with: pip install alibabacloud-oss-v2",
@@ -2539,3 +2586,86 @@ ls -lh /output/{zip_filename}
2539
2586
  "error": str(e),
2540
2587
  "message": f"Exception occurred while publishing agent runtime version: {str(e)}",
2541
2588
  }
2589
+
2590
+ async def stop(self, deploy_id: str, **kwargs) -> Dict[str, Any]:
2591
+ """Stop AgentRun deployment by deleting it.
2592
+
2593
+ Args:
2594
+ deploy_id: AgentRun runtime ID (agent_runtime_id)
2595
+ **kwargs: Additional parameters
2596
+
2597
+ Returns:
2598
+ Dict with success status, message, and details
2599
+ """
2600
+ try:
2601
+ # Try to get deployment info from state for context
2602
+ deployment_info = None
2603
+ deployment = None
2604
+ try:
2605
+ deployment = self.state_manager.get(deploy_id)
2606
+ if deployment:
2607
+ deployment_info = {
2608
+ "url": deployment.url
2609
+ if hasattr(deployment, "url")
2610
+ else None,
2611
+ "resource_name": getattr(
2612
+ deployment,
2613
+ "resource_name",
2614
+ None,
2615
+ ),
2616
+ }
2617
+ logger.debug(
2618
+ f"Fetched deployment info from state: {deployment_info}",
2619
+ )
2620
+ except Exception as e:
2621
+ logger.debug(
2622
+ f"Could not fetch deployment info from state: {e}",
2623
+ )
2624
+
2625
+ logger.info(f"Stopping AgentRun deployment: {deploy_id}")
2626
+
2627
+ # Get agent_runtime_id from deployment config
2628
+ agent_runtime_id = None
2629
+ if deployment and deployment.config:
2630
+ agent_runtime_id = deployment.config.get("agent_runtime_id")
2631
+
2632
+ if not agent_runtime_id:
2633
+ # Fallback: try using deploy_id as agent_runtime_id for backward compatibility
2634
+ agent_runtime_id = deploy_id
2635
+ logger.warning(
2636
+ f"Could not find agent_runtime_id in deployment config, "
2637
+ f"using deploy_id as fallback: {deploy_id}",
2638
+ )
2639
+
2640
+ # Use the existing delete method with agent_runtime_id
2641
+ result = await self.delete(agent_runtime_id)
2642
+
2643
+ if result.get("success"):
2644
+ # Remove from state manager on successful deletion
2645
+ try:
2646
+ self.state_manager.update_status(deploy_id, "stopped")
2647
+ except KeyError:
2648
+ logger.debug(
2649
+ f"Deployment {deploy_id} not found in state (already removed)",
2650
+ )
2651
+
2652
+ return {
2653
+ "success": True,
2654
+ "message": f"AgentRun deployment {deploy_id} deleted successfully",
2655
+ "details": result,
2656
+ }
2657
+ else:
2658
+ return {
2659
+ "success": False,
2660
+ "message": f"Failed to delete AgentRun deployment: {result.get('message', 'Unknown error')}",
2661
+ "details": result,
2662
+ }
2663
+ except Exception as e:
2664
+ logger.error(
2665
+ f"Failed to stop AgentRun deployment {deploy_id}: {e}",
2666
+ )
2667
+ return {
2668
+ "success": False,
2669
+ "message": f"Failed to stop AgentRun deployment: {e}",
2670
+ "details": {"deploy_id": deploy_id, "error": str(e)},
2671
+ }
@@ -1,18 +1,43 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import uuid
3
3
  from abc import abstractmethod, ABC
4
- from typing import Dict
4
+ from typing import Dict, Any
5
5
 
6
6
 
7
7
  # there is not many attributes in it, consider it as interface, instead of
8
8
  # pydantic BaseModel
9
9
  class DeployManager(ABC):
10
- def __init__(self):
10
+ def __init__(self, state_manager=None):
11
11
  self.deploy_id = str(uuid.uuid4())
12
12
  self._app = None
13
13
 
14
+ # Initialize state manager - shared across all deployers
15
+ if state_manager is None:
16
+ from agentscope_runtime.engine.deployers.state import (
17
+ DeploymentStateManager,
18
+ )
19
+
20
+ state_manager = DeploymentStateManager()
21
+ self.state_manager = state_manager
22
+
14
23
  @abstractmethod
15
24
  async def deploy(self, *args, **kwargs) -> Dict[str, str]:
16
25
  """Deploy the service and return a dictionary with deploy_id and
17
26
  URL."""
18
27
  raise NotImplementedError
28
+
29
+ @abstractmethod
30
+ async def stop(self, deploy_id: str, **kwargs) -> Dict[str, Any]:
31
+ """Stop the deployed service.
32
+
33
+ Args:
34
+ deploy_id: Deployment identifier
35
+ **kwargs: Platform-specific parameters (url, namespace, etc.)
36
+
37
+ Returns:
38
+ Dict with keys:
39
+ - success (bool): Whether stop succeeded
40
+ - message (str): Status message
41
+ - details (dict, optional): Platform-specific details
42
+ """
43
+ raise NotImplementedError
@@ -1,17 +1,19 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import logging
3
3
  import os
4
- import time
4
+ from datetime import datetime
5
5
  from typing import Optional, Dict, List, Union, Any
6
6
 
7
7
  from pydantic import BaseModel, Field
8
8
 
9
+ from agentscope_runtime.engine.deployers.state import Deployment
9
10
  from .adapter.protocol_adapter import ProtocolAdapter
10
11
  from .base import DeployManager
11
12
  from .utils.docker_image_utils import (
12
13
  ImageFactory,
13
14
  RegistryConfig,
14
15
  )
16
+ from .utils.k8s_utils import isLocalK8sEnvironment
15
17
  from ...common.container_clients.kubernetes_client import (
16
18
  KubernetesClient,
17
19
  )
@@ -36,7 +38,7 @@ class K8sConfig(BaseModel):
36
38
  class BuildConfig(BaseModel):
37
39
  """Build configuration"""
38
40
 
39
- build_context_dir: str = "/tmp/k8s_build"
41
+ build_context_dir: Optional[str] = None # None allows caching
40
42
  dockerfile_template: str = None
41
43
  build_timeout: int = 600 # 10 minutes
42
44
  push_timeout: int = 300 # 5 minutes
@@ -51,15 +53,15 @@ class KubernetesDeployManager(DeployManager):
51
53
  kube_config: K8sConfig = None,
52
54
  registry_config: RegistryConfig = RegistryConfig(),
53
55
  use_deployment: bool = True,
54
- build_context_dir: str = "/tmp/k8s_build",
56
+ build_context_dir: Optional[str] = None,
57
+ state_manager=None,
55
58
  ):
56
- super().__init__()
59
+ super().__init__(state_manager=state_manager)
57
60
  self.kubeconfig = kube_config
58
61
  self.registry_config = registry_config
59
62
  self.image_factory = ImageFactory()
60
63
  self.use_deployment = use_deployment
61
64
  self.build_context_dir = build_context_dir
62
- self._deployed_resources = {}
63
65
  self._built_images = {}
64
66
 
65
67
  self.k8s_client = KubernetesClient(
@@ -67,10 +69,65 @@ class KubernetesDeployManager(DeployManager):
67
69
  image_registry=self.registry_config.get_full_url(),
68
70
  )
69
71
 
72
+ @staticmethod
73
+ def get_service_endpoint(
74
+ service_external_ip: Optional[str],
75
+ service_port: Optional[Union[int, list]],
76
+ fallback_host: str = "127.0.0.1",
77
+ ) -> str:
78
+ """
79
+ Auto-select appropriate service endpoint based on detected environment.
80
+
81
+ Solves the common issue where Kubernetes LoadBalancer/ExternalIP is not
82
+ reachable from localhost in local clusters (e.g., Minikube/Kind).
83
+
84
+ Args:
85
+ service_external_ip: ExternalIP or LoadBalancer IP from Service
86
+ service_port: Target port
87
+ fallback_host: Host to use in local environments (default:
88
+ 127.0.0.1)
89
+
90
+ Returns:
91
+ str: Full HTTP endpoint URL: http://<host>:<port>
92
+
93
+ Example:
94
+ >>> endpoint = get_service_endpoint('192.168.5.1', 8080)
95
+ >>> # In local env → 'http://127.0.0.1:8080'
96
+ >>> # In cloud env → 'http://192.168.5.1:8080'
97
+ """
98
+ if not service_external_ip:
99
+ service_external_ip = "127.0.0.1"
100
+
101
+ if not service_port:
102
+ service_port = 8080
103
+
104
+ if isinstance(service_port, list):
105
+ service_port = service_port[0]
106
+
107
+ if isLocalK8sEnvironment():
108
+ host = fallback_host
109
+ logger.info(
110
+ f"Local K8s environment detected; using {host} instead of "
111
+ f"{service_external_ip}",
112
+ )
113
+ else:
114
+ host = service_external_ip
115
+ logger.info(
116
+ f"Cloud/remote environment detected; using External IP: "
117
+ f"{host}",
118
+ )
119
+
120
+ return f"http://{host}:{service_port}"
121
+
122
+ @staticmethod
123
+ def get_resource_name(deploy_id: str) -> str:
124
+ return f"agent-{deploy_id[:8]}"
125
+
70
126
  async def deploy(
71
127
  self,
72
128
  app=None,
73
129
  runner=None,
130
+ entrypoint: Optional[str] = None,
74
131
  endpoint_path: str = "/process",
75
132
  stream: bool = True,
76
133
  custom_endpoints: Optional[List[Dict]] = None,
@@ -86,15 +143,20 @@ class KubernetesDeployManager(DeployManager):
86
143
  image_name: str = "agent_llm",
87
144
  image_tag: str = "latest",
88
145
  push_to_registry: bool = False,
146
+ use_cache: bool = True,
147
+ pypi_mirror: Optional[str] = None,
89
148
  **kwargs,
90
149
  ) -> Dict[str, Any]:
91
150
  """
92
151
  Deploy runner to Kubernetes.
93
152
 
153
+ All temporary files are created in cwd/.agentscope_runtime/ by default.
154
+
94
155
  Args:
95
156
  app: Agent app to be deployed
96
157
  runner: Complete Runner object with agent, environment_manager,
97
158
  context_manager
159
+ entrypoint: Entrypoint spec (e.g., "app.py" or "app.py:handler")
98
160
  endpoint_path: API endpoint path
99
161
  stream: Enable streaming responses
100
162
  custom_endpoints: Custom endpoints from agent app
@@ -108,6 +170,8 @@ class KubernetesDeployManager(DeployManager):
108
170
  environment: Environment variables dict
109
171
  mount_dir: Mount directory
110
172
  runtime_config: K8s runtime configuration
173
+ use_cache: Enable build cache (default: True)
174
+ pypi_mirror: PyPI mirror URL for pip package installation
111
175
  # Backward compatibility
112
176
  image_name: Image name
113
177
  image_tag: Image tag
@@ -132,6 +196,7 @@ class KubernetesDeployManager(DeployManager):
132
196
  built_image_name = self.image_factory.build_image(
133
197
  app=app,
134
198
  runner=runner,
199
+ entrypoint=entrypoint,
135
200
  requirements=requirements,
136
201
  extra_packages=extra_packages or [],
137
202
  base_image=base_image,
@@ -145,6 +210,8 @@ class KubernetesDeployManager(DeployManager):
145
210
  port=port,
146
211
  protocol_adapters=protocol_adapters,
147
212
  custom_endpoints=custom_endpoints,
213
+ use_cache=use_cache,
214
+ pypi_mirror=pypi_mirror,
148
215
  **kwargs,
149
216
  )
150
217
  if not built_image_name:
@@ -173,7 +240,7 @@ class KubernetesDeployManager(DeployManager):
173
240
  else:
174
241
  volume_bindings = {}
175
242
 
176
- resource_name = f"agent-{deploy_id[:8]}"
243
+ resource_name = self.get_resource_name(deploy_id)
177
244
 
178
245
  logger.info(f"Building kubernetes deployment for {deploy_id}")
179
246
 
@@ -197,33 +264,36 @@ class KubernetesDeployManager(DeployManager):
197
264
  )
198
265
 
199
266
  if ports:
200
- url = f"http://{ip}:{ports[0]}"
267
+ url = self.get_service_endpoint(ip, ports)
201
268
  else:
202
- url = f"http://{ip}:8080"
269
+ url = self.get_service_endpoint(ip, port)
203
270
 
204
271
  logger.info(f"Deployment {deploy_id} successful: {url}")
205
272
 
206
- self._deployed_resources[deploy_id] = {
207
- "resource_name": resource_name,
208
- "service_name": _id,
209
- "image": built_image_name,
210
- "created_at": time.time(),
211
- "replicas": replicas if self.use_deployment else 1,
212
- "config": {
213
- "runner": runner.__class__.__name__,
273
+ deployment = Deployment(
274
+ id=deploy_id,
275
+ platform="k8s",
276
+ url=url,
277
+ status="running",
278
+ created_at=datetime.now().isoformat(),
279
+ agent_source=kwargs.get("agent_source"),
280
+ config={
281
+ "service_name": _id,
282
+ "image": built_image_name,
283
+ "replicas": replicas if self.use_deployment else 1,
284
+ "runner": runner.__class__.__name__ if runner else None,
214
285
  "extra_packages": extra_packages,
215
- "requirements": requirements, # New format
286
+ "requirements": requirements,
216
287
  "base_image": base_image,
217
288
  "port": port,
218
- "replicas": replicas,
219
289
  "environment": environment,
220
290
  "runtime_config": runtime_config,
221
291
  "endpoint_path": endpoint_path,
222
292
  "stream": stream,
223
- "protocol_adapters": protocol_adapters,
224
- **kwargs,
225
293
  },
226
- }
294
+ )
295
+ self.state_manager.save(deployment)
296
+
227
297
  return {
228
298
  "deploy_id": deploy_id,
229
299
  "url": url,
@@ -240,21 +310,81 @@ class KubernetesDeployManager(DeployManager):
240
310
  f"Deployment failed: {e}, {traceback.format_exc()}",
241
311
  ) from e
242
312
 
243
- async def stop(self) -> bool:
244
- """Stop service"""
245
- if self.deploy_id not in self._deployed_resources:
246
- return False
313
+ async def stop(
314
+ self,
315
+ deploy_id: str,
316
+ **kwargs,
317
+ ) -> Dict[str, Any]:
318
+ """Stop Kubernetes deployment.
319
+
320
+ Args:
321
+ deploy_id: Deployment identifier
322
+ **kwargs: Additional parameters
323
+
324
+ Returns:
325
+ Dict with success status, message, and details
326
+ """
327
+ # Derive resource name from deploy_id
328
+ resource_name = self.get_resource_name(deploy_id)
329
+
330
+ try:
331
+ # Try to remove the deployment
332
+ success = self.k8s_client.remove_deployment(resource_name)
333
+
334
+ if success:
335
+ # Remove from state manager
336
+ try:
337
+ self.state_manager.update_status(deploy_id, "stopped")
338
+ except KeyError:
339
+ logger.debug(
340
+ f"Deployment {deploy_id} not found "
341
+ f"in state (already removed)",
342
+ )
247
343
 
248
- resources = self._deployed_resources[self.deploy_id]
249
- service_name = resources["service_name"]
250
- return self.k8s_client.remove_deployment(service_name)
344
+ return {
345
+ "success": True,
346
+ "message": f"Kubernetes deployment {resource_name} "
347
+ f"removed",
348
+ "details": {
349
+ "deploy_id": deploy_id,
350
+ "resource_name": resource_name,
351
+ },
352
+ }
353
+ else:
354
+ return {
355
+ "success": False,
356
+ "message": f"Kubernetes deployment {resource_name} not "
357
+ f"found (may already be deleted), Please check the "
358
+ f"detail in cluster",
359
+ "details": {
360
+ "deploy_id": deploy_id,
361
+ "resource_name": resource_name,
362
+ },
363
+ }
364
+ except Exception as e:
365
+ logger.error(
366
+ f"Failed to remove K8s deployment {resource_name}: {e}",
367
+ )
368
+ return {
369
+ "success": False,
370
+ "message": f"Failed to remove K8s deployment: {e}",
371
+ "details": {
372
+ "deploy_id": deploy_id,
373
+ "resource_name": resource_name,
374
+ "error": str(e),
375
+ },
376
+ }
251
377
 
252
378
  def get_status(self) -> str:
253
379
  """Get deployment status"""
254
- if self.deploy_id not in self._deployed_resources:
380
+ deployment = self.state_manager.get(self.deploy_id)
381
+ if not deployment:
255
382
  return "not_found"
256
383
 
257
- resources = self._deployed_resources[self.deploy_id]
258
- service_name = resources["service_name"]
384
+ # Get service_name from config
385
+ config = getattr(deployment, "config", {})
386
+ service_name = config.get("service_name")
387
+ if not service_name:
388
+ return "unknown"
259
389
 
260
390
  return self.k8s_client.get_deployment_status(service_name)