feat: create the program manager agent to interpret programs from the UI
Extracts norms and goals and sends these to the BDI agent. ref: N25B-200
This commit is contained in:
@@ -0,0 +1,27 @@
|
|||||||
|
import zmq
|
||||||
|
from zmq.asyncio import Context
|
||||||
|
|
||||||
|
from control_backend.agents import BaseAgent
|
||||||
|
from control_backend.core.config import settings
|
||||||
|
|
||||||
|
from .receive_programs_behavior import ReceiveProgramsBehavior
|
||||||
|
|
||||||
|
|
||||||
|
class BDIProgramManager(BaseAgent):
|
||||||
|
"""
|
||||||
|
Will interpret programs received from the HTTP endpoint. Extracts norms, goals, triggers and
|
||||||
|
forwards them to the BDI as beliefs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.sub_socket = None
|
||||||
|
|
||||||
|
async def setup(self):
|
||||||
|
context = Context.instance()
|
||||||
|
|
||||||
|
self.sub_socket = context.socket(zmq.SUB)
|
||||||
|
self.sub_socket.connect(settings.zmq_settings.internal_sub_address)
|
||||||
|
self.sub_socket.subscribe("program")
|
||||||
|
|
||||||
|
self.add_behaviour(ReceiveProgramsBehavior())
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from pydantic import ValidationError
|
||||||
|
from spade.behaviour import CyclicBehaviour
|
||||||
|
from spade.message import Message
|
||||||
|
|
||||||
|
from control_backend.core.config import settings
|
||||||
|
from control_backend.schemas.program import Program
|
||||||
|
|
||||||
|
|
||||||
|
class ReceiveProgramsBehavior(CyclicBehaviour):
|
||||||
|
async def _receive(self) -> Program | None:
|
||||||
|
topic, body = await self.agent.sub_socket.recv_multipart()
|
||||||
|
|
||||||
|
try:
|
||||||
|
return Program.model_validate_json(body)
|
||||||
|
except ValidationError as e:
|
||||||
|
self.agent.logger.error("Received an invalid program.", exc_info=e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _extract_norms(self, program: Program) -> str:
|
||||||
|
"""First phase only for now, as a single newline delimited string."""
|
||||||
|
if not program.phases:
|
||||||
|
return ""
|
||||||
|
if not program.phases[0].phaseData.norms:
|
||||||
|
return ""
|
||||||
|
norm_values = [norm.value for norm in program.phases[0].phaseData.norms]
|
||||||
|
return "\n".join(norm_values)
|
||||||
|
|
||||||
|
def _extract_goals(self, program: Program) -> str:
|
||||||
|
"""First phase only for now, as a single newline delimited string."""
|
||||||
|
if not program.phases:
|
||||||
|
return ""
|
||||||
|
if not program.phases[0].phaseData.goals:
|
||||||
|
return ""
|
||||||
|
goal_descriptions = [goal.description for goal in program.phases[0].phaseData.goals]
|
||||||
|
return "\n".join(goal_descriptions)
|
||||||
|
|
||||||
|
async def _send_to_bdi(self, program: Program):
|
||||||
|
temp_allowed_parts = {
|
||||||
|
"norms": [self._extract_norms(program)],
|
||||||
|
"goals": [self._extract_goals(program)],
|
||||||
|
}
|
||||||
|
|
||||||
|
message = Message(
|
||||||
|
to=settings.agent_settings.bdi_core_agent_name + "@" + settings.agent_settings.host,
|
||||||
|
sender=self.agent.jid,
|
||||||
|
body=json.dumps(temp_allowed_parts),
|
||||||
|
thread="beliefs",
|
||||||
|
)
|
||||||
|
await self.send(message)
|
||||||
|
self.agent.logger.debug("Sent new norms and goals to the BDI agent.")
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
program = await self._receive()
|
||||||
|
if not program:
|
||||||
|
return
|
||||||
|
|
||||||
|
await self._send_to_bdi(program)
|
||||||
@@ -39,7 +39,7 @@ class BeliefSetterBehaviour(CyclicBehaviour):
|
|||||||
"Message is from the belief collector agent. Processing as belief message."
|
"Message is from the belief collector agent. Processing as belief message."
|
||||||
)
|
)
|
||||||
self._process_belief_message(message)
|
self._process_belief_message(message)
|
||||||
case settings.agent_settings.program_manager_name:
|
case settings.agent_settings.program_manager_agent_name:
|
||||||
self.agent.logger.debug(
|
self.agent.logger.debug(
|
||||||
"Processing message from the program manager. Processing as belief message."
|
"Processing message from the program manager. Processing as belief message."
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ class RICommunicationAgent(BaseAgent):
|
|||||||
settings.agent_settings.ri_command_agent_name
|
settings.agent_settings.ri_command_agent_name
|
||||||
+ "@"
|
+ "@"
|
||||||
+ settings.agent_settings.host,
|
+ settings.agent_settings.host,
|
||||||
"pohpu7-huqsyH-qutduk",
|
settings.agent_settings.ri_command_agent_name,
|
||||||
address=addr,
|
address=addr,
|
||||||
bind=bind,
|
bind=bind,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class AgentSettings(BaseModel):
|
|||||||
llm_agent_name: str = "llm_agent"
|
llm_agent_name: str = "llm_agent"
|
||||||
test_agent_name: str = "test_agent"
|
test_agent_name: str = "test_agent"
|
||||||
transcription_agent_name: str = "transcription_agent"
|
transcription_agent_name: str = "transcription_agent"
|
||||||
|
program_manager_agent_name: str = "program_manager"
|
||||||
|
|
||||||
ri_communication_agent_name: str = "ri_communication_agent"
|
ri_communication_agent_name: str = "ri_communication_agent"
|
||||||
ri_command_agent_name: str = "ri_command_agent"
|
ri_command_agent_name: str = "ri_command_agent"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from control_backend.agents import (
|
|||||||
VADAgent,
|
VADAgent,
|
||||||
)
|
)
|
||||||
from control_backend.agents.bdi import BDICoreAgent, TBeliefExtractorAgent
|
from control_backend.agents.bdi import BDICoreAgent, TBeliefExtractorAgent
|
||||||
|
from control_backend.agents.bdi.bdi_program_manager.bdi_program_manager import BDIProgramManager
|
||||||
from control_backend.api.v1.router import api_router
|
from control_backend.api.v1.router import api_router
|
||||||
from control_backend.core.config import settings
|
from control_backend.core.config import settings
|
||||||
from control_backend.logging import setup_logging
|
from control_backend.logging import setup_logging
|
||||||
@@ -115,6 +116,15 @@ async def lifespan(app: FastAPI):
|
|||||||
VADAgent,
|
VADAgent,
|
||||||
{"audio_in_address": "tcp://localhost:5558", "audio_in_bind": False},
|
{"audio_in_address": "tcp://localhost:5558", "audio_in_bind": False},
|
||||||
),
|
),
|
||||||
|
"ProgramManager": (
|
||||||
|
BDIProgramManager,
|
||||||
|
{
|
||||||
|
"name": settings.agent_settings.program_manager_agent_name,
|
||||||
|
"jid": f"{settings.agent_settings.program_manager_agent_name}@"
|
||||||
|
f"{settings.agent_settings.host}",
|
||||||
|
"password": settings.agent_settings.program_manager_agent_name,
|
||||||
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
vad_agent_instance = None
|
vad_agent_instance = None
|
||||||
|
|||||||
Reference in New Issue
Block a user