Skip to content

Commit

Permalink
Combine logic-adapter features into base class.
Browse files Browse the repository at this point in the history
The functionality of the no-knowledge adapter and the default
response adapter can be recreated by adding a default response
function to the parent class for logic-adapters.
  • Loading branch information
gunthercox committed Jan 2, 2019
1 parent 4d49b25 commit f46422b
Show file tree
Hide file tree
Showing 19 changed files with 134 additions and 273 deletions.
22 changes: 1 addition & 21 deletions chatterbot/chatterbot.py
Expand Up @@ -24,11 +24,6 @@ def __init__(self, name, **kwargs):

storage_adapter = kwargs.get('storage_adapter', 'chatterbot.storage.SQLStorageAdapter')

# These are logic adapters that are required for normal operation
system_logic_adapters = kwargs.get('system_logic_adapters', (
'chatterbot.logic.NoKnowledgeAdapter',
))

logic_adapters = kwargs.get('logic_adapters', [
'chatterbot.logic.BestMatch'
])
Expand All @@ -45,19 +40,10 @@ def __init__(self, name, **kwargs):
# Logic adapters used by the chat bot
self.logic_adapters = []

# Required logic adapters that must always be present
self.system_logic_adapters = []

self.storage = utils.initialize_class(storage_adapter, **kwargs)
self.input = utils.initialize_class(input_adapter, self, **kwargs)
self.output = utils.initialize_class(output_adapter, self, **kwargs)

# Add required system logic adapter
for system_logic_adapter in system_logic_adapters:
utils.validate_adapter_class(system_logic_adapter, LogicAdapter)
logic_adapter = utils.initialize_class(system_logic_adapter, self, **kwargs)
self.system_logic_adapters.append(logic_adapter)

for adapter in logic_adapters:
utils.validate_adapter_class(adapter, LogicAdapter)
logic_adapter = utils.initialize_class(adapter, self, **kwargs)
Expand Down Expand Up @@ -165,7 +151,7 @@ def generate_response(self, input_statement):
result = None
max_confidence = -1

for adapter in self.get_logic_adapters():
for adapter in self.logic_adapters:
if adapter.can_process(input_statement):

output = adapter.process(input_statement)
Expand Down Expand Up @@ -267,11 +253,5 @@ def get_latest_response(self, conversation):

return None

def get_logic_adapters(self):
"""
Return a list of all logic adapters being used, including system logic adapters.
"""
return self.logic_adapters + self.system_logic_adapters

class ChatBotException(Exception):
pass
4 changes: 0 additions & 4 deletions chatterbot/logic/__init__.py
@@ -1,18 +1,14 @@
from chatterbot.logic.logic_adapter import LogicAdapter
from chatterbot.logic.best_match import BestMatch
from chatterbot.logic.low_confidence import LowConfidenceAdapter
from chatterbot.logic.mathematical_evaluation import MathematicalEvaluation
from chatterbot.logic.no_knowledge_adapter import NoKnowledgeAdapter
from chatterbot.logic.specific_response import SpecificResponseAdapter
from chatterbot.logic.time_adapter import TimeLogicAdapter
from chatterbot.logic.unit_conversion import UnitConversion

__all__ = (
'LogicAdapter',
'BestMatch',
'LowConfidenceAdapter',
'MathematicalEvaluation',
'NoKnowledgeAdapter',
'SpecificResponseAdapter',
'TimeLogicAdapter',
'UnitConversion',
Expand Down
21 changes: 1 addition & 20 deletions chatterbot/logic/best_match.py
@@ -1,5 +1,4 @@
from chatterbot.logic import LogicAdapter
from chatterbot.storage import StorageAdapter
from chatterbot import filters


Expand All @@ -23,13 +22,6 @@ def __init__(self, chatbot, **kwargs):

self.excluded_words = kwargs.get('excluded_words')

def can_process(self, statement):
"""
Check that the chatbot's storage adapter is available to the logic
adapter and there is at least one statement in the database.
"""
return self.chatbot.storage.count()

def process(self, input_statement):
search_results = self.search_algorithm.search(input_statement)

Expand Down Expand Up @@ -111,17 +103,6 @@ def process(self, input_statement):
response.confidence = closest_match.confidence
self.chatbot.logger.info('Alternate response selected. Using "{}"'.format(response.text))
else:
try:
response = self.chatbot.storage.get_random()
except StorageAdapter.EmptyDatabaseException:
response = input_statement
self.chatbot.logger.info(
'No response to "{}" found. Selecting a random response.'.format(
closest_match.text
)
)

# Set confidence to zero because a random response is selected
response.confidence = 0
response = self.get_default_response(input_statement)

return response
49 changes: 47 additions & 2 deletions chatterbot/logic/logic_adapter.py
@@ -1,5 +1,7 @@
from chatterbot.adapters import Adapter
from chatterbot.storage import StorageAdapter
from chatterbot.search import IndexedTextSearch
from chatterbot.conversation import Statement


class LogicAdapter(Adapter):
Expand All @@ -18,8 +20,15 @@ class LogicAdapter(Adapter):
is found or the search set is exhausted.
Defaults to 0.95
:param response_selection_method: The a response selection method.
Defaults to ``get_first_response``.
:param response_selection_method:
The a response selection method.
Defaults to ``get_first_response``
:type response_selection_method: collections.abc.Callable
:param default_response:
The default response returned by this logic adaper
if there is no other possible response to return.
:type default_response: str or list or tuple
"""

def __init__(self, chatbot, **kwargs):
Expand All @@ -45,6 +54,18 @@ def __init__(self, chatbot, **kwargs):
get_first_response
)

default_responses = kwargs.get('default_response', [])

# Convert a single string into a list
if isinstance(default_responses, str):
default_responses = [
default_responses
]

self.default_responses = [
Statement(text=default) for default in default_responses
]

def can_process(self, statement):
"""
A preliminary check that is called to determine if a
Expand Down Expand Up @@ -75,6 +96,30 @@ def process(self, statement):
"""
raise self.AdapterMethodNotImplementedError()

def get_default_response(self, input_statement):
"""
This method is called when a logic adapter is unable to generate any
other meaningful response.
"""
from random import choice

if self.default_responses:
response = choice(self.default_responses)
else:
try:
response = self.chatbot.storage.get_random()
except StorageAdapter.EmptyDatabaseException:
response = input_statement

self.chatbot.logger.info(
'No known response to the input was found. Selecting a random response.'
)

# Set confidence to zero because a random response is selected
response.confidence = 0

return response

@property
def class_name(self):
"""
Expand Down
75 changes: 0 additions & 75 deletions chatterbot/logic/low_confidence.py

This file was deleted.

25 changes: 0 additions & 25 deletions chatterbot/logic/no_knowledge_adapter.py

This file was deleted.

8 changes: 7 additions & 1 deletion chatterbot/storage/django_storage.py
Expand Up @@ -193,7 +193,13 @@ def get_random(self):
Returns a random statement from the database
"""
Statement = self.get_model('statement')
return Statement.objects.order_by('?').first()

statement = Statement.objects.order_by('?').first()

if statement is None:
raise self.EmptyDatabaseException()

return statement

def remove(self, statement_text):
"""
Expand Down
21 changes: 6 additions & 15 deletions docs/logic/index.rst
Expand Up @@ -113,21 +113,6 @@ This adapter is able to handle any combination of word and numeric operators.
Bot: (4 + 4) = 8
Low Confidence Response Adapter
===============================

This adapter returns a specified default response if a response can not be
determined with a high amount of confidence.

.. autofunction:: chatterbot.logic.LowConfidenceAdapter

Low confidence response example
-------------------------------

.. literalinclude:: ../../examples/default_response_example.py
:language: python


Specific Response Adapter
=========================

Expand All @@ -141,3 +126,9 @@ Specific response example

.. literalinclude:: ../../examples/specific_response_example.py
:language: python

Low confidence response example
-------------------------------

.. literalinclude:: ../../examples/default_response_example.py
:language: python
6 changes: 1 addition & 5 deletions examples/default_response_example.py
Expand Up @@ -8,11 +8,7 @@
storage_adapter='chatterbot.storage.SQLStorageAdapter',
logic_adapters=[
{
'import_path': 'chatterbot.logic.BestMatch'
},
{
'import_path': 'chatterbot.logic.LowConfidenceAdapter',
'threshold': 0.65,
'import_path': 'chatterbot.logic.BestMatch',
'default_response': 'I am sorry, but I do not understand.'
}
]
Expand Down
2 changes: 1 addition & 1 deletion examples/django_app/tests/test_api.py
Expand Up @@ -6,7 +6,7 @@
class ApiTestCase(TestCase):

def setUp(self):
super(ApiTestCase, self).setUp()
super().setUp()
self.api_url = reverse('chatterbot')

def test_invalid_text(self):
Expand Down
6 changes: 3 additions & 3 deletions examples/django_app/tests/test_example.py
Expand Up @@ -6,7 +6,7 @@
class ViewTestCase(TestCase):

def setUp(self):
super(ViewTestCase, self).setUp()
super().setUp()
self.url = reverse('main')

def test_get_main_page(self):
Expand All @@ -24,7 +24,7 @@ class ApiTestCase(TestCase):
"""

def setUp(self):
super(ApiTestCase, self).setUp()
super().setUp()
self.api_url = reverse('chatterbot')

def test_post(self):
Expand Down Expand Up @@ -73,7 +73,7 @@ class ApiIntegrationTestCase(TestCase):
"""

def setUp(self):
super(ApiIntegrationTestCase, self).setUp()
super().setUp()
self.api_url = reverse('chatterbot')

def test_get(self):
Expand Down

0 comments on commit f46422b

Please sign in to comment.