Full Code of gunthercox/ChatterBot for AI

master 7acf675b204d cached
236 files
1.3 MB
331.2k tokens
1619 symbols
1 requests
Download .txt
Showing preview only (1,382K chars total). Download the full file or copy to clipboard to get everything.
Repository: gunthercox/ChatterBot
Branch: master
Commit: 7acf675b204d
Files: 236
Total size: 1.3 MB

Directory structure:
gitextract_if0fu1k3/

├── .codeclimate.yml
├── .github/
│   ├── FUNDING.yml
│   └── 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
│   │   │   │   ├── 0021_increase_text_max_length_to_1100.py
│   │   │   │   └── __init__.py
│   │   │   ├── model_admin.py
│   │   │   ├── models.py
│   │   │   └── settings.py
│   │   └── sqlalchemy_app/
│   │       ├── __init__.py
│   │       └── models.py
│   ├── filters.py
│   ├── languages.py
│   ├── logic/
│   │   ├── __init__.py
│   │   ├── best_match.py
│   │   ├── llm_adapters.py
│   │   ├── logic_adapter.py
│   │   ├── mathematical_evaluation.py
│   │   ├── mcp_tools.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/
│   │   ├── mobile.js
│   │   ├── silktide-consent-manager.css
│   │   ├── silktide-consent-manager.js
│   │   └── style.css
│   ├── _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/
│   │   ├── custom-models.rst
│   │   ├── index.rst
│   │   ├── settings.rst
│   │   ├── tutorial/
│   │   │   ├── django-filter-tutorial/
│   │   │   │   └── index.rst
│   │   │   ├── django-rest-framework-tutorial/
│   │   │   │   └── index.rst
│   │   │   ├── django-tutorial/
│   │   │   │   ├── 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
│   ├── security.rst
│   ├── 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
│   │   │   │   │   └── custom.css
│   │   │   │   └── js/
│   │   │   │       ├── bootstrap.js
│   │   │   │       ├── 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.xcf
│   └── chatterbot.xcf
├── pyproject.toml
├── setup.cfg
└── tests/
    ├── __init__.py
    ├── base_case.py
    ├── django_integration/
    │   ├── __init__.py
    │   ├── base_case.py
    │   ├── test_chatbot.py
    │   ├── test_chatterbot_corpus_training.py
    │   ├── test_chatterbot_settings.py
    │   ├── test_custom_models.py
    │   ├── test_django_adapter.py
    │   ├── test_logic_adapter_integration.py
    │   ├── test_secondary_database.py
    │   ├── test_settings.py
    │   └── test_statement_integration.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

================================================
FILE CONTENTS
================================================

================================================
FILE: .codeclimate.yml
================================================
engines:
  pep8:
    enabled: true
ratings:
  paths:
  - "**.py"
exclude_paths:
- tests/*
- examples/*
- chatterbot/ext/django_chatterbot/migrations/*


================================================
FILE: .github/FUNDING.yml
================================================
github: gunthercox


================================================
FILE: .github/workflows/publish-documentation.yml
================================================
name: Deploy Sphinx documentation to Pages

on:
  push:
    branches: [master] # branch to trigger deployment

jobs:
  pages:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    permissions:
      pages: write
      id-token: write
    steps:
    - id: deployment
      uses: sphinx-notes/pages@v3
      with:
        pyproject_extras: 'test'
        sphinx_build_options: '-b dirhtml'


================================================
FILE: .github/workflows/python-package.yml
================================================
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Python package

permissions:
  contents: read
  pull-requests: write
  checks: write

on:
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "*" ]
env:
  CHATTERBOT_SHOW_TRAINING_PROGRESS: '0'

jobs:

  build:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
    services:
      redis:
        image: redis/redis-stack-server:latest
        ports:
          - 6379:6379
    steps:
    - uses: actions/checkout@v4
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
        cache: 'pip'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install .[test,dev,redis,mongodb]
        python -m spacy download en_core_web_sm
        python -m spacy download de_core_news_sm
    - name: Lint with flake8
      run: |
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Start MongoDB
      uses: supercharge/mongodb-github-action@1.11.0
      with:
        mongodb-version: '8.0'
    - name: Run tests
      run: |
        python -Wonce -m unittest discover -s tests -v
    - name: Run tests for Django example app
      run: |
        python -Wonce examples/django_example/manage.py test examples/django_example/
    # --------------------------------------------------------------
    # TODO: Fix & re-enable later
    # https://github.com/marketplace/actions/coveralls-github-action
    # - name: Coveralls GitHub Action
    #   uses: coverallsapp/github-action@v2.3.4
    # - name: Generate code coverage
    #   uses: paambaati/codeclimate-action@v9.0.0
    #   env:
    #     CC_TEST_REPORTER_ID: 3ec30a156224df0f59620967241d9659086e918fd824f4f69b8ce7b55b5a590f
    #   with:
    #     coverageCommand: coverage
    #     debug: true


================================================
FILE: .gitignore
================================================
bin
build
html
dist
venv
.env
.out
.coverage
.python-version
*.pyc
*.swp
*.egg-info
*.egg/*
*.eggs/*
*.doctrees

# Database files
.database
*.sqlite3
*.sqlite3-*

# IDE files
.vscode


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or
  advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
  address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
community@chatterbot.us.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior,  harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.


================================================
FILE: LICENSE
================================================
Copyright (c) 2016 - 2025, Gunther Cox
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

* 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.

* 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.

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.


================================================
FILE: README.md
================================================
![ChatterBot: Machine learning in Python](https://i.imgur.com/b3SCmGT.png)

# ChatterBot

ChatterBot is a machine-learning based conversational dialog engine built in
Python which makes it possible to generate responses based on collections of
known conversations. The language independent design of ChatterBot allows it
to be trained to speak any language.

[![Package Version](https://img.shields.io/pypi/v/chatterbot.svg)](https://pypi.python.org/pypi/chatterbot/)
[![Python 3.12](https://img.shields.io/badge/python-3.12-blue.svg)](https://www.python.org/downloads/release/python-360/)
[![Coverage Status](https://img.shields.io/coveralls/gunthercox/ChatterBot.svg)](https://coveralls.io/r/gunthercox/ChatterBot)
[![Follow on Bluesky](https://img.shields.io/badge/🦋%20Bluesky-1185fe)](https://bsky.app/profile/chatterbot.us)
[![Join the chat at https://gitter.im/chatterbot/Lobby](https://badges.gitter.im/chatterbot/Lobby.svg)](https://gitter.im/chatterbot/Lobby?utm_source=badge&utm_medium=badge&utm_content=badge)
<!-- [![Code Climate](https://codeclimate.com/github/gunthercox/ChatterBot/badges/gpa.svg)](https://codeclimate.com/github/gunthercox/ChatterBot) -->

An example of typical input would be something like this:

> **user:** Good morning! How are you doing?  
> **bot:**  I am doing very well, thank you for asking.  
> **user:** You're welcome.  
> **bot:** Do you like hats?  

## How it works

An untrained instance of ChatterBot starts off with no knowledge of how to communicate. Each time a user enters a statement, the library saves the text that they entered and the text that the statement was in response to. As ChatterBot receives more input the number of responses that it can reply to, and the accuracy of each response in relation to the input statement increases. The program selects the closest matching response by searching for the closest matching known statement that matches the input, it then returns the most likely response to that statement based on how frequently each response is issued by the people the bot communicates with.

# [Documentation](https://docs.chatterbot.us)

View the [documentation](https://docs.chatterbot.us)
for ChatterBot.

## Installation

This package can be installed from [PyPi](https://pypi.python.org/pypi/ChatterBot) by running:

```bash
pip install chatterbot
```

## Basic Usage

```python
from chatterbot import ChatBot
from chatterbot.trainers import ChatterBotCorpusTrainer

chatbot = ChatBot('Ron Obvious')

# Create a new trainer for the chatbot
trainer = ChatterBotCorpusTrainer(chatbot)

# Train the chatbot based on the english corpus
trainer.train("chatterbot.corpus.english")

# Get a response to an input statement
chatbot.get_response("Hello, how are you today?")
```

# Training data

ChatterBot comes with a data utility module that can be used to train chat bots.
At the moment there is training data for over a dozen languages in this module.
Contributions of additional training data or training data
in other languages would be greatly appreciated. Take a look at the data files
in the [chatterbot-corpus](https://github.com/gunthercox/chatterbot-corpus)
package if you are interested in contributing.

```python
from chatterbot.trainers import ChatterBotCorpusTrainer

# Create a new trainer for the chatbot
trainer = ChatterBotCorpusTrainer(chatbot)

# Train based on the english corpus
trainer.train("chatterbot.corpus.english")

# Train based on english greetings corpus
trainer.train("chatterbot.corpus.english.greetings")

# Train based on the english conversations corpus
trainer.train("chatterbot.corpus.english.conversations")
```

**Corpus contributions are welcome! Please make a pull request.**

# Examples

For examples, see the [examples](https://docs.chatterbot.us/examples/)
section of the documentation.

# History

See release notes for changes https://github.com/gunthercox/ChatterBot/releases

# Contributing

Contributions are welcomed, to help ensure a smooth process please start with the contributing guidelines in our documentation:
https://docs.chatterbot.us/contributing/

# Sponsors

ChatterBot is sponsored by:

<p>
   <a href="https://www.testmuai.com/?utm_source=chatterbot&utm_medium=sponsor" target="_blank">
      <img src="docs/_static/testmu-ai-white-logo.png" style="vertical-align: middle;" width="250" height="80" />
   </a>
</p>

# License

ChatterBot is licensed under the [BSD 3-clause license](https://opensource.org/licenses/BSD-3-Clause).


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Supported Versions

Actively supported versions of ChatterBot can be determined using the following table.

| Version | Supported          |
| ------- | ------------------ |
| 1.2.x   | :white_check_mark: |
| < 1.2   | :x:                |

## Reporting a Vulnerability

ChatterBot uses GitHub's private security vulnerability reporting to accept reports about potential security vulnerabilities.

https://github.com/gunthercox/ChatterBot/security/advisories

To begin the process select the "Report a vulnerability" button on the security advisories page.
Once the report has been investigated an response plan will be issued based on the level of severity.
A response can generally be expected within 24 hours of report submission.


================================================
FILE: chatterbot/__init__.py
================================================
"""
ChatterBot is a machine learning, conversational dialog engine.
"""
from .chatterbot import ChatBot


__version__ = '1.2.12'

__all__ = (
    'ChatBot',
)


================================================
FILE: chatterbot/__main__.py
================================================
"""
Example usage for ChatterBot command line arguments:

python -m chatterbot --help
"""

import sys


def get_chatterbot_version():
    """
    Return the version of the current package.
    """
    from chatterbot import __version__

    return __version__


if __name__ == '__main__':
    if '--version' in sys.argv:
        print(get_chatterbot_version())
    elif '--help' in sys.argv:
        print('usage: chatterbot [--version, --help]')
        print('  --version: Print the version of ChatterBot')
        print('  --help: Print this help message')
        print()
        print('Documentation at https://docs.chatterbot.us')


================================================
FILE: chatterbot/adapters.py
================================================
class Adapter(object):
    """
    A superclass for all adapter classes.

    :param chatbot: A ChatBot instance.
    """

    def __init__(self, chatbot, **kwargs):
        self.chatbot = chatbot

    class AdapterMethodNotImplementedError(NotImplementedError):
        """
        An exception to be raised when an adapter method has not been implemented.
        Typically this indicates that the developer is expected to implement the
        method in a subclass.
        """

        def __init__(self, message='This method must be overridden in a subclass method.'):
            """
            Set the message for the exception.
            """
            super().__init__(message)

    class InvalidAdapterTypeException(Exception):
        """
        An exception to be raised when an adapter
        of an unexpected class type is received.
        """
        pass


================================================
FILE: chatterbot/chatterbot.py
================================================
import logging
import uuid
from typing import Union
from chatterbot.storage import StorageAdapter
from chatterbot.logic import LogicAdapter
from chatterbot.search import TextSearch, IndexedTextSearch, SemanticVectorSearch
from chatterbot.tagging import PosLemmaTagger
from chatterbot.conversation import Statement
from chatterbot import languages
from chatterbot import utils
import spacy


class ChatBot(object):
    """
    A conversational dialog chat bot.

    :param name: A name is the only required parameter for the ChatBot class.
    :type name: str

    :keyword storage_adapter: The dot-notated import path to a storage adapter class.
                              Defaults to ``"chatterbot.storage.SQLStorageAdapter"``.
    :type storage_adapter: str

    :param logic_adapters: A list of dot-notated import paths to each logic adapter the bot uses.
                           Defaults to ``["chatterbot.logic.BestMatch"]``.
    :type logic_adapters: list

    :param tagger: The tagger to use for the chat bot.
                   Defaults to :class:`~chatterbot.tagging.PosLemmaTagger`
    :type tagger: object

    :param tagger_language: The language to use for the tagger.
                            Defaults to :class:`~chatterbot.languages.ENG`.
    :type tagger_language: object

    :param preprocessors: A list of preprocessor functions to use for the chat bot.
    :type preprocessors: list

    :param read_only: If True, the chat bot will not save any input it receives, defaults to False.
    :type read_only: bool

    :param logger: A ``Logger`` object.
    :type logger: logging.Logger

    :param model: A definition used to load a large language model.
                  Defaults to ``None``.
                  (Added in version 1.2.7)
    :type model: dict

    :param stream: Return output as a streaming responses when a ``model`` is defined.
                   (Added in version 1.2.7)
    """

    def __init__(self, name, stream=False, **kwargs):
        self.name = name

        self.stream = stream

        # Generate a default conversation ID for this ChatBot instance.
        # This is used as a fallback when callers don't provide an explicit
        # conversation ID, ensuring that conversation history is tracked
        # within a session. Conversation IDs are necessary for cases such as
        # the LLM-based logic adapters which require it to retrieve previous
        # messages.
        self.default_conversation = uuid.uuid4().hex

        self.logger = kwargs.get('logger', logging.getLogger(__name__))

        storage_adapter = kwargs.get('storage_adapter', 'chatterbot.storage.SQLStorageAdapter')

        logic_adapters = kwargs.get('logic_adapters', [
            'chatterbot.logic.BestMatch'
        ])

        # Check that each adapter is a valid subclass of it's respective parent
        utils.validate_adapter_class(storage_adapter, StorageAdapter)

        # Logic adapters used by the chat bot
        self.logic_adapters = []

        self.storage = utils.initialize_class(storage_adapter, **kwargs)

        tagger_language = kwargs.get('tagger_language', languages.ENG)

        # Check if storage adapter has a preferred tagger
        PreferredTagger = self.storage.get_preferred_tagger()

        if PreferredTagger is not None:
            # Storage adapter specifies its own tagger
            self.tagger = PreferredTagger(language=tagger_language)
        else:
            # Use default or user-specified tagger
            try:
                Tagger = kwargs.get('tagger', PosLemmaTagger)

                # Allow instances to be provided for performance optimization
                # (Example: a pre-loaded model in a tagger when unit testing)
                if not isinstance(Tagger, type):
                    self.tagger = Tagger
                else:
                    self.tagger = Tagger(language=tagger_language)
            except IOError as io_error:
                # Return a more helpful error message if possible
                if "Can't find model" in str(io_error):
                    model_name = utils.get_model_for_language(tagger_language)
                    if hasattr(tagger_language, 'ENGLISH_NAME'):
                        language_name = tagger_language.ENGLISH_NAME
                    else:
                        language_name = tagger_language
                    raise self.ChatBotException(
                        'Setup error:\n'
                        f'The Spacy model for "{language_name}" language is missing.\n'
                        'Please install the model using the command:\n\n'
                        f'python -m spacy download {model_name}\n\n'
                        'See https://spacy.io/usage/models for more information about available models.'
                    ) from io_error
                else:
                    raise io_error

        # Initialize search algorithms
        primary_search_algorithm = IndexedTextSearch(self, **kwargs)
        text_search_algorithm = TextSearch(self, **kwargs)
        semantic_vector_search_algorithm = SemanticVectorSearch(self, **kwargs)

        self.search_algorithms = {
            primary_search_algorithm.name: primary_search_algorithm,
            text_search_algorithm.name: text_search_algorithm,
            semantic_vector_search_algorithm.name: semantic_vector_search_algorithm
        }

        # Check if storage adapter has a preferred search algorithm
        preferred_search_algorithm = self.storage.get_preferred_search_algorithm()
        if preferred_search_algorithm and preferred_search_algorithm in self.search_algorithms:
            # Set as default for logic adapters that don't specify their own search algorithm
            # This ensures BestMatch and other adapters use the optimal search method
            self.logger.info(f'Storage adapter prefers search algorithm: {preferred_search_algorithm}')
            kwargs.setdefault('search_algorithm_name', preferred_search_algorithm)

        for adapter in logic_adapters:
            utils.validate_adapter_class(adapter, LogicAdapter)
            logic_adapter = utils.initialize_class(adapter, self, **kwargs)
            self.logic_adapters.append(logic_adapter)

        preprocessors = kwargs.get(
            'preprocessors', [
                'chatterbot.preprocessors.clean_whitespace'
            ]
        )

        self.preprocessors = []

        for preprocessor in preprocessors:
            self.preprocessors.append(utils.import_module(preprocessor))

        # NOTE: 'xx' is the language code for a multi-language model
        self.nlp = spacy.blank(self.tagger.language.ISO_639_1)

        # Allow the bot to save input it receives so that it can learn
        self.read_only = kwargs.get('read_only', False)

    def get_response(self, statement: Union[Statement, str, dict] = None, **kwargs) -> Statement:
        """
        Return the bot's response based on the input.

        :param statement: An statement object or string.
        :returns: A response to the input.

        :param additional_response_selection_parameters: Parameters to pass to the
            chat bot's logic adapters to control response selection.
        :type additional_response_selection_parameters: dict

        :param persist_values_to_response: Values that should be saved to the response
            that the chat bot generates.
        :type persist_values_to_response: dict
        """
        Statement = self.storage.get_object('statement')

        additional_response_selection_parameters = kwargs.pop('additional_response_selection_parameters', {})

        persist_values_to_response = kwargs.pop('persist_values_to_response', {})

        if isinstance(statement, str):
            kwargs['text'] = statement

        if isinstance(statement, dict):
            kwargs.update(statement)

        if statement is None and 'text' not in kwargs:
            raise self.ChatBotException(
                'Either a statement object or a "text" keyword '
                'argument is required. Neither was provided.'
            )

        if hasattr(statement, 'serialize'):
            kwargs.update(**statement.serialize())

        tags = kwargs.pop('tags', [])

        text = kwargs.pop('text')

        input_statement = Statement(text=text, **kwargs)

        input_statement.add_tags(*tags)

        # If no conversation ID was provided, use the default session ID
        # so that conversation history is tracked across calls. Callers
        # can override this by passing an explicit conversation kwarg or
        # setting it on the Statement object.
        if not input_statement.conversation:
            input_statement.conversation = self.default_conversation

        # Preprocess the input statement
        for preprocessor in self.preprocessors:
            input_statement = preprocessor(input_statement)

        # Mark the statement as being a response to the previous
        if input_statement.in_response_to is None:
            previous_statement = self.get_latest_response(input_statement.conversation)
            if previous_statement:
                input_statement.in_response_to = previous_statement.text

        # Make sure the input statement has its search text saved
        if not self.tagger.needs_text_indexing():
            # Tagger doesn't transform text, use it directly
            if not input_statement.search_text:
                input_statement.search_text = input_statement.text
            if not input_statement.search_in_response_to and input_statement.in_response_to:
                input_statement.search_in_response_to = input_statement.in_response_to
        else:
            # Use tagger for text indexing or transformations
            if not input_statement.search_text:
                _search_text = self.tagger.get_text_index_string(input_statement.text)
                input_statement.search_text = _search_text

            if not input_statement.search_in_response_to and input_statement.in_response_to:
                input_statement.search_in_response_to = self.tagger.get_text_index_string(
                    input_statement.in_response_to
                )

        response = self.generate_response(
            input_statement,
            additional_response_selection_parameters
        )

        # If streaming is enabled return the response immediately
        if self.stream:
            return response

        # Update any response data that needs to be changed
        if persist_values_to_response:
            for response_key in persist_values_to_response:
                response_value = persist_values_to_response[response_key]
                if response_key == 'tags':
                    input_statement.add_tags(*response_value)
                    response.add_tags(*response_value)
                else:
                    setattr(input_statement, response_key, response_value)
                    setattr(response, response_key, response_value)

        if not self.read_only:

            # Save the input statement
            self.storage.create(**input_statement.serialize())

            # Save the response generated for the input
            self.learn_response(response, previous_statement=input_statement)

        return response

    def generate_response(self, input_statement, additional_response_selection_parameters=None):
        """
        Return a response based on a given input statement.

        :param input_statement: The input statement to be processed.
        """
        Statement = self.storage.get_object('statement')

        results = []
        result = None
        max_confidence = -1

        for adapter in self.logic_adapters:
            if adapter.can_process(input_statement):

                output = adapter.process(input_statement, additional_response_selection_parameters)
                results.append(output)

                self.logger.info(
                    '{} selected "{}" as a response with a confidence of {}'.format(
                        adapter.class_name, output.text, output.confidence
                    )
                )

                if output.confidence > max_confidence:
                    result = output
                    max_confidence = output.confidence
            else:
                self.logger.info(
                    'Not processing the statement using {}'.format(adapter.class_name)
                )

        class ResultOption:
            def __init__(self, statement, count=1):
                self.statement = statement
                self.count = count

        # If multiple adapters agree on the same statement,
        # then that statement is more likely to be the correct response
        if len(results) >= 3:
            result_options = {}
            for result_option in results:
                result_string = result_option.text + ':' + (result_option.in_response_to or '')

                if result_string in result_options:
                    result_options[result_string].count += 1
                    if result_options[result_string].statement.confidence < result_option.confidence:
                        result_options[result_string].statement = result_option
                else:
                    result_options[result_string] = ResultOption(
                        result_option
                    )

            most_common = list(result_options.values())[0]

            for result_option in result_options.values():
                if result_option.count > most_common.count:
                    most_common = result_option

            self.logger.info('Selecting "{}" as the most common response'.format(most_common.statement.text))

            if most_common.count > 1:
                result = most_common.statement

        response = Statement(
            text=result.text,
            in_response_to=input_statement.text,
            conversation=input_statement.conversation,
            persona='bot:' + self.name
        )

        response.add_tags(*result.get_tags())

        response.confidence = result.confidence

        return response

    def learn_response(self, statement, previous_statement=None):
        """
        Learn that the statement provided is a valid response.
        """
        if not previous_statement:
            previous_statement = statement.in_response_to

        if not previous_statement:
            previous_statement = self.get_latest_response(statement.conversation)
            if previous_statement:
                previous_statement = previous_statement.text

        previous_statement_text = previous_statement

        if not isinstance(previous_statement, (str, type(None), )):
            statement.in_response_to = previous_statement.text
        elif isinstance(previous_statement, str):
            statement.in_response_to = previous_statement

        self.logger.info('Adding "{}" as a response to "{}"'.format(
            statement.text,
            previous_statement_text
        ))

        if not statement.persona:
            statement.persona = 'bot:' + self.name

        # Save the response statement
        return self.storage.create(**statement.serialize())

    def get_latest_response(self, conversation: str):
        """
        Returns the latest response in a conversation if it exists.
        Returns None if a matching conversation cannot be found.
        """
        conversation_statements = list(self.storage.filter(
            conversation=conversation,
            order_by=['id']
        ))

        # Get the most recent statement in the conversation if one exists
        latest_statement = conversation_statements[-1] if len(conversation_statements) else None

        return latest_statement

    class ChatBotException(Exception):
        pass


================================================
FILE: chatterbot/comparisons.py
================================================
"""
This module contains various text-comparison algorithms
designed to compare one statement to another.
"""
from chatterbot.utils import get_model_for_language
from difflib import SequenceMatcher
import spacy


class Comparator:
    """
    Base class establishing the interface that all comparators should implement.
    """

    def __init__(self, language):

        self.language = language

    def __call__(self, statement_a, statement_b):
        return self.compare(statement_a, statement_b)

    def compare_text(self, text_a: str, text_b: str) -> float:
        """
        Implemented in subclasses: compare text_a to text_b.

        :return: The percent of similarity between the statements based on the implemented algorithm.
        """
        return 0

    def compare(self, statement_a, statement_b) -> float:
        """
        :return: The percent of similarity between the statements based on the implemented algorithm.
        """
        return self.compare_text(statement_a.text, statement_b.text)


class LevenshteinDistance(Comparator):
    """
    Compare two statements based on the Levenshtein distance
    of each statement's text.

    For example, there is a 65% similarity between the statements
    "where is the post office?" and "looking for the post office"
    based on the Levenshtein distance algorithm.
    """

    def compare_text(self, text_a: str, text_b: str) -> float:
        """
        Compare the two pieces of text.

        :return: The percent of similarity between the text of the statements.
        """

        # Return 0 if either statement has a None text value
        if text_a is None or text_b is None:
            return 0

        # Get the lowercase version of both strings
        statement_a_text = str(text_a.lower())
        statement_b_text = str(text_b.lower())

        similarity = SequenceMatcher(
            None,
            statement_a_text,
            statement_b_text
        )

        # Calculate a decimal percent of the similarity
        percent = round(similarity.ratio(), 2)

        return percent


class SpacySimilarity(Comparator):
    """
    Calculate the similarity of two statements using Spacy models.

    NOTE:
        You will also need to download a ``spacy`` model to use for tagging. Internally these are used to determine parts of speech for words.

        The easiest way to do this is to use the ``spacy download`` command directly:

        .. code-block:: python

           python -m spacy download en_core_web_sm
           python -m spacy download de_core_news_sm

        Alternatively, the ``spacy`` models can be installed as Python
        packages. The following lines could be included in a
        ``requirements.txt`` or ``pyproject.yml`` file if you needed to pin
        specific versions:

        .. code-block:: text

           https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.3.0/en_core_web_sm-2.3.0.tar.gz#egg=en_core_web_sm
           https://github.com/explosion/spacy-models/releases/download/de_core_news_sm-2.3.0/de_core_news_sm-2.3.0.tar.gz#egg=de_core_news_sm

    """

    def __init__(self, language):
        super().__init__(language)

        model = get_model_for_language(language)

        # Disable the Named Entity Recognition (NER) component because it is not necessary
        self.nlp = spacy.load(model, exclude=['ner'])

    def compare_text(self, text_a: str, text_b: str) -> float:
        """
        Compare the similarity of two strings.

        :return: The percent of similarity between the closest synset distance.
        """

        # Return 0 if either statement has a None text value
        if text_a is None or text_b is None:
            return 0

        document_a = self.nlp(text_a)
        document_b = self.nlp(text_b)

        return document_a.similarity(document_b)


class JaccardSimilarity(Comparator):
    """
    Calculates the similarity of two statements based on the Jaccard index.

    The Jaccard index is composed of a numerator and denominator.
    In the numerator, we count the number of items that are shared between the sets.
    In the denominator, we count the total number of items across both sets.
    Let's say we define sentences to be equivalent if 50% or more of their tokens are equivalent.
    Here are two sample sentences:

        The young cat is hungry.
        The cat is very hungry.

    When we parse these sentences to remove stopwords, we end up with the following two sets:

        {young, cat, hungry}
        {cat, very, hungry}

    In our example above, our intersection is {cat, hungry}, which has count of two.
    The union of the sets is {young, cat, very, hungry}, which has a count of four.
    Therefore, our `Jaccard similarity index`_ is two divided by four, or 50%.
    Given our similarity threshold above, we would consider this to be a match.

    .. _`Jaccard similarity index`: https://en.wikipedia.org/wiki/Jaccard_index
    """

    def __init__(self, language):
        super().__init__(language)

        model = get_model_for_language(language)

        # Disable the Named Entity Recognition (NER) component because it is not necessary
        self.nlp = spacy.load(model, exclude=['ner'])

    def compare_text(self, text_a: str, text_b: str) -> float:
        """
        Return the calculated similarity of two
        statements based on the Jaccard index.
        """

        # Return 0 if either statement has a None text value
        if text_a is None or text_b is None:
            return 0

        # Make both strings lowercase
        document_a = self.nlp(text_a.lower())
        document_b = self.nlp(text_b.lower())

        statement_a_lemmas = frozenset([
            token.lemma_ for token in document_a if not token.is_stop
        ])
        statement_b_lemmas = frozenset([
            token.lemma_ for token in document_b if not token.is_stop
        ])

        # Calculate Jaccard similarity
        numerator = len(statement_a_lemmas.intersection(statement_b_lemmas))
        denominator = float(len(statement_a_lemmas.union(statement_b_lemmas)))
        ratio = numerator / denominator

        return ratio


================================================
FILE: chatterbot/components.py
================================================
"""
Custom components for Spacy processing pipelines.
https://spacy.io/usage/processing-pipelines#custom-components
"""
import string
from spacy.language import Language
from spacy.tokens import Doc


punctuation_table = str.maketrans(dict.fromkeys(string.punctuation))


@Language.component('chatterbot_bigram_indexer')
def chatterbot_bigram_indexer(document):
    """
    Generate the text string for a bigram-based search index.
    """

    if not Doc.has_extension('search_index'):
        Doc.set_extension('search_index', default='')

    tokens = [
        token for token in document if not (token.is_punct or token.is_stop)
    ]

    # Fall back to including stop words if needed
    if not tokens or len(tokens) == 1:
        tokens = [
            token for token in document if not (token.is_punct)
        ]

    # Pairs consist of the part-of-speech of the first token and the
    # lemma of the second token in the bigram. This provides a good
    # balance of generalization and specificity for matching.
    bigram_pairs = [
        f"{tokens[i - 1].pos_}:{tokens[i].lemma_.lower()}"
        for i in range(1, len(tokens))
    ]

    if not bigram_pairs:

        text_without_punctuation = document.text.translate(
            punctuation_table
        )
        if len(text_without_punctuation) >= 1:
            text = text_without_punctuation.lower()
        else:
            text = document.text.lower()

        bigram_pairs = [text]

    # Assign a custom attribute at the Doc level
    document._.search_index = ' '.join(bigram_pairs)

    return document


@Language.component('chatterbot_lowercase_indexer')
def chatterbot_lowercase_indexer(document):
    """
    Generate the a lowercase text string for search index.
    """

    if not Doc.has_extension('search_index'):
        Doc.set_extension('search_index', default='')

    # Assign a custom attribute at the Doc level
    document._.search_index = document.text.lower()

    return document


================================================
FILE: chatterbot/constants.py
================================================
"""
ChatterBot constants
"""
from chatterbot import languages

'''
The maximum length of characters that the text of a statement can contain.
The number 1100 is used to support longer conversational statements while
remaining within VARCHAR limits for most databases. This value should be
enforced on a per-model basis by the data model for each storage adapter.
'''
STATEMENT_TEXT_MAX_LENGTH = 1100

'''
The maximum length of characters that the text label of a conversation can contain.
The number 32 was chosen because that is the length of the string representation
of a UUID4 with no hyphens.
'''
CONVERSATION_LABEL_MAX_LENGTH = 32

'''
The maximum length of text that can be stored in the persona field of the statement model.
'''
PERSONA_MAX_LENGTH = 50

# The maximum length of characters that the name of a tag can contain
TAG_NAME_MAX_LENGTH = 50

# See other model options: https://spacy.io/models/
DEFAULT_LANGUAGE_TO_SPACY_MODEL_MAP = {
    languages.CAT: 'ca_core_news_sm',
    languages.CHI: 'zh_core_web_sm',
    languages.HRV: 'hr_core_news_sm',
    languages.DAN: 'da_core_news_sm',
    languages.DUT: 'nl_core_news_sm',
    languages.ENG: 'en_core_web_sm',
    languages.FIN: 'fi_core_news_sm',
    languages.FRE: 'fr_core_news_sm',
    languages.GER: 'de_core_news_sm',
    languages.GRE: 'el_core_news_sm',
    languages.ITA: 'it_core_news_sm',
    languages.JPN: 'ja_core_news_sm',
    languages.KOR: 'ko_core_news_sm',
    languages.LIT: 'lt_core_news_sm',
    languages.MAC: 'mk_core_news_sm',
    languages.NOR: 'nb_core_news_sm',
    languages.POL: 'pl_core_news_sm',
    languages.POR: 'pt_core_news_sm',
    languages.RUM: 'ro_core_news_sm',
    languages.RUS: 'ru_core_news_sm',
    languages.SLO: 'sl_core_news_sm',
    languages.SPA: 'es_core_news_sm',
    languages.SWE: 'sv_core_news_sm',
    languages.UKR: 'uk_core_news_sm',
}

DEFAULT_DJANGO_APP_NAME = 'django_chatterbot'


================================================
FILE: chatterbot/conversation.py
================================================
from datetime import datetime, timezone
from dateutil import parser as date_parser


class StatementMixin(object):
    """
    This class has shared methods used to
    normalize different statement models.
    """

    statement_field_names = [
        'id',
        'text',
        'search_text',
        'conversation',
        'persona',
        'tags',
        'in_response_to',
        'search_in_response_to',
        'created_at',
    ]

    extra_statement_field_names = []

    def get_statement_field_names(self) -> list[str]:
        """
        Return the list of field names for the statement.
        """
        return self.statement_field_names + self.extra_statement_field_names

    def get_tags(self) -> list[str]:
        """
        Return the list of tags for this statement.
        """
        return self.tags

    def add_tags(self, *tags):
        """
        Add a list of strings to the statement as tags.
        """
        self.tags.extend(tags)

    def serialize(self) -> dict:
        """
        :returns: A dictionary representation of the statement object.
        """
        data = {}

        for field_name in self.get_statement_field_names():
            format_method = getattr(self, 'get_{}'.format(
                field_name
            ), None)

            if format_method:
                data[field_name] = format_method()
            else:
                data[field_name] = getattr(self, field_name)

        return data


class Statement(StatementMixin):
    """
    A statement represents a single spoken entity, sentence or
    phrase that someone can say.
    """

    __slots__ = (
        'id',
        'text',
        'search_text',
        'conversation',
        'persona',
        'tags',
        'in_response_to',
        'search_in_response_to',
        'created_at',
        'confidence',
        'storage',
    )

    def __init__(self, text: str, in_response_to=None, **kwargs):

        self.id = kwargs.get('id')
        self.text = str(text)
        self.search_text = kwargs.get('search_text', '')
        self.conversation = kwargs.get('conversation', '')
        self.persona = kwargs.get('persona', '')
        self.tags = kwargs.pop('tags', [])
        self.in_response_to = in_response_to
        self.search_in_response_to = kwargs.get('search_in_response_to', '')
        self.created_at = kwargs.get('created_at', datetime.now())

        if not isinstance(self.created_at, datetime):
            self.created_at = date_parser.parse(self.created_at)

        # Set timezone to UTC if no timezone was provided
        if not self.created_at.tzinfo:
            self.created_at = self.created_at.replace(tzinfo=timezone.utc)

        # This is the confidence with which the chat bot believes
        # this is an accurate response. This value is set when the
        # statement is returned by the chat bot.
        self.confidence = kwargs.get('confidence', 0)

        self.storage = None

    def __str__(self):
        return self.text

    def __repr__(self):
        return '<Statement text:%s>' % (self.text)

    def save(self):
        """
        Save the statement in the database.
        """
        self.storage.update(self)


================================================
FILE: chatterbot/corpus.py
================================================
import os
import io
import glob
from pathlib import Path
from chatterbot.exceptions import OptionalDependencyImportError

try:
    from chatterbot_corpus.corpus import DATA_DIRECTORY
except (ImportError, ModuleNotFoundError):
    # Default to the home directory of the current user
    DATA_DIRECTORY = os.path.join(
        Path.home(),
        'chatterbot_corpus',
        'data'
    )


CORPUS_EXTENSION = 'yml'


def get_file_path(dotted_path, extension='json') -> str:
    """
    Reads a dotted file path and returns the file path.
    """
    # If the operating system's file path seperator character is in the string
    if os.sep in dotted_path or '/' in dotted_path:
        # Assume the path is a valid file path
        return dotted_path

    parts = dotted_path.split('.')
    if parts[0] == 'chatterbot':
        parts.pop(0)
        parts[0] = DATA_DIRECTORY

    corpus_path = os.path.join(*parts)

    path_with_extension = '{}.{}'.format(corpus_path, extension)
    if os.path.exists(path_with_extension):
        corpus_path = path_with_extension

    return corpus_path


def read_corpus(file_name) -> dict:
    """
    Read and return the data from a corpus json file.
    """
    try:
        import yaml
    except ImportError:
        message = (
            'Unable to import "yaml".\n'
            'Please install "pyyaml" to enable chatterbot corpus functionality:\n'
            'pip install pyyaml'
        )
        raise OptionalDependencyImportError(message)

    with io.open(file_name, encoding='utf-8') as data_file:
        return yaml.safe_load(data_file)


def list_corpus_files(dotted_path) -> list[str]:
    """
    Return a list of file paths to each data file in the specified corpus.
    """
    corpus_path = get_file_path(dotted_path, extension=CORPUS_EXTENSION)
    paths = []

    if os.path.isdir(corpus_path):
        paths = glob.glob(corpus_path + '/**/*.' + CORPUS_EXTENSION, recursive=True)
    else:
        paths.append(corpus_path)

    paths.sort()
    return paths


def load_corpus(*data_file_paths):
    """
    Return the data contained within a specified corpus.
    """
    for file_path in data_file_paths:
        corpus = []
        corpus_data = read_corpus(file_path)

        conversations = corpus_data.get('conversations', [])
        corpus.extend(conversations)

        categories = corpus_data.get('categories', [])

        yield corpus, categories, file_path


================================================
FILE: chatterbot/exceptions.py
================================================
class OptionalDependencyImportError(ImportError):
    """
    An exception raised when a feature requires an optional dependency to be installed.
    """
    pass


================================================
FILE: chatterbot/ext/__init__.py
================================================


================================================
FILE: chatterbot/ext/django_chatterbot/__init__.py
================================================
default_app_config = (
    'chatterbot.ext.django_chatterbot.apps.DjangoChatterBotConfig'
)


================================================
FILE: chatterbot/ext/django_chatterbot/abstract_models.py
================================================
from chatterbot.conversation import StatementMixin
from chatterbot import constants
from django.db import models
from django.utils import timezone
from django.conf import settings
from django.apps import apps


DJANGO_APP_NAME = constants.DEFAULT_DJANGO_APP_NAME

# Default model paths for swappable models
# These can be overridden via CHATTERBOT_STATEMENT_MODEL and CHATTERBOT_TAG_MODEL settings
DEFAULT_STATEMENT_MODEL = f'{DJANGO_APP_NAME}.Statement'
DEFAULT_TAG_MODEL = f'{DJANGO_APP_NAME}.Tag'


class AbstractBaseTag(models.Model):
    """
    The abstract base tag allows other models to be created
    using the attributes that exist on the default models.
    """

    name = models.SlugField(
        max_length=constants.TAG_NAME_MAX_LENGTH,
        unique=True,
        help_text='The unique name of the tag.'
    )

    class Meta:
        abstract = True

    def __str__(self):
        return self.name


class AbstractBaseStatement(models.Model, StatementMixin):
    """
    The abstract base statement allows other models to be created
    using the attributes that exist on the default models.
    """

    text = models.CharField(
        max_length=constants.STATEMENT_TEXT_MAX_LENGTH,
        help_text='The text of the statement.'
    )

    search_text = models.CharField(
        max_length=constants.STATEMENT_TEXT_MAX_LENGTH,
        blank=True,
        help_text='A modified version of the statement text optimized for searching.'
    )

    conversation = models.CharField(
        max_length=constants.CONVERSATION_LABEL_MAX_LENGTH,
        help_text='A label used to link this statement to a conversation.'
    )

    created_at = models.DateTimeField(
        default=timezone.now,
        help_text='The date and time that the statement was created at.'
    )

    in_response_to = models.CharField(
        max_length=constants.STATEMENT_TEXT_MAX_LENGTH,
        null=True,
        help_text='The text of the statement that this statement is in response to.'
    )

    search_in_response_to = models.CharField(
        max_length=constants.STATEMENT_TEXT_MAX_LENGTH,
        blank=True,
        help_text='A modified version of the in_response_to text optimized for searching.'
    )

    persona = models.CharField(
        max_length=constants.PERSONA_MAX_LENGTH,
        help_text='A label used to link this statement to a persona.'
    )

    tags = models.ManyToManyField(
        settings.CHATTERBOT_TAG_MODEL if hasattr(
            settings, 'CHATTERBOT_TAG_MODEL'
        ) else DEFAULT_TAG_MODEL,
        related_name='statements',
        help_text='The tags that are associated with this statement.'
    )

    # This is the confidence with which the chat bot believes
    # this is an accurate response. This value is set when the
    # statement is returned by the chat bot.
    confidence = 0

    class Meta:
        abstract = True
        indexes = [
            models.Index(
                fields=['search_text'],
                name='idx_cb_search_text'
            ),
            models.Index(
                fields=['search_in_response_to'], name='idx_cb_search_in_response_to'
            ),
        ]

    def __str__(self):
        if len(self.text.strip()) > 60:
            return '{}...'.format(self.text[:57])
        elif len(self.text.strip()) > 0:
            return self.text
        return '<empty>'

    @classmethod
    def get_tag_model(cls):
        """
        Return the Tag model class, respecting the swappable setting.

        This method checks:
        1. Django settings (CHATTERBOT_TAG_MODEL) - project-wide configuration
        2. The model referenced by the 'tags' field - handles custom models via kwargs
        3. Falls back to DEFAULT_TAG_MODEL if introspection fails

        This ensures the correct Tag model is used even when custom models
        are specified via storage adapter kwargs rather than Django settings.
        """
        tag_model_path = getattr(settings, 'CHATTERBOT_TAG_MODEL', None)

        if tag_model_path:
            return apps.get_model(tag_model_path)

        # If no setting, infer from the ManyToManyField relationship for
        # cases where custom models are specified via kwargs
        try:
            # Get the model that this class's 'tags' field points to
            tags_field = cls._meta.get_field('tags')
            related_model = tags_field.related_model

            # Resolve strings (lazy references)
            if isinstance(related_model, str):
                return apps.get_model(related_model)
            return related_model
        except Exception:
            # Fallback to default if introspection fails
            return apps.get_model(DEFAULT_TAG_MODEL)

    def get_tags(self) -> list[str]:
        """
        Return the list of tags for this statement.
        """
        return list(self.tags.values_list('name', flat=True))

    def add_tags(self, *tags):
        """
        Add a list of strings to the statement as tags.
        """
        TagModel = self.get_tag_model()

        for tag_name in tags:
            tag_obj, _created = TagModel.objects.get_or_create(name=tag_name)
            self.tags.add(tag_obj)


================================================
FILE: chatterbot/ext/django_chatterbot/admin.py
================================================
from django.contrib import admin
from chatterbot.ext.django_chatterbot.model_admin import StatementAdmin, TagAdmin
from chatterbot.ext.django_chatterbot.models import Statement, Tag


admin.site.register(Statement, StatementAdmin)
admin.site.register(Tag, TagAdmin)


================================================
FILE: chatterbot/ext/django_chatterbot/apps.py
================================================
from django.apps import AppConfig


class DjangoChatterBotConfig(AppConfig):

    name = 'chatterbot.ext.django_chatterbot'
    label = 'django_chatterbot'
    verbose_name = 'Django ChatterBot'

    def ready(self):
        from chatterbot.ext.django_chatterbot import settings as defaults
        from django.conf import settings

        settings.CHATTERBOT = getattr(settings, 'CHATTERBOT', {})
        settings.CHATTERBOT.update(defaults.CHATTERBOT_DEFAULTS)


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0001_initial.py
================================================
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = []

    operations = [
        migrations.CreateModel(
            name='Response',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('occurrence', models.PositiveIntegerField(default=0)),
            ],
        ),
        migrations.CreateModel(
            name='Statement',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('text', models.CharField(max_length=255, unique=True)),
            ],
        ),
        migrations.AddField(
            model_name='response',
            name='response',
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='django_chatterbot.Statement'),
        ),
        migrations.AddField(
            model_name='response',
            name='statement',
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='in_response_to', to='django_chatterbot.Statement'),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0002_statement_extra_data.py
================================================
# Generated by Django 1.10.2 on 2016-10-30 12:13
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='statement',
            name='extra_data',
            field=models.CharField(default='{}', max_length=500),
            preserve_default=False,
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0003_change_occurrence_default.py
================================================
# Generated by Django 1.9 on 2016-12-12 00:06
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0002_statement_extra_data'),
    ]

    operations = [
        migrations.AlterField(
            model_name='response',
            name='occurrence',
            field=models.PositiveIntegerField(default=1),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0004_rename_in_response_to.py
================================================
# Generated by Django 1.10.3 on 2016-12-04 23:52
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0003_change_occurrence_default'),
    ]

    operations = [
        migrations.AlterField(
            model_name='response',
            name='statement',
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='in_response', to='django_chatterbot.Statement'),
        ),
        migrations.AlterField(
            model_name='response',
            name='response',
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='responses', to='django_chatterbot.Statement'),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0005_statement_created_at.py
================================================
# Generated by Django 1.10.1 on 2016-12-29 19:20
from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0004_rename_in_response_to'),
    ]

    operations = [
        migrations.AddField(
            model_name='statement',
            name='created_at',
            field=models.DateTimeField(
                default=django.utils.timezone.now,
                help_text='The date and time that this statement was created at.'
            ),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0006_create_conversation.py
================================================
# Generated by Django 1.9 on 2017-01-17 07:02
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0005_statement_created_at'),
    ]

    operations = [
        migrations.CreateModel(
            name='Conversation',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
            ],
        ),
        migrations.AlterField(
            model_name='statement',
            name='created_at',
            field=models.DateTimeField(default=django.utils.timezone.now, help_text='The date and time that this statement was created at.'),
        ),
        migrations.AddField(
            model_name='conversation',
            name='statements',
            field=models.ManyToManyField(help_text='The statements in this conversation.', related_name='conversation', to='django_chatterbot.Statement'),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0007_response_created_at.py
================================================
# Generated by Django 1.11 on 2017-07-18 00:16
from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0006_create_conversation'),
    ]

    operations = [
        migrations.AddField(
            model_name='response',
            name='created_at',
            field=models.DateTimeField(
                default=django.utils.timezone.now,
                help_text='The date and time that this response was created at.'
            ),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0008_update_conversations.py
================================================
# Generated by Django 1.11 on 2017-07-18 11:25
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0007_response_created_at'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='conversation',
            name='statements',
        ),
        migrations.RemoveField(
            model_name='response',
            name='occurrence',
        ),
        migrations.RemoveField(
            model_name='statement',
            name='created_at',
        ),
        migrations.AddField(
            model_name='conversation',
            name='responses',
            field=models.ManyToManyField(help_text='The responses in this conversation.', related_name='conversations', to='django_chatterbot.Response'),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0009_tags.py
================================================
# Generated by Django 1.11a1 on 2017-07-07 00:12
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0008_update_conversations'),
    ]

    operations = [
        migrations.CreateModel(
            name='Tag',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.SlugField()),
            ],
            options={
                'abstract': False,
            },
        ),
        migrations.AlterField(
            model_name='statement',
            name='text',
            field=models.CharField(max_length=255, unique=True),
        ),
        migrations.AddField(
            model_name='tag',
            name='statements',
            field=models.ManyToManyField(related_name='tags', to='django_chatterbot.Statement'),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0010_statement_text.py
================================================
# Generated by Django 1.11.4 on 2017-08-16 00:56
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0009_tags'),
    ]

    operations = [
        migrations.AlterField(
            model_name='statement',
            name='text',
            field=models.CharField(max_length=400, unique=True),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0011_blank_extra_data.py
================================================
# Generated by Django 1.11.4 on 2017-08-20 13:55
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0010_statement_text'),
    ]

    operations = [
        migrations.AlterField(
            model_name='statement',
            name='extra_data',
            field=models.CharField(blank=True, max_length=500),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0012_statement_created_at.py
================================================
from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0011_blank_extra_data'),
    ]

    operations = [
        migrations.AddField(
            model_name='statement',
            name='created_at',
            field=models.DateTimeField(
                default=django.utils.timezone.now,
                help_text='The date and time that the statement was created at.'
            ),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0013_change_conversations.py
================================================
# Generated by Django 1.11 on 2018-09-13 01:01
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0012_statement_created_at'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='conversation',
            name='responses',
        ),
        migrations.RemoveField(
            model_name='response',
            name='response',
        ),
        migrations.RemoveField(
            model_name='response',
            name='statement',
        ),
        migrations.AddField(
            model_name='statement',
            name='conversation',
            field=models.CharField(default='default', max_length=32),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name='statement',
            name='in_response_to',
            field=models.CharField(max_length=400, null=True),
        ),
        migrations.AlterField(
            model_name='statement',
            name='text',
            field=models.CharField(max_length=400),
        ),
        migrations.DeleteModel(
            name='Conversation',
        ),
        migrations.DeleteModel(
            name='Response',
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0014_remove_statement_extra_data.py
================================================
from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0013_change_conversations'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='statement',
            name='extra_data',
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0015_statement_persona.py
================================================
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0014_remove_statement_extra_data'),
    ]

    operations = [
        migrations.AddField(
            model_name='statement',
            name='persona',
            field=models.CharField(default='', max_length=50),
            preserve_default=False,
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0016_statement_stemmed_text.py
================================================
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0015_statement_persona'),
    ]

    operations = [
        migrations.AddField(
            model_name='statement',
            name='search_text',
            field=models.CharField(blank=True, max_length=400),
        ),
        migrations.AddField(
            model_name='statement',
            name='search_in_response_to',
            field=models.CharField(blank=True, max_length=400),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0017_tags_unique.py
================================================
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0016_statement_stemmed_text'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='tag',
            name='statements',
        ),
        migrations.AddField(
            model_name='statement',
            name='tags',
            field=models.ManyToManyField(
                related_name='statements',
                to='django_chatterbot.Tag'
            ),
        ),
        migrations.AlterField(
            model_name='tag',
            name='name',
            field=models.SlugField(unique=True),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0018_text_max_length.py
================================================
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0017_tags_unique'),
    ]

    operations = [
        migrations.AlterField(
            model_name='statement',
            name='in_response_to',
            field=models.CharField(max_length=255, null=True),
        ),
        migrations.AlterField(
            model_name='statement',
            name='search_in_response_to',
            field=models.CharField(blank=True, max_length=255),
        ),
        migrations.AlterField(
            model_name='statement',
            name='search_text',
            field=models.CharField(blank=True, max_length=255),
        ),
        migrations.AlterField(
            model_name='statement',
            name='text',
            field=models.CharField(max_length=255),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0019_alter_statement_id_alter_tag_id_and_more.py
================================================
# Generated by Django 4.2.19 on 2025-02-09 13:57

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0018_text_max_length'),
    ]

    operations = [
        migrations.AlterField(
            model_name='statement',
            name='id',
            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
        ),
        migrations.AlterField(
            model_name='tag',
            name='id',
            field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
        ),
        migrations.AddIndex(
            model_name='statement',
            index=models.Index(fields=['search_text'], name='idx_cb_search_text'),
        ),
        migrations.AddIndex(
            model_name='statement',
            index=models.Index(fields=['search_in_response_to'], name='idx_cb_search_in_response_to'),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0020_alter_statement_conversation_and_more.py
================================================
# Generated by Django 4.1 on 2025-03-29 23:27

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0019_alter_statement_id_alter_tag_id_and_more'),
    ]

    operations = [
        migrations.AlterField(
            model_name='statement',
            name='conversation',
            field=models.CharField(help_text='A label used to link this statement to a conversation.', max_length=32),
        ),
        migrations.AlterField(
            model_name='statement',
            name='in_response_to',
            field=models.CharField(help_text='The text of the statement that this statement is in response to.', max_length=255, null=True),
        ),
        migrations.AlterField(
            model_name='statement',
            name='persona',
            field=models.CharField(help_text='A label used to link this statement to a persona.', max_length=50),
        ),
        migrations.AlterField(
            model_name='statement',
            name='search_in_response_to',
            field=models.CharField(blank=True, help_text='A modified version of the in_response_to text optimized for searching.', max_length=255),
        ),
        migrations.AlterField(
            model_name='statement',
            name='search_text',
            field=models.CharField(blank=True, help_text='A modified version of the statement text optimized for searching.', max_length=255),
        ),
        migrations.AlterField(
            model_name='statement',
            name='tags',
            field=models.ManyToManyField(help_text='The tags that are associated with this statement.', related_name='statements', to='django_chatterbot.tag'),
        ),
        migrations.AlterField(
            model_name='statement',
            name='text',
            field=models.CharField(help_text='The text of the statement.', max_length=255),
        ),
        migrations.AlterField(
            model_name='tag',
            name='name',
            field=models.SlugField(help_text='The unique name of the tag.', unique=True),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/0021_increase_text_max_length_to_1100.py
================================================
"""
Django migration to increase text field max_length from 255 to 1100.

This migration alters all text-related fields in the Statement model:
- text
- search_text
- in_response_to
- search_in_response_to

This change supports longer conversational statements while remaining
within VARCHAR limits for most databases.
"""
from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('django_chatterbot', '0020_alter_statement_conversation_and_more'),
    ]

    operations = [
        migrations.AlterField(
            model_name='statement',
            name='text',
            field=models.CharField(max_length=1100, help_text='The text of the statement.'),
        ),
        migrations.AlterField(
            model_name='statement',
            name='search_text',
            field=models.CharField(
                blank=True,
                max_length=1100,
                help_text='A modified version of the statement text optimized for searching.'
            ),
        ),
        migrations.AlterField(
            model_name='statement',
            name='in_response_to',
            field=models.CharField(
                max_length=1100,
                null=True,
                help_text='The text of the statement that this statement is in response to.'
            ),
        ),
        migrations.AlterField(
            model_name='statement',
            name='search_in_response_to',
            field=models.CharField(
                blank=True,
                max_length=1100,
                help_text='A modified version of the in_response_to text optimized for searching.'
            ),
        ),
    ]


================================================
FILE: chatterbot/ext/django_chatterbot/migrations/__init__.py
================================================


================================================
FILE: chatterbot/ext/django_chatterbot/model_admin.py
================================================
from django.contrib import admin


class StatementAdmin(admin.ModelAdmin):
    list_display = ('text', 'in_response_to', 'conversation', 'created_at', )
    list_filter = ('text', 'created_at', )
    search_fields = ('text', )


class TagAdmin(admin.ModelAdmin):
    list_display = ('name', )
    list_filter = ('name', )
    search_fields = ('name', )


================================================
FILE: chatterbot/ext/django_chatterbot/models.py
================================================
from chatterbot.ext.django_chatterbot.abstract_models import AbstractBaseStatement, AbstractBaseTag


class Statement(AbstractBaseStatement):
    """
    A statement represents a single spoken entity, sentence or
    phrase that someone can say.

    This model can be swapped for a custom model by setting
    CHATTERBOT_STATEMENT_MODEL in your Django settings.
    """

    class Meta:
        swappable = 'CHATTERBOT_STATEMENT_MODEL'


class Tag(AbstractBaseTag):
    """
    A label that categorizes a statement.

    This model can be swapped for a custom model by setting
    CHATTERBOT_TAG_MODEL in your Django settings.
    """

    class Meta:
        swappable = 'CHATTERBOT_TAG_MODEL'


================================================
FILE: chatterbot/ext/django_chatterbot/settings.py
================================================
"""
Default ChatterBot settings for Django.
"""
from django.conf import settings
from chatterbot import constants


CHATTERBOT = getattr(settings, 'CHATTERBOT', {})

CHATTERBOT_DEFAULTS = {
    'name': 'ChatterBot',
    'storage_adapter': 'chatterbot.storage.DjangoStorageAdapter',
    'django_app_name': constants.DEFAULT_DJANGO_APP_NAME
}

CHATTERBOT.update(CHATTERBOT_DEFAULTS)


================================================
FILE: chatterbot/ext/sqlalchemy_app/__init__.py
================================================


================================================
FILE: chatterbot/ext/sqlalchemy_app/models.py
================================================
from sqlalchemy import Table, Column, Integer, String, DateTime, ForeignKey
from sqlalchemy.orm import relationship, declarative_base
from sqlalchemy.sql import func
from sqlalchemy.ext.declarative import declared_attr

from chatterbot.conversation import StatementMixin
from chatterbot import constants


class ModelBase(object):
    """
    An augmented base class for SqlAlchemy models.
    """

    @declared_attr
    def __tablename__(cls) -> str:
        """
        Return the lowercase class name as the name of the table.
        """
        return cls.__name__.lower()

    id = Column(
        Integer,
        primary_key=True,
        autoincrement=True
    )


Base = declarative_base(cls=ModelBase)


tag_association_table = Table(
    'tag_association',
    Base.metadata,
    Column('tag_id', Integer, ForeignKey('tag.id')),
    Column('statement_id', Integer, ForeignKey('statement.id'))
)


class Tag(Base):
    """
    A tag that describes a statement.
    """

    name = Column(
        String(constants.TAG_NAME_MAX_LENGTH),
        unique=True
    )


class Statement(Base, StatementMixin):
    """
    A Statement represents a sentence or phrase.
    """

    confidence = 0

    text = Column(
        String(constants.STATEMENT_TEXT_MAX_LENGTH)
    )

    search_text = Column(
        String(constants.STATEMENT_TEXT_MAX_LENGTH),
        nullable=False,
        server_default=''
    )

    conversation = Column(
        String(constants.CONVERSATION_LABEL_MAX_LENGTH),
        nullable=False,
        server_default=''
    )

    created_at = Column(
        DateTime(timezone=True),
        server_default=func.now()
    )

    tags = relationship(
        'Tag',
        secondary=lambda: tag_association_table,
        backref='statements'
    )

    in_response_to = Column(
        String(constants.STATEMENT_TEXT_MAX_LENGTH),
        nullable=True
    )

    search_in_response_to = Column(
        String(constants.STATEMENT_TEXT_MAX_LENGTH),
        nullable=False,
        server_default=''
    )

    persona = Column(
        String(constants.PERSONA_MAX_LENGTH),
        nullable=False,
        server_default=''
    )

    def get_tags(self) -> list[str]:
        """
        Return a list of tags for this statement.
        """
        return [tag.name for tag in self.tags]

    def add_tags(self, *tags):
        """
        Add a list of strings to the statement as tags.
        """
        self.tags.extend([
            Tag(name=tag) for tag in tags
        ])


================================================
FILE: chatterbot/filters.py
================================================
def get_recent_repeated_responses(chatbot, conversation, sample=10, threshold=3, quantity=3) -> list:
    """
    A filter that eliminates possibly repetitive responses to prevent
    a chat bot from repeating statements that it has recently said.
    """
    from collections import Counter

    # Get the most recent statements from the conversation
    conversation_statements = list(chatbot.storage.filter(
        conversation=conversation,
        order_by=['id']
    ))[sample * -1:]

    text_of_recent_responses = [
        statement.text for statement in conversation_statements
    ]

    counter = Counter(text_of_recent_responses)

    # Find the n most common responses from the conversation
    most_common = counter.most_common(quantity)

    return [
        counted[0] for counted in most_common
        if counted[1] >= threshold
    ]


================================================
FILE: chatterbot/languages.py
================================================
import sys
import inspect


class AAR:
    ISO_639_1 = ''
    ISO_639 = 'aar'
    ENGLISH_NAME = 'Afar'


class ABK:
    ISO_639_1 = ''
    ISO_639 = 'abk'
    ENGLISH_NAME = 'Abkhazian'


class ACE:
    ISO_639_1 = ''
    ISO_639 = 'ace'
    ENGLISH_NAME = 'Achinese'


class ACH:
    ISO_639_1 = ''
    ISO_639 = 'ach'
    ENGLISH_NAME = 'Acoli'


class ADA:
    ISO_639_1 = ''
    ISO_639 = 'ada'
    ENGLISH_NAME = 'Adangme'


class ADY:
    ISO_639_1 = ''
    ISO_639 = 'ady'
    ENGLISH_NAME = 'Adyghe'


class AFH:
    ISO_639_1 = ''
    ISO_639 = 'afh'
    ENGLISH_NAME = 'Afrihili'


class AFR:
    ISO_639_1 = ''
    ISO_639 = 'afr'
    ENGLISH_NAME = 'Afrikaans'


class AIN:
    ISO_639_1 = ''
    ISO_639 = 'ain'
    ENGLISH_NAME = 'Ainu'


class AKA:
    ISO_639_1 = ''
    ISO_639 = 'aka'
    ENGLISH_NAME = 'Akan'


class AKK:
    ISO_639_1 = ''
    ISO_639 = 'akk'
    ENGLISH_NAME = 'Akkadian'


class ALB:
    ISO_639_1 = ''
    ISO_639 = 'alb'
    ENGLISH_NAME = 'Albanian'


class ALE:
    ISO_639_1 = ''
    ISO_639 = 'ale'
    ENGLISH_NAME = 'Aleut'


class ALT:
    ISO_639_1 = ''
    ISO_639 = 'alt'
    ENGLISH_NAME = 'SouthernAltai'


class AMH:
    ISO_639_1 = ''
    ISO_639 = 'amh'
    ENGLISH_NAME = 'Amharic'


class ANP:
    ISO_639_1 = ''
    ISO_639 = 'anp'
    ENGLISH_NAME = 'Angika'


class ARA:
    ISO_639_1 = ''
    ISO_639 = 'ara'
    ENGLISH_NAME = 'Arabic'


class ARG:
    ISO_639_1 = ''
    ISO_639 = 'arg'
    ENGLISH_NAME = 'Aragonese'


class ARM:
    ISO_639_1 = ''
    ISO_639 = 'arm'
    ENGLISH_NAME = 'Armenian'


class ARN:
    ISO_639_1 = ''
    ISO_639 = 'arn'
    ENGLISH_NAME = 'Mapudungun'


class ARP:
    ISO_639_1 = ''
    ISO_639 = 'arp'
    ENGLISH_NAME = 'Arapaho'


class ARW:
    ISO_639_1 = ''
    ISO_639 = 'arw'
    ENGLISH_NAME = 'Arawak'


class ASM:
    ISO_639_1 = ''
    ISO_639 = 'asm'
    ENGLISH_NAME = 'Assamese'


class AST:
    ISO_639_1 = ''
    ISO_639 = 'ast'
    ENGLISH_NAME = 'Asturian'


class AVA:
    ISO_639_1 = ''
    ISO_639 = 'ava'
    ENGLISH_NAME = 'Avaric'


class AVE:
    ISO_639_1 = ''
    ISO_639 = 'ave'
    ENGLISH_NAME = 'Avestan'


class AWA:
    ISO_639_1 = ''
    ISO_639 = 'awa'
    ENGLISH_NAME = 'Awadhi'


class AYM:
    ISO_639_1 = ''
    ISO_639 = 'aym'
    ENGLISH_NAME = 'Aymara'


class AZE:
    ISO_639_1 = ''
    ISO_639 = 'aze'
    ENGLISH_NAME = 'Azerbaijani'


class BAK:
    ISO_639_1 = ''
    ISO_639 = 'bak'
    ENGLISH_NAME = 'Bashkir'


class BAL:
    ISO_639_1 = ''
    ISO_639 = 'bal'
    ENGLISH_NAME = 'Baluchi'


class BAM:
    ISO_639_1 = ''
    ISO_639 = 'bam'
    ENGLISH_NAME = 'Bambara'


class BAN:
    ISO_639_1 = ''
    ISO_639 = 'ban'
    ENGLISH_NAME = 'Balinese'


class BAQ:
    ISO_639_1 = ''
    ISO_639 = 'baq'
    ENGLISH_NAME = 'Basque'


class BAS:
    ISO_639_1 = ''
    ISO_639 = 'bas'
    ENGLISH_NAME = 'Basa'


class BEJ:
    ISO_639_1 = ''
    ISO_639 = 'bej'
    ENGLISH_NAME = 'Beja'


class BEL:
    ISO_639_1 = ''
    ISO_639 = 'bel'
    ENGLISH_NAME = 'Belarusian'


class BEM:
    ISO_639_1 = ''
    ISO_639 = 'bem'
    ENGLISH_NAME = 'Bemba'


class BEN:
    ISO_639_1 = 'bn'
    ISO_639 = 'ben'
    ENGLISH_NAME = 'Bengali'


class BHO:
    ISO_639_1 = ''
    ISO_639 = 'bho'
    ENGLISH_NAME = 'Bhojpuri'


class BIK:
    ISO_639_1 = ''
    ISO_639 = 'bik'
    ENGLISH_NAME = 'Bikol'


class BIN:
    ISO_639_1 = ''
    ISO_639 = 'bin'
    ENGLISH_NAME = 'Bini'


class BIS:
    ISO_639_1 = ''
    ISO_639 = 'bis'
    ENGLISH_NAME = 'Bislama'


class BLA:
    ISO_639_1 = ''
    ISO_639 = 'bla'
    ENGLISH_NAME = 'Siksika'


class BOS:
    ISO_639_1 = ''
    ISO_639 = 'bos'
    ENGLISH_NAME = 'Bosnian'


class BRA:
    ISO_639_1 = ''
    ISO_639 = 'bra'
    ENGLISH_NAME = 'Braj'


class BRE:
    ISO_639_1 = ''
    ISO_639 = 'bre'
    ENGLISH_NAME = 'Breton'


class BUA:
    ISO_639_1 = ''
    ISO_639 = 'bua'
    ENGLISH_NAME = 'Buriat'


class BUG:
    ISO_639_1 = ''
    ISO_639 = 'bug'
    ENGLISH_NAME = 'Buginese'


class BUL:
    ISO_639_1 = ''
    ISO_639 = 'bul'
    ENGLISH_NAME = 'Bulgarian'


class BUR:
    ISO_639_1 = ''
    ISO_639 = 'bur'
    ENGLISH_NAME = 'Burmese'


class BYN:
    ISO_639_1 = ''
    ISO_639 = 'byn'
    ENGLISH_NAME = 'Blin'


class CAD:
    ISO_639_1 = ''
    ISO_639 = 'cad'
    ENGLISH_NAME = 'Caddo'


class CAR:
    ISO_639_1 = ''
    ISO_639 = 'car'
    ENGLISH_NAME = 'GalibiCarib'


class CAT:
    ISO_639_1 = ''
    ISO_639 = 'cat'
    ENGLISH_NAME = 'Catalan'


class CEB:
    ISO_639_1 = ''
    ISO_639 = 'ceb'
    ENGLISH_NAME = 'Cebuano'


class CHA:
    ISO_639_1 = ''
    ISO_639 = 'cha'
    ENGLISH_NAME = 'Chamorro'


class CHB:
    ISO_639_1 = ''
    ISO_639 = 'chb'
    ENGLISH_NAME = 'Chibcha'


class CHE:
    ISO_639_1 = ''
    ISO_639 = 'che'
    ENGLISH_NAME = 'Chechen'


class CHG:
    ISO_639_1 = ''
    ISO_639 = 'chg'
    ENGLISH_NAME = 'Chagatai'


class CHI:
    ISO_639_1 = 'zh'
    ISO_639 = 'chi'
    ENGLISH_NAME = 'Chinese'


class CHK:
    ISO_639_1 = ''
    ISO_639 = 'chk'
    ENGLISH_NAME = 'Chuukese'


class CHM:
    ISO_639_1 = ''
    ISO_639 = 'chm'
    ENGLISH_NAME = 'Mari'


class CHN:
    ISO_639_1 = ''
    ISO_639 = 'chn'
    ENGLISH_NAME = 'Chinookjargon'


class CHO:
    ISO_639_1 = ''
    ISO_639 = 'cho'
    ENGLISH_NAME = 'Choctaw'


class CHP:
    ISO_639_1 = ''
    ISO_639 = 'chp'
    ENGLISH_NAME = 'Chipewyan'


class CHR:
    ISO_639_1 = ''
    ISO_639 = 'chr'
    ENGLISH_NAME = 'Cherokee'


class CHV:
    ISO_639_1 = ''
    ISO_639 = 'chv'
    ENGLISH_NAME = 'Chuvash'


class CHY:
    ISO_639_1 = ''
    ISO_639 = 'chy'
    ENGLISH_NAME = 'Cheyenne'


class CNR:
    ISO_639_1 = ''
    ISO_639 = 'cnr'
    ENGLISH_NAME = 'Montenegrin'


class COP:
    ISO_639_1 = ''
    ISO_639 = 'cop'
    ENGLISH_NAME = 'Coptic'


class COR:
    ISO_639_1 = ''
    ISO_639 = 'cor'
    ENGLISH_NAME = 'Cornish'


class COS:
    ISO_639_1 = ''
    ISO_639 = 'cos'
    ENGLISH_NAME = 'Corsican'


class CPE:
    ISO_639_1 = ''
    ISO_639 = 'cpe'
    ENGLISH_NAME = 'Creolesandpidgins'


class CPF:
    ISO_639_1 = ''
    ISO_639 = 'cpf'
    ENGLISH_NAME = 'Creolesandpidgins'


class CPP:
    ISO_639_1 = ''
    ISO_639 = 'cpp'
    ENGLISH_NAME = 'Creolesandpidgins'


class CRE:
    ISO_639_1 = ''
    ISO_639 = 'cre'
    ENGLISH_NAME = 'Cree'


class CRH:
    ISO_639_1 = ''
    ISO_639 = 'crh'
    ENGLISH_NAME = 'CrimeanTatar'


class CRP:
    ISO_639_1 = ''
    ISO_639 = 'crp'
    ENGLISH_NAME = 'Creolesandpidgins'


class CSB:
    ISO_639_1 = ''
    ISO_639 = 'csb'
    ENGLISH_NAME = 'Kashubian'


class CZE:
    ISO_639_1 = ''
    ISO_639 = 'cze'
    ENGLISH_NAME = 'Czech'


class DAK:
    ISO_639_1 = ''
    ISO_639 = 'dak'
    ENGLISH_NAME = 'Dakota'


class DAN:
    ISO_639_1 = ''
    ISO_639 = 'dan'
    ENGLISH_NAME = 'Danish'


class DAR:
    ISO_639_1 = ''
    ISO_639 = 'dar'
    ENGLISH_NAME = 'Dargwa'


class DEL:
    ISO_639_1 = ''
    ISO_639 = 'del'
    ENGLISH_NAME = 'Delaware'


class DEN:
    ISO_639_1 = ''
    ISO_639 = 'den'
    ENGLISH_NAME = 'Slave'


class DGR:
    ISO_639_1 = ''
    ISO_639 = 'dgr'
    ENGLISH_NAME = 'Dogrib'


class DIN:
    ISO_639_1 = ''
    ISO_639 = 'din'
    ENGLISH_NAME = 'Dinka'


class DIV:
    ISO_639_1 = ''
    ISO_639 = 'div'
    ENGLISH_NAME = 'Divehi'


class DOI:
    ISO_639_1 = ''
    ISO_639 = 'doi'
    ENGLISH_NAME = 'Dogri'


class DUA:
    ISO_639_1 = ''
    ISO_639 = 'dua'
    ENGLISH_NAME = 'Duala'


class DUT:
    ISO_639_1 = 'nl'
    ISO_639 = 'dut'
    ENGLISH_NAME = 'Dutch'


class DYU:
    ISO_639_1 = ''
    ISO_639 = 'dyu'
    ENGLISH_NAME = 'Dyula'


class DZO:
    ISO_639_1 = ''
    ISO_639 = 'dzo'
    ENGLISH_NAME = 'Dzongkha'


class EFI:
    ISO_639_1 = ''
    ISO_639 = 'efi'
    ENGLISH_NAME = 'Efik'


class EKA:
    ISO_639_1 = ''
    ISO_639 = 'eka'
    ENGLISH_NAME = 'Ekajuk'


class ELX:
    ISO_639_1 = ''
    ISO_639 = 'elx'
    ENGLISH_NAME = 'Elamite'


class ENG:
    ISO_639_1 = 'en'
    ISO_639 = 'eng'
    ENGLISH_NAME = 'English'


class EPO:
    ISO_639_1 = ''
    ISO_639 = 'epo'
    ENGLISH_NAME = 'Esperanto'


class EST:
    ISO_639_1 = ''
    ISO_639 = 'est'
    ENGLISH_NAME = 'Estonian'


class EWE:
    ISO_639_1 = ''
    ISO_639 = 'ewe'
    ENGLISH_NAME = 'Ewe'


class EWO:
    ISO_639_1 = ''
    ISO_639 = 'ewo'
    ENGLISH_NAME = 'Ewondo'


class FAN:
    ISO_639_1 = ''
    ISO_639 = 'fan'
    ENGLISH_NAME = 'Fang'


class FAO:
    ISO_639_1 = ''
    ISO_639 = 'fao'
    ENGLISH_NAME = 'Faroese'


class FAT:
    ISO_639_1 = ''
    ISO_639 = 'fat'
    ENGLISH_NAME = 'Fanti'


class FIJ:
    ISO_639_1 = ''
    ISO_639 = 'fij'
    ENGLISH_NAME = 'Fijian'


class FIL:
    ISO_639_1 = ''
    ISO_639 = 'fil'
    ENGLISH_NAME = 'Filipino'


class FIN:
    ISO_639_1 = ''
    ISO_639 = 'fin'
    ENGLISH_NAME = 'Finnish'


class FON:
    ISO_639_1 = ''
    ISO_639 = 'fon'
    ENGLISH_NAME = 'Fon'


class FRE:
    ISO_639_1 = ''
    ISO_639 = 'fre'
    ENGLISH_NAME = 'French'


class FRR:
    ISO_639_1 = ''
    ISO_639 = 'frr'
    ENGLISH_NAME = 'NorthernFrisian'


class FRS:
    ISO_639_1 = ''
    ISO_639 = 'frs'
    ENGLISH_NAME = 'EasternFrisian'


class FRY:
    ISO_639_1 = ''
    ISO_639 = 'fry'
    ENGLISH_NAME = 'WesternFrisian'


class FUL:
    ISO_639_1 = ''
    ISO_639 = 'ful'
    ENGLISH_NAME = 'Fulah'


class FUR:
    ISO_639_1 = ''
    ISO_639 = 'fur'
    ENGLISH_NAME = 'Friulian'


class GAA:
    ISO_639_1 = ''
    ISO_639 = 'gaa'
    ENGLISH_NAME = 'Ga'


class GAY:
    ISO_639_1 = ''
    ISO_639 = 'gay'
    ENGLISH_NAME = 'Gayo'


class GBA:
    ISO_639_1 = ''
    ISO_639 = 'gba'
    ENGLISH_NAME = 'Gbaya'


class GEO:
    ISO_639_1 = ''
    ISO_639 = 'geo'
    ENGLISH_NAME = 'Georgian'


class GER:
    ISO_639_1 = 'de'
    ISO_639 = 'ger'
    ENGLISH_NAME = 'German'


class GEZ:
    ISO_639_1 = ''
    ISO_639 = 'gez'
    ENGLISH_NAME = 'Geez'


class GIL:
    ISO_639_1 = ''
    ISO_639 = 'gil'
    ENGLISH_NAME = 'Gilbertese'


class GLA:
    ISO_639_1 = ''
    ISO_639 = 'gla'
    ENGLISH_NAME = 'Gaelic'


class GLE:
    ISO_639_1 = ''
    ISO_639 = 'gle'
    ENGLISH_NAME = 'Irish'


class GLG:
    ISO_639_1 = ''
    ISO_639 = 'glg'
    ENGLISH_NAME = 'Galician'


class GLV:
    ISO_639_1 = ''
    ISO_639 = 'glv'
    ENGLISH_NAME = 'Manx'


class GON:
    ISO_639_1 = ''
    ISO_639 = 'gon'
    ENGLISH_NAME = 'Gondi'


class GOR:
    ISO_639_1 = ''
    ISO_639 = 'gor'
    ENGLISH_NAME = 'Gorontalo'


class GOT:
    ISO_639_1 = ''
    ISO_639 = 'got'
    ENGLISH_NAME = 'Gothic'


class GRB:
    ISO_639_1 = ''
    ISO_639 = 'grb'
    ENGLISH_NAME = 'Grebo'


class GRE:
    ISO_639_1 = 'el'
    ISO_639 = 'gre'
    ENGLISH_NAME = 'Greek'


class GRN:
    ISO_639_1 = ''
    ISO_639 = 'grn'
    ENGLISH_NAME = 'Guarani'


class GSW:
    ISO_639_1 = ''
    ISO_639 = 'gsw'
    ENGLISH_NAME = 'SwissGerman'


class GUJ:
    ISO_639_1 = ''
    ISO_639 = 'guj'
    ENGLISH_NAME = 'Gujarati'


class GWI:
    ISO_639_1 = ''
    ISO_639 = 'gwi'
    ENGLISH_NAME = 'Gwichin'


class HAI:
    ISO_639_1 = ''
    ISO_639 = 'hai'
    ENGLISH_NAME = 'Haida'


class HAT:
    ISO_639_1 = ''
    ISO_639 = 'hat'
    ENGLISH_NAME = 'Haitian'


class HAU:
    ISO_639_1 = ''
    ISO_639 = 'hau'
    ENGLISH_NAME = 'Hausa'


class HAW:
    ISO_639_1 = ''
    ISO_639 = 'haw'
    ENGLISH_NAME = 'Hawaiian'


class HEB:
    ISO_639_1 = 'he'
    ISO_639 = 'heb'
    ENGLISH_NAME = 'Hebrew'


class HER:
    ISO_639_1 = ''
    ISO_639 = 'her'
    ENGLISH_NAME = 'Herero'


class HIL:
    ISO_639_1 = ''
    ISO_639 = 'hil'
    ENGLISH_NAME = 'Hiligaynon'


class HIN:
    ISO_639_1 = 'hi'
    ISO_639 = 'hin'
    ENGLISH_NAME = 'Hindi'


class HIT:
    ISO_639_1 = ''
    ISO_639 = 'hit'
    ENGLISH_NAME = 'Hittite'


class HMN:
    ISO_639_1 = ''
    ISO_639 = 'hmn'
    ENGLISH_NAME = 'Hmong'


class HMO:
    ISO_639_1 = ''
    ISO_639 = 'hmo'
    ENGLISH_NAME = 'HiriMotu'


class HRV:
    ISO_639_1 = ''
    ISO_639 = 'hrv'
    ENGLISH_NAME = 'Croatian'


class HSB:
    ISO_639_1 = ''
    ISO_639 = 'hsb'
    ENGLISH_NAME = 'UpperSorbian'


class HUN:
    ISO_639_1 = ''
    ISO_639 = 'hun'
    ENGLISH_NAME = 'Hungarian'


class HUP:
    ISO_639_1 = ''
    ISO_639 = 'hup'
    ENGLISH_NAME = 'Hupa'


class IBA:
    ISO_639_1 = ''
    ISO_639 = 'iba'
    ENGLISH_NAME = 'Iban'


class IBO:
    ISO_639_1 = ''
    ISO_639 = 'ibo'
    ENGLISH_NAME = 'Igbo'


class ICE:
    ISO_639_1 = ''
    ISO_639 = 'ice'
    ENGLISH_NAME = 'Icelandic'


class IDO:
    ISO_639_1 = ''
    ISO_639 = 'ido'
    ENGLISH_NAME = 'Ido'


class III:
    ISO_639_1 = ''
    ISO_639 = 'iii'
    ENGLISH_NAME = 'SichuanYi'


class IKU:
    ISO_639_1 = ''
    ISO_639 = 'iku'
    ENGLISH_NAME = 'Inuktitut'


class ILE:
    ISO_639_1 = ''
    ISO_639 = 'ile'
    ENGLISH_NAME = 'Interlingue'


class ILO:
    ISO_639_1 = ''
    ISO_639 = 'ilo'
    ENGLISH_NAME = 'Iloko'


class INA:
    ISO_639_1 = ''
    ISO_639 = 'ina'
    ENGLISH_NAME = 'Interlingua'


class IND:
    ISO_639_1 = 'id'
    ISO_639 = 'ind'
    ENGLISH_NAME = 'Indonesian'


class INH:
    ISO_639_1 = ''
    ISO_639 = 'inh'
    ENGLISH_NAME = 'Ingush'


class IPK:
    ISO_639_1 = ''
    ISO_639 = 'ipk'
    ENGLISH_NAME = 'Inupiaq'


class ITA:
    ISO_639_1 = ''
    ISO_639 = 'ita'
    ENGLISH_NAME = 'Italian'


class JAV:
    ISO_639_1 = ''
    ISO_639 = 'jav'
    ENGLISH_NAME = 'Javanese'


class JBO:
    ISO_639_1 = ''
    ISO_639 = 'jbo'
    ENGLISH_NAME = 'Lojban'


class JPN:
    ISO_639_1 = 'ja'
    ISO_639 = 'jpn'
    ENGLISH_NAME = 'Japanese'


class JPR:
    ISO_639_1 = ''
    ISO_639 = 'jpr'
    ENGLISH_NAME = 'JudeoPersian'


class JRB:
    ISO_639_1 = ''
    ISO_639 = 'jrb'
    ENGLISH_NAME = 'JudeoArabic'


class KAA:
    ISO_639_1 = ''
    ISO_639 = 'kaa'
    ENGLISH_NAME = 'KaraKalpak'


class KAB:
    ISO_639_1 = ''
    ISO_639 = 'kab'
    ENGLISH_NAME = 'Kabyle'


class KAC:
    ISO_639_1 = ''
    ISO_639 = 'kac'
    ENGLISH_NAME = 'Kachin'


class KAL:
    ISO_639_1 = ''
    ISO_639 = 'kal'
    ENGLISH_NAME = 'Kalaallisut'


class KAM:
    ISO_639_1 = ''
    ISO_639 = 'kam'
    ENGLISH_NAME = 'Kamba'


class KAN:
    ISO_639_1 = ''
    ISO_639 = 'kan'
    ENGLISH_NAME = 'Kannada'


class KAS:
    ISO_639_1 = ''
    ISO_639 = 'kas'
    ENGLISH_NAME = 'Kashmiri'


class KAU:
    ISO_639_1 = ''
    ISO_639 = 'kau'
    ENGLISH_NAME = 'Kanuri'


class KAW:
    ISO_639_1 = ''
    ISO_639 = 'kaw'
    ENGLISH_NAME = 'Kawi'


class KAZ:
    ISO_639_1 = ''
    ISO_639 = 'kaz'
    ENGLISH_NAME = 'Kazakh'


class KBD:
    ISO_639_1 = ''
    ISO_639 = 'kbd'
    ENGLISH_NAME = 'Kabardian'


class KHA:
    ISO_639_1 = ''
    ISO_639 = 'kha'
    ENGLISH_NAME = 'Khasi'


class KHM:
    ISO_639_1 = ''
    ISO_639 = 'khm'
    ENGLISH_NAME = 'CentralKhmer'


class KHO:
    ISO_639_1 = ''
    ISO_639 = 'kho'
    ENGLISH_NAME = 'Khotanese'


class KIK:
    ISO_639_1 = ''
    ISO_639 = 'kik'
    ENGLISH_NAME = 'Kikuyu'


class KIN:
    ISO_639_1 = ''
    ISO_639 = 'kin'
    ENGLISH_NAME = 'Kinyarwanda'


class KIR:
    ISO_639_1 = ''
    ISO_639 = 'kir'
    ENGLISH_NAME = 'Kirghiz'


class KMB:
    ISO_639_1 = ''
    ISO_639 = 'kmb'
    ENGLISH_NAME = 'Kimbundu'


class KOK:
    ISO_639_1 = ''
    ISO_639 = 'kok'
    ENGLISH_NAME = 'Konkani'


class KOM:
    ISO_639_1 = ''
    ISO_639 = 'kom'
    ENGLISH_NAME = 'Komi'


class KON:
    ISO_639_1 = ''
    ISO_639 = 'kon'
    ENGLISH_NAME = 'Kongo'


class KOR:
    ISO_639_1 = 'ko'
    ISO_639 = 'kor'
    ENGLISH_NAME = 'Korean'


class KOS:
    ISO_639_1 = ''
    ISO_639 = 'kos'
    ENGLISH_NAME = 'Kosraean'


class KPE:
    ISO_639_1 = ''
    ISO_639 = 'kpe'
    ENGLISH_NAME = 'Kpelle'


class KRC:
    ISO_639_1 = ''
    ISO_639 = 'krc'
    ENGLISH_NAME = 'KarachayBalkar'


class KRL:
    ISO_639_1 = ''
    ISO_639 = 'krl'
    ENGLISH_NAME = 'Karelian'


class KRU:
    ISO_639_1 = ''
    ISO_639 = 'kru'
    ENGLISH_NAME = 'Kurukh'


class KUA:
    ISO_639_1 = ''
    ISO_639 = 'kua'
    ENGLISH_NAME = 'Kuanyama'


class KUM:
    ISO_639_1 = ''
    ISO_639 = 'kum'
    ENGLISH_NAME = 'Kumyk'


class KUR:
    ISO_639_1 = ''
    ISO_639 = 'kur'
    ENGLISH_NAME = 'Kurdish'


class KUT:
    ISO_639_1 = ''
    ISO_639 = 'kut'
    ENGLISH_NAME = 'Kutenai'


class LAD:
    ISO_639_1 = ''
    ISO_639 = 'lad'
    ENGLISH_NAME = 'Ladino'


class LAH:
    ISO_639_1 = ''
    ISO_639 = 'lah'
    ENGLISH_NAME = 'Lahnda'


class LAM:
    ISO_639_1 = ''
    ISO_639 = 'lam'
    ENGLISH_NAME = 'Lamba'


class LAO:
    ISO_639_1 = ''
    ISO_639 = 'lao'
    ENGLISH_NAME = 'Lao'


class LAT:
    ISO_639_1 = ''
    ISO_639 = 'lat'
    ENGLISH_NAME = 'Latin'


class LAV:
    ISO_639_1 = ''
    ISO_639 = 'lav'
    ENGLISH_NAME = 'Latvian'


class LEZ:
    ISO_639_1 = ''
    ISO_639 = 'lez'
    ENGLISH_NAME = 'Lezghian'


class LIM:
    ISO_639_1 = ''
    ISO_639 = 'lim'
    ENGLISH_NAME = 'Limburgan'


class LIN:
    ISO_639_1 = ''
    ISO_639 = 'lin'
    ENGLISH_NAME = 'Lingala'


class LIT:
    ISO_639_1 = ''
    ISO_639 = 'lit'
    ENGLISH_NAME = 'Lithuanian'


class LOL:
    ISO_639_1 = ''
    ISO_639 = 'lol'
    ENGLISH_NAME = 'Mongo'


class LOZ:
    ISO_639_1 = ''
    ISO_639 = 'loz'
    ENGLISH_NAME = 'Lozi'


class LTZ:
    ISO_639_1 = ''
    ISO_639 = 'ltz'
    ENGLISH_NAME = 'Luxembourgish'


class LUA:
    ISO_639_1 = ''
    ISO_639 = 'lua'
    ENGLISH_NAME = 'LubaLulua'


class LUB:
    ISO_639_1 = ''
    ISO_639 = 'lub'
    ENGLISH_NAME = 'LubaKatanga'


class LUG:
    ISO_639_1 = ''
    ISO_639 = 'lug'
    ENGLISH_NAME = 'Ganda'


class LUI:
    ISO_639_1 = ''
    ISO_639 = 'lui'
    ENGLISH_NAME = 'Luiseno'


class LUN:
    ISO_639_1 = ''
    ISO_639 = 'lun'
    ENGLISH_NAME = 'Lunda'


class LUO:
    ISO_639_1 = ''
    ISO_639 = 'luo'
    ENGLISH_NAME = 'Luo'


class LUS:
    ISO_639_1 = ''
    ISO_639 = 'lus'
    ENGLISH_NAME = 'Lushai'


class MAC:
    ISO_639_1 = ''
    ISO_639 = 'mac'
    ENGLISH_NAME = 'Macedonian'


class MAD:
    ISO_639_1 = ''
    ISO_639 = 'mad'
    ENGLISH_NAME = 'Madurese'


class MAG:
    ISO_639_1 = ''
    ISO_639 = 'mag'
    ENGLISH_NAME = 'Magahi'


class MAH:
    ISO_639_1 = ''
    ISO_639 = 'mah'
    ENGLISH_NAME = 'Marshallese'


class MAI:
    ISO_639_1 = ''
    ISO_639 = 'mai'
    ENGLISH_NAME = 'Maithili'


class MAK:
    ISO_639_1 = ''
    ISO_639 = 'mak'
    ENGLISH_NAME = 'Makasar'


class MAL:
    ISO_639_1 = ''
    ISO_639 = 'mal'
    ENGLISH_NAME = 'Malayalam'


class MAN:
    ISO_639_1 = ''
    ISO_639 = 'man'
    ENGLISH_NAME = 'Mandingo'


class MAO:
    ISO_639_1 = ''
    ISO_639 = 'mao'
    ENGLISH_NAME = 'Maori'


class MAR:
    ISO_639_1 = 'mr'
    ISO_639 = 'mar'
    ENGLISH_NAME = 'Marathi'


class MAS:
    ISO_639_1 = ''
    ISO_639 = 'mas'
    ENGLISH_NAME = 'Masai'


class MAY:
    ISO_639_1 = ''
    ISO_639 = 'may'
    ENGLISH_NAME = 'Malay'


class MDF:
    ISO_639_1 = ''
    ISO_639 = 'mdf'
    ENGLISH_NAME = 'Moksha'


class MDR:
    ISO_639_1 = ''
    ISO_639 = 'mdr'
    ENGLISH_NAME = 'Mandar'


class MEN:
    ISO_639_1 = ''
    ISO_639 = 'men'
    ENGLISH_NAME = 'Mende'


class MIC:
    ISO_639_1 = ''
    ISO_639 = 'mic'
    ENGLISH_NAME = 'Mikmaq'


class MIN:
    ISO_639_1 = ''
    ISO_639 = 'min'
    ENGLISH_NAME = 'Minangkabau'


class MLG:
    ISO_639_1 = ''
    ISO_639 = 'mlg'
    ENGLISH_NAME = 'Malagasy'


class MLT:
    ISO_639_1 = ''
    ISO_639 = 'mlt'
    ENGLISH_NAME = 'Maltese'


class MNC:
    ISO_639_1 = ''
    ISO_639 = 'mnc'
    ENGLISH_NAME = 'Manchu'


class MNI:
    ISO_639_1 = ''
    ISO_639 = 'mni'
    ENGLISH_NAME = 'Manipuri'


class MOH:
    ISO_639_1 = ''
    ISO_639 = 'moh'
    ENGLISH_NAME = 'Mohawk'


class MON:
    ISO_639_1 = ''
    ISO_639 = 'mon'
    ENGLISH_NAME = 'Mongolian'


class MOS:
    ISO_639_1 = ''
    ISO_639 = 'mos'
    ENGLISH_NAME = 'Mossi'


class MUS:
    ISO_639_1 = ''
    ISO_639 = 'mus'
    ENGLISH_NAME = 'Creek'


class MWL:
    ISO_639_1 = ''
    ISO_639 = 'mwl'
    ENGLISH_NAME = 'Mirandese'


class MWR:
    ISO_639_1 = ''
    ISO_639 = 'mwr'
    ENGLISH_NAME = 'Marwari'


class MYV:
    ISO_639_1 = ''
    ISO_639 = 'myv'
    ENGLISH_NAME = 'Erzya'


class NAP:
    ISO_639_1 = ''
    ISO_639 = 'nap'
    ENGLISH_NAME = 'Neapolitan'


class NAU:
    ISO_639_1 = ''
    ISO_639 = 'nau'
    ENGLISH_NAME = 'Nauru'


class NAV:
    ISO_639_1 = ''
    ISO_639 = 'nav'
    ENGLISH_NAME = 'Navajo'


class NBL:
    ISO_639_1 = ''
    ISO_639 = 'nbl'
    ENGLISH_NAME = 'Ndebele'


class NDE:
    ISO_639_1 = ''
    ISO_639 = 'nde'
    ENGLISH_NAME = 'Ndebele'


class NDO:
    ISO_639_1 = ''
    ISO_639 = 'ndo'
    ENGLISH_NAME = 'Ndonga'


class NEP:
    ISO_639_1 = ''
    ISO_639 = 'nep'
    ENGLISH_NAME = 'Nepali'


class NEW:
    ISO_639_1 = ''
    ISO_639 = 'new'
    ENGLISH_NAME = 'NepalBhasa'


class NIA:
    ISO_639_1 = ''
    ISO_639 = 'nia'
    ENGLISH_NAME = 'Nias'


class NIU:
    ISO_639_1 = ''
    ISO_639 = 'niu'
    ENGLISH_NAME = 'Niuean'


class NNO:
    ISO_639_1 = ''
    ISO_639 = 'nno'
    ENGLISH_NAME = 'NorwegianNynorsk'


class NOB:
    ISO_639_1 = ''
    ISO_639 = 'nob'
    ENGLISH_NAME = 'Bokmål'


class NOG:
    ISO_639_1 = ''
    ISO_639 = 'nog'
    ENGLISH_NAME = 'Nogai'


class NOR:
    ISO_639_1 = ''
    ISO_639 = 'nor'
    ENGLISH_NAME = 'Norwegian'


class NQO:
    ISO_639_1 = ''
    ISO_639 = 'nqo'
    ENGLISH_NAME = 'NKo'


class NSO:
    ISO_639_1 = ''
    ISO_639 = 'nso'
    ENGLISH_NAME = 'Pedi'


class NYA:
    ISO_639_1 = ''
    ISO_639 = 'nya'
    ENGLISH_NAME = 'Chichewa'


class NYM:
    ISO_639_1 = ''
    ISO_639 = 'nym'
    ENGLISH_NAME = 'Nyamwezi'


class NYN:
    ISO_639_1 = ''
    ISO_639 = 'nyn'
    ENGLISH_NAME = 'Nyankole'


class NYO:
    ISO_639_1 = ''
    ISO_639 = 'nyo'
    ENGLISH_NAME = 'Nyoro'


class NZI:
    ISO_639_1 = ''
    ISO_639 = 'nzi'
    ENGLISH_NAME = 'Nzima'


class OJI:
    ISO_639_1 = ''
    ISO_639 = 'oji'
    ENGLISH_NAME = 'Ojibwa'


class ORI:
    ISO_639_1 = 'or'
    ISO_639 = 'ori'
    ENGLISH_NAME = 'Oriya'


class ORM:
    ISO_639_1 = ''
    ISO_639 = 'orm'
    ENGLISH_NAME = 'Oromo'


class OSA:
    ISO_639_1 = ''
    ISO_639 = 'osa'
    ENGLISH_NAME = 'Osage'


class OSS:
    ISO_639_1 = ''
    ISO_639 = 'oss'
    ENGLISH_NAME = 'Ossetian'


class PAG:
    ISO_639_1 = ''
    ISO_639 = 'pag'
    ENGLISH_NAME = 'Pangasinan'


class PAL:
    ISO_639_1 = ''
    ISO_639 = 'pal'
    ENGLISH_NAME = 'Pahlavi'


class PAM:
    ISO_639_1 = ''
    ISO_639 = 'pam'
    ENGLISH_NAME = 'Pampanga'


class PAN:
    ISO_639_1 = ''
    ISO_639 = 'pan'
    ENGLISH_NAME = 'Panjabi'


class PAP:
    ISO_639_1 = ''
    ISO_639 = 'pap'
    ENGLISH_NAME = 'Papiamento'


class PAU:
    ISO_639_1 = ''
    ISO_639 = 'pau'
    ENGLISH_NAME = 'Palauan'


class PER:
    ISO_639_1 = 'fa'
    ISO_639 = 'per'
    ENGLISH_NAME = 'Persian'


class PHN:
    ISO_639_1 = ''
    ISO_639 = 'phn'
    ENGLISH_NAME = 'Phoenician'


class PLI:
    ISO_639_1 = ''
    ISO_639 = 'pli'
    ENGLISH_NAME = 'Pali'


class POL:
    ISO_639_1 = ''
    ISO_639 = 'pol'
    ENGLISH_NAME = 'Polish'


class PON:
    ISO_639_1 = ''
    ISO_639 = 'pon'
    ENGLISH_NAME = 'Pohnpeian'


class POR:
    ISO_639_1 = 'pt'
    ISO_639 = 'por'
    ENGLISH_NAME = 'Portuguese'


class PUS:
    ISO_639_1 = ''
    ISO_639 = 'pus'
    ENGLISH_NAME = 'Pushto'


class QUE:
    ISO_639_1 = ''
    ISO_639 = 'que'
    ENGLISH_NAME = 'Quechua'


class RAJ:
    ISO_639_1 = ''
    ISO_639 = 'raj'
    ENGLISH_NAME = 'Rajasthani'


class RAP:
    ISO_639_1 = ''
    ISO_639 = 'rap'
    ENGLISH_NAME = 'Rapanui'


class RAR:
    ISO_639_1 = ''
    ISO_639 = 'rar'
    ENGLISH_NAME = 'Rarotongan'


class ROH:
    ISO_639_1 = ''
    ISO_639 = 'roh'
    ENGLISH_NAME = 'Romansh'


class ROM:
    ISO_639_1 = ''
    ISO_639 = 'rom'
    ENGLISH_NAME = 'Romany'


class RUM:
    ISO_639_1 = ''
    ISO_639 = 'rum'
    ENGLISH_NAME = 'Romanian'


class RUN:
    ISO_639_1 = ''
    ISO_639 = 'run'
    ENGLISH_NAME = 'Rundi'


class RUP:
    ISO_639_1 = ''
    ISO_639 = 'rup'
    ENGLISH_NAME = 'Aromanian'


class RUS:
    ISO_639_1 = 'ru'
    ISO_639 = 'rus'
    ENGLISH_NAME = 'Russian'


class SAD:
    ISO_639_1 = ''
    ISO_639 = 'sad'
    ENGLISH_NAME = 'Sandawe'


class SAG:
    ISO_639_1 = ''
    ISO_639 = 'sag'
    ENGLISH_NAME = 'Sango'


class SAH:
    ISO_639_1 = ''
    ISO_639 = 'sah'
    ENGLISH_NAME = 'Yakut'


class SAM:
    ISO_639_1 = ''
    ISO_639 = 'sam'
    ENGLISH_NAME = 'SamaritanAramaic'


class SAN:
    ISO_639_1 = ''
    ISO_639 = 'san'
    ENGLISH_NAME = 'Sanskrit'


class SAS:
    ISO_639_1 = ''
    ISO_639 = 'sas'
    ENGLISH_NAME = 'Sasak'


class SAT:
    ISO_639_1 = ''
    ISO_639 = 'sat'
    ENGLISH_NAME = 'Santali'


class SCN:
    ISO_639_1 = ''
    ISO_639 = 'scn'
    ENGLISH_NAME = 'Sicilian'


class SCO:
    ISO_639_1 = ''
    ISO_639 = 'sco'
    ENGLISH_NAME = 'Scots'


class SEL:
    ISO_639_1 = ''
    ISO_639 = 'sel'
    ENGLISH_NAME = 'Selkup'


class SHN:
    ISO_639_1 = ''
    ISO_639 = 'shn'
    ENGLISH_NAME = 'Shan'


class SID:
    ISO_639_1 = ''
    ISO_639 = 'sid'
    ENGLISH_NAME = 'Sidamo'


class SIN:
    ISO_639_1 = ''
    ISO_639 = 'sin'
    ENGLISH_NAME = 'Sinhala'


class SLO:
    ISO_639_1 = ''
    ISO_639 = 'slo'
    ENGLISH_NAME = 'Slovak'


class SLV:
    ISO_639_1 = ''
    ISO_639 = 'slv'
    ENGLISH_NAME = 'Slovenian'


class SMA:
    ISO_639_1 = ''
    ISO_639 = 'sma'
    ENGLISH_NAME = 'SouthernSami'


class SME:
    ISO_639_1 = ''
    ISO_639 = 'sme'
    ENGLISH_NAME = 'NorthernSami'


class SMJ:
    ISO_639_1 = ''
    ISO_639 = 'smj'
    ENGLISH_NAME = 'LuleSami'


class SMN:
    ISO_639_1 = ''
    ISO_639 = 'smn'
    ENGLISH_NAME = 'InariSami'


class SMO:
    ISO_639_1 = ''
    ISO_639 = 'smo'
    ENGLISH_NAME = 'Samoan'


class SMS:
    ISO_639_1 = ''
    ISO_639 = 'sms'
    ENGLISH_NAME = 'SkoltSami'


class SNA:
    ISO_639_1 = ''
    ISO_639 = 'sna'
    ENGLISH_NAME = 'Shona'


class SND:
    ISO_639_1 = ''
    ISO_639 = 'snd'
    ENGLISH_NAME = 'Sindhi'


class SNK:
    ISO_639_1 = ''
    ISO_639 = 'snk'
    ENGLISH_NAME = 'Soninke'


class SOG:
    ISO_639_1 = ''
    ISO_639 = 'sog'
    ENGLISH_NAME = 'Sogdian'


class SOM:
    ISO_639_1 = ''
    ISO_639 = 'som'
    ENGLISH_NAME = 'Somali'


class SOT:
    ISO_639_1 = ''
    ISO_639 = 'sot'
    ENGLISH_NAME = 'Sotho'


class SPA:
    ISO_639_1 = 'es'
    ISO_639 = 'spa'
    ENGLISH_NAME = 'Spanish'


class SRD:
    ISO_639_1 = ''
    ISO_639 = 'srd'
    ENGLISH_NAME = 'Sardinian'


class SRN:
    ISO_639_1 = ''
    ISO_639 = 'srn'
    ENGLISH_NAME = 'SrananTongo'


class SRP:
    ISO_639_1 = ''
    ISO_639 = 'srp'
    ENGLISH_NAME = 'Serbian'


class SRR:
    ISO_639_1 = ''
    ISO_639 = 'srr'
    ENGLISH_NAME = 'Serer'


class SSW:
    ISO_639_1 = ''
    ISO_639 = 'ssw'
    ENGLISH_NAME = 'Swati'


class SUK:
    ISO_639_1 = ''
    ISO_639 = 'suk'
    ENGLISH_NAME = 'Sukuma'


class SUN:
    ISO_639_1 = ''
    ISO_639 = 'sun'
    ENGLISH_NAME = 'Sundanese'


class SUS:
    ISO_639_1 = ''
    ISO_639 = 'sus'
    ENGLISH_NAME = 'Susu'


class SUX:
    ISO_639_1 = ''
    ISO_639 = 'sux'
    ENGLISH_NAME = 'Sumerian'


class SWA:
    ISO_639_1 = ''
    ISO_639 = 'swa'
    ENGLISH_NAME = 'Swahili'


class SWE:
    ISO_639_1 = 'sv'
    ISO_639 = 'swe'
    ENGLISH_NAME = 'Swedish'


class SYC:
    ISO_639_1 = ''
    ISO_639 = 'syc'
    ENGLISH_NAME = 'ClassicalSyriac'


class SYR:
    ISO_639_1 = ''
    ISO_639 = 'syr'
    ENGLISH_NAME = 'Syriac'


class TAH:
    ISO_639_1 = 'th'
    ISO_639 = 'tah'
    ENGLISH_NAME = 'Tahitian'


class TAM:
    ISO_639_1 = ''
    ISO_639 = 'tam'
    ENGLISH_NAME = 'Tamil'


class TAT:
    ISO_639_1 = ''
    ISO_639 = 'tat'
    ENGLISH_NAME = 'Tatar'


class TEL:
    ISO_639_1 = 'te'
    ISO_639 = 'tel'
    ENGLISH_NAME = 'Telugu'


class TEM:
    ISO_639_1 = ''
    ISO_639 = 'tem'
    ENGLISH_NAME = 'Timne'


class TER:
    ISO_639_1 = ''
    ISO_639 = 'ter'
    ENGLISH_NAME = 'Tereno'


class TET:
    ISO_639_1 = ''
    ISO_639 = 'tet'
    ENGLISH_NAME = 'Tetum'


class TGK:
    ISO_639_1 = ''
    ISO_639 = 'tgk'
    ENGLISH_NAME = 'Tajik'


class TGL:
    ISO_639_1 = ''
    ISO_639 = 'tgl'
    ENGLISH_NAME = 'Tagalog'


class THA:
    ISO_639_1 = ''
    ISO_639 = 'tha'
    ENGLISH_NAME = 'Thai'


class TIB:
    ISO_639_1 = ''
    ISO_639 = 'tib'
    ENGLISH_NAME = 'Tibetan'


class TIG:
    ISO_639_1 = ''
    ISO_639 = 'tig'
    ENGLISH_NAME = 'Tigre'


class TIR:
    ISO_639_1 = ''
    ISO_639 = 'tir'
    ENGLISH_NAME = 'Tigrinya'


class TIV:
    ISO_639_1 = ''
    ISO_639 = 'tiv'
    ENGLISH_NAME = 'Tiv'


class TKL:
    ISO_639_1 = ''
    ISO_639 = 'tkl'
    ENGLISH_NAME = 'Tokelau'


class TLH:
    ISO_639_1 = ''
    ISO_639 = 'tlh'
    ENGLISH_NAME = 'Klingon'


class TLI:
    ISO_639_1 = ''
    ISO_639 = 'tli'
    ENGLISH_NAME = 'Tlingit'


class TMH:
    ISO_639_1 = ''
    ISO_639 = 'tmh'
    ENGLISH_NAME = 'Tamashek'


class TOG:
    ISO_639_1 = ''
    ISO_639 = 'tog'
    ENGLISH_NAME = 'Tonga'


class TON:
    ISO_639_1 = ''
    ISO_639 = 'ton'
    ENGLISH_NAME = 'Tonga'


class TPI:
    ISO_639_1 = ''
    ISO_639 = 'tpi'
    ENGLISH_NAME = 'TokPisin'


class TSI:
    ISO_639_1 = ''
    ISO_639 = 'tsi'
    ENGLISH_NAME = 'Tsimshian'


class TSN:
    ISO_639_1 = ''
    ISO_639 = 'tsn'
    ENGLISH_NAME = 'Tswana'


class TSO:
    ISO_639_1 = ''
    ISO_639 = 'tso'
    ENGLISH_NAME = 'Tsonga'


class TUK:
    ISO_639_1 = ''
    ISO_639 = 'tuk'
    ENGLISH_NAME = 'Turkmen'


class TUM:
    ISO_639_1 = ''
    ISO_639 = 'tum'
    ENGLISH_NAME = 'Tumbuka'


class TUR:
    ISO_639_1 = ''
    ISO_639 = 'tur'
    ENGLISH_NAME = 'Turkish'


class TVL:
    ISO_639_1 = ''
    ISO_639 = 'tvl'
    ENGLISH_NAME = 'Tuvalu'


class TWI:
    ISO_639_1 = ''
    ISO_639 = 'twi'
    ENGLISH_NAME = 'Twi'


class TYV:
    ISO_639_1 = ''
    ISO_639 = 'tyv'
    ENGLISH_NAME = 'Tuvinian'


class UDM:
    ISO_639_1 = ''
    ISO_639 = 'udm'
    ENGLISH_NAME = 'Udmurt'


class UGA:
    ISO_639_1 = ''
    ISO_639 = 'uga'
    ENGLISH_NAME = 'Ugaritic'


class UIG:
    ISO_639_1 = ''
    ISO_639 = 'uig'
    ENGLISH_NAME = 'Uighur'


class UKR:
    ISO_639_1 = ''
    ISO_639 = 'ukr'
    ENGLISH_NAME = 'Ukrainian'


class UMB:
    ISO_639_1 = ''
    ISO_639 = 'umb'
    ENGLISH_NAME = 'Umbundu'


class UND:
    ISO_639_1 = ''
    ISO_639 = 'und'
    ENGLISH_NAME = 'Undetermined'


class URD:
    ISO_639_1 = ''
    ISO_639 = 'urd'
    ENGLISH_NAME = 'Urdu'


class UZB:
    ISO_639_1 = ''
    ISO_639 = 'uzb'
    ENGLISH_NAME = 'Uzbek'


class VAI:
    ISO_639_1 = ''
    ISO_639 = 'vai'
    ENGLISH_NAME = 'Vai'


class VEN:
    ISO_639_1 = ''
    ISO_639 = 'ven'
    ENGLISH_NAME = 'Venda'


class VIE:
    ISO_639_1 = ''
    ISO_639 = 'vie'
    ENGLISH_NAME = 'Vietnamese'


class VOL:
    ISO_639_1 = ''
    ISO_639 = 'vol'
    ENGLISH_NAME = 'Volapük'


class VOT:
    ISO_639_1 = ''
    ISO_639 = 'vot'
    ENGLISH_NAME = 'Votic'


class WAL:
    ISO_639_1 = ''
    ISO_639 = 'wal'
    ENGLISH_NAME = 'Wolaitta'


class WAR:
    ISO_639_1 = ''
    ISO_639 = 'war'
    ENGLISH_NAME = 'Waray'


class WAS:
    ISO_639_1 = ''
    ISO_639 = 'was'
    ENGLISH_NAME = 'Washo'


class WEL:
    ISO_639_1 = ''
    ISO_639 = 'wel'
    ENGLISH_NAME = 'Welsh'


class WLN:
    ISO_639_1 = ''
    ISO_639 = 'wln'
    ENGLISH_NAME = 'Walloon'


class WOL:
    ISO_639_1 = ''
    ISO_639 = 'wol'
    ENGLISH_NAME = 'Wolof'


class XAL:
    ISO_639_1 = ''
    ISO_639 = 'xal'
    ENGLISH_NAME = 'Kalmyk'


class XHO:
    ISO_639_1 = ''
    ISO_639 = 'xho'
    ENGLISH_NAME = 'Xhosa'


class YAO:
    ISO_639_1 = ''
    ISO_639 = 'yao'
    ENGLISH_NAME = 'Yao'


class YAP:
    ISO_639_1 = ''
    ISO_639 = 'yap'
    ENGLISH_NAME = 'Yapese'


class YID:
    ISO_639_1 = ''
    ISO_639 = 'yid'
    ENGLISH_NAME = 'Yiddish'


class YOR:
    ISO_639_1 = ''
    ISO_639 = 'yor'
    ENGLISH_NAME = 'Yoruba'


class ZAP:
    ISO_639_1 = ''
    ISO_639 = 'zap'
    ENGLISH_NAME = 'Zapotec'


class ZBL:
    ISO_639_1 = ''
    ISO_639 = 'zbl'
    ENGLISH_NAME = 'Blissymbols'


class ZEN:
    ISO_639_1 = ''
    ISO_639 = 'zen'
    ENGLISH_NAME = 'Zenaga'


class ZGH:
    ISO_639_1 = ''
    ISO_639 = 'zgh'
    ENGLISH_NAME = 'StandardMoroccanTamazight'


class ZHA:
    ISO_639_1 = ''
    ISO_639 = 'zha'
    ENGLISH_NAME = 'Zhuang'


class ZHS:
    ISO_639_1 = ''
    ISO_639 = 'zhs'
    ENGLISH_NAME = 'SimplifiedChinese'


class ZHT:
    ISO_639_1 = ''
    ISO_639 = 'zht'
    ENGLISH_NAME = 'TraditionalChinese'


class ZUL:
    ISO_639_1 = ''
    ISO_639 = 'zul'
    ENGLISH_NAME = 'Zulu'


class ZUN:
    ISO_639_1 = ''
    ISO_639 = 'zun'
    ENGLISH_NAME = 'Zuni'


class ZZA:
    ISO_639_1 = ''
    ISO_639 = 'zza'
    ENGLISH_NAME = 'Zaza'


def get_language_classes():
    return inspect.getmembers(sys.modules[__name__], inspect.isclass)


================================================
FILE: chatterbot/logic/__init__.py
================================================
from chatterbot.logic.logic_adapter import LogicAdapter
from chatterbot.logic.best_match import BestMatch
from chatterbot.logic.mathematical_evaluation import MathematicalEvaluation
from chatterbot.logic.specific_response import SpecificResponseAdapter
from chatterbot.logic.time_adapter import TimeLogicAdapter
from chatterbot.logic.unit_conversion import UnitConversion
from chatterbot.logic.llm_adapters import (
    LLMLogicAdapter,
    OllamaLogicAdapter,
    OpenAILogicAdapter,
)


__all__ = (
    'LogicAdapter',
    'BestMatch',
    'MathematicalEvaluation',
    'SpecificResponseAdapter',
    'TimeLogicAdapter',
    'UnitConversion',
    'LLMLogicAdapter',
    'OllamaLogicAdapter',
    'OpenAILogicAdapter',
)


================================================
FILE: chatterbot/logic/best_match.py
================================================
from chatterbot.logic import LogicAdapter
from chatterbot.conversation import Statement
from chatterbot import filters


class BestMatch(LogicAdapter):
    """
    A logic adapter that returns a response based on known responses to
    the closest matches to the input statement.

    :param excluded_words:
        The excluded_words parameter allows a list of words to be set that will
        prevent the logic adapter from returning statements that have text
        containing any of those words. This can be useful for preventing your
        chat bot from saying swears when it is being demonstrated in front of
        an audience.
        Defaults to None
    :type excluded_words: list
    """

    def __init__(self, chatbot, **kwargs):
        super().__init__(chatbot, **kwargs)

        self.excluded_words = kwargs.get('excluded_words')

    def process(self, input_statement: Statement, additional_response_selection_parameters=None) -> Statement:

        # Get all statements that have a response text similar to the input statement
        search_results = self.search_algorithm.search(input_statement)

        # Use the input statement as the closest match if no other results are found
        input_statement.confidence = 0  # Use 0 confidence when no other results are found
        closest_match = input_statement

        # Search for the closest match to the input statement
        for result in search_results:
            closest_match = result

            # Stop searching if a match that is close enough is found
            if result.confidence >= self.maximum_similarity_threshold:
                break

        self.chatbot.logger.info('Selecting "{}" as a response to "{}" with a confidence of {}'.format(
            closest_match.text, input_statement.text, closest_match.confidence
        ))

        # Semantic vector search vs indexed text search have different architectures:
        #
        # For SQL with indexed text search:
        #   - Phase 1 finds a match based on string similarity (Levenshtein distance)
        #   - Phase 2 finds variations of that match to get diverse responses
        #   - This makes sense because you might have multiple instances of similar statements
        #     learned from different conversations that provide different response options
        #
        # For Redis with semantic vectors:
        #   - Phase 1 finds semantically similar responses using vector embeddings
        #   - The semantic similarity already captures the "closeness" we want
        #   - Phase 2 would be redundant - we already have the best semantic match
        #   - The vector search inherently considers the entire semantic space, not just
        #     exact string matches, so additional variation searching is unnecessary
        #
        # NOTE: This difference of functionality may need to be modified in the future
        # if the redis adapter is determined to benefit from a Phase 2 style response
        # selection. The main symptom that would drive such a change would be low
        # quality or repetitive responses when using semantic vector search.
        #
        # Therefore, semantic vector search returns the Phase 1 result directly.
        if self.search_algorithm.name == 'semantic_vector_search' and closest_match.confidence > 0:
            response = closest_match
            self.chatbot.logger.info('Using semantic search result directly: "{}"'.format(response.text))
        else:
            # For other search algorithms (indexed_text_search, text_search),
            # we need to find responses to the closest match
            recent_repeated_responses = filters.get_recent_repeated_responses(
                self.chatbot,
                input_statement.conversation
            )

            for index, recent_repeated_response in enumerate(recent_repeated_responses):
                self.chatbot.logger.info('{}. Excluding recent repeated response of "{}"'.format(
                    index, recent_repeated_response
                ))

            response_selection_parameters = {
                'search_text': closest_match.search_text,
                'persona_not_startswith': 'bot:',
                'exclude_text': recent_repeated_responses,
                'exclude_text_words': self.excluded_words
            }

            alternate_response_selection_parameters = {
                'search_in_response_to': input_statement.search_text or self.chatbot.tagger.get_text_index_string(
                    input_statement.text
                ),
                'persona_not_startswith': 'bot:',
                'exclude_text': recent_repeated_responses,
                'exclude_text_words': self.excluded_words
            }

            if additional_response_selection_parameters:
                response_selection_parameters.update(
                    additional_response_selection_parameters
                )
                alternate_response_selection_parameters.update(
                    additional_response_selection_parameters
                )

            # Get all statements with text similar to the closest match
            response_list = list(self.chatbot.storage.filter(**response_selection_parameters))

            if response_list:
                response = self.select_response(
                    input_statement,
                    response_list,
                    self.chatbot.storage
                )

                response.confidence = closest_match.confidence
                self.chatbot.logger.info('Selecting "{}" from {} optimal responses.'.format(
                    response.text,
                    len(response_list)
                ))
            else:
                '''
                The case where there was no responses returned for the selected match
                but a value exists for the statement the match is in response to.
                '''
                self.chatbot.logger.info('No responses found. Generating alternate response list.')

                alternate_response_list = list(self.chatbot.storage.filter(
                    **alternate_response_selection_parameters
                ))

                if alternate_response_list:
                    response = self.select_response(
                        input_statement,
                        alternate_response_list,
                        self.chatbot.storage
                    )

                    response.confidence = closest_match.confidence
                    self.chatbot.logger.info('Selected alternative response "{}" from {} options'.format(
                        response.text,
                        len(alternate_response_list)
                    ))
                else:
                    response = self.get_default_response(input_statement)
                    self.chatbot.logger.info('Using "%s" as a default response.', response.text)

        return response


================================================
FILE: chatterbot/logic/llm_adapters.py
================================================
"""
LLM Logic Adapters for ChatterBot.

This module provides logic adapters that integrate Large Language Models.
LLM adapters can use other logic adapters as tools via MCP (Model Context Protocol).
"""
import json
from typing import Any, Dict, List, Optional, Union

from chatterbot.logic.logic_adapter import LogicAdapter
from chatterbot.conversation import Statement
from chatterbot.logic.mcp_tools import (
    is_tool_adapter,
    convert_to_openai_tool_format,
    convert_to_ollama_tool_format
)
from chatterbot import utils


class LLMLogicAdapter(LogicAdapter):
    """
    Base class for Large Language Model logic adapters.

    .. warning::
        LLM logic adapters are experimental and may change in future releases.
        Tool calling functionality is still being refined and may have limitations.

    LLM adapters can participate in ChatterBot's consensus voting mechanism
    alongside traditional logic adapters. They can also use other logic
    adapters as tools through MCP.

    Configuration parameters:
        model (str): The LLM model name (required)
        host (str): API endpoint URL (optional, provider-specific default)
        logic_adapters_as_tools (list): List of logic adapters to expose as tools
        force_native_tools (bool): Force native tool calling (None=auto-detect)
        min_confidence (float): Minimum confidence for LLM responses (default: 0.5)
        max_confidence (float): Maximum confidence for LLM responses (default: 0.85)
        conversation_context_count (int): Number of previous statements to include (default: 5)
        system_message (str): Custom system message for the LLM

    Example:
        {
            'import_path': 'chatterbot.logic.OllamaLogicAdapter',
            'model': 'llama3.1',
            'logic_adapters_as_tools': [
                'chatterbot.logic.MathematicalEvaluation',
                'chatterbot.logic.TimeLogicAdapter'
            ],
            'min_confidence': 0.6,
            'max_confidence': 0.9
        }
    """

    def __init__(self, chatbot, **kwargs):
        super().__init__(chatbot, **kwargs)

        # Model configuration
        self.model = kwargs.get('model')
        if not self.model:
            raise ValueError("LLM logic adapters require a 'model' parameter")

        self.host = kwargs.get('host')

        # Confidence range for LLM responses (for consensus voting)
        self.min_confidence = kwargs.get('min_confidence', 0.5)
        self.max_confidence = kwargs.get('max_confidence', 0.85)

        # Conversation context
        self.conversation_context_count = kwargs.get('conversation_context_count', 5)

        # System message
        default_system_message = (
            "You are a helpful AI assistant engaged in a direct conversation. "
            "Address the person you're speaking with directly rather than referring to them in third person. "
            "Please keep responses concise, conversational, and under 1100 tokens."
        )

        # If tools are configured, enhance system message to clarify tool usage
        if kwargs.get('logic_adapters_as_tools'):
            default_system_message += (
                "\n\nYou have access to specialized tools that can help you answer certain types of questions. "
                "Use these tools when they would be helpful, but you should respond naturally to ALL questions, "
                "not just tool-related ones. For general conversation, greetings, or topics outside the tools' scope, "
                "respond directly without using tools."
            )

        self.system_message = kwargs.get('system_message', default_system_message)

        # Tool calling configuration
        self.force_native_tools = kwargs.get('force_native_tools', None)
        self.tool_registry = {}
        self._native_tools_supported = None  # Cached tool capability detection result

        # Initialize tool adapters if provided
        logic_adapters_as_tools = kwargs.get('logic_adapters_as_tools', [])
        if logic_adapters_as_tools:
            self._initialize_tool_adapters(logic_adapters_as_tools, **kwargs)
            # Detect tool capability once during initialization
            self._native_tools_supported = self._detect_tool_capability()

    def _initialize_tool_adapters(self, adapter_configs: List[Union[str, Dict]], **kwargs):
        """
        Initialize logic adapters to be used as tools.

        Args:
            adapter_configs: List of adapter import paths or config dicts
            **kwargs: Additional kwargs to pass to adapters
        """
        for adapter_config in adapter_configs:
            # Validate and initialize the adapter
            utils.validate_adapter_class(adapter_config, LogicAdapter)
            adapter = utils.initialize_class(adapter_config, self.chatbot, **kwargs)

            # Check if adapter supports tool functionality
            if is_tool_adapter(adapter):
                tool_name = adapter.get_tool_name()
                self.tool_registry[tool_name] = adapter
                self.chatbot.logger.info(
                    f"Registered tool: {tool_name} from {adapter.__class__.__name__}"
                )
            else:
                self.chatbot.logger.warning(
                    f"Adapter {adapter.__class__.__name__} does not implement MCPToolAdapter, skipping"
                )

    def _get_conversation_context(self, input_statement: Statement) -> List[Dict[str, str]]:
        """
        Retrieve previous conversation context from storage.

        .. note::
            Security Note: Conversation history is loaded from storage without modification.
            If you need to scan historical messages for security issues (e.g., context poisoning),
            override this method in a base class.

        Args:
            input_statement: The current input statement

        Returns:
            List of message dicts in LLM format
        """
        messages = []

        if not input_statement.conversation:
            return messages

        try:
            # Query storage for recent statements in this conversation
            previous_statements = self.chatbot.storage.filter(
                conversation=input_statement.conversation,
                order_by=['id'],
                page_size=self.conversation_context_count * 2  # x2 to account for bot responses
            )

            # Convert to LLM message format
            for stmt in previous_statements:
                # Determine role based on persona
                if stmt.persona and stmt.persona.startswith('bot:'):
                    role = 'assistant'
                else:
                    role = 'user'

                messages.append({
                    'role': role,
                    'content': stmt.text
                })

        except Exception as e:
            self.chatbot.logger.warning(f"Failed to retrieve conversation context: {e}")

        return messages

    def _build_base_messages(self, input_statement: Statement, system_message: Optional[str] = None) -> List[Dict[str, str]]:
        """
        Build base message list for LLM API calls.

        Args:
            input_statement: The input statement
            system_message: Optional system message override

        Returns:
            List of message dicts in LLM format
        """
        messages = [{'role': 'system', 'content': system_message or self.system_message}]
        messages.extend(self._get_conversation_context(input_statement))
        messages.append({'role': 'user', 'content': input_statement.text})
        return messages

    def _format_error_response(self, error: Exception) -> str:
        """
        Format a consistent error response message.

        Args:
            error: The exception that occurred

        Returns:
            Formatted error message string
        """
        return f"I apologize, but I encountered an error: {str(error)}"

    def _supports_native_tools(self) -> bool:
        """
        Determine if the current model supports native tool calling.

        Returns:
            True if native tools are supported
        """
        # If user explicitly set force_native_tools, use that
        if self.force_native_tools is not None:
            return self.force_native_tools

        # Otherwise, use cached detection result
        # (detection happens once during initialization)
        if self._native_tools_supported is None:
            # Fallback: detect now if somehow not set during init
            self._native_tools_supported = self._detect_tool_capability()

        return self._native_tools_supported

    def _detect_tool_capability(self) -> bool:
        """
        Detect if the model supports native tool calling.
        Override in subclasses for provider-specific detection.

        Returns:
            True if tools are supported
        """
        return False

    def _get_tools_for_llm(self) -> List[Dict[str, Any]]:
        """
        Get tool definitions in the format expected by the LLM provider.
        Override in subclasses for provider-specific formats.

        Returns:
            List of tool definitions
        """
        raise NotImplementedError("Subclasses must implement _get_tools_for_llm()")

    def _execute_tool(self, tool_name: str, parameters: Dict[str, Any]) -> str:
        """
        Execute a tool by its name with the given parameters.

        Args:
            tool_name: Name of the tool to execute
            parameters: Tool parameters

        Returns:
            Tool execution result as string
        """
        if tool_name not in self.tool_registry:
            self.chatbot.logger.warning(f"Tool not found: '{tool_name}'")
            return f"Error: Tool '{tool_name}' not found"

        adapter = self.tool_registry[tool_name]

        try:
            # Validate parameters
            if not adapter.validate_tool_parameters(**parameters):
                self.chatbot.logger.warning(f"Invalid parameters for tool '{tool_name}': {parameters}")
                return f"Error: Invalid parameters for tool '{tool_name}'"

            # Log tool execution
            self.chatbot.logger.info(f"Executing tool: '{tool_name}' with parameters: {parameters}")

            # Execute tool
            result = adapter.execute_as_tool(**parameters)

            # Convert result to string if needed
            if not isinstance(result, str):
                result = str(result)

            self.chatbot.logger.info(f"Tool '{tool_name}' completed successfully")
            return result

        except Exception as e:
            self.chatbot.logger.error(f"Tool execution error for '{tool_name}': {e}")
            return f"Error executing tool '{tool_name}': {str(e)}"

    def _handle_native_tool_calling(self, input_statement: Statement) -> Statement:
        """
        Handle tool calling with native LLM support.
        Override in subclasses for provider-specific implementation.

        Args:
            input_statement: The input statement to process

        Returns:
            Response statement with confidence
        """
        raise NotImplementedError("Subclasses must implement _handle_native_tool_calling()")

    def _handle_prompt_based_tool_calling(self, input_statement: Statement) -> Statement:
        """
        Handle tool calling via prompt engineering for models without native support.

        This method guides the LLM to output structured JSON that can be parsed
        and routed to appropriate tools.

        Args:
            input_statement: The input statement to process

        Returns:
            Response statement with confidence
        """
        # Build tool descriptions for prompt
        tool_descriptions = []
        for adapter in self.tool_registry.values():
            schema = adapter.get_tool_schema()
            tool_desc = f"- {schema['name']}: {schema['description']}"
            tool_descriptions.append(tool_desc)

        tools_text = "\n".join(tool_descriptions)

        # TODO: Consider switching from JSON to TOON

        # Enhanced system message with tool instructions
        system_msg = f"""{self.system_message}

You have access to the following specialized tools:
{tools_text}

IMPORTANT: You can respond to ANY question the user asks. Use tools when they would be helpful for specific tasks, but respond naturally to general conversation, greetings, or topics that don't require tools.

When you need to use a tool, respond with a JSON object in this exact format:
{{"tool": "tool_name", "parameters": {{"param1": "value1"}}}}

For all other questions, respond normally with plain text conversationally."""

        # Get LLM response
        response_text = self._call_llm(input_statement, system_msg)

        # Try to parse as JSON (tool call)
        if response_text.strip().startswith('{'):
            try:
                tool_call = json.loads(response_text)
                tool_name = tool_call.get('tool')
                parameters = tool_call.get('parameters', {})

                self.chatbot.logger.info(f"LLM requested tool via prompt: '{tool_name}'")

                # Execute tool
                tool_result = self._execute_tool(tool_name, parameters)

                # Get final response from LLM with tool result
                followup_msg = f"Tool '{tool_name}' returned: {tool_result}\nProvide a natural language response to the user."
                final_response = self._call_llm_with_context(input_statement, followup_msg)

                response = Statement(text=final_response)
                response.confidence = self._calculate_confidence(final_response)
                return response

            except json.JSONDecodeError:
                pass  # Not a tool call, treat as normal response

        # Regular text response
        response = Statement(text=response_text)
        response.confidence = self._calculate_confidence(response_text)
        return response

    def _call_llm(self, input_statement: Statement, system_message: Optional[str] = None) -> str:
        """
        Make a direct LLM API call without tool support.
        Override in subclasses for provider-specific implementation.

        Args:
            input_statement: The input statement
            system_message: Optional system message override

        Returns:
            LLM response text
        """
        raise NotImplementedError("Subclasses must implement _call_llm()")

    def _call_llm_with_context(self, input_statement: Statement, additional_context: str) -> str:
        """
        Make an LLM call with additional context message.

        Args:
            input_statement: The input statement
            additional_context: Additional context to include

        Returns:
            LLM response text
        """
        # This will be implemented in subclasses using their specific API
        raise NotImplementedError("Subclasses must implement _call_llm_with_context()")

    def _calculate_confidence(self, response_text: str) -> float:
        """
        Calculate confidence score for LLM response.

        Uses a simple heuristic based on response length and quality indicators.
        Returns a value between min_confidence and max_confidence.

        Args:
            response_text: The LLM's response text

        Returns:
            Confidence score between 0 and 1
        """
        # Base confidence (middle of range)
        confidence = (self.min_confidence + self.max_confidence) / 2

        # Adjust based on response length (very short or very long may be less reliable)
        length = len(response_text)
        if length < 10:
            confidence -= 0.1
        elif 50 < length < 200:
            confidence += 0.05

        # Clamp to configured range
        confidence = max(self.min_confidence, min(self.max_confidence, confidence))

        return confidence

    def process(self, statement: Statement, additional_response_selection_parameters: dict = None) -> Statement:
        """
        Process the input statement using the LLM.

        Args:
            statement: The input statement to process
            additional_response_selection_parameters: Additional parameters (unused)

        Returns:
            Response statement with confidence score
        """
        # If no tools are configured, just call LLM directly
        if not self.tool_registry:
            response_text = self._call_llm(statement)
            response = Statement(text=response_text)
            response.confidence = self._calculate_confidence(response_text)
            return response

        # Determine tool calling method
        if self._supports_native_tools():
            return self._handle_native_tool_calling(statement)
        else:
            return self._handle_prompt_based_tool_calling(statement)


class OllamaLogicAdapter(LLMLogicAdapter):
    """
    Logic adapter for Ollama LLMs with MCP tool support.

    .. warning::
        This adapter is experimental. Tool capability detection uses template
        inspection which may not work for all model formats. Tool calling behavior
        varies significantly between models.

    Configuration:
        model (str): Ollama model name (e.g., 'llama3.1', 'mistral')
        host (str): Ollama API endpoint (default: http://localhost:11434)
        logic_adapters_as_tools (list): Logic adapters to expose as tools

    Example:
        {
            'import_path': 'chatterbot.logic.OllamaLogicAdapter',
            'model': 'llama3.1',
            'host': 'http://localhost:11434',
            'logic_adapters_as_tools': [
                'chatterbot.logic.MathematicalEvaluation',
                'chatterbot.logic.TimeLogicAdapter'
            ]
        }
    """

    def __init__(self, chatbot, **kwargs):
        # Set default host before parent init
        if 'host' not in kwargs:
            kwargs['host'] = 'http://localhost:11434'

        super().__init__(chatbot, **kwargs)

        # Initialize Ollama client
        try:
            from ollama import Client
            self.client = Client(host=self.host)
        except ImportError:
            raise ImportError(
                "Ollama library not installed. Install with: pip install chatterbot[dev]"
            )

    def _detect_tool_capability(self) -> bool:
        """
        Detect if the Ollama model supports native tool calling.

        Uses a combination of known model patterns and template inspection
        to determine tool support.

        Returns:
            True if model supports tools
        """
        # Known models with tool support (as of 2026)
        # Check model name patterns - handles versioned models (e.g., llama3.1:8b)
        model_base = self.model.split(':')[0].lower()

        # Known tool-supporting model patterns
        tool_supporting_patterns = [
            # Llama series
            'llama3.1', 'llama3.2', 'llama3-groq-tool',
            # Mistral series
            'mistral', 'mistral-nemo', 'mistral-large',
            # Qwen series
            'qwen2.5', 'qwen2.5-coder',
            # Specialized models
            'firefunction', 'nemotron', 'command-r', 'command-r-plus',
            # Enterprise models
            'granite3.1-dense', 'hermes3'
        ]

        # Check if model matches any known pattern
        for pattern in tool_supporting_patterns:
            if pattern in model_base:
                self.chatbot.logger.info(
                    f"Model '{self.model}' supports native tool calling (known model)"
                )
                return True

        # Fallback to template inspection for unknown models
        try:
            # Get model metadata
            model_info = self.client.show(self.model)

            # Get the template string
            template = model_info.get('template', '')

            # Check for tool-specific tokens in the template
            has_tools = '{{ .Tools }}' in template or '{{ tools }}' in template

            if has_tools:
                self.chatbot.logger.info(
                    f"Model '{self.model}' supports native tool calling (template inspection)"
                )
            else:
                self.chatbot.logger.info(
                    f"Model '{self.model}' does not support native tool calling, will use prompt-based approach"
                )

            return has_tools

        except Exception as e:
            self.chatbot.logger.warning(
                f"Failed to inspect model '{self.model}' for tool support: {e}. "
                f"Falling back to prompt-based tool calling."
            )
            return False

    def _get_tools_for_llm(self) -> List[Dict[str, Any]]:
        """
        Get tool definitions in Ollama format.

        Returns:
            List of Ollama-formatted tool definitions
        """
        tools = []
        for adapter in self.tool_registry.values():
            schema = adapter.get_tool_schema()
            ollama_tool = convert_to_ollama_tool_format(schema)
            tools.append(ollama_tool)
        return tools

    def _call_llm(self, input_statement: Statement, system_message: Optional[str] = None) -> str:
        """
        Call Ollama API without tool support.

        Args:
            input_statement: The input statement
            system_message: Optional system message override

        Returns:
            LLM response text
        """
        # Build messages with conversation context
        messages = self._build_base_messages(input_statement, system_message)

        try:
            response = self.client.chat(
                model=self.model,
                messages=messages
            )
            return response['message']['content']
        except Exception as e:
            self.chatbot.logger.error(f"Ollama API error: {e}")
            return self._format_error_response(e)

    def _call_llm_with_context(self, input_statement: Statement, additional_context: str) -> str:
        """
        Call Ollama with additional context for tool result processing.

        Args:
            input_statement: The input statement
            additional_context: Additional context message

        Returns:
            LLM response text
        """
        messages = self._build_base_messages(input_statement)
        messages.append({'role': 'assistant', 'content': additional_context})

        try:
            response = self.client.chat(
                model=self.model,
                messages=messages
            )
            return response['message']['content']
        except Exception as e:
            self.chatbot.logger.error(f"Ollama API error: {e}")
            return self._format_error_response(e)

    def _handle_native_tool_calling(self, input_statement: Statement) -> Statement:
        """
        Handle tool calling with Ollama's native function calling support.

        Args:
            input_statement: The input statement to process

        Returns:
            Response statement with confidence
        """
        # Build messages
        messages = self._build_base_messages(input_statement)

        # Get tools in Ollama format
        tools = self._get_tools_for_llm()

        # TODO: Look into support for thinking mode

        try:
            # Initial LLM call with tools
            response = self.client.chat(
                model=self.model,
                messages=messages,
                tools=tools
            )

            message = response['message']

            # Check if LLM wants to use a tool
            if tool_calls := message.get('tool_calls'):
                self.chatbot.logger.info(f"Ollama LLM requested {len(tool_calls)} tool(s)")

                # Serialize the message properly for Ollama API
                # The message object needs to be converted to dict format
                if hasattr(message, 'model_dump'):
                    # Pydantic v2
                    message_dict = message.model_dump(exclude_none=True)
                elif hasattr(message, 'dict'):
                    # Pydantic v1
                    message_dict = message.dict(exclude_none=True)
                else:
                    # Fallback if it's already a dict or needs manual conversion
                    message_dict = dict(message) if not isinstance(message, dict) else message

                messages.append(message_dict)

                # Execute each tool call and add results
                for tool_call in tool_calls:
                    function = tool_call['function']
                    tool_name = function['name']
                    parameters = function.get('arguments', {})

                    # Execute tool
                    tool_result = self._execute_tool(tool_name, parameters)

                    # Add tool result to conversation with tool_name field
                    messages.append({
                        'role': 'tool',
                        'content': tool_result,
                        'tool_name': tool_name
                    })

                # Get final response from LLM with tool results
                final_response = self.client.chat(
                    model=self.model,
                    messages=messages,
                    tools=tools
                )

                response_text = final_response['message']['content']
            else:
                # No tool call, use direct response
                response_text = message['content']

            response = Statement(text=response_text)
            response.confidence = self._calculate_confidence(response_text)
            return response

        except Exception as e:
            self.chatbot.logger.error(f"Ollama tool calling error: {e}")
            response = Statement(text=self._format_error_response(e))
            response.confidence = self.min_confidence
            return response


class OpenAILogicAdapter(LLMLogicAdapter):
    """
    Logic adapter for OpenAI LLMs with MCP tool support.

    .. warning::
        This adapter is experimental.

    Configuration:
        model (str): OpenAI model name (e.g., 'gpt-4', 'gpt-3.5-turbo')
        host (str): Optional custom API endpoint
        logic_adapters_as_tools (list): Logic adapters to expose as tools

    Environment:
        OPENAI_API_KEY: Required for authentication

    Example:
        {
            'import_path': 'chatterbot.logic.OpenAILogicAdapter',
            'model': 'gpt-4o-mini',
            'logic_adapters_as_tools': [
                'chatterbot.logic.MathematicalEvaluation',
                'chatterbot.logic.TimeLogicAdapter'
            ]
        }
    """

    def __init__(self, chatbot, **kwargs):
        super().__init__(chatbot, **kwargs)

        # Initialize OpenAI client
        try:
            from openai import OpenAI as OpenAIClient
            if self.host:
                self.client = OpenAIClient(base_url=self.host)
            else:
                self.client = OpenAIClient()
        except ImportError:
            raise ImportError(
                "OpenAI library not installed. Install with: pip install chatterbot[dev]"
            )

    def _detect_tool_capability(self) -> bool:
        """
        Detect if the OpenAI model supports tool calling.

        Returns:
            True (all current OpenAI models support tool calling)
        """
        return True

    def _get_tools_for_llm(self) -> List[Dict[str, Any]]:
        """
        Get tool definitions in OpenAI format.

        Returns:
            List of OpenAI-formatted tool definitions
        """
        tools = []
        for adapter in self.tool_registry.values():
            schema = adapter.get_tool_schema()
            openai_tool = convert_to_openai_tool_format(schema)
            tools.append(openai_tool)
        return tools

    def _call_llm(self, input_statement: Statement, system_message: Optional[str] = None) -> str:
        """
        Call OpenAI API without tool support.

        Args:
            input_statement: The input statement
            system_message: Optional system message override

        Returns:
            LLM response text
        """
        # Build messages with conversation context
        messages = self._build_base_messages(input_statement, system_message)

        try:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages
            )
            return response.choices[0].message.content
        except Exception as e:
            self.chatbot.logger.error(f"OpenAI API error: {e}")
            return self._format_error_response(e)

    def _call_llm_with_context(self, input_statement: Statement, additional_context: str) -> str:
        """
        Call OpenAI with additional context for tool result processing.

        Args:
            input_statement: The input statement
            additional_context: Additional context message

        Returns:
            LLM response text
        """
        messages = self._build_base_messages(input_statement)
        messages.append({'role': 'assistant', 'content': additional_context})

        try:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages
            )
            return response.choices[0].message.content
        except Exception as e:
            self.chatbot.logger.error(f"OpenAI API error: {e}")
            return self._format_error_response(e)

    def _handle_native_tool_calling(self, input_statement: Statement) -> Statement:
        """
        Handle tool calling with OpenAI's native function calling support.

        Args:
            input_statement: The input statement to process

        Returns:
            Response statement with confidence
        """
        # Build messages
        messages = self._build_base_messages(input_statement)

        # Get tools in OpenAI format
        tools = self._get_tools_for_llm()

        try:
            # Initial LLM call with tools
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
                tools=tools
            )

            message = response.choices[0].message

            # Check if LLM wants to use a tool
            if tool_calls := message.tool_calls:
                self.chatbot.logger.info(f"OpenAI LLM requested {len(tool_calls)} tool(s)")
                # Execute each tool call
                for tool_call in tool_calls:
                    function = tool_call.function
                    tool_name = function.name
                    parameters = json.loads(function.arguments)

                    # Execute tool
                    tool_result = self._execute_tool(tool_name, parameters)

                    # Add assistant message with tool call
                    messages.append({
                        'role': 'assistant',
                        'content': None,
                        'tool_calls': [{
                            'id': tool_call.id,
                            'type': 'function',
                            'function': {
                                'name': tool_name,
                                'arguments': function.arguments
                            }
                        }]
                    })

                    # Add tool result message
                    messages.append({
                        'role': 'tool',
                        'tool_call_id': tool_call.id,
                        'content': tool_result
                    })

                # Get final response from LLM with tool results
                final_response = self.client.chat.completions.create(
                    model=self.model,
                    messages=messages,
                    tools=tools
                )

                response_text = final_response.choices[0].message.content
            else:
                # No tool call, use direct response
                response_text = message.content

            response = Statement(text=response_text)
            response.confidence = self._calculate_confidence(response_text)
            return response

        except Exception as e:
            self.chatbot.logger.error(f"OpenAI tool calling error: {e}")
            response = Statement(text=self._format_error_response(e))
            response.confidence = self.min_confidence
            return response


================================================
FILE: chatterbot/logic/logic_adapter.py
================================================
from random import choice

from chatterbot.adapters import Adapter
from chatterbot.storage import StorageAdapter
from chatterbot.search import IndexedTextSearch
from chatterbot.conversation import Statement
from chatterbot import utils


class LogicAdapter(Adapter):
    """
    This is an abstract class that represents the interface
    that all logic adapters should implement.

    :param search_algorithm_name: The name of the search algorithm that should
        be used to search for close matches to the provided input.
        Defaults to the value of ``Search.name``.

    :param maximum_similarity_threshold:
        The maximum amount of similarity between two statement that is required
        before the search process is halted. The search for a matching statement
        will continue until a statement with a greater than or equal similarity
        is found or the search set is exhausted.
        Defaults to 0.95

    :param response_selection_method:
          The a response selection method.
          Defaults to ``get_first_response``
    :type response_selection_method: collections.abc.Callable

    :param default_response:
          The default response returned by this logic adapter
          if there is no other possible response to return.
    :type default_response: str or list or tuple
    """

    def __init__(self, chatbot, **kwargs):
        super().__init__(chatbot, **kwargs)
        from chatterbot.response_selection import get_first_response

        self.search_algorithm_name = kwargs.get(
            'search_algorithm_name',
            IndexedTextSearch.name
        )

        self.search_algorithm = self.chatbot.search_algorithms[
            self.search_algorithm_name
        ]

        self.maximum_similarity_threshold = kwargs.get(
            'maximum_similarity_threshold', 0.95
        )

        if response_selection_method := kwargs.get('response_selection_method'):
            if isinstance(response_selection_method, str):
                # If an import path is provided, import the method
                response_selection_method = utils.import_module(
                    response_selection_method
                )
                kwargs['response_selection_method'] = response_selection_method

        # By default, select the first available response
        self.select_response = kwargs.get(
            'response_selection_method',
            get_first_response
        )

        default_responses = kwargs.get('default_response', [])

        # Convert a single string into a list
        if isinstance(default_responses, str):
            default_responses = [
                default_responses
            ]

        self.default_responses = [
            Statement(text=default) for default in default_responses
        ]

    def can_process(self, statement) -> bool:
        """
        A preliminary check that is called to determine if a
        logic adapter can process a given statement. By default,
        this method returns true but it can be overridden in
        child classes as needed.
        """
        return True

    def process(self, statement: Statement, additional_response_selection_parameters: dict = None) -> Statement:
        """
        Override this method and implement your logic for selecting a response to an input statement.

        A confidence value and the selected response statement should be returned.
        The confidence value represents a rating of how accurate the logic adapter
        expects the selected response to be. Confidence scores are used to select
        the best response from multiple logic adapters.

        The confidence value should be a number between 0 and 1 where 0 is the
        lowest confidence level and 1 is the highest.

        :param statement: An input statement to be processed by the logic adapter.

        :param additional_response_selection_parameters: Parameters to be used when
            filtering results to choose a response from.
        """
        raise self.AdapterMethodNotImplementedError()

    def get_default_response(self, input_statement: Statement) -> Statement:
        """
        This method is called when a logic adapter is unable to generate any
        other meaningful response.
        """
        if self.default_responses:
            response = choice(self.default_responses)
        else:
            try:
                response = self.chatbot.storage.get_random()
            except StorageAdapter.EmptyDatabaseException:
                response = input_statement

        self.chatbot.logger.info(
            'No known response to the input was found. Selecting a random response.'
        )

        # Set confidence to zero because a random response is selected
        response.confidence = 0

        return response

    @property
    def class_name(self) -> str:
        """
        Return the name of the current logic adapter class.
        This is typically used for logging and debugging.
        """
        return str(self.__class__.__name__)


================================================
FILE: chatterbot/logic/mathematical_evaluation.py
================================================
from chatterbot.logic import LogicAdapter
from chatterbot.conversation import Statement
from chatterbot import languages
from chatterbot.logic.mcp_tools import MCPToolAdapter


class MathematicalEvaluation(LogicAdapter, MCPToolAdapter):
    """
    The MathematicalEvaluation logic adapter parses input to determine
    whether the user is asking a question that requires math to be done.
    If so, the equation is extracted from the input and returned with
    the evaluated result.

    For example:
        User: 'What is three plus five?'
        Bot: 'Three plus five equals eight'

    :kwargs:
        * *language* (``object``) --
          The language is set to ``chatterbot.languages.ENG`` for English by default.
    """

    def __init__(self, chatbot, **kwargs):
        super().__init__(chatbot, **kwargs)

        self.language = kwargs.get('language', languages.ENG)
        self.cache = {}

    def can_process(self, statement) -> bool:
        """
        Determines whether it is appropriate for this
        adapter to respond to the user input.
        """
        response = self.process(statement)
        self.cache[statement.text] = response
        return response.confidence == 1

    def process(self, statement: Statement, additional_response_selection_parameters: dict = None) -> Statement:
        """
        Takes a statement string.
        Returns the equation from the statement with the mathematical terms solved.
        """
        from mathparse import mathparse

        input_text = statement.text

        # Use the result cached by the process method if it exists
        if input_text in self.cache:
            cached_result = self.cache[input_text]
            self.cache = {}
            return cached_result

        # Getting the mathematical terms within the input statement
        expression = mathparse.extract_expression(input_text, language=self.language.ISO_639.upper())

        response = Statement(text=expression)

        try:
            response.text = '{} = {}'.format(
                response.text,
                mathparse.parse(expression, language=self.language.ISO_639.upper())
            )

            # The confidence is 1 if the expression could be evaluated
            response.confidence = 1
        except mathparse.PostfixTokenEvaluationException:
            response.confidence = 0

        return response

    def get_tool_schema(self):
        """
        Return the MCP tool schema for mathematical evaluation.
        """
        return {
            "name": "calculate",
            "description": "Evaluate mathematical expressions and solve equations. Supports basic arithmetic, algebra, and common mathematical functions.",
            "parameters": {
                "type": "object",
                "properties": {
                    "expression": {
                        "type": "string",
                        "description": "The mathematical expression to evaluate (e.g., '2 + 2', 'sqrt(16)', 'three plus five')"
                    }
                },
                "required": ["expression"]
            }
        }

    def execute_as_tool(self, **kwargs):
        """
        Execute mathematical evaluation as a tool.

        Args:
            **kwargs: Must contain 'expression' parameter

        Returns:
            The evaluation result as a string
        """
        from mathparse import mathparse

        expression = kwargs.get("expression", "")

        if not expression:
            return "Error: No expression provided"

        try:
            # Extract mathematical expression
            extracted = mathparse.extract_expression(expression, language=self.language.ISO_639.upper())

            # Evaluate the expression
            result = mathparse.parse(extracted, language=self.language.ISO_639.upper())

            return f"{extracted} = {result}"

        except mathparse.PostfixTokenEvaluationException:
            return f"Error: Could not evaluate expression '{expression}'"
        except Exception as e:
            return f"Error: {str(e)}"


================================================
FILE: chatterbot/logic/mcp_tools.py
================================================
"""
MCP (Model Context Protocol) tool adapter for ChatterBot logic adapters.

This module provides a mixin class that allows logic adapters to be exposed
as MCP-compatible tools to LLMs. Logic adapters that inherit from MCPToolAdapter
can define tool schemas and be invoked by LLM adapters.
"""
from typing import Any, Dict
from abc import ABC, abstractmethod


class MCPToolAdapter(ABC):
    """
    Mixin class for logic adapters that can be used as MCP tools.

    Logic adapters that want to be callable as tools should inherit from this
    class and implement the get_tool_schema() and execute_as_tool() methods.

    Example:
        class MathematicalEvaluation(LogicAdapter, MCPToolAdapter):
            def get_tool_schema(self) -> Dict[str, Any]:
                return {
                    "name": "calculate",
                    "description": "Evaluate mathematical expressions",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "expression": {
                                "type": "string",
                                "description": "Mathematical expression to evaluate"
                            }
                        },
                        "required": ["expression"]
                    }
                }

            def execute_as_tool(self, **kwargs) -> str:
                expression = kwargs.get("expression")
                # ... evaluation logic
                return result
    """

    @abstractmethod
    def get_tool_schema(self) -> Dict[str, Any]:
        """
        Return the tool schema for this logic adapter.

        The schema should follow the OpenAI/MCP function calling format:
        {
            "name": "tool_name",
            "description": "Tool description",
            "parameters": {
                "type": "object",
                "properties": {
                    "param_name": {
                        "type": "string|number|boolean|array|object",
                        "description": "Parameter description"
                    }
                },
                "required": ["param_name"]
            }
        }

        Returns:
            Dict containing the tool schema
        """
        raise NotImplementedError(
            "Logic adapters using MCPToolAdapter must implement get_tool_schema()"
        )

    @abstractmethod
    def execute_as_tool(self, **kwargs) -> Any:
        """
        Execute this logic adapter as a tool with the given parameters.

        This method is called when an LLM requests to use this adapter as a tool.
        It should extract the necessary parameters from kwargs and execute the
        logic adapter's functionality in a tool-calling context.

        Args:
            **kwargs: Tool parameters as defined in the tool schema

        Returns:
            Tool execution result (will be converted to string if needed)
        """
        raise NotImplementedError(
            "Logic adapters using MCPToolAdapter must implement execute_as_tool()"
        )

    def get_tool_name(self) -> str:
        """
        Get the name of this tool.

        Returns:
            The tool name from the schema
        """
        schema = self.get_tool_schema()
        return schema.get("name", self.__class__.__name__)

    def validate_tool_parameters(self, **kwargs) -> bool:
        """
        Validate that the provided parameters match the tool schema.

        Args:
            **kwargs: Parameters to validate

        Returns:
            True if parameters are valid, False otherwise
        """
        schema = self.get_tool_schema()
        parameters = schema.get("parameters", {})
        required = parameters.get("required", [])
        properties = parameters.get("properties", {})

        # Check required parameters
        for param in required:
            if param not in kwargs:
                return False

        # Check parameter types (basic validation)
        for param_name, param_value in kwargs.items():
            if param_name not in properties:
                continue

            expected_type = properties[param_name].get("type")
            if expected_type == "string" and not isinstance(param_value, str):
                return False
            elif expected_type == "number" and not isinstance(param_value, (int, float)):
                return False
            elif expected_type == "boolean" and not isinstance(param_value, bool):
                return False
            elif expected_type == "array" and not isinstance(param_value, list):
                return False
            elif expected_type == "object" and not isinstance(param_value, dict):
                return False

        return True


def is_tool_adapter(adapter) -> bool:
    """
    Check if a logic adapter instance supports MCP tool functionality.

    Args:
        adapter: Logic adapter instance to check

    Returns:
        True if the adapter has MCPToolAdapter capabilities
    """
    return (
        hasattr(adapter, 'get_tool_schema') and
        callable(adapter.get_tool_schema) and
        hasattr(adapter, 'execute_as_tool') and
        callable(adapter.execute_as_tool)
    )


def convert_to_openai_tool_format(schema: Dict[str, Any]) -> Dict[str, Any]:
    """
    Convert MCP tool schema to OpenAI function calling format.

    OpenAI expects:
    {
        "type": "function",
        "function": {
            "name": "...",
            "description": "...",
            "parameters": {...}
        }
    }

    Args:
        schema: MCP tool schema

    Returns:
        OpenAI-formatted tool definition
    """
    return {
        "type": "function",
        "function": {
            "name": schema.get("name"),
            "description": schema.get("description", ""),
            "parameters": schema.get("parameters", {})
        }
    }


def convert_to_ollama_tool_format(schema: Dict[str, Any]) -> Dict[str, Any]:
    """
    Convert MCP tool schema to Ollama function calling format.

    Ollama uses a similar format to OpenAI:
    {
        "type": "function",
        "function": {
            "name": "...",
            "description": "...",
            "parameters": {...}
        }
    }

    Args:
        schema: MCP tool schema

    Returns:
        Ollama-formatted tool definition
    """
    # Ollama format is identical to OpenAI for now
    return convert_to_openai_tool_format(schema)


================================================
FILE: chatterbot/logic/specific_response.py
================================================
from chatterbot.logic import LogicAdapter
from chatterbot.conversation import Statement
from chatterbot import languages
from chatterbot.utils import get_model_for_language
import spacy


class SpecificResponseAdapter(LogicAdapter):
    """
    Return a specific response to a specific input.

    :kwargs:
        * *input_text* (``str``) --
          The input text that triggers this logic adapter.
        * *output_text* (``str`` or ``function``) --
          The output text returned by this logic adapter.
          If a function is provided, it should return a string.
    """

    def __init__(self, chatbot, **kwargs):
        super().__init__(chatbot, **kwargs)

        try:
            self.input_text = kwargs['input_text']
        except KeyError:
            raise chatbot.ChatBotException(
                'The SpecificResponseAdapter requires an input_text parameter.'
            )

        try:
            self._output_text = kwargs['output_text']
        except KeyError:
            raise chatbot.ChatBotException(
                'The SpecificResponseAdapter requires an output_text parameter.'
            )

        self.matcher = None

        if MatcherClass := kwargs.get('matcher'):
            language = kwargs.get('language', languages.ENG)

            self.nlp = self._initialize_nlp(language)

            self.matcher = MatcherClass(self.nlp.vocab)

            self.matcher.add('SpecificResponse', [self.input_text])

    def _initialize_nlp(self, language):
        model = get_model_for_language(language)

        return spacy.load(model)

    def can_process(self, statement) -> bool:
        if self.matcher:
            doc = self.nlp(statement.text)
            matches = self.matcher(doc)

            if matches:
                return True
        elif statement.text == self.input_text:
            return True

        return False

    def process(self, statement: Statement, additional_response_selection_parameters: dict = None) -> Statement:

        if callable(self._output_text):
            response_statement = Statement(text=self._output_text())
        else:
            response_statement = Statement(text=self._output_text)

        if self.matcher:
            doc = self.nlp(statement.text)
            matches = self.matcher(doc)

            if matches:
                response_statement.confidence = 1
            else:
                response_statement.confidence = 0

        elif statement.text == self.input_text:
            response_statement.confidence = 1
        else:
            response_statement.confidence = 0

        return response_statement


================================================
FILE: chatterbot/logic/time_adapter.py
================================================
from datetime import datetime
from chatterbot import languages
from chatterbot.logic import LogicAdapter
from chatterbot.conversation import Statement
from chatterbot.utils import get_model_for_language
from chatterbot.logic.mcp_tools import MCPToolAdapter
import spacy


class TimeLogicAdapter(LogicAdapter, MCPToolAdapter):
    """
    The TimeLogicAdapter returns the current time.

    :kwargs:
        * *positive* (``list``) --
          The time-related questions used to identify time questions about the current time.
          Defaults to a list of English sentences.
        * *language* (``str``) --
          The language for the spacy model. Defaults to English.
    """

    def __init__(self, chatbot, **kwargs):
        super().__init__(chatbot, **kwargs)

        # TODO / FUTURE: Switch `positive` to `patterns` for more accurate naming
        phrases = kwargs.get('positive', [
            'What time is it?',
            'Hey, what time is it?',
            'Do you have the time?',
            'Do you know the time?',
            'Do you know what time it is?',
            'What is the time?',
            'What time is it now?',
            'Can you tell me the time?',
            'Could you tell me the time?',
            'What is the current time?',
        ])

        language = kwargs.get('language', languages.ENG)

        model = get_model_for_language(language)

        self.nlp = spacy.load(model)

        # Set up rules for spacy's rule-based matching
        # https://spacy.io/usage/rule-based-matching

        self.matcher = spacy.matcher.PhraseMatcher(self.nlp.vocab)

        patterns = [self.nlp.make_doc(text) for text in phrases]

        # Add the patterns to the matcher
        self.matcher.add('TimeQuestionList', patterns)

    def process(self, statement: Statement, additional_response_selection_parameters: dict = None) -> Statement:
        now = datetime.now()

        # Check if the input statement contains a time-related question
        doc = self.nlp(statement.text)

        matches = self.matcher(doc)

        self.chatbot.logger.info('TimeLogicAdapter detected {} matches'.format(len(matches)))

        confidence = 1 if matches else 0
        response = Statement(text='The current time is ' + now.strftime('%I:%M %p'))

        response.confidence = confidence
        return response

    def get_tool_schema(self):
        """
        Return the MCP tool schema for getting current time.
        """
        return {
            "name": "get_current_time",
            "description": "Get the current date and time. Returns formatted time string.",
            "parameters": {
                "type": "object",
                "properties": {},
                "required": []
            }
        }

    def execute_as_tool(self, **kwargs):
        """
        Execute time query as a tool.

        Returns:
            Current time as formatted string
        """
        now = datetime.now()
        return f"The current time is {now.strftime('%I:%M %p')} on {now.strftime('%A, %B %d, %Y')}"


================================================
FILE: chatterbot/logic/unit_conversion.py
================================================
from chatterbot.logic import LogicAdapter
from chatterbot.conversation import Statement
from chatterbot.exceptions import OptionalDependencyImportError
from chatterbot import languages
from chatterbot import parsing
from chatterbot.logic.mcp_tools import MCPToolAdapter
from mathparse import mathparse
import re


class UnitConversion(LogicAdapter, MCPToolAdapter):
    """
    The UnitConversion logic adapter parse inputs to convert values
    between several metric units.

    For example:
        User: 'How many meters are in one kilometer?'
        Bot: '1000.0'

    :kwargs:
        * *language* (``object``) --
        The language is set to ``chatterbot.languages.ENG`` for English by default.
    """

    def __init__(self, chatbot, **kwargs):
        super().__init__(chatbot, **kwargs)
        try:
            from pint import UnitRegistry
        except ImportError:
            message = (
                'Unable to import "pint".\n'
                'Please install "pint" before using the UnitConversion logic adapter:\n'
                'pip install pint'
            )
            raise OptionalDependencyImportError(message)

        self.language = kwargs.get('language', languages.ENG)
        self.cache = {}
        self.patterns = [
            (
                re.compile(r'''
                   (([Hh]ow\s+many)\s+
                   (?P<target>\S+)\s+ # meter, celsius, hours
                   ((are)*\s*in)\s+
                   (?P<number>([+-]?\d+(?:\.\d+)?)|(a|an)|(%s[-\s]?)+)\s+
                   (?P<from>\S+)\s*) # meter, celsius, hours
                   ''' % (parsing.numbers),
                    (re.VERBOSE | re.IGNORECASE)
                ),
                lambda m: self.handle_matches(m)
            ),
            (
                re.compile(r'''
                   ((?P<number>([+-]?\d+(?:\.\d+)?)|(%s[-\s]?)+)\s+
                   (?P<from>\S+)\s+ # meter, celsius, hours
                   (to)\s+
                   (?P<target>\S+)\s*) # meter, celsius, hours
                   ''' % (parsing.numbers),
                    (re.VERBOSE | re.IGNORECASE)
                ),
                lambda m: self.handle_matches(m)
            ),
            (
                re.compile(r'''
                   ((?P<number>([+-]?\d+(?:\.\d+)?)|(a|an)|(%s[-\s]?)+)\s+
                   (?P<from>\S+)\s+ # meter, celsius, hours
                   (is|are)\s+
                   (how\s+many)*\s+
                   (?P<target>\S+)\s*) # meter, celsius, hours
                   ''' % (parsing.numbers),
                    (re.VERBOSE | re.IGNORECASE)
                ),
                lambda m: self.handle_matches(m)
            )
        ]
        self.unit_registry = UnitRegistry()

    def get_unit(self, unit_variations):
        """
        Get the first match unit metric object supported by pint library
        given a variation of unit metric names (Ex:['HOUR', 'hour']).

        :param unit_variations: A list of strings with names of units
        :type unit_variations: str
        """
        for unit in unit_variations:
            try:
                return getattr(self.unit_registry, unit)
            except AttributeError:
                continue
        return None

    def get_valid_units(self, from_unit, target_unit):
        """
        Returns the first match `pint.unit.Unit` object for from_unit and
        target_unit strings from a possible variation of metric unit names
        supported by pint library.

        :param from_unit: source metric unit
        :type from_unit: str

        :param from_unit: target metric unit
        :type from_unit: str
        """
        from_unit_variations = [from_unit.lower(), from_unit.upper()]
        target_unit_variations = [target_unit.lower(), target_unit.upper()]
        from_unit = self.get_unit(from_unit_variations)
        target_unit = self.get_unit(target_unit_variations)
        return from_unit, target_unit

    def handle_matches(self, match):
        """
        Returns a response statement from a matched input statement.

        :param match: It is a valid matched pattern from the input statement
        :type: `_sre.SRE_Match`
        """
        response = Statement(text='')

        from_parsed = match.group("from")
        target_parsed = match.group("target")
        n_statement = match.group("number")

        if n_statement == 'a' or n_statement == 'an':
            n_statement = '1.0'

        n = mathparse.parse(n_statement, self.language.ISO_639.upper())

        from_parsed, target_parsed = self.get_valid_units(from_parsed, target_parsed)

        if from_parsed is None or target_parsed is None:
            response.confidence = 0.0
        else:
            from_value = self.unit_registry.Quantity(float(n), from_parsed)
            target_value = from_value.to(target_parsed)
            response.confidence = 1.0
            response.text = str(target_value.magnitude)

        return response

    def can_process(self, statement) -> bool:
        response = self.process(statement)
        self.cache[statement.text] = response
        return response.confidence == 1.0

    def process(self, statement: Statement, additional_response_selection_parameters: dict = None) -> Statement:
        response = Statement(text='')
        input_text = statement.text
        try:
            # Use the result cached by the process method if it exists
            if input_text in self.cache:
                response = self.cache[input_text]
                self.cache = {}
                return response

            for pattern, func in self.patterns:
                p = pattern.match(input_text)
                if p is not None:
                    response = func(p)
                    if response.confidence == 1.0:
                        break
        except Exception as e:
            self.chatbot.logger.warning('Error during UnitConversion: {}'.format(str(e)))
            response.confidence = 0.0

        return response

    def get_tool_schema(self):
        """
        Return the MCP tool schema for unit conversion.
        """
        return {
            "name": "convert_units",
            "description": "Convert values between different units of measurement. Supports distance, weight, temperature, time, and other common unit conversions.",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "Unit conversion query in natural language (e.g., 'How many meters are in 5 kilometers?', '100 fahrenheit to celsius')"
                    }
                },
                "required": ["query"]
            }
        }

    def execute_as_tool(self, **kwargs):
        """
        Execute unit conversion as a tool.

        Args:
            **kwargs: Must contain 'query' parameter

        Returns:
            The conversion result as a string
        """
        query = kwargs.get("query", "")

        if not query:
            return "Error: No conversion query provided"

        try:
            # Create a statement and process it
            input_statement = Statement(text=query)
            response = self.process(input_statement)

            if response.confidence == 1.0:
                return response.text
            else:
                return f"Error: Could not parse unit conversion from '{query}'. Try formats like 'X units to Y units' or 'How many Y in X units?'"

        except Exception as e:
            return f"Error: {str(e)}"


================================================
FILE: chatterbot/parsing.py
================================================
import re
from datetime import timedelta, datetime
import calendar

# Variations of dates that the parser can capture
year_variations = ['year', 'years', 'yrs']
day_variations = ['days', 'day']
minute_variations = ['minute', 'minutes', 'mins']
hour_variations = ['hrs', 'hours', 'hour']
week_variations = ['weeks', 'week', 'wks']
month_variations = ['month', 'months']

# Variables used for RegEx Matching
day_names = 'monday|tuesday|wednesday|thursday|friday|saturday|sunday'
month_names_long = (
    'january|february|march|april|may|june|july|august|september|october|november|december'
)
month_names = month_names_long + '|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec'
day_nearest_names = 'today|yesterday|tomorrow|tonight|tonite'
numbers = (
    r'(^a(?=\s)|one|two|three|four|five|six|seven|eight|nine|ten|'
    r'eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|'
    r'eighteen|nineteen|twenty|thirty|forty|fifty|sixty|seventy|'
    r'eighty|ninety|hundred|thousand)'
)
re_dmy = '(' + '|'.join(day_variations + minute_variations + year_variations + week_variations + month_variations) + ')'
re_duration = r'(before|after|earlier|later|ago|from\snow)'
re_year = r'(19|20)\d{2}|^(19|20)\d{2}'
re_timeframe = r'this|coming|next|following|previous|last|end\sof\sthe'
re_ordinal = r'st|nd|rd|th|first|second|third|fourth|fourth|' + re_timeframe
re_time = r'(?P<hour>\d{1,2})(?=\s?(\:\d|(a|p)m))(\:(?P<minute>\d{1,2}))?(\s?(?P<convention>(am|pm)))?'
re_separator = r'of|at|on'

NUMBERS = {
    'zero': 0,
    'one': 1,
    'two': 2,
    'three': 3,
    'four': 4,
    'five': 5,
    'six': 6,
    'seven': 7,
    'eight': 8,
    'nine': 9,
    'ten': 10,
    'eleven': 11,
    'twelve': 12,
    'thirteen': 13,
    'fourteen': 14,
    'fifteen': 15,
    'sixteen': 16,
    'seventeen': 17,
    'eighteen': 18,
    'nineteen': 19,
    'twenty': 20,
    'thirty': 30,
    'forty': 40,
    'fifty': 50,
    'sixty': 60,
    'seventy': 70,
    'eighty': 80,
    'ninety': 90,
    'hundred': 100,
    'thousand': 1000,
    'million': 1000000,
    'billion': 1000000000,
    'trillion': 1000000000000,
}


# Mapping of Month name and Value
HASHMONTHS = {
    'january': 1,
    'jan': 1,
    'february': 2,
    'feb': 2,
    'march': 3,
    'mar': 3,
    'april': 4,
    'apr': 4,
    'may': 5,
    'june': 6,
    'jun': 6,
    'july': 7,
    'jul': 7,
    'august': 8,
    'aug': 8,
    'september': 9,
    'sep': 9,
    'october': 10,
    'oct': 10,
    'november': 11,
    'nov': 11,
    'december': 12,
    'dec': 12
}

# Days to number mapping
HASHWEEKDAYS = {
    'monday': 0,
    'mon': 0,
    'tuesday': 1,
    'tue': 1,
    'wednesday': 2,
    'wed': 2,
    'thursday': 3,
    'thu': 3,
    'friday': 4,
    'fri': 4,
    'saturday': 5,
    'sat': 5,
    'sunday': 6,
    'sun': 6
}

# Ordinal to number
HASHORDINALS = {
    'zeroth': 0,
    'first': 1,
    'second': 2,
    'third': 3,
    'fourth': 4,
    'forth': 4,
    'fifth': 5,
    'sixth': 6,
    'seventh': 7,
    'eighth': 8,
    'ninth': 9,
    'tenth': 10,
    'eleventh': 11,
    'twelfth': 12,
    'thirteenth': 13,
    'fourteenth': 14,
    'fifteenth': 15,
    'sixteenth': 16,
    'seventeenth': 17,
    'eighteenth': 18,
    'nineteenth': 19,
    'twentieth': 20,
    'last': -1
}

# A list tuple of regular expressions / parser fn to match
# Start with the widest match and narrow it down because the order of the match in this list matters
regex = [
    (
        re.compile(
            r'''
            (
                ((?P<dow>%s)[,\s]\s*)? #Matches Monday, 12 Jan 2012, 12 Jan 2012 etc
                (?P<day>\d{1,2}) # Matches a digit
                (%s)?
                [-\s] # One or more space
                (?P<month>%s) # Matches any month name
                [-\s] # Space
                (?P<year>%s) # Year
                ((\s|,\s|\s(%s))?\s*(%s))?
            )
            ''' % (day_names, re_ordinal, month_names, re_year, re_separator, re_time),
            (re.VERBOSE | re.IGNORECASE)
        ),
        lambda m, base_date: datetime(
            int(m.group('year') if m.group('year') else base_date.year),
            HASHMONTHS[m.group('month').strip().lower()],
            int(m.group('day') if m.group('day') else 1),
        ) + timedelta(**convert_time_to_hour_minute(
            m.group('hour'),
            m.group('minute'),
            m.group('convention')
        ))
    ),
    (
        re.compile(
            r'''
            (
                ((?P<dow>%s)[,\s][-\s]*)? #Matches Monday, Jan 12 2012, Jan 12 2012 etc
                (?P<month>%s) # Matches any month name
                [-\s] # Space
                ((?P<day>\d{1,2})) # Matches a digit
                (%s)?
                ([-\s](?P<year>%s))? # Year
                ((\s|,\s|\s(%s))?\s*(%s))?
            )
            ''' % (day_names, month_names, re_ordinal, re_year, re_separator, re_time),
            (re.VERBOSE | re.IGNORECASE)
        ),
        lambda m, base_date: datetime(
            int(m.group('year') if m.group('year') else base_date.year),
            HASHMONTHS[m.group('month').strip().lower()],
            int(m.group('day') if m.group('day') else 1)
        ) + timedelta(**convert_time_to_hour_minute(
            m.group('hour'),
            m.group('minute'),
            m.group('convention')
        ))
    ),
    (
        re.compile(
            r'''
            (
                (?P<month>%s) # Matches any month name
                [-\s] # One or more space
                (?P<day>\d{1,2}) # Matches a digit
                (%s)?
                [-\s]\s*?
                (?P<year>%s) # Year
                ((\s|,\s|\s(%s))?\s*(%s))?
            )
            ''' % (month_names, re_ordinal, re_year, re_separator, re_time),
            (re.VERBOSE | re.IGNORECASE)
        ),
        lambda m, base_date: datetime(
            int(m.group('year') if m.group('year') else base_date.year),
            HASHMONTHS[m.group('month').strip().lower()],
            int(m.group('day') if m.group('day') else 1),
        ) + timedelta(**convert_time_to_hour_minute(
            m.group('hour'),
            m.group('minute'),
            m.group('convention')
        ))
    ),
    (
        re.compile(
            r'''
            (
                ((?P<number>\d+|(%s[-\s]?)+)\s)? # Matches any number or string 25 or twenty five
                (?P<unit>%s)s?\s # Matches days, months, years, weeks, minutes
                (?P<duration>%s) # before, after, earlier, later, ago, from now
                (\s*(?P<base_time>(%s)))?
                ((\s|,\s|\s(%s))?\s*(%s))?
            )
            ''' % (numbers, re_dmy, re_duration, day_nearest_names, re_separator, re_time),
            (re.VERBOSE | re.IGNORECASE)
        ),
        lambda m, base_date: date_from_duration(
            base_date,
            m.group('number'),
            m.group('unit').lower(),
            m.group('duration').lower(),
            m.group('base_time')
        ) + timedelta(**convert_time_to_hour_minute(
            m.group('hour'),
            m.group('minute'),
            m.group('convention')
        ))
    ),
    (
        re.compile(
            r'''
            (
                (?P<ordinal>%s) # First quarter of 2014
                \s+
                quarter\sof
                \s+
                (?P<year>%s)
            )
            ''' % (re_ordinal, re_year),
            (re.VERBOSE | re.IGNORECASE)
        ),
        lambda m, base_date: date_from_quarter(
            base_date,
            HASHORDINALS[m.group('ordinal').lower()],
            int(m.group('year') if m.group('year') else base_date.year)
        )
    ),
    (
        re.compile(
            r'''
            (
                (?P<ordinal_value>\d+)
                (?P<ordinal>%s) # 1st January 2012
                ((\s|,\s|\s(%s))?\s*)?
                (?P<month>%s)
                ([,\s]\s*(?P<year>%s))?
            )
            ''' % (re_ordinal, re_separator, month_names, re_year),
            (re.VERBOSE | re.IGNORECASE)
        ),
        lambda m, base_date: datetime(
            int(m.group('year') if m.group('year') else base_date.year),
            int(HASHMONTHS[m.group('month').lower()] if m.group('month') else 1),
            int(m.group('ordinal_value') if m.group('ordinal_value') else 1),
        )
    ),
    (
        re.compile(
            r'''
            (
                (?P<month>%s)
                \s+
                (?P<ordinal_value>\d+)
                (?P<ordinal>%s) # January 1st 2012
                ([,\s]\s*(?P<year>%s))?
            )
            ''' % (month_names, re_ordinal, re_year),
            (re.VERBOSE | re.IGNORECASE)
        ),
        lambda m, base_date: datetime(
            int(m.group('year') if m.group('year') else base_date.year),
            int(HASHMONTHS[m.group('month').lower()] if m.group('month') else 1),
            int(m.group('ordinal_value') if m.group('ordinal_value') else 1),
        )
    ),
    (
        re.compile(
            r'''
            (?P<time>%s) # this, next, following, previous, last
            \s+
            ((?P<number>\d+|(%s[-\s]?)+)\s)?
            (?P<dmy>%s) # year, day, week, month, night, minute, min
            ((\s|,\s|\s(%s))?\s*(%s))?
            ''' % (re_timeframe, numbers, re_dmy, re_separator, re_time),
            (re.VERBOSE | re.IGNORECASE),
        ),
        lambda m, base_date: date_from_relative_week_year(
            base_date,
            m.group('time').lower(),
            m.group('dmy').lower(),
            m.group('number')
        ) + timedelta(**convert_time_to_hour_minute(
            m.group('hour'),
            m.group('minute'),
            m.group('convention')
        ))
    ),
    (
        re.compile(
            r'''
            (?P<time>%s) # this, next, following, previous, last
            \s+
            (?P<dow>%s) # mon - fri
            ((\s|,\s|\s(%s))?\s*(%s))?
            ''' % (re_timeframe, day_names, re_separator, re_time),
            (re.VERBOSE | re.IGNORECASE),
        ),
        lambda m, base_date: date_from_relative_day(
            base_date,
            m.group('time').lower(),
            m.group('dow')
        ) + timedelta(**convert_time_to_hour_minute(
            m.group('hour'),
            m.group('minute'),
            m.group('convention')
        ))
    ),
    (
        re.compile(
            r'''
            (
                (?P<day>\d{1,2}) # Day, Month
                (%s)
                [-\s] # One or more space
                (?P<month>%s)
            )
            ''' % (re_ordinal, month_names),
            (re.VERBOSE | re.IGNORECASE)
        ),
        lambda m, base_date: datetime(
            base_date.year,
            HASHMONTHS[m.group('month').strip().lower()],
            int(m.group('day') if m.group('day') else 1)
        )
    ),
    (
        re.compile(
            r'''
            (
                (?P<month>%s) # Month, day
                [-\s] # One or more space
                ((?P<day>\d{1,2})\b) # Matches a digit January 12
                (%s)?
            )
            ''' % (month_names, re_ordinal),
            (re.VERBOSE | re.IGNORECASE)
        ),
        lambda m, base_date: datetime(
            base_date.year,
            HASHMONTHS[m.group('month').strip().lower()],
            int(m.group('day') if m.group('day') else 1)
        )
    ),
    (
        re.compile(
            r'''
            (
                (?P<month>%s) # Month, year
                [-\s] # One or more space
                ((?P<year>\d{1,4})\b) # Matches a digit January 12
            )
            ''' % (month_names),
            (re.VERBOSE | re.IGNORECASE)
        ),
        lambda m, base_date: datetime(
            int(m.group('year')),
            HASHMONTHS[m.group('month').strip().lower()],
         
Download .txt
gitextract_if0fu1k3/

├── .codeclimate.yml
├── .github/
│   ├── FUNDING.yml
│   └── 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
│   │   │   │   ├── 0021_increase_text_max_length_to_1100.py
│   │   │   │   └── __init__.py
│   │   │   ├── model_admin.py
│   │   │   ├── models.py
│   │   │   └── settings.py
│   │   └── sqlalchemy_app/
│   │       ├── __init__.py
│   │       └── models.py
│   ├── filters.py
│   ├── languages.py
│   ├── logic/
│   │   ├── __init__.py
│   │   ├── best_match.py
│   │   ├── llm_adapters.py
│   │   ├── logic_adapter.py
│   │   ├── mathematical_evaluation.py
│   │   ├── mcp_tools.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/
│   │   ├── mobile.js
│   │   ├── silktide-consent-manager.css
│   │   ├── silktide-consent-manager.js
│   │   └── style.css
│   ├── _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/
│   │   ├── custom-models.rst
│   │   ├── index.rst
│   │   ├── settings.rst
│   │   ├── tutorial/
│   │   │   ├── django-filter-tutorial/
│   │   │   │   └── index.rst
│   │   │   ├── django-rest-framework-tutorial/
│   │   │   │   └── index.rst
│   │   │   ├── django-tutorial/
│   │   │   │   ├── 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
│   ├── security.rst
│   ├── 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
│   │   │   │   │   └── custom.css
│   │   │   │   └── js/
│   │   │   │       ├── bootstrap.js
│   │   │   │       ├── 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.xcf
│   └── chatterbot.xcf
├── pyproject.toml
├── setup.cfg
└── tests/
    ├── __init__.py
    ├── base_case.py
    ├── django_integration/
    │   ├── __init__.py
    │   ├── base_case.py
    │   ├── test_chatbot.py
    │   ├── test_chatterbot_corpus_training.py
    │   ├── test_chatterbot_settings.py
    │   ├── test_custom_models.py
    │   ├── test_django_adapter.py
    │   ├── test_logic_adapter_integration.py
    │   ├── test_secondary_database.py
    │   ├── test_settings.py
    │   └── test_statement_integration.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
Download .txt
SYMBOL INDEX (1619 symbols across 117 files)

FILE: chatterbot/__main__.py
  function get_chatterbot_version (line 10) | def get_chatterbot_version():

FILE: chatterbot/adapters.py
  class Adapter (line 1) | class Adapter(object):
    method __init__ (line 8) | def __init__(self, chatbot, **kwargs):
    class AdapterMethodNotImplementedError (line 11) | class AdapterMethodNotImplementedError(NotImplementedError):
      method __init__ (line 18) | def __init__(self, message='This method must be overridden in a subc...
    class InvalidAdapterTypeException (line 24) | class InvalidAdapterTypeException(Exception):

FILE: chatterbot/chatterbot.py
  class ChatBot (line 14) | class ChatBot(object):
    method __init__ (line 55) | def __init__(self, name, stream=False, **kwargs):
    method get_response (line 162) | def get_response(self, statement: Union[Statement, str, dict] = None, ...
    method generate_response (line 271) | def generate_response(self, input_statement, additional_response_selec...
    method learn_response (line 348) | def learn_response(self, statement, previous_statement=None):
    method get_latest_response (line 378) | def get_latest_response(self, conversation: str):
    class ChatBotException (line 393) | class ChatBotException(Exception):

FILE: chatterbot/comparisons.py
  class Comparator (line 10) | class Comparator:
    method __init__ (line 15) | def __init__(self, language):
    method __call__ (line 19) | def __call__(self, statement_a, statement_b):
    method compare_text (line 22) | def compare_text(self, text_a: str, text_b: str) -> float:
    method compare (line 30) | def compare(self, statement_a, statement_b) -> float:
  class LevenshteinDistance (line 37) | class LevenshteinDistance(Comparator):
    method compare_text (line 47) | def compare_text(self, text_a: str, text_b: str) -> float:
  class SpacySimilarity (line 74) | class SpacySimilarity(Comparator):
    method __init__ (line 100) | def __init__(self, language):
    method compare_text (line 108) | def compare_text(self, text_a: str, text_b: str) -> float:
  class JaccardSimilarity (line 125) | class JaccardSimilarity(Comparator):
    method __init__ (line 151) | def __init__(self, language):
    method compare_text (line 159) | def compare_text(self, text_a: str, text_b: str) -> float:

FILE: chatterbot/components.py
  function chatterbot_bigram_indexer (line 14) | def chatterbot_bigram_indexer(document):
  function chatterbot_lowercase_indexer (line 59) | def chatterbot_lowercase_indexer(document):

FILE: chatterbot/conversation.py
  class StatementMixin (line 5) | class StatementMixin(object):
    method get_statement_field_names (line 25) | def get_statement_field_names(self) -> list[str]:
    method get_tags (line 31) | def get_tags(self) -> list[str]:
    method add_tags (line 37) | def add_tags(self, *tags):
    method serialize (line 43) | def serialize(self) -> dict:
  class Statement (line 62) | class Statement(StatementMixin):
    method __init__ (line 82) | def __init__(self, text: str, in_response_to=None, **kwargs):
    method __str__ (line 108) | def __str__(self):
    method __repr__ (line 111) | def __repr__(self):
    method save (line 114) | def save(self):

FILE: chatterbot/corpus.py
  function get_file_path (line 21) | def get_file_path(dotted_path, extension='json') -> str:
  function read_corpus (line 44) | def read_corpus(file_name) -> dict:
  function list_corpus_files (line 62) | def list_corpus_files(dotted_path) -> list[str]:
  function load_corpus (line 78) | def load_corpus(*data_file_paths):

FILE: chatterbot/exceptions.py
  class OptionalDependencyImportError (line 1) | class OptionalDependencyImportError(ImportError):

FILE: chatterbot/ext/django_chatterbot/abstract_models.py
  class AbstractBaseTag (line 17) | class AbstractBaseTag(models.Model):
    class Meta (line 29) | class Meta:
    method __str__ (line 32) | def __str__(self):
  class AbstractBaseStatement (line 36) | class AbstractBaseStatement(models.Model, StatementMixin):
    class Meta (line 93) | class Meta:
    method __str__ (line 105) | def __str__(self):
    method get_tag_model (line 113) | def get_tag_model(cls):
    method get_tags (line 145) | def get_tags(self) -> list[str]:
    method add_tags (line 151) | def add_tags(self, *tags):

FILE: chatterbot/ext/django_chatterbot/apps.py
  class DjangoChatterBotConfig (line 4) | class DjangoChatterBotConfig(AppConfig):
    method ready (line 10) | def ready(self):

FILE: chatterbot/ext/django_chatterbot/migrations/0001_initial.py
  class Migration (line 5) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0002_statement_extra_data.py
  class Migration (line 5) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0003_change_occurrence_default.py
  class Migration (line 5) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0004_rename_in_response_to.py
  class Migration (line 6) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0005_statement_created_at.py
  class Migration (line 6) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0006_create_conversation.py
  class Migration (line 7) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0007_response_created_at.py
  class Migration (line 6) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0008_update_conversations.py
  class Migration (line 5) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0009_tags.py
  class Migration (line 5) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0010_statement_text.py
  class Migration (line 5) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0011_blank_extra_data.py
  class Migration (line 5) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0012_statement_created_at.py
  class Migration (line 5) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0013_change_conversations.py
  class Migration (line 5) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0014_remove_statement_extra_data.py
  class Migration (line 4) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0015_statement_persona.py
  class Migration (line 4) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0016_statement_stemmed_text.py
  class Migration (line 4) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0017_tags_unique.py
  class Migration (line 4) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0018_text_max_length.py
  class Migration (line 4) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0019_alter_statement_id_alter_tag_id_and_more.py
  class Migration (line 6) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0020_alter_statement_conversation_and_more.py
  class Migration (line 6) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/migrations/0021_increase_text_max_length_to_1100.py
  class Migration (line 16) | class Migration(migrations.Migration):

FILE: chatterbot/ext/django_chatterbot/model_admin.py
  class StatementAdmin (line 4) | class StatementAdmin(admin.ModelAdmin):
  class TagAdmin (line 10) | class TagAdmin(admin.ModelAdmin):

FILE: chatterbot/ext/django_chatterbot/models.py
  class Statement (line 4) | class Statement(AbstractBaseStatement):
    class Meta (line 13) | class Meta:
  class Tag (line 17) | class Tag(AbstractBaseTag):
    class Meta (line 25) | class Meta:

FILE: chatterbot/ext/sqlalchemy_app/models.py
  class ModelBase (line 10) | class ModelBase(object):
    method __tablename__ (line 16) | def __tablename__(cls) -> str:
  class Tag (line 40) | class Tag(Base):
  class Statement (line 51) | class Statement(Base, StatementMixin):
    method get_tags (line 102) | def get_tags(self) -> list[str]:
    method add_tags (line 108) | def add_tags(self, *tags):

FILE: chatterbot/filters.py
  function get_recent_repeated_responses (line 1) | def get_recent_repeated_responses(chatbot, conversation, sample=10, thre...

FILE: chatterbot/languages.py
  class AAR (line 5) | class AAR:
  class ABK (line 11) | class ABK:
  class ACE (line 17) | class ACE:
  class ACH (line 23) | class ACH:
  class ADA (line 29) | class ADA:
  class ADY (line 35) | class ADY:
  class AFH (line 41) | class AFH:
  class AFR (line 47) | class AFR:
  class AIN (line 53) | class AIN:
  class AKA (line 59) | class AKA:
  class AKK (line 65) | class AKK:
  class ALB (line 71) | class ALB:
  class ALE (line 77) | class ALE:
  class ALT (line 83) | class ALT:
  class AMH (line 89) | class AMH:
  class ANP (line 95) | class ANP:
  class ARA (line 101) | class ARA:
  class ARG (line 107) | class ARG:
  class ARM (line 113) | class ARM:
  class ARN (line 119) | class ARN:
  class ARP (line 125) | class ARP:
  class ARW (line 131) | class ARW:
  class ASM (line 137) | class ASM:
  class AST (line 143) | class AST:
  class AVA (line 149) | class AVA:
  class AVE (line 155) | class AVE:
  class AWA (line 161) | class AWA:
  class AYM (line 167) | class AYM:
  class AZE (line 173) | class AZE:
  class BAK (line 179) | class BAK:
  class BAL (line 185) | class BAL:
  class BAM (line 191) | class BAM:
  class BAN (line 197) | class BAN:
  class BAQ (line 203) | class BAQ:
  class BAS (line 209) | class BAS:
  class BEJ (line 215) | class BEJ:
  class BEL (line 221) | class BEL:
  class BEM (line 227) | class BEM:
  class BEN (line 233) | class BEN:
  class BHO (line 239) | class BHO:
  class BIK (line 245) | class BIK:
  class BIN (line 251) | class BIN:
  class BIS (line 257) | class BIS:
  class BLA (line 263) | class BLA:
  class BOS (line 269) | class BOS:
  class BRA (line 275) | class BRA:
  class BRE (line 281) | class BRE:
  class BUA (line 287) | class BUA:
  class BUG (line 293) | class BUG:
  class BUL (line 299) | class BUL:
  class BUR (line 305) | class BUR:
  class BYN (line 311) | class BYN:
  class CAD (line 317) | class CAD:
  class CAR (line 323) | class CAR:
  class CAT (line 329) | class CAT:
  class CEB (line 335) | class CEB:
  class CHA (line 341) | class CHA:
  class CHB (line 347) | class CHB:
  class CHE (line 353) | class CHE:
  class CHG (line 359) | class CHG:
  class CHI (line 365) | class CHI:
  class CHK (line 371) | class CHK:
  class CHM (line 377) | class CHM:
  class CHN (line 383) | class CHN:
  class CHO (line 389) | class CHO:
  class CHP (line 395) | class CHP:
  class CHR (line 401) | class CHR:
  class CHV (line 407) | class CHV:
  class CHY (line 413) | class CHY:
  class CNR (line 419) | class CNR:
  class COP (line 425) | class COP:
  class COR (line 431) | class COR:
  class COS (line 437) | class COS:
  class CPE (line 443) | class CPE:
  class CPF (line 449) | class CPF:
  class CPP (line 455) | class CPP:
  class CRE (line 461) | class CRE:
  class CRH (line 467) | class CRH:
  class CRP (line 473) | class CRP:
  class CSB (line 479) | class CSB:
  class CZE (line 485) | class CZE:
  class DAK (line 491) | class DAK:
  class DAN (line 497) | class DAN:
  class DAR (line 503) | class DAR:
  class DEL (line 509) | class DEL:
  class DEN (line 515) | class DEN:
  class DGR (line 521) | class DGR:
  class DIN (line 527) | class DIN:
  class DIV (line 533) | class DIV:
  class DOI (line 539) | class DOI:
  class DUA (line 545) | class DUA:
  class DUT (line 551) | class DUT:
  class DYU (line 557) | class DYU:
  class DZO (line 563) | class DZO:
  class EFI (line 569) | class EFI:
  class EKA (line 575) | class EKA:
  class ELX (line 581) | class ELX:
  class ENG (line 587) | class ENG:
  class EPO (line 593) | class EPO:
  class EST (line 599) | class EST:
  class EWE (line 605) | class EWE:
  class EWO (line 611) | class EWO:
  class FAN (line 617) | class FAN:
  class FAO (line 623) | class FAO:
  class FAT (line 629) | class FAT:
  class FIJ (line 635) | class FIJ:
  class FIL (line 641) | class FIL:
  class FIN (line 647) | class FIN:
  class FON (line 653) | class FON:
  class FRE (line 659) | class FRE:
  class FRR (line 665) | class FRR:
  class FRS (line 671) | class FRS:
  class FRY (line 677) | class FRY:
  class FUL (line 683) | class FUL:
  class FUR (line 689) | class FUR:
  class GAA (line 695) | class GAA:
  class GAY (line 701) | class GAY:
  class GBA (line 707) | class GBA:
  class GEO (line 713) | class GEO:
  class GER (line 719) | class GER:
  class GEZ (line 725) | class GEZ:
  class GIL (line 731) | class GIL:
  class GLA (line 737) | class GLA:
  class GLE (line 743) | class GLE:
  class GLG (line 749) | class GLG:
  class GLV (line 755) | class GLV:
  class GON (line 761) | class GON:
  class GOR (line 767) | class GOR:
  class GOT (line 773) | class GOT:
  class GRB (line 779) | class GRB:
  class GRE (line 785) | class GRE:
  class GRN (line 791) | class GRN:
  class GSW (line 797) | class GSW:
  class GUJ (line 803) | class GUJ:
  class GWI (line 809) | class GWI:
  class HAI (line 815) | class HAI:
  class HAT (line 821) | class HAT:
  class HAU (line 827) | class HAU:
  class HAW (line 833) | class HAW:
  class HEB (line 839) | class HEB:
  class HER (line 845) | class HER:
  class HIL (line 851) | class HIL:
  class HIN (line 857) | class HIN:
  class HIT (line 863) | class HIT:
  class HMN (line 869) | class HMN:
  class HMO (line 875) | class HMO:
  class HRV (line 881) | class HRV:
  class HSB (line 887) | class HSB:
  class HUN (line 893) | class HUN:
  class HUP (line 899) | class HUP:
  class IBA (line 905) | class IBA:
  class IBO (line 911) | class IBO:
  class ICE (line 917) | class ICE:
  class IDO (line 923) | class IDO:
  class III (line 929) | class III:
  class IKU (line 935) | class IKU:
  class ILE (line 941) | class ILE:
  class ILO (line 947) | class ILO:
  class INA (line 953) | class INA:
  class IND (line 959) | class IND:
  class INH (line 965) | class INH:
  class IPK (line 971) | class IPK:
  class ITA (line 977) | class ITA:
  class JAV (line 983) | class JAV:
  class JBO (line 989) | class JBO:
  class JPN (line 995) | class JPN:
  class JPR (line 1001) | class JPR:
  class JRB (line 1007) | class JRB:
  class KAA (line 1013) | class KAA:
  class KAB (line 1019) | class KAB:
  class KAC (line 1025) | class KAC:
  class KAL (line 1031) | class KAL:
  class KAM (line 1037) | class KAM:
  class KAN (line 1043) | class KAN:
  class KAS (line 1049) | class KAS:
  class KAU (line 1055) | class KAU:
  class KAW (line 1061) | class KAW:
  class KAZ (line 1067) | class KAZ:
  class KBD (line 1073) | class KBD:
  class KHA (line 1079) | class KHA:
  class KHM (line 1085) | class KHM:
  class KHO (line 1091) | class KHO:
  class KIK (line 1097) | class KIK:
  class KIN (line 1103) | class KIN:
  class KIR (line 1109) | class KIR:
  class KMB (line 1115) | class KMB:
  class KOK (line 1121) | class KOK:
  class KOM (line 1127) | class KOM:
  class KON (line 1133) | class KON:
  class KOR (line 1139) | class KOR:
  class KOS (line 1145) | class KOS:
  class KPE (line 1151) | class KPE:
  class KRC (line 1157) | class KRC:
  class KRL (line 1163) | class KRL:
  class KRU (line 1169) | class KRU:
  class KUA (line 1175) | class KUA:
  class KUM (line 1181) | class KUM:
  class KUR (line 1187) | class KUR:
  class KUT (line 1193) | class KUT:
  class LAD (line 1199) | class LAD:
  class LAH (line 1205) | class LAH:
  class LAM (line 1211) | class LAM:
  class LAO (line 1217) | class LAO:
  class LAT (line 1223) | class LAT:
  class LAV (line 1229) | class LAV:
  class LEZ (line 1235) | class LEZ:
  class LIM (line 1241) | class LIM:
  class LIN (line 1247) | class LIN:
  class LIT (line 1253) | class LIT:
  class LOL (line 1259) | class LOL:
  class LOZ (line 1265) | class LOZ:
  class LTZ (line 1271) | class LTZ:
  class LUA (line 1277) | class LUA:
  class LUB (line 1283) | class LUB:
  class LUG (line 1289) | class LUG:
  class LUI (line 1295) | class LUI:
  class LUN (line 1301) | class LUN:
  class LUO (line 1307) | class LUO:
  class LUS (line 1313) | class LUS:
  class MAC (line 1319) | class MAC:
  class MAD (line 1325) | class MAD:
  class MAG (line 1331) | class MAG:
  class MAH (line 1337) | class MAH:
  class MAI (line 1343) | class MAI:
  class MAK (line 1349) | class MAK:
  class MAL (line 1355) | class MAL:
  class MAN (line 1361) | class MAN:
  class MAO (line 1367) | class MAO:
  class MAR (line 1373) | class MAR:
  class MAS (line 1379) | class MAS:
  class MAY (line 1385) | class MAY:
  class MDF (line 1391) | class MDF:
  class MDR (line 1397) | class MDR:
  class MEN (line 1403) | class MEN:
  class MIC (line 1409) | class MIC:
  class MIN (line 1415) | class MIN:
  class MLG (line 1421) | class MLG:
  class MLT (line 1427) | class MLT:
  class MNC (line 1433) | class MNC:
  class MNI (line 1439) | class MNI:
  class MOH (line 1445) | class MOH:
  class MON (line 1451) | class MON:
  class MOS (line 1457) | class MOS:
  class MUS (line 1463) | class MUS:
  class MWL (line 1469) | class MWL:
  class MWR (line 1475) | class MWR:
  class MYV (line 1481) | class MYV:
  class NAP (line 1487) | class NAP:
  class NAU (line 1493) | class NAU:
  class NAV (line 1499) | class NAV:
  class NBL (line 1505) | class NBL:
  class NDE (line 1511) | class NDE:
  class NDO (line 1517) | class NDO:
  class NEP (line 1523) | class NEP:
  class NEW (line 1529) | class NEW:
  class NIA (line 1535) | class NIA:
  class NIU (line 1541) | class NIU:
  class NNO (line 1547) | class NNO:
  class NOB (line 1553) | class NOB:
  class NOG (line 1559) | class NOG:
  class NOR (line 1565) | class NOR:
  class NQO (line 1571) | class NQO:
  class NSO (line 1577) | class NSO:
  class NYA (line 1583) | class NYA:
  class NYM (line 1589) | class NYM:
  class NYN (line 1595) | class NYN:
  class NYO (line 1601) | class NYO:
  class NZI (line 1607) | class NZI:
  class OJI (line 1613) | class OJI:
  class ORI (line 1619) | class ORI:
  class ORM (line 1625) | class ORM:
  class OSA (line 1631) | class OSA:
  class OSS (line 1637) | class OSS:
  class PAG (line 1643) | class PAG:
  class PAL (line 1649) | class PAL:
  class PAM (line 1655) | class PAM:
  class PAN (line 1661) | class PAN:
  class PAP (line 1667) | class PAP:
  class PAU (line 1673) | class PAU:
  class PER (line 1679) | class PER:
  class PHN (line 1685) | class PHN:
  class PLI (line 1691) | class PLI:
  class POL (line 1697) | class POL:
  class PON (line 1703) | class PON:
  class POR (line 1709) | class POR:
  class PUS (line 1715) | class PUS:
  class QUE (line 1721) | class QUE:
  class RAJ (line 1727) | class RAJ:
  class RAP (line 1733) | class RAP:
  class RAR (line 1739) | class RAR:
  class ROH (line 1745) | class ROH:
  class ROM (line 1751) | class ROM:
  class RUM (line 1757) | class RUM:
  class RUN (line 1763) | class RUN:
  class RUP (line 1769) | class RUP:
  class RUS (line 1775) | class RUS:
  class SAD (line 1781) | class SAD:
  class SAG (line 1787) | class SAG:
  class SAH (line 1793) | class SAH:
  class SAM (line 1799) | class SAM:
  class SAN (line 1805) | class SAN:
  class SAS (line 1811) | class SAS:
  class SAT (line 1817) | class SAT:
  class SCN (line 1823) | class SCN:
  class SCO (line 1829) | class SCO:
  class SEL (line 1835) | class SEL:
  class SHN (line 1841) | class SHN:
  class SID (line 1847) | class SID:
  class SIN (line 1853) | class SIN:
  class SLO (line 1859) | class SLO:
  class SLV (line 1865) | class SLV:
  class SMA (line 1871) | class SMA:
  class SME (line 1877) | class SME:
  class SMJ (line 1883) | class SMJ:
  class SMN (line 1889) | class SMN:
  class SMO (line 1895) | class SMO:
  class SMS (line 1901) | class SMS:
  class SNA (line 1907) | class SNA:
  class SND (line 1913) | class SND:
  class SNK (line 1919) | class SNK:
  class SOG (line 1925) | class SOG:
  class SOM (line 1931) | class SOM:
  class SOT (line 1937) | class SOT:
  class SPA (line 1943) | class SPA:
  class SRD (line 1949) | class SRD:
  class SRN (line 1955) | class SRN:
  class SRP (line 1961) | class SRP:
  class SRR (line 1967) | class SRR:
  class SSW (line 1973) | class SSW:
  class SUK (line 1979) | class SUK:
  class SUN (line 1985) | class SUN:
  class SUS (line 1991) | class SUS:
  class SUX (line 1997) | class SUX:
  class SWA (line 2003) | class SWA:
  class SWE (line 2009) | class SWE:
  class SYC (line 2015) | class SYC:
  class SYR (line 2021) | class SYR:
  class TAH (line 2027) | class TAH:
  class TAM (line 2033) | class TAM:
  class TAT (line 2039) | class TAT:
  class TEL (line 2045) | class TEL:
  class TEM (line 2051) | class TEM:
  class TER (line 2057) | class TER:
  class TET (line 2063) | class TET:
  class TGK (line 2069) | class TGK:
  class TGL (line 2075) | class TGL:
  class THA (line 2081) | class THA:
  class TIB (line 2087) | class TIB:
  class TIG (line 2093) | class TIG:
  class TIR (line 2099) | class TIR:
  class TIV (line 2105) | class TIV:
  class TKL (line 2111) | class TKL:
  class TLH (line 2117) | class TLH:
  class TLI (line 2123) | class TLI:
  class TMH (line 2129) | class TMH:
  class TOG (line 2135) | class TOG:
  class TON (line 2141) | class TON:
  class TPI (line 2147) | class TPI:
  class TSI (line 2153) | class TSI:
  class TSN (line 2159) | class TSN:
  class TSO (line 2165) | class TSO:
  class TUK (line 2171) | class TUK:
  class TUM (line 2177) | class TUM:
  class TUR (line 2183) | class TUR:
  class TVL (line 2189) | class TVL:
  class TWI (line 2195) | class TWI:
  class TYV (line 2201) | class TYV:
  class UDM (line 2207) | class UDM:
  class UGA (line 2213) | class UGA:
  class UIG (line 2219) | class UIG:
  class UKR (line 2225) | class UKR:
  class UMB (line 2231) | class UMB:
  class UND (line 2237) | class UND:
  class URD (line 2243) | class URD:
  class UZB (line 2249) | class UZB:
  class VAI (line 2255) | class VAI:
  class VEN (line 2261) | class VEN:
  class VIE (line 2267) | class VIE:
  class VOL (line 2273) | class VOL:
  class VOT (line 2279) | class VOT:
  class WAL (line 2285) | class WAL:
  class WAR (line 2291) | class WAR:
  class WAS (line 2297) | class WAS:
  class WEL (line 2303) | class WEL:
  class WLN (line 2309) | class WLN:
  class WOL (line 2315) | class WOL:
  class XAL (line 2321) | class XAL:
  class XHO (line 2327) | class XHO:
  class YAO (line 2333) | class YAO:
  class YAP (line 2339) | class YAP:
  class YID (line 2345) | class YID:
  class YOR (line 2351) | class YOR:
  class ZAP (line 2357) | class ZAP:
  class ZBL (line 2363) | class ZBL:
  class ZEN (line 2369) | class ZEN:
  class ZGH (line 2375) | class ZGH:
  class ZHA (line 2381) | class ZHA:
  class ZHS (line 2387) | class ZHS:
  class ZHT (line 2393) | class ZHT:
  class ZUL (line 2399) | class ZUL:
  class ZUN (line 2405) | class ZUN:
  class ZZA (line 2411) | class ZZA:
  function get_language_classes (line 2417) | def get_language_classes():

FILE: chatterbot/logic/best_match.py
  class BestMatch (line 6) | class BestMatch(LogicAdapter):
    method __init__ (line 21) | def __init__(self, chatbot, **kwargs):
    method process (line 26) | def process(self, input_statement: Statement, additional_response_sele...

FILE: chatterbot/logic/llm_adapters.py
  class LLMLogicAdapter (line 20) | class LLMLogicAdapter(LogicAdapter):
    method __init__ (line 55) | def __init__(self, chatbot, **kwargs):
    method _initialize_tool_adapters (line 102) | def _initialize_tool_adapters(self, adapter_configs: List[Union[str, D...
    method _get_conversation_context (line 127) | def _get_conversation_context(self, input_statement: Statement) -> Lis...
    method _build_base_messages (line 173) | def _build_base_messages(self, input_statement: Statement, system_mess...
    method _format_error_response (line 189) | def _format_error_response(self, error: Exception) -> str:
    method _supports_native_tools (line 201) | def _supports_native_tools(self) -> bool:
    method _detect_tool_capability (line 220) | def _detect_tool_capability(self) -> bool:
    method _get_tools_for_llm (line 230) | def _get_tools_for_llm(self) -> List[Dict[str, Any]]:
    method _execute_tool (line 240) | def _execute_tool(self, tool_name: str, parameters: Dict[str, Any]) ->...
    method _handle_native_tool_calling (line 280) | def _handle_native_tool_calling(self, input_statement: Statement) -> S...
    method _handle_prompt_based_tool_calling (line 293) | def _handle_prompt_based_tool_calling(self, input_statement: Statement...
    method _call_llm (line 361) | def _call_llm(self, input_statement: Statement, system_message: Option...
    method _call_llm_with_context (line 375) | def _call_llm_with_context(self, input_statement: Statement, additiona...
    method _calculate_confidence (line 389) | def _calculate_confidence(self, response_text: str) -> float:
    method process (line 417) | def process(self, statement: Statement, additional_response_selection_...
  class OllamaLogicAdapter (line 442) | class OllamaLogicAdapter(LLMLogicAdapter):
    method __init__ (line 468) | def __init__(self, chatbot, **kwargs):
    method _detect_tool_capability (line 484) | def _detect_tool_capability(self) -> bool:
    method _get_tools_for_llm (line 549) | def _get_tools_for_llm(self) -> List[Dict[str, Any]]:
    method _call_llm (line 563) | def _call_llm(self, input_statement: Statement, system_message: Option...
    method _call_llm_with_context (line 587) | def _call_llm_with_context(self, input_statement: Statement, additiona...
    method _handle_native_tool_calling (line 611) | def _handle_native_tool_calling(self, input_statement: Statement) -> S...
  class OpenAILogicAdapter (line 696) | class OpenAILogicAdapter(LLMLogicAdapter):
    method __init__ (line 722) | def __init__(self, chatbot, **kwargs):
    method _detect_tool_capability (line 737) | def _detect_tool_capability(self) -> bool:
    method _get_tools_for_llm (line 746) | def _get_tools_for_llm(self) -> List[Dict[str, Any]]:
    method _call_llm (line 760) | def _call_llm(self, input_statement: Statement, system_message: Option...
    method _call_llm_with_context (line 784) | def _call_llm_with_context(self, input_statement: Statement, additiona...
    method _handle_native_tool_calling (line 808) | def _handle_native_tool_calling(self, input_statement: Statement) -> S...

FILE: chatterbot/logic/logic_adapter.py
  class LogicAdapter (line 10) | class LogicAdapter(Adapter):
    method __init__ (line 37) | def __init__(self, chatbot, **kwargs):
    method can_process (line 80) | def can_process(self, statement) -> bool:
    method process (line 89) | def process(self, statement: Statement, additional_response_selection_...
    method get_default_response (line 108) | def get_default_response(self, input_statement: Statement) -> Statement:
    method class_name (line 131) | def class_name(self) -> str:

FILE: chatterbot/logic/mathematical_evaluation.py
  class MathematicalEvaluation (line 7) | class MathematicalEvaluation(LogicAdapter, MCPToolAdapter):
    method __init__ (line 23) | def __init__(self, chatbot, **kwargs):
    method can_process (line 29) | def can_process(self, statement) -> bool:
    method process (line 38) | def process(self, statement: Statement, additional_response_selection_...
    method get_tool_schema (line 71) | def get_tool_schema(self):
    method execute_as_tool (line 90) | def execute_as_tool(self, **kwargs):

FILE: chatterbot/logic/mcp_tools.py
  class MCPToolAdapter (line 12) | class MCPToolAdapter(ABC):
    method get_tool_schema (line 44) | def get_tool_schema(self) -> Dict[str, Any]:
    method execute_as_tool (line 72) | def execute_as_tool(self, **kwargs) -> Any:
    method get_tool_name (line 90) | def get_tool_name(self) -> str:
    method validate_tool_parameters (line 100) | def validate_tool_parameters(self, **kwargs) -> bool:
  function is_tool_adapter (line 140) | def is_tool_adapter(adapter) -> bool:
  function convert_to_openai_tool_format (line 158) | def convert_to_openai_tool_format(schema: Dict[str, Any]) -> Dict[str, A...
  function convert_to_ollama_tool_format (line 188) | def convert_to_ollama_tool_format(schema: Dict[str, Any]) -> Dict[str, A...

FILE: chatterbot/logic/specific_response.py
  class SpecificResponseAdapter (line 8) | class SpecificResponseAdapter(LogicAdapter):
    method __init__ (line 20) | def __init__(self, chatbot, **kwargs):
    method _initialize_nlp (line 48) | def _initialize_nlp(self, language):
    method can_process (line 53) | def can_process(self, statement) -> bool:
    method process (line 65) | def process(self, statement: Statement, additional_response_selection_...

FILE: chatterbot/logic/time_adapter.py
  class TimeLogicAdapter (line 10) | class TimeLogicAdapter(LogicAdapter, MCPToolAdapter):
    method __init__ (line 22) | def __init__(self, chatbot, **kwargs):
    method process (line 55) | def process(self, statement: Statement, additional_response_selection_...
    method get_tool_schema (line 71) | def get_tool_schema(self):
    method execute_as_tool (line 85) | def execute_as_tool(self, **kwargs):

FILE: chatterbot/logic/unit_conversion.py
  class UnitConversion (line 11) | class UnitConversion(LogicAdapter, MCPToolAdapter):
    method __init__ (line 25) | def __init__(self, chatbot, **kwargs):
    method get_unit (line 78) | def get_unit(self, unit_variations):
    method get_valid_units (line 93) | def get_valid_units(self, from_unit, target_unit):
    method handle_matches (line 111) | def handle_matches(self, match):
    method can_process (line 141) | def can_process(self, statement) -> bool:
    method process (line 146) | def process(self, statement: Statement, additional_response_selection_...
    method get_tool_schema (line 168) | def get_tool_schema(self):
    method execute_as_tool (line 187) | def execute_as_tool(self, **kwargs):

FILE: chatterbot/parsing.py
  function convert_string_to_number (line 506) | def convert_string_to_number(value: str) -> int:
  function convert_time_to_hour_minute (line 520) | def convert_time_to_hour_minute(hour: str, minute: str, convention: str)...
  function date_from_quarter (line 547) | def date_from_quarter(base_date: datetime, ordinal: int, year: int) -> l...
  function date_from_relative_day (line 564) | def date_from_relative_day(base_date: datetime, time: str, dow: str) -> ...
  function date_from_relative_week_year (line 587) | def date_from_relative_week_year(base_date: datetime, time: str, dow: st...
  function date_from_adverb (line 649) | def date_from_adverb(base_date: datetime, name: str) -> datetime:
  function date_from_duration (line 665) | def date_from_duration(base_date: datetime, number_as_string: str, unit:...
  function this_week_day (line 695) | def this_week_day(base_date: datetime, weekday: int) -> datetime:
  function previous_week_day (line 711) | def previous_week_day(base_date: datetime, weekday: int) -> datetime:
  function next_week_day (line 721) | def next_week_day(base_date: datetime, weekday: int) -> datetime:
  function datetime_parsing (line 733) | def datetime_parsing(text: str, base_date: datetime = datetime.now()) ->...

FILE: chatterbot/preprocessors.py
  function clean_whitespace (line 10) | def clean_whitespace(statement: Statement) -> Statement:
  function unescape_html (line 28) | def unescape_html(statement: Statement) -> Statement:
  function convert_to_ascii (line 38) | def convert_to_ascii(statement: Statement) -> Statement:

FILE: chatterbot/response_selection.py
  function get_most_frequent_response (line 9) | def get_most_frequent_response(input_statement: Statement, response_list...
  function get_first_response (line 53) | def get_first_response(input_statement: Statement, response_list: list[S...
  function get_random_response (line 72) | def get_random_response(input_statement: Statement, response_list: list[...

FILE: chatterbot/search.py
  class IndexedTextSearch (line 1) | class IndexedTextSearch:
    method __init__ (line 13) | def __init__(self, chatbot, **kwargs):
    method search (line 31) | def search(self, input_statement, **additional_parameters):
  class TextSearch (line 82) | class TextSearch:
    method __init__ (line 94) | def __init__(self, chatbot, **kwargs):
    method search (line 112) | def search(self, input_statement, **additional_parameters):
  class SemanticVectorSearch (line 162) | class SemanticVectorSearch:
    method __init__ (line 196) | def __init__(self, chatbot, **kwargs):
    method search (line 203) | def search(self, input_statement, **additional_parameters):

FILE: chatterbot/storage/django_storage.py
  class DjangoStorageAdapter (line 5) | class DjangoStorageAdapter(StorageAdapter):
    method __init__ (line 18) | def __init__(self, **kwargs):
    method get_statement_model (line 48) | def get_statement_model(self):
    method get_tag_model (line 52) | def get_tag_model(self):
    method count (line 56) | def count(self) -> int:
    method filter (line 60) | def filter(self, **kwargs):
    method create (line 132) | def create(self, **kwargs):
    method create_many (line 159) | def create_many(self, statements):
    method update (line 189) | def update(self, statement):
    method get_random (line 215) | def get_random(self):
    method remove (line 228) | def remove(self, statement_text):
    method drop (line 240) | def drop(self):

FILE: chatterbot/storage/mongodb.py
  class MongoDatabaseAdapter (line 6) | class MongoDatabaseAdapter(StorageAdapter):
    method __init__ (line 33) | def __init__(self, **kwargs):
    method get_statement_model (line 60) | def get_statement_model(self):
    method count (line 72) | def count(self) -> int:
    method mongo_to_object (line 75) | def mongo_to_object(self, statement_data):
    method filter (line 86) | def filter(self, **kwargs):
    method create (line 176) | def create(self, **kwargs):
    method create_many (line 192) | def create_many(self, statements):
    method update (line 207) | def update(self, statement):
    method get_random (line 242) | def get_random(self):
    method remove (line 257) | def remove(self, statement_text):
    method drop (line 263) | def drop(self):
    method close (line 269) | def close(self):

FILE: chatterbot/storage/redis.py
  function _escape_redis_special_characters (line 8) | def _escape_redis_special_characters(text):
  class RedisVectorStorageAdapter (line 26) | class RedisVectorStorageAdapter(StorageAdapter):
    class RedisMetaDataType (line 93) | class RedisMetaDataType:
    method __init__ (line 101) | def __init__(self, **kwargs):
    method get_preferred_tagger (line 190) | def get_preferred_tagger(self):
    method get_preferred_search_algorithm (line 198) | def get_preferred_search_algorithm(self):
    method get_statement_model (line 205) | def get_statement_model(self):
    method _calculate_confidence_from_distance (line 217) | def _calculate_confidence_from_distance(self, distance):
    method _add_confidence_to_results (line 228) | def _add_confidence_to_results(self, results):
    method model_to_object (line 241) | def model_to_object(self, document):
    method count (line 275) | def count(self) -> int:
    method remove (line 288) | def remove(self, statement):
    method filter (line 297) | def filter(self, page_size=4, **kwargs):
    method create (line 457) | def create(
    method create_many (line 508) | def create_many(self, statements):
    method update (line 533) | def update(self, statement):
    method get_random (line 574) | def get_random(self):
    method drop (line 609) | def drop(self):
    method close (line 626) | def close(self):

FILE: chatterbot/storage/sql_storage.py
  class SQLStorageAdapter (line 5) | class SQLStorageAdapter(StorageAdapter):
    method __init__ (line 20) | def __init__(self, **kwargs):
    method get_statement_model (line 119) | def get_statement_model(self):
    method get_tag_model (line 126) | def get_tag_model(self):
    method model_to_object (line 133) | def model_to_object(self, statement):
    method count (line 138) | def count(self) -> int:
    method remove (line 151) | def remove(self, statement_text):
    method filter (line 168) | def filter(self, **kwargs):
    method create (line 258) | def create(
    method create_many (line 318) | def create_many(self, statements):
    method update (line 373) | def update(self, statement):
    method get_random (line 428) | def get_random(self):
    method drop (line 449) | def drop(self):
    method create_database (line 465) | def create_database(self):
    method close (line 472) | def close(self):

FILE: chatterbot/storage/storage_adapter.py
  class StorageAdapter (line 4) | class StorageAdapter(object):
    method __init__ (line 10) | def __init__(self, *args, **kwargs):
    method get_model (line 22) | def get_model(self, model_name):
    method get_object (line 34) | def get_object(self, object_name):
    method get_statement_object (line 46) | def get_statement_object(self):
    method count (line 57) | def count(self) -> int:
    method remove (line 65) | def remove(self, statement_text):
    method filter (line 75) | def filter(self, **kwargs):
    method create (line 126) | def create(self, **kwargs):
    method create_many (line 135) | def create_many(self, statements):
    method update (line 143) | def update(self, statement):
    method get_random (line 152) | def get_random(self):
    method drop (line 160) | def drop(self):
    method close (line 168) | def close(self):
    method get_preferred_tagger (line 176) | def get_preferred_tagger(self):
    method get_preferred_search_algorithm (line 215) | def get_preferred_search_algorithm(self):
    class EmptyDatabaseException (line 257) | class EmptyDatabaseException(Exception):
      method __init__ (line 259) | def __init__(self, message=None):
    class AdapterMethodNotImplementedError (line 263) | class AdapterMethodNotImplementedError(NotImplementedError):

FILE: chatterbot/tagging.py
  class NoOpTagger (line 7) | class NoOpTagger(object):
    method __init__ (line 13) | def __init__(self, language=None):
    method needs_text_indexing (line 16) | def needs_text_indexing(self):
    method get_text_index_string (line 25) | def get_text_index_string(self, text: Union[str, List[str]]):
    method as_nlp_pipeline (line 31) | def as_nlp_pipeline(
  class LowercaseTagger (line 57) | class LowercaseTagger(object):
    method __init__ (line 62) | def __init__(self, language=None):
    method needs_text_indexing (line 74) | def needs_text_indexing(self):
    method get_text_index_string (line 83) | def get_text_index_string(self, text: Union[str, List[str]]):
    method as_nlp_pipeline (line 91) | def as_nlp_pipeline(
  class PosLemmaTagger (line 119) | class PosLemmaTagger(object):
    method __init__ (line 121) | def __init__(self, language=None):
    method needs_text_indexing (line 135) | def needs_text_indexing(self):
    method get_text_index_string (line 144) | def get_text_index_string(self, text: Union[str, List[str]]) -> str:
    method as_nlp_pipeline (line 155) | def as_nlp_pipeline(

FILE: chatterbot/trainers.py
  class Trainer (line 14) | class Trainer(object):
    method __init__ (line 24) | def __init__(self, chatbot: ChatBot, **kwargs):
    method get_preprocessed_statement (line 34) | def get_preprocessed_statement(self, input_statement: Statement) -> St...
    method train (line 43) | def train(self, *args, **kwargs):
    class TrainerInitializationException (line 49) | class TrainerInitializationException(Exception):
      method __init__ (line 55) | def __init__(self, message=None):
    method _generate_export_data (line 62) | def _generate_export_data(self) -> list:
    method export_for_training (line 70) | def export_for_training(self, file_path='./export.json'):
  class ListTrainer (line 80) | class ListTrainer(Trainer):
    method train (line 86) | def train(self, conversation: List[str]):
  class ChatterBotCorpusTrainer (line 138) | class ChatterBotCorpusTrainer(Trainer):
    method train (line 144) | def train(self, *corpus_paths: Union[str, List[str]]):
  class GenericFileTrainer (line 222) | class GenericFileTrainer(Trainer):
    method __init__ (line 238) | def __init__(self, chatbot: ChatBot, **kwargs):
    method _get_file_list (line 252) | def _get_file_list(self, data_path: str, limit: Union[int, None]):
    method train (line 278) | def train(self, data_path: str, limit=None):
  class CsvFileTrainer (line 467) | class CsvFileTrainer(GenericFileTrainer):
    method __init__ (line 483) | def __init__(self, chatbot: ChatBot, **kwargs):
  class JsonFileTrainer (line 489) | class JsonFileTrainer(GenericFileTrainer):
    method __init__ (line 509) | def __init__(self, chatbot: ChatBot, **kwargs):
  class UbuntuCorpusTrainer (line 520) | class UbuntuCorpusTrainer(CsvFileTrainer):
    method __init__ (line 533) | def __init__(self, chatbot: ChatBot, **kwargs):
    method is_downloaded (line 555) | def is_downloaded(self, file_path: str):
    method is_extracted (line 565) | def is_extracted(self, file_path: str):
    method download (line 575) | def download(self, url: str, show_status=True):
    method extract (line 614) | def extract(self, file_path: str):
    method _get_file_list (line 654) | def _get_file_list(self, data_path: str, limit: Union[int, None]):
    method train (line 685) | def train(self, data_download_url: str, limit: Union[int, None] = None):

FILE: chatterbot/utils.py
  function import_module (line 9) | def import_module(dotted_path: str):
  function initialize_class (line 21) | def initialize_class(data: Union[dict, str], *args, **kwargs):
  function validate_adapter_class (line 37) | def validate_adapter_class(validate_class, adapter_class):
  function get_response_time (line 74) | def get_response_time(chatbot, statement='Hello') -> float:
  function get_model_for_language (line 91) | def get_model_for_language(language):

FILE: chatterbot/vectorstores.py
  class RedisVectorStore (line 15) | class RedisVectorStore(LangChainRedisVectorStore):
    method query_search (line 20) | def query_search(

FILE: docs/_ext/canonical.py
  function setup_canonical_func (line 5) | def setup_canonical_func(app, pagename, templatename, context, doctree):
  function setup (line 28) | def setup(app):

FILE: docs/_ext/github.py
  function setup_github_func (line 9) | def setup_github_func(app, pagename, templatename, context, doctree):
  function setup (line 25) | def setup(app):

FILE: docs/_static/mobile.js
  function createOverlay (line 21) | function createOverlay() {
  function openMobileMenu (line 37) | function openMobileMenu() {
  function closeMobileMenu (line 50) | function closeMobileMenu() {
  function toggleMobileMenu (line 63) | function toggleMobileMenu(event) {
  function initMobileMenu (line 79) | function initMobileMenu() {
  function cleanupMobileMenu (line 126) | function cleanupMobileMenu() {
  function makeTablesResponsive (line 162) | function makeTablesResponsive() {
  function enhanceCodeBlocks (line 186) | function enhanceCodeBlocks() {
  function handleResize (line 202) | function handleResize() {
  function improveAccessibility (line 217) | function improveAccessibility() {
  function init (line 246) | function init() {

FILE: docs/_static/silktide-consent-manager.js
  class SilktideCookieBanner (line 3) | class SilktideCookieBanner {
    method constructor (line 4) | constructor(config) {
    method destroyCookieBanner (line 37) | destroyCookieBanner() {
    method createWrapper (line 57) | createWrapper() {
    method createWrapperChild (line 66) | createWrapperChild(htmlContent, id) {
    method createBackdrop (line 85) | createBackdrop() {
    method showBackdrop (line 89) | showBackdrop() {
    method hideBackdrop (line 99) | hideBackdrop() {
    method shouldShowBackdrop (line 110) | shouldShowBackdrop() {
    method updateCheckboxState (line 115) | updateCheckboxState(saveToStorage = false) {
    method setInitialCookieChoiceMade (line 167) | setInitialCookieChoiceMade() {
    method handleCookieChoice (line 174) | handleCookieChoice(accepted) {
    method getAcceptedCookies (line 213) | getAcceptedCookies() {
    method runAcceptedCookieCallbacks (line 222) | runAcceptedCookieCallbacks() {
    method runRejectedCookieCallbacks (line 234) | runRejectedCookieCallbacks() {
    method runStoredCookiePreferenceCallbacks (line 248) | runStoredCookiePreferenceCallbacks() {
    method loadRequiredCookies (line 261) | loadRequiredCookies() {
    method getBannerContent (line 273) | getBannerContent() {
    method hasSetInitialCookieChoices (line 330) | hasSetInitialCookieChoices() {
    method createBanner (line 334) | createBanner() {
    method removeBanner (line 349) | removeBanner() {
    method shouldShowBanner (line 361) | shouldShowBanner() {
    method getModalContent (line 373) | getModalContent() {
    method createModal (line 475) | createModal() {
    method toggleModal (line 480) | toggleModal(show) {
    method getCookieIconContent (line 522) | getCookieIconContent() {
    method createCookieIcon (line 530) | createCookieIcon() {
    method showCookieIcon (line 559) | showCookieIcon() {
    method hideCookieIcon (line 565) | hideCookieIcon() {
    method handleClosedWithNoChoice (line 575) | handleClosedWithNoChoice() {
    method getFocusableElements (line 606) | getFocusableElements(element) {
    method setupEventListeners (line 615) | setupEventListeners() {
    method getBannerSuffix (line 766) | getBannerSuffix() {
    method preventBodyScroll (line 773) | preventBodyScroll() {
    method allowBodyScroll (line 780) | allowBodyScroll() {
  function updateCookieBannerConfig (line 793) | function updateCookieBannerConfig(userConfig = {}) {
  function initCookieBanner (line 811) | function initCookieBanner() {
  function injectScript (line 817) | function injectScript(url, loadOption) {

FILE: examples/django_example/django_example/management/commands/train.py
  class Command (line 20) | class Command(BaseCommand):
    method handle (line 23) | def handle(self, *args, **options):

FILE: examples/django_example/django_example/static/js/bootstrap.js
  function i (line 6) | function i(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.en...
  function s (line 6) | function s(e,t,n){return t&&i(e.prototype,t),n&&i(e,n),e}
  function t (line 6) | function t(t,e){var n=Object.keys(t);if(Object.getOwnPropertySymbols){va...
  function l (line 6) | function l(o){for(var e=1;e<arguments.length;e++){var r=null!=arguments[...
  function o (line 6) | function o(e){var t=this,n=!1;return p(this).one(m.TRANSITION_END,functi...
  function i (line 6) | function i(e){this._element=e}
  function n (line 6) | function n(e){this._element=e}
  function r (line 6) | function r(e,t){this._items=null,this._interval=null,this._activeElement...
  function a (line 6) | function a(t,e){this._isTransitioning=!1,this._element=t,this._config=th...
  function Ne (line 6) | function Ne(e){return e&&"[object Function]"==={}.toString.call(e)}
  function ke (line 6) | function ke(e,t){if(1!==e.nodeType)return[];var n=e.ownerDocument.defaul...
  function Le (line 6) | function Le(e){return"HTML"===e.nodeName?e:e.parentNode||e.host}
  function Pe (line 6) | function Pe(e){if(!e)return document.body;switch(e.nodeName){case"HTML":...
  function xe (line 6) | function xe(e){return e&&e.referenceNode?e.referenceNode:e}
  function Re (line 6) | function Re(e){return 11===e?je:10===e?He:je||He}
  function Fe (line 6) | function Fe(e){if(!e)return document.documentElement;for(var t=Re(10)?do...
  function Me (line 6) | function Me(e){return null!==e.parentNode?Me(e.parentNode):e}
  function We (line 6) | function We(e,t){if(!(e&&e.nodeType&&t&&t.nodeType))return document.docu...
  function Ue (line 6) | function Ue(e,t){var n="top"===(1<arguments.length&&void 0!==t?t:"top")?...
  function Be (line 6) | function Be(e,t){var n="x"===t?"Left":"Top",i="Left"==n?"Right":"Bottom"...
  function qe (line 6) | function qe(e,t,n,i){return Math.max(t["offset"+e],t["scroll"+e],n["clie...
  function Ke (line 6) | function Ke(e){var t=e.body,n=e.documentElement,i=Re(10)&&getComputedSty...
  function Ve (line 6) | function Ve(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.e...
  function Ye (line 6) | function Ye(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enum...
  function Xe (line 6) | function Xe(e){return ze({},e,{right:e.left+e.width,bottom:e.top+e.heigh...
  function Ge (line 6) | function Ge(e){var t={};try{if(Re(10)){t=e.getBoundingClientRect();var n...
  function $e (line 6) | function $e(e,t,n){var i=2<arguments.length&&void 0!==n&&n,o=Re(10),r="H...
  function Je (line 6) | function Je(e){if(!e||!e.parentElement||Re())return document.documentEle...
  function Ze (line 6) | function Ze(e,t,n,i,o){var r=4<arguments.length&&void 0!==o&&o,s={top:0,...
  function et (line 6) | function et(e,t,i,n,o,r){var s=5<arguments.length&&void 0!==r?r:0;if(-1=...
  function tt (line 6) | function tt(e,t,n,i){var o=3<arguments.length&&void 0!==i?i:null;return ...
  function nt (line 6) | function nt(e){var t=e.ownerDocument.defaultView.getComputedStyle(e),n=p...
  function it (line 6) | function it(e){var t={left:"right",right:"left",bottom:"top",top:"bottom...
  function ot (line 6) | function ot(e,t,n){n=n.split("-")[0];var i=nt(e),o={width:i.width,height...
  function rt (line 6) | function rt(e,t){return Array.prototype.find?e.find(t):e.filter(t)[0]}
  function st (line 6) | function st(e,n,t){return(void 0===t?e:e.slice(0,function(e,t,n){if(Arra...
  function at (line 6) | function at(e,n){return e.some(function(e){var t=e.name;return e.enabled...
  function lt (line 6) | function lt(e){for(var t=[!1,"ms","Webkit","Moz","O"],n=e.charAt(0).toUp...
  function ct (line 6) | function ct(e){var t=e.ownerDocument;return t?t.defaultView:window}
  function ht (line 6) | function ht(e,t,n,i){n.updateBound=i,ct(e).addEventListener("resize",n.u...
  function ut (line 6) | function ut(){this.state.eventsEnabled&&(cancelAnimationFrame(this.sched...
  function ft (line 6) | function ft(e){return""!==e&&!isNaN(parseFloat(e))&&isFinite(e)}
  function dt (line 6) | function dt(n,i){Object.keys(i).forEach(function(e){var t="";-1!==["widt...
  function pt (line 6) | function pt(e,t){function n(e){return e}var i=e.offsets,o=i.popper,r=i.r...
  function gt (line 6) | function gt(e,t,n){var i=rt(e,function(e){return e.name===t}),o=!!i&&e.s...
  function yt (line 6) | function yt(e,t){var n=1<arguments.length&&void 0!==t&&t,i=vt.indexOf(e)...
  function Tt (line 6) | function Tt(e,o,r,t){var s=[0,0],a=-1!==["right","left"].indexOf(t),n=e....
  function Dt (line 6) | function Dt(e,t){var n=this,i=2<arguments.length&&void 0!==arguments[2]?...
  function c (line 6) | function c(e,t){this._element=e,this._popper=null,this._config=this._get...
  function o (line 6) | function o(e,t){this._config=this._getConfig(t),this._element=e,this._di...
  function o (line 6) | function o(){t._config.focus&&t._element.focus(),t._isTransitioning=!1,p...
  function In (line 6) | function In(e,r,t){if(0===e.length)return e;if(t&&"function"==typeof t)r...
  function i (line 6) | function i(e,t){if("undefined"==typeof St)throw new TypeError("Bootstrap...
  function t (line 6) | function t(){n._hoverState!==Fn&&i.parentNode&&i.parentNode.removeChild(...
  function i (line 6) | function i(){return e.apply(this,arguments)||this}
  function n (line 6) | function n(e,t){var n=this;this._element=e,this._scrollElement="BODY"===...
  function i (line 6) | function i(e){this._element=e}
  function i (line 6) | function i(){return o._transitionComplete(e,r,n)}
  function i (line 6) | function i(e,t){this._element=e,this._config=this._getConfig(t),this._ti...
  function e (line 6) | function e(){t._element.classList.add(Zi),p(t._element).trigger($i.HIDDEN)}

FILE: examples/django_example/django_example/static/js/jquery.js
  function isArraylike (line 848) | function isArraylike( obj ) {
  function Sizzle (line 1048) | function Sizzle( selector, context, results, seed ) {
  function createCache (line 1163) | function createCache() {
  function markFunction (line 1181) | function markFunction( fn ) {
  function assert (line 1190) | function assert( fn ) {
  function addHandle (line 1212) | function addHandle( attrs, handler ) {
  function siblingCheck (line 1227) | function siblingCheck( a, b ) {
  function createInputPseudo (line 1254) | function createInputPseudo( type ) {
  function createButtonPseudo (line 1265) | function createButtonPseudo( type ) {
  function createPositionalPseudo (line 1276) | function createPositionalPseudo( fn ) {
  function setFilters (line 2259) | function setFilters() {}
  function tokenize (line 2263) | function tokenize( selector, parseOnly ) {
  function toSelector (line 2330) | function toSelector( tokens ) {
  function addCombinator (line 2340) | function addCombinator( matcher, combinator, base ) {
  function elementMatcher (line 2390) | function elementMatcher( matchers ) {
  function condense (line 2404) | function condense( unmatched, map, filter, context, xml ) {
  function setMatcher (line 2425) | function setMatcher( preFilter, selector, matcher, postFilter, postFinde...
  function matcherFromTokens (line 2518) | function matcherFromTokens( tokens ) {
  function matcherFromGroupMatchers (line 2573) | function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
  function multipleContexts (line 2701) | function multipleContexts( selector, contexts, results ) {
  function select (line 2710) | function select( selector, context, results, seed ) {
  function createOptions (line 2850) | function createOptions( options ) {
  function Data (line 3312) | function Data() {
  function dataAttr (line 3625) | function dataAttr( elem, key, data ) {
  function returnTrue (line 4305) | function returnTrue() {
  function returnFalse (line 4309) | function returnFalse() {
  function safeActiveElement (line 4313) | function safeActiveElement() {
  function sibling (line 5268) | function sibling( cur, dir ) {
  function winnow (line 5384) | function winnow( elements, qualifier, not ) {
  function manipulationTarget (line 5893) | function manipulationTarget( elem, content ) {
  function disableScript (line 5903) | function disableScript( elem ) {
  function restoreScript (line 5907) | function restoreScript( elem ) {
  function setGlobalEval (line 5920) | function setGlobalEval( elems, refElements ) {
  function cloneCopyEvent (line 5931) | function cloneCopyEvent( src, dest ) {
  function getAll (line 5966) | function getAll( context, tag ) {
  function fixInput (line 5977) | function fixInput( src, dest ) {
  function vendorPropName (line 6078) | function vendorPropName( style, name ) {
  function isHidden (line 6100) | function isHidden( elem, el ) {
  function getStyles (line 6109) | function getStyles( elem ) {
  function showHide (line 6113) | function showHide( elements, show ) {
  function setPositiveNumber (line 6384) | function setPositiveNumber( elem, value, subtract ) {
  function augmentWidthOrHeight (line 6392) | function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
  function getWidthOrHeight (line 6431) | function getWidthOrHeight( elem, name, extra ) {
  function css_defaultDisplay (line 6475) | function css_defaultDisplay( nodeName ) {
  function actualDisplay (line 6507) | function actualDisplay( name, doc ) {
  function buildParams (line 6693) | function buildParams( prefix, obj, traditional, add ) {
  function addToPrefiltersOrTransports (line 6809) | function addToPrefiltersOrTransports( structure ) {
  function inspectPrefiltersOrTransports (line 6841) | function inspectPrefiltersOrTransports( structure, options, originalOpti...
  function ajaxExtend (line 6868) | function ajaxExtend( target, src ) {
  function done (line 7314) | function done( status, nativeStatusText, responses, headers ) {
  function ajaxHandleResponses (line 7461) | function ajaxHandleResponses( s, jqXHR, responses ) {
  function ajaxConvert (line 7517) | function ajaxConvert( s, response, jqXHR, isSuccess ) {
  function createFxNow (line 7912) | function createFxNow() {
  function createTween (line 7919) | function createTween( value, prop, animation ) {
  function Animation (line 7933) | function Animation( elem, properties, options ) {
  function propFilter (line 8037) | function propFilter( props, specialEasing ) {
  function defaultPrefilter (line 8104) | function defaultPrefilter( elem, props, opts ) {
  function Tween (line 8227) | function Tween( elem, options, prop, end, easing ) {
  function genFx (line 8451) | function genFx( type, includeWidth ) {
  function getWindow (line 8749) | function getWindow( elem ) {

FILE: examples/django_example/django_example/static/js/js.cookie.js
  function a (line 2) | function a(){for(var a=0,b={};a<arguments.length;a++){var c=arguments[a]...
  function b (line 2) | function b(c){function d(b,e,f){var g;if(arguments.length>1){if(f=a({pat...

FILE: examples/django_example/django_example/tests/test_api.py
  class ApiTestCase (line 6) | class ApiTestCase(TestCase):
    method setUp (line 8) | def setUp(self):
    method test_invalid_text (line 12) | def test_invalid_text(self):
    method test_post (line 26) | def test_post(self):
    method test_post_unicode (line 44) | def test_post_unicode(self):
    method test_escaped_unicode_post (line 62) | def test_escaped_unicode_post(self):
    method test_post_tags (line 79) | def test_post_tags(self):
    method test_get (line 99) | def test_get(self):
    method test_patch (line 104) | def test_patch(self):
    method test_put (line 109) | def test_put(self):
    method test_delete (line 114) | def test_delete(self):

FILE: examples/django_example/django_example/tests/test_example.py
  class ViewTestCase (line 6) | class ViewTestCase(TestCase):
    method setUp (line 8) | def setUp(self):
    method test_get_main_page (line 12) | def test_get_main_page(self):
  class ApiTestCase (line 20) | class ApiTestCase(TestCase):
    method setUp (line 26) | def setUp(self):
    method test_post (line 30) | def test_post(self):
    method test_post_tags (line 48) | def test_post_tags(self):
  class ApiIntegrationTestCase (line 69) | class ApiIntegrationTestCase(TestCase):
    method setUp (line 75) | def setUp(self):
    method test_get (line 79) | def test_get(self):

FILE: examples/django_example/django_example/views.py
  class ChatterBotAppView (line 10) | class ChatterBotAppView(TemplateView):
  class ChatterBotApiView (line 14) | class ChatterBotApiView(View):
    method post (line 21) | def post(self, request, *args, **kwargs):
    method get (line 42) | def get(self, request, *args, **kwargs):

FILE: examples/django_example/manage.py
  function main (line 7) | def main():

FILE: examples/learning_feedback_example.py
  function get_feedback (line 21) | def get_feedback():

FILE: examples/tkinter_gui.py
  class TkinterGUIExample (line 12) | class TkinterGUIExample(tk.Tk):
    method __init__ (line 14) | def __init__(self, *args, **kwargs):
    method initialize (line 33) | def initialize(self):
    method get_response (line 51) | def get_response(self):

FILE: tests/base_case.py
  class ChatBotTestCase (line 6) | class ChatBotTestCase(TestCase):
    method setUpClass (line 16) | def setUpClass(cls):
    method setUp (line 22) | def setUp(self):
    method _add_search_text (line 27) | def _add_search_text(self, **kwargs):
    method _create_with_search_text (line 44) | def _create_with_search_text(self, text, in_response_to=None, **kwargs):
    method _create_many_with_search_text (line 63) | def _create_many_with_search_text(self, statements):
    method tearDown (line 83) | def tearDown(self):
    method assertIsLength (line 90) | def assertIsLength(self, item, length):
    method get_kwargs (line 98) | def get_kwargs(self):
  class ChatBotMongoTestCase (line 107) | class ChatBotMongoTestCase(ChatBotTestCase):
    method setUpClass (line 110) | def setUpClass(cls):
    method get_kwargs (line 129) | def get_kwargs(self):
  class ChatBotSQLTestCase (line 136) | class ChatBotSQLTestCase(ChatBotTestCase):
    method get_kwargs (line 138) | def get_kwargs(self):

FILE: tests/django_integration/__init__.py
  function load_tests (line 34) | def load_tests(loader, tests, pattern):

FILE: tests/django_integration/base_case.py
  class ChatterBotTestCase (line 6) | class ChatterBotTestCase(TransactionTestCase):
    method setUp (line 11) | def setUp(self):
    method _create_with_search_text (line 18) | def _create_with_search_text(self, text, in_response_to=None, **kwargs):
    method _create_many_with_search_text (line 37) | def _create_many_with_search_text(self, statements):

FILE: tests/django_integration/test_chatbot.py
  class ChatBotTests (line 5) | class ChatBotTests(ChatterBotTestCase):
    method test_get_response_text (line 7) | def test_get_response_text(self):
    method test_no_statements_known (line 10) | def test_no_statements_known(self):
    method test_one_statement_known_no_response (line 27) | def test_one_statement_known_no_response(self):
    method test_one_statement_one_response_known (line 39) | def test_one_statement_one_response_known(self):
    method test_two_statements_one_response_known (line 51) | def test_two_statements_one_response_known(self):
    method test_three_statements_two_responses_known (line 64) | def test_three_statements_two_responses_known(self):
    method test_four_statements_three_responses_known (line 76) | def test_four_statements_three_responses_known(self):
    method test_second_response_unknown (line 90) | def test_second_response_unknown(self):
    method test_statement_added_to_conversation (line 116) | def test_statement_added_to_conversation(self):
    method test_get_response_additional_response_selection_parameters (line 126) | def test_get_response_additional_response_selection_parameters(self):
    method test_get_response_unicode (line 142) | def test_get_response_unicode(self):
    method test_get_response_emoji (line 149) | def test_get_response_emoji(self):
    method test_get_response_non_whitespace (line 156) | def test_get_response_non_whitespace(self):
    method test_get_response_two_byte_characters (line 163) | def test_get_response_two_byte_characters(self):
    method test_get_response_corrupted_text (line 170) | def test_get_response_corrupted_text(self):
    method test_response_with_tags_added (line 177) | def test_response_with_tags_added(self):
    method test_get_response_with_text_and_kwargs (line 194) | def test_get_response_with_text_and_kwargs(self):
    method test_get_response_missing_text (line 203) | def test_get_response_missing_text(self):
    method test_get_response_missing_text_with_conversation (line 207) | def test_get_response_missing_text_with_conversation(self):
    method test_generate_response (line 211) | def test_generate_response(self):
    method test_learn_response (line 218) | def test_learn_response(self):
    method test_get_response_does_not_add_new_statement (line 226) | def test_get_response_does_not_add_new_statement(self):
    method test_get_latest_response_from_zero_responses (line 236) | def test_get_latest_response_from_zero_responses(self):
    method test_get_latest_response_from_one_responses (line 241) | def test_get_latest_response_from_one_responses(self):
    method test_get_latest_response_from_two_responses (line 249) | def test_get_latest_response_from_two_responses(self):
    method test_get_latest_response_from_three_responses (line 258) | def test_get_latest_response_from_three_responses(self):
    method test_search_text_results_after_training (line 268) | def test_search_text_results_after_training(self):
    method test_search_text_contains_results_after_training (line 289) | def test_search_text_contains_results_after_training(self):

FILE: tests/django_integration/test_chatterbot_corpus_training.py
  class ChatterBotCorpusTrainingTestCase (line 5) | class ChatterBotCorpusTrainingTestCase(ChatterBotTestCase):
    method setUp (line 12) | def setUp(self):
    method tearDown (line 20) | def tearDown(self):
    method test_train_with_english_greeting_corpus (line 24) | def test_train_with_english_greeting_corpus(self):
    method test_train_with_english_greeting_corpus_tags (line 31) | def test_train_with_english_greeting_corpus_tags(self):
    method test_train_with_multiple_corpora (line 40) | def test_train_with_multiple_corpora(self):
    method test_train_with_english_corpus (line 49) | def test_train_with_english_corpus(self):

FILE: tests/django_integration/test_chatterbot_settings.py
  class SettingsTestCase (line 5) | class SettingsTestCase(TestCase):
    method test_modified_settings (line 7) | def test_modified_settings(self):
    method test_name_setting (line 12) | def test_name_setting(self):

FILE: tests/django_integration/test_custom_models.py
  class CustomModelsTestCase (line 9) | class CustomModelsTestCase(ChatterBotTestCase):
    method test_default_models_used_without_settings (line 14) | def test_default_models_used_without_settings(self):
    method test_custom_models_via_kwargs (line 33) | def test_custom_models_via_kwargs(self):
    method test_custom_models_via_settings (line 51) | def test_custom_models_via_settings(self):
    method test_kwargs_override_settings (line 64) | def test_kwargs_override_settings(self):
    method test_get_statement_model (line 83) | def test_get_statement_model(self):
    method test_get_tag_model (line 98) | def test_get_tag_model(self):
    method test_model_configuration_persists (line 113) | def test_model_configuration_persists(self):

FILE: tests/django_integration/test_django_adapter.py
  class DjangoAdapterTestCase (line 7) | class DjangoAdapterTestCase(TestCase):
    method setUp (line 9) | def setUp(self):
    method tearDown (line 15) | def tearDown(self):
  class DjangoStorageAdapterTests (line 22) | class DjangoStorageAdapterTests(DjangoAdapterTestCase):
    method test_count_returns_zero (line 24) | def test_count_returns_zero(self):
    method test_count_returns_value (line 31) | def test_count_returns_value(self):
    method test_filter_statement_not_found (line 39) | def test_filter_statement_not_found(self):
    method test_filter_statement_found (line 47) | def test_filter_statement_found(self):
    method test_update_adds_new_statement (line 59) | def test_update_adds_new_statement(self):
    method test_update_modifies_existing_statement (line 68) | def test_update_modifies_existing_statement(self):
    method test_get_random_returns_statement (line 87) | def test_get_random_returns_statement(self):
    method test_get_random_no_data (line 93) | def test_get_random_no_data(self):
    method test_filter_by_text_multiple_results (line 99) | def test_filter_by_text_multiple_results(self):
    method test_remove (line 113) | def test_remove(self):
    method test_remove_response (line 122) | def test_remove_response(self):
  class DjangoAdapterFilterTests (line 131) | class DjangoAdapterFilterTests(DjangoAdapterTestCase):
    method test_filter_text_no_matches (line 133) | def test_filter_text_no_matches(self):
    method test_filter_in_response_to_no_matches (line 142) | def test_filter_in_response_to_no_matches(self):
    method test_filter_equal_results (line 152) | def test_filter_equal_results(self):
    method test_filter_contains_result (line 166) | def test_filter_contains_result(self):
    method test_filter_contains_no_result (line 182) | def test_filter_contains_no_result(self):
    method test_filter_no_parameters (line 193) | def test_filter_no_parameters(self):
    method test_filter_by_tag (line 205) | def test_filter_by_tag(self):
    method test_filter_by_tags (line 218) | def test_filter_by_tags(self):
    method test_filter_page_size (line 233) | def test_filter_page_size(self):
    method test_confidence (line 247) | def test_confidence(self):
    method test_exclude_text (line 262) | def test_exclude_text(self):
    method test_exclude_text_words (line 275) | def test_exclude_text_words(self):
    method test_persona_not_startswith (line 289) | def test_persona_not_startswith(self):
    method test_search_text_contains (line 300) | def test_search_text_contains(self):
    method test_search_text_contains_multiple_matches (line 311) | def test_search_text_contains_multiple_matches(self):
  class DjangoOrderingTests (line 322) | class DjangoOrderingTests(DjangoAdapterTestCase):
    method test_order_by_text (line 327) | def test_order_by_text(self):
    method test_reverse_order_by_text (line 337) | def test_reverse_order_by_text(self):
  class StorageAdapterCreateTests (line 348) | class StorageAdapterCreateTests(DjangoAdapterTestCase):
    method test_create_text (line 353) | def test_create_text(self):
    method test_create_search_text (line 361) | def test_create_search_text(self):
    method test_create_search_in_response_to (line 372) | def test_create_search_in_response_to(self):
    method test_create_tags (line 383) | def test_create_tags(self):
    method test_create_duplicate_tags (line 392) | def test_create_duplicate_tags(self):
    method test_create_many_text (line 405) | def test_create_many_text(self):
    method test_create_many_search_text (line 417) | def test_create_many_search_text(self):
    method test_create_many_search_in_response_to (line 429) | def test_create_many_search_in_response_to(self):
    method test_create_many_tags (line 441) | def test_create_many_tags(self):
    method test_create_many_duplicate_tags (line 454) | def test_create_many_duplicate_tags(self):
  class StorageAdapterUpdateTests (line 470) | class StorageAdapterUpdateTests(DjangoAdapterTestCase):
    method test_update_adds_tags (line 475) | def test_update_adds_tags(self):
    method test_update_duplicate_tags (line 486) | def test_update_duplicate_tags(self):

FILE: tests/django_integration/test_logic_adapter_integration.py
  class LogicIntegrationTestCase (line 5) | class LogicIntegrationTestCase(ChatterBotTestCase):
    method setUp (line 11) | def setUp(self):
    method test_best_match (line 16) | def test_best_match(self):
    method test_mathematical_evaluation (line 37) | def test_mathematical_evaluation(self):
    method test_time (line 49) | def test_time(self):

FILE: tests/django_integration/test_secondary_database.py
  class SecondaryDatabaseTestCase (line 8) | class SecondaryDatabaseTestCase(ChatterBotTestCase):
    method setUp (line 13) | def setUp(self):
    method test_database_parameter_accepted (line 26) | def test_database_parameter_accepted(self):
    method test_database_parameter_default (line 32) | def test_database_parameter_default(self):
    method test_operations_with_database_parameter (line 42) | def test_operations_with_database_parameter(self):

FILE: tests/django_integration/test_statement_integration.py
  class StatementIntegrationTestCase (line 7) | class StatementIntegrationTestCase(TestCase):
    method setUp (line 13) | def setUp(self):
    method test_text (line 25) | def test_text(self):
    method test_in_response_to (line 29) | def test_in_response_to(self):
    method test_conversation (line 33) | def test_conversation(self):
    method test_tags (line 37) | def test_tags(self):
    method test__str__ (line 41) | def test__str__(self):
    method test_add_tags (line 47) | def test_add_tags(self):
    method test_serialize (line 54) | def test_serialize(self):

FILE: tests/logic/test_best_match.py
  class BestMatchTestCase (line 6) | class BestMatchTestCase(ChatBotTestCase):
    method setUp (line 11) | def setUp(self):
    method test_no_data (line 15) | def test_no_data(self):
    method test_no_choices (line 25) | def test_no_choices(self):
    method test_no_known_responses (line 38) | def test_no_known_responses(self):
    method test_match_with_no_response (line 57) | def test_match_with_no_response(self):
    method test_match_with_response (line 72) | def test_match_with_response(self):
    method test_excluded_words (line 90) | def test_excluded_words(self):
    method test_low_confidence (line 115) | def test_low_confidence(self):
    method test_low_confidence_options_list (line 125) | def test_low_confidence_options_list(self):
    method test_text_search_algorithm (line 139) | def test_text_search_algorithm(self):

FILE: tests/logic/test_data_cache.py
  class DummyMutatorLogicAdapter (line 6) | class DummyMutatorLogicAdapter(LogicAdapter):
    method process (line 12) | def process(self, statement, additional_response_selection_parameters=...
  class DataCachingTests (line 18) | class DataCachingTests(ChatBotTestCase):
    method setUp (line 20) | def setUp(self):
    method test_additional_attributes_saved (line 37) | def test_additional_attributes_saved(self):

FILE: tests/logic/test_logic_adapter.py
  class LogicAdapterTestCase (line 6) | class LogicAdapterTestCase(ChatBotTestCase):
    method setUp (line 14) | def setUp(self):
    method test_class_name (line 18) | def test_class_name(self):
    method test_can_process (line 24) | def test_can_process(self):
    method test_process (line 30) | def test_process(self):
    method test_get_default_response (line 34) | def test_get_default_response(self):
    method test_get_default_response_from_options (line 39) | def test_get_default_response_from_options(self):
    method test_get_default_response_from_database (line 47) | def test_get_default_response_from_database(self):

FILE: tests/logic/test_mathematical_evaluation.py
  class MathematicalEvaluationTests (line 6) | class MathematicalEvaluationTests(ChatBotTestCase):
    method setUp (line 8) | def setUp(self):
    method test_can_process (line 12) | def test_can_process(self):
    method test_can_not_process (line 16) | def test_can_not_process(self):
    method test_addition_operator (line 20) | def test_addition_operator(self):
    method test_subtraction_operator (line 26) | def test_subtraction_operator(self):
    method test_multiplication_operator (line 32) | def test_multiplication_operator(self):
    method test_division_operator (line 38) | def test_division_operator(self):
    method test_exponent_operator (line 45) | def test_exponent_operator(self):
    method test_parenthesized_multiplication_and_addition (line 51) | def test_parenthesized_multiplication_and_addition(self):
    method test_parenthesized_with_words (line 57) | def test_parenthesized_with_words(self):
    method test_word_numbers_addition (line 63) | def test_word_numbers_addition(self):
    method test_word_division_operator (line 69) | def test_word_division_operator(self):
    method test_large_word_division_operator (line 76) | def test_large_word_division_operator(self):
    method test_negative_multiplication (line 84) | def test_negative_multiplication(self):
    method test_negative_decimal_multiplication (line 90) | def test_negative_decimal_multiplication(self):
    method test_pi_constant (line 96) | def test_pi_constant(self):
    method test_e_constant (line 102) | def test_e_constant(self):
    method test_log_function (line 108) | def test_log_function(self):
    method test_square_root_function (line 114) | def test_square_root_function(self):

FILE: tests/logic/test_specific_response.py
  class SpecificResponseAdapterTestCase (line 7) | class SpecificResponseAdapterTestCase(ChatBotTestCase):
    method setUp (line 12) | def setUp(self):
    method test_initialization_with_missing_input_text (line 20) | def test_initialization_with_missing_input_text(self):
    method test_initialization_with_missing_output_text (line 30) | def test_initialization_with_missing_output_text(self):
    method test_exact_match (line 40) | def test_exact_match(self):
    method test_not_exact_match (line 50) | def test_not_exact_match(self):
  class SpecificResponseAdapterSpacyTestCase (line 61) | class SpecificResponseAdapterSpacyTestCase(ChatBotTestCase):
    method setUp (line 66) | def setUp(self):
    method test_pattern_match (line 86) | def test_pattern_match(self):
  class SpecificResponseAdapterFunctionResponseTestCase (line 97) | class SpecificResponseAdapterFunctionResponseTestCase(ChatBotTestCase):
    method setUp (line 102) | def setUp(self):
    method test_function_response (line 114) | def test_function_response(self):

FILE: tests/logic/test_time.py
  class TimeAdapterTests (line 6) | class TimeAdapterTests(ChatBotTestCase):
    method setUp (line 8) | def setUp(self):
    method test_positive_input (line 12) | def test_positive_input(self):
    method test_negative_input (line 19) | def test_negative_input(self):

FILE: tests/logic/test_unit_conversion.py
  class UnitConversionTests (line 6) | class UnitConversionTests(ChatBotTestCase):
    method setUp (line 7) | def setUp(self):
    method test_can_process (line 11) | def test_can_process(self):
    method test_can_process_pattern_x_unit_to_y_unit (line 15) | def test_can_process_pattern_x_unit_to_y_unit(self):
    method test_can_process_x_unit_is_how_many_y_unit (line 19) | def test_can_process_x_unit_is_how_many_y_unit(self):
    method test_can_not_process (line 23) | def test_can_not_process(self):
    method test_can_not_convert_inches_to_kilometer (line 27) | def test_can_not_convert_inches_to_kilometer(self):
    method test_inches_to_kilometers (line 31) | def test_inches_to_kilometers(self):
    method test_inches_to_kilometers_variation_1 (line 40) | def test_inches_to_kilometers_variation_1(self):
    method test_inches_to_kilometers_variation_2 (line 49) | def test_inches_to_kilometers_variation_2(self):
    method test_inches_to_kilometers_variation_3 (line 58) | def test_inches_to_kilometers_variation_3(self):
    method test_meter_to_kilometer (line 67) | def test_meter_to_kilometer(self):
    method test_meter_to_kilometer_variation (line 76) | def test_meter_to_kilometer_variation(self):
    method test_temperature_celsius_to_fahrenheit (line 85) | def test_temperature_celsius_to_fahrenheit(self):
    method test_negative_temperature_celsius_to_fahrenheit (line 94) | def test_negative_temperature_celsius_to_fahrenheit(self):
    method test_time_two_hours_to_seconds (line 103) | def test_time_two_hours_to_seconds(self):
    method test_pattern_x_unit_to_y_unit (line 112) | def test_pattern_x_unit_to_y_unit(self):
    method test_pattern_x_unit_is_how_many_y_unit (line 121) | def test_pattern_x_unit_is_how_many_y_unit(self):

FILE: tests/storage/test_mongo_adapter.py
  class MongoAdapterTestCase (line 6) | class MongoAdapterTestCase(TestCase):
    method setUpClass (line 9) | def setUpClass(cls):
    method tearDownClass (line 39) | def tearDownClass(cls):
    method setUp (line 46) | def setUp(self):
    method tearDown (line 53) | def tearDown(self):
  class MongoDatabaseAdapterTestCase (line 60) | class MongoDatabaseAdapterTestCase(MongoAdapterTestCase):
    method test_count_returns_zero (line 62) | def test_count_returns_zero(self):
    method test_count_returns_value (line 69) | def test_count_returns_value(self):
    method test_mongodb_client_kwargs_parameter (line 77) | def test_mongodb_client_kwargs_parameter(self):
    method test_filter_text_statement_not_found (line 98) | def test_filter_text_statement_not_found(self):
    method test_filter_text_statement_found (line 106) | def test_filter_text_statement_found(self):
    method test_update_adds_new_statement (line 117) | def test_update_adds_new_statement(self):
    method test_update_modifies_existing_statement (line 125) | def test_update_modifies_existing_statement(self):
    method test_get_random_returns_statement (line 146) | def test_get_random_returns_statement(self):
    method test_get_random_no_data (line 153) | def test_get_random_no_data(self):
    method test_mongo_to_object (line 159) | def test_mongo_to_object(self):
    method test_remove (line 170) | def test_remove(self):
    method test_remove_response (line 178) | def test_remove_response(self):
  class MongoAdapterFilterTestCase (line 187) | class MongoAdapterFilterTestCase(MongoAdapterTestCase):
    method test_filter_text_no_matches (line 189) | def test_filter_text_no_matches(self):
    method test_filter_in_response_to_no_matches (line 198) | def test_filter_in_response_to_no_matches(self):
    method test_filter_equal_results (line 207) | def test_filter_equal_results(self):
    method test_filter_no_parameters (line 229) | def test_filter_no_parameters(self):
    method test_filter_in_response_to (line 241) | def test_filter_in_response_to(self):
    method test_filter_by_tag (line 255) | def test_filter_by_tag(self):
    method test_filter_by_tags (line 268) | def test_filter_by_tags(self):
    method test_filter_page_size (line 283) | def test_filter_page_size(self):
    method test_exclude_text (line 297) | def test_exclude_text(self):
    method test_exclude_text_words (line 310) | def test_exclude_text_words(self):
    method test_persona_not_startswith (line 324) | def test_persona_not_startswith(self):
    method test_search_text_contains (line 335) | def test_search_text_contains(self):
    method test_search_text_contains_multiple_matches (line 346) | def test_search_text_contains_multiple_matches(self):
  class MongoOrderingTestCase (line 357) | class MongoOrderingTestCase(MongoAdapterTestCase):
    method test_order_by_text (line 362) | def test_order_by_text(self):
    method test_order_by_created_at (line 375) | def test_order_by_created_at(self):
  class StorageAdapterCreateTestCase (line 400) | class StorageAdapterCreateTestCase(MongoAdapterTestCase):
    method test_create_text (line 405) | def test_create_text(self):
    method test_create_search_text (line 413) | def test_create_search_text(self):
    method test_create_search_in_response_to (line 424) | def test_create_search_in_response_to(self):
    method test_create_tags (line 435) | def test_create_tags(self):
    method test_create_duplicate_tags (line 444) | def test_create_duplicate_tags(self):
    method test_create_many_text (line 457) | def test_create_many_text(self):
    method test_create_many_search_text (line 469) | def test_create_many_search_text(self):
    method test_create_many_search_in_response_to (line 481) | def test_create_many_search_in_response_to(self):
    method test_create_many_tags (line 494) | def test_create_many_tags(self):
    method test_create_many_duplicate_tags (line 507) | def test_create_many_duplicate_tags(self):
  class StorageAdapterUpdateTestCase (line 523) | class StorageAdapterUpdateTestCase(MongoAdapterTestCase):
    method test_update_adds_tags (line 528) | def test_update_adds_tags(self):
    method test_update_duplicate_tags (line 539) | def test_update_duplicate_tags(self):

FILE: tests/storage/test_redis_adapter.py
  class RedisStorageAdapterTestCase (line 12) | class RedisStorageAdapterTestCase(TestCase):
    method setUpClass (line 15) | def setUpClass(cls):
    method tearDown (line 28) | def tearDown(self):
  class RedisStorageAdapterTests (line 35) | class RedisStorageAdapterTests(RedisStorageAdapterTestCase):
    method test_count_returns_zero (line 37) | def test_count_returns_zero(self):
    method test_count_returns_value (line 44) | def test_count_returns_value(self):
    method test_filter_text_statement_not_found (line 52) | def test_filter_text_statement_not_found(self):
    method test_filter_text_statement_found (line 60) | def test_filter_text_statement_found(self):
    method test_update_adds_new_statement (line 73) | def test_update_adds_new_statement(self):
    method test_update_modifies_existing_statement (line 81) | def test_update_modifies_existing_statement(self):
    method test_get_random_returns_statement (line 102) | def test_get_random_returns_statement(self):
    method test_get_random_no_data (line 108) | def test_get_random_no_data(self):
    method test_remove (line 114) | def test_remove(self):
  class RedisStorageAdapterFilterTests (line 125) | class RedisStorageAdapterFilterTests(RedisStorageAdapterTestCase):
    method test_filter_text_no_matches (line 127) | def test_filter_text_no_matches(self):
    method test_filter_in_response_to_no_matches (line 137) | def test_filter_in_response_to_no_matches(self):
    method test_filter_equal_results (line 147) | def test_filter_equal_results(self):
    method test_filter_no_parameters (line 169) | def test_filter_no_parameters(self):
    method test_filter_by_tag (line 181) | def test_filter_by_tag(self):
    method test_filter_by_tags (line 194) | def test_filter_by_tags(self):
    method test_filter_page_size (line 209) | def test_filter_page_size(self):
    method test_exclude_text (line 224) | def test_exclude_text(self):
    method test_exclude_text_words (line 237) | def test_exclude_text_words(self):
    method test_persona_not_startswith (line 251) | def test_persona_not_startswith(self):
    method test_search_in_response_to_contains (line 262) | def test_search_in_response_to_contains(self):
    method test_search_in_response_to_contains_multiple_matches (line 275) | def test_search_in_response_to_contains_multiple_matches(self):
  class RedisOrderingTests (line 286) | class RedisOrderingTests(RedisStorageAdapterTestCase):
    method test_order_by_text (line 291) | def test_order_by_text(self):
    method test_order_by_created_at (line 304) | def test_order_by_created_at(self):
  class StorageAdapterCreateTests (line 329) | class StorageAdapterCreateTests(RedisStorageAdapterTestCase):
    method test_create_text (line 334) | def test_create_text(self):
    method test_create_tags (line 342) | def test_create_tags(self):
    method test_create_duplicate_tags (line 351) | def test_create_duplicate_tags(self):
    method test_create_many_text (line 364) | def test_create_many_text(self):
    method test_create_many_tags (line 377) | def test_create_many_tags(self):
    method test_create_many_duplicate_tags (line 398) | def test_create_many_duplicate_tags(self):
  class StorageAdapterUpdateTests (line 414) | class StorageAdapterUpdateTests(RedisStorageAdapterTestCase):
    method test_update_adds_tags (line 419) | def test_update_adds_tags(self):
    method test_update_duplicate_tags (line 430) | def test_update_duplicate_tags(self):

FILE: tests/storage/test_sql_adapter.py
  class SQLStorageAdapterTestCase (line 6) | class SQLStorageAdapterTestCase(TestCase):
    method setUpClass (line 9) | def setUpClass(cls):
    method tearDownClass (line 16) | def tearDownClass(cls):
    method tearDown (line 22) | def tearDown(self):
  class SQLStorageAdapterTests (line 29) | class SQLStorageAdapterTests(SQLStorageAdapterTestCase):
    method test_set_database_uri_none (line 31) | def test_set_database_uri_none(self):
    method test_set_database_uri (line 36) | def test_set_database_uri(self):
    method test_count_returns_zero (line 41) | def test_count_returns_zero(self):
    method test_count_returns_value (line 48) | def test_count_returns_value(self):
    method test_filter_text_statement_not_found (line 56) | def test_filter_text_statement_not_found(self):
    method test_filter_text_statement_found (line 64) | def test_filter_text_statement_found(self):
    method test_update_adds_new_statement (line 76) | def test_update_adds_new_statement(self):
    method test_update_modifies_existing_statement (line 84) | def test_update_modifies_existing_statement(self):
    method test_get_random_returns_statement (line 104) | def test_get_random_returns_statement(self):
    method test_get_random_no_data (line 110) | def test_get_random_no_data(self):
    method test_remove (line 116) | def test_remove(self):
  class SQLStorageAdapterFilterTests (line 125) | class SQLStorageAdapterFilterTests(SQLStorageAdapterTestCase):
    method test_filter_text_no_matches (line 127) | def test_filter_text_no_matches(self):
    method test_filter_in_response_to_no_matches (line 136) | def test_filter_in_response_to_no_matches(self):
    method test_filter_equal_results (line 146) | def test_filter_equal_results(self):
    method test_filter_no_parameters (line 168) | def test_filter_no_parameters(self):
    method test_filter_by_tag (line 180) | def test_filter_by_tag(self):
    method test_filter_by_tags (line 193) | def test_filter_by_tags(self):
    method test_filter_page_size (line 208) | def test_filter_page_size(self):
    method test_exclude_text (line 222) | def test_exclude_text(self):
    method test_exclude_text_words (line 235) | def test_exclude_text_words(self):
    method test_persona_not_startswith (line 249) | def test_persona_not_startswith(self):
    method test_search_text_contains (line 260) | def test_search_text_contains(self):
    method test_search_text_contains_multiple_matches (line 271) | def test_search_text_contains_multiple_matches(self):
  class SQLOrderingTests (line 282) | class SQLOrderingTests(SQLStorageAdapterTestCase):
    method test_order_by_text (line 287) | def test_order_by_text(self):
    method test_order_by_created_at (line 300) | def test_order_by_created_at(self):
  class StorageAdapterCreateTests (line 325) | class StorageAdapterCreateTests(SQLStorageAdapterTestCase):
    method test_create_text (line 330) | def test_create_text(self):
    method test_create_search_text (line 338) | def test_create_search_text(self):
    method test_create_search_in_response_to (line 349) | def test_create_search_in_response_to(self):
    method test_create_tags (line 360) | def test_create_tags(self):
    method test_create_duplicate_tags (line 369) | def test_create_duplicate_tags(self):
    method test_create_many_text (line 382) | def test_create_many_text(self):
    method test_create_many_search_text (line 394) | def test_create_many_search_text(self):
    method test_create_many_search_in_response_to (line 406) | def test_create_many_search_in_response_to(self):
    method test_create_many_tags (line 419) | def test_create_many_tags(self):
    method test_create_many_duplicate_tags (line 432) | def test_create_many_duplicate_tags(self):
  class StorageAdapterUpdateTests (line 448) | class StorageAdapterUpdateTests(SQLStorageAdapterTestCase):
    method test_update_adds_tags (line 453) | def test_update_adds_tags(self):
    method test_update_duplicate_tags (line 464) | def test_update_duplicate_tags(self):

FILE: tests/storage/test_storage_adapter.py
  class StorageAdapterTestCase (line 5) | class StorageAdapterTestCase(TestCase):
    method setUpClass (line 14) | def setUpClass(cls):
    method test_count (line 17) | def test_count(self):
    method test_filter (line 21) | def test_filter(self):
    method test_remove (line 25) | def test_remove(self):
    method test_create (line 29) | def test_create(self):
    method test_create_many (line 33) | def test_create_many(self):
    method test_update (line 37) | def test_update(self):
    method test_get_random (line 41) | def test_get_random(self):
    method test_drop (line 45) | def test_drop(self):
    method test_get_model_invalid (line 49) | def test_get_model_invalid(self):
    method test_get_object_invalid (line 53) | def test_get_object_invalid(self):

FILE: tests/test_adapter_validation.py
  class AdapterValidationTests (line 6) | class AdapterValidationTests(ChatBotTestCase):
    method test_invalid_storage_adapter (line 8) | def test_invalid_storage_adapter(self):
    method test_valid_storage_adapter (line 14) | def test_valid_storage_adapter(self):
    method test_invalid_logic_adapter (line 22) | def test_invalid_logic_adapter(self):
    method test_valid_logic_adapter (line 28) | def test_valid_logic_adapter(self):
    method test_valid_adapter_dictionary (line 36) | def test_valid_adapter_dictionary(self):
    method test_invalid_adapter_dictionary (line 46) | def test_invalid_adapter_dictionary(self):

FILE: tests/test_benchmarks.py
  function get_list_trainer (line 37) | def get_list_trainer(chatbot):
  function get_chatterbot_corpus_trainer (line 44) | def get_chatterbot_corpus_trainer(chatbot):
  function get_ubuntu_corpus_trainer (line 51) | def get_ubuntu_corpus_trainer(chatbot):
  class BenchmarkingMixin (line 58) | class BenchmarkingMixin(object):
    method assert_response_duration_is_less_than (line 60) | def assert_response_duration_is_less_than(self, maximum_duration, stri...
  class SqlBenchmarkingTests (line 86) | class SqlBenchmarkingTests(BenchmarkingMixin, ChatBotSQLTestCase):
    method get_kwargs (line 91) | def get_kwargs(self):
    method test_levenshtein_distance_comparisons (line 96) | def test_levenshtein_distance_comparisons(self):
    method test_spacy_similarity_comparisons (line 111) | def test_spacy_similarity_comparisons(self):
    method test_get_response_after_chatterbot_corpus_training (line 126) | def test_get_response_after_chatterbot_corpus_training(self):
    method test_get_response_after_ubuntu_corpus_training (line 136) | def test_get_response_after_ubuntu_corpus_training(self):
  class MongoBenchmarkingTests (line 146) | class MongoBenchmarkingTests(BenchmarkingMixin, ChatBotMongoTestCase):
    method get_kwargs (line 151) | def get_kwargs(self):
    method test_levenshtein_distance_comparisons (line 156) | def test_levenshtein_distance_comparisons(self):
    method test_spacy_similarity_comparisons (line 171) | def test_spacy_similarity_comparisons(self):
    method test_get_response_after_chatterbot_corpus_training (line 186) | def test_get_response_after_chatterbot_corpus_training(self):
    method test_get_response_after_ubuntu_corpus_training (line 196) | def test_get_response_after_ubuntu_corpus_training(self):

FILE: tests/test_chatbot.py
  class ChatBotInitializationTestCase (line 8) | class ChatBotInitializationTestCase(ChatBotTestCase):
    method test_initialization_with_unmapped_spacy_model (line 13) | def test_initialization_with_unmapped_spacy_model(self):
    method test_initialization_with_missing_spacy_model (line 29) | def test_initialization_with_missing_spacy_model(self):
  class ChatterBotResponseTestCase (line 49) | class ChatterBotResponseTestCase(ChatBotTestCase):
    method test_conversation_values_persisted_to_response (line 51) | def test_conversation_values_persisted_to_response(self):
    method test_tag_values_persisted_to_response (line 57) | def test_tag_values_persisted_to_response(self):
    method test_in_response_to_provided (line 68) | def test_in_response_to_provided(self):
    method test_no_statements_known (line 83) | def test_no_statements_known(self):
    method test_one_statement_known_no_response (line 99) | def test_one_statement_known_no_response(self):
    method test_one_statement_one_response_known (line 111) | def test_one_statement_one_response_known(self):
    method test_two_statements_one_response_known (line 123) | def test_two_statements_one_response_known(self):
    method test_three_statements_two_responses_known (line 136) | def test_three_statements_two_responses_known(self):
    method test_four_statements_three_responses_known (line 153) | def test_four_statements_three_responses_known(self):
    method test_second_response_unknown (line 167) | def test_second_response_unknown(self):
    method test_statement_added_to_conversation (line 193) | def test_statement_added_to_conversation(self):
    method test_get_response_additional_response_selection_parameters (line 203) | def test_get_response_additional_response_selection_parameters(self):
    method test_get_response_unicode (line 222) | def test_get_response_unicode(self):
    method test_get_response_emoji (line 229) | def test_get_response_emoji(self):
    method test_get_response_non_whitespace (line 236) | def test_get_response_non_whitespace(self):
    method test_get_response_two_byte_characters (line 243) | def test_get_response_two_byte_characters(self):
    method test_get_response_corrupted_text (line 250) | def test_get_response_corrupted_text(self):
    method test_response_with_tags_added (line 257) | def test_response_with_tags_added(self):
    method test_response_preserves_tags (line 273) | def test_response_preserves_tags(self):
    method test_get_response_with_text_and_kwargs (line 283) | def test_get_response_with_text_and_kwargs(self):
    method test_get_response_missing_text (line 291) | def test_get_response_missing_text(self):
    method test_get_response_missing_text_with_conversation (line 295) | def test_get_response_missing_text_with_conversation(self):
    method test_generate_response (line 299) | def test_generate_response(self):
    method test_learn_response (line 306) | def test_learn_response(self):
    method test_get_response_does_not_add_new_statement (line 314) | def test_get_response_does_not_add_new_statement(self):
    method test_get_latest_response_from_zero_responses (line 324) | def test_get_latest_response_from_zero_responses(self):
    method test_get_latest_response_from_one_responses (line 329) | def test_get_latest_response_from_one_responses(self):
    method test_get_latest_response_from_two_responses (line 337) | def test_get_latest_response_from_two_responses(self):
    method test_get_latest_response_from_three_responses (line 346) | def test_get_latest_response_from_three_responses(self):
    method test_search_text_results_after_training (line 356) | def test_search_text_results_after_training(self):
  class TestAdapterA (line 378) | class TestAdapterA(LogicAdapter):
    method process (line 380) | def process(self, statement, additional_response_selection_parameters=...
  class TestAdapterB (line 386) | class TestAdapterB(LogicAdapter):
    method process (line 388) | def process(self, statement, additional_response_selection_parameters=...
  class TestAdapterC (line 394) | class TestAdapterC(LogicAdapter):
    method process (line 396) | def process(self, statement, additional_response_selection_parameters=...
  class ChatBotLogicAdapterTestCase (line 402) | class ChatBotLogicAdapterTestCase(ChatBotTestCase):
    method test_sub_adapter_agreement (line 404) | def test_sub_adapter_agreement(self):
    method test_chatbot_set_for_all_logic_adapters (line 421) | def test_chatbot_set_for_all_logic_adapters(self):
    method test_response_persona_is_bot (line 429) | def test_response_persona_is_bot(self):

FILE: tests/test_cli.py
  class CommandLineInterfaceTests (line 5) | class CommandLineInterfaceTests(TestCase):
    method test_get_chatterbot_version (line 10) | def test_get_chatterbot_version(self):

FILE: tests/test_comparisons.py
  class LevenshteinDistanceTestCase (line 11) | class LevenshteinDistanceTestCase(TestCase):
    method setUp (line 13) | def setUp(self):
    method test_levenshtein_distance_statement_false (line 20) | def test_levenshtein_distance_statement_false(self):
    method test_levenshtein_distance_other_statement_false (line 31) | def test_levenshtein_distance_other_statement_false(self):
    method test_levenshtein_distance_statement_integer (line 42) | def test_levenshtein_distance_statement_integer(self):
    method test_exact_match_different_capitalization (line 54) | def test_exact_match_different_capitalization(self):
  class SpacySimilarityTests (line 66) | class SpacySimilarityTests(TestCase):
    method setUp (line 68) | def setUp(self):
    method test_exact_match_different_stopwords (line 76) | def test_exact_match_different_stopwords(self):
    method test_exact_match_different_capitalization (line 87) | def test_exact_match_different_capitalization(self):
  class JaccardSimilarityTestCase (line 99) | class JaccardSimilarityTestCase(TestCase):
    method setUp (line 101) | def setUp(self):
    method test_exact_match_different_capitalization (line 108) | def test_exact_match_different_capitalization(self):

FILE: tests/test_conversations.py
  class StatementTests (line 7) | class StatementTests(TestCase):
    method setUp (line 9) | def setUp(self):
    method test_serializer (line 12) | def test_serializer(self):
  class DefaultConversationTestCase (line 17) | class DefaultConversationTestCase(ChatBotTestCase):
    method test_default_conversation_is_set (line 23) | def test_default_conversation_is_set(self):
    method test_default_conversation_is_unique_per_instance (line 33) | def test_default_conversation_is_unique_per_instance(self):
    method test_response_gets_default_conversation (line 49) | def test_response_gets_default_conversation(self):
    method test_explicit_conversation_overrides_default (line 57) | def test_explicit_conversation_overrides_default(self):
    method test_statement_object_conversation_overrides_default (line 65) | def test_statement_object_conversation_overrides_default(self):
    method test_empty_conversation_gets_default (line 74) | def test_empty_conversation_gets_default(self):
    method test_statements_saved_with_default_conversation (line 86) | def test_statements_saved_with_default_conversation(self):
    method test_conversation_history_accumulates (line 98) | def test_conversation_history_accumulates(self):

FILE: tests/test_corpus.py
  class CorpusLoadingTestCase (line 7) | class CorpusLoadingTestCase(TestCase):
    method test_load_corpus_chinese (line 9) | def test_load_corpus_chinese(self):
    method test_load_corpus_english (line 15) | def test_load_corpus_english(self):
    method test_load_corpus_english_greetings (line 21) | def test_load_corpus_english_greetings(self):
    method test_load_corpus_english_categories (line 33) | def test_load_corpus_english_categories(self):
    method test_load_corpus_french (line 43) | def test_load_corpus_french(self):
    method test_load_corpus_german (line 49) | def test_load_corpus_german(self):
    method test_load_corpus_hindi (line 55) | def test_load_corpus_hindi(self):
    method test_load_corpus_indonesian (line 61) | def test_load_corpus_indonesian(self):
    method test_load_corpus_italian (line 67) | def test_load_corpus_italian(self):
    method test_load_corpus_marathi (line 73) | def test_load_corpus_marathi(self):
    method test_load_corpus_portuguese (line 79) | def test_load_corpus_portuguese(self):
    method test_load_corpus_russian (line 85) | def test_load_corpus_russian(self):
    method test_load_corpus_spanish (line 91) | def test_load_corpus_spanish(self):
    method test_load_corpus_telugu (line 97) | def test_load_corpus_telugu(self):
  class CorpusUtilsTestCase (line 104) | class CorpusUtilsTestCase(TestCase):
    method test_get_file_path (line 106) | def test_get_file_path(self):
    method test_read_english_corpus (line 116) | def test_read_english_corpus(self):
    method test_list_english_corpus_files (line 124) | def test_list_english_corpus_files(self):
    method test_load_corpus (line 130) | def test_load_corpus(self):
  class CorpusFilePathTestCase (line 140) | class CorpusFilePathTestCase(TestCase):
    method test_load_corpus_file (line 142) | def test_load_corpus_file(self):
    method test_load_corpus_file_non_existent (line 169) | def test_load_corpus_file_non_existent(self):
    method test_load_corpus_english_greetings (line 179) | def test_load_corpus_english_greetings(self):
    method test_load_corpus_english (line 186) | def test_load_corpus_english(self):
    method test_load_corpus_english_trailing_slash (line 193) | def test_load_corpus_english_trailing_slash(self):

FILE: tests/test_examples.py
  class ExamplesSmokeTestCase (line 4) | class ExamplesSmokeTestCase(TestCase):
    method test_basic_example (line 10) | def test_basic_example(self):
    method test_convert_units (line 13) | def test_convert_units(self):
    method test_default_response_example (line 16) | def test_default_response_example(self):
    method test_export_example (line 19) | def test_export_example(self):
    method test_learning_feedback_example (line 24) | def test_learning_feedback_example(self):
    method test_math_and_time (line 31) | def test_math_and_time(self):
    method test_memory_sql_example (line 34) | def test_memory_sql_example(self):
    method test_specific_response_example (line 37) | def test_specific_response_example(self):
    method test_tagged_dataset_example (line 40) | def test_tagged_dataset_example(self):
    method test_terminal_example (line 43) | def test_terminal_example(self):
    method test_terminal_mongo_example (line 50) | def test_terminal_mongo_example(self):
    method test_tkinter_gui (line 57) | def test_tkinter_gui(self):
    method test_training_example_chatterbot_corpus (line 63) | def test_training_example_chatterbot_corpus(self):
    method test_training_example_list_data (line 66) | def test_training_example_list_data(self):
    method test_training_example_ubuntu_corpus (line 69) | def test_training_example_ubuntu_corpus(self):

FILE: tests/test_filters.py
  class RepetitiveResponseFilterTestCase (line 5) | class RepetitiveResponseFilterTestCase(ChatBotMongoTestCase):
    method test_filter_selection (line 10) | def test_filter_selection(self):

FILE: tests/test_initialization.py
  class StringInitializationTestCase (line 4) | class StringInitializationTestCase(ChatBotTestCase):
    method get_kwargs (line 6) | def get_kwargs(self):
    method test_storage_initialized (line 12) | def test_storage_initialized(self):
    method test_logic_initialized (line 16) | def test_logic_initialized(self):
  class DictionaryInitializationTestCase (line 22) | class DictionaryInitializationTestCase(ChatBotTestCase):
    method get_kwargs (line 24) | def get_kwargs(self):
    method test_storage_initialized (line 40) | def test_storage_initialized(self):
    method test_logic_initialized (line 44) | def test_logic_initialized(self):

FILE: tests/test_languages.py
  class LanguageClassTests (line 6) | class LanguageClassTests(TestCase):
    method test_classes_have_correct_attributes (line 8) | def test_classes_have_correct_attributes(self):

FILE: tests/test_parsing.py
  class DateTimeParsingFunctionIntegrationTestCases (line 6) | class DateTimeParsingFunctionIntegrationTestCases(TestCase):
    method setUp (line 14) | def setUp(self):
    method test_captured_pattern_is_on_date (line 18) | def test_captured_pattern_is_on_date(self):
    method test_captured_pattern_this_weekday (line 25) | def test_captured_pattern_this_weekday(self):
    method test_captured_pattern_last_weekday (line 35) | def test_captured_pattern_last_weekday(self):
    method test_captured_pattern_next_weekday (line 45) | def test_captured_pattern_next_weekday(self):
    method test_captured_pattern_minutes_from_now (line 55) | def test_captured_pattern_minutes_from_now(self):
    method test_captured_pattern_days_later (line 67) | def test_captured_pattern_days_later(self):
    method test_captured_pattern_year (line 77) | def test_captured_pattern_year(self):
    method test_captured_pattern_today (line 84) | def test_captured_pattern_today(self):
    method test_captured_pattern_tomorrow (line 91) | def test_captured_pattern_tomorrow(self):
    method test_captured_pattern_yesterday (line 101) | def test_captured_pattern_yesterday(self):
    method test_captured_pattern_before_yesterday (line 111) | def test_captured_pattern_before_yesterday(self):
    method test_captured_pattern_before_today (line 121) | def test_captured_pattern_before_today(self):
    method test_captured_pattern_before_tomorrow (line 131) | def test_captured_pattern_before_tomorrow(self):
    method test_captured_pattern_two_days (line 150) | def test_captured_pattern_two_days(self):
    method test_captured_pattern_first_quarter_of_year (line 165) | def test_captured_pattern_first_quarter_of_year(self):
    method test_captured_pattern_last_quarter_of_year (line 173) | def test_captured_pattern_last_quarter_of_year(self):
    method test_captured_pattern_is_next_three_weeks (line 181) | def test_captured_pattern_is_next_three_weeks(self):
    method test_captured_pattern_is_next_x_weeks_case_insensitive (line 191) | def test_captured_pattern_is_next_x_weeks_case_insensitive(self):
    method test_captured_pattern_is_next_eight_days (line 201) | def test_captured_pattern_is_next_eight_days(self):
    method test_captured_pattern_is_next_x_days_case_insensitive (line 211) | def test_captured_pattern_is_next_x_days_case_insensitive(self):
    method test_captured_pattern_is_next_ten_years (line 221) | def test_captured_pattern_is_next_ten_years(self):
    method test_captured_pattern_is_next_x_years_case_insensitive (line 231) | def test_captured_pattern_is_next_x_years_case_insensitive(self):
    method test_captured_pattern_is_next_eleven_months (line 241) | def test_captured_pattern_is_next_eleven_months(self):
    method test_captured_pattern_is_next_x_months_case_insensitive (line 256) | def test_captured_pattern_is_next_x_months_case_insensitive(self):
    method test_captured_pattern_is_on_day (line 271) | def test_captured_pattern_is_on_day(self):
    method test_captured_pattern_is_on_day_of_year_variation1 (line 279) | def test_captured_pattern_is_on_day_of_year_variation1(self):
    method test_captured_pattern_is_on_day_of_year_variation2 (line 286) | def test_captured_pattern_is_on_day_of_year_variation2(self):
    method test_captured_pattern_has_am (line 293) | def test_captured_pattern_has_am(self):
    method test_captured_pattern_has_am_case_insensitive_1 (line 301) | def test_captured_pattern_has_am_case_insensitive_1(self):
    method test_captured_pattern_has_am_case_insensitive_2 (line 309) | def test_captured_pattern_has_am_case_insensitive_2(self):
    method test_captured_pattern_has_am_case_insensitive_3 (line 317) | def test_captured_pattern_has_am_case_insensitive_3(self):
    method test_captured_pattern_has_pm (line 325) | def test_captured_pattern_has_pm(self):
    method test_captured_pattern_has_pm_case_insensitive_1 (line 333) | def test_captured_pattern_has_pm_case_insensitive_1(self):
    method test_captured_pattern_has_pm_case_insensitive_2 (line 341) | def test_captured_pattern_has_pm_case_insensitive_2(self):
    method test_captured_pattern_has_pm_case_insensitive_3 (line 349) | def test_captured_pattern_has_pm_case_insensitive_3(self):
  class DateTimeParsingTestCases (line 358) | class DateTimeParsingTestCases(TestCase):
    method test_next_week_day (line 363) | def test_next_week_day(self):
    method test_previous_week_day (line 370) | def test_previous_week_day(self):
    method test_this_week_day_before_day (line 377) | def test_this_week_day_before_day(self):
    method test_this_week_day_after_day (line 384) | def test_this_week_day_after_day(self):
    method test_today_at_time_uses_base_date (line 391) | def test_today_at_time_uses_base_date(self):
    method test_next_month_from_january_31st (line 410) | def test_next_month_from_january_31st(self):
    method test_next_3_months_crosses_year_boundary (line 424) | def test_next_3_months_crosses_year_boundary(self):
    method test_next_month_from_march_31st (line 437) | def test_next_month_from_march_31st(self):
    method test_next_month_from_may_31st (line 451) | def test_next_month_from_may_31st(self):
    method test_multiple_datetime_expressions (line 465) | def test_multiple_datetime_expressions(self):
    method test_duration_from_yesterday (line 485) | def test_duration_from_yesterday(self):
    method test_duration_from_tomorrow (line 499) | def test_duration_from_tomorrow(self):
    method test_duration_from_today (line 513) | def test_duration_from_today(self):
    method test_noon_without_convention (line 527) | def test_noon_without_convention(self):
    method test_twelve_pm (line 540) | def test_twelve_pm(self):
    method test_twelve_am (line 552) | def test_twelve_am(self):
    method test_one_am (line 564) | def test_one_am(self):
    method test_one_pm (line 576) | def test_one_pm(self):

FILE: tests/test_preprocessors.py
  class PreprocessorIntegrationTestCase (line 6) | class PreprocessorIntegrationTestCase(ChatBotTestCase):
    method test_clean_whitespace (line 11) | def test_clean_whitespace(self):
  class CleanWhitespacePreprocessorTestCase (line 18) | class CleanWhitespacePreprocessorTestCase(ChatBotTestCase):
    method test_clean_whitespace (line 23) | def test_clean_whitespace(self):
    method test_leading_or_trailing_whitespace_removed (line 30) | def test_leading_or_trailing_whitespace_removed(self):
    method test_consecutive_spaces_removed (line 37) | def test_consecutive_spaces_removed(self):
  class HTMLUnescapePreprocessorTestCase (line 45) | class HTMLUnescapePreprocessorTestCase(ChatBotTestCase):
    method test_html_unescape (line 50) | def test_html_unescape(self):
  class ConvertToASCIIPreprocessorTestCase (line 70) | class ConvertToASCIIPreprocessorTestCase(ChatBotTestCase):
    method test_convert_to_ascii (line 75) | def test_convert_to_ascii(self):

FILE: tests/test_response_selection.py
  class ResponseSelectionTests (line 6) | class ResponseSelectionTests(ChatBotSQLTestCase):
    method test_get_most_frequent_response (line 8) | def test_get_most_frequent_response(self):
    method test_get_first_response (line 35) | def test_get_first_response(self):
    method test_get_random_response (line 46) | def test_get_random_response(self):

FILE: tests/test_search.py
  class SearchTestCase (line 7) | class SearchTestCase(ChatBotTestCase):
    method setUp (line 9) | def setUp(self):
    method test_search_no_results (line 13) | def test_search_no_results(self):
    method test_search_cast_to_list_no_results (line 22) | def test_search_cast_to_list_no_results(self):
    method test_search_additional_parameters (line 33) | def test_search_additional_parameters(self):
  class IndexedTextSearchComparisonFunctionSpacySimilarityTests (line 53) | class IndexedTextSearchComparisonFunctionSpacySimilarityTests(ChatBotTes...
    method setUp (line 59) | def setUp(self):
    method test_get_closest_statement (line 66) | def test_get_closest_statement(self):
    method test_different_punctuation (line 85) | def test_different_punctuation(self):
  class IndexedTextSearchComparisonFunctionLevenshteinDistanceComparisonTests (line 100) | class IndexedTextSearchComparisonFunctionLevenshteinDistanceComparisonTe...
    method setUp (line 106) | def setUp(self):
    method test_get_closest_statement (line 113) | def test_get_closest_statement(self):
    method test_confidence_exact_match (line 134) | def test_confidence_exact_match(self):
    method test_confidence_half_match (line 143) | def test_confidence_half_match(self):
    method test_confidence_no_match (line 157) | def test_confidence_no_match(self):
  class TextSearchComparisonFunctionSpacySimilarityTests (line 171) | class TextSearchComparisonFunctionSpacySimilarityTests(ChatBotTestCase):
    method setUp (line 177) | def setUp(self):
    method test_get_closest_statement (line 184) | def test_get_closest_statement(self):
    method test_different_punctuation (line 203) | def test_different_punctuation(self):
  class TextSearchComparisonFunctionLevenshteinDistanceComparisonTests (line 218) | class TextSearchComparisonFunctionLevenshteinDistanceComparisonTests(Cha...
    method setUp (line 224) | def setUp(self):
    method test_get_closest_statement (line 231) | def test_get_closest_statement(self):
    method test_confidence_exact_match (line 252) | def test_confidence_exact_match(self):
    method test_confidence_half_match (line 261) | def test_confidence_half_match(self):
    method test_confidence_no_match (line 275) | def test_confidence_no_match(self):

FILE: tests/test_tagging.py
  class PosLemmaTaggerTests (line 6) | class PosLemmaTaggerTests(TestCase):
    method setUp (line 8) | def setUp(self):
    method test_empty_string (line 11) | def test_empty_string(self):
    method test_tagging (line 18) | def test_tagging(self):
    method test_tagging_english (line 25) | def test_tagging_english(self):
    method test_tagging_german (line 36) | def test_tagging_german(self):
    method test_string_becomes_lowercase (line 47) | def test_string_becomes_lowercase(self):
    method test_tagging_medium_sized_words (line 52) | def test_tagging_medium_sized_words(self):
    method test_tagging_long_words (line 57) | def test_tagging_long_words(self):
    method test_get_text_index_string_punctuation_only (line 62) | def test_get_text_index_string_punctuation_only(self):
    method test_get_text_index_string_single_character (line 69) | def test_get_text_index_string_single_character(self):
    method test_get_text_index_string_single_character_punctuated (line 76) | def test_get_text_index_string_single_character_punctuated(self):
    method test_get_text_index_string_two_characters (line 83) | def test_get_text_index_string_two_characters(self):
    method test_get_text_index_string_three_characters (line 90) | def test_get_text_index_string_three_characters(self):
    method test_get_text_index_string_four_characters (line 97) | def test_get_text_index_string_four_characters(self):
    method test_get_text_index_string_five_characters (line 104) | def test_get_text_index_string_five_characters(self):
    method test_get_text_index_string_single_word (line 111) | def test_get_text_index_string_single_word(self):
    method test_get_text_index_string_multiple_words (line 118) | def test_get_text_index_string_multiple_words(self):
    method test_get_text_index_string_single_character_words (line 125) | def test_get_text_index_string_single_character_words(self):
    method test_get_text_index_string_two_character_words (line 132) | def test_get_text_index_string_two_character_words(self):
  class LowercaseTaggerTests (line 140) | class LowercaseTaggerTests(TestCase):
    method setUp (line 142) | def setUp(self):
    method test_lowercase_tagger (line 145) | def test_lowercase_tagger(self):

FILE: tests/test_turing.py
  class TuringTests (line 4) | class TuringTests(TestCase):
    method setUp (line 6) | def setUp(self):
    method test_ask_name (line 12) | def test_ask_name(self):
    method test_repeat_information (line 19) | def test_repeat_information(self):
    method test_repeat_input (line 26) | def test_repeat_input(self):
    method test_contradicting_responses (line 33) | def test_contradicting_responses(self):
    method test_mathematical_ability (line 40) | def test_mathematical_ability(self):
    method test_response_time (line 51) | def test_response_time(self):

FILE: tests/test_utils.py
  class UtilityTests (line 6) | class UtilityTests(TestCase):
    method test_import_module (line 8) | def test_import_module(self):
  class UtilityChatBotTestCase (line 13) | class UtilityChatBotTestCase(ChatBotTestCase):
    method test_get_response_time (line 15) | def test_get_response_time(self):

FILE: tests/training/test_chatterbot_corpus_training.py
  class ChatterBotCorpusTrainingTestCase (line 5) | class ChatterBotCorpusTrainingTestCase(ChatBotTestCase):
    method setUp (line 12) | def setUp(self):
    method test_train_with_english_greeting_corpus (line 19) | def test_train_with_english_greeting_corpus(self):
    method test_train_with_english_greeting_corpus_search_text (line 26) | def test_train_with_english_greeting_corpus_search_text(self):
    method test_train_with_english_greeting_corpus_search_in_response_to (line 34) | def test_train_with_english_greeting_corpus_search_in_response_to(self):
    method test_train_with_english_greeting_corpus_tags (line 42) | def test_train_with_english_greeting_corpus_tags(self):
    method test_train_with_multiple_corpora (line 51) | def test_train_with_multiple_corpora(self):
    method test_train_with_english_corpus (line 60) | def test_train_with_english_corpus(self):

FILE: tests/training/test_csv_file_training.py
  class CsvFileTrainerTestCase (line 6) | class CsvFileTrainerTestCase(ChatBotTestCase):
    method setUp (line 11) | def setUp(self):
    method test_train (line 32) | def test_train(self):
    method test_train_sets_search_text (line 41) | def test_train_sets_search_text(self):
    method test_train_sets_search_in_response_to (line 52) | def test_train_sets_search_in_response_to(self):

FILE: tests/training/test_json_file_training.py
  class JsonFileTrainerTestCase (line 6) | class JsonFileTrainerTestCase(ChatBotTestCase):
    method setUp (line 11) | def setUp(self):
    method test_train (line 32) | def test_train(self):
    method test_train_sets_search_text (line 41) | def test_train_sets_search_text(self):
    method test_train_sets_search_in_response_to (line 52) | def test_train_sets_search_in_response_to(self):

FILE: tests/training/test_list_training.py
  class ListTrainingTests (line 6) | class ListTrainingTests(ChatBotTestCase):
    method setUp (line 8) | def setUp(self):
    method test_training_cleans_whitespace (line 15) | def test_training_cleans_whitespace(self):
    method test_training_adds_statements (line 33) | def test_training_adds_statements(self):
    method test_training_sets_in_response_to (line 57) | def test_training_sets_in_response_to(self):
    method test_training_sets_search_text (line 73) | def test_training_sets_search_text(self):
    method test_training_sets_search_in_response_to (line 89) | def test_training_sets_search_in_response_to(self):
    method test_database_has_correct_format (line 105) | def test_database_has_correct_format(self):
    method test_training_with_unicode_characters (line 139) | def test_training_with_unicode_characters(self):
    method test_training_with_emoji_characters (line 160) | def test_training_with_emoji_characters(self):
    method test_training_with_unicode_bytestring (line 176) | def test_training_with_unicode_bytestring(self):
    method test_similar_sentence_gets_same_response_multiple_times (line 192) | def test_similar_sentence_gets_same_response_multiple_times(self):
    method test_consecutive_trainings_same_responses_different_inputs (line 224) | def test_consecutive_trainings_same_responses_different_inputs(self):
  class ChatterBotResponseTests (line 238) | class ChatterBotResponseTests(ChatBotTestCase):
    method setUp (line 240) | def setUp(self):
    method test_answer_to_known_input (line 273) | def test_answer_to_known_input(self):
    method test_answer_close_to_known_input (line 283) | def test_answer_close_to_known_input(self):
    method test_match_has_no_response (line 290) | def test_match_has_no_response(self):
    method test_empty_input (line 300) | def test_empty_input(self):

FILE: tests/training/test_training.py
  class TrainingTests (line 6) | class TrainingTests(ChatBotTestCase):
    method setUp (line 8) | def setUp(self):
    method test_trainer_not_set (line 13) | def test_trainer_not_set(self):
    method test_generate_export_data (line 17) | def test_generate_export_data(self):

FILE: tests/training/test_ubuntu_corpus_training.py
  class UbuntuCorpusTrainerTestCase (line 10) | class UbuntuCorpusTrainerTestCase(ChatBotTestCase):
    method setUp (line 15) | def setUp(self):
    method tearDown (line 26) | def tearDown(self):
    method _get_data (line 31) | def _get_data(self):
    method _remove_data (line 49) | def _remove_data(self):
    method _create_test_corpus (line 58) | def _create_test_corpus(self, data):
    method _destroy_test_corpus (line 84) | def _destroy_test_corpus(self):
    method _mock_get_response (line 93) | def _mock_get_response(self, *args, **kwargs):
    method test_download (line 102) | def test_download(self):
    method test_download_file_exists (line 119) | def test_download_file_exists(self):
    method test_download_url_not_found (line 136) | def test_download_url_not_found(self):
    method test_extract (line 142) | def test_extract(self):
    method test_train (line 156) | def test_train(self):
    method test_train_sets_search_text (line 168) | def test_train_sets_search_text(self):
    method test_train_sets_search_in_response_to (line 182) | def test_train_sets_search_in_response_to(self):
    method test_is_extracted (line 196) | def test_is_extracted(self):
    method test_is_not_extracted (line 208) | def test_is_not_extracted(self):
Condensed preview — 236 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,401K chars).
[
  {
    "path": ".codeclimate.yml",
    "chars": 151,
    "preview": "engines:\n  pep8:\n    enabled: true\nratings:\n  paths:\n  - \"**.py\"\nexclude_paths:\n- tests/*\n- examples/*\n- chatterbot/ext/"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 19,
    "preview": "github: gunthercox\n"
  },
  {
    "path": ".github/workflows/publish-documentation.yml",
    "chars": 459,
    "preview": "name: Deploy Sphinx documentation to Pages\n\non:\n  push:\n    branches: [master] # branch to trigger deployment\n\njobs:\n  p"
  },
  {
    "path": ".github/workflows/python-package.yml",
    "chars": 2417,
    "preview": "# This workflow will install Python dependencies, run tests and lint with a variety of Python versions\n# For more inform"
  },
  {
    "path": ".gitignore",
    "chars": 183,
    "preview": "bin\nbuild\nhtml\ndist\nvenv\n.env\n.out\n.coverage\n.python-version\n*.pyc\n*.swp\n*.egg-info\n*.egg/*\n*.eggs/*\n*.doctrees\n\n# Datab"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5225,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participa"
  },
  {
    "path": "LICENSE",
    "chars": 1476,
    "preview": "Copyright (c) 2016 - 2025, Gunther Cox\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or "
  },
  {
    "path": "README.md",
    "chars": 4478,
    "preview": "![ChatterBot: Machine learning in Python](https://i.imgur.com/b3SCmGT.png)\n\n# ChatterBot\n\nChatterBot is a machine-learni"
  },
  {
    "path": "SECURITY.md",
    "chars": 756,
    "preview": "# Security Policy\n\n## Supported Versions\n\nActively supported versions of ChatterBot can be determined using the followin"
  },
  {
    "path": "chatterbot/__init__.py",
    "chars": 159,
    "preview": "\"\"\"\nChatterBot is a machine learning, conversational dialog engine.\n\"\"\"\nfrom .chatterbot import ChatBot\n\n\n__version__ = "
  },
  {
    "path": "chatterbot/__main__.py",
    "chars": 637,
    "preview": "\"\"\"\nExample usage for ChatterBot command line arguments:\n\npython -m chatterbot --help\n\"\"\"\n\nimport sys\n\n\ndef get_chatterb"
  },
  {
    "path": "chatterbot/adapters.py",
    "chars": 878,
    "preview": "class Adapter(object):\n    \"\"\"\n    A superclass for all adapter classes.\n\n    :param chatbot: A ChatBot instance.\n    \"\""
  },
  {
    "path": "chatterbot/chatterbot.py",
    "chars": 15884,
    "preview": "import logging\nimport uuid\nfrom typing import Union\nfrom chatterbot.storage import StorageAdapter\nfrom chatterbot.logic "
  },
  {
    "path": "chatterbot/comparisons.py",
    "chars": 6208,
    "preview": "\"\"\"\nThis module contains various text-comparison algorithms\ndesigned to compare one statement to another.\n\"\"\"\nfrom chatt"
  },
  {
    "path": "chatterbot/components.py",
    "chars": 1981,
    "preview": "\"\"\"\nCustom components for Spacy processing pipelines.\nhttps://spacy.io/usage/processing-pipelines#custom-components\n\"\"\"\n"
  },
  {
    "path": "chatterbot/constants.py",
    "chars": 1909,
    "preview": "\"\"\"\nChatterBot constants\n\"\"\"\nfrom chatterbot import languages\n\n'''\nThe maximum length of characters that the text of a s"
  },
  {
    "path": "chatterbot/conversation.py",
    "chars": 3219,
    "preview": "from datetime import datetime, timezone\nfrom dateutil import parser as date_parser\n\n\nclass StatementMixin(object):\n    \""
  },
  {
    "path": "chatterbot/corpus.py",
    "chars": 2437,
    "preview": "import os\nimport io\nimport glob\nfrom pathlib import Path\nfrom chatterbot.exceptions import OptionalDependencyImportError"
  },
  {
    "path": "chatterbot/exceptions.py",
    "chars": 163,
    "preview": "class OptionalDependencyImportError(ImportError):\n    \"\"\"\n    An exception raised when a feature requires an optional de"
  },
  {
    "path": "chatterbot/ext/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "chatterbot/ext/django_chatterbot/__init__.py",
    "chars": 92,
    "preview": "default_app_config = (\n    'chatterbot.ext.django_chatterbot.apps.DjangoChatterBotConfig'\n)\n"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/abstract_models.py",
    "chars": 5192,
    "preview": "from chatterbot.conversation import StatementMixin\nfrom chatterbot import constants\nfrom django.db import models\nfrom dj"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/admin.py",
    "chars": 266,
    "preview": "from django.contrib import admin\nfrom chatterbot.ext.django_chatterbot.model_admin import StatementAdmin, TagAdmin\nfrom "
  },
  {
    "path": "chatterbot/ext/django_chatterbot/apps.py",
    "chars": 464,
    "preview": "from django.apps import AppConfig\n\n\nclass DjangoChatterBotConfig(AppConfig):\n\n    name = 'chatterbot.ext.django_chatterb"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0001_initial.py",
    "chars": 1266,
    "preview": "from django.db import migrations, models\nimport django.db.models.deletion\n\n\nclass Migration(migrations.Migration):\n\n    "
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0002_statement_extra_data.py",
    "chars": 441,
    "preview": "# Generated by Django 1.10.2 on 2016-10-30 12:13\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.M"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0003_change_occurrence_default.py",
    "chars": 408,
    "preview": "# Generated by Django 1.9 on 2016-12-12 00:06\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Migr"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0004_rename_in_response_to.py",
    "chars": 782,
    "preview": "# Generated by Django 1.10.3 on 2016-12-04 23:52\nfrom django.db import migrations, models\nimport django.db.models.deleti"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0005_statement_created_at.py",
    "chars": 570,
    "preview": "# Generated by Django 1.10.1 on 2016-12-29 19:20\nfrom django.db import migrations, models\nimport django.utils.timezone\n\n"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0006_create_conversation.py",
    "chars": 1047,
    "preview": "# Generated by Django 1.9 on 2017-01-17 07:02\nfrom django.db import migrations, models\nimport django.db.models.deletion\n"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0007_response_created_at.py",
    "chars": 564,
    "preview": "# Generated by Django 1.11 on 2017-07-18 00:16\nfrom django.db import migrations, models\nimport django.utils.timezone\n\n\nc"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0008_update_conversations.py",
    "chars": 837,
    "preview": "# Generated by Django 1.11 on 2017-07-18 11:25\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Mig"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0009_tags.py",
    "chars": 947,
    "preview": "# Generated by Django 1.11a1 on 2017-07-07 00:12\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.M"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0010_statement_text.py",
    "chars": 397,
    "preview": "# Generated by Django 1.11.4 on 2017-08-16 00:56\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.M"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0011_blank_extra_data.py",
    "chars": 412,
    "preview": "# Generated by Django 1.11.4 on 2017-08-20 13:55\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.M"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0012_statement_created_at.py",
    "chars": 515,
    "preview": "from django.db import migrations, models\nimport django.utils.timezone\n\n\nclass Migration(migrations.Migration):\n\n    depe"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0013_change_conversations.py",
    "chars": 1262,
    "preview": "# Generated by Django 1.11 on 2018-09-13 01:01\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Mig"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0014_remove_statement_extra_data.py",
    "chars": 298,
    "preview": "from django.db import migrations\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('django_chatter"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0015_statement_persona.py",
    "chars": 406,
    "preview": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('django"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0016_statement_stemmed_text.py",
    "chars": 547,
    "preview": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('django"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0017_tags_unique.py",
    "chars": 692,
    "preview": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('django"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0018_text_max_length.py",
    "chars": 876,
    "preview": "from django.db import migrations, models\n\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('django"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0019_alter_statement_id_alter_tag_id_and_more.py",
    "chars": 999,
    "preview": "# Generated by Django 4.2.19 on 2025-02-09 13:57\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations."
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0020_alter_statement_conversation_and_more.py",
    "chars": 2133,
    "preview": "# Generated by Django 4.1 on 2025-03-29 23:27\n\nfrom django.db import migrations, models\n\n\nclass Migration(migrations.Mig"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/0021_increase_text_max_length_to_1100.py",
    "chars": 1696,
    "preview": "\"\"\"\nDjango migration to increase text field max_length from 255 to 1100.\n\nThis migration alters all text-related fields "
  },
  {
    "path": "chatterbot/ext/django_chatterbot/migrations/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "chatterbot/ext/django_chatterbot/model_admin.py",
    "chars": 353,
    "preview": "from django.contrib import admin\n\n\nclass StatementAdmin(admin.ModelAdmin):\n    list_display = ('text', 'in_response_to',"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/models.py",
    "chars": 696,
    "preview": "from chatterbot.ext.django_chatterbot.abstract_models import AbstractBaseStatement, AbstractBaseTag\n\n\nclass Statement(Ab"
  },
  {
    "path": "chatterbot/ext/django_chatterbot/settings.py",
    "chars": 381,
    "preview": "\"\"\"\nDefault ChatterBot settings for Django.\n\"\"\"\nfrom django.conf import settings\nfrom chatterbot import constants\n\n\nCHAT"
  },
  {
    "path": "chatterbot/ext/sqlalchemy_app/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "chatterbot/ext/sqlalchemy_app/models.py",
    "chars": 2512,
    "preview": "from sqlalchemy import Table, Column, Integer, String, DateTime, ForeignKey\nfrom sqlalchemy.orm import relationship, dec"
  },
  {
    "path": "chatterbot/filters.py",
    "chars": 855,
    "preview": "def get_recent_repeated_responses(chatbot, conversation, sample=10, threshold=3, quantity=3) -> list:\n    \"\"\"\n    A filt"
  },
  {
    "path": "chatterbot/languages.py",
    "chars": 32782,
    "preview": "import sys\nimport inspect\n\n\nclass AAR:\n    ISO_639_1 = ''\n    ISO_639 = 'aar'\n    ENGLISH_NAME = 'Afar'\n\n\nclass ABK:\n   "
  },
  {
    "path": "chatterbot/logic/__init__.py",
    "chars": 722,
    "preview": "from chatterbot.logic.logic_adapter import LogicAdapter\nfrom chatterbot.logic.best_match import BestMatch\nfrom chatterbo"
  },
  {
    "path": "chatterbot/logic/best_match.py",
    "chars": 6961,
    "preview": "from chatterbot.logic import LogicAdapter\nfrom chatterbot.conversation import Statement\nfrom chatterbot import filters\n\n"
  },
  {
    "path": "chatterbot/logic/llm_adapters.py",
    "chars": 32652,
    "preview": "\"\"\"\nLLM Logic Adapters for ChatterBot.\n\nThis module provides logic adapters that integrate Large Language Models.\nLLM ad"
  },
  {
    "path": "chatterbot/logic/logic_adapter.py",
    "chars": 5067,
    "preview": "from random import choice\n\nfrom chatterbot.adapters import Adapter\nfrom chatterbot.storage import StorageAdapter\nfrom ch"
  },
  {
    "path": "chatterbot/logic/mathematical_evaluation.py",
    "chars": 4090,
    "preview": "from chatterbot.logic import LogicAdapter\nfrom chatterbot.conversation import Statement\nfrom chatterbot import languages"
  },
  {
    "path": "chatterbot/logic/mcp_tools.py",
    "chars": 6494,
    "preview": "\"\"\"\nMCP (Model Context Protocol) tool adapter for ChatterBot logic adapters.\n\nThis module provides a mixin class that al"
  },
  {
    "path": "chatterbot/logic/specific_response.py",
    "chars": 2629,
    "preview": "from chatterbot.logic import LogicAdapter\nfrom chatterbot.conversation import Statement\nfrom chatterbot import languages"
  },
  {
    "path": "chatterbot/logic/time_adapter.py",
    "chars": 3071,
    "preview": "from datetime import datetime\nfrom chatterbot import languages\nfrom chatterbot.logic import LogicAdapter\nfrom chatterbot"
  },
  {
    "path": "chatterbot/logic/unit_conversion.py",
    "chars": 7618,
    "preview": "from chatterbot.logic import LogicAdapter\nfrom chatterbot.conversation import Statement\nfrom chatterbot.exceptions impor"
  },
  {
    "path": "chatterbot/parsing.py",
    "chars": 23890,
    "preview": "import re\nfrom datetime import timedelta, datetime\nimport calendar\n\n# Variations of dates that the parser can capture\nye"
  },
  {
    "path": "chatterbot/preprocessors.py",
    "chars": 1386,
    "preview": "\"\"\"\nStatement pre-processors.\n\"\"\"\nfrom chatterbot.conversation import Statement\nfrom unicodedata import normalize\nfrom r"
  },
  {
    "path": "chatterbot/response_selection.py",
    "chars": 3482,
    "preview": "\"\"\"\nResponse selection methods determines which response should be used in\nthe event that multiple responses are generat"
  },
  {
    "path": "chatterbot/search.py",
    "chars": 8716,
    "preview": "class IndexedTextSearch:\n    \"\"\"\n    :param statement_comparison_function: A comparison class.\n        Defaults to ``Lev"
  },
  {
    "path": "chatterbot/storage/__init__.py",
    "chars": 465,
    "preview": "from chatterbot.storage.storage_adapter import StorageAdapter\nfrom chatterbot.storage.django_storage import DjangoStorag"
  },
  {
    "path": "chatterbot/storage/django_storage.py",
    "chars": 7545,
    "preview": "from chatterbot.storage import StorageAdapter\nfrom chatterbot import constants\n\n\nclass DjangoStorageAdapter(StorageAdapt"
  },
  {
    "path": "chatterbot/storage/mongodb.py",
    "chars": 8817,
    "preview": "import re\nfrom random import randint\nfrom chatterbot.storage import StorageAdapter\n\n\nclass MongoDatabaseAdapter(StorageA"
  },
  {
    "path": "chatterbot/storage/redis.py",
    "chars": 24400,
    "preview": "from datetime import datetime\nimport json\nimport re\nfrom chatterbot.storage import StorageAdapter\nfrom chatterbot.conver"
  },
  {
    "path": "chatterbot/storage/sql_storage.py",
    "chars": 16792,
    "preview": "import random\nfrom chatterbot.storage import StorageAdapter\n\n\nclass SQLStorageAdapter(StorageAdapter):\n    \"\"\"\n    The S"
  },
  {
    "path": "chatterbot/storage/storage_adapter.py",
    "chars": 9275,
    "preview": "import logging\n\n\nclass StorageAdapter(object):\n    \"\"\"\n    This is an abstract class that represents the interface\n    t"
  },
  {
    "path": "chatterbot/tagging.py",
    "chars": 5953,
    "preview": "from typing import List, Union, Tuple\nfrom chatterbot import languages\nfrom chatterbot.utils import get_model_for_langua"
  },
  {
    "path": "chatterbot/trainers.py",
    "chars": 25532,
    "preview": "import os\nimport csv\nimport time\nimport glob\nimport json\nimport tarfile\nfrom typing import List, Union\nfrom tqdm import "
  },
  {
    "path": "chatterbot/utils.py",
    "chars": 2972,
    "preview": "\"\"\"\nChatterBot utility functions\n\"\"\"\nfrom typing import Union\nimport importlib\nimport time\n\n\ndef import_module(dotted_pa"
  },
  {
    "path": "chatterbot/vectorstores.py",
    "chars": 1995,
    "preview": "\"\"\"\nRedis vector store.\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import List\n\nfrom langchain_core.documents i"
  },
  {
    "path": "docs/_ext/canonical.py",
    "chars": 757,
    "preview": "\"\"\"\nAdd GitHub repository details to the Sphinx context.\n\"\"\"\n\ndef setup_canonical_func(app, pagename, templatename, cont"
  },
  {
    "path": "docs/_ext/github.py",
    "chars": 623,
    "preview": "\"\"\"\nAdd GitHub repository details to the Sphinx context.\n\"\"\"\n\nGITHUB_USER = 'gunthercox'\nGITHUB_REPO = 'ChatterBot'\n\n\nde"
  },
  {
    "path": "docs/_includes/python_module_structure.txt",
    "chars": 150,
    "preview": "IronyAdapter/\n|── README\n|── pyproject.toml\n|── irony_adapter\n|   |── __init__.py\n|   └── logic.py\n└── tests\n    |── __i"
  },
  {
    "path": "docs/_static/mobile.js",
    "chars": 8819,
    "preview": "/**\n * Mobile Menu and Responsive Enhancements for ChatterBot Documentation\n * Provides mobile-friendly navigation and t"
  },
  {
    "path": "docs/_static/silktide-consent-manager.css",
    "chars": 11710,
    "preview": "/* \n  Silktide Consent Manager - https://silktide.com/consent-manager/  \n\n  Styles are at risked of being overridden by "
  },
  {
    "path": "docs/_static/silktide-consent-manager.js",
    "chars": 32740,
    "preview": "// Silktide Consent Manager - https://silktide.com/consent-manager/  \n\nclass SilktideCookieBanner {\n  constructor(config"
  },
  {
    "path": "docs/_static/style.css",
    "chars": 14043,
    "preview": "/* ===================================================================\n   MOBILE-FIRST RESPONSIVE DESIGN\n   Optimized fo"
  },
  {
    "path": "docs/_templates/footer.html",
    "chars": 2825,
    "preview": "<div class=\"help-footer\">\n    <table style=\"margin: 0px auto;\">\n        <tbody>\n            <tr>\n                <td sty"
  },
  {
    "path": "docs/_templates/layout.html",
    "chars": 4705,
    "preview": "{% extends '!layout.html' %}\n{# https://github.com/sphinx-doc/sphinx/blob/master/sphinx/themes/basic/layout.html #}\n\n{% "
  },
  {
    "path": "docs/_templates/page.html",
    "chars": 138,
    "preview": "{# Template for simple pages #}\n{%- extends \"layout.html\" %}\n\n{% block body %}\n  {{ body }}\n  {%- include \"footer.html\" "
  },
  {
    "path": "docs/_templates/sidebar_ad.html",
    "chars": 305,
    "preview": "<!-- ChatterBot Docs Ad -->\n<ins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-301979040570"
  },
  {
    "path": "docs/chatterbot.rst",
    "chars": 3741,
    "preview": "==========\nChatterBot\n==========\n\nThe main class ``ChatBot`` is a connecting point between each of\nChatterBot's :term:`a"
  },
  {
    "path": "docs/commands.rst",
    "chars": 575,
    "preview": "==================\nCommand line tools\n==================\n\nChatterBot comes with a few command line tools that can help\nw"
  },
  {
    "path": "docs/comparisons.rst",
    "chars": 2469,
    "preview": "===========\nComparisons\n===========\n\n.. _statement-comparison:\n\nStatement comparison\n====================\n\nChatterBot us"
  },
  {
    "path": "docs/conf.py",
    "chars": 6771,
    "preview": "import os\nimport sys\nfrom pathlib import Path\nfrom datetime import datetime\n\n\ncurrent_directory = os.path.dirname(os.pat"
  },
  {
    "path": "docs/contributing.rst",
    "chars": 3016,
    "preview": "==========================\nContributing to ChatterBot\n==========================\n\nThere are numerous ways to contribute "
  },
  {
    "path": "docs/conversations.rst",
    "chars": 2985,
    "preview": "=============\nConversations\n=============\n\nChatterBot supports the ability to have multiple concurrent conversations.\nA "
  },
  {
    "path": "docs/corpus.rst",
    "chars": 1863,
    "preview": "ChatterBot Corpus\n=================\n\nThis is a :term:`corpus` of dialog data that is included in the chatterbot module.\n"
  },
  {
    "path": "docs/development.rst",
    "chars": 1768,
    "preview": "===========\nDevelopment\n===========\n\nAs the code for ChatterBot is written, the developers attempt to describe\nthe logic"
  },
  {
    "path": "docs/django/custom-models.rst",
    "chars": 13999,
    "preview": "=======================\nCustom Statement Models\n=======================\n\nChatterBot allows you to replace the default St"
  },
  {
    "path": "docs/django/index.rst",
    "chars": 1939,
    "preview": "==================\nDjango Integration\n==================\n\nChatterBot has direct support for integration with Django's OR"
  },
  {
    "path": "docs/django/settings.rst",
    "chars": 4117,
    "preview": "==========================\nChatterBot Django Settings\n==========================\n\nYou can edit the ChatterBot configurat"
  },
  {
    "path": "docs/django/tutorial/django-filter-tutorial/index.rst",
    "chars": 2697,
    "preview": "======================\ndjango-filter tutorial\n======================\n\nIn this section, we will be adding filtering funct"
  },
  {
    "path": "docs/django/tutorial/django-rest-framework-tutorial/index.rst",
    "chars": 4411,
    "preview": "==============================\nDjango REST Framework Tutorial\n==============================\n\nThis is the second portion"
  },
  {
    "path": "docs/django/tutorial/django-tutorial/index.rst",
    "chars": 3219,
    "preview": "========================\nDjango Tutorial (Part 1)\n========================\n\nAs mentioned in the previous section, this i"
  },
  {
    "path": "docs/django/tutorial/django-tutorial/part-2.rst",
    "chars": 5763,
    "preview": "========================\nDjango Tutorial (Part 2)\n========================\n\nIn the :ref:`previous part <Django Tutorial "
  },
  {
    "path": "docs/django/tutorial/index.rst",
    "chars": 1188,
    "preview": "================\nDjango Tutorials\n================\n\nThese are mini tutorials designed to introduce basic parts of Django"
  },
  {
    "path": "docs/django/tutorial/writing-tests.rst",
    "chars": 5072,
    "preview": "=====================================\nWriting Tests for Django Applications\n=====================================\n\nWriti"
  },
  {
    "path": "docs/django/views.rst",
    "chars": 1241,
    "preview": "=============================\nChatterBot Django Sample Code\n=============================\n\n.. note::\n\n   Looking for the"
  },
  {
    "path": "docs/django/wsgi.rst",
    "chars": 1488,
    "preview": "Webservices\n===========\n\nHosting and serving web applications typically involves setting up a few additional components."
  },
  {
    "path": "docs/encoding.rst",
    "chars": 2905,
    "preview": "======================\nPython String Encoding\n======================\n\nThe Python developer community has published a gre"
  },
  {
    "path": "docs/examples.rst",
    "chars": 2942,
    "preview": "========\nExamples\n========\n\nThe following examples are available to help you get started with ChatterBot.\n\n.. note::\n   "
  },
  {
    "path": "docs/faq.rst",
    "chars": 2733,
    "preview": "==========================\nFrequently Asked Questions\n==========================\n\nThis document is comprised of question"
  },
  {
    "path": "docs/filters.rst",
    "chars": 447,
    "preview": "=======\nFilters\n=======\n\nFilters are an efficient way to create queries that can be passed to ChatterBot's storage adapt"
  },
  {
    "path": "docs/glossary.rst",
    "chars": 2439,
    "preview": "========\nGlossary\n========\n\n.. glossary::\n\n   adapters\n      A base class that allows a ChatBot instance to execute some"
  },
  {
    "path": "docs/index.rst",
    "chars": 4839,
    "preview": ".. meta::\n   :description: ChatterBot documentation: Python machine learning chatbot library with semantic vector search"
  },
  {
    "path": "docs/large-language-models.rst",
    "chars": 9377,
    "preview": "=====================\nLarge Language Models\n=====================\n\n.. warning::\n\n    Starting in ChatterBot 1.2.7 experi"
  },
  {
    "path": "docs/logic/create-a-logic-adapter.rst",
    "chars": 4698,
    "preview": "============================\nCreating a new logic adapter\n============================\n\nYou can write your own logic ada"
  },
  {
    "path": "docs/logic/index.rst",
    "chars": 3782,
    "preview": "==============\nLogic Adapters\n==============\n\nLogic adapters determine the logic for how ChatterBot selects a response t"
  },
  {
    "path": "docs/logic/response-selection.rst",
    "chars": 3834,
    "preview": "====================================\nHow logic adapters select a response\n====================================\n\nA typica"
  },
  {
    "path": "docs/packaging.rst",
    "chars": 2722,
    "preview": "==================================\nPackaging your code for ChatterBot\n==================================\n\nThere are case"
  },
  {
    "path": "docs/preprocessors.rst",
    "chars": 1069,
    "preview": "=============\nPreprocessors\n=============\n\nChatterBot's :term:`preprocessors` are simple functions that modify the input"
  },
  {
    "path": "docs/quickstart.rst",
    "chars": 1410,
    "preview": "=================\nQuick Start Guide\n=================\n\nThe first thing you'll need to do to get started is install Chatt"
  },
  {
    "path": "docs/releases.rst",
    "chars": 1148,
    "preview": "====================\nReleasing ChatterBot\n====================\n\nChatterBot follows the following rules when it comes to "
  },
  {
    "path": "docs/robots.txt",
    "chars": 71,
    "preview": "User-agent: *\nDisallow:\n\nSitemap: https:docs.chatterbot.us/sitemap.xml\n"
  },
  {
    "path": "docs/security.rst",
    "chars": 2015,
    "preview": "========\nSecurity\n========\n\n**Deploying chat bots to production environments requires due diligence,\nespecially in cases"
  },
  {
    "path": "docs/setup.rst",
    "chars": 2807,
    "preview": "============\nInstallation\n============\n\nThe recommended method for installing ChatterBot is by using `pip`_.\n\nInstalling"
  },
  {
    "path": "docs/statements.txt",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/storage/create-a-storage-adapter.rst",
    "chars": 425,
    "preview": "Creating a new storage adapter\n==============================\n\nYou can write your own storage adapters by creating a new"
  },
  {
    "path": "docs/storage/index.rst",
    "chars": 3327,
    "preview": "================\nStorage Adapters\n================\n\n.. meta::\n   :description: ChatterBot storage adapters: SQL, Redis v"
  },
  {
    "path": "docs/storage/mongodb.rst",
    "chars": 4360,
    "preview": "MongoDB Storage Adapter\n=======================\n\n.. image:: /_static/MongoDB_Fores-Green.svg\n   :alt: MongoDB Logo\n   :a"
  },
  {
    "path": "docs/storage/redis.rst",
    "chars": 13913,
    "preview": "Redis Vector Storage Adapter\n============================\n\n.. note::\n\n   **(December, 2025)**:\n   The ``RedisVectorStora"
  },
  {
    "path": "docs/storage/sql.rst",
    "chars": 106,
    "preview": "SQL Storage Adapter\n===================\n\n.. autoclass:: chatterbot.storage.SQLStorageAdapter\n   :members:\n"
  },
  {
    "path": "docs/storage/text-search.rst",
    "chars": 886,
    "preview": "===========\nText Search\n===========\n\nChatterBot's storage adapters support text search functionality.\n\nText Search Examp"
  },
  {
    "path": "docs/testing.rst",
    "chars": 2539,
    "preview": "============\nUnit Testing\n============\n\n*\"A true professional does not waste the time and money of other people by handi"
  },
  {
    "path": "docs/training.rst",
    "chars": 6865,
    "preview": "========\nTraining\n========\n\nChatterBot includes tools that help simplify the process of training a chat bot instance.\nCh"
  },
  {
    "path": "docs/tutorial.rst",
    "chars": 5155,
    "preview": "===================\nChatterBot Tutorial\n===================\n\nThis tutorial will guide you through the process of creatin"
  },
  {
    "path": "docs/upgrading.rst",
    "chars": 878,
    "preview": "===========================\nUpgrading to Newer Releases\n===========================\n\nLike any software, changes will be "
  },
  {
    "path": "docs/utils.rst",
    "chars": 541,
    "preview": "===============\nUtility Methods\n===============\n\nChatterBot has a utility module that contains\na collection of miscellan"
  },
  {
    "path": "examples/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "examples/basic_example.py",
    "chars": 460,
    "preview": "from chatterbot import ChatBot\nfrom chatterbot.trainers import ListTrainer\n\n# Create a new chat bot named Charlie\nchatbo"
  },
  {
    "path": "examples/convert_units.py",
    "chars": 487,
    "preview": "from chatterbot import ChatBot\n\n\nbot = ChatBot(\n    'Unit Converter',\n    logic_adapters=[\n        'chatterbot.logic.Uni"
  },
  {
    "path": "examples/default_response_example.py",
    "chars": 846,
    "preview": "from chatterbot import ChatBot\nfrom chatterbot.trainers import ListTrainer\n\n\n# Create a new instance of a ChatBot\nbot = "
  },
  {
    "path": "examples/django_example/README.rst",
    "chars": 960,
    "preview": "=========================\nChatterBot Django Example\n=========================\n\nThis is an example Django app that shows "
  },
  {
    "path": "examples/django_example/django_example/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "examples/django_example/django_example/asgi.py",
    "chars": 405,
    "preview": "\"\"\"\nASGI config for django_example project.\n\nIt exposes the ASGI callable as a module-level variable named ``application"
  },
  {
    "path": "examples/django_example/django_example/management/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "examples/django_example/django_example/management/commands/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "examples/django_example/django_example/management/commands/train.py",
    "chars": 1112,
    "preview": "\"\"\"\nThis is an example of a custom Django management command that\ntrains a ChatterBot instance with specified data.\n\nFor"
  },
  {
    "path": "examples/django_example/django_example/settings.py",
    "chars": 3134,
    "preview": "\"\"\"\nDjango settings for django_example project.\n\nGenerated by 'django-admin startproject' using Django 4.2.19.\n\nFor more"
  },
  {
    "path": "examples/django_example/django_example/static/css/bootstrap.css",
    "chars": 159511,
    "preview": "/*!\n * Bootstrap v4.4.1 (https://getbootstrap.com/)\n * Copyright 2011-2019 The Bootstrap Authors\n * Copyright 2011-2019 "
  },
  {
    "path": "examples/django_example/django_example/static/css/custom.css",
    "chars": 183,
    "preview": "/* Alternate the background color of the output rows */\n.list-group-item:nth-child(even) {\n    background-color: #bdf1ac"
  },
  {
    "path": "examples/django_example/django_example/static/js/bootstrap.js",
    "chars": 80687,
    "preview": "/*!\n  * Bootstrap v4.4.1 (https://getbootstrap.com/)\n  * Copyright 2011-2019 The Bootstrap Authors (https://github.com/t"
  },
  {
    "path": "examples/django_example/django_example/static/js/jquery.js",
    "chars": 261746,
    "preview": "/*!\n * jQuery JavaScript Library v2.0.3\n * http://jquery.com/\n *\n * Includes Sizzle.js\n * http://sizzlejs.com/\n *\n * Cop"
  },
  {
    "path": "examples/django_example/django_example/static/js/js.cookie.js",
    "chars": 1615,
    "preview": "/*! js-cookie v2.0.3 | MIT */\n!function(a){if(\"function\"==typeof define&&define.amd)define(a);else if(\"object\"==typeof e"
  },
  {
    "path": "examples/django_example/django_example/templates/app.html",
    "chars": 3864,
    "preview": "{% load static %}\n<!doctype html>\n<html>\n  <head>\n    <title>Django ChatterBot Example</title>\n    <link rel=\"shortcut i"
  },
  {
    "path": "examples/django_example/django_example/templates/nav.html",
    "chars": 1126,
    "preview": "{% load static %}\n\n<nav class=\"navbar navbar-expand-lg navbar-dark bg-dark\">\n\n  <a class=\"navbar-brand\" href=\"{% url 'ma"
  },
  {
    "path": "examples/django_example/django_example/tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "examples/django_example/django_example/tests/test_api.py",
    "chars": 3362,
    "preview": "import json\nfrom django.test import TestCase\nfrom django.urls import reverse\n\n\nclass ApiTestCase(TestCase):\n\n    def set"
  },
  {
    "path": "examples/django_example/django_example/tests/test_example.py",
    "chars": 2125,
    "preview": "import json\nfrom django.test import TestCase\nfrom django.urls import reverse\n\n\nclass ViewTestCase(TestCase):\n\n    def se"
  },
  {
    "path": "examples/django_example/django_example/urls.py",
    "chars": 390,
    "preview": "\"\"\"\nURL configuration for django_example project.\n\"\"\"\nfrom django.contrib import admin\nfrom django.urls import path\nfrom"
  },
  {
    "path": "examples/django_example/django_example/views.py",
    "chars": 1265,
    "preview": "import json\nfrom django.views.generic.base import TemplateView\nfrom django.views.generic import View\nfrom django.http im"
  },
  {
    "path": "examples/django_example/django_example/wsgi.py",
    "chars": 405,
    "preview": "\"\"\"\nWSGI config for django_example project.\n\nIt exposes the WSGI callable as a module-level variable named ``application"
  },
  {
    "path": "examples/django_example/manage.py",
    "chars": 670,
    "preview": "#!/usr/bin/env python\n\"\"\"Django's command-line utility for administrative tasks.\"\"\"\nimport os\nimport sys\n\n\ndef main():\n "
  },
  {
    "path": "examples/django_example/requirements.txt",
    "chars": 38,
    "preview": "django>=4.2,<4.3\nchatterbot>=1.2,<2.0\n"
  },
  {
    "path": "examples/export_example.py",
    "chars": 481,
    "preview": "from chatterbot import ChatBot\nfrom chatterbot.trainers import ChatterBotCorpusTrainer\n\n'''\nThis is an example showing h"
  },
  {
    "path": "examples/learning_feedback_example.py",
    "chars": 1475,
    "preview": "from chatterbot import ChatBot\nfrom chatterbot.conversation import Statement\n\n\"\"\"\nThis example shows how to create a cha"
  },
  {
    "path": "examples/math_and_time.py",
    "chars": 431,
    "preview": "from chatterbot import ChatBot\n\n\nbot = ChatBot(\n    'Math & Time Bot',\n    logic_adapters=[\n        'chatterbot.logic.Ma"
  },
  {
    "path": "examples/memory_sql_example.py",
    "chars": 585,
    "preview": "from chatterbot import ChatBot\n\n# Uncomment the following lines to enable verbose logging\n# import logging\n# logging.bas"
  },
  {
    "path": "examples/ollama_example.py",
    "chars": 1493,
    "preview": "\"\"\"\nEXPERIMENTAL: See https://docs.chatterbot.us/large-language-models/ for more information.\n\nExample of using the Olla"
  },
  {
    "path": "examples/openai_example.py",
    "chars": 1551,
    "preview": "\"\"\"\nEXPERIMENTAL: See https://docs.chatterbot.us/large-language-models/ for more information.\n\nExample of using the Open"
  },
  {
    "path": "examples/specific_response_example.py",
    "chars": 589,
    "preview": "from chatterbot import ChatBot\n\n\n# Create a new instance of a ChatBot\nbot = ChatBot(\n    'Exact Response Example Bot',\n "
  },
  {
    "path": "examples/tagged_dataset_example.py",
    "chars": 1404,
    "preview": "from chatterbot import ChatBot\nfrom chatterbot.conversation import Statement\n\n\nchatbot = ChatBot(\n    'Example Bot',\n   "
  },
  {
    "path": "examples/terminal_example.py",
    "chars": 990,
    "preview": "from chatterbot import ChatBot\n\n\n# Uncomment the following lines to enable verbose logging\n# import logging\n# logging.ba"
  },
  {
    "path": "examples/terminal_mongo_example.py",
    "chars": 700,
    "preview": "from chatterbot import ChatBot\n\n# Uncomment the following lines to enable verbose logging\n# import logging\n# logging.bas"
  },
  {
    "path": "examples/tkinter_gui.py",
    "chars": 2035,
    "preview": "from chatterbot import ChatBot\nimport tkinter as tk\ntry:\n    import ttk as ttk\n    import ScrolledText\nexcept ImportErro"
  },
  {
    "path": "examples/training_example_chatterbot_corpus.py",
    "chars": 590,
    "preview": "from chatterbot import ChatBot\nfrom chatterbot.trainers import ChatterBotCorpusTrainer\nimport logging\n\n\n'''\nThis is an e"
  },
  {
    "path": "examples/training_example_list_data.py",
    "chars": 704,
    "preview": "from chatterbot import ChatBot\nfrom chatterbot.trainers import ListTrainer\n\n\n'''\nThis is an example showing how to train"
  },
  {
    "path": "examples/training_example_ubuntu_corpus.py",
    "chars": 628,
    "preview": "\"\"\"\nThis example shows how to train a chat bot using the\nUbuntu Corpus of conversation dialog.\n\"\"\"\nimport logging\nfrom c"
  },
  {
    "path": "graphics/README.md",
    "chars": 214,
    "preview": "# ChatterBot Graphics\n\nConcept art and imagery for ChatterBot was designed by [Griffin Cox](https://github.com/griffincx"
  },
  {
    "path": "pyproject.toml",
    "chars": 2570,
    "preview": "[build-system]\nrequires = [\"setuptools\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[tool.setuptools]\npackages=[\n    \"chat"
  },
  {
    "path": "setup.cfg",
    "chars": 217,
    "preview": "[flake8]\n# H306: imports not in alphabetical order (time, os)\n# W504: line break after binary operator (conflicts with W"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/base_case.py",
    "chars": 4313,
    "preview": "from unittest import TestCase, SkipTest\nfrom chatterbot import ChatBot\nfrom chatterbot.conversation import Statement\n\n\nc"
  },
  {
    "path": "tests/django_integration/__init__.py",
    "chars": 1512,
    "preview": "\"\"\"\nDjango integration tests for ChatterBot.\n\nThis package contains tests that require Django to be installed.\nIf Django"
  },
  {
    "path": "tests/django_integration/base_case.py",
    "chars": 1757,
    "preview": "from chatterbot import ChatBot\nfrom django.test import TransactionTestCase\nfrom tests.django_integration import test_set"
  },
  {
    "path": "tests/django_integration/test_chatbot.py",
    "chars": 12426,
    "preview": "from tests.django_integration.base_case import ChatterBotTestCase\nfrom chatterbot.conversation import Statement\n\n\nclass "
  },
  {
    "path": "tests/django_integration/test_chatterbot_corpus_training.py",
    "chars": 1666,
    "preview": "from tests.django_integration.base_case import ChatterBotTestCase\nfrom chatterbot.trainers import ChatterBotCorpusTraine"
  },
  {
    "path": "tests/django_integration/test_chatterbot_settings.py",
    "chars": 508,
    "preview": "from django.test import TestCase\nfrom django.conf import settings\n\n\nclass SettingsTestCase(TestCase):\n\n    def test_modi"
  },
  {
    "path": "tests/django_integration/test_custom_models.py",
    "chars": 4502,
    "preview": "\"\"\"\nTests for custom model swapping functionality.\n\"\"\"\nfrom tests.django_integration.base_case import ChatterBotTestCase"
  },
  {
    "path": "tests/django_integration/test_django_adapter.py",
    "chars": 16449,
    "preview": "from django.test import TestCase\nfrom chatterbot.storage import DjangoStorageAdapter\nfrom chatterbot.conversation import"
  },
  {
    "path": "tests/django_integration/test_logic_adapter_integration.py",
    "chars": 1644,
    "preview": "from tests.django_integration.base_case import ChatterBotTestCase\nfrom chatterbot.conversation import Statement\n\n\nclass "
  },
  {
    "path": "tests/django_integration/test_secondary_database.py",
    "chars": 2131,
    "preview": "\"\"\"\nTests for using DjangoStorageAdapter with a secondary database.\n\"\"\"\nfrom tests.django_integration.base_case import C"
  },
  {
    "path": "tests/django_integration/test_settings.py",
    "chars": 1874,
    "preview": "\"\"\"\nDjango settings for when tests are run.\n\"\"\"\nimport os\nfrom chatterbot import constants\n\nDEBUG = True\n\nBASE_DIR = os."
  },
  {
    "path": "tests/django_integration/test_statement_integration.py",
    "chars": 1922,
    "preview": "from datetime import datetime, timezone\nfrom django.test import TestCase\nfrom chatterbot.conversation import Statement a"
  },
  {
    "path": "tests/logic/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/logic/test_best_match.py",
    "chars": 5472,
    "preview": "from chatterbot.logic import BestMatch\nfrom chatterbot.conversation import Statement\nfrom tests.base_case import ChatBot"
  },
  {
    "path": "tests/logic/test_data_cache.py",
    "chars": 1443,
    "preview": "from tests.base_case import ChatBotTestCase\nfrom chatterbot.logic import LogicAdapter\nfrom chatterbot.trainers import Li"
  },
  {
    "path": "tests/logic/test_logic_adapter.py",
    "chars": 1697,
    "preview": "from tests.base_case import ChatBotTestCase\nfrom chatterbot.logic import LogicAdapter\nfrom chatterbot.conversation impor"
  },
  {
    "path": "tests/logic/test_mathematical_evaluation.py",
    "chars": 4970,
    "preview": "from tests.base_case import ChatBotTestCase\nfrom chatterbot.logic import MathematicalEvaluation\nfrom chatterbot.conversa"
  },
  {
    "path": "tests/logic/test_specific_response.py",
    "chars": 3660,
    "preview": "from tests.base_case import ChatBotTestCase\nfrom chatterbot.logic import SpecificResponseAdapter\nfrom chatterbot.convers"
  },
  {
    "path": "tests/logic/test_time.py",
    "chars": 815,
    "preview": "from tests.base_case import ChatBotTestCase\nfrom chatterbot.logic import TimeLogicAdapter\nfrom chatterbot.conversation i"
  },
  {
    "path": "tests/logic/test_unit_conversion.py",
    "chars": 6359,
    "preview": "from tests.base_case import ChatBotTestCase\nfrom chatterbot.logic import UnitConversion\nfrom chatterbot.conversation imp"
  },
  {
    "path": "tests/storage/__init__.py",
    "chars": 0,
    "preview": ""
  }
]

// ... and 36 more files (download for full content)

About this extraction

This page contains the full source code of the gunthercox/ChatterBot GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 236 files (1.3 MB), approximately 331.2k tokens, and a symbol index with 1619 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!