# Beszel Tool for OpenWebUI ## Description Permet à Effi d'accéder aux métriques système de FolzoCorp, EffiServer et NAS Synology via Beszel. ## Installation 1. **Admin Panel** → **Tools** → **Create Tool** 2. **Name**: `Beszel System Monitor` 3. **Description**: `Monitor system resources via Beszel. Usage: get_all_stats(), get_system_stats("folzocorp"), check_alerts("folzocorp"), get_gpu_status("folzocorp"), can_run_comfyui()` 4. **Coller le code ci-dessous** 5. **Save and Activate** 6. **Configurer les variables** via la roue crantée (Vannes) ## Tool Code ```python import requests from pydantic import BaseModel, Field class Tools: class Valves(BaseModel): beszel_url: str = Field( default="http://192.168.1.25:8090", description="Beszel Hub URL", ) beszel_user: str = Field( default="admin", description="Beszel username (email)", ) beszel_password: str = Field( default="", description="Beszel password", ) def __init__(self): self.valves = self.Valves() def _login(self): try: response = requests.post( f"{self.valves.beszel_url}/api/collections/users/auth-with-password", json={ "identity": self.valves.beszel_user, "password": self.valves.beszel_password }, timeout=10 ) if response.status_code == 200: data = response.json() return data.get("token") return None except Exception: return None def _get_systems(self): token = self._login() if not token: return None try: headers = { "Authorization": f"{token}", "Content-Type": "application/json" } response = requests.get( f"{self.valves.beszel_url}/api/collections/systems/records", headers=headers, timeout=10 ) if response.status_code == 200: data = response.json() return data.get("items", []) return None except Exception: return None def _get_system_stats(self, system_id): token = self._login() if not token: return None try: headers = { "Authorization": f"{token}", "Content-Type": "application/json" } response = requests.get( f"{self.valves.beszel_url}/api/collections/systems/records/{system_id}", headers=headers, timeout=10 ) if response.status_code == 200: return response.json() return None except Exception: return None def _format_stats_text(self, stats, system_name="System"): lines = [] lines.append(f"=== {system_name} ===") lines.append(f"CPU: {stats.get('cpu', 0):.1f}%") lines.append(f"Mémoire: {stats.get('memory', 0):.1f}%") if 'swap' in stats: lines.append(f"Swap: {stats.get('swap', 0):.1f}%") if 'disk' in stats: disk_data = stats.get('disk', {}) if isinstance(disk_data, dict): for disk, info in disk_data.items(): lines.append(f"Disque {disk}: {info.get('used_percent', 0):.1f}%") if 'load' in stats: load = stats.get('load', []) if len(load) >= 3: lines.append(f"Load: {load[0]:.2f} {load[1]:.2f} {load[2]:.2f}") if 'gpu' in stats and stats['gpu']: lines.append("") lines.append("GPU:") gpu_data = stats.get('gpu', {}) if isinstance(gpu_data, dict): for gpu_name, gpu_info in gpu_data.items(): if isinstance(gpu_info, dict): vram_used = gpu_info.get('memory_used', 0) / (1024**3) vram_total = gpu_info.get('memory_total', 0) / (1024**3) vram_pct = (gpu_info.get('memory_used', 0) / max(gpu_info.get('memory_total', 1), 1)) * 100 lines.append(f" {gpu_name}:") lines.append(f" VRAM: {vram_used:.2f}GB / {vram_total:.2f}GB ({vram_pct:.1f}%)") lines.append(f" Utilisation GPU: {gpu_info.get('utilization_gpu', 0)}%") if 'temperature' in gpu_info: lines.append(f" Température: {gpu_info.get('temperature', 0)}°C") if 'power_draw' in gpu_info: lines.append(f" Consommation: {gpu_info.get('power_draw', 0)}W") if 'containers' in stats and stats['containers']: lines.append("") lines.append("Containers Docker:") for container in stats['containers']: name = container.get('name', 'unknown') cpu = container.get('cpu_percent', 0) mem = container.get('memory_percent', 0) lines.append(f" {name}: CPU {cpu:.1f}% MEM {mem:.1f}%") return "\n".join(lines) def _check_thresholds(self, stats, thresholds=None): if thresholds is None: thresholds = {'cpu': 80, 'memory': 90, 'vram': 90} alerts = [] cpu = stats.get('cpu', 0) if cpu > thresholds['cpu']: alerts.append(f"⚠️ CPU elevated: {cpu:.1f}%") memory = stats.get('memory', 0) if memory > thresholds['memory']: alerts.append(f"⚠️ Memory elevated: {memory:.1f}%") if 'gpu' in stats and stats['gpu']: gpu_data = stats.get('gpu', {}) if isinstance(gpu_data, dict): for gpu_name, gpu_info in gpu_data.items(): if isinstance(gpu_info, dict): vram_pct = (gpu_info.get('memory_used', 0) / max(gpu_info.get('memory_total', 1), 1)) * 100 if vram_pct > thresholds['vram']: alerts.append(f"⚠️ VRAM {gpu_name} elevated: {vram_pct:.1f}%") util = gpu_info.get('utilization_gpu', 0) if util > thresholds['vram']: alerts.append(f"⚠️ GPU {gpu_name} utilization elevated: {util}%") return alerts def get_all_stats(self): systems = self._get_systems() if not systems: return "[ERROR] Cannot connect to Beszel. Check URL, username and password." results = [] for sys_info in systems: sys_id = sys_info.get('id') sys_name = sys_info.get('name', sys_id) stats = self._get_system_stats(sys_id) if stats: results.append(self._format_stats_text(stats, sys_name)) else: results.append(f"=== {sys_name} ===\n[ERROR] Cannot get stats") return "\n\n".join(results) def get_system_stats(self, system_name: str): systems = self._get_systems() if not systems: return "[ERROR] Cannot connect to Beszel. Check URL, username and password." system_map = { 'folzocorp': 'folzocorp', 'effiserver': 'effiserver', 'synology': 'synology-nas', 'nas': 'synology-nas' } target_name = system_map.get(system_name.lower(), system_name.lower()) for sys_info in systems: sys_id = sys_info.get('id') sys_nm = sys_info.get('name', '') if sys_nm.lower() == target_name or sys_id == target_name: stats = self._get_system_stats(sys_id) if stats: return self._format_stats_text(stats, sys_nm or system_name) return f"[ERROR] Cannot get stats for {system_name}" available = [s.get('name', s.get('id')) for s in systems] return f"[ERROR] System '{system_name}' not found. Available: {', '.join(available)}" def check_alerts(self, system_name: str = "all"): systems = self._get_systems() if not systems: return "[ERROR] Cannot connect to Beszel" alerts = [] for sys_info in systems: sys_id = sys_info.get('id') sys_name = sys_info.get('name', sys_id) if system_name.lower() != 'all' and sys_name.lower() != system_name.lower(): continue stats = self._get_system_stats(sys_id) if stats: system_alerts = self._check_thresholds(stats) if system_alerts: alerts.append(f"=== {sys_name} ===") alerts.extend(system_alerts) if not alerts: return "✅ All systems nominal" return "\n".join(alerts) def get_gpu_status(self, system_name: str = "folzocorp"): systems = self._get_systems() if not systems: return "[ERROR] Cannot connect to Beszel" target_name = system_map.get(system_name.lower(), system_name.lower()) for sys_info in systems: sys_id = sys_info.get('id') sys_name = sys_info.get('name', '') if sys_name.lower() == target_name or sys_id == target_name: stats = self._get_system_stats(sys_id) if stats: if 'gpu' not in stats or not stats['gpu']: return f"[INFO] No GPU data for {system_name}" lines = [f"=== GPU Status: {system_name} ==="] gpu_data = stats.get('gpu', {}) if isinstance(gpu_data, dict): for gpu_name, gpu_info in gpu_data.items(): if isinstance(gpu_info, dict): vram_used = gpu_info.get('memory_used', 0) / (1024**3) vram_total = gpu_info.get('memory_total', 0) / (1024**3) vram_pct = (gpu_info.get('memory_used', 0) / max(gpu_info.get('memory_total', 1), 1)) * 100 lines.append(f"\n{gpu_name}:") lines.append(f" VRAM: {vram_used:.2f}GB / {vram_total:.2f}GB ({vram_pct:.1f}%)") lines.append(f" GPU Utilization: {gpu_info.get('utilization_gpu', 0)}%") if 'temperature' in gpu_info: lines.append(f" Temperature: {gpu_info.get('temperature', 0)}°C") if 'power_draw' in gpu_info: lines.append(f" Power: {gpu_info.get('power_draw', 0)}W") return "\n".join(lines) return f"[ERROR] Cannot get stats for {system_name}" available = [s.get('name', s.get('id')) for s in systems] return f"[ERROR] System '{system_name}' not found. Available: {', '.join(available)}" def can_run_comfyui(self): stats = self._get_system_stats("folzocorp") if not stats: return "[ERROR] Cannot get FolzoCorp stats" memory_pct = stats.get('memory', 0) vram_info = None vram_pct = 0 if 'gpu' in stats and stats['gpu']: gpu_data = stats.get('gpu', {}) if isinstance(gpu_data, dict): for gpu_name, gpu_info in gpu_data.items(): if isinstance(gpu_info, dict): vram_total = gpu_info.get('memory_total', 0) / (1024**3) vram_free = gpu_info.get('memory_total', 0) - gpu_info.get('memory_used', 0) vram_free_gb = vram_free / (1024**3) vram_pct = (gpu_info.get('memory_used', 0) / max(gpu_info.get('memory_total', 1), 1)) * 100 vram_info = f"{gpu_name}: {vram_free_gb:.1f}GB free ({vram_pct:.1f}% used)" can_run = memory_pct < 85 and vram_pct < 85 lines = ["=== ComfyUI Readiness Check ==="] lines.append(f"RAM: {100-memory_pct:.1f}% available ({memory_pct:.1f}% used)") if vram_info: lines.append(f"VRAM: {vram_info}") lines.append(f"\nConclusion: {'✅ Can run ComfyUI' if can_run else '⚠️ Resources low, may be slow'}") return "\n".join(lines) def get_all_stats(): tools = Tools() return tools.get_all_stats() def get_system_stats(system_name: str): tools = Tools() return tools.get_system_stats(system_name) def check_alerts(system_name: str = "all"): tools = Tools() return tools.check_alerts(system_name) def get_gpu_status(system_name: str = "folzocorp"): tools = Tools() return tools.get_gpu_status(system_name) def can_run_comfyui(): tools = Tools() return tools.can_run_comfyui() ``` ## Usage Examples ``` get_all_stats() get_system_stats("folzocorp") check_alerts() check_alerts("folzocorp") get_gpu_status("folzocorp") can_run_comfyui() ``` ## System IDs | System | ID | GPU | |--------|-----|-----| | FolzoCorp | `folzocorp` | AMD RX 7800 XT | | EffiServer | `effiserver` | None (Intel J5040) | | NAS Synology | `synology-nas` | Intel HD Graphics | ## Variables (Vannes) Configurables | Variable | Description | |----------|-------------| | `beszel_url` | URL du hub Beszel (défaut: http://192.168.1.25:8090) | | `beszel_user` | Email utilisateur Beszel | | `beszel_password` | Mot de passe Beszel | --- *Created 12 April 2026 - Updated API endpoints for PocketBase*