Skip to content

Commit

Permalink
Fix merge residual
Browse files Browse the repository at this point in the history
  • Loading branch information
NoB0 committed Aug 31, 2023
1 parent 3a8a890 commit a77a49c
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 161 deletions.
4 changes: 2 additions & 2 deletions moviebot/agent/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ def receive_utterance(

agent_dacts = self.dialogue_manager.generate_output()
dialogue_state = (
self._dialogue_connector.dialogue_state_tracker.dialogue_state
self.dialogue_manager.dialogue_state_tracker.dialogue_state
)
agent_response, options = self.nlg.generate_output(
agent_dacts,
Expand Down Expand Up @@ -249,4 +249,4 @@ def terminated_dialogue(self) -> bool:
Returns:
True if conversation is finished.
"""
return self._dialogue_connector.get_state().at_terminal_state
return self.dialogue_manager.get_state().at_terminal_state
5 changes: 0 additions & 5 deletions moviebot/controller/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@
from dialoguekit.platforms import Platform as DialogueKitPlatform
from moviebot.connector.dialogue_connector import MovieBotDialogueConnector
from moviebot.core.utterance.utterance import UserUtterance
from moviebot.dialogue_manager.dialogue_manager import DialogueManager

if TYPE_CHECKING:
from moviebot.agent.agent import MovieBotAgent


if TYPE_CHECKING:
from moviebot.agent.agent import MovieBotAgent
Expand Down
115 changes: 21 additions & 94 deletions moviebot/dialogue_manager/dialogue_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,23 @@
"""


from collections import defaultdict
from dataclasses import asdict
from typing import TYPE_CHECKING, Any, Dict, List

from dialoguekit.connector import DialogueConnector
from dialoguekit.core import AnnotatedUtterance
from dialoguekit.participant import User
from moviebot.core.utterance.utterance import UserUtterance
from typing import Any, Dict, List

from moviebot.core.intents.agent_intents import AgentIntents
from moviebot.dialogue_manager.dialogue_act import DialogueAct
from moviebot.dialogue_manager.dialogue_policy import DialoguePolicy
from moviebot.dialogue_manager.dialogue_state import DialogueState
from moviebot.dialogue_manager.dialogue_state_tracker import (
DialogueStateTracker,
)
from moviebot.nlu.annotation.item_constraint import ItemConstraint
from moviebot.nlu.annotation.operator import Operator
from moviebot.recommender.recommender_model import RecommenderModel

if TYPE_CHECKING:
from moviebot.agent.agent import MovieBotAgent
from moviebot.controller.controller import Controller


class DialogueManager(DialogueConnector):
class DialogueManager:
def __init__(
self,
config: Dict[str, Any],
isBot: bool,
new_user: bool,
agent: "MovieBotAgent",
user: User,
platform: "Controller",
conversation_id: str = None,
save_dialogue_history: bool = True,
self, config: Dict[str, Any], isBot: bool, new_user: bool
) -> None:
"""Initializes the dialogue manager including the state tracker and
dialogue policy.
Expand All @@ -47,90 +32,32 @@ def __init__(
config: The settings for components to be initialized.
isBot: If the conversation is via bot or not.
new_user: Whether the user is new or not.
agent: The agent.
user: The user.
platform: The platform.
conversation_id: The conversation ID. Defaults to None.
save_dialogue_history: Whether to save the dialogue history or not.
Defaults to True.
"""
super().__init__(
agent, user, platform, conversation_id, save_dialogue_history
)
self.isBot = isBot
self.new_user = new_user
self.dialogue_state_tracker = DialogueStateTracker(config, self.isBot)
self.dialogue_policy = DialoguePolicy(self.isBot, self.new_user)
self.recommender: RecommenderModel = config.get("recommender")

def start(self) -> None:
"""Starts the dialogue."""
self.dialogue_state_tracker.initialize()
self._agent.welcome(self._user.id)

def close(self) -> None:
"""Closes the conversation.
If '_save_dialogue_history' is set to True it will export the
dialogue history.
"""
if self._save_dialogue_history:
self._stringify_dialogue_acts()
self._dump_dialogue_history()

def register_agent_utterance(
self, annotated_utterance: AnnotatedUtterance
) -> None:
"""Registers an annotated utterance from the agent.
This method takes a AnnotatedUtterance but only a Utterance gets sent to
the User. The AnnotatedUtterance gets used to store the conversation for
future reference, and if the Agent wants to end the conversation with
the _agent.stop_intent Intent, the DialogueConnector will end the
conversation with the close() method.
Note:
If the Intent label is 'EXIT' the DialogueConnector will close. Thus
it is only the agent that can close the DialogueConnector.
def start_dialogue(self, new_user: bool = False) -> List[DialogueAct]:
"""Starts the dialogue by generating a response from the agent.
Args:
annotated_utterance: Agent utterance.
"""
self._dialogue_history.add_utterance(annotated_utterance)
self._platform.display_agent_utterance(
self._user.id, annotated_utterance
)
if annotated_utterance.intent == self._agent.stop_intent:
self.close()
else:
self._user.receive_utterance(annotated_utterance)

def register_user_utterance(
self, annotated_utterance: AnnotatedUtterance
) -> None:
"""Registers an annotated utterance from the user.
In most cases the Agent should not know about the Users Intent and
Annotation-s. But for some use cases this additional information may
become useful, depending on the UI etc.
Thus the complete AnnotatedUtterance will be sent to the Agent. It is
the Agents responsibility to only use the information it is supposed
to.
new_user: Whether the user is new or not. Defaults to False.
Args:
annotated_utterance: User utterance.
Returns:
A list with the first agent response.
"""
user_options = self._dialogue_history.utterances[-1].metadata.get(
"options", {}
)
self._dialogue_history.add_utterance(annotated_utterance)
self._platform.display_user_utterance(
self._user.id, annotated_utterance
)
user_utterance = UserUtterance(
**asdict(annotated_utterance.get_utterance())
self.dialogue_state_tracker.initialize()
agent_dact = DialogueAct(
AgentIntents.WELCOME,
[
ItemConstraint("new_user", Operator.EQ, new_user),
ItemConstraint("is_bot", Operator.EQ, self.isBot),
],
)
self._agent.receive_utterance(user_utterance, user_options)
self.dialogue_state_tracker.update_state_agent([agent_dact])
return [agent_dact]

def receive_input(self, user_dacts: List[DialogueAct]) -> None:
"""Receives the input from the agent and updates state/context.
Expand Down
73 changes: 13 additions & 60 deletions tests/dialogue_manager/test_dialogue_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from unittest import mock

import pytest
from dialoguekit.participant import User

from moviebot.core.intents.agent_intents import AgentIntents
from moviebot.dialogue_manager.dialogue_act import DialogueAct
Expand Down Expand Up @@ -34,70 +33,24 @@ def config() -> Dict[str, Any]:


@pytest.fixture
def agent() -> mock.MagicMock:
with mock.patch("moviebot.agent.agent.MovieBotAgent") as MockAgent:
yield MockAgent()
def dialogue_manager(config) -> DialogueManager:
yield DialogueManager(config, isBot=False, new_user=True)


@pytest.fixture
def user() -> User:
return User("TestUser")


@pytest.fixture
def platform() -> mock.MagicMock:
with mock.patch(
"moviebot.controller.controller_terminal.ControllerTerminal"
) as MockController:
yield MockController()


@pytest.fixture
def dialogue_manager(
config: Dict[str, Any],
agent: mock.MagicMock,
user: User,
platform: mock.MagicMock,
) -> DialogueManager:
yield DialogueManager(
config,
isBot=False,
new_user=True,
agent=agent,
user=user,
platform=platform,
)


def test_dialogue_manager(
config: Dict[str, Any],
agent: mock.MagicMock,
user: User,
platform: mock.MagicMock,
) -> None:
dialogue_manager = DialogueManager(
config,
isBot=False,
new_user=True,
agent=agent,
user=user,
platform=platform,
)
def test_dialogue_manager(config):
dialogue_manager = DialogueManager(config, isBot=False, new_user=True)
assert hasattr(dialogue_manager, "dialogue_state_tracker")
assert hasattr(dialogue_manager, "dialogue_policy")


@mock.patch.object(DialogueStateTracker, "initialize")
def test_start(
mocked_initialize: mock.MagicMock,
dialogue_manager: DialogueManager,
def test_start_dialogue(
mocked_initialize: mock.MagicMock, dialogue_manager: DialogueManager
):
dialogue_manager.start()
agent_dact = dialogue_manager.start_dialogue()
assert len(agent_dact) == 1
assert agent_dact[0].intent == AgentIntents.WELCOME
mocked_initialize.assert_called()
assert (
mock.call.welcome(dialogue_manager._user.id)
in dialogue_manager._agent.mock_calls
)


@mock.patch.object(DialogueStateTracker, "update_state_user")
Expand All @@ -117,7 +70,7 @@ def test_receive_input(


def test_generate_output(dialogue_manager: DialogueManager):
dialogue_manager.start()
dialogue_manager.start_dialogue()
dialogue_acts = dialogue_manager.generate_output()
assert len(dialogue_acts) == 1
assert dialogue_acts[0].intent == AgentIntents.WELCOME
Expand All @@ -134,7 +87,7 @@ def test_generate_output_next_action_called(
mocked_next_action: mock.MagicMock,
dialogue_manager: DialogueManager,
):
dialogue_manager.start()
dialogue_manager.start_dialogue()
dialogue_acts = dialogue_manager.generate_output()
mocked_next_action.assert_called()
mocked_update_state_agent.assert_called()
Expand All @@ -151,7 +104,7 @@ def test_generate_output_next_action_called(
def test_generate_output_with_lookup(
mock_agent_can_lookup, dialogue_manager: DialogueManager
):
dialogue_manager.start()
dialogue_manager.start_dialogue()
dialogue_manager.generate_output()
dialogue_manager.recommender.recommend_items.assert_called()

Expand All @@ -166,7 +119,7 @@ def test_generate_output_restart(
mocked_next_action: mock.MagicMock,
dialogue_manager: DialogueManager,
):
dialogue_manager.start()
dialogue_manager.start_dialogue()
with mock.patch.object(
DialogueStateTracker,
"initialize",
Expand Down

0 comments on commit a77a49c

Please sign in to comment.