shared-tensor 0.2.12__tar.gz → 0.2.13__tar.gz
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.
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/PKG-INFO +1 -1
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/pyproject.toml +1 -1
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/__init__.py +1 -1
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/async_client.py +1 -1
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/client.py +34 -6
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/provider.py +1 -1
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/utils.py +84 -36
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/LICENSE +0 -0
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/MANIFEST.in +0 -0
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/README.md +0 -0
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/setup.cfg +0 -0
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/async_task.py +0 -0
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/errors.py +0 -0
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/managed_object.py +0 -0
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/runtime.py +0 -0
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/server.py +0 -0
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor/transport.py +0 -0
- {shared_tensor-0.2.12 → shared_tensor-0.2.13}/shared_tensor.egg-info/SOURCES.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shared-tensor
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.13
|
|
4
4
|
Summary: Native PyTorch CUDA IPC over Unix Domain Socket for same-host process separation
|
|
5
5
|
Author-email: Athena Team <contact@world-sim-dev.org>
|
|
6
6
|
Maintainer-email: Athena Team <contact@world-sim-dev.org>
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
+
import errno
|
|
6
7
|
import socket
|
|
8
|
+
import time
|
|
7
9
|
from dataclasses import dataclass
|
|
8
10
|
from typing import Any, cast
|
|
9
11
|
|
|
@@ -32,6 +34,9 @@ from shared_tensor.utils import (
|
|
|
32
34
|
|
|
33
35
|
logger = logging.getLogger(__name__)
|
|
34
36
|
|
|
37
|
+
CONNECT_RETRY_TIMEOUT_SECONDS = 60.0
|
|
38
|
+
CONNECT_RETRY_INTERVAL_SECONDS = 0.5
|
|
39
|
+
|
|
35
40
|
|
|
36
41
|
@dataclass(slots=True)
|
|
37
42
|
class _ClientReleaser(ReleaseHandle):
|
|
@@ -53,7 +58,7 @@ class SharedTensorClient:
|
|
|
53
58
|
base_path: str = "/tmp/shared-tensor",
|
|
54
59
|
*,
|
|
55
60
|
device_index: int | None = None,
|
|
56
|
-
timeout: float =
|
|
61
|
+
timeout: float = 600.0,
|
|
57
62
|
verbose_debug: bool = False,
|
|
58
63
|
) -> None:
|
|
59
64
|
self.base_path = base_path
|
|
@@ -117,12 +122,35 @@ class SharedTensorClient:
|
|
|
117
122
|
method = request.get("method", "<unknown>")
|
|
118
123
|
if self.verbose_debug:
|
|
119
124
|
logger.debug("Client sending request", extra={"method": method, "socket_path": self.socket_path})
|
|
125
|
+
started_at = time.monotonic()
|
|
120
126
|
try:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
127
|
+
while True:
|
|
128
|
+
try:
|
|
129
|
+
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
|
|
130
|
+
sock.settimeout(self.timeout)
|
|
131
|
+
sock.connect(self.socket_path)
|
|
132
|
+
send_message(sock, request)
|
|
133
|
+
response = recv_message(sock)
|
|
134
|
+
break
|
|
135
|
+
except OSError as exc:
|
|
136
|
+
retryable = exc.errno in {
|
|
137
|
+
errno.ENOENT,
|
|
138
|
+
errno.ECONNREFUSED,
|
|
139
|
+
}
|
|
140
|
+
elapsed = time.monotonic() - started_at
|
|
141
|
+
if not retryable or elapsed >= CONNECT_RETRY_TIMEOUT_SECONDS:
|
|
142
|
+
raise
|
|
143
|
+
if self.verbose_debug:
|
|
144
|
+
logger.info(
|
|
145
|
+
"Client retrying request connection",
|
|
146
|
+
extra={
|
|
147
|
+
"method": method,
|
|
148
|
+
"socket_path": self.socket_path,
|
|
149
|
+
"elapsed": round(elapsed, 3),
|
|
150
|
+
"error": str(exc),
|
|
151
|
+
},
|
|
152
|
+
)
|
|
153
|
+
time.sleep(CONNECT_RETRY_INTERVAL_SECONDS)
|
|
126
154
|
except TimeoutError as exc:
|
|
127
155
|
if self.verbose_debug:
|
|
128
156
|
logger.warning("Client request timed out", extra={"method": method, "socket_path": self.socket_path})
|
|
@@ -91,7 +91,7 @@ class SharedTensorProvider:
|
|
|
91
91
|
*,
|
|
92
92
|
enabled: bool | None = None,
|
|
93
93
|
device_index: int | None = None,
|
|
94
|
-
timeout: float =
|
|
94
|
+
timeout: float = 600.0,
|
|
95
95
|
execution_mode: str = "auto",
|
|
96
96
|
server_startup_timeout: float = 30.0,
|
|
97
97
|
verbose_debug: bool = False,
|
|
@@ -9,6 +9,8 @@ import io
|
|
|
9
9
|
import multiprocessing.reduction as mp_reduction
|
|
10
10
|
import os
|
|
11
11
|
import pickle
|
|
12
|
+
import sys
|
|
13
|
+
import tempfile
|
|
12
14
|
from pathlib import Path
|
|
13
15
|
from collections.abc import Callable
|
|
14
16
|
from typing import Any, cast
|
|
@@ -174,16 +176,10 @@ def _serialize_transformers_model(module: Any) -> bytes:
|
|
|
174
176
|
config = getattr(module, "config", None)
|
|
175
177
|
if config is None:
|
|
176
178
|
raise SharedTensorSerializationError("transformers model is missing a config")
|
|
177
|
-
|
|
178
|
-
raise SharedTensorSerializationError("transformers model config must provide to_dict()")
|
|
179
|
+
module_sources = _collect_transformers_module_sources(type(module).__module__, type(config).__module__)
|
|
179
180
|
payload = {
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"config_module": type(config).__module__,
|
|
183
|
-
"config_name": type(config).__name__,
|
|
184
|
-
"config_dict": config.to_dict(),
|
|
185
|
-
"training": bool(module.training),
|
|
186
|
-
"state_bytes": _torch_serialize(module.state_dict()),
|
|
181
|
+
"module_sources": module_sources,
|
|
182
|
+
"module_bytes": _torch_serialize(module),
|
|
187
183
|
}
|
|
188
184
|
return pickle.dumps(payload, protocol=pickle.HIGHEST_PROTOCOL)
|
|
189
185
|
|
|
@@ -240,37 +236,89 @@ def serialize_call_payloads(
|
|
|
240
236
|
raise SharedTensorSerializationError(f"Failed to serialize call payloads: {exc}") from exc
|
|
241
237
|
|
|
242
238
|
|
|
243
|
-
def
|
|
244
|
-
|
|
245
|
-
if
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
239
|
+
def _bundle_module_files(module_name: str) -> dict[str, str] | None:
|
|
240
|
+
module = sys.modules.get(module_name)
|
|
241
|
+
if module is None:
|
|
242
|
+
module = importlib.import_module(module_name)
|
|
243
|
+
module_file = getattr(module, "__file__", None)
|
|
244
|
+
if not module_file:
|
|
245
|
+
return None
|
|
246
|
+
module_path = Path(module_file).resolve()
|
|
247
|
+
if module_path.suffix != ".py":
|
|
248
|
+
return None
|
|
249
|
+
transformers_root = None
|
|
250
|
+
if TRANSFORMERS_PRETRAINED_MODEL is not None:
|
|
251
|
+
transformers_file = getattr(sys.modules.get("transformers"), "__file__", None)
|
|
252
|
+
if transformers_file is not None:
|
|
253
|
+
transformers_root = Path(transformers_file).resolve().parent
|
|
254
|
+
if transformers_root is not None:
|
|
255
|
+
try:
|
|
256
|
+
module_path.relative_to(transformers_root)
|
|
257
|
+
if not module_name.startswith("transformers_modules."):
|
|
258
|
+
return None
|
|
259
|
+
except ValueError:
|
|
260
|
+
pass
|
|
261
|
+
files = {module_path.name: module_path.read_text(encoding="utf-8")}
|
|
262
|
+
get_relative_import_files = getattr(importlib.import_module("transformers.dynamic_module_utils"), "get_relative_import_files", None)
|
|
263
|
+
if callable(get_relative_import_files):
|
|
264
|
+
for needed in get_relative_import_files(str(module_path)):
|
|
265
|
+
needed_path = Path(needed).resolve()
|
|
266
|
+
files[needed_path.name] = needed_path.read_text(encoding="utf-8")
|
|
267
|
+
return files
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _collect_transformers_module_sources(*module_names: str) -> dict[str, dict[str, str]]:
|
|
271
|
+
bundles: dict[str, dict[str, str]] = {}
|
|
272
|
+
for module_name in module_names:
|
|
273
|
+
bundle = _bundle_module_files(module_name)
|
|
274
|
+
if bundle:
|
|
275
|
+
bundles[module_name] = bundle
|
|
276
|
+
return bundles
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _stage_transformers_module_sources(module_sources: dict[str, dict[str, str]]) -> None:
|
|
280
|
+
if not module_sources:
|
|
281
|
+
return
|
|
282
|
+
staging_root = Path(tempfile.gettempdir()) / "shared-tensor-transformers-modules"
|
|
283
|
+
staging_root.mkdir(parents=True, exist_ok=True)
|
|
284
|
+
root_str = str(staging_root)
|
|
285
|
+
if root_str not in sys.path:
|
|
286
|
+
sys.path.insert(0, root_str)
|
|
287
|
+
for module_name, files in module_sources.items():
|
|
288
|
+
parts = module_name.split(".")
|
|
289
|
+
package_dir = staging_root.joinpath(*parts[:-1])
|
|
290
|
+
package_dir.mkdir(parents=True, exist_ok=True)
|
|
291
|
+
current = staging_root
|
|
292
|
+
for part in parts[:-1]:
|
|
293
|
+
current = current / part
|
|
294
|
+
init_file = current / "__init__.py"
|
|
295
|
+
if not init_file.exists():
|
|
296
|
+
init_file.write_text("", encoding="utf-8")
|
|
297
|
+
for filename, content in files.items():
|
|
298
|
+
(package_dir / filename).write_text(content, encoding="utf-8")
|
|
299
|
+
importlib.invalidate_caches()
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def _import_transformers_module(module_name: str, module_sources: dict[str, dict[str, str]]) -> Any:
|
|
303
|
+
try:
|
|
304
|
+
return importlib.import_module(module_name)
|
|
305
|
+
except ModuleNotFoundError:
|
|
306
|
+
_stage_transformers_module_sources(module_sources)
|
|
307
|
+
return importlib.import_module(module_name)
|
|
255
308
|
|
|
256
309
|
|
|
257
310
|
def _deserialize_transformers_model(payload: bytes) -> Any:
|
|
258
311
|
encoded = pickle.loads(payload)
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
first_tensor = next(iter(state_dict.values()), None)
|
|
270
|
-
if TORCH_MODULE is not None and isinstance(first_tensor, TORCH_MODULE.Tensor):
|
|
271
|
-
model = model.to(first_tensor.device)
|
|
272
|
-
_load_module_state_dict(model, state_dict)
|
|
273
|
-
model.train(bool(encoded["training"]))
|
|
312
|
+
module_sources = cast(dict[str, dict[str, str]], encoded.get("module_sources", {}))
|
|
313
|
+
for module_name in module_sources:
|
|
314
|
+
_import_transformers_module(module_name, module_sources)
|
|
315
|
+
module_bytes = encoded.get("module_bytes")
|
|
316
|
+
if not isinstance(module_bytes, (bytes, bytearray)):
|
|
317
|
+
raise SharedTensorSerializationError("transformers module payload is invalid")
|
|
318
|
+
model = pickle.loads(module_bytes)
|
|
319
|
+
eval_fn = getattr(model, "eval", None)
|
|
320
|
+
if callable(eval_fn):
|
|
321
|
+
eval_fn()
|
|
274
322
|
_validate_module_device(model)
|
|
275
323
|
return model
|
|
276
324
|
|
|
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
|
|
File without changes
|