Source code for protomotions.agents.peft.utils.adapter_state
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""State-dict filtering for PEFT adapter checkpoints.
High-level actor and agent code should talk in terms of "save adapter" and
"load adapter". This module owns the low-level key rules that decide which
entries are adapter/task state and which entries belong to the frozen prior,
critic, or training-only scaffolding.
"""
from __future__ import annotations
import torch
from torch import nn
from protomotions.agents.utils.normalization import (
materialize_lazy_running_stats_from_state_dict,
)
[docs]
def strip_actor_prefix(key: str) -> str:
"""Accept both model-level keys and actor-local keys."""
return key[len("_actor.") :] if key.startswith("_actor.") else key
[docs]
def is_adapter_state_key(key: str) -> bool:
"""Return True for keys that belong in a slim PEFT checkpoint."""
key = strip_actor_prefix(key)
if key.startswith("prior_with_peft._anchor_transformer."):
return False
if key.startswith("prior_with_peft.reference_prior."):
return False
if key.startswith("prior_with_peft.reference_film_input_norm."):
return False
if key.startswith("actor_peft_model."):
return True
if key.startswith("prior_with_peft.film_input_norm."):
return True
return key.startswith("prior_with_peft.") and (
".lora." in key
or ".gamma." in key
or ".beta." in key
or key.endswith(".m")
)
[docs]
def contains_adapter_state(state_dict: dict) -> bool:
"""Return True when a checkpoint contains at least one adapter key."""
return any(is_adapter_state_key(key) for key in state_dict)
[docs]
def build_adapter_state_dict(module: nn.Module) -> dict[str, torch.Tensor]:
"""Build an adapter-only state dict from a live actor module."""
return {
key: value
for key, value in module.state_dict().items()
if is_adapter_state_key(key)
}
[docs]
def load_adapter_state_dict(
module: nn.Module,
state_dict: dict,
*,
strict: bool = True,
):
"""Load adapter/task keys into ``module`` without touching frozen prior state."""
actor_state = {strip_actor_prefix(key): value for key, value in state_dict.items()}
materialize_lazy_running_stats_from_state_dict(module, actor_state)
expected_keys = set(build_adapter_state_dict(module).keys())
adapter_state = {}
unexpected_keys = []
for raw_key, value in state_dict.items():
key = strip_actor_prefix(raw_key)
if key in expected_keys:
adapter_state[key] = value
elif is_adapter_state_key(key):
unexpected_keys.append(key)
missing_keys = sorted(expected_keys - set(adapter_state.keys()))
unexpected_keys = sorted(unexpected_keys)
if strict and (missing_keys or unexpected_keys):
raise RuntimeError(
"Adapter state dict mismatch: "
f"missing_keys={missing_keys}, unexpected_keys={unexpected_keys}"
)
merged_state = module.state_dict()
merged_state.update(adapter_state)
module.load_state_dict(merged_state, strict=True)
return {"missing_keys": missing_keys, "unexpected_keys": unexpected_keys}