weftcodec 1.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.
weftcodec/dcp.py ADDED
@@ -0,0 +1,2108 @@
1
+ """PyTorch Distributed Checkpoint (DCP) storage adapter over WeftFS.
2
+
3
+ ``weftcodec.dcp`` implements the shape of PyTorch's
4
+ :class:`torch.distributed.checkpoint.StorageWriter` and ``StorageReader``
5
+ interfaces so that ``torch.distributed.checkpoint.save`` / ``load`` can write
6
+ and read a checkpoint through a WeftFS store, with the training process never
7
+ needing to mount anything.
8
+
9
+ Everything durable goes through the shipped Rust CLI:
10
+
11
+ * tensor bytes become one canonical ``weft.checkpoint.v1`` checkpoint written by
12
+ ``weftfs checkpoint write`` (one mode-10 shard object per tensor slice, or an
13
+ exact ``zstd``/``raw`` shard when the caller asks for it);
14
+ * the DCP protocol metadata (state-dict schema, storage keys, planner data) is
15
+ serialised as canonical WCB and stored as an exact store file object at
16
+ ``/checkpoints/<storage-name>/dcp.metadata``;
17
+ * reads use ``weftfs checkpoint restore`` and re-verify every recorded BLAKE3
18
+ content root before handing bytes to DCP. The default restore is read-only in
19
+ the store; ``import_restored=True`` opts into the CLI's ``--import``, which
20
+ publishes a second exact copy under ``/dcp-reads/<uuid>/``.
21
+
22
+ There is no approximate path and no silent fallback. A missing CLI, a missing
23
+ store, a non-canonical manifest, a content-root mismatch, or an unsupported
24
+ dtype raises a typed error.
25
+
26
+ ``torch`` is optional. Importing :mod:`weftcodec.dcp` without PyTorch works and
27
+ exposes the same classes/methods (contract mode); operations that need tensors
28
+ raise :class:`TorchRequired`. When PyTorch is importable the classes subclass the
29
+ real DCP interfaces.
30
+
31
+ Not claimed: GPU-direct I/O, a multi-rank cluster run, or stock DCP binaries.
32
+ The adapters do not need a read-only store: ``checkpoint restore`` only reads
33
+ unless ``--import`` is requested.
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ import ctypes
39
+ import importlib
40
+ import io
41
+ import json
42
+ import math
43
+ import os
44
+ import shutil
45
+ import struct
46
+ import subprocess
47
+ import tempfile
48
+ import threading
49
+ import time
50
+ import uuid
51
+ from concurrent.futures import ThreadPoolExecutor
52
+ from dataclasses import dataclass
53
+ from pathlib import Path
54
+
55
+ from . import wcb
56
+ from .hashing import content_root
57
+ from .service import (
58
+ CheckpointService,
59
+ ServiceError,
60
+ ServiceProtocolError,
61
+ ServiceUnavailable,
62
+ canonical_element_bytes,
63
+ )
64
+
65
+ __all__ = [
66
+ "AsyncSaveHandle",
67
+ "DCP_METADATA_NAME",
68
+ "DCP_METADATA_SCHEMA",
69
+ "DcpError",
70
+ "ExactnessError",
71
+ "InvalidCheckpointId",
72
+ "InvalidMetadata",
73
+ "MetadataMissing",
74
+ "RestoredEntry",
75
+ "SaveFailed",
76
+ "StagingLimitExceeded",
77
+ "StoreCommandError",
78
+ "StoreUnavailable",
79
+ "TORCH_AVAILABLE",
80
+ "TORCH_VERSION",
81
+ "TorchRequired",
82
+ "UnsupportedDtype",
83
+ "UnsupportedStateObject",
84
+ "WeftStorageReader",
85
+ "WeftStorageWriter",
86
+ "init_store",
87
+ "save_checkpoint_async",
88
+ "verify_checkpoint",
89
+ ]
90
+
91
+
92
+ DCP_METADATA_SCHEMA = "weft.dcp.metadata.v1"
93
+ DCP_METADATA_NAME = "dcp.metadata"
94
+ DEFAULT_STAGING_BYTES = 256 * 1024 * 1024
95
+ DEFAULT_TIMEOUT_SECONDS = 1800.0
96
+ DEFAULT_CLI = "/work/.docker-cache/target-rust/release/weftfs"
97
+
98
+ #: torch dtype spelling -> (canonical WEFT-TENSOR dtype, safetensors spelling,
99
+ #: element bytes). Only these dtypes have an exact Rust safetensors mapping.
100
+ _SAFETENSORS_BY_TORCH = {
101
+ "torch.float16": ("f16", "F16", 2),
102
+ "torch.bfloat16": ("bf16", "BF16", 2),
103
+ "torch.float32": ("f32", "F32", 4),
104
+ "torch.float64": ("f64", "F64", 8),
105
+ "torch.int8": ("i8", "I8", 1),
106
+ "torch.uint8": ("u8", "U8", 1),
107
+ "torch.int16": ("i16", "I16", 2),
108
+ "torch.int32": ("i32", "I32", 4),
109
+ "torch.int64": ("i64", "I64", 8),
110
+ }
111
+ _CANONICAL_TO_TORCH = {
112
+ canonical: torch_name
113
+ for torch_name, (canonical, _sf, _size) in _SAFETENSORS_BY_TORCH.items()
114
+ }
115
+
116
+
117
+ class DcpError(RuntimeError):
118
+ """Base class for every adapter error."""
119
+
120
+
121
+ class StoreUnavailable(DcpError):
122
+ """The WeftFS CLI or the store it names is not usable."""
123
+
124
+
125
+ class StoreCommandError(DcpError):
126
+ """A WeftFS CLI invocation failed."""
127
+
128
+ def __init__(self, message, *, command, returncode, stderr, error_class=None):
129
+ super().__init__(message)
130
+ self.command = tuple(command)
131
+ self.returncode = returncode
132
+ self.stderr = stderr
133
+ self.error_class = error_class
134
+
135
+
136
+ class InvalidCheckpointId(DcpError):
137
+ """The requested checkpoint id is not a legal single store name."""
138
+
139
+
140
+ class UnsupportedDtype(DcpError):
141
+ """A tensor dtype has no exact safetensors/WEFT-TENSOR v1 mapping."""
142
+
143
+
144
+ class UnsupportedStateObject(DcpError):
145
+ """The state dict contains an object this adapter does not persist."""
146
+
147
+
148
+ class StagingLimitExceeded(DcpError):
149
+ """A single resolved tensor payment does not fit the staging bound."""
150
+
151
+
152
+ class MetadataMissing(DcpError):
153
+ """No DCP metadata object and manifest synthesis is disabled."""
154
+
155
+
156
+ class InvalidMetadata(DcpError):
157
+ """The stored DCP metadata is malformed or refers to unknown entries."""
158
+
159
+
160
+ class ExactnessError(DcpError):
161
+ """A stored content root disagrees with the bytes/manifests."""
162
+
163
+
164
+ class TorchRequired(DcpError):
165
+ """This operation needs PyTorch but it is not installed."""
166
+
167
+
168
+ class SaveFailed(DcpError):
169
+ """A save did not produce a usable checkpoint."""
170
+
171
+
172
+ # ---------------------------------------------------------------------------
173
+ # Optional PyTorch. Importing ``weftcodec`` never imports this module.
174
+
175
+
176
+ def _load_torch():
177
+ try:
178
+ return importlib.import_module("torch")
179
+ except ImportError:
180
+ return None
181
+
182
+
183
+ _TORCH = _load_torch()
184
+ TORCH_AVAILABLE = _TORCH is not None
185
+ TORCH_VERSION = getattr(_TORCH, "__version__", None) if _TORCH is not None else None
186
+
187
+ if TORCH_AVAILABLE: # pragma: no branch - both paths are exercised by the court
188
+ try:
189
+ from torch.distributed.checkpoint.metadata import (
190
+ BytesStorageMetadata as _BytesStorageMetadata,
191
+ )
192
+ from torch.distributed.checkpoint.metadata import (
193
+ ChunkStorageMetadata as _ChunkStorageMetadata,
194
+ )
195
+ from torch.distributed.checkpoint.metadata import (
196
+ Metadata as _TorchMetadata,
197
+ )
198
+ from torch.distributed.checkpoint.metadata import (
199
+ MetadataIndex as _MetadataIndex,
200
+ )
201
+ from torch.distributed.checkpoint.metadata import (
202
+ StorageMeta as _TorchStorageMeta,
203
+ )
204
+ from torch.distributed.checkpoint.metadata import (
205
+ TensorProperties as _TensorProperties,
206
+ )
207
+ from torch.distributed.checkpoint.metadata import (
208
+ TensorStorageMetadata as _TensorStorageMetadata,
209
+ )
210
+ from torch.distributed.checkpoint.storage import (
211
+ StorageReader as _StorageReaderBase,
212
+ )
213
+ from torch.distributed.checkpoint.storage import (
214
+ StorageWriter as _StorageWriterBase,
215
+ )
216
+ from torch.futures import Future as _TorchFuture
217
+
218
+ _TORCH_IMPORT_ERROR = None
219
+ except ImportError as exc: # pragma: no cover - defensive for odd builds
220
+ TORCH_AVAILABLE = False
221
+ TORCH_VERSION = None
222
+ _TORCH = None
223
+ _TORCH_IMPORT_ERROR = str(exc)
224
+ else: # pragma: no cover - the court always has torch
225
+ _TORCH_IMPORT_ERROR = "torch is not installed"
226
+
227
+ if not TORCH_AVAILABLE:
228
+
229
+ class _StorageWriterBase: # type: ignore[no-redef]
230
+ """Contract-mode stand-in when PyTorch is absent."""
231
+
232
+ class _StorageReaderBase: # type: ignore[no-redef]
233
+ """Contract-mode stand-in when PyTorch is absent."""
234
+
235
+ class _TorchFuture: # type: ignore[no-redef]
236
+ """Minimal future with the subset of ``torch.futures.Future`` used here."""
237
+
238
+ def __init__(self):
239
+ self._ready = threading.Event()
240
+ self._value = None
241
+ self._error = None
242
+
243
+ def set_result(self, value):
244
+ self._value = value
245
+ self._ready.set()
246
+
247
+ def set_exception(self, error):
248
+ self._error = error
249
+ self._ready.set()
250
+
251
+ def wait(self):
252
+ self._ready.wait()
253
+ return self
254
+
255
+ def value(self):
256
+ self._ready.wait()
257
+ if self._error is not None:
258
+ raise self._error
259
+ return self._value
260
+
261
+ def done(self):
262
+ return self._ready.is_set()
263
+
264
+ def add_done_callback(self, callback):
265
+ self._ready.wait()
266
+ callback(self)
267
+
268
+ @dataclass(frozen=True)
269
+ class _FallbackStorageMeta: # type: ignore[no-redef]
270
+ checkpoint_id: str = None
271
+ save_id: str = None
272
+ load_id: str = None
273
+ modules: tuple = ()
274
+
275
+ _TorchStorageMeta = _FallbackStorageMeta
276
+ _MetadataIndex = None
277
+ _TorchMetadata = None
278
+ _TensorStorageMetadata = None
279
+ _BytesStorageMetadata = None
280
+ _ChunkStorageMetadata = None
281
+ _TensorProperties = None
282
+
283
+
284
+ def _require_torch(operation):
285
+ if not TORCH_AVAILABLE:
286
+ raise TorchRequired(
287
+ "%s requires PyTorch (install with `pip install weftcodec[torch]`); "
288
+ "weftcodec.dcp imported in contract mode: %s"
289
+ % (operation, _TORCH_IMPORT_ERROR)
290
+ )
291
+
292
+
293
+ # ---------------------------------------------------------------------------
294
+ # Small canonical helpers
295
+
296
+
297
+ def _u(value):
298
+ return wcb.U64(int(value))
299
+
300
+
301
+ def _i(value):
302
+ if isinstance(value, wcb.U64):
303
+ return int(value.value)
304
+ if isinstance(value, wcb.I64):
305
+ return int(value.value)
306
+ return int(value)
307
+
308
+
309
+ def _u_list(values):
310
+ return [_u(value) for value in values]
311
+
312
+
313
+ def _i_list(values):
314
+ return tuple(_i(value) for value in values)
315
+
316
+
317
+ def _dtype_name(dtype) -> str:
318
+ """Canonical torch dtype spelling, e.g. ``torch.float32``."""
319
+ text = str(dtype)
320
+ if not text.startswith("torch."):
321
+ raise UnsupportedDtype("unrecognised torch dtype %r" % (dtype,))
322
+ return text
323
+
324
+
325
+ def _canonical_dtype(torch_dtype_name: str) -> str:
326
+ try:
327
+ return _SAFETENSORS_BY_TORCH[torch_dtype_name][0]
328
+ except KeyError:
329
+ raise UnsupportedDtype(
330
+ "dtype %s has no exact WEFT-TENSOR/safetensors v1 mapping; "
331
+ "supported: %s"
332
+ % (torch_dtype_name, ", ".join(sorted(_SAFETENSORS_BY_TORCH)))
333
+ )
334
+
335
+
336
+ def _safetensors_dtype(torch_dtype_name: str) -> str:
337
+ try:
338
+ return _SAFETENSORS_BY_TORCH[torch_dtype_name][1]
339
+ except KeyError:
340
+ raise UnsupportedDtype(
341
+ "dtype %s has no exact safetensors mapping; supported: %s"
342
+ % (torch_dtype_name, ", ".join(sorted(_SAFETENSORS_BY_TORCH)))
343
+ )
344
+
345
+
346
+ def _element_bytes(torch_dtype_name: str) -> int:
347
+ try:
348
+ return _SAFETENSORS_BY_TORCH[torch_dtype_name][2]
349
+ except KeyError:
350
+ raise UnsupportedDtype("dtype %s has no element size" % torch_dtype_name)
351
+
352
+
353
+ def _torch_dtype(torch_dtype_name: str):
354
+ _require_torch("resolving a tensor dtype")
355
+ try:
356
+ return getattr(_TORCH, torch_dtype_name.split(".", 1)[1])
357
+ except AttributeError:
358
+ raise UnsupportedDtype("this PyTorch has no dtype %s" % torch_dtype_name)
359
+
360
+
361
+ def _validate_checkpoint_id(checkpoint_id) -> str:
362
+ if checkpoint_id is None:
363
+ raise InvalidCheckpointId("checkpoint id is required")
364
+ text = os.fspath(checkpoint_id)
365
+ if not isinstance(text, str) or not text:
366
+ raise InvalidCheckpointId("checkpoint id must be a non-empty path name")
367
+ if "/" in text or "\\" in text or text in (".", "..") or "\x00" in text:
368
+ raise InvalidCheckpointId(
369
+ "checkpoint id %r must be a single store name; the store directory is "
370
+ "selected with store_root" % text
371
+ )
372
+ return text
373
+
374
+
375
+ def _generation(name: str, base: str):
376
+ if name == base:
377
+ return 1
378
+ prefix = base + ".v"
379
+ if name.startswith(prefix):
380
+ suffix = name[len(prefix) :]
381
+ if suffix.isdigit() and str(int(suffix)) == suffix and int(suffix) >= 2:
382
+ return int(suffix)
383
+ return None
384
+
385
+
386
+ def _parse_topology(topology, axis=None):
387
+ if isinstance(topology, (tuple, list)):
388
+ if len(topology) != 2:
389
+ raise ValueError("topology must be an int or a (shards, axis) pair")
390
+ shards, explicit_axis = topology
391
+ if axis is not None and int(axis) != int(explicit_axis):
392
+ raise ValueError("topology axis and the axis argument disagree")
393
+ axis = explicit_axis
394
+ else:
395
+ shards = topology
396
+ shards = int(shards)
397
+ axis = 0 if axis is None else int(axis)
398
+ if shards < 1:
399
+ raise ValueError("topology shard count must be >= 1")
400
+ if axis < 0 or axis > 0xFFFFFFFF:
401
+ raise ValueError("topology axis must be a u32")
402
+ return shards, axis
403
+
404
+
405
+ def _default_cli(cli=None):
406
+ if cli:
407
+ return os.fspath(cli)
408
+ env = os.environ.get("WEFTFS_CLI")
409
+ if env:
410
+ return env
411
+ if os.path.isfile(DEFAULT_CLI) and os.access(DEFAULT_CLI, os.X_OK):
412
+ return DEFAULT_CLI
413
+ found = shutil.which("weftfs")
414
+ if found:
415
+ return found
416
+ raise StoreUnavailable(
417
+ "no weftfs CLI found: set WEFTFS_CLI or pass weftfs_cli=... (looked at "
418
+ "%s and PATH)" % DEFAULT_CLI
419
+ )
420
+
421
+
422
+ def _parse_error_class(stderr: str):
423
+ first = stderr.strip().splitlines()[0] if stderr.strip() else ""
424
+ if ":" in first:
425
+ head = first.split(":", 1)[0].strip()
426
+ if head and head.replace("_", "").isalnum() and head[0].isupper():
427
+ return head
428
+ return None
429
+
430
+
431
+ class _Storage:
432
+ """Thin, fail-closed wrapper around the ``weftfs`` CLI."""
433
+
434
+ def __init__(self, store_root, *, cli=None, timeout=DEFAULT_TIMEOUT_SECONDS):
435
+ self.root = Path(store_root)
436
+ self.cli = _default_cli(cli)
437
+ self.timeout = float(timeout)
438
+ self._lock = threading.Lock()
439
+ self.command_ms = 0.0
440
+ self.calls = 0
441
+
442
+ # -- process plumbing ---------------------------------------------------
443
+
444
+ def _run(self, args, *, binary=False):
445
+ command = [self.cli, "--store", os.fspath(self.root), *[str(a) for a in args]]
446
+ started = time.monotonic()
447
+ try:
448
+ proc = subprocess.run(
449
+ command,
450
+ capture_output=True,
451
+ timeout=self.timeout,
452
+ check=False,
453
+ )
454
+ except FileNotFoundError as exc:
455
+ raise StoreUnavailable("weftfs CLI not executable: %s" % exc) from exc
456
+ except subprocess.TimeoutExpired as exc:
457
+ raise StoreCommandError(
458
+ "weftfs command timed out after %.0fs: %s"
459
+ % (self.timeout, " ".join(command)),
460
+ command=command,
461
+ returncode=-1,
462
+ stderr="",
463
+ error_class="Timeout",
464
+ ) from exc
465
+ elapsed = (time.monotonic() - started) * 1000.0
466
+ with self._lock:
467
+ self.command_ms += elapsed
468
+ self.calls += 1
469
+ if proc.returncode != 0:
470
+ stderr = proc.stderr.decode("utf-8", "replace").strip()
471
+ error_class = _parse_error_class(stderr)
472
+ raise StoreCommandError(
473
+ "weftfs %s failed (%d): %s"
474
+ % (" ".join(str(a) for a in args), proc.returncode, stderr),
475
+ command=command,
476
+ returncode=proc.returncode,
477
+ stderr=stderr,
478
+ error_class=error_class,
479
+ )
480
+ return proc
481
+
482
+ def _json(self, args):
483
+ proc = self._run([*args, "--json"])
484
+ text = proc.stdout.decode("utf-8")
485
+ try:
486
+ return json.loads(text)
487
+ except ValueError as exc:
488
+ raise StoreCommandError(
489
+ "weftfs %s produced invalid JSON: %s"
490
+ % (" ".join(str(a) for a in args), text[:400]),
491
+ command=[self.cli, "--store", os.fspath(self.root), *args],
492
+ returncode=0,
493
+ stderr=text[:400],
494
+ error_class="InvalidJson",
495
+ ) from exc
496
+
497
+ # -- store verbs --------------------------------------------------------
498
+
499
+ def requires_store(self):
500
+ if not (self.root / "FORMAT").is_file():
501
+ raise StoreUnavailable(
502
+ "no WeftFS store at %s (missing FORMAT); call init_store() first"
503
+ % self.root
504
+ )
505
+
506
+ def init(self):
507
+ if (self.root / "FORMAT").is_file():
508
+ return
509
+ self._run(["init"])
510
+
511
+ def list_checkpoints(self):
512
+ payload = self._json(["checkpoint", "list"])
513
+ return [row["name"] for row in payload.get("checkpoints", [])]
514
+
515
+ def checkpoint_show(self, name):
516
+ payload = self._json(["checkpoint", "show", "--name", name])
517
+ manifest = payload.get("manifest")
518
+ if not isinstance(manifest, dict):
519
+ raise InvalidMetadata("checkpoint show --json has no manifest object")
520
+ return payload
521
+
522
+ def checkpoint_write(self, staging, name, *, shards, axis, strategy):
523
+ args = [
524
+ "checkpoint",
525
+ "write",
526
+ "--file",
527
+ os.fspath(staging),
528
+ "--name",
529
+ name,
530
+ "--shards",
531
+ str(shards),
532
+ "--axis",
533
+ str(axis),
534
+ ]
535
+ if strategy:
536
+ args += ["--shard-strategy", strategy]
537
+ return self._json(args)
538
+
539
+ def restore(self, name, *, entry=None, topology=None, axis=0, host_out, import_restored=False):
540
+ """Restore to ``host_out``; ``import_restored`` opts into a store copy.
541
+
542
+ ``checkpoint restore`` is read-only in the store by default. Passing
543
+ ``import_restored=True`` adds ``--import`` and publishes the exact
544
+ restored container under ``/dcp-reads/<uuid>/``, which is a real,
545
+ durable second copy and therefore opt-in.
546
+ """
547
+ args = [
548
+ "checkpoint",
549
+ "restore",
550
+ "--name",
551
+ name,
552
+ "--host-out",
553
+ os.fspath(host_out),
554
+ ]
555
+ receipt = os.environ.get("WEFT_LOAD_RECEIPT")
556
+ if receipt:
557
+ args += [
558
+ "--load-receipt",
559
+ receipt,
560
+ "--verify-load",
561
+ os.environ.get("WEFT_VERIFY_LOAD", "lazy"),
562
+ "--replica",
563
+ os.environ.get("WEFT_REPLICA", "local"),
564
+ ]
565
+ if import_restored:
566
+ out_dir = "/dcp-reads/%s" % uuid.uuid4().hex
567
+ args += ["--import", "--out", out_dir]
568
+ if entry is not None:
569
+ args += ["--tensor", entry]
570
+ if topology is not None:
571
+ args += ["--topology", str(topology), "--axis", str(axis)]
572
+ return self._json(args)
573
+
574
+ def put_file(self, store_path, payload: bytes):
575
+ with tempfile.NamedTemporaryFile(
576
+ prefix="weft-dcp-put-", suffix=".bin", delete=False
577
+ ) as handle:
578
+ handle.write(payload)
579
+ host_path = handle.name
580
+ try:
581
+ return self._json(["create", store_path, "--file", host_path])
582
+ finally:
583
+ try:
584
+ os.unlink(host_path)
585
+ except OSError:
586
+ pass
587
+
588
+ def get_file(self, store_path):
589
+ """Exact bytes of a store file object."""
590
+ proc = self._run(["cat", store_path], binary=True)
591
+ return proc.stdout
592
+
593
+ def file_exists(self, store_path):
594
+ try:
595
+ self._run(["stat", store_path])
596
+ return True
597
+ except StoreCommandError as exc:
598
+ if exc.error_class in ("NotFound",):
599
+ return False
600
+ raise
601
+
602
+ def verify_full(self):
603
+ return self._json(["verify", "--full"])
604
+
605
+ def checkpoint_verify(self, name):
606
+ return self._json(["checkpoint", "verify", "--name", name])
607
+
608
+
609
+ # ---------------------------------------------------------------------------
610
+ # DCP metadata schema (canonical WCB)
611
+
612
+
613
+ @dataclass(frozen=True)
614
+ class StorageInfo:
615
+ """Where one DCP storage index lives inside the checkpoint container."""
616
+
617
+ entry: str
618
+ dtype: str # torch dtype spelling
619
+ shape: tuple
620
+ storage_offsets: tuple
621
+ lengths: tuple
622
+ content_root: bytes
623
+
624
+ def to_value(self):
625
+ return {
626
+ "entry": self.entry,
627
+ "dtype": self.dtype,
628
+ "shape": _u_list(self.shape),
629
+ "storage_offsets": _u_list(self.storage_offsets),
630
+ "lengths": _u_list(self.lengths),
631
+ "content_root": self.content_root,
632
+ }
633
+
634
+ @classmethod
635
+ def from_value(cls, value):
636
+ try:
637
+ return cls(
638
+ entry=value["entry"],
639
+ dtype=value["dtype"],
640
+ shape=_i_list(value["shape"]),
641
+ storage_offsets=_i_list(value.get("storage_offsets", [])),
642
+ lengths=_i_list(value.get("lengths", [])),
643
+ content_root=bytes(value["content_root"]),
644
+ )
645
+ except (KeyError, TypeError) as exc:
646
+ raise InvalidMetadata("bad storage entry: %s" % exc) from exc
647
+
648
+
649
+ @dataclass(frozen=True)
650
+ class RestoredEntry:
651
+ """One entry restored (and re-verified) through the Rust CLI."""
652
+
653
+ entry: str
654
+ data: bytes
655
+ dtype: str
656
+ shape: tuple
657
+ content_root: bytes
658
+ topology_root: "str | None"
659
+ shards_read: int
660
+ store_bytes_read: int
661
+
662
+ @property
663
+ def content_root_hex(self) -> str:
664
+ return self.content_root.hex()
665
+
666
+
667
+ def _state_dict_value(state_dict_metadata):
668
+ rows = []
669
+ for fqn in sorted(state_dict_metadata):
670
+ item = state_dict_metadata[fqn]
671
+ if _TensorStorageMetadata is not None and isinstance(
672
+ item, _TensorStorageMetadata
673
+ ):
674
+ rows.append(
675
+ {
676
+ "fqn": fqn,
677
+ "kind": "tensor",
678
+ "dtype": _dtype_name(item.properties.dtype),
679
+ "size": _u_list(item.size),
680
+ "chunks": [
681
+ {
682
+ "offsets": _u_list(chunk.offsets),
683
+ "sizes": _u_list(chunk.sizes),
684
+ }
685
+ for chunk in item.chunks
686
+ ],
687
+ }
688
+ )
689
+ elif _BytesStorageMetadata is not None and isinstance(
690
+ item, _BytesStorageMetadata
691
+ ):
692
+ rows.append({"fqn": fqn, "kind": "bytes"})
693
+ else:
694
+ raise UnsupportedStateObject(
695
+ "state dict entry %r has storage metadata %s; this adapter "
696
+ "persists plain tensors and byte objects only"
697
+ % (fqn, type(item).__name__)
698
+ )
699
+ return rows
700
+
701
+
702
+ def _planner_data_value(planner_data):
703
+ if planner_data is None:
704
+ return {"kind": "none"}
705
+ if isinstance(planner_data, dict) and all(
706
+ isinstance(key, str)
707
+ and isinstance(value, (tuple, list))
708
+ and all(isinstance(part, str) for part in value)
709
+ for key, value in planner_data.items()
710
+ ):
711
+ return {
712
+ "kind": "dict_str_tuple",
713
+ "entries": [
714
+ {"key": key, "value": list(planner_data[key])}
715
+ for key in sorted(planner_data)
716
+ ],
717
+ }
718
+ raise UnsupportedStateObject(
719
+ "planner_data of type %s is not persistable by this adapter; only None "
720
+ "and dict[str, tuple[str, ...]] are supported" % type(planner_data).__name__
721
+ )
722
+
723
+
724
+ def serialize_metadata(metadata, *, checkpoint_id, storage_name) -> bytes:
725
+ """Canonical WCB bytes for a DCP ``Metadata`` object."""
726
+ storage_rows = []
727
+ storage_data = metadata.storage_data
728
+ if storage_data is None:
729
+ raise InvalidMetadata("metadata.storage_data is None; write_data/finish "
730
+ "must run before metadata is serialised")
731
+ for key in sorted(storage_data, key=lambda k: (k.fqn, tuple(k.offset or ()))):
732
+ info = storage_data[key]
733
+ if not isinstance(info, StorageInfo):
734
+ raise InvalidMetadata(
735
+ "storage_data[%r] is %s, expected StorageInfo"
736
+ % (key, type(info).__name__)
737
+ )
738
+ storage_rows.append(
739
+ {
740
+ "fqn": key.fqn,
741
+ "offset": None if key.offset is None else _u_list(key.offset),
742
+ "index": None if key.index is None else _u(key.index),
743
+ "info": info.to_value(),
744
+ }
745
+ )
746
+ version = getattr(metadata, "version", None)
747
+ value = {
748
+ "schema": DCP_METADATA_SCHEMA,
749
+ "checkpoint_id": checkpoint_id,
750
+ "storage_name": storage_name,
751
+ "dcp_version": None if version is None else str(version),
752
+ "state_dict": _state_dict_value(metadata.state_dict_metadata),
753
+ "storage_data": storage_rows,
754
+ "planner_data": _planner_data_value(getattr(metadata, "planner_data", None)),
755
+ }
756
+ return wcb.encode(value)
757
+
758
+
759
+ def _metadata_from_value(value, *, storage_name):
760
+ _require_torch("reconstructing DCP metadata")
761
+ if not isinstance(value, dict) or value.get("schema") != DCP_METADATA_SCHEMA:
762
+ raise InvalidMetadata(
763
+ "DCP metadata schema is %r, expected %r"
764
+ % (
765
+ value.get("schema") if isinstance(value, dict) else None,
766
+ DCP_METADATA_SCHEMA,
767
+ )
768
+ )
769
+ if value.get("storage_name") != storage_name:
770
+ raise InvalidMetadata(
771
+ "DCP metadata names storage %r but the resolved checkpoint is %r"
772
+ % (value.get("storage_name"), storage_name)
773
+ )
774
+ state_dict_metadata = {}
775
+ for row in value.get("state_dict", []):
776
+ fqn = row.get("fqn")
777
+ kind = row.get("kind")
778
+ if kind == "tensor":
779
+ dtype_name = row.get("dtype")
780
+ if dtype_name not in _SAFETENSORS_BY_TORCH:
781
+ raise UnsupportedDtype(
782
+ "stored tensor %r has unsupported dtype %r" % (fqn, dtype_name)
783
+ )
784
+ state_dict_metadata[fqn] = _TensorStorageMetadata(
785
+ properties=_TensorProperties(dtype=_torch_dtype(dtype_name)),
786
+ size=_TORCH.Size(_i_list(row.get("size", []))),
787
+ chunks=[
788
+ _ChunkStorageMetadata(
789
+ offsets=_TORCH.Size(_i_list(chunk.get("offsets", []))),
790
+ sizes=_TORCH.Size(_i_list(chunk.get("sizes", []))),
791
+ )
792
+ for chunk in row.get("chunks", [])
793
+ ],
794
+ )
795
+ elif kind == "bytes":
796
+ state_dict_metadata[fqn] = _BytesStorageMetadata()
797
+ else:
798
+ raise InvalidMetadata("unknown state-dict entry kind %r" % (kind,))
799
+
800
+ storage_data = {}
801
+ for row in value.get("storage_data", []):
802
+ info = StorageInfo.from_value(row.get("info", {}))
803
+ offset = row.get("offset")
804
+ index = row.get("index")
805
+ key = _MetadataIndex(
806
+ row.get("fqn"),
807
+ offset=None if offset is None else _i_list(offset),
808
+ index=None if index is None else _i(index),
809
+ )
810
+ storage_data[key] = info
811
+
812
+ planner = value.get("planner_data") or {"kind": "none"}
813
+ if planner.get("kind") == "none":
814
+ planner_data = None
815
+ elif planner.get("kind") == "dict_str_tuple":
816
+ planner_data = {
817
+ entry["key"]: tuple(entry["value"]) for entry in planner.get("entries", [])
818
+ }
819
+ else:
820
+ raise InvalidMetadata("unknown planner_data kind %r" % (planner.get("kind"),))
821
+
822
+ return _TorchMetadata(
823
+ state_dict_metadata=state_dict_metadata,
824
+ planner_data=planner_data,
825
+ storage_data=storage_data,
826
+ storage_meta=_TorchStorageMeta(
827
+ checkpoint_id=value.get("checkpoint_id"),
828
+ save_id=value.get("storage_name"),
829
+ ),
830
+ version=value.get("dcp_version"),
831
+ )
832
+
833
+
834
+ def _metadata_from_manifest(show, *, checkpoint_id, storage_name):
835
+ """Build DCP metadata for a checkpoint written by the Rust CLI alone.
836
+
837
+ Every manifest tensor becomes a state-dict key of the same name with one
838
+ full-shape chunk. This is exact for plain safetensors checkpoints; it is
839
+ reported as ``metadata_source == "manifest"`` so callers can tell it apart
840
+ from adapter-written checkpoints that carry DCP metadata.
841
+ """
842
+ _require_torch("synthesising DCP metadata from a manifest")
843
+ manifest = show["manifest"]
844
+ state_dict_metadata = {}
845
+ storage_data = {}
846
+ for tensor in manifest.get("tensors", []):
847
+ name = tensor["name"]
848
+ canonical = tensor["dtype"]
849
+ torch_name = _CANONICAL_TO_TORCH.get(canonical)
850
+ if torch_name is None:
851
+ raise UnsupportedDtype(
852
+ "manifest tensor %r has dtype %r with no exact torch dtype"
853
+ % (name, canonical)
854
+ )
855
+ shape = tuple(int(x) for x in tensor["shape"])
856
+ storage_offsets = tuple([0] * len(shape))
857
+ element_bytes = int(tensor["element_bytes"])
858
+ expected = 1
859
+ for extent in shape:
860
+ expected *= extent
861
+ expected *= _element_bytes(torch_name)
862
+ if expected != element_bytes:
863
+ raise InvalidMetadata(
864
+ "manifest tensor %r element_bytes %d != shape product %d"
865
+ % (name, element_bytes, expected)
866
+ )
867
+ key = _MetadataIndex(name, offset=storage_offsets)
868
+ state_dict_metadata[name] = _TensorStorageMetadata(
869
+ properties=_TensorProperties(dtype=_torch_dtype(torch_name)),
870
+ size=_TORCH.Size(shape),
871
+ chunks=[
872
+ _ChunkStorageMetadata(
873
+ offsets=_TORCH.Size([0] * len(shape)),
874
+ sizes=_TORCH.Size(shape),
875
+ )
876
+ ],
877
+ )
878
+ storage_data[key] = StorageInfo(
879
+ entry=name,
880
+ dtype=torch_name,
881
+ shape=shape,
882
+ storage_offsets=storage_offsets,
883
+ lengths=shape,
884
+ content_root=bytes.fromhex(tensor["content_root"]),
885
+ )
886
+ return _TorchMetadata(
887
+ state_dict_metadata=state_dict_metadata,
888
+ planner_data=None,
889
+ storage_data=storage_data,
890
+ storage_meta=_TorchStorageMeta(
891
+ checkpoint_id=checkpoint_id, save_id=storage_name
892
+ ),
893
+ version="1.0.0",
894
+ )
895
+
896
+
897
+ # ---------------------------------------------------------------------------
898
+ # safetensors helpers (writing the staging container, reading restored bytes)
899
+
900
+
901
+ def _tensor_bytes(tensor):
902
+ """Exact little-endian row-major bytes of a CPU tensor, without numpy.
903
+
904
+ ``data_ptr()`` addresses the tensor's first logical element, so a single
905
+ ``ctypes.string_at`` copies exactly ``numel * element_size`` bytes for a
906
+ contiguous tensor, including dtypes numpy cannot represent (bfloat16,
907
+ float8). This is a host-memory copy, not a zero-copy path.
908
+ """
909
+ _require_torch("serialising a tensor")
910
+ t = tensor.detach()
911
+ if t.device.type != "cpu":
912
+ t = t.cpu()
913
+ if t.numel() == 0:
914
+ return b""
915
+ t = t.contiguous()
916
+ expected = t.numel() * t.element_size()
917
+ payload = ctypes.string_at(t.data_ptr(), expected)
918
+ if len(payload) != expected:
919
+ raise ExactnessError(
920
+ "tensor byte extraction produced %d bytes, expected %d"
921
+ % (len(payload), expected)
922
+ )
923
+ return payload
924
+
925
+
926
+ def _narrow(tensor, offsets, lengths):
927
+ if not offsets:
928
+ return tensor
929
+ slices = tuple(
930
+ slice(int(offset), int(offset) + int(length))
931
+ for offset, length in zip(offsets, lengths)
932
+ )
933
+ return tensor[slices]
934
+
935
+
936
+ def _parse_safetensors(path):
937
+ with open(path, "rb") as handle:
938
+ prefix = handle.read(8)
939
+ if len(prefix) != 8:
940
+ raise InvalidMetadata("restored container is shorter than 8 bytes")
941
+ header_len = struct.unpack("<Q", prefix)[0]
942
+ if header_len > 64 * 1024 * 1024:
943
+ raise InvalidMetadata(
944
+ "restored container header length %d exceeds the 64 MiB bound"
945
+ % header_len
946
+ )
947
+ header_raw = handle.read(header_len)
948
+ if len(header_raw) != header_len:
949
+ raise InvalidMetadata("restored container header is truncated")
950
+ try:
951
+ header = json.loads(header_raw)
952
+ except ValueError as exc:
953
+ raise InvalidMetadata("restored container header is not JSON: %s" % exc)
954
+ data_start = 8 + header_len
955
+ entries = {}
956
+ for name, spec in header.items():
957
+ if name == "__metadata__":
958
+ continue
959
+ try:
960
+ begin, end = spec["data_offsets"]
961
+ dtype = spec["dtype"]
962
+ shape = tuple(int(x) for x in spec["shape"])
963
+ except (KeyError, TypeError, ValueError) as exc:
964
+ raise InvalidMetadata(
965
+ "restored container tensor %r descriptor is invalid: %s"
966
+ % (name, exc)
967
+ )
968
+ if begin < 0 or end < begin:
969
+ raise InvalidMetadata(
970
+ "restored container tensor %r has invalid data_offsets" % name
971
+ )
972
+ handle.seek(data_start + begin)
973
+ payload = handle.read(end - begin)
974
+ if len(payload) != end - begin:
975
+ raise InvalidMetadata(
976
+ "restored container tensor %r is truncated" % name
977
+ )
978
+ entries[name] = (dtype, shape, payload)
979
+ return entries
980
+
981
+
982
+ # ---------------------------------------------------------------------------
983
+ # Writer
984
+
985
+
986
+ class WeftStorageWriter(_StorageWriterBase):
987
+ """DCP ``StorageWriter`` that publishes a canonical WeftFS checkpoint.
988
+
989
+ Parameters
990
+ ----------
991
+ path:
992
+ DCP checkpoint id. It becomes a single WeftFS checkpoint name under
993
+ ``/checkpoints``; it must not contain ``/``.
994
+ store_root:
995
+ Directory of an initialized WeftFS store (or pass ``init_store=True``).
996
+ topology:
997
+ ``n`` or ``(n, axis)``: store every tensor split n ways along ``axis``.
998
+ ``n == 1`` stores whole tensors. The Rust CLI rejects a non-divisible
999
+ extent ("no silent padding"); the adapter never pads or reinterprets.
1000
+ This applies to non-tensor (``BYTE_IO``) state objects too: they are
1001
+ stored as ``u8[len]`` tensors, so a split topology needs their
1002
+ serialized length to be divisible by the shard count. Use ``topology=1``
1003
+ for state dicts whose byte objects have arbitrary lengths.
1004
+ shard_strategy:
1005
+ ``zstd`` (default; exact, works for any shard length), ``raw`` (exact),
1006
+ or ``tensor-sharded`` (mode-10 byte-word transport). ``tensor-sharded``
1007
+ additionally requires every shard's byte length to be divisible by 16
1008
+ (the smallest mode-10 block); the Rust CLI rejects anything else rather
1009
+ than pad.
1010
+ staging_bytes:
1011
+ Hard bound on one resolved tensor copy staged for the safetensors
1012
+ container. Tensors above the bound raise :class:`StagingLimitExceeded`.
1013
+ overwrite:
1014
+ When True (default) a second save to the same id is written as a new
1015
+ generation ``<id>.v2``, ``<id>.v3``, ... (no store-side delete exists).
1016
+ When False an existing generation raises ``FileExistsError``.
1017
+ """
1018
+
1019
+ def __init__(
1020
+ self,
1021
+ path,
1022
+ *,
1023
+ store_root,
1024
+ topology=1,
1025
+ axis=None,
1026
+ shard_strategy="zstd",
1027
+ staging_bytes=DEFAULT_STAGING_BYTES,
1028
+ overwrite=True,
1029
+ init_store=False,
1030
+ keep_staging=False,
1031
+ weftfs_cli=None,
1032
+ timeout=DEFAULT_TIMEOUT_SECONDS,
1033
+ ):
1034
+ self._path = _validate_checkpoint_id(path)
1035
+ self.store_root = Path(store_root)
1036
+ self._topology = _parse_topology(topology, axis)
1037
+ if shard_strategy not in ("tensor-sharded", "tensor-tiled", "zstd", "raw"):
1038
+ raise ValueError(
1039
+ "shard_strategy must be tensor-sharded, tensor-tiled, zstd, or raw"
1040
+ )
1041
+ if shard_strategy == "tensor-tiled":
1042
+ raise ValueError(
1043
+ "tensor-tiled checkpoint shards need a tile shape, which this "
1044
+ "adapter does not expose; use tensor-sharded, zstd, or raw"
1045
+ )
1046
+ staging_bytes = int(staging_bytes)
1047
+ if staging_bytes <= 0:
1048
+ raise ValueError("staging_bytes must be a positive byte count")
1049
+ self.staging_bytes = staging_bytes
1050
+ self.shard_strategy = shard_strategy
1051
+ self.overwrite = bool(overwrite)
1052
+ self.keep_staging = bool(keep_staging)
1053
+ self._store = _Storage(self.store_root, cli=weftfs_cli, timeout=timeout)
1054
+ if init_store:
1055
+ self._store.init()
1056
+ self._store.requires_store()
1057
+ self._service_cli = _default_cli(weftfs_cli)
1058
+ self._timeout = float(timeout)
1059
+ self._service = None
1060
+ self._lock = threading.RLock()
1061
+ self._save_id = uuid.uuid4().hex
1062
+ self._storage_name = None
1063
+ self._entries = {}
1064
+ self._results = []
1065
+ self._pending_metadata = None
1066
+ self._finished = False
1067
+ self._manifest = None
1068
+ self._stats = {
1069
+ "entries": 0,
1070
+ "tensor_bytes": 0,
1071
+ "staging_peak_bytes": 0,
1072
+ "checkpoint_write_ms": 0.0,
1073
+ }
1074
+
1075
+ # -- DCP interface ------------------------------------------------------
1076
+
1077
+ @classmethod
1078
+ def validate_checkpoint_id(cls, checkpoint_id) -> bool:
1079
+ try:
1080
+ _validate_checkpoint_id(checkpoint_id)
1081
+ return True
1082
+ except (InvalidCheckpointId, TypeError):
1083
+ return False
1084
+
1085
+ @property
1086
+ def checkpoint_id(self):
1087
+ return self._path
1088
+
1089
+ def _get_service(self):
1090
+ """One helper process for every operation of this writer."""
1091
+ if self._service is None:
1092
+ self._service = CheckpointService(
1093
+ self.store_root, cli=self._service_cli, timeout=self._timeout
1094
+ )
1095
+ return self._service
1096
+
1097
+ def close(self):
1098
+ if self._service is not None:
1099
+ self._service.close()
1100
+ self._service = None
1101
+
1102
+ @property
1103
+ def store_name(self):
1104
+ return self._storage_name
1105
+
1106
+ @property
1107
+ def stats(self):
1108
+ payload = dict(self._stats)
1109
+ payload.update(
1110
+ {
1111
+ "checkpoint_id": self._path,
1112
+ "storage_name": self._storage_name,
1113
+ "store_root": os.fspath(self.store_root),
1114
+ "store_command_ms": self._store.command_ms,
1115
+ "store_command_calls": self._store.calls,
1116
+ "service": (
1117
+ None if self._service is None else dict(self._service.stats)
1118
+ ),
1119
+ "topology": self._topology,
1120
+ "shard_strategy": self.shard_strategy,
1121
+ "staging_bytes": self.staging_bytes,
1122
+ }
1123
+ )
1124
+ return payload
1125
+
1126
+ def reset(self, checkpoint_id=None):
1127
+ with self._lock:
1128
+ if checkpoint_id is not None:
1129
+ self._path = _validate_checkpoint_id(checkpoint_id)
1130
+ self._save_id = uuid.uuid4().hex
1131
+ self._storage_name = None
1132
+ self._entries = {}
1133
+ self._results = []
1134
+ self._pending_metadata = None
1135
+ self._finished = False
1136
+ self._manifest = None
1137
+ self._stats = {
1138
+ "entries": 0,
1139
+ "tensor_bytes": 0,
1140
+ "staging_peak_bytes": 0,
1141
+ "checkpoint_write_ms": 0.0,
1142
+ }
1143
+ return None
1144
+
1145
+ def set_up_storage_writer(self, is_coordinator, *args, **kwargs):
1146
+ self._is_coordinator = bool(is_coordinator)
1147
+ return None
1148
+
1149
+ def prepare_local_plan(self, plan):
1150
+ return plan
1151
+
1152
+ def prepare_global_plan(self, plans):
1153
+ return plans
1154
+
1155
+ def storage_meta(self):
1156
+ return _TorchStorageMeta(checkpoint_id=self._path, save_id=self._save_id)
1157
+
1158
+ def write_metadata(self, metadata):
1159
+ """Record the DCP metadata object.
1160
+
1161
+ PyTorch 2.15's ``save`` does not call this method (it writes metadata in
1162
+ ``finish``); the method is implemented for interface parity and older
1163
+ callers. The metadata is persisted once, in ``finish``, because the
1164
+ storage keys are only final after ``write_data``.
1165
+ """
1166
+ if metadata is None:
1167
+ raise InvalidMetadata("write_metadata(None)")
1168
+ with self._lock:
1169
+ self._pending_metadata = metadata
1170
+ return None
1171
+
1172
+ def write_data(self, plan, planner):
1173
+ future = _TorchFuture()
1174
+ try:
1175
+ results = self._execute(plan, planner)
1176
+ except BaseException as exc: # propagated through the DCP future
1177
+ future.set_exception(exc)
1178
+ return future
1179
+ future.set_result(results)
1180
+ return future
1181
+
1182
+ def finish(self, metadata, results):
1183
+ with self._lock:
1184
+ if self._storage_name is None:
1185
+ raise SaveFailed(
1186
+ "finish() called before a successful write_data(); no "
1187
+ "checkpoint was published"
1188
+ )
1189
+ if self._finished:
1190
+ raise SaveFailed("finish() called twice for one save")
1191
+ merged = {}
1192
+ for group in results:
1193
+ for result in group:
1194
+ merged[result.index] = result.storage_data
1195
+ metadata.storage_data = merged
1196
+ metadata.storage_meta = self.storage_meta()
1197
+ if getattr(metadata, "version", None) is None:
1198
+ metadata.version = "1.0.0"
1199
+ blob = serialize_metadata(
1200
+ metadata,
1201
+ checkpoint_id=self._path,
1202
+ storage_name=self._storage_name,
1203
+ )
1204
+ try:
1205
+ self._get_service().write_file(
1206
+ "/checkpoints/%s/%s" % (self._storage_name, DCP_METADATA_NAME),
1207
+ blob,
1208
+ )
1209
+ except (StoreCommandError, ServiceError) as exc:
1210
+ raise SaveFailed(
1211
+ "checkpoint %r was published but its DCP metadata was not: %s"
1212
+ % (self._storage_name, exc)
1213
+ ) from exc
1214
+ self._finished = True
1215
+ self._metadata = metadata
1216
+ return None
1217
+
1218
+ # -- internals ----------------------------------------------------------
1219
+
1220
+ def _allocate_storage_name(self):
1221
+ existing = set(self._get_service().list_checkpoints())
1222
+ if self._path not in existing:
1223
+ return self._path
1224
+ if not self.overwrite:
1225
+ raise FileExistsError(
1226
+ "checkpoint %r already exists in %s and overwrite=False"
1227
+ % (self._path, self.store_root)
1228
+ )
1229
+ highest = 1
1230
+ for name in existing:
1231
+ generation = _generation(name, self._path)
1232
+ if generation is not None and generation > highest:
1233
+ highest = generation
1234
+ return "%s.v%d" % (self._path, highest + 1)
1235
+
1236
+ def _entry_names(self, items):
1237
+ used = set()
1238
+ names = {}
1239
+ for item in items:
1240
+ fqn = item.index.fqn
1241
+ name = fqn
1242
+ suffix = 1
1243
+ while name in used:
1244
+ name = "%s.__weftwp_%d" % (fqn, suffix)
1245
+ suffix += 1
1246
+ used.add(name)
1247
+ names[(fqn, tuple(item.index.offset or ()), item.index.index)] = name
1248
+ return names
1249
+
1250
+ def _execute(self, plan, planner):
1251
+ _require_torch("writing a DCP checkpoint")
1252
+ items = list(plan.items)
1253
+ if not items:
1254
+ raise SaveFailed(
1255
+ "save plan contains no items; refusing to publish an empty "
1256
+ "checkpoint"
1257
+ )
1258
+ items.sort(
1259
+ key=lambda item: (
1260
+ item.index.fqn,
1261
+ tuple(item.index.offset or ()),
1262
+ -1 if item.index.index is None else int(item.index.index),
1263
+ )
1264
+ )
1265
+ names = self._entry_names(items)
1266
+
1267
+ # Pass 1: derive lengths, dtypes and shapes; cache byte payloads.
1268
+ staged = []
1269
+ byte_payloads = {}
1270
+ byte_bytes = 0
1271
+ for item in items:
1272
+ key = (item.index.fqn, tuple(item.index.offset or ()), item.index.index)
1273
+ entry = names[key]
1274
+ if getattr(item.type, "name", str(item.type)) == "BYTE_IO":
1275
+ data = planner.resolve_data(item)
1276
+ if isinstance(data, io.BytesIO):
1277
+ payload = data.getvalue()
1278
+ elif isinstance(data, (bytes, bytearray, memoryview)):
1279
+ payload = bytes(data)
1280
+ else:
1281
+ raise UnsupportedStateObject(
1282
+ "BYTE_IO write item %r resolved to %s, expected bytes"
1283
+ % (item.index.fqn, type(data).__name__)
1284
+ )
1285
+ byte_bytes += len(payload)
1286
+ if byte_bytes > self.staging_bytes:
1287
+ raise StagingLimitExceeded(
1288
+ "byte state objects need %d staged bytes, above the %d "
1289
+ "staging_bytes bound" % (byte_bytes, self.staging_bytes)
1290
+ )
1291
+ byte_payloads[key] = payload
1292
+ staged.append(
1293
+ {
1294
+ "item": item,
1295
+ "entry": entry,
1296
+ "kind": "bytes",
1297
+ "dtype": "torch.uint8",
1298
+ "shape": (len(payload),),
1299
+ "payload": payload,
1300
+ "offsets": (),
1301
+ "lengths": (),
1302
+ }
1303
+ )
1304
+ continue
1305
+
1306
+ tensor_data = item.tensor_data
1307
+ if tensor_data is None:
1308
+ raise UnsupportedStateObject(
1309
+ "write item %r has no tensor_data" % (item.index.fqn,)
1310
+ )
1311
+ chunk = tensor_data.chunk
1312
+ shape = tuple(int(x) for x in tensor_data.size)
1313
+ offsets = tuple(int(x) for x in chunk.offsets)
1314
+ lengths = tuple(int(x) for x in chunk.sizes)
1315
+ if offsets != tuple([0] * len(shape)) or lengths != shape:
1316
+ raise UnsupportedStateObject(
1317
+ "write item %r is a chunk at %r of shape %r; sharded/"
1318
+ "chunked state objects are not persisted by this adapter"
1319
+ % (item.index.fqn, offsets, shape)
1320
+ )
1321
+ dtype = _dtype_name(tensor_data.properties.dtype)
1322
+ _canonical_dtype(dtype) # validates the exact mapping
1323
+ staged.append(
1324
+ {
1325
+ "item": item,
1326
+ "entry": entry,
1327
+ "kind": "tensor",
1328
+ "dtype": dtype,
1329
+ "shape": shape,
1330
+ "payload": None,
1331
+ "offsets": offsets,
1332
+ "lengths": lengths,
1333
+ }
1334
+ )
1335
+
1336
+ # Header is fixed before data is streamed.
1337
+ header = {}
1338
+ cursor = 0
1339
+ for row in staged:
1340
+ nbytes = (
1341
+ len(row["payload"])
1342
+ if row["kind"] == "bytes"
1343
+ else _element_bytes(row["dtype"]) * math.prod(row["shape"])
1344
+ )
1345
+ header[row["entry"]] = {
1346
+ "dtype": _safetensors_dtype(row["dtype"]),
1347
+ "shape": list(row["shape"]),
1348
+ "data_offsets": [cursor, cursor + nbytes],
1349
+ }
1350
+ row["nbytes"] = nbytes
1351
+ cursor += nbytes
1352
+ header_raw = json.dumps(
1353
+ header, sort_keys=True, separators=(",", ":")
1354
+ ).encode("utf-8")
1355
+ if len(header_raw) > 64 * 1024 * 1024:
1356
+ raise SaveFailed("staging safetensors header exceeds 64 MiB")
1357
+
1358
+ # Stage the canonical container in memory. It must exist as
1359
+ # safetensors bytes because the checkpoint manifest records the source
1360
+ # header and SHA-256, but it never reaches the disk, is never a path
1361
+ # argument to a CLI process, and is handed to the helper over a memfd.
1362
+ container = bytearray(struct.pack("<Q", len(header_raw)))
1363
+ container += header_raw
1364
+ infos = []
1365
+ peak = 0
1366
+ for row in staged:
1367
+ if row["kind"] == "bytes":
1368
+ payload = row["payload"]
1369
+ else:
1370
+ tensor = planner.resolve_data(row["item"])
1371
+ if not hasattr(tensor, "detach"):
1372
+ raise UnsupportedStateObject(
1373
+ "tensor write item %r resolved to %s"
1374
+ % (row["item"].index.fqn, type(tensor).__name__)
1375
+ )
1376
+ actual_dtype = _dtype_name(tensor.dtype)
1377
+ actual_shape = tuple(int(x) for x in tensor.shape)
1378
+ if actual_dtype != row["dtype"] or actual_shape != row["shape"]:
1379
+ raise SaveFailed(
1380
+ "planner resolved %r as %s%r, plan said %s%r"
1381
+ % (
1382
+ row["item"].index.fqn,
1383
+ actual_dtype,
1384
+ actual_shape,
1385
+ row["dtype"],
1386
+ row["shape"],
1387
+ )
1388
+ )
1389
+ if row["nbytes"] > self.staging_bytes:
1390
+ raise StagingLimitExceeded(
1391
+ "tensor %r needs %d staged bytes, above the %d "
1392
+ "staging_bytes bound"
1393
+ % (
1394
+ row["item"].index.fqn,
1395
+ row["nbytes"],
1396
+ self.staging_bytes,
1397
+ )
1398
+ )
1399
+ payload = _tensor_bytes(tensor)
1400
+ peak = max(peak, len(payload))
1401
+ if len(payload) != row["nbytes"]:
1402
+ raise SaveFailed(
1403
+ "entry %r produced %d bytes, header declared %d"
1404
+ % (row["entry"], len(payload), row["nbytes"])
1405
+ )
1406
+ container += payload
1407
+ infos.append(
1408
+ StorageInfo(
1409
+ entry=row["entry"],
1410
+ dtype=row["dtype"],
1411
+ shape=row["shape"],
1412
+ storage_offsets=row["offsets"],
1413
+ lengths=row["lengths"],
1414
+ content_root=content_root(payload),
1415
+ )
1416
+ )
1417
+
1418
+ shards, axis = self._topology
1419
+ storage_name = self._allocate_storage_name()
1420
+ service = self._get_service()
1421
+ started = time.monotonic()
1422
+ write_report = service.write_checkpoint(
1423
+ storage_name,
1424
+ container,
1425
+ shards=shards,
1426
+ axis=axis,
1427
+ strategy=self.shard_strategy,
1428
+ )
1429
+ self._stats["checkpoint_write_ms"] = (time.monotonic() - started) * 1000.0
1430
+ self._stats["container_bytes"] = len(container)
1431
+ manifest_value = write_report.manifest.to_value()
1432
+ self._verify_report(
1433
+ {
1434
+ "complete": write_report.complete,
1435
+ "tensors": manifest_value["tensors"],
1436
+ },
1437
+ manifest_value["tensors"],
1438
+ infos,
1439
+ storage_name,
1440
+ )
1441
+ self._storage_name = storage_name
1442
+ self._manifest = {
1443
+ "manifest_root": write_report.manifest_root,
1444
+ "manifest": manifest_value,
1445
+ }
1446
+ self._stats["entries"] = len(infos)
1447
+ self._stats["tensor_bytes"] = sum(
1448
+ int(t["element_bytes"]) for t in manifest_value["tensors"]
1449
+ )
1450
+ self._stats["staging_peak_bytes"] = peak
1451
+
1452
+ results = []
1453
+ for row, info in zip(staged, infos):
1454
+ results.append(_write_result(row["item"], info, row["nbytes"]))
1455
+ with self._lock:
1456
+ self._results = results
1457
+ self._entries = {info.entry: info for info in infos}
1458
+ return results
1459
+
1460
+ def _verify_report(self, report, manifest_tensors, infos, storage_name):
1461
+ tensors = manifest_tensors
1462
+ if not isinstance(tensors, list) or not tensors:
1463
+ raise SaveFailed(
1464
+ "checkpoint manifest for %r has no tensor list" % storage_name
1465
+ )
1466
+ by_name = {row["name"]: row for row in tensors}
1467
+ if set(by_name) != {info.entry for info in infos}:
1468
+ raise SaveFailed(
1469
+ "checkpoint %r stored tensors %r, expected %r"
1470
+ % (
1471
+ storage_name,
1472
+ sorted(by_name),
1473
+ sorted(info.entry for info in infos),
1474
+ )
1475
+ )
1476
+ for info in infos:
1477
+ row = by_name[info.entry]
1478
+ if row.get("dtype") != _canonical_dtype(info.dtype):
1479
+ raise ExactnessError(
1480
+ "entry %r dtype %r != canonical %r"
1481
+ % (info.entry, row.get("dtype"), _canonical_dtype(info.dtype))
1482
+ )
1483
+ if tuple(int(x) for x in row.get("shape", [])) != info.shape:
1484
+ raise ExactnessError(
1485
+ "entry %r shape %r != %r"
1486
+ % (info.entry, row.get("shape"), info.shape)
1487
+ )
1488
+ stored = row.get("content_root")
1489
+ if stored != info.content_root.hex():
1490
+ raise ExactnessError(
1491
+ "entry %r content root %s != the bytes written (%s)"
1492
+ % (info.entry, stored, info.content_root.hex())
1493
+ )
1494
+ if report.get("complete") is not True:
1495
+ raise SaveFailed(
1496
+ "checkpoint %r is not complete" % storage_name
1497
+ )
1498
+
1499
+
1500
+ def _write_result(item, info, nbytes):
1501
+ _require_torch("building a DCP WriteResult")
1502
+ from torch.distributed.checkpoint.storage import WriteResult as _WriteResult
1503
+
1504
+ return _WriteResult(
1505
+ index=item.index, size_in_bytes=int(nbytes), storage_data=info
1506
+ )
1507
+
1508
+
1509
+ # ---------------------------------------------------------------------------
1510
+ # Reader
1511
+
1512
+
1513
+ class _RestoredHandle:
1514
+ """One memfd-backed restored tensor plus its recorded metadata."""
1515
+
1516
+ __slots__ = ("entry", "buffer", "info")
1517
+
1518
+ def __init__(self, entry, buffer, info):
1519
+ self.entry = entry
1520
+ self.buffer = buffer
1521
+ self.info = info
1522
+
1523
+
1524
+ class WeftStorageReader(_StorageReaderBase):
1525
+ """DCP ``StorageReader`` over a WeftFS checkpoint.
1526
+
1527
+ The checkpoint manifest (``weft.checkpoint.v1``) is the authority for tensor
1528
+ identity. If ``/checkpoints/<name>/dcp.metadata`` exists it supplies the DCP
1529
+ protocol metadata and is verified against the manifest; otherwise metadata is
1530
+ synthesised from the manifest itself (``metadata_source == "manifest"``,
1531
+ exact for plain-integer/packed safetensors checkpoints written by the Rust
1532
+ CLI).
1533
+ """
1534
+
1535
+ def __init__(
1536
+ self,
1537
+ path,
1538
+ *,
1539
+ store_root,
1540
+ weftfs_cli=None,
1541
+ timeout=DEFAULT_TIMEOUT_SECONDS,
1542
+ allow_manifest_synthesis=True,
1543
+ import_restored=False,
1544
+ ):
1545
+ self._path = _validate_checkpoint_id(path)
1546
+ self.store_root = Path(store_root)
1547
+ self.allow_manifest_synthesis = bool(allow_manifest_synthesis)
1548
+ # Restore is read-only in the store by default. Importing a second,
1549
+ # exact copy into ``/dcp-reads/...`` is opt-in because it is a durable
1550
+ # store mutation.
1551
+ self.import_restored = bool(import_restored)
1552
+ if self.import_restored:
1553
+ raise DcpError(
1554
+ "import_restored is no longer supported: the checkpoint service "
1555
+ "restores directly into caller buffers and never materializes a "
1556
+ "container to import"
1557
+ )
1558
+ self._store = _Storage(self.store_root, cli=weftfs_cli, timeout=timeout)
1559
+ self._store.requires_store()
1560
+ self._service_cli = _default_cli(weftfs_cli)
1561
+ self._timeout = float(timeout)
1562
+ self._service = None
1563
+ self._storage_name = None
1564
+ self._manifest = None
1565
+ self._metadata = None
1566
+ self._metadata_source = None
1567
+ self._storage_data = None
1568
+ self._stats = {"restores": 0, "entries_restored": 0, "restored_bytes": 0}
1569
+ self._lock = threading.RLock()
1570
+
1571
+ # -- DCP interface ------------------------------------------------------
1572
+
1573
+ @classmethod
1574
+ def validate_checkpoint_id(cls, checkpoint_id) -> bool:
1575
+ try:
1576
+ _validate_checkpoint_id(checkpoint_id)
1577
+ return True
1578
+ except (InvalidCheckpointId, TypeError):
1579
+ return False
1580
+
1581
+ @property
1582
+ def checkpoint_id(self):
1583
+ return self._path
1584
+
1585
+ @property
1586
+ def storage_name(self):
1587
+ return self._storage_name
1588
+
1589
+ def _get_service(self):
1590
+ """One helper process for every read of this reader."""
1591
+ if self._service is None:
1592
+ self._service = CheckpointService(
1593
+ self.store_root, cli=self._service_cli, timeout=self._timeout
1594
+ )
1595
+ return self._service
1596
+
1597
+ def close(self):
1598
+ if self._service is not None:
1599
+ self._service.close()
1600
+ self._service = None
1601
+
1602
+ def __enter__(self):
1603
+ return self
1604
+
1605
+ def __exit__(self, *exc):
1606
+ self.close()
1607
+ return False
1608
+
1609
+ @property
1610
+ def metadata_source(self):
1611
+ return self._metadata_source
1612
+
1613
+ @property
1614
+ def stats(self):
1615
+ payload = dict(self._stats)
1616
+ payload.update(
1617
+ {
1618
+ "checkpoint_id": self._path,
1619
+ "storage_name": self._storage_name,
1620
+ "metadata_source": self._metadata_source,
1621
+ "store_root": os.fspath(self.store_root),
1622
+ "store_command_ms": self._store.command_ms,
1623
+ "store_command_calls": self._store.calls,
1624
+ "service": (
1625
+ None if self._service is None else dict(self._service.stats)
1626
+ ),
1627
+ }
1628
+ )
1629
+ return payload
1630
+
1631
+ def reset(self, checkpoint_id=None):
1632
+ with self._lock:
1633
+ if checkpoint_id is not None:
1634
+ self._path = _validate_checkpoint_id(checkpoint_id)
1635
+ self._storage_name = None
1636
+ self._manifest = None
1637
+ self._metadata = None
1638
+ self._metadata_source = None
1639
+ self._storage_data = None
1640
+ self._stats = {"restores": 0, "entries_restored": 0, "restored_bytes": 0}
1641
+ return None
1642
+
1643
+ def _resolve_storage_name(self):
1644
+ names = set(self._get_service().list_checkpoints())
1645
+ highest = 1
1646
+ chosen = None
1647
+ for name in names:
1648
+ generation = _generation(name, self._path)
1649
+ if generation is not None and generation >= highest:
1650
+ highest = generation
1651
+ chosen = name
1652
+ if chosen is None:
1653
+ raise MetadataMissing(
1654
+ "checkpoint %r does not exist in %s" % (self._path, self.store_root)
1655
+ )
1656
+ return chosen
1657
+
1658
+ def read_metadata(self, *args, **kwargs):
1659
+ _require_torch("reading DCP metadata")
1660
+ with self._lock:
1661
+ if self._metadata is not None:
1662
+ return self._metadata
1663
+ storage_name = self._resolve_storage_name()
1664
+ service = self._get_service()
1665
+ # A reader is a long-lived object; refresh so commits made by
1666
+ # another process are visible before the manifest is read.
1667
+ service.refresh()
1668
+ descriptor = service.manifest(storage_name)
1669
+ manifest = descriptor.to_value()
1670
+ show = {
1671
+ "manifest_root": descriptor.manifest_root,
1672
+ "manifest": manifest,
1673
+ }
1674
+ manifest_rows = {
1675
+ row["name"]: row for row in manifest.get("tensors", [])
1676
+ }
1677
+ metadata_path = "/checkpoints/%s/%s" % (storage_name, DCP_METADATA_NAME)
1678
+ blob = None
1679
+ exists, _size = service.stat_file(metadata_path)
1680
+ if exists:
1681
+ with service.read_file(metadata_path) as view:
1682
+ blob = view.bytes
1683
+ if blob is not None:
1684
+ try:
1685
+ value = wcb.decode(blob)
1686
+ except Exception as exc:
1687
+ raise InvalidMetadata(
1688
+ "stored DCP metadata at %s is not canonical WCB: %s"
1689
+ % (metadata_path, exc)
1690
+ ) from exc
1691
+ metadata = _metadata_from_value(value, storage_name=storage_name)
1692
+ source = "dcp"
1693
+ self._validate_against_manifest(metadata, manifest_rows)
1694
+ else:
1695
+ if not self.allow_manifest_synthesis:
1696
+ raise MetadataMissing(
1697
+ "checkpoint %r has no %s and manifest synthesis is "
1698
+ "disabled" % (storage_name, DCP_METADATA_NAME)
1699
+ )
1700
+ metadata = _metadata_from_manifest(
1701
+ show,
1702
+ checkpoint_id=self._path,
1703
+ storage_name=storage_name,
1704
+ )
1705
+ source = "manifest"
1706
+ metadata.storage_meta = _TorchStorageMeta(
1707
+ checkpoint_id=self._path,
1708
+ save_id=storage_name,
1709
+ load_id=uuid.uuid4().hex,
1710
+ )
1711
+ self._storage_name = storage_name
1712
+ self._manifest = show
1713
+ self._metadata = metadata
1714
+ self._metadata_source = source
1715
+ self._storage_data = metadata.storage_data
1716
+ return metadata
1717
+
1718
+ def set_up_storage_reader(self, metadata, is_coordinator, *args, **kwargs):
1719
+ if metadata.storage_data is None:
1720
+ raise InvalidMetadata("metadata.storage_data is None")
1721
+ with self._lock:
1722
+ self._metadata = metadata
1723
+ self._storage_data = metadata.storage_data
1724
+ if self._storage_name is None:
1725
+ # Metadata supplied by the caller rather than read_metadata().
1726
+ self._storage_name = getattr(
1727
+ getattr(metadata, "storage_meta", None), "save_id", None
1728
+ )
1729
+ return None
1730
+
1731
+ def prepare_local_plan(self, plan):
1732
+ return plan
1733
+
1734
+ def prepare_global_plan(self, plans):
1735
+ return plans
1736
+
1737
+ def read_data(self, plan, planner):
1738
+ future = _TorchFuture()
1739
+ try:
1740
+ self._read_plan(plan, planner)
1741
+ except BaseException as exc:
1742
+ future.set_exception(exc)
1743
+ return future
1744
+ future.set_result(None)
1745
+ return future
1746
+
1747
+ # -- helpers ------------------------------------------------------------
1748
+
1749
+ def _validate_against_manifest(self, metadata, manifest_rows):
1750
+ for info in metadata.storage_data.values():
1751
+ row = manifest_rows.get(info.entry)
1752
+ if row is None:
1753
+ raise InvalidMetadata(
1754
+ "DCP metadata names entry %r, absent from the checkpoint "
1755
+ "manifest" % info.entry
1756
+ )
1757
+ if row.get("content_root") != info.content_root.hex():
1758
+ raise ExactnessError(
1759
+ "DCP metadata content root for %r (%s) != manifest (%s)"
1760
+ % (info.entry, info.content_root.hex(), row.get("content_root"))
1761
+ )
1762
+ if tuple(int(x) for x in row.get("shape", [])) != info.shape:
1763
+ raise ExactnessError(
1764
+ "DCP metadata shape for %r (%r) != manifest (%r)"
1765
+ % (info.entry, info.shape, row.get("shape"))
1766
+ )
1767
+ expected_canonical = _canonical_dtype(info.dtype)
1768
+ if row.get("dtype") != expected_canonical:
1769
+ raise ExactnessError(
1770
+ "DCP metadata dtype for %r (%s) != manifest (%s)"
1771
+ % (info.entry, expected_canonical, row.get("dtype"))
1772
+ )
1773
+
1774
+ def _restore(self, entries, *, topology=None, axis=0):
1775
+ """Restore entries straight into caller-owned memfds.
1776
+
1777
+ Returns ``{entry: _RestoredHandle}``. The handle owns a memfd-backed
1778
+ mapping; callers must ``close()`` it (or pass it to
1779
+ :meth:`_release_handles`). No file is written and the store is never
1780
+ mutated.
1781
+ """
1782
+ if self._storage_name is None:
1783
+ raise DcpError("read_data() before read_metadata()")
1784
+ service = self._get_service()
1785
+ manifest_rows = {
1786
+ row["name"]: row for row in self._manifest["manifest"].get("tensors", [])
1787
+ }
1788
+ unknown = [entry for entry in entries if entry not in manifest_rows]
1789
+ if unknown:
1790
+ raise InvalidMetadata(
1791
+ "requested entries %r are not in checkpoint %r"
1792
+ % (sorted(unknown), self._storage_name)
1793
+ )
1794
+ topology_arg = None if topology is None else (int(topology), int(axis))
1795
+ found = {}
1796
+ try:
1797
+ for entry in sorted(entries):
1798
+ info = self._entry_info(entry)
1799
+ try:
1800
+ buffer = service.restore_tensor(
1801
+ self._storage_name,
1802
+ entry,
1803
+ topology=topology_arg,
1804
+ expected_size=int(manifest_rows[entry]["element_bytes"]),
1805
+ )
1806
+ except ServiceError as exc:
1807
+ raise self._translate(exc) from exc
1808
+ if tuple(buffer.shape) != tuple(info.shape):
1809
+ buffer.close()
1810
+ raise ExactnessError(
1811
+ "entry %r restored with shape %r, expected %r"
1812
+ % (entry, tuple(buffer.shape), info.shape)
1813
+ )
1814
+ actual = content_root(buffer.view)
1815
+ if actual != info.content_root:
1816
+ buffer.close()
1817
+ raise ExactnessError(
1818
+ "entry %r restored content root %s != recorded %s"
1819
+ % (entry, actual.hex(), info.content_root.hex())
1820
+ )
1821
+ found[entry] = _RestoredHandle(entry, buffer, info)
1822
+ except BaseException:
1823
+ self._release_handles(found)
1824
+ raise
1825
+ with self._lock:
1826
+ self._stats["restores"] += 1
1827
+ self._stats["entries_restored"] += len(found)
1828
+ self._stats["restored_bytes"] += sum(
1829
+ handle.buffer.size for handle in found.values()
1830
+ )
1831
+ return found
1832
+
1833
+ @staticmethod
1834
+ def _release_handles(handles):
1835
+ for handle in handles.values():
1836
+ handle.buffer.close()
1837
+
1838
+ @staticmethod
1839
+ def _translate(exc):
1840
+ """Map a typed service error onto the adapter's error hierarchy."""
1841
+ message = str(exc)
1842
+ if exc.class_name == "NotFound":
1843
+ return MetadataMissing(message)
1844
+ if exc.class_name in ("Corrupt", "Malformed", "NonCanonical"):
1845
+ return ExactnessError(message)
1846
+ if exc.class_name in ("LimitExceeded", "BudgetExceeded"):
1847
+ return StagingLimitExceeded(message)
1848
+ return DcpError(message)
1849
+
1850
+ def _entry_info(self, entry):
1851
+ for info in self._storage_data.values():
1852
+ if info.entry == entry:
1853
+ return info
1854
+ raise InvalidMetadata("no storage entry %r in DCP metadata" % entry)
1855
+
1856
+ def restore_entry(self, entry, *, topology=None, axis=0):
1857
+ """Restore exactly one manifest entry, optionally through a topology.
1858
+
1859
+ The service decodes the entry straight into a caller-owned memfd and
1860
+ verifies the shard roots, the whole-tensor logical root (and the
1861
+ re-layout topology root when requested). ``data`` is an explicit copy
1862
+ of those bytes; the zero-copy path is ``WeftStorageReader.load``.
1863
+ """
1864
+ if self._metadata is None:
1865
+ self.read_metadata()
1866
+ handles = self._restore([entry], topology=topology, axis=axis)
1867
+ handle = handles[entry]
1868
+ try:
1869
+ buffer = handle.buffer
1870
+ return RestoredEntry(
1871
+ entry=entry,
1872
+ data=buffer.bytes,
1873
+ dtype=handle.info.dtype,
1874
+ shape=tuple(buffer.shape),
1875
+ content_root=handle.info.content_root,
1876
+ topology_root=buffer.topology_root,
1877
+ shards_read=buffer.shards_read,
1878
+ store_bytes_read=buffer.store_bytes_read,
1879
+ )
1880
+ finally:
1881
+ handle.buffer.close()
1882
+
1883
+ def _read_plan(self, plan, planner):
1884
+ _require_torch("reading checkpoint data")
1885
+ if not plan.items:
1886
+ return
1887
+ storage = self._storage_data
1888
+ if storage is None:
1889
+ raise DcpError("read_data() before read_metadata()")
1890
+ items_by_entry = {}
1891
+ for read_item in plan.items:
1892
+ key = (
1893
+ read_item.storage_index.fqn,
1894
+ None
1895
+ if read_item.storage_index.offset is None
1896
+ else tuple(int(x) for x in read_item.storage_index.offset),
1897
+ )
1898
+ info = None
1899
+ for stored_key, stored_info in storage.items():
1900
+ stored_offset = (
1901
+ None
1902
+ if stored_key.offset is None
1903
+ else tuple(int(x) for x in stored_key.offset)
1904
+ )
1905
+ if stored_key.fqn == key[0] and stored_offset == key[1]:
1906
+ info = stored_info
1907
+ break
1908
+ if info is None:
1909
+ raise InvalidMetadata(
1910
+ "load plan requests storage index %r, absent from DCP metadata"
1911
+ % (read_item.storage_index,)
1912
+ )
1913
+ items_by_entry.setdefault(info.entry, []).append((read_item, info))
1914
+
1915
+ entries = sorted(items_by_entry)
1916
+ handles = self._restore(entries)
1917
+ try:
1918
+ for entry in entries:
1919
+ info = items_by_entry[entry][0][1]
1920
+ handle = handles[entry]
1921
+ buffer = handle.buffer
1922
+ for read_item, _info in items_by_entry[entry]:
1923
+ item_type = getattr(read_item.type, "name", str(read_item.type))
1924
+ if item_type == "BYTE_IO":
1925
+ planner.load_bytes(read_item, io.BytesIO(buffer.bytes))
1926
+ continue
1927
+ dtype = _torch_dtype(info.dtype)
1928
+ if buffer.size:
1929
+ # Zero-copy: torch views the caller-owned memfd.
1930
+ base = _TORCH.frombuffer(buffer.view, dtype=_TORCH.uint8)
1931
+ base = base.view(dtype).reshape(tuple(info.shape))
1932
+ else:
1933
+ base = _TORCH.empty(tuple(info.shape), dtype=dtype)
1934
+ offsets = tuple(int(x) for x in read_item.storage_offsets)
1935
+ lengths = tuple(int(x) for x in read_item.lengths)
1936
+ narrowed = _narrow(base, offsets, lengths)
1937
+ target = planner.resolve_tensor(read_item).detach()
1938
+ if tuple(target.shape) != tuple(narrowed.shape):
1939
+ raise ExactnessError(
1940
+ "target %r shape %r != stored slice %r"
1941
+ % (
1942
+ read_item.dest_index.fqn,
1943
+ tuple(target.shape),
1944
+ tuple(narrowed.shape),
1945
+ )
1946
+ )
1947
+ # DCP allocates the destination tensor, so this copy is the
1948
+ # interface's own contract, not the adapter's staging.
1949
+ target.copy_(narrowed)
1950
+ planner.commit_tensor(read_item, target)
1951
+ del narrowed, base
1952
+ finally:
1953
+ self._release_handles(handles)
1954
+
1955
+
1956
+ # ---------------------------------------------------------------------------
1957
+ # Async save
1958
+
1959
+
1960
+ class AsyncSaveHandle:
1961
+ """Handle returned by :func:`save_checkpoint_async`.
1962
+
1963
+ ``result()`` blocks and re-raises any save error on the caller's thread;
1964
+ ``done()``/``cancel()`` mirror ``concurrent.futures.Future``. The returned
1965
+ metadata object is the same one ``torch.distributed.checkpoint.save``
1966
+ returns.
1967
+ """
1968
+
1969
+ def __init__(self, future, writer, executor):
1970
+ self._future = future
1971
+ self._writer = writer
1972
+ self._executor = executor
1973
+ self.metadata = None
1974
+
1975
+ def result(self, timeout=None):
1976
+ metadata = self._future.result(timeout=timeout)
1977
+ self.metadata = metadata
1978
+ return metadata
1979
+
1980
+ def done(self):
1981
+ return self._future.done()
1982
+
1983
+ def cancel(self):
1984
+ return self._future.cancel()
1985
+
1986
+ def add_done_callback(self, callback):
1987
+ return self._future.add_done_callback(callback)
1988
+
1989
+ @property
1990
+ def writer(self):
1991
+ return self._writer
1992
+
1993
+ @property
1994
+ def stats(self):
1995
+ return self._writer.stats
1996
+
1997
+ def wait(self, timeout=None):
1998
+ return self.result(timeout=timeout)
1999
+
2000
+ def __enter__(self):
2001
+ return self
2002
+
2003
+ def __exit__(self, exc_type, exc, tb):
2004
+ if exc_type is None:
2005
+ self.result()
2006
+ self._executor.shutdown(wait=True, cancel_futures=True)
2007
+ return False
2008
+
2009
+
2010
+ def save_checkpoint_async(
2011
+ state_dict,
2012
+ *,
2013
+ checkpoint_id,
2014
+ store_root,
2015
+ topology=1,
2016
+ axis=None,
2017
+ shard_strategy="zstd",
2018
+ staging_bytes=DEFAULT_STAGING_BYTES,
2019
+ overwrite=True,
2020
+ init_store=False,
2021
+ planner=None,
2022
+ weftfs_cli=None,
2023
+ timeout=DEFAULT_TIMEOUT_SECONDS,
2024
+ max_workers=1,
2025
+ ):
2026
+ """Run ``torch.distributed.checkpoint.save`` on a worker thread.
2027
+
2028
+ The encode/store work (resolving tensors, copying them to host memory,
2029
+ writing the staging safetensors container, running ``weftfs checkpoint
2030
+ write``, fsync, metadata publication) happens on a
2031
+ :class:`~concurrent.futures.ThreadPoolExecutor` worker, so a training loop
2032
+ can keep stepping while the checkpoint is stored.
2033
+
2034
+ This still **copies every tensor through host memory** (one tensor at a time,
2035
+ bounded by ``staging_bytes``); it is not a GPU-direct or zero-copy path. The
2036
+ worker count is bounded (default 1) and every worker uses the same hard
2037
+ staging bound.
2038
+ """
2039
+ _require_torch("save_checkpoint_async")
2040
+ import torch.distributed.checkpoint as dcp
2041
+
2042
+ workers = int(max_workers)
2043
+ if workers < 1 or workers > 4:
2044
+ raise ValueError("max_workers must be between 1 and 4")
2045
+ writer = WeftStorageWriter(
2046
+ checkpoint_id,
2047
+ store_root=store_root,
2048
+ topology=topology,
2049
+ axis=axis,
2050
+ shard_strategy=shard_strategy,
2051
+ staging_bytes=staging_bytes,
2052
+ overwrite=overwrite,
2053
+ init_store=init_store,
2054
+ weftfs_cli=weftfs_cli,
2055
+ timeout=timeout,
2056
+ )
2057
+ executor = ThreadPoolExecutor(
2058
+ max_workers=workers, thread_name_prefix="weft-dcp-save"
2059
+ )
2060
+
2061
+ def _run():
2062
+ try:
2063
+ return dcp.save(
2064
+ state_dict,
2065
+ storage_writer=writer,
2066
+ planner=planner,
2067
+ no_dist=True,
2068
+ )
2069
+ finally:
2070
+ executor.shutdown(wait=False)
2071
+
2072
+ future = executor.submit(_run)
2073
+ return AsyncSaveHandle(future, writer, executor)
2074
+
2075
+
2076
+ # ---------------------------------------------------------------------------
2077
+ # Convenience helpers used by tests, docs, and the bench harness
2078
+
2079
+
2080
+ def init_store(store_root, *, weftfs_cli=None, timeout=DEFAULT_TIMEOUT_SECONDS):
2081
+ """Create a WeftFS store at ``store_root`` if it does not exist."""
2082
+ store = _Storage(store_root, cli=weftfs_cli, timeout=timeout)
2083
+ store.init()
2084
+ store.requires_store()
2085
+ return Path(store_root)
2086
+
2087
+
2088
+ def verify_checkpoint(
2089
+ store_root,
2090
+ name,
2091
+ *,
2092
+ full=False,
2093
+ weftfs_cli=None,
2094
+ timeout=DEFAULT_TIMEOUT_SECONDS,
2095
+ ):
2096
+ """Run ``checkpoint verify`` (and optionally ``verify --full``).
2097
+
2098
+ Returns ``{"checkpoint": ..., "tensors_verified": N, "full": {...} | None}``.
2099
+ Every failure is a :class:`StoreCommandError`; nothing is swallowed.
2100
+ """
2101
+ store = _Storage(store_root, cli=weftfs_cli, timeout=timeout)
2102
+ store.requires_store()
2103
+ result = {"checkpoint": name, "tensors_verified": None, "full": None}
2104
+ payload = store.checkpoint_verify(name)
2105
+ result["tensors_verified"] = payload.get("tensors_verified")
2106
+ if full:
2107
+ result["full"] = store.verify_full()
2108
+ return result