inference / README.md
0xarchit's picture
some experimental
57426fb
|
Raw
History Blame Contribute Delete
7.68 kB
---
title: Inference
emoji:
colorFrom: gray
colorTo: indigo
sdk: docker
pinned: false
---
# CPU Inference Backend
This Space is a backend-only OpenAI-compatible inference server built on `llama.cpp` and optimized for CPU-only Hugging Face Docker Spaces.
It automatically downloads the target Hugging Face model from `MODEL_NAME`, stores it under `/data/models/model.gguf`, and starts `llama-server` with OpenAI-compatible endpoints.
## Environment variables
Set these in the Space settings:
- `MODEL_NAME`: Hugging Face repo id that contains one or more GGUF files
- `MODEL_FILE`: optional exact GGUF filename inside `MODEL_NAME`; useful for testing faster/lower quality quants
- `HF_TOKEN`: Hugging Face token used for the download
- `API_PASSWORD`: bearer token required by the API
Optional tuning variables:
- `CTX_SIZE`: context size, default `4096` (reduce to 2048-4096 for small models to improve speed)
- `THREADS`: CPU thread count, default auto-detect via cgroup CPU quota, then `nproc`
- `OMP_PROC_BIND`, `OMP_PLACES`, `OMP_WAIT_POLICY`: OpenMP CPU placement controls, default `FALSE`, `cores`, and `PASSIVE`
- `THREADS_BATCH`: batch thread count, default matches THREADS
- `BATCH_SIZE`: prompt batch size, default 128 for balanced mode
- `UBATCH_SIZE`: micro-batch size, default 128 for balanced mode
- `CACHE_TYPE_K`: KV cache type for keys, default `f16` (use `q4_0` for memory savings)
- `CACHE_TYPE_V`: KV cache type for values, default `f16` (use `q4_0` for memory savings)
- `PERF_PROFILE`: `low_latency`, `balanced` (default), or `throughput`
- `REASONING`: reasoning mode, accepts `True`, `False`, or `auto` and maps to llama.cpp `--reasoning`
- `LANGSEARCH_API_KEY`: optional API key for LangSearch web search tool (free tier: 1 req/sec, 60/min, 1000/day)
- `ENABLE_TOOLS`: set to `1` to enable llama.cpp shell tool support when `LANGSEARCH_API_KEY` is present, default `0`
- `HTTP_THREADS`: HTTP server worker threads, default `1` for personal single-request inference
- `LOG_VERBOSITY`: llama.cpp log verbosity, default `1` to reduce runtime logging overhead
- `MMAP`: set to `1` to use memory-mapped model loading, default `0`
- `FLASH_ATTN`: set to `0` to disable flash attention if a model hangs or fails during startup, default `1`
- `NO_WARMUP`: set to `1` to skip model warmup and save startup time
- `PORT`: listen port, default `7860`
## Endpoints
The server exposes:
- `/v1/chat/completions`
- `/v1/completions`
- `/v1/models`
Streaming is supported by `llama-server`. Bearer token authentication uses `--api-key` directly.
## Build and runtime
The Docker image uses a multi-stage build on Debian bookworm. The builder stage installs `git`, `build-essential`, `cmake`, and OpenBLAS, clones `llama.cpp`, and compiles `llama-server` with `-Ofast`, `-march=native`, `-flto`, OpenBLAS, and native CPU optimizations. The runtime stage keeps only Python, OpenBLAS/OpenMP runtime libraries, the compiled server, and the downloader.
Model downloads and HF cache live on the `/data` bucket so restarts do not redownload the model.
## Performance Optimization
For small 2B models on CPU-only inference (~2-4 t/s baseline), optimize these settings:
**High Impact:**
- The server forces `--parallel 1`, so only one request runs at a time and all CPU is focused on the active query
- Runtime uses `--no-mmap`, `--flash-attn on`, `--threads-http 1`, and low log verbosity to favor single-request inference over serving overhead
- OpenBLAS is enabled because benchmark results on the Space showed BLAS-off hurt prompt processing and did not recover generation speed
- `BATCH_SIZE` & `UBATCH_SIZE`: Use **64-256** for faster prompt ingestion; tiny values like 8 cause many small prompt batches
- `PERF_PROFILE`: Use **`balanced`** for normal personal use; `throughput` only changes prompt batch sizing now
**Medium Impact:**
- `NO_WARMUP=1`: Skip model warmup to reduce startup latency by ~0.2s
- `CACHE_TYPE_K`, `CACHE_TYPE_V`: Try `q4_0` instead of `f16` to reduce KV cache bandwidth and memory use at longer contexts
- `ENABLE_TOOLS`: Leave as `0` unless you need tool calling. Built-in shell tools add prompt/tool overhead and should not be exposed publicly.
- `MODEL_FILE`: If the repo has multiple GGUFs, test a lighter quant such as `Q4_0`, `Q4_K_S`, or `Q3_K_M` for speed/quality tradeoffs.
- Compatibility: if a GGUF hangs during startup, try `FLASH_ATTN=0`, `CACHE_TYPE_K=f16`, `CACHE_TYPE_V=f16`, and then `MMAP=1`.
**Example for 2B model (CPU-only):**
```
CTX_SIZE=2048
BATCH_SIZE=128
UBATCH_SIZE=128
PERF_PROFILE=balanced
REASONING=False
ENABLE_TOOLS=0
CACHE_TYPE_K=q4_0
CACHE_TYPE_V=q4_0
```
Expected: faster first-token latency from prompt ingestion. Generation speed is still CPU-bound on the free tier, so a 2B Q4 model will usually remain in the low single-digit tokens/sec range.
## Web Search Tool (Standalone)
When `ENABLE_TOOLS=1` and `LANGSEARCH_API_KEY` is set, `start.sh` enables llama.cpp's shell tool so the model can launch the local LangSearch wrapper.
llama.cpp does not expose arbitrary external HTTP APIs as first-class built-in tools, so LangSearch is still implemented as a local wrapper that runs through the shell tool.
**Use cases:**
- Call from your application layer to augment model responses
- Pre-process queries before sending to model
- Post-process to fetch real-time data for specific topics
**API Usage (from application):**
```python
import requests
import json
response = requests.post(
"http://localhost:7860/v1/chat/completions",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={
"model": "model",
"messages": [{"role": "user", "content": "What's the latest news?"}],
"temperature": 0.7
}
)
latest_response = response.json()['choices'][0]['message']['content']
```
Then call the search tool from your backend or from the shell tool:
```bash
python3 /app/search_tool.py "latest AI news" 5 true noLimit
```
Combine results before returning to user.
**Command-line (Docker container):**
Examples:
```bash
python3 /app/search_tool.py "latest AI news" 5 true noLimit
python3 /app/search_tool.py "Python 3.13 release" 3 false oneWeek
```
Parameters:
- `query`: search string (required)
- `count`: max results 1-10 (default: 5)
- `summary`: include summaries (default: true)
- `freshness`: `oneDay`, `oneWeek`, `oneMonth`, `oneYear`, `noLimit` (default: `noLimit`)
Output is JSON with search results, URLs, snippets, and optional summaries.
## curl examples
```bash
curl http://localhost:7860/v1/models \
-H "Authorization: Bearer $API_PASSWORD"
```
```bash
curl http://localhost:7860/v1/chat/completions \
-H "Authorization: Bearer $API_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"model": "model",
"messages": [
{"role": "user", "content": "Write a one-sentence summary of llama.cpp."}
],
"stream": false
}'
```
```bash
curl http://localhost:7860/v1/completions \
-H "Authorization: Bearer $API_PASSWORD" \
-H "Content-Type: application/json" \
-d '{
"model": "model",
"prompt": "Explain KV cache in one paragraph.",
"stream": false
}'
```
## OpenAI SDK example
```python
import os
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:7860/v1",
api_key=os.environ["API_PASSWORD"],
)
response = client.chat.completions.create(
model="model",
messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)
```
## Hugging Face Spaces notes
This repository is ready for a Docker Space with no frontend. The only required changes at deployment time are the three environment variables above.