Skip to content

Commit

Permalink
Merge pull request #850 from stanford-oval/wip/fixes
Browse files Browse the repository at this point in the history
Small fixes
  • Loading branch information
gcampax committed Dec 6, 2021
2 parents 89b8921 + 5a5783e commit 9957909
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 69 deletions.
17 changes: 13 additions & 4 deletions lib/config.js → lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,19 @@
//
// Author: Giovanni Campagna <gcampagn@cs.stanford.edu>


// The URL of the Cloud version of ThingEngine (used for Cloud sync)
/**
* The URL of the Cloud version of ThingEngine (used for Cloud sync).
*/
export const THINGENGINE_URL = 'https://thingpedia.stanford.edu';

// The default URL of Thingpedia, used if the platform layer does not provide
// a different URL or a platform-specific ThingpediaClient
/**
* The default URL of Thingpedia, used if the platform layer does not provide
* a different URL or a platform-specific ThingpediaClient.
*/
export const THINGPEDIA_URL = 'https://thingpedia.stanford.edu/thingpedia';

/**
* The default URL of the NLP server, used if the platform layer does not
* provide a different URL.
*/
export const NLP_URL = 'https://almond-nl.stanford.edu';
74 changes: 64 additions & 10 deletions lib/dialogue-agent/dialogue-loop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,19 +246,13 @@ export class DialogueLoop {

private async _analyzeCommand(command : UserInput) : Promise<[DialogueHandler<any, any>|undefined, CommandAnalysisResult]> {
try {
// This algorithm will choose the dialogue handlers that reports:
// - the highest confidence
// - if a tie, the highest priority
// - if a tie, the current handler
// - if a tie, the first handler that reports any confidence at all

const handlers = [...this._iterateDialogueHandlers()];
const handlerCandidates = await Promise.all(handlers.map(async (handler) => {
const analysis = await handler.analyzeCommand(command);
return { handler: handler, analysis: analysis };
}));

return pickHandler(this._currentHandler, handlerCandidates, command, this._debug);
return pickHandler(this._currentHandler, this.expecting, handlerCandidates, command, this._debug);
} catch(e : any) {
if (e.code === 'EHOSTUNREACH' || e.code === 'ETIMEDOUT') {
await this.reply(this._("Sorry, I cannot contact the Genie service. Please check your Internet connection and try again later."), null);
Expand Down Expand Up @@ -702,20 +696,80 @@ export class DialogueLoop {
}

export function pickHandler(currentHandler : DialogueHandler<CommandAnalysisResult, any> | null,
expecting : ValueCategory|null,
handlerCandidates : Array<{ handler : DialogueHandler<CommandAnalysisResult, any>; analysis : CommandAnalysisResult; }>,
command : UserInput,
debug = false) : [DialogueHandler<any, any>|undefined, CommandAnalysisResult] {
let best : DialogueHandler<any, any>|undefined = undefined;
let bestanalysis : CommandAnalysisResult|undefined = undefined;
let bestconfidence = Confidence.NO;

// If "expecting === null",
// this algorithm will choose the dialogue handlers that reports:
// - the highest confidence
// - if a tie, the highest priority
// - if a tie, the current handler
// - if a tie, the first handler that reports any confidence at all
//
// If "expecting !== null",
// this algorithm will choose the current handler, unless one of the following
// is true:
// - some other handler returns exact_in_domain_command (or similar exact level type)
// and it's either higher priority or higher confidence than the current handler
// - the current handler returns out_of_domain

if (debug) {
for (const handlerItem of handlerCandidates) {
const handler = handlerItem.handler;
const analysis = handlerItem.analysis;
console.log(`Handler ${handler.uniqueId} reports ${CommandAnalysisType[analysis.type]}`);
}
}

if (expecting !== null && currentHandler !== null) {
const currentAnalysis = handlerCandidates.find((cand) => cand.handler === currentHandler)!.analysis;
if (currentAnalysis.type !== CommandAnalysisType.OUT_OF_DOMAIN_COMMAND) {
let best : DialogueHandler<any, any>|undefined = undefined;
let bestanalysis : CommandAnalysisResult|undefined = undefined;

for (const handlerItem of handlerCandidates) {
const handler = handlerItem.handler;
const analysis = handlerItem.analysis;

switch (analysis.type) {
case CommandAnalysisType.STOP:
case CommandAnalysisType.DEBUG:
case CommandAnalysisType.NEVERMIND:
case CommandAnalysisType.WAKEUP:
case CommandAnalysisType.EXACT_IN_DOMAIN_COMMAND:
if (best === undefined ||
(
handler.priority > best.priority ||
(currentHandler === handler && handler.priority >= best.priority)
)) {
best = handler;
bestanalysis = analysis;
}
break;

default:
// ignore this handler
}
}

if (best)
return [best, bestanalysis!];

return [currentHandler, currentAnalysis];
}

// fallthrough to the expecting === null case
}

for (const handlerItem of handlerCandidates) {
const handler = handlerItem.handler;
const analysis = handlerItem.analysis;

if (debug)
console.log(`Handler ${handler.uniqueId} reports ${CommandAnalysisType[analysis.type]}`);

switch (analysis.type) {
case CommandAnalysisType.STOP:
case CommandAnalysisType.DEBUG:
Expand Down
4 changes: 2 additions & 2 deletions lib/prediction/parserclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import LocalParserClient, { LocalParserOptions } from './localparserclient';
import { ExactMatcher } from './types';
export * from './types';

const URL = 'https://almond-nl.stanford.edu';
import { NLP_URL } from '../config';

// export for documentation
export { RemoteParserClient, LocalParserClient };
export type ParserClient = RemoteParserClient | LocalParserClient;

export function get(url = URL,
export function get(url = NLP_URL,
locale : string,
platform ?: Tp.BasePlatform,
exactmatcher ?: ExactMatcher,
Expand Down
2 changes: 1 addition & 1 deletion lib/templates/dialogue.genie
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ $agent : S.AgentReplyRecord = {
( ctx:ctx_greet ('hello' | 'hi') ('!' | ',') ('how can i help you' | 'what are you interested in' | 'what can i do for you') '?'
| ctx:ctx_init ('hello' | 'hi') ('!' | ',') ('how can i help you' | 'what are you interested in' | 'what can i do for you') '?'
| ctx:ctx_reinit ('how can i help you' | 'what are you interested in' | 'what can i do for you') '?'
) => S.makeAgentReply(ctx, S.makeSimpleState(ctx, 'sys_greet', null), null, null, { end: false }),
) => S.makeAgentReply(ctx, S.makeSimpleState(ctx, 'sys_greet', null), null, null),

?anything_else {
ctx:ctx_cancel anything_else_phrase
Expand Down
2 changes: 1 addition & 1 deletion test/agent/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ async function main(onlyIds) {
>> expecting = null
Hello! How can I help you?
>> context = $dialogue @org.thingpedia.dialogue.transaction . sys_greet ; // {}
>> expecting = generic
>> expecting = null
`);

const TEST_CASES = await loadTestCases();
Expand Down
4 changes: 2 additions & 2 deletions test/agent/tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ U: \t $dialogue @org.thingpedia.dialogue.transaction.greet;

A: Hello! How can I help you?
A: >> context = $dialogue @org.thingpedia.dialogue.transaction . sys_greet ; @org.thingpedia.weather . current ( location = LOCATION_0 ) #[ results = [ { location = LOCATION_0 , temperature = MEASURE_C_0 , wind_speed = MEASURE_mps_0 , humidity = NUMBER_0 , cloudiness = NUMBER_1 , fog = 0 , status = enum sunny , icon = PICTURE_0 } ] ] ; // {"LOCATION_0":{"latitude":37.442156,"longitude":-122.1634471,"display":"Palo Alto, California"},"MEASURE_C_0":{"unit":"C","value":15.6},"MEASURE_mps_0":{"unit":"mps","value":3.4},"NUMBER_0":91.3,"NUMBER_1":4.7,"PICTURE_0":"http://api.met.no/weatherapi/weathericon/1.1/?symbol=1;content_type=image/png"}
A: >> expecting = generic
A: >> expecting = null

U: \t $dialogue @org.thingpedia.dialogue.transaction.cancel;

Expand Down Expand Up @@ -1031,7 +1031,7 @@ U: \t $wakeup;

A: Hello! How can I help you?
A: >> context = $dialogue @org.thingpedia.dialogue.transaction . sys_greet ; // {}
A: >> expecting = generic
A: >> expecting = null

====
# 67-default-temperature
Expand Down
76 changes: 59 additions & 17 deletions test/unit/test_dialogue_loop.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ import assert from 'assert';

import * as constants from '../../lib/dialogue-agent/dialogue-loop';
import { pickHandler } from '../../lib/dialogue-agent/dialogue-loop';
import ValueCategory from '../../lib/dialogue-agent/value-category';

const thingtalk = { uniqueId: 'thingtalk', priority: 2 };
const bing = { uniqueId: 'bing', priority: 1 };

const TEST_CASES = [
// #1
[
null,
null, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_COMMAND } }
Expand All @@ -39,7 +40,7 @@ const TEST_CASES = [

// #2
[
null,
null, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } }
Expand All @@ -49,7 +50,7 @@ const TEST_CASES = [

// #3
[
null,
null, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_COMMAND } }
Expand All @@ -59,7 +60,7 @@ const TEST_CASES = [

// #4
[
null,
null, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } }
Expand All @@ -69,7 +70,7 @@ const TEST_CASES = [

// #5
[
null,
null, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.NONCONFIDENT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } }
Expand All @@ -79,7 +80,7 @@ const TEST_CASES = [

// #6
[
null,
null, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.NONCONFIDENT_IN_DOMAIN_COMMAND } }
Expand All @@ -89,7 +90,7 @@ const TEST_CASES = [

// #7
[
null,
null, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.NONCONFIDENT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.NONCONFIDENT_IN_DOMAIN_COMMAND } }
Expand All @@ -99,7 +100,7 @@ const TEST_CASES = [

// #8
[
null,
null, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.NONCONFIDENT_IN_DOMAIN_COMMAND } }
Expand All @@ -109,7 +110,7 @@ const TEST_CASES = [

// #9
[
null,
null, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.NONCONFIDENT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_COMMAND } }
Expand All @@ -119,7 +120,7 @@ const TEST_CASES = [

// #10
[
thingtalk,
thingtalk, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_FOLLOWUP } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_FOLLOWUP } }
Expand All @@ -129,7 +130,7 @@ const TEST_CASES = [

// #11
[
thingtalk,
thingtalk, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_FOLLOWUP } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_FOLLOWUP } }
Expand All @@ -139,7 +140,7 @@ const TEST_CASES = [

// #12
[
thingtalk,
thingtalk, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_FOLLOWUP } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_FOLLOWUP } }
Expand All @@ -149,7 +150,7 @@ const TEST_CASES = [

// #13
[
thingtalk,
thingtalk, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_FOLLOWUP } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_FOLLOWUP } }
Expand All @@ -159,7 +160,7 @@ const TEST_CASES = [

// #14
[
null,
null, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.STRONGLY_CONFIDENT_IN_DOMAIN_COMMAND } }
Expand All @@ -169,31 +170,72 @@ const TEST_CASES = [

// #15
[
thingtalk,
thingtalk, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_FOLLOWUP } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.STRONGLY_CONFIDENT_IN_DOMAIN_FOLLOWUP } }
],
'thingtalk'
],

// #16
[
thingtalk, null,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.STRONGLY_CONFIDENT_IN_DOMAIN_COMMAND } }
],
'bing'
],

// #17
[
thingtalk, ValueCategory.Generic,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.STRONGLY_CONFIDENT_IN_DOMAIN_COMMAND } }
],
'thingtalk'
],

// #18
[
thingtalk, ValueCategory.Generic,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.EXACT_IN_DOMAIN_COMMAND } }
],
'bing'
],

// #19
[
thingtalk, ValueCategory.Generic,
[
{ handler: thingtalk, analysis: { type: constants.CommandAnalysisType.OUT_OF_DOMAIN_COMMAND } },
{ handler: bing, analysis: { type: constants.CommandAnalysisType.CONFIDENT_IN_DOMAIN_COMMAND } }
],
'bing'
],
];

async function test(i, reverse=false) {
if (reverse)
console.log('Test Case # %d [reverse]', (i+1));
else
console.log('Test Case #' + (i+1));
let [currentHandler, testCase, expected] = TEST_CASES[i];
let [currentHandler, expecting, testCase, expected] = TEST_CASES[i];
const best = undefined;
const bestanalysis = undefined;
if (reverse)
testCase.reverse();
const [handler, analysis] = pickHandler(currentHandler,
expecting,
testCase,
best,
bestanalysis,
constants.Confidence.NO);
console.log("handler: %O | analysis: %O", handler, analysis);
console.log("handler: %O | analysis: %s", handler, constants.CommandAnalysisType[analysis.type]);
assert.strictEqual(handler.uniqueId, expected);
}

Expand Down
Loading

0 comments on commit 9957909

Please sign in to comment.