* hotswap: keep load-time-derived and transformed tensors coherent after reloads - Re-derive the MLA combined attn_kv_b (computed_wkv_b) in place when a layer's attn_k_b/attn_v_b are hot-swapped: the mla>1 prompt-processing path consumes the derived tensor, so swaps of the source tensors previously had no effect (KLD stayed exactly 0 in per-tensor benchmarks, e.g. GLM-5.2 attn_k_b/v_b). - Refuse (loudly) hot-swaps that cannot be correct: views into -mqkv/-muge merged tensors, khad-folded MLA weights, in-place-scaled ffn_gate_inp_s, BitNet fused scales, OpenPangu parameter-sink sources, and same-dtype swaps of mmap-backed tensors. A refused reload produces no 'reloaded tensor' line, so benchmark drivers quarantine the round instead of recording wrong data. - Propagate reloaded data to same-name duplicate instances (tied lm head copy of token_embd, per-layer rope_freqs/rope_factors copies, expert-bias dups), warning when a duplicate cannot be refreshed. - Warn that derived state stays stale where a refresh is not possible: pre-transposed wk_b_pp under -sm graph/attn, requantized MTP head (output_extra.weight), and k_b/v_b derived from a reloaded attn_kv_b. - Warn at registration time when -rtr is enabled (restores cannot reproduce the run-time-repacked state; F16 -> BF16_R16 is lossy). - server: only attempt the /health hot-swap reload when no slot is processing, and clear the KV cache + cached prompts after a successful reload (they were computed with the previous weights). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Deduplicate llm_compute_wkv_b --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
26 KiB
On-Demand Tensor Reload
Overview
This patch introduces selective tensor hot-swapping for ik_llama.cpp models, now with full support for graph/layer split mode.
It allows individual tensors (or groups of tensors) to be reloaded from their original on-disk GGUF files without tearing down the process, the llama_model, or the llama_context. Tensors may reside on any backend—GPU, CPU, or split across multiple GPUs—and the reload logic preserves that placement.
This is primarily intended for:
- Iterative experimentation and LoRA-like surgical updates.
- Dynamic MoE (Mixture-of-Experts) expert swapping.
- Mixed-quantization perplexity benchmarks, where the bulk of a model lives in one quant (e.g., Q4_X) on GPU while individual experts are hot-swapped one-by-one into a different quant (e.g., IQ1_KT) to measure isolated quality impact.
Motivation
Standard ik_llama.cpp workflows require restarting the entire executable to pick up new weights. For large models distributed across multiple GPUs—or models that spill into CPU memory—this incurs significant downtime. This patch solves that by:
- Tracking provenance: At load time, every tensor is mapped back to its source GGUF shard, byte offset, and modification time.
- Detecting changes: At runtime, it cheaply
stat()s the source files to see if a tensor’s backing data has changed. - Surgical replacement: Only the changed tensors are re-mapped/re-allocated. The rest of the model stays resident in GPU/CPU memory.
- Graph safety: Cached CUDA graphs are invalidated and the context’s cached compute graphs (
ctx->prev/ctx->prev_mtp) are reset so that the next evaluation rebuilds the graph with the new buffer pointers, sizes, or types.
High-Level Architecture
The patch adds a reload_info registry to llama_model (defined in src/llama-reload-info.h). The lifecycle has five phases:
1. Registration Phase (llama_model_load)
During model loading, every weight that is successfully mapped gets an entry in model.reload->tensor_reload_sources only when the environment variable LLAMA_HOTSWAP_ENABLED is set:
struct tensor_reload_source {
std::string path; // Absolute path to the GGUF shard
size_t data_offset; // Byte offset of the tensor data in the file
size_t nbytes; // Current byte size
int64_t last_mtime; // Last modification time (seconds)
int64_t last_mtime_ns; // Nanosecond precision on Linux
// Snapshots of the *original* loaded state so we can reattach later
ggml_backend_buffer_t original_buffer;
void * original_data;
ggml_type original_type;
int64_t original_ne[GGML_MAX_DIMS];
size_t original_nb[GGML_MAX_DIMS];
ggml_split_tensor_t * original_extra;
std::vector<split_info> original_splits;
std::vector<std::string> sibling_names; // MoE siblings
reload_state state;
};
2. Snapshot Phase (snapshot_all_reload_tensors)
The first time a reload is requested, an eager snapshot is taken of every registered tensor and its MoE siblings. This captures the original buffer handles, split descriptors, and strides. This snapshot is essential for:
- Reattachment: If a tensor was detached to a private buffer because it grew, but later shrinks back to its original size/type, it can be reattached to the original shared buffer, avoiding memory fragmentation.
- MoE consistency: MoE layers often have three sibling tensors (
ffn_down_exps,ffn_up_exps,ffn_gate_exps) that must share the same split topology across GPUs.
3. Detection Phase (reload_changed_tensors)
When the user (or the server health-check loop) calls llama_reload_changed_tensors():
- It iterates over the registry and
stat()s each source file. - If
mtime(ormtime_ns) differs, it re-parses the GGUF header (gguf_find_tensor_meta) to get the newoffset,nbytes,ggml_type, and on-disk shape (ne). - Shape verification: If the on-disk dimensions differ from the model tensor (
file_ne[i] != tensor->ne[i]), the tensor is skipped entirely; the reload logic refuses to change logical shapes. - It builds a sorted job list: tensors that are returning to their original snapshot are processed first. This maximizes the chance of freeing private buffers before allocating new ones, reducing memory pressure.
4. Reload Phase (reload_tensor)
For each changed tensor, the patch performs a careful in-place update.
0. Shape Verification
Before any metadata or buffer changes, the code verifies that the on-disk ne[0..3] exactly match the current model tensor. If any dimension differs, the reload is aborted with a log message and the tensor is left untouched.
A. Returning Check
The first decision is whether the tensor's new on-disk type matches its original snapshot type (curr_type == src.original_type).
- Returning to original: The tensor is reattached to its original shared buffer and original split descriptors. Any private buffer allocated during a previous reload is freed (only if the tensor's state is
DETACHEDorFALLBACK_CPU). State becomesON_ORIGINAL. - Changed: Proceed to metadata update and buffer reallocation.
B. Metadata Update & Block-Size Alignment
If the tensor’s ggml_type changed (e.g., Q4_X → IQ1_KT), the main tensor descriptor and all its split descriptors are updated with new type and nb values. The logical shape (ne) is guaranteed unchanged by the preceding shape verification. However, for fused/multi-GPU splits the per-device boundaries must be recalculated.
Critical constraint for fused/multi-GPU splits:
Different quants use different block sizes:
- Q4_X / Q4_0: block size 32
- IQ1_KT: block size 256
When a tensor changes between these types, apply_tensor_type_change() re-rounds every GPU slice’s ne[0] to the nearest multiple of the new block size. If this redistribution is not propagated to all siblings in the same MoE layer, the CUDA split backend dispatches rows to the wrong devices and matmul fails.
C. Buffer Lifecycle
The patch tracks each tensor with a reload_state enum (UNINITIALIZED, ON_ORIGINAL, DETACHED, FALLBACK_CPU). Buffers are only freed if the state is not ON_ORIGINAL, ensuring shared original buffers are never corrupted.
| Scenario | Action |
|---|---|
| Returning to original snapshot | Reattach to original_buffer, restore original splits, free old private buffer if any. |
| Changed type/size while previously on original | Detach from the shared buffer to a newly allocated private buffer so the shared region isn’t corrupted for other tensors. |
| Changed type/size while already detached | Free old private buffer, allocate new one. |
| Allocation fails on target backend | CPU fallback: allocate on ggml_backend_cpu_buffer_type() and clear split metadata. State becomes FALLBACK_CPU. |
D. Split Tensor (Multi-GPU) Handling
For split tensors, the patch:
- Recomputes per-device bounds using the new block-size alignment.
- Reallocates per-device split buffers if necessary.
- Resyncs MoE siblings: If
ffn_down_expschanges its split topology,ffn_up_expsandffn_gate_expsin the same layer are forced to adopt identical per-devicene[0]distributions and strides. This is required by the CUDA split-backend contract.
E. Data Copy
Finally, the tensor bytes are read from the updated file and copied into the (possibly new) backend buffer via ggml_backend_tensor_set.
Hybrid CPU/GPU Inference
When running with --split-mode layer --fit --gpu-layers 99 (or any configuration where the model does not fully fit in VRAM), some tensors naturally land in CPU memory. The hot-swap system fully supports this:
- CPU tensors are reloadable: The reload logic reads the new data from disk and copies it into the CPU backend buffer exactly as it would for CUDA buffers.
- Fallback allocator: If a GPU buffer allocation fails during a reload (e.g., because an IQ1_KT expert is larger than the original Q4_X expert), the system automatically falls back to a CPU buffer for that tensor.
This allows you to keep, for example, 90 % of an MoE model on 13 GPUs while a few large expert tensors cycle through CPU RAM, or to benchmark quants that vary in size per-expert without worrying about exact VRAM fitting.
API & Environment Variables
Public C API
// include/llama.h
LLAMA_API bool llama_reload_changed_tensors(struct llama_context * ctx);
Returns true if at least one tensor was reloaded. When this happens, the function also resets the context’s cached compute graphs (ctx->prev and ctx->prev_mtp) so that the next evaluation performs a full graph rebuild with the new tensor pointers.
Environment Variables
| Variable | Purpose |
|---|---|
LLAMA_HOTSWAP_ENABLED |
Enables the hot-swap loop in perplexity and the health-check hook in server. |
LLAMA_PERPLEXITY_PRE_RELOAD_SCRIPT |
Path to an executable script run between perplexity iterations (e.g., to regenerate/re-quantize a tensor file). Legacy loop mode only. |
LLAMA_HOTSWAP_CONTROL_FILE |
Path to a command file polled by perplexity. Setting it (together with LLAMA_HOTSWAP_ENABLED) switches the tool into signal mode (see below). |
LLAMA_HOTSWAP_STATUS_FILE |
Path to a status file written by perplexity in signal mode. Required when LLAMA_HOTSWAP_CONTROL_FILE is set. |
Integration Points
examples/perplexity/perplexity.cpp (legacy loop mode)
When LLAMA_HOTSWAP_ENABLED is set (and LLAMA_HOTSWAP_CONTROL_FILE is not), the tool runs in a loop:
- Perform an initial
llama_reload_changed_tensors()to apply any pending changes before the first evaluation. - Compute perplexity (or Hellaswag, KL divergence, etc.).
- Print timings and write logs.
- Execute the optional pre-reload script.
- Call
llama_reload_changed_tensors(ctx). If no tensors changed, exit; otherwise repeat from step 2.
examples/perplexity/perplexity.cpp (signal mode)
When both LLAMA_HOTSWAP_ENABLED and LLAMA_HOTSWAP_CONTROL_FILE are set (the latter requires LLAMA_HOTSWAP_STATUS_FILE too), the tool never terminates on its own. Instead, an external driver (e.g. benchmark_each_tensor.sh --hotswap from Thireus' GGUF Tool Suite) swaps GGUF shards on disk and steers the process through two plain files. Files are used instead of POSIX signals so the exact same protocol works on Windows, macOS and Linux.
Status file (written atomically by perplexity via tmp-file + rename, single line):
<state> <iter> <seq>
<state>—computing(an evaluation is running),done(evaluation finished, waiting for a command),reload_failed(areloadcommand found no changed tensor; still waiting),exiting(anexitcommand was accepted).<iter>— 1-based index of the evaluation. It only advances when areloadorcomputecommand is accepted.<seq>— sequence number of the last accepted command (0before any command).
Control file (written by the driver — ideally atomically via tmp-file + rename —, consumed and deleted by perplexity, single line):
<command> <seq>
reload <seq>— callllama_reload_changed_tensors(); on success re-evaluate, on "nothing changed" publishreload_failedand keep waiting.compute <seq>— re-evaluate without reloading (useful when the on-disk state already matches the desired state).exit <seq>— publishexiting, free the model and terminate (aliases:quit,stop).<seq>must increase monotonically; a command whose<seq>is not greater than the last accepted one is deleted and ignored, which makes duplicate delivery harmless. A command without<seq>is always executed (convenient for manual testing:echo reload > ctrl).
Driver loop in a nutshell:
- Swap the shard(s) of the tensor(s) to benchmark, start
llama-perplexitywith the three environment variables, redirecting stdout+stderr to a session log; wait fordone 1. - Capture the per-benchmark log by remembering the session log's byte size before each command and slicing from that offset once
done <iter>appears. - Restore the previous shard(s), swap the next one(s), write
reload <seq>and wait fordone <iter+1>— both the restore and the new swap are applied by the same reload. - When finished, write
exit <seq>.
This mode works with any of the evaluation flavours (perplexity, --kl-divergence, HellaSwag, ...); KL divergence is fully re-entrant since the base-logits file is re-read on every iteration.
Load-time-derived tensors (MLA). llm_prepare_mla derives tensors at load time: when a GGUF ships attn_k_b/attn_v_b but no attn_kv_b, a combined attn_kv_b is computed from them - and the mla>1 prompt-processing path consumes the derived tensor, not the originals. Reloading attn_k_b/attn_v_b therefore also re-derives the affected layer's attn_kv_b in place (llm_refresh_computed_wkv_b), logging hotswap: refreshed derived tensor 'blk.N.attn_kv_b.weight'. If a refresh is impossible (per-rank split tensors under -sm graph/attn, or the reverse derivation where attn_k_b/attn_v_b were computed from a reloaded attn_kv_b), a hotswap: failed to refresh derived tensor ... warning is emitted so external drivers can quarantine the affected evaluation instead of recording stale results.
Other load-time weight transforms (refused or propagated). Several mechanisms transform, merge or fuse weights at load/context-creation time, making a raw-byte reload wrong or ineffective. The reload machinery handles them as follows:
- Refused with a loud
hotswap: tensor '...' cannot be hot-swapped: <reason>warning (reload_tensorreturns false, so drivers see noreloaded tensorline and quarantine the round):- views into merged tensors created by
-mqkv(mergedwqkv) and-muge(mergedffn_gate_up_exps, which is additionally re-interleaved per expert at context creation); attn_k_b/attn_v_b/attn_kv_bwhen the-khadHadamard transform was folded into the MLA weights (khad_pretransformed);ffn_gate_inp_s(scaled in place at load byllm_scale_gate_inp_s);- BitNet
*.scaletensors (fused into the paired weight'sop_paramsat load); - OpenPangu
param_sink*andattn_kv_a_norm(feed load-time-derived parameter sinks); - same-dtype swaps of tensors whose original data is mmap-backed (read-only mapping cannot be refreshed in place; use
--no-mmapfor hot-swap workflows).
- views into merged tensors created by
- Propagated automatically: tensors physically duplicated at load under the same name (tied lm-head copy of
token_embd, per-layerrope_freqs/rope_factorscopies, expert-bias*_dupcopies) - the reloaded data is written to every duplicate instance, or afailed to refresh derived tensorwarning is emitted when that is impossible. - Warned as stale: the pre-transposed
wk_b_ppunder-sm graph/attn, and the runtime-requantized MTP head (output_extra.weight) after anoutput.weightreload. -rtr(run-time repacking): a warning is emitted at registration - restores write the plain file type and cannot reproduce the repacked state (mathematically equivalent for lossless repacks, butF16 -> BF16_R16is lossy), so benchmark results may be inconsistent.
llama-server specifics. The /health hot-swap hook now only attempts a reload when no slot is processing (best effort - a request arriving concurrently can still race it), and after a successful reload it clears the KV cache and all cached prompts, since those were computed with the previous weights. Note that a draft/speculative model and an mtmd projector are separate models with their own files and are not covered by the hot-swap registry.
examples/server/server.cpp
On every health-check (/health) request, if LLAMA_HOTSWAP_ENABLED is set, the server calls llama_reload_changed_tensors(). This provides a convenient, external trigger: simply touch or overwrite a tensor’s source GGUF file and poll /health to apply the change.
MoE Sibling Resync
MoE weights are often stored as three separate tensors that must be split identically across GPUs. The patch automatically detects these families by suffix:
.ffn_down_exps.weight.ffn_up_exps.weight.ffn_gate_exps.weight
When one member of the family is reloaded and its per-device split dimensions change—especially when crossing quant types with different block sizes (Q4_X=32 vs IQ1_KT=256)—resync_moe_sibling_splits() is invoked. The logic follows these steps:
- Fast path: If the reference tensor is returning to its original snapshot, the siblings are also reattached to their original snapshots via
reattach_split_tensor_to_shared()—no data movement is required. - Phase A – Detach: Siblings are detached from shared buffers (freeing only non-original buffers) and new main handles are allocated. Split tensors receive a dummy
datapointer because the split backend usesextra->splits. - Phase B – Propagate dimensions: The reference tensor’s per-device
ne[0]distribution is copied to the siblings, and strides (nb[]) are recomputed using a temporaryggml_context. This step is mandatory because the valid split boundaries depend on the quantization block size. - Phase C – Allocate GPU splits: New per-device GPU buffers are allocated for each sibling split.
- Phase D – CPU fallback (if needed): If any GPU allocation fails, the entire sibling group is moved to CPU buffers to maintain consistency.
- Phase E – Write back: The original sibling data (which has not changed, only the layout) is written back into the new buffers via
ggml_backend_tensor_set.
Buffer Lifecycle Details
Reattachment to Shared Buffers
If a tensor was originally loaded in a large shared GGUF buffer alongside other tensors, and it was previously detached because it grew, the patch attempts to reattach it when it returns to its original size and type. This is done by restoring:
tensor->buffer = original_buffertensor->data = original_datatensor->extra = original_extra(restoring all split descriptors)
This prevents unbounded memory growth during iterative experiments where tensors oscillate between two states.
State Machine
Because ggml does not provide native reference counting on buffers, the patch uses a per-tensor state machine to avoid corrupting shared allocations:
ON_ORIGINAL: The tensor still lives in its initial shared buffer. This buffer is never freed during reload.DETACHED: The tensor was moved to a privately allocated buffer. This buffer is freed before the next reload.FALLBACK_CPU: The tensor was moved to CPU memory after a GPU allocation failure.
Only buffers belonging to tensors in the DETACHED or FALLBACK_CPU states are released, ensuring that shared original buffers remain valid for all other tensors that still reference them.
Limitations & Safety Notes
- File path stability: The source file must remain at the same path. Renaming or removing shards will cause
stat()oropen()to fail. - No locking: There is no file-locking protocol. The user must ensure the GGUF file is not being written to while
ik_llama.cppis reading it. - Graph rebuild cost: While cheaper than a full process restart, rebuilding the CUDA graph (or CPU graph) incurs a one-time latency spike after a reload.
- Platform specifics: Nanosecond mtime checks use
st_mtim.tv_nsecand are guarded by#ifdef __linux__. - Thread safety:
llama_reload_changed_tensorsis not thread-safe with active inference. Ensure the context is idle before calling (the perplexity example naturally guarantees this; the server example only invokes it during the synchronous/healthhandler).
Usage Example: Per-Expert Quantization Sweep (Q4_X ↔ IQ1_KT)
This example benchmarks a massive MoE model where the base weights are Q4_X. The tool iteratively replaces individual ffn_down_exps.weight tensors with IQ1_KT equivalents to measure the isolated perplexity impact of each expert's quantization level.
A sanity check is embedded in the source directory: one of the "IQ1_KT" shard files is actually the original Q4_X tensor. When the rotation reaches that slot, the reloaded tensor is byte-for-byte identical to the baseline, so the PPL must match exactly—confirming that the hot-swap machinery introduces no loss.
1. Helper script (tensor-swap.sh)
Place the rotation script in your model directory (e.g., /opt/THIREUS/Kimi-K2.6/Q4_X/). It maintains .bak files so that each iteration restores the previous tensor before installing the next candidate.
#!/bin/bash
set -euo pipefail
TARGET_GLOB="*Q4_X*gguf"
SOURCE_DIR="../smol-IQ1-KT-mist.bin"
TENSOR_NAME_PATTERN="blk\.[0-9]+\.ffn_down_exps\.weight"
# ... (see full script in patch) ...
The script scans for target files matching *Q4_X*gguf containing blk.[N].ffn_down_exps.weight, then pulls replacements from ../smol-IQ1-KT-mist.bin/ by matching the SPECIAL_TENSOR-NNNN-of-XXXX.gguf shard number.
2. Launch perplexity with hot-swap enabled
ulimit -n 9999
ulimit -l unlimited
export CUDA_VISIBLE_DEVICES="0,1,2,3,4,5,6,7,8,9,10,11,12"
export LLAMA_HOTSWAP_ENABLED=1
export LLAMA_PERPLEXITY_PRE_RELOAD_SCRIPT=./tensor-swap.sh
export LLAMA_DEBUG=1
# --offload-policy -1,off \
GGML_CUDA_NO_PINNED=1 \
/opt/ik_llama.cpp/ik_llama.cpp/build/bin/llama-perplexity \
--chunks 8 \
-f /opt/ik_llama.cpp/wiki.test.raw \
--model /opt/THIREUS/Kimi-K2.6/Q4_X/Kimi-K2.6-THIREUS-Q4_X-SPECIAL_TENSOR-00001-of-01097.gguf \
--alias THIREUS/Kimi-K2.6-Q4_X.bin \
-b 512 -ub 512 \
--ctx-size 512 \
--fit \
--fit-margin 4200 \
--gpu-fit-margin 0,4400,12,4400 \
--temp 0.0 --top-k 0 --top-p 1.0 \
-ctk f16 \
-ctv q8_0 \
-amb 128 \
-mea 128 \
-wgt 1 \
--mlock \
--split-mode layer \
--graph-reduce-type f16 \
--threads $(grep ^cpu\\scores /proc/cpuinfo | uniq | awk '{print $4}' | xargs -I{} echo "{}-0" | bc) \
-sas \
--gpu-layers 99 \
--no-offload-only-active-experts \
--host 0.0.0.0 \
--port 8080 \
--log-enable \
--logdir /var/log/ \
--jinja \
--special \
--prompt-cache "$HOME/.cache/ik_llama.cpp/prompt-cache.bin" --prompt-cache-all \
--slot-save-path "$HOME/.cache/ik_llama.cpp/slot.bin" \
--lookup-cache-dynamic "$HOME/.cache/ik_llama.cpp/slot.bin" \
--keep -1 \
--slot-prompt-similarity 0.35 \
--metrics \
-cuda fusion=1
3. What happens
- The model loads with Q4_X weights distributed across 13 GPUs using layer splitting.
- The first pass computes the baseline perplexity over 8 chunks.
tensor-swap.shruns between iterations:- Restores the previously swapped tensor from
.bakto its original Q4_X state. - Copies the next IQ1_KT expert shard into place.
- Restores the previously swapped tensor from
llama_reload_changed_tensors()detects themtimechanges, re-parses the GGUF headers, and reloads the affectedffn_down_exps.weighttensor(s).- The restored tensor returns to its original Q4_X snapshot and reattaches to its shared buffer.
- The newly swapped tensor is loaded into a private buffer with the new IQ1_KT data.
- Because Q4_X and IQ1_KT have different block sizes (32 vs 256), the split backend redistributes per-device boundaries and resyncs the MoE siblings (
ffn_up_expsandffn_gate_exps) to the same layout.
- The CUDA graphs are invalidated and the next perplexity iteration begins.
- When the rotation hits the sanity-check slot (where the source file is actually the original Q4_X tensor), the perplexity returns to the exact baseline value, confirming the reload is lossless.
4. Expected behavior
snapshot_all_reload_tensors: eager snapshot of all reload tensors + siblings
perplexity: calculating perplexity over 8 chunks, n_ctx=512, batch_size=512, n_seq=1
[1]1.0622,[2]1.2068,[3]1.2327,[4]1.1873,[5]1.1487,[6]1.1283,[7]1.1214,[8]1.1109,
Final estimate: PPL = 1.1109
main: executing pre-reload script: ./tensor-swap.sh
main: [pre-reload] Swapped index 0 (tensor #00918)
reloaded tensor 'blk.1.ffn_down_exps.weight'
perplexity: calculating perplexity over 8 chunks ...
Final estimate: PPL = 1.1105
main: executing pre-reload script: ./tensor-swap.sh
main: [pre-reload] Restored index 0. Advancing to index 1.
main: [pre-reload] Swapped index 1 (tensor #00921)
reloaded tensor 'blk.1.ffn_down_exps.weight'
reloaded tensor 'blk.2.ffn_down_exps.weight'
perplexity: calculating perplexity over 8 chunks ...
Final estimate: PPL = 1.1080
Notice that when the script restores a tensor to its original Q4_X shard, the reload reattaches it to the shared buffer with zero copy. When the sanity-check slot is reached, the PPL returns to the exact baseline, proving the mechanism is sound.
Summary of Changed Files
| File | Change |
|---|---|
examples/perplexity/perplexity.cpp |
Hot-swap loop + pre-reload script execution. |
examples/server/server.cpp |
Trigger reload on /health when env var is set. |
ggml/include/ggml-cuda.h |
Add ggml_backend_cuda_invalidate_graphs(). |
ggml/include/ggml.h |
Conditional GGML_MAX_SRC override. |
ggml/src/CMakeLists.txt |
Propagate GGML_MAX_SRC compile definition. |
ggml/src/ggml-cuda.cu |
Implement graph invalidation; debug prints for split tensors. |
ggml/src/ggml.c |
Debug print in ggml_mul_mat_id for shape mismatches. |
include/llama.h |
Declare llama_reload_changed_tensors(). |
src/llama-mmap.cpp/h |
Expose llama_file::get_path() so reload registry knows the source file path. |
src/llama-model.h |
Add std::unique_ptr<reload_info> reload to llama_model. |
src/llama-reload-info.h |
New. Defines tensor_reload_source, reload_state, and reload_info registry. |
src/llama-reload.cpp |
New. Core implementation: GGUF header parser, snapshot, reload, MoE resync, buffer management, CPU fallback, shape verification. |
src/llama.cpp |
Wire reload registry into llama_model_load; reset cached compute graphs (ctx->prev / ctx->prev_mtp) on reload; export C API. |
src/CMakeLists.txt |
Propagate GGML_MAX_SRC compile definition. |