Merge remote-tracking branch 'origin/feat/extra-agentspeak-functionality' into feat/monitoringpage-cb
This commit is contained in:
@@ -10,7 +10,7 @@ LLM_SETTINGS__LOCAL_LLM_URL="http://localhost:1234/v1/chat/completions"
|
||||
LLM_SETTINGS__LOCAL_LLM_MODEL="gpt-oss"
|
||||
|
||||
# Number of non-speech chunks to wait before speech ended. A chunk is approximately 31 ms. Increasing this number allows longer pauses in speech, but also increases response time.
|
||||
BEHAVIOUR_SETTINGS__VAD_NON_SPEECH_PATIENCE_CHUNKS=3
|
||||
BEHAVIOUR_SETTINGS__VAD_NON_SPEECH_PATIENCE_CHUNKS=15
|
||||
|
||||
# Timeout in milliseconds for socket polling. Increase this number if network latency/jitter is high, often the case when using Wi-Fi. Perhaps 500 ms. A symptom of this issue is transcriptions getting cut off.
|
||||
BEHAVIOUR_SETTINGS__SOCKET_POLLER_TIMEOUT_MS=100
|
||||
|
||||
@@ -145,7 +145,10 @@ class AgentSpeakGenerator:
|
||||
type=TriggerType.ADDED_BELIEF,
|
||||
trigger_literal=AstLiteral("user_said", [AstVar("Message")]),
|
||||
context=[AstLiteral("phase", [AstString("end")])],
|
||||
body=[AstStatement(StatementType.ACHIEVE_GOAL, AstLiteral("reply"))],
|
||||
body=[
|
||||
AstStatement(StatementType.DO_ACTION, AstLiteral("notify_user_said")),
|
||||
AstStatement(StatementType.ACHIEVE_GOAL, AstLiteral("reply")),
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
@@ -171,9 +174,10 @@ class AgentSpeakGenerator:
|
||||
self._astify(to_phase) if to_phase else AstLiteral("phase", [AstString("end")])
|
||||
)
|
||||
|
||||
context = [from_phase_ast, ~AstLiteral("responded_this_turn")]
|
||||
if from_phase and from_phase.goals:
|
||||
context.append(self._astify(from_phase.goals[-1], achieved=True))
|
||||
context = [from_phase_ast]
|
||||
if from_phase:
|
||||
for goal in from_phase.goals:
|
||||
context.append(self._astify(goal, achieved=True))
|
||||
|
||||
body = [
|
||||
AstStatement(
|
||||
@@ -190,17 +194,17 @@ class AgentSpeakGenerator:
|
||||
AstStatement(StatementType.ADD_BELIEF, to_phase_ast),
|
||||
]
|
||||
|
||||
if from_phase:
|
||||
body.extend(
|
||||
[
|
||||
AstStatement(
|
||||
StatementType.TEST_GOAL, AstLiteral("user_said", [AstVar("Message")])
|
||||
),
|
||||
AstStatement(
|
||||
StatementType.REPLACE_BELIEF, AstLiteral("user_said", [AstVar("Message")])
|
||||
),
|
||||
]
|
||||
)
|
||||
# if from_phase:
|
||||
# body.extend(
|
||||
# [
|
||||
# AstStatement(
|
||||
# StatementType.TEST_GOAL, AstLiteral("user_said", [AstVar("Message")])
|
||||
# ),
|
||||
# AstStatement(
|
||||
# StatementType.REPLACE_BELIEF, AstLiteral("user_said", [AstVar("Message")])
|
||||
# ),
|
||||
# ]
|
||||
# )
|
||||
|
||||
self._asp.plans.append(
|
||||
AstPlan(TriggerType.ADDED_GOAL, AstLiteral("transition_phase"), context, body)
|
||||
|
||||
@@ -101,7 +101,6 @@ class BDICoreAgent(BaseAgent):
|
||||
maybe_more_work = True
|
||||
while maybe_more_work:
|
||||
maybe_more_work = False
|
||||
self.logger.debug("Stepping BDI.")
|
||||
if self.bdi_agent.step():
|
||||
maybe_more_work = True
|
||||
|
||||
@@ -213,6 +212,22 @@ class BDICoreAgent(BaseAgent):
|
||||
agentspeak.runtime.Intention(),
|
||||
)
|
||||
|
||||
# Check for transitions
|
||||
self.bdi_agent.call(
|
||||
agentspeak.Trigger.addition,
|
||||
agentspeak.GoalType.achievement,
|
||||
agentspeak.Literal("transition_phase"),
|
||||
agentspeak.runtime.Intention(),
|
||||
)
|
||||
|
||||
# Check triggers
|
||||
self.bdi_agent.call(
|
||||
agentspeak.Trigger.addition,
|
||||
agentspeak.GoalType.achievement,
|
||||
agentspeak.Literal("check_triggers"),
|
||||
agentspeak.runtime.Intention(),
|
||||
)
|
||||
|
||||
self._wake_bdi_loop.set()
|
||||
|
||||
self.logger.debug(f"Added belief {self.format_belief_string(name, args)}")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
norms("").
|
||||
|
||||
+user_said(Message) : norms(Norms) <-
|
||||
.notify_user_said(Message);
|
||||
-user_said(Message);
|
||||
.reply(Message, Norms).
|
||||
|
||||
@@ -90,7 +90,7 @@ class TextBeliefExtractorAgent(BaseAgent):
|
||||
self.logger.debug("Received text from LLM: %s", msg.body)
|
||||
self._apply_conversation_message(ChatMessage(role="assistant", content=msg.body))
|
||||
case settings.agent_settings.bdi_program_manager_name:
|
||||
self._handle_program_manager_message(msg)
|
||||
await self._handle_program_manager_message(msg)
|
||||
case _:
|
||||
self.logger.info("Discarding message from %s", sender)
|
||||
return
|
||||
@@ -105,7 +105,7 @@ class TextBeliefExtractorAgent(BaseAgent):
|
||||
length_limit = settings.behaviour_settings.conversation_history_length_limit
|
||||
self.conversation.messages = (self.conversation.messages + [message])[-length_limit:]
|
||||
|
||||
def _handle_program_manager_message(self, msg: InternalMessage):
|
||||
async def _handle_program_manager_message(self, msg: InternalMessage):
|
||||
"""
|
||||
Handle a message from the program manager: extract available beliefs and goals from it.
|
||||
|
||||
@@ -114,8 +114,10 @@ class TextBeliefExtractorAgent(BaseAgent):
|
||||
match msg.thread:
|
||||
case "beliefs":
|
||||
self._handle_beliefs_message(msg)
|
||||
await self._infer_new_beliefs()
|
||||
case "goals":
|
||||
self._handle_goals_message(msg)
|
||||
await self._infer_goal_completions()
|
||||
case "conversation_history":
|
||||
if msg.body == "reset":
|
||||
self._reset()
|
||||
@@ -141,8 +143,9 @@ class TextBeliefExtractorAgent(BaseAgent):
|
||||
available_beliefs = [b for b in belief_list.beliefs if isinstance(b, SemanticBelief)]
|
||||
self.belief_inferrer.available_beliefs = available_beliefs
|
||||
self.logger.debug(
|
||||
"Received %d semantic beliefs from the program manager.",
|
||||
"Received %d semantic beliefs from the program manager: %s",
|
||||
len(available_beliefs),
|
||||
", ".join(b.name for b in available_beliefs),
|
||||
)
|
||||
|
||||
def _handle_goals_message(self, msg: InternalMessage):
|
||||
@@ -158,8 +161,9 @@ class TextBeliefExtractorAgent(BaseAgent):
|
||||
available_goals = [g for g in goals_list.goals if g.can_fail]
|
||||
self.goal_inferrer.goals = available_goals
|
||||
self.logger.debug(
|
||||
"Received %d failable goals from the program manager.",
|
||||
"Received %d failable goals from the program manager: %s",
|
||||
len(available_goals),
|
||||
", ".join(g.name for g in available_goals),
|
||||
)
|
||||
|
||||
async def _user_said(self, text: str):
|
||||
@@ -183,6 +187,7 @@ class TextBeliefExtractorAgent(BaseAgent):
|
||||
|
||||
new_beliefs = conversation_beliefs - self._current_beliefs
|
||||
if not new_beliefs:
|
||||
self.logger.debug("No new beliefs detected.")
|
||||
return
|
||||
|
||||
self._current_beliefs |= new_beliefs
|
||||
@@ -217,6 +222,7 @@ class TextBeliefExtractorAgent(BaseAgent):
|
||||
self._current_goal_completions[goal] = achieved
|
||||
|
||||
if not new_achieved and not new_not_achieved:
|
||||
self.logger.debug("No goal achievement changes detected.")
|
||||
return
|
||||
|
||||
belief_changes = BeliefMessage(
|
||||
|
||||
@@ -248,7 +248,7 @@ class RICommunicationAgent(BaseAgent):
|
||||
self._req_socket.recv_json(), timeout=seconds_to_wait_total / 2
|
||||
)
|
||||
|
||||
if message["endpoint"] and message["endpoint"] != "ping":
|
||||
if "endpoint" in message and message["endpoint"] != "ping":
|
||||
self.logger.debug(f'Received message "{message}" from RI.')
|
||||
if "endpoint" not in message:
|
||||
self.logger.warning("No received endpoint in message, expected ping endpoint.")
|
||||
|
||||
@@ -229,10 +229,11 @@ class VADAgent(BaseAgent):
|
||||
assert self.model is not None
|
||||
prob = self.model(torch.from_numpy(chunk), settings.vad_settings.sample_rate_hz).item()
|
||||
non_speech_patience = settings.behaviour_settings.vad_non_speech_patience_chunks
|
||||
begin_silence_length = settings.behaviour_settings.vad_begin_silence_chunks
|
||||
prob_threshold = settings.behaviour_settings.vad_prob_threshold
|
||||
|
||||
if prob > prob_threshold:
|
||||
if self.i_since_speech > non_speech_patience:
|
||||
if self.i_since_speech > non_speech_patience + begin_silence_length:
|
||||
self.logger.debug("Speech started.")
|
||||
self.audio_buffer = np.append(self.audio_buffer, chunk)
|
||||
self.i_since_speech = 0
|
||||
@@ -246,11 +247,12 @@ class VADAgent(BaseAgent):
|
||||
continue
|
||||
|
||||
# Speech probably ended. Make sure we have a usable amount of data.
|
||||
if len(self.audio_buffer) >= 3 * len(chunk):
|
||||
if len(self.audio_buffer) > begin_silence_length * len(chunk):
|
||||
self.logger.debug("Speech ended.")
|
||||
assert self.audio_out_socket is not None
|
||||
await self.audio_out_socket.send(self.audio_buffer[: -2 * len(chunk)].tobytes())
|
||||
|
||||
# At this point, we know that the speech has ended.
|
||||
# Prepend the last chunk that had no speech, for a more fluent boundary
|
||||
self.audio_buffer = chunk
|
||||
# At this point, we know that there is no speech.
|
||||
# Prepend the last few chunks that had no speech, for a more fluent boundary.
|
||||
self.audio_buffer = np.append(self.audio_buffer, chunk)
|
||||
self.audio_buffer = self.audio_buffer[-begin_silence_length * len(chunk) :]
|
||||
|
||||
@@ -73,6 +73,7 @@ class BehaviourSettings(BaseModel):
|
||||
:ivar vad_prob_threshold: Probability threshold for Voice Activity Detection.
|
||||
:ivar vad_initial_since_speech: Initial value for 'since speech' counter in VAD.
|
||||
:ivar vad_non_speech_patience_chunks: Number of non-speech chunks to wait before speech ended.
|
||||
:ivar vad_begin_silence_chunks: The number of chunks of silence to prepend to speech chunks.
|
||||
:ivar transcription_max_concurrent_tasks: Maximum number of concurrent transcription tasks.
|
||||
:ivar transcription_words_per_minute: Estimated words per minute for transcription timing.
|
||||
:ivar transcription_words_per_token: Estimated words per token for transcription timing.
|
||||
@@ -90,6 +91,7 @@ class BehaviourSettings(BaseModel):
|
||||
vad_prob_threshold: float = 0.5
|
||||
vad_initial_since_speech: int = 100
|
||||
vad_non_speech_patience_chunks: int = 15
|
||||
vad_begin_silence_chunks: int = 6
|
||||
|
||||
# transcription behaviour
|
||||
transcription_max_concurrent_tasks: int = 3
|
||||
|
||||
@@ -180,7 +180,6 @@ class Trigger(ProgramElement):
|
||||
:ivar plan: The plan to execute.
|
||||
"""
|
||||
|
||||
name: str = ""
|
||||
condition: Belief
|
||||
plan: Plan
|
||||
|
||||
|
||||
@@ -110,8 +110,8 @@ async def test_send_clear_llm_history(mock_settings):
|
||||
|
||||
await manager._send_clear_llm_history()
|
||||
|
||||
assert manager.send.await_count == 1
|
||||
msg: InternalMessage = manager.send.await_args[0][0]
|
||||
assert manager.send.await_count == 2
|
||||
msg: InternalMessage = manager.send.await_args_list[0][0][0]
|
||||
|
||||
# Verify the content and recipient
|
||||
assert msg.body == "clear_history"
|
||||
|
||||
@@ -6,10 +6,13 @@ import httpx
|
||||
import pytest
|
||||
|
||||
from control_backend.agents.bdi import TextBeliefExtractorAgent
|
||||
from control_backend.agents.bdi.text_belief_extractor_agent import BeliefState
|
||||
from control_backend.core.agent_system import InternalMessage
|
||||
from control_backend.core.config import settings
|
||||
from control_backend.schemas.belief_list import BeliefList
|
||||
from control_backend.schemas.belief_message import Belief as InternalBelief
|
||||
from control_backend.schemas.belief_message import BeliefMessage
|
||||
from control_backend.schemas.chat_history import ChatHistory, ChatMessage
|
||||
from control_backend.schemas.program import (
|
||||
ConditionalNorm,
|
||||
KeywordBelief,
|
||||
@@ -23,11 +26,21 @@ from control_backend.schemas.program import (
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def agent():
|
||||
agent = TextBeliefExtractorAgent("text_belief_agent")
|
||||
agent.send = AsyncMock()
|
||||
agent._query_llm = AsyncMock()
|
||||
return agent
|
||||
def llm():
|
||||
llm = TextBeliefExtractorAgent.LLM(MagicMock(), 4)
|
||||
llm._query_llm = AsyncMock()
|
||||
return llm
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def agent(llm):
|
||||
with patch(
|
||||
"control_backend.agents.bdi.text_belief_extractor_agent.TextBeliefExtractorAgent.LLM",
|
||||
return_value=llm,
|
||||
):
|
||||
agent = TextBeliefExtractorAgent("text_belief_agent")
|
||||
agent.send = AsyncMock()
|
||||
return agent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -102,24 +115,12 @@ async def test_handle_message_from_transcriber(agent, mock_settings):
|
||||
|
||||
agent.send.assert_awaited_once() # noqa # `agent.send` has no such property, but we mock it.
|
||||
sent: InternalMessage = agent.send.call_args.args[0] # noqa
|
||||
assert sent.to == mock_settings.agent_settings.bdi_belief_collector_name
|
||||
assert sent.to == mock_settings.agent_settings.bdi_core_name
|
||||
assert sent.thread == "beliefs"
|
||||
parsed = json.loads(sent.body)
|
||||
assert parsed == {"beliefs": {"user_said": [transcription]}, "type": "belief_extraction_text"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_process_user_said(agent, mock_settings):
|
||||
transcription = "this is a test"
|
||||
|
||||
await agent._user_said(transcription)
|
||||
|
||||
agent.send.assert_awaited_once() # noqa # `agent.send` has no such property, but we mock it.
|
||||
sent: InternalMessage = agent.send.call_args.args[0] # noqa
|
||||
assert sent.to == mock_settings.agent_settings.bdi_belief_collector_name
|
||||
assert sent.thread == "beliefs"
|
||||
parsed = json.loads(sent.body)
|
||||
assert parsed["beliefs"]["user_said"] == [transcription]
|
||||
parsed = BeliefMessage.model_validate_json(sent.body)
|
||||
replaced_last = parsed.replace.pop()
|
||||
assert replaced_last.name == "user_said"
|
||||
assert replaced_last.arguments == [transcription]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -144,46 +145,46 @@ async def test_query_llm():
|
||||
"control_backend.agents.bdi.text_belief_extractor_agent.httpx.AsyncClient",
|
||||
return_value=mock_async_client,
|
||||
):
|
||||
agent = TextBeliefExtractorAgent("text_belief_agent")
|
||||
llm = TextBeliefExtractorAgent.LLM(MagicMock(), 4)
|
||||
|
||||
res = await agent._query_llm("hello world", {"type": "null"})
|
||||
res = await llm._query_llm("hello world", {"type": "null"})
|
||||
# Response content was set as "null", so should be deserialized as None
|
||||
assert res is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_retry_query_llm_success(agent):
|
||||
agent._query_llm.return_value = None
|
||||
res = await agent._retry_query_llm("hello world", {"type": "null"})
|
||||
async def test_retry_query_llm_success(llm):
|
||||
llm._query_llm.return_value = None
|
||||
res = await llm.query("hello world", {"type": "null"})
|
||||
|
||||
agent._query_llm.assert_called_once()
|
||||
llm._query_llm.assert_called_once()
|
||||
assert res is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_retry_query_llm_success_after_failure(agent):
|
||||
agent._query_llm.side_effect = [KeyError(), "real value"]
|
||||
res = await agent._retry_query_llm("hello world", {"type": "string"})
|
||||
async def test_retry_query_llm_success_after_failure(llm):
|
||||
llm._query_llm.side_effect = [KeyError(), "real value"]
|
||||
res = await llm.query("hello world", {"type": "string"})
|
||||
|
||||
assert agent._query_llm.call_count == 2
|
||||
assert llm._query_llm.call_count == 2
|
||||
assert res == "real value"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_retry_query_llm_failures(agent):
|
||||
agent._query_llm.side_effect = [KeyError(), KeyError(), KeyError(), "real value"]
|
||||
res = await agent._retry_query_llm("hello world", {"type": "string"})
|
||||
async def test_retry_query_llm_failures(llm):
|
||||
llm._query_llm.side_effect = [KeyError(), KeyError(), KeyError(), "real value"]
|
||||
res = await llm.query("hello world", {"type": "string"})
|
||||
|
||||
assert agent._query_llm.call_count == 3
|
||||
assert llm._query_llm.call_count == 3
|
||||
assert res is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_retry_query_llm_fail_immediately(agent):
|
||||
agent._query_llm.side_effect = [KeyError(), "real value"]
|
||||
res = await agent._retry_query_llm("hello world", {"type": "string"}, tries=1)
|
||||
async def test_retry_query_llm_fail_immediately(llm):
|
||||
llm._query_llm.side_effect = [KeyError(), "real value"]
|
||||
res = await llm.query("hello world", {"type": "string"}, tries=1)
|
||||
|
||||
assert agent._query_llm.call_count == 1
|
||||
assert llm._query_llm.call_count == 1
|
||||
assert res is None
|
||||
|
||||
|
||||
@@ -192,7 +193,7 @@ async def test_extracting_semantic_beliefs(agent):
|
||||
"""
|
||||
The Program Manager sends beliefs to this agent. Test whether the agent handles them correctly.
|
||||
"""
|
||||
assert len(agent.available_beliefs) == 0
|
||||
assert len(agent.belief_inferrer.available_beliefs) == 0
|
||||
beliefs = BeliefList(
|
||||
beliefs=[
|
||||
KeywordBelief(
|
||||
@@ -213,26 +214,28 @@ async def test_extracting_semantic_beliefs(agent):
|
||||
to=settings.agent_settings.text_belief_extractor_name,
|
||||
sender=settings.agent_settings.bdi_program_manager_name,
|
||||
body=beliefs.model_dump_json(),
|
||||
thread="beliefs",
|
||||
),
|
||||
)
|
||||
assert len(agent.available_beliefs) == 2
|
||||
assert len(agent.belief_inferrer.available_beliefs) == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_invalid_program(agent, sample_program):
|
||||
agent.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
assert len(agent.available_beliefs) == 2
|
||||
async def test_handle_invalid_beliefs(agent, sample_program):
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
assert len(agent.belief_inferrer.available_beliefs) == 2
|
||||
|
||||
await agent.handle_message(
|
||||
InternalMessage(
|
||||
to=settings.agent_settings.text_belief_extractor_name,
|
||||
sender=settings.agent_settings.bdi_program_manager_name,
|
||||
body=json.dumps({"phases": "Invalid"}),
|
||||
thread="beliefs",
|
||||
),
|
||||
)
|
||||
|
||||
assert len(agent.available_beliefs) == 2
|
||||
assert len(agent.belief_inferrer.available_beliefs) == 2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -254,13 +257,13 @@ async def test_handle_robot_response(agent):
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simulated_real_turn_with_beliefs(agent, sample_program):
|
||||
async def test_simulated_real_turn_with_beliefs(agent, llm, sample_program):
|
||||
"""Test sending user message to extract beliefs from."""
|
||||
agent.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
|
||||
# Send a user message with the belief that there's no more booze
|
||||
agent._query_llm.return_value = {"is_pirate": None, "no_more_booze": True}
|
||||
llm._query_llm.return_value = {"is_pirate": None, "no_more_booze": True}
|
||||
assert len(agent.conversation.messages) == 0
|
||||
await agent.handle_message(
|
||||
InternalMessage(
|
||||
@@ -275,20 +278,20 @@ async def test_simulated_real_turn_with_beliefs(agent, sample_program):
|
||||
assert agent.send.call_count == 2
|
||||
|
||||
# First should be the beliefs message
|
||||
message: InternalMessage = agent.send.call_args_list[0].args[0]
|
||||
message: InternalMessage = agent.send.call_args_list[1].args[0]
|
||||
beliefs = BeliefMessage.model_validate_json(message.body)
|
||||
assert len(beliefs.create) == 1
|
||||
assert beliefs.create[0].name == "no_more_booze"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simulated_real_turn_no_beliefs(agent, sample_program):
|
||||
async def test_simulated_real_turn_no_beliefs(agent, llm, sample_program):
|
||||
"""Test a user message to extract beliefs from, but no beliefs are formed."""
|
||||
agent.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
|
||||
# Send a user message with no new beliefs
|
||||
agent._query_llm.return_value = {"is_pirate": None, "no_more_booze": None}
|
||||
llm._query_llm.return_value = {"is_pirate": None, "no_more_booze": None}
|
||||
await agent.handle_message(
|
||||
InternalMessage(
|
||||
to=settings.agent_settings.text_belief_extractor_name,
|
||||
@@ -302,17 +305,17 @@ async def test_simulated_real_turn_no_beliefs(agent, sample_program):
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simulated_real_turn_no_new_beliefs(agent, sample_program):
|
||||
async def test_simulated_real_turn_no_new_beliefs(agent, llm, sample_program):
|
||||
"""
|
||||
Test a user message to extract beliefs from, but no new beliefs are formed because they already
|
||||
existed.
|
||||
"""
|
||||
agent.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
agent.beliefs["is_pirate"] = True
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
agent._current_beliefs = BeliefState(true={InternalBelief(name="is_pirate", arguments=None)})
|
||||
|
||||
# Send a user message with the belief the user is a pirate, still
|
||||
agent._query_llm.return_value = {"is_pirate": True, "no_more_booze": None}
|
||||
llm._query_llm.return_value = {"is_pirate": True, "no_more_booze": None}
|
||||
await agent.handle_message(
|
||||
InternalMessage(
|
||||
to=settings.agent_settings.text_belief_extractor_name,
|
||||
@@ -326,17 +329,19 @@ async def test_simulated_real_turn_no_new_beliefs(agent, sample_program):
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_simulated_real_turn_remove_belief(agent, sample_program):
|
||||
async def test_simulated_real_turn_remove_belief(agent, llm, sample_program):
|
||||
"""
|
||||
Test a user message to extract beliefs from, but an existing belief is determined no longer to
|
||||
hold.
|
||||
"""
|
||||
agent.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
agent.beliefs["no_more_booze"] = True
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
agent._current_beliefs = BeliefState(
|
||||
true={InternalBelief(name="no_more_booze", arguments=None)},
|
||||
)
|
||||
|
||||
# Send a user message with the belief the user is a pirate, still
|
||||
agent._query_llm.return_value = {"is_pirate": None, "no_more_booze": False}
|
||||
llm._query_llm.return_value = {"is_pirate": None, "no_more_booze": False}
|
||||
await agent.handle_message(
|
||||
InternalMessage(
|
||||
to=settings.agent_settings.text_belief_extractor_name,
|
||||
@@ -349,18 +354,23 @@ async def test_simulated_real_turn_remove_belief(agent, sample_program):
|
||||
assert agent.send.call_count == 2
|
||||
|
||||
# Agent's current beliefs should've changed
|
||||
assert not agent.beliefs["no_more_booze"]
|
||||
assert any(b.name == "no_more_booze" for b in agent._current_beliefs.false)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_llm_failure_handling(agent, sample_program):
|
||||
async def test_llm_failure_handling(agent, llm, sample_program):
|
||||
"""
|
||||
Check that the agent handles failures gracefully without crashing.
|
||||
"""
|
||||
agent._query_llm.side_effect = httpx.HTTPError("")
|
||||
agent.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
llm._query_llm.side_effect = httpx.HTTPError("")
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].norms[0].condition)
|
||||
agent.belief_inferrer.available_beliefs.append(sample_program.phases[0].triggers[0].condition)
|
||||
|
||||
belief_changes = await agent._infer_turn()
|
||||
belief_changes = await agent.belief_inferrer.infer_from_conversation(
|
||||
ChatHistory(
|
||||
messages=[ChatMessage(role="user", content="Good day!")],
|
||||
),
|
||||
)
|
||||
|
||||
assert len(belief_changes) == 0
|
||||
assert len(belief_changes.true) == 0
|
||||
assert len(belief_changes.false) == 0
|
||||
|
||||
Reference in New Issue
Block a user