File size: 7,675 Bytes
957406c
 
 
 
 
 
 
 
 
8ebf335
 
 
 
 
 
 
 
 
 
 
434da24
8ebf335
 
 
 
 
392efee
e22f356
57426fb
392efee
e22f356
 
392efee
 
 
a6b5235
c459436
434da24
0c43530
 
3a9ad3f
 
392efee
8ebf335
 
 
 
 
 
 
 
 
 
 
 
 
 
57426fb
8ebf335
 
 
392efee
c459436
e22f356
392efee
 
e22f356
0c43530
57426fb
e22f356
 
392efee
 
 
434da24
 
 
3a9ad3f
392efee
 
 
 
e22f356
 
 
 
434da24
edf2ef0
 
392efee
 
e22f356
392efee
 
 
434da24
1420bd1
 
392efee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1420bd1
c459436
 
392efee
c459436
 
392efee
 
 
 
c459436
 
 
 
 
 
 
 
 
 
 
 
 
 
8ebf335
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
---
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.