From cd410e2ad3ca1ab55ea8afaa712004cc80890920 Mon Sep 17 00:00:00 2001 From: Vladislav Maraev Date: Sat, 17 Aug 2024 23:48:05 +0200 Subject: [PATCH] One object for rules --- src/isu.ts | 137 +++++++++++++++++--------------- src/nlug.ts | 4 + src/rules.ts | 215 ++++++++++++++++++++++++++++----------------------- src/types.ts | 36 +++++---- 4 files changed, 219 insertions(+), 173 deletions(-) diff --git a/src/isu.ts b/src/isu.ts index 22c0be6..7800c9b 100644 --- a/src/isu.ts +++ b/src/isu.ts @@ -2,8 +2,8 @@ import { assign, createActor, setup, AnyMachineSnapshot, raise } from "xstate"; import { speechstate } from "speechstate"; import { createBrowserInspector } from "@statelyai/inspect"; import { KEY } from "./azure"; -import { DMContext, DMEvent, UpdateEvent } from "./types"; -import { preconditions, effects } from "./rules"; +import { DMContext, DMEvent } from "./types"; +import { rules } from "./rules"; import { nlg, nlu } from "./nlug"; const inspector = createBrowserInspector(); @@ -26,11 +26,14 @@ const settings = { const dmMachine = setup({ guards: { /** preconditions */ - update: preconditions, + isu: ({ context }, params: { name: string }) => + rules[params.name](context).preconditions, }, actions: { /** effects */ - update: effects, + isu: assign(({ context }, params: { name: string }) => { + return { is: rules[params.name](context).effects }; + }), /** update latest_move (outside IS!) based on ASR/TTS (SAYS event) */ updateLatestMove: assign(({ event }) => { console.debug("[DM updateLatestMove]", event); @@ -66,7 +69,14 @@ const dmMachine = setup({ content: null, }, is: { - private: { agenda: [] }, + private: { + agenda: [ + { + type: "greet", + content: null, + }, + ], + }, shared: { lu: undefined, qud: [], com: [] }, }, }; @@ -109,16 +119,16 @@ const dmMachine = setup({ ASR_NOINPUT: { target: "Idle", // FOR TESTING - // actions: raise({ - // type: "SAYS", - // value: { - // speaker: "usr", - // move: { - // type: "ask", - // content: (x) => `favorite_food ${x}`, - // }, - // }, - // }), + actions: raise({ + type: "SAYS", + value: { + speaker: "usr", + move: { + type: "ask", + content: (x: string) => `favorite_food ${x}`, + }, + }, + }), }, }, }, @@ -153,26 +163,6 @@ const dmMachine = setup({ }, }, }, - UpdateRules: { - on: { - UPDATE: [ - { - guard: { - type: "update", - params: ({ event }: { event: UpdateEvent }) => ({ - ruleName: event.value, - }), - }, - actions: { - type: "update", - params: ({ event }: { event: UpdateEvent }) => ({ - ruleName: event.value, - }), - }, - }, - ], - }, - }, DME: { initial: "Update", // todo: shd be Select states: { @@ -183,10 +173,8 @@ const dmMachine = setup({ Init: { always: { target: "Grounding", - actions: raise({ - type: "UPDATE", - value: "clear_agenda", - }), + guard: { type: "isu", params: { name: "clear_agenda" } }, + actions: { type: "isu", params: { name: "clear_agenda" } }, }, }, Grounding: { @@ -198,33 +186,60 @@ const dmMachine = setup({ { type: "updateLatestMove", }, - raise({ - type: "UPDATE", - value: "get_latest_move", - }), + { type: "isu", params: { name: "get_latest_move" } }, ], }, }, }, Integrate: { - always: { - target: "Init", - actions: [ - raise({ - type: "UPDATE", - value: "integrate_sys_greet", - }), - raise({ - type: "UPDATE", - value: "integrate_sys_ask", - }), - raise({ - type: "UPDATE", - value: "integrate_usr_ask", - }), - ], - }, + always: [ + { + target: "DowndateQUD", + guard: { + type: "isu", + params: { name: "integrate_sys_ask" }, + }, + actions: { + type: "isu", + params: { name: "integrate_sys_ask" }, + }, + }, + { + target: "DowndateQUD", + guard: { + type: "isu", + params: { name: "integrate_usr_ask" }, + }, + actions: { + type: "isu", + params: { name: "integrate_usr_ask" }, + }, + }, + { + target: "DowndateQUD", + guard: { + type: "isu", + params: { name: "integrate_greet" }, + }, + actions: { + type: "isu", + params: { name: "integrate_greet" }, + }, + }, + ], + }, + DowndateQUD: { + always: { target: "LoadPlan" }, + }, + LoadPlan: { + always: { target: "ExecPlan" }, }, + ExecPlan: { + type: "final", + }, + }, + onDone: { + target: "Select", }, }, }, @@ -242,10 +257,10 @@ let is = dmActor.getSnapshot().context.is; console.log("[IS (initial)]", is); dmActor.subscribe((snapshot: AnyMachineSnapshot) => { /* if you want to log some parts of the state */ - // is !== snapshot.context.is && console.log("[IS]", snapshot.context.is); is = snapshot.context.is; // console.log("IS", is); + console.log("%cState value:", "background-color: #056dff", snapshot.value); }); export function setupButton(element: HTMLElement) { diff --git a/src/nlug.ts b/src/nlug.ts index 94008da..1642640 100644 --- a/src/nlug.ts +++ b/src/nlug.ts @@ -10,6 +10,10 @@ const nluMapping: NLUMapping = { type: "ask", content: (x) => `favorite_food ${x}`, }, + Pizza: { + type: "answer", + content: "pizza", + }, }; const nlgMapping: NLGMapping = [ [ diff --git a/src/rules.ts b/src/rules.ts index cd3ab60..40ddba9 100644 --- a/src/rules.ts +++ b/src/rules.ts @@ -1,133 +1,158 @@ -import { WhQuestion, UpdateEvent, DMContext } from "./types"; -import { assign, ActionFunction } from "xstate"; +import { Question, DMContext, InformationState } from "./types"; -export const preconditions = ({ - context, - event, -}: { - context: DMContext; - event: any; -}) => { - switch ((event as UpdateEvent).value) { - case "clear_agenda": - return true; - case "get_latest_move": - return true; - case "integrate_sys_greet": { - if ( - context.is.shared.lu!.speaker === "sys" && - context.is.shared.lu!.move.type === "greet" - ) { - return true; - } - return false; - } - case "integrate_sys_ask": { - if ( - context.is.shared.lu!.speaker === "sys" && - context.is.shared.lu!.move.type === "ask" - ) { - return true; - } - return false; - } - case "integrate_usr_ask": { - if ( - context.is.shared.lu!.speaker === "usr" && - context.is.shared.lu!.move.type === "ask" - ) { - return true; - } - return false; - } - default: - console.error("Precondition not implemented! Rule:", event.value); +/** TODO need something better... */ +function domainRelevant(answer: string, question: Question): boolean { + if ( + question === ((x: string) => `favorite_food ${x}`) && + answer === "pizza" + ) { + return true; } return false; +} + +type Rules = { + [index: string]: (context: DMContext) => { + preconditions: boolean; + effects: InformationState; + }; }; -export const effects: ActionFunction< - DMContext, - any, - any, - any, - any, - any, - any, - any, - any -> = assign(({ context, event }) => { - switch ((event as UpdateEvent).value) { - case "clear_agenda": { - const newIS = { - ...context.is, - private: { - ...context.is.private, - agenda: [], +export const rules: Rules = { + clear_agenda: (context) => { + const newIS = { + ...context.is, + private: { ...context.is.private, agenda: [] }, + }; + console.debug(`[ISU clear_agenda]`, newIS); + return { + preconditions: true, + effects: newIS, + }; + }, + + /** + * Grounding + */ + get_latest_move: (context) => { + const newIS = { + ...context.is, + shared: { + ...context.is.shared, + lu: { + move: context.latest_move!, + speaker: context.latest_speaker!, }, - }; - console.debug(`[ISU ${event.value}]`, newIS); - return { is: newIS }; - } + }, + }; + console.debug(`[ISU get_latest_move]`, newIS); + return { + preconditions: true, + effects: newIS, + }; + }, - case "get_latest_move": { + /** + * Integrate + */ + /** rule 2.2 */ + integrate_sys_ask: (context) => { + if ( + context.is.shared.lu!.speaker === "sys" && + context.is.shared.lu!.move.type === "ask" + ) { const newIS = { ...context.is, shared: { ...context.is.shared, - lu: { - move: context.latest_move!, - speaker: context.latest_speaker!, - }, + qud: [ + context.is.shared.lu!.move.content as Question, + ...context.is.shared.qud, + ], }, }; - console.debug(`[ISU ${event.value}]`, newIS); - return { is: newIS }; - } - - case "integrate_sys_greet": { - const newIS = { - ...context.is, + console.debug(`[ISU integrate_sys_ask]`, newIS); + return { + preconditions: true, + effects: newIS, }; - console.debug(`[ISU ${event.value}]`, newIS); - return { is: newIS }; } + return { + preconditions: false, + effects: context.is, + }; + }, - case "integrate_sys_ask": { + /** rule 2.3 */ + integrate_usr_ask: (context) => { + if ( + context.is.shared.lu!.speaker === "usr" && + context.is.shared.lu!.move.type === "ask" + ) { const newIS = { ...context.is, shared: { ...context.is.shared, qud: [ - context.is.shared.lu!.move.content as WhQuestion, + context.is.shared.lu!.move.content as Question, ...context.is.shared.qud, ], }, }; - console.debug(`[ISU ${event.value}]`, newIS); - return { is: newIS }; + console.debug(`[ISU integrate_usr_ask]`, newIS); + return { + preconditions: true, + effects: newIS, + }; } + return { + preconditions: false, + effects: context.is, + }; + }, - case "integrate_sys_ask": { + /** rule 2.4 */ + integrate_answer: (context) => { + const q = context.is.shared.qud[0]; + const a = context.is.shared.lu?.move.content as string; + if (context.is.shared.lu?.move.type === "answer" && domainRelevant(a, q)) { const newIS = { ...context.is, shared: { ...context.is.shared, - qud: [ - context.is.shared.lu!.move.content as WhQuestion, - ...context.is.shared.qud, - ], + com: [q(a), ...context.is.shared.com], }, }; - console.debug(`[ISU ${event.value}]`, newIS); - return { is: newIS }; + console.debug(`[ISU integrate_answer]`, newIS); + return { + preconditions: false, + effects: context.is, + }; } + return { + preconditions: false, + effects: context.is, + }; + }, - default: - console.error("Effect not implemented! Rule:", event.value); - } - return { is: context.is }; -}); + /** rule 2.6 */ + integrate_greet: (context) => { + if (context.is.shared.lu!.move.type === "greet") { + const newIS = { + ...context.is, + }; + console.debug(`[ISU integrate_greet]`, newIS); + return { + preconditions: true, + effects: newIS, + }; + } + return { + preconditions: false, + effects: context.is, + }; + }, +}; // resolve_top_qud: (c) => { // if (c.is.shared.lu) { diff --git a/src/types.ts b/src/types.ts index f7fc630..a5c81a4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,35 +1,37 @@ import { SpeechStateExternalEvent } from "speechstate"; -export type WhQuestion = (a: string) => string; +export type Question = WhQuestion; +type WhQuestion = (a: string) => string; + export interface Move { type: "ask" | "answer" | "respond" | "greet" | "unknown"; - content: null | string | WhQuestion; + content: null | string | Question; } type Speaker = "usr" | "sys"; +export interface InformationState { + private: { agenda: Move[] }; + shared: { + lu?: { speaker: Speaker; move: Move }; + qud: ((a: string) => string)[]; + com: string[]; + }; +} + export interface DMContext { - ssRef?: any; + ssRef: any; + + /** interface variables */ next_move: Move | null; latest_speaker?: Speaker; latest_move?: Move; - is: { - private: { agenda: Move[] }; - shared: { - lu?: { speaker: Speaker; move: Move }; - qud: ((a: string) => string)[]; - com: string[]; - }; - }; + /** information state */ + is: InformationState; } -export type DMEvent = SpeechStateExternalEvent | SaysMoveEvent | UpdateEvent; - -export type UpdateEvent = { - type: "UPDATE"; - value: string; -}; +export type DMEvent = SpeechStateExternalEvent | SaysMoveEvent; export type SaysMoveEvent = { type: "SAYS"; value: { speaker: string; move: Move };