-
Notifications
You must be signed in to change notification settings - Fork 0
fix(studio): custom folder scan fails to find GGUF variants when pointing directly at a model directory #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0c753ab
d955ee2
19f06ff
f5ebf10
a4e5673
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -146,6 +146,39 @@ def _scan_models_dir( | |
| if not models_dir.exists() or not models_dir.is_dir(): | ||
| return [] | ||
|
|
||
| # Check if the directory itself IS a model (has a model config AND | ||
| # weight files). Both conditions are required: a bare directory with | ||
| # only loose .gguf files (no config) might be a mixed collection that | ||
| # should list files individually, and a config.json alone (no weights) | ||
| # does not make a model directory. | ||
| try: | ||
| _has_config = (models_dir / "config.json").exists() or ( | ||
| models_dir / "adapter_config.json" | ||
| ).exists() | ||
| _has_weights = any( | ||
| f.suffix.lower() in (".gguf", ".safetensors", ".bin") | ||
| for f in models_dir.iterdir() | ||
| if f.is_file() | ||
| ) | ||
|
Comment on lines
+158
to
+162
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The iterdir() at line 162 only runs when the directory itself is a model (early return path). When it's not, the second iterdir() at line 183 runs instead. They don't both execute for the same directory, so there's no redundant scan. |
||
| _is_self_model = _has_config and _has_weights | ||
|
Comment on lines
+155
to
+163
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for detecting if a directory is a model (checking for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Good suggestion. The model detection logic does appear in multiple places with intentionally different criteria (e.g. _is_self_model requires config AND weights, while Phase 1 uses a broader OR check). Refactoring into a shared helper would require careful parameterization to preserve these distinctions. I think this is better suited as a follow-up PR to keep this fix focused. |
||
| except OSError: | ||
| _is_self_model = False | ||
|
|
||
| if _is_self_model: | ||
| try: | ||
| updated_at = models_dir.stat().st_mtime | ||
| except OSError: | ||
| updated_at = None | ||
| return [ | ||
| LocalModelInfo( | ||
| id = str(models_dir), | ||
| display_name = models_dir.name, | ||
| path = str(models_dir), | ||
| source = "models_dir", | ||
| updated_at = updated_at, | ||
| ), | ||
| ] | ||
|
|
||
| found: List[LocalModelInfo] = [] | ||
| for child in models_dir.iterdir(): | ||
| if limit is not None and len(found) >= limit: | ||
|
|
@@ -243,6 +276,17 @@ def _scan_lmstudio_dir(lm_dir: Path) -> List[LocalModelInfo]: | |
| if not lm_dir.exists() or not lm_dir.is_dir(): | ||
| return [] | ||
|
|
||
| # If the directory itself is a model directory (has config files), | ||
| # it is not an LM Studio publisher structure -- _scan_models_dir | ||
| # already handles it. | ||
| try: | ||
| if (lm_dir / "config.json").exists() or ( | ||
| lm_dir / "adapter_config.json" | ||
| ).exists(): | ||
| return [] | ||
| except OSError: | ||
|
|
||
| pass | ||
|
|
||
| found: List[LocalModelInfo] = [] | ||
| for child in lm_dir.iterdir(): | ||
| try: | ||
|
|
@@ -263,6 +307,19 @@ def _scan_lmstudio_dir(lm_dir: Path) -> List[LocalModelInfo]: | |
| ) | ||
| continue | ||
|
|
||
| # If the child directory itself looks like a model (has config | ||
| # or model weight files), skip it -- _scan_models_dir already | ||
| # handles it. Only treat it as a publisher directory otherwise. | ||
| _child_is_model = ( | ||
| (child / "config.json").exists() | ||
| or (child / "adapter_config.json").exists() | ||
| or any(child.glob("*.safetensors")) | ||
| or any(child.glob("*.bin")) | ||
| or any(child.glob("*.gguf")) | ||
| ) | ||
| if _child_is_model: | ||
| continue | ||
|
|
||
| # child is a publisher directory -- scan its sub-directories | ||
| for model_dir in child.iterdir(): | ||
| try: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The list of weight extensions here (
.gguf,.safetensors,.bin) is slightly inconsistent with other parts of the codebase (e.g., line 634 includes.ptand.pth). While these three are the most common for this application, ensuring a single source of truth for supported weight formats would be beneficial.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The extensions here (.gguf, .safetensors, .bin) are intentionally scoped to the formats relevant to local model detection. .pt and .pth at line 634 serve a different purpose (training checkpoint detection). A unified constant could help but is out of scope for this PR.