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:
@@ -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', {})
|
||||||
|
if isinstance(disk_data, dict):
|
||||||
|
for disk, info in disk_data.items():
|
||||||
lines.append(f"Disque {disk}: {info.get('used_percent', 0):.1f}%")
|
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', [])
|
||||||
|
if len(load) >= 3:
|
||||||
lines.append(f"Load: {load[0]:.2f} {load[1]:.2f} {load[2]:.2f}")
|
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):
|
||||||
|
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" {gpu_name}:")
|
||||||
lines.append(f" VRAM: {vram_used:.2f}GB / {vram_total:.2f}GB ({vram_pct:.1f}%)")
|
lines.append(f" VRAM: {vram_used:.2f}GB / {vram_total:.2f}GB ({vram_pct:.1f}%)")
|
||||||
lines.append(f" Utilisation GPU: {gpu_data.get('utilization_gpu', 0)}%")
|
lines.append(f" Utilisation GPU: {gpu_info.get('utilization_gpu', 0)}%")
|
||||||
if 'temperature' in gpu_data:
|
if 'temperature' in gpu_info:
|
||||||
lines.append(f" Température: {gpu_data.get('temperature', 0)}°C")
|
lines.append(f" Température: {gpu_info.get('temperature', 0)}°C")
|
||||||
if 'power_draw' in gpu_data:
|
if 'power_draw' in gpu_info:
|
||||||
lines.append(f" Consommation: {gpu_data.get('power_draw', 0)}W")
|
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):
|
||||||
|
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']:
|
if vram_pct > thresholds['vram']:
|
||||||
alerts.append(f"⚠️ VRAM {gpu_name} elevated: {vram_pct:.1f}%")
|
alerts.append(f"⚠️ VRAM {gpu_name} elevated: {vram_pct:.1f}%")
|
||||||
|
|
||||||
util = gpu_data.get('utilization_gpu', 0)
|
util = gpu_info.get('utilization_gpu', 0)
|
||||||
if util > thresholds['vram']:
|
if util > thresholds['vram']:
|
||||||
alerts.append(f"⚠️ GPU {gpu_name} utilization elevated: {util}%")
|
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 not target_id:
|
if stats:
|
||||||
available = [s.get('name', s.get('id')) for s in systems]
|
|
||||||
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']:
|
if 'gpu' not in stats or not stats['gpu']:
|
||||||
return f"[INFO] No GPU data for {system_name}"
|
return f"[INFO] No GPU data for {system_name}"
|
||||||
|
|
||||||
lines = [f"=== GPU Status: {system_name} ==="]
|
lines = [f"=== GPU Status: {system_name} ==="]
|
||||||
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):
|
||||||
|
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"\n{gpu_name}:")
|
||||||
lines.append(f" VRAM: {vram_used:.2f}GB / {vram_total:.2f}GB ({vram_pct:.1f}%)")
|
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)}%")
|
lines.append(f" GPU Utilization: {gpu_info.get('utilization_gpu', 0)}%")
|
||||||
|
|
||||||
if 'temperature' in gpu_data:
|
if 'temperature' in gpu_info:
|
||||||
lines.append(f" Temperature: {gpu_data.get('temperature', 0)}°C")
|
lines.append(f" Temperature: {gpu_info.get('temperature', 0)}°C")
|
||||||
if 'power_draw' in gpu_data:
|
if 'power_draw' in gpu_info:
|
||||||
lines.append(f" Power: {gpu_data.get('power_draw', 0)}W")
|
lines.append(f" Power: {gpu_info.get('power_draw', 0)}W")
|
||||||
|
|
||||||
return "\n".join(lines)
|
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):
|
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,11 +298,14 @@ 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():
|
||||||
|
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_free_gb = vram_free / (1024**3)
|
||||||
vram_pct = (gpu_data.get('memory_used', 0) / gpu_data.get('memory_total', 1)) * 100
|
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)"
|
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*
|
||||||
|
|||||||
Reference in New Issue
Block a user