From fa42ecd79a51701f27b1864d314eda72ddd200f8 Mon Sep 17 00:00:00 2001 From: velaraptor-runpod Date: Thu, 30 Apr 2026 17:54:14 -0500 Subject: [PATCH] fix: resolve lowercase HF cache paths when MODEL_NAME uses original casing Fixes FDE-174. Some model stores (e.g. RunPod pre-cached network volumes) normalize repo IDs to lowercase. HuggingFace Hub caches using the original casing, so MODEL_NAME=Qwen/Qwen2.5-Coder-32B-Instruct-AWQ would miss a cache stored as models--qwen--qwen2.5-coder-32b-instruct-awq/ and attempt a redundant download that fails on limited container storage. If the exact-case HF cache directory is absent but a lowercase variant exists, the latest snapshot path is returned directly so vLLM loads from disk. Absolute paths and models with no lowercase cache are unchanged. Co-Authored-By: Claude Sonnet 4.6 --- src/engine_args.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/engine_args.py b/src/engine_args.py index 8ed132c..3ba1844 100644 --- a/src/engine_args.py +++ b/src/engine_args.py @@ -342,6 +342,58 @@ def _sanitize_hf_overrides(hf_overrides: dict) -> dict | None: return result or None +def _resolve_cached_model_path(model_name: str) -> str: + """Return a local snapshot path when the HF cache was stored with lowercase names. + + Some model stores (e.g. RunPod pre-cached volumes) normalize repo IDs to + lowercase. HuggingFace Hub stores caches as + ``models--{org}--{model}/snapshots/{hash}/`` preserving the original casing, + so MODEL_NAME=Qwen/Qwen2.5-Coder-32B-Instruct-AWQ will miss a cache stored + as ``models--qwen--qwen2.5-coder-32b-instruct-awq/``. + + If the exact-case cache directory is absent but a lowercase variant exists, + the latest snapshot path is returned so vLLM loads from disk rather than + attempting a redundant download. + """ + if os.path.isabs(model_name): + return model_name + + cache_dir = ( + os.getenv("HUGGINGFACE_HUB_CACHE") + or os.getenv("HF_HOME") + or os.path.expanduser("~/.cache/huggingface/hub") + ) + + folder_name = f"models--{model_name.replace('/', '--')}" + + if os.path.isdir(os.path.join(cache_dir, folder_name)): + return model_name + + lower_dir = os.path.join(cache_dir, folder_name.lower()) + if not os.path.isdir(lower_dir): + return model_name + + snapshots_dir = os.path.join(lower_dir, "snapshots") + if not os.path.isdir(snapshots_dir): + return model_name + + try: + snapshots = sorted(os.listdir(snapshots_dir)) + except OSError: + return model_name + + if not snapshots: + return model_name + + resolved = os.path.join(snapshots_dir, snapshots[-1]) + logging.info( + "MODEL_NAME %r not found at original casing in HF cache; " + "resolved to lowercase cached snapshot at %r", + model_name, resolved, + ) + return resolved + + def get_local_args(): """ Retrieve local arguments from a JSON file. @@ -517,4 +569,8 @@ def get_engine_args(): if speculative_config: args["speculative_config"] = speculative_config + # Resolve lowercase HF cache paths (FDE-174) + if args.get("model"): + args["model"] = _resolve_cached_model_path(args["model"]) + return AsyncEngineArgs(**args)