Skip to content

Commit f9df669

Browse files
feat(engine): Kakeya Inference Engine v1 core (chunked restoration prefill + bounded-KV decode + peak-window admission)
Architecture design doc (docs/design/kakeya-inference-engine-architecture.md, product terms only) + implementation: - inference_engine/engine/admission.py: bounded-KV cost model + peak-window admission (pure; 9 unit tests). - inference_engine/engine/kakeya_engine.py: KakeyaEngine runtime, NativeHybridBounded policy (chunked restoration prefill via prefill_chunk_size + sliding-window-bounded cache; bounded-KV decode), per ADR 0015. - scripts/eval/kakeya_engine_throughput_eval.py: long-context concurrency/throughput evaluation driver. Co-authored-by: FluffyAIcode <FluffyAIcode@users.noreply.github.com>
1 parent 5efa19d commit f9df669

7 files changed

Lines changed: 634 additions & 0 deletions

File tree

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Kakeya Inference Engine Architecture Design
2+
3+
Status: Accepted (v1 core); Date: 2026-06-15. Governing decision: [ADR 0015](../adr/0015-kakeya-attention-and-engine-substrate.md).
4+
5+
## 1. Purpose
6+
7+
The Kakeya Inference Engine is a **product-grade LLM inference engine whose goal
8+
is to replace vLLM**. Its native, first-class attention algorithm is **Kakeya
9+
Attention** — sink+window bound + f_θ KV-projection + dLLM-proposer restoration,
10+
as one primitive. The engine is designed **bounded-KV-native**: the full token
11+
history is never resident; evicted context is reconstructed on demand. Every
12+
subsystem (prefill, KV layout, admission, decode) is built around that invariant,
13+
the way vLLM is built around full-KV PagedAttention.
14+
15+
The engine **replaces** vLLM rather than extending it. Of vLLM's three
16+
prefill-engineering pieces:
17+
18+
- **Chunked prefill** → adopted, reinterpreted as **chunked restoration**.
19+
- **FlashAttention** → wrapped and called as a kernel (table stakes).
20+
- **Paged KV****not used** — paging manages a growing full KV that this
21+
engine never holds.
22+
23+
## 2. Core invariant
24+
25+
For a session of logical length `T`, the **resident KV is bounded** and
26+
independent of `T` beyond the exact-layer term:
27+
28+
```
29+
resident_KV(session) = Σ_exact_layers (full T) + Σ_other_layers (sink + window)
30+
```
31+
32+
- **Exact layers** keep full-context KV (the recall-critical layers).
33+
- **All other layers** keep only `sink + window` resident positions; positions
34+
outside that window are **evicted** and, when a query needs them, **restored on
35+
demand** by the restoration policy (§5).
36+
- The full per-layer KV for evicted positions is **never materialized or stored**.
37+
38+
Memory is provisioned for the **peak resident window**, not the conversation
39+
length. This is the structural source of the engine's concurrency advantage over
40+
full-KV engines.
41+
42+
## 3. Subsystems
43+
44+
```
45+
prompt stream token stream
46+
│ ▲
47+
▼ │
48+
┌───────────────────────┐ bounded KV ┌────────────────────┐
49+
│ Chunked Restoration │──────────────▶ │ Bounded-KV Decode │
50+
│ Prefill (§4) │ + restore idx │ Engine (§6) │
51+
└───────────────────────┘ └────────────────────┘
52+
▲ ▲
53+
│ ┌──────────────────┐ │
54+
└────────────│ Peak-Window │──────┘
55+
admit/reject │ Admission (§7) │ schedule cohort
56+
└──────────────────┘
57+
58+
┌──────────────────┐
59+
│ Restoration │ (§5: native-hybrid | f_θ)
60+
│ Policy │
61+
└──────────────────┘
62+
```
63+
64+
## 4. Chunked Restoration Prefill
65+
66+
**Interface**
67+
68+
```
69+
prefill(prompt_ids: int[T], policy: RestorationPolicy, chunk: int = 2048)
70+
-> BoundedKVState
71+
```
72+
73+
**Behavior** — consume the prompt in fixed `chunk`-sized blocks. For each block:
74+
75+
1. Run the verifier forward over the block against the in-progress
76+
`BoundedKVState` (the resident KV so far).
77+
2. Emit/update each layer's resident KV per the core invariant (§2): exact layers
78+
accumulate full; other layers retain only `sink + window`.
79+
3. The restoration policy (§5) supplies the K/V for any evicted positions a block
80+
needs to attend to (for full-attention models); on hybrid models with native
81+
sliding the block simply does not attend beyond its window.
82+
83+
**Invariants**
84+
85+
- Per-block working memory is **O(N · chunk · cache_len)** for the mask/activation
86+
— never **O(N · T²)**. Memory is decoupled from total prompt length.
87+
- The LM head is evaluated only where needed (last position for next-token), never
88+
as a full `[N, T, vocab]` tensor.
89+
- No `[·, ·, T, T]` attention mask is ever materialized — attention is computed by
90+
the wrapped flash kernel (§6) with window/causal as kernel parameters.
91+
92+
## 5. Restoration Policy
93+
94+
A pluggable policy supplies the K/V for evicted positions, keeping the rest of the
95+
engine model-agnostic:
96+
97+
| Policy | Model class | Mechanism | Restoration cost |
98+
| --- | --- | --- | --- |
99+
| **NativeHybridBounded** | hybrid-attention (e.g. Gemma-4: full + sliding layers) | exact full-attn layers carry recall; other layers are natively local → no reconstruction needed | none (free) |
100+
| **FThetaRestored** | full-attention (e.g. Qwen/Llama) | dLLM proposer produces transient K/V over history; f_θ projects to verifier K/V at evicted positions | one proposer forward |
101+
102+
The policy decides **where recall comes from** and **whether reconstruction
103+
runs**. The bounded-KV layout, prefill chunking, admission, and decode are
104+
identical across policies.
105+
106+
## 6. Bounded-KV Decode Engine
107+
108+
**Interface**
109+
110+
```
111+
decode(cohort: list[BoundedKVState], max_new_tokens) -> list[token[]]
112+
```
113+
114+
**Behavior** — decode an admitted cohort in one batched step per token. Each
115+
session is one batch row over its bounded KV. Attention is the **wrapped flash
116+
kernel** with the Kakeya window / exact-layer-full as kernel parameters; the
117+
decode step is **graph-capturable** (static shapes per cohort). The resident KV
118+
grows only within the bound (sink+window for non-exact layers).
119+
120+
**Invariants**
121+
122+
- Decode-step memory is bounded by the cohort's resident KV (§2), not by total
123+
generated length beyond the exact-layer term.
124+
- No full-KV cache and no paged store: the resident set is the only KV that exists.
125+
126+
## 7. Peak-Window Admission
127+
128+
**Interface**
129+
130+
```
131+
admit(memory_budget_bytes, model_bytes, sessions) -> admitted_cohort
132+
```
133+
134+
**Behavior** — a session's cost is its **bounded resident KV** (§2), computed from
135+
the model's layer layout and the engine's `(sink, window)`**not** its token
136+
count. Max concurrency:
137+
138+
```
139+
max_concurrent = (memory_budget - model_bytes) // resident_KV_per_session
140+
```
141+
142+
Admission is by **peak window**, so concurrency does **not** degrade as
143+
conversations lengthen — the defining difference from full-KV admission, where a
144+
session's cost grows with its history and concurrency collapses at long context.
145+
146+
## 8. Where the engine wins vs vLLM
147+
148+
The advantage scales with the model's **full-attention fraction** (the exact-layer
149+
term in §2):
150+
151+
- **Hybrid models** (Gemma-4, 25/30 natively sliding): vLLM already bounds the
152+
sliding layers and the 5 full-attention layers dominate KV in both engines →
153+
the engine is **competitive** (it removes the O(N·T²) prefill cost and provisions
154+
by peak window) but has no large structural KV edge.
155+
- **Full-attention models** (Qwen/Llama, no native sliding): vLLM must keep **all**
156+
layers' full KV; the Kakeya engine keeps only `exact + sink + window` and
157+
restores the rest via f_θ+proposer → a **large** resident-KV edge → it admits
158+
many more concurrent long-context sessions. **This is the engine's target
159+
regime.**
160+
161+
## 9. v1 scope and sequencing
162+
163+
- **v1 (this design):** Chunked Restoration Prefill + Bounded-KV Decode +
164+
Peak-Window Admission, with the **NativeHybridBounded** policy (Gemma-4) and the
165+
**FThetaRestored** policy interface. Flash kernel wrapped via SDPA/FlashAttention.
166+
- **v1.1:** graph-captured decode + fused-MoE kernels.
167+
- **v1.2:** FThetaRestored policy fully wired for a full-attention verifier — the
168+
configuration that demonstrates the decisive concurrency win over vLLM.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Kakeya Inference Engine (product runtime).
2+
3+
Bounded-KV-native LLM inference engine — the product-grade vLLM replacement
4+
defined in ADR 0015 and `docs/design/kakeya-inference-engine-architecture.md`.
5+
6+
Public surface:
7+
* :mod:`inference_engine.engine.admission` — peak-window admission + the
8+
bounded-KV memory model (pure stdlib; the concurrency math).
9+
* :mod:`inference_engine.engine.kakeya_engine` — the engine runtime
10+
(chunked restoration prefill + bounded-KV decode). Imports torch lazily.
11+
"""
12+
13+
from inference_engine.engine.admission import (
14+
BoundedKVModel,
15+
full_kv_bytes_per_session,
16+
max_concurrent_sessions,
17+
resident_kv_bytes_per_session,
18+
)
19+
20+
__all__ = [
21+
"BoundedKVModel",
22+
"resident_kv_bytes_per_session",
23+
"full_kv_bytes_per_session",
24+
"max_concurrent_sessions",
25+
]
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""Peak-window admission + bounded-KV memory model (§2, §7).
2+
3+
Pure stdlib — this is the concurrency math the engine admits sessions by, and
4+
the model that quantifies the bounded-KV advantage over a full-KV engine. A
5+
session's cost is its **bounded resident KV** (exact layers full + other layers
6+
sink+window), NOT its token count — so concurrency does not degrade as
7+
conversations lengthen.
8+
"""
9+
10+
from __future__ import annotations
11+
12+
from dataclasses import dataclass
13+
from typing import Sequence
14+
15+
16+
@dataclass(frozen=True)
17+
class BoundedKVModel:
18+
"""Per-session bounded-KV cost model for a given model + engine config.
19+
20+
Attributes
21+
----------
22+
num_layers, num_kv_heads, head_dim
23+
Verifier layer layout (KV side).
24+
n_exact_layers
25+
Number of full-context (recall-critical) layers kept exact.
26+
sink, window
27+
Resident sink + sliding window for the non-exact layers.
28+
dtype_bytes
29+
Bytes per KV element (bf16 → 2).
30+
"""
31+
32+
num_layers: int
33+
num_kv_heads: int
34+
head_dim: int
35+
n_exact_layers: int
36+
sink: int
37+
window: int
38+
dtype_bytes: int = 2
39+
40+
def __post_init__(self) -> None:
41+
if self.n_exact_layers > self.num_layers:
42+
raise ValueError("n_exact_layers cannot exceed num_layers")
43+
for name in ("num_layers", "num_kv_heads", "head_dim"):
44+
if getattr(self, name) <= 0:
45+
raise ValueError(f"{name} must be positive")
46+
if self.sink < 0 or self.window <= 0:
47+
raise ValueError("sink must be >=0 and window must be >0")
48+
49+
@property
50+
def per_token_per_layer_bytes(self) -> int:
51+
"""K and V for one token at one layer."""
52+
return 2 * self.num_kv_heads * self.head_dim * self.dtype_bytes
53+
54+
def resident_bytes(self, context_len: int) -> int:
55+
"""Bounded resident KV for a session at logical length ``context_len``."""
56+
if context_len < 0:
57+
raise ValueError("context_len must be >= 0")
58+
n_other = self.num_layers - self.n_exact_layers
59+
exact = self.n_exact_layers * context_len
60+
other = n_other * min(context_len, self.sink + self.window)
61+
return (exact + other) * self.per_token_per_layer_bytes
62+
63+
def full_kv_bytes(self, context_len: int) -> int:
64+
"""Full-KV cost (every layer keeps full context) — the full-attention
65+
engine's per-session cost, for the advantage ratio."""
66+
if context_len < 0:
67+
raise ValueError("context_len must be >= 0")
68+
return self.num_layers * context_len * self.per_token_per_layer_bytes
69+
70+
def advantage_ratio(self, context_len: int) -> float:
71+
"""full-KV / bounded-KV per-session bytes at ``context_len``."""
72+
b = self.resident_bytes(context_len)
73+
return self.full_kv_bytes(context_len) / b if b else float("inf")
74+
75+
76+
def resident_kv_bytes_per_session(model: BoundedKVModel, context_len: int) -> int:
77+
return model.resident_bytes(context_len)
78+
79+
80+
def full_kv_bytes_per_session(model: BoundedKVModel, context_len: int) -> int:
81+
return model.full_kv_bytes(context_len)
82+
83+
84+
def max_concurrent_sessions(
85+
*, memory_budget_bytes: int, model_weight_bytes: int,
86+
per_session_bytes: int,
87+
) -> int:
88+
"""Max sessions that fit: (budget − weights) // per-session KV."""
89+
if per_session_bytes <= 0:
90+
raise ValueError("per_session_bytes must be positive")
91+
free = memory_budget_bytes - model_weight_bytes
92+
if free <= 0:
93+
return 0
94+
return int(free // per_session_bytes)
95+
96+
97+
def exact_layer_indices_for_layer_types(layer_types: Sequence[str]) -> list:
98+
"""Indices of full-attention layers given a hybrid model's layer_types."""
99+
return [i for i, t in enumerate(layer_types)
100+
if "full" in str(t).lower()]

0 commit comments

Comments
 (0)