distributed-gguf-runtime: add CMake skeleton, gRPC harness, split-GGUF provisioning, performance contracts
DGR-019 Lock alpha/beta performance contracts (evidence + contract framework) DGR-020 Run controlled whole-model GGUF baseline (benchmark results & contracts) DGR-024 Real generated-gRPC protocol harness (shard_runtime_server.py + tests) DGR-026 split-GGUF provisioning outside /home (provision script + manifest + tests) DGR-028 Numbered patch-stack apply & verify (llama_cpp_dependency.py + UPSTREAM_LOCK.json) DGR-029 Native CMake skeleton + deterministic CPU lane (UPSTREAM_LOCK.json + cmake gating) New modules: packages/node/meshnet_node/dgr_performance/ — performance contract framework packages/node/meshnet_node/split_gguf/ — split-GGUF manifest & provisioning scripts/provision_split_gguf.py — artifact provisioning CLI tests/test_dgr_performance_contract.py — contract validation tests tests/test_split_gguf_manifest.py — manifest tests tests/test_split_gguf_provision.py — provisioning tests tests/test_shard_runtime_harness.py — gRPC harness tests
This commit is contained in:
323
packages/node/meshnet_node/split_gguf/manifest.py
Normal file
323
packages/node/meshnet_node/split_gguf/manifest.py
Normal file
@@ -0,0 +1,323 @@
|
||||
"""Exact split-GGUF artifact manifest (DGR-026).
|
||||
|
||||
A split-GGUF artifact is only as trustworthy as its binding to the whole-model
|
||||
artifact it was cut from. This module defines the manifest that makes a set of
|
||||
split files an *exact*, checkable artifact rather than a pile of files someone
|
||||
happened to name plausibly: it pins the source artifact's own content hash, the
|
||||
tokenizer/revision the splits were tokenized against, and — per split — the
|
||||
exact file name, size, cryptographic hash, and its range/role within the
|
||||
source.
|
||||
|
||||
Quantization and split topology (how many splits, which layers each one
|
||||
covers) are recipe inputs recorded on the manifest, never constants in this
|
||||
module. A manifest with two splits and one with twenty are both valid; nothing
|
||||
here assumes a stage count or a fixed layer range. Provisioning
|
||||
(:mod:`meshnet_node.split_gguf.provision`) consumes whatever this manifest
|
||||
declares.
|
||||
|
||||
This module mirrors two existing conventions rather than inventing new ones:
|
||||
the DGR-017 pinned-shard manifest shape (`meshnet_node.glm_alpha.manifest`) for
|
||||
per-file identity records, and the DGR-003 `DerivativeBinding` range/source
|
||||
convention (`meshnet_node.runtime_recipe`) for binding a split to its source by
|
||||
digest and half-open layer range.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, Mapping
|
||||
|
||||
SPLIT_ARTIFACT_MANIFEST_SCHEMA_VERSION = 1
|
||||
|
||||
_SHA256_RE = re.compile(r"\A[0-9a-f]{64}\Z")
|
||||
_REVISION_RE = re.compile(r"\A[0-9a-f]{40}\Z")
|
||||
|
||||
|
||||
class SplitArtifactManifestError(ValueError):
|
||||
"""Raised when a split-GGUF manifest is missing, malformed, or self-inconsistent."""
|
||||
|
||||
|
||||
def canonical_sha256(value: Any) -> str:
|
||||
"""SHA-256 over canonical JSON — the repository's digest convention."""
|
||||
payload = json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
|
||||
return hashlib.sha256(payload.encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def _require_mapping(value: Any, what: str, origin: str) -> Mapping[str, Any]:
|
||||
if not isinstance(value, Mapping):
|
||||
raise SplitArtifactManifestError(
|
||||
f"{what} in {origin} must be a JSON object, got {type(value).__name__}"
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def _require_text(value: Any, what: str, origin: str) -> str:
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
raise SplitArtifactManifestError(f"{what} in {origin} must be a non-empty string")
|
||||
return value
|
||||
|
||||
|
||||
def _require_int(value: Any, what: str, origin: str, minimum: int = 0) -> int:
|
||||
if not isinstance(value, int) or isinstance(value, bool):
|
||||
raise SplitArtifactManifestError(
|
||||
f"{what} in {origin} must be an integer, got {type(value).__name__}"
|
||||
)
|
||||
if value < minimum:
|
||||
raise SplitArtifactManifestError(f"{what} in {origin} must be >= {minimum}, got {value}")
|
||||
return value
|
||||
|
||||
|
||||
def _require_sha256(value: Any, what: str, origin: str) -> str:
|
||||
text = _require_text(value, what, origin)
|
||||
if not _SHA256_RE.match(text):
|
||||
raise SplitArtifactManifestError(
|
||||
f"{what} in {origin} must be a lowercase 64-character hex SHA-256, got {text!r}"
|
||||
)
|
||||
return text
|
||||
|
||||
|
||||
def _require_revision(value: Any, what: str, origin: str) -> str:
|
||||
text = _require_text(value, what, origin)
|
||||
if not _REVISION_RE.match(text):
|
||||
raise SplitArtifactManifestError(
|
||||
f"{what} in {origin} must be a full 40-character commit revision, got {text!r}; "
|
||||
"a branch name, tag, or short SHA is not an immutable pin"
|
||||
)
|
||||
return text
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SourceArtifact:
|
||||
"""The whole-model artifact every split in this manifest was cut from."""
|
||||
|
||||
artifact_id: str
|
||||
repo_id: str
|
||||
revision: str
|
||||
sha256: str
|
||||
size_bytes: int
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"artifact_id": self.artifact_id,
|
||||
"repo_id": self.repo_id,
|
||||
"revision": self.revision,
|
||||
"sha256": self.sha256,
|
||||
"size_bytes": self.size_bytes,
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TokenizerRef:
|
||||
"""The exact tokenizer/revision the split artifact's routing assumes."""
|
||||
|
||||
repo_id: str
|
||||
revision: str
|
||||
sha256: str
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {"repo_id": self.repo_id, "revision": self.revision, "sha256": self.sha256}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SplitFile:
|
||||
"""One split-GGUF file: name, size, hash, and its role/range in the source.
|
||||
|
||||
`shard_start`/`shard_end` are half-open (end-exclusive), matching the
|
||||
`DerivativeBinding` protocol convention in `meshnet_node.runtime_recipe`.
|
||||
They are optional because not every split is a layer range — a shared
|
||||
embedding or tokenizer-adjacent split may carry only a `role` label — but
|
||||
when present they must describe a real, non-empty range.
|
||||
"""
|
||||
|
||||
name: str
|
||||
size_bytes: int
|
||||
sha256: str
|
||||
role: str
|
||||
url: str = ""
|
||||
shard_start: int | None = None
|
||||
shard_end: int | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if (self.shard_start is None) != (self.shard_end is None):
|
||||
raise SplitArtifactManifestError(
|
||||
f"split {self.name!r} must declare both shard_start and shard_end, or neither"
|
||||
)
|
||||
if self.shard_start is not None and self.shard_end is not None:
|
||||
if self.shard_start < 0:
|
||||
raise SplitArtifactManifestError(f"split {self.name!r} shard_start must be >= 0")
|
||||
if self.shard_end <= self.shard_start:
|
||||
raise SplitArtifactManifestError(
|
||||
f"split {self.name!r} shard_end ({self.shard_end}) must be greater than "
|
||||
f"shard_start ({self.shard_start}); an empty range covers nothing"
|
||||
)
|
||||
|
||||
@property
|
||||
def has_range(self) -> bool:
|
||||
return self.shard_start is not None
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
doc: dict[str, Any] = {
|
||||
"name": self.name,
|
||||
"size_bytes": self.size_bytes,
|
||||
"sha256": self.sha256,
|
||||
"role": self.role,
|
||||
"url": self.url,
|
||||
}
|
||||
if self.has_range:
|
||||
doc["shard_start"] = self.shard_start
|
||||
doc["shard_end"] = self.shard_end
|
||||
return doc
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SplitArtifactManifest:
|
||||
"""A parsed, self-consistent exact split-GGUF artifact manifest."""
|
||||
|
||||
schema_version: int
|
||||
manifest_id: str
|
||||
manifest_version: str
|
||||
quantization: str
|
||||
source: SourceArtifact
|
||||
tokenizer: TokenizerRef
|
||||
total_bytes: int
|
||||
splits: tuple[SplitFile, ...]
|
||||
raw: Mapping[str, Any]
|
||||
origin: str = "<memory>"
|
||||
|
||||
@property
|
||||
def digest(self) -> str:
|
||||
"""Stable identity of this manifest, for binding into the DGR-003 recipe identity."""
|
||||
return canonical_sha256(self.raw)
|
||||
|
||||
def split(self, name: str) -> SplitFile:
|
||||
for split in self.splits:
|
||||
if split.name == name:
|
||||
return split
|
||||
raise SplitArtifactManifestError(f"split {name!r} is not in {self.origin}")
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return dict(self.raw)
|
||||
|
||||
|
||||
def _parse_splits(raw: Any, expected_total: int, origin: str) -> tuple[SplitFile, ...]:
|
||||
if not isinstance(raw, list) or not raw:
|
||||
raise SplitArtifactManifestError(f"'splits' in {origin} must be a non-empty JSON array")
|
||||
|
||||
splits: list[SplitFile] = []
|
||||
seen_names: set[str] = set()
|
||||
seen_sha: set[str] = set()
|
||||
for position, entry in enumerate(raw):
|
||||
item = _require_mapping(entry, f"splits[{position}]", origin)
|
||||
name = _require_text(item.get("name"), f"splits[{position}].name", origin)
|
||||
if name in seen_names:
|
||||
raise SplitArtifactManifestError(f"duplicate split name {name!r} in {origin}")
|
||||
seen_names.add(name)
|
||||
|
||||
size_bytes = _require_int(item.get("size_bytes"), f"splits[{name}].size_bytes", origin, minimum=1)
|
||||
sha256 = _require_sha256(item.get("sha256"), f"splits[{name}].sha256", origin)
|
||||
if sha256 in seen_sha:
|
||||
raise SplitArtifactManifestError(
|
||||
f"split {name!r} repeats SHA-256 {sha256}; two distinct splits cannot "
|
||||
"have the same content digest"
|
||||
)
|
||||
seen_sha.add(sha256)
|
||||
|
||||
role = _require_text(item.get("role"), f"splits[{name}].role", origin)
|
||||
url = item.get("url", "")
|
||||
if not isinstance(url, str):
|
||||
raise SplitArtifactManifestError(f"splits[{name}].url in {origin} must be a string")
|
||||
|
||||
shard_start = item.get("shard_start")
|
||||
shard_end = item.get("shard_end")
|
||||
if shard_start is not None:
|
||||
shard_start = _require_int(shard_start, f"splits[{name}].shard_start", origin, minimum=0)
|
||||
if shard_end is not None:
|
||||
shard_end = _require_int(shard_end, f"splits[{name}].shard_end", origin, minimum=1)
|
||||
|
||||
splits.append(
|
||||
SplitFile(
|
||||
name=name,
|
||||
size_bytes=size_bytes,
|
||||
sha256=sha256,
|
||||
role=role,
|
||||
url=url,
|
||||
shard_start=shard_start,
|
||||
shard_end=shard_end,
|
||||
)
|
||||
)
|
||||
|
||||
summed = sum(split.size_bytes for split in splits)
|
||||
if summed != expected_total:
|
||||
raise SplitArtifactManifestError(
|
||||
f"declared total_bytes {expected_total} in {origin} does not equal the sum of "
|
||||
f"the split sizes {summed}; the manifest is not self-consistent"
|
||||
)
|
||||
|
||||
return tuple(splits)
|
||||
|
||||
|
||||
def parse_split_artifact_manifest(data: Any, origin: str = "<memory>") -> SplitArtifactManifest:
|
||||
"""Validate an already-decoded split-artifact manifest document, failing closed."""
|
||||
doc = _require_mapping(data, "manifest root", origin)
|
||||
|
||||
schema_version = _require_int(doc.get("schema_version"), "'schema_version'", origin, minimum=1)
|
||||
if schema_version != SPLIT_ARTIFACT_MANIFEST_SCHEMA_VERSION:
|
||||
raise SplitArtifactManifestError(
|
||||
f"{origin} declares split-artifact manifest schema version {schema_version}, "
|
||||
f"but this reader understands version {SPLIT_ARTIFACT_MANIFEST_SCHEMA_VERSION}"
|
||||
)
|
||||
|
||||
manifest_id = _require_text(doc.get("manifest_id"), "'manifest_id'", origin)
|
||||
manifest_version = _require_text(doc.get("manifest_version"), "'manifest_version'", origin)
|
||||
quantization = _require_text(doc.get("quantization"), "'quantization'", origin)
|
||||
|
||||
source_doc = _require_mapping(doc.get("source"), "'source'", origin)
|
||||
source = SourceArtifact(
|
||||
artifact_id=_require_text(source_doc.get("artifact_id"), "source.artifact_id", origin),
|
||||
repo_id=_require_text(source_doc.get("repo_id"), "source.repo_id", origin),
|
||||
revision=_require_revision(source_doc.get("revision"), "source.revision", origin),
|
||||
sha256=_require_sha256(source_doc.get("sha256"), "source.sha256", origin),
|
||||
size_bytes=_require_int(source_doc.get("size_bytes"), "source.size_bytes", origin, minimum=1),
|
||||
)
|
||||
|
||||
tokenizer_doc = _require_mapping(doc.get("tokenizer"), "'tokenizer'", origin)
|
||||
tokenizer = TokenizerRef(
|
||||
repo_id=_require_text(tokenizer_doc.get("repo_id"), "tokenizer.repo_id", origin),
|
||||
revision=_require_revision(tokenizer_doc.get("revision"), "tokenizer.revision", origin),
|
||||
sha256=_require_sha256(tokenizer_doc.get("sha256"), "tokenizer.sha256", origin),
|
||||
)
|
||||
|
||||
total_bytes = _require_int(doc.get("total_bytes"), "'total_bytes'", origin, minimum=1)
|
||||
splits = _parse_splits(doc.get("splits"), total_bytes, origin)
|
||||
|
||||
return SplitArtifactManifest(
|
||||
schema_version=schema_version,
|
||||
manifest_id=manifest_id,
|
||||
manifest_version=manifest_version,
|
||||
quantization=quantization,
|
||||
source=source,
|
||||
tokenizer=tokenizer,
|
||||
total_bytes=total_bytes,
|
||||
splits=splits,
|
||||
raw=doc,
|
||||
origin=origin,
|
||||
)
|
||||
|
||||
|
||||
def load_split_artifact_manifest(path: Path) -> SplitArtifactManifest:
|
||||
"""Load and validate a split-artifact manifest from *path*."""
|
||||
try:
|
||||
raw = path.read_text(encoding="utf-8")
|
||||
except OSError as exc:
|
||||
raise SplitArtifactManifestError(f"cannot read split-artifact manifest {path}: {exc.strerror or exc}") from exc
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise SplitArtifactManifestError(
|
||||
f"{path} is not valid JSON: {exc.msg} at line {exc.lineno} column {exc.colno}"
|
||||
) from exc
|
||||
return parse_split_artifact_manifest(data, origin=str(path))
|
||||
Reference in New Issue
Block a user