diff --git a/studio/backend/utils/inference/inference_config.py b/studio/backend/utils/inference/inference_config.py index a0377f9869..9efc281b0b 100644 --- a/studio/backend/utils/inference/inference_config.py +++ b/studio/backend/utils/inference/inference_config.py @@ -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 diff --git a/studio/backend/utils/models/model_config.py b/studio/backend/utils/models/model_config.py index 5de3fd2cf9..f7d9b33542 100644 --- a/studio/backend/utils/models/model_config.py +++ b/studio/backend/utils/models/model_config.py @@ -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():