/
chatterbot.py
189 lines (149 loc) · 6.37 KB
/
chatterbot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
from .adapters.storage import StorageAdapter
from .adapters.logic import LogicAdapter, MultiLogicAdapter
from .adapters.input import InputAdapter
from .adapters.output import OutputAdapter
from .conversation import Statement, Response
from .trainers import Trainer
from .utils.queues import ResponseQueue
from .utils.module_loading import import_module
import logging
class ChatBot(object):
def __init__(self, name, **kwargs):
kwargs["name"] = name
storage_adapter = kwargs.get("storage_adapter",
"chatterbot.adapters.storage.JsonFileStorageAdapter"
)
logic_adapters = kwargs.get("logic_adapters", [
"chatterbot.adapters.logic.ClosestMatchAdapter"
])
input_adapter = kwargs.get("input_adapter",
"chatterbot.adapters.input.VariableInputTypeAdapter"
)
output_adapter = kwargs.get("output_adapter",
"chatterbot.adapters.output.OutputFormatAdapter"
)
input_output_adapter_pairs = kwargs.get(
"io_adapter_pairs"
)
# The last 10 statement inputs and outputs
self.recent_statements = ResponseQueue(maxsize=10)
# The storage adapter must be an instance of StorageAdapter
self.validate_adapter_class(storage_adapter, StorageAdapter)
# The input adapter must be an instance of InputAdapter
self.validate_adapter_class(input_adapter, InputAdapter)
# The output adapter must be an instance of OutputAdapter
self.validate_adapter_class(output_adapter, OutputAdapter)
StorageAdapterClass = import_module(storage_adapter)
InputAdapterClass = import_module(input_adapter)
OutputAdapterClass = import_module(output_adapter)
self.storage = StorageAdapterClass(**kwargs)
self.logic = MultiLogicAdapter(**kwargs)
self.input = InputAdapterClass(**kwargs)
self.output = OutputAdapterClass(**kwargs)
# Add required system logic adapter
self.add_adapter("chatterbot.adapters.logic.NoKnowledgeAdapter")
for adapter in logic_adapters:
self.add_adapter(adapter, **kwargs)
# Share context information such as the name, the current conversation,
# or access to other adapters with each of the adapters
self.storage.set_context(self)
self.logic.set_context(self)
self.input.set_context(self)
self.output.set_context(self)
self.trainer = Trainer(self.storage)
self.logger = kwargs.get('logger', logging.getLogger(__name__))
def add_adapter(self, adapter, **kwargs):
self.validate_adapter_class(adapter, LogicAdapter)
NewAdapter = import_module(adapter)
adapter = NewAdapter(**kwargs)
self.logic.add_adapter(adapter)
def validate_adapter_class(self, validate_class, adapter_class):
"""
Raises an exception if validate_class is
not a subclass of adapter_class.
"""
from .adapters import Adapter
if not issubclass(import_module(validate_class), Adapter):
raise self.InvalidAdapterException(
'{} must be a subclass of {}'.format(
validate_class,
Adapter.__name__
)
)
if not issubclass(import_module(validate_class), adapter_class):
raise self.InvalidAdapterException(
'{} must be a subclass of {}'.format(
validate_class,
adapter_class.__name__
)
)
def get_last_conversance(self):
"""
Return the most recent input statement and response pair.
"""
if not self.recent_statements.empty():
return self.recent_statements[-1]
return None
def get_last_response_statement(self):
"""
Return the last statement that was received.
"""
previous_interaction = self.get_last_conversance()
if previous_interaction:
# Return the output statement
return previous_interaction[1]
return None
def get_last_input_statement(self):
"""
Return the last response that was given.
"""
previous_interaction = self.get_last_conversance()
if previous_interaction:
# Return the input statement
return previous_interaction[0]
return None
def get_response(self, input_item):
"""
Return the bot's response based on the input.
"""
input_statement = self.input.process_input(input_item)
self.logger.info(u'Recieved input statement: {}'.format(input_statement.text))
existing_statement = self.storage.find(input_statement.text)
if existing_statement:
self.logger.info(u'{} is a known statement'.format(input_statement.text))
input_statement = existing_statement
else:
self.logger.info(u'{} is not a known statement'.format(input_statement.text))
# Select a response to the input statement
confidence, response = self.logic.process(input_statement)
self.logger.info(u'Selecting "{}" as response with a confidence of {}'.format(response.text, confidence))
previous_statement = self.get_last_response_statement()
if previous_statement:
input_statement.add_response(
Response(previous_statement.text)
)
self.logger.info(u'Adding the previous statement "{}" as response to {}'.format(
previous_statement.text,
input_statement.text
))
# Update the database after selecting a response
self.storage.update(input_statement)
self.recent_statements.append(
(input_statement, response, )
)
# Process the response output with the output adapter
return self.output.process_response(response)
def set_trainer(self, training_class, **kwargs):
"""
Set the module used to train the chatbot.
"""
self.trainer = training_class(self.storage, **kwargs)
@property
def train(self):
# Proxy method to the trainer
return self.trainer.train
class InvalidAdapterException(Exception):
def __init__(self, value='Recieved an unexpected adapter setting.'):
self.value = value
def __str__(self):
return repr(self.value)