mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
Panneau Matériel : GPU vu par Loki vs GPU utilisé par Ollama
- routes/system : /api/system/hardware — liste les GPU vus par le conteneur (nvidia-smi, multi-GPU) ou l'override GPU_VRAM_MB, et le placement réel des modèles chargés côté Ollama (GPU/CPU/mixte + VRAM) via /api/ps. - config : réintègre gpu_vram_mb / gpu_name (perdus lors d'une fusion). - UI : carte MATÉRIEL dans Configuration (rafraîchie toutes les 5 s) montrant les deux points de vue et si Ollama est local ou distant. Répond à « l'appli est-elle réservée à la 3060 ? » (non) et permet de voir tout de suite si Ollama charge un modèle sur CPU faute de support GPU. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVay7z3y7q2gEe54ByAE6N
This commit is contained in:
4 files changed
+243
-2
No files matched your search
@@ -15,6 +15,11 @@ class Settings(BaseSettings):
|
||||
# Permet de vérifier que l'image déployée est bien à jour.
|
||||
loki_version: str = "dev"
|
||||
|
||||
# Override manuel de la VRAM (Mo) si la détection GPU échoue dans le
|
||||
# conteneur (utile quand Ollama tourne sur une autre machine).
|
||||
gpu_vram_mb: int = 0
|
||||
gpu_name: str = ""
|
||||
|
||||
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
||||
|
||||
|
||||
|
||||
@@ -4,9 +4,13 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import shutil
|
||||
|
||||
import httpx
|
||||
import psutil
|
||||
from fastapi import APIRouter
|
||||
|
||||
from ..config import settings
|
||||
from ..ollama_client import ollama
|
||||
|
||||
router = APIRouter(prefix="/api/system", tags=["system"])
|
||||
|
||||
_NVIDIA_SMI = shutil.which("nvidia-smi")
|
||||
@@ -50,3 +54,90 @@ async def stats() -> dict:
|
||||
"ram_pct": mem.percent,
|
||||
"gpu": gpu,
|
||||
}
|
||||
|
||||
|
||||
async def _all_local_gpus() -> list[dict]:
|
||||
"""Tous les GPU NVIDIA visibles depuis le CONTENEUR Loki (peut être vide)."""
|
||||
if not _NVIDIA_SMI:
|
||||
return []
|
||||
try:
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
_NVIDIA_SMI,
|
||||
"--query-gpu=index,name,memory.total,memory.used,utilization.gpu",
|
||||
"--format=csv,noheader,nounits",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
out, _ = await asyncio.wait_for(proc.communicate(), timeout=3)
|
||||
gpus = []
|
||||
for line in out.decode().strip().splitlines():
|
||||
idx, name, total, used, util = (p.strip() for p in line.split(","))
|
||||
gpus.append({
|
||||
"index": int(idx), "name": name,
|
||||
"vram_total_mb": float(total), "vram_used_mb": float(used),
|
||||
"util_pct": float(util),
|
||||
})
|
||||
return gpus
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
@router.get("/hardware")
|
||||
async def hardware() -> dict:
|
||||
"""Vue matériel : GPU vu par Loki vs GPU réellement utilisé par Ollama."""
|
||||
# 1) Ce que voit le conteneur Loki (nvidia-smi) + override éventuel.
|
||||
local_gpus = await _all_local_gpus()
|
||||
override = None
|
||||
if settings.gpu_vram_mb > 0:
|
||||
override = {
|
||||
"name": settings.gpu_name or "GPU déclaré (GPU_VRAM_MB)",
|
||||
"vram_total_mb": settings.gpu_vram_mb,
|
||||
}
|
||||
|
||||
# 2) Ce qu'Ollama utilise réellement (modèles chargés + placement VRAM/CPU).
|
||||
ollama_info: dict = {"host": ollama.host, "connected": False, "running": []}
|
||||
try:
|
||||
version = await ollama.ping()
|
||||
ollama_info["connected"] = True
|
||||
ollama_info["version"] = version.get("version")
|
||||
except (httpx.HTTPError, OSError) as exc:
|
||||
ollama_info["error"] = str(exc)[:200]
|
||||
|
||||
if ollama_info["connected"]:
|
||||
try:
|
||||
for m in await ollama.ps():
|
||||
size = m.get("size", 0) or 0
|
||||
vram = m.get("size_vram", 0) or 0
|
||||
if size <= 0:
|
||||
where = "inconnu"
|
||||
elif vram >= size * 0.99:
|
||||
where = "GPU"
|
||||
elif vram <= size * 0.01:
|
||||
where = "CPU"
|
||||
else:
|
||||
where = "mixte"
|
||||
ollama_info["running"].append({
|
||||
"name": m.get("name") or m.get("model"),
|
||||
"processor": where,
|
||||
"gpu_percent": int(vram / size * 100) if size else 0,
|
||||
"size_mb": round(size / 1_000_000),
|
||||
"vram_mb": round(vram / 1_000_000),
|
||||
})
|
||||
except (httpx.HTTPError, OSError):
|
||||
pass
|
||||
|
||||
# Ollama est-il sur la même machine que Loki ? (heuristique sur l'hôte)
|
||||
host = ollama.host.lower()
|
||||
is_local = any(h in host for h in ("localhost", "127.0.0.1", "host.docker.internal"))
|
||||
|
||||
return {
|
||||
"loki_gpus": local_gpus,
|
||||
"gpu_override": override,
|
||||
"ollama": ollama_info,
|
||||
"ollama_is_local": is_local,
|
||||
"note": (
|
||||
"Loki ne voit pas directement le GPU d'un Ollama distant : il déduit "
|
||||
"le placement (GPU/CPU) depuis les modèles chargés. Déclare GPU_VRAM_MB "
|
||||
"pour l'auto-réglage si Ollama tourne sur une autre machine."
|
||||
),
|
||||
}
|
||||
Reference in new issue
Block a user