├── .dockerignore ├── .gitignore ├── .python-version ├── .travis.yml ├── Dockerfile ├── License.txt ├── Procfile ├── README.md ├── app.json ├── app ├── .gitignore ├── __init__.py ├── agents │ ├── __init__.py │ ├── controllers.py │ └── models.py ├── commons │ ├── .gitignore │ ├── __init__.py │ ├── build_response.py │ ├── error_codes.py │ ├── logger.py │ └── utils.py ├── endpoint │ ├── __init__.py │ ├── controllers.py │ └── utils.py ├── entities │ ├── __init__.py │ ├── controllers.py │ └── models.py ├── intents │ ├── __init__.py │ ├── controllers.py │ └── models.py ├── nlu │ ├── .gitignore │ ├── __init__.py │ ├── classifiers │ │ ├── __init__.py │ │ ├── sklearn_intent_classifer.py │ │ └── tf_intent_classifer.py │ ├── controllers.py │ ├── entity_extractor.py │ └── tasks.py └── train │ ├── __init__.py │ └── controllers.py ├── config.py ├── docker-compose.yml ├── examples ├── default_intents.json ├── nodejs │ └── discordRequest.js ├── python │ └── app.py ├── restaurant_search.json └── ruby │ └── request.rb ├── frontend ├── .angular-cli.json ├── .dockerignore ├── .editorconfig ├── .gitignore ├── Dockerfile ├── README.md ├── e2e │ ├── app.e2e-spec.ts │ ├── app.po.ts │ └── tsconfig.e2e.json ├── karma.conf.js ├── nginx │ └── default.conf ├── package-lock.json ├── package.json ├── protractor.conf.js ├── src │ ├── app │ │ ├── agent │ │ │ ├── agent-routing.module.ts │ │ │ ├── agent.module.ts │ │ │ ├── chat │ │ │ │ ├── chat.component.html │ │ │ │ ├── chat.component.scss │ │ │ │ └── chat.component.ts │ │ │ ├── entities │ │ │ │ ├── entities.component.html │ │ │ │ ├── entities.component.scss │ │ │ │ ├── entities.component.spec.ts │ │ │ │ └── entities.component.ts │ │ │ ├── entity │ │ │ │ ├── entity.component.html │ │ │ │ ├── entity.component.scss │ │ │ │ ├── entity.component.spec.ts │ │ │ │ └── entity.component.ts │ │ │ ├── intent │ │ │ │ ├── intent.component.html │ │ │ │ ├── intent.component.scss │ │ │ │ └── intent.component.ts │ │ │ ├── intents │ │ │ │ ├── intents.component.html │ │ │ │ ├── intents.component.scss │ │ │ │ └── intents.component.ts │ │ │ ├── settings │ │ │ │ ├── settings.component.html │ │ │ │ ├── settings.component.scss │ │ │ │ ├── settings.component.spec.ts │ │ │ │ └── settings.component.ts │ │ │ └── train │ │ │ │ ├── train.component.html │ │ │ │ ├── train.component.scss │ │ │ │ └── train.component.ts │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── commons │ │ │ └── commons.module.ts │ │ ├── dashboard │ │ │ ├── dashboard.module.ts │ │ │ ├── layout │ │ │ │ ├── layout.component.html │ │ │ │ ├── layout.component.scss │ │ │ │ ├── layout.component.spec.ts │ │ │ │ └── layout.component.ts │ │ │ └── sidebar │ │ │ │ ├── sidebar.component.html │ │ │ │ ├── sidebar.component.scss │ │ │ │ ├── sidebar.component.spec.ts │ │ │ │ └── sidebar.component.ts │ │ ├── directives │ │ │ ├── autofocus.directive.spec.ts │ │ │ └── autofocus.directive.ts │ │ └── services │ │ │ ├── agents.service.ts │ │ │ ├── chat.service.ts │ │ │ ├── entities.service.ts │ │ │ ├── intent-resolver.service.ts │ │ │ ├── intent.service.ts │ │ │ ├── training.service.ts │ │ │ └── utils.service.ts │ ├── assets │ │ ├── .gitkeep │ │ ├── images │ │ │ └── iky-logo.png │ │ └── widget │ │ │ ├── iky_widget.js │ │ │ └── style.css │ ├── environments │ │ ├── environment.docker.ts │ │ ├── environment.prod.ts │ │ ├── environment.test.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── main.ts │ ├── polyfills.ts │ ├── styles.scss │ ├── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ └── typings.d.ts ├── tsconfig.json └── tslint.json ├── helm └── ai-chatbot-framework │ ├── .gitignore │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ ├── _helpers.tpl │ ├── deployment-backend.yaml │ ├── deployment-frontend.yaml │ ├── ingress.yaml │ └── service.yaml │ └── values.yaml ├── logs └── .keep ├── manage.py ├── model_files └── .gitignore ├── requirements.txt ├── run.py ├── swagger.yaml └── tests ├── __init__.py └── test_nlu.py /.dockerignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | logs/* 3 | !logs/.keep 4 | *.pyc 5 | venv/ 6 | env/ 7 | .DS_Store 8 | model_files/*.model 9 | frontend/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | logs/* 3 | !logs/.keep 4 | *.pyc 5 | *.pyo 6 | venv/ 7 | env/ 8 | .DS_Store 9 | .pytest_cache/ 10 | .vscode -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.9.6 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.9.6" 4 | install: 5 | - pip install -r requirements.txt 6 | script: 7 | - pytest 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.6-slim 2 | 3 | # Install common libraries 4 | RUN apt-get update -qq \ 5 | && apt-get install -y --no-install-recommends build-essential && apt-get autoremove -y 6 | 7 | WORKDIR /usr/src/app 8 | 9 | COPY requirements.txt ./ 10 | RUN pip install --no-cache-dir -r requirements.txt 11 | 12 | EXPOSE 80 13 | 14 | COPY . . 15 | 16 | CMD ["gunicorn", "run:app" ,"--log-level=debug", "--timeout", "90","--bind", "0.0.0.0:80" ] 17 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alfred Francis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | release: APPLICATION_ENV="Heroku" python manage.py migrate 2 | web: APPLICATION_ENV="Heroku" gunicorn -k gevent run:app 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | [![Join the chat at https://gitter.im/ai-chatbot-framework/Lobby](https://badges.gitter.im/ai-chatbot-framework/Lobby.svg)](https://gitter.im/ai-chatbot-framework/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.com/alfredfrancis/ai-chatbot-framework.svg?branch=master)](https://travis-ci.com/alfredfrancis/ai-chatbot-framework.svg?branch=master) 4 | 5 | 6 | 7 | ### An AI Chatbot framework built in Python 8 | 9 | Building a chatbot can sound daunting, but it’s totally doable. AI Chatbot Framework is an AI powered conversational dialog interface built in Python. With this tool, it’s easy to create Natural Language conversational scenarios with no coding efforts whatsoever. The smooth UI makes it effortless to create and train conversations to the bot and it continuously gets smarter as it learns from conversations it has with people. AI Chatbot Framework can live on any channel of your choice (such as Messenger, Slack etc.) by integrating it’s API with that platform. 10 | 11 | You don’t need to be an expert at artificial intelligence to create an awesome chatbot that has AI capabilities. With this boilerplate project you can create an AI powered chatting machine in no time.There may be scores of bugs. So feel free to contribute via pull requests. 12 | 13 | ![](https://image.ibb.co/eMJ9Wx/Screen_Shot_2018_04_28_at_1_45_28_PM.png) 14 | 15 | ### Installation 16 | 17 | ### Using docker-compose 18 | ```sh 19 | docker-compose up -d 20 | ``` 21 | 22 | ### Using Helm 23 | 24 | ```sh 25 | helm dep update helm/ai-chatbot-framework 26 | 27 | helm upgrade --install --create-namespace -n ai-chatbot-framework ai-chatbot-framework helm/ai-chatbot-framework 28 | 29 | # port forward for local installation 30 | kubectl port-forward --namespace=ai-chatbot-framework service/ingress-nginx-controller 8080:80 31 | ``` 32 | 33 | ### Using Docker 34 | ```sh 35 | 36 | # pull docker images 37 | docker pull alfredfrancis/ai-chatbot-framework_backend:latest 38 | docker pull alfredfrancis/ai-chatbot-framework_frontend:latest 39 | 40 | # start a mongodb server 41 | docker run --name mongodb -d mongo:3.6 42 | 43 | # start iky backend 44 | docker run -d --name=iky_backend --link mongodb:mongodb -e="APPLICATION_ENV=Production" alfredfrancis/ai-chatbot-framework_backend:latest 45 | 46 | # setup default intents 47 | docker exec -it iky_backend python manage.py migrate 48 | 49 | # start iky gateway with frontend 50 | docker run -d --name=iky_gateway --link iky_backend:iky_backend -p 8080:80 alfredfrancis/ai-chatbot-framework_frontend:latest 51 | 52 | ``` 53 | 54 | ### without docker 55 | 56 | * Setup Virtualenv and install python requirements 57 | ```sh 58 | virtualenv -p python3 venv 59 | source venv/bin/activate 60 | pip install -r requirements.txt 61 | python manage.py migrate 62 | python run.py 63 | ``` 64 | * Production 65 | ```sh 66 | APPLICATION_ENV="Production" gunicorn -k gevent --bind 0.0.0.0:8080 run:app 67 | ``` 68 | * Open http://localhost:8080/ 69 | 70 | #### Update Frontend Dist 71 | * Run Development mode 72 | ```sh 73 | cd frontend 74 | npm install 75 | ng serve 76 | ``` 77 | * Take Production build 78 | ```sh 79 | cd frontend 80 | ng build --prod --optimize 81 | ``` 82 | 83 | ### Heroku 84 | [![Deploy](https://www.herokucdn.com/deploy/button.png)](https://heroku.com/deploy) 85 | 86 | * add your dev/production configurations in config.py 87 | 88 | ### DB 89 | 90 | #### Restore 91 | You can import some default intents using following steps 92 | 93 | - goto http://localhost:8080/agent/default/settings 94 | - click 'choose file' 95 | - choose 'examples/default_intents.json file' 96 | - click import 97 | 98 | ### Screenshots 99 | 100 | ![](https://image.ibb.co/i9ReWx/Screen_Shot_2018_04_28_at_1_38_15_PM.png) 101 | --- 102 | ![](https://image.ibb.co/ivXKWx/Screen_Shot_2018_04_28_at_1_38_36_PM.png) 103 | --- 104 | ![](https://image.ibb.co/nf9Bdc/Screen_Shot_2018_04_28_at_1_38_57_PM.png) 105 | --- 106 | ![](https://image.ibb.co/b4q1dc/Screen_Shot_2018_04_28_at_1_43_06_PM.png) 107 | ### Tutorial 108 | 109 | Checkout this basic tutorial on youtube, 110 | 111 | [![IMAGE ALT TEXT HERE](https://preview.ibb.co/fj9N3v/Screenshot_from_2017_04_05_03_11_04.png)](https://www.youtube.com/watch?v=S1Fj7WinaBA) 112 | 113 | 114 | Watch tutorial on [Fullfilling your Chatbot Intent with an API Call - Recipe Search Bot](https://www.youtube.com/watch?v=gqO69ojLobQ) 115 | 116 | ### Todos 117 | * Write Unit Tests 118 | * Multilingual Intent Classifier 119 | * PyCRFSuite to sklearn-crfsuite migration 120 | * Support follow up conversations 121 | 122 | ### Dependencies documentations 123 | * [SKLearn documentation](http://scikit-learn.org/) 124 | * [CRFsuite documentation](http://www.chokkan.org/software/crfsuite/) 125 | * [python CRfSuite](https://python-crfsuite.readthedocs.io/en/latest/) 126 | 127 | **Free Software, Hell Yeah!** 128 |
129 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IKY - AI chatbot framework", 3 | "description": "A python chatbot framework with Natural Language Understanding and Artificial Intelligence.", 4 | "logo": "https://raw.githubusercontent.com/alfredfrancis/ai-chatbot-framework/master/app/static/images/iky.png", 5 | "image": "heroku/python", 6 | "repository": "https://github.com/alfredfrancis/ai-chatbot-framework/", 7 | "keywords": ["python", "flask","spacy","scikit-learn"], 8 | "addons": [ "mongolab" ], 9 | "buildpacks": [ 10 | { 11 | "url": "https://github.com/heroku/exec-buildpack" 12 | }, 13 | { 14 | "url": "heroku/python" 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc -------------------------------------------------------------------------------- /app/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | from flask import Flask,send_from_directory 3 | from flask_cors import CORS 4 | from flask_mongoengine import MongoEngine 5 | 6 | APP_ROOT = os.path.dirname(os.path.abspath(__file__ + "../../")) 7 | 8 | db = MongoEngine() 9 | 10 | spacy_tokenizer = None 11 | 12 | def create_app(env = 'Development'): 13 | app = Flask(__name__) 14 | CORS(app) 15 | # Configurations 16 | try: 17 | env = os.environ['APPLICATION_ENV'] 18 | except KeyError as e: 19 | app.logger.info('Unknown environment key, defaulting to Development') 20 | 21 | app.config.from_object('config.%s' % env) 22 | db.init_app(app) 23 | 24 | import spacy 25 | global spacy_tokenizer 26 | spacy_tokenizer = spacy.load(app.config["SPACY_LANG_MODEL"]) 27 | 28 | from app.agents.controllers import bots 29 | from app.nlu.controllers import nlu 30 | from app.intents.controllers import intents 31 | from app.train.controllers import train 32 | from app.endpoint.controllers import endpoint 33 | from app.entities.controllers import entities_blueprint 34 | 35 | app.register_blueprint(nlu) 36 | app.register_blueprint(intents) 37 | app.register_blueprint(train) 38 | app.register_blueprint(endpoint) 39 | app.register_blueprint(bots) 40 | app.register_blueprint(entities_blueprint) 41 | 42 | admin_panel_dist = os.path.join(APP_ROOT, 'frontend/dist/') 43 | 44 | @app.route('/ready') 45 | def ready(): 46 | return "ok",200 47 | 48 | @app.route('/', methods=['GET']) 49 | def static_proxy(path): 50 | return send_from_directory(admin_panel_dist, path) 51 | 52 | @app.route('/') 53 | def root(): 54 | print(admin_panel_dist) 55 | return send_from_directory(admin_panel_dist, 'index.html') 56 | 57 | @app.errorhandler(404) 58 | def not_found(error): 59 | return "Not found", 404 60 | 61 | from app.endpoint.controllers import update_model 62 | with app.app_context(): 63 | update_model() 64 | 65 | return app 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /app/agents/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/app/agents/__init__.py -------------------------------------------------------------------------------- /app/agents/controllers.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, request 2 | 3 | from app.agents.models import Bot 4 | from app.commons import build_response 5 | 6 | bots = Blueprint('bots_blueprint', __name__, 7 | url_prefix='/agents/') 8 | 9 | 10 | @bots.route('/config', methods=['PUT']) 11 | def set_config(bot_name): 12 | """ 13 | Read bot config 14 | :param bot_name: 15 | :return: 16 | """ 17 | 18 | content = request.get_json(silent=True) 19 | bot = Bot.objects.get(name=bot_name) 20 | bot.config = content 21 | bot.save() 22 | return build_response.sent_ok() 23 | 24 | 25 | @bots.route('/config', methods=['GET']) 26 | def get_config(bot_name): 27 | """ 28 | Update bot config 29 | :return: 30 | """ 31 | bot = Bot.objects.get(name=bot_name) 32 | 33 | return build_response.build_json(bot.config) 34 | -------------------------------------------------------------------------------- /app/agents/models.py: -------------------------------------------------------------------------------- 1 | from mongoengine.fields import DictField 2 | from mongoengine.fields import Document 3 | from mongoengine.fields import StringField 4 | 5 | 6 | class Bot(Document): 7 | name = StringField(max_length=100, required=True, unique=True) 8 | config = DictField(required=True, default={ 9 | "confidence_threshold": .50 10 | }) 11 | -------------------------------------------------------------------------------- /app/commons/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc -------------------------------------------------------------------------------- /app/commons/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/app/commons/__init__.py -------------------------------------------------------------------------------- /app/commons/build_response.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from flask import Response 4 | 5 | 6 | def build_json(result): 7 | return Response(response=json.dumps(result), 8 | status=200, 9 | mimetype="application/json") 10 | 11 | 12 | def sent_json(result): 13 | return Response(response=result, 14 | status=200, 15 | mimetype="application/json") 16 | 17 | 18 | def sent_ok(): 19 | return Response(response=json.dumps({"result": True}), 20 | status=200, 21 | mimetype="application/json") 22 | 23 | 24 | def sent_plain_text(result): 25 | return Response(response=result.strip(), status=200, mimetype="text") 26 | -------------------------------------------------------------------------------- /app/commons/error_codes.py: -------------------------------------------------------------------------------- 1 | emptyInput = {"errorCode": 601, "description": "empty input"} 2 | InvalidInput = {"errorCode": 602, "description": "Invalid input"} 3 | 4 | UnidentifiedIntent = { 5 | "errorCode": 701, 6 | "description": "Can't identify the intent"} 7 | NotEnoughData = { 8 | "errorCode": 702, 9 | "description": "Not enough Training Data. Please Add more intents"} 10 | 11 | UnableToextractentities = {"errorCode": 801, 12 | "description": "Unable extract entities"} 13 | -------------------------------------------------------------------------------- /app/commons/logger.py: -------------------------------------------------------------------------------- 1 | import logging.handlers 2 | 3 | import json_log_formatter 4 | 5 | formatter = json_log_formatter.JSONFormatter() 6 | 7 | logger = logging.getLogger("query") 8 | logger.setLevel(logging.INFO) 9 | 10 | stream_handler = logging.StreamHandler() 11 | stream_handler.setLevel(logging.INFO) 12 | stream_handler.setFormatter(formatter) 13 | 14 | log_file_path = "logs/query-log.json" 15 | file_handler = logging.handlers.TimedRotatingFileHandler( 16 | filename=log_file_path, when='midnight', backupCount=30) 17 | file_handler.setFormatter(formatter) 18 | file_handler.setLevel(logging.INFO) 19 | 20 | logger.addHandler(file_handler) 21 | logger.addHandler(stream_handler) 22 | -------------------------------------------------------------------------------- /app/commons/utils.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | import parsedatetime as pdt 4 | from mongoengine.fields import EmbeddedDocumentField 5 | from mongoengine.fields import EmbeddedDocumentListField 6 | from mongoengine.fields import GenericEmbeddedDocumentField 7 | from mongoengine.fields import GenericReferenceField 8 | from mongoengine.fields import ListField 9 | from mongoengine.fields import ReferenceField 10 | from mongoengine.fields import SortedListField 11 | 12 | 13 | def date_from_string(timeString): 14 | cal = pdt.Calendar() 15 | now = datetime.now() 16 | result = str(cal.parseDT(timeString.strip(), now)[0]) 17 | return result 18 | 19 | 20 | def update_document(document, data_dict): 21 | """ 22 | Recreate Document object from python dictionary 23 | :param document: 24 | :param data_dict: 25 | :return: 26 | """ 27 | 28 | def field_value(field, value): 29 | 30 | if field.__class__ in ( 31 | ListField, 32 | SortedListField, 33 | EmbeddedDocumentListField): 34 | return [ 35 | field_value(field.field, item) 36 | for item in value 37 | ] 38 | if field.__class__ in ( 39 | EmbeddedDocumentField, 40 | GenericEmbeddedDocumentField, 41 | ReferenceField, 42 | GenericReferenceField 43 | ): 44 | return field.document_type(**value) 45 | else: 46 | return value 47 | 48 | [setattr( 49 | document, key.replace("_id", "id"), 50 | field_value(document._fields[key.replace("_id", "id")], value) 51 | ) for key, value in data_dict.items()] 52 | 53 | return document 54 | 55 | 56 | def is_list_empty(inList): 57 | if isinstance(inList, list): # Is a list 58 | return all(map(is_list_empty, inList)) 59 | return False # Not a list 60 | -------------------------------------------------------------------------------- /app/endpoint/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/app/endpoint/__init__.py -------------------------------------------------------------------------------- /app/endpoint/controllers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import copy 3 | import json 4 | from flask import Blueprint, request, abort 5 | from jinja2 import Template 6 | from flask import current_app as app 7 | from app.agents.models import Bot 8 | from app.commons import build_response 9 | from app.endpoint.utils import SilentUndefined 10 | from app.endpoint.utils import call_api 11 | from app.endpoint.utils import get_synonyms 12 | from app.endpoint.utils import split_sentence 13 | from app.intents.models import Intent 14 | from app.nlu.classifiers.sklearn_intent_classifer import \ 15 | SklearnIntentClassifier 16 | from app.nlu.entity_extractor import EntityExtractor 17 | endpoint = Blueprint('api', __name__, url_prefix='/api') 18 | sentence_classifier = SklearnIntentClassifier() 19 | synonyms = None 20 | entity_extraction = None 21 | 22 | @endpoint.route('/v1', methods=['POST']) 23 | def api(): 24 | """ 25 | Endpoint to Converse with the Chatbot. 26 | Chat context is maintained by exchanging the payload between client and bot. 27 | :param json: 28 | :return json: 29 | """ 30 | request_json = request.get_json(silent=True) 31 | print(request_json) 32 | result_json = copy.deepcopy(request_json) 33 | 34 | if request_json: 35 | context = {"context": request_json["context"]} 36 | 37 | # check if input method is event or raw text 38 | if request_json.get("input","").startswith("/"): 39 | intent_id = request_json.get("input").split("/")[1] 40 | confidence = 1 41 | else: 42 | intent_id, confidence, suggestions = predict(request_json.get("input")) 43 | 44 | app.logger.info("intent_id => %s" % intent_id) 45 | intent = Intent.objects.get(intentId=intent_id) 46 | 47 | # set intent as fallback intent 48 | if intent is None: 49 | intent = Intent.objects.get(intentId=app.config["DEFAULT_FALLBACK_INTENT_NAME"]) 50 | 51 | parameters = [] 52 | if intent.parameters: 53 | parameters = intent.parameters 54 | result_json["extractedParameters"] = request_json.get("extractedParameters") or {} 55 | 56 | if ((request_json.get("complete") is None) or (request_json.get("complete") is True)): 57 | result_json["intent"] = { 58 | "object_id": str(intent.id), 59 | "confidence": confidence, 60 | "id": intent.intentId 61 | } 62 | 63 | if parameters: 64 | # Extract NER entities 65 | result_json["extractedParameters"].update(entity_extraction.predict( 66 | intent_id, request_json.get("input"))) 67 | 68 | missing_parameters = [] 69 | result_json["missingParameters"] = [] 70 | result_json["parameters"] = [] 71 | for parameter in parameters: 72 | result_json["parameters"].append({ 73 | "name": parameter.name, 74 | "type": parameter.type, 75 | "required": parameter.required 76 | }) 77 | 78 | if parameter.required: 79 | if parameter.name not in result_json["extractedParameters"].keys(): 80 | result_json["missingParameters"].append( 81 | parameter.name) 82 | missing_parameters.append(parameter) 83 | 84 | if missing_parameters: 85 | result_json["complete"] = False 86 | current_node = missing_parameters[0] 87 | result_json["currentNode"] = current_node["name"] 88 | result_json["speechResponse"] = split_sentence(current_node["prompt"]) 89 | else: 90 | result_json["complete"] = True 91 | context["parameters"] = result_json["extractedParameters"] 92 | else: 93 | result_json["complete"] = True 94 | 95 | elif request_json.get("complete") is False: 96 | if "cancel" not in intent.name: 97 | intent_id = request_json["intent"]["id"] 98 | app.logger.info(intent_id) 99 | intent = Intent.objects.get(intentId=intent_id) 100 | 101 | extracted_parameter = entity_extraction.replace_synonyms({ 102 | request_json.get("currentNode"): request_json.get("input") 103 | }) 104 | 105 | # replace synonyms for entity values 106 | result_json["extractedParameters"].update(extracted_parameter) 107 | 108 | result_json["missingParameters"].remove( 109 | request_json.get("currentNode")) 110 | 111 | if len(result_json["missingParameters"]) == 0: 112 | result_json["complete"] = True 113 | context = {"parameters": result_json["extractedParameters"], 114 | "context": request_json["context"]} 115 | else: 116 | missing_parameter = result_json["missingParameters"][0] 117 | result_json["complete"] = False 118 | current_node = [ 119 | node for node in intent.parameters if missing_parameter in node.name][0] 120 | result_json["currentNode"] = current_node.name 121 | result_json["speechResponse"] = split_sentence(current_node.prompt) 122 | else: 123 | result_json["currentNode"] = None 124 | result_json["missingParameters"] = [] 125 | result_json["parameters"] = {} 126 | result_json["intent"] = {} 127 | result_json["complete"] = True 128 | 129 | if result_json["complete"]: 130 | if intent.apiTrigger: 131 | isJson = False 132 | parameters = result_json["extractedParameters"] 133 | headers = intent.apiDetails.get_headers() 134 | app.logger.info("headers %s" % headers) 135 | url_template = Template( 136 | intent.apiDetails.url, undefined=SilentUndefined) 137 | rendered_url = url_template.render(**context) 138 | if intent.apiDetails.isJson: 139 | isJson = True 140 | request_template = Template( 141 | intent.apiDetails.jsonData, undefined=SilentUndefined) 142 | parameters = json.loads(request_template.render(**context)) 143 | 144 | try: 145 | result = call_api(rendered_url, 146 | intent.apiDetails.requestType, headers, 147 | parameters, isJson) 148 | except Exception as e: 149 | app.logger.warn("API call failed", e) 150 | result_json["speechResponse"] = ["Service is not available. Please try again later."] 151 | else: 152 | context["result"] = result 153 | template = Template( 154 | intent.speechResponse, undefined=SilentUndefined) 155 | result_json["speechResponse"] = split_sentence(template.render(**context)) 156 | else: 157 | context["result"] = {} 158 | template = Template(intent.speechResponse, 159 | undefined=SilentUndefined) 160 | result_json["speechResponse"] = split_sentence(template.render(**context)) 161 | app.logger.info(request_json.get("input"), extra=result_json) 162 | return build_response.build_json(result_json) 163 | else: 164 | return abort(400) 165 | 166 | 167 | def update_model(): 168 | """ 169 | Signal hook to be called after training is completed. 170 | Reloads ml models and synonyms. 171 | :param app: 172 | :param message: 173 | :param extra: 174 | :return: 175 | """ 176 | global sentence_classifier 177 | 178 | sentence_classifier.load(app.config["MODELS_DIR"]) 179 | 180 | synonyms = get_synonyms() 181 | 182 | global entity_extraction 183 | 184 | entity_extraction = EntityExtractor(synonyms) 185 | app.logger.info("Intent Model updated") 186 | 187 | def predict(sentence): 188 | """ 189 | Predict Intent using Intent classifier 190 | :param sentence: 191 | :return: 192 | """ 193 | bot = Bot.objects.get(name="default") 194 | predicted, intents = sentence_classifier.process(sentence) 195 | app.logger.info("predicted intent %s", predicted) 196 | app.logger.info("other intents %s", intents) 197 | if predicted["confidence"] < bot.config.get("confidence_threshold", .90): 198 | intent = Intent.objects.get(intentId=app.config["DEFAULT_FALLBACK_INTENT_NAME"]) 199 | return intent.intentId, 1.0, [] 200 | else: 201 | return predicted["intent"], predicted["confidence"], intents[1:] 202 | -------------------------------------------------------------------------------- /app/endpoint/utils.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | import requests 4 | from jinja2 import Undefined 5 | 6 | from flask import current_app as app 7 | from app.entities.models import Entity 8 | 9 | 10 | def split_sentence(sentence): 11 | return sentence.split("###") 12 | 13 | 14 | def get_synonyms(): 15 | """ 16 | Build synonyms dict from DB 17 | :return: 18 | """ 19 | synonyms = {} 20 | 21 | for entity in Entity.objects: 22 | for value in entity.entity_values: 23 | for synonym in value.synonyms: 24 | synonyms[synonym] = value.value 25 | app.logger.info("loaded synonyms %s", synonyms) 26 | return synonyms 27 | 28 | 29 | def call_api(url, type, headers={}, parameters={}, is_json=False): 30 | """ 31 | Call external API 32 | :param url: 33 | :param type: 34 | :param parameters: 35 | :param is_json: 36 | :return: 37 | """ 38 | app.logger.info("Initiating API Call with following info: url => {} payload => {}".format(url, parameters)) 39 | if "GET" in type: 40 | response = requests.get(url, headers=headers, params=parameters, timeout=5) 41 | elif "POST" in type: 42 | if is_json: 43 | response = requests.post(url, headers=headers, json=parameters, timeout=5) 44 | else: 45 | response = requests.post(url, headers=headers, params=parameters, timeout=5) 46 | elif "PUT" in type: 47 | if is_json: 48 | response = requests.put(url, headers=headers, json=parameters, timeout=5) 49 | else: 50 | response = requests.put(url, headers=headers, params=parameters, timeout=5) 51 | elif "DELETE" in type: 52 | response = requests.delete(url, headers=headers, params=parameters, timeout=5) 53 | else: 54 | raise Exception("unsupported request method.") 55 | result = json.loads(response.text) 56 | app.logger.info("API response => %s", result) 57 | return result 58 | 59 | 60 | class SilentUndefined(Undefined): 61 | """ 62 | Class to suppress jinja2 errors and warnings 63 | """ 64 | 65 | def _fail_with_undefined_error(self, *args, **kwargs): 66 | return 'undefined' 67 | 68 | __add__ = __radd__ = __mul__ = __rmul__ = __div__ = __rdiv__ = \ 69 | __truediv__ = __rtruediv__ = __floordiv__ = __rfloordiv__ = \ 70 | __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ 71 | __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = __int__ = \ 72 | __float__ = __complex__ = __pow__ = __rpow__ = \ 73 | _fail_with_undefined_error 74 | -------------------------------------------------------------------------------- /app/entities/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/app/entities/__init__.py -------------------------------------------------------------------------------- /app/entities/controllers.py: -------------------------------------------------------------------------------- 1 | from bson.json_util import dumps, loads 2 | from bson.objectid import ObjectId 3 | from flask import Blueprint, request, Response 4 | 5 | from app.commons import build_response 6 | from app.commons.utils import update_document 7 | from app.entities.models import Entity 8 | 9 | entities_blueprint = Blueprint('entities_blueprint', __name__, 10 | url_prefix='/entities') 11 | 12 | 13 | @entities_blueprint.route('/', methods=['POST']) 14 | def create_entity(): 15 | """ 16 | Create a story from the provided json 17 | :return: 18 | """ 19 | content = request.get_json(silent=True) 20 | 21 | entity = Entity() 22 | entity.name = content.get("name") 23 | entity.entity_values = [] 24 | 25 | try: 26 | entity_id = entity.save() 27 | except Exception as e: 28 | return build_response.build_json({"error": str(e)}) 29 | 30 | return build_response.build_json({ 31 | "_id": str(entity_id.id) 32 | }) 33 | 34 | 35 | @entities_blueprint.route('/') 36 | def read_entities(): 37 | """ 38 | find list of entities 39 | :return: 40 | """ 41 | intents = Entity.objects.only('name', 'id') 42 | return build_response.sent_json(intents.to_json()) 43 | 44 | 45 | @entities_blueprint.route('/') 46 | def read_entity(id): 47 | """ 48 | Find details for the given entity name 49 | :param id: 50 | :return: 51 | """ 52 | return Response( 53 | response=dumps(Entity.objects.get( 54 | id=ObjectId(id)).to_mongo().to_dict()), 55 | status=200, mimetype="application/json") 56 | 57 | 58 | @entities_blueprint.route('/', methods=['PUT']) 59 | def update_entity(id): 60 | """ 61 | Update a story from the provided json 62 | :param id: 63 | :return: 64 | """ 65 | json_data = loads(request.get_data()) 66 | entity = Entity.objects.get(id=ObjectId(id)) 67 | entity = update_document(entity, json_data) 68 | entity.save() 69 | return build_response.sent_ok() 70 | 71 | 72 | @entities_blueprint.route('/', methods=['DELETE']) 73 | def delete_entity(id): 74 | """ 75 | Delete a intent 76 | :param id: 77 | :return: 78 | """ 79 | Entity.objects.get(id=ObjectId(id)).delete() 80 | 81 | return build_response.sent_ok() 82 | -------------------------------------------------------------------------------- /app/entities/models.py: -------------------------------------------------------------------------------- 1 | from mongoengine.fields import Document 2 | from mongoengine.fields import EmbeddedDocument 3 | from mongoengine.fields import EmbeddedDocumentListField 4 | from mongoengine.fields import ListField 5 | from mongoengine.fields import StringField 6 | 7 | 8 | class EntityValue(EmbeddedDocument): 9 | value = StringField(required=True) 10 | synonyms = ListField(required=True, default=[]) 11 | 12 | 13 | class Entity(Document): 14 | name = StringField(max_length=100, required=True, unique=True) 15 | entity_values = EmbeddedDocumentListField(EntityValue) 16 | meta = { 17 | 'indexes': [ 18 | { 19 | 'fields': ['$name'], 20 | 'default_language': 'english' 21 | } 22 | ]} 23 | -------------------------------------------------------------------------------- /app/intents/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/app/intents/__init__.py -------------------------------------------------------------------------------- /app/intents/controllers.py: -------------------------------------------------------------------------------- 1 | import os 2 | from bson.json_util import dumps 3 | from bson.json_util import loads 4 | from bson.objectid import ObjectId 5 | from flask import Blueprint, request, Response 6 | from flask import abort 7 | from flask import current_app as app 8 | from app.commons import build_response 9 | from app.commons.utils import update_document 10 | from app.intents.models import ApiDetails 11 | from app.intents.models import Intent 12 | from app.intents.models import Parameter 13 | from app.nlu.tasks import train_models 14 | 15 | intents = Blueprint('intents_blueprint', __name__, 16 | url_prefix='/intents') 17 | 18 | 19 | @intents.route('/', methods=['POST']) 20 | def create_intent(): 21 | """ 22 | Create a story from the provided json 23 | :return: 24 | """ 25 | content = request.get_json(silent=True) 26 | 27 | intent = Intent() 28 | intent.name = content.get("name") 29 | intent.intentId = content.get("intentId") 30 | intent.speechResponse = content.get("speechResponse") 31 | intent.trainingData = [] 32 | 33 | if content.get("apiTrigger") is True: 34 | intent.apiTrigger = True 35 | api_details = ApiDetails() 36 | isJson = content.get("apiDetails").get("isJson") 37 | api_details.isJson = isJson 38 | if isJson: 39 | api_details.jsonData = content.get("apiDetails").get("jsonData") 40 | 41 | api_details.url = content.get("apiDetails").get("url") 42 | api_details.headers = content.get("apiDetails").get("headers") 43 | api_details.requestType = content.get("apiDetails").get("requestType") 44 | intent.apiDetails = api_details 45 | else: 46 | intent.apiTrigger = False 47 | 48 | if content.get("parameters"): 49 | for param in content.get("parameters"): 50 | parameter = Parameter() 51 | update_document(parameter, param) 52 | intent.parameters.append(parameter) 53 | try: 54 | story_id = intent.save() 55 | except Exception as e: 56 | return build_response.build_json({"error": str(e)}) 57 | 58 | return build_response.build_json({ 59 | "_id": str(story_id.id) 60 | }) 61 | 62 | 63 | @intents.route('/') 64 | def read_intents(): 65 | """ 66 | find list of intents for the agent 67 | :return: 68 | """ 69 | all_intents = Intent.objects 70 | return build_response.sent_json(all_intents.to_json()) 71 | 72 | 73 | @intents.route('/') 74 | def read_intent(id): 75 | """ 76 | Find details for the given intent id 77 | :param id: 78 | :return: 79 | """ 80 | return Response(response=dumps( 81 | Intent.objects.get( 82 | id=ObjectId(id)).to_mongo().to_dict()), 83 | status=200, 84 | mimetype="application/json") 85 | 86 | 87 | @intents.route('/', methods=['PUT']) 88 | def update_intent(id): 89 | """ 90 | Update a story from the provided json 91 | :return: 92 | """ 93 | json_data = loads(request.get_data()) 94 | intent = Intent.objects.get(id=ObjectId(id)) 95 | intent = update_document(intent, json_data) 96 | intent.save() 97 | return 'success', 200 98 | 99 | 100 | @intents.route('/', methods=['DELETE']) 101 | def delete_intent(id): 102 | """ 103 | Delete a intent 104 | :param id: 105 | :return: 106 | """ 107 | Intent.objects.get(id=ObjectId(id)).delete() 108 | 109 | try: 110 | train_models() 111 | except BaseException: 112 | pass 113 | 114 | # remove NER model for the deleted story 115 | try: 116 | os.remove("{}/{}.model".format(app.config["MODELS_DIR"], id)) 117 | except OSError: 118 | pass 119 | return build_response.sent_ok() 120 | 121 | 122 | @intents.route('/export', methods=['GET']) 123 | def export_intents(): 124 | """ 125 | Deserialize and export Mongoengines as jsonfile 126 | :return: 127 | """ 128 | intents_json = Intent.objects.to_json() 129 | app.logger.info(intents_json) 130 | return Response(intents_json, 131 | mimetype='application/json', 132 | headers={'Content-Disposition':'attachment;filename=intents.json'}) 133 | 134 | 135 | @intents.route('/import', methods=['POST']) 136 | def import_intents(): 137 | """ 138 | Convert json files to Intents objects and insert to MongoDB 139 | :return: 140 | """ 141 | # check if the post request has the file part 142 | if 'file' not in request.files: 143 | abort(400, 'No file part') 144 | json_file = request.files['file'] 145 | all_intents = import_json(json_file) 146 | 147 | return build_response.build_json({"num_intents_created": len(all_intents)}) 148 | 149 | 150 | def import_json(json_file): 151 | json_data = json_file.read() 152 | # intents = Intent.objects.from_json(json_data) 153 | all_intents = loads(json_data) 154 | 155 | creates_intents = [] 156 | for intent in all_intents: 157 | new_intent = Intent() 158 | new_intent = update_document(new_intent, intent) 159 | new_intent.save() 160 | creates_intents.append(new_intent) 161 | return creates_intents 162 | -------------------------------------------------------------------------------- /app/intents/models.py: -------------------------------------------------------------------------------- 1 | from bson.objectid import ObjectId 2 | from mongoengine.fields import BooleanField 3 | from mongoengine.fields import Document 4 | from mongoengine.fields import EmbeddedDocument 5 | from mongoengine.fields import EmbeddedDocumentField 6 | from mongoengine.fields import EmbeddedDocumentListField 7 | from mongoengine.fields import ListField 8 | from mongoengine.fields import ObjectIdField 9 | from mongoengine.fields import StringField 10 | 11 | 12 | class LabeledSentences(EmbeddedDocument): 13 | id = ObjectIdField(required=True, default=lambda: ObjectId()) 14 | data = ListField(required=True) 15 | 16 | 17 | class Parameter(EmbeddedDocument): 18 | id = ObjectIdField(default=lambda: ObjectId()) 19 | name = StringField(required=True) 20 | required = BooleanField(default=False) 21 | type = StringField(required=False) 22 | prompt = StringField() 23 | 24 | 25 | class ApiDetails(EmbeddedDocument): 26 | url = StringField(required=True) 27 | requestType = StringField( 28 | choices=[ 29 | "POST", 30 | "GET", 31 | "DELETE", 32 | "PUT"], 33 | required=True) 34 | headers = ListField(default=[]) 35 | isJson = BooleanField(default=False) 36 | jsonData = StringField(default="{}") 37 | 38 | def get_headers(self): 39 | headers = {} 40 | for header in self.headers: 41 | headers[header["headerKey"]] = header["headerValue"] 42 | return headers 43 | 44 | 45 | class Intent(Document): 46 | name = StringField(max_length=100, required=True, unique=True) 47 | userDefined = BooleanField(default=True) 48 | intentId = StringField(required=True, unique=True) 49 | apiTrigger = BooleanField(required=True) 50 | apiDetails = EmbeddedDocumentField(ApiDetails) 51 | speechResponse = StringField(required=True) 52 | parameters = ListField(EmbeddedDocumentField(Parameter)) 53 | labeledSentences = EmbeddedDocumentListField(LabeledSentences) 54 | trainingData = ListField(required=False) 55 | -------------------------------------------------------------------------------- /app/nlu/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc -------------------------------------------------------------------------------- /app/nlu/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/app/nlu/__init__.py -------------------------------------------------------------------------------- /app/nlu/classifiers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/app/nlu/classifiers/__init__.py -------------------------------------------------------------------------------- /app/nlu/classifiers/sklearn_intent_classifer.py: -------------------------------------------------------------------------------- 1 | import os 2 | import cloudpickle 3 | import numpy as np 4 | from app import spacy_tokenizer 5 | 6 | class SklearnIntentClassifier: 7 | 8 | def __init__(self): 9 | self.model = None 10 | 11 | def get_spacy_embedding(self, sentence): 12 | """ 13 | perform basic cleaning,tokenization and lemmatization 14 | :param sentence: 15 | :return list of clean tokens: 16 | """ 17 | spacy_obj = spacy_tokenizer(sentence) 18 | return np.array(spacy_obj.vector) 19 | 20 | def train(self, X, y, outpath=None, verbose=True): 21 | """ 22 | Train intent classifier for given training data 23 | :param X: 24 | :param y: 25 | :param outpath: 26 | :param verbose: 27 | :return: 28 | """ 29 | from sklearn.model_selection import GridSearchCV 30 | from sklearn.svm import SVC 31 | 32 | X = np.stack( 33 | [ 34 | self.get_spacy_embedding(example) 35 | for example in X 36 | ] 37 | ) 38 | 39 | items, counts = np.unique(y, return_counts=True) 40 | cv_splits = max(2, min(5, np.min(counts) // 5)) 41 | 42 | tuned_parameters = [ 43 | {"C": [1, 2, 5, 10, 20, 100], "gamma": [0.1], "kernel": ["linear"]} 44 | ] 45 | 46 | classifier = GridSearchCV( 47 | SVC(C=1, probability=True, class_weight="balanced"), 48 | param_grid=tuned_parameters, 49 | n_jobs=-1, 50 | cv=cv_splits, 51 | scoring="f1_weighted", 52 | verbose=1, 53 | ) 54 | 55 | classifier.fit(X, y) 56 | 57 | if outpath: 58 | path = os.path.join(outpath, "sklearn_intent_model.hd5") 59 | with open(path, 'wb') as f: 60 | cloudpickle.dump(classifier.best_estimator_, f) 61 | 62 | if verbose: 63 | print("Model written out to {}".format(outpath)) 64 | 65 | return classifier.best_estimator_ 66 | 67 | def load(self, PATH): 68 | """ 69 | load trained model from given path 70 | :param PATH: 71 | :return: 72 | """ 73 | try: 74 | PATH = os.path.join(PATH, "sklearn_intent_model.hd5") 75 | with open(PATH, 'rb') as f: 76 | self.model = cloudpickle.load(f) 77 | except IOError: 78 | return False 79 | 80 | def predict_proba(self, X): 81 | """Given a bow vector of an input text, predict most probable label. 82 | Returns only the most likely label. 83 | 84 | :param X: bow of input text 85 | :return: tuple of first, the most probable label 86 | and second, its probability""" 87 | 88 | pred_result = self.model.predict_proba([self.get_spacy_embedding(X)]) 89 | # sort the probabilities retrieving the indices of the elements 90 | sorted_indices = np.fliplr(np.argsort(pred_result, axis=1)) 91 | return sorted_indices, pred_result[:, sorted_indices] 92 | 93 | def process(self, x, INTENT_RANKING_LENGTH=5): 94 | """Returns the most likely intent and 95 | its probability for the input text.""" 96 | 97 | intent = {"name": None, "confidence": 0.0} 98 | intent_ranking = [] 99 | 100 | if self.model: 101 | intents, probabilities = self.predict_proba(x) 102 | intents = [self.model.classes_[intent] 103 | for intent in intents.flatten()] 104 | probabilities = probabilities.flatten() 105 | 106 | if len(intents) > 0 and len(probabilities) > 0: 107 | ranking = list(zip(list(intents), list(probabilities))) 108 | ranking = ranking[:INTENT_RANKING_LENGTH] 109 | 110 | intent = {"intent": intents[0], "confidence": probabilities[0]} 111 | intent_ranking = [{"intent": intent_name, "confidence": score} 112 | for intent_name, score in ranking] 113 | 114 | else: 115 | intent = {"name": None, "confidence": 0.0} 116 | intent_ranking = [] 117 | 118 | return intent, intent_ranking 119 | 120 | -------------------------------------------------------------------------------- /app/nlu/classifiers/tf_intent_classifer.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | import cloudpickle 5 | import numpy as np 6 | import spacy 7 | import tensorflow as tf 8 | from sklearn.preprocessing import LabelBinarizer 9 | from tensorflow.python.keras import Sequential 10 | from tensorflow.python.layers.core import Dense 11 | from tensorflow.python.layers.core import Dropout 12 | 13 | np.random.seed(1) 14 | 15 | 16 | class TfIntentClassifier: 17 | 18 | def __init__(self): 19 | self.model = None 20 | self.nlp = spacy.load('en') 21 | self.label_encoder = LabelBinarizer() 22 | self.graph = None 23 | 24 | def train(self, X, y, models_dir=None, verbose=True): 25 | """ 26 | Train intent classifier for given training data 27 | :param X: 28 | :param y: 29 | :param models_dir: 30 | :param verbose: 31 | :return: 32 | """ 33 | 34 | def create_model(): 35 | """ 36 | Define and return tensorflow model. 37 | """ 38 | model = Sequential() 39 | model.add(Dense(256, activation=tf.nn.relu, 40 | input_shape=(vocab_size,))) 41 | model.add(Dropout(0.2)) 42 | model.add(Dense(128, activation=tf.nn.relu)) 43 | model.add(Dropout(0.2)) 44 | model.add(Dense(num_labels, activation=tf.nn.softmax)) 45 | 46 | """ 47 | tried: 48 | loss functions => categorical_crossentropy, binary_crossentropy 49 | optimizers => adam, rmsprop 50 | """ 51 | 52 | model.compile(loss='categorical_crossentropy', 53 | optimizer='adam', 54 | metrics=['accuracy']) 55 | 56 | model.summary() 57 | 58 | return model 59 | 60 | # spacy context vector size 61 | vocab_size = 384 62 | 63 | # create spacy doc vector matrix 64 | x_train = np.array([list(self.nlp(x).vector) for x in X]) 65 | 66 | num_labels = len(set(y)) 67 | self.label_encoder.fit(y) 68 | y_train = self.label_encoder.transform(y) 69 | 70 | del self.model 71 | tf.keras.backend.clear_session() 72 | time.sleep(3) 73 | 74 | self.model = create_model() 75 | # start training 76 | self.model.fit(x_train, y_train, shuffle=True, epochs=300, verbose=1) 77 | 78 | if models_dir: 79 | tf.keras.models.save_model( 80 | self.model, 81 | os.path.join(models_dir, "tf_intent_model.hd5") 82 | 83 | ) 84 | if verbose: 85 | print("TF Model written out to {}" 86 | .format(os.path.join(models_dir, "tf_intent_model.hd5"))) 87 | 88 | cloudpickle.dump(self.label_encoder, open( 89 | os.path.join(models_dir, "labels.pkl"), 'wb')) 90 | 91 | if verbose: 92 | print("Labels written out to {}" 93 | .format(os.path.join(models_dir, "labels.pkl"))) 94 | 95 | def load(self, models_dir): 96 | try: 97 | del self.model 98 | 99 | tf.keras.backend.clear_session() 100 | 101 | self.model = tf.keras.models.load_model( 102 | os.path.join(models_dir, "tf_intent_model.hd5"), compile=True) 103 | 104 | self.graph = tf.get_default_graph() 105 | 106 | print("Tf model loaded") 107 | 108 | with open(os.path.join(models_dir, "labels.pkl"), 'rb') as f: 109 | self.label_encoder = cloudpickle.load(f) 110 | print("Labels model loaded") 111 | 112 | except IOError: 113 | return False 114 | 115 | def predict(self, text): 116 | """ 117 | Predict class label for given model 118 | :param text: 119 | :return: 120 | """ 121 | return self.process(text) 122 | 123 | def predict_proba(self, x): 124 | """Given a bow vector of an input text, predict most probable label. 125 | Returns only the most likely label. 126 | 127 | :param x: raw input text 128 | :return: tuple of first, the most probable label and second, 129 | its probability""" 130 | 131 | x_predict = [self.nlp(x).vector] 132 | with self.graph.as_default(): 133 | pred_result = self.model.predict(np.array([x_predict[0]])) 134 | sorted_indices = np.fliplr(np.argsort(pred_result, axis=1)) 135 | return sorted_indices, pred_result[:, sorted_indices] 136 | 137 | def process(self, x, return_type="intent", INTENT_RANKING_LENGTH=5): 138 | """Returns the most likely intent and 139 | its probability for the input text.""" 140 | 141 | if not self.model: 142 | print("no class") 143 | intent = None 144 | intent_ranking = [] 145 | else: 146 | intents, probabilities = self.predict_proba(x) 147 | intents = [self.label_encoder.classes_[intent] 148 | for intent in intents.flatten()] 149 | probabilities = probabilities.flatten() 150 | 151 | if len(intents) > 0 and len(probabilities) > 0: 152 | ranking = list(zip(list(intents), list(probabilities))) 153 | ranking = ranking[:INTENT_RANKING_LENGTH] 154 | 155 | intent = {"intent": intents[0], 156 | "confidence": float("%.2f" % probabilities[0])} 157 | 158 | intent_ranking = [{"intent": intent_name, 159 | "confidence": float("%.2f" % score)} 160 | for intent_name, score in ranking] 161 | 162 | else: 163 | intent = {"name": None, "confidence": 0.0} 164 | intent_ranking = [] 165 | if return_type == "intent": 166 | return intent 167 | else: 168 | return intent_ranking 169 | -------------------------------------------------------------------------------- /app/nlu/controllers.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | from app.commons import build_response 4 | from app.nlu.tasks import train_models 5 | 6 | nlu = Blueprint('nlu_blueprint', __name__, url_prefix='/nlu') 7 | 8 | 9 | @nlu.route('/build_models', methods=['POST']) 10 | def build_models(): 11 | """ 12 | Build Intent classification and NER Models 13 | :return: 14 | """ 15 | train_models() 16 | return build_response.sent_ok() 17 | -------------------------------------------------------------------------------- /app/nlu/entity_extractor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pycrfsuite 4 | from flask import current_app as app 5 | from app import spacy_tokenizer 6 | 7 | 8 | class EntityExtractor: 9 | """ 10 | Performs NER training, prediction, model import/export 11 | """ 12 | 13 | def __init__(self, synonyms=[]): 14 | self.synonyms = synonyms 15 | 16 | def replace_synonyms(self, entities): 17 | """ 18 | replace extracted entity values with 19 | root word by matching with synonyms dict. 20 | :param entities: 21 | :return: 22 | """ 23 | for entity in entities.keys(): 24 | 25 | entity_value = str(entities[entity]) 26 | 27 | if entity_value.lower() in self.synonyms: 28 | entities[entity] = self.synonyms[entity_value.lower()] 29 | 30 | return entities 31 | 32 | def extract_features(self, sent, i): 33 | """ 34 | Extract features for a given sentence 35 | :param sent: 36 | :param i: 37 | :return: 38 | """ 39 | word = sent[i][0] 40 | postag = sent[i][1] 41 | features = [ 42 | 'bias', 43 | 'word.lower=' + word.lower(), 44 | 'word[-3:]=' + word[-3:], 45 | 'word[-2:]=' + word[-2:], 46 | 'word.isupper=%s' % word.isupper(), 47 | 'word.istitle=%s' % word.istitle(), 48 | 'word.isdigit=%s' % word.isdigit(), 49 | 'postag=' + postag, 50 | 'postag[:2]=' + postag[:2], 51 | ] 52 | if i > 0: 53 | word1 = sent[i - 1][0] 54 | postag1 = sent[i - 1][1] 55 | features.extend([ 56 | '-1:word.lower=' + word1.lower(), 57 | '-1:word.istitle=%s' % word1.istitle(), 58 | '-1:word.isupper=%s' % word1.isupper(), 59 | '-1:postag=' + postag1, 60 | '-1:postag[:2]=' + postag1[:2], 61 | ]) 62 | else: 63 | features.append('BOS') 64 | 65 | if i < len(sent) - 1: 66 | word1 = sent[i + 1][0] 67 | postag1 = sent[i + 1][1] 68 | features.extend([ 69 | '+1:word.lower=' + word1.lower(), 70 | '+1:word.istitle=%s' % word1.istitle(), 71 | '+1:word.isupper=%s' % word1.isupper(), 72 | '+1:postag=' + postag1, 73 | '+1:postag[:2]=' + postag1[:2], 74 | ]) 75 | else: 76 | features.append('EOS') 77 | 78 | return features 79 | 80 | def sent_to_features(self, sent): 81 | """ 82 | Extract features from training Data 83 | :param sent: 84 | :return: 85 | """ 86 | return [self.extract_features(sent, i) for i in range(len(sent))] 87 | 88 | def sent_to_labels(self, sent): 89 | """ 90 | Extract labels from training data 91 | :param sent: 92 | :return: 93 | """ 94 | return [label for token, postag, label in sent] 95 | 96 | def sent_to_tokens(self, sent): 97 | """ 98 | Extract tokens from training data 99 | :param sent: 100 | :return: 101 | """ 102 | return [token for token, postag, label in sent] 103 | 104 | def train(self, train_sentences, model_name): 105 | """ 106 | Train NER model for given model 107 | :param train_sentences: 108 | :param model_name: 109 | :return: 110 | """ 111 | features = [self.sent_to_features(s) for s in train_sentences] 112 | labels = [self.sent_to_labels(s) for s in train_sentences] 113 | 114 | trainer = pycrfsuite.Trainer(verbose=False) 115 | for xseq, yseq in zip(features, labels): 116 | trainer.append(xseq, yseq) 117 | 118 | trainer.set_params({ 119 | 'c1': 1.0, # coefficient for L1 penalty 120 | 'c2': 1e-3, # coefficient for L2 penalty 121 | 'max_iterations': 50, # stop earlier 122 | 123 | # include transitions that are possible, but not observed 124 | 'feature.possible_transitions': True 125 | }) 126 | trainer.train('model_files/%s.model' % model_name) 127 | return True 128 | 129 | # Extract Labels from BIO tagged sentence 130 | def crf2json(self, tagged_sentence): 131 | """ 132 | Extract label-value pair from NER prediction output 133 | :param tagged_sentence: 134 | :return: 135 | """ 136 | labeled = {} 137 | labels = set() 138 | for s, tp in tagged_sentence: 139 | if tp != "O": 140 | label = tp[2:] 141 | if tp.startswith("B"): 142 | labeled[label] = s 143 | labels.add(label) 144 | elif tp.startswith("I") and (label in labels): 145 | labeled[label] += " %s" % s 146 | return labeled 147 | 148 | def extract_ner_labels(self, predicted_labels): 149 | """ 150 | Extract name of labels from NER 151 | :param predicted_labels: 152 | :return: 153 | """ 154 | labels = [] 155 | for tp in predicted_labels: 156 | if tp != "O": 157 | labels.append(tp[2:]) 158 | return labels 159 | 160 | def predict(self, model_name, sentence): 161 | """ 162 | Predict NER labels for given model and query 163 | :param model_name: 164 | :param sentence: 165 | :return: 166 | """ 167 | from app.nlu.tasks import pos_tagger 168 | 169 | doc = spacy_tokenizer(sentence) 170 | words = [token.text for token in doc] 171 | tagged_token = pos_tagger(sentence) 172 | tagger = pycrfsuite.Tagger() 173 | tagger.open("{}/{}.model".format(app.config["MODELS_DIR"], model_name)) 174 | predicted_labels = tagger.tag(self.sent_to_features(tagged_token)) 175 | extracted_entities = self.crf2json( 176 | zip(words, predicted_labels)) 177 | return self.replace_synonyms(extracted_entities) 178 | 179 | @staticmethod 180 | def json2crf(training_data): 181 | """ 182 | Takes json annotated data and converts to 183 | CRFSuite training data representation 184 | :param training_data: 185 | :return labeled_examples: 186 | """ 187 | from app.nlu.tasks import sentence_tokenize, pos_tag_and_label 188 | 189 | labeled_examples = [] 190 | 191 | for example in training_data: 192 | # POS tag and initialize bio label as 'O' for all the tokens 193 | tagged_example = pos_tag_and_label(example.get("text")) 194 | 195 | # find no of words before selection 196 | for enitity in example.get("entities"): 197 | 198 | try: 199 | begin_index = enitity.get("begin") 200 | end_index = enitity.get("end") 201 | # find no of words before the entity 202 | inverse_selection = example.get("text")[0:begin_index - 1] 203 | inverse_selection = sentence_tokenize(inverse_selection) 204 | inverse_selection = inverse_selection.split(" ") 205 | inverse_word_count = len(inverse_selection) 206 | 207 | # get the entity value from selection 208 | selection = example.get("text")[begin_index:end_index] 209 | 210 | tokens = sentence_tokenize(selection).split(" ") 211 | 212 | selection_word_count = len(tokens) 213 | 214 | # build BIO tagging 215 | for i in range(1, selection_word_count + 1): 216 | if i == 1: 217 | bio = "B-" + enitity.get("name") 218 | else: 219 | bio = "I-" + enitity.get("name") 220 | tagged_example[(inverse_word_count + i) - 1][2] = bio 221 | except: 222 | # catches and skips invalid offsets and annotation 223 | continue 224 | 225 | labeled_examples.append(tagged_example) 226 | return labeled_examples 227 | -------------------------------------------------------------------------------- /app/nlu/tasks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from flask import current_app as app 4 | from app.endpoint.controllers import update_model 5 | from app.intents.models import Intent 6 | from app import spacy_tokenizer 7 | from app.nlu.classifiers.sklearn_intent_classifer import \ 8 | SklearnIntentClassifier 9 | from app.nlu.entity_extractor import EntityExtractor 10 | 11 | 12 | def train_models(): 13 | """ 14 | Initiate NER and Intent Classification training 15 | :return: 16 | """ 17 | # generate intent classifier training data 18 | intents = Intent.objects 19 | 20 | if not intents: 21 | raise Exception("NO_DATA") 22 | 23 | # train intent classifier on all intents 24 | train_intent_classifier(intents) 25 | 26 | # train ner model for each Stories 27 | for intent in intents: 28 | train_all_ner(intent.intentId, intent.trainingData) 29 | 30 | update_model() 31 | 32 | def train_intent_classifier(intents): 33 | """ 34 | Train intent classifier model 35 | :param intents: 36 | :return: 37 | """ 38 | X = [] 39 | y = [] 40 | for intent in intents: 41 | training_data = intent.trainingData 42 | for example in training_data: 43 | if example.get("text").strip() == "": 44 | continue 45 | X.append(example.get("text")) 46 | y.append(intent.intentId) 47 | 48 | intent_classifier = SklearnIntentClassifier() 49 | intent_classifier.train(X, y,outpath=app.config["MODELS_DIR"]) 50 | 51 | def train_all_ner(story_id, training_data): 52 | """ 53 | Train NER model for single Story 54 | :param story_id: 55 | :param training_data: 56 | :return: 57 | """ 58 | entityExtraction = EntityExtractor() 59 | # generate crf training data 60 | ner_training_data = entityExtraction.json2crf(training_data) 61 | # train and store ner model 62 | entityExtraction.train(ner_training_data, story_id) 63 | 64 | 65 | # Load and initialize Perceptron tagger 66 | 67 | def pos_tagger(sentence): 68 | """ 69 | perform POS tagging on a given sentence 70 | :param sentence: 71 | :return: 72 | """ 73 | doc = spacy_tokenizer(sentence) 74 | taged_sentance = [] 75 | for token in doc: 76 | taged_sentance.append((token.text, token.tag_)) 77 | return taged_sentance 78 | 79 | 80 | def pos_tag_and_label(sentence): 81 | """ 82 | Perform POS tagging and BIO labeling on given sentence 83 | :param sentence: 84 | :return: 85 | """ 86 | tagged_sentence = pos_tagger(sentence) 87 | tagged_sentence_json = [] 88 | for token, postag in tagged_sentence: 89 | tagged_sentence_json.append([token, postag, "O"]) 90 | return tagged_sentence_json 91 | 92 | 93 | def sentence_tokenize(sentences): 94 | """ 95 | Sentence tokenizer 96 | :param sentences: 97 | :return: 98 | """ 99 | doc = spacy_tokenizer(sentences) 100 | words = [token.text for token in doc] 101 | return " ".join(words) 102 | -------------------------------------------------------------------------------- /app/train/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/app/train/__init__.py -------------------------------------------------------------------------------- /app/train/controllers.py: -------------------------------------------------------------------------------- 1 | from bson.objectid import ObjectId 2 | from flask import Blueprint, request 3 | 4 | from app.commons import build_response 5 | from app.intents.models import Intent 6 | 7 | train = Blueprint('train_blueprint', __name__, 8 | url_prefix='/train') 9 | 10 | 11 | @train.route('//data', methods=['POST']) 12 | def save_training_data(story_id): 13 | """ 14 | Save training data for given story 15 | :param story_id: 16 | :return: 17 | """ 18 | story = Intent.objects.get(id=ObjectId(story_id)) 19 | story.trainingData = request.json 20 | story.save() 21 | return build_response.sent_ok() 22 | 23 | 24 | @train.route('//data', methods=['GET']) 25 | def get_training_data(story_id): 26 | """ 27 | retrieve training data for a given story 28 | :param story_id: 29 | :return: 30 | """ 31 | story = Intent.objects.get(id=ObjectId(story_id)) 32 | return build_response.build_json(story.trainingData) 33 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | class Config(object): 4 | DEBUG = False 5 | MONGODB_HOST = "mongodb://127.0.0.1:27017/iky-ai" 6 | 7 | # Intent Classifier model details 8 | MODELS_DIR = "model_files/" 9 | INTENT_MODEL_NAME = "intent.model" 10 | DEFAULT_FALLBACK_INTENT_NAME = "fallback" 11 | DEFAULT_WELCOME_INTENT_NAME = "init_conversation" 12 | USE_WORD_VECTORS = True 13 | SPACY_LANG_MODEL = "en_core_web_md" 14 | 15 | class Development(Config): 16 | DEBUG = True 17 | TEMPLATES_AUTO_RELOAD=True 18 | 19 | class Testing(Config): 20 | DEBUG = True 21 | TESTING = True 22 | 23 | class Production(Config): 24 | # MongoDB Database Details 25 | MONGODB_HOST = "mongodb://mongodb:27017/iky-ai" 26 | 27 | # Web Server details 28 | WEB_SERVER_PORT = 8001 29 | 30 | class Heroku(Production): 31 | MONGODB_HOST = os.environ.get('MONGO_URL') 32 | 33 | class Helm(Production): 34 | MONGODB_HOST = os.environ.get('MONGO_URL') -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | migrate: 4 | container_name: iky_migrate 5 | image: alfredfrancis/ai-chatbot-framework_backend:latest 6 | command: python manage.py migrate 7 | environment: 8 | APPLICATION_ENV: Production 9 | depends_on: 10 | - mongodb 11 | 12 | iky_backend: 13 | container_name: iky_backend 14 | image: alfredfrancis/ai-chatbot-framework_backend:latest 15 | hostname: iky_backend 16 | environment: 17 | APPLICATION_ENV: Production 18 | depends_on: 19 | - migrate 20 | - mongodb 21 | 22 | iky_gateway: 23 | container_name: iky_gateway 24 | image: alfredfrancis/ai-chatbot-framework_frontend:latest 25 | ports: 26 | - "8080:80" 27 | depends_on: 28 | - iky_backend 29 | 30 | mongodb: 31 | container_name: mongodb 32 | image: mongo:4.2.20 33 | hostname: mongodb 34 | ports: 35 | - "27017:27017" 36 | volumes: 37 | - mongodbdata:/data 38 | 39 | volumes: 40 | mongodbdata: 41 | -------------------------------------------------------------------------------- /examples/default_intents.json: -------------------------------------------------------------------------------- 1 | [{"name": "Default Fallback intent", "userDefined": false, "apiTrigger": false, "intentId": "fallback", "_id": {"$oid": "59aae7bd26f6f60007b06fb3"}, "speechResponse": "{{ \n\n[\n \"Sorry ### I'm having trouble understanding you.\",\n \"Hmm ### I cant handle that yet.\",\n \"Can you please re-phrase your query ?\"\n] | random \n\n}}\ufeff\n\n"}, {"name": "cancel", "trainingData": [{"text": "i want to cancel", "entities": []}, {"text": "cancel that", "entities": []}, {"text": "cancel", "entities": []}], "userDefined": false, "apiTrigger": false, "intentId": "cancel", "_id": {"$oid": "59aae7bd26f6f60007b06fb5"}, "speechResponse": "Ok. Canceled."}, {"name": "Welcome message", "trainingData": [{"text": "hello there", "entities": []}, {"text": "hey there", "entities": []}, {"text": "hii", "entities": []}, {"text": "heyy", "entities": []}, {"text": "howdy", "entities": []}, {"text": "hey", "entities": []}, {"text": "hello", "entities": []}, {"text": "hi", "entities": []}], "userDefined": false, "apiTrigger": false, "intentId": "init_conversation", "_id": {"$oid": "59aae7bd26f6f60007b06fb7"}, "speechResponse": "Hi {{context[\"username\"] }} ### What can i do for you ?"}] -------------------------------------------------------------------------------- /examples/nodejs/discordRequest.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | //"msg" here is equal to the "message" object in discord.js. It represents a message sent by the user. 3 | chatRequest: function (msg) { 4 | //These are just here for modularity and can be removed if they're already set in the file calling this function. 5 | const Discord = require('discord.js'); 6 | const request = require('request'); 7 | 8 | var payload = { 9 | "currentNode": "", 10 | "complete": null, 11 | "context":{}, 12 | "parameters": [], 13 | "extractedParameters": {}, 14 | "speechResponse": "", 15 | "intent": {}, 16 | "input": '', 17 | "missingParameters": [] 18 | }; 19 | //Set the input to a clean string of the user's message. 20 | var userInput= msg.cleanContent; 21 | payload["input"] = userInput; 22 | 23 | var options = { 24 | uri: 'http://localhost:8001/api/v1', 25 | headers: { 26 | 'Content-Type': 'application/json' 27 | }, 28 | json: payload 29 | }; 30 | 31 | request.post(options, function(err, res, body) { 32 | //Log any errors, along with the payload that got sent and the response you got and then have the bot respond to the user. 33 | console.log('ERROR: \n', err); 34 | console.log('PAYLOAD: \n', options['json']); 35 | console.log('RESPONSE: \n', body); 36 | msg.channel.send(body.speechResponse); 37 | }); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /examples/python/app.py: -------------------------------------------------------------------------------- 1 | import requests,json 2 | 3 | ''' 4 | define initial payload 5 | set input = 'init_conversation' so that bot will return default welcome message 6 | ''' 7 | payload = { 8 | "currentNode": "", 9 | "complete": None, 10 | "context": {}, 11 | "parameters": [], 12 | "extractedParameters": {}, 13 | "speechResponse": "", 14 | "intent": {}, 15 | "input": "init_conversation", 16 | "missingParameters": [] 17 | } 18 | 19 | while True: 20 | r = requests.post("http://localhost:8001/api/v1", json=payload) 21 | # replace payload variable with api result 22 | payload = json.loads(r.text) 23 | 24 | print("Iky\t" + payload.get("speechResponse")) 25 | 26 | # read user input 27 | payload["input"]=raw_input("You:\t") 28 | -------------------------------------------------------------------------------- /examples/restaurant_search.json: -------------------------------------------------------------------------------- 1 | [{"name": "Restaurant search", "parameters": [{"prompt": "Sure ### tell me your location?", "required": true, "type": "free_text", "id": {"$oid": "5ae0dd474ae5357959ad5f1d"}, "name": "location"}, {"prompt": "Ok. ### What type cuisine are you looking for?", "required": true, "type": "free_text", "id": {"$oid": "5ae0dd474ae5357959ad5f1e"}, "name": "cuisine"}], "userDefined": true, "trainingData": [{"text": "im looking for a place in banglore serving Chinese", "entities": [{"begin": 26, "end": 34, "name": "location", "value": "banglore"}, {"begin": 43, "end": 50, "name": "cuisine", "value": "Chinese"}]}, {"text": "i'm looking for Chinese food", "entities": [{"begin": 16, "end": 23, "name": "cuisine", "value": "Chinese"}]}, {"text": "I'm looking for south indian places", "entities": [{"begin": 12, "end": 28, "name": "cuisine", "value": "for south indian"}]}, {"text": "im looking for a place near banglore", "entities": [{"begin": 28, "end": 36, "name": "location", "value": "banglore"}]}, {"text": "i'm looking for a place to eat near down town la", "entities": [{"begin": 36, "end": 48, "name": "location", "value": "down town la"}]}, {"text": "i'm looking for a place in new york", "entities": [{"begin": 27, "end": 35, "name": "location", "value": "new york"}]}, {"text": "im looking for a place in banglore", "entities": [{"begin": 26, "end": 34, "name": "location", "value": "banglore"}]}, {"text": "looking for indian cuisine in new york", "entities": [{"begin": 12, "end": 18, "name": "cuisine", "value": "indian"}, {"begin": 30, "end": 38, "name": "location", "value": "new york"}]}, {"text": "central indian restaurant", "entities": [{"begin": 0, "end": 7, "name": "location", "value": "central"}, {"begin": 8, "end": 14, "name": "cuisine", "value": "indian"}]}, {"text": "I am looking for mexican indian fusion", "entities": [{"begin": 17, "end": 38, "name": "cuisine", "value": "mexican indian fusion"}]}, {"text": "I am looking a restaurant in 29432", "entities": [{"begin": 29, "end": 34, "name": "location", "value": "29432"}]}, {"text": "I am looking for asian fusion food", "entities": [{"begin": 17, "end": 29, "name": "cuisine", "value": "asian fusion"}]}, {"text": "anywhere near 18328", "entities": [{"begin": 14, "end": 19, "name": "location", "value": "18328"}]}, {"text": "anywhere in the west", "entities": [{"begin": 16, "end": 20, "name": "location", "value": "west"}]}, {"text": "search for restaurants", "entities": []}, {"text": "i am looking for an indian spot called olaolaolaolaolaola", "entities": [{"begin": 39, "end": 57, "name": "location", "value": "olaolaolaolaolaola"}, {"begin": 20, "end": 26, "name": "cuisine", "value": "indian"}]}, {"text": "show me a mexican place in the centre", "entities": [{"begin": 10, "end": 17, "name": "cuisine", "value": "mexican"}, {"begin": 31, "end": 37, "name": "location", "value": "centre"}]}, {"text": "show me chines restaurants in the north", "entities": [{"begin": 8, "end": 14, "name": "cuisine", "value": "chines"}, {"begin": 34, "end": 39, "name": "location", "value": "north"}]}, {"text": "show me chinese restaurants", "entities": [{"begin": 8, "end": 15, "name": "cuisine", "value": "chinese"}]}, {"text": "i'm looking for a place in the north of town", "entities": [{"begin": 31, "end": 44, "name": "location", "value": "north of town"}]}, {"text": "I am searching for a dinner spot", "entities": []}, {"text": "I want to grab lunch", "entities": []}, {"text": "i'm looking for a place to eat", "entities": []}], "apiTrigger": false, "intentId": "restaurant_search", "_id": {"$oid": "5adb265507440e00128fcfa1"}, "speechResponse": "Ok ### I found following restaurants serving {{parameters[\"cuisine\"] }} in {{parameters[\"location\"] }}\n###\n Restaurant Name 1\n###\n Restaurant Name 2"}, {"name": "Affirm", "trainingData": [{"text": "sounds really good", "entities": []}, {"text": "great choice\n", "entities": []}, {"text": "correct\n", "entities": []}, {"text": "right, thank you\n", "entities": []}, {"text": "great\n", "entities": []}, {"text": "ok\n", "entities": []}, {"text": "that's right\n", "entities": []}, {"text": "indeed\n", "entities": []}, {"text": "yeah\n", "entities": []}, {"text": "yep\n", "entities": []}, {"text": "yes\n", "entities": []}], "userDefined": true, "apiTrigger": false, "intentId": "affirm", "_id": {"$oid": "5adc8a3d4ae5353f40be85ca"}, "speechResponse": "I'm glad that you agree"}, {"name": "Goodbye", "trainingData": [{"text": "have a good one", "entities": []}, {"text": "Bye bye\n", "entities": []}, {"text": "farewell\n", "entities": []}, {"text": "end\n", "entities": []}, {"text": "stop\n", "entities": []}, {"text": "good bye\n", "entities": []}, {"text": "goodbye\n", "entities": []}, {"text": "bye", "entities": []}], "userDefined": true, "apiTrigger": false, "intentId": "goodbye", "_id": {"$oid": "5adc8a9c4ae5353f40be85cf"}, "speechResponse": "It was nice talking to you sir"}, {"name": "Thank you", "trainingData": [{"text": "thank you iky", "entities": []}, {"text": "thanks", "entities": []}, {"text": "thank you very much", "entities": []}], "userDefined": true, "apiTrigger": false, "intentId": "thank_you", "_id": {"$oid": "5adcb6314ae535517d9f8218"}, "speechResponse": "I'm happy to help :)"}] -------------------------------------------------------------------------------- /examples/ruby/request.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | require 'json' 3 | 4 | uri = URI('http://localhost:8001/api/v1') 5 | http = Net::HTTP.new(uri.host, uri.port) 6 | request = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json') 7 | 8 | request.body = { 9 | 'currentNode': '', 10 | 'complete': nil, 11 | 'context': {}, 12 | 'parameters': [], 13 | 'extractedParameters': {}, 14 | 'speechResponse': '', 15 | 'intent': {}, 16 | 'input': 'init_conversation', 17 | 'missingParameters': [] 18 | }.to_json 19 | 20 | while true 21 | response = http.request(request).body 22 | json_response = JSON.load(response) 23 | 24 | puts "Iky #{json_response['speechResponse']}" 25 | 26 | original_request_body = JSON.load(request.body) 27 | original_request_body['input'] = gets.chomp 28 | request.body = original_request_body.to_json 29 | end 30 | -------------------------------------------------------------------------------- /frontend/.angular-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "project": { 4 | "name": "frontend" 5 | }, 6 | "apps": [ 7 | { 8 | "root": "src", 9 | "outDir": "dist", 10 | "assets": [ 11 | "assets", 12 | "favicon.ico" 13 | ], 14 | "index": "index.html", 15 | "main": "main.ts", 16 | "polyfills": "polyfills.ts", 17 | "test": "test.ts", 18 | "tsconfig": "tsconfig.app.json", 19 | "testTsconfig": "tsconfig.spec.json", 20 | "prefix": "app", 21 | "styles": [ 22 | "styles.scss" 23 | ], 24 | "scripts": [], 25 | 26 | "environmentSource": "environments/environment.ts", 27 | "environments": { 28 | "dev": "environments/environment.ts", 29 | "test": "environments/environment.test.ts", 30 | "prod": "environments/environment.prod.ts", 31 | "docker": "environments/environment.docker.ts" 32 | } 33 | } 34 | ], 35 | "e2e": { 36 | "protractor": { 37 | "config": "./protractor.conf.js" 38 | } 39 | }, 40 | "lint": [ 41 | { 42 | "project": "src/tsconfig.app.json", 43 | "exclude": "**/node_modules/**" 44 | }, 45 | { 46 | "project": "src/tsconfig.spec.json", 47 | "exclude": "**/node_modules/**" 48 | }, 49 | { 50 | "project": "e2e/tsconfig.e2e.json", 51 | "exclude": "**/node_modules/**" 52 | } 53 | ], 54 | "test": { 55 | "karma": { 56 | "config": "./karma.conf.js" 57 | } 58 | }, 59 | "defaults": { 60 | "styleExt": "scss", 61 | "component": {} 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /frontend/.dockerignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /dist-server 4 | /tmp 5 | /out-tsc 6 | 7 | # dependencies 8 | /node_modules 9 | 10 | # IDEs and editors 11 | /.idea 12 | .project 13 | .classpath 14 | .c9/ 15 | *.launch 16 | .settings/ 17 | *.sublime-workspace 18 | 19 | # IDE - VSCode 20 | .vscode/* 21 | !.vscode/settings.json 22 | !.vscode/tasks.json 23 | !.vscode/launch.json 24 | !.vscode/extensions.json 25 | 26 | # misc 27 | /.sass-cache 28 | /connect.lock 29 | /coverage 30 | /libpeerconnection.log 31 | npm-debug.log 32 | yarn-error.log 33 | testem.log 34 | /typings 35 | 36 | # e2e 37 | /e2e/*.js 38 | /e2e/*.map 39 | 40 | # System Files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist-server 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | /node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # e2e 38 | /e2e/*.js 39 | /e2e/*.map 40 | 41 | # System Files 42 | .DS_Store 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | ### STAGE 1: Build ### 2 | 3 | # We label our stage as 'builder' 4 | FROM node:8-alpine as builder 5 | 6 | COPY package.json package-lock.json ./ 7 | 8 | RUN npm set progress=false && npm config set depth 0 && npm cache clean --force 9 | 10 | ## Storing node modules on a separate layer will prevent unnecessary npm installs at each build 11 | RUN npm i && mkdir /ng-app && cp -R ./node_modules ./ng-app 12 | 13 | WORKDIR /ng-app 14 | 15 | COPY . . 16 | 17 | ## Build the angular app in production mode and store the artifacts in dist folder 18 | RUN $(npm bin)/ng build --environment=docker --prod --build-optimizer 19 | 20 | ### STAGE 2: Setup ### 21 | 22 | FROM nginx:1.13.3-alpine 23 | 24 | ## Copy our default nginx config 25 | COPY nginx/default.conf /etc/nginx/conf.d/ 26 | 27 | ## Remove default nginx website 28 | RUN rm -rf /usr/share/nginx/html/* 29 | 30 | ## From 'builder' stage copy over the artifacts in dist folder to default nginx public folder 31 | COPY --from=builder /ng-app/dist /usr/share/nginx/html 32 | 33 | CMD ["nginx", "-g", "daemon off;"] 34 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # Frontend 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.7.3. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /frontend/e2e/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('frontend App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to app!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /frontend/e2e/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frontend/e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular/cli'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular/cli/plugins/karma') 14 | ], 15 | client:{ 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | reports: [ 'html', 'lcovonly' ], 20 | fixWebpackSourcePaths: true 21 | }, 22 | angularCli: { 23 | environment: 'dev' 24 | }, 25 | reporters: ['progress', 'kjhtml'], 26 | port: 9876, 27 | colors: true, 28 | logLevel: config.LOG_INFO, 29 | autoWatch: true, 30 | browsers: ['Chrome'], 31 | singleRun: false 32 | }); 33 | }; 34 | -------------------------------------------------------------------------------- /frontend/nginx/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | 3 | listen 80; 4 | sendfile on; 5 | 6 | root /usr/share/nginx/html; 7 | resolver 127.0.0.11 valid=30s; 8 | 9 | set $backend http://iky_backend; 10 | 11 | location / { 12 | try_files $uri $uri/ /index.html =404; 13 | } 14 | 15 | location /gateway/ { 16 | rewrite ^/gateway/?(.*)$ /$1 break; 17 | proxy_pass $backend; 18 | proxy_set_header Host $host; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "start": "ng serve", 8 | "build": "ng build --prod", 9 | "test": "ng test", 10 | "lint": "ng lint", 11 | "e2e": "ng e2e" 12 | }, 13 | "private": true, 14 | "dependencies": { 15 | "@angular/animations": "^5.2.9", 16 | "@angular/cdk": "^5.2.4", 17 | "@angular/common": "^5.2.0", 18 | "@angular/compiler": "^5.2.0", 19 | "@angular/core": "^5.2.0", 20 | "@angular/flex-layout": "^5.0.0-beta.14", 21 | "@angular/forms": "^5.2.0", 22 | "@angular/http": "^5.2.0", 23 | "@angular/material": "^5.2.4", 24 | "@angular/platform-browser": "^5.2.0", 25 | "@angular/platform-browser-dynamic": "^5.2.0", 26 | "@angular/router": "^5.2.0", 27 | "core-js": "^2.4.1", 28 | "hammerjs": "^2.0.8", 29 | "rxjs": "^5.5.6", 30 | "zone.js": "^0.8.19" 31 | }, 32 | "devDependencies": { 33 | "@angular/cli": "~1.7.3", 34 | "@angular/compiler-cli": "^5.2.0", 35 | "@angular/language-service": "^5.2.0", 36 | "@types/jasmine": "~2.8.3", 37 | "@types/jasminewd2": "~2.0.2", 38 | "@types/node": "~6.0.60", 39 | "codelyzer": "^4.0.1", 40 | "jasmine-core": "~2.8.0", 41 | "jasmine-spec-reporter": "~4.2.1", 42 | "karma": "~2.0.0", 43 | "karma-chrome-launcher": "~2.2.0", 44 | "karma-coverage-istanbul-reporter": "^1.2.1", 45 | "karma-jasmine": "~1.1.0", 46 | "karma-jasmine-html-reporter": "^0.2.2", 47 | "protractor": "~5.1.2", 48 | "ts-node": "~4.1.0", 49 | "tslint": "~5.9.1", 50 | "typescript": "~2.5.3" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /frontend/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './e2e/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: 'e2e/tsconfig.e2e.json' 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /frontend/src/app/agent/agent-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | import {IntentsComponent} from './intents/intents.component' 5 | import {IntentComponent} from './intent/intent.component'; 6 | import {TrainComponent} from './train/train.component'; 7 | import {SettingsComponent} from './settings/settings.component'; 8 | import {ChatComponent} from './chat/chat.component'; 9 | import {EntitiesComponent} from './entities/entities.component' 10 | import {EntityComponent} from './entity/entity.component' 11 | import {EntityResolverService} from '../services/entities.service' 12 | import {IntentResolverService} from '../services/intent-resolver.service'; 13 | 14 | const routes: Routes = [ 15 | { path: '', redirectTo: 'intents'}, 16 | { 17 | path: 'intents', component: IntentsComponent, 18 | }, 19 | { 20 | path: 'create-intent', component: IntentComponent, 21 | }, 22 | { 23 | resolve: { 24 | story: IntentResolverService, 25 | }, 26 | path: 'edit-intent/:intent_id', component: IntentComponent, 27 | }, 28 | { 29 | path: 'entities', component: EntitiesComponent, 30 | }, 31 | { 32 | resolve: { 33 | entity: EntityResolverService, 34 | }, 35 | path: 'edit-entity/:entity_id', component: EntityComponent, 36 | }, 37 | { 38 | resolve: { 39 | story: IntentResolverService, 40 | }, 41 | path: 'train-intent/:intent_id', component: TrainComponent, 42 | }, 43 | { 44 | path: 'settings', component: SettingsComponent, 45 | }, 46 | { 47 | path: 'chat', component: ChatComponent, 48 | } 49 | ]; 50 | 51 | 52 | @NgModule({ 53 | imports: [RouterModule.forChild(routes)], 54 | exports: [RouterModule] 55 | }) 56 | export class AgentRoutingModule { } 57 | -------------------------------------------------------------------------------- /frontend/src/app/agent/agent.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { ReactiveFormsModule } from '@angular/forms'; 4 | import { HttpClientModule } from '@angular/common/http'; 5 | import { FormsModule } from '@angular/forms'; 6 | import { FlexLayoutModule } from '@angular/flex-layout'; 7 | 8 | import { AgentRoutingModule } from './agent-routing.module'; 9 | import { IntentsComponent } from './intents/intents.component'; 10 | 11 | /* Material UI imports begins here */ 12 | import {MatIconModule,MatCardModule,MatInputModule, 13 | MatOptionModule,MatSelectModule,MatCheckboxModule,MatButtonModule} from '@angular/material'; 14 | import {MatGridListModule} from '@angular/material/grid-list'; 15 | import {MatDividerModule} from '@angular/material/divider'; 16 | import {MatExpansionModule} from '@angular/material/expansion'; 17 | import {MatSliderModule} from '@angular/material/slider'; 18 | import {MatChipsModule} from '@angular/material/chips'; 19 | import {MatAutocompleteModule} from '@angular/material/autocomplete'; 20 | import {MatToolbarModule} from '@angular/material/toolbar'; 21 | import {MatTooltipModule} from '@angular/material/tooltip'; 22 | /* Material UI imports ends here */ 23 | 24 | 25 | /* Services imports begins here */ 26 | import { IntentService } from '../services/intent.service'; 27 | import {TrainingService} from '../services/training.service' 28 | import {IntentResolverService} from '../services/intent-resolver.service'; 29 | import {ChatService} from '../services/chat.service' 30 | import {AgentsService} from '../services/agents.service' 31 | import {EntitiesService,EntityResolverService} from '../services/entities.service' 32 | 33 | /* Services imports ends here */ 34 | 35 | import { SettingsComponent } from './settings/settings.component'; 36 | import { ChatComponent } from './chat/chat.component' 37 | import { IntentComponent } from './intent/intent.component'; 38 | import { TrainComponent } from './train/train.component'; 39 | 40 | import { AutofocusDirective } from '../directives/autofocus.directive'; 41 | import { EntitiesComponent } from './entities/entities.component'; 42 | import { EntityComponent } from './entity/entity.component'; 43 | 44 | @NgModule({ 45 | imports: [ 46 | CommonModule, 47 | ReactiveFormsModule, 48 | HttpClientModule, 49 | FormsModule, 50 | FlexLayoutModule, 51 | 52 | AgentRoutingModule, 53 | 54 | MatIconModule, 55 | MatCardModule, 56 | MatInputModule, 57 | MatOptionModule, 58 | MatSelectModule, 59 | MatCheckboxModule, 60 | MatButtonModule, 61 | MatGridListModule, 62 | MatDividerModule, 63 | MatExpansionModule, 64 | MatSliderModule, 65 | MatChipsModule, 66 | MatAutocompleteModule, 67 | MatToolbarModule, 68 | MatTooltipModule 69 | 70 | ], 71 | declarations: [IntentsComponent, IntentComponent, TrainComponent, SettingsComponent, 72 | ChatComponent,AutofocusDirective, EntitiesComponent, EntityComponent], 73 | providers:[AgentsService,IntentService, 74 | IntentResolverService,TrainingService,ChatService,EntitiesService,EntityResolverService] 75 | }) 76 | export class AgentModule { } 77 | -------------------------------------------------------------------------------- /frontend/src/app/agent/chat/chat.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 |
6 | 7 |
8 |
9 | {{message.content}} 10 |
11 |
12 | 13 |
14 |
15 | 16 |
17 |
18 | 19 | 20 |
21 |
22 | 23 |
24 | 25 | 26 | 27 |
28 |
29 | 30 | 31 |

POST /api/v1

32 |
{{prettyChatCurrent}}
33 |
34 |
-------------------------------------------------------------------------------- /frontend/src/app/agent/chat/chat.component.scss: -------------------------------------------------------------------------------- 1 | .textright { 2 | text-align: right; 3 | } 4 | 5 | .textleft { 6 | text-align: left; 7 | } 8 | 9 | .textcenter { 10 | text-align: center; 11 | } 12 | 13 | .fullwidth { 14 | width: 100%; 15 | } 16 | mat-card{ 17 | margin: 10px; 18 | 19 | height: 600px; 20 | } 21 | 22 | ::-webkit-scrollbar { 23 | width: 0px; /* remove scrollbar space */ 24 | background: transparent; /* optional: just make scrollbar invisible */ 25 | } 26 | 27 | .chat-container { 28 | overflow-y: auto; 29 | } 30 | 31 | .chat-message { 32 | margin: 10px 0; 33 | min-height: 20px; 34 | 35 | animation: fadeIn 0.5s linear; 36 | animation-fill-mode: both; 37 | 38 | &:after { 39 | display: block; 40 | content: ""; 41 | clear: both; 42 | } 43 | } 44 | 45 | .chat-message-content { 46 | width: auto; 47 | max-width: 85%; 48 | display: inline-block; 49 | 50 | padding: 7px 13px; 51 | border-radius: 15px; 52 | color: #595a5a; 53 | background-color: #eeeeee; 54 | 55 | &.human { 56 | float: right; 57 | color: #f7f8f8; 58 | background-color: #3392fb; 59 | } 60 | } 61 | 62 | 63 | .json-response{ 64 | background-color:#efefef; 65 | pre{ 66 | height: auto; 67 | height: 500px; 68 | overflow: auto; 69 | background-color: #eeeeee; 70 | word-break: normal !important; 71 | word-wrap: normal !important; 72 | white-space: pre !important; 73 | } 74 | 75 | } 76 | 77 | $total-items: 5; 78 | 79 | // Set delay per List Item 80 | @for $i from 1 through $total-items { 81 | .chat-message:nth-child(#{$i}) { 82 | animation-delay: .25s * $i; 83 | } 84 | } 85 | 86 | // Keyframe animation 87 | @-webkit-keyframes fadeIn { 88 | 0% { 89 | opacity: 0; 90 | } 91 | 75% { 92 | opacity: 0.5; 93 | } 94 | 100% { 95 | opacity: 1; 96 | } 97 | } -------------------------------------------------------------------------------- /frontend/src/app/agent/chat/chat.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input,AfterViewChecked, ElementRef, ViewChild } from '@angular/core'; 2 | import { ChatService } from '../../services/chat.service'; 3 | import { FormBuilder, FormGroup } from '@angular/forms'; 4 | import { trigger,style,transition,animate,keyframes,query,stagger } from '@angular/animations'; 5 | 6 | @Component({ 7 | // tslint:disable-next-line:component-selector 8 | selector: 'app-chat', 9 | templateUrl: './chat.component.html', 10 | styleUrls: ['./chat.component.scss'], 11 | animations: [ 12 | 13 | trigger('listAnimation', [ 14 | transition('* => *', [ 15 | 16 | query(':enter', style({ opacity: 0 }), {optional: true}), 17 | 18 | query(':enter', stagger('500ms', [ 19 | animate('.3s ease-in-out', keyframes([ 20 | style({opacity: 0, offset: 0}), 21 | style({opacity: .5, offset: 0.5}), 22 | style({opacity: 1, offset: 1.0}), 23 | ]))]), {optional: true}) 24 | ]) 25 | ]) 26 | 27 | ] 28 | }) 29 | 30 | export class ChatComponent implements OnInit { 31 | chatInitial; 32 | chatCurrent; 33 | 34 | messages: Message[] = []; 35 | prettyChatCurrent; 36 | 37 | chatForm: FormGroup; 38 | chatFormFields: any; 39 | @ViewChild('scrollMe') private myScrollContainer: ElementRef; 40 | 41 | constructor( 42 | public fb: FormBuilder, 43 | public chatService: ChatService) { 44 | 45 | this.chatFormFields = { 46 | input: [''], 47 | }; 48 | this.chatForm = this.fb.group(this.chatFormFields); 49 | 50 | } 51 | 52 | ngOnInit() { 53 | this.chatInitial = { 54 | 'currentNode': '', 55 | 'complete': null, 'context': {}, 56 | 'parameters': [], 57 | 'extractedParameters': {}, 58 | 'speechResponse': '', 59 | 'intent': {}, 60 | 'input': '/init_conversation', 61 | 'missingParameters': [] 62 | }; 63 | 64 | this.chatService.converse(this.chatInitial) 65 | .then((c: any) => { 66 | c.owner = 'chat'; 67 | this.changeCurrent(c); 68 | 69 | this.render_bubbles(c) 70 | }); 71 | } 72 | 73 | 74 | scrollToBottom(): void { 75 | try { 76 | this.myScrollContainer.nativeElement.scrollTop = this.myScrollContainer.nativeElement.scrollHeight; 77 | } catch(err) { } 78 | } 79 | 80 | render_bubbles(c){ 81 | c.speechResponse.forEach((item, index) => { 82 | if (index == 0){ 83 | this.add_to_messages(item,"chat") 84 | }else{ 85 | setTimeout(()=>{ 86 | this.add_to_messages(item,"chat") 87 | },500) 88 | } 89 | 90 | }); 91 | } 92 | add_to_messages(message,author){ 93 | let new_message = new Message(message,author) 94 | this.messages.push(new_message); 95 | setTimeout(()=>{ 96 | this.scrollToBottom(); 97 | },300) 98 | 99 | } 100 | 101 | changeCurrent(c) { 102 | c.date = new Date(); 103 | this.chatCurrent = c; 104 | this.prettyChatCurrent = JSON ? JSON.stringify(c, null, ' ') : 'your browser doesnt support JSON so cant pretty print'; 105 | } 106 | 107 | send() { 108 | const form = this.chatForm.value; 109 | const sendMessage = { 110 | ... this.chatCurrent, 111 | input: form.input, 112 | owner: 'user' 113 | }; 114 | this.add_to_messages(form.input,"user") 115 | 116 | this.changeCurrent(sendMessage); 117 | this.chatService.converse(sendMessage) 118 | .then((c: any) => { 119 | c.owner = 'chat'; 120 | this.changeCurrent(c); 121 | this.chatForm.reset(); 122 | setTimeout( 123 | ()=>{ 124 | this.render_bubbles(c); 125 | },1000 126 | ) 127 | 128 | }); 129 | 130 | } 131 | 132 | } 133 | 134 | export class Message { 135 | content: string; 136 | author: string; 137 | 138 | constructor(content: string, author: string){ 139 | this.content = content; 140 | this.author = author; 141 | } 142 | } -------------------------------------------------------------------------------- /frontend/src/app/agent/entities/entities.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 |
8 | 9 |
10 |
11 |
12 | {{entity.name}} 13 |
14 |
15 | 18 | 21 |
22 |
23 |
24 |
25 | -------------------------------------------------------------------------------- /frontend/src/app/agent/entities/entities.component.scss: -------------------------------------------------------------------------------- 1 | mat-card{ 2 | margin: 10px; 3 | .full-width { 4 | width: 100%; 5 | } 6 | .entities{ 7 | 8 | margin: 10px; 9 | .entity{ 10 | display: flex; 11 | align-items: center; 12 | 13 | padding:5px; 14 | margin-bottom: 3px; 15 | cursor: pointer; 16 | } 17 | } 18 | .entity-container{ 19 | border-bottom: 1px solid lightgray; 20 | padding:10px; 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /frontend/src/app/agent/entities/entities.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { EntitiesComponent } from './entities.component'; 4 | 5 | describe('EntitiesComponent', () => { 6 | let component: EntitiesComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ EntitiesComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(EntitiesComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/agent/entities/entities.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { EntitiesService } from '../../services/entities.service' 3 | import {Router} from "@angular/router"; 4 | import {UtilsService} from "../../services/utils.service"; 5 | @Component({ 6 | selector: 'app-entities', 7 | templateUrl: './entities.component.html', 8 | styleUrls: ['./entities.component.scss'] 9 | }) 10 | export class EntitiesComponent implements OnInit { 11 | 12 | constructor(private _router: Router, private coreService:UtilsService, private entitiesService: EntitiesService) { } 13 | 14 | entities = [] 15 | 16 | ngOnInit() { 17 | this.entitiesService.getEntities().then( 18 | (result: Array) => { 19 | this.entities = result 20 | } 21 | ) 22 | } 23 | 24 | newEntity(name) { 25 | if (this.entities.find(x => x.name === name)) { 26 | alert("Entity already exist"); 27 | return; 28 | } 29 | this.entitiesService.create_entity({ "name": name }).then( 30 | (result) => { 31 | this.entities.push({ 32 | "_id":{ 33 | "$oid":result["_id"] 34 | }, 35 | "name": name 36 | }) 37 | } 38 | ) 39 | } 40 | 41 | edit(entity) { 42 | this._router.navigate(["/agent/default/edit-entity", entity._id.$oid]) 43 | } 44 | 45 | deleteEntity(id,i){ 46 | if (confirm('Are u sure want to delete this entity?')) { 47 | this.coreService.displayLoader(true); 48 | this.entitiesService.delete_entity(id).then( 49 | () => { 50 | this.entities.splice(i, 1); 51 | this.ngOnInit(); 52 | this.coreService.displayLoader(false); 53 | }); 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /frontend/src/app/agent/entity/entity.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 5 |
6 |
7 | 10 |
11 |
12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 22 |
23 | 24 |
25 |
26 | 27 |

{{value.value}}

28 |
29 | 30 | 31 | 32 | {{synonym}} 33 | cancel 34 | 35 | 37 | 38 | 39 |
40 | 43 |
44 |
45 |
46 | 47 | 48 |
49 | -------------------------------------------------------------------------------- /frontend/src/app/agent/entity/entity.component.scss: -------------------------------------------------------------------------------- 1 | .values{ 2 | margin-top:20px; 3 | } -------------------------------------------------------------------------------- /frontend/src/app/agent/entity/entity.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { EntityComponent } from './entity.component'; 4 | 5 | describe('EntityComponent', () => { 6 | let component: EntityComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ EntityComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(EntityComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/agent/entity/entity.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { ActivatedRoute, Params, Router } from '@angular/router'; 3 | import { EntitiesService } from '../../services/entities.service' 4 | 5 | import {MatChipInputEvent} from '@angular/material'; 6 | import {ENTER, COMMA} from '@angular/cdk/keycodes'; 7 | 8 | @Component({ 9 | selector: 'app-entity', 10 | templateUrl: './entity.component.html', 11 | styleUrls: ['./entity.component.scss'] 12 | }) 13 | export class EntityComponent implements OnInit { 14 | 15 | constructor(private entitiesService: EntitiesService,private _activatedRoute: ActivatedRoute) { } 16 | 17 | separatorKeysCodes = [ENTER, COMMA]; 18 | entity; 19 | ngOnInit() { 20 | 21 | this._activatedRoute.data 22 | .subscribe((data:{entity:any}) => { 23 | console.log("selected entity =>>",data.entity) 24 | this.entity = data.entity 25 | }); 26 | 27 | } 28 | newValue(value){ 29 | this.entity["entity_values"].push({ 30 | "value":value, 31 | "synonyms":[] 32 | }) 33 | } 34 | 35 | add(value_index,event: MatChipInputEvent): void { 36 | let input = event.input; 37 | let value = event.value; 38 | 39 | // Add our fruit 40 | if ((value || '').trim()) { 41 | this.entity.entity_values[value_index].synonyms.push(value); 42 | } 43 | 44 | // Reset the input value 45 | if (input) { 46 | input.value = ''; 47 | } 48 | } 49 | 50 | remove_synonym(value_index,synonym_index): void { 51 | 52 | if (synonym_index >= 0) { 53 | this.entity.entity_values[value_index].synonyms.splice(synonym_index, 1); 54 | } 55 | } 56 | saveEntity(){ 57 | this.entitiesService.saveEntity(this.entity).then( 58 | (result:any)=>{ 59 | console.log("Success: " + JSON.stringify(result)); 60 | } 61 | ) 62 | } 63 | deleteEntityValue(value_index){ 64 | this.entity["entity_values"].splice(value_index,1); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /frontend/src/app/agent/intent/intent.component.html: -------------------------------------------------------------------------------- 1 | 2 |

{{message}}

3 | 4 |
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 |

Parameters 14 | 17 |

18 |
19 |
20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {{ intentTypes[intent] }} 30 | 31 | 32 | 33 | Required 34 | 35 | 36 | 37 | 38 | 41 |
42 |
43 | 44 | 45 |
46 | 47 | 48 |

49 | API trigger 50 |

51 | 52 |
53 |

HTTP Headers 54 | 57 |

58 |
59 |
60 | 61 | 62 | 63 | 64 | 65 | 66 | 69 |
70 | 71 |
72 | 73 |
74 | 75 | 76 | 77 | 78 | 79 | 80 | GET 81 | POST 82 | PUT 83 | DELETE 84 | 85 | 86 |
87 | 88 | JSON payload 89 |
90 | Extracted parameters can be used to build your json. Example, 91 |
 {{ '\{ 
 92 |     "name": \{\{ parameters\[\"name\"\] \}\} 
 93 |  \}' }}
94 |
95 | 96 | 97 | 98 | 99 |
100 |
101 | 102 | 103 | 104 | 105 |

106 | Extracted parameters and api call response can be accessed from speech respone using "parameters" and "result" objects respectively. 107 | Jinja Templating enabled. 108 |

109 | 110 | 111 | 112 | 113 | 114 | 115 |
116 |
-------------------------------------------------------------------------------- /frontend/src/app/agent/intent/intent.component.scss: -------------------------------------------------------------------------------- 1 | mat-card{ 2 | margin: 10px; 3 | .full-width { 4 | width: 100%; 5 | } 6 | .parameter{ 7 | padding:15px; 8 | margin-bottom: 10px; 9 | } 10 | pre{ 11 | background-color:#efefef; 12 | padding: 5px; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /frontend/src/app/agent/intent/intent.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { FormGroup, FormBuilder, FormArray,Validators } from '@angular/forms'; 3 | import { ActivatedRoute, Params, Router } from '@angular/router'; 4 | 5 | import { UtilsService } from '../../services/utils.service'; 6 | import {IntentService } from '../../services/intent.service'; 7 | 8 | @Component({ 9 | selector: 'app-intent', 10 | templateUrl: './intent.component.html', 11 | styleUrls: ['./intent.component.scss'] 12 | }) 13 | export class IntentComponent implements OnInit { 14 | 15 | intent: any; 16 | intentForm: FormGroup; 17 | intentFormFields: any; 18 | 19 | intentTypes; 20 | intentTypesArray; 21 | message; 22 | 23 | constructor( 24 | public fb: FormBuilder, 25 | public coreService: UtilsService, 26 | public intentService: IntentService, 27 | private _activatedRoute: ActivatedRoute, private _router: Router) { 28 | 29 | 30 | this.intentTypes = IntentService.intentTypes; 31 | this.intentTypesArray = Object.keys(this.intentTypes); 32 | 33 | 34 | } 35 | 36 | loadForm(){ 37 | this.intentFormFields = { 38 | _id: [''], 39 | name: [''], 40 | intentId: [''], 41 | userDefined: [true], 42 | speechResponse: [''], 43 | apiTrigger: [false], 44 | apiDetails: this.initApiDetails(), 45 | parameters: this.fb.array( 46 | this.intent && this.intent.parameters ? this.intent.parameters.map(n => { 47 | return this.initParameter(n); 48 | }) : [] 49 | ) 50 | }; 51 | this.intentForm = this.fb.group(this.intentFormFields); 52 | } 53 | 54 | ngOnInit() { 55 | this.loadForm() 56 | 57 | this._activatedRoute.params.subscribe((params: Params) => { 58 | console.log("active agent reached " + params['intent_id']) 59 | }); 60 | 61 | 62 | this._activatedRoute.data 63 | .subscribe((data:{story:any}) => { 64 | console.log("selected intent =>>") 65 | console.log(data.story) 66 | this.intent = data.story; 67 | this.loadForm(); 68 | this.coreService.setDataForm(this.intentForm, this.intentFormFields, this.intent); 69 | }); 70 | 71 | 72 | 73 | } 74 | 75 | addParameter() { 76 | const control = this.intentForm.controls['parameters']; 77 | control.push(this.initParameter()); 78 | } 79 | 80 | initParameter(parameter = null) { 81 | const fields = { 82 | name: ['', Validators.required], 83 | type: ['', Validators.required], 84 | required: [false], 85 | prompt: [''] 86 | }; 87 | const g = this.fb.group(fields); 88 | if (parameter) { 89 | // setdataform 90 | } 91 | return g; 92 | } 93 | 94 | deleteParameter(i) { 95 | const control = this.intentForm.controls['parameters']; 96 | control.removeAt(i); 97 | } 98 | 99 | 100 | initApiDetails(parameter = null) { 101 | const fields = { 102 | isJson: [false], 103 | url: [''], 104 | headers: this.fb.array( 105 | this.intent && this.intent.apiTrigger ? this.intent.apiDetails.headers.map(n => { 106 | return this.initApiHeaders(); 107 | }) : []), 108 | requestType: [''], 109 | jsonData: [''] 110 | }; 111 | const g = this.fb.group(fields); 112 | if (parameter) { 113 | // setdataform 114 | } 115 | return g; 116 | } 117 | initApiHeaders() { 118 | const fields = { 119 | headerKey: ['', Validators.required], 120 | headerValue: ['', Validators.required], 121 | }; 122 | const g = this.fb.group(fields); 123 | return g; 124 | } 125 | 126 | addHeader(){ 127 | const header = this.intentForm.controls["apiDetails"]["controls"]["headers"]; 128 | header.push(this.initApiHeaders()); 129 | 130 | } 131 | deleteHeader(j) { 132 | const control = this.intentForm.controls["apiDetails"]["controls"]["headers"]; 133 | control.removeAt(j); 134 | } 135 | save() { 136 | const form = this.intentForm.value; 137 | if (form._id && form._id.$oid) { 138 | form._id = form._id.$oid; 139 | } 140 | if (!this.apiTrigger()) { 141 | delete form.apiDetails; 142 | } 143 | 144 | this.intentService.saveIntent(form) 145 | .then(c => { 146 | this.message = 'Intent created!'; 147 | this._router.navigate(["/agent/default/edit-intent", c["_id"]]) 148 | }) 149 | } 150 | 151 | apiTrigger() { 152 | return this.intentForm.value.apiTrigger; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /frontend/src/app/agent/intents/intents.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | List of Intents 4 | 5 | 6 | 9 | 10 |
11 | 12 | 13 | 14 | 15 |
16 |
17 | {{intent.name}} 18 |
19 | 20 |
21 | 23 | 26 | 29 |
30 |
31 |
32 | 33 |
-------------------------------------------------------------------------------- /frontend/src/app/agent/intents/intents.component.scss: -------------------------------------------------------------------------------- 1 | mat-card{ 2 | margin:10px; 3 | .intent-container{ 4 | border: 1px solid rgb(221, 221, 221); 5 | border-radius: 5px; 6 | padding:10px; 7 | margin-bottom: 8px; 8 | } 9 | } 10 | .header{ 11 | margin: 10px; 12 | } 13 | .example-spacer { 14 | flex: 1 1 auto; 15 | } 16 | .margin-left{ 17 | margin-left:5px; 18 | } -------------------------------------------------------------------------------- /frontend/src/app/agent/intents/intents.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Inject, Input } from '@angular/core'; 2 | 3 | import { ActivatedRoute, Params, Router } from '@angular/router'; 4 | 5 | import { IntentService } from '../../services/intent.service'; 6 | import {TrainingService} from '../../services/training.service' 7 | import { UtilsService } from '../../services/utils.service'; 8 | 9 | @Component({ 10 | selector: 'app-intents', 11 | templateUrl: './intents.component.html', 12 | styleUrls: ['./intents.component.scss'] 13 | }) 14 | export class IntentsComponent implements OnInit { 15 | 16 | 17 | 18 | intents: any; 19 | 20 | constructor(public intentService: IntentService, private _activatedRoute: ActivatedRoute, 21 | private _router: Router,private trainingService:TrainingService, private coreService: UtilsService) { } 22 | 23 | ngOnInit() { 24 | 25 | this.intentService.getIntents().then((s: any) => { 26 | this.intents = s; 27 | }); 28 | } 29 | 30 | 31 | add() { 32 | this._router.navigate(["/agent/default/create-intent"]) 33 | } 34 | 35 | edit(intent) { 36 | this._router.navigate(["/agent/default/edit-intent", intent._id.$oid]) 37 | } 38 | 39 | train(intent) { 40 | this._router.navigate(["/agent/default/train-intent", intent._id.$oid]) 41 | } 42 | 43 | delete(intent) { 44 | if (confirm('Are u sure want to delete this story?')) { 45 | this.coreService.displayLoader(true); 46 | this.intentService.delete_intent(intent._id.$oid).then((s: any) => { 47 | this.ngOnInit(); 48 | this.coreService.displayLoader(false); 49 | }); 50 | } 51 | } 52 | 53 | trainModels() { 54 | this.coreService.displayLoader(true); 55 | this.trainingService.trainModels().then((s: any) => { 56 | this.coreService.displayLoader(false); 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /frontend/src/app/agent/settings/settings.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | Settings 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 |
12 |

Tune ML

13 | 14 | Intent Detection threshold = {{config_form.value.confidence_threshold | percent}} 15 |
16 |
17 |
18 |

Chat Widget

19 |

Copy and paste the below snippet into your HTML code.

20 |
21 |                     {{ code }}  
22 |             
23 |

24 | In the above code snippet, 25 | replace win.iky_base_url value with your iky installation in following format "ip:port/" and win.chat_context with any global context 26 |

27 | 28 |
29 | 30 |

Import/Export Intents

31 |
32 | 33 |
34 |
35 | 38 |
39 |
40 |
41 | 42 |
43 |
-------------------------------------------------------------------------------- /frontend/src/app/agent/settings/settings.component.scss: -------------------------------------------------------------------------------- 1 | mat-card{ 2 | margin:10px; 3 | .import-group{ 4 | width:500px; 5 | border: 1px solid lightgray; 6 | padding: 5px; 7 | } 8 | 9 | /* Code Styles */ 10 | pre { 11 | white-space: pre-wrap; 12 | white-space: -moz-pre-wrap; 13 | white-space: -o-pre-wrap; 14 | word-wrap: break-word; 15 | 16 | margin: 20px 0; 17 | width: 800px; 18 | max-width: 100%; 19 | outline: none; 20 | white-space: normal; 21 | background-color: #f6f8fa; 22 | border-color: #ccc; 23 | color: #666; 24 | font-size: 15px!important; 25 | padding: 7px; 26 | box-shadow: 1px 2px 3px lightgrey; 27 | } 28 | 29 | code { 30 | font-family: Courier, 'New Courier', monospace; 31 | font-size: 12px; 32 | } 33 | 34 | #codeStyler { 35 | border-radius: 5px; 36 | -moz-border-radius: 5px; 37 | -webkit-border-radius: 5px; 38 | margin: 1em 0; 39 | } 40 | } -------------------------------------------------------------------------------- /frontend/src/app/agent/settings/settings.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SettingsComponent } from './settings.component'; 4 | 5 | describe('SettingsComponent', () => { 6 | let component: SettingsComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SettingsComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SettingsComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/agent/settings/settings.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import { environment } from '../../../environments/environment'; 3 | import { FormGroup, FormBuilder, FormArray } from '@angular/forms'; 4 | import {IntentService} from '../../services/intent.service' 5 | import {AgentsService} from '../../services/agents.service' 6 | import { UtilsService } from '../../services/utils.service'; 7 | 8 | @Component({ 9 | selector: 'app-settings', 10 | templateUrl: './settings.component.html', 11 | styleUrls: ['./settings.component.scss'] 12 | }) 13 | export class SettingsComponent implements OnInit { 14 | 15 | 16 | fileToUpload: File = null; 17 | config_form = this.fb.group({ 18 | "confidence_threshold":[] 19 | }); 20 | 21 | constructor(private intentService:IntentService,private agent_service:AgentsService, 22 | public fb: FormBuilder,private utilsService:UtilsService) { } 23 | 24 | code = ` 25 | 26 | 31 | ` 32 | ngOnInit() { 33 | this.agent_service.get_config().then( 34 | (result)=>{ 35 | this.config_form.setValue(result); 36 | } 37 | ) 38 | } 39 | 40 | threshold_value_changed(){ 41 | this.save_config() 42 | } 43 | save_config(){ 44 | this.agent_service.update_config(this.config_form.value) 45 | console.log(this.config_form.value) 46 | } 47 | 48 | export(){ 49 | window.open(environment.ikyBackend+"intents/export","_blank") 50 | } 51 | handleFileInput(files: FileList) { 52 | this.fileToUpload = files.item(0); 53 | } 54 | 55 | uploadFileToActivity() { 56 | this.utilsService.displayLoader(true) 57 | this.intentService.importIntents(this.fileToUpload).then ((result)=>{ 58 | this.utilsService.displayLoader(false) 59 | console.log(result) 60 | }) 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /frontend/src/app/agent/train/train.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Train your intent

4 |
5 | 6 |
7 | 8 |
9 |

10 | Train your story for possible user inputs. Tag required parameters using mouse and give em labels 11 |

12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
{{example.text}}
32 | 35 | 36 |
37 | 38 |
39 | Select text to add an entity 40 | 41 | 42 | 43 | 44 | 45 | 46 | {{ entity.name }} 47 | 48 | 49 | 50 | 51 | 52 | 53 | Add an entity for 54 | "{{this.selectionInfo.value}}" 55 | 56 | 57 |
58 | 59 |
60 | 61 | 62 |

Entities

63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 76 | 77 |
EntityValue
{{entity.name}}{{entity.value}} 72 | 75 |
78 |
79 |
80 |
81 |
82 | -------------------------------------------------------------------------------- /frontend/src/app/agent/train/train.component.scss: -------------------------------------------------------------------------------- 1 | .round-button{ 2 | border:1px solid;border-radius:5px;padding:5px 3 | } 4 | .round-button:hover{ 5 | cursor: pointer; 6 | background-color: gray; 7 | color:#fff; 8 | } 9 | .add-entity{ 10 | margin-top: 10px; 11 | } 12 | .entity-table{ 13 | padding:5px; 14 | 15 | } 16 | .full-width { 17 | width: 100%; 18 | } 19 | 20 | mat-card{ 21 | margin: 5px; 22 | 23 | } 24 | .mat-accordion { 25 | margin: 5px; 26 | } 27 | 28 | .header{ 29 | padding:5px; 30 | } 31 | #textarea_highlight{ 32 | border-radius: 5px; 33 | padding:10px; 34 | background-color:#efefef; 35 | } 36 | .table-header{ 37 | padding: 10px; 38 | background: #e9eaeb; 39 | } -------------------------------------------------------------------------------- /frontend/src/app/agent/train/train.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, Input } from '@angular/core'; 2 | import { FormGroup, FormBuilder } from '@angular/forms'; 3 | import { ActivatedRoute, Params, Router } from '@angular/router'; 4 | 5 | import {IntentService } from '../../services/intent.service'; 6 | 7 | import { TrainingService } from '../../services/training.service'; 8 | import { EntitiesService } from '../../services/entities.service' 9 | 10 | @Component({ 11 | selector: 'app-train', 12 | templateUrl: './train.component.html', 13 | styleUrls: ['./train.component.scss'] 14 | }) 15 | export class TrainComponent implements OnInit { 16 | 17 | selectionInfo = { 18 | "value":"", 19 | "begin":0, 20 | "end":0 21 | }; 22 | 23 | intentId:string = null ; 24 | trainingData: Array; 25 | newExampleText: String; 26 | newEntityName: String; 27 | 28 | story: any; 29 | entities: Array = []; 30 | 31 | constructor( 32 | public storyService: IntentService, 33 | private _activatedRoute: ActivatedRoute, 34 | private _router: Router, 35 | private trainingService: TrainingService, 36 | private entitiesService: EntitiesService) { 37 | 38 | this.trainingData = [] 39 | 40 | this.newEntityName = null; 41 | } 42 | 43 | ngOnInit() { 44 | this._activatedRoute.params.subscribe((params: Params) => { 45 | console.log("current intent " + params['intent_id']) 46 | this.intentId = params['intent_id'] 47 | this.trainingService.getTrainingData(params['intent_id']).then( 48 | (result: Array)=>{ 49 | this.trainingData = result; 50 | } 51 | ) 52 | 53 | }); 54 | 55 | 56 | this._activatedRoute.data 57 | .subscribe((data:{story:any}) => { 58 | console.log("selected intent =>>") 59 | console.log(data.story) 60 | this.story = data.story; 61 | 62 | }); 63 | 64 | if (this.story) { 65 | if (this.story._id && this.story._id.$oid) { 66 | this.story._id = this.story._id.$oid; 67 | } 68 | } 69 | 70 | this.entitiesService.getEntities().then( 71 | (result: Array) => { 72 | this.entities = result 73 | } 74 | ) 75 | 76 | } 77 | 78 | // 123456 79 | getAnnotatedText(example){ 80 | let text = example.text 81 | example.entities.forEach(entity => { 82 | var key =entity.value; 83 | var regex = new RegExp(key,'g'); 84 | text = text.replace(regex,' '+key+' ' ); 85 | }); 86 | return text 87 | } 88 | // updateValue($event,example_index){ 89 | // this.trainingData[example_index]["text"]=$event.srcElement.outerText; 90 | 91 | // } 92 | 93 | addNewExample(){ 94 | this.trainingData.unshift({ 95 | "text":this.newExampleText, 96 | "entities":[] 97 | }) 98 | this.newExampleText = ""; 99 | } 100 | 101 | deleteExample(example_index){ 102 | this.trainingData.splice(example_index,1) 103 | } 104 | 105 | deleteEntity(example_index,entity_index){ 106 | this.trainingData[example_index].entities.splice(entity_index,1) 107 | 108 | } 109 | 110 | getSelectionInfo():any { 111 | let selection = window.getSelection(); 112 | if (selection.anchorOffset == selection.extentOffset) 113 | return false; 114 | 115 | let result = { 116 | "value":selection.toString(), 117 | } 118 | 119 | if (selection.anchorOffset > selection.extentOffset) 120 | { 121 | result["begin"] = selection.extentOffset; 122 | result["end"] = selection.anchorOffset; 123 | } 124 | else if (selection.anchorOffset < selection.extentOffset){ 125 | result["begin"] = selection.anchorOffset; 126 | result["end"] = selection.extentOffset; 127 | } 128 | 129 | return result; 130 | 131 | } 132 | 133 | addNewEntity(example_index){ 134 | this.trainingData[example_index]["entities"].push({ 135 | "value":this.selectionInfo.value, 136 | "begin":this.selectionInfo.begin, 137 | "end":this.selectionInfo.end, 138 | "name":this.newEntityName 139 | }) 140 | this.newEntityName = null; 141 | } 142 | 143 | annotate(){ 144 | // snap selection to the word 145 | this.snapSelectionToWord(); 146 | let result = this.getSelectionInfo() 147 | if (result) 148 | this.selectionInfo = result; 149 | 150 | console.log(this.selectionInfo); 151 | } 152 | 153 | updateTrainingData(){ 154 | this.trainingService.saveTrainingData(this.intentId,this.trainingData).then(()=>{ 155 | console.log("Success"); 156 | }) 157 | } 158 | 159 | snapSelectionToWord() { 160 | var sel; 161 | 162 | // Check for existence of window.getSelection() and that it has a 163 | // modify() method. IE 9 has both selection APIs but no modify() method. 164 | if (window.getSelection && ((sel = window.getSelection())).modify) { 165 | sel = window.getSelection(); 166 | if (!sel.isCollapsed) { 167 | 168 | // Detect if selection is backwards 169 | var range = document.createRange(); 170 | range.setStart(sel.anchorNode, sel.anchorOffset); 171 | range.setEnd(sel.focusNode, sel.focusOffset); 172 | var backwards = range.collapsed; 173 | range.detach(); 174 | 175 | // modify() works on the focus of the selection 176 | var endNode = sel.focusNode, endOffset = sel.focusOffset; 177 | sel.collapse(sel.anchorNode, sel.anchorOffset); 178 | 179 | var direction = []; 180 | if (backwards) { 181 | direction = ['backward', 'forward']; 182 | } else { 183 | direction = ['forward', 'backward']; 184 | } 185 | 186 | sel.modify("move", direction[0], "character"); 187 | sel.modify("move", direction[1], "word"); 188 | sel.extend(endNode, endOffset); 189 | sel.modify("extend", direction[1], "character"); 190 | sel.modify("extend", direction[0], "word"); 191 | } 192 | } else if ( (sel = (document).selection) && sel.type != "Control") { 193 | var textRange = sel.createRange(); 194 | if (textRange.text) { 195 | textRange.expand("word"); 196 | // Move the end back to not include the word's trailing space(s), 197 | // if necessary 198 | while (/\s$/.test(textRange.text)) { 199 | textRange.moveEnd("character", -1); 200 | } 201 | textRange.select(); 202 | } 203 | } 204 | } 205 | 206 | //place curser at the end of content editable div 207 | placeCaretAtEnd(el) { 208 | el.focus(); 209 | if (typeof window.getSelection != "undefined" 210 | && typeof document.createRange != "undefined") { 211 | var range = document.createRange(); 212 | range.selectNodeContents(el); 213 | range.collapse(false); 214 | var sel = window.getSelection(); 215 | sel.removeAllRanges(); 216 | sel.addRange(range); 217 | } else if (typeof (document.body).createTextRange != "undefined") { 218 | var textRange = (document.body).createTextRange(); 219 | textRange.moveToElementText(el); 220 | textRange.collapse(false); 221 | textRange.select(); 222 | } 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /frontend/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | 5 | import {LayoutComponent} from './dashboard/layout/layout.component' 6 | const routes: Routes = [ 7 | { path: '', redirectTo: 'agent/default', pathMatch: 'full' }, 8 | { 9 | path: 'agent/default', 10 | component: LayoutComponent, 11 | loadChildren: './agent/agent.module#AgentModule' 12 | }, 13 | { 14 | path: '**', 15 | redirectTo: 'agent/default' 16 | } 17 | ]; 18 | 19 | @NgModule({ 20 | imports: [RouterModule.forRoot(routes, {useHash: true})], 21 | exports: [RouterModule] 22 | }) 23 | export class AppRoutingModule { } 24 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /frontend/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | mat-spinner{ 2 | position: fixed; 3 | z-index: 999; 4 | height: 2em; 5 | width: 2em; 6 | overflow: show; 7 | margin: auto; 8 | top: 0; 9 | left: 0; 10 | bottom: 0; 11 | right: 0; 12 | } -------------------------------------------------------------------------------- /frontend/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | import {UtilsService} from './services/utils.service' 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.scss'] 8 | }) 9 | export class AppComponent implements OnInit { 10 | title = 'app'; 11 | showLoader: boolean; 12 | 13 | constructor( 14 | private coreService: UtilsService) { 15 | } 16 | ngOnInit() { 17 | this.coreService.status.subscribe((val: boolean) => { 18 | console.log("value changed") 19 | this.showLoader = val; 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frontend/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | 7 | 8 | 9 | /* Material UI imports begins here */ 10 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 11 | import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; 12 | /* Material UI imports ends here */ 13 | 14 | 15 | /* Project Components imports begins here */ 16 | import {CommonsModule} from './commons/commons.module' 17 | import { DashboardModule } from './dashboard/dashboard.module'; 18 | /* Project Components imports ends here */ 19 | 20 | 21 | @NgModule({ 22 | declarations: [ 23 | AppComponent 24 | ], 25 | imports: [ 26 | BrowserModule, 27 | AppRoutingModule, 28 | BrowserAnimationsModule, 29 | CommonsModule.forRoot(), 30 | DashboardModule, 31 | MatProgressSpinnerModule 32 | ], 33 | providers: [], 34 | bootstrap: [AppComponent] 35 | }) 36 | export class AppModule { } 37 | -------------------------------------------------------------------------------- /frontend/src/app/commons/commons.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule,ModuleWithProviders } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import {UtilsService} from '../services/utils.service' 4 | 5 | @NgModule({ 6 | imports: [ 7 | CommonModule 8 | ], 9 | declarations: [], 10 | providers:[UtilsService] 11 | }) 12 | export class CommonsModule { 13 | 14 | constructor(utilsService: UtilsService){ 15 | 16 | } 17 | static forRoot(): ModuleWithProviders { 18 | return { 19 | ngModule: CommonsModule, 20 | providers: [UtilsService] 21 | }; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/dashboard.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | import { LayoutComponent } from './layout/layout.component'; 4 | import { SidebarComponent } from './sidebar/sidebar.component'; 5 | 6 | import {RouterModule } from '@angular/router'; 7 | 8 | 9 | /* Material UI imports begins here */ 10 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 11 | 12 | import { MatToolbarModule, MatIconModule, 13 | MatListModule,MatSidenavModule, MatFormFieldModule 14 | 15 | } from '@angular/material'; 16 | 17 | 18 | /* Material UI imports ends here */ 19 | 20 | @NgModule({ 21 | imports: [ 22 | CommonModule, 23 | RouterModule, 24 | MatToolbarModule, 25 | MatIconModule, 26 | MatListModule, 27 | MatSidenavModule, 28 | MatFormFieldModule 29 | ], 30 | declarations: [LayoutComponent, SidebarComponent] 31 | }) 32 | export class DashboardModule { } 33 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/layout/layout.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/layout/layout.component.scss: -------------------------------------------------------------------------------- 1 | mat-sidenav{ 2 | width: 350px; 3 | border-right: 1px solid #e7e7e7; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/layout/layout.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { LayoutComponent } from './layout.component'; 4 | 5 | describe('LayoutComponent', () => { 6 | let component: LayoutComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ LayoutComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(LayoutComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/layout/layout.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-layout', 5 | templateUrl: './layout.component.html', 6 | styleUrls: ['./layout.component.scss'] 7 | }) 8 | export class LayoutComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/sidebar/sidebar.component.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 |
7 | 8 | speaker_notes 9 |

Intents

10 |

Create and Manage your Intents

11 |
12 | 13 | playlist_add_check 14 |

Entities

15 |

Entities and Synonyms

16 |
17 | 18 | question_answer 19 |

Chat

20 |

Test and Debug your agent response

21 |
22 | 23 | settings 24 |

Settings

25 |

Tune ML, export data

26 |
27 |
28 | 29 |
30 | 31 | 32 | 33 | 34 | 35 | help_outline 36 |

Github

37 |

Learn, Contribute and support

38 |
39 |
-------------------------------------------------------------------------------- /frontend/src/app/dashboard/sidebar/sidebar.component.scss: -------------------------------------------------------------------------------- 1 | .bottom{ 2 | position: absolute; 3 | width: 100%; 4 | bottom: 0; 5 | height: 120px; 6 | left: 0; 7 | } 8 | 9 | .main-logo{ 10 | padding: 5px; 11 | border-bottom: 1px solid #efefef; 12 | } -------------------------------------------------------------------------------- /frontend/src/app/dashboard/sidebar/sidebar.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SidebarComponent } from './sidebar.component'; 4 | 5 | describe('SidebarComponent', () => { 6 | let component: SidebarComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async(() => { 10 | TestBed.configureTestingModule({ 11 | declarations: [ SidebarComponent ] 12 | }) 13 | .compileComponents(); 14 | })); 15 | 16 | beforeEach(() => { 17 | fixture = TestBed.createComponent(SidebarComponent); 18 | component = fixture.componentInstance; 19 | fixture.detectChanges(); 20 | }); 21 | 22 | it('should create', () => { 23 | expect(component).toBeTruthy(); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/app/dashboard/sidebar/sidebar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-sidebar', 5 | templateUrl: './sidebar.component.html', 6 | styleUrls: ['./sidebar.component.scss'] 7 | }) 8 | export class SidebarComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/directives/autofocus.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { AutofocusDirective } from './autofocus.directive'; 2 | 3 | describe('AutofocusDirective', () => { 4 | it('should create an instance', () => { 5 | const directive = new AutofocusDirective(); 6 | expect(directive).toBeTruthy(); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /frontend/src/app/directives/autofocus.directive.ts: -------------------------------------------------------------------------------- 1 | import { Directive, AfterViewInit, ElementRef } from '@angular/core'; 2 | 3 | @Directive({ 4 | selector: '[appAutofocus]' 5 | }) 6 | export class AutofocusDirective implements AfterViewInit { 7 | 8 | constructor(private el: ElementRef) { 9 | } 10 | 11 | ngAfterViewInit() { 12 | this.el.nativeElement.focus(); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /frontend/src/app/services/agents.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { environment } from '../../environments/environment'; 4 | 5 | @Injectable() 6 | export class AgentsService { 7 | 8 | constructor(public http: HttpClient) { } 9 | 10 | get_config() { 11 | return this.http.get(environment.ikyBackend + `agents/default/config`).toPromise(); 12 | } 13 | update_config(data) { 14 | return this.http.put(environment.ikyBackend + `agents/default/config`, data).toPromise(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/app/services/chat.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { environment } from '../../environments/environment'; 4 | @Injectable() 5 | export class ChatService { 6 | 7 | constructor(private http:HttpClient) { } 8 | 9 | converse(intent, botId = 'default') { 10 | return this.http.post(environment.ikyBackend + `api/v1`, intent).toPromise(); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/app/services/entities.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { environment } from '../../environments/environment'; 4 | @Injectable() 5 | export class EntitiesService { 6 | constructor(public http: HttpClient) { 7 | } 8 | 9 | getEntities() { 10 | return this.http.get(environment.ikyBackend + 'entities/').toPromise(); 11 | } 12 | 13 | getEntity(id) { 14 | return this.http.get(environment.ikyBackend + `entities/${id}`).toPromise(); 15 | } 16 | 17 | saveEntity(entity) { 18 | if (entity._id) { 19 | return this.update_entity(entity); 20 | } else { 21 | delete entity._id; 22 | return this.create_entity(entity); 23 | } 24 | } 25 | 26 | create_entity(entity) { 27 | return this.http.post(environment.ikyBackend + `entities/`, entity).toPromise(); 28 | } 29 | 30 | update_entity(entity) { 31 | return this.http.put(environment.ikyBackend + `entities/${entity._id.$oid}`, entity).toPromise(); 32 | } 33 | 34 | delete_entity(id) { 35 | return this.http.delete(environment.ikyBackend + `entities/${id}`, {}).toPromise(); 36 | } 37 | } 38 | 39 | 40 | import {Resolve, Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; 41 | @Injectable() 42 | export class EntityResolverService implements Resolve { 43 | 44 | constructor(private _router: Router,private entityService: EntitiesService) { } 45 | 46 | resolve(route: ActivatedRouteSnapshot): Promise | boolean { 47 | return new Promise((resolve,reject)=>{ 48 | this.entityService.getEntity(route.params['entity_id']).then( 49 | (result) => { 50 | console.log("intent details resolved"); 51 | resolve(result); 52 | }, 53 | (err)=>{ 54 | new Error("Could'nt get intent details") 55 | } 56 | ) 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /frontend/src/app/services/intent-resolver.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import {Resolve, Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; 3 | import {IntentService } from './intent.service'; 4 | 5 | @Injectable() 6 | export class IntentResolverService implements Resolve { 7 | 8 | constructor(private intentService: IntentService, private _router: Router) { } 9 | 10 | resolve(route: ActivatedRouteSnapshot): Promise | boolean { 11 | return new Promise((resolve,reject)=>{ 12 | this.intentService.getIntent(route.params['intent_id']).then( 13 | (result) => { 14 | console.log("intent details resolved") 15 | resolve(result) 16 | }, 17 | (err)=>{ 18 | new Error("Could'nt get intent details") 19 | } 20 | ) 21 | }); 22 | } 23 | } -------------------------------------------------------------------------------- /frontend/src/app/services/intent.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { environment } from '../../environments/environment'; 4 | 5 | @Injectable() 6 | export class IntentService { 7 | public static intentTypes = { 8 | 'mobile': 'Mobile number', 9 | 'email': 'Email', 10 | 'free_text': 'Free Text', 11 | 'number': 'Number', 12 | 'list': 'List', 13 | }; 14 | 15 | constructor(public http: HttpClient) { 16 | } 17 | 18 | getIntents() { 19 | return this.http.get(environment.ikyBackend + 'intents/').toPromise(); 20 | } 21 | 22 | getIntent(id) { 23 | return this.http.get(environment.ikyBackend + `intents/${id}`).toPromise(); 24 | } 25 | 26 | saveIntent(intent) { 27 | if (intent._id) { 28 | return this.update_intent(intent); 29 | } else { 30 | delete intent._id; 31 | return this.create_intent(intent); 32 | } 33 | } 34 | 35 | create_intent(intent) { 36 | return this.http.post(environment.ikyBackend + `intents/`, intent).toPromise(); 37 | } 38 | 39 | update_intent(intent) { 40 | return this.http.put(environment.ikyBackend + `intents/${intent._id}`, intent).toPromise(); 41 | } 42 | 43 | delete_intent(id) { 44 | return this.http.delete(environment.ikyBackend + `intents/${id}`, {}).toPromise(); 45 | } 46 | 47 | importIntents(fileToUpload: File){ 48 | const formData: FormData = new FormData(); 49 | formData.append('file', fileToUpload, fileToUpload.name); 50 | return this.http 51 | .post(environment.ikyBackend +"intents/import", formData).toPromise(); 52 | } 53 | 54 | } 55 | 56 | -------------------------------------------------------------------------------- /frontend/src/app/services/training.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { HttpClient } from '@angular/common/http'; 3 | import { environment } from '../../environments/environment'; 4 | 5 | @Injectable() 6 | export class TrainingService { 7 | 8 | constructor(public http: HttpClient) {} 9 | 10 | saveTrainingData(intent_id,data) { 11 | return this.http.post(environment.ikyBackend + `train/${intent_id}/data`, data).toPromise(); 12 | } 13 | 14 | getTrainingData(intent_id) { 15 | return this.http.get(environment.ikyBackend + `train/${intent_id}/data`).toPromise(); 16 | } 17 | 18 | trainModels() { 19 | return this.http.post(environment.ikyBackend + `nlu/build_models`, {}).toPromise(); 20 | } 21 | 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /frontend/src/app/services/utils.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { FormGroup } from '@angular/forms'; 3 | import { BehaviorSubject } from 'rxjs/BehaviorSubject'; 4 | 5 | @Injectable() 6 | export class UtilsService { 7 | 8 | public status: BehaviorSubject = new BehaviorSubject(false); 9 | 10 | constructor() { } 11 | 12 | setDataForm(form, keys, data) { 13 | if (!data) { return; } 14 | const formData = {}; 15 | for (const key in keys) { 16 | if (key in data) { 17 | const value = data[key]; 18 | formData[key] = value; 19 | } else { formData[key] = ''; } 20 | } 21 | // (form).setValue(formData, { onlySelf: true }); 22 | (form).patchValue(formData, { onlySelf: true }); 23 | } 24 | 25 | 26 | displayLoader(value: boolean) { 27 | this.status.next(value); 28 | console.log("loader",value) 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /frontend/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/frontend/src/assets/.gitkeep -------------------------------------------------------------------------------- /frontend/src/assets/images/iky-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/frontend/src/assets/images/iky-logo.png -------------------------------------------------------------------------------- /frontend/src/assets/widget/iky_widget.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var config = { 3 | "base_url": window.iky_base_url, 4 | "chat_context": window.chat_context 5 | } 6 | 7 | // Localize jQuery variable 8 | var jQuery; 9 | 10 | /******** Load jQuery if not present *********/ 11 | if (window.jQuery === undefined) { 12 | var script_tag = document.createElement('script'); 13 | script_tag.setAttribute("type", "text/javascript"); 14 | script_tag.setAttribute("src", 15 | "http://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"); 16 | if (script_tag.readyState) { 17 | script_tag.onreadystatechange = function () { // For old versions of IE 18 | if (this.readyState == 'complete' || this.readyState == 'loaded') { 19 | scriptLoadHandler(); 20 | } 21 | }; 22 | } else { 23 | script_tag.onload = scriptLoadHandler; 24 | } 25 | // Try to find the head, otherwise default to the documentElement 26 | (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag); 27 | } else { 28 | // The jQuery version on the window is the one we want to use 29 | jQuery = window.jQuery; 30 | main(config); 31 | } 32 | 33 | /******** Called once jQuery has loaded ******/ 34 | function scriptLoadHandler() { 35 | // Restore $ and window.jQuery to their previous values and store the 36 | // new jQuery in our local jQuery variable 37 | jQuery = window.jQuery.noConflict(true); 38 | // Call our main function 39 | main(config); 40 | } 41 | 42 | /******** Our main function ********/ 43 | function main(config) { 44 | jQuery(document).ready(function ($) { 45 | console.log("received", config) 46 | /******* Load CSS *******/ 47 | var css_link = $("", { 48 | rel: "stylesheet", 49 | type: "text/css", 50 | href: config["base_url"] +"assets/widget/style.css" 51 | }); 52 | css_link.appendTo('head'); 53 | 54 | 55 | content = ` 56 |
57 |
58 |
59 |
60 |
61 |
62 |
    63 |
64 |
65 |
66 | 67 |
68 |
69 |
70 | 71 |
72 |
73 | ` 74 | 75 | $("body").append(content); 76 | 77 | $(".iky_container").hide(); 78 | 79 | 80 | /******* chat begins *******/ 81 | 82 | if (typeof payload == "undefined") { 83 | payload = 84 | { 85 | "currentNode": "", 86 | "complete": true, 87 | "parameters": [], 88 | "extractedParameters": {}, 89 | "missingParameters": [], 90 | "intent": {}, 91 | "context": config["chat_context"], 92 | "input": "init_conversation", 93 | "speechResponse": [] 94 | } 95 | 96 | 97 | } 98 | 99 | $.ajaxSetup({ 100 | headers: { 101 | 'Content-Type': 'application/json', 102 | 'Accept': 'application/json' 103 | } 104 | }); 105 | 106 | var send_req = (userQuery)=> { 107 | showTyping(); 108 | // send request to bot 109 | payload["input"] = userQuery; 110 | 111 | $.post(config["base_url"]+'gateway/api/v1', JSON.stringify(payload)) 112 | .done(((response)=>{ 113 | successRoutes(response);})) 114 | .fail((x,t,m)=>{ 115 | errorRoutes(x,t,m); 116 | }); 117 | 118 | // $.ajax({ 119 | // // url: config["base_url"]+'gateway/api/v1', 120 | // url: 'http://localhost:8080/gateway/api/v1', 121 | // type: 'POST', 122 | // data: JSON.stringify(payload), 123 | // contentType: 'application/json; charset=utf-8', 124 | // datatype: "json", 125 | // success: successRoutes, 126 | // error: errorRoutes 127 | 128 | // }); 129 | return true; 130 | }; 131 | 132 | function showTyping(){ 133 | $("input.iky_user_input_field").prop('disabled', true); 134 | html_data = '
  • '; 135 | $("ul.iky_Chat_container").append(html_data); 136 | scrollToBottom(); 137 | 138 | } 139 | 140 | function hideTyping(){ 141 | $('li#typing-indicator').remove(); 142 | $("input.iky_user_input_field").prop('disabled', false); 143 | } 144 | 145 | 146 | successRoutes = function (response) { 147 | hideTyping(); 148 | var responseObject; 149 | if (typeof response == 'object') { 150 | responseObject = response; 151 | } 152 | else { 153 | var parsedResponse = JSON.parse(response); 154 | responseObject = parsedResponse.responseData; 155 | } 156 | payload = responseObject; 157 | put_text(responseObject); 158 | }; 159 | 160 | errorRoutes = function (x, t, m) { 161 | hideTyping(); 162 | responseObject = payload; 163 | if (t === "timeout") { 164 | responseObject["speechResponse"] = ["Due to band-width constraints, I'm not able to serve you now","please try again later"] 165 | } else { 166 | responseObject["speechResponse"] = ["I'm not able to serve you at the moment"," please try again later"] 167 | } 168 | payload = responseObject; 169 | put_text(responseObject); 170 | }; 171 | 172 | function scrollToBottom() { 173 | $(".iky_Chat_container").stop().animate({ scrollTop: $(".iky_Chat_container")[0].scrollHeight}, 1000); 174 | } 175 | 176 | var put_text = function (bot_say) { 177 | $.each(bot_say["speechResponse"],function (index, data) { 178 | html_data = '
  • '+ data +'
  • '; 179 | $("ul.iky_Chat_container").append(html_data); 180 | }); 181 | scrollToBottom(); 182 | }; 183 | 184 | 185 | 186 | send_req("init_conversation"); 187 | 188 | 189 | $('.iky_user_input_field').keydown(function (e) { 190 | if (e.keyCode == 13) { 191 | userQuery = $(".iky_user_input_field").val(); 192 | $(".iky_user_input_field").val(""); 193 | html_data = '
  • '+userQuery+'
  • '; 194 | $("ul.iky_Chat_container").append(html_data); 195 | send_req(userQuery); 196 | 197 | } 198 | }) 199 | $(".iky_action, .btn_close").click(function(){ 200 | $(".iky_container").toggle(); 201 | $("input.iky_user_input_field").focus(); 202 | }); 203 | 204 | }); 205 | 206 | 207 | } 208 | 209 | })(); // We call our anonymous function immediately -------------------------------------------------------------------------------- /frontend/src/assets/widget/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background: #f6f6f6; 3 | margin: 0; 4 | padding: 0; 5 | font-family: 'Open Sans', sans-serif; 6 | overflow: -moz-scrollbars-none; 7 | position: relative; 8 | } 9 | 10 | .iky_chatbox{ 11 | width: 400px; 12 | position: fixed; 13 | right: 20px; 14 | bottom: 20px; 15 | font-size: 14px; 16 | } 17 | .iky_container{ 18 | width: cacl(100% - 20px); 19 | height: auto; 20 | background: #fff; 21 | position: relative; 22 | height: 450px; 23 | padding: 50px 10px 0; 24 | box-shadow: 0 0 80px -30px #a0a0a0; 25 | border-radius: 10px; 26 | } 27 | .iky_input{ 28 | border-top: 1px solid #ded9d9; 29 | width: 100%; 30 | 31 | } 32 | .iky_input input{ 33 | padding: 10px; 34 | font-size: 20px; 35 | font-weight: lighter; 36 | width: calc(100% - 24px); 37 | border: 0px; 38 | border-radius: 5px; 39 | margin-bottom: 5px; 40 | } 41 | .iky_input input:focus{ 42 | outline: none; 43 | color: #666; 44 | } 45 | .iky_btn_action{ 46 | width: 100%; 47 | position: relative; 48 | /*bottom: -60px;*/ 49 | left: 0; 50 | padding: 0 0; 51 | height: 50px; 52 | 53 | } 54 | .iky_action{ 55 | float: right; 56 | border-radius: 100px; 57 | background:#124E78; 58 | padding: 6px 20px; 59 | border: none; 60 | color: #fff; 61 | font-size: 18px; 62 | line-height: 25px; 63 | margin-top: 20px; 64 | } 65 | .iky_action:focus{ 66 | outline: none; 67 | } 68 | .iky_smile{ 69 | width: 25px; 70 | float: right; 71 | margin-left: 5px 72 | } 73 | 74 | .iky_chat_holder{ 75 | position: relative; 76 | height: 400px; 77 | overflow: hidden; 78 | padding:0; 79 | margin: 0; 80 | } 81 | .iky_Chat_container{ 82 | margin: 0px; 83 | padding:0px 10px 0px 10px; 84 | width: calc(100% - 20px); 85 | height: 100%; 86 | overflow-y: scroll; 87 | list-style-type: none; 88 | } 89 | .iky_Chat_container::-webkit-scrollbar { 90 | width: 0px; /* remove scrollbar space */ 91 | background: transparent; /* optional: just make scrollbar invisible */ 92 | } 93 | 94 | .message_row{ 95 | float: left; 96 | min-height: 20px; 97 | border-radius: 100px; 98 | padding: .5rem 1rem; 99 | margin-bottom: .2rem; 100 | clear: both; 101 | 102 | } 103 | .iky_text{ 104 | width: auto; 105 | max-width: 85%; 106 | display: inline-block; 107 | 108 | padding: 7px 13px; 109 | border-radius: 15px; 110 | color: #595a5a; 111 | background-color: #ebebeb; 112 | 113 | } 114 | .iky_user_text{ 115 | width: auto; 116 | max-width: 85%; 117 | display: inline-block; 118 | 119 | padding: 7px 13px; 120 | border-radius: 15px; 121 | color: #595a5a; 122 | background-color: #ebebeb; 123 | 124 | float: right; 125 | color: #f7f8f8; 126 | background-color: #28635a; 127 | } 128 | 129 | .iky_sugestions{ 130 | width: 370px; 131 | padding: .5rem 0; 132 | height: 60px; 133 | overflow-x: scroll; 134 | overflow-y: hidden; 135 | white-space: nowrap; 136 | overflow: -moz-scrollbars-vertical; 137 | } 138 | .iky_sugestions span{ 139 | background: #f6f6f6; 140 | color: #000; 141 | border-radius: 10rem; 142 | border: 1px dashed #999; 143 | padding: .3rem .5rem; 144 | display: inline-block; 145 | margin-right: .5rem; 146 | 147 | 148 | } 149 | .iky_menu{ 150 | position: absolute; 151 | right: 5px; 152 | top: 5px; 153 | width: 100px; 154 | padding: 5px; 155 | 156 | } 157 | .btn_close{ 158 | text-align: center; 159 | border-radius: 100px; 160 | background: #ccc; 161 | color: #494747; 162 | border: none; 163 | float: right; 164 | outline: none; 165 | border: 1px solid #ccc; 166 | 167 | font-size: 25px; 168 | font-weight: lighter; 169 | display: inline-block; 170 | line-height: 0px; 171 | padding: 11px 5px; 172 | 173 | } 174 | .btn_close:before { 175 | content: "x"; 176 | 177 | } 178 | 179 | .typing-indicator { 180 | background-color: #ebebeb; 181 | will-change: transform; 182 | width: auto; 183 | border-radius:25px; 184 | padding-top: 10px; 185 | display: table; 186 | -webkit-animation: 2s bulge infinite ease-out; 187 | animation: 2s bulge infinite ease-out; 188 | } 189 | .typing-indicator span { 190 | height: 15px; 191 | width: 15px; 192 | float: left; 193 | margin: 0 1px; 194 | background-color: #9E9EA1; 195 | display: block; 196 | border-radius: 50%; 197 | opacity: 0.4; 198 | margin-bottom: 0px; 199 | } 200 | .typing-indicator span:nth-of-type(1) { 201 | -webkit-animation: 1s blink infinite 0.3333s; 202 | animation: 1s blink infinite 0.3333s; 203 | } 204 | .typing-indicator span:nth-of-type(2) { 205 | -webkit-animation: 1s blink infinite 0.6666s; 206 | animation: 1s blink infinite 0.6666s; 207 | } 208 | .typing-indicator span:nth-of-type(3) { 209 | -webkit-animation: 1s blink infinite 0.9999s; 210 | animation: 1s blink infinite 0.9999s; 211 | } 212 | 213 | @-webkit-keyframes blink { 214 | 50% { 215 | opacity: 1; 216 | } 217 | } 218 | 219 | @keyframes blink { 220 | 50% { 221 | opacity: 1; 222 | } 223 | } 224 | @-webkit-keyframes bulge { 225 | 50% { 226 | -webkit-transform: scale(1.05); 227 | transform: scale(1.05); 228 | } 229 | } 230 | @keyframes bulge { 231 | 50% { 232 | -webkit-transform: scale(1.05); 233 | transform: scale(1.05); 234 | } 235 | } -------------------------------------------------------------------------------- /frontend/src/environments/environment.docker.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | ikyBackend: "/gateway/" 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | ikyBackend: "/" 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/src/environments/environment.test.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | ikyBackend: "http://localhost:8080/" 4 | }; 5 | -------------------------------------------------------------------------------- /frontend/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // The file contents for the current environment will overwrite these during build. 2 | // The build system defaults to the dev environment which uses `environment.ts`, but if you do 3 | // `ng build --env=prod` then `environment.prod.ts` will be used instead. 4 | // The list of which env maps to which file can be found in `.angular-cli.json`. 5 | 6 | export const environment = { 7 | production: false, 8 | ikyBackend: "http://localhost:8080/" 9 | }; 10 | -------------------------------------------------------------------------------- /frontend/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/frontend/src/favicon.ico -------------------------------------------------------------------------------- /frontend/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Frontend 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /frontend/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | import 'hammerjs'; 8 | 9 | if (environment.production) { 10 | enableProdMode(); 11 | } 12 | 13 | platformBrowserDynamic().bootstrapModule(AppModule) 14 | .catch(err => console.log(err)); 15 | -------------------------------------------------------------------------------- /frontend/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 38 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 39 | 40 | /** IE10 and IE11 requires the following for the Reflect API. */ 41 | // import 'core-js/es6/reflect'; 42 | 43 | 44 | /** Evergreen browsers require these. **/ 45 | // Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. 46 | import 'core-js/es7/reflect'; 47 | 48 | 49 | /** 50 | * Required to support Web Animations `@angular/platform-browser/animations`. 51 | * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation 52 | **/ 53 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 54 | 55 | /** 56 | * By default, zone.js will patch all possible macroTask and DomEvents 57 | * user can disable parts of macroTask/DomEvents patch by setting following flags 58 | */ 59 | 60 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 61 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 62 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 63 | 64 | /* 65 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 66 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 67 | */ 68 | // (window as any).__Zone_enable_cross_context_check = true; 69 | 70 | /*************************************************************************************************** 71 | * Zone JS is required by default for Angular itself. 72 | */ 73 | import 'zone.js/dist/zone'; // Included with Angular CLI. 74 | 75 | 76 | 77 | /*************************************************************************************************** 78 | * APPLICATION IMPORTS 79 | */ 80 | -------------------------------------------------------------------------------- /frontend/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | @import "~@angular/material/prebuilt-themes/indigo-pink.css"; 3 | 4 | 5 | mat-card.container{ 6 | margin:10px; 7 | } -------------------------------------------------------------------------------- /frontend/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /frontend/src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "baseUrl": "./", 6 | "module": "es2015", 7 | "types": [] 8 | }, 9 | "exclude": [ 10 | "test.ts", 11 | "**/*.spec.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /frontend/src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "baseUrl": "./", 6 | "module": "commonjs", 7 | "types": [ 8 | "jasmine", 9 | "node" 10 | ] 11 | }, 12 | "files": [ 13 | "test.ts" 14 | ], 15 | "include": [ 16 | "**/*.spec.ts", 17 | "**/*.d.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/typings.d.ts: -------------------------------------------------------------------------------- 1 | /* SystemJS module definition */ 2 | declare var module: NodeModule; 3 | interface NodeModule { 4 | id: string; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "outDir": "./dist/out-tsc", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "target": "es5", 11 | "typeRoots": [ 12 | "node_modules/@types" 13 | ], 14 | "lib": [ 15 | "es2017", 16 | "dom" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /frontend/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rulesDirectory": [ 3 | "node_modules/codelyzer" 4 | ], 5 | "rules": { 6 | "arrow-return-shorthand": true, 7 | "callable-types": true, 8 | "class-name": true, 9 | "comment-format": [ 10 | true, 11 | "check-space" 12 | ], 13 | "curly": true, 14 | "deprecation": { 15 | "severity": "warn" 16 | }, 17 | "eofline": true, 18 | "forin": true, 19 | "import-blacklist": [ 20 | true, 21 | "rxjs", 22 | "rxjs/Rx" 23 | ], 24 | "import-spacing": true, 25 | "indent": [ 26 | true, 27 | "spaces" 28 | ], 29 | "interface-over-type-literal": true, 30 | "label-position": true, 31 | "max-line-length": [ 32 | true, 33 | 140 34 | ], 35 | "member-access": false, 36 | "member-ordering": [ 37 | true, 38 | { 39 | "order": [ 40 | "static-field", 41 | "instance-field", 42 | "static-method", 43 | "instance-method" 44 | ] 45 | } 46 | ], 47 | "no-arg": true, 48 | "no-bitwise": true, 49 | "no-console": [ 50 | true, 51 | "debug", 52 | "info", 53 | "time", 54 | "timeEnd", 55 | "trace" 56 | ], 57 | "no-construct": true, 58 | "no-debugger": true, 59 | "no-duplicate-super": true, 60 | "no-empty": false, 61 | "no-empty-interface": true, 62 | "no-eval": true, 63 | "no-inferrable-types": [ 64 | true, 65 | "ignore-params" 66 | ], 67 | "no-misused-new": true, 68 | "no-non-null-assertion": true, 69 | "no-shadowed-variable": true, 70 | "no-string-literal": false, 71 | "no-string-throw": true, 72 | "no-switch-case-fall-through": true, 73 | "no-trailing-whitespace": true, 74 | "no-unnecessary-initializer": true, 75 | "no-unused-expression": true, 76 | "no-use-before-declare": true, 77 | "no-var-keyword": true, 78 | "object-literal-sort-keys": false, 79 | "one-line": [ 80 | true, 81 | "check-open-brace", 82 | "check-catch", 83 | "check-else", 84 | "check-whitespace" 85 | ], 86 | "prefer-const": true, 87 | "quotemark": [ 88 | true, 89 | "single" 90 | ], 91 | "radix": true, 92 | "semicolon": [ 93 | true, 94 | "always" 95 | ], 96 | "triple-equals": [ 97 | true, 98 | "allow-null-check" 99 | ], 100 | "typedef-whitespace": [ 101 | true, 102 | { 103 | "call-signature": "nospace", 104 | "index-signature": "nospace", 105 | "parameter": "nospace", 106 | "property-declaration": "nospace", 107 | "variable-declaration": "nospace" 108 | } 109 | ], 110 | "unified-signatures": true, 111 | "variable-name": false, 112 | "whitespace": [ 113 | true, 114 | "check-branch", 115 | "check-decl", 116 | "check-operator", 117 | "check-separator", 118 | "check-type" 119 | ], 120 | "directive-selector": [ 121 | true, 122 | "attribute", 123 | "app", 124 | "camelCase" 125 | ], 126 | "component-selector": [ 127 | true, 128 | "element", 129 | "app", 130 | "kebab-case" 131 | ], 132 | "no-output-on-prefix": true, 133 | "use-input-property-decorator": true, 134 | "use-output-property-decorator": true, 135 | "use-host-property-decorator": true, 136 | "no-input-rename": true, 137 | "no-output-rename": true, 138 | "use-life-cycle-interface": true, 139 | "use-pipe-transform-interface": true, 140 | "component-class-suffix": true, 141 | "directive-class-suffix": true 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /helm/ai-chatbot-framework/.gitignore: -------------------------------------------------------------------------------- 1 | charts/* 2 | Chart.lock -------------------------------------------------------------------------------- /helm/ai-chatbot-framework/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /helm/ai-chatbot-framework/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: ai-chatbot-framework 3 | description: A python chatbot framework with Natural Language Understanding and Artificial Intelligence. 4 | type: application 5 | version: 0.1.0 6 | appVersion: "1.0.0" 7 | dependencies: 8 | - name: mongodb 9 | version: 10.3.3 10 | repository: "https://charts.bitnami.com/bitnami" 11 | alias: mongodb 12 | condition: mongodb.enabled 13 | 14 | -------------------------------------------------------------------------------- /helm/ai-chatbot-framework/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "ai-chatbot-framework.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "ai-chatbot-framework.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "ai-chatbot-framework.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "ai-chatbot-framework.labels" -}} 37 | helm.sh/chart: {{ include "ai-chatbot-framework.chart" . }} 38 | {{ include "ai-chatbot-framework.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "ai-chatbot-framework.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "ai-chatbot-framework.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the service account to use 55 | */}} 56 | {{- define "ai-chatbot-framework.serviceAccountName" -}} 57 | {{- if .Values.serviceAccount.create }} 58 | {{- default (include "ai-chatbot-framework.fullname" .) .Values.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | -------------------------------------------------------------------------------- /helm/ai-chatbot-framework/templates/deployment-backend.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "ai-chatbot-framework.fullname" . }}-backend 5 | labels: 6 | app: "ai-chatbot-framework" 7 | name: "ai-chatbot-framework-backend" 8 | {{- include "ai-chatbot-framework.labels" . | nindent 4 }} 9 | spec: 10 | replicas: {{ .Values.replicaCount }} 11 | selector: 12 | matchLabels: 13 | app: "ai-chatbot-framework" 14 | name: "ai-chatbot-framework-backend" 15 | template: 16 | metadata: 17 | labels: 18 | app: "ai-chatbot-framework" 19 | name: "ai-chatbot-framework-backend" 20 | spec: 21 | initContainers: 22 | - name: {{ .Chart.Name }}-init 23 | image: "{{ .Values.image.backend.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 24 | imagePullPolicy: {{ .Values.image.pullPolicy }} 25 | command: ["python","manage.py","migrate"] 26 | env: 27 | - name: MONGO_URL 28 | value: {{.Values.mongodb.uri}} 29 | - name: APPLICATION_ENV 30 | value: 'Helm' 31 | containers: 32 | - name: {{ .Chart.Name }} 33 | image: "{{ .Values.image.backend.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 34 | imagePullPolicy: {{ .Values.image.pullPolicy }} 35 | command: ["gunicorn", "run:app" ,"--log-level=debug", "--timeout", "90","--bind", "0.0.0.0:80" ] 36 | env: 37 | - name: MONGO_URL 38 | value: {{.Values.mongodb.uri}} 39 | - name: APPLICATION_ENV 40 | value: 'Helm' 41 | ports: 42 | - name: http 43 | containerPort: 80 44 | protocol: TCP 45 | livenessProbe: 46 | initialDelaySeconds: 120 47 | httpGet: 48 | path: /ready 49 | port: http 50 | readinessProbe: 51 | initialDelaySeconds: 120 52 | httpGet: 53 | path: /ready 54 | port: http 55 | resources: 56 | {{- toYaml .Values.resources.backend | nindent 12 }} 57 | -------------------------------------------------------------------------------- /helm/ai-chatbot-framework/templates/deployment-frontend.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "ai-chatbot-framework.fullname" . }}-frontend 5 | labels: 6 | app: "ai-chatbot-framework" 7 | name: "ai-chatbot-framework-frontend" 8 | {{- include "ai-chatbot-framework.labels" . | nindent 4 }} 9 | spec: 10 | replicas: {{ .Values.replicaCount }} 11 | selector: 12 | matchLabels: 13 | app: "ai-chatbot-framework" 14 | name: "ai-chatbot-framework-frontend" 15 | template: 16 | metadata: 17 | {{- with .Values.podAnnotations }} 18 | {{- end }} 19 | labels: 20 | app: "ai-chatbot-framework" 21 | name: "ai-chatbot-framework-frontend" 22 | spec: 23 | containers: 24 | - name: {{ .Chart.Name }} 25 | image: "{{ .Values.image.frontend.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 26 | imagePullPolicy: {{ .Values.image.pullPolicy }} 27 | command: ["nginx", "-g", "daemon off;"] 28 | ports: 29 | - name: http 30 | containerPort: 80 31 | protocol: TCP 32 | resources: 33 | {{- toYaml .Values.resources.frontend | nindent 12 }} 34 | -------------------------------------------------------------------------------- /helm/ai-chatbot-framework/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: {{ include "ai-chatbot-framework.fullname" . }} 5 | annotations: 6 | nginx.ingress.kubernetes.io/rewrite-target: /$1 7 | labels: 8 | app: "ai-chatbot-framework" 9 | name: "ai-chatbot-framework-ingress" 10 | {{- include "ai-chatbot-framework.labels" . | nindent 4 }} 11 | spec: 12 | ingressClassName: nginx 13 | rules: 14 | - http: 15 | paths: 16 | - pathType: Prefix 17 | path: /(.*) 18 | backend: 19 | service: 20 | name: {{ include "ai-chatbot-framework.fullname" . }}-frontend 21 | port: 22 | number: 80 23 | - pathType: Prefix 24 | path: /gateway/(.*) 25 | backend: 26 | service: 27 | name: {{ include "ai-chatbot-framework.fullname" . }}-backend 28 | port: 29 | number: 80 -------------------------------------------------------------------------------- /helm/ai-chatbot-framework/templates/service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ include "ai-chatbot-framework.fullname" . }}-backend 6 | labels: 7 | app: "ai-chatbot-framework" 8 | name: "ai-chatbot-framework-backend" 9 | {{- include "ai-chatbot-framework.labels" . | nindent 4 }} 10 | spec: 11 | type: {{ .Values.service.type }} 12 | ports: 13 | - port: {{ .Values.service.port }} 14 | targetPort: 80 15 | protocol: TCP 16 | name: http-backend 17 | selector: 18 | name: "ai-chatbot-framework-backend" 19 | 20 | --- 21 | apiVersion: v1 22 | kind: Service 23 | metadata: 24 | name: {{ include "ai-chatbot-framework.fullname" . }}-frontend 25 | labels: 26 | app: "ai-chatbot-framework" 27 | name: "ai-chatbot-framework-frontend" 28 | {{- include "ai-chatbot-framework.labels" . | nindent 4 }} 29 | spec: 30 | type: {{ .Values.service.type }} 31 | ports: 32 | - port: {{ .Values.service.port }} 33 | targetPort: 80 34 | protocol: TCP 35 | name: http-frontend 36 | selector: 37 | name: "ai-chatbot-framework-frontend" -------------------------------------------------------------------------------- /helm/ai-chatbot-framework/values.yaml: -------------------------------------------------------------------------------- 1 | replicaCount: 2 2 | 3 | image: 4 | backend: 5 | repository: "alfredfrancis/ai-chatbot-framework_backend" 6 | frontend: 7 | repository: "alfredfrancis/ai-chatbot-framework_frontend" 8 | pullPolicy: Always 9 | tag: "latest" 10 | 11 | nameOverride: "" 12 | fullnameOverride: "" 13 | 14 | mongodb: 15 | enabled: true 16 | uri: "mongodb://root:root@ai-chatbot-framework-mongodb:27017/ai_chatbot_framework" 17 | auth: 18 | username: root 19 | password: root 20 | database: ai_chatbot_framework 21 | rootPassword: root 22 | service: 23 | name: ai-chatbot-framework-mongodb 24 | port: 27017 25 | 26 | service: 27 | url: ai-chatbot-framework.local 28 | type: ClusterIP 29 | port: 80 30 | 31 | resources: 32 | frontend: 33 | requests: 34 | memory: "64Mi" 35 | cpu: "100m" 36 | limits: 37 | memory: "128Mi" 38 | cpu: "500m" 39 | backend: 40 | requests: 41 | memory: "1024Mi" 42 | cpu: "100m" 43 | limits: 44 | memory: "2048Mi" 45 | cpu: "500m" -------------------------------------------------------------------------------- /logs/.keep: -------------------------------------------------------------------------------- 1 | *.* -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | from flask_script import Manager 2 | from app import create_app 3 | 4 | app = create_app() 5 | 6 | manager = Manager(app) 7 | 8 | @manager.command 9 | def migrate(): 10 | from app.agents.models import Bot 11 | 12 | try: 13 | # create default bot 14 | bot = Bot() 15 | bot.name = "default" 16 | bot.save() 17 | print("Created default bot") 18 | except: 19 | print("Default agent exists.. skipping..") 20 | 21 | 22 | # import some default intents 23 | from app.intents.controllers import import_json 24 | json_file = open("examples/default_intents.json", "r+") 25 | stories = import_json(json_file) 26 | print("Imported {} Stories".format(len(stories))) 27 | 28 | try: 29 | print("Training models..") 30 | from app.nlu.tasks import train_models 31 | train_models() 32 | print("Training models finished..") 33 | except Exception as e: 34 | e = str(e) 35 | if e == "NO_DATA": 36 | e = "load Data first into mongodb. Reffer Readme." 37 | print("Could not train models..skipping.. (reason: {})".format(e)) 38 | 39 | 40 | if __name__ == "__main__": 41 | manager.run() 42 | -------------------------------------------------------------------------------- /model_files/.gitignore: -------------------------------------------------------------------------------- 1 | *.model 2 | *.pkl 3 | *.hd5 4 | *.index 5 | *.meta 6 | checkpoint 7 | intent_* -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | attrs==21.2.0 2 | beautifulsoup4==4.9.3 3 | blis==0.4.1 4 | bs4==0.0.1 5 | catalogue==2.0.6 6 | certifi==2021.5.30 7 | chardet==3.0.4 8 | click==7.1.2 9 | cloudpickle==1.6.0 10 | cymem==2.0.5 11 | spacy==3.2.0 12 | en-core-web-md @ https://github.com/explosion/spacy-models/releases/download/en_core_web_md-3.2.0/en_core_web_md-3.2.0-py3-none-any.whl 13 | Flask==1.1.2 14 | Flask-Cors==3.0.3 15 | flask-mongoengine==0.9.5 16 | Flask-Script==2.0.6 17 | Flask-WTF==0.15.1 18 | gevent==21.1.2 19 | greenlet==1.1.0 20 | gunicorn==20.1.0 21 | html2text==2017.10.4 22 | idna==2.6 23 | itsdangerous==2.0.1 24 | Jinja2==3.0.1 25 | joblib==1.0.1 26 | MarkupSafe==2.0.1 27 | mongoengine==0.23.1 28 | murmurhash==1.0.5 29 | numpy==1.21.1 30 | packaging==21.0 31 | pandas==1.3.1 32 | parsedatetime==2.6 33 | pathy==0.6.0 34 | plac==1.1.3 35 | pluggy==0.6.0 36 | preshed==3.0.5 37 | py==1.10.0 38 | pydantic==1.8.2 39 | pymongo==3.12.0 40 | pyparsing==2.4.7 41 | pytest==3.4.2 42 | python-crfsuite==0.9.7 43 | python-dateutil==2.8.2 44 | pytz==2021.1 45 | requests==2.18.4 46 | scikit-learn==0.24.2 47 | scipy==1.7.0 48 | simplejson==3.13.2 49 | six==1.16.0 50 | smart-open==5.1.0 51 | soupsieve==2.2.1 52 | srsly==2.4.1 53 | thinc==8.0.12 54 | threadpoolctl==2.2.0 55 | tqdm==4.61.2 56 | typer==0.3.2 57 | typing-extensions==3.10.0.0 58 | urllib3==1.22 59 | wasabi==0.8.2 60 | Werkzeug==2.0.1 61 | WTForms==2.3.3 62 | zope.event==4.5.0 63 | zope.interface==5.4.0 64 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from app import create_app 2 | app = create_app() 3 | if __name__ == '__main__': 4 | app.run(host='127.0.0.1', port=8080, debug=True, threaded=True) 5 | -------------------------------------------------------------------------------- /swagger.yaml: -------------------------------------------------------------------------------- 1 | openapi: 3.0.3 2 | info: 3 | title: AI Chatbot Framework 4 | description: |- 5 | This is the conversational API for AI Chatbot Framework 6 | version: 1.0.11 7 | externalDocs: 8 | description: Find out more about AI Chatbot Framework 9 | url: https://github.com/alfredfrancis/ai-chatbot-framework/ 10 | servers: 11 | - url: http://localhost:8080/api 12 | paths: 13 | /v1: 14 | post: 15 | summary: Call the converse API to chat with the bot 16 | operationId: converse 17 | requestBody: 18 | content: 19 | application/json: 20 | schema: 21 | $ref: '#/components/schemas/Converse' 22 | required: true 23 | responses: 24 | '200': 25 | description: Successful operation 26 | content: 27 | application/json: 28 | schema: 29 | $ref: '#/components/schemas/Converse' 30 | '400': 31 | description: Invalid input 32 | components: 33 | schemas: 34 | Converse: 35 | type: object 36 | properties: 37 | currentNode: 38 | type: string 39 | example: ask_name 40 | complete: 41 | type: boolean 42 | example: false 43 | context: 44 | type: object 45 | parameters: 46 | type: array 47 | items: 48 | type: object 49 | properties: 50 | name: 51 | type: string 52 | example: full_name 53 | type: 54 | type: string 55 | example: name 56 | required: 57 | type: boolean 58 | example: true 59 | extractedParameters: 60 | type: object 61 | example: {"country":"india"} 62 | speechResponse: 63 | type: array 64 | items: 65 | example: "Please enter your name" 66 | type: string 67 | intent: 68 | type: object 69 | properties: 70 | object_id: 71 | example: "88ea1ef0-5782-11ed-9b6a-0242ac120002" 72 | type: string 73 | confidence: 74 | type: number 75 | example: 1 76 | id: 77 | type: string 78 | example: "ask_name" 79 | input: 80 | type: string 81 | example: "My name is Alfred" 82 | missingParameters: 83 | type: array 84 | items: 85 | type: string 86 | example: phone -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Polymath1108/Chatbot-builder/7be874edba55a5e4100d29ad8526dde39699fefe/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_nlu.py: -------------------------------------------------------------------------------- 1 | from app import create_app 2 | import unittest 3 | 4 | class TestCase(unittest.TestCase): 5 | def setUp(self): 6 | self.app = create_app('Testing') 7 | self.app_context = self.app.app_context() 8 | self.app_context.push() 9 | self.client = self.app.test_client() 10 | 11 | def tearDown(self): 12 | self.app_context.pop() 13 | 14 | def test_train_nlu(self): 15 | rv = self.client.post('/nlu/build_models', json={}, follow_redirects=True) 16 | assert rv.status_code == 200 17 | 18 | if __name__ == '__main__': 19 | unittest.main() --------------------------------------------------------------------------------