Skip to content

ruvllm: LoRA adapters never reach generation; add adapter loading to LlmBackend #1191

Closed

Description

@ohdearquant

Issue filed by an AI agent on behalf of @ohdearquant.

Summary

LoRA adaptation in ruvllm does not reach generated text today. The adapters exist and learn, but no inference backend applies them, and LlmBackend has no way to load one. I would like to add adapter loading to the backend trait, implement it first in the backend that already supports runtime LoRA, and then connect SONA's learning to it.

What the current code does (main at 3c70b26)

  • LlmBackend in crates/ruvllm/src/backends/mod.rs has methods for loading a model, generating, embeddings, tokenizer and model info, and nothing for adapters.
  • candle_backend.rs and lattice_backend.rs contain no LoRA code. mistral_backend.rs carries an X-LoRA configuration for the mistral.rs path.
  • lora::MicroLoRA (crates/ruvllm/src/lora/micro_lora.rs) builds every target module's adapter with the same in_features x out_features (hidden x hidden by default). One instance therefore cannot cover q together with k/v under grouped-query attention, or the MLP projections, whose widths differ from the hidden size.
  • SONA's micro and base LoRA (crates/sona) transform vectors the caller passes in. apply_micro_lora and apply_base_lora are exposed through the N-API, Python and Postgres bindings, and SonaIntegration::apply_transform in ruvllm is only called from tests. None of these run inside a model forward pass.

So feedback can be recorded and adapters updated while generation output stays exactly what the base model produces. This is the same pattern #519 and #553 reported at the JS boundary, one layer further down.

Proposal

  1. Adapter API on LlmBackend. Load an adapter from a PEFT safetensors file or directory with a scale, unload it, and report which adapter is active. The default implementation returns a typed "not supported by this backend" error, so a backend that cannot apply adapters says so instead of silently ignoring them. A failed load leaves the previous adapter (or none) in place.
  2. First implementation in the Metal backend. lattice_backend.rs already depends on a runtime that hot-swaps LoRA adapters on the Qwen 3.5 path, so the adapter can be applied per layer and per projection without reloading the model. Tests: an adapter with nonzero weights changes greedy logits; unloading restores logits bit-identical to the base model; a corrupt or mismatched adapter fails without changing state.
  3. Docs. A short README section saying which backends apply adapters, and that MicroLoRA and SONA's adapters are vector-space transforms rather than model adapters.
  4. Follow-up, separately. Turn SONA feedback into a real per-layer PEFT adapter that step 1 can load, so learning shows up in generation. I would scope that in its own issue once 1 and 2 land.

Steps 1 to 3 would be one PR. The new trait methods have default implementations, so other backends and downstream implementors keep compiling unchanged, and the Metal part stays behind the existing lattice feature. Happy to adjust names or shape to fit how you want the backend trait to grow.

Activity

  1. ruvnet commented on Oct 24, 2026

    @ruvnet
    Owner

    Fixed by #1203.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions