Skip to content

[NPUW] Model builder: add LoRA adapter support#36320

Merged
AlexanderKalistratov merged 6 commits into
openvinotoolkit:masterfrom
dylanneve1:EISW-209517-lora
Jul 14, 2026
Merged

[NPUW] Model builder: add LoRA adapter support#36320
AlexanderKalistratov merged 6 commits into
openvinotoolkit:masterfrom
dylanneve1:EISW-209517-lora

Conversation

@dylanneve1

@dylanneve1 dylanneve1 commented Jun 9, 2026

Copy link
Copy Markdown
Member

Details:

Adds LoRA adapter support to the NPUW synthetic model builder so LoRA-enabled LLM paths can be tested without real models or adapters.

  • LoRAInjector carries the LoRA settings (max rank, targets, precision, stateless/stateful form) through make_linear, the Attention block and the SwiGLU/GELU FFNs. Adapted projections get the standard low-rank branch out = base + (input @ A^T * alpha) @ B^T.
  • Two LoRA forms:
    • stateless — lora_state_* Parameters (NPUW form),
    • stateful — ReadValue/Assign state pairs (GenAI dynamic form before NPUW conversion).
  • Shapes and names follow the NPUW convention (A [r, in], B [out, r], alpha [1, r] f32, ids ending in MatMul.A/B/alpha).
  • LLMConfig gets lora_rank/lora_targets/lora_stateful; build_lora_adapter(LoRAConfig) builds a minimal standalone adapter model.
  • Projection MatMuls are now named optimum-style (<layer>/MatMul) so GenAI dynamic-LoRA state ids carry MatMul and NPUW pins the LoRA rank static via NPUW_LLM_MAX_LORA_RANK.
  • Unit tests cover both forms, build_llm injection, A/B/alpha shapes/precision and target filtering.

Validated by running synthetic models with a separate PEFT adapter end to end via GenAI dynamic LoRA (NPU and NPUW_DEVICES=CPU), and comparing the resulting subgraphs against TinyLlama with real LoRA adapters: identical LoRA structure.

Tickets:

@dylanneve1 dylanneve1 requested review from a team as code owners June 9, 2026 10:05
@github-actions github-actions Bot added category: build OpenVINO cmake script / infra category: NPU OpenVINO NPU plugin category: NPUW NPUW plugin labels Jun 9, 2026
@dylanneve1 dylanneve1 requested review from Copilot and removed request for a team June 9, 2026 10:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds LoRA adapter injection support to the NPUW synthetic LLM model builder used in Intel NPU plugin tests, enabling construction of models with LoRA A/B/alpha tensors and supporting both stateless (Parameters) and stateful (ReadValue/Assign) forms.

Changes:

  • Extend the test-engine ModelBuilder LLM configuration and make_linear()/Attention/FFN builders to optionally inject LoRA subgraphs.
  • Add stateful→stateless conversion coverage and LoRA-subgraph-fusion compatibility coverage via new unit tests.
  • Adjust SDPA scale constant typing to match the query element type to avoid mixed-dtype validation issues.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/plugins/intel_npu/tests/unit/npuw/model_builder_lora_test.cpp Adds unit tests validating LoRA input naming/shapes, stateful→stateless conversion, fusion, and basic inference impact.
src/plugins/intel_npu/tests/unit/CMakeLists.txt Registers the new LoRA unit test source in the unit test target.
src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder.hpp Extends make_linear() signature and LLMConfig with LoRA configuration fields.
src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder.cpp Implements LoRA tensor creation (Parameter or ReadValue/Assign), injects LoRA into linear layers, and wires LoRA into attention/FFN during LLM build.
src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder_types.hpp Introduces LoRAInjector describing LoRA injection settings and target matching.
src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder_ffn.hpp Propagates optional LoRA injector through FFN functors (SwiGLU/GELU).
src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder_ffn.cpp Passes LoRA injector to underlying linear projections in FFN implementations.
src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder_attention.hpp Adds optional LoRA injector plumbing to attention output projection and Attention struct.
src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder_attention.cpp Uses element-type-consistent SDPA scale and propagates LoRA injector through Q/K/V and O projections.

Comment thread src/plugins/intel_npu/tests/unit/npuw/model_builder_lora_test.cpp Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

@dylanneve1 dylanneve1 force-pushed the EISW-209517-lora branch 4 times, most recently from fa12d2b to 590a9be Compare June 9, 2026 12:17
@dylanneve1 dylanneve1 requested a review from Copilot June 9, 2026 12:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

@dylanneve1 dylanneve1 force-pushed the EISW-209517-lora branch 6 times, most recently from 9d0f772 to 81f9582 Compare June 16, 2026 12:52
@dylanneve1 dylanneve1 requested review from a team and AsyaPronina June 17, 2026 08:42
Add low-rank adaptation (LoRA) injection to the synthetic model builder.
When LLMConfig::lora_rank > 0, every adapted linear projection gets three
extra A/B/alpha tensors wired as:

  lora_out = input @ A^T * alpha @ B^T
  output = base_linear_out + lora_out

Tensor names follow the NPUW lora_state_* convention:
  lora_state_<layer_name>.MatMul.A      [rank, in_features]
  lora_state_<layer_name>.MatMul.B      [out_features, rank]
  lora_state_<layer_name>.MatMul.alpha  [1, rank]

Default adapted projections: q_proj, k_proj, v_proj, o_proj, gate_proj,
up_proj, down_proj (configurable via LLMConfig::lora_targets). Stateless
form exposes A/B/alpha as Parameters; stateful form (lora_stateful=true)
exposes them as ReadValue/Assign state pairs.

Ported onto the split model_builder.* layout: LoRAInjector lives in
model_builder_types.hpp; make_lora_value + make_linear injection in
model_builder.cpp; SwiGLU/GELU wiring in model_builder_ffn.*; Attention
+ make_attention_output wiring in model_builder_attention.*; LLMConfig
fields and build_llm setup in model_builder.{hpp,cpp}. make_sdpa now
builds its scale constant in q's element type so f16 LoRA models don't
trip SDPA's mixed-input-type check.

All four ModelBuilderLoraTest cases pass.
Add a dedicated LoRAConfig (extends BaseModelConfig) and build_lora_adapter
so a generic LoRA model can be built without standing up a full LLM and
disabling its KV cache / position-id machinery by hand.

build_lora_adapter emits a minimal embed -> [adapted linear] x layers ->
output graph with no attention, KV cache or RoPE. Every layer applies one
LoRA-injected make_linear per target projection, producing the same
lora_state_*.MatMul.{A,B,alpha} Parameters (stateless) or ReadValue/Assign
states (stateful) that NPUW's LoRA transforms consume.

Replace the model_builder_lora_test with two simple sanity checks (stateless
exposes lora_state_ Parameters with the expected A/B/alpha names; stateful
exposes states). Richer LoRA test infrastructure is follow-up work.
LoRA stays explicitly threaded as a const LoRAInjector* through make_linear
and the Attention / SwiGLU / GELU functors (no global state).

- LoRAInjector::max_rank / precision get safe defaults (0 / f32) and
  should_adapt() returns false for max_rank == 0, so a default-constructed
  injector is an inert no-op.
- build_llm holds the injector on the stack and recreates only the default
  dense FFN as a LoRA-aware SwiGLU; custom and MoE FFN graphs are untouched.
- Add unit coverage for the build_llm LoRA path (attention + FFN adapted)
  and the no-LoRA negative case, alongside the build_lora_adapter checks.
…ic LoRA

Append "/MatMul" to the projection MatMul friendly name so GenAI's
dynamic-LoRA lora_state_* ids carry "MatMul"; NPUW's ReshapeToStatic only
pins the LoRA rank static for those, otherwise NPU compile throws
"to_shape was called on a dynamic shape".
Extract inject_lora() from make_linear, share the default target list via
LoRAInjector::default_targets(), use static ov::Shape for LoRA tensors, and
strengthen the stateful test to verify lora_state_*.MatMul.* variable ids.
# Conflicts:
#	src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder.cpp
#	src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder.hpp
#	src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder_attention.cpp
#	src/plugins/intel_npu/tests/functional/behavior/npuw/test_engine/models/model_builder_attention.hpp
@AlexanderKalistratov AlexanderKalistratov added this pull request to the merge queue Jul 14, 2026
Merged via the queue into openvinotoolkit:master with commit d8047fb Jul 14, 2026
192 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category: build OpenVINO cmake script / infra category: NPU OpenVINO NPU plugin category: NPUW NPUW plugin

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants