import logging from typing import Any, Dict, Tuple, Literal from reachy_mini.utils import create_head_pose from reachy_mini_conversation_app.tools.core_tools import Tool, ToolDependencies from reachy_mini_conversation_app.dance_emotion_moves import GotoQueueMove logger = logging.getLogger(__name__) Direction = Literal["left", "right", "up", "down", "front"] class MoveHead(Tool): """Move head in a given direction.""" name = "move_head" description = "Move your head in a given direction: left, right, up, down or front." parameters_schema = { "type": "object", "properties": { "direction": { "type": "string", "enum": ["left", "right", "up", "down", "front"], }, }, "required": ["direction"], } # mapping: direction -> args for create_head_pose DELTAS: Dict[str, Tuple[int, int, int, int, int, int]] = { "left": (0, 0, 0, 0, 0, 40), "right": (0, 0, 0, 0, 0, -40), "up": (0, 0, 0, 0, -30, 0), "down": (0, 0, 0, 0, 30, 0), "front": (0, 0, 0, 0, 0, 0), } async def __call__(self, deps: ToolDependencies, **kwargs: Any) -> Dict[str, Any]: """Move head in a given direction.""" direction_raw = kwargs.get("direction") if not isinstance(direction_raw, str): return {"error": "direction must be a string"} direction: Direction = direction_raw # type: ignore[assignment] logger.info("Tool call: move_head direction=%s", direction) deltas = self.DELTAS.get(direction, self.DELTAS["front"]) target = create_head_pose(*deltas, degrees=True) # Use new movement manager try: movement_manager = deps.movement_manager # Get current state for interpolation current_head_pose = deps.reachy_mini.get_current_head_pose() _, current_antennas = deps.reachy_mini.get_current_joint_positions() # Create goto move goto_move = GotoQueueMove( target_head_pose=target, start_head_pose=current_head_pose, target_antennas=(0, 0), # Reset antennas to default start_antennas=( current_antennas[0], current_antennas[1], ), # Skip body_yaw target_body_yaw=0, # Reset body yaw start_body_yaw=current_antennas[0], # body_yaw is first in joint positions duration=deps.motion_duration_s, ) movement_manager.queue_move(goto_move) movement_manager.set_moving_state(deps.motion_duration_s) return {"status": f"looking {direction}"} except Exception as e: logger.error("move_head failed") return {"error": f"move_head failed: {type(e).__name__}: {e}"}