Skip to content

Commit

Permalink
Partially implement IBIS1 (#1)
Browse files Browse the repository at this point in the history
Co-authored-by: Alex Berman <[email protected]>
  • Loading branch information
vladmaraev and alex-berman authored Sep 11, 2024
1 parent f6da6e0 commit 4c3be64
Show file tree
Hide file tree
Showing 14 changed files with 2,737 additions and 713 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/check-typescript.js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Typechecker

on:
pull_request:
branches: [ "main" ]
jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Enable Corepack
run: corepack enable
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'
registry-url: 'https://registry.npmjs.org'
cache: 'yarn'
- run: yarn
- run: yarn exec tsc --noemit
13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
"preview": "vite preview",
"test": "vitest"
},
"devDependencies": {
"typescript": "^5.2.2",
"vite": "^5.2.0"
"vite": "^5.2.0",
"vitest": "^2.0.5"
},
"dependencies": {
"@statelyai/inspect": "^0.2.5",
"speechstate": "^2.0.5"
}
"@statelyai/inspect": "^0.4.0",
"speechstate": "^2.4.0"
},
"packageManager": "[email protected]+sha256.7f7d51b38db0d94adf25c512e3f3d3b47d23c97922eecc540f7440f116bdb99a"
}
19 changes: 9 additions & 10 deletions src/main.ts → src/dipper/main.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { assign, createActor, setup, AnyMachineSnapshot } from "xstate";
import { speechstate } from "speechstate";
import { createBrowserInspector } from "@statelyai/inspect";
import { KEY } from "./azure";
// import { KEY } from "../azure";

const inspector = createBrowserInspector();

const azureCredentials = {
endpoint:
"https://northeurope.api.cognitive.microsoft.com/sts/v1.0/issuetoken",
key: KEY,
// key: KEY,
};

const settings = {
Expand All @@ -17,6 +17,7 @@ const settings = {
asrDefaultNoInputTimeout: 5000,
locale: "en-US",
ttsDefaultVoice: "en-US-DavisNeural",
azureRegion: "northeurope",
};

const dmMachine = setup({
Expand Down Expand Up @@ -86,19 +87,17 @@ const dmMachine = setup({
};
},
}).createMachine({
context: {
is: { input: ["ping"], output: [] },
context: ({ spawn }) => {
return {
ssRef: spawn(speechstate, { input: settings }),
is: { input: ["ping"], output: [] },
};
},
id: "DM",
initial: "Prepare",
states: {
Prepare: {
entry: [
assign({
ssRef: ({ spawn }) => spawn(speechstate, { input: settings }),
}),
({ context }) => context.ssRef.send({ type: "PREPARE" }),
],
entry: ({ context }) => context.ssRef.send({ type: "PREPARE" }),
on: { ASRTTS_READY: "WaitToStart" },
},
WaitToStart: {
Expand Down
153 changes: 153 additions & 0 deletions src/dme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { setup, assign, sendTo, AnyTransitionConfig } from "xstate";
import { rules } from "./rules";
import { SaysMoveEvent, DMEEvent, DMEContext } from "./types";

/**
* Creates a transition with a guarded ISU.
*
* @param nextState Target state.
* @param ruleName Name of ISU rule.
* @param [sendBackNextMove=false] If `true`, communicate next move to the parent machine.
*/
function isuTransition(
nextState: string,
ruleName: string,
sendBackNextMove: boolean = false,
): AnyTransitionConfig {
return {
target: nextState,
guard: { type: "isu", params: { name: ruleName } },
actions: sendBackNextMove
? [
{ type: "isu", params: { name: ruleName } },
{ type: "sendBackNextMove" },
]
: [{ type: "isu", params: { name: ruleName } }],
};
}

export const dme = setup({
types: {} as {
input: DMEContext;
context: DMEContext;
events: DMEEvent;
},
guards: {
isu: ({ context }, params: { name: string }) =>
!!rules[params.name](context),
latestSpeakerIsUsr: ({ context }) => {
return context.latest_speaker == "usr";
},
},
actions: {
sendBackNextMove: sendTo(
({ context }) => context.parentRef,
({ context }) => {
return {
type: "NEXT_MOVE",
value: context.is.next_move,
};
},
),
isu: assign(({ context }, params: { name: string }) => {
let ruleName = params.name;
let newIS = rules[ruleName](context)!(); // we assume that this is never called without a guard
console.info(`[ISU ${ruleName}]`, newIS);
return { is: newIS };
}),
updateLatestMove: assign(({ event }) => {
console.info("[DM updateLatestMove]", event);
return {
latest_move: (event as SaysMoveEvent).value.move,
latest_speaker: (event as SaysMoveEvent).value.speaker,
};
}),
},
}).createMachine({
context: ({ input }) => {
return input;
},
initial: "Select",
states: {
Select: {
initial: "SelectAction",
states: {
SelectAction: {
always: [
isuTransition("SelectMove", "select_respond"),
isuTransition("SelectMove", "select_from_plan"),
{ target: "SelectMove" }, // TODO check it -- needed for greeting
],
},
SelectMove: {
always: [
isuTransition("SelectionDone", "select_ask", true),
isuTransition("SelectionDone", "select_answer", true),
isuTransition("SelectionDone", "select_other", true),
{ target: "SelectionDone" },
],
},
SelectionDone: { type: "final" },
},
onDone: "Update",
},
Update: {
initial: "Init",
states: {
Init: {
always: isuTransition("Grounding", "clear_agenda"),
},
Grounding: {
// TODO: rename to Perception?
on: {
SAYS: {
target: "Integrate",
actions: [
{
type: "updateLatestMove",
},
{ type: "isu", params: { name: "get_latest_move" } },
],
},
},
},
Integrate: {
always: [
isuTransition("DowndateQUD", "integrate_usr_request"),
isuTransition("DowndateQUD", "integrate_sys_ask"),
isuTransition("DowndateQUD", "integrate_usr_ask"),
isuTransition("DowndateQUD", "integrate_answer"),
isuTransition("DowndateQUD", "integrate_greet"),
],
},
DowndateQUD: {
always: [
isuTransition("LoadPlan", "downdate_qud"),
isuTransition("LoadPlan", "find_plan"),
{ target: "LoadPlan" },
],
},
LoadPlan: {
always: { target: "ExecPlan" },
},
ExecPlan: {
always: [
isuTransition("ExecPlan", "remove_findout"),
isuTransition("ExecPlan", "exec_consultDB"),
{ target: "FinalGroup" },
],
},
FinalGroup: {
type: "final",
},
},
onDone: [
{
target: "Select",
guard: "latestSpeakerIsUsr"
},
{ target: "Update" },
],
},
},
});
90 changes: 90 additions & 0 deletions src/is.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { InformationState } from "./types";
import {
objectsEqual,
WHQ,
findout,
consultDB,
getFactArgument,
} from "./utils";

export const initialIS = (): InformationState => {
const predicates: { [index: string]: string } = {
// Mapping from predicate to sort
favorite_food: "food",
booking_course: "course",
};
const individuals: { [index: string]: string } = {
// Mapping from individual to sort
pizza: "food",
LT2319: "course",
};
return {
domain: {
predicates: predicates,
individuals: individuals,
relevant: (a, q) => {
if (
typeof a === "string" &&
predicates[q.predicate] === individuals[a]
) {
return true;
}
if (typeof a === "object" && q.predicate === a.predicate) {
return true;
}
return false;
},
resolves: (a, q) => {
if (typeof a === "object" && q.predicate === a.predicate) {
return true;
}
return false;
},
combine: (q, a) => {
if (
typeof a === "string" &&
predicates[q.predicate] === individuals[a]
) {
return { predicate: q.predicate, argument: a };
}
if (typeof a === "object" && q.predicate === a.predicate) {
return a;
}
throw new Error("Combine failed.");
},
plans: [
{
type: "issue",
content: WHQ("booking_room"),
plan: [
findout(WHQ("booking_course")),
consultDB(WHQ("booking_room")),
],
},
],
},
database: {
consultDB: (question, facts) => {
if (objectsEqual(question, WHQ("booking_room"))) {
const course = getFactArgument(facts, "booking_course");
if (course == "LT2319") {
return { predicate: "booking_room", argument: "G212" };
}
}
return null;
},
},
next_move: null,
private: {
plan: [],
agenda: [
{
type: "greet",
content: null,
},
],
bel: [{ predicate: "favorite_food", argument: "pizza" }],
},
shared: { lu: undefined, qud: [], com: [] },
};
};
Loading

0 comments on commit 4c3be64

Please sign in to comment.