# Plan — Ferrum Engine
## Vision
LLM inference engine in Rust, open source, with higher throughput than vLLM and simpler distribution than Ollama. Key differentiator: **a single binary, no Python, no runtime dependencies**.
---
## Released versions
### v0.1.0 (2026-03-08) — Functional MVP
- OpenAI-compatible API: `POST /v1/chat/completions` (SSE streaming + non-streaming), `POST /v1/completions`, `GET /v1/models`, `GET /health`
- Inference engine: llama.cpp FFI, GGUF, continuous batching with LIFO preemption
- KV cache manager: block pool with free_list, automatic calculation from GPU memory
- Sampling: temperature, top-p
- Full config: CLI flags + env vars
- Graceful shutdown, structured logging (tracing, JSON mode)
### v0.2.0 (2026-03-09) — Performance and observability
- Full stochastic sampling: top-K, repetition penalty, seed for reproducibility
- Critical fix: stable `kv_seq_id` per request (prevents llama_decode crashes with >1 request)
- Docker support: multi-stage Dockerfile + docker-compose
- `ferrum-bench`: integrated benchmark with TTFT P50/P95, throughput, latency P50/P95/P99
- `SamplingParams` unified struct for grouping sampling hyper-parameters
### v0.3.0 (2026-03-09) — Memory optimisation and complete API
- PageTable: explicit logical→physical block mapping per request; ref_count + CoW infrastructure per block
- Hash-based prefix caching: reuse KV cache across requests with identical prompts (skip re-prefill)
- Stop sequences: `stop: string | string[]` with rolling-buffer cross-token-boundary detection
- `GET /metrics` Prometheus: 8 metrics (request rate, latency histogram, KV usage, prefix hit ratio)
- Streaming SSE: `usage` included in the final chunk (OpenAI spec)
- Repo quality: GitHub Actions CI, `cargo fmt`, `clippy -D warnings` clean, 22 unit tests
### v0.3.1 (2026-03-09) — Patch release
- Fix: SIGABRT crash on hybrid/recurrent models (Qwen3.5, Mamba) — `llama_memory_seq_cp` is
now only called when `llama_memory_can_shift()` returns true; prefix caching is automatically
disabled for unsupported backends
- Fix: CUDA build — removed `cudarc` dep, corrected linker flags for `ggml-cuda` + driver API
- Fix: prefix-cache boundary token submitted at wrong position (pos n instead of n-1)
### v0.4.0 (2026-03-09) — CLI, robustness and internal improvements
- `ferrum` binary: unified CLI with `serve`, `run` (single-shot terminal inference) and `pull` (HuggingFace Hub download) subcommands
- `--system-prompt` flag for both `serve` and `run`; `--json-logs` on `serve`
- CUDA build: dynamic `nvcc` detection in `build.rs`; works with non-standard CUDA installations
- Fix: KV cache positional gap — `prefilled_tokens` field + corrected `context_len()` prevents `find_slot: non-consecutive token position` on hybrid/recurrent models
- Fix: graceful recovery on KV cache exhaustion (`llama_decode` failure now marks requests `StopReason::Length` instead of crashing the engine)
- Fix: stale `stop_reason` cleared when a preempted request is re-admitted to `Prefilling`
- Internal: `ahash` replaces `DefaultHasher` for `hash_tokens` (faster, less collision-prone)
- Internal: prefix cache uses `lru::LruCache` instead of `HashMap` (correct LRU ordering for future eviction)
- 13 unit tests for sampler pipeline (`sample_greedy`, `apply_repetition_penalty`, `sample_token`)
### v0.5.0 (2026-03-09) — Block-level prefix caching, True CoW, and CPU↔GPU swap scaffold
- Block-level chain-hash prefix caching: `compute_block_hash` + `prompt_block_hashes`; partial prefix hits (e.g. shared system prompt)
- True CoW before decode: `is_shared` + `copy_on_write` guard in `run_decode`
- `RequestState::Swapped` + `swap_out` / `swap_in` scaffold; `--swap-fraction` flag (placeholder pending llama.cpp API)
### v0.5.1 (2026-03-09) — Output pipeline fixes and `--show-thinking`
- Fix: EOG token detection uses `llama_vocab_is_eog` — covers all end-of-generation tokens, not just the primary EOS (critical for Qwen3.5 which has 5 EOG tokens)
- Fix: `<|im_end|>` generated as 6 separate BPE tokens no longer leaks into user output — two-stage `pending_output` buffer detects multi-token control patterns and stops generation correctly
- `--show-thinking` flag for `ferrum run`: stream the `…` reasoning block to stdout for debugging or transparency
### v0.7.0 — Ollama Drop-in
- Full Ollama-compatible API: `POST /api/generate`, `POST /api/chat` (NDJSON streaming)
- `GET /api/tags`, `GET /api/ps`, `POST /api/show`, `DELETE /api/delete`
- `POST /api/pull` with SSE progress events; HuggingFace Hub download backend
- `GET /api/version` for Ollama client auto-detection
### v0.8.0 — Ecosystem Ready
- Function calling: tools → system message injection + `try_parse_tool_call` post-generation
- Structured output: `response_format.type == "json_object"` → system message injection
- Embeddings: `POST /v1/embeddings`, `POST /api/embed`
- `fox show` CLI: `parse_architecture()`, `parse_quantization()`, QUANT_TAGS / ARCH_TAGS
- KV cache type configuration; enhanced model loading with GPU support
### v0.9.0 — Multi-Model
- `ModelRegistry` with DashMap + LRU eviction (`EngineEntry`, `RegistryConfig`)
- `AppState` with `registry: Arc` replacing single engine
- Engine loops started inside `ModelRegistry::get_or_load`; aborted on eviction via `Drop`
- Keep-alive: `last_used: DashMap` + background task (`start_eviction_task`)
- `--max-models`, `--keep-alive-secs` flags; `GET /api/ps` lists all loaded models
- Model name resolution: alias → exact stem → starts-with → contains
### v0.10.0 — Ollama Compat + Tools
- Aliases file: `~/.config/ferrumox/aliases.toml`; `--alias-file` flag
- Config file: `~/.config/ferrumox/config.toml` (env `$FOX_CONFIG`) loaded before CLI parse
- Request cancellation: `handle_logits` detects `send().is_err()` → `clear_sequence + mark_finished(Preempt)`
- `--model-path` now optional (lazy loading when omitted)
- Comprehensive tests for configuration and pull handler
### v1.0.0 — Production
- Full authentication and middleware support (API key validation, middleware stack)
- System metrics tracking in benchmark tool
- Binary renamed to `fox`; default models dir `~/.cache/ferrumox/models`
- All endpoints stable; production-grade error handling
### v1.0.x — Post-release patches (2026-03-22)
**Thinking auto-detection by model**
- `show_thinking` was hardcoded (`false` in OpenAI, `true` in Ollama); now calls `engine.supports_thinking()` in all endpoints and the CLI
- Logic: `` tokenises to ≤2 tokens on reasoning models (Qwen3, DeepSeek-R1) → `show_thinking = true`; multi-token split on non-reasoning models → `false`
- `sampling_from_ollama` now receives `show_thinking: bool` instead of hardcoding it
- Files: `src/api/v1/chat.rs`, `src/api/ollama/chat.rs`, `src/api/ollama/generate.rs`, `src/api/shared/inference.rs`
**LenientJson extractor — fix LiteLLM / Google ADK**
- New `src/api/shared/extractor.rs`: `LenientJson` parses request body as JSON without requiring `Content-Type: application/json`
- All Ollama handlers (`/api/chat`, `/api/generate`, `/api/embed`, `/api/show`, `/api/delete`) now use `LenientJson`
- OpenAI handlers (`/v1/*`) keep strict `Json` (correct per spec)
- Root cause: LiteLLM and Google ADK omit or vary the Content-Type header, triggering Axum's JSON rejection
**Improved serve logs**
- On model load: `INFO model="…" thinking=true model ready` — immediately visible if a model supports reasoning
- On every request: `thinking=true/false` field added alongside `model`, `stream`, `prompt_tokens`
- Allows instant diagnosis when Google ADK / LiteLLM doesn't show `` output
**Model load error differentiation**
- Before: any `get_or_load` failure → HTTP 404 + `WARN "model not found"` (even OOM)
- After: `src/api/error.rs` adds `load_model_or_respond()` helper that splits the two cases:
- Model not on disk → `WARN "model not found"` → HTTP 404
- Model exists but load failed (OOM, corrupt file…) → `ERROR "failed to load model"` → HTTP 503
- `AppError::ModelLoadFailed` → 503 Service Unavailable
- All handlers use the helper; `ModelRegistry::resolve_model_name` made `pub(crate)`
**Thinking suppression in streaming mode**
- OpenAI and Ollama streaming endpoints now suppress `…` from the output (`show_thinking=false`, `initial_in_thinking=supports_thinking`): the model still reasons but the client only receives the visible answer
- Ollama non-streaming: `show_thinking=true` so `extract_thinking` can populate `message.thinking` separately
- Ollama `/api/generate` (no `thinking` field): always suppressed
- Integration tests added: `ThinkingStubModel` + 6 tests in `tests/integration.rs`; bash e2e script `scripts/e2e_thinking.sh`
**Pending — Ollama streaming thinking (ticket for v1.1.0 or later)**
- Currently: thinking is suppressed from Ollama chat streams (no `message.thinking` per chunk)
- Real Ollama ≥0.7 sends per-chunk thinking: `{"message":{"content":"","thinking":"…"},"done":false}`
- To implement: track in-thinking state across stream chunks via `Arc>` shared into the `ndjson_stream` closure; when `in_thinking=true`, route token to `message.thinking` instead of `message.content`
- Requires: `show_thinking=true` for Ollama streaming (not false as today), state machine in the stream callback
- Impact: Google ADK and other Ollama-aware clients would see the reasoning in real time
---
## Phases (completed)
### Phase 1 — Functional MVP ✅
**Goal: something that works and can be demonstrated**
- OpenAI-compatible server with SSE streaming
- Continuous batching with LIFO preemption
- KV cache manager with block pool
- ferrum-bench integrated benchmark
**Deliverable:** functional server, OpenAI-compatible, with basic continuous batching
### Phase 2 — Performance ✅
**Goal: outperform vLLM in throughput on equivalent hardware**
- PageTable + true CoW + block-level prefix caching
- CPU↔GPU swap scaffold
- Stop sequences, Prometheus metrics
**Deliverable:** published benchmarks, technical blog post, first GitHub stars
### Phase 3 — Complete product ✅
**Goal: real alternative with a community**
- Multi-model registry (v0.9.0)
- Ollama drop-in compatibility (v0.7.0)
- Function calling + structured output (v0.8.0)
- Production-grade auth + middleware (v1.0.0)
---
## Future Roadmap (post-v1.0.0)
### v1.1.0 — "Benchmarks & Visibility"
**Theme:** Demonstrate publicly that ferrumox outperforms Ollama, and make it trivially easy to install on any platform. Without published data and simple installation, the performance differentiator has no credibility.
#### Observability
| Feature | Details |
|---|---|
| CI Benchmark Gate | GitHub Actions: `fox-bench --output json` on each PR; fails if TTFT P95 regresses >5% or throughput drops >3% |
| TTFT Histogram (Prometheus) | `ferrumox_ttft_seconds` histogram + `model_name` label on all existing metrics |
| OpenTelemetry (feature flag) | Spans on `run_prefill` / `run_decode` / `handle_logits`; OTLP export; `--features telemetry` |
| Grafana Dashboard Template | `docs/grafana.json` with pre-built dashboard (queue depth, TTFT, KV usage, prefix hit ratio) |
| Published Benchmarks | Results vs Ollama on RTX 3090 / A10G / A100 in `benches/results.md` with README badge |
#### Distribution
| Platform | Feature | Details |
|---|---|---|
| All | Shell install script | `curl -fsSL https://get.ferrumox.dev \| sh`; detects OS/arch, downloads correct binary from GitHub Releases |
| All | GitHub Releases matrix | CI cross-compiles and uploads binaries on every tag: `fox-linux-x86_64`, `fox-linux-aarch64`, `fox-macos-arm64`, `fox-macos-x86_64`, `fox-windows-x86_64.exe` |
| macOS | Homebrew formula | `brew install ferrumox/tap/fox`; universal binary (x86_64 + arm64 via `lipo`); code-signed + notarized for Gatekeeper |
| macOS | Metal backend | `--features metal` in build; `lparams.n_gpu_layers` routed to Metal on Apple Silicon; no CUDA required |
| Windows | WinGet package | `winget install ferrumox.fox`; MSVC-compiled binary; ships with bundled CUDA runtime DLLs (optional) |
| Windows | CPU-only build | Default Windows binary uses CPU; `--features cuda` variant available separately for users with NVIDIA GPUs |
| Linux | `.deb` / `.rpm` packages | Built in CI via `cargo-deb` and `cargo-generate-rpm`; systemd unit file included |
| Linux | AppImage | Single-file portable binary; no root, no package manager required |
#### OOM Recovery — automatic retry on GPU out-of-memory
When llama.cpp fails with `cudaMalloc failed: out of memory` allocating the KV cache, Fox retries automatically instead of returning 503.
**Implementation in `src/model_registry/loader.rs` — retry chain (first success wins):**
| Attempt | context_len | type_kv | KV VRAM (approx.) |
|---------|-------------|---------|-------------------|
| 1 | model default | f16 | baseline |
| 2 | 4096 | f16 | reduced |
| 3 | 2048 | f16 | ~50% |
| 4 | 2048 | q8_0 | ~25% |
| 5 | 1024 | q8_0 | ~12% |
- OOM detection: error string contains `"out of memory"` or `"failed to allocate"`
- On degraded load: `WARN model="…" context=2048 type_kv="q8_0" loaded with reduced settings (OOM recovery)`
- Optional `degraded: Option` field in `EngineEntry` to surface in request logs
- **Phase 2** (lower priority): partial CPU offload — reduce `n_gpu_layers` progressively (all → 32 → 24 → 16) until the model fits; requires exposing `n_gpu_layers` in `LlamaCppModel::load`
- **Phase 3** (lower priority): read free VRAM before attempting load (nvml / sysfs) and skip directly to a reduced config when the model clearly won't fit
#### Developer Experience
| Feature | Details |
|---|---|
| CORS middleware | `tower-http::cors` by default (`Access-Control-Allow-Origin: *`); `--cors-origins` flag to restrict; enables browser-native clients without proxy |
| Port auto-detection | `--port auto`: scans range 8080–8180, falls back to OS ephemeral; `$FOX_PORT=auto` supported |
| VRAM estimation on load | Before loading a model, estimate memory requirement (`file_size × 1.8`); warn if available VRAM is tight; recommend `--n-gpu-layers` reduction or MoE offload if >threshold |
| `fox probe` command | Load a model + optional LoRA, run a dummy forward pass, report success/failure and load time; useful for CI/CD validation before deploying |
| `fox gpu-info` command | Display detected GPU backend, VRAM total/available, driver version; useful for debugging hardware configuration |
| GPU backend runtime flag | `--gpu-backend auto\|cuda\|vulkan\|opencl\|cpu`; auto-detect chain: CUDA → Vulkan → OpenCL → CPU; overrides build-time defaults |
**Key files:** `.github/workflows/release.yml`, `build.rs`, `Cargo.toml` (features), `Formula/fox.rb`, `src/cli/serve.rs`, `src/cli/mod.rs`
**Prerequisite:** none
**Success criterion:** ferrumox >2x throughput vs Ollama on 7B (RTX 3090), visible in CI; `brew install fox` works on a fresh M-series Mac; Windows binary runs without admin rights
---
### v1.2.0 — "Compute Acceleration"
**Theme:** Maximize tokens/sec on existing hardware. Closes Phase 2 pending items.
| Feature | Details |
|---|---|
| Flash Attention | `lparams.flash_attn = true` in `LlamaCppModel::load`; `--flash-attn` flag in `ServeArgs` |
| CUDA Graphs | `lparams.use_cuda_graphs = true`; expose in `RegistryConfig`; only effective on CUDA |
| Full CPU↔GPU Swap | C glue in `vendor/llama.cpp/src/ferrum_kv_transfer.cpp` with `ggml_backend_tensor_get/set`; called from `InferenceEngine::run_decode` |
| Apple Silicon — Unified Memory | Detect `ggml_backend_metal_*`; skip CPU↔GPU swap path entirely (memory is already shared); report unified memory pool in `GET /metrics` |
| Additional Samplers | min-p, mirostat v2, presence penalty, frequency penalty (separate from repetition penalty, full OpenAI spec) in `SamplingParams` and `ChatCompletionRequest` |
| MoE CPU Offloading | `--moe-cpu` flag: offload all expert tensors to CPU (~80% VRAM reduction for Mixtral/DeepSeek-MoE/Qwen-MoE); `--n-moe-cpu N` for partial offload of first N expert layers; configure via llama.cpp `n_gpu_layers` expert-layer logic |
| `fox convert` CLI | Wraps `llama-convert` for AWQ/GPTQ → GGUF; access to quantized HF models without a GGUF |
**Key files:** `src/engine/model.rs`, `src/cli/serve.rs`, `src/scheduler/mod.rs`, `vendor/llama.cpp/`
**Prerequisite:** CI benchmark gate from v1.1.0 (to measure impact of each change)
**Success criterion:** >3000 tok/s on 7B (A100), TTFT P95 <100ms; Mixtral 8x7B runs on 8GB VRAM with `--moe-cpu`
---
### v1.3.0 — "MCP + Ecosystem"
**Theme:** Make ferrumox the easiest LLM server to integrate. MCP is the highest-adoption-impact feature in the roadmap.
| Feature | Details |
|---|---|
| MCP Server | `fox mcp` subcommand; stdio transport (IDEs) + SSE on `/mcp` (axum); tools: `generate`, `chat`, `embed`; resource: model list |
| Web UI | SPA embedded at `/ui`; model selector, streaming chat, system prompt editor, sampling sliders |
| Rate Limiting per API Key | Token bucket per key in `AppState`; `--rate-limit-rpm N`, `--rate-limit-tpm N`; 429 with `Retry-After` |
| Multi-key Auth | `[[api_keys]]` in config.toml; `ApiKeyConfig` with rate_limit, allowed_models, label |
| Webhook Notifications | `--webhook-url`; POST JSON on pull complete, request finish, error |
| Multi-path Model Discovery | `--model-dirs` accepts multiple paths (`;`-separated); auto-scan `~/.cache/huggingface/hub/` (direct sinergia with `fox pull`), `~/.ollama/models/` (parse manifests + GGUF magic-byte verification), `~/.lmstudio/models/`; max depth 4 levels; dedup by resolved path; filter non-LLM files (audio, vision-only models) |
| `fox discover` command | Explicit discovery refresh: re-scans all configured paths and prints newly found models; useful when models are added without restarting the server |
| Shard detection | Group `model-00001-of-00005.gguf` files as a single logical model entry; display combined size |
| HTTP model management | `POST /api/models/:name/load` — load model on demand without restart; `POST /api/models/:name/unload` — explicit unload; `GET /api/models/:name/status` — loaded/unloaded/loading |
| WebSocket streaming | `GET /ws/generate` — bidirectional WebSocket transport; client sends JSON request, server streams tokens as text frames, closes with `{"done":true}`; same inference pipeline as SSE, different transport; useful for clients behind proxies that block SSE |
| Anthropic API compatibility | `POST /anthropic/v1/messages` — maps Anthropic Messages format to internal engine; supports text content blocks, system prompt, streaming; allows Claude SDK clients to use local models without code changes |
| `fox init` command | Generate deployment scaffolding: Dockerfile, docker-compose.yml, k8s deployment YAML, Railway/Fly.io config; output to stdout or directory |
**Key files:** `src/api/routes.rs` (AppState), `src/api/auth.rs`, new `src/mcp/`, `src/cli/mod.rs`, `src/cli/discover.rs`
**Prerequisite:** stable v1.2.0 baseline
**Success criterion:** ferrumox listed as compatible MCP server in Cursor/Continue.dev; Web UI functional without extra configuration; models in HF cache appear in `/v1/models` automatically
---
### v1.4.0 — "Vision & Structured Output"
**Theme:** Expand supported model types. Vision models are the most-requested feature after text LLMs.
| Feature | Details |
|---|---|
| Vision Models (LLaVA / Qwen-VL) | Extend `Model` trait with `encode_image`; preprocessing with `image` crate; `content: Vec` in `ChatMessage` for OpenAI vision format |
| JSON Schema Constrained Output | `response_format: {"type": "json_schema", "json_schema": {...}}`; JSON Schema → GBNF grammar → llama.cpp sampler |
| Reranker API | `POST /v1/rerank` (Cohere-compatible); cross-encoder via embeddings path |
| RoPE Scaling | `--rope-freq-base`, `--rope-freq-scale` in `ServeArgs`; enables 32K–128K context on compatible models |
**Key files:** `src/engine/model.rs` (trait), `src/api/types.rs` (ChatMessage), `src/api/routes.rs`
**Prerequisite:** llama.cpp multimodal API audit (clip model loading)
**Success criterion:** LLaVA-1.6 answering image questions; JSON Schema output passing test suite
---
### v2.0.0 — "Scale-Out"
**Theme:** Multi-GPU and distributed inference. Major version bump due to invasive changes in `EngineEntry` and the scheduler.
| Feature | Details |
|---|---|
| Tensor Parallelism | `EngineEntry.gpu_devices: Vec`; `--tensor-parallel-size N`; sharding via llama.cpp multi-device |
| Speculative Decoding | `InferenceEngine.draft_engine: Option>`; `--speculative-model`; K-token draft + verify in one forward pass |
| Prefill/Decode Disaggregation (experimental) | `--prefill-only` / `--decode-only` modes; KV transfer protocol via gRPC |
| Persistent KV Cache | Serialize prefix cache entries to disk (`~/.cache/ferrumox/kv/`); reload on restart |
| Request Prioritization | `priority: u8` field in requests; `X-Fox-Priority` header; weighted admission in scheduler |
**Key files:** `src/model_registry.rs`, `src/scheduler/mod.rs`, `src/engine/mod.rs`
**Prerequisite:** full CPU-GPU swap from v1.2.0; multi-GPU hardware for testing
**Success criterion:** >5000 tok/s on 7B (A100), TTFT P95 <50ms, 256 concurrent requests
---
### v2.1.0 — "Alternative Backends"
**Theme:** Reduce dependency on llama.cpp; new deployment targets.
| Feature | Details |
|---|---|
| candle Backend (Pure Rust) | Implement `Model` trait with `candle`; `--backend candle`; `--features candle-backend`; Llama 3 + Mistral first |
| Safetensors Loading | `fox pull` downloads safetensors when `--backend candle`; eliminates GGUF conversion step |
| WebGPU Backend (experimental) | `wgpu` compute shaders; works on DirectX 12 / Vulkan / Metal without CUDA drivers; primary path for Windows CPU-GPU users |
| CoreML Backend (Apple) | `--backend coreml`; `--features coreml`; model converted via `coremltools`; runs on ANE (Neural Engine) on M-series chips; dramatically lower power draw for small models |
| ONNX Runtime | `ort` bindings; serve fine-tuned models with PEFT/LoRA exported to ONNX |
**Key files:** `src/engine/model.rs` (stable trait boundary), new `src/engine/candle_model.rs`
**Prerequisite:** stable `Model` trait (no breaking changes since v1.x)
**Success criterion:** Llama 3.1 8B via candle within 20% of llama.cpp throughput
---
### v2.2.0 — "Platform & Plugin System"
**Theme:** Extensibility and enterprise features.
| Feature | Details |
|---|---|
| Plugin System (Samplers) | `SamplerPlugin` trait; dynamic `.so/.dll` loading; C ABI for cross-language compatibility |
| LoRA Adapter Hot-Loading | `llama_lora_adapter_init`; `--lora-path`; `lora_adapter` field in requests; multiple simultaneous adapters |
| LoRA Auto-Detection | During model discovery, scan same directory for `*lora*` / `*adapter*` files matching the model stem; auto-associate without explicit `--lora-path`; surface in `/v1/models` metadata |
| Response Cache | Middleware layer between router and engine; key: `xxhash3_64(model + prompt + max_tokens + temperature + top_p + stop)`; LRU eviction at 1000 entries / 512MB; TTL 1h; enabled only when `temperature == 0.0` by default (deterministic mode); stats in `/metrics`: `cache_hits_total`, `cache_misses_total`, `cache_size_bytes` |
| Batch Inference API | `POST /v1/batch`; array of requests → array of responses; concurrent scheduler dispatch |
| Admin API | `GET/POST /admin/models`, `GET /admin/config`; separate `--admin-key` |
| Helm Chart | Official chart; HPA based on `ferrumox_queue_depth`; graceful shutdown with drain timeout |
**Key files:** `src/api/routes.rs`, `src/engine/model.rs`, new `charts/ferrumox/`, new `src/cache/`
**Success criterion:** watermarking plugin example documented; LoRA with 5+ simultaneous adapters; Helm chart in public repo; cache hit rate >80% on repeated identical prompts with `temperature=0`
---
## Dependency Graph
```
v1.1.0 (Benchmarks & Visibility)
│
└── CI gate required before v1.2.0
v1.2.0 (Compute Acceleration)
│
├── v1.3.0 (MCP + Ecosystem) ─── can advance in parallel
└── v1.4.0 (Vision) ─── can advance in parallel
│
v2.0.0 (Scale-Out)
│
v2.1.0 (Alt Backends)
│
v2.2.0 (Platform)
```
---
## Current architecture (v1.0.0)
```
┌─────────────────────────────────────────────────────┐
│ Client │
│ (curl, OpenAI SDK, LangChain, Ollama CLI...) │
└──────────────────────┬──────────────────────────────┘
│ HTTP / SSE / NDJSON
┌──────────────────────▼──────────────────────────────┐
│ API Layer (axum) │
│ OpenAI: /v1/chat/completions /v1/completions │
│ /v1/embeddings /v1/models │
│ Ollama: /api/generate /api/chat /api/pull │
│ /api/tags /api/ps /api/show │
│ System: /health /metrics /api/version │
│ Auth: API key middleware │
└──────────────────────┬──────────────────────────────┘
│
┌──────────────────────▼──────────────────────────────┐
│ Model Registry (DashMap + LRU) │
│ ModelRegistry · RegistryConfig │
│ get_or_load · LRU eviction · keep-alive task │
│ Alias resolution: alias → stem → prefix → contains │
└───────────┬──────────────────────┬──────────────────┘
│ │
┌───────────▼──────────┐ ┌────────▼─────────────────┐
│ KV Cache Manager │ │ Inference Engine │
│ PageTable per req │ │ prefill() + decode() │
│ ref_count / block │ │ stop sequences │
│ copy_on_write │ │ prefix KV copy │
│ block-level hash │ │ function calling │
└──────────────────────┘ │ structured output │
└────────┬─────────────────┘
│
┌──────────▼──────────────────┐
│ Model Backend │
│ llama.cpp FFI (GGUF) │
└──────────┬──────────────────┘
│
┌──────────▼──────────────────┐
│ GPU / CPU │
│ CUDA · CPU-only │
└─────────────────────────────┘
```
---
## Success metrics
| Metric | v1.0.0 (current) | v1.2.0 | v2.0.0 |
|---|---|---|---|
| Tokens/sec (7B, A100) | >500 | >3 000 | >5 000 |
| TTFT P95 (512 tokens, A100) | <500ms | <100ms | <50ms |
| Concurrent requests | 64 | 64 | 256 |
| Supported model formats | GGUF | GGUF | GGUF + safetensors |
| GitHub stars | — | 500+ | 2 000+ |
---
## CLI commands
```
fox serve # start the HTTP server
# --gpu-backend auto|cuda|vulkan|opencl|cpu (v1.1.0)
# --port auto (v1.1.0)
# --moe-cpu (v1.2.0)
# --n-moe-cpu N (v1.2.0)
# --model-dirs path1;path2;... (v1.3.0)
fox run # single-shot terminal inference
# --show-thinking stream the … block
fox pull # download GGUF model from HuggingFace Hub
fox show # show model info (architecture, quantization, size)
fox probe # load model + optional LoRA, verify forward pass, report errors (v1.1.0)
fox gpu-info # display GPU backend, VRAM total/available, driver version (v1.1.0)
fox discover # refresh model discovery from all configured paths (v1.3.0)
fox init # generate deployment scaffolding: Dockerfile, k8s, Railway, Fly (v1.3.0)
fox mcp # start MCP server (planned v1.3.0)
fox convert # convert AWQ/GPTQ → GGUF (planned v1.2.0)
fox-bench # integrated benchmark
```