Fish-Audio-S2-Pro-NVFP4-Balanced / validate_release.py
ajh-code's picture
Add files using upload-large-folder tool
16f5171 verified
Raw
History Blame Contribute Delete
11.4 kB
#!/usr/bin/env python3
"""Validate the self-contained Fish S2-Pro NVFP4/MXFP8 V1 release."""
from __future__ import annotations
import hashlib
import json
import struct
from pathlib import Path, PurePosixPath
ROOT = Path(__file__).resolve().parent
DTYPE_BYTES = {
"BF16": 2,
"F32": 4,
"F8_E4M3": 1,
"I32": 4,
"U8": 1,
}
def fail(message: str) -> None:
raise RuntimeError(message)
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(8 * 1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def load_json(relative: str) -> dict:
path = ROOT / relative
if not path.is_file():
fail(f"missing required JSON file: {relative}")
try:
return json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
fail(f"invalid JSON in {relative}: {error}")
def safetensors_header(path: Path) -> dict:
with path.open("rb") as handle:
raw_length = handle.read(8)
if len(raw_length) != 8:
fail(f"truncated safetensors length: {path.name}")
header_length = struct.unpack("<Q", raw_length)[0]
if header_length <= 2 or header_length > path.stat().st_size - 8:
fail(f"invalid safetensors header length: {path.name}")
try:
return json.loads(handle.read(header_length))
except json.JSONDecodeError as error:
fail(f"invalid safetensors header in {path.name}: {error}")
def validate_metadata() -> dict:
config = load_json("config.json")
policy = config.get("fish_s2_quantization")
if not isinstance(policy, dict):
fail("config.json has no fish_s2_quantization dictionary")
expected = {
"format": "mixed_nvfp4_mxfp8",
"profile": "balanced",
"release": "v1",
"policy": "w4a16_gate_up_middle30_mxfp8_rest",
"nvfp4_modules": 60,
"mxfp8_modules": 120,
"runtime": "bundled",
"hardware_family": "sm_120",
}
if policy != expected:
fail(f"unexpected config quantization policy: {policy}")
quantization = load_json("quantization.json")
if quantization.get("format") != "fish-s2-pro-project-local-nvfp4-mixed":
fail("unexpected quantization format")
if quantization.get("policy") != "w4a16_gate_up_middle30_mxfp8_rest":
fail("unexpected quantization policy")
release = quantization.get("release", {})
if release.get("version") != "1.0" or release.get("xpo3_release") is not False:
fail("release must identify V1 and explicitly remain outside XPO3")
if release.get("repository_name") != "ajh-code/Fish-Audio-S2-Pro-NVFP4-Balanced":
fail("release metadata has the wrong Hugging Face repository name")
if release.get("self_contained_weights") is not True:
fail("release does not declare self-contained weights")
fresh_load = quantization.get("fresh_load_verification", {})
if fresh_load.get("status") != "passed":
fail("checkpoint lacks passed fresh-load verification")
conversion = quantization.get("conversion", {})
if len(conversion.get("records", [])) != 180:
fail("quantization metadata must contain 180 projection records")
if conversion.get("correction_parameters") != 0:
fail("V1 loader does not accept correction-bearing checkpoints")
if conversion.get("w4a16_max_m") != 1:
fail("V1 must use the qualified M=1 W4A16 decode boundary")
source = (ROOT / ".source").read_text(encoding="utf-8")
for revision in (
"1de9996b6be38b745688de084d87a5633f714e4e",
"e5e292632cb11e7a27b2b7487f58f612bc101e13",
"a04c1b63b1a7a670840fb3e97a82c0dbe2a35ded",
"7a03467b90d6feff6bd196928dfe156bd173f36e",
):
if revision not in source:
fail(f".source is missing pinned revision {revision}")
license_text = (ROOT / "LICENSE.md").read_text(encoding="utf-8")
if "FISH AUDIO RESEARCH LICENSE AGREEMENT" not in license_text:
fail("LICENSE.md is not the Fish Audio Research License")
notice = (ROOT / "Notice").read_text(encoding="utf-8")
if "This model is licensed under the Fish Audio Research License" not in notice:
fail("Notice lacks the required Fish Audio attribution")
if "Built with Fish Audio" not in notice:
fail("Notice lacks the required Built with Fish Audio statement")
readme = (ROOT / "README.md").read_text(encoding="utf-8")
for required in (
"Built with Fish Audio",
"This is not yet an XPO3 release",
"Commercial use requires a separate",
):
if required not in readme:
fail(f"README.md lacks required release statement: {required}")
return quantization
def validate_checkpoint(quantization: dict) -> tuple[int, int, int]:
index = load_json("model.safetensors.index.json")
weight_map = index.get("weight_map")
if not isinstance(weight_map, dict) or not weight_map:
fail("checkpoint index has no weight map")
shard_names = sorted(set(weight_map.values()))
if shard_names != [
"model-00001-of-00003.safetensors",
"model-00002-of-00003.safetensors",
"model-00003-of-00003.safetensors",
]:
fail(f"unexpected checkpoint shards: {shard_names}")
discovered: dict[str, str] = {}
logical_bytes = 0
for shard_name in shard_names:
shard_path = ROOT / shard_name
if not shard_path.is_file():
fail(f"missing checkpoint shard: {shard_name}")
header = safetensors_header(shard_path)
for name, record in header.items():
if name == "__metadata__":
continue
if name in discovered:
fail(f"duplicate tensor across shards: {name}")
dtype = record.get("dtype")
shape = record.get("shape")
offsets = record.get("data_offsets")
if dtype not in DTYPE_BYTES or not isinstance(shape, list):
fail(f"unsupported tensor metadata for {name}")
if (
not isinstance(offsets, list)
or len(offsets) != 2
or not all(isinstance(value, int) for value in offsets)
or offsets[0] < 0
or offsets[1] < offsets[0]
):
fail(f"invalid data offsets for {name}")
elements = 1
for dimension in shape:
if not isinstance(dimension, int) or dimension < 0:
fail(f"invalid shape for {name}")
elements *= dimension
tensor_bytes = elements * DTYPE_BYTES[dtype]
if offsets[1] - offsets[0] != tensor_bytes:
fail(f"tensor byte range mismatch for {name}")
logical_bytes += tensor_bytes
discovered[name] = shard_name
if discovered != weight_map:
missing = sorted(set(weight_map) - set(discovered))
extra = sorted(set(discovered) - set(weight_map))
fail(f"checkpoint index mismatch; missing={missing[:3]} extra={extra[:3]}")
expected_size = int(index.get("metadata", {}).get("total_size", -1))
if logical_bytes != expected_size:
fail(f"logical checkpoint size mismatch: {logical_bytes} != {expected_size}")
if logical_bytes != quantization.get("state_payload_bytes"):
fail("quantization state_payload_bytes does not match the checkpoint")
counts = {
"qdata": sum(name.endswith(".qdata") for name in weight_map),
"weight_block_scale": sum(
name.endswith(".weight_block_scale") for name in weight_map
),
"weight_scale": sum(name.endswith(".weight_scale") for name in weight_map),
"weight_fp8": sum(name.endswith(".weight_fp8") for name in weight_map),
"weight_scale_storage": sum(
name.endswith(".weight_scale_storage") for name in weight_map
),
}
if counts != {
"qdata": 60,
"weight_block_scale": 60,
"weight_scale": 60,
"weight_fp8": 120,
"weight_scale_storage": 120,
}:
fail(f"unexpected packed tensor counts: {counts}")
if counts != quantization.get("packed_tensor_counts"):
fail("packed tensor counts disagree with quantization.json")
return len(weight_map), len(shard_names), logical_bytes
def validate_runtime() -> None:
required = (
"codec.pth",
"client.py",
"launch.sh",
"install.sh",
"Dockerfile",
"compose.yaml",
"runtime/server.py",
"runtime/web/index.html",
"runtime/experimental/codec.py",
"runtime/experimental/nvfp4/checkpoint.py",
"runtime/experimental/nvfp4/modules.py",
"runtime/experimental/fp8/modules.py",
"runtime/native/smallm_gemv/smallm_gemv.cpp",
"runtime/native/smallm_gemv/smallm_gemv.cu",
"runtime/native/smallm_gemv/smallm_gemv.h",
"runtime/native/LICENSE",
"vendor/fish-speech/.project-root",
"vendor/fish-speech/LICENSE",
"vendor/fish-speech/pyproject.toml",
"vendor/fish-speech/fish_speech/configs/modded_dac_vq.yaml",
"vendor/fish-speech/tools/api_server.py",
)
for relative in required:
if not (ROOT / relative).is_file():
fail(f"missing required release file: {relative}")
def validate_manifest() -> int:
manifest = load_json("MANIFEST.json")
records = manifest.get("files")
if not isinstance(records, list) or not records:
fail("MANIFEST.json has no file records")
seen: set[str] = set()
for record in records:
relative = record.get("path")
if not isinstance(relative, str):
fail("manifest record has no path")
pure = PurePosixPath(relative)
if pure.is_absolute() or ".." in pure.parts or relative == "MANIFEST.json":
fail(f"unsafe or recursive manifest path: {relative}")
if relative in seen:
fail(f"duplicate manifest path: {relative}")
seen.add(relative)
path = ROOT.joinpath(*pure.parts)
if not path.is_file():
fail(f"manifest file is missing: {relative}")
if path.stat().st_size != record.get("size"):
fail(f"manifest size mismatch: {relative}")
if sha256(path) != record.get("sha256"):
fail(f"manifest hash mismatch: {relative}")
return len(records)
def main() -> None:
quantization = validate_metadata()
checkpoint_tensors, checkpoint_shards, logical_bytes = validate_checkpoint(
quantization
)
validate_runtime()
manifest_files = validate_manifest()
print(
json.dumps(
{
"status": "pass",
"release": "v1",
"xpo3_release": False,
"checkpoint_tensors": checkpoint_tensors,
"checkpoint_shards": checkpoint_shards,
"checkpoint_logical_bytes": logical_bytes,
"nvfp4_projections": 60,
"mxfp8_projections": 120,
"manifest_files": manifest_files,
"self_contained_weights": True,
},
indent=2,
)
)
if __name__ == "__main__":
main()