File size: 7,591 Bytes
5e0c7a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c537e8c
5e0c7a6
 
 
 
 
 
 
 
 
 
 
 
 
 
88dd4ad
 
 
5e0c7a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88dd4ad
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
# syntax=docker/dockerfile:1
# ---------------------------------------------------------------------------
# Dockerfile for a Hugging Face "Docker Space" that serves a GGUF model
# (empero-ai/Qwythos-9B-Claude-Mythos-5-1M-GGUF) through llama-cpp-python
# with a streaming Gradio chat UI.
#
# Design goals:
#   - Small final image  -> multi-stage build. All compilers / build tools
#     (cmake, ninja, gcc) live ONLY in the builder stage. The runtime stage
#     just installs the pre-built wheel.
#   - Reliable build      -> pin apt/pip behaviour (no interactive prompts,
#     no cache dirs left behind, explicit CMake flags for llama.cpp so the
#     build never silently falls back to something incompatible with the
#     CPU the Space actually runs on).
#   - CPU-only            -> no CUDA/ROCm toolkits anywhere in the image.
# ---------------------------------------------------------------------------

# =========================== 1. Builder stage ===============================
FROM python:3.11-slim AS builder

# ca-certificates is needed either way (HTTPS to PyPI / abetlen's wheel
# index / the HF Hub later). The heavy compiler toolchain (build-essential,
# cmake, ninja, git) is installed ONLY in the fallback branch below, so the
# common case pays no apt cost either.
RUN apt-get update && apt-get install -y --no-install-recommends \
        ca-certificates \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /build
RUN pip install --no-cache-dir --upgrade pip wheel setuptools

# ---------------------------------------------------------------------------
# Fast path vs. fallback path for llama-cpp-python.
#
# The project's maintainer publishes prebuilt CPU-only wheels at a custom
# index (https://abetlen.github.io/llama-cpp-python/whl/cpu). When one
# matches our Python ABI (cp311) / platform (manylinux x86_64), this avoids
# a from-source compile that otherwise takes 15-30+ minutes on a free HF
# Spaces builder. `--only-binary=:all:` makes pip fail fast instead of
# silently falling back to a slow sdist build if no wheel matches, so we
# can detect the miss and switch strategies ourselves.
#
# If no prebuilt wheel is available (e.g. maintainer hasn't published one
# for the current llama-cpp-python release / Python version yet), we fall
# back to the original from-source build: install compilers, then build
# with explicit CMake flags tuned for broad CPU compatibility:
#   - GGML_NATIVE=OFF   : don't auto-detect the *build* machine's CPU flags.
#                         The image is built on different hardware than it
#                         runs on, so "native" builds can crash with
#                         "illegal instruction" on the actual Space runner.
#   - GGML_AVX2/FMA/F16C: supported by virtually all modern x86_64 cloud
#                         CPUs (including HF's free-tier runners), giving
#                         good performance without AVX-512-only risk.
#   - CMAKE_BUILD_PARALLEL_LEVEL=nproc: the previous Dockerfile left this
#                         unset, which on some setups serializes the C++
#                         compile across hundreds of translation units —
#                         a big, avoidable chunk of that 20+ minute build.
#   - CMAKE_BUILD_TYPE=Release + Ninja: faster/smaller than default Make.
RUN set -eux; \
    if pip wheel --no-cache-dir --only-binary=:all: --wheel-dir /wheels \
         --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu \
         "llama-cpp-python"; \
    then \
        echo "==> Installed prebuilt CPU wheel for llama-cpp-python (fast path)"; \
    else \
        echo "==> No prebuilt wheel available; building llama-cpp-python from source"; \
        apt-get update && apt-get install -y --no-install-recommends \
            build-essential cmake ninja-build git \
        && rm -rf /var/lib/apt/lists/*; \
        export CMAKE_ARGS="-DGGML_NATIVE=OFF -DGGML_AVX2=ON -DGGML_FMA=ON -DGGML_F16C=ON -DCMAKE_BUILD_TYPE=Release -GNinja"; \
        export FORCE_CMAKE=1; \
        # Cap parallel compile jobs at 4: the free HF Spaces Docker builder
        # may report more cores than it has RAM to back, and a runaway
        # `-j$(nproc)` C++ compile is a classic way to get the build OOM-killed.
        BUILD_CORES="$(nproc)"; \
        if [ "$BUILD_CORES" -gt 4 ]; then BUILD_CORES=4; fi; \
        export CMAKE_BUILD_PARALLEL_LEVEL="$BUILD_CORES"; \
        pip wheel --no-cache-dir --wheel-dir /wheels "llama-cpp-python"; \
    fi

# ============================ 2. Runtime stage ================================
FROM python:3.11-slim AS runtime

LABEL maintainer="hf-space" \
      description="Gradio chat UI serving empero-ai/Qwythos-9B-Claude-Mythos-5-1M-GGUF via llama-cpp-python"

# Runtime-only system dependencies:
#   - libgomp1: OpenMP runtime required by llama.cpp's multithreaded kernels.
#   - ca-certificates: needed for HTTPS downloads from the Hugging Face Hub.
# NOTE: no compilers, no cmake, no git here -> keeps the final image lean.
RUN apt-get update && apt-get install -y --no-install-recommends \
        libgomp1 \
        ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# Run as a non-root user (Hugging Face Spaces requirement/best practice).
RUN useradd --create-home --uid 1000 appuser
WORKDIR /app

# Install the pre-built llama-cpp-python wheel from the builder stage.
COPY --from=builder /wheels /wheels
COPY requirements.txt .

RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir /wheels/*.whl && \
    pip install --no-cache-dir -r requirements.txt && \
    rm -rf /wheels /root/.cache/pip ~/.cache/pip

# Application code
COPY app.py .

# ---------------------------------------------------------------------------
# Runtime configuration (all overridable as Space "Variables and secrets"
# without touching the Dockerfile). See README.md for the full list.
# ---------------------------------------------------------------------------
ENV \
    # Where to look for the GGUF model on the Hub.
    GGUF_REPO_ID="empero-ai/Qwythos-9B-Claude-Mythos-5-1M-GGUF" \
    # Leave empty to auto-select; set to force an exact filename.
    GGUF_FILENAME="" \
    # Quantization to prefer when auto-selecting (falls back automatically
    # if this exact quant isn't present in the repo).
    PREFERRED_QUANT="Q4_K_M" \
    # Local, persistent-within-container cache for downloaded model files
    # and Hub metadata so restarts of a *running* Space don't re-download.
    MODEL_CACHE_DIR="/data/models" \
    HF_HOME="/data/hf_home" \
    HF_HUB_ENABLE_HF_TRANSFER="0" \
    # Context window (tokens). The model supports up to 1,048,576 via
    # baked-in YaRN scaling, but free CPU Spaces cannot allocate that much
    # KV-cache. Default is a safe value for a 16GB-RAM CPU Space; raise it
    # via the Space's Variables UI if you have more headroom.
    N_CTX="4096" \
    # Free tier has 2 vCPUs; more threads just adds contention.
    N_THREADS="2" \
    N_BATCH="256" \
    MAX_NEW_TOKENS="1024" \
    TEMPERATURE="0.6" \
    TOP_P="0.95" \
    TOP_K="20" \
    REPEAT_PENALTY="1.05" \
    SYSTEM_PROMPT="" \
    # Number of retries when downloading the model file from the Hub.
    DOWNLOAD_MAX_RETRIES="5" \
    # Gradio server bind settings (must be 0.0.0.0 + 7860 for HF Spaces).
    GRADIO_SERVER_NAME="0.0.0.0" \
    GRADIO_SERVER_PORT="7860" \
    PYTHONUNBUFFERED="1"

# Cache/data directories must be writable by the non-root user.
RUN mkdir -p /data/models /data/hf_home && \
    chown -R appuser:appuser /data /app

USER appuser

EXPOSE 7860

CMD ["python", "app.py"]