File size: 6,658 Bytes
f35c74b 4d9c0d5 f35c74b 9abb58f f35c74b 9abb58f f35c74b 9abb58f f35c74b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 | from __future__ import annotations
import os
import sys
from pathlib import Path
from huggingface_hub import hf_hub_download, list_repo_files
MODEL_REPO = os.getenv("MODEL_REPO", "ggml-org/SmolVLM2-2.2B-Instruct-GGUF")
QUANT = os.getenv("QUANT", "Q4_K_M") # used to auto-pick the model file
MODEL_FILE = os.getenv("MODEL_FILE", "auto") # or an exact filename
MMPROJ_FILE = os.getenv("MMPROJ_FILE", "auto") # or an exact filename, or "none" for text-only
MMPROJ_PREFER = os.getenv("MMPROJ_PREFER", "Q8_0,f16,F16") # auto-pick order
MODEL_DIR = Path(os.getenv("MODEL_DIR", "/data/models/smolvlm2"))
LLAMA_SERVER_BIN = os.getenv("LLAMA_SERVER_BIN", "/opt/llama.cpp/llama-server")
LLAMA_HOST = os.getenv("LLAMA_HOST", "0.0.0.0")
LLAMA_PORT = os.getenv("LLAMA_PORT", "7860")
THREADS = os.getenv("THREADS", "8")
CTX_SIZE = os.getenv("CTX_SIZE", "8192")
BATCH_SIZE = os.getenv("BATCH_SIZE", "default")
UBATCH_SIZE = os.getenv("UBATCH_SIZE", "default")
GPU_LAYERS = os.getenv("GPU_LAYERS", "0")
FLASH_ATTN = os.getenv("FLASH_ATTN", "default") # on | off | auto | default
CACHE_TYPE_K = os.getenv("CACHE_TYPE_K", "default")
CACHE_TYPE_V = os.getenv("CACHE_TYPE_V", "default")
IMAGE_MAX_TOKENS = os.getenv("IMAGE_MAX_TOKENS", "default") # only for dynamic-resolution models
CHAT_TEMPLATE_FILE = os.getenv("CHAT_TEMPLATE_FILE", "default") # default = use template embedded in GGUF
TEMPERATURE = os.getenv("TEMPERATURE", "0.2")
TOP_P = os.getenv("TOP_P", "0.95")
TOP_K = os.getenv("TOP_K", "64")
REPEAT_PENALTY = os.getenv("REPEAT_PENALTY", "1.08") # keep 1.0 for OCR / vision
def log(message: str) -> None:
print(f"[startup] {message}", flush=True)
def is_set(value: str) -> bool:
return value.strip().lower() not in {"", "default", "auto", "none", "off"}
def pick_files() -> tuple[str, str | None]:
"""Resolve model + mmproj filenames from the repo listing (or env overrides)."""
files = []
if MODEL_FILE.strip().lower() == "auto" or MMPROJ_FILE.strip().lower() == "auto":
files = [f for f in list_repo_files(MODEL_REPO) if f.lower().endswith(".gguf")]
log(f"GGUF files in repo: {files}")
# main model
if MODEL_FILE.strip().lower() == "auto":
cands = [f for f in files if "mmproj" not in f.lower() and QUANT.lower() in f.lower()]
if not cands:
raise RuntimeError(f"No model file matching QUANT={QUANT}; set MODEL_FILE explicitly.")
model_file = sorted(cands)[0]
else:
model_file = MODEL_FILE
# projector
mmproj_file = None
flag = MMPROJ_FILE.strip().lower()
if flag == "auto":
mm = [f for f in files if "mmproj" in f.lower()]
for pref in [p.strip() for p in MMPROJ_PREFER.split(",") if p.strip()]:
hit = [f for f in mm if pref.lower() in f.lower()]
if hit:
mmproj_file = sorted(hit)[0]
break
if not mmproj_file and mm:
mmproj_file = sorted(mm)[0]
if not mmproj_file:
log("WARNING: no mmproj file found; image input will not work")
elif flag not in {"", "none", "off"}:
mmproj_file = MMPROJ_FILE
return model_file, mmproj_file
def fetch(filename: str) -> str:
MODEL_DIR.mkdir(parents=True, exist_ok=True)
local = MODEL_DIR / filename
if local.exists():
log(f"Using cached file: {local}")
return str(local)
log(f"Downloading {MODEL_REPO}/{filename}")
path = hf_hub_download(repo_id=MODEL_REPO, filename=filename, local_dir=str(MODEL_DIR))
log(f"Ready: {path}")
return path
def locate_server() -> str:
"""Find llama-server even if the release tarball layout differs; dump diagnostics if not."""
import shutil
import subprocess
root = Path(LLAMA_SERVER_BIN).parent
candidates = [Path(LLAMA_SERVER_BIN)]
for base in {root, Path(os.getenv("LLAMA_DIR", "/opt/llama.cpp"))}:
if base.exists():
candidates += list(base.rglob("llama-server"))
found = next((c for c in candidates if c.is_file()), None) or (
Path(shutil.which("llama-server")) if shutil.which("llama-server") else None
)
if not found:
log(f"llama-server not found. Contents of {root}:")
subprocess.run(["ls", "-laR", str(root)], check=False)
raise FileNotFoundError("llama-server binary missing; see listing above")
os.chmod(found, 0o755)
log(f"Using llama-server at {found}")
# Show missing shared libraries / bad loader, the other common ENOENT cause
subprocess.run(["ldd", str(found)], check=False)
os.environ["LD_LIBRARY_PATH"] = f"{found.parent}:{os.environ.get('LD_LIBRARY_PATH', '')}"
return str(found)
def build_command(model_path: str, mmproj_path: str | None, server_bin: str) -> list[str]:
cmd = [
server_bin,
"-m", model_path,
"--host", LLAMA_HOST,
"--port", LLAMA_PORT,
"--threads", THREADS,
"--ctx-size", CTX_SIZE,
"--n-gpu-layers", GPU_LAYERS,
"--parallel", "1",
"--cont-batching",
"--temp", TEMPERATURE,
"--top-p", TOP_P,
"--top-k", TOP_K,
"--repeat-penalty", REPEAT_PENALTY,
]
def add(flag: str, value: str) -> None:
if is_set(value):
cmd.extend([flag, value])
add("--batch-size", BATCH_SIZE)
add("--ubatch-size", UBATCH_SIZE)
add("--cache-type-k", CACHE_TYPE_K)
add("--cache-type-v", CACHE_TYPE_V)
add("-fa", FLASH_ATTN)
add("--image-max-tokens", IMAGE_MAX_TOKENS)
add("--chat-template-file", CHAT_TEMPLATE_FILE)
if mmproj_path:
cmd.extend(["--mmproj", mmproj_path])
if GPU_LAYERS == "0":
cmd.append("--no-mmproj-offload") # keep the image encoder on CPU
return cmd
def main() -> None:
binary_dir = str(Path(LLAMA_SERVER_BIN).parent)
existing = os.environ.get("LD_LIBRARY_PATH")
os.environ["LD_LIBRARY_PATH"] = binary_dir if not existing else f"{binary_dir}:{existing}"
os.environ.setdefault("OMP_NUM_THREADS", THREADS)
os.environ.setdefault("OPENBLAS_NUM_THREADS", THREADS)
os.environ.setdefault("MKL_NUM_THREADS", THREADS)
model_file, mmproj_file = pick_files()
model_path = fetch(model_file)
mmproj_path = fetch(mmproj_file) if mmproj_file else None
server_bin = locate_server()
cmd = build_command(model_path, mmproj_path, server_bin)
log("Starting native llama.cpp web UI")
log(" ".join(cmd))
os.execvpe(cmd[0], cmd, os.environ)
if __name__ == "__main__":
try:
main()
except Exception as exc:
print(f"[fatal] {exc}", file=sys.stderr, flush=True)
raise
|