Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intents and slots prompts confirmation #366

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* [#352](https://github.com/alexa-js/alexa-app/pull/352): Allow utterance expansion in custom slot type synonyms - [@daanzu](https://github.com/daanzu).
* [#361](https://github.com/alexa-js/alexa-app/pull/361): Fix object reference for dialogState in in doc - [@Sephtenen](https://github.com/Sephtenen).
* [#364](https://github.com/alexa-js/alexa-app/pull/364): Fix reprompt() to concatenate multiple SSML prompts - [@andrewjhunt](https://github.com/andrewjhunt).
* [#366](https://github.com/alexa-js/alexa-app/pull/358): Add support for including samples in slots and intents for confirmations - [@fabien88](https://github.com/fabien88).
* Your contribution here.

### 4.2.2 (April 7, 2018)
Expand Down
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -622,16 +622,27 @@ The alexa-app module makes it easy to define your intent schema and generate man
### Schema Syntax

Pass an object with two properties: slots and utterances.
A third (optional) property : prompts. To confirm intent on alexa side with dialog delegate.
slot prompts and intents prompts are working only with askcli command.

```javascript
app.intent("sampleIntent", {
"slots": {
"NAME": "AMAZON.US_FIRST_NAME",
"AGE": "AMAZON.NUMBER"
"AGE": "AMAZON.NUMBER",
"CITY": {
"type": "AMAZON.US_CITY",
"elicitationPrompts": [
"Oh I forgot to ask you, in which city do you live ?",
],
"samples": ['I live in {-|CITY}', '{in|at|near|} {-|CITY}'],
"confirmationPrompts": ['Ok so you live in {CITY} right ?'],
}
},
"utterances": [
"my {name is|name's} {NAME} and {I am|I'm} {-|AGE}{ years old|}"
]
],
"prompts": ['Ok do you confirm your name is {NAME}, your age is {AGE} and that you live in {CITY} ?'],
},
function(request, response) { ... }
);
Expand Down Expand Up @@ -747,7 +758,7 @@ WhatsMyColorIntent tell me what my favorite color is

#### Skill Builder Syntax

If you are using the Skill Builder Beta, the `schemas.skillBuilder()` function will generate a single schema JSON string
If you are using the Skill Builder Beta, the `schemas.skillBuilder()` function will generate a single schema JSON string
that includes your intents with all of their utterances

```javascript
Expand Down Expand Up @@ -1056,4 +1067,4 @@ All named apps can be found in the `alexa.apps` object, keyed by name. The value

Copyright (c) 2016-2017 Matt Kruse

MIT License, see [LICENSE](LICENSE.md) for details.
MIT License, see [LICENSE](LICENSE.md) for details.
109 changes: 102 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ alexa.intent = function(name, schema, handler) {
this.dialog = (schema && typeof schema.dialog !== "undefined") ? schema.dialog : {};
this.slots = (schema && typeof schema["slots"] !== "undefined") ? schema["slots"] : null;
this.utterances = (schema && typeof schema["utterances"] !== "undefined") ? schema["utterances"] : null;
this.prompts = schema && typeof schema["prompts"] !== "undefined" ? schema["prompts"] : null;

this.isDelegatedDialog = function() {
return this.dialog.type === "delegate";
Expand Down Expand Up @@ -684,12 +685,26 @@ alexa.app = function(name) {
if (intent.slots && Object.keys(intent.slots).length > 0) {
intentSchema["slots"] = [];
for (key in intent.slots) {
// It's unclear whether `samples` is actually used for slots,
// but the interaction model will not build without an (empty) array
const slot = intent.slots[key];
const type = slot.type ? slot.type : slot;
const samples = [];
if (slot.samples) {
slot.samples.forEach(function(sample) {
var list = AlexaUtterances(
sample,
intent.slots,
self.dictionary,
self.exhaustiveUtterances
);
list.forEach(function(utterance) {
samples.push(utterance);
});
});
}
intentSchema.slots.push({
"name": key,
"type": intent.slots[key],
"samples": []
name: key,
type,
samples
});
}
}
Expand Down Expand Up @@ -733,9 +748,13 @@ alexa.app = function(name) {
if (intent.slots && Object.keys(intent.slots).length > 0) {
intentSchema["slots"] = [];
for (key in intent.slots) {
const slot = intent.slots[key];
const type = slot.type ? slot.type : slot;
const samples = slot.type ? slot.samples : [];
intentSchema.slots.push({
"name": key,
"type": intent.slots[key]
"type": type,
"samples": samples
});
}
}
Expand All @@ -750,16 +769,92 @@ alexa.app = function(name) {
},
askcli: function(invocationName) {
var model = skillBuilderSchema();
var { prompts, dialog } = skillBuilderDialog();
model.invocationName = invocationName || self.invocationName || self.name;
var schema = {
interactionModel: {
languageModel: model
languageModel: model,
dialog,
prompts
}
};
return JSON.stringify(schema, null, 3);
}
};

var skillBuilderDialog = function() {
var schema = {
dialog: {
intents: []
},
prompts: []
},
intentName,
intent,
key;

var creatPrompt = function(promptId, variations) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be named createPrompt, yeah?

return {
id: promptId,
variations: variations.map(prompt => ({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this should check if the user's passed in an already-constructed response object, and if so just use that instead of string-concatenating SSML for them?

I'm not sure if I'm being silly, and maybe there's no reason anyone would want to do that in practice, but seeing SSML being manually constructed like this usually gives me pause.

type: "SSML",
value: "<speak>" + prompt + "</speak>"
}))
};
};

for (intentName in self.intents) {
intent = self.intents[intentName];
var intentSchema = {
name: intent.name,
confirmationRequired: false,
prompts: {},
slots: []
};

if (intent.prompts) {
var intentConfirmPromptId = "Confirm.Intent." + schema.prompts.length;
schema.prompts.push(creatPrompt(intentConfirmPromptId, intent.prompts));
intentSchema.prompts.confirmation = intentConfirmPromptId;
intentSchema.confirmationRequired = true;
}

if (intent.slots && Object.keys(intent.slots).length > 0) {
for (key in intent.slots) {
var slot = intent.slots[key];
var dialogIntentSlot = {
name: key,
type: slot.type || slot,
prompts: {},
elicitationRequired: false,
confirmationRequired: false
};
if (slot.elicitationPrompts) {
var elicitPromptId = "Elicit.Slot." + schema.prompts.length;
schema.prompts.push(
creatPrompt(elicitPromptId, slot.elicitationPrompts)
);
dialogIntentSlot.elicitationRequired = true;
dialogIntentSlot.prompts.elicitation = elicitPromptId;
}
if (slot.confirmationPrompts) {
var confirmPromptId = "Confirm.Slot." + schema.prompts.length;
schema.prompts.push(
creatPrompt(confirmPromptId, slot.confirmationPrompts)
);
dialogIntentSlot.confirmationRequired = true;
dialogIntentSlot.prompts.confirmation = confirmPromptId;
}

intentSchema.slots.push(dialogIntentSlot);
}
}
schema.dialog.intents.push(intentSchema);
}

return schema;
};

// extract the schema and generate a schema JSON object
this.schema = function() {
return this.schemas.intent();
Expand Down
Loading