fix: made beliefs a dict of lists

[
    Before it was a list of a dict of lists of lists of strings
    now it is a dict of lists of lists of strings as prescribed by architecture (knowledge base)
    *also added some tests, but will have to add some more
]

[ref]: N25B-206
This commit is contained in:
Pim Hutting
2025-10-28 14:17:07 +01:00
parent 1f34b14dfa
commit 2efce93a37
3 changed files with 110 additions and 12 deletions

View File

@@ -1,7 +1,7 @@
import json
import logging
from spade.behaviour import CyclicBehaviour
from spade.message import Message
from spade.agent import Message
from control_backend.core.config import settings
logger = logging.getLogger(__name__)
@@ -62,20 +62,29 @@ class ContinuousBeliefCollector(CyclicBehaviour):
Expected payload:
{
"type": "belief_extraction_text",
"beliefs": [
{"user_said": [["hello"],["Can you help me?"],["stop talking to me"],["No"],["Pepper do a dance"]]}
]
"beliefs": {"user_said": [["hello","test"],["Can you help me?"],["stop talking to me"],["No"],["Pepper do a dance"]]}
}
"""
beliefs = payload.get("beliefs", [])
if not isinstance(beliefs, list):
logger.warning("BeliefCollector: 'beliefs' is not a list: %r", beliefs)
beliefs = payload.get("beliefs", dict)
if not beliefs:
logger.info("BeliefCollector: no beliefs to process.")
return
if not isinstance(beliefs, dict):
logger.warning("BeliefCollector: 'beliefs' is not a dict: %r", beliefs)
return
if not all(isinstance(v, list) for v in beliefs.values()):
logger.warning("BeliefCollector: 'beliefs' values are not all lists: %r", beliefs)
return
logger.info("BeliefCollector: forwarding %d beliefs.", len(beliefs))
for b in beliefs:
logger.info(" - %s", b)
for belief_name, belief_lists in beliefs.items():
for args in belief_lists:
logger.info(" - %s %s", belief_name, " ".join(map(str, args)))
await self._send_beliefs_to_bdi(beliefs, origin=origin)

View File

@@ -12,9 +12,8 @@ class BeliefTextAgent(Agent):
# Send multiple beliefs in one JSON payload
payload = {
"type": "belief_extraction_text",
"beliefs": [
{"user_said": [["hello"],["Can you help me?"],["stop talking to me"],["No"],["Pepper do a dance"]]}
]
"beliefs": {"user_said": [["hello","test"],["Can you help me?"],["stop talking to me"],["No"],["Pepper do a dance"]]}
}
msg = Message(to=to_jid)

View File

@@ -0,0 +1,90 @@
import json
import logging
from unittest.mock import MagicMock, AsyncMock, call
import pytest
from control_backend.agents.belief_collector.behaviours.continuous_collect import ContinuousBeliefCollector
@pytest.fixture
def mock_agent(mocker):
"""Fixture to create a mock Agent."""
agent = MagicMock()
agent.jid = "belief_collector_agent@test"
return agent
@pytest.fixture
def continuous_collector(mock_agent, mocker):
"""Fixture to create an instance of ContinuousBeliefCollector with a mocked agent."""
# Patch asyncio.sleep to prevent tests from actually waiting
mocker.patch("asyncio.sleep", return_value=None)
collector = ContinuousBeliefCollector()
collector.agent = mock_agent
# Mock the receive method, we will control its return value in each test
collector.receive = AsyncMock()
return collector
@pytest.mark.asyncio
async def test_run_no_message_received(continuous_collector, mocker):
"""
Test that when no message is received, _process_message is not called.
"""
# Arrange
continuous_collector.receive.return_value = None
mocker.patch.object(continuous_collector, "_process_message")
# Act
await continuous_collector.run()
# Assert
continuous_collector._process_message.assert_not_called()
@pytest.mark.asyncio
async def test_run_message_received(continuous_collector, mocker):
"""
Test that when a message is received, _process_message is called with that message.
"""
# Arrange
mock_msg = MagicMock()
continuous_collector.receive.return_value = mock_msg
mocker.patch.object(continuous_collector, "_process_message")
# Act
await continuous_collector.run()
# Assert
continuous_collector._process_message.assert_awaited_once_with(mock_msg)
@pytest.mark.asyncio
async def test_process_message_invalid(continuous_collector, mocker):
"""
Test that when an invalid JSON message is received, a warning is logged and processing stops.
"""
# Arrange
invalid_json = "this is not json"
msg = MagicMock()
msg.body = invalid_json
msg.sender = "belief_text_agent_mock@test"
logger_mock = mocker.patch("control_backend.agents.belief_collector.behaviours.continuous_collect.logger")
# Act
await continuous_collector._process_message(msg)
# Assert
logger_mock.warning.assert_called_once()
def test_get_sender_from_message(continuous_collector):
"""
Test that _sender_node correctly extracts the sender node from the message JID.
"""
# Arrange
msg = MagicMock()
msg.sender = "agent_node@host/resource"
# Act
sender_node = continuous_collector._sender_node(msg)
# Assert
assert sender_node == "agent_node"