ChatterBot 1.2.3__py3-none-any.whl → 1.2.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chatterbot/__init__.py +1 -1
- chatterbot/__main__.py +15 -0
- chatterbot/chatterbot.py +55 -9
- chatterbot/comparisons.py +8 -22
- chatterbot/conversation.py +1 -2
- chatterbot/ext/django_chatterbot/abstract_models.py +16 -8
- chatterbot/ext/django_chatterbot/apps.py +7 -0
- chatterbot/ext/django_chatterbot/migrations/0020_alter_statement_conversation_and_more.py +53 -0
- chatterbot/ext/django_chatterbot/settings.py +2 -3
- chatterbot/ext/sqlalchemy_app/models.py +2 -2
- chatterbot/logic/logic_adapter.py +14 -9
- chatterbot/logic/specific_response.py +3 -7
- chatterbot/logic/time_adapter.py +3 -7
- chatterbot/preprocessors.py +4 -3
- chatterbot/response_selection.py +4 -6
- chatterbot/search.py +0 -55
- chatterbot/storage/django_storage.py +4 -1
- chatterbot/storage/sql_storage.py +1 -1
- chatterbot/tagging.py +3 -7
- chatterbot/trainers.py +306 -109
- chatterbot/utils.py +17 -28
- {chatterbot-1.2.3.dist-info → chatterbot-1.2.5.dist-info}/METADATA +8 -18
- {chatterbot-1.2.3.dist-info → chatterbot-1.2.5.dist-info}/RECORD +26 -25
- {chatterbot-1.2.3.dist-info → chatterbot-1.2.5.dist-info}/WHEEL +1 -1
- {chatterbot-1.2.3.dist-info → chatterbot-1.2.5.dist-info/licenses}/LICENSE +0 -0
- {chatterbot-1.2.3.dist-info → chatterbot-1.2.5.dist-info}/top_level.txt +0 -0
chatterbot/__init__.py
CHANGED
chatterbot/__main__.py
CHANGED
@@ -1,7 +1,16 @@
|
|
1
|
+
"""
|
2
|
+
Example usage for ChatterBot command line arguments:
|
3
|
+
|
4
|
+
python -m chatterbot --help
|
5
|
+
"""
|
6
|
+
|
1
7
|
import sys
|
2
8
|
|
3
9
|
|
4
10
|
def get_chatterbot_version():
|
11
|
+
"""
|
12
|
+
Return the version of the current package.
|
13
|
+
"""
|
5
14
|
from chatterbot import __version__
|
6
15
|
|
7
16
|
return __version__
|
@@ -10,3 +19,9 @@ def get_chatterbot_version():
|
|
10
19
|
if __name__ == '__main__':
|
11
20
|
if '--version' in sys.argv:
|
12
21
|
print(get_chatterbot_version())
|
22
|
+
elif '--help' in sys.argv:
|
23
|
+
print('usage: chatterbot [--version, --help]')
|
24
|
+
print(' --version: Print the version of ChatterBot')
|
25
|
+
print(' --help: Print this help message')
|
26
|
+
print()
|
27
|
+
print('Documentation at https://docs.chatterbot.us')
|
chatterbot/chatterbot.py
CHANGED
@@ -3,6 +3,7 @@ from chatterbot.storage import StorageAdapter
|
|
3
3
|
from chatterbot.logic import LogicAdapter
|
4
4
|
from chatterbot.search import TextSearch, IndexedTextSearch
|
5
5
|
from chatterbot.tagging import PosLemmaTagger
|
6
|
+
from chatterbot.conversation import Statement
|
6
7
|
from chatterbot import languages
|
7
8
|
from chatterbot import utils
|
8
9
|
import spacy
|
@@ -11,11 +12,41 @@ import spacy
|
|
11
12
|
class ChatBot(object):
|
12
13
|
"""
|
13
14
|
A conversational dialog chat bot.
|
15
|
+
|
16
|
+
:param name: A name is the only required parameter for the ChatBot class.
|
17
|
+
:type name: str
|
18
|
+
|
19
|
+
:keyword storage_adapter: The dot-notated import path to a storage adapter class.
|
20
|
+
Defaults to ``"chatterbot.storage.SQLStorageAdapter"``.
|
21
|
+
:type storage_adapter: str
|
22
|
+
|
23
|
+
:param logic_adapters: A list of dot-notated import paths to each logic adapter the bot uses.
|
24
|
+
Defaults to ``["chatterbot.logic.BestMatch"]``.
|
25
|
+
:type logic_adapters: list
|
26
|
+
|
27
|
+
:param tagger: The tagger to use for the chat bot.
|
28
|
+
Defaults to :class:`~chatterbot.tagging.PosLemmaTagger`
|
29
|
+
:type tagger: object
|
30
|
+
|
31
|
+
:param tagger_language: The language to use for the tagger.
|
32
|
+
Defaults to :class:`~chatterbot.languages.ENG`.
|
33
|
+
:type tagger_language: object
|
34
|
+
|
35
|
+
:param preprocessors: A list of preprocessor functions to use for the chat bot.
|
36
|
+
:type preprocessors: list
|
37
|
+
|
38
|
+
:param read_only: If True, the chat bot will not save any input it receives, defaults to False.
|
39
|
+
:type read_only: bool
|
40
|
+
|
41
|
+
:param logger: A ``Logger`` object.
|
42
|
+
:type logger: logging.Logger
|
14
43
|
"""
|
15
44
|
|
16
45
|
def __init__(self, name, **kwargs):
|
17
46
|
self.name = name
|
18
47
|
|
48
|
+
self.logger = kwargs.get('logger', logging.getLogger(__name__))
|
49
|
+
|
19
50
|
storage_adapter = kwargs.get('storage_adapter', 'chatterbot.storage.SQLStorageAdapter')
|
20
51
|
|
21
52
|
logic_adapters = kwargs.get('logic_adapters', [
|
@@ -30,11 +61,29 @@ class ChatBot(object):
|
|
30
61
|
|
31
62
|
self.storage = utils.initialize_class(storage_adapter, **kwargs)
|
32
63
|
|
33
|
-
|
64
|
+
tagger_language = kwargs.get('tagger_language', languages.ENG)
|
34
65
|
|
35
|
-
|
36
|
-
'
|
37
|
-
|
66
|
+
try:
|
67
|
+
Tagger = kwargs.get('tagger', PosLemmaTagger)
|
68
|
+
|
69
|
+
self.tagger = Tagger(language=tagger_language)
|
70
|
+
except IOError as io_error:
|
71
|
+
# Return a more helpful error message if possible
|
72
|
+
if "Can't find model" in str(io_error):
|
73
|
+
model_name = utils.get_model_for_language(tagger_language)
|
74
|
+
if hasattr(tagger_language, 'ENGLISH_NAME'):
|
75
|
+
language_name = tagger_language.ENGLISH_NAME
|
76
|
+
else:
|
77
|
+
language_name = tagger_language
|
78
|
+
raise self.ChatBotException(
|
79
|
+
'Setup error:\n'
|
80
|
+
f'The Spacy model for "{language_name}" language is missing.\n'
|
81
|
+
'Please install the model using the command:\n\n'
|
82
|
+
f'python -m spacy download {model_name}\n\n'
|
83
|
+
'See https://spacy.io/usage/models for more information about available models.'
|
84
|
+
) from io_error
|
85
|
+
else:
|
86
|
+
raise io_error
|
38
87
|
|
39
88
|
primary_search_algorithm = IndexedTextSearch(self, **kwargs)
|
40
89
|
text_search_algorithm = TextSearch(self, **kwargs)
|
@@ -63,18 +112,15 @@ class ChatBot(object):
|
|
63
112
|
# NOTE: 'xx' is the language code for a multi-language model
|
64
113
|
self.nlp = spacy.blank(self.tagger.language.ISO_639_1)
|
65
114
|
|
66
|
-
self.logger = kwargs.get('logger', logging.getLogger(__name__))
|
67
|
-
|
68
115
|
# Allow the bot to save input it receives so that it can learn
|
69
116
|
self.read_only = kwargs.get('read_only', False)
|
70
117
|
|
71
|
-
def get_response(self, statement=None, **kwargs):
|
118
|
+
def get_response(self, statement=None, **kwargs) -> Statement:
|
72
119
|
"""
|
73
120
|
Return the bot's response based on the input.
|
74
121
|
|
75
122
|
:param statement: An statement object or string.
|
76
123
|
:returns: A response to the input.
|
77
|
-
:rtype: Statement
|
78
124
|
|
79
125
|
:param additional_response_selection_parameters: Parameters to pass to the
|
80
126
|
chat bot's logic adapters to control response selection.
|
@@ -268,7 +314,7 @@ class ChatBot(object):
|
|
268
314
|
# Save the response statement
|
269
315
|
return self.storage.create(**statement.serialize())
|
270
316
|
|
271
|
-
def get_latest_response(self, conversation):
|
317
|
+
def get_latest_response(self, conversation: str):
|
272
318
|
"""
|
273
319
|
Returns the latest response in a conversation if it exists.
|
274
320
|
Returns None if a matching conversation cannot be found.
|
chatterbot/comparisons.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
This module contains various text-comparison algorithms
|
3
3
|
designed to compare one statement to another.
|
4
4
|
"""
|
5
|
-
from chatterbot import
|
5
|
+
from chatterbot.utils import get_model_for_language
|
6
6
|
from difflib import SequenceMatcher
|
7
7
|
import spacy
|
8
8
|
|
@@ -19,19 +19,17 @@ class Comparator:
|
|
19
19
|
def __call__(self, statement_a, statement_b):
|
20
20
|
return self.compare(statement_a, statement_b)
|
21
21
|
|
22
|
-
def compare_text(self, text_a, text_b):
|
22
|
+
def compare_text(self, text_a: str, text_b: str) -> float:
|
23
23
|
"""
|
24
24
|
Implemented in subclasses: compare text_a to text_b.
|
25
25
|
|
26
26
|
:return: The percent of similarity between the statements based on the implemented algorithm.
|
27
|
-
:rtype: float
|
28
27
|
"""
|
29
28
|
return 0
|
30
29
|
|
31
|
-
def compare(self, statement_a, statement_b):
|
30
|
+
def compare(self, statement_a, statement_b) -> float:
|
32
31
|
"""
|
33
32
|
:return: The percent of similarity between the statements based on the implemented algorithm.
|
34
|
-
:rtype: float
|
35
33
|
"""
|
36
34
|
return self.compare_text(statement_a.text, statement_b.text)
|
37
35
|
|
@@ -46,12 +44,11 @@ class LevenshteinDistance(Comparator):
|
|
46
44
|
based on the Levenshtein distance algorithm.
|
47
45
|
"""
|
48
46
|
|
49
|
-
def compare_text(self, text_a, text_b):
|
47
|
+
def compare_text(self, text_a: str, text_b: str) -> float:
|
50
48
|
"""
|
51
49
|
Compare the two pieces of text.
|
52
50
|
|
53
51
|
:return: The percent of similarity between the text of the statements.
|
54
|
-
:rtype: float
|
55
52
|
"""
|
56
53
|
|
57
54
|
# Return 0 if either statement has a None text value
|
@@ -100,22 +97,16 @@ class SpacySimilarity(Comparator):
|
|
100
97
|
def __init__(self, language):
|
101
98
|
super().__init__(language)
|
102
99
|
|
103
|
-
|
104
|
-
model = constants.DEFAULT_LANGUAGE_TO_SPACY_MODEL_MAP[self.language]
|
105
|
-
except KeyError as e:
|
106
|
-
raise KeyError(
|
107
|
-
f'Spacy model is not available for language {self.language}'
|
108
|
-
) from e
|
100
|
+
model = get_model_for_language(language)
|
109
101
|
|
110
102
|
# Disable the Named Entity Recognition (NER) component because it is not necessary
|
111
103
|
self.nlp = spacy.load(model, exclude=['ner'])
|
112
104
|
|
113
|
-
def compare_text(self, text_a, text_b):
|
105
|
+
def compare_text(self, text_a: str, text_b: str) -> float:
|
114
106
|
"""
|
115
107
|
Compare the similarity of two strings.
|
116
108
|
|
117
109
|
:return: The percent of similarity between the closest synset distance.
|
118
|
-
:rtype: float
|
119
110
|
"""
|
120
111
|
|
121
112
|
# Return 0 if either statement has a None text value
|
@@ -157,17 +148,12 @@ class JaccardSimilarity(Comparator):
|
|
157
148
|
def __init__(self, language):
|
158
149
|
super().__init__(language)
|
159
150
|
|
160
|
-
|
161
|
-
model = constants.DEFAULT_LANGUAGE_TO_SPACY_MODEL_MAP[self.language]
|
162
|
-
except KeyError as e:
|
163
|
-
raise KeyError(
|
164
|
-
f'Spacy model is not available for language {self.language}'
|
165
|
-
) from e
|
151
|
+
model = get_model_for_language(language)
|
166
152
|
|
167
153
|
# Disable the Named Entity Recognition (NER) component because it is not necessary
|
168
154
|
self.nlp = spacy.load(model, exclude=['ner'])
|
169
155
|
|
170
|
-
def compare_text(self, text_a, text_b):
|
156
|
+
def compare_text(self, text_a: str, text_b: str) -> float:
|
171
157
|
"""
|
172
158
|
Return the calculated similarity of two
|
173
159
|
statements based on the Jaccard index.
|
chatterbot/conversation.py
CHANGED
@@ -32,7 +32,8 @@ class AbstractBaseTag(models.Model):
|
|
32
32
|
|
33
33
|
name = models.SlugField(
|
34
34
|
max_length=constants.TAG_NAME_MAX_LENGTH,
|
35
|
-
unique=True
|
35
|
+
unique=True,
|
36
|
+
help_text='The unique name of the tag.'
|
36
37
|
)
|
37
38
|
|
38
39
|
class Meta:
|
@@ -49,16 +50,19 @@ class AbstractBaseStatement(models.Model, StatementMixin):
|
|
49
50
|
"""
|
50
51
|
|
51
52
|
text = models.CharField(
|
52
|
-
max_length=constants.STATEMENT_TEXT_MAX_LENGTH
|
53
|
+
max_length=constants.STATEMENT_TEXT_MAX_LENGTH,
|
54
|
+
help_text='The text of the statement.'
|
53
55
|
)
|
54
56
|
|
55
57
|
search_text = models.CharField(
|
56
58
|
max_length=constants.STATEMENT_TEXT_MAX_LENGTH,
|
57
|
-
blank=True
|
59
|
+
blank=True,
|
60
|
+
help_text='A modified version of the statement text optimized for searching.'
|
58
61
|
)
|
59
62
|
|
60
63
|
conversation = models.CharField(
|
61
|
-
max_length=constants.CONVERSATION_LABEL_MAX_LENGTH
|
64
|
+
max_length=constants.CONVERSATION_LABEL_MAX_LENGTH,
|
65
|
+
help_text='A label used to link this statement to a conversation.'
|
62
66
|
)
|
63
67
|
|
64
68
|
created_at = models.DateTimeField(
|
@@ -68,21 +72,25 @@ class AbstractBaseStatement(models.Model, StatementMixin):
|
|
68
72
|
|
69
73
|
in_response_to = models.CharField(
|
70
74
|
max_length=constants.STATEMENT_TEXT_MAX_LENGTH,
|
71
|
-
null=True
|
75
|
+
null=True,
|
76
|
+
help_text='The text of the statement that this statement is in response to.'
|
72
77
|
)
|
73
78
|
|
74
79
|
search_in_response_to = models.CharField(
|
75
80
|
max_length=constants.STATEMENT_TEXT_MAX_LENGTH,
|
76
|
-
blank=True
|
81
|
+
blank=True,
|
82
|
+
help_text='A modified version of the in_response_to text optimized for searching.'
|
77
83
|
)
|
78
84
|
|
79
85
|
persona = models.CharField(
|
80
|
-
max_length=constants.PERSONA_MAX_LENGTH
|
86
|
+
max_length=constants.PERSONA_MAX_LENGTH,
|
87
|
+
help_text='A label used to link this statement to a persona.'
|
81
88
|
)
|
82
89
|
|
83
90
|
tags = models.ManyToManyField(
|
84
91
|
TAG_MODEL,
|
85
|
-
related_name='statements'
|
92
|
+
related_name='statements',
|
93
|
+
help_text='The tags that are associated with this statement.'
|
86
94
|
)
|
87
95
|
|
88
96
|
# This is the confidence with which the chat bot believes
|
@@ -6,3 +6,10 @@ class DjangoChatterBotConfig(AppConfig):
|
|
6
6
|
name = 'chatterbot.ext.django_chatterbot'
|
7
7
|
label = 'django_chatterbot'
|
8
8
|
verbose_name = 'Django ChatterBot'
|
9
|
+
|
10
|
+
def ready(self):
|
11
|
+
from chatterbot.ext.django_chatterbot import settings as defaults
|
12
|
+
from django.conf import settings
|
13
|
+
|
14
|
+
settings.CHATTERBOT = getattr(settings, 'CHATTERBOT', {})
|
15
|
+
settings.CHATTERBOT.update(defaults.CHATTERBOT_DEFAULTS)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Generated by Django 4.1 on 2025-03-29 23:27
|
2
|
+
|
3
|
+
from django.db import migrations, models
|
4
|
+
|
5
|
+
|
6
|
+
class Migration(migrations.Migration):
|
7
|
+
|
8
|
+
dependencies = [
|
9
|
+
('django_chatterbot', '0019_alter_statement_id_alter_tag_id_and_more'),
|
10
|
+
]
|
11
|
+
|
12
|
+
operations = [
|
13
|
+
migrations.AlterField(
|
14
|
+
model_name='statement',
|
15
|
+
name='conversation',
|
16
|
+
field=models.CharField(help_text='A label used to link this statement to a conversation.', max_length=32),
|
17
|
+
),
|
18
|
+
migrations.AlterField(
|
19
|
+
model_name='statement',
|
20
|
+
name='in_response_to',
|
21
|
+
field=models.CharField(help_text='The text of the statement that this statement is in response to.', max_length=255, null=True),
|
22
|
+
),
|
23
|
+
migrations.AlterField(
|
24
|
+
model_name='statement',
|
25
|
+
name='persona',
|
26
|
+
field=models.CharField(help_text='A label used to link this statement to a persona.', max_length=50),
|
27
|
+
),
|
28
|
+
migrations.AlterField(
|
29
|
+
model_name='statement',
|
30
|
+
name='search_in_response_to',
|
31
|
+
field=models.CharField(blank=True, help_text='A modified version of the in_response_to text optimized for searching.', max_length=255),
|
32
|
+
),
|
33
|
+
migrations.AlterField(
|
34
|
+
model_name='statement',
|
35
|
+
name='search_text',
|
36
|
+
field=models.CharField(blank=True, help_text='A modified version of the statement text optimized for searching.', max_length=255),
|
37
|
+
),
|
38
|
+
migrations.AlterField(
|
39
|
+
model_name='statement',
|
40
|
+
name='tags',
|
41
|
+
field=models.ManyToManyField(help_text='The tags that are associated with this statement.', related_name='statements', to='django_chatterbot.tag'),
|
42
|
+
),
|
43
|
+
migrations.AlterField(
|
44
|
+
model_name='statement',
|
45
|
+
name='text',
|
46
|
+
field=models.CharField(help_text='The text of the statement.', max_length=255),
|
47
|
+
),
|
48
|
+
migrations.AlterField(
|
49
|
+
model_name='tag',
|
50
|
+
name='name',
|
51
|
+
field=models.SlugField(help_text='The unique name of the tag.', unique=True),
|
52
|
+
),
|
53
|
+
]
|
@@ -5,7 +5,7 @@ from django.conf import settings
|
|
5
5
|
from chatterbot import constants
|
6
6
|
|
7
7
|
|
8
|
-
|
8
|
+
CHATTERBOT = getattr(settings, 'CHATTERBOT', {})
|
9
9
|
|
10
10
|
CHATTERBOT_DEFAULTS = {
|
11
11
|
'name': 'ChatterBot',
|
@@ -13,5 +13,4 @@ CHATTERBOT_DEFAULTS = {
|
|
13
13
|
'django_app_name': constants.DEFAULT_DJANGO_APP_NAME
|
14
14
|
}
|
15
15
|
|
16
|
-
CHATTERBOT
|
17
|
-
CHATTERBOT.update(CHATTERBOT_SETTINGS)
|
16
|
+
CHATTERBOT.update(CHATTERBOT_DEFAULTS)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from sqlalchemy import Table, Column, Integer, String, DateTime, ForeignKey
|
2
|
-
from sqlalchemy.orm import relationship
|
2
|
+
from sqlalchemy.orm import relationship, declarative_base
|
3
3
|
from sqlalchemy.sql import func
|
4
|
-
from sqlalchemy.ext.declarative import declared_attr
|
4
|
+
from sqlalchemy.ext.declarative import declared_attr
|
5
5
|
|
6
6
|
from chatterbot.conversation import StatementMixin
|
7
7
|
from chatterbot import constants
|
@@ -4,6 +4,7 @@ from chatterbot.adapters import Adapter
|
|
4
4
|
from chatterbot.storage import StorageAdapter
|
5
5
|
from chatterbot.search import IndexedTextSearch
|
6
6
|
from chatterbot.conversation import Statement
|
7
|
+
from chatterbot import utils
|
7
8
|
|
8
9
|
|
9
10
|
class LogicAdapter(Adapter):
|
@@ -28,7 +29,7 @@ class LogicAdapter(Adapter):
|
|
28
29
|
:type response_selection_method: collections.abc.Callable
|
29
30
|
|
30
31
|
:param default_response:
|
31
|
-
The default response returned by this logic
|
32
|
+
The default response returned by this logic adapter
|
32
33
|
if there is no other possible response to return.
|
33
34
|
:type default_response: str or list or tuple
|
34
35
|
"""
|
@@ -50,6 +51,14 @@ class LogicAdapter(Adapter):
|
|
50
51
|
'maximum_similarity_threshold', 0.95
|
51
52
|
)
|
52
53
|
|
54
|
+
if response_selection_method := kwargs.get('response_selection_method'):
|
55
|
+
if isinstance(response_selection_method, str):
|
56
|
+
# If an import path is provided, import the method
|
57
|
+
response_selection_method = utils.import_module(
|
58
|
+
response_selection_method
|
59
|
+
)
|
60
|
+
kwargs['response_selection_method'] = response_selection_method
|
61
|
+
|
53
62
|
# By default, select the first available response
|
54
63
|
self.select_response = kwargs.get(
|
55
64
|
'response_selection_method',
|
@@ -68,18 +77,16 @@ class LogicAdapter(Adapter):
|
|
68
77
|
Statement(text=default) for default in default_responses
|
69
78
|
]
|
70
79
|
|
71
|
-
def can_process(self, statement):
|
80
|
+
def can_process(self, statement) -> bool:
|
72
81
|
"""
|
73
82
|
A preliminary check that is called to determine if a
|
74
83
|
logic adapter can process a given statement. By default,
|
75
84
|
this method returns true but it can be overridden in
|
76
85
|
child classes as needed.
|
77
|
-
|
78
|
-
:rtype: bool
|
79
86
|
"""
|
80
87
|
return True
|
81
88
|
|
82
|
-
def process(self, statement, additional_response_selection_parameters=None):
|
89
|
+
def process(self, statement, additional_response_selection_parameters=None) -> Statement:
|
83
90
|
"""
|
84
91
|
Override this method and implement your logic for selecting a response to an input statement.
|
85
92
|
|
@@ -97,12 +104,10 @@ class LogicAdapter(Adapter):
|
|
97
104
|
:param additional_response_selection_parameters: Parameters to be used when
|
98
105
|
filtering results to choose a response from.
|
99
106
|
:type additional_response_selection_parameters: dict
|
100
|
-
|
101
|
-
:rtype: Statement
|
102
107
|
"""
|
103
108
|
raise self.AdapterMethodNotImplementedError()
|
104
109
|
|
105
|
-
def get_default_response(self, input_statement):
|
110
|
+
def get_default_response(self, input_statement: Statement) -> Statement:
|
106
111
|
"""
|
107
112
|
This method is called when a logic adapter is unable to generate any
|
108
113
|
other meaningful response.
|
@@ -125,7 +130,7 @@ class LogicAdapter(Adapter):
|
|
125
130
|
return response
|
126
131
|
|
127
132
|
@property
|
128
|
-
def class_name(self):
|
133
|
+
def class_name(self) -> str:
|
129
134
|
"""
|
130
135
|
Return the name of the current logic adapter class.
|
131
136
|
This is typically used for logging and debugging.
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from chatterbot.logic import LogicAdapter
|
2
2
|
from chatterbot.conversation import Statement
|
3
|
-
from chatterbot import
|
3
|
+
from chatterbot import languages
|
4
|
+
from chatterbot.utils import get_model_for_language
|
4
5
|
import spacy
|
5
6
|
|
6
7
|
|
@@ -35,12 +36,7 @@ class SpecificResponseAdapter(LogicAdapter):
|
|
35
36
|
self._output_text = kwargs.get('output_text')
|
36
37
|
|
37
38
|
def _initialize_nlp(self, language):
|
38
|
-
|
39
|
-
model = constants.DEFAULT_LANGUAGE_TO_SPACY_MODEL_MAP[language]
|
40
|
-
except KeyError as e:
|
41
|
-
raise KeyError(
|
42
|
-
f'Spacy model is not available for language {language}'
|
43
|
-
) from e
|
39
|
+
model = get_model_for_language(language)
|
44
40
|
|
45
41
|
return spacy.load(model)
|
46
42
|
|
chatterbot/logic/time_adapter.py
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
from datetime import datetime
|
2
|
-
from chatterbot import
|
2
|
+
from chatterbot import languages
|
3
3
|
from chatterbot.logic import LogicAdapter
|
4
4
|
from chatterbot.conversation import Statement
|
5
|
+
from chatterbot.utils import get_model_for_language
|
5
6
|
import spacy
|
6
7
|
|
7
8
|
|
@@ -36,12 +37,7 @@ class TimeLogicAdapter(LogicAdapter):
|
|
36
37
|
|
37
38
|
language = kwargs.get('language', languages.ENG)
|
38
39
|
|
39
|
-
|
40
|
-
model = constants.DEFAULT_LANGUAGE_TO_SPACY_MODEL_MAP[language]
|
41
|
-
except KeyError as e:
|
42
|
-
raise KeyError(
|
43
|
-
f'Spacy model is not available for language {language}'
|
44
|
-
) from e
|
40
|
+
model = get_model_for_language(language)
|
45
41
|
|
46
42
|
self.nlp = spacy.load(model)
|
47
43
|
|
chatterbot/preprocessors.py
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
"""
|
2
2
|
Statement pre-processors.
|
3
3
|
"""
|
4
|
+
from chatterbot.conversation import Statement
|
4
5
|
from unicodedata import normalize
|
5
6
|
from re import sub as re_sub
|
6
7
|
from html import unescape
|
7
8
|
|
8
9
|
|
9
|
-
def clean_whitespace(statement):
|
10
|
+
def clean_whitespace(statement: Statement) -> Statement:
|
10
11
|
"""
|
11
12
|
Remove any consecutive whitespace characters from the statement text.
|
12
13
|
"""
|
@@ -24,7 +25,7 @@ def clean_whitespace(statement):
|
|
24
25
|
return statement
|
25
26
|
|
26
27
|
|
27
|
-
def unescape_html(statement):
|
28
|
+
def unescape_html(statement: Statement) -> Statement:
|
28
29
|
"""
|
29
30
|
Convert escaped html characters into unescaped html characters.
|
30
31
|
For example: "<b>" becomes "<b>".
|
@@ -34,7 +35,7 @@ def unescape_html(statement):
|
|
34
35
|
return statement
|
35
36
|
|
36
37
|
|
37
|
-
def convert_to_ascii(statement):
|
38
|
+
def convert_to_ascii(statement: Statement) -> Statement:
|
38
39
|
"""
|
39
40
|
Converts unicode characters to ASCII character equivalents.
|
40
41
|
For example: "på fédéral" becomes "pa federal".
|
chatterbot/response_selection.py
CHANGED
@@ -2,10 +2,11 @@
|
|
2
2
|
Response selection methods determines which response should be used in
|
3
3
|
the event that multiple responses are generated within a logic adapter.
|
4
4
|
"""
|
5
|
+
from chatterbot.conversation import Statement
|
5
6
|
import logging
|
6
7
|
|
7
8
|
|
8
|
-
def get_most_frequent_response(input_statement, response_list, storage=None):
|
9
|
+
def get_most_frequent_response(input_statement, response_list, storage=None) -> Statement:
|
9
10
|
"""
|
10
11
|
:param input_statement: A statement, that closely matches an input to the chat bot.
|
11
12
|
:type input_statement: Statement
|
@@ -18,7 +19,6 @@ def get_most_frequent_response(input_statement, response_list, storage=None):
|
|
18
19
|
:type storage: StorageAdapter
|
19
20
|
|
20
21
|
:return: The response statement with the greatest number of occurrences.
|
21
|
-
:rtype: Statement
|
22
22
|
"""
|
23
23
|
matching_response = None
|
24
24
|
occurrence_count = -1
|
@@ -41,7 +41,7 @@ def get_most_frequent_response(input_statement, response_list, storage=None):
|
|
41
41
|
return matching_response
|
42
42
|
|
43
43
|
|
44
|
-
def get_first_response(input_statement, response_list, storage=None):
|
44
|
+
def get_first_response(input_statement, response_list, storage=None) -> Statement:
|
45
45
|
"""
|
46
46
|
:param input_statement: A statement, that closely matches an input to the chat bot.
|
47
47
|
:type input_statement: Statement
|
@@ -54,7 +54,6 @@ def get_first_response(input_statement, response_list, storage=None):
|
|
54
54
|
:type storage: StorageAdapter
|
55
55
|
|
56
56
|
:return: Return the first statement in the response list.
|
57
|
-
:rtype: Statement
|
58
57
|
"""
|
59
58
|
logger = logging.getLogger(__name__)
|
60
59
|
logger.info('Selecting first response from list of {} options.'.format(
|
@@ -63,7 +62,7 @@ def get_first_response(input_statement, response_list, storage=None):
|
|
63
62
|
return response_list[0]
|
64
63
|
|
65
64
|
|
66
|
-
def get_random_response(input_statement, response_list, storage=None):
|
65
|
+
def get_random_response(input_statement, response_list, storage=None) -> Statement:
|
67
66
|
"""
|
68
67
|
:param input_statement: A statement, that closely matches an input to the chat bot.
|
69
68
|
:type input_statement: Statement
|
@@ -76,7 +75,6 @@ def get_random_response(input_statement, response_list, storage=None):
|
|
76
75
|
:type storage: StorageAdapter
|
77
76
|
|
78
77
|
:return: Choose a random response from the selection.
|
79
|
-
:rtype: Statement
|
80
78
|
"""
|
81
79
|
from random import choice
|
82
80
|
logger = logging.getLogger(__name__)
|
chatterbot/search.py
CHANGED
@@ -149,58 +149,3 @@ class TextSearch:
|
|
149
149
|
))
|
150
150
|
|
151
151
|
yield statement
|
152
|
-
|
153
|
-
|
154
|
-
class VectorSearch:
|
155
|
-
"""
|
156
|
-
.. note:: BETA feature: this search method is new and experimental.
|
157
|
-
|
158
|
-
Search for similar text based on a :term:`vector database`.
|
159
|
-
"""
|
160
|
-
|
161
|
-
name = 'vector_search'
|
162
|
-
|
163
|
-
def __init__(self, chatbot, **kwargs):
|
164
|
-
from chatterbot.storage import RedisVectorStorageAdapter
|
165
|
-
|
166
|
-
# Good documentation:
|
167
|
-
# https://python.langchain.com/docs/integrations/vectorstores/redis/
|
168
|
-
#
|
169
|
-
# https://hub.docker.com/r/redis/redis-stack
|
170
|
-
|
171
|
-
# Mondodb:
|
172
|
-
# > Vector Search is only supported on Atlas Clusters
|
173
|
-
# https://www.mongodb.com/community/forums/t/can-a-local-mongodb-instance-be-used-when-working-with-langchain-mongodbatlasvectorsearch/265356
|
174
|
-
|
175
|
-
# FAISS:
|
176
|
-
# https://python.langchain.com/docs/integrations/vectorstores/faiss/
|
177
|
-
|
178
|
-
print("Starting Redis Vector Store")
|
179
|
-
|
180
|
-
# TODO: look into:
|
181
|
-
# https://python.langchain.com/api_reference/redis/chat_message_history/langchain_redis.chat_message_history.RedisChatMessageHistory.html
|
182
|
-
|
183
|
-
# The VectorSearch class is only compatible with the RedisVectorStorageAdapter
|
184
|
-
if not isinstance(chatbot.storage, RedisVectorStorageAdapter):
|
185
|
-
raise Exception(
|
186
|
-
'The VectorSearch search method requires the RedisVectorStorageAdapter storage adapter.'
|
187
|
-
)
|
188
|
-
|
189
|
-
def search(self, input_statement, **additional_parameters):
|
190
|
-
print("Querying Vector Store")
|
191
|
-
|
192
|
-
# Similarity search with score and filter
|
193
|
-
# NOTE: It looks like `return_all` is needed to return the full document
|
194
|
-
# specifically what we need here is the ID
|
195
|
-
scored_results = self.storage.vector_store.similarity_search_with_score(
|
196
|
-
input_statement.text, k=2, return_all=True
|
197
|
-
)
|
198
|
-
# sort_by="score", filter={"category": "likes"})
|
199
|
-
|
200
|
-
print("Similarity Search with Score Results:\n")
|
201
|
-
for doc, score in scored_results:
|
202
|
-
print(f"Content: {doc.page_content[:150]}...")
|
203
|
-
print(f"ID: {doc.id}")
|
204
|
-
print(f"Metadata: {doc.metadata}")
|
205
|
-
print(f"Score: {score}")
|
206
|
-
print()
|