Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

botbuilder-ai

Package Overview
Dependencies
Maintainers
1
Versions
548
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

botbuilder-ai - npm Package Compare versions

Comparing version 4.6.2 to 4.7.0

lib/luisRecognizerOptions.d.ts

120

lib/luisRecognizer.d.ts

@@ -28,2 +28,3 @@ /**

/**
*
* Options per LUIS prediction.

@@ -93,2 +94,90 @@ */

}
export interface LuisRecognizerOptions {
/**
* (Optional) Telemetry Client.
*/
telemetryClient?: BotTelemetryClient;
/**
* (Optional) Designates whether personal information should be logged in telemetry.
*/
logPersonalInformation?: boolean;
/**
* (Optional) Force the inclusion of LUIS Api call in results returned by [recognize()](#recognize). Defaults to a value of `false`
*/
includeAPIResults?: boolean;
}
export interface LuisRecognizerOptionsV3 extends LuisRecognizerOptions {
/**
* (Optional) Luis Api endpoint version.
*/
apiVersion: "v3";
/**
* (Optional) Determine if all intents come back or only the top one.
*/
includeAllIntents?: boolean;
/**
* (Optional) A value indicating whether or not instance data should be included in response.
*/
includeInstanceData?: boolean;
/**
* (Optional) If queries should be logged in LUIS.
*/
log?: boolean;
/**
* (Optional) Dynamic lists of things like contact names to recognize at query time..
*/
dynamicLists?: Array<any>;
/**
* (Optional) External entities recognized in query.
*/
externalEntities?: Array<any>;
/**
* (Optional) Boolean for if external entities should be preferred to the results from LUIS models.
*/
preferExternalEntities?: boolean;
/**
* (Optional) By default this uses the production slot. You can find other standard slots in <see cref="LuisSlot"/>.
* If you specify a Version, then a private version of the application is used instead of a slot.
*/
slot?: 'production' | 'staging';
/**
* (Optional) LUIS supports versions and this is the version to use instead of a slot.
* If this is specified, then the <see cref="Slot"/> is ignored..
*/
version?: string;
}
export interface LuisRecognizerOptionsV2 extends LuisRecognizerOptions {
/**
* Luis Api endpoint version.
*/
apiVersion: "v2";
/**
* (Optional) Bing Spell Check subscription key.
*/
bingSpellCheckSubscriptionKey?: string;
/**
* (Optional) Determine if all intents come back or only the top one.
*/
includeAllIntents?: boolean;
/**
* (Optional) A value indicating whether or not instance data should be included in response.
*/
includeInstanceData?: boolean;
/**
* (Optional) If queries should be logged in LUIS.
*/
log?: boolean;
/**
* (Optional) Whether to spell check query.
*/
spellCheck?: boolean;
/**
* (Optional) Whether to use the staging endpoint.
*/
staging?: boolean;
/**
* (Optional) The time zone offset for resolving datetimes.
*/
timezoneOffset?: number;
}
/**

@@ -111,10 +200,12 @@ * Recognize intents in a user utterance using a configured LUIS model.

private cacheKey;
private luisRecognizerInternal;
/**
* Creates a new LuisRecognizer instance.
* @param application An object conforming to the [LuisApplication](#luisapplication) definition or a string representing a LUIS application endpoint, usually retrieved from https://luis.ai.
* @param options (Optional) options object used to control predictions. Should conform to the [LuisPrectionOptions](#luispredictionoptions) definition.
* @param includeApiResults (Optional) flag that if set to `true` will force the inclusion of LUIS Api call in results returned by [recognize()](#recognize). Defaults to a value of `false`.
* @param options (Optional) options object used to control predictions. Should conform to the [LuisRecognizerOptions](#luisrecognizeroptions) definition.
* @param includeApiResults (Deprecated) flag that if set to `true` will force the inclusion of LUIS Api call in results returned by [recognize()](#recognize). Defaults to a value of `false`.
*/
constructor(application: string, options?: LuisPredictionOptions, includeApiResults?: boolean);
constructor(application: LuisApplication, options?: LuisPredictionOptions, includeApiResults?: boolean);
constructor(application: LuisApplication | string, options?: LuisRecognizerOptionsV3 | LuisRecognizerOptionsV2);
/**

@@ -164,3 +255,3 @@ * Gets a value indicating whether determines whether to log personal information that came from the user.

* @param telemetryMetrics Additional metrics to be logged to telemetry with the LuisResult event.
* @param options (Optional) options object used to control predictions. Should conform to the [LuisPrectionOptions](#luispredictionoptions) definition.
* @param options (Optional) options object used to override control predictions. Should conform to the [LuisRecognizerOptionsV2] or [LuisRecognizerOptionsV3] definition.
*/

@@ -171,3 +262,3 @@ recognize(context: TurnContext, telemetryProperties?: {

[key: string]: number;
}, options?: LuisPredictionOptions): Promise<RecognizerResult>;
}, options?: LuisRecognizerOptionsV2 | LuisRecognizerOptionsV3 | LuisPredictionOptions): Promise<RecognizerResult>;
/**

@@ -198,21 +289,4 @@ * Invoked prior to a LuisResult Event being logged.

}>;
private getUserAgent;
private emitTraceInfo;
private prepareErrorMessage;
private normalizeName;
private getIntents;
private getEntitiesAndMetadata;
private getEntityValue;
private getEntityMetadata;
private getNormalizedEntityName;
private populateCompositeEntity;
/**
* If a property doesn't exist add it to a new array, otherwise append it to the existing array
* @param obj Object on which the property is to be set
* @param key Property Key
* @param value Property Value
*/
private addProperty;
private getSentiment;
/**
* Merges the default options set by the Recognizer contructor with the 'user' options passed into the 'recognize' method

@@ -227,2 +301,6 @@ */

private validateLuisApplication;
/**
* Builds a LuisRecognizer Strategy depending on the options passed
*/
private buildRecognizer;
}

324

lib/luisRecognizer.js

@@ -11,19 +11,7 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
/**
* @module botbuilder-ai
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
const cognitiveservices_luis_runtime_1 = require("@azure/cognitiveservices-luis-runtime");
const msRest = require("@azure/ms-rest-js");
const botbuilder_core_1 = require("botbuilder-core");
const os = require("os");
const Url = require("url-parse");
const luisTelemetryConstants_1 = require("./luisTelemetryConstants");
const pjson = require('../package.json');
const LUIS_TRACE_TYPE = 'https://www.luis.ai/schemas/trace';
const LUIS_TRACE_NAME = 'LuisRecognizer';
const LUIS_TRACE_LABEL = 'Luis Trace';
const luisRecognizerOptionsV2_1 = require("./luisRecognizerOptionsV2");
const luisRecognizerOptionsV3_1 = require("./luisRecognizerOptionsV3");
/**

@@ -61,13 +49,18 @@ * Recognize intents in a user utterance using a configured LUIS model.

this.validateLuisApplication();
this.options = Object.assign({ includeAllIntents: false, includeInstanceData: true, log: true, spellCheck: false, staging: false }, options);
this.includeApiResults = !!includeApiResults;
// Create client
// - We have to cast "creds as any" to avoid a build break relating to different versions
// of autorest being used by our various components. This is just a build issue and
// shouldn't effect production bots.
const creds = new msRest.TokenCredentials(this.application.endpointKey);
const baseUri = this.application.endpoint || 'https://westus.api.cognitive.microsoft.com';
this.luisClient = new cognitiveservices_luis_runtime_1.LUISRuntimeClient(creds, baseUri);
this._telemetryClient = this.options.telemetryClient || new botbuilder_core_1.NullTelemetryClient();
this._logPersonalInformation = this.options.logPersonalInformation || false;
this._telemetryClient = (options && options.telemetryClient) || new botbuilder_core_1.NullTelemetryClient();
this._logPersonalInformation = (options && options.logPersonalInformation) || false;
if (!options) {
this.luisRecognizerInternal = new luisRecognizerOptionsV2_1.LuisRecognizerV2(this.application);
}
else if (luisRecognizerOptionsV3_1.isLuisRecognizerOptionsV3(options)) {
this.luisRecognizerInternal = new luisRecognizerOptionsV3_1.LuisRecognizerV3(this.application, options);
}
else if (luisRecognizerOptionsV2_1.isLuisRecognizerOptionsV2(options)) {
this.luisRecognizerInternal = new luisRecognizerOptionsV2_1.LuisRecognizerV2(this.application, options);
}
else {
this.options = Object.assign({}, options);
let recOptions = Object.assign({ includeAPIResults: !!includeApiResults }, options, { apiVersion: 'v2' });
this.luisRecognizerInternal = new luisRecognizerOptionsV2_1.LuisRecognizerV2(this.application, recOptions);
}
}

@@ -132,7 +125,7 @@ /**

* @param telemetryMetrics Additional metrics to be logged to telemetry with the LuisResult event.
* @param options (Optional) options object used to control predictions. Should conform to the [LuisPrectionOptions](#luispredictionoptions) definition.
* @param options (Optional) options object used to override control predictions. Should conform to the [LuisRecognizerOptionsV2] or [LuisRecognizerOptionsV3] definition.
*/
recognize(context, telemetryProperties, telemetryMetrics, options) {
const cached = context.turnState.get(this.cacheKey);
const luisPredictionOptions = options ? this.setLuisPredictionOptions(this.options, options) : this.options;
const luisRecognizer = options ? this.buildRecognizer(options) : this.luisRecognizerInternal;
if (!cached) {

@@ -150,15 +143,3 @@ const utterance = context.activity.text || '';

else {
recognizerPromise = this.luisClient.prediction.resolve(this.application.applicationId, utterance, Object.assign({ verbose: luisPredictionOptions.includeAllIntents, customHeaders: {
'Ocp-Apim-Subscription-Key': this.application.endpointKey,
'User-Agent': this.getUserAgent()
} }, luisPredictionOptions))
// Map results
.then((luisResult) => ({
text: luisResult.query,
alteredText: luisResult.alteredQuery,
intents: this.getIntents(luisResult),
entities: this.getEntitiesAndMetadata(luisResult.entities, luisResult.compositeEntities, luisPredictionOptions.includeInstanceData === undefined || luisPredictionOptions.includeInstanceData),
sentiment: this.getSentiment(luisResult),
luisResult: (this.includeApiResults ? luisResult : null)
}));
recognizerPromise = luisRecognizer.recognizeInternalAsync(context);
}

@@ -171,5 +152,3 @@ return recognizerPromise

this.onRecognizerResults(recognizerResult, context, telemetryProperties, telemetryMetrics);
return this.emitTraceInfo(context, recognizerResult.luisResult || null, recognizerResult).then(() => {
return recognizerResult;
});
return recognizerResult;
})

@@ -247,29 +226,2 @@ .catch((error) => {

}
getUserAgent() {
// Note when the ms-rest dependency the LuisClient uses has been updated
// this code should be modified to use the client's addUserAgentInfo() function.
const packageUserAgent = `${pjson.name}/${pjson.version}`;
const platformUserAgent = `(${os.arch()}-${os.type()}-${os.release()}; Node.js,Version=${process.version})`;
const userAgent = `${packageUserAgent} ${platformUserAgent}`;
return userAgent;
}
emitTraceInfo(context, luisResult, recognizerResult) {
const traceInfo = {
recognizerResult: recognizerResult,
luisResult: luisResult,
luisOptions: {
Staging: this.options.staging
},
luisModel: {
ModelID: this.application.applicationId
}
};
return context.sendActivity({
type: 'trace',
valueType: LUIS_TRACE_TYPE,
name: LUIS_TRACE_NAME,
label: LUIS_TRACE_LABEL,
value: traceInfo
});
}
prepareErrorMessage(error) {

@@ -313,216 +265,3 @@ // If the `error` received is a azure-cognitiveservices-luis-runtime error,

}
normalizeName(name) {
return name.replace(/\.| /g, '_');
}
getIntents(luisResult) {
const intents = {};
if (luisResult.intents) {
luisResult.intents.reduce((prev, curr) => {
prev[this.normalizeName(curr.intent)] = { score: curr.score };
return prev;
}, intents);
}
else {
const topScoringIntent = luisResult.topScoringIntent;
intents[this.normalizeName((topScoringIntent).intent)] = { score: topScoringIntent.score };
}
return intents;
}
getEntitiesAndMetadata(entities, compositeEntities, verbose) {
const entitiesAndMetadata = verbose ? { $instance: {} } : {};
let compositeEntityTypes = [];
// We start by populating composite entities so that entities covered by them are removed from the entities list
if (compositeEntities) {
compositeEntityTypes = compositeEntities.map((compositeEntity) => compositeEntity.parentType);
compositeEntities.forEach((compositeEntity) => {
entities = this.populateCompositeEntity(compositeEntity, entities, entitiesAndMetadata, verbose);
});
}
entities.forEach((entity) => {
// we'll address composite entities separately
if (compositeEntityTypes.indexOf(entity.type) > -1) {
return;
}
let val = this.getEntityValue(entity);
if (val != null) {
this.addProperty(entitiesAndMetadata, this.getNormalizedEntityName(entity), val);
if (verbose) {
this.addProperty(entitiesAndMetadata.$instance, this.getNormalizedEntityName(entity), this.getEntityMetadata(entity));
}
}
});
return entitiesAndMetadata;
}
getEntityValue(entity) {
if (entity.type.startsWith("builtin.geographyV2.")) {
return {
"type": entity.type.substring(20),
"location": entity.entity
};
}
if (entity.type.startsWith('builtin.ordinalV2')) {
return {
"relativeTo": entity.resolution.relativeTo,
"offset": Number(entity.resolution.offset)
};
}
if (!entity.resolution) {
return entity.entity;
}
if (entity.type.startsWith('builtin.datetimeV2.')) {
if (!entity.resolution.values || !entity.resolution.values.length) {
return entity.resolution;
}
const vals = entity.resolution.values;
const type = vals[0].type;
const timexes = vals.map((t) => t.timex);
const distinct = timexes.filter((v, i, a) => a.indexOf(v) === i);
return { type: type, timex: distinct };
}
else {
const res = entity.resolution;
switch (entity.type) {
case 'builtin.number':
case 'builtin.ordinal': return Number(res.value);
case 'builtin.percentage':
{
let svalue = res.value;
if (svalue.endsWith('%')) {
svalue = svalue.substring(0, svalue.length - 1);
}
return Number(svalue);
}
case 'builtin.age':
case 'builtin.dimension':
case 'builtin.currency':
case 'builtin.temperature':
{
const val = res.value;
const obj = {};
if (val) {
obj.number = Number(val);
}
obj.units = res.unit;
return obj;
}
default:
// This will return null if there is no value/values which can happen when a new prebuilt is introduced
return entity.resolution.value ?
entity.resolution.value :
entity.resolution.values;
}
}
}
getEntityMetadata(entity) {
const res = {
startIndex: entity.startIndex,
endIndex: entity.endIndex + 1,
score: entity.score,
text: entity.entity,
type: entity.type
};
if (entity.resolution && entity.resolution.subtype) {
res.subtype = entity.resolution.subtype;
}
return res;
}
getNormalizedEntityName(entity) {
// Type::Role -> Role
let type = entity.type.split(':').pop();
if (type.startsWith('builtin.datetimeV2.')) {
type = 'datetime';
}
else if (type.startsWith('builtin.currency')) {
type = 'money';
}
else if (type.startsWith('builtin.geographyV2')) {
type = 'geographyV2';
}
else if (type.startsWith('builtin.ordinalV2')) {
type = 'ordinalV2';
}
else if (type.startsWith('builtin.')) {
type = type.substring(8);
}
if (entity.role !== null && entity.role !== '' && entity.role !== undefined) {
type = entity.role;
}
return type.replace(/\.|\s/g, '_');
}
populateCompositeEntity(compositeEntity, entities, entitiesAndMetadata, verbose) {
const childrenEntites = verbose ? { $instance: {} } : {};
let childrenEntitiesMetadata = {};
// This is now implemented as O(n^2) search and can be reduced to O(2n) using a map as an optimization if n grows
const compositeEntityMetadata = entities.find((entity) => {
// For now we are matching by value, which can be ambiguous if the same composite entity shows up with the same text
// multiple times within an utterance, but this is just a stop gap solution till the indices are included in composite entities
return entity.type === compositeEntity.parentType && entity.entity === compositeEntity.value;
});
const filteredEntities = [];
if (verbose) {
childrenEntitiesMetadata = this.getEntityMetadata(compositeEntityMetadata);
}
// This is now implemented as O(n*k) search and can be reduced to O(n + k) using a map as an optimization if n or k grow
const coveredSet = new Set();
compositeEntity.children.forEach((childEntity) => {
for (let i = 0; i < entities.length; i++) {
const entity = entities[i];
if (!coveredSet.has(i) &&
childEntity.type === entity.type &&
compositeEntityMetadata &&
entity.startIndex !== undefined &&
compositeEntityMetadata.startIndex !== undefined &&
entity.startIndex >= compositeEntityMetadata.startIndex &&
entity.endIndex !== undefined &&
compositeEntityMetadata.endIndex !== undefined &&
entity.endIndex <= compositeEntityMetadata.endIndex) {
// Add to the set to ensure that we don't consider the same child entity more than once per composite
coveredSet.add(i);
let val = this.getEntityValue(entity);
if (val != null) {
this.addProperty(childrenEntites, this.getNormalizedEntityName(entity), val);
if (verbose) {
this.addProperty(childrenEntites.$instance, this.getNormalizedEntityName(entity), this.getEntityMetadata(entity));
}
}
}
}
});
// filter entities that were covered by this composite entity
for (let i = 0; i < entities.length; i++) {
if (!coveredSet.has(i)) {
filteredEntities.push(entities[i]);
}
}
this.addProperty(entitiesAndMetadata, this.getNormalizedEntityName(compositeEntityMetadata), childrenEntites);
if (verbose) {
this.addProperty(entitiesAndMetadata.$instance, this.getNormalizedEntityName(compositeEntityMetadata), childrenEntitiesMetadata);
}
return filteredEntities;
}
/**
* If a property doesn't exist add it to a new array, otherwise append it to the existing array
* @param obj Object on which the property is to be set
* @param key Property Key
* @param value Property Value
*/
addProperty(obj, key, value) {
if (key in obj) {
obj[key] = obj[key].concat(value);
}
else {
obj[key] = [value];
}
}
getSentiment(luis) {
let result;
if (luis.sentimentAnalysis) {
result = {
label: luis.sentimentAnalysis.label,
score: luis.sentimentAnalysis.score
};
}
return result;
}
/**
* Merges the default options set by the Recognizer contructor with the 'user' options passed into the 'recognize' method

@@ -546,4 +285,23 @@ */

}
/**
* Builds a LuisRecognizer Strategy depending on the options passed
*/
buildRecognizer(userOptions) {
if (luisRecognizerOptionsV3_1.isLuisRecognizerOptionsV3(userOptions)) {
return new luisRecognizerOptionsV3_1.LuisRecognizerV3(this.application, userOptions);
}
else if (luisRecognizerOptionsV2_1.isLuisRecognizerOptionsV2(userOptions)) {
return new luisRecognizerOptionsV2_1.LuisRecognizerV2(this.application, userOptions);
}
else {
if (!this.options) {
this.options = {};
}
const merge = Object.assign(this.options, userOptions);
let recOptions = Object.assign({}, merge, { apiVersion: 'v2' });
return new luisRecognizerOptionsV2_1.LuisRecognizerV2(this.application, recOptions);
}
}
}
exports.LuisRecognizer = LuisRecognizer;
//# sourceMappingURL=luisRecognizer.js.map

@@ -49,2 +49,10 @@ /**

qnaId?: number;
/**
* A value indicating whether to call test or prod environment of knowledgebase.
*/
isTest?: boolean;
/**
* Ranker types.
*/
rankerType?: string;
}

@@ -20,2 +20,3 @@ "use strict";

const __1 = require("..");
const rankerTypes_1 = require("../qnamaker-interfaces/rankerTypes");
/**

@@ -60,3 +61,4 @@ * Generate Answer api utils class.

const url = `${endpoint.host}/knowledgebases/${endpoint.knowledgeBaseId}/generateanswer`;
const queryOptions = Object.assign({}, this._options, options);
var queryOptions = Object.assign({}, this._options, options);
queryOptions.rankerType = !queryOptions.rankerType ? rankerTypes_1.RankerTypes.default : queryOptions.rankerType;
this.validateOptions(queryOptions);

@@ -105,3 +107,3 @@ var payloadBody = JSON.stringify(Object.assign({ question: question }, queryOptions));

validateOptions(options) {
const { scoreThreshold, top } = options;
const { scoreThreshold, top, rankerType } = options;
if (scoreThreshold) {

@@ -108,0 +110,0 @@ this.validateScoreThreshold(scoreThreshold);

@@ -5,3 +5,3 @@ {

"description": "Cognitive services extensions for Microsoft BotBuilder.",
"version": "4.6.2",
"version": "4.7.0",
"license": "MIT",

@@ -28,3 +28,3 @@ "keywords": [

"@types/node": "^10.12.18",
"botbuilder-core": "4.6.2",
"botbuilder-core": "4.7.0",
"moment": "^2.20.1",

@@ -51,3 +51,3 @@ "node-fetch": "^2.3.0",

"clean": "erase /q /s .\\lib",
"set-version": "npm version --allow-same-version 4.6.2"
"set-version": "npm version --allow-same-version 4.7.0"
},

@@ -54,0 +54,0 @@ "files": [

@@ -9,15 +9,8 @@ /**

import { LUISRuntimeClient as LuisClient, LUISRuntimeModels as LuisModels } from '@azure/cognitiveservices-luis-runtime';
import * as msRest from '@azure/ms-rest-js';
import { BotTelemetryClient, NullTelemetryClient, RecognizerResult, TurnContext } from 'botbuilder-core';
import * as os from 'os';
import * as Url from 'url-parse';
import { LuisTelemetryConstants } from './luisTelemetryConstants';
import { isLuisRecognizerOptionsV2, LuisRecognizerV2 } from './luisRecognizerOptionsV2';
import { isLuisRecognizerOptionsV3, LuisRecognizerV3 } from './luisRecognizerOptionsV3';
const pjson = require('../package.json');
const LUIS_TRACE_TYPE = 'https://www.luis.ai/schemas/trace';
const LUIS_TRACE_NAME = 'LuisRecognizer';
const LUIS_TRACE_LABEL = 'Luis Trace';
/**

@@ -68,2 +61,3 @@ * @private

/**
*
* Options per LUIS prediction.

@@ -141,3 +135,109 @@ */

export interface LuisRecognizerOptions {
/**
* (Optional) Telemetry Client.
*/
telemetryClient?: BotTelemetryClient;
/**
* (Optional) Designates whether personal information should be logged in telemetry.
*/
logPersonalInformation?: boolean;
/**
* (Optional) Force the inclusion of LUIS Api call in results returned by [recognize()](#recognize). Defaults to a value of `false`
*/
includeAPIResults?: boolean;
}
export interface LuisRecognizerOptionsV3 extends LuisRecognizerOptions {
/**
* (Optional) Luis Api endpoint version.
*/
apiVersion: "v3";
/**
* (Optional) Determine if all intents come back or only the top one.
*/
includeAllIntents?: boolean;
/**
* (Optional) A value indicating whether or not instance data should be included in response.
*/
includeInstanceData?: boolean;
/**
* (Optional) If queries should be logged in LUIS.
*/
log?: boolean;
/**
* (Optional) Dynamic lists of things like contact names to recognize at query time..
*/
dynamicLists?: Array<any>;
/**
* (Optional) External entities recognized in query.
*/
externalEntities?: Array<any>;
/**
* (Optional) Boolean for if external entities should be preferred to the results from LUIS models.
*/
preferExternalEntities?: boolean;
/**
* (Optional) By default this uses the production slot. You can find other standard slots in <see cref="LuisSlot"/>.
* If you specify a Version, then a private version of the application is used instead of a slot.
*/
slot?: 'production' | 'staging';
/**
* (Optional) LUIS supports versions and this is the version to use instead of a slot.
* If this is specified, then the <see cref="Slot"/> is ignored..
*/
version?: string;
}
export interface LuisRecognizerOptionsV2 extends LuisRecognizerOptions {
/**
* Luis Api endpoint version.
*/
apiVersion: "v2";
/**
* (Optional) Bing Spell Check subscription key.
*/
bingSpellCheckSubscriptionKey?: string;
/**
* (Optional) Determine if all intents come back or only the top one.
*/
includeAllIntents?: boolean;
/**
* (Optional) A value indicating whether or not instance data should be included in response.
*/
includeInstanceData?: boolean;
/**
* (Optional) If queries should be logged in LUIS.
*/
log?: boolean;
/**
* (Optional) Whether to spell check query.
*/
spellCheck?: boolean;
/**
* (Optional) Whether to use the staging endpoint.
*/
staging?: boolean;
/**
* (Optional) The time zone offset for resolving datetimes.
*/
timezoneOffset?: number;
}
/**

@@ -162,2 +262,3 @@ * Recognize intents in a user utterance using a configured LUIS model.

private cacheKey: symbol = Symbol('results');
private luisRecognizerInternal: LuisRecognizerV2 | LuisRecognizerV3;

@@ -167,8 +268,9 @@ /**

* @param application An object conforming to the [LuisApplication](#luisapplication) definition or a string representing a LUIS application endpoint, usually retrieved from https://luis.ai.
* @param options (Optional) options object used to control predictions. Should conform to the [LuisPrectionOptions](#luispredictionoptions) definition.
* @param includeApiResults (Optional) flag that if set to `true` will force the inclusion of LUIS Api call in results returned by [recognize()](#recognize). Defaults to a value of `false`.
* @param options (Optional) options object used to control predictions. Should conform to the [LuisRecognizerOptions](#luisrecognizeroptions) definition.
* @param includeApiResults (Deprecated) flag that if set to `true` will force the inclusion of LUIS Api call in results returned by [recognize()](#recognize). Defaults to a value of `false`.
*/
constructor(application: string, options?: LuisPredictionOptions, includeApiResults?: boolean);
constructor(application: LuisApplication, options?: LuisPredictionOptions, includeApiResults?: boolean);
constructor(application: LuisApplication | string, options?: LuisPredictionOptions, includeApiResults?: boolean) {
constructor(application: LuisApplication | string, options?: LuisRecognizerOptionsV3 | LuisRecognizerOptionsV2);
constructor(application: LuisApplication | string, options?: LuisRecognizerOptionsV3 | LuisRecognizerOptionsV2 | LuisPredictionOptions, includeApiResults?: boolean) {
if (typeof application === 'string') {

@@ -194,22 +296,25 @@ const parsedEndpoint: Url = Url(application);

this.options = {
includeAllIntents: false,
includeInstanceData: true,
log: true,
spellCheck: false,
staging: false,
...options
};
this.includeApiResults = !!includeApiResults;
this._telemetryClient = (options && options.telemetryClient) || new NullTelemetryClient();
this._logPersonalInformation = (options && options.logPersonalInformation) || false;
// Create client
// - We have to cast "creds as any" to avoid a build break relating to different versions
// of autorest being used by our various components. This is just a build issue and
// shouldn't effect production bots.
const creds: msRest.TokenCredentials = new msRest.TokenCredentials(this.application.endpointKey);
const baseUri: string = this.application.endpoint || 'https://westus.api.cognitive.microsoft.com';
this.luisClient = new LuisClient(creds as any, baseUri);
if(!options) {
this.luisRecognizerInternal = new LuisRecognizerV2(this.application);
} else if (isLuisRecognizerOptionsV3(options)) {
this.luisRecognizerInternal = new LuisRecognizerV3(this.application, options);
} else if (isLuisRecognizerOptionsV2(options)) {
this.luisRecognizerInternal = new LuisRecognizerV2(this.application, options);
} else {
this._telemetryClient = this.options.telemetryClient || new NullTelemetryClient();
this._logPersonalInformation = this.options.logPersonalInformation || false;
this.options = {
...options,
}
let recOptions: LuisRecognizerOptionsV2 = {
includeAPIResults: !!includeApiResults,
...options,
apiVersion: 'v2'
};
this.luisRecognizerInternal = new LuisRecognizerV2(this.application, recOptions);
}
}

@@ -279,7 +384,7 @@

* @param telemetryMetrics Additional metrics to be logged to telemetry with the LuisResult event.
* @param options (Optional) options object used to control predictions. Should conform to the [LuisPrectionOptions](#luispredictionoptions) definition.
* @param options (Optional) options object used to override control predictions. Should conform to the [LuisRecognizerOptionsV2] or [LuisRecognizerOptionsV3] definition.
*/
public recognize(context: TurnContext, telemetryProperties?: { [key: string]: string }, telemetryMetrics?: { [key: string]: number }, options?: LuisPredictionOptions): Promise<RecognizerResult> {
public recognize(context: TurnContext, telemetryProperties?: { [key: string]: string }, telemetryMetrics?: { [key: string]: number }, options?: LuisRecognizerOptionsV2 | LuisRecognizerOptionsV3 | LuisPredictionOptions): Promise<RecognizerResult> {
const cached: any = context.turnState.get(this.cacheKey);
const luisPredictionOptions = options ? this.setLuisPredictionOptions(this.options, options) : this.options;
const luisRecognizer = options ? this.buildRecognizer(options) : this.luisRecognizerInternal;
if (!cached) {

@@ -297,25 +402,3 @@ const utterance: string = context.activity.text || '';

} else {
recognizerPromise = this.luisClient.prediction.resolve(
this.application.applicationId, utterance,
{
verbose: luisPredictionOptions.includeAllIntents,
customHeaders: {
'Ocp-Apim-Subscription-Key': this.application.endpointKey,
'User-Agent': this.getUserAgent()
},
...luisPredictionOptions
})
// Map results
.then((luisResult: LuisModels.LuisResult) => ({
text: luisResult.query,
alteredText: luisResult.alteredQuery,
intents: this.getIntents(luisResult),
entities: this.getEntitiesAndMetadata(
luisResult.entities,
luisResult.compositeEntities,
luisPredictionOptions.includeInstanceData === undefined || luisPredictionOptions.includeInstanceData
),
sentiment: this.getSentiment(luisResult),
luisResult: (this.includeApiResults ? luisResult : null)
}));
recognizerPromise = luisRecognizer.recognizeInternalAsync(context);
}

@@ -331,5 +414,3 @@

return this.emitTraceInfo(context, recognizerResult.luisResult || null, recognizerResult).then(() => {
return recognizerResult;
});
return recognizerResult;
})

@@ -414,36 +495,2 @@ .catch((error: any) => {

private getUserAgent(): string {
// Note when the ms-rest dependency the LuisClient uses has been updated
// this code should be modified to use the client's addUserAgentInfo() function.
const packageUserAgent = `${pjson.name}/${pjson.version}`;
const platformUserAgent = `(${os.arch()}-${os.type()}-${os.release()}; Node.js,Version=${process.version})`;
const userAgent = `${packageUserAgent} ${platformUserAgent}`;
return userAgent;
}
private emitTraceInfo(context: TurnContext, luisResult: LuisModels.LuisResult, recognizerResult: RecognizerResult): Promise<any> {
const traceInfo: LuisTraceInfo = {
recognizerResult: recognizerResult,
luisResult: luisResult,
luisOptions: {
Staging: this.options.staging
},
luisModel: {
ModelID: this.application.applicationId
}
};
return context.sendActivity({
type: 'trace',
valueType: LUIS_TRACE_TYPE,
name: LUIS_TRACE_NAME,
label: LUIS_TRACE_LABEL,
value: traceInfo
});
}
private prepareErrorMessage(error: Error): void {

@@ -488,259 +535,3 @@ // If the `error` received is a azure-cognitiveservices-luis-runtime error,

private normalizeName(name: string): string {
return name.replace(/\.| /g, '_');
}
private getIntents(luisResult: LuisModels.LuisResult): any {
const intents: { [name: string]: { score: number } } = {};
if (luisResult.intents) {
luisResult.intents.reduce(
(prev: any, curr: LuisModels.IntentModel) => {
prev[this.normalizeName(curr.intent)] = { score: curr.score };
return prev;
},
intents
);
} else {
const topScoringIntent: LuisModels.IntentModel = luisResult.topScoringIntent;
intents[this.normalizeName((topScoringIntent).intent)] = { score: topScoringIntent.score };
}
return intents;
}
private getEntitiesAndMetadata(
entities: LuisModels.EntityModel[],
compositeEntities: LuisModels.CompositeEntityModel[] | undefined,
verbose: boolean
): any {
const entitiesAndMetadata: any = verbose ? { $instance: {} } : {};
let compositeEntityTypes: string[] = [];
// We start by populating composite entities so that entities covered by them are removed from the entities list
if (compositeEntities) {
compositeEntityTypes = compositeEntities.map((compositeEntity: LuisModels.CompositeEntityModel) => compositeEntity.parentType);
compositeEntities.forEach((compositeEntity: LuisModels.CompositeEntityModel) => {
entities = this.populateCompositeEntity(compositeEntity, entities, entitiesAndMetadata, verbose);
});
}
entities.forEach((entity: LuisModels.EntityModel) => {
// we'll address composite entities separately
if (compositeEntityTypes.indexOf(entity.type) > -1) {
return;
}
let val = this.getEntityValue(entity);
if (val != null) {
this.addProperty(entitiesAndMetadata, this.getNormalizedEntityName(entity), val);
if (verbose) {
this.addProperty(entitiesAndMetadata.$instance, this.getNormalizedEntityName(entity), this.getEntityMetadata(entity));
}
}
});
return entitiesAndMetadata;
}
private getEntityValue(entity: LuisModels.EntityModel): any {
if (entity.type.startsWith("builtin.geographyV2.")) {
return {
"type": entity.type.substring(20),
"location": entity.entity
};
}
if (entity.type.startsWith('builtin.ordinalV2')) {
return {
"relativeTo": entity.resolution.relativeTo,
"offset": Number(entity.resolution.offset)
}
}
if (!entity.resolution) {
return entity.entity;
}
if (entity.type.startsWith('builtin.datetimeV2.')) {
if (!entity.resolution.values || !entity.resolution.values.length) {
return entity.resolution;
}
const vals: any = entity.resolution.values;
const type: any = vals[0].type;
const timexes: any[] = vals.map((t: any) => t.timex);
const distinct: any = timexes.filter((v: any, i: number, a: any[]) => a.indexOf(v) === i);
return { type: type, timex: distinct };
} else {
const res: any = entity.resolution;
switch (entity.type) {
case 'builtin.number':
case 'builtin.ordinal': return Number(res.value);
case 'builtin.percentage':
{
let svalue: string = res.value;
if (svalue.endsWith('%')) {
svalue = svalue.substring(0, svalue.length - 1);
}
return Number(svalue);
}
case 'builtin.age':
case 'builtin.dimension':
case 'builtin.currency':
case 'builtin.temperature':
{
const val: any = res.value;
const obj: any = {};
if (val) {
obj.number = Number(val);
}
obj.units = res.unit;
return obj;
}
default:
// This will return null if there is no value/values which can happen when a new prebuilt is introduced
return entity.resolution.value ?
entity.resolution.value :
entity.resolution.values;
}
}
}
private getEntityMetadata(entity: LuisModels.EntityModel): any {
const res: any = {
startIndex: entity.startIndex,
endIndex: entity.endIndex + 1,
score: entity.score,
text: entity.entity,
type: entity.type
};
if (entity.resolution && entity.resolution.subtype) {
res.subtype = entity.resolution.subtype;
}
return res;
}
private getNormalizedEntityName(entity: LuisModels.EntityModel): string {
// Type::Role -> Role
let type: string = entity.type.split(':').pop();
if (type.startsWith('builtin.datetimeV2.')) {
type = 'datetime';
}
else if (type.startsWith('builtin.currency')) {
type = 'money';
}
else if (type.startsWith('builtin.geographyV2')) {
type = 'geographyV2';
}
else if (type.startsWith('builtin.ordinalV2')) {
type = 'ordinalV2';
}
else if (type.startsWith('builtin.')) {
type = type.substring(8);
}
if (entity.role !== null && entity.role !== '' && entity.role !== undefined) {
type = entity.role;
}
return type.replace(/\.|\s/g, '_');
}
private populateCompositeEntity(
compositeEntity: LuisModels.CompositeEntityModel,
entities: LuisModels.EntityModel[],
entitiesAndMetadata: any,
verbose: boolean
): LuisModels.EntityModel[] {
const childrenEntites: any = verbose ? { $instance: {} } : {};
let childrenEntitiesMetadata: any = {};
// This is now implemented as O(n^2) search and can be reduced to O(2n) using a map as an optimization if n grows
const compositeEntityMetadata: LuisModels.EntityModel | undefined = entities.find((entity: LuisModels.EntityModel) => {
// For now we are matching by value, which can be ambiguous if the same composite entity shows up with the same text
// multiple times within an utterance, but this is just a stop gap solution till the indices are included in composite entities
return entity.type === compositeEntity.parentType && entity.entity === compositeEntity.value;
});
const filteredEntities: LuisModels.EntityModel[] = [];
if (verbose) {
childrenEntitiesMetadata = this.getEntityMetadata(compositeEntityMetadata);
}
// This is now implemented as O(n*k) search and can be reduced to O(n + k) using a map as an optimization if n or k grow
const coveredSet: Set<any> = new Set();
compositeEntity.children.forEach((childEntity: LuisModels.CompositeChildModel) => {
for (let i = 0; i < entities.length; i++) {
const entity: LuisModels.EntityModel = entities[i];
if (!coveredSet.has(i) &&
childEntity.type === entity.type &&
compositeEntityMetadata &&
entity.startIndex !== undefined &&
compositeEntityMetadata.startIndex !== undefined &&
entity.startIndex >= compositeEntityMetadata.startIndex &&
entity.endIndex !== undefined &&
compositeEntityMetadata.endIndex !== undefined &&
entity.endIndex <= compositeEntityMetadata.endIndex
) {
// Add to the set to ensure that we don't consider the same child entity more than once per composite
coveredSet.add(i);
let val = this.getEntityValue(entity);
if (val != null) {
this.addProperty(childrenEntites, this.getNormalizedEntityName(entity), val);
if (verbose) {
this.addProperty(childrenEntites.$instance, this.getNormalizedEntityName(entity), this.getEntityMetadata(entity));
}
}
}
}
});
// filter entities that were covered by this composite entity
for (let i = 0; i < entities.length; i++) {
if (!coveredSet.has(i)) {
filteredEntities.push(entities[i]);
}
}
this.addProperty(entitiesAndMetadata, this.getNormalizedEntityName(compositeEntityMetadata), childrenEntites);
if (verbose) {
this.addProperty(entitiesAndMetadata.$instance, this.getNormalizedEntityName(compositeEntityMetadata), childrenEntitiesMetadata);
}
return filteredEntities;
}
/**
* If a property doesn't exist add it to a new array, otherwise append it to the existing array
* @param obj Object on which the property is to be set
* @param key Property Key
* @param value Property Value
*/
private addProperty(obj: any, key: string, value: any): void {
if (key in obj) {
obj[key] = obj[key].concat(value);
} else {
obj[key] = [value];
}
}
private getSentiment(luis: LuisModels.LuisResult): any {
let result: any;
if (luis.sentimentAnalysis) {
result = {
label: luis.sentimentAnalysis.label,
score: luis.sentimentAnalysis.score
};
}
return result;
}
/**
* Merges the default options set by the Recognizer contructor with the 'user' options passed into the 'recognize' method

@@ -765,2 +556,25 @@ */

}
/**
* Builds a LuisRecognizer Strategy depending on the options passed
*/
private buildRecognizer(userOptions: LuisRecognizerOptionsV2 | LuisRecognizerOptionsV3 | LuisPredictionOptions): LuisRecognizerV3 | LuisRecognizerV2 {
if (isLuisRecognizerOptionsV3(userOptions)) {
return new LuisRecognizerV3(this.application, userOptions);
} else if (isLuisRecognizerOptionsV2(userOptions)) {
return new LuisRecognizerV2(this.application, userOptions);
} else {
if (!this.options) {
this.options = {};
}
const merge = Object.assign(this.options, userOptions);
let recOptions: LuisRecognizerOptionsV2 = {
... merge,
apiVersion: 'v2'
};
return new LuisRecognizerV2(this.application, recOptions);
}
}
}

@@ -57,2 +57,12 @@ /**

qnaId?: number;
/**
* A value indicating whether to call test or prod environment of knowledgebase.
*/
isTest?: boolean;
/**
* Ranker types.
*/
rankerType?: string;
}

@@ -20,2 +20,3 @@ /**

import { QNAMAKER_TRACE_TYPE, QNAMAKER_TRACE_LABEL, QNAMAKER_TRACE_NAME } from '..';
import { RankerTypes } from '../qnamaker-interfaces/rankerTypes';

@@ -62,4 +63,5 @@ /**

const url: string = `${ endpoint.host }/knowledgebases/${ endpoint.knowledgeBaseId }/generateanswer`;
const queryOptions: QnAMakerOptions = { ...this._options, ...options } as QnAMakerOptions;
var queryOptions: QnAMakerOptions = { ...this._options, ...options } as QnAMakerOptions;
queryOptions.rankerType = !queryOptions.rankerType ? RankerTypes.default : queryOptions.rankerType;
this.validateOptions(queryOptions);

@@ -114,4 +116,4 @@

*/
public validateOptions(options: QnAMakerOptions): void {
const { scoreThreshold, top } = options;
public validateOptions(options: QnAMakerOptions) {
const { scoreThreshold, top, rankerType } = options;

@@ -118,0 +120,0 @@ if (scoreThreshold) {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc