feat: added a functionality for monitoring page
ref: N25B-400
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
phase("0e0f239c-efe9-442c-bdd7-3aabfccd1c49").
|
phase("db4c68c3-0316-4905-a8db-22dd5bec7abf").
|
||||||
keyword_said(Keyword) :- (user_said(Message) & .substring(Keyword, Message, Pos)) & (Pos >= 0).
|
keyword_said(Keyword) :- (user_said(Message) & .substring(Keyword, Message, Pos)) & (Pos >= 0).
|
||||||
|
norm("do nothing and make a little dance, do a little laugh") :- phase("db4c68c3-0316-4905-a8db-22dd5bec7abf") & keyword_said("hi").
|
||||||
|
|
||||||
|
|
||||||
+!reply_with_goal(Goal)
|
+!reply_with_goal(Goal)
|
||||||
@@ -19,36 +20,30 @@ keyword_said(Keyword) :- (user_said(Message) & .substring(Keyword, Message, Pos)
|
|||||||
.reply(Message, Norms).
|
.reply(Message, Norms).
|
||||||
|
|
||||||
+user_said(Message)
|
+user_said(Message)
|
||||||
: phase("0e0f239c-efe9-442c-bdd7-3aabfccd1c49")
|
: phase("db4c68c3-0316-4905-a8db-22dd5bec7abf")
|
||||||
<- .notify_user_said(Message);
|
<- .notify_user_said(Message);
|
||||||
-responded_this_turn;
|
-responded_this_turn;
|
||||||
!check_triggers;
|
!check_triggers;
|
||||||
!transition_phase.
|
!transition_phase.
|
||||||
|
|
||||||
+!transition_phase
|
+!check_triggers
|
||||||
: phase("0e0f239c-efe9-442c-bdd7-3aabfccd1c49") &
|
: phase("db4c68c3-0316-4905-a8db-22dd5bec7abf") &
|
||||||
not responded_this_turn
|
semantic_hello
|
||||||
<- -phase("0e0f239c-efe9-442c-bdd7-3aabfccd1c49");
|
<- .notify_trigger_start("trigger_");
|
||||||
+phase("1fc60869-86db-483d-b475-b8ecdec4bba8");
|
.notify_trigger_end("trigger_").
|
||||||
?user_said(Message);
|
|
||||||
-+user_said(Message);
|
|
||||||
.notify_transition_phase("0e0f239c-efe9-442c-bdd7-3aabfccd1c49", "1fc60869-86db-483d-b475-b8ecdec4bba8").
|
|
||||||
|
|
||||||
+user_said(Message)
|
+!trigger_
|
||||||
: phase("1fc60869-86db-483d-b475-b8ecdec4bba8")
|
<- .notify_trigger_start("trigger_");
|
||||||
<- .notify_user_said(Message);
|
.notify_trigger_end("trigger_").
|
||||||
-responded_this_turn;
|
|
||||||
!check_triggers;
|
|
||||||
!transition_phase.
|
|
||||||
|
|
||||||
+!transition_phase
|
+!transition_phase
|
||||||
: phase("1fc60869-86db-483d-b475-b8ecdec4bba8") &
|
: phase("db4c68c3-0316-4905-a8db-22dd5bec7abf") &
|
||||||
not responded_this_turn
|
not responded_this_turn
|
||||||
<- -phase("1fc60869-86db-483d-b475-b8ecdec4bba8");
|
<- .notify_transition_phase("db4c68c3-0316-4905-a8db-22dd5bec7abf", "end");
|
||||||
|
-phase("db4c68c3-0316-4905-a8db-22dd5bec7abf");
|
||||||
+phase("end");
|
+phase("end");
|
||||||
?user_said(Message);
|
?user_said(Message);
|
||||||
-+user_said(Message);
|
-+user_said(Message).
|
||||||
.notify_transition_phase("1fc60869-86db-483d-b475-b8ecdec4bba8", "end").
|
|
||||||
|
|
||||||
+user_said(Message)
|
+user_said(Message)
|
||||||
: phase("end")
|
: phase("end")
|
||||||
|
|||||||
@@ -176,6 +176,16 @@ class AgentSpeakGenerator:
|
|||||||
context.append(self._astify(from_phase.goals[-1], achieved=True))
|
context.append(self._astify(from_phase.goals[-1], achieved=True))
|
||||||
|
|
||||||
body = [
|
body = [
|
||||||
|
AstStatement(
|
||||||
|
StatementType.DO_ACTION,
|
||||||
|
AstLiteral(
|
||||||
|
"notify_transition_phase",
|
||||||
|
[
|
||||||
|
AstString(str(from_phase.id)),
|
||||||
|
AstString(str(to_phase.id) if to_phase else "end"),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
AstStatement(StatementType.REMOVE_BELIEF, from_phase_ast),
|
AstStatement(StatementType.REMOVE_BELIEF, from_phase_ast),
|
||||||
AstStatement(StatementType.ADD_BELIEF, to_phase_ast),
|
AstStatement(StatementType.ADD_BELIEF, to_phase_ast),
|
||||||
]
|
]
|
||||||
@@ -192,20 +202,6 @@ class AgentSpeakGenerator:
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Notify outside world about transition
|
|
||||||
body.append(
|
|
||||||
AstStatement(
|
|
||||||
StatementType.DO_ACTION,
|
|
||||||
AstLiteral(
|
|
||||||
"notify_transition_phase",
|
|
||||||
[
|
|
||||||
AstString(str(from_phase.id)),
|
|
||||||
AstString(str(to_phase.id) if to_phase else "end"),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self._asp.plans.append(
|
self._asp.plans.append(
|
||||||
AstPlan(TriggerType.ADDED_GOAL, AstLiteral("transition_phase"), context, body)
|
AstPlan(TriggerType.ADDED_GOAL, AstLiteral("transition_phase"), context, body)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -311,6 +311,15 @@ class BDICoreAgent(BaseAgent):
|
|||||||
message_text = agentspeak.grounded(term.args[0], intention.scope)
|
message_text = agentspeak.grounded(term.args[0], intention.scope)
|
||||||
norms = agentspeak.grounded(term.args[1], intention.scope)
|
norms = agentspeak.grounded(term.args[1], intention.scope)
|
||||||
|
|
||||||
|
norm_update_message = InternalMessage(
|
||||||
|
to=settings.agent_settings.user_interrupt_name,
|
||||||
|
sender=self.name,
|
||||||
|
thread="active_norms_update",
|
||||||
|
body=str(norms),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add_behavior(self.send(norm_update_message))
|
||||||
|
|
||||||
self.logger.debug("Norms: %s", norms)
|
self.logger.debug("Norms: %s", norms)
|
||||||
self.logger.debug("User text: %s", message_text)
|
self.logger.debug("User text: %s", message_text)
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,12 @@ class BDIProgramManager(BaseAgent):
|
|||||||
self._transition_phase(phases["old"], phases["new"])
|
self._transition_phase(phases["old"], phases["new"])
|
||||||
|
|
||||||
def _transition_phase(self, old: str, new: str):
|
def _transition_phase(self, old: str, new: str):
|
||||||
assert old == str(self._phase.id)
|
if old != str(self._phase.id):
|
||||||
|
self.logger.warning(
|
||||||
|
f"Phase transition desync detected! ASL requested move from '{old}', "
|
||||||
|
f"but Python is currently in '{self._phase.id}'. Request ignored."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
if new == "end":
|
if new == "end":
|
||||||
self._phase = None
|
self._phase = None
|
||||||
@@ -208,6 +213,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_clear_llm_history()
|
await self._send_clear_llm_history()
|
||||||
|
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
@@ -216,6 +222,21 @@ class BDIProgramManager(BaseAgent):
|
|||||||
self._send_goals_to_semantic_belief_extractor(),
|
self._send_goals_to_semantic_belief_extractor(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _send_program_to_user_interrupt(self, program: Program):
|
||||||
|
"""
|
||||||
|
Send the received program to the User Interrupt Agent.
|
||||||
|
|
||||||
|
:param program: The program object received from the API.
|
||||||
|
"""
|
||||||
|
msg = InternalMessage(
|
||||||
|
sender=self.name,
|
||||||
|
to=settings.agent_settings.user_interrupt_name,
|
||||||
|
body=program.model_dump_json(),
|
||||||
|
thread="new_program",
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.send(msg)
|
||||||
|
|
||||||
async def setup(self):
|
async def setup(self):
|
||||||
"""
|
"""
|
||||||
Initialize the agent.
|
Initialize the agent.
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import zmq
|
|||||||
from zmq.asyncio import Context
|
from zmq.asyncio import Context
|
||||||
|
|
||||||
from control_backend.agents import BaseAgent
|
from control_backend.agents import BaseAgent
|
||||||
|
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.program import Program
|
||||||
from control_backend.schemas.ri_message import GestureCommand, RIEndpoint, SpeechCommand
|
from control_backend.schemas.ri_message import GestureCommand, RIEndpoint, SpeechCommand
|
||||||
|
|
||||||
|
|
||||||
@@ -32,6 +34,16 @@ class UserInterruptAgent(BaseAgent):
|
|||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.sub_socket = None
|
self.sub_socket = None
|
||||||
self.pub_socket = None
|
self.pub_socket = None
|
||||||
|
self._trigger_map = {}
|
||||||
|
self._trigger_reverse_map = {}
|
||||||
|
|
||||||
|
self._goal_map = {}
|
||||||
|
self._goal_reverse_map = {}
|
||||||
|
|
||||||
|
self._cond_norm_map = {}
|
||||||
|
self._cond_norm_reverse_map = {}
|
||||||
|
|
||||||
|
self._belief_condition_map = {}
|
||||||
|
|
||||||
async def setup(self):
|
async def setup(self):
|
||||||
"""
|
"""
|
||||||
@@ -87,11 +99,19 @@ class UserInterruptAgent(BaseAgent):
|
|||||||
event_context,
|
event_context,
|
||||||
)
|
)
|
||||||
elif event_type == "override":
|
elif event_type == "override":
|
||||||
await self._send_to_program_manager(event_context)
|
ui_id = str(event_context)
|
||||||
self.logger.info(
|
if asl_trigger := self._trigger_map.get(ui_id):
|
||||||
"Forwarded button press (override) with context '%s' to BDIProgramManager.",
|
await self._send_to_bdi("force_trigger", asl_trigger)
|
||||||
event_context,
|
self.logger.info(
|
||||||
)
|
"Forwarded button press (override) with context '%s' to BDI Core.",
|
||||||
|
event_context,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await self._send_to_program_manager(event_context)
|
||||||
|
self.logger.info(
|
||||||
|
"Forwarded button press (override) with context '%s' to BDIProgramManager.",
|
||||||
|
event_context,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.logger.warning(
|
self.logger.warning(
|
||||||
"Received button press with unknown type '%s' (context: '%s').",
|
"Received button press with unknown type '%s' (context: '%s').",
|
||||||
@@ -104,6 +124,26 @@ class UserInterruptAgent(BaseAgent):
|
|||||||
Handle commands received from other internal Python agents.
|
Handle commands received from other internal Python agents.
|
||||||
"""
|
"""
|
||||||
match msg.thread:
|
match msg.thread:
|
||||||
|
case "new_program":
|
||||||
|
self._create_mapping(msg.body)
|
||||||
|
case "trigger_start":
|
||||||
|
# msg.body is the sluggified trigger
|
||||||
|
asl_slug = msg.body
|
||||||
|
ui_id = self._trigger_reverse_map.get(asl_slug)
|
||||||
|
|
||||||
|
if ui_id:
|
||||||
|
payload = {"type": "trigger_update", "id": ui_id, "achieved": True}
|
||||||
|
await self._send_experiment_update(payload)
|
||||||
|
self.logger.info(f"UI Update: Trigger {asl_slug} started (ID: {ui_id})")
|
||||||
|
|
||||||
|
case "trigger_end":
|
||||||
|
asl_slug = msg.body
|
||||||
|
ui_id = self._trigger_reverse_map.get(asl_slug)
|
||||||
|
|
||||||
|
if ui_id:
|
||||||
|
payload = {"type": "trigger_update", "id": ui_id, "achieved": False}
|
||||||
|
await self._send_experiment_update(payload)
|
||||||
|
self.logger.info(f"UI Update: Trigger {asl_slug} ended (ID: {ui_id})")
|
||||||
case "transition_phase":
|
case "transition_phase":
|
||||||
new_phase_id = msg.body
|
new_phase_id = msg.body
|
||||||
self.logger.info(f"Phase transition detected: {new_phase_id}")
|
self.logger.info(f"Phase transition detected: {new_phase_id}")
|
||||||
@@ -111,6 +151,10 @@ class UserInterruptAgent(BaseAgent):
|
|||||||
payload = {"type": "phase_update", "phase_id": new_phase_id}
|
payload = {"type": "phase_update", "phase_id": new_phase_id}
|
||||||
|
|
||||||
await self._send_experiment_update(payload)
|
await self._send_experiment_update(payload)
|
||||||
|
case "active_norms_update":
|
||||||
|
asl_slugs = [s.strip() for s in msg.body.split(";")]
|
||||||
|
|
||||||
|
await self._broadcast_cond_norms(asl_slugs)
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
self.logger.debug(f"Received internal message on unhandled thread: {msg.thread}")
|
self.logger.debug(f"Received internal message on unhandled thread: {msg.thread}")
|
||||||
@@ -132,6 +176,54 @@ class UserInterruptAgent(BaseAgent):
|
|||||||
|
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
|
|
||||||
|
async def _broadcast_cond_norms(self, active_slugs: list[str]):
|
||||||
|
"""
|
||||||
|
Sends the current state of all conditional norms to the UI.
|
||||||
|
:param active_slugs: A list of slugs (strings) currently active in the BDI core.
|
||||||
|
"""
|
||||||
|
updates = []
|
||||||
|
|
||||||
|
for asl_slug, ui_id in self._cond_norm_reverse_map.items():
|
||||||
|
is_active = asl_slug in active_slugs
|
||||||
|
updates.append({"id": ui_id, "name": asl_slug, "active": is_active})
|
||||||
|
|
||||||
|
payload = {"type": "cond_norms_state_update", "norms": updates}
|
||||||
|
|
||||||
|
await self._send_experiment_update(payload)
|
||||||
|
self.logger.debug(f"Broadcasted state for {len(updates)} conditional norms.")
|
||||||
|
|
||||||
|
def _create_mapping(self, program_json: str):
|
||||||
|
try:
|
||||||
|
program = Program.model_validate_json(program_json)
|
||||||
|
self._trigger_map = {}
|
||||||
|
self._trigger_reverse_map = {}
|
||||||
|
self._goal_map = {}
|
||||||
|
self._cond_norm_map = {}
|
||||||
|
self._cond_norm_reverse_map = {}
|
||||||
|
|
||||||
|
for phase in program.phases:
|
||||||
|
for trigger in phase.triggers:
|
||||||
|
slug = AgentSpeakGenerator.slugify(trigger)
|
||||||
|
self._trigger_map[str(trigger.id)] = slug
|
||||||
|
self._trigger_reverse_map[slug] = str(trigger.id)
|
||||||
|
|
||||||
|
for goal in phase.goals:
|
||||||
|
self._goal_map[str(goal.id)] = AgentSpeakGenerator.slugify(goal)
|
||||||
|
|
||||||
|
for norm in phase.conditional_norms:
|
||||||
|
if norm.condition:
|
||||||
|
asl_slug = AgentSpeakGenerator.slugify(norm)
|
||||||
|
belief_id = str(norm.condition)
|
||||||
|
|
||||||
|
self._cond_norm_map[belief_id] = asl_slug
|
||||||
|
self._cond_norm_reverse_map[asl_slug] = belief_id
|
||||||
|
|
||||||
|
self.logger.info(
|
||||||
|
f"Mapped {len(self._trigger_map)} triggers and {len(self._goal_map)} goals."
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.error(f"Mapping failed: {e}")
|
||||||
|
|
||||||
async def _send_experiment_update(self, data):
|
async def _send_experiment_update(self, data):
|
||||||
"""
|
"""
|
||||||
Sends an update to the 'experiment' topic.
|
Sends an update to the 'experiment' topic.
|
||||||
@@ -194,3 +286,11 @@ class UserInterruptAgent(BaseAgent):
|
|||||||
"Sent button_override belief with id '%s' to Program manager.",
|
"Sent button_override belief with id '%s' to Program manager.",
|
||||||
belief_id,
|
belief_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async def _send_to_bdi(self, thread: str, body: str):
|
||||||
|
"""Send slug of trigger to BDI"""
|
||||||
|
msg = InternalMessage(
|
||||||
|
to=settings.agent_settings.bdi_core_name, sender=self.name, thread=thread, body=body
|
||||||
|
)
|
||||||
|
await self.send(msg)
|
||||||
|
self.logger.info(f"Directly forced {thread} in BDI: {body}")
|
||||||
|
|||||||
Reference in New Issue
Block a user