from __future__ import annotations
import hashlib
import json
import logging
from dataclasses import dataclass, field
import torch
logger = logging.getLogger("modelexpress.transfer_safety")
def detect_model_features(model_config) -> dict[str, str]:
hf_config = model_config.hf_text_config
features: dict[str, str] = {}
features["model_type"] = getattr(hf_config, "model_type", "unknown")
features["dtype"] = str(model_config.dtype).replace("torch.", "")
features["quantization"] = model_config.quantization or "none"
kv_lora_rank = getattr(hf_config, "kv_lora_rank", None)
has_mla = isinstance(kv_lora_rank, int)
features["attention"] = "mla" if has_mla else "standard"
num_experts = (
getattr(hf_config, "n_routed_experts", None)
or getattr(hf_config, "num_local_experts", None)
)
if isinstance(num_experts, int) and num_experts > 1:
features["moe"] = str(num_experts)
return features
def check_transfer_allowed(model_config) -> tuple[bool, str]:
features = detect_model_features(model_config)
logger.info(f"[Transfer Safety] P2P transfer allowed. Features: {features}")
return True, "allowed"
def get_vllm_version() -> str:
try:
import vllm
return getattr(vllm, "__version__", "unknown")
except ImportError:
return "unknown"
def get_torch_version() -> str:
return torch.__version__
def get_cuda_version() -> str:
return getattr(torch.version, "cuda", "unknown") or "unknown"
def _get_attention_backend() -> str:
try:
from vllm.config import get_current_vllm_config
config = get_current_vllm_config()
if hasattr(config, "model_config") and config.model_config is not None:
cache_config = getattr(config, "cache_config", None)
if cache_config is not None:
return getattr(cache_config, "attention_backend", "unknown") or "unknown"
except Exception:
pass
return os.environ.get("VLLM_ATTENTION_BACKEND", "auto")
def get_deep_gemm_version() -> str:
try:
from importlib.metadata import version
return version("deep-gemm")
except Exception:
return "unknown"
def get_gpu_capability() -> str:
try:
if torch.cuda.is_available():
props = torch.cuda.get_device_properties(0)
return f"{props.major}.{props.minor}"
except Exception:
pass
return "unknown"
@dataclass
class TransferFingerprint:
vllm_version: str = ""
torch_version: str = ""
cuda_version: str = ""
deep_gemm_version: str = ""
gpu_capability: str = ""
attention_backend: str = ""
manifest_hash: str = ""
tensor_count: int = 0
@classmethod
def from_environment(cls, tensors: dict[str, torch.Tensor] | None = None) -> TransferFingerprint:
fp = cls(
vllm_version=get_vllm_version(),
torch_version=get_torch_version(),
cuda_version=get_cuda_version(),
deep_gemm_version=get_deep_gemm_version(),
gpu_capability=get_gpu_capability(),
attention_backend=_get_attention_backend(),
)
if tensors is not None:
fp.manifest_hash = _compute_manifest_hash(tensors)
fp.tensor_count = len(tensors)
return fp
def to_json(self) -> str:
return json.dumps({
"vllm_version": self.vllm_version,
"torch_version": self.torch_version,
"cuda_version": self.cuda_version,
"deep_gemm_version": self.deep_gemm_version,
"gpu_capability": self.gpu_capability,
"attention_backend": self.attention_backend,
"manifest_hash": self.manifest_hash,
"tensor_count": self.tensor_count,
}, sort_keys=True)
@classmethod
def from_json(cls, s: str) -> TransferFingerprint:
d = json.loads(s)
return cls(**d)
def _compute_manifest_hash(tensors: dict[str, torch.Tensor]) -> str:
entries = []
for name in sorted(tensors.keys()):
t = tensors[name]
entries.append(f"{name}:{list(t.shape)}:{t.dtype}:{t.numel() * t.element_size()}")
manifest_str = "\n".join(entries)
return hashlib.sha256(manifest_str.encode()).hexdigest()