Cnass commited on
Commit
0f0e3d8
·
verified ·
1 Parent(s): c41ae85

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +79 -0
  2. README.md +125 -5
  3. entrypoint.sh +47 -0
  4. requirements.txt +5 -0
Dockerfile ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # syntax=docker/dockerfile:1
2
+
3
+ # ---------------------------------------------------------------------------
4
+ # Stage 1: build llama-server from source.
5
+ #
6
+ # IMPORTANT: tencent/Hy-MT2-7B-GGUF depends on a custom "STQ" ggml kernel
7
+ # that shipped in https://github.com/ggml-org/llama.cpp/pull/22836. A pip
8
+ # install of llama-cpp-python pins an older llama.cpp commit and will very
9
+ # likely NOT be able to load this GGUF (unknown tensor type / load error).
10
+ # Building llama.cpp from source here (default: the `main` branch, which
11
+ # should have the PR merged by now) avoids that. If it hasn't landed yet,
12
+ # rebuild with:
13
+ # docker build --build-arg LLAMA_CPP_REF=refs/pull/22836/head -t hy-mt2-api .
14
+ # ---------------------------------------------------------------------------
15
+ FROM debian:bookworm-slim AS builder
16
+
17
+ ARG LLAMA_CPP_REF=main
18
+
19
+ RUN apt-get update && apt-get install -y --no-install-recommends \
20
+ build-essential cmake git ca-certificates libcurl4-openssl-dev pkg-config \
21
+ && rm -rf /var/lib/apt/lists/*
22
+
23
+ WORKDIR /build
24
+ RUN git clone https://github.com/ggml-org/llama.cpp.git . \
25
+ && if [ "$LLAMA_CPP_REF" != "main" ]; then \
26
+ git fetch origin "$LLAMA_CPP_REF" && git checkout FETCH_HEAD; \
27
+ fi
28
+
29
+ # GGML_NATIVE=OFF because the Docker build machine's CPU may differ from the
30
+ # machine the image eventually runs on; GGML_AVX2=ON targets the common
31
+ # baseline for modern cloud/Space CPUs. Drop it if you hit "illegal
32
+ # instruction" at runtime on older hardware.
33
+ RUN cmake -B build \
34
+ -DCMAKE_BUILD_TYPE=Release \
35
+ -DLLAMA_CURL=OFF \
36
+ -DGGML_NATIVE=OFF \
37
+ -DGGML_AVX2=ON \
38
+ && cmake --build build --config Release -j"$(nproc)" --target llama-server
39
+
40
+ # ---------------------------------------------------------------------------
41
+ # Stage 2: runtime image (FastAPI gateway + llama-server binary)
42
+ # ---------------------------------------------------------------------------
43
+ FROM python:3.11-slim
44
+
45
+ RUN apt-get update && apt-get install -y --no-install-recommends \
46
+ libgomp1 libcurl4 curl \
47
+ && rm -rf /var/lib/apt/lists/*
48
+
49
+ WORKDIR /app
50
+
51
+ COPY --from=builder /build/build/bin/llama-server /app/llama-server
52
+
53
+ COPY requirements.txt .
54
+ RUN pip install --no-cache-dir -r requirements.txt
55
+
56
+ COPY app ./app
57
+ COPY entrypoint.sh .
58
+ RUN chmod +x /app/llama-server /app/entrypoint.sh
59
+
60
+ ENV MODEL_REPO=tencent/Hy-MT2-7B-GGUF \
61
+ MODEL_FILE=Hy-MT2-7B-Q4_K_M.gguf \
62
+ MODEL_DIR=/app/models \
63
+ LLAMA_SERVER_HOST=127.0.0.1 \
64
+ LLAMA_SERVER_PORT=8080 \
65
+ THREADS=8 \
66
+ PARALLEL_SLOTS=8 \
67
+ CTX_SIZE=16384 \
68
+ MAX_TOKENS=512 \
69
+ TEMPERATURE=0.7 \
70
+ TOP_P=0.6 \
71
+ TOP_K=20 \
72
+ REPEAT_PENALTY=1.05 \
73
+ MAX_BATCH_SIZE=200 \
74
+ API_KEY="" \
75
+ PORT=7860
76
+
77
+ EXPOSE 7860
78
+
79
+ CMD ["./entrypoint.sh"]
README.md CHANGED
@@ -1,10 +1,130 @@
1
  ---
2
- title: Mtapi
3
- emoji: 🐠
4
- colorFrom: pink
5
- colorTo: blue
6
  sdk: docker
 
7
  pinned: false
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Hy-MT2 Translation API
3
+ emoji: 🌐
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: docker
7
+ app_port: 7860
8
  pinned: false
9
+ license: apache-2.0
10
  ---
11
 
12
+ # Hy-MT2 Translation API
13
+
14
+ A batching-optimized translation API for [tencent/Hy-MT2-7B-GGUF](https://huggingface.co/tencent/Hy-MT2-7B-GGUF),
15
+ built to run as a Hugging Face Space Docker app on an 8 vCPU / 32 GB RAM
16
+ instance.
17
+
18
+ ## How it works
19
+
20
+ - **Inference engine**: `llama-server` (from [llama.cpp](https://github.com/ggml-org/llama.cpp)),
21
+ compiled from source in the Docker build (see the note in the `Dockerfile`
22
+ about the STQ kernel this GGUF depends on — it needs a recent llama.cpp).
23
+ It's started with `--threads 8 --parallel 8 --cont-batching`, which uses
24
+ llama.cpp's continuous batching to combine multiple in-flight requests'
25
+ forward passes into shared compute instead of running them one at a time.
26
+ This is the real mechanism behind the "batch instead of one-by-one"
27
+ optimization you asked for — the 8 cores get fed one bigger batched matmul
28
+ per step instead of 8 independent tiny ones.
29
+ - **Gateway**: a small FastAPI app (`app/main.py`) in front of it, which
30
+ builds the Hy-MT2 instruction-format prompt (see `app/translation.py`) for
31
+ each string and fans batch requests out concurrently to `llama-server`,
32
+ bounded by `PARALLEL_SLOTS` so requests queue cleanly instead of
33
+ overloading the model.
34
+ - Chat formatting uses the GGUF's embedded Jinja chat template
35
+ (`llama-server --jinja`), matching the model card's documented usage.
36
+
37
+ ## Endpoints
38
+
39
+ ### `GET /health`
40
+ Backend status check.
41
+
42
+ ### `GET /languages`
43
+ Returns the map of supported language codes → names (the 33 languages
44
+ Hy-MT2 documents support for).
45
+
46
+ ### `POST /translate` — single string
47
+ ```json
48
+ {
49
+ "text": "Where is the nearest potion shop?",
50
+ "target_lang": "tr",
51
+ "preserve_placeholders": true
52
+ }
53
+ ```
54
+ ```json
55
+ { "translation": "En yakın iksir dükkanı nerede?", "elapsed_seconds": 0.41 }
56
+ ```
57
+
58
+ ### `POST /translate/batch` — many strings in one call (recommended: 50-100 per call)
59
+ ```json
60
+ {
61
+ "texts": ["Hello!", "Welcome!", "Potion", "Attack"],
62
+ "target_lang": "tr"
63
+ }
64
+ ```
65
+ ```json
66
+ {
67
+ "translations": [
68
+ { "original": "Hello!", "translation": "Merhaba!" },
69
+ { "original": "Welcome!", "translation": "Hoş geldin!" },
70
+ { "original": "Potion", "translation": "İksir" },
71
+ { "original": "Attack", "translation": "Saldırı" }
72
+ ],
73
+ "count": 4,
74
+ "elapsed_seconds": 0.9,
75
+ "items_per_second": 4.44
76
+ }
77
+ ```
78
+
79
+ For 50,000 game strings: split them into chunks of ~50-100 and call
80
+ `/translate/batch` per chunk (either sequentially or a few chunks at a time)
81
+ rather than one string per HTTP call. `MAX_BATCH_SIZE` (default 200) is a
82
+ server-side safety cap on a single call.
83
+
84
+ **Request fields common to both endpoints:**
85
+ | field | type | notes |
86
+ |---|---|---|
87
+ | `target_lang` | string | ISO code (`tr`, `en`, `ja`, ...) or full name |
88
+ | `source_lang` | string, optional | omit to let the model auto-detect |
89
+ | `style` | string, optional | e.g. `"casual, playful"` — injected as a style instruction |
90
+ | `glossary` | object, optional | `{"HP": "Can Puanı"}` — forces specific term translations |
91
+ | `preserve_placeholders` | bool, default `true` | instructs the model to never touch `\N[1]`, `\V[1]`, `{var}`, `%s`, `%1`, etc. — important for RPG Maker / game strings |
92
+
93
+ ### Auth
94
+ Set the `API_KEY` env var on the Space to require an `X-API-Key` header on
95
+ `/translate*`. Leave empty (default) to disable auth.
96
+
97
+ ## Configuration (env vars)
98
+
99
+ | var | default | meaning |
100
+ |---|---|---|
101
+ | `MODEL_FILE` | `Hy-MT2-7B-Q4_K_M.gguf` | switch to `HY-MT2-7B-Q6_K.gguf` or `HY-MT2-7B-Q8_0.gguf` for higher quality / slower |
102
+ | `THREADS` | `8` | CPU threads for llama-server |
103
+ | `PARALLEL_SLOTS` | `8` | concurrent generation slots (continuous batching) |
104
+ | `CTX_SIZE` | `16384` | total context, split across `PARALLEL_SLOTS` slots |
105
+ | `MAX_TOKENS` | `512` | max output tokens per translation |
106
+ | `MAX_BATCH_SIZE` | `200` | max items accepted per `/translate/batch` call |
107
+ | `TEMPERATURE`/`TOP_P`/`TOP_K`/`REPEAT_PENALTY` | `0.7`/`0.6`/`20`/`1.05` | Hy-MT2 7B recommended sampling params |
108
+ | `API_KEY` | *(empty)* | optional shared secret for `X-API-Key` |
109
+
110
+ ## Deploying
111
+
112
+ 1. On [huggingface.co/new-space](https://huggingface.co/new-space), choose
113
+ **Docker** as the SDK, then push this folder's contents (or upload via
114
+ the web UI / `huggingface_hub`).
115
+ 2. In the Space's **Settings → Hardware**, select a CPU tier with 8 vCPU /
116
+ 32 GB RAM (this needs to be set manually — it's a paid tier and isn't
117
+ controlled by any file in this repo).
118
+ 3. First boot downloads the ~4.6 GB GGUF file, so the first request after a
119
+ cold start can take a few minutes; enable **persistent storage** in Space
120
+ settings to avoid re-downloading on every restart.
121
+
122
+ ## Local testing
123
+
124
+ ```bash
125
+ docker build -t hy-mt2-api .
126
+ docker run -p 7860:7860 --cpus 8 --memory 32g hy-mt2-api
127
+ ```
128
+
129
+ Then open `test-ui/index.html` in a browser, point it at
130
+ `http://localhost:7860`, and use it to benchmark single vs. batch requests.
entrypoint.sh ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ MODEL_PATH="${MODEL_DIR}/${MODEL_FILE}"
5
+
6
+ if [ ! -f "$MODEL_PATH" ]; then
7
+ echo "[entrypoint] Downloading ${MODEL_FILE} from ${MODEL_REPO} ..."
8
+ python -c "
9
+ from huggingface_hub import hf_hub_download
10
+ hf_hub_download(repo_id='${MODEL_REPO}', filename='${MODEL_FILE}', local_dir='${MODEL_DIR}')
11
+ "
12
+ else
13
+ echo "[entrypoint] Found cached model at ${MODEL_PATH}"
14
+ fi
15
+
16
+ echo "[entrypoint] Starting llama-server (threads=${THREADS}, parallel=${PARALLEL_SLOTS}, ctx=${CTX_SIZE}) ..."
17
+ /app/llama-server \
18
+ --model "$MODEL_PATH" \
19
+ --host "${LLAMA_SERVER_HOST}" \
20
+ --port "${LLAMA_SERVER_PORT}" \
21
+ --threads "${THREADS}" \
22
+ --threads-batch "${THREADS}" \
23
+ --ctx-size "${CTX_SIZE}" \
24
+ --parallel "${PARALLEL_SLOTS}" \
25
+ --cont-batching \
26
+ --jinja \
27
+ --no-webui &
28
+ LLAMA_PID=$!
29
+
30
+ echo "[entrypoint] Waiting for llama-server to become healthy ..."
31
+ for _ in $(seq 1 150); do
32
+ if curl -sf "http://${LLAMA_SERVER_HOST}:${LLAMA_SERVER_PORT}/health" >/dev/null 2>&1; then
33
+ echo "[entrypoint] llama-server is ready."
34
+ break
35
+ fi
36
+ if ! kill -0 "$LLAMA_PID" 2>/dev/null; then
37
+ echo "[entrypoint] llama-server exited unexpectedly, aborting." >&2
38
+ exit 1
39
+ fi
40
+ sleep 2
41
+ done
42
+
43
+ echo "[entrypoint] Starting API gateway on port ${PORT} ..."
44
+ uvicorn app.main:app --host 0.0.0.0 --port "${PORT}" &
45
+ API_PID=$!
46
+
47
+ wait -n "$LLAMA_PID" "$API_PID"
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ fastapi==0.115.6
2
+ uvicorn[standard]==0.34.0
3
+ httpx==0.28.1
4
+ pydantic==2.10.5
5
+ huggingface_hub==0.27.1