oki0ki commited on
Commit
f3bc30f
·
verified ·
1 Parent(s): 2085209

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +35 -93
app.py CHANGED
@@ -1,96 +1,25 @@
1
  #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
  """
4
- Ultralekki serwer OpenAI-compatible dla HF Spaces
5
- Model: unsloth/granite-4.1-3b-GGUF (UD-IQ2_M)
6
- Brak auth | ✅ Streaming | ✅ Odporny na zerwania | ✅ Optymalizacja CPU/RAM
7
  """
8
-
9
  import os
10
  import sys
11
  import signal
12
- import asyncio
13
  import logging
14
- from contextlib import asynccontextmanager
15
  from huggingface_hub import hf_hub_download
16
- from fastapi import FastAPI, Request
17
- from fastapi.middleware.cors import CORSMiddleware
18
- from fastapi.responses import JSONResponse
19
- import uvicorn
20
-
21
- # ---------------- KONFIGURACJA ----------------
22
- MODEL_REPO = "unsloth/granite-4.1-3b-GGUF"
23
- MODEL_FILE = os.environ.get("MODEL_FILE", "granite-4.1-3b-UD-IQ2_M.gguf")
24
- PORT = int(os.environ.get("PORT", 7860))
25
- N_CTX = int(os.environ.get("N_CTX", 2048)) # Limit kontekstu dla oszczędności RAM
26
- N_THREADS = int(os.environ.get("N_THREADS", 2)) # Dopasowane do free tier HF
27
- N_BATCH = int(os.environ.get("N_BATCH", 512))
28
- MAX_CONCURRENCY = int(os.environ.get("MAX_CONCURRENCY", 3))
29
-
30
- # Wymuś CPU, wyłącz detekcję GPU i niepotrzebne overheady
31
- os.environ.update({
32
- "LLAMA_NO_METAL": "1",
33
- "LLAMA_NO_CUDA": "1",
34
- "LLAMA_NO_VULKAN": "1",
35
- "USE_MMAP": "1",
36
- "USE_MLOCK": "0",
37
- "FLASH_ATTN": "0",
38
- "VERBOSE": "0"
39
- })
40
 
41
  logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
42
  logger = logging.getLogger(__name__)
43
 
44
- # ---------------- POBIERANIE MODELU ----------------
45
- def get_model_path() -> str:
46
- logger.info(f"⬇️ Pobieranie/weryfikacja: {MODEL_REPO}/{MODEL_FILE}")
47
- return hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE, resume_download=True)
48
-
49
- # ---------------- LIFECYCLE & APP ----------------
50
- @asynccontextmanager
51
- async def lifespan(app: FastAPI):
52
- # Startup
53
- model_path = get_model_path()
54
- os.environ["MODEL"] = model_path
55
- logger.info("📦 Model gotowy. Inicjalizacja llama.cpp server...")
56
-
57
- # Importuj dopiero po ustawieniu envów (llama_cpp czyta je przy starcie)
58
- import llama_cpp.server.app as server_module
59
- app.mount("/", server_module.app)
60
-
61
- yield
62
- # Shutdown
63
- logger.info("🛑 Zamykanie serwera...")
64
-
65
- app = FastAPI(title="Granite-4.1-3B-IQ2M OpenAI API", lifespan=lifespan)
66
-
67
- # ---------------- MIDDLEWARE & RESILIENCE ----------------
68
- app.add_middleware(
69
- CORSMiddleware,
70
- allow_origins=["*"],
71
- allow_credentials=True,
72
- allow_methods=["*"],
73
- allow_headers=["*"],
74
- )
75
-
76
- @app.middleware("http")
77
- async def connection_resilience(request: Request, call_next):
78
- try:
79
- response = await call_next(request)
80
- return response
81
- except (BrokenPipeError, ConnectionResetError, asyncio.CancelledError):
82
- # Ciche ignorowanie zerwanych połączeń klienckich (np. zamknięcie karty, timeout sieci)
83
- logger.debug("🔌 Połączenie klienta zerwane – ignorowanie błędu")
84
- return JSONResponse(status_code=499, content={"error": "Client closed request"})
85
- except Exception as e:
86
- logger.error(f"❌ Błąd serwera: {e}")
87
- return JSONResponse(status_code=500, content={"error": str(e)})
88
-
89
- @app.get("/health")
90
- async def health():
91
- return {"status": "ok", "model": MODEL_FILE, "ctx": N_CTX, "threads": N_THREADS}
92
 
93
- # ---------------- SIGNALS ----------------
94
  def graceful_shutdown(signum, frame):
95
  logger.info("📡 Otrzymano sygnał zakończenia. Zamykanie...")
96
  sys.exit(0)
@@ -98,17 +27,30 @@ def graceful_shutdown(signum, frame):
98
  signal.signal(signal.SIGTERM, graceful_shutdown)
99
  signal.signal(signal.SIGINT, graceful_shutdown)
100
 
101
- # ---------------- START ----------------
102
  if __name__ == "__main__":
103
- logger.info(f"🚀 Start na porcie {PORT} | Context: {N_CTX} | Wątki: {N_THREADS} | Max concurrent: {MAX_CONCURRENCY}")
104
- uvicorn.run(
105
- app,
106
- host="0.0.0.0",
107
- port=PORT,
108
- log_level="info",
109
- timeout_keep_alive=120, # Utrzymuje połączenie przy krótkich zrywach sieci
110
- limit_concurrency=MAX_CONCURRENCY, # Chroni przed OOM na free tier
111
- backlog=16,
112
- ws_ping_interval=30,
113
- ws_ping_timeout=10
114
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  #!/usr/bin/env python3
 
2
  """
3
+ Ultralekki entrypoint dla HF Spaces (Docker SDK)
4
+ Pobiera model z cache HF → uruchamia llama_cpp.server
5
+ OpenAI format | ✅ Brak auth | ✅ Odporny na zerwania | ✅ Optymalizacja CPU/RAM
6
  """
 
7
  import os
8
  import sys
9
  import signal
 
10
  import logging
 
11
  from huggingface_hub import hf_hub_download
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
14
  logger = logging.getLogger(__name__)
15
 
16
+ MODEL_REPO = "unsloth/granite-4.1-3b-GGUF"
17
+ MODEL_FILE = os.environ.get("MODEL_FILE", "granite-4.1-3b-UD-IQ2_M.gguf")
18
+ PORT = os.environ.get("PORT", "7860")
19
+ N_CTX = os.environ.get("N_CTX", "2048")
20
+ N_THREADS = os.environ.get("N_THREADS", "2")
21
+ N_BATCH = os.environ.get("N_BATCH", "512")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
 
23
  def graceful_shutdown(signum, frame):
24
  logger.info("📡 Otrzymano sygnał zakończenia. Zamykanie...")
25
  sys.exit(0)
 
27
  signal.signal(signal.SIGTERM, graceful_shutdown)
28
  signal.signal(signal.SIGINT, graceful_shutdown)
29
 
 
30
  if __name__ == "__main__":
31
+ logger.info(f"⬇️ Pobieranie/weryfikacja: {MODEL_REPO}/{MODEL_FILE}")
32
+ model_path = hf_hub_download(
33
+ repo_id=MODEL_REPO,
34
+ filename=MODEL_FILE,
35
+ resume_download=True,
36
+ local_dir_use_symlinks=False
37
+ )
38
+ logger.info(f"✅ Model gotowy: {model_path}")
39
+
40
+ # Komenda startowa llama_cpp.server (wbudowany serwer OpenAI-compatible)
41
+ cmd = [
42
+ sys.executable, "-m", "llama_cpp.server",
43
+ "--model", model_path,
44
+ "--host", "0.0.0.0",
45
+ "--port", PORT,
46
+ "--n_ctx", N_CTX,
47
+ "--n_threads", N_THREADS,
48
+ "--n_batch", N_BATCH,
49
+ "--n_gpu_layers", "0",
50
+ "--use_mmap",
51
+ "--no_flash_attn"
52
+ ]
53
+
54
+ logger.info(f"🚀 Start serwera: {' '.join(cmd)}")
55
+ # execvp zastępuje proces Pythona serwerem → poprawna obsługa sygnałów Docker/HF
56
+ os.execvp(sys.executable, cmd)