asksuite-core
Version:
286 lines (225 loc) • 8.34 kB
JavaScript
const Util = require('./util');
const RequestPreprocessor = require('./util/RequestPreprocessor');
const _ = require('lodash');
const IntentResolver = require('./util/IntentResolver');
const AsksuiteTranslator = require('./util/Translator');
const StringUtils = require('./util/StringUtils');
const AsksuiteUtil = require('./asksuite.util')({});
const DEFAULT_LANGUAGE = 'pt-br';
class AsksuiteProcessingNaturalUtils {
static get EXPIRATION_TIME() {
return 60 * 60 * 24;
} // 24 hrs
constructor(config) {
this.config = config;
if (!_.isEmpty(_.get(this.config, 'redis'))) {
this.redis = this.config.redis;
}
}
withRedis() {
return !_.isEmpty(_.get(this.config, 'redis'));
}
resolveLanguage(paramLanguage, languages) {
let language;
if (languages) {
language = _.find(languages, _.matchesProperty('formatIso', paramLanguage));
if (language) {
return language.formatIso;
}
language = _.find(
languages,
_.matchesProperty('parentLanguage', paramLanguage.substring(0, 2)),
);
if (language) {
return language.formatIso;
}
}
language = paramLanguage.toLowerCase();
if (language.startsWith('pt')) {
return DEFAULT_LANGUAGE;
} else if (language.startsWith('en')) {
return 'en-us';
} else if (language.startsWith('es')) {
return 'es-es';
} else {
return language;
}
}
async getInCacheDialogflow(request) {
if (!this.withRedis()) {
return Promise.resolve(null);
}
const hash = StringUtils.generateHash(request.text);
const language = this.resolveLanguage(request.language, request.languages);
const data = await this.getInCache(`dialogflow:${language}:${hash}`);
if (!data) {
return Promise.resolve(null);
}
const { intent, extractedData, processingStep, parameterFields } = data;
console.log(`OBTAINED BY CACHE ${intent}`);
return { intent, extractedData, processingStep, parameterFields };
}
findDialogByIntent(request, intent) {
const { intents, companyId, text } = request;
const intentResolver = new IntentResolver(intents, companyId, text);
return intentResolver.findDialogByIntent(intent);
}
async saveInCacheDialogflow(intent, request) {
if (!this.withRedis()) {
return Promise.resolve(null);
}
if (Util.isNaoentendi(intent.intent)) {
return Promise.resolve(null);
}
const { text } = request;
const hash = StringUtils.generateHash(text);
const language = this.resolveLanguage(request.language, request.languages);
return this.setInCache(`dialogflow:${language}:${hash}`, {
intent: intent.intent,
extractedData: intent.extractedData,
processingStep: intent.processingStep,
text,
hash,
});
}
async removeInCacheDialogflow({ text, language }) {
if (!this.withRedis()) {
return Promise.resolve(null);
}
const normalizedLanguage = this.resolveLanguage(language);
const hash = StringUtils.generateHash(text);
const key = `dialogflow:${normalizedLanguage}:${hash}`;
return this.removeInCache(key);
}
async removeInCache(key) {
return this.redis.remove(`${key}`);
}
async getInCache(key) {
return this.redis.getValue(`${key}`);
}
async setInCache(key, intent) {
return this.redis.setValueWithExpirationTime(
`${key}`,
intent,
true,
AsksuiteProcessingNaturalUtils.EXPIRATION_TIME,
);
}
async translateRequest(request) {
if (
AsksuiteUtil.resolveLanguageGoogle(request.language, request.languages, 'formatGoogle') !==
AsksuiteUtil.resolveLanguageGoogle(DEFAULT_LANGUAGE, request.languages, 'formatGoogle') &&
!request.translation
) {
request.translated = true;
const asksuiteTranslator = new AsksuiteTranslator(
request.config.INFOCHAT_DATA_SERVER,
request.config.GOOGLE_TRANSLATE_KEY,
request.config.TRANSLATION_API_URL
);
const { translation } = await asksuiteTranslator.translateByTranslationApi(request.text, request.language, 'pt-br', request.languages);
request.translation = translation;
}
}
defineRegexLanguages(languages) {
if (!languages || _.isEmpty(languages)) {
return new RegExp('(pt|en|es|fr)', 'i');
}
const joinedLanguages = _.chain(languages)
.map(formatIso => formatIso.split('-')[0])
.uniq()
.join('|')
.value();
return new RegExp(`(${joinedLanguages})`, 'i');
}
async detectLanguage(request) {
const { GOOGLE_TRANSLATE_KEY } = request.config;
const [detection] = await AsksuiteTranslator.detectLanguage(GOOGLE_TRANSLATE_KEY, request.text);
const detectedLanguageCode = detection.language;
const companyLanguagesRegex = this.defineRegexLanguages(request.companyLanguages);
const isSupportedByCompany = companyLanguagesRegex.test(detectedLanguageCode);
const fallbackLanguageCode = request.defaultLanguage;
const outputLanguageCode = isSupportedByCompany ? detectedLanguageCode : fallbackLanguageCode;
const googleOutputLanguageCode = AsksuiteUtil.resolveLanguageGoogle(
outputLanguageCode,
request.languages,
'formatGoogle',
);
const language = await AsksuiteTranslator.getLanguageByCode(
GOOGLE_TRANSLATE_KEY,
detectedLanguageCode,
googleOutputLanguageCode,
);
const fallbackLanguage = await AsksuiteTranslator.getLanguageByCode(
GOOGLE_TRANSLATE_KEY,
fallbackLanguageCode,
googleOutputLanguageCode,
);
return Object.assign(detection, {
isSupported: isSupportedByCompany,
language,
fallbackLanguage,
});
}
async detectLanguageByTranslationsApi(request) {
const asksuiteTranslator = new AsksuiteTranslator(
request.config.INFOCHAT_DATA_SERVER,
request.config.GOOGLE_TRANSLATE_KEY,
request.config.TRANSLATION_API_URL
);
const detectedLanguageCode = await asksuiteTranslator.detectLanguageByTranslationsApi(request.text);
const detectedLanguage = {
code: detectedLanguageCode.code,
name: detectedLanguageCode.name
}
const companyLanguagesRegex = this.defineRegexLanguages(request.companyLanguages);
const isSupportedByCompany = companyLanguagesRegex.test(detectedLanguage.code);
const fallbackLanguage = { code: request.defaultLanguage };
const language = isSupportedByCompany ? detectedLanguage : fallbackLanguage;
return {
isSupported: isSupportedByCompany,
language,
fallbackLanguage,
};
}
isLanguageSupportedByCompany(request, languageCode) {
const companyLanguagesRegex = this.defineRegexLanguages(request.companyLanguages);
const isSupportedByCompany = companyLanguagesRegex.test(languageCode);
return isSupportedByCompany;
}
async copyRequestToDefaultLanguage(request) {
await this.translateRequest(request);
const toSend = _.clone(request);
toSend.text = toSend.translation || toSend.text;
toSend.language = this.resolveLanguage(DEFAULT_LANGUAGE, request.languages);
return RequestPreprocessor.process(toSend);
}
async findIntentDialogFlow(dialogFlowAccessor, request, toDefaultLanguage) {
if (toDefaultLanguage) {
await this.translateRequest(request);
}
let fromCache = false;
let toSend = _.cloneDeep(request);
if (toDefaultLanguage) {
toSend = await this.copyRequestToDefaultLanguage(request);
}
let intent = await this.getInCacheDialogflow(toSend);
if (!intent) {
intent = await dialogFlowAccessor.resolveText(toSend);
await this.saveInCacheDialogflow(intent, toSend);
} else {
fromCache = true;
}
const extractedData = intent.extractedData;
const processingStep = intent.processingStep;
const parameterFields = intent.parameterFields;
intent = this.findDialogByIntent(toSend, intent.intent);
const translatedFrom = 'translation_api';
intent.fromCache = fromCache;
intent.extractedData = extractedData;
intent.processingStep = { ...processingStep, translatedFrom: request.translated ? translatedFrom : null };
intent.parameterFields = parameterFields;
return intent;
}
}
module.exports = AsksuiteProcessingNaturalUtils;