states.py
1 """ 2 DreamTalk State Machines 3 4 Support for agentic holons with discrete states. 5 6 States represent relational configurations - specific parameter values 7 that define a mode of being. Transitions between states are animations. 8 9 Usage: 10 class MindVirus(Holon): 11 fold: Bipolar = 0 12 13 class States: 14 idle = State(fold=1) 15 hunting = State(fold=0.5) 16 attached = State(fold=-1) 17 18 # In a Dream: 19 virus = MindVirus() 20 self.play(virus.transition_to(virus.States.hunting), run_time=0.5) 21 """ 22 23 24 class State: 25 """ 26 A discrete state configuration. 27 28 Defines specific parameter values for a mode of being. 29 30 Args: 31 **param_values: Parameter name → value mappings 32 """ 33 34 def __init__(self, **param_values): 35 self.param_values = param_values 36 self.name = None # Set when collected from States class 37 38 def __repr__(self): 39 if self.name: 40 return f"State.{self.name}({self.param_values})" 41 return f"State({self.param_values})" 42 43 44 class StateMachine: 45 """ 46 Manages state transitions for a holon. 47 48 Created automatically when a Holon has a States class defined. 49 """ 50 51 def __init__(self, holon, states_class): 52 """ 53 Initialize state machine from a States class. 54 55 Args: 56 holon: The holon this state machine controls 57 states_class: The class containing State definitions 58 """ 59 self.holon = holon 60 self.states = {} 61 self.current_state = None 62 63 # Collect states from class attributes 64 for name in dir(states_class): 65 if name.startswith('_'): 66 continue 67 value = getattr(states_class, name) 68 if isinstance(value, State): 69 value.name = name 70 self.states[name] = value 71 72 def get_state(self, name): 73 """Get a state by name.""" 74 return self.states.get(name) 75 76 def transition_to(self, state): 77 """ 78 Create animation to transition to a state. 79 80 Args: 81 state: State instance or state name 82 83 Returns: 84 AnimationGroup animating all parameters to their state values 85 """ 86 from DreamTalk.animation.animation import ScalarAnimation 87 from DreamTalk.animation.abstract_animators import AnimationGroup 88 89 # Handle state name string 90 if isinstance(state, str): 91 state = self.states.get(state) 92 if state is None: 93 raise ValueError(f"Unknown state: {state}") 94 95 animations = [] 96 97 for param_name, target_value in state.param_values.items(): 98 # Try to find the parameter on the holon 99 param = None 100 101 # Check for parameter object (e.g., fold_parameter) 102 param_attr = f"{param_name}_parameter" 103 if hasattr(self.holon, param_attr): 104 param = getattr(self.holon, param_attr) 105 106 # Check for parameter in parameters list 107 if param is None and hasattr(self.holon, 'parameters'): 108 for p in self.holon.parameters: 109 if hasattr(p, 'name') and p.name.lower() == param_name.lower(): 110 param = p 111 break 112 113 if param is not None and hasattr(param, 'desc_id'): 114 # Determine the correct target object 115 # In generator_mode, parameters may need to target child objects 116 target_obj = self.holon 117 118 anim = ScalarAnimation( 119 target=target_obj, 120 descriptor=param.desc_id, 121 value_fin=target_value 122 ) 123 animations.append(anim) 124 125 # Update the actual value 126 target_obj.obj[param.desc_id] = target_value 127 128 self.current_state = state 129 130 if len(animations) == 0: 131 return None 132 elif len(animations) == 1: 133 return animations[0] 134 else: 135 return AnimationGroup(*animations) 136 137 138 def collect_states(holon): 139 """ 140 Collect states from a holon's States class. 141 142 Called during holon initialization. 143 144 Args: 145 holon: The holon instance 146 147 Returns: 148 StateMachine if States class exists, None otherwise 149 """ 150 states_class = getattr(holon.__class__, 'States', None) 151 if states_class is None: 152 return None 153 154 return StateMachine(holon, states_class)