From 02063a73b2b8edbc98039e5c8463108466e6e389 Mon Sep 17 00:00:00 2001
From: Pim Hutting
Date: Thu, 22 Jan 2026 11:15:13 +0100
Subject: [PATCH] feat: added recursive mapping
ref: N25B-400
---
.../visual_emotion_recognition_agent.py | 4 +---
.../user_interrupt/user_interrupt_agent.py | 17 ++++++++++++++---
2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/src/control_backend/agents/perception/visual_emotion_recognition_agent/visual_emotion_recognition_agent.py b/src/control_backend/agents/perception/visual_emotion_recognition_agent/visual_emotion_recognition_agent.py
index 5344b9b..7f21d21 100644
--- a/src/control_backend/agents/perception/visual_emotion_recognition_agent/visual_emotion_recognition_agent.py
+++ b/src/control_backend/agents/perception/visual_emotion_recognition_agent/visual_emotion_recognition_agent.py
@@ -7,7 +7,6 @@ import numpy as np
import zmq
import zmq.asyncio as azmq
from pydantic_core import ValidationError
-import struct
from control_backend.agents import BaseAgent
from control_backend.agents.perception.visual_emotion_recognition_agent.visual_emotion_recognizer import ( # noqa
@@ -89,7 +88,7 @@ class VisualEmotionRecognitionAgent(BaseAgent):
while self._running:
try:
frame_bytes = await self.video_in_socket.recv()
-
+
# Convert bytes to a numpy buffer
nparr = np.frombuffer(frame_bytes, np.uint8)
@@ -126,7 +125,6 @@ class VisualEmotionRecognitionAgent(BaseAgent):
except zmq.Again:
self.logger.warning("No video frame received within timeout.")
-
async def update_emotions(self, prev_emotions: set[str], emotions: set[str]):
"""
Compare emotions from previous window and current emotions,
diff --git a/src/control_backend/agents/user_interrupt/user_interrupt_agent.py b/src/control_backend/agents/user_interrupt/user_interrupt_agent.py
index 7320896..8bf5b77 100644
--- a/src/control_backend/agents/user_interrupt/user_interrupt_agent.py
+++ b/src/control_backend/agents/user_interrupt/user_interrupt_agent.py
@@ -9,7 +9,7 @@ from control_backend.agents.bdi.agentspeak_generator import AgentSpeakGenerator
from control_backend.core.agent_system import InternalMessage
from control_backend.core.config import settings
from control_backend.schemas.belief_message import Belief, BeliefMessage
-from control_backend.schemas.program import ConditionalNorm, Program
+from control_backend.schemas.program import ConditionalNorm, Goal, Program
from control_backend.schemas.ri_message import (
GestureCommand,
PauseCommand,
@@ -250,6 +250,18 @@ class UserInterruptAgent(BaseAgent):
self._cond_norm_map = {}
self._cond_norm_reverse_map = {}
+ def _register_goal(goal: Goal):
+ """Recursively register goals and their subgoals."""
+ slug = AgentSpeakGenerator.slugify(goal)
+ self._goal_map[str(goal.id)] = slug
+ self._goal_reverse_map[slug] = str(goal.id)
+
+ # Recursively check steps for subgoals
+ if goal.plan and goal.plan.steps:
+ for step in goal.plan.steps:
+ if isinstance(step, Goal):
+ _register_goal(step)
+
for phase in program.phases:
for trigger in phase.triggers:
slug = AgentSpeakGenerator.slugify(trigger)
@@ -257,8 +269,7 @@ class UserInterruptAgent(BaseAgent):
self._trigger_reverse_map[slug] = str(trigger.id)
for goal in phase.goals:
- self._goal_map[str(goal.id)] = AgentSpeakGenerator.slugify(goal)
- self._goal_reverse_map[AgentSpeakGenerator.slugify(goal)] = str(goal.id)
+ _register_goal(goal)
for goal, id in self._goal_reverse_map.items():
self.logger.debug(f"Goal mapping: UI ID {goal} -> {id}")