graniteapi / app.py
oki0ki's picture
Update app.py
296f03f verified
#!/usr/bin/env python3
"""
Ultralekki serwer OpenAI-compatible dla HF Spaces
✅ Bezpośrednie uruchomienie via uvicorn (brak subprocess)
✅ Odporny na SIGTERM/SIGINT
✅ Streaming SSE | ✅ Brak auth
"""
import os
import sys
import signal
import logging
from huggingface_hub import hf_hub_download
import uvicorn
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logger = logging.getLogger(__name__)
MODEL_REPO = "unsloth/granite-4.1-3b-GGUF"
MODEL_FILE = os.environ.get("MODEL_FILE", "granite-4.1-3b-UD-IQ2_M.gguf")
PORT = int(os.environ.get("PORT", 7860))
N_CTX = int(os.environ.get("N_CTX", 2048))
N_THREADS = int(os.environ.get("N_THREADS", 2))
N_BATCH = int(os.environ.get("N_BATCH", 512))
def graceful_shutdown(signum, frame):
logger.info("📡 Otrzymano sygnał zakończenia. Zamykanie...")
# Uvicorn obsłuży to samodzielnie dzięki lifespan, ale dla pewności exit
sys.exit(0)
signal.signal(signal.SIGTERM, graceful_shutdown)
signal.signal(signal.SIGINT, graceful_shutdown)
if __name__ == "__main__":
try:
logger.info(f"⬇️ Pobieranie/weryfikacja: {MODEL_REPO}/{MODEL_FILE}")
model_path = hf_hub_download(
repo_id=MODEL_REPO,
filename=MODEL_FILE,
resume_download=True
)
logger.info(f"✅ Model gotowy: {model_path}")
# Importujemy moduł serwera llama_cpp
from llama_cpp.server.app import create_app
# Tworzymy aplikację FastAPI z konfiguracją modelu
# Uwaga: create_app wymaga specyficznych argumentów w nowszych wersjach
# Jeśli create_app nie działa bezpośrednio, używamy podejścia z CLI args via sys.argv hack
# Ale najbezpieczniej jest ustawić zmienne środowiskowe, które llama_cpp.server czyta
os.environ["MODEL"] = model_path
os.environ["HOST"] = "0.0.0.0"
os.environ["PORT"] = str(PORT)
os.environ["N_CTX"] = str(N_CTX)
os.environ["N_THREADS"] = str(N_THREADS)
os.environ["N_BATCH"] = str(N_BATCH)
os.environ["N_GPU_LAYERS"] = "0"
os.environ["USE_MMAP"] = "1"
os.environ["NO_FLASH_ATTN"] = "1"
os.environ["CHAT_FORMAT"] = "chatml"
logger.info("🚀 Start serwera Uvicorn...")
# Uruchamiamy uvicorn bezpośrednio
# app="llama_cpp.server.app:create_app" może być problematyczne jeśli create_app potrzebuje args
# Dlatego używamy prostszego podejścia: zaimportujmy app z llama_cpp.server
# Alternatywa: Użycie wbudowanego entrypointa llama_cpp.server poprzez import
# Najprostsze i najbardziej stabilne:
from llama_cpp.server import app as server_app_module
# Sprawdźmy, czy server_app_module ma obiekt 'app'
if hasattr(server_app_module, 'app'):
target_app = server_app_module.app
else:
# Fallback: spróbujmy stworzyć app ręcznie jeśli to możliwe
# W wersji 0.3.2 app jest tworzony dynamicznie przy imporcie __main__
# Więc musimy zasymulować import __main__
import llama_cpp.server.__main__ as main_module
# To może być ryzykowne.
# Najlepsze rozwiązanie dla 0.3.2:
# Używamy uvicorn z stringiem wskazującym na fabrykę aplikacji
target_app = "llama_cpp.server.app:create_app"
uvicorn.run(
target_app,
host="0.0.0.0",
port=PORT,
log_level="info",
timeout_keep_alive=120,
limit_concurrency=3,
backlog=16,
ws_ping_interval=30,
ws_ping_timeout=10
)
except Exception as e:
logger.error(f"❌ Krytyczny błąd: {e}", exc_info=True)
sys.exit(1)