返回 JoyAI-Echo
release_checkpoint.py
1 """Validation and resolution for the three public Echo 1.5 checkpoints."""
2
3 from __future__ import annotations
4
5 import json
6 from dataclasses import dataclass
7 from pathlib import Path
8 from typing import Any
9
10
11 MANIFEST_NAME = "checkpoint.json"
12 SCHEMA_VERSION = 1
13 FULL_DMD = "echo15_full_dmd"
14 FP8 = "echo15_fp8"
15 FP4 = "echo15_fp4"
16 RELEASE_CHECKPOINTS = {
17 FULL_DMD: "bf16",
18 FP8: "fp8",
19 FP4: "fp4",
20 }
21
22
23 @dataclass(frozen=True)
24 class ReleaseCheckpoint:
25 """Resolved files for one validated public release checkpoint."""
26
27 root: Path
28 name: str
29 precision: str
30 model_path: Path
31 modelopt_path: Path | None = None
32
33
34 def _load_manifest(path: Path) -> dict[str, Any]:
35 try:
36 payload = json.loads(path.read_text(encoding="utf-8"))
37 except json.JSONDecodeError as error:
38 raise ValueError(f"invalid checkpoint manifest {path}: {error}") from error
39 if not isinstance(payload, dict):
40 raise ValueError(f"checkpoint manifest must be a JSON object: {path}")
41 return payload
42
43
44 def _resolve_member(root: Path, value: object, *, label: str) -> Path:
45 if not isinstance(value, str) or not value.strip():
46 raise ValueError(
47 f"checkpoint manifest field files.{label} must be a relative path"
48 )
49 relative = Path(value)
50 if relative.is_absolute():
51 raise ValueError(f"checkpoint manifest field files.{label} must be relative")
52 resolved = (root / relative).resolve()
53 try:
54 resolved.relative_to(root)
55 except ValueError as error:
56 raise ValueError(
57 f"checkpoint manifest field files.{label} escapes its directory"
58 ) from error
59 if not resolved.is_file():
60 raise FileNotFoundError(f"checkpoint file not found: {resolved}")
61 return resolved
62
63
64 def resolve_release_checkpoint(path: str | Path) -> ReleaseCheckpoint:
65 """Resolve one of the three supported checkpoint directory formats.
66
67 Runtime callers provide one directory only. The manifest selects the loader
68 and any internal companion file, so precision and weight paths cannot drift.
69 """
70
71 root = Path(path).expanduser().resolve()
72 if not root.is_dir():
73 raise FileNotFoundError(f"release checkpoint directory not found: {root}")
74 manifest_path = root / MANIFEST_NAME
75 if not manifest_path.is_file():
76 raise FileNotFoundError(f"checkpoint manifest not found: {manifest_path}")
77 manifest = _load_manifest(manifest_path)
78
79 if manifest.get("schema_version") != SCHEMA_VERSION:
80 raise ValueError(
81 f"unsupported checkpoint schema {manifest.get('schema_version')!r}; "
82 f"expected {SCHEMA_VERSION}"
83 )
84 name = manifest.get("name")
85 if name not in RELEASE_CHECKPOINTS:
86 supported = ", ".join(RELEASE_CHECKPOINTS)
87 raise ValueError(
88 f"unsupported release checkpoint {name!r}; choose one of: {supported}"
89 )
90 expected_precision = RELEASE_CHECKPOINTS[name]
91 if manifest.get("precision") != expected_precision:
92 raise ValueError(
93 f"checkpoint {name} must declare precision={expected_precision!r}, "
94 f"got {manifest.get('precision')!r}"
95 )
96
97 files = manifest.get("files")
98 if not isinstance(files, dict):
99 raise ValueError("checkpoint manifest field files must be a JSON object")
100 model_field = "components" if name == FP4 else "model"
101 model_path = _resolve_member(root, files.get(model_field), label=model_field)
102 if model_path.suffix != ".safetensors":
103 raise ValueError(f"checkpoint {model_field} must use the safetensors format")
104
105 modelopt_path = None
106 if name == FP4:
107 if "model" in files:
108 raise ValueError(
109 "echo15_fp4 must be standalone: use files.components, not a BF16 files.model"
110 )
111 modelopt_path = _resolve_member(root, files.get("modelopt"), label="modelopt")
112 if modelopt_path.suffix != ".pt":
113 raise ValueError("echo15_fp4 ModelOpt state must use the .pt format")
114 elif "modelopt" in files:
115 raise ValueError(f"checkpoint {name} must not contain a ModelOpt state")
116
117 return ReleaseCheckpoint(
118 root=root,
119 name=name,
120 precision=expected_precision,
121 model_path=model_path,
122 modelopt_path=modelopt_path,
123 )
124
124 lines PYTHON