├── .codeclimate.yml ├── .github └── workflows │ ├── publish-documentation.yml │ └── python-package.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── chatterbot ├── __init__.py ├── __main__.py ├── adapters.py ├── chatterbot.py ├── comparisons.py ├── components.py ├── constants.py ├── conversation.py ├── corpus.py ├── exceptions.py ├── ext │ ├── __init__.py │ ├── django_chatterbot │ │ ├── __init__.py │ │ ├── abstract_models.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ ├── 0001_initial.py │ │ │ ├── 0002_statement_extra_data.py │ │ │ ├── 0003_change_occurrence_default.py │ │ │ ├── 0004_rename_in_response_to.py │ │ │ ├── 0005_statement_created_at.py │ │ │ ├── 0006_create_conversation.py │ │ │ ├── 0007_response_created_at.py │ │ │ ├── 0008_update_conversations.py │ │ │ ├── 0009_tags.py │ │ │ ├── 0010_statement_text.py │ │ │ ├── 0011_blank_extra_data.py │ │ │ ├── 0012_statement_created_at.py │ │ │ ├── 0013_change_conversations.py │ │ │ ├── 0014_remove_statement_extra_data.py │ │ │ ├── 0015_statement_persona.py │ │ │ ├── 0016_statement_stemmed_text.py │ │ │ ├── 0017_tags_unique.py │ │ │ ├── 0018_text_max_length.py │ │ │ ├── 0019_alter_statement_id_alter_tag_id_and_more.py │ │ │ ├── 0020_alter_statement_conversation_and_more.py │ │ │ └── __init__.py │ │ ├── model_admin.py │ │ ├── models.py │ │ └── settings.py │ └── sqlalchemy_app │ │ ├── __init__.py │ │ └── models.py ├── filters.py ├── languages.py ├── llm.py ├── logic │ ├── __init__.py │ ├── best_match.py │ ├── logic_adapter.py │ ├── mathematical_evaluation.py │ ├── specific_response.py │ ├── time_adapter.py │ └── unit_conversion.py ├── parsing.py ├── preprocessors.py ├── response_selection.py ├── search.py ├── storage │ ├── __init__.py │ ├── django_storage.py │ ├── mongodb.py │ ├── redis.py │ ├── sql_storage.py │ └── storage_adapter.py ├── tagging.py ├── trainers.py ├── utils.py └── vectorstores.py ├── docs ├── _ext │ ├── canonical.py │ └── github.py ├── _includes │ └── python_module_structure.txt ├── _static │ ├── MongoDB_Fores-Green.svg │ ├── Redis_Logo_Red_RGB.svg │ ├── bigrams.svg │ ├── chatterbot-process-flow.svg │ ├── dialog-processing-flow-llm.svg │ ├── dialog-processing-flow.svg │ ├── favicon.ico │ ├── github-mark.png │ ├── so-icon.png │ ├── statement-relationship.svg │ ├── statement-response-relationship.svg │ ├── style.css │ ├── terminal-example.gif │ └── training-graph.svg ├── _templates │ ├── footer.html │ ├── layout.html │ ├── page.html │ └── sidebar_ad.html ├── chatterbot.rst ├── commands.rst ├── comparisons.rst ├── conf.py ├── contributing.rst ├── conversations.rst ├── corpus.rst ├── development.rst ├── django │ ├── index.rst │ ├── settings.rst │ ├── tutorial │ │ ├── django-filter-tutorial │ │ │ └── index.rst │ │ ├── django-rest-framework-tutorial │ │ │ └── index.rst │ │ ├── django-tutorial │ │ │ ├── django-installed.png │ │ │ ├── index.rst │ │ │ └── part-2.rst │ │ ├── index.rst │ │ └── writing-tests.rst │ ├── views.rst │ └── wsgi.rst ├── encoding.rst ├── examples.rst ├── faq.rst ├── filters.rst ├── glossary.rst ├── index.rst ├── large-language-models.rst ├── logic │ ├── create-a-logic-adapter.rst │ ├── index.rst │ └── response-selection.rst ├── packaging.rst ├── preprocessors.rst ├── quickstart.rst ├── releases.rst ├── robots.txt ├── setup.rst ├── statements.txt ├── storage │ ├── create-a-storage-adapter.rst │ ├── index.rst │ ├── mongodb.rst │ ├── redis.rst │ ├── sql.rst │ └── text-search.rst ├── testing.rst ├── training.rst ├── tutorial.rst ├── upgrading.rst └── utils.rst ├── examples ├── __init__.py ├── basic_example.py ├── convert_units.py ├── default_response_example.py ├── django_example │ ├── README.rst │ ├── django_example │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── management │ │ │ ├── __init__.py │ │ │ └── commands │ │ │ │ ├── __init__.py │ │ │ │ └── train.py │ │ ├── settings.py │ │ ├── static │ │ │ ├── css │ │ │ │ ├── bootstrap.css │ │ │ │ ├── bootstrap.css.map │ │ │ │ └── custom.css │ │ │ ├── favicon.ico │ │ │ ├── img │ │ │ │ └── chatterbot.png │ │ │ └── js │ │ │ │ ├── bootstrap.js │ │ │ │ ├── bootstrap.js.map │ │ │ │ ├── jquery.js │ │ │ │ └── js.cookie.js │ │ ├── templates │ │ │ ├── app.html │ │ │ └── nav.html │ │ ├── tests │ │ │ ├── __init__.py │ │ │ ├── test_api.py │ │ │ └── test_example.py │ │ ├── urls.py │ │ ├── views.py │ │ └── wsgi.py │ ├── manage.py │ └── requirements.txt ├── export_example.py ├── learning_feedback_example.py ├── math_and_time.py ├── memory_sql_example.py ├── ollama_example.py ├── openai_example.py ├── specific_response_example.py ├── tagged_dataset_example.py ├── terminal_example.py ├── terminal_mongo_example.py ├── tkinter_gui.py ├── training_example_chatterbot_corpus.py ├── training_example_list_data.py └── training_example_ubuntu_corpus.py ├── graphics ├── README.md ├── ad.png ├── ad.xcf ├── banner.png ├── chatterbot.xcf └── scan.jpg ├── pyproject.toml ├── runtests.py ├── setup.cfg ├── tests ├── __init__.py ├── base_case.py ├── logic │ ├── __init__.py │ ├── test_best_match.py │ ├── test_data_cache.py │ ├── test_logic_adapter.py │ ├── test_mathematical_evaluation.py │ ├── test_specific_response.py │ ├── test_time.py │ └── test_unit_conversion.py ├── storage │ ├── __init__.py │ ├── test_mongo_adapter.py │ ├── test_redis_adapter.py │ ├── test_sql_adapter.py │ └── test_storage_adapter.py ├── test_adapter_validation.py ├── test_benchmarks.py ├── test_chatbot.py ├── test_cli.py ├── test_comparisons.py ├── test_conversations.py ├── test_corpus.py ├── test_examples.py ├── test_filters.py ├── test_initialization.py ├── test_languages.py ├── test_parsing.py ├── test_preprocessors.py ├── test_response_selection.py ├── test_search.py ├── test_tagging.py ├── test_turing.py ├── test_utils.py └── training │ ├── __init__.py │ ├── test_chatterbot_corpus_training.py │ ├── test_csv_file_training.py │ ├── test_data │ ├── csv_corpus │ │ ├── 1.csv │ │ └── 2.csv │ ├── get_search.json │ └── json_corpus │ │ ├── 1.json │ │ └── 2.json │ ├── test_json_file_training.py │ ├── test_list_training.py │ ├── test_training.py │ └── test_ubuntu_corpus_training.py └── tests_django ├── __init__.py ├── base_case.py ├── test_chatbot.py ├── test_chatterbot_corpus_training.py ├── test_chatterbot_settings.py ├── test_django_adapter.py ├── test_logic_adapter_integration.py ├── test_settings.py └── test_statement_integration.py /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | pep8: 3 | enabled: true 4 | ratings: 5 | paths: 6 | - "**.py" 7 | exclude_paths: 8 | - tests/* 9 | - examples/* 10 | - chatterbot/ext/django_chatterbot/migrations/* 11 | -------------------------------------------------------------------------------- /.github/workflows/publish-documentation.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Sphinx documentation to Pages 2 | 3 | on: 4 | push: 5 | branches: [master] # branch to trigger deployment 6 | 7 | jobs: 8 | pages: 9 | runs-on: ubuntu-latest 10 | environment: 11 | name: github-pages 12 | url: ${{ steps.deployment.outputs.page_url }} 13 | permissions: 14 | pages: write 15 | id-token: write 16 | steps: 17 | - id: deployment 18 | uses: sphinx-notes/pages@v3 19 | with: 20 | pyproject_extras: 'test' 21 | sphinx_build_options: '-b dirhtml' 22 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Python package 5 | 6 | permissions: 7 | contents: read 8 | pull-requests: write 9 | checks: write 10 | 11 | on: 12 | push: 13 | branches: [ "master" ] 14 | pull_request: 15 | branches: [ "*" ] 16 | env: 17 | CHATTERBOT_SHOW_TRAINING_PROGRESS: '0' 18 | 19 | jobs: 20 | 21 | build: 22 | runs-on: ubuntu-latest 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | python-version: ["3.9", "3.10", "3.11", "3.12"] 27 | services: 28 | redis: 29 | image: redis/redis-stack-server:latest 30 | ports: 31 | - 6379:6379 32 | steps: 33 | - uses: actions/checkout@v4 34 | - name: Set up Python ${{ matrix.python-version }} 35 | uses: actions/setup-python@v5 36 | with: 37 | python-version: ${{ matrix.python-version }} 38 | cache: 'pip' 39 | - name: Install dependencies 40 | run: | 41 | python -m pip install --upgrade pip 42 | pip install .[test,dev,redis,mongodb] 43 | python -m spacy download en_core_web_sm 44 | python -m spacy download de_core_news_sm 45 | - name: Lint with flake8 46 | run: | 47 | # stop the build if there are Python syntax errors or undefined names 48 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 49 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 50 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 51 | - name: Start MongoDB 52 | uses: supercharge/mongodb-github-action@1.11.0 53 | with: 54 | mongodb-version: '8.0' 55 | - name: Run tests 56 | run: | 57 | python -Wonce -m unittest discover -s tests -v 58 | - name: Run tests for Django example app 59 | run: | 60 | python -m pip install "django<=4.1" 61 | python -Wonce runtests.py 62 | python -Wonce examples/django_example/manage.py test examples/django_example/ 63 | # -------------------------------------------------------------- 64 | # TODO: Fix & re-enable later 65 | # https://github.com/marketplace/actions/coveralls-github-action 66 | # - name: Coveralls GitHub Action 67 | # uses: coverallsapp/github-action@v2.3.4 68 | # - name: Generate code coverage 69 | # uses: paambaati/codeclimate-action@v9.0.0 70 | # env: 71 | # CC_TEST_REPORTER_ID: 3ec30a156224df0f59620967241d9659086e918fd824f4f69b8ce7b55b5a590f 72 | # with: 73 | # coverageCommand: coverage 74 | # debug: true 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | build 3 | html 4 | dist 5 | venv 6 | .env 7 | .out 8 | .coverage 9 | .python-version 10 | *.pyc 11 | *.swp 12 | *.egg-info 13 | *.egg/* 14 | *.eggs/* 15 | *.doctrees 16 | 17 | # Database files 18 | .database 19 | *.sqlite3 20 | *.sqlite3-* 21 | 22 | # IDE files 23 | .vscode 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 - 2025, Gunther Cox 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | * Neither the name of ChatterBot nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Actively supported versions of ChatterBot can be determined using the following table. 6 | 7 | | Version | Supported | 8 | | ------- | ------------------ | 9 | | 1.2.x | :white_check_mark: | 10 | | < 1.2 | :x: | 11 | 12 | ## Reporting a Vulnerability 13 | 14 | ChatterBot uses GitHub's private security vulnerability reporting to accept reports about potential security vulnerabilities. 15 | 16 | https://github.com/gunthercox/ChatterBot/security/advisories 17 | 18 | To begin the process select the "Report a vulnerability" button on the security advisories page. 19 | Once the report has been investigated an response plan will be issued based on the level of severity. 20 | A response can generally be expected within 24 hours of report submission. 21 | -------------------------------------------------------------------------------- /chatterbot/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ChatterBot is a machine learning, conversational dialog engine. 3 | """ 4 | from .chatterbot import ChatBot 5 | 6 | 7 | __version__ = '1.2.7' 8 | 9 | __all__ = ( 10 | 'ChatBot', 11 | ) 12 | -------------------------------------------------------------------------------- /chatterbot/__main__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example usage for ChatterBot command line arguments: 3 | 4 | python -m chatterbot --help 5 | """ 6 | 7 | import sys 8 | 9 | 10 | def get_chatterbot_version(): 11 | """ 12 | Return the version of the current package. 13 | """ 14 | from chatterbot import __version__ 15 | 16 | return __version__ 17 | 18 | 19 | if __name__ == '__main__': 20 | if '--version' in sys.argv: 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') 28 | -------------------------------------------------------------------------------- /chatterbot/adapters.py: -------------------------------------------------------------------------------- 1 | class Adapter(object): 2 | """ 3 | A superclass for all adapter classes. 4 | 5 | :param chatbot: A ChatBot instance. 6 | """ 7 | 8 | def __init__(self, chatbot, **kwargs): 9 | self.chatbot = chatbot 10 | 11 | class AdapterMethodNotImplementedError(NotImplementedError): 12 | """ 13 | An exception to be raised when an adapter method has not been implemented. 14 | Typically this indicates that the developer is expected to implement the 15 | method in a subclass. 16 | """ 17 | 18 | def __init__(self, message='This method must be overridden in a subclass method.'): 19 | """ 20 | Set the message for the exception. 21 | """ 22 | super().__init__(message) 23 | 24 | class InvalidAdapterTypeException(Exception): 25 | """ 26 | An exception to be raised when an adapter 27 | of an unexpected class type is received. 28 | """ 29 | pass 30 | -------------------------------------------------------------------------------- /chatterbot/components.py: -------------------------------------------------------------------------------- 1 | """ 2 | Custom components for Spacy processing pipelines. 3 | https://spacy.io/usage/processing-pipelines#custom-components 4 | """ 5 | import string 6 | from spacy.language import Language 7 | from spacy.tokens import Doc 8 | 9 | 10 | punctuation_table = str.maketrans(dict.fromkeys(string.punctuation)) 11 | 12 | 13 | @Language.component('chatterbot_bigram_indexer') 14 | def chatterbot_bigram_indexer(document): 15 | """ 16 | Generate the text string for a bigram-based search index. 17 | """ 18 | 19 | if not Doc.has_extension('search_index'): 20 | Doc.set_extension('search_index', default='') 21 | 22 | tokens = [ 23 | token for token in document if not (token.is_punct or token.is_stop) 24 | ] 25 | 26 | # Fall back to including stop words if needed 27 | if not tokens or len(tokens) == 1: 28 | tokens = [ 29 | token for token in document if not (token.is_punct) 30 | ] 31 | 32 | bigram_pairs = [ 33 | f"{tokens[i - 1].pos_}:{tokens[i].lemma_.lower()}" 34 | for i in range(1, len(tokens)) 35 | ] 36 | 37 | if not bigram_pairs: 38 | 39 | text_without_punctuation = document.text.translate( 40 | punctuation_table 41 | ) 42 | if len(text_without_punctuation) >= 1: 43 | text = text_without_punctuation.lower() 44 | else: 45 | text = document.text.lower() 46 | 47 | bigram_pairs = [text] 48 | 49 | # Assign a custom attribute at the Doc level 50 | document._.search_index = ' '.join(bigram_pairs) 51 | 52 | return document 53 | 54 | 55 | @Language.component('chatterbot_lowercase_indexer') 56 | def chatterbot_lowercase_indexer(document): 57 | """ 58 | Generate the a lowercase text string for search index. 59 | """ 60 | 61 | if not Doc.has_extension('search_index'): 62 | Doc.set_extension('search_index', default='') 63 | 64 | # Assign a custom attribute at the Doc level 65 | document._.search_index = document.text.lower() 66 | 67 | return document 68 | -------------------------------------------------------------------------------- /chatterbot/constants.py: -------------------------------------------------------------------------------- 1 | """ 2 | ChatterBot constants 3 | """ 4 | from chatterbot import languages 5 | 6 | ''' 7 | The maximum length of characters that the text of a statement can contain. 8 | The number 255 is used because that is the maximum length of a char field 9 | in most databases. This value should be enforced on a per-model basis by 10 | the data model for each storage adapter. 11 | ''' 12 | STATEMENT_TEXT_MAX_LENGTH = 255 13 | 14 | ''' 15 | The maximum length of characters that the text label of a conversation can contain. 16 | The number 32 was chosen because that is the length of the string representation 17 | of a UUID4 with no hyphens. 18 | ''' 19 | CONVERSATION_LABEL_MAX_LENGTH = 32 20 | 21 | ''' 22 | The maximum length of text that can be stored in the persona field of the statement model. 23 | ''' 24 | PERSONA_MAX_LENGTH = 50 25 | 26 | # The maximum length of characters that the name of a tag can contain 27 | TAG_NAME_MAX_LENGTH = 50 28 | 29 | # See other model options: https://spacy.io/models/ 30 | DEFAULT_LANGUAGE_TO_SPACY_MODEL_MAP = { 31 | languages.CAT: 'ca_core_news_sm', 32 | languages.CHI: 'zh_core_web_sm', 33 | languages.HRV: 'hr_core_news_sm', 34 | languages.DAN: 'da_core_news_sm', 35 | languages.DUT: 'nl_core_news_sm', 36 | languages.ENG: 'en_core_web_sm', 37 | languages.FIN: 'fi_core_news_sm', 38 | languages.FRE: 'fr_core_news_sm', 39 | languages.GER: 'de_core_news_sm', 40 | languages.GRE: 'el_core_news_sm', 41 | languages.ITA: 'it_core_news_sm', 42 | languages.JPN: 'ja_core_news_sm', 43 | languages.KOR: 'ko_core_news_sm', 44 | languages.LIT: 'lt_core_news_sm', 45 | languages.MAC: 'mk_core_news_sm', 46 | languages.NOR: 'nb_core_news_sm', 47 | languages.POL: 'pl_core_news_sm', 48 | languages.POR: 'pt_core_news_sm', 49 | languages.RUM: 'ro_core_news_sm', 50 | languages.RUS: 'ru_core_news_sm', 51 | languages.SLO: 'sl_core_news_sm', 52 | languages.SPA: 'es_core_news_sm', 53 | languages.SWE: 'sv_core_news_sm', 54 | languages.UKR: 'uk_core_news_sm', 55 | } 56 | 57 | DEFAULT_DJANGO_APP_NAME = 'django_chatterbot' 58 | -------------------------------------------------------------------------------- /chatterbot/corpus.py: -------------------------------------------------------------------------------- 1 | import os 2 | import io 3 | import glob 4 | from pathlib import Path 5 | from chatterbot.exceptions import OptionalDependencyImportError 6 | 7 | try: 8 | from chatterbot_corpus.corpus import DATA_DIRECTORY 9 | except (ImportError, ModuleNotFoundError): 10 | # Default to the home directory of the current user 11 | DATA_DIRECTORY = os.path.join( 12 | Path.home(), 13 | 'chatterbot_corpus', 14 | 'data' 15 | ) 16 | 17 | 18 | CORPUS_EXTENSION = 'yml' 19 | 20 | 21 | def get_file_path(dotted_path, extension='json') -> str: 22 | """ 23 | Reads a dotted file path and returns the file path. 24 | """ 25 | # If the operating system's file path seperator character is in the string 26 | if os.sep in dotted_path or '/' in dotted_path: 27 | # Assume the path is a valid file path 28 | return dotted_path 29 | 30 | parts = dotted_path.split('.') 31 | if parts[0] == 'chatterbot': 32 | parts.pop(0) 33 | parts[0] = DATA_DIRECTORY 34 | 35 | corpus_path = os.path.join(*parts) 36 | 37 | path_with_extension = '{}.{}'.format(corpus_path, extension) 38 | if os.path.exists(path_with_extension): 39 | corpus_path = path_with_extension 40 | 41 | return corpus_path 42 | 43 | 44 | def read_corpus(file_name) -> dict: 45 | """ 46 | Read and return the data from a corpus json file. 47 | """ 48 | try: 49 | import yaml 50 | except ImportError: 51 | message = ( 52 | 'Unable to import "yaml".\n' 53 | 'Please install "pyyaml" to enable chatterbot corpus functionality:\n' 54 | 'pip install pyyaml' 55 | ) 56 | raise OptionalDependencyImportError(message) 57 | 58 | with io.open(file_name, encoding='utf-8') as data_file: 59 | return yaml.safe_load(data_file) 60 | 61 | 62 | def list_corpus_files(dotted_path) -> list[str]: 63 | """ 64 | Return a list of file paths to each data file in the specified corpus. 65 | """ 66 | corpus_path = get_file_path(dotted_path, extension=CORPUS_EXTENSION) 67 | paths = [] 68 | 69 | if os.path.isdir(corpus_path): 70 | paths = glob.glob(corpus_path + '/**/*.' + CORPUS_EXTENSION, recursive=True) 71 | else: 72 | paths.append(corpus_path) 73 | 74 | paths.sort() 75 | return paths 76 | 77 | 78 | def load_corpus(*data_file_paths): 79 | """ 80 | Return the data contained within a specified corpus. 81 | """ 82 | for file_path in data_file_paths: 83 | corpus = [] 84 | corpus_data = read_corpus(file_path) 85 | 86 | conversations = corpus_data.get('conversations', []) 87 | corpus.extend(conversations) 88 | 89 | categories = corpus_data.get('categories', []) 90 | 91 | yield corpus, categories, file_path 92 | -------------------------------------------------------------------------------- /chatterbot/exceptions.py: -------------------------------------------------------------------------------- 1 | class OptionalDependencyImportError(ImportError): 2 | """ 3 | An exception raised when a feature requires an optional dependency to be installed. 4 | """ 5 | pass 6 | -------------------------------------------------------------------------------- /chatterbot/ext/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/chatterbot/ext/__init__.py -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = ( 2 | 'chatterbot.ext.django_chatterbot.apps.DjangoChatterBotConfig' 3 | ) 4 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from chatterbot.ext.django_chatterbot.model_admin import StatementAdmin, TagAdmin 3 | from chatterbot.ext.django_chatterbot.models import Statement, Tag 4 | 5 | 6 | admin.site.register(Statement, StatementAdmin) 7 | admin.site.register(Tag, TagAdmin) 8 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DjangoChatterBotConfig(AppConfig): 5 | 6 | name = 'chatterbot.ext.django_chatterbot' 7 | label = 'django_chatterbot' 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) 16 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | import django.db.models.deletion 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | initial = True 8 | 9 | dependencies = [] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name='Response', 14 | fields=[ 15 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 16 | ('occurrence', models.PositiveIntegerField(default=0)), 17 | ], 18 | ), 19 | migrations.CreateModel( 20 | name='Statement', 21 | fields=[ 22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 23 | ('text', models.CharField(max_length=255, unique=True)), 24 | ], 25 | ), 26 | migrations.AddField( 27 | model_name='response', 28 | name='response', 29 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='django_chatterbot.Statement'), 30 | ), 31 | migrations.AddField( 32 | model_name='response', 33 | name='statement', 34 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='in_response_to', to='django_chatterbot.Statement'), 35 | ), 36 | ] 37 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0002_statement_extra_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.10.2 on 2016-10-30 12:13 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0001_initial'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='statement', 14 | name='extra_data', 15 | field=models.CharField(default='{}', max_length=500), 16 | preserve_default=False, 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0003_change_occurrence_default.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.9 on 2016-12-12 00:06 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0002_statement_extra_data'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='response', 14 | name='occurrence', 15 | field=models.PositiveIntegerField(default=1), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0004_rename_in_response_to.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.10.3 on 2016-12-04 23:52 2 | from django.db import migrations, models 3 | import django.db.models.deletion 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('django_chatterbot', '0003_change_occurrence_default'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='response', 15 | name='statement', 16 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='in_response', to='django_chatterbot.Statement'), 17 | ), 18 | migrations.AlterField( 19 | model_name='response', 20 | name='response', 21 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='responses', to='django_chatterbot.Statement'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0005_statement_created_at.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.10.1 on 2016-12-29 19:20 2 | from django.db import migrations, models 3 | import django.utils.timezone 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('django_chatterbot', '0004_rename_in_response_to'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='statement', 15 | name='created_at', 16 | field=models.DateTimeField( 17 | default=django.utils.timezone.now, 18 | help_text='The date and time that this statement was created at.' 19 | ), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0006_create_conversation.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.9 on 2017-01-17 07:02 2 | from django.db import migrations, models 3 | import django.db.models.deletion 4 | import django.utils.timezone 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('django_chatterbot', '0005_statement_created_at'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Conversation', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ], 19 | ), 20 | migrations.AlterField( 21 | model_name='statement', 22 | name='created_at', 23 | field=models.DateTimeField(default=django.utils.timezone.now, help_text='The date and time that this statement was created at.'), 24 | ), 25 | migrations.AddField( 26 | model_name='conversation', 27 | name='statements', 28 | field=models.ManyToManyField(help_text='The statements in this conversation.', related_name='conversation', to='django_chatterbot.Statement'), 29 | ), 30 | ] 31 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0007_response_created_at.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11 on 2017-07-18 00:16 2 | from django.db import migrations, models 3 | import django.utils.timezone 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('django_chatterbot', '0006_create_conversation'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='response', 15 | name='created_at', 16 | field=models.DateTimeField( 17 | default=django.utils.timezone.now, 18 | help_text='The date and time that this response was created at.' 19 | ), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0008_update_conversations.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11 on 2017-07-18 11:25 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0007_response_created_at'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name='conversation', 14 | name='statements', 15 | ), 16 | migrations.RemoveField( 17 | model_name='response', 18 | name='occurrence', 19 | ), 20 | migrations.RemoveField( 21 | model_name='statement', 22 | name='created_at', 23 | ), 24 | migrations.AddField( 25 | model_name='conversation', 26 | name='responses', 27 | field=models.ManyToManyField(help_text='The responses in this conversation.', related_name='conversations', to='django_chatterbot.Response'), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0009_tags.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11a1 on 2017-07-07 00:12 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0008_update_conversations'), 9 | ] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name='Tag', 14 | fields=[ 15 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 16 | ('name', models.SlugField()), 17 | ], 18 | options={ 19 | 'abstract': False, 20 | }, 21 | ), 22 | migrations.AlterField( 23 | model_name='statement', 24 | name='text', 25 | field=models.CharField(max_length=255, unique=True), 26 | ), 27 | migrations.AddField( 28 | model_name='tag', 29 | name='statements', 30 | field=models.ManyToManyField(related_name='tags', to='django_chatterbot.Statement'), 31 | ), 32 | ] 33 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0010_statement_text.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11.4 on 2017-08-16 00:56 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0009_tags'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='statement', 14 | name='text', 15 | field=models.CharField(max_length=400, unique=True), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0011_blank_extra_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11.4 on 2017-08-20 13:55 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0010_statement_text'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name='statement', 14 | name='extra_data', 15 | field=models.CharField(blank=True, max_length=500), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0012_statement_created_at.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | import django.utils.timezone 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0011_blank_extra_data'), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name='statement', 14 | name='created_at', 15 | field=models.DateTimeField( 16 | default=django.utils.timezone.now, 17 | help_text='The date and time that the statement was created at.' 18 | ), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0013_change_conversations.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 1.11 on 2018-09-13 01:01 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [ 8 | ('django_chatterbot', '0012_statement_created_at'), 9 | ] 10 | 11 | operations = [ 12 | migrations.RemoveField( 13 | model_name='conversation', 14 | name='responses', 15 | ), 16 | migrations.RemoveField( 17 | model_name='response', 18 | name='response', 19 | ), 20 | migrations.RemoveField( 21 | model_name='response', 22 | name='statement', 23 | ), 24 | migrations.AddField( 25 | model_name='statement', 26 | name='conversation', 27 | field=models.CharField(default='default', max_length=32), 28 | preserve_default=False, 29 | ), 30 | migrations.AddField( 31 | model_name='statement', 32 | name='in_response_to', 33 | field=models.CharField(max_length=400, null=True), 34 | ), 35 | migrations.AlterField( 36 | model_name='statement', 37 | name='text', 38 | field=models.CharField(max_length=400), 39 | ), 40 | migrations.DeleteModel( 41 | name='Conversation', 42 | ), 43 | migrations.DeleteModel( 44 | name='Response', 45 | ), 46 | ] 47 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0014_remove_statement_extra_data.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('django_chatterbot', '0013_change_conversations'), 8 | ] 9 | 10 | operations = [ 11 | migrations.RemoveField( 12 | model_name='statement', 13 | name='extra_data', 14 | ), 15 | ] 16 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0015_statement_persona.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('django_chatterbot', '0014_remove_statement_extra_data'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name='statement', 13 | name='persona', 14 | field=models.CharField(default='', max_length=50), 15 | preserve_default=False, 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0016_statement_stemmed_text.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('django_chatterbot', '0015_statement_persona'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AddField( 12 | model_name='statement', 13 | name='search_text', 14 | field=models.CharField(blank=True, max_length=400), 15 | ), 16 | migrations.AddField( 17 | model_name='statement', 18 | name='search_in_response_to', 19 | field=models.CharField(blank=True, max_length=400), 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0017_tags_unique.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('django_chatterbot', '0016_statement_stemmed_text'), 8 | ] 9 | 10 | operations = [ 11 | migrations.RemoveField( 12 | model_name='tag', 13 | name='statements', 14 | ), 15 | migrations.AddField( 16 | model_name='statement', 17 | name='tags', 18 | field=models.ManyToManyField( 19 | related_name='statements', 20 | to='django_chatterbot.Tag' 21 | ), 22 | ), 23 | migrations.AlterField( 24 | model_name='tag', 25 | name='name', 26 | field=models.SlugField(unique=True), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0018_text_max_length.py: -------------------------------------------------------------------------------- 1 | from django.db import migrations, models 2 | 3 | 4 | class Migration(migrations.Migration): 5 | 6 | dependencies = [ 7 | ('django_chatterbot', '0017_tags_unique'), 8 | ] 9 | 10 | operations = [ 11 | migrations.AlterField( 12 | model_name='statement', 13 | name='in_response_to', 14 | field=models.CharField(max_length=255, null=True), 15 | ), 16 | migrations.AlterField( 17 | model_name='statement', 18 | name='search_in_response_to', 19 | field=models.CharField(blank=True, max_length=255), 20 | ), 21 | migrations.AlterField( 22 | model_name='statement', 23 | name='search_text', 24 | field=models.CharField(blank=True, max_length=255), 25 | ), 26 | migrations.AlterField( 27 | model_name='statement', 28 | name='text', 29 | field=models.CharField(max_length=255), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0019_alter_statement_id_alter_tag_id_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.19 on 2025-02-09 13:57 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('django_chatterbot', '0018_text_max_length'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='statement', 15 | name='id', 16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), 17 | ), 18 | migrations.AlterField( 19 | model_name='tag', 20 | name='id', 21 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), 22 | ), 23 | migrations.AddIndex( 24 | model_name='statement', 25 | index=models.Index(fields=['search_text'], name='idx_cb_search_text'), 26 | ), 27 | migrations.AddIndex( 28 | model_name='statement', 29 | index=models.Index(fields=['search_in_response_to'], name='idx_cb_search_in_response_to'), 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/0020_alter_statement_conversation_and_more.py: -------------------------------------------------------------------------------- 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 | ] 54 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/chatterbot/ext/django_chatterbot/migrations/__init__.py -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/model_admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | 4 | class StatementAdmin(admin.ModelAdmin): 5 | list_display = ('text', 'in_response_to', 'conversation', 'created_at', ) 6 | list_filter = ('text', 'created_at', ) 7 | search_fields = ('text', ) 8 | 9 | 10 | class TagAdmin(admin.ModelAdmin): 11 | list_display = ('name', ) 12 | list_filter = ('name', ) 13 | search_fields = ('name', ) 14 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/models.py: -------------------------------------------------------------------------------- 1 | from chatterbot.ext.django_chatterbot.abstract_models import AbstractBaseStatement, AbstractBaseTag 2 | 3 | 4 | class Statement(AbstractBaseStatement): 5 | """ 6 | A statement represents a single spoken entity, sentence or 7 | phrase that someone can say. 8 | """ 9 | pass 10 | 11 | 12 | class Tag(AbstractBaseTag): 13 | """ 14 | A label that categorizes a statement. 15 | """ 16 | pass 17 | -------------------------------------------------------------------------------- /chatterbot/ext/django_chatterbot/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Default ChatterBot settings for Django. 3 | """ 4 | from django.conf import settings 5 | from chatterbot import constants 6 | 7 | 8 | CHATTERBOT = getattr(settings, 'CHATTERBOT', {}) 9 | 10 | CHATTERBOT_DEFAULTS = { 11 | 'name': 'ChatterBot', 12 | 'storage_adapter': 'chatterbot.storage.DjangoStorageAdapter', 13 | 'django_app_name': constants.DEFAULT_DJANGO_APP_NAME 14 | } 15 | 16 | CHATTERBOT.update(CHATTERBOT_DEFAULTS) 17 | -------------------------------------------------------------------------------- /chatterbot/ext/sqlalchemy_app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/chatterbot/ext/sqlalchemy_app/__init__.py -------------------------------------------------------------------------------- /chatterbot/ext/sqlalchemy_app/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Table, Column, Integer, String, DateTime, ForeignKey 2 | from sqlalchemy.orm import relationship, declarative_base 3 | from sqlalchemy.sql import func 4 | from sqlalchemy.ext.declarative import declared_attr 5 | 6 | from chatterbot.conversation import StatementMixin 7 | from chatterbot import constants 8 | 9 | 10 | class ModelBase(object): 11 | """ 12 | An augmented base class for SqlAlchemy models. 13 | """ 14 | 15 | @declared_attr 16 | def __tablename__(cls) -> str: 17 | """ 18 | Return the lowercase class name as the name of the table. 19 | """ 20 | return cls.__name__.lower() 21 | 22 | id = Column( 23 | Integer, 24 | primary_key=True, 25 | autoincrement=True 26 | ) 27 | 28 | 29 | Base = declarative_base(cls=ModelBase) 30 | 31 | 32 | tag_association_table = Table( 33 | 'tag_association', 34 | Base.metadata, 35 | Column('tag_id', Integer, ForeignKey('tag.id')), 36 | Column('statement_id', Integer, ForeignKey('statement.id')) 37 | ) 38 | 39 | 40 | class Tag(Base): 41 | """ 42 | A tag that describes a statement. 43 | """ 44 | 45 | name = Column( 46 | String(constants.TAG_NAME_MAX_LENGTH), 47 | unique=True 48 | ) 49 | 50 | 51 | class Statement(Base, StatementMixin): 52 | """ 53 | A Statement represents a sentence or phrase. 54 | """ 55 | 56 | confidence = 0 57 | 58 | text = Column( 59 | String(constants.STATEMENT_TEXT_MAX_LENGTH) 60 | ) 61 | 62 | search_text = Column( 63 | String(constants.STATEMENT_TEXT_MAX_LENGTH), 64 | nullable=False, 65 | server_default='' 66 | ) 67 | 68 | conversation = Column( 69 | String(constants.CONVERSATION_LABEL_MAX_LENGTH), 70 | nullable=False, 71 | server_default='' 72 | ) 73 | 74 | created_at = Column( 75 | DateTime(timezone=True), 76 | server_default=func.now() 77 | ) 78 | 79 | tags = relationship( 80 | 'Tag', 81 | secondary=lambda: tag_association_table, 82 | backref='statements' 83 | ) 84 | 85 | in_response_to = Column( 86 | String(constants.STATEMENT_TEXT_MAX_LENGTH), 87 | nullable=True 88 | ) 89 | 90 | search_in_response_to = Column( 91 | String(constants.STATEMENT_TEXT_MAX_LENGTH), 92 | nullable=False, 93 | server_default='' 94 | ) 95 | 96 | persona = Column( 97 | String(constants.PERSONA_MAX_LENGTH), 98 | nullable=False, 99 | server_default='' 100 | ) 101 | 102 | def get_tags(self) -> list[str]: 103 | """ 104 | Return a list of tags for this statement. 105 | """ 106 | return [tag.name for tag in self.tags] 107 | 108 | def add_tags(self, *tags): 109 | """ 110 | Add a list of strings to the statement as tags. 111 | """ 112 | self.tags.extend([ 113 | Tag(name=tag) for tag in tags 114 | ]) 115 | -------------------------------------------------------------------------------- /chatterbot/filters.py: -------------------------------------------------------------------------------- 1 | def get_recent_repeated_responses(chatbot, conversation, sample=10, threshold=3, quantity=3) -> list: 2 | """ 3 | A filter that eliminates possibly repetitive responses to prevent 4 | a chat bot from repeating statements that it has recently said. 5 | """ 6 | from collections import Counter 7 | 8 | # Get the most recent statements from the conversation 9 | conversation_statements = list(chatbot.storage.filter( 10 | conversation=conversation, 11 | order_by=['id'] 12 | ))[sample * -1:] 13 | 14 | text_of_recent_responses = [ 15 | statement.text for statement in conversation_statements 16 | ] 17 | 18 | counter = Counter(text_of_recent_responses) 19 | 20 | # Find the n most common responses from the conversation 21 | most_common = counter.most_common(quantity) 22 | 23 | return [ 24 | counted[0] for counted in most_common 25 | if counted[1] >= threshold 26 | ] 27 | -------------------------------------------------------------------------------- /chatterbot/logic/__init__.py: -------------------------------------------------------------------------------- 1 | from chatterbot.logic.logic_adapter import LogicAdapter 2 | from chatterbot.logic.best_match import BestMatch 3 | from chatterbot.logic.mathematical_evaluation import MathematicalEvaluation 4 | from chatterbot.logic.specific_response import SpecificResponseAdapter 5 | from chatterbot.logic.time_adapter import TimeLogicAdapter 6 | from chatterbot.logic.unit_conversion import UnitConversion 7 | 8 | 9 | __all__ = ( 10 | 'LogicAdapter', 11 | 'BestMatch', 12 | 'MathematicalEvaluation', 13 | 'SpecificResponseAdapter', 14 | 'TimeLogicAdapter', 15 | 'UnitConversion', 16 | ) 17 | -------------------------------------------------------------------------------- /chatterbot/logic/mathematical_evaluation.py: -------------------------------------------------------------------------------- 1 | from chatterbot.logic import LogicAdapter 2 | from chatterbot.conversation import Statement 3 | from chatterbot import languages 4 | 5 | 6 | class MathematicalEvaluation(LogicAdapter): 7 | """ 8 | The MathematicalEvaluation logic adapter parses input to determine 9 | whether the user is asking a question that requires math to be done. 10 | If so, the equation is extracted from the input and returned with 11 | the evaluated result. 12 | 13 | For example: 14 | User: 'What is three plus five?' 15 | Bot: 'Three plus five equals eight' 16 | 17 | :kwargs: 18 | * *language* (``object``) -- 19 | The language is set to ``chatterbot.languages.ENG`` for English by default. 20 | """ 21 | 22 | def __init__(self, chatbot, **kwargs): 23 | super().__init__(chatbot, **kwargs) 24 | 25 | self.language = kwargs.get('language', languages.ENG) 26 | self.cache = {} 27 | 28 | def can_process(self, statement) -> bool: 29 | """ 30 | Determines whether it is appropriate for this 31 | adapter to respond to the user input. 32 | """ 33 | response = self.process(statement) 34 | self.cache[statement.text] = response 35 | return response.confidence == 1 36 | 37 | def process(self, statement: Statement, additional_response_selection_parameters: dict = None) -> Statement: 38 | """ 39 | Takes a statement string. 40 | Returns the equation from the statement with the mathematical terms solved. 41 | """ 42 | from mathparse import mathparse 43 | 44 | input_text = statement.text 45 | 46 | # Use the result cached by the process method if it exists 47 | if input_text in self.cache: 48 | cached_result = self.cache[input_text] 49 | self.cache = {} 50 | return cached_result 51 | 52 | # Getting the mathematical terms within the input statement 53 | expression = mathparse.extract_expression(input_text, language=self.language.ISO_639.upper()) 54 | 55 | response = Statement(text=expression) 56 | 57 | try: 58 | response.text = '{} = {}'.format( 59 | response.text, 60 | mathparse.parse(expression, language=self.language.ISO_639.upper()) 61 | ) 62 | 63 | # The confidence is 1 if the expression could be evaluated 64 | response.confidence = 1 65 | except mathparse.PostfixTokenEvaluationException: 66 | response.confidence = 0 67 | 68 | return response 69 | -------------------------------------------------------------------------------- /chatterbot/logic/specific_response.py: -------------------------------------------------------------------------------- 1 | from chatterbot.logic import LogicAdapter 2 | from chatterbot.conversation import Statement 3 | from chatterbot import languages 4 | from chatterbot.utils import get_model_for_language 5 | import spacy 6 | 7 | 8 | class SpecificResponseAdapter(LogicAdapter): 9 | """ 10 | Return a specific response to a specific input. 11 | 12 | :kwargs: 13 | * *input_text* (``str``) -- 14 | The input text that triggers this logic adapter. 15 | * *output_text* (``str`` or ``function``) -- 16 | The output text returned by this logic adapter. 17 | If a function is provided, it should return a string. 18 | """ 19 | 20 | def __init__(self, chatbot, **kwargs): 21 | super().__init__(chatbot, **kwargs) 22 | 23 | self.input_text = kwargs.get('input_text') 24 | 25 | self.matcher = None 26 | 27 | if MatcherClass := kwargs.get('matcher'): 28 | language = kwargs.get('language', languages.ENG) 29 | 30 | self.nlp = self._initialize_nlp(language) 31 | 32 | self.matcher = MatcherClass(self.nlp.vocab) 33 | 34 | self.matcher.add('SpecificResponse', [self.input_text]) 35 | 36 | self._output_text = kwargs.get('output_text') 37 | 38 | def _initialize_nlp(self, language): 39 | model = get_model_for_language(language) 40 | 41 | return spacy.load(model) 42 | 43 | def can_process(self, statement) -> bool: 44 | if self.matcher: 45 | doc = self.nlp(statement.text) 46 | matches = self.matcher(doc) 47 | 48 | if matches: 49 | return True 50 | elif statement.text == self.input_text: 51 | return True 52 | 53 | return False 54 | 55 | def process(self, statement: Statement, additional_response_selection_parameters: dict = None) -> Statement: 56 | 57 | if callable(self._output_text): 58 | response_statement = Statement(text=self._output_text()) 59 | else: 60 | response_statement = Statement(text=self._output_text) 61 | 62 | if self.matcher: 63 | doc = self.nlp(statement.text) 64 | matches = self.matcher(doc) 65 | 66 | if matches: 67 | response_statement.confidence = 1 68 | else: 69 | response_statement.confidence = 0 70 | 71 | elif statement.text == self.input_text: 72 | response_statement.confidence = 1 73 | else: 74 | response_statement.confidence = 0 75 | 76 | return response_statement 77 | -------------------------------------------------------------------------------- /chatterbot/logic/time_adapter.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from chatterbot import languages 3 | from chatterbot.logic import LogicAdapter 4 | from chatterbot.conversation import Statement 5 | from chatterbot.utils import get_model_for_language 6 | import spacy 7 | 8 | 9 | class TimeLogicAdapter(LogicAdapter): 10 | """ 11 | The TimeLogicAdapter returns the current time. 12 | 13 | :kwargs: 14 | * *positive* (``list``) -- 15 | The time-related questions used to identify time questions about the current time. 16 | Defaults to a list of English sentences. 17 | * *language* (``str``) -- 18 | The language for the spacy model. Defaults to English. 19 | """ 20 | 21 | def __init__(self, chatbot, **kwargs): 22 | super().__init__(chatbot, **kwargs) 23 | 24 | # TODO / FUTURE: Switch `positive` to `patterns` for more accurate naming 25 | phrases = kwargs.get('positive', [ 26 | 'What time is it?', 27 | 'Hey, what time is it?', 28 | 'Do you have the time?', 29 | 'Do you know the time?', 30 | 'Do you know what time it is?', 31 | 'What is the time?', 32 | 'What time is it now?', 33 | 'Can you tell me the time?', 34 | 'Could you tell me the time?', 35 | 'What is the current time?', 36 | ]) 37 | 38 | language = kwargs.get('language', languages.ENG) 39 | 40 | model = get_model_for_language(language) 41 | 42 | self.nlp = spacy.load(model) 43 | 44 | # Set up rules for spacy's rule-based matching 45 | # https://spacy.io/usage/rule-based-matching 46 | 47 | self.matcher = spacy.matcher.PhraseMatcher(self.nlp.vocab) 48 | 49 | patterns = [self.nlp.make_doc(text) for text in phrases] 50 | 51 | # Add the patterns to the matcher 52 | self.matcher.add('TimeQuestionList', patterns) 53 | 54 | def process(self, statement: Statement, additional_response_selection_parameters: dict = None) -> Statement: 55 | now = datetime.now() 56 | 57 | # Check if the input statement contains a time-related question 58 | doc = self.nlp(statement.text) 59 | 60 | matches = self.matcher(doc) 61 | 62 | self.chatbot.logger.info('TimeLogicAdapter detected {} matches'.format(len(matches))) 63 | 64 | confidence = 1 if matches else 0 65 | response = Statement(text='The current time is ' + now.strftime('%I:%M %p')) 66 | 67 | response.confidence = confidence 68 | return response 69 | -------------------------------------------------------------------------------- /chatterbot/preprocessors.py: -------------------------------------------------------------------------------- 1 | """ 2 | Statement pre-processors. 3 | """ 4 | from chatterbot.conversation import Statement 5 | from unicodedata import normalize 6 | from re import sub as re_sub 7 | from html import unescape 8 | 9 | 10 | def clean_whitespace(statement: Statement) -> Statement: 11 | """ 12 | Remove any consecutive whitespace characters from the statement text. 13 | """ 14 | # Replace linebreaks and tabs with spaces 15 | # Uses splitlines() which includes a superset of universal newlines: 16 | # https://docs.python.org/3/library/stdtypes.html#str.splitlines 17 | statement.text = ' '.join(statement.text.splitlines()).replace('\t', ' ') 18 | 19 | # Remove any leading or trailing whitespace 20 | statement.text = statement.text.strip() 21 | 22 | # Remove consecutive spaces 23 | statement.text = re_sub(' +', ' ', statement.text) 24 | 25 | return statement 26 | 27 | 28 | def unescape_html(statement: Statement) -> Statement: 29 | """ 30 | Convert escaped html characters into unescaped html characters. 31 | For example: "<b>" becomes "". 32 | """ 33 | statement.text = unescape(statement.text) 34 | 35 | return statement 36 | 37 | 38 | def convert_to_ascii(statement: Statement) -> Statement: 39 | """ 40 | Converts unicode characters to ASCII character equivalents. 41 | For example: "på fédéral" becomes "pa federal". 42 | """ 43 | text = normalize('NFKD', statement.text) 44 | text = text.encode('ascii', 'ignore').decode('utf-8') 45 | 46 | statement.text = str(text) 47 | return statement 48 | -------------------------------------------------------------------------------- /chatterbot/response_selection.py: -------------------------------------------------------------------------------- 1 | """ 2 | Response selection methods determines which response should be used in 3 | the event that multiple responses are generated within a logic adapter. 4 | """ 5 | from chatterbot.conversation import Statement 6 | import logging 7 | 8 | 9 | def get_most_frequent_response(input_statement: Statement, response_list: list[Statement], storage=None) -> Statement: 10 | """ 11 | :param input_statement: A statement, that closely matches an input to the chat bot. 12 | 13 | :param response_list: A list of statement options to choose a response from. 14 | 15 | :param storage: An instance of a storage adapter to allow the response selection 16 | method to access other statements if needed. 17 | :type storage: StorageAdapter 18 | 19 | :return: The response statement with the greatest number of occurrences. 20 | """ 21 | matching_response = None 22 | occurrence_count = -1 23 | 24 | logger = logging.getLogger(__name__) 25 | logger.info('Selecting response with greatest number of occurrences.') 26 | 27 | for statement in response_list: 28 | count = len(list(storage.filter( 29 | text=statement.text, 30 | in_response_to=input_statement.text) 31 | )) 32 | 33 | # Keep the more common statement 34 | if count >= occurrence_count: 35 | matching_response = statement 36 | occurrence_count = count 37 | 38 | # Choose the most commonly occurring matching response 39 | return matching_response 40 | 41 | 42 | def get_first_response(input_statement: Statement, response_list: list[Statement], storage=None) -> Statement: 43 | """ 44 | :param input_statement: A statement, that closely matches an input to the chat bot. 45 | 46 | :param response_list: A list of statement options to choose a response from. 47 | 48 | :param storage: An instance of a storage adapter to allow the response selection 49 | method to access other statements if needed. 50 | :type storage: StorageAdapter 51 | 52 | :return: Return the first statement in the response list. 53 | """ 54 | logger = logging.getLogger(__name__) 55 | logger.info('Selecting first response from list of {} options.'.format( 56 | len(response_list) 57 | )) 58 | return response_list[0] 59 | 60 | 61 | def get_random_response(input_statement: Statement, response_list: list[Statement], storage=None) -> Statement: 62 | """ 63 | :param input_statement: A statement, that closely matches an input to the chat bot. 64 | :type input_statement: Statement 65 | 66 | :param response_list: A list of statement options to choose a response from. 67 | :type response_list: list 68 | 69 | :param storage: An instance of a storage adapter to allow the response selection 70 | method to access other statements if needed. 71 | :type storage: StorageAdapter 72 | 73 | :return: Choose a random response from the selection. 74 | """ 75 | from random import choice 76 | logger = logging.getLogger(__name__) 77 | logger.info('Selecting a response from list of {} options.'.format( 78 | len(response_list) 79 | )) 80 | return choice(response_list) 81 | -------------------------------------------------------------------------------- /chatterbot/storage/__init__.py: -------------------------------------------------------------------------------- 1 | from chatterbot.storage.storage_adapter import StorageAdapter 2 | from chatterbot.storage.django_storage import DjangoStorageAdapter 3 | from chatterbot.storage.mongodb import MongoDatabaseAdapter 4 | from chatterbot.storage.sql_storage import SQLStorageAdapter 5 | from chatterbot.storage.redis import RedisVectorStorageAdapter 6 | 7 | 8 | __all__ = ( 9 | 'StorageAdapter', 10 | 'DjangoStorageAdapter', 11 | 'MongoDatabaseAdapter', 12 | 'SQLStorageAdapter', 13 | 'RedisVectorStorageAdapter', 14 | ) 15 | -------------------------------------------------------------------------------- /chatterbot/tagging.py: -------------------------------------------------------------------------------- 1 | from typing import List, Union, Tuple 2 | from chatterbot import languages 3 | from chatterbot.utils import get_model_for_language 4 | import spacy 5 | 6 | 7 | class LowercaseTagger(object): 8 | """ 9 | Returns the text in lowercase. 10 | """ 11 | 12 | def __init__(self, language=None): 13 | from chatterbot.components import chatterbot_lowercase_indexer # noqa 14 | 15 | self.language = language or languages.ENG 16 | 17 | # Create a new empty spacy nlp object 18 | self.nlp = spacy.blank(self.language.ISO_639_1) 19 | 20 | self.nlp.add_pipe( 21 | 'chatterbot_lowercase_indexer', name='chatterbot_lowercase_indexer', last=True 22 | ) 23 | 24 | def get_text_index_string(self, text: Union[str, List[str]]): 25 | if isinstance(text, list): 26 | documents = self.nlp.pipe(text) 27 | return [document._.search_index for document in documents] 28 | else: 29 | document = self.nlp(text) 30 | return document._.search_index 31 | 32 | def as_nlp_pipeline(self, texts: Union[List[str], Tuple[str, dict]]): 33 | 34 | process_as_tuples = texts and isinstance(texts[0], tuple) 35 | 36 | documents = self.nlp.pipe(texts, as_tuples=process_as_tuples) 37 | return documents 38 | 39 | 40 | class PosLemmaTagger(object): 41 | 42 | def __init__(self, language=None): 43 | from chatterbot.components import chatterbot_bigram_indexer # noqa 44 | 45 | self.language = language or languages.ENG 46 | 47 | model = get_model_for_language(self.language) 48 | 49 | # Disable the Named Entity Recognition (NER) component because it is not necessary 50 | self.nlp = spacy.load(model, exclude=['ner']) 51 | 52 | self.nlp.add_pipe( 53 | 'chatterbot_bigram_indexer', name='chatterbot_bigram_indexer', last=True 54 | ) 55 | 56 | def get_text_index_string(self, text: Union[str, List[str]]) -> str: 57 | """ 58 | Return a string of text containing part-of-speech, lemma pairs. 59 | """ 60 | if isinstance(text, list): 61 | documents = self.nlp.pipe(text) 62 | return [document._.search_index for document in documents] 63 | else: 64 | document = self.nlp(text) 65 | return document._.search_index 66 | 67 | def as_nlp_pipeline(self, texts: Union[List[str], Tuple[str, dict]]): 68 | """ 69 | Accepts a single string or a list of strings, or a list of tuples 70 | where the first element is the text and the second element is a 71 | dictionary of context to return alongside the generated document. 72 | """ 73 | 74 | process_as_tuples = texts and isinstance(texts[0], tuple) 75 | 76 | documents = self.nlp.pipe(texts, as_tuples=process_as_tuples) 77 | return documents 78 | -------------------------------------------------------------------------------- /chatterbot/vectorstores.py: -------------------------------------------------------------------------------- 1 | """ 2 | Redis vector store. 3 | """ 4 | from __future__ import annotations 5 | 6 | from typing import Any, List, Sequence 7 | 8 | from langchain_core.documents import Document 9 | from redisvl.redis.utils import convert_bytes 10 | from redisvl.query import FilterQuery 11 | 12 | from langchain_core.documents import Document 13 | from langchain_redis.vectorstores import RedisVectorStore as LangChainRedisVectorStore 14 | 15 | 16 | class RedisVectorStore(LangChainRedisVectorStore): 17 | """ 18 | Redis vector store integration. 19 | """ 20 | 21 | def query_search( 22 | self, 23 | k=4, 24 | filter=None, 25 | sort_by=None, 26 | ) -> List[Document]: 27 | """ 28 | Return docs based on the provided query. 29 | 30 | k: int, default=4 31 | Number of documents to return. 32 | filter: str, default=None 33 | A filter expression to apply to the query. 34 | sort_by: str, default=None 35 | A field to sort the results by. 36 | 37 | returns: 38 | A list of Documents most matching the query. 39 | """ 40 | from chatterbot import ChatBot 41 | 42 | return_fields = [ 43 | self.config.content_field 44 | ] 45 | return_fields += [ 46 | field.name 47 | for field in self._index.schema.fields.values() 48 | if field.name 49 | not in [self.config.embedding_field, self.config.content_field] 50 | ] 51 | 52 | query = FilterQuery( 53 | return_fields=return_fields, 54 | num_results=k, 55 | filter_expression=filter, 56 | sort_by=sort_by, 57 | ) 58 | 59 | try: 60 | results = self._index.query(query) 61 | except Exception as e: 62 | raise ChatBot.ChatBotException(f'Error querying index: {query}') from e 63 | 64 | if results: 65 | with self._index.client.pipeline(transaction=False) as pipe: 66 | for document in results: 67 | pipe.hgetall(document['id']) 68 | full_documents = convert_bytes(pipe.execute()) 69 | else: 70 | full_documents = [] 71 | 72 | return self._prepare_docs_full( 73 | True, results, full_documents, True 74 | ) 75 | -------------------------------------------------------------------------------- /docs/_ext/canonical.py: -------------------------------------------------------------------------------- 1 | """ 2 | Add GitHub repository details to the Sphinx context. 3 | """ 4 | 5 | def setup_canonical_func(app, pagename, templatename, context, doctree): 6 | """ 7 | Return the url to the specified page on GitHub. 8 | 9 | (Sphinx 7.4 generates a canonical link with a .html extension even 10 | when run in dirhtml mode) 11 | """ 12 | 13 | conf = app.config 14 | 15 | def canonical_func(): 16 | # Special case for the root index page 17 | if pagename == 'index': 18 | return conf.html_baseurl 19 | 20 | dir_name = pagename.replace('/index', '/') 21 | return f'{conf.html_baseurl}{dir_name}' 22 | 23 | # Add it to the page's context 24 | context['canonical_url'] = canonical_func 25 | 26 | 27 | # Extension setup function 28 | def setup(app): 29 | app.connect('html-page-context', setup_canonical_func) 30 | -------------------------------------------------------------------------------- /docs/_ext/github.py: -------------------------------------------------------------------------------- 1 | """ 2 | Add GitHub repository details to the Sphinx context. 3 | """ 4 | 5 | GITHUB_USER = 'gunthercox' 6 | GITHUB_REPO = 'ChatterBot' 7 | 8 | 9 | def setup_github_func(app, pagename, templatename, context, doctree): 10 | """ 11 | Return the url to the specified page on GitHub. 12 | """ 13 | 14 | github_version = 'master' 15 | docs_path = 'docs' 16 | 17 | def my_func(): 18 | return f'https://github.com/{GITHUB_USER}/{GITHUB_REPO}/blob/{github_version}/{docs_path}/{pagename}.rst' 19 | 20 | # Add it to the page's context 21 | context['github_page_link'] = my_func 22 | 23 | 24 | # Extension setup function 25 | def setup(app): 26 | app.connect('html-page-context', setup_github_func) 27 | -------------------------------------------------------------------------------- /docs/_includes/python_module_structure.txt: -------------------------------------------------------------------------------- 1 | IronyAdapter/ 2 | |── README 3 | |── pyproject.toml 4 | |── irony_adapter 5 | | |── __init__.py 6 | | └── logic.py 7 | └── tests 8 | |── __init__.py 9 | └── test_logic.py -------------------------------------------------------------------------------- /docs/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/docs/_static/favicon.ico -------------------------------------------------------------------------------- /docs/_static/github-mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/docs/_static/github-mark.png -------------------------------------------------------------------------------- /docs/_static/so-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/docs/_static/so-icon.png -------------------------------------------------------------------------------- /docs/_static/style.css: -------------------------------------------------------------------------------- 1 | table { 2 | width: 100%; 3 | } 4 | 5 | th.head p { 6 | text-align: center; 7 | } 8 | 9 | .table-justified td { 10 | width: 50%; 11 | } 12 | 13 | div.code-block-caption { 14 | padding: 4px; 15 | } 16 | 17 | .wy-side-nav-search { 18 | background-color: #300a24; 19 | } 20 | 21 | .toctree-l1 { 22 | padding-bottom: 3px; 23 | } 24 | 25 | div.sphinxsidebar { 26 | overflow: hidden; 27 | } 28 | 29 | table caption span.caption-text { 30 | color: #efefef; 31 | background-color: #1c4e63; 32 | width: 100%; 33 | display: block; 34 | } 35 | 36 | .banner { 37 | margin: 0px -20px; 38 | } 39 | 40 | #searchbox { 41 | margin-bottom: 15px; 42 | } 43 | 44 | div.sphinxsidebar input[name="q"] { 45 | border-top-left-radius: 5px; 46 | border-bottom-left-radius: 5px; 47 | } 48 | 49 | div.sphinxsidebar input[type="submit"] { 50 | border-top-right-radius: 5px; 51 | border-bottom-right-radius: 5px; 52 | } 53 | 54 | 55 | .help-footer { 56 | margin-top: 15px; 57 | border-top: 15px #320023 solid; 58 | } 59 | 60 | .help-footer h1, .help-footer h2 { 61 | color: gray!important; 62 | background-color: transparent!important; 63 | } 64 | 65 | .help-footer a { 66 | color: #2980B9; 67 | text-decoration: none; 68 | } 69 | 70 | 71 | .bluesky-social-icon { 72 | width: 30px; 73 | margin-right: 10px; 74 | } 75 | 76 | .inline-block { 77 | display: inline-block; 78 | } 79 | 80 | .admonition { 81 | background-color: #1c4e63; 82 | background-color: #49ff8d; 83 | padding: 12px; 84 | border: 1px solid; 85 | border-radius: 2px; 86 | } 87 | -------------------------------------------------------------------------------- /docs/_static/terminal-example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/docs/_static/terminal-example.gif -------------------------------------------------------------------------------- /docs/_templates/footer.html: -------------------------------------------------------------------------------- 1 | 44 | 45 | 46 | 59 | -------------------------------------------------------------------------------- /docs/_templates/layout.html: -------------------------------------------------------------------------------- 1 | {% extends '!layout.html' %} 2 | {# https://github.com/sphinx-doc/sphinx/blob/master/sphinx/themes/basic/layout.html #} 3 | 4 | {% set pageurl = canonical_url() %} 5 | 6 | {%- block htmltitle %} 7 | 8 | 9 | 11 | 12 | {{ super() }} 13 | {%- endblock %} 14 | 15 | {%- block relbaritems %} 16 |
  • Edit on GitHub |
  • 17 | {% endblock %} 18 | 19 | {% block menu %} 20 | {{ super() }} 21 | {% endblock %} 22 | 23 | {%- block footer %} 24 | {{ super() }} 25 | 26 | 27 | 34 | {% endblock %} -------------------------------------------------------------------------------- /docs/_templates/page.html: -------------------------------------------------------------------------------- 1 | {# Template for simple pages #} 2 | {%- extends "layout.html" %} 3 | 4 | {% block body %} 5 | {{ body }} 6 | {%- include "footer.html" %} 7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /docs/_templates/sidebar_ad.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | -------------------------------------------------------------------------------- /docs/commands.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Command line tools 3 | ================== 4 | 5 | ChatterBot comes with a few command line tools that can help 6 | with general packaging-related tasks. 7 | 8 | Get the installed ChatterBot version 9 | ==================================== 10 | 11 | If have ChatterBot installed and you want to check what version 12 | you have then you can run the following command. 13 | 14 | .. code-block:: bash 15 | 16 | python -m chatterbot --version 17 | 18 | 19 | List available commands 20 | ======================= 21 | 22 | To see a list of all available commands you can run the following: 23 | 24 | .. code-block:: bash 25 | 26 | python -m chatterbot --help 27 | -------------------------------------------------------------------------------- /docs/comparisons.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Comparisons 3 | =========== 4 | 5 | .. _statement-comparison: 6 | 7 | Statement comparison 8 | ==================== 9 | 10 | ChatterBot uses ``Statement`` objects to hold information 11 | about things that can be said. An important part of how a chat bot 12 | selects a response is based on its ability to compare two statements 13 | to each other. There are a number of ways to do this, and ChatterBot 14 | comes with a handful of methods built in for you to use. 15 | 16 | .. automodule:: chatterbot.comparisons 17 | :members: 18 | 19 | Use your own comparison function 20 | ++++++++++++++++++++++++++++++++ 21 | 22 | You can create your own comparison function and use it as long as the function takes two statements 23 | as parameters and returns a numeric value between 0 and 1. A 0 should represent the lowest possible 24 | similarity and a 1 should represent the highest possible similarity. 25 | 26 | .. code-block:: python 27 | 28 | def comparison_function(statement, other_statement): 29 | 30 | # Your comparison logic 31 | 32 | # Return your calculated value here 33 | return 0.0 34 | 35 | Setting the comparison method 36 | ----------------------------- 37 | 38 | To set the statement comparison method for your chat bot, you 39 | will need to pass the ``statement_comparison_function`` parameter 40 | to your chat bot when you initialize it. An example of this 41 | is shown below. 42 | 43 | .. code-block:: python 44 | 45 | from chatterbot import ChatBot 46 | from chatterbot.comparisons import LevenshteinDistance 47 | 48 | chatbot = ChatBot( 49 | # ... 50 | statement_comparison_function=LevenshteinDistance 51 | ) 52 | 53 | 54 | Taggers 55 | ======= 56 | 57 | ChatterBot supports a number of different taggers that can be used to 58 | process the input text. The taggers are used to identify the parts of speech 59 | in the input text and can be used to improve the accuracy of the response selection. 60 | 61 | .. automodule:: chatterbot.tagging 62 | :members: 63 | :undoc-members: 64 | 65 | Languages 66 | ========= 67 | 68 | ChatterBot's ``languages`` module contains helper classes for working with 69 | language codes and names. 70 | 71 | .. autoclass:: chatterbot.languages.ENG 72 | 73 | .. autoclass:: chatterbot.languages.FRE 74 | 75 | .. autoclass:: chatterbot.languages.GER 76 | 77 | .. autoclass:: chatterbot.languages.ITA 78 | 79 | .. autoclass:: chatterbot.languages.JPN 80 | 81 | .. autoclass:: chatterbot.languages.KOR 82 | 83 | .. autoclass:: chatterbot.languages.POR 84 | 85 | .. autoclass:: chatterbot.languages.RUS 86 | 87 | .. autoclass:: chatterbot.languages.SPA 88 | 89 | .. autoclass:: chatterbot.languages.SWE 90 | 91 | .. autoclass:: chatterbot.languages.TUR 92 | 93 | .. autoclass:: chatterbot.languages.ZHT 94 | 95 | See ``chatterbot.languages`` for the full list of languages. 96 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Contributing to ChatterBot 3 | ========================== 4 | 5 | There are numerous ways to contribute to ChatterBot. All of which are highly encouraged. 6 | 7 | - Contributing bug reports and feature requests 8 | 9 | - Contributing documentation 10 | 11 | - Contributing code for new features 12 | 13 | - Contributing fixes for bugs 14 | 15 | Every bit of help received on this project is highly appreciated. 16 | 17 | 18 | Setting Up a Development Environment 19 | ==================================== 20 | 21 | To contribute to ChatterBot's development, you simply need: 22 | 23 | - Python 24 | 25 | - pip 26 | 27 | - A few python packages. You can install them from this projects ``pyproject.yml`` file by running: 28 | 29 | .. code-block:: bash 30 | 31 | pip .[dev,test] 32 | 33 | - A text editor 34 | 35 | 36 | Reporting a Bug 37 | =============== 38 | 39 | If you discover a bug in ChatterBot and wish to report it, please be 40 | sure that you adhere to the following when you report it on GitHub. 41 | 42 | 1. Before creating a new bug report, please search to see if an open or closed report matching yours already exists. 43 | 2. Please include a description that will allow others to recreate the problem you encountered. 44 | 45 | 46 | Requesting New Features 47 | ======================= 48 | 49 | When requesting a new feature in ChatterBot, please make sure to include 50 | the following details in your request. 51 | 52 | 1. Your use case. Describe what you are doing that requires this new functionality. 53 | 54 | 55 | Contributing Documentation 56 | ========================== 57 | 58 | ChatterBot's documentation is written in reStructuredText and is 59 | compiled by Sphinx. The reStructuredText source of the documentation 60 | is located in the ``docs/`` directory. 61 | 62 | To build the documentation yourself, run: 63 | 64 | .. code-block:: bash 65 | 66 | sphinx-build -nW -b dirhtml docs/ html/ 67 | 68 | A useful way to view the documentation is to use the Python built-in HTTP server. You can do this by running: 69 | 70 | .. code-block:: bash 71 | 72 | python -m http.server 73 | 74 | Then navigate to ``http://localhost:8000/`` in your web browser. 75 | 76 | Contributing Code 77 | ================= 78 | 79 | The development of ChatterBot happens on GitHub. Code contributions should be 80 | submitted there in the form of pull requests. 81 | 82 | Pull requests should meet the following criteria. 83 | 84 | 1. Fix one issue and fix it well. 85 | 2. Do not include extraneous changes that do not relate to the issue being fixed. 86 | 3. Include a descriptive title and description for the pull request. 87 | 4. Have descriptive commit messages. 88 | -------------------------------------------------------------------------------- /docs/conversations.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Conversations 3 | ============= 4 | 5 | ChatterBot supports the ability to have multiple concurrent conversations. 6 | A conversations is where the :term:`chat bot` interacts with a person, and supporting 7 | multiple concurrent conversations means that the chat bot can have multiple 8 | different conversations with different people at the same time. 9 | 10 | Conversation scope 11 | ------------------ 12 | 13 | If two ``ChatBot`` instances are created, each will have conversations separate from each other. 14 | 15 | An adapter can access any conversation as long as the unique identifier for the conversation is provided. 16 | 17 | Conversation example 18 | -------------------- 19 | 20 | The following example is taken from the Django ``ChatterBotApiView`` built into ChatterBot. 21 | In this method, the unique identifiers for each chat session are being stored in Django's 22 | session objects. This allows different users who interact with the bot through different 23 | web browsers to have separate conversations with the chat bot. 24 | 25 | .. literalinclude:: ../examples/django_example/django_example/views.py 26 | :caption: examples/django_example/django_example/views.py 27 | :language: python 28 | :pyobject: ChatterBotApiView.post 29 | :dedent: 4 30 | 31 | 32 | .. _conversation_statements: 33 | 34 | Statements 35 | ========== 36 | 37 | ChatterBot's statement objects represent either an input statement that the 38 | chat bot has received from a user, or an output statement that the chat bot 39 | has returned based on some input. 40 | 41 | .. autoclass:: chatterbot.conversation.Statement 42 | :members: 43 | 44 | .. autoattribute:: chatterbot.conversation.Statement.confidence 45 | 46 | ChatterBot's logic adapters assign a confidence score to the statement 47 | before it is returned. The confidence score indicates the degree of 48 | certainty with which the chat bot believes this is the correct response 49 | to the given input. 50 | 51 | .. autoattribute:: chatterbot.conversation.Statement.in_response_to 52 | 53 | The response attribute represents the relationship between two statements. 54 | This value of this field indicates that one statement was issued in response 55 | to another statement. 56 | 57 | 58 | Statement-response relationship 59 | =============================== 60 | 61 | ChatterBot stores knowledge of conversations as statements. Each statement can have any 62 | number of possible responses. 63 | 64 | .. image:: _static/statement-response-relationship.svg 65 | :alt: ChatterBot statement-response relationship 66 | 67 | Each ``Statement`` object has an ``in_response_to`` reference which links the 68 | statement to a number of other statements that it has been learned to be in response to. 69 | The ``in_response_to`` attribute is essentially a reference to all parent statements 70 | of the current statement. 71 | 72 | .. image:: _static/statement-relationship.svg 73 | :alt: ChatterBot statement relationship 74 | 75 | The count of recorded statements with matching, or similar text indicates the number of 76 | times that the statement has been given as a response. This makes it possible for the 77 | chat bot to determine if a particular response is more commonly used than another. 78 | -------------------------------------------------------------------------------- /docs/corpus.rst: -------------------------------------------------------------------------------- 1 | ChatterBot Corpus 2 | ================= 3 | 4 | This is a :term:`corpus` of dialog data that is included in the chatterbot module. 5 | 6 | Additional information about the ``chatterbot-corpus`` module can be found 7 | in the `ChatterBot Corpus Documentation`_. 8 | 9 | Corpus language availability 10 | ---------------------------- 11 | 12 | Corpus data is user contributed, but it is also not difficult to create one if you are familiar with the language. 13 | This is because each corpus is just a sample of various input statements and their responses for the bot to train itself with. 14 | 15 | To explore what languages and collections of corpora are available, 16 | check out the `chatterbot_corpus/data`_ directory in the separate chatterbot-corpus repository. 17 | 18 | .. note:: 19 | If you are interested in contributing content to the corpus, please feel free to 20 | submit a pull request on ChatterBot's corpus GitHub page. Contributions are welcomed! 21 | 22 | https://github.com/gunthercox/chatterbot-corpus 23 | 24 | The ``chatterbot-corpus`` is distributed in its own Python package so that it can 25 | be released and upgraded independently from the ``chatterbot`` package. 26 | 27 | 28 | Exporting your chat bot's database as a training corpus 29 | ------------------------------------------------------- 30 | 31 | Now that you have created your chat bot and sent it out into the world, perhaps 32 | you are looking for a way to share what it has learned with other chat bots? 33 | ChatterBot's training module provides methods that allow you to export the 34 | content of your chat bot's database as a training corpus that can be used to 35 | train other chat bots. 36 | 37 | Here is an example: 38 | 39 | .. literalinclude:: ../examples/export_example.py 40 | :caption: /examples/export_example.py 41 | :language: python 42 | 43 | .. _chatterbot_corpus/data: https://github.com/gunthercox/chatterbot-corpus/tree/master/chatterbot_corpus/data 44 | .. _ChatterBot Corpus Documentation: https://corpus.chatterbot.us/ 45 | -------------------------------------------------------------------------------- /docs/development.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Development 3 | =========== 4 | 5 | As the code for ChatterBot is written, the developers attempt to describe 6 | the logic and reasoning for the various decisions that go into creating the 7 | internal structure of the software. This internal documentation is intended 8 | for future developers and maintainers of the project. A majority of this 9 | information is unnecessary for the typical developer using ChatterBot. 10 | 11 | It is not always possible for every idea to be documented. As a result, the 12 | need may arise to question the developers and maintainers of this project 13 | in order to pull concepts from their minds and place them in these documents. 14 | Please pull gently. 15 | 16 | .. toctree:: 17 | :maxdepth: 2 18 | :caption: Contents: 19 | 20 | contributing 21 | releases 22 | Release Notes 23 | testing 24 | packaging 25 | 26 | Suggested Development Tools 27 | =========================== 28 | 29 | To help developers work with ChatterBot and projects built using it, the 30 | following tools are suggested. Keep in mind that none of these are required, 31 | but this list has been assembled because it is often useful to have a 32 | tool or technology recommended by people who have experience using it. 33 | 34 | Text Editors 35 | ------------ 36 | 37 | Visual Studio Code 38 | ++++++++++++++++++ 39 | 40 | Website: https://code.visualstudio.com/ 41 | 42 | | I find Visual Studio Code to be an optimally light-weight 43 | | and versatile editor. 44 | | 45 | | ~ Gunther Cox 46 | 47 | Database Clients 48 | ---------------- 49 | 50 | SQLite Viewer 51 | +++++++++++++ 52 | 53 | | This is a very simple SQLite viewer extension for VS Code that allows you to 54 | | sqlite databases. 55 | 56 | Website: https://marketplace.visualstudio.com/items?itemName=qwtel.sqlite-viewer 57 | 58 | 59 | pgAdmin 60 | +++++++ 61 | 62 | | pgAdmin a pretty good database client when working with PostgreSQL. 63 | 64 | Website: https://www.pgadmin.org/ -------------------------------------------------------------------------------- /docs/django/index.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Django Integration 3 | ================== 4 | 5 | ChatterBot has direct support for integration with Django's ORM. 6 | It is relatively easy to use ChatterBot within your Django application 7 | to create conversational pages and endpoints. 8 | 9 | Install packages 10 | ================ 11 | 12 | Begin by making sure that you have installed both ``django`` and ``chatterbot``. 13 | 14 | .. sourcecode:: sh 15 | 16 | pip install django chatterbot 17 | 18 | For more details on installing and using Django, see the `Django documentation`_. 19 | 20 | Installed Apps 21 | -------------- 22 | 23 | Add ``chatterbot.ext.django_chatterbot`` to your ``INSTALLED_APPS`` in the 24 | ``settings.py`` file of your Django project. 25 | 26 | .. code-block:: python 27 | 28 | INSTALLED_APPS = ( 29 | # ... 30 | 'chatterbot.ext.django_chatterbot', 31 | ) 32 | 33 | 34 | Migrations 35 | ---------- 36 | 37 | You can run the Django database migrations for your chat bot with the 38 | following command. 39 | 40 | .. sourcecode:: sh 41 | 42 | python manage.py migrate django_chatterbot 43 | 44 | MongoDB and Django 45 | ------------------ 46 | 47 | ChatterBot has a storage adapter for MongoDB but it does not work with Django. 48 | If you want to use MongoDB as your database for Django and your chat bot then 49 | you will need to install a **Django storage backend** such as `Django MongoDB Engine`_. 50 | 51 | The reason this is required is because Django's storage backends are different 52 | and completely separate from ChatterBot's storage adapters. 53 | 54 | Django App Development 55 | ====================== 56 | 57 | .. toctree:: 58 | :maxdepth: 2 59 | 60 | settings 61 | views 62 | wsgi 63 | 64 | General Django Tutorials 65 | ======================== 66 | 67 | These are general tutorials related to Django and are not specific to ChatterBot. The goal of these tutorials is to help provide a foundation of knowledge around building Django applications and APIs. 68 | 69 | .. toctree:: 70 | :maxdepth: 4 71 | 72 | tutorial/index 73 | 74 | .. _Django documentation: https://docs.djangoproject.com/en/dev/intro/install/ 75 | .. _Django MongoDB Engine: https://django-mongodb-engine.readthedocs.io/ 76 | -------------------------------------------------------------------------------- /docs/django/settings.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Chatterbot Django Settings 3 | ========================== 4 | 5 | You can edit the ChatterBot configuration through your Django settings.py file. 6 | 7 | .. code-block:: python 8 | :caption: settings.py 9 | 10 | CHATTERBOT = { 11 | 'name': 'Tech Support Bot', 12 | 'logic_adapters': [ 13 | 'chatterbot.logic.MathematicalEvaluation', 14 | 'chatterbot.logic.TimeLogicAdapter', 15 | 'chatterbot.logic.BestMatch' 16 | ] 17 | } 18 | 19 | Any setting that gets set in the CHATTERBOT dictionary will be passed to the chat bot that powers your django app. 20 | 21 | Additional Django settings 22 | ========================== 23 | 24 | - ``django_app_name`` [default: 'django_chatterbot'] The Django app name to look up the models from. 25 | -------------------------------------------------------------------------------- /docs/django/tutorial/django-filter-tutorial/index.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | django-filter tutorial 3 | ====================== 4 | 5 | In this section, we will be adding filtering functionality to the API built in the :ref:`Django REST Framework Tutorial`. To do this we'll be using the `django-filter` package. 6 | 7 | For more information, ``django-filter`` has a comprehensive setup guide in its `documentation `_. 8 | 9 | 1. Install django-filter 10 | ======================== 11 | 12 | To install ``django-filter``, run the following command: 13 | 14 | .. sourcecode:: sh 15 | 16 | pip install django-filter 17 | 18 | 19 | 2. Configure django-filter settings 20 | =================================== 21 | 22 | Add ``django_filters`` to your ``INSTALLED_APPS`` in the ``settings.py`` file of your Django project. 23 | 24 | .. code-block:: python 25 | :caption: tutorial/settings.py 26 | 27 | INSTALLED_APPS = ( 28 | # ... 29 | 'django_filters', 30 | ) 31 | 32 | Also add the following lines to the ``REST_FRAMEWORK`` setting in the same file: 33 | 34 | .. code-block:: python 35 | :caption: tutorial/settings.py 36 | 37 | REST_FRAMEWORK = { 38 | 'DEFAULT_FILTER_BACKENDS': ( 39 | 'django_filters.rest_framework.DjangoFilterBackend', 40 | # ... 41 | ) 42 | } 43 | 44 | 45 | 3. Configure filtering for the API 46 | ================================== 47 | 48 | Define a new ``filterset_fields`` variable at the class level of the viewset you created in the previous tutorial: 49 | 50 | .. code-block:: python 51 | :caption: tutorial/viewsets.py 52 | 53 | # ... 54 | 55 | class ChapterViewSet(viewsets.ModelViewSet): 56 | queryset = Chapter.objects.all() 57 | serializer_class = ChapterSerializer 58 | 59 | # Add this line: 60 | filterset_fields = ('number', 'title') 61 | 62 | # ... 63 | 64 | 4. Test the filtering 65 | ===================== 66 | 67 | You can now test the filtering by adding query parameters to the API URL. For example, to filter chapters by title, you can use the following URL: 68 | 69 | .. code-block:: text 70 | 71 | http://localhost:8000/api/chapters?title=Introduction&number=1 72 | 73 | 74 | You'll see that only the chapter with the title "Introduction" and number "1" is returned in the response. Note that you may need to add more chapters to your database to fully see the filtering in action. 75 | 76 | 5. Conclusion on django-filter 77 | ============================== 78 | 79 | Now you know how to add filtering functionality to your APIs. The ``django-filter`` package has a number of additional features including the ability to create more complex and custom filters. For more information, see the `django-filter documentation `_. 80 | 81 | To wrap up these tutorials, in the :ref:`next section ` we'll be covering how to write tests for the API you just built. 82 | -------------------------------------------------------------------------------- /docs/django/tutorial/django-tutorial/django-installed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/docs/django/tutorial/django-tutorial/django-installed.png -------------------------------------------------------------------------------- /docs/django/tutorial/index.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | Django Tutorials 3 | ================ 4 | 5 | These are mini tutorials designed to introduce basic parts of Django in a way that is quick and brief yet easy to understand. For a more comprehensive explanation, see the tutorial in Django's official documentation: https://docs.djangoproject.com/en/4.2/intro/tutorial01/ 6 | 7 | There are three tutorials in this collection. The first one is a basic introduction to Django, and the second one builds on the first to introduce another common library used alongside Django; `Django REST framework`_. The third tutorial continues by configuring the ``django-filter`` package to filter results from the API built in part 2. 8 | 9 | Django 10 | ------ 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | django-tutorial/index 16 | django-tutorial/part-2 17 | 18 | 19 | Django REST Framework 20 | --------------------- 21 | 22 | .. toctree:: 23 | :maxdepth: 2 24 | 25 | django-rest-framework-tutorial/index 26 | 27 | 28 | .. _Django REST framework: https://www.django-rest-framework.org/ 29 | 30 | 31 | django-filter 32 | ------------- 33 | 34 | .. toctree:: 35 | :maxdepth: 2 36 | 37 | django-filter-tutorial/index 38 | 39 | 40 | Writing Tests 41 | ============= 42 | 43 | .. toctree:: 44 | :maxdepth: 2 45 | 46 | writing-tests 47 | 48 | 49 | .. _Django REST framework: https://www.django-rest-framework.org/ 50 | -------------------------------------------------------------------------------- /docs/django/views.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | ChatterBot Django Sample Code 3 | ============================= 4 | 5 | .. note:: 6 | 7 | Looking for the full example app? Check it out on GitHub: 8 | https://github.com/gunthercox/ChatterBot/tree/master/examples/django_example 9 | 10 | Example API Views 11 | ================= 12 | 13 | ChatterBot's Django example comes with an API view that demonstrates 14 | one way to use ChatterBot to create an conversational API endpoint 15 | for your application. 16 | 17 | The endpoint expects a JSON request in the following format: 18 | 19 | .. code-block:: json 20 | 21 | {"text": "My input statement"} 22 | 23 | 24 | .. literalinclude:: ../../examples/django_example/django_example/views.py 25 | :caption: examples/django_example/django_example/views.py 26 | :language: python 27 | :pyobject: ChatterBotApiView 28 | 29 | 30 | Example Django Management Commands 31 | ================================== 32 | 33 | ChatterBot's Django example includes a management command that 34 | demonstrates a simple example of training. This can be used as 35 | a basis for other custom management commands used with other 36 | :ref:`training options `. 37 | 38 | .. literalinclude:: ../../examples/django_example/django_example/management/commands/train.py 39 | :caption: examples/django_example/django_example/management/commands/train.py 40 | :language: python 41 | -------------------------------------------------------------------------------- /docs/django/wsgi.rst: -------------------------------------------------------------------------------- 1 | Webservices 2 | =========== 3 | 4 | Hosting and serving web applications typically involves setting up a few additional components. A few common items are noted here to help you get started. 5 | 6 | Environments 7 | ------------ 8 | 9 | If you want to host your Django app, you need to choose a method through 10 | which it will be hosted. There are a few free services that you can use 11 | to do this such as `Heroku`_ and `PythonAnyWhere`_. 12 | 13 | Another good option is DigitalOcean, which is notoriously easy to use and offers a number of affordable hosting plans. If you're interested in trying out DigitalOcean we have a `referral link `_ from them that will give you $200 in credit over 60 days. 14 | 15 | WSGI 16 | ---- 17 | 18 | A common method for serving Python web applications involves using a 19 | Web Server Gateway Interface (`WSGI`_) package. 20 | 21 | `Gunicorn`_ is a great choice for a WSGI server. They have detailed 22 | documentation and installation instructions on their website. 23 | 24 | Serving static files 25 | -------------------- 26 | 27 | There are numerous ways to host static files for your Django application. 28 | One extremely easy way to do this is by using `WhiteNoise`_, a python package 29 | designed to make it possible to serve static files from just about any web application. 30 | 31 | .. _Heroku: https://heroku.com/ 32 | .. _PythonAnyWhere: https://www.pythonanywhere.com/details/django_hosting 33 | .. _Gunicorn: http://gunicorn.org/ 34 | .. _WhiteNoise: http://whitenoise.evans.io/en/stable/ 35 | .. _WSGI: http://wsgi.readthedocs.io/en/latest/what.html 36 | -------------------------------------------------------------------------------- /docs/encoding.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | Python String Encoding 3 | ====================== 4 | 5 | The Python developer community has published a great article that covers the 6 | details of unicode character processing. 7 | 8 | - Python 3: https://docs.python.org/3/howto/unicode.html 9 | - Python 2: https://docs.python.org/2/howto/unicode.html 10 | 11 | The following notes are intended to help answer some common questions and issues 12 | that developers frequently encounter while learning to properly work with different 13 | character encodings in Python. 14 | 15 | Does ChatterBot handle non-ascii characters? 16 | ============================================ 17 | 18 | ChatterBot is able to handle unicode values correctly. You can pass to it 19 | non-encoded data and it should be able to process it properly 20 | (you will need to make sure that you decode the output that is returned). 21 | 22 | Below is one of ChatterBot's tests from `tests/test_chatbot.py`_, 23 | this is just a simple check that a unicode response can be processed. 24 | 25 | .. code-block:: python 26 | 27 | def test_get_response_unicode(self): 28 | """ 29 | Test the case that a unicode string is passed in. 30 | """ 31 | response = self.chatbot.get_response(u'سلام') 32 | self.assertGreater(len(response.text), 0) 33 | 34 | This test passes Python 3. It also verifies that 35 | ChatterBot *can* take unicode input without issue. 36 | 37 | How do I fix Python encoding errors? 38 | ==================================== 39 | 40 | When working with string type data in Python, it is possible to encounter errors 41 | such as the following. 42 | 43 | .. code-block:: text 44 | 45 | UnicodeDecodeError: 'utf8' codec can't decode byte 0x92 in position 48: invalid start byte 46 | 47 | Depending on what your code looks like, there are a few things that you can do 48 | to prevent errors like this. 49 | 50 | Unicode header 51 | -------------- 52 | 53 | .. code-block:: python 54 | 55 | # -*- coding: utf-8 -*- 56 | 57 | When to use the unicode header 58 | ++++++++++++++++++++++++++++++ 59 | 60 | If your strings use escaped unicode characters (they look like ``u'\u00b0C'``) then 61 | you do not need to add the header. If you use strings like ``'ØÆÅ'`` then you are required 62 | to use the header. 63 | 64 | If you are using this header it must be the first line in your Python file. 65 | 66 | Unicode escape characters 67 | ------------------------- 68 | 69 | .. code-block:: text 70 | 71 | >>> print u'\u0420\u043e\u0441\u0441\u0438\u044f' 72 | Россия 73 | 74 | When to use escape characters 75 | +++++++++++++++++++++++++++++ 76 | 77 | Prefix your strings with the unicode escape character ``u'...'`` when you are 78 | using escaped unicode characters. 79 | 80 | Import unicode literals from future 81 | ----------------------------------- 82 | 83 | .. code-block:: python 84 | 85 | from __future__ import unicode_literals 86 | 87 | When to import unicode literals 88 | +++++++++++++++++++++++++++++++ 89 | 90 | Use this when you need to make sure that Python 3 code also works in Python 2. 91 | 92 | A good article on this can be found here: http://python-future.org/unicode_literals.html 93 | 94 | .. _`tests/test_chatbot.py`: https://github.com/gunthercox/ChatterBot/blob/master/tests/test_chatbot.py 95 | -------------------------------------------------------------------------------- /docs/examples.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Examples 3 | ======== 4 | 5 | The following examples are available to help you get started with ChatterBot. 6 | 7 | .. note:: 8 | Before you run any example, you will need to install ChatterBot on your system. 9 | See the :ref:`Setup guide ` for instructions. 10 | 11 | All of these examples and more are available in the `examples `_ directory of the ChatterBot repository on GitHub. 12 | 13 | Simple Example 14 | ============== 15 | 16 | .. literalinclude:: ../examples/basic_example.py 17 | :caption: examples/basic_example.py 18 | :language: python 19 | 20 | Terminal Example 21 | ================ 22 | 23 | This example program shows how to create a simple terminal client 24 | that allows you to communicate with your chat bot by typing into 25 | your terminal. 26 | 27 | .. image:: _static/terminal-example.gif 28 | :alt: ChatterBot terminal example running in Python console 29 | 30 | .. literalinclude:: ../examples/terminal_example.py 31 | :caption: examples/terminal_example.py 32 | :language: python 33 | 34 | Using MongoDB 35 | ============= 36 | 37 | Before you can use ChatterBot's built in adapter for MongoDB, 38 | you will need to `install MongoDB`_. Make sure MongoDB is 39 | running in your environment before you execute your program. 40 | To tell ChatterBot to use this adapter, you will need to set 41 | the `storage_adapter` parameter. 42 | 43 | .. code-block:: python 44 | 45 | storage_adapter="chatterbot.storage.MongoDatabaseAdapter" 46 | 47 | .. literalinclude:: ../examples/terminal_mongo_example.py 48 | :caption: examples/terminal_mongo_example.py 49 | :language: python 50 | 51 | Time and Mathematics Example 52 | ============================ 53 | 54 | ChatterBot has natural language evaluation capabilities that 55 | allow it to process and evaluate mathematical and time-based 56 | inputs. 57 | 58 | .. literalinclude:: ../examples/math_and_time.py 59 | :caption: examples/math_and_time.py 60 | :language: python 61 | 62 | Using SQL Adapter 63 | ================= 64 | 65 | ChatterBot data can be saved and retrieved from SQL databases. 66 | 67 | .. literalinclude:: ../examples/memory_sql_example.py 68 | :caption: examples/memory_sql_example.py 69 | :language: python 70 | 71 | Read only mode 72 | ============== 73 | 74 | Your chat bot will learn based on each new input statement it receives. 75 | If you want to disable this learning feature after your bot has been trained, 76 | you can set `read_only=True` as a parameter when initializing the bot. 77 | 78 | .. code-block:: python 79 | 80 | chatbot = ChatBot("Johnny Five", read_only=True) 81 | 82 | Using Large Language Models 83 | =========================== 84 | 85 | Support for large language models (LLMs) is in ChatterBot is still experimental 86 | (as of version 1.2.7). Notes and current usage example can be found in the 87 | :ref:`LLM Roadmap`. 88 | 89 | Django and Flask 90 | ================ 91 | 92 | 1. Django: A number of :ref:`example views are documented `, as well as full example Django app. 93 | 2. Flask: There is a separately maintained example `Flask project using ChatterBot `_. 94 | 95 | .. _install MongoDB: https://docs.mongodb.com/manual/installation/ 96 | -------------------------------------------------------------------------------- /docs/faq.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Frequently Asked Questions 3 | ========================== 4 | 5 | This document is comprised of questions that are frequently 6 | asked about ChatterBot and chat bots in general. 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | encoding 12 | 13 | How do I deploy my chat bot to the web? 14 | --------------------------------------- 15 | 16 | There are a number of excellent web frameworks for creating 17 | Python projects out there. Django and Flask are two excellent 18 | examples of these. ChatterBot is designed to be agnostic to 19 | the platform it is deployed on and it is very easy to get set up. 20 | 21 | To run ChatterBot inside of a web application you just need a way 22 | for your application to receive incoming data and to return data. 23 | You can do this any way you want, HTTP requests, web sockets, etc. 24 | 25 | There are a number of existing examples that show how to do this. 26 | 27 | 1. An example using Django: https://github.com/gunthercox/ChatterBot/tree/master/examples/django_example 28 | 2. An example using Flask: https://github.com/chamkank/flask-chatterbot/blob/master/app.py 29 | 30 | Additional details and recommendations for configuring Django can be found 31 | in the :ref:`Webservices` section of ChatterBot's Django documentation. 32 | 33 | What kinds of machine learning does ChatterBot use? 34 | --------------------------------------------------- 35 | 36 | In brief, ChatterBot uses a number of different machine learning techniques to 37 | generate its responses. The specific algorithms depend on how the chat bot is 38 | used and the settings that it is configured with. 39 | 40 | Here is a general overview of some of the various machine learning techniques 41 | that are employed throughout ChatterBot's codebase. 42 | 43 | 1. Search algorithms 44 | ++++++++++++++++++++ 45 | 46 | Searching is the most rudimentary form of artificial intelligence. To be fair, 47 | there are differences between machine learning and artificial intelligence but 48 | lets avoid those for now and instead focus on the topic of algorithms that make 49 | the chat bot talk intelligently. 50 | 51 | Search is a crucial part of how a chat bot quickly and efficiently retrieves 52 | the possible candidate statements that it can respond with. 53 | 54 | Some examples of attributes that help the chat bot select a response include 55 | 56 | - the similarity of an input statement to known statements 57 | - the frequency in which similar known responses occur 58 | - the likeliness of an input statement to fit into a category that known statements are a part of 59 | 60 | 2. Classification algorithms 61 | ++++++++++++++++++++++++++++ 62 | 63 | Several logic adapters in ChatterBot use `naive Bayesian classification`_ 64 | algorithms to determine if an input statement meets a particular set of 65 | criteria that warrant a response to be generated from that logic adapter. 66 | 67 | .. _naive Bayesian classification: https://en.wikipedia.org/wiki/Naive_Bayes_classifier 68 | -------------------------------------------------------------------------------- /docs/filters.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Filters 3 | ======= 4 | 5 | Filters are an efficient way to create queries that can be passed to ChatterBot's storage adapters. 6 | Filters will reduce the number of statements that a chat bot has to process when it is selecting a response. 7 | 8 | Setting filters 9 | =============== 10 | 11 | .. code-block:: python 12 | 13 | chatbot = ChatBot( 14 | "My ChatterBot", 15 | filters=[filters.get_recent_repeated_responses] 16 | ) 17 | 18 | .. automodule:: chatterbot.filters 19 | :members: 20 | -------------------------------------------------------------------------------- /docs/glossary.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Glossary 3 | ======== 4 | 5 | .. glossary:: 6 | 7 | adapters 8 | A base class that allows a ChatBot instance to execute some kind of functionality. 9 | 10 | chat bot 11 | Computer software that can converse conversation with human users or other chat bots [1]_. 12 | 13 | logic adapter 14 | An adapter class that allows a ChatBot instance to select a response to 15 | 16 | RAG 17 | Retrieval-Augmented Generation. A method of by which a large language model 18 | can retrieve information from a database or other source of information. 19 | 20 | storage adapter 21 | A class that allows a chat bot to store information somewhere, such as a database. 22 | 23 | corpus 24 | In linguistics, a corpus (plural corpora) or text corpus is a large 25 | and structured set of texts. They are used to do statistical analysis 26 | and hypothesis testing, checking occurrences or validating linguistic 27 | rules within a specific language territory [2]_. 28 | 29 | large language models 30 | A type of artificial intelligence model that can generate generate 31 | human-like text, often trained on a significantly large corpus of 32 | text data. 33 | 34 | MCP 35 | Model Context Protocol. A protocol for providing context data to a large 36 | language model to enable or improve its ability to perform various tasks. 37 | 38 | preprocessors 39 | A member of a list of functions that can be used to modify text 40 | input that the chat bot receives before the text is passed to 41 | the logic adapter for processing. 42 | 43 | statement 44 | A single string of text representing something that can be said. 45 | 46 | search word 47 | A word that is not a stop word and has been trimmed in some way ( 48 | for example through stemming). 49 | 50 | stemming 51 | A process through which a word is reduced into a derivative form. 52 | 53 | stop word 54 | A common word that is often filtered out during the process of 55 | analyzing text. 56 | 57 | response 58 | A single string of text that is uttered as an answer, a reply or 59 | an acknowledgement to a statement. 60 | 61 | untrained instance 62 | An untrained instance of the chat bot has an empty database. 63 | 64 | vector 65 | A mathematical representation of text that can be used to calculate 66 | the similarity between two pieces of text based on the distance 67 | between their vectors. 68 | 69 | vector database 70 | Any database capable of storing vectors. 71 | 72 | ---- 73 | 74 | .. [1] https://en.wikipedia.org/wiki/Chatbot 75 | .. [2] https://en.wikipedia.org/wiki/Text_corpus 76 | -------------------------------------------------------------------------------- /docs/packaging.rst: -------------------------------------------------------------------------------- 1 | ================================== 2 | Packaging your code for ChatterBot 3 | ================================== 4 | 5 | There are cases where developers may want to contribute code to ChatterBot but for 6 | various reasons it doesn't make sense or isn't possible to add the code to the 7 | main ChatterBot repository on GitHub. 8 | 9 | Common reasons that code can't be contributed include: 10 | 11 | - Licensing: It may not be possible to contribute code to ChatterBot due to a licensing restriction or a copyright. 12 | - Demand: There needs to be a general demand from the open source community for a particular feature so that there are developers who will want to fix and improve the feature if it requires maintenance. 13 | 14 | In addition, all code should be well documented and thoroughly tested. 15 | 16 | Package directory structure 17 | --------------------------- 18 | 19 | Suppose we want to create a new logic adapter for ChatterBot and add it the 20 | Python Package Index (PyPI) so that other developers can install it and use it. 21 | We would begin doing this by setting up a directory file the following structure. 22 | 23 | .. literalinclude:: _includes/python_module_structure.txt 24 | :caption: Python Module Structure 25 | 26 | More information on creating Python packages can be found here: 27 | https://packaging.python.org/tutorials/distributing-packages/ 28 | 29 | Register on PyPI 30 | ================ 31 | 32 | Create an account: https://pypi.python.org/pypi?%3Aaction=register_form 33 | 34 | Create a ``.pypirc`` configuration file. 35 | 36 | .. code-block:: bash 37 | :caption: .pypirc file contents 38 | 39 | [distutils] 40 | index-servers = 41 | pypi 42 | 43 | [pypi] 44 | username=my_username 45 | password=my_password 46 | 47 | Generate packages 48 | ================= 49 | 50 | .. code-block:: bash 51 | 52 | python -m build 53 | 54 | Upload packages 55 | =============== 56 | 57 | The official tool for uploading Python packages is called twine. 58 | You can install twine with pip if you don't already have it installed. 59 | 60 | .. code-block:: bash 61 | 62 | pip install twine 63 | 64 | .. code-block:: bash 65 | 66 | twine upload dist/* 67 | 68 | Install your package locally 69 | ============================ 70 | 71 | .. code-block:: bash 72 | 73 | cd IronyAdapter 74 | pip install . --upgrade 75 | 76 | Using your package 77 | ================== 78 | 79 | If you are creating a module that ChatterBot imports from a dotted module path then you 80 | can set the following in your chat bot. 81 | 82 | .. code-block:: python 83 | 84 | chatbot = ChatBot( 85 | "My ChatBot", 86 | logic_adapters=[ 87 | "irony_adapter.logic.IronyAdapter" 88 | ] 89 | ) 90 | 91 | Testing your code 92 | ================= 93 | 94 | .. code-block:: python 95 | 96 | from unittest import TestCase 97 | 98 | 99 | class IronyAdapterTestCase(TestCase): 100 | """ 101 | Test that the irony adapter allows 102 | the chat bot to understand irony. 103 | """ 104 | 105 | def test_irony(self): 106 | # TODO: Implement test logic 107 | self.assertTrue(response.irony) -------------------------------------------------------------------------------- /docs/preprocessors.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Preprocessors 3 | ============= 4 | 5 | ChatterBot's :term:`preprocessors` are simple functions that modify the input statement 6 | that a chat bot receives before the statement gets processed by the logic adaper. 7 | 8 | Here is an example of how to set preprocessors. The ``preprocessors`` 9 | parameter should be a list of strings of the import paths to your preprocessors. 10 | 11 | .. code-block:: python 12 | 13 | chatbot = ChatBot( 14 | 'Bob the Bot', 15 | preprocessors=[ 16 | 'chatterbot.preprocessors.clean_whitespace' 17 | ] 18 | ) 19 | 20 | Preprocessor functions 21 | ====================== 22 | 23 | ChatterBot comes with several built-in preprocessors. 24 | 25 | .. autofunction:: chatterbot.preprocessors.clean_whitespace 26 | 27 | .. autofunction:: chatterbot.preprocessors.unescape_html 28 | 29 | .. autofunction:: chatterbot.preprocessors.convert_to_ascii 30 | 31 | 32 | Creating new preprocessors 33 | ========================== 34 | 35 | It is simple to create your own preprocessors. A preprocessor is just a function 36 | with a few requirements. 37 | 38 | 1. It must take one parameter, a ``Statement`` instance. 39 | 2. It must return a statement instance. 40 | -------------------------------------------------------------------------------- /docs/quickstart.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | Quick Start Guide 3 | ================= 4 | 5 | The first thing you'll need to do to get started is install ChatterBot. 6 | 7 | .. code-block:: bash 8 | 9 | pip install chatterbot 10 | 11 | See :ref:`Installation` for options for alternative installation methods. 12 | 13 | Create a new chat bot 14 | ===================== 15 | 16 | .. code-block:: python 17 | 18 | from chatterbot import ChatBot 19 | chatbot = ChatBot("Ron Obvious") 20 | 21 | .. note:: 22 | 23 | The only required parameter for the `ChatBot` is a name. 24 | This can be anything you want. 25 | 26 | Training your ChatBot 27 | ===================== 28 | 29 | After creating a new ChatterBot instance it is also possible to train the bot. 30 | Training is a good way to ensure that the bot starts off with knowledge about 31 | specific responses. The current training method takes a list of statements that 32 | represent a conversation. 33 | Additional notes on training can be found in the :ref:`Training` documentation. 34 | 35 | .. note:: 36 | 37 | Training is not required but it is recommended. 38 | 39 | .. code-block:: python 40 | 41 | from chatterbot.trainers import ListTrainer 42 | 43 | conversation = [ 44 | "Hello", 45 | "Hi there!", 46 | "How are you doing?", 47 | "I'm doing great.", 48 | "That is good to hear", 49 | "Thank you.", 50 | "You're welcome." 51 | ] 52 | 53 | trainer = ListTrainer(chatbot) 54 | 55 | trainer.train(conversation) 56 | 57 | Get a response 58 | ============== 59 | 60 | .. code-block:: python 61 | 62 | response = chatbot.get_response("Good morning!") 63 | print(response) 64 | -------------------------------------------------------------------------------- /docs/releases.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Releasing ChatterBot 3 | ==================== 4 | 5 | ChatterBot follows the following rules when it comes to new versions and updates. 6 | 7 | Versioning 8 | ========== 9 | 10 | ChatterBot follows semantic versioning as a set of guidelines for release versions. 11 | 12 | - **Major** releases (2.0.0, 3.0.0, etc.) are used for large, almost 13 | entirely backwards incompatible changes. 14 | 15 | - **Minor** releases (2.1.0, 2.2.0, 3.1.0, 3.2.0, etc.) are used for 16 | releases that contain small, backwards incompatible changes. Known 17 | backwards incompatibilities will be described in the release notes. 18 | 19 | - **Patch** releases (e.g., 2.1.1, 2.1.2, 3.0.1, 3.0.10, etc.) are used 20 | for releases that contain bug fixes, features and dependency changes. 21 | 22 | 23 | Release Process 24 | =============== 25 | 26 | The following procedure is used to finalize a new version of ChatterBot. 27 | 28 | 1. We make sure that all CI tests on the master branch are passing. 29 | 30 | 2. We tag the release on GitHub. 31 | 32 | 3. A new package is generated from the latest version of the master branch. 33 | 34 | .. code-block:: bash 35 | 36 | python -m build 37 | 38 | 4. The Python package files are uploaded to PyPi. 39 | 40 | .. code-block:: bash 41 | 42 | twine upload dist/* 43 | -------------------------------------------------------------------------------- /docs/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | 4 | Sitemap: https:docs.chatterbot.us/sitemap.xml 5 | -------------------------------------------------------------------------------- /docs/setup.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | The recommended method for installing ChatterBot is by using `pip`_. 6 | 7 | Installing from PyPi 8 | -------------------- 9 | 10 | If you are just getting started with ChatterBot, it is recommended that you 11 | start by installing the latest version from the Python Package Index (`PyPi`_). 12 | To install ChatterBot from PyPi using pip run the following command in your terminal. 13 | 14 | .. code-block:: bash 15 | 16 | pip install chatterbot 17 | 18 | 19 | Optional dependencies 20 | --------------------- 21 | 22 | ChatterBot offers two collections of optional dependencies: ``dev`` and ``test``. Neither of these are required for all ChatterBot use cases, but both provide full support for additional features. The ``dev`` collection includes dependencies such as ``pymongo``, and ``pint`` (which are useful for working on various changes during development but are not required to use all chatterbot features). Separately the ``test`` collection includes dependencies such as ``flake8``, and ``coverage``. The specifics of each collection of optional dependencies can be reviewed via the project's `pyproject.yml`_ file. To install these optional dependencies, you can use the following commands. 23 | 24 | .. code-block:: bash 25 | 26 | pip install chatterbot[dev] 27 | 28 | 29 | .. code-block:: bash 30 | 31 | pip install chatterbot[test] 32 | 33 | 34 | .. code-block:: bash 35 | 36 | pip install chatterbot[dev,test] 37 | 38 | 39 | Similarly, if you have `cloned the repository <#installing-from-source>`_ and want to install the optional dependencies, you can run commands in the following format: 40 | 41 | .. code-block:: bash 42 | 43 | pip install .[dev,test] 44 | 45 | 46 | Installing from GitHub 47 | ---------------------- 48 | 49 | You can install the latest **development** version of ChatterBot directly from GitHub using ``pip``. 50 | 51 | .. code-block:: bash 52 | 53 | pip install git+git://github.com/gunthercox/ChatterBot.git@master 54 | 55 | 56 | Installing from source 57 | ---------------------- 58 | 59 | 1. Download a copy of the code from GitHub. You may need to install `git`_. 60 | 61 | .. code-block:: bash 62 | 63 | git clone https://github.com/gunthercox/ChatterBot.git 64 | 65 | 2. Install the code you have just downloaded using pip 66 | 67 | .. code-block:: bash 68 | 69 | pip install ./ChatterBot 70 | 71 | 72 | Checking the version of ChatterBot that you have installed 73 | ========================================================== 74 | 75 | If you already have ChatterBot installed and you want to check what version you 76 | have installed you can run the following command. 77 | 78 | .. code-block:: bash 79 | 80 | python -m chatterbot --version 81 | 82 | Upgrading ChatterBot to the latest version 83 | ========================================== 84 | 85 | .. toctree:: 86 | :maxdepth: 4 87 | 88 | upgrading 89 | 90 | .. _git: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git 91 | .. _pip: https://pip.pypa.io/en/stable/installing/ 92 | .. _PyPi: https://pypi.python.org/pypi 93 | .. _pyproject.yml: https://github.com/gunthercox/ChatterBot/blob/master/pyproject.toml -------------------------------------------------------------------------------- /docs/statements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/docs/statements.txt -------------------------------------------------------------------------------- /docs/storage/create-a-storage-adapter.rst: -------------------------------------------------------------------------------- 1 | Creating a new storage adapter 2 | ============================== 3 | 4 | You can write your own storage adapters by creating a new class that 5 | inherits from ``StorageAdapter`` and overrides necessary 6 | methods established in the base ``StorageAdapter`` class. 7 | 8 | You will then need to implement the interface established by the ``StorageAdapter`` class. 9 | 10 | .. literalinclude:: ../../chatterbot/storage/storage_adapter.py 11 | :language: python 12 | -------------------------------------------------------------------------------- /docs/storage/index.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | Storage Adapters 3 | ================ 4 | 5 | Storage adapters provide an interface that allows ChatterBot 6 | to connect to different storage technologies. 7 | 8 | The storage adapter that your bot uses can be specified by setting 9 | the ``storage_adapter`` parameter to the import path of the 10 | storage adapter you want to use. 11 | 12 | .. code-block:: python 13 | 14 | chatbot = ChatBot( 15 | "My ChatterBot", 16 | storage_adapter="chatterbot.storage.SQLStorageAdapter" 17 | ) 18 | 19 | Built-in Storage Adapters 20 | ========================= 21 | 22 | .. toctree:: 23 | :maxdepth: 2 24 | 25 | redis 26 | mongodb 27 | sql 28 | ../django/index 29 | 30 | Common storage adapter attributes 31 | ================================= 32 | 33 | Each storage adapter inherits the following attributes and methods. 34 | 35 | .. autoclass:: chatterbot.storage.StorageAdapter 36 | :members: 37 | 38 | Database Migrations 39 | =================== 40 | 41 | Various frameworks such as Django and SQL Alchemy support 42 | functionality that allows revisions to be made to databases 43 | programmatically. This makes it possible for updates and 44 | revisions to structures in the database to be be applied 45 | in consecutive version releases. 46 | 47 | The following explains the included migration process for 48 | each of the databases that ChatterBot comes with support for. 49 | 50 | * Django: Full schema migrations and data migrations will 51 | be included with each release. 52 | * SQL Alchemy: No migrations are currently provided in 53 | releases. If you require migrations between versions 54 | `Alembic`_ is the recommended solution for generating them. 55 | * MongoDB: No migrations are provided. 56 | * Redis: No migrations are provided. 57 | 58 | Further Reading 59 | =============== 60 | 61 | .. toctree:: 62 | :maxdepth: 2 63 | 64 | text-search 65 | create-a-storage-adapter 66 | 67 | .. _Alembic: https://alembic.sqlalchemy.org 68 | -------------------------------------------------------------------------------- /docs/storage/mongodb.rst: -------------------------------------------------------------------------------- 1 | MongoDB Storage Adapter 2 | ======================= 3 | 4 | .. image:: /_static/MongoDB_Fores-Green.svg 5 | :alt: MongoDB Logo 6 | :align: center 7 | .. 8 | Imaged used in accordance with the MongoDB Trademark Usage Guidelines 9 | https://www.mongodb.com/legal/trademark-usage-guidelines 10 | 11 | ChatterBot includes support for integration with MongoDB databases via its ``MongoDatabaseAdapter`` class. 12 | 13 | Before you can use this storage adapter you will need to install `pymongo`_. An easy way to install it is to use the ``chatterbot[mongodb]`` extra when installing ChatterBot. For example: 14 | 15 | .. code-block:: bash 16 | 17 | pip install chatterbot[mongodb] 18 | 19 | You'll also need to have a MongoDB server running. An easy way to run one locally is to use Docker: 20 | 21 | .. code-block:: yaml 22 | :caption: docker-compose.yml 23 | 24 | services: 25 | mongo: 26 | # Use the latest stable version of the mongo image 27 | image: mongo:8.0 28 | # Expose the default MongoDB port 29 | ports: 30 | - "27017:27017" 31 | # Persist the MongoDB data 32 | volumes: 33 | - ./.database/mongodb/db:/data/db 34 | 35 | To start the MongoDB container, run: 36 | 37 | .. code-block:: bash 38 | 39 | docker compose up -d 40 | 41 | .. note:: 42 | 43 | For more information on Docker and ``docker compose``, see the `Docker Compose documentation`_. 44 | 45 | MongoDB Adapter Class Attributes 46 | -------------------------------- 47 | 48 | .. autoclass:: chatterbot.storage.MongoDatabaseAdapter 49 | :members: 50 | 51 | .. _pymongo: https://pypi.org/project/pymongo/ 52 | .. _Docker Compose documentation: https://docs.docker.com/compose/ 53 | -------------------------------------------------------------------------------- /docs/storage/sql.rst: -------------------------------------------------------------------------------- 1 | SQL Storage Adapter 2 | =================== 3 | 4 | .. autoclass:: chatterbot.storage.SQLStorageAdapter 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/storage/text-search.rst: -------------------------------------------------------------------------------- 1 | =========== 2 | Text Search 3 | =========== 4 | 5 | ChatterBot's storage adapters support text search functionality. 6 | 7 | Text Search Example 8 | =================== 9 | 10 | .. literalinclude:: ../../tests/test_chatbot.py 11 | :language: python 12 | :pyobject: ChatterBotResponseTestCase.test_search_text_results_after_training 13 | 14 | Bigram Text Index 15 | ================= 16 | 17 | Bigram pairs are used for text search 18 | 19 | In addition, the generation of the pairs ensures that there is a smaller number 20 | of possible matches based on the probability of finding two neighboring words 21 | in an existing string that match the search parameter. 22 | 23 | For searches in larger data sets, the bigrams also reduce the number of ``OR`` 24 | comparisons that need to occur on a database level. This will always be a 25 | reduction of ``n - 1`` where ``n`` is the number of search words. 26 | 27 | .. image:: ../_static/bigrams.svg 28 | :alt: ChatterBot bigram generation process 29 | -------------------------------------------------------------------------------- /docs/testing.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Unit Testing 3 | ============ 4 | 5 | *"A true professional does not waste the time and money of other people by handing over software that is not reasonably free of obvious bugs; 6 | that has not undergone minimal unit testing; that does not meet the specifications and requirements; 7 | that is gold-plated with unnecessary features; or that looks like junk."* – Daniel Read 8 | 9 | Running tests 10 | ------------- 11 | 12 | You can run ChatterBot's main test suite using Python's built-in test runner. For example: 13 | 14 | .. sourcecode:: sh 15 | 16 | python -m unittest discover -s tests -v 17 | 18 | *Note* that the ``unittest`` command also allows you to specify individual test cases to run. 19 | For example, the following command will run all tests in the test-module `tests/logic/` 20 | 21 | .. sourcecode:: sh 22 | 23 | python -m unittest discover -s tests/logic/ -v 24 | 25 | To run a specific test in a test class you can specify the test method name using the following pattern: 26 | 27 | .. sourcecode:: sh 28 | 29 | python -m unittest tests.logic.test_best_match.BestMatchTestCase.test_match_with_response 30 | 31 | Tests can also be run in "fail fast" mode, in which case they will run until the first test failure is encountered. 32 | 33 | .. sourcecode:: sh 34 | 35 | python -m unittest discover -f tests 36 | 37 | For more information on ``unittest`` functionality, see the `unittest documentation`_. 38 | 39 | Django integration tests 40 | ------------------------ 41 | 42 | Tests for Django integration have been included in the `tests_django` directory and 43 | can be run with: 44 | 45 | .. sourcecode:: sh 46 | 47 | python runtests.py 48 | 49 | Django example app tests 50 | ------------------------ 51 | 52 | Tests for the example Django app can be run with the following command from within the `examples/django_example` directory. 53 | 54 | .. sourcecode:: sh 55 | 56 | python manage.py test 57 | 58 | Benchmark tests 59 | --------------- 60 | 61 | You can run a series of benchmark tests that test a variety of different chat bot configurations for 62 | performance by running the following command. 63 | 64 | .. sourcecode:: sh 65 | 66 | python tests/benchmarks.py 67 | 68 | 69 | Testing documentation builds 70 | ---------------------------- 71 | 72 | The HTML documentation for ChatterBot can be compiled using using `Sphinx`_. To build it run the following command from the root directory of the project: 73 | 74 | .. sourcecode:: sh 75 | 76 | sphinx-build -nW -b dirhtml docs/ html/ 77 | 78 | 79 | .. _Sphinx: http://www.sphinx-doc.org/ 80 | .. _unittest documentation: https://docs.python.org/3/library/unittest.html#command-line-interface 81 | -------------------------------------------------------------------------------- /docs/upgrading.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Upgrading to Newer Releases 3 | =========================== 4 | 5 | Like any software, changes will be made to ChatterBot over time. 6 | Most of these changes are improvements. Frequently, you don't have 7 | to change anything in your code to benefit from a new release. 8 | 9 | Occasionally there are changes that will require modifications in 10 | your code or there will be changes that make it possible for you 11 | to improve your code by taking advantage of new features. 12 | 13 | To view a record of ChatterBot's history of changes, visit the 14 | releases tab on ChatterBot's GitHub page. 15 | 16 | - https://github.com/gunthercox/ChatterBot/releases 17 | 18 | Use the pip command to upgrade your existing ChatterBot 19 | installation by providing the --upgrade parameter: 20 | 21 | .. code-block:: bash 22 | 23 | pip install chatterbot --upgrade 24 | 25 | Also see :ref:`Versioning` for information about ChatterBot's versioning policy. 26 | -------------------------------------------------------------------------------- /docs/utils.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | Utility Methods 3 | =============== 4 | 5 | ChatterBot has a utility module that contains 6 | a collection of miscellaneous but useful functions. 7 | 8 | 9 | Module imports 10 | -------------- 11 | 12 | .. autofunction:: chatterbot.utils.import_module 13 | 14 | 15 | Class initialization 16 | -------------------- 17 | 18 | .. autofunction:: chatterbot.utils.initialize_class 19 | 20 | 21 | ChatBot response time 22 | --------------------- 23 | 24 | .. autofunction:: chatterbot.utils.get_response_time 25 | 26 | 27 | Parsing datetime information 28 | ---------------------------- 29 | 30 | .. autofunction:: chatterbot.parsing.datetime_parsing 31 | -------------------------------------------------------------------------------- /examples/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/examples/__init__.py -------------------------------------------------------------------------------- /examples/basic_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.trainers import ListTrainer 3 | 4 | # Create a new chat bot named Charlie 5 | chatbot = ChatBot('Charlie') 6 | 7 | trainer = ListTrainer(chatbot) 8 | 9 | trainer.train([ 10 | "Hi, can I help you?", 11 | "Sure, I'd like to book a flight to Iceland.", 12 | "Your flight has been booked." 13 | ]) 14 | 15 | # Get a response to the input text 'I would like to book a flight.' 16 | response = chatbot.get_response('I would like to book a flight.') 17 | 18 | print(response) 19 | -------------------------------------------------------------------------------- /examples/convert_units.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | 4 | bot = ChatBot( 5 | 'Unit Converter', 6 | logic_adapters=[ 7 | 'chatterbot.logic.UnitConversion', 8 | ] 9 | ) 10 | 11 | questions = [ 12 | 'How many meters are in a kilometer?', 13 | 'How many meters are in one inch?', 14 | '0 celsius to fahrenheit', 15 | 'one hour is how many minutes ?' 16 | ] 17 | 18 | # Prints the convertion given the specific question 19 | for question in questions: 20 | response = bot.get_response(question) 21 | print(question + ' - Response: ' + response.text) 22 | -------------------------------------------------------------------------------- /examples/default_response_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.trainers import ListTrainer 3 | 4 | 5 | # Create a new instance of a ChatBot 6 | bot = ChatBot( 7 | 'Example Bot', 8 | storage_adapter='chatterbot.storage.SQLStorageAdapter', 9 | logic_adapters=[ 10 | { 11 | 'import_path': 'chatterbot.logic.BestMatch', 12 | 'default_response': 'I am sorry, but I do not understand.', 13 | 'maximum_similarity_threshold': 0.90 14 | } 15 | ] 16 | ) 17 | 18 | trainer = ListTrainer(bot) 19 | 20 | # Train the chat bot with a few responses 21 | trainer.train([ 22 | 'How can I help you?', 23 | 'I want to create a chat bot', 24 | 'Have you read the documentation?', 25 | 'No, I have not', 26 | 'This should help get you started: https://docs.chatterbot.us/quickstart/' 27 | ]) 28 | 29 | # Get a response for some unexpected input 30 | response = bot.get_response('How do I make an omelette?') 31 | print(response) 32 | -------------------------------------------------------------------------------- /examples/django_example/README.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | ChatterBot Django Example 3 | ========================= 4 | 5 | This is an example Django app that shows how to create a simple chat bot web 6 | app using Django_ and ChatterBot_. 7 | 8 | Quick Start 9 | ----------- 10 | 11 | To run this example you will need to have Django and ChatterBot installed. The `requirements.txt` file contains the recommended versions of these packages for this example project. 12 | 13 | ```bash 14 | pip install -r requirements.txt 15 | ``` 16 | 17 | Run the Django migrations to populate ChatterBot database tables: 18 | 19 | ```bash 20 | python manage.py migrate 21 | ``` 22 | 23 | Start the Django app by running the following: 24 | 25 | ```bash 26 | python manage.py runserver 0.0.0.0:8000 27 | ``` 28 | 29 | Documentation 30 | ------------- 31 | 32 | Further documentation on getting set up with Django and ChatterBot can be 33 | found in the `ChatterBot documentation`_. 34 | 35 | .. _Django: https://www.djangoproject.com 36 | .. _ChatterBot: https://github.com/gunthercox/ChatterBot 37 | .. _ChatterBot documentation: https://docs.chatterbot.us/django/ 38 | -------------------------------------------------------------------------------- /examples/django_example/django_example/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/examples/django_example/django_example/__init__.py -------------------------------------------------------------------------------- /examples/django_example/django_example/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for django_example project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_example.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /examples/django_example/django_example/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/examples/django_example/django_example/management/__init__.py -------------------------------------------------------------------------------- /examples/django_example/django_example/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/examples/django_example/django_example/management/commands/__init__.py -------------------------------------------------------------------------------- /examples/django_example/django_example/management/commands/train.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is an example of a custom Django management command that 3 | trains a ChatterBot instance with specified data. 4 | 5 | For more information on how to create custom management commands, 6 | see the Django documentation: 7 | https://docs.djangoproject.com/en/4.2/howto/custom-management-commands/ 8 | 9 | For details on the available training options for ChatterBot see: 10 | http://docs.chatterbot.us/training/ 11 | """ 12 | 13 | from django.core.management.base import BaseCommand 14 | from django.conf import settings 15 | 16 | from chatterbot import ChatBot 17 | from chatterbot.trainers import ListTrainer 18 | 19 | 20 | class Command(BaseCommand): 21 | help = 'Train a ChatterBot instance with specified data.' 22 | 23 | def handle(self, *args, **options): 24 | chatbot = ChatBot(**settings.CHATTERBOT) 25 | 26 | trainer = ListTrainer(chatbot) 27 | 28 | trainer.train([ 29 | 'Hello, how are you?', 30 | 'I am good.', 31 | 'That is good to hear.', 32 | 'I am glad to hear that.', 33 | 'Thank you.', 34 | 'You are welcome.', 35 | ]) 36 | 37 | self.stdout.write( 38 | self.style.SUCCESS('Training completed successfully') 39 | ) 40 | -------------------------------------------------------------------------------- /examples/django_example/django_example/static/css/custom.css: -------------------------------------------------------------------------------- 1 | /* Alternate the background color of the output rows */ 2 | .list-group-item:nth-child(even) { 3 | background-color: #bdf1ac; 4 | } 5 | 6 | .chat-log { 7 | max-height:200px; 8 | overflow-y:scroll; 9 | } -------------------------------------------------------------------------------- /examples/django_example/django_example/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/examples/django_example/django_example/static/favicon.ico -------------------------------------------------------------------------------- /examples/django_example/django_example/static/img/chatterbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/examples/django_example/django_example/static/img/chatterbot.png -------------------------------------------------------------------------------- /examples/django_example/django_example/static/js/js.cookie.js: -------------------------------------------------------------------------------- 1 | /*! js-cookie v2.0.3 | MIT */ 2 | !function(a){if("function"==typeof define&&define.amd)define(a);else if("object"==typeof exports)module.exports=a();else{var b=window.Cookies,c=window.Cookies=a(window.jQuery);c.noConflict=function(){return window.Cookies=b,c}}}(function(){function a(){for(var a=0,b={};a1){if(f=a({path:"/"},d.defaults,f),"number"==typeof f.expires){var h=new Date;h.setMilliseconds(h.getMilliseconds()+864e5*f.expires),f.expires=h}try{g=JSON.stringify(e),/^[\{\[]/.test(g)&&(e=g)}catch(i){}return e=encodeURIComponent(String(e)),e=e.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),b=encodeURIComponent(String(b)),b=b.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),b=b.replace(/[\(\)]/g,escape),document.cookie=[b,"=",e,f.expires&&"; expires="+f.expires.toUTCString(),f.path&&"; path="+f.path,f.domain&&"; domain="+f.domain,f.secure?"; secure":""].join("")}b||(g={});for(var j=document.cookie?document.cookie.split("; "):[],k=/(%[0-9A-Z]{2})+/g,l=0;l 4 | 5 | 6 | ChatterBot 7 | ChatterBot 8 | 9 | 12 | 13 | 32 | -------------------------------------------------------------------------------- /examples/django_example/django_example/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/examples/django_example/django_example/tests/__init__.py -------------------------------------------------------------------------------- /examples/django_example/django_example/tests/test_example.py: -------------------------------------------------------------------------------- 1 | import json 2 | from django.test import TestCase 3 | from django.urls import reverse 4 | 5 | 6 | class ViewTestCase(TestCase): 7 | 8 | def setUp(self): 9 | super().setUp() 10 | self.url = reverse('main') 11 | 12 | def test_get_main_page(self): 13 | """ 14 | Test that the main page can be loaded. 15 | """ 16 | response = self.client.get(self.url) 17 | self.assertEqual(response.status_code, 200) 18 | 19 | 20 | class ApiTestCase(TestCase): 21 | """ 22 | Tests to make sure that the ChatterBot app is 23 | properly working with the Django example app. 24 | """ 25 | 26 | def setUp(self): 27 | super().setUp() 28 | self.api_url = reverse('chatterbot') 29 | 30 | def test_post(self): 31 | """ 32 | Test that a response is returned. 33 | """ 34 | data = { 35 | 'text': 'How are you?' 36 | } 37 | response = self.client.post( 38 | self.api_url, 39 | data=json.dumps(data), 40 | content_type='application/json', 41 | format='json' 42 | ) 43 | 44 | self.assertEqual(response.status_code, 200) 45 | self.assertIn('text', response.json()) 46 | self.assertIn('in_response_to', response.json()) 47 | 48 | def test_post_tags(self): 49 | post_data = { 50 | 'text': 'Good morning.', 51 | 'tags': [ 52 | 'user:jen@example.com' 53 | ] 54 | } 55 | response = self.client.post( 56 | self.api_url, 57 | data=json.dumps(post_data), 58 | content_type='application/json', 59 | format='json' 60 | ) 61 | 62 | self.assertEqual(response.status_code, 200) 63 | self.assertIn('text', response.json()) 64 | self.assertIn('in_response_to', response.json()) 65 | self.assertIn('tags', response.json()) 66 | self.assertEqual(response.json()['tags'], ['user:jen@example.com']) 67 | 68 | 69 | class ApiIntegrationTestCase(TestCase): 70 | """ 71 | Test to make sure the ChatterBot API view works 72 | properly with the example Django app. 73 | """ 74 | 75 | def setUp(self): 76 | super().setUp() 77 | self.api_url = reverse('chatterbot') 78 | 79 | def test_get(self): 80 | response = self.client.get(self.api_url) 81 | 82 | self.assertIn('name', response.json()) 83 | -------------------------------------------------------------------------------- /examples/django_example/django_example/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for django_example project. 3 | """ 4 | from django.contrib import admin 5 | from django.urls import path 6 | from django_example.views import ChatterBotAppView, ChatterBotApiView 7 | 8 | 9 | urlpatterns = [ 10 | path('', ChatterBotAppView.as_view(), name='main'), 11 | path('api/chatterbot/', ChatterBotApiView.as_view(), name='chatterbot'), 12 | path('admin/', admin.site.urls, name='admin'), 13 | ] 14 | -------------------------------------------------------------------------------- /examples/django_example/django_example/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | from django.views.generic.base import TemplateView 3 | from django.views.generic import View 4 | from django.http import JsonResponse 5 | from django.conf import settings 6 | 7 | from chatterbot import ChatBot 8 | 9 | 10 | class ChatterBotAppView(TemplateView): 11 | template_name = 'app.html' 12 | 13 | 14 | class ChatterBotApiView(View): 15 | """ 16 | Provide an API endpoint to interact with ChatterBot. 17 | """ 18 | 19 | chatterbot = ChatBot(**settings.CHATTERBOT) 20 | 21 | def post(self, request, *args, **kwargs): 22 | """ 23 | Return a response to the statement in the posted data. 24 | 25 | * The JSON data should contain a 'text' attribute. 26 | """ 27 | input_data = json.loads(request.body.decode('utf-8')) 28 | 29 | if 'text' not in input_data: 30 | return JsonResponse({ 31 | 'text': [ 32 | 'The attribute "text" is required.' 33 | ] 34 | }, status=400) 35 | 36 | response = self.chatterbot.get_response(**input_data) 37 | 38 | response_data = response.serialize() 39 | 40 | return JsonResponse(response_data, status=200) 41 | 42 | def get(self, request, *args, **kwargs): 43 | """ 44 | Return data corresponding to the current conversation. 45 | """ 46 | return JsonResponse({ 47 | 'name': self.chatterbot.name 48 | }) 49 | -------------------------------------------------------------------------------- /examples/django_example/django_example/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_example project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_example.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /examples/django_example/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_example.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /examples/django_example/requirements.txt: -------------------------------------------------------------------------------- 1 | django>=4.2,<4.3 2 | chatterbot>=1.2,<2.0 3 | -------------------------------------------------------------------------------- /examples/export_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.trainers import ChatterBotCorpusTrainer 3 | 4 | ''' 5 | This is an example showing how to create an export file from 6 | an existing chat bot that can then be used to train other bots. 7 | ''' 8 | 9 | chatbot = ChatBot('Export Example Bot') 10 | 11 | # First, lets train our bot with some data 12 | trainer = ChatterBotCorpusTrainer(chatbot) 13 | 14 | trainer.train('chatterbot.corpus.english') 15 | 16 | # Now we can export the data to a file 17 | trainer.export_for_training('./my_export.json') 18 | -------------------------------------------------------------------------------- /examples/learning_feedback_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.conversation import Statement 3 | 4 | """ 5 | This example shows how to create a chat bot that 6 | will learn responses based on an additional feedback 7 | element from the user. 8 | """ 9 | 10 | # Uncomment the following line to enable verbose logging 11 | # import logging 12 | # logging.basicConfig(level=logging.INFO) 13 | 14 | # Create a new instance of a ChatBot 15 | bot = ChatBot( 16 | 'Feedback Learning Bot', 17 | storage_adapter='chatterbot.storage.SQLStorageAdapter' 18 | ) 19 | 20 | 21 | def get_feedback(): 22 | 23 | text = input() 24 | 25 | if 'yes' in text.lower(): 26 | return True 27 | elif 'no' in text.lower(): 28 | return False 29 | else: 30 | print('Please type either "Yes" or "No"') 31 | return get_feedback() 32 | 33 | 34 | print('Type something to begin...') 35 | 36 | # The following loop will execute each time the user enters input 37 | while True: 38 | try: 39 | input_statement = Statement(text=input()) 40 | response = bot.generate_response( 41 | input_statement 42 | ) 43 | 44 | print('\n Is "{}" a coherent response to "{}"? \n'.format( 45 | response.text, 46 | input_statement.text 47 | )) 48 | if get_feedback() is False: 49 | print('please input the correct one') 50 | correct_response = Statement(text=input()) 51 | bot.learn_response(correct_response, input_statement) 52 | print('Responses added to bot!') 53 | 54 | # Press ctrl-c or ctrl-d on the keyboard to exit 55 | except (KeyboardInterrupt, EOFError, SystemExit): 56 | break 57 | -------------------------------------------------------------------------------- /examples/math_and_time.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | 4 | bot = ChatBot( 5 | 'Math & Time Bot', 6 | logic_adapters=[ 7 | 'chatterbot.logic.MathematicalEvaluation', 8 | 'chatterbot.logic.TimeLogicAdapter' 9 | ] 10 | ) 11 | 12 | # Print an example of getting one math based response 13 | response = bot.get_response('What is 4 + 9?') 14 | print(response) 15 | 16 | # Print an example of getting one time based response 17 | response = bot.get_response('What time is it?') 18 | print(response) 19 | -------------------------------------------------------------------------------- /examples/memory_sql_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | # Uncomment the following lines to enable verbose logging 4 | # import logging 5 | # logging.basicConfig(level=logging.INFO) 6 | 7 | # Create a new instance of a ChatBot 8 | bot = ChatBot( 9 | 'SQLMemoryTerminal', 10 | storage_adapter='chatterbot.storage.SQLStorageAdapter', 11 | database_uri=None, 12 | logic_adapters=[ 13 | 'chatterbot.logic.MathematicalEvaluation', 14 | 'chatterbot.logic.TimeLogicAdapter', 15 | 'chatterbot.logic.BestMatch' 16 | ] 17 | ) 18 | 19 | # Get a few responses from the bot 20 | 21 | bot.get_response('What time is it?') 22 | 23 | bot.get_response('What is 7 plus 7?') 24 | -------------------------------------------------------------------------------- /examples/ollama_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | EXPERIMENTAL: See https://docs.chatterbot.us/large-language-models/ for more information. 3 | Example of using the Ollama API with the Ollama Python client. 4 | """ 5 | from chatterbot import ChatBot 6 | 7 | 8 | # Create a new instance of a ChatBot 9 | bot = ChatBot( 10 | 'Ollama Example Bot', 11 | model={ 12 | 'client': 'chatterbot.llm.Ollama', 13 | 'model': 'gemma3:1b', 14 | 'host': 'http://localhost:11434' 15 | }, 16 | stream=True # Enable streaming responses 17 | ) 18 | 19 | print('Type something to begin...') 20 | 21 | # The following loop will execute each time the user enters input 22 | while True: 23 | try: 24 | user_input = input() 25 | 26 | bot_response = bot.get_response(user_input) 27 | 28 | for part in bot_response: 29 | print(part, end='', flush=True) 30 | print() 31 | 32 | # Press ctrl-c or ctrl-d on the keyboard to exit 33 | except (KeyboardInterrupt, EOFError, SystemExit): 34 | break 35 | -------------------------------------------------------------------------------- /examples/openai_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | EXPERIMENTAL: See https://docs.chatterbot.us/large-language-models/ for more information. 3 | Example of using the OpenAI API with the OpenAI Python client. 4 | """ 5 | from chatterbot import ChatBot 6 | from dotenv import load_dotenv 7 | 8 | # Load the OPENAI_API_KEY from the .env file 9 | load_dotenv('../.env') 10 | 11 | # Create a new instance of a ChatBot 12 | bot = ChatBot( 13 | 'OpenAI Example Bot', 14 | model={ 15 | 'client': 'chatterbot.llm.OpenAI', 16 | 'model': 'gpt-4o-mini', 17 | }, 18 | stream=True # Enable streaming responses 19 | ) 20 | 21 | print('Type something to begin...') 22 | 23 | # The following loop will execute each time the user enters input 24 | while True: 25 | try: 26 | user_input = input() 27 | 28 | bot_response = bot.get_response(user_input) 29 | 30 | for part in bot_response: 31 | print(part, end='', flush=True) 32 | print() 33 | 34 | # Press ctrl-c or ctrl-d on the keyboard to exit 35 | except (KeyboardInterrupt, EOFError, SystemExit): 36 | break 37 | -------------------------------------------------------------------------------- /examples/specific_response_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | 4 | # Create a new instance of a ChatBot 5 | bot = ChatBot( 6 | 'Exact Response Example Bot', 7 | storage_adapter='chatterbot.storage.SQLStorageAdapter', 8 | logic_adapters=[ 9 | { 10 | 'import_path': 'chatterbot.logic.BestMatch' 11 | }, 12 | { 13 | 'import_path': 'chatterbot.logic.SpecificResponseAdapter', 14 | 'input_text': 'Help me!', 15 | 'output_text': 'Ok, here is a link: https://docs.chatterbot.us' 16 | } 17 | ] 18 | ) 19 | 20 | # Get a response given the specific input 21 | response = bot.get_response('Help me!') 22 | print(response) 23 | -------------------------------------------------------------------------------- /examples/tagged_dataset_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.conversation import Statement 3 | 4 | 5 | chatbot = ChatBot( 6 | 'Example Bot', 7 | # This database will be a temporary in-memory database 8 | database_uri=None 9 | ) 10 | 11 | label_a_statements = [ 12 | Statement(text='Hello', tags=['label_a']), 13 | Statement(text='Hi', tags=['label_a']), 14 | Statement(text='How are you?', tags=['label_a']) 15 | ] 16 | 17 | label_b_statements = [ 18 | Statement(text='I like dogs.', tags=['label_b']), 19 | Statement(text='I like cats.', tags=['label_b']), 20 | Statement(text='I like animals.', tags=['label_b']) 21 | ] 22 | 23 | all_statements = [ 24 | *label_a_statements, 25 | *label_b_statements 26 | ] 27 | 28 | # Populate search text since we're adding statements directly to the database 29 | for statement in all_statements: 30 | statement.search_text = chatbot.tagger.get_text_index_string(statement.text) 31 | 32 | chatbot.storage.create_many(all_statements) 33 | 34 | # Return a response from "label_a_statements" 35 | response_from_label_a = chatbot.get_response( 36 | 'How are you?', 37 | additional_response_selection_parameters={ 38 | 'tags': ['label_a'] 39 | } 40 | ) 41 | 42 | # Return a response from "label_b_statements" 43 | response_from_label_b = chatbot.get_response( 44 | 'How are you?', 45 | additional_response_selection_parameters={ 46 | 'tags': ['label_b'] 47 | } 48 | ) 49 | 50 | print('Response from label_a collection:', response_from_label_a.text) 51 | print('Response from label_b collection:', response_from_label_b.text) 52 | -------------------------------------------------------------------------------- /examples/terminal_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | 4 | # Uncomment the following lines to enable verbose logging 5 | # import logging 6 | # logging.basicConfig(level=logging.INFO) 7 | 8 | # NOTE: The order of logic adapters is important 9 | # because the first logic adapter takes precedence 10 | # if a good response cannot be determined. 11 | 12 | # Create a new instance of a ChatBot 13 | bot = ChatBot( 14 | 'Terminal', 15 | storage_adapter='chatterbot.storage.SQLStorageAdapter', 16 | logic_adapters=[ 17 | 'chatterbot.logic.BestMatch', 18 | 'chatterbot.logic.TimeLogicAdapter', 19 | 'chatterbot.logic.MathematicalEvaluation' 20 | ], 21 | database_uri='sqlite:///database.sqlite3' 22 | ) 23 | 24 | print('Type something to begin...') 25 | 26 | # The following loop will execute each time the user enters input 27 | while True: 28 | try: 29 | user_input = input() 30 | 31 | bot_response = bot.get_response(user_input) 32 | 33 | print(bot_response) 34 | 35 | # Press ctrl-c or ctrl-d on the keyboard to exit 36 | except (KeyboardInterrupt, EOFError, SystemExit): 37 | break 38 | -------------------------------------------------------------------------------- /examples/terminal_mongo_example.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | 3 | # Uncomment the following lines to enable verbose logging 4 | # import logging 5 | # logging.basicConfig(level=logging.INFO) 6 | 7 | # Create a new ChatBot instance 8 | bot = ChatBot( 9 | 'Terminal', 10 | storage_adapter='chatterbot.storage.MongoDatabaseAdapter', 11 | logic_adapters=[ 12 | 'chatterbot.logic.BestMatch' 13 | ], 14 | database_uri='mongodb://localhost:27017/chatterbot-database' 15 | ) 16 | 17 | print('Type something to begin...') 18 | 19 | while True: 20 | try: 21 | user_input = input() 22 | 23 | bot_response = bot.get_response(user_input) 24 | 25 | print(bot_response) 26 | 27 | # Press ctrl-c or ctrl-d on the keyboard to exit 28 | except (KeyboardInterrupt, EOFError, SystemExit): 29 | break 30 | -------------------------------------------------------------------------------- /examples/tkinter_gui.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | import tkinter as tk 3 | try: 4 | import ttk as ttk 5 | import ScrolledText 6 | except ImportError: 7 | import tkinter.ttk as ttk 8 | import tkinter.scrolledtext as ScrolledText 9 | import time 10 | 11 | 12 | class TkinterGUIExample(tk.Tk): 13 | 14 | def __init__(self, *args, **kwargs): 15 | """ 16 | Create & set window variables. 17 | """ 18 | tk.Tk.__init__(self, *args, **kwargs) 19 | 20 | self.chatbot = ChatBot( 21 | "GUI Bot", 22 | storage_adapter="chatterbot.storage.SQLStorageAdapter", 23 | logic_adapters=[ 24 | "chatterbot.logic.BestMatch" 25 | ], 26 | database_uri="sqlite:///database.sqlite3" 27 | ) 28 | 29 | self.title("Chatterbot") 30 | 31 | self.initialize() 32 | 33 | def initialize(self): 34 | """ 35 | Set window layout. 36 | """ 37 | self.grid() 38 | 39 | self.respond = ttk.Button(self, text='Get Response', command=self.get_response) 40 | self.respond.grid(column=0, row=0, sticky='nesw', padx=3, pady=3) 41 | 42 | self.usr_input = ttk.Entry(self, state='normal') 43 | self.usr_input.grid(column=1, row=0, sticky='nesw', padx=3, pady=3) 44 | 45 | self.conversation_lbl = ttk.Label(self, anchor=tk.E, text='Conversation:') 46 | self.conversation_lbl.grid(column=0, row=1, sticky='nesw', padx=3, pady=3) 47 | 48 | self.conversation = ScrolledText.ScrolledText(self, state='disabled') 49 | self.conversation.grid(column=0, row=2, columnspan=2, sticky='nesw', padx=3, pady=3) 50 | 51 | def get_response(self): 52 | """ 53 | Get a response from the chatbot and display it. 54 | """ 55 | user_input = self.usr_input.get() 56 | self.usr_input.delete(0, tk.END) 57 | 58 | response = self.chatbot.get_response(user_input) 59 | 60 | self.conversation['state'] = 'normal' 61 | self.conversation.insert( 62 | tk.END, "Human: " + user_input + "\n" + "ChatBot: " + str(response.text) + "\n" 63 | ) 64 | self.conversation['state'] = 'disabled' 65 | 66 | time.sleep(0.5) 67 | 68 | 69 | gui_example = TkinterGUIExample() 70 | gui_example.mainloop() 71 | -------------------------------------------------------------------------------- /examples/training_example_chatterbot_corpus.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.trainers import ChatterBotCorpusTrainer 3 | import logging 4 | 5 | 6 | ''' 7 | This is an example showing how to train a chat bot using the 8 | ChatterBot Corpus of conversation dialog. 9 | ''' 10 | 11 | # Enable info level logging 12 | logging.basicConfig(level=logging.INFO) 13 | 14 | chatbot = ChatBot('Example Bot') 15 | 16 | # Start by training our bot with the ChatterBot corpus data 17 | trainer = ChatterBotCorpusTrainer(chatbot) 18 | 19 | trainer.train( 20 | 'chatterbot.corpus.english' 21 | ) 22 | 23 | # Now let's get a response to a greeting 24 | response = chatbot.get_response('How are you doing today?') 25 | print(response) 26 | -------------------------------------------------------------------------------- /examples/training_example_list_data.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.trainers import ListTrainer 3 | 4 | 5 | ''' 6 | This is an example showing how to train a chat bot using the 7 | ChatterBot ListTrainer. 8 | ''' 9 | 10 | chatbot = ChatBot('Example Bot') 11 | 12 | # Start by training our bot with the ChatterBot corpus data 13 | trainer = ListTrainer(chatbot) 14 | 15 | trainer.train([ 16 | 'Hello, how are you?', 17 | 'I am doing well.', 18 | 'That is good to hear.', 19 | 'Thank you' 20 | ]) 21 | 22 | # You can train with a second list of data to add response variations 23 | 24 | trainer.train([ 25 | 'Hello, how are you?', 26 | 'I am great.', 27 | 'That is awesome.', 28 | 'Thanks' 29 | ]) 30 | 31 | # Now let's get a response to a greeting 32 | response = chatbot.get_response('How are you doing today?') 33 | print(response) 34 | -------------------------------------------------------------------------------- /examples/training_example_ubuntu_corpus.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example shows how to train a chat bot using the 3 | Ubuntu Corpus of conversation dialog. 4 | """ 5 | import logging 6 | from chatterbot import ChatBot 7 | from chatterbot.trainers import UbuntuCorpusTrainer 8 | 9 | # Enable info level logging 10 | logging.basicConfig(level=logging.INFO) 11 | 12 | chatbot = ChatBot('Example Bot') 13 | 14 | trainer = UbuntuCorpusTrainer(chatbot) 15 | 16 | # Start by training our bot with the Ubuntu corpus data 17 | trainer.train( 18 | 'http://cs.mcgill.ca/~jpineau/datasets/ubuntu-corpus-1.0/ubuntu_dialogs.tgz', 19 | limit=100 20 | ) 21 | 22 | # Now let's get a response to a greeting 23 | response = chatbot.get_response('How are you doing today?') 24 | print(response) 25 | -------------------------------------------------------------------------------- /graphics/README.md: -------------------------------------------------------------------------------- 1 | # ChatterBot Graphics 2 | 3 | Concept art and imagery for ChatterBot was designed by [Griffin Cox](https://github.com/griffincx). 4 | 5 | Files using a `.xcf` format can be viewed and edited using [GIMP](https://www.gimp.org/). 6 | -------------------------------------------------------------------------------- /graphics/ad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/graphics/ad.png -------------------------------------------------------------------------------- /graphics/ad.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/graphics/ad.xcf -------------------------------------------------------------------------------- /graphics/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/graphics/banner.png -------------------------------------------------------------------------------- /graphics/chatterbot.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/graphics/chatterbot.xcf -------------------------------------------------------------------------------- /graphics/scan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/graphics/scan.jpg -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.setuptools] 6 | packages=[ 7 | "chatterbot", 8 | "chatterbot.storage", 9 | "chatterbot.logic", 10 | "chatterbot.ext", 11 | "chatterbot.ext.sqlalchemy_app", 12 | "chatterbot.ext.django_chatterbot", 13 | "chatterbot.ext.django_chatterbot.migrations", 14 | ] 15 | 16 | [tool.setuptools.dynamic] 17 | version = {attr = "chatterbot.__version__"} 18 | 19 | [project] 20 | name = "ChatterBot" 21 | requires-python = ">=3.9,<3.13" 22 | urls = { Documentation = "https://docs.chatterbot.us", Repository = "https://github.com/gunthercox/ChatterBot", Changelog = "https://github.com/gunthercox/ChatterBot/releases" } 23 | description = "ChatterBot is a machine learning, conversational dialog engine" 24 | authors = [ 25 | {name = "Gunther Cox"}, 26 | ] 27 | license = "BSD-3-Clause" 28 | readme = "README.md" 29 | dynamic = ["version"] 30 | keywords = [ 31 | "ChatterBot", 32 | "chatbot", 33 | "chat", 34 | "bot", 35 | "natural language processing", 36 | "nlp", 37 | "artificial intelligence", 38 | "ai" 39 | ] 40 | classifiers = [ 41 | "Development Status :: 4 - Beta", 42 | "Intended Audience :: Developers", 43 | "Operating System :: OS Independent", 44 | "Environment :: Console", 45 | "Environment :: Web Environment", 46 | "Topic :: Internet", 47 | "Topic :: Software Development :: Libraries", 48 | "Topic :: Software Development :: Libraries :: Python Modules", 49 | "Topic :: Scientific/Engineering :: Artificial Intelligence", 50 | "Topic :: Scientific/Engineering :: Human Machine Interfaces", 51 | "Topic :: Communications :: Chat", 52 | "Topic :: Text Processing", 53 | "Topic :: Text Processing :: Filters", 54 | "Topic :: Text Processing :: General", 55 | "Topic :: Text Processing :: Indexing", 56 | "Topic :: Text Processing :: Linguistic", 57 | "Programming Language :: Python", 58 | "Programming Language :: Python :: 3", 59 | "Programming Language :: Python :: 3.9", 60 | "Programming Language :: Python :: 3 :: Only", 61 | ] 62 | dependencies = [ 63 | "mathparse>=0.1,<0.2", 64 | "python-dateutil>=2.9,<2.10", 65 | "sqlalchemy>=2.0,<2.1", 66 | "spacy>=3.8,<3.9", 67 | "tqdm", 68 | ] 69 | 70 | [project.optional-dependencies] 71 | test = [ 72 | "flake8", 73 | "coverage", 74 | "sphinx>=5.3,<8.2", 75 | "sphinx-sitemap>=2.6.0", 76 | "huggingface_hub", 77 | ] 78 | dev = [ 79 | "pint>=0.8.1", 80 | "pyyaml>=6.0,<7.0", 81 | "chatterbot-corpus>=1.2.2", 82 | "ollama>=0.4.7,<1.0", 83 | "openai" 84 | ] 85 | redis = [ 86 | "redis[hiredis]", 87 | "langchain-redis", 88 | "langchain-huggingface", 89 | "accelerate", 90 | "sentence-transformers", 91 | ] 92 | mongodb = [ 93 | "pymongo>=4.11,<4.12", 94 | ] 95 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | This is the test runner for the ChatterBot's Django tests. 5 | """ 6 | 7 | import os 8 | import sys 9 | 10 | import django 11 | from django.conf import settings 12 | from django.test.utils import get_runner 13 | 14 | if __name__ == '__main__': 15 | os.environ['DJANGO_SETTINGS_MODULE'] = 'tests_django.test_settings' 16 | django.setup() 17 | TestRunner = get_runner(settings) 18 | test_runner = TestRunner( 19 | verbosity=2 20 | ) 21 | failures = test_runner.run_tests(['tests_django']) 22 | sys.exit(bool(failures)) 23 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | # H306: imports not in alphabetical order (time, os) 3 | ignore = H306 4 | max_line_length = 175 5 | exclude = .eggs, .git, build, 6 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/tests/__init__.py -------------------------------------------------------------------------------- /tests/logic/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/tests/logic/__init__.py -------------------------------------------------------------------------------- /tests/logic/test_data_cache.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.logic import LogicAdapter 3 | from chatterbot.trainers import ListTrainer 4 | 5 | 6 | class DummyMutatorLogicAdapter(LogicAdapter): 7 | """ 8 | This is a dummy class designed to modify a 9 | the resulting statement before it is returned. 10 | """ 11 | 12 | def process(self, statement, additional_response_selection_parameters=None): 13 | statement.add_tags('pos_tags:NN') 14 | statement.confidence = 1 15 | return statement 16 | 17 | 18 | class DataCachingTests(ChatBotTestCase): 19 | 20 | def setUp(self): 21 | super().setUp() 22 | 23 | self.chatbot.logic_adapters = [ 24 | DummyMutatorLogicAdapter(self.chatbot) 25 | ] 26 | 27 | self.trainer = ListTrainer( 28 | self.chatbot, 29 | show_training_progress=False 30 | ) 31 | 32 | self.trainer.train([ 33 | 'Hello', 34 | 'How are you?' 35 | ]) 36 | 37 | def test_additional_attributes_saved(self): 38 | """ 39 | Test that an additional data attribute can be added to the statement 40 | and that this attribute is saved. 41 | """ 42 | self.chatbot.get_response('Hello', conversation='test') 43 | results = list(self.chatbot.storage.filter( 44 | text='Hello', 45 | in_response_to=None, 46 | conversation='test' 47 | )) 48 | 49 | self.assertEqual(len(results), 1, msg=f'Results: {[result.serialize() for result in results]}') 50 | self.assertIn('pos_tags:NN', results[0].get_tags()) 51 | -------------------------------------------------------------------------------- /tests/logic/test_logic_adapter.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.logic import LogicAdapter 3 | from chatterbot.conversation import Statement 4 | 5 | 6 | class LogicAdapterTestCase(ChatBotTestCase): 7 | """ 8 | This test case is for the LogicAdapter base class. 9 | Although this class is not intended for direct use, 10 | this test case ensures that exceptions requiring 11 | basic functionality are triggered when needed. 12 | """ 13 | 14 | def setUp(self): 15 | super().setUp() 16 | self.adapter = LogicAdapter(self.chatbot) 17 | 18 | def test_class_name(self): 19 | """ 20 | Test that the logic adapter can return its own class name. 21 | """ 22 | self.assertEqual(self.adapter.class_name, 'LogicAdapter') 23 | 24 | def test_can_process(self): 25 | """ 26 | This method should return true by default. 27 | """ 28 | self.assertTrue(self.adapter.can_process('')) 29 | 30 | def test_process(self): 31 | with self.assertRaises(LogicAdapter.AdapterMethodNotImplementedError): 32 | self.adapter.process('') 33 | 34 | def test_get_default_response(self): 35 | response = self.adapter.get_default_response(Statement(text='...')) 36 | 37 | self.assertEqual(response.text, '...') 38 | 39 | def test_get_default_response_from_options(self): 40 | self.adapter.default_responses = [ 41 | Statement(text='The default') 42 | ] 43 | response = self.adapter.get_default_response(Statement(text='...')) 44 | 45 | self.assertEqual(response.text, 'The default') 46 | 47 | def test_get_default_response_from_database(self): 48 | self._create_with_search_text(text='The default') 49 | 50 | response = self.adapter.get_default_response(Statement(text='...')) 51 | 52 | self.assertEqual(response.text, 'The default') 53 | -------------------------------------------------------------------------------- /tests/logic/test_specific_response.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.logic import SpecificResponseAdapter 3 | from chatterbot.conversation import Statement 4 | from spacy.matcher import Matcher 5 | 6 | 7 | class SpecificResponseAdapterTestCase(ChatBotTestCase): 8 | """ 9 | Test cases for the SpecificResponseAdapter 10 | """ 11 | 12 | def setUp(self): 13 | super().setUp() 14 | self.adapter = SpecificResponseAdapter( 15 | self.chatbot, 16 | input_text='Open sesame!', 17 | output_text='Your sesame seed hamburger roll is now open.' 18 | ) 19 | 20 | def test_exact_match(self): 21 | """ 22 | Test the case that an exact match is given. 23 | """ 24 | statement = Statement(text='Open sesame!') 25 | match = self.adapter.process(statement) 26 | 27 | self.assertEqual(match.confidence, 1) 28 | self.assertEqual(match.text, 'Your sesame seed hamburger roll is now open.') 29 | 30 | def test_not_exact_match(self): 31 | """ 32 | Test the case that an exact match is not given. 33 | """ 34 | statement = Statement(text='Open says me!') 35 | match = self.adapter.process(statement) 36 | 37 | self.assertEqual(match.confidence, 0) 38 | self.assertEqual(match.text, 'Your sesame seed hamburger roll is now open.') 39 | 40 | 41 | class SpecificResponseAdapterSpacyTestCase(ChatBotTestCase): 42 | """ 43 | Tests specific response adapter with spacy. 44 | """ 45 | 46 | def setUp(self): 47 | super().setUp() 48 | 49 | pattern = [ 50 | { 51 | 'LOWER': 'open' 52 | }, 53 | { 54 | 'LOWER': 'sesame' 55 | } 56 | ] 57 | 58 | self.adapter = SpecificResponseAdapter( 59 | self.chatbot, 60 | input_text=pattern, 61 | matcher=Matcher, 62 | output_text='Your sesame seed hamburger roll is now open.', 63 | use_patterns=False 64 | ) 65 | 66 | def test_pattern_match(self): 67 | """ 68 | Test the case that a pattern match is given. 69 | """ 70 | statement = Statement(text='Open sesame!') 71 | match = self.adapter.process(statement) 72 | 73 | self.assertEqual(match.confidence, 1) 74 | self.assertEqual(match.text, 'Your sesame seed hamburger roll is now open.') 75 | 76 | 77 | class SpecificResponseAdapterFunctionResponseTestCase(ChatBotTestCase): 78 | """ 79 | Tests specific response adapter using a function that returns a response. 80 | """ 81 | 82 | def setUp(self): 83 | super().setUp() 84 | 85 | def response_function(): 86 | return 'Your sesame seed hamburger roll is now open.' 87 | 88 | self.adapter = SpecificResponseAdapter( 89 | self.chatbot, 90 | input_text='Open sesame!', 91 | output_text=response_function 92 | ) 93 | 94 | def test_function_response(self): 95 | """ 96 | Test the case that a function is given as the output_text. 97 | """ 98 | statement = Statement(text='Open sesame!') 99 | match = self.adapter.process(statement) 100 | 101 | self.assertEqual(match.confidence, 1) 102 | self.assertEqual(match.text, 'Your sesame seed hamburger roll is now open.') 103 | -------------------------------------------------------------------------------- /tests/logic/test_time.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.logic import TimeLogicAdapter 3 | from chatterbot.conversation import Statement 4 | 5 | 6 | class TimeAdapterTests(ChatBotTestCase): 7 | 8 | def setUp(self): 9 | super().setUp() 10 | self.adapter = TimeLogicAdapter(self.chatbot) 11 | 12 | def test_positive_input(self): 13 | statement = Statement(text="Do you know what time it is?") 14 | response = self.adapter.process(statement) 15 | 16 | self.assertEqual(response.confidence, 1) 17 | self.assertIn("The current time is ", response.text) 18 | 19 | def test_negative_input(self): 20 | statement = Statement(text="What is an example of a pachyderm?") 21 | response = self.adapter.process(statement) 22 | 23 | self.assertEqual(response.confidence, 0) 24 | self.assertIn("The current time is ", response.text) 25 | -------------------------------------------------------------------------------- /tests/storage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/tests/storage/__init__.py -------------------------------------------------------------------------------- /tests/storage/test_storage_adapter.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from chatterbot.storage import StorageAdapter 3 | 4 | 5 | class StorageAdapterTestCase(TestCase): 6 | """ 7 | This test case is for the StorageAdapter base class. 8 | Although this class is not intended for direct use, 9 | this test case ensures that exceptions requiring 10 | basic functionality are triggered when needed. 11 | """ 12 | 13 | @classmethod 14 | def setUpClass(cls): 15 | cls.adapter = StorageAdapter() 16 | 17 | def test_count(self): 18 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 19 | self.adapter.count() 20 | 21 | def test_filter(self): 22 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 23 | self.adapter.filter() 24 | 25 | def test_remove(self): 26 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 27 | self.adapter.remove('') 28 | 29 | def test_create(self): 30 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 31 | self.adapter.create() 32 | 33 | def test_create_many(self): 34 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 35 | self.adapter.create_many([]) 36 | 37 | def test_update(self): 38 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 39 | self.adapter.update('') 40 | 41 | def test_get_random(self): 42 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 43 | self.adapter.get_random() 44 | 45 | def test_drop(self): 46 | with self.assertRaises(StorageAdapter.AdapterMethodNotImplementedError): 47 | self.adapter.drop() 48 | 49 | def test_get_model_invalid(self): 50 | with self.assertRaises(AttributeError): 51 | self.adapter.get_model('invalid') 52 | 53 | def test_get_object_invalid(self): 54 | with self.assertRaises(AttributeError): 55 | self.adapter.get_object('invalid') 56 | -------------------------------------------------------------------------------- /tests/test_adapter_validation.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from chatterbot.adapters import Adapter 3 | from tests.base_case import ChatBotTestCase 4 | 5 | 6 | class AdapterValidationTests(ChatBotTestCase): 7 | 8 | def test_invalid_storage_adapter(self): 9 | kwargs = self.get_kwargs() 10 | kwargs['storage_adapter'] = 'chatterbot.logic.LogicAdapter' 11 | with self.assertRaises(Adapter.InvalidAdapterTypeException): 12 | self.chatbot = ChatBot('Test Bot', **kwargs) 13 | 14 | def test_valid_storage_adapter(self): 15 | kwargs = self.get_kwargs() 16 | kwargs['storage_adapter'] = 'chatterbot.storage.SQLStorageAdapter' 17 | try: 18 | self.chatbot = ChatBot('Test Bot', **kwargs) 19 | except Adapter.InvalidAdapterTypeException: 20 | self.fail('Test raised InvalidAdapterException unexpectedly!') 21 | 22 | def test_invalid_logic_adapter(self): 23 | kwargs = self.get_kwargs() 24 | kwargs['logic_adapters'] = ['chatterbot.storage.StorageAdapter'] 25 | with self.assertRaises(Adapter.InvalidAdapterTypeException): 26 | self.chatbot = ChatBot('Test Bot', **kwargs) 27 | 28 | def test_valid_logic_adapter(self): 29 | kwargs = self.get_kwargs() 30 | kwargs['logic_adapters'] = ['chatterbot.logic.BestMatch'] 31 | try: 32 | self.chatbot = ChatBot('Test Bot', **kwargs) 33 | except Adapter.InvalidAdapterTypeException: 34 | self.fail('Test raised InvalidAdapterException unexpectedly!') 35 | 36 | def test_valid_adapter_dictionary(self): 37 | kwargs = self.get_kwargs() 38 | kwargs['storage_adapter'] = { 39 | 'import_path': 'chatterbot.storage.SQLStorageAdapter' 40 | } 41 | try: 42 | self.chatbot = ChatBot('Test Bot', **kwargs) 43 | except Adapter.InvalidAdapterTypeException: 44 | self.fail('Test raised InvalidAdapterException unexpectedly!') 45 | 46 | def test_invalid_adapter_dictionary(self): 47 | kwargs = self.get_kwargs() 48 | kwargs['storage_adapter'] = { 49 | 'import_path': 'chatterbot.logic.BestMatch' 50 | } 51 | with self.assertRaises(Adapter.InvalidAdapterTypeException): 52 | self.chatbot = ChatBot('Test Bot', **kwargs) 53 | -------------------------------------------------------------------------------- /tests/test_cli.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from chatterbot import __main__ as main 3 | 4 | 5 | class CommandLineInterfaceTests(TestCase): 6 | """ 7 | Tests for the command line tools that are included with ChatterBot. 8 | """ 9 | 10 | def test_get_chatterbot_version(self): 11 | version = main.get_chatterbot_version() 12 | version_parts = version.split('.') 13 | self.assertEqual(len(version_parts), 3) 14 | self.assertTrue(version_parts[0].isdigit()) 15 | self.assertTrue(version_parts[1].isdigit()) 16 | -------------------------------------------------------------------------------- /tests/test_conversations.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from chatterbot.conversation import Statement 3 | 4 | 5 | class StatementTests(TestCase): 6 | 7 | def setUp(self): 8 | self.statement = Statement(text='A test statement.') 9 | 10 | def test_serializer(self): 11 | data = self.statement.serialize() 12 | self.assertEqual(self.statement.text, data['text']) 13 | -------------------------------------------------------------------------------- /tests/test_examples.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | 4 | class ExamplesSmokeTestCase(TestCase): 5 | """ 6 | These are just basic tests that run each example 7 | to make sure no errors are triggered. 8 | """ 9 | 10 | def test_basic_example(self): 11 | from examples import basic_example # NOQA 12 | 13 | def test_convert_units(self): 14 | from examples import convert_units # NOQA 15 | 16 | def test_default_response_example(self): 17 | from examples import default_response_example # NOQA 18 | 19 | def test_export_example(self): 20 | self.skipTest( 21 | 'This is being skipped to avoid creating files during tests.' 22 | ) 23 | 24 | def test_learning_feedback_example(self): 25 | self.skipTest( 26 | 'This is being skipped because it contains ' 27 | 'a while loop in the code body and will not ' 28 | 'terminate on its own.' 29 | ) 30 | 31 | def test_math_and_time(self): 32 | from examples import math_and_time # NOQA 33 | 34 | def test_memory_sql_example(self): 35 | from examples import memory_sql_example # NOQA 36 | 37 | def test_specific_response_example(self): 38 | from examples import specific_response_example # NOQA 39 | 40 | def test_tagged_dataset_example(self): 41 | from examples import tagged_dataset_example # NOQA 42 | 43 | def test_terminal_example(self): 44 | self.skipTest( 45 | 'This is being skipped because it contains ' 46 | 'a while loop in the code body and will not ' 47 | 'terminate on its own.' 48 | ) 49 | 50 | def test_terminal_mongo_example(self): 51 | self.skipTest( 52 | 'This is being skipped so that we do not have ' 53 | 'to check if Mongo DB is running before running ' 54 | 'this test.' 55 | ) 56 | 57 | def test_tkinter_gui(self): 58 | self.skipTest( 59 | 'This is being skipped so that we do not open up ' 60 | 'a GUI during testing.' 61 | ) 62 | 63 | def test_training_example_chatterbot_corpus(self): 64 | from examples import training_example_chatterbot_corpus # NOQA 65 | 66 | def test_training_example_list_data(self): 67 | from examples import training_example_list_data # NOQA 68 | 69 | def test_training_example_ubuntu_corpus(self): 70 | self.skipTest( 71 | 'This test is being skipped because it takes ' 72 | 'hours to download and train from this corpus.' 73 | ) 74 | -------------------------------------------------------------------------------- /tests/test_filters.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotMongoTestCase 2 | from chatterbot import filters 3 | 4 | 5 | class RepetitiveResponseFilterTestCase(ChatBotMongoTestCase): 6 | """ 7 | Test case for the repetitive response filter. 8 | """ 9 | 10 | def test_filter_selection(self): 11 | """ 12 | Test that repetitive responses are filtered out of the results. 13 | """ 14 | from chatterbot.conversation import Statement 15 | from chatterbot.trainers import ListTrainer 16 | 17 | self.chatbot.filters = (filters.get_recent_repeated_responses, ) 18 | 19 | self.trainer = ListTrainer( 20 | self.chatbot, 21 | show_training_progress=False 22 | ) 23 | 24 | self.trainer.train([ 25 | 'Hi', 26 | 'Hello', 27 | 'Hi', 28 | 'Hello', 29 | 'Hi', 30 | 'Hello', 31 | 'How are you?', 32 | 'I am good', 33 | 'Glad to hear', 34 | 'How are you?' 35 | ]) 36 | 37 | statement = Statement(text='Hello', conversation='training') 38 | first_response = self.chatbot.get_response(statement) 39 | second_response = self.chatbot.get_response(statement) 40 | 41 | self.assertEqual('How are you?', first_response.text) 42 | self.assertEqual('Hi', second_response.text) 43 | -------------------------------------------------------------------------------- /tests/test_initialization.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | 3 | 4 | class StringInitializationTestCase(ChatBotTestCase): 5 | 6 | def get_kwargs(self): 7 | return { 8 | 'storage_adapter': 'chatterbot.storage.SQLStorageAdapter', 9 | 'database_uri': None 10 | } 11 | 12 | def test_storage_initialized(self): 13 | from chatterbot.storage import SQLStorageAdapter 14 | self.assertTrue(isinstance(self.chatbot.storage, SQLStorageAdapter)) 15 | 16 | def test_logic_initialized(self): 17 | from chatterbot.logic import BestMatch 18 | self.assertEqual(len(self.chatbot.logic_adapters), 1) 19 | self.assertTrue(isinstance(self.chatbot.logic_adapters[0], BestMatch)) 20 | 21 | 22 | class DictionaryInitializationTestCase(ChatBotTestCase): 23 | 24 | def get_kwargs(self): 25 | return { 26 | 'storage_adapter': { 27 | 'import_path': 'chatterbot.storage.SQLStorageAdapter', 28 | 'database_uri': None 29 | }, 30 | 'logic_adapters': [ 31 | { 32 | 'import_path': 'chatterbot.logic.BestMatch', 33 | }, 34 | { 35 | 'import_path': 'chatterbot.logic.MathematicalEvaluation', 36 | } 37 | ] 38 | } 39 | 40 | def test_storage_initialized(self): 41 | from chatterbot.storage import SQLStorageAdapter 42 | self.assertTrue(isinstance(self.chatbot.storage, SQLStorageAdapter)) 43 | 44 | def test_logic_initialized(self): 45 | from chatterbot.logic import BestMatch 46 | from chatterbot.logic import MathematicalEvaluation 47 | self.assertEqual(len(self.chatbot.logic_adapters), 2) 48 | self.assertTrue(isinstance(self.chatbot.logic_adapters[0], BestMatch)) 49 | self.assertTrue(isinstance(self.chatbot.logic_adapters[1], MathematicalEvaluation)) 50 | -------------------------------------------------------------------------------- /tests/test_languages.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | from chatterbot import languages 3 | from unittest import TestCase 4 | 5 | 6 | class LanguageClassTests(TestCase): 7 | 8 | def test_classes_have_correct_attributes(self): 9 | language_classes = languages.get_language_classes() 10 | 11 | for name, obj in language_classes: 12 | self.assertTrue(inspect.isclass(obj)) 13 | self.assertTrue(hasattr(obj, 'ISO_639')) 14 | self.assertTrue(hasattr(obj, 'ISO_639_1')) 15 | self.assertTrue(hasattr(obj, 'ENGLISH_NAME')) 16 | self.assertEqual(name, obj.ISO_639.upper()) 17 | 18 | self.assertEqual(len(language_classes), 402) 19 | -------------------------------------------------------------------------------- /tests/test_preprocessors.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.conversation import Statement 3 | from chatterbot import preprocessors 4 | 5 | 6 | class PreprocessorIntegrationTestCase(ChatBotTestCase): 7 | """ 8 | Make sure that preprocessors work with the chat bot. 9 | """ 10 | 11 | def test_clean_whitespace(self): 12 | self.chatbot.preprocessors = [preprocessors.clean_whitespace] 13 | response = self.chatbot.get_response('Hello, how are you?') 14 | 15 | self.assertEqual(response.text, 'Hello, how are you?') 16 | 17 | 18 | class CleanWhitespacePreprocessorTestCase(ChatBotTestCase): 19 | """ 20 | Make sure that ChatterBot's whitespace removing preprocessor works as expected. 21 | """ 22 | 23 | def test_clean_whitespace(self): 24 | statement = Statement(text='\tThe quick \nbrown fox \rjumps over \vthe \alazy \fdog\\.') 25 | cleaned = preprocessors.clean_whitespace(statement) 26 | normal_text = 'The quick brown fox jumps over the \alazy dog\\.' 27 | 28 | self.assertEqual(cleaned.text, normal_text) 29 | 30 | def test_leading_or_trailing_whitespace_removed(self): 31 | statement = Statement(text=' The quick brown fox jumps over the lazy dog. ') 32 | cleaned = preprocessors.clean_whitespace(statement) 33 | normal_text = 'The quick brown fox jumps over the lazy dog.' 34 | 35 | self.assertEqual(cleaned.text, normal_text) 36 | 37 | def test_consecutive_spaces_removed(self): 38 | statement = Statement(text='The quick brown fox jumps over the lazy dog.') 39 | cleaned = preprocessors.clean_whitespace(statement) 40 | normal_text = 'The quick brown fox jumps over the lazy dog.' 41 | 42 | self.assertEqual(cleaned.text, normal_text) 43 | 44 | 45 | class HTMLUnescapePreprocessorTestCase(ChatBotTestCase): 46 | """ 47 | Make sure that ChatterBot's html unescaping preprocessor works as expected. 48 | """ 49 | 50 | def test_html_unescape(self): 51 | 52 | # implicit concatenation 53 | statement = Statement( 54 | text=( 55 | 'The quick brown fox <b>jumps</b> over' 56 | ' the lazy dog.' 57 | ) 58 | ) 59 | 60 | normal_text = ( 61 | 'The quick brown fox jumps over' 62 | ' the lazy dog.' 63 | ) 64 | 65 | cleaned = preprocessors.unescape_html(statement) 66 | 67 | self.assertEqual(cleaned.text, normal_text) 68 | 69 | 70 | class ConvertToASCIIPreprocessorTestCase(ChatBotTestCase): 71 | """ 72 | Make sure that ChatterBot's ASCII conversion preprocessor works as expected. 73 | """ 74 | 75 | def test_convert_to_ascii(self): 76 | statement = Statement(text=u'Klüft skräms inför på fédéral électoral große') 77 | cleaned = preprocessors.convert_to_ascii(statement) 78 | normal_text = 'Kluft skrams infor pa federal electoral groe' 79 | 80 | self.assertEqual(cleaned.text, normal_text) 81 | -------------------------------------------------------------------------------- /tests/test_response_selection.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotSQLTestCase 2 | from chatterbot import response_selection 3 | from chatterbot.conversation import Statement 4 | 5 | 6 | class ResponseSelectionTests(ChatBotSQLTestCase): 7 | 8 | def test_get_most_frequent_response(self): 9 | statement_list = [ 10 | Statement(text='What... is your quest?', in_response_to='Hello'), 11 | Statement(text='What... is your quest?', in_response_to='Hello'), 12 | Statement(text='This is a phone.', in_response_to='Hello'), 13 | Statement(text='This is a phone.', in_response_to='Hello'), 14 | Statement(text='This is a phone.', in_response_to='Hello'), 15 | Statement(text='This is a phone.', in_response_to='Hello'), 16 | Statement(text='A what?', in_response_to='Hello'), 17 | Statement(text='A what?', in_response_to='Hello'), 18 | Statement(text='A phone.', in_response_to='Hello') 19 | ] 20 | 21 | for statement in statement_list: 22 | self._create_with_search_text( 23 | text=statement.text, 24 | in_response_to=statement.in_response_to 25 | ) 26 | 27 | output = response_selection.get_most_frequent_response( 28 | Statement(text='Hello'), 29 | statement_list, 30 | self.chatbot.storage 31 | ) 32 | 33 | self.assertEqual('This is a phone.', output.text) 34 | 35 | def test_get_first_response(self): 36 | statement_list = [ 37 | Statement(text='What... is your quest?'), 38 | Statement(text='A what?'), 39 | Statement(text='A quest.') 40 | ] 41 | 42 | output = response_selection.get_first_response(Statement(text='Hello'), statement_list) 43 | 44 | self.assertEqual(output.text, 'What... is your quest?') 45 | 46 | def test_get_random_response(self): 47 | statement_list = [ 48 | Statement(text='This is a phone.'), 49 | Statement(text='A what?'), 50 | Statement(text='A phone.') 51 | ] 52 | 53 | output = response_selection.get_random_response(Statement(text='Hello'), statement_list) 54 | 55 | self.assertTrue(output) 56 | -------------------------------------------------------------------------------- /tests/test_turing.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase, expectedFailure 2 | 3 | 4 | class TuringTests(TestCase): 5 | 6 | def setUp(self): 7 | from chatterbot import ChatBot 8 | 9 | self.chatbot = ChatBot('Agent Jr.') 10 | 11 | @expectedFailure 12 | def test_ask_name(self): 13 | response = self.chatbot.get_response( 14 | 'What is your name?' 15 | ) 16 | self.assertIn('Agent', response.text) 17 | 18 | @expectedFailure 19 | def test_repeat_information(self): 20 | """ 21 | Test if we can detect any repeat responses from the agent. 22 | """ 23 | self.fail('Condition not met.') 24 | 25 | @expectedFailure 26 | def test_repeat_input(self): 27 | """ 28 | Test what the responses are like if we keep giving the same input. 29 | """ 30 | self.fail('Condition not met.') 31 | 32 | @expectedFailure 33 | def test_contradicting_responses(self): 34 | """ 35 | Test if we can get the agent to contradict themselves. 36 | """ 37 | self.fail('Condition not met.') 38 | 39 | @expectedFailure 40 | def test_mathematical_ability(self): 41 | """ 42 | The math questions inherently suggest that the agent 43 | should get some math problems wrong in order to seem 44 | more human. My view on this is that it is more useful 45 | to have a bot that is good at math, which could just 46 | as easily be a human. 47 | """ 48 | self.fail('Condition not met.') 49 | 50 | @expectedFailure 51 | def test_response_time(self): 52 | """ 53 | Does the agent respond in a realistic amount of time? 54 | """ 55 | self.fail('Condition not met.') 56 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from unittest import TestCase 3 | from chatterbot import utils 4 | 5 | 6 | class UtilityTests(TestCase): 7 | 8 | def test_import_module(self): 9 | datetime = utils.import_module('datetime.datetime') 10 | self.assertTrue(hasattr(datetime, 'now')) 11 | 12 | 13 | class UtilityChatBotTestCase(ChatBotTestCase): 14 | 15 | def test_get_response_time(self): 16 | """ 17 | Test that a response time is returned. 18 | """ 19 | 20 | response_time = utils.get_response_time(self.chatbot) 21 | 22 | self.assertGreater(response_time, 0) 23 | -------------------------------------------------------------------------------- /tests/training/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/tests/training/__init__.py -------------------------------------------------------------------------------- /tests/training/test_chatterbot_corpus_training.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.trainers import ChatterBotCorpusTrainer 3 | 4 | 5 | class ChatterBotCorpusTrainingTestCase(ChatBotTestCase): 6 | """ 7 | Test case for training with data from the ChatterBot Corpus. 8 | 9 | Note: This class has a mirror tests_django/integration_tests/ 10 | """ 11 | 12 | def setUp(self): 13 | super().setUp() 14 | self.trainer = ChatterBotCorpusTrainer( 15 | self.chatbot, 16 | show_training_progress=False 17 | ) 18 | 19 | def test_train_with_english_greeting_corpus(self): 20 | self.trainer.train('chatterbot.corpus.english.greetings') 21 | 22 | results = list(self.chatbot.storage.filter(text='Hello')) 23 | 24 | self.assertGreater(len(results), 1) 25 | 26 | def test_train_with_english_greeting_corpus_search_text(self): 27 | self.trainer.train('chatterbot.corpus.english.greetings') 28 | 29 | results = list(self.chatbot.storage.filter(text='Hello')) 30 | 31 | self.assertGreater(len(results), 1) 32 | self.assertEqual(results[0].search_text, 'hello') 33 | 34 | def test_train_with_english_greeting_corpus_search_in_response_to(self): 35 | self.trainer.train('chatterbot.corpus.english.greetings') 36 | 37 | results = list(self.chatbot.storage.filter(in_response_to='Hello')) 38 | 39 | self.assertGreater(len(results), 1) 40 | self.assertEqual(results[0].search_in_response_to, 'hello') 41 | 42 | def test_train_with_english_greeting_corpus_tags(self): 43 | self.trainer.train('chatterbot.corpus.english.greetings') 44 | 45 | results = list(self.chatbot.storage.filter(text='Hello')) 46 | 47 | self.assertGreater(len(results), 1) 48 | statement = results[0] 49 | self.assertEqual(['greetings'], statement.get_tags()) 50 | 51 | def test_train_with_multiple_corpora(self): 52 | self.trainer.train( 53 | 'chatterbot.corpus.english.greetings', 54 | 'chatterbot.corpus.english.conversations', 55 | ) 56 | results = list(self.chatbot.storage.filter(text='Hello')) 57 | 58 | self.assertGreater(len(results), 1) 59 | 60 | def test_train_with_english_corpus(self): 61 | self.trainer.train('chatterbot.corpus.english') 62 | results = list(self.chatbot.storage.filter(text='Hello')) 63 | 64 | self.assertGreater(len(results), 1) 65 | -------------------------------------------------------------------------------- /tests/training/test_csv_file_training.py: -------------------------------------------------------------------------------- 1 | import os 2 | from tests.base_case import ChatBotTestCase 3 | from chatterbot.trainers import CsvFileTrainer 4 | 5 | 6 | class CsvFileTrainerTestCase(ChatBotTestCase): 7 | """ 8 | Test training from CSV files. 9 | """ 10 | 11 | def setUp(self): 12 | super().setUp() 13 | 14 | current_directory = os.path.dirname(os.path.abspath(__file__)) 15 | 16 | self.data_file_path = os.path.join( 17 | current_directory, 18 | 'test_data/csv_corpus/' 19 | ) 20 | 21 | self.trainer = CsvFileTrainer( 22 | self.chatbot, 23 | show_training_progress=False, 24 | field_map={ 25 | 'created_at': 0, 26 | 'persona': 1, 27 | 'text': 2, 28 | 'conversation': 3 29 | } 30 | ) 31 | 32 | def test_train(self): 33 | """ 34 | Test that the chat bot is trained using data from the CSV files. 35 | """ 36 | self.trainer.train(self.data_file_path) 37 | 38 | response = self.chatbot.get_response('Is anyone there?') 39 | self.assertEqual(response.text, 'Yes') 40 | 41 | def test_train_sets_search_text(self): 42 | """ 43 | Test that the chat bot is trained using data from the CSV files. 44 | """ 45 | self.trainer.train(self.data_file_path) 46 | 47 | results = list(self.chatbot.storage.filter(text='Is anyone there?')) 48 | 49 | self.assertEqual(len(results), 2, msg='Results: {}'.format(results)) 50 | self.assertEqual(results[0].search_text, 'AUX:anyone PRON:there') 51 | 52 | def test_train_sets_search_in_response_to(self): 53 | """ 54 | Test that the chat bot is trained using data from the CSV files. 55 | """ 56 | self.trainer.train(self.data_file_path) 57 | 58 | results = list(self.chatbot.storage.filter(in_response_to='Is anyone there?')) 59 | 60 | self.assertEqual(len(results), 2) 61 | self.assertEqual(results[0].search_in_response_to, 'AUX:anyone PRON:there') 62 | -------------------------------------------------------------------------------- /tests/training/test_data/csv_corpus/1.csv: -------------------------------------------------------------------------------- 1 | 2004-11-04T16:49:00.000Z,tom,Hello,testing 2 | 2004-11-04T16:49:00.000Z,tom,Is anyone there?,testing 3 | 2004-11-04T16:49:00.000Z,jane,Yes,testing 4 | -------------------------------------------------------------------------------- /tests/training/test_data/csv_corpus/2.csv: -------------------------------------------------------------------------------- 1 | 2004-11-04T16:49:00.000Z,tom,Hello,testing-2 2 | 2004-11-04T16:49:00.000Z,tom,Is anyone there?,testing-2 3 | 2004-11-04T16:49:00.000Z,jane,Yes,testing-2 4 | -------------------------------------------------------------------------------- /tests/training/test_data/json_corpus/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "conversation": [ 3 | { 4 | "text": "Hello", 5 | "in_response_to": null, 6 | "persona": "user 1", 7 | "conversation": "test", 8 | "tags": [ 9 | "greeting" 10 | ] 11 | }, 12 | { 13 | "text": "Is anyone there?", 14 | "in_response_to": "Hello", 15 | "persona": "user 1", 16 | "conversation": "test", 17 | "tags": [ 18 | "question" 19 | ] 20 | }, 21 | { 22 | "text": "Yes", 23 | "in_response_to": "Is anyone there?", 24 | "persona": "user 2", 25 | "conversation": "test" 26 | } 27 | ] 28 | } -------------------------------------------------------------------------------- /tests/training/test_data/json_corpus/2.json: -------------------------------------------------------------------------------- 1 | { 2 | "conversation": [ 3 | { 4 | "text": "Hello", 5 | "in_response_to": null, 6 | "persona": "user 1", 7 | "conversation": "test-2" 8 | }, 9 | { 10 | "text": "Is anyone there?", 11 | "in_response_to": "Hello", 12 | "persona": "user 1", 13 | "conversation": "test-2" 14 | }, 15 | { 16 | "text": "Yes", 17 | "in_response_to": "Is anyone there?", 18 | "persona": "user 2", 19 | "conversation": "test-2" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /tests/training/test_json_file_training.py: -------------------------------------------------------------------------------- 1 | import os 2 | from tests.base_case import ChatBotTestCase 3 | from chatterbot.trainers import JsonFileTrainer 4 | 5 | 6 | class JsonFileTrainerTestCase(ChatBotTestCase): 7 | """ 8 | Test training from JSON files. 9 | """ 10 | 11 | def setUp(self): 12 | super().setUp() 13 | 14 | current_directory = os.path.dirname(os.path.abspath(__file__)) 15 | 16 | self.data_file_path = os.path.join( 17 | current_directory, 18 | 'test_data/json_corpus/' 19 | ) 20 | 21 | self.trainer = JsonFileTrainer( 22 | self.chatbot, 23 | show_training_progress=False, 24 | field_map={ 25 | 'persona': 'persona', 26 | 'text': 'text', 27 | 'conversation': 'conversation', 28 | 'in_response_to': 'in_response_to', 29 | } 30 | ) 31 | 32 | def test_train(self): 33 | """ 34 | Test that the chat bot is trained using data from the JSON files. 35 | """ 36 | self.trainer.train(self.data_file_path) 37 | 38 | response = self.chatbot.get_response('Is anyone there?') 39 | self.assertEqual(response.text, 'Yes') 40 | 41 | def test_train_sets_search_text(self): 42 | """ 43 | Test that the chat bot is trained using data from the JSON files. 44 | """ 45 | self.trainer.train(self.data_file_path) 46 | 47 | results = list(self.chatbot.storage.filter(text='Is anyone there?')) 48 | 49 | self.assertEqual(len(results), 2, msg='Results: {}'.format(results)) 50 | self.assertEqual(results[0].search_text, 'AUX:anyone PRON:there') 51 | 52 | def test_train_sets_search_in_response_to(self): 53 | """ 54 | Test that the chat bot is trained using data from the JSON files. 55 | """ 56 | self.trainer.train(self.data_file_path) 57 | 58 | results = list(self.chatbot.storage.filter(in_response_to='Is anyone there?')) 59 | 60 | self.assertEqual(len(results), 2) 61 | self.assertEqual(results[0].search_in_response_to, 'AUX:anyone PRON:there') 62 | -------------------------------------------------------------------------------- /tests/training/test_training.py: -------------------------------------------------------------------------------- 1 | from tests.base_case import ChatBotTestCase 2 | from chatterbot.trainers import Trainer 3 | from chatterbot.conversation import Statement 4 | 5 | 6 | class TrainingTests(ChatBotTestCase): 7 | 8 | def setUp(self): 9 | super().setUp() 10 | 11 | self.trainer = Trainer(self.chatbot) 12 | 13 | def test_trainer_not_set(self): 14 | with self.assertRaises(Trainer.TrainerInitializationException): 15 | self.trainer.train() 16 | 17 | def test_generate_export_data(self): 18 | self._create_many_with_search_text([ 19 | Statement(text='Hello, how are you?'), 20 | Statement(text='I am good.', in_response_to='Hello, how are you?') 21 | ]) 22 | data = self.trainer._generate_export_data() 23 | 24 | self.assertEqual( 25 | [['Hello, how are you?', 'I am good.']], data 26 | ) 27 | -------------------------------------------------------------------------------- /tests_django/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gunthercox/ChatterBot/1b26cd4a24848b8f1caf06b9046dfc2dee3caf98/tests_django/__init__.py -------------------------------------------------------------------------------- /tests_django/base_case.py: -------------------------------------------------------------------------------- 1 | from chatterbot import ChatBot 2 | from django.test import TransactionTestCase 3 | from tests_django import test_settings 4 | 5 | 6 | class ChatterBotTestCase(TransactionTestCase): 7 | 8 | def setUp(self): 9 | super().setUp() 10 | self.chatbot = ChatBot(**test_settings.CHATTERBOT) 11 | 12 | def _create_with_search_text(self, text, in_response_to=None, **kwargs): 13 | """ 14 | Helper function to create a statement with the search text populated. 15 | """ 16 | search_in_response_to = None 17 | 18 | if in_response_to: 19 | search_in_response_to = self.chatbot.tagger.get_text_index_string( 20 | in_response_to 21 | ) 22 | 23 | return self.chatbot.storage.create( 24 | text=text, 25 | in_response_to=in_response_to, 26 | search_text=self.chatbot.tagger.get_text_index_string(text), 27 | search_in_response_to=search_in_response_to, 28 | **kwargs 29 | ) 30 | 31 | def _create_many_with_search_text(self, statements): 32 | """ 33 | Helper function to bulk-create statements with the search text populated. 34 | """ 35 | modified_statements = [] 36 | 37 | for statement in statements: 38 | statement.search_text = self.chatbot.tagger.get_text_index_string( 39 | statement.text 40 | ) 41 | 42 | if statement.in_response_to: 43 | statement.search_in_response_to = self.chatbot.tagger.get_text_index_string( 44 | statement.in_response_to 45 | ) 46 | 47 | modified_statements.append(statement) 48 | 49 | self.chatbot.storage.create_many(statements) 50 | -------------------------------------------------------------------------------- /tests_django/test_chatterbot_corpus_training.py: -------------------------------------------------------------------------------- 1 | from tests_django.base_case import ChatterBotTestCase 2 | from chatterbot.trainers import ChatterBotCorpusTrainer 3 | 4 | 5 | class ChatterBotCorpusTrainingTestCase(ChatterBotTestCase): 6 | """ 7 | Test case for training with data from the ChatterBot Corpus. 8 | 9 | Note: This class has a mirror tests/training_tests/ 10 | """ 11 | 12 | def setUp(self): 13 | super().setUp() 14 | 15 | self.trainer = ChatterBotCorpusTrainer( 16 | self.chatbot, 17 | show_training_progress=False 18 | ) 19 | 20 | def tearDown(self): 21 | super().tearDown() 22 | self.chatbot.storage.drop() 23 | 24 | def test_train_with_english_greeting_corpus(self): 25 | self.trainer.train('chatterbot.corpus.english.greetings') 26 | 27 | results = list(self.chatbot.storage.filter(text='Hello')) 28 | 29 | self.assertGreater(len(results), 1) 30 | 31 | def test_train_with_english_greeting_corpus_tags(self): 32 | self.trainer.train('chatterbot.corpus.english.greetings') 33 | 34 | results = list(self.chatbot.storage.filter(text='Hello')) 35 | 36 | self.assertGreater(len(results), 1) 37 | statement = results[0] 38 | self.assertEqual(['greetings'], statement.get_tags()) 39 | 40 | def test_train_with_multiple_corpora(self): 41 | self.trainer.train( 42 | 'chatterbot.corpus.english.greetings', 43 | 'chatterbot.corpus.english.conversations', 44 | ) 45 | results = list(self.chatbot.storage.filter(text='Hello')) 46 | 47 | self.assertGreater(len(results), 1) 48 | 49 | def test_train_with_english_corpus(self): 50 | self.trainer.train('chatterbot.corpus.english') 51 | results = list(self.chatbot.storage.filter(text='Hello')) 52 | 53 | self.assertGreater(len(results), 1) 54 | -------------------------------------------------------------------------------- /tests_django/test_chatterbot_settings.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | from django.conf import settings 3 | 4 | 5 | class SettingsTestCase(TestCase): 6 | 7 | def test_modified_settings(self): 8 | with self.settings(CHATTERBOT={'name': 'Jim'}): 9 | self.assertIn('name', settings.CHATTERBOT) 10 | self.assertEqual('Jim', settings.CHATTERBOT['name']) 11 | 12 | def test_name_setting(self): 13 | with self.settings(): 14 | self.assertIn('name', settings.CHATTERBOT) 15 | self.assertEqual('ChatterBot', settings.CHATTERBOT['name']) 16 | -------------------------------------------------------------------------------- /tests_django/test_logic_adapter_integration.py: -------------------------------------------------------------------------------- 1 | from tests_django.base_case import ChatterBotTestCase 2 | from chatterbot.conversation import Statement 3 | 4 | 5 | class LogicIntegrationTestCase(ChatterBotTestCase): 6 | """ 7 | Tests to make sure that logic adapters 8 | function correctly when using Django. 9 | """ 10 | 11 | def setUp(self): 12 | super().setUp() 13 | 14 | self._create_with_search_text(text='Default statement') 15 | 16 | def test_best_match(self): 17 | from chatterbot.logic import BestMatch 18 | 19 | adapter = BestMatch(self.chatbot) 20 | 21 | statement1 = self._create_with_search_text( 22 | text='Do you like programming?', 23 | conversation='test' 24 | ) 25 | 26 | self._create_with_search_text( 27 | text='Yes', 28 | in_response_to=statement1.text, 29 | conversation='test' 30 | ) 31 | 32 | response = adapter.process(statement1) 33 | 34 | self.assertEqual(response.text, 'Yes') 35 | self.assertEqual(response.confidence, 1) 36 | 37 | def test_mathematical_evaluation(self): 38 | from chatterbot.logic import MathematicalEvaluation 39 | 40 | adapter = MathematicalEvaluation(self.chatbot) 41 | 42 | statement = Statement(text='What is 6 + 6?') 43 | 44 | response = adapter.process(statement) 45 | 46 | self.assertEqual(response.text, '6 + 6 = 12') 47 | self.assertEqual(response.confidence, 1) 48 | 49 | def test_time(self): 50 | from chatterbot.logic import TimeLogicAdapter 51 | 52 | adapter = TimeLogicAdapter(self.chatbot) 53 | 54 | statement = Statement(text='What time is it?') 55 | 56 | response = adapter.process(statement) 57 | 58 | self.assertIn('The current time is', response.text) 59 | self.assertEqual(response.confidence, 1) 60 | -------------------------------------------------------------------------------- /tests_django/test_settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for when tests are run. 3 | """ 4 | import os 5 | from chatterbot import constants 6 | 7 | DEBUG = True 8 | 9 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 10 | 11 | SECRET_KEY = 'fake-key' 12 | 13 | INSTALLED_APPS = [ 14 | 'django.contrib.auth', 15 | 'django.contrib.contenttypes', 16 | 'django.contrib.sessions', 17 | 'chatterbot.ext.django_chatterbot', 18 | 'tests_django', 19 | ] 20 | 21 | CHATTERBOT = { 22 | 'name': 'Test Django ChatterBot', 23 | 'logic_adapters': [ 24 | { 25 | 'import_path': 'chatterbot.logic.BestMatch', 26 | }, 27 | { 28 | 'import_path': 'chatterbot.logic.MathematicalEvaluation', 29 | } 30 | ], 31 | 'storage_adapter': 'chatterbot.storage.DjangoStorageAdapter', 32 | 'django_app_name': constants.DEFAULT_DJANGO_APP_NAME, 33 | 'initialize': False 34 | } 35 | 36 | MIDDLEWARE_CLASSES = ( 37 | 'django.contrib.sessions.middleware.SessionMiddleware', 38 | 'django.middleware.common.CommonMiddleware', 39 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 40 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 41 | 'django.contrib.messages.middleware.MessageMiddleware', 42 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 43 | 'django.middleware.security.SecurityMiddleware', 44 | ) 45 | 46 | DATABASES = { 47 | 'default': { 48 | 'ENGINE': 'django.db.backends.sqlite3', 49 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 50 | } 51 | } 52 | 53 | # Using the MD5 password hasher improves test performance 54 | PASSWORD_HASHERS = ( 55 | 'django.contrib.auth.hashers.MD5PasswordHasher', 56 | ) 57 | 58 | USE_TZ = True 59 | 60 | LOGGING = { 61 | 'version': 1, 62 | 'disable_existing_loggers': False, 63 | 'handlers': { 64 | 'console': { 65 | 'class': 'logging.StreamHandler', 66 | }, 67 | }, 68 | 'loggers': { 69 | 'django': { 70 | 'handlers': ['console'], 71 | 'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'), 72 | }, 73 | }, 74 | } 75 | -------------------------------------------------------------------------------- /tests_django/test_statement_integration.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timezone 2 | from django.test import TestCase 3 | from chatterbot.conversation import Statement as StatementObject 4 | from chatterbot.ext.django_chatterbot.models import Statement as StatementModel 5 | 6 | 7 | class StatementIntegrationTestCase(TestCase): 8 | """ 9 | Test case to make sure that the Django Statement model 10 | and ChatterBot Statement object have a common interface. 11 | """ 12 | 13 | def setUp(self): 14 | super().setUp() 15 | 16 | now = datetime(2020, 2, 15, 3, 14, 10, 0, timezone.utc) 17 | 18 | self.object = StatementObject(text='_', created_at=now) 19 | self.model = StatementModel(text='_', created_at=now) 20 | 21 | # Simulate both statements being saved 22 | self.model.save() 23 | self.object.id = self.model.id 24 | 25 | def test_text(self): 26 | self.assertTrue(hasattr(self.object, 'text')) 27 | self.assertTrue(hasattr(self.model, 'text')) 28 | 29 | def test_in_response_to(self): 30 | self.assertTrue(hasattr(self.object, 'in_response_to')) 31 | self.assertTrue(hasattr(self.model, 'in_response_to')) 32 | 33 | def test_conversation(self): 34 | self.assertTrue(hasattr(self.object, 'conversation')) 35 | self.assertTrue(hasattr(self.model, 'conversation')) 36 | 37 | def test_tags(self): 38 | self.assertTrue(hasattr(self.object, 'tags')) 39 | self.assertTrue(hasattr(self.model, 'tags')) 40 | 41 | def test__str__(self): 42 | self.assertTrue(hasattr(self.object, '__str__')) 43 | self.assertTrue(hasattr(self.model, '__str__')) 44 | 45 | self.assertEqual(str(self.object), str(self.model)) 46 | 47 | def test_add_tags(self): 48 | self.object.add_tags('a', 'b') 49 | self.model.add_tags('a', 'b') 50 | 51 | self.assertIn('a', self.object.get_tags()) 52 | self.assertIn('a', self.model.get_tags()) 53 | 54 | def test_serialize(self): 55 | object_data = self.object.serialize() 56 | model_data = self.model.serialize() 57 | 58 | self.assertEqual(object_data, model_data) 59 | --------------------------------------------------------------------------------