高性能 LLM 推理引擎 - 可替换 Ollama,具有更快的多轮推理、更低的 TTFT 和更高的吞吐量,通过前缀缓存实现
Fox is dual-licensed MIT OR Apache-2.0 and stays that way. There is no paid tier and no plan for one.
# Linux x86_64 — picks the Vulkan build when a GPU is present, CPU otherwise
curl -fsSL https://github.com/ferrumox/fox/releases/latest/download/install.sh | shmacOS and Windows: build from source (below), or run the Linux installer under WSL2. Prebuilt binaries are Linux x86_64 for now.
# Pull a model and start (qwen3.6 is 22 GB; qwen3.5 is 2.7 GB if you want a quicker first run)
fox pull qwen3.6
fox serve
# Ask something (OpenAI-compatible)
curl http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"qwen3.6","messages":[{"role":"user","content":"Hello!"}],"stream":true}'
# If you already use Ollama — just change the port from 11434 to 8080. That's it.Fox wraps llama.cpp, so a single request decoding on its own runs the same kernels
llama-server runs. There is no room for fox to be dramatically faster at that, and it
isn't. Where fox pulls ahead is when requests arrive together and share a prompt.
Radeon 890M, Vulkan, Llama-3.2-1B-Instruct-Q8_0, 1856-token shared system prompt. Both servers built from the same vendored llama.cpp, one running at a time, arms alternated across 3 rounds. All ranges below are disjoint.
| Workload | fox | llama-server |
|---|---|---|
| 8 clients, shared prompt, cold — TTFT p50 | 1129 ms | 4550 ms |
| 16 clients, shared prompt, cold — TTFT p50 | 1402 ms | 8064 ms |
| 16 clients, whole burst wall clock | 3.8 s | 16.2 s |
| 4 clients, short unrelated prompts — throughput | 96% of llama-server | baseline |
Doubling the clients costs fox 24% more time to first token and llama-server 79%.
That last row is not a typo and it is not buried on purpose: on single-turn requests with short prompts, fox is about 4% behind. That workload cannot see any of the work fox does, because there is no prompt worth reusing. If your traffic looks like that, fox will not make it faster.
Reproduce either one:
scripts/ab_shared_prefix.sh # concurrent burst behind a shared prompt
scripts/ab_bench.sh # decode-bound throughputFull methodology, including two ways these benchmarks produced convincing wrong answers
before they produced right ones, is in docs/design/rocm-benchmarking-2026-08.md.
Numbers against Ollama are pending re-measurement on current hardware. The figures that
used to sit here were from an RTX 4060 with no recorded methodology, and this project's
rule is that a before/after claim comes from scripts/ab_bench.sh or it does not get
published.
Sequences remember what they hold. Every sequence keeps the tokens resident in its KV cache, including the tokens it generated. A new request is matched to the sequence sharing the longest prefix with it and skips the prefill for that overlap. In a chat, the second turn does not re-read the first.
Requests can copy a prefix from a live sequence. This is the part other llama.cpp
servers do not do. Slot affinity normally reuses an idle sequence, so when eight requests
carrying the same system prompt arrive at once, none of them can reuse anything and all
eight prefill the same tokens. Fox copies the shared prefix out of a sibling that is
already decoding. llama-server cannot: its slot selection skips busy slots in both its
similarity pass and its LRU fallback.
A shared prefix is paid for once. Sequences sharing a prefix share the block budget for it instead of each reserving a copy, so the server admits as much concurrency as the hardware actually holds.
Requests do not queue behind each other. Continuous batching decodes concurrent requests in the same pass, so a long generation for one client does not delay a short question from another.
No code changes needed — just change the base URL to http://localhost:8080.
| Client / Tool | Protocol | Status |
|---|---|---|
| Open WebUI | Ollama | ✓ Works out of the box |
| Continue.dev | Ollama | ✓ Works out of the box |
| LangChain | OpenAI | ✓ Works out of the box |
| LlamaIndex | OpenAI | ✓ Works out of the box |
| Cursor / Copilot Chat | OpenAI | ✓ Works out of the box |
ollama CLI |
Ollama | ✓ Works out of the box |
openai Python SDK |
OpenAI | ✓ Works out of the box |
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="sk-local")
resp = client.chat.completions.create(
model="qwen3.6",
messages=[{"role": "user", "content": "Say hi in 5 words."}],
)
print(resp.choices[0].message.content)import OpenAI from "openai";
const openai = new OpenAI({ baseURL: "http://localhost:8080/v1", apiKey: "sk-local" });
const resp = await openai.chat.completions.create({
model: "qwen3.6",
messages: [{ role: "user", content: "Say hi in 5 words." }],
});
console.log(resp.choices[0].message?.content);VSCode / Cursor
{ "github.copilot.advanced": { "serverUrl": "http://localhost:8080" } }Continue.dev (~/.continue/config.json)
{
"models": [{
"title": "fox (local)",
"provider": "openai",
"model": "qwen3.6",
"apiBase": "http://localhost:8080/v1"
}]
}See examples/ for more integration guides.
Fox detects CUDA, ROCm, Metal, and Vulkan at runtime — one binary runs on any hardware.
| Platform | GPU backends |
|---|---|
| Linux x86_64 | CUDA, ROCm, Vulkan |
| Windows x86_64 | CUDA, Vulkan |
| macOS Apple Silicon | Metal |
| macOS Intel | CPU only |
| Linux ARM64 | CPU only |
Backends are compiled as shared libraries and loaded at runtime, which is why one binary covers all of them rather than needing a build per vendor.
Auto-detection priority: CUDA → ROCm → Vulkan → Metal → CPU.
curl -fsSL https://github.com/ferrumox/fox/releases/latest/download/install.sh | shIt detects /dev/dri and installs the Vulkan build when a GPU is present (AMD/Intel
iGPUs included) or the CPU build otherwise, verifies the published checksum, and
tells you if $PREFIX/bin is not on your PATH. Override with --vulkan, --cpu,
--version vX.Y.Z or --prefix ~/.local.
Or take the tarball yourself — two variants per release:
V=0.20.2
curl -LO https://github.com/ferrumox/fox/releases/download/v$V/fox-$V-x86_64-unknown-linux-gnu-vulkan.tar.gz
tar xzf fox-$V-x86_64-unknown-linux-gnu-vulkan.tar.gz # drop -vulkan for the CPU buildThe .so files in the tarball must stay beside the binary: fox is linked with
RPATH=$ORIGIN and looks for its backends nowhere else.
No prebuilt binaries yet — the release workflow builds Linux x86_64 only. Either run the Linux installer under WSL2, or build from source:
git clone --recurse-submodules https://github.com/ferrumox/fox
cd fox && cargo build --release --bin fox--recurse-submodules is not optional: llama.cpp is vendored, not a system dependency.
git clone --recurse-submodules https://github.com/ferrumox/fox
cd fox
cargo build --releaseGPU backend is detected at runtime — no recompilation needed when switching between CPU, CUDA, and Metal.
docker run -p 8080:8080 \
-v ~/.cache/ferrumox/models:/root/.cache/ferrumox/models \
ferrumox/fox serve
# Or with docker compose
docker compose up…| Method | Path | Description |
|---|---|---|
| POST | /v1/chat/completions |
Chat completions — streaming + non-streaming (OpenAI) |
| POST | /v1/completions |
Text completions (OpenAI) |
| POST | /v1/embeddings |
Embeddings (OpenAI) |
| GET | /v1/models |
List all models on disk (OpenAI) |
| GET | /v1/models/:model |
Single model info (OpenAI) |
| POST | /api/chat |
Chat — NDJSON streaming (Ollama) |
| POST | /api/generate |
Generate — NDJSON streaming (Ollama) |
| POST | /api/embed |
Embeddings (Ollama) |
| GET | /api/tags |
List models on disk (Ollama) |
| GET | /api/ps |
List loaded models (Ollama) |
| POST | /api/show |
Model metadata (Ollama) |
| DELETE | /api/delete |
Remove a model file (Ollama) |
| POST | /api/pull |
Pull a model from HuggingFace (SSE) |
| POST | /api/copy |
Duplicate a model under a new name (Ollama) |
| POST | /api/create |
Create a model from a Modelfile (Ollama) |
| POST | /api/models/:name/load |
Load a model into memory on demand |
| POST | /api/models/:name/unload |
Evict a loaded model from memory |
| GET | /api/version |
Server version — for Ollama client detection |
| POST | /infill |
Fill-in-the-middle completion for editor plugins |
| POST | /rerank, /v1/rerank |
Score documents against a query (needs --reranking) |
| POST | /tokenize, /detokenize |
Convert between text and token ids |
| POST | /apply-template |
Render messages through the model's chat template |
| GET | /props |
Server and model introspection, sampling defaults |
| GET | /slots |
Per-sequence state, resident tokens, KV pool occupancy |
| GET/POST | /lora-adapters |
Inspect loaded LoRA adapters and re-scale them at runtime |
| GET | /health |
Health + KV cache metrics |
| GET | /metrics |
Prometheus scrape endpoint |
Runs any GGUF model: Llama, Mistral, Gemma, Qwen, DeepSeek and the rest.
Two APIs, no code changes. OpenAI-compatible /v1/* and Ollama-compatible /api/*
on the same port. Point an existing client at localhost:8080 and it works.
Prompt reuse that survives concurrency. Sequences keep the tokens they hold, including generated ones, and a new request skips the prefill for whatever prefix it shares. Requests arriving together can copy a shared prefix out of a sequence that is still decoding, and they share the block budget for it rather than each reserving a copy.
Continuous batching. Concurrent requests decode in the same pass instead of queueing.
Speculative decoding. N-gram proposal built in, or a draft model via --draft-model.
Multi-model serving with lazy loading and LRU eviction. No model needs naming up
front; fox loads it on first request and evicts by --max-models and --keep-alive-secs.
Structured output and function calling. JSON Schema compiled to GBNF, raw GBNF grammars accepted directly, and tool-call parsers for Hermes, Mistral and Llama 3.
Vision via llama.cpp mtmd (--mmproj), embeddings, and reranking.
LoRA adapters loaded at startup and re-scaled at runtime without a restart.
Runs where the memory is. Multi-GPU layer split (--split-mode, --tensor-split,
--main-gpu), MoE expert offload to RAM (--moe-cpu), KV cache quantization (f16,
q8_0, q4_0), and a host-RAM prompt cache (--cache-ram) for conversations that
should stay warm without holding GPU blocks.
Survives real traffic. Closing a connection frees its GPU memory immediately. Context rolling keeps a generation going when the window fills. Decode failures retry by batch bisection instead of dropping the request.
Operable. Prometheus metrics, optional FOX_API_KEY auth, permissive CORS, a config
file at ~/.config/ferrumox/config.toml, model aliases, Docker and systemd units.
All flags can also be set via environment variable or ~/.config/ferrumox/config.toml.
| Flag | Env | Default | Description |
|---|---|---|---|
--model-path |
FOX_MODEL_PATH |
— | GGUF model to pre-load (optional; supports nested paths) |
--port |
FOX_PORT |
8080 |
Bind port |
--host |
FOX_HOST |
0.0.0.0 |
Bind host |
--max-models |
FOX_MAX_MODELS |
1 |
Max models in memory simultaneously (LRU evicti |
暂无开放 Issues,或尚未同步最近议题。