74 lines
3.6 KiB
Python
74 lines
3.6 KiB
Python
import shutil
|
|
import subprocess
|
|
|
|
import requests
|
|
|
|
from . import database as db, config
|
|
|
|
|
|
def _check_ffmpeg() -> dict:
|
|
path = shutil.which("ffmpeg")
|
|
if not path:
|
|
return {"name": "ffmpeg", "ok": False, "message": "ffmpeg not found on PATH. This should be baked into the Docker image - if you're seeing this, the image build is broken."}
|
|
return {"name": "ffmpeg", "ok": True, "message": path}
|
|
|
|
|
|
def _check_gpu() -> dict:
|
|
try:
|
|
import torch
|
|
if torch.cuda.is_available():
|
|
name = torch.cuda.get_device_name(0)
|
|
free, total = torch.cuda.mem_get_info(0)
|
|
free_gb, total_gb = free / 1e9, total / 1e9
|
|
return {"name": "gpu", "ok": True, "message": f"{name} — {free_gb:.1f}GB free of {total_gb:.1f}GB"}
|
|
return {"name": "gpu", "ok": False, "message": "No CUDA GPU visible. Check that the container was started with --gpus all / GPU passthrough in compose, and that host NVIDIA drivers + Container Toolkit are installed. Transcription will fall back to CPU, which is much slower."}
|
|
except Exception as e:
|
|
return {"name": "gpu", "ok": False, "message": f"Could not query GPU: {e}"}
|
|
|
|
|
|
def _check_hf_token(campaign_id: str = "default") -> dict:
|
|
settings = db.get_campaign_settings(campaign_id)
|
|
token = settings.get("hf_token", "")
|
|
if not token:
|
|
return {"name": "huggingface_token", "ok": False, "message": "No HF token set. Diarization (speaker separation) will fail without one. Add it in Settings."}
|
|
return {"name": "huggingface_token", "ok": True, "message": "Token is set (not validated against HF until first diarization run)."}
|
|
|
|
|
|
def _check_llm_backend(campaign_id: str = "default") -> dict:
|
|
settings = db.get_campaign_settings(campaign_id)
|
|
if settings.get("llm_mode") == "api":
|
|
if not settings.get("api_key"):
|
|
return {"name": "llm_backend", "ok": False, "message": "Hosted API selected but no API key set."}
|
|
return {"name": "llm_backend", "ok": True, "message": f"Hosted API configured ({settings.get('api_base_url')})"}
|
|
|
|
host = settings.get("ollama_host", "http://localhost:11434")
|
|
model = settings.get("ollama_model", "")
|
|
try:
|
|
resp = requests.get(f"{host.rstrip('/')}/api/tags", timeout=5)
|
|
resp.raise_for_status()
|
|
models = [m["name"] for m in resp.json().get("models", [])]
|
|
if model not in models:
|
|
return {"name": "llm_backend", "ok": False, "message": f"Reached Ollama at {host}, but model '{model}' isn't pulled there. Available: {', '.join(models) or '(none)'}. Run: ollama pull {model}"}
|
|
return {"name": "llm_backend", "ok": True, "message": f"Ollama reachable at {host}, model '{model}' available"}
|
|
except requests.exceptions.ConnectionError:
|
|
return {"name": "llm_backend", "ok": False, "message": f"Could not connect to Ollama at {host}. Check the host/port in Settings, and that Ollama is running and reachable from this container."}
|
|
except Exception as e:
|
|
return {"name": "llm_backend", "ok": False, "message": f"Error checking Ollama: {e}"}
|
|
|
|
|
|
def _check_disk_space() -> dict:
|
|
usage = shutil.disk_usage(config.DATA_DIR)
|
|
free_gb = usage.free / 1e9
|
|
ok = free_gb > 5
|
|
return {"name": "disk_space", "ok": ok, "message": f"{free_gb:.1f}GB free in {config.DATA_DIR}" + ("" if ok else " — this is low, recordings and model caches need room")}
|
|
|
|
|
|
def run_diagnostics(campaign_id: str = "default") -> list[dict]:
|
|
return [
|
|
_check_ffmpeg(),
|
|
_check_gpu(),
|
|
_check_hf_token(campaign_id),
|
|
_check_llm_backend(campaign_id),
|
|
_check_disk_space(),
|
|
]
|