ChatterBot 1.2.8__tar.gz → 1.2.10__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 (94) hide show
  1. {chatterbot-1.2.8 → chatterbot-1.2.10}/ChatterBot.egg-info/PKG-INFO +17 -13
  2. {chatterbot-1.2.8 → chatterbot-1.2.10}/ChatterBot.egg-info/requires.txt +3 -3
  3. {chatterbot-1.2.8 → chatterbot-1.2.10}/PKG-INFO +17 -13
  4. {chatterbot-1.2.8 → chatterbot-1.2.10}/README.md +14 -10
  5. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/__init__.py +1 -1
  6. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/chatterbot.py +62 -36
  7. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/components.py +3 -0
  8. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/logic/specific_response.py +13 -3
  9. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/parsing.py +25 -15
  10. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/search.py +70 -0
  11. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/storage/redis.py +120 -40
  12. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/storage/sql_storage.py +24 -2
  13. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/storage/storage_adapter.py +81 -0
  14. chatterbot-1.2.10/chatterbot/tagging.py +182 -0
  15. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/trainers.py +116 -49
  16. {chatterbot-1.2.8 → chatterbot-1.2.10}/pyproject.toml +3 -3
  17. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_chatbot.py +5 -4
  18. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_parsing.py +197 -0
  19. chatterbot-1.2.8/chatterbot/tagging.py +0 -77
  20. {chatterbot-1.2.8 → chatterbot-1.2.10}/ChatterBot.egg-info/SOURCES.txt +0 -0
  21. {chatterbot-1.2.8 → chatterbot-1.2.10}/ChatterBot.egg-info/dependency_links.txt +0 -0
  22. {chatterbot-1.2.8 → chatterbot-1.2.10}/ChatterBot.egg-info/top_level.txt +0 -0
  23. {chatterbot-1.2.8 → chatterbot-1.2.10}/LICENSE +0 -0
  24. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/__main__.py +0 -0
  25. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/adapters.py +0 -0
  26. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/comparisons.py +0 -0
  27. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/constants.py +0 -0
  28. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/conversation.py +0 -0
  29. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/corpus.py +0 -0
  30. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/exceptions.py +0 -0
  31. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/__init__.py +0 -0
  32. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/__init__.py +0 -0
  33. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/abstract_models.py +0 -0
  34. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/admin.py +0 -0
  35. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/apps.py +0 -0
  36. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0001_initial.py +0 -0
  37. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0002_statement_extra_data.py +0 -0
  38. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0003_change_occurrence_default.py +0 -0
  39. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0004_rename_in_response_to.py +0 -0
  40. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0005_statement_created_at.py +0 -0
  41. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0006_create_conversation.py +0 -0
  42. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0007_response_created_at.py +0 -0
  43. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0008_update_conversations.py +0 -0
  44. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0009_tags.py +0 -0
  45. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0010_statement_text.py +0 -0
  46. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0011_blank_extra_data.py +0 -0
  47. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0012_statement_created_at.py +0 -0
  48. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0013_change_conversations.py +0 -0
  49. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0014_remove_statement_extra_data.py +0 -0
  50. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0015_statement_persona.py +0 -0
  51. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0016_statement_stemmed_text.py +0 -0
  52. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0017_tags_unique.py +0 -0
  53. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0018_text_max_length.py +0 -0
  54. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0019_alter_statement_id_alter_tag_id_and_more.py +0 -0
  55. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/0020_alter_statement_conversation_and_more.py +0 -0
  56. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/migrations/__init__.py +0 -0
  57. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/model_admin.py +0 -0
  58. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/models.py +0 -0
  59. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/django_chatterbot/settings.py +0 -0
  60. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/sqlalchemy_app/__init__.py +0 -0
  61. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/ext/sqlalchemy_app/models.py +0 -0
  62. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/filters.py +0 -0
  63. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/languages.py +0 -0
  64. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/llm.py +0 -0
  65. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/logic/__init__.py +0 -0
  66. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/logic/best_match.py +0 -0
  67. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/logic/logic_adapter.py +0 -0
  68. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/logic/mathematical_evaluation.py +0 -0
  69. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/logic/time_adapter.py +0 -0
  70. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/logic/unit_conversion.py +0 -0
  71. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/preprocessors.py +0 -0
  72. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/response_selection.py +0 -0
  73. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/storage/__init__.py +0 -0
  74. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/storage/django_storage.py +0 -0
  75. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/storage/mongodb.py +0 -0
  76. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/utils.py +0 -0
  77. {chatterbot-1.2.8 → chatterbot-1.2.10}/chatterbot/vectorstores.py +0 -0
  78. {chatterbot-1.2.8 → chatterbot-1.2.10}/setup.cfg +0 -0
  79. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_adapter_validation.py +0 -0
  80. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_benchmarks.py +0 -0
  81. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_cli.py +0 -0
  82. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_comparisons.py +0 -0
  83. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_conversations.py +0 -0
  84. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_corpus.py +0 -0
  85. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_examples.py +0 -0
  86. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_filters.py +0 -0
  87. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_initialization.py +0 -0
  88. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_languages.py +0 -0
  89. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_preprocessors.py +0 -0
  90. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_response_selection.py +0 -0
  91. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_search.py +0 -0
  92. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_tagging.py +0 -0
  93. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_turing.py +0 -0
  94. {chatterbot-1.2.8 → chatterbot-1.2.10}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ChatterBot
3
- Version: 1.2.8
3
+ Version: 1.2.10
4
4
  Summary: ChatterBot is a machine learning, conversational dialog engine
5
5
  Author: Gunther Cox
6
6
  License-Expression: BSD-3-Clause
@@ -47,16 +47,16 @@ Provides-Extra: dev
47
47
  Requires-Dist: pint>=0.8.1; extra == "dev"
48
48
  Requires-Dist: pyyaml<7.0,>=6.0; extra == "dev"
49
49
  Requires-Dist: chatterbot-corpus<1.3.0,>=1.2.2; extra == "dev"
50
- Requires-Dist: ollama<1.0,>=0.4.7; extra == "dev"
50
+ Requires-Dist: ollama<1.0,>=0.6.0; extra == "dev"
51
51
  Requires-Dist: openai; extra == "dev"
52
52
  Provides-Extra: redis
53
- Requires-Dist: redis[hiredis]<5.3; extra == "redis"
53
+ Requires-Dist: redis[hiredis]<7.0; extra == "redis"
54
54
  Requires-Dist: langchain-redis<=0.2.5; extra == "redis"
55
55
  Requires-Dist: langchain-huggingface<=0.1.2; extra == "redis"
56
56
  Requires-Dist: accelerate<=1.6.0; extra == "redis"
57
57
  Requires-Dist: sentence-transformers<=4.0.2; extra == "redis"
58
58
  Provides-Extra: mongodb
59
- Requires-Dist: pymongo<4.12,>=4.11; extra == "mongodb"
59
+ Requires-Dist: pymongo<4.16,>=4.11; extra == "mongodb"
60
60
  Dynamic: license-file
61
61
 
62
62
  ![ChatterBot: Machine learning in Python](https://i.imgur.com/b3SCmGT.png)
@@ -153,16 +153,20 @@ section of the documentation.
153
153
 
154
154
  See release notes for changes https://github.com/gunthercox/ChatterBot/releases
155
155
 
156
- # Development pattern for contributors
156
+ # Contributing
157
157
 
158
- 1. [Create a fork](https://help.github.com/articles/fork-a-repo/) of
159
- the [main ChatterBot repository](https://github.com/gunthercox/ChatterBot) on GitHub.
160
- 2. Make your changes in a branch named something different from `master`, e.g. create
161
- a new branch `my-pull-request`.
162
- 3. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/).
163
- 4. Please follow the [Python style guide for PEP-8](https://www.python.org/dev/peps/pep-0008/).
164
- 5. Use the projects [built-in automated testing](https://docs.chatterbot.us/testing/).
165
- to help make sure that your contribution is free from errors.
158
+ Contributions are welcomed, to help ensure a smooth process please start with the contributing guidelines in our documentation:
159
+ https://docs.chatterbot.us/contributing/
160
+
161
+ # Sponsors
162
+
163
+ ChatterBot is sponsored by:
164
+
165
+ <p style="font-size:21px; color:black;">Browser testing via
166
+ <a href="https://www.lambdatest.com/?utm_source=chatterbot&utm_medium=sponsor" target="_blank">
167
+ <img src="https://www.lambdatest.com/blue-logo.png" style="vertical-align: middle;" width="250" height="45" />
168
+ </a>
169
+ </p>
166
170
 
167
171
  # License
168
172
 
@@ -8,14 +8,14 @@ tqdm
8
8
  pint>=0.8.1
9
9
  pyyaml<7.0,>=6.0
10
10
  chatterbot-corpus<1.3.0,>=1.2.2
11
- ollama<1.0,>=0.4.7
11
+ ollama<1.0,>=0.6.0
12
12
  openai
13
13
 
14
14
  [mongodb]
15
- pymongo<4.12,>=4.11
15
+ pymongo<4.16,>=4.11
16
16
 
17
17
  [redis]
18
- redis[hiredis]<5.3
18
+ redis[hiredis]<7.0
19
19
  langchain-redis<=0.2.5
20
20
  langchain-huggingface<=0.1.2
21
21
  accelerate<=1.6.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ChatterBot
3
- Version: 1.2.8
3
+ Version: 1.2.10
4
4
  Summary: ChatterBot is a machine learning, conversational dialog engine
5
5
  Author: Gunther Cox
6
6
  License-Expression: BSD-3-Clause
@@ -47,16 +47,16 @@ Provides-Extra: dev
47
47
  Requires-Dist: pint>=0.8.1; extra == "dev"
48
48
  Requires-Dist: pyyaml<7.0,>=6.0; extra == "dev"
49
49
  Requires-Dist: chatterbot-corpus<1.3.0,>=1.2.2; extra == "dev"
50
- Requires-Dist: ollama<1.0,>=0.4.7; extra == "dev"
50
+ Requires-Dist: ollama<1.0,>=0.6.0; extra == "dev"
51
51
  Requires-Dist: openai; extra == "dev"
52
52
  Provides-Extra: redis
53
- Requires-Dist: redis[hiredis]<5.3; extra == "redis"
53
+ Requires-Dist: redis[hiredis]<7.0; extra == "redis"
54
54
  Requires-Dist: langchain-redis<=0.2.5; extra == "redis"
55
55
  Requires-Dist: langchain-huggingface<=0.1.2; extra == "redis"
56
56
  Requires-Dist: accelerate<=1.6.0; extra == "redis"
57
57
  Requires-Dist: sentence-transformers<=4.0.2; extra == "redis"
58
58
  Provides-Extra: mongodb
59
- Requires-Dist: pymongo<4.12,>=4.11; extra == "mongodb"
59
+ Requires-Dist: pymongo<4.16,>=4.11; extra == "mongodb"
60
60
  Dynamic: license-file
61
61
 
62
62
  ![ChatterBot: Machine learning in Python](https://i.imgur.com/b3SCmGT.png)
@@ -153,16 +153,20 @@ section of the documentation.
153
153
 
154
154
  See release notes for changes https://github.com/gunthercox/ChatterBot/releases
155
155
 
156
- # Development pattern for contributors
156
+ # Contributing
157
157
 
158
- 1. [Create a fork](https://help.github.com/articles/fork-a-repo/) of
159
- the [main ChatterBot repository](https://github.com/gunthercox/ChatterBot) on GitHub.
160
- 2. Make your changes in a branch named something different from `master`, e.g. create
161
- a new branch `my-pull-request`.
162
- 3. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/).
163
- 4. Please follow the [Python style guide for PEP-8](https://www.python.org/dev/peps/pep-0008/).
164
- 5. Use the projects [built-in automated testing](https://docs.chatterbot.us/testing/).
165
- to help make sure that your contribution is free from errors.
158
+ Contributions are welcomed, to help ensure a smooth process please start with the contributing guidelines in our documentation:
159
+ https://docs.chatterbot.us/contributing/
160
+
161
+ # Sponsors
162
+
163
+ ChatterBot is sponsored by:
164
+
165
+ <p style="font-size:21px; color:black;">Browser testing via
166
+ <a href="https://www.lambdatest.com/?utm_source=chatterbot&utm_medium=sponsor" target="_blank">
167
+ <img src="https://www.lambdatest.com/blue-logo.png" style="vertical-align: middle;" width="250" height="45" />
168
+ </a>
169
+ </p>
166
170
 
167
171
  # License
168
172
 
@@ -92,16 +92,20 @@ section of the documentation.
92
92
 
93
93
  See release notes for changes https://github.com/gunthercox/ChatterBot/releases
94
94
 
95
- # Development pattern for contributors
96
-
97
- 1. [Create a fork](https://help.github.com/articles/fork-a-repo/) of
98
- the [main ChatterBot repository](https://github.com/gunthercox/ChatterBot) on GitHub.
99
- 2. Make your changes in a branch named something different from `master`, e.g. create
100
- a new branch `my-pull-request`.
101
- 3. [Create a pull request](https://help.github.com/articles/creating-a-pull-request/).
102
- 4. Please follow the [Python style guide for PEP-8](https://www.python.org/dev/peps/pep-0008/).
103
- 5. Use the projects [built-in automated testing](https://docs.chatterbot.us/testing/).
104
- to help make sure that your contribution is free from errors.
95
+ # Contributing
96
+
97
+ Contributions are welcomed, to help ensure a smooth process please start with the contributing guidelines in our documentation:
98
+ https://docs.chatterbot.us/contributing/
99
+
100
+ # Sponsors
101
+
102
+ ChatterBot is sponsored by:
103
+
104
+ <p style="font-size:21px; color:black;">Browser testing via
105
+ <a href="https://www.lambdatest.com/?utm_source=chatterbot&utm_medium=sponsor" target="_blank">
106
+ <img src="https://www.lambdatest.com/blue-logo.png" style="vertical-align: middle;" width="250" height="45" />
107
+ </a>
108
+ </p>
105
109
 
106
110
  # License
107
111
 
@@ -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.8'
7
+ __version__ = '1.2.10'
8
8
 
9
9
  __all__ = (
10
10
  'ChatBot',
@@ -2,7 +2,7 @@ import logging
2
2
  from typing import Union
3
3
  from chatterbot.storage import StorageAdapter
4
4
  from chatterbot.logic import LogicAdapter
5
- from chatterbot.search import TextSearch, IndexedTextSearch
5
+ from chatterbot.search import TextSearch, IndexedTextSearch, SemanticVectorSearch
6
6
  from chatterbot.tagging import PosLemmaTagger
7
7
  from chatterbot.conversation import Statement
8
8
  from chatterbot import languages
@@ -74,41 +74,60 @@ class ChatBot(object):
74
74
 
75
75
  tagger_language = kwargs.get('tagger_language', languages.ENG)
76
76
 
77
- try:
78
- Tagger = kwargs.get('tagger', PosLemmaTagger)
79
-
80
- # Allow instances to be provided for performance optimization
81
- # (Example: a pre-loaded model in a tagger when unit testing)
82
- if not isinstance(Tagger, type):
83
- self.tagger = Tagger
84
- else:
85
- self.tagger = Tagger(language=tagger_language)
86
- except IOError as io_error:
87
- # Return a more helpful error message if possible
88
- if "Can't find model" in str(io_error):
89
- model_name = utils.get_model_for_language(tagger_language)
90
- if hasattr(tagger_language, 'ENGLISH_NAME'):
91
- language_name = tagger_language.ENGLISH_NAME
77
+ # Check if storage adapter has a preferred tagger
78
+ PreferredTagger = self.storage.get_preferred_tagger()
79
+
80
+ if PreferredTagger is not None:
81
+ # Storage adapter specifies its own tagger
82
+ self.tagger = PreferredTagger(language=tagger_language)
83
+ else:
84
+ # Use default or user-specified tagger
85
+ try:
86
+ Tagger = kwargs.get('tagger', PosLemmaTagger)
87
+
88
+ # Allow instances to be provided for performance optimization
89
+ # (Example: a pre-loaded model in a tagger when unit testing)
90
+ if not isinstance(Tagger, type):
91
+ self.tagger = Tagger
92
92
  else:
93
- language_name = tagger_language
94
- raise self.ChatBotException(
95
- 'Setup error:\n'
96
- f'The Spacy model for "{language_name}" language is missing.\n'
97
- 'Please install the model using the command:\n\n'
98
- f'python -m spacy download {model_name}\n\n'
99
- 'See https://spacy.io/usage/models for more information about available models.'
100
- ) from io_error
101
- else:
102
- raise io_error
93
+ self.tagger = Tagger(language=tagger_language)
94
+ except IOError as io_error:
95
+ # Return a more helpful error message if possible
96
+ if "Can't find model" in str(io_error):
97
+ model_name = utils.get_model_for_language(tagger_language)
98
+ if hasattr(tagger_language, 'ENGLISH_NAME'):
99
+ language_name = tagger_language.ENGLISH_NAME
100
+ else:
101
+ language_name = tagger_language
102
+ raise self.ChatBotException(
103
+ 'Setup error:\n'
104
+ f'The Spacy model for "{language_name}" language is missing.\n'
105
+ 'Please install the model using the command:\n\n'
106
+ f'python -m spacy download {model_name}\n\n'
107
+ 'See https://spacy.io/usage/models for more information about available models.'
108
+ ) from io_error
109
+ else:
110
+ raise io_error
103
111
 
112
+ # Initialize search algorithms
104
113
  primary_search_algorithm = IndexedTextSearch(self, **kwargs)
105
114
  text_search_algorithm = TextSearch(self, **kwargs)
115
+ semantic_vector_search_algorithm = SemanticVectorSearch(self, **kwargs)
106
116
 
107
117
  self.search_algorithms = {
108
118
  primary_search_algorithm.name: primary_search_algorithm,
109
- text_search_algorithm.name: text_search_algorithm
119
+ text_search_algorithm.name: text_search_algorithm,
120
+ semantic_vector_search_algorithm.name: semantic_vector_search_algorithm
110
121
  }
111
122
 
123
+ # Check if storage adapter has a preferred search algorithm
124
+ preferred_search_algorithm = self.storage.get_preferred_search_algorithm()
125
+ if preferred_search_algorithm and preferred_search_algorithm in self.search_algorithms:
126
+ # Set as default for logic adapters that don't specify their own search algorithm
127
+ # This ensures BestMatch and other adapters use the optimal search method
128
+ self.logger.info(f'Storage adapter prefers search algorithm: {preferred_search_algorithm}')
129
+ kwargs.setdefault('search_algorithm_name', preferred_search_algorithm)
130
+
112
131
  for adapter in logic_adapters:
113
132
  utils.validate_adapter_class(adapter, LogicAdapter)
114
133
  logic_adapter = utils.initialize_class(adapter, self, **kwargs)
@@ -191,15 +210,22 @@ class ChatBot(object):
191
210
  input_statement.in_response_to = previous_statement.text
192
211
 
193
212
  # Make sure the input statement has its search text saved
194
-
195
- if not input_statement.search_text:
196
- _search_text = self.tagger.get_text_index_string(input_statement.text)
197
- input_statement.search_text = _search_text
198
-
199
- if not input_statement.search_in_response_to and input_statement.in_response_to:
200
- input_statement.search_in_response_to = self.tagger.get_text_index_string(
201
- input_statement.in_response_to
202
- )
213
+ if not self.tagger.needs_text_indexing():
214
+ # Tagger doesn't transform text, use it directly
215
+ if not input_statement.search_text:
216
+ input_statement.search_text = input_statement.text
217
+ if not input_statement.search_in_response_to and input_statement.in_response_to:
218
+ input_statement.search_in_response_to = input_statement.in_response_to
219
+ else:
220
+ # Use tagger for text indexing or transformations
221
+ if not input_statement.search_text:
222
+ _search_text = self.tagger.get_text_index_string(input_statement.text)
223
+ input_statement.search_text = _search_text
224
+
225
+ if not input_statement.search_in_response_to and input_statement.in_response_to:
226
+ input_statement.search_in_response_to = self.tagger.get_text_index_string(
227
+ input_statement.in_response_to
228
+ )
203
229
 
204
230
  response = self.generate_response(
205
231
  input_statement,
@@ -29,6 +29,9 @@ def chatterbot_bigram_indexer(document):
29
29
  token for token in document if not (token.is_punct)
30
30
  ]
31
31
 
32
+ # Pairs consist of the part-of-speech of the first token and the
33
+ # lemma of the second token in the bigram. This provides a good
34
+ # balance of generalization and specificity for matching.
32
35
  bigram_pairs = [
33
36
  f"{tokens[i - 1].pos_}:{tokens[i].lemma_.lower()}"
34
37
  for i in range(1, len(tokens))
@@ -20,7 +20,19 @@ class SpecificResponseAdapter(LogicAdapter):
20
20
  def __init__(self, chatbot, **kwargs):
21
21
  super().__init__(chatbot, **kwargs)
22
22
 
23
- self.input_text = kwargs.get('input_text')
23
+ try:
24
+ self.input_text = kwargs['input_text']
25
+ except KeyError:
26
+ raise chatbot.ChatBotException(
27
+ 'The SpecificResponseAdapter requires an input_text parameter.'
28
+ )
29
+
30
+ try:
31
+ self._output_text = kwargs['output_text']
32
+ except KeyError:
33
+ raise chatbot.ChatBotException(
34
+ 'The SpecificResponseAdapter requires an output_text parameter.'
35
+ )
24
36
 
25
37
  self.matcher = None
26
38
 
@@ -33,8 +45,6 @@ class SpecificResponseAdapter(LogicAdapter):
33
45
 
34
46
  self.matcher.add('SpecificResponse', [self.input_text])
35
47
 
36
- self._output_text = kwargs.get('output_text')
37
-
38
48
  def _initialize_nlp(self, language):
39
49
  model = get_model_for_language(language)
40
50
 
@@ -503,7 +503,7 @@ regex = [
503
503
  ]
504
504
 
505
505
 
506
- def convert_string_to_number(value):
506
+ def convert_string_to_number(value: str) -> int:
507
507
  """
508
508
  Convert strings to numbers
509
509
  """
@@ -517,7 +517,7 @@ def convert_string_to_number(value):
517
517
  return sum(num_list)
518
518
 
519
519
 
520
- def convert_time_to_hour_minute(hour, minute, convention):
520
+ def convert_time_to_hour_minute(hour: str, minute: str, convention: str) -> dict:
521
521
  """
522
522
  Convert time to hour, minute
523
523
  """
@@ -532,12 +532,19 @@ def convert_time_to_hour_minute(hour, minute, convention):
532
532
  minute = int(minute)
533
533
 
534
534
  if convention.lower() == 'pm':
535
- hour += 12
535
+ # Handle 12 PM (noon) - it stays as 12
536
+ # Handle 1-11 PM - add 12
537
+ if hour != 12:
538
+ hour += 12
539
+ else:
540
+ # Handle 12 AM (midnight) - convert to 0
541
+ if hour == 12:
542
+ hour = 0
536
543
 
537
544
  return {'hours': hour, 'minutes': minute}
538
545
 
539
546
 
540
- def date_from_quarter(base_date, ordinal, year):
547
+ def date_from_quarter(base_date: datetime, ordinal: int, year: int) -> list[datetime]:
541
548
  """
542
549
  Extract date from quarter of a year
543
550
  """
@@ -554,7 +561,7 @@ def date_from_quarter(base_date, ordinal, year):
554
561
  ]
555
562
 
556
563
 
557
- def date_from_relative_day(base_date, time, dow):
564
+ def date_from_relative_day(base_date: datetime, time: str, dow: str) -> datetime:
558
565
  """
559
566
  Converts relative day to time
560
567
  Ex: this tuesday, last tuesday
@@ -577,7 +584,7 @@ def date_from_relative_day(base_date, time, dow):
577
584
  return next_week_day(base_date, num)
578
585
 
579
586
 
580
- def date_from_relative_week_year(base_date, time, dow, ordinal=1):
587
+ def date_from_relative_week_year(base_date: datetime, time: str, dow: str, ordinal: int = 1) -> datetime:
581
588
  """
582
589
  Converts relative day to time
583
590
  Eg. this tuesday, last tuesday
@@ -608,7 +615,10 @@ def date_from_relative_week_year(base_date, time, dow, ordinal=1):
608
615
  day = min(relative_date.day, calendar.monthrange(year, month)[1])
609
616
  return datetime(year, month, day)
610
617
  else:
611
- return datetime(relative_date.year, relative_date.month + ord, relative_date.day)
618
+ # Base the day to valid range on the target month
619
+ target_month = relative_date.month + ord
620
+ day = min(relative_date.day, calendar.monthrange(relative_date.year, target_month)[1])
621
+ return datetime(relative_date.year, target_month, day)
612
622
  elif time == 'end of the':
613
623
  return datetime(
614
624
  relative_date.year,
@@ -636,7 +646,7 @@ def date_from_relative_week_year(base_date, time, dow, ordinal=1):
636
646
  return datetime(relative_date.year, relative_date.month, relative_date.day, 23, 59, 59)
637
647
 
638
648
 
639
- def date_from_adverb(base_date, name):
649
+ def date_from_adverb(base_date: datetime, name: str) -> datetime:
640
650
  """
641
651
  Convert Day adverbs to dates
642
652
  Tomorrow => Date
@@ -645,14 +655,14 @@ def date_from_adverb(base_date, name):
645
655
  # Reset date to start of the day
646
656
  adverb_date = datetime(base_date.year, base_date.month, base_date.day)
647
657
  if name == 'today' or name == 'tonite' or name == 'tonight':
648
- return adverb_date.today().replace(hour=0, minute=0, second=0, microsecond=0)
658
+ return adverb_date
649
659
  elif name == 'yesterday':
650
660
  return adverb_date - timedelta(days=1)
651
661
  elif name == 'tomorrow' or name == 'tom':
652
662
  return adverb_date + timedelta(days=1)
653
663
 
654
664
 
655
- def date_from_duration(base_date, number_as_string, unit, duration, base_time=None):
665
+ def date_from_duration(base_date: datetime, number_as_string: str, unit: str, duration: str, base_time: str = None) -> datetime:
656
666
  """
657
667
  Find dates from duration
658
668
  Eg: 20 days from now
@@ -682,7 +692,7 @@ def date_from_duration(base_date, number_as_string, unit, duration, base_time=No
682
692
  return base_date + timedelta(**args)
683
693
 
684
694
 
685
- def this_week_day(base_date, weekday):
695
+ def this_week_day(base_date: datetime, weekday: int) -> datetime:
686
696
  """
687
697
  Finds coming weekday
688
698
  """
@@ -698,7 +708,7 @@ def this_week_day(base_date, weekday):
698
708
  return day
699
709
 
700
710
 
701
- def previous_week_day(base_date, weekday):
711
+ def previous_week_day(base_date: datetime, weekday: int) -> datetime:
702
712
  """
703
713
  Finds previous weekday
704
714
  """
@@ -708,9 +718,9 @@ def previous_week_day(base_date, weekday):
708
718
  return day
709
719
 
710
720
 
711
- def next_week_day(base_date, weekday):
721
+ def next_week_day(base_date: datetime, weekday: int) -> datetime:
712
722
  """
713
- Finds next weekday
723
+ Finds the next weekday.
714
724
  """
715
725
  day_of_week = base_date.weekday()
716
726
  end_of_this_week = base_date + timedelta(days=6 - day_of_week)
@@ -720,7 +730,7 @@ def next_week_day(base_date, weekday):
720
730
  return day
721
731
 
722
732
 
723
- def datetime_parsing(text, base_date=datetime.now()):
733
+ def datetime_parsing(text: str, base_date: datetime = datetime.now()) -> list[tuple[str, datetime, tuple[int, int]]]:
724
734
  """
725
735
  Extract datetime objects from a string of text.
726
736
  """
@@ -157,3 +157,73 @@ class TextSearch:
157
157
  if confidence >= 1.0:
158
158
  self.chatbot.logger.info('Exact match found, stopping search')
159
159
  break
160
+
161
+
162
+ class SemanticVectorSearch:
163
+ """
164
+ Semantic vector search for storage adapters that use vector embeddings.
165
+ Does not require a tagger or comparison function - relies on the storage
166
+ adapter's native vector similarity search capabilities.
167
+
168
+ :param search_page_size:
169
+ The maximum number of records to load into memory at a time when searching.
170
+ Defaults to 1000
171
+ """
172
+
173
+ name = 'semantic_vector_search'
174
+
175
+ def __init__(self, chatbot, **kwargs):
176
+ self.chatbot = chatbot
177
+
178
+ self.search_page_size = kwargs.get(
179
+ 'search_page_size', 1000
180
+ )
181
+
182
+ def search(self, input_statement, **additional_parameters):
183
+ """
184
+ Search for semantically similar statements using vector similarity.
185
+ Confidence scores are calculated by the storage adapter based on
186
+ vector distances and returned in the results.
187
+
188
+ :param input_statement: A statement.
189
+ :type input_statement: chatterbot.conversation.Statement
190
+
191
+ :param **additional_parameters: Additional parameters to be passed
192
+ to the ``filter`` method of the storage adapter when searching.
193
+
194
+ :rtype: Generator yielding one closest matching statement at a time.
195
+ """
196
+ self.chatbot.logger.info('Beginning semantic vector search')
197
+
198
+ search_parameters = {
199
+ 'search_in_response_to_contains': input_statement.text,
200
+ 'persona_not_startswith': 'bot:',
201
+ 'page_size': self.search_page_size
202
+ }
203
+
204
+ if additional_parameters:
205
+ search_parameters.update(additional_parameters)
206
+
207
+ statement_list = self.chatbot.storage.filter(**search_parameters)
208
+
209
+ best_confidence_so_far = 0
210
+
211
+ self.chatbot.logger.info('Processing search results')
212
+
213
+ # Yield statements with confidence scores from vector similarity
214
+ for statement in statement_list:
215
+ # Confidence should already be set by the storage adapter
216
+ confidence = getattr(statement, 'confidence', 0.0)
217
+
218
+ if confidence > best_confidence_so_far:
219
+ best_confidence_so_far = confidence
220
+
221
+ self.chatbot.logger.info('Similar statement found: {} {}'.format(
222
+ statement.in_response_to, confidence
223
+ ))
224
+
225
+ yield statement
226
+
227
+ if confidence >= 1.0:
228
+ self.chatbot.logger.info('Exact match found, stopping search')
229
+ break