Vishwac/orchestrator (#2654)
* initial commit * add orchestrator pkg to updateDep script, minor cleanup * updates to samples * updates * update * removing sample * updates * updates * update exports * Dispatch sample * add adaptive sample * Adaptive recognizer with sample * add timing and update samples * updates. * made some changes to make declarative sample work * updates * updates to recognizer * tests * removing samples * updates to lerna * fixing build issues * cleanup in orchestrator lib * sort ai-orchestrator index.ts * adding tests. * remove `dom` from bb-ai-orchestrator * revert Orchestrator require() * updating orchestrator-core pkg. * updates to package.json * rev orchestrator-core pkg * revert readme.md * revert readme.md * update package.json to match bf-cli Co-authored-by: stevengum <14935595+stevengum@users.noreply.github.com> Co-authored-by: Zichuan Ma <zim@microsoft.com>
This commit is contained in:
Родитель
fa3e1c970f
Коммит
01cd644408
|
@ -16,6 +16,7 @@
|
|||
"packages": [
|
||||
"libraries/botbuilder",
|
||||
"libraries/botbuilder-ai",
|
||||
"libraries/botbuilder-ai-orchestrator",
|
||||
"libraries/botbuilder-applicationinsights",
|
||||
"libraries/botbuilder-azure",
|
||||
"libraries/botbuilder-core",
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/**/node_modules
|
||||
/**/.vscode
|
||||
/**/lib/*
|
||||
coverage
|
||||
.nyc_output
|
||||
/**/.antlr
|
||||
/**/*.interp
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"extension": [
|
||||
".js"
|
||||
],
|
||||
"include": [
|
||||
"lib/**/*.js"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**",
|
||||
"**/tests/**",
|
||||
"**/coverage/**",
|
||||
"**/*.d.ts"
|
||||
],
|
||||
"reporter": [
|
||||
"html"
|
||||
],
|
||||
"all": true,
|
||||
"cache": true
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"name": "botbuilder-ai-orchestrator",
|
||||
"author": "Microsoft Corp.",
|
||||
"description": "Orchestrator recognizer",
|
||||
"version": "4.1.6",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"botbuilder",
|
||||
"botframework",
|
||||
"orchestrator"
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://github.com/Microsoft/botbuilder-js/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/botbuilder-js.git"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"typings": "./lib/index.d.ts",
|
||||
"dependencies": {
|
||||
"orchestrator-core": "beta",
|
||||
"adaptive-expressions": "4.1.6",
|
||||
"botbuilder-core": "4.1.6",
|
||||
"botbuilder-dialogs": "4.1.6",
|
||||
"botbuilder-dialogs-adaptive": "4.1.6",
|
||||
"botbuilder-dialogs-declarative": "4.1.6",
|
||||
"read-text-file": "1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/api-extractor": "^7.7.12",
|
||||
"@types/mocha": "^2.2.47",
|
||||
"@types/node": "^12.6.9",
|
||||
"mocha": "^5.2.0",
|
||||
"nyc": "^15.0.0",
|
||||
"source-map-support": "^0.5.3",
|
||||
"ts-node": "^4.1.0",
|
||||
"typescript": "3.5.3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build-docs": "typedoc --theme markdown --entryPoint botbuilder-ai-orchestrator --excludePrivate --includeDeclarations --ignoreCompilerErrors --module amd --out ..\\..\\doc\\botbuilder-ai-orchestrator .\\lib\\index.d.ts --hideGenerator --name \"Bot Builder SDK - Orchestrator\" --readme none",
|
||||
"build:rollup": "npm run clean && npm run build && api-extractor run --verbose --local",
|
||||
"clean": "erase /q /s .\\lib",
|
||||
"set-version": "npm version --allow-same-version ${Version}",
|
||||
"test": "tsc && nyc mocha tests/",
|
||||
"test:compat": "api-extractor run --verbose"
|
||||
},
|
||||
"files": [
|
||||
"/lib",
|
||||
"/schema",
|
||||
"/src"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"$schema": "https://schemas.botframework.com/schemas/component/v1.0/component.schema",
|
||||
"$role": "implements(Microsoft.IRecognizer)",
|
||||
"title": "QnAMaker Recognizer",
|
||||
"description": "Recognizer for generating QnAMatch intents from a KB.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"title": "Id",
|
||||
"description": "Optional unique id using with RecognizerSet."
|
||||
},
|
||||
"modelPath": {
|
||||
"$ref": "schema:#/definitions/stringExpression",
|
||||
"title": "Model",
|
||||
"description": "NLR model file path.",
|
||||
"default": "settings.orchestrator.modelpath"
|
||||
},
|
||||
"snapshotPath": {
|
||||
"$ref": "schema:#/definitions/stringExpression",
|
||||
"title": "Endpoint Key",
|
||||
"description": "SnapShot file path.",
|
||||
"default": "settings.orchestrator.shapshotpath"
|
||||
},
|
||||
"useCompactEmbeddings": {
|
||||
"$ref": "schema:#/definitions/booleanExpression",
|
||||
"title": "Use compact embeddings",
|
||||
"description": "If true, compact embeddings will be used.",
|
||||
"default": "true",
|
||||
"examples": [
|
||||
true,
|
||||
"=turn.useCompactEmbeddings"
|
||||
]
|
||||
},
|
||||
"entityRecognizers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$kind": "Microsoft.IEntityRecognizer"
|
||||
},
|
||||
"title": "Entity recognizers",
|
||||
"description": "Collection of entity recognizers to use."
|
||||
},
|
||||
"disambiguationScoreThreshold": {
|
||||
"$ref": "schema:#/definitions/numberExpression",
|
||||
"title": "Threshold",
|
||||
"description": "Recognizer returns ChooseIntent (disambiguation) if other intents are classified within this score of the top scoring intent.",
|
||||
"default": 0.05,
|
||||
"examples": [
|
||||
true,
|
||||
"=turn.scoreThreshold",
|
||||
"=settings.orchestrator.disambigscorethreshold"
|
||||
]
|
||||
},
|
||||
"detectAmbiguousIntents": {
|
||||
"$ref": "schema:#/definitions/booleanExpression",
|
||||
"title": "Threshold",
|
||||
"description": "If true, recognizer will look for ambiguous intents (intents with close recognition scores from top scoring intent).",
|
||||
"default": false,
|
||||
"examples": [
|
||||
true,
|
||||
"=turn.detectAmbiguousIntents",
|
||||
"=settings.orchestrator.detectambiguousintents"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"model",
|
||||
"shapShot"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* @module botbuilder-ai-orchestrator
|
||||
*/
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
export { OrchestratorAdaptiveRecognizer } from './orchestratorAdaptiveRecognizer';
|
||||
export { OrchestratorComponentRegistration } from './orchestratorComponentRegistration';
|
||||
export { OrchestratorRecognizer } from './orchestratorRecognizer';
|
|
@ -0,0 +1,282 @@
|
|||
/**
|
||||
* @module botbuilder-ai-orchestrator
|
||||
*/
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { existsSync } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { TextEncoder } from 'util';
|
||||
|
||||
import { BoolExpression, NumberExpression, StringExpression } from 'adaptive-expressions';
|
||||
import { Activity, Entity, RecognizerResult } from 'botbuilder-core';
|
||||
import { DialogContext } from 'botbuilder-dialogs';
|
||||
import { createRecognizerResult, EntityRecognizer, EntityRecognizerSet, Recognizer, TextEntity } from 'botbuilder-dialogs-adaptive';
|
||||
|
||||
const oc: any = require('orchestrator-core/orchestrator-core.node');
|
||||
const ReadText: any = require('read-text-file');
|
||||
|
||||
export class OrchestratorAdaptiveRecognizer extends Recognizer {
|
||||
/**
|
||||
* Recognizers unique ID.
|
||||
*/
|
||||
public id: string;
|
||||
|
||||
/**
|
||||
* Path to the model to load.
|
||||
*/
|
||||
public modelPath: StringExpression = new StringExpression('');
|
||||
|
||||
/**
|
||||
* Path to the snapshot (.blu file) to load.
|
||||
*/
|
||||
public snapshotPath: StringExpression = new StringExpression('');
|
||||
|
||||
/**
|
||||
* Threshold value to use for ambiguous intent detection.
|
||||
* Any intents that are classified with a score that is within this value from the top scoring intent is determined to be ambiguous.
|
||||
*/
|
||||
public disambiguationScoreThreshold: NumberExpression = new NumberExpression(0.05);
|
||||
|
||||
/**
|
||||
* Enable ambiguous intent detection.
|
||||
*/
|
||||
public detectAmbiguousIntents: BoolExpression = new BoolExpression(false);
|
||||
|
||||
/**
|
||||
* The entity recognizers.
|
||||
*/
|
||||
public entityRecognizers: EntityRecognizer[] = [];
|
||||
|
||||
/**
|
||||
* Intent name if ambiguous intents are detected.
|
||||
*/
|
||||
public readonly chooseIntent: string = 'ChooseIntent';
|
||||
|
||||
/**
|
||||
* Property under which ambiguous intents are returned.
|
||||
*/
|
||||
public readonly candidatesCollection: string = 'candidates';
|
||||
|
||||
/**
|
||||
* Intent name when no intent matches.
|
||||
*/
|
||||
public readonly noneIntent: string = 'None';
|
||||
|
||||
/**
|
||||
* Full recognition results are available under this property
|
||||
*/
|
||||
public readonly resultProperty: string = 'result';
|
||||
|
||||
private readonly unknownIntentFilterScore = 0.4;
|
||||
private static orchestrator: any = null;
|
||||
private resolver: any = null;
|
||||
private _modelPath: string = null;
|
||||
private _snapshotPath: string = null;
|
||||
|
||||
/**
|
||||
* Returns an OrchestratorAdaptiveRecognizer instance.
|
||||
* @param modelPath Path to NLR model.
|
||||
* @param snapshoPath Path to snapshot.
|
||||
* @param resolver Orchestrator resolver to use.
|
||||
*/
|
||||
constructor(modelPath?: string, snapshoPath?: string, resolver?: any)
|
||||
{
|
||||
super()
|
||||
this._modelPath = modelPath !== undefined ? modelPath : null;
|
||||
this._snapshotPath = snapshoPath !== undefined ? snapshoPath : null;
|
||||
this.resolver = resolver !== undefined ? resolver : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new OrchestratorAdaptiveRecognizer instance.
|
||||
* @param dialogContext Context for the current dialog.
|
||||
* @param activity Current activity sent from user.
|
||||
*/
|
||||
public async recognize(dialogContext: DialogContext, activity: Activity): Promise<RecognizerResult> {
|
||||
if (this.modelPath === null) {
|
||||
throw new Error(`Missing "ModelPath" information.`);
|
||||
}
|
||||
|
||||
if (this.snapshotPath === null) {
|
||||
throw new Error(`Missing "SnapshotPath" information.`);
|
||||
}
|
||||
|
||||
const text = activity.text || '';
|
||||
this._modelPath = this.modelPath.getValue(dialogContext.state);
|
||||
this._snapshotPath = this.snapshotPath.getValue(dialogContext.state);
|
||||
const detectAmbiguity = this.detectAmbiguousIntents.getValue(dialogContext.state);
|
||||
const disambiguationScoreThreshold = this.disambiguationScoreThreshold.getValue(dialogContext.state);
|
||||
|
||||
const recognizerResult: RecognizerResult = createRecognizerResult(text, {}, {});
|
||||
|
||||
if (text === '') {
|
||||
return recognizerResult;
|
||||
}
|
||||
|
||||
if (OrchestratorAdaptiveRecognizer.orchestrator === null || this.resolver === null) {
|
||||
this.Initialize();
|
||||
}
|
||||
|
||||
// recognize text
|
||||
let result = await this.resolver.score(text);
|
||||
|
||||
if (Object.entries(result).length !== 0) {
|
||||
this.AddTopScoringIntent(result, recognizerResult);
|
||||
}
|
||||
|
||||
// run entity recognizers
|
||||
await this.recognizeEntities(dialogContext, text, activity.locale || '', recognizerResult);
|
||||
|
||||
// Add full recognition result as a 'result' property
|
||||
recognizerResult[this.resultProperty] = result;
|
||||
|
||||
// disambiguate
|
||||
if (detectAmbiguity) {
|
||||
const topScoringIntent = result[0].score;
|
||||
const scoreDelta = topScoringIntent - disambiguationScoreThreshold;
|
||||
const ambiguousIntents = result.filter(x => x.score >= scoreDelta);
|
||||
if (ambiguousIntents.length > 1) {
|
||||
recognizerResult.intents = {};
|
||||
recognizerResult.intents[this.chooseIntent] = { score: 1.0 };
|
||||
recognizerResult[this.candidatesCollection] = [];
|
||||
ambiguousIntents.forEach(item => {
|
||||
let itemRecoResult = {
|
||||
text: text,
|
||||
intents: {},
|
||||
entities: {},
|
||||
score: item.score
|
||||
};
|
||||
itemRecoResult.intents[item.label.name] = {
|
||||
score: item.score
|
||||
};
|
||||
itemRecoResult.entities = recognizerResult.entities;
|
||||
recognizerResult[this.candidatesCollection].push({
|
||||
intent: item.label.name,
|
||||
score: item.score,
|
||||
closestText: item.closest_text,
|
||||
result: itemRecoResult
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.entries(recognizerResult.intents).length == 0) {
|
||||
recognizerResult.intents[this.noneIntent] = { score: 1.0 };
|
||||
}
|
||||
|
||||
await dialogContext.context.sendTraceActivity('OrchestratorRecognizer', recognizerResult, 'RecognizerResult', 'Orchestrator recognizer RecognizerResult');
|
||||
|
||||
return recognizerResult;
|
||||
}
|
||||
|
||||
private AddTopScoringIntent(result: any, recognizerResult: RecognizerResult): void {
|
||||
const topScoringIntent = result[0].label.name;
|
||||
const topScore = result[0].score;
|
||||
|
||||
// if top scoring intent is less than threshold, return None
|
||||
if (topScore < this.unknownIntentFilterScore) {
|
||||
recognizerResult.intents['None'] = { score: 1.0 };
|
||||
} else if (!recognizerResult.intents[topScoringIntent]) {
|
||||
recognizerResult.intents[topScoringIntent] = {
|
||||
score: topScore
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async recognizeEntities(dialogContext: DialogContext, text: string, locale: string, recognizerResult: RecognizerResult) {
|
||||
const entityPool: Entity[] = [];
|
||||
const textEntity = new TextEntity(text);
|
||||
textEntity['start'] = 0;
|
||||
textEntity['end'] = text.length;
|
||||
textEntity['score'] = 1.0;
|
||||
|
||||
entityPool.push(textEntity);
|
||||
if (this.entityRecognizers) {
|
||||
const entitySet = new EntityRecognizerSet();
|
||||
entitySet.push(...this.entityRecognizers);
|
||||
const newEntities = await entitySet.recognizeEntities(dialogContext, text, locale, entityPool);
|
||||
if (newEntities.length > 0) {
|
||||
entityPool.push(...newEntities);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < entityPool.length; i++) {
|
||||
const entityResult = entityPool[i];
|
||||
let values = [];
|
||||
if (!recognizerResult.entities.hasOwnProperty(entityResult.type)) {
|
||||
recognizerResult.entities[entityResult.type] = values;
|
||||
} else {
|
||||
values = recognizerResult.entities[entityResult.type];
|
||||
}
|
||||
values.push(entityResult['text']);
|
||||
|
||||
let instanceRoot = {};
|
||||
if (!recognizerResult.entities.hasOwnProperty('$instance')) {
|
||||
recognizerResult.entities['$instance'] = instanceRoot;
|
||||
} else {
|
||||
instanceRoot = recognizerResult.entities['$instance'];
|
||||
}
|
||||
|
||||
let instanceData = [];
|
||||
if (!instanceRoot.hasOwnProperty(entityResult.type)) {
|
||||
instanceRoot[entityResult.type] = instanceData;
|
||||
} else {
|
||||
instanceData = instanceRoot[entityResult.type];
|
||||
}
|
||||
|
||||
const instance = {
|
||||
startIndex: entityResult['start'],
|
||||
endIndex: entityResult['end'],
|
||||
score: 1.0,
|
||||
text: entityResult['text'],
|
||||
type: entityResult['type'],
|
||||
resolution: entityResult['resolution']
|
||||
};
|
||||
instanceData.push(instance);
|
||||
}
|
||||
}
|
||||
|
||||
private Initialize() {
|
||||
if (OrchestratorAdaptiveRecognizer.orchestrator == null && this.resolver == null)
|
||||
{
|
||||
if (this._modelPath == null) {
|
||||
throw new Error(`Missing "ModelPath" information.`);
|
||||
}
|
||||
|
||||
if (this._snapshotPath == null) {
|
||||
throw new Error(`Missing "ShapshotPath" information.`);
|
||||
}
|
||||
const fullModelPath = resolve(this._modelPath);
|
||||
const fullSnapshotPath = resolve(this._snapshotPath);
|
||||
if (!existsSync(fullModelPath)) {
|
||||
throw new Error(`Model folder does not exist at ${fullModelPath}.`);
|
||||
}
|
||||
if (!existsSync(fullSnapshotPath)) {
|
||||
throw new Error(`Snapshot file does not exist at ${fullSnapshotPath}.`);
|
||||
}
|
||||
|
||||
if (OrchestratorAdaptiveRecognizer.orchestrator == null) {
|
||||
console.time("Model load");
|
||||
// Create orchestrator core
|
||||
OrchestratorAdaptiveRecognizer.orchestrator = new oc.Orchestrator();
|
||||
if (OrchestratorAdaptiveRecognizer.orchestrator.load(fullModelPath) === false) {
|
||||
throw new Error(`Model load failed.`);
|
||||
}
|
||||
console.timeEnd("Model load");
|
||||
}
|
||||
|
||||
if (this.resolver == null) {
|
||||
// Load the snapshot
|
||||
const encoder: any = new TextEncoder();
|
||||
const fileContent: string = ReadText.readSync(fullSnapshotPath)
|
||||
const snapshot: Uint8Array = encoder.encode(fileContent);
|
||||
|
||||
// Load snapshot and create resolver
|
||||
this.resolver = OrchestratorAdaptiveRecognizer.orchestrator.createLabelResolver(snapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* @module botbuilder-ai-orchestrator
|
||||
*/
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { BoolExpressionConverter, NumberExpressionConverter, StringExpressionConverter } from 'adaptive-expressions';
|
||||
import { AdaptiveTypeBuilder } from 'botbuilder-dialogs-adaptive';
|
||||
import { BuilderRegistration, ComponentRegistration, ResourceExplorer } from 'botbuilder-dialogs-declarative';
|
||||
|
||||
import { OrchestratorAdaptiveRecognizer } from './orchestratorAdaptiveRecognizer';
|
||||
|
||||
export class OrchestratorComponentRegistration implements ComponentRegistration {
|
||||
private readonly _builderRegistrations: BuilderRegistration[] = [];
|
||||
private _resourceExplorer: ResourceExplorer;
|
||||
|
||||
public getTypeBuilders(): BuilderRegistration[] {
|
||||
return this._builderRegistrations;
|
||||
}
|
||||
|
||||
public constructor(resourceExplorer: ResourceExplorer) {
|
||||
this._resourceExplorer = resourceExplorer;
|
||||
|
||||
this._builderRegistrations.push(
|
||||
new BuilderRegistration('Microsoft.OrchestratorRecognizer', new AdaptiveTypeBuilder(OrchestratorAdaptiveRecognizer, this._resourceExplorer, {
|
||||
modelPath: new StringExpressionConverter(),
|
||||
snapshotPath: new StringExpressionConverter(),
|
||||
disambiguationScoreThreshold: new NumberExpressionConverter(),
|
||||
detectAmbiguousIntents: new BoolExpressionConverter(),
|
||||
}))
|
||||
);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* @module botbuilder-ai-orchestrator
|
||||
*/
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { BoolExpression, NumberExpression, StringExpression } from 'adaptive-expressions';
|
||||
import { RecognizerResult, TurnContext } from 'botbuilder-core';
|
||||
import { Configurable, DialogContext, DialogSet } from 'botbuilder-dialogs';
|
||||
import { EntityRecognizer } from 'botbuilder-dialogs-adaptive';
|
||||
|
||||
import { OrchestratorAdaptiveRecognizer } from './orchestratorAdaptiveRecognizer';
|
||||
|
||||
export class OrchestratorRecognizer extends Configurable {
|
||||
/**
|
||||
* Full recognition results are available under this property
|
||||
*/
|
||||
public readonly resultProperty: string = 'result';
|
||||
|
||||
/**
|
||||
* Recognizers unique ID.
|
||||
*/
|
||||
public id: string;
|
||||
|
||||
/**
|
||||
* Path to the model to load.
|
||||
*/
|
||||
public modelPath: string = null;
|
||||
|
||||
/**
|
||||
* Path to the snapshot (.blu file) to load.
|
||||
*/
|
||||
public snapshotPath: string = null;
|
||||
|
||||
/**
|
||||
* The entity recognizers.
|
||||
*/
|
||||
public entityRecognizers: EntityRecognizer[] = [];
|
||||
|
||||
/**
|
||||
* Threshold value to use for ambiguous intent detection. Defaults to 0.05.
|
||||
* Any intents that are classified with a score that is within this value from the top
|
||||
* scoring intent is determined to be ambiguous.
|
||||
*/
|
||||
public disambiguationScoreThreshold: number = 0.05;
|
||||
|
||||
/**
|
||||
* Enable ambiguous intent detection. Defaults to false.
|
||||
*/
|
||||
public detectAmbiguousIntents: boolean = false;
|
||||
|
||||
/**
|
||||
* Returns recognition result. Also sends trace activity with recognition result.
|
||||
* @param context Context for the current turn of conversation with the use.
|
||||
*/
|
||||
public async recognize(context: TurnContext): Promise<RecognizerResult> {
|
||||
const rec = new OrchestratorAdaptiveRecognizer();
|
||||
rec.id = this.id;
|
||||
rec.modelPath = new StringExpression(this.modelPath);
|
||||
rec.snapshotPath = new StringExpression(this.snapshotPath);
|
||||
rec.entityRecognizers = this.entityRecognizers;
|
||||
rec.disambiguationScoreThreshold = new NumberExpression(this.disambiguationScoreThreshold);
|
||||
rec.detectAmbiguousIntents = new BoolExpression(this.detectAmbiguousIntents);
|
||||
|
||||
const dc = new DialogContext(new DialogSet(), context, { dialogStack:[] });
|
||||
return await rec.recognize(dc, context.activity);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
--require ts-node/register
|
||||
--require source-map-support/register
|
||||
--recursive
|
||||
**/*.js
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
class MockResolver {
|
||||
constructor(score)
|
||||
{
|
||||
this._score = score;
|
||||
}
|
||||
|
||||
score(text)
|
||||
{
|
||||
return this._score;
|
||||
}
|
||||
}
|
||||
|
||||
class TestAdapterSettings {
|
||||
constructor(appId, appPassword) {
|
||||
this.appId = appId;
|
||||
this.appPassword = appPassword;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {MockResolver, TestAdapterSettings};
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
const { MockResolver, TestAdapterSettings } = require('./mockResolver');
|
||||
const assert = require('assert');
|
||||
const { OrchestratorAdaptiveRecognizer } = require('../lib');
|
||||
const { DialogContext, DialogSet } = require('botbuilder-dialogs');
|
||||
const { TurnContext, MessageFactory } = require('botbuilder-core');
|
||||
const { BotFrameworkAdapter } = require('../../botbuilder/lib');
|
||||
const { StringExpression, BoolExpression, NumberExpression } = require('adaptive-expressions');
|
||||
const { NumberEntityRecognizer } = require('botbuilder-dialogs-adaptive');
|
||||
|
||||
describe('OrchestratorAdpativeRecognizer tests', function() {
|
||||
it('Test intent recognition', (done) => {
|
||||
let result = [
|
||||
{
|
||||
score : 0.9,
|
||||
label : {
|
||||
name : "mockLabel"
|
||||
}
|
||||
}
|
||||
];
|
||||
let mockResolver = new MockResolver(result);
|
||||
let testPaths = "test";
|
||||
let rec = new OrchestratorAdaptiveRecognizer(testPaths, testPaths, mockResolver);
|
||||
rec.modelPath = new StringExpression(testPaths);
|
||||
rec.snapshotPath = new StringExpression(testPaths);
|
||||
let {dc, activity} = createTestDcAndActivity("hello")
|
||||
rec.recognize(dc, activity)
|
||||
.then(res => {
|
||||
assert(res.text, "hello");
|
||||
assert(res.intents.mockLabel.score, 0.9);
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err))
|
||||
})
|
||||
|
||||
it('Test entity recognition', (done) => {
|
||||
let result = [
|
||||
{
|
||||
score : 0.9,
|
||||
label : {
|
||||
name : "mockLabel"
|
||||
}
|
||||
}
|
||||
];
|
||||
let mockResolver = new MockResolver(result);
|
||||
let testPaths = "test";
|
||||
let rec = new OrchestratorAdaptiveRecognizer(testPaths, testPaths, mockResolver);
|
||||
rec.modelPath = new StringExpression(testPaths);
|
||||
rec.snapshotPath = new StringExpression(testPaths);
|
||||
rec.entityRecognizers = [
|
||||
new NumberEntityRecognizer()
|
||||
];
|
||||
let {dc, activity} = createTestDcAndActivity("hello 123")
|
||||
rec.recognize(dc, activity)
|
||||
.then(res => {
|
||||
assert(res.text, "hello 123");
|
||||
assert(res.intents.mockLabel.score, 0.9);
|
||||
assert(res.entities.number[0], "123");
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err))
|
||||
})
|
||||
|
||||
it('Test ambiguous intent recognition', (done) => {
|
||||
let result = [
|
||||
{
|
||||
score : 0.9,
|
||||
label : {
|
||||
name : "mockLabel1"
|
||||
}
|
||||
},
|
||||
{
|
||||
score : 0.85,
|
||||
label : {
|
||||
name : "mockLabel2"
|
||||
}
|
||||
},
|
||||
{
|
||||
score : 0.79,
|
||||
label : {
|
||||
name : "mockLabel3"
|
||||
}
|
||||
}
|
||||
];
|
||||
let mockResolver = new MockResolver(result);
|
||||
let testPaths = "test";
|
||||
let rec = new OrchestratorAdaptiveRecognizer(testPaths, testPaths, mockResolver);
|
||||
rec.modelPath = new StringExpression(testPaths);
|
||||
rec.snapshotPath = new StringExpression(testPaths);
|
||||
rec.detectAmbiguousIntents = new BoolExpression(true);
|
||||
rec.disambiguationScoreThreshold = new NumberExpression(0.1);
|
||||
let {dc, activity} = createTestDcAndActivity("hello")
|
||||
rec.recognize(dc, activity)
|
||||
.then(res => {
|
||||
assert(res.intents.ChooseIntent.score, 1);
|
||||
assert(res.candidates[0].intent, "mockLabel1");
|
||||
assert(res.candidates[1].intent, "mockLabel2");
|
||||
done();
|
||||
})
|
||||
.catch(err => done(err))
|
||||
})
|
||||
})
|
||||
|
||||
const createTestDcAndActivity = function (message) {
|
||||
let settings = new TestAdapterSettings('appId', 'password');
|
||||
let adapter = new BotFrameworkAdapter(settings);
|
||||
let activity = MessageFactory.text(message);
|
||||
let turnContext = new TurnContext(adapter, activity);
|
||||
turnContext.sendTraceActivity = function() {}
|
||||
let state = [{dialogStack: []}];
|
||||
let dc = new DialogContext(new DialogSet(), turnContext, state);
|
||||
return {dc, activity};
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"lib": ["es2015"],
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"downlevelIteration": true,
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"outDir": "./lib",
|
||||
"rootDir": "./src",
|
||||
"types" : ["node"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
|
@ -13,8 +13,9 @@ if(previewVersion === 'adaptive-expressions') {
|
|||
previewVersion = undefined;
|
||||
}
|
||||
const previewPackages = {
|
||||
'botbuilder-ai-orchestrator': true,
|
||||
'botbuilder-dialogs-adaptive': true,
|
||||
'botbuilder-dialogs-adaptive-tests': true,
|
||||
'botbuilder-dialogs-adaptive-testing': true,
|
||||
'botbuilder-dialogs-declarative': true
|
||||
}
|
||||
var dependencies = myArgs.slice(previewVersion ? 3 : 2);
|
||||
|
|
Загрузка…
Ссылка в новой задаче