ChatterBot 1.2.1__tar.gz → 1.2.3__tar.gz

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.
Files changed (92) hide show
  1. {chatterbot-1.2.1 → chatterbot-1.2.3}/ChatterBot.egg-info/PKG-INFO +9 -3
  2. {chatterbot-1.2.1 → chatterbot-1.2.3}/ChatterBot.egg-info/SOURCES.txt +2 -0
  3. {chatterbot-1.2.1 → chatterbot-1.2.3}/ChatterBot.egg-info/requires.txt +10 -1
  4. {chatterbot-1.2.1 → chatterbot-1.2.3}/PKG-INFO +9 -3
  5. {chatterbot-1.2.1 → chatterbot-1.2.3}/README.md +0 -1
  6. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/__init__.py +1 -1
  7. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/chatterbot.py +41 -8
  8. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/comparisons.py +32 -15
  9. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/logic/best_match.py +42 -35
  10. chatterbot-1.2.3/chatterbot/logic/specific_response.py +80 -0
  11. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/logic/unit_conversion.py +4 -3
  12. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/response_selection.py +1 -1
  13. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/search.py +65 -17
  14. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/storage/__init__.py +2 -0
  15. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/storage/django_storage.py +13 -23
  16. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/storage/mongodb.py +7 -26
  17. chatterbot-1.2.3/chatterbot/storage/redis.py +390 -0
  18. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/storage/sql_storage.py +77 -68
  19. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/storage/storage_adapter.py +9 -7
  20. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/trainers.py +3 -3
  21. chatterbot-1.2.3/chatterbot/vectorstores.py +74 -0
  22. {chatterbot-1.2.1 → chatterbot-1.2.3}/pyproject.toml +12 -3
  23. {chatterbot-1.2.1 → chatterbot-1.2.3}/setup.cfg +2 -5
  24. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_chatbot.py +43 -33
  25. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_response_selection.py +1 -1
  26. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_search.py +39 -39
  27. chatterbot-1.2.1/chatterbot/logic/specific_response.py +0 -37
  28. {chatterbot-1.2.1 → chatterbot-1.2.3}/ChatterBot.egg-info/dependency_links.txt +0 -0
  29. {chatterbot-1.2.1 → chatterbot-1.2.3}/ChatterBot.egg-info/top_level.txt +0 -0
  30. {chatterbot-1.2.1 → chatterbot-1.2.3}/LICENSE +0 -0
  31. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/__main__.py +0 -0
  32. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/adapters.py +0 -0
  33. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/components.py +0 -0
  34. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/constants.py +0 -0
  35. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/conversation.py +0 -0
  36. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/corpus.py +0 -0
  37. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/exceptions.py +0 -0
  38. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/__init__.py +0 -0
  39. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/__init__.py +0 -0
  40. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/abstract_models.py +0 -0
  41. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/admin.py +0 -0
  42. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/apps.py +0 -0
  43. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0001_initial.py +0 -0
  44. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0002_statement_extra_data.py +0 -0
  45. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0003_change_occurrence_default.py +0 -0
  46. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0004_rename_in_response_to.py +0 -0
  47. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0005_statement_created_at.py +0 -0
  48. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0006_create_conversation.py +0 -0
  49. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0007_response_created_at.py +0 -0
  50. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0008_update_conversations.py +0 -0
  51. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0009_tags.py +0 -0
  52. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0010_statement_text.py +0 -0
  53. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0011_blank_extra_data.py +0 -0
  54. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0012_statement_created_at.py +0 -0
  55. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0013_change_conversations.py +0 -0
  56. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0014_remove_statement_extra_data.py +0 -0
  57. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0015_statement_persona.py +0 -0
  58. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0016_statement_stemmed_text.py +0 -0
  59. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0017_tags_unique.py +0 -0
  60. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0018_text_max_length.py +0 -0
  61. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/0019_alter_statement_id_alter_tag_id_and_more.py +0 -0
  62. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/migrations/__init__.py +0 -0
  63. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/model_admin.py +0 -0
  64. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/models.py +0 -0
  65. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/django_chatterbot/settings.py +0 -0
  66. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/sqlalchemy_app/__init__.py +0 -0
  67. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/ext/sqlalchemy_app/models.py +0 -0
  68. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/filters.py +0 -0
  69. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/languages.py +0 -0
  70. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/logic/__init__.py +0 -0
  71. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/logic/logic_adapter.py +0 -0
  72. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/logic/mathematical_evaluation.py +0 -0
  73. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/logic/time_adapter.py +0 -0
  74. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/parsing.py +0 -0
  75. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/preprocessors.py +0 -0
  76. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/tagging.py +0 -0
  77. {chatterbot-1.2.1 → chatterbot-1.2.3}/chatterbot/utils.py +0 -0
  78. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_adapter_validation.py +0 -0
  79. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_benchmarks.py +0 -0
  80. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_cli.py +0 -0
  81. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_comparisons.py +0 -0
  82. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_conversations.py +0 -0
  83. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_corpus.py +0 -0
  84. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_examples.py +0 -0
  85. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_filters.py +0 -0
  86. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_initialization.py +0 -0
  87. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_languages.py +0 -0
  88. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_parsing.py +0 -0
  89. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_preprocessors.py +0 -0
  90. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_tagging.py +0 -0
  91. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_turing.py +0 -0
  92. {chatterbot-1.2.1 → chatterbot-1.2.3}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ChatterBot
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Summary: ChatterBot is a machine learning, conversational dialog engine
5
5
  Author: Gunther Cox
6
6
  License: Copyright (c) 2016 - 2025, Gunther Cox
@@ -53,11 +53,18 @@ Requires-Dist: flake8; extra == "test"
53
53
  Requires-Dist: coverage; extra == "test"
54
54
  Requires-Dist: nose; extra == "test"
55
55
  Requires-Dist: sphinx<8.2,>=5.3; extra == "test"
56
+ Requires-Dist: sphinx-sitemap>=2.6.0; extra == "test"
56
57
  Provides-Extra: dev
57
58
  Requires-Dist: pint>=0.8.1; extra == "dev"
58
- Requires-Dist: pymongo<4.12,>=4.11; extra == "dev"
59
59
  Requires-Dist: pyyaml<7.0,>=6.0; extra == "dev"
60
60
  Requires-Dist: chatterbot-corpus>=1.2.2; extra == "dev"
61
+ Provides-Extra: redis
62
+ Requires-Dist: redis[hiredis]; extra == "redis"
63
+ Requires-Dist: langchain-redis; extra == "redis"
64
+ Requires-Dist: langchain-huggingface; extra == "redis"
65
+ Requires-Dist: sentence-transformers; extra == "redis"
66
+ Provides-Extra: mongodb
67
+ Requires-Dist: pymongo<4.12,>=4.11; extra == "mongodb"
61
68
 
62
69
  ![ChatterBot: Machine learning in Python](https://i.imgur.com/b3SCmGT.png)
63
70
 
@@ -70,7 +77,6 @@ to be trained to speak any language.
70
77
 
71
78
  [![Package Version](https://img.shields.io/pypi/v/chatterbot.svg)](https://pypi.python.org/pypi/chatterbot/)
72
79
  [![Python 3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/release/python-360/)
73
- [![Django 2.0](https://img.shields.io/badge/Django-2.0-blue.svg)](https://docs.djangoproject.com/en/2.1/releases/2.0/)
74
80
  [![Coverage Status](https://img.shields.io/coveralls/gunthercox/ChatterBot.svg)](https://coveralls.io/r/gunthercox/ChatterBot)
75
81
  [![Code Climate](https://codeclimate.com/github/gunthercox/ChatterBot/badges/gpa.svg)](https://codeclimate.com/github/gunthercox/ChatterBot)
76
82
  [![Join the chat at https://gitter.im/chatterbot/Lobby](https://badges.gitter.im/chatterbot/Lobby.svg)](https://gitter.im/chatterbot/Lobby?utm_source=badge&utm_medium=badge&utm_content=badge)
@@ -26,6 +26,7 @@ chatterbot/search.py
26
26
  chatterbot/tagging.py
27
27
  chatterbot/trainers.py
28
28
  chatterbot/utils.py
29
+ chatterbot/vectorstores.py
29
30
  chatterbot/ext/__init__.py
30
31
  chatterbot/ext/django_chatterbot/__init__.py
31
32
  chatterbot/ext/django_chatterbot/abstract_models.py
@@ -66,6 +67,7 @@ chatterbot/logic/unit_conversion.py
66
67
  chatterbot/storage/__init__.py
67
68
  chatterbot/storage/django_storage.py
68
69
  chatterbot/storage/mongodb.py
70
+ chatterbot/storage/redis.py
69
71
  chatterbot/storage/sql_storage.py
70
72
  chatterbot/storage/storage_adapter.py
71
73
  tests/test_adapter_validation.py
@@ -6,12 +6,21 @@ tqdm
6
6
 
7
7
  [dev]
8
8
  pint>=0.8.1
9
- pymongo<4.12,>=4.11
10
9
  pyyaml<7.0,>=6.0
11
10
  chatterbot-corpus>=1.2.2
12
11
 
12
+ [mongodb]
13
+ pymongo<4.12,>=4.11
14
+
15
+ [redis]
16
+ redis[hiredis]
17
+ langchain-redis
18
+ langchain-huggingface
19
+ sentence-transformers
20
+
13
21
  [test]
14
22
  flake8
15
23
  coverage
16
24
  nose
17
25
  sphinx<8.2,>=5.3
26
+ sphinx-sitemap>=2.6.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: ChatterBot
3
- Version: 1.2.1
3
+ Version: 1.2.3
4
4
  Summary: ChatterBot is a machine learning, conversational dialog engine
5
5
  Author: Gunther Cox
6
6
  License: Copyright (c) 2016 - 2025, Gunther Cox
@@ -53,11 +53,18 @@ Requires-Dist: flake8; extra == "test"
53
53
  Requires-Dist: coverage; extra == "test"
54
54
  Requires-Dist: nose; extra == "test"
55
55
  Requires-Dist: sphinx<8.2,>=5.3; extra == "test"
56
+ Requires-Dist: sphinx-sitemap>=2.6.0; extra == "test"
56
57
  Provides-Extra: dev
57
58
  Requires-Dist: pint>=0.8.1; extra == "dev"
58
- Requires-Dist: pymongo<4.12,>=4.11; extra == "dev"
59
59
  Requires-Dist: pyyaml<7.0,>=6.0; extra == "dev"
60
60
  Requires-Dist: chatterbot-corpus>=1.2.2; extra == "dev"
61
+ Provides-Extra: redis
62
+ Requires-Dist: redis[hiredis]; extra == "redis"
63
+ Requires-Dist: langchain-redis; extra == "redis"
64
+ Requires-Dist: langchain-huggingface; extra == "redis"
65
+ Requires-Dist: sentence-transformers; extra == "redis"
66
+ Provides-Extra: mongodb
67
+ Requires-Dist: pymongo<4.12,>=4.11; extra == "mongodb"
61
68
 
62
69
  ![ChatterBot: Machine learning in Python](https://i.imgur.com/b3SCmGT.png)
63
70
 
@@ -70,7 +77,6 @@ to be trained to speak any language.
70
77
 
71
78
  [![Package Version](https://img.shields.io/pypi/v/chatterbot.svg)](https://pypi.python.org/pypi/chatterbot/)
72
79
  [![Python 3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/release/python-360/)
73
- [![Django 2.0](https://img.shields.io/badge/Django-2.0-blue.svg)](https://docs.djangoproject.com/en/2.1/releases/2.0/)
74
80
  [![Coverage Status](https://img.shields.io/coveralls/gunthercox/ChatterBot.svg)](https://coveralls.io/r/gunthercox/ChatterBot)
75
81
  [![Code Climate](https://codeclimate.com/github/gunthercox/ChatterBot/badges/gpa.svg)](https://codeclimate.com/github/gunthercox/ChatterBot)
76
82
  [![Join the chat at https://gitter.im/chatterbot/Lobby](https://badges.gitter.im/chatterbot/Lobby.svg)](https://gitter.im/chatterbot/Lobby?utm_source=badge&utm_medium=badge&utm_content=badge)
@@ -9,7 +9,6 @@ to be trained to speak any language.
9
9
 
10
10
  [![Package Version](https://img.shields.io/pypi/v/chatterbot.svg)](https://pypi.python.org/pypi/chatterbot/)
11
11
  [![Python 3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/release/python-360/)
12
- [![Django 2.0](https://img.shields.io/badge/Django-2.0-blue.svg)](https://docs.djangoproject.com/en/2.1/releases/2.0/)
13
12
  [![Coverage Status](https://img.shields.io/coveralls/gunthercox/ChatterBot.svg)](https://coveralls.io/r/gunthercox/ChatterBot)
14
13
  [![Code Climate](https://codeclimate.com/github/gunthercox/ChatterBot/badges/gpa.svg)](https://codeclimate.com/github/gunthercox/ChatterBot)
15
14
  [![Join the chat at https://gitter.im/chatterbot/Lobby](https://badges.gitter.im/chatterbot/Lobby.svg)](https://gitter.im/chatterbot/Lobby?utm_source=badge&utm_medium=badge&utm_content=badge)
@@ -4,7 +4,7 @@ ChatterBot is a machine learning, conversational dialog engine.
4
4
  from .chatterbot import ChatBot
5
5
 
6
6
 
7
- __version__ = '1.2.1'
7
+ __version__ = '1.2.3'
8
8
 
9
9
  __all__ = (
10
10
  'ChatBot',
@@ -2,7 +2,10 @@ import logging
2
2
  from chatterbot.storage import StorageAdapter
3
3
  from chatterbot.logic import LogicAdapter
4
4
  from chatterbot.search import TextSearch, IndexedTextSearch
5
+ from chatterbot.tagging import PosLemmaTagger
6
+ from chatterbot import languages
5
7
  from chatterbot import utils
8
+ import spacy
6
9
 
7
10
 
8
11
  class ChatBot(object):
@@ -27,6 +30,12 @@ class ChatBot(object):
27
30
 
28
31
  self.storage = utils.initialize_class(storage_adapter, **kwargs)
29
32
 
33
+ Tagger = kwargs.get('tagger', PosLemmaTagger)
34
+
35
+ self.tagger = Tagger(language=kwargs.get(
36
+ 'tagger_language', languages.ENG
37
+ ))
38
+
30
39
  primary_search_algorithm = IndexedTextSearch(self, **kwargs)
31
40
  text_search_algorithm = TextSearch(self, **kwargs)
32
41
 
@@ -51,6 +60,9 @@ class ChatBot(object):
51
60
  for preprocessor in preprocessors:
52
61
  self.preprocessors.append(utils.import_module(preprocessor))
53
62
 
63
+ # NOTE: 'xx' is the language code for a multi-language model
64
+ self.nlp = spacy.blank(self.tagger.language.ISO_639_1)
65
+
54
66
  self.logger = kwargs.get('logger', logging.getLogger(__name__))
55
67
 
56
68
  # Allow the bot to save input it receives so that it can learn
@@ -105,16 +117,27 @@ class ChatBot(object):
105
117
  for preprocessor in self.preprocessors:
106
118
  input_statement = preprocessor(input_statement)
107
119
 
120
+ # Mark the statement as being a response to the previous
121
+ if input_statement.in_response_to is None:
122
+ previous_statement = self.get_latest_response(input_statement.conversation)
123
+ if previous_statement:
124
+ input_statement.in_response_to = previous_statement.text
125
+
108
126
  # Make sure the input statement has its search text saved
109
127
 
110
128
  if not input_statement.search_text:
111
- _search_text = self.storage.tagger.get_text_index_string(input_statement.text)
129
+ _search_text = self.tagger.get_text_index_string(input_statement.text)
112
130
  input_statement.search_text = _search_text
113
131
 
114
132
  if not input_statement.search_in_response_to and input_statement.in_response_to:
115
- input_statement.search_in_response_to = self.storage.tagger.get_text_index_string(input_statement.in_response_to)
133
+ input_statement.search_in_response_to = self.tagger.get_text_index_string(
134
+ input_statement.in_response_to
135
+ )
116
136
 
117
- response = self.generate_response(input_statement, additional_response_selection_parameters)
137
+ response = self.generate_response(
138
+ input_statement,
139
+ additional_response_selection_parameters
140
+ )
118
141
 
119
142
  # Update any response data that needs to be changed
120
143
  if persist_values_to_response:
@@ -128,10 +151,13 @@ class ChatBot(object):
128
151
  setattr(response, response_key, response_value)
129
152
 
130
153
  if not self.read_only:
131
- self.learn_response(input_statement)
154
+
155
+ # Save the input statement
156
+ self.storage.create(**input_statement.serialize())
132
157
 
133
158
  # Save the response generated for the input
134
- self.storage.create(**response.serialize())
159
+ self.learn_response(response, previous_statement=input_statement)
160
+
135
161
 
136
162
  return response
137
163
 
@@ -194,6 +220,8 @@ class ChatBot(object):
194
220
  if result_option.count > most_common.count:
195
221
  most_common = result_option
196
222
 
223
+ self.logger.info('Selecting "{}" as the most common response'.format(most_common.statement.text))
224
+
197
225
  if most_common.count > 1:
198
226
  result = most_common.statement
199
227
 
@@ -204,6 +232,8 @@ class ChatBot(object):
204
232
  persona='bot:' + self.name
205
233
  )
206
234
 
235
+ response.add_tags(*result.get_tags())
236
+
207
237
  response.confidence = result.confidence
208
238
 
209
239
  return response
@@ -228,11 +258,14 @@ class ChatBot(object):
228
258
  statement.in_response_to = previous_statement
229
259
 
230
260
  self.logger.info('Adding "{}" as a response to "{}"'.format(
231
- statement.text,
232
- previous_statement_text
261
+ previous_statement_text,
262
+ statement.text
233
263
  ))
234
264
 
235
- # Save the input statement
265
+ if not statement.persona:
266
+ statement.persona = 'bot:' + self.name
267
+
268
+ # Save the response statement
236
269
  return self.storage.create(**statement.serialize())
237
270
 
238
271
  def get_latest_response(self, conversation):
@@ -19,15 +19,22 @@ 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(self, statement_a, statement_b):
22
+ def compare_text(self, text_a, text_b):
23
23
  """
24
- Implemented in subclasses: compare statement_a to statement_b.
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
27
  :rtype: float
28
28
  """
29
29
  return 0
30
30
 
31
+ def compare(self, statement_a, statement_b):
32
+ """
33
+ :return: The percent of similarity between the statements based on the implemented algorithm.
34
+ :rtype: float
35
+ """
36
+ return self.compare_text(statement_a.text, statement_b.text)
37
+
31
38
 
32
39
  class LevenshteinDistance(Comparator):
33
40
  """
@@ -39,21 +46,21 @@ class LevenshteinDistance(Comparator):
39
46
  based on the Levenshtein distance algorithm.
40
47
  """
41
48
 
42
- def compare(self, statement_a, statement_b):
49
+ def compare_text(self, text_a, text_b):
43
50
  """
44
- Compare the two input statements.
51
+ Compare the two pieces of text.
45
52
 
46
53
  :return: The percent of similarity between the text of the statements.
47
54
  :rtype: float
48
55
  """
49
56
 
50
- # Return 0 if either statement has a falsy text value
51
- if not statement_a.text or not statement_b.text:
57
+ # Return 0 if either statement has a None text value
58
+ if text_a is None or text_b is None:
52
59
  return 0
53
60
 
54
61
  # Get the lowercase version of both strings
55
- statement_a_text = str(statement_a.text.lower())
56
- statement_b_text = str(statement_b.text.lower())
62
+ statement_a_text = str(text_a.lower())
63
+ statement_b_text = str(text_b.lower())
57
64
 
58
65
  similarity = SequenceMatcher(
59
66
  None,
@@ -103,15 +110,20 @@ class SpacySimilarity(Comparator):
103
110
  # Disable the Named Entity Recognition (NER) component because it is not necessary
104
111
  self.nlp = spacy.load(model, exclude=['ner'])
105
112
 
106
- def compare(self, statement_a, statement_b):
113
+ def compare_text(self, text_a, text_b):
107
114
  """
108
- Compare the two input statements.
115
+ Compare the similarity of two strings.
109
116
 
110
117
  :return: The percent of similarity between the closest synset distance.
111
118
  :rtype: float
112
119
  """
113
- document_a = self.nlp(statement_a.text)
114
- document_b = self.nlp(statement_b.text)
120
+
121
+ # Return 0 if either statement has a None text value
122
+ if text_a is None or text_b is None:
123
+ return 0
124
+
125
+ document_a = self.nlp(text_a)
126
+ document_b = self.nlp(text_b)
115
127
 
116
128
  return document_a.similarity(document_b)
117
129
 
@@ -155,14 +167,19 @@ class JaccardSimilarity(Comparator):
155
167
  # Disable the Named Entity Recognition (NER) component because it is not necessary
156
168
  self.nlp = spacy.load(model, exclude=['ner'])
157
169
 
158
- def compare(self, statement_a, statement_b):
170
+ def compare_text(self, text_a, text_b):
159
171
  """
160
172
  Return the calculated similarity of two
161
173
  statements based on the Jaccard index.
162
174
  """
175
+
176
+ # Return 0 if either statement has a None text value
177
+ if text_a is None or text_b is None:
178
+ return 0
179
+
163
180
  # Make both strings lowercase
164
- document_a = self.nlp(statement_a.text.lower())
165
- document_b = self.nlp(statement_b.text.lower())
181
+ document_a = self.nlp(text_a.lower())
182
+ document_b = self.nlp(text_b.lower())
166
183
 
167
184
  statement_a_lemmas = frozenset([
168
185
  token.lemma_ for token in document_a if not token.is_stop
@@ -23,10 +23,13 @@ class BestMatch(LogicAdapter):
23
23
  self.excluded_words = kwargs.get('excluded_words')
24
24
 
25
25
  def process(self, input_statement, additional_response_selection_parameters=None):
26
+
27
+ # Get all statements that have a response text similar to the input statement
26
28
  search_results = self.search_algorithm.search(input_statement)
27
29
 
28
30
  # Use the input statement as the closest match if no other results are found
29
- closest_match = next(search_results, input_statement)
31
+ input_statement.confidence = 0 # Use 0 confidence when no other results are found
32
+ closest_match = input_statement
30
33
 
31
34
  # Search for the closest match to the input statement
32
35
  for result in search_results:
@@ -36,8 +39,8 @@ class BestMatch(LogicAdapter):
36
39
  if result.confidence >= self.maximum_similarity_threshold:
37
40
  break
38
41
 
39
- self.chatbot.logger.info('Using "{}" as a close match to "{}" with a confidence of {}'.format(
40
- closest_match.text, input_statement.text, closest_match.confidence
42
+ self.chatbot.logger.info('Selecting "{}" as a response to "{}" with a confidence of {}'.format(
43
+ closest_match.in_response_to, input_statement.text, closest_match.confidence
41
44
  ))
42
45
 
43
46
  recent_repeated_responses = filters.get_recent_repeated_responses(
@@ -51,39 +54,34 @@ class BestMatch(LogicAdapter):
51
54
  ))
52
55
 
53
56
  response_selection_parameters = {
54
- 'search_in_response_to': closest_match.search_text,
57
+ 'search_text': closest_match.search_text,
58
+ 'persona_not_startswith': 'bot:',
55
59
  'exclude_text': recent_repeated_responses,
56
60
  'exclude_text_words': self.excluded_words
57
61
  }
58
62
 
59
63
  alternate_response_selection_parameters = {
60
- 'search_in_response_to': self.chatbot.storage.tagger.get_text_index_string(
64
+ 'search_in_response_to': self.chatbot.tagger.get_text_index_string(
61
65
  input_statement.text
62
66
  ),
67
+ 'persona_not_startswith': 'bot:',
63
68
  'exclude_text': recent_repeated_responses,
64
69
  'exclude_text_words': self.excluded_words
65
70
  }
66
71
 
67
72
  if additional_response_selection_parameters:
68
- response_selection_parameters.update(additional_response_selection_parameters)
69
- alternate_response_selection_parameters.update(additional_response_selection_parameters)
70
-
71
- # Get all statements that are in response to the closest match
72
- response_list = list(self.chatbot.storage.filter(**response_selection_parameters))
73
+ response_selection_parameters.update(
74
+ additional_response_selection_parameters
75
+ )
76
+ alternate_response_selection_parameters.update(
77
+ additional_response_selection_parameters
78
+ )
73
79
 
74
- alternate_response_list = []
75
80
 
76
- if not response_list:
77
- self.chatbot.logger.info('No responses found. Generating alternate response list.')
78
- alternate_response_list = list(self.chatbot.storage.filter(**alternate_response_selection_parameters))
81
+ # Get all statements with text similar to the closest match
82
+ response_list = list(self.chatbot.storage.filter(**response_selection_parameters))
79
83
 
80
84
  if response_list:
81
- self.chatbot.logger.info(
82
- 'Selecting response from {} optimal responses.'.format(
83
- len(response_list)
84
- )
85
- )
86
-
87
85
  response = self.select_response(
88
86
  input_statement,
89
87
  response_list,
@@ -91,26 +89,35 @@ class BestMatch(LogicAdapter):
91
89
  )
92
90
 
93
91
  response.confidence = closest_match.confidence
94
- self.chatbot.logger.info('Response selected. Using "{}"'.format(response.text))
95
- elif alternate_response_list:
92
+ self.chatbot.logger.info('Selecting "{}" from {} optimal responses.'.format(
93
+ response.text,
94
+ len(response_list)
95
+ ))
96
+ else:
96
97
  '''
97
98
  The case where there was no responses returned for the selected match
98
99
  but a value exists for the statement the match is in response to.
99
100
  '''
100
- self.chatbot.logger.info(
101
- 'Selecting response from {} optimal alternate responses.'.format(
102
- len(alternate_response_list)
101
+ self.chatbot.logger.info('No responses found. Generating alternate response list.')
102
+
103
+ alternate_response_list = list(self.chatbot.storage.filter(
104
+ **alternate_response_selection_parameters
105
+ ))
106
+
107
+ if alternate_response_list:
108
+ response = self.select_response(
109
+ input_statement,
110
+ alternate_response_list,
111
+ self.chatbot.storage
103
112
  )
104
- )
105
- response = self.select_response(
106
- input_statement,
107
- alternate_response_list,
108
- self.chatbot.storage
109
- )
110
113
 
111
- response.confidence = closest_match.confidence
112
- self.chatbot.logger.info('Alternate response selected. Using "{}"'.format(response.text))
113
- else:
114
- response = self.get_default_response(input_statement)
114
+ response.confidence = closest_match.confidence
115
+ self.chatbot.logger.info('Selected alternative response "{}" from {} options'.format(
116
+ response.text,
117
+ len(alternate_response_list)
118
+ ))
119
+ else:
120
+ response = self.get_default_response(input_statement)
121
+ self.chatbot.logger.info('Using "%s" as a default response.', response.text)
115
122
 
116
123
  return response
@@ -0,0 +1,80 @@
1
+ from chatterbot.logic import LogicAdapter
2
+ from chatterbot.conversation import Statement
3
+ from chatterbot import constants, languages
4
+ import spacy
5
+
6
+
7
+ class SpecificResponseAdapter(LogicAdapter):
8
+ """
9
+ Return a specific response to a specific input.
10
+
11
+ :kwargs:
12
+ * *input_text* (``str``) --
13
+ The input text that triggers this logic adapter.
14
+ * *output_text* (``str`` or ``function``) --
15
+ The output text returned by this logic adapter.
16
+ If a function is provided, it should return a string.
17
+ """
18
+
19
+ def __init__(self, chatbot, **kwargs):
20
+ super().__init__(chatbot, **kwargs)
21
+
22
+ self.input_text = kwargs.get('input_text')
23
+
24
+ self.matcher = None
25
+
26
+ if MatcherClass := kwargs.get('matcher'):
27
+ language = kwargs.get('language', languages.ENG)
28
+
29
+ self.nlp = self._initialize_nlp(language)
30
+
31
+ self.matcher = MatcherClass(self.nlp.vocab)
32
+
33
+ self.matcher.add('SpecificResponse', [self.input_text])
34
+
35
+ self._output_text = kwargs.get('output_text')
36
+
37
+ def _initialize_nlp(self, language):
38
+ try:
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
44
+
45
+ return spacy.load(model)
46
+
47
+ def can_process(self, statement):
48
+ if self.matcher:
49
+ doc = self.nlp(statement.text)
50
+ matches = self.matcher(doc)
51
+
52
+ if matches:
53
+ return True
54
+ elif statement.text == self.input_text:
55
+ return True
56
+
57
+ return False
58
+
59
+ def process(self, statement, additional_response_selection_parameters=None):
60
+
61
+ if callable(self._output_text):
62
+ response_statement = Statement(text=self._output_text())
63
+ else:
64
+ response_statement = Statement(text=self._output_text)
65
+
66
+ if self.matcher:
67
+ doc = self.nlp(statement.text)
68
+ matches = self.matcher(doc)
69
+
70
+ if matches:
71
+ response_statement.confidence = 1
72
+ else:
73
+ response_statement.confidence = 0
74
+
75
+ elif statement.text == self.input_text:
76
+ response_statement.confidence = 1
77
+ else:
78
+ response_statement.confidence = 0
79
+
80
+ return response_statement
@@ -158,7 +158,8 @@ class UnitConversion(LogicAdapter):
158
158
  response = func(p)
159
159
  if response.confidence == 1.0:
160
160
  break
161
- except Exception:
161
+ except Exception as e:
162
+ self.chatbot.logger.warning('Error during UnitConversion: {}'.format(str(e)))
162
163
  response.confidence = 0.0
163
- finally:
164
- return response
164
+
165
+ return response
@@ -37,7 +37,7 @@ def get_most_frequent_response(input_statement, response_list, storage=None):
37
37
  matching_response = statement
38
38
  occurrence_count = count
39
39
 
40
- # Choose the most commonly occuring matching response
40
+ # Choose the most commonly occurring matching response
41
41
  return matching_response
42
42
 
43
43