Skip to content

Commit b263dfa

Browse files
author
AdamMiltonBarker
committedJul 16, 2020
Preparing 0.1.0: Chatbot updated
1 parent 63a3620 commit b263dfa

21 files changed

+1734
-0
lines changed
 

‎Chatbot/Chatbot.py

+363
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,363 @@
1+
############################################################################################
2+
#
3+
# Project: Peter Moss Acute Myeloid & Lymphoblastic Leukemia AI Research Project
4+
# Repository: ALL Detection System 2019
5+
# Project: Chatbot
6+
#
7+
# Author: Adam Milton-Barker (AdamMiltonBarker.com)
8+
# Contributors:
9+
# Title: Training Class
10+
# Description: Trains the ALL Detection System 2019 Chatbot.
11+
# License: MIT License
12+
# Last Modified: 2020-07-15
13+
#
14+
# Example Usage:
15+
#
16+
# python3 Chatbot.py TRAIN
17+
# python3 Chatbot.py INPUT
18+
# python3 Chatbot.py LOCAL "Hi how are you?"
19+
# python3 Chatbot.py SERVER
20+
#
21+
############################################################################################
22+
23+
import sys, os, random, json, string, warnings
24+
25+
from flask import Flask, Response, request
26+
from Train import Trainer
27+
28+
from Classes.Helpers import Helpers
29+
from Classes.Data import Data
30+
from Classes.Mitie import Entities
31+
from Classes.Model import Model
32+
from Classes.Context import Context
33+
from Classes.Extensions import Extensions
34+
35+
app = Flask(__name__)
36+
37+
class Chatbot():
38+
""" ALL Detection System 2019 Chatbot Class
39+
40+
The ALL Detection System 2019 Chatbot.
41+
"""
42+
43+
def __init__(self):
44+
""" Initializes the Chatbot class. """
45+
46+
self.isTraining = False
47+
self.ner = None
48+
49+
self.Helpers = Helpers()
50+
51+
self.user = {}
52+
53+
self.LogFile = self.Helpers.setLogFile(self.Helpers.confs["System"]["Logs"]+"NLU/")
54+
self.ChatLogFile = self.Helpers.setLogFile(self.Helpers.confs["System"]["Logs"]+"Chat/")
55+
56+
def initiateSession(self):
57+
""" Initializes a Chatbot sesiion.
58+
59+
Initiates empty guest user session, GeniSys will ask the user
60+
verify their GeniSys user by speaking or typing if it does
61+
not know who it is speaking to.
62+
"""
63+
64+
self.userID = 0
65+
if not self.userID in self.user:
66+
self.user[self.userID] = {}
67+
self.user[self.userID]["history"] = {}
68+
69+
def initNLU(self):
70+
""" Initializes a Chatbot sesiion.
71+
72+
Initiates the NLU setting up the data, NLU / entities models
73+
and required modules such as context and extensions.
74+
"""
75+
76+
self.Data = Data()
77+
self.trainingData = self.Data.loadTrainingData()
78+
self.trainedData = self.Data.loadTrainedData()
79+
80+
self.Model = Model()
81+
self.Context = Context()
82+
self.Extensions = Extensions()
83+
84+
self.restoreData()
85+
self.restoreNER()
86+
self.restoreNLU()
87+
88+
self.initiateSession()
89+
self.setThresholds()
90+
91+
def commandsCallback(self,topic,payload):
92+
""" iotJumpWay callback function.
93+
94+
The callback function that is triggerend in the event of a
95+
command communication from the iotJumpWay.
96+
"""
97+
98+
self.Helpers.logMessage(self.LogFile, "iotJumpWay", "INFO",
99+
"Recieved iotJumpWay Command Data : " + str(payload))
100+
101+
commandData = json.loads(payload.decode("utf-8"))
102+
103+
def restoreData(self):
104+
""" Restores the training data.
105+
106+
Sets the local trained data using data retrieved above
107+
"""
108+
109+
self.trainedWords = self.trainedData["words"]
110+
self.trainedClasses = self.trainedData["classes"]
111+
self.x = self.trainedData["x"]
112+
self.y = self.trainedData["y"]
113+
self.intentMap = self.trainedData["intentMap"][0]
114+
115+
def loadEntityController(self):
116+
""" Initiates the entity extractor class """
117+
118+
self.entityController = Entities()
119+
120+
def restoreNER(self):
121+
""" Loads entity controller and restores the NER model """
122+
123+
self.loadEntityController()
124+
self.ner = self.entityController.restoreNER()
125+
126+
def restoreNLU(self):
127+
""" Restores the NLU model """
128+
129+
self.tmodel = self.Model.buildDNN(self.x, self.y)
130+
131+
def setThresholds(self):
132+
""" Sets thresholds
133+
134+
Sets the threshold for the NLU engine, this can be changed
135+
using arguments to commandline programs or paramters for
136+
API calls.
137+
"""
138+
139+
self.threshold = self.Helpers.confs["NLU"]["Threshold"]
140+
self.entityThrshld = self.Helpers.confs["NLU"]["Mitie"]["Threshold"]
141+
142+
def communicate(self, sentence):
143+
""" Responds to the user
144+
145+
First checks to ensure that the program is not training,
146+
then parses any entities that may be in the intent, then
147+
checks context and extensions before providing a response.
148+
"""
149+
150+
if self.isTraining == False:
151+
152+
parsed, fallback, entityHolder, parsedSentence = self.entityController.parseEntities(
153+
sentence,
154+
self.ner,
155+
self.trainingData
156+
)
157+
158+
classification = self.Model.predict(self.tmodel, parsedSentence,
159+
self.trainedWords, self.trainedClasses)
160+
161+
if len(classification) > 0:
162+
163+
clearEntities = False
164+
theIntent = self.trainingData["intents"][self.intentMap[classification[0][0]]]
165+
166+
if len(entityHolder) and not len(theIntent["entities"]):
167+
clearEntities = True
168+
169+
if(self.Context.checkSessionContext(self.user[self.userID], theIntent)):
170+
171+
if self.Context.checkClearContext(theIntent, 0):
172+
self.user[self.userID]["context"] = ""
173+
174+
contextIn, contextOut, contextCurrent = self.Context.setContexts(theIntent, self.user[self.userID])
175+
176+
if not len(entityHolder) and len(theIntent["entities"]):
177+
response, entities = self.entityController.replaceResponseEntities(random.choice(theIntent["fallbacks"]), entityHolder)
178+
extension, extensionResponses, exEntities = self.Extensions.setExtension(theIntent)
179+
elif clearEntities:
180+
entityHolder = []
181+
response = random.choice(theIntent["responses"])
182+
extension, extensionResponses, exEntities = self.Extensions.setExtension(theIntent)
183+
else:
184+
response, entities = self.entityController.replaceResponseEntities(random.choice(theIntent["responses"]), entityHolder)
185+
extension, extensionResponses, exEntities = self.Extensions.setExtension(theIntent)
186+
187+
if extension != None:
188+
classParts = extension.split(".")
189+
classFolder = classParts[0]
190+
className = classParts[1]
191+
theEntities = None
192+
193+
if exEntities != False:
194+
theEntities = entities
195+
196+
module = __import__(classParts[0]+"."+classParts[1], globals(), locals(), [className])
197+
extensionClass = getattr(module, className)()
198+
response = getattr(extensionClass, classParts[2])(extensionResponses, theEntities)
199+
200+
return {
201+
"Response": "OK",
202+
"ResponseData": [{
203+
"Received": sentence,
204+
"Intent": classification[0][0],
205+
"Confidence": str(classification[0][1]),
206+
"Response": response,
207+
"Context": [{
208+
"In": contextIn,
209+
"Out": contextOut,
210+
"Current": contextCurrent
211+
}],
212+
"Extension": extension,
213+
"Entities": entityHolder
214+
}]
215+
}
216+
217+
else:
218+
219+
self.user[self.userID]["context"] = ""
220+
contextIn, contextOut, contextCurrent = self.Context.setContexts(theIntent, self.user[self.userID])
221+
222+
if fallback and fallback in theIntent and len(theIntent["fallbacks"]):
223+
response, entities = self.entityController.replaceResponseEntities(random.choice(theIntent["fallbacks"]), entityHolder)
224+
extension, extensionResponses = None, []
225+
else:
226+
response, entities = self.entityController.replaceResponseEntities(random.choice(theIntent["responses"]), entityHolder)
227+
extension, extensionResponses, exEntities = self.Extensions.setExtension(theIntent)
228+
229+
if extension != None:
230+
classParts = extension.split(".")
231+
classFolder = classParts[0]
232+
className = classParts[1]
233+
theEntities = None
234+
235+
if exEntities != False:
236+
theEntities = entities
237+
238+
module = __import__(classParts[0]+"."+classParts[1], globals(), locals(), [className])
239+
extensionClass = getattr(module, className)()
240+
response = getattr(extensionClass, classParts[2])(extensionResponses, theEntities)
241+
242+
else:
243+
response = self.entityController.replaceResponseEntities(random.choice(theIntent["responses"]), entityHolder)
244+
if(type(response)==tuple):
245+
response = response[0]
246+
247+
return {
248+
"Response": "OK",
249+
"ResponseData": [{
250+
"Received": sentence,
251+
"Intent": classification[0][0],
252+
"Confidence": str(classification[0][1]),
253+
"Response": response,
254+
"Context": [{
255+
"In": contextIn,
256+
"Out": contextOut,
257+
"Current": contextCurrent
258+
}],
259+
"Extension": extension,
260+
"Entities": entityHolder
261+
}]
262+
}
263+
264+
else:
265+
266+
contextCurrent = self.Context.getCurrentContext(self.user[self.userID])
267+
268+
return {
269+
"Response": "FAILED",
270+
"ResponseData": [{
271+
"Received": sentence,
272+
"Intent": "UNKNOWN",
273+
"Confidence": "NA",
274+
"Responses": [],
275+
"Response": random.choice(self.Helpers.confs["NLU"]["defaultResponses"]),
276+
"Context": [{
277+
"In": "NA",
278+
"Out": "NA",
279+
"Current": contextCurrent
280+
}],
281+
"Extension":"NA",
282+
"Entities": entityHolder
283+
}]
284+
}
285+
else:
286+
287+
return {
288+
"Response": "FAILED",
289+
"ResponseData": [{
290+
"Status": "Training",
291+
"Message": "NLU Engine is currently training"
292+
}]
293+
}
294+
295+
296+
Chatbot = Chatbot()
297+
298+
@app.route("/infer", methods = ["POST"])
299+
def infer():
300+
""" Inference endpoint
301+
302+
Is triggered when an authorized request is made to the infer
303+
endpoint.
304+
"""
305+
306+
Chatbot.initiateSession()
307+
308+
if request.headers["Content-Type"] == "application/json":
309+
query = request.json
310+
response = Chatbot.communicate(query["query"])
311+
312+
return Response(response=json.dumps(response, indent=4, sort_keys=True),
313+
status=200, mimetype="application/json")
314+
315+
if __name__ == "__main__":
316+
317+
if sys.argv[1] == "TRAIN":
318+
""" Training mode
319+
320+
Is triggered when the 1st commandline line argument is TRAIN.
321+
"""
322+
323+
Train = Trainer()
324+
Train.trainModel()
325+
326+
elif sys.argv[1] == "SERVER":
327+
""" Server mode
328+
329+
Is triggered when the 1st commandline line argument is SERVER.
330+
"""
331+
332+
Chatbot.initNLU()
333+
334+
Chatbot.Helpers.logMessage(
335+
Chatbot.LogFile,
336+
"Inference",
337+
"INFO",
338+
"Inference Started In SERVER Mode")
339+
340+
app.run(host=Chatbot.Helpers.confs["System"]["IP"], port=Chatbot.Helpers.confs["System"]["Port"])
341+
342+
elif sys.argv[1] == "INPUT":
343+
""" Input mode
344+
345+
Is triggered when the 1st commandline line argument is INPUT.
346+
"""
347+
348+
Chatbot.initNLU()
349+
350+
while True:
351+
352+
intent = input(">")
353+
354+
Chatbot.Helpers.logMessage(Chatbot.ChatLogFile,
355+
"Human", "Intent", intent)
356+
357+
response = Chatbot.communicate(intent)
358+
359+
Chatbot.Helpers.logMessage(Chatbot.ChatLogFile, "GeniSys",
360+
"Response", str(response["ResponseData"][0]["Response"]))
361+
362+
Chatbot.Helpers.logMessage(Chatbot.ChatLogFile, "GeniSys",
363+
"Raw Response", str(response["ResponseData"]), True)

0 commit comments

Comments
 (0)