Repository navigation
feat(ruvllm): load PEFT LoRA adapters through LlmBackend - #1203
Merged
Merged
Conversation
Moves the optional lattice dependencies from 0.6.1 to 0.11.1 in ruvector-core and ruvllm, with the lockfile resolved for the new versions (lattice-fann 0.11.1 enters through the metal-gpu feature). lattice-inference 0.11 exports GenerateConfig at the crate root and marks it non_exhaustive, so the backend now imports it from there and builds it from its default with the mapped fields assigned one by one. Refs ruvnet#1196
Add load_lora_adapter, unload_lora_adapter and active_lora_adapter to LlmBackend. They return ModelAdapterError, so RuvLLMError is unchanged, and the default implementations return a typed unsupported error, so other backends compile unchanged. The macOS Lattice backend validates a PEFT SafeTensors adapter against the loaded Qwen 3.5 model and installs it through the runtime's adapter hot-swap without reloading the model. A failed load keeps the previous adapter, and unloading restores base-model generation. Refs ruvnet#1191
Owner
This was referenced Oct 24, 2026
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
This implements steps 1 to 3 of #1191: generation adapters on
LlmBackend, with the macOS Lattice backend as the first implementation.LlmBackendgainsload_lora_adapter(path, scale),unload_lora_adapter()andactive_lora_adapter(), plus abackends::AdapterInforecord. The three methods returnResult<_, ModelAdapterError>, soRuvLLMErroris unchanged. The default implementations returnModelAdapterError::UnsupportedBackend, so the other backends and any downstream implementors compile unchanged and report the operation as unsupported.latticefeature, macOS). The backend reads a PEFT SafeTensors adapter (file or directory) withadapter_config.jsonbeside it. It validates it against the loaded Qwen 3.5 model, then installs it through the runtime's adapter hot-swap, with no model reload. The scale multiplies the adapter'salpha / rank. Supported: uniform-rank LoRA on the full-attentionq_proj,k_proj,v_proj,o_projand the dense MLPgate_proj,up_proj,down_proj. GDN and MoE layers, DoRA, rank-stabilized LoRA, saved bias or modules, and non-uniform rank or alpha patterns are rejected with a typed error.config.jsonis also read for adapter validation. An error from that read is reported only when an adapter is loaded;load_modelreports only the base loader's own errors.ModelInfo::namekeeps the path string the caller passed.kasa_config,lora_ga_config,use_bdloraand others, all null or false) and often a nulltask_type. Unknown fields are accepted only when null, false, an empty object or an empty array.peft_versionaccepts any string, andtask_typemay be absent, null orCAUSAL_LM. Base-model identity is reported inAdapterInfo::declared_base_model/declared_revisionrather than enforced, so callers can apply their own policy.base_model.model.model.layers.*), that a negative scale is accepted and inverts the adapter's update, and that adapter factors are kept on the host as f32 (about twice their on-disk size for F16 or BF16 factors, plus a temporary copy during installation). It also states that MicroLoRA and SONA operate on supplied embeddings rather than on generation.[Unreleased]/Addedentry.Depends on #1198
The first two commits are #1198 (lattice-inference 0.6.1 to 0.11.1, which provides the runtime adapter API, and its lockfile follow-up). Once #1198 merges, this pull request reduces to the last commit.
Checks
macOS (Apple silicon, Metal, rustc 1.93.0),
--features lattice:cargo check --locked -p ruvllm@2.5.0 --features lattice --all-targetscargo clippy --locked -p ruvllm@2.5.0 --features lattice --all-targets -- -W warningscargo test --locked -p ruvllm@2.5.0 --features lattice --lib validation_tests:: -- --test-threads=1cargo test --locked -p ruvllm@2.5.0 --features lattice --lib backends::lattice_backend::adapter_tests:: -- --test-threads=1cargo test --locked -p ruvllm@2.5.0 --features lattice --lib backends::tests:: -- --test-threads=1cargo test --locked -p ruvllm@2.5.0 --features lattice --lib backends::lattice_backend:: -- --test-threads=1The six
adapter_testsrun a small synthetic Qwen 3.5 model on Metal. They check that an adapter ondown_proj, on the gatedq_projand ongate_projeach changes the logits and that unloading restores the base logits bit-for-bit; that a failed load leaves the base model and the active adapter unchanged; that a base model whoseconfig.jsonthe adapter validator rejects still loads, with the error reported at adapter load; and thatModelInfo::namekeeps the caller's path string. For 27 individual checks (the unsupported defaults, the shape, rank and target validation, the rollback paths, the PEFT config rules, each projection's application, the deferred config error and the model name), removing that one check makes its test fail; each was removed and tested on its own. Changing the three adapter methods back to the crate-wideResultmakes the test module fail to compile.Linux x86_64 (rustc 1.98.0), default features:
rustfmt --edition 2021 --checkon the six changed Rust filescargo check --locked -p ruvllm@2.5.0 --all-targetscargo clippy --locked -p ruvllm@2.5.0 --all-targets -- -W warningscargo test --locked -p ruvllm@2.5.0 --lib backends::tests::Real model: Qwen3.5-0.8B with an adapter trained and saved by PEFT 0.21.2 (r=8, alpha=16, all seven projections, 192 tensors,
adapter_config.jsonunmodified,base_model_name_or_pathset to the hub id). Through this API it loads, and greedy decoding of 12 tokens for "The three primary colors are" matches Hugging Face transformers with PEFT token for token (" the most commonly cited as the most common colors in the context"). Afterunload_lora_adapterthe output equals the base model's, which also matches transformers (" red, green, and blue." ...).API notes
ModelAdapterErrorandbackends::AdapterInfoare#[non_exhaustive], so later variants or fields are not breaking changes.AdapterInfois not re-exported at the crate root, becauseloraandadapter_manageralready define types with that name.CI on main
On
main,Tests (core-and-rest)andTests (core-platform)currently fail (an ONNX link error inruvector-typesafe-trainand a read-latency assertion inmcp-brain-server, both tracked in #1178), andsupply-chainfails onnpm audit. None of the three failures is in ruvllm.For the maintainer
latticefeature, so the macOS tests above run only where someone runs them by hand. A macOS job runningcargo test -p ruvllm --features lattice --lib backends::would cover them.RuvLtraAdapters::new) and ships no weights. I left that existing text unchanged and added only the one clarifying paragraph in the new section. I can update the existing wording too if you would like.Refs #1191