Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Response Selector training data format #6591

Merged
merged 43 commits into from
Sep 11, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5593201
first implementation works
dakshvar22 Sep 7, 2020
97b06c7
Merge branch 'master' into new_rs_format
dakshvar22 Sep 7, 2020
c4df077
more reuse
dakshvar22 Sep 7, 2020
f9d4996
Update rasa/core/actions/action.py
dakshvar22 Sep 8, 2020
172245e
change if condition style
dakshvar22 Sep 8, 2020
f4622a4
Merge branch 'new_rs_format' of github.com:RasaHQ/rasa into new_rs_fo…
dakshvar22 Sep 8, 2020
1af6cbe
refactor call to update retrieval intent props
dakshvar22 Sep 8, 2020
2b34931
merge master
dakshvar22 Sep 8, 2020
34ac797
fix existing tests. TODO: add more tests
dakshvar22 Sep 8, 2020
8f8c73a
Merge branch 'master' into new_rs_format
dakshvar22 Sep 8, 2020
9b7d25a
more tests
dakshvar22 Sep 8, 2020
ffe598e
add docsstrings, tests, changelog
dakshvar22 Sep 9, 2020
7c69bb3
add migration guide
dakshvar22 Sep 9, 2020
763f300
Merge branch 'master' into new_rs_format
dakshvar22 Sep 9, 2020
8bfc171
fix deepsource issues
dakshvar22 Sep 9, 2020
f20b872
Merge branch 'new_rs_format' of github.com:RasaHQ/rasa into new_rs_fo…
dakshvar22 Sep 9, 2020
0b430d0
Merge branch 'master' into new_rs_format
dakshvar22 Sep 9, 2020
e5ca3cc
merge master
dakshvar22 Sep 9, 2020
4f17e66
change import to make shared independent
dakshvar22 Sep 9, 2020
02b9898
add docs first pass
dakshvar22 Sep 9, 2020
bbcc9fc
bring back retrieval actions page
dakshvar22 Sep 9, 2020
2e0f78c
Merge branch 'master' into new_rs_format
dakshvar22 Sep 9, 2020
5a56afc
Apply suggestions from code review
dakshvar22 Sep 9, 2020
466b757
code formatting, remove arguments from tests
dakshvar22 Sep 9, 2020
d0bc08a
full sync of response templates between nlu data and domain
dakshvar22 Sep 9, 2020
fc346ae
Merge branch 'master' into new_rs_format
dakshvar22 Sep 9, 2020
482d742
new data importer
dakshvar22 Sep 10, 2020
ca1769f
fix tests, NLU eval
dakshvar22 Sep 10, 2020
2cf5b85
add test for evaluation.
dakshvar22 Sep 10, 2020
fbc61cd
refactored retrieval action to use utter action class.
dakshvar22 Sep 10, 2020
58899b0
make linter happy
dakshvar22 Sep 10, 2020
9c43df8
fix tests, change param to train on text
dakshvar22 Sep 10, 2020
3af7bcd
Merge branch 'master' into new_rs_format
dakshvar22 Sep 10, 2020
db54aa5
merge master, resolve zillion conflicts
dakshvar22 Sep 10, 2020
39711d2
Merge branch 'master' into new_rs_format
dakshvar22 Sep 10, 2020
05cee33
omit empty domain warning
dakshvar22 Sep 11, 2020
00bebbb
Merge branch 'new_rs_format' of github.com:RasaHQ/rasa into new_rs_fo…
dakshvar22 Sep 11, 2020
151e424
merge master, resolve conflicts
dakshvar22 Sep 11, 2020
84db4cd
review comments on shared
dakshvar22 Sep 11, 2020
f20928d
Merge branch 'master' into new_rs_format
dakshvar22 Sep 11, 2020
4e04e4f
add missing import
dakshvar22 Sep 11, 2020
2cc1b9d
Merge branch 'new_rs_format' of github.com:RasaHQ/rasa into new_rs_fo…
dakshvar22 Sep 11, 2020
a648d81
Merge branch 'master' into new_rs_format
dakshvar22 Sep 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/responseselectorbot/data/responses.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
version: "2.0"

responses:
chitchat/ask_name:
utter_chitchat/ask_name:
- image: "https://i.imgur.com/zTvA58i.jpeg"
text: hello, my name is retrieval bot.
- text: Oh yeah, I am called the retrieval bot.

chitchat/ask_weather:
utter_chitchat/ask_weather:
- text: Oh, it does look sunny right now in Berlin.
image: "https://i.imgur.com/vwv7aHN.png"
- text: I am not sure of the whole week but I can see the sun is out today.
2 changes: 1 addition & 1 deletion examples/responseselectorbot/data/rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ rules:
- rule: Response with a chitchat utterance whenever user indulges in some chitchat
steps:
- intent: chitchat
- action: respond_chitchat
- action: utter_chitchat
3 changes: 0 additions & 3 deletions examples/responseselectorbot/domain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ intents:
- bot_challenge
- chitchat

actions:
tmbo marked this conversation as resolved.
Show resolved Hide resolved
- respond_chitchat

responses:
utter_greet:
- text: "Hey! How are you?"
Expand Down
26 changes: 21 additions & 5 deletions rasa/core/actions/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
REQUESTED_SLOT,
USER_INTENT_OUT_OF_SCOPE,
UTTER_PREFIX,
RESPOND_PREFIX,
)
from rasa.nlu.constants import (
RESPONSE_SELECTOR_DEFAULT_INTENT,
Expand Down Expand Up @@ -92,6 +91,11 @@ def default_action_names() -> List[Text]:
return [a.name() for a in default_actions()] + [RULE_SNIPPET_ACTION_NAME]


def construct_retrieval_action_names(retrieval_intents: List[Text]) -> List[Text]:

return [f"{UTTER_PREFIX}{intent}" for intent in retrieval_intents]


def combine_user_with_default_actions(user_actions: List[Text]) -> List[Text]:
# remove all user actions that overwrite default actions
# this logic is a bit reversed, you'd think that we should remove
Expand All @@ -115,11 +119,21 @@ def combine_with_templates(
return actions + unique_template_names


def is_retrieval_action(action_name: Text, retrieval_intents: List[Text]) -> bool:

return (
True
if retrieval_intents and action_name.split(UTTER_PREFIX)[1] in retrieval_intents
else False
)
dakshvar22 marked this conversation as resolved.
Show resolved Hide resolved


def action_from_name(
name: Text,
action_endpoint: Optional[EndpointConfig],
user_actions: List[Text],
should_use_form_action: bool = False,
retrieval_intents: List[Text] = [],
dakshvar22 marked this conversation as resolved.
Show resolved Hide resolved
) -> "Action":
"""Return an action instance for the name."""

Expand All @@ -128,9 +142,11 @@ def action_from_name(
if name in defaults and name not in user_actions:
return defaults[name]
elif name.startswith(UTTER_PREFIX):
return ActionUtterTemplate(name)
elif name.startswith(RESPOND_PREFIX):
return ActionRetrieveResponse(name)
return (
ActionRetrieveResponse(name)
if is_retrieval_action(name, retrieval_intents)
else ActionUtterTemplate(name)
)
dakshvar22 marked this conversation as resolved.
Show resolved Hide resolved
elif should_use_form_action:
from rasa.core.actions.forms import FormAction

Expand Down Expand Up @@ -220,7 +236,7 @@ def __init__(self, name: Text, silent_fail: Optional[bool] = False):
self.silent_fail = silent_fail

def intent_name_from_action(self) -> Text:
return self.action_name.split(RESPOND_PREFIX)[1]
return self.action_name.split(UTTER_PREFIX)[1]

async def run(
self,
Expand Down
1 change: 0 additions & 1 deletion rasa/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
# it is the highest to prioritize form to the rest of the policies
FORM_POLICY_PRIORITY = 5
UTTER_PREFIX = "utter_"
RESPOND_PREFIX = "respond_"

DIALOGUE = "dialogue"

Expand Down
26 changes: 24 additions & 2 deletions rasa/core/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@
SLOT_LAST_OBJECT_TYPE,
SLOT_LISTED_ITEMS,
DEFAULT_INTENTS,
UTTER_PREFIX,
)
from rasa.core.events import SlotSet, UserUttered
from rasa.shared.core.slots import Slot, UnfeaturizedSlot, CategoricalSlot
from rasa.utils.endpoints import EndpointConfig
from rasa.utils.validation import InvalidYamlFileError, validate_yaml_schema
from rasa.nlu.training_data import TrainingData

logger = logging.getLogger(__name__)

Expand All @@ -47,6 +49,7 @@
USED_ENTITIES_KEY = "used_entities"
USE_ENTITIES_KEY = "use_entities"
IGNORE_ENTITIES_KEY = "ignore_entities"
IS_RETRIEVAL_INTENT_KEY = "is_retrieval_intent"

KEY_SLOTS = "slots"
KEY_INTENTS = "intents"
Expand Down Expand Up @@ -311,6 +314,7 @@ def _transform_intent_properties_for_internal_use(

properties.setdefault(USE_ENTITIES_KEY, True)
properties.setdefault(IGNORE_ENTITIES_KEY, [])
properties.setdefault(IS_RETRIEVAL_INTENT_KEY, False)
if not properties[USE_ENTITIES_KEY]: # this covers False, None and []
properties[USE_ENTITIES_KEY] = []

Expand Down Expand Up @@ -343,6 +347,18 @@ def _transform_intent_properties_for_internal_use(

return intent

def _update_retrieval_intent_properties(self, retrieval_intents: List[Text]):
for retrieval_intent in retrieval_intents:
self.intent_properties[retrieval_intent][IS_RETRIEVAL_INTENT_KEY] = True

@lazy_property
def retrieval_intents(self) -> List[Text]:
return [
intent
for intent in self.intent_properties
if self.intent_properties[intent][IS_RETRIEVAL_INTENT_KEY]
]

@classmethod
def collect_intent_properties(
cls, intents: List[Union[Text, Dict[Text, Any]]], entities: List[Text]
Expand Down Expand Up @@ -385,7 +401,13 @@ def _intent_properties(
) -> Tuple[Text, Dict[Text, Any]]:
if not isinstance(intent, dict):
intent_name = intent
intent = {intent_name: {USE_ENTITIES_KEY: True, IGNORE_ENTITIES_KEY: []}}
intent = {
intent_name: {
USE_ENTITIES_KEY: True,
IGNORE_ENTITIES_KEY: [],
IS_RETRIEVAL_INTENT_KEY: False,
}
}
else:
intent_name = list(intent.keys())[0]

Expand Down Expand Up @@ -537,6 +559,7 @@ def action_for_name(
action_endpoint,
self.user_actions_and_forms,
should_use_form_action,
self.retrieval_intents,
)

def action_for_index(
Expand Down Expand Up @@ -635,7 +658,6 @@ def input_state_map(self) -> Dict[Text, int]:
@lazy_property
def input_states(self) -> List[Text]:
"""Returns all available states."""

return (
self.intent_states
+ self.entity_states
Expand Down
28 changes: 27 additions & 1 deletion rasa/importers/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from rasa.importers.autoconfig import TrainingType
import rasa.utils.io as io_utils
import rasa.utils.common as common_utils
from rasa.core.actions import action

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -251,10 +252,35 @@ async def get_domain(self) -> Domain:
domains = [importer.get_domain() for importer in self._importers]
domains = await asyncio.gather(*domains)

return reduce(
# Check if NLU data has any retrieval intents,
# if yes add corresponding retrieval actions with `utter_` prefix automatically to an empty domain.
nlu_data = await self.get_nlu_data()
if nlu_data.retrieval_intents:
domains.append(
self._get_domain_with_retrieval_actions(nlu_data.retrieval_intents)
)

combined_domain = reduce(
lambda merged, other: merged.merge(other), domains, Domain.empty()
)

# Make the domain known which intents are retrieval intents
combined_domain._update_retrieval_intent_properties(nlu_data.retrieval_intents)
dakshvar22 marked this conversation as resolved.
Show resolved Hide resolved

return combined_domain

@staticmethod
def _get_domain_with_retrieval_actions(retrieval_intents):

return Domain(
[],
[],
[],
{},
action.construct_retrieval_action_names(retrieval_intents),
[],
)

async def get_stories(
self,
template_variables: Optional[Dict] = None,
Expand Down
12 changes: 8 additions & 4 deletions rasa/nlu/selectors/response_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
TEXT,
INTENT_NAME_KEY,
)
from rasa.nlu.training_data import training_data

from rasa.utils.tensorflow.model_data import RasaModelData
from rasa.utils.tensorflow.models import RasaModel
Expand Down Expand Up @@ -350,13 +351,14 @@ def _resolve_intent_response_key(
for key, responses in self.responses.items():

# First check if the predicted label was the key itself
if hash(key) == label.get("id"):
return key
search_key = training_data.template_key_to_intent_response_key(key)
if hash(search_key) == label.get("id"):
return search_key

# Otherwise loop over the responses to check if the text has a direct match
for response in responses:
if hash(response.get(TEXT, "")) == label.get("id"):
return key
return search_key
return None

def process(self, message: Message, **kwargs: Any) -> None:
Expand All @@ -370,7 +372,9 @@ def process(self, message: Message, **kwargs: Any) -> None:
label_intent_response_key = (
self._resolve_intent_response_key(top_label) or top_label[INTENT_NAME_KEY]
)
label_response_templates = self.responses.get(label_intent_response_key)
label_response_templates = self.responses.get(
training_data.intent_response_key_to_template_key(label_intent_response_key)
)

if label_intent_response_key and not label_response_templates:
# response templates seem to be unavailable,
Expand Down
17 changes: 14 additions & 3 deletions rasa/nlu/training_data/training_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
ENTITIES,
TEXT,
)
from rasa.core.constants import UTTER_PREFIX
from rasa.nlu.training_data.message import Message
from rasa.nlu.training_data.util import check_duplicate_synonym
from rasa.nlu.utils import list_to_str
Expand All @@ -31,6 +32,14 @@
logger = logging.getLogger(__name__)


def intent_response_key_to_template_key(intent_response_key: Text) -> Text:
return f"{UTTER_PREFIX}{intent_response_key}"


def template_key_to_intent_response_key(template_key: Text) -> Text:
return template_key.split(UTTER_PREFIX)[1]


class TrainingData:
"""Holds loaded intent and entity training data."""

Expand Down Expand Up @@ -240,8 +249,10 @@ def _fill_response_phrases(self) -> None:
continue

# look for corresponding bot utterance
story_lookup_intent = example.get_full_intent()
assistant_utterances = self.responses.get(story_lookup_intent, [])
story_lookup_key = intent_response_key_to_template_key(
example.get_full_intent()
)
assistant_utterances = self.responses.get(story_lookup_key, [])
if assistant_utterances:

# Use the first response text as training label if needed downstream
Expand All @@ -251,7 +262,7 @@ def _fill_response_phrases(self) -> None:

# If no text attribute was found use the key for training
if not example.get(RESPONSE):
example.set(RESPONSE, story_lookup_intent)
example.set(RESPONSE, story_lookup_key)

def nlu_as_json(self, **kwargs: Any) -> Text:
"""Represent this set of training examples as json."""
Expand Down