Fix Windows local GGUF model loading crash (#4730)
* Fix Windows "Non-relative patterns are unsupported" when loading local GGUF models When a user loads a GGUF model from a local Windows path (e.g. C:\Users\danie\.lmstudio\models\unsloth\functiongemma-270m-it-GGUF), the model identifier contains backslashes and a drive letter. Both load_model_defaults() and _has_specific_yaml() constructed a YAML filename from the full absolute path and passed it to Path.rglob(), which rejects non-relative patterns on Windows. Fixed by detecting Windows-style paths (drive letters, UNC paths, backslashes) in addition to Unix-style paths, and using only the directory basename for the YAML filename lookup when the identifier is a local filesystem path. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Refactor: reuse is_local_path helper, fix case-sensitive suffix lookup - Replace inline local-path detection in model_config.py and inference_config.py with the existing is_local_path() from utils.paths, which already handles Unix, Windows drive-letter, UNC, and backslash paths - Fix case-sensitive suffix lookup in load_model_defaults(): the _REVERSE_MODEL_MAPPING is lowercase-keyed, so suffix comparisons must use .lower() to match paths like /path/to/Spark-TTS-0.5B/LLM * Fix WSL path parsing and _has_specific_yaml suffix lookup - Use normalize_path() before Path() operations so backslash Windows paths (e.g. C:\Users\...\model) are correctly split on POSIX/WSL hosts where pathlib treats backslashes as literal characters - Add suffix-based (2-component and 1-component) lookup to _has_specific_yaml() so it matches the same resolution rules as load_model_defaults(), fixing wrong inference params for local suffix-mapped models like Spark-TTS-0.5B/LLM --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
256c6e4884
commit
6c0826a9e4
2 changed files with 40 additions and 12 deletions
|
|
@ -17,6 +17,7 @@ import structlog
|
|||
from loggers import get_logger
|
||||
|
||||
from utils.models.model_config import load_model_defaults
|
||||
from utils.paths import is_local_path, normalize_path
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
|
@ -93,8 +94,28 @@ def _has_specific_yaml(model_identifier: str) -> bool:
|
|||
if model_identifier.lower() in _REVERSE_MODEL_MAPPING:
|
||||
return True
|
||||
|
||||
# Check for exact filename match
|
||||
model_filename = model_identifier.replace("/", "_") + ".yaml"
|
||||
# For local filesystem paths (e.g. C:\Users\...\model on Windows),
|
||||
# normalize backslashes so Path().parts splits correctly on POSIX/WSL,
|
||||
# then try matching the last 1-2 path components against the registry
|
||||
# (mirrors the logic in load_model_defaults).
|
||||
_is_local = is_local_path(model_identifier)
|
||||
_normalized = normalize_path(model_identifier) if _is_local else model_identifier
|
||||
|
||||
if _is_local:
|
||||
parts = Path(_normalized).parts
|
||||
for depth in (2, 1):
|
||||
if len(parts) >= depth:
|
||||
suffix = "/".join(parts[-depth:])
|
||||
if suffix.lower() in _REVERSE_MODEL_MAPPING:
|
||||
return True
|
||||
_lookup = Path(_normalized).name
|
||||
else:
|
||||
_lookup = model_identifier
|
||||
|
||||
# Check for exact filename match (basename for local paths to avoid
|
||||
# passing absolute paths into rglob which raises
|
||||
# "Non-relative patterns are unsupported" on Windows).
|
||||
model_filename = _lookup.replace("/", "_") + ".yaml"
|
||||
for config_path in defaults_dir.rglob(model_filename):
|
||||
if config_path.is_file():
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -1420,17 +1420,20 @@ def load_model_defaults(model_name: str) -> Dict[str, Any]:
|
|||
return config
|
||||
|
||||
# If model_name is a local path (e.g. /home/.../Spark-TTS-0.5B/LLM from
|
||||
# adapter_config.json), try matching the last 1-2 path components against
|
||||
# the registry (e.g. "Spark-TTS-0.5B/LLM").
|
||||
if model_name not in _REVERSE_MODEL_MAPPING and (
|
||||
model_name.startswith("/") or model_name.startswith(".")
|
||||
):
|
||||
parts = Path(model_name).parts
|
||||
# adapter_config.json, or C:\Users\...\model on Windows), try matching
|
||||
# the last 1-2 path components against the registry
|
||||
# (e.g. "Spark-TTS-0.5B/LLM").
|
||||
_is_local_path = is_local_path(model_name)
|
||||
# Normalize Windows backslash paths so Path().parts splits correctly
|
||||
# on POSIX/WSL hosts (pathlib treats backslashes as literals on Linux).
|
||||
_normalized = normalize_path(model_name) if _is_local_path else model_name
|
||||
if model_name.lower() not in _REVERSE_MODEL_MAPPING and _is_local_path:
|
||||
parts = Path(_normalized).parts
|
||||
for depth in [2, 1]:
|
||||
if len(parts) >= depth:
|
||||
suffix = "/".join(parts[-depth:])
|
||||
if suffix in _REVERSE_MODEL_MAPPING:
|
||||
canonical_file = _REVERSE_MODEL_MAPPING[suffix]
|
||||
if suffix.lower() in _REVERSE_MODEL_MAPPING:
|
||||
canonical_file = _REVERSE_MODEL_MAPPING[suffix.lower()]
|
||||
for config_path in defaults_dir.rglob(canonical_file):
|
||||
if config_path.is_file():
|
||||
with open(config_path, "r", encoding = "utf-8") as f:
|
||||
|
|
@ -1440,8 +1443,12 @@ def load_model_defaults(model_name: str) -> Dict[str, Any]:
|
|||
)
|
||||
return config
|
||||
|
||||
# Try exact model name match (for backward compatibility)
|
||||
model_filename = model_name.replace("/", "_") + ".yaml"
|
||||
# Try exact model name match (for backward compatibility).
|
||||
# For local filesystem paths, use only the directory basename to
|
||||
# avoid passing absolute paths (e.g. C:\...) into rglob which
|
||||
# raises "Non-relative patterns are unsupported" on Windows.
|
||||
_lookup_name = Path(_normalized).name if _is_local_path else model_name
|
||||
model_filename = _lookup_name.replace("/", "_") + ".yaml"
|
||||
# Search in subfolders and root
|
||||
for config_path in defaults_dir.rglob(model_filename):
|
||||
if config_path.is_file():
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue