import uuid import logging from typing import Any, Callable, Dict, List, Union log = logging.getLogger(__name__) MorphologyType = Union[str, Callable[[str], List[Dict[str, Any]]]] def generate_id() -> str: return uuid.uuid4().hex def get_InitSensor(morphology: MorphologyType): """ Return the initial sensor configuration for a given morphology. This helper selects a minimal starting set of sensors used to bootstrap a new agent's morphology. It resolves the full sensor list for the provided morphology and returns a list containing only the first sensor entry. Raises: ValueError: If the resolved morphology provides no sensors. """ sensors = get_Sensors(morphology) if not sensors: log.error("Morphology has no sensors.") raise ValueError("Morphology has no sensors.") return [sensors[0]] def get_InitActuator(morphology: MorphologyType): """ Return the initial actuator configuration for a given morphology. This helper selects a minimal starting set of actuators used to bootstrap a new agent's morphology. It resolves the full actuator list for the provided morphology and returns a list containing only the first actuator entry. Raises: ValueError: If the resolved morphology provides no actuators. """ actuators = get_Actuators(morphology) if not actuators: log.error("Morphology has no actuators.") raise ValueError("Morphology has no actuators.") return [actuators[0]] def get_Sensors(morphology: MorphologyType) -> List[Dict[str, Any]]: """ Resolve and return the list of sensor specifications for a morphology. The morphology may be provided as: - a callable that accepts a kind string ("sensors" or "actuators"), - a registered string key mapping to a known morphology implementation, - or a module-like object exposing a callable 'xor_mimic' function. Args: morphology: Morphology selector (callable, string key, or module-like). Returns: A list of sensor specification dictionaries, each describing a sensor actor (e.g., name, vector length, and associated scape). """ fn = _resolve_morphology(morphology) return fn("sensors") def get_Actuators(morphology: MorphologyType) -> List[Dict[str, Any]]: """ Resolve and return the list of actuator specifications for a morphology. The morphology may be provided as: - a callable that accepts a kind string ("sensors" or "actuators"), - a registered string key mapping to a known morphology implementation, - or a module-like object exposing a callable 'xor_mimic' function. Args: morphology: Morphology selector (callable, string key, or module-like). Returns: A list of actuator specification dictionaries, each describing an actuator actor (e.g., name, vector length, and associated scape). """ fn = _resolve_morphology(morphology) return fn("actuators") def _resolve_morphology(morphology: MorphologyType) -> Callable[[str], List[Dict[str, Any]]]: """ Resolve a morphology selector into a callable that can produce sensor or actuator specs. This function normalizes different morphology representations into a single callable interface: fn(kind) -> List[Dict[str, Any]]. Supported inputs are: - A callable: returned as-is. - A string key: looked up in a registry of known morphologies. - A module-like object: if it exposes a callable attribute 'xor_mimic', that callable is used. Args: morphology: Morphology selector (callable, string key, or module-like). Returns: A callable that accepts "sensors" or "actuators" and returns the corresponding specification list. Raises: ValueError: If a string key is provided but not registered. TypeError: If morphology cannot be resolved to a valid callable. """ if callable(morphology): return morphology if isinstance(morphology, str): reg = { "car_racing_features": car_racing_features } if morphology in reg: return reg[morphology] log.error(f"Unknown morphology name: {morphology}") raise ValueError(f"Unknown morphology name: {morphology}") try: if hasattr(morphology, "xor_mimic") and callable(getattr(morphology, "xor_mimic")): return getattr(morphology, "xor_mimic") except Exception: pass log.error("morphology must be a callable, a module with 'xor_mimic', or a registered string key") raise TypeError("morphology must be a callable, a module with 'xor_mimic', or a registered string key") def car_racing_features(kind: str) -> List[Dict[str, Any]]: """ Provide a feature-based CarRacing morphology specification. This morphology exposes: - One sensor ("car_GetFeatures") producing a fixed-length feature vector derived from a look-ahead horizon plus additional scalar features. - One actuator ("car_ApplyAction") consuming a 3-element action vector. Args: kind: Either "sensors" or "actuators". Returns: A list containing a single specification dictionary for the requested kind. Raises: ValueError: If kind is not "sensors" or "actuators". """ LOOK_AHEAD = 10 feature_len = LOOK_AHEAD + 6 if kind == "sensors": return [ { "name": "car_GetFeatures", "vector_length": feature_len, "scape": "car_racing" } ] elif kind == "actuators": return [ { "name": "car_ApplyAction", "vector_length": 3, "scape": "car_racing" } ] else: log.error(f"car_racing_features: unsupported kind '{kind}', expected 'sensors' or 'actuators'") raise ValueError("car_racing_features: kind must be 'sensors' or 'actuators'")