Plan: lossless long context — derived hot/cold sizing (#5) + host-RAM KV cold store (#3)
Execution plan for requirements #3 and #5 in
effective-context/architecture.md. Scope:modeld/only. The sizing split and append-mode cold KV plumbing now exist for both adapters. The remaining work is making retrieval automatic and proving when restore beats recompute and when longer logical context still helps model quality.
Why #3 and #5 are one piece
- #5 (sizing) splits the budget into a physical hot VRAM budget
(
HotContextTokens) and a larger logical window (PlannerEffectiveContext). - #3 (cold store) is what makes that split lossless: tokens evicted to fit
HotContextTokensare parked in host RAM, not discarded, and paged back when relevant. Without #3 the split is lossy (today’s slide); with #3 it is lossless. - Build order: #5’s derivation → thread the hot budget → #3’s store → retrieval.
#5 — Derived hot/cold sizing
Landed status: capacity.Resolve (modeld/capacity/capacity.go) now
accepts HostColdBudgetBytes. With no host-cold budget it preserves the old dense
behavior; with a budget it keeps EffectiveContext as the dense compatibility
window, keeps HotContextTokens as the physical KV budget, and lets
PlannerEffectiveContext grow from hot + hostColdBudget/KVBytesPerToken (capped by
the request/model ceiling for now). Current shape:
capacity.Resolve:HotContextTokens= physical VRAM KV budget =memoryTokens(already computed:(usable − weights − overhead) / KVBytesPerToken) — the evictionmax_cache_size.PlannerEffectiveContext=HotContextTokens + coldStoreTokens, wherecoldStoreTokens = HostColdBudgetBytes / KVBytesPerToken. Only clamp byModelMaxContextwhen the model truly cannot attend further (sparse attention may exceed the dense ceiling).- Keep
EffectiveContext(dense window served today) for back-compat;PlannerEffectiveContextbecomes what the runtime serves once #3 exists.
- Capacity policy input:
HostColdBudgetBytesis incapacity.Policy/modeld.json(and aMemorySourcefor host RAM), defaulting to a fraction of free system RAM. Derived, not asserted. - Thread the hot budget: sessions evict to
HotContextTokensand servePlannerEffectiveContext.transport.ModelInfocarries both, service code passes them into session config, andDeriveEvictionBudgetuses the hot budget.
Plumbing landed
capacity.Policy.HostColdBudgetBytesviamodeld.json(host_cold_budget/host_cold_budget_bytes),CONTENOX_MODELD_MEM_COLD, andmodeld serve --mem-cold.- Host-cold defaults derive from host RAM (
DefaultHostColdFrac) separately from the hot device memory source. transport.ModelInforeportsHostColdBudgetBytes;transport.ConfigcarriesHotContextTokensandPlannerEffectiveContextinto sessions.- llama/OpenVINO services thread the split into session config; OpenVINO native
CacheEvictionConfigis derived from the hot budget. - Sessions surface hot/planner context in
ExplainContext.
#3 — Host-RAM KV cold store + retrieval
The store holds evicted KV so it can be paged back losslessly. The implementation is backend-specific.
llama — KV-byte offload (append-mode landed)
The shim already exposes per-sequence KV export: StateSeqGetData(seq) /
StateSeqSetData (llama_state_seq_get_data/set_data) plus MemorySeqCopy,
MemorySeqAdd, MemorySeqRemove. So:
- Evict→cold (in
EvictRange):MemorySeqCopy(0→scratch, a, b),StateSeqGetData(scratch)→ bytes, store in a host-RAM cold map keyed by the range’s token-hash + position +CacheClass; then drop the scratch seq and do the existing remove+slide on seq 0. - Cold store:
map[tokenHash]coldBlock{tokens []int; kv []byte; class CacheClass}bounded bycoldStoreTokens; when full, drop byMoreEvictableThan+ LRU (lossy only at the cold tier, and only for the most-evictable classes). - Retrieve→hot (
AdmitRange, real impl):- Append mode (first):
StateSeqSetDatainto a scratch seq,MemorySeqCopyto the tail of seq 0, fix positions withMemorySeqAdd. The block returns as recent context — sidesteps mid-sequence insertion; good for “bring a relevant file back.” - In-place mode (later): shift seq-0 positions up to open a hole, copy the block in at its original position. Preserves structure; harder.
- Append mode (first):
- Why bytes, not recompute: bulk PCIe restore (~25 GB/s) beats re-prefilling a large block on the GPU, so the KV-byte store is a real win for large stable context (repo map / files) scrolling out and back. Token-recompute is the fallback when no bytes are stored.
Current llama status: append-mode cold store is implemented behind
PlannerEffectiveContext > NumCtx. EvictRange exports a block through scratch seq 1,
stores KV bytes plus token/hash/range/cache-class metadata, and then removes/slides as
before. AdmitRange imports that block back through scratch seq 1, shifts it to the
current tail, copies it into seq 0, and appends its tokens. The store is bounded by
PlannerEffectiveContext - NumCtx and evicts cold blocks by cache class plus LRU. It does
not yet do runtime-driven hash matching, in-place insertion, or snapshot serialization of
cold blocks.
llama — sparse attention parity boundary
llama.cpp already implements model-native SWA for GGUFs that declare
*.attention.sliding_window. modeld now:
- parses the GGUF sliding-window metadata in
ggufModelParams; - reports
SparseAttentionandSlidingWindowAttentionTokensfromDescribe; - carries those fields over the gRPC wire;
- reads the loaded model’s
llama_model_n_swa()through the direct shim and surfaces it in live residency capabilities.
This is true parity with what stock llama.cpp can execute. It is not OpenVINO XAttention for dense llama models, and it is not a custom sparse mask over arbitrary retrieved cold blocks.
OpenVINO — KV-byte offload (append-mode landed)
OpenVINO GenAI holds KV as block-structured per-layer ov::Tensors inside the continuous
batching prefix cache. The local bridge now reaches those internals (#define protected public around the GenAI CB headers) and exposes SupportsColdKV, ExportColdKV, and
ImportColdKV through modeld/openvino/ovsession.
Current OpenVINO status:
EvictRangeexports the evicted logical token range into a host cold block, then rebuilds the remaining resident prefix throughPrefillTokens.AdmitRangerestores the saved block at the hot tail by importing into destination prefix-cache blocks. Shifted imports copy values, copy keys, and rotate RoPE-positioned keys by the destination/source position delta.- The bridge advertises cold KV only for exact float KV precisions currently wired through
config (
f16/f32).u8/i8generation remains valid, but cold KV import/export is not advertised there. - System coverage exists for cold-KV capability and shifted import
(
TestSystem_OpenVINOGenAI_ColdKVCapability,TestSystem_OpenVINOGenAI_ShiftedColdKVImport).
Remaining OpenVINO gaps: runtime-driven cold-hit matching, in-place reinsertion, cold-block snapshot serialization, quantized KV restore, and restore-vs-recompute latency policy.
So the backends are not asymmetric in capability for append-mode KV-byte restore: llama uses public llama.cpp state APIs; OpenVINO uses the local GenAI bridge over prefix cache blocks. They still differ in sparse attention: OpenVINO has XAttention/cache eviction, while llama only has model-native SWA unless we build a custom llama attention path.
Who triggers retrieval
Runtime-driven (still open): the agent runtime assembles context and knows relevance. When it
re-includes an evicted segment in EnsurePrefix/PrefillSuffix, modeld matches it
against the cold store by token-hash and restores instead of a cold prefill — extending
the existing commonPrefixLen warm reuse to a cold tier. A modeld-internal attention-score
retriever is a later, optional autonomous mode.
Phased execution (each phase has a local proof point on the 3060 / IR)
- #5 sizing (landed) — diverge
HotContextTokensvsPlannerEffectiveContext+ the host-cold-budget policy input. Proof: capacity unit tests;ModelInforeportsPlannerEffectiveContext > HotContextTokenson a small-VRAM profile. - Thread hot budget (landed) — sessions evict to
HotContextTokens, servePlannerEffectiveContext. Proof: llama physical resident bounded atHotContextTokenswhile generating a longer logical context. - llama cold store (append-mode landed) —
EvictRangeoffloads KV bytes;AdmitRangerestores. Proof: direct llama tag build passes; tail evict→admit has a system test comparing the restored one-token continuation to the pre-eviction reference whenCONTENOX_LLAMA_TINY_GGUFis set. Latency benchmark still pending. - OpenVINO cold store (append-mode landed for f16/f32) —
EvictRangeexports KV bytes;AdmitRangeimports into shifted destination prefix-cache blocks with RoPE key rotation. Proof:make -f Makefile.openvino test-genai, includingTestSystem_OpenVINOGenAI_ShiftedColdKVImport. - llama native-SWA observability (landed) — report GGUF/llama.cpp SWA support and
window size through
Describe, gRPC, and residency capabilities. Proof: GGUF parsing, service describe, gRPC, and direct llama tag tests. - Runtime-driven retrieval — match re-sent segments to the cold store; restore instead of re-prefill. Proof: re-sent evicted segment is a cold hit.
- (optional) in-place retrieval + autonomous attention-score retriever.
Code map
modeld/capacity/capacity.go— hot/cold sizing split;PolicygainsHostColdBudgetBytes.modeld/residency/residency.go— cold-store types + aPlanthat marks hot/cold/retrieve; reuseEvictionBudget,CacheClass.MoreEvictableThan.modeld/llama/llamacppshim/direct.go—StateSeqGetData/SetData,MemorySeqCopy,MemorySeqAdd, plusSlidingWindowAttention()viallama_model_n_swa.modeld/llama/llamasession/llama.go— realEvictRange→cold /AdmitRange←cold + cold-store field.modeld/openvino/ovsession(genai.h/.cpp+ shim) —SupportsColdKV,ExportColdKV,ImportColdKV; destination block composition and RoPE key rotation for shifted imports.runtime/transport/session.go,runtime/transport/grpc/*— sparse attention and sliding-window metadata through local and gRPCDescribe, plus live residency capabilities.modeld/*/service.go— threadHotContextTokens/PlannerEffectiveContext.
Verification (all local)
- Pure: capacity sizing + residency cold-store policy unit tests (
go test ./modeld/...). - llama (CPU + 3060): evict→admit round-trip is lossless (restored continuation == reference); restore latency < re-prefill for a large block.
- OpenVINO (IR/GenAI bridge): evict→restore via
ExportColdKV/ImportColdKVworks for f16/f32 KV; native eviction still bounds VRAM. Quantized cold restore remains open. - No regressions: full
llamasession+ default sweep.
Risks / honest unknowns
- Restore value is real but bounded: PCIe bulk restore beats re-prefill for large
blocks; for small blocks recompute wins. The cold store should prefer big stable
segments (
task_pinned/repo_map), not churnyvolatile— theCacheClassplan gates what earns a KV-byte slot. - In-place retrieval (positional) is the hard part; append-mode is the pragmatic first cut and may suffice for agent workloads.
- OpenVINO byte-offload depends on GenAI internals — the local bridge uses protected continuous-batching internals, so version bumps can break it. That is now a maintenance risk, not an unproven capability.
- llama sparse attention is bounded by stock llama.cpp — native SWA is wired and reported; arbitrary XAttention/retrieved-block sparse masks for dense llama models are not implemented.
- Quality at long effective context stays unproven — even lossless KV doesn’t guarantee the model attends well at ~200k. A long-context quality benchmark (separate lab task) is the final arbiter.