agentscope-runtime 0.1.5b2__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentscope_runtime/common/__init__.py +0 -0
- agentscope_runtime/common/collections/in_memory_mapping.py +27 -0
- agentscope_runtime/common/collections/redis_mapping.py +42 -0
- agentscope_runtime/common/container_clients/__init__.py +0 -0
- agentscope_runtime/common/container_clients/agentrun_client.py +1098 -0
- agentscope_runtime/common/container_clients/docker_client.py +250 -0
- agentscope_runtime/{sandbox/manager → common}/container_clients/kubernetes_client.py +6 -13
- agentscope_runtime/engine/__init__.py +12 -0
- agentscope_runtime/engine/agents/agentscope_agent.py +567 -0
- agentscope_runtime/engine/agents/agno_agent.py +26 -27
- agentscope_runtime/engine/agents/autogen_agent.py +13 -8
- agentscope_runtime/engine/agents/langgraph_agent.py +52 -9
- agentscope_runtime/engine/agents/utils.py +53 -0
- agentscope_runtime/engine/app/__init__.py +6 -0
- agentscope_runtime/engine/app/agent_app.py +239 -0
- agentscope_runtime/engine/app/base_app.py +181 -0
- agentscope_runtime/engine/app/celery_mixin.py +92 -0
- agentscope_runtime/engine/deployers/adapter/responses/response_api_adapter_utils.py +5 -1
- agentscope_runtime/engine/deployers/base.py +1 -0
- agentscope_runtime/engine/deployers/cli_fc_deploy.py +39 -20
- agentscope_runtime/engine/deployers/kubernetes_deployer.py +12 -5
- agentscope_runtime/engine/deployers/local_deployer.py +61 -3
- agentscope_runtime/engine/deployers/modelstudio_deployer.py +201 -40
- agentscope_runtime/engine/deployers/utils/docker_image_utils/runner_image_factory.py +9 -0
- agentscope_runtime/engine/deployers/utils/package_project_utils.py +234 -3
- agentscope_runtime/engine/deployers/utils/service_utils/fastapi_factory.py +567 -7
- agentscope_runtime/engine/deployers/utils/service_utils/standalone_main.py.j2 +211 -0
- agentscope_runtime/engine/deployers/utils/wheel_packager.py +1 -1
- agentscope_runtime/engine/helpers/helper.py +60 -41
- agentscope_runtime/engine/runner.py +40 -24
- agentscope_runtime/engine/schemas/agent_schemas.py +42 -0
- agentscope_runtime/engine/schemas/modelstudio_llm.py +14 -14
- agentscope_runtime/engine/services/sandbox_service.py +62 -70
- agentscope_runtime/engine/services/tablestore_memory_service.py +307 -0
- agentscope_runtime/engine/services/tablestore_rag_service.py +143 -0
- agentscope_runtime/engine/services/tablestore_session_history_service.py +293 -0
- agentscope_runtime/engine/services/utils/__init__.py +0 -0
- agentscope_runtime/engine/services/utils/tablestore_service_utils.py +352 -0
- agentscope_runtime/engine/tracing/__init__.py +9 -3
- agentscope_runtime/engine/tracing/asyncio_util.py +24 -0
- agentscope_runtime/engine/tracing/base.py +66 -34
- agentscope_runtime/engine/tracing/local_logging_handler.py +45 -31
- agentscope_runtime/engine/tracing/message_util.py +528 -0
- agentscope_runtime/engine/tracing/tracing_metric.py +20 -8
- agentscope_runtime/engine/tracing/tracing_util.py +130 -0
- agentscope_runtime/engine/tracing/wrapper.py +794 -169
- agentscope_runtime/sandbox/__init__.py +2 -0
- agentscope_runtime/sandbox/box/base/__init__.py +4 -0
- agentscope_runtime/sandbox/box/base/base_sandbox.py +6 -4
- agentscope_runtime/sandbox/box/browser/__init__.py +4 -0
- agentscope_runtime/sandbox/box/browser/browser_sandbox.py +10 -14
- agentscope_runtime/sandbox/box/dummy/__init__.py +4 -0
- agentscope_runtime/sandbox/box/dummy/dummy_sandbox.py +2 -1
- agentscope_runtime/sandbox/box/filesystem/__init__.py +4 -0
- agentscope_runtime/sandbox/box/filesystem/filesystem_sandbox.py +10 -7
- agentscope_runtime/sandbox/box/gui/__init__.py +4 -0
- agentscope_runtime/sandbox/box/gui/box/__init__.py +0 -0
- agentscope_runtime/sandbox/box/gui/gui_sandbox.py +81 -0
- agentscope_runtime/sandbox/box/sandbox.py +5 -2
- agentscope_runtime/sandbox/box/shared/routers/generic.py +20 -1
- agentscope_runtime/sandbox/box/training_box/__init__.py +4 -0
- agentscope_runtime/sandbox/box/training_box/training_box.py +7 -54
- agentscope_runtime/sandbox/build.py +143 -58
- agentscope_runtime/sandbox/client/http_client.py +87 -59
- agentscope_runtime/sandbox/client/training_client.py +0 -1
- agentscope_runtime/sandbox/constant.py +27 -1
- agentscope_runtime/sandbox/custom/custom_sandbox.py +7 -6
- agentscope_runtime/sandbox/custom/example.py +4 -3
- agentscope_runtime/sandbox/enums.py +1 -1
- agentscope_runtime/sandbox/manager/sandbox_manager.py +212 -106
- agentscope_runtime/sandbox/manager/server/app.py +82 -14
- agentscope_runtime/sandbox/manager/server/config.py +50 -3
- agentscope_runtime/sandbox/model/container.py +12 -23
- agentscope_runtime/sandbox/model/manager_config.py +93 -5
- agentscope_runtime/sandbox/registry.py +1 -1
- agentscope_runtime/sandbox/tools/gui/__init__.py +7 -0
- agentscope_runtime/sandbox/tools/gui/tool.py +77 -0
- agentscope_runtime/sandbox/tools/mcp_tool.py +6 -2
- agentscope_runtime/sandbox/tools/tool.py +4 -0
- agentscope_runtime/sandbox/utils.py +124 -0
- agentscope_runtime/version.py +1 -1
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/METADATA +246 -111
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/RECORD +96 -80
- agentscope_runtime/engine/agents/agentscope_agent/__init__.py +0 -6
- agentscope_runtime/engine/agents/agentscope_agent/agent.py +0 -401
- agentscope_runtime/engine/agents/agentscope_agent/hooks.py +0 -169
- agentscope_runtime/engine/agents/llm_agent.py +0 -51
- agentscope_runtime/engine/llms/__init__.py +0 -3
- agentscope_runtime/engine/llms/base_llm.py +0 -60
- agentscope_runtime/engine/llms/qwen_llm.py +0 -47
- agentscope_runtime/sandbox/manager/collections/in_memory_mapping.py +0 -22
- agentscope_runtime/sandbox/manager/collections/redis_mapping.py +0 -26
- agentscope_runtime/sandbox/manager/container_clients/__init__.py +0 -10
- agentscope_runtime/sandbox/manager/container_clients/docker_client.py +0 -422
- /agentscope_runtime/{sandbox/manager → common}/collections/__init__.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_mapping.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/base_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/in_memory_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/redis_queue.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/collections/redis_set.py +0 -0
- /agentscope_runtime/{sandbox/manager → common}/container_clients/base_client.py +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/WHEEL +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/entry_points.txt +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
import os
|
|
3
|
-
|
|
4
|
-
from openai import Client, AsyncClient
|
|
5
|
-
|
|
6
|
-
from .base_llm import BaseLLM
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class QwenLLM(BaseLLM):
|
|
10
|
-
"""
|
|
11
|
-
QwenLLM is a class that provides a wrapper around the Qwen LLM model.
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
base_url = None
|
|
15
|
-
|
|
16
|
-
def __init__(
|
|
17
|
-
self,
|
|
18
|
-
model_name: str = "qwen-turbo",
|
|
19
|
-
api_key: str = None,
|
|
20
|
-
**kwargs,
|
|
21
|
-
):
|
|
22
|
-
"""
|
|
23
|
-
Initialize the QwenLLM class.
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
model_name (str): The name of the Qwen LLM model to use.
|
|
27
|
-
Defaults to "qwen-turbo".
|
|
28
|
-
api_key (str): The API key for Qwen service.
|
|
29
|
-
If None, will read from DASHSCOPE_API_KEY environment variable.
|
|
30
|
-
"""
|
|
31
|
-
super().__init__(model_name, **kwargs)
|
|
32
|
-
|
|
33
|
-
if api_key is None:
|
|
34
|
-
api_key = os.getenv("DASHSCOPE_API_KEY")
|
|
35
|
-
if self.base_url is None:
|
|
36
|
-
default_base_url = (
|
|
37
|
-
"https://dashscope.aliyuncs.com/compatible-mode/v1"
|
|
38
|
-
)
|
|
39
|
-
self.base_url = os.getenv("DASHSCOPE_BASE_URL", default_base_url)
|
|
40
|
-
self.client = Client(
|
|
41
|
-
api_key=api_key,
|
|
42
|
-
base_url=self.base_url,
|
|
43
|
-
)
|
|
44
|
-
self.async_client = AsyncClient(
|
|
45
|
-
api_key=api_key,
|
|
46
|
-
base_url=self.base_url,
|
|
47
|
-
)
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
from .base_mapping import Mapping
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class InMemoryMapping(Mapping):
|
|
6
|
-
def __init__(self):
|
|
7
|
-
self.store = {}
|
|
8
|
-
|
|
9
|
-
def set(self, key: str, value: dict):
|
|
10
|
-
self.store[key] = value
|
|
11
|
-
|
|
12
|
-
def get(self, key: str) -> dict:
|
|
13
|
-
return self.store.get(key)
|
|
14
|
-
|
|
15
|
-
def delete(self, key: str):
|
|
16
|
-
if key in self.store:
|
|
17
|
-
del self.store[key]
|
|
18
|
-
|
|
19
|
-
def scan(self, prefix: str):
|
|
20
|
-
for key in list(self.store.keys()):
|
|
21
|
-
if key.startswith(prefix):
|
|
22
|
-
yield key
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
import json
|
|
3
|
-
|
|
4
|
-
from .base_mapping import Mapping
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class RedisMapping(Mapping):
|
|
8
|
-
def __init__(self, redis_client):
|
|
9
|
-
self.client = redis_client
|
|
10
|
-
|
|
11
|
-
def set(self, key: str, value: dict):
|
|
12
|
-
self.client.set(key, json.dumps(value))
|
|
13
|
-
|
|
14
|
-
def get(self, key: str) -> dict:
|
|
15
|
-
value = self.client.get(key)
|
|
16
|
-
return json.loads(value) if value else None
|
|
17
|
-
|
|
18
|
-
def delete(self, key: str):
|
|
19
|
-
self.client.delete(key)
|
|
20
|
-
|
|
21
|
-
def scan(self, prefix: str):
|
|
22
|
-
cursor = 0
|
|
23
|
-
while cursor != 0:
|
|
24
|
-
cursor, keys = self.client.scan(cursor=cursor, match=f"{prefix}*")
|
|
25
|
-
for key in keys:
|
|
26
|
-
yield key.decode("utf-8")
|
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
import traceback
|
|
3
|
-
import logging
|
|
4
|
-
import platform
|
|
5
|
-
import socket
|
|
6
|
-
import subprocess
|
|
7
|
-
|
|
8
|
-
import docker
|
|
9
|
-
|
|
10
|
-
from .base_client import BaseClient
|
|
11
|
-
from ..collections import RedisSetCollection, InMemorySetCollection
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
logger = logging.getLogger(__name__)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def is_port_available(port):
|
|
18
|
-
"""
|
|
19
|
-
Check if a given port is available (not in use) on the local system.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
port (int): The port number to check.
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
bool: True if the port is available, False if it is in use.
|
|
26
|
-
"""
|
|
27
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
28
|
-
try:
|
|
29
|
-
s.bind(("", port))
|
|
30
|
-
# Port is available
|
|
31
|
-
return True
|
|
32
|
-
except OSError:
|
|
33
|
-
# Port is in use
|
|
34
|
-
return False
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def sweep_port(port):
|
|
38
|
-
"""Sweep all processes found listening on a given port.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
port (int): The port number.
|
|
42
|
-
|
|
43
|
-
Returns:
|
|
44
|
-
bool: True if successful, False if failed.
|
|
45
|
-
"""
|
|
46
|
-
try:
|
|
47
|
-
system = platform.system().lower()
|
|
48
|
-
if system == "windows":
|
|
49
|
-
return _sweep_port_windows(port)
|
|
50
|
-
else:
|
|
51
|
-
return _sweep_port_unix(port)
|
|
52
|
-
except Exception as e:
|
|
53
|
-
logger.error(
|
|
54
|
-
f"An error occurred while killing processes on port {port}: {e}",
|
|
55
|
-
)
|
|
56
|
-
return False
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def _sweep_port_unix(port):
|
|
60
|
-
"""
|
|
61
|
-
Sweep all processes found listening on a given port.
|
|
62
|
-
|
|
63
|
-
Args:
|
|
64
|
-
port (int): The port number.
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
int: Number of processes swept (terminated).
|
|
68
|
-
"""
|
|
69
|
-
try:
|
|
70
|
-
# Use lsof to find the processes using the port
|
|
71
|
-
# TODO: support windows
|
|
72
|
-
result = subprocess.run(
|
|
73
|
-
["lsof", "-i", f":{port}"],
|
|
74
|
-
capture_output=True,
|
|
75
|
-
text=True,
|
|
76
|
-
check=True,
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
# Parse the output
|
|
80
|
-
lines = result.stdout.strip().split("\n")
|
|
81
|
-
if len(lines) <= 1:
|
|
82
|
-
# No process is using the port
|
|
83
|
-
return True
|
|
84
|
-
|
|
85
|
-
# Iterate over each line (excluding the header) and kill each process
|
|
86
|
-
killed_count = 0
|
|
87
|
-
for line in lines[1:]:
|
|
88
|
-
parts = line.split()
|
|
89
|
-
if len(parts) > 1:
|
|
90
|
-
pid = parts[1]
|
|
91
|
-
|
|
92
|
-
# Kill the process using the PID
|
|
93
|
-
subprocess.run(["kill", "-9", pid], check=False)
|
|
94
|
-
killed_count += 1
|
|
95
|
-
|
|
96
|
-
if not is_port_available(port):
|
|
97
|
-
logger.warning(
|
|
98
|
-
f"Port {port} is still in use after killing processes.",
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
return True
|
|
102
|
-
|
|
103
|
-
except Exception as e:
|
|
104
|
-
logger.error(
|
|
105
|
-
f"An error occurred while killing processes on port {port}: {e}",
|
|
106
|
-
)
|
|
107
|
-
return False
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def _sweep_port_windows(port):
|
|
111
|
-
"""
|
|
112
|
-
Windows implementation using netstat and taskkill
|
|
113
|
-
"""
|
|
114
|
-
try:
|
|
115
|
-
# Use netstat to find the processes using the port
|
|
116
|
-
result = subprocess.run(
|
|
117
|
-
["netstat", "-ano"],
|
|
118
|
-
capture_output=True,
|
|
119
|
-
text=True,
|
|
120
|
-
check=True,
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
# Parse the output to find processes using the specific port
|
|
124
|
-
lines = result.stdout.strip().split("\n")
|
|
125
|
-
pids_to_kill = set()
|
|
126
|
-
|
|
127
|
-
for line in lines:
|
|
128
|
-
if f":{port}" in line and "LISTENING" in line:
|
|
129
|
-
parts = line.split()
|
|
130
|
-
if len(parts) >= 5:
|
|
131
|
-
pid = parts[-1] # PID is usually the last column
|
|
132
|
-
if pid.isdigit(): # Ensure it's a valid PID
|
|
133
|
-
pids_to_kill.add(pid)
|
|
134
|
-
|
|
135
|
-
if not pids_to_kill:
|
|
136
|
-
return True
|
|
137
|
-
|
|
138
|
-
# Kill the processes
|
|
139
|
-
killed_count = 0
|
|
140
|
-
for pid in pids_to_kill:
|
|
141
|
-
try:
|
|
142
|
-
result = subprocess.run(
|
|
143
|
-
["taskkill", "/PID", pid, "/F"],
|
|
144
|
-
capture_output=True,
|
|
145
|
-
text=True,
|
|
146
|
-
check=False,
|
|
147
|
-
)
|
|
148
|
-
if result.returncode == 0:
|
|
149
|
-
killed_count += 1
|
|
150
|
-
except Exception as e:
|
|
151
|
-
logger.debug(f"Failed to kill process {pid}: {e}")
|
|
152
|
-
continue
|
|
153
|
-
|
|
154
|
-
if not is_port_available(port):
|
|
155
|
-
logger.warning(
|
|
156
|
-
f"Port {port} is still in use after killing processes.",
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
return True
|
|
160
|
-
|
|
161
|
-
except subprocess.CalledProcessError as e:
|
|
162
|
-
logger.error(f"netstat command failed: {e}")
|
|
163
|
-
return False
|
|
164
|
-
except Exception as e:
|
|
165
|
-
logger.error(f"Error in Windows port sweep: {e}")
|
|
166
|
-
return False
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
class DockerClient(BaseClient):
|
|
170
|
-
def __init__(self, config=None):
|
|
171
|
-
self.config = config
|
|
172
|
-
self.port_range = range(*self.config.port_range)
|
|
173
|
-
|
|
174
|
-
if self.config.redis_enabled:
|
|
175
|
-
import redis
|
|
176
|
-
|
|
177
|
-
redis_client = redis.Redis(
|
|
178
|
-
host=self.config.redis_server,
|
|
179
|
-
port=self.config.redis_port,
|
|
180
|
-
db=self.config.redis_db,
|
|
181
|
-
username=self.config.redis_user,
|
|
182
|
-
password=self.config.redis_password,
|
|
183
|
-
decode_responses=True,
|
|
184
|
-
)
|
|
185
|
-
try:
|
|
186
|
-
redis_client.ping()
|
|
187
|
-
except ConnectionError as e:
|
|
188
|
-
raise RuntimeError(
|
|
189
|
-
"Unable to connect to the Redis server.",
|
|
190
|
-
) from e
|
|
191
|
-
|
|
192
|
-
self.port_set = RedisSetCollection(
|
|
193
|
-
redis_client,
|
|
194
|
-
set_name=self.config.redis_port_key,
|
|
195
|
-
)
|
|
196
|
-
else:
|
|
197
|
-
self.port_set = InMemorySetCollection()
|
|
198
|
-
|
|
199
|
-
try:
|
|
200
|
-
self.client = docker.from_env()
|
|
201
|
-
except Exception as e:
|
|
202
|
-
raise RuntimeError(
|
|
203
|
-
f"Docker client initialization failed: {str(e)}\n"
|
|
204
|
-
"Solutions:\n"
|
|
205
|
-
"• Ensure Docker is running\n"
|
|
206
|
-
"• Check Docker permissions\n"
|
|
207
|
-
"• For Colima: "
|
|
208
|
-
"export DOCKER_HOST=unix://$HOME/.colima/docker.sock",
|
|
209
|
-
) from e
|
|
210
|
-
|
|
211
|
-
def _try_pull_from_acr(self, image):
|
|
212
|
-
"""
|
|
213
|
-
Attempt to pull the image from the Alibaba Cloud Container Registry
|
|
214
|
-
(ACR) and retag it.
|
|
215
|
-
"""
|
|
216
|
-
try:
|
|
217
|
-
acr_registry = "agentscope-registry.ap-southeast-1.cr.aliyuncs.com"
|
|
218
|
-
acr_image = f"{acr_registry}/{image}"
|
|
219
|
-
|
|
220
|
-
logger.info(
|
|
221
|
-
f"Attempting to pull from ACR: {acr_image}, it might take "
|
|
222
|
-
f"several minutes.",
|
|
223
|
-
)
|
|
224
|
-
self.client.images.pull(acr_image)
|
|
225
|
-
logger.info(f"Successfully pulled image from ACR: {acr_image}")
|
|
226
|
-
|
|
227
|
-
# Retag the image
|
|
228
|
-
acr_img_obj = self.client.images.get(acr_image)
|
|
229
|
-
acr_img_obj.tag(image)
|
|
230
|
-
logger.debug(f"Successfully tagged image as: {image}")
|
|
231
|
-
|
|
232
|
-
# Optionally remove the original tag to save space
|
|
233
|
-
try:
|
|
234
|
-
self.client.images.remove(acr_image)
|
|
235
|
-
logger.debug(f"Removed original tag: {acr_image}")
|
|
236
|
-
except Exception as e:
|
|
237
|
-
logger.debug(f"Failed to remove original tag: {e}")
|
|
238
|
-
return True
|
|
239
|
-
except Exception as e:
|
|
240
|
-
logger.error(
|
|
241
|
-
f"Failed to pull from ACR: {e}, {traceback.format_exc()}",
|
|
242
|
-
)
|
|
243
|
-
return False
|
|
244
|
-
|
|
245
|
-
def create(
|
|
246
|
-
self,
|
|
247
|
-
image,
|
|
248
|
-
name=None,
|
|
249
|
-
ports=None,
|
|
250
|
-
volumes=None,
|
|
251
|
-
environment=None,
|
|
252
|
-
runtime_config=None,
|
|
253
|
-
):
|
|
254
|
-
"""Create a new Docker container."""
|
|
255
|
-
if runtime_config is None:
|
|
256
|
-
runtime_config = {}
|
|
257
|
-
|
|
258
|
-
port_mapping = {}
|
|
259
|
-
|
|
260
|
-
if ports:
|
|
261
|
-
free_port = self._find_free_ports(len(ports))
|
|
262
|
-
for container_port, host_port in zip(ports, free_port):
|
|
263
|
-
port_mapping[container_port] = host_port
|
|
264
|
-
|
|
265
|
-
try:
|
|
266
|
-
try:
|
|
267
|
-
# Check if the image exists locally
|
|
268
|
-
self.client.images.get(image)
|
|
269
|
-
logger.debug(f"Image '{image}' found locally.")
|
|
270
|
-
except docker.errors.ImageNotFound:
|
|
271
|
-
logger.info(
|
|
272
|
-
f"Image '{image}' not found locally. "
|
|
273
|
-
f"Attempting to pull it...",
|
|
274
|
-
)
|
|
275
|
-
try:
|
|
276
|
-
logger.info(
|
|
277
|
-
f"Attempting to pull: {image}, "
|
|
278
|
-
f"it might take several minutes.",
|
|
279
|
-
)
|
|
280
|
-
self.client.images.pull(image)
|
|
281
|
-
logger.debug(
|
|
282
|
-
f"Image '{image}' successfully pulled from default "
|
|
283
|
-
f"registry.",
|
|
284
|
-
)
|
|
285
|
-
pull_success = True
|
|
286
|
-
except docker.errors.APIError as e:
|
|
287
|
-
logger.warning(
|
|
288
|
-
f"Failed to pull from default registry: {e}",
|
|
289
|
-
)
|
|
290
|
-
logger.warning("Trying to pull from ACR fallback...")
|
|
291
|
-
|
|
292
|
-
pull_success = self._try_pull_from_acr(image)
|
|
293
|
-
|
|
294
|
-
if not pull_success:
|
|
295
|
-
logger.error(
|
|
296
|
-
f"Failed to pull image '{image}' from both "
|
|
297
|
-
f"default and ACR",
|
|
298
|
-
)
|
|
299
|
-
return None, None, None
|
|
300
|
-
|
|
301
|
-
except docker.errors.APIError as e:
|
|
302
|
-
logger.error(f"Error occurred while checking the image: {e}")
|
|
303
|
-
return None, None, None
|
|
304
|
-
|
|
305
|
-
# Create and run the container
|
|
306
|
-
container = self.client.containers.run(
|
|
307
|
-
image,
|
|
308
|
-
detach=True,
|
|
309
|
-
ports=port_mapping,
|
|
310
|
-
name=name,
|
|
311
|
-
volumes=volumes,
|
|
312
|
-
environment=environment,
|
|
313
|
-
**runtime_config,
|
|
314
|
-
)
|
|
315
|
-
container.reload()
|
|
316
|
-
_id = container.id
|
|
317
|
-
return _id, list(port_mapping.values()), "localhost"
|
|
318
|
-
except Exception as e:
|
|
319
|
-
logger.error(f"An error occurred: {e}, {traceback.format_exc()}")
|
|
320
|
-
return None, None, None
|
|
321
|
-
|
|
322
|
-
def start(self, container_id):
|
|
323
|
-
"""Start a Docker container."""
|
|
324
|
-
try:
|
|
325
|
-
container = self.client.containers.get(
|
|
326
|
-
container_id,
|
|
327
|
-
)
|
|
328
|
-
|
|
329
|
-
# Check whether the ports are occupied by other processes
|
|
330
|
-
port_mapping = container.attrs["NetworkSettings"]["Ports"]
|
|
331
|
-
for _, mappings in port_mapping.items():
|
|
332
|
-
if mappings is not None:
|
|
333
|
-
for mapping in mappings:
|
|
334
|
-
host_port = int(mapping["HostPort"])
|
|
335
|
-
if is_port_available(host_port):
|
|
336
|
-
continue
|
|
337
|
-
sweep_port(host_port["HostPort"])
|
|
338
|
-
|
|
339
|
-
container.start()
|
|
340
|
-
return True
|
|
341
|
-
except Exception as e:
|
|
342
|
-
logger.error(f"An error occurred: {e}, {traceback.format_exc()}")
|
|
343
|
-
return False
|
|
344
|
-
|
|
345
|
-
def stop(self, container_id, timeout=None):
|
|
346
|
-
"""Stop a Docker container."""
|
|
347
|
-
try:
|
|
348
|
-
container = self.client.containers.get(
|
|
349
|
-
container_id,
|
|
350
|
-
)
|
|
351
|
-
container.stop(timeout=timeout)
|
|
352
|
-
return True
|
|
353
|
-
except Exception as e:
|
|
354
|
-
logger.error(f"An error occurred: {e}, {traceback.format_exc()}")
|
|
355
|
-
return False
|
|
356
|
-
|
|
357
|
-
def remove(self, container_id, force=False):
|
|
358
|
-
"""Remove a Docker container."""
|
|
359
|
-
try:
|
|
360
|
-
container = self.client.containers.get(
|
|
361
|
-
container_id,
|
|
362
|
-
)
|
|
363
|
-
# Remove ports
|
|
364
|
-
port_mapping = container.attrs["NetworkSettings"]["Ports"]
|
|
365
|
-
container.remove(force=force)
|
|
366
|
-
|
|
367
|
-
# Iterate over each port and its mappings
|
|
368
|
-
for _, mappings in port_mapping.items():
|
|
369
|
-
if mappings is not None:
|
|
370
|
-
for mapping in mappings:
|
|
371
|
-
host_port = int(mapping["HostPort"])
|
|
372
|
-
self.port_set.remove(host_port)
|
|
373
|
-
|
|
374
|
-
return True
|
|
375
|
-
except Exception as e:
|
|
376
|
-
logger.error(f"An error occurred: {e}, {traceback.format_exc()}")
|
|
377
|
-
return False
|
|
378
|
-
|
|
379
|
-
def inspect(self, container_id):
|
|
380
|
-
"""Inspect a Docker container."""
|
|
381
|
-
try:
|
|
382
|
-
# Get the container object
|
|
383
|
-
container = self.client.containers.get(container_id)
|
|
384
|
-
# Access the detailed information
|
|
385
|
-
return container.attrs
|
|
386
|
-
except Exception:
|
|
387
|
-
return None
|
|
388
|
-
|
|
389
|
-
def get_status(self, container_id):
|
|
390
|
-
"""Get the current status of the specified container."""
|
|
391
|
-
container_attrs = self.inspect(container_id=container_id)
|
|
392
|
-
if container_attrs:
|
|
393
|
-
return container_attrs["State"]["Status"]
|
|
394
|
-
return None
|
|
395
|
-
|
|
396
|
-
def _find_free_ports(self, n):
|
|
397
|
-
free_ports = []
|
|
398
|
-
|
|
399
|
-
for port in self.port_range:
|
|
400
|
-
if len(free_ports) >= n:
|
|
401
|
-
break # We have found enough ports
|
|
402
|
-
|
|
403
|
-
if not self.port_set.add(port):
|
|
404
|
-
continue
|
|
405
|
-
|
|
406
|
-
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
407
|
-
try:
|
|
408
|
-
s.bind(("", port))
|
|
409
|
-
free_ports.append(port) # Port is available
|
|
410
|
-
|
|
411
|
-
except OSError:
|
|
412
|
-
# Bind failed, port is in use
|
|
413
|
-
self.port_set.remove(port)
|
|
414
|
-
# Try the next one
|
|
415
|
-
continue
|
|
416
|
-
|
|
417
|
-
if len(free_ports) < n:
|
|
418
|
-
raise RuntimeError(
|
|
419
|
-
"Not enough free ports available in the specified range.",
|
|
420
|
-
)
|
|
421
|
-
|
|
422
|
-
return free_ports
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{agentscope_runtime-0.1.5b2.dist-info → agentscope_runtime-0.2.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|