From 8d97e992e7b05c6898572525d43a21cf081d7fca Mon Sep 17 00:00:00 2001 From: arunkumarp93 Date: Sun, 4 Jul 2021 09:26:05 +0530 Subject: [PATCH 1/5] Add memory and template_tags docs --- chatbot/constants.py | 9 +- docs/Makefile | 20 +++ docs/source/conf.py | 218 ++++++++++++++++++++++++++++ docs/source/index.rst | 23 +++ docs/source/memory.md | 194 +++++++++++++++++++++++++ docs/source/template_tags.md | 265 +++++++++++++++++++++++++++++++++++ setup.py | 22 +-- 7 files changed, 741 insertions(+), 10 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/source/conf.py create mode 100644 docs/source/index.rst create mode 100644 docs/source/memory.md create mode 100644 docs/source/template_tags.md diff --git a/chatbot/constants.py b/chatbot/constants.py index 87de5a7b..9a91f4d0 100644 --- a/chatbot/constants.py +++ b/chatbot/constants.py @@ -10,4 +10,11 @@ "pt-br": "sair", "he": "זהו" } -LANGUAGE_SUPPORT = ["en", "de", "pt-br","he"] + +LANGUAGE_SUPPORT = ["en", "de", "pt-br", "he"] + +NAME = 'chatbotAI' +AUTHOR = "Ahmad Faizal B H" +AUTHOR_EMAIL = "ahmadfaizalbh726@gmail.com" +COPYRIGHT = "Copyright (c) 2017 Ahmad Faizal B H" +URL = "https://github.com/ahmadfaizalbh/Chatbot" diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..a7ca318f --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +html: + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(0) + diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..6629ea0b --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use abspath to make it absolute, like shown here. + +from os.path import dirname, join, abspath +from runpy import run_path + +import sphinx_bootstrap_theme +from recommonmark.transform import AutoStructify +from recommonmark.parser import CommonMarkParser + +source_parsers = { + '.md': CommonMarkParser +} + +constants = run_path(join(dirname(dirname(dirname(abspath(__file__)))), + 'chatbot', 'constants.py')) +# -- Project information ----------------------------------------------------- + +project = constants['NAME'] +copyright = constants['COPYRIGHT'] +author = constants['AUTHOR'] + +# The short X.Y version +version = '' +# The full version, including alpha/beta/rc tags +release = '' + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', + 'sphinx.ext.coverage', + 'sphinx.ext.mathjax', + 'sphinx.ext.ifconfig', + 'sphinx.ext.viewcode', + 'sphinx_markdown_tables'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +source_suffix = ['.rst', '.md'] +# source_suffix = '.md' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'bootstrap' +html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() + +html_theme_options = { + # Bootswatch (http://bootswatch.com/) theme. + # + # Options are nothing (default) or the name of a valid theme + # such as "cosmo" or "sandstone". + # + # The set of valid themes depend on the version of Bootstrap + # that's used (the next config option). + # + # Currently, the supported themes are: + # - Bootstrap 2: https://bootswatch.com/2 + # - Bootstrap 3: https://bootswatch.com/3 + 'bootswatch_theme': "yeti", + + # Choose Bootstrap version. + # Values: "3" (default) or "2" (in quotes) + 'bootstrap_version': "3", +} + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'ChatBotdoc' + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'ChatBot.tex', 'ChatBot Documentation', + 'Arunkumar', 'manual'), +] + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'chatbot', 'ChatBot Documentation', + [author], 1) +] + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'ChatBot', 'ChatBot Documentation', + author, 'ChatBot', 'One line description of project.', + 'Miscellaneous'), +] + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} + + +def setup(app): + app.add_config_value('recommonmark_config', { + 'enable_math': True, + 'enable_eval_rst': True, + 'auto_code_block': True, + }, True) + app.add_transform(AutoStructify) diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..96f36c7d --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,23 @@ +.. ChatBot documentation master file, created by + sphinx-quickstart on Sat May 22 20:00:38 2021. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to ChatBot's documentation! +=================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + template_tags + memory + + + + + + +Indices and tables +================== +* :ref:`search` diff --git a/docs/source/memory.md b/docs/source/memory.md new file mode 100644 index 00000000..642f208b --- /dev/null +++ b/docs/source/memory.md @@ -0,0 +1,194 @@ +# Memory +> Memory store the information/value for any user specific attributes, for example storing users names, age, other details or any other information user want the bot to remember. +> +> Helps in setting up remainders and knowledge-base. This can be done in template response tag or python call back functions through session objects. + +## SET MEMORY +### Set memory with-in template tag +Setting memory with-in template should be done between +`{% response %}` and `{% endresponse %}` tags. + +#### Set memory in copy mode +setting up memory in copy mode stores value for given variable name for current user and puts the value as part of the +text in place of set memory tag or can be used for any other purpose like with-in condition or callbacks. + +##### Syntax +``` +{ vairable_name : value } +``` + +##### Example +1. constant value set string value `Arun` in variable `Name`. + + ``` + { Name : Arun } + ``` + +2. dynamic value (set from client match group) set `full_name` entity from client message match group as `Name`. + + ``` + { Name : %full_name } + ``` + + or set first matching group from client message as `Name` + + ``` + { Name : %1 } + ``` + +3. dynamic variable name with constant value set `object` entity from client message group as variable name with +constant value `Hot`. + + ``` + { %object: Hot } + ``` + +4. dynamic variable name and value + + set `object` entity from client message group as variable name and `property` entity from client message group. + + ``` + { %object : %property } + ``` + +5. Full block example + ##### Template + Below Template, we have the variable as `name` and value as `value` for the variable both are regex based inputs. + when user enter the inputs it'll be stored in memory and return the response tag value to user. + ``` + {% group %} + {% block %} + {% client %}remember (?P.*) is (?P.*){% endclient %} + {% response %}I will remember %name is { %name : %value }{% endresponse %} + {% endblock %} + {% endgroup %} + ``` + + ##### Chat Sample + In below example memory for current user session variable `sun` will be set with value + `is red hot star in our solar system`. + + Now the response text is return automatically from the template. + ``` + > remember sun is red hot star in our solar system + I will remember sun is red hot star in our solar system + ``` + + + +#### Set memory in think mode + +setting up memory in copy mode stores value for given variable name for current user and returns nothing. Hence, when it +is used with-in response tag it'll be replaced by empty string. + +As we can see in below syntax we have the `!` symbol before the variable name to differentiate between think and copy mode of template tag. + +##### Syntax + +``` +{! vairable_name : value } +``` + +##### Example +Number 25 is set to Age variable +``` +{!Age: 25} +``` + +##### Full block example +Below example age input field takes the Integer values and store it in memory. +``` +{% group %} + {% block %} + {% client %}My age is (?P\d+){% endclient %} + {% response %} + I will remember your age {! age: %age } + { % endresponse %} + {% endblock %} +{% endgroup %} +``` + +##### Chat Sample +Age will be store for the current user in memory and not included in the response, as you can see from the above +example. +``` +> My age is 29 +I will remember your age +``` +## GET MEMORY +Get Memory is used to get the values stored in the memory object. +we can get memory in two ways +1. Template Tag +2. Python session + +### Get memory value with-in template tag +This type of get memory used inside the template tags. +It can be used for response or ask query to user. + +##### Syntax +``` +{ variable_name } +``` + +#### Example +`name` is the key value store in the memory. + +``` +{name} +``` +#### Full block sample + +##### Template +Below template sample we have the query +`Do you (know|remember) my name?` for this the response will be checking the +`name` key in the memory and return the value if it exists or just return the question. +``` + +{% block %} + {% client %}Do you (know|remember) my name{% endclient %} + {% response %}{% if {name} %}Yes I do {name}{% else %}No,{% chat what is my name %}{% endif %}{% endresponse %} +{% endblock %} +``` + +#####Chat Sample +Chat sample name is already stored in memory . So it's returning +`Yes I do Arun`. + +``` +> do you name my name? +> Yes I do Arun +``` + +### Set and Get memory value in python session +Session object in python caller function has attribute memory, +Which is dictionary kind of object where user specific info is stored. + +#### Syntax set memory +``` + session.memory[variable_name] = value +``` +##### Example +Below example variable name is `year` and setting value as `2021`. +``` +session.memory[year] = 2021 +``` + +#### Syntax + +1. ``` + session.memory.get(variable_name) + ``` + + 2. ``` + session.memory[variable_name] + ``` + +##### Example +To get the `year` value from the memory we can use below ways + +1. ``` + session.memory.get('year') + ``` +2. ``` + session.memory['year'] + ``` \ No newline at end of file diff --git a/docs/source/template_tags.md b/docs/source/template_tags.md new file mode 100644 index 00000000..c669d2f9 --- /dev/null +++ b/docs/source/template_tags.md @@ -0,0 +1,265 @@ +# Template Tags +> Template tags are generating the chat response. +> These are like web template system similar to JINJA template with following tags Group, Block, Client, Response and Learn Tags + +## Group Tags +Group tag are used to group the response to particular topic. Each group can have multiple subgroups group. +If we didn't create block inside the group or group without topic name those will be a generic group. + +Sub-group response will be checked and return from the current block, parent block and generic block. + +### Template Syntax +``` +{% group topicname %} + ... +{% endgroup %} +``` + +#### Generic Group sample Template +Below Template sample we have the general group. +`{% group %}` group tag doesn't have the name + +``` +{% group %} +{% block %} + {% client %}show (?P.*){% endclient %} + {% response %}{ %name }{% endresponse %} +{% endblock %} +{% endgroup %} +``` + +### Named group sample Template +Below template is sample for named group and nested group. + +We have the name `{% group continent %}` inside the group tag. So this will be under the continent group. + +``` +{% group continent %} + {% group asia %} + {% client %}Regex{% endclient %} + {% response %} Response{% endresponse %} + {% endgroup %} + {% group europe %} + {% client %}Regex{% endclient %} + {% response %} Response{% endresponse %} + {% endgroup %} + ... +{% endgroup %} +``` + + + +## Block Tags +Block holds each client query and response details. Every block must have the at-least one client block. + +### Template Syntax +``` +{% block %} +... +{% endblock %} +``` +### Template sample +Sample block tag with at-least single client tag. +``` +{% block %} + {% client %}(what is|(do you remember|tell me) about) (?P.*){% endclient %} +{% endblock %} +``` + +## Client Tags +Client tag is user input handler. We can have static, dynamic and multiple user input inside this tag. +Dynamic text we are handling with python [regex](https://docs.python.org/3/howto/regex.html). +Multiple user input we need to user `|` operator. + +### Template Syntax +``` +{% client %} +{% endclient %} +``` + +### Static input sample +Below sample, we are handing the static user input. + +``` +{% block %} + {% client %}What is your name{% endclient %} + {% response %}My name is ChatBot{% endresponse %} +{% endblock %} +``` + +#### Chat sample +``` +> What is your name +My name is ChatBot +``` + +### Dynamic input sample +`(?P.*)` is the regex based input and stores the input in the variable `name`. +Later we have display this to user or store in memory +``` +{% block %} + {% client %} show (?P.*){% endclient %} + {% response %}%name{% endresponse%} +{% endblock %} +``` +#### Chat sample +Below example takes the random input from user. + +``` +> show Age +Age +``` +### Multiple input Template +Using the `|` we can take multiple inputs +``` +{% block %} + {% client %}(what is|(do you remember|tell me) about) (?P.*){% endclient %} + {% response %} %name {% endresponse %} +{% endblock %} +``` + +#### Chat Sample +Below sample, we can return same response to multiple user queries +1. +``` +> What is about sun + sun +``` +2. +``` +> do you remember about sun +sun +``` + +## Response Tags +Response tag used for give response to user queries. +we can have the static response, dynamic response or calling a function. + +### Template Syntax +``` +{% response %} +... +{% endresponse %} +``` + +### Dynamic response Template +Below sample have the random response for the single client + +``` + {% block %} + {% client %}I need (.*){% endclient %} + {% response %}Why do you need %1?{% endresponse %} + {% response %}Would it really help you to get %1?{% endresponse %} + {% response %}Are you sure you need %1?{% endresponse %} + {% endblock %} +``` +#### Chat Sample + +When, user enter the sample query multiple times we get the random response. +1. +``` +>I Need something +Why do you need something +``` + +2. +``` +>I Need something +Are you sure you need something +``` + +### Function call sample Template + +`call` is used for calling custom function which is having the logic to handle the use inputs. + +Below is the sample of increment_count function. This will take the variable and increase it count on memory + +``` +{% block %} + {% client %}increment (?P.*){% endclient %} + {% response %}{% call increment_count: %name %}{% endresponse %} +{% endblock %} +``` + +#### Chat Sample +Below sample Age variable value is increased in the memory. + +1. +``` +> increment Age +Age 1 +``` +2. +``` +increment Age +Age 2 +``` + + + +## Previous Tags +Previous tag helps to give the proper response or save some input for future response + +### Template Syntax +``` +{% prev %} +{% endprev %} +``` + +### Sample previous Template +``` +{% block %} + {% client %}(I (am|feel) )?(feeling )?(absolutely )?(.*){% endclient %} + {% prev %}.*how are you{% endprev %} + {% response %}{% if {%low %5 %} == fine | {%low %5 %} == good | {%low %5 %} == happy %} Nice to know that you are %5. What else? {% else %} why you feel %5 {% endif %}{% endresponse %} +{% endblock %} +``` +#### Chat Sample +``` +> I am feeling good + Nice to know that you are good. What else? +``` + +## Learn Tags +Learn tag is dynamic object and description without static object and response. + +### Syntax +``` +{% learn %} +{% endlearn %} +``` +### Learn tag template sample +Below example, we do not have the static variable and value +`(?P.*) ` is variable take any input , their value must start with is/are `(?P(is|are) .*)`. + +Above mention object and description is added inside the learn tag with client query and response for the user next response. + +``` +{% block %} + {% client %}Remember (?P.*) (?P(is|are) .*){% endclient %} + {% response %}I will remember that %object %description {% endresponse %} + {% learn %} + {% group %} + {% block %} + {% client %}(Do you know|tell me) about %object{% endclient %} + {% response %}%object %description{% endresponse %} + {% endblock %} + {% endgroup %} + {% endlearn %} +{% endblock %} +``` + +###Chat Sample to get the value from learn tag + +remember is the client static input and `learn` is a variable startswith `is` and other description. +This will be added to memory. +``` +> remember learn is tag give dynamic response +I will remember that learn is tag give dynamic response +``` + +We can return the description response when the query raised from user `tell me about learn`/ +``` +> tell me about learn +learn is tag give dynamic response +``` diff --git a/setup.py b/setup.py index 9fe666e2..b7bc9892 100755 --- a/setup.py +++ b/setup.py @@ -1,9 +1,13 @@ #!/usr/bin/env python +from os.path import join, dirname, abspath +from runpy import run_path from setuptools import setup version = __import__('chatbot.version').__version__ -LANGUAGE_SUPPORT = __import__('chatbot.constants').LANGUAGE_SUPPORT +constants = run_path(join(abspath(dirname(__file__)), 'chatbot', 'constants.py')) + +LANGUAGE_SUPPORT = constants['LANGUAGE_SUPPORT'] package_data = [] with open("README.md", "r") as fh: @@ -16,12 +20,12 @@ "local/%s/substitutions.json" % language ]) setup( - name='chatbotAI', + name=constants['NAME'], version=version, - author="Ahmad Faizal B H", - author_email="ahmadfaizalbh726@gmail.com", - url="https://github.com/ahmadfaizalbh/Chatbot", - description="A chatbot AI engine is a chatbot builder platform that provids both bot intelligence and" + author=constants['AUTHOR'], + author_email=constants['AUTHOR_EMAIL'], + url=constants['URL'], + description="A chatbot AI engine is a chatbot builder platform that provides both bot intelligence and" " chat handler with minimal codding", long_description=long_description, long_description_content_type="text/markdown", @@ -35,8 +39,8 @@ 'chatbot.substitution': 'chatbot/substitution' }, include_package_data=True, - package_data={"chatbot": package_data}, + package_data={"chatbot": package_data}, install_requires=[ - 'requests', - ] + 'requests', + ] ) From 5a8af5f9c76def043f9078d8ba46214119d18835 Mon Sep 17 00:00:00 2001 From: arunkumarp93 Date: Sat, 10 Jul 2021 16:27:14 +0530 Subject: [PATCH 2/5] Add recursion docs --- docs/source/index.rst | 2 +- docs/source/recursion.md | 76 ++++++++++++++++++++++++++++++++++++ docs/source/template_tags.md | 19 +++++++-- 3 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 docs/source/recursion.md diff --git a/docs/source/index.rst b/docs/source/index.rst index 96f36c7d..68614ca2 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -12,7 +12,7 @@ Welcome to ChatBot's documentation! template_tags memory - + recursion diff --git a/docs/source/recursion.md b/docs/source/recursion.md new file mode 100644 index 00000000..aad66ff7 --- /dev/null +++ b/docs/source/recursion.md @@ -0,0 +1,76 @@ +# Recursion +> Recursion gives the recursive multiple response to the user query. +> It picks the particular block matches with the user query and return the response on that block +> Whichever block or prev tag matched to user input will be picked for the response. + +## Syntax +``` +{% chat statement %} +``` + +### Template Sample +`Please (.*)` is the user query matching text. `chat` will match the specific `block` +or `prev` tag matched with the `Please` +``` +{% block %} +{% client %}Please (.*){% endclient %} +{% response %}{% chat %1 %}{% endresponse %} +{% endblock %} +``` + +#### Full Template sample +Below sample, we are having the user input matching template and matched response template + +1. user input block + + +When user type `Please` with some text we have the response as `chat` this will be matched with the other blocks +with the matching user input. + +``` +{% block %} +{% client %}Please (.*){% endclient %} +{% response %}{% chat %1 %}{% endresponse %} +{% endblock %} +``` + +1. response block + +Sample response for the above user input. + +``` +{% response %}Please tell me more.{% endresponse %} +{% response %}Let's change focus a bit... Tell me about your family.{% endresponse %} +{% response %}Can you elaborate on that?{% endresponse %} +{% response %}Why do you say that %0?{% endresponse %} +{% response %}I see.{% endresponse %} +{% response %}Very interesting.{% endresponse %} +{% response %}%0.{% endresponse %} +{% response %}I see. And what does that tell you?{% endresponse %} +{% response %}How does that make you feel?{% endresponse %} +{% response %}How do you feel when you say that?{% endresponse %} +``` + +#### Chat Sample +Below example when user type `Please reply` +now chatbot will match this to recursive response as `Very interesting.` +that we have in above full template sample. + +If we ask the sample question we get the different response from first matched block. + +One of our response have `{% response %}%0.{% endresponse %}` +so we are getting the user input value as response in the last chat sample + + + +``` +> Please reply +Very interesting. + +> Please do something +How do you feel when you say that? + +> Please help to get the info +help to get the info . + +``` \ No newline at end of file diff --git a/docs/source/template_tags.md b/docs/source/template_tags.md index c669d2f9..e04fc044 100644 --- a/docs/source/template_tags.md +++ b/docs/source/template_tags.md @@ -198,7 +198,7 @@ Age 2 ## Previous Tags -Previous tag helps to give the proper response or save some input for future response +Previous tag used to give the auto response for the chat query from the Chatbot. ### Template Syntax ``` @@ -207,6 +207,8 @@ Previous tag helps to give the proper response or save some input for future res ``` ### Sample previous Template +If Chatbot ask query `how are you` and user text can be `I feel/am feeling/ am absolutely good`. +we are matching the current user input with the previous text and returning the response. ``` {% block %} {% client %}(I (am|feel) )?(feeling )?(absolutely )?(.*){% endclient %} @@ -214,10 +216,19 @@ Previous tag helps to give the proper response or save some input for future res {% response %}{% if {%low %5 %} == fine | {%low %5 %} == good | {%low %5 %} == happy %} Nice to know that you are %5. What else? {% else %} why you feel %5 {% endif %}{% endresponse %} {% endblock %} ``` -#### Chat Sample +#### Chat Sample +Below Sample, we have the auto query from bot `Hi, how are you?`. +If user enter `I feel good` or in the pattern inside the client tag. +They get the response based on the input in this sample we have input `good`. +so we get response `Nice to know that you are good`. +Other than fine, good, happy response will be `why you feel ` + + ``` -> I am feeling good - Nice to know that you are good. What else? +Hi, how are you? +> I feel good + Nice to know that you are good. What else? + ``` ## Learn Tags From e4fd8c27706d731796cf4b08a667a8b55cd06e54 Mon Sep 17 00:00:00 2001 From: arunkumarp93 Date: Sat, 11 Sep 2021 16:58:08 +0530 Subject: [PATCH 3/5] template and language docs added --- chatbot/__init__.py | 176 +++++++++------- docs/source/language_and_template.md | 197 +++++++++++++++++ docs/source/recursion.md | 54 +++-- docs/source/template_tags.md | 304 +++++++++++++++++++-------- 4 files changed, 548 insertions(+), 183 deletions(-) create mode 100644 docs/source/language_and_template.md diff --git a/chatbot/__init__.py b/chatbot/__init__.py index 3a564651..00a8ad3d 100644 --- a/chatbot/__init__.py +++ b/chatbot/__init__.py @@ -19,10 +19,8 @@ except NameError: input_reader = input - __version__ = version.__version__ - DEFAULT_ATTRIBUTE = {"match": None, "pmatch": None, "_quote": False, "substitute": True} RE_TAG_PARENTHESIS = re.compile(r'{%?|%?}|\[|\]') RE_OPERATORS = re.compile(r'([\<\>!=]=|[\<\>]|&|\|)') @@ -203,20 +201,20 @@ def __init__handler(self): "topic": self.__topic_handler, "map": self.__map_handler, "eval": self.__eval_handler, - } + } self.__conditional_operator = { - "!=": lambda a, b: a != b, - ">=": lambda a, b: a >= b, - "<=": lambda a, b: a <= b, - "==": lambda a, b: a == b, - "<": lambda a, b: a < b, - ">": lambda a, b: a > b - } + "!=": lambda a, b: a != b, + ">=": lambda a, b: a >= b, + "<=": lambda a, b: a <= b, + "==": lambda a, b: a == b, + "<": lambda a, b: a < b, + ">": lambda a, b: a > b + } self.__logical_operator = { - '&': lambda a, b: a and b, - '|': lambda a, b: a or b, - '^': lambda a, b: a ^ b - } + '&': lambda a, b: a and b, + '|': lambda a, b: a or b, + '^': lambda a, b: a ^ b + } def __normalize(self, text): """ @@ -235,18 +233,29 @@ def __error_message(expected, text, pos, index): return "Expected '%s' tag found '%s' in line `%s`" % (expected, pos[index][2], content) def __response_tags(self, text, pos, index): - next_index = index+1 + next_index = index + 1 if pos[next_index][2] != "endresponse": raise SyntaxError(self.__error_message("endresponse", text, pos, index)) return text[pos[index][1]:pos[next_index][0]].strip(" \t\n") def __block_tags(self, text, pos, length, index): + """ + text: (str) template file + pos: (list) list of tuples + (tag_content_start, tag_content_pos_end, tag_name, '') + length:(int) total count of pos list + index:(int) current processing tag index inside the pos list + + Process the each block tags then + return the index and content inside the block. + + """ within_block = {"learn": {}, "response": [], "client": [], "prev": []} while pos[index][2] != "endblock": if pos[index][2] == "learn": within_block["learn"] = {} index = self.__group_tags(text, pos, within_block["learn"], - (lambda i: pos[i][2] != "endlearn"), length, index+1) + (lambda i: pos[i][2] != "endlearn"), length, index + 1) index -= 1 elif pos[index][2] == "response": within_block["response"].append(self.__response_tags(text, pos, index)) @@ -255,36 +264,48 @@ def __block_tags(self, text, pos, length, index): index += 1 if pos[index][2] != "endclient": raise SyntaxError(self.__error_message("endclient", text, pos, index)) - within_block["client"].append(text[pos[index-1][1]:pos[index][0]].strip(" \t\n")) + within_block["client"].append(text[pos[index - 1][1]:pos[index][0]].strip(" \t\n")) elif pos[index][2] == "prev": index += 1 if pos[index][2] != "endprev": raise SyntaxError(self.__error_message("endprev", text, pos, index)) - within_block["prev"].append(text[pos[index-1][1]:pos[index][0]].strip(" \t\n")) + within_block["prev"].append(text[pos[index - 1][1]:pos[index][0]].strip(" \t\n")) else: - content = text[max(0, pos[index-1][0]): pos[index][1]+5].strip() + content = text[max(0, pos[index - 1][0]): pos[index][1] + 5].strip() raise NameError("Invalid Tag '%s': Error in `%s` " % (pos[index][2], content)) index += 1 - return index+1, (within_block["client"], - within_block["prev"] if within_block["prev"] else None, - within_block["response"], - within_block["learn"]) + return index + 1, (within_block["client"], + within_block["prev"] if within_block["prev"] else None, + within_block["response"], + within_block["learn"]) def __group_tags(self, text, pos, groups, condition, length, index=0, name=""): + """ + text: (str) template file + pos: (list) list of tuples + (tag_content_start, tag_content_pos_end, tag_name, '') + groups: (dict) groups key values pairs + condition: (function) condition to process the tag + length:(int) total count of pos list + index:(int) current processing tag index inside the pos list + + Iterate the processed template based on block type (block, response, group). + Group tags are recursively processed. Both are append to pairs and defaults. + """ pairs = [] defaults = [] while condition(index): if pos[index][2] == "block": - p, within = self.__block_tags(text, pos, length, index+1) + p, within = self.__block_tags(text, pos, length, index + 1) pairs.append(within) index = p elif pos[index][2] == "response": defaults.append(self.__response_tags(text, pos, index)) index += 2 elif pos[index][2] == "group": - child_name = (name+"."+pos[index][3].strip()) if name else pos[index][3].strip() + child_name = (name + "." + pos[index][3].strip()) if name else pos[index][3].strip() index = self.__group_tags(text, pos, groups, - (lambda i: pos[i][2] != "endgroup"), length, index+1, name=child_name) + (lambda i: pos[i][2] != "endgroup"), length, index + 1, name=child_name) else: raise SyntaxError(self.__error_message('group, block, or response', text, pos, index)) if name in groups: @@ -292,9 +313,19 @@ def __group_tags(self, text, pos, groups, condition, length, index=0, name=""): groups[name]["defaults"].extend(defaults) else: groups[name] = {"pairs": pairs, "defaults": defaults} - return index+1 + return index + 1 def __process_template_file(self, file_name): + """ + file_name: (str) template file path + return: (dict) Each group response separate by group name. + + Read the template file and generate the list of tuple + (tag_start_pos, tag_end_pos, tag_name, '') for each block tags. + + Then, Update groups dictionary with specific group. If group name is not given + then it'll consider as general group. + """ with open(file_name, encoding='utf-8') as template: text = template.read() pos = [(m.start(0), m.end(0), text[m.start(1):m.end(1)], text[m.start(4):m.end(4)]) @@ -314,7 +345,7 @@ def __build_pattern(self, patterns): try: regexps.append(re.compile(self.__normalize(pattern), re.IGNORECASE)) except Exception as e: - e.args = (str(e) + " in pattern "+pattern, ) + e.args = (str(e) + " in pattern " + pattern,) raise e return regexps @@ -390,22 +421,22 @@ def _restructure(self, group, index=None): def _sub_action(self, group, start_end_pair, action): return {i: { - "action": action[i], - "start": start_end_pair[i][0], - "end": start_end_pair[i][1], - "child": self._sub_action(group[i], start_end_pair, action) - } for i in group} + "action": action[i], + "start": start_end_pair[i][0], + "end": start_end_pair[i][1], + "child": self._sub_action(group[i], start_end_pair, action) + } for i in group} def _get_within(self, group, index): def init_group(p): group[index[p]]["within"] = [] ordered_group.append(group[index[p]]) - return p+1 + return p + 1 def append_group(position, p): position, within = self._get_within(group, index[position:]) - group[index[p-1]]["within"] += within + group[index[p - 1]]["within"] += within return position i = 0 @@ -423,7 +454,7 @@ def append_group(position, p): pos = i = init_group(i) start_if = False while group[index[pos]]["action"] != "endif": - pos = append_group(pos, i)+i + pos = append_group(pos, i) + i i = init_group(pos) elif group[index[i]]["action"] == "endif": i = init_group(i) @@ -463,11 +494,11 @@ def _inherit(self, start_end_pair, action): def __action(self, response, pos, index): end_tag = pos.pop(index) - begin_tag = pos.pop(index-1) - b_n = begin_tag[1]-begin_tag[0] - e_n = end_tag[1]-end_tag[0] + begin_tag = pos.pop(index - 1) + b_n = begin_tag[1] - begin_tag[0] + e_n = end_tag[1] - end_tag[0] start_char = response[begin_tag[0]] - end_char = response[end_tag[1]-1] + end_char = response[end_tag[1] - 1] if b_n != e_n or not ((start_char == "{" and end_char == "}") or (start_char == "[" and end_char == "]")): raise SyntaxError("invalid syntax '%s'" % response) if b_n == 2: @@ -483,16 +514,16 @@ def __action(self, response, pos, index): def _condition(self, response): pos = ((m.start(0), m.end(0)) for m in RE_TAG_PARENTHESIS.finditer(response)) - pos = [(start, end) for start, end in pos if (not start) or response[start-1] != "\\"] + pos = [(start, end) for start, end in pos if (not start) or response[start - 1] != "\\"] start_end_pair = [] actions = [] while pos: index = 0 for _, ele in pos[1:]: index += 1 - if response[ele-1] in "}]": + if response[ele - 1] in "}]": break - if not (index and response[pos[index-1][0]] in "{["): + if not (index and response[pos[index - 1][0]] in "{["): raise SyntaxError("invalid syntax in \"%s\"" % response) start, end, action = self.__action(response, pos, index) start_end_pair.append((start, end)) @@ -529,7 +560,7 @@ def _check_if(self, session, con): for j, ele in enumerate(pos): s, e, o = ele try: - second = con[e:pos[j+1][0]].strip() + second = con[e:pos[j + 1][0]].strip() except IndexError: second = con[e:].strip() try: @@ -560,7 +591,7 @@ def __if_handler(self, session, i, condition, response): start, end) i += 1 if self._check_if(session, con): - matched_index = i-1 + matched_index = i - 1 while condition[i]["action"] != "endif": i += 1 check = False @@ -578,8 +609,8 @@ def __if_handler(self, session, i, condition, response): session.attr["substitute"] = substitute return ((self._check_and_evaluate_condition(session, response, condition[matched_index]["within"], - condition[matched_index]["end"]+2, - condition[matched_index+1]["start"]-2 + condition[matched_index]["end"] + 2, + condition[matched_index + 1]["start"] - 2 ) if matched_index is not None else ""), i) def __handler(self, session, condition, response, action): @@ -616,7 +647,7 @@ def __topic_handler(self, session, condition, response): @staticmethod def __get_start_pos(start, response, exp): - return start+re.compile(r"([\s\t]*"+exp+r"[\s\t]+)").search(response[start:]).end(1) + return start + re.compile(r"([\s\t]*" + exp + r"[\s\t]+)").search(response[start:]).end(1) def __map_handler(self, session, condition, response): start = condition["start"] @@ -631,7 +662,7 @@ def __map_handler(self, session, condition, response): this_index = 0 for this_index in range(1, len(content)): if name[-1] == "\\": - name += ":"+content[this_index] + name += ":" + content[this_index] else: this_index -= 1 break @@ -639,9 +670,9 @@ def __map_handler(self, session, condition, response): name = name.strip().lower() if this_index < (len(content)): value = content[this_index] - for this_index in range(this_index+1, len(content)): + for this_index in range(this_index + 1, len(content)): if value[-1] == "\\": - value += ":"+content[this_index] + value += ":" + content[this_index] else: break session.memory[name] = self._substitute(session, value.strip()) @@ -673,7 +704,7 @@ def __eval_handler(self, session, condition, response): key = pair[0] data[key] = ":".join(pair[1:]) elif key is not None: - data[key] += ","+pair[0] + data[key] += "," + pair[0] else: raise SyntaxError("invalid syntax '%s'" % response[start:end]) result = self.__api_handler(api_name, method_name, data) @@ -683,7 +714,7 @@ def __api_request(self, url, method, **karg): try: return requests.__dict__[method.lower().strip()](url, **karg) except requests.exceptions.MissingSchema: - return self.__api_request("http://"+url, method, **karg) + return self.__api_request("http://" + url, method, **karg) except requests.exceptions.ConnectionError: raise RuntimeError("Couldn't connect to server (unreachable). Check your network") except KeyError: @@ -742,7 +773,7 @@ def __substitute_from_client_statement(self, session, match, prev_response, sile for m in re_numbered_group.finditer(prev_response): start = m.start(0) end = m.end(0) - num = int(prev_response[start+start_padding:end]) + num = int(prev_response[start + start_padding:end]) final_response += prev_response[prev:start] try: final_response += self._quote(session, self._substitute(session, match.group(num))) @@ -757,7 +788,7 @@ def __substitute_from_client_statement(self, session, match, prev_response, sile for m in re_named_group.finditer(prev_response): start = m.start(1) end = m.end(1) - final_response += prev_response[prev:start-start_padding] + final_response += prev_response[prev:start - start_padding] value = named_group.get(prev_response[start:end], "").strip() if value: final_response += self._quote(session, self._substitute(session, value)) @@ -778,7 +809,7 @@ def _check_and_evaluate_condition(self, session, response, condition=[], start_i final_response = "" _quote = session.attr.get("_quote", True) while i < len(condition): - pos = condition[i]["start"]-(1 if condition[i]["action"] in ("map", "eval") else 2) + pos = condition[i]["start"] - (1 if condition[i]["action"] in ("map", "eval") else 2) final_response += self._check_and_evaluate_condition(session, response[start_index:pos]) try: session.attr["_quote"] = False @@ -790,7 +821,7 @@ def _check_and_evaluate_condition(self, session, response, condition=[], start_i if condition[i]["action"] == "if": response_txt, i = self.__if_handler(session, i, condition, response) final_response += response_txt - start_index = condition[i]["end"]+(1 if condition[i]["action"] in ("map", "eval") else 2) + start_index = condition[i]["end"] + (1 if condition[i]["action"] in ("map", "eval") else 2) i += 1 final_response += self._check_and_evaluate_condition(session, response[start_index:end_index]) return final_response @@ -802,7 +833,7 @@ def _wildcards(self, session, response, match, parent_match): return re.sub(r'\\([\[\]{}%:])', r"\1", self._check_and_evaluate_condition(session, response, condition)) def __chose_and_process(self, session, choices, match, parent_match): - resp = random.choice(choices) # pick a random response + resp = random.choice(choices) # pick a random response resp = self._wildcards(session, resp, match, parent_match) # process wildcards # fix munged punctuation at the end if resp[-2:] == '?.': @@ -828,17 +859,18 @@ def __intend_selection(self, text, previous_text, current_topic): def __response_on_topic(self, session, text, previous_text, text_correction, current_topic): match = self.__intend_selection(text, previous_text, current_topic) or \ - self.__intend_selection(text_correction, previous_text, current_topic) + self.__intend_selection(text_correction, previous_text, current_topic) if match: match, parent_match, response, learn = match if learn: self.__process_learn({ self._wildcards(session, (topic, self._condition(topic)), match, parent_match): - { - 'pairs': [self.__substitute_in_learn(session, pair, match, parent_match) - for pair in learn[topic]['pairs']], - 'defaults': [self._wildcards(session, (default, self._condition(default)), match, parent_match) - for default in learn[topic]['defaults']]} + { + 'pairs': [self.__substitute_in_learn(session, pair, match, parent_match) + for pair in learn[topic]['pairs']], + 'defaults': [ + self._wildcards(session, (default, self._condition(default)), match, parent_match) + for default in learn[topic]['defaults']]} for topic in learn }) return self.__chose_and_process(session, response, match, parent_match) @@ -894,34 +926,34 @@ def save_template(self, filename): self.__generate_and_write_template(template, self._pairs, topic_name, sub_topic) def __generate_and_write_template(self, template, pairs, topic, sub_topics, base_path=None, padding=""): - full_path = (base_path+"."+topic) if base_path else topic + full_path = (base_path + "." + topic) if base_path else topic if topic: - template.write(padding + "{% group "+topic+" %}\n") + template.write(padding + "{% group " + topic + " %}\n") new_padding = padding + "\t" else: new_padding = padding for topic_name, sub_topic in sub_topics.items(): self.__generate_and_write_template(template, pairs, topic_name, sub_topic, full_path, - padding=new_padding+"\t") + padding=new_padding + "\t") for (patterns, parents, response, learn) in pairs[full_path]["pairs"]: template.write(new_padding + "{% block %}\n") if parents is None: parents = [] for parent in parents: - template.write(new_padding + "\t{% prev %}"+parent.pattern+"{% endprev %}\n") + template.write(new_padding + "\t{% prev %}" + parent.pattern + "{% endprev %}\n") for pattern in patterns: - template.write(new_padding + "\t{% client %}"+pattern.pattern+"{% endclient %}\n") + template.write(new_padding + "\t{% client %}" + pattern.pattern + "{% endclient %}\n") for res in response: - template.write(new_padding + "\t{% response %}"+res[0]+"{% response %}\n") + template.write(new_padding + "\t{% response %}" + res[0] + "{% response %}\n") if learn: template.write(new_padding + "\t{% learn %}\n") for topic_name, sub_topic in self.__get_topic_recursion(learn).items(): self.__generate_and_write_template(template, learn, topic_name, sub_topic, - padding=new_padding+"\t") + padding=new_padding + "\t") template.write(new_padding + "\t{% endlearn %}\n") template.write(new_padding + "{% endblock %}\n") for res in pairs[topic]["defaults"]: - template.write(new_padding + "{% response %}"+res[0]+"{% response %}\n") + template.write(new_padding + "{% response %}" + res[0] + "{% response %}\n") if topic: template.write(padding + "{% endgroup %}\n") diff --git a/docs/source/language_and_template.md b/docs/source/language_and_template.md new file mode 100644 index 00000000..cd209ac6 --- /dev/null +++ b/docs/source/language_and_template.md @@ -0,0 +1,197 @@ +Currently, supported to following language support English, German, Hebrew, Portuguese (Brazil). Each language we have +substitutions and spellcheck. + +# Add a new language + +Add a new language inside the local folder. Folder name must be +in [ISO-639_1 codes](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). Local folder is inside +the [chatbot](https://github.com/ahmadfaizalbh/Chatbot/tree/master/chatbot/local) folder. + +# Add a template + +Add a ***default.template*** file inside a specific language folder and add the template details. + +## Example + +iso code for english is `en`. So Folder name must be **en**. Inside this folder we create file **default.template** + +# Spellcheck and Substitutions + +Spellcheck we have ***words.txt*** file. If the word is more than three character then it'll be compare inside the words.txt +file for corrections. + +Each language folder have the ***substitutions.json*** for substitutions for texts. This will be working when the input +is processed. + +substitutions file must have a dictionary with key value pairs of words and their alternatives. + +## Syntax + +gender: This contains male and female pronouns as key value pair. +person: This contains the subject pronouns. +person2: This contains the subject pronoun as key and object pronoun as value +normal: short word as a key their full-form as value +reflections: reflections of same sentence + +{ +"gender":{"he":"she", "him":"her"}, +"person":{"I": "he","she": "I"}, +"person2":{ "I": "you","me": "you",}, +"normal":{ "wanna": "want to","gonna": "going to",}, +"reflections":{ "i am": "you are","i was": "you were",} } + + +## Example of English +Here we have full list of english language substitutions +` +{ +"gender": { +"he": "she", +"him": "her", +"his": "her", +"himself": "herself", +"she": "he", +"her": "him", +"hers": "his", +"herself": "himself" +}, +"person": { +"I": "he", +"me": "him", +"my": "his", +"mine": "his", +"myself": "himself", +"he": "I", +"him": "me", +"his": "my", +"himself": "myself", +"she": "I", +"her": "me", +"hers": "mine", +"herself": "myself" +}, +"person2": { +"I": "you", +"me": "you", +"my": "your", +"mine": "yours", +"myself": "yourself", +"you": "me", +"your": "my", +"yours": "mine", +"yourself": "myself" +}, +"normal": { +"wanna": "want to", +"gonna": "going to", +"yha": "yes", +"aye": "yes", +"yep": "yes", +"yea": "yes", +"affirmative": "yes", +"roger": "yes", +"uh-huh": "yes", +"righto": "yes", +"yuppers": "yes", +"yup": "yes", +"ja": "yes", +"surely": "yes", +"amen": "yes", +"totally": "yes", +"sure": "yes", +"yessir": "yes", +"okey-dokey": "yes", +"uh-uh": "no", +"nix": "no", +"nixie": "no", +"nixy": "no", +"nixey": "no", +"nope": "no", +"nay": "no", +"nah": "no", +"negative": "no", +"veto": "no", +"none": "no", +"ok": "okay", +"o.k": "okay", +"okey": "okay", +"I'm": "I am", +"I'd": "I would", +"I'll": "I will", +"I've": "I have", +"you'd": "you would", +"you're": "you are", +"you've": "you have", +"you'll": "you will", +"he's": "he is", +"he'd": "he would", +"he'll": "he will", +"she's": "she is", +"she'd": "she would", +"she'll": "she will", +"we're": "we are", +"we'd": "we would", +"we'll": "we will", +"we've": "we have", +"they're": "they are", +"they'd": "they would", +"they'll": "they will", +"they've": "they have", +"y'all": "you all", +"can't": "can not", +"cannot": "can not", +"couldn't": "could not", +"wouldn't": "would not", +"shouldn't": "should not", +"isn't": "is not", +"ain't": "is not", +"don't": "do not", +"aren't": "are not", +"won't": "will not", +"weren't": "were not", +"wasn't": "was not", +"didn't": "did not", +"hasn't": "has not", +"hadn't": "had not", +"haven't": "have not", +"where's": "where is", +"where'd": "where did", +"where'll": "where will", +"who's": "who is", +"who'd": "who did", +"who'll": "who will", +"what's": "what is", +"whats": "what is", +"what'd": "what did", +"what'll": "what will", +"when's": "when is", +"when'd": "when did", +"when'll": "when will", +"why's": "why is", +"why'd": "why did", +"why'll": "why will", +"it's": "it is", +"it'd": "it would", +"it'll": "it will" +}, +"reflections": { +"i am": "you are", +"i was": "you were", +"i": "you", +"i'm": "you are", +"i'd": "you would", +"i've": "you have", +"i'll": "you will", +"my": "your", +"you are": "I am", +"you were": "I was", +"you've": "I have", +"you'll": "I will", +"your": "my", +"yours": "mine", +"you": "me", +"me": "you" +} +} + +` diff --git a/docs/source/recursion.md b/docs/source/recursion.md index aad66ff7..9a6b3221 100644 --- a/docs/source/recursion.md +++ b/docs/source/recursion.md @@ -1,7 +1,8 @@ # Recursion > Recursion gives the recursive multiple response to the user query. -> It picks the particular block matches with the user query and return the response on that block -> Whichever block or prev tag matched to user input will be picked for the response. +> It picks the particular block matches with the user query and return the response to that block +> Whichever block or prev tag matched to user input first will be picked for the response. +> If none matched response will be from the general block. ## Syntax ``` @@ -22,34 +23,31 @@ or `prev` tag matched with the `Please` Below sample, we are having the user input matching template and matched response template 1. user input block - - -When user type `Please` with some text we have the response as `chat` this will be matched with the other blocks + When user type `Please` with some text we have the response as `chat` this will be matched with the other blocks with the matching user input. -``` -{% block %} -{% client %}Please (.*){% endclient %} -{% response %}{% chat %1 %}{% endresponse %} -{% endblock %} -``` - -1. response block - -Sample response for the above user input. - -``` -{% response %}Please tell me more.{% endresponse %} -{% response %}Let's change focus a bit... Tell me about your family.{% endresponse %} -{% response %}Can you elaborate on that?{% endresponse %} -{% response %}Why do you say that %0?{% endresponse %} -{% response %}I see.{% endresponse %} -{% response %}Very interesting.{% endresponse %} -{% response %}%0.{% endresponse %} -{% response %}I see. And what does that tell you?{% endresponse %} -{% response %}How does that make you feel?{% endresponse %} -{% response %}How do you feel when you say that?{% endresponse %} -``` + ``` + {% block %} + {% client %}Please (.*){% endclient %} + {% response %}{% chat %1 %}{% endresponse %} + {% endblock %} + ``` +2. response block + + Sample response for the above user input. + + ``` + {% response %}Please tell me more.{% endresponse %} + {% response %}Let's change focus a bit... Tell me about your family.{% endresponse %} + {% response %}Can you elaborate on that?{% endresponse %} + {% response %}Why do you say that %0?{% endresponse %} + {% response %}I see.{% endresponse %} + {% response %}Very interesting.{% endresponse %} + {% response %}%0.{% endresponse %} + {% response %}I see. And what does that tell you?{% endresponse %} + {% response %}How does that make you feel?{% endresponse %} + {% response %}How do you feel when you say that?{% endresponse %} + ``` #### Chat Sample Below example when user type `Please reply` diff --git a/docs/source/template_tags.md b/docs/source/template_tags.md index e04fc044..f14eb8f6 100644 --- a/docs/source/template_tags.md +++ b/docs/source/template_tags.md @@ -1,23 +1,53 @@ # Template Tags -> Template tags are generating the chat response. -> These are like web template system similar to JINJA template with following tags Group, Block, Client, Response and Learn Tags + +> Template tags are used to generating the chat response. These are like web template system similar to JINJA template. +> Currently we have two type of tags Block tags and General tags. Each of them have different type of tags list. + +> Block tags are block,learn,response,client,prev,group. + +> General tags are if,endif,elif,else,chat,low,up,cap,call,topic + +> Both tags have their own handler methods to process the template. +> Some tags are exceptional doesn't have endtag like call, low, up. + +## syntax + +Each tag must be inside this `{% %}` with single space before and after tagname. +It must have the endtag except some exceptional tags. + +``` +{% tagname %} ... {% endtagname %} +``` + +### Example + +Sample block tag. + +``` +{% block %} ... {% endblock %} +``` ## Group Tags -Group tag are used to group the response to particular topic. Each group can have multiple subgroups group. -If we didn't create block inside the group or group without topic name those will be a generic group. -Sub-group response will be checked and return from the current block, parent block and generic block. +Group template tag keyword is `group`. +It is a combination of multiple block tags or response tags into a single group. + +Each group can have multiple sub-groups, client, response and learn tags. If we didn't +create block inside the group or group without topic name those will be a generic group. + +Response of sub-groups order will be current block, parent block and generic block. +> Group tag must have at-least one of these tags block, group or response tag. ### Template Syntax + ``` -{% group topicname %} - ... -{% endgroup %} +{% group topicname %}...{% endgroup %} ``` #### Generic Group sample Template -Below Template sample we have the general group. -`{% group %}` group tag doesn't have the name + +Below Template sample is a general group. +`{% group %}` group tag without the topicname. ``` {% group %} @@ -29,9 +59,11 @@ Below Template sample we have the general group. ``` ### Named group sample Template -Below template is sample for named group and nested group. -We have the name `{% group continent %}` inside the group tag. So this will be under the continent group. +Below template is named group sample `continent` is a group name. +It's also a nested group. + +We have the name `{% group continent %}` after the group keyword. ``` {% group continent %} @@ -47,19 +79,38 @@ We have the name `{% group continent %}` inside the group tag. So this will be u {% endgroup %} ``` +## Block Tags + +Block template tag keyword is `block`. +This used to separate the client query and response inside the particular group. +> Block tag must have at-least one of these tags client, learn, response or prev. -## Block Tags -Block holds each client query and response details. Every block must have the at-least one client block. +Example, Asia continent group we can have population and country query in different blocks. + +``` +{% group asia%} + {% block %} + {% client %}What is the population{% endclient %} + {% response %}100000{% endresponse %} + {% endblock %} + {% block %} + {% client %}How many countries?{% endclient %} + {% response %}100{% endresponse %} + {% endblock %} +{% endgroup %} +``` ### Template Syntax + ``` -{% block %} -... -{% endblock %} +{% block %}.*{% endblock %} ``` + ### Template sample + Sample block tag with at-least single client tag. + ``` {% block %} {% client %}(what is|(do you remember|tell me) about) (?P.*){% endclient %} @@ -67,18 +118,24 @@ Sample block tag with at-least single client tag. ``` ## Client Tags -Client tag is user input handler. We can have static, dynamic and multiple user input inside this tag. -Dynamic text we are handling with python [regex](https://docs.python.org/3/howto/regex.html). -Multiple user input we need to user `|` operator. + +Client template tag keyword is `block`. +This is used as a user input matcher. +It can have static, dynamic and multiple user inputs matches inside this tag. + +Multiple user input we need to user `|`,`&` operator [regex](https://docs.python.org/3/howto/regex.html) +used to find the user inputs matching. ### Template Syntax + ``` -{% client %} -{% endclient %} +{% client %}...{% endclient %} ``` ### Static input sample -Below sample, we are handing the static user input. + +Below sample, we are handing the static user input. Giving response +to the client query. ``` {% block %} @@ -88,29 +145,37 @@ Below sample, we are handing the static user input. ``` #### Chat sample + ``` > What is your name My name is ChatBot ``` ### Dynamic input sample -`(?P.*)` is the regex based input and stores the input in the variable `name`. + +`(?P.*)` is the regex based input and stores the input in the variable `name`. Later we have display this to user or store in memory + ``` {% block %} {% client %} show (?P.*){% endclient %} {% response %}%name{% endresponse%} {% endblock %} ``` + #### Chat sample + Below example takes the random input from user. ``` > show Age Age ``` -### Multiple input Template + +### Multiple input sample + Using the `|` we can take multiple inputs + ``` {% block %} {% client %}(what is|(do you remember|tell me) about) (?P.*){% endclient %} @@ -119,96 +184,89 @@ Using the `|` we can take multiple inputs ``` #### Chat Sample + Below sample, we can return same response to multiple user queries -1. + +1. + ``` > What is about sun sun ``` -2. + +2. + ``` > do you remember about sun sun ``` ## Response Tags -Response tag used for give response to user queries. -we can have the static response, dynamic response or calling a function. +Response template tag keyword is `response`. +This is used for giving response to user queries. + +It can have the static response, dynamic response or calling a function. ### Template Syntax + ``` -{% response %} -... -{% endresponse %} +{% response %}...{% endresponse %} ``` ### Dynamic response Template -Below sample have the random response for the single client +Below sample have the random response for the single user input. + +Block tag have the single client tag and multiple response tags. +Response tag are chosen randomly. ``` {% block %} - {% client %}I need (.*){% endclient %} - {% response %}Why do you need %1?{% endresponse %} - {% response %}Would it really help you to get %1?{% endresponse %} - {% response %}Are you sure you need %1?{% endresponse %} + {% client %}I need (.*){% endclient %} + {% response %}Why do you need %1?{% endresponse %} + {% response %}Would it really help you to get %1?{% endresponse %} + {% response %}Are you sure you need %1?{% endresponse %} {% endblock %} ``` + #### Chat Sample -When, user enter the sample query multiple times we get the random response. -1. +When, user enter the sample query multiple times we get the random response from the current block. + +1. + ``` >I Need something Why do you need something ``` -2. +2. + ``` >I Need something Are you sure you need something ``` -### Function call sample Template +## Previous Tags -`call` is used for calling custom function which is having the logic to handle the use inputs. +Previous template tag keyword is `prev`. +Previous tag used to give the auto response based on the previous input from the user. -Below is the sample of increment_count function. This will take the variable and increase it count on memory +It must me inside the block tag also must have at-least one client tag inside this block. + +### Syntax ``` {% block %} - {% client %}increment (?P.*){% endclient %} - {% response %}{% call increment_count: %name %}{% endresponse %} + {% client %} ... {% endclient %} + {% prev %}{% endprev %} {% endblock %} ``` -#### Chat Sample -Below sample Age variable value is increased in the memory. - -1. -``` -> increment Age -Age 1 -``` -2. -``` -increment Age -Age 2 -``` - - - -## Previous Tags -Previous tag used to give the auto response for the chat query from the Chatbot. +### Sample Template -### Template Syntax -``` -{% prev %} -{% endprev %} -``` - -### Sample previous Template If Chatbot ask query `how are you` and user text can be `I feel/am feeling/ am absolutely good`. -we are matching the current user input with the previous text and returning the response. +We are matching the current user input with the previous text and returning the response. + ``` {% block %} {% client %}(I (am|feel) )?(feeling )?(absolutely )?(.*){% endclient %} @@ -216,13 +274,13 @@ we are matching the current user input with the previous text and returning the {% response %}{% if {%low %5 %} == fine | {%low %5 %} == good | {%low %5 %} == happy %} Nice to know that you are %5. What else? {% else %} why you feel %5 {% endif %}{% endresponse %} {% endblock %} ``` -#### Chat Sample + +#### Chat Sample + Below Sample, we have the auto query from bot `Hi, how are you?`. -If user enter `I feel good` or in the pattern inside the client tag. -They get the response based on the input in this sample we have input `good`. -so we get response `Nice to know that you are good`. -Other than fine, good, happy response will be `why you feel ` +If user enter `I feel good` or in the pattern inside the above sample template client tag. +It matched the response condition now we get the response `Nice to know that you are good`. ``` Hi, how are you? @@ -232,18 +290,38 @@ Hi, how are you? ``` ## Learn Tags -Learn tag is dynamic object and description without static object and response. + +Learn template tag keyword is `learn`. +It is used to give dynamic object and description without static object and response. + +It must me inside the block tag also must have at-least one client tag +either inside the learn tag block or on outer block tag. ### Syntax + +It must have the group and block tag inside it. All the rules and features for group and block +tags applies for these inner tag as well. + ``` -{% learn %} -{% endlearn %} +{% block %} + {% client %}...{% endclient %} + {% learn %} + {% group %} + {% block %} + .... + {% endblock %} + {% endgroup %} + {% endlearn %} +{% endblock %} ``` + ### Learn tag template sample + Below example, we do not have the static variable and value `(?P.*) ` is variable take any input , their value must start with is/are `(?P(is|are) .*)`. -Above mention object and description is added inside the learn tag with client query and response for the user next response. +Above mention object and description is added inside the learn tag with client query and response for the user next +response. ``` {% block %} @@ -260,17 +338,77 @@ Above mention object and description is added inside the learn tag with client q {% endblock %} ``` -###Chat Sample to get the value from learn tag +### Chat Sample to get the value from learn tag -remember is the client static input and `learn` is a variable startswith `is` and other description. +Below sample `remember` is the client static input and `learn` is a variable startswith `is` and other description. This will be added to memory. + ``` > remember learn is tag give dynamic response I will remember that learn is tag give dynamic response ``` -We can return the description response when the query raised from user `tell me about learn`/ +We can return the description response when the query raised from user `tell me about learn`. + ``` > tell me about learn learn is tag give dynamic response ``` + +## Call tag + +Call template tag keyword is `call`. +It is used for calling custom function which is having the logic to handle the use inputs. + +Function needs to be registered the before calling it. Each function name must be unique. +`register_call` decorator will check the unique function name +and append the current function to the session + +### Sample code to register a function: + +``` +from chatbot import register_call + +@register_call("increment_count") +def memory_get_set_example(session, query): + name=query.strip().lower() + # Get memory + old_count = session.memory.get(name, '0') + new_count = int(old_count) + 1 + # Set memory + session.memory[name]=str(new_count) + return f"count {new_count}" + +``` + +### Syntax +``` +{% call function_name %} +``` + +### Template Sample + +Below is the sample of `increment_count` function. +This will take the variable and increase it count on memory + +``` +{% block %} + {% client %}increment (?P.*){% endclient %} + {% response %}{% call increment_count: %name %}{% endresponse %} +{% endblock %} +``` + +#### Chat Sample + +Below sample Age variable value is increased in the memory. + +1. + ``` + > increment Age + Age 1 + ``` +2. + ``` + increment Age + Age 2 + ``` \ No newline at end of file From ca1c96a2edff6ff912a21dd87123cc63344e6b64 Mon Sep 17 00:00:00 2001 From: arunkumarp93 Date: Thu, 21 Oct 2021 14:28:31 +0530 Subject: [PATCH 4/5] Fix docs --- docs/source/recursion.md | 73 ------------------------------------ docs/source/template_tags.md | 7 ++-- setup.py | 4 +- 3 files changed, 6 insertions(+), 78 deletions(-) diff --git a/docs/source/recursion.md b/docs/source/recursion.md index 9a6b3221..c6da42b4 100644 --- a/docs/source/recursion.md +++ b/docs/source/recursion.md @@ -1,74 +1 @@ # Recursion -> Recursion gives the recursive multiple response to the user query. -> It picks the particular block matches with the user query and return the response to that block -> Whichever block or prev tag matched to user input first will be picked for the response. -> If none matched response will be from the general block. - -## Syntax -``` -{% chat statement %} -``` - -### Template Sample -`Please (.*)` is the user query matching text. `chat` will match the specific `block` -or `prev` tag matched with the `Please` -``` -{% block %} -{% client %}Please (.*){% endclient %} -{% response %}{% chat %1 %}{% endresponse %} -{% endblock %} -``` - -#### Full Template sample -Below sample, we are having the user input matching template and matched response template - -1. user input block - When user type `Please` with some text we have the response as `chat` this will be matched with the other blocks -with the matching user input. - - ``` - {% block %} - {% client %}Please (.*){% endclient %} - {% response %}{% chat %1 %}{% endresponse %} - {% endblock %} - ``` -2. response block - - Sample response for the above user input. - - ``` - {% response %}Please tell me more.{% endresponse %} - {% response %}Let's change focus a bit... Tell me about your family.{% endresponse %} - {% response %}Can you elaborate on that?{% endresponse %} - {% response %}Why do you say that %0?{% endresponse %} - {% response %}I see.{% endresponse %} - {% response %}Very interesting.{% endresponse %} - {% response %}%0.{% endresponse %} - {% response %}I see. And what does that tell you?{% endresponse %} - {% response %}How does that make you feel?{% endresponse %} - {% response %}How do you feel when you say that?{% endresponse %} - ``` - -#### Chat Sample -Below example when user type `Please reply` -now chatbot will match this to recursive response as `Very interesting.` -that we have in above full template sample. - -If we ask the sample question we get the different response from first matched block. - -One of our response have `{% response %}%0.{% endresponse %}` -so we are getting the user input value as response in the last chat sample - - - -``` -> Please reply -Very interesting. - -> Please do something -How do you feel when you say that? - -> Please help to get the info -help to get the info . - -``` \ No newline at end of file diff --git a/docs/source/template_tags.md b/docs/source/template_tags.md index f14eb8f6..375d427a 100644 --- a/docs/source/template_tags.md +++ b/docs/source/template_tags.md @@ -249,16 +249,17 @@ Are you sure you need something ## Previous Tags Previous template tag keyword is `prev`. -Previous tag used to give the auto response based on the previous input from the user. +Previous tag used to match previous bot message. +A regex pattern is added between {% prev %} and {% endprev %}. -It must me inside the block tag also must have at-least one client tag inside this block. +It must be inside the block tag also must have at-least one client tag inside this block. ### Syntax ``` {% block %} {% client %} ... {% endclient %} - {% prev %}{% endprev %} + {% prev %}Regex Pattern{% endprev %} {% endblock %} ``` diff --git a/setup.py b/setup.py index c1804069..7268587d 100755 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup -version = __import__('chatbot.version').__version__ +version = run_path(join(abspath(dirname(__file__)), 'chatbot', 'version.py')) constants = run_path(join(abspath(dirname(__file__)), 'chatbot', 'constants.py')) LANGUAGE_SUPPORT = constants['LANGUAGE_SUPPORT'] @@ -21,7 +21,7 @@ ]) setup( name=constants['NAME'], - version=version, + version=version['__version__'], author=constants['AUTHOR'], author_email=constants['AUTHOR_EMAIL'], url=constants['URL'], From 79f2d8c69856b9aa677bb79195ccd054c53ea428 Mon Sep 17 00:00:00 2001 From: arunkumarp93 Date: Sun, 14 Nov 2021 19:51:22 +0530 Subject: [PATCH 5/5] Update recursion docs --- docs/source/language_and_template.md | 2 + docs/source/recursion.md | 108 +++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/docs/source/language_and_template.md b/docs/source/language_and_template.md index cd209ac6..d9dabc6f 100644 --- a/docs/source/language_and_template.md +++ b/docs/source/language_and_template.md @@ -20,6 +20,8 @@ iso code for english is `en`. So Folder name must be **en**. Inside this folder Spellcheck we have ***words.txt*** file. If the word is more than three character then it'll be compare inside the words.txt file for corrections. +Spellchecker works during the user input process. Typos will be autocorrected before matching it with template. + Each language folder have the ***substitutions.json*** for substitutions for texts. This will be working when the input is processed. diff --git a/docs/source/recursion.md b/docs/source/recursion.md index c6da42b4..50b3068c 100644 --- a/docs/source/recursion.md +++ b/docs/source/recursion.md @@ -1 +1,109 @@ # Recursion +Chat tag `{% chat %}` is used to handle the recursion. +It must be inside the response tag. + +## Syntax + +Chat tag can be added along with other responses. + +``` +{% response %}... {% chat ... %} ... {% endresponse %} +``` + +## Some UseCases +Recursion is useful in scenarios like handling user input implicitly. + +i) Removing or ignoring repeated words in the Inputs +ii) sending multiple response + +### Remove repeated inputs +Sometimes user accidentally enter the same text multiple times. +This is unwanted chat need only regex pattern matched text. +Here chat remove the unwanted text recursively ( number of the times text appears ). + +#### Template Example +Below sample, `{% chat %1 %}` tag is added. +This the regex pattern `%1` matches the first value in the client tag that's `Please`. +Now this text will be ignored recursively and final response will be based on user entered text. + +``` +{% block %} + {% client %}Please (.*){% endclient %} + {% response %}{% chat %1 %}{% endresponse %} +{% endblock %} +``` + + +#### Chat Example + +Multiple **please** word is entered in the chat those are removed implicitly and user input +will be taken for response. These responses are based on `default.template`. + +1. + +Below sample `check` is taken as user input and matched with template and return the response. + +``` +> please please check +> why you feel check this +``` + +2. +Below sample `note this` is taken as user input and matched with template and return the response. + +``` +> please please note this +> note this . +``` + +### Send the Multiple chat response to user +If user enter multiple queries in a chat, they need answers for both. + +Instance like **tell me about apples and mango?** +we need to give answer for both apple and mango. + +#### Template Sample +``` +{% block %} + {% client %}(Do you know about|what is|who is|tell me about) (?P.*)(,| and | \& )(?P.*){% endclient %} + {% response %} + {% chat %1 %query1 %} + ------------------------------------ + {% chat %1 %query2 %} + {% endresponse %} +{% endblock %} +{% endblock %} +``` + +Above sample, we have regex pattern match `(?P.*)(,| and | \& )(?P.*)`. +This will match the user input in two variable *query1* , *query2* and then create two different +chat queries. `, and &` these will separate the two inputs `(,| and | \& )`. + +If user enter below query +> `Do you know about Python and C` + +This will be converted to two chat responses by template itself. + +``` +{% chat Do you know about Python %} +----------------------------------- +{% chat Do you know about c %} +``` + +Now this query will be matched with other template and get the response. + +#### Chat Sample + +Below chat get the response from wikipedia after converting to two different chat each of them will be matched in other template +that will call the wikipedia and return the response. + +This operation occurs recursively and get the response from wiki. Both query responses return after fetching the information. + +``` +> Do you know about Python and C + +> Python is an interpreted high-level general-purpose programming language. + Its design philosophy emphasizes code readability with its use of significant indentation..... +> ----------------------------------------- +> C is a general-purpose, procedural computer programming language supporting structured programming, + lexical variable scope, and recursion, with a static type system......