@cognigy/rest-api-client
Version:
Cognigy REST-Client
266 lines • 11.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.patternMatcher = void 0;
const cognigySlots_1 = require("../../../../interfaces/cognigySlots");
const systemSlots = new Set(cognigySlots_1.cognigySystemSlots);
/**
* Finds compound slots using pattern inside the user input
* @param input Cognigy Input Object
* @param patterns The patterns to check for
* @param patternGroupName Name for the compound group
* @param detailedCompoundSlots Store detailed results for compound slots
* @param tagExistingSlots Tag existing slots
* @param createNewSlots Create new slots from tags
* @param nluResult result from cognigy NLU since there was an alternative input
*/
const patternMatcher = (params) => {
var _a;
const { input, patternGroupName, detailedCompoundSlots, tagExistingSlots, createNewSlots, nluResult, useFullSystemslotText } = params;
let { patterns } = params;
// prune patterns if more than 20
if (patterns.length > 20) {
patterns = patterns.slice(0, 20);
}
// firstly sort array to start with the longest patterns, as those will win
patterns.sort((a, b) => {
return b.length - a.length;
});
// we use let because we strip out found phrases later
let inputText = nluResult.text || input.text; // ci.processText
let inputSlots = nluResult.slots || input.slots;
const detailedSlots = ((_a = input.nlu) === null || _a === void 0 ? void 0 : _a.detailedSlots) || {};
const regexPatterns = [];
// go through patterns and create regex versions of each that is not empty string
patterns.forEach((pattern) => {
if (pattern) {
// set parsedPattern to pattern in case there are no slots
let parsedPattern = pattern;
// match all mentioned slots in the pattern
const foundSlots = pattern.match(/\@[\w>]+/g);
const tags = [];
const slots = [];
// if there are slots, process them
if (Array.isArray(foundSlots) && foundSlots.length > 0) {
foundSlots.forEach((slot) => {
// splits tags from slots
const split = slot.split(">");
const slotName = split[0].replace("@", "");
slots.push(slotName);
// if there is a tag (SLOT>TAG), remember it
if (split.length === 2)
tags.push(split[1]);
else
tags.push("");
const slotValues = [];
if (inputSlots[slotName]) {
if (systemSlots.has(slotName)) {
processFoundSystemSlots(useFullSystemslotText, inputSlots, detailedSlots, slotName, slotValues);
}
else {
inputSlots[slotName].forEach((s) => {
// it's important to use the synonym, as this is the text that was actually found
slotValues.push(s.synonym);
});
}
const slotRegex = slotValues.join("|");
// replace the slots name with the list of actually found entites for this slot
parsedPattern = parsedPattern.replace(new RegExp(`${slot}`, "g"), `(${slotRegex})`);
}
});
}
if (parsedPattern != "()") {
regexPatterns.push({
"pattern": pattern,
"parsedPattern": parsedPattern,
"tags": tags,
"slots": slots
});
}
}
});
const compoundGroups = [];
// go through all created patterns and test them
regexPatterns.forEach((pattern) => {
let result;
const regExp = new RegExp(pattern.parsedPattern, "gi");
const regexMatches = [];
while (result = regExp.exec(inputText)) {
regexMatches.push(result);
}
regexMatches.forEach((m) => {
inputText = inputText.replace(m[0], "");
});
if (regexMatches.length > 0) {
// we found a match for this pattern!
regexMatches.forEach((matches, mi) => {
// set up a compoundGroup scaffold, just in case we need it
const matchedPatternGroup = {
"matchedPhrase": matches[0],
"components": []
};
if (!detailedCompoundSlots)
delete matchedPatternGroup.components;
// iterate through matches, starting at the first group result ([0] is the full match)
for (let i = 1; i < matches.length; i++) {
let originalSlot = null;
const match = matches[i];
const slot = pattern.slots[i - 1];
if (systemSlots.has(slot)) {
originalSlot = handleSystemSlotMatches(useFullSystemslotText, match, slot, inputSlots, detailedSlots);
}
else {
originalSlot = inputSlots[pattern.slots[i - 1]].find(e => (e.synonym) ? e.synonym.toLowerCase() === matches[i].toLowerCase() : false);
}
// create compound groups
if (detailedCompoundSlots) {
const components = {
"slot": pattern.slots[i - 1],
"value": originalSlot.keyphrase || originalSlot
};
if (pattern.tags[i - 1])
components["tag"] = pattern.tags[i - 1];
else
components["tag"] = null;
matchedPatternGroup.components.push(components);
}
else {
if (pattern.tags[i - 1])
matchedPatternGroup[pattern.tags[i - 1]] = (originalSlot === null || originalSlot === void 0 ? void 0 : originalSlot.keyphrase) || originalSlot;
else
matchedPatternGroup[pattern.slots[i - 1]] = originalSlot || originalSlot.keyphrase;
}
if (pattern.tags[i - 1]) {
if (createNewSlots) {
if (inputSlots[pattern.tags[i - 1]]) {
inputSlots[pattern.tags[i - 1]].push(originalSlot);
}
else
inputSlots[pattern.tags[i - 1]] = [originalSlot];
}
if (tagExistingSlots) {
inputSlots[pattern.slots[i - 1]].forEach((s) => {
var _a, _b;
if (((_a = s === null || s === void 0 ? void 0 : s.synonym) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === ((_b = matches[i]) === null || _b === void 0 ? void 0 : _b.toLowerCase())) {
s["tags"] = (Array.isArray(s["tags"])) ? s["tags"].push(pattern.tags[i - 1]) : [pattern.tags[i - 1]];
}
});
}
}
}
compoundGroups.push(matchedPatternGroup);
});
}
});
if (!input["matchedPatterns"])
input["matchedPatterns"] = {};
if (compoundGroups && Array.isArray(compoundGroups) && compoundGroups.length > 0) {
input["matchedPatterns"][patternGroupName] = compoundGroups;
}
if (compoundGroups && compoundGroups.length === 0 && (typeof input["matchedPatterns"] !== 'object' || Object.keys(input["matchedPatterns"]).length === 0))
delete input["matchedPatterns"];
return input;
};
exports.patternMatcher = patternMatcher;
const processFoundSystemSlots = (useFullSystemslotText, inputSlots, detailedSlots, slotName, slotValues) => {
var _a;
if (useFullSystemslotText) {
// we use the detailed system slots to get the text value
(_a = detailedSlots[slotName]) === null || _a === void 0 ? void 0 : _a.forEach((s) => {
slotValues.push(s.text);
});
}
else if (inputSlots[slotName]) {
switch (slotName) {
case "DATE":
inputSlots.DATE.forEach((s) => {
if (s.start)
slotValues.push(s["text"]);
});
break;
case "NUMBER":
inputSlots.NUMBER.forEach((s) => {
slotValues.push(s);
});
break;
case "DURATION":
inputSlots.DURATION.forEach((s) => {
slotValues.push(s["years"] || s["months"] || s["weeks"] || s["days"] || s["hours"] || s["minutes"] || s["seconds"]);
});
break;
case "TEMPERATURE":
inputSlots.TEMPERATURE.forEach((s) => {
slotValues.push(s.value);
});
break;
case "AGE":
inputSlots.AGE.forEach((s) => {
slotValues.push(s.value);
});
break;
case "PERCENTAGE":
inputSlots.PERCENTAGE.forEach((s) => {
slotValues.push(s + "%");
slotValues.push(s + " percent");
});
break;
case "EMAIL":
inputSlots.EMAIL.forEach((s) => {
slotValues.push(s);
});
break;
case "URL":
inputSlots.URL.forEach((s) => {
slotValues.push(s);
});
break;
case "MONEY":
inputSlots.MONEY.forEach((s) => {
slotValues.push(s.value);
});
break;
case "DISTANCE":
inputSlots.DISTANCE.forEach((s) => {
slotValues.push(s.value);
});
break;
}
}
};
const handleSystemSlotMatches = (useFullSystemslotText, match, slotName, inputSlots, detailedSlots) => {
if (useFullSystemslotText) {
const matchedSlot = detailedSlots[slotName].find(e => (e["text"] === match));
if (!matchedSlot) {
return;
}
switch (slotName) {
case "NUMBER":
case "PERCENTAGE":
return matchedSlot.data.value;
case "DATE":
return Object.assign(Object.assign({}, matchedSlot.data), { text: matchedSlot.text });
default:
return matchedSlot.data;
}
}
else {
switch (slotName) {
case "PERCENTAGE":
return Number(match.replace(new RegExp("[^0-9]", "gi"), ""));
case "TEMPERATURE":
case "AGE":
case "NUMBER":
case "MONEY":
case "DISTANCE":
case "DURATION":
return Number(match);
case "DATE":
// @ts-ignore
const dateSlot = inputSlots.DATE.find(e => (e["text"] === match));
// @ts-ignore
return (dateSlot === null || dateSlot === void 0 ? void 0 : dateSlot.start) || (dateSlot === null || dateSlot === void 0 ? void 0 : dateSlot.end);
default:
return match;
}
}
};
//# sourceMappingURL=patternMatcher.js.map