Source code for protomotions.agents.peft.model
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Top-level discrete-prior PEFT model container.
The pretrained prior is passed in by the agent. This class only wires the
PEFT actor to the optional PPO critic. Rollout is the module forward path.
"""
from __future__ import annotations
from protomotions.agents.base_agent.model import BaseModel
from tensordict import TensorDict
from protomotions.agents.peft.actor import DiscretePriorPEFTActor
from protomotions.utils.hydra_replacement import get_class
[docs]
class DiscretePriorPEFTModel(BaseModel):
"""Discrete-prior PEFT actor plus optional PPO critic.
``forward`` is reserved for rollout TensorDicts. SFT teacher forcing lives
on ``DiscretePriorPEFTSFTModel`` so RLFT and SFT flows stay separate.
"""
[docs]
def __init__(
self,
config,
pretrained_prior_model,
mimic_target_poses_dim: int = 0,
):
super().__init__(config)
ActorClass = get_class(config.actor._target_)
self._actor: DiscretePriorPEFTActor = ActorClass(
config=config.actor,
pretrained_prior_model=pretrained_prior_model,
mimic_target_poses_dim=mimic_target_poses_dim,
)
self.in_keys = list(self._actor.in_keys)
self.out_keys = list(self._actor.out_keys)
self._critic = None
critic_config = getattr(config, "critic", None)
if critic_config is not None:
CriticClass = get_class(critic_config._target_)
self._critic = CriticClass(config=critic_config)
# Add critic in_keys and out_keys
self.in_keys = list(dict.fromkeys(self.in_keys + self._critic.in_keys))
self.out_keys = list(dict.fromkeys(self.out_keys + ["value"]))
[docs]
def optional_full_checkpoint_state_prefixes(self) -> tuple[str, ...]:
"""State sections that may differ between SFT and RLFT checkpoints."""
return ("_critic.",)
def _forward_critic(self, tensordict: TensorDict) -> TensorDict:
if self._critic is None:
return tensordict
tensordict = self._critic(tensordict)
out_key = self._critic.out_keys[0]
if out_key != "value":
tensordict["value"] = tensordict[out_key]
return tensordict
[docs]
def forward(self, tensordict: TensorDict) -> TensorDict:
"""Run rollout inference and critic evaluation."""
if not isinstance(tensordict, TensorDict):
raise TypeError(
"DiscretePriorPEFTModel.forward expects a TensorDict rollout input."
)
return self.forward_rollout(tensordict)
[docs]
def forward_rollout(self, tensordict: TensorDict) -> TensorDict:
output_td = self._actor.get_action_and_logp(tensordict)
return self._forward_critic(output_td)