Compare commits

..

5 Commits

Author SHA1 Message Date
Twirre Meulenbelt
9248eaadbc fix: default belief false, user interrupt chat role, vad initial silence, unused import
ref: N25B-453
2026-01-26 14:36:10 +01:00
Twirre Meulenbelt
3095cb437b feat: rollover experiment logs when new program starts
ref: N25B-401
2026-01-22 11:27:36 +01:00
Twirre Meulenbelt
482c6b1082 Merge remote-tracking branch 'origin/demo' into demo 2026-01-20 12:53:49 +01:00
Twirre Meulenbelt
b3721322b9 Merge remote-tracking branch 'origin/demo' into demo 2026-01-20 12:31:51 +01:00
Twirre Meulenbelt
4fb10730a4 Merge remote-tracking branch 'origin/feat/visual-emotion-recognition' into demo 2026-01-20 12:20:24 +01:00
4 changed files with 31 additions and 18 deletions

View File

@@ -1,10 +1,12 @@
import asyncio import asyncio
import json import json
import logging
import zmq import zmq
from pydantic import ValidationError from pydantic import ValidationError
from zmq.asyncio import Context from zmq.asyncio import Context
import control_backend
from control_backend.agents import BaseAgent from control_backend.agents import BaseAgent
from control_backend.agents.bdi.agentspeak_generator import AgentSpeakGenerator from control_backend.agents.bdi.agentspeak_generator import AgentSpeakGenerator
from control_backend.core.config import settings from control_backend.core.config import settings
@@ -19,6 +21,8 @@ from control_backend.schemas.program import (
Program, Program,
) )
experiment_logger = logging.getLogger(settings.logging_settings.experiment_logger_name)
class BDIProgramManager(BaseAgent): class BDIProgramManager(BaseAgent):
""" """
@@ -241,6 +245,18 @@ class BDIProgramManager(BaseAgent):
await self.send(extractor_msg) await self.send(extractor_msg)
self.logger.debug("Sent message to extractor agent to clear history.") self.logger.debug("Sent message to extractor agent to clear history.")
@staticmethod
def _rollover_experiment_logs():
"""
A new experiment program started; make a new experiment log file.
"""
handlers = logging.getLogger("experiment").handlers
for handler in handlers:
if isinstance(handler, control_backend.logging.DatedFileHandler):
experiment_logger.action("Doing rollover...")
handler.do_rollover()
experiment_logger.debug("Finished rollover.")
async def _receive_programs(self): async def _receive_programs(self):
""" """
Continuous loop that receives program updates from the HTTP endpoint. Continuous loop that receives program updates from the HTTP endpoint.
@@ -261,6 +277,7 @@ class BDIProgramManager(BaseAgent):
self._initialize_internal_state(program) self._initialize_internal_state(program)
await self._send_program_to_user_interrupt(program) await self._send_program_to_user_interrupt(program)
await self._send_clear_llm_history() await self._send_clear_llm_history()
self._rollover_experiment_logs()
await asyncio.gather( await asyncio.gather(
self._create_agentspeak_and_send_to_bdi(program), self._create_agentspeak_and_send_to_bdi(program),

View File

@@ -150,6 +150,9 @@ class TextBeliefExtractorAgent(BaseAgent):
return return
available_beliefs = [b for b in belief_list.beliefs if isinstance(b, SemanticBelief)] available_beliefs = [b for b in belief_list.beliefs if isinstance(b, SemanticBelief)]
self._current_beliefs = BeliefState(
false={InternalBelief(name=b.name, arguments=None) for b in available_beliefs},
)
self.belief_inferrer.available_beliefs = available_beliefs self.belief_inferrer.available_beliefs = available_beliefs
self.logger.debug( self.logger.debug(
"Received %d semantic beliefs from the program manager: %s", "Received %d semantic beliefs from the program manager: %s",
@@ -170,6 +173,9 @@ class TextBeliefExtractorAgent(BaseAgent):
available_goals = {g for g in goals_list.goals if g.can_fail} available_goals = {g for g in goals_list.goals if g.can_fail}
available_goals -= self._force_completed_goals available_goals -= self._force_completed_goals
self.goal_inferrer.goals = available_goals self.goal_inferrer.goals = available_goals
self._current_goal_completions = {
f"achieved_{AgentSpeakGenerator.slugify(goal)}": False for goal in available_goals
}
self.logger.debug( self.logger.debug(
"Received %d failable goals from the program manager: %s", "Received %d failable goals from the program manager: %s",
len(available_goals), len(available_goals),

View File

@@ -285,9 +285,10 @@ class VADAgent(BaseAgent):
assert self.audio_out_socket is not None assert self.audio_out_socket is not None
await self.audio_out_socket.send(self.audio_buffer[: -2 * len(chunk)].tobytes()) await self.audio_out_socket.send(self.audio_buffer[: -2 * len(chunk)].tobytes())
# At this point, we know that the speech has ended. # At this point, we know that there is no speech.
# Prepend the last chunk that had no speech, for a more fluent boundary # Prepend the last few chunks that had no speech, for a more fluent boundary.
self.audio_buffer = chunk self.audio_buffer = np.append(self.audio_buffer, chunk)
self.audio_buffer = self.audio_buffer[-begin_silence_length * len(chunk) :]
async def handle_message(self, msg: InternalMessage): async def handle_message(self, msg: InternalMessage):
""" """

View File

@@ -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.agent_system import InternalMessage
from control_backend.core.config import settings from control_backend.core.config import settings
from control_backend.schemas.belief_message import Belief, BeliefMessage from control_backend.schemas.belief_message import Belief, BeliefMessage
from control_backend.schemas.program import ConditionalNorm, Goal, Program from control_backend.schemas.program import ConditionalNorm, Program
from control_backend.schemas.ri_message import ( from control_backend.schemas.ri_message import (
GestureCommand, GestureCommand,
PauseCommand, PauseCommand,
@@ -250,18 +250,6 @@ class UserInterruptAgent(BaseAgent):
self._cond_norm_map = {} self._cond_norm_map = {}
self._cond_norm_reverse_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 phase in program.phases:
for trigger in phase.triggers: for trigger in phase.triggers:
slug = AgentSpeakGenerator.slugify(trigger) slug = AgentSpeakGenerator.slugify(trigger)
@@ -269,7 +257,8 @@ class UserInterruptAgent(BaseAgent):
self._trigger_reverse_map[slug] = str(trigger.id) self._trigger_reverse_map[slug] = str(trigger.id)
for goal in phase.goals: for goal in phase.goals:
_register_goal(goal) self._goal_map[str(goal.id)] = AgentSpeakGenerator.slugify(goal)
self._goal_reverse_map[AgentSpeakGenerator.slugify(goal)] = str(goal.id)
for goal, id in self._goal_reverse_map.items(): for goal, id in self._goal_reverse_map.items():
self.logger.debug(f"Goal mapping: UI ID {goal} -> {id}") self.logger.debug(f"Goal mapping: UI ID {goal} -> {id}")
@@ -311,7 +300,7 @@ class UserInterruptAgent(BaseAgent):
:param text_to_say: The string that the robot has to say. :param text_to_say: The string that the robot has to say.
""" """
experiment_logger.chat(text_to_say, extra={"role": "user"}) experiment_logger.chat(text_to_say, extra={"role": "assistant"})
cmd = SpeechCommand(data=text_to_say, is_priority=True) cmd = SpeechCommand(data=text_to_say, is_priority=True)
out_msg = InternalMessage( out_msg = InternalMessage(
to=settings.agent_settings.robot_speech_name, to=settings.agent_settings.robot_speech_name,