We developing a skill it requires multi-turn dialog. However, when alexa is confirming the first slot it throws up saying "There was a problem with the requested skill's response."
The lambda code that alexa calls looks like this.
'DialogIntent': function(){
if (this.event.request.dialogState === "STARTED") {
console.log("in STARTED");
var updatedIntent = this.event.request.intent;
this.emit(":delegate", updatedIntent);
} else if (this.event.request.dialogState !== "COMPLETED") {
console.log("in not completed");
this.emit(":delegate", updatedIntent);
} else {
console.log("in completed");
return this.event.request.intent.slots;
}
return null;
}
we are doing everything suggested in https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html
Can you let us know if we are missing something?
My Answer
You aren't defining the updatedIntent variable during responses when the Dialog state is in any state other than started. To fix this, try moving the updatedIntent declaration to just before the if/else statement.
'DialogIntent': function(){
var updatedIntent = this.event.request.intent;
if (this.event.request.dialogState === "STARTED") {
this.emit(":delegate", updatedIntent);
} else if (this.event.request.dialogState !== "COMPLETED") {
this.emit(":delegate", updatedIntent);
} else {
return this.event.request.intent.slots;
}
return null;
}
The reason this is needed is because each request will set the Dialog to one of three states 1) STARTED which is sent only on the first request of a Dialog, 2) IN_PROGRESS which it is set to in every following request until the dialog is finished, and COMPLETE which it is set to once all of the required slots have been filled and any necessary confirmation is fulfilled.
In your example, you only set updatedIntent on requests with the dialog state set to STARTED, so only on the first request of a dialog. Every request after that will skip the initial step in the if statement, thus never defining the updated intent variable, which you try to pass back to Alexa in the 'else if' statement.
Refactor #1
If you do not need to do any additional set up when the dialog state has just been "STARTED", you can omit that section from the if else statement all together, since you are doing the exact same thing in both === "STARTED and !== "COMPLETED":
'DialogIntent': function(){
var updatedIntent = this.event.request.intent;
if (this.event.request.dialogState !== "COMPLETED") {
this.emit(":delegate", updatedIntent);
} else {
return this.event.request.intent.slots;
}
return null;
}
Refactor #2
You probably don't need to use updatedIntent. All though I am not entirely sure how the Alexa Skills Kit in Node.js works (which I assume is what you're using), you shouldn't need to pass the updatedIntent back to Alexa. The updated intent variable is only needed if for some reason you need to update the intent manually in code. If you don't, Alexa can handle the entire dialog without it:
'DialogIntent': function(){
if (this.event.request.dialogState !== "COMPLETED") {
this.emit(":delegate");
} else {
return this.event.request.intent.slots;
}
return null;
}
Related
Hi currently thinking about a good design to manage multiple output chains in an Alexa skill. For example if I start with one intent called "yesterday" and another one called "today". I want to pass this information (is it "yesterday" I started the chain with or "today") to a NextIntent chain.
Whats the best way to pass information between intents?
Just found out here https://developer.amazon.com/de/blogs/alexa/post/f167aa0f-8abe-4602-b985-65118b3032ca/code-deep-dive-slots-and-session-attributes-in-the-ask-sdk-for-node-js how to do it.
I was searching for session attributes
These can be used like this
1) The first intent I called to start the chain:
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'IntentRequest'
&& handlerInput.requestEnvelope.request.intent.name === 'BesterAutorIntent';
},
handle(handlerInput) {
try {
const speechOutput = "Ich könnte dir sagen wer der beste Autor ist, aber ich muss erst HR fragen ob ich darf";
const attributes = handlerInput.attributesManager.getSessionAttributes();
attributes.kaesebrot = "kaesebrot"
handlerInput.attributesManager.setSessionAttributes(attributes)
return handlerInput.responseBuilder
.speak(speechOutput)
.reprompt()
.withSimpleCard(defaulttext.SKILL_NAME, speechOutput)
.getResponse();
} catch (error) {
console.error(error);
}
},
};
In there you can I set an attribute called kaesebrot to be a session attribute:
const attributes = handlerInput.attributesManager.getSessionAttributes();
attributes.kaesebrot = "kaesebrot"
handlerInput.attributesManager.setSessionAttributes(attributes)
Later in another function you can get it like this
let counter = 0;
const NextIntentHandler = {
canHandle(handlerInput) {
const request = handlerInput.requestEnvelope.request;
return request.type === 'IntentRequest'
&& request.intent.name === 'AMAZON.NextIntent';
},
handle(handlerInput) {
try {
counter = counter + 1;
const attributes = handlerInput.attributesManager.getSessionAttributes();
speechOutput = counter + " TEST " + attributes.kaesebrot;
return handlerInput.responseBuilder
.speak(speechOutput)
.reprompt()
.withSimpleCard(defaulttext.SKILL_NAME, speechOutput)
.getResponse();
} catch (error) {
console.error(error);
}
},
};
Tobi's answer is good.
I am using sdk v1 and the code to keep attributes on dynamoDB is like this:
exports.handler = function( event, context, callback ) {
const alexa = Alexa.handler( event, context );
alexa.dynamoDBTableName = "alexaTable";
alexa.registerHandlers( handlers );
alexa.execute();
};
...
const handlers = {
...
"AMAZON.NextIntent": function () {
console.log( "AMAZON.NextIntent: " + this.attributes.index );
}
...
}
A dynamodb table "alexaTable" will be created the first time the lambda function is invoked and will store and retrieve the attributes automatically.
In the above example "index" is an attribute carried over from a previous intent.
Table schema consists of a key and a field.
Key: userId, alexa skill user id
Field: mapAttr, JSON structured attributes
but it is self-managed by the alexa-sdk.
This post can help further
https://developer.amazon.com/blogs/alexa/post/648c46a1-b491-49bc-902d-d05ecf5c65b4/tips-on-state-management-at-three-different-levels
SessionAttributes is a way to persist state across intents but there's a new feature jsut out of the oven to specifically chain intents and pass slot values.
It's called Intent Chaining and you can see more here:
https://developer.amazon.com/blogs/alexa/post/9ffdbddb-948a-4eff-8408-7e210282ed38/intent-chaining-for-alexa-skill
Intent chaining allows your skill code to start dialog management from any intent, including the LaunchRequest. You can chain into any of your custom intents as long as your interaction model has a dialog model.
For example, from YesterdayIntent, in the response builder you would do:
handlerInput.responseBuilder.addDelegateDirective({
name: 'TodayIntent',
confirmationStatus: 'NONE',
slots: {} // the slot values to pass go here
})
I want to create a simple multi-turn dialog with the Alexa Skill model. My intent consists of 3 slots, each of which are required to fulfill the intent. I prompt every slot and defined all of the needed utterances.
Now I want to handle the request with a Lambda function. This is my function for this specific Intent:
function getData(intentRequest, session, callback) {
if (intentRequest.dialogState != "COMPLETED"){
// return a Dialog.Delegate directive with no updatedIntent property.
} else {
// do my thing
}
}
So how would I go on to build my response with the Dialog.Delegate directive, as mentioned in the Alexa documentation?
https://developer.amazon.com/docs/custom-skills/dialog-interface-reference.html#scenario-delegate
Thank you in advance.
With Dialog.Delegate directive you cannot send outputSpeech or reprompt from your code. Instead those defined in interaction model will be used.
Do not include outputSpeech or reprompt with the Dialog.Directive.
Alexa uses the prompts defined in the dialog model to ask the user for
the slot values and confirmations.
What this means is that you cannot delegate and provide your own response, but instead you can use any other Dialog directive to provide your outputSpeech and reprompt.
Ex: Dialog.ElicitSlot, Dialog.ConfirmSlot and Dialog.ConfirmIntent.
At any point, you can take over the dialog rather than continuing to delegate to Alexa.
...
const updatedIntent = handlerInput.requestEnvelope.request.intent;
if (intentRequest.dialogState != "COMPLETED"){
return handlerInput.responseBuilder
.addDelegateDirective(updatedIntent)
.getResponse();
} else {
// Once dialoState is completed, do your thing.
return handlerInput.responseBuilder
.speak(speechOutput)
.reprompt(reprompt)
.getResponse();
}
...
The updatedIntent parameter in addDelegateDirective() is optional.
It is an intent object representing the intent sent to your skill. You can use this property set or change slot values and confirmation status if necessary.
More on Dialog directives here
In nodejs you can use
if (this.event.request.dialogState != 'COMPLETED'){
//your logic
this.emit(':delegate');
} else {
this.response.speak(message);
this.emit(':responseReady');
}
In nodeJS we can check the dialogState and act accordingly.
if (this.event.request.dialogState === 'STARTED') {
let updatedIntent = this.event.request.intent;
this.emit(':delegate', updatedIntent);
} else if (this.event.request.dialogState != 'COMPLETED'){
if(//logic to elicit slot if any){
this.emit(':elicitSlot', slotToElicit, speechOutput, repromptSpeech);
} else {
this.emit(':delegate');
}
} else {
if(this.event.request.intent.confirmationStatus == 'CONFIRMED'){
//logic for message
this.response.speak(message);
this.emit(':responseReady');
}else{
this.response.speak("Sure, I will not create any new service request");
this.emit(':responseReady');
}
}
I am using async queue to process a huge amount of data. The queue works great until I try to perform an update in the database with MongoDB findOneAndUpdate method.
I first establish the queue, and start pushing objects to it from a Node Stream:
//Create queue to process all items
let q = async.queue(processLink, 2);
// Create Node Stream
let createStream = function () {
let stream = fs.createReadStream(LinkData, {encoding: 'utf8'});
let parser = JSONStream.parse('RECDATA.*');
return stream.pipe(parser);
};
//Listen to 'data' event on stream and add object to queue
createStream().on('data', function(link){
q.push(link)
});
Here is my task function, 'processLink'. It is here, that I am having trouble tracking down the problem. Whenever the findOneAndUpdate callback is fired, it enters one of the conditional blocks, and I am getting the message logged out to the console, but when I call the async callback processComplete(), the task does not finish as expected.
As the title suggests, Why are my async callbacks not completing each task?
function processLink(link, processComplete){
if(_.includes(link.URL, 'www.usda.gov') && _.includes(link.URL, '?recid=')){
let url_items = _.split(link.URL, '=',2);
let facilityOrgID = url_items[1];
let update = {$push: {"links": link}};
if(_.isNumber(parseInt(facilityOrgID)) && facilityOrgID.length > 4 ){
Facility.findOneAndUpdate({facilityOrgID: parseInt(facilityOrgID)}, update, (err, result) => {
if(err !== null){
console.log("Error:",err);
return processComplete(err); /** NOT FIRING **/
} else if(err === null && result !== null){
console.log("Link added to:", result.name);
return processComplete(); /** NOT FIRING **/
}else if(err === null && result === null){
console.log('Facility not in database');
processComplete(); /** NOT FIRING **/
}else{
console.log('Something has gone terrible wrong');
}
});
}else{
console.log("Invalid facilityID");
return processComplete();
}
}else{
console.log('Link Discarded:', link.URL);
processComplete(); /** Fires normally **/
}
}
I solved my problem this morning. The callbacks were fine, but rather there was a conditional state of the data I was not aware of, thus it was leading to a state in which the code would never call the processComplete() callback.
If anyone else is finding themselves in a similar bind, and you have tons of data, I would ensure that your data is being reported accurately and there are not some edge cases that were not being processed as expected.
I am trying to load an image using the fromURL. The issue is that I'd like it to be able to load a default icon if it is not able to reach the Image server to download the image. Looking at the docs I did not see an error callback for the fromURL function. How are we supposed to catch that the call was not successful and therefore do the appropriate thing? It does not seem that the callback gets called at all when image load was unsuccessful.
You can use fabric.util.loadImage() method instead of fabric.Image.fromURL().
If you look at the fromURL() method implementation, internally it uses the loadImage().
The following code may help you:
fabric.util.loadImage('https://s3-eu-west-1.amazonaws.com/kienzle.dev.cors/img/image2.png', function(img) {
if(img == null) {
alert("Error!");
}else {
var image = new fabric.Image(img);
canvas.add(image).setActiveObject(image);
canvas.renderAll();
}
}, { crossOrigin: 'anonymous' });
Here is the fiddle: http://jsfiddle.net/k7moorthi/30kmn5kL/
once you have done the function, even if theres a mistake the callback keeps running, then you could check for the element (as other said) in this way:
let fabricBackgroundInstance = new fabric.Image.fromURL(imageToUse, (oImg) => {
if(oImg._element == null){
console.error('oImg', oImg._element);
return;
}
You could use getElement() to check this error.
fabric.Image.fromURL('/foo.jpg', (img) => {
if (img.getElement() === undefined) {
console.log('Failed to load image!');
return;
}
// do something on success
}
You can add the second argument isError to your callback function.
fabric.Image.fromURL("your image URL", (img, isError) => {
if (isError) {
console.log('Something Wrong with loading image');
return;
}
// do something on success
}
Also check fabric.js source code of Image.fromURL http://fabricjs.com/docs/fabric.js.html#line21471
I'm using a route to process a url. The functions fire as expected. What I'm trying to do is use the response.write() inside of a call back. I understand this isn't working because the call back doesn't have access to the same variables as the function that calls it, but I'm wondering what the correct NODE way of doing this would be.
route.post('/{type}subject/{method}', function (request,response) {
var post = "";
request.on('data', function (chunk){
post += chunk;
});
request.on('end', function (){
postData = qs.parse(post);
response.writeHead(200);
switch(request.params['method'].toLowerCase())
{
case "registerobserver":
if (postData['uri']){
registerObserver (request.params['type'], postData['uri']);
response.write(success("registerobserver"));
}
else
response.write(failure("1","uri undefined"));
break;
case "unregisterobserver":
if (postData['uri']){
client.query ('DELETE observers FROM observers INNER JOIN type ON (observers.TypeID = type.TypeID) WHERE observers.uri ="'+postData['uri']+'" AND type.name = "'+request.params['type']+'"',
function(err, info)
{
if(err){
response.write(failure("2", "general database failure"));}
else{
if(info.affectedRows != 0)
response.write(success("unregisterobserver")); //this code does not trigger a response due to namespace
else
response.write(failure("1", "uri not an observer"));//this code does not trigger a response
console.log("uri not observer");
}
console.log("done");
})
}
else
response.write(failure("1","uri required"));
break;
default:
}
response.end();
})
//response.write("type: "+request.params['type']+"<br/>method="+request.params['method']);
});
function success(method){return "<?xml version=\"1.0\"?>\n<response stat=\"ok\">\n\t<method>"+method+"</method>\n</response>";}
function failure(code, message){return "<?xml version=\"1.0\"?>\n<response stat=\"fail\">\n\t<err code=\""+code+"\" msg = \""+message+"\" />\n</response>";}
Basically what happens is that the async query handler function will be called after your response.end() function call. As such any writes will fail.
You need to call response.end() from inside the callback instead, and take care to not otherwise call response.end() once you are in async code path. Ie. return immediately after the client.query() call.