File size: 4,261 Bytes
da89010
bc610c2
 
 
 
 
 
 
 
 
9e27039
da89010
 
 
 
 
9e27039
da89010
bc610c2
 
 
 
9e27039
da89010
 
 
 
 
1f1ea05
 
 
2f079cf
 
 
 
 
da89010
 
1f1ea05
2f079cf
da89010
 
bc610c2
da89010
 
 
9e27039
da89010
f95c194
9e27039
da89010
9e27039
 
da89010
fd8d54e
 
 
 
 
 
 
 
 
da89010
 
 
 
fd8d54e
da89010
 
 
1f1ea05
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
# ── Base image ────────────────────────────────────────────────
# Alpine (musl libc), not Debian slim (glibc) β€” the abetlen prebuilt CPU
# wheel for llama-cpp-python is itself linked against musl (confirmed via
# readelf: NEEDED libc.musl-x86_64.so.1), even though its filename is
# tagged with the generic "linux_x86_64" platform tag rather than a
# proper musllinux tag. On a glibc image this crashes at import time with
# "OSError: libc.musl-x86_64.so.1: cannot open shared object file".
# All other compiled deps here (pydantic-core, uvloop, httptools) publish
# real musllinux wheels, so nothing needs to compile from source.
FROM python:3.11-alpine

# ── Environment ───────────────────────────────────────────────
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

# ── System dependencies ───────────────────────────────────────
# Runtime-only β€” libstdc++/libgomp satisfy the wheel's NEEDED entries
# (ggml-cpu uses OpenMP + C++). No compiler needed since nothing builds
# from source.
RUN apk add --no-cache curl libstdc++ libgomp

# ── Work directory ────────────────────────────────────────────
WORKDIR /app

# ── Python dependencies ───────────────────────────────────────
# Install llama-cpp-python from precompiled CPU wheels (no compile β€” fast!)
# Pinned version + --only-binary forces pip to fail fast if the abetlen wheel
# index ever stops publishing a matching wheel, instead of silently falling
# back to a from-source cmake build that blows past HF's build timeout.
# Plain llama-cpp-python (no [server] extra) β€” we load the model directly
# with the Llama class and implement the OpenAI-style routes ourselves,
# rather than running llama_cpp.server's own bundled FastAPI app (which had
# a bug in its generic error-handling path that returned empty 400s on every
# proxied request). Then install our own minimal FastAPI stack.
RUN pip install --upgrade pip && \
    pip install \
        --only-binary=llama-cpp-python \
        "llama-cpp-python==0.3.19" \
        --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu && \
    pip install \
        fastapi \
        "uvicorn[standard]" \
        huggingface_hub \
        pydantic

# ── Copy app ──────────────────────────────────────────────────
COPY app.py .

# ── Port ──────────────────────────────────────────────────────
EXPOSE 7860

# ── Default environment (override via Space Settings) ─────────
# SPACE_ROLE: "reasoning" | "uncensored" | "general" (default: general) β€”
# picks the curated model automatically. Deliberately NOT setting MODEL_ID
# here: a hardcoded ENV MODEL_ID would always exist in os.environ, so
# app.py's `os.environ.get("MODEL_ID", ROLE_TO_MODEL_ID[SPACE_ROLE])`
# fallback could never trigger β€” every Space would pin to that value
# regardless of SPACE_ROLE. (This bit us once: this line used to read
# `ENV MODEL_ID="qwen"` and silently overrode SPACE_ROLE on every Space.)
# MODEL_ID / MODEL_REPO / MODEL_FILE: optional raw overrides, set only as a
# Space Variable if you want to bypass role-based selection entirely.
# N_CTX: context window tokens (default: 8192)
# N_THREADS: CPU threads (default: 4)
# API_KEY: optional bearer token to protect the API
# CONFIG_URL: optional URL to a JSON config (for multi-Space clusters)
ENV N_CTX="8192" \
    N_THREADS="4"

# ── Launch ────────────────────────────────────────────────────
CMD ["python", "app.py"]