feat(doctor): add RAM and disk space checks to soup doctor command wi… (#7)

* feat(doctor): add RAM and disk space checks to soup doctor command with tests and updated docs

* fix(doctor): resolve subprocess type checker error by manually validating macOS RAM query return code
This commit is contained in:
Salil Mhatre 2026-04-05 17:58:05 +05:30 committed by GitHub
parent 7ed0b3225e
commit de5505c7d8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 90 additions and 6 deletions

View File

@ -87,7 +87,7 @@ soup_cli/
runs.py # soup runs (list/show/compare/delete experiments)
sweep.py # soup sweep (grid/random hyperparameter search)
diff.py # soup diff (compare two models side-by-side)
doctor.py # soup doctor (dependency + GPU checker)
doctor.py # soup doctor (resources, dependency + GPU checker)
quickstart.py # soup quickstart (20-example TinyLlama demo)
ui.py # soup ui (launches FastAPI web UI)
ui/
@ -174,7 +174,7 @@ soup runs compare # Side-by-side loss curves
soup runs delete <id> # Remove from DB
soup sweep # Hyperparameter search (grid/random)
soup diff # Compare two models' outputs
soup doctor # Check system + dependencies
soup doctor # Check system resources, GPU, + dependencies
soup quickstart # One-command demo
soup ui # Web UI (Dashboard, Training, Data Explorer, Chat)
soup version # Show version (--full for details)
@ -399,7 +399,7 @@ soup version # Show version (--full for details)
| test_diff.py | Diff prompts collection, metrics, CLI |
| test_deepspeed.py | DeepSpeed configs, multi-GPU detection, trainer integration |
| test_errors.py | Friendly error messages, --verbose flag, error mapping |
| test_doctor.py | `soup doctor` command, version checking, dependency table |
| test_doctor.py | `soup doctor` command, version checking, system resources, dependency table |
| test_quickstart.py | `soup quickstart` demo, data/config creation, --dry-run |
| test_grpo.py | GRPO config, rewards, data prep, template, sweep shortcuts |
| test_progress.py | Rich download progress bar, `_enable_hf_transfer_progress` |

View File

@ -165,7 +165,7 @@ pytest tests/ --cov=soup_cli --cov-report=html
| test_diff.py | Diff prompts collection, metrics, CLI |
| test_deepspeed.py | DeepSpeed configs, multi-GPU detection, trainer integration |
| test_errors.py | Friendly error messages, --verbose flag, error mapping |
| test_doctor.py | `soup doctor` command, version checking, dependency table |
| test_doctor.py | `soup doctor` command, version checking, system resources, dependency table |
| test_quickstart.py | `soup quickstart` demo, data/config creation, --dry-run |
| test_grpo.py | GRPO config, rewards, data prep, template, sweep shortcuts |
| test_progress.py | Rich download progress bar, `_enable_hf_transfer_progress` |

View File

@ -1151,7 +1151,7 @@ Check your environment for compatibility issues:
soup doctor
```
Shows: Python version, GPU availability, all dependency versions, and fix suggestions.
Shows: Python version, GPU availability, system resources (RAM/Disk), all dependency versions, and fix suggestions.
## Version Info
@ -1556,7 +1556,7 @@ pip install soup-cli
### Quick environment check
```bash
soup doctor # Shows GPU, dependencies, and version info
soup doctor # Shows GPU, system resources, dependencies, and version info
```
## Development

View File

@ -58,6 +58,9 @@ def doctor():
# GPU check
_check_gpu()
# Resources check
_check_resources()
# Dependencies table
table = Table(title="Dependencies")
table.add_column("Package", style="bold")
@ -171,6 +174,78 @@ def _check_gpu():
)
def _check_resources():
"""Check RAM and Disk space and display info."""
import shutil
table = Table(title="System Resources")
table.add_column("Resource", style="bold")
table.add_column("Value")
# RAM
ram_str = "Unknown"
try:
import psutil
mem = psutil.virtual_memory()
ram_str = f"{mem.total / (1024 ** 3):.0f} GB"
except ImportError:
pass
if ram_str == "Unknown":
if platform.system() == "Linux":
try:
with open("/proc/meminfo", "r") as f:
for line in f:
if line.startswith("MemTotal:"):
kb = int(line.split()[1])
ram_str = f"{kb / (1024 ** 2):.0f} GB"
break
except Exception:
pass
elif platform.system() == "Darwin":
try:
import subprocess
res = subprocess.run(["sysctl", "-n", "hw.memsize"], capture_output=True, text=True)
if res.returncode == 0:
ram_str = f"{int(res.stdout.strip()) / (1024 ** 3):.0f} GB"
except Exception:
pass
elif platform.system() == "Windows":
try:
import ctypes
class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [
("dwLength", ctypes.c_ulong),
("dwMemoryLoad", ctypes.c_ulong),
("ullTotalPhys", ctypes.c_ulonglong),
("ullAvailPhys", ctypes.c_ulonglong),
("ullTotalPageFile", ctypes.c_ulonglong),
("ullAvailPageFile", ctypes.c_ulonglong),
("ullTotalVirtual", ctypes.c_ulonglong),
("ullAvailVirtual", ctypes.c_ulonglong),
("sullAvailExtendedVirtual", ctypes.c_ulonglong),
]
stat = MEMORYSTATUSEX()
stat.dwLength = ctypes.sizeof(MEMORYSTATUSEX)
ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
ram_str = f"{stat.ullTotalPhys / (1024 ** 3):.0f} GB"
except Exception:
pass
table.add_row("RAM", ram_str)
# Disk
try:
usage = shutil.disk_usage(".")
disk_str = f"{usage.free / (1024 ** 3):.0f} GB"
except Exception:
disk_str = "Unknown"
table.add_row("Disk", disk_str)
console.print(table)
console.print()
def _check_torchvision_compat(issues: list):
"""Check that torchvision version is compatible with torch."""
try:

View File

@ -80,6 +80,15 @@ def test_doctor_shows_gpu_section():
assert "GPU" in result.output
def test_doctor_shows_system_resources():
"""soup doctor shows System Resources section."""
result = runner.invoke(app, ["doctor"])
assert result.exit_code == 0
assert "System Resources" in result.output
assert "RAM" in result.output
assert "Disk" in result.output
def test_doctor_checks_torch():
"""soup doctor checks for torch."""
result = runner.invoke(app, ["doctor"])