Q-Prefer-D2 / code /Q-Prefer /scripts /verify_artifact.py
qgfvadfuvads's picture
Upload Q-Prefer training and inference code
aa7758f verified
Raw
History Blame Contribute Delete
6.55 kB
#!/usr/bin/env python3
"""Verify a Q-Prefer adapter without loading the 4B base model."""
from __future__ import annotations
import argparse
import hashlib
import json
from pathlib import Path
from safetensors import safe_open
from qprefer_reward.constants import (
BASE_MODEL_ID,
BASE_MODEL_REVISION,
EXPECTED_SPECIAL_TOKEN_IDS,
PUBLISHED_ADAPTER_SHA256,
PUBLISHED_ADAPTER_TENSOR_COUNT,
PUBLISHED_RM_HEAD_SHAPE,
PUBLISHED_SPECIAL_EMBEDDINGS_SHA256,
SPECIAL_TOKENS,
)
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("adapter", type=Path)
parser.add_argument("--allow-unknown-checkpoint", action="store_true")
return parser.parse_args()
def verify_manifest(root: Path) -> None:
manifest_path = root / "artifact_manifest.json"
if not manifest_path.is_file():
raise FileNotFoundError(f"required artifact manifest is missing: {manifest_path}")
manifest = json.loads(manifest_path.read_text())
expected_metadata = {
"format_version": 1,
"base_model": BASE_MODEL_ID,
"base_revision": BASE_MODEL_REVISION,
"special_tokens": list(SPECIAL_TOKENS),
"special_token_ids": list(EXPECTED_SPECIAL_TOKEN_IDS),
"supported_dimensions": ["visual_quality", "text_alignment"],
"unsupported_dimensions": ["motion_quality"],
}
mismatches = {
key: (expected, manifest.get(key))
for key, expected in expected_metadata.items()
if manifest.get(key) != expected
}
if mismatches:
raise RuntimeError(f"unexpected artifact manifest metadata: {mismatches}")
files = manifest.get("files")
if not isinstance(files, dict) or not files:
raise RuntimeError("artifact manifest has no file checksums")
required = {
"adapter_config.json",
"adapter_model.safetensors",
"chat_template.jinja",
"README.md",
"special_token_embeddings.safetensors",
"tokenizer.json",
"tokenizer_config.json",
}
missing = required.difference(files)
if missing:
raise RuntimeError(f"artifact manifest is missing required files: {sorted(missing)}")
for filename, record in files.items():
if Path(filename).name != filename or filename == manifest_path.name:
raise RuntimeError(f"invalid artifact filename in manifest: {filename!r}")
if not isinstance(record, dict):
raise RuntimeError(f"invalid manifest record for {filename!r}")
path = root / filename
if not path.is_file():
raise FileNotFoundError(f"manifest file is missing: {path}")
observed_bytes = path.stat().st_size
observed_sha = sha256(path)
if record.get("bytes") != observed_bytes or record.get("sha256") != observed_sha:
raise RuntimeError(
f"artifact integrity check failed for {filename}: "
f"expected bytes/sha256={record.get('bytes')}/{record.get('sha256')}, "
f"observed={observed_bytes}/{observed_sha}"
)
def main() -> None:
args = parse_args()
root = args.adapter.expanduser().resolve()
weights = root / "adapter_model.safetensors"
config_path = root / "adapter_config.json"
if not weights.is_file() or not config_path.is_file():
raise FileNotFoundError("adapter_model.safetensors or adapter_config.json is missing")
verify_manifest(root)
observed_sha = sha256(weights)
if observed_sha != PUBLISHED_ADAPTER_SHA256 and not args.allow_unknown_checkpoint:
raise RuntimeError(
"adapter checksum mismatch: "
f"expected={PUBLISHED_ADAPTER_SHA256}, observed={observed_sha}"
)
config = json.loads(config_path.read_text())
expected_config = {
"peft_type": "LORA",
"r": 64,
"lora_alpha": 128,
"lora_dropout": 0.05,
}
mismatches = {
key: (expected, config.get(key))
for key, expected in expected_config.items()
if config.get(key) != expected
}
if mismatches:
raise RuntimeError(f"unexpected adapter configuration: {mismatches}")
if "rm_head" not in config.get("modules_to_save", []):
raise RuntimeError("adapter config does not preserve rm_head")
with safe_open(weights, framework="pt", device="cpu") as handle:
tensor_keys = list(handle.keys())
reward_head_keys = [key for key in tensor_keys if key.endswith("rm_head.weight")]
if len(tensor_keys) != PUBLISHED_ADAPTER_TENSOR_COUNT:
raise RuntimeError(
"unexpected adapter tensor count: "
f"expected={PUBLISHED_ADAPTER_TENSOR_COUNT}, observed={len(tensor_keys)}"
)
if len(reward_head_keys) != 1:
raise RuntimeError(f"expected one rm_head.weight, found {reward_head_keys}")
shape = tuple(handle.get_tensor(reward_head_keys[0]).shape)
if shape != PUBLISHED_RM_HEAD_SHAPE:
raise RuntimeError(
f"unexpected rm_head shape: expected={PUBLISHED_RM_HEAD_SHAPE}, observed={shape}"
)
embeddings = root / "special_token_embeddings.safetensors"
if not embeddings.is_file():
raise FileNotFoundError(f"required special-token embeddings are missing: {embeddings}")
embedding_sha = sha256(embeddings)
if embedding_sha != PUBLISHED_SPECIAL_EMBEDDINGS_SHA256:
raise RuntimeError(
"special-token embedding checksum mismatch: "
f"expected={PUBLISHED_SPECIAL_EMBEDDINGS_SHA256}, observed={embedding_sha}"
)
with safe_open(embeddings, framework="pt", device="cpu") as handle:
embedding_shape = tuple(handle.get_tensor("special_token_embeddings").shape)
if embedding_shape != PUBLISHED_RM_HEAD_SHAPE:
raise RuntimeError(
"special-token embedding shape must equal (3, hidden_size): "
f"expected={PUBLISHED_RM_HEAD_SHAPE}, observed={embedding_shape}"
)
print("Q-Prefer artifact verified")
print(f"adapter: {root}")
print(f"sha256: {observed_sha}")
print(f"tensors: {len(tensor_keys)}")
print(f"rm_head: {shape}")
print(f"special embeddings sha256: {embedding_sha}")
if __name__ == "__main__":
main()