Source code for protomotions.agents.supervised.expert_utils
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Expert observation utilities for supervised agents."""
import logging
from typing import Dict, Any
from copy import deepcopy
log = logging.getLogger(__name__)
def _require_expert_actor_in_keys(expert_agent_config: Any) -> list:
actor_in_keys = get_expert_actor_in_keys(expert_agent_config)
if not actor_in_keys:
raise ValueError(
"Expert agent config must define model.actor.in_keys or model.in_keys "
"so supervised expert observations can be wired explicitly."
)
return actor_in_keys
[docs]
def get_expert_actor_in_keys(expert_agent_config: Any) -> list:
"""Extract the input keys required by the expert's actor."""
if hasattr(expert_agent_config, 'model'):
model_config = expert_agent_config.model
if hasattr(model_config, 'actor') and hasattr(model_config.actor, 'in_keys'):
return list(model_config.actor.in_keys)
if hasattr(model_config, 'in_keys'):
return list(model_config.in_keys)
log.warning("Could not determine expert actor in_keys")
return []
[docs]
def get_expert_observation_keys(
expert_env_config: Any,
expert_agent_config: Any,
prefix: str = "expert_",
) -> list:
"""Get the prefixed observation keys that would be added for the expert."""
if not hasattr(expert_env_config, 'observation_components'):
return []
if expert_env_config.observation_components is None:
return []
actor_in_keys = _require_expert_actor_in_keys(expert_agent_config)
return [
f"{prefix}{obs_key}"
for obs_key in expert_env_config.observation_components.keys()
if obs_key in actor_in_keys
]
[docs]
def get_expert_observation_components(
expert_env_config: Any,
expert_agent_config: Any,
existing_obs_keys: list = None,
prefix: str = "expert_",
) -> Dict[str, Any]:
"""Extract observation components needed by expert's actor with prefixed keys."""
expert_obs_components = {}
if not hasattr(expert_env_config, 'observation_components'):
return expert_obs_components
if expert_env_config.observation_components is None:
return expert_obs_components
actor_in_keys = _require_expert_actor_in_keys(expert_agent_config)
existing_keys_set = set(existing_obs_keys) if existing_obs_keys else set()
for obs_key, obs_config in expert_env_config.observation_components.items():
if obs_key not in actor_in_keys:
continue
prefixed_key = f"{prefix}{obs_key}"
if prefixed_key in existing_keys_set:
raise ValueError(f"Expert observation key '{prefixed_key}' conflicts with existing observation.")
expert_obs_components[prefixed_key] = deepcopy(obs_config)
log.info(f"Loaded {len(expert_obs_components)} expert observation components with prefix '{prefix}'")
return expert_obs_components