fix: Beszel tool - correct PocketBase API endpoints

- Changed /api/auth/login to /api/collections/users/auth-with-password
- Changed /api/systems to /api/collections/systems/records
- Added proper error messages for connection issues
This commit is contained in:
Folzo
2026-04-12 13:59:50 +02:00
parent 26e6b89229
commit cf406b764f
+108 -83
View File
@@ -28,7 +28,7 @@ class Tools:
) )
beszel_user: str = Field( beszel_user: str = Field(
default="admin", default="admin",
description="Beszel username", description="Beszel username (email)",
) )
beszel_password: str = Field( beszel_password: str = Field(
default="", default="",
@@ -41,12 +41,16 @@ class Tools:
def _login(self): def _login(self):
try: try:
response = requests.post( response = requests.post(
f"{self.valves.beszel_url}/api/auth/login", f"{self.valves.beszel_url}/api/collections/users/auth-with-password",
json={"identity": self.valves.beszel_user, "password": self.valves.beszel_password}, json={
"identity": self.valves.beszel_user,
"password": self.valves.beszel_password
},
timeout=10 timeout=10
) )
if response.status_code == 200: if response.status_code == 200:
return response.json().get("token") data = response.json()
return data.get("token")
return None return None
except Exception: except Exception:
return None return None
@@ -56,10 +60,18 @@ class Tools:
if not token: if not token:
return None return None
try: try:
headers = {"Authorization": f"Bearer {token}"} headers = {
response = requests.get(f"{self.valves.beszel_url}/api/systems", headers=headers, timeout=10) "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: if response.status_code == 200:
return response.json().get("items", []) data = response.json()
return data.get("items", [])
return None return None
except Exception: except Exception:
return None return None
@@ -69,9 +81,12 @@ class Tools:
if not token: if not token:
return None return None
try: try:
headers = {"Authorization": f"Bearer {token}"} headers = {
"Authorization": f"{token}",
"Content-Type": "application/json"
}
response = requests.get( response = requests.get(
f"{self.valves.beszel_url}/api/systems/{system_id}/stats", f"{self.valves.beszel_url}/api/collections/systems/records/{system_id}",
headers=headers, headers=headers,
timeout=10 timeout=10
) )
@@ -91,27 +106,33 @@ class Tools:
lines.append(f"Swap: {stats.get('swap', 0):.1f}%") lines.append(f"Swap: {stats.get('swap', 0):.1f}%")
if 'disk' in stats: if 'disk' in stats:
for disk, info in stats['disk'].items(): disk_data = stats.get('disk', {})
lines.append(f"Disque {disk}: {info.get('used_percent', 0):.1f}%") 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: if 'load' in stats:
load = stats['load'] load = stats.get('load', [])
lines.append(f"Load: {load[0]:.2f} {load[1]:.2f} {load[2]:.2f}") if len(load) >= 3:
lines.append(f"Load: {load[0]:.2f} {load[1]:.2f} {load[2]:.2f}")
if 'gpu' in stats and stats['gpu']: if 'gpu' in stats and stats['gpu']:
lines.append("") lines.append("")
lines.append("GPU:") lines.append("GPU:")
for gpu_name, gpu_data in stats['gpu'].items(): gpu_data = stats.get('gpu', {})
vram_used = gpu_data.get('memory_used', 0) / (1024**3) if isinstance(gpu_data, dict):
vram_total = gpu_data.get('memory_total', 0) / (1024**3) for gpu_name, gpu_info in gpu_data.items():
vram_pct = (gpu_data.get('memory_used', 0) / gpu_data.get('memory_total', 1)) * 100 if isinstance(gpu_info, dict):
lines.append(f" {gpu_name}:") vram_used = gpu_info.get('memory_used', 0) / (1024**3)
lines.append(f" VRAM: {vram_used:.2f}GB / {vram_total:.2f}GB ({vram_pct:.1f}%)") vram_total = gpu_info.get('memory_total', 0) / (1024**3)
lines.append(f" Utilisation GPU: {gpu_data.get('utilization_gpu', 0)}%") vram_pct = (gpu_info.get('memory_used', 0) / max(gpu_info.get('memory_total', 1), 1)) * 100
if 'temperature' in gpu_data: lines.append(f" {gpu_name}:")
lines.append(f" Température: {gpu_data.get('temperature', 0)}°C") lines.append(f" VRAM: {vram_used:.2f}GB / {vram_total:.2f}GB ({vram_pct:.1f}%)")
if 'power_draw' in gpu_data: lines.append(f" Utilisation GPU: {gpu_info.get('utilization_gpu', 0)}%")
lines.append(f" Consommation: {gpu_data.get('power_draw', 0)}W") 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']: if 'containers' in stats and stats['containers']:
lines.append("") lines.append("")
@@ -139,23 +160,25 @@ class Tools:
alerts.append(f"⚠️ Memory elevated: {memory:.1f}%") alerts.append(f"⚠️ Memory elevated: {memory:.1f}%")
if 'gpu' in stats and stats['gpu']: if 'gpu' in stats and stats['gpu']:
for gpu_name, gpu_data in stats['gpu'].items(): gpu_data = stats.get('gpu', {})
vram_pct = (gpu_data.get('memory_used', 0) / gpu_data.get('memory_total', 1)) * 100 if isinstance(gpu_data, dict):
if vram_pct > thresholds['vram']: for gpu_name, gpu_info in gpu_data.items():
alerts.append(f"⚠️ VRAM {gpu_name} elevated: {vram_pct:.1f}%") if isinstance(gpu_info, dict):
vram_pct = (gpu_info.get('memory_used', 0) / max(gpu_info.get('memory_total', 1), 1)) * 100
util = gpu_data.get('utilization_gpu', 0) if vram_pct > thresholds['vram']:
if util > thresholds['vram']: alerts.append(f"⚠️ VRAM {gpu_name} elevated: {vram_pct:.1f}%")
alerts.append(f"⚠️ GPU {gpu_name} utilization elevated: {util}%")
util = gpu_info.get('utilization_gpu', 0)
if util > thresholds['vram']:
alerts.append(f"⚠️ GPU {gpu_name} utilization elevated: {util}%")
return alerts return alerts
def get_all_stats(self): def get_all_stats(self):
"""Get stats from all monitored systems."""
systems = self._get_systems() systems = self._get_systems()
if not systems: if not systems:
return "[ERROR] Cannot connect to Beszel or no systems found" return "[ERROR] Cannot connect to Beszel. Check URL, username and password."
results = [] results = []
for sys_info in systems: for sys_info in systems:
@@ -171,11 +194,10 @@ class Tools:
return "\n\n".join(results) return "\n\n".join(results)
def get_system_stats(self, system_name: str): def get_system_stats(self, system_name: str):
"""Get stats for a specific system. Options: folzocorp, effiserver, synology-nas"""
systems = self._get_systems() systems = self._get_systems()
if not systems: if not systems:
return "[ERROR] Cannot connect to Beszel" return "[ERROR] Cannot connect to Beszel. Check URL, username and password."
system_map = { system_map = {
'folzocorp': 'folzocorp', 'folzocorp': 'folzocorp',
@@ -184,20 +206,21 @@ class Tools:
'nas': 'synology-nas' 'nas': 'synology-nas'
} }
target_id = system_map.get(system_name.lower(), system_name.lower()) target_name = system_map.get(system_name.lower(), system_name.lower())
for sys_info in systems: for sys_info in systems:
if sys_info.get('id') == target_id or sys_info.get('name', '').lower() == system_name.lower(): sys_id = sys_info.get('id')
stats = self._get_system_stats(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: if stats:
return self._format_stats_text(stats, sys_info.get('name', system_name)) return self._format_stats_text(stats, sys_nm or system_name)
return f"[ERROR] Cannot get stats for {system_name}" return f"[ERROR] Cannot get stats for {system_name}"
available = [s.get('name', s.get('id')) for s in systems] available = [s.get('name', s.get('id')) for s in systems]
return f"[ERROR] System '{system_name}' not found. Available: {', '.join(available)}" return f"[ERROR] System '{system_name}' not found. Available: {', '.join(available)}"
def check_alerts(self, system_name: str = "all"): def check_alerts(self, system_name: str = "all"):
"""Check for resource alerts. Use 'all' or specific system name."""
systems = self._get_systems() systems = self._get_systems()
if not systems: if not systems:
@@ -225,48 +248,47 @@ class Tools:
return "\n".join(alerts) return "\n".join(alerts)
def get_gpu_status(self, system_name: str = "folzocorp"): def get_gpu_status(self, system_name: str = "folzocorp"):
"""Get GPU status for a specific system. Most useful for folzocorp (AMD RX 7800 XT)."""
systems = self._get_systems() systems = self._get_systems()
if not systems: if not systems:
return "[ERROR] Cannot connect to Beszel" return "[ERROR] Cannot connect to Beszel"
target_id = None target_name = system_map.get(system_name.lower(), system_name.lower())
for sys_info in systems: for sys_info in systems:
if sys_info.get('id') == system_name.lower() or sys_info.get('name', '').lower() == system_name.lower(): sys_id = sys_info.get('id')
target_id = sys_info.get('id') sys_name = sys_info.get('name', '')
break 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}"
if not target_id: available = [s.get('name', s.get('id')) for s in systems]
available = [s.get('name', s.get('id')) for s in systems] return f"[ERROR] System '{system_name}' not found. Available: {', '.join(available)}"
return f"[ERROR] System '{system_name}' not found. Available: {', '.join(available)}"
stats = self._get_system_stats(target_id)
if not stats:
return f"[ERROR] Cannot get stats for {system_name}"
if 'gpu' not in stats or not stats['gpu']:
return f"[INFO] No GPU data for {system_name}"
lines = [f"=== GPU Status: {system_name} ==="]
for gpu_name, gpu_data in stats['gpu'].items():
vram_used = gpu_data.get('memory_used', 0) / (1024**3)
vram_total = gpu_data.get('memory_total', 0) / (1024**3)
vram_pct = (gpu_data.get('memory_used', 0) / gpu_data.get('memory_total', 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_data.get('utilization_gpu', 0)}%")
if 'temperature' in gpu_data:
lines.append(f" Temperature: {gpu_data.get('temperature', 0)}°C")
if 'power_draw' in gpu_data:
lines.append(f" Power: {gpu_data.get('power_draw', 0)}W")
return "\n".join(lines)
def can_run_comfyui(self): def can_run_comfyui(self):
"""Check if there's enough RAM and VRAM to run ComfyUI."""
stats = self._get_system_stats("folzocorp") stats = self._get_system_stats("folzocorp")
if not stats: if not stats:
return "[ERROR] Cannot get FolzoCorp stats" return "[ERROR] Cannot get FolzoCorp stats"
@@ -276,12 +298,15 @@ class Tools:
vram_info = None vram_info = None
vram_pct = 0 vram_pct = 0
if 'gpu' in stats and stats['gpu']: if 'gpu' in stats and stats['gpu']:
for gpu_name, gpu_data in stats['gpu'].items(): gpu_data = stats.get('gpu', {})
vram_total = gpu_data.get('memory_total', 0) / (1024**3) if isinstance(gpu_data, dict):
vram_free = gpu_data.get('memory_total', 0) - gpu_data.get('memory_used', 0) for gpu_name, gpu_info in gpu_data.items():
vram_free_gb = vram_free / (1024**3) if isinstance(gpu_info, dict):
vram_pct = (gpu_data.get('memory_used', 0) / gpu_data.get('memory_total', 1)) * 100 vram_total = gpu_info.get('memory_total', 0) / (1024**3)
vram_info = f"{gpu_name}: {vram_free_gb:.1f}GB free ({vram_pct:.1f}% used)" 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 can_run = memory_pct < 85 and vram_pct < 85
@@ -339,9 +364,9 @@ can_run_comfyui()
| Variable | Description | | Variable | Description |
|----------|-------------| |----------|-------------|
| `beszel_url` | URL du hub Beszel (défaut: http://192.168.1.25:8090) | | `beszel_url` | URL du hub Beszel (défaut: http://192.168.1.25:8090) |
| `beszel_user` | Username admin (défaut: admin) | | `beszel_user` | Email utilisateur Beszel |
| `beszel_password` | Mot de passe admin | | `beszel_password` | Mot de passe Beszel |
--- ---
*Created 12 April 2026* *Created 12 April 2026 - Updated API endpoints for PocketBase*