BotFramework passing results from prompt Validator - python

I am using python sdk of botframework for my bot design. I am using waterfall style of dialogs for my conversation design.
My bot starts with a dialog by asking user: "I can show documents for topic A, B, C. Of what topic you would like to see documents?"
To verify if the user has submitted right topic, I use custom Validator and using luis I verify if the user has entered correct topic.
In waterfall step of dialog, I use the topic entered by user to show him the respective topics. But here also I have to hit luis service again to extract the topic from the user message and then using that entity filter from the list of topics.
My question is: Is it possible to pass on the values from the promptValidatorContext to current step context or the next dialog in the waterfall dialog set.
As you can see with following sample code, I am hitting the luis app twice with the same user message, if it's possible to share values between promptValidatorContext and dialogContext, this would me help me avoid hitting luis service twice and could do the same job with one time hitting.
Sample code:
class MainDialog(ComponentDialog):
def __init__(self, dialog_id, luis_app):
self.dialog_id = dialog_id
self.luis_app = luis_app
self.add_dialog(TextPrompt('topic', self.TopicValidator))
self.add_dialog(WaterFallDialog('wf_dialog', [self.Welcome, self.Topic, self.FinalStep])
async def Welcome(self, step_context):
return await step_context.prompt(
'topic',
options = PromptOptions(
prompt = MessageFactory.text('Welcome to the bot, I can show you documents of topic Math, English, Science'),
retry_prompt = MessageFactory.text("I am sorry I didn't understand please try again with different wording")
)
)
async def TopicValidator(self, prompt_context: PromptValidatorContext):
for_luis = prompt_context.recognized.value
#hit the luis app to get the topic name
topic_name = self.luis_app(for_luis)
if topic_name in ['Math', 'Science', 'English']:
return True
else:
return False
async def Topic(self, step_context):
topic_name = self.luis_app(step_context.context.activity.text) #using the same user message as used in Validator function
#filter documents based on topics with custom function filter_doc
docs = filter_doc(topic_name)
return await step_context.prompt('docs', options = PromptOptions(prompt = docs))
async def FinalStep(self, step_context):
#code for final step

I really want to reiterate that I think you should use a choice prompt. Choice prompts are a lot more flexible than you seem to think. The choice recognition is really quite advanced and it is able to recognize a choice in the middle of a phrase and you can even provide synonyms just like in a LUIS list entity. There's an entire folder in the dialogs library dedicated to choice recognition. You think you're giving the user a better experience by using LUIS, but you're actually giving them a worse experience because they can't type an ordinal like 1 or 2 as their choice. Based on what you've told me, I'm certain that a choice prompt is the best option for you.
That being said, here's the information you asked for.
It's common practice to only call any given LUIS endpoint once per turn. If your LUIS model is global for your bot then it would make more sense to call it outside of that dialog, so I'll assume this LUIS model is specific to this dialog.
The most obvious solution would be to store the LUIS result in the turn state. You could store it like this:
prompt_context.context.turn_state[MY_KEY] = self.luis_app(for_luis);
And you could retrieve the value like this:
topic_name = step_context.context.turn_state[MY_KEY];
Another idea would to make luis_app a memoized function (a function that caches its own results). Since the best place for its cache would be the turn state, this isn't so different from the first idea. The main difference is that you'd be changing the luis_app code and leaving your dialog code as it is.
Another idea would be to put your LUIS result in the prompt result. You are free to modify the recognized value from within the validator as you please.
prompt_context.recognized.value = self.luis_app(prompt_context.recognized.value);
That value is accessed in the next step of the waterfall using step_context.result. I should mention that you can also modify the text property of the incoming activity, but that might be considered bad practice.
Finally, you could create your own prompt class that automatically uses a LUIS entity as its recognized value. Perhaps you could call it EntityPrompt or something.

Related

How to find 'user_hash' on telegram?

I'm trying to use telethon and telebot to send notification from my code in python.
I have configureted everythings, but the code ask me the following:
receiver = InputPeerUser('user_id', 'user_hash')
I fund user_id, but I don't understand where I could catch my user_hash.
You must have an open dialogue with that user. Or you received a message from him. When all the dialogs are loaded, the information of that user comes with it.
If you don't have a 'user_hash', just enter 0.
I.e.:
userid=555555555
userhash=0
receiver=InputPeerUser(userid, userhash)
For those requests having a “limit” parameter, you can often set it to
zero to signify “return default amount”. This won’t work for all of
them though, for instance, in “messages.search” it will actually
return 0 items.
Reference:
https://telethonn.readthedocs.io/en/latest/extra/advanced-usage/accessing-the-full-api.html

How can make conversation between bot and user with telepot

I want to create the bot with telepot that ask the users frequent questions.
For example first ask 'whats your name.?' then the user reply 'user-name',then ask how old are you? and the user reply his age and ...
I had written a code for this chat between user and bot,but sometimes I am getting error. Please guide me how can I make this bot with telepot.?
I want to make conversation between bot and users with telepot
I am no longer maintaining this library. Thanks for considering
telepot.
- the maintainer, nickoala
What you're looking for is DelegatorBot.
Consider this tutorial.
Consider this scenario. A bot wants to have an intelligent
conversation with a lot of users, and if we could only use a single
line of execution to handle messages (like what we have done so far),
we would have to maintain some state variables about each conversation
outside the message-handling function(s). On receiving each message,
we first have to check whether the user already has a conversation
started, and if so, what we have been talking about. To avoid such
mundaneness, we need a structured way to maintain “threads” of
conversation.
DelegatorBot provides you with one instance of your bot for every user, so you don't have to think about what happens when multiple users talk to it. (If it helps you, feel free to have a look at how I am using it.)
The tutorial's example is a simple counter of how many messages the user has sent:
import sys
import time
import telepot
from telepot.loop import MessageLoop
from telepot.delegate import pave_event_space, per_chat_id, create_open
class MessageCounter(telepot.helper.ChatHandler):
def __init__(self, *args, **kwargs):
super(MessageCounter, self).__init__(*args, **kwargs)
self._count = 0
def on_chat_message(self, msg):
self._count += 1
self.sender.sendMessage(self._count)
TOKEN = sys.argv[1] # get token from command-line
bot = telepot.DelegatorBot(TOKEN, [
pave_event_space()(
per_chat_id(), create_open, MessageCounter, timeout=10),
])
MessageLoop(bot).run_as_thread()
while 1:
time.sleep(10)
This code creates an instance of MessageCounter for every individual user.
I had written a code for this chat between user and bot,but sometimes I am getting error.
If your question was rather about the errors you're getting than about how to keep a conversation with state, you need to provide more information about what errors you're getting, and when those appear.

How to delete context/session_id at end of conversation in Wit.ai bot

I've been having issues with Wit.ai where my Python bot will retain the context after ending a conversation. This behaviour is the same in the Facebook client and the pywit interactive client.
The conversation starts with a simple 'Hi' and can end at different points within different branches if a user taps a 'Thanks, bye' quick reply after a successful query.
If the conversation is then started with 'Hi' once again, the session state is saved from before which leads to wrong responses. What is the best way to delete the context after the user has said goodbye?
I tried creating a goodbye function that triggers after the bot has sent its final message but it didn't work e.g.
def goodbye(request):
del request['context'] # or request.clear()
return request
The documentation (https://wit.ai/docs/http/20160526#post--converse-link) suggests you clear the session_id and generate a new one but gives no hints as to how.
You can generate new Session Ids using uuid. Session ID has to be any text that is unique, it can even be system date. I suggest you use uuid
Check here as to how to generate it.
I was confronted with the same issue and I solved it in the following way.
I first created a simple end_session action, to be called at the end of each conversation path:
def end_session(request):
return {'end_session': True}
Then I inserted the following code just after returning from run_actions:
if 'end_session' in context:
context = {}
session_hash = uuid.uuid1().hex
As you see, in addition to clearing the context, as you do, I also recreate a new session id (as per Swapnesh Khare's suggestion).
I'm not sure this is the best solution, but it works for me.

Dragon NaturallySpeaking Programmers

Is there anyway to encorporate Dragon NaturallySpeaking into an event driven program? My boss would really like it if I used DNS to record user voice input without writing it to the screen and saving it directly to XML. I've been doing research for several days now and I can not see a way for this to happen without the (really expensive) SDK, I don't even know that it would work then.
Microsoft has the ability to write a (Python) program where it's speech recognizer can wait until it detects a speech event and then process it. It also has the handy quality of being able to suggest alternative phrases to the one that it thinks is the best guess and recording the .wav file for later use. Sample code:
spEngine = MsSpeech()
spEngine.setEventHandler(RecoEventHandler(spEngine.context))
class RecoEventHandler(SpRecoContext):
def OnRecognition(self, StreamNumber, StreamPosition, RecognitionType, Result):
res = win32com.client.Dispatch(Result)
phrase = res.PhraseInfo.GetText()
#from here I would save it as XML
# write reco phrases
altPhrases = reco.Alternates(NBEST)
for phrase in altPhrases:
nodePhrase = self.doc.createElement(TAG_PHRASE)
I can not seem to make DNS do this. The closest I can do-hickey it to is:
while keepGoing == True:
yourWords = raw_input("Your input: ")
transcript_el = createTranscript(doc, "user", yourWords)
speech_el.appendChild(transcript_el)
if yourWords == 'bye':
break
It even has the horrible side effect of making the user say "new-line" after every sentence! Not the preferred solution at all! Is there anyway to make DNS do what Microsoft Speech does?
FYI: I know the logical solution would be to simply switch to Microsoft Speech but let's assume, just for grins and giggles, that that is not an option.
UPDATE - Has anyone bought the SDK? Did you find it useful?
Solution: download Natlink - http://qh.antenna.nl/unimacro/installation/installation.html
It's not quite as flexible to use as SAPI but it covers the basics and I got almost everything that I needed out of it. Also, heads up, it and Python need to be downloaded for all users on your machine or it won't work properly and it works for every version of Python BUT 2.4.
Documentation for all supported commands is found under C:\NatLink\NatLink\MiscScripts\natlink.txt after you download it. It's under all the updates at the top of the file.
Example code:
#make sure DNS is running before you start
if not natlink.isNatSpeakRunning():
raiseError('must start up Dragon NaturallySpeaking first!')
shutdownServer()
return
#connect to natlink and load the grammer it's supposed to recognize
natlink.natConnect()
loggerGrammar = LoggerGrammar()
loggerGrammar.initialize()
if natlink.getMicState() == 'off':
natlink.setMicState('on')
userName = 'Danni'
natlink.openUser(userName)
#natlink.waitForSpeech() continuous loop waiting for input.
#Results are sent to gotResultsObject method of the logger grammar
natlink.waitForSpeech()
natlink.natDisconnect()
The code's severely abbreviated from my production version but I hope you get the idea. Only problem now is that I still have to returned to the mini-window natlink.waitForSpeech() creates to click 'close' before I can exit the program safely. A way to signal the window to close from python without using the timeout parameter would be fantastic.

pyxmpp: quick tutorial for creating a muc client?

I'm attempting to write a quick load-test script for our ejabberd cluster that simply logs into a chat room, posts a couple of random messages, then exits.
We had attempted this particular test with tsung, but according to the authors, the muc functionality did not make it into this release.
pyxmpp seems to have this functionality, but darned if I can figure out how to make it work. Here's hoping someone has a quick explanation of how to build the client and join/post to the muc.
Thanks!
Hey I stumbled over your question a few times, while trying the same thing.
Here is my answer:
Using http://pyxmpp.jajcus.net/svn/pyxmpp/trunk/examples/echobot.py as a quickstart, all you have to do is import the MUC-Stuff
from pyxmpp.jabber.muc import MucRoomState, MucRoomManager
And once your Client is connected, you can connect to your room:
def session_started(self):
"""Handle session started event. May be overriden in derived classes.
This one requests the user's roster and sends the initial presence."""
print u'SESSION STARTED'
self.request_roster()
p=Presence()
self.stream.send(p)
print u'ConnectToParty'
self.connectToMUC()
def connectToMUC(self):
self.roomManager = MucRoomManager(self.stream);
self.roomHandler = MucRoomHandler()
self.roomState = self.roomManager.join(
room=JID('room#conference.server.domain'),
nick='PartyBot',
handler=self.roomHandler,
history_maxchars=0,
password = None)
self.roomManager.set_handlers()
To send a message, all you have to do is call self.roomState.send_message("Sending this Message")
To do stuff, inherit from MucRoomHandler and react on events. Notice the "set_handlers()" to roomManager though, it's is important, otherwise callbacks will not be called..

Categories