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

Prevent statements that are not in response to a known statement from being passed to the logic adapter. #81

Merged
merged 2 commits into from
Nov 24, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 10 additions & 3 deletions chatterbot/adapters/storage/jsondatabase.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,19 @@ def _all_kwargs_match_values(self, kwarguments, values):
if "__" in kwarg:
kwarg_parts = kwarg.split("__")

if kwarg_parts[1] == "contains":
key = kwarg_parts[0]
identifier = kwarg_parts[1]

if identifier == "contains":
text_values = []
for val in values[kwarg_parts[0]]:
for val in values[key]:
text_values.append(val[0])

if (kwarguments[kwarg] not in text_values) and (kwarguments[kwarg] not in values[kwarg_parts[0]]):
if (kwarguments[kwarg] not in text_values) and (kwarguments[kwarg] not in values[key]):
return False

if identifier == "not":
if (key in values) and (kwarguments[kwarg] == values[key]):
return False

if kwarg in values:
Expand Down
16 changes: 16 additions & 0 deletions chatterbot/chatterbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ def get_response(self, input_text):

all_statements = self.storage.filter()

'''
Filter out all statements that are not in response to another statement.
A statement must exist which lists the closest matching statement in the
in_response_to field. Otherwise, the logic adapter may find a closest
matching statement that does not have a known response.
'''
for statement in all_statements:
response_exists = False
for s in all_statements:
if statement in s.in_response_to:
response_exists = True
break # Exit for loop since one exists

if not response_exists:
all_statements.remove(statement)

# Select the closest match to the input statement
closest_match = self.logic.get(
input_statement,
Expand Down
52 changes: 52 additions & 0 deletions tests/storage_adapter_tests/test_jsondb_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,58 @@ def test_filter_returns_statement_with_multiple_responses(self):

self.assertEqual(len(response.in_response_to), 2)

def test_not_filter_no_responses(self):
statement = Statement(
"You are welcome.",
in_response_to=[]
)
self.adapter.update(statement)
response = self.adapter.filter(
in_response_to__not=[]
)

self.assertEqual(
len(response),
0,
"A response was found when none should have been returned."
)

def test_not_filter_multiple_responses(self):
statements = [
Statement(
"You are welcome.",
in_response_to=[]
),
Statement(
"I like ducks.",
in_response_to=[]
),
Statement(
"Hello.",
in_response_to=[Response("Hi.")]
),
Statement(
"Hi.",
in_response_to=[Response("Hello.")]
),
Statement(
"Hey!",
in_response_to=[Response("Hello.")]
)
]

for statement in statements:
self.adapter.update(statement)

response = self.adapter.filter(
in_response_to__not=[]
)

self.assertEqual(len(response), 3)
self.assertIn("Hello.", response)
self.assertIn("Hi.", response)
self.assertIn("Hey!", response)


class ReadOnlyJsonDatabaseAdapterTestCase(BaseJsonDatabaseAdapterTestCase):

Expand Down
8 changes: 4 additions & 4 deletions tests/test_chatbot_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,12 +170,12 @@ def test_second_response_format(self):
response = self.chatbot.get_response("Hi")
# response = "Hello"
second_response = self.chatbot.get_response("How are you?")
statement_object = self.chatbot.storage.find(second_response)
statement = self.chatbot.storage.find(second_response)

self.assertEqual(second_response, self.test_statement.text)
#TODO: self.assertEqual(statement_object.get_response_count(), 2)
self.assertEqual(len(statement_object.in_response_to), 1)
self.assertIn("Hi", statement_object.in_response_to)
#TODO: self.assertEqual(statement.get_response_count(), 2)
self.assertEqual(len(statement.in_response_to), 1)
self.assertIn("Hi", statement.in_response_to)


class ChatterBotStorageIntegrationTests(UntrainedChatBotTestCase):
Expand Down
21 changes: 21 additions & 0 deletions tests/training_tests/test_training.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,24 @@ def test_training_with_unicode_characters(self):

self.assertEqual(response, conversation[2])

def test_similar_sentence_gets_same_response_multiple_times(self):
"""
Tests if the bot returns the same response for the same question (which
is similar to the one present in the training set) when asked repeatedly.
"""
training = [
'how do you login to gmail?',
'Goto gmail.com, enter your login information and hit enter!'
]

similar_question = 'how do I login to gmail?'

self.chatbot.train(training)

response_to_trained_set = self.chatbot.get_response('how do you login to gmail?')
response_to_similar_question_1 = self.chatbot.get_response(similar_question)
response_to_similar_question_2 = self.chatbot.get_response(similar_question)

self.assertEqual(response_to_trained_set, response_to_similar_question_1)
self.assertEqual(response_to_similar_question_1, response_to_similar_question_2)