fix: 500er bei register/update_model/install-recipe, Download & favicon
- write_config schreibt atomar (tmp + os.replace) -> llama-swap (-watch-config) sieht nie eine halb geschriebene config.yaml; PermissionError wird in eine klare Klartext-Meldung uebersetzt (Hinweis auf chown) - generischer Exception-Handler: unerwartete Fehler kommen als lesbare JSON- Meldung im UI an statt als nackter 500 (Anfaenger-Diagnose) - Download: nutzt die `hf`-CLI aus dem venv (Dienst-PATH kennt venv/bin nicht) + HF_HUB_DISABLE_XET=1 statt HF_XET_HIGH_PERFORMANCE (XET-Haenger bei ~6 MB) - huggingface_hub als Dependency ergaenzt (liefert `hf`) - favicon.ico: themed SVG (Link-Tag + Route) -> kein 404 mehr Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -17,7 +17,7 @@ Neue Bereiche kommen als routers/<bereich>.py + static/js/panels/<bereich>.js da
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.responses import FileResponse, JSONResponse
|
||||
from fastapi.responses import FileResponse, JSONResponse, Response
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from routers import jobs, maintenance, models, system, cookbook, integration, news
|
||||
@@ -52,9 +52,29 @@ def index():
|
||||
return FileResponse(_STATIC / "index.html")
|
||||
|
||||
|
||||
_FAVICON = (
|
||||
b"<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'>"
|
||||
b"<rect width='32' height='32' rx='7' fill='#0f1720'/>"
|
||||
b"<circle cx='16' cy='16' r='8' fill='none' stroke='#2dd4bf' stroke-width='3'/>"
|
||||
b"<circle cx='16' cy='16' r='2.5' fill='#2dd4bf'/></svg>"
|
||||
)
|
||||
|
||||
|
||||
@app.get("/favicon.ico")
|
||||
def favicon():
|
||||
return Response(content=_FAVICON, media_type="image/svg+xml")
|
||||
|
||||
|
||||
app.mount("/static", StaticFiles(directory=_STATIC), name="static")
|
||||
|
||||
|
||||
@app.exception_handler(HTTPException)
|
||||
def _http_exc(_req, exc: HTTPException):
|
||||
return JSONResponse(status_code=exc.status_code, content={"error": exc.detail})
|
||||
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
def _any_exc(_req, exc: Exception):
|
||||
"""Unerwartete Fehler als lesbare Meldung ans (vertrauenswuerdige LAN-)UI geben,
|
||||
statt nur einen generischen 500 ohne Hinweis. Erleichtert Anfaengern die Diagnose."""
|
||||
return JSONResponse(status_code=500, content={"error": str(exc) or exc.__class__.__name__})
|
||||
|
||||
+16
-3
@@ -6,6 +6,8 @@ Helfer rund um llama-swap und dessen config.yaml.
|
||||
sodass Kommentare und Quotes erhalten bleiben.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import httpx
|
||||
|
||||
from config import CONFIG_PATH, LLAMA_SWAP_URL, yaml
|
||||
@@ -29,6 +31,17 @@ def read_config() -> dict:
|
||||
|
||||
|
||||
def write_config(cfg: dict) -> None:
|
||||
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
with CONFIG_PATH.open("w", encoding="utf-8") as f:
|
||||
yaml.dump(cfg, f)
|
||||
"""Schreibt die config.yaml atomar (tmp-Datei + os.replace), damit llama-swap
|
||||
mit -watch-config nie eine halb geschriebene Datei sieht. Fehlende Schreibrechte
|
||||
werden in eine klare Meldung uebersetzt statt als roher Traceback zu landen."""
|
||||
try:
|
||||
CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
tmp = CONFIG_PATH.with_name(CONFIG_PATH.name + ".tmp")
|
||||
with tmp.open("w", encoding="utf-8") as f:
|
||||
yaml.dump(cfg, f)
|
||||
os.replace(tmp, CONFIG_PATH)
|
||||
except PermissionError as exc:
|
||||
raise PermissionError(
|
||||
f"Mission Control darf '{CONFIG_PATH}' nicht schreiben. "
|
||||
f"Einmalig Besitz uebergeben: sudo chown -R hitonabi:hitonabi {CONFIG_PATH.parent}"
|
||||
) from exc
|
||||
|
||||
@@ -3,3 +3,4 @@ uvicorn[standard]>=0.29
|
||||
httpx>=0.27
|
||||
ruamel.yaml>=0.18
|
||||
psutil>=5.9.0
|
||||
huggingface_hub>=0.34 # liefert die `hf`-CLI fuer Modell-Downloads
|
||||
|
||||
+12
-2
@@ -19,11 +19,20 @@ from llamaswap import _swap_get, read_config, write_config
|
||||
from hw_math import extract_params_b, max_ctx_for, estimate_memory_gb
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import psutil
|
||||
|
||||
router = APIRouter(prefix="/api", dependencies=[Depends(auth)])
|
||||
|
||||
|
||||
def _hf_bin() -> str:
|
||||
"""Pfad zur `hf`-CLI. Bevorzugt die im venv installierte (neben dem laufenden
|
||||
Python), da der Dienst-PATH das venv/bin meist nicht enthaelt. Faellt sonst auf
|
||||
ein global installiertes `hf` zurueck."""
|
||||
cand = os.path.join(os.path.dirname(sys.executable), "hf")
|
||||
return cand if os.path.exists(cand) else "hf"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Request-Modelle
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -139,8 +148,9 @@ def download(req: DownloadReq):
|
||||
sub = req.subdir or req.repo.split("/")[-1]
|
||||
target = MODELS_DIR / sub
|
||||
target.mkdir(parents=True, exist_ok=True)
|
||||
args = ["hf", "download", req.repo, req.file, "--local-dir", str(target)]
|
||||
env = {"HF_XET_HIGH_PERFORMANCE": "1"}
|
||||
args = [_hf_bin(), "download", req.repo, req.file, "--local-dir", str(target)]
|
||||
# XET deaktivieren: mit aktivem XET haengt der Download reproduzierbar bei ~6 MB (siehe CLAUDE.md).
|
||||
env = {"HF_HUB_DISABLE_XET": "1"}
|
||||
if req.hf_token:
|
||||
env["HF_TOKEN"] = req.hf_token
|
||||
job_id = start_job(args, f"download {req.repo}/{req.file}", env=env)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Mission Control</title>
|
||||
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%230f1720'/%3E%3Ccircle cx='16' cy='16' r='8' fill='none' stroke='%232dd4bf' stroke-width='3'/%3E%3Ccircle cx='16' cy='16' r='2.5' fill='%232dd4bf'/%3E%3C/svg%3E">
|
||||
<link rel="stylesheet" href="/static/css/base.css">
|
||||
<link rel="stylesheet" href="/static/css/components.css">
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user