Files
weekii 4145577458 feat: ComfyUI-GGUF-FX with Nexa SDK integration
Complete GGUF model support for ComfyUI with three inference modes:
1. GGUF mode - Local quantized models (llama-cpp-python)
2. Transformers mode - Full HuggingFace models
3. Nexa SDK mode - Remote/local GGUF via Nexa SDK service (NEW)

Major Features:
- 🔷 Nexa SDK integration with 3 new nodes
- 📁 ComfyUI /models/LLM directory integration
- ⚙️  Configurable API endpoints
- 🔄 Dual mode: Remote service + Local GGUF files
- 💭 Thinking mode support (DeepSeek-R1, Qwen3-Thinking)
- 📝 Unified output naming: all text outputs as 'context'
- 🖼️  Multi-image analysis (up to 6 images)
- 🎯 System prompt presets

New Nodes:
- Nexa Model Selector
- Nexa Text Generation
- Nexa Service Status
- Text Model Loader
- Text Generation
- Vision Model Loader (GGUF)
- Vision Description (GGUF)
- Vision Model Loader (Transformers)
- Vision Generation (Transformers)
- Multi-Image Analysis
- System Prompt Config

Documentation:
- Complete integration guide
- Usage examples
- API reference
- Troubleshooting guide

Author: weekii <weekii2024@gmail.com>
2025-10-29 12:54:14 +00:00

193 lines
5.4 KiB
Python

"""
Model Validator - 验证模型配置和文件
"""
import os
import re
from typing import Dict, List, Optional
from pathlib import Path
class ModelValidator:
"""模型验证器"""
@staticmethod
def validate_gguf_file(file_path: str) -> Dict:
"""
验证 GGUF 文件
Args:
file_path: 文件路径
Returns:
验证结果字典
"""
result = {
'valid': False,
'exists': False,
'is_gguf': False,
'readable': False,
'size': 0,
'errors': []
}
# 检查文件是否存在
if not os.path.exists(file_path):
result['errors'].append(f"File not found: {file_path}")
return result
result['exists'] = True
# 检查文件扩展名
if not file_path.lower().endswith('.gguf'):
result['errors'].append("File does not have .gguf extension")
return result
result['is_gguf'] = True
# 检查文件是否可读
try:
with open(file_path, 'rb') as f:
# 读取前几个字节检查 GGUF 魔数
magic = f.read(4)
if magic != b'GGUF':
result['errors'].append("Invalid GGUF file: magic number mismatch")
return result
result['readable'] = True
except Exception as e:
result['errors'].append(f"Cannot read file: {e}")
return result
# 获取文件大小
try:
result['size'] = os.path.getsize(file_path)
except Exception as e:
result['errors'].append(f"Cannot get file size: {e}")
result['valid'] = True
return result
@staticmethod
def validate_model_config(config: Dict) -> Dict:
"""
验证模型配置
Args:
config: 模型配置字典
Returns:
验证结果
"""
result = {
'valid': False,
'errors': [],
'warnings': []
}
# 必需字段
required_fields = ['name', 'repo', 'file']
for field in required_fields:
if field not in config or not config[field]:
result['errors'].append(f"Missing required field: {field}")
if result['errors']:
return result
# 验证 repo 格式
repo = config['repo']
if not re.match(r'^[\w\-\.]+/[\w\-\.]+$', repo):
result['errors'].append(f"Invalid repo format: {repo} (expected: username/repo-name)")
# 验证文件名格式
file = config['file']
if not file.lower().endswith('.gguf'):
result['errors'].append(f"Invalid file extension: {file} (expected: .gguf)")
# 可选字段验证
if 'mmproj' in config and config['mmproj']:
mmproj = config['mmproj']
if not mmproj.lower().endswith('.gguf'):
result['warnings'].append(f"mmproj file should have .gguf extension: {mmproj}")
# 如果没有错误,标记为有效
if not result['errors']:
result['valid'] = True
return result
@staticmethod
def validate_repo_id(repo_id: str) -> bool:
"""
验证 HuggingFace 仓库 ID 格式
Args:
repo_id: 仓库 ID
Returns:
是否有效
"""
pattern = r'^[\w\-\.]+/[\w\-\.]+$'
return bool(re.match(pattern, repo_id))
@staticmethod
def check_model_compatibility(model_info: Dict, business_type: str) -> Dict:
"""
检查模型与业务类型的兼容性
Args:
model_info: 模型信息
business_type: 业务类型
Returns:
兼容性检查结果
"""
result = {
'compatible': False,
'warnings': []
}
model_business_type = model_info.get('business_type', 'unknown')
if model_business_type == business_type:
result['compatible'] = True
elif model_business_type == 'unknown':
result['compatible'] = True
result['warnings'].append("Model business type is unknown, compatibility uncertain")
else:
result['warnings'].append(
f"Model is designed for {model_business_type}, but used for {business_type}"
)
return result
@staticmethod
def format_validation_result(result: Dict) -> str:
"""
格式化验证结果为可读字符串
Args:
result: 验证结果字典
Returns:
格式化的字符串
"""
lines = []
if result.get('valid'):
lines.append("✅ Validation passed")
else:
lines.append("❌ Validation failed")
if result.get('errors'):
lines.append("\nErrors:")
for error in result['errors']:
lines.append(f" - {error}")
if result.get('warnings'):
lines.append("\nWarnings:")
for warning in result['warnings']:
lines.append(f" - {warning}")
return "\n".join(lines)