docs: define distributed GGUF runtime plan
This commit is contained in:
@@ -1,177 +1,121 @@
|
||||
# ADR-0020: Distributed GGUF/llama.cpp Runtime With Per-Shard Local KV
|
||||
# ADR-0020: Lean Native Distributed GGUF Runtime
|
||||
|
||||
Status: Proposed
|
||||
Status: Accepted
|
||||
Date: 2026-07-13
|
||||
|
||||
## Context
|
||||
|
||||
The project currently uses PyTorch/Transformers for real model shards. That decision was captured in ADR-0001 because llama.cpp RPC at the time required the primary node to load the full model and distribute weights to workers, which conflicted with the desired model where nodes independently hold shards.
|
||||
The project currently uses Transformers/safetensors as its real model execution backend. This provides broad architecture coverage and a correctness reference, but reported and observed consumer CPU/GPU inference performance motivates evaluating llama.cpp/GGML and quantized GGUF.
|
||||
|
||||
We now want to serve very large open models, including GLM-5.2 and Ornith-class MoE models, over a torrent-like inference marketplace. CPU and mixed consumer hardware matter. LM Studio and llama.cpp demonstrate much better CPU/GGUF performance than our current PyTorch CPU path. The user also has a personal relationship with Georgi Gerganov, making upstream collaboration plausible.
|
||||
The product objective is not merely local GGUF serving. It is performant concurrent inference for top open models whose weights do not fit on one consumer node. The project already owns the Tracker, Inference Route, Route Session, Activation Seam, local Hot KV State, relay/direct transport, cancellation, telemetry, billing, and capability admission.
|
||||
|
||||
The current distributed PyTorch path is not yet production-grade: it recomputes the full growing sequence for every output token and disables KV cache inside manual layer calls. It sends hidden activations across seams, not KV, but those activations currently cover the full sequence every decode step.
|
||||
Research audited llama.cpp RPC, GPUStack/llama-box, Nakshatra, prima.cpp, llama-gguf, LiGGUF, vLLM and its GGUF plugin, Petals, exo, and related projects. No repository provides the complete public-network contract. llama.cpp is the strongest GGUF execution substrate. vLLM has mature managed-cluster parallelism and scheduling concepts but its PP/TP/EP runtime assumes a static trusted distributed world and is unsuitable as the public Shard runtime.
|
||||
|
||||
The project must remain lean and avoid combining several half-integrated inference control planes.
|
||||
|
||||
## Decision
|
||||
|
||||
Adopt a distributed GGUF/llama.cpp runtime track while keeping PyTorch as the reference and fast-architecture backend.
|
||||
### Primary native runtime
|
||||
|
||||
The runtime model is:
|
||||
Use llama.cpp/GGML through one standalone C++ Shard worker and a small exact-commit patch stack.
|
||||
|
||||
- GGUF/model artifacts are distributed through torrent/content-addressed storage.
|
||||
- Nodes independently acquire and verify artifacts; no root node streams model weights to workers at session start.
|
||||
- Tracker chooses a sticky route covering all layers.
|
||||
- Each node owns hot KV/state for the layers it executes.
|
||||
- Prefill sends chunked activations through the route and builds local per-shard KV.
|
||||
- Decode sends one-step activations through the route and appends local KV at every shard.
|
||||
- Cache/CDN servers store cold artifacts and optional prefix/session snapshots, not hot per-token KV.
|
||||
- Context is capped at 128K for the first serious product path.
|
||||
The patch scope is limited to:
|
||||
|
||||
## Technical Framework
|
||||
- Range-aware GGUF tensor ownership/loading.
|
||||
- Architecture-defined intermediate boundary input/output.
|
||||
- Intermediate output before tail normalization/head.
|
||||
- Layer-filtered KV and external session-to-sequence mapping.
|
||||
|
||||
The design separates five planes:
|
||||
Meshnet networking, routing, admission, billing, telemetry, and work evidence stay outside llama.cpp.
|
||||
|
||||
- **Control plane**: tracker registry, coverage map, route selection, session lifecycle, telemetry, billing, and audit.
|
||||
- **Artifact plane**: Shard Swarms, GGUF/safetensors/tokenizer files, manifests, hashes, and local node storage.
|
||||
- **Execution plane**: active Inference Route, chunked prefill, one-step decode, and hidden-state movement across activation seams.
|
||||
- **Session state plane**: per-shard Hot KV State on route nodes, plus optional Prefix Snapshots outside the hot loop.
|
||||
- **Economics/trust plane**: reward accounting, validation events, slash proofs, public/private route policy.
|
||||
Nakshatra, prima.cpp, llama-gguf, LiGGUF, and historical GPUStack are source/test donors only. Their repositories are not runtime dependencies.
|
||||
|
||||
Hard invariants:
|
||||
### Distributed parallelism
|
||||
|
||||
1. Public-network Shards are contiguous layer ranges.
|
||||
2. Hot KV State is local to the node serving that Shard in that Route Session.
|
||||
3. Artifact distribution and route execution are separate systems.
|
||||
4. Decode seam payload must be `O(hidden_size)`.
|
||||
5. Prefill may be `O(sequence_length * hidden_size)`, but only in bounded chunks.
|
||||
6. The tracker chooses routes; nodes do not negotiate route topology peer-to-peer.
|
||||
7. Model/backend-specific cache internals stay behind backend capability reports.
|
||||
8. PyTorch remains the correctness/reference backend while llama.cpp/GGUF becomes the performance backend.
|
||||
9. Streaming responses are preferred when feasible; Generation Telemetry is always required.
|
||||
The first public-network primitive is layer/pipeline parallelism through contiguous Shards in an Inference Route.
|
||||
|
||||
The full challenge register is in [technical-challenges.md](./technical-challenges.md). The open decision gates are in [decision-framework.md](./decision-framework.md).
|
||||
Per-node continuous batching combines decode steps from compatible active Route Sessions. Multiple complete routes provide data parallelism.
|
||||
|
||||
Resolved gate:
|
||||
Tensor and expert parallel collectives may later operate inside one trusted composite node or managed cluster represented as one provider. They are not public WAN routing primitives.
|
||||
|
||||
- Public-network Shards are layer ranges. Tensor-parallel/ring execution belongs inside a trusted node, colocated pod, or future composite node abstraction, not as the v1 public routing primitive.
|
||||
- Hot KV State is local to each route node for the Shard it serves. Cache servers may store Prefix Snapshots, but they are not part of the per-token decode path.
|
||||
- Distributed Route Session and Hot KV State semantics will be proven in the PyTorch route before llama.cpp/GGUF is extended for layer-boundary execution.
|
||||
- Streaming responses are preferred when feasible. Realtime Generation Telemetry is required so clients can see phase, generated token count, and tokens/sec even during prefill or non-streaming fallback paths.
|
||||
- llama.cpp/GGUF work targets upstreamable `libllama`/ggml hooks. A prototype fork is acceptable for exploration, but a permanent fork is not the plan.
|
||||
- Model targeting is two-tiered: use a small llama.cpp-supported GGUF model for the first protocol smoke test, then use `deepseek-ai/DeepSeek-V4-Flash` as the first serious large-model target. GLM-5.2 and Ornith remain later support audits.
|
||||
- Alpha fails Route Sessions on route-node loss instead of attempting automatic route repair. Repair requires compatible Prefix Snapshots and is a later capability.
|
||||
- v1 activation transfer stays on binary HTTP as defined by ADR-0008. QUIC/WebRTC/custom transport can be introduced later behind the same activation protocol.
|
||||
### Transport
|
||||
|
||||
## Non-Goals
|
||||
Use gRPC over HTTP/2 with Protocol Buffers for the native Python/C++ Shard data plane.
|
||||
|
||||
- Do not put remote cache servers in the per-token hot KV path.
|
||||
- Do not require every node to hold the full model.
|
||||
- Do not fork llama.cpp long-term if upstream APIs can support the needed layer-boundary hooks.
|
||||
- Do not target GLM-5.2 or Ornith first; prove the route/KV protocol on a simpler well-supported GGUF model, then target DeepSeek-V4-Flash as the first serious large model.
|
||||
- One long-lived bidirectional stream per Route Session Activation Seam.
|
||||
- Deadlines, cancellation, flow control, TLS/authentication hooks, structured status, and generated schemas.
|
||||
- Bounded chunks for prefill and a small decode fast path.
|
||||
- Existing relay infrastructure may carry the same versioned protobuf frames as opaque binary when direct connectivity is unavailable.
|
||||
- OpenAI client APIs remain HTTP/SSE; existing Tracker APIs remain unchanged.
|
||||
|
||||
## Options Considered
|
||||
The boundary payload is a versioned named-tensor bundle because architecture boundaries may require more than one tensor.
|
||||
|
||||
### A. Keep PyTorch-only distributed inference
|
||||
### vLLM
|
||||
|
||||
Pros:
|
||||
Do not fork vLLM for public distributed Shards and do not transplant PagedAttention, Torch process groups, or the vLLM GGUF plugin into the llama.cpp worker.
|
||||
|
||||
- Easy access to new Hugging Face architectures.
|
||||
- Transformers has mature single-process KV semantics.
|
||||
- Existing code already loads shards.
|
||||
Allow unmodified vLLM as an optional whole-model backend or managed TP/PP/EP cluster represented as one logical provider.
|
||||
|
||||
Cons:
|
||||
Adapt only small control-plane concepts:
|
||||
|
||||
- CPU inference is much slower than llama.cpp/GGUF.
|
||||
- Current distributed path bypasses `generate()` and disables cache.
|
||||
- Quantized GGUF ecosystem and LM Studio users are outside the runtime.
|
||||
- Named intermediate bundles.
|
||||
- Continuous batching and request ownership.
|
||||
- Versioned cache-transfer compatibility fingerprints.
|
||||
- Explicit transfer failure/abort lifecycle.
|
||||
- Load telemetry and fair tie-breaking.
|
||||
|
||||
### B. Use llama.cpp only as a full local model backend
|
||||
### Benchmark gate
|
||||
|
||||
Pros:
|
||||
GGUF performance is a hypothesis. Before expensive native work, compare the current Transformers/safetensors recipe with whole-model llama.cpp on controlled model, hardware, prompt, context, output, sampling, concurrency, memory, and quality lanes.
|
||||
|
||||
- Quick performance win for nodes with enough RAM/VRAM.
|
||||
- Minimal coordination with distributed protocol.
|
||||
Later distributed release gates use thresholds locked before implementation results are known. The native track stops if llama.cpp/GGUF offers neither a meaningful performance benefit nor a meaningful model-fit benefit at useful speed.
|
||||
|
||||
Cons:
|
||||
### Concurrency
|
||||
|
||||
- Does not unlock 397B/753B-class models for ordinary nodes.
|
||||
- Does not solve marketplace layer routing.
|
||||
A native worker must isolate `(Route Session ID, route epoch)` through a llama sequence or bounded context and must not serialize all generations behind one global serving sequence.
|
||||
|
||||
### C. Distributed GGUF with per-shard local KV (chosen)
|
||||
The node admits sessions against weight/KV/scratch budgets, batches compatible decode steps, prevents prefill starvation, applies backpressure, and exposes queue/batch/KV telemetry.
|
||||
|
||||
Pros:
|
||||
### Architecture certification
|
||||
|
||||
- Aligns with torrent artifact distribution.
|
||||
- Avoids root streaming weights to workers.
|
||||
- Uses llama.cpp/GGUF performance where supported.
|
||||
- Compatible with public node rewards by layer/work contribution.
|
||||
- Scales KV memory by layer range.
|
||||
Dense Llama-family is first. Qwen3/Qwen3-MoE is a separate explicit adapter. Every architecture/backend/recipe remains registered-but-dark until a real distributed forward, parity test, concurrency test, and capability admission pass.
|
||||
|
||||
Cons:
|
||||
## Alternatives rejected
|
||||
|
||||
- Requires new runtime APIs around layer-boundary hidden states and per-session KV.
|
||||
- Requires model-specific cache metadata for DSA/MLA/hybrid attention.
|
||||
- Harder to debug than single-process `generate()`.
|
||||
### Fork vLLM for the public mesh
|
||||
|
||||
### D. Centralized KV cache servers
|
||||
Rejected because extracting its PP/TP/EP stages requires replacing static process groups, rank lifecycle, scheduler, request ownership, cache layout, failure behavior, and hardware assumptions. This would create a large difficult fork while discarding much of vLLM's core architecture.
|
||||
|
||||
Pros:
|
||||
### llama.cpp RPC as the public protocol
|
||||
|
||||
- Easier apparent session failover.
|
||||
- Central accounting of active cache.
|
||||
Rejected because it exposes coordinator-owned raw GGML devices, not independent Shards. Its trust, security, failure, cache, and per-node accounting model is unsuitable for arbitrary volunteer nodes.
|
||||
|
||||
Cons:
|
||||
### Adopt Nakshatra or prima.cpp wholesale
|
||||
|
||||
- Puts remote storage in the per-token hot path.
|
||||
- Adds bandwidth and latency at the worst possible point.
|
||||
- Creates consistency and privacy problems.
|
||||
Rejected because their repositories, build reproducibility, session/concurrency semantics, architecture coverage, protocol identity, and control planes do not satisfy the project contract. Their partial-loading and boundary work remains valuable evidence.
|
||||
|
||||
Rejected for hot decode. Accepted only for cold prefix snapshots and failover checkpoints.
|
||||
### Build a custom GGUF engine
|
||||
|
||||
Rejected because llama.cpp already provides the parser, kernels, architecture graphs, KV, tokenizer, and heterogeneous backends. Reimplementing these would spread effort and increase correctness risk.
|
||||
|
||||
### Invent a custom transport
|
||||
|
||||
Rejected. gRPC/HTTP2 already provides mature streaming, flow control, deadlines, cancellation, TLS, and cross-language schema generation.
|
||||
|
||||
## Consequences
|
||||
|
||||
- ADR-0001 should eventually be amended: PyTorch remains valid, but llama.cpp/GGUF becomes a first-class backend.
|
||||
- The activation protocol must split prefill and decode explicitly.
|
||||
- Session IDs must be stable across the full request. The current fresh UUID-per-hop-call behavior must change.
|
||||
- Backends must report cache budget and cache compatibility.
|
||||
- Tracker route selection must include disk, memory pressure, cache warmth, and network latency.
|
||||
- Billing can be based on layer work, prefill tokens, decode tokens, and observed route participation.
|
||||
- Client UX should stream token deltas when feasible and must include route-session progress telemetry even when token deltas are not streamed.
|
||||
- The critical path contains Meshnet, one standalone worker, and one small pinned llama.cpp patch stack.
|
||||
- Transformers/safetensors remains the correctness reference and fallback for unsupported architectures.
|
||||
- Whole-model llama.cpp and vLLM managed clusters remain useful optional provider types.
|
||||
- The first milestone emphasizes controlled benchmark, parity, concurrent KV, and real two-machine evidence rather than a large-model demo.
|
||||
- Upstream collaboration with llama.cpp targets generic local hooks only; the project remains able to ship a narrow pinned fork if upstream acceptance takes time.
|
||||
- QUIC, public tensor parallelism, disaggregated prefill, speculative decode, route repair, and KV migration remain deferred until the core route passes release gates.
|
||||
|
||||
## Required Runtime Capabilities
|
||||
## Verification gates
|
||||
|
||||
PyTorch path:
|
||||
|
||||
- manual layer calls with `past_key_values` / model-specific cache object
|
||||
- per-shard session cache store
|
||||
- prefill chunk append
|
||||
- decode step append
|
||||
- stable session lifecycle endpoints
|
||||
|
||||
llama.cpp/GGUF path:
|
||||
|
||||
- full local GGUF serving
|
||||
- layer/tensor map extraction from GGUF
|
||||
- optional partial layer loading or mmap-backed selected execution
|
||||
- inbound hidden-state execution from arbitrary start layer
|
||||
- outbound hidden-state return at stop layer
|
||||
- per-session KV ownership for loaded layers
|
||||
- cache budget/compatibility introspection
|
||||
- GLM-5.2 DSA support when upstream/runtime supports it
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
1. Add full-model `LlamaCppBackend` using `llama-server` or `libllama`.
|
||||
2. Implement distributed KV in the PyTorch path to prove semantics.
|
||||
3. Add session lifecycle and prefill/decode wire protocol.
|
||||
4. Add model artifact manifest and torrent seeding metadata.
|
||||
5. Prototype localhost two-process llama.cpp layer boundary execution.
|
||||
6. Generalize to network route.
|
||||
7. Bring in GLM-5.2/Ornith once backend support and cache accounting are verified.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- A two-node localhost route can prefill once and decode N tokens without recomputing the full prompt.
|
||||
- Seam payload during decode is `O(hidden_size)`, not `O(sequence_length * hidden_size)`.
|
||||
- Per-node KV memory grows with owned layer count and context length.
|
||||
- Route loss during alpha fails cleanly with explicit reason.
|
||||
- Full local GGUF backend outperforms PyTorch CPU on a supported model.
|
||||
- Artifact manifest can identify exactly which files/chunks a node must seed for its advertised layer range.
|
||||
1. Controlled safetensors-versus-GGUF performance contract.
|
||||
2. Two-process local range parity.
|
||||
3. Four-session concurrent KV isolation.
|
||||
4. Real two-machine execution using both Shards.
|
||||
5. End-to-end performance/fit advantage over the current distributed route.
|
||||
6. Separate Qwen3-family architecture certification.
|
||||
|
||||
Reference in New Issue
Block a user