Skip to content

Find decoder layers through PEFT wrappers and other layouts - #20

Merged
JenD86 merged 1 commit into
mainfrom
feat/layer-lookup
Nov 7, 2026
Merged

JenD86 merged 1 commit into
mainfrom
feat/layer-lookup

Conversation

@JenD86

@JenD86 JenD86 commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Why

Steering, residual-norm calibration and extraction hooked model.model.layers directly. A PEFT-wrapped model (e.g. one carrying an Afterlife LoRA adapter) doesn't have that path: peft_model.model is the *ForCausalLM, so its layers are at peft_model.model.model.layers, and the first steered turn (or create) raises AttributeError. Found while running Modulo and Afterlife together in habitat.

What

  • New modulo/layers.py:
    • unwrap() goes through PEFT via get_base_model().
    • decoder_layers() tries model.layers, the multimodal language_model layouts, OPT, transformer.h and gpt_neox.layers, in that order. If none match, it falls back to the ModuleList as long as num_hidden_layers. If that fails too, it raises a clear error.
    • num_layers() reads the config, including multimodal text_config.
  • agent.py, extraction.py and cli.py now use the helpers instead of model.model.layers / config.num_hidden_layers.
  • Docs (README, AGENTS.md, docstrings) now say plain or PEFT-wrapped. The troubleshooting entry is rewritten.

Plain models are unaffected: model.layers is tried first.

Testing

  • New tests/test_layers.py covers:
    • the same layers being found with and without PEFT
    • a hook on the PEFT-wrapped model's layers changing the logits
    • the other layouts, the fallback and the failure case
  • Full suite: 167 passed, 1 skipped (CPU).
  • On an RTX 5090: Qwen3-8B with an Afterlife-trained SFT adapter loaded through PEFT, steered with curiosity +4 during the same turn. It answered with the adapter's trained fact, with steering active.

🤖 Generated with Claude Code

Steering, residual-norm calibration and extraction hooked model.model.layers
directly, which a PEFT-wrapped model (e.g. one carrying an Afterlife LoRA)
doesn't have. modulo.layers.decoder_layers unwraps PEFT and tries the usual
paths, then falls back to the ModuleList matching num_hidden_layers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JenD86
JenD86 merged commit 564c822 into main Nov 7, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant