Source: Nlu.js

/**
 * Copyright (c) 2017 Baidu, Inc. All Rights Reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


'use strict';

const extend = require('node.extend');

/**
 * 封装DuerOS 对query的解析结果
 * 只有IntentRequest 才有Nlu结构
 **/
class Nlu{

    /**
     * 构造函数
     *
     * @param {array} intents IntentRequest 中的intents
     **/
    constructor (intents) {
        this._data = extend(true, intents);
        this.requestIntents = intents;

        /**
         * 记录返回的指令
         **/
        this._directive = null;
    }

    /**
     * 通过槽位名设置一个槽位的值,如果没有此槽位,新增一个
     *
     * @param {string} field 槽位名
     * @param {string} value 值
     * @param {Integer} index 第几个intent,默认第一个
     * @public
     **/
    setSlot (field, value, index = 0) {
        if (!field || !this._data[index]) {
            return; 
        }

        let slots = this._data[index].slots;
        if(!slots) {
            return; 
        }

        if (slots[field]) {
            slots[field].value = value;
        }

        //add a new field
        slots[field] = {
            name: field,
            value: value
        } 
    }

    /**
     * 通过槽位名获取一个槽位的值
     *
     * @param {string} field 槽位名
     * @param {Integer} index 第几个intent,默认第一个
     * @return {null|string}
     * @public
     **/
    getSlot (field, index = 0) {
        if (!field || !this._data[index]) {
            return; 
        }

        let slots = this._data[index].slots;
        if(!slots || !slots[field]) {
            return; 
        }

        return slots[field].value;
    }

    /**
     * 获取DuerOS请求中的意图名
     *
     * @return {string|null}
     * @public
     **/
    getIntentName () {
        if(this._data[0]) {
            return this._data[0].name;
        }
    }

    /**
     * Bot是否在询问用户,等待用户的回复
     *
     * @return {Boolean}
     **/
    hasAsked () {
        return !!this._directive; 
    }

    /**
     * Bot主动发起对一个槽位的询问。比如:打车时询问用户目的地
     *
     * @example
     * this.ask('destination');
     *
     * @param {string} slot 槽位名
     * @return {null} 
     * @public
     **/
    ask (slot) {
        if (!slot) {
            return; 
        }

        this._directive = {
            type: 'Dialog.ElicitSlot',
            slotToElicit: slot,
            updatedIntent: this._getUpdateIntent()
        };
    }

    /**
     * @return {array}
     **/
    toDirective () {
        return this._directive;
    }

    /**
     * @return {Object}
     **/
    toUpdateIntent () {
        return {
            intent: this._data[0]
        }; 
    }

    /**
     * @return {array}
     * @private
     **/
    _getUpdateIntent () {
        return {
            name: this.getIntentName(),
            slots: this._data[0].slots,
        }; 
    }

    /**
     * 设置将对话的处理代理给Dialog Management(DM)。
     *     按事先配置的顺序,包括对缺失槽位的询问,槽位值的确认(如果设置了槽位需要确认,以及确认的话术)
     *     和整个意图的确认(如果设置了意图需要确认,以及确认的话术。比如可以将收集的槽位依次列出,等待用户确认)
     *
     * @public
     **/
    setDelegate () {
        this._directive = {
            type: 'Dialog.Delegate',
            updatedIntent: this._getUpdateIntent()
        };
    }

    /**
     * 主动发起对一个槽位的确认,此时还需同时返回询问的outputSpeach。
     * 主动发起的确认,DM不会使用默认配置的话术。
     *
     * @example
     * this.setConfirmSlot('destination');
     *
     * @param {string} field 槽位名
     * @public
     **/
    setConfirmSlot (field) {
        let slots = this._data[0].slots; 
        
        if(!slots) {
            return; 
        }
        
        if(slots[field]) {
            this._directive = {
                type: 'Dialog.ConfirmSlot',
                slotToConfirm: field,
                updatedIntent: this._getUpdateIntent()
            }; 
        }
    }

    /**
     * 主动发起对一个意图的确认,此时还需同时返回询问的outputSpeach。
     * 主动发起的确认,DM不会使用默认配置的话术。
     * 一般当槽位填槽完毕,在进行下一步操作之前,一次性的询问各个槽位,是否符合用户预期。 
     *
     * @example
     * this.setConfirmIntent();
     *
     * @public
     **/
    setConfirmIntent () {
        this._directive = {
            type: 'Dialog.ConfirmIntent',
            updatedIntent: this._getUpdateIntent()
        };  
    }

    /**
     * 获取槽位的确认状态
     * @desc 获取一个slot对应的confirmationStatus
     * @param {string} field 槽位名
     * @return {string} 槽位的confirmationStatus
     */
    getSlotConfirmationStatus(field, index = 0) {
        if(!field){
            return;
        }

        let slots = this._data[index].slots;
        return slots[field].confirmationStatus;
    }

     /**
     * 获取意图的确认状态
     * @desc 获取一个intent对应的confirmationStatus
     * @return {string 意图的confirmationStatus
     **/
    getIntentConfirmationStatus(index = 0) {
        return this._data[index].confirmationStatus;
    }
}

Nlu.SLOT_NOT_UNDERSTAND = 'da_system_not_understand';

module.exports = Nlu;