Implement ADR-018 (persistent model + stateful sessions) + P0 performance ADRs#10
Merged
Conversation
…estone Record the first-milestone (EPIC 1-4) architectural decisions derived from docs/research/improvements-plan.md, scoped to EdgeIntelligence's edge goals: - ADR-018 persistent model instances and stateful inference sessions - ADR-019 in-loop incremental decoding and real token streaming - ADR-020 batched (single-pass) prefill - ADR-021 memory-mapped verified GGUF loading - ADR-022 two-tier quantized KV cache with attention-aware eviction - ADR-023 baseline performance instrumentation Each ADR is grounded in the current code seams and links to the existing ADR chain (provenance gate, air-gap, decode-time safety loop, memory plan). Adds the source plan and the index table + relationship-graph rows for 018-023.
… sessions Load model weights once and reuse a resident session across turns instead of rebuilding the engine and re-reading the GGUF on every chat (the dominant per-turn cost for a small local model). Verified enabler: candle quantized_qwen2 attention discards/replaces its KV cache on a forward at index_pos == 0, so the engine is reusable without reloading weights. - el-runtime: add required InferenceEngine::reset_cache (release a conversation's KV, keep weights), distinct from rollback. reset()/close() return Result and propagate a reset_cache failure (state untouched on error); close(&mut self) releases the conversation while keeping the model resident and the session reusable; reset() preserves undrained events (turn isolation is the provider's job). Implemented for every engine + test engines. - el-engine-candle: QwenChatProvider holds the QwenEngine in a resident Mutex<ChatSession> (lazy Loaded->Active so builder config is final), reused per turn; the per-turn QwenEngine::from_path disk reload is gone. QwenEngine::reset_cache evicts candle's KV in place via a benign position-0 forward, gated by a cache_dirty flag so a partially-failed eviction is retried, not skipped; prompt/logits buffers are released (Vec::new), not just cleared. QwenChatProvider::end_session exposes the explicit conversation release. - el-chat: /reset, /system, and generation-error recovery release the conversation before reporting success, and stop (dropping the provider, freeing KV via ownership) if it cannot be cleared rather than failing open. Scope: AC-1/AC-2/AC-4 + the engine seam. AC-3 (cross-turn prefix reuse), expert persistence, and concurrent multi-session weight sharing are deferred (see the ADR-018 implementation status). Workspace tests, cargo fmt --check, and clippy -D warnings are green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Authors the first-milestone (EPIC 1–4) ADRs for the edge-inference performance work derived from
docs/research/improvements-plan.md, and implements ADR-018 (persistent model instances + stateful inference sessions).Headline change: the local Qwen chat backend now loads model weights once and reuses a resident session across turns, instead of rebuilding the engine and re-reading the GGUF on every
chat— the dominant per-turn cost for a small on-device model.ADRs (docs)
019–023 are design records only; just 018 is implemented in this PR.
ADR-018 implementation
Verified enabler: candle
quantized_qwen2attention discards/replaces its KV cache on a forward atindex_pos == 0, so the engine is reusable without reloading weights.el-runtime— newInferenceEngine::reset_cache(release a conversation's KV, keep weights), distinct fromrollback.reset()/close()returnResultand propagate failures (state untouched on error);close(&mut self)releases the conversation while keeping the model resident and the session reusable;reset()preserves undrained events (turn isolation is the provider's job).el-engine-candle—QwenChatProviderholds the engine in a residentMutex<ChatSession>(lazyLoaded→Activeso builder config is final), reused per turn — the per-turnQwenEngine::from_pathdisk reload is gone.QwenEngine::reset_cacheevicts candle's KV in place via a benign position-0 forward, gated by acache_dirtyflag so a partially-failed eviction is retried, not skipped; prompt/logits buffers are released (Vec::new), not just cleared.QwenChatProvider::end_session()exposes the explicit conversation release.el-chat—/reset,/system, and generation-error recovery release the conversation before reporting success, and stop (dropping the provider → KV freed via ownership) if it can't be cleared, rather than failing open.Scope
In scope: AC-1 (weights load once), AC-2 (no per-turn reload), AC-4 (conversation memory measurable + explicitly released), and the engine seam.
Deferred (documented in the ADR-018 implementation status): AC-3 cross-turn prefix reuse / incremental prefill; safety-expert persistence; concurrent multi-session weight sharing (candle couples weights + KV cache — same root constraint noted in ADR-022).
Verification
cargo test --workspace— green (el-runtime 24, el-engine-candle 20, 0 failures).cargo fmt --all -- --check— clean.cargo clippy --all-targets -- -D warnings— clean on the changed crates +el-chat.Notes
docs/followups.md) are in a separate open PR (add safety ADRs #9) oncodex/implement-followups; they are intentionally not part of this branch, so the index here lists013 → 018–023.🤖 Generated with Claude Code