From 8f6662e64a72376298ff0c87fbf0d7b0df721423 Mon Sep 17 00:00:00 2001 From: Kasper Marinus Date: Wed, 14 Jan 2026 13:22:51 +0100 Subject: [PATCH] feat: phase transitions ref: N25B-446 --- .../agents/bdi/agentspeak_generator.py | 23 +++++++++++++-- .../agents/bdi/bdi_core_agent.py | 28 +++++++++---------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/control_backend/agents/bdi/agentspeak_generator.py b/src/control_backend/agents/bdi/agentspeak_generator.py index 68d1393..11bb2c8 100644 --- a/src/control_backend/agents/bdi/agentspeak_generator.py +++ b/src/control_backend/agents/bdi/agentspeak_generator.py @@ -197,10 +197,12 @@ class AgentSpeakGenerator: self._astify(to_phase) if to_phase else AstLiteral("phase", [AstString("end")]) ) - context = [from_phase_ast] + check_context = [from_phase_ast] if from_phase: for goal in from_phase.goals: - context.append(self._astify(goal, achieved=True)) + check_context.append(self._astify(goal, achieved=True)) + + force_context = [from_phase_ast] body = [ AstStatement( @@ -229,8 +231,23 @@ class AgentSpeakGenerator: # ] # ) + # Check self._asp.plans.append( - AstPlan(TriggerType.ADDED_GOAL, AstLiteral("transition_phase"), context, body) + AstPlan( + TriggerType.ADDED_GOAL, + AstLiteral("transition_phase"), + check_context, + [ + AstStatement(StatementType.ACHIEVE_GOAL, AstLiteral("force_transition_phase")), + ], + ) + ) + + # Force + self._asp.plans.append( + AstPlan( + TriggerType.ADDED_GOAL, AstLiteral("force_transition_phase"), force_context, body + ) ) def _process_norm(self, norm: Norm, phase: Phase) -> None: diff --git a/src/control_backend/agents/bdi/bdi_core_agent.py b/src/control_backend/agents/bdi/bdi_core_agent.py index 9f8e2e4..8eb4d23 100644 --- a/src/control_backend/agents/bdi/bdi_core_agent.py +++ b/src/control_backend/agents/bdi/bdi_core_agent.py @@ -156,8 +156,7 @@ class BDICoreAgent(BaseAgent): ) await self.send(out_msg) case settings.agent_settings.user_interrupt_name: - content = msg.body - self.logger.debug("Received user interruption: %s", content) + self.logger.debug("Received user interruption: %s", msg) match msg.thread: case "force_phase_transition": @@ -166,6 +165,8 @@ class BDICoreAgent(BaseAgent): self._force_trigger(msg.body) case "force_norm": self._force_norm(msg.body) + case "force_next_phase": + self._force_next_phase() case _: self.logger.warning("Received unknow user interruption: %s", msg) @@ -304,26 +305,21 @@ class BDICoreAgent(BaseAgent): self.logger.debug(f"Set goal !{self.format_belief_string(name, args)}.") def _force_trigger(self, name: str): - self.bdi_agent.call( - agentspeak.Trigger.addition, - agentspeak.GoalType.achievement, - agentspeak.Literal(name), - agentspeak.runtime.Intention(), - ) + self._set_goal(name) self.logger.info("Manually forced trigger %s.", name) # TODO: make this compatible for critical norms def _force_norm(self, name: str): - self.bdi_agent.call( - agentspeak.Trigger.addition, - agentspeak.GoalType.belief, - agentspeak.Literal(f"force_{name}"), - agentspeak.runtime.Intention(), - ) + self._add_belief(f"force_{name}") self.logger.info("Manually forced norm %s.", name) + def _force_next_phase(self): + self._set_goal("force_transition_phase") + + self.logger.info("Manually forced phase transition.") + def _add_custom_actions(self) -> None: """ Add any custom actions here. Inside `@self.actions.add()`, the first argument is @@ -520,6 +516,10 @@ class BDICoreAgent(BaseAgent): yield + @self.actions.add(".notify_ui", 0) + def _notify_ui(agent, term, intention): + pass + async def _send_to_llm(self, text: str, norms: str, goals: str): """ Sends a text query to the LLM agent asynchronously.