lmms 1.1.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: lmms
3
+ Version: 1.1.7
4
+ Summary: Builder, hardware profiler, and toolchain manager for the LMMs Ecosystem.
5
+ Author: MarkanM
6
+ Requires-Python: >=3.9
7
+ Requires-Dist: psutil
8
+ Requires-Dist: rich
9
+ Requires-Dist: requests
10
+ Dynamic: author
11
+ Dynamic: requires-dist
12
+ Dynamic: requires-python
13
+ Dynamic: summary
@@ -0,0 +1,10 @@
1
+ lmms_builder/__init__.py,sha256=s4WXUCdtZenBWJdOJj8FZE-SsdNjqm_9x6igsTb_quM,52
2
+ lmms_builder/cli.py,sha256=CLfuR3X_hVT6ryxMRH_oGBy9fgsV2xM1dKcHNlOVU0I,5575
3
+ lmms_builder/hardware.py,sha256=R6d85tn3PCOQYgj2vMzmFE8iz7nCO9s_OzO8mBZnwSo,3092
4
+ lmms_builder/installer.py,sha256=wqNT1BAEKx_hjlLmM05hErTLlYLmdkCF4SdUfT0bsBU,13779
5
+ lmms_builder/reporter.py,sha256=AqYfzThEbDio_ftJ5Q2fo9isd_YFFjTsXckC-kLug4A,3898
6
+ lmms-1.1.7.dist-info/METADATA,sha256=KMn5d-UaekkTc5up7nUzB9GgjU7G8acVYRou4i7x-d8,317
7
+ lmms-1.1.7.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
8
+ lmms-1.1.7.dist-info/entry_points.txt,sha256=BpRHTzaz0SeFuDpI_5OO1bB1IsREzlfY_mpXeHOSJok,141
9
+ lmms-1.1.7.dist-info/top_level.txt,sha256=CvyaNnnSTryMQZoYWbnMFuzmbwXLwjS8KWanpLg0kl0,13
10
+ lmms-1.1.7.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ LMMs = lmms_builder.cli:unified_main
3
+ LMMs-uninstall = lmms_builder.cli:uninstall_main
4
+ lmms = lmms_builder.cli:unified_main
@@ -0,0 +1 @@
1
+ lmms_builder
@@ -0,0 +1,3 @@
1
+ """
2
+ LMMS Builder - Bootstrapper for the LMMs OS
3
+ """
lmms_builder/cli.py ADDED
@@ -0,0 +1,136 @@
1
+ import argparse
2
+ import sys
3
+ import os
4
+ import shutil
5
+ import platform
6
+ import subprocess
7
+ import json
8
+ from pathlib import Path
9
+
10
+ from . import hardware
11
+ from . import reporter
12
+ from . import installer
13
+
14
+ LMMS_CONFIG_DIR = Path(os.path.expanduser("~/.lmms"))
15
+ LMMS_PROJECT_DIR = Path(os.path.expanduser("~/.lmms/app/src"))
16
+ LMMS_APP_DIR = Path(os.path.expanduser("~/.lmms/app"))
17
+
18
+ def print_help():
19
+ reporter.print_header()
20
+ print("\n[bold yellow]=== LMMs Complete Command List ===[/bold yellow]")
21
+ print("\n[bold cyan]1) Installer / Updater[/bold cyan]")
22
+ print(" LMMs -check : Automatically detects OS, hardware compatibility, and installed LMMs components.")
23
+ print(" LMMs install --all : Downloads and installs the entire LMMs ecosystem.")
24
+ print(" LMMs update --all : Forces a download of the latest code/binary version from GitHub Releases.")
25
+
26
+ print("\n[bold cyan]2) Uninstall[/bold cyan]")
27
+ print(" LMMs-uninstall -all : Deletes the LMMs source code but keeps Pip packages and User Data safe.")
28
+ print(" LMMs-uninstall -all --purge : Completely removes ALL LMMs code, pip environments, and user data (Factory Reset).")
29
+
30
+ print("\n[bold cyan]3) Launch Options[/bold cyan]")
31
+ print(" LMMs --gui|--cli|--engine : Directly runs the specified component overriding the default.")
32
+ print(" LMMs : Runs the default configured component.\n")
33
+
34
+
35
+ def builder_main():
36
+ args_lower = [arg.lower() for arg in sys.argv]
37
+ if "-check" in args_lower:
38
+ reporter.print_step("Running LMMs System Check...")
39
+ hw = hardware.detect_all()
40
+ reporter.print_hardware_report(hw)
41
+ installed = installer.get_installed_components()
42
+ reporter.print_step("Installed Components:")
43
+ for k, v in installed.items():
44
+ if v:
45
+ print(f" - {k.capitalize()}")
46
+ if not any(installed.values()):
47
+ print(" - None")
48
+ return
49
+
50
+ if "install" in args_lower or "update" in args_lower:
51
+ force_update = "update" in args_lower
52
+ if "--all" in args_lower:
53
+ installer.install_component("all", force=force_update)
54
+ else:
55
+ print("Usage: LMMs install --all")
56
+ return
57
+
58
+ print_help()
59
+
60
+
61
+ def uninstall_main():
62
+ install_dir = Path.home() / ".lmms" / "bin" if platform.system().lower() == "windows" else Path("/usr/local/bin")
63
+ fallback_dir = Path.home() / ".local" / "bin"
64
+
65
+ args_lower = [arg.lower() for arg in sys.argv]
66
+ if "-all" not in args_lower:
67
+ print("Usage: LMMs-uninstall -all [--purge]")
68
+ return
69
+
70
+ if "--purge" in args_lower:
71
+ ans = input("WARNING: This will PURGE LMMs code, Python environments, and ALL user data (models, chats). Continue? [y/N]: ")
72
+ if ans.lower() == 'y':
73
+ shutil.rmtree(LMMS_APP_DIR, ignore_errors=True)
74
+ shutil.rmtree(LMMS_CONFIG_DIR, ignore_errors=True)
75
+
76
+ # Remove global binaries
77
+ bin_name = "lmms.exe" if platform.system().lower() == "windows" else "lmms"
78
+ (install_dir / bin_name).unlink(missing_ok=True)
79
+ (fallback_dir / bin_name).unlink(missing_ok=True)
80
+ reporter.print_success("Purge complete. All LMMs files and data have been removed.")
81
+ else:
82
+ ans = input("INFO: This will delete the LMMs source code but KEEP your Python environment and User Data safe. Continue? [y/N]: ")
83
+ if ans.lower() == 'y':
84
+ src_dir = LMMS_APP_DIR / "src"
85
+ shutil.rmtree(src_dir, ignore_errors=True)
86
+
87
+ # Mark uninstalled
88
+ installed = installer.get_installed_components()
89
+ installed["all"] = False
90
+ with open(installer.get_registry_path(), "w") as f:
91
+ json.dump(installed, f)
92
+ reporter.print_success("Code uninstalled. Pip packages and User Data remain safe.")
93
+
94
+
95
+ def lmms_main():
96
+ args_lower = [arg.lower() for arg in sys.argv]
97
+ mode = None
98
+ if "--gui" in args_lower: mode = "gui"
99
+ elif "--cli" in args_lower: mode = "cli"
100
+ elif "--engine" in args_lower: mode = "engine"
101
+ else:
102
+ # Load default
103
+ config_path = LMMS_CONFIG_DIR / "config" / "config.json"
104
+ if config_path.exists():
105
+ with open(config_path, "r") as f:
106
+ try: mode = json.load(f).get("default_mode", "cli")
107
+ except: mode = "cli"
108
+ else:
109
+ mode = "cli"
110
+
111
+ # Always use the launcher script through the VENV python
112
+ venv_python = LMMS_APP_DIR / "venv" / "Scripts" / "python.exe" if platform.system().lower() == "windows" else LMMS_APP_DIR / "venv" / "bin" / "python"
113
+ launcher_script = LMMS_PROJECT_DIR / "lmms_launcher.pyc"
114
+ if not launcher_script.exists():
115
+ launcher_script = LMMS_PROJECT_DIR / "lmms_launcher.py"
116
+
117
+ if launcher_script.exists():
118
+ cmd = [str(venv_python), str(launcher_script)]
119
+ if mode and mode != "cli":
120
+ cmd.append(mode)
121
+
122
+ forward_args = [a for a in sys.argv[1:] if a not in ["--gui", "--cli", "--engine"]]
123
+ cmd.extend(forward_args)
124
+
125
+ os.execvp(str(venv_python), cmd)
126
+ else:
127
+ print("LMMs launcher not found. Please run: LMMs install --all")
128
+
129
+
130
+ def unified_main():
131
+ """Entry point for the 'LMMs' command."""
132
+ args_lower = [arg.lower() for arg in sys.argv]
133
+ if len(args_lower) > 1 and args_lower[1] in ["install", "update", "-check", "-help", "--help"]:
134
+ builder_main()
135
+ else:
136
+ lmms_main()
@@ -0,0 +1,106 @@
1
+ import os
2
+ import platform
3
+ import psutil
4
+ import subprocess
5
+
6
+ def detect_os():
7
+ return {
8
+ "system": platform.system(),
9
+ "release": platform.release(),
10
+ "architecture": platform.machine(),
11
+ }
12
+
13
+ def detect_cpu():
14
+ return {
15
+ "cores": psutil.cpu_count(logical=False),
16
+ "threads": psutil.cpu_count(logical=True),
17
+ }
18
+
19
+ def detect_ram():
20
+ mem = psutil.virtual_memory()
21
+ return {
22
+ "total_gb": round(mem.total / (1024**3), 2),
23
+ "available_gb": round(mem.available / (1024**3), 2)
24
+ }
25
+
26
+ def detect_gpu():
27
+ try:
28
+ # Check nvidia-smi
29
+ result = subprocess.run(
30
+ ["nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader"],
31
+ capture_output=True, text=True, check=True
32
+ )
33
+ lines = result.stdout.strip().split("\n")
34
+ if lines and lines[0]:
35
+ parts = lines[0].split(",")
36
+ name = parts[0].strip()
37
+ vram_str = parts[1].strip()
38
+ vram_mb = int(vram_str.replace(" MiB", ""))
39
+ return {
40
+ "detected": True,
41
+ "name": name,
42
+ "vram_gb": round(vram_mb / 1024, 2),
43
+ "type": "nvidia"
44
+ }
45
+ except (FileNotFoundError, subprocess.CalledProcessError):
46
+ pass
47
+
48
+ return {
49
+ "detected": False,
50
+ "name": "None",
51
+ "vram_gb": 0.0,
52
+ "type": "none"
53
+ }
54
+
55
+ def detect_cuda():
56
+ try:
57
+ result = subprocess.run(["nvcc", "--version"], capture_output=True, text=True, check=True)
58
+ for line in result.stdout.split("\n"):
59
+ if "release" in line:
60
+ version = line.split("release")[1].strip().split(",")[0]
61
+ return {"available": True, "version": version}
62
+ except (FileNotFoundError, subprocess.CalledProcessError):
63
+ pass
64
+
65
+ try:
66
+ result = subprocess.run(["nvidia-smi"], capture_output=True, text=True, check=True)
67
+ if "CUDA Version: " in result.stdout:
68
+ version = result.stdout.split("CUDA Version: ")[1].split(" ")[0]
69
+ return {"available": True, "version": version}
70
+ except (FileNotFoundError, subprocess.CalledProcessError):
71
+ pass
72
+
73
+ return {"available": False, "version": None}
74
+
75
+ def detect_python():
76
+ return {
77
+ "version": platform.python_version()
78
+ }
79
+
80
+ import shutil
81
+ from pathlib import Path
82
+
83
+ def detect_tools():
84
+ system = platform.system().lower()
85
+ vscode = shutil.which("code") is not None
86
+ bt = False
87
+ if system == "windows":
88
+ bt_path = Path("C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools")
89
+ bt_path2 = Path("C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools")
90
+ bt = bt_path.exists() or bt_path2.exists()
91
+
92
+ return {
93
+ "vscode": vscode,
94
+ "build_tools": bt
95
+ }
96
+
97
+ def detect_all():
98
+ return {
99
+ "os": detect_os(),
100
+ "cpu": detect_cpu(),
101
+ "ram": detect_ram(),
102
+ "gpu": detect_gpu(),
103
+ "cuda": detect_cuda(),
104
+ "python": detect_python(),
105
+ "tools": detect_tools()
106
+ }
@@ -0,0 +1,341 @@
1
+ import subprocess
2
+ import sys
3
+ import json
4
+ import os
5
+ import platform
6
+ import shutil
7
+ import zipfile
8
+ import requests
9
+ from pathlib import Path
10
+ from rich.progress import Progress, TextColumn, BarColumn, DownloadColumn, TransferSpeedColumn, TimeRemainingColumn
11
+ from . import reporter
12
+
13
+ def check_python_version():
14
+ return sys.version_info >= (3, 9)
15
+
16
+ def check_prerequisites():
17
+ system = platform.system().lower()
18
+ missing_tools = []
19
+
20
+ reporter.print_step("Checking prerequisites...")
21
+
22
+ # Check VS Code
23
+ if not shutil.which("code"):
24
+ missing_tools.append(("Visual Studio Code", "Microsoft.VisualStudioCode"))
25
+
26
+ # Check Build Tools (Windows only)
27
+ if system == "windows":
28
+ bt_path = Path("C:/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools")
29
+ bt_path2 = Path("C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools")
30
+ if not bt_path.exists() and not bt_path2.exists():
31
+ missing_tools.append(("Visual Studio Build Tools (C++)", "Microsoft.VisualStudio.2022.BuildTools"))
32
+
33
+ if missing_tools:
34
+ reporter.print_error("The following required tools are missing:")
35
+ for name, _ in missing_tools:
36
+ print(f" - {name}")
37
+
38
+ ans = input("Would you like to automatically install them now? [y/N]: ")
39
+ if ans.lower() == 'y':
40
+ for name, pkg_id in missing_tools:
41
+ reporter.print_step(f"Installing {name}...")
42
+ if system == "windows":
43
+ try:
44
+ subprocess.run(["winget", "install", "-e", "--id", pkg_id, "--accept-package-agreements", "--accept-source-agreements"], check=True)
45
+ reporter.print_success(f"✓ {name} installed.")
46
+ except:
47
+ reporter.print_error(f"Failed to install {name} via winget. Please install it manually.")
48
+ else:
49
+ reporter.print_error("Auto-install for this tool is only supported on Windows via winget.")
50
+ else:
51
+ reporter.print_error("Please install them manually to avoid issues.")
52
+ else:
53
+ reporter.print_success("✓ All prerequisites are installed.")
54
+ return True
55
+
56
+ def get_registry_path():
57
+ p = Path(os.path.expanduser("~/.lmms/config/installed_components.json"))
58
+ p.parent.mkdir(parents=True, exist_ok=True)
59
+ return p
60
+
61
+ def get_installed_components():
62
+ reg = get_registry_path()
63
+ if reg.exists():
64
+ try:
65
+ with open(reg, "r") as f:
66
+ return json.load(f)
67
+ except:
68
+ pass
69
+ return {}
70
+
71
+ def mark_installed(component):
72
+ installed = get_installed_components()
73
+ installed[component] = True
74
+ with open(get_registry_path(), "w") as f:
75
+ json.dump(installed, f)
76
+
77
+ def get_app_dir():
78
+ return Path.home() / ".lmms" / "app"
79
+
80
+ def get_venv_python():
81
+ system = platform.system().lower()
82
+ app_dir = get_app_dir()
83
+ if system == "windows":
84
+ return app_dir / "venv" / "Scripts" / "python.exe"
85
+ return app_dir / "venv" / "bin" / "python"
86
+
87
+ def install_python_os(system):
88
+ reporter.print_step("Attempting to auto-install Python...")
89
+ try:
90
+ if system == "linux":
91
+ reporter.print_step("Using apt to install Python 3.11... (You may be prompted for sudo password)")
92
+ subprocess.run(["sudo", "apt", "update"], check=True)
93
+ subprocess.run(["sudo", "apt", "install", "-y", "python3.11", "python3.11-venv"], check=True)
94
+ return True
95
+ elif system == "darwin":
96
+ reporter.print_step("Using brew to install Python 3.11...")
97
+ subprocess.run(["brew", "install", "python@3.11"], check=True)
98
+ return True
99
+ elif system == "windows":
100
+ reporter.print_step("Using winget to install Python 3.11...")
101
+ subprocess.run(["winget", "install", "-e", "--id", "Python.Python.3.11", "--accept-package-agreements", "--accept-source-agreements"], check=True)
102
+ return True
103
+ except subprocess.CalledProcessError as e:
104
+ reporter.print_error(f"Failed to auto-install Python: {e}")
105
+ return False
106
+
107
+ def ensure_stable_python():
108
+ system = platform.system().lower()
109
+ # Check if a stable version (3.11 or 3.12) exists
110
+ for py_cmd in ["python3.11", "python3.12", "python3.10"]:
111
+ cmd_path = shutil.which(py_cmd)
112
+ if cmd_path:
113
+ reporter.print_success(f"✓ Found compatible stable Python at {cmd_path}")
114
+ return cmd_path
115
+
116
+ # Fallback to general 'python3' or 'python' and check version
117
+ for py_cmd in ["python3", "python"]:
118
+ cmd_path = shutil.which(py_cmd)
119
+ if cmd_path:
120
+ try:
121
+ output = subprocess.check_output([cmd_path, "--version"], text=True, stderr=subprocess.STDOUT)
122
+ if "Python 3.11" in output or "Python 3.12" in output or "Python 3.10" in output:
123
+ reporter.print_success(f"✓ Found compatible stable Python at {cmd_path}")
124
+ return cmd_path
125
+ except Exception:
126
+ continue
127
+
128
+ # Not found!
129
+ reporter.print_error("Warning: A highly compatible Python version (3.11 or 3.12) was NOT found on your system!")
130
+ reporter.print_error("Llama-cpp-python and GPU acceleration run best on Python 3.11/3.12. Using Python 3.13 may cause build failures.")
131
+ try:
132
+ ans = input("Would you like me to attempt to auto-download and install Python 3.11 now? (y/n): ").strip().lower()
133
+ except EOFError:
134
+ ans = "n"
135
+
136
+ if ans == "y":
137
+ if install_python_os(system):
138
+ # Check again
139
+ for py_cmd in ["python3.11", "python3.12"]:
140
+ cmd_path = shutil.which(py_cmd)
141
+ if cmd_path:
142
+ reporter.print_success(f"✓ Successfully installed and found Python at {cmd_path}")
143
+ return cmd_path
144
+ reporter.print_error("Python installation finished but executable not found in PATH. You may need to restart your terminal.")
145
+ else:
146
+ reporter.print_error("Auto-installation failed.")
147
+
148
+ reporter.print_step("Falling back to current Python executable (this may be 3.13 and might cause issues)...")
149
+ return sys.executable
150
+
151
+ def setup_venv(python_path):
152
+ app_dir = get_app_dir()
153
+ venv_dir = app_dir / "venv"
154
+ if not venv_dir.exists():
155
+ reporter.print_step(f"Creating isolated Virtual Environment at {venv_dir}...")
156
+ try:
157
+ subprocess.run([python_path, "-m", "venv", str(venv_dir)], check=True)
158
+ reporter.print_success("✓ Virtual Environment created successfully.")
159
+ except subprocess.CalledProcessError as e:
160
+ reporter.print_error(f"Failed to create VENV: {e}")
161
+ return False
162
+ else:
163
+ reporter.print_success("✓ Virtual Environment already exists.")
164
+
165
+ if platform.system().lower() == "windows":
166
+ try:
167
+ subprocess.run(["attrib", "+h", str(app_dir.parent)], shell=True, check=False)
168
+ except: pass
169
+
170
+ return True
171
+
172
+ def download_source(component_name):
173
+ app_dir = get_app_dir()
174
+ src_dir = app_dir / "src"
175
+
176
+ repo_url = "https://github.com/MarkanM-Official/LMMs/archive/refs/heads/main.zip"
177
+ zip_path = app_dir / "main.zip"
178
+
179
+ app_dir.mkdir(parents=True, exist_ok=True)
180
+
181
+ reporter.print_step("Downloading latest LMMs source code...")
182
+ try:
183
+ response = requests.get(repo_url, stream=True)
184
+ response.raise_for_status()
185
+ total_size = int(response.headers.get('content-length', 0))
186
+
187
+ with Progress(
188
+ TextColumn("[bold cyan]{task.description}"),
189
+ BarColumn(bar_width=40),
190
+ "[progress.percentage]{task.percentage:>3.1f}%",
191
+ "•",
192
+ DownloadColumn(),
193
+ "•",
194
+ TransferSpeedColumn(),
195
+ "•",
196
+ TimeRemainingColumn(),
197
+ console=reporter.console
198
+ ) as progress:
199
+ task = progress.add_task("Downloading...", total=total_size if total_size > 0 else None)
200
+ with open(zip_path, "wb") as file:
201
+ for chunk in response.iter_content(chunk_size=8192):
202
+ if chunk:
203
+ file.write(chunk)
204
+ progress.update(task, advance=len(chunk))
205
+
206
+ reporter.print_step("Extracting source code...")
207
+ if src_dir.exists():
208
+ shutil.rmtree(src_dir)
209
+
210
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
211
+ zip_ref.extractall(app_dir)
212
+
213
+ # The zip extracts as "LMMs-main", rename it to "src"
214
+ extracted_dir = app_dir / "LMMs-main"
215
+ if extracted_dir.exists():
216
+ extracted_dir.rename(src_dir)
217
+
218
+ zip_path.unlink()
219
+ reporter.print_success(f"✓ Source code installed to {src_dir}")
220
+ return True
221
+ except Exception as e:
222
+ reporter.print_error(f"Download/Extraction failed: {e}")
223
+ return False
224
+
225
+ def create_global_wrapper(component_name):
226
+ system = platform.system().lower()
227
+ app_dir = get_app_dir()
228
+ venv_python = get_venv_python()
229
+ launcher_script = app_dir / "src" / "lmms_launcher.py"
230
+
231
+ if system == "windows":
232
+ install_dir = Path.home() / ".lmms" / "bin"
233
+ install_path = install_dir / "lmms.bat"
234
+ wrapper_content = f'@echo off\n"{venv_python}" "{launcher_script}" %*'
235
+ else:
236
+ install_dir = Path("/usr/local/bin")
237
+ if not os.access(install_dir, os.W_OK):
238
+ install_dir = Path.home() / ".local" / "bin"
239
+ install_path = install_dir / "lmms"
240
+ wrapper_content = f'#!/bin/bash\n"{venv_python}" "{launcher_script}" "$@"'
241
+
242
+ install_dir.mkdir(parents=True, exist_ok=True)
243
+
244
+ try:
245
+ with open(install_path, "w") as f:
246
+ f.write(wrapper_content)
247
+
248
+ if system != "windows":
249
+ os.chmod(install_path, 0o755)
250
+
251
+ reporter.print_success(f"✓ Global wrapper created at {install_path}")
252
+ return True
253
+ except Exception as e:
254
+ reporter.print_error(f"Failed to create wrapper: {e}")
255
+ return False
256
+
257
+ def install_pip_requirements(component_name):
258
+ req_file = get_app_dir() / "src" / "requirements.txt"
259
+ if not req_file.exists():
260
+ reporter.print_error("requirements.txt not found in source code!")
261
+ return False
262
+
263
+ reporter.print_step("Installing Pip Requirements...")
264
+ reporter.print_step("The following dependencies will be installed:")
265
+ try:
266
+ with open(req_file, "r") as f:
267
+ for line in f:
268
+ if line.strip() and not line.startswith("#"):
269
+ print(f" - {line.strip()}")
270
+ except: pass
271
+
272
+ try:
273
+ venv_python = get_venv_python()
274
+ # Upgrade pip
275
+ subprocess.run([str(venv_python), "-m", "pip", "install", "--upgrade", "pip"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
276
+
277
+ # Install requirements
278
+ cmd = [str(venv_python), "-m", "pip", "install", "-r", str(req_file)]
279
+ subprocess.run(cmd, check=True)
280
+
281
+ # Check if playwright is in requirements and install browser binaries
282
+ with open(req_file, "r") as f:
283
+ reqs_text = f.read().lower()
284
+ if "playwright" in reqs_text:
285
+ reporter.print_step("Installing Playwright browser binaries...")
286
+ subprocess.run([str(venv_python), "-m", "playwright", "install", "chromium"], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
287
+
288
+ reporter.print_success("✓ Pip requirements installed successfully.")
289
+ return True
290
+ except subprocess.CalledProcessError as e:
291
+ reporter.print_error(f"Failed to install pip requirements: {e}")
292
+ return False
293
+
294
+ def install_component(component_name, force=False):
295
+ installed = get_installed_components()
296
+ if installed.get(component_name) and not force:
297
+ reporter.print_success(f"✓ {component_name.capitalize()} is already installed. Skipping. (Use update to force)")
298
+ return True, ""
299
+
300
+ if force:
301
+ reporter.print_step(f"Updating {component_name.capitalize()} to the latest version...")
302
+ if component_name == "all":
303
+ try:
304
+ reporter.print_step("Checking for LMMs-builder updates...")
305
+ subprocess.run([sys.executable, "-m", "pip", "install", "--upgrade", "lmms-builder"],
306
+ check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
307
+ except Exception:
308
+ pass
309
+ else:
310
+ reporter.print_step(f"Installing {component_name.capitalize()}...")
311
+
312
+ if component_name in ["engine", "backend", "gui", "cli", "all"]:
313
+ # 0. Check Prerequisites
314
+ check_prerequisites()
315
+
316
+ # 1. Check Python version
317
+ python_path = ensure_stable_python()
318
+
319
+ # 2. Setup VENV
320
+ if not setup_venv(python_path):
321
+ return False, "VENV setup failed"
322
+
323
+ # 3. Download Source
324
+ if not download_source(component_name):
325
+ return False, "Source download failed"
326
+
327
+ # 4. Install Pip Requirements inside VENV
328
+ if not install_pip_requirements(component_name):
329
+ return False, "Pip installation failed"
330
+
331
+ # 5. Create Global Wrapper
332
+ if not create_global_wrapper(component_name):
333
+ return False, "Wrapper creation failed"
334
+
335
+ mark_installed(component_name)
336
+ return True, ""
337
+
338
+ else:
339
+ reporter.print_error(f"Unknown component: {component_name}")
340
+ return False, "Unknown component"
341
+
@@ -0,0 +1,101 @@
1
+ from rich.console import Console
2
+ from rich.table import Table
3
+ from rich.panel import Panel
4
+
5
+ console = Console()
6
+
7
+ def print_header():
8
+ console.print(Panel("[bold cyan]LMMs Builder[/bold cyan]\n[bold yellow]Powered by MarkanM. For more details: Lmms.markanm.com[/bold yellow]\n[dim]Bootstrapper & Hardware Profiler[/dim]", border_style="cyan"))
9
+
10
+ def print_hardware_report(hw):
11
+ table = Table(title="Hardware Detection Report", show_header=True, header_style="bold magenta")
12
+ table.add_column("Component", style="cyan")
13
+ table.add_column("Details", style="green")
14
+
15
+ os_info = f"{hw['os']['system']} {hw['os']['release']} ({hw['os']['architecture']})"
16
+ table.add_row("OS", os_info)
17
+
18
+ cpu_info = f"{hw['cpu']['cores']} Cores / {hw['cpu']['threads']} Threads"
19
+ table.add_row("CPU", cpu_info)
20
+
21
+ ram_info = f"{hw['ram']['total_gb']} GB (Available: {hw['ram']['available_gb']} GB)"
22
+ table.add_row("RAM", ram_info)
23
+
24
+ if hw['gpu']['detected']:
25
+ gpu_info = f"{hw['gpu']['name']} ({hw['gpu']['vram_gb']} GB VRAM)"
26
+ else:
27
+ gpu_info = "None Detected"
28
+ table.add_row("GPU", gpu_info)
29
+
30
+ if hw['cuda']['available']:
31
+ cuda_info = f"Version {hw['cuda']['version']}"
32
+ else:
33
+ cuda_info = "Not Available"
34
+ table.add_row("CUDA", cuda_info)
35
+
36
+ table.add_row("Python", hw['python']['version'])
37
+
38
+ vscode_status = "[green]Installed[/green]" if hw['tools']['vscode'] else "[red]Not Installed[/red]"
39
+ table.add_row("VS Code", vscode_status)
40
+
41
+ import platform
42
+ if platform.system().lower() == "windows":
43
+ bt_status = "[green]Installed[/green]" if hw['tools']['build_tools'] else "[red]Not Installed[/red]"
44
+ table.add_row("C++ Build Tools", bt_status)
45
+
46
+ console.print(table)
47
+
48
+ def print_compatibility_report(hw):
49
+ console.print("\n[bold magenta]========================[/bold magenta]")
50
+ console.print("[bold cyan]LMMS Compatibility[/bold cyan]\n")
51
+
52
+ engine_comp = 100
53
+ cli_comp = 100
54
+ gui_comp = 100
55
+ air_comp = 100 if hw['ram']['total_gb'] >= 16 else 50
56
+ vllm_comp = 100 if hw['gpu']['detected'] and hw['gpu']['vram_gb'] >= 8 else 0
57
+
58
+ console.print(f"Engine: {engine_comp}%")
59
+ console.print(f"CLI: {cli_comp}%")
60
+ console.print(f"GUI: {gui_comp}%")
61
+ console.print(f"Air Engine: {air_comp}%")
62
+ console.print(f"vLLM: {vllm_comp}%")
63
+
64
+ overall = (engine_comp + cli_comp + gui_comp + air_comp + vllm_comp) // 5
65
+ console.print(f"\n[bold green]Overall: {overall}%[/bold green]")
66
+ console.print("[bold magenta]========================[/bold magenta]\n")
67
+
68
+ def print_benchmark_estimate(hw):
69
+ console.print("\n[bold cyan]Hardware Benchmark (Estimated)[/bold cyan]")
70
+
71
+ # Calculate an estimated TPS multiplier based on hardware
72
+ base = 5.0
73
+ if hw['gpu']['detected']:
74
+ base += hw['gpu']['vram_gb'] * 2
75
+ else:
76
+ base += hw['cpu']['cores'] * 1.5
77
+
78
+ console.print("llama.cpp (7B): ~[green]{:.1f} tok/s[/green]".format(base))
79
+ console.print("ollama (7B): ~[green]{:.1f} tok/s[/green]".format(base * 0.95))
80
+ if hw['ram']['total_gb'] >= 16:
81
+ console.print("air (70B): ~[yellow]{:.1f} tok/s[/yellow]".format(max(1.0, base / 10)))
82
+ else:
83
+ console.print("air (70B): [red]Not Recommended (Insufficent RAM)[/red]")
84
+
85
+ def print_step(step_msg):
86
+ console.print(f"[bold blue]>[/bold blue] {step_msg}")
87
+
88
+ def print_success(msg):
89
+ console.print(f"[bold green]✓[/bold green] {msg}")
90
+
91
+ def print_error(msg):
92
+ console.print(f"[bold red]✗[/bold red] {msg}")
93
+
94
+ def print_autoset_summary(mode, runtime):
95
+ console.print(Panel(
96
+ f"[bold green]AutoSet Complete![/bold green]\n\n"
97
+ f"[cyan]Default Mode:[/cyan] {mode}\n"
98
+ f"[cyan]Default Runtime:[/cyan] {runtime}\n\n"
99
+ "Run [bold magenta]lmms[/bold magenta] to launch.",
100
+ border_style="green"
101
+ ))