mirror of
https://github.com/zenlm/zen-family.git
synced 2026-10-30 14:58:06 +00:00
399 lines
14 KiB
Python
399 lines
14 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Validate Zen models on HuggingFace
|
|
Checks existence, size, architecture, and all details
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
import json
|
|
from pathlib import Path
|
|
from huggingface_hub import HfApi, hf_hub_download, model_info
|
|
from huggingface_hub.utils import RepositoryNotFoundError
|
|
import requests
|
|
|
|
class ZenModelValidator:
|
|
"""Validate Zen models on HuggingFace"""
|
|
|
|
def __init__(self):
|
|
self.api = HfApi()
|
|
self.errors = []
|
|
self.warnings = []
|
|
|
|
# Expected model specifications (September 2025)
|
|
self.model_specs = {
|
|
"zenlm/zen-nano-instruct": {
|
|
"params": "600M",
|
|
"architecture": "Qwen3-0.6B",
|
|
"size_mb": 600,
|
|
"context": 32768,
|
|
"layers": 24,
|
|
"hidden_size": 1024,
|
|
"formats": ["safetensors", "gguf", "mlx"]
|
|
},
|
|
"zenlm/zen-eco-instruct": {
|
|
"params": "4B",
|
|
"architecture": "Qwen3-4B",
|
|
"size_mb": 4000,
|
|
"context": 32768,
|
|
"layers": 28,
|
|
"hidden_size": 3584,
|
|
"formats": ["safetensors", "gguf", "mlx"]
|
|
},
|
|
"zenlm/zen-coder-instruct": {
|
|
"params": "480B-A35B",
|
|
"architecture": "Qwen3-Coder-480B-A35B",
|
|
"size_mb": 35000,
|
|
"context": 128000,
|
|
"layers": 80,
|
|
"num_experts": 64,
|
|
"active_experts": 8,
|
|
"formats": ["safetensors", "gguf"]
|
|
},
|
|
"zenlm/zen-omni-instruct": {
|
|
"params": "30B-A3B",
|
|
"architecture": "Qwen3-Omni-30B-A3B",
|
|
"size_mb": 3000,
|
|
"context": 65536,
|
|
"layers": 32,
|
|
"num_experts": 32,
|
|
"active_experts": 4,
|
|
"modalities": ["text", "vision", "audio"],
|
|
"formats": ["safetensors", "gguf", "mlx"]
|
|
},
|
|
"zenlm/zen-next-instruct": {
|
|
"params": "80B-A3B",
|
|
"architecture": "Qwen3-Next-80B-A3B",
|
|
"size_mb": 3000,
|
|
"context": 128000,
|
|
"layers": 60,
|
|
"num_experts": 128,
|
|
"active_experts": 2,
|
|
"sparsity": 0.9625,
|
|
"formats": ["safetensors", "gguf", "mlx"]
|
|
}
|
|
}
|
|
|
|
def validate_model(self, repo_id, expected_size=None, expected_params=None, expected_arch=None):
|
|
"""Validate a single model"""
|
|
print(f"\n{'='*60}")
|
|
print(f"Validating: {repo_id}")
|
|
print(f"{'='*60}")
|
|
|
|
# Check if model exists
|
|
if not self.check_exists(repo_id):
|
|
return False
|
|
|
|
# Get model info
|
|
info = self.get_model_info(repo_id)
|
|
if not info:
|
|
return False
|
|
|
|
# Validate specifications
|
|
specs = self.model_specs.get(repo_id, {})
|
|
|
|
# Check parameters
|
|
if expected_params or specs.get("params"):
|
|
self.validate_params(repo_id, expected_params or specs["params"], info)
|
|
|
|
# Check architecture
|
|
if expected_arch or specs.get("architecture"):
|
|
self.validate_architecture(repo_id, expected_arch or specs["architecture"], info)
|
|
|
|
# Check size
|
|
if expected_size or specs.get("size_mb"):
|
|
self.validate_size(repo_id, expected_size or specs["size_mb"], info)
|
|
|
|
# Check model card
|
|
self.validate_model_card(repo_id, info)
|
|
|
|
# Check files
|
|
self.validate_files(repo_id)
|
|
|
|
# Check tags
|
|
self.validate_tags(repo_id, info)
|
|
|
|
# Print results
|
|
self.print_results(repo_id)
|
|
|
|
return len(self.errors) == 0
|
|
|
|
def check_exists(self, repo_id):
|
|
"""Check if model exists on HuggingFace"""
|
|
try:
|
|
self.api.model_info(repo_id)
|
|
print(f"✅ Model exists: {repo_id}")
|
|
return True
|
|
except RepositoryNotFoundError:
|
|
self.errors.append(f"Model not found: {repo_id}")
|
|
print(f"❌ Model not found: {repo_id}")
|
|
return False
|
|
except Exception as e:
|
|
self.errors.append(f"Error checking model: {e}")
|
|
print(f"❌ Error: {e}")
|
|
return False
|
|
|
|
def get_model_info(self, repo_id):
|
|
"""Get model information from HuggingFace"""
|
|
try:
|
|
info = self.api.model_info(repo_id)
|
|
print(f"✅ Retrieved model info")
|
|
|
|
# Print basic info
|
|
print(f" Downloads: {getattr(info, 'downloads', 'N/A')}")
|
|
print(f" Likes: {getattr(info, 'likes', 0)}")
|
|
print(f" Tags: {', '.join(info.tags) if info.tags else 'None'}")
|
|
|
|
return info
|
|
except Exception as e:
|
|
self.errors.append(f"Failed to get model info: {e}")
|
|
return None
|
|
|
|
def validate_params(self, repo_id, expected, info):
|
|
"""Validate parameter count"""
|
|
# Check in model card or config
|
|
try:
|
|
config_url = f"https://huggingface.co/{repo_id}/raw/main/config.json"
|
|
response = requests.get(config_url)
|
|
|
|
if response.status_code == 200:
|
|
config = response.json()
|
|
# Check various parameter fields
|
|
params = config.get("num_parameters") or config.get("n_params") or config.get("parameters")
|
|
|
|
if params:
|
|
print(f"✅ Parameters validated: {params}")
|
|
else:
|
|
self.warnings.append(f"Could not verify parameters (expected: {expected})")
|
|
print(f"⚠️ Could not verify parameters (expected: {expected})")
|
|
else:
|
|
self.warnings.append(f"Config.json not found (expected params: {expected})")
|
|
|
|
except Exception as e:
|
|
self.warnings.append(f"Could not validate parameters: {e}")
|
|
|
|
def validate_architecture(self, repo_id, expected, info):
|
|
"""Validate model architecture"""
|
|
# Check in config or model card
|
|
try:
|
|
config_url = f"https://huggingface.co/{repo_id}/raw/main/config.json"
|
|
response = requests.get(config_url)
|
|
|
|
if response.status_code == 200:
|
|
config = response.json()
|
|
arch = config.get("architectures") or config.get("model_type") or config.get("architecture")
|
|
|
|
if arch:
|
|
print(f"✅ Architecture: {arch}")
|
|
if expected.lower() not in str(arch).lower():
|
|
self.warnings.append(f"Architecture mismatch: expected {expected}, found {arch}")
|
|
else:
|
|
self.warnings.append(f"Architecture not specified (expected: {expected})")
|
|
|
|
except Exception as e:
|
|
self.warnings.append(f"Could not validate architecture: {e}")
|
|
|
|
def validate_size(self, repo_id, expected_mb, info):
|
|
"""Validate model size"""
|
|
# This would check actual file sizes in production
|
|
# For now, we'll check if files exist
|
|
try:
|
|
files = self.api.list_repo_files(repo_id)
|
|
|
|
# Look for model files
|
|
model_files = [f for f in files if f.endswith(('.bin', '.safetensors', '.gguf', '.mlx'))]
|
|
|
|
if model_files:
|
|
print(f"✅ Found {len(model_files)} model files")
|
|
print(f" Files: {', '.join(model_files[:5])}")
|
|
else:
|
|
self.warnings.append("No model files found")
|
|
print(f"⚠️ No model files found")
|
|
|
|
except Exception as e:
|
|
self.warnings.append(f"Could not check model files: {e}")
|
|
|
|
def validate_model_card(self, repo_id, info):
|
|
"""Validate model card content"""
|
|
try:
|
|
# Check if README exists
|
|
readme_url = f"https://huggingface.co/{repo_id}/raw/main/README.md"
|
|
response = requests.get(readme_url)
|
|
|
|
if response.status_code == 200:
|
|
content = response.text
|
|
|
|
# Check for required sections
|
|
required_sections = [
|
|
"Model Information",
|
|
"Usage",
|
|
"Training",
|
|
"License"
|
|
]
|
|
|
|
missing = []
|
|
for section in required_sections:
|
|
if section.lower() not in content.lower():
|
|
missing.append(section)
|
|
|
|
if missing:
|
|
self.warnings.append(f"Model card missing sections: {', '.join(missing)}")
|
|
print(f"⚠️ Missing sections: {', '.join(missing)}")
|
|
else:
|
|
print(f"✅ Model card complete")
|
|
|
|
# Check for v1.0.1 mentions
|
|
if "1.0.1" in content or "v1.0.1" in content:
|
|
print(f"✅ v1.0.1 documentation found")
|
|
else:
|
|
self.warnings.append("v1.0.1 not mentioned in model card")
|
|
|
|
else:
|
|
self.errors.append("README.md not found")
|
|
print(f"❌ README.md not found")
|
|
|
|
except Exception as e:
|
|
self.warnings.append(f"Could not validate model card: {e}")
|
|
|
|
def validate_files(self, repo_id):
|
|
"""Validate required files exist"""
|
|
try:
|
|
files = self.api.list_repo_files(repo_id)
|
|
|
|
# Check for required files
|
|
required = ["README.md", "config.json"]
|
|
missing = []
|
|
|
|
for req in required:
|
|
if req not in files:
|
|
missing.append(req)
|
|
|
|
if missing:
|
|
self.warnings.append(f"Missing files: {', '.join(missing)}")
|
|
print(f"⚠️ Missing files: {', '.join(missing)}")
|
|
else:
|
|
print(f"✅ All required files present")
|
|
|
|
# Check for format files
|
|
formats = {
|
|
"safetensors": any(f.endswith('.safetensors') for f in files),
|
|
"gguf": any(f.endswith('.gguf') for f in files),
|
|
"mlx": any(f.endswith('.mlx') for f in files),
|
|
"pytorch": any(f.endswith('.bin') for f in files)
|
|
}
|
|
|
|
available_formats = [fmt for fmt, exists in formats.items() if exists]
|
|
if available_formats:
|
|
print(f"✅ Available formats: {', '.join(available_formats)}")
|
|
else:
|
|
self.warnings.append("No model format files found")
|
|
|
|
except Exception as e:
|
|
self.warnings.append(f"Could not validate files: {e}")
|
|
|
|
def validate_tags(self, repo_id, info):
|
|
"""Validate model tags"""
|
|
required_tags = ["zen", "text-generation", "transformers"]
|
|
|
|
if info.tags:
|
|
missing = [tag for tag in required_tags if tag not in info.tags]
|
|
|
|
if missing:
|
|
self.warnings.append(f"Missing tags: {', '.join(missing)}")
|
|
print(f"⚠️ Missing tags: {', '.join(missing)}")
|
|
else:
|
|
print(f"✅ All required tags present")
|
|
|
|
# Check for version tags
|
|
if "v1.0.1" in info.tags or "1.0.1" in info.tags:
|
|
print(f"✅ Version tag found")
|
|
else:
|
|
self.warnings.append("v1.0.1 tag not found")
|
|
else:
|
|
self.errors.append("No tags found")
|
|
|
|
def print_results(self, repo_id):
|
|
"""Print validation results"""
|
|
print(f"\n{'='*60}")
|
|
print(f"Results for {repo_id}:")
|
|
print(f"{'='*60}")
|
|
|
|
if self.errors:
|
|
print(f"\n❌ ERRORS ({len(self.errors)}):")
|
|
for error in self.errors:
|
|
print(f" - {error}")
|
|
|
|
if self.warnings:
|
|
print(f"\n⚠️ WARNINGS ({len(self.warnings)}):")
|
|
for warning in self.warnings:
|
|
print(f" - {warning}")
|
|
|
|
if not self.errors and not self.warnings:
|
|
print(f"\n✅ PERFECT - All validations passed!")
|
|
|
|
# Clear for next model
|
|
self.errors = []
|
|
self.warnings = []
|
|
|
|
def validate_all(self):
|
|
"""Validate all Zen models"""
|
|
print("\n" + "="*60)
|
|
print("ZEN MODEL VALIDATION SUITE")
|
|
print("="*60)
|
|
|
|
all_passed = True
|
|
results = {}
|
|
|
|
for repo_id, specs in self.model_specs.items():
|
|
passed = self.validate_model(repo_id)
|
|
results[repo_id] = passed
|
|
if not passed:
|
|
all_passed = False
|
|
|
|
# Summary
|
|
print("\n" + "="*60)
|
|
print("VALIDATION SUMMARY")
|
|
print("="*60)
|
|
|
|
for repo_id, passed in results.items():
|
|
status = "✅ PASS" if passed else "❌ FAIL"
|
|
print(f"{status}: {repo_id}")
|
|
|
|
if all_passed:
|
|
print("\n🎉 ALL MODELS VALIDATED SUCCESSFULLY!")
|
|
else:
|
|
print("\n⚠️ Some models need attention")
|
|
|
|
return all_passed
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Validate Zen models on HuggingFace")
|
|
parser.add_argument("--repo-id", help="Model repository ID")
|
|
parser.add_argument("--expected-size", type=int, help="Expected size in MB")
|
|
parser.add_argument("--expected-params", help="Expected parameter count")
|
|
parser.add_argument("--expected-arch", help="Expected architecture")
|
|
parser.add_argument("--all", action="store_true", help="Validate all models")
|
|
|
|
args = parser.parse_args()
|
|
|
|
validator = ZenModelValidator()
|
|
|
|
if args.all:
|
|
success = validator.validate_all()
|
|
elif args.repo_id:
|
|
success = validator.validate_model(
|
|
args.repo_id,
|
|
args.expected_size,
|
|
args.expected_params,
|
|
args.expected_arch
|
|
)
|
|
else:
|
|
print("Please specify --repo-id or --all")
|
|
sys.exit(1)
|
|
|
|
sys.exit(0 if success else 1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |