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

Feature/slot scheme with condition #79

Merged
merged 8 commits into from
Mar 1, 2018
Merged
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
133 changes: 104 additions & 29 deletions functions/actions/music-query.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const debug = require('debug')('ia:actions:music-query:debug');
const warning = require('debug')('ia:actions:music-query:warning');
const _ = require('lodash');
const math = require('mathjs');
const mustache = require('mustache');

const dialog = require('../dialog');
Expand All @@ -16,7 +17,7 @@ const {
} = require('../slots/slots-of-template');
const playlist = require('../state/playlist');
const querySlots = require('../state/query');
const queryDialogScheme = require('../strings').intents.musicQuery;
const availableSchemes = require('../strings').intents.musicQuery;

/**
* Handle music query action
Expand All @@ -35,45 +36,119 @@ function handler (app) {
debug('Start music query handler');

const answer = [];
const newValues = fillSlots(app);
let slotScheme = getActualSlotScheme(availableSchemes, querySlots.getSlots(app));
checkSlotScheme(slotScheme);
let newValues = fillSlots(app, slotScheme);

const complete = querySlots.hasSlots(app, queryDialogScheme.slots);
// new values could change actual slot scheme
const newScheme = getActualSlotScheme(availableSchemes, querySlots.getSlots(app));
if (slotScheme !== newScheme) {
slotScheme = newScheme;
// update slots for new scheme
checkSlotScheme(slotScheme);
newValues = Object.assign({}, newValues, fillSlots(app, slotScheme));
}

const complete = querySlots.hasSlots(app, slotScheme.slots);
if (complete) {
debug('we got all needed slots');
const feeder = feeders.getByName(queryDialogScheme.fulfillment);
return feeder
.build(app, querySlots, playlist)
.then(() => {
if (feeder.isEmpty(app, querySlots, playlist)) {
// TODO: feeder can't find anything by music query
// isn't covered case should be implemented
dialog.ask(
`We haven't find anything by your request would you like something else?`
);
} else {
dialog.playSong(app, feeder.getCurrentItem(app, querySlots, playlist));
}
});
const feeder = feeders.getByName(slotScheme.fulfillment);
if (!feeder) {
// TODO: we should softly fallback here
warning(`we need feeder "${slotScheme.fulfillment}" for fulfillment slot dialog`);
return Promise.resolve();
} else {
return feeder
.build(app, querySlots, playlist)
.then(() => {
if (feeder.isEmpty(app, querySlots, playlist)) {
// TODO: feeder can't find anything by music query
// isn't covered case should be implemented
dialog.ask(
`We haven't find anything by your request would you like something else?`
);
} else {
dialog.playSong(app, feeder.getCurrentItem(app, querySlots, playlist));
}
});
}
}

return generateAcknowledge(app, newValues)
return generateAcknowledge(app, slotScheme, newValues)
.then(res => {
answer.push(res);
return generatePrompt(app);
return generatePrompt(app, slotScheme);
})
.then(res => {
answer.push(res);

const groupedAnswers = groupAnswers(answer);
if (groupedAnswers.speech.length > 0) {
if (groupedAnswers.speech && groupedAnswers.speech.length > 0) {
dialog.ask(app, {
speech: groupedAnswers.speech.join(' '),
suggestions: groupedAnswers.suggestions,
});
} else {
// TODO: we don't have anything to say should warn about it
}
});
}

/**
*
* @param slotScheme
*/
function checkSlotScheme (slotScheme) {
if (!slotScheme) {
throw new Error('There are no valid slot scheme. Need at least default');
}

if (slotScheme && slotScheme.name) {
debug(`we are going with "${slotScheme.name}" slot scheme`);
}
}

/**
* Get valid slot scheme by to meet conditions
*
* @param availableSchemes
* @param slotsState
* @returns {*}
*/
function getActualSlotScheme (availableSchemes, slotsState) {
if (!Array.isArray(availableSchemes)) {
return availableSchemes;
}

return availableSchemes.find((scheme, idx) => {
if (!scheme.conditions) {
// DEFAULT
debug('we get default slot scheme');

// if scheme doesn't have conditions it is default scheme
// usually it is at the end of list

if (idx < availableSchemes.length - 1) {
// if we have schemes after the default one
// we should warn about it
// because we won't never reach schemes after default one
warning('we have schemes after the default one', scheme.name || '');
}
return true;
}

// all conditionals should be valid
try {
return scheme.conditions
.every(condition => math.eval(condition, slotsState));
} catch (error) {
debug(error && error.message);

return false;
}
});
}

/**
* Squeeze array of answer in the single object of arrays
*
Expand Down Expand Up @@ -107,8 +182,8 @@ function groupAnswers (answer) {
* @param app
* @returns {{}}
*/
function fillSlots (app) {
return queryDialogScheme.slots
function fillSlots (app, slotScheme) {
return slotScheme.slots
.reduce((newValues, slotName) => {
const value = app.getArgument(slotName);
if (value) {
Expand All @@ -126,7 +201,7 @@ function fillSlots (app) {
* @param newValues
* @returns {*}
*/
function generateAcknowledge (app, newValues) {
function generateAcknowledge (app, slotScheme, newValues) {
debug('we had slots:', Object.keys(querySlots.getSlots(app)));

const newNames = Object.keys(newValues);
Expand All @@ -138,22 +213,22 @@ function generateAcknowledge (app, newValues) {

debug('and get new slots:', newValues);

const acknowledgeRequirements = extractRequrements(queryDialogScheme.acknowledges);
const acknowledgeRequirements = extractRequrements(slotScheme.acknowledges);

// find the list of acknowledges which match recieved slots
let validAcknowledges = getMatchedTemplatesExactly(
acknowledgeRequirements,
newNames
);

if (validAcknowledges.length === 0) {
if (validAcknowledges && validAcknowledges.length === 0) {
validAcknowledges = getMatchedTemplates(
acknowledgeRequirements,
newNames
);
}

if (validAcknowledges.length === 0) {
if (!validAcknowledges || validAcknowledges.length === 0) {
warning(`there is no valid acknowledges for ${newNames}. Maybe we should write few?`);
return Promise.resolve(null);
}
Expand Down Expand Up @@ -256,9 +331,9 @@ function fetchSuggestions (app, promptScheme) {
* @param app
* @returns {*}
*/
function generatePrompt (app) {
function generatePrompt (app, slotScheme) {
const missedSlots =
queryDialogScheme.slots
slotScheme.slots
.filter(slotName => !querySlots.hasSlot(app, slotName));

if (missedSlots.length === 0) {
Expand All @@ -268,7 +343,7 @@ function generatePrompt (app) {

debug('we missed slots:', missedSlots);
const promptScheme = getPromptsForSlots(
queryDialogScheme.prompts,
slotScheme.prompts,
missedSlots
);

Expand Down
76 changes: 74 additions & 2 deletions functions/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"firebase-functions": "^0.8.1",
"glob": "^7.1.2",
"lodash": "^4.17.5",
"mathjs": "^3.20.2",
"mustache": "^2.3.0",
"node-fetch": "^2.0.0",
"replaceall": "^0.1.6",
Expand Down
Loading