diff --git a/libraries/botbuilder-ai/src/luisRecognizer.ts b/libraries/botbuilder-ai/src/luisRecognizer.ts index b579a387f6..ef7260d67d 100644 --- a/libraries/botbuilder-ai/src/luisRecognizer.ts +++ b/libraries/botbuilder-ai/src/luisRecognizer.ts @@ -6,18 +6,11 @@ * Licensed under the MIT License. */ 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'; - -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'; +import { isLuisRecognizerOptionsV2, LuisRecognizerV2 } from './luisRecognizerOptionsV2'; +import { isLuisRecognizerOptionsV3, LuisRecognizerV3 } from './luisRecognizerOptionsV3'; /** * @private @@ -64,6 +57,7 @@ export interface LuisApplication { } /** + * * Options per LUIS prediction. */ export interface LuisPredictionOptions extends LuisModels.PredictionResolveOptionalParams { @@ -136,6 +130,112 @@ export interface LuisRecognizerTelemetryClient { recognize(context: TurnContext, telemetryProperties?: { [key: string]: string }, telemetryMetrics?: { [key: string]: number }): Promise; } +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; + + /** + * (Optional) External entities recognized in query. + */ + externalEntities?: Array; + + /** + * (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 . + * 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 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; +} /** * Recognize intents in a user utterance using a configured LUIS model. @@ -156,16 +256,18 @@ export class LuisRecognizer implements LuisRecognizerTelemetryClient { private luisClient: LuisClient; private cacheKey: symbol = Symbol('results'); + private luisRecognizerInternal: LuisRecognizerV2 | LuisRecognizerV3; /** * 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?: LuisPredictionOptions, includeApiResults?: boolean) { + constructor(application: LuisApplication | string, options?: LuisRecognizerOptionsV3 | LuisRecognizerOptionsV2); + constructor(application: LuisApplication | string, options?: LuisRecognizerOptionsV3 | LuisRecognizerOptionsV2 | LuisPredictionOptions, includeApiResults?: boolean) { if (typeof application === 'string') { const parsedEndpoint: Url = Url(application); // Use exposed querystringify to parse the query string for the endpointKey value. @@ -186,26 +288,29 @@ export class LuisRecognizer implements LuisRecognizerTelemetryClient { } this.validateLuisApplication(); - this.options = { - 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: 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); - - this._telemetryClient = this.options.telemetryClient || new NullTelemetryClient(); - this._logPersonalInformation = this.options.logPersonalInformation || false; + this._telemetryClient = (options && options.telemetryClient) || new NullTelemetryClient(); + this._logPersonalInformation = (options && options.logPersonalInformation) || false; + + 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.options = { + ...options, + } + + let recOptions: LuisRecognizerOptionsV2 = { + includeAPIResults: !!includeApiResults, + ...options, + apiVersion: 'v2' + }; + + this.luisRecognizerInternal = new LuisRecognizerV2(this.application, recOptions); + } } /** @@ -270,11 +375,11 @@ export class LuisRecognizer implements LuisRecognizerTelemetryClient { * @param context Context for the current turn of conversation with the use. * @param telemetryProperties Additional properties to be logged to telemetry with the LuisResult event. * @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 { + public recognize(context: TurnContext, telemetryProperties?: { [key: string]: string }, telemetryMetrics?: { [key: string]: number }, options?: LuisRecognizerOptionsV2 | LuisRecognizerOptionsV3 | LuisPredictionOptions): Promise { 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) { const utterance: string = context.activity.text || ''; let recognizerPromise: Promise; @@ -287,29 +392,7 @@ export class LuisRecognizer implements LuisRecognizerTelemetryClient { entities: {}, }); } 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); } return recognizerPromise @@ -320,9 +403,7 @@ export class LuisRecognizer implements LuisRecognizerTelemetryClient { // Log telemetry this.onRecognizerResults(recognizerResult, context, telemetryProperties, telemetryMetrics); - return this.emitTraceInfo(context, recognizerResult.luisResult || null, recognizerResult).then(() => { - return recognizerResult; - }); + return recognizerResult; }) .catch((error: any) => { this.prepareErrorMessage(error); @@ -402,40 +483,6 @@ export class LuisRecognizer implements LuisRecognizerTelemetryClient { return properties; } - - 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 { - 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 { // If the `error` received is a azure-cognitiveservices-luis-runtime error, // it may have a `response` property and `response.statusCode`. @@ -475,262 +522,6 @@ export class LuisRecognizer implements LuisRecognizerTelemetryClient { } } - 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 childrenEntities: 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 = 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(childrenEntities, this.getNormalizedEntityName(entity), val); - if (verbose) { - this.addProperty(childrenEntities.$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), childrenEntities); - 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 */ @@ -751,4 +542,27 @@ export class LuisRecognizer implements LuisRecognizerTelemetryClient { throw new Error(`Invalid \`endpointKey\` value detected: ${this.application.endpointKey}\nPlease make sure your endpointKey is a valid LUIS Endpoint Key, e.g. "048ec46dc58e495482b0c447cfdbd291".`); } } + + /** + * 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); + } + } } diff --git a/libraries/botbuilder-ai/src/luisRecognizerOptions.ts b/libraries/botbuilder-ai/src/luisRecognizerOptions.ts new file mode 100644 index 0000000000..7003903d81 --- /dev/null +++ b/libraries/botbuilder-ai/src/luisRecognizerOptions.ts @@ -0,0 +1,27 @@ +/** + * @module botbuilder-ai + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import {LuisApplication, LuisRecognizerOptions} from './luisRecognizer' +import { RecognizerResult, TurnContext } from 'botbuilder-core'; + +export abstract class LuisRecognizerInternal { + + constructor(application: LuisApplication, options?: LuisRecognizerOptions) + { + if (!application) { + throw new Error('Null Application\n'); + } + this.application = application; + this.application.endpoint = this.application.endpoint ? this.application.endpoint : 'https://westus.api.cognitive.microsoft.com'; + } + + application: LuisApplication; + + abstract recognizeInternalAsync(context: TurnContext): Promise; + +} \ No newline at end of file diff --git a/libraries/botbuilder-ai/src/luisRecognizerOptionsV2.ts b/libraries/botbuilder-ai/src/luisRecognizerOptionsV2.ts new file mode 100644 index 0000000000..f5c04d9e8d --- /dev/null +++ b/libraries/botbuilder-ai/src/luisRecognizerOptionsV2.ts @@ -0,0 +1,385 @@ +/** + * @module botbuilder-ai + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import { LUISRuntimeClient as LuisClient, LUISRuntimeModels as LuisModels } from '@azure/cognitiveservices-luis-runtime'; +import * as msRest from '@azure/ms-rest-js'; +import { LuisRecognizerInternal } from './luisRecognizerOptions' +import { LuisApplication, LuisRecognizerOptionsV2} from './luisRecognizer' +import { NullTelemetryClient, TurnContext, RecognizerResult } from 'botbuilder-core'; +import * as os from 'os'; +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'; + + + +export function isLuisRecognizerOptionsV2(options: any): options is LuisRecognizerOptionsV2 { + return (options.apiVersion && options.apiVersion === "v2"); +} + +export class LuisRecognizerV2 extends LuisRecognizerInternal { + constructor (application: LuisApplication, options?: LuisRecognizerOptionsV2) { + super(application); + // 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(application.endpointKey); + const baseUri: string = application.endpoint || 'https://westus.api.cognitive.microsoft.com'; + this.luisClient = new LuisClient(creds as any, baseUri); + + this.options = { + includeAllIntents: false, + includeInstanceData: true, + log: true, + spellCheck: false, + staging: false, + telemetryClient: new NullTelemetryClient(), + logPersonalInformation: false, + includeAPIResults: true, + ...options + }; + } + + public options: LuisRecognizerOptionsV2; + + private luisClient: LuisClient; + + async recognizeInternalAsync(context: TurnContext): Promise { + const luisPredictionOptions = this.options; + const utterance: string = context.activity.text || ''; + + if (!utterance.trim()) { + // Bypass LUIS if the activity's text is null or whitespace + return { + text: utterance, + intents: { '': { score: 1 } }, + entities: {}, + }; + } + + const luisResult: LuisModels.LuisResult = await 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 + const result = { + 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: (luisPredictionOptions.includeAPIResults ? luisResult : null) + }; + + this.emitTraceInfo(context, luisResult, result); + return result; + } + + 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 populateCompositeEntity( + compositeEntity: LuisModels.CompositeEntityModel, + entities: LuisModels.EntityModel[], + entitiesAndMetadata: any, + verbose: boolean + ): LuisModels.EntityModel[] { + const childrenEntities: 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 = 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); + + const val = this.getEntityValue(entity); + if (val != null) { + this.addProperty(childrenEntities, this.getNormalizedEntityName(entity), val); + if (verbose) { + this.addProperty(childrenEntities.$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), childrenEntities); + if (verbose) { + this.addProperty(entitiesAndMetadata.$instance, this.getNormalizedEntityName(compositeEntityMetadata), childrenEntitiesMetadata); + } + + return filteredEntities; + } + + 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, '_'); + } + + /** + * 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; + } + + 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 { + const traceInfo: any = { + 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 + }); + } + +} \ No newline at end of file diff --git a/libraries/botbuilder-ai/src/luisRecognizerOptionsV3.ts b/libraries/botbuilder-ai/src/luisRecognizerOptionsV3.ts new file mode 100644 index 0000000000..4cd6440b2b --- /dev/null +++ b/libraries/botbuilder-ai/src/luisRecognizerOptionsV3.ts @@ -0,0 +1,291 @@ +/** + * @module botbuilder-ai + */ +/** + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import { LUISRuntimeModels as LuisModels } from '@azure/cognitiveservices-luis-runtime'; +import { LuisRecognizerInternal } from './luisRecognizerOptions' +import { LuisApplication , LuisRecognizerOptionsV3} from './luisRecognizer' +import { NullTelemetryClient, TurnContext , RecognizerResult} from 'botbuilder-core'; +const fetch = require('node-fetch'); +const LUIS_TRACE_TYPE = 'https://www.luis.ai/schemas/trace'; +const LUIS_TRACE_NAME = 'LuisRecognizer'; +const LUIS_TRACE_LABEL = 'LuisV3 Trace'; +const _dateSubtypes = [ "date", "daterange", "datetime", "datetimerange", "duration", "set", "time", "timerange" ]; +const _geographySubtypes = [ "poi", "city", "countryRegion", "continet", "state" ]; +const MetadataKey = "$instance"; + + +export function isLuisRecognizerOptionsV3(options: any): options is LuisRecognizerOptionsV3 { + return (options.apiVersion && options.apiVersion === "v3"); +} + +export class LuisRecognizerV3 extends LuisRecognizerInternal { + + constructor (application: LuisApplication, options?: LuisRecognizerOptionsV3) { + super(application); + + this.predictionOptions = { + includeAllIntents: false, + includeInstanceData: true, + log: true, + preferExternalEntities: true, + slot: 'production', + telemetryClient: new NullTelemetryClient(), + logPersonalInformation: false, + includeAPIResults: true, + ...options + }; + } + + public predictionOptions: LuisRecognizerOptionsV3; + + async recognizeInternalAsync(context: TurnContext): Promise { + const utterance: string = context.activity.text || ''; + if (!utterance.trim()) { + // Bypass LUIS if the activity's text is null or whitespace + return Promise.resolve({ + text: utterance, + intents: { '': { score: 1 } }, + entities: {}, + }); + } + + const uri = this.buildUrl(); + const httpOptions = this.buildRequestBody(utterance); + + const data = await fetch(uri, httpOptions) + const response = await data.json(); + const result: RecognizerResult = { + text: utterance, + intents : getIntents(response.prediction), + entities : extractEntitiesAndMetadata(response.prediction), + sentiment: getSentiment(response.prediction), + luisResult: (this.predictionOptions.includeAPIResults ? response : null) + } + + if (this.predictionOptions.includeInstanceData) { + result.entities[MetadataKey] = result.entities[MetadataKey] ? result.entities[MetadataKey] : {} + } + + this.emitTraceInfo(context, response.prediction, result); + + return result; + + } + + private buildUrl() { + const baseUri = this.application.endpoint || 'https://westus.api.cognitive.microsoft.com'; + let uri = `${baseUri}/luis/prediction/v3.0/apps/${this.application.applicationId}`; + + if (this.predictionOptions.version) { + uri += `/versions/${this.predictionOptions.version}/predict` + } else { + uri += `/slots/${this.predictionOptions.slot}/predict` + } + + const params = `?verbose=${this.predictionOptions.includeInstanceData}&log=${this.predictionOptions.log}&show-all-intents=${this.predictionOptions.includeAllIntents}`; + + uri += params; + return uri; + } + + private buildRequestBody(utterance: string){ + const content = { + 'query': utterance, + 'options': { + 'overridePredictions': this.predictionOptions.preferExternalEntities + } + }; + + if (this.predictionOptions.dynamicLists){ + content['dynamicLists'] = this.predictionOptions.dynamicLists + } + + if (this.predictionOptions.externalEntities){ + content['externalEntities'] = this.predictionOptions.externalEntities + } + + return { + method: 'POST', + body: JSON.stringify(content), + headers: { + 'Content-Type': 'application/json', + 'Ocp-Apim-Subscription-Key' : this.application.endpointKey, + } + }; + + } + + private emitTraceInfo(context: TurnContext, luisResult: LuisModels.LuisResult, recognizerResult: RecognizerResult): Promise { + const traceInfo: any = { + recognizerResult: recognizerResult, + luisResult: luisResult, + luisOptions: this.predictionOptions, + luisModel: { + ModelID: this.application.applicationId + } + }; + + return context.sendActivity({ + type: 'trace', + valueType: LUIS_TRACE_TYPE, + name: LUIS_TRACE_NAME, + label: LUIS_TRACE_LABEL, + value: traceInfo + }); + } +} + +function normalizeName(name) { + return name.replace(/\.| /g, '_'); +} + +function getIntents(luisResult) { + // let intents: { [name: string]: { score: number } } = {}; + const intents = {}; + if (luisResult.intents) { + for (let intent in luisResult.intents) { + intents[normalizeName(intent)] = { score: luisResult.intents[intent].score}; + } + } + + return intents; +} + +function normalizeEntity(entity) { + const splitEntity = entity.split(':'); + const entityName = splitEntity[splitEntity.length -1]; + return entityName.replace(/\.| /g, '_'); +} + +function mapProperties(source, inInstance){ + let result = source; + if (source instanceof Array) { + let narr = []; + for (let item of source) { + + // Check if element is geographyV2 + let isGeographyV2 = ''; + if (item['type'] && _geographySubtypes.includes(item['type'])) + { + isGeographyV2 = item['type']; + } + + if (!inInstance && isGeographyV2) { + let geoEntity: any = {}; + for (let itemProps in item) { + if (itemProps === 'value') + { + geoEntity.location = item[itemProps]; + } + } + geoEntity.type = isGeographyV2; + narr.push(geoEntity); + } else { + narr.push(mapProperties(item, inInstance)); + } + } + result = narr; + + } else if (source instanceof Object && typeof source !== 'string') { + let nobj: any = {}; + + // Fix datetime by reverting to simple timex + if (!inInstance && source.type && typeof source.type === 'string' && _dateSubtypes.includes(source.type)) + { + let timexs = source.values; + let arr = []; + if (timexs) + { + let unique = []; + for(let elt of timexs) + { + if (elt.timex && !unique.includes(elt.timex)) { + unique.push(elt.timex); + } + } + + for (let timex of unique) + { + arr.push(timex); + } + + nobj.timex = arr; + } + + nobj.type = source.type; + } + else + { + // Map or remove properties + for (let property in source) + { + let name = normalizeEntity(property); + let isArray = source[property] instanceof Array; + let isString = typeof source[property] === 'string'; + let isInt = Number.isInteger(source[property]); + let val = mapProperties(source[property], inInstance || property == MetadataKey); + if (name == "datetime" && isArray) + { + nobj.datetimeV1 = val; + } + else if (name == "datetimeV2" && isArray) + { + nobj.datetime = val; + } + else if (inInstance) + { + // Correct $instance issues + if (name == "length" && isInt) + { + nobj['endIndex'] = source[name] + source.startIndex; + } + else if (!((isInt && name === "modelTypeId") || + (isString && name === "role"))) + { + nobj[name] = val; + } + } + else + { + // Correct non-$instance values + if (name == "unit" && isString) + { + nobj.units = val; + } + else + { + nobj[name] = val; + } + } + } + + } + result = nobj; + } + return result; +} + +function extractEntitiesAndMetadata(prediction){ + + const entities = prediction.entities; + return mapProperties(entities, false); +} + +function getSentiment(luis): any { + let result: any; + if (luis.sentiment) { + result = { + label: luis.sentiment.label, + score: luis.sentiment.score + }; + } + + return result; +} + + diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Composite1.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Composite1.json new file mode 100644 index 0000000000..ddff83e083 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Composite1.json @@ -0,0 +1,1285 @@ +{ + "text": "12 years old and 3 days old and monday july 3rd, 2019 and every monday and between 3am and 5:30am and 4 acres and 4 pico meters and chrimc@hotmail.com and $4 and $4.25 and also 32 and 210.4 and first and 10% and 10.5% and 425-555-1234 and 3 degrees and -27.5 degrees c and the next one and the previous one", + "intents": { + "Cancel": { + "score": 0.00000156337478 + }, + "Delivery": { + "score": 0.0002846266 + }, + "EntityTests": { + "score": 0.953405857 + }, + "Greeting": { + "score": 8.20979437e-7 + }, + "Help": { + "score": 0.00000481870757 + }, + "None": { + "score": 0.01040122 + }, + "Roles": { + "score": 0.197366714 + }, + "search": { + "score": 0.14049834 + }, + "SpecifyName": { + "score": 0.000137732946 + }, + "Travel": { + "score": 0.0100996653 + }, + "Weather_GetForecast": { + "score": 0.0143940123 + } + }, + "entities": { + "$instance": { + "Composite1": [ + { + "endIndex": 306, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.880988955, + "startIndex": 0, + "text": "12 years old and 3 days old and monday july 3rd, 2019 and every monday and between 3am and 5:30am and 4 acres and 4 pico meters and chrimc@hotmail.com and $4 and $4.25 and also 32 and 210.4 and first and 10% and 10.5% and 425-555-1234 and 3 degrees and -27.5 degrees c and the next one and the previous one", + "type": "Composite1" + } + ], + "ordinalV2": [ + { + "endIndex": 47, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 44, + "text": "3rd", + "type": "builtin.ordinalV2" + }, + { + "endIndex": 199, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 194, + "text": "first", + "type": "builtin.ordinalV2" + }, + { + "endIndex": 285, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 277, + "text": "next one", + "type": "builtin.ordinalV2.relative" + }, + { + "endIndex": 306, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 294, + "text": "previous one", + "type": "builtin.ordinalV2.relative" + } + ] + }, + "Composite1": [ + { + "$instance": { + "age": [ + { + "endIndex": 12, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "12 years old", + "type": "builtin.age" + }, + { + "endIndex": 27, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "3 days old", + "type": "builtin.age" + } + ], + "datetime": [ + { + "endIndex": 8, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "12 years", + "type": "builtin.datetimeV2.duration" + }, + { + "endIndex": 23, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "3 days", + "type": "builtin.datetimeV2.duration" + }, + { + "endIndex": 53, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 32, + "text": "monday july 3rd, 2019", + "type": "builtin.datetimeV2.date" + }, + { + "endIndex": 70, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 58, + "text": "every monday", + "type": "builtin.datetimeV2.set" + }, + { + "endIndex": 97, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 75, + "text": "between 3am and 5:30am", + "type": "builtin.datetimeV2.timerange" + } + ], + "dimension": [ + { + "endIndex": 109, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 102, + "text": "4 acres", + "type": "builtin.dimension" + }, + { + "endIndex": 127, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 114, + "text": "4 pico meters", + "type": "builtin.dimension" + } + ], + "email": [ + { + "endIndex": 150, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 132, + "text": "chrimc@hotmail.com", + "type": "builtin.email" + } + ], + "money": [ + { + "endIndex": 157, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 155, + "text": "$4", + "type": "builtin.currency" + }, + { + "endIndex": 167, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 162, + "text": "$4.25", + "type": "builtin.currency" + } + ], + "number": [ + { + "endIndex": 2, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "12", + "type": "builtin.number" + }, + { + "endIndex": 18, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "3", + "type": "builtin.number" + }, + { + "endIndex": 53, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 49, + "text": "2019", + "type": "builtin.number" + }, + { + "endIndex": 92, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 91, + "text": "5", + "type": "builtin.number" + }, + { + "endIndex": 103, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 102, + "text": "4", + "type": "builtin.number" + }, + { + "endIndex": 115, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 114, + "text": "4", + "type": "builtin.number" + }, + { + "endIndex": 157, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 156, + "text": "4", + "type": "builtin.number" + }, + { + "endIndex": 167, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 163, + "text": "4.25", + "type": "builtin.number" + }, + { + "endIndex": 179, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 177, + "text": "32", + "type": "builtin.number" + }, + { + "endIndex": 189, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 184, + "text": "210.4", + "type": "builtin.number" + }, + { + "endIndex": 206, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 204, + "text": "10", + "type": "builtin.number" + }, + { + "endIndex": 216, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 212, + "text": "10.5", + "type": "builtin.number" + }, + { + "endIndex": 225, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 222, + "text": "425", + "type": "builtin.number" + }, + { + "endIndex": 229, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 226, + "text": "555", + "type": "builtin.number" + }, + { + "endIndex": 234, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 230, + "text": "1234", + "type": "builtin.number" + }, + { + "endIndex": 240, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 239, + "text": "3", + "type": "builtin.number" + }, + { + "endIndex": 258, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 253, + "text": "-27.5", + "type": "builtin.number" + }, + { + "endIndex": 285, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 282, + "text": "one", + "type": "builtin.number" + }, + { + "endIndex": 306, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 303, + "text": "one", + "type": "builtin.number" + } + ], + "percentage": [ + { + "endIndex": 207, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 204, + "text": "10%", + "type": "builtin.percentage" + }, + { + "endIndex": 217, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 212, + "text": "10.5%", + "type": "builtin.percentage" + } + ], + "phonenumber": [ + { + "endIndex": 234, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.9, + "startIndex": 222, + "text": "425-555-1234", + "type": "builtin.phonenumber" + } + ], + "temperature": [ + { + "endIndex": 248, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 239, + "text": "3 degrees", + "type": "builtin.temperature" + }, + { + "endIndex": 268, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 253, + "text": "-27.5 degrees c", + "type": "builtin.temperature" + } + ] + }, + "age": [ + { + "number": 12, + "units": "Year" + }, + { + "number": 3, + "units": "Day" + } + ], + "datetime": [ + { + "timex": [ + "P12Y" + ], + "type": "duration" + }, + { + "timex": [ + "P3D" + ], + "type": "duration" + }, + { + "timex": [ + "2019-07-03" + ], + "type": "date" + }, + { + "timex": [ + "XXXX-WXX-1" + ], + "type": "set" + }, + { + "timex": [ + "(T03,T05:30,PT2H30M)" + ], + "type": "timerange" + } + ], + "dimension": [ + { + "number": 4, + "units": "Acre" + }, + { + "number": 4, + "units": "Picometer" + } + ], + "email": [ + "chrimc@hotmail.com" + ], + "money": [ + { + "number": 4, + "units": "Dollar" + }, + { + "number": 4.25, + "units": "Dollar" + } + ], + "number": [ + 12, + 3, + 2019, + 5, + 4, + 4, + 4, + 4.25, + 32, + 210.4, + 10, + 10.5, + 425, + 555, + 1234, + 3, + -27.5, + 1, + 1 + ], + "percentage": [ + 10, + 10.5 + ], + "phonenumber": [ + "425-555-1234" + ], + "temperature": [ + { + "number": 3, + "units": "Degree" + }, + { + "number": -27.5, + "units": "C" + } + ] + } + ], + "ordinalV2": [ + { + "offset": 3, + "relativeTo": "start" + }, + { + "offset": 1, + "relativeTo": "start" + }, + { + "offset": 1, + "relativeTo": "current" + }, + { + "offset": -1, + "relativeTo": "current" + } + ] + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "v3": { + "response": { + "prediction": { + "entities": { + "$instance": { + "Composite1": [ + { + "length": 306, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "model" + ], + "score": 0.880988955, + "startIndex": 0, + "text": "12 years old and 3 days old and monday july 3rd, 2019 and every monday and between 3am and 5:30am and 4 acres and 4 pico meters and chrimc@hotmail.com and $4 and $4.25 and also 32 and 210.4 and first and 10% and 10.5% and 425-555-1234 and 3 degrees and -27.5 degrees c and the next one and the previous one", + "type": "Composite1" + } + ], + "ordinalV2": [ + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 44, + "text": "3rd", + "type": "builtin.ordinalV2" + }, + { + "length": 5, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 194, + "text": "first", + "type": "builtin.ordinalV2" + }, + { + "length": 8, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 277, + "text": "next one", + "type": "builtin.ordinalV2.relative" + }, + { + "length": 12, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 294, + "text": "previous one", + "type": "builtin.ordinalV2.relative" + } + ] + }, + "Composite1": [ + { + "$instance": { + "age": [ + { + "length": 12, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "12 years old", + "type": "builtin.age" + }, + { + "length": 10, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "3 days old", + "type": "builtin.age" + } + ], + "datetimeV2": [ + { + "length": 8, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "12 years", + "type": "builtin.datetimeV2.duration" + }, + { + "length": 6, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "3 days", + "type": "builtin.datetimeV2.duration" + }, + { + "length": 21, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 32, + "text": "monday july 3rd, 2019", + "type": "builtin.datetimeV2.date" + }, + { + "length": 12, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 58, + "text": "every monday", + "type": "builtin.datetimeV2.set" + }, + { + "length": 22, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 75, + "text": "between 3am and 5:30am", + "type": "builtin.datetimeV2.timerange" + } + ], + "dimension": [ + { + "length": 7, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 102, + "text": "4 acres", + "type": "builtin.dimension" + }, + { + "length": 13, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 114, + "text": "4 pico meters", + "type": "builtin.dimension" + } + ], + "email": [ + { + "length": 18, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 132, + "text": "chrimc@hotmail.com", + "type": "builtin.email" + } + ], + "money": [ + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 155, + "text": "$4", + "type": "builtin.currency" + }, + { + "length": 5, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 162, + "text": "$4.25", + "type": "builtin.currency" + } + ], + "number": [ + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "12", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "3", + "type": "builtin.number" + }, + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 49, + "text": "2019", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 91, + "text": "5", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 102, + "text": "4", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 114, + "text": "4", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 156, + "text": "4", + "type": "builtin.number" + }, + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 163, + "text": "4.25", + "type": "builtin.number" + }, + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 177, + "text": "32", + "type": "builtin.number" + }, + { + "length": 5, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 184, + "text": "210.4", + "type": "builtin.number" + }, + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 204, + "text": "10", + "type": "builtin.number" + }, + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 212, + "text": "10.5", + "type": "builtin.number" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 222, + "text": "425", + "type": "builtin.number" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 226, + "text": "555", + "type": "builtin.number" + }, + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 230, + "text": "1234", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 239, + "text": "3", + "type": "builtin.number" + }, + { + "length": 5, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 253, + "text": "-27.5", + "type": "builtin.number" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 282, + "text": "one", + "type": "builtin.number" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 303, + "text": "one", + "type": "builtin.number" + } + ], + "percentage": [ + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 204, + "text": "10%", + "type": "builtin.percentage" + }, + { + "length": 5, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 212, + "text": "10.5%", + "type": "builtin.percentage" + } + ], + "phonenumber": [ + { + "length": 12, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "score": 0.9, + "startIndex": 222, + "text": "425-555-1234", + "type": "builtin.phonenumber" + } + ], + "temperature": [ + { + "length": 9, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 239, + "text": "3 degrees", + "type": "builtin.temperature" + }, + { + "length": 15, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 253, + "text": "-27.5 degrees c", + "type": "builtin.temperature" + } + ] + }, + "age": [ + { + "number": 12, + "unit": "Year" + }, + { + "number": 3, + "unit": "Day" + } + ], + "datetimeV2": [ + { + "type": "duration", + "values": [ + { + "timex": "P12Y", + "value": "378432000" + } + ] + }, + { + "type": "duration", + "values": [ + { + "timex": "P3D", + "value": "259200" + } + ] + }, + { + "type": "date", + "values": [ + { + "timex": "2019-07-03", + "value": "2019-07-03" + } + ] + }, + { + "type": "set", + "values": [ + { + "timex": "XXXX-WXX-1", + "value": "not resolved" + } + ] + }, + { + "type": "timerange", + "values": [ + { + "end": "05:30:00", + "start": "03:00:00", + "timex": "(T03,T05:30,PT2H30M)" + } + ] + } + ], + "dimension": [ + { + "number": 4, + "unit": "Acre" + }, + { + "number": 4, + "unit": "Picometer" + } + ], + "email": [ + "chrimc@hotmail.com" + ], + "money": [ + { + "number": 4, + "unit": "Dollar" + }, + { + "number": 4.25, + "unit": "Dollar" + } + ], + "number": [ + 12, + 3, + 2019, + 5, + 4, + 4, + 4, + 4.25, + 32, + 210.4, + 10, + 10.5, + 425, + 555, + 1234, + 3, + -27.5, + 1, + 1 + ], + "percentage": [ + 10, + 10.5 + ], + "phonenumber": [ + "425-555-1234" + ], + "temperature": [ + { + "number": 3, + "unit": "Degree" + }, + { + "number": -27.5, + "unit": "C" + } + ] + } + ], + "ordinalV2": [ + { + "offset": 3, + "relativeTo": "start" + }, + { + "offset": 1, + "relativeTo": "start" + }, + { + "offset": 1, + "relativeTo": "current" + }, + { + "offset": -1, + "relativeTo": "current" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.00000156337478 + }, + "Delivery": { + "score": 0.0002846266 + }, + "EntityTests": { + "score": 0.953405857 + }, + "Greeting": { + "score": 8.20979437e-7 + }, + "Help": { + "score": 0.00000481870757 + }, + "None": { + "score": 0.01040122 + }, + "Roles": { + "score": 0.197366714 + }, + "search": { + "score": 0.14049834 + }, + "SpecifyName": { + "score": 0.000137732946 + }, + "Travel": { + "score": 0.0100996653 + }, + "Weather.GetForecast": { + "score": 0.0143940123 + } + }, + "normalizedQuery": "12 years old and 3 days old and monday july 3rd, 2019 and every monday and between 3am and 5:30am and 4 acres and 4 pico meters and chrimc@hotmail.com and $4 and $4.25 and also 32 and 210.4 and first and 10% and 10.5% and 425-555-1234 and 3 degrees and -27.5 degrees c and the next one and the previous one", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "EntityTests" + }, + "query": "12 years old and 3 days old and monday july 3rd, 2019 and every monday and between 3am and 5:30am and 4 acres and 4 pico meters and chrimc@hotmail.com and $4 and $4.25 and also 32 and 210.4 and first and 10% and 10.5% and 425-555-1234 and 3 degrees and -27.5 degrees c and the next one and the previous one" + }, + "options": { + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + } + } +} diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Composite2.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Composite2.json new file mode 100644 index 0000000000..11fc7bb894 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Composite2.json @@ -0,0 +1,312 @@ +{ + "entities": { + "$instance": { + "Composite2": [ + { + "endIndex": 69, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.97076714, + "startIndex": 0, + "text": "http://foo.com is where you can fly from seattle to dallas via denver", + "type": "Composite2" + } + ], + "geographyV2": [ + { + "endIndex": 48, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 41, + "text": "seattle", + "type": "builtin.geographyV2.city" + } + ] + }, + "Composite2": [ + { + "$instance": { + "City": [ + { + "endIndex": 69, + "modelType": "Hierarchical Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.984581649, + "startIndex": 63, + "text": "denver", + "type": "City" + } + ], + "From": [ + { + "endIndex": 48, + "modelType": "Hierarchical Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.999511, + "startIndex": 41, + "text": "seattle", + "type": "City::From" + } + ], + "To": [ + { + "endIndex": 58, + "modelType": "Hierarchical Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.9984612, + "startIndex": 52, + "text": "dallas", + "type": "City::To" + } + ], + "url": [ + { + "endIndex": 14, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "http://foo.com", + "type": "builtin.url" + } + ] + }, + "City": [ + "denver" + ], + "From": [ + "seattle" + ], + "To": [ + "dallas" + ], + "url": [ + "http://foo.com" + ] + } + ], + "geographyV2": [ + { + "location": "seattle", + "type": "city" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.000227437369 + }, + "Delivery": { + "score": 0.001310123 + }, + "EntityTests": { + "score": 0.94500196 + }, + "Greeting": { + "score": 0.000152356763 + }, + "Help": { + "score": 0.000547201431 + }, + "None": { + "score": 0.004187195 + }, + "Roles": { + "score": 0.0300086979 + }, + "search": { + "score": 0.0108942846 + }, + "SpecifyName": { + "score": 0.00168467627 + }, + "Travel": { + "score": 0.0154484725 + }, + "Weather_GetForecast": { + "score": 0.0237181056 + } + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "text": "http://foo.com is where you can fly from seattle to dallas via denver", + "v3": { + "options": { + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "$instance": { + "Composite2": [ + { + "length": 69, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "model" + ], + "score": 0.97076714, + "startIndex": 0, + "text": "http://foo.com is where you can fly from seattle to dallas via denver", + "type": "Composite2" + } + ], + "geographyV2": [ + { + "length": 7, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 41, + "text": "seattle", + "type": "builtin.geographyV2.city" + } + ] + }, + "Composite2": [ + { + "$instance": { + "City": [ + { + "length": 6, + "modelType": "Hierarchical Entity Extractor", + "modelTypeId": 3, + "recognitionSources": [ + "model" + ], + "score": 0.984581649, + "startIndex": 63, + "text": "denver", + "type": "City" + } + ], + "City::From": [ + { + "length": 7, + "modelType": "Hierarchical Entity Extractor", + "modelTypeId": 3, + "recognitionSources": [ + "model" + ], + "score": 0.999511, + "startIndex": 41, + "text": "seattle", + "type": "City::From" + } + ], + "City::To": [ + { + "length": 6, + "modelType": "Hierarchical Entity Extractor", + "modelTypeId": 3, + "recognitionSources": [ + "model" + ], + "score": 0.9984612, + "startIndex": 52, + "text": "dallas", + "type": "City::To" + } + ], + "url": [ + { + "length": 14, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "http://foo.com", + "type": "builtin.url" + } + ] + }, + "City": [ + "denver" + ], + "City::From": [ + "seattle" + ], + "City::To": [ + "dallas" + ], + "url": [ + "http://foo.com" + ] + } + ], + "geographyV2": [ + { + "type": "city", + "value": "seattle" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.000227437369 + }, + "Delivery": { + "score": 0.001310123 + }, + "EntityTests": { + "score": 0.94500196 + }, + "Greeting": { + "score": 0.000152356763 + }, + "Help": { + "score": 0.000547201431 + }, + "None": { + "score": 0.004187195 + }, + "Roles": { + "score": 0.0300086979 + }, + "search": { + "score": 0.0108942846 + }, + "SpecifyName": { + "score": 0.00168467627 + }, + "Travel": { + "score": 0.0154484725 + }, + "Weather.GetForecast": { + "score": 0.0237181056 + } + }, + "normalizedQuery": "http://foo.com is where you can fly from seattle to dallas via denver", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "EntityTests" + }, + "query": "http://foo.com is where you can fly from seattle to dallas via denver" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Composite3.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Composite3.json new file mode 100644 index 0000000000..c9f5b8cd23 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Composite3.json @@ -0,0 +1,315 @@ +{ + "text": "Deliver from 12345 VA to 12346 WA", + "intents": { + "Cancel": { + "score": 1.01764708e-9 + }, + "Delivery": { + "score": 0.00238572317 + }, + "EntityTests": { + "score": 4.757576e-10 + }, + "Greeting": { + "score": 1.0875e-9 + }, + "Help": { + "score": 1.01764708e-9 + }, + "None": { + "score": 0.00000117844979 + }, + "Roles": { + "score": 0.999911964 + }, + "search": { + "score": 0.000009494859 + }, + "SpecifyName": { + "score": 3.0666667e-9 + }, + "Travel": { + "score": 0.00000309763345 + }, + "Weather_GetForecast": { + "score": 0.00000102792524 + } + }, + "entities": { + "$instance": { + "Destination": [ + { + "endIndex": 33, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.9818366, + "startIndex": 25, + "text": "12346 WA", + "type": "Address" + } + ], + "Source": [ + { + "endIndex": 21, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.9345161, + "startIndex": 13, + "text": "12345 VA", + "type": "Address" + } + ] + }, + "Destination": [ + { + "$instance": { + "number": [ + { + "endIndex": 30, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 25, + "text": "12346", + "type": "builtin.number" + } + ], + "State": [ + { + "endIndex": 33, + "modelType": "Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.9893861, + "startIndex": 31, + "text": "WA", + "type": "State" + } + ] + }, + "number": [ + 12346 + ], + "State": [ + "WA" + ] + } + ], + "Source": [ + { + "$instance": { + "number": [ + { + "endIndex": 18, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 13, + "text": "12345", + "type": "builtin.number" + } + ], + "State": [ + { + "endIndex": 21, + "modelType": "Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.941649556, + "startIndex": 19, + "text": "VA", + "type": "State" + } + ] + }, + "number": [ + 12345 + ], + "State": [ + "VA" + ] + } + ] + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "v3": { + "response": { + "prediction": { + "entities": { + "$instance": { + "Destination": [ + { + "length": 8, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "model" + ], + "role": "Destination", + "score": 0.9818366, + "startIndex": 25, + "text": "12346 WA", + "type": "Address" + } + ], + "Source": [ + { + "length": 8, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "model" + ], + "role": "Source", + "score": 0.9345161, + "startIndex": 13, + "text": "12345 VA", + "type": "Address" + } + ] + }, + "Destination": [ + { + "$instance": { + "number": [ + { + "length": 5, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 25, + "text": "12346", + "type": "builtin.number" + } + ], + "State": [ + { + "length": 2, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "model" + ], + "score": 0.9893861, + "startIndex": 31, + "text": "WA", + "type": "State" + } + ] + }, + "number": [ + 12346 + ], + "State": [ + "WA" + ] + } + ], + "Source": [ + { + "$instance": { + "number": [ + { + "length": 5, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 13, + "text": "12345", + "type": "builtin.number" + } + ], + "State": [ + { + "length": 2, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "model" + ], + "score": 0.941649556, + "startIndex": 19, + "text": "VA", + "type": "State" + } + ] + }, + "number": [ + 12345 + ], + "State": [ + "VA" + ] + } + ] + }, + "intents": { + "Cancel": { + "score": 1.01764708e-9 + }, + "Delivery": { + "score": 0.00238572317 + }, + "EntityTests": { + "score": 4.757576e-10 + }, + "Greeting": { + "score": 1.0875e-9 + }, + "Help": { + "score": 1.01764708e-9 + }, + "None": { + "score": 0.00000117844979 + }, + "Roles": { + "score": 0.999911964 + }, + "search": { + "score": 0.000009494859 + }, + "SpecifyName": { + "score": 3.0666667e-9 + }, + "Travel": { + "score": 0.00000309763345 + }, + "Weather.GetForecast": { + "score": 0.00000102792524 + } + }, + "normalizedQuery": "deliver from 12345 va to 12346 wa", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "Roles" + }, + "query": "Deliver from 12345 VA to 12346 WA" + }, + "options": { + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production", + "version": "GeoPeople" + } + } +} diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/DynamicListsAndList.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/DynamicListsAndList.json new file mode 100644 index 0000000000..c20840cff3 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/DynamicListsAndList.json @@ -0,0 +1,213 @@ +{ + "entities": { + "$instance": { + "Airline": [ + { + "endIndex": 21, + "modelType": "List Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 4, + "text": "zimbabwe airlines", + "type": "Airline" + }, + { + "endIndex": 33, + "modelType": "List Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 25, + "text": "deltaair", + "type": "Airline" + } + ], + "endloc": [ + { + "endIndex": 12, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 4, + "text": "zimbabwe", + "type": "builtin.geographyV2.countryRegion" + } + ] + }, + "Airline": [ + [ + "ZimAir" + ], + [ + "DeltaAir" + ] + ], + "endloc": [ + { + "location": "zimbabwe", + "type": "countryRegion" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.006612578 + }, + "Delivery": { + "score": 0.00618253648 + }, + "EntityTests": { + "score": 0.05013815 + }, + "Greeting": { + "score": 0.005450715 + }, + "Help": { + "score": 0.00571808033 + }, + "None": { + "score": 0.0357324854 + }, + "Roles": { + "score": 0.0241017919 + }, + "search": { + "score": 0.006795147 + }, + "SpecifyName": { + "score": 0.006843741 + }, + "Travel": { + "score": 0.017196469 + }, + "Weather_GetForecast": { + "score": 0.006921158 + } + }, + "text": "fly zimbabwe airlines or deltaair", + "v3": { + "options": { + "dynamicLists": [ + { + "listEntityName": "Airline", + "requestLists": [ + { + "canonicalForm": "ZimAir", + "synonyms": [ + "zimbabwe airlines" + ] + }, + { + "canonicalForm": "DeltaAir" + } + ] + } + ], + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "$instance": { + "Airline": [ + { + "length": 17, + "modelType": "List Entity Extractor", + "modelTypeId": 5, + "recognitionSources": [ + "model" + ], + "startIndex": 4, + "text": "zimbabwe airlines", + "type": "Airline" + }, + { + "length": 8, + "modelType": "List Entity Extractor", + "modelTypeId": 5, + "recognitionSources": [ + "model" + ], + "startIndex": 25, + "text": "deltaair", + "type": "Airline" + } + ], + "endloc": [ + { + "length": 8, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "endloc", + "startIndex": 4, + "text": "zimbabwe", + "type": "builtin.geographyV2.countryRegion" + } + ] + }, + "Airline": [ + [ + "ZimAir" + ], + [ + "DeltaAir" + ] + ], + "endloc": [ + { + "type": "countryRegion", + "value": "zimbabwe" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.006612578 + }, + "Delivery": { + "score": 0.00618253648 + }, + "EntityTests": { + "score": 0.05013815 + }, + "Greeting": { + "score": 0.005450715 + }, + "Help": { + "score": 0.00571808033 + }, + "None": { + "score": 0.0357324854 + }, + "Roles": { + "score": 0.0241017919 + }, + "search": { + "score": 0.006795147 + }, + "SpecifyName": { + "score": 0.006843741 + }, + "Travel": { + "score": 0.017196469 + }, + "Weather.GetForecast": { + "score": 0.006921158 + } + }, + "topIntent": "EntityTests" + }, + "query": "fly zimbabwe airlines or deltaair" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndBuiltin.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndBuiltin.json new file mode 100644 index 0000000000..5d95dad74a --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndBuiltin.json @@ -0,0 +1,168 @@ +{ + "text": "buy hul and 2 items", + "intents": { + "Cancel": { + "score": 0.006906527 + }, + "Delivery": { + "score": 0.00567273 + }, + "EntityTests": { + "score": 0.128755629 + }, + "Greeting": { + "score": 0.00450348156 + }, + "Help": { + "score": 0.00583425 + }, + "None": { + "score": 0.0135525977 + }, + "Roles": { + "score": 0.04635598 + }, + "search": { + "score": 0.008885799 + }, + "SpecifyName": { + "score": 0.00721160974 + }, + "Travel": { + "score": 0.005146626 + }, + "Weather_GetForecast": { + "score": 0.00913477 + } + }, + "entities": { + "$instance": { + "number": [ + { + "endIndex": 7, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 4, + "text": "hul", + "type": "builtin.number" + }, + { + "endIndex": 13, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 12, + "text": "2", + "type": "builtin.number" + } + ] + }, + "number": [ + 8, + 2 + ] + }, + "sentiment": { + "label": "positive", + "score": 0.7149857 + }, + "v3": { + "response": { + "prediction": { + "entities": { + "$instance": { + "number": [ + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 4, + "text": "hul", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 12, + "text": "2", + "type": "builtin.number" + } + ] + }, + "number": [ + 8, + 2 + ] + }, + "intents": { + "Cancel": { + "score": 0.006906527 + }, + "Delivery": { + "score": 0.00567273 + }, + "EntityTests": { + "score": 0.128755629 + }, + "Greeting": { + "score": 0.00450348156 + }, + "Help": { + "score": 0.00583425 + }, + "None": { + "score": 0.0135525977 + }, + "Roles": { + "score": 0.04635598 + }, + "search": { + "score": 0.008885799 + }, + "SpecifyName": { + "score": 0.00721160974 + }, + "Travel": { + "score": 0.005146626 + }, + "Weather.GetForecast": { + "score": 0.00913477 + } + }, + "normalizedQuery": "buy hul and 2 items", + "sentiment": { + "label": "positive", + "score": 0.7149857 + }, + "topIntent": "EntityTests" + }, + "query": "buy hul and 2 items" + }, + "options": { + "externalEntities": [ + { + "entityLength": 3, + "entityName": "number", + "resolution": 8, + "startIndex": 4 + } + ], + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + } + } +} diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndComposite.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndComposite.json new file mode 100644 index 0000000000..33c5d73428 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndComposite.json @@ -0,0 +1,261 @@ +{ + "entities": { + "$instance": { + "Address": [ + { + "endIndex": 13, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.7160641, + "startIndex": 8, + "text": "35 WA", + "type": "Address" + }, + { + "endIndex": 33, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 17, + "text": "repent harelquin", + "type": "Address" + } + ] + }, + "Address": [ + { + "$instance": { + "number": [ + { + "endIndex": 10, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 8, + "text": "35", + "type": "builtin.number" + } + ], + "State": [ + { + "endIndex": 13, + "modelType": "Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.614376, + "startIndex": 11, + "text": "WA", + "type": "State" + } + ] + }, + "number": [ + 35 + ], + "State": [ + "WA" + ] + }, + { + "number": [ + 3 + ], + "State": [ + "France" + ] + } + ] + }, + "intents": { + "Cancel": { + "score": 0.00325984019 + }, + "Delivery": { + "score": 0.482009649 + }, + "EntityTests": { + "score": 0.00372873852 + }, + "Greeting": { + "score": 0.00283122621 + }, + "Help": { + "score": 0.00292110164 + }, + "None": { + "score": 0.0208108239 + }, + "Roles": { + "score": 0.069060266 + }, + "search": { + "score": 0.009682492 + }, + "SpecifyName": { + "score": 0.00586992875 + }, + "Travel": { + "score": 0.007831623 + }, + "Weather_GetForecast": { + "score": 0.009580207 + } + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "text": "deliver 35 WA to repent harelquin", + "v3": { + "options": { + "externalEntities": [ + { + "entityLength": 16, + "entityName": "Address", + "resolution": { + "number": [ + 3 + ], + "State": [ + "France" + ] + }, + "startIndex": 17 + } + ], + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "$instance": { + "Address": [ + { + "length": 5, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "model" + ], + "score": 0.7160641, + "startIndex": 8, + "text": "35 WA", + "type": "Address" + }, + { + "length": 16, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 17, + "text": "repent harelquin", + "type": "Address" + } + ] + }, + "Address": [ + { + "$instance": { + "number": [ + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 8, + "text": "35", + "type": "builtin.number" + } + ], + "State": [ + { + "length": 2, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "model" + ], + "score": 0.614376, + "startIndex": 11, + "text": "WA", + "type": "State" + } + ] + }, + "number": [ + 35 + ], + "State": [ + "WA" + ] + }, + { + "number": [ + 3 + ], + "State": [ + "France" + ] + } + ] + }, + "intents": { + "Cancel": { + "score": 0.00325984019 + }, + "Delivery": { + "score": 0.482009649 + }, + "EntityTests": { + "score": 0.00372873852 + }, + "Greeting": { + "score": 0.00283122621 + }, + "Help": { + "score": 0.00292110164 + }, + "None": { + "score": 0.0208108239 + }, + "Roles": { + "score": 0.069060266 + }, + "search": { + "score": 0.009682492 + }, + "SpecifyName": { + "score": 0.00586992875 + }, + "Travel": { + "score": 0.007831623 + }, + "Weather.GetForecast": { + "score": 0.009580207 + } + }, + "normalizedQuery": "deliver 35 wa to repent harelquin", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "Delivery" + }, + "query": "deliver 35 WA to repent harelquin" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndList.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndList.json new file mode 100644 index 0000000000..e2cf8eb630 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndList.json @@ -0,0 +1,178 @@ +{ + "entities": { + "$instance": { + "Airline": [ + { + "endIndex": 23, + "modelType": "List Entity Extractor", + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 7, + "text": "humberg airlines", + "type": "Airline" + }, + { + "endIndex": 32, + "modelType": "List Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 27, + "text": "Delta", + "type": "Airline" + } + ] + }, + "Airline": [ + [ + "HumAir" + ], + [ + "Delta" + ] + ] + }, + "intents": { + "Cancel": { + "score": 0.00330878259 + }, + "Delivery": { + "score": 0.00452178251 + }, + "EntityTests": { + "score": 0.052175343 + }, + "Greeting": { + "score": 0.002769983 + }, + "Help": { + "score": 0.002995687 + }, + "None": { + "score": 0.0302589461 + }, + "Roles": { + "score": 0.132316783 + }, + "search": { + "score": 0.007362695 + }, + "SpecifyName": { + "score": 0.00500302855 + }, + "Travel": { + "score": 0.0146034053 + }, + "Weather_GetForecast": { + "score": 0.005048246 + } + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "text": "fly on humberg airlines or Delta", + "v3": { + "options": { + "externalEntities": [ + { + "entityLength": 16, + "entityName": "Airline", + "resolution": [ + "HumAir" + ], + "startIndex": 7 + } + ], + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "$instance": { + "Airline": [ + { + "length": 16, + "modelType": "List Entity Extractor", + "modelTypeId": 5, + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 7, + "text": "humberg airlines", + "type": "Airline" + }, + { + "length": 5, + "modelType": "List Entity Extractor", + "modelTypeId": 5, + "recognitionSources": [ + "model" + ], + "startIndex": 27, + "text": "Delta", + "type": "Airline" + } + ] + }, + "Airline": [ + [ + "HumAir" + ], + [ + "Delta" + ] + ] + }, + "intents": { + "Cancel": { + "score": 0.00330878259 + }, + "Delivery": { + "score": 0.00452178251 + }, + "EntityTests": { + "score": 0.052175343 + }, + "Greeting": { + "score": 0.002769983 + }, + "Help": { + "score": 0.002995687 + }, + "None": { + "score": 0.0302589461 + }, + "Roles": { + "score": 0.132316783 + }, + "search": { + "score": 0.007362695 + }, + "SpecifyName": { + "score": 0.00500302855 + }, + "Travel": { + "score": 0.0146034053 + }, + "Weather.GetForecast": { + "score": 0.005048246 + } + }, + "normalizedQuery": "fly on humberg airlines or delta", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "Roles" + }, + "query": "fly on humberg airlines or Delta" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndRegex.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndRegex.json new file mode 100644 index 0000000000..3a92a6ef75 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndRegex.json @@ -0,0 +1,167 @@ +{ + "entities": { + "$instance": { + "Part": [ + { + "endIndex": 5, + "modelType": "Regex Entity Extractor", + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 0, + "text": "42ski", + "type": "Part" + }, + { + "endIndex": 26, + "modelType": "Regex Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 21, + "text": "kb423", + "type": "Part" + } + ] + }, + "Part": [ + "42ski", + "kb423" + ] + }, + "intents": { + "Cancel": { + "score": 0.0127721056 + }, + "Delivery": { + "score": 0.004578639 + }, + "EntityTests": { + "score": 0.008811761 + }, + "Greeting": { + "score": 0.00256775436 + }, + "Help": { + "score": 0.00214677141 + }, + "None": { + "score": 0.27875194 + }, + "Roles": { + "score": 0.0273685548 + }, + "search": { + "score": 0.0084077 + }, + "SpecifyName": { + "score": 0.0148377549 + }, + "Travel": { + "score": 0.0039825947 + }, + "Weather_GetForecast": { + "score": 0.009611839 + } + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "text": "42ski is a part like kb423", + "v3": { + "options": { + "ExternalEntities": [ + { + "entityLength": 5, + "entityName": "Part", + "startIndex": 0 + } + ], + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "$instance": { + "Part": [ + { + "length": 5, + "modelType": "Regex Entity Extractor", + "modelTypeId": 8, + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 0, + "text": "42ski", + "type": "Part" + }, + { + "length": 5, + "modelType": "Regex Entity Extractor", + "modelTypeId": 8, + "recognitionSources": [ + "model" + ], + "startIndex": 21, + "text": "kb423", + "type": "Part" + } + ] + }, + "Part": [ + "42ski", + "kb423" + ] + }, + "intents": { + "Cancel": { + "score": 0.0127721056 + }, + "Delivery": { + "score": 0.004578639 + }, + "EntityTests": { + "score": 0.008811761 + }, + "Greeting": { + "score": 0.00256775436 + }, + "Help": { + "score": 0.00214677141 + }, + "None": { + "score": 0.27875194 + }, + "Roles": { + "score": 0.0273685548 + }, + "search": { + "score": 0.0084077 + }, + "SpecifyName": { + "score": 0.0148377549 + }, + "Travel": { + "score": 0.0039825947 + }, + "Weather.GetForecast": { + "score": 0.009611839 + } + }, + "normalizedQuery": "42ski is a part like kb423", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "None" + }, + "query": "42ski is a part like kb423" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndSimple.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndSimple.json new file mode 100644 index 0000000000..e7073627d7 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndSimple.json @@ -0,0 +1,292 @@ +{ + "entities": { + "$instance": { + "Address": [ + { + "endIndex": 13, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.7033113, + "startIndex": 8, + "text": "37 wa", + "type": "Address" + } + ], + "number": [ + { + "endIndex": 19, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "82", + "type": "builtin.number" + } + ], + "State": [ + { + "endIndex": 22, + "modelType": "Entity Extractor", + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 20, + "text": "co", + "type": "State" + } + ] + }, + "Address": [ + { + "$instance": { + "number": [ + { + "endIndex": 10, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 8, + "text": "37", + "type": "builtin.number" + } + ], + "State": [ + { + "endIndex": 13, + "modelType": "Entity Extractor", + "recognitionSources": [ + "model", + "externalEntities" + ], + "score": 0.5987082, + "startIndex": 11, + "text": "wa", + "type": "State" + } + ] + }, + "number": [ + 37 + ], + "State": [ + "wa" + ] + } + ], + "number": [ + 82 + ], + "State": [ + { + "state": "Colorado" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.004045653 + }, + "Delivery": { + "score": 0.511144161 + }, + "EntityTests": { + "score": 0.004197402 + }, + "Greeting": { + "score": 0.00286332145 + }, + "Help": { + "score": 0.00351834856 + }, + "None": { + "score": 0.01229356 + }, + "Roles": { + "score": 0.08465987 + }, + "search": { + "score": 0.009909824 + }, + "SpecifyName": { + "score": 0.006426142 + }, + "Travel": { + "score": 0.008369388 + }, + "Weather_GetForecast": { + "score": 0.0112502193 + } + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "text": "deliver 37 wa to 82 co", + "v3": { + "options": { + "externalEntities": [ + { + "entityLength": 2, + "entityName": "State", + "startIndex": 11 + }, + { + "entityLength": 2, + "entityName": "State", + "resolution": { + "state": "Colorado" + }, + "startIndex": 20 + } + ], + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "$instance": { + "Address": [ + { + "length": 5, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "model" + ], + "score": 0.7033113, + "startIndex": 8, + "text": "37 wa", + "type": "Address" + } + ], + "number": [ + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "82", + "type": "builtin.number" + } + ], + "State": [ + { + "length": 2, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 20, + "text": "co", + "type": "State" + } + ] + }, + "Address": [ + { + "$instance": { + "number": [ + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 8, + "text": "37", + "type": "builtin.number" + } + ], + "State": [ + { + "length": 2, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "model", + "externalEntities" + ], + "score": 0.5987082, + "startIndex": 11, + "text": "wa", + "type": "State" + } + ] + }, + "number": [ + 37 + ], + "State": [ + "wa" + ] + } + ], + "number": [ + 82 + ], + "State": [ + { + "state": "Colorado" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.004045653 + }, + "Delivery": { + "score": 0.511144161 + }, + "EntityTests": { + "score": 0.004197402 + }, + "Greeting": { + "score": 0.00286332145 + }, + "Help": { + "score": 0.00351834856 + }, + "None": { + "score": 0.01229356 + }, + "Roles": { + "score": 0.08465987 + }, + "search": { + "score": 0.009909824 + }, + "SpecifyName": { + "score": 0.006426142 + }, + "Travel": { + "score": 0.008369388 + }, + "Weather.GetForecast": { + "score": 0.0112502193 + } + }, + "normalizedQuery": "deliver 37 wa to 82 co", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "Delivery" + }, + "query": "deliver 37 wa to 82 co" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndSimpleOverride.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndSimpleOverride.json new file mode 100644 index 0000000000..8f48817dd7 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/ExternalEntitiesAndSimpleOverride.json @@ -0,0 +1,299 @@ +{ + "entities": { + "$instance": { + "Address": [ + { + "endIndex": 13, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.7033113, + "startIndex": 8, + "text": "37 wa", + "type": "Address" + } + ], + "number": [ + { + "endIndex": 19, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "82", + "type": "builtin.number" + } + ], + "State": [ + { + "endIndex": 22, + "modelType": "Entity Extractor", + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 20, + "text": "co", + "type": "State" + } + ] + }, + "Address": [ + { + "$instance": { + "number": [ + { + "endIndex": 10, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 8, + "text": "37", + "type": "builtin.number" + } + ], + "State": [ + { + "endIndex": 13, + "modelType": "Entity Extractor", + "recognitionSources": [ + "model", + "externalEntities" + ], + "score": 0.5987082, + "startIndex": 11, + "text": "wa", + "type": "State" + } + ] + }, + "number": [ + 37 + ], + "State": [ + { + "state": "Washington" + } + ] + } + ], + "number": [ + 82 + ], + "State": [ + { + "state": "Colorado" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.004045653 + }, + "Delivery": { + "score": 0.511144161 + }, + "EntityTests": { + "score": 0.004197402 + }, + "Greeting": { + "score": 0.00286332145 + }, + "Help": { + "score": 0.00351834856 + }, + "None": { + "score": 0.01229356 + }, + "Roles": { + "score": 0.08465987 + }, + "search": { + "score": 0.009909824 + }, + "SpecifyName": { + "score": 0.006426142 + }, + "Travel": { + "score": 0.008369388 + }, + "Weather_GetForecast": { + "score": 0.0112502193 + } + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "text": "deliver 37 wa to 82 co", + "v3": { + "options": { + "externalEntities": [ + { + "entityLength": 2, + "entityName": "State", + "resolution": { + "state": "Washington" + }, + "startIndex": 11 + }, + { + "entityLength": 2, + "entityName": "State", + "resolution": { + "state": "Colorado" + }, + "startIndex": 20 + } + ], + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "$instance": { + "Address": [ + { + "length": 5, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "model" + ], + "score": 0.7033113, + "startIndex": 8, + "text": "37 wa", + "type": "Address" + } + ], + "number": [ + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "82", + "type": "builtin.number" + } + ], + "State": [ + { + "length": 2, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "externalEntities" + ], + "startIndex": 20, + "text": "co", + "type": "State" + } + ] + }, + "Address": [ + { + "$instance": { + "number": [ + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 8, + "text": "37", + "type": "builtin.number" + } + ], + "State": [ + { + "length": 2, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "model", + "externalEntities" + ], + "score": 0.5987082, + "startIndex": 11, + "text": "wa", + "type": "State" + } + ] + }, + "number": [ + 37 + ], + "State": [ + { + "state": "Washington" + } + ] + } + ], + "number": [ + 82 + ], + "State": [ + { + "state": "Colorado" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.004045653 + }, + "Delivery": { + "score": 0.511144161 + }, + "EntityTests": { + "score": 0.004197402 + }, + "Greeting": { + "score": 0.00286332145 + }, + "Help": { + "score": 0.00351834856 + }, + "None": { + "score": 0.01229356 + }, + "Roles": { + "score": 0.08465987 + }, + "search": { + "score": 0.009909824 + }, + "SpecifyName": { + "score": 0.006426142 + }, + "Travel": { + "score": 0.008369388 + }, + "Weather.GetForecast": { + "score": 0.0112502193 + } + }, + "normalizedQuery": "deliver 37 wa to 82 co", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "Delivery" + }, + "query": "deliver 37 wa to 82 co" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/GeoPeopleOrdinal.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/GeoPeopleOrdinal.json new file mode 100644 index 0000000000..4ac3ed4ff5 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/GeoPeopleOrdinal.json @@ -0,0 +1,321 @@ +{ + "entities": { + "$instance": { + "child": [ + { + "endIndex": 99, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 87, + "text": "lisa simpson", + "type": "builtin.personName" + } + ], + "endloc": [ + { + "endIndex": 51, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 44, + "text": "jakarta", + "type": "builtin.geographyV2.city" + } + ], + "ordinalV2": [ + { + "endIndex": 28, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 24, + "text": "last", + "type": "builtin.ordinalV2.relative" + } + ], + "parent": [ + { + "endIndex": 69, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 56, + "text": "homer simpson", + "type": "builtin.personName" + } + ], + "startloc": [ + { + "endIndex": 40, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 34, + "text": "london", + "type": "builtin.geographyV2.city" + } + ], + "startpos": [ + { + "endIndex": 20, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 8, + "text": "next to last", + "type": "builtin.ordinalV2.relative" + } + ] + }, + "child": [ + "lisa simpson" + ], + "endloc": [ + { + "location": "jakarta", + "type": "city" + } + ], + "ordinalV2": [ + { + "offset": 0, + "relativeTo": "end" + } + ], + "parent": [ + "homer simpson" + ], + "startloc": [ + { + "location": "london", + "type": "city" + } + ], + "startpos": [ + { + "offset": -1, + "relativeTo": "end" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.000107549029 + }, + "Delivery": { + "score": 0.00123035291 + }, + "EntityTests": { + "score": 0.0009487789 + }, + "Greeting": { + "score": 5.293933E-05 + }, + "Help": { + "score": 0.0001358991 + }, + "None": { + "score": 0.0109820236 + }, + "Roles": { + "score": 0.999204934 + }, + "search": { + "score": 0.0263254233 + }, + "SpecifyName": { + "score": 0.00104324089 + }, + "Travel": { + "score": 0.01043327 + }, + "Weather_GetForecast": { + "score": 0.0106523167 + } + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "text": "go from next to last to last move london to jakarta and homer simpson is the parent of lisa simpson", + "v3": { + "options": { + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "$instance": { + "child": [ + { + "length": 12, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "child", + "startIndex": 87, + "text": "lisa simpson", + "type": "builtin.personName" + } + ], + "endloc": [ + { + "length": 7, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "endloc", + "startIndex": 44, + "text": "jakarta", + "type": "builtin.geographyV2.city" + } + ], + "ordinalV2": [ + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 24, + "text": "last", + "type": "builtin.ordinalV2.relative" + } + ], + "parent": [ + { + "length": 13, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "parent", + "startIndex": 56, + "text": "homer simpson", + "type": "builtin.personName" + } + ], + "startloc": [ + { + "length": 6, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "startloc", + "startIndex": 34, + "text": "london", + "type": "builtin.geographyV2.city" + } + ], + "startpos": [ + { + "length": 12, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "startpos", + "startIndex": 8, + "text": "next to last", + "type": "builtin.ordinalV2.relative" + } + ] + }, + "child": [ + "lisa simpson" + ], + "endloc": [ + { + "value": "jakarta", + "type": "city" + } + ], + "ordinalV2": [ + { + "offset": 0, + "relativeTo": "end" + } + ], + "parent": [ + "homer simpson" + ], + "startloc": [ + { + "value": "london", + "type": "city" + } + ], + "startpos": [ + { + "offset": -1, + "relativeTo": "end" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.000107549029 + }, + "Delivery": { + "score": 0.00123035291 + }, + "EntityTests": { + "score": 0.0009487789 + }, + "Greeting": { + "score": 5.293933E-05 + }, + "Help": { + "score": 0.0001358991 + }, + "None": { + "score": 0.0109820236 + }, + "Roles": { + "score": 0.999204934 + }, + "search": { + "score": 0.0263254233 + }, + "SpecifyName": { + "score": 0.00104324089 + }, + "Travel": { + "score": 0.01043327 + }, + "Weather.GetForecast": { + "score": 0.0106523167 + } + }, + "normalizedQuery": "go from next to last to last move london to jakarta and homer simpson is the parent of lisa simpson", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "Roles" + }, + "query": "go from next to last to last move london to jakarta and homer simpson is the parent of lisa simpson" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Minimal.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Minimal.json new file mode 100644 index 0000000000..b810446ad4 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Minimal.json @@ -0,0 +1,83 @@ +{ + "entities": { + "Airline": [ + [ + "Delta" + ] + ], + "datetime": [ + { + "timex": [ + "T15" + ], + "type": "time" + } + ], + "dimension": [ + { + "number": 3, + "units": "Picometer" + } + ] + }, + "intents": { + "Roles": { + "score": 0.446264923 + } + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "text": "fly on delta at 3pm", + "v3": { + "options": { + "includeAllIntents": false, + "includeAPIResults": true, + "includeInstanceData": false, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "Airline": [ + [ + "Delta" + ] + ], + "datetimeV2": [ + { + "type": "time", + "values": [ + { + "timex": "T15", + "value": "15:00:00" + } + ] + } + ], + "dimension": [ + { + "number": 3, + "unit": "Picometer" + } + ] + }, + "intents": { + "Roles": { + "score": 0.446264923 + } + }, + "normalizedQuery": "fly on delta at 3pm", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "Roles" + }, + "query": "fly on delta at 3pm" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/NoEntitiesInstanceTrue.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/NoEntitiesInstanceTrue.json new file mode 100644 index 0000000000..0cbf851573 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/NoEntitiesInstanceTrue.json @@ -0,0 +1,33 @@ +{ + "entities": { + "$instance": {} + }, + "intents": { + "Greeting": { + "score": 0.9589885 + } + }, + "text": "Hi", + "v3": { + "options": { + "includeAllIntents": false, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "query": "Hi", + "prediction": { + "topIntent": "Greeting", + "intents": { + "Greeting": { + "score": 0.9589885 + } + }, + "entities": {} + } + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Patterns.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Patterns.json new file mode 100644 index 0000000000..824bf5f549 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Patterns.json @@ -0,0 +1,262 @@ +{ + "entities": { + "$instance": { + "extra": [ + { + "endIndex": 76, + "modelType": "Pattern.Any Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 71, + "text": "kb435", + "type": "subject" + } + ], + "Part": [ + { + "endIndex": 76, + "modelType": "Regex Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 71, + "text": "kb435", + "type": "Part" + } + ], + "person": [ + { + "endIndex": 61, + "modelType": "Pattern.Any Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 49, + "text": "bart simpson", + "type": "person" + } + ], + "personName": [ + { + "endIndex": 61, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 49, + "text": "bart simpson", + "type": "builtin.personName" + } + ], + "subject": [ + { + "endIndex": 43, + "modelType": "Pattern.Any Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 12, + "text": "something wicked this way comes", + "type": "subject" + } + ] + }, + "extra": [ + "kb435" + ], + "Part": [ + "kb435" + ], + "person": [ + "bart simpson" + ], + "personName": [ + "bart simpson" + ], + "subject": [ + "something wicked this way comes" + ] + }, + "intents": { + "Cancel": { + "score": 1.01764708E-09 + }, + "Delivery": { + "score": 1.8E-09 + }, + "EntityTests": { + "score": 1.044335E-05 + }, + "Greeting": { + "score": 1.0875E-09 + }, + "Help": { + "score": 1.01764708E-09 + }, + "None": { + "score": 2.38094663E-06 + }, + "Roles": { + "score": 5.98274755E-06 + }, + "search": { + "score": 0.9999993 + }, + "SpecifyName": { + "score": 3.0666667E-09 + }, + "Travel": { + "score": 3.09763345E-06 + }, + "Weather_GetForecast": { + "score": 1.02792524E-06 + } + }, + "sentiment": { + "label": "negative", + "score": 0.210341513 + }, + "text": "email about something wicked this way comes from bart simpson and also kb435", + "v3": { + "options": { + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "$instance": { + "extra": [ + { + "length": 5, + "modelType": "Pattern.Any Entity Extractor", + "modelTypeId": 7, + "recognitionSources": [ + "model" + ], + "role": "extra", + "startIndex": 71, + "text": "kb435", + "type": "subject" + } + ], + "Part": [ + { + "length": 5, + "modelType": "Regex Entity Extractor", + "modelTypeId": 8, + "recognitionSources": [ + "model" + ], + "startIndex": 71, + "text": "kb435", + "type": "Part" + } + ], + "person": [ + { + "length": 12, + "modelType": "Pattern.Any Entity Extractor", + "modelTypeId": 7, + "recognitionSources": [ + "model" + ], + "startIndex": 49, + "text": "bart simpson", + "type": "person" + } + ], + "personName": [ + { + "length": 12, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 49, + "text": "bart simpson", + "type": "builtin.personName" + } + ], + "subject": [ + { + "length": 31, + "modelType": "Pattern.Any Entity Extractor", + "modelTypeId": 7, + "recognitionSources": [ + "model" + ], + "startIndex": 12, + "text": "something wicked this way comes", + "type": "subject" + } + ] + }, + "extra": [ + "kb435" + ], + "Part": [ + "kb435" + ], + "person": [ + "bart simpson" + ], + "personName": [ + "bart simpson" + ], + "subject": [ + "something wicked this way comes" + ] + }, + "intents": { + "Cancel": { + "score": 1.01764708E-09 + }, + "Delivery": { + "score": 1.8E-09 + }, + "EntityTests": { + "score": 1.044335E-05 + }, + "Greeting": { + "score": 1.0875E-09 + }, + "Help": { + "score": 1.01764708E-09 + }, + "None": { + "score": 2.38094663E-06 + }, + "Roles": { + "score": 5.98274755E-06 + }, + "search": { + "score": 0.9999993 + }, + "SpecifyName": { + "score": 3.0666667E-09 + }, + "Travel": { + "score": 3.09763345E-06 + }, + "Weather.GetForecast": { + "score": 1.02792524E-06 + } + }, + "normalizedQuery": "email about something wicked this way comes from bart simpson and also kb435", + "sentiment": { + "label": "negative", + "score": 0.210341513 + }, + "topIntent": "search" + }, + "query": "email about something wicked this way comes from bart simpson and also kb435" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Prebuilt.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Prebuilt.json new file mode 100644 index 0000000000..9cb4ab1344 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/Prebuilt.json @@ -0,0 +1,246 @@ +{ + "entities": { + "$instance": { + "Composite2": [ + { + "endIndex": 66, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.7077416, + "startIndex": 0, + "text": "http://foo.com is where you can get a weather forecast for seattle", + "type": "Composite2" + } + ], + "geographyV2": [ + { + "endIndex": 66, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 59, + "text": "seattle", + "type": "builtin.geographyV2.city" + } + ] + }, + "Composite2": [ + { + "$instance": { + "url": [ + { + "endIndex": 14, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "http://foo.com", + "type": "builtin.url" + } + ], + "Weather_Location": [ + { + "endIndex": 66, + "modelType": "Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.76184386, + "startIndex": 59, + "text": "seattle", + "type": "Weather.Location" + } + ] + }, + "url": [ + "http://foo.com" + ], + "Weather_Location": [ + "seattle" + ] + } + ], + "geographyV2": [ + { + "location": "seattle", + "type": "city" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.000171828113 + }, + "Delivery": { + "score": 0.0011408634 + }, + "EntityTests": { + "score": 0.342939854 + }, + "Greeting": { + "score": 0.0001518702 + }, + "Help": { + "score": 0.0005502715 + }, + "None": { + "score": 0.0175834317 + }, + "Roles": { + "score": 0.0432791822 + }, + "search": { + "score": 0.01050759 + }, + "SpecifyName": { + "score": 0.001833231 + }, + "Travel": { + "score": 0.004430798 + }, + "Weather_GetForecast": { + "score": 0.669524968 + } + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "text": "http://foo.com is where you can get a weather forecast for seattle", + "v3": { + "options": { + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + }, + "response": { + "prediction": { + "entities": { + "$instance": { + "Composite2": [ + { + "length": 66, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "model" + ], + "score": 0.7077416, + "startIndex": 0, + "text": "http://foo.com is where you can get a weather forecast for seattle", + "type": "Composite2" + } + ], + "geographyV2": [ + { + "length": 7, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 59, + "text": "seattle", + "type": "builtin.geographyV2.city" + } + ] + }, + "Composite2": [ + { + "$instance": { + "url": [ + { + "length": 14, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "http://foo.com", + "type": "builtin.url" + } + ], + "Weather.Location": [ + { + "length": 7, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "model" + ], + "score": 0.76184386, + "startIndex": 59, + "text": "seattle", + "type": "Weather.Location" + } + ] + }, + "url": [ + "http://foo.com" + ], + "Weather.Location": [ + "seattle" + ] + } + ], + "geographyV2": [ + { + "type": "city", + "value": "seattle" + } + ] + }, + "intents": { + "Cancel": { + "score": 0.000171828113 + }, + "Delivery": { + "score": 0.0011408634 + }, + "EntityTests": { + "score": 0.342939854 + }, + "Greeting": { + "score": 0.0001518702 + }, + "Help": { + "score": 0.0005502715 + }, + "None": { + "score": 0.0175834317 + }, + "Roles": { + "score": 0.0432791822 + }, + "search": { + "score": 0.01050759 + }, + "SpecifyName": { + "score": 0.001833231 + }, + "Travel": { + "score": 0.004430798 + }, + "Weather.GetForecast": { + "score": 0.669524968 + } + }, + "normalizedQuery": "http://foo.com is where you can get a weather forecast for seattle", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "Weather.GetForecast" + }, + "query": "http://foo.com is where you can get a weather forecast for seattle" + } + } + } \ No newline at end of file diff --git a/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/roles.json b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/roles.json new file mode 100644 index 0000000000..60aca7d2a3 --- /dev/null +++ b/libraries/botbuilder-ai/tests/TestData/LuisRecognizerV3/roles.json @@ -0,0 +1,1759 @@ +{ + "text": "3 inches long by 2 inches wide and 5% to 10% and are you between 6 years old and 8 years old and can i trade kb457 for kb922 and change 425-777-1212 to 206-666-4123 and did delta buy virgin and did the rain from hawaii get to redmond and http://foo.com changed to http://blah.com and i like between 68 degrees and 72 degrees and john likes mary and leave 3pm and arrive 5pm and pay between $400 and $500 and send chrimc@hotmail.com from emad@gmail.com", + "intents": { + "Cancel": { + "score": 4.50860341e-7 + }, + "Delivery": { + "score": 0.00007978094 + }, + "EntityTests": { + "score": 0.0046325135 + }, + "Greeting": { + "score": 4.73494453e-7 + }, + "Help": { + "score": 7.622754e-7 + }, + "None": { + "score": 0.00093744183 + }, + "Roles": { + "score": 1 + }, + "search": { + "score": 0.07635335 + }, + "SpecifyName": { + "score": 0.00009136085 + }, + "Travel": { + "score": 0.00771805458 + }, + "Weather_GetForecast": { + "score": 0.0100867962 + } + }, + "entities": { + "$instance": { + "a": [ + { + "endIndex": 309, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 299, + "text": "68 degrees", + "type": "builtin.temperature" + } + ], + "arrive": [ + { + "endIndex": 373, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 370, + "text": "5pm", + "type": "builtin.datetimeV2.time" + } + ], + "b": [ + { + "endIndex": 324, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 314, + "text": "72 degrees", + "type": "builtin.temperature" + } + ], + "begin": [ + { + "endIndex": 76, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 65, + "text": "6 years old", + "type": "builtin.age" + } + ], + "buy": [ + { + "endIndex": 124, + "modelType": "Regex Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 119, + "text": "kb922", + "type": "Part" + } + ], + "Buyer": [ + { + "endIndex": 178, + "modelType": "List Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 173, + "text": "delta", + "type": "Airline" + } + ], + "Composite1": [ + { + "endIndex": 172, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.01107535, + "startIndex": 0, + "text": "3 inches long by 2 inches wide and 5% to 10% and are you between 6 years old and 8 years old and can i trade kb457 for kb922 and change 425-777-1212 to 206-666-4123 and did", + "type": "Composite1" + } + ], + "Composite2": [ + { + "endIndex": 283, + "modelType": "Composite Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.15191336, + "startIndex": 238, + "text": "http://foo.com changed to http://blah.com and", + "type": "Composite2" + } + ], + "destination": [ + { + "endIndex": 233, + "modelType": "Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.985884964, + "startIndex": 226, + "text": "redmond", + "type": "Weather.Location" + } + ], + "dimension": [ + { + "endIndex": 358, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 355, + "text": "3pm", + "type": "builtin.dimension" + }, + { + "endIndex": 373, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 370, + "text": "5pm", + "type": "builtin.dimension" + } + ], + "end": [ + { + "endIndex": 92, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 81, + "text": "8 years old", + "type": "builtin.age" + } + ], + "geographyV2": [ + { + "endIndex": 218, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 212, + "text": "hawaii", + "type": "builtin.geographyV2.state" + }, + { + "endIndex": 233, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 226, + "text": "redmond", + "type": "builtin.geographyV2.city" + } + ], + "leave": [ + { + "endIndex": 358, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 355, + "text": "3pm", + "type": "builtin.datetimeV2.time" + } + ], + "length": [ + { + "endIndex": 8, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "3 inches", + "type": "builtin.dimension" + } + ], + "likee": [ + { + "endIndex": 344, + "modelType": "Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.9900547, + "startIndex": 340, + "text": "mary", + "type": "Name" + } + ], + "liker": [ + { + "endIndex": 333, + "modelType": "Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.992201567, + "startIndex": 329, + "text": "john", + "type": "Name" + } + ], + "max": [ + { + "endIndex": 403, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 399, + "text": "$500", + "type": "builtin.currency" + } + ], + "maximum": [ + { + "endIndex": 44, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 41, + "text": "10%", + "type": "builtin.percentage" + } + ], + "min": [ + { + "endIndex": 394, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 390, + "text": "$400", + "type": "builtin.currency" + } + ], + "minimum": [ + { + "endIndex": 37, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 35, + "text": "5%", + "type": "builtin.percentage" + } + ], + "newPhone": [ + { + "endIndex": 164, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.9, + "startIndex": 152, + "text": "206-666-4123", + "type": "builtin.phonenumber" + } + ], + "number": [ + { + "endIndex": 301, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 299, + "text": "68", + "type": "builtin.number" + }, + { + "endIndex": 316, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 314, + "text": "72", + "type": "builtin.number" + }, + { + "endIndex": 394, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 391, + "text": "400", + "type": "builtin.number" + }, + { + "endIndex": 403, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 400, + "text": "500", + "type": "builtin.number" + } + ], + "old": [ + { + "endIndex": 148, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.9, + "startIndex": 136, + "text": "425-777-1212", + "type": "builtin.phonenumber" + } + ], + "oldURL": [ + { + "endIndex": 252, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 238, + "text": "http://foo.com", + "type": "builtin.url" + } + ], + "personName": [ + { + "endIndex": 333, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 329, + "text": "john", + "type": "builtin.personName" + }, + { + "endIndex": 344, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 340, + "text": "mary", + "type": "builtin.personName" + } + ], + "receiver": [ + { + "endIndex": 431, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 413, + "text": "chrimc@hotmail.com", + "type": "builtin.email" + } + ], + "sell": [ + { + "endIndex": 114, + "modelType": "Regex Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 109, + "text": "kb457", + "type": "Part" + } + ], + "Seller": [ + { + "endIndex": 189, + "modelType": "List Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 183, + "text": "virgin", + "type": "Airline" + } + ], + "sender": [ + { + "endIndex": 451, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 437, + "text": "emad@gmail.com", + "type": "builtin.email" + } + ], + "source": [ + { + "endIndex": 218, + "modelType": "Entity Extractor", + "recognitionSources": [ + "model" + ], + "score": 0.9713092, + "startIndex": 212, + "text": "hawaii", + "type": "Weather.Location" + } + ], + "width": [ + { + "endIndex": 25, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "2 inches", + "type": "builtin.dimension" + } + ] + }, + "a": [ + { + "number": 68, + "units": "Degree" + } + ], + "arrive": [ + { + "timex": [ + "T17" + ], + "type": "time" + } + ], + "b": [ + { + "number": 72, + "units": "Degree" + } + ], + "begin": [ + { + "number": 6, + "units": "Year" + } + ], + "buy": [ + "kb922" + ], + "Buyer": [ + [ + "Delta" + ] + ], + "Composite1": [ + { + "$instance": { + "datetime": [ + { + "endIndex": 72, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 65, + "text": "6 years", + "type": "builtin.datetimeV2.duration" + }, + { + "endIndex": 88, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 81, + "text": "8 years", + "type": "builtin.datetimeV2.duration" + } + ], + "number": [ + { + "endIndex": 1, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "3", + "type": "builtin.number" + }, + { + "endIndex": 18, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "2", + "type": "builtin.number" + }, + { + "endIndex": 36, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 35, + "text": "5", + "type": "builtin.number" + }, + { + "endIndex": 43, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 41, + "text": "10", + "type": "builtin.number" + }, + { + "endIndex": 66, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 65, + "text": "6", + "type": "builtin.number" + }, + { + "endIndex": 82, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 81, + "text": "8", + "type": "builtin.number" + }, + { + "endIndex": 139, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 136, + "text": "425", + "type": "builtin.number" + }, + { + "endIndex": 143, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 140, + "text": "777", + "type": "builtin.number" + }, + { + "endIndex": 148, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 144, + "text": "1212", + "type": "builtin.number" + }, + { + "endIndex": 155, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 152, + "text": "206", + "type": "builtin.number" + }, + { + "endIndex": 159, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 156, + "text": "666", + "type": "builtin.number" + }, + { + "endIndex": 164, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 160, + "text": "4123", + "type": "builtin.number" + } + ] + }, + "datetime": [ + { + "timex": [ + "P6Y" + ], + "type": "duration" + }, + { + "timex": [ + "P8Y" + ], + "type": "duration" + } + ], + "number": [ + 3, + 2, + 5, + 10, + 6, + 8, + 425, + 777, + 1212, + 206, + 666, + 4123 + ] + } + ], + "Composite2": [ + { + "$instance": { + "url": [ + { + "endIndex": 279, + "modelType": "Prebuilt Entity Extractor", + "recognitionSources": [ + "model" + ], + "startIndex": 264, + "text": "http://blah.com", + "type": "builtin.url" + } + ] + }, + "url": [ + "http://blah.com" + ] + } + ], + "destination": [ + "redmond" + ], + "dimension": [ + { + "number": 3, + "units": "Picometer" + }, + { + "number": 5, + "units": "Picometer" + } + ], + "end": [ + { + "number": 8, + "units": "Year" + } + ], + "geographyV2": [ + { + "location": "hawaii", + "type": "state" + }, + { + "location": "redmond", + "type": "city" + } + ], + "leave": [ + { + "timex": [ + "T15" + ], + "type": "time" + } + ], + "length": [ + { + "number": 3, + "units": "Inch" + } + ], + "likee": [ + "mary" + ], + "liker": [ + "john" + ], + "max": [ + { + "number": 500, + "units": "Dollar" + } + ], + "maximum": [ + 10 + ], + "min": [ + { + "number": 400, + "units": "Dollar" + } + ], + "minimum": [ + 5 + ], + "newPhone": [ + "206-666-4123" + ], + "number": [ + 68, + 72, + 400, + 500 + ], + "old": [ + "425-777-1212" + ], + "oldURL": [ + "http://foo.com" + ], + "personName": [ + "john", + "mary" + ], + "receiver": [ + "chrimc@hotmail.com" + ], + "sell": [ + "kb457" + ], + "Seller": [ + [ + "Virgin" + ] + ], + "sender": [ + "emad@gmail.com" + ], + "source": [ + "hawaii" + ], + "width": [ + { + "number": 2, + "units": "Inch" + } + ] + }, + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "v3": { + "response": { + "prediction": { + "entities": { + "$instance": { + "a": [ + { + "length": 10, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "a", + "startIndex": 299, + "text": "68 degrees", + "type": "builtin.temperature" + } + ], + "arrive": [ + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "arrive", + "startIndex": 370, + "text": "5pm", + "type": "builtin.datetimeV2.time" + } + ], + "b": [ + { + "length": 10, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "b", + "startIndex": 314, + "text": "72 degrees", + "type": "builtin.temperature" + } + ], + "begin": [ + { + "length": 11, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "begin", + "startIndex": 65, + "text": "6 years old", + "type": "builtin.age" + } + ], + "buy": [ + { + "length": 5, + "modelType": "Regex Entity Extractor", + "modelTypeId": 8, + "recognitionSources": [ + "model" + ], + "role": "buy", + "startIndex": 119, + "text": "kb922", + "type": "Part" + } + ], + "Buyer": [ + { + "length": 5, + "modelType": "List Entity Extractor", + "modelTypeId": 5, + "recognitionSources": [ + "model" + ], + "role": "Buyer", + "startIndex": 173, + "text": "delta", + "type": "Airline" + } + ], + "Composite1": [ + { + "length": 172, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "model" + ], + "score": 0.01107535, + "startIndex": 0, + "text": "3 inches long by 2 inches wide and 5% to 10% and are you between 6 years old and 8 years old and can i trade kb457 for kb922 and change 425-777-1212 to 206-666-4123 and did", + "type": "Composite1" + } + ], + "Composite2": [ + { + "length": 45, + "modelType": "Composite Entity Extractor", + "modelTypeId": 4, + "recognitionSources": [ + "model" + ], + "score": 0.15191336, + "startIndex": 238, + "text": "http://foo.com changed to http://blah.com and", + "type": "Composite2" + } + ], + "destination": [ + { + "length": 7, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "model" + ], + "role": "destination", + "score": 0.985884964, + "startIndex": 226, + "text": "redmond", + "type": "Weather.Location" + } + ], + "dimension": [ + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 355, + "text": "3pm", + "type": "builtin.dimension" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 370, + "text": "5pm", + "type": "builtin.dimension" + } + ], + "end": [ + { + "length": 11, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "end", + "startIndex": 81, + "text": "8 years old", + "type": "builtin.age" + } + ], + "geographyV2": [ + { + "length": 6, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 212, + "text": "hawaii", + "type": "builtin.geographyV2.state" + }, + { + "length": 7, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 226, + "text": "redmond", + "type": "builtin.geographyV2.city" + } + ], + "leave": [ + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "leave", + "startIndex": 355, + "text": "3pm", + "type": "builtin.datetimeV2.time" + } + ], + "length": [ + { + "length": 8, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "length", + "startIndex": 0, + "text": "3 inches", + "type": "builtin.dimension" + } + ], + "likee": [ + { + "length": 4, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "model" + ], + "role": "likee", + "score": 0.9900547, + "startIndex": 340, + "text": "mary", + "type": "Name" + } + ], + "liker": [ + { + "length": 4, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "model" + ], + "role": "liker", + "score": 0.992201567, + "startIndex": 329, + "text": "john", + "type": "Name" + } + ], + "max": [ + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "max", + "startIndex": 399, + "text": "$500", + "type": "builtin.currency" + } + ], + "maximum": [ + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "maximum", + "startIndex": 41, + "text": "10%", + "type": "builtin.percentage" + } + ], + "min": [ + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "min", + "startIndex": 390, + "text": "$400", + "type": "builtin.currency" + } + ], + "minimum": [ + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "minimum", + "startIndex": 35, + "text": "5%", + "type": "builtin.percentage" + } + ], + "newPhone": [ + { + "length": 12, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "newPhone", + "score": 0.9, + "startIndex": 152, + "text": "206-666-4123", + "type": "builtin.phonenumber" + } + ], + "number": [ + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 299, + "text": "68", + "type": "builtin.number" + }, + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 314, + "text": "72", + "type": "builtin.number" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 391, + "text": "400", + "type": "builtin.number" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 400, + "text": "500", + "type": "builtin.number" + } + ], + "old": [ + { + "length": 12, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "old", + "score": 0.9, + "startIndex": 136, + "text": "425-777-1212", + "type": "builtin.phonenumber" + } + ], + "oldURL": [ + { + "length": 14, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "oldURL", + "startIndex": 238, + "text": "http://foo.com", + "type": "builtin.url" + } + ], + "personName": [ + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 329, + "text": "john", + "type": "builtin.personName" + }, + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 340, + "text": "mary", + "type": "builtin.personName" + } + ], + "receiver": [ + { + "length": 18, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "receiver", + "startIndex": 413, + "text": "chrimc@hotmail.com", + "type": "builtin.email" + } + ], + "sell": [ + { + "length": 5, + "modelType": "Regex Entity Extractor", + "modelTypeId": 8, + "recognitionSources": [ + "model" + ], + "role": "sell", + "startIndex": 109, + "text": "kb457", + "type": "Part" + } + ], + "Seller": [ + { + "length": 6, + "modelType": "List Entity Extractor", + "modelTypeId": 5, + "recognitionSources": [ + "model" + ], + "role": "Seller", + "startIndex": 183, + "text": "virgin", + "type": "Airline" + } + ], + "sender": [ + { + "length": 14, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "sender", + "startIndex": 437, + "text": "emad@gmail.com", + "type": "builtin.email" + } + ], + "source": [ + { + "length": 6, + "modelType": "Entity Extractor", + "modelTypeId": 1, + "recognitionSources": [ + "model" + ], + "role": "source", + "score": 0.9713092, + "startIndex": 212, + "text": "hawaii", + "type": "Weather.Location" + } + ], + "width": [ + { + "length": 8, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "role": "width", + "startIndex": 17, + "text": "2 inches", + "type": "builtin.dimension" + } + ] + }, + "a": [ + { + "number": 68, + "unit": "Degree" + } + ], + "arrive": [ + { + "type": "time", + "values": [ + { + "timex": "T17", + "value": "17:00:00" + } + ] + } + ], + "b": [ + { + "number": 72, + "unit": "Degree" + } + ], + "begin": [ + { + "number": 6, + "unit": "Year" + } + ], + "buy": [ + "kb922" + ], + "Buyer": [ + [ + "Delta" + ] + ], + "Composite1": [ + { + "$instance": { + "datetimeV2": [ + { + "length": 7, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 65, + "text": "6 years", + "type": "builtin.datetimeV2.duration" + }, + { + "length": 7, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 81, + "text": "8 years", + "type": "builtin.datetimeV2.duration" + } + ], + "number": [ + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 0, + "text": "3", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 17, + "text": "2", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 35, + "text": "5", + "type": "builtin.number" + }, + { + "length": 2, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 41, + "text": "10", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 65, + "text": "6", + "type": "builtin.number" + }, + { + "length": 1, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 81, + "text": "8", + "type": "builtin.number" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 136, + "text": "425", + "type": "builtin.number" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 140, + "text": "777", + "type": "builtin.number" + }, + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 144, + "text": "1212", + "type": "builtin.number" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 152, + "text": "206", + "type": "builtin.number" + }, + { + "length": 3, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 156, + "text": "666", + "type": "builtin.number" + }, + { + "length": 4, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 160, + "text": "4123", + "type": "builtin.number" + } + ] + }, + "datetimeV2": [ + { + "type": "duration", + "values": [ + { + "timex": "P6Y", + "value": "189216000" + } + ] + }, + { + "type": "duration", + "values": [ + { + "timex": "P8Y", + "value": "252288000" + } + ] + } + ], + "number": [ + 3, + 2, + 5, + 10, + 6, + 8, + 425, + 777, + 1212, + 206, + 666, + 4123 + ] + } + ], + "Composite2": [ + { + "$instance": { + "url": [ + { + "length": 15, + "modelType": "Prebuilt Entity Extractor", + "modelTypeId": 2, + "recognitionSources": [ + "model" + ], + "startIndex": 264, + "text": "http://blah.com", + "type": "builtin.url" + } + ] + }, + "url": [ + "http://blah.com" + ] + } + ], + "destination": [ + "redmond" + ], + "dimension": [ + { + "number": 3, + "unit": "Picometer" + }, + { + "number": 5, + "unit": "Picometer" + } + ], + "end": [ + { + "number": 8, + "unit": "Year" + } + ], + "geographyV2": [ + { + "type": "state", + "value": "hawaii" + }, + { + "type": "city", + "value": "redmond" + } + ], + "leave": [ + { + "type": "time", + "values": [ + { + "timex": "T15", + "value": "15:00:00" + } + ] + } + ], + "length": [ + { + "number": 3, + "unit": "Inch" + } + ], + "likee": [ + "mary" + ], + "liker": [ + "john" + ], + "max": [ + { + "number": 500, + "unit": "Dollar" + } + ], + "maximum": [ + 10 + ], + "min": [ + { + "number": 400, + "unit": "Dollar" + } + ], + "minimum": [ + 5 + ], + "newPhone": [ + "206-666-4123" + ], + "number": [ + 68, + 72, + 400, + 500 + ], + "old": [ + "425-777-1212" + ], + "oldURL": [ + "http://foo.com" + ], + "personName": [ + "john", + "mary" + ], + "receiver": [ + "chrimc@hotmail.com" + ], + "sell": [ + "kb457" + ], + "Seller": [ + [ + "Virgin" + ] + ], + "sender": [ + "emad@gmail.com" + ], + "source": [ + "hawaii" + ], + "width": [ + { + "number": 2, + "unit": "Inch" + } + ] + }, + "intents": { + "Cancel": { + "score": 4.50860341e-7 + }, + "Delivery": { + "score": 0.00007978094 + }, + "EntityTests": { + "score": 0.0046325135 + }, + "Greeting": { + "score": 4.73494453e-7 + }, + "Help": { + "score": 7.622754e-7 + }, + "None": { + "score": 0.00093744183 + }, + "Roles": { + "score": 1 + }, + "search": { + "score": 0.07635335 + }, + "SpecifyName": { + "score": 0.00009136085 + }, + "Travel": { + "score": 0.00771805458 + }, + "Weather.GetForecast": { + "score": 0.0100867962 + } + }, + "normalizedQuery": "3 inches long by 2 inches wide and 5% to 10% and are you between 6 years old and 8 years old and can i trade kb457 for kb922 and change 425-777-1212 to 206-666-4123 and did delta buy virgin and did the rain from hawaii get to redmond and http://foo.com changed to http://blah.com and i like between 68 degrees and 72 degrees and john likes mary and leave 3pm and arrive 5pm and pay between $400 and $500 and send chrimc@hotmail.com from emad@gmail.com", + "sentiment": { + "label": "neutral", + "score": 0.5 + }, + "topIntent": "Roles" + }, + "query": "3 inches long by 2 inches wide and 5% to 10% and are you between 6 years old and 8 years old and can i trade kb457 for kb922 and change 425-777-1212 to 206-666-4123 and did delta buy virgin and did the rain from hawaii get to redmond and http://foo.com changed to http://blah.com and i like between 68 degrees and 72 degrees and john likes mary and leave 3pm and arrive 5pm and pay between $400 and $500 and send chrimc@hotmail.com from emad@gmail.com" + }, + "options": { + "includeAllIntents": true, + "includeAPIResults": true, + "includeInstanceData": true, + "log": true, + "preferExternalEntities": true, + "slot": "production" + } + } +} diff --git a/libraries/botbuilder-ai/tests/luisRecognizer.test.js b/libraries/botbuilder-ai/tests/luisRecognizer.test.js index b2bff41888..354be77cf8 100644 --- a/libraries/botbuilder-ai/tests/luisRecognizer.test.js +++ b/libraries/botbuilder-ai/tests/luisRecognizer.test.js @@ -308,7 +308,7 @@ describe('LuisRecognizer', function () { it('should cache multiple calls to recognize()', done => { var expected = GetExpected(ExpectedPath("SingleIntent_SimplyEntity.json")); - var recognizer = new LuisRecognizer({ applicationId: luisAppId, endpointKey: endpointKey }, { includeAllIntents: true }, true); + var recognizer = new LuisRecognizer({ applicationId: luisAppId, endpointKey: endpointKey }, { includeAllIntents: true, apiVersion: 'v2', includeAPIResults: true}); var context = new TestContext({ text: expected.text }); recognizer.recognize(context) .then(res => { @@ -891,6 +891,42 @@ describe('LuisRecognizer', function () { throttle(done); }); + it('should accept LuisRecognizerOptions passed into recognizer "recognize" method', done => { + const luisPredictionDefaultOptions = { + includeAllIntents: true, + includeInstanceData: true, + apiVersion: 'v3' + }; + const luisPredictionUserOptions = { + includeAllIntents: false, + includeInstanceData: false, + apiVersion: 'v3' + }; + const recognizer = new LuisRecognizer({ applicationId: luisAppId, endpointKey: endpointKey }, luisPredictionDefaultOptions, true); + const mergedOptions = recognizer.buildRecognizer(luisPredictionUserOptions) + assert(mergedOptions.predictionOptions.includeAllIntents === false); + assert(mergedOptions.predictionOptions.includeInstanceData === false); + throttle(done); + }); + + it('should accept LuisRecognizerOptions passed into recognizer "recognize" method. v3 to v2', done => { + const luisPredictionDefaultOptions = { + includeAllIntents: true, + includeInstanceData: true, + apiVersion: 'v3' + }; + const luisPredictionUserOptions = { + includeAllIntents: false, + includeInstanceData: false, + apiVersion: 'v2' + }; + const recognizer = new LuisRecognizer({ applicationId: luisAppId, endpointKey: endpointKey }, luisPredictionDefaultOptions, true); + const mergedOptions = recognizer.buildRecognizer(luisPredictionUserOptions) + assert(mergedOptions.options.includeAllIntents === false); + assert(mergedOptions.options.includeInstanceData === false); + throttle(done); + }); + it('should use default Luis prediction options if no user options passed in', done => { const luisPredictionDefaultOptions = { includeAllIntents: true, @@ -903,7 +939,6 @@ describe('LuisRecognizer', function () { assert(mergedOptions.includeInstanceData === true); throttle(done); }); - }); class telemetryOverrideRecognizer extends LuisRecognizer { diff --git a/libraries/botbuilder-ai/tests/luisV3OracleTests.test.js b/libraries/botbuilder-ai/tests/luisV3OracleTests.test.js new file mode 100644 index 0000000000..bd4501eff0 --- /dev/null +++ b/libraries/botbuilder-ai/tests/luisV3OracleTests.test.js @@ -0,0 +1,180 @@ +const { LuisRecognizer } = require('../'); +const { TestAdapter, TurnContext } = require('botbuilder-core'); +const luisAppId = '38330cad-f768-4619-96f9-69ea333e594b'; +const fs = require('fs-extra'); +const nock = require('nock'); +const assert = require('assert'); + +// This can be any endpoint key for calling LUIS +const endpointKey = process.env.LUISAPPKEY || "MockedKey"; + +// If this is true, then LUIS responses will come from oracle files. +// If it is false, the LUIS service will be called and if there are changes you will get a new oracle file. +const mockLuis = true; + +class TestContext extends TurnContext { + constructor(request) { + super(new TestAdapter(), request); + this.sent = undefined; + this.onSendActivities((context, activities, next) => { + this.sent = activities; + }); + } +} + +function TestJson(file, done, includeAllIntents, includeInstance, telemetryClient, telemetryProperties, telemetryMetrics, logPersonalInformation) { + if (includeAllIntents === undefined) includeAllIntents = true; + if (includeInstance === undefined) includeInstance = true; + if (logPersonalInformation === undefined) logPersonalInformation = true; + if (telemetryProperties === undefined) telemetryProperties = null; + + const version = 'v3'; + let expectedPath = ExpectedPath(file); + const oracle = GetExpected(expectedPath); + const oldResponse = oracle[version]; + const newPath = expectedPath + ".new"; + const query = oracle['text']; + const context = new TestContext({ text: query }); + const response = oracle[version]; + const oracleOptions = response['options']; + const options = oracleOptions ? oracleOptions : {apiVersion: 'v3'}; + const luisRecognizer = GetLuisRecognizer({ applicationId: luisAppId, endpointKey: endpointKey }, options, true) + + luisRecognizer.recognize(context, telemetryProperties, telemetryMetrics).then(res => { + res.v3 = { + response: res.luisResult + } + res.v3.options = options; + delete res.luisResult; + if (!WithinDelta(oracle, res, 0.1, false) && res.v3 !== oldResponse) { + fs.outputJSONSync(newPath, res, { spaces: 2 }); + assert(false, "\nReturned JSON\n " + newPath + "\n!= expected JSON\n " + expectedPath); + } + else if (fs.existsSync(newPath)) { + fs.unlinkSync(newPath); + } + done(res); + }); +} + + +function GetExpected(oracle) { + let expected = fs.readJSONSync(oracle); + let uri = `/luis/prediction/v3.0/apps/${luisAppId}` ; + + if (expected.v3.options.version) { + uri += `/versions/${expected.v3.options.version}/predict` + } else { + uri += `/slots/${expected.v3.options.slot}/predict` + } + + const params = `?verbose=${expected.v3.options.includeInstanceData}&log=${expected.v3.options.log}&show-all-intents=${expected.v3.options.includeAllIntents}`; + var responseBody = expected.v3.response; + + if (mockLuis) { + nock('https://westus.api.cognitive.microsoft.com') + .post(uri + params) + .reply(200, responseBody); + } + return expected; +} + +function ExpectedPath(file) { + return __dirname + "/TestData/LuisRecognizerV3/" + file; +} + +function GetLuisRecognizer(application, options, includeApiResults ) { + const optsV3 = { + apiVersion: 'v3', + includeAPIResults: includeApiResults, + ...options + } + + return new LuisRecognizer(application, optsV3); +} + +function throttle(callback) { + if (mockLuis) { + callback(); + } else { + // If actually calling LUIS, need to throttle our requests + setTimeout(callback, 1000); + } +} + +describe('LuisRecognizer V3', function () { + this.timeout(15000); + + if (!mockLuis && endpointKey === "MockedKey") { + console.warn('WARNING: skipping LuisRecognizer test suite because the LUISAPPKEY environment variable is not defined'); + return; + } + + it('Composite1', done => TestJson("Composite1.json", res => throttle(done))); + + it('Composite2', done => TestJson("Composite2.json", res => throttle(done))); + + it('Composite 3', done => TestJson("Composite3.json", res => throttle(done))); + + it('DynamicLists', done => TestJson("DynamicListsAndList.json", res => throttle(done))); + + it('ExternalEntitiesAndBuiltin', done => TestJson("ExternalEntitiesAndBuiltin.json", res => throttle(done))); + + it('ExternalEntitiesAndComposite', done => TestJson("ExternalEntitiesAndComposite.json", res => throttle(done))); + + it('ExternalEntitiesAndList', done => TestJson("ExternalEntitiesAndList.json", res => throttle(done))); + + it('ExternalEntitiesAndRegex', done => TestJson("ExternalEntitiesAndRegex.json", res => throttle(done))); + + it('ExternalEntitiesAndSimple', done => TestJson("ExternalEntitiesAndSimple.json", res => throttle(done))); + + it('ExternalEntitiesAndSimpleOverride', done => TestJson("ExternalEntitiesAndSimpleOverride.json", res => throttle(done))); + + it('GeoPeopleOrdinal', done => TestJson("GeoPeopleOrdinal.json", res => throttle(done))); + + it('Minimal', done => TestJson("Minimal.json", res => throttle(done))); + + it('Prebuilt', done => TestJson("Prebuilt.json", res => throttle(done))); + + it('Patterns', done => TestJson("Patterns.json", res => throttle(done))); + + it('roles', done => TestJson("roles.json", res => throttle(done))); + + it('NoEntitiesInstanceTrue', done => TestJson("NoEntitiesInstanceTrue.json", res => throttle(done))); + +}) + + +function WithinDelta(token1, token2, delta, compare) { + var within = true; + if (token1 == null || token2 == null) { + within = token1 == token2; + } + else if (Array.isArray(token1) && Array.isArray(token2)) { + within = token1.length == token2.length; + for (var i = 0; within && i < token1.length; ++i) { + within = WithinDelta(token1[i], token2[i], delta, compare); + } + } + else if (typeof token1 === "object" && typeof token2 === "object") { + Object.keys(token2).forEach(k => token2[k] === undefined && delete token2[k]); + within = Object.keys(token1).length === Object.keys(token2).length; + Object.keys(token1).forEach(function (key) { + if (!within) return; + within = WithinDelta(token1[key], token2[key], delta, compare || key === "score" || key === "intents"); + }); + } + else if (token1 !== token2) { + if (token1 !== undefined && token2 != undefined && token1.Type == token2.Type) { + within = false; + if (compare && + typeof token1 === "number" && typeof token2 === "number") { + within = Math.abs(token1 - token2) < delta; + } + } + else { + within = false; + } + } + return within; +} \ No newline at end of file